import json from contextlib import contextmanager from unittest.mock import patch import requests from odoo.tests.common import tagged from odoo import Command from odoo.addons.delivery_ups_rest.tests.common import DeliveryUPSCommon from odoo.tools import mute_logger from odoo.tools.float_utils import float_repr @contextmanager def _mock_request_call(specific_ups_check=None): def _mock_request(*args, **kwargs): url = kwargs.get('url') if 'shipments' in url: url = url.split('/shipments')[1] responses = { 'token': {'access_token': 'mock_token'}, 'rating': {'RateResponse': { 'Response': { "Alert": {"Code": "string", "Description": "string"}, }, 'RatedShipment': {'TotalCharges': {'MonetaryValue': '5.5', 'CurrencyCode': 'USD'}}}}, 'ship': {'ShipmentResponse': {'ShipmentResults': { 'ShipmentCharges': {'TotalCharges': {'MonetaryValue': '5.5', 'CurrencyCode': 'USD'}}, 'ShipmentIdentificationNumber': '123', 'PackageResults': {'TrackingNumber': '123', 'ShippingLabel': {'GraphicImage': 'some_imag'}} }}}, 'cancel': {'VoidShipmentResponse': {'SummaryResult': {'Status': {}}}}, } for endpoint, content in responses.items(): if endpoint in url: if specific_ups_check: specific_ups_check(endpoint, kwargs['json']) response = requests.Response() response._content = json.dumps(content).encode() response.status_code = 200 return response raise Exception('unhandled request url %s' % url) with patch.object(requests.Session, 'request', _mock_request): yield @tagged('post_install', '-at_install') class TestDeliveryUPS(DeliveryUPSCommon): def setUp(self): super().setUp() self.env.company.partner_id.write({ 'country_id': self.env.ref('base.us').id, 'state_id': self.env.ref('base.state_us_5').id, 'city': 'Los Angelos', 'street': 'My street', 'phone': '1234567890', 'zip': '1234', }) self.product = self.env['product.product'].create({ 'name': 'fancy box', 'type': 'consu', 'weight': 10.0, }) self.partner = self.env['res.partner'].create({ 'name': 'Cool Customer', 'country_id': self.env.ref('base.us').id, 'state_id': self.env.ref('base.state_us_5').id, 'street': 'My street', 'city': 'Los Angelos', 'phone': '1234567890', 'zip': '1234', }) self.hong_kong_partner = self.env['res.partner'].create({ 'name': 'Hong Kong Customer', 'country_id': self.env.ref('base.hk').id, 'state_id': self.env.ref('base.state_hk_hk').id, 'street': "1 H-K Road", 'city': "Hong Kong", 'phone': '1234567890', 'zip': '999077', }) self.philippino_partner = self.env['res.partner'].create({ 'name': 'PH Customer', 'country_id': self.env.ref('base.ph').id, 'state_id': self.env.ref('base.state_ph_26').id, 'street': "1 Manille Road", 'city': "Manille", 'phone': '1234567890', 'zip': '4607', }) def test_ups_basic_flow(self): def delivery_confirmation_data_check(endpoint, payload): if endpoint == 'rating': shipment = payload['RateRequest']['Shipment'] self.assertFalse(shipment['ShipmentServiceOptions']) self.assertEqual(shipment['PackageServiceOptions']['DeliveryConfirmation']['DCISType'], '2') self.ups_delivery.ups_require_signature = True sale_order = self.env['sale.order'].create({ 'partner_id': self.partner.id, 'order_line': [(0, 0, { 'product_id': self.product.id, 'name': "Fancy box", 'product_uom_qty': 1.0, 'price_unit': 20, })] }) wiz_action = sale_order.action_open_delivery_wizard() choose_delivery_carrier = self.env[wiz_action['res_model']].with_context(wiz_action['context']).create({ 'carrier_id': self.ups_delivery.id, 'order_id': sale_order.id }) with _mock_request_call(delivery_confirmation_data_check): choose_delivery_carrier.update_price() choose_delivery_carrier.update_price() choose_delivery_carrier.button_confirm() self.assertEqual(choose_delivery_carrier.display_price, 5.5) sale_order.action_confirm() self.assertGreater(len(sale_order.picking_ids), 0, "The Sales Order did not generate pickings for shipment.") picking = sale_order.picking_ids[0] self.assertEqual(picking.carrier_id.id, sale_order.carrier_id.id, "Carrier is not the same on Picking and on SO.") picking.action_assign() picking.move_line_ids[0].quantity = 1.0 self.assertGreater(picking.weight, 0.0, "Picking weight should be positive.") picking._action_done() self.assertEqual(picking.carrier_tracking_ref, '123', 'Tracking ref should be set from mock response') self.assertEqual(picking.carrier_price, 5.5, 'Price should be set from mock response') picking.cancel_shipment() self.assertEqual(picking.carrier_tracking_ref, False, 'Shipment cancel failed') def test_ups_rest_sends_correct_delivery_type_for_amazon(self): amazon_expected_delivery_type = self.ups_delivery._get_delivery_type() self.assertEqual(amazon_expected_delivery_type, 'ups') def test_ups_invoice_uses_correct_currency(self): usd = self.env.ref('base.USD') eur = self.env.ref('base.EUR') pricelists = self.env['product.pricelist'].create([ { 'name': 'USD Pricelist', 'currency_id': usd.id, }, { 'name': 'EUR Pricelist', 'currency_id': eur.id, }, ]) for pricelist in pricelists: sale_order = self.env['sale.order'].create({ 'partner_id': self.partner.id, 'pricelist_id': pricelist.id, 'order_line': [Command.create({ 'product_id': self.product.id, 'name': "Fancy box", 'product_uom_qty': 1.0, 'price_unit': 20, })] }) wiz_action = sale_order.action_open_delivery_wizard() choose_delivery_carrier = self.env[wiz_action['res_model']].with_context(wiz_action['context']).create({ 'carrier_id': self.ups_delivery.id, 'order_id': sale_order.id }) with _mock_request_call(): choose_delivery_carrier.update_price() choose_delivery_carrier.button_confirm() sale_order.action_confirm() picking = sale_order.picking_ids[0] picking.action_assign() picking._action_done() _, shipment_info, _, _, _ = self.ups_delivery._prepare_shipping_data(picking) self.assertEqual(shipment_info['itl_currency_code'], pricelist.currency_id.name) def test_ups_commercial_invoice_freight_charge(self): ''' Ensure freight charge is correctly transmitted for internatonal UPS deliveries. ''' def freight_charge_check(endpoint, payload): if endpoint == 'ship': payload_freight_charge = payload['ShipmentRequest']['Shipment']['ShipmentServiceOptions']['InternationalForms']['FreightCharges']['MonetaryValue'] self.assertEqual(payload_freight_charge, freight_charge) sale_order = self.env['sale.order'].create({ 'partner_id': self.hong_kong_partner.id, 'order_line': [(0, 0, { 'product_id': self.product.id, 'name': "Fancy box", 'product_uom_qty': 1.0, 'price_unit': 20, })] }) wiz_action = sale_order.action_open_delivery_wizard() choose_delivery_carrier = self.env[wiz_action['res_model']].with_context(wiz_action['context']).create({ 'carrier_id': self.ups_delivery.id, 'order_id': sale_order.id }) with _mock_request_call(freight_charge_check): choose_delivery_carrier.update_price() choose_delivery_carrier.button_confirm() sale_order.action_confirm() freight_charge = float_repr(sale_order.order_line.filtered(lambda sol: sol.is_delivery).price_total, sale_order.currency_id.decimal_places) picking = sale_order.picking_ids[0] picking._action_done() def test_ups_partner_with_long_province_code(self): ''' Ensure long province codes are correctly sanitised when being sent to UPS. ''' def province_code_length_check(endpoint, payload): if endpoint == 'ship': province_code = payload['ShipmentRequest']['Shipment']['ShipTo']['Address']['StateProvinceCode'] self.assertEqual(province_code, '') sale_order = self.env['sale.order'].create({ 'partner_id': self.philippino_partner.id, 'order_line': [(0, 0, { 'product_id': self.product.id, 'name': "Fancy box", 'product_uom_qty': 1.0, 'price_unit': 20, })] }) wiz_action = sale_order.action_open_delivery_wizard() choose_delivery_carrier = self.env[wiz_action['res_model']].with_context(wiz_action['context']).create({ 'carrier_id': self.ups_delivery.id, 'order_id': sale_order.id }) with _mock_request_call(province_code_length_check): choose_delivery_carrier.update_price() choose_delivery_carrier.button_confirm() sale_order.action_confirm() self.assertGreater(len(sale_order.picking_ids), 0) picking = sale_order.picking_ids[0] picking.action_assign() picking.move_line_ids[0].quantity = 1.0 picking._action_done() @mute_logger('odoo.tools.translate') def test_ups_commercial_invoice_with_different_delivery_and_invoice_address(self): ''' Ensure the commercial invoice uses the partner's delivery address and invoicing address when specified. If the 2 address' country are different, ensure 'SoldTo' defaults to the delivery address (because UPS does not accept for them to be in different countries). ''' def same_country_commercial_invoice_address_check(endpoint, payload): if endpoint == 'ship': ship_to = payload['ShipmentRequest']['Shipment']['ShipTo'] sold_to = payload['ShipmentRequest']['Shipment']['ShipmentServiceOptions']['InternationalForms']['Contacts']['SoldTo'] self.assertEqual(ship_to['Address']['AddressLine'][0], delivery_address.street) self.assertEqual(sold_to['Address']['AddressLine'][0], invoicing_address.street) def different_country_commercial_invoice_address_check(endpoint, payload): if endpoint == 'ship': ship_to = payload['ShipmentRequest']['Shipment']['ShipTo'] sold_to = payload['ShipmentRequest']['Shipment']['ShipmentServiceOptions']['InternationalForms']['Contacts']['SoldTo'] self.assertEqual(ship_to['Address']['AddressLine'][0], sold_to['Address']['AddressLine'][0]) self.assertEqual(sold_to['Address']['AddressLine'][0], delivery_address.street) delivery_address, invoicing_address = self.env['res.partner'].create([ { 'name': 'Hong Kong Delivery Address', 'type': 'delivery', 'country_id': self.env.ref('base.hk').id, 'street': 'Delivery Street 1', 'state_id': self.env.ref('base.state_hk_hk').id, 'city': "Hong Kong", 'phone': '1234567890', 'zip': '999077', 'parent_id': self.hong_kong_partner.id, }, { 'name': 'Hong Kong Invoicing Address', 'type': 'invoice', 'country_id': self.env.ref('base.hk').id, 'street': 'Invoicing Street 1', 'state_id': self.env.ref('base.state_hk_hk').id, 'city': "Hong Kong", 'phone': '1234567890', 'zip': '999077', 'parent_id': self.hong_kong_partner.id, }, ]) sale_order_1, sale_order_2 = self.env['sale.order'].create([ { 'partner_id': self.hong_kong_partner.id, 'order_line': [Command.create({ 'product_id': self.product.id, 'name': "Fancy box", 'product_uom_qty': 1.0, 'price_unit': 20, })] }, { 'partner_id': self.hong_kong_partner.id, 'order_line': [Command.create({ 'product_id': self.product.id, 'name': "Fancy box", 'product_uom_qty': 3.0, 'price_unit': 30, })] }, ]) wiz_action = sale_order_1.action_open_delivery_wizard() choose_delivery_carrier = self.env[wiz_action['res_model']].with_context(wiz_action['context']).create({ 'carrier_id': self.ups_delivery.id, 'order_id': sale_order_1.id }) with _mock_request_call(same_country_commercial_invoice_address_check): choose_delivery_carrier.update_price() choose_delivery_carrier.button_confirm() sale_order_1.action_confirm() self.assertGreater(len(sale_order_1.picking_ids), 0) picking = sale_order_1.picking_ids[0] picking.action_assign() picking.move_line_ids[0].quantity = 1.0 picking._action_done() delivery_address.country_id = self.env.ref('base.uk') wiz_action = sale_order_2.action_open_delivery_wizard() choose_delivery_carrier = self.env[wiz_action['res_model']].with_context(wiz_action['context']).create({ 'carrier_id': self.ups_delivery.id, 'order_id': sale_order_2.id }) with _mock_request_call(different_country_commercial_invoice_address_check): choose_delivery_carrier.update_price() choose_delivery_carrier.button_confirm() sale_order_2.action_confirm() self.assertGreater(len(sale_order_2.picking_ids), 0) picking = sale_order_2.picking_ids[0] picking.action_assign() picking.move_line_ids[0].quantity = 1.0 picking._action_done()