417 lines
14 KiB
PHP
417 lines
14 KiB
PHP
|
<?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();
|
||
|
}
|
||
|
|
||
|
}
|