2424import uuid
2525
2626
27+ def is_usmca_eligible (shipper_country : str , recipient_country : str ) -> bool :
28+ """Check if shipment is eligible for USMCA customs handling (US, CA, MX)."""
29+ USMCA_COUNTRIES = {"US" , "CA" , "MX" }
30+ return (
31+ (shipper_country in USMCA_COUNTRIES and recipient_country in USMCA_COUNTRIES ) and
32+ shipper_country != recipient_country
33+ )
34+
35+
2736def parse_shipment_response (
2837 _response : lib .Deserializable [dict ],
2938 settings : provider_utils .Settings ,
@@ -180,7 +189,9 @@ def shipment_request(
180189 )
181190
182191 is_intl = shipper .country_code != recipient .country_code
183- is_ca_to_us = shipper .country_code == "CA" and recipient .country_code == "US"
192+ is_usmca_route = is_usmca_eligible (shipper .country_code , recipient .country_code )
193+ use_usmca_option = options .freightcom_use_usmca .state if hasattr (options , 'freightcom_use_usmca' ) and options .freightcom_use_usmca .state is not None else True
194+ is_usmca = is_usmca_route and use_usmca_option
184195 customs = lib .to_customs_info (
185196 payload .customs ,
186197 shipper = payload .shipper ,
@@ -208,22 +219,22 @@ def shipment_request(
208219 ),
209220 )
210221
211- payment_method_id = settings .payment_method
212-
213- if not payment_method_id :
214- raise Exception ("No payment method found need to be set in config" )
215-
216- # Check if it's DDP (Delivered Duty Paid)
217222 is_ddp = (
218223 customs and (
219224 customs .incoterm == "DDP" or
220225 (customs .duty and customs .duty .paid_by == "sender" )
221226 )
222227 ) if customs else False
223228
224- # For DDP shipments with Net Terms, need credit card for customs/duties payment
229+ payment_method_id = settings .payment_method
230+
231+ if not payment_method_id :
232+ raise Exception ("No payment method found need to be set in config" )
233+
225234 customs_and_duties_payment_method_id = None
226- if is_ddp and settings .connection_config .payment_method_type .state == provider_utils .PaymentMethodType .net_terms .value :
235+ if settings .connection_config .payment_method_type .state == provider_utils .PaymentMethodType .credit_card .value :
236+ customs_and_duties_payment_method_id = payment_method_id
237+ elif settings .connection_config .customs_and_duties_payment_method .state :
227238 customs_and_duties_payment_method_id = settings .customs_and_duties_payment_method
228239
229240 request = freightcom_rest_req .ShipmentRequestType (
@@ -355,20 +366,23 @@ def shipment_request(
355366 value = str (int (item .value_amount * 100 ))
356367 ),
357368 description = item .description ,
358- fda_regulated = "no"
369+ fda_regulated = "no" ,
370+ cusma_included = True if is_usmca else None ,
371+ non_auto_parts = options .freightcom_non_auto_parts .state if hasattr (options , 'freightcom_non_auto_parts' ) and options .freightcom_non_auto_parts .state else None ,
359372 ) for item in customs .commodities
360373 ] if customs and customs .commodities else [],
361- request_guaranteed_customs_charges = options . request_guaranteed_customs_charges .state if hasattr ( options , 'request_guaranteed_customs_charges' ) else None
374+ request_guaranteed_customs_charges = settings . connection_config . request_guaranteed_customs_charges .state
362375 )
363- if is_ca_to_us and customs and any (customs .commodities )
376+ if is_usmca and customs and any (customs .commodities )
364377 else None
365378 ),
366379 ),
367380 customs_invoice = (
368381 freightcom_rest_req .CustomsInvoiceType (
369382 source = "details" ,
370383 broker = freightcom_rest_req .BrokerType (
371- use_carrier = True ,
384+ use_carrier = True ,
385+ usmca_number = options .freightcom_usmca_number .state if hasattr (options , 'freightcom_usmca_number' ) and options .freightcom_usmca_number .state else None ,
372386 ),
373387 details = freightcom_rest_req .CustomsInvoiceDetailsType (
374388 products = [
@@ -387,7 +401,9 @@ def shipment_request(
387401 value = str (int (item .value_amount * 100 ))
388402 ),
389403 description = item .description ,
390- fda_regulated = "no"
404+ fda_regulated = "no" ,
405+ cusma_included = True if is_usmca else None ,
406+ non_auto_parts = options .freightcom_non_auto_parts .state if hasattr (options , 'freightcom_non_auto_parts' ) and options .freightcom_non_auto_parts .state else None ,
391407 ) for item in customs .commodities
392408 ],
393409 tax_recipient = freightcom_rest_req .TaxRecipientType (
@@ -417,7 +433,23 @@ def shipment_request(
417433 )
418434 )
419435 )
420- if customs and customs .commodities
436+ if is_intl and customs and customs .commodities
437+ else None
438+ ),
439+ paperless_customs_documents = (
440+ [
441+ freightcom_rest_req .PaperlessCustomsDocumentType (
442+ type = "cusma-form" if doc .get ("doc_type" ) == "cusma-form" else (
443+ "other" if doc .get ("doc_type" ) == "certificate_of_origin" else "other"
444+ ),
445+ type_other_name = doc .get ("doc_type" ) if doc .get ("doc_type" ) not in ["cusma-form" ] else None ,
446+ file_name = doc .get ("doc_name" ) or "document.pdf" ,
447+ file_base64 = doc .get ("doc_file" ),
448+ )
449+ for doc in (options .doc_files .state or [])
450+ if doc .get ("doc_type" ) in ["cusma-form" , "certificate_of_origin" ] and doc .get ("doc_file" )
451+ ]
452+ if hasattr (options , 'doc_files' ) and options .doc_files .state and is_usmca
421453 else None
422454 ),
423455 #TODO: validate if we need to do pickup in the ship request
0 commit comments