diff --git a/backend/api/v1/modules/a76/general_catalogs/company/__init__.py b/backend/api/v1/modules/a76/general_catalogs/company/__init__.py index 6f72af22..8078325f 100644 --- a/backend/api/v1/modules/a76/general_catalogs/company/__init__.py +++ b/backend/api/v1/modules/a76/general_catalogs/company/__init__.py @@ -1,7 +1,26 @@ """ Módulo de Company """ - +from .models import Company from .routes import router +from .submodels import ( + CompanyAddress, + CompanyCertification, + CompanyDigitalCertificate, + CompanyVU, + CompanyElectronicAgent, + CompanyPrevalidator, + CompanyCFDI, +) -__all__ = ["router"] +__all__ = [ + "Company", + "router", + "CompanyAddress", + "CompanyCertification", + "CompanyDigitalCertificate", + "CompanyVU", + "CompanyElectronicAgent", + "CompanyPrevalidator", + "CompanyCFDI", +] \ No newline at end of file diff --git a/backend/api/v1/modules/a76/general_catalogs/company/models.py b/backend/api/v1/modules/a76/general_catalogs/company/models.py index 18bdd840..c6d37213 100644 --- a/backend/api/v1/modules/a76/general_catalogs/company/models.py +++ b/backend/api/v1/modules/a76/general_catalogs/company/models.py @@ -1,53 +1,57 @@ """ -Modelos ORM para gestión de empresa +Modelo principal de Company """ - -from typing import Optional - -from api.v1.common.base_models import TimestampMixin +from typing import Optional, TYPE_CHECKING +from sqlalchemy import Integer, String, SmallInteger, ForeignKey, Boolean +from sqlalchemy.orm import Mapped, mapped_column, relationship from core.database import Base -from sqlalchemy import ( - Boolean, - ForeignKey, - ForeignKeyConstraint, - Integer, - PrimaryKeyConstraint, - SmallInteger, - String, -) -from sqlalchemy.orm import Mapped, mapped_column +from api.v1.common.base_models import TimestampMixin + +if TYPE_CHECKING: + from .submodels.address import CompanyAddress + from .submodels.certification import CompanyCertification + from .submodels.digital_certificate import CompanyDigitalCertificate + from .submodels.ventanilla_unica import CompanyVU + from .submodels.electronic_agent import CompanyElectronicAgent + from .submodels.prevalidator import CompanyPrevalidator + from .submodels.cfdi import CompanyCFDI class Company(Base, TimestampMixin): - """ - Modelo para la tabla Company - Información de la empresa - """ - - __tablename__ = "company" #GEmpresa - __table_args__ = ( - PrimaryKeyConstraint("id", name="company_pkey"), - {"schema": "a76"}, - ) - + """Información principal de la empresa""" + + __tablename__ = "company" + __table_args__ = {"schema": "a76"} + # Primary key id: Mapped[int] = mapped_column(Integer, primary_key=True) - tenant_id: Mapped[int] = mapped_column(Integer, ForeignKey("core.tenants.id"), nullable=False, index=True) - - # Información básica de la empresa - name: Mapped[Optional[str]] = mapped_column(String(255)) + tenant_id: Mapped[int] = mapped_column( + ForeignKey("core.tenants.id"), + nullable=False, + index=True + ) + + # Información básica + name: Mapped[Optional[str]] = mapped_column(String(256)) rfc: Mapped[Optional[str]] = mapped_column(String(30)) - main_activity: Mapped[Optional[str]] = mapped_column(String(255)) - - # Información del programa - program: Mapped[Optional[str]] = mapped_column(String(10)) + curp: Mapped[Optional[str]] = mapped_column(String(19)) + main_activity: Mapped[Optional[str]] = mapped_column(String(80)) + + # Programa + program: Mapped[Optional[str]] = mapped_column(String(7)) program_number: Mapped[Optional[str]] = mapped_column(String(40)) prosec: Mapped[Optional[int]] = mapped_column(SmallInteger) prosec_authorization: Mapped[Optional[str]] = mapped_column(String(20)) - + + # Sectores + sector1: Mapped[Optional[str]] = mapped_column(String(150)) + sector2: Mapped[Optional[str]] = mapped_column(String(150)) + sector3: Mapped[Optional[str]] = mapped_column(String(5)) + # Identificadores manufacturer_id: Mapped[Optional[str]] = mapped_column(String(25)) - broker_company: Mapped[Optional[str]] = mapped_column(String(10)) - + broker_company: Mapped[Optional[str]] = mapped_column(String(6)) + # Responsable responsible: Mapped[Optional[str]] = mapped_column(String(80)) responsible_name: Mapped[Optional[str]] = mapped_column(String(20)) @@ -55,24 +59,88 @@ class Company(Base, TimestampMixin): responsible_mother_last_name: Mapped[Optional[str]] = mapped_column(String(20)) responsible_rfc: Mapped[Optional[str]] = mapped_column(String(30)) position: Mapped[Optional[str]] = mapped_column(String(30)) - - # Configuración - logo: Mapped[Optional[str]] = mapped_column(String(500)) - has_express_line: Mapped[Optional[bool]] = mapped_column(Boolean) + + # Configuración básica + logo: Mapped[Optional[str]] = mapped_column(String(255)) + has_express_line: Mapped[Optional[str]] = mapped_column(String(2)) order_format_type: Mapped[Optional[str]] = mapped_column(String(19)) - previous_code: Mapped[Optional[int]] = mapped_column(SmallInteger) - is_service_company: Mapped[Optional[bool]] = mapped_column(Boolean) - - # Cliente y submaquila + is_service_company: Mapped[Optional[bool]] = mapped_column(Boolean, default=False) client_name: Mapped[Optional[str]] = mapped_column(String(300)) subassembly_mode: Mapped[Optional[str]] = mapped_column(String(7)) - - # Información adicional - curp: Mapped[Optional[str]] = mapped_column(String(19)) + + # Configuraciones técnicas (flags) + previous_code: Mapped[Optional[int]] = mapped_column(SmallInteger) + active_labels: Mapped[Optional[int]] = mapped_column(SmallInteger) + active_fractions: Mapped[Optional[int]] = mapped_column(SmallInteger) + activate_caat: Mapped[Optional[int]] = mapped_column(SmallInteger) + trans_interface: Mapped[Optional[int]] = mapped_column(SmallInteger) + american_costs: Mapped[Optional[int]] = mapped_column(SmallInteger) + scaf_readonly: Mapped[Optional[int]] = mapped_column(SmallInteger) + parts_replacement: Mapped[Optional[int]] = mapped_column(SmallInteger) + activate_facmexame: Mapped[Optional[int]] = mapped_column(SmallInteger) + part_reference: Mapped[Optional[int]] = mapped_column(SmallInteger) + international_firm: Mapped[Optional[int]] = mapped_column(SmallInteger) + + # Configuraciones simples + ftp_key: Mapped[Optional[str]] = mapped_column(String(10)) + sifra_path: Mapped[Optional[str]] = mapped_column(String(255)) + version_type: Mapped[Optional[str]] = mapped_column(String(20)) + sql_language: Mapped[Optional[str]] = mapped_column(String(19)) + balance_operation_mode: Mapped[Optional[str]] = mapped_column(String(50)) inter_db_name: Mapped[Optional[str]] = mapped_column(String(100)) - ctpat_svi: Mapped[Optional[str]] = mapped_column(String(100)) - trusted_exporter_number: Mapped[Optional[str]] = mapped_column(String(50)) - prevalidator_key: Mapped[Optional[str]] = mapped_column(String(20)) - seventh_amendment: Mapped[Optional[bool]] = mapped_column( - Boolean - ) # FINALCONTADORAELECTRONICO renombrado + + # ==================== RELACIONES CON SUBTABLAS ==================== + + addresses: Mapped[list["CompanyAddress"]] = relationship( + "CompanyAddress", + back_populates="company", + cascade="all, delete-orphan", + lazy="selectin" + ) + + certification: Mapped[Optional["CompanyCertification"]] = relationship( + "CompanyCertification", + back_populates="company", + uselist=False, + cascade="all, delete-orphan", + lazy="selectin" + ) + + digital_certificates: Mapped[list["CompanyDigitalCertificate"]] = relationship( + "CompanyDigitalCertificate", + back_populates="company", + cascade="all, delete-orphan", + lazy="selectin" + ) + + ventanilla_unica: Mapped[Optional["CompanyVU"]] = relationship( + "CompanyVU", + back_populates="company", + uselist=False, + cascade="all, delete-orphan", + lazy="selectin" + ) + + electronic_agent: Mapped[Optional["CompanyElectronicAgent"]] = relationship( + "CompanyElectronicAgent", + back_populates="company", + uselist=False, + cascade="all, delete-orphan", + lazy="selectin" + ) + + prevalidator: Mapped[Optional["CompanyPrevalidator"]] = relationship( + "CompanyPrevalidator", + back_populates="company", + uselist=False, + cascade="all, delete-orphan", + lazy="selectin" + ) + + cfdi: Mapped[Optional["CompanyCFDI"]] = relationship( + "CompanyCFDI", + back_populates="company", + uselist=False, + cascade="all, delete-orphan", + lazy="selectin" + ) \ No newline at end of file diff --git a/backend/api/v1/modules/a76/general_catalogs/company/submodels/__init__.py b/backend/api/v1/modules/a76/general_catalogs/company/submodels/__init__.py new file mode 100644 index 00000000..8afa2f2b --- /dev/null +++ b/backend/api/v1/modules/a76/general_catalogs/company/submodels/__init__.py @@ -0,0 +1,20 @@ +""" +Submodelos de Company +""" +from .address import CompanyAddress +from .certification import CompanyCertification +from .digital_certificate import CompanyDigitalCertificate +from .vu import CompanyVU +from .electronic_agent import CompanyElectronicAgent +from .prevalidator import CompanyPrevalidator +from .cfdi import CompanyCFDI + +__all__ = [ + "CompanyAddress", + "CompanyCertification", + "CompanyDigitalCertificate", + "CompanyVU", + "CompanyElectronicAgent", + "CompanyPrevalidator", + "CompanyCFDI", +] \ No newline at end of file diff --git a/backend/api/v1/modules/a76/general_catalogs/company/submodels/address.py b/backend/api/v1/modules/a76/general_catalogs/company/submodels/address.py new file mode 100644 index 00000000..3ecfe266 --- /dev/null +++ b/backend/api/v1/modules/a76/general_catalogs/company/submodels/address.py @@ -0,0 +1,46 @@ +""" +Modelo de direcciones de empresa +""" +from typing import Optional, TYPE_CHECKING +from sqlalchemy import Integer, String, ForeignKey +from sqlalchemy.orm import Mapped, mapped_column, relationship +from core.database import Base +from api.v1.common.base_models import TimestampMixin + +if TYPE_CHECKING: + from ..models import Company + + +class CompanyAddress(Base, TimestampMixin): + """Direcciones de la empresa (principal, industrial 1 y 2)""" + + __tablename__ = "company_address" + __table_args__ = {"schema": "a76"} + + id: Mapped[int] = mapped_column(Integer, primary_key=True) + company_id: Mapped[int] = mapped_column( + ForeignKey("a76.company.id", ondelete="CASCADE"), + nullable=False, + index=True + ) + address_type: Mapped[str] = mapped_column(String(20)) # 'main', 'industrial', 'industrial2' + + # Campos de dirección + street: Mapped[Optional[str]] = mapped_column(String(100)) + exterior_number: Mapped[Optional[str]] = mapped_column(String(20)) + interior_number: Mapped[Optional[str]] = mapped_column(String(20)) + postal_code: Mapped[Optional[str]] = mapped_column(String(15)) + neighborhood: Mapped[Optional[str]] = mapped_column(String(40)) + city: Mapped[Optional[str]] = mapped_column(String(40)) + municipality: Mapped[Optional[str]] = mapped_column(String(50)) + state: Mapped[Optional[str]] = mapped_column(String(30)) + country: Mapped[Optional[str]] = mapped_column(String(4)) + phone: Mapped[Optional[str]] = mapped_column(String(30)) + fax: Mapped[Optional[str]] = mapped_column(String(30)) + email: Mapped[Optional[str]] = mapped_column(String(100)) + + # Relación inversa + company: Mapped["Company"] = relationship( + "Company", + back_populates="addresses" + ) \ No newline at end of file diff --git a/backend/api/v1/modules/a76/general_catalogs/company/submodels/certification.py b/backend/api/v1/modules/a76/general_catalogs/company/submodels/certification.py new file mode 100644 index 00000000..5624704b --- /dev/null +++ b/backend/api/v1/modules/a76/general_catalogs/company/submodels/certification.py @@ -0,0 +1,52 @@ +""" +Modelo de certificaciones de empresa +""" +from typing import Optional, TYPE_CHECKING +from sqlalchemy import Integer, String, SmallInteger, ForeignKey +from sqlalchemy.orm import Mapped, mapped_column, relationship +from core.database import Base +from api.v1.common.base_models import TimestampMixin + +if TYPE_CHECKING: + from ..models import Company + + +class CompanyCertification(Base, TimestampMixin): + """Certificaciones de la empresa""" + + __tablename__ = "company_certification" + __table_args__ = {"schema": "a76"} + + id: Mapped[int] = mapped_column(Integer, primary_key=True) + company_id: Mapped[int] = mapped_column( + ForeignKey("a76.company.id", ondelete="CASCADE"), + nullable=False, + unique=True, + index=True + ) + + # Certificación general + is_certified_company: Mapped[Optional[str]] = mapped_column(String(1)) + certified_company_registration: Mapped[Optional[str]] = mapped_column(String(40)) + certified_company_start_date: Mapped[Optional[int]] = mapped_column(Integer) + certified_company_end_date: Mapped[Optional[int]] = mapped_column(Integer) + + # Anexo 31 + annex31_certification_date: Mapped[Optional[int]] = mapped_column(Integer) + annex31_certification_number: Mapped[Optional[str]] = mapped_column(String(50)) + annex31_modality: Mapped[Optional[str]] = mapped_column(String(50)) + annex31_company_type: Mapped[Optional[str]] = mapped_column(String(50)) + annex31_renewal_date: Mapped[Optional[int]] = mapped_column(Integer) + annex31_final_certification_date: Mapped[Optional[int]] = mapped_column(Integer) + + # Otras certificaciones + is_oea_company: Mapped[Optional[int]] = mapped_column(SmallInteger) + ctpat_svi: Mapped[Optional[str]] = mapped_column(String(100)) + trusted_exporter_number: Mapped[Optional[str]] = mapped_column(String(50)) + neec_company: Mapped[Optional[int]] = mapped_column(Integer) + + # Relación inversa + company: Mapped["Company"] = relationship( + "Company", + back_populates="certification" + ) \ No newline at end of file diff --git a/backend/api/v1/modules/a76/general_catalogs/company/submodels/cfdi.py b/backend/api/v1/modules/a76/general_catalogs/company/submodels/cfdi.py new file mode 100644 index 00000000..f5e9d48f --- /dev/null +++ b/backend/api/v1/modules/a76/general_catalogs/company/submodels/cfdi.py @@ -0,0 +1,37 @@ +""" +Modelo de configuración de CFDI (Facturación Electrónica) +""" +from typing import Optional, TYPE_CHECKING +from sqlalchemy import Integer, String, ForeignKey +from sqlalchemy.orm import Mapped, mapped_column, relationship +from core.database import Base +from api.v1.common.base_models import TimestampMixin + +if TYPE_CHECKING: + from ..models import Company + + +class CompanyCFDI(Base, TimestampMixin): + """Configuración de Facturación Electrónica (CFDI)""" + + __tablename__ = "company_cfdi" + __table_args__ = {"schema": "a76"} + + id: Mapped[int] = mapped_column(Integer, primary_key=True) + company_id: Mapped[int] = mapped_column( + ForeignKey("a76.company.id", ondelete="CASCADE"), + nullable=False, + unique=True, + index=True + ) + + # Rutas de aplicaciones y archivos + xml_save_path: Mapped[Optional[str]] = mapped_column(String(5000)) + cfdi_app_path: Mapped[Optional[str]] = mapped_column(String(5000)) + pac_app_path: Mapped[Optional[str]] = mapped_column(String(5000)) + + # Relación inversa + company: Mapped["Company"] = relationship( + "Company", + back_populates="cfdi" + ) \ No newline at end of file diff --git a/backend/api/v1/modules/a76/general_catalogs/company/submodels/digital_certificate.py b/backend/api/v1/modules/a76/general_catalogs/company/submodels/digital_certificate.py new file mode 100644 index 00000000..6de413b9 --- /dev/null +++ b/backend/api/v1/modules/a76/general_catalogs/company/submodels/digital_certificate.py @@ -0,0 +1,44 @@ +""" +Modelo de certificados digitales (FIEL, CFDI, Cancelación) +""" +from typing import Optional, TYPE_CHECKING +from sqlalchemy import Integer, String, ForeignKey +from sqlalchemy.orm import Mapped, mapped_column, relationship +from core.database import Base +from api.v1.common.base_models import TimestampMixin + +if TYPE_CHECKING: + from ..models import Company + + +class CompanyDigitalCertificate(Base, TimestampMixin): + """Certificados digitales (FIEL, CFDI)""" + + __tablename__ = "company_digital_certificate" + __table_args__ = {"schema": "a76"} + + id: Mapped[int] = mapped_column(Integer, primary_key=True) + company_id: Mapped[int] = mapped_column( + ForeignKey("a76.company.id", ondelete="CASCADE"), + nullable=False, + index=True + ) + certificate_type: Mapped[str] = mapped_column(String(20)) # 'fiel', 'cfdi', 'cancellation' + + # Rutas de archivos + cer_file_path: Mapped[Optional[str]] = mapped_column(String(5000)) + key_file_path: Mapped[Optional[str]] = mapped_column(String(5000)) + + # Contraseñas y claves + password: Mapped[Optional[str]] = mapped_column(String(200)) + access_key: Mapped[Optional[str]] = mapped_column(String(50)) + + # Fechas de expiración + cer_expiration_date: Mapped[Optional[int]] = mapped_column(Integer) + key_expiration_date: Mapped[Optional[int]] = mapped_column(Integer) + + # Relación inversa + company: Mapped["Company"] = relationship( + "Company", + back_populates="digital_certificates" + ) \ No newline at end of file diff --git a/backend/api/v1/modules/a76/general_catalogs/company/submodels/electronic_agent.py b/backend/api/v1/modules/a76/general_catalogs/company/submodels/electronic_agent.py new file mode 100644 index 00000000..d57c9c0c --- /dev/null +++ b/backend/api/v1/modules/a76/general_catalogs/company/submodels/electronic_agent.py @@ -0,0 +1,46 @@ +""" +Modelo de configuración del Agente Aduanal Electrónico +""" +from typing import Optional, TYPE_CHECKING +from sqlalchemy import Integer, String, ForeignKey +from sqlalchemy.orm import Mapped, mapped_column, relationship +from core.database import Base +from api.v1.common.base_models import TimestampMixin + +if TYPE_CHECKING: + from ..models import Company + + +class CompanyElectronicAgent(Base, TimestampMixin): + """Configuración de Agente Aduanal Electrónico""" + + __tablename__ = "company_electronic_agent" + __table_args__ = {"schema": "a76"} + + id: Mapped[int] = mapped_column(Integer, primary_key=True) + company_id: Mapped[int] = mapped_column( + ForeignKey("a76.company.id", ondelete="CASCADE"), + nullable=False, + unique=True, + index=True + ) + + # Carpetas + input_folder: Mapped[Optional[str]] = mapped_column(String(1000)) + output_folder: Mapped[Optional[str]] = mapped_column(String(1000)) + + # Máscaras y extensiones + send_mask: Mapped[Optional[str]] = mapped_column(String(20)) + response_mask: Mapped[Optional[str]] = mapped_column(String(20)) + response_extension: Mapped[Optional[str]] = mapped_column(String(20)) + + # Contadores + counter_start: Mapped[Optional[int]] = mapped_column(Integer) + counter_end: Mapped[Optional[int]] = mapped_column(Integer) + counter_next: Mapped[Optional[int]] = mapped_column(Integer) + + # Relación inversa + company: Mapped["Company"] = relationship( + "Company", + back_populates="electronic_agent" + ) \ No newline at end of file diff --git a/backend/api/v1/modules/a76/general_catalogs/company/submodels/prevalidator.py b/backend/api/v1/modules/a76/general_catalogs/company/submodels/prevalidator.py new file mode 100644 index 00000000..05da674b --- /dev/null +++ b/backend/api/v1/modules/a76/general_catalogs/company/submodels/prevalidator.py @@ -0,0 +1,38 @@ +""" +Modelo de configuración del Prevalidador Aduanal +""" +from typing import Optional, TYPE_CHECKING +from sqlalchemy import Integer, String, ForeignKey +from sqlalchemy.orm import Mapped, mapped_column, relationship +from core.database import Base +from api.v1.common.base_models import TimestampMixin + +if TYPE_CHECKING: + from ..models import Company + + +class CompanyPrevalidator(Base, TimestampMixin): + """Configuración de Prevalidador Aduanal""" + + __tablename__ = "company_prevalidator" + __table_args__ = {"schema": "a76"} + + id: Mapped[int] = mapped_column(Integer, primary_key=True) + company_id: Mapped[int] = mapped_column( + ForeignKey("a76.company.id", ondelete="CASCADE"), + nullable=False, + unique=True, + index=True + ) + + # Información del prevalidador + customs: Mapped[Optional[str]] = mapped_column(String(20)) + key: Mapped[Optional[str]] = mapped_column(String(20)) + patent: Mapped[Optional[str]] = mapped_column(String(4)) + description: Mapped[Optional[str]] = mapped_column(String(100)) + + # Relación inversa + company: Mapped["Company"] = relationship( + "Company", + back_populates="prevalidator" + ) \ No newline at end of file diff --git a/backend/api/v1/modules/a76/general_catalogs/company/submodels/vu.py b/backend/api/v1/modules/a76/general_catalogs/company/submodels/vu.py new file mode 100644 index 00000000..d094e598 --- /dev/null +++ b/backend/api/v1/modules/a76/general_catalogs/company/submodels/vu.py @@ -0,0 +1,50 @@ +""" +Modelo de configuración de Ventanilla Única +""" +from typing import Optional, TYPE_CHECKING +from sqlalchemy import Integer, String, ForeignKey +from sqlalchemy.orm import Mapped, mapped_column, relationship +from core.database import Base +from api.v1.common.base_models import TimestampMixin + +if TYPE_CHECKING: + from ..models import Company + + +class CompanyVU(Base, TimestampMixin): + """Configuración de Ventanilla Única""" + + __tablename__ = "CompanyVU" + __table_args__ = {"schema": "a76"} + + id: Mapped[int] = mapped_column(Integer, primary_key=True) + company_id: Mapped[int] = mapped_column( + ForeignKey("a76.company.id", ondelete="CASCADE"), + nullable=False, + unique=True, + index=True + ) + + # Credenciales + webservice_user: Mapped[Optional[str]] = mapped_column(String(100)) + webservice_password: Mapped[Optional[str]] = mapped_column(String(100)) + email: Mapped[Optional[str]] = mapped_column(String(800)) + + # Configuración + figure_type: Mapped[Optional[str]] = mapped_column(String(29)) + central_path: Mapped[Optional[str]] = mapped_column(String(1499)) + xml_files_path: Mapped[Optional[str]] = mapped_column(String(1499)) + + # RFC y validación + query_rfc: Mapped[Optional[str]] = mapped_column(String(30)) + validation_rfc: Mapped[Optional[str]] = mapped_column(String(30)) + + # Otras configuraciones + configuration_source: Mapped[Optional[str]] = mapped_column(String(30)) + measurement_units: Mapped[Optional[str]] = mapped_column(String(3)) + + # Relación inversa + company: Mapped["Company"] = relationship( + "Company", + back_populates="ventanilla_unica" + ) \ No newline at end of file diff --git a/frontend/src/lib/components/dashboard/clients_and_providers/columns.ts b/frontend/src/lib/components/dashboard/clients_and_providers/columns.ts index a09246ab..b6dc5529 100644 --- a/frontend/src/lib/components/dashboard/clients_and_providers/columns.ts +++ b/frontend/src/lib/components/dashboard/clients_and_providers/columns.ts @@ -37,6 +37,61 @@ export function createColumns(onSuccess) { return renderSnippet(snippet, { val: row.original.name }); } }, + { + id: "streets", + header: "Calles", + cell: ({ row }) => { + const snippet = createRawSnippet((getData) => { + const { val } = getData(); + return { render: () => `
${val || '-'}
` }; + }); + return renderSnippet(snippet, { val: row.original.address?.streets }); + } + }, + { + id: "exterior_number", + header: "Num Ext.", + cell: ({ row }) => { + const snippet = createRawSnippet((getData) => { + const { val } = getData(); + return { render: () => `
${val || '-'}
` }; + }); + return renderSnippet(snippet, { val: row.original.address?.exterior_number }); + } + }, + { + id: "postal_code", + header: "Código Postal", + cell: ({ row }) => { + const snippet = createRawSnippet((getData) => { + const { val } = getData(); + return { render: () => `${val || '-'}` }; + }); + return renderSnippet(snippet, { val: row.original.address?.postal_code }); + } + }, + { + id: "neighborhood", + header: "Colonia", + cell: ({ row }) => { + const snippet = createRawSnippet((getData) => { + const { val } = getData(); + return { render: () => `
${val || '-'}
` }; + }); + return renderSnippet(snippet, { val: row.original.address?.neighborhood }); + } + }, + { + id: "city", + header: "Ciudad", + cell: ({ row }) => { + const snippet = createRawSnippet((getData) => { + const { val } = getData(); + return { render: () => `
${val || '-'}
` }; + }); + return renderSnippet(snippet, { val: row.original.address?.city }); + } + }, { id: "country", header: "País", diff --git a/frontend/src/lib/components/dashboard/customs_brokers/columns.ts b/frontend/src/lib/components/dashboard/customs_brokers/columns.ts index 4f099fd3..24400b55 100644 --- a/frontend/src/lib/components/dashboard/customs_brokers/columns.ts +++ b/frontend/src/lib/components/dashboard/customs_brokers/columns.ts @@ -22,9 +22,22 @@ export function createColumns(onSuccess?: () => void): ColumnDef[ return renderSnippet(keySnippet, { key: row.original.broker_key }); } }, + { + accessorKey: "type", + header: "Tipo", + cell: ({ row }) => { + const typeSnippet = createRawSnippet<[{ type: string | null | undefined }]>((getType) => { + const { type } = getType(); + return { + render: () => `
${type || '-'}
` + }; + }); + return renderSnippet(typeSnippet, { type: row.original.type }); + } + }, { accessorKey: "name", - header: "Nombre", + header: "Nombre Completo", cell: ({ row }) => { const nameSnippet = createRawSnippet<[{ name: string | null | undefined }]>((getName) => { const { name } = getName(); @@ -36,16 +49,84 @@ export function createColumns(onSuccess?: () => void): ColumnDef[ } }, { - accessorKey: "type", - header: "Tipo", + accessorKey: "address", + header: "Dirección", cell: ({ row }) => { - const typeSnippet = createRawSnippet<[{ type: string | null | undefined }]>((getType) => { - const { type } = getType(); + const addressSnippet = createRawSnippet<[{ address: string | null | undefined }]>((getAddress) => { + const { address } = getAddress(); return { - render: () => `
${type || '-'}
` + render: () => `
${address || '-'}
` }; }); - return renderSnippet(typeSnippet, { type: row.original.type }); + return renderSnippet(addressSnippet, { address: row.original.address }); + } + }, + { + accessorKey: "postal_code", + header: "Código Postal", + cell: ({ row }) => { + const postalSnippet = createRawSnippet<[{ postal: string | null | undefined }]>((getPostal) => { + const { postal } = getPostal(); + return { + render: () => + postal + ? `${postal}` + : `-` + }; + }); + return renderSnippet(postalSnippet, { postal: row.original.postal_code }); + } + }, + { + accessorKey: "city", + header: "Ciudad", + cell: ({ row }) => { + const citySnippet = createRawSnippet<[{ city: string | null | undefined }]>((getCity) => { + const { city } = getCity(); + return { + render: () => `
${city || '-'}
` + }; + }); + return renderSnippet(citySnippet, { city: row.original.city }); + } + }, + { + accessorKey: "state", + header: "Estado", + cell: ({ row }) => { + const stateSnippet = createRawSnippet<[{ state: string | null | undefined }]>((getState) => { + const { state } = getState(); + return { + render: () => `
${state || '-'}
` + }; + }); + return renderSnippet(stateSnippet, { state: row.original.state }); + } + }, + { + accessorKey: "phone", + header: "Teléfono", + cell: ({ row }) => { + const phoneSnippet = createRawSnippet<[{ phone: string | null | undefined }]>((getPhone) => { + const { phone } = getPhone(); + return { + render: () => `
${phone || '-'}
` + }; + }); + return renderSnippet(phoneSnippet, { phone: row.original.phone }); + } + }, + { + accessorKey: "fax", + header: "Número Fax", + cell: ({ row }) => { + const faxSnippet = createRawSnippet<[{ fax: string | null | undefined }]>((getFax) => { + const { fax } = getFax(); + return { + render: () => `
${fax || '-'}
` + }; + }); + return renderSnippet(faxSnippet, { fax: row.original.fax }); } }, { @@ -90,32 +171,6 @@ export function createColumns(onSuccess?: () => void): ColumnDef[ return renderSnippet(emailSnippet, { email: row.original.email }); } }, - { - accessorKey: "phone", - header: "Teléfono", - cell: ({ row }) => { - const phoneSnippet = createRawSnippet<[{ phone: string | null | undefined }]>((getPhone) => { - const { phone } = getPhone(); - return { - render: () => `
${phone || '-'}
` - }; - }); - return renderSnippet(phoneSnippet, { phone: row.original.phone }); - } - }, - { - accessorKey: "city", - header: "Ciudad", - cell: ({ row }) => { - const citySnippet = createRawSnippet<[{ city: string | null | undefined }]>((getCity) => { - const { city } = getCity(); - return { - render: () => `
${city || '-'}
` - }; - }); - return renderSnippet(citySnippet, { city: row.original.city }); - } - }, { id: "actions", header: "Acciones", diff --git a/frontend/src/lib/components/dashboard/goods/parts/columns.ts b/frontend/src/lib/components/dashboard/goods/parts/columns.ts index 6230f6f1..1f161298 100644 --- a/frontend/src/lib/components/dashboard/goods/parts/columns.ts +++ b/frontend/src/lib/components/dashboard/goods/parts/columns.ts @@ -112,7 +112,25 @@ export function createColumns(onSuccess?: () => void): ColumnDef[] { } }, - // 7. FRACCION + // 7. U.M. (Unidad de Medida) + { + accessorKey: "unit_of_measure", + header: "U.M.", + cell: ({ row }) => { + const umSnippet = createRawSnippet<[{ um: string }]>((getUm) => { + const { um } = getUm(); + return { + render: () => + ` + ${um} + ` + }; + }); + return renderSnippet(umSnippet, { um: row.original.unit_of_measure }); + } + }, + + // 8. FRACCION { accessorKey: "fraction", header: "Fracción", @@ -127,25 +145,22 @@ export function createColumns(onSuccess?: () => void): ColumnDef[] { } }, - // 8. UMT (Unidad de Medida) + // 9. U.M.T. (del inv_data) { - accessorKey: "unit_of_measure", - header: "UMT", + id: "stock_uom", + header: "U.M.T.", cell: ({ row }) => { - const umtSnippet = createRawSnippet<[{ um: string }]>((getUm) => { - const { um } = getUm(); + const umtSnippet = createRawSnippet<[{ umt: string }]>((getUmt) => { + const { umt } = getUmt(); return { - render: () => - ` - ${um} - ` + render: () => `
${umt || '-'}
` }; }); - return renderSnippet(umtSnippet, { um: row.original.unit_of_measure }); + return renderSnippet(umtSnippet, { umt: row.original.inv_data?.stock_uom || '' }); } }, - // 9. COSTO + // 10. COSTO { accessorKey: "unit_cost", header: "Costo", @@ -163,6 +178,78 @@ export function createColumns(onSuccess?: () => void): ColumnDef[] { } }, + // 11. MONEDA + { + accessorKey: "currency_key", + header: "Moneda", + cell: ({ row }) => { + const currSnippet = createRawSnippet<[{ curr: string }]>((getCurr) => { + const { curr } = getCurr(); + return { + render: () => `${curr || '-'}` + }; + }); + return renderSnippet(currSnippet, { curr: row.original.currency_key || '' }); + } + }, + + // 12. PESO UNITARIO + { + accessorKey: "unit_weight", + header: "Peso Unitario", + cell: ({ row }) => { + const weightSnippet = createRawSnippet<[{ weight: number | null, type: string | null }]>((getWeight) => { + const { weight, type } = getWeight(); + return { + render: () => { + if (weight === null || weight === undefined) return '-'; + return `
${weight.toFixed(4)} ${type || ''}
`; + } + }; + }); + return renderSnippet(weightSnippet, { + weight: row.original.unit_weight, + type: row.original.weight_type + }); + } + }, + + // 13. U.M. EXIST (Stock UOM del inv_data) + { + id: "equivalent_uom", + header: "U.M. Exist", + cell: ({ row }) => { + const eqUomSnippet = createRawSnippet<[{ uom: string }]>((getUom) => { + const { uom } = getUom(); + return { + render: () => `
${uom || '-'}
` + }; + }); + return renderSnippet(eqUomSnippet, { uom: row.original.inv_data?.equivalent_uom || '' }); + } + }, + + // 14. FECHA MODIFICACIÓN + { + accessorKey: "updated_at", + header: "Fecha Modificación", + cell: ({ row }) => { + const dateSnippet = createRawSnippet<[{ date: string }]>((getDate) => { + const { date } = getDate(); + if (!date) return { render: () => '-' }; + const formatted = new Date(date).toLocaleDateString('es-MX', { + year: 'numeric', + month: '2-digit', + day: '2-digit' + }); + return { + render: () => `
${formatted}
` + }; + }); + return renderSnippet(dateSnippet, { date: row.original.updated_at }); + } + }, + // ACCIONES { id: "actions", diff --git a/frontend/src/lib/components/dashboard/pedimentos/columns.ts b/frontend/src/lib/components/dashboard/pedimentos/columns.ts index 340ea641..2bdfbe51 100644 --- a/frontend/src/lib/components/dashboard/pedimentos/columns.ts +++ b/frontend/src/lib/components/dashboard/pedimentos/columns.ts @@ -117,6 +117,152 @@ export function createColumns(onSuccess?: () => void): ColumnDef[] { return renderSnippet(numberSnippet, { number: fullNumber }); } }, + { + accessorKey: "pedimento_type", + header: "Tipo", + cell: ({ row }) => { + const typeSnippet = createRawSnippet<[{ type?: string | null }]>((getType) => { + const { type } = getType(); + return { + render: () => + `
${type || '-'}
` + }; + }); + return renderSnippet(typeSnippet, { type: row.original.pedimento_type }); + } + }, + { + accessorKey: "pedimento_code", + header: "Clave", + cell: ({ row }) => { + const codeSnippet = createRawSnippet<[{ code?: string | null }]>((getCode) => { + const { code } = getCode(); + return { + render: () => + `
${code || '-'}
` + }; + }); + return renderSnippet(codeSnippet, { code: row.original.pedimento_code }); + } + }, + { + accessorKey: "regime", + header: "Régimen", + cell: ({ row }) => { + const regimeSnippet = createRawSnippet<[{ regime?: string | null }]>((getRegime) => { + const { regime } = getRegime(); + return { + render: () => + `
${regime || '-'}
` + }; + }); + return renderSnippet(regimeSnippet, { regime: row.original.regime }); + } + }, + { + accessorKey: "pedimento_dates.start_date", + header: "Fecha Inicio", + cell: ({ row }) => { + const dateSnippet = createRawSnippet<[{ date: string }]>((getDate) => { + const { date } = getDate(); + return { + render: () => + `
${date}
` + }; + }); + return renderSnippet(dateSnippet, { date: formatDate(row.original.pedimento_dates?.start_date) }); + } + }, + { + accessorKey: "pedimento_dates.end_date", + header: "Fecha Final", + cell: ({ row }) => { + const dateSnippet = createRawSnippet<[{ date: string }]>((getDate) => { + const { date } = getDate(); + return { + render: () => + `
${date}
` + }; + }); + return renderSnippet(dateSnippet, { date: formatDate(row.original.pedimento_dates?.end_date) }); + } + }, + { + accessorKey: "pedimento_dates.payment_date", + header: "Fecha de Pago", + cell: ({ row }) => { + const dateSnippet = createRawSnippet<[{ date: string }]>((getDate) => { + const { date } = getDate(); + return { + render: () => + `
${date}
` + }; + }); + return renderSnippet(dateSnippet, { date: formatDate(row.original.pedimento_dates?.payment_date) }); + } + }, + { + accessorKey: "pedimento_config_update_rectification.pediment_rectifed_18", + header: "Pedimento 18", + cell: ({ row }) => { + const ped18Snippet = createRawSnippet<[{ value?: string | null }]>((getValue) => { + const { value } = getValue(); + return { + render: () => + `
${value || '-'}
` + }; + }); + return renderSnippet(ped18Snippet, { value: row.original.pedimento_config_update_rectification?.pediment_rectifed_18 }); + } + }, + { + accessorKey: "pedimento_config_update_rectification.r1", + header: "Pedimento R1", + cell: ({ row }) => { + const r1Snippet = createRawSnippet<[{ value?: string | null }]>((getValue) => { + const { value } = getValue(); + return { + render: () => + `
${value || '-'}
` + }; + }); + return renderSnippet(r1Snippet, { value: row.original.pedimento_config_update_rectification?.r1 }); + } + }, + { + accessorKey: "pedimento_validation.electronic_signature", + header: "Acuse Electrónico", + cell: ({ row }) => { + const ackSnippet = createRawSnippet<[{ value?: string | null }]>((getValue) => { + const { value } = getValue(); + const hasValue = value && value !== ''; + return { + render: () => + ` + ${hasValue ? 'Sí' : 'No'} + ` + }; + }); + return renderSnippet(ackSnippet, { value: row.original.pedimento_validation?.electronic_signature }); + } + }, + { + accessorKey: "pedimento_payments.total_contributions", + header: "¿Se pagó el impuesto?", + cell: ({ row }) => { + const paidSnippet = createRawSnippet<[{ value?: string | null }]>((getValue) => { + const { value } = getValue(); + const isPaid = value && parseFloat(value) > 0; + return { + render: () => + ` + ${isPaid ? 'Sí' : 'No'} + ` + }; + }); + return renderSnippet(paidSnippet, { value: row.original.pedimento_payments?.total_contributions }); + } + }, { accessorKey: "client_id", header: "Cliente", diff --git a/frontend/src/routes/dashboard/clients_and_providers/+page.svelte b/frontend/src/routes/dashboard/clients_and_providers/+page.svelte index 2cc0ee23..78e70445 100644 --- a/frontend/src/routes/dashboard/clients_and_providers/+page.svelte +++ b/frontend/src/routes/dashboard/clients_and_providers/+page.svelte @@ -203,7 +203,7 @@ {#if error} - + Error @@ -214,11 +214,10 @@ {/if} - +
- Listado de Clientes y Proveedores Mostrando {allItems.length} de {totalItems} registros {#if companyStore.activeCompany} diff --git a/frontend/src/routes/dashboard/customs_brokers/+page.svelte b/frontend/src/routes/dashboard/customs_brokers/+page.svelte index 611dce23..19b39457 100644 --- a/frontend/src/routes/dashboard/customs_brokers/+page.svelte +++ b/frontend/src/routes/dashboard/customs_brokers/+page.svelte @@ -130,8 +130,13 @@ if (response.data) { if (Array.isArray(response.data)) { brokersList = response.data; - } else if (response.data.items && Array.isArray(response.data.items)) { - brokersList = response.data.items; + } else if (typeof response.data === 'object' && 'items' in response.data) { + const dataWithItems = response.data as { items?: CustomsBroker[] }; + if (Array.isArray(dataWithItems.items)) { + brokersList = dataWithItems.items; + } else { + brokersList = []; + } } else { console.error("⚠️ Estructura de datos inesperada:", response.data); brokersList = []; @@ -171,9 +176,8 @@
- + - Buscar Agente Aduanal Ingresa la clave del agente aduanal para buscarlo {#if companyStore.activeCompany} @@ -222,7 +226,7 @@ - +
@@ -271,4 +275,12 @@
- \ No newline at end of file + { + // El CreateDialog maneja la creación internamente + // Después de guardar exitosamente, recargar los datos + await handleSuccess(); + }} +/> \ No newline at end of file diff --git a/frontend/src/routes/dashboard/goods/parts/+page.svelte b/frontend/src/routes/dashboard/goods/parts/+page.svelte index dae74cc6..bba5417e 100644 --- a/frontend/src/routes/dashboard/goods/parts/+page.svelte +++ b/frontend/src/routes/dashboard/goods/parts/+page.svelte @@ -121,11 +121,7 @@
- - - Búsqueda Rápida - Ingresa el número de parte o descripción para filtrar. - +
@@ -155,7 +151,7 @@ - + {#if isSearching} diff --git a/frontend/src/routes/dashboard/invoices/+page.svelte b/frontend/src/routes/dashboard/invoices/+page.svelte index 0c70436d..7108c742 100644 --- a/frontend/src/routes/dashboard/invoices/+page.svelte +++ b/frontend/src/routes/dashboard/invoices/+page.svelte @@ -392,11 +392,7 @@
- - - Filtros - Filtra las facturas por diferentes criterios (los filtros se aplican automáticamente) - +
@@ -458,7 +454,7 @@ {#if error} - + Error {error} @@ -467,7 +463,7 @@ {/if} - +
diff --git a/frontend/src/routes/dashboard/pedimentos/+page.svelte b/frontend/src/routes/dashboard/pedimentos/+page.svelte index 711d5173..28b09205 100644 --- a/frontend/src/routes/dashboard/pedimentos/+page.svelte +++ b/frontend/src/routes/dashboard/pedimentos/+page.svelte @@ -267,18 +267,15 @@
- - - Filtros - Filtra los pedimentos por diferentes criterios - + -
{ e.preventDefault(); applyFilters(); }} class="grid grid-cols-1 md:grid-cols-4 gap-4"> +
- -
- - -
- +
{#if error} - + Error {error} @@ -331,7 +320,7 @@ {/if} - +