diff --git a/README.md b/README.md index 927f2561..8c54b962 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,13 @@ # Anexo76 -**Aplicación SaaS para gestión de comercio exterior conforme a Anexos 24, 31 y 22 del SAT** +**Aplicación SaaS para gestión de comercio exterior conforme a Anexos 24, 30 y 22 del SAT** Anexo76 es una plataforma multi-tenant diseñada para maquilas, empresas IMMEX y agentes aduanales, que permite gestionar inventarios, pedimentos y facturas de importación/exportación con control de licencias y cumplimiento normativo. ## 🏗️ Arquitectura ### Backend + - **Framework**: FastAPI 0.110+ - **Autenticación**: Keycloak (OpenID Connect) - **Base de Datos**: PostgreSQL con SQLAlchemy @@ -20,11 +21,13 @@ Anexo76 es una plataforma multi-tenant diseñada para maquilas, empresas IMMEX y - `routes.py`: Endpoints API ### Frontend + - **Framework**: SvelteKit - **Autenticación**: keycloak-js - **UI**: Dashboard moderno y responsivo ### Infraestructura + - **Containerización**: Docker / Docker Compose - **Orquestación**: Kubernetes (futuro) - **Monitoreo**: Prometheus + Grafana @@ -64,6 +67,7 @@ anexo76/ ## 🚀 Inicio Rápido ### Requisitos Previos + - Docker y Docker Compose - Python 3.11+ (para desarrollo local) - Node.js 18+ (para desarrollo frontend) @@ -89,6 +93,7 @@ docker-compose up -d ``` Esto iniciará: + - **PostgreSQL** en `localhost:5432` - **Keycloak** en `localhost:8080` - **Backend API** en `localhost:8000` @@ -150,17 +155,20 @@ npm run dev ## 📦 Módulos Principales ### 1. **Auth** (`/v1/auth`) + - Login con Keycloak - Refresh token - Logout - Información de usuario ### 2. **Tenants** (`/v1/tenants`) + - Creación y gestión de tenants - Upgrade de BD compartida a dedicada - Gestión de realms de Keycloak ### 3. **Licenses** (`/v1/licenses`) + - Control de planes (Free, Basic, Professional, Enterprise) - Validación de licencias activas - Tracking de uso (usuarios, storage, operaciones) @@ -188,11 +196,13 @@ npm run dev ### Modelo Híbrido **BD Compartida** (tenants pequeños/medianos): + - Tabla única con `tenant_id` como foreign key - Row-level security - Más económico para clientes con bajo volumen **BD Dedicada** (tenants enterprise): + - Base de datos PostgreSQL independiente - Máximo aislamiento y performance - Configuración almacenada en `tenants.db_config` @@ -219,16 +229,18 @@ service.upgrade_to_dedicated(tenant_id=123, db_config=db_config) ### Planes Disponibles -| Plan | Usuarios | Storage | Operaciones/mes | Features | -|------|----------|---------|-----------------|----------| -| Free | 5 | 10 GB | 1,000 | API básica | -| Basic | 20 | 50 GB | 10,000 | + Reportes | -| Professional | 100 | 200 GB | 50,000 | + Integraciones | -| Enterprise | Ilimitado | Ilimitado | Ilimitado | + Soporte dedicado + BD dedicada | + +| Plan | Usuarios | Storage | Operaciones/mes | Features | +| ------------ | --------- | --------- | --------------- | -------------------------------- | +| Free | 5 | 10 GB | 1,000 | API básica | +| Basic | 20 | 50 GB | 10,000 | + Reportes | +| Professional | 100 | 200 GB | 50,000 | + Integraciones | +| Enterprise | Ilimitado | Ilimitado | Ilimitado | + Soporte dedicado + BD dedicada | ### Middleware de Validación El `LicenseValidationMiddleware` verifica en cada request: + - ✅ Licencia activa - ✅ No expirada - ✅ Límites no excedidos @@ -254,6 +266,7 @@ npm test ### Prometheus Metrics El backend expone métricas en `/metrics`: + - Request duration - Request count por endpoint - Error rate @@ -262,6 +275,7 @@ El backend expone métricas en `/metrics`: ### Logging Logs estructurados con nivel configurable: + - INFO: Operaciones normales - WARNING: Validaciones fallidas - ERROR: Errores de sistema @@ -282,6 +296,7 @@ Este proyecto es privado y propietario. ## 📞 Soporte Para soporte técnico o consultas: + - Email: soporte@anexo76.com - Documentación: https://docs.anexo76.com diff --git a/backend/alembic/versions/03b786378f94_pedimentos.py b/backend/alembic/versions/03b786378f94_pedimentos.py deleted file mode 100644 index be9283e3..00000000 --- a/backend/alembic/versions/03b786378f94_pedimentos.py +++ /dev/null @@ -1,424 +0,0 @@ -"""Pedimentos - -Revision ID: 03b786378f94 -Revises: 7937209f9718 -Create Date: 2025-11-06 17:07:16.536298 - -""" -from typing import Sequence, Union - -from alembic import op -import sqlalchemy as sa - - -# revision identifiers, used by Alembic. -revision: str = '03b786378f94' -down_revision: Union[str, Sequence[str], None] = '54f2046774d0' -branch_labels: Union[str, Sequence[str], None] = None -depends_on: Union[str, Sequence[str], None] = None - - -def upgrade() -> None: - """Upgrade schema.""" - # ### commands auto generated by Alembic - please adjust! ### - op.create_table('pedimentos', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('tenant_id', sa.Integer(), nullable=False), - sa.Column('year', sa.String(length=2), nullable=True), - sa.Column('customs_office', sa.String(length=2), nullable=True), - sa.Column('license', sa.String(length=4), nullable=True), - sa.Column('pedimento_number', sa.String(length=7), nullable=True), - sa.Column('client_id', sa.Integer(), nullable=True), - sa.Column('operation_type', sa.Integer(), nullable=True), - sa.Column('pedimento_type', sa.Integer(), nullable=True), - sa.Column('pedimento_key', sa.String(length=2), nullable=True), - sa.Column('regime', sa.String(length=3), nullable=True), - sa.Column('status', sa.String(length=30), nullable=True), - sa.Column('usd_value', sa.Numeric(precision=17, scale=6), nullable=True), - sa.Column('paid_price', sa.Numeric(precision=17, scale=6), nullable=True), - sa.Column('gross_weight', sa.Numeric(precision=19, scale=3), nullable=True), - sa.Column('exchange_rate', sa.Numeric(precision=9, scale=5), nullable=True), - sa.Column('created_at', sa.DateTime(), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=True), - sa.ForeignKeyConstraint(['tenant_id'], ['a76.tenants.id'], ), - sa.PrimaryKeyConstraint('id', name='pedimentos_pkey'), - schema='a76' - ) - op.create_index('idx_pedimentos_client_id', 'pedimentos', ['client_id'], unique=False, schema='a76') - op.create_index('idx_pedimentos_created_at', 'pedimentos', ['created_at'], unique=False, schema='a76') - op.create_index('idx_pedimentos_status', 'pedimentos', ['status'], unique=False, schema='a76') - op.create_index(op.f('ix_a76_pedimentos_tenant_id'), 'pedimentos', ['tenant_id'], unique=False, schema='a76') - op.create_table('pedimento_config_additional', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('pedimento_id', sa.Integer(), nullable=False), - sa.Column('tenant_id', sa.Integer(), nullable=False), - sa.Column('add_po_identifier', sa.SmallInteger(), nullable=True), - sa.Column('do_not_exempt_norms_complement_x', sa.SmallInteger(), nullable=True), - sa.Column('manual_pedimento_year', sa.String(length=2), nullable=True), - sa.Column('enable_import_invoice_recipient', sa.SmallInteger(), nullable=True), - sa.Column('send_502_validation_file_for_consolidated', sa.SmallInteger(), nullable=True), - sa.Column('add_remove_norms', sa.SmallInteger(), nullable=True), - sa.Column('created_at', sa.DateTime(), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=True), - sa.ForeignKeyConstraint(['pedimento_id'], ['a76.pedimentos.id'], name='fk_pedimento_config_additional', ondelete='CASCADE'), - sa.ForeignKeyConstraint(['tenant_id'], ['a76.tenants.id'], ), - sa.PrimaryKeyConstraint('id', name='pedimento_config_additional_pkey'), - sa.UniqueConstraint('pedimento_id', name='pedimento_config_additional_pedimento_id_key'), - schema='a76' - ) - op.create_index(op.f('ix_a76_pedimento_config_additional_tenant_id'), 'pedimento_config_additional', ['tenant_id'], unique=False, schema='a76') - op.create_table('pedimento_config_calculations', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('pedimento_id', sa.Integer(), nullable=False), - sa.Column('tenant_id', sa.Integer(), nullable=False), - sa.Column('dta_type', sa.String(length=1), nullable=True), - sa.Column('dta_operation', sa.SmallInteger(), nullable=True), - sa.Column('dta_vehicle_count', sa.SmallInteger(), nullable=True), - sa.Column('dta_mixed_rate_8permil', sa.SmallInteger(), nullable=True), - sa.Column('pays_vat', sa.SmallInteger(), nullable=True), - sa.Column('pays_prevalidation', sa.SmallInteger(), nullable=True), - sa.Column('include_sagar_certificate_fee', sa.SmallInteger(), nullable=True), - sa.Column('fixed_vehicle_dta_fee', sa.SmallInteger(), nullable=True), - sa.Column('additional_fixed_fee', sa.SmallInteger(), nullable=True), - sa.Column('additional_fixed_fee_payment_method', sa.SmallInteger(), nullable=True), - sa.Column('created_at', sa.DateTime(), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=True), - sa.ForeignKeyConstraint(['pedimento_id'], ['a76.pedimentos.id'], name='fk_pedimento_config_calculations', ondelete='CASCADE'), - sa.ForeignKeyConstraint(['tenant_id'], ['a76.tenants.id'], ), - sa.PrimaryKeyConstraint('id', name='pedimento_config_calculations_pkey'), - sa.UniqueConstraint('pedimento_id', name='pedimento_config_calculations_pedimento_id_key'), - schema='a76' - ) - op.create_index(op.f('ix_a76_pedimento_config_calculations_tenant_id'), 'pedimento_config_calculations', ['tenant_id'], unique=False, schema='a76') - op.create_table('pedimento_config_parameters', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('pedimento_id', sa.Integer(), nullable=False), - sa.Column('tenant_id', sa.Integer(), nullable=False), - sa.Column('is_embassy', sa.SmallInteger(), nullable=True), - sa.Column('embassy_dta', sa.Numeric(precision=11, scale=2), nullable=True), - sa.Column('rule_3121_section_ii', sa.SmallInteger(), nullable=True), - sa.Column('use_previous_tariff', sa.SmallInteger(), nullable=True), - sa.Column('use_payment_date_fi', sa.SmallInteger(), nullable=True), - sa.Column('add_state_supplier_record_505', sa.SmallInteger(), nullable=True), - sa.Column('customs_value_calculation', sa.SmallInteger(), nullable=True), - sa.Column('two_decimals_unit_value', sa.SmallInteger(), nullable=True), - sa.Column('customs_value_per_item', sa.SmallInteger(), nullable=True), - sa.Column('is_national_supplier', sa.SmallInteger(), nullable=True), - sa.Column('is_consolidated', sa.SmallInteger(), nullable=True), - sa.Column('created_at', sa.DateTime(), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=True), - sa.ForeignKeyConstraint(['pedimento_id'], ['a76.pedimentos.id'], name='fk_pedimento_config_parameters', ondelete='CASCADE'), - sa.ForeignKeyConstraint(['tenant_id'], ['a76.tenants.id'], ), - sa.PrimaryKeyConstraint('id', name='pedimento_config_parameters_pkey'), - sa.UniqueConstraint('pedimento_id', name='pedimento_config_parameters_pedimento_id_key'), - schema='a76' - ) - op.create_index(op.f('ix_a76_pedimento_config_parameters_tenant_id'), 'pedimento_config_parameters', ['tenant_id'], unique=False, schema='a76') - op.create_table('pedimento_config_surcharges', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('pedimento_id', sa.Integer(), nullable=False), - sa.Column('tenant_id', sa.Integer(), nullable=False), - sa.Column('surcharge_igi', sa.SmallInteger(), nullable=True), - sa.Column('surcharge_dta', sa.SmallInteger(), nullable=True), - sa.Column('surcharge_vat', sa.SmallInteger(), nullable=True), - sa.Column('surcharge_isan', sa.SmallInteger(), nullable=True), - sa.Column('surcharge_ieps', sa.SmallInteger(), nullable=True), - sa.Column('surcharge_cc', sa.SmallInteger(), nullable=True), - sa.Column('created_at', sa.DateTime(), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=True), - sa.ForeignKeyConstraint(['pedimento_id'], ['a76.pedimentos.id'], name='fk_pedimento_config_surcharges', ondelete='CASCADE'), - sa.ForeignKeyConstraint(['tenant_id'], ['a76.tenants.id'], ), - sa.PrimaryKeyConstraint('id', name='pedimento_config_surcharges_pkey'), - sa.UniqueConstraint('pedimento_id', name='pedimento_config_surcharges_pedimento_id_key'), - schema='a76' - ) - op.create_index(op.f('ix_a76_pedimento_config_surcharges_tenant_id'), 'pedimento_config_surcharges', ['tenant_id'], unique=False, schema='a76') - op.create_table('pedimento_config_update_rectification', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('pedimento_id', sa.Integer(), nullable=False), - sa.Column('tenant_id', sa.Integer(), nullable=False), - sa.Column('update_vat', sa.SmallInteger(), nullable=True), - sa.Column('update_advalorem', sa.SmallInteger(), nullable=True), - sa.Column('update_cc', sa.SmallInteger(), nullable=True), - sa.Column('update_ieps', sa.SmallInteger(), nullable=True), - sa.Column('calculate_surcharge', sa.SmallInteger(), nullable=True), - sa.Column('created_at', sa.DateTime(), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=True), - sa.ForeignKeyConstraint(['pedimento_id'], ['a76.pedimentos.id'], name='fk_pedimento_config_update_rectification', ondelete='CASCADE'), - sa.ForeignKeyConstraint(['tenant_id'], ['a76.tenants.id'], ), - sa.PrimaryKeyConstraint('id', name='pedimento_config_update_rectification_pkey'), - sa.UniqueConstraint('pedimento_id', name='pedimento_config_update_rectification_pedimento_id_key'), - schema='a76' - ) - op.create_index(op.f('ix_a76_pedimento_config_update_rectification_tenant_id'), 'pedimento_config_update_rectification', ['tenant_id'], unique=False, schema='a76') - op.create_table('pedimento_config_updates', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('pedimento_id', sa.Integer(), nullable=False), - sa.Column('tenant_id', sa.Integer(), nullable=False), - sa.Column('update_vat', sa.SmallInteger(), nullable=True), - sa.Column('update_advalorem', sa.SmallInteger(), nullable=True), - sa.Column('update_cc', sa.SmallInteger(), nullable=True), - sa.Column('update_ieps', sa.SmallInteger(), nullable=True), - sa.Column('created_at', sa.DateTime(), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=True), - sa.ForeignKeyConstraint(['pedimento_id'], ['a76.pedimentos.id'], name='fk_pedimento_config_updates', ondelete='CASCADE'), - sa.ForeignKeyConstraint(['tenant_id'], ['a76.tenants.id'], ), - sa.PrimaryKeyConstraint('id', name='pedimento_config_updates_pkey'), - sa.UniqueConstraint('pedimento_id', name='pedimento_config_updates_pedimento_id_key'), - schema='a76' - ) - op.create_index(op.f('ix_a76_pedimento_config_updates_tenant_id'), 'pedimento_config_updates', ['tenant_id'], unique=False, schema='a76') - op.create_table('pedimento_customs_offices', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('pedimento_id', sa.Integer(), nullable=False), - sa.Column('tenant_id', sa.Integer(), nullable=False), - sa.Column('dispatch_customs', sa.String(length=3), nullable=True), - sa.Column('entry_exit_customs', sa.String(length=3), nullable=True), - sa.Column('created_at', sa.DateTime(), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=True), - sa.ForeignKeyConstraint(['pedimento_id'], ['a76.pedimentos.id'], name='fk_pedimento_customs_offices', ondelete='CASCADE'), - sa.ForeignKeyConstraint(['tenant_id'], ['a76.tenants.id'], ), - sa.PrimaryKeyConstraint('id', name='pedimento_customs_offices_pkey'), - sa.UniqueConstraint('pedimento_id', name='pedimento_customs_offices_pedimento_id_key'), - schema='a76' - ) - op.create_index(op.f('ix_a76_pedimento_customs_offices_tenant_id'), 'pedimento_customs_offices', ['tenant_id'], unique=False, schema='a76') - op.create_table('pedimento_dates', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('pedimento_id', sa.Integer(), nullable=False), - sa.Column('tenant_id', sa.Integer(), nullable=False), - sa.Column('entry_date', sa.DateTime(), nullable=True), - sa.Column('pedimento_date', sa.DateTime(), nullable=True), - sa.Column('payment_date', sa.DateTime(), nullable=True), - sa.Column('rectification_payment_date', sa.DateTime(), nullable=True), - sa.Column('extraction_date', sa.DateTime(), nullable=True), - sa.Column('submission_date', sa.DateTime(), nullable=True), - sa.Column('eucan_date', sa.DateTime(), nullable=True), - sa.Column('original_date', sa.DateTime(), nullable=True), - sa.Column('start_date', sa.DateTime(), nullable=True), - sa.Column('end_date', sa.DateTime(), nullable=True), - sa.Column('capture_date', sa.DateTime(), nullable=True), - sa.Column('capture_time', sa.Time(), nullable=True), - sa.Column('created_at', sa.DateTime(), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=True), - sa.ForeignKeyConstraint(['pedimento_id'], ['a76.pedimentos.id'], name='fk_pedimento_dates', ondelete='CASCADE'), - sa.ForeignKeyConstraint(['tenant_id'], ['a76.tenants.id'], ), - sa.PrimaryKeyConstraint('id', name='pedimento_dates_pkey'), - sa.UniqueConstraint('pedimento_id', name='pedimento_dates_pedimento_id_key'), - schema='a76' - ) - op.create_index('idx_pedimento_dates_pedimento_id', 'pedimento_dates', ['pedimento_id'], unique=False, schema='a76') - op.create_index(op.f('ix_a76_pedimento_dates_tenant_id'), 'pedimento_dates', ['tenant_id'], unique=False, schema='a76') - op.create_table('pedimento_decrementables', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('pedimento_id', sa.Integer(), nullable=False), - sa.Column('tenant_id', sa.Integer(), nullable=False), - sa.Column('freight', sa.Numeric(precision=13, scale=2), nullable=True), - sa.Column('insurance', sa.Numeric(precision=13, scale=2), nullable=True), - sa.Column('loading', sa.Numeric(precision=13, scale=2), nullable=True), - sa.Column('unloading', sa.Numeric(precision=13, scale=2), nullable=True), - sa.Column('others', sa.Numeric(precision=13, scale=2), nullable=True), - sa.Column('currency', sa.String(length=3), nullable=True), - sa.Column('currency_factor', sa.Numeric(precision=15, scale=8), nullable=True), - sa.Column('not_affect_usd_value', sa.SmallInteger(), nullable=True), - sa.Column('not_affect_customs_value', sa.SmallInteger(), nullable=True), - sa.Column('created_at', sa.DateTime(), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=True), - sa.ForeignKeyConstraint(['pedimento_id'], ['a76.pedimentos.id'], name='fk_pedimento_decrementables', ondelete='CASCADE'), - sa.ForeignKeyConstraint(['tenant_id'], ['a76.tenants.id'], ), - sa.PrimaryKeyConstraint('id', name='pedimento_decrementables_pkey'), - sa.UniqueConstraint('pedimento_id', name='pedimento_decrementables_pedimento_id_key'), - schema='a76' - ) - op.create_index(op.f('ix_a76_pedimento_decrementables_tenant_id'), 'pedimento_decrementables', ['tenant_id'], unique=False, schema='a76') - op.create_table('pedimento_incrementables', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('pedimento_id', sa.Integer(), nullable=False), - sa.Column('tenant_id', sa.Integer(), nullable=False), - sa.Column('insured_value', sa.Numeric(precision=13, scale=2), nullable=True), - sa.Column('freight', sa.Numeric(precision=13, scale=2), nullable=True), - sa.Column('insurance', sa.Numeric(precision=13, scale=2), nullable=True), - sa.Column('packaging', sa.Numeric(precision=13, scale=2), nullable=True), - sa.Column('others', sa.Numeric(precision=13, scale=3), nullable=True), - sa.Column('deductibles', sa.Numeric(precision=13, scale=3), nullable=True), - sa.Column('currency', sa.String(length=3), nullable=True), - sa.Column('currency_factor', sa.Numeric(precision=15, scale=8), nullable=True), - sa.Column('not_affect_usd_value', sa.SmallInteger(), nullable=True), - sa.Column('not_affect_customs_value', sa.SmallInteger(), nullable=True), - sa.Column('created_at', sa.DateTime(), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=True), - sa.ForeignKeyConstraint(['pedimento_id'], ['a76.pedimentos.id'], name='fk_pedimento_incrementables', ondelete='CASCADE'), - sa.ForeignKeyConstraint(['tenant_id'], ['a76.tenants.id'], ), - sa.PrimaryKeyConstraint('id', name='pedimento_incrementables_pkey'), - sa.UniqueConstraint('pedimento_id', name='pedimento_incrementables_pedimento_id_key'), - schema='a76' - ) - op.create_index(op.f('ix_a76_pedimento_incrementables_tenant_id'), 'pedimento_incrementables', ['tenant_id'], unique=False, schema='a76') - op.create_table('pedimento_indexes', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('pedimento_id', sa.Integer(), nullable=False), - sa.Column('tenant_id', sa.Integer(), nullable=False), - sa.Column('update_factor_type', sa.SmallInteger(), nullable=True), - sa.Column('update_factor', sa.Numeric(precision=7, scale=4), nullable=True), - sa.Column('manual_update_factor', sa.SmallInteger(), nullable=True), - sa.Column('created_at', sa.DateTime(), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=True), - sa.ForeignKeyConstraint(['pedimento_id'], ['a76.pedimentos.id'], name='fk_pedimento_indexes', ondelete='CASCADE'), - sa.ForeignKeyConstraint(['tenant_id'], ['a76.tenants.id'], ), - sa.PrimaryKeyConstraint('id', name='pedimento_indexes_pkey'), - sa.UniqueConstraint('pedimento_id', name='pedimento_indexes_pedimento_id_key'), - schema='a76' - ) - op.create_index(op.f('ix_a76_pedimento_indexes_tenant_id'), 'pedimento_indexes', ['tenant_id'], unique=False, schema='a76') - op.create_table('pedimento_payments', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('pedimento_id', sa.Integer(), nullable=False), - sa.Column('tenant_id', sa.Integer(), nullable=False), - sa.Column('acknowledgment', sa.String(length=20), nullable=True), - sa.Column('operation_number', sa.String(length=14), nullable=True), - sa.Column('bank_code', sa.Integer(), nullable=True), - sa.Column('cashier', sa.String(length=2), nullable=True), - sa.Column('date', sa.Date(), nullable=True), - sa.Column('time', sa.Time(), nullable=True), - sa.Column('shift', sa.String(length=1), nullable=True), - sa.Column('total_cash_paid', sa.Integer(), nullable=True), - sa.Column('total_contributions', sa.Integer(), nullable=True), - sa.Column('counter_payment', sa.SmallInteger(), nullable=True), - sa.Column('pece_code', sa.String(length=5), nullable=True), - sa.Column('payment_id', sa.Integer(), nullable=True), - sa.Column('created_at', sa.DateTime(), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=True), - sa.ForeignKeyConstraint(['pedimento_id'], ['a76.pedimentos.id'], name='fk_pedimento_payments', ondelete='CASCADE'), - sa.ForeignKeyConstraint(['tenant_id'], ['a76.tenants.id'], ), - sa.PrimaryKeyConstraint('id', name='pedimento_payments_pkey'), - sa.UniqueConstraint('pedimento_id', name='pedimento_payments_pedimento_id_key'), - schema='a76' - ) - op.create_index('idx_pedimento_payments_pedimento_id', 'pedimento_payments', ['pedimento_id'], unique=False, schema='a76') - op.create_index(op.f('ix_a76_pedimento_payments_tenant_id'), 'pedimento_payments', ['tenant_id'], unique=False, schema='a76') - op.create_table('pedimento_rectification_destination', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('pedimento_id', sa.Integer(), nullable=False), - sa.Column('tenant_id', sa.Integer(), nullable=False), - sa.Column('destination_pedimento_year', sa.String(length=2), nullable=True), - sa.Column('destination_customs_office', sa.String(length=3), nullable=True), - sa.Column('destination_license', sa.String(length=4), nullable=True), - sa.Column('destination_pedimento_number', sa.String(length=7), nullable=True), - sa.ForeignKeyConstraint(['pedimento_id'], ['a76.pedimentos.id'], name='fk_pedimento_rectification_destination', ondelete='CASCADE'), - sa.ForeignKeyConstraint(['tenant_id'], ['a76.tenants.id'], ), - sa.PrimaryKeyConstraint('id', name='pedimento_rectification_destination_pkey'), - sa.UniqueConstraint('pedimento_id', name='pedimento_rectification_destination_pedimento_id_key'), - schema='a76' - ) - op.create_index(op.f('ix_a76_pedimento_rectification_destination_tenant_id'), 'pedimento_rectification_destination', ['tenant_id'], unique=False, schema='a76') - op.create_table('pedimento_rectification_origin', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('pedimento_id', sa.Integer(), nullable=False), - sa.Column('tenant_id', sa.Integer(), nullable=False), - sa.Column('original_pedimento_year', sa.String(length=2), nullable=True), - sa.Column('original_customs_office', sa.String(length=3), nullable=True), - sa.Column('original_license', sa.String(length=4), nullable=True), - sa.Column('original_pedimento_number', sa.String(length=7), nullable=True), - sa.Column('original_pedimento_key', sa.String(length=2), nullable=True), - sa.Column('original_payment_date', sa.DateTime(), nullable=True), - sa.Column('total_cash', sa.Integer(), nullable=True), - sa.Column('total_others', sa.Integer(), nullable=True), - sa.Column('reason', sa.String(length=255), nullable=True), - sa.Column('charge_to_client', sa.SmallInteger(), nullable=True), - sa.Column('use_original_payment_date_for_interest_calc', sa.SmallInteger(), nullable=True), - sa.Column('manual_calculation', sa.SmallInteger(), nullable=True), - sa.Column('original_pedimento_norms', sa.SmallInteger(), nullable=True), - sa.ForeignKeyConstraint(['pedimento_id'], ['a76.pedimentos.id'], name='fk_pedimento_rectification_origin', ondelete='CASCADE'), - sa.ForeignKeyConstraint(['tenant_id'], ['a76.tenants.id'], ), - sa.PrimaryKeyConstraint('id', name='pedimento_rectification_origin_pkey'), - sa.UniqueConstraint('pedimento_id', name='pedimento_rectification_origin_pedimento_id_key'), - schema='a76' - ) - op.create_index(op.f('ix_a76_pedimento_rectification_origin_tenant_id'), 'pedimento_rectification_origin', ['tenant_id'], unique=False, schema='a76') - op.create_table('pedimento_transport_means', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('pedimento_id', sa.Integer(), nullable=False), - sa.Column('tenant_id', sa.Integer(), nullable=False), - sa.Column('destination', sa.SmallInteger(), nullable=True), - sa.Column('entry_exit', sa.String(length=2), nullable=True), - sa.Column('arrival', sa.String(length=2), nullable=True), - sa.Column('departure', sa.String(length=2), nullable=True), - sa.Column('created_at', sa.DateTime(), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=True), - sa.ForeignKeyConstraint(['pedimento_id'], ['a76.pedimentos.id'], name='fk_pedimento_transport_means', ondelete='CASCADE'), - sa.ForeignKeyConstraint(['tenant_id'], ['a76.tenants.id'], ), - sa.PrimaryKeyConstraint('id', name='pedimento_transport_means_pkey'), - sa.UniqueConstraint('pedimento_id', name='pedimento_transport_means_pedimento_id_key'), - schema='a76' - ) - op.create_index(op.f('ix_a76_pedimento_transport_means_tenant_id'), 'pedimento_transport_means', ['tenant_id'], unique=False, schema='a76') - op.create_table('pedimento_validation', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('pedimento_id', sa.Integer(), nullable=False), - sa.Column('tenant_id', sa.Integer(), nullable=False), - sa.Column('validator', sa.String(length=3), nullable=True), - sa.Column('validation_ack', sa.String(length=8), nullable=True), - sa.Column('pre_ack', sa.String(length=8), nullable=True), - sa.Column('line_signature', sa.String(length=50), nullable=True), - sa.Column('electronic_signature', sa.String(length=999), nullable=True), - sa.Column('certificate_number', sa.String(length=99), nullable=True), - sa.Column('validator_id', sa.Integer(), nullable=True), - sa.Column('responsible_id', sa.Integer(), nullable=True), - sa.Column('created_at', sa.DateTime(), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=True), - sa.ForeignKeyConstraint(['pedimento_id'], ['a76.pedimentos.id'], name='fk_pedimento_validation', ondelete='CASCADE'), - sa.ForeignKeyConstraint(['tenant_id'], ['a76.tenants.id'], ), - sa.PrimaryKeyConstraint('id', name='pedimento_validation_pkey'), - sa.UniqueConstraint('pedimento_id', name='pedimento_validation_pedimento_id_key'), - schema='a76' - ) - op.create_index(op.f('ix_a76_pedimento_validation_tenant_id'), 'pedimento_validation', ['tenant_id'], unique=False, schema='a76') - op.drop_constraint(op.f('fk_regimenped'), 'code_pedimento_regimens', type_='foreignkey') - op.drop_constraint(op.f('fk_codeped'), 'code_pedimento_regimens', type_='foreignkey') - op.create_foreign_key('fk_codeped', 'code_pedimento_regimens', 'pedimento_codes', ['pedimento_code'], ['code'], source_schema='public', referent_schema='public') - op.create_foreign_key('fk_regimenped', 'code_pedimento_regimens', 'pedimento_regimens', ['regimen_code'], ['code'], source_schema='public', referent_schema='public') - # ### end Alembic commands ### - - -def downgrade() -> None: - """Downgrade schema.""" - # ### commands auto generated by Alembic - please adjust! ### - op.drop_constraint('fk_regimenped', 'code_pedimento_regimens', schema='public', type_='foreignkey') - op.drop_constraint('fk_codeped', 'code_pedimento_regimens', schema='public', type_='foreignkey') - op.create_foreign_key(op.f('fk_codeped'), 'code_pedimento_regimens', 'pedimento_codes', ['pedimento_code'], ['code']) - op.create_foreign_key(op.f('fk_regimenped'), 'code_pedimento_regimens', 'pedimento_regimens', ['regimen_code'], ['code']) - op.drop_index(op.f('ix_a76_pedimento_validation_tenant_id'), table_name='pedimento_validation', schema='a76') - op.drop_table('pedimento_validation', schema='a76') - op.drop_index(op.f('ix_a76_pedimento_transport_means_tenant_id'), table_name='pedimento_transport_means', schema='a76') - op.drop_table('pedimento_transport_means', schema='a76') - op.drop_index(op.f('ix_a76_pedimento_rectification_origin_tenant_id'), table_name='pedimento_rectification_origin', schema='a76') - op.drop_table('pedimento_rectification_origin', schema='a76') - op.drop_index(op.f('ix_a76_pedimento_rectification_destination_tenant_id'), table_name='pedimento_rectification_destination', schema='a76') - op.drop_table('pedimento_rectification_destination', schema='a76') - op.drop_index(op.f('ix_a76_pedimento_payments_tenant_id'), table_name='pedimento_payments', schema='a76') - op.drop_index('idx_pedimento_payments_pedimento_id', table_name='pedimento_payments', schema='a76') - op.drop_table('pedimento_payments', schema='a76') - op.drop_index(op.f('ix_a76_pedimento_indexes_tenant_id'), table_name='pedimento_indexes', schema='a76') - op.drop_table('pedimento_indexes', schema='a76') - op.drop_index(op.f('ix_a76_pedimento_incrementables_tenant_id'), table_name='pedimento_incrementables', schema='a76') - op.drop_table('pedimento_incrementables', schema='a76') - op.drop_index(op.f('ix_a76_pedimento_decrementables_tenant_id'), table_name='pedimento_decrementables', schema='a76') - op.drop_table('pedimento_decrementables', schema='a76') - op.drop_index(op.f('ix_a76_pedimento_dates_tenant_id'), table_name='pedimento_dates', schema='a76') - op.drop_index('idx_pedimento_dates_pedimento_id', table_name='pedimento_dates', schema='a76') - op.drop_table('pedimento_dates', schema='a76') - op.drop_index(op.f('ix_a76_pedimento_customs_offices_tenant_id'), table_name='pedimento_customs_offices', schema='a76') - op.drop_table('pedimento_customs_offices', schema='a76') - op.drop_index(op.f('ix_a76_pedimento_config_updates_tenant_id'), table_name='pedimento_config_updates', schema='a76') - op.drop_table('pedimento_config_updates', schema='a76') - op.drop_index(op.f('ix_a76_pedimento_config_update_rectification_tenant_id'), table_name='pedimento_config_update_rectification', schema='a76') - op.drop_table('pedimento_config_update_rectification', schema='a76') - op.drop_index(op.f('ix_a76_pedimento_config_surcharges_tenant_id'), table_name='pedimento_config_surcharges', schema='a76') - op.drop_table('pedimento_config_surcharges', schema='a76') - op.drop_index(op.f('ix_a76_pedimento_config_parameters_tenant_id'), table_name='pedimento_config_parameters', schema='a76') - op.drop_table('pedimento_config_parameters', schema='a76') - op.drop_index(op.f('ix_a76_pedimento_config_calculations_tenant_id'), table_name='pedimento_config_calculations', schema='a76') - op.drop_table('pedimento_config_calculations', schema='a76') - op.drop_index(op.f('ix_a76_pedimento_config_additional_tenant_id'), table_name='pedimento_config_additional', schema='a76') - op.drop_table('pedimento_config_additional', schema='a76') - op.drop_index(op.f('ix_a76_pedimentos_tenant_id'), table_name='pedimentos', schema='a76') - op.drop_index('idx_pedimentos_status', table_name='pedimentos', schema='a76') - op.drop_index('idx_pedimentos_created_at', table_name='pedimentos', schema='a76') - op.drop_index('idx_pedimentos_client_id', table_name='pedimentos', schema='a76') - op.drop_table('pedimentos', schema='a76') - op.drop_index(op.f('ix_a76_licenses_tenant_id'), table_name='licenses', schema='a76') - op.drop_index(op.f('ix_a76_licenses_id'), table_name='licenses', schema='a76') - op.drop_table('licenses', schema='a76') - op.drop_index(op.f('ix_a76_license_usage_tenant_id'), table_name='license_usage', schema='a76') - op.drop_index(op.f('ix_a76_license_usage_id'), table_name='license_usage', schema='a76') - op.drop_table('license_usage', schema='a76') - op.drop_index(op.f('ix_a76_tenants_slug'), table_name='tenants', schema='a76') - op.drop_index(op.f('ix_a76_tenants_name'), table_name='tenants', schema='a76') - op.drop_index(op.f('ix_a76_tenants_id'), table_name='tenants', schema='a76') - op.drop_table('tenants', schema='a76') - # ### end Alembic commands ### diff --git a/backend/alembic/versions/54f2046774d0_create_new_a76_tables_only.py b/backend/alembic/versions/54f2046774d0_create_new_a76_tables_only.py deleted file mode 100644 index 272f1821..00000000 --- a/backend/alembic/versions/54f2046774d0_create_new_a76_tables_only.py +++ /dev/null @@ -1,195 +0,0 @@ -"""Create new A76 tables only - company, clients, parts, classes - -Revision ID: 54f2046774d0 -Revises: 7937209f9718 -Create Date: 2025-11-06 03:38:27.848630 - -""" -from typing import Sequence, Union - -from alembic import op -import sqlalchemy as sa - - -# revision identifiers, used by Alembic. -revision: str = '54f2046774d0' -down_revision: Union[str, Sequence[str], None] = '7937209f9718' -branch_labels: Union[str, Sequence[str], None] = None -depends_on: Union[str, Sequence[str], None] = None - - -def upgrade() -> None: - """Upgrade schema - Create only new A76 tables.""" - - # Create new A76 tables only (skip existing tenants, licenses, license_usage) - op.create_table('client_provider', - sa.Column('client_id', sa.String(length=8), nullable=False), - sa.Column('type_nat_foreign', sa.String(length=1), nullable=True), - sa.Column('name', sa.String(length=256), nullable=True), - sa.Column('short_name', sa.String(length=10), nullable=True), - sa.Column('rfc', sa.String(length=30), nullable=True), - sa.Column('curp', sa.String(length=19), nullable=True), - sa.Column('client_or_provider', sa.String(length=1), nullable=True), - sa.Column('linking', sa.String(length=1), nullable=True), - sa.Column('transform_subassembly', sa.String(length=1), nullable=True), - sa.Column('extra_information', sa.String(length=399), nullable=True), - sa.Column('web_key', sa.String(length=40), nullable=True), - sa.Column('responsible', sa.String(length=80), nullable=True), - sa.Column('position', sa.String(length=30), nullable=True), - sa.Column('incoterm', sa.String(length=19), nullable=True), - sa.Column('is_national_provider', sa.String(length=2), nullable=True), - sa.Column('enabled_disabled', sa.SmallInteger(), nullable=True), - sa.PrimaryKeyConstraint('client_id'), - schema='a76' - ) - - op.create_table('gcompany', - sa.Column('id', sa.String(length=3), nullable=False), - sa.Column('consecutive', sa.Boolean(), nullable=False), - sa.Column('name', sa.String(length=255), nullable=True), - sa.Column('rfc', sa.String(length=30), nullable=True), - sa.Column('main_activity', sa.String(length=255), nullable=True), - sa.Column('program', sa.String(length=10), nullable=True), - sa.Column('program_number', sa.String(length=40), nullable=True), - sa.Column('prosec', sa.SmallInteger(), nullable=True), - sa.Column('prosec_authorization', sa.String(length=20), nullable=True), - sa.Column('manufacturer_id', sa.String(length=25), nullable=True), - sa.Column('broker_company', sa.String(length=10), nullable=True), - sa.Column('responsible', sa.String(length=80), nullable=True), - sa.Column('responsible_name', sa.String(length=20), nullable=True), - sa.Column('responsible_last_name', sa.String(length=20), nullable=True), - sa.Column('responsible_mother_last_name', sa.String(length=20), nullable=True), - sa.Column('responsible_rfc', sa.String(length=30), nullable=True), - sa.Column('position', sa.String(length=30), nullable=True), - sa.Column('logo', sa.String(length=255), nullable=True), - sa.Column('has_express_line', sa.Boolean(), nullable=True), - sa.Column('order_format_type', sa.String(length=19), nullable=True), - sa.Column('previous_code', sa.SmallInteger(), nullable=True), - sa.Column('is_service_company', sa.Boolean(), nullable=True), - sa.Column('client_name', sa.String(length=300), nullable=True), - sa.Column('subassembly_mode', sa.String(length=7), nullable=True), - sa.Column('curp', sa.String(length=19), nullable=True), - sa.Column('inter_db_name', sa.String(length=100), nullable=True), - sa.Column('ctpat_svi', sa.String(length=100), nullable=True), - sa.Column('trusted_exporter_number', sa.String(length=50), nullable=True), - sa.Column('prevalidator_key', sa.String(length=20), nullable=True), - sa.Column('seventh_amendment', sa.Boolean(), nullable=True), - sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False), - sa.Column('updated_at', sa.DateTime(timezone=True), nullable=True), - sa.PrimaryKeyConstraint('id'), - sa.UniqueConstraint('consecutive'), - schema='a76' - ) - - op.create_table('classes', - sa.Column('client_key', sa.Integer(), nullable=False), - sa.Column('class_code', sa.String(length=8), nullable=False), - sa.Column('description_spanish', sa.String(length=500), nullable=True), - sa.Column('description_english', sa.String(length=500), nullable=True), - sa.Column('material_key', sa.String(length=10), nullable=True), - sa.Column('unit_of_measure', sa.String(length=5), nullable=True), - sa.Column('fraction', sa.String(length=10), nullable=True), - sa.Column('us_fraction', sa.String(length=16), nullable=True), - sa.Column('sub_key', sa.String(length=5), nullable=True), - sa.Column('physical_review', sa.SmallInteger(), nullable=True), - sa.Column('iva_exempt_fraction', sa.String(length=4), nullable=True), - sa.ForeignKeyConstraint(['material_key'], ['public.material_types.key'], ), - sa.PrimaryKeyConstraint('client_key', 'class_code'), - schema='a76' - ) - - op.create_table('parts', - sa.Column('client_key', sa.Integer(), nullable=False), - sa.Column('part_number', sa.String(length=49), nullable=False), - sa.Column('fraction', sa.String(length=10), nullable=True), - sa.Column('description_spanish', sa.String(length=500), nullable=True), - sa.Column('description_english', sa.String(length=500), nullable=True), - sa.Column('part_class', sa.String(length=8), nullable=True), - sa.Column('unit_of_measure', sa.String(length=5), nullable=True), - sa.Column('commercial_part_number', sa.String(length=70), nullable=True), - sa.Column('country_of_origin', sa.String(length=3), nullable=True), - sa.Column('unit_cost', sa.Numeric(precision=23, scale=8), nullable=True), - sa.Column('currency_type', sa.String(length=2), nullable=True), - sa.Column('currency_key', sa.String(length=3), nullable=True), - sa.Column('unit_weight', sa.Numeric(precision=19, scale=8), nullable=True), - sa.Column('weight_type', sa.String(length=6), nullable=True), - sa.Column('us_fraction', sa.String(length=16), nullable=True), - sa.Column('fda_key', sa.String(length=20), nullable=True), - sa.Column('fcc_key', sa.String(length=30), nullable=True), - sa.Column('license_code', sa.String(length=3), nullable=True), - sa.Column('eccn', sa.String(length=20), nullable=True), - sa.Column('export_code', sa.String(length=2), nullable=True), - sa.Column('exclusion_symbol', sa.String(length=19), nullable=True), - sa.Column('supplier', sa.String(length=14), nullable=True), - sa.Column('alternate_unit_measure', sa.String(length=14), nullable=True), - sa.Column('added_value', sa.Numeric(precision=23, scale=8), nullable=True), - sa.Column('enabled_disabled', sa.SmallInteger(), nullable=True), - sa.Column('creation_date', sa.Integer(), nullable=True), - sa.Column('modification_date', sa.Integer(), nullable=True), - sa.Column('modification_date_iso', sa.DateTime(timezone=True), nullable=True), - sa.Column('part_photo', sa.String(length=255), nullable=True), - sa.ForeignKeyConstraint(['country_of_origin'], ['public.countries.m3_key'], ), - sa.ForeignKeyConstraint(['currency_key'], ['public.currency_types.code'], ), - sa.PrimaryKeyConstraint('client_key', 'part_number'), - schema='a76' - ) - - # Create dependent tables after main tables - op.create_table('gclient_provider_address', - sa.Column('client_id', sa.String(length=8), nullable=False), - sa.Column('municipality', sa.String(length=150), nullable=True), - sa.Column('streets', sa.String(length=100), nullable=True), - sa.Column('neighborhood', sa.String(length=40), nullable=True), - sa.Column('interior_number', sa.String(length=20), nullable=True), - sa.Column('exterior_number', sa.String(length=20), nullable=True), - sa.Column('postal_code', sa.String(length=15), nullable=True), - sa.Column('city', sa.String(length=30), nullable=True), - sa.Column('state', sa.String(length=30), nullable=True), - sa.Column('country', sa.String(length=3), nullable=True), - sa.Column('phone', sa.String(length=30), nullable=True), - sa.Column('fax_number', sa.String(length=30), nullable=True), - sa.Column('email', sa.String(length=100), nullable=True), - sa.Column('contact', sa.String(length=50), nullable=True), - sa.Column('reference', sa.String(length=250), nullable=True), - sa.ForeignKeyConstraint(['client_id'], ['a76.client_provider.client_id'], ondelete='CASCADE'), - sa.PrimaryKeyConstraint('client_id'), - schema='a76' - ) - - op.create_table('gclient_provider_programs', - sa.Column('client_id', sa.String(length=8), nullable=False), - sa.Column('program', sa.String(length=7), nullable=True), - sa.Column('program_number', sa.String(length=40), nullable=True), - sa.Column('prosec', sa.SmallInteger(), nullable=True), - sa.Column('prosec_authorization', sa.String(length=20), nullable=True), - sa.Column('secon_auth_date', sa.Integer(), nullable=True), - sa.Column('manufacturer_id', sa.String(length=25), nullable=True), - sa.Column('tax_id', sa.String(length=30), nullable=True), - sa.Column('broker', sa.String(length=6), nullable=True), - sa.Column('import_broker', sa.String(length=6), nullable=True), - sa.Column('transfer_key', sa.String(length=8), nullable=True), - sa.Column('secon_authorization', sa.String(length=20), nullable=True), - sa.Column('applied_proportion', sa.Numeric(precision=7, scale=2), nullable=True), - sa.Column('is_certified_company', sa.String(length=1), nullable=True), - sa.Column('certified_company_registry', sa.String(length=40), nullable=True), - sa.Column('donation_auth_number', sa.String(length=50), nullable=True), - sa.Column('ctpat_svi', sa.String(length=100), nullable=True), - sa.Column('tax_registry_number', sa.String(length=40), nullable=True), - sa.Column('subassembly_service', sa.SmallInteger(), nullable=True), - sa.Column('autse_dates', sa.Integer(), nullable=True), - sa.Column('autse_number', sa.String(length=300), nullable=True), - sa.ForeignKeyConstraint(['client_id'], ['a76.client_provider.client_id'], ondelete='CASCADE'), - sa.PrimaryKeyConstraint('client_id'), - schema='a76' - ) - - -def downgrade() -> None: - """Downgrade schema - Drop only new A76 tables.""" - # Drop tables in reverse dependency order - op.drop_table('gclient_provider_programs', schema='a76') - op.drop_table('gclient_provider_address', schema='a76') - op.drop_table('parts', schema='a76') - op.drop_table('classes', schema='a76') - op.drop_table('gcompany', schema='a76') - op.drop_table('client_provider', schema='a76') \ No newline at end of file diff --git a/backend/api/v1/modules/a76/client_and_provider/models.py b/backend/api/v1/modules/a76/client_and_provider/models.py index 7949da2b..44007a5f 100644 --- a/backend/api/v1/modules/a76/client_and_provider/models.py +++ b/backend/api/v1/modules/a76/client_and_provider/models.py @@ -1,11 +1,10 @@ """ Modelos ORM para gestión de clientes y proveedores """ -from sqlalchemy import Column, Integer, String, DateTime, Boolean, Text, SmallInteger, Numeric, ForeignKey +from sqlalchemy import Column, Integer, String, SmallInteger, Numeric, ForeignKey from sqlalchemy.sql import func from sqlalchemy.orm import relationship from core.database import Base -import enum class ClientProvider(Base): @@ -17,7 +16,7 @@ class ClientProvider(Base): # Primary key client_id = Column(String(8), primary_key=True, nullable=False) - + # Basic information type_nat_foreign = Column(String(1), nullable=True) # TIPO NACIONAL/EXTRANJERO name = Column(String(256), nullable=True) @@ -37,15 +36,15 @@ class ClientProvider(Base): tenant_id = Column(String, ForeignKey("a76.tenants.id"), nullable=False) # Relationships - address = relationship("GClientProviderAddress", back_populates="client_provider", uselist=False, cascade="all, delete-orphan") - programs = relationship("GClientProviderPrograms", back_populates="client_provider", uselist=False, cascade="all, delete-orphan") + address = relationship("ClientProviderAddress", back_populates="client_provider", uselist=False, cascade="all, delete-orphan") + programs = relationship("ClientProviderPrograms", back_populates="client_provider", uselist=False, cascade="all, delete-orphan") -class GClientProviderAddress(Base): +class ClientProviderAddress(Base): """ Modelo para la tabla GClientesPro_Direccion - Dirección de clientes y proveedores """ - __tablename__ = "gclient_provider_address" + __tablename__ = "client_provider_address" __table_args__ = {"schema": "a76"} # Primary key (foreign key) @@ -71,11 +70,11 @@ class GClientProviderAddress(Base): client_provider = relationship("ClientProvider", back_populates="address") -class GClientProviderPrograms(Base): +class ClientProviderPrograms(Base): """ Modelo para la tabla GClientesPro_Programas - Programas de clientes y proveedores """ - __tablename__ = "gclient_provider_programs" + __tablename__ = "client_provider_programs" __table_args__ = {"schema": "a76"} # Primary key (foreign key) diff --git a/backend/api/v1/modules/a76/client_and_provider/service.py b/backend/api/v1/modules/a76/client_and_provider/service.py index 99340da0..f12763b7 100644 --- a/backend/api/v1/modules/a76/client_and_provider/service.py +++ b/backend/api/v1/modules/a76/client_and_provider/service.py @@ -8,7 +8,7 @@ from fastapi import HTTPException from typing import List, Optional import logging -from .models import ClientProvider, GClientProviderAddress, GClientProviderPrograms +from .models import ClientProvider, ClientProviderAddress, ClientProviderPrograms from .dto import ( ClientProviderCreateDTO, ClientProviderUpdateDTO, @@ -72,7 +72,7 @@ class ClientProviderService: # Crear dirección si se proporciona if client_data.address: - db_address = GClientProviderAddress( + db_address = ClientProviderAddress( client_id=client_data.client_id, **client_data.address.model_dump(exclude_unset=True) ) @@ -80,7 +80,7 @@ class ClientProviderService: # Crear programas si se proporciona if client_data.programs: - db_programs = GClientProviderPrograms( + db_programs = ClientProviderPrograms( client_id=client_data.client_id, **client_data.programs.model_dump(exclude_unset=True) ) @@ -207,7 +207,7 @@ class ClientProviderService: # Actualizar dirección if client_data.address: - address = self.db.query(GClientProviderAddress).filter(GClientProviderAddress.client_id == client_id).first() + address = self.db.query(ClientProviderAddress).filter(ClientProviderAddress.client_id == client_id).first() if address: # Actualizar dirección existente address_data = client_data.address.model_dump(exclude_unset=True) @@ -215,7 +215,7 @@ class ClientProviderService: setattr(address, field, value) else: # Crear nueva dirección - address = GClientProviderAddress( + address = ClientProviderAddress( client_id=client_id, **client_data.address.model_dump(exclude_unset=True) ) @@ -223,7 +223,7 @@ class ClientProviderService: # Actualizar programas if client_data.programs: - programs = self.db.query(GClientProviderPrograms).filter(GClientProviderPrograms.client_id == client_id).first() + programs = self.db.query(ClientProviderPrograms).filter(ClientProviderPrograms.client_id == client_id).first() if programs: # Actualizar programas existentes programs_data = client_data.programs.model_dump(exclude_unset=True) @@ -231,7 +231,7 @@ class ClientProviderService: setattr(programs, field, value) else: # Crear nuevos programas - programs = GClientProviderPrograms( + programs = ClientProviderPrograms( client_id=client_id, **client_data.programs.model_dump(exclude_unset=True) ) diff --git a/backend/api/v1/modules/a76/company/models.py b/backend/api/v1/modules/a76/company/models.py index 464174b1..50df9a03 100644 --- a/backend/api/v1/modules/a76/company/models.py +++ b/backend/api/v1/modules/a76/company/models.py @@ -7,11 +7,11 @@ from core.database import Base import enum -class GCompany(Base): +class Company(Base): """ - Modelo para la tabla GCompany - Información de la empresa + Modelo para la tabla Company - Información de la empresa """ - __tablename__ = "gcompany" + __tablename__ = "company" __table_args__ = {"schema": "a76"} # Primary key diff --git a/backend/api/v1/modules/a76/company/service.py b/backend/api/v1/modules/a76/company/service.py index 405ad1a2..249f7803 100644 --- a/backend/api/v1/modules/a76/company/service.py +++ b/backend/api/v1/modules/a76/company/service.py @@ -7,7 +7,7 @@ from fastapi import HTTPException from typing import List, Optional import logging -from .models import GCompany +from .models import Company from .dto import CompanyCreateDTO, CompanyUpdateDTO, CompanyResponseDTO logger = logging.getLogger(__name__) @@ -34,12 +34,12 @@ class CompanyService: """ try: # Verificar que no exista ya una empresa (solo puede haber una por el consecutivo único) - existing = self.db.query(GCompany).filter(GCompany.consecutive == True).first() + existing = self.db.query(Company).filter(Company.consecutive == True).first() if existing: raise HTTPException(status_code=400, detail="A company is already registered in the system") # Crear empresa - db_company = GCompany( + db_company = Company( id=company_data.id, consecutive=company_data.consecutive, name=company_data.name, @@ -98,7 +98,7 @@ class CompanyService: Returns: CompanyResponseDTO o None si no existe """ - company = self.db.query(GCompany).filter(GCompany.consecutive == True).first() + company = self.db.query(Company).filter(Company.consecutive == True).first() if not company: return None return CompanyResponseDTO.model_validate(company) @@ -113,7 +113,7 @@ class CompanyService: Returns: CompanyResponseDTO o None si no existe """ - company = self.db.query(GCompany).filter(GCompany.id == company_id).first() + company = self.db.query(Company).filter(Company.id == company_id).first() if not company: return None return CompanyResponseDTO.model_validate(company) @@ -129,7 +129,7 @@ class CompanyService: Returns: CompanyResponseDTO actualizada o None si no existe """ - company = self.db.query(GCompany).filter(GCompany.id == company_id).first() + company = self.db.query(Company).filter(Company.id == company_id).first() if not company: return None @@ -158,7 +158,7 @@ class CompanyService: Returns: True si se eliminó, False si no existe """ - company = self.db.query(GCompany).filter(GCompany.id == company_id).first() + company = self.db.query(Company).filter(Company.id == company_id).first() if not company: return False @@ -179,6 +179,6 @@ class CompanyService: Returns: True si existe una empresa, False en caso contrario """ - return self.db.query(GCompany).filter(GCompany.consecutive == True).first() is not None + return self.db.query(Company).filter(Company.consecutive == True).first() is not None diff --git a/backend/api/v1/modules/a76/pedmientos/dtos/pedimento_dates.py b/backend/api/v1/modules/a76/pedmientos/dtos/pedimento_dates.py index 7cf2f9dc..98d0c593 100644 --- a/backend/api/v1/modules/a76/pedmientos/dtos/pedimento_dates.py +++ b/backend/api/v1/modules/a76/pedmientos/dtos/pedimento_dates.py @@ -5,8 +5,6 @@ from datetime import datetime, time class PedimentoDatesBase(BaseModel): """Base schema for Pedimento Dates""" - pedimento_id: int = Field(..., description="Pedimento ID") - tenant_id: int = Field(..., description="Tenant ID") entry_date: Optional[datetime] = Field(None, description="Entry date") pedimento_date: Optional[datetime] = Field(None, description="Pedimento date") payment_date: Optional[datetime] = Field(None, description="Payment date") @@ -45,6 +43,8 @@ class PedimentoDatesUpdate(BaseModel): class PedimentoDatesResponse(PedimentoDatesBase): """Schema for Pedimento Dates response""" id: int + pedimento_id: int = Field(..., description="Pedimento ID") + tenant_id: int = Field(..., description="Tenant ID") created_at: datetime model_config = ConfigDict(from_attributes=True) diff --git a/backend/api/v1/modules/a76/pedmientos/dtos/pedimento_rectification_origin.py b/backend/api/v1/modules/a76/pedmientos/dtos/pedimento_rectification_origin.py index ee8dfeda..ec8464c0 100644 --- a/backend/api/v1/modules/a76/pedmientos/dtos/pedimento_rectification_origin.py +++ b/backend/api/v1/modules/a76/pedmientos/dtos/pedimento_rectification_origin.py @@ -11,7 +11,7 @@ class PedimentoRectificationOriginBase(BaseModel): original_customs_office: Optional[str] = Field(None, max_length=3, description="Original customs office") original_license: Optional[str] = Field(None, max_length=4, description="Original license") original_pedimento_number: Optional[str] = Field(None, max_length=7, description="Original pedimento number") - original_pedimento_key: Optional[str] = Field(None, max_length=2, description="Original pedimento key") + original_pedimento_code: Optional[str] = Field(None, max_length=2, description="Original pedimento key") original_payment_date: Optional[datetime] = Field(None, description="Original payment date") total_cash: Optional[int] = Field(None, description="Total cash") total_others: Optional[int] = Field(None, description="Total others") @@ -33,7 +33,7 @@ class PedimentoRectificationOriginUpdate(BaseModel): original_customs_office: Optional[str] = Field(None, max_length=3) original_license: Optional[str] = Field(None, max_length=4) original_pedimento_number: Optional[str] = Field(None, max_length=7) - original_pedimento_key: Optional[str] = Field(None, max_length=2) + original_pedimento_code: Optional[str] = Field(None, max_length=2) original_payment_date: Optional[datetime] = None total_cash: Optional[int] = None total_others: Optional[int] = None diff --git a/backend/api/v1/modules/a76/pedmientos/dtos/pedimentos.py b/backend/api/v1/modules/a76/pedmientos/dtos/pedimentos.py index 6ac4c4cf..b9a2e45f 100644 --- a/backend/api/v1/modules/a76/pedmientos/dtos/pedimentos.py +++ b/backend/api/v1/modules/a76/pedmientos/dtos/pedimentos.py @@ -13,7 +13,7 @@ class PedimentosBase(BaseModel): client_id: Optional[int] = Field(None, description="Client ID") operation_type: Optional[int] = Field(None, description="Operation type") pedimento_type: Optional[int] = Field(None, description="Pedimento type") - pedimento_key: Optional[str] = Field(None, max_length=2, description="Pedimento key") + pedimento_code: Optional[str] = Field(None, max_length=2, description="Pedimento key") regime: Optional[str] = Field(None, max_length=3, description="Regime") status: Optional[str] = Field(None, max_length=30, description="Status") usd_value: Optional[Decimal] = Field(None, description="USD value") @@ -36,7 +36,7 @@ class PedimentosUpdate(BaseModel): client_id: Optional[int] = None operation_type: Optional[int] = None pedimento_type: Optional[int] = None - pedimento_key: Optional[str] = Field(None, max_length=2) + pedimento_code: Optional[str] = Field(None, max_length=2) regime: Optional[str] = Field(None, max_length=3) status: Optional[str] = Field(None, max_length=30) usd_value: Optional[Decimal] = None diff --git a/backend/api/v1/modules/a76/pedmientos/models/pedimento_rectification_origin.py b/backend/api/v1/modules/a76/pedmientos/models/pedimento_rectification_origin.py index 9e92068f..951c66b2 100644 --- a/backend/api/v1/modules/a76/pedmientos/models/pedimento_rectification_origin.py +++ b/backend/api/v1/modules/a76/pedmientos/models/pedimento_rectification_origin.py @@ -20,7 +20,7 @@ class PedimentoRectificationOrigin(Base): original_customs_office = mapped_column(String(3)) original_license = mapped_column(String(4)) original_pedimento_number = mapped_column(String(7)) - original_pedimento_key = mapped_column(String(2)) + original_pedimento_code = mapped_column(String(2)) original_payment_date = mapped_column(DateTime) total_cash = mapped_column(Integer) total_others = mapped_column(Integer) diff --git a/backend/api/v1/modules/a76/pedmientos/models/pedimentos.py b/backend/api/v1/modules/a76/pedmientos/models/pedimentos.py index 977003d9..a984364f 100644 --- a/backend/api/v1/modules/a76/pedmientos/models/pedimentos.py +++ b/backend/api/v1/modules/a76/pedmientos/models/pedimentos.py @@ -7,6 +7,8 @@ from core.database import Base class Pedimentos(Base): __tablename__ = 'pedimentos' __table_args__ = ( + ForeignKeyConstraint(['regime'], ['public.pedimento_regimens.code']), + ForeignKeyConstraint(['pedimento_code'], ['public.pedimento_codes.code']), ForeignKeyConstraint(['tenant_id'], ['a76.tenants.id']), PrimaryKeyConstraint('id', name='pedimentos_pkey'), Index('idx_pedimentos_client_id', 'client_id'), @@ -24,7 +26,7 @@ class Pedimentos(Base): client_id = mapped_column(Integer) operation_type = mapped_column(Integer) pedimento_type = mapped_column(Integer) - pedimento_key = mapped_column(String(2)) + pedimento_code = mapped_column(String(2)) regime = mapped_column(String(3)) status = mapped_column(String(30)) usd_value = mapped_column(Numeric(17, 6)) diff --git a/backend/api/v1/modules/a76/pedmientos/routes/pedimento_dates.py b/backend/api/v1/modules/a76/pedmientos/routes/pedimento_dates.py index ae83a91e..2d3f5990 100644 --- a/backend/api/v1/modules/a76/pedmientos/routes/pedimento_dates.py +++ b/backend/api/v1/modules/a76/pedmientos/routes/pedimento_dates.py @@ -1,6 +1,7 @@ """ Routes for PedimentoDates CRUD operations """ +import logging from fastapi import APIRouter, Depends, HTTPException from sqlalchemy.orm import Session from core.database import get_core_db @@ -15,7 +16,7 @@ from ..dtos.pedimento_dates import ( router = APIRouter(prefix="/{pedimento_id}/dates") - +logger = logging.getLogger(__name__) @router.get("/", response_model=PedimentoDatesResponse) async def get_dates( @@ -36,8 +37,7 @@ async def get_dates( @router.post("/", response_model=PedimentoDatesResponse, status_code=201) -async def create_dates( - pedimento_id: int, +async def create_dates( data: PedimentoDatesCreate, db: Session = Depends(get_core_db), current_user: dict = Depends(get_current_user) @@ -46,11 +46,7 @@ async def create_dates( tenant_id = get_tenant_from_token(current_user) if not tenant_id: raise HTTPException(status_code=400, detail="Tenant ID not found in token") - - # Ensure pedimento_id matches - if data.pedimento_id != pedimento_id: - raise HTTPException(status_code=400, detail="Pedimento ID mismatch") - + dates = PedimentoDatesService.create(db, data, tenant_id) return dates diff --git a/backend/api/v1/modules/a76/pedmientos/services/pedimento_dates.py b/backend/api/v1/modules/a76/pedmientos/services/pedimento_dates.py index 84708efd..2e928978 100644 --- a/backend/api/v1/modules/a76/pedmientos/services/pedimento_dates.py +++ b/backend/api/v1/modules/a76/pedmientos/services/pedimento_dates.py @@ -1,12 +1,14 @@ """ Service layer for PedimentoDates CRUD operations """ +import logging from typing import Optional from sqlalchemy.orm import Session from ..models.pedimento_dates import PedimentoDates from ..dtos.pedimento_dates import PedimentoDatesCreate, PedimentoDatesUpdate +logger = logging.getLogger(__name__) class PedimentoDatesService: """Service class for PedimentoDates business logic""" diff --git a/backend/main.py b/backend/main.py index a11f226f..fb95f89c 100644 --- a/backend/main.py +++ b/backend/main.py @@ -12,6 +12,7 @@ from core.middleware import ( LicenseValidationMiddleware, RequestLoggingMiddleware ) +from core.database import init_db from api.v1.router import router as api_v1_router # Configurar logging @@ -26,12 +27,20 @@ logger = logging.getLogger(__name__) app = FastAPI( title="Anexo76 API", version=settings.APP_VERSION, - description="Aplicación SaaS para gestión de comercio exterior conforme a Anexos 24, 31 y 22 del SAT", + description="Aplicación SaaS para gestión de comercio exterior conforme a Anexos 24, 30 y 22 del SAT", docs_url="/api/docs" if settings.DEBUG else None, redoc_url="/api/redoc" if settings.DEBUG else None, openapi_url="/api/openapi.json" if settings.DEBUG else None, ) +# Inicializar la base de datos +@app.on_event("startup") +async def on_startup(): + """Evento de inicio de la aplicación""" + logger.info("Iniciando la aplicación Anexo76...") + init_db() + logger.info("Base de datos inicializada correctamente.") + # Configurar CORS app.add_middleware( CORSMiddleware, @@ -41,6 +50,8 @@ app.add_middleware( allow_headers=["*"], ) +logger.info(f"CORS configurado para orígenes: {settings.cors_origins_list}") + # Agregar middlewares personalizados app.add_middleware(RequestLoggingMiddleware) app.add_middleware(LicenseValidationMiddleware) diff --git a/docker-compose.yml b/docker-compose.yml index 3d54d692..d3423ec7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -162,7 +162,7 @@ services: - KEYCLOAK_REALM=${KEYCLOAK_REALM:-master} - KEYCLOAK_CLIENT_ID=${KEYCLOAK_CLIENT_ID:-anexo76-backend} - KEYCLOAK_CLIENT_SECRET=${KEYCLOAK_CLIENT_SECRET:-dev-secret} - - CORS_ORIGINS=http://localhost:5180,http://localhost:3000 + - CORS_ORIGINS=${CORS_ORIGINS:-http://localhost:5180,http://localhost:3000} ports: - "8000:8000" depends_on: diff --git a/docs/SCHEMA_A76_UPDATE.md b/docs/SCHEMA_A76_UPDATE.md index c4587692..b0b6ca65 100644 --- a/docs/SCHEMA_A76_UPDATE.md +++ b/docs/SCHEMA_A76_UPDATE.md @@ -12,10 +12,10 @@ Se han actualizado todos los modelos en `api/v1/modules/a76/` para usar el schem | Módulo | Tabla | Schema | Estado | |--------|-------|---------|---------| -| **Company** | `gcompany` | `a76` | ✅ Actualizada | +| **Company** | `company` | `a76` | ✅ Actualizada | | **Client & Provider** | `client_provider` | `a76` | ✅ Actualizada | -| **Client & Provider** | `gclient_provider_address` | `a76` | ✅ Actualizada | -| **Client & Provider** | `gclient_provider_programs` | `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 | @@ -27,12 +27,12 @@ Se han actualizado todos los modelos en `api/v1/modules/a76/` para usar el schem #### 1. Configuración de Schema ```python # ANTES -class GCompany(Base): - __tablename__ = "gcompany" +class Company(Base): + __tablename__ = "company" # DESPUÉS -class GCompany(Base): - __tablename__ = "gcompany" +class Company(Base): + __tablename__ = "company" __table_args__ = {"schema": "a76"} ``` @@ -59,10 +59,10 @@ PostgreSQL Database ├── tenants ├── licenses ├── license_usage - ├── gcompany + ├── company ├── client_provider - ├── gclient_provider_address - ├── gclient_provider_programs + ├── client_provider_address + ├── client_provider_programs ├── parts └── classes ``` diff --git a/docs/a76.json b/docs/a76.json index 331f38fc..9d62e2bf 100644 --- a/docs/a76.json +++ b/docs/a76.json @@ -1,7 +1,7 @@ { "context": { "project_name": "Anexo76", - "description": "Aplicación SaaS para gestión de comercio exterior conforme a Anexos 24, 31 y 22 del SAT.", + "description": "Aplicación SaaS para gestión de comercio exterior conforme a Anexos 24, 30 y 22 del SAT.", "business_goal": "Ofrecer una plataforma multi-tenant para maquilas, IMMEX y agentes aduanales que permita manejar inventarios, pedimentos y facturas de importación/exportación con control de licencias y cumplimiento normativo." }, "architecture": { diff --git a/frontend/src/app.html b/frontend/src/app.html index 608bad89..26eb1a95 100644 --- a/frontend/src/app.html +++ b/frontend/src/app.html @@ -4,7 +4,7 @@ Anexo76 - Gestión de Comercio Exterior - + - - - - - - {isEditing ? "Editar" : "Nuevo"} Pedimento - - - {isEditing - ? "Modifica los datos del pedimento." - : "Completa los datos para crear un nuevo pedimento."} - - - -
- {#if error} -
- {error} -
- {/if} - - -
-

Información del Pedimento

- -
-
- - -
- -
- - -
- -
- - -
- -
- - -
-
- -
-
- - -
- -
- - -
-
-
- - -
-

Cliente y Operación

- -
-
- - -
- -
- - -
- -
- - -
- -
- - -
-
-
- - -
-

Información Financiera

- -
-
- - -
- -
- - -
- -
- - -
- -
- - -
-
-
- - - - - -
-
-
diff --git a/frontend/src/lib/components/dashboard/pedimentos/data-table-actions.svelte b/frontend/src/lib/components/dashboard/pedimentos/data-table-actions.svelte index 9df58ec8..01f9bba9 100644 --- a/frontend/src/lib/components/dashboard/pedimentos/data-table-actions.svelte +++ b/frontend/src/lib/components/dashboard/pedimentos/data-table-actions.svelte @@ -11,8 +11,6 @@ onSuccess?: () => void; } = $props(); - let showEditDialog = $state(false); - let showDeleteDialog = $state(false); let loading = $state(false); let error = $state(null); @@ -54,12 +52,8 @@ } function handleEdit() { - showEditDialog = true; - } - - function handleView() { - // Navegar a la vista de detalles - window.location.href = `/dashboard/pedimentos/${item.id}`; + // Navegar a la página de edición + window.location.href = `/dashboard/pedimentos/edit/${item.id}`; } @@ -89,24 +83,6 @@ Acciones - - - - - - Ver detalles - - -{#if showEditDialog} - - {#await import('./create-edit-dialog.svelte') then { default: CreateEditDialog }} - - {/await} -{/if} diff --git a/frontend/src/lib/components/dashboard/pedimentos/edit/dates-tab-form.svelte b/frontend/src/lib/components/dashboard/pedimentos/edit/dates-tab-form.svelte new file mode 100644 index 00000000..e8847fc9 --- /dev/null +++ b/frontend/src/lib/components/dashboard/pedimentos/edit/dates-tab-form.svelte @@ -0,0 +1,248 @@ + + + + + Fechas del Pedimento + + Gestiona las fechas importantes del pedimento + + + + {#if loading} +
+ + + +
+ {:else} +
+
+ +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+
+
+ {/if} +
+
diff --git a/frontend/src/lib/components/dashboard/pedimentos/edit/general-tab-form.svelte b/frontend/src/lib/components/dashboard/pedimentos/edit/general-tab-form.svelte new file mode 100644 index 00000000..02123c1b --- /dev/null +++ b/frontend/src/lib/components/dashboard/pedimentos/edit/general-tab-form.svelte @@ -0,0 +1,242 @@ + + + + + Información General + + Edita los datos principales del pedimento + + + +
+ +
+ +
+ + +
+ + +
-
+ + +
+ + +
+ + +
-
+ + +
+ + +
+ + +
-
+ + +
+ + +
+
+ +
+ +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+
+
+
+
diff --git a/frontend/src/lib/components/dashboard/pedimentos/edit/payments-tab-form.svelte b/frontend/src/lib/components/dashboard/pedimentos/edit/payments-tab-form.svelte new file mode 100644 index 00000000..430726a4 --- /dev/null +++ b/frontend/src/lib/components/dashboard/pedimentos/edit/payments-tab-form.svelte @@ -0,0 +1,257 @@ + + + + + Información de Pagos + + Gestiona la información de pagos del pedimento + + + + {#if loading} +
+ + +
+ {:else} +
+
+ +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+
+
+ {/if} +
+
diff --git a/frontend/src/lib/components/dashboard/pedimentos/edit/transport-tab-form.svelte b/frontend/src/lib/components/dashboard/pedimentos/edit/transport-tab-form.svelte new file mode 100644 index 00000000..3c42a633 --- /dev/null +++ b/frontend/src/lib/components/dashboard/pedimentos/edit/transport-tab-form.svelte @@ -0,0 +1,141 @@ + + + + + Medios de Transporte + + Gestiona la información de transporte del pedimento + + + + {#if loading} +
+ + + + +
+ {:else} +
+
+ +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+
+
+ {/if} +
+
diff --git a/frontend/src/lib/components/dashboard/pedimentos/edit/validation-tab-form.svelte b/frontend/src/lib/components/dashboard/pedimentos/edit/validation-tab-form.svelte new file mode 100644 index 00000000..baabf978 --- /dev/null +++ b/frontend/src/lib/components/dashboard/pedimentos/edit/validation-tab-form.svelte @@ -0,0 +1,207 @@ + + + + + Validación del Pedimento + + Gestiona la documentación de validación del pedimento + + + + {#if loading} +
+ + + + +
+ {:else} +
+
+ +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+
+ + +
+ + +
+ + +
+ +