mirror of
https://github.com/bringout/oca-ocb-l10n_asia-pacific.git
synced 2026-04-26 18:41:59 +02:00
19.0 vanilla
This commit is contained in:
parent
7dc55599c6
commit
7f43bbbfcc
650 changed files with 45260 additions and 33436 deletions
8
odoo-bringout-oca-ocb-l10n_in/l10n_in/tests/__init__.py
Normal file
8
odoo-bringout-oca-ocb-l10n_in/l10n_in/tests/__init__.py
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
from . import common
|
||||
from . import test_hsn_summary
|
||||
from . import test_partner_details_on_invoice
|
||||
from . import test_l10n_in_fiscal_position
|
||||
from . import test_check_status
|
||||
from . import test_tds_tcs_alert
|
||||
from . import test_gstr_section
|
||||
from . import test_invoice_label
|
||||
271
odoo-bringout-oca-ocb-l10n_in/l10n_in/tests/common.py
Normal file
271
odoo-bringout-oca-ocb-l10n_in/l10n_in/tests/common.py
Normal file
|
|
@ -0,0 +1,271 @@
|
|||
from datetime import date
|
||||
|
||||
from odoo.addons.account.tests.common import AccountTestInvoicingCommon
|
||||
from odoo import Command
|
||||
|
||||
|
||||
class L10nInTestInvoicingCommon(AccountTestInvoicingCommon):
|
||||
@classmethod
|
||||
@AccountTestInvoicingCommon.setup_country('in')
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
|
||||
cls.maxDiff = None
|
||||
cls.test_date = date(2023, 5, 20)
|
||||
|
||||
# === Countries === #
|
||||
cls.country_in = cls.env.ref('base.in')
|
||||
cls.country_us = cls.env.ref('base.us')
|
||||
|
||||
# === States === #
|
||||
cls.state_in_gj = cls.env.ref('base.state_in_gj')
|
||||
cls.state_in_mh = cls.env.ref('base.state_in_mh')
|
||||
cls.state_in_hp = cls.env.ref('base.state_in_hp')
|
||||
|
||||
# === Companies === #
|
||||
cls.default_company = cls.company_data['company']
|
||||
cls.default_company.write({
|
||||
'name': "Default Company",
|
||||
'state_id': cls.state_in_gj.id,
|
||||
'vat': "24AAGCC7144L6ZE",
|
||||
'street': "Khodiyar Chowk",
|
||||
'street2': "Sala Number 3",
|
||||
'city': "Amreli",
|
||||
'zip': "365220",
|
||||
'l10n_in_is_gst_registered': True,
|
||||
'l10n_in_tds_feature': True,
|
||||
'l10n_in_tcs_feature': True,
|
||||
})
|
||||
|
||||
cls.outside_in_company = cls.env['res.company'].create({
|
||||
'name': 'Outside India Company',
|
||||
'country_id': cls.country_us.id,
|
||||
})
|
||||
|
||||
cls.user.write({
|
||||
'company_ids': [cls.default_company.id, cls.outside_in_company.id],
|
||||
'company_id': cls.default_company.id,
|
||||
})
|
||||
|
||||
# === Partners === #
|
||||
cls.partner_a.write({
|
||||
'name': "Partner Intra State",
|
||||
'vat': '24ABCPM8965E1ZE',
|
||||
'state_id': cls.default_company.state_id.id,
|
||||
'country_id': cls.country_in.id,
|
||||
'street': "Karansinhji Rd",
|
||||
'street2': "Karanpara",
|
||||
'city': "Rajkot",
|
||||
'zip': "360001",
|
||||
})
|
||||
|
||||
cls.partner_b.write({
|
||||
'vat': '27DJMPM8965E1ZE',
|
||||
'state_id': cls.state_in_mh.id,
|
||||
'country_id': cls.country_in.id,
|
||||
'street': "Sangeet Samrat Naushad Ali Rd",
|
||||
'city': "Mumbai",
|
||||
'zip': "400052",
|
||||
})
|
||||
|
||||
cls.partner_foreign = cls.env['res.partner'].create({
|
||||
'name': "Foreign Partner",
|
||||
'country_id': cls.country_us.id,
|
||||
'state_id': cls.env.ref("base.state_us_1").id,
|
||||
'street': "351 Horner Chapel Rd",
|
||||
'city': "Peebles",
|
||||
'zip': "45660",
|
||||
})
|
||||
|
||||
cls.partner_foreign_no_state = cls.env['res.partner'].create({
|
||||
'name': "Foreign Partner Without State",
|
||||
'country_id': cls.country_us.id,
|
||||
# No state_id defined
|
||||
})
|
||||
cls.sez_partner = cls.env['res.partner'].create({
|
||||
'name': 'SEZ Partner',
|
||||
'vat': '36AAAAA1234AAZA',
|
||||
'l10n_in_gst_treatment': 'special_economic_zone',
|
||||
'street': 'Block no. 402',
|
||||
'city': 'Some city',
|
||||
'zip': '500002',
|
||||
'state_id': cls.env.ref('base.state_in_gj').id,
|
||||
'country_id': cls.env.ref('base.in').id,
|
||||
})
|
||||
|
||||
# === Taxes === #
|
||||
AccountChartTemplate = cls.env['account.chart.template']
|
||||
cls.sgst_sale_5 = AccountChartTemplate.ref('sgst_sale_5')
|
||||
cls.sgst_purchase_5 = AccountChartTemplate.ref('sgst_purchase_5')
|
||||
cls.igst_sale_5 = AccountChartTemplate.ref('igst_sale_5')
|
||||
cls.igst_sale_18 = AccountChartTemplate.ref('igst_sale_18')
|
||||
cls.sgst_sale_18 = AccountChartTemplate.ref('sgst_sale_18')
|
||||
cls.igst_sale_18_rcm = AccountChartTemplate.ref('igst_sale_18_rc')
|
||||
cls.igst_sale_18_sez_lut = AccountChartTemplate.ref('igst_sale_18_sez_lut')
|
||||
cls.igst_sale_18_sez_exp_lut = AccountChartTemplate.ref('igst_sale_18_sez_exp_lut')
|
||||
cls.igst_sale_18_sez_exp = AccountChartTemplate.ref('igst_sale_18_sez_exp')
|
||||
cls.igst_sale_18_sez_exp_inc = cls.igst_sale_18_sez_exp.copy({'price_include_override': 'tax_included'})
|
||||
cls.gst_with_cess = (
|
||||
AccountChartTemplate.ref("sgst_sale_12")
|
||||
+ AccountChartTemplate.ref("cess_5_plus_1591_sale")
|
||||
)
|
||||
cls.exempt = AccountChartTemplate.ref('exempt_sale')
|
||||
|
||||
# === Products === #
|
||||
cls.product_a.write({
|
||||
"l10n_in_hsn_code": "111111",
|
||||
'taxes_id': cls.sgst_sale_5,
|
||||
'supplier_taxes_id': cls.sgst_purchase_5,
|
||||
})
|
||||
|
||||
cls.product_b.write({
|
||||
"l10n_in_hsn_code": "111111",
|
||||
'uom_id': cls.env.ref('uom.product_uom_unit').id,
|
||||
'lst_price': 1000.0,
|
||||
'standard_price': 1000.0,
|
||||
'taxes_id': cls.sgst_sale_5.ids,
|
||||
'supplier_taxes_id': cls.sgst_purchase_5.ids,
|
||||
})
|
||||
cls.product_with_cess = cls.env["product.product"].create({
|
||||
"name": "product_with_cess",
|
||||
"uom_id": cls.env.ref("uom.product_uom_unit").id,
|
||||
"lst_price": 1000.0,
|
||||
"standard_price": 800.0,
|
||||
"property_account_income_id": cls.company_data["default_account_revenue"].id,
|
||||
"property_account_expense_id": cls.company_data["default_account_expense"].id,
|
||||
"taxes_id": [Command.set(cls.gst_with_cess.ids)],
|
||||
"supplier_taxes_id": [Command.set(cls.sgst_purchase_5.ids)],
|
||||
"l10n_in_hsn_code": "333333",
|
||||
})
|
||||
|
||||
# === Fiscal Positions === #
|
||||
cls.fp_in_intra_state = cls.env["account.chart.template"].ref('fiscal_position_in_intra_state')
|
||||
cls.fp_in_inter_state = cls.env["account.chart.template"].ref('fiscal_position_in_inter_state')
|
||||
cls.fp_in_export = cls.env["account.chart.template"].ref('fiscal_position_in_export_sez_in')
|
||||
|
||||
# === Invoices === #
|
||||
cls.invoice_a = cls.init_invoice(
|
||||
move_type='out_invoice',
|
||||
partner=cls.partner_a,
|
||||
amounts=[110, 500],
|
||||
taxes=cls.igst_sale_18,
|
||||
)
|
||||
|
||||
cls.invoice_b = cls.init_invoice(
|
||||
move_type='out_invoice',
|
||||
partner=cls.partner_b,
|
||||
amounts=[250, 600],
|
||||
taxes=cls.igst_sale_18,
|
||||
)
|
||||
|
||||
cls.invoice_c = cls.init_invoice(
|
||||
move_type='out_invoice',
|
||||
partner=cls.partner_foreign,
|
||||
amounts=[300, 740],
|
||||
taxes=cls.igst_sale_18,
|
||||
)
|
||||
|
||||
cls.invoice_d = cls.init_invoice(
|
||||
move_type='out_invoice',
|
||||
partner=cls.partner_foreign_no_state,
|
||||
amounts=[100, 200],
|
||||
taxes=cls.igst_sale_18,
|
||||
)
|
||||
|
||||
cls.invoice_with_rcm = cls.init_invoice(
|
||||
"out_invoice",
|
||||
partner=cls.partner_b,
|
||||
products=cls.product_a,
|
||||
taxes=cls.igst_sale_18_rcm,
|
||||
)
|
||||
|
||||
cls.invoice_with_sez_lut = cls.init_invoice(
|
||||
"out_invoice",
|
||||
partner=cls.sez_partner,
|
||||
products=cls.product_a,
|
||||
taxes=cls.igst_sale_18_sez_lut,
|
||||
)
|
||||
|
||||
cls.invoice_with_sez_without_lut = cls.init_invoice(
|
||||
"out_invoice",
|
||||
partner=cls.sez_partner,
|
||||
products=cls.product_a,
|
||||
taxes=cls.igst_sale_18,
|
||||
)
|
||||
|
||||
cls.invoice_with_export_lut = cls.init_invoice(
|
||||
"out_invoice",
|
||||
partner=cls.partner_foreign,
|
||||
products=cls.product_a,
|
||||
taxes=cls.igst_sale_18_sez_exp_lut,
|
||||
)
|
||||
|
||||
cls.invoice_with_export_without_lut = cls.init_invoice(
|
||||
"out_invoice",
|
||||
partner=cls.partner_foreign,
|
||||
products=cls.product_a,
|
||||
taxes=cls.igst_sale_18_sez_exp,
|
||||
)
|
||||
cls.invoice_with_export_without_lut_inc = cls.init_invoice(
|
||||
"out_invoice",
|
||||
partner=cls.partner_foreign,
|
||||
products=cls.product_a,
|
||||
taxes=cls.igst_sale_18_sez_exp_inc,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _set_vals_and_post(cls, move, ref=None, line_vals=None, post=True, irn=None):
|
||||
if ref:
|
||||
move.ref = ref
|
||||
if irn:
|
||||
move.l10n_in_irn_number = irn
|
||||
|
||||
if line_vals:
|
||||
move.write({'invoice_line_ids': [Command.update(line.id, line_vals) for line in move.line_ids]})
|
||||
|
||||
if post:
|
||||
move.action_post()
|
||||
return move
|
||||
|
||||
@classmethod
|
||||
def _init_inv(cls, move_type='out_invoice', company=None, ref=None, partner=None, taxes=None, invoice_date=None, products=None, line_vals=None, post=True, irn=None):
|
||||
return cls._set_vals_and_post(
|
||||
move=cls.init_invoice(
|
||||
move_type,
|
||||
products=products or cls.product_a,
|
||||
invoice_date=invoice_date or cls.test_date,
|
||||
taxes=taxes,
|
||||
company=company or cls.default_company,
|
||||
partner=partner,
|
||||
),
|
||||
ref=ref,
|
||||
irn=irn,
|
||||
line_vals=line_vals,
|
||||
post=post
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _create_credit_note(cls, inv, ref=None, credit_note_date=None, line_vals=None, post=True):
|
||||
move = inv._reverse_moves()
|
||||
move.invoice_date = credit_note_date or cls.test_date
|
||||
|
||||
return cls._set_vals_and_post(
|
||||
move=move,
|
||||
ref=ref,
|
||||
line_vals=line_vals,
|
||||
post=post
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _create_debit_note(cls, inv, ref=None, debit_note_date=None, line_vals=None):
|
||||
move_debit_note_wiz = cls.env['account.debit.note'].with_context(
|
||||
active_model="account.move",
|
||||
active_ids=inv.ids
|
||||
).create({
|
||||
'date': debit_note_date or cls.test_date,
|
||||
'reason': 'no reason',
|
||||
'copy_lines': True,
|
||||
})
|
||||
move_debit_note_wiz.create_debit()
|
||||
|
||||
return cls._set_vals_and_post(move=inv.debit_note_ids[0], ref=ref, line_vals=line_vals)
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
from unittest.mock import patch
|
||||
from freezegun import freeze_time
|
||||
|
||||
from odoo.addons.l10n_in.models.iap_account import IapAccount
|
||||
from odoo.tests.common import TransactionCase, tagged
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.tools import mute_logger
|
||||
from datetime import date
|
||||
|
||||
|
||||
@tagged('post_install_l10n', 'post_install', '-at_install')
|
||||
class TestGSTStatusFeature(TransactionCase):
|
||||
def setUp(self):
|
||||
self.partner1 = self.env["res.partner"].create(
|
||||
{"name": "Active GSTIN", "vat": "36AAACM4154G1ZO"}
|
||||
)
|
||||
self.partner2 = self.env["res.partner"].create(
|
||||
{"name": "Cancelled GSTIN", "vat": "19AABCT1332L2ZD"}
|
||||
)
|
||||
self.partner3 = self.env["res.partner"].create(
|
||||
{"name": "Invalid GSTIN", "vat": "19AACCT6304M1ZB"}
|
||||
)
|
||||
self.partner4 = self.env["res.partner"].create(
|
||||
{"name": "No Records GSTIN", "vat": "19AACCT6304M1DB"}
|
||||
)
|
||||
self.partner5 = self.env["res.partner"].create(
|
||||
{
|
||||
"name": "Partner Vat Reset",
|
||||
"vat": "36AAACM4154G1ZO",
|
||||
"country_id": self.env.ref('base.in').id,
|
||||
"l10n_in_gstin_verified_status": "active",
|
||||
"l10n_in_gstin_verified_date": "2024-06-01",
|
||||
}
|
||||
)
|
||||
self.mock_responses = {
|
||||
"active": {
|
||||
"data": {"sts": "Active"}
|
||||
},
|
||||
"cancelled": {
|
||||
"data": {"sts": "Cancelled"}
|
||||
},
|
||||
"invalid": {
|
||||
"error": [{"code": "SWEB_9035", "message": "Invalid GSTIN / UID"}],
|
||||
},
|
||||
"no_records": {
|
||||
"error": [{"code": "FO8000", "message": "No records found for the provided GSTIN."}],
|
||||
},
|
||||
}
|
||||
self.env.company.l10n_in_gstin_status_feature = True
|
||||
self.env.company.account_fiscal_country_id = self.env.ref("base.in")
|
||||
|
||||
@freeze_time('2024-05-20')
|
||||
@mute_logger('odoo.addons.l10n_in.models.res_partner')
|
||||
def check_gstin_status(self, partner, expected_status, mock_response, raises_exception=False):
|
||||
with patch.object(IapAccount, "_l10n_in_connect_to_server", return_value=mock_response):
|
||||
if raises_exception:
|
||||
with self.assertRaises(UserError):
|
||||
partner.action_l10n_in_verify_gstin_status()
|
||||
else:
|
||||
partner.action_l10n_in_verify_gstin_status()
|
||||
self.assertEqual(partner.l10n_in_gstin_verified_status, expected_status)
|
||||
self.assertEqual(partner.l10n_in_gstin_verified_date, date(2024, 5, 20))
|
||||
|
||||
def test_gstin_status(self):
|
||||
"""Test GSTIN status for various cases"""
|
||||
self.check_gstin_status(
|
||||
self.partner1,
|
||||
expected_status=True,
|
||||
mock_response=self.mock_responses["active"]
|
||||
)
|
||||
self.check_gstin_status(
|
||||
self.partner2,
|
||||
expected_status=False,
|
||||
mock_response=self.mock_responses["cancelled"]
|
||||
)
|
||||
self.check_gstin_status(
|
||||
self.partner3,
|
||||
expected_status=False,
|
||||
raises_exception=True,
|
||||
mock_response=self.mock_responses["invalid"],
|
||||
)
|
||||
self.check_gstin_status(
|
||||
self.partner4,
|
||||
expected_status=False,
|
||||
raises_exception=True,
|
||||
mock_response=self.mock_responses["no_records"],
|
||||
)
|
||||
124
odoo-bringout-oca-ocb-l10n_in/l10n_in/tests/test_gstr_section.py
Normal file
124
odoo-bringout-oca-ocb-l10n_in/l10n_in/tests/test_gstr_section.py
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
from datetime import date
|
||||
|
||||
from odoo import Command
|
||||
from odoo.addons.l10n_in.tests.common import L10nInTestInvoicingCommon
|
||||
from odoo.tests import tagged
|
||||
|
||||
TEST_DATE = date(2025, 6, 8)
|
||||
|
||||
|
||||
@tagged('post_install_l10n', 'post_install', '-at_install')
|
||||
class TestGstrSection(L10nInTestInvoicingCommon):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
|
||||
cls.partner_b.l10n_in_gst_treatment = "regular"
|
||||
cls.partner_foreign.l10n_in_gst_treatment = 'overseas'
|
||||
cls.sez_partner = cls.partner_a.copy({"l10n_in_gst_treatment": "special_economic_zone"})
|
||||
cls.large_unregistered_partner = cls.partner_a.copy({"state_id": cls.state_in_mh.id, "vat": None, "l10n_in_gst_treatment": "unregistered"})
|
||||
|
||||
ChartTemplate = cls.env['account.chart.template']
|
||||
cls.nil_rated_tax = ChartTemplate.ref('nil_rated_sale')
|
||||
cls.igst_lut_sale_28_sez = ChartTemplate.ref('igst_sale_28_sez_lut')
|
||||
cls.igst_rc_sale_18 = ChartTemplate.ref('igst_sale_18_rc')
|
||||
cls.igst_exp_sale_18 = ChartTemplate.ref('igst_sale_18_sez_exp')
|
||||
|
||||
def test_gstr_sections(self):
|
||||
def assert_line_sections(lines, expected_sections):
|
||||
for line in lines.filtered(lambda l: l.display_type in ('product', 'tax')):
|
||||
if line.display_type == 'product':
|
||||
tax_types = {tax.l10n_in_tax_type for tax in line.tax_ids}
|
||||
matched = tax_types & expected_sections.keys()
|
||||
if matched:
|
||||
expected = expected_sections[matched.pop()]
|
||||
else:
|
||||
expected = expected_sections.get('with_gst_tag')
|
||||
else: # tax line
|
||||
expected = expected_sections['with_gst_tag'] if line.tax_tag_ids else expected_sections['no_tag']
|
||||
self.assertEqual(line.l10n_in_gstr_section, expected)
|
||||
|
||||
# SEZ without payment with Nil Rated
|
||||
sez_invoice = self._init_inv(
|
||||
partner=self.sez_partner,
|
||||
taxes=self.igst_lut_sale_28_sez,
|
||||
line_vals={'price_unit': 1000, 'quantity': 1},
|
||||
post=False,
|
||||
invoice_date=TEST_DATE,
|
||||
)
|
||||
sez_invoice.write({
|
||||
'invoice_line_ids': [Command.create({
|
||||
'product_id': self.product_b.id,
|
||||
'account_id': sez_invoice.invoice_line_ids[0].account_id.id,
|
||||
'price_unit': 500,
|
||||
'quantity': 1,
|
||||
'tax_ids': [(6, 0, [self.nil_rated_tax.id])],
|
||||
})],
|
||||
})
|
||||
sez_invoice.action_post()
|
||||
assert_line_sections(sez_invoice.line_ids, {
|
||||
'with_gst_tag': 'sale_sez_wop',
|
||||
'nil_rated': 'sale_nil_rated',
|
||||
'no_tag': 'sale_out_of_scope',
|
||||
})
|
||||
|
||||
sez_credit_note = self._create_credit_note(inv=sez_invoice)
|
||||
assert_line_sections(sez_credit_note.line_ids, {
|
||||
'with_gst_tag': 'sale_cdnr_sez_wop',
|
||||
'nil_rated': 'sale_nil_rated',
|
||||
'no_tag': 'sale_out_of_scope',
|
||||
})
|
||||
|
||||
# Export with payment
|
||||
exp_invoice = self._init_inv(
|
||||
partner=self.partner_foreign,
|
||||
taxes=self.igst_exp_sale_18,
|
||||
line_vals={'price_unit': 3000, 'quantity': 1},
|
||||
invoice_date=TEST_DATE,
|
||||
)
|
||||
assert_line_sections(exp_invoice.line_ids, {
|
||||
'with_gst_tag': 'sale_exp_wp',
|
||||
'no_tag': 'sale_out_of_scope',
|
||||
})
|
||||
|
||||
exp_credit_note = self._create_credit_note(inv=exp_invoice)
|
||||
assert_line_sections(exp_credit_note.line_ids, {
|
||||
'with_gst_tag': 'sale_cdnur_exp_wp',
|
||||
'no_tag': 'sale_out_of_scope',
|
||||
})
|
||||
|
||||
# B2B RCM
|
||||
b2b_rcm_invoice = self._init_inv(
|
||||
partner=self.partner_b,
|
||||
taxes=self.igst_rc_sale_18,
|
||||
line_vals={'price_unit': 1000, 'quantity': 1},
|
||||
invoice_date=TEST_DATE,
|
||||
)
|
||||
assert_line_sections(b2b_rcm_invoice.line_ids, {
|
||||
'with_gst_tag': 'sale_b2b_rcm',
|
||||
'no_tag': 'sale_out_of_scope',
|
||||
})
|
||||
|
||||
b2b_rcm_credit_note = self._create_credit_note(inv=b2b_rcm_invoice)
|
||||
assert_line_sections(b2b_rcm_credit_note.line_ids, {
|
||||
'with_gst_tag': 'sale_cdnr_rcm',
|
||||
'no_tag': 'sale_out_of_scope',
|
||||
})
|
||||
|
||||
# B2CL
|
||||
b2cl_invoice = self._init_inv(
|
||||
partner=self.large_unregistered_partner,
|
||||
taxes=self.igst_sale_18,
|
||||
line_vals={'price_unit': 220000, 'quantity': 1},
|
||||
invoice_date=TEST_DATE,
|
||||
)
|
||||
assert_line_sections(b2cl_invoice.line_ids, {
|
||||
'with_gst_tag': 'sale_b2cl',
|
||||
'no_tag': 'sale_out_of_scope',
|
||||
})
|
||||
|
||||
b2cl_credit_note = self._create_credit_note(inv=b2cl_invoice, line_vals={'quantity': 0.5})
|
||||
assert_line_sections(b2cl_credit_note.line_ids, {
|
||||
'with_gst_tag': 'sale_cdnur_b2cl',
|
||||
'no_tag': 'sale_out_of_scope',
|
||||
})
|
||||
770
odoo-bringout-oca-ocb-l10n_in/l10n_in/tests/test_hsn_summary.py
Normal file
770
odoo-bringout-oca-ocb-l10n_in/l10n_in/tests/test_hsn_summary.py
Normal file
|
|
@ -0,0 +1,770 @@
|
|||
from odoo import Command
|
||||
from odoo.addons.account.tests.common import TestTaxCommon
|
||||
from odoo.tests import tagged
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install', 'post_install_l10n')
|
||||
class TestL10nInHSNSummary(TestTaxCommon):
|
||||
|
||||
@classmethod
|
||||
@TestTaxCommon.setup_country('in')
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
|
||||
cls.company_data['company'].write({
|
||||
'l10n_in_is_gst_registered': True,
|
||||
})
|
||||
|
||||
cls.test_hsn_code_1 = '1234'
|
||||
cls.test_hsn_code_2 = '4321'
|
||||
|
||||
cls.uom_unit = cls.env.ref('uom.product_uom_unit')
|
||||
cls.uom_dozen = cls.env.ref('uom.product_uom_dozen')
|
||||
|
||||
cls.product_a.l10n_in_hsn_code = cls.test_hsn_code_1
|
||||
cls.product_b.l10n_in_hsn_code = cls.test_hsn_code_2
|
||||
cls.product_c = cls.env['product.product'].create({
|
||||
'name': 'product_c',
|
||||
'l10n_in_hsn_code': cls.test_hsn_code_1,
|
||||
'uom_id': cls.env.ref('uom.product_uom_unit').id,
|
||||
'lst_price': 1000.0,
|
||||
'property_account_income_id': cls.company_data['default_account_revenue'].id,
|
||||
})
|
||||
|
||||
ChartTemplate = cls.env['account.chart.template']
|
||||
cls.gst_5 = ChartTemplate.ref('sgst_sale_5')
|
||||
cls.gst_18 = ChartTemplate.ref('sgst_sale_18')
|
||||
cls.igst_5 = ChartTemplate.ref('igst_sale_5')
|
||||
cls.igst_18 = ChartTemplate.ref('igst_sale_18')
|
||||
cls.cess_5_plus_1591 = ChartTemplate.ref('cess_5_plus_1591_sale')
|
||||
cls.nil_rated = ChartTemplate.ref('nil_rated_sale')
|
||||
cls.exempt_0 = ChartTemplate.ref('exempt_sale')
|
||||
cls.igst_18_rc = ChartTemplate.ref('igst_sale_18_rc')
|
||||
|
||||
def _jsonify_tax(self, tax):
|
||||
# EXTENDS 'account.
|
||||
values = super()._jsonify_tax(tax)
|
||||
values['l10n_in_gst_tax_type'] = tax.l10n_in_gst_tax_type
|
||||
return values
|
||||
|
||||
def _jsonify_document_line(self, document, index, line):
|
||||
# EXTENDS 'account.
|
||||
values = super()._jsonify_document_line(document, index, line)
|
||||
values['l10n_in_hsn_code'] = line['l10n_in_hsn_code']
|
||||
return values
|
||||
|
||||
def convert_base_line_to_invoice_line(self, document, base_line):
|
||||
# EXTENDS 'account.
|
||||
values = super().convert_base_line_to_invoice_line(document, base_line)
|
||||
values['l10n_in_hsn_code'] = base_line['l10n_in_hsn_code']
|
||||
return values
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# l10n_in_hsn_summary
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
def _assert_sub_test_l10n_in_hsn_summary(self, results, expected_values):
|
||||
self.assertEqual(
|
||||
{k: len(v) if k == 'items' else v for k, v in results['hsn'].items()},
|
||||
{k: len(v) if k == 'items' else v for k, v in expected_values.items()},
|
||||
)
|
||||
self.assertEqual(len(results['hsn']['items']), len(expected_values['items']))
|
||||
for item, expected_item in zip(results['hsn']['items'], expected_values['items']):
|
||||
self.assertDictEqual(item, expected_item)
|
||||
|
||||
def _create_py_sub_test_l10n_in_hsn_summary(self, document, display_uom):
|
||||
return {
|
||||
'hsn': self.env['account.tax']._l10n_in_get_hsn_summary_table(document['lines'], display_uom),
|
||||
}
|
||||
|
||||
def _create_js_sub_test_l10n_in_hsn_summary(self, document, display_uom):
|
||||
return {
|
||||
'test': 'l10n_in_hsn_summary',
|
||||
'document': self._jsonify_document(document),
|
||||
'display_uom': display_uom,
|
||||
}
|
||||
|
||||
def assert_l10n_in_hsn_summary(
|
||||
self,
|
||||
document,
|
||||
expected_values,
|
||||
display_uom=False,
|
||||
):
|
||||
self._create_assert_test(
|
||||
expected_values,
|
||||
self._create_py_sub_test_l10n_in_hsn_summary,
|
||||
self._create_js_sub_test_l10n_in_hsn_summary,
|
||||
self._assert_sub_test_l10n_in_hsn_summary,
|
||||
document,
|
||||
display_uom,
|
||||
)
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# invoice l10n_in_hsn_summary
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
def assert_invoice_l10n_in_hsn_summary(self, invoice, expected_values):
|
||||
results = {'hsn': {
|
||||
**invoice._l10n_in_get_hsn_summary_table(),
|
||||
# 'display_uom' is just checking if the user has the uom group. It's irrelevant to test it.
|
||||
'display_uom': expected_values['display_uom'],
|
||||
}}
|
||||
self._assert_sub_test_l10n_in_hsn_summary(results, expected_values)
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Tests
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
def _test_l10n_in_hsn_summary_1(self):
|
||||
""" Test GST/IGST taxes. """
|
||||
document = self.populate_document(self.init_document([
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 2.0, 'price_unit': 100.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.gst_5},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 1.0, 'price_unit': 600.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.gst_5},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 5.0, 'price_unit': 300.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.gst_5},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 2.0, 'price_unit': 100.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.gst_18},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 1.0, 'price_unit': 600.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.gst_18},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 5.0, 'price_unit': 300.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.gst_18},
|
||||
]))
|
||||
expected_values = {
|
||||
'has_igst': False,
|
||||
'has_gst': True,
|
||||
'has_cess': False,
|
||||
'nb_columns': 7,
|
||||
'display_uom': False,
|
||||
'items': [
|
||||
{
|
||||
'l10n_in_hsn_code': self.test_hsn_code_1,
|
||||
'quantity': 8.0,
|
||||
'uom_name': self.uom_unit.name,
|
||||
'rate': 5.0,
|
||||
'amount_untaxed': 2300.0,
|
||||
'tax_amount_igst': 0.0,
|
||||
'tax_amount_cgst': 57.5,
|
||||
'tax_amount_sgst': 57.5,
|
||||
'tax_amount_cess': 0.0,
|
||||
},
|
||||
{
|
||||
'l10n_in_hsn_code': self.test_hsn_code_1,
|
||||
'quantity': 8.0,
|
||||
'uom_name': self.uom_unit.name,
|
||||
'rate': 18.0,
|
||||
'amount_untaxed': 2300.0,
|
||||
'tax_amount_igst': 0.0,
|
||||
'tax_amount_cgst': 207.0,
|
||||
'tax_amount_sgst': 207.0,
|
||||
'tax_amount_cess': 0.0,
|
||||
},
|
||||
],
|
||||
}
|
||||
yield 1, document, expected_values
|
||||
|
||||
# Another UOM on the second line.
|
||||
document = self.populate_document(self.init_document([
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 2.0, 'price_unit': 100.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.gst_5},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 1.0, 'price_unit': 12000.0, 'product_uom_id': self.uom_dozen, 'tax_ids': self.gst_5},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 5.0, 'price_unit': 300.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.gst_5},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 2.0, 'price_unit': 100.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.gst_18},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 1.0, 'price_unit': 600.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.gst_18},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 5.0, 'price_unit': 300.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.gst_18},
|
||||
]))
|
||||
expected_values = {
|
||||
'has_igst': False,
|
||||
'has_gst': True,
|
||||
'has_cess': False,
|
||||
'nb_columns': 7,
|
||||
'display_uom': False,
|
||||
'items': [
|
||||
{
|
||||
'l10n_in_hsn_code': self.test_hsn_code_1,
|
||||
'quantity': 7.0,
|
||||
'uom_name': self.uom_unit.name,
|
||||
'rate': 5.0,
|
||||
'amount_untaxed': 1700.0,
|
||||
'tax_amount_igst': 0.0,
|
||||
'tax_amount_cgst': 42.5,
|
||||
'tax_amount_sgst': 42.5,
|
||||
'tax_amount_cess': 0.0,
|
||||
},
|
||||
{
|
||||
'l10n_in_hsn_code': self.test_hsn_code_1,
|
||||
'quantity': 1.0,
|
||||
'uom_name': self.uom_dozen.name,
|
||||
'rate': 5.0,
|
||||
'amount_untaxed': 12000.0,
|
||||
'tax_amount_igst': 0.0,
|
||||
'tax_amount_cgst': 300.0,
|
||||
'tax_amount_sgst': 300.0,
|
||||
'tax_amount_cess': 0.0,
|
||||
},
|
||||
{
|
||||
'l10n_in_hsn_code': self.test_hsn_code_1,
|
||||
'quantity': 8.0,
|
||||
'uom_name': self.uom_unit.name,
|
||||
'rate': 18.0,
|
||||
'amount_untaxed': 2300.0,
|
||||
'tax_amount_igst': 0.0,
|
||||
'tax_amount_cgst': 207.0,
|
||||
'tax_amount_sgst': 207.0,
|
||||
'tax_amount_cess': 0.0,
|
||||
}
|
||||
]
|
||||
}
|
||||
yield 2, document, expected_values
|
||||
|
||||
# Change GST 5% taxes to IGST.
|
||||
document = self.populate_document(self.init_document([
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 2.0, 'price_unit': 100.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.igst_5},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 1.0, 'price_unit': 12000.0, 'product_uom_id': self.uom_dozen, 'tax_ids': self.igst_5},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 5.0, 'price_unit': 300.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.igst_5},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 2.0, 'price_unit': 100.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.gst_18},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 1.0, 'price_unit': 600.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.gst_18},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 5.0, 'price_unit': 300.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.gst_18},
|
||||
]))
|
||||
expected_values = {
|
||||
'has_igst': True,
|
||||
'has_gst': True,
|
||||
'has_cess': False,
|
||||
'nb_columns': 8,
|
||||
'display_uom': False,
|
||||
'items': [
|
||||
{
|
||||
'l10n_in_hsn_code': self.test_hsn_code_1,
|
||||
'quantity': 7.0,
|
||||
'uom_name': self.uom_unit.name,
|
||||
'rate': 5.0,
|
||||
'amount_untaxed': 1700.0,
|
||||
'tax_amount_igst': 85.0,
|
||||
'tax_amount_cgst': 0.0,
|
||||
'tax_amount_sgst': 0.0,
|
||||
'tax_amount_cess': 0.0,
|
||||
},
|
||||
{
|
||||
'l10n_in_hsn_code': self.test_hsn_code_1,
|
||||
'quantity': 1.0,
|
||||
'uom_name': self.uom_dozen.name,
|
||||
'rate': 5.0,
|
||||
'amount_untaxed': 12000.0,
|
||||
'tax_amount_igst': 600.0,
|
||||
'tax_amount_cgst': 0.0,
|
||||
'tax_amount_sgst': 0.0,
|
||||
'tax_amount_cess': 0.0,
|
||||
},
|
||||
{
|
||||
'l10n_in_hsn_code': self.test_hsn_code_1,
|
||||
'quantity': 8.0,
|
||||
'uom_name': self.uom_unit.name,
|
||||
'rate': 18.0,
|
||||
'amount_untaxed': 2300.0,
|
||||
'tax_amount_igst': 0.0,
|
||||
'tax_amount_cgst': 207.0,
|
||||
'tax_amount_sgst': 207.0,
|
||||
'tax_amount_cess': 0.0,
|
||||
},
|
||||
],
|
||||
}
|
||||
yield 3, document, expected_values
|
||||
|
||||
# Put back the UOM of the second line to unit.
|
||||
document = self.populate_document(self.init_document([
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 2.0, 'price_unit': 100.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.igst_5},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 1.0, 'price_unit': 600.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.igst_5},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 5.0, 'price_unit': 300.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.igst_5},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 2.0, 'price_unit': 100.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.gst_18},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 1.0, 'price_unit': 600.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.gst_18},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 5.0, 'price_unit': 300.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.gst_18},
|
||||
]))
|
||||
expected_values = {
|
||||
'has_igst': True,
|
||||
'has_gst': True,
|
||||
'has_cess': False,
|
||||
'nb_columns': 8,
|
||||
'display_uom': False,
|
||||
'items': [
|
||||
{
|
||||
'l10n_in_hsn_code': self.test_hsn_code_1,
|
||||
'quantity': 8.0,
|
||||
'uom_name': self.uom_unit.name,
|
||||
'rate': 5.0,
|
||||
'amount_untaxed': 2300.0,
|
||||
'tax_amount_igst': 115.0,
|
||||
'tax_amount_cgst': 0.0,
|
||||
'tax_amount_sgst': 0.0,
|
||||
'tax_amount_cess': 0.0,
|
||||
},
|
||||
{
|
||||
'l10n_in_hsn_code': self.test_hsn_code_1,
|
||||
'quantity': 8.0,
|
||||
'uom_name': self.uom_unit.name,
|
||||
'rate': 18.0,
|
||||
'amount_untaxed': 2300.0,
|
||||
'tax_amount_igst': 0.0,
|
||||
'tax_amount_cgst': 207.0,
|
||||
'tax_amount_sgst': 207.0,
|
||||
'tax_amount_cess': 0.0,
|
||||
},
|
||||
],
|
||||
}
|
||||
yield 4, document, expected_values
|
||||
|
||||
# Change GST 18% taxes to IGST.
|
||||
document = self.populate_document(self.init_document([
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 2.0, 'price_unit': 100.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.igst_5},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 1.0, 'price_unit': 600.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.igst_5},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 5.0, 'price_unit': 300.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.igst_5},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 2.0, 'price_unit': 100.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.igst_18},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 1.0, 'price_unit': 600.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.igst_18},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 5.0, 'price_unit': 300.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.igst_18},
|
||||
]))
|
||||
expected_values = {
|
||||
'has_igst': True,
|
||||
'has_gst': False,
|
||||
'has_cess': False,
|
||||
'nb_columns': 6,
|
||||
'display_uom': False,
|
||||
'items': [
|
||||
{
|
||||
'l10n_in_hsn_code': self.test_hsn_code_1,
|
||||
'quantity': 8.0,
|
||||
'uom_name': self.uom_unit.name,
|
||||
'rate': 5.0,
|
||||
'amount_untaxed': 2300.0,
|
||||
'tax_amount_igst': 115.0,
|
||||
'tax_amount_cgst': 0.0,
|
||||
'tax_amount_sgst': 0.0,
|
||||
'tax_amount_cess': 0.0,
|
||||
},
|
||||
{
|
||||
'l10n_in_hsn_code': self.test_hsn_code_1,
|
||||
'quantity': 8.0,
|
||||
'uom_name': self.uom_unit.name,
|
||||
'rate': 18.0,
|
||||
'amount_untaxed': 2300.0,
|
||||
'tax_amount_igst': 414.0,
|
||||
'tax_amount_cgst': 0.0,
|
||||
'tax_amount_sgst': 0.0,
|
||||
'tax_amount_cess': 0.0,
|
||||
},
|
||||
],
|
||||
}
|
||||
yield 5, document, expected_values
|
||||
|
||||
def test_l10n_in_hsn_summary_1_generic_helpers(self):
|
||||
for test_index, document, expected_values in self._test_l10n_in_hsn_summary_1():
|
||||
with self.subTest(test_index=test_index):
|
||||
self.assert_l10n_in_hsn_summary(document, expected_values)
|
||||
self._run_js_tests()
|
||||
|
||||
def test_l10n_in_hsn_summary_1_invoices(self):
|
||||
for test_index, document, expected_values in self._test_l10n_in_hsn_summary_1():
|
||||
with self.subTest(test_index=test_index):
|
||||
invoice = self.convert_document_to_invoice(document)
|
||||
self.assert_invoice_l10n_in_hsn_summary(invoice, expected_values)
|
||||
|
||||
def _test_l10n_in_hsn_summary_2(self):
|
||||
""" Test CESS taxes in combination with GST/IGST. """
|
||||
# Need the tax to be evaluated at the end.
|
||||
self.cess_5_plus_1591.sequence = 100
|
||||
|
||||
document = self.populate_document(self.init_document([
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 1.0, 'price_unit': 15.80, 'product_uom_id': self.uom_unit, 'tax_ids': self.gst_18 + self.cess_5_plus_1591},
|
||||
]))
|
||||
expected_values = {
|
||||
'has_igst': False,
|
||||
'has_gst': True,
|
||||
'has_cess': True,
|
||||
'nb_columns': 8,
|
||||
'display_uom': False,
|
||||
'items': [
|
||||
{
|
||||
'l10n_in_hsn_code': self.test_hsn_code_1,
|
||||
'quantity': 1.0,
|
||||
'uom_name': self.uom_unit.name,
|
||||
'rate': 18.0,
|
||||
'amount_untaxed': 15.8,
|
||||
'tax_amount_igst': 0.0,
|
||||
'tax_amount_cgst': 1.42,
|
||||
'tax_amount_sgst': 1.42,
|
||||
'tax_amount_cess': 2.38,
|
||||
},
|
||||
],
|
||||
}
|
||||
yield 1, document, expected_values
|
||||
|
||||
# Change GST 18% taxes to IGST.
|
||||
document = self.populate_document(self.init_document([
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 1.0, 'price_unit': 15.80, 'product_uom_id': self.uom_unit, 'tax_ids': self.igst_18 + self.cess_5_plus_1591},
|
||||
]))
|
||||
expected_values = {
|
||||
'has_igst': True,
|
||||
'has_gst': False,
|
||||
'has_cess': True,
|
||||
'nb_columns': 7,
|
||||
'display_uom': False,
|
||||
'items': [
|
||||
{
|
||||
'l10n_in_hsn_code': self.test_hsn_code_1,
|
||||
'quantity': 1.0,
|
||||
'uom_name': self.uom_unit.name,
|
||||
'rate': 18.0,
|
||||
'amount_untaxed': 15.8,
|
||||
'tax_amount_igst': 2.84,
|
||||
'tax_amount_cgst': 0.0,
|
||||
'tax_amount_sgst': 0.0,
|
||||
'tax_amount_cess': 2.38,
|
||||
},
|
||||
],
|
||||
}
|
||||
yield 2, document, expected_values
|
||||
|
||||
def test_l10n_in_hsn_summary_2_generic_helpers(self):
|
||||
for test_index, document, expected_values in self._test_l10n_in_hsn_summary_2():
|
||||
with self.subTest(test_index=test_index):
|
||||
self.assert_l10n_in_hsn_summary(document, expected_values)
|
||||
self._run_js_tests()
|
||||
|
||||
def test_l10n_in_hsn_summary_2_invoices(self):
|
||||
for test_index, document, expected_values in self._test_l10n_in_hsn_summary_2():
|
||||
with self.subTest(test_index=test_index):
|
||||
invoice = self.convert_document_to_invoice(document)
|
||||
self.assert_invoice_l10n_in_hsn_summary(invoice, expected_values)
|
||||
|
||||
def _test_l10n_in_hsn_summary_3(self):
|
||||
""" Test with mixed HSN codes. """
|
||||
document = self.populate_document(self.init_document([
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 1.0, 'price_unit': 100.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.gst_18},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 2.0, 'price_unit': 50.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.gst_18},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_2, 'quantity': 1.0, 'price_unit': 100.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.gst_18},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_2, 'quantity': 2.0, 'price_unit': 50.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.gst_18},
|
||||
]))
|
||||
expected_values = {
|
||||
'has_igst': False,
|
||||
'has_gst': True,
|
||||
'has_cess': False,
|
||||
'nb_columns': 7,
|
||||
'display_uom': False,
|
||||
'items': [
|
||||
{
|
||||
'l10n_in_hsn_code': self.test_hsn_code_1,
|
||||
'quantity': 3.0,
|
||||
'uom_name': self.uom_unit.name,
|
||||
'rate': 18.0,
|
||||
'amount_untaxed': 200.0,
|
||||
'tax_amount_igst': 0.0,
|
||||
'tax_amount_cgst': 18.0,
|
||||
'tax_amount_sgst': 18.0,
|
||||
'tax_amount_cess': 0.0,
|
||||
},
|
||||
{
|
||||
'l10n_in_hsn_code': self.test_hsn_code_2,
|
||||
'quantity': 3.0,
|
||||
'uom_name': self.uom_unit.name,
|
||||
'rate': 18.0,
|
||||
'amount_untaxed': 200.0,
|
||||
'tax_amount_igst': 0.0,
|
||||
'tax_amount_cgst': 18.0,
|
||||
'tax_amount_sgst': 18.0,
|
||||
'tax_amount_cess': 0.0,
|
||||
},
|
||||
],
|
||||
}
|
||||
yield 1, document, expected_values
|
||||
|
||||
# Change GST 18% taxes to IGST.
|
||||
document = self.populate_document(self.init_document([
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 1.0, 'price_unit': 100.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.igst_18},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 2.0, 'price_unit': 50.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.igst_18},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_2, 'quantity': 1.0, 'price_unit': 100.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.igst_18},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_2, 'quantity': 2.0, 'price_unit': 50.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.igst_18},
|
||||
]))
|
||||
expected_values = {
|
||||
'has_igst': True,
|
||||
'has_gst': False,
|
||||
'has_cess': False,
|
||||
'nb_columns': 6,
|
||||
'display_uom': False,
|
||||
'items': [
|
||||
{
|
||||
'l10n_in_hsn_code': self.test_hsn_code_1,
|
||||
'quantity': 3.0,
|
||||
'uom_name': self.uom_unit.name,
|
||||
'rate': 18.0,
|
||||
'amount_untaxed': 200.0,
|
||||
'tax_amount_igst': 36.0,
|
||||
'tax_amount_cgst': 0.0,
|
||||
'tax_amount_sgst': 0.0,
|
||||
'tax_amount_cess': 0.0,
|
||||
},
|
||||
{
|
||||
'l10n_in_hsn_code': self.test_hsn_code_2,
|
||||
'quantity': 3.0,
|
||||
'uom_name': self.uom_unit.name,
|
||||
'rate': 18.0,
|
||||
'amount_untaxed': 200.0,
|
||||
'tax_amount_igst': 36.0,
|
||||
'tax_amount_cgst': 0.0,
|
||||
'tax_amount_sgst': 0.0,
|
||||
'tax_amount_cess': 0.0,
|
||||
},
|
||||
],
|
||||
}
|
||||
yield 2, document, expected_values
|
||||
|
||||
def test_l10n_in_hsn_summary_3_generic_helpers(self):
|
||||
for test_index, document, expected_values in self._test_l10n_in_hsn_summary_3():
|
||||
with self.subTest(test_index=test_index):
|
||||
self.assert_l10n_in_hsn_summary(document, expected_values)
|
||||
self._run_js_tests()
|
||||
|
||||
def test_l10n_in_hsn_summary_3_invoices(self):
|
||||
for test_index, document, expected_values in self._test_l10n_in_hsn_summary_3():
|
||||
with self.subTest(test_index=test_index):
|
||||
invoice = self.convert_document_to_invoice(document)
|
||||
self.assert_invoice_l10n_in_hsn_summary(invoice, expected_values)
|
||||
|
||||
def _test_l10n_in_hsn_summary_4(self):
|
||||
""" Zero rated GST or no taxes at all."""
|
||||
document = self.populate_document(self.init_document([
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 1.0, 'price_unit': 350.0, 'product_uom_id': self.uom_unit},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 1.0, 'price_unit': 350.0, 'product_uom_id': self.uom_unit},
|
||||
]))
|
||||
expected_values = {
|
||||
'has_igst': False,
|
||||
'has_gst': False,
|
||||
'has_cess': False,
|
||||
'nb_columns': 5,
|
||||
'display_uom': False,
|
||||
'items': [
|
||||
{
|
||||
'l10n_in_hsn_code': self.test_hsn_code_1,
|
||||
'quantity': 2.0,
|
||||
'uom_name': self.uom_unit.name,
|
||||
'rate': 0.0,
|
||||
'amount_untaxed': 700.0,
|
||||
'tax_amount_igst': 0.0,
|
||||
'tax_amount_cgst': 0.0,
|
||||
'tax_amount_sgst': 0.0,
|
||||
'tax_amount_cess': 0.0,
|
||||
},
|
||||
],
|
||||
}
|
||||
yield 1, document, expected_values
|
||||
|
||||
# No tax to Nil Rated/exempt.
|
||||
document = self.populate_document(self.init_document([
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 1.0, 'price_unit': 350.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.nil_rated},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 1.0, 'price_unit': 350.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.exempt_0},
|
||||
]))
|
||||
expected_values = {
|
||||
'has_igst': False,
|
||||
'has_gst': False,
|
||||
'has_cess': False,
|
||||
'nb_columns': 5,
|
||||
'display_uom': False,
|
||||
'items': [
|
||||
{
|
||||
'l10n_in_hsn_code': self.test_hsn_code_1,
|
||||
'quantity': 2.0,
|
||||
'uom_name': self.uom_unit.name,
|
||||
'rate': 0.0,
|
||||
'amount_untaxed': 700.0,
|
||||
'tax_amount_igst': 0.0,
|
||||
'tax_amount_cgst': 0.0,
|
||||
'tax_amount_sgst': 0.0,
|
||||
'tax_amount_cess': 0.0,
|
||||
},
|
||||
],
|
||||
}
|
||||
yield 2, document, expected_values
|
||||
|
||||
# Put one IGST 18% to get a value on the IGST column.
|
||||
document = self.populate_document(self.init_document([
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 1.0, 'price_unit': 350.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.igst_18},
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 1.0, 'price_unit': 350.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.exempt_0},
|
||||
]))
|
||||
expected_values = {
|
||||
'has_igst': True,
|
||||
'has_gst': False,
|
||||
'has_cess': False,
|
||||
'nb_columns': 6,
|
||||
'display_uom': False,
|
||||
'items': [
|
||||
{
|
||||
'l10n_in_hsn_code': self.test_hsn_code_1,
|
||||
'quantity': 1.0,
|
||||
'uom_name': self.uom_unit.name,
|
||||
'rate': 18.0,
|
||||
'amount_untaxed': 350.0,
|
||||
'tax_amount_igst': 63.0,
|
||||
'tax_amount_cgst': 0.0,
|
||||
'tax_amount_sgst': 0.0,
|
||||
'tax_amount_cess': 0.0,
|
||||
},
|
||||
{
|
||||
'l10n_in_hsn_code': self.test_hsn_code_1,
|
||||
'quantity': 1.0,
|
||||
'uom_name': self.uom_unit.name,
|
||||
'rate': 0.0,
|
||||
'amount_untaxed': 350.0,
|
||||
'tax_amount_igst': 0.0,
|
||||
'tax_amount_cgst': 0.0,
|
||||
'tax_amount_sgst': 0.0,
|
||||
'tax_amount_cess': 0.0,
|
||||
},
|
||||
],
|
||||
}
|
||||
yield 3, document, expected_values
|
||||
|
||||
def test_l10n_in_hsn_summary_4_generic_helpers(self):
|
||||
for test_index, document, expected_values in self._test_l10n_in_hsn_summary_4():
|
||||
with self.subTest(test_index=test_index):
|
||||
self.assert_l10n_in_hsn_summary(document, expected_values)
|
||||
self._run_js_tests()
|
||||
|
||||
def test_l10n_in_hsn_summary_4_invoices(self):
|
||||
for test_index, document, expected_values in self._test_l10n_in_hsn_summary_4():
|
||||
with self.subTest(test_index=test_index):
|
||||
invoice = self.convert_document_to_invoice(document)
|
||||
self.assert_invoice_l10n_in_hsn_summary(invoice, expected_values)
|
||||
|
||||
def _test_l10n_in_hsn_summary_5(self):
|
||||
""" Test with discount. """
|
||||
document = self.populate_document(self.init_document([
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 1.0, 'price_unit': 100.0, 'discount': 10.0, 'product_uom_id': self.uom_unit},
|
||||
]))
|
||||
expected_values = {
|
||||
'has_igst': False,
|
||||
'has_gst': False,
|
||||
'has_cess': False,
|
||||
'nb_columns': 5,
|
||||
'display_uom': False,
|
||||
'items': [
|
||||
{
|
||||
'l10n_in_hsn_code': self.test_hsn_code_1,
|
||||
'quantity': 1.0,
|
||||
'uom_name': self.uom_unit.name,
|
||||
'rate': 0.0,
|
||||
'amount_untaxed': 90.0,
|
||||
'tax_amount_igst': 0.0,
|
||||
'tax_amount_cgst': 0.0,
|
||||
'tax_amount_sgst': 0.0,
|
||||
'tax_amount_cess': 0.0,
|
||||
},
|
||||
],
|
||||
}
|
||||
yield 1, document, expected_values
|
||||
|
||||
def test_l10n_in_hsn_summary_5_generic_helpers(self):
|
||||
for test_index, document, expected_values in self._test_l10n_in_hsn_summary_5():
|
||||
with self.subTest(test_index=test_index):
|
||||
self.assert_l10n_in_hsn_summary(document, expected_values)
|
||||
self._run_js_tests()
|
||||
|
||||
def test_l10n_in_hsn_summary_5_invoices(self):
|
||||
for test_index, document, expected_values in self._test_l10n_in_hsn_summary_5():
|
||||
with self.subTest(test_index=test_index):
|
||||
invoice = self.convert_document_to_invoice(document)
|
||||
self.assert_invoice_l10n_in_hsn_summary(invoice, expected_values)
|
||||
|
||||
def _test_l10n_in_hsn_summary_6(self):
|
||||
""" Test with Sale RC tax. """
|
||||
document = self.populate_document(self.init_document([
|
||||
{'l10n_in_hsn_code': self.test_hsn_code_1, 'quantity': 1.0, 'price_unit': 100.0, 'product_uom_id': self.uom_unit, 'tax_ids': self.igst_18_rc},
|
||||
]))
|
||||
expected_values = {
|
||||
'has_igst': True,
|
||||
'has_gst': False,
|
||||
'has_cess': False,
|
||||
'nb_columns': 6,
|
||||
'display_uom': False,
|
||||
'items': [
|
||||
{
|
||||
'l10n_in_hsn_code': self.test_hsn_code_1,
|
||||
'quantity': 1.0,
|
||||
'uom_name': self.uom_unit.name,
|
||||
'rate': 18.0,
|
||||
'amount_untaxed': 100.0,
|
||||
'tax_amount_igst': 0.0,
|
||||
'tax_amount_cgst': 0.0,
|
||||
'tax_amount_sgst': 0.0,
|
||||
'tax_amount_cess': 0.0,
|
||||
},
|
||||
],
|
||||
}
|
||||
yield 1, document, expected_values
|
||||
|
||||
def test_l10n_in_hsn_summary_6_generic_helpers(self):
|
||||
for test_index, document, expected_values in self._test_l10n_in_hsn_summary_6():
|
||||
with self.subTest(test_index=test_index):
|
||||
self.assert_l10n_in_hsn_summary(document, expected_values)
|
||||
self._run_js_tests()
|
||||
|
||||
def test_l10n_in_hsn_summary_6_invoices(self):
|
||||
for test_index, document, expected_values in self._test_l10n_in_hsn_summary_6():
|
||||
with self.subTest(test_index=test_index):
|
||||
invoice = self.convert_document_to_invoice(document)
|
||||
self.assert_invoice_l10n_in_hsn_summary(invoice, expected_values)
|
||||
|
||||
def test_l10n_in_hsn_summary_manual_edit_invoice_taxes(self):
|
||||
invoice = self.env['account.move'].create({
|
||||
'move_type': 'out_invoice',
|
||||
'invoice_date': '2017-01-01',
|
||||
'partner_id': self.partner_a.id,
|
||||
'invoice_line_ids': [
|
||||
Command.create({
|
||||
'product_id': self.product_a.id,
|
||||
'l10n_in_hsn_code': self.test_hsn_code_1,
|
||||
'price_unit': 1000.0,
|
||||
'tax_ids': [Command.set(self.gst_5.ids)],
|
||||
}),
|
||||
Command.create({
|
||||
'product_id': self.product_a.id,
|
||||
'l10n_in_hsn_code': self.test_hsn_code_2,
|
||||
'price_unit': 1000.0,
|
||||
'tax_ids': [Command.set(self.gst_5.ids)],
|
||||
}),
|
||||
],
|
||||
})
|
||||
|
||||
# Manual edition of the tax.
|
||||
sgst_tax = self.gst_5.children_tax_ids.filtered(lambda tax: tax.l10n_in_gst_tax_type == 'sgst')
|
||||
cgst_tax = self.gst_5.children_tax_ids.filtered(lambda tax: tax.l10n_in_gst_tax_type == 'cgst')
|
||||
tax_line_sgst = invoice.line_ids.filtered(lambda aml: aml.tax_line_id == sgst_tax)
|
||||
tax_line_cgst = invoice.line_ids.filtered(lambda aml: aml.tax_line_id == cgst_tax)
|
||||
payment_term = invoice.line_ids.filtered(lambda aml: aml.display_type == 'payment_term')
|
||||
invoice.line_ids = [
|
||||
Command.update(tax_line_sgst.id, {'amount_currency': tax_line_sgst.amount_currency + 1.0}),
|
||||
Command.update(tax_line_cgst.id, {'amount_currency': tax_line_cgst.amount_currency + 1.0}),
|
||||
Command.update(payment_term.id, {'amount_currency': payment_term.amount_currency - 2.0}),
|
||||
]
|
||||
|
||||
self.assert_invoice_l10n_in_hsn_summary(invoice, {
|
||||
'has_igst': False,
|
||||
'has_gst': True,
|
||||
'has_cess': False,
|
||||
'nb_columns': 7,
|
||||
'display_uom': False,
|
||||
'items': [
|
||||
{
|
||||
'l10n_in_hsn_code': self.test_hsn_code_1,
|
||||
'quantity': 1.0,
|
||||
'uom_name': self.uom_unit.name,
|
||||
'rate': 5.0,
|
||||
'amount_untaxed': 1000.0,
|
||||
'tax_amount_igst': 0.0,
|
||||
'tax_amount_cgst': 24.5,
|
||||
'tax_amount_sgst': 24.5,
|
||||
'tax_amount_cess': 0.0,
|
||||
},
|
||||
{
|
||||
'l10n_in_hsn_code': self.test_hsn_code_2,
|
||||
'quantity': 1.0,
|
||||
'uom_name': self.uom_unit.name,
|
||||
'rate': 5.0,
|
||||
'amount_untaxed': 1000.0,
|
||||
'tax_amount_igst': 0.0,
|
||||
'tax_amount_cgst': 24.5,
|
||||
'tax_amount_sgst': 24.5,
|
||||
'tax_amount_cess': 0.0,
|
||||
},
|
||||
],
|
||||
})
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
from odoo import Command
|
||||
from odoo.addons.l10n_in.tests.common import L10nInTestInvoicingCommon
|
||||
from odoo.tests import tagged
|
||||
|
||||
|
||||
@tagged('post_install_l10n', 'post_install', '-at_install')
|
||||
class TestInvoiceLabel(L10nInTestInvoicingCommon):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
|
||||
cls.partner_b.l10n_in_gst_treatment = "regular"
|
||||
cls.unregistered_partner = cls.partner_a.copy({"state_id": cls.state_in_mh.id, "vat": None, "l10n_in_gst_treatment": "unregistered"})
|
||||
|
||||
def test_invoice_label(self):
|
||||
# Regular with taxable items
|
||||
regular_taxable_invoice = self._init_inv(
|
||||
partner=self.partner_b,
|
||||
taxes=self.igst_sale_18,
|
||||
line_vals={'price_unit': 1000, 'quantity': 1},
|
||||
)
|
||||
invoice_label = regular_taxable_invoice._get_l10n_in_invoice_label()
|
||||
self.assertEqual(invoice_label, 'Tax Invoice')
|
||||
|
||||
# Regular with exempt items
|
||||
regular_exempt_invoice = self._init_inv(
|
||||
partner=self.partner_b,
|
||||
taxes=self.exempt,
|
||||
line_vals={'price_unit': 1000, 'quantity': 1},
|
||||
)
|
||||
invoice_label = regular_exempt_invoice._get_l10n_in_invoice_label()
|
||||
self.assertEqual(invoice_label, 'Bill of Supply')
|
||||
|
||||
# Regular with taxable and exempt items
|
||||
regular_mix_invoice = self._init_inv(
|
||||
partner=self.partner_b,
|
||||
taxes=self.igst_sale_18,
|
||||
line_vals={'price_unit': 1000, 'quantity': 1},
|
||||
post=False,
|
||||
)
|
||||
regular_mix_invoice.write({
|
||||
'invoice_line_ids': [Command.create({
|
||||
'product_id': self.product_b.id,
|
||||
'account_id': regular_mix_invoice.invoice_line_ids[0].account_id.id,
|
||||
'price_unit': 500,
|
||||
'quantity': 1,
|
||||
'tax_ids': [(6, 0, [self.exempt.id])],
|
||||
})],
|
||||
})
|
||||
regular_mix_invoice.action_post()
|
||||
invoice_label = regular_mix_invoice._get_l10n_in_invoice_label()
|
||||
self.assertEqual(invoice_label, 'Invoice')
|
||||
|
||||
# unregistered with taxable and exempt items
|
||||
unregistered_invoice = self._init_inv(
|
||||
partner=self.unregistered_partner,
|
||||
taxes=self.igst_sale_18,
|
||||
line_vals={'price_unit': 220000, 'quantity': 1},
|
||||
post=False,
|
||||
)
|
||||
unregistered_invoice.write({
|
||||
'invoice_line_ids': [Command.create({
|
||||
'product_id': self.product_b.id,
|
||||
'account_id': unregistered_invoice.invoice_line_ids[0].account_id.id,
|
||||
'price_unit': 500,
|
||||
'quantity': 1,
|
||||
'tax_ids': [(6, 0, [self.exempt.id])],
|
||||
})],
|
||||
})
|
||||
unregistered_invoice.action_post()
|
||||
invoice_label = unregistered_invoice._get_l10n_in_invoice_label()
|
||||
self.assertEqual(invoice_label, 'Invoice-cum-Bill of Supply')
|
||||
|
|
@ -0,0 +1,242 @@
|
|||
from contextlib import contextmanager
|
||||
from unittest.mock import patch
|
||||
|
||||
from odoo.tests import tagged
|
||||
|
||||
from odoo.addons.l10n_in.tests.common import L10nInTestInvoicingCommon
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install', 'post_install_l10n')
|
||||
class TestFiscal(L10nInTestInvoicingCommon):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
|
||||
# Remove fiscal position 'fiscal_pos_a' so it does not show up on invoice
|
||||
cls.partner_b.property_account_position_id = None
|
||||
|
||||
@contextmanager
|
||||
def _set_env_company(self, companies):
|
||||
cls = self.__class__
|
||||
env = cls.env(context=dict(cls.env.context, allowed_company_ids=companies.ids))
|
||||
with patch.object(cls, "env", env):
|
||||
yield
|
||||
|
||||
def _assert_in_intra_state_fiscal_with_company(self, companies):
|
||||
for company in companies:
|
||||
state = company.state_id
|
||||
name = state and 'Within %s' % state.name or 'Intra State'
|
||||
self.assertRecordValues(
|
||||
self.env['account.chart.template'].with_company(company).ref('fiscal_position_in_intra_state'),
|
||||
[{
|
||||
'name': name,
|
||||
'state_ids': state.ids,
|
||||
'company_id': company.id, # To make sure for branches don't get parent fiscal position
|
||||
'auto_apply': True
|
||||
}]
|
||||
)
|
||||
|
||||
def _assert_invoice_fiscal_position(self, fiscal_position_ref, partner, taxes=None, move_type='out_invoice', post=True):
|
||||
test_invoice = self.init_invoice(
|
||||
move_type=move_type,
|
||||
partner=partner,
|
||||
post=post,
|
||||
amounts=[110, 500],
|
||||
taxes=taxes
|
||||
)
|
||||
self.assertEqual(test_invoice.fiscal_position_id, self.env['account.chart.template'].ref(fiscal_position_ref))
|
||||
return test_invoice
|
||||
|
||||
def test_l10n_in_setting_up_company(self):
|
||||
company = self._create_company(name='Fiscal Setup Test Company')
|
||||
# Test with no state but country india
|
||||
self._assert_in_intra_state_fiscal_with_company(company)
|
||||
# Change state
|
||||
company.write({'state_id': self.default_company.state_id.id})
|
||||
self._assert_in_intra_state_fiscal_with_company(company)
|
||||
# Change State Again
|
||||
company.write({'state_id': self.env.ref('base.state_in_ap')})
|
||||
self._assert_in_intra_state_fiscal_with_company(company)
|
||||
|
||||
def test_l10n_in_auto_apply_fiscal_invoices(self):
|
||||
self._assert_in_intra_state_fiscal_with_company(self.default_company)
|
||||
|
||||
# Intra State
|
||||
self._assert_invoice_fiscal_position(
|
||||
fiscal_position_ref='fiscal_position_in_intra_state',
|
||||
partner=self.partner_a,
|
||||
taxes=self.sgst_sale_18,
|
||||
)
|
||||
# Inter State
|
||||
self._assert_invoice_fiscal_position(
|
||||
fiscal_position_ref='fiscal_position_in_inter_state',
|
||||
partner=self.partner_b,
|
||||
taxes=self.igst_sale_18,
|
||||
)
|
||||
# Outside India
|
||||
self._assert_invoice_fiscal_position(
|
||||
fiscal_position_ref='fiscal_position_in_export_sez_in',
|
||||
partner=self.partner_foreign,
|
||||
taxes=self.igst_sale_18,
|
||||
)
|
||||
|
||||
def test_l10n_in_fiscal_for_branch(self):
|
||||
branch_1 = self._create_company(
|
||||
name='Branch 1',
|
||||
parent_id=self.default_company.id,
|
||||
state_id=self.partner_b.state_id.id, # Setting Partner B state will be now Intra State for branch 1
|
||||
account_fiscal_country_id=self.country_in.id,
|
||||
)
|
||||
with self._set_env_company(self.outside_in_company):
|
||||
branch_2 = self.env['res.company'].create({
|
||||
'name': 'Branch 2',
|
||||
'parent_id': self.default_company.id,
|
||||
'account_fiscal_country_id': self.country_in.id,
|
||||
'country_id': self.country_in.id,
|
||||
})
|
||||
# Check Branch with country india and no state
|
||||
self._assert_in_intra_state_fiscal_with_company(branch_2)
|
||||
# Set state after creating branch
|
||||
branch_2.write({'state_id': self.env.ref('base.state_in_mp').id})
|
||||
self._assert_in_intra_state_fiscal_with_company(branch_1 + branch_2)
|
||||
# Invoice fiscal test with branch
|
||||
with self._set_env_company(branch_1):
|
||||
self._assert_invoice_fiscal_position(
|
||||
fiscal_position_ref='fiscal_position_in_intra_state',
|
||||
partner=self.partner_b,
|
||||
taxes=self.sgst_sale_18,
|
||||
)
|
||||
self._assert_invoice_fiscal_position(
|
||||
fiscal_position_ref='fiscal_position_in_inter_state',
|
||||
partner=self.partner_a,
|
||||
taxes=self.igst_sale_18,
|
||||
)
|
||||
self._assert_invoice_fiscal_position(
|
||||
fiscal_position_ref='fiscal_position_in_export_sez_in',
|
||||
partner=self.partner_foreign,
|
||||
taxes=self.igst_sale_18,
|
||||
)
|
||||
|
||||
def test_l10n_in_fiscal_in_bill_to_ship_to(self):
|
||||
with self._set_env_company(self.default_company):
|
||||
# Inter State
|
||||
out_invoice = self._assert_invoice_fiscal_position(
|
||||
fiscal_position_ref='fiscal_position_in_inter_state',
|
||||
partner=self.partner_b,
|
||||
taxes=self.igst_sale_18,
|
||||
post=False,
|
||||
)
|
||||
# Intra State
|
||||
out_invoice.write({
|
||||
'l10n_in_state_id': self.env.ref('base.state_in_gj').id,
|
||||
})
|
||||
self.assertEqual(
|
||||
out_invoice.fiscal_position_id,
|
||||
self.env['account.chart.template'].ref('fiscal_position_in_intra_state')
|
||||
)
|
||||
# Outside India (Export/SEZ)
|
||||
out_invoice.write({
|
||||
'l10n_in_state_id': self.env.ref('l10n_in.state_in_oc').id, # Other Country State
|
||||
})
|
||||
self.assertEqual(
|
||||
out_invoice.fiscal_position_id,
|
||||
self.env['account.chart.template'].ref('fiscal_position_in_export_sez_in')
|
||||
)
|
||||
|
||||
def test_l10n_in_fiscal_in_vendor_bills(self):
|
||||
'''
|
||||
In Purchase Document: Compare place of supply with vendor state
|
||||
'''
|
||||
|
||||
with self._set_env_company(self.default_company):
|
||||
template = self.env['account.chart.template']
|
||||
company_state = self.env.company.state_id
|
||||
other_state = self.env['res.country.state'].search([
|
||||
('id', '!=', company_state.id),
|
||||
('country_id', '=', company_state.country_id.id)
|
||||
], limit=1)
|
||||
|
||||
# Sub-test: Intra-State
|
||||
with self.subTest(scenario="Intra-State"):
|
||||
self.partner_a.write({'state_id': company_state.id})
|
||||
vendor_bill = self._assert_invoice_fiscal_position(
|
||||
fiscal_position_ref='fiscal_position_in_intra_state',
|
||||
partner=self.partner_a,
|
||||
move_type='in_invoice',
|
||||
post=False,
|
||||
)
|
||||
self.partner_a.write({'state_id': other_state.id})
|
||||
vendor_bill.write({'l10n_in_state_id': other_state.id})
|
||||
self.assertEqual(
|
||||
vendor_bill.fiscal_position_id,
|
||||
template.ref('fiscal_position_in_intra_state')
|
||||
)
|
||||
|
||||
# Sub-test: Inter-State
|
||||
with self.subTest(scenario="Inter-State"):
|
||||
self.partner_a.write({'state_id': other_state.id})
|
||||
vendor_bill = self._assert_invoice_fiscal_position(
|
||||
fiscal_position_ref='fiscal_position_in_inter_state',
|
||||
partner=self.partner_a,
|
||||
move_type='in_invoice',
|
||||
post=False,
|
||||
)
|
||||
self.partner_a.write({'state_id': company_state.id})
|
||||
vendor_bill.write({'l10n_in_state_id': other_state.id})
|
||||
self.assertEqual(
|
||||
vendor_bill.fiscal_position_id,
|
||||
template.ref('fiscal_position_in_inter_state')
|
||||
)
|
||||
|
||||
# Sub-test: Export/SEZ (Outside India)
|
||||
with self.subTest(scenario="Export/SEZ"):
|
||||
vendor_bill = self._assert_invoice_fiscal_position(
|
||||
fiscal_position_ref='fiscal_position_in_export_sez_in',
|
||||
partner=self.partner_foreign,
|
||||
move_type='in_invoice',
|
||||
post=False,
|
||||
)
|
||||
vendor_bill.write({'l10n_in_state_id': self.env.ref('l10n_in.state_in_oc').id}) # Other Country State
|
||||
self.assertEqual(
|
||||
vendor_bill.fiscal_position_id,
|
||||
template.ref('fiscal_position_in_export_sez_in')
|
||||
)
|
||||
# Here fpos should Inter-State. But due to `l10n_in_gst_treatment` it will be Export/SEZ
|
||||
self.partner_a.write({'state_id': other_state.id})
|
||||
vendor_bill.write({
|
||||
'partner_id': self.partner_a.id, # Inter-State Partner
|
||||
'l10n_in_state_id': company_state.id, # Company State
|
||||
'l10n_in_gst_treatment': 'special_economic_zone',
|
||||
})
|
||||
self.assertEqual(
|
||||
vendor_bill.fiscal_position_id,
|
||||
template.ref('fiscal_position_in_sez')
|
||||
)
|
||||
|
||||
# Sub-test: Manual Partner Fiscal Check
|
||||
with self.subTest(scenario="Manual Partner Fiscal Check"):
|
||||
# Here fpos should be Intra-State. But due to `property_account_position_id` it will be Export/SEZ
|
||||
self.partner_a.write({
|
||||
'state_id': company_state.id, # Intra-State Partner
|
||||
'property_account_position_id': template.ref('fiscal_position_in_export_sez_in').id
|
||||
})
|
||||
self._assert_invoice_fiscal_position(
|
||||
fiscal_position_ref='fiscal_position_in_export_sez_in',
|
||||
partner=self.partner_a,
|
||||
move_type='in_invoice',
|
||||
)
|
||||
|
||||
def test_l10n_in_company_with_no_vat(self):
|
||||
"""
|
||||
Test the company with no VAT and update the partner and company states as per the GSTIN number
|
||||
"""
|
||||
company = self.default_company
|
||||
|
||||
company.write({'vat': False})
|
||||
self.assertFalse(company.vat)
|
||||
company.action_update_state_as_per_gstin()
|
||||
self.assertEqual(company.partner_id.state_id, self.env.ref('base.state_in_gj'))
|
||||
|
||||
company.write({'vat': '36AABCT1332L011'})
|
||||
company.action_update_state_as_per_gstin()
|
||||
self.assertEqual(company.state_id, self.env.ref('base.state_in_ts'))
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
from odoo.tests import tagged
|
||||
import logging
|
||||
|
||||
from odoo.addons.l10n_in.tests.common import L10nInTestInvoicingCommon
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
@tagged('post_install_l10n', 'post_install', '-at_install')
|
||||
class TestReports(L10nInTestInvoicingCommon):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
|
||||
cls.partner_b.l10n_in_gst_treatment = 'regular'
|
||||
cls.partner_a.l10n_in_gst_treatment = 'composition'
|
||||
cls.partner_foreign.l10n_in_gst_treatment = 'overseas'
|
||||
cls.partner_foreign_no_state.l10n_in_gst_treatment = 'overseas'
|
||||
|
||||
cls.igst_sale_18 = cls.env['account.chart.template'].ref('igst_sale_18')
|
||||
|
||||
def test_partner_details_change_with_invoice(self):
|
||||
invoice_b_2 = self.init_invoice(
|
||||
move_type='out_invoice',
|
||||
partner=self.partner_b,
|
||||
amounts=[250, 600],
|
||||
taxes=[self.igst_sale_18],
|
||||
post=True
|
||||
)
|
||||
|
||||
# Place of Supply (pos) is same as of state of partner (for journal type sale)
|
||||
expected_pos_id = self.state_in_mh.id
|
||||
self.assertRecordValues(
|
||||
self.invoice_b,
|
||||
[{
|
||||
'state': 'draft',
|
||||
'l10n_in_gst_treatment': self.partner_b.l10n_in_gst_treatment,
|
||||
'l10n_in_state_id': expected_pos_id,
|
||||
}]
|
||||
)
|
||||
self.assertRecordValues(
|
||||
invoice_b_2,
|
||||
[{
|
||||
'state': 'posted',
|
||||
'l10n_in_gst_treatment': self.partner_b.l10n_in_gst_treatment,
|
||||
'l10n_in_state_id': expected_pos_id,
|
||||
}]
|
||||
)
|
||||
self.assertRecordValues(
|
||||
invoice_b_2,
|
||||
[{ # check gst treatment and pos doesn't change on posted invoice
|
||||
'state': 'posted',
|
||||
'l10n_in_gst_treatment': 'regular',
|
||||
}]
|
||||
)
|
||||
|
||||
def test_partner_change_with_invoice(self):
|
||||
in_invoice = self.init_invoice(
|
||||
move_type='in_invoice',
|
||||
partner=self.partner_b,
|
||||
amounts=[452, 58, 110],
|
||||
taxes=[self.igst_sale_18],
|
||||
)
|
||||
|
||||
self.invoice_a.partner_id = self.partner_foreign
|
||||
self.assertRecordValues(
|
||||
self.invoice_a,
|
||||
[{
|
||||
'state': 'draft',
|
||||
'l10n_in_gst_treatment': self.partner_foreign.l10n_in_gst_treatment,
|
||||
'l10n_in_state_id': self.env.ref("l10n_in.state_in_oc").id,
|
||||
}]
|
||||
)
|
||||
self.assertRecordValues(
|
||||
in_invoice,
|
||||
[{
|
||||
'state': 'draft',
|
||||
'l10n_in_gst_treatment': self.partner_b.l10n_in_gst_treatment,
|
||||
'l10n_in_state_id': self.env.company.state_id.id,
|
||||
}]
|
||||
)
|
||||
|
||||
def test_place_of_supply(self):
|
||||
child_partner = self.env['res.partner'].create({
|
||||
'name': "Child Contact",
|
||||
'type': "delivery",
|
||||
'parent_id': self.partner_a.id,
|
||||
'state_id': self.state_in_hp.id
|
||||
})
|
||||
|
||||
self.assertRecordValues(
|
||||
self.invoice_a,
|
||||
[{
|
||||
'partner_shipping_id': self.partner_a.id,
|
||||
'l10n_in_state_id': self.partner_a.state_id.id,
|
||||
}]
|
||||
)
|
||||
self.invoice_a.partner_shipping_id = child_partner
|
||||
self.assertRecordValues(
|
||||
self.invoice_a,
|
||||
[{
|
||||
'partner_shipping_id': child_partner.id,
|
||||
'l10n_in_state_id': child_partner.state_id.id,
|
||||
}]
|
||||
)
|
||||
self.invoice_a.partner_shipping_id = self.partner_b
|
||||
self.assertRecordValues(
|
||||
self.invoice_a,
|
||||
[{
|
||||
'l10n_in_state_id': self.partner_a.state_id.id,
|
||||
}]
|
||||
)
|
||||
|
||||
def test_foreign_customer_without_state(self):
|
||||
""" Verify foreign customer without state_id gets foreign state reference """
|
||||
self.assertRecordValues(
|
||||
self.invoice_d,
|
||||
[{
|
||||
'l10n_in_gst_treatment': 'overseas',
|
||||
'l10n_in_state_id': self.env.ref("l10n_in.state_in_oc").id,
|
||||
}]
|
||||
)
|
||||
|
||||
def test_government_gstin_extraction_tan(self):
|
||||
""" Verify that a GSTIN based on a TAN (Government entity) correctly populates the TAN field and leaves the PAN field empty. """
|
||||
gov_partner = self.env['res.partner'].create({
|
||||
'name': "Gov Partner",
|
||||
'country_id': self.env.ref('base.in').id,
|
||||
'state_id': self.env.ref('base.state_in_dl').id,
|
||||
'vat': '07DELN10357E1DH',
|
||||
})
|
||||
self.assertRecordValues(gov_partner, [{
|
||||
'l10n_in_pan_entity_id': False,
|
||||
'l10n_in_tan': 'DELN10357E',
|
||||
}])
|
||||
|
|
@ -0,0 +1,596 @@
|
|||
from odoo import Command
|
||||
from odoo.addons.l10n_in.tests.common import L10nInTestInvoicingCommon
|
||||
from odoo.tests import tagged
|
||||
|
||||
|
||||
@tagged('post_install_l10n', 'post_install', '-at_install')
|
||||
class TestTdsTcsAlert(L10nInTestInvoicingCommon):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
ChartTemplate = cls.env['account.chart.template']
|
||||
|
||||
# ==== Chart of Accounts ====
|
||||
cls.purchase_account = ChartTemplate.ref('p2107')
|
||||
cls.purchase_account.write({
|
||||
'l10n_in_tds_tcs_section_id': cls.env.ref('l10n_in.tds_section_194c').id
|
||||
})
|
||||
cls.house_expense_account = ChartTemplate.ref('p2103')
|
||||
cls.house_expense_account.write({
|
||||
'l10n_in_tds_tcs_section_id': cls.env.ref('l10n_in.tds_section_194c').id
|
||||
})
|
||||
cls.internet_account = ChartTemplate.ref('p2105')
|
||||
cls.internet_account.write({
|
||||
'l10n_in_tds_tcs_section_id': cls.env.ref('l10n_in.tds_section_194j').id
|
||||
})
|
||||
cls.rent_account = ChartTemplate.ref('p2111')
|
||||
cls.rent_account.write({
|
||||
'l10n_in_tds_tcs_section_id': cls.env.ref('l10n_in.tds_section_194ib').id
|
||||
})
|
||||
cls.sale_account = ChartTemplate.ref('p20011')
|
||||
cls.sale_account.write({
|
||||
'l10n_in_tds_tcs_section_id': cls.env.ref('l10n_in.tcs_section_206c1g_r').id
|
||||
})
|
||||
cls.service_account = ChartTemplate.ref('p20021')
|
||||
cls.creditors_account = ChartTemplate.ref('p11211')
|
||||
|
||||
# ==== Taxes ====
|
||||
cls.tax_194c = ChartTemplate.ref('tds_20_us_194c')
|
||||
cls.tax_194c.write({'l10n_in_section_id': cls.env.ref('l10n_in.tds_section_194c').id})
|
||||
cls.tax_194j = ChartTemplate.ref('tds_10_us_194j')
|
||||
cls.tax_194j.write({'l10n_in_section_id': cls.env.ref('l10n_in.tds_section_194j').id})
|
||||
cls.tax_194ib = ChartTemplate.ref('tds_20_us_194ib')
|
||||
cls.tax_194ib.write({'l10n_in_section_id': cls.env.ref('l10n_in.tds_section_194ib').id})
|
||||
cls.tax_206c1g_r = ChartTemplate.ref('tcs_5_us_206c_1g_som')
|
||||
cls.tax_206c1g_r.write({'l10n_in_section_id': cls.env.ref('l10n_in.tcs_section_206c1g_r').id})
|
||||
|
||||
country_in_id = cls.env.ref("base.in").id
|
||||
|
||||
cls.partner_b.write({
|
||||
'vat': '27ABCPM8965E1ZE',
|
||||
})
|
||||
cls.partner_foreign_2 = cls.partner_foreign.copy()
|
||||
|
||||
# ==== Company ====
|
||||
cls.env.company.write({
|
||||
'child_ids': [
|
||||
Command.create({
|
||||
'name': 'Branch A',
|
||||
"state_id": cls.env.ref("base.state_in_gj").id,
|
||||
'account_fiscal_country_id': country_in_id,
|
||||
'country_id': country_in_id,
|
||||
}),
|
||||
Command.create({
|
||||
'name': 'Branch B',
|
||||
"state_id": cls.env.ref("base.state_in_mh").id,
|
||||
'account_fiscal_country_id': country_in_id,
|
||||
'country_id': country_in_id,
|
||||
}),
|
||||
Command.create({
|
||||
'name': 'Branch C',
|
||||
"state_id": cls.env.ref("base.state_in_mp").id,
|
||||
'account_fiscal_country_id': country_in_id,
|
||||
'country_id': country_in_id,
|
||||
}),
|
||||
],
|
||||
})
|
||||
cls.cr.precommit.run() # load the CoA
|
||||
|
||||
cls.branch_a, cls.branch_b, cls.branch_c = cls.env.company.child_ids
|
||||
|
||||
def create_invoice(self, move_type=None, partner=None, invoice_date=None, amounts=None, taxes=[], company=None, accounts=[], quantities=[]):
|
||||
invoice = self.init_invoice(
|
||||
move_type=move_type or 'in_invoice',
|
||||
partner=partner,
|
||||
invoice_date=invoice_date,
|
||||
post=False,
|
||||
amounts=amounts,
|
||||
company=company
|
||||
)
|
||||
|
||||
for i, account in enumerate(accounts):
|
||||
invoice.invoice_line_ids[i].account_id = account
|
||||
|
||||
for i, quantity in enumerate(quantities):
|
||||
invoice.invoice_line_ids[i].quantity = quantity
|
||||
|
||||
for i, tax in enumerate(taxes):
|
||||
invoice.invoice_line_ids[i].tax_ids = tax
|
||||
invoice.action_post()
|
||||
return invoice
|
||||
|
||||
def tds_wizard_entry(self, move, lines):
|
||||
journal_id = self.env['account.journal'].search([('company_id', '=', self.env.company.id),('type', '=', 'general')], limit=1)
|
||||
for tax, amount in lines:
|
||||
self.env['l10n_in.withhold.wizard'].with_context(active_model='account.move', active_ids=move.ids).create({
|
||||
'journal_id': journal_id.id,
|
||||
'tax_id': tax.id,
|
||||
'base': amount,
|
||||
'date': move.invoice_date,
|
||||
}).action_create_and_post_withhold()
|
||||
|
||||
def reverse_move(self, move, date):
|
||||
move_reversal = self.env['account.move.reversal'].with_context(active_model="account.move", active_ids=move.ids).create({
|
||||
'date': date,
|
||||
'reason': 'no reason',
|
||||
'journal_id': move.journal_id.id,
|
||||
})
|
||||
return move_reversal.refund_moves()
|
||||
|
||||
def test_tcs_tds_warning(self):
|
||||
'''
|
||||
Test that if any of the limit is not exceeded.
|
||||
'''
|
||||
|
||||
move = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
invoice_date='2024-06-05',
|
||||
amounts=[29000],
|
||||
company=self.branch_a,
|
||||
accounts=[self.internet_account],
|
||||
quantities=[1]
|
||||
)
|
||||
|
||||
self.assertEqual(move.l10n_in_warning, False)
|
||||
|
||||
def test_tcs_tds_warning_on_exceeded_per_transaction_limit(self):
|
||||
'''
|
||||
Test that if the per transaction limit is exceeded.
|
||||
'''
|
||||
|
||||
move = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
invoice_date='2024-06-05',
|
||||
amounts=[31000],
|
||||
company=self.branch_a,
|
||||
quantities=[1]
|
||||
)
|
||||
self.assertEqual(move.l10n_in_warning['tds_tcs_threshold_alert']['message'], "It's advisable to deduct TDS u/s 194C on this transaction.")
|
||||
|
||||
move_1 = self.create_invoice(
|
||||
partner=self.partner_b,
|
||||
invoice_date='2024-06-05',
|
||||
amounts=[31000],
|
||||
company=self.branch_b,
|
||||
quantities=[1]
|
||||
)
|
||||
self.assertEqual(move_1.l10n_in_warning['tds_tcs_threshold_alert']['message'], "It's advisable to deduct TDS u/s 194C on this transaction.")
|
||||
|
||||
self.create_invoice(
|
||||
partner=self.partner_b,
|
||||
invoice_date='2024-06-05',
|
||||
amounts=[31000],
|
||||
company=self.branch_b,
|
||||
quantities=[1]
|
||||
)
|
||||
|
||||
move_3 = self.create_invoice(
|
||||
partner=self.partner_b,
|
||||
invoice_date='2024-06-05',
|
||||
amounts=[31000],
|
||||
company=self.branch_b,
|
||||
quantities=[1]
|
||||
)
|
||||
self.assertEqual(move_3.l10n_in_warning['tds_tcs_threshold_alert']['message'], "It's advisable to deduct TDS u/s 194C on this transaction.")
|
||||
|
||||
def test_tcs_tds_warning_on_monthly_aggregate_limit(self):
|
||||
'''
|
||||
Test the monthly aggregate limit, the warning
|
||||
message should be set accordingly.
|
||||
'''
|
||||
|
||||
move = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
invoice_date='2024-06-05',
|
||||
amounts=[30000],
|
||||
company=self.branch_a,
|
||||
accounts=[self.rent_account]
|
||||
)
|
||||
self.assertEqual(move.l10n_in_warning, False)
|
||||
|
||||
move_1 = self.create_invoice(
|
||||
partner=self.partner_b,
|
||||
invoice_date='2024-07-06',
|
||||
amounts=[20000],
|
||||
company=self.branch_b,
|
||||
accounts=[self.rent_account]
|
||||
)
|
||||
self.assertEqual(move_1.l10n_in_warning, False)
|
||||
|
||||
move_2 = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
invoice_date='2024-07-16',
|
||||
amounts=[31000],
|
||||
company=self.branch_c,
|
||||
accounts=[self.rent_account]
|
||||
)
|
||||
self.assertEqual(move_2.l10n_in_warning['tds_tcs_threshold_alert']['message'], "It's advisable to deduct TDS u/s 194IB on this transaction.")
|
||||
|
||||
move_3 = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
invoice_date='2024-09-06',
|
||||
amounts=[50000],
|
||||
company=self.branch_c,
|
||||
accounts=[self.rent_account]
|
||||
)
|
||||
self.assertEqual(move_3.l10n_in_warning, False)
|
||||
|
||||
move_4 = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
invoice_date='2024-09-16',
|
||||
amounts=[50000],
|
||||
company=self.branch_c,
|
||||
accounts=[self.rent_account]
|
||||
)
|
||||
self.assertEqual(move_4.l10n_in_warning['tds_tcs_threshold_alert']['message'], "It's advisable to deduct TDS u/s 194IB on this transaction.")
|
||||
|
||||
def test_tcs_tds_warning_partner_wiht_pan(self):
|
||||
'''
|
||||
Test the aggregate limit when partner don't have
|
||||
pan number and having pan number.
|
||||
'''
|
||||
# no pan number
|
||||
move = self.create_invoice(
|
||||
partner=self.partner_foreign,
|
||||
invoice_date='2024-06-05',
|
||||
amounts=[30000],
|
||||
company=self.branch_a,
|
||||
accounts=[self.internet_account]
|
||||
)
|
||||
self.assertEqual(move.l10n_in_warning, False)
|
||||
|
||||
move_1 = self.create_invoice(
|
||||
partner=self.partner_foreign_2,
|
||||
invoice_date='2024-06-05',
|
||||
amounts=[30000],
|
||||
company=self.branch_b,
|
||||
accounts=[self.internet_account]
|
||||
)
|
||||
self.assertEqual(move_1.l10n_in_warning, False)
|
||||
|
||||
# same pan number
|
||||
move_2 = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
invoice_date='2024-06-05',
|
||||
amounts=[30000],
|
||||
company=self.branch_a,
|
||||
accounts=[self.internet_account]
|
||||
)
|
||||
self.assertEqual(move_2.l10n_in_warning, False)
|
||||
|
||||
move_3 = self.create_invoice(
|
||||
partner=self.partner_b,
|
||||
invoice_date='2024-06-05',
|
||||
amounts=[30000],
|
||||
company=self.branch_b,
|
||||
accounts=[self.internet_account]
|
||||
)
|
||||
self.assertEqual(move_3.l10n_in_warning['tds_tcs_threshold_alert']['message'], "It's advisable to deduct TDS u/s 194J on this transaction.")
|
||||
|
||||
def test_tcs_tds_warning_on_exceeded_aggregate_limit(self):
|
||||
'''
|
||||
Test that if the aggregate limit is exceeded.
|
||||
'''
|
||||
|
||||
move = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
invoice_date='2024-06-05',
|
||||
amounts=[20000],
|
||||
company=self.branch_a,
|
||||
)
|
||||
self.assertEqual(move.l10n_in_warning, False)
|
||||
|
||||
move_1 = self.create_invoice(
|
||||
partner=self.partner_b,
|
||||
invoice_date='2024-07-06',
|
||||
amounts=[20000],
|
||||
company=self.branch_b,
|
||||
)
|
||||
self.assertEqual(move_1.l10n_in_warning, False)
|
||||
|
||||
move_2 = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
invoice_date='2024-08-06',
|
||||
amounts=[31000],
|
||||
company=self.branch_c,
|
||||
)
|
||||
self.assertEqual(move_2.l10n_in_warning['tds_tcs_threshold_alert']['message'], "It's advisable to deduct TDS u/s 194C on this transaction.")
|
||||
|
||||
move_3 = self.create_invoice(
|
||||
partner=self.partner_b,
|
||||
invoice_date='2024-09-06',
|
||||
amounts=[5000],
|
||||
company=self.branch_a,
|
||||
)
|
||||
self.assertEqual(move_3.l10n_in_warning, False)
|
||||
|
||||
move_4 = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
invoice_date='2024-10-07',
|
||||
amounts=[20000],
|
||||
company=self.branch_b,
|
||||
)
|
||||
self.assertEqual(move_4.l10n_in_warning, False)
|
||||
|
||||
move_5 = self.create_invoice(
|
||||
partner=self.partner_b,
|
||||
invoice_date='2024-11-08',
|
||||
amounts=[25000],
|
||||
company=self.branch_c,
|
||||
)
|
||||
self.assertEqual(move_5.l10n_in_warning['tds_tcs_threshold_alert']['message'], "It's advisable to deduct TDS u/s 194C on this transaction.")
|
||||
|
||||
def test_tcs_tds_warning_on_case_of_credit_note(self):
|
||||
'''
|
||||
Test that the aggregate limit in case of debit/credit note.
|
||||
'''
|
||||
|
||||
move = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
invoice_date='2024-09-01',
|
||||
amounts=[2000],
|
||||
company=self.branch_a,
|
||||
accounts=[self.internet_account]
|
||||
)
|
||||
self.assertEqual(move.l10n_in_warning, False)
|
||||
|
||||
move_1 = self.create_invoice(
|
||||
partner=self.partner_b,
|
||||
invoice_date='2024-09-01',
|
||||
amounts=[3000],
|
||||
company=self.branch_a,
|
||||
accounts=[self.internet_account]
|
||||
)
|
||||
self.reverse_move(move, '2024-09-01')
|
||||
|
||||
self.assertEqual(move_1.l10n_in_warning, False)
|
||||
|
||||
move_2 = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
invoice_date='2024-09-01',
|
||||
amounts=[2000],
|
||||
company=self.branch_a,
|
||||
accounts=[self.internet_account]
|
||||
)
|
||||
self.assertEqual(move_2.l10n_in_warning, False)
|
||||
|
||||
def test_tcs_tds_warning_cleared_on_available_tax(self):
|
||||
'''
|
||||
Test when a tax is added to the move line with a similar tax group
|
||||
as the account.
|
||||
'''
|
||||
|
||||
move = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
move_type='out_invoice',
|
||||
invoice_date='2022-12-12',
|
||||
amounts=[710000],
|
||||
taxes=[self.tax_206c1g_r],
|
||||
company=self.branch_a,
|
||||
)
|
||||
|
||||
self.assertEqual(move.l10n_in_warning, False)
|
||||
|
||||
def test_tcs_tds_warning_for_multiple_accounts_in_lines(self):
|
||||
'''
|
||||
Test when there are multiple products in the move line and some of them
|
||||
have different accounts which have the different tax group as the account.
|
||||
'''
|
||||
|
||||
move = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
move_type='in_invoice',
|
||||
invoice_date='2022-12-12',
|
||||
amounts=[100000, 1100000, 710000],
|
||||
company=self.branch_a,
|
||||
accounts=[self.rent_account, self.internet_account, self.purchase_account],
|
||||
quantities=[15, 16, 10]
|
||||
)
|
||||
self.assertTrue(move.l10n_in_warning)
|
||||
|
||||
move_1 = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
move_type='in_invoice',
|
||||
invoice_date='2022-12-12',
|
||||
amounts=[1000000.0, 1100000.0, 710000],
|
||||
company=self.branch_a,
|
||||
accounts=[self.rent_account, self.internet_account, self.purchase_account],
|
||||
)
|
||||
self.tds_wizard_entry(move=move_1, lines=[(self.tax_194ib, 100000), (self.tax_194j, 100000), (self.tax_194c, 100000)])
|
||||
move_1.button_draft()
|
||||
move_1.action_post()
|
||||
self.assertEqual(move_1.l10n_in_warning, False)
|
||||
|
||||
def test_tcs_tds_warning_for_if_line_has_price_zero(self):
|
||||
'''
|
||||
Test when any invoice line has Zero
|
||||
'''
|
||||
move = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
invoice_date='2022-12-12',
|
||||
amounts=[101000, 0],
|
||||
company=self.branch_a,
|
||||
)
|
||||
self.assertEqual(move.l10n_in_warning['tds_tcs_threshold_alert']['message'], "It's advisable to deduct TDS u/s 194C on this transaction.")
|
||||
|
||||
move_1 = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
invoice_date='2022-12-12',
|
||||
amounts=[0],
|
||||
company=self.branch_a,
|
||||
)
|
||||
self.assertEqual(move_1.l10n_in_warning, False)
|
||||
|
||||
def test_tcs_tds_warning_for_all_lines_do_not_have_taxes(self):
|
||||
'''
|
||||
Test when tds entry created and warning will removed
|
||||
'''
|
||||
move = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
invoice_date='2022-12-12',
|
||||
amounts=[1000, 6000],
|
||||
company=self.branch_a,
|
||||
accounts=[],
|
||||
quantities=[15, 16]
|
||||
)
|
||||
self.assertEqual(move.l10n_in_warning['tds_tcs_threshold_alert']['message'], "It's advisable to deduct TDS u/s 194C on this transaction.")
|
||||
self.tds_wizard_entry(move=move, lines=[(self.tax_194c, 100000)])
|
||||
move.line_ids.remove_move_reconcile()
|
||||
self.assertEqual(move.l10n_in_warning, False)
|
||||
|
||||
def test_tcs_tds_warning_for_company_branches(self):
|
||||
'''
|
||||
Test when the aggregate limit is exceeded in case of multiple branches
|
||||
of the company,the warning message should be set accordingly.
|
||||
'''
|
||||
|
||||
self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
invoice_date='2024-05-14',
|
||||
amounts=[25000],
|
||||
company=self.branch_a,
|
||||
)
|
||||
|
||||
self.create_invoice(
|
||||
partner=self.partner_b,
|
||||
invoice_date='2024-05-14',
|
||||
amounts=[25000],
|
||||
company=self.branch_b,
|
||||
)
|
||||
|
||||
self.create_invoice(
|
||||
partner=self.partner_b,
|
||||
invoice_date='2024-05-14',
|
||||
amounts=[25000],
|
||||
company=self.branch_c,
|
||||
quantities=[25]
|
||||
)
|
||||
|
||||
move = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
invoice_date='2024-05-14',
|
||||
amounts=[28000],
|
||||
company=self.branch_a,
|
||||
)
|
||||
|
||||
self.assertEqual(move.l10n_in_warning['tds_tcs_threshold_alert']['message'], "It's advisable to deduct TDS u/s 194C on this transaction.")
|
||||
|
||||
def test_tcs_tds_warning_tcs_use_in_bill(self):
|
||||
'''
|
||||
Test when tcs section is used in the bill creation.
|
||||
'''
|
||||
|
||||
move = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
invoice_date='2024-05-29',
|
||||
amounts=[1100000],
|
||||
company=self.branch_a,
|
||||
accounts=[self.sale_account]
|
||||
)
|
||||
self.assertEqual(move.l10n_in_warning, False)
|
||||
|
||||
def test_tcs_tds_warning_tds_use_in_invoice(self):
|
||||
'''
|
||||
Test when tcs section is used in the bill creation.
|
||||
'''
|
||||
|
||||
move = self.create_invoice(
|
||||
move_type='out_invoice',
|
||||
partner=self.partner_a,
|
||||
invoice_date='2024-05-29',
|
||||
amounts=[110000],
|
||||
company=self.branch_a,
|
||||
)
|
||||
self.assertEqual(move.l10n_in_warning, False)
|
||||
|
||||
def test_tcs_tds_warning_for_multiple_accounts_same_section_in_lines(self):
|
||||
'''
|
||||
Test when there are multiple products in the move line and some of them
|
||||
have different accounts which have the same tax group as the account.
|
||||
'''
|
||||
|
||||
move = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
invoice_date='2022-12-12',
|
||||
amounts=[17000, 14000],
|
||||
company=self.branch_a,
|
||||
accounts=[self.house_expense_account, self.purchase_account],
|
||||
)
|
||||
self.assertEqual(move.l10n_in_warning['tds_tcs_threshold_alert']['message'], "It's advisable to deduct TDS u/s 194C on this transaction.")
|
||||
|
||||
move_1 = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
invoice_date='2022-12-12',
|
||||
amounts=[17000, 13000],
|
||||
company=self.branch_a,
|
||||
accounts=[self.house_expense_account, self.purchase_account],
|
||||
)
|
||||
self.assertEqual(move_1.l10n_in_warning, False)
|
||||
|
||||
move_2 = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
invoice_date='2022-12-12',
|
||||
amounts=[30000],
|
||||
company=self.branch_a,
|
||||
accounts=[self.house_expense_account],
|
||||
)
|
||||
self.assertEqual(move_2.l10n_in_warning, False)
|
||||
|
||||
move_3 = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
invoice_date='2022-12-12',
|
||||
amounts=[10000],
|
||||
company=self.branch_a,
|
||||
)
|
||||
self.assertEqual(move_3.l10n_in_warning['tds_tcs_threshold_alert']['message'], "It's advisable to deduct TDS u/s 194C on this transaction.")
|
||||
|
||||
def test_tcs_tds_warning_for_not_consider_draft_cancel_invoices_for_aggregate(self):
|
||||
'''
|
||||
Test to exclude draft and canceled invoices from aggregate
|
||||
total calculation.
|
||||
'''
|
||||
|
||||
move = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
invoice_date='2022-12-12',
|
||||
amounts=[16000],
|
||||
company=self.branch_a,
|
||||
accounts=[self.purchase_account],
|
||||
)
|
||||
move.button_cancel()
|
||||
self.assertEqual(move.l10n_in_warning, False)
|
||||
|
||||
move_1 = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
invoice_date='2022-12-12',
|
||||
amounts=[25000],
|
||||
company=self.branch_a,
|
||||
accounts=[self.purchase_account],
|
||||
)
|
||||
self.assertEqual(move_1.l10n_in_warning, False)
|
||||
|
||||
move_2 = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
invoice_date='2022-12-12',
|
||||
amounts=[85000],
|
||||
company=self.branch_a,
|
||||
accounts=[self.purchase_account],
|
||||
)
|
||||
self.assertEqual(move_2.l10n_in_warning['tds_tcs_threshold_alert']['message'], "It's advisable to deduct TDS u/s 194C on this transaction.")
|
||||
|
||||
def test_tcs_tds_warning_if_some_lines_has_tax(self):
|
||||
'''
|
||||
Test when a tax is added to the some of the move line
|
||||
'''
|
||||
|
||||
move = self.create_invoice(
|
||||
partner=self.partner_a,
|
||||
move_type='out_invoice',
|
||||
invoice_date='2022-12-12',
|
||||
amounts=[710000, 710000],
|
||||
taxes=[self.tax_206c1g_r],
|
||||
company=self.branch_a,
|
||||
)
|
||||
|
||||
self.assertEqual(move.l10n_in_warning['tds_tcs_threshold_alert']['message'], "It's advisable to collect TCS u/s 206C(1G) Remittance on this transaction.")
|
||||
Loading…
Add table
Add a link
Reference in a new issue