Files
enterprise-addons/account_avatax/tests/common.py
T

245 lines
9.2 KiB
Python

import os
from contextlib import contextmanager
from unittest import SkipTest
from unittest.mock import patch
from odoo import Command
from odoo.addons.account_avatax.lib.avatax_client import AvataxClient
from odoo.addons.account.tests.common import TestTaxCommon
from odoo.tests.common import TransactionCase
from .mocked_invoice_1_response import generate_response as generate_response_invoice_1
from .mocked_invoice_2_response import generate_response as generate_response_invoice_2
from .mocked_invoice_3_response import generate_response as generate_response_invoice_3
NOTHING = object()
class TestAvataxCommon(TransactionCase):
@classmethod
def setUpClass(cls):
res = super().setUpClass()
cls.env.company.avalara_api_id = os.getenv("AVALARA_LOGIN_ID") or "AVALARA_LOGIN_ID"
cls.env.company.avalara_api_key = os.getenv("AVALARA_API_KEY") or "AVALARA_API_KEY"
cls.env.company.avalara_environment = 'sandbox'
cls.env.company.avalara_commit = True
# Update address of company
company = cls.env.user.company_id
company.write({
'street': "250 Executive Park Blvd",
'city': "San Francisco",
'state_id': cls.env.ref("base.state_us_5").id,
'country_id': cls.env.ref("base.us").id,
'zip': "94134",
})
company.partner_id.avalara_partner_code = os.getenv("AVALARA_COMPANY_CODE") or "DEFAULT"
cls.fp_avatax = cls.env['account.fiscal.position'].create({
'name': 'Avatax',
'is_avatax': True,
})
# Create partner with correct US address
cls.partner = cls.env["res.partner"].create({
'name': "Sale Partner",
'street': "2280 Market St",
'city': "San Francisco",
'state_id': cls.env.ref("base.state_us_5").id,
'country_id': cls.env.ref("base.us").id,
'zip': "94114",
'avalara_partner_code': 'CUST123456',
'property_account_position_id': cls.fp_avatax.id,
})
return res
@classmethod
@contextmanager
def _capture_request(cls, return_value=NOTHING, return_func=NOTHING):
class Capture:
val = None
def capture_request(self, method, *args, **kwargs):
self.val = kwargs
if return_value is NOTHING:
return return_func(method, *args, **kwargs)
return return_value
capture = Capture()
with patch(f'{AvataxClient.__module__}.AvataxClient.request', capture.capture_request):
yield capture
@classmethod
@contextmanager
def _skip_no_credentials(cls):
if not os.getenv("AVALARA_LOGIN_ID") or not os.getenv("AVALARA_API_KEY") or not os.getenv("AVALARA_COMPANY_CODE"):
raise SkipTest("no Avalara credentials")
yield
class TestAccountAvataxCommon(TestAvataxCommon, TestTaxCommon):
@classmethod
def setUpClass(cls):
res = super().setUpClass()
cls.foreign_currency = cls.setup_other_currency('EUR')
cls.product = cls.env["product.product"].create({
'name': "Product",
'default_code': 'PROD1',
'barcode': '123456789',
'list_price': 15.00,
'standard_price': 15.00,
'supplier_taxes_id': None,
'avatax_category_id': cls.env.ref('account_avatax.DC010000').id,
})
cls.product_user = cls.env["product.product"].create({
'name': "Odoo User",
'list_price': 35.00,
'standard_price': 35.00,
'supplier_taxes_id': None,
'avatax_category_id': cls.env.ref('account_avatax.DC010000').id,
})
cls.product_user_discound = cls.env["product.product"].create({
'name': "Odoo User Initial Discount",
'list_price': -5.00,
'standard_price': -5.00,
'supplier_taxes_id': None,
'avatax_category_id': cls.env.ref('account_avatax.DC010000').id,
})
cls.product_accounting = cls.env["product.product"].create({
'name': "Accounting",
'list_price': 30.00,
'standard_price': 30.00,
'supplier_taxes_id': None,
'avatax_category_id': cls.env.ref('account_avatax.DC010000').id,
})
cls.product_expenses = cls.env["product.product"].create({
'name': "Expenses",
'list_price': 15.00,
'standard_price': 15.00,
'supplier_taxes_id': None,
'avatax_category_id': cls.env.ref('account_avatax.DC010000').id,
})
cls.product_invoicing = cls.env["product.product"].create({
'name': "Invoicing",
'list_price': 15.00,
'standard_price': 15.00,
'supplier_taxes_id': None,
'avatax_category_id': cls.env.ref('account_avatax.DC010000').id,
})
# This tax is deliberately wrong with an amount of 1. This is used
# to make sure we use the tax values that Avatax returns and not the tax values
# Odoo computes (these values would be wrong if a user manually changes it for example).
cls.example_tax = cls.env["account.tax"].create({
'name': 'CA STATE 6%',
'company_id': cls.env.user.company_id.id,
'amount': 1,
'amount_type': 'percent',
})
return res
@classmethod
def _create_invoice(cls, post=True, **kwargs):
invoice = cls.env['account.move'].create({
'move_type': 'out_invoice',
'partner_id': cls.partner.id,
'fiscal_position_id': cls.fp_avatax.id,
'invoice_date': '2020-01-01',
'invoice_line_ids': [
(0, 0, {'product_id': cls.product.id, 'price_unit': 100}),
],
**kwargs,
})
if post:
invoice.action_post()
return invoice
@classmethod
def _create_invoice_01_and_expected_response(cls):
invoice = cls.env['account.move'].create({
'move_type': 'out_invoice',
'partner_id': cls.partner.id,
'fiscal_position_id': cls.fp_avatax.id,
'invoice_date': '2021-01-01',
'invoice_line_ids': [
(0, 0, {
'product_id': cls.product_user.id,
'tax_ids': None,
'price_unit': cls.product_user.list_price,
}),
(0, 0, {
'product_id': cls.product_user_discound.id,
'tax_ids': None,
'price_unit': cls.product_user_discound.list_price,
}),
(0, 0, {
'product_id': cls.product_accounting.id,
'tax_ids': None,
'price_unit': cls.product_accounting.list_price,
}),
(0, 0, {
'product_id': cls.product_expenses.id,
'tax_ids': None,
'price_unit': cls.product_expenses.list_price,
}),
(0, 0, {
'product_id': cls.product_invoicing.id,
'tax_ids': None,
'price_unit': cls.product_invoicing.list_price,
}),
]
})
response = generate_response_invoice_1(invoice.invoice_line_ids)
return invoice, response
@classmethod
def _create_invoice_02_and_expected_response(cls):
invoice = cls.env['account.move'].create({
'move_type': 'out_invoice',
'partner_id': cls.partner.id,
'fiscal_position_id': cls.fp_avatax.id,
'invoice_line_ids': [
(0, 0, {
'product_id': cls.product_user.id,
'tax_ids': None,
'price_unit': cls.product_user.list_price,
'discount': 1 / 7 * 100,
}),
(0, 0, {
'product_id': cls.product_accounting.id,
'tax_ids': None,
'price_unit': cls.product_accounting.list_price,
}),
(0, 0, {
'product_id': cls.product_expenses.id,
'tax_ids': None,
'price_unit': cls.product_expenses.list_price,
}),
(0, 0, {
'product_id': cls.product_invoicing.id,
'tax_ids': None,
'price_unit': cls.product_invoicing.list_price,
}),
]
})
response = generate_response_invoice_2(invoice.invoice_line_ids)
return invoice, response
@classmethod
def _create_invoice_03_and_expected_response(cls):
invoice = cls.env['account.move'].create({
'move_type': 'out_invoice',
'partner_id': cls.partner.id,
'fiscal_position_id': cls.fp_avatax.id,
'invoice_line_ids': [
Command.create({
'product_id': cls.product_accounting.id,
'tax_ids': None,
'price_unit': cls.product_accounting.list_price,
}),
]
})
response = generate_response_invoice_3(invoice.invoice_line_ids)
return invoice, response