Merge pull request 'pedimentos' (#36) from pedimentos into development

Reviewed-on: ADUANASOFT/anexo76#36
This commit is contained in:
2025-12-31 14:24:57 +00:00
58 changed files with 941 additions and 362 deletions

View File

@@ -1,28 +0,0 @@
"""increase_port_description_length
Revision ID: 3a012dff0274
Revises: 7937209f9718
Create Date: 2025-12-24 10:24:49.927020
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = '3a012dff0274'
down_revision: Union[str, Sequence[str], None] = '7937209f9718'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade schema."""
pass
def downgrade() -> None:
"""Downgrade schema."""
pass

View File

@@ -14,10 +14,10 @@ class CurrencyMixin:
class AffectValueMixin:
"""Mixin for value affect flags"""
not_affect_usd_value: Optional[int] = Field(
not_affect_usd_value: Optional[bool] = Field(
None, description="Not affect USD value"
)
not_affect_customs_value: Optional[int] = Field(
not_affect_customs_value: Optional[bool] = Field(
None, description="Not affect customs value"
)
@@ -25,7 +25,8 @@ class AffectValueMixin:
class UpdateFlagsMixin:
"""Mixin for update flags"""
update_vat: Optional[int] = Field(None, description="Update VAT")
update_advalorem: Optional[int] = Field(None, description="Update advalorem")
update_cc: Optional[int] = Field(None, description="Update CC")
update_ieps: Optional[int] = Field(None, description="Update IEPS")
update_vat: Optional[bool] = Field(None, description="Update VAT")
update_advalorem: Optional[bool] = Field(None, description="Update advalorem")
update_dta: Optional[bool] = Field(None, description="Update DTA")
update_cc: Optional[bool] = Field(None, description="Update CC")
update_ieps: Optional[bool] = Field(None, description="Update IEPS")

View File

@@ -301,8 +301,15 @@ class TenantCRUDRoutes(
db, company_id, current_user)
# For child resources, parent_id validation would go here
resource = self.service.create(db, data, tenant_id, company_id)
return resource
try:
resource = self.service.create(db, data, tenant_id, company_id)
return resource
except ValueError as e:
# Capturar errores de validación (como duplicados)
raise HTTPException(status_code=400, detail=str(e))
except Exception as e:
# Re-lanzar otros errores
raise
else:
# Parent resource - no parent_id needed
@@ -324,8 +331,15 @@ class TenantCRUDRoutes(
):
tenant_id = validate_access_to_resource(
db, company_id, current_user)
resource = self.service.create(db, data, tenant_id, company_id)
return resource
try:
resource = self.service.create(db, data, tenant_id, company_id)
return resource
except ValueError as e:
# Capturar errores de validación (como duplicados)
raise HTTPException(status_code=400, detail=str(e))
except Exception as e:
# Re-lanzar otros errores
raise
# PUT route
# For parent resources: PUT /{id}
@@ -353,9 +367,13 @@ class TenantCRUDRoutes(
db, company_id, current_user)
parent_id = path_params.get(self.parent_id_name)
resource = self.service.update(
db, parent_id, tenant_id, data, company_id
)
try:
resource = self.service.update(
db, parent_id, tenant_id, data, company_id
)
except ValueError as e:
# Capturar errores de validación (como duplicados)
raise HTTPException(status_code=400, detail=str(e))
if not resource:
raise HTTPException(
@@ -388,9 +406,13 @@ class TenantCRUDRoutes(
tenant_id = validate_access_to_resource(
db, company_id, current_user)
resource = self.service.update(
db, resource_id, tenant_id, data, company_id
)
try:
resource = self.service.update(
db, resource_id, tenant_id, data, company_id
)
except ValueError as e:
# Capturar errores de validación (como duplicados)
raise HTTPException(status_code=400, detail=str(e))
if not resource:
raise HTTPException(

View File

@@ -7,22 +7,22 @@ from pydantic import BaseModel, ConfigDict, Field
class PedimentoConfigAdditionalBase(BaseModel):
"""Base schema for Pedimento Config Additional"""
pedimento_id: int = Field(..., description="Pedimento ID")
tenant_id: int = Field(..., description="Tenant ID")
add_po_identifier: Optional[int] = Field(None, description="Add PO identifier")
do_not_exempt_norms_complement_x: Optional[int] = Field(
pedimento_id: Optional[int] = Field(None, description="Pedimento ID")
tenant_id: Optional[int] = Field(None, description="Tenant ID")
add_po_identifier: Optional[bool] = Field(None, description="Add PO identifier")
do_not_exempt_norms_complement_x: Optional[bool] = Field(
None, description="Do not exempt norms complement X"
)
manual_pedimento_year: Optional[str] = Field(
None, max_length=2, description="Manual pedimento year"
)
enable_import_invoice_recipient: Optional[int] = Field(
enable_import_invoice_recipient: Optional[bool] = Field(
None, description="Enable import invoice recipient"
)
send_502_validation_file_for_consolidated: Optional[int] = Field(
send_502_validation_file_for_consolidated: Optional[bool] = Field(
None, description="Send 502 validation file for consolidated"
)
add_remove_norms: Optional[int] = Field(None, description="Add/remove norms")
add_remove_norms: Optional[bool] = Field(None, description="Add/remove norms")
class PedimentoConfigAdditionalCreate(PedimentoConfigAdditionalBase):
@@ -34,12 +34,12 @@ class PedimentoConfigAdditionalCreate(PedimentoConfigAdditionalBase):
class PedimentoConfigAdditionalUpdate(BaseModel):
"""Schema for updating a Pedimento Config Additional"""
add_po_identifier: Optional[int] = None
do_not_exempt_norms_complement_x: Optional[int] = None
add_po_identifier: Optional[bool] = None
do_not_exempt_norms_complement_x: Optional[bool] = None
manual_pedimento_year: Optional[str] = Field(None, max_length=2)
enable_import_invoice_recipient: Optional[int] = None
send_502_validation_file_for_consolidated: Optional[int] = None
add_remove_norms: Optional[int] = None
enable_import_invoice_recipient: Optional[bool] = None
send_502_validation_file_for_consolidated: Optional[bool] = None
add_remove_norms: Optional[bool] = None
class PedimentoConfigAdditionalResponse(PedimentoConfigAdditionalBase):

View File

@@ -7,24 +7,24 @@ from pydantic import BaseModel, ConfigDict, Field
class PedimentoConfigCalculationsBase(BaseModel):
"""Base schema for Pedimento Config Calculations"""
pedimento_id: int = Field(..., description="Pedimento ID")
tenant_id: int = Field(..., description="Tenant ID")
pedimento_id: Optional[int] = Field(None, description="Pedimento ID")
tenant_id: Optional[int] = Field(None, description="Tenant ID")
dta_type: Optional[str] = Field(None, max_length=1, description="DTA type")
dta_operation: Optional[int] = Field(None, description="DTA operation")
dta_vehicle_count: Optional[int] = Field(None, description="DTA vehicle count")
dta_mixed_rate_8permil: Optional[int] = Field(
None, description="DTA mixed rate 8 per mil"
dta_operation: Optional[bool] = Field(False, description="DTA operation")
dta_vehicle_count: Optional[int] = Field(0, description="DTA vehicle count")
dta_mixed_rate_8permil: Optional[bool] = Field(
False, description="DTA mixed rate 8 per mil"
)
pays_vat: Optional[int] = Field(None, description="Pays VAT")
pays_prevalidation: Optional[int] = Field(None, description="Pays prevalidation")
include_sagar_certificate_fee: Optional[int] = Field(
None, description="Include SAGAR certificate fee"
pays_vat: Optional[bool] = Field(False, description="Pays VAT")
pays_prevalidation: Optional[bool] = Field(False, description="Pays prevalidation")
include_sagar_certificate_fee: Optional[bool] = Field(
False, description="Include SAGAR certificate fee"
)
fixed_vehicle_dta_fee: Optional[int] = Field(
None, description="Fixed vehicle DTA fee"
fixed_vehicle_dta_fee: Optional[bool] = Field(
False, description="Fixed vehicle DTA fee"
)
additional_fixed_fee: Optional[int] = Field(
None, description="Additional fixed fee"
0, description="Additional fixed fee"
)
additional_fixed_fee_payment_method: Optional[int] = Field(
None, description="Additional fixed fee payment method"
@@ -41,13 +41,13 @@ class PedimentoConfigCalculationsUpdate(BaseModel):
"""Schema for updating a Pedimento Config Calculations"""
dta_type: Optional[str] = Field(None, max_length=1)
dta_operation: Optional[int] = None
dta_operation: Optional[bool] = None
dta_vehicle_count: Optional[int] = None
dta_mixed_rate_8permil: Optional[int] = None
pays_vat: Optional[int] = None
pays_prevalidation: Optional[int] = None
include_sagar_certificate_fee: Optional[int] = None
fixed_vehicle_dta_fee: Optional[int] = None
dta_mixed_rate_8permil: Optional[bool] = None
pays_vat: Optional[bool] = None
pays_prevalidation: Optional[bool] = None
include_sagar_certificate_fee: Optional[bool] = None
fixed_vehicle_dta_fee: Optional[bool] = None
additional_fixed_fee: Optional[int] = None
additional_fixed_fee_payment_method: Optional[int] = None

View File

@@ -8,31 +8,31 @@ from pydantic import BaseModel, ConfigDict, Field
class PedimentoConfigParametersBase(BaseModel):
"""Base schema for Pedimento Config Parameters"""
pedimento_id: int = Field(..., description="Pedimento ID")
tenant_id: int = Field(..., description="Tenant ID")
is_embassy: Optional[int] = Field(None, description="Is embassy")
embassy_dta: Optional[Decimal] = Field(None, description="Embassy DTA")
rule_3121_section_ii: Optional[int] = Field(
None, description="Rule 3.1.21 Section II"
pedimento_id: Optional[int] = Field(None, description="Pedimento ID")
tenant_id: Optional[int] = Field(None, description="Tenant ID")
is_embassy: Optional[bool] = Field(False, description="Is embassy")
embassy_dta: Optional[Decimal] = Field(Decimal('0.00'), description="Embassy DTA")
rule_3121_section_ii: Optional[bool] = Field(
False, description="Rule 3.1.21 Section II"
)
use_previous_tariff: Optional[int] = Field(None, description="Use previous tariff")
use_payment_date_fi: Optional[int] = Field(None, description="Use payment date FI")
add_state_supplier_record_505: Optional[int] = Field(
None, description="Add state supplier record 505"
use_previous_tariff: Optional[bool] = Field(False, description="Use previous tariff")
use_payment_date_fi: Optional[bool] = Field(False, description="Use payment date FI")
add_state_supplier_record_505: Optional[bool] = Field(
False, description="Add state supplier record 505"
)
customs_value_calculation: Optional[int] = Field(
None, description="Customs value calculation"
customs_value_calculation: Optional[bool] = Field(
False, description="Customs value calculation"
)
two_decimals_unit_value: Optional[int] = Field(
None, description="Two decimals unit value"
two_decimals_unit_value: Optional[bool] = Field(
False, description="Two decimals unit value"
)
customs_value_per_item: Optional[int] = Field(
None, description="Customs value per item"
customs_value_per_item: Optional[bool] = Field(
False, description="Customs value per item"
)
is_national_supplier: Optional[int] = Field(
None, description="Is national supplier"
is_national_supplier: Optional[bool] = Field(
False, description="Is national supplier"
)
is_consolidated: Optional[int] = Field(None, description="Is consolidated")
is_consolidated: Optional[bool] = Field(False, description="Is consolidated")
class PedimentoConfigParametersCreate(PedimentoConfigParametersBase):
@@ -44,17 +44,17 @@ class PedimentoConfigParametersCreate(PedimentoConfigParametersBase):
class PedimentoConfigParametersUpdate(BaseModel):
"""Schema for updating a Pedimento Config Parameters"""
is_embassy: Optional[int] = None
is_embassy: Optional[bool] = None
embassy_dta: Optional[Decimal] = None
rule_3121_section_ii: Optional[int] = None
use_previous_tariff: Optional[int] = None
use_payment_date_fi: Optional[int] = None
add_state_supplier_record_505: Optional[int] = None
customs_value_calculation: Optional[int] = None
two_decimals_unit_value: Optional[int] = None
customs_value_per_item: Optional[int] = None
is_national_supplier: Optional[int] = None
is_consolidated: Optional[int] = None
rule_3121_section_ii: Optional[bool] = None
use_previous_tariff: Optional[bool] = None
use_payment_date_fi: Optional[bool] = None
add_state_supplier_record_505: Optional[bool] = None
customs_value_calculation: Optional[bool] = None
two_decimals_unit_value: Optional[bool] = None
customs_value_per_item: Optional[bool] = None
is_national_supplier: Optional[bool] = None
is_consolidated: Optional[bool] = None
class PedimentoConfigParametersResponse(PedimentoConfigParametersBase):

View File

@@ -7,14 +7,14 @@ from pydantic import BaseModel, ConfigDict, Field
class PedimentoConfigSurchargesBase(BaseModel):
"""Base schema for Pedimento Config Surcharges"""
pedimento_id: int = Field(..., description="Pedimento ID")
tenant_id: int = Field(..., description="Tenant ID")
surcharge_igi: Optional[int] = Field(None, description="Surcharge IGI")
surcharge_dta: Optional[int] = Field(None, description="Surcharge DTA")
surcharge_vat: Optional[int] = Field(None, description="Surcharge VAT")
surcharge_isan: Optional[int] = Field(None, description="Surcharge ISAN")
surcharge_ieps: Optional[int] = Field(None, description="Surcharge IEPS")
surcharge_cc: Optional[int] = Field(None, description="Surcharge CC")
pedimento_id: Optional[int] = Field(None, description="Pedimento ID")
tenant_id: Optional[int] = Field(None, description="Tenant ID")
surcharge_igi: Optional[bool] = Field(None, description="Surcharge IGI")
surcharge_dta: Optional[bool] = Field(None, description="Surcharge DTA")
surcharge_vat: Optional[bool] = Field(None, description="Surcharge VAT")
surcharge_isan: Optional[bool] = Field(None, description="Surcharge ISAN")
surcharge_ieps: Optional[bool] = Field(None, description="Surcharge IEPS")
surcharge_cc: Optional[bool] = Field(None, description="Surcharge CC")
class PedimentoConfigSurchargesCreate(PedimentoConfigSurchargesBase):
@@ -26,12 +26,12 @@ class PedimentoConfigSurchargesCreate(PedimentoConfigSurchargesBase):
class PedimentoConfigSurchargesUpdate(BaseModel):
"""Schema for updating a Pedimento Config Surcharges"""
surcharge_igi: Optional[int] = None
surcharge_dta: Optional[int] = None
surcharge_vat: Optional[int] = None
surcharge_isan: Optional[int] = None
surcharge_ieps: Optional[int] = None
surcharge_cc: Optional[int] = None
surcharge_igi: Optional[bool] = None
surcharge_dta: Optional[bool] = None
surcharge_vat: Optional[bool] = None
surcharge_isan: Optional[bool] = None
surcharge_ieps: Optional[bool] = None
surcharge_cc: Optional[bool] = None
class PedimentoConfigSurchargesResponse(PedimentoConfigSurchargesBase):

View File

@@ -9,9 +9,9 @@ from api.v1.common.dto_mixins import UpdateFlagsMixin
class PedimentoConfigUpdateRectificationBase(BaseModel, UpdateFlagsMixin):
"""Base schema for Pedimento Config Update Rectification"""
pedimento_id: int = Field(..., description="Pedimento ID")
tenant_id: int = Field(..., description="Tenant ID")
calculate_surcharge: Optional[int] = Field(None, description="Calculate surcharge")
pedimento_id: Optional[int] = Field(None, description="Pedimento ID")
tenant_id: Optional[int] = Field(None, description="Tenant ID")
calculate_surcharge: Optional[bool] = Field(None, description="Calculate surcharge")
class PedimentoConfigUpdateRectificationCreate(PedimentoConfigUpdateRectificationBase):
@@ -23,7 +23,7 @@ class PedimentoConfigUpdateRectificationCreate(PedimentoConfigUpdateRectificatio
class PedimentoConfigUpdateRectificationUpdate(BaseModel, UpdateFlagsMixin):
"""Schema for updating a Pedimento Config Update Rectification"""
calculate_surcharge: Optional[int] = None
calculate_surcharge: Optional[bool] = None
class PedimentoConfigUpdateRectificationResponse(

View File

@@ -9,8 +9,8 @@ from api.v1.common.dto_mixins import UpdateFlagsMixin
class PedimentoConfigUpdatesBase(BaseModel, UpdateFlagsMixin):
"""Base schema for Pedimento Config Updates"""
pedimento_id: int = Field(..., description="Pedimento ID")
tenant_id: int = Field(..., description="Tenant ID")
pedimento_id: Optional[int] = Field(None, description="Pedimento ID")
tenant_id: Optional[int] = Field(None, description="Tenant ID")
class PedimentoConfigUpdatesCreate(PedimentoConfigUpdatesBase):
"""Schema for creating a new Pedimento Config Updates"""

View File

@@ -7,8 +7,8 @@ from pydantic import BaseModel, ConfigDict, Field
class PedimentoCustomsOfficesBase(BaseModel):
"""Base schema for Pedimento Customs Offices"""
pedimento_id: int = Field(..., description="Pedimento ID")
tenant_id: int = Field(..., description="Tenant ID")
pedimento_id: Optional[int] = Field(None, description="Pedimento ID")
tenant_id: Optional[int] = Field(None, description="Tenant ID")
dispatch_customs: Optional[str] = Field(
None, max_length=3, description="Dispatch customs"
)

View File

@@ -7,7 +7,8 @@ from pydantic import BaseModel, ConfigDict, Field
class PedimentoDatesBase(BaseModel):
"""Base schema for Pedimento Dates"""
entry_date: Optional[datetime] = Field(None, description="Entry date")
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")
rectification_payment_date: Optional[datetime] = Field(
None, description="Rectification payment date"
@@ -23,7 +24,8 @@ class PedimentoDatesBase(BaseModel):
class PedimentoDatesCreate(BaseModel):
"""Schema for creating a new Pedimento Dates - pedimento_id and tenant_id are set by backend"""
entry_date: Optional[datetime] = Field(None, description="Entry date")
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")
rectification_payment_date: Optional[datetime] = Field(None, description="Rectification payment date")
extraction_date: Optional[datetime] = Field(None, description="Extraction date")
@@ -37,7 +39,8 @@ class PedimentoDatesCreate(BaseModel):
class PedimentoDatesUpdate(BaseModel):
"""Schema for updating a Pedimento Dates"""
entry_date: Optional[datetime] = None
entry_date: Optional[datetime] = None
pedimento_date: Optional[datetime] = None
payment_date: Optional[datetime] = None
rectification_payment_date: Optional[datetime] = None
extraction_date: Optional[datetime] = None

View File

@@ -8,8 +8,8 @@ from pydantic import BaseModel, ConfigDict, Field
class PedimentoDecrementablesBase(BaseModel):
"""Base schema for Pedimento Decrementables"""
pedimento_id: int = Field(..., description="Pedimento ID")
tenant_id: int = Field(..., description="Tenant ID")
pedimento_id: Optional[int] = Field(None, description="Pedimento ID")
tenant_id: Optional[int] = Field(None, description="Tenant ID")
freight: Optional[Decimal] = Field(None, description="Freight")
insurance: Optional[Decimal] = Field(None, description="Insurance")
loading: Optional[Decimal] = Field(None, description="Loading")
@@ -17,10 +17,10 @@ class PedimentoDecrementablesBase(BaseModel):
others: Optional[Decimal] = Field(None, description="Others")
currency: Optional[str] = Field(None, max_length=3, description="Currency")
currency_factor: Optional[Decimal] = Field(None, description="Currency factor")
not_affect_usd_value: Optional[int] = Field(
not_affect_usd_value: Optional[bool] = Field(
None, description="Not affect USD value"
)
not_affect_customs_value: Optional[int] = Field(
not_affect_customs_value: Optional[bool] = Field(
None, description="Not affect customs value"
)
@@ -41,8 +41,8 @@ class PedimentoDecrementablesUpdate(BaseModel):
others: Optional[Decimal] = None
currency: Optional[str] = Field(None, max_length=3)
currency_factor: Optional[Decimal] = None
not_affect_usd_value: Optional[int] = None
not_affect_customs_value: Optional[int] = None
not_affect_usd_value: Optional[bool] = None
not_affect_customs_value: Optional[bool] = None
class PedimentoDecrementablesResponse(PedimentoDecrementablesBase):

View File

@@ -8,8 +8,8 @@ from pydantic import BaseModel, ConfigDict, Field
class PedimentoIncrementablesBase(BaseModel):
"""Base schema for Pedimento Incrementables"""
pedimento_id: int = Field(..., description="Pedimento ID")
tenant_id: int = Field(..., description="Tenant ID")
pedimento_id: Optional[int] = Field(None, description="Pedimento ID")
tenant_id: Optional[int] = Field(None, description="Tenant ID")
insured_value: Optional[Decimal] = Field(None, description="Insured value")
freight: Optional[Decimal] = Field(None, description="Freight")
insurance: Optional[Decimal] = Field(None, description="Insurance")
@@ -18,10 +18,10 @@ class PedimentoIncrementablesBase(BaseModel):
deductibles: Optional[Decimal] = Field(None, description="Deductibles")
currency: Optional[str] = Field(None, max_length=3, description="Currency")
currency_factor: Optional[Decimal] = Field(None, description="Currency factor")
not_affect_usd_value: Optional[int] = Field(
not_affect_usd_value: Optional[bool] = Field(
None, description="Not affect USD value"
)
not_affect_customs_value: Optional[int] = Field(
not_affect_customs_value: Optional[bool] = Field(
None, description="Not affect customs value"
)
@@ -43,8 +43,8 @@ class PedimentoIncrementablesUpdate(BaseModel):
deductibles: Optional[Decimal] = None
currency: Optional[str] = Field(None, max_length=3)
currency_factor: Optional[Decimal] = None
not_affect_usd_value: Optional[int] = None
not_affect_customs_value: Optional[int] = None
not_affect_usd_value: Optional[bool] = None
not_affect_customs_value: Optional[bool] = None
class PedimentoIncrementablesResponse(PedimentoIncrementablesBase):

View File

@@ -8,11 +8,11 @@ from pydantic import BaseModel, ConfigDict, Field
class PedimentoIndexesBase(BaseModel):
"""Base schema for Pedimento Indexes"""
pedimento_id: int = Field(..., description="Pedimento ID")
tenant_id: int = Field(..., description="Tenant ID")
pedimento_id: Optional[int] = Field(None, description="Pedimento ID")
tenant_id: Optional[int] = Field(None, description="Tenant ID")
update_factor_type: Optional[int] = Field(None, description="Update factor type")
update_factor: Optional[Decimal] = Field(None, description="Update factor")
manual_update_factor: Optional[int] = Field(
manual_update_factor: Optional[bool] = Field(
None, description="Manual update factor"
)
@@ -28,7 +28,7 @@ class PedimentoIndexesUpdate(BaseModel):
update_factor_type: Optional[int] = None
update_factor: Optional[Decimal] = None
manual_update_factor: Optional[int] = None
manual_update_factor: Optional[bool] = None
class PedimentoIndexesResponse(PedimentoIndexesBase):

View File

@@ -9,8 +9,8 @@ from pydantic import BaseModel, ConfigDict, Field
class PedimentoPaymentsBase(BaseModel):
"""Base schema for Pedimento Payments"""
pedimento_id: int = Field(..., description="Pedimento ID")
tenant_id: int = Field(..., description="Tenant ID")
pedimento_id: Optional[int] = Field(None, description="Pedimento ID")
tenant_id: Optional[int] = Field(None, description="Tenant ID")
acknowledgment: Optional[str] = Field(
None, max_length=20, description="Acknowledgment"
)

View File

@@ -6,8 +6,8 @@ from pydantic import BaseModel, ConfigDict, Field
class PedimentoRectificationDestinationBase(BaseModel):
"""Base schema for Pedimento Rectification Destination"""
pedimento_id: int = Field(..., description="Pedimento ID")
tenant_id: int = Field(..., description="Tenant ID")
pedimento_id: Optional[int] = Field(None, description="Pedimento ID")
tenant_id: Optional[int] = Field(None, description="Tenant ID")
destination_pedimento_year: Optional[str] = Field(
None, max_length=2, description="Destination pedimento year"
)

View File

@@ -7,8 +7,8 @@ from pydantic import BaseModel, ConfigDict, Field
class PedimentoRectificationOriginBase(BaseModel):
"""Base schema for Pedimento Rectification Origin"""
pedimento_id: int = Field(..., description="Pedimento ID")
tenant_id: int = Field(..., description="Tenant ID")
pedimento_id: Optional[int] = Field(None, description="Pedimento ID")
tenant_id: Optional[int] = Field(None, description="Tenant ID")
original_pedimento_year: Optional[str] = Field(
None, max_length=2, description="Original pedimento year"
)

View File

@@ -7,8 +7,8 @@ from pydantic import BaseModel, ConfigDict, Field
class PedimentoTransportMeansBase(BaseModel):
"""Base schema for Pedimento Transport Means"""
pedimento_id: int = Field(..., description="Pedimento ID")
tenant_id: int = Field(..., description="Tenant ID")
pedimento_id: Optional[int] = Field(None, description="Pedimento ID")
tenant_id: Optional[int] = Field(None, description="Tenant ID")
destination: Optional[int] = Field(None, description="Destination")
entry_exit: Optional[str] = Field(None, max_length=2, description="Entry/exit")
arrival: str = Field(..., max_length=2, description="Arrival")

View File

@@ -7,8 +7,8 @@ from pydantic import BaseModel, ConfigDict, Field
class PedimentoValidationBase(BaseModel):
"""Base schema for Pedimento Validation"""
pedimento_id: int = Field(..., description="Pedimento ID")
tenant_id: int = Field(..., description="Tenant ID")
pedimento_id: Optional[int] = Field(None, description="Pedimento ID")
tenant_id: Optional[int] = Field(None, description="Tenant ID")
validator: Optional[str] = Field(None, max_length=3, description="Validator")
validation_ack: Optional[str] = Field(
None, max_length=8, description="Validation acknowledgment"

View File

@@ -50,7 +50,8 @@ class PedimentosBase(BaseModel):
usd_value: Optional[Decimal] = Field(None, description="USD value")
paid_price: Optional[Decimal] = Field(None, description="Paid price")
gross_weight: Optional[Decimal] = Field(None, description="Gross weight")
exchange_rate: Optional[Decimal] = Field(None, description="Exchange rate")
exchange_rate: Optional[Decimal] = Field(None, description="Exchange rate")
observations: Optional[str] = Field(None, description="Observations")
class PedimentosCreate(PedimentosBase):
"""Schema for creating a new Pedimento"""
@@ -61,13 +62,11 @@ class PedimentosCreate(PedimentosBase):
license: str = Field(..., max_length=4, description="License")
pedimento_number: str = Field(..., max_length=7, description="Pedimento number")
client_id: int = Field(..., description="Client ID")
operation_type: int = Field(..., description="Operation type")
pedimento_type: str = Field(..., max_length=20, description="Pedimento type")
# operation_type, pedimento_type, status son opcionales - se pueden llenar después
pedimento_code: str = Field(
..., max_length=2, description="Pedimento key"
)
regime: str = Field(..., max_length=3, description="Regime")
status: str = Field(..., max_length=30, description="Status")
regime: str = Field(..., max_length=3, description="Regime")
pedimento_dates: Optional[PedimentoDatesCreate] = None
pedimento_decrementables: Optional[PedimentoDecrementablesCreate] = None
@@ -103,6 +102,7 @@ class PedimentosUpdate(BaseModel):
paid_price: Optional[Decimal] = None
gross_weight: Optional[Decimal] = None
exchange_rate: Optional[Decimal] = None
observations: Optional[str] = None
# Sub-resources
pedimento_dates: Optional[PedimentoDatesCreate] = None

View File

@@ -1,8 +1,9 @@
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
from sqlalchemy import (
Boolean,
ForeignKeyConstraint,
Integer,
PrimaryKeyConstraint,
@@ -38,16 +39,16 @@ class PedimentoConfigCalculations(Base, TenantScopedMixin, TimestampMixin):
id: Mapped[int] = mapped_column(Integer)
pedimento_id: Mapped[int] = mapped_column(Integer, nullable=False)
dta_type: Mapped[str] = mapped_column(String(1))
dta_operation: Mapped[int] = mapped_column(SmallInteger)
dta_vehicle_count: Mapped[int] = mapped_column(SmallInteger)
dta_mixed_rate_8permil: Mapped[int] = mapped_column(SmallInteger)
pays_vat: Mapped[int] = mapped_column(SmallInteger)
pays_prevalidation: Mapped[int] = mapped_column(SmallInteger)
include_sagar_certificate_fee: Mapped[int] = mapped_column(SmallInteger)
fixed_vehicle_dta_fee: Mapped[int] = mapped_column(SmallInteger)
additional_fixed_fee: Mapped[int] = mapped_column(SmallInteger)
additional_fixed_fee_payment_method: Mapped[int] = mapped_column(SmallInteger)
dta_type: Mapped[Optional[str]] = mapped_column(String(1), nullable=True)
dta_operation: Mapped[bool] = mapped_column(Boolean, default=False, server_default='false')
dta_vehicle_count: Mapped[int] = mapped_column(SmallInteger, default=0, server_default='0')
dta_mixed_rate_8permil: Mapped[bool] = mapped_column(Boolean, default=False, server_default='false')
pays_vat: Mapped[bool] = mapped_column(Boolean, default=False, server_default='false')
pays_prevalidation: Mapped[bool] = mapped_column(Boolean, default=False, server_default='false')
include_sagar_certificate_fee: Mapped[bool] = mapped_column(Boolean, default=False, server_default='false')
fixed_vehicle_dta_fee: Mapped[bool] = mapped_column(Boolean, default=False, server_default='false')
additional_fixed_fee: Mapped[int] = mapped_column(SmallInteger, default=0, server_default='0')
additional_fixed_fee_payment_method: Mapped[Optional[int]] = mapped_column(SmallInteger, nullable=True)
pedimento: Mapped["Pedimentos"] = relationship(
"Pedimentos", back_populates="pedimento_config_calculations"

View File

@@ -4,6 +4,7 @@ from typing import TYPE_CHECKING
from api.v1.common.base_models import TenantScopedMixin, TimestampMixin
from core.database import Base
from sqlalchemy import (
Boolean,
ForeignKeyConstraint,
Integer,
Numeric,
@@ -39,17 +40,17 @@ class PedimentoConfigParameters(Base, TenantScopedMixin, TimestampMixin):
id: Mapped[int] = mapped_column(Integer)
pedimento_id: Mapped[int] = mapped_column(Integer, nullable=False)
is_embassy: Mapped[int] = mapped_column(SmallInteger)
embassy_dta: Mapped[Decimal] = mapped_column(Numeric(11, 2))
rule_3121_section_ii: Mapped[int] = mapped_column(SmallInteger)
use_previous_tariff: Mapped[int] = mapped_column(SmallInteger)
use_payment_date_fi: Mapped[int] = mapped_column(SmallInteger)
add_state_supplier_record_505: Mapped[int] = mapped_column(SmallInteger)
customs_value_calculation: Mapped[int] = mapped_column(SmallInteger)
two_decimals_unit_value: Mapped[int] = mapped_column(SmallInteger)
customs_value_per_item: Mapped[int] = mapped_column(SmallInteger)
is_national_supplier: Mapped[int] = mapped_column(SmallInteger)
is_consolidated: Mapped[int] = mapped_column(SmallInteger)
is_embassy: Mapped[bool] = mapped_column(Boolean, default=False, server_default='false')
embassy_dta: Mapped[Decimal] = mapped_column(Numeric(11, 2), default=Decimal('0.00'), server_default='0.00')
rule_3121_section_ii: Mapped[bool] = mapped_column(Boolean, default=False, server_default='false')
use_previous_tariff: Mapped[bool] = mapped_column(Boolean, default=False, server_default='false')
use_payment_date_fi: Mapped[bool] = mapped_column(Boolean, default=False, server_default='false')
add_state_supplier_record_505: Mapped[bool] = mapped_column(Boolean, default=False, server_default='false')
customs_value_calculation: Mapped[bool] = mapped_column(Boolean, default=False, server_default='false')
two_decimals_unit_value: Mapped[bool] = mapped_column(Boolean, default=False, server_default='false')
customs_value_per_item: Mapped[bool] = mapped_column(Boolean, default=False, server_default='false')
is_national_supplier: Mapped[bool] = mapped_column(Boolean, default=False, server_default='false')
is_consolidated: Mapped[bool] = mapped_column(Boolean, default=False, server_default='false')
pedimento: Mapped["Pedimentos"] = relationship(
"Pedimentos", back_populates="pedimento_config_parameters"

View File

@@ -3,10 +3,10 @@ from typing import TYPE_CHECKING
from api.v1.common.base_models import TenantScopedMixin, TimestampMixin
from core.database import Base
from sqlalchemy import (
Boolean,
ForeignKeyConstraint,
Integer,
PrimaryKeyConstraint,
SmallInteger,
UniqueConstraint,
)
from sqlalchemy.orm import Mapped, mapped_column, relationship
@@ -37,11 +37,12 @@ class PedimentoConfigUpdateRectification(Base, TenantScopedMixin, TimestampMixin
id: Mapped[int] = mapped_column(Integer)
pedimento_id: Mapped[int] = mapped_column(Integer, nullable=False)
update_vat: Mapped[int] = mapped_column(SmallInteger)
update_advalorem: Mapped[int] = mapped_column(SmallInteger)
update_cc: Mapped[int] = mapped_column(SmallInteger)
update_ieps: Mapped[int] = mapped_column(SmallInteger)
calculate_surcharge: Mapped[int] = mapped_column(SmallInteger)
update_vat: Mapped[bool] = mapped_column(Boolean, default=False, server_default='false')
update_advalorem: Mapped[bool] = mapped_column(Boolean, default=False, server_default='false')
update_dta: Mapped[bool] = mapped_column(Boolean, default=False, server_default='false')
update_cc: Mapped[bool] = mapped_column(Boolean, default=False, server_default='false')
update_ieps: Mapped[bool] = mapped_column(Boolean, default=False, server_default='false')
calculate_surcharge: Mapped[bool] = mapped_column(Boolean, default=False, server_default='false')
pedimento: Mapped["Pedimentos"] = relationship(
"Pedimentos", back_populates="pedimento_config_update_rectification"

View File

@@ -3,10 +3,10 @@ from typing import TYPE_CHECKING
from api.v1.common.base_models import TenantScopedMixin, TimestampMixin
from core.database import Base
from sqlalchemy import (
Boolean,
ForeignKeyConstraint,
Integer,
PrimaryKeyConstraint,
SmallInteger,
UniqueConstraint,
)
from sqlalchemy.orm import Mapped, mapped_column, relationship
@@ -37,10 +37,11 @@ class PedimentoConfigUpdates(Base, TenantScopedMixin, TimestampMixin):
id: Mapped[int] = mapped_column(Integer)
pedimento_id: Mapped[int] = mapped_column(Integer, nullable=False)
update_vat: Mapped[int] = mapped_column(SmallInteger)
update_advalorem: Mapped[int] = mapped_column(SmallInteger)
update_cc: Mapped[int] = mapped_column(SmallInteger)
update_ieps: Mapped[int] = mapped_column(SmallInteger)
update_vat: Mapped[bool] = mapped_column(Boolean, default=False, server_default='false')
update_advalorem: Mapped[bool] = mapped_column(Boolean, default=False, server_default='false')
update_dta: Mapped[bool] = mapped_column(Boolean, default=False, server_default='false')
update_cc: Mapped[bool] = mapped_column(Boolean, default=False, server_default='false')
update_ieps: Mapped[bool] = mapped_column(Boolean, default=False, server_default='false')
pedimento: Mapped["Pedimentos"] = relationship(
"Pedimentos", back_populates="pedimento_config_updates"

View File

@@ -10,6 +10,7 @@ from sqlalchemy import (
Numeric,
PrimaryKeyConstraint,
String,
Text,
UniqueConstraint,
)
from sqlalchemy.orm import Mapped, mapped_column, relationship
@@ -102,65 +103,67 @@ class Pedimentos(Base, TenantScopedMixin, TimestampMixin):
pedimento_type: Mapped[str] = mapped_column(String(20))
pedimento_code: Mapped[str] = mapped_column(String(2))
regime: Mapped[str] = mapped_column(String(3))
status: Mapped[str] = mapped_column(String(30))
status: Mapped[Optional[str]] = mapped_column(String(30))
usd_value: Mapped[Optional[Decimal]] = mapped_column(Numeric(17, 6))
paid_price: Mapped[Optional[Decimal]] = mapped_column(Numeric(17, 6))
gross_weight: Mapped[Optional[Decimal]] = mapped_column(Numeric(19, 3))
exchange_rate: Mapped[Optional[Decimal]] = mapped_column(Numeric(9, 5))
observations: Mapped[Optional[str]] = mapped_column(Text)
pedimento_config_additional: Mapped["PedimentoConfigAdditional"] = relationship(
"PedimentoConfigAdditional", uselist=False, back_populates="pedimento"
"PedimentoConfigAdditional", uselist=False, back_populates="pedimento", cascade="all, delete-orphan"
)
pedimento_config_calculations: Mapped["PedimentoConfigCalculations"] = relationship(
"PedimentoConfigCalculations", uselist=False, back_populates="pedimento"
"PedimentoConfigCalculations", uselist=False, back_populates="pedimento", cascade="all, delete-orphan"
)
pedimento_config_parameters: Mapped["PedimentoConfigParameters"] = relationship(
"PedimentoConfigParameters", uselist=False, back_populates="pedimento"
"PedimentoConfigParameters", uselist=False, back_populates="pedimento", cascade="all, delete-orphan"
)
pedimento_config_surcharges: Mapped["PedimentoConfigSurcharges"] = relationship(
"PedimentoConfigSurcharges", uselist=False, back_populates="pedimento"
"PedimentoConfigSurcharges", uselist=False, back_populates="pedimento", cascade="all, delete-orphan"
)
pedimento_config_update_rectification: Mapped[
"PedimentoConfigUpdateRectification"
] = relationship(
"PedimentoConfigUpdateRectification", uselist=False, back_populates="pedimento"
"PedimentoConfigUpdateRectification", uselist=False, back_populates="pedimento", cascade="all, delete-orphan"
)
pedimento_config_updates: Mapped["PedimentoConfigUpdates"] = relationship(
"PedimentoConfigUpdates", uselist=False, back_populates="pedimento"
"PedimentoConfigUpdates", uselist=False, back_populates="pedimento", cascade="all, delete-orphan"
)
pedimento_customs_offices: Mapped["PedimentoCustomsOffices"] = relationship(
"PedimentoCustomsOffices", uselist=False, back_populates="pedimento"
"PedimentoCustomsOffices", uselist=False, back_populates="pedimento", cascade="all, delete-orphan"
)
pedimento_dates: Mapped["PedimentoDates"] = relationship(
"PedimentoDates", uselist=False, back_populates="pedimento"
"PedimentoDates", uselist=False, back_populates="pedimento", cascade="all, delete-orphan"
)
pedimento_decrementables: Mapped["PedimentoDecrementables"] = relationship(
"PedimentoDecrementables", uselist=False, back_populates="pedimento"
"PedimentoDecrementables", uselist=False, back_populates="pedimento", cascade="all, delete-orphan"
)
pedimento_incrementables: Mapped["PedimentoIncrementables"] = relationship(
"PedimentoIncrementables", uselist=False, back_populates="pedimento"
"PedimentoIncrementables", uselist=False, back_populates="pedimento", cascade="all, delete-orphan"
)
pedimento_indexes: Mapped["PedimentoIndexes"] = relationship(
"PedimentoIndexes", uselist=False, back_populates="pedimento"
"PedimentoIndexes", uselist=False, back_populates="pedimento", cascade="all, delete-orphan"
)
pedimento_payments: Mapped["PedimentoPayments"] = relationship(
"PedimentoPayments", uselist=False, back_populates="pedimento"
"PedimentoPayments", uselist=False, back_populates="pedimento", cascade="all, delete-orphan"
)
pedimento_rectification_destination: Mapped["PedimentoRectificationDestination"] = (
relationship(
"PedimentoRectificationDestination",
uselist=False,
back_populates="pedimento",
cascade="all, delete-orphan"
)
)
pedimento_rectification_origin: Mapped["PedimentoRectificationOrigin"] = (
relationship(
"PedimentoRectificationOrigin", uselist=False, back_populates="pedimento"
"PedimentoRectificationOrigin", uselist=False, back_populates="pedimento", cascade="all, delete-orphan"
)
)
pedimento_transport_means: Mapped["PedimentoTransportMeans"] = relationship(
"PedimentoTransportMeans", uselist=False, back_populates="pedimento"
"PedimentoTransportMeans", uselist=False, back_populates="pedimento", cascade="all, delete-orphan"
)
pedimento_validation: Mapped["PedimentoValidation"] = relationship(
"PedimentoValidation", uselist=False, back_populates="pedimento"
"PedimentoValidation", uselist=False, back_populates="pedimento", cascade="all, delete-orphan"
)

View File

@@ -18,7 +18,7 @@ class PedimentoConfigParametersService:
@staticmethod
def get_by_pedimento_id(
db: Session, pedimento_id: int, tenant_id: int
db: Session, pedimento_id: int, tenant_id: int, company_id: int = None
) -> Optional[PedimentoConfigParameters]:
"""Get config by pedimento ID"""
return (

View File

@@ -18,7 +18,7 @@ class PedimentoConfigSurchargesService:
@staticmethod
def get_by_pedimento_id(
db: Session, pedimento_id: int, tenant_id: int
db: Session, pedimento_id: int, tenant_id: int, company_id: int = None
) -> Optional[PedimentoConfigSurcharges]:
"""Get config by pedimento ID"""
return (

View File

@@ -20,7 +20,7 @@ class PedimentoConfigUpdateRectificationService:
@staticmethod
def get_by_pedimento_id(
db: Session, pedimento_id: int, tenant_id: int
db: Session, pedimento_id: int, tenant_id: int, company_id: int = None
) -> Optional[PedimentoConfigUpdateRectification]:
"""Get config by pedimento ID"""
return (

View File

@@ -18,7 +18,7 @@ class PedimentoConfigUpdatesService:
@staticmethod
def get_by_pedimento_id(
db: Session, pedimento_id: int, tenant_id: int
db: Session, pedimento_id: int, tenant_id: int, company_id: int = None
) -> Optional[PedimentoConfigUpdates]:
"""Get config by pedimento ID"""
return (

View File

@@ -18,7 +18,7 @@ class PedimentoCustomsOfficesService:
@staticmethod
def get_by_pedimento_id(
db: Session, pedimento_id: int, tenant_id: int
db: Session, pedimento_id: int, tenant_id: int, company_id: int = None
) -> Optional[PedimentoCustomsOffices]:
"""Get customs offices by pedimento ID"""
return (

View File

@@ -18,7 +18,7 @@ class PedimentoDatesService:
@staticmethod
def get_by_pedimento_id(
db: Session, pedimento_id: int, tenant_id: int
db: Session, pedimento_id: int, tenant_id: int, company_id: int = None
) -> Optional[PedimentoDates]:
"""Get dates by pedimento ID"""
return (

View File

@@ -18,7 +18,7 @@ class PedimentoDecrementablesService:
@staticmethod
def get_by_pedimento_id(
db: Session, pedimento_id: int, tenant_id: int
db: Session, pedimento_id: int, tenant_id: int, company_id: int = None
) -> Optional[PedimentoDecrementables]:
"""Get decrementables by pedimento ID"""
return (

View File

@@ -18,7 +18,7 @@ class PedimentoIncrementablesService:
@staticmethod
def get_by_pedimento_id(
db: Session, pedimento_id: int, tenant_id: int
db: Session, pedimento_id: int, tenant_id: int, company_id: int = None
) -> Optional[PedimentoIncrementables]:
"""Get incrementables by pedimento ID"""
return (

View File

@@ -15,7 +15,7 @@ class PedimentoIndexesService:
@staticmethod
def get_by_pedimento_id(
db: Session, pedimento_id: int, tenant_id: int
db: Session, pedimento_id: int, tenant_id: int, company_id: int = None
) -> Optional[PedimentoIndexes]:
"""Get indexes by pedimento ID"""
return (

View File

@@ -15,7 +15,7 @@ class PedimentoPaymentsService:
@staticmethod
def get_by_pedimento_id(
db: Session, pedimento_id: int, tenant_id: int
db: Session, pedimento_id: int, tenant_id: int, company_id: int = None
) -> Optional[PedimentoPayments]:
"""Get payments by pedimento ID"""
return (

View File

@@ -20,7 +20,7 @@ class PedimentoRectificationDestinationService:
@staticmethod
def get_by_pedimento_id(
db: Session, pedimento_id: int, tenant_id: int
db: Session, pedimento_id: int, tenant_id: int, company_id: int = None
) -> Optional[PedimentoRectificationDestination]:
"""Get rectification destination by pedimento ID"""
return (

View File

@@ -18,7 +18,7 @@ class PedimentoRectificationOriginService:
@staticmethod
def get_by_pedimento_id(
db: Session, pedimento_id: int, tenant_id: int
db: Session, pedimento_id: int, tenant_id: int, company_id: int = None
) -> Optional[PedimentoRectificationOrigin]:
"""Get rectification origin by pedimento ID"""
return (

View File

@@ -18,7 +18,7 @@ class PedimentoTransportMeansService:
@staticmethod
def get_by_pedimento_id(
db: Session, pedimento_id: int, tenant_id: int
db: Session, pedimento_id: int, tenant_id: int, company_id: int = None
) -> Optional[PedimentoTransportMeans]:
"""Get transport means by pedimento ID"""
return (

View File

@@ -18,7 +18,7 @@ class PedimentoValidationService:
@staticmethod
def get_by_pedimento_id(
db: Session, pedimento_id: int, tenant_id: int
db: Session, pedimento_id: int, tenant_id: int, company_id: int = None
) -> Optional[PedimentoValidation]:
"""Get validation by pedimento ID"""
return (

View File

@@ -8,6 +8,7 @@ from typing import Any, Dict, List, Optional
from sqlalchemy import desc
from sqlalchemy.orm import Session, joinedload
from sqlalchemy.orm import selectinload
from sqlalchemy.exc import IntegrityError
from datetime import datetime
from ..dtos.pedimentos import PedimentosCreate, PedimentosUpdate
@@ -222,10 +223,15 @@ class PedimentosService:
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
obj_dict = data.model_dump() if data else {}
obj_dict = data.model_dump(exclude_none=True) if data else {}
# Agregar campos extra si se proporcionan
if extra_fields:
obj_dict.update(extra_fields)
# Solo crear si hay datos significativos (más que solo IDs)
significant_fields = {k: v for k, v in obj_dict.items()
if k not in ('pedimento_id', 'tenant_id', 'company_id') and v is not None}
if not significant_fields and not extra_fields:
return
obj = model_class(**obj_dict)
obj.pedimento_id = pedimento.id
obj.tenant_id = tenant_id
@@ -272,6 +278,15 @@ class PedimentosService:
db.refresh(pedimento)
return pedimento
except IntegrityError as e:
db.rollback()
# Detectar si es un error de pedimento duplicado
error_msg = str(e.orig)
if 'pedimentos_unique_key' in error_msg or 'duplicate key value violates unique constraint' in error_msg:
logger.warning(f"Attempted to create duplicate pedimento: {e}")
raise ValueError("Ya existe un pedimento con estos datos (Año, Aduana, Patente, Número)")
logger.error(f"Integrity error creating pedimento: {e}")
raise
except Exception as e:
db.rollback()
logger.error(f"Error creating pedimento with related data: {e}")
@@ -329,11 +344,11 @@ class PedimentosService:
return
existing = service_class.get_by_pedimento_id(
db, pedimento_id, tenant_id)
db, pedimento_id, tenant_id, company_id)
if existing:
# Actualizar existente
# Actualizar existente (excluir pedimento_id, tenant_id, company_id)
for field, value in data.items():
if hasattr(existing, field):
if hasattr(existing, field) and field not in ('pedimento_id', 'tenant_id', 'company_id'):
setattr(existing, field, value)
else:
# Crear nuevo
@@ -381,6 +396,15 @@ class PedimentosService:
db.refresh(pedimento)
return pedimento
except IntegrityError as e:
db.rollback()
# Detectar si es un error de pedimento duplicado
error_msg = str(e.orig)
if 'pedimentos_unique_key' in error_msg or 'duplicate key value violates unique constraint' in error_msg:
logger.warning(f"Attempted to update to duplicate pedimento: {e}")
raise ValueError("Ya existe otro pedimento con estos datos (Año, Aduana, Patente, Número)")
logger.error(f"Integrity error updating pedimento: {e}")
raise
except Exception as e:
db.rollback()
logger.error(f"Error updating pedimento with related data: {e}")

View File

@@ -1,3 +1,4 @@
import { deLocalizeUrl } from '$lib/paraglide/runtime';
import type { RequestEvent } from '@sveltejs/kit';
export const reroute = (request) => deLocalizeUrl(request.url).pathname;
export const reroute = (request: { url: string }) => deLocalizeUrl(request.url).pathname;

View File

@@ -26,23 +26,32 @@ export interface ExchangeRateListResponse {
*/
export async function getExchangeRateByDate(date: string, companyId: number): Promise<ExchangeRate | null> {
try {
console.log('📡 [API] Solicitando tipos de cambio para company_id:', companyId);
// Get all exchange rates and filter by date on client side
const response = await api.get<ExchangeRateListResponse>(`/v1/a76/exchange-rate/?company_id=${companyId}`);
console.log('📡 [API] Respuesta recibida:', response.data?.total, 'tipos de cambio');
if (response.data && response.data.items && response.data.items.length > 0) {
// Filter by date and find USD exchange rate
const dateOnly = date.split('T')[0]; // Get YYYY-MM-DD part
console.log('🔍 [API] Buscando fecha:', dateOnly, 'en', response.data.items.length, 'registros');
const matchingRates = response.data.items.filter(rate => {
const rateDate = rate.date.split('T')[0];
return rateDate === dateOnly && rate.foreign_currency === 'USD';
const matches = rateDate === dateOnly && rate.foreign_currency === 'USD';
console.log(' - Comparando:', rateDate, '===', dateOnly, '&& USD ===', rate.foreign_currency, '→', matches);
return matches;
});
console.log('✅ [API] Encontrados', matchingRates.length, 'tipos de cambio que coinciden');
return matchingRates.length > 0 ? matchingRates[0] : null;
}
console.log('⚠️ [API] No hay datos en la respuesta');
return null;
} catch (error) {
console.error('Error fetching exchange rate:', error);
console.error('❌ [API] Error fetching exchange rate:', error);
return null;
}
}

View File

@@ -56,8 +56,8 @@ export interface PedimentoIncrementables {
freight?: number | null;
deductibles?: number | null;
currency?: string | null;
not_affect_usd_value?: number | null;
not_affect_customs_value?: number | null;
not_affect_usd_value?: boolean | null;
not_affect_customs_value?: boolean | null;
}
export interface PedimentoDecrementables {
@@ -67,22 +67,86 @@ export interface PedimentoDecrementables {
unloading?: number | null;
others?: number | null;
currency?: string | null;
not_affect_usd_value?: number | null;
not_affect_usd_value?: boolean | null;
}
export interface PedimentoIndexes {
update_factor_type?: number | null;
update_factor?: number | null;
manual_update_factor?: number | null;
manual_update_factor?: boolean | null;
}
export interface PedimentoConfigAdditional {
manual_pedimento_year?: string | null;
add_po_identifier?: number | null;
do_not_exempt_norms_complement_x?: number | null;
enable_import_invoice_recipient?: number | null;
send_502_validation_file_for_consolidated?: number | null;
add_remove_norms?: number | null;
add_po_identifier?: boolean | null;
do_not_exempt_norms_complement_x?: boolean | null;
enable_import_invoice_recipient?: boolean | null;
send_502_validation_file_for_consolidated?: boolean | null;
add_remove_norms?: boolean | null;
}
export interface PedimentoConfigCalculations {
dta_type?: string | null;
dta_operation?: boolean | null;
dta_vehicle_count?: boolean | null;
dta_mixed_rate_8permil?: boolean | null;
pays_vat?: boolean | null;
pays_prevalidation?: boolean | null;
include_sagar_certificate_fee?: boolean | null;
fixed_vehicle_dta_fee?: boolean | null;
additional_fixed_fee?: number | null;
additional_fixed_fee_payment_method?: number | null;
}
export interface PedimentoConfigSurcharges {
surcharge_igi?: boolean | null;
surcharge_dta?: boolean | null;
surcharge_vat?: boolean | null;
surcharge_isan?: boolean | null;
surcharge_ieps?: boolean | null;
surcharge_cc?: boolean | null;
}
export interface PedimentoConfigParameters {
is_embassy?: boolean | null;
embassy_dta?: string | null;
rule_3121_section_ii?: boolean | null;
use_previous_tariff?: boolean | null;
use_payment_date_fi?: boolean | null;
add_state_supplier_record_505?: boolean | null;
customs_value_calculation?: boolean | null;
two_decimals_unit_value?: boolean | null;
customs_value_per_item?: boolean | null;
is_national_supplier?: boolean | null;
is_consolidated?: boolean | null;
}
export interface PedimentoConfigUpdates {
update_vat?: boolean | null;
update_advalorem?: boolean | null;
update_dta?: boolean | null;
update_cc?: boolean | null;
update_ieps?: boolean | null;
}
export interface PedimentoConfigUpdateRectification {
update_vat?: boolean | null;
update_advalorem?: boolean | null;
update_dta?: boolean | null;
update_cc?: boolean | null;
update_ieps?: boolean | null;
calculate_surcharge?: boolean | null;
}
export interface Identificador {
id?: number;
pedimento_id?: number;
caso: string;
complemento1: string;
complemento2: string;
complemento3: string;
nodo: string;
observaciones: string;
}
export interface Pedimento {
@@ -102,7 +166,7 @@ export interface Pedimento {
paid_price?: number | null;
gross_weight?: number | null;
exchange_rate?: number | null;
observaciones?: string | null;
observations?: string | null;
created_at: string;
// Sub-resources
pedimento_dates?: PedimentoDates | null;
@@ -113,6 +177,12 @@ export interface Pedimento {
pedimento_decrementables?: PedimentoDecrementables | null;
pedimento_indexes?: PedimentoIndexes | null;
pedimento_config_additional?: PedimentoConfigAdditional | null;
pedimento_config_calculations?: PedimentoConfigCalculations | null;
pedimento_config_surcharges?: PedimentoConfigSurcharges | null;
pedimento_config_parameters?: PedimentoConfigParameters | null;
pedimento_config_updates?: PedimentoConfigUpdates | null;
pedimento_config_update_rectification?: PedimentoConfigUpdateRectification | null;
identificadores?: Identificador[] | null;
}
export interface PedimentoListResponse {
@@ -137,7 +207,7 @@ export interface CreatePedimentoData {
paid_price?: number | null;
gross_weight?: number | null;
exchange_rate?: number | null;
observaciones?: string | null;
observations?: string | null;
// Sub-resources
pedimento_dates?: PedimentoDates | null;
pedimento_payments?: PedimentoPayments | null;
@@ -147,6 +217,12 @@ export interface CreatePedimentoData {
pedimento_decrementables?: PedimentoDecrementables | null;
pedimento_indexes?: PedimentoIndexes | null;
pedimento_config_additional?: PedimentoConfigAdditional | null;
pedimento_config_calculations?: PedimentoConfigCalculations | null;
pedimento_config_surcharges?: PedimentoConfigSurcharges | null;
pedimento_config_parameters?: PedimentoConfigParameters | null;
pedimento_config_updates?: PedimentoConfigUpdates | null;
pedimento_config_update_rectification?: PedimentoConfigUpdateRectification | null;
identificadores?: Identificador[] | null;
}
export interface UpdatePedimentoData {
@@ -164,7 +240,7 @@ export interface UpdatePedimentoData {
paid_price?: number | null;
gross_weight?: number | null;
exchange_rate?: number | null;
observaciones?: string | null;
observations?: string | null;
// Sub-resources
pedimento_dates?: PedimentoDates | null;
pedimento_payments?: PedimentoPayments | null;
@@ -174,6 +250,12 @@ export interface UpdatePedimentoData {
pedimento_decrementables?: PedimentoDecrementables | null;
pedimento_indexes?: PedimentoIndexes | null;
pedimento_config_additional?: PedimentoConfigAdditional | null;
pedimento_config_calculations?: PedimentoConfigCalculations | null;
pedimento_config_surcharges?: PedimentoConfigSurcharges | null;
pedimento_config_parameters?: PedimentoConfigParameters | null;
pedimento_config_updates?: PedimentoConfigUpdates | null;
pedimento_config_update_rectification?: PedimentoConfigUpdateRectification | null;
identificadores?: Identificador[] | null;
}
export interface PedimentoFilters {

View File

@@ -2,26 +2,7 @@ import type { ColumnDef } from "@tanstack/table-core";
import { renderComponent, renderSnippet } from "$lib/components/ui/data-table/index.js";
import { createRawSnippet } from "svelte";
import DataTableActions from "./data-table-actions.svelte";
export type Pedimento = {
id: number;
tenant_id: number;
year?: string | null;
customs_office?: string | null;
license?: string | null;
pedimento_number?: string | null;
client_id?: number | null;
operation_type?: number | null;
pedimento_type?: number | null;
pedimento_code?: string | null;
regime?: string | null;
status?: string | null;
usd_value?: number | null;
paid_price?: number | null;
gross_weight?: number | null;
exchange_rate?: number | null;
created_at: string;
};
import type { Pedimento } from "$lib/api/dashboard/a76/pedimentos";
/**
* Formatea un número como moneda

View File

@@ -2,6 +2,7 @@
import { Button } from "$lib/components/ui/button";
import * as DropdownMenu from "$lib/components/ui/dropdown-menu";
import { pedimentosApi, type Pedimento } from "$lib/api/dashboard/a76/pedimentos";
import { companyStore } from "$lib/stores/company.svelte";
import { EllipsisVertical, Pencil, LoaderCircle, Trash2 } from 'lucide-svelte';
let {
@@ -24,7 +25,8 @@
error = null;
try {
const response = await pedimentosApi.delete(item.id);
const companyId = companyStore.activeCompany?.id;
const response = await pedimentosApi.delete(item.id, companyId);
if (response.error) {
if (response.status === 401) {

View File

@@ -19,7 +19,7 @@
DialogTitle,
DialogFooter
} from '$lib/components/ui/dialog';
import Checkbox from '$lib/components/ui/checkbox/checkbox.svelte';
import { Checkbox } from '$lib/components/ui/checkbox';
import {
Plus,
Pencil,

View File

@@ -17,14 +17,27 @@
pedimentoNumber?: string;
} = $props();
// Inicializar formData
if (!formData) {
formData = {
observaciones: pedimento?.observaciones || ''
};
}
exists = !!pedimento?.observaciones;
// Asegurar que formData siempre tenga un valor por defecto
formData = formData || { observaciones: '' };
// Variable para rastrear el último valor del servidor
let lastServerObservations = $state<string | null>(null);
// Actualizar formData solo cuando el pedimento cambie desde el servidor
$effect(() => {
const currentObservations = pedimento?.observations ?? null;
console.log('🔄 Pedimento changed in dates-tab-form:', currentObservations);
// Solo actualizar si el valor del servidor cambió realmente
if (currentObservations !== lastServerObservations) {
lastServerObservations = currentObservations;
formData = {
observaciones: currentObservations || ''
};
exists = !!currentObservations;
console.log('✅ Updated formData.observaciones:', formData.observaciones);
}
});
// Mapear el tipo de pedimento a un nombre legible
const tipoPedimentoNombre = $derived.by(() => {

View File

@@ -11,10 +11,12 @@
import type { CustomsBroker } from '$lib/api/dashboard/a76/customs-brokers';
import type { ClientProvider } from '$lib/api/dashboard/a76/clients-providers';
import type { CodePedimentoRegimen } from '$lib/api/dashboard/refrence_data/code_pedimento_regimens';
import IdentificadoresTabForm from './identifiers-tab-form.svelte';
let {
pedimento,
formData = $bindable(),
identificadoresFormData = $bindable(),
pedimentoCodes = [],
customsSections = [],
customsBrokers = [],
@@ -23,6 +25,7 @@
}: {
pedimento: Pedimento | null;
formData?: any;
identificadoresFormData?: any;
pedimentoCodes?: PedimentoCode[];
customsSections?: CustomsSection[];
customsBrokers?: CustomsBroker[];
@@ -249,19 +252,23 @@
// Obtener automáticamente el tipo de cambio cuando cambie la fecha de entrada
$effect(() => {
if (formData && formData.entry_date && companyStore.activeCompany) {
console.log('Buscando tipo de cambio para fecha:', formData.entry_date);
console.log('🔍 [TIPO CAMBIO] Buscando para fecha:', formData.entry_date, 'Company ID:', companyStore.activeCompany.id);
getExchangeRateByDate(formData.entry_date, companyStore.activeCompany.id)
.then(usdRate => {
console.log('Tipo de cambio USD encontrado:', usdRate);
console.log('✅ [TIPO CAMBIO] Respuesta recibida:', usdRate);
if (usdRate && formData) {
formData.exchange_rate = usdRate.value;
console.log('Tipo de cambio actualizado a:', usdRate.value);
console.log('✅ [TIPO CAMBIO] Actualizado a:', usdRate.value);
} else {
console.warn('No se encontró tipo de cambio USD para la fecha:', formData.entry_date);
console.warn('⚠️ [TIPO CAMBIO] No encontrado para fecha:', formData.entry_date);
}
})
.catch(err => console.error('Error al obtener tipo de cambio:', err));
.catch(err => {
console.error('❌ [TIPO CAMBIO] Error:', err);
});
} else {
console.log('⏭️ [TIPO CAMBIO] Saltado - formData:', !!formData, 'entry_date:', formData?.entry_date, 'company:', !!companyStore.activeCompany);
}
});
@@ -417,10 +424,9 @@
<Label for="exchange_rate">Tipo de Cambio <span class="text-red-500">*</span></Label>
<Input
id="exchange_rate"
type="number"
step="0.0001"
bind:value={formData.exchange_rate}
placeholder="En fecha de entrada"
type="text"
value={formData.exchange_rate ? Number(formData.exchange_rate).toFixed(6) : ''}
placeholder="Se obtiene automáticamente de la fecha de entrada"
readonly
disabled
class="bg-muted cursor-not-allowed"
@@ -898,9 +904,7 @@
</div>
{:else if activeSection === 'identificadores'}
<!-- Identificadores -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<p class="text-muted-foreground col-span-full">Campos de Identificadores - Por configurar</p>
</div>
<IdentificadoresTabForm bind:formData={identificadoresFormData} />
{:else if activeSection === 'indices'}
<!-- Indices -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">

View File

@@ -0,0 +1,174 @@
<script lang="ts">
import { Card } from '$lib/components/ui/card';
import { Button } from '$lib/components/ui/button';
import { Input } from '$lib/components/ui/input';
import { Label } from '$lib/components/ui/label';
import { Textarea } from '$lib/components/ui/textarea';
import * as Table from '$lib/components/ui/table';
import * as Dialog from '$lib/components/ui/dialog';
interface Identificador {
caso: string;
complemento1: string;
complemento2: string;
complemento3: string;
nodo: string;
observaciones: string;
}
let {
formData = $bindable({
identificadores: []
})
}: {
formData: {
identificadores: Identificador[];
};
} = $props();
let isIdentificadorDialogOpen = $state(false);
let editingIdentificadorIndex = $state<number | null>(null);
let currentIdentificador = $state<Identificador>({
caso: '',
complemento1: '',
complemento2: '',
complemento3: '',
nodo: '',
observaciones: ''
});
function handleNuevoIdentificador() {
currentIdentificador = {
caso: '',
complemento1: '',
complemento2: '',
complemento3: '',
nodo: '',
observaciones: ''
};
editingIdentificadorIndex = null;
isIdentificadorDialogOpen = true;
}
function handleEditarIdentificador(index: number) {
currentIdentificador = { ...formData.identificadores[index] };
editingIdentificadorIndex = index;
isIdentificadorDialogOpen = true;
}
function handleBorrarIdentificador(index: number) {
formData.identificadores = formData.identificadores.filter((_, i) => i !== index);
}
function saveIdentificador() {
if (editingIdentificadorIndex !== null) {
formData.identificadores[editingIdentificadorIndex] = { ...currentIdentificador };
} else {
formData.identificadores = [...formData.identificadores, { ...currentIdentificador }];
}
isIdentificadorDialogOpen = false;
}
</script>
<Card class="p-6">
<div class="space-y-4">
<div class="flex items-center justify-between">
<h3 class="text-lg font-semibold">Identificadores</h3>
<Button size="sm" onclick={handleNuevoIdentificador}>
Nuevo
</Button>
</div>
{#if formData.identificadores.length === 0}
<p class="text-sm text-muted-foreground text-center py-8">
No hay identificadores registrados
</p>
{:else}
<Table.Root>
<Table.Header>
<Table.Row>
<Table.Head>Caso</Table.Head>
<Table.Head>Complemento 1</Table.Head>
<Table.Head>Complemento 2</Table.Head>
<Table.Head>Complemento 3</Table.Head>
<Table.Head>Nodo</Table.Head>
<Table.Head>Observaciones</Table.Head>
<Table.Head class="text-right">Acciones</Table.Head>
</Table.Row>
</Table.Header>
<Table.Body>
{#each formData.identificadores as identificador, index}
<Table.Row>
<Table.Cell>{identificador.caso}</Table.Cell>
<Table.Cell>{identificador.complemento1}</Table.Cell>
<Table.Cell>{identificador.complemento2}</Table.Cell>
<Table.Cell>{identificador.complemento3}</Table.Cell>
<Table.Cell>{identificador.nodo}</Table.Cell>
<Table.Cell class="max-w-xs truncate">{identificador.observaciones}</Table.Cell>
<Table.Cell class="text-right">
<div class="flex justify-end gap-2">
<Button size="sm" variant="outline" onclick={() => handleEditarIdentificador(index)}>
Editar
</Button>
<Button size="sm" variant="destructive" onclick={() => handleBorrarIdentificador(index)}>
Borrar
</Button>
</div>
</Table.Cell>
</Table.Row>
{/each}
</Table.Body>
</Table.Root>
{/if}
</div>
</Card>
<!-- Dialog para agregar/editar identificador -->
<Dialog.Root bind:open={isIdentificadorDialogOpen}>
<Dialog.Content class="max-w-2xl">
<Dialog.Header>
<Dialog.Title>
{editingIdentificadorIndex !== null ? 'Editar' : 'Nuevo'} Identificador
</Dialog.Title>
</Dialog.Header>
<div class="grid grid-cols-2 gap-4">
<div class="space-y-2">
<Label for="caso">Caso</Label>
<Input id="caso" bind:value={currentIdentificador.caso} />
</div>
<div class="space-y-2">
<Label for="complemento1">Complemento 1</Label>
<Input id="complemento1" bind:value={currentIdentificador.complemento1} />
</div>
<div class="space-y-2">
<Label for="complemento2">Complemento 2</Label>
<Input id="complemento2" bind:value={currentIdentificador.complemento2} />
</div>
<div class="space-y-2">
<Label for="complemento3">Complemento 3</Label>
<Input id="complemento3" bind:value={currentIdentificador.complemento3} />
</div>
<div class="space-y-2">
<Label for="nodo">Nodo</Label>
<Input id="nodo" bind:value={currentIdentificador.nodo} />
</div>
<div class="space-y-2 col-span-2">
<Label for="observaciones">Observaciones</Label>
<Textarea id="observaciones" bind:value={currentIdentificador.observaciones} rows={3} />
</div>
</div>
<Dialog.Footer>
<Button variant="outline" onclick={() => (isIdentificadorDialogOpen = false)}>
Cancelar
</Button>
<Button onclick={saveIdentificador}>Aceptar</Button>
</Dialog.Footer>
</Dialog.Content>
</Dialog.Root>

View File

@@ -4,7 +4,7 @@
import { Input } from '$lib/components/ui/input';
import { Label } from '$lib/components/ui/label';
import * as Select from '$lib/components/ui/select';
import Checkbox from '$lib/components/ui/checkbox/checkbox.svelte';
import { Checkbox } from '$lib/components/ui/checkbox';
import {
Table,
TableBody,
@@ -102,6 +102,7 @@
}
let {
pedimento = $bindable(null),
formData = $bindable({
tipo_calculo: 'ninguno',
dta_por_operacion_ag_facturas: false,
@@ -141,9 +142,107 @@
agregar_entidad_federativa_proveedor: false
})
}: {
pedimento?: any;
formData: OtrosDatosFormData;
} = $props();
// Rastrear los valores del servidor para evitar sobrescribir cambios del usuario
let lastServerConfigHash = $state<string | null>(null);
// Cargar datos del pedimento cuando está disponible
$effect(() => {
if (pedimento) {
// Crear un hash de las configuraciones relevantes
const configHash = JSON.stringify({
calc: pedimento.pedimento_config_calculations,
params: pedimento.pedimento_config_parameters,
updates: pedimento.pedimento_config_updates,
rect: pedimento.pedimento_config_update_rectification,
surcharges: pedimento.pedimento_config_surcharges
});
// Solo actualizar si las configuraciones cambiaron
if (configHash !== lastServerConfigHash) {
lastServerConfigHash = configHash;
console.log('🔄 Cargando datos del servidor en other-data-tab-form');
console.log('📥 pedimento_config_updates del servidor:', pedimento.pedimento_config_updates);
// Mapear pedimento_config_calculations
if (pedimento.pedimento_config_calculations) {
const calc = pedimento.pedimento_config_calculations;
formData.dta_por_operacion_ag_facturas = calc.dta_operation ?? false;
formData.dta_por_numero_vehiculos = calc.dta_vehicle_count ?? false;
formData.paga_iva = calc.pays_vat ?? false;
formData.paga_prevalidacion = calc.pays_prevalidation ?? false;
formData.aplicar_dta_8_millar_partida = calc.dta_mixed_rate_8permil ?? false;
formData.incluir_eci = calc.include_sagar_certificate_fee ?? false;
formData.cuota_fija_adicional_vehiculo = calc.fixed_vehicle_dta_fee ?? false;
formData.cuota_fija_adicional_fp = calc.additional_fixed_fee?.toString() || '0';
// Mapear tipo_calculo desde dta_type
if (calc.dta_type === '1') {
formData.tipo_calculo = 'Cuota fija';
} else if (calc.dta_type === '2') {
formData.tipo_calculo = '8 al millar';
} else if (calc.dta_type === '3') {
formData.tipo_calculo = '1.76 al millar';
} else if (calc.dta_type === '4') {
formData.tipo_calculo = 'Estados extranjeros';
} else {
formData.tipo_calculo = 'ninguno';
}
}
// Mapear pedimento_config_parameters
if (pedimento.pedimento_config_parameters) {
const params = pedimento.pedimento_config_parameters;
formData.es_embajada = params.is_embassy ?? false;
formData.embajada_dta = params.embassy_dta || '0.00';
formData.regla_3_1_21_factores = params.rule_3121_section_ii ?? false;
formData.cambio_tarifa_anterior = params.use_previous_tariff ? 'true' : '';
formData.agregar_entidad_federativa_proveedor = params.add_state_supplier_record_505 ?? false;
formData.calculo_valor_aduana_v2 = params.customs_value_calculation ?? false;
formData.calculo_2_decimales_valor_unitario = params.two_decimals_unit_value ?? false;
formData.calcular_valor_aduana_base_partidas = params.customs_value_per_item ?? false;
formData.proveedor_nacional_modifico_dta = params.is_national_supplier ?? false;
}
// Mapear pedimento_config_updates
if (pedimento.pedimento_config_updates) {
const updates = pedimento.pedimento_config_updates;
formData.actualizar_iva = updates.update_vat ?? false;
formData.actualizar_advalorem = updates.update_advalorem ?? false;
formData.actualizar_dta = updates.update_dta ?? false;
formData.actualizar_cc = updates.update_cc ?? false;
formData.actualizar_ieps = updates.update_ieps ?? false;
console.log('✅ Mapeado update_dta:', updates.update_dta, '→ formData.actualizar_dta:', formData.actualizar_dta);
}
// Mapear pedimento_config_update_rectification
if (pedimento.pedimento_config_update_rectification) {
const rect = pedimento.pedimento_config_update_rectification;
formData.actualizar_iva_rect = rect.update_vat ?? false;
formData.actualizar_advalorem_rect = rect.update_advalorem ?? false;
formData.actualizar_dta_rect = rect.update_dta ?? false;
formData.actualizar_cc_rect = rect.update_cc ?? false;
formData.actualizar_ieps_rect = rect.update_ieps ?? false;
formData.calcular_recargos_diferencias = rect.calculate_surcharge ?? false;
}
// Mapear pedimento_config_surcharges
if (pedimento.pedimento_config_surcharges) {
const surcharges = pedimento.pedimento_config_surcharges;
formData.recargo_igi = surcharges.surcharge_igi ?? false;
formData.deducible_recargos = surcharges.surcharge_dta ?? false;
formData.recargo_iva = surcharges.surcharge_vat ?? false;
formData.recargo_ieps = surcharges.surcharge_ieps ?? false;
formData.recargo_isan = surcharges.surcharge_isan ?? false;
formData.recargo_cc = surcharges.surcharge_cc ?? false;
}
}
}
});
// Estados para vista de bitácora
let showBitacora = $state(false);
let currentBitacoraPage = $state(0);
@@ -447,32 +546,7 @@
currentPartesIIPage = totalPartesIIPages - 1;
}
function goToNotasFirstPage() {
currentNotasPage = 0;
}
function goToNotasPreviousPage() {
if (currentNotasPage > 0) currentNotasPage--;
}
function goToNotasNextPage() {
if (currentNotasPage < totalNotasPages - 1) currentNotasPage++;
}
function goToNotasLastPage() {
currentNotasPage = totalNotasPages - 1;
}
function toggleSelectAll() {
if (selectedEmbarques.length === embarquesParciales.length) {
selectedEmbarques = [];
} else {
selectedEmbarques = embarquesParciales.map((_, i) => i);
}
}
function handleNuevoEmbarque() {
editingEmbarqueIndex = null;
currentEmbarque = {
numero: '',
peso: 0,
@@ -495,6 +569,14 @@
isEmbarqueDialogOpen = true;
}
function toggleSelectAll() {
if (selectedEmbarques.length === embarquesParciales.length) {
selectedEmbarques = [];
} else {
selectedEmbarques = embarquesParciales.map((_, i) => i);
}
}
function handleEditarEmbarque() {
alert('Editar Embarque no implementado');
}
@@ -654,6 +736,22 @@
}
}
function goToNotasFirstPage() {
currentNotasPage = 0;
}
function goToNotasPreviousPage() {
if (currentNotasPage > 0) currentNotasPage--;
}
function goToNotasNextPage() {
if (currentNotasPage < totalNotasPages - 1) currentNotasPage++;
}
function goToNotasLastPage() {
currentNotasPage = totalNotasPages - 1;
}
function handleSeleccionAutomatizada() {
showBitacora = false;
showPartesII = false;
@@ -726,17 +824,23 @@
<span class="truncate">
{formData?.tipo_calculo === 'ninguno'
? 'Ninguno'
: formData?.tipo_calculo === 'normal'
? 'Normal'
: formData?.tipo_calculo === 'simplificado'
? 'Simplificado'
: formData?.tipo_calculo === 'Cuota fija'
? 'Cuota fija'
: formData?.tipo_calculo === '8 al millar'
? '8 al millar'
: formData?.tipo_calculo === '1.76 al millar'
? '1.76 al millar'
: formData?.tipo_calculo === 'Estados extranjeros'
? 'Estados extranjeros'
: 'Seleccionar'}
</span>
</Select.Trigger>
<Select.Content>
<Select.Item value="ninguno">Ninguno</Select.Item>
<Select.Item value="normal">Normal</Select.Item>
<Select.Item value="simplificado">Simplificado</Select.Item>
<Select.Item value="Cuota fija">Cuota fija</Select.Item>
<Select.Item value="8 al millar">8 al millar</Select.Item>
<Select.Item value="1.76 al millar">1.76 al millar</Select.Item>
<Select.Item value="Estados extranjeros">Estados extranjeros</Select.Item>
</Select.Content>
</Select.Root>
</div>

View File

@@ -63,6 +63,7 @@
}
let {
pedimento = $bindable(null),
formData = $bindable({
bultos: {
cantidad: 0,
@@ -76,6 +77,7 @@
contenedores: [] as Contenedor[]
})
}: {
pedimento?: any;
formData: {
bultos: {
cantidad: number;
@@ -90,6 +92,29 @@
};
} = $props();
// Cargar datos del pedimento cuando está disponible
$effect(() => {
if (pedimento && pedimento.pedimento_transport_means) {
const transport = pedimento.pedimento_transport_means;
// Por ahora solo log para ver qué datos vienen
console.log('📦 pedimento_transport_means del servidor:', transport);
}
});
// Log cuando el componente se monta/desmonta
$effect(() => {
console.log('🚀 BultosTransportesTabForm montado - Datos actuales:', {
transportes: formData.transportes.length,
guias: formData.guias.length,
precintos: formData.precintos.length,
contenedores: formData.contenedores.length
});
return () => {
console.log('💥 BultosTransportesTabForm desmontado');
};
});
// Estados para diálogos
let isTransporteDialogOpen = $state(false);
let isPrecintoDialogOpen = $state(false);
@@ -735,9 +760,7 @@
<TableHeader>
<TableRow>
<TableHead>Número</TableHead>
<TableHead>Identificación</TableHead>
<TableHead>Tipo</TableHead>
<TableHead class="w-[100px]">Acciones</TableHead>
</TableRow>
</TableHeader>
<TableBody>
@@ -751,7 +774,6 @@
{#each formData.contenedores as contenedor, index}
<TableRow>
<TableCell>{contenedor.numero}</TableCell>
<TableCell>{contenedor.identificacion}</TableCell>
<TableCell>{contenedor.tipo}</TableCell>
<TableCell>
<div class="flex gap-1">

View File

@@ -1,5 +1,5 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { goto, invalidateAll } from '$app/navigation';
import * as Tabs from '$lib/components/ui/tabs';
import * as Alert from '$lib/components/ui/alert';
import { Button } from '$lib/components/ui/button';
@@ -27,14 +27,14 @@
// Importar los componentes de cada pestaña (ahora sin botones de guardar propios)
import GeneralTabForm from '$lib/components/dashboard/pedimentos/edit/general-tab-form.svelte';
import DatesTabForm from '$lib/components/dashboard/pedimentos/edit/dates-tab-form.svelte';
import FacturasTabForm from '$lib/components/dashboard/pedimentos/edit/facturas-tab-form.svelte';
import PartidasTabForm from '$lib/components/dashboard/pedimentos/edit/partidas-tab-form.svelte';
import BultosTransportesTabForm from '$lib/components/dashboard/pedimentos/edit/bultos-transportes-tab-form.svelte';
import DescargasTabForm from '$lib/components/dashboard/pedimentos/edit/descargas-tab-form.svelte';
import ContribucionesTabForm from '$lib/components/dashboard/pedimentos/edit/contribuciones-tab-form.svelte';
import CuentasCompensacionTabForm from '$lib/components/dashboard/pedimentos/edit/cuentas-compensacion-tab-form.svelte';
import DigitalizacionTabForm from '$lib/components/dashboard/pedimentos/edit/digitalizacion-tab-form.svelte';
import OtrosDatosTabForm from '$lib/components/dashboard/pedimentos/edit/otros-datos-tab-form.svelte';
import FacturasTabForm from '$lib/components/dashboard/pedimentos/edit/invoices-tab-form.svelte';
import PartidasTabForm from '$lib/components/dashboard/pedimentos/edit/items-tab-form.svelte';
import BultosTransportesTabForm from '$lib/components/dashboard/pedimentos/edit/package-transportation-tab-form.svelte';
import DescargasTabForm from '$lib/components/dashboard/pedimentos/edit/discharge-tab-form.svelte';
import ContribucionesTabForm from '$lib/components/dashboard/pedimentos/edit/contributions-tab-form.svelte';
import CuentasCompensacionTabForm from '$lib/components/dashboard/pedimentos/edit/accounts-compensation-tab-form.svelte';
import DigitalizacionTabForm from '$lib/components/dashboard/pedimentos/edit/digitization-tab-form.svelte';
import OtrosDatosTabForm from '$lib/components/dashboard/pedimentos/edit/other-data-tab-form.svelte';
import PaymentsTabForm from '$lib/components/dashboard/pedimentos/edit/payments-tab-form.svelte';
import TransportTabForm from '$lib/components/dashboard/pedimentos/edit/transport-tab-form.svelte';
import ValidationTabForm from '$lib/components/dashboard/pedimentos/edit/validation-tab-form.svelte';
@@ -104,6 +104,9 @@
let digitalizacionFormData = $state({
digitalizaciones: []
});
let identificadoresFormData = $state({
identificadores: []
});
let otrosDatosFormData = $state({
tipo_calculo: 'ninguno',
dta_por_operacion_ag_facturas: false,
@@ -187,11 +190,8 @@
license: 'Patente',
pedimento_number: 'Número de Pedimento',
client_id: 'ID del Cliente',
operation_type: 'Tipo de Operación',
pedimento_type: 'Tipo de Pedimento',
pedimento_code: 'Clave',
regime: 'Régimen',
status: 'Estado'
regime: 'Régimen'
};
const missingFields: string[] = [];
@@ -226,13 +226,28 @@
exchange_rate: generalFormData?.exchange_rate || undefined
};
// Fechas - enviar si hay al menos un campo con valor
if (generalFormData) {
const hasDatesValue = generalFormData.entry_date || generalFormData.pedimento_date ||
generalFormData.extraction_date || generalFormData.rectification_payment_date ||
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
};
}
}
// Solo agregar sub-recursos en modo UPDATE (no en CREATE)
// Y solo si tienen valores reales (no enviar objetos vacíos/null)
// Observaciones - solo enviar si hay valor
if (observacionesFormData?.observaciones) {
payload.observaciones = observacionesFormData.observaciones;
}
// Observaciones - siempre enviar (puede ser string vacío para borrar)
payload.observations = observacionesFormData?.observaciones || '';
// Incrementables - solo enviar si hay al menos un campo con valor
if (generalFormData) {
@@ -246,8 +261,8 @@
freight: generalFormData.fletes || null,
deductibles: generalFormData.deducibles || null,
currency: generalFormData.moneda_incrementables || null,
not_affect_usd_value: generalFormData.no_afectar_valor_dolares_inc ? 1 : 0,
not_affect_customs_value: generalFormData.no_afectar_valor_aduana ? 1 : 0
not_affect_usd_value: generalFormData.no_afectar_valor_dolares_inc || false,
not_affect_customs_value: generalFormData.no_afectar_valor_aduana || false
};
}
}
@@ -265,7 +280,7 @@
unloading: generalFormData.descarga || null,
others: generalFormData.otros || null,
currency: generalFormData.moneda_decrementable || null,
not_affect_usd_value: generalFormData.afectar_valor_dolares ? 1 : 0
not_affect_usd_value: generalFormData.afectar_valor_dolares || false
};
}
}
@@ -286,7 +301,7 @@
payload.pedimento_indexes = {
update_factor_type: updateFactorType,
update_factor: generalFormData.factor_actualizacion || null,
manual_update_factor: generalFormData.factor_actualizacion_manual ? 1 : 0
manual_update_factor: generalFormData.factor_actualizacion_manual || false
};
}
}
@@ -300,18 +315,153 @@
if (hasConfigAdditionalValue) {
payload.pedimento_config_additional = {
manual_pedimento_year: generalFormData.anio_impresion || null,
add_po_identifier: generalFormData.agregar_po_auto ? 1 : 0,
do_not_exempt_norms_complement_x: generalFormData.no_eximir_normas ? 1 : 0,
enable_import_invoice_recipient: generalFormData.activar_destinatario ? 1 : 0,
send_502_validation_file_for_consolidated: generalFormData.agregar_registro_502 ? 1 : 0,
add_remove_norms: generalFormData.agregar_quitar_normas ? 1 : 0
add_po_identifier: generalFormData.agregar_po_auto || false,
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
};
}
}
// Eliminar campos undefined para no enviarlos
Object.keys(payload).forEach(key => {
// Identificadores - enviar array de identificadores si existen
if (identificadoresFormData?.identificadores && identificadoresFormData.identificadores.length > 0) {
payload.identificadores = identificadoresFormData.identificadores;
}
// Config Calculations - configuraciones de cálculo DTA, IVA, prevalidación
if (otrosDatosFormData) {
const hasCalculationsValue = otrosDatosFormData.tipo_calculo ||
otrosDatosFormData.dta_por_operacion_ag_facturas !== undefined ||
otrosDatosFormData.dta_por_numero_vehiculos !== undefined ||
otrosDatosFormData.paga_iva !== undefined ||
otrosDatosFormData.paga_prevalidacion !== undefined ||
otrosDatosFormData.aplicar_dta_8_millar_partida !== undefined ||
otrosDatosFormData.incluir_eci !== undefined ||
otrosDatosFormData.cuota_fija_adicional_vehiculo !== undefined ||
otrosDatosFormData.cuota_fija_adicional_fp;
if (hasCalculationsValue) {
// Convertir tipo_calculo a dta_type
let dtaTypeValue = null;
if (otrosDatosFormData.tipo_calculo === 'Cuota fija') {
dtaTypeValue = '1';
} else if (otrosDatosFormData.tipo_calculo === '8 al millar') {
dtaTypeValue = '2';
} else if (otrosDatosFormData.tipo_calculo === '1.76 al millar') {
dtaTypeValue = '3';
} else if (otrosDatosFormData.tipo_calculo === 'Estados extranjeros') {
dtaTypeValue = '4';
}
payload.pedimento_config_calculations = {
dta_type: dtaTypeValue,
dta_operation: otrosDatosFormData.dta_por_operacion_ag_facturas || false,
dta_vehicle_count: otrosDatosFormData.dta_por_numero_vehiculos || false,
dta_mixed_rate_8permil: otrosDatosFormData.aplicar_dta_8_millar_partida || false,
pays_vat: otrosDatosFormData.paga_iva || false,
pays_prevalidation: otrosDatosFormData.paga_prevalidacion || false,
include_sagar_certificate_fee: otrosDatosFormData.incluir_eci || false,
fixed_vehicle_dta_fee: otrosDatosFormData.cuota_fija_adicional_vehiculo || false,
additional_fixed_fee: otrosDatosFormData.cuota_fija_adicional_fp ? parseInt(otrosDatosFormData.cuota_fija_adicional_fp) : 0,
additional_fixed_fee_payment_method: null
};
}
}
// Config Surcharges - configuraciones de recargos
if (otrosDatosFormData) {
const hasSurchargesValue = otrosDatosFormData.recargo_igi !== undefined ||
otrosDatosFormData.deducible_recargos !== undefined ||
otrosDatosFormData.recargo_iva !== undefined ||
otrosDatosFormData.recargo_ieps !== undefined ||
otrosDatosFormData.recargo_isan !== undefined ||
otrosDatosFormData.recargo_cc !== undefined;
if (hasSurchargesValue) {
payload.pedimento_config_surcharges = {
surcharge_igi: otrosDatosFormData.recargo_igi || false,
surcharge_dta: otrosDatosFormData.deducible_recargos || false,
surcharge_vat: otrosDatosFormData.recargo_iva || false,
surcharge_ieps: otrosDatosFormData.recargo_ieps || false,
surcharge_isan: otrosDatosFormData.recargo_isan || false,
surcharge_cc: otrosDatosFormData.recargo_cc || false
};
}
}
// Config Parameters - parámetros de configuración del pedimento
if (otrosDatosFormData) {
const hasParametersValue = otrosDatosFormData.es_embajada !== undefined ||
otrosDatosFormData.embajada_dta ||
otrosDatosFormData.regla_3_1_21_factores !== undefined ||
otrosDatosFormData.cambio_tarifa_anterior ||
otrosDatosFormData.agregar_entidad_federativa_proveedor !== undefined ||
otrosDatosFormData.calculo_valor_aduana_v2 !== undefined ||
otrosDatosFormData.calculo_2_decimales_valor_unitario !== undefined ||
otrosDatosFormData.calcular_valor_aduana_base_partidas !== undefined ||
otrosDatosFormData.proveedor_nacional_modifico_dta !== undefined;
if (hasParametersValue) {
payload.pedimento_config_parameters = {
is_embassy: otrosDatosFormData.es_embajada || false,
embassy_dta: otrosDatosFormData.embajada_dta || '0.00',
rule_3121_section_ii: otrosDatosFormData.regla_3_1_21_factores || false,
use_previous_tariff: !!otrosDatosFormData.cambio_tarifa_anterior,
use_payment_date_fi: null,
add_state_supplier_record_505: otrosDatosFormData.agregar_entidad_federativa_proveedor || false,
customs_value_calculation: otrosDatosFormData.calculo_valor_aduana_v2 || false,
two_decimals_unit_value: otrosDatosFormData.calculo_2_decimales_valor_unitario || false,
customs_value_per_item: otrosDatosFormData.calcular_valor_aduana_base_partidas || false,
is_national_supplier: otrosDatosFormData.proveedor_nacional_modifico_dta || false,
is_consolidated: null
};
}
}
// Config Updates - configuraciones de actualización de valores
if (otrosDatosFormData) {
const hasUpdatesValue = otrosDatosFormData.actualizar_iva !== undefined ||
otrosDatosFormData.actualizar_advalorem !== undefined ||
otrosDatosFormData.actualizar_cc !== undefined ||
otrosDatosFormData.actualizar_ieps !== undefined;
if (hasUpdatesValue) {
payload.pedimento_config_updates = {
update_vat: otrosDatosFormData.actualizar_iva || false,
update_advalorem: otrosDatosFormData.actualizar_advalorem || false,
update_dta: otrosDatosFormData.actualizar_dta || false,
update_cc: otrosDatosFormData.actualizar_cc || false,
update_ieps: otrosDatosFormData.actualizar_ieps || false
};
console.log('📤 Enviando pedimento_config_updates:', payload.pedimento_config_updates);
}
}
// Config Update Rectification - configuraciones de actualización en rectificación
if (otrosDatosFormData) {
const hasUpdateRectValue = otrosDatosFormData.actualizar_iva_rect !== undefined ||
otrosDatosFormData.actualizar_advalorem_rect !== undefined ||
otrosDatosFormData.actualizar_dta_rect !== undefined ||
otrosDatosFormData.actualizar_cc_rect !== undefined ||
otrosDatosFormData.actualizar_ieps_rect !== undefined ||
otrosDatosFormData.calcular_recargos_diferencias !== undefined;
if (hasUpdateRectValue) {
payload.pedimento_config_update_rectification = {
update_vat: otrosDatosFormData.actualizar_iva_rect || false,
update_advalorem: otrosDatosFormData.actualizar_advalorem_rect || false,
update_dta: otrosDatosFormData.actualizar_dta_rect || false,
update_cc: otrosDatosFormData.actualizar_cc_rect || false,
update_ieps: otrosDatosFormData.actualizar_ieps_rect || false,
calculate_surcharge: otrosDatosFormData.calcular_recargos_diferencias || false
};
}
}
// Eliminar campos undefined para no enviarlos
Object.keys(payload).forEach(key => {
if (payload[key as keyof typeof payload] === undefined) {
delete payload[key as keyof typeof payload];
}
@@ -319,7 +469,10 @@
let newPedimentoId = pedimentoId;
if (data.isCreate) {
// Determinar si es creación o actualización basándose en si tenemos un ID
const isCreating = !pedimentoId || data.isCreate;
if (isCreating) {
// Crear nuevo pedimento con todos sus sub-recursos
const response = await pedimentosApi.create(payload as CreatePedimentoData);
if (response.error) {
@@ -336,6 +489,9 @@
// Actualizar pedimento existente con todos sus sub-recursos
const response = await pedimentosApi.update(pedimentoId!, payload as UpdatePedimentoData);
if (response.error) throw new Error(response.error);
// Recargar los datos del pedimento desde el servidor
await invalidateAll();
}
success = true;
@@ -420,6 +576,7 @@
<GeneralTabForm
pedimento={data.pedimento}
bind:formData={generalFormData}
bind:identificadoresFormData={identificadoresFormData}
pedimentoCodes={data.pedimentoCodes || []}
customsSections={data.customsSections || []}
customsBrokers={data.customsBrokers || []}
@@ -458,32 +615,32 @@
<Tabs.Content value="bultos-transportes">
<BultosTransportesTabForm
pedimento={data.pedimento}
bind:formData={bultosTransportesFormData}
/>
</Tabs.Content>
<!-- <Tabs.Content value="descargas">
<!-- <Tabs.Content value="descargas">
<DescargasTabForm
bind:formData={descargasFormData}
/>
</Tabs.Content> -->
<Tabs.Content value="contribuciones">
<Tabs.Content value="contribuciones">
<ContribucionesTabForm
bind:formData={contribucionesFormData}
/>
</Tabs.Content>
<Tabs.Content value="cuentas-compensacion">
<Tabs.Content value="cuentas-compensacion">
<CuentasCompensacionTabForm
bind:formData={cuentasCompensacionFormData}
/>
</Tabs.Content>
<Tabs.Content value="otros-datos">
<OtrosDatosTabForm bind:formData={otrosDatosFormData} />
</Tabs.Content>
<Tabs.Content value="digitalizacion">
<DigitalizacionTabForm bind:formData={digitalizacionFormData} />
</Tabs.Content>
</Tabs.Root>
<Tabs.Content value="otros-datos">
<OtrosDatosTabForm pedimento={data.pedimento} bind:formData={otrosDatosFormData} />
</Tabs.Content>
<Tabs.Content value="digitalizacion">
<DigitalizacionTabForm bind:formData={digitalizacionFormData} />
</Tabs.Content>
</Tabs.Root>
</div>
</div>
@@ -528,8 +685,8 @@
<Tabs.Trigger value="cuentas-compensacion" class="flex items-center gap-1.5 px-2.5 py-1.5 text-sm">
<Wallet size={14} />
<span>Cuentas/Comp.</span>
</Tabs.Trigger>
<Tabs.Trigger value="otros-datos" class="flex items-center gap-1.5 px-2.5 py-1.5 text-sm">
</Tabs.Trigger>
<Tabs.Trigger value="otros-datos" class="flex items-center gap-1.5 px-2.5 py-1.5 text-sm">
<FileStack size={14} />
<span>Otros</span>
</Tabs.Trigger>

View File

@@ -9,7 +9,8 @@
"skipLibCheck": true,
"sourceMap": true,
"strict": true,
"moduleResolution": "bundler"
"moduleResolution": "bundler",
"allowArbitraryExtensions": true
}
// Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias
// except $lib which is handled by https://svelte.dev/docs/kit/configuration#files