From f6b2b09858c61b785f4ddd98e346cd3dfe9b6d88 Mon Sep 17 00:00:00 2001 From: JesusMendoza Date: Wed, 20 May 2026 19:48:16 -0600 Subject: [PATCH] v4.0.386 complemento-comercio-exterior added --- ...s-factura-comercio-exterior-referencias.py | 1182 +++++++++++++++++ ...mplos-factura-comercio-exterior-valores.py | 1133 ++++++++++++++++ examples/ejemplos-firma-manifiestos.py | 55 + fiscalapi/__init__.py | 36 + fiscalapi/models/__init__.py | 32 + fiscalapi/models/comercio_exterior_models.py | 145 ++ fiscalapi/models/fiscalapi_models.py | 7 +- fiscalapi/models/manifest_models.py | 24 + fiscalapi/services/__init__.py | 2 + fiscalapi/services/fiscalapi_client.py | 4 +- fiscalapi/services/manifest_service.py | 11 + setup.py | 2 +- 12 files changed, 2630 insertions(+), 3 deletions(-) create mode 100644 examples/ejemplos-factura-comercio-exterior-referencias.py create mode 100644 examples/ejemplos-factura-comercio-exterior-valores.py create mode 100644 examples/ejemplos-firma-manifiestos.py create mode 100644 fiscalapi/models/comercio_exterior_models.py create mode 100644 fiscalapi/models/manifest_models.py create mode 100644 fiscalapi/services/manifest_service.py diff --git a/examples/ejemplos-factura-comercio-exterior-referencias.py b/examples/ejemplos-factura-comercio-exterior-referencias.py new file mode 100644 index 0000000..ccbe4b3 --- /dev/null +++ b/examples/ejemplos-factura-comercio-exterior-referencias.py @@ -0,0 +1,1182 @@ +# ============================================================================ +# CONFIGURACION +# ============================================================================ +# +# Ejemplos de Factura de Comercio Exterior - Modo POR REFERENCIAS. +# +# Cada caso de uso expone dos funciones: +# 1. xxx_update_people() -> Sincroniza emisor (issuer_id) y receptor +# (recipient_id) en el sistema usando +# client.people.update(...). +# 2. xxx_por_referencias() -> Llama primero a xxx_update_people() y luego +# crea la factura enviando issuer/recipient +# unicamente con su `id`. +# +# Los IDs estan centralizados al inicio del archivo. Los datos de items, +# complementos (comercioExterior y cartaPorte), totales y decimales se +# conservan identicos a los del archivo por valores. +# ============================================================================ + +from datetime import datetime +from decimal import Decimal + +from fiscalapi.models.common_models import FiscalApiSettings +from fiscalapi.services.fiscalapi_client import FiscalApiClient +from fiscalapi.models.fiscalapi_models import ( + Invoice, + InvoiceComplement, + InvoiceIssuer, + InvoiceRecipient, + InvoiceItem, + ItemTax, + TaxCredential, + Person, +) +from fiscalapi.models.comercio_exterior_models import ( + ComercioExteriorComplement, + ComercioExteriorEmisor, + ComercioExteriorEmisorDomicilio, + ComercioExteriorReceptor, + ComercioExteriorReceptorDomicilio, + ComercioExteriorDestinatario, + ComercioExteriorDestinatarioDomicilio, + ComercioExteriorMercancia, +) +from fiscalapi.models.carta_porte_models import ( + CartaPorteComplement, + Ubicacion, + UbicacionDomicilio, + Mercancia, + Autotransporte, + TipoFigura, + RegimenAduanero, +) + + +settings = FiscalApiSettings( + api_url="https://test.fiscalapi.com", + api_key="API_KEY", + tenant="TENANT_ID" +) + +client = FiscalApiClient(settings=settings) + +# Valores centralizados para todos los ejemplos (cambia aqui una sola vez) +current_date = datetime.fromisoformat("2026-05-19T08:56:40") +tipo_cambio = Decimal("17.3477") +issuer_id = "2e7b988f-3a2a-4f67-86e9-3f931dd48581" # ESCUELA KEMPER URGATE +recipient_id = "109f4d94-63ea-4a21-ab15-20c8b87d8ee9" # KARLA FUENTES + + + +# Helper: Emisor Kemper estandar para client.people.update(...) +def kemper_issuer_person(): + return Person( + id=issuer_id, + tin="EKU9003173C9", + legal_name="ESCUELA KEMPER URGATE", + sat_tax_regime_id="601", + zip_code="42501", + email="kemper@fiscalapi.com", + ) + + +# Helper: Imprimir respuesta de la API de forma legible +def print_response(response): + if response.succeeded: + print(response.data.number) + else: + print(response) + + +# ============================================================================ +# 1. Factura CE Ingreso Con Carta Porte 31 +# ============================================================================ +def factura_ce_ingreso_con_carta_porte_31_update_people(): + client.people.update(kemper_issuer_person()) + client.people.update(Person( + id=recipient_id, + tin="XEXX010101000", + legal_name="Persona Fisica Extranjera", + zip_code="42501", + sat_tax_regime_id="616", + sat_cfdi_use_id="S01", + country_id="USA", + foreign_tin="123456789", + email="karla.fuentes@fiscalapi.com", + )) + + +def factura_ce_ingreso_con_carta_porte_31_por_referencias(): + print("=== 1. Factura CE Ingreso Con Carta Porte 31 (Por Referencias) ===") + factura_ce_ingreso_con_carta_porte_31_update_people() + + invoice = Invoice( + version_code="4.0", + payment_form_code="99", + payment_method_code="PUE", + currency_code="USD", + type_code="I", + expedition_zip_code="42501", + series="CCE", + date=current_date, + payment_conditions="CondicionesDePago", + export_code="02", + issuer=InvoiceIssuer(id=issuer_id), + recipient=InvoiceRecipient(id=recipient_id), + items=[ + InvoiceItem( + item_code="78101800", + item_sku="SERV02", + quantity=Decimal("1.000000"), + unit_of_measurement_code="HUR", + description="FLETE", + unit_price=Decimal("2300.000000"), + discount=Decimal("0"), + tax_object_code="02", + item_taxes=[ + ItemTax(tax_code="002", tax_type_code="Tasa", tax_rate=Decimal("0.160000"), tax_flag_code="T"), + ItemTax(tax_code="003", tax_type_code="Tasa", tax_rate=Decimal("0.300000"), tax_flag_code="R"), + ], + ), + InvoiceItem( + item_code="50161509", + item_sku="A0001", + quantity=Decimal("1.000000"), + unit_of_measurement_code="H87", + description="Gomitas", + unit_price=Decimal("120.000000"), + discount=Decimal("0"), + tax_object_code="02", + item_taxes=[ + ItemTax(tax_code="002", tax_type_code="Tasa", tax_rate=Decimal("0.160000"), tax_flag_code="T"), + ], + ), + InvoiceItem( + item_code="50307037", + item_sku="A0002", + quantity=Decimal("1.000000"), + unit_of_measurement_code="H87", + description="Pulparindo", + unit_price=Decimal("100.000000"), + discount=Decimal("0"), + tax_object_code="02", + item_taxes=[ + ItemTax(tax_code="002", tax_type_code="Tasa", tax_rate=Decimal("0.160000"), tax_flag_code="T"), + ], + ), + ], + complement=InvoiceComplement( + carta_porte=CartaPorteComplement( + transp_internac_id="Sí", + entrada_salida_merc_id="Salida", + pais_origen_destino_id="ALB", + via_entrada_salida_id="01", + total_dist_rec=Decimal("120.00"), + unidad_peso_id="KGM", + regimen_aduaneros=[RegimenAduanero(regimen_aduanero_id="EXD")], + ubicaciones=[ + Ubicacion( + tipo_ubicacion="Origen", + id_ubicacion="OR000001", + rfc_remitente_destinatario="XAXX010101000", + nombre_remitente_destinatario="Origen Nacional", + fecha_hora_salida_llegada="2026-04-27T08:00:00", + domicilio=UbicacionDomicilio( + calle="xola", + numero_exterior="531", + colonia_id="0496", + localidad_id="03", + municipio_id="014", + estado_id="CMX", + pais_id="MEX", + codigo_postal_id="03100", + ), + ), + Ubicacion( + tipo_ubicacion="Destino", + id_ubicacion="DE000001", + rfc_remitente_destinatario="XAXX010101000", + nombre_remitente_destinatario="Destino Nacional", + fecha_hora_salida_llegada="2026-04-27T20:00:00", + distancia_recorrida=Decimal("120.00"), + domicilio=UbicacionDomicilio( + calle="Av Coyoacan", + numero_exterior="120", + colonia_id="2624", + localidad_id="03", + municipio_id="014", + estado_id="CMX", + pais_id="MEX", + codigo_postal_id="03100", + ), + ), + ], + mercancias=[ + Mercancia( + bienes_transp_id="50433238", + descripcion="Gomitas", + cantidad=Decimal("1"), + clave_unidad_id="XPK", + peso_en_kg=Decimal("10.000"), + valor_mercancia=Decimal("1200.00"), + moneda_id="USD", + fraccion_arancelaria_id="2005800100", + tipo_materia_id="04", + ), + Mercancia( + bienes_transp_id="50433238", + descripcion="Pulparindo", + cantidad=Decimal("1"), + clave_unidad_id="XPK", + peso_en_kg=Decimal("10.000"), + valor_mercancia=Decimal("1000.00"), + moneda_id="USD", + fraccion_arancelaria_id="2005800100", + tipo_materia_id="04", + ), + ], + autotransporte=Autotransporte( + perm_sct_id="TPAF02", + num_permiso_sct="123456", + config_vehicular_id="C2", + peso_bruto_vehicular=Decimal("1"), + placa_vm="555TTT", + anio_modelo_vm=2023, + asegura_resp_civil="ODISEA", + poliza_resp_civil="3456YUHNB234RT", + ), + tipos_figura=[ + TipoFigura( + tipo_figura_id="01", + rfc_figura="KAHO641101B39", + num_licencia="D0908240", + nombre_figura="OSCAR KALA HAAK", + ) + ], + ), + comercio_exterior=ComercioExteriorComplement( + clave_de_pedimento_id="A1", + certificado_origen=0, + incoterm_id="CIF", + tipo_cambio_usd=tipo_cambio, + emisor=ComercioExteriorEmisor( + domicilio=ComercioExteriorEmisorDomicilio( + calle="Av Siempre viva", + numero_exterior="123", + colonia_id="0001", + localidad_id="06", + municipio_id="025", + estado_id="COA", + pais_id="MEX", + codigo_postal_id="26015", + ), + ), + receptor=ComercioExteriorReceptor( + num_reg_id_trib="123456789", + domicilio=ComercioExteriorReceptorDomicilio( + calle="Clinton ST", + numero_exterior="10002", + estado="NY", + pais_id="USA", + codigo_postal="10002-0000", + ), + ), + mercancias=[ + ComercioExteriorMercancia( + no_identificacion="A0001", + fraccion_arancelaria_id="4011101099", + cantidad_aduana=Decimal("1.000"), + unidad_aduana_id="06", + valor_unitario_aduana=Decimal("120.00"), + valor_dolares=Decimal("120.00"), + ), + ComercioExteriorMercancia( + no_identificacion="A0002", + fraccion_arancelaria_id="8407210299", + cantidad_aduana=Decimal("1.000"), + unidad_aduana_id="06", + valor_unitario_aduana=Decimal("100.00"), + valor_dolares=Decimal("100.00"), + ), + ], + ), + ), + ) + + print_response(client.invoices.create(invoice)) + + +# ============================================================================ +# 2. Factura CE Ingreso Diferentes Monedas +# ============================================================================ +def factura_ce_ingreso_diferentes_monedas_update_people(): + client.people.update(kemper_issuer_person()) + client.people.update(Person( + id=recipient_id, + tin="XEXX010101000", + legal_name="Persona Fisica Extranjera", + zip_code="42501", + sat_tax_regime_id="616", + sat_cfdi_use_id="S01", + country_id="USA", + foreign_tin="123456789", + email="karla.fuentes@fiscalapi.com", + )) + + +def factura_ce_ingreso_diferentes_monedas_por_referencias(): + print("=== 2. Factura CE Ingreso Diferentes Monedas (Por Referencias) ===") + factura_ce_ingreso_diferentes_monedas_update_people() + + invoice = Invoice( + version_code="4.0", + payment_form_code="99", + payment_method_code="PPD", + currency_code="MXN", + type_code="I", + expedition_zip_code="42501", + series="CCE", + date=current_date, + payment_conditions="CondicionesDePago", + export_code="02", + issuer=InvoiceIssuer(id=issuer_id), + recipient=InvoiceRecipient(id=recipient_id), + items=[ + InvoiceItem( + item_code="50211503", + item_sku="131494-1055", + quantity=Decimal("2"), + unit_of_measurement_code="H87", + description="Cigarros", + unit_price=Decimal("200.00"), + discount=Decimal("0"), + tax_object_code="02", + item_taxes=[ + ItemTax(tax_code="002", tax_type_code="Tasa", tax_rate=Decimal("0.160000"), tax_flag_code="T"), + ItemTax(tax_code="001", tax_type_code="Tasa", tax_rate=Decimal("0.100000"), tax_flag_code="R"), + ItemTax(tax_code="002", tax_type_code="Tasa", tax_rate=Decimal("0.106666"), tax_flag_code="R"), + ], + ), + ], + complement=InvoiceComplement( + comercio_exterior=ComercioExteriorComplement( + clave_de_pedimento_id="A1", + certificado_origen=0, + incoterm_id="FOB", + tipo_cambio_usd=tipo_cambio, + emisor=ComercioExteriorEmisor( + domicilio=ComercioExteriorEmisorDomicilio( + calle="CALLE DEL PAPEL", + colonia_id="0214", + localidad_id="01", + municipio_id="014", + estado_id="QUE", + pais_id="MEX", + codigo_postal_id="76199", + ), + ), + receptor=ComercioExteriorReceptor( + num_reg_id_trib="123456789", + domicilio=ComercioExteriorReceptorDomicilio( + calle="ST. A", + estado="TX", + pais_id="USA", + codigo_postal="00000", + ), + ), + mercancias=[ + ComercioExteriorMercancia( + no_identificacion="131494-1055", + fraccion_arancelaria_id="2402200100", + cantidad_aduana=Decimal("2.00"), + unidad_aduana_id="01", + valor_unitario_aduana=Decimal("11.74"), + valor_dolares=Decimal("23.47"), + ), + ], + ), + ), + ) + + print_response(client.invoices.create(invoice)) + + +# ============================================================================ +# 3. Factura CE Kit Parte +# ============================================================================ +def factura_ce_kit_parte_update_people(): + client.people.update(kemper_issuer_person()) + client.people.update(Person( + id=recipient_id, + tin="XEXX010101000", + legal_name="U.S. 0026 SW", + zip_code="42501", + sat_tax_regime_id="616", + sat_cfdi_use_id="CP01", + country_id="USA", + foreign_tin="123456789", + email="karla.fuentes@fiscalapi.com", + )) + + +def factura_ce_kit_parte_por_referencias(): + print("=== 3. Factura CE Kit Parte (Por Referencias) ===") + factura_ce_kit_parte_update_people() + + invoice = Invoice( + version_code="4.0", + payment_form_code="01", + payment_method_code="PUE", + currency_code="MXN", + type_code="I", + expedition_zip_code="42501", + series="CCE", + date=current_date, + payment_conditions="CondicionesDePago", + export_code="02", + issuer=InvoiceIssuer(id=issuer_id), + recipient=InvoiceRecipient(id=recipient_id), + items=[ + InvoiceItem( + item_code="51241200", + item_sku="131494-1055", + quantity=Decimal("1.0"), + unit_of_measurement_code="H87", + description="FORMULA MAGISTRAL", + unit_price=Decimal("200.00"), + discount=Decimal("0"), + tax_object_code="01", + item_taxes=[], + ), + InvoiceItem( + item_code="51241200", + item_sku="131494-1055", + quantity=Decimal("1.0"), + unit_of_measurement_code="H87", + description="FORMULA MAGISTRAL", + unit_price=Decimal("200.00"), + discount=Decimal("0"), + tax_object_code="01", + item_taxes=[], + ), + ], + complement=InvoiceComplement( + comercio_exterior=ComercioExteriorComplement( + clave_de_pedimento_id="A1", + certificado_origen=0, + incoterm_id="FOB", + tipo_cambio_usd=tipo_cambio, + emisor=ComercioExteriorEmisor( + domicilio=ComercioExteriorEmisorDomicilio( + calle="CALLE DEL PAPEL", + colonia_id="0214", + localidad_id="01", + municipio_id="014", + estado_id="QUE", + pais_id="MEX", + codigo_postal_id="76199", + ), + ), + receptor=ComercioExteriorReceptor( + num_reg_id_trib="123456789", + domicilio=ComercioExteriorReceptorDomicilio( + calle="ST. A", + estado="TX", + pais_id="USA", + codigo_postal="00000", + ), + ), + mercancias=[ + ComercioExteriorMercancia( + no_identificacion="131494-1055", + fraccion_arancelaria_id="2402200100", + cantidad_aduana=Decimal("2"), + unidad_aduana_id="01", + valor_unitario_aduana=Decimal("10.00"), + valor_dolares=Decimal("20.00"), + ), + ], + ), + ), + ) + + print_response(client.invoices.create(invoice)) + + +# ============================================================================ +# 4. Factura CE Receptor Extranjero +# ============================================================================ +def factura_ce_receptor_extranjero_update_people(): + client.people.update(kemper_issuer_person()) + client.people.update(Person( + id=recipient_id, + tin="XEXX010101000", + legal_name="U.S. 0026 SW", + zip_code="42501", + sat_tax_regime_id="616", + sat_cfdi_use_id="CP01", + country_id="USA", + foreign_tin="123456789", + email="karla.fuentes@fiscalapi.com", + )) + + +def factura_ce_receptor_extranjero_por_referencias(): + print("=== 4. Factura CE Receptor Extranjero (Por Referencias) ===") + factura_ce_receptor_extranjero_update_people() + + invoice = Invoice( + version_code="4.0", + payment_form_code="99", + payment_method_code="PPD", + currency_code="USD", + type_code="I", + expedition_zip_code="42501", + series="CCE", + date=current_date, + payment_conditions="CondicionesDePago", + export_code="02", + issuer=InvoiceIssuer(id=issuer_id), + recipient=InvoiceRecipient(id=recipient_id), + items=[ + InvoiceItem( + item_code="50211503", + item_sku="131494-1055", + quantity=Decimal("2"), + unit_of_measurement_code="H87", + description="Cigarros", + unit_price=Decimal("200.00"), + discount=Decimal("0"), + tax_object_code="02", + item_taxes=[ + ItemTax(tax_code="002", tax_type_code="Tasa", tax_rate=Decimal("0.160000"), tax_flag_code="T"), + ItemTax(tax_code="001", tax_type_code="Tasa", tax_rate=Decimal("0.100000"), tax_flag_code="R"), + ], + ), + ], + complement=InvoiceComplement( + comercio_exterior=ComercioExteriorComplement( + clave_de_pedimento_id="A1", + certificado_origen=0, + incoterm_id="FOB", + tipo_cambio_usd=tipo_cambio, + emisor=ComercioExteriorEmisor( + domicilio=ComercioExteriorEmisorDomicilio( + calle="CALLE DEL PAPEL", + colonia_id="0214", + localidad_id="01", + municipio_id="014", + estado_id="QUE", + pais_id="MEX", + codigo_postal_id="76199", + ), + ), + receptor=ComercioExteriorReceptor( + num_reg_id_trib="123456789", + domicilio=ComercioExteriorReceptorDomicilio( + calle="ST. A", + estado="TX", + pais_id="USA", + codigo_postal="00000", + ), + ), + mercancias=[ + ComercioExteriorMercancia( + no_identificacion="131494-1055", + fraccion_arancelaria_id="2402200100", + cantidad_aduana=Decimal("117.64"), + unidad_aduana_id="01", + valor_unitario_aduana=Decimal("3.40"), + valor_dolares=Decimal("400.00"), + ), + ], + ), + ), + ) + + print_response(client.invoices.create(invoice)) + + +# ============================================================================ +# 5. Factura CE Receptor Nacional +# ============================================================================ +def factura_ce_receptor_nacional_update_people(): + client.people.update(kemper_issuer_person()) + client.people.update(Person( + id=recipient_id, + tin="URE180429TM6", + legal_name="UNIVERSIDAD ROBOTICA ESPAÑOLA", + zip_code="86991", + sat_tax_regime_id="601", + sat_cfdi_use_id="G01", + email="karla.fuentes@fiscalapi.com", + )) + + +def factura_ce_receptor_nacional_por_referencias(): + print("=== 5. Factura CE Receptor Nacional (Por Referencias) ===") + factura_ce_receptor_nacional_update_people() + + invoice = Invoice( + version_code="4.0", + payment_form_code="99", + payment_method_code="PPD", + currency_code="USD", + type_code="I", + expedition_zip_code="42501", + series="CCE", + date=current_date, + payment_conditions="CondicionesDePago", + export_code="02", + issuer=InvoiceIssuer(id=issuer_id), + recipient=InvoiceRecipient(id=recipient_id), + items=[ + InvoiceItem( + item_code="50211503", + item_sku="131494-1055", + quantity=Decimal("2"), + unit_of_measurement_code="H87", + description="Cigarros", + unit_price=Decimal("200.00"), + discount=Decimal("0"), + tax_object_code="02", + item_taxes=[ + ItemTax(tax_code="002", tax_type_code="Tasa", tax_rate=Decimal("0.160000"), tax_flag_code="T"), + ItemTax(tax_code="001", tax_type_code="Tasa", tax_rate=Decimal("0.100000"), tax_flag_code="R"), + ItemTax(tax_code="002", tax_type_code="Tasa", tax_rate=Decimal("0.106666"), tax_flag_code="R"), + ], + ), + ], + complement=InvoiceComplement( + comercio_exterior=ComercioExteriorComplement( + clave_de_pedimento_id="A1", + certificado_origen=0, + incoterm_id="FOB", + tipo_cambio_usd=tipo_cambio, + emisor=ComercioExteriorEmisor( + domicilio=ComercioExteriorEmisorDomicilio( + calle="CALLE DEL PAPEL", + colonia_id="0214", + localidad_id="01", + municipio_id="014", + estado_id="QUE", + pais_id="MEX", + codigo_postal_id="76199", + ), + ), + receptor=ComercioExteriorReceptor( + domicilio=ComercioExteriorReceptorDomicilio( + calle="CALLE DEL PAPEL", + colonia="0214", + localidad="01", + municipio="014", + estado="QUE", + pais_id="MEX", + codigo_postal="76199", + ), + ), + mercancias=[ + ComercioExteriorMercancia( + no_identificacion="131494-1055", + fraccion_arancelaria_id="2402200100", + cantidad_aduana=Decimal("117.64"), + unidad_aduana_id="01", + valor_unitario_aduana=Decimal("3.40"), + valor_dolares=Decimal("400.00"), + ), + ], + ), + ), + ) + + print_response(client.invoices.create(invoice)) + + +# ============================================================================ +# 6. Factura CE Traslado Con Carta Porte 31 +# ============================================================================ +def factura_ce_traslado_con_carta_porte_31_update_people(): + # Traslado (tipo "T"): receptor debe ser el mismo RFC que el emisor. + # Se referencia issuer_id (Kemper) tambien como receptor; se actualiza + # el Person con sat_cfdi_use_id para satisfacer la validacion del receptor. + client.people.update(Person( + id=issuer_id, + tin="EKU9003173C9", + legal_name="ESCUELA KEMPER URGATE", + zip_code="42501", + sat_tax_regime_id="601", + sat_cfdi_use_id="S01", + email="kemper@fiscalapi.com", + )) + + +def factura_ce_traslado_con_carta_porte_31_por_referencias(): + print("=== 6. Factura CE Traslado Con Carta Porte 31 (Por Referencias) ===") + factura_ce_traslado_con_carta_porte_31_update_people() + + invoice = Invoice( + version_code="4.0", + currency_code="XXX", + type_code="T", + expedition_zip_code="42501", + series="CCE", + date=current_date, + export_code="02", + issuer=InvoiceIssuer(id=issuer_id), + recipient=InvoiceRecipient(id=issuer_id), + items=[ + InvoiceItem( + item_code="78101800", + item_sku="TR01", + quantity=Decimal("1.0"), + unit_of_measurement_code="H87", + description="TRANSPORTE DE CARGA", + unit_price=Decimal("0.00"), + discount=Decimal("0"), + tax_object_code="01", + item_taxes=[], + ), + InvoiceItem( + item_code="32101622", + item_sku="UT421511", + quantity=Decimal("100.00"), + unit_of_measurement_code="XBX", + description="MEMORIA FLASH", + unit_price=Decimal("0.00"), + discount=Decimal("0"), + tax_object_code="01", + item_taxes=[], + ), + ], + complement=InvoiceComplement( + carta_porte=CartaPorteComplement( + transp_internac_id="Sí", + entrada_salida_merc_id="Salida", + pais_origen_destino_id="ALB", + via_entrada_salida_id="01", + total_dist_rec=Decimal("120.00"), + unidad_peso_id="KGM", + regimen_aduaneros=[RegimenAduanero(regimen_aduanero_id="EXD")], + ubicaciones=[ + Ubicacion( + tipo_ubicacion="Origen", + id_ubicacion="OR000001", + rfc_remitente_destinatario="XAXX010101000", + nombre_remitente_destinatario="Origen Nacional", + fecha_hora_salida_llegada="2026-04-27T08:00:00", + domicilio=UbicacionDomicilio( + calle="xola", + numero_exterior="531", + colonia_id="0496", + localidad_id="03", + municipio_id="014", + estado_id="CMX", + pais_id="MEX", + codigo_postal_id="03100", + ), + ), + Ubicacion( + tipo_ubicacion="Destino", + id_ubicacion="DE000001", + rfc_remitente_destinatario="XAXX010101000", + nombre_remitente_destinatario="Destino Nacional", + fecha_hora_salida_llegada="2026-04-27T20:00:00", + distancia_recorrida=Decimal("120.00"), + domicilio=UbicacionDomicilio( + calle="Av Coyoacan", + numero_exterior="120", + colonia_id="2624", + localidad_id="03", + municipio_id="014", + estado_id="CMX", + pais_id="MEX", + codigo_postal_id="03100", + ), + ), + ], + mercancias=[ + Mercancia( + bienes_transp_id="50433238", + descripcion="Gomitas", + cantidad=Decimal("1"), + clave_unidad_id="XPK", + peso_en_kg=Decimal("10.000"), + valor_mercancia=Decimal("1200.00"), + moneda_id="USD", + fraccion_arancelaria_id="2005800100", + tipo_materia_id="04", + ), + Mercancia( + bienes_transp_id="50433238", + descripcion="Pulparindo", + cantidad=Decimal("1"), + clave_unidad_id="XPK", + peso_en_kg=Decimal("10.000"), + valor_mercancia=Decimal("1000.00"), + moneda_id="USD", + fraccion_arancelaria_id="2005800100", + tipo_materia_id="04", + ), + ], + autotransporte=Autotransporte( + perm_sct_id="TPAF02", + num_permiso_sct="123456", + config_vehicular_id="C2", + peso_bruto_vehicular=Decimal("1"), + placa_vm="555TTT", + anio_modelo_vm=2023, + asegura_resp_civil="ODISEA", + poliza_resp_civil="3456YUHNB234RT", + ), + tipos_figura=[ + TipoFigura( + tipo_figura_id="01", + rfc_figura="KAHO641101B39", + num_licencia="D0908240", + nombre_figura="OSCAR KALA HAAK", + ) + ], + ), + comercio_exterior=ComercioExteriorComplement( + clave_de_pedimento_id="A1", + certificado_origen=0, + incoterm_id="FOB", + tipo_cambio_usd=tipo_cambio, + emisor=ComercioExteriorEmisor( + domicilio=ComercioExteriorEmisorDomicilio( + calle="CALLE DEL PAPEL", + colonia_id="0214", + localidad_id="01", + municipio_id="014", + estado_id="QUE", + pais_id="MEX", + codigo_postal_id="76199", + ), + ), + receptor=ComercioExteriorReceptor( + domicilio=ComercioExteriorReceptorDomicilio( + calle="ST. A", + estado="TX", + pais_id="USA", + codigo_postal="00000", + ), + ), + mercancias=[ + ComercioExteriorMercancia( + no_identificacion="UT421511", + fraccion_arancelaria_id="2402200100", + cantidad_aduana=Decimal("100.00"), + unidad_aduana_id="01", + valor_unitario_aduana=Decimal("1.00"), + valor_dolares=Decimal("0.00"), + ), + ], + ), + ), + ) + + print_response(client.invoices.create(invoice)) + + +# ============================================================================ +# 7. Factura CE Traslado Traslado Mercancia Propia +# ============================================================================ +def factura_ce_traslado_traslado_mercancia_propia_update_people(): + # Traslado (tipo "T"): receptor debe ser el mismo RFC que el emisor. + # Se referencia issuer_id (Kemper) tambien como receptor; se actualiza + # el Person con sat_cfdi_use_id para satisfacer la validacion del receptor. + client.people.update(Person( + id=issuer_id, + tin="EKU9003173C9", + legal_name="ESCUELA KEMPER URGATE", + zip_code="42501", + sat_tax_regime_id="601", + sat_cfdi_use_id="S01", + email="kemper@fiscalapi.com", + )) + + +def factura_ce_traslado_traslado_mercancia_propia_por_referencias(): + print("=== 7. Factura CE Traslado Traslado Mercancia Propia (Por Referencias) ===") + factura_ce_traslado_traslado_mercancia_propia_update_people() + + invoice = Invoice( + version_code="4.0", + currency_code="USD", + type_code="T", + expedition_zip_code="42501", + series="CCE", + date=current_date, + export_code="02", + issuer=InvoiceIssuer(id=issuer_id), + recipient=InvoiceRecipient(id=issuer_id), + items=[ + InvoiceItem( + item_code="50211503", + item_sku="131494-1055", + quantity=Decimal("1.0"), + unit_of_measurement_code="H87", + description="My description...", + unit_price=Decimal("0.00"), + discount=Decimal("0"), + tax_object_code="01", + item_taxes=[], + ), + ], + complement=InvoiceComplement( + comercio_exterior=ComercioExteriorComplement( + motivo_traslado_id="02", + clave_de_pedimento_id="A1", + certificado_origen=0, + incoterm_id="FCA", + tipo_cambio_usd=tipo_cambio, + emisor=ComercioExteriorEmisor( + domicilio=ComercioExteriorEmisorDomicilio( + calle="CALLE DEL PAPEL", + colonia_id="0214", + localidad_id="01", + municipio_id="014", + estado_id="QUE", + pais_id="MEX", + codigo_postal_id="76199", + ), + ), + receptor=ComercioExteriorReceptor( + domicilio=ComercioExteriorReceptorDomicilio( + calle="SW Street.", + numero_exterior="12345", + localidad="Oregon", + estado="OR", + pais_id="USA", + codigo_postal="12345", + ), + ), + destinatarios=[ + ComercioExteriorDestinatario( + num_reg_id_trib="123456789", + nombre="EKU9003173C9", + domicilios=[ + ComercioExteriorDestinatarioDomicilio( + calle="SW Street.", + numero_exterior="12345", + localidad="Oregon", + estado="OR", + pais_id="USA", + codigo_postal="12345", + ), + ], + ), + ], + mercancias=[ + ComercioExteriorMercancia( + no_identificacion="131494-1055", + fraccion_arancelaria_id="0101210100", + cantidad_aduana=Decimal("1"), + unidad_aduana_id="07", + valor_unitario_aduana=Decimal("22.64"), + valor_dolares=Decimal("22.64"), + ), + ], + ), + ), + ) + + print_response(client.invoices.create(invoice)) + + +# ============================================================================ +# 8. Factura CE Traslado Traslado +# ============================================================================ +def factura_ce_traslado_traslado_update_people(): + # Traslado (tipo "T"): receptor debe ser el mismo RFC que el emisor. + # Se referencia issuer_id (Kemper) tambien como receptor; se actualiza + # el Person con sat_cfdi_use_id para satisfacer la validacion del receptor. + client.people.update(Person( + id=issuer_id, + tin="EKU9003173C9", + legal_name="ESCUELA KEMPER URGATE", + zip_code="42501", + sat_tax_regime_id="601", + sat_cfdi_use_id="G01", + email="kemper@fiscalapi.com", + )) + + +def factura_ce_traslado_traslado_por_referencias(): + print("=== 8. Factura CE Traslado Traslado (Por Referencias) ===") + factura_ce_traslado_traslado_update_people() + + invoice = Invoice( + version_code="4.0", + currency_code="USD", + type_code="T", + expedition_zip_code="42501", + series="CCE", + date=current_date, + export_code="02", + issuer=InvoiceIssuer(id=issuer_id), + recipient=InvoiceRecipient(id=issuer_id), + items=[ + InvoiceItem( + item_code="50211503", + item_sku="131494-1055", + quantity=Decimal("2"), + unit_of_measurement_code="H87", + description="Cigarros", + unit_price=Decimal("200.00"), + discount=Decimal("0"), + tax_object_code="01", + item_taxes=[], + ), + ], + complement=InvoiceComplement( + comercio_exterior=ComercioExteriorComplement( + clave_de_pedimento_id="A1", + certificado_origen=0, + incoterm_id="FOB", + tipo_cambio_usd=tipo_cambio, + emisor=ComercioExteriorEmisor( + domicilio=ComercioExteriorEmisorDomicilio( + calle="CALLE DEL PAPEL", + colonia_id="0214", + localidad_id="01", + municipio_id="014", + estado_id="QUE", + pais_id="MEX", + codigo_postal_id="76199", + ), + ), + receptor=ComercioExteriorReceptor( + domicilio=ComercioExteriorReceptorDomicilio( + calle="ST. A", + estado="TX", + pais_id="USA", + codigo_postal="00000", + ), + ), + mercancias=[ + ComercioExteriorMercancia( + no_identificacion="131494-1055", + fraccion_arancelaria_id="2402200100", + cantidad_aduana=Decimal("117.64"), + unidad_aduana_id="01", + valor_unitario_aduana=Decimal("3.40"), + valor_dolares=Decimal("400.00"), + ), + ], + ), + ), + ) + + print_response(client.invoices.create(invoice)) + + +# ============================================================================ +# 9. Factura CE Unidades De Medida No Equivalentes +# ============================================================================ +def factura_ce_unidades_de_medida_no_equivalentes_update_people(): + client.people.update(kemper_issuer_person()) + client.people.update(Person( + id=recipient_id, + tin="XEXX010101000", + legal_name="U.S. 0026 SW", + zip_code="42501", + sat_tax_regime_id="616", + sat_cfdi_use_id="CP01", + country_id="USA", + foreign_tin="123456789", + email="karla.fuentes@fiscalapi.com", + )) + + +def factura_ce_unidades_de_medida_no_equivalentes_por_referencias(): + print("=== 9. Factura CE Unidades De Medida No Equivalentes (Por Referencias) ===") + factura_ce_unidades_de_medida_no_equivalentes_update_people() + + invoice = Invoice( + version_code="4.0", + payment_form_code="99", + payment_method_code="PPD", + currency_code="USD", + type_code="I", + expedition_zip_code="42501", + series="CCE", + date=current_date, + payment_conditions="CondicionesDePago", + export_code="02", + issuer=InvoiceIssuer(id=issuer_id), + recipient=InvoiceRecipient(id=recipient_id), + items=[ + InvoiceItem( + item_code="50201708", + item_sku="131494-1055", + quantity=Decimal("1.000"), + unit_of_measurement_code="H87", + description="Bebida", + unit_price=Decimal("100.00"), + discount=Decimal("0"), + tax_object_code="02", + item_taxes=[ + ItemTax(tax_code="002", tax_type_code="Tasa", tax_rate=Decimal("0.160000"), tax_flag_code="T"), + ItemTax(tax_code="001", tax_type_code="Tasa", tax_rate=Decimal("0.100000"), tax_flag_code="R"), + ItemTax(tax_code="002", tax_type_code="Tasa", tax_rate=Decimal("0.106666"), tax_flag_code="R"), + ], + ), + ], + complement=InvoiceComplement( + comercio_exterior=ComercioExteriorComplement( + clave_de_pedimento_id="A1", + certificado_origen=0, + incoterm_id="FOB", + tipo_cambio_usd=tipo_cambio, + emisor=ComercioExteriorEmisor( + domicilio=ComercioExteriorEmisorDomicilio( + calle="CALLE DEL PAPEL", + colonia_id="0214", + localidad_id="01", + municipio_id="014", + estado_id="QUE", + pais_id="MEX", + codigo_postal_id="76199", + ), + ), + receptor=ComercioExteriorReceptor( + num_reg_id_trib="123456789", + domicilio=ComercioExteriorReceptorDomicilio( + calle="ST. A", + estado="TX", + pais_id="USA", + codigo_postal="00000", + ), + ), + mercancias=[ + ComercioExteriorMercancia( + no_identificacion="131494-1055", + fraccion_arancelaria_id="2009310201", + cantidad_aduana=Decimal("0.500"), + unidad_aduana_id="08", + valor_unitario_aduana=Decimal("200.00"), + valor_dolares=Decimal("100.00"), + ), + ], + ), + ), + ) + + print_response(client.invoices.create(invoice)) + + +# ============================================================================ +# MAIN +# ============================================================================ +if __name__ == "__main__": + factura_ce_ingreso_con_carta_porte_31_por_referencias() + # factura_ce_ingreso_diferentes_monedas_por_referencias() + # factura_ce_kit_parte_por_referencias() + # factura_ce_receptor_extranjero_por_referencias() + # factura_ce_receptor_nacional_por_referencias() + # factura_ce_traslado_con_carta_porte_31_por_referencias() + # factura_ce_traslado_traslado_mercancia_propia_por_referencias() + # factura_ce_traslado_traslado_por_referencias() + # factura_ce_unidades_de_medida_no_equivalentes_por_referencias() diff --git a/examples/ejemplos-factura-comercio-exterior-valores.py b/examples/ejemplos-factura-comercio-exterior-valores.py new file mode 100644 index 0000000..28bad6e --- /dev/null +++ b/examples/ejemplos-factura-comercio-exterior-valores.py @@ -0,0 +1,1133 @@ +# ============================================================================ +# CONFIGURACION +# ============================================================================ + +from datetime import datetime +from decimal import Decimal + +from fiscalapi.models.common_models import FiscalApiSettings +from fiscalapi.services.fiscalapi_client import FiscalApiClient +from fiscalapi.models.fiscalapi_models import ( + Invoice, + InvoiceComplement, + InvoiceIssuer, + InvoiceRecipient, + InvoiceItem, + ItemTax, + TaxCredential, +) +from fiscalapi.models.comercio_exterior_models import ( + ComercioExteriorComplement, + ComercioExteriorEmisor, + ComercioExteriorEmisorDomicilio, + ComercioExteriorReceptor, + ComercioExteriorReceptorDomicilio, + ComercioExteriorDestinatario, + ComercioExteriorDestinatarioDomicilio, + ComercioExteriorMercancia, +) +from fiscalapi.models.carta_porte_models import ( + CartaPorteComplement, + Ubicacion, + UbicacionDomicilio, + Mercancia, + Autotransporte, + TipoFigura, + RegimenAduanero, +) + + +settings = FiscalApiSettings( + api_url="https://test.fiscalapi.com", + api_key="API_KEY", + tenant="TENANT_ID" +) + +client = FiscalApiClient(settings=settings) + +# Certificados en base64 +escuela_kemper_urgate_base64_cer = "MIIFsDCCA5igAwIBAgIUMzAwMDEwMDAwMDA1MDAwMDM0MTYwDQYJKoZIhvcNAQELBQAwggErMQ8wDQYDVQQDDAZBQyBVQVQxLjAsBgNVBAoMJVNFUlZJQ0lPIERFIEFETUlOSVNUUkFDSU9OIFRSSUJVVEFSSUExGjAYBgNVBAsMEVNBVC1JRVMgQXV0aG9yaXR5MSgwJgYJKoZIhvcNAQkBFhlvc2Nhci5tYXJ0aW5lekBzYXQuZ29iLm14MR0wGwYDVQQJDBQzcmEgY2VycmFkYSBkZSBjYWxpejEOMAwGA1UEEQwFMDYzNzAxCzAJBgNVBAYTAk1YMRkwFwYDVQQIDBBDSVVEQUQgREUgTUVYSUNPMREwDwYDVQQHDAhDT1lPQUNBTjERMA8GA1UELRMIMi41LjQuNDUxJTAjBgkqhkiG9w0BCQITFnJlc3BvbnNhYmxlOiBBQ0RNQS1TQVQwHhcNMjMwNTE4MTE0MzUxWhcNMjcwNTE4MTE0MzUxWjCB1zEnMCUGA1UEAxMeRVNDVUVMQSBLRU1QRVIgVVJHQVRFIFNBIERFIENWMScwJQYDVQQpEx5FU0NVRUxBIEtFTVBFUiBVUkdBVEUgU0EgREUgQ1YxJzAlBgNVBAoTHkVTQ1VFTEEgS0VNUEVSIFVSR0FURSBTQSBERSBDVjElMCMGA1UELRMcRUtVOTAwMzE3M0M5IC8gVkFEQTgwMDkyN0RKMzEeMBwGA1UEBRMVIC8gVkFEQTgwMDkyN0hTUlNSTDA1MRMwEQYDVQQLEwpTdWN1cnNhbCAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtmecO6n2GS0zL025gbHGQVxznPDICoXzR2uUngz4DqxVUC/w9cE6FxSiXm2ap8Gcjg7wmcZfm85EBaxCx/0J2u5CqnhzIoGCdhBPuhWQnIh5TLgj/X6uNquwZkKChbNe9aeFirU/JbyN7Egia9oKH9KZUsodiM/pWAH00PCtoKJ9OBcSHMq8Rqa3KKoBcfkg1ZrgueffwRLws9yOcRWLb02sDOPzGIm/jEFicVYt2Hw1qdRE5xmTZ7AGG0UHs+unkGjpCVeJ+BEBn0JPLWVvDKHZAQMj6s5Bku35+d/MyATkpOPsGT/VTnsouxekDfikJD1f7A1ZpJbqDpkJnss3vQIDAQABox0wGzAMBgNVHRMBAf8EAjAAMAsGA1UdDwQEAwIGwDANBgkqhkiG9w0BAQsFAAOCAgEAFaUgj5PqgvJigNMgtrdXZnbPfVBbukAbW4OGnUhNrA7SRAAfv2BSGk16PI0nBOr7qF2mItmBnjgEwk+DTv8Zr7w5qp7vleC6dIsZFNJoa6ZndrE/f7KO1CYruLXr5gwEkIyGfJ9NwyIagvHHMszzyHiSZIA850fWtbqtythpAliJ2jF35M5pNS+YTkRB+T6L/c6m00ymN3q9lT1rB03YywxrLreRSFZOSrbwWfg34EJbHfbFXpCSVYdJRfiVdvHnewN0r5fUlPtR9stQHyuqewzdkyb5jTTw02D2cUfL57vlPStBj7SEi3uOWvLrsiDnnCIxRMYJ2UA2ktDKHk+zWnsDmaeleSzonv2CHW42yXYPCvWi88oE1DJNYLNkIjua7MxAnkNZbScNw01A6zbLsZ3y8G6eEYnxSTRfwjd8EP4kdiHNJftm7Z4iRU7HOVh79/lRWB+gd171s3d/mI9kte3MRy6V8MMEMCAnMboGpaooYwgAmwclI2XZCczNWXfhaWe0ZS5PmytD/GDpXzkX0oEgY9K/uYo5V77NdZbGAjmyi8cE2B2ogvyaN2XfIInrZPgEffJ4AB7kFA2mwesdLOCh0BLD9itmCve3A1FGR4+stO2ANUoiI3w3Tv2yQSg4bjeDlJ08lXaaFCLW2peEXMXjQUk7fmpb5MNuOUTW6BE=" +escuela_kemper_urgate_base64_key = "MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIAgEAAoIBAQACAggAMBQGCCqGSIb3DQMHBAgwggS/AgEAMASCBMh4EHl7aNSCaMDA1VlRoXCZ5UUmqErAbucoZQObOaLUEm+I+QZ7Y8Giupo+F1XWkLvAsdk/uZlJcTfKLJyJbJwsQYbSpLOCLataZ4O5MVnnmMbfG//NKJn9kSMvJQZhSwAwoGLYDm1ESGezrvZabgFJnoQv8Si1nAhVGTk9FkFBesxRzq07dmZYwFCnFSX4xt2fDHs1PMpQbeq83aL/PzLCce3kxbYSB5kQlzGtUYayiYXcu0cVRu228VwBLCD+2wTDDoCmRXtPesgrLKUR4WWWb5N2AqAU1mNDC+UEYsENAerOFXWnmwrcTAu5qyZ7GsBMTpipW4Dbou2yqQ0lpA/aB06n1kz1aL6mNqGPaJ+OqoFuc8Ugdhadd+MmjHfFzoI20SZ3b2geCsUMNCsAd6oXMsZdWm8lzjqCGWHFeol0ik/xHMQvuQkkeCsQ28PBxdnUgf7ZGer+TN+2ZLd2kvTBOk6pIVgy5yC6cZ+o1Tloql9hYGa6rT3xcMbXlW+9e5jM2MWXZliVW3ZhaPjptJFDbIfWxJPjz4QvKyJk0zok4muv13Iiwj2bCyefUTRz6psqI4cGaYm9JpscKO2RCJN8UluYGbbWmYQU+Int6LtZj/lv8p6xnVjWxYI+rBPdtkpfFYRp+MJiXjgPw5B6UGuoruv7+vHjOLHOotRo+RdjZt7NqL9dAJnl1Qb2jfW6+d7NYQSI/bAwxO0sk4taQIT6Gsu/8kfZOPC2xk9rphGqCSS/4q3Os0MMjA1bcJLyoWLp13pqhK6bmiiHw0BBXH4fbEp4xjSbpPx4tHXzbdn8oDsHKZkWh3pPC2J/nVl0k/yF1KDVowVtMDXE47k6TGVcBoqe8PDXCG9+vjRpzIidqNo5qebaUZu6riWMWzldz8x3Z/jLWXuDiM7/Yscn0Z2GIlfoeyz+GwP2eTdOw9EUedHjEQuJY32bq8LICimJ4Ht+zMJKUyhwVQyAER8byzQBwTYmYP5U0wdsyIFitphw+/IH8+v08Ia1iBLPQAeAvRfTTIFLCs8foyUrj5Zv2B/wTYIZy6ioUM+qADeXyo45uBLLqkN90Rf6kiTqDld78NxwsfyR5MxtJLVDFkmf2IMMJHTqSfhbi+7QJaC11OOUJTD0v9wo0X/oO5GvZhe0ZaGHnm9zqTopALuFEAxcaQlc4R81wjC4wrIrqWnbcl2dxiBtD73KW+wcC9ymsLf4I8BEmiN25lx/OUc1IHNyXZJYSFkEfaxCEZWKcnbiyf5sqFSSlEqZLc4lUPJFAoP6s1FHVcyO0odWqdadhRZLZC9RCzQgPlMRtji/OXy5phh7diOBZv5UYp5nb+MZ2NAB/eFXm2JLguxjvEstuvTDmZDUb6Uqv++RdhO5gvKf/AcwU38ifaHQ9uvRuDocYwVxZS2nr9rOwZ8nAh+P2o4e0tEXjxFKQGhxXYkn75H3hhfnFYjik/2qunHBBZfcdG148MaNP6DjX33M238T9Zw/GyGx00JMogr2pdP4JAErv9a5yt4YR41KGf8guSOUbOXVARw6+ybh7+meb7w4BeTlj3aZkv8tVGdfIt3lrwVnlbzhLjeQY6PplKp3/a5Kr5yM0T4wJoKQQ6v3vSNmrhpbuAtKxpMILe8CQoo=" +password = "12345678a" + +# Valores centralizados para todos los ejemplos (cambia aqui una sola vez) +current_date = datetime.fromisoformat("2026-05-19T08:56:40") +tipo_cambio = Decimal("17.3477") +issuer_id = "2e7b988f-3a2a-4f67-86e9-3f931dd48581" #ESCUELA KEMPER URGATE +recipient_id = "109f4d94-63ea-4a21-ab15-20c8b87d8ee9" #KARLA FUENTES + + +# Helper: Credenciales de la escuela Kemper Urgate +def kemper_credentials(): + return [ + TaxCredential(base64_file=escuela_kemper_urgate_base64_cer, file_type=0, password=password), + TaxCredential(base64_file=escuela_kemper_urgate_base64_key, file_type=1, password=password), + ] + +# Helper: Imprimir respuesta de la API de forma legible +def print_response(response): + if response.succeeded: + print(response.data.number) + else: + print(response.message) + print(response.details) + + +# ============================================================================ +# 1. Factura CE Ingreso Con Carta Porte 31 +# ============================================================================ +def factura_ce_ingreso_con_carta_porte_31_por_valores(): + print("=== 1. Factura CE Ingreso Con Carta Porte 31 ===") + + invoice = Invoice( + version_code="4.0", + payment_form_code="99", + payment_method_code="PUE", + currency_code="USD", + type_code="I", + expedition_zip_code="42501", + series="CCE", + date=current_date, + payment_conditions="CondicionesDePago", + export_code="02", + issuer=InvoiceIssuer( + tin="EKU9003173C9", + legal_name="ESCUELA KEMPER URGATE", + tax_regime_code="601", + tax_credentials=kemper_credentials(), + ), + recipient=InvoiceRecipient( + tin="XEXX010101000", + legal_name="Persona Fisica Extranjera", + zip_code="42501", + tax_regime_code="616", + cfdi_use_code="S01", + country_id="USA", + foreign_tin="123456789", + ), + items=[ + InvoiceItem( + item_code="78101800", + item_sku="SERV02", + quantity=Decimal("1.000000"), + unit_of_measurement_code="HUR", + description="FLETE", + unit_price=Decimal("2300.000000"), + discount=Decimal("0"), + tax_object_code="02", + item_taxes=[ + ItemTax(tax_code="002", tax_type_code="Tasa", tax_rate=Decimal("0.160000"), tax_flag_code="T"), + ItemTax(tax_code="003", tax_type_code="Tasa", tax_rate=Decimal("0.300000"), tax_flag_code="R"), + ], + ), + InvoiceItem( + item_code="50161509", + item_sku="A0001", + quantity=Decimal("1.000000"), + unit_of_measurement_code="H87", + description="Gomitas", + unit_price=Decimal("120.000000"), + discount=Decimal("0"), + tax_object_code="02", + item_taxes=[ + ItemTax(tax_code="002", tax_type_code="Tasa", tax_rate=Decimal("0.160000"), tax_flag_code="T"), + ], + ), + InvoiceItem( + item_code="50307037", + item_sku="A0002", + quantity=Decimal("1.000000"), + unit_of_measurement_code="H87", + description="Pulparindo", + unit_price=Decimal("100.000000"), + discount=Decimal("0"), + tax_object_code="02", + item_taxes=[ + ItemTax(tax_code="002", tax_type_code="Tasa", tax_rate=Decimal("0.160000"), tax_flag_code="T"), + ], + ), + ], + complement=InvoiceComplement( + carta_porte=CartaPorteComplement( + transp_internac_id="Sí", + entrada_salida_merc_id="Salida", + pais_origen_destino_id="ALB", + via_entrada_salida_id="01", + total_dist_rec=Decimal("120.00"), + unidad_peso_id="KGM", + regimen_aduaneros=[RegimenAduanero(regimen_aduanero_id="EXD")], + ubicaciones=[ + Ubicacion( + tipo_ubicacion="Origen", + id_ubicacion="OR000001", + rfc_remitente_destinatario="XAXX010101000", + nombre_remitente_destinatario="Origen Nacional", + fecha_hora_salida_llegada="2026-04-27T08:00:00", + domicilio=UbicacionDomicilio( + calle="xola", + numero_exterior="531", + colonia_id="0496", + localidad_id="03", + municipio_id="014", + estado_id="CMX", + pais_id="MEX", + codigo_postal_id="03100", + ), + ), + Ubicacion( + tipo_ubicacion="Destino", + id_ubicacion="DE000001", + rfc_remitente_destinatario="XAXX010101000", + nombre_remitente_destinatario="Destino Nacional", + fecha_hora_salida_llegada="2026-04-27T20:00:00", + distancia_recorrida=Decimal("120.00"), + domicilio=UbicacionDomicilio( + calle="Av Coyoacan", + numero_exterior="120", + colonia_id="2624", + localidad_id="03", + municipio_id="014", + estado_id="CMX", + pais_id="MEX", + codigo_postal_id="03100", + ), + ), + ], + mercancias=[ + Mercancia( + bienes_transp_id="50433238", + descripcion="Gomitas", + cantidad=Decimal("1"), + clave_unidad_id="XPK", + peso_en_kg=Decimal("10.000"), + valor_mercancia=Decimal("1200.00"), + moneda_id="USD", + fraccion_arancelaria_id="2005800100", + tipo_materia_id="04", + ), + Mercancia( + bienes_transp_id="50433238", + descripcion="Pulparindo", + cantidad=Decimal("1"), + clave_unidad_id="XPK", + peso_en_kg=Decimal("10.000"), + valor_mercancia=Decimal("1000.00"), + moneda_id="USD", + fraccion_arancelaria_id="2005800100", + tipo_materia_id="04", + ), + ], + autotransporte=Autotransporte( + perm_sct_id="TPAF02", + num_permiso_sct="123456", + config_vehicular_id="C2", + peso_bruto_vehicular=Decimal("1"), + placa_vm="555TTT", + anio_modelo_vm=2023, + asegura_resp_civil="ODISEA", + poliza_resp_civil="3456YUHNB234RT", + ), + tipos_figura=[ + TipoFigura( + tipo_figura_id="01", + rfc_figura="KAHO641101B39", + num_licencia="D0908240", + nombre_figura="OSCAR KALA HAAK", + ) + ], + ), + comercio_exterior=ComercioExteriorComplement( + clave_de_pedimento_id="A1", + certificado_origen=0, + incoterm_id="CIF", + tipo_cambio_usd=tipo_cambio, + emisor=ComercioExteriorEmisor( + domicilio=ComercioExteriorEmisorDomicilio( + calle="Av Siempre viva", + numero_exterior="123", + colonia_id="0001", + localidad_id="06", + municipio_id="025", + estado_id="COA", + pais_id="MEX", + codigo_postal_id="26015", + ), + ), + receptor=ComercioExteriorReceptor( + num_reg_id_trib="123456789", + domicilio=ComercioExteriorReceptorDomicilio( + calle="Clinton ST", + numero_exterior="10002", + estado="NY", + pais_id="USA", + codigo_postal="10002-0000", + ), + ), + mercancias=[ + ComercioExteriorMercancia( + no_identificacion="A0001", + fraccion_arancelaria_id="4011101099", + cantidad_aduana=Decimal("1.000"), + unidad_aduana_id="06", + valor_unitario_aduana=Decimal("120.00"), + valor_dolares=Decimal("120.00"), + ), + ComercioExteriorMercancia( + no_identificacion="A0002", + fraccion_arancelaria_id="8407210299", + cantidad_aduana=Decimal("1.000"), + unidad_aduana_id="06", + valor_unitario_aduana=Decimal("100.00"), + valor_dolares=Decimal("100.00"), + ), + ], + ), + ), + ) + + print_response(client.invoices.create(invoice)) + + +# ============================================================================ +# 2. Factura CE Ingreso Diferentes Monedas +# ============================================================================ +def factura_ce_ingreso_diferentes_monedas_por_valores(): + print("=== 2. Factura CE Ingreso Diferentes Monedas ===") + + invoice = Invoice( + version_code="4.0", + payment_form_code="99", + payment_method_code="PPD", + currency_code="MXN", + type_code="I", + expedition_zip_code="42501", + series="CCE", + date=current_date, + payment_conditions="CondicionesDePago", + export_code="02", + issuer=InvoiceIssuer( + tin="EKU9003173C9", + legal_name="ESCUELA KEMPER URGATE", + tax_regime_code="601", + tax_credentials=kemper_credentials(), + ), + recipient=InvoiceRecipient( + tin="XEXX010101000", + legal_name="Persona Fisica Extranjera", + zip_code="42501", + tax_regime_code="616", + cfdi_use_code="S01", + country_id="USA", + foreign_tin="123456789", + ), + items=[ + InvoiceItem( + item_code="50211503", + item_sku="131494-1055", + quantity=Decimal("2"), + unit_of_measurement_code="H87", + description="Cigarros", + unit_price=Decimal("200.00"), + discount=Decimal("0"), + tax_object_code="02", + item_taxes=[ + ItemTax(tax_code="002", tax_type_code="Tasa", tax_rate=Decimal("0.160000"), tax_flag_code="T"), + ItemTax(tax_code="001", tax_type_code="Tasa", tax_rate=Decimal("0.100000"), tax_flag_code="R"), + ItemTax(tax_code="002", tax_type_code="Tasa", tax_rate=Decimal("0.106666"), tax_flag_code="R"), + ], + ), + ], + complement=InvoiceComplement( + comercio_exterior=ComercioExteriorComplement( + clave_de_pedimento_id="A1", + certificado_origen=0, + incoterm_id="FOB", + tipo_cambio_usd=tipo_cambio, + emisor=ComercioExteriorEmisor( + domicilio=ComercioExteriorEmisorDomicilio( + calle="CALLE DEL PAPEL", + colonia_id="0214", + localidad_id="01", + municipio_id="014", + estado_id="QUE", + pais_id="MEX", + codigo_postal_id="76199", + ), + ), + receptor=ComercioExteriorReceptor( + num_reg_id_trib="123456789", + domicilio=ComercioExteriorReceptorDomicilio( + calle="ST. A", + estado="TX", + pais_id="USA", + codigo_postal="00000", + ), + ), + mercancias=[ + ComercioExteriorMercancia( + no_identificacion="131494-1055", + fraccion_arancelaria_id="2402200100", + cantidad_aduana=Decimal("2.00"), + unidad_aduana_id="01", + valor_unitario_aduana=Decimal("11.74"), + valor_dolares=Decimal("23.47"), + ), + ], + ), + ), + ) + + print_response(client.invoices.create(invoice)) + + +# ============================================================================ +# 3. Factura CE Kit Parte +# ============================================================================ +def factura_ce_kit_parte_por_valores(): + print("=== 3. Factura CE Kit Parte ===") + + invoice = Invoice( + version_code="4.0", + payment_form_code="01", + payment_method_code="PUE", + currency_code="MXN", + type_code="I", + expedition_zip_code="42501", + series="CCE", + date=current_date, + payment_conditions="CondicionesDePago", + export_code="02", + issuer=InvoiceIssuer( + tin="EKU9003173C9", + legal_name="ESCUELA KEMPER URGATE", + tax_regime_code="601", + tax_credentials=kemper_credentials(), + ), + recipient=InvoiceRecipient( + tin="XEXX010101000", + legal_name="U.S. 0026 SW", + zip_code="42501", + tax_regime_code="616", + cfdi_use_code="CP01", + country_id="USA", + foreign_tin="123456789", + ), + items=[ + InvoiceItem( + item_code="51241200", + item_sku="131494-1055", + quantity=Decimal("1.0"), + unit_of_measurement_code="H87", + description="FORMULA MAGISTRAL", + unit_price=Decimal("200.00"), + discount=Decimal("0"), + tax_object_code="01", + item_taxes=[], + ), + InvoiceItem( + item_code="51241200", + item_sku="131494-1055", + quantity=Decimal("1.0"), + unit_of_measurement_code="H87", + description="FORMULA MAGISTRAL", + unit_price=Decimal("200.00"), + discount=Decimal("0"), + tax_object_code="01", + item_taxes=[], + ), + ], + complement=InvoiceComplement( + comercio_exterior=ComercioExteriorComplement( + clave_de_pedimento_id="A1", + certificado_origen=0, + incoterm_id="FOB", + tipo_cambio_usd=tipo_cambio, + emisor=ComercioExteriorEmisor( + domicilio=ComercioExteriorEmisorDomicilio( + calle="CALLE DEL PAPEL", + colonia_id="0214", + localidad_id="01", + municipio_id="014", + estado_id="QUE", + pais_id="MEX", + codigo_postal_id="76199", + ), + ), + receptor=ComercioExteriorReceptor( + num_reg_id_trib="123456789", + domicilio=ComercioExteriorReceptorDomicilio( + calle="ST. A", + estado="TX", + pais_id="USA", + codigo_postal="00000", + ), + ), + mercancias=[ + ComercioExteriorMercancia( + no_identificacion="131494-1055", + fraccion_arancelaria_id="2402200100", + cantidad_aduana=Decimal("2"), + unidad_aduana_id="01", + valor_unitario_aduana=Decimal("10.00"), + valor_dolares=Decimal("20.00"), + ), + ], + ), + ), + ) + + print_response(client.invoices.create(invoice)) + + +# ============================================================================ +# 4. Factura CE Receptor Extranjero +# ============================================================================ +def factura_ce_receptor_extranjero_por_valores(): + print("=== 4. Factura CE Receptor Extranjero ===") + + invoice = Invoice( + version_code="4.0", + payment_form_code="99", + payment_method_code="PPD", + currency_code="USD", + type_code="I", + expedition_zip_code="42501", + series="CCE", + date=current_date, + payment_conditions="CondicionesDePago", + export_code="02", + issuer=InvoiceIssuer( + tin="EKU9003173C9", + legal_name="ESCUELA KEMPER URGATE", + tax_regime_code="601", + tax_credentials=kemper_credentials(), + ), + recipient=InvoiceRecipient( + tin="XEXX010101000", + legal_name="U.S. 0026 SW", + zip_code="42501", + tax_regime_code="616", + cfdi_use_code="CP01", + country_id="USA", + foreign_tin="123456789", + ), + items=[ + InvoiceItem( + item_code="50211503", + item_sku="131494-1055", + quantity=Decimal("2"), + unit_of_measurement_code="H87", + description="Cigarros", + unit_price=Decimal("200.00"), + discount=Decimal("0"), + tax_object_code="02", + item_taxes=[ + ItemTax(tax_code="002", tax_type_code="Tasa", tax_rate=Decimal("0.160000"), tax_flag_code="T"), + ItemTax(tax_code="001", tax_type_code="Tasa", tax_rate=Decimal("0.100000"), tax_flag_code="R"), + ], + ), + ], + complement=InvoiceComplement( + comercio_exterior=ComercioExteriorComplement( + clave_de_pedimento_id="A1", + certificado_origen=0, + incoterm_id="FOB", + tipo_cambio_usd=tipo_cambio, + emisor=ComercioExteriorEmisor( + domicilio=ComercioExteriorEmisorDomicilio( + calle="CALLE DEL PAPEL", + colonia_id="0214", + localidad_id="01", + municipio_id="014", + estado_id="QUE", + pais_id="MEX", + codigo_postal_id="76199", + ), + ), + receptor=ComercioExteriorReceptor( + num_reg_id_trib="123456789", + domicilio=ComercioExteriorReceptorDomicilio( + calle="ST. A", + estado="TX", + pais_id="USA", + codigo_postal="00000", + ), + ), + mercancias=[ + ComercioExteriorMercancia( + no_identificacion="131494-1055", + fraccion_arancelaria_id="2402200100", + cantidad_aduana=Decimal("117.64"), + unidad_aduana_id="01", + valor_unitario_aduana=Decimal("3.40"), + valor_dolares=Decimal("400.00"), + ), + ], + ), + ), + ) + + print_response(client.invoices.create(invoice)) + + +# ============================================================================ +# 5. Factura CE Receptor Nacional +# ============================================================================ +def factura_ce_receptor_nacional_por_valores(): + print("=== 5. Factura CE Receptor Nacional ===") + + invoice = Invoice( + version_code="4.0", + payment_form_code="99", + payment_method_code="PPD", + currency_code="USD", + type_code="I", + expedition_zip_code="42501", + series="CCE", + date=current_date, + payment_conditions="CondicionesDePago", + export_code="02", + issuer=InvoiceIssuer( + tin="EKU9003173C9", + legal_name="ESCUELA KEMPER URGATE", + tax_regime_code="601", + tax_credentials=kemper_credentials(), + ), + recipient=InvoiceRecipient( + tin="URE180429TM6", + legal_name="UNIVERSIDAD ROBOTICA ESPAÑOLA", + zip_code="86991", + tax_regime_code="601", + cfdi_use_code="G01", + ), + items=[ + InvoiceItem( + item_code="50211503", + item_sku="131494-1055", + quantity=Decimal("2"), + unit_of_measurement_code="H87", + description="Cigarros", + unit_price=Decimal("200.00"), + discount=Decimal("0"), + tax_object_code="02", + item_taxes=[ + ItemTax(tax_code="002", tax_type_code="Tasa", tax_rate=Decimal("0.160000"), tax_flag_code="T"), + ItemTax(tax_code="001", tax_type_code="Tasa", tax_rate=Decimal("0.100000"), tax_flag_code="R"), + ItemTax(tax_code="002", tax_type_code="Tasa", tax_rate=Decimal("0.106666"), tax_flag_code="R"), + ], + ), + ], + complement=InvoiceComplement( + comercio_exterior=ComercioExteriorComplement( + clave_de_pedimento_id="A1", + certificado_origen=0, + incoterm_id="FOB", + tipo_cambio_usd=tipo_cambio, + emisor=ComercioExteriorEmisor( + domicilio=ComercioExteriorEmisorDomicilio( + calle="CALLE DEL PAPEL", + colonia_id="0214", + localidad_id="01", + municipio_id="014", + estado_id="QUE", + pais_id="MEX", + codigo_postal_id="76199", + ), + ), + receptor=ComercioExteriorReceptor( + domicilio=ComercioExteriorReceptorDomicilio( + calle="CALLE DEL PAPEL", + colonia="0214", + localidad="01", + municipio="014", + estado="QUE", + pais_id="MEX", + codigo_postal="76199", + ), + ), + mercancias=[ + ComercioExteriorMercancia( + no_identificacion="131494-1055", + fraccion_arancelaria_id="2402200100", + cantidad_aduana=Decimal("117.64"), + unidad_aduana_id="01", + valor_unitario_aduana=Decimal("3.40"), + valor_dolares=Decimal("400.00"), + ), + ], + ), + ), + ) + + print_response(client.invoices.create(invoice)) + + +# ============================================================================ +# 6. Factura CE Traslado Con Carta Porte 31 +# ============================================================================ +def factura_ce_traslado_con_carta_porte_31_por_valores(): + print("=== 6. Factura CE Traslado Con Carta Porte 31 ===") + + invoice = Invoice( + version_code="4.0", + currency_code="XXX", + type_code="T", + expedition_zip_code="42501", + series="CCE", + date=current_date, + export_code="02", + issuer=InvoiceIssuer( + tin="EKU9003173C9", + legal_name="ESCUELA KEMPER URGATE", + tax_regime_code="601", + tax_credentials=kemper_credentials(), + ), + recipient=InvoiceRecipient( + tin="EKU9003173C9", + legal_name="ESCUELA KEMPER URGATE", + zip_code="42501", + tax_regime_code="601", + cfdi_use_code="S01", + ), + items=[ + InvoiceItem( + item_code="78101800", + item_sku="TR01", + quantity=Decimal("1.0"), + unit_of_measurement_code="H87", + description="TRANSPORTE DE CARGA", + unit_price=Decimal("0.00"), + discount=Decimal("0"), + tax_object_code="01", + item_taxes=[], + ), + InvoiceItem( + item_code="32101622", + item_sku="UT421511", + quantity=Decimal("100.00"), + unit_of_measurement_code="XBX", + description="MEMORIA FLASH", + unit_price=Decimal("0.00"), + discount=Decimal("0"), + tax_object_code="01", + item_taxes=[], + ), + ], + complement=InvoiceComplement( + carta_porte=CartaPorteComplement( + transp_internac_id="Sí", + entrada_salida_merc_id="Salida", + pais_origen_destino_id="ALB", + via_entrada_salida_id="01", + total_dist_rec=Decimal("120.00"), + unidad_peso_id="KGM", + regimen_aduaneros=[RegimenAduanero(regimen_aduanero_id="EXD")], + ubicaciones=[ + Ubicacion( + tipo_ubicacion="Origen", + id_ubicacion="OR000001", + rfc_remitente_destinatario="XAXX010101000", + nombre_remitente_destinatario="Origen Nacional", + fecha_hora_salida_llegada="2026-04-27T08:00:00", + domicilio=UbicacionDomicilio( + calle="xola", + numero_exterior="531", + colonia_id="0496", + localidad_id="03", + municipio_id="014", + estado_id="CMX", + pais_id="MEX", + codigo_postal_id="03100", + ), + ), + Ubicacion( + tipo_ubicacion="Destino", + id_ubicacion="DE000001", + rfc_remitente_destinatario="XAXX010101000", + nombre_remitente_destinatario="Destino Nacional", + fecha_hora_salida_llegada="2026-04-27T20:00:00", + distancia_recorrida=Decimal("120.00"), + domicilio=UbicacionDomicilio( + calle="Av Coyoacan", + numero_exterior="120", + colonia_id="2624", + localidad_id="03", + municipio_id="014", + estado_id="CMX", + pais_id="MEX", + codigo_postal_id="03100", + ), + ), + ], + mercancias=[ + Mercancia( + bienes_transp_id="50433238", + descripcion="Gomitas", + cantidad=Decimal("1"), + clave_unidad_id="XPK", + peso_en_kg=Decimal("10.000"), + valor_mercancia=Decimal("1200.00"), + moneda_id="USD", + fraccion_arancelaria_id="2005800100", + tipo_materia_id="04", + ), + Mercancia( + bienes_transp_id="50433238", + descripcion="Pulparindo", + cantidad=Decimal("1"), + clave_unidad_id="XPK", + peso_en_kg=Decimal("10.000"), + valor_mercancia=Decimal("1000.00"), + moneda_id="USD", + fraccion_arancelaria_id="2005800100", + tipo_materia_id="04", + ), + ], + autotransporte=Autotransporte( + perm_sct_id="TPAF02", + num_permiso_sct="123456", + config_vehicular_id="C2", + peso_bruto_vehicular=Decimal("1"), + placa_vm="555TTT", + anio_modelo_vm=2023, + asegura_resp_civil="ODISEA", + poliza_resp_civil="3456YUHNB234RT", + ), + tipos_figura=[ + TipoFigura( + tipo_figura_id="01", + rfc_figura="KAHO641101B39", + num_licencia="D0908240", + nombre_figura="OSCAR KALA HAAK", + ) + ], + ), + comercio_exterior=ComercioExteriorComplement( + clave_de_pedimento_id="A1", + certificado_origen=0, + incoterm_id="FOB", + tipo_cambio_usd=tipo_cambio, + emisor=ComercioExteriorEmisor( + domicilio=ComercioExteriorEmisorDomicilio( + calle="CALLE DEL PAPEL", + colonia_id="0214", + localidad_id="01", + municipio_id="014", + estado_id="QUE", + pais_id="MEX", + codigo_postal_id="76199", + ), + ), + receptor=ComercioExteriorReceptor( + domicilio=ComercioExteriorReceptorDomicilio( + calle="ST. A", + estado="TX", + pais_id="USA", + codigo_postal="00000", + ), + ), + mercancias=[ + ComercioExteriorMercancia( + no_identificacion="UT421511", + fraccion_arancelaria_id="2402200100", + cantidad_aduana=Decimal("100.00"), + unidad_aduana_id="01", + valor_unitario_aduana=Decimal("1.00"), + valor_dolares=Decimal("0.00"), + ), + ], + ), + ), + ) + + print_response(client.invoices.create(invoice)) + + +# ============================================================================ +# 7. Factura CE Traslado Traslado Mercancia Propia +# ============================================================================ +def factura_ce_traslado_traslado_mercancia_propia_por_valores(): + print("=== 7. Factura CE Traslado Traslado Mercancia Propia ===") + + invoice = Invoice( + version_code="4.0", + currency_code="USD", + type_code="T", + expedition_zip_code="42501", + series="CCE", + date=current_date, + export_code="02", + issuer=InvoiceIssuer( + tin="EKU9003173C9", + legal_name="ESCUELA KEMPER URGATE", + tax_regime_code="601", + tax_credentials=kemper_credentials(), + ), + recipient=InvoiceRecipient( + tin="EKU9003173C9", + legal_name="ESCUELA KEMPER URGATE", + zip_code="42501", + tax_regime_code="601", + cfdi_use_code="S01", + ), + items=[ + InvoiceItem( + item_code="50211503", + item_sku="131494-1055", + quantity=Decimal("1.0"), + unit_of_measurement_code="H87", + description="My description...", + unit_price=Decimal("0.00"), + discount=Decimal("0"), + tax_object_code="01", + item_taxes=[], + ), + ], + complement=InvoiceComplement( + comercio_exterior=ComercioExteriorComplement( + motivo_traslado_id="02", + clave_de_pedimento_id="A1", + certificado_origen=0, + incoterm_id="FCA", + tipo_cambio_usd=tipo_cambio, + emisor=ComercioExteriorEmisor( + domicilio=ComercioExteriorEmisorDomicilio( + calle="CALLE DEL PAPEL", + colonia_id="0214", + localidad_id="01", + municipio_id="014", + estado_id="QUE", + pais_id="MEX", + codigo_postal_id="76199", + ), + ), + receptor=ComercioExteriorReceptor( + domicilio=ComercioExteriorReceptorDomicilio( + calle="SW Street.", + numero_exterior="12345", + localidad="Oregon", + estado="OR", + pais_id="USA", + codigo_postal="12345", + ), + ), + destinatarios=[ + ComercioExteriorDestinatario( + num_reg_id_trib="123456789", + nombre="EKU9003173C9", + domicilios=[ + ComercioExteriorDestinatarioDomicilio( + calle="SW Street.", + numero_exterior="12345", + localidad="Oregon", + estado="OR", + pais_id="USA", + codigo_postal="12345", + ), + ], + ), + ], + mercancias=[ + ComercioExteriorMercancia( + no_identificacion="131494-1055", + fraccion_arancelaria_id="0101210100", + cantidad_aduana=Decimal("1"), + unidad_aduana_id="07", + valor_unitario_aduana=Decimal("22.64"), + valor_dolares=Decimal("22.64"), + ), + ], + ), + ), + ) + + print_response(client.invoices.create(invoice)) + + +# ============================================================================ +# 8. Factura CE Traslado Traslado +# ============================================================================ +def factura_ce_traslado_traslado_por_valores(): + print("=== 8. Factura CE Traslado Traslado ===") + + invoice = Invoice( + version_code="4.0", + currency_code="USD", + type_code="T", + expedition_zip_code="42501", + series="CCE", + date=current_date, + export_code="02", + issuer=InvoiceIssuer( + tin="EKU9003173C9", + legal_name="ESCUELA KEMPER URGATE", + tax_regime_code="601", + tax_credentials=kemper_credentials(), + ), + recipient=InvoiceRecipient( + tin="EKU9003173C9", + legal_name="ESCUELA KEMPER URGATE", + zip_code="42501", + tax_regime_code="601", + cfdi_use_code="G01", + ), + items=[ + InvoiceItem( + item_code="50211503", + item_sku="131494-1055", + quantity=Decimal("2"), + unit_of_measurement_code="H87", + description="Cigarros", + unit_price=Decimal("200.00"), + discount=Decimal("0"), + tax_object_code="01", + item_taxes=[], + ), + ], + complement=InvoiceComplement( + comercio_exterior=ComercioExteriorComplement( + clave_de_pedimento_id="A1", + certificado_origen=0, + incoterm_id="FOB", + tipo_cambio_usd=tipo_cambio, + emisor=ComercioExteriorEmisor( + domicilio=ComercioExteriorEmisorDomicilio( + calle="CALLE DEL PAPEL", + colonia_id="0214", + localidad_id="01", + municipio_id="014", + estado_id="QUE", + pais_id="MEX", + codigo_postal_id="76199", + ), + ), + receptor=ComercioExteriorReceptor( + domicilio=ComercioExteriorReceptorDomicilio( + calle="ST. A", + estado="TX", + pais_id="USA", + codigo_postal="00000", + ), + ), + mercancias=[ + ComercioExteriorMercancia( + no_identificacion="131494-1055", + fraccion_arancelaria_id="2402200100", + cantidad_aduana=Decimal("117.64"), + unidad_aduana_id="01", + valor_unitario_aduana=Decimal("3.40"), + valor_dolares=Decimal("400.00"), + ), + ], + ), + ), + ) + + print_response(client.invoices.create(invoice)) + + +# ============================================================================ +# 9. Factura CE Unidades De Medida No Equivalentes +# ============================================================================ +def factura_ce_unidades_de_medida_no_equivalentes_por_valores(): + print("=== 9. Factura CE Unidades De Medida No Equivalentes ===") + + invoice = Invoice( + version_code="4.0", + payment_form_code="99", + payment_method_code="PPD", + currency_code="USD", + type_code="I", + expedition_zip_code="42501", + series="CCE", + date=current_date, + payment_conditions="CondicionesDePago", + export_code="02", + issuer=InvoiceIssuer( + tin="EKU9003173C9", + legal_name="ESCUELA KEMPER URGATE", + tax_regime_code="601", + tax_credentials=kemper_credentials(), + ), + recipient=InvoiceRecipient( + tin="XEXX010101000", + legal_name="U.S. 0026 SW", + zip_code="42501", + tax_regime_code="616", + cfdi_use_code="CP01", + country_id="USA", + foreign_tin="123456789", + ), + items=[ + InvoiceItem( + item_code="50201708", + item_sku="131494-1055", + quantity=Decimal("1.000"), + unit_of_measurement_code="H87", + description="Bebida", + unit_price=Decimal("100.00"), + discount=Decimal("0"), + tax_object_code="02", + item_taxes=[ + ItemTax(tax_code="002", tax_type_code="Tasa", tax_rate=Decimal("0.160000"), tax_flag_code="T"), + ItemTax(tax_code="001", tax_type_code="Tasa", tax_rate=Decimal("0.100000"), tax_flag_code="R"), + ItemTax(tax_code="002", tax_type_code="Tasa", tax_rate=Decimal("0.106666"), tax_flag_code="R"), + ], + ), + ], + complement=InvoiceComplement( + comercio_exterior=ComercioExteriorComplement( + clave_de_pedimento_id="A1", + certificado_origen=0, + incoterm_id="FOB", + tipo_cambio_usd=tipo_cambio, + emisor=ComercioExteriorEmisor( + domicilio=ComercioExteriorEmisorDomicilio( + calle="CALLE DEL PAPEL", + colonia_id="0214", + localidad_id="01", + municipio_id="014", + estado_id="QUE", + pais_id="MEX", + codigo_postal_id="76199", + ), + ), + receptor=ComercioExteriorReceptor( + num_reg_id_trib="123456789", + domicilio=ComercioExteriorReceptorDomicilio( + calle="ST. A", + estado="TX", + pais_id="USA", + codigo_postal="00000", + ), + ), + mercancias=[ + ComercioExteriorMercancia( + no_identificacion="131494-1055", + fraccion_arancelaria_id="2009310201", + cantidad_aduana=Decimal("0.500"), + unidad_aduana_id="08", + valor_unitario_aduana=Decimal("200.00"), + valor_dolares=Decimal("100.00"), + ), + ], + ), + ), + ) + + print_response(client.invoices.create(invoice)) + + +# ============================================================================ +# MAIN +# ============================================================================ +if __name__ == "__main__": + # factura_ce_ingreso_con_carta_porte_31_por_valores() + # factura_ce_ingreso_diferentes_monedas_por_valores() + # factura_ce_kit_parte_por_valores() + # factura_ce_receptor_extranjero_por_valores() + # factura_ce_receptor_nacional_por_valores() + # factura_ce_traslado_con_carta_porte_31_por_valores() + # factura_ce_traslado_traslado_mercancia_propia_por_valores() + # factura_ce_traslado_traslado_por_valores() + factura_ce_unidades_de_medida_no_equivalentes_por_valores() diff --git a/examples/ejemplos-firma-manifiestos.py b/examples/ejemplos-firma-manifiestos.py new file mode 100644 index 0000000..905c073 --- /dev/null +++ b/examples/ejemplos-firma-manifiestos.py @@ -0,0 +1,55 @@ +""" +Ejemplo: firma de carta manifiesto vía POST /api/v4/manifests usando el SDK de FiscalAPI. + +El endpoint recibe el certificado (.cer) y la llave privada (.key) de la FIEL (e.firma) +del contribuyente en base64, además de la contraseña de la llave. Devuelve el PDF de la +carta manifiesto firmada (también en base64). + +Importante: usar FIEL, NO CSD. El payload es idéntico al de tax-files, pero el endpoint +requiere los archivos de la e.firma del SAT (no los del CSD de timbrado). +""" +from fiscalapi import FiscalApiClient, FiscalApiSettings +from fiscalapi.models import SignManifestRequest + + +# Configuración de FiscalAPI +settings = FiscalApiSettings( + api_url="https://test.fiscalapi.com", + api_key="API_KEY", + tenant="TENANT_ID" +) + +# Credenciales FIEL de prueba (ESCUELA KEMPER URGATE) +escuela_kemper_urgate_base64_cer_fiel = "MIIGBDCCA+ygAwIBAgIUMzAwMDEwMDAwMDA1MDAwMDM0MTUwDQYJKoZIhvcNAQELBQAwggErMQ8wDQYDVQQDDAZBQyBVQVQxLjAsBgNVBAoMJVNFUlZJQ0lPIERFIEFETUlOSVNUUkFDSU9OIFRSSUJVVEFSSUExGjAYBgNVBAsMEVNBVC1JRVMgQXV0aG9yaXR5MSgwJgYJKoZIhvcNAQkBFhlvc2Nhci5tYXJ0aW5lekBzYXQuZ29iLm14MR0wGwYDVQQJDBQzcmEgY2VycmFkYSBkZSBjYWxpejEOMAwGA1UEEQwFMDYzNzAxCzAJBgNVBAYTAk1YMRkwFwYDVQQIDBBDSVVEQUQgREUgTUVYSUNPMREwDwYDVQQHDAhDT1lPQUNBTjERMA8GA1UELRMIMi41LjQuNDUxJTAjBgkqhkiG9w0BCQITFnJlc3BvbnNhYmxlOiBBQ0RNQS1TQVQwHhcNMjMwNTE4MDQzNzE0WhcNMjcwNTE3MDQzNzE0WjCB+TEnMCUGA1UEAxMeRVNDVUVMQSBLRU1QRVIgVVJHQVRFIFNBIERFIENWMScwJQYDVQQpEx5FU0NVRUxBIEtFTVBFUiBVUkdBVEUgU0EgREUgQ1YxJzAlBgNVBAoTHkVTQ1VFTEEgS0VNUEVSIFVSR0FURSBTQSBERSBDVjELMAkGA1UEBhMCTVgxKDAmBgkqhkiG9w0BCQEWGVNBVHBydWViYXNAcHJ1ZWJhcy5nb2IubXgxJTAjBgNVBC0THEVLVTkwMDMxNzNDOSAvIFZBREE4MDA5MjdESjMxHjAcBgNVBAUTFSAvIFZBREE4MDA5MjdIU1JTUkwwNTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANGvrdNGWRoqw2vVRPwA3oL5g5oEoTV3YufXF/1xzM4/vk7Nyt7m10+OStBdk0tKJ+DtOXdBFnYauwkq3ts1iOH2yr69CqLfHwPjQ9zKLn+A17ZUJK7UImHHgiVP0LkbLWc0rKtU2LnSlTvWoysOljm+4pn1OUMWbTpnxNDzjl4SoFcmKZ6WhyXIDM6oV3Aqt5zjRyFTFcRiZ8Etx0Nf62PwHpwBK+lxa0FwdVv/aj4a13vbtHS2MrDU7HquPkEtYILlTaGQKt7fljGWKgfJa9UKUg3xSzy+Wc2AuyjYBsg9igP/Q1b1fsJ+lzLsNdRJnAb/aDIXbbrFR/YfxIdo2lcCAwEAAaNPME0wDAYDVR0TAQH/BAIwADALBgNVHQ8EBAMCA9gwEQYJYIZIAYb4QgEBBAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMEBggrBgEFBQcDAjANBgkqhkiG9w0BAQsFAAOCAgEAtaZpEeckrtGhCHn/7TjipPsCgf5UAw6FSqaQALL1cQt6M+XkEqeZQEJHBfSHLhdJw/FziELA2Qc7hv7dv6M5muA0wiFTRdxNT5faD8Dh3SomOmOxcGG4RSX7Yxm3AohSU2ktbImZB1Ku8zszMfBGGBVuJM5tUzRaGO/8313T/GN5Bu/ficBaUKGMKLqPVCmhHmHLphP++rnq1W04hOhdZ1GwdfEMlPJJTxGzKevfesTX1kTAAOkvJ7efWm3+FHOosyTUZsBplAPX6v5lPs8dTjyOuPsqJNELXDamJ7+ALhwkvYTTjpINUkG8UZZ/gllu0T12CqC7z/dHckZTyevJ8IfZYOJOXa63GIABcrSB/vatzQHJ1f1MS+psMQIrVbLuv0S0n6IlGe17NI6Mzu8sUXku+pcICElqrfs7hoTvSpl33gDOgb/AH9/KQHv5izWs94C+taXeHd+ZhZxzlr6FLIJjzc7EP+a9x8ntJELUYgpLuehuGvMOJtJT/cOhnyZ79sGPq8LEsTma0Hse/mujtJNbN8ZlhnrGnIsMONvRUJm6LFpU5rPqG8zKJZliKJGBj/4zKNKx3jc8Jy5pMcaqnG0W5Q8QcYorTKMIsPBKlTVOF7x2E9kvRbQuVL36MmljSVOsK73gm4OZ6ORKM+K4wZKrOoz9uGvSzVyDIgytCvg="; +escuela_kemper_urgate_base64_key_fiel = "MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIAgEAAoIBAQACAggAMBQGCCqGSIb3DQMHBAgwggS9AgEAMASCBMh4EHl7aNSCaMDA1VlRoXCZ5UUmqErAbucRBAKNQXH8t0f2aSYkk74BKjNDtQ2u5LylXUf6MrOHJZV3J/U8IVU02MuKmvSb5kIvEKE3FZ/OSKoZC58sLQ/+m1joBEHn2QgMld3NsSRA6rv5Jv7Gyl8URb3XT6W7qr/nznufyuxyBSTj68+HgdJt/EeGp3Ud1Sk/1mY8PEt7tT86UjH+20/07ZVIhik6fQDFxELrm4jCJkTrQenqEykHVy/w5mJgrqwfS4SsGaNY9J5TUJNauyw193y/6C48SxwpTjA2GYtpxIUl9GNtXbUHUvsbVTZ06gnnCXUx3IOj+wFI9qUvPYl2DVSfV3iVAYsTXG/R5gOIw4k6s235gZEfJpoQZ4eq1LiYbKXqTjR9ntCEit0RyWirwt/2UcrRNgnXFU58xYUzY45noSR6QD8u05ZGsGT1Q3xesanSxCmsjrfh26a5EoH3voFhpI99/M2CyhwIm63ab2CKz8DMrhCPWxUgKDn2xZgW79zBgQ6qN0t9iqJ69guwG4rkWNqs/5vyYdK5PbJSt9KmeHZNYl1/wBMP4PDomA0iHiRQ2Jpc7tdedQXPid3DSIrk9XqMwB6SBThGOSzJ4Tkb78ybsHTjx3apF4ZHH6U66WzRbjggt2H0XmCbKLvNbAPqfA+vZzpIZK0LrsUGvG/wtoqFedZ/AV3wBvQTIRkwHImvFqoDd7fMCg8MLV3yJJGNcgcsI1RSV0EAJ5YafNHR9IPNl6pI7X6RDT7jobeyelyE5xepJklnNCIGLkYTGmXgXG0cYxtWYqvT7jQuEeseFSlxScxbvWI1vTzRpuis2YiZYteX4f+kGnMq6eOw+qtqXwB2bsw48ZGC9Ar34znqX/iOogQD+I2zWfllEdhQWdBF11m5YmwfnfQyoOOhx5s2bdqWv+XN6BMtRxfG7UOvB0p9ki1FdGOBD9BsSaF0etF8HPmy9zHPugCRQH50eVbqNnxPAnyaucPR/8ZEJXVuQ99KkhZRwr5brR9WP6ooDnws+yMe3q95hVVXWd4zDWPuh2JcT4ZzVzm9iwB7EZDYYCq/UXTg07Bb8hL0pw7UJFDelNMr4HOsarJZyUZferGQ62Ki7d/9xGOF4aL/lmni5okXU+fUKWkT7UhJCcxr3zeCnbe5FTNZgjIbieAMPtJiS30ow1EtxUsQF32f14lDdsi8twTT0GsILvZwNvGk/KZRlQU2iLm4hr44w6gmyg1J7m5ivL/MwbQ/DIf4djcOqckWBqtLCAp/HwSYjIXxrlPF1pICnzbfxTc6gLyTOsvoCngSnP2feOMTMMpFXVC1h0mfObxOhcbwxCC18AnNqfxh5rCvNxKYi0yqa5g9UFBOOKyxrhvP/eMdnPpd1DtvzP79zMQCQ+NlXt/XtmDRNUpPc7nPwhdPbRJTsetDqZK9NQf6cZ4/2cDIMd1/QomTmzKU9cjNXZgMcDSY7UYrN+n8CrETwu9dgNDuYjynh4XYlm0x9/Rx5r+77d1nzE6rkBH17/lH3fb8p4MWDlr6HIIgXxYCmeAhiws8tthJoD/nk1n6fvrxTFrEmEE1XG7JrNrDP1dLnjH+paHXy0thdQ8lpBMM4Wtqk0KVPn2SyaY3dslkdHg="; +password = "12345678a" + + +# ============================================================================ +# 1. FIRMA DE CARTA MANIFIESTO (ESCUELA KEMPER URGATE) +# ============================================================================ +def firma_carta_manifiesto(client: FiscalApiClient) -> None: + request = SignManifestRequest( + base64_cer=escuela_kemper_urgate_base64_cer_fiel, + base64_key=escuela_kemper_urgate_base64_key_fiel, + password=password, + ) + response = client.manifests.sign(request) + print("Response:", response) + + +# ============================================================================ +# FUNCIÓN PRINCIPAL +# ============================================================================ +def main() -> None: + client = FiscalApiClient(settings=settings) + try: + + firma_carta_manifiesto(client) + pass + except Exception as error: + print("Error:", error) + + +if __name__ == "__main__": + main() diff --git a/fiscalapi/__init__.py b/fiscalapi/__init__.py index c3eb3cf..c76947b 100644 --- a/fiscalapi/__init__.py +++ b/fiscalapi/__init__.py @@ -47,6 +47,26 @@ CartaPorteComplement, ) +# Modelos de Comercio Exterior +from .models.comercio_exterior_models import ( + ComercioExteriorEmisorDomicilio, + ComercioExteriorReceptorDomicilio, + ComercioExteriorDestinatarioDomicilio, + ComercioExteriorEmisor, + ComercioExteriorPropietario, + ComercioExteriorReceptor, + ComercioExteriorDestinatario, + ComercioExteriorMercanciaDescripcionEspecifica, + ComercioExteriorMercancia, + ComercioExteriorComplement, +) + +# Modelos de firma de manifiestos +from .models.manifest_models import ( + SignManifestRequest, + SignManifestResponse, +) + # Modelos de dominio from .models.fiscalapi_models import ( # Product models @@ -141,6 +161,7 @@ from .services.product_service import ProductService from .services.tax_file_service import TaxFileService from .services.stamp_service import StampService +from .services.manifest_service import ManifestService # Cliente principal from .services.fiscalapi_client import FiscalApiClient @@ -219,6 +240,17 @@ "ParteTransporte", "TipoFigura", "CartaPorteComplement", + # Comercio Exterior models + "ComercioExteriorEmisorDomicilio", + "ComercioExteriorReceptorDomicilio", + "ComercioExteriorDestinatarioDomicilio", + "ComercioExteriorEmisor", + "ComercioExteriorPropietario", + "ComercioExteriorReceptor", + "ComercioExteriorDestinatario", + "ComercioExteriorMercanciaDescripcionEspecifica", + "ComercioExteriorMercancia", + "ComercioExteriorComplement", # Invoice models "InvoiceResponse", "Invoice", @@ -251,6 +283,9 @@ "UserLookupDto", "StampTransaction", "StampTransactionParams", + # Manifest models + "SignManifestRequest", + "SignManifestResponse", # Servicios "BaseService", "ApiKeyService", @@ -265,6 +300,7 @@ "ProductService", "TaxFileService", "StampService", + "ManifestService", # Cliente principal "FiscalApiClient", ] diff --git a/fiscalapi/models/__init__.py b/fiscalapi/models/__init__.py index 1c66551..b6e2b61 100644 --- a/fiscalapi/models/__init__.py +++ b/fiscalapi/models/__init__.py @@ -33,6 +33,24 @@ TipoFigura, CartaPorteComplement, ) +from .comercio_exterior_models import ( + # Comercio Exterior models + ComercioExteriorEmisorDomicilio, + ComercioExteriorReceptorDomicilio, + ComercioExteriorDestinatarioDomicilio, + ComercioExteriorEmisor, + ComercioExteriorPropietario, + ComercioExteriorReceptor, + ComercioExteriorDestinatario, + ComercioExteriorMercanciaDescripcionEspecifica, + ComercioExteriorMercancia, + ComercioExteriorComplement, +) +from .manifest_models import ( + # Manifest models + SignManifestRequest, + SignManifestResponse, +) from .fiscalapi_models import ( # Product models ProductTax, @@ -137,6 +155,17 @@ "ParteTransporte", "TipoFigura", "CartaPorteComplement", + # Comercio Exterior models + "ComercioExteriorEmisorDomicilio", + "ComercioExteriorReceptorDomicilio", + "ComercioExteriorDestinatarioDomicilio", + "ComercioExteriorEmisor", + "ComercioExteriorPropietario", + "ComercioExteriorReceptor", + "ComercioExteriorDestinatario", + "ComercioExteriorMercanciaDescripcionEspecifica", + "ComercioExteriorMercancia", + "ComercioExteriorComplement", "BaseDto", "CatalogDto", "FiscalApiSettings", @@ -218,4 +247,7 @@ "UserLookupDto", "StampTransaction", "StampTransactionParams", + # Manifest models + "SignManifestRequest", + "SignManifestResponse", ] diff --git a/fiscalapi/models/comercio_exterior_models.py b/fiscalapi/models/comercio_exterior_models.py new file mode 100644 index 0000000..d28c622 --- /dev/null +++ b/fiscalapi/models/comercio_exterior_models.py @@ -0,0 +1,145 @@ +"""Modelos del complemento Comercio Exterior para CFDI 4.0.""" + +from decimal import Decimal +from typing import Optional + +from pydantic import BaseModel, ConfigDict, Field + + +# ===== Domicilios ===== + +class ComercioExteriorEmisorDomicilio(BaseModel): + """Domicilio del emisor del complemento Comercio Exterior.""" + calle: str = Field(default=..., alias="calle") + numero_exterior: Optional[str] = Field(default=None, alias="numeroExterior") + numero_interior: Optional[str] = Field(default=None, alias="numeroInterior") + colonia_id: Optional[str] = Field(default=None, alias="coloniaId") + localidad_id: Optional[str] = Field(default=None, alias="localidadId") + referencia: Optional[str] = Field(default=None, alias="referencia") + municipio_id: Optional[str] = Field(default=None, alias="municipioId") + estado_id: str = Field(default=..., alias="estadoId") + pais_id: str = Field(default=..., alias="paisId") + codigo_postal_id: str = Field(default=..., alias="codigoPostalId") + + model_config = ConfigDict(populate_by_name=True) + + +class ComercioExteriorReceptorDomicilio(BaseModel): + """Domicilio del receptor del complemento Comercio Exterior.""" + calle: str = Field(default=..., alias="calle") + numero_exterior: Optional[str] = Field(default=None, alias="numeroExterior") + numero_interior: Optional[str] = Field(default=None, alias="numeroInterior") + colonia: Optional[str] = Field(default=None, alias="colonia") + localidad: Optional[str] = Field(default=None, alias="localidad") + referencia: Optional[str] = Field(default=None, alias="referencia") + municipio: Optional[str] = Field(default=None, alias="municipio") + estado: str = Field(default=..., alias="estado") + pais_id: str = Field(default=..., alias="paisId") + codigo_postal: str = Field(default=..., alias="codigoPostal") + + model_config = ConfigDict(populate_by_name=True) + + +class ComercioExteriorDestinatarioDomicilio(BaseModel): + """Domicilio de un destinatario del complemento Comercio Exterior.""" + calle: str = Field(default=..., alias="calle") + numero_exterior: Optional[str] = Field(default=None, alias="numeroExterior") + numero_interior: Optional[str] = Field(default=None, alias="numeroInterior") + colonia: Optional[str] = Field(default=None, alias="colonia") + localidad: Optional[str] = Field(default=None, alias="localidad") + referencia: Optional[str] = Field(default=None, alias="referencia") + municipio: Optional[str] = Field(default=None, alias="municipio") + estado: str = Field(default=..., alias="estado") + pais_id: str = Field(default=..., alias="paisId") + codigo_postal: str = Field(default=..., alias="codigoPostal") + + model_config = ConfigDict(populate_by_name=True) + + +# ===== Emisor ===== + +class ComercioExteriorEmisor(BaseModel): + """Emisor del complemento Comercio Exterior.""" + curp: Optional[str] = Field(default=None, alias="curp") + domicilio: ComercioExteriorEmisorDomicilio = Field(default=..., alias="domicilio") + + model_config = ConfigDict(populate_by_name=True) + + +# ===== Propietario ===== + +class ComercioExteriorPropietario(BaseModel): + """Propietario de las mercancías exportadas.""" + num_reg_id_trib: str = Field(default=..., alias="numRegIdTrib") + residencia_fiscal_id: str = Field(default=..., alias="residenciaFiscalId") + + model_config = ConfigDict(populate_by_name=True) + + +# ===== Receptor ===== + +class ComercioExteriorReceptor(BaseModel): + """Receptor del complemento Comercio Exterior.""" + num_reg_id_trib: Optional[str] = Field(default=None, alias="numRegIdTrib") + domicilio: Optional[ComercioExteriorReceptorDomicilio] = Field(default=None, alias="domicilio") + + model_config = ConfigDict(populate_by_name=True) + + +# ===== Destinatario ===== + +class ComercioExteriorDestinatario(BaseModel): + """Destinatario de las mercancías exportadas.""" + num_reg_id_trib: Optional[str] = Field(default=None, alias="numRegIdTrib") + nombre: Optional[str] = Field(default=None, alias="nombre") + domicilios: list[ComercioExteriorDestinatarioDomicilio] = Field(default_factory=list, alias="domicilios") + + model_config = ConfigDict(populate_by_name=True) + + +# ===== Mercancías ===== + +class ComercioExteriorMercanciaDescripcionEspecifica(BaseModel): + """Descripción específica de una mercancía del complemento Comercio Exterior.""" + marca: str = Field(default=..., alias="marca") + modelo: Optional[str] = Field(default=None, alias="modelo") + sub_modelo: Optional[str] = Field(default=None, alias="subModelo") + numero_serie: Optional[str] = Field(default=None, alias="numeroSerie") + + model_config = ConfigDict(populate_by_name=True) + + +class ComercioExteriorMercancia(BaseModel): + """Mercancía del complemento Comercio Exterior.""" + no_identificacion: str = Field(default=..., alias="noIdentificacion") + fraccion_arancelaria_id: Optional[str] = Field(default=None, alias="fraccionArancelariaId") + cantidad_aduana: Optional[Decimal] = Field(default=None, alias="cantidadAduana") + unidad_aduana_id: Optional[str] = Field(default=None, alias="unidadAduanaId") + valor_unitario_aduana: Optional[Decimal] = Field(default=None, alias="valorUnitarioAduana") + valor_dolares: Decimal = Field(default=..., alias="valorDolares") + descripciones_especificas: Optional[list[ComercioExteriorMercanciaDescripcionEspecifica]] = Field( + default=None, alias="descripcionesEspecificas" + ) + + model_config = ConfigDict(populate_by_name=True, json_encoders={Decimal: str}) + + +# ===== Comercio Exterior ===== + +class ComercioExteriorComplement(BaseModel): + """Complemento Comercio Exterior para exportación de mercancías en CFDI 4.0.""" + motivo_traslado_id: Optional[str] = Field(default=None, alias="motivoTrasladoId") + clave_de_pedimento_id: str = Field(default=..., alias="claveDePedimentoId") + certificado_origen: int = Field(default=..., alias="certificadoOrigen") + num_certificado_origen: Optional[str] = Field(default=None, alias="numCertificadoOrigen") + numero_exportador_confiable: Optional[str] = Field(default=None, alias="numeroExportadorConfiable") + incoterm_id: Optional[str] = Field(default=None, alias="incotermId") + observaciones: Optional[str] = Field(default=None, alias="observaciones") + tipo_cambio_usd: Decimal = Field(default=..., alias="tipoCambioUSD") + emisor: Optional[ComercioExteriorEmisor] = Field(default=None, alias="emisor") + receptor: Optional[ComercioExteriorReceptor] = Field(default=None, alias="receptor") + propietarios: Optional[list[ComercioExteriorPropietario]] = Field(default=None, alias="propietarios") + destinatarios: Optional[list[ComercioExteriorDestinatario]] = Field(default=None, alias="destinatarios") + mercancias: list[ComercioExteriorMercancia] = Field(default_factory=list, alias="mercancias") + + model_config = ConfigDict(populate_by_name=True, json_encoders={Decimal: str}) diff --git a/fiscalapi/models/fiscalapi_models.py b/fiscalapi/models/fiscalapi_models.py index 6cf8a34..fd04926 100644 --- a/fiscalapi/models/fiscalapi_models.py +++ b/fiscalapi/models/fiscalapi_models.py @@ -2,6 +2,7 @@ from pydantic import BaseModel, ConfigDict, EmailStr, Field from fiscalapi.models.common_models import BaseDto, CatalogDto from fiscalapi.models.carta_porte_models import CartaPorteComplement +from fiscalapi.models.comercio_exterior_models import ComercioExteriorComplement from typing import Literal, Optional from datetime import datetime @@ -74,7 +75,9 @@ class Person(BaseDto): committed_balance: Optional[Decimal] = Field(default=None, alias="committedBalance", description="Saldo en tránsito.") tenant_id: Optional[str] = Field(default=None, alias="tenantId", description="ID del tenant al que pertenece el emisor.") tenant: Optional[CatalogDto] = Field(default=None, alias="tenant", description="Tenant expandido.") - + country_id: Optional[str] = Field(default=None, alias="countryId", description="Código del país de residencia para extranjeros (catálogo c_Pais).") + foreign_tin: Optional[str] = Field(default=None, alias="foreignTin", description="Número de identificación fiscal del extranjero.") + model_config = ConfigDict( populate_by_name=True, json_encoders={Decimal: str} @@ -218,6 +221,7 @@ class InvoiceRecipient(BaseDto): cfdi_use_code: Optional[str] = Field(default=None, alias="cfdiUseCode", description="Código del uso CFDI.") email: Optional[str] = Field(default=None, description="Correo electrónico del receptor.") foreign_country_code: Optional[str] = Field(default=None, alias="foreignCountryCode", description="Código del país de residencia para extranjeros.") + country_id: Optional[str] = Field(default=None, alias="countryId", description="Código del país de residencia para extranjeros (catálogo c_Pais).") foreign_tin: Optional[str] = Field(default=None, alias="foreignTin", description="Número de identificación fiscal del extranjero.") employee_data: Optional[InvoiceRecipientEmployeeData] = Field(default=None, alias="employeeData", description="Datos del empleado para CFDI de nómina.") @@ -529,6 +533,7 @@ class InvoiceComplement(BaseDto): payment: Optional[PaymentComplement] = Field(default=None, alias="payment", description="Complemento de pago.") payroll: Optional[PayrollComplement] = Field(default=None, alias="payroll", description="Complemento de nómina.") carta_porte: Optional[CartaPorteComplement] = Field(default=None, alias="cartaPorte", description="Complemento carta porte.") + comercio_exterior: Optional[ComercioExteriorComplement] = Field(default=None, alias="comercioExterior", description="Complemento comercio exterior.") model_config = ConfigDict(populate_by_name=True) diff --git a/fiscalapi/models/manifest_models.py b/fiscalapi/models/manifest_models.py new file mode 100644 index 0000000..731b952 --- /dev/null +++ b/fiscalapi/models/manifest_models.py @@ -0,0 +1,24 @@ +"""Modelos para firma de carta manifiesto (endpoint POST /api/v4/manifests).""" + +from typing import Optional +from pydantic import BaseModel, ConfigDict, Field + + +class SignManifestRequest(BaseModel): + """Petición para firmar una carta manifiesto con la FIEL del contribuyente.""" + + base64_cer: str = Field(default=..., alias="base64Cer", description="Certificado FIEL (.cer) en base64.") + base64_key: str = Field(default=..., alias="base64Key", description="Llave privada FIEL (.key) en base64.") + password: str = Field(default=..., alias="password", description="Contraseña de la llave privada FIEL.") + + model_config = ConfigDict(populate_by_name=True) + + +class SignManifestResponse(BaseModel): + """Respuesta con el PDF firmado de la carta manifiesto.""" + + base64_file: Optional[str] = Field(default=None, alias="base64File", description="PDF firmado en base64.") + file_name: Optional[str] = Field(default=None, alias="fileName", description="Nombre sugerido del archivo (RFC.pdf).") + file_extension: Optional[str] = Field(default=None, alias="fileExtension", description="Extensión del archivo (.pdf).") + + model_config = ConfigDict(populate_by_name=True) diff --git a/fiscalapi/services/__init__.py b/fiscalapi/services/__init__.py index 79c75c5..35128ed 100644 --- a/fiscalapi/services/__init__.py +++ b/fiscalapi/services/__init__.py @@ -12,6 +12,7 @@ from .invoice_service import InvoiceService from .people_service import PeopleService from .product_service import ProductService +from .manifest_service import ManifestService from .stamp_service import StampService from .tax_file_service import TaxFileService @@ -26,6 +27,7 @@ "EmployerService", "FiscalApiClient", "InvoiceService", + "ManifestService", "PeopleService", "ProductService", "StampService", diff --git a/fiscalapi/services/fiscalapi_client.py b/fiscalapi/services/fiscalapi_client.py index f0adcc3..9584933 100644 --- a/fiscalapi/services/fiscalapi_client.py +++ b/fiscalapi/services/fiscalapi_client.py @@ -9,11 +9,12 @@ from fiscalapi.services.download_rule_service import DownloadRuleService from fiscalapi.services.download_request_service import DownloadRequestService from fiscalapi.services.stamp_service import StampService +from fiscalapi.services.manifest_service import ManifestService class FiscalApiClient: - + def __init__(self, settings: FiscalApiSettings): self.products = ProductService(settings) self.people = PeopleService(settings) @@ -25,4 +26,5 @@ def __init__(self, settings: FiscalApiSettings): self.download_rules = DownloadRuleService(settings) self.download_requests = DownloadRequestService(settings) self.stamps = StampService(settings) + self.manifests = ManifestService(settings) self.settings = settings \ No newline at end of file diff --git a/fiscalapi/services/manifest_service.py b/fiscalapi/services/manifest_service.py new file mode 100644 index 0000000..aff01b4 --- /dev/null +++ b/fiscalapi/services/manifest_service.py @@ -0,0 +1,11 @@ +from fiscalapi.models.common_models import ApiResponse +from fiscalapi.models.manifest_models import SignManifestRequest, SignManifestResponse +from fiscalapi.services.base_service import BaseService + + +class ManifestService(BaseService): + """Servicio para firmar cartas manifiesto con la FIEL del contribuyente.""" + + def sign(self, request: SignManifestRequest) -> ApiResponse[SignManifestResponse]: + endpoint = "manifests" + return self.send_request("POST", endpoint, SignManifestResponse, payload=request) diff --git a/setup.py b/setup.py index 56cb417..e585fa3 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ import os from setuptools import setup, find_packages -VERSION = "4.0.384" +VERSION = "4.0.386" DESCRIPTION = "Genera facturas CFDI válidas ante el SAT consumiendo el API de https://www.fiscalapi.com"