Add basic shipping functionality

This commit is contained in:
Nathan Chapman 2022-04-23 15:17:11 -06:00
parent a41d75e713
commit c330acc5f6
6 changed files with 86 additions and 6 deletions

38
src/core/usps.py Normal file
View File

@ -0,0 +1,38 @@
import json
import requests
import xmltodict
from lxml import etree
from usps import USPSApi
class USPSApiWithRate(USPSApi):
urls = {
'tracking': 'TrackV2{test}&XML={xml}',
'label': 'eVS{test}&XML={xml}',
'validate': 'Verify&XML={xml}',
'rate': 'RateV4&XML={xml}',
}
def get_rate(self, *args, **kwargs):
return Rate(self, *args, **kwargs)
class Rate:
def __init__(self, usps, box, **kwargs):
xml = etree.Element('RateV4Request', {'USERID': usps.api_user_id})
etree.SubElement(xml, 'Revision').text = '2'
package = etree.SubElement(xml, 'Package', {'ID': '0'})
etree.SubElement(package, 'Service').text = box['service']
etree.SubElement(package, 'ZipOrigination').text = box['zip_origination']
etree.SubElement(package, 'ZipDestination').text = box['zip_destination']
etree.SubElement(package, 'Pounds').text = box['pounds']
etree.SubElement(package, 'Ounces').text = box['ounces']
etree.SubElement(package, 'Container').text = box['container']
etree.SubElement(package, 'Width').text = box['width']
etree.SubElement(package, 'Length').text = box['length']
etree.SubElement(package, 'Height').text = box['height']
etree.SubElement(package, 'Girth').text = box['girth']
etree.SubElement(package, 'Machinable').text = box['machinable']
self.result = usps.send_request('rate', xml)

View File

@ -20,6 +20,7 @@ CACHE_CONFIG = {
PAYPAL_CLIENT_ID = os.environ.get('PAYPAL_CLIENT_ID', '') PAYPAL_CLIENT_ID = os.environ.get('PAYPAL_CLIENT_ID', '')
PAYPAL_SECRET_ID = os.environ.get('PAYPAL_SECRET_ID', '') PAYPAL_SECRET_ID = os.environ.get('PAYPAL_SECRET_ID', '')
USPS_USER_ID = os.environ.get('USPS_USER_ID', '639NATHA3105')
ANYMAIL_CONFIG = { ANYMAIL_CONFIG = {
'MAILGUN_API_KEY': os.environ.get('MAILGUN_API_KEY', ''), 'MAILGUN_API_KEY': os.environ.get('MAILGUN_API_KEY', ''),

View File

@ -1,8 +1,8 @@
:root { :root {
--fg-color: #34201a; --fg-color: #34201a;
--fg-alt-color: #663a2d; --fg-alt-color: #663a2d;
--bg-color: #f5f5f5; --bg-color: #fffbf8;
--bg-alt-color: #c8a783; --bg-alt-color: #b07952;
--gray-color: #9d9d9d; --gray-color: #9d9d9d;
--yellow-color: #f8a911; --yellow-color: #f8a911;
--yellow-alt-color: #ffce6f; --yellow-alt-color: #ffce6f;

View File

@ -4,6 +4,7 @@ from django.conf import settings
from core.models import Product, OrderLine, Coupon from core.models import Product, OrderLine, Coupon
from .payments import CreateOrder from .payments import CreateOrder
from core.usps import USPSApiWithRate
from core import ( from core import (
DiscountValueType, DiscountValueType,
VoucherType, VoucherType,
@ -63,6 +64,39 @@ class Cart:
def __len__(self): def __len__(self):
return sum(item['quantity'] for item in self.cart.values()) return sum(item['quantity'] for item in self.cart.values())
def get_total_weight(self):
return sum([item['product'].weight.value * item['quantity'] for item in self.cart.values()])
def get_shipping_box(self):
logger.debug(len(self))
if len(self) > 6 and len(self) <= 10:
return "LG FLAT RATE BOX"
elif len(self) > 2 and len(self) <= 6:
return "REGIONALRATEBOXB"
elif len(self) <= 2:
return "REGIONALRATEBOXA"
else:
return "VARIABLE"
def get_shipping_cost(self):
box = {
'service': 'PRIORITY COMMERCIAL',
'zip_origination': '98368',
'zip_destination': f'{self.session.get("shipping_address")["postal_code"]}',
'pounds': '0',
'ounces': f'{self.get_total_weight()}',
'container': f'{self.get_shipping_box()}',
'width': '',
'length': '',
'height': '',
'girth': '',
'machinable': 'TRUE'
}
usps = USPSApiWithRate(settings.USPS_USER_ID, test=True)
validation = usps.get_rate(box)
return Decimal(validation.result['RateV4Response']['Package']['Postage']['CommercialRate'])
def get_total_price(self): def get_total_price(self):
return sum(Decimal(item['price']) * item['quantity'] for item in self.cart.values()) return sum(Decimal(item['price']) * item['quantity'] for item in self.cart.values())
@ -81,7 +115,7 @@ class Cart:
'total_price': f'{self.get_total_price_after_discount()}', 'total_price': f'{self.get_total_price_after_discount()}',
'item_total': f'{self.get_total_price()}', 'item_total': f'{self.get_total_price()}',
'discount': f'{self.get_discount()}', 'discount': f'{self.get_discount()}',
'shipping_price': '0', 'shipping_price': f'{self.get_shipping_cost()}',
'tax_total': '0', 'tax_total': '0',
'shipping_method': 'US POSTAL SERVICE', 'shipping_method': 'US POSTAL SERVICE',
'shipping_address': self.build_shipping_address(self.session.get('shipping_address')), 'shipping_address': self.build_shipping_address(self.session.get('shipping_address')),
@ -130,5 +164,8 @@ class Cart:
return round((self.coupon.discount_value / Decimal('100')) * self.get_total_price(), 2) return round((self.coupon.discount_value / Decimal('100')) * self.get_total_price(), 2)
return Decimal('0') return Decimal('0')
def get_total_price_after_discount(self): def get_subtotal_price_after_discount(self):
return round(self.get_total_price() - self.get_discount(), 2) return round(self.get_total_price() - self.get_discount(), 2)
def get_total_price_after_discount(self):
return round(self.get_total_price() - self.get_discount() + self.get_shipping_cost(), 2)

View File

@ -61,7 +61,7 @@
{% endif %} {% endif %}
<tr> <tr>
<th>Total</th> <th>Total</th>
<td><strong>${{cart.get_total_price_after_discount|floatformat:"2"}}</strong></td> <td><strong>${{cart.get_subtotal_price_after_discount|floatformat:"2"}}</strong></td>
</tr> </tr>
</table> </table>
</div> </div>

View File

@ -62,6 +62,10 @@
<td>{{cart.coupon.discount_value}} {{cart.coupon.get_discount_value_type_display}}</td> <td>{{cart.coupon.discount_value}} {{cart.coupon.get_discount_value_type_display}}</td>
</tr> </tr>
{% endif %} {% endif %}
<tr>
<td>Shipping</td>
<td>${{ cart.get_shipping_cost }}</small></td>
</tr>
<tr> <tr>
<th>Total</th> <th>Total</th>
<td><strong>${{cart.get_total_price_after_discount|floatformat:"2"}}</strong></td> <td><strong>${{cart.get_total_price_after_discount|floatformat:"2"}}</strong></td>