From f65169ebbc34429b1f98004157f80ba8e1f1a01a Mon Sep 17 00:00:00 2001 From: Nathan Chapman Date: Fri, 17 Nov 2023 22:31:21 -0700 Subject: [PATCH] Implement basic shipping with XPS Ship API --- core/shipping.py | 48 +++++++++++--------------------- core/xps.py | 68 +++++++++++++++++++++++++++++++++++++++++++++- storefront/cart.py | 46 ++++++++----------------------- 3 files changed, 95 insertions(+), 67 deletions(-) diff --git a/core/shipping.py b/core/shipping.py index 172a09f..60eff27 100644 --- a/core/shipping.py +++ b/core/shipping.py @@ -5,8 +5,7 @@ from django.db.models import Q from measurement.measures import Weight -from core.usps import USPSApi -from core.exceptions import USPSPostageError, ShippingAddressError +from core.xps import get_quote from core.models import ( ShippingRate, SiteSettings, @@ -43,42 +42,27 @@ def get_shipping_container_from_choices(choices): return SiteSettings.load().default_shipping_rate.container return choices[0].container +# - A 48 36 +# - B 80 48 60 72 64 80 96 +# - Small mailing box: 12 24 16 32 +# - Shoe box: 48 64 +# - Large flat rate box: 96 128 + + def get_shipping_cost(total_weight, postal_code): if not total_weight > Weight(lb=0): return Decimal('0.00') - container = get_shipping_container_from_choices( - get_shipping_container_choices_from_weight(total_weight) - ) + container = "box_a" + if total_weight <= Weight(lb=3): + container = "box_a" + elif total_weight <= Weight(lb=6): + container = "box_b" + else: + container = "large_flat_rate_box" - usps_rate_request = build_usps_rate_request( - str(total_weight.lb), container, str(postal_code) - ) - - usps = USPSApi(SiteSettings.load().usps_user_id) - - try: - validation = usps.get_rate(usps_rate_request) - except ConnectionError as e: - raise e( - 'Could not connect to USPS, try again.' - ) - - logger.info(validation.result) - try: - postage = dict( - validation.result['RateV4Response']['Package']['Postage'] - ) - except KeyError: - raise USPSPostageError( - 'Could not retrieve postage.' - ) - - if usps_rate_request['service'] == ShippingContainer.PRIORITY: - shipping_cost = Decimal(postage['Rate']) - elif usps_rate_request['service'] == ShippingContainer.PRIORITY_COMMERCIAL: - shipping_cost = Decimal(postage['CommercialRate']) + shipping_cost = get_quote(postal_code, total_weight, container) return shipping_cost diff --git a/core/xps.py b/core/xps.py index d8af082..416cffe 100644 --- a/core/xps.py +++ b/core/xps.py @@ -10,12 +10,78 @@ customer_id = "12375190" headers = {'Authorization': 'RSIS ' + api_key, 'Content-Type': 'application/json'} base_url = "https://xpsshipper.com/restapi/v1/customers/" + customer_id +def get_container(weight, name): + containers = { + "box_a": { + "type_code": "usps_custom_package", + "dim_unit": "in", + "pieces": { + "weight": weight, + "length": "10.125", + "width": "7.125", + "height": "5", + "insuranceAmount": None, + "declaredValue": None + } + }, + "box_b": { + "type_code": "usps_custom_package", + "dim_unit": "in", + "pieces": { + "weight": weight, + "length": "13", + "width": "11", + "height": "6", + "insuranceAmount": None, + "declaredValue": None + } + }, + "large_flat_rate_box": { + "type_code": "usps_large_flat_rate_box", + "dim_unit": None, + "pieces": { + "weight": weight, + "length": None, + "width": None, + "height": None, + "insuranceAmount": None, + "declaredValue": None + } + } + } + return containers[name] -def get_quote(weight): + +def get_quote(destination_zip, weight, container): + data = get_data(destination_zip, weight, container) resp = request("/quote", data) + return resp["totalAmount"] def request(url, data): r = requests.post(base_url + url, headers=headers, data=json.dumps(data)) return r.json() +def get_data(destination_zip, weight, container): + container = get_container(weight, container) + data = { + "carrierCode": "usps", + "serviceCode": "usps_priority", + "packageTypeCode": container["type_code"], + "signatureOptionCode": "NO_SIGNATURE_REQUIRED", + "sender": { + "country": "US", + "zip": "98368" + }, + "receiver": { + "country": "US", + "zip": destination_zip + }, + "residential": True, + "weightUnit": "lb", + "dimUnit": container["dim_unit"], + "currency": "USD", + "customsCurrency": "USD", + "pieces": [ container["pieces"] ] + } + return data diff --git a/storefront/cart.py b/storefront/cart.py index 28f0886..237ac07 100644 --- a/storefront/cart.py +++ b/storefront/cart.py @@ -14,8 +14,8 @@ from core.models import ( ProductCategory, Product, ProductVariant, OrderLine, Coupon, ShippingRate, SiteSettings ) -from core.usps import USPSApi -from core.exceptions import USPSPostageError, ShippingAddressError + +from core.xps import get_data, get_quote from core import ( DiscountValueType, VoucherType, @@ -259,42 +259,20 @@ class Cart: return Decimal('0.00') if len(self) > 0 and self.session.get('shipping_address'): - usps_rate_request = build_usps_rate_request( - str(self.total_weight.lb), - container, - str(self.session.get('shipping_address')['postal_code']) - ) + postal_code = str(self.session.get('shipping_address')['postal_code']) - usps = USPSApi(self.site_settings.usps_user_id) + container = "box_a" - try: - validation = usps.get_rate(usps_rate_request) - except ConnectionError as e: - raise e( - 'Could not connect to USPS, try again.' - ) + if self.total_weight <= Weight(lb=3): + container = "box_a" + elif self.total_weight <= Weight(lb=6): + container = "box_b" + else: + container = "large_flat_rate_box" - logger.info(validation.result) - try: - postage = dict( - validation.result['RateV4Response']['Package']['Postage'] - ) - except KeyError: - logger.warning(validation.result) - raise USPSPostageError( - 'Could not retrieve postage.' - ) + shipping_price = get_quote(postal_code, str(self.total_weight.lb), container) - if usps_rate_request['service'] == ShippingContainer.PRIORITY: - shipping_price = Decimal(postage['Rate']) - elif usps_rate_request['service'] == ShippingContainer.PRIORITY_COMMERCIAL: - shipping_price = Decimal(postage['CommercialRate']) - - return shipping_price - else: - raise ShippingAddressError( - 'Could not retrieve shipping address.' - ) + return Decimal(shipping_price) def create_order(self): params = self.build_order_params()