Appearance
Tarjetas - Documentacion Tecnica Backend
Modulo: ventas Feature: Tarjetas (tipos de tarjeta para facturacion) Fecha: 2026-02-09
DOCUMENTACION RETROSPECTIVA - Generada a partir de codigo implementado el 2026-02-09
Documento de Negocio
Requisitos de Negocio - Tarjetas
Arquitectura Implementada
| Capa | Archivo | Responsabilidad |
|---|---|---|
| Route | Routes/Venta/TarjetaRoute.php | Definicion de endpoints REST |
| Controller | controller/modulo-venta/TarjetaController.php | Manejo HTTP request/response |
| Service | service/Venta/TarjetaService.php | Validaciones de negocio, orquestacion |
| Model | models/modulo-venta/Tarjeta.php | Acceso a datos, mapeo a DTO |
| DTO | Resources/Venta/Tarjeta.php | Objeto de transferencia de datos |
| Validator | Validators/Venta/TarjetaValidator.php | Validacion estructural de datos |
Registro de ruta: Routes/Venta/VentaRoutes.php registra $group->group('/tarjetas', TarjetaRoute::class), bajo el grupo principal /mod-ventas.
Flujo de Peticion
Request → AuthMiddleware → ConnectionMiddleware → TarjetaRoute
→ TarjetaController → TarjetaService → (TarjetaValidator + Tarjeta Model) → Database
→ Response (JSON)Nota: El TarjetaValidator no esta aplicado como middleware en la ruta. La validacion se invoca directamente desde el TarjetaService.validateRequestData().
Endpoints API
GET /api/mod-ventas/tarjetas
Descripcion: Obtener listado de todas las tarjetas activas (no eliminadas).
Parametros Body (opcionales):
| Campo | Tipo | Descripcion |
|---|---|---|
filter | string | Filtro ILIKE por nombre de tarjeta |
Respuesta 200:
json
{
"status": 200,
"message": "Datos recibidos correctamente.",
"data": [
{ "id": 1, "nombre": "Visa", "cuenta": 11050 },
{ "id": 2, "nombre": "Mastercard", "cuenta": 11051 }
]
}Logica:
- Filtra registros donde
deleted_at IS NULL - Si se proporciona
filter, aplicanombre ILIKE %{filter}% - Retorna array de
TarjetaDTO
GET /api/mod-ventas/tarjetas/{id}
Descripcion: Obtener una tarjeta por su ID.
Parametros Path:
| Campo | Tipo | Requerido | Descripcion |
|---|---|---|---|
id | integer | Si | ID de la tarjeta |
Respuesta 200:
json
{
"status": 200,
"message": "Datos recibidos correctamente.",
"data": { "id": 1, "nombre": "Visa", "cuenta": 11050 }
}Respuesta 400: Si no se proporciona ID.
Nota: La consulta getById no filtra por deleted_at. Esto significa que es posible obtener tarjetas eliminadas por ID.
POST /api/mod-ventas/tarjetas
Descripcion: Crear una nueva tarjeta.
Request Body:
| Campo | Tipo | Requerido | Validacion | Descripcion |
|---|---|---|---|---|
nombre | string | Si | required|max:100 | Nombre de la tarjeta |
cuenta | integer|null | Condicional | nullable|integer | Numero de cuenta contable |
Logica de validacion en Service:
- Se ejecuta
TarjetaValidator.validate(data)(validacion estructural). - Se consulta la configuracion del sistema (
PermisosEmpresa) para verificar si el modulo Tesoreria esta habilitado. - Si Tesoreria esta habilitado:
- El campo
cuentase vuelve obligatorio (error 400 si es null/falsy). - Se verifica que la cuenta exista en el plan de cuentas mediante
Cuenta.getById()(error 400 si no existe).
- El campo
Respuesta 200:
json
{
"status": 200,
"message": "Datos recibidos correctamente.",
"data": { "id": 3, "nombre": "American Express", "cuenta": 11052 }
}Nota sobre HTTP status: El controller retorna 200 en lugar del estandar 201 para creacion.
PUT /api/mod-ventas/tarjetas/{id}
Descripcion: Actualizar una tarjeta existente.
Parametros Path:
| Campo | Tipo | Requerido |
|---|---|---|
id | integer | Si |
Request Body: Mismo esquema que POST.
Logica: Misma validacion que POST (validateRequestData), luego actualiza todos los campos.
Respuesta 200: Tarjeta actualizada como DTO.
DELETE /api/mod-ventas/tarjetas/{id}
Descripcion: Eliminacion logica de una tarjeta.
Parametros Path:
| Campo | Tipo | Requerido |
|---|---|---|
id | integer | Si |
Logica: Ejecuta UPDATE tarjetas SET deleted_at = :fecha WHERE id = :id. No verifica si la tarjeta existe ni si tiene comprobantes asociados.
Respuesta 204: Sin contenido.
Capa de Servicio
TarjetaService
Dependencias:
| Dependencia | Tipo | Proposito |
|---|---|---|
ModelFactory | Factory | Creacion de modelos con conexion inyectada |
Tarjeta (Model) | Model | Acceso a datos de tarjetas (conexion oficial) |
Config (Model) | Model | Lectura de configuracion del sistema (conexion oficial) |
Cuenta (Model) | Model | Verificacion de existencia de cuentas (conexion oficial) |
TarjetaValidator | Validator | Validacion estructural de datos de entrada |
Metodos publicos:
| Metodo | Parametros | Retorno | Descripcion |
|---|---|---|---|
getAll | array $options = [] | TarjetaDTO[] | Listar tarjetas con filtro opcional |
getById | int $id | ?TarjetaDTO | Obtener tarjeta por ID |
insert | array $data | ?TarjetaDTO | Crear tarjeta con validacion |
update | int $id, array $data | ?TarjetaDTO | Actualizar tarjeta con validacion |
delete | int $id | bool | Eliminacion logica |
Metodo privado validateRequestData(array $data):
- Ejecuta validacion estructural via
TarjetaValidator. - Obtiene configuracion
PermisosEmpresadel sistema. - Si
modulo_tesoreriaesta habilitado:- Verifica que
$data['cuenta']no sea falsy (error 400). - Verifica que la cuenta exista via
Cuenta.getById()(error 400).
- Verifica que
Observaciones:
- No utiliza transacciones (
beginTransaction/commit/rollback). - No implementa el trait
AuditableniConectable. - No implementa
AuditableInterface. - Usa
ModelFactoryen lugar del patronConnectionManagerdirecto.
Capa de Datos (Model)
Tarjeta Model
Tabla: tarjetasConexion: Instanciado via ModelFactory con conexion oficial (PDO).
Metodos:
| Metodo | SQL | Retorno |
|---|---|---|
getAll(array $options) | SELECT id, nombre, cuenta FROM tarjetas WHERE deleted_at IS NULL [AND nombre ILIKE ...] | TarjetaDTO[] |
getById(int $id) | SELECT nombre, cuenta FROM tarjetas WHERE id = :id | ?TarjetaDTO |
insert(array $data) | INSERT INTO tarjetas (nombre, cuenta) VALUES (...) RETURNING id | ?TarjetaDTO |
update(int $id, array $data) | UPDATE tarjetas SET nombre = ..., cuenta = ... WHERE id = :id | ?TarjetaDTO |
delete(int $id) | UPDATE tarjetas SET deleted_at = :fecha WHERE id = :id | bool |
Observaciones:
getByIdno filtra pordeleted_at IS NULL, permitiendo obtener tarjetas eliminadas.deleteno verifica quedeleted_at IS NULLantes de marcar como eliminado.- Usa prepared statements en todos los metodos (seguro contra SQL injection).
DTO (Data Transfer Object)
Tarjeta DTO
Clase: App\Resources\Venta\Tarjeta extends DTO
| Propiedad | Tipo | Default | Descripcion |
|---|---|---|---|
id | int | - | ID de la tarjeta |
nombre | string | - | Nombre de la tarjeta |
cuenta | ?int | null | Numero de cuenta contable |
Hereda fromArray() y toArray() de la clase base DTO.
Esquema de Base de Datos
Tabla: tarjetas
Nivel de tenencia: EMPRESA (schema public)
Fuente: Migracion 20250610112734_new_table_tarjetas.php
| Campo | Tipo | Constraints | Descripcion |
|---|---|---|---|
id | SERIAL | PRIMARY KEY | ID autoincrementable |
nombre | VARCHAR(100) | NOT NULL | Nombre de la tarjeta |
cuenta | DECIMAL(10) | NULL | Numero de cuenta contable |
deleted_at | TIMESTAMP | NULL | Fecha de eliminacion logica |
Comentario de tabla: "Listado correspondiente a los diferentes tipos de tarjetas."
Condicion de ejecucion: La migracion solo se ejecuta si el modulo de Ventas esta habilitado (isVentasEnabled()).
Observacion sobre tipo de cuenta: En la migracion se define como DECIMAL(10) pero en el DTO y validador se trata como integer. Esto no genera error en PostgreSQL pero podria causar inconsistencias en precision.
Relaciones con otras tablas
Las tarjetas se referencian desde comprobantes de venta mediante el campo id_tarjeta:
| Tabla | Campo | Tipo | Constraint | Migracion |
|---|---|---|---|---|
factura | id_tarjeta | INTEGER | NULL | 20250610120831_new_field_id_tarjeta_in_factura.php |
credito | id_tarjeta | INTEGER | NULL | 20250610120834_new_field_id_tarjeta_in_credito.php |
debito | id_tarjeta | INTEGER | NULL | 20250610120838_new_field_id_tarjeta_in_debito.php |
Nota: No se definen FOREIGN KEY constraints formales en las migraciones. La relacion es logica, no fisicamente restringida por la base de datos.
Diagrama de Base de Datos
mermaid
erDiagram
tarjetas {
serial id PK
varchar_100 nombre "NOT NULL"
decimal_10 cuenta "NULL"
timestamp deleted_at "NULL"
}
factura {
integer id_tarjeta FK "NULL - referencia a tarjetas"
}
credito {
integer id_tarjeta FK "NULL - referencia a tarjetas"
}
debito {
integer id_tarjeta FK "NULL - referencia a tarjetas"
}
tarjetas ||--o{ factura : "referenciada por"
tarjetas ||--o{ credito : "referenciada por"
tarjetas ||--o{ debito : "referenciada por"Validaciones Implementadas
Nivel 1: Validacion Estructural (TarjetaValidator)
| Campo | Regla | Mensaje |
|---|---|---|
nombre | required|max:100 | Campo obligatorio, maximo 100 caracteres |
cuenta | nullable|integer | Puede ser null, si tiene valor debe ser entero |
Nota: El validator no esta aplicado como middleware de ruta. Se invoca directamente desde TarjetaService.validateRequestData().
Nivel 2: Validacion de Negocio (TarjetaService)
| Regla | Condicion | Accion | Error |
|---|---|---|---|
| Cuenta obligatoria | Modulo Tesoreria habilitado | Verifica que cuenta no sea falsy | 400: "La cuenta es obligatoria." |
| Cuenta existente | Modulo Tesoreria habilitado y cuenta proporcionada | Verifica existencia via Cuenta.getById() | 400: "La cuenta seleccionada no existe." |
Nota sobre validación condicional por módulos: Este recurso implementa validación de negocio que depende de módulos habilitados (modulo_tesoreria). La falta de datos por módulos no se valida en backend sino que suelen tener campos por defecto como deshabilitación (Null, 0, N, false, etc.). En este caso, el campo cuenta es nullable y solo se valida su existencia cuando el módulo de tesorería está habilitado.
Integracion con Facturacion
La tarjeta se integra con el proceso de facturacion de la siguiente manera:
- CondicionVenta enum:
TARJETA = 3indica condicion de venta por tarjeta. - CondicionVenta model: La tabla
condvtatiene un campo booleanotarjeta(aliases_tarjeta) que indica si una condicion de venta es de tipo tarjeta. - Comprobante DTO:
ComprobanteRequestyComprobanteincluyen propiedad?int $tarjeta. - ComprobanteService: Al registrar un comprobante con
condicionVenta === TARJETA:- Valida que se haya especificado una tarjeta.
- Obtiene la cuenta contable de la tarjeta via
Tarjeta.getById(). - Genera un movimiento de caja adicional contra la cuenta de la tarjeta.
- Models de comprobantes:
Factura,NotaCreditoyNotaDebitoalmacenanid_tarjetaen sus respectivas tablas.
Tests Implementados
TarjetaModelMethodsTest (Unit)
| Test | Descripcion |
|---|---|
testGetAllExistingRecords | Verifica que getAll retorna solo registros no eliminados |
testGetAllWithFilters (DataProvider) | Verifica filtrado ILIKE por nombre con multiples escenarios |
testUpdateARecord | Verifica actualizacion de nombre |
testDeleteArecord | Verifica soft delete (registro no aparece en getAll despues de delete) |
TarjetaServiceMethodsTest (Unit)
| Test | Descripcion |
|---|---|
testValidOperations (DataProvider) | Verifica insert y update con Tesoreria deshabilitado |
testExeptions (DataProvider) | Verifica errores: cuenta faltante y cuenta inexistente con Tesoreria habilitado |
Deuda Técnica Identificada
Durante el análisis retrospectivo del código se identificaron los siguientes aspectos pendientes de mejora:
Arquitectura Moderna con Service Layer
Este recurso utiliza correctamente la arquitectura de 5 capas (Routes + Validators + Controller + Service + Model). Es uno de los recursos que sigue el patrón moderno del sistema.
Aspectos positivos:
- Usa Slim Routes con Validator middleware
- Implementa Service Layer con validaciones de negocio
- Implementa soft delete correctamente
- Tiene tests unitarios implementados
Foreign Keys Lógicas
Las relaciones se validan en la capa de aplicación y Service Layer. No poseen FK físicas de momento, sólo FK lógicas como muchas de las FK del sistema.
Audit Logging Ausente
Aunque tiene Service Layer, no implementa audit logging automático. Pendiente de implementación en refactorización futura.
Nota: Este es uno de los recursos legacy mejor refactorizados. Sirve como ejemplo para la migración de otros recursos.
Falta de Índices en Tabla tarjetas
La migración de la tabla tarjetas no define índices explícitos (excepto PK automática sobre codigo). Casi ninguna tabla de recursos viejos poseen índices, esto es por migración pero podrían empezar a implementarse.
Campos candidatos para índices:
concepto(búsquedas por nombre con ILIKE en filter)defecto(filtro por tarjeta por defecto)activo(filtro por tarjetas activas)tipo(FK lógica a condiciones de venta tipo tarjeta)
Agregado a TODO.md para evaluación e implementación gradual.
Preguntas Tecnicas Pendientes
Hay aspectos tecnicos que requieren validacion. Ver: Preguntas sobre Tarjetas
Referencias
NOTA IMPORTANTE: Esta documentacion fue generada automaticamente analizando el codigo implementado. Validar cambios futuros contra este baseline.