laravel-sat-catalogs/Services/SatCatalogsService.php

417 lines
14 KiB
PHP
Raw Permalink Normal View History

2025-03-10 18:25:41 -06:00
<?php
namespace Koneko\SatCatalogs\Services;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class SatCatalogService
{
protected $catalogs = [
'banco' => [
'table' => 'sat_banco',
'key' => 'c_banco',
'value' => 'descripcion AS item',
'order_by' => 'descripcion',
'search_columns' => ['descripcion'],
'use_status' => true,
'limit' => 10
],
'clave_prod_serv' => [
'table' => 'sat_clave_prod_serv',
'key' => 'c_clave_prod_serv',
'value' => "CONCAT_WS(' - ', c_clave_prod_serv , descripcion) as item",
'search_type' => 'MATCH',
'search_columns' => ['c_clave_prod_serv_text', 'descripcion'],
],
'clave_unidad' => [
'table' => 'sat_clave_unidad',
'key' => 'c_clave_unidad',
'value' => "CONCAT_WS(' - ', c_clave_unidad , nombre) as item",
'search_columns' => ['c_clave_unidad', 'nombre'],
'limit' => 20
],
'deduccion' => [
'table' => 'sat_deduccion',
'key' => 'c_deduccion',
'value' => "CONCAT_WS(' - ', c_deduccion , descripcion) as item",
'search_columns' => ['c_deduccion', 'descripcion'],
'limit' => 20
],
'forma_pago' => [
'table' => 'sat_forma_pago',
'key' => 'c_forma_pago',
'value' => "CONCAT_WS(' - ', c_forma_pago , descripcion) as item",
'search_columns' => ['c_forma_pago', 'descripcion'],
'limit' => 10
],
'moneda' => [
'table' => 'sat_moneda',
'key' => 'c_moneda',
'value' => "CONCAT_WS(' - ', c_moneda , descripcion) as item",
'order_by' => 'descripcion',
'search_columns' => ['c_moneda', 'descripcion'],
'limit' => 10
],
'pais' => [
'table' => 'sat_pais',
'key' => 'c_pais',
'value' => 'descripcion AS item',
'order_by' => 'descripcion',
'search_columns' => ['c_pais', 'descripcion'],
'limit' => 10
],
'estado' => [
'table' => 'sat_estado',
'key' => 'c_estado',
'value' => 'nombre_del_estado AS item',
'order_by' => 'nombre_del_estado',
'extra_conditions' => ['c_pais'],
'search_columns' => ['nombre_del_estado'],
],
'municipio' => [
'table' => 'sat_municipio',
'key' => 'c_municipio',
'value' => 'descripcion AS item',
'order_by' => 'descripcion',
'extra_conditions' => ['c_estado'],
'search_columns' => ['descripcion'],
],
'localidad' => [
'table' => 'sat_localidad',
'key' => 'c_localidad',
'value' => 'descripcion AS item',
'order_by' => 'descripcion',
'extra_conditions' => ['c_estado'],
'search_columns' => ['descripcion'],
],
'colonia' => [
'table' => 'sat_colonia',
'joins' => [
[
'table' => 'sat_codigo_postal',
'first' => 'sat_colonia.c_codigo_postal',
'second' => 'sat_codigo_postal.c_codigo_postal',
],
],
'key' => 'sat_colonia.c_colonia',
'value' => 'sat_colonia.nombre_del_asentamiento AS item',
'columns' => [
'sat_colonia.c_codigo_postal',
],
'order_by' => 'sat_colonia.nombre_del_asentamiento',
'extra_conditions' => ['sat_codigo_postal.c_codigo_postal', 'sat_codigo_postal.c_estado', 'sat_codigo_postal.c_municipio', 'sat_colonia.c_colonia'],
'search_columns' => ['sat_colonia.nombre_del_asentamiento'],
],
'codigo_postal' => [
'table' => 'sat_codigo_postal',
'joins' => [
[
'table' => 'sat_localidad',
'first' => 'sat_codigo_postal.c_localidad',
'second' => 'sat_localidad.c_localidad',
'and' => ['sat_codigo_postal.c_estado = sat_localidad.c_estado'],
'type' => 'leftJoin',
],
[
'table' => 'sat_municipio',
'first' => 'sat_codigo_postal.c_municipio',
'second' => 'sat_municipio.c_municipio',
'and' => ['sat_codigo_postal.c_estado = sat_municipio.c_estado'],
'type' => 'leftJoin',
],
[
'table' => 'sat_estado',
'first' => 'sat_codigo_postal.c_estado',
'second' => 'sat_estado.c_estado',
'type' => 'leftJoin',
],
],
'key' => 'sat_codigo_postal.c_codigo_postal',
'columns' => [
'sat_codigo_postal.c_estado',
'sat_estado.nombre_del_estado as estado',
'sat_codigo_postal.c_localidad',
'sat_localidad.descripcion as localidad',
'sat_codigo_postal.c_municipio',
'sat_municipio.descripcion as municipio',
],
'search_columns' => ['sat_codigo_postal.c_codigo_postal'],
],
];
protected $fixedCatalogs = [
'exportacion' => [
1 => 'No aplica',
2 => 'Definitiva con clave A1',
3 => 'Temporal',
4 => 'Definitiva con clave distinta a A1 o cuando no existe enajenación en términos del CFF'
],
'horas_extra' => [
'01' => 'Dobles',
'02' => 'Triples',
'03' => 'Simples'
],
'impuestos' => [
1 => 'ISR',
2 => 'IVA',
3 => 'IEPS'
],
'incapacidad' => [
1 => 'Riesgo de trabajo',
2 => 'Enfermedad en general',
3 => 'Maternidad',
4 => 'Licencia por cuidados médicos de hijos diagnosticados con cáncer'
],
'jornada' => [
'01' => 'Diurna',
'02' => 'Nocturna',
'03' => 'Mixta',
'04' => 'Por hora',
'05' => 'Reducida',
'06' => 'Continuada',
'07' => 'Partida',
'08' => 'Por turnos',
'99' => 'Otra Jornada'
],
'metodo_pago' => [
'PUE' => 'Pago en una sola exhibición',
'PPD' => 'Pago en parcialidades o diferido'
],
'nomina' => [
'O' => 'Nómina ordinaria',
'E' => 'Nómina extraordinaria'
],
'objeto_imp' => [
1 => 'No objeto de impuesto.',
2 => 'Sí objeto de impuesto.',
3 => 'Sí objeto del impuesto y no obligado al desglose.',
4 => 'Sí objeto del impuesto y no causa impuesto.',
5 => 'Sí objeto del impuesto, IVA crédito PODEBI.'
],
'origen_recurso' => [
'IP' => 'Ingresos propios',
'IF' => 'Ingresos federales',
'IM' => 'Ingresos mixtos'
],
'otro_pago' => [
1 => 'Reintegro de ISR pagado en exceso',
2 => 'Subsidio para el empleo',
3 => 'Viáticos',
4 => 'Aplicación de saldo a favor por compensación anual',
5 => 'Reintegro de ISR retenido en exceso de ejercicio anterior',
6 => 'Alimentos en bienes',
7 => 'ISR ajustado por subsidio',
8 => 'Subsidio efectivamente entregado que no correspondía',
999 => 'Pagos distintos a los listados'
],
'periodicidad' => [
1 => 'Diario',
2 => 'Semanal',
3 => 'Quincenal',
4 => 'Mensual',
5 => 'Bimestral'
],
'periodicidad_pago' => [
1 => 'Diario',
2 => 'Semanal',
3 => 'Catorcenal',
4 => 'Quincenal',
5 => 'Mensual',
6 => 'Bimestral',
7 => 'Unidad obra',
8 => 'Comisión',
9 => 'Precio alzado',
10 => 'Decenal',
99 => 'Otra Periodicidad'
],
'riesgo_puesto' => [
'1' => 'Clase I',
'2' => 'Clase II',
'3' => 'Clase III',
'4' => 'Clase IV',
'5' => 'Clase V'
],
'tipo_comprobante' => [
'I' => 'Ingreso',
'E' => 'Egreso',
'T' => 'Traslado',
'N' => 'Nómina',
'P' => 'Pago'
],
'tipo_factor' => [
1 => 'Tasa',
2 => 'Cuota',
3 => 'Exento'
],
'tipo_relacion' => [
1 => 'Nota de crédito de los documentos relacionados',
2 => 'Nota de débito de los documentos relacionados',
3 => 'Devolución de mercancía sobre facturas o traslados previos',
4 => 'Sustitución de los CFDI previos',
5 => 'Traslados de mercancías facturados previamente',
6 => 'Factura generada por los traslados previos',
7 => 'CFDI por aplicación de anticipo'
]
];
public function searchCatalog(string $catalog, string $searchTerm = '', array $options = []): array
{
// 1. Validar si es un catálogo fijo o uno definido en $this->catalogs
if (isset($this->fixedCatalogs[$catalog])) {
return $this->fixedCatalogs[$catalog];
}
if (!isset($this->catalogs[$catalog])) {
return [];
}
$config = $this->catalogs[$catalog];
// 2. Construye Query Builder base
$query = DB::table($config['table']);
// 3. Aplica joins
if (!empty($config['joins'])) {
foreach ($config['joins'] as $join) {
$type = $join['type'] ?? 'join';
$query->{$type}($join['table'], function($joinObj) use ($join) {
$joinObj->on($join['first'], '=', $join['second']);
// Soporte para AND en ON, si está definidio
if (!empty($join['and'])) {
foreach ((array) $join['and'] as $andCondition) {
// 'sat_codigo_postal.c_estado = sat_localidad.c_estado'
$parts = explode('=', $andCondition);
if (count($parts) === 2) {
$left = trim($parts[0]);
$right = trim($parts[1]);
$joinObj->whereRaw("$left = $right");
}
}
}
});
}
}
// 4. Construir la lista de columnas a seleccionar
$selectFields = [];
// - Si hay "columns", añádelas
if (!empty($config['columns'])) {
foreach ($config['columns'] as $col) {
$selectFields[] = DB::raw($col);
}
}
// - Si también tienes "key" y "value" (por ejemplo para select2),
// añádelos (si no están ya en columns).
if (!empty($config['key'])) {
$selectFields[] = DB::raw($config['key']);
}
if (!empty($config['value'])) {
$selectFields[] = DB::raw($config['value']);
}
// - Si al final no hay nada, por fallback selecciona todo (o lanza un error)
if (empty($selectFields)) {
$query->select('*');
} else {
$query->select($selectFields);
}
// 5. Filtrado por status si aplica
if (($config['use_status'] ?? false) === true) {
$status = isset($options['status'])
? $options['status']
: (isset($config['status']) ? $config['status']: null);
if ($status !== null) {
$query->where('status', $status);
}
}
// 6. Filtrar según extra_conditions (ahora puede incluir columnas de la tabla unida)
if (isset($config['extra_conditions'])) {
foreach ($config['extra_conditions'] as $field) {
if (array_key_exists($field, $options)) {
$query->where($field, $options[$field]);
}
}
}
// 7. Búsqueda
if (!empty($searchTerm) && !empty($config['search_columns'])) {
if (($config['search_type'] ?? 'LIKE') === 'MATCH') {
// Ejemplo: MATCH..AGAINST
$cols = implode(',', $config['search_columns']);
$query->whereRaw("MATCH ($cols) AGAINST (? IN BOOLEAN MODE)", [$searchTerm]);
} else {
// Búsqueda por LIKE
$query->where(function ($subQ) use ($config, $searchTerm) {
foreach ($config['search_columns'] as $col) {
$subQ->orWhere($col, 'LIKE', "%{$searchTerm}%");
}
});
}
}
// 8. Ordenar resultados
$orderBy = $options['order_by'] ?? $config['order_by'] ?? $config['key'];
$orderDir = $options['order_dir'] ?? $config['order_dir'] ?? 'asc';
$query->orderBy($orderBy, $orderDir);
// 9. Limitar
$limit = array_key_exists('limit', $options) ? $options['limit'] : ($config['limit'] ?? 50);
if ($limit !== null) {
$query->limit($limit);
}
// Para ver la sentencia SQL (con placeholders ?)
// dump($query->toSql()); dd($query->getBindings());
// 10. Revisar modo de respuesta
$rawMode = ($config['rawMode'] ?? false) || ($options['rawMode'] ?? false);
$firstRow = ($config['firstRow'] ?? false) || ($options['firstRow'] ?? false);
$select2Mode = $options['select2Mode'] ?? false;
// (a) Modo raw -> devolvemos tal cual
if ($rawMode) {
if($firstRow){
return (array) $query->first();
}
return $query->get()->toArray();
}
$shortKey = Str::afterLast($config['key'], '.');
// (b) Devuelve en formato "select2" o en un array
if ($select2Mode) {
$response = [];
foreach ($query->get() as $row) {
$response[] = [
'id' => $row->{$shortKey},
'text' => $row->item,
];
}
return $response;
}
// (c) Por defecto, regresa "pluck" id => texto
return $query->pluck('item', $shortKey)->toArray();
}
}