allow specification of carrier+service

This commit is contained in:
Harald Welte 2021-02-14 21:16:23 +01:00
parent 5048d25df2
commit 997b465ea9
6 changed files with 243 additions and 22 deletions

View File

@ -9,7 +9,9 @@
'author': 'Harald Welte',
'depends': ['odoo_shipping_service_apps', 'shipment_packaging'],
'data': [
'views/sc_delivery_carrier.xml',
'views/res_config.xml',
'data/data.xml',
],
'installable': True,
'application': True,

106
data/data.xml Normal file
View File

@ -0,0 +1,106 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="1">
<!-- UPS -->
<record forcecreate="True" id="sc_cservice_ups_one_day" model="delivery.carrier.sc.carrier_service">
<field name="name">UPS Express (one day)</field>
<field name="carrier">ups</field>
<field name="service">one_day</field>
<field name="label_size">pdf_a5</field>
</record>
<record forcecreate="True" id="sc_cservice_ups_one_day_early" model="delivery.carrier.sc.carrier_service">
<field name="name">UPS Express (one day early)</field>
<field name="carrier">ups</field>
<field name="service">one_day_early</field>
<field name="label_size">pdf_a5</field>
</record>
<record forcecreate="True" id="sc_cservice_ups_expedited" model="delivery.carrier.sc.carrier_service">
<field name="name">UPS Expedited</field>
<field name="carrier">ups</field>
<field name="service">ups_expedited</field>
<field name="label_size">pdf_a5</field>
</record>
<record forcecreate="True" id="sc_cservice_ups_returns" model="delivery.carrier.sc.carrier_service">
<field name="name">UPS Returns</field>
<field name="carrier">ups</field>
<field name="service">returns</field>
<field name="label_size">pdf_a5</field>
</record>
<!-- DPD -->
<record forcecreate="True" id="sc_cservice_dpd_one_day" model="delivery.carrier.sc.carrier_service">
<field name="name">DPD (one day)</field>
<field name="carrier">dpd</field>
<field name="service">one_day</field>
<field name="label_size">pdf_a5</field>
</record>
<record forcecreate="True" id="sc_cservice_dpd_one_day_early" model="delivery.carrier.sc.carrier_service">
<field name="name">DPD (one day early)</field>
<field name="carrier">dpd</field>
<field name="service">one_day_early</field>
<field name="label_size">pdf_a5</field>
</record>
<record forcecreate="True" id="sc_cservice_dpd_standard" model="delivery.carrier.sc.carrier_service">
<field name="name">DPD Standard</field>
<field name="carrier">dpd</field>
<field name="service">standard</field>
<field name="label_size">pdf_a5</field>
</record>
<record forcecreate="True" id="sc_cservice_dpd_returns" model="delivery.carrier.sc.carrier_service">
<field name="name">DPD Returns</field>
<field name="carrier">ups</field>
<field name="service">returns</field>
<field name="label_size">pdf_a5</field>
</record>
<!-- GLS -->
<record forcecreate="True" id="sc_cservice_gls_standard" model="delivery.carrier.sc.carrier_service">
<field name="name">GLS Standard</field>
<field name="carrier">gls</field>
<field name="service">standard</field>
<field name="label_size">pdf_a5</field>
</record>
<record forcecreate="True" id="sc_cservice_gls_express_12" model="delivery.carrier.sc.carrier_service">
<field name="name">GLS Express 12:00</field>
<field name="carrier">gls</field>
<field name="service">gls_express_1200</field>
<field name="label_size">pdf_a5</field>
</record>
<record forcecreate="True" id="sc_cservice_dpd_returns" model="delivery.carrier.sc.carrier_service">
<field name="name">GLS Returns</field>
<field name="carrier">gls</field>
<field name="service">returns</field>
<field name="label_size">pdf_a5</field>
</record>
<!-- DHL Paket -->
<record forcecreate="True" id="sc_cservice_dhl_paket" model="delivery.carrier.sc.carrier_service">
<field name="name">DHL Paket</field>
<field name="carrier">dhl</field>
<field name="service">standard</field>
<field name="label_size">pdf_a5</field>
</record>
<record forcecreate="True" id="sc_cservice_dhl_returns" model="delivery.carrier.sc.carrier_service">
<field name="name">DHL Retoure</field>
<field name="carrier">dhl</field>
<field name="service">returns</field>
<field name="label_size">pdf_a5</field>
</record>
<!-- DHL Express -->
<record forcecreate="True" id="sc_cservice_dhle_one_day" model="delivery.carrier.sc.carrier_service">
<field name="name">DHL Express (one day)</field>
<field name="carrier">dhl_express</field>
<field name="service">one_day</field>
<field name="label_size">pdf_a5</field>
</record>
<record forcecreate="True" id="sc_cservice_dhle_one_day_early" model="delivery.carrier.sc.carrier_service">
<field name="name">DHL Express (one day early)</field>
<field name="carrier">dhl_express</field>
<field name="service">one_day_early</field>
<field name="label_size">pdf_a5</field>
</record>
</data>
</openerp>

View File

@ -96,6 +96,16 @@ class api(object):
sh = shipment.copy()
sh['create_shipping_label'] = gen_label
res = self._transport.rest_post('/shipments', sh, sandbox)
# {u'label_url': u'https://shipping-labels.shipcloud.io/shipments/a948e8c2/e3fb26be59/label/shipping_label_e3fb26be59.pdf', u'price': 0.0, u'id': u'e3fb26be59a68acd04d565dda027efd415ca8117', u'tracking_url': u'https://track.shipcloud.io/e3fb26be59a68acd04d565dda027efd415ca8117', u'carrier_tracking_no': u'1ZV306W00493609016'}
if 'label_url' in res:
r = requests.get(res['label_url'], stream=True)
if r.ok:
res['label_bin'] = r.content
return res
def delete_shipment(self, shipment_id):
sandbox = True if self._transport._auth_sandbox else False
res = self._transport.rest_delete('/shipments/%s' % shipment_id, None, sandbox)
return res
@ -151,18 +161,18 @@ def gen_package(width_cm, length_cm, height_cm, weight_kgs, value=None, currency
return package
def gen_shipment(from_addr, to_addr, pkg, ref, descr=None, customs_decl=None, incoterm='dap'):
def gen_shipment(from_addr, to_addr, pkg, ref, carrier='ups', service='one_day', label_fmt='pdf_a5', descr=None, customs_decl=None, incoterm='dap'):
"""Generate a dict for a shipment in accordance with
https://developers.shipcloud.io/reference/shipments_request_schema.html"""
shipment = {
'from': from_addr,
'to': to_addr,
'carrier': 'ups',
'service': 'one_day',
'carrier': carrier,
'service': service,
'package' : pkg,
'reference_number': ref,
'label': {
'format': 'pdf_a5',
'format': label_fmt,
},
'notification_mail': 'hwelte@sysmocom.de',
'incoterm': incoterm,

View File

@ -78,7 +78,7 @@ class SCDeliveryCarrier(models.Model):
"""Convert an Odoo stock.picking or sale.order into a shipcloud package"""
pkg = {}
pkg['type'] = 'parcel'
pkg['weight'] = self._get_weight(order, picking)
pkg['weight'] = self._get_weight_with_tare(order, picking)
if picking:
pkg['length'] = picking.packaging_length
pkg['width'] = picking.packaging_width
@ -94,14 +94,19 @@ class SCDeliveryCarrier(models.Model):
def build_sc_customs_item(self, line):
"""Generate a shipcloud customs_item from a stock.move (line of a picking)"""
product_uom_obj = self.env['product.uom']
q = product_uom_obj._compute_qty_obj(self._get_default_uom(), line.product_oum_qty, self.uom_id)
q = product_uom_obj._compute_qty_obj(self._get_default_uom(), line.product_uom_qty, self.uom_id)
product = line.product_id
if product:
if product.x_sysmo_customs_code:
hts = product.x_sysmo_customs_code
else:
raise Warning('Product Variant %s has no HTS defined' % (product.name))
orig = product.x_country_of_origin
if product.x_country_of_origin:
orig = product.x_country_of_origin.code
elif line.product_tmpl_id and line.product_tmpl_id.x_country_of_origin:
orig = line.product_tmpl_id.x_country_of_origin.code
else:
raise Warning('Product Variant %s has no Country of Origin defined' % (product.name))
weight = product.weight
elif line.product_tmpl_id:
ptmpl = line.product_tmpl_id
@ -109,7 +114,10 @@ class SCDeliveryCarrier(models.Model):
hts = ptempl.x_sysmo_default_customs_code
else:
raise Warning('Product %s has no HTS defined' % (ptempl.name))
orig = ptempl.x_default_country_of_origin
if ptempl.x_country_of_origin:
orig = ptempl.x_country_of_origin.code
else:
raise Warning('Product %s has no Country of Origin defined' % (ptempl.name))
weight = ptempl.weight
res = {
'origin_country': orig,
@ -119,9 +127,10 @@ class SCDeliveryCarrier(models.Model):
'value_amount': line.price_unit,
'net_weight': weight,
}
return res
def build_sc_customs_decl(self, picking):
items = [build_sc_customs_item(x) for x in picking.move_lines]
items = [self.build_sc_customs_item(x) for x in picking.move_lines]
total = 0.0
for i in items:
total += i['value_amount']
@ -146,13 +155,16 @@ class SCDeliveryCarrier(models.Model):
"""Obtain a shipping quote for the given sale.order"""
recipient = order.partner_shipping_id if order.partner_shipping_id else order.partner_id
warehouse = order.warehouse_id.partner_id
carrier_service = self.sudo().sc_carrier_service
# build individual sub-objects of the shipment
from_addr = self.build_sc_addr(warehouse)
to_addr = self.build_sc_addr(recipient)
pkg = self.build_sc_pkg(order=order)
# build the actual shipment object
shp = shipcloud.gen_shipment(from_addr, to_addr, pkg, order.name)
shp = shipcloud.gen_shipment(from_addr, to_addr, pkg, order.name,
carrier=carrier_service.carrier, service=carrier_service.service,
label_fmt=carrier_service.label_size)
# convert shipment to quote object
api = self._shipcloud_api()
try:
@ -162,43 +174,53 @@ class SCDeliveryCarrier(models.Model):
# { "shipment_quote": { "price": 42.12 } }
return result['shipment_quote']['price']
@api.one
def sc_send_shipping(self, pickings):
"""Generate a shipping label from the given stock.picking"""
order = self.env['sale.order'].search([('name','=',pickings.origin)])
recipient = pickings.partner_id
warehouse = pickings.picking_type_id.warehouse_id.partner_id
carrier_service = self.sudo().sc_carrier_service
# build individual sub-objects of the shipment
from_addr = self.build_sc_addr(warehouse)
to_addr = self.build_sc_addr(recipient)
pkg = self.build_sc_pkg(pickings=pickings)
pkg = self.build_sc_pkg(picking=pickings)
customs = self.build_sc_customs_decl(pickings)
# build the actual shipment object
shp = shipcloud.gen_shipment(from_addr, to_addr, pkg, picking.name, customs_decl=customs)
shp = shipcloud.gen_shipment(from_addr, to_addr, pkg, pickings.name, customs_decl=customs,
carrier=carrier_service.carrier, service=carrier_service.service,
label_fmt=carrier_service.label_size)
api = self._shipcloud_api()
try:
result = api.create_shipment(shp)
#print("SHP: %s" % shp)
result = api.create_shipment(shp, gen_label=True)
#print("RES: %s" % result)
except shipcloud.ApiError as err:
raise Warning(err)
# result = ["id", "carrier_tracking_no", "tracking_url", "label_url", "price"]
self.update({'sc_shipment_id': result['id'],
'sc_tracking_url': result['tracking_url']})
# {u'label_url': u'https://shipping-labels.shipcloud.io/shipments/a948e8c2/e3fb26be59/label/shipping_label_e3fb26be59.pdf', u'price': 0.0, u'id': u'e3fb26be59a68acd04d565dda027efd415ca8117', u'tracking_url': u'https://track.shipcloud.io/e3fb26be59a68acd04d565dda027efd415ca8117', u'carrier_tracking_no': u'1ZV306W00493609016'}
filename = '%s.pdf' % result['carrier_tracking_no']
pickings.update({'sc_shipment_id': result['id'],
'sc_tracking_url': result['tracking_url']})
# TODO: download label from label_url so it can be returned as attachment
res = {'exact_price': result['price'],
'weight': pkg['weight'],
'tracking_number': result['carrier_tracking_no'],
'attachments': [(filename, label.pdf_bin)]}
'date_delivery': None,
'tracking_number': ' ' + result['carrier_tracking_no'],
'attachments': [(filename, result['label_bin'])]}
return res
def sc_cancel_shipment(self, pickings):
@api.one
def sc_cancel_shipment(self, picking):
"""Cancel a shipping label"""
# TODO: use sc_shipment_id to issue a cancel request in the API
# DELETE /v1/shipments/:id -> 204 on success
api = self._shipcloud_api()
api.delete_shipment(picking.sc_shipment_id)
@api.one
def sc_get_tracking_link(self, pickings):
"""Return a tracking link for the given picking"""
return pickings.sc_tracking_url

View File

@ -4,10 +4,19 @@ from openerp import api, fields, models
class SMCShippingShipcloud(models.Model):
_inherit = 'delivery.carrier'
delivery_type = fields.Selection(selection_add=[('sc', 'shipcloud')])
sc_carrier_service = fields.Many2one(comodel_name='delivery.carrier.sc.carrier_service',
string='Shipcloud Carrier Service')
# extend stock.picking with fields related to shipcloud
class SMCStockPickingShipclodu(models.Model):
_inherit = 'stock.picking'
sc_shipment_id = fields.Char(string='shipcloud shipment ID')
sc_tracking_url = fields.Char(string='shipcloud tracking URL')
class SMCShippingScCarrierService(models.Model):
_name = 'delivery.carrier.sc.carrier_service'
name = fields.Char(string="Name", required=1)
carrier = fields.Char(string="shipcloud Carrier", required=1)
service = fields.Char(string="shipcloud Service", required=1)
label_size = fields.Char(string="shipcloud Label Size", required=1)

View File

@ -0,0 +1,72 @@
<openerp>
<data>
<record id="view_picking_withlabel_form_view_sc" model="ir.ui.view">
<field name="name">delivery.stock.picking_withlabel.form.view</field>
<field name="model">stock.picking</field>
<field name="inherit_id" ref="odoo_shipping_service_apps.view_picking_withlabel_form_view"/>
<field name="arch" type="xml">
<xpath expr="//label[@for='carrier_tracking_ref']" position="before">
<field name="sc_shipment_id" attrs="{'invisible':[('delivery_type', '!=', 'sc')]}"/>
</xpath>
</field>
</record>
<record id="shipping_sc_form" model="ir.ui.view">
<field name="name">shipping.sc.form</field>
<field name="model">delivery.carrier</field>
<field name="type">form</field>
<field name="inherit_id" ref="odoo_shipping_service_apps.view_delivery_carrier_form_inherit_wk_shipping"/>
<field name="arch" type="xml">
<xpath expr="//group[@name='Delivery Setting']" col='2' position="after">
<group string="SC Shipping Information" attrs="{'invisible':[('delivery_type', '!=', 'sc')]}">
<field name="sc_carrier_service" attrs="{'required':[('delivery_type', '==', 'sc')]}"/>
</group>
</xpath>
</field>
</record>
<record id="delivery_carrier_sc_service_form" model="ir.ui.view">
<field name="name">delivery.carrier.sc.carrier_service.form</field>
<field name="model">delivery.carrier.sc.carrier_service</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form>
<sheet>
<group>
<field name="name"/>
</group>
<group>
<field name="carrier"/>
<field name="service"/>
</group>
<group>
<field name="label_size"/>
</group>
</sheet>
</form>
</field>
</record>
<record id="delivery_carrier_sc_service_tree" model="ir.ui.view">
<field name="name">delivery.carrier.sc.carrier_service.tree</field>
<field name="model">delivery.carrier.sc.carrier_service</field>
<field name="type">tree</field>
<field name="arch" type="xml">
<tree string="package">
<field name="name"/>
<field name="carrier"/>
<field name="service"/>
<field name="label_size"/>
</tree>
</field>
</record>
<record model="ir.actions.act_window" id="action_delivery_carrier_sc_service">
<field name="name">SC Carrier Services</field>
<field name="res_model">delivery.carrier.sc.carrier_service</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="delivery_carrier_sc_service_tree"/>
</record>
<menuitem id="menu_shipping_sc_service" parent="delivery.menu_delivery" string="SC Carrier Service" action="action_delivery_carrier_sc_service"/>
</data>
</openerp>