Odoo18-Base/addons/l10n_id_efaktur/tests/test_l10n_id_efaktur.py
2025-03-10 11:12:23 +07:00

291 lines
14 KiB
Python

import csv
from odoo import Command
from odoo.exceptions import ValidationError
from odoo.tests import tagged, common
from odoo.addons.l10n_id_efaktur.models.account_move import FK_HEAD_LIST, LT_HEAD_LIST, OF_HEAD_LIST, _csv_row, AccountMove
from odoo.exceptions import RedirectWarning
from unittest.mock import patch
@tagged('post_install', '-at_install', 'post_install_l10n')
class TestIndonesianEfaktur(common.TransactionCase):
def setUp(self):
"""
1) contact with l10n_id_pkp=True, l10n_id_kode_transaksi="01"
2) tax: amount=10, type_tax_use=sale, price_include=True
3) invoice with partner_id=contact, journal=customer invoices,
"""
super().setUp()
self.maxDiff = 1500
# change company info for csv detai later
self.env.company.country_id = self.env.ref('base.id')
self.env.company.account_fiscal_country_id = self.env.company.country_id
self.env.company.street = "test"
self.env.company.phone = "12345"
self.env.company.vat = "1234567890123456"
self.partner_id = self.env['res.partner'].create({"name": "l10ntest", "l10n_id_pkp": True, "l10n_id_kode_transaksi": "01", "l10n_id_nik": "12345", "vat": "000000000000000"})
self.partner_id_vat = self.env['res.partner'].create({"name": "l10ntest3", "l10n_id_pkp": True, "l10n_id_kode_transaksi": "01", "l10n_id_nik": "67890", "vat": "010000000000000"})
self.tax_id = self.env['account.tax'].create({"name": "test tax", "type_tax_use": "sale", "amount": 10.0, "price_include": True})
self.efaktur = self.env['l10n_id_efaktur.efaktur.range'].create({'min': '0000000000001', 'max': '0000000000010'})
self.out_invoice_1 = self.env['account.move'].create({
'move_type': 'out_invoice',
'partner_id': self.partner_id.id,
'invoice_date': '2019-05-01',
'date': '2019-05-01',
'invoice_line_ids': [
(0, 0, {'name': 'line1', 'price_unit': 110.0, 'tax_ids': self.tax_id.ids}),
],
'l10n_id_kode_transaksi': "01",
})
self.out_invoice_1.action_post()
self.out_invoice_2 = self.env['account.move'].create({
'move_type': 'out_invoice',
'partner_id': self.partner_id.id,
'invoice_date': '2019-05-01',
'date': '2019-05-01',
'invoice_line_ids': [
(0, 0, {'name': 'line1', 'price_unit': 110.11, 'quantity': 400, 'tax_ids': self.tax_id.ids})
],
'l10n_id_kode_transaksi': '01'
})
self.out_invoice_2.action_post()
# For the sake of unit test of this module, we want to retain the the compute method for field
# l10n_id_need_kode_transaksi of this module. In the coretax module, l10n_id_need_kode_transaksi
# is always set to False to prevent the flows of old module to be triggered
patch_kode_transaksi = patch('odoo.addons.l10n_id_efaktur_coretax.models.account_move.AccountMove._compute_need_kode_transaksi',
AccountMove._compute_need_kode_transaksi)
self.startClassPatcher(patch_kode_transaksi)
def test_efaktur_csv_output_1(self):
"""
Test to ensure that the output csv data contains tax-excluded prices regardless of whether the tax configuration is tax-included or tax-excluded.
Current test is using price of 110 which is tax-included with tax of amount 10%. So the unit price listed has to be 100 whereas the original result would have 110 instead.
"""
# to check the diff when test fails
efaktur_csv_output = self.out_invoice_1._generate_efaktur_invoice(',')
output_head = '%s%s%s' % (
_csv_row(FK_HEAD_LIST, ','),
_csv_row(LT_HEAD_LIST, ','),
_csv_row(OF_HEAD_LIST, ','),
)
# remaining lines
line_4 = '"FK","01","0","0000000000001","5","2019","1/5/2019","000000000000000","12345#NIK#NAMA#l10ntest","","100","10","0","","0","0","0","0","INV/2019/00001","0"\n'
line_5 = '"OF","","","100.00","1.0","100.00","0","100.00","10.00","0","0"\n'
efaktur_csv_expected = output_head + line_4 + line_5
self.assertEqual(efaktur_csv_expected, efaktur_csv_output)
def test_efaktur_csv_output_decimal_place(self):
"""
Test to ensure that decimal place conversion is only done when inputting to csv
This is to test original calculation of invoice_line_total_price: invoice_line_total_price = invoice_line_unit_price * line.quantity
as invoice_line_unit_price is already converted to be tax-excluded and set to the decimal place as configured on the currency, the calculation of total could be flawed.
In this test case, the tax-included price unit is 110.11, hence tax-excluded is 100.1,
invoice_line_unit_price will be 100, if we continue with the calculation of total price, it will be 100*400 = 40000
eventhough the total is supposed to be 100.1*400 = 40040, there is a 40 discrepancy
"""
efaktur_csv_output = self.out_invoice_2._generate_efaktur_invoice(',')
output_head = '%s%s%s' % (
_csv_row(FK_HEAD_LIST, ','),
_csv_row(LT_HEAD_LIST, ','),
_csv_row(OF_HEAD_LIST, ','),
)
line_4 = '"FK","01","0","0000000000002","5","2019","1/5/2019","000000000000000","12345#NIK#NAMA#l10ntest","","40040","4004","0","","0","0","0","0","INV/2019/00002","0"\n'
line_5 = '"OF","","","100.10","400.0","40040.00","0","40040.00","4004.00","0","0"\n'
efaktur_csv_expected = output_head + line_4 + line_5
self.assertEqual(efaktur_csv_expected, efaktur_csv_output)
def test_efaktur_use_vat(self):
""" Test to ensure that the e-faktur uses the VAT on NPWP column of efaktur when
VAT is non-zeros """
out_invoice = self.env['account.move'].create({
'move_type': 'out_invoice',
'partner_id': self.partner_id_vat.id,
'invoice_date': '2019-05-01',
'date': '2019-05-01',
'invoice_line_ids': [
(0, 0, {'name': 'line1', 'price_unit': 110.11, 'quantity': 400, 'tax_ids': self.tax_id.ids})
],
'l10n_id_kode_transaksi': '01'
})
out_invoice.action_post()
efaktur_csv_output = out_invoice._generate_efaktur_invoice(',')
output_head = '%s%s%s' % (
_csv_row(FK_HEAD_LIST, ','),
_csv_row(LT_HEAD_LIST, ','),
_csv_row(OF_HEAD_LIST, ','),
)
line_4 = '"FK","01","0","0000000000003","5","2019","1/5/2019","010000000000000","l10ntest3","","40040","4004","0","","0","0","0","0","INV/2019/00003","0"\n'
line_5 = '"OF","","","100.10","400.0","40040.00","0","40040.00","4004.00","0","0"\n'
efaktur_csv_expected = output_head + line_4 + line_5
self.assertEqual(efaktur_csv_expected, efaktur_csv_output)
def test_efaktur_no_vat_nik(self):
""" Test to ensure that when no VAT and NIK is supplied, a RedirectWarning should be raised """
partner_no_vat_nik = self.env['res.partner'].create({"name": "l10ntest4", "l10n_id_pkp": True, "l10n_id_kode_transaksi": "01"})
out_invoice = self.env['account.move'].create({
'move_type': 'out_invoice',
'partner_id': partner_no_vat_nik.id,
'invoice_date': '2019-05-01',
'date': '2019-05-01',
'invoice_line_ids': [
(0, 0, {'name': 'line1', 'price_unit': 110.11, 'quantity': 400, 'tax_ids': self.tax_id.ids})
],
'l10n_id_kode_transaksi': '01'
})
out_invoice.action_post()
with self.assertRaises(RedirectWarning):
out_invoice._generate_efaktur_invoice(',')
def test_efaktur_nik_with_no_vat(self):
""" Test to ensure if there is contact has no VAT but has NIK
NPWP would contain NIK, NAMA contains customer's name, REFERENSI would contain invoice name with customer's NIK"""
partner_nik_no_vat = self.env['res.partner'].create({"name": "l10ntest4", "l10n_id_pkp": True, "l10n_id_kode_transaksi": "01", "l10n_id_nik": "1532167"})
out_invoice = self.env['account.move'].create({
'move_type': 'out_invoice',
'partner_id': partner_nik_no_vat.id,
'invoice_date': '2019-05-01',
'date': '2019-05-01',
'invoice_line_ids': [
(0, 0, {'name': 'line1', 'price_unit': 110.11, 'quantity': 400, 'tax_ids': self.tax_id.ids})
],
'l10n_id_kode_transaksi': '01'
})
out_invoice.action_post()
efaktur_csv_output = out_invoice._generate_efaktur_invoice(',')
output_head = '%s%s%s' % (
_csv_row(FK_HEAD_LIST, ','),
_csv_row(LT_HEAD_LIST, ','),
_csv_row(OF_HEAD_LIST, ','),
)
line_4 = '"FK","01","0","0000000000003","5","2019","1/5/2019","1532167","l10ntest4","","40040","4004","0","","0","0","0","0","INV/2019/00003 1532167","0"\n'
line_5 = '"OF","","","100.10","400.0","40040.00","0","40040.00","4004.00","0","0"\n'
efaktur_csv_expected = output_head + line_4 + line_5
self.assertEqual(efaktur_csv_expected, efaktur_csv_output)
def test_efaktur_total_rounding_accuracy(self):
""" Use case:
Using a tax of 11% price included on every line:
line | qty | price | subtotal
1 | 24 | 57851 | 1250832.43
2 | 24 | 65184 | 1409383.78
3 | 24 | 77134 | 1667762.16
4 | 24 | 87835 | 1899135.14
5 | 20 | 180342| 3249405.41
Untaxed Amount: 9474250.92
Taxes: 1042176.08
Total: 10518936.00
Efaktur will display both:
-The detail of the lines.
and
- the amount_untaxed and amount_tax rounded to 0 decimals.
The sum of the lines MUST exactly match with the amount_untaxed and amount_tax.
Which most of the case won't be happening because sum(rounded(vals)) is usually not equal to rounded(sum(vals))
when using integers.
To remediate to that issue we are putting the difference in the amount of the first line.
"""
# Prepare the test invoice.
tax_id = self.env["account.tax"].create(
{"name": "test tax 11", "type_tax_use": "sale", "amount": 11.0, "price_include": True}
)
invoice = self.env['account.move'].create({
'move_type': 'out_invoice',
'partner_id': self.partner_id.id,
'invoice_date': '2019-05-01',
'date': '2019-05-01',
'invoice_line_ids': [
Command.create({'name': 'line1', 'price_unit': 57851, 'quantity': 24, 'tax_ids': tax_id.ids}),
Command.create({'name': 'line2', 'price_unit': 65184, 'quantity': 24, 'tax_ids': tax_id.ids}),
Command.create({'name': 'line3', 'price_unit': 77134, 'quantity': 24, 'tax_ids': tax_id.ids}),
Command.create({'name': 'line4', 'price_unit': 87835, 'quantity': 24, 'tax_ids': tax_id.ids}),
Command.create({'name': 'line5', 'price_unit': 180342, 'quantity': 20, 'tax_ids': tax_id.ids}),
],
'l10n_id_kode_transaksi': '01',
})
invoice.action_post()
# Generate the generate efaktur csv.
efaktur_csv_output = invoice._generate_efaktur_invoice(',')
# Validate the result: the sum of the lines must exactly match with the amount_untaxed and amount_tax.
dict_reader = csv.DictReader(efaktur_csv_output.splitlines(), delimiter=",")
amount_untaxed_total = 0
amount_tax_total = 0
amount_untaxed_sum = 0
amount_tax_sum = 0
for row in dict_reader:
row_code = row["FK"]
# Besides the first row, FK is used for the total row.
if row_code == "FK":
amount_untaxed_total = int(row["JUMLAH_DPP"]) # These are rounded to 0 decimal in the file.
amount_tax_total = int(row["JUMLAH_PPN"]) # These are rounded to 0 decimal in the file.
# OF lines are the data lines, but also used in another case (for codes). Data lines do not have a KD_JENIS_TRANSAKSI though.
elif row_code == "OF" and not row["KD_JENIS_TRANSAKSI"]:
amount_untaxed_sum += float(row["NPWP"]) # These are not.
amount_tax_sum += float(row["NAMA"]) # These are not.
self.assertEqual(amount_untaxed_total, amount_untaxed_sum)
self.assertEqual(amount_tax_total, amount_tax_sum)
def test_efaktur_do_not_consume_code(self):
""" Ensure that an invoice with no taxes at all will not consume a code. """
available_code = self.efaktur.available
# 1. No taxes
out_invoice_no_taxes = self.env["account.move"].create({
"move_type": "out_invoice",
"partner_id": self.partner_id.id,
"invoice_date": "2019-05-01",
"date": "2019-05-01",
"invoice_line_ids": [
Command.create({"name": "line1", "price_unit": 110.0, "tax_ids": False}),
],
"l10n_id_kode_transaksi": "01",
})
out_invoice_no_taxes.action_post()
# The tax number is not set.
self.assertFalse(out_invoice_no_taxes.l10n_id_tax_number)
# No codes have been consumed.
self.assertEqual(self.efaktur.available, available_code)
with self.assertRaises(ValidationError, msg='E-faktur is not available for invoices without any taxes.'), self.cr.savepoint():
out_invoice_no_taxes.download_efaktur()
def test_efaktur_consume_code(self):
""" Ensure that an invoice with taxes will consume a code. """
available_code = self.efaktur.available
out_invoice_no_taxes = self.env["account.move"].create({
"move_type": "out_invoice",
"partner_id": self.partner_id.id,
"invoice_date": "2019-05-01",
"date": "2019-05-01",
"invoice_line_ids": [
Command.create({"name": "line1", "price_unit": 110.0, "tax_ids": self.tax_id.ids}),
],
"l10n_id_kode_transaksi": "01",
})
out_invoice_no_taxes.action_post()
# The tax number is set.
self.assertEqual(out_invoice_no_taxes.l10n_id_tax_number, '0100000000000003')
# A code has been consumed.
self.assertEqual(self.efaktur.available, available_code - 1)
# No error is raised when downloading.
out_invoice_no_taxes.download_efaktur()