From 676a6fe413eeb81bfca848648d871220370e4a5d Mon Sep 17 00:00:00 2001 From: hreyes Date: Mon, 25 May 2026 11:03:24 -0600 Subject: [PATCH] feature/seed-digitalizacion --- ...a5b6c7_seed_document_types_digitization.py | 57 +++++++++++++++++++ .../api/v1/modules/a76/doc_types_dig/dto.py | 2 - .../v1/modules/a76/doc_types_dig/models.py | 2 - .../v1/modules/a76/doc_types_dig/routes.py | 3 - .../api/v1/modules/a76/doc_types_dig/seed.py | 40 +++++++++++++ .../v1/modules/a76/doc_types_dig/service.py | 30 ---------- .../a76/general_catalogs/company/service.py | 15 +++++ .../document_types_digitization.ts | 10 +--- .../digitalizacion/create-edit-dialog.svelte | 2 +- .../edit/digitization-tab-form.svelte | 2 +- .../document_types_digitization/columns.ts | 19 ------- 11 files changed, 116 insertions(+), 66 deletions(-) create mode 100644 backend/alembic/versions/d2e3f4a5b6c7_seed_document_types_digitization.py create mode 100644 backend/api/v1/modules/a76/doc_types_dig/seed.py diff --git a/backend/alembic/versions/d2e3f4a5b6c7_seed_document_types_digitization.py b/backend/alembic/versions/d2e3f4a5b6c7_seed_document_types_digitization.py new file mode 100644 index 00000000..87f7c247 --- /dev/null +++ b/backend/alembic/versions/d2e3f4a5b6c7_seed_document_types_digitization.py @@ -0,0 +1,57 @@ +"""seed document_types_digitization + +Revision ID: d2e3f4a5b6c7 +Revises: f1a2b3c4d5e6 +Create Date: 2026-05-25 + +""" +from typing import Sequence, Union + +import sqlalchemy as sa +from alembic import op +from sqlalchemy import text +from sqlalchemy.orm import Session + +from api.v1.modules.a76.doc_types_dig.seed import seed as doc_types_dig_seed + +revision: str = "d2e3f4a5b6c7" +down_revision: Union[str, None] = "f1a2b3c4d5e6" +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # Quitar columna active (no se usa en ningún flujo) + op.drop_column('document_types_digitization', 'active', schema='a76') + + def format_value(val): + if val is None or str(val).strip() == "" or str(val).upper() == "NONE": + return "NULL" + return f"'{str(val).replace(chr(39), chr(39)*2)}'" + + bind = op.get_bind() + session = Session(bind=bind) + companies = session.execute( + text("SELECT tenant_id, id FROM a76.company WHERE deleted_at IS NULL") + ).fetchall() + + for tenant_id, company_id in companies: + values = ", ".join([ + f"({format_value(code)}, {format_value(description)}, {tenant_id}, {company_id})" + for code, description in doc_types_dig_seed + ]) + if values: + session.execute(text(f""" + INSERT INTO a76.document_types_digitization (code, description, tenant_id, company_id) + VALUES {values} + ON CONFLICT (tenant_id, company_id, code) DO NOTHING; + """)) + session.commit() + + +def downgrade() -> None: + op.add_column( + 'document_types_digitization', + sa.Column('active', sa.Boolean(), nullable=False, server_default=sa.text('true')), + schema='a76', + ) diff --git a/backend/api/v1/modules/a76/doc_types_dig/dto.py b/backend/api/v1/modules/a76/doc_types_dig/dto.py index 21ee57e2..b8a334da 100644 --- a/backend/api/v1/modules/a76/doc_types_dig/dto.py +++ b/backend/api/v1/modules/a76/doc_types_dig/dto.py @@ -6,7 +6,6 @@ class DocumentTypeDigitizationBase(BaseModel): code: str = Field(..., max_length=10, description="Código del tipo de documento") description: str = Field(..., description="Descripción del tipo de documento") - active: bool = Field(default=True, description="Indica si el tipo está activo") class DocumentTypeDigitizationCreate(DocumentTypeDigitizationBase): @@ -20,7 +19,6 @@ class DocumentTypeDigitizationUpdate(BaseModel): code: str | None = Field(None, max_length=10) description: str | None = None - active: bool | None = None class DocumentTypeDigitizationResponse(DocumentTypeDigitizationBase): diff --git a/backend/api/v1/modules/a76/doc_types_dig/models.py b/backend/api/v1/modules/a76/doc_types_dig/models.py index 61b2efd0..8c522dfb 100644 --- a/backend/api/v1/modules/a76/doc_types_dig/models.py +++ b/backend/api/v1/modules/a76/doc_types_dig/models.py @@ -1,7 +1,6 @@ from api.v1.common.base_models import TenantScopedMixin, TimestampMixin from core.database import Base from sqlalchemy import ( - Boolean, Integer, PrimaryKeyConstraint, String, @@ -29,4 +28,3 @@ class DocumentTypeDigitization(Base, TenantScopedMixin, TimestampMixin): id: Mapped[int] = mapped_column(Integer) code: Mapped[str] = mapped_column(String(10), nullable=False, index=True) description: Mapped[str] = mapped_column(Text, nullable=False) - active: Mapped[bool] = mapped_column(Boolean, default=True, server_default="true") diff --git a/backend/api/v1/modules/a76/doc_types_dig/routes.py b/backend/api/v1/modules/a76/doc_types_dig/routes.py index 5dc43488..688db40f 100644 --- a/backend/api/v1/modules/a76/doc_types_dig/routes.py +++ b/backend/api/v1/modules/a76/doc_types_dig/routes.py @@ -19,7 +19,6 @@ async def list_document_types( page: int = Query(1, ge=1, description='Page number'), page_size: int = Query(50, ge=1, le=2000, description='Page size'), search: Optional[str] = Query(None, description='Search by code or description'), - active_only: bool = Query(False, description='Only active records'), sort_by: Optional[str] = Query('code', description='Column to sort by'), sort_order: str = Query('asc', pattern='^(asc|desc)$', description='Sort order'), db: Session = Depends(get_core_db), @@ -31,8 +30,6 @@ async def list_document_types( if search: filters['search'] = search - if active_only: - filters['active_only'] = True items, total = DocumentTypeDigitizationService.get_all( db, diff --git a/backend/api/v1/modules/a76/doc_types_dig/seed.py b/backend/api/v1/modules/a76/doc_types_dig/seed.py new file mode 100644 index 00000000..02cde648 --- /dev/null +++ b/backend/api/v1/modules/a76/doc_types_dig/seed.py @@ -0,0 +1,40 @@ +# Catálogo SAT — tipos de documentos para digitalización (Ventanilla Única) +seed = [ + ('168', 'Calca o fotografía digital del NIV del vehículo.'), + ('169', 'Aviso.'), + ('170', 'Factura.'), + ('171', 'Documento con el que se acredite la propiedad de la mercancía.'), + ('172', 'Contratos.'), + ('176', 'Documentación relacionada con la garantía otorgada en términos de los artículos 84.'), + ('177', 'Identificación Oficial.'), + ('179', 'Comprobante de domicilio.'), + ('184', 'Documento que ampara el avaluó de las mercancías.'), + ('185', 'Documentos de adjudicación judicial de las mercancías.'), + ('187', 'Solicitud de retiro de mercancías que causaron abandono.'), + ('189', 'Actas.'), + ('192', 'Escritos.'), + ('420', 'Certificado de peso o volumen.'), + ('421', 'Comprobante de la importación temporal de la embarcación debidamente formalizado.'), + ('422', 'Comprobante expedido por donataria.'), + ('423', 'Consulta en la que conste que el vehículo no se encuentra reportado como robado,'), + ('424', 'Clave Unica del Registro de Población.'), + ('425', 'Declaración de internación o extracción de cantidades en efectivo y/o documentos p'), + ('426', 'Declaración de operaciones que no confieren origen en países no parte de acuerdo'), + ('427', 'Declaración en la que se señalen los motivos por los que efectúa la devolución de m'), + ('428', 'Documentación con información que permita la identificación, análisis y control en tér'), + ('429', 'Documentación que acredite que acepta y subsana la irregularidad.'), + ('430', 'Documentación que ampara la importación temporal del vehículo de que se trate.'), + ('431', 'Documentación que compruebe que la adquisición de las mercancías fue efectuada '), + ('433', 'Documento con base en el cual se determine la procedencia y el origen de las merca'), + ('434', 'Documento con que se acredite el reintegro del IVA, en caso de que el contribuyente '), + ('435', 'Documentos previstos en la regla 8.7., fracciones I a IV de la Resolución del TLCAN.'), + ('436', 'El Documento que compruebe el cumplimiento de las regulaciones y restricciones no '), + ('438', 'Guía aérea, conocimiento de embarque o carta de porte.'), + ('439', 'Hoja con los datos de la matrícula y nombre del barco, el lugar donde se localiza y se '), + ('440', 'Manifiesto de carga.'), + ('441', 'Oficios emitidos por autoridad.'), + ('442', 'Pedimentos.'), + ('443', 'Programa IMMEX.'), + ('444', 'Relación de candados.'), + ('445', 'Relación de certificados de origen.'), +] diff --git a/backend/api/v1/modules/a76/doc_types_dig/service.py b/backend/api/v1/modules/a76/doc_types_dig/service.py index 5e645b98..74d9fe6c 100644 --- a/backend/api/v1/modules/a76/doc_types_dig/service.py +++ b/backend/api/v1/modules/a76/doc_types_dig/service.py @@ -18,14 +18,6 @@ class DocumentTypeDigitizationService: def _normalize_description(description: str) -> str: return (description or '').strip() - @staticmethod - def _is_truthy_filter(value: Any, default: bool = False) -> bool: - if value is None: - return default - if isinstance(value, bool): - return value - return str(value).strip().lower() in {'1', 'true', 'yes', 'si'} - @staticmethod def get_all( db: Session, @@ -45,15 +37,8 @@ class DocumentTypeDigitizationService: query = query.filter(DocumentTypeDigitization.company_id == company_id) filters = filters or {} - active_only = DocumentTypeDigitizationService._is_truthy_filter( - filters.get('active_only'), - default=False, - ) search = (filters.get('search') or '').strip() - if active_only: - query = query.filter(DocumentTypeDigitization.active.is_(True)) - if search: like = f'%{search}%' query = query.filter( @@ -69,7 +54,6 @@ class DocumentTypeDigitizationService: 'id': DocumentTypeDigitization.id, 'code': DocumentTypeDigitization.code, 'description': DocumentTypeDigitization.description, - 'active': DocumentTypeDigitization.active, }.get(sort_by or 'code', DocumentTypeDigitization.code) if sort_order == 'desc': @@ -195,17 +179,3 @@ class DocumentTypeDigitizationService: db.refresh(db_obj) return db_obj - @staticmethod - def delete( - db: Session, - id: int, - tenant_id: int, - company_id: int, - ) -> bool: - db_obj = DocumentTypeDigitizationService.get_by_id(db, id, tenant_id, company_id) - if not db_obj: - return False - - db_obj.active = False - db.commit() - return True \ No newline at end of file diff --git a/backend/api/v1/modules/a76/general_catalogs/company/service.py b/backend/api/v1/modules/a76/general_catalogs/company/service.py index 42a84244..1d758980 100644 --- a/backend/api/v1/modules/a76/general_catalogs/company/service.py +++ b/backend/api/v1/modules/a76/general_catalogs/company/service.py @@ -21,6 +21,7 @@ from ..units_of_measure.seed import seed as units_of_measure_seed from ..fractions.historical_tariff_fractions.seed import seed as historical_tariff_fractions_seed from ..fractions.warning_fractions.seed import seed as warning_fractions_seed from ..sectors.seed import seed as sectors_seed +from ...doc_types_dig.seed import seed as doc_types_dig_seed from core.context import get_user_context from sqlalchemy import text @@ -812,6 +813,20 @@ class CompanyService: ON CONFLICT (key, tenant_id, company_id) DO NOTHING; """)) + # 5. Document Types for Digitization + values_doc_types = ", ".join( + [ + f"({format_value(code)}, {format_value(description)}, {tenant_id}, {company_id})" + for code, description in doc_types_dig_seed + ] + ) + if values_doc_types: + db.execute(text(f""" + INSERT INTO a76.document_types_digitization (code, description, tenant_id, company_id) + VALUES {values_doc_types} + ON CONFLICT (tenant_id, company_id, code) DO NOTHING; + """)) + def get_companies_by_tenant(self, tenant_id: int) -> List[Company]: """Get all companies for a tenant""" return ( diff --git a/frontend/src/lib/api/dashboard/reference_data/document_types_digitization.ts b/frontend/src/lib/api/dashboard/reference_data/document_types_digitization.ts index 7a1a7737..8336f278 100644 --- a/frontend/src/lib/api/dashboard/reference_data/document_types_digitization.ts +++ b/frontend/src/lib/api/dashboard/reference_data/document_types_digitization.ts @@ -4,7 +4,6 @@ export interface DocumentTypeDigitization { id: number; code: string; description: string; - active: boolean; } export interface DocumentTypeDigitizationListResponse { @@ -25,7 +24,6 @@ export const documentTypesDigitizationApi = { pageSize = 50, companyId: number, search?: string, - activeOnly = false ) => { const params = new URLSearchParams({ page: page.toString(), @@ -37,15 +35,11 @@ export const documentTypesDigitizationApi = { params.append('search', search); } - if (activeOnly) { - params.append('active_only', 'true'); - } - return api.get(`${BASE_URL}/?${params.toString()}`); }, - getAll: (companyId: number, activeOnly = true, search?: string) => { - return documentTypesDigitizationApi.list(1, 2000, companyId, search, activeOnly); + getAll: (companyId: number, search?: string) => { + return documentTypesDigitizationApi.list(1, 2000, companyId, search); }, getById: (id: number, companyId: number) => { diff --git a/frontend/src/lib/components/dashboard/digitalizacion/create-edit-dialog.svelte b/frontend/src/lib/components/dashboard/digitalizacion/create-edit-dialog.svelte index 188ca3d5..ab40bd35 100644 --- a/frontend/src/lib/components/dashboard/digitalizacion/create-edit-dialog.svelte +++ b/frontend/src/lib/components/dashboard/digitalizacion/create-edit-dialog.svelte @@ -119,7 +119,7 @@ if (docTypes.length === 0) { docTypesLoading = true; documentTypesDigitizationApi - .getAll(companyId, true) + .getAll(companyId) .then((res) => { docTypes = res.data?.items || []; }) diff --git a/frontend/src/lib/components/dashboard/pedimentos/edit/digitization-tab-form.svelte b/frontend/src/lib/components/dashboard/pedimentos/edit/digitization-tab-form.svelte index d5d8dc03..36c3c0ee 100644 --- a/frontend/src/lib/components/dashboard/pedimentos/edit/digitization-tab-form.svelte +++ b/frontend/src/lib/components/dashboard/pedimentos/edit/digitization-tab-form.svelte @@ -81,7 +81,7 @@ tiposDocumentos = []; return; } - const response = await documentTypesDigitizationApi.getAll(companyId, true); + const response = await documentTypesDigitizationApi.getAll(companyId); tiposDocumentos = response.data?.items || []; } catch (error) { console.error('Error al cargar tipos de documentos:', error); diff --git a/frontend/src/lib/components/dashboard/reference_data/document_types_digitization/columns.ts b/frontend/src/lib/components/dashboard/reference_data/document_types_digitization/columns.ts index b4475b4d..438c1821 100644 --- a/frontend/src/lib/components/dashboard/reference_data/document_types_digitization/columns.ts +++ b/frontend/src/lib/components/dashboard/reference_data/document_types_digitization/columns.ts @@ -34,25 +34,6 @@ export function createColumns(): ColumnDef[] { return renderSnippet(descriptionSnippet, { description: row.original.description }); } }, - { - accessorKey: 'active', - header: 'Estatus', - cell: ({ row }) => { - const activeSnippet = createRawSnippet<[{ active: boolean }]>((getActive) => { - const { active } = getActive(); - const className = active - ? 'border-emerald-200 bg-emerald-50 text-emerald-700' - : 'border-slate-200 bg-slate-50 text-slate-600'; - const label = active ? 'Activo' : 'Inactivo'; - return { - render: () => - `${label}` - }; - }); - - return renderSnippet(activeSnippet, { active: row.original.active }); - } - } ]; }