forked from Mapan/odoo17e
889 lines
43 KiB
Python
889 lines
43 KiB
Python
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from datetime import datetime
|
|
from unittest.mock import Mock, patch
|
|
|
|
from odoo.exceptions import UserError
|
|
from odoo.tests.common import tagged
|
|
from odoo.tools import mute_logger
|
|
|
|
from odoo.addons.sale_amazon import utils as amazon_utils
|
|
from odoo.addons.sale_amazon.tests import common
|
|
|
|
|
|
@tagged('post_install', '-at_install')
|
|
class TestAmazon(common.TestAmazonCommon):
|
|
|
|
@mute_logger('odoo.addons.sale_amazon.models.amazon_account')
|
|
def test_update_marketplaces_no_change(self):
|
|
""" Test the available marketplaces synchronization with no change. """
|
|
marketplaces = self.env['amazon.marketplace'].search([])
|
|
with patch(
|
|
'odoo.addons.sale_amazon.models.amazon_account.AmazonAccount'
|
|
'._get_available_marketplaces', return_value=marketplaces
|
|
):
|
|
self.account.write({
|
|
'available_marketplace_ids': [(6, 0, marketplaces.ids)],
|
|
'active_marketplace_ids': [(6, 0, marketplaces.ids)],
|
|
})
|
|
self.account.action_update_available_marketplaces()
|
|
self.assertEqual(self.account.available_marketplace_ids.ids, marketplaces.ids)
|
|
self.assertEqual(self.account.active_marketplace_ids.ids, marketplaces.ids)
|
|
|
|
@mute_logger('odoo.addons.sale_amazon.models.amazon_account')
|
|
def test_update_marketplaces_remove(self):
|
|
""" Test the available marketplaces synchronization with a marketplace removal. """
|
|
marketplaces = self.env['amazon.marketplace'].search([], limit=2)
|
|
with patch(
|
|
'odoo.addons.sale_amazon.models.amazon_account.AmazonAccount'
|
|
'._get_available_marketplaces', return_value=marketplaces[:1]
|
|
):
|
|
self.account.write({
|
|
'available_marketplace_ids': [(6, 0, marketplaces.ids)],
|
|
'active_marketplace_ids': [(6, 0, marketplaces.ids)],
|
|
})
|
|
self.account.action_update_available_marketplaces()
|
|
self.assertEqual(self.account.available_marketplace_ids.ids, marketplaces.ids[:1])
|
|
self.assertEqual(
|
|
self.account.active_marketplace_ids.ids,
|
|
marketplaces.ids[:1],
|
|
msg="Unavailable marketplaces should be removed from the list of active "
|
|
"marketplaces.",
|
|
)
|
|
|
|
@mute_logger('odoo.addons.sale_amazon.models.amazon_account')
|
|
def test_update_marketplaces_add(self):
|
|
""" Test the available marketplaces synchronization with a new marketplace. """
|
|
marketplaces = self.env['amazon.marketplace'].search([], limit=2)
|
|
with patch(
|
|
'odoo.addons.sale_amazon.models.amazon_account.AmazonAccount'
|
|
'._get_available_marketplaces', return_value=marketplaces
|
|
):
|
|
self.account.write({
|
|
'available_marketplace_ids': [(6, 0, marketplaces.ids[:1])],
|
|
'active_marketplace_ids': [(6, 0, marketplaces.ids[:1])],
|
|
})
|
|
self.account.action_update_available_marketplaces()
|
|
self.assertEqual(self.account.available_marketplace_ids.ids, marketplaces.ids)
|
|
self.assertEqual(
|
|
self.account.active_marketplace_ids.ids,
|
|
marketplaces.ids,
|
|
msg="New available marketplaces should be added to the list of active marketplaces"
|
|
)
|
|
|
|
@mute_logger('odoo.addons.sale_amazon.models.amazon_account')
|
|
def test_sync_orders_full(self):
|
|
""" Test the orders synchronization with on-the-fly creation of all required records. """
|
|
|
|
def find_matching_product_mock(
|
|
_self, product_code_, _default_xmlid, default_name_, default_type_
|
|
):
|
|
""" Return a product created on-the-fly with the product code as internal reference. """
|
|
product_ = self.env['product.product'].create({
|
|
'name': default_name_,
|
|
'type': default_type_,
|
|
'list_price': 0.0,
|
|
'sale_ok': False,
|
|
'purchase_ok': False,
|
|
'default_code': product_code_,
|
|
})
|
|
product_.product_tmpl_id.taxes_id = False
|
|
return product_
|
|
|
|
# Create a warehouse that is prioritized when creating a normal order
|
|
view_location = self.env['stock.location'].create({
|
|
'name': "some location", 'usage': 'view', 'company_id': self.env.company.id
|
|
})
|
|
stock_location = self.env['stock.location'].create({
|
|
'name': "some location", 'usage': 'internal', 'company_id': self.env.company.id
|
|
})
|
|
other_warehouse = self.env['stock.warehouse'].create({
|
|
'name': "Other warehouse",
|
|
'code': "OW",
|
|
'view_location_id': view_location.id,
|
|
'lot_stock_id': stock_location.id,
|
|
'sequence': 1,
|
|
})
|
|
|
|
self.assertNotEqual(self.account.location_id.warehouse_id, other_warehouse)
|
|
|
|
with patch(
|
|
'odoo.addons.sale_amazon.utils.make_sp_api_request',
|
|
new=lambda _account, operation, **kwargs: common.OPERATIONS_RESPONSES_MAP[operation],
|
|
), patch(
|
|
'odoo.addons.sale_amazon.models.amazon_account.AmazonAccount._recompute_subtotal',
|
|
new=lambda self_, subtotal_, *args_, **kwargs_: subtotal_,
|
|
), patch(
|
|
'odoo.addons.sale_amazon.models.amazon_account.AmazonAccount._find_matching_product',
|
|
new=find_matching_product_mock,
|
|
):
|
|
self.account._sync_orders(auto_commit=False)
|
|
self.assertEqual(
|
|
self.account.last_orders_sync,
|
|
datetime(2020, 1, 1),
|
|
msg="The last_order_sync should be equal to the date returned by get_orders_data "
|
|
"when the synchronization is completed."
|
|
)
|
|
order = self.env['sale.order'].search([('amazon_order_ref', '=', '123456789')])
|
|
self.assertEqual(len(order), 1, msg="There should have been exactly one order created.")
|
|
self.assertEqual(order.origin, 'Amazon Order 123456789')
|
|
self.assertEqual(order.date_order, datetime(1378, 4, 8))
|
|
self.assertEqual(order.company_id.id, self.account.company_id.id)
|
|
self.assertEqual(order.user_id.id, self.account.user_id.id)
|
|
self.assertEqual(order.team_id.id, self.account.team_id.id)
|
|
self.assertNotEqual(order.warehouse_id.id, self.account.location_id.warehouse_id.id)
|
|
self.assertEqual(order.amazon_channel, 'fbm')
|
|
|
|
order_lines = self.env['sale.order.line'].search([('order_id', '=', order.id)])
|
|
self.assertEqual(
|
|
len(order_lines),
|
|
4,
|
|
msg="There should have been four order lines created: one for the product, one for "
|
|
"the gift wrapping charges, one (note) for the gift message and one for the "
|
|
"shipping."
|
|
)
|
|
product_line = order_lines.filtered(lambda l: l.product_id.default_code == 'TEST')
|
|
self.assertEqual(
|
|
product_line.price_unit,
|
|
50.0,
|
|
msg="The unitary price should be the quotient of the item price (tax excluded) "
|
|
"divided by the quantity.",
|
|
)
|
|
self.assertEqual(product_line.discount, 5) # 5% discount.
|
|
self.assertEqual(product_line.product_uom_qty, 2.0)
|
|
self.assertEqual(product_line.amazon_item_ref, '987654321')
|
|
self.assertTrue(product_line.amazon_offer_id)
|
|
|
|
shipping_line = order_lines.filtered(
|
|
lambda l: l.product_id.default_code == 'SHIPPING-CODE'
|
|
)
|
|
self.assertEqual(shipping_line.price_unit, 12.5)
|
|
self.assertEqual(shipping_line.discount, 20) # 2.5/12.5*100
|
|
self.assertEqual(shipping_line.product_uom_qty, 1.0)
|
|
self.assertFalse(shipping_line.amazon_item_ref)
|
|
self.assertFalse(shipping_line.amazon_offer_id)
|
|
|
|
gift_wrapping_line = order_lines.filtered(
|
|
lambda l: l.product_id.default_code == 'WRAP-CODE'
|
|
)
|
|
self.assertEqual(gift_wrapping_line.price_unit, 3.33)
|
|
self.assertEqual(gift_wrapping_line.product_uom_qty, 1.0)
|
|
self.assertFalse(gift_wrapping_line.amazon_item_ref)
|
|
self.assertFalse(gift_wrapping_line.amazon_offer_id)
|
|
|
|
@mute_logger('odoo.addons.sale_amazon.models.amazon_account')
|
|
def test_sync_orders_partial(self):
|
|
""" Test the orders synchronization interruption with API throttling. """
|
|
|
|
def get_sp_api_response_mock(_account, operation_, **_kwargs):
|
|
""" Return a mocked response without making an actual call to the SP-API. """
|
|
base_response_ = common.OPERATIONS_RESPONSES_MAP[operation_]
|
|
if operation_ == 'getOrders':
|
|
response_ = dict(base_response_, payload={
|
|
'LastUpdatedBefore': '2020-01-01T00:00:00Z',
|
|
'Orders': [common.ORDER_MOCK, dict(
|
|
common.ORDER_MOCK,
|
|
AmazonOrderId={'value': '987654321'},
|
|
LastUpdateDate='2019-01-20T00:00:00Z',
|
|
)],
|
|
})
|
|
elif operation_ == 'getOrderItems':
|
|
response_ = base_response_
|
|
self.get_order_items_count += 1
|
|
if self.get_order_items_count == 2:
|
|
raise amazon_utils.AmazonRateLimitError(operation_)
|
|
else:
|
|
response_ = base_response_
|
|
return response_
|
|
|
|
with patch(
|
|
'odoo.addons.sale_amazon.utils.make_sp_api_request', new=get_sp_api_response_mock
|
|
):
|
|
self.get_order_items_count = 0
|
|
self.account._sync_orders(auto_commit=False)
|
|
self.assertEqual(
|
|
self.account.last_orders_sync,
|
|
datetime(2017, 1, 20),
|
|
msg="The last_order_sync should be equal to the LastUpdateDate of the last fully "
|
|
"synchronized order if no all orders could be synchronized.",
|
|
)
|
|
|
|
@mute_logger('odoo.addons.sale_amazon.models.amazon_account')
|
|
def test_sync_orders_fail(self):
|
|
""" Test the orders synchronization cancellation with API throttling. """
|
|
|
|
def get_sp_api_response_mock(_account, operation_, **_kwargs):
|
|
""" Return a mocked response or raise an AmazonRateLimitError without making an actual
|
|
call to the SP-API. """
|
|
if operation_ != 'getOrderItems':
|
|
return common.OPERATIONS_RESPONSES_MAP[operation_]
|
|
else:
|
|
raise amazon_utils.AmazonRateLimitError(operation_)
|
|
|
|
with patch(
|
|
'odoo.addons.sale_amazon.utils.make_sp_api_request', new=get_sp_api_response_mock
|
|
):
|
|
last_order_sync_copy = self.account.last_orders_sync
|
|
self.account._sync_orders(auto_commit=False)
|
|
self.assertEqual(
|
|
self.account.last_orders_sync,
|
|
last_order_sync_copy,
|
|
msg="The last_order_sync field should not have been modified if the rate limit of "
|
|
"one operation was reached when synchronizing an order.",
|
|
)
|
|
|
|
@mute_logger('odoo.addons.sale_amazon.models.amazon_account')
|
|
def test_sync_orders_abort(self):
|
|
""" Test the orders synchronization cancellation with no active marketplace. """
|
|
last_order_sync_copy = self.account.last_orders_sync
|
|
self.account.active_marketplace_ids = False
|
|
with self.assertRaises(UserError):
|
|
self.account._sync_orders(auto_commit=False)
|
|
self.assertEqual(
|
|
self.account.last_orders_sync,
|
|
last_order_sync_copy,
|
|
msg="The last_order_sync field should not have been modified if there is no active "
|
|
"marketplace selected for the account.",
|
|
)
|
|
|
|
@mute_logger('odoo.addons.sale_amazon.models.amazon_account')
|
|
def test_sync_orders_fba(self):
|
|
""" Test the orders synchronization with Fulfillment By Amazon. """
|
|
|
|
def get_sp_api_response_mock(_account, operation_, **_kwargs):
|
|
""" Return a mocked response without making an actual call to the SP-API. """
|
|
base_response_ = common.OPERATIONS_RESPONSES_MAP[operation_]
|
|
if operation_ == 'getOrders':
|
|
return dict(base_response_, payload={
|
|
'LastUpdatedBefore': base_response_['payload']['LastUpdatedBefore'],
|
|
'Orders': [
|
|
dict(common.ORDER_MOCK, OrderStatus='Shipped', FulfillmentChannel='AFN')
|
|
],
|
|
})
|
|
else:
|
|
return base_response_
|
|
|
|
def find_matching_product_mock(
|
|
_self, product_code_, _default_xmlid, default_name_, default_type_
|
|
):
|
|
""" Return a product created on-the-fly with the product code as internal reference. """
|
|
product_ = self.env['product.product'].create({
|
|
'name': default_name_,
|
|
'type': default_type_,
|
|
'list_price': 0.0,
|
|
'sale_ok': False,
|
|
'purchase_ok': False,
|
|
'default_code': product_code_,
|
|
})
|
|
product_.product_tmpl_id.taxes_id = False
|
|
return product_
|
|
|
|
# Create a warehouse that is prioritized when creating a normal order
|
|
view_location = self.env['stock.location'].create({
|
|
'name': "some location", 'usage': 'view', 'company_id': self.env.company.id
|
|
})
|
|
stock_location = self.env['stock.location'].create({
|
|
'name': "some location", 'usage': 'internal', 'company_id': self.env.company.id
|
|
})
|
|
other_warehouse = self.env['stock.warehouse'].create({
|
|
'name': "Other warehouse",
|
|
'code': "OW",
|
|
'view_location_id': view_location.id,
|
|
'lot_stock_id': stock_location.id,
|
|
'sequence': 1,
|
|
})
|
|
|
|
self.assertNotEqual(self.account.location_id.warehouse_id, other_warehouse)
|
|
|
|
with patch(
|
|
'odoo.addons.sale_amazon.utils.make_sp_api_request', new=get_sp_api_response_mock
|
|
), patch(
|
|
'odoo.addons.sale_amazon.models.amazon_account.AmazonAccount._find_matching_product',
|
|
new=find_matching_product_mock,
|
|
):
|
|
self.account._sync_orders(auto_commit=False)
|
|
order = self.env['sale.order'].search([('amazon_order_ref', '=', '123456789')])
|
|
self.assertEqual(order.amazon_channel, 'fba')
|
|
self.assertEqual(order.warehouse_id.id, self.account.location_id.warehouse_id.id)
|
|
picking = self.env['stock.picking'].search([('sale_id', '=', order.id)])
|
|
self.assertEqual(len(picking), 0, msg="FBA orders should generate no picking.")
|
|
products = order.order_line.mapped('product_id').filtered(lambda p: p.type != 'service')
|
|
moves = self.env['stock.move'].search([('product_id', 'in', products.ids)])
|
|
self.assertEqual(
|
|
len(moves),
|
|
len(products),
|
|
msg="FBA orders should generate one stock move per product that is not a service.",
|
|
)
|
|
|
|
@mute_logger('odoo.addons.sale_amazon.models.amazon_account')
|
|
def test_sync_orders_europe(self):
|
|
""" Test the orders synchronization with an European marketplace. """
|
|
|
|
def get_sp_api_response_mock(_account, operation_, **_kwargs):
|
|
""" Return a mocked response without making an actual call to the SP-API. """
|
|
base_response_ = common.OPERATIONS_RESPONSES_MAP[operation_]
|
|
if operation_ == 'getOrders':
|
|
response_ = dict(base_response_, payload={
|
|
'LastUpdatedBefore': base_response_['payload']['LastUpdatedBefore'],
|
|
'Orders': [dict(common.ORDER_MOCK, MarketplaceId='A13V1IB3VIYZZH')]
|
|
})
|
|
else:
|
|
response_ = base_response_
|
|
return response_
|
|
|
|
def find_matching_product_mock(
|
|
_self, product_code_, _default_xmlid, default_name_, default_type_
|
|
):
|
|
""" Return a product created on-the-fly with the product code as internal reference. """
|
|
product_ = self.env['product.product'].create({
|
|
'name': default_name_,
|
|
'type': default_type_,
|
|
'list_price': 0.0,
|
|
'sale_ok': False,
|
|
'purchase_ok': False,
|
|
'default_code': product_code_,
|
|
})
|
|
product_.product_tmpl_id.taxes_id = False
|
|
return product_
|
|
|
|
with patch(
|
|
'odoo.addons.sale_amazon.utils.make_sp_api_request', new=get_sp_api_response_mock
|
|
), patch(
|
|
'odoo.addons.sale_amazon.models.amazon_account.AmazonAccount._recompute_subtotal',
|
|
new=lambda self_, subtotal_, *args_, **kwargs_: subtotal_,
|
|
), patch(
|
|
'odoo.addons.sale_amazon.models.amazon_account.AmazonAccount._find_matching_product',
|
|
new=find_matching_product_mock,
|
|
):
|
|
europe_mp = self.env['amazon.marketplace'].search([('api_ref', '=', 'A13V1IB3VIYZZH')])
|
|
self.account.base_marketplace_id = europe_mp.id
|
|
self.account.available_marketplace_ids = [europe_mp.id]
|
|
self.account.active_marketplace_ids = [europe_mp.id]
|
|
|
|
self.account._sync_orders(auto_commit=False)
|
|
order_lines = self.env['sale.order.line'].search(
|
|
[('order_id.amazon_order_ref', '=', '123456789')]
|
|
)
|
|
product_line = order_lines.filtered(lambda l: l.product_id.default_code == 'TEST')
|
|
shipping_line = order_lines.filtered(
|
|
lambda l: l.product_id.default_code == 'SHIPPING-CODE'
|
|
)
|
|
gift_wrapping_line = order_lines.filtered(
|
|
lambda l: l.product_id.default_code == 'WRAP-CODE'
|
|
)
|
|
self.assertEqual(
|
|
product_line.price_unit,
|
|
40, # (100 - 20)/2
|
|
msg="Tax amounts should be deducted from the item price for European marketplaces.",
|
|
)
|
|
self.assertEqual(
|
|
shipping_line.price_unit,
|
|
10, # 12.50 - 2.50
|
|
msg="Tax amounts should be deducted from the shipping price for European "
|
|
"marketplaces.",
|
|
)
|
|
self.assertEqual(
|
|
gift_wrapping_line.price_unit,
|
|
2, # 3.33 - 1.33
|
|
msg="Tax amounts should be deducted from the gift wrap price for European "
|
|
"marketplaces.",
|
|
)
|
|
|
|
@mute_logger('odoo.addons.sale_amazon.models.amazon_account')
|
|
def test_sync_orders_cancel(self):
|
|
""" Test the orders synchronization with cancellation from Amazon. """
|
|
|
|
def get_sp_api_response_mock(_account, operation_, **_kwargs):
|
|
""" Return a mocked response without making an actual call to the SP-API. """
|
|
base_response_ = common.OPERATIONS_RESPONSES_MAP[operation_]
|
|
order_status_ = 'Unshipped' if not order_created else 'Canceled'
|
|
if operation_ == 'getOrders':
|
|
return dict(base_response_, payload={
|
|
'LastUpdatedBefore': base_response_['payload']['LastUpdatedBefore'],
|
|
'Orders': [dict(common.ORDER_MOCK, OrderStatus=order_status_)],
|
|
})
|
|
else:
|
|
return base_response_
|
|
|
|
with patch(
|
|
'odoo.addons.sale_amazon.utils.make_sp_api_request', new=get_sp_api_response_mock
|
|
):
|
|
# Sync an order created on Amazon.
|
|
order_created = False
|
|
self.account._sync_orders(auto_commit=False)
|
|
|
|
# Sync an order canceled from Amazon.
|
|
order_created = True
|
|
self.account._sync_orders(auto_commit=False)
|
|
order = self.env['sale.order'].search([('amazon_order_ref', '=', '123456789')])
|
|
self.assertEqual(
|
|
order.state,
|
|
'cancel',
|
|
msg="The cancellation of orders should be synchronized from Amazon.",
|
|
)
|
|
|
|
@mute_logger('odoo.addons.sale_amazon.models.amazon_account')
|
|
@mute_logger('odoo.addons.sale_amazon.models.stock_picking')
|
|
def test_sync_orders_cancel_abort(self):
|
|
""" Test the pickings that were confirmed at odoo and then order is canceled at Amazon. """
|
|
|
|
def get_sp_api_response_mock(_account, operation_, **_kwargs):
|
|
""" Return a mock response without making an actual call to the Selling Partner API. """
|
|
base_response_ = common.OPERATIONS_RESPONSES_MAP[operation_]
|
|
order_status_ = 'Unshipped' if not self.order_canceled else 'Canceled'
|
|
if operation_ == 'getOrders':
|
|
response_ = dict(base_response_, payload={
|
|
'LastUpdatedBefore': base_response_['payload']['LastUpdatedBefore'],
|
|
'Orders': [dict(common.ORDER_MOCK, OrderStatus=order_status_)]
|
|
})
|
|
else:
|
|
response_ = base_response_
|
|
return response_
|
|
|
|
with patch(
|
|
'odoo.addons.sale_amazon.utils.make_sp_api_request', new=get_sp_api_response_mock
|
|
):
|
|
# Sync an order created on Amazon.
|
|
self.order_canceled = False
|
|
self.account._sync_orders(auto_commit=False)
|
|
|
|
# Check the order state and validate the picking.
|
|
order = self.env['sale.order'].search([('amazon_order_ref', '=', '123456789')])
|
|
self.assertNotEqual(order.state, 'cancel')
|
|
picking = self.env['stock.picking'].search([('sale_id', '=', order.id)])
|
|
picking.move_ids.picked = True
|
|
picking.carrier_id, picking.carrier_tracking_ref = self.carrier, self.tracking_ref
|
|
picking._action_done()
|
|
self.assertEqual(picking.state, 'done')
|
|
|
|
# Sync an order canceled from Amazon.
|
|
self.order_canceled = True
|
|
self.account._sync_orders(auto_commit=False)
|
|
|
|
def test_inventory_sync_is_skipped_when_disabled(self):
|
|
""" Test that the inventory synchronization is skipped when the account has disabled it. """
|
|
self.account.synchronize_inventory = False
|
|
with patch(
|
|
'odoo.addons.sale_amazon.utils.submit_feed', return_value='An_amazing_id'
|
|
) as mock:
|
|
self.assertEqual(self.account.offer_ids, self.offer)
|
|
self.assertEqual(self.offer.amazon_sync_status, False)
|
|
self.account._sync_inventory()
|
|
self.assertEqual(
|
|
mock.call_count,
|
|
0,
|
|
msg="The stock synchronization is deactivated, no call should have been made.",
|
|
)
|
|
self.assertEqual(self.offer.amazon_sync_status, False)
|
|
|
|
@mute_logger('odoo.addons.sale_amazon.models.amazon_account')
|
|
@mute_logger('odoo.addons.sale_amazon.models.amazon_offer')
|
|
def test_sync_inventory(self):
|
|
""" Test the inventory availability confirmation synchronization. """
|
|
self.account.synchronize_inventory = True
|
|
with patch(
|
|
'odoo.addons.sale_amazon.utils.submit_feed', return_value='An_amazing_id'
|
|
) as mock:
|
|
self.assertEqual(self.account.offer_ids, self.offer)
|
|
self.assertEqual(self.offer.amazon_sync_status, False)
|
|
self.account._sync_inventory()
|
|
self.assertEqual(self.offer.amazon_sync_status, 'processing')
|
|
self.assertEqual(
|
|
mock.call_count,
|
|
1,
|
|
msg="An inventory availability feed should be sent to Amazon for all the offers.",
|
|
)
|
|
|
|
@mute_logger('odoo.addons.sale_amazon.models.amazon_account')
|
|
@mute_logger('odoo.addons.sale_amazon.models.stock_picking')
|
|
def test_sync_pickings(self):
|
|
""" Test the pickings confirmation synchronization. """
|
|
with patch(
|
|
'odoo.addons.sale_amazon.utils.make_sp_api_request',
|
|
new=lambda account_, operation_, **_kwargs: common.OPERATIONS_RESPONSES_MAP[operation_],
|
|
), patch(
|
|
'odoo.addons.sale_amazon.utils.submit_feed', new=Mock(return_value='An_amazing_id'),
|
|
) as mock:
|
|
self.account._sync_orders(auto_commit=False)
|
|
order = self.env['sale.order'].search([('amazon_order_ref', '=', '123456789')])
|
|
picking = self.env['stock.picking'].search([('sale_id', '=', order.id)])
|
|
self.assertEqual(len(picking), 1, msg="FBM orders should generate exactly one picking.")
|
|
picking.carrier_id, picking.carrier_tracking_ref = self.carrier, self.tracking_ref
|
|
picking._action_done()
|
|
self.assertEqual(picking.amazon_sync_status, 'pending')
|
|
self.account.company_id.write({"street": "street1"}) # needed in no-demo
|
|
picking._sync_pickings(account_ids=(self.account.id,))
|
|
self.assertEqual(
|
|
mock.call_count,
|
|
1,
|
|
msg="An order fulfillment feed should be sent to Amazon for each confirmed "
|
|
"picking.",
|
|
)
|
|
self.assertEqual(picking.amazon_sync_status, 'processing')
|
|
|
|
def test_find_matching_product_search(self):
|
|
""" Test the product search based on the internal reference. """
|
|
self.env['product.product'].create({
|
|
'name': "Test Name",
|
|
'type': 'consu',
|
|
'default_code': 'TEST_CODE',
|
|
})
|
|
self.assertTrue(self.account._find_matching_product('TEST_CODE', None, None, None))
|
|
|
|
def test_find_matching_product_use_fallback(self):
|
|
""" Test the product search failure with use of the fallback. """
|
|
default_product = self.env['product.product'].create({
|
|
'name': "Default Name",
|
|
'type': 'consu',
|
|
})
|
|
self.env['ir.model.data'].create({
|
|
'module': 'sale_amazon',
|
|
'name': 'test_xmlid',
|
|
'model': 'product.product',
|
|
'res_id': default_product.id,
|
|
})
|
|
self.assertTrue(
|
|
self.account._find_matching_product('INCORRECT_CODE', 'test_xmlid', None, None)
|
|
)
|
|
|
|
def test_find_matching_product_regen_fallback(self):
|
|
""" Test the product search failure with regeneration of the fallback. """
|
|
default_product = self.env['product.product'].create({
|
|
'name': "Default Name",
|
|
'type': 'consu',
|
|
})
|
|
self.env['ir.model.data'].create({
|
|
'module': 'sale_amazon',
|
|
'name': 'test_xmlid',
|
|
'model': 'product.product',
|
|
'res_id': default_product.id,
|
|
})
|
|
default_product.unlink() # Simulate deletion of the default product added with data
|
|
product = self.account._find_matching_product(
|
|
'INCORRECT_CODE', 'test_xmlid', 'Default Name', 'consu'
|
|
)
|
|
self.assertEqual(product.name, 'Default Name')
|
|
self.assertEqual(product.type, 'consu')
|
|
self.assertEqual(product.list_price, 0.)
|
|
self.assertFalse(product.sale_ok)
|
|
self.assertFalse(product.purchase_ok)
|
|
|
|
def test_find_matching_product_no_fallback(self):
|
|
""" Test the product search failure without regeneration of the fallback. """
|
|
self.assertFalse(self.account._find_matching_product(
|
|
'INCORRECT_CODE', 'test_xmlid', 'Default Name', 'consu', fallback=False
|
|
))
|
|
self.assertFalse(self.env.ref('sale_amazon.test_xmlid', raise_if_not_found=False))
|
|
|
|
def test_get_pricelist_search(self):
|
|
""" Test the pricelist search. """
|
|
currency = self.env['res.currency'].create({
|
|
'name': 'TEST',
|
|
'symbol': 'T',
|
|
})
|
|
self.env['product.pricelist'].create({
|
|
'name': 'Amazon Pricelist %s' % currency.name,
|
|
'active': False,
|
|
'currency_id': currency.id,
|
|
})
|
|
pricelists_count = self.env['product.pricelist'].with_context(
|
|
active_test=False).search_count([])
|
|
self.assertTrue(self.account._find_or_create_pricelist(currency))
|
|
self.assertEqual(self.env['product.pricelist'].with_context(
|
|
active_test=False).search_count([]), pricelists_count)
|
|
|
|
def test_get_pricelist_creation(self):
|
|
""" Test the pricelist creation. """
|
|
currency = self.env['res.currency'].create({
|
|
'name': 'TEST',
|
|
'symbol': 'T',
|
|
})
|
|
pricelists_count = self.env['product.pricelist'].with_context(
|
|
active_test=False).search_count([])
|
|
pricelist = self.account._find_or_create_pricelist(currency)
|
|
self.assertEqual(self.env['product.pricelist'].with_context(
|
|
active_test=False).search_count([]), pricelists_count + 1)
|
|
self.assertFalse(pricelist.active)
|
|
self.assertEqual(pricelist.currency_id.id, currency.id)
|
|
|
|
def test_get_partners_no_creation_same_partners(self):
|
|
""" Test the partners search with contact as delivery. """
|
|
with patch(
|
|
'odoo.addons.sale_amazon.utils.make_sp_api_request',
|
|
new=lambda account_, operation_, **kwargs: common.OPERATIONS_RESPONSES_MAP[
|
|
operation_],
|
|
):
|
|
country_id = self.env['res.country'].search([('code', '=', 'US')], limit=1).id
|
|
self.env['res.partner'].create({
|
|
'name': 'Gederic Frilson',
|
|
'is_company': True,
|
|
'street': '123 RainBowMan Street',
|
|
'zip': '12345-1234',
|
|
'city': 'New Duck City DC',
|
|
'country_id': country_id,
|
|
'state_id': self.env['res.country.state'].search(
|
|
[('country_id', '=', country_id), ('code', '=', 'CA')], limit=1
|
|
).id,
|
|
'phone': '+1 234-567-8910 ext. 12345',
|
|
'customer_rank': 1,
|
|
'company_id': self.account.company_id.id,
|
|
'amazon_email': 'iliketurtles@marketplace.amazon.com',
|
|
})
|
|
contacts_count = self.env['res.partner'].search_count([])
|
|
order_data = common.OPERATIONS_RESPONSES_MAP['getOrders']['payload']['Orders'][0]
|
|
contact, delivery = self.account._find_or_create_partners_from_data(order_data)
|
|
self.assertEqual(self.env['res.partner'].search_count([]), contacts_count)
|
|
self.assertEqual(contact.id, delivery.id)
|
|
self.assertEqual(contact.type, 'contact')
|
|
self.assertEqual(contact.amazon_email, 'iliketurtles@marketplace.amazon.com')
|
|
|
|
def test_get_partners_no_creation_different_partners(self):
|
|
""" Test the partners search with different partners for contact and delivery. """
|
|
country_id = self.env['res.country'].search([('code', '=', 'US')], limit=1).id
|
|
new_partner_vals = {
|
|
'is_company': True,
|
|
'street': '123 RainBowMan Street',
|
|
'zip': '12345-1234',
|
|
'city': 'New Duck City DC',
|
|
'country_id': country_id,
|
|
'state_id': self.env['res.country.state'].search(
|
|
[('country_id', '=', country_id), ('code', '=', 'CA')], limit=1
|
|
).id,
|
|
'phone': '+1 234-567-8910 ext. 12345',
|
|
'customer_rank': 1,
|
|
'company_id': self.account.company_id.id,
|
|
'amazon_email': 'iliketurtles@marketplace.amazon.com',
|
|
}
|
|
contact = self.env['res.partner'].create(dict(new_partner_vals, name='Gederic Frilson'))
|
|
self.env['res.partner'].create(dict(
|
|
new_partner_vals,
|
|
name='Gederic Frilson Delivery',
|
|
type='delivery',
|
|
parent_id=contact.id,
|
|
))
|
|
partners_count = self.env['res.partner'].search_count([])
|
|
order_data = dict(common.ORDER_MOCK, ShippingAddress=dict(
|
|
common.ORDER_ADDRESS_MOCK, Name='Gederic Frilson Delivery'
|
|
))
|
|
contact, delivery = self.account._find_or_create_partners_from_data(order_data)
|
|
self.assertEqual(self.env['res.partner'].search_count([]), partners_count)
|
|
self.assertNotEqual(contact.id, delivery.id)
|
|
self.assertEqual(delivery.type, 'delivery')
|
|
self.assertEqual(delivery.parent_id.id, contact.id)
|
|
self.assertEqual(contact.amazon_email, delivery.amazon_email)
|
|
|
|
def test_get_partners_creation_delivery(self):
|
|
""" Test the partners search with creation of the delivery. """
|
|
|
|
def get_sp_api_response_mock(_account, operation_, **_kwargs):
|
|
""" Return a mocked response without making an actual call to the SP-API. """
|
|
base_response_ = common.OPERATIONS_RESPONSES_MAP[operation_]
|
|
if operation_ == 'getOrderAddress':
|
|
return dict(base_response_, payload={
|
|
'ShippingAddress': {
|
|
'AddressLine1': '123 RainBowMan Street',
|
|
'Phone': '+1 234-567-8910 ext. 12345',
|
|
'PostalCode': '12345-1234',
|
|
'City': 'New Duck City DC',
|
|
'StateOrRegion': 'CA',
|
|
'CountryCode': 'US',
|
|
'Name': 'Gederic Frilson Delivery',
|
|
'AddressType': 'Commercial',
|
|
}
|
|
})
|
|
else:
|
|
return base_response_
|
|
|
|
with patch(
|
|
'odoo.addons.sale_amazon.utils.make_sp_api_request', new=get_sp_api_response_mock
|
|
):
|
|
self.env['res.partner'].create({
|
|
'name': 'Gederic Frilson',
|
|
'company_id': self.account.company_id.id,
|
|
'amazon_email': 'iliketurtles@marketplace.amazon.com',
|
|
})
|
|
partners_count = self.env['res.partner'].search_count([])
|
|
order_data = common.OPERATIONS_RESPONSES_MAP['getOrders']['payload']['Orders'][0]
|
|
contact, delivery = self.account._find_or_create_partners_from_data(order_data)
|
|
self.assertEqual(
|
|
self.env['res.partner'].search_count([]),
|
|
partners_count + 1,
|
|
msg="A delivery partner should be created when a field of the address is different "
|
|
"from that of the contact.",
|
|
)
|
|
self.assertNotEqual(contact.id, delivery.id)
|
|
self.assertEqual(delivery.type, 'delivery')
|
|
self.assertEqual(delivery.parent_id.id, contact.id)
|
|
self.assertEqual(delivery.company_id.id, self.account.company_id.id)
|
|
self.assertEqual(contact.amazon_email, delivery.amazon_email)
|
|
|
|
def test_get_partners_creation_contact(self):
|
|
""" Test the partners search with creation of the contact. """
|
|
with patch(
|
|
'odoo.addons.sale_amazon.utils.make_sp_api_request',
|
|
new=lambda account_, operation_, **kwargs: common.OPERATIONS_RESPONSES_MAP[
|
|
operation_],
|
|
):
|
|
partners_count = self.env['res.partner'].search_count([])
|
|
order_data = common.OPERATIONS_RESPONSES_MAP['getOrders']['payload']['Orders'][0]
|
|
contact, delivery = self.account._find_or_create_partners_from_data(order_data)
|
|
self.assertEqual(
|
|
self.env['res.partner'].search_count([]),
|
|
partners_count + 1,
|
|
"No delivery partner should be created when the contact is not found and the name "
|
|
"on the order is the same as that of the address.",
|
|
)
|
|
self.assertEqual(contact.id, delivery.id)
|
|
self.assertEqual(contact.name, 'Gederic Frilson')
|
|
self.assertEqual(contact.type, 'contact')
|
|
self.assertTrue(contact.is_company)
|
|
self.assertEqual(contact.street, '123 RainBowMan Street')
|
|
self.assertFalse(contact.street2)
|
|
self.assertEqual(contact.zip, '12345-1234')
|
|
self.assertEqual(contact.city, 'New Duck City DC')
|
|
self.assertEqual(contact.country_id.code, 'US')
|
|
self.assertEqual(contact.state_id.code, 'CA')
|
|
self.assertEqual(contact.phone, '+1 234-567-8910 ext. 12345')
|
|
self.assertEqual(contact.customer_rank, 1)
|
|
self.assertEqual(contact.company_id.id, self.account.company_id.id)
|
|
self.assertEqual(contact.amazon_email, 'iliketurtles@marketplace.amazon.com')
|
|
|
|
def test_get_partners_creation_contact_delivery(self):
|
|
""" Test the partners search with creation of the contact and delivery. """
|
|
partners_count = self.env['res.partner'].search_count([])
|
|
order_data = dict(common.ORDER_MOCK, BuyerInfo=dict(
|
|
common.ORDER_BUYER_INFO_MOCK, BuyerName='Not Gederic Frilson'
|
|
))
|
|
contact, delivery = self.account._find_or_create_partners_from_data(order_data)
|
|
self.assertEqual(
|
|
self.env['res.partner'].search_count([]),
|
|
partners_count + 2,
|
|
msg="A contact partner and a delivery partner should be created when the contact "
|
|
"is not found and the name on the order is different from that of the address.",
|
|
)
|
|
self.assertNotEqual(contact.id, delivery.id)
|
|
self.assertEqual(contact.type, 'contact')
|
|
self.assertEqual(delivery.type, 'delivery')
|
|
self.assertEqual(delivery.parent_id.id, contact.id)
|
|
self.assertEqual(contact.company_id.id, delivery.company_id.id)
|
|
self.assertEqual(contact.amazon_email, delivery.amazon_email)
|
|
|
|
def test_get_partners_missing_buyer_name(self):
|
|
""" Test the partners search with missing buyer name. """
|
|
self.env['res.partner'].create({
|
|
'name': 'Gederic Frilson',
|
|
'company_id': self.account.company_id.id,
|
|
'amazon_email': 'iliketurtles@marketplace.amazon.com',
|
|
})
|
|
partners_count = self.env['res.partner'].search_count([])
|
|
order_data = dict(common.ORDER_MOCK, BuyerInfo=dict(
|
|
common.ORDER_BUYER_INFO_MOCK, BuyerName=None
|
|
))
|
|
contact, delivery = self.account._find_or_create_partners_from_data(order_data)
|
|
self.assertEqual(
|
|
self.env['res.partner'].search_count([]),
|
|
partners_count + 2,
|
|
msg="A contact partner should be created when the buyer name is missing, "
|
|
"regardless of whether the same customer already had a partner, and a delivery "
|
|
"partner should also be created if the address name is different.",
|
|
)
|
|
self.assertNotEqual(contact.id, delivery.id)
|
|
self.assertEqual(contact.type, 'contact')
|
|
self.assertEqual(delivery.type, 'delivery')
|
|
self.assertEqual(delivery.parent_id.id, contact.id)
|
|
self.assertEqual(contact.amazon_email, 'iliketurtles@marketplace.amazon.com')
|
|
self.assertEqual(
|
|
contact.street,
|
|
'123 RainBowMan Street',
|
|
msg="Partners synchronized with partial personal information should still hold all "
|
|
"the available personal information.",
|
|
)
|
|
|
|
def test_get_partners_missing_amazon_email(self):
|
|
""" Test the partners search with missing amazon email. """
|
|
self.env['res.partner'].create({
|
|
'name': 'Gederic Frilson',
|
|
'company_id': self.account.company_id.id,
|
|
'amazon_email': 'iliketurtles@marketplace.amazon.com',
|
|
})
|
|
partners_count = self.env['res.partner'].search_count([])
|
|
order_data = dict(common.ORDER_MOCK, BuyerInfo=dict(
|
|
common.ORDER_BUYER_INFO_MOCK, BuyerEmail=None
|
|
))
|
|
contact, _delivery = self.account._find_or_create_partners_from_data(order_data)
|
|
self.assertEqual(
|
|
self.env['res.partner'].search_count([]),
|
|
partners_count + 1,
|
|
msg="A contact partner should always be created when the amazon email is missing.",
|
|
)
|
|
self.assertFalse(contact.amazon_email)
|
|
|
|
def test_get_partners_arbitrary_fields(self):
|
|
""" Test the partners search with all PII filled but in arbitrary fields. """
|
|
order_data = dict(common.ORDER_MOCK, ShippingAddress=dict(
|
|
common.ORDER_ADDRESS_MOCK, AddressLine1=None, AddressLine2='123 test Street',
|
|
))
|
|
contact, _delivery = self.account._find_or_create_partners_from_data(order_data)
|
|
self.assertFalse(contact.street)
|
|
self.assertTrue(contact.street2)
|
|
self.assertTrue(contact.phone)
|
|
self.assertTrue(contact.customer_rank)
|
|
self.assertTrue(contact.amazon_email)
|
|
|
|
def _get_activity_count(self, contact):
|
|
"""" Return activity count of given the contact. """
|
|
activity_id = self.env.ref('mail.mail_activity_data_todo').id
|
|
return self.env['mail.activity'].search_count([
|
|
('activity_type_id', '=', activity_id),
|
|
('res_model_id', '=', 'res.partner'),
|
|
('res_id', '=', contact.id),
|
|
])
|
|
|
|
def test_activity_on_partners_for_no_matching_state(self):
|
|
""" Test activity created for the salesman if the state received in the Amazon data
|
|
didn't match any existing state when creating a partner.
|
|
"""
|
|
order_data = dict(common.ORDER_MOCK, ShippingAddress=dict(
|
|
common.ORDER_ADDRESS_MOCK, StateOrRegion="dummy_state"
|
|
))
|
|
contact, delivery = self.account._find_or_create_partners_from_data(order_data)
|
|
self.assertEqual(self._get_activity_count(contact), 1)
|
|
self.assertEqual(self._get_activity_count(delivery), 1)
|
|
|
|
def test_no_activity_on_partners_for_no_state_in_amazon_data(self):
|
|
""" Test no activity created for the salesman if there is no state received in the Amazon
|
|
data.
|
|
"""
|
|
order_data = dict(common.ORDER_MOCK, ShippingAddress=dict(
|
|
common.ORDER_ADDRESS_MOCK, StateOrRegion=None,
|
|
))
|
|
contact, delivery = self.account._find_or_create_partners_from_data(order_data)
|
|
self.assertFalse(self._get_activity_count(contact))
|
|
self.assertFalse(self._get_activity_count(delivery))
|
|
|
|
def test_get_amazon_offer_search(self):
|
|
""" Test the offer search. """
|
|
marketplace = self.env['amazon.marketplace'].search([('api_ref', '=', 'ATVPDKIKX0DER')])
|
|
self.env['amazon.offer'].create({
|
|
'account_id': self.account.id,
|
|
'marketplace_id': marketplace.id,
|
|
'product_id': self.account._find_matching_product(
|
|
None, 'default_product', None, None
|
|
).id,
|
|
'sku': 'TEST',
|
|
})
|
|
offers_count = self.env['amazon.offer'].search_count([])
|
|
self.assertTrue(self.account._find_or_create_offer('TEST', marketplace))
|
|
self.assertEqual(self.env['amazon.offer'].search_count([]), offers_count)
|
|
|
|
def test_get_amazon_offer_creation(self):
|
|
""" Test the offer creation. """
|
|
marketplace = self.env['amazon.marketplace'].search([('api_ref', '=', 'ATVPDKIKX0DER')])
|
|
offers_count = self.env['amazon.offer'].search_count([])
|
|
offer = self.account._find_or_create_offer('TEST', marketplace)
|
|
self.assertEqual(self.env['amazon.offer'].search_count([]), offers_count + 1)
|
|
self.assertEqual(offer.account_id.id, self.account.id)
|
|
self.assertEqual(offer.company_id.id, self.account.company_id.id)
|
|
self.assertEqual(offer.marketplace_id.api_ref, 'ATVPDKIKX0DER')
|
|
self.assertEqual(offer.sku, 'TEST')
|