1
0
forked from Mapan/odoo17e
odoo17e-kedaikipas58/addons/account_avatax_sale/tests/test_avatax.py
2024-12-10 09:04:09 +07:00

367 lines
16 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from odoo import fields
from odoo.tests.common import tagged
from odoo.tools.misc import formatLang
from odoo.addons.account_avatax.tests.common import TestAccountAvataxCommon
from .mocked_so_response import generate_response
@tagged("-at_install", "post_install")
class TestSaleAvalara(TestAccountAvataxCommon):
@classmethod
def setUpClass(cls, chart_template_ref=None):
res = super().setUpClass(chart_template_ref)
# 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 or if they're partially
# exempt).
cls.tax_with_diff_amount = cls.env["account.tax"].create({
'name': 'CA COUNTY TAX [075] (0.2500 %)',
'company_id': cls.env.user.company_id.id,
'amount': 1,
'amount_type': 'percent',
})
cls.sales_user = cls.env['res.users'].create({
'name': 'Sales user',
'login': 'sales',
'email': 'sale_user@test.com',
'groups_id': [(6, 0, [cls.env.ref('base.group_user').id, cls.env.ref('sales_team.group_sale_salesman').id])],
})
cls.env = cls.env(user=cls.sales_user)
cls.cr = cls.env.cr
return res
def assertOrder(self, order, mocked_response=None):
if mocked_response:
self.assertRecordValues(order, [{
'amount_total': 97.68,
'amount_untaxed': 90.0,
'amount_tax': 7.68,
}])
totals = order.tax_totals
subtotal_group = totals['groups_by_subtotal']['Untaxed Amount']
self.assertEqual(len(subtotal_group), 1, 'There should only be one subtotal group (Untaxed Amount)')
self.assertEqual(subtotal_group[0]['tax_group_amount'], order.amount_tax, 'The tax on tax_totals is different from amount_tax.')
self.assertEqual(totals['amount_total'], order.amount_total)
self.assertEqual(totals['formatted_amount_total'], formatLang(self.env, order.amount_total, currency_obj=order.currency_id))
for avatax_line in mocked_response['lines']:
so_line = order.order_line.filtered(lambda l: str(l.id) == avatax_line['lineNumber'].split(',')[1])
self.assertRecordValues(so_line, [{
'price_subtotal': avatax_line['taxableAmount'],
'price_tax': avatax_line['tax'],
'price_total': avatax_line['taxableAmount'] + avatax_line['tax'],
}])
else:
for line in order.order_line:
product_name = line.product_id.display_name
self.assertGreater(len(line.tax_id), 0, "Line with %s did not get any taxes set." % product_name)
self.assertGreater(order.amount_tax, 0.0, "Invoice has a tax_amount of 0.0.")
def _create_sale_order(self):
return self.env['sale.order'].create({
'user_id': self.sales_user.id,
'partner_id': self.partner.id,
'fiscal_position_id': self.fp_avatax.id,
'date_order': '2021-01-01',
'order_line': [
(0, 0, {
'product_id': self.product_user.id,
'tax_id': None,
'price_unit': self.product_user.list_price,
}),
(0, 0, {
'product_id': self.product_user_discound.id,
'tax_id': None,
'price_unit': self.product_user_discound.list_price,
}),
(0, 0, {
'product_id': self.product_accounting.id,
'tax_id': None,
'price_unit': self.product_accounting.list_price,
}),
(0, 0, {
'product_id': self.product_expenses.id,
'tax_id': None,
'price_unit': self.product_expenses.list_price,
}),
(0, 0, {
'product_id': self.product_invoicing.id,
'tax_id': None,
'price_unit': self.product_invoicing.list_price,
}),
]
})
def test_compute_on_send(self):
order = self._create_sale_order()
mocked_response = generate_response(order.order_line)
with self._capture_request(return_value=mocked_response):
order.action_quotation_send()
self.assertOrder(order, mocked_response=mocked_response)
def test_01_odoo_sale_order(self):
order = self._create_sale_order()
mocked_response = generate_response(order.order_line)
with self._capture_request(return_value=mocked_response):
order.button_external_tax_calculation()
self.assertOrder(order, mocked_response=mocked_response)
def test_integration_01_odoo_sale_order(self):
with self._skip_no_credentials():
order = self._create_sale_order()
order.button_external_tax_calculation()
self.assertOrder(order)
def test_tax_round_globally(self):
"""The total amount of sale orders elligible for Avatax should never be computed with
the 'round_globally' option but should instead use the 'round_per_line' mechanism"""
self.env.company.sudo().tax_calculation_rounding_method = 'round_globally'
order = self.env['sale.order'].create({
'user_id': self.sales_user.id,
'partner_id': self.partner.id,
'fiscal_position_id': self.fp_avatax.id,
'date_order': '2021-01-01',
'order_line': [
(0, 0, {
'product_id': self.product.id,
'product_uom_qty': 1,
'price_unit': 1.48,
'tax_id': self.tax_with_diff_amount.ids,
}),
(0, 0, {
'product_id': self.product.id,
'product_uom_qty': 1,
'price_unit': 1.48,
'tax_id': self.tax_with_diff_amount.ids,
}),
],
})
self.assertEqual(order.amount_total, 2.98)
def test_sale_order_downpayment(self):
""" Test the expected down payment flow. Down payments are not sent to Avalara. We invoice everything on the final "regular"
invoice, as if the down payments never happened.
"""
order = self._create_sale_order()
mocked_response = generate_response(order.order_line)
with self._capture_request(return_value=mocked_response):
order.action_confirm()
downpayment_pct = 50
payment_ctx = {
"active_model": "sale.order",
"active_ids": [order.id],
"active_id": order.id,
}
wizard = (
self.env["sale.advance.payment.inv"]
.with_context(**payment_ctx)
.create({
'advance_payment_method': 'percentage',
'amount': downpayment_pct,
})
)
wizard.product_id.taxes_id = self.env["account.tax"].sudo().create({"name": "downpayment tax", "amount": 20})
wizard.sudo().create_invoices()
downpayment_invoice = order.invoice_ids
with self._capture_request(return_value={'lines': [], 'summary': []}) as capture:
downpayment_invoice.sudo().action_post()
self.assertIsNone(capture.val, "Shouldn't call Avatax when posting a down payment invoice.")
self.assertEqual(len(order.order_line.filtered(lambda line: not line.display_type)), 6, "Should have generated a new down payment line.")
self.assertFalse(order.order_line.filtered('is_downpayment').tax_id, "Down payment lines on the quotation shouldn't have taxes.")
self.assertAlmostEqual(downpayment_invoice.amount_total, order.amount_total * downpayment_pct / 100, msg="Down payment has the wrong amount.")
self.assertEqual(downpayment_invoice.amount_tax, 0, "Down payment shouldn't have taxes.")
wizard = (
self.env["sale.advance.payment.inv"]
.with_context(**payment_ctx)
.create({
'advance_payment_method': 'delivered',
})
)
with self._capture_request(return_value={'lines': [], 'summary': []}) as capture:
wizard.sudo().create_invoices()
sent_lines = capture.val['json']['createTransactionModel']['lines']
self.assertEqual(len(sent_lines), 5, "Should send only the regular lines.")
@tagged("-at_install", "post_install")
class TestAccountAvalaraSalesTaxItemsIntegration(TestAccountAvataxCommon):
"""https://developer.avalara.com/certification/avatax/sales-tax-badge/"""
@classmethod
def setUpClass(cls, chart_template_ref=None):
res = super().setUpClass(chart_template_ref)
shipping_partner = cls.env["res.partner"].create({
'name': "Shipping Partner",
'street': "234 W 18th Ave",
'city': "Columbus",
'state_id': cls.env.ref("base.state_us_30").id, # Ohio
'country_id': cls.env.ref("base.us").id,
'zip': "43210",
})
with cls._capture_request(return_value={'lines': [], 'summary': []}) as capture:
cls.sale_order = cls.env['sale.order'].create({
'partner_id': cls.partner.id,
'partner_shipping_id': shipping_partner.id,
'fiscal_position_id': cls.fp_avatax.id,
'date_order': '2021-01-01',
'order_line': [
(0, 0, {
'product_id': cls.product.id,
'tax_id': None,
'price_unit': cls.product.list_price,
}),
]
})
cls.sale_order.button_external_tax_calculation()
cls.captured_arguments = capture.val['json']['createTransactionModel']
return res
def test_item_code(self):
"""Identify customer code (number, ID) to pass to the AvaTax service."""
line_model, line_id = self.captured_arguments['lines'][0]['number'].split(',')
self.assertEqual(self.sale_order.order_line, self.env[line_model].browse(int(line_id)))
def test_item_description(self):
"""Identify item/service/charge description to pass to the AvaTax service with a
human-readable description or item name.
"""
line_description = self.captured_arguments['lines'][0]['description']
self.assertEqual(self.sale_order.order_line.name, line_description)
def test_tax_code_mapping(self):
"""Association of an item or item group to an AvaTax Tax Code to describe the taxability
(e.g. Clothing-Shirts B-to-C).
"""
tax_code = self.captured_arguments['lines'][0]['taxCode']
self.assertEqual(self.product.avatax_category_id.code, tax_code)
def test_doc_code(self):
"""Values that can come across to AvaTax as the DocCode."""
code = self.captured_arguments['code']
sent_so = self.env['sale.order'].search([('avatax_unique_code', '=', code)])
self.assertEqual(self.sale_order, sent_so)
def test_customer_code(self):
"""Values that can come across to AvaTax as the Customer Code."""
customer_code = self.captured_arguments['customerCode']
self.assertEqual(self.sale_order.partner_id.avalara_partner_code, customer_code)
def test_doc_date(self):
"""Value that comes across to AvaTax as the DocDate."""
doc_date = self.captured_arguments['date'] # didn't find anything with "DocDate"
self.assertEqual(self.sale_order.date_order.date(), fields.Date.to_date(doc_date))
def test_calculation_date(self):
"""Value that is used for Tax Calculation Date in AvaTax."""
tax_date = self.captured_arguments['taxOverride']['taxDate']
self.assertEqual(self.sale_order.date_order.date(), fields.Date.to_date(tax_date))
def test_doc_type(self):
"""DocType used for varying stages of the transaction life cycle."""
doc_type = self.captured_arguments['type']
self.assertEqual('SalesOrder', doc_type)
def test_header_level_destination_address(self):
"""Value that is sent to AvaTax for Destination Address at the header level."""
destination_address = self.captured_arguments['addresses']['shipTo']
self.assertEqual(destination_address, {
'city': 'Columbus',
'country': 'US',
'line1': '234 W 18th Ave',
'postalCode': '43210',
'region': 'OH',
})
def test_header_level_origin_address(self):
"""Value that is sent to AvaTax for Origin Address at the header level."""
origin_address = self.captured_arguments['addresses']['shipFrom']
self.assertEqual(origin_address, {
'city': 'San Francisco',
'country': 'US',
'line1': '250 Executive Park Blvd',
'postalCode': '94134',
'region': 'CA',
})
def test_quantity(self):
"""Value that is sent to AvaTax for the Quantity."""
quantity = self.captured_arguments['lines'][0]['quantity']
self.assertEqual(self.sale_order.order_line.product_uom_qty, quantity)
def test_amount(self):
"""Value that is sent to AvaTax for the Amount."""
amount = self.captured_arguments['lines'][0]['amount']
self.assertEqual(self.sale_order.order_line.price_subtotal, amount)
def test_tax_code(self):
"""Value that is sent to AvaTax for the Tax Code."""
tax_code = self.captured_arguments['lines'][0]['taxCode']
self.assertEqual(self.sale_order.order_line.product_id.avatax_category_id.code, tax_code)
def test_sales_order(self):
"""Ensure that invoices are processed through a logical document lifecycle."""
self.assertEqual(self.captured_arguments['type'], 'SalesOrder')
with self._capture_request({'lines': [], 'summary': []}) as capture:
self.sale_order.action_quotation_send()
self.sale_order.action_confirm()
invoice = self.sale_order._create_invoices()
invoice.button_external_tax_calculation()
self.assertEqual(capture.val['json']['createTransactionModel']['type'], 'SalesInvoice')
with self._capture_request({'lines': [], 'summary': []}) as capture:
invoice.action_post()
self.assertTrue(capture.val['json']['createTransactionModel']['commit'])
def test_commit_tax(self):
"""Ensure that invoices are committed/posted for reporting appropriately."""
with self._capture_request({'lines': [], 'summary': []}) as capture:
self.sale_order.action_quotation_send()
self.sale_order.action_confirm()
invoice = self.sale_order._create_invoices()
invoice.action_post()
self.assertTrue(capture.val['json']['createTransactionModel']['commit'])
def test_merge_sale_orders(self):
"""Ensure sale orders with different shipping partner are not merged
in the same invoice
"""
shipping_partner_b = self.env["res.partner"].create({
'name': "Shipping Partner B",
'street': "4557 De Silva St",
'city': "Freemont",
'state_id': self.env.ref("base.state_us_13").id,
'country_id': self.env.ref("base.us").id,
'zip': "94538",
})
with self._capture_request(return_value={'lines': [], 'summary': []}):
sale_order_b = self.env['sale.order'].create({
'partner_id': self.partner.id,
'partner_shipping_id': shipping_partner_b.id,
'fiscal_position_id': self.fp_avatax.id,
'date_order': '2021-01-01',
'order_line': [
(0, 0, {
'product_id': self.product.id,
'tax_id': None,
'price_unit': self.product.list_price,
}),
]
})
orders = self.sale_order | sale_order_b
orders.action_confirm()
orders._create_invoices()
self.assertEqual(len(orders.invoice_ids), 2, "Different invoices should be created")