diff --git a/delivery_ups_oca_hibou/__init__.py b/delivery_ups_oca_hibou/__init__.py new file mode 100644 index 00000000..09434554 --- /dev/null +++ b/delivery_ups_oca_hibou/__init__.py @@ -0,0 +1,3 @@ +# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details. + +from . import models diff --git a/delivery_ups_oca_hibou/__manifest__.py b/delivery_ups_oca_hibou/__manifest__.py new file mode 100644 index 00000000..dddd00a6 --- /dev/null +++ b/delivery_ups_oca_hibou/__manifest__.py @@ -0,0 +1,19 @@ +{ + 'name': 'Hibou UPS Shipping (OCA)', + 'version': '15.0.1.0.0', + 'category': 'Stock', + 'author': "Hibou Corp.", + 'license': 'OPL-1', + 'website': 'https://hibou.io/', + 'depends': [ + 'delivery_ups_oca', + 'delivery_hibou', + ], + 'data': [ + 'views/stock_views.xml', + ], + 'demo': [ + ], + 'installable': True, + 'application': False, + } diff --git a/delivery_ups_oca_hibou/models/__init__.py b/delivery_ups_oca_hibou/models/__init__.py new file mode 100644 index 00000000..df67c538 --- /dev/null +++ b/delivery_ups_oca_hibou/models/__init__.py @@ -0,0 +1,5 @@ +# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details. + +from . import delivery_carrier +from . import stock +from . import ups_request_patch diff --git a/delivery_ups_oca_hibou/models/delivery_carrier.py b/delivery_ups_oca_hibou/models/delivery_carrier.py new file mode 100644 index 00000000..c1b3e112 --- /dev/null +++ b/delivery_ups_oca_hibou/models/delivery_carrier.py @@ -0,0 +1,47 @@ +# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details. + +from odoo import models +from odoo.exceptions import ValidationError + + +class DeliveryCarrier(models.Model): + _inherit = 'delivery.carrier' + + def _get_ups_signature_required_type(self, order=None, picking=None, package=None): + # '3' for Adult Sig. + return '2' + + def _get_ups_signature_required(self, order=None, picking=None, package=None): + if self.get_signature_required(order=order, picking=picking, package=package): + return self._get_ups_signature_required_type(order=order, picking=picking, package=package) + return False + + def _get_main_ups_account_number(self, order=None, picking=None): + wh = None + if order: + wh = order.warehouse_id + if picking: + wh = picking.picking_type_id.warehouse_id + if wh and wh.ups_shipper_number: + return wh.ups_shipper_number + return self.ups_shipper_number + + def _get_ups_account_number(self, order=None, picking=None): + """ + Common hook to customize what UPS Account number to use. + :return: UPS Account Number + """ + # Provided by Hibou Odoo Suite `delivery_hibou` + third_party_account = self.get_third_party_account(order=order, picking=picking) + if third_party_account: + if not third_party_account.delivery_type == 'ups': + raise ValidationError('Non-UPS Shipping Account indicated during UPS shipment.') + return third_party_account.name + if picking and picking.picking_type_id.warehouse_id.ups_shipper_number: + return picking.picking_type_id.warehouse_id.ups_shipper_number + return self.ups_shipper_number + + def _get_ups_carrier_account(self, picking): + # 3rd party billing should return False if not used. + account = self._get_ups_account_number(picking=picking) + return account if account not in (self.ups_shipper_number, picking.picking_type_id.warehouse_id.ups_shipper_number) else False diff --git a/delivery_ups_oca_hibou/models/stock.py b/delivery_ups_oca_hibou/models/stock.py new file mode 100644 index 00000000..2c06bfed --- /dev/null +++ b/delivery_ups_oca_hibou/models/stock.py @@ -0,0 +1,9 @@ +# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details. + +from odoo import fields, models + + +class StockWarehouse(models.Model): + _inherit = 'stock.warehouse' + + ups_shipper_number = fields.Char(string='UPS Shipper Number') diff --git a/delivery_ups_oca_hibou/models/ups_request_patch.py b/delivery_ups_oca_hibou/models/ups_request_patch.py new file mode 100644 index 00000000..0de24c2d --- /dev/null +++ b/delivery_ups_oca_hibou/models/ups_request_patch.py @@ -0,0 +1,159 @@ +# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details. + +from odoo.addons.delivery_ups_oca.models.ups_request import UpsRequest + +import logging + +_logger = logging.getLogger(__name__) + + + +def _quant_package_data(self, package, picking): + # TODO do we want to call this without a package? + if not package and picking.package_ids: + package = picking.package_ids + currency = picking.sale_id.currency_id if picking.sale_id else picking.company_id.currency_id + insurance_currency_code = currency.name + res = [] + NumOfPieces = picking.number_of_packages + PackageWeight = picking.shipping_weight + if package: + NumOfPieces = len(package) + # PackageWeight = sum(package.mapped('shipping_weight')) + for p in package: + package_data = { + "Description": p.name, + "NumOfPieces": str(NumOfPieces), + "Packaging": { + "Code": p.package_type_id.shipper_package_code, + "Description": p.name, + }, + "Dimensions": { + "UnitOfMeasurement": {"Code": self.package_dimension_code}, + "Length": str(p.package_type_id.packaging_length), + "Width": str(p.package_type_id.width), + "Height": str(p.package_type_id.height), + }, + "PackageWeight": { + "UnitOfMeasurement": {"Code": self.package_weight_code}, + "Weight": str(p.shipping_weight), + }, + "PackageServiceOptions": "", + } + # Hibou Insurance & Signature Requirement + insurance_value = self.carrier.get_insurance_value(picking=picking, package=p) + if insurance_value: + if not package_data['PackageServiceOptions']: + package_data['PackageServiceOptions'] = {} + package_data['PackageServiceOptions']['DeclaredValue'] = { + 'Type': {'Code': '01'}, + 'MonetaryValue': str(insurance_value), + 'CurrencyCode': insurance_currency_code, + } + signature_required = self.carrier._get_ups_signature_required(picking=picking, package=p) + if signature_required: + if not package_data['PackageServiceOptions']: + package_data['PackageServiceOptions'] = {} + package_data['PackageServiceOptions']['DeliveryConfirmation'] = { + 'DCISType': signature_required, + } + res.append(package_data) + return res + + package_type = self.carrier.ups_default_package_type_id + return [{ + "Description": picking.name, + "NumOfPieces": str(NumOfPieces), + "Packaging": { + "Code": package_type.shipper_package_code, + "Description": package_type.name, + }, + "Dimensions": { + "UnitOfMeasurement": {"Code": self.package_dimension_code}, + "Length": str(package_type.packaging_length), + "Width": str(package_type.width), + "Height": str(package_type.height), + }, + "PackageWeight": { + "UnitOfMeasurement": {"Code": self.package_weight_code}, + "Weight": str(PackageWeight), + }, + # TODO add signature requirements... + "PackageServiceOptions": "", + }] + +def _prepare_create_shipping(self, picking, package=None): + _logger.warning('_prepare_create_shipping(%s, %s)' % (picking, package)) + """Return a dict that can be passed to the shipping endpoint of the UPS API""" + + # setup some request level account details + self.shipper_number = self.carrier._get_main_ups_account_number(picking=picking) + + if not package: + package = picking.package_ids + packages = [] + if package: + packages = self._quant_package_data(package, picking) + else: + _logger.warning(' NOT COMPLETE _prepare_create_shipping') + packages = [] + package_info = self._quant_package_data_from_picking( + self.default_packaging_id, picking, False + ) + package_weight = round( + (picking.shipping_weight / picking.number_of_packages), 2 + ) + for i in range(0, picking.number_of_packages): + package_item = package_info + package_name = "%s (%s)" % (picking.name, i + 1) + package_item["Description"] = package_name + package_item["NumOfPieces"] = "1" + package_item["Packaging"]["Description"] = package_name + package_item["PackageWeight"]["Weight"] = str(package_weight) + packages.append(package_item) + + res = { + "ShipmentRequest": { + "Shipment": { + "Description": picking.name, + "Shipper": self._partner_to_shipping_data( + partner=picking.company_id.partner_id, + ShipperNumber=self.shipper_number, + ), + "ShipTo": self._partner_to_shipping_data(picking.partner_id), + "ShipFrom": self._partner_to_shipping_data( + picking.picking_type_id.warehouse_id.partner_id + or picking.company_id.partner_id + ), + "PaymentInformation": { + "ShipmentCharge": { + "Type": "01", + "BillShipper": { + "AccountNumber": self.shipper_number, + }, + } + }, + "Service": {"Code": self.service_code}, + "Package": packages, + }, + "LabelSpecification": self._label_data(), + } + } + + ups_carrier_account = self.carrier._get_ups_carrier_account(picking) + if ups_carrier_account: + # del res['ShipmentRequest']['Shipment']['PaymentInformation']['ShipmentCharge']['BillShipper'] + # res['ShipmentRequest']['Shipment']['PaymentInformation']['ShipmentCharge']['Type'] = '02' + res['ShipmentRequest']['Shipment']['PaymentInformation']['ShipmentCharge'] = { + 'Type': '01', + 'BillReceiver': { + 'Address': self._partner_to_shipping_data(picking.partner_id), + 'AccountNumber': ups_carrier_account, + } + } + # TODO do we need to change the BillReceiver.Address.PostalCode ? + _logger.warning(' ' + str(res)) + return res + +UpsRequest._prepare_create_shipping = _prepare_create_shipping +UpsRequest._quant_package_data = _quant_package_data diff --git a/delivery_ups_oca_hibou/views/stock_views.xml b/delivery_ups_oca_hibou/views/stock_views.xml new file mode 100644 index 00000000..e74ebd99 --- /dev/null +++ b/delivery_ups_oca_hibou/views/stock_views.xml @@ -0,0 +1,15 @@ + + + + + stock.warehouse + stock.warehouse + + + + + + + + + diff --git a/external/hibou-oca/delivery-carrier b/external/hibou-oca/delivery-carrier index d4173b21..64bc9a5c 160000 --- a/external/hibou-oca/delivery-carrier +++ b/external/hibou-oca/delivery-carrier @@ -1 +1 @@ -Subproject commit d4173b215f9074fa8580d63245ee2fb49e028a9c +Subproject commit 64bc9a5cf1e9c1240bc3bb28f5373f97107f3fab