feat: update invoice and item tabs to use 'Datos Soriana' instead of 'Datos Sonana'

refactor: enhance package transportation tab to prevent infinite loops and load data only once

fix: include credentials in fetch request for company data

feat: implement loading of additional pedimento data in edit page, including contributions, packages, transport carriers, guides, seals, and containers

add: create DTOs and models for pedimento contributions, packages, transport carriers, guides, seals, and containers
This commit is contained in:
Galindo97
2026-01-02 18:04:28 -06:00
parent 0b4c385aeb
commit 97a318fdba
27 changed files with 997 additions and 110 deletions

View File

@@ -23,6 +23,7 @@ class PedimentoConfigAdditionalBase(BaseModel):
None, description="Send 502 validation file for consolidated"
)
add_remove_norms: Optional[bool] = Field(None, description="Add/remove norms")
choose_invoice_cove: Optional[bool] = Field(None, description="Choose invoice COVE in items")
class PedimentoConfigAdditionalCreate(PedimentoConfigAdditionalBase):
@@ -40,6 +41,7 @@ class PedimentoConfigAdditionalUpdate(BaseModel):
enable_import_invoice_recipient: Optional[bool] = None
send_502_validation_file_for_consolidated: Optional[bool] = None
add_remove_norms: Optional[bool] = None
choose_invoice_cove: Optional[bool] = None
class PedimentoConfigAdditionalResponse(PedimentoConfigAdditionalBase):

View File

@@ -0,0 +1,49 @@
from decimal import Decimal
from datetime import datetime
from typing import Optional
from pydantic import BaseModel, ConfigDict
class PedimentoContributionBase(BaseModel):
"""Base schema for Pedimento Contribution"""
pedimento_id: Optional[int] = None
contribucion: Optional[str] = None
tipo_tasa: Optional[str] = None
tasa: Optional[Decimal] = None
forma_pago: Optional[str] = None
importe: Optional[Decimal] = None
gravamen: Optional[str] = None
abreviacion: Optional[str] = None
forma_pago_2: Optional[str] = None
importe_2: Optional[Decimal] = None
class PedimentoContributionCreate(PedimentoContributionBase):
"""Schema for creating Pedimento Contribution"""
pass
class PedimentoContributionUpdate(BaseModel):
"""Schema for updating Pedimento Contribution"""
contribucion: Optional[str] = None
tipo_tasa: Optional[str] = None
tasa: Optional[Decimal] = None
forma_pago: Optional[str] = None
importe: Optional[Decimal] = None
gravamen: Optional[str] = None
abreviacion: Optional[str] = None
forma_pago_2: Optional[str] = None
importe_2: Optional[Decimal] = None
class PedimentoContributionResponse(PedimentoContributionBase):
"""Schema for Pedimento Contribution response"""
id: int
created_at: datetime
model_config = ConfigDict(from_attributes=True)

View File

@@ -9,7 +9,7 @@ class PedimentoDatesBase(BaseModel):
entry_date: Optional[datetime] = Field(None, description="Entry date")
pedimento_date: Optional[datetime] = Field(None, description="Pedimento date")
payment_date: datetime = Field(..., description="Payment date")
payment_date: Optional[datetime] = Field(None, description="Payment date")
rectification_payment_date: Optional[datetime] = Field(
None, description="Rectification payment date"
)
@@ -26,7 +26,7 @@ class PedimentoDatesCreate(BaseModel):
entry_date: Optional[datetime] = Field(None, description="Entry date")
pedimento_date: Optional[datetime] = Field(None, description="Pedimento date")
payment_date: datetime = Field(..., description="Payment date")
payment_date: Optional[datetime] = Field(None, description="Payment date")
rectification_payment_date: Optional[datetime] = Field(None, description="Rectification payment date")
extraction_date: Optional[datetime] = Field(None, description="Extraction date")
submission_date: Optional[datetime] = Field(None, description="Submission date")

View File

@@ -0,0 +1,178 @@
from datetime import datetime
from typing import Optional
from pydantic import BaseModel, ConfigDict
class PedimentoPackagesBase(BaseModel):
"""Base schema for Pedimento Packages"""
pedimento_id: Optional[int] = None
quantity: Optional[int] = None
brand: Optional[str] = None
number: Optional[str] = None
vehicles: Optional[int] = None
class PedimentoPackagesCreate(PedimentoPackagesBase):
"""Schema for creating Pedimento Packages"""
pass
class PedimentoPackagesUpdate(BaseModel):
"""Schema for updating Pedimento Packages"""
quantity: Optional[int] = None
brand: Optional[str] = None
number: Optional[str] = None
vehicles: Optional[int] = None
class PedimentoPackagesResponse(PedimentoPackagesBase):
"""Schema for Pedimento Packages response"""
id: int
created_at: datetime
model_config = ConfigDict(from_attributes=True)
class PedimentoTransportCarrierBase(BaseModel):
"""Base schema for Pedimento Transport Carrier"""
pedimento_id: Optional[int] = None
carrier: Optional[str] = None
rfc: Optional[str] = None
curp: Optional[str] = None
name: Optional[str] = None
address: Optional[str] = None
city: Optional[str] = None
state: Optional[str] = None
country: Optional[str] = None
tax_id: Optional[str] = None
total_packages: Optional[int] = None
identification: Optional[str] = None
class PedimentoTransportCarrierCreate(PedimentoTransportCarrierBase):
"""Schema for creating Pedimento Transport Carrier"""
pass
class PedimentoTransportCarrierUpdate(BaseModel):
"""Schema for updating Pedimento Transport Carrier"""
carrier: Optional[str] = None
rfc: Optional[str] = None
curp: Optional[str] = None
name: Optional[str] = None
address: Optional[str] = None
city: Optional[str] = None
state: Optional[str] = None
country: Optional[str] = None
tax_id: Optional[str] = None
total_packages: Optional[int] = None
identification: Optional[str] = None
class PedimentoTransportCarrierResponse(PedimentoTransportCarrierBase):
"""Schema for Pedimento Transport Carrier response"""
id: int
created_at: datetime
model_config = ConfigDict(from_attributes=True)
class PedimentoSealBase(BaseModel):
"""Base schema for Pedimento Seal"""
pedimento_id: Optional[int] = None
number: Optional[str] = None
identification: Optional[str] = None
class PedimentoSealCreate(PedimentoSealBase):
"""Schema for creating Pedimento Seal"""
pass
class PedimentoSealUpdate(BaseModel):
"""Schema for updating Pedimento Seal"""
number: Optional[str] = None
identification: Optional[str] = None
class PedimentoSealResponse(PedimentoSealBase):
"""Schema for Pedimento Seal response"""
id: int
created_at: datetime
model_config = ConfigDict(from_attributes=True)
class PedimentoContainerBase(BaseModel):
"""Base schema for Pedimento Container"""
pedimento_id: Optional[int] = None
number: Optional[str] = None
identification: Optional[str] = None
type: Optional[str] = None
class PedimentoContainerCreate(PedimentoContainerBase):
"""Schema for creating Pedimento Container"""
pass
class PedimentoContainerUpdate(BaseModel):
"""Schema for updating Pedimento Container"""
number: Optional[str] = None
identification: Optional[str] = None
type: Optional[str] = None
class PedimentoContainerResponse(PedimentoContainerBase):
"""Schema for Pedimento Container response"""
id: int
created_at: datetime
model_config = ConfigDict(from_attributes=True)
class PedimentoGuideBase(BaseModel):
"""Base schema for Pedimento Guide"""
pedimento_id: Optional[int] = None
guide: Optional[str] = None
identifier: Optional[str] = None
class PedimentoGuideCreate(PedimentoGuideBase):
"""Schema for creating Pedimento Guide"""
pass
class PedimentoGuideUpdate(BaseModel):
"""Schema for updating Pedimento Guide"""
guide: Optional[str] = None
identifier: Optional[str] = None
class PedimentoGuideResponse(PedimentoGuideBase):
"""Schema for Pedimento Guide response"""
id: int
created_at: datetime
model_config = ConfigDict(from_attributes=True)

View File

@@ -16,6 +16,22 @@ from .pedimento_dates import PedimentoDatesCreate, PedimentoDatesResponse
from .pedimento_decrementables import PedimentoDecrementablesCreate, PedimentoDecrementablesResponse
from .pedimento_incrementables import PedimentoIncrementablesCreate, PedimentoIncrementablesResponse
from .pedimento_indexes import PedimentoIndexesCreate, PedimentoIndexesResponse
from .pedimento_packages_transport import (
PedimentoContainerCreate,
PedimentoContainerResponse,
PedimentoPackagesCreate,
PedimentoPackagesResponse,
PedimentoSealCreate,
PedimentoSealResponse,
PedimentoTransportCarrierCreate,
PedimentoTransportCarrierResponse,
PedimentoGuideCreate,
PedimentoGuideResponse,
)
from .pedimento_contributions import (
PedimentoContributionCreate,
PedimentoContributionResponse,
)
from .pedimento_payments import PedimentoPaymentsCreate, PedimentoPaymentsResponse
from .pedimento_rectification_destination import PedimentoRectificationDestinationCreate, PedimentoRectificationDestinationResponse
from .pedimento_rectification_origin import PedimentoRectificationOriginCreate, PedimentoRectificationOriginResponse
@@ -121,6 +137,12 @@ class PedimentosUpdate(BaseModel):
pedimento_config_surcharges: Optional[PedimentoConfigSurchargesCreate] = None
pedimento_config_update_rectification: Optional[PedimentoConfigUpdateRectificationCreate] = None
pedimento_config_updates: Optional[PedimentoConfigUpdatesCreate] = None
pedimento_packages: Optional[PedimentoPackagesCreate] = None
pedimento_transport_carriers: Optional[list[PedimentoTransportCarrierCreate]] = None
pedimento_guides: Optional[list[PedimentoGuideCreate]] = None
pedimento_contributions: Optional[list[PedimentoContributionCreate]] = None
pedimento_seals: Optional[list[PedimentoSealCreate]] = None
pedimento_containers: Optional[list[PedimentoContainerCreate]] = None
class PedimentosResponse(PedimentosBase):
@@ -146,5 +168,11 @@ class PedimentosResponse(PedimentosBase):
pedimento_config_surcharges: Optional[PedimentoConfigSurchargesResponse] = None
pedimento_config_update_rectification: Optional[PedimentoConfigUpdateRectificationResponse] = None
pedimento_config_updates: Optional[PedimentoConfigUpdatesResponse] = None
pedimento_packages: Optional[PedimentoPackagesResponse] = None
pedimento_transport_carriers: Optional[list[PedimentoTransportCarrierResponse]] = None
pedimento_guides: Optional[list[PedimentoGuideResponse]] = None
pedimento_contributions: Optional[list[PedimentoContributionResponse]] = None
pedimento_seals: Optional[list[PedimentoSealResponse]] = None
pedimento_containers: Optional[list[PedimentoContainerResponse]] = None
model_config = ConfigDict(from_attributes=True)

View File

@@ -0,0 +1,84 @@
"""Models for pedimentos module"""
# Import all models to ensure they are registered with SQLAlchemy
from api.v1.modules.a76.pedmientos.models.pedimento_config_additional import (
PedimentoConfigAdditional,
)
from api.v1.modules.a76.pedmientos.models.pedimento_config_calculations import (
PedimentoConfigCalculations,
)
from api.v1.modules.a76.pedmientos.models.pedimento_config_parameters import (
PedimentoConfigParameters,
)
from api.v1.modules.a76.pedmientos.models.pedimento_config_surcharges import (
PedimentoConfigSurcharges,
)
from api.v1.modules.a76.pedmientos.models.pedimento_config_update_rectification import (
PedimentoConfigUpdateRectification,
)
from api.v1.modules.a76.pedmientos.models.pedimento_config_updates import (
PedimentoConfigUpdates,
)
from api.v1.modules.a76.pedmientos.models.pedimento_containers import (
PedimentoContainers,
)
from api.v1.modules.a76.pedmientos.models.pedimento_contributions import (
PedimentoContributions,
)
from api.v1.modules.a76.pedmientos.models.pedimento_customs_offices import (
PedimentoCustomsOffices,
)
from api.v1.modules.a76.pedmientos.models.pedimento_dates import PedimentoDates
from api.v1.modules.a76.pedmientos.models.pedimento_decrementables import (
PedimentoDecrementables,
)
from api.v1.modules.a76.pedmientos.models.pedimento_incrementables import (
PedimentoIncrementables,
)
from api.v1.modules.a76.pedmientos.models.pedimento_indexes import PedimentoIndexes
from api.v1.modules.a76.pedmientos.models.pedimento_packages import PedimentoPackages
from api.v1.modules.a76.pedmientos.models.pedimento_payments import PedimentoPayments
from api.v1.modules.a76.pedmientos.models.pedimento_guides import PedimentoGuides
from api.v1.modules.a76.pedmientos.models.pedimento_rectification_destination import (
PedimentoRectificationDestination,
)
from api.v1.modules.a76.pedmientos.models.pedimento_rectification_origin import (
PedimentoRectificationOrigin,
)
from api.v1.modules.a76.pedmientos.models.pedimento_seals import PedimentoSeals
from api.v1.modules.a76.pedmientos.models.pedimento_transport_carriers import (
PedimentoTransportCarriers,
)
from api.v1.modules.a76.pedmientos.models.pedimento_transport_means import (
PedimentoTransportMeans,
)
from api.v1.modules.a76.pedmientos.models.pedimento_validation import (
PedimentoValidation,
)
from api.v1.modules.a76.pedmientos.models.pedimentos import Pedimentos
__all__ = [
"Pedimentos",
"PedimentoConfigAdditional",
"PedimentoConfigCalculations",
"PedimentoConfigParameters",
"PedimentoConfigSurcharges",
"PedimentoConfigUpdateRectification",
"PedimentoConfigUpdates",
"PedimentoContainers",
"PedimentoContributions",
"PedimentoCustomsOffices",
"PedimentoDates",
"PedimentoDecrementables",
"PedimentoIncrementables",
"PedimentoIndexes",
"PedimentoPackages",
"PedimentoGuides",
"PedimentoPayments",
"PedimentoRectificationDestination",
"PedimentoRectificationOrigin",
"PedimentoSeals",
"PedimentoTransportCarriers",
"PedimentoTransportMeans",
"PedimentoValidation",
]

View File

@@ -43,6 +43,7 @@ class PedimentoConfigAdditional(Base, TenantScopedMixin, TimestampMixin):
enable_import_invoice_recipient: Mapped[bool] = mapped_column(Boolean, default=False)
send_502_validation_file_for_consolidated: Mapped[bool] = mapped_column(Boolean, default=False)
add_remove_norms: Mapped[bool] = mapped_column(Boolean, default=False)
choose_invoice_cove: Mapped[bool] = mapped_column(Boolean, default=False)
pedimento: Mapped["Pedimentos"] = relationship(
"Pedimentos", back_populates="pedimento_config_additional"

View File

@@ -0,0 +1,39 @@
from typing import TYPE_CHECKING
from api.v1.common.base_models import TenantScopedMixin, TimestampMixin
from core.database import Base
from sqlalchemy import (
ForeignKeyConstraint,
Integer,
PrimaryKeyConstraint,
String,
)
from sqlalchemy.orm import Mapped, mapped_column, relationship
if TYPE_CHECKING:
from api.v1.modules.a76.pedmientos.models.pedimentos import Pedimentos
class PedimentoContainers(Base, TenantScopedMixin, TimestampMixin):
__tablename__ = "pedimento_containers"
__table_args__ = (
PrimaryKeyConstraint("id", name="pedimento_containers_pkey"),
ForeignKeyConstraint(
["pedimento_id"],
["a76.pedimentos.id"],
ondelete="CASCADE",
name="fk_pedimento_containers",
),
{"schema": "a76"},
)
id: Mapped[int] = mapped_column(Integer)
pedimento_id: Mapped[int] = mapped_column(Integer, nullable=False)
number: Mapped[str | None] = mapped_column(String(100), nullable=True)
identification: Mapped[str | None] = mapped_column(String(100), nullable=True)
type: Mapped[str | None] = mapped_column(String(50), nullable=True)
pedimento: Mapped["Pedimentos"] = relationship(
"Pedimentos", back_populates="pedimento_containers"
)

View File

@@ -0,0 +1,46 @@
from typing import TYPE_CHECKING
from api.v1.common.base_models import TenantScopedMixin, TimestampMixin
from core.database import Base
from sqlalchemy import (
ForeignKeyConstraint,
Integer,
Numeric,
PrimaryKeyConstraint,
String,
)
from sqlalchemy.orm import Mapped, mapped_column, relationship
if TYPE_CHECKING:
from api.v1.modules.a76.pedmientos.models.pedimentos import Pedimentos
class PedimentoContributions(Base, TenantScopedMixin, TimestampMixin):
__tablename__ = "pedimento_contributions"
__table_args__ = (
PrimaryKeyConstraint("id", name="pedimento_contributions_pkey"),
ForeignKeyConstraint(
["pedimento_id"],
["a76.pedimentos.id"],
ondelete="CASCADE",
name="fk_pedimento_contributions",
),
{"schema": "a76"},
)
id: Mapped[int] = mapped_column(Integer)
pedimento_id: Mapped[int] = mapped_column(Integer, nullable=False)
contribucion: Mapped[str | None] = mapped_column(String(50), nullable=True)
tipo_tasa: Mapped[str | None] = mapped_column(String(100), nullable=True)
tasa: Mapped[Numeric | None] = mapped_column(Numeric(15, 8), nullable=True)
forma_pago: Mapped[str | None] = mapped_column(String(50), nullable=True)
importe: Mapped[Numeric | None] = mapped_column(Numeric(17, 2), nullable=True)
gravamen: Mapped[str | None] = mapped_column(String(100), nullable=True)
abreviacion: Mapped[str | None] = mapped_column(String(50), nullable=True)
forma_pago_2: Mapped[str | None] = mapped_column(String(50), nullable=True)
importe_2: Mapped[Numeric | None] = mapped_column(Numeric(17, 2), nullable=True)
pedimento: Mapped["Pedimentos"] = relationship(
"Pedimentos", back_populates="pedimento_contributions"
)

View File

@@ -44,7 +44,7 @@ class PedimentoDates(Base, TenantScopedMixin, TimestampMixin):
entry_date: Mapped[Optional[datetime]] = mapped_column(DateTime)
pedimento_date: Mapped[Optional[datetime]] = mapped_column(DateTime)
payment_date: Mapped[datetime] = mapped_column(DateTime)
payment_date: Mapped[Optional[datetime]] = mapped_column(DateTime)
rectification_payment_date: Mapped[Optional[datetime]] = mapped_column(DateTime)
extraction_date: Mapped[Optional[datetime]] = mapped_column(DateTime)
submission_date: Mapped[Optional[datetime]] = mapped_column(DateTime)

View File

@@ -1,5 +1,5 @@
from decimal import Decimal
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Optional
from api.v1.common.base_models import TenantScopedMixin, TimestampMixin
from core.database import Base
@@ -40,15 +40,15 @@ class PedimentoDecrementables(Base, TenantScopedMixin, TimestampMixin):
id: Mapped[int] = mapped_column(Integer)
pedimento_id: Mapped[int] = mapped_column(Integer, nullable=False)
freight: Mapped[Decimal] = mapped_column(Numeric(13, 2))
insurance: Mapped[Decimal] = mapped_column(Numeric(13, 2))
loading: Mapped[Decimal] = mapped_column(Numeric(13, 2))
unloading: Mapped[Decimal] = mapped_column(Numeric(13, 2))
others: Mapped[Decimal] = mapped_column(Numeric(13, 2))
currency: Mapped[str] = mapped_column(String(3))
currency_factor: Mapped[Decimal] = mapped_column(Numeric(15, 8))
not_affect_usd_value: Mapped[int] = mapped_column(SmallInteger)
not_affect_customs_value: Mapped[int] = mapped_column(SmallInteger)
freight: Mapped[Optional[Decimal]] = mapped_column(Numeric(13, 2))
insurance: Mapped[Optional[Decimal]] = mapped_column(Numeric(13, 2))
loading: Mapped[Optional[Decimal]] = mapped_column(Numeric(13, 2))
unloading: Mapped[Optional[Decimal]] = mapped_column(Numeric(13, 2))
others: Mapped[Optional[Decimal]] = mapped_column(Numeric(13, 2))
currency: Mapped[Optional[str]] = mapped_column(String(3))
currency_factor: Mapped[Optional[Decimal]] = mapped_column(Numeric(15, 8))
not_affect_usd_value: Mapped[Optional[int]] = mapped_column(SmallInteger)
not_affect_customs_value: Mapped[Optional[int]] = mapped_column(SmallInteger)
pedimento: Mapped["Pedimentos"] = relationship(
"Pedimentos", back_populates="pedimento_decrementables"

View File

@@ -0,0 +1,38 @@
from typing import TYPE_CHECKING
from api.v1.common.base_models import TenantScopedMixin, TimestampMixin
from core.database import Base
from sqlalchemy import (
ForeignKeyConstraint,
Integer,
PrimaryKeyConstraint,
String,
)
from sqlalchemy.orm import Mapped, mapped_column, relationship
if TYPE_CHECKING:
from api.v1.modules.a76.pedmientos.models.pedimentos import Pedimentos
class PedimentoGuides(Base, TenantScopedMixin, TimestampMixin):
__tablename__ = "pedimento_guides"
__table_args__ = (
PrimaryKeyConstraint("id", name="pedimento_guides_pkey"),
ForeignKeyConstraint(
["pedimento_id"],
["a76.pedimentos.id"],
ondelete="CASCADE",
name="fk_pedimento_guides",
),
{"schema": "a76"},
)
id: Mapped[int] = mapped_column(Integer)
pedimento_id: Mapped[int] = mapped_column(Integer, nullable=False)
guide: Mapped[str | None] = mapped_column(String(100), nullable=True)
identifier: Mapped[str | None] = mapped_column(String(100), nullable=True)
pedimento: Mapped["Pedimentos"] = relationship(
"Pedimentos", back_populates="pedimento_guides"
)

View File

@@ -1,5 +1,5 @@
from decimal import Decimal
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Optional
from api.v1.common.base_models import TenantScopedMixin, TimestampMixin
from core.database import Base
@@ -40,16 +40,16 @@ class PedimentoIncrementables(Base, TenantScopedMixin, TimestampMixin):
id: Mapped[int] = mapped_column(Integer)
pedimento_id: Mapped[int] = mapped_column(Integer, nullable=False)
insured_value: Mapped[Decimal] = mapped_column(Numeric(13, 2))
freight: Mapped[Decimal] = mapped_column(Numeric(13, 2))
insurance: Mapped[Decimal] = mapped_column(Numeric(13, 2))
packaging: Mapped[Decimal] = mapped_column(Numeric(13, 2))
others: Mapped[Decimal] = mapped_column(Numeric(13, 3))
deductibles: Mapped[Decimal] = mapped_column(Numeric(13, 3))
currency: Mapped[str] = mapped_column(String(3))
currency_factor: Mapped[Decimal] = mapped_column(Numeric(15, 8))
not_affect_usd_value: Mapped[int] = mapped_column(SmallInteger)
not_affect_customs_value: Mapped[int] = mapped_column(SmallInteger)
insured_value: Mapped[Optional[Decimal]] = mapped_column(Numeric(13, 2))
freight: Mapped[Optional[Decimal]] = mapped_column(Numeric(13, 2))
insurance: Mapped[Optional[Decimal]] = mapped_column(Numeric(13, 2))
packaging: Mapped[Optional[Decimal]] = mapped_column(Numeric(13, 2))
others: Mapped[Optional[Decimal]] = mapped_column(Numeric(13, 3))
deductibles: Mapped[Optional[Decimal]] = mapped_column(Numeric(13, 3))
currency: Mapped[Optional[str]] = mapped_column(String(3))
currency_factor: Mapped[Optional[Decimal]] = mapped_column(Numeric(15, 8))
not_affect_usd_value: Mapped[Optional[int]] = mapped_column(SmallInteger)
not_affect_customs_value: Mapped[Optional[int]] = mapped_column(SmallInteger)
pedimento: Mapped["Pedimentos"] = relationship(
"Pedimentos", back_populates="pedimento_incrementables"

View File

@@ -1,5 +1,5 @@
from decimal import Decimal
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Optional
from api.v1.common.base_models import TenantScopedMixin, TimestampMixin
from core.database import Base
@@ -39,9 +39,9 @@ class PedimentoIndexes(Base, TenantScopedMixin, TimestampMixin):
id: Mapped[int] = mapped_column(Integer)
pedimento_id: Mapped[int] = mapped_column(Integer, nullable=False)
update_factor_type: Mapped[int] = mapped_column(SmallInteger)
update_factor: Mapped[Decimal] = mapped_column(Numeric(7, 4))
manual_update_factor: Mapped[int] = mapped_column(SmallInteger)
update_factor_type: Mapped[Optional[int]] = mapped_column(SmallInteger)
update_factor: Mapped[Optional[Decimal]] = mapped_column(Numeric(7, 4))
manual_update_factor: Mapped[Optional[int]] = mapped_column(SmallInteger)
pedimento: Mapped["Pedimentos"] = relationship(
"Pedimentos", back_populates="pedimento_indexes"

View File

@@ -0,0 +1,48 @@
from typing import TYPE_CHECKING
from api.v1.common.base_models import TenantScopedMixin, TimestampMixin
from core.database import Base
from sqlalchemy import (
ForeignKeyConstraint,
Integer,
Numeric,
PrimaryKeyConstraint,
String,
UniqueConstraint,
)
from sqlalchemy.orm import Mapped, mapped_column, relationship
if TYPE_CHECKING:
from api.v1.modules.a76.pedmientos.models.pedimentos import Pedimentos
class PedimentoPackages(Base, TenantScopedMixin, TimestampMixin):
__tablename__ = "pedimento_packages"
__table_args__ = (
PrimaryKeyConstraint("id", name="pedimento_packages_pkey"),
ForeignKeyConstraint(
["pedimento_id"],
["a76.pedimentos.id"],
ondelete="CASCADE",
name="fk_pedimento_packages",
),
UniqueConstraint(
"tenant_id",
"company_id",
"pedimento_id",
name="pedimento_packages_pedimento_id_key",
),
{"schema": "a76"},
)
id: Mapped[int] = mapped_column(Integer)
pedimento_id: Mapped[int] = mapped_column(Integer, nullable=False)
quantity: Mapped[int | None] = mapped_column(Integer, nullable=True)
brand: Mapped[str | None] = mapped_column(String(100), nullable=True)
number: Mapped[str | None] = mapped_column(String(100), nullable=True)
vehicles: Mapped[int | None] = mapped_column(Integer, nullable=True)
pedimento: Mapped["Pedimentos"] = relationship(
"Pedimentos", back_populates="pedimento_packages"
)

View File

@@ -0,0 +1,38 @@
from typing import TYPE_CHECKING
from api.v1.common.base_models import TenantScopedMixin, TimestampMixin
from core.database import Base
from sqlalchemy import (
ForeignKeyConstraint,
Integer,
PrimaryKeyConstraint,
String,
)
from sqlalchemy.orm import Mapped, mapped_column, relationship
if TYPE_CHECKING:
from api.v1.modules.a76.pedmientos.models.pedimentos import Pedimentos
class PedimentoSeals(Base, TenantScopedMixin, TimestampMixin):
__tablename__ = "pedimento_seals"
__table_args__ = (
PrimaryKeyConstraint("id", name="pedimento_seals_pkey"),
ForeignKeyConstraint(
["pedimento_id"],
["a76.pedimentos.id"],
ondelete="CASCADE",
name="fk_pedimento_seals",
),
{"schema": "a76"},
)
id: Mapped[int] = mapped_column(Integer)
pedimento_id: Mapped[int] = mapped_column(Integer, nullable=False)
number: Mapped[str | None] = mapped_column(String(100), nullable=True)
identification: Mapped[str | None] = mapped_column(String(100), nullable=True)
pedimento: Mapped["Pedimentos"] = relationship(
"Pedimentos", back_populates="pedimento_seals"
)

View File

@@ -0,0 +1,47 @@
from typing import TYPE_CHECKING
from api.v1.common.base_models import TenantScopedMixin, TimestampMixin
from core.database import Base
from sqlalchemy import (
ForeignKeyConstraint,
Integer,
PrimaryKeyConstraint,
String,
)
from sqlalchemy.orm import Mapped, mapped_column, relationship
if TYPE_CHECKING:
from api.v1.modules.a76.pedmientos.models.pedimentos import Pedimentos
class PedimentoTransportCarriers(Base, TenantScopedMixin, TimestampMixin):
__tablename__ = "pedimento_transport_carriers"
__table_args__ = (
PrimaryKeyConstraint("id", name="pedimento_transport_carriers_pkey"),
ForeignKeyConstraint(
["pedimento_id"],
["a76.pedimentos.id"],
ondelete="CASCADE",
name="fk_pedimento_transport_carriers",
),
{"schema": "a76"},
)
id: Mapped[int] = mapped_column(Integer)
pedimento_id: Mapped[int] = mapped_column(Integer, nullable=False)
carrier: Mapped[str | None] = mapped_column(String(200), nullable=True)
rfc: Mapped[str | None] = mapped_column(String(20), nullable=True)
curp: Mapped[str | None] = mapped_column(String(20), nullable=True)
name: Mapped[str | None] = mapped_column(String(200), nullable=True)
address: Mapped[str | None] = mapped_column(String(300), nullable=True)
city: Mapped[str | None] = mapped_column(String(100), nullable=True)
state: Mapped[str | None] = mapped_column(String(100), nullable=True)
country: Mapped[str | None] = mapped_column(String(3), nullable=True)
tax_id: Mapped[str | None] = mapped_column(String(50), nullable=True)
total_packages: Mapped[int | None] = mapped_column(Integer, nullable=True)
identification: Mapped[str | None] = mapped_column(String(100), nullable=True)
pedimento: Mapped["Pedimentos"] = relationship(
"Pedimentos", back_populates="pedimento_transport_carriers"
)

View File

@@ -34,6 +34,9 @@ if TYPE_CHECKING:
from api.v1.modules.a76.pedmientos.models.pedimento_config_updates import (
PedimentoConfigUpdates,
)
from api.v1.modules.a76.pedmientos.models.pedimento_containers import (
PedimentoContainers,
)
from api.v1.modules.a76.pedmientos.models.pedimento_customs_offices import (
PedimentoCustomsOffices,
)
@@ -45,6 +48,15 @@ if TYPE_CHECKING:
PedimentoIncrementables,
)
from api.v1.modules.a76.pedmientos.models.pedimento_indexes import PedimentoIndexes
from api.v1.modules.a76.pedmientos.models.pedimento_packages import (
PedimentoPackages,
)
from api.v1.modules.a76.pedmientos.models.pedimento_guides import (
PedimentoGuides,
)
from api.v1.modules.a76.pedmientos.models.pedimento_contributions import (
PedimentoContributions,
)
from api.v1.modules.a76.pedmientos.models.pedimento_payments import (
PedimentoPayments,
)
@@ -54,6 +66,12 @@ if TYPE_CHECKING:
from api.v1.modules.a76.pedmientos.models.pedimento_rectification_origin import (
PedimentoRectificationOrigin,
)
from api.v1.modules.a76.pedmientos.models.pedimento_seals import (
PedimentoSeals,
)
from api.v1.modules.a76.pedmientos.models.pedimento_transport_carriers import (
PedimentoTransportCarriers,
)
from api.v1.modules.a76.pedmientos.models.pedimento_transport_means import (
PedimentoTransportMeans,
)
@@ -167,3 +185,21 @@ class Pedimentos(Base, TenantScopedMixin, TimestampMixin):
pedimento_validation: Mapped["PedimentoValidation"] = relationship(
"PedimentoValidation", uselist=False, back_populates="pedimento", cascade="all, delete-orphan"
)
pedimento_packages: Mapped["PedimentoPackages"] = relationship(
"PedimentoPackages", uselist=False, back_populates="pedimento", cascade="all, delete-orphan"
)
pedimento_transport_carriers: Mapped[list["PedimentoTransportCarriers"]] = relationship(
"PedimentoTransportCarriers", back_populates="pedimento", cascade="all, delete-orphan"
)
pedimento_guides: Mapped[list["PedimentoGuides"]] = relationship(
"PedimentoGuides", back_populates="pedimento", cascade="all, delete-orphan"
)
pedimento_contributions: Mapped[list["PedimentoContributions"]] = relationship(
"PedimentoContributions", back_populates="pedimento", cascade="all, delete-orphan"
)
pedimento_seals: Mapped[list["PedimentoSeals"]] = relationship(
"PedimentoSeals", back_populates="pedimento", cascade="all, delete-orphan"
)
pedimento_containers: Mapped[list["PedimentoContainers"]] = relationship(
"PedimentoContainers", back_populates="pedimento", cascade="all, delete-orphan"
)

View File

@@ -48,6 +48,12 @@ from ..models.pedimento_config_parameters import PedimentoConfigParameters
from ..models.pedimento_config_surcharges import PedimentoConfigSurcharges
from ..models.pedimento_config_update_rectification import PedimentoConfigUpdateRectification
from ..models.pedimento_config_updates import PedimentoConfigUpdates
from ..models.pedimento_packages import PedimentoPackages
from ..models.pedimento_transport_carriers import PedimentoTransportCarriers
from ..models.pedimento_seals import PedimentoSeals
from ..models.pedimento_containers import PedimentoContainers
from ..models.pedimento_guides import PedimentoGuides
from ..models.pedimento_contributions import PedimentoContributions
logger = logging.getLogger(__name__)
@@ -110,6 +116,12 @@ class PedimentosService:
selectinload(Pedimentos.pedimento_config_surcharges),
selectinload(Pedimentos.pedimento_config_update_rectification),
selectinload(Pedimentos.pedimento_config_updates),
selectinload(Pedimentos.pedimento_packages),
selectinload(Pedimentos.pedimento_transport_carriers),
selectinload(Pedimentos.pedimento_guides),
selectinload(Pedimentos.pedimento_contributions),
selectinload(Pedimentos.pedimento_seals),
selectinload(Pedimentos.pedimento_containers),
)
.order_by(desc(Pedimentos.created_at))
.offset(skip)
@@ -160,6 +172,12 @@ class PedimentosService:
selectinload(Pedimentos.pedimento_config_surcharges),
selectinload(Pedimentos.pedimento_config_update_rectification),
selectinload(Pedimentos.pedimento_config_updates),
selectinload(Pedimentos.pedimento_packages),
selectinload(Pedimentos.pedimento_transport_carriers),
selectinload(Pedimentos.pedimento_guides),
selectinload(Pedimentos.pedimento_contributions),
selectinload(Pedimentos.pedimento_seals),
selectinload(Pedimentos.pedimento_containers),
)
return query.first()
@@ -199,6 +217,12 @@ class PedimentosService:
'pedimento_config_surcharges': pedimento_data.pedimento_config_surcharges,
'pedimento_config_update_rectification': pedimento_data.pedimento_config_update_rectification,
'pedimento_config_updates': pedimento_data.pedimento_config_updates,
'pedimento_packages': pedimento_data.pedimento_packages,
'pedimento_transport_carriers': pedimento_data.pedimento_transport_carriers,
'pedimento_guides': pedimento_data.pedimento_guides,
'pedimento_contributions': getattr(pedimento_data, 'pedimento_contributions', None),
'pedimento_seals': pedimento_data.pedimento_seals,
'pedimento_containers': pedimento_data.pedimento_containers,
}
# Crear pedimento principal (excluyendo relaciones)
@@ -209,7 +233,8 @@ class PedimentosService:
'pedimento_rectification_origin', 'pedimento_transport_means',
'pedimento_config_additional', 'pedimento_config_calculations',
'pedimento_config_parameters', 'pedimento_config_surcharges',
'pedimento_config_update_rectification', 'pedimento_config_updates'
'pedimento_config_update_rectification', 'pedimento_config_updates',
'pedimento_packages', 'pedimento_transport_carriers', 'pedimento_guides', 'pedimento_seals', 'pedimento_containers'
})
pedimento = Pedimentos(**pedimento_dict)
@@ -219,7 +244,7 @@ class PedimentosService:
db.add(pedimento)
db.flush() # Flush para obtener el ID sin commit
# Helper function para crear objetos relacionados
# Helper function para crear objetos relacionados (uno a uno)
def create_related(model_class, data, extra_fields=None):
if data or extra_fields:
# Inicializar obj_dict desde data si existe, sino como dict vacío
@@ -274,6 +299,29 @@ class PedimentosService:
create_related(PedimentoConfigUpdates,
related_data['pedimento_config_updates'])
# Crear PedimentoPackages (uno a uno)
create_related(PedimentoPackages, related_data['pedimento_packages'])
# Crear relaciones uno a muchos
def create_many(model_class, items):
if not items:
return
for item in items:
obj_dict = item.model_dump(exclude_none=True)
obj = model_class(**obj_dict)
obj.pedimento_id = pedimento.id
obj.tenant_id = tenant_id
obj.company_id = company_id
db.add(obj)
create_many(PedimentoTransportCarriers,
related_data['pedimento_transport_carriers'])
create_many(PedimentoGuides, related_data['pedimento_guides'])
create_many(PedimentoContributions, related_data['pedimento_contributions'])
create_many(PedimentoSeals, related_data['pedimento_seals'])
create_many(PedimentoContainers,
related_data['pedimento_containers'])
db.commit()
db.refresh(pedimento)
return pedimento
@@ -323,7 +371,8 @@ class PedimentosService:
'pedimento_rectification_origin', 'pedimento_transport_means',
'pedimento_config_additional', 'pedimento_config_calculations',
'pedimento_config_parameters', 'pedimento_config_surcharges',
'pedimento_config_update_rectification', 'pedimento_config_updates'
'pedimento_config_update_rectification', 'pedimento_config_updates',
'pedimento_packages', 'pedimento_transport_carriers', 'pedimento_guides', 'pedimento_contributions', 'pedimento_seals', 'pedimento_containers'
})
for field, value in update_data.items():
@@ -343,8 +392,13 @@ class PedimentosService:
if not data:
return
existing = service_class.get_by_pedimento_id(
db, pedimento_id, tenant_id, company_id)
existing = None
if service_class:
existing = service_class.get_by_pedimento_id(
db, pedimento_id, tenant_id, company_id)
else:
existing = getattr(pedimento, data_attr, None)
if existing:
# Actualizar existente (excluir pedimento_id, tenant_id, company_id)
for field, value in data.items():
@@ -358,6 +412,28 @@ class PedimentosService:
obj.company_id = company_id
db.add(obj)
def upsert_one_to_many(model_class, data_attr):
full_data = pedimento_data.model_dump()
if data_attr not in full_data:
return
items = full_data[data_attr]
if items is None:
return
# Reemplazar completamente la colección por simplicidad
existing_items = getattr(pedimento, data_attr)
if existing_items:
for item in list(existing_items):
db.delete(item)
for item in items:
obj = model_class(**item)
obj.pedimento_id = pedimento_id
obj.tenant_id = tenant_id
obj.company_id = company_id
db.add(obj)
# Actualizar o crear tablas relacionadas
update_or_create_related(
PedimentoDatesService, PedimentoDates, 'pedimento_dates')
@@ -392,6 +468,15 @@ class PedimentosService:
update_or_create_related(
PedimentoConfigUpdatesService, PedimentoConfigUpdates, 'pedimento_config_updates')
# Manejar nuevas relaciones
update_or_create_related(None, PedimentoPackages, 'pedimento_packages')
upsert_one_to_many(PedimentoTransportCarriers,
'pedimento_transport_carriers')
upsert_one_to_many(PedimentoGuides, 'pedimento_guides')
upsert_one_to_many(PedimentoContributions, 'pedimento_contributions')
upsert_one_to_many(PedimentoSeals, 'pedimento_seals')
upsert_one_to_many(PedimentoContainers, 'pedimento_containers')
db.commit()
db.refresh(pedimento)
return pedimento

View File

@@ -54,8 +54,11 @@ export interface PedimentoIncrementables {
insured_value?: number | null;
packaging?: number | null;
freight?: number | null;
insurance?: number | null;
others?: number | null;
deductibles?: number | null;
currency?: string | null;
currency_factor?: number | null;
not_affect_usd_value?: boolean | null;
not_affect_customs_value?: boolean | null;
}
@@ -83,6 +86,7 @@ export interface PedimentoConfigAdditional {
enable_import_invoice_recipient?: boolean | null;
send_502_validation_file_for_consolidated?: boolean | null;
add_remove_norms?: boolean | null;
choose_invoice_cove?: boolean | null;
}
export interface PedimentoConfigCalculations {

View File

@@ -629,7 +629,7 @@
<Select.Item value="TasaEspecificaPreciosReferenciaUM">Tasa especifica sobre precios de referencia con UM</Select.Item>
</Select.Content>
</Select.Root>
<Button size="icon" variant="outline">
<Button size="icon" variant="outline" onclick={openNewContribGenInDialog}>
<Plus class="h-4 w-4" />
</Button>
</div>

View File

@@ -166,26 +166,27 @@
});
// Cuando cambia el Tipo de Operación
// Optimizado: solo ejecutar cuando cambien los campos específicos relevantes
$effect(() => {
if (!formData) return;
// Solo rastrear los campos que realmente importan
const currentType = formData.operation_type;
const currentCode = formData.pedimento_code;
const currentRegime = formData.regime;
// Detectar si el tipo de operación cambió
const typeChanged = currentType !== previousOperationType;
previousOperationType = currentType;
// Si no hay clave seleccionada, no hacer nada
if (!formData.pedimento_code) return;
// Si el tipo es null/undefined, no hacer nada
if (currentType === null || currentType === undefined) return;
// Si no hay clave seleccionada o tipo no definido, salir temprano
if (!currentCode || currentType === null || currentType === undefined) return;
const expectedTypeCode = operationTypeToTypeCode(currentType);
// Buscar matches para esta combinación de clave + tipo de operación
const matches = codePedimentoRegimens.filter(r =>
r.pedimento_code === formData.pedimento_code &&
r.pedimento_code === currentCode &&
r.type_code === expectedTypeCode
);
@@ -195,7 +196,7 @@
const validRegimens = new Set(matches.map(m => m.regimen_code).filter((code): code is string => code !== null));
// Si el tipo de operación cambió O el régimen actual no es válido, actualizar el régimen
if (typeChanged || !formData.regime || !validRegimens.has(formData.regime)) {
if (typeChanged || !currentRegime || !validRegimens.has(currentRegime)) {
const firstMatch = matches[0];
if (firstMatch?.regimen_code) {
formData.regime = firstMatch.regimen_code;
@@ -214,6 +215,11 @@
// Inicializar formData con los valores del pedimento (o vacío si es null)
if (!formData) {
const datesData = pedimento?.pedimento_dates;
const incrementablesData = pedimento?.pedimento_incrementables;
const decrementablesData = pedimento?.pedimento_decrementables;
const indexesData = pedimento?.pedimento_indexes;
const configAdditionalData = pedimento?.pedimento_config_additional;
formData = {
year: pedimento?.year || currentYear,
customs_office: pedimento?.customs_office || '',
@@ -236,39 +242,87 @@
rectification_payment_date: datesData?.rectification_payment_date ? datesData.rectification_payment_date.substring(0, 10) : '',
original_date: datesData?.original_date ? datesData.original_date.substring(0, 10) : '',
payment_date: datesData?.payment_date ? datesData.payment_date.substring(0, 10) : '',
// Incrementables fields
valor_seguro: incrementablesData?.insured_value ?? null,
embalajes: incrementablesData?.packaging ?? null,
fletes: incrementablesData?.freight ?? null,
seguros_incrementables: incrementablesData?.insurance ?? null,
otros_incrementables: incrementablesData?.others ?? null,
deducibles: incrementablesData?.deductibles ?? null,
moneda_incrementables: incrementablesData?.currency ?? '',
factor_moneda_incrementables: incrementablesData?.currency_factor ?? null,
no_afectar_valor_dolares_inc: incrementablesData?.not_affect_usd_value ?? false,
no_afectar_valor_aduana: incrementablesData?.not_affect_customs_value ?? false,
// Decrementables fields
fletes_decrementable: decrementablesData?.freight ?? null,
seguros: decrementablesData?.insurance ?? null,
carga: decrementablesData?.loading ?? null,
descarga: decrementablesData?.unloading ?? null,
otros: decrementablesData?.others ?? null,
moneda_decrementable: decrementablesData?.currency ?? '',
afectar_valor_dolares: decrementablesData?.not_affect_usd_value ?? false,
// Indexes fields
tipo_factor: indexesData?.update_factor_type === 1 ? 'INPC' : indexesData?.update_factor_type === 2 ? 'variacion_cambiaria' : null,
factor_actualizacion: indexesData?.update_factor ?? null,
factor_actualizacion_manual: indexesData?.manual_update_factor ?? false,
// Config Additional fields
anio_impresion: configAdditionalData?.manual_pedimento_year ?? '',
agregar_po_auto: configAdditionalData?.add_po_identifier ?? false,
no_eximir_normas: configAdditionalData?.do_not_exempt_norms_complement_x ?? false,
activar_destinatario: configAdditionalData?.enable_import_invoice_recipient ?? false,
agregar_registro_502: configAdditionalData?.send_502_validation_file_for_consolidated ?? false,
agregar_quitar_normas: configAdditionalData?.add_remove_norms ?? false,
facturar_cove: configAdditionalData?.choose_invoice_cove ?? false,
// Campos de captura automática
fecha_captura: currentDate,
hora_captura: currentTime
};
}
// Asegurar que el año siempre esté actualizado con el año actual
// Flag para evitar loops en actualización de año
let yearInitialized = false;
// Asegurar que el año siempre esté actualizado con el año actual (solo una vez)
$effect(() => {
if (formData && !pedimento?.year) {
if (formData && !pedimento?.year && !yearInitialized) {
formData.year = currentYear;
yearInitialized = true;
}
});
// Track para evitar loops en obtención de tipo de cambio
let lastFetchedDate: string | null = null;
let lastCompanyId: number | null = null;
// Obtener automáticamente el tipo de cambio cuando cambie la fecha de entrada
$effect(() => {
if (formData && formData.entry_date && companyStore.activeCompany) {
console.log('🔍 [TIPO CAMBIO] Buscando para fecha:', formData.entry_date, 'Company ID:', companyStore.activeCompany.id);
const entryDate = formData?.entry_date;
const companyId = companyStore.activeCompany?.id;
// Solo ejecutar si los valores clave cambiaron
if (formData && entryDate && companyId &&
(entryDate !== lastFetchedDate || companyId !== lastCompanyId)) {
getExchangeRateByDate(formData.entry_date, companyStore.activeCompany.id)
lastFetchedDate = entryDate;
lastCompanyId = companyId;
console.log('🔍 [TIPO CAMBIO] Buscando para fecha:', entryDate, 'Company ID:', companyId);
getExchangeRateByDate(entryDate, companyId)
.then(usdRate => {
console.log('✅ [TIPO CAMBIO] Respuesta recibida:', usdRate);
if (usdRate && formData) {
formData.exchange_rate = usdRate.value;
console.log('✅ [TIPO CAMBIO] Actualizado a:', usdRate.value);
} else {
console.warn('⚠️ [TIPO CAMBIO] No encontrado para fecha:', formData.entry_date);
console.warn('⚠️ [TIPO CAMBIO] No encontrado para fecha:', entryDate);
}
})
.catch(err => {
console.error('❌ [TIPO CAMBIO] Error:', err);
});
} else {
console.log('⏭️ [TIPO CAMBIO] Saltado - formData:', !!formData, 'entry_date:', formData?.entry_date, 'company:', !!companyStore.activeCompany);
console.log('⏭️ [TIPO CAMBIO] Saltado - Sin cambios en fecha o company');
}
});
@@ -866,9 +920,10 @@
<Label for="moneda_incrementables">Moneda</Label>
<Input
id="moneda_incrementables"
type="number"
type="text"
maxlength={3}
bind:value={formData.moneda_incrementables}
placeholder="0.00"
placeholder="USD"
/>
</div>

View File

@@ -203,8 +203,8 @@
alert('Reordenar facturas no implementado aún');
}
function datosSonana() {
alert('Datos Sonana no implementado aún');
function datosSoriana() {
alert('Datos Soriana no implementado aún');
}
</script>
@@ -273,9 +273,9 @@
<ArrowUpDown class="mr-1.5" size={14} />
Reordenar
</Button>
<Button size="sm" variant="outline" onclick={datosSonana}>
<Button size="sm" variant="outline" onclick={datosSoriana}>
<FileText class="mr-1.5" size={14} />
Datos Sonana
Datos Soriana
</Button>
</div>

View File

@@ -183,8 +183,8 @@
alert('Detalles de Consolidación no implementado aún');
}
function datosSonana() {
alert('Datos Sonana no implementado aún');
function datosSoriana() {
alert('Datos Soriana no implementado aún');
}
function refreshPartidas() {
@@ -241,9 +241,9 @@
<Info class="mr-1.5" size={14} />
Detalles de Consolidación
</Button>
<Button size="sm" variant="outline" onclick={datosSonana}>
<Button size="sm" variant="outline" onclick={datosSoriana}>
<FileText class="mr-1.5" size={14} />
Datos Sonana
Datos Soriana
</Button>
<Button size="sm" variant="outline" onclick={refreshPartidas}>
<RotateCw size={14} />

View File

@@ -93,14 +93,27 @@
};
} = $props();
// Cargar datos del pedimento cuando está disponible
// Flag para evitar loops infinitos al cargar datos
let dataLoaded = false;
let lastPedimentoId: number | null = null;
// Cargar datos del pedimento cuando está disponible (solo una vez)
$effect(() => {
if (pedimento) {
const pedimentoId = pedimento?.id ?? null;
// Si el pedimento cambió, resetear el flag
if (pedimentoId !== lastPedimentoId) {
lastPedimentoId = pedimentoId;
dataLoaded = false;
}
if (pedimento && !dataLoaded) {
dataLoaded = true;
console.log('📦 Cargando datos del pedimento en package-transportation-tab-form');
// Cargar datos de bultos desde pedimento_bultos
if (pedimento.pedimento_bultos) {
const bultos = pedimento.pedimento_bultos;
// Cargar datos de bultos desde pedimento_packages
if (pedimento.pedimento_packages) {
const bultos = pedimento.pedimento_packages;
formData.bultos = {
cantidad: bultos.quantity ?? 0,
marcas: bultos.brand || 'S/M',
@@ -109,9 +122,9 @@
};
}
// Cargar transportes desde pedimento_transport_means
if (pedimento.pedimento_transport_means && Array.isArray(pedimento.pedimento_transport_means)) {
formData.transportes = pedimento.pedimento_transport_means.map((t: any) => ({
// Cargar transportes desde pedimento_transport_carriers
if (pedimento.pedimento_transport_carriers && Array.isArray(pedimento.pedimento_transport_carriers)) {
formData.transportes = pedimento.pedimento_transport_carriers.map((t: any) => ({
id: t.id,
transportista: t.carrier || '',
rfc: t.rfc || '',
@@ -126,6 +139,15 @@
identificacion: t.identification || ''
}));
}
// Cargar guías desde pedimento_guides
if (pedimento.pedimento_guides && Array.isArray(pedimento.pedimento_guides)) {
formData.guias = pedimento.pedimento_guides.map((g: any) => ({
id: g.id,
guia: g.guide || '',
identificador: g.identifier || ''
}));
}
// Cargar precintos desde pedimento_seals
if (pedimento.pedimento_seals && Array.isArray(pedimento.pedimento_seals)) {
@@ -155,20 +177,6 @@
}
});
// Log cuando el componente se monta/desmonta
$effect(() => {
console.log('🚀 BultosTransportesTabForm montado - Datos actuales:', {
transportes: formData?.transportes?.length ?? 0,
guias: formData?.guias?.length ?? 0,
precintos: formData?.precintos?.length ?? 0,
contenedores: formData?.contenedores?.length ?? 0
});
return () => {
console.log('💥 BultosTransportesTabForm desmontado');
};
});
// Cargar países al montar el componente
onMount(async () => {
try {

View File

@@ -76,7 +76,9 @@ class CompanyStore {
this._loading = true;
try {
const response = await fetch('/api/v1/a76/company/my-companies');
const response = await fetch('/api/v1/a76/company/my-companies', {
credentials: 'include'
});
if (response.ok) {
const newCompanies = await response.json();

View File

@@ -146,9 +146,24 @@
agregar_entidad_federativa_proveedor: false
});
// Flag para evitar loops infinitos en el effect
let otrosDatosLoaded = false;
let lastPedimentoId: number | null = null;
// Cargar datos del pedimento en otrosDatosFormData cuando data.pedimento está disponible
// Solo ejecutar una vez cuando el pedimento cambia
$effect(() => {
if (data.pedimento && !data.isCreate) {
// Solo rastrear data.pedimento?.id para evitar re-ejecuciones innecesarias
const pedimentoId = data.pedimento?.id ?? null;
// Si el ID del pedimento cambió, resetear el flag
if (pedimentoId !== lastPedimentoId) {
lastPedimentoId = pedimentoId;
otrosDatosLoaded = false;
}
if (data.pedimento && !data.isCreate && !otrosDatosLoaded) {
otrosDatosLoaded = true;
const pedimento = data.pedimento;
// Mapear pedimento_config_calculations
@@ -304,6 +319,15 @@
exchange_rate: generalFormData?.exchange_rate || undefined
};
// Helper function para convertir fecha YYYY-MM-DD a ISO datetime
function dateToISO(dateString: string | null | undefined): string | null {
if (!dateString) return null;
// Si ya tiene formato ISO completo, retornarlo
if (dateString.includes('T')) return dateString;
// Si es solo fecha (YYYY-MM-DD), agregar tiempo medianoche
return `${dateString}T00:00:00`;
}
// Fechas - enviar si hay al menos un campo con valor
if (generalFormData) {
const hasDatesValue = generalFormData.entry_date || generalFormData.pedimento_date ||
@@ -311,12 +335,12 @@
generalFormData.original_date || generalFormData.payment_date;
if (hasDatesValue) {
payload.pedimento_dates = {
entry_date: generalFormData.entry_date || null,
pedimento_date: generalFormData.pedimento_date || null,
payment_date: generalFormData.payment_date || null,
rectification_payment_date: generalFormData.rectification_payment_date || null,
extraction_date: generalFormData.extraction_date || null,
original_date: generalFormData.original_date || null
entry_date: dateToISO(generalFormData.entry_date),
pedimento_date: dateToISO(generalFormData.pedimento_date),
payment_date: dateToISO(generalFormData.payment_date),
rectification_payment_date: dateToISO(generalFormData.rectification_payment_date),
extraction_date: dateToISO(generalFormData.extraction_date),
original_date: dateToISO(generalFormData.original_date)
};
}
}
@@ -397,16 +421,20 @@
do_not_exempt_norms_complement_x: generalFormData.no_eximir_normas || false,
enable_import_invoice_recipient: generalFormData.activar_destinatario || false,
send_502_validation_file_for_consolidated: generalFormData.agregar_registro_502 || false,
add_remove_norms: generalFormData.agregar_quitar_normas || false
};
}
add_remove_norms: generalFormData.agregar_quitar_normas || false,
choose_invoice_cove: generalFormData.facturar_cove || false
};
}
}
// Identificadores - enviar array de identificadores si existen
// Identificadores - TEMPORALMENTE DESHABILITADO (no hay soporte en backend)
// TODO: Implementar tabla de identificadores en el backend
/*
if (identificadoresFormData?.identificadores && identificadoresFormData.identificadores.length > 0) {
payload.identificadores = identificadoresFormData.identificadores;
}
*/
// Config Calculations - configuraciones de cálculo DTA, IVA, prevalidación
if (otrosDatosFormData) {
@@ -502,6 +530,7 @@
if (otrosDatosFormData) {
const hasUpdatesValue = otrosDatosFormData.actualizar_iva !== undefined ||
otrosDatosFormData.actualizar_advalorem !== undefined ||
otrosDatosFormData.actualizar_dta !== undefined ||
otrosDatosFormData.actualizar_cc !== undefined ||
otrosDatosFormData.actualizar_ieps !== undefined;
@@ -538,6 +567,77 @@
}
}
// Bultos/Transportes - enviar datos de paquetes, transportistas, guías, precintos y contenedores
if (bultosTransportesFormData) {
// Paquetes (pedimento_packages) - relación one-to-one
const bultos = bultosTransportesFormData.bultos;
if (bultos && (bultos.cantidad || bultos.marcas || bultos.numero || bultos.vehiculos)) {
payload.pedimento_packages = {
quantity: bultos.cantidad || null,
brand: bultos.marcas || null,
number: bultos.numero || null,
vehicles: bultos.vehiculos || null
};
}
// Transportistas (pedimento_transport_carriers) - relación one-to-many
if (bultosTransportesFormData.transportes && bultosTransportesFormData.transportes.length > 0) {
payload.pedimento_transport_carriers = bultosTransportesFormData.transportes.map((t: any) => ({
carrier: t.transportista || null,
rfc: t.rfc || null,
curp: t.curp || null,
name: t.nombre || null,
address: t.domicilio || null,
city: t.ciudad || null,
state: t.estado || null,
country: t.pais || null,
tax_id: t.identificacion_fiscal || null,
total_packages: t.total_bultos || null,
identification: t.identificacion || null
}));
}
// Guías (pedimento_guides) - relación one-to-many
if (bultosTransportesFormData.guias && bultosTransportesFormData.guias.length > 0) {
payload.pedimento_guides = bultosTransportesFormData.guias.map((g: any) => ({
guide: g.guia || null,
identifier: g.identificador || null
}));
}
// Precintos (pedimento_seals) - relación one-to-many
if (bultosTransportesFormData.precintos && bultosTransportesFormData.precintos.length > 0) {
payload.pedimento_seals = bultosTransportesFormData.precintos.map((p: any) => ({
number: p.numero || null,
identification: p.identificacion || null
}));
}
// Contenedores (pedimento_containers) - relación one-to-many
if (bultosTransportesFormData.contenedores && bultosTransportesFormData.contenedores.length > 0) {
payload.pedimento_containers = bultosTransportesFormData.contenedores.map((c: any) => ({
number: c.numero || null,
identification: c.identificacion || null,
type: c.tipo || null
}));
}
}
// Contribuciones - enviar datos de contribuciones generales
if (contribucionesFormData && contribucionesFormData.contribuciones && contribucionesFormData.contribuciones.length > 0) {
payload.pedimento_contributions = contribucionesFormData.contribuciones.map((c: any) => ({
contribucion: c.contribucion || null,
tipo_tasa: c.tipo_tasa || null,
tasa: c.tasa || null,
forma_pago: c.forma_pago || null,
importe: c.importe || null,
gravamen: c.gravamen || null,
abreviacion: c.abreviacion || null,
forma_pago_2: c.forma_pago_2 || null,
importe_2: c.importe_2 || null
}));
}
// Digitalización - solo enviar si hay digitalizaciones
if (digitalizacionFormData && digitalizacionFormData.digitalizaciones && digitalizacionFormData.digitalizaciones.length > 0) {
(payload as any).digitalizaciones = digitalizacionFormData.digitalizaciones.map((d: any) => ({
@@ -692,9 +792,10 @@
</Alert.Root>
{/if}
<!-- Contenido de las tabs con padding inferior para el footer flotante -->
<div class="pb-56">
<Tabs.Root bind:value={activeTab} class="space-y-4">
<!-- Tabs con footer fijo -->
<Tabs.Root value={activeTab} onValueChange={(v) => { if (v) activeTab = v; }} class="space-y-4">
<!-- Contenido de las tabs con padding inferior para el footer flotante -->
<div class="pb-56">
<Tabs.Content value="general">
<GeneralTabForm
pedimento={data.pedimento}
@@ -765,20 +866,17 @@
<Tabs.Content value="digitalizacion">
<DigitalizacionTabForm bind:formData={digitalizacionFormData} />
</Tabs.Content>
</Tabs.Root>
</div>
</div>
</div>
<!-- Footer fijo en la parte inferior - Fuera del contenedor principal -->
<Tabs.Root bind:value={activeTab}>
<div
class="fixed bottom-0 left-0 right-0 bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/80 border-t shadow-lg z-[5] transition-[left] duration-200 ease-linear"
style:left={sidebar.isMobile ? '0' : (sidebar.open ? 'var(--sidebar-width)' : '0')}
>
<div class="px-4 py-4 space-y-4 max-w-[1400px] mx-auto">
<!-- Tabs Navigation -->
<div class="w-full overflow-x-auto pb-2">
<Tabs.List class="inline-flex w-full gap-1">
<!-- Footer fijo en la parte inferior -->
<div
class="fixed bottom-0 left-0 right-0 bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/80 border-t shadow-lg z-[5] transition-[left] duration-200 ease-linear"
style:left={sidebar.isMobile ? '0' : (sidebar.open ? 'var(--sidebar-width)' : '0')}
>
<div class="px-4 py-4 space-y-4 max-w-[1400px] mx-auto">
<!-- Tabs Navigation -->
<div class="w-full overflow-x-auto pb-2">
<Tabs.List class="inline-flex w-full gap-1">
<Tabs.Trigger value="general" class="flex items-center gap-1.5 px-2.5 py-1.5 text-sm">
<FileText size={14} />
<span>General</span>
@@ -839,4 +937,5 @@
</div>
</div>
</div>
</Tabs.Root>
</Tabs.Root>
</div>