Merge branch 'mig/15.0/delivery_easypost_hibou' into '15.0'

WIP: mig/15.0/delivery_easypost_hibou into 15.0

See merge request hibou-io/hibou-odoo/suite!1403
This commit is contained in:
Jared Kipe
2022-05-13 21:29:52 +00:00
6 changed files with 239 additions and 0 deletions

View File

@@ -0,0 +1 @@
from . import models

View File

@@ -0,0 +1,19 @@
{
'name': 'Hibou EasyPost Shipping',
'version': '15.0.1.0.0',
'category': 'Stock',
'author': "Hibou Corp.",
'license': 'AGPL-3',
'website': 'https://hibou.io/',
'depends': [
'delivery_easypost',
'delivery_hibou',
],
'data': [
'views/delivery_carrier_views.xml',
],
'demo': [
],
'installable': True,
'application': False,
}

View File

@@ -0,0 +1,2 @@
from . import delivery_carrier
from . import easypost_patch

View File

@@ -0,0 +1,74 @@
import requests
from odoo import fields, models, _
from odoo.exceptions import UserError
from odoo.addons.delivery_easypost.models.easypost_request import EasypostRequest
class DeliveryCarrier(models.Model):
_inherit = 'delivery.carrier'
easypost_return_method = fields.Selection([
('ep', 'EasyPost Return'),
('swap', 'Swap Addresses')
], string='Return Method', default='ep')
def easypost_send_shipping(self, pickings):
""" It creates an easypost order and buy it with the selected rate on
delivery method or cheapest rate if it is not set. It will use the
packages used with the put in pack functionality or a single package if
the user didn't use packages.
Once the order is purchased. It will post as message the tracking
links and the shipping labels.
"""
superself = self.sudo()
res = []
ep = EasypostRequest(self.sudo().easypost_production_api_key if self.prod_environment else self.sudo().easypost_test_api_key, self.log_xml)
for picking in pickings:
# Call Hibou delivery method to get picking type
if self.easypost_return_method == 'ep':
is_return = superself._classify_picking(picking) in ('in', 'dropship_in',)
result = ep.send_shipping(self, picking.partner_id, picking.picking_type_id.warehouse_id.partner_id,
picking=picking, is_return=is_return)
else:
shipper = superself.get_shipper_warehouse(picking=picking)
recipient = superself.get_recipient(picking=picking)
result = ep.send_shipping(self, recipient, shipper, picking=picking)
if result.get('error_message'):
raise UserError(result['error_message'])
rate = result.get('rate')
if rate['currency'] == picking.company_id.currency_id.name:
price = float(rate['rate'])
else:
quote_currency = self.env['res.currency'].search([('name', '=', rate['currency'])], limit=1)
price = quote_currency._convert(float(rate['rate']), picking.company_id.currency_id, self.env.company, fields.Date.today())
# return tracking information
carrier_tracking_link = ""
for track_number, tracker_url in result.get('track_shipments_url').items():
carrier_tracking_link += '<a href=' + tracker_url + '>' + track_number + '</a><br/>'
carrier_tracking_ref = ' + '.join(result.get('track_shipments_url').keys())
labels = []
for track_number, label_url in result.get('track_label_data').items():
label = requests.get(label_url)
labels.append(('LabelEasypost-%s.%s' % (track_number, self.easypost_label_file_type), label.content))
logmessage = _("Shipment created into Easypost<br/>"
"<b>Tracking Numbers:</b> %s<br/>") % (carrier_tracking_link)
if picking.sale_id:
for pick in picking.sale_id.picking_ids:
pick.message_post(body=logmessage, attachments=labels)
else:
picking.message_post(body=logmessage, attachments=labels)
shipping_data = {'exact_price': price,
'tracking_number': carrier_tracking_ref}
res = res + [shipping_data]
# store order reference on picking
picking.ep_order_ref = result.get('id')
if picking.carrier_id.return_label_on_delivery:
self.get_return_label(picking)
return res

View File

@@ -0,0 +1,130 @@
from odoo.tools.float_utils import float_round, float_is_zero
from odoo.addons.delivery_easypost.models.easypost_request import EasypostRequest
from odoo.tools.float_utils import float_round, float_is_zero, float_repr
# Patches to add customs lines during SO rating.
def _prepare_order_shipments(self, carrier, order):
""" Method used in order to estimate delivery
cost for a quotation. It estimates the price with
the default package defined on the carrier.
e.g: if the default package on carrier is a 10kg Fedex
box and the customer ships 35kg it will create a shipment
with 4 packages (3 with 10kg and the last with 5 kg.).
It ignores reality with dimension or the fact that items
can not be cut in multiple pieces in order to allocate them
in different packages. It also ignores customs info.
"""
# Max weight for carrier default package
max_weight = carrier._easypost_convert_weight(carrier.easypost_default_package_type_id.max_weight)
# Order weight
total_weight = carrier._easypost_convert_weight(order._get_estimated_weight())
# Create shipments
shipments = {}
if max_weight and total_weight > max_weight:
# Integer division for packages with maximal weight.
total_shipment = int(total_weight // max_weight)
# Remainder for last package.
last_shipment_weight = float_round(total_weight % max_weight, precision_digits=1)
for shp_id in range(0, total_shipment):
shipments.update(self._prepare_parcel(shp_id, carrier.easypost_default_package_type_id, max_weight, carrier.easypost_label_file_type))
shipments.update(self._customs_info_sale_order(shp_id, order.order_line))
shipments.update(self._options(shp_id, carrier))
if not float_is_zero(last_shipment_weight, precision_digits=1):
shipments.update(self._prepare_parcel(total_shipment, carrier.easypost_default_package_type_id, last_shipment_weight, carrier.easypost_label_file_type))
shipments.update(self._customs_info_sale_order(shp_id, order.order_line))
shipments.update(self._options(total_shipment, carrier))
else:
shipments.update(self._prepare_parcel(0, carrier.easypost_default_package_type_id, total_weight, carrier.easypost_label_file_type))
shipments.update(self._customs_info_sale_order(0, order.order_line))
shipments.update(self._options(0, carrier))
return shipments
def _customs_info_sale_order(self, shipment_id, lines):
""" generate a dict with customs info for each package... or each line
https://www.easypost.com/customs-guide.html
Currently general customs info for all packages are not generate.
For each shipment add a customs items by move line containing
- Product description (care it crash if bracket are used)
- Quantity for this product in the current package
- Product price
- Product price currency
- Total weight in ounces.
- Original country code(warehouse)
"""
customs_info = {}
customs_item_id = 0
for line in lines:
# skip service
if line.product_id.type not in ['product', 'consu']:
continue
unit_quantity = line.product_uom._compute_quantity(line.product_uom_qty, line.product_id.uom_id,
rounding_method='HALF-UP')
hs_code = line.product_id.hs_code or ''
price_unit = line.price_reduce_taxinc
customs_info.update({
'order[shipments][%d][customs_info][customs_items][%d][description]' % (
shipment_id, customs_item_id): line.product_id.name,
'order[shipments][%d][customs_info][customs_items][%d][quantity]' % (
shipment_id, customs_item_id): unit_quantity,
'order[shipments][%d][customs_info][customs_items][%d][value]' % (
shipment_id, customs_item_id): unit_quantity * price_unit,
'order[shipments][%d][customs_info][customs_items][%d][currency]' % (
shipment_id, customs_item_id): line.order_id.company_id.currency_id.name,
'order[shipments][%d][customs_info][customs_items][%d][weight]' % (shipment_id, customs_item_id):
line.env['delivery.carrier']._easypost_convert_weight(line.product_id.weight * unit_quantity),
'order[shipments][%d][customs_info][customs_items][%d][origin_country]' % (
shipment_id, customs_item_id): line.order_id.warehouse_id.partner_id.country_id.code,
'order[shipments][%d][customs_info][customs_items][%d][hs_tariff_number]' % (
shipment_id, customs_item_id): hs_code,
})
customs_item_id += 1
return customs_info
# Patch to prevent sending delivery customs for same-country-shipments
def _customs_info(self, shipment_id, lines):
""" generate a dict with customs info for each package.
https://www.easypost.com/customs-guide.html
Currently general customs info for all packages are not generate.
For each shipment add a customs items by move line containing
- Product description (care it crash if bracket are used)
- Quantity for this product in the current package
- Product price
- Product price currency
- Total weight in ounces.
- Original country code(warehouse)
"""
customs_info = {}
customs_item_id = 0
for line in lines:
# Customization to return early if same country
# only need early return if one line does this
if line.picking_id.picking_type_id.warehouse_id.partner_id.country_id.code == line.picking_id.partner_id.country_id.code:
return {}
# skip service
if line.product_id.type not in ['product', 'consu']:
continue
if line.picking_id.picking_type_code == 'incoming':
unit_quantity = line.product_uom_id._compute_quantity(line.product_qty, line.product_id.uom_id, rounding_method='HALF-UP')
else:
unit_quantity = line.product_uom_id._compute_quantity(line.qty_done, line.product_id.uom_id, rounding_method='HALF-UP')
rounded_qty = max(1, float_round(unit_quantity, precision_digits=0, rounding_method='HALF-UP'))
rounded_qty = float_repr(rounded_qty, precision_digits=0)
hs_code = line.product_id.hs_code or ''
customs_info.update({
'order[shipments][%d][customs_info][customs_items][%d][description]' % (shipment_id, customs_item_id): line.product_id.name,
'order[shipments][%d][customs_info][customs_items][%d][quantity]' % (shipment_id, customs_item_id): rounded_qty,
'order[shipments][%d][customs_info][customs_items][%d][value]' % (shipment_id, customs_item_id): line.sale_price,
'order[shipments][%d][customs_info][customs_items][%d][currency]' % (shipment_id, customs_item_id): line.picking_id.company_id.currency_id.name,
'order[shipments][%d][customs_info][customs_items][%d][weight]' % (shipment_id, customs_item_id): line.env['delivery.carrier']._easypost_convert_weight(line.product_id.weight * unit_quantity),
'order[shipments][%d][customs_info][customs_items][%d][origin_country]' % (shipment_id, customs_item_id): line.picking_id.picking_type_id.warehouse_id.partner_id.country_id.code,
'order[shipments][%d][customs_info][customs_items][%d][hs_tariff_number]' % (shipment_id, customs_item_id): hs_code,
})
customs_item_id += 1
return customs_info
EasypostRequest._prepare_order_shipments = _prepare_order_shipments
EasypostRequest._customs_info_sale_order = _customs_info_sale_order
EasypostRequest._customs_info = _customs_info

View File

@@ -0,0 +1,13 @@
<?xml version='1.0' encoding='utf-8'?>
<odoo>
<record id="view_delivery_carrier_form_inherit_delivery_easypost_hibou" model="ir.ui.view">
<field name="name">delivery.carrier.form.inherit.delivery.easypost.hibou</field>
<field name="model">delivery.carrier</field>
<field name="inherit_id" ref="delivery_easypost.view_delivery_carrier_form_inherit_delivery_easypost"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='easypost_label_file_type']" position="after">
<field name="easypost_return_method" attrs="{'required': [('delivery_type', '=', 'easypost')]}"/>
</xpath>
</field>
</record>
</odoo>