feat: Implement client and provider management dashboard

- Added data table component for displaying clients and providers with infinite scroll functionality.
- Created dropdown actions for each client/provider including copy ID, copy RFC, view details, edit, toggle status, and delete.
- Implemented dialogs for creating, editing, viewing details, and deleting clients/providers.
- Integrated API calls for fetching, creating, editing, deleting, and toggling status of clients/providers.
- Enhanced error handling and loading states for better user experience.
- Updated server-side logic to handle pagination and company selection for client/provider data retrieval.
This commit is contained in:
2025-11-12 16:08:42 -06:00
parent 67a309912c
commit 6225122832
32 changed files with 1974 additions and 543 deletions

View File

@@ -4,11 +4,12 @@
1. [Visión General](#visión-general)
2. [Stack Tecnológico](#stack-tecnológico)
3. [Arquitectura del Sistema](#arquitectura-del-sistema)
4. [Estructura del Proyecto](#estructura-del-proyecto)
5. [Flujos Principales](#flujos-principales)
6. [Seguridad](#seguridad)
7. [Base de Datos](#base-de-datos)
8. [API Reference](#api-reference)
4. [Arquitectura de Schemas y Módulos](#arquitectura-de-schemas-y-módulos)
5. [Estructura del Proyecto](#estructura-del-proyecto)
6. [Flujos Principales](#flujos-principales)
7. [Seguridad](#seguridad)
8. [Base de Datos](#base-de-datos)
9. [API Reference](#api-reference)
---
@@ -78,12 +79,12 @@ Anexo76 es una aplicación SaaS multi-tenant para gestión de comercio exterior
┌───────▼──────────────────────────┐
│ DATABASE LAYER (Multi-tenant) │
│ ┌──────────┐ ┌──────────────┐ │
│ │ Core DB │ │ Tenant 1 DB │ │
│ │ (shared) │ │ (dedicated) │ │
│ └──────────┘ └──────────────┘ │
└──────────────────────────────────
│ │
│ ┌──────────┐ ┌──────────────┐
│ │ Core DB │ │ Tenant 1 DB │
│ │ (shared) │ │ (dedicated) │
│ └──────────┘ └──────────────┘
└──────────────────────────────────┘
```
### Estructura Modular (por módulo)
@@ -123,6 +124,104 @@ modules/{module_name}/
---
## Arquitectura de Schemas y Módulos
### Estructura de Schemas en Base de Datos
La aplicación utiliza una arquitectura de schemas para organizar lógicamente las tablas según su funcionalidad y alcance:
#### **Schema `a24` (Anexo 24)**
Contiene todas las tablas relacionadas con el **Anexo 24 del SAT** (control de inventarios para empresas IMMEX):
- Gestión de inventarios
- Control de entradas y salidas de mercancías
- Reportes de existencias
- Cumplimiento de obligaciones fiscales del Anexo 24
#### **Schema `a76` (Anexo 76)**
Contiene todas las tablas relacionadas con el **Anexo 76 del SAT** (comercio exterior):
- Pedimentos aduanales
- Facturas de importación/exportación
- Documentación de comercio exterior
- Cumplimiento normativo de comercio exterior
#### **Schema `public` (Catálogos Fijos)**
Contiene **catálogos compartidos** y datos de referencia que no cambian frecuentemente:
- Catálogos del SAT (tipos de material, unidades de medida, etc.)
- Códigos de país
- Catálogos de aduanas
- Tipos de documento
- Datos maestros compartidos entre módulos
### Convención de Prefijos de Tablas
Para mantener claridad y trazabilidad, las tablas utilizan prefijos que identifican su módulo funcional:
#### **Prefijo `inv_` (Inventarios)**
Tablas relacionadas con el **control de inventarios**:
- `inv_products`: Productos en inventario
- `inv_movements`: Movimientos de entrada/salida
- `inv_warehouses`: Almacenes
- `inv_balances`: Saldos de inventario
**Nota histórica**: Anteriormente se utilizaba el prefijo `s` (SCAII - Sistema de aduanas e Inventarios).
#### **Prefijo `fa_` (Fixed Assets / Activos Fijos)**
Tablas relacionadas con la **gestión de activos fijos**:
- `fa_assets`: Registro de activos fijos
- `fa_depreciation`: Depreciación de activos
- `fa_maintenance`: Mantenimiento de activos
- `fa_transfers`: Transferencias de activos
**Nota histórica**: Anteriormente se utilizaba el prefijo `q` (SCAF - Sistema de Control de Activos Fijos).
### Diagrama de Arquitectura de Schemas
```
┌─────────────────────────────────────────────────────────────┐
│ DATABASE: anexo76_db │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌────────────────┐ ┌────────────────┐ ┌───────────────┐ │
│ │ Schema: a24 │ │ Schema: a76 │ │Schema: public │ │
│ │ (Anexo 24) │ │ (Anexo 76) │ │ (Catálogos) │ │
│ ├────────────────┤ ├────────────────┤ ├───────────────┤ │
│ │ │ │ │ │ │ │
│ │ inv_products │ │ pedimentos │ │ material_types│ │
│ │ inv_movements │ │ facturas │ │ uom_codes │ │
│ │ inv_warehouses │ │ customs_docs │ │ countries │ │
│ │ inv_balances │ │ export_ops │ │ customs_list │ │
│ │ │ │ │ │ document_types│ │
│ │ fa_assets │ │ │ │ │ │
│ │ fa_depreciation│ │ │ │ │ │
│ │ fa_maintenance │ │ │ │ │ │
│ │ fa_transfers │ │ │ │ │ │
│ │ │ │ │ │ │ │
│ └────────────────┘ └────────────────┘ └───────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
```
### Ventajas de esta Arquitectura
1. **Separación Lógica**: Cada schema representa un dominio específico del negocio
2. **Escalabilidad**: Facilita la adición de nuevos módulos sin afectar los existentes
3. **Seguridad**: Permite aplicar permisos a nivel de schema
4. **Mantenibilidad**: Código y migraciones organizados por dominio
5. **Claridad**: Los prefijos hacen evidente la funcionalidad de cada tabla
6. **Migración Gradual**: Permite actualizar sistemas legados (SCAII/SCAF) sin interrupciones
### Mapeo de Sistemas Legados
| Sistema Legacy | Prefijo Antiguo | Sistema Nuevo | Prefijo Nuevo | Schema |
|----------------------|-----------------|-------------------|---------------|----------|
| SCAII (Inventarios) | `s` | Inventarios | `inv_` | `a24` |
| SCAF (Activos Fijos) | `q` | Fixed Assets | `fa_` | `a24` |
| Winsaii (Pedimentos) | `w` | - | - | `a22` |
| - | `g` | Comercio Exterior | - | `a76` |
| - | `g` | Catálogos SAT | - | `public` |
---
## Estructura del Proyecto
```
@@ -143,11 +242,25 @@ anexo76/
│ └── api/
│ └── v1/
│ ├── router.py # Router principal v1
── modules/ # Módulos de negocio
├── auth/ # Autenticación
├── tenants/ # Gestión de tenants
├── licenses/ # Control de licencias
└── ... # Futuros módulos
── common/ # Utilidades compartidas
├── base_models.py
├── crud_routes.py
├── dto_mixins.py
└── tenant_crud_routes.py
│ │
│ └── modules/ # Módulos de negocio por schema
│ ├── a24/ # Módulo Anexo 24 (Inventarios)
│ │ ├── inventarios/
│ │ └── activos_fijos/
│ │
│ ├── a76/ # Módulo Anexo 76 (Comercio Exterior)
│ │ ├── pedimentos/
│ │ └── facturas/
│ │
│ └── public/ # Catálogos compartidos
│ ├── material_types/
│ ├── uom_codes/
│ └── countries/
├── frontend/
│ ├── src/

View File

@@ -2,6 +2,15 @@
Esta guía te ayudará a configurar Keycloak para usar con Anexo76.
# Script auto initialize
Te genera toda la configruracion inicial de keycloack que se ve en este documento,
aparte de esto te genera un primer usuario configurado con su tenant y una company
```
scripts/init_first_time.sh
```
## 1. Acceder a Keycloak Admin Console
1. Abrir http://localhost:8080
@@ -11,6 +20,7 @@ Esta guía te ayudará a configurar Keycloak para usar con Anexo76.
## 2. Configurar Cliente Backend
### Crear Cliente Backend
1. En el menú izquierdo, ir a **Clients**
2. Clic en **Create client**
3. Configurar:
@@ -29,6 +39,7 @@ Esta guía te ayudará a configurar Keycloak para usar con Anexo76.
- Clic en **Save**
### Obtener Client Secret
1. Ir a la pestaña **Credentials**
2. Copiar el **Client secret**
3. Agregar al archivo `backend/.env`:
@@ -39,6 +50,7 @@ Esta guía te ayudará a configurar Keycloak para usar con Anexo76.
## 3. Configurar Cliente Frontend
### Crear Cliente Frontend
1. En **Clients**, clic en **Create client**
2. Configurar:
- **Client ID**: `anexo76-frontend`
@@ -51,13 +63,13 @@ Esta guía te ayudará a configurar Keycloak para usar con Anexo76.
- Clic en **Next**
4. En "Login settings":
- **Root URL**: `http://localhost:5173`
- **Valid redirect URIs**:
- **Valid redirect URIs**:
- `http://localhost:5173/*`
- `http://localhost:3000/*`
- **Valid post logout redirect URIs**:
- **Valid post logout redirect URIs**:
- `http://localhost:5173/*`
- `http://localhost:3000/*`
- **Web origins**:
- **Web origins**:
- `http://localhost:5173`
- `http://localhost:3000`
- Clic en **Save**
@@ -65,6 +77,7 @@ Esta guía te ayudará a configurar Keycloak para usar con Anexo76.
## 4. Crear Usuario de Prueba
### Crear Usuario
1. En el menú izquierdo, ir a **Users**
2. Clic en **Add user**
3. Configurar:
@@ -76,6 +89,7 @@ Esta guía te ayudará a configurar Keycloak para usar con Anexo76.
- Clic en **Create**
### Establecer Contraseña
1. Ir a la pestaña **Credentials**
2. Clic en **Set password**
3. Configurar:
@@ -85,6 +99,7 @@ Esta guía te ayudará a configurar Keycloak para usar con Anexo76.
4. Clic en **Save**
### Agregar Atributo tenant_id
1. En el mismo usuario, ir a la pestaña **Attributes**
2. Clic en **Add an attribute**
3. Configurar:
@@ -93,6 +108,7 @@ Esta guía te ayudará a configurar Keycloak para usar con Anexo76.
4. Clic en **Save**
### Asignar Roles
1. Ir a la pestaña **Role mappings**
2. En "Available roles", buscar y asignar:
- `admin` (si existe)
@@ -126,6 +142,7 @@ Repetir para el cliente `anexo76-frontend` si es necesario.
## 6. Verificar Configuración
### Probar desde el Frontend
1. Abrir http://localhost:5173
2. Hacer clic en "Iniciar Sesión"
3. Ingresar credenciales:
@@ -134,6 +151,7 @@ Repetir para el cliente `anexo76-frontend` si es necesario.
4. Deberías ver el dashboard con información del usuario y licencia
### Probar desde el API
```bash
# Obtener token
curl -X POST http://localhost:8080/realms/master/protocol/openid-connect/token \
@@ -152,11 +170,13 @@ curl -X GET http://localhost:8000/v1/auth/me \
## 7. Configuración Adicional (Opcional)
### Personalizar Tema de Login
1. Ir a **Realm settings** → **Themes**
2. Seleccionar tema de login deseado
3. Guardar cambios
### Configurar Timeout de Sesión
1. Ir a **Realm settings** → **Sessions**
2. Ajustar:
- **SSO Session Idle**: Tiempo de inactividad antes de expirar (ej: 30 minutos)
@@ -164,6 +184,7 @@ curl -X GET http://localhost:8000/v1/auth/me \
3. Guardar cambios
### Habilitar Registro de Usuarios (Opcional)
1. Ir a **Realm settings** → **Login**
2. Activar **User registration**
3. Guardar cambios
@@ -171,20 +192,24 @@ curl -X GET http://localhost:8000/v1/auth/me \
## Troubleshooting
### Error: "Invalid redirect URI"
- Verificar que las URIs en el cliente coincidan exactamente
- Incluir el protocolo (http:// o https://)
- Incluir el puerto si es necesario
### Error: "Client not found"
- Verificar que el Client ID sea exacto
- Verificar que el realm sea correcto
### Token no incluye tenant_id
- Verificar que el usuario tenga el atributo configurado
- Verificar que el mapper esté configurado correctamente
- Probar obteniendo un nuevo token
### Usuario no puede hacer login
- Verificar que el usuario esté habilitado (User enabled: ON)
- Verificar que el email esté verificado (Email verified: ON)
- Verificar que la contraseña no sea temporal

View File

@@ -1,174 +0,0 @@
# Módulos A76 Implementados - Anexo 76
**Fecha de implementación:** 4 de noviembre de 2025
---
## ✨ Nuevas Funcionalidades
### Módulo de Empresa (Company)
- Gestión de empresa única con información comercial completa
- Manejo de datos fiscales y operativos centralizados
### Módulo de Clientes y Proveedores (Client & Provider)
- Gestión integral de clientes y proveedores
- Relaciones con direcciones y programas asociados
- Capacidad de diferenciar entre clientes y proveedores
### Módulo de Partes (GParts)
- Gestión de partes/componentes para los sistemas SCAII, SCAF y WINSAAI
- Control de inventario y clasificación arancelaria
- Información regulatoria y de cumplimiento
### Módulo de Clases (Class)
- Clasificaciones para sistemas SCAII y SCAF
- Información arancelaria detallada
- Gestión de fracciones arancelarias y materiales
---
## 🔗 Relaciones de Base de Datos
### Relaciones Principales
- **Part ↔ Class**: Relación de clave compuesta (client_id, part_class ↔ class_code)
- **Part → Country**: Clave foránea a public.countries (country_of_origin)
- **Part → CurrencyType**: Clave foránea a public.currency_types (currency_key)
- **Class → MaterialType**: Clave foránea a public.material_types (material_key)
### Esquema de Relaciones
```
Part (Partes)
├── País de origen → Country
├── Tipo de moneda → CurrencyType
└── Información de clase → Class
└── Tipo de material → MaterialType
```
---
## 📊 Endpoints de API Agregados
### Módulo Empresa (`/company`)
| Método | Endpoint | Descripción |
|--------|----------|-------------|
| POST | `/` | Crear empresa |
| GET | `/` | Obtener información de la empresa |
### Módulo Clientes y Proveedores (`/clients-providers`)
| Método | Endpoint | Descripción |
|--------|----------|-------------|
| POST | `/` | Crear cliente/proveedor |
| GET | `/` | Listar todos con paginación |
| GET | `/clients` | Listar solo clientes |
| GET | `/providers` | Listar solo proveedores |
| GET | `/search/rfc/{rfc}` | Buscar por RFC |
| GET | `/{client_id}` | Obtener por ID |
| PUT | `/{client_id}` | Actualizar cliente/proveedor |
| DELETE | `/{client_id}` | Eliminar cliente/proveedor |
| PATCH | `/{client_id}/toggle-status` | Cambiar estatus |
| GET | `/{client_id}/address` | Obtener información de dirección |
| GET | `/{client_id}/programs` | Obtener información de programas |
| GET | `/{client_id}/basic` | Obtener información básica |
### Módulo Partes (`/parts`)
| Método | Endpoint | Descripción |
|--------|----------|-------------|
| POST | `/` | Crear parte |
| GET | `/` | Listar todas con paginación y filtros |
| GET | `/client/{client_id}` | Obtener partes por cliente |
| GET | `/search/fraction/{fraction}` | Buscar por fracción arancelaria |
| GET | `/search/supplier/{supplier}` | Buscar por proveedor |
| GET | `/search/country/{country_code}` | Buscar por país |
| GET | `/statistics` | Obtener estadísticas de partes |
| GET | `/{client_id}/{part_number}` | Obtener parte específica |
| PUT | `/{client_id}/{part_number}` | Actualizar parte |
| DELETE | `/{client_id}/{part_number}` | Eliminar parte |
| PATCH | `/{client_id}/{part_number}/toggle-status` | Cambiar estatus |
| GET | `/{client_id}/{part_number}/basic` | Obtener información básica |
| GET | `/{client_id}/{part_number}/regulatory` | Obtener información regulatoria |
### Módulo Clases (`/classes`)
| Método | Endpoint | Descripción |
|--------|----------|-------------|
| POST | `/` | Crear clase |
| GET | `/` | Listar todas con paginación y filtros |
| GET | `/client/{client_id}` | Obtener clases por cliente |
| GET | `/search/fraction/{fraction}` | Buscar por fracción arancelaria |
| GET | `/search/material/{material_key}` | Buscar por material |
| GET | `/search/unit-measure/{unit_of_measure}` | Buscar por unidad de medida |
| GET | `/search/physical-review/{physical_review}` | Buscar por revisión física |
| GET | `/statistics` | Obtener estadísticas de clases |
| GET | `/{client_id}/{class_code}` | Obtener clase específica |
| PUT | `/{client_id}/{class_code}` | Actualizar clase |
| DELETE | `/{client_id}/{class_code}` | Eliminar clase |
| GET | `/{client_id}/{class_code}/basic` | Obtener información básica |
| GET | `/{client_id}/{class_code}/tariff` | Obtener información arancelaria |
---
## 🏗️ Arquitectura Implementada
### Diseño Modular
- **Modelos**: Definición de entidades ORM con SQLAlchemy
- **DTOs**: Objetos de transferencia de datos con validación Pydantic
- **Servicios**: Lógica de negocio y operaciones de base de datos
- **Rutas**: Endpoints REST API con documentación automática
### Características Técnicas
- **Nombres de campos en inglés** para consistencia internacional
- **Claves primarias compuestas** donde es aplicable
- **Operaciones CRUD completas** con endpoints de búsqueda especializados
- **Relaciones SQLAlchemy** con restricciones de clave foránea apropiadas
- **DTOs type-safe** con validación Pydantic
### Patrones de Desarrollo
- Estructura consistente en todos los módulos para facilitar mantenimiento
- Separación clara de responsabilidades (models, DTOs, services, routes)
- Validación de datos en múltiples capas
- Manejo de errores estandarizado
- Documentación automática con FastAPI/OpenAPI
---
## 📝 Documentación
### Archivos de Documentación
- **RELATIONSHIPS.md**: Documentación completa de relaciones de base de datos
- **Type hints detallados** en todos los métodos de servicio
- **Comentarios explicativos** en modelos y funciones complejas
### Estándares de Código
- Consistencia en patrones de desarrollo entre módulos
- Nomenclatura estandarizada para endpoints y funciones
- Validación robusta de datos de entrada y salida
- Manejo de excepciones centralizado
---
## 📈 Resumen de Implementación
### Números Totales
- **4 módulos completos** implementados
- **42+ endpoints** REST API disponibles
- **23 archivos nuevos** agregados al proyecto
- **2,798+ líneas de código** implementadas
### Estado del Proyecto
- ✅ Modelos de base de datos implementados
- ✅ Relaciones entre entidades establecidas
- ✅ DTOs con validación completa
- ✅ Servicios con lógica de negocio
- ✅ Endpoints REST API funcionales
- ✅ Integración en router principal
- ⏳ Migraciones de base de datos (pendiente)
### Próximos Pasos
1. Crear migraciones de Alembic para las nuevas tablas
2. Implementar tests unitarios para cada módulo
3. Agregar documentación de API con ejemplos
4. Implementar autenticación y autorización
5. Optimizar consultas de base de datos
---
*Documento generado automáticamente el 4 de noviembre de 2025*

View File

@@ -1,107 +0,0 @@
# Relaciones entre Modelos A76
## Resumen de Relaciones Establecidas
### Part (Tabla: parts)
El modelo `Part` representa las partes/componentes en los sistemas SCAII, SCAF y WINSAAI.
#### Relaciones:
1. **Con Country (public.countries)**
- Campo: `country_of_origin``countries.m3_key`
- Relación: Many-to-One
- Propósito: País de origen de la parte
2. **Con CurrencyType (public.currency_types)**
- Campo: `currency_key``currency_types.code`
- Relación: Many-to-One
- Propósito: Tipo de moneda para el costo unitario
3. **Con Class (classes)**
- Campos: `(client_id, part_class)``(client_id, class_code)`
- Relación: Many-to-One (usando primaryjoin complejo)
- Propósito: Clasificación de la parte
- Atributo: `part_class_info`
### Class (Tabla: classes)
El modelo `Class` representa las clases de clasificación en sistemas SCAII y SCAF.
#### Relaciones:
1. **Con MaterialType (public.material_types)**
- Campo: `material_key``material_types.key`
- Relación: Many-to-One
- Propósito: Tipo de material de la clase
2. **Con Part (parts)**
- Campos: `(client_id, class_code)``(client_id, part_class)`
- Relación: One-to-Many (inversa de la relación en Part)
- Propósito: Partes que pertenecen a esta clase
- Atributo: `parts`
## Esquema de Relaciones
```
Part
├── country (Country) # País de origen
├── currency (CurrencyType) # Tipo de moneda
└── part_class_info (Class) # Información de clasificación
└── material_type (MaterialType) # Tipo de material
Class
├── material_type (MaterialType) # Tipo de material
└── parts (List[Part]) # Partes que usan esta clase
```
## Uso de las Relaciones
### En consultas:
```python
# Obtener una parte con su información completa
part = session.query(Part).options(
joinedload(Part.country),
joinedload(Part.currency),
joinedload(Part.part_class_info).joinedload(Class.material_type)
).filter(
Part.client_id == 1,
Part.part_number == "PART001"
).first()
# Acceder a los datos relacionados
print(f"País: {part.country.description_es}")
print(f"Moneda: {part.currency.currency_name}")
print(f"Clase: {part.part_class_info.description_spanish}")
print(f"Material: {part.part_class_info.material_type.description}")
```
### En DTOs:
Los DTOs pueden incluir información relacionada:
```python
class PartDetailResponseDTO(BaseModel):
client_id: int
part_number: str
description_spanish: Optional[str]
country_name: Optional[str] = None
currency_name: Optional[str] = None
class_description: Optional[str] = None
material_type: Optional[str] = None
```
## Consideraciones Técnicas
1. **Composite Foreign Keys**: La relación entre `Part` y `Class` usa claves foráneas compuestas que requieren `primaryjoin` personalizado.
2. **Viewonly Relationships**: Algunas relaciones están marcadas como `viewonly=True` para evitar problemas de escritura accidental.
3. **Lazy Loading**: Por defecto, las relaciones usan lazy loading. Para consultas que necesiten datos relacionados, usar `joinedload` o `selectinload`.
4. **Type Hints**: Se usan `TYPE_CHECKING` imports para evitar import circulares mientras se mantienen los type hints.
## Futuras Relaciones
Potenciales relaciones adicionales que se pueden agregar:
1. **Con Sectors** (public.sectors) - para clasificación sectorial
2. **Con Transport Types** (public.transport_types) - para modo de transporte
3. **Con Customs Sections** (public.customs_sections) - para sección aduanera
4. **Relaciones con tablas subsidiarias** como `SPartes`, `QPartes`, etc.

View File

@@ -1,126 +0,0 @@
# Actualización de Schemas A76
**Fecha de actualización:** 5 de noviembre de 2025
---
## ✅ Modelos Actualizados al Schema A76
Se han actualizado todos los modelos en `api/v1/modules/a76/` para usar el schema `a76` en PostgreSQL.
### 📋 Tablas Configuradas
| Módulo | Tabla | Schema | Estado |
|--------|-------|---------|---------|
| **Company** | `company` | `a76` | ✅ Actualizada |
| **Client & Provider** | `client_provider` | `a76` | ✅ Actualizada |
| **Client & Provider** | `client_provider_address` | `a76` | ✅ Actualizada |
| **Client & Provider** | `client_provider_programs` | `a76` | ✅ Actualizada |
| **GParts** | `parts` | `a76` | ✅ Actualizada |
| **Class** | `classes` | `a76` | ✅ Actualizada |
| **Licenses** | `licenses` | `a76` | ✅ Ya estaba |
| **Licenses** | `license_usage` | `a76` | ✅ Ya estaba |
| **Tenants** | `tenants` | `a76` | ✅ Ya estaba |
### 🔄 Cambios Realizados
#### 1. Configuración de Schema
```python
# ANTES
class Company(Base):
__tablename__ = "company"
# DESPUÉS
class Company(Base):
__tablename__ = "company"
__table_args__ = {"schema": "a76"}
```
#### 2. Foreign Keys Actualizadas
```python
# ANTES
client_id = Column(String(8), ForeignKey('client_provider.client_id'), ...)
# DESPUÉS
client_id = Column(String(8), ForeignKey('a76.client_provider.client_id'), ...)
```
### 🏗️ Estructura de Schemas
```
PostgreSQL Database
├── Schema: public
│ ├── countries
│ ├── currency_types
│ ├── material_types
│ └── ... (reference data)
└── Schema: a76
├── tenants
├── licenses
├── license_usage
├── company
├── client_provider
├── client_provider_address
├── client_provider_programs
├── parts
└── classes
```
### 🔗 Relaciones Mantenidas
Las relaciones entre schemas funcionan correctamente:
- **A76 → Public**: Los modelos A76 pueden referenciar datos de referencia en `public`
- **A76 → A76**: Las relaciones internas del schema A76 están actualizadas
- **Composite Keys**: Las relaciones con claves compuestas funcionan correctamente
#### Ejemplos de Relaciones Cross-Schema:
```python
# Part (a76) → Country (public)
country_of_origin = Column(String(3), ForeignKey('public.countries.m3_key'))
# Part (a76) → CurrencyType (public)
currency_key = Column(String(3), ForeignKey('public.currency_types.code'))
# Class (a76) → MaterialType (public)
material_key = Column(String(10), ForeignKey('public.material_types.key'))
```
### 🎯 Beneficios de la Separación
1. **Organización**: Datos de negocio separados de datos de referencia
2. **Seguridad**: Permisos granulares por schema
3. **Mantenimiento**: Facilita respaldos y migraciones selectivas
4. **Escalabilidad**: Permite distribuir schemas en el futuro
5. **Claridad**: Separación lógica de responsabilidades
### ⚠️ Consideraciones Importantes
1. **Migraciones**: Las nuevas migraciones deben especificar el schema `a76`
2. **Permisos DB**: El usuario de base de datos necesita permisos en ambos schemas
3. **Testing**: Los tests deben considerar la estructura de schemas
4. **Backup**: Configurar respaldos para incluir ambos schemas
### 📝 Próximos Pasos
1. **Crear migraciones de Alembic** con el schema correcto
2. **Verificar permisos** de base de datos para el usuario de aplicación
3. **Actualizar tests** para considerar la estructura de schemas
4. **Documentar convenciones** de naming para futuros modelos
---
### 🔧 Comando de Verificación
Para verificar que todos los modelos tienen el schema correcto:
```bash
grep -r "__table_args__ = {\"schema\": \"a76\"}" backend/api/v1/modules/a76/*/models.py
```
**Resultado esperado:** 8 coincidencias (una por cada modelo A76)
---
*Actualización completada el 5 de noviembre de 2025*