diff --git a/models/dp_delivery_carrier.py b/models/dp_delivery_carrier.py index 88c116f..ae8b477 100644 --- a/models/dp_delivery_carrier.py +++ b/models/dp_delivery_carrier.py @@ -3,6 +3,7 @@ import logging from openerp.exceptions import Warning import pycountry from inema import Internetmarke +from inema import WarenpostInt _logger = logging.getLogger(__name__) @@ -86,6 +87,141 @@ class DPDeliveryCarrier(models.Model): last = last, address = addr) + def conn_auth_wpi(self): + """Connect to the Warenpost International API""" + config = self._get_config() + partner_id = config['dp_partner_id'] + key = config['dp_key'] + key_phase = config['dp_key_phase'] + pk_user = config['dp_portokasse_user'] + pk_passwd = config['dp_portokasse_passwd'] + ekp = config['dp_wpi_ekp'] + use_sandbox = config['dp_wpi_sandbox'] + print(__name__) + print(partner_id, key, ekp, pk_user, pk_passwd, key_phase, use_sandbox) + wpi = WarenpostInt(partner_id, key, ekp, pk_user, pk_passwd, key_phase, use_sandbox) + wpi.get_token() + return wpi + + def build_wpi_addr(self, wpi, partner): + """Build a WarenpostInt.Address object from an Odoo partner object.""" + + def trim_phone(ph): + if not ph: + return None + if len(ph) <= 15: + return ph + ph = ph.replace('-','') + ph = ph.replace(' ','') + return ph + + wpi_addr_lines = [] + if partner.is_company: + wpi_name = partner.name + else: + if partner.parent_id.name: + wpi_name = partner.parent_id.name + if partner.name: + wpi_addr_lines.append(partner.name) + else: + wpi_name = partner.name + wpi_addr_lines.append(partner.street) + if partner.street2: + wpi_addr_lines.append(partner.street2) + wpi_phone = trim_phone(partner.phone) + wpi_fax = trim_phone(partner.fax) + wpi_state = partner.state_id.name if partner.state_id else None + return wpi.Address(wpi_name, wpi_addr_lines, partner.city, partner.country_id.code, + partner.zip, wpi_state, wpi_phone, wpi_fax, partner.email) + + def build_wpi_content_item(self, wpi, line): + """Build contentPiece from Odoo 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_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)) + 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 + if ptempl.x_sysmo_default_customs_code: + hts = ptempl.x_sysmo_default_customs_code + else: + raise Warning('Product %s has no HTS defined' % (ptempl.name)) + 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 + if line.procurement_id and line.procurement_id.sale_line_id: + price_unit = line.procurement_id.sale_line_id.price_unit + else: + raise Warning('Line has no procurement or procurement no sale order line?!?') + weight_g = weight * 1000 + line_value = q * price_unit + return wpi.build_content_item(weight_g, line_value, q, hts, orig, product.name) + + def build_wpi_content(self, wpi, picking): + """Build contentPieces from Odoo stock.picking.""" + content = [self.build_wpi_content_item(wpi, x) for x in picking.move_lines] + total = 0.0 + for i in content: + total += float(i['contentPieceValue']) + return (content, total) + + @api.one + def wpi_send_shipping(self, pickings): + config = self._get_config() + order = self.env['sale.order'].search([('name','=',pickings.origin)]) + recipient = pickings.partner_id + warehouse = pickings.picking_type_id.warehouse_id.partner_id + # determine weight and DP service/product + weight = self._get_weight(order, pickings) + service = self.get_service_by_class(recipient, weight, self.dp_service_class) + if not service: + raise Warning("Service not available for weight!") + # connect to API + wpi = self.conn_auth_wpi() + # build various data structures for the API + wpi_recipient = self.build_wpi_addr(wpi, recipient) + wpi_sender = self.build_wpi_addr(wpi, warehouse) + if self._country_code_outside_eu(recipient.country_id.code): + (wpi_content, total_value) = self.build_wpi_content(wpi, pickings) + else: + wpi_content = [] + total_value = 0 + wpi_item = wpi.build_item(service.code, wpi_sender, wpi_recipient, weight*1000, total_value, + 'EUR', customer_reference=pickings.name, contents=wpi_content) + # actually create the order + download the label + wpi_res = wpi.api_create_order([wpi_item], 'Max Mustermann') + wpi_res_item = wpi_res['shipments'][0]['items'][0] + png = wpi.api_get_item_label(wpi_res_item['id'], 'image/png') + # build result dict + awb = wpi_res['shipments'][0]['awb'] + voucher_id = wpi_res_item['voucherId'] + filename = 'WPI'+voucher_id+'.png' + tracking_nr = ' ' + if 'barcode' in wpi_res_item: + tracking_nr += wpi_res_item['barcode'] + result = { 'exact_price': service.cost_price, + 'weight': service.weight, + 'date_delivery': None, + 'tracking_number': tracking_nr, + 'voucher_id' : voucher_id, + 'order_id' : awb, + 'attachments': [(filename, png)]} + _logger.debug(result) + return result + def _get_eu_res_country_group(self): eu_group = self.env.ref("base.europe", raise_if_not_found=False) if not eu_group: @@ -99,11 +235,21 @@ class DPDeliveryCarrier(models.Model): else: eu_country_group = self._get_eu_res_country_group() country_id = self.env['res.country'].search([('code','=',country_code)]) - if country_id in eu_country_group.country_ids.ids: + if country_id.id in eu_country_group.country_ids.ids: return service_class.services_eu else: return service_class.services_intl + def _country_code_outside_eu(self, country_code): + """Is the specified two-digit country code outside the EU?""" + if country_code == 'DE': + return False + eu_country_group = self._get_eu_res_country_group() + country_id = self.env['res.country'].search([('code','=',country_code)]) + if country_id.id in eu_country_group.country_ids.ids: + return False + return True + # determine lowest-matching-max-weight service within same class def get_service_by_class(self, recipient, weight, service_class): services = self.get_services_by_country(service_class, recipient.country_id.code) @@ -126,6 +272,8 @@ class DPDeliveryCarrier(models.Model): @api.one def dp_send_shipping(self, pickings): + if self.dp_service_class.is_wpi: + return self.wpi_send_shipping(pickings)[0] config = self._get_config() order = self.env['sale.order'].search([('name','=',pickings.origin)]) recipient = pickings.partner_id diff --git a/models/dp_shipping_service.py b/models/dp_shipping_service.py index cef502c..e2cb3c8 100644 --- a/models/dp_shipping_service.py +++ b/models/dp_shipping_service.py @@ -10,6 +10,7 @@ class SMCShippingDp(models.Model): class SMCShippingDpClass(models.Model): _name = "delivery.carrier.dp.class" name = fields.Char(string="Name", required=1) + is_wpi = fields.Boolean("Warenpost International") # list of services in this class for national delivery services_natl = fields.Many2many(comodel_name = 'delivery.carrier.dp.service', relation = 'dp_class_natl_services_rel') diff --git a/models/res_config.py b/models/res_config.py index 081a98a..50d20ba 100755 --- a/models/res_config.py +++ b/models/res_config.py @@ -12,6 +12,8 @@ class website_config_settings(models.Model): dp_key_phase = fields.Char('Key Phase', required=1) dp_portokasse_user = fields.Char('Portokasse User', required=1) dp_portokasse_passwd = fields.Char('Portokasse Password', required=1) + dp_wpi_ekp = fields.Char('EKP Number', required=1) + dp_wpi_sandbox = fields.Boolean('Use WPI Sandbox environment') @api.model def get_default_dp_values(self, fields): @@ -31,4 +33,6 @@ class website_config_settings(models.Model): ir_values.set_default('delivery.carrier', 'dp_key_phase', config.dp_key_phase or '') ir_values.set_default('delivery.carrier', 'dp_portokasse_user', config.dp_portokasse_user or '') ir_values.set_default('delivery.carrier', 'dp_portokasse_passwd', config.dp_portokasse_passwd or '') + ir_values.set_default('delivery.carrier', 'dp_wpi_ekp', config.dp_wpi_ekp or '') + ir_values.set_default('delivery.carrier', 'dp_wpi_sandbox', config.dp_wpi_sandbox or True) return True diff --git a/views/dp_delivery_carrier.xml b/views/dp_delivery_carrier.xml index 5863e00..667091e 100644 --- a/views/dp_delivery_carrier.xml +++ b/views/dp_delivery_carrier.xml @@ -83,6 +83,7 @@
+ @@ -97,6 +98,7 @@ + diff --git a/views/res_config.xml b/views/res_config.xml index 9a13732..b7cb288 100644 --- a/views/res_config.xml +++ b/views/res_config.xml @@ -15,6 +15,10 @@ + + + +