This commit is contained in:
@@ -1,11 +1,8 @@
|
||||
import unittest
|
||||
from datetime import date
|
||||
from decimal import Decimal
|
||||
from types import SimpleNamespace
|
||||
import unittest
|
||||
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
from trytond.modules.account_invoice_do.cron import _build_error_message
|
||||
from trytond.modules.account_invoice_do.invoice import Invoice, InvoiceLine
|
||||
|
||||
|
||||
@@ -15,68 +12,113 @@ class AccountInvoiceDoUnitTestCase(unittest.TestCase):
|
||||
def round(self, amount):
|
||||
return amount.quantize(Decimal('0.01'))
|
||||
|
||||
def test_due_date_from_payment_term(self):
|
||||
invoice = SimpleNamespace()
|
||||
invoice.payment_term = SimpleNamespace(lines=[
|
||||
SimpleNamespace(relativedeltas=[
|
||||
relativedelta(months=1, days=5),
|
||||
]),
|
||||
])
|
||||
invoice.invoice_date = date(2026, 1, 10)
|
||||
self.assertEqual(
|
||||
Invoice.get_remainder_time(invoice, 'due_date'),
|
||||
date(2026, 2, 15))
|
||||
def _tax_line(
|
||||
self, code, amount, template_name=None, rate='0.10'):
|
||||
template = (SimpleNamespace(name=template_name)
|
||||
if template_name else None)
|
||||
tax = SimpleNamespace(
|
||||
group=SimpleNamespace(code=code), template=template,
|
||||
type='percentage', rate=Decimal(rate), amount=None)
|
||||
return SimpleNamespace(tax=tax, amount=Decimal(amount))
|
||||
|
||||
def test_amount_paid_only_for_posted_or_paid_invoices(self):
|
||||
posted = SimpleNamespace(
|
||||
id=1, state='posted', total_amount=Decimal('118.00'),
|
||||
amount_to_pay=Decimal('18.00'), currency=self.Currency())
|
||||
draft = SimpleNamespace(
|
||||
id=2, state='draft', total_amount=Decimal('118.00'),
|
||||
amount_to_pay=Decimal('0.00'), currency=self.Currency())
|
||||
self.assertEqual(Invoice.get_amount_paid([posted, draft], 'amount_paid'), {
|
||||
1: Decimal('100.00'),
|
||||
2: Decimal('0.00'),
|
||||
})
|
||||
|
||||
def test_tax_kind_amount_and_line_total(self):
|
||||
def _line(self, *tax_lines, **values):
|
||||
line = SimpleNamespace(
|
||||
id=1,
|
||||
type='line',
|
||||
currency=self.Currency(),
|
||||
invoice=None,
|
||||
amount=Decimal('100.00'),
|
||||
taxes=[
|
||||
SimpleNamespace(tax_kind='itbis', rate=Decimal('0.18')),
|
||||
SimpleNamespace(tax_kind='tip', rate=Decimal('0.10')),
|
||||
],
|
||||
currency=self.Currency())
|
||||
taxes=[tax.tax for tax in tax_lines],
|
||||
)
|
||||
line._get_taxes = lambda: {
|
||||
index: tax for index, tax in enumerate(tax_lines)}
|
||||
for name, value in values.items():
|
||||
setattr(line, name, value)
|
||||
return line
|
||||
|
||||
def test_due_date_uses_last_payment_term_maturity(self):
|
||||
term = SimpleNamespace(compute=lambda amount, currency, start: [
|
||||
(date(2026, 2, 10), Decimal('40.00')),
|
||||
(date(2026, 3, 10), Decimal('60.00')),
|
||||
])
|
||||
invoice = SimpleNamespace(
|
||||
lines_to_pay=[], payment_term=term, payment_term_date=None,
|
||||
invoice_date=date(2026, 1, 10), currency=self.Currency(),
|
||||
total_amount=Decimal('100.00'))
|
||||
self.assertEqual(
|
||||
InvoiceLine._compute_tax_kind_amount(line, 'itbis'),
|
||||
Decimal('18.00'))
|
||||
Invoice._compute_due_date(invoice), date(2026, 3, 10))
|
||||
|
||||
def test_due_date_prefers_posted_maturity_dates(self):
|
||||
invoice = SimpleNamespace(lines_to_pay=[
|
||||
SimpleNamespace(maturity_date=date(2026, 2, 10)),
|
||||
SimpleNamespace(maturity_date=date(2026, 4, 10)),
|
||||
])
|
||||
self.assertEqual(
|
||||
InvoiceLine.on_change_with_itbis_invoiced_line(line),
|
||||
Decimal('18.00'))
|
||||
Invoice._compute_due_date(invoice), date(2026, 4, 10))
|
||||
|
||||
def test_amount_paid_preserves_credit_note_sign(self):
|
||||
credit = SimpleNamespace(
|
||||
id=1, state='posted', total_amount=Decimal('-118.00'),
|
||||
amount_to_pay=Decimal('-18.00'), currency=self.Currency())
|
||||
self.assertEqual(
|
||||
InvoiceLine._compute_line_total_amount(line),
|
||||
Decimal('128.00'))
|
||||
Invoice.get_amount_paid([credit], 'amount_paid'),
|
||||
{1: Decimal('-100.00')})
|
||||
|
||||
def test_days_elapsed_does_not_require_payment_term(self):
|
||||
invoice = SimpleNamespace(
|
||||
state='posted', invoice_date=date(2026, 1, 1), due_date=None,
|
||||
_today=lambda: date(2026, 1, 11))
|
||||
invoice._positive_age = lambda reference: Invoice._positive_age(
|
||||
invoice, reference)
|
||||
self.assertEqual(Invoice.get_days_elapsed(invoice), 10)
|
||||
self.assertIsNone(Invoice.get_days_overdue(invoice))
|
||||
|
||||
def test_tax_amounts_use_tryton_computed_tax_lines(self):
|
||||
line = self._line(
|
||||
self._tax_line('ITBIS', '18.00'),
|
||||
self._tax_line('ITBIS', '-5.40', rate='-0.054'),
|
||||
self._tax_line('ISC', '10.00'),
|
||||
self._tax_line('CDT', '2.00'),
|
||||
self._tax_line('ISR', '-10.00'),
|
||||
self._tax_line('OTROS', '10.00', 'Legal Tip 10%'))
|
||||
self.assertEqual(InvoiceLine._compute_amounts(line), {
|
||||
'itbis': Decimal('18.00'),
|
||||
'itbis_withholding': Decimal('5.40'),
|
||||
'isc': Decimal('10.00'),
|
||||
'cdt': Decimal('2.00'),
|
||||
'isr_withholding': Decimal('10.00'),
|
||||
'tip': Decimal('10.00'),
|
||||
'line_total': Decimal('124.60'),
|
||||
})
|
||||
|
||||
def test_credit_note_itbis_is_not_classified_as_withholding(self):
|
||||
line = self._line(self._tax_line('ITBIS', '-18.00', rate='0.18'))
|
||||
self.assertEqual(InvoiceLine._compute_amounts(line), {
|
||||
'itbis': Decimal('18.00'),
|
||||
'itbis_withholding': Decimal('0.00'),
|
||||
'isc': Decimal('0.00'),
|
||||
'cdt': Decimal('0.00'),
|
||||
'isr_withholding': Decimal('0.00'),
|
||||
'tip': Decimal('0.00'),
|
||||
'line_total': Decimal('82.00'),
|
||||
})
|
||||
|
||||
def test_tax_kind_ignores_incomplete_external_tax_lines(self):
|
||||
self.assertIsNone(InvoiceLine._tax_kind(SimpleNamespace(tax=None)))
|
||||
tax = SimpleNamespace(
|
||||
group=None, template=None, type='percentage',
|
||||
rate=Decimal('0.18'), amount=None)
|
||||
self.assertIsNone(InvoiceLine._tax_kind(SimpleNamespace(tax=tax)))
|
||||
|
||||
def test_subtotal_resets_after_previous_subtotal(self):
|
||||
invoice = SimpleNamespace()
|
||||
line1 = SimpleNamespace(
|
||||
type='line', amount=Decimal('100.00'), taxes=[], currency=None)
|
||||
line1 = self._line()
|
||||
subtotal1 = SimpleNamespace(
|
||||
id=1, type='subtotal', invoice=invoice, currency=None)
|
||||
line2 = SimpleNamespace(
|
||||
type='line', amount=Decimal('50.00'), taxes=[], currency=None)
|
||||
line2 = self._line(amount=Decimal('50.00'))
|
||||
subtotal2 = SimpleNamespace(
|
||||
id=2, type='subtotal', invoice=invoice, currency=None)
|
||||
invoice.lines = [line1, subtotal1, line2, subtotal2]
|
||||
self.assertEqual(
|
||||
InvoiceLine._compute_line_total_amount(subtotal2),
|
||||
InvoiceLine._compute_amounts(subtotal2)['line_total'],
|
||||
Decimal('50.00'))
|
||||
|
||||
def test_cron_error_message_uses_fallbacks(self):
|
||||
inv = SimpleNamespace(id=10)
|
||||
message = _build_error_message(inv, ValueError('boom'))
|
||||
self.assertIn('ID: 10', message)
|
||||
self.assertIn('Numero: N/D', message)
|
||||
self.assertIn('Error: boom', message)
|
||||
|
||||
+15
-100
@@ -1,11 +1,8 @@
|
||||
import datetime
|
||||
from decimal import Decimal
|
||||
from types import SimpleNamespace
|
||||
import unittest
|
||||
|
||||
from trytond.modules.account.tests.test_module import (
|
||||
create_chart, get_fiscalyear)
|
||||
from trytond.modules.account_invoice_do.invoice import InvoiceLine
|
||||
from trytond.modules.company.tests import create_company, set_company
|
||||
from trytond.pool import Pool
|
||||
from trytond.tests.test_tryton import ModuleTestCase, with_transaction
|
||||
@@ -15,7 +12,8 @@ class AccountInvoiceDoTestCase(ModuleTestCase):
|
||||
"Test Account Invoice Do module"
|
||||
module = 'account_invoice_do'
|
||||
|
||||
def _prepare_invoice_company(self, chart='account_do.do_account_root'):
|
||||
def _prepare_invoice_company(
|
||||
self, chart='account_do.do_account_root_en'):
|
||||
pool = Pool()
|
||||
FiscalYear = pool.get('account.fiscalyear')
|
||||
InvoiceSequence = pool.get('account.fiscalyear.invoice_sequence')
|
||||
@@ -83,59 +81,17 @@ class AccountInvoiceDoTestCase(ModuleTestCase):
|
||||
return lines[0]
|
||||
|
||||
@with_transaction()
|
||||
def test_customer_invoice_without_product_posts_and_computes_do_fields(self):
|
||||
def test_customer_invoice_without_product_computes_do_fields(self):
|
||||
'Post customer invoice and compute Dominican invoice helper fields'
|
||||
pool = Pool()
|
||||
Account = pool.get('account.account')
|
||||
FiscalYear = pool.get('account.fiscalyear')
|
||||
InvoiceSequence = pool.get('account.fiscalyear.invoice_sequence')
|
||||
Invoice = pool.get('account.invoice')
|
||||
Journal = pool.get('account.journal')
|
||||
Party = pool.get('party.party')
|
||||
PaymentTerm = pool.get('account.invoice.payment_term')
|
||||
Sequence = pool.get('ir.sequence.strict')
|
||||
SequenceType = pool.get('ir.sequence.type')
|
||||
|
||||
today = datetime.date.today()
|
||||
company = create_company()
|
||||
create_chart(company)
|
||||
fiscalyear = get_fiscalyear(company, today=today)
|
||||
sequence_type, = SequenceType.search([
|
||||
('name', '=', 'Invoice'),
|
||||
], limit=1)
|
||||
invoice_sequence, = Sequence.create([{
|
||||
'name': fiscalyear.name,
|
||||
'sequence_type': sequence_type.id,
|
||||
'company': company.id,
|
||||
}])
|
||||
fiscalyear.invoice_sequences = [InvoiceSequence(
|
||||
company=company,
|
||||
in_invoice_sequence=invoice_sequence,
|
||||
in_credit_note_sequence=invoice_sequence,
|
||||
out_invoice_sequence=invoice_sequence,
|
||||
out_credit_note_sequence=invoice_sequence)]
|
||||
fiscalyear.save()
|
||||
FiscalYear.create_period([fiscalyear])
|
||||
|
||||
receivable, = Account.search([
|
||||
('company', '=', company.id),
|
||||
('type.receivable', '=', True),
|
||||
('party_required', '=', True),
|
||||
], limit=1)
|
||||
revenue, = Account.search([
|
||||
('company', '=', company.id),
|
||||
('type.revenue', '=', True),
|
||||
('closed', '!=', True),
|
||||
], limit=1)
|
||||
company, term, today = self._prepare_invoice_company()
|
||||
receivable = self._account(company, '110201')
|
||||
revenue = self._account(company, '4102')
|
||||
journal, = Journal.search([('type', '=', 'revenue')], limit=1)
|
||||
party, = Party.create([{
|
||||
'name': 'Cliente local',
|
||||
'addresses': [('create', [{}])],
|
||||
}])
|
||||
term, = PaymentTerm.create([{
|
||||
'name': 'Contado',
|
||||
'lines': [('create', [{'type': 'remainder'}])],
|
||||
}])
|
||||
party = self._party('Cliente local')
|
||||
|
||||
with set_company(company):
|
||||
invoice, = Invoice.create([{
|
||||
@@ -182,8 +138,8 @@ class AccountInvoiceDoTestCase(ModuleTestCase):
|
||||
payable = self._account(company, '210101')
|
||||
revenue = self._account(company, '4102')
|
||||
expense = self._account(company, '6103')
|
||||
sale_tax = self._tax(company, 'ITBIS 18% Ventas')
|
||||
purchase_tax = self._tax(company, 'ITBIS 18% Compras')
|
||||
sale_tax = self._tax(company, 'ITBIS 18% Sales')
|
||||
purchase_tax = self._tax(company, 'ITBIS 18% Purchases')
|
||||
revenue_journal, = Journal.search([('type', '=', 'revenue')], limit=1)
|
||||
expense_journal, = Journal.search([('type', '=', 'expense')], limit=1)
|
||||
|
||||
@@ -237,61 +193,20 @@ class AccountInvoiceDoTestCase(ModuleTestCase):
|
||||
in_invoice = Invoice(in_invoice.id)
|
||||
|
||||
self.assertEqual(out_invoice.tax_amount, Decimal('180.00'))
|
||||
out_line, = out_invoice.lines
|
||||
self.assertEqual(out_line.itbis_invoiced_line, Decimal('180.00'))
|
||||
self.assertEqual(out_line.line_total, Decimal('1180.00'))
|
||||
out_tax_line = self._invoice_tax_line(out_invoice, '21020101')
|
||||
self.assertEqual(out_tax_line.debit, Decimal('0.00'))
|
||||
self.assertEqual(out_tax_line.credit, Decimal('180.00'))
|
||||
|
||||
self.assertEqual(in_invoice.tax_amount, Decimal('180.00'))
|
||||
in_line, = in_invoice.lines
|
||||
self.assertEqual(in_line.itbis_invoiced_line, Decimal('180.00'))
|
||||
self.assertEqual(in_line.line_total, Decimal('1180.00'))
|
||||
in_tax_line = self._invoice_tax_line(in_invoice, '11040101')
|
||||
self.assertEqual(in_tax_line.debit, Decimal('180.00'))
|
||||
self.assertEqual(in_tax_line.credit, Decimal('0.00'))
|
||||
|
||||
|
||||
del ModuleTestCase
|
||||
|
||||
|
||||
class AccountInvoiceDoUnitTestCase(unittest.TestCase):
|
||||
|
||||
def _currency(self):
|
||||
return SimpleNamespace(round=lambda amount: amount.quantize(
|
||||
Decimal('0.01')))
|
||||
|
||||
def _tax(self, kind, rate):
|
||||
return SimpleNamespace(tax_kind=kind, rate=Decimal(rate))
|
||||
|
||||
def _line(self, **values):
|
||||
defaults = {
|
||||
'id': 1,
|
||||
'type': 'line',
|
||||
'product': None,
|
||||
'taxes': [],
|
||||
'currency': self._currency(),
|
||||
'amount': Decimal('100.00'),
|
||||
}
|
||||
defaults.update(values)
|
||||
return SimpleNamespace(**defaults)
|
||||
|
||||
def test_product_type_is_empty_without_product(self):
|
||||
line = self._line()
|
||||
self.assertEqual(InvoiceLine.on_change_with_product_type(line), '')
|
||||
|
||||
def test_tax_kind_amounts_are_computed_from_line_base(self):
|
||||
line = self._line(taxes=[
|
||||
self._tax('itbis', '0.18'),
|
||||
self._tax('isc', '0.10'),
|
||||
])
|
||||
self.assertEqual(
|
||||
InvoiceLine._compute_tax_kind_amount(line, 'itbis'),
|
||||
Decimal('18.00'))
|
||||
self.assertEqual(
|
||||
InvoiceLine._compute_tax_kind_amount(line, 'isc'),
|
||||
Decimal('10.00'))
|
||||
|
||||
def test_line_total_includes_all_line_taxes(self):
|
||||
line = self._line(taxes=[
|
||||
self._tax('itbis', '0.18'),
|
||||
self._tax('cdt', '0.02'),
|
||||
])
|
||||
self.assertEqual(
|
||||
InvoiceLine._compute_line_total_amount(line),
|
||||
Decimal('120.00'))
|
||||
|
||||
Reference in New Issue
Block a user