diff --git a/delivery_gls_nl/__init__.py b/delivery_gls_nl/__init__.py deleted file mode 100644 index 09434554..00000000 --- a/delivery_gls_nl/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details. - -from . import models diff --git a/delivery_gls_nl/__manifest__.py b/delivery_gls_nl/__manifest__.py deleted file mode 100644 index a917e826..00000000 --- a/delivery_gls_nl/__manifest__.py +++ /dev/null @@ -1,28 +0,0 @@ -# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details. - -{ - 'name': 'GLS Netherlands Shipping', - 'summary': 'Create and print your shipping labels with GLS from the Netherlands.', - 'version': '15.0.1.0.0', - 'author': "Hibou Corp.", - 'category': 'Warehouse', - 'license': 'LGPL-3', - 'images': [], - 'website': "https://hibou.io", - 'description': """ -GLS Netherlands Shipping -======================== - -Create and print your shipping labels with GLS from the Netherlands. - -""", - 'depends': [ - 'delivery_hibou', - ], - 'demo': [], - 'data': [ - 'views/delivery_gls_nl_view.xml', - ], - 'auto_install': False, - 'installable': True, -} diff --git a/delivery_gls_nl/models/__init__.py b/delivery_gls_nl/models/__init__.py deleted file mode 100644 index 3da2e91f..00000000 --- a/delivery_gls_nl/models/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details. - -from . import delivery_gls_nl diff --git a/delivery_gls_nl/models/delivery_gls_nl.py b/delivery_gls_nl/models/delivery_gls_nl.py deleted file mode 100644 index fffc7c6c..00000000 --- a/delivery_gls_nl/models/delivery_gls_nl.py +++ /dev/null @@ -1,311 +0,0 @@ -# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details. - -from odoo import api, fields, models, _ -from odoo.exceptions import UserError, ValidationError -from .gls_nl_request import GLSNLRequest -from requests import HTTPError -from base64 import decodebytes, b64decode -from csv import reader as csv_reader - - -class PackageType(models.Model): - _inherit = 'stock.package.type' - - package_carrier_type = fields.Selection(selection_add=[('gls_nl', 'GLS Netherlands')]) - - -class ProviderGLSNL(models.Model): - _inherit = 'delivery.carrier' - - GLS_NL_SOFTWARE_NAME = 'Odoo' - GLS_NL_SOFTWARE_VER = '15.0' - GLS_NL_COUNTRY_NOT_FOUND = 'GLS_NL_COUNTRY_NOT_FOUND' - - delivery_type = fields.Selection(selection_add=[('gls_nl', 'GLS Netherlands')], - ondelete={'gls_nl': lambda recs: recs.write({'delivery_type': 'fixed', 'fixed_price': 0})}) - - gls_nl_username = fields.Char(string='GLS NL Username', groups='base.group_system') - gls_nl_password = fields.Char(string='GLS NL Password', groups='base.group_system') - gls_nl_labeltype = fields.Selection([ - ('zpl', 'ZPL'), - ('pdf', 'PDF'), - ], string='GLS NL Label Type') - gls_nl_express = fields.Selection([ - ('t9', 'Delivery before 09:00 on weekdays'), - ('t12', 'Delivery before 12:00 on weekdays'), - ('t17', 'Delivery before 17:00 on weekdays'), - ('s9', 'Delivery before 09:00 on Saturday'), - ('s12', 'Delivery before 12:00 on Saturday'), - ('s17', 'Delivery before 17:00 on Saturday'), - ], string='GLS NL Express', help='Express service tier (leave blank for regular)') - gls_nl_rate_id = fields.Many2one('ir.attachment', string='GLS NL Rates') - - def button_gls_nl_test_rates(self): - self.ensure_one() - if not self.gls_nl_rate_id: - raise UserError(_('No GLS NL Rate file is attached.')) - rate_data = self._gls_nl_process_rates() - weight_col_count = len(rate_data['w']) - row_count = len(rate_data['r']) - country_col = rate_data['c'] - country_model = self.env['res.country'] - for row in rate_data['r']: - country = country_model.search([('code', '=', row[country_col])], limit=1) - if not country: - raise ValidationError(_('Could not locate country by code: "%s" for row: %s') % (row[country_col], row)) - for w, i in rate_data['w'].items(): - try: - cost = float(row[i]) - except ValueError: - raise ValidationError(_('Could not process cost for row: %s') % (row, )) - raise ValidationError(_('Looks good! %s weights, %s countries located.') % (weight_col_count, row_count)) - - def _gls_nl_process_rates(self): - """ - 'w' key will be weights to row index map - 'c' key will be the country code index - 'r' key will be rows from the original that can use indexes above - :return: - """ - self = self.sudo() - datab = decodebytes(self.gls_nl_rate_id.datas) - csv_data = datab.decode() - csv_data = csv_data.replace('\r', '') - csv_lines = csv_data.splitlines() - header = [csv_lines[0]] - body = csv_lines[1:] - data = {'w': {}, 'r': []} - for row in csv_reader(header): - for i, col in enumerate(row): - if col == 'Country': - data['c'] = i - else: - try: - weight = float(col) - data['w'][weight] = i - except ValueError: - pass - if 'c' not in data: - raise ValidationError(_('Could not locate "Country" column.')) - if not data['w']: - raise ValidationError(_('Could not locate any weight columns.')) - for row in csv_reader(body): - data['r'].append(row) - return data - - def _gls_nl_rate(self, country_code, weight): - if weight < 0.0: - return 0.0 - rate_data = self._gls_nl_process_rates() - country_col = rate_data['c'] - rate = None - country_found = False - for row in rate_data['r']: - if row[country_col] == country_code: - country_found = True - for w, i in rate_data['w'].items(): - if weight <= w: - try: - rate = float(row[i]) - break - except ValueError: - pass - else: - # our w, i will be the last weight and rate. - try: - # Return Max rate + remaining weight rated - return float(row[i]) + self._gls_nl_rate(country_code, weight-w) - except ValueError: - pass - break - if rate is None and not country_found: - return self.GLS_NL_COUNTRY_NOT_FOUND - return rate - - def gls_nl_rate_shipment(self, order): - recipient = self.get_recipient(order=order) - rate = None - dest_country = recipient.country_id.code - est_weight_value = sum([(line.product_id.weight * line.product_uom_qty) for line in order.order_line]) or 0.0 - if dest_country: - rate = self._gls_nl_rate(dest_country, est_weight_value) - - # Handle errors and rate conversions. - error_message = None - if not dest_country or rate == self.GLS_NL_COUNTRY_NOT_FOUND: - error_message = _('Destination country not found: "%s"') % (dest_country, ) - if rate is None or error_message: - if not error_message: - error_message = _('Rate not found for weight: "%s"') % (est_weight_value, ) - return {'success': False, - 'price': 0.0, - 'error_message': error_message, - 'warning_message': False} - - euro_currency = self.env['res.currency'].search([('name', '=', 'EUR')], limit=1) - if euro_currency and order.currency_id and euro_currency != order.currency_id: - rate = euro_currency._convert(rate, - order.currency_id, - order.company_id, - order.date_order or fields.Date.today()) - - return {'success': True, - 'price': rate, - 'error_message': False, - 'warning_message': False} - - def _get_gls_nl_service(self): - return GLSNLRequest(self.prod_environment) - - def _gls_nl_make_address(self, partner): - # Addresses look like - # { - # 'name1': '', - # 'name2': '', - # 'name3': '', - # 'street': '', - # 'houseNo': '', - # 'houseNoExt': '', - # 'zipCode': '', - # 'city': '', - # 'countrycode': '', - # 'contact': '', - # 'phone': '', - # 'email': '', - # } - address = {} - pieces = partner.street.split(' ') - street = ' '.join(pieces[:-1]).strip(' ,') - house = pieces[-1] - address['name1'] = partner.name - address['street'] = street - address['houseNo'] = house - if partner.street2: - address['houseNoExt'] = partner.street2 - address['zipCode'] = partner.zip - address['city'] = partner.city - address['countrycode'] = partner.country_id.code - if partner.phone: - address['phone'] = partner.phone - if partner.email: - address['email'] = partner.email - return address - - def gls_nl_send_shipping(self, pickings): - res = [] - sudoself = self.sudo() - service = sudoself._get_gls_nl_service() - - for picking in pickings: - #company = self.get_shipper_company(picking=picking) # Requester not needed currently - from_ = self.get_shipper_warehouse(picking=picking) - to = self.get_recipient(picking=picking) - total_rate = 0.0 - - request_body = { - 'labelType': sudoself.gls_nl_labeltype, - 'username': sudoself.gls_nl_username, - 'password': sudoself.gls_nl_password, - 'shiptype': 'p', # note not shipType, 'f' for Freight - 'trackingLinkType': 's', - # 'customerNo': '', # needed if there are more 'customer numbers' attached to a single MyGLS API Account - 'reference': picking.name, - 'addresses': { - 'pickupAddress': self._gls_nl_make_address(from_), - 'deliveryAddress': self._gls_nl_make_address(to), - #'requesterAddress': {}, # Not needed currently - }, - 'units': [], - 'services': {}, - 'shippingDate': fields.Date.to_string(fields.Date.today()), - 'shippingSystemName': self.GLS_NL_SOFTWARE_NAME, - 'shippingSystemVersion': self.GLS_NL_SOFTWARE_VER, - } - - if sudoself.gls_nl_express: - request_body['services']['expressService'] = sudoself.gls_nl_express - - # Build out units - # Units look like: - # { - # 'unitId': 'A', - # 'unitType': '', # only for freight - # 'weight': 0.0, - # 'additionalInfo1': '', - # 'additionalInfo2': '', - # } - if picking.package_ids: - for package in picking.package_ids: - converted_weight = self._gls_convert_weight(package.shipping_weight) - rate = self._gls_nl_rate(to.country_id.code, converted_weight or 0.0) - if rate and rate != self.GLS_NL_COUNTRY_NOT_FOUND: - total_rate += rate - unit = { - 'unitId': package.name, - 'weight': converted_weight - } - request_body['units'].append(unit) - else: - converted_weight = self._gls_convert_weight(picking.shipping_weight) - rate = self._gls_nl_rate(to.country_id.code, converted_weight or 0.0) - if rate and rate != self.GLS_NL_COUNTRY_NOT_FOUND: - total_rate += rate - unit = { - 'unitId': picking.name, - 'weight': converted_weight, - } - request_body['units'].append(unit) - - try: - # Create label - label = service.create_label(request_body) - trackings = [] - uniq_nos = [] - attachments = [] - for i, unit in enumerate(label['units'], 1): - trackings.append(unit['unitNo']) - uniq_nos.append(unit['uniqueNo']) - label_data = unit.get('label') - if label_data and sudoself.gls_nl_labeltype == 'pdf': - label_data = b64decode(label_data) - attachments.append(('LabelGLSNL-%s-%s.%s' % (unit['unitNo'], i, sudoself.gls_nl_labeltype), label_data)) - - tracking = ', '.join(set(trackings)) - logmessage = _("Shipment created into GLS NL
" - "Tracking Number: %s
" - "UniqueNo: %s") % (tracking, ', '.join(set(uniq_nos))) - picking.message_post(body=logmessage, attachments=attachments) - shipping_data = {'exact_price': total_rate, 'tracking_number': tracking} - res.append(shipping_data) - except HTTPError as e: - raise ValidationError(e) - return res - - def gls_nl_get_tracking_link(self, pickings): - return 'https://gls-group.eu/EU/en/parcel-tracking?match=%s' % pickings.carrier_tracking_ref - - def gls_nl_cancel_shipment(self, picking): - sudoself = self.sudo() - service = sudoself._get_gls_nl_service() - try: - request_body = { - 'unitNo': picking.carrier_tracking_ref, - 'username': sudoself.gls_nl_username, - 'password': sudoself.gls_nl_password, - 'shiptype': 'p', - 'shippingSystemName': self.GLS_NL_SOFTWARE_NAME, - 'shippingSystemVersion': self.GLS_NL_SOFTWARE_VER, - } - service.delete_label(request_body) - picking.message_post(body=_('Shipment N° %s has been cancelled' % picking.carrier_tracking_ref)) - picking.write({'carrier_tracking_ref': '', 'carrier_price': 0.0}) - except HTTPError as e: - raise ValidationError(e) - - def _gls_convert_weight(self, weight): - get_param = self.env['ir.config_parameter'].sudo().get_param - product_weight_in_lbs_param = get_param('product.weight_in_lbs') - if product_weight_in_lbs_param == '1': - return weight / 2.20462 - else: - return weight diff --git a/delivery_gls_nl/models/gls_nl_request.py b/delivery_gls_nl/models/gls_nl_request.py deleted file mode 100644 index 1c92d562..00000000 --- a/delivery_gls_nl/models/gls_nl_request.py +++ /dev/null @@ -1,38 +0,0 @@ -# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details. - -import requests -from json import dumps - - -class GLSNLRequest: - def __init__(self, production): - self.production = production - self.api_key = '234a6d4ad5fd4d039526a8a1074051ee' if production else 'f80d41c6f7d542878c9c0a4295de7a6a' - self.url = 'https://api.gls.nl/V1/api' if production else 'https://api.gls.nl/Test/V1/api' - self.headers = self._make_headers() - - def _make_headers(self): - return { - 'Content-Type': 'application/json', - 'Ocp-Apim-Subscription-Key': self.api_key, - } - - def post_request(self, endpoint, body): - if not self.production and body.get('username') == 'test': - # Override to test credentials - body['username'] = 'apitest1@gls-netherlands.com' - body['password'] = '9PMev9qM' - url = self.url + endpoint - result = requests.request('POST', url, headers=self.headers, data=dumps(body)) - if result.status_code != 200: - raise requests.HTTPError(result.text) - return result.json() - - def create_label(self, body): - return self.post_request('/Label/Create', body) - - def confirm_label(self, body): - return self.post_request('/Label/Confirm', body) - - def delete_label(self, body): - return self.post_request('/Label/Delete', body) diff --git a/delivery_gls_nl/views/delivery_gls_nl_view.xml b/delivery_gls_nl/views/delivery_gls_nl_view.xml deleted file mode 100644 index 8a928d17..00000000 --- a/delivery_gls_nl/views/delivery_gls_nl_view.xml +++ /dev/null @@ -1,61 +0,0 @@ - - - - - delivery.carrier.form.provider.gls_nl - delivery.carrier - - - - - - - - - - -