312 lines
18 KiB
Python
312 lines
18 KiB
Python
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
from freezegun import freeze_time
|
|
|
|
from odoo import Command
|
|
from odoo.addons.account_reports.tests.common import TestAccountReportsCommon
|
|
from odoo.tests import tagged
|
|
|
|
|
|
@tagged('post_install_l10n', 'post_install', '-at_install')
|
|
class TestWT003Generation(TestAccountReportsCommon):
|
|
|
|
@classmethod
|
|
@TestAccountReportsCommon.setup_country('kh')
|
|
def setUpClass(cls):
|
|
super().setUpClass()
|
|
# Prepare a sample of taxes for the tests.
|
|
cls.ChartTemplate = cls.env['account.chart.template'].with_company(cls.company_data['company'])
|
|
wth_tax_xml_ids = [
|
|
'l10n_kh_tax_purchase_15_wht_s',
|
|
'l10n_kh_tax_purchase_15_wht_i',
|
|
'l10n_kh_tax_purchase_6_wht_fd',
|
|
'l10n_kh_tax_purchase_4_wht_sd',
|
|
'l10n_kh_tax_purchase_10_wht_rental_legal',
|
|
'l10n_kh_tax_purchase_10_wht_rental_physical',
|
|
'l10n_kh_tax_purchase_14_wht_nr_i',
|
|
'l10n_kh_tax_purchase_14_wht_nr_r',
|
|
'l10n_kh_tax_purchase_14_wht_nr_t',
|
|
'l10n_kh_tax_purchase_14_wht_nr_div',
|
|
'l10n_kh_tax_purchase_14_wht_nr_s',
|
|
'l10n_kh_tax_purchase_10_m',
|
|
]
|
|
cls.wth_tax_set = cls.env['account.tax'].union(*[cls.ChartTemplate.ref(tax_xml_id) for tax_xml_id in wth_tax_xml_ids])
|
|
|
|
cls.withholding_sequence = cls.env['ir.sequence'].create({
|
|
'implementation': 'no_gap',
|
|
'name': 'Withholding Sequence',
|
|
'padding': 4,
|
|
'number_increment': 1,
|
|
})
|
|
cls.wth_tax_set.withholding_sequence_id = cls.withholding_sequence
|
|
# VAT
|
|
cls.l10n_kh_tax_purchase_10_m = cls.ChartTemplate.ref('l10n_kh_tax_purchase_10_m')
|
|
|
|
cls.company_data['company'].withholding_tax_base_account_id = cls.env['account.account'].create({
|
|
'code': 'WITHB',
|
|
'name': 'Withholding Tax Base Account',
|
|
'reconcile': True,
|
|
'account_type': 'asset_current',
|
|
})
|
|
|
|
@freeze_time('2025-01-15')
|
|
def test_wth_on_payment_export(self):
|
|
""" Generate an amount of bills using wth tax on payment, pay them, then test the export. """
|
|
invoice_data = [
|
|
("in_invoice", self.partner_a, "2025-01-15", [(100 * i, tax)])
|
|
for i, tax in enumerate(self.wth_tax_set, start=1)
|
|
]
|
|
|
|
invoice_vals = []
|
|
for move_type, partner, invoice_date, lines_data in invoice_data:
|
|
invoice_vals.append({
|
|
'move_type': move_type,
|
|
'invoice_date': invoice_date,
|
|
'partner_id': partner.id,
|
|
'invoice_line_ids': [
|
|
Command.create({
|
|
'name': 'Test line',
|
|
'quantity': 1.0,
|
|
'price_unit': amount,
|
|
'tax_ids': tax,
|
|
}) for amount, tax in lines_data
|
|
],
|
|
})
|
|
invoices = self.env['account.move'].create(invoice_vals)
|
|
invoices.action_post()
|
|
for invoice in invoices:
|
|
wizard = self.env['account.payment.register']\
|
|
.with_context(active_model='account.move', active_ids=invoice.ids)\
|
|
.create({})
|
|
wizard._create_payments()
|
|
|
|
report = self.env.ref('l10n_kh.l10n_kh_wt003')
|
|
options = self._generate_options(report, '2025-01-01', '2025-01-31')
|
|
|
|
sls = self.env[report.custom_handler_model_name].export_wt_003(options)['file_content']
|
|
|
|
expected_row_values = {
|
|
# Headers
|
|
**self._get_header_row_vals(),
|
|
# Sales data
|
|
2: (1, '01/15/2025', 'BILL/2025/01/0011', 1, '', 'partner_a', '1100.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '154.00'),
|
|
3: (2, '01/15/2025', 'BILL/2025/01/0010', 1, '', 'partner_a', '1000.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '140.00', '0.00'),
|
|
4: (3, '01/15/2025', 'BILL/2025/01/0009', 1, '', 'partner_a', '900.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '126.00', '0.00', '0.00'),
|
|
5: (4, '01/15/2025', 'BILL/2025/01/0008', 1, '', 'partner_a', '800.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '112.00', '0.00', '0.00', '0.00'),
|
|
6: (5, '01/15/2025', 'BILL/2025/01/0007', 1, '', 'partner_a', '700.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '98.00', '0.00', '0.00', '0.00', '0.00'),
|
|
7: (6, '01/15/2025', 'BILL/2025/01/0006', 1, '', 'partner_a', '600.00', '0.00', '0.00', '0.00', '0.00', '0.00', '60.00', '0.00', '0.00', '0.00', '0.00', '0.00'),
|
|
8: (7, '01/15/2025', 'BILL/2025/01/0005', 1, '', 'partner_a', '500.00', '0.00', '0.00', '0.00', '0.00', '50.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00'),
|
|
9: (8, '01/15/2025', 'BILL/2025/01/0004', 1, '', 'partner_a', '400.00', '0.00', '0.00', '0.00', '16.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00'),
|
|
10: (9, '01/15/2025', 'BILL/2025/01/0003', 1, '', 'partner_a', '300.00', '0.00', '0.00', '18.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00'),
|
|
11: (10, '01/15/2025', 'BILL/2025/01/0002', 1, '', 'partner_a', '200.00', '0.00', '30.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00'),
|
|
12: (11, '01/15/2025', 'BILL/2025/01/0001', 1, '', 'partner_a', '100.00', '15.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00'),
|
|
}
|
|
self._test_xlsx_file(sls, expected_row_values)
|
|
|
|
@freeze_time('2025-01-15')
|
|
def test_wth_on_invoice_export(self):
|
|
""" Repeat the previous test, but set the withholding tax to be applied on invoice. """
|
|
invoice_data = [
|
|
("in_invoice", self.partner_a, "2025-01-15", [(100 * i, tax)])
|
|
for i, tax in enumerate(self.wth_tax_set, start=1)
|
|
]
|
|
self.wth_tax_set.is_withholding_tax_on_payment = False
|
|
|
|
invoice_vals = []
|
|
for move_type, partner, invoice_date, lines_data in invoice_data:
|
|
invoice_vals.append({
|
|
'move_type': move_type,
|
|
'invoice_date': invoice_date,
|
|
'partner_id': partner.id,
|
|
'invoice_line_ids': [
|
|
Command.create({
|
|
'name': 'Test line',
|
|
'quantity': 1.0,
|
|
'price_unit': amount,
|
|
'tax_ids': tax,
|
|
}) for amount, tax in lines_data
|
|
],
|
|
})
|
|
invoices = self.env['account.move'].create(invoice_vals)
|
|
invoices.action_post()
|
|
|
|
report = self.env.ref('l10n_kh.l10n_kh_wt003')
|
|
options = self._generate_options(report, '2025-01-01', '2025-01-31')
|
|
|
|
sls = self.env[report.custom_handler_model_name].export_wt_003(options)['file_content']
|
|
|
|
expected_row_values = {
|
|
# Headers
|
|
**self._get_header_row_vals(),
|
|
# Sales data
|
|
2: (1, '01/15/2025', 'BILL/2025/01/0011', 1, '', 'partner_a', '1100.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '154.00'),
|
|
3: (2, '01/15/2025', 'BILL/2025/01/0010', 1, '', 'partner_a', '1000.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '140.00', '0.00'),
|
|
4: (3, '01/15/2025', 'BILL/2025/01/0009', 1, '', 'partner_a', '900.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '126.00', '0.00', '0.00'),
|
|
5: (4, '01/15/2025', 'BILL/2025/01/0008', 1, '', 'partner_a', '800.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '112.00', '0.00', '0.00', '0.00'),
|
|
6: (5, '01/15/2025', 'BILL/2025/01/0007', 1, '', 'partner_a', '700.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '98.00', '0.00', '0.00', '0.00', '0.00'),
|
|
7: (6, '01/15/2025', 'BILL/2025/01/0006', 1, '', 'partner_a', '600.00', '0.00', '0.00', '0.00', '0.00', '0.00', '60.00', '0.00', '0.00', '0.00', '0.00', '0.00'),
|
|
8: (7, '01/15/2025', 'BILL/2025/01/0005', 1, '', 'partner_a', '500.00', '0.00', '0.00', '0.00', '0.00', '50.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00'),
|
|
9: (8, '01/15/2025', 'BILL/2025/01/0004', 1, '', 'partner_a', '400.00', '0.00', '0.00', '0.00', '16.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00'),
|
|
10: (9, '01/15/2025', 'BILL/2025/01/0003', 1, '', 'partner_a', '300.00', '0.00', '0.00', '18.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00'),
|
|
11: (10, '01/15/2025', 'BILL/2025/01/0002', 1, '', 'partner_a', '200.00', '0.00', '30.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00'),
|
|
12: (11, '01/15/2025', 'BILL/2025/01/0001', 1, '', 'partner_a', '100.00', '15.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00'),
|
|
}
|
|
self._test_xlsx_file(sls, expected_row_values)
|
|
|
|
@freeze_time('2025-01-15')
|
|
def test_partner_type_export(self):
|
|
""" Generate an amount of bills having partners of different fpos, pay them, then test the export. """
|
|
fpos_ntax = self.ChartTemplate.ref('l10n_kh_fiscal_position_non_taxable_person')
|
|
fpos_oc = self.ChartTemplate.ref('l10n_kh_fiscal_position_overseas_company')
|
|
|
|
partner_ntax = self.partner_a.copy()
|
|
partner_ntax.write({
|
|
'name': 'Partner NTAX',
|
|
'property_account_position_id': fpos_ntax.id,
|
|
})
|
|
partner_oc = self.partner_a.copy()
|
|
partner_oc.write({
|
|
'name': 'Partner OC',
|
|
'property_account_position_id': fpos_oc.id,
|
|
})
|
|
|
|
self.partner_a.vat = '987654312'
|
|
invoice_data = [
|
|
("in_invoice", self.partner_a, '2025-01-15', [(500, self.wth_tax_set[0])]),
|
|
("in_invoice", partner_ntax, '2025-01-15', [(500, self.wth_tax_set[0])]),
|
|
("in_invoice", partner_oc, '2025-01-15', [(500, self.wth_tax_set[0])]),
|
|
]
|
|
|
|
invoice_vals = []
|
|
for move_type, partner, invoice_date, lines_data in invoice_data:
|
|
invoice_vals.append({
|
|
'move_type': move_type,
|
|
'invoice_date': invoice_date,
|
|
'partner_id': partner.id,
|
|
'invoice_line_ids': [
|
|
Command.create({
|
|
'name': 'Test line',
|
|
'quantity': 1.0,
|
|
'price_unit': amount,
|
|
'tax_ids': tax,
|
|
}) for amount, tax in lines_data
|
|
],
|
|
})
|
|
invoices = self.env['account.move'].create(invoice_vals)
|
|
invoices.action_post()
|
|
for invoice in invoices:
|
|
wizard = self.env['account.payment.register']\
|
|
.with_context(active_model='account.move', active_ids=invoice.ids)\
|
|
.create({})
|
|
wizard._create_payments()
|
|
|
|
report = self.env.ref('l10n_kh.l10n_kh_wt003')
|
|
options = self._generate_options(report, '2025-01-01', '2025-01-31')
|
|
|
|
sls = self.env[report.custom_handler_model_name].export_wt_003(options)['file_content']
|
|
|
|
expected_row_values = {
|
|
# Headers
|
|
**self._get_header_row_vals(),
|
|
# Sales data
|
|
2: (1, '01/15/2025', 'BILL/2025/01/0003', 3, '', 'Partner OC', '500.00', '75.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00'),
|
|
3: (2, '01/15/2025', 'BILL/2025/01/0002', 2, '', 'Partner NTAX', '500.00', '75.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00'),
|
|
4: (3, '01/15/2025', 'BILL/2025/01/0001', 1, '987654312', 'partner_a', '500.00', '75.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00'),
|
|
}
|
|
self._test_xlsx_file(sls, expected_row_values)
|
|
|
|
@freeze_time('2025-01-15')
|
|
def test_number_gap(self):
|
|
""" Generate an amount of bills with wth and vat, and validate the numbers. """
|
|
invoice_data = [
|
|
("in_invoice", self.partner_a, '2025-01-15', [(500, self.wth_tax_set[0])]),
|
|
("in_invoice", self.partner_a, '2025-01-15', [(400, self.wth_tax_set[0]), (250, self.l10n_kh_tax_purchase_10_m)]),
|
|
("in_invoice", self.partner_a, '2025-01-15', [(300, self.l10n_kh_tax_purchase_10_m)]), # Will be skipped
|
|
("in_invoice", self.partner_a, '2025-01-15', [(650, False), (800, self.l10n_kh_tax_purchase_10_m)]), # Will be skipped
|
|
("in_invoice", self.partner_a, '2025-01-15', [(700, self.wth_tax_set[0])]),
|
|
]
|
|
|
|
invoice_vals = []
|
|
for move_type, partner, invoice_date, lines_data in invoice_data:
|
|
invoice_vals.append({
|
|
'move_type': move_type,
|
|
'invoice_date': invoice_date,
|
|
'partner_id': partner.id,
|
|
'invoice_line_ids': [
|
|
Command.create({
|
|
'name': 'Test line',
|
|
'quantity': 1.0,
|
|
'price_unit': amount,
|
|
'tax_ids': tax,
|
|
}) for amount, tax in lines_data
|
|
],
|
|
})
|
|
invoices = self.env['account.move'].create(invoice_vals)
|
|
invoices.action_post()
|
|
for invoice in invoices:
|
|
wizard = self.env['account.payment.register']\
|
|
.with_context(active_model='account.move', active_ids=invoice.ids)\
|
|
.create({})
|
|
wizard._create_payments()
|
|
|
|
report = self.env.ref('l10n_kh.l10n_kh_wt003')
|
|
options = self._generate_options(report, '2025-01-01', '2025-01-31')
|
|
|
|
sls = self.env[report.custom_handler_model_name].export_wt_003(options)['file_content']
|
|
|
|
expected_row_values = {
|
|
# Headers
|
|
**self._get_header_row_vals(),
|
|
# Sales data
|
|
2: (1, '01/15/2025', 'BILL/2025/01/0005', 1, '', 'partner_a', '700.00', '105.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00'),
|
|
3: (2, '01/15/2025', 'BILL/2025/01/0002', 1, '', 'partner_a', '400.00', '60.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00'),
|
|
4: (3, '01/15/2025', 'BILL/2025/01/0001', 1, '', 'partner_a', '500.00', '75.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00'),
|
|
}
|
|
self._test_xlsx_file(sls, expected_row_values)
|
|
|
|
def _get_header_row_vals(self):
|
|
""" return the header vals to be used in the tests. They are quite long, so this avoids repetition."""
|
|
first_header = (
|
|
"ល.រ\nNo.",
|
|
"កាលបរិច្ឆេទ\nDate",
|
|
"លេខវិក្កយបត្រ\nInvoice Number",
|
|
"អ្នកទទួលប្រាក់\nRecipient",
|
|
"",
|
|
"",
|
|
"ទឹកប្រាក់ត្រូវបើកមុនកាត់ទុក\nAmount",
|
|
"ពន្ធកាត់ទុកលើនិវាសជន\nWithholding Tax on Residents",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"ពន្ធកាត់ទុកលើនិវាសជន\nWithholding Tax on Non-residents",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
)
|
|
second_header = (
|
|
"",
|
|
"",
|
|
"",
|
|
"ប្រភេទ\nType",
|
|
"លេខសម្គាល់ការចុះបញ្ចីពន្ធដារ\nTIN/BIN/TID",
|
|
"ឈ្មោះ\nName",
|
|
"",
|
|
"ការបំពេញសេវានានា សួយសារចំពោះទ្រព្យ អរូបីភាគកម្មក្នុងធនធានរ៉ែ\nPerformance of Service and \nRoyalty for intangibles, \ninterests in minerals(15%)",
|
|
"ការបង់ការប្រាក់ឱ្យអ្នកជាប់ពន្ធ មិនមែនជាធនាគារ\nPayment of interest to \nnon-bank or saving \ninstitution taxpayers(15%)",
|
|
"ការបង់ការប្រាក់ឱ្យអ្នកជាប់ពន្ធ ដែលមានគណនីសន្សំមានកាលកំណត់\nPayment of interest to \ntaxpayers who have fixed \nterm deposit accounts(6%)",
|
|
"ការបង់ការប្រាក់ឱ្យអ្នកជាប់ពន្ធ ដែលមានគណនីសន្សំគ្មានកាលកំណត់\nPayment of interest to \ntaxpayers who have \nnon-fixed term saving(4%)",
|
|
"ការបង់ថ្លៃឈ្នួលចលនទ្រព្យ និង អចលនទ្រព្យ (នីតិបុគ្គល)\nPayment of rental/lease of \nmovable and immovable \nproperty - Legal Person(10%)",
|
|
"ការបង់ថ្លៃឈ្នួលចលនទ្រព្យ និង អចលនទ្រព្យ (រូបវន្តបុគ្គល)\nPayment of rental/lease of \nmovable and immovable \nproperty - Physical Person(10%)",
|
|
"ការបង់ការប្រាក់\nPayment of Interest(14%)",
|
|
"ការបង់សួយសារ ថ្លៃឈ្នួល ចំនូលផ្សេងៗទាក់ទិន និងការប្រើប្រាស់ទ្រព្យសម្បត្តិ\nPayment of royalty, \nrental/leasing, and \nincome related to \nthe use of property(14%)",
|
|
"ការទូទាត់ថ្លៃសេវាគ្រប់គ្រង និងសេវាបច្ចេកទេសនានា\nPayment of management \nfee and technical \nservices(14%)",
|
|
"ការបង់ភាគលាភ\nPayment of Dividend(14%)",
|
|
"សេវា\nService(14%)",
|
|
)
|
|
return {
|
|
0: first_header,
|
|
1: second_header,
|
|
}
|