diff --git a/__openerp__.py b/__openerp__.py index 61cdf4e..c5a9482 100644 --- a/__openerp__.py +++ b/__openerp__.py @@ -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, diff --git a/data/data.xml b/data/data.xml new file mode 100644 index 0000000..4c7a1d8 --- /dev/null +++ b/data/data.xml @@ -0,0 +1,106 @@ + + + + + + + UPS Express (one day) + ups + one_day + pdf_a5 + + + UPS Express (one day early) + ups + one_day_early + pdf_a5 + + + UPS Expedited + ups + ups_expedited + pdf_a5 + + + UPS Returns + ups + returns + pdf_a5 + + + + + DPD (one day) + dpd + one_day + pdf_a5 + + + DPD (one day early) + dpd + one_day_early + pdf_a5 + + + DPD Standard + dpd + standard + pdf_a5 + + + DPD Returns + ups + returns + pdf_a5 + + + + + GLS Standard + gls + standard + pdf_a5 + + + GLS Express 12:00 + gls + gls_express_1200 + pdf_a5 + + + GLS Returns + gls + returns + pdf_a5 + + + + + DHL Paket + dhl + standard + pdf_a5 + + + DHL Retoure + dhl + returns + pdf_a5 + + + + + DHL Express (one day) + dhl_express + one_day + pdf_a5 + + + DHL Express (one day early) + dhl_express + one_day_early + pdf_a5 + + + + diff --git a/models/shipcloud.py b/models/shipcloud.py index ff4313e..490f43b 100644 --- a/models/shipcloud.py +++ b/models/shipcloud.py @@ -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, diff --git a/models/shipcloud_delivery_carrier.py b/models/shipcloud_delivery_carrier.py index 7fc54c0..c28dd04 100644 --- a/models/shipcloud_delivery_carrier.py +++ b/models/shipcloud_delivery_carrier.py @@ -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 diff --git a/models/shipcloud_shipping_service.py b/models/shipcloud_shipping_service.py index 7785a07..ee16b6a 100644 --- a/models/shipcloud_shipping_service.py +++ b/models/shipcloud_shipping_service.py @@ -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) diff --git a/views/sc_delivery_carrier.xml b/views/sc_delivery_carrier.xml new file mode 100644 index 0000000..cc01421 --- /dev/null +++ b/views/sc_delivery_carrier.xml @@ -0,0 +1,72 @@ + + + + delivery.stock.picking_withlabel.form.view + stock.picking + + + + + + + + + + shipping.sc.form + delivery.carrier + form + + + + + + + + + + + + delivery.carrier.sc.carrier_service.form + delivery.carrier.sc.carrier_service + form + +
+ + + + + + + + + + + + +
+
+
+ + delivery.carrier.sc.carrier_service.tree + delivery.carrier.sc.carrier_service + tree + + + + + + + + + + + SC Carrier Services + delivery.carrier.sc.carrier_service + form + tree,form + + + + +
+