mirror of
https://gitlab.com/hibou-io/hibou-odoo/suite.git
synced 2025-01-20 12:37:31 +02:00
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:
1
delivery_easypost_hibou/__init__.py
Normal file
1
delivery_easypost_hibou/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from . import models
|
||||
19
delivery_easypost_hibou/__manifest__.py
Normal file
19
delivery_easypost_hibou/__manifest__.py
Normal 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,
|
||||
}
|
||||
2
delivery_easypost_hibou/models/__init__.py
Normal file
2
delivery_easypost_hibou/models/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
from . import delivery_carrier
|
||||
from . import easypost_patch
|
||||
74
delivery_easypost_hibou/models/delivery_carrier.py
Normal file
74
delivery_easypost_hibou/models/delivery_carrier.py
Normal 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
|
||||
130
delivery_easypost_hibou/models/easypost_patch.py
Normal file
130
delivery_easypost_hibou/models/easypost_patch.py
Normal 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
|
||||
13
delivery_easypost_hibou/views/delivery_carrier_views.xml
Normal file
13
delivery_easypost_hibou/views/delivery_carrier_views.xml
Normal 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>
|
||||
Reference in New Issue
Block a user