Prepare ncf manager for Tryton 8.0
This commit is contained in:
@@ -0,0 +1,448 @@
|
||||
import datetime
|
||||
from decimal import Decimal
|
||||
from types import SimpleNamespace
|
||||
import unittest
|
||||
|
||||
from trytond.modules.ncf_manager.sequence import Sequence
|
||||
from trytond.modules.ncf_manager.invoice import Invoice
|
||||
from trytond.pool import Pool
|
||||
from trytond.tests.test_tryton import ModuleTestCase, with_transaction
|
||||
from trytond.transaction import Transaction
|
||||
|
||||
|
||||
class NcfManagerTestCase(ModuleTestCase):
|
||||
"Test Ncf Manager module"
|
||||
module = 'ncf_manager'
|
||||
|
||||
def _create_company(self):
|
||||
pool = Pool()
|
||||
Company = pool.get('company.company')
|
||||
Currency = pool.get('currency.currency')
|
||||
Party = pool.get('party.party')
|
||||
|
||||
currency, = Currency.create([{
|
||||
'name': 'Dominican Peso',
|
||||
'code': 'DOP',
|
||||
'symbol': 'RD$',
|
||||
'digits': 2,
|
||||
'rounding': '0.01',
|
||||
}])
|
||||
party, = Party.create([{
|
||||
'name': 'Empresa NCF',
|
||||
'addresses': [('create', [{}])],
|
||||
}])
|
||||
company, = Company.create([{
|
||||
'party': party.id,
|
||||
'currency': currency.id,
|
||||
}])
|
||||
return company
|
||||
|
||||
def _create_chart(self, company):
|
||||
pool = Pool()
|
||||
Account = pool.get('account.account')
|
||||
AccountTemplate = pool.get('account.account.template')
|
||||
CreateChart = pool.get('account.create_chart', type='wizard')
|
||||
Journal = pool.get('account.journal')
|
||||
ModelData = pool.get('ir.model.data')
|
||||
|
||||
session_id, _start, _end = CreateChart.create()
|
||||
chart = CreateChart(session_id)
|
||||
chart.account.account_template = AccountTemplate(ModelData.get_id(
|
||||
'account_do', 'do_account_root'))
|
||||
chart.account.company = company
|
||||
chart.transition_create_account()
|
||||
|
||||
receivable, = Account.search([
|
||||
('company', '=', company.id),
|
||||
('code', '=', '110201'),
|
||||
], limit=1)
|
||||
payable, = Account.search([
|
||||
('company', '=', company.id),
|
||||
('code', '=', '210101'),
|
||||
], limit=1)
|
||||
revenue, = Account.search([
|
||||
('company', '=', company.id),
|
||||
('code', '=', '4102'),
|
||||
], limit=1)
|
||||
chart.properties.company = company
|
||||
chart.properties.account_receivable = receivable
|
||||
chart.properties.account_payable = payable
|
||||
with Transaction().set_context(company=company.id):
|
||||
chart.transition_create_properties()
|
||||
CreateChart.delete(session_id)
|
||||
journal, = Journal.search([('type', '=', 'revenue')], limit=1)
|
||||
return receivable, revenue, journal
|
||||
|
||||
def _create_fiscalyear(self, company, today=None):
|
||||
pool = Pool()
|
||||
FiscalYear = pool.get('account.fiscalyear')
|
||||
InvoiceSequence = pool.get('account.fiscalyear.invoice_sequence')
|
||||
StrictSequence = pool.get('ir.sequence.strict')
|
||||
SequenceType = pool.get('ir.sequence.type')
|
||||
today = today or datetime.date.today()
|
||||
year = today.year
|
||||
|
||||
move_type, = SequenceType.search([
|
||||
('name', '=', 'Account Move'),
|
||||
], limit=1)
|
||||
invoice_type, = SequenceType.search([
|
||||
('name', '=', 'Invoice'),
|
||||
], limit=1)
|
||||
move_sequence, = StrictSequence.create([{
|
||||
'name': 'FY 2026 Moves',
|
||||
'sequence_type': move_type.id,
|
||||
'company': company.id,
|
||||
}])
|
||||
invoice_sequence, = StrictSequence.create([{
|
||||
'name': 'FY 2026 Invoices',
|
||||
'sequence_type': invoice_type.id,
|
||||
'company': company.id,
|
||||
}])
|
||||
fiscalyear, = FiscalYear.create([{
|
||||
'name': str(year),
|
||||
'start_date': datetime.date(year, 1, 1),
|
||||
'end_date': datetime.date(year, 12, 31),
|
||||
'company': company.id,
|
||||
'move_sequence': move_sequence.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])
|
||||
|
||||
def _create_payment_term(self):
|
||||
PaymentTerm = Pool().get('account.invoice.payment_term')
|
||||
term, = PaymentTerm.create([{
|
||||
'name': 'Contado',
|
||||
'lines': [('create', [{'type': 'remainder'}])],
|
||||
}])
|
||||
return term
|
||||
|
||||
def _create_ncf_sequence(self, company, today=None):
|
||||
pool = Pool()
|
||||
ModelData = pool.get('ir.model.data')
|
||||
Manager = pool.get('ncf.manager')
|
||||
ManagerSequence = pool.get('ncf.manager.sequence')
|
||||
StrictSequence = pool.get('ir.sequence.strict')
|
||||
SequenceType = pool.get('ir.sequence.type')
|
||||
today = today or datetime.date.today()
|
||||
|
||||
sequence_type = SequenceType(ModelData.get_id(
|
||||
'ncf_manager', 'sequence_type_out_invoice_01'))
|
||||
strict_sequence, = StrictSequence.create([{
|
||||
'name': 'B01 Factura Credito Fiscal',
|
||||
'sequence_type': sequence_type.id,
|
||||
'company': company.id,
|
||||
'prefix': 'B01',
|
||||
'padding': 8,
|
||||
'number_next': 1,
|
||||
'number_increment': 1,
|
||||
'from_number': 1,
|
||||
'until_number': 2,
|
||||
'expiration_date': datetime.date(today.year, 12, 31),
|
||||
}])
|
||||
manager, = Manager.create([{
|
||||
'company': company.id,
|
||||
'app_date': datetime.date(today.year, 1, 1),
|
||||
'app_number': '0000000001',
|
||||
'auth_number': '0000000001',
|
||||
'state': 'active',
|
||||
}])
|
||||
with Transaction().set_context(company=company.id):
|
||||
sequence, = ManagerSequence.create([{
|
||||
'pos': manager.id,
|
||||
'ncf_type': '1',
|
||||
'ncf_sequences': strict_sequence.id,
|
||||
'invoice_type': 'invoice_customer',
|
||||
'state': 'active',
|
||||
}])
|
||||
return sequence
|
||||
|
||||
@with_transaction()
|
||||
def test_customer_invoice_post_assigns_b01_ncf(self):
|
||||
'Test full NCF assignment workflow on posted customer invoice'
|
||||
pool = Pool()
|
||||
Invoice = pool.get('account.invoice')
|
||||
Party = pool.get('party.party')
|
||||
|
||||
today = datetime.date.today()
|
||||
company = self._create_company()
|
||||
receivable, revenue, journal = self._create_chart(company)
|
||||
self._create_fiscalyear(company, today=today)
|
||||
payment_term = self._create_payment_term()
|
||||
ncf_sequence = self._create_ncf_sequence(company, today=today)
|
||||
party, = Party.create([{
|
||||
'name': 'Cliente NCF',
|
||||
'addresses': [('create', [{}])],
|
||||
}])
|
||||
|
||||
with Transaction().set_context(
|
||||
company=company.id, date=today):
|
||||
invoice, = Invoice.create([{
|
||||
'type': 'out',
|
||||
'company': company.id,
|
||||
'party': party.id,
|
||||
'invoice_address': party.addresses[0].id,
|
||||
'account': receivable.id,
|
||||
'currency': company.currency.id,
|
||||
'invoice_date': today,
|
||||
'accounting_date': today,
|
||||
'journal': journal.id,
|
||||
'payment_term': payment_term.id,
|
||||
'ncf_type': '1',
|
||||
'ncf_sequence': ncf_sequence.id,
|
||||
'revenue_type': '01',
|
||||
'payment_method': '04',
|
||||
'lines': [('create', [{
|
||||
'type': 'line',
|
||||
'description': 'Servicio',
|
||||
'quantity': 1,
|
||||
'unit_price': Decimal('500.00'),
|
||||
'account': revenue.id,
|
||||
}])],
|
||||
}])
|
||||
Invoice.post([invoice])
|
||||
invoice = Invoice(invoice.id)
|
||||
|
||||
self.assertEqual(invoice.state, 'posted')
|
||||
self.assertEqual(invoice.ncf_number, 'B0100000001')
|
||||
self.assertEqual(invoice.ncf_sequence, ncf_sequence)
|
||||
self.assertEqual(
|
||||
invoice.expiration_date, datetime.date(today.year, 12, 31))
|
||||
|
||||
|
||||
del ModuleTestCase
|
||||
|
||||
|
||||
class NcfManagerUnitTestCase(unittest.TestCase):
|
||||
|
||||
def test_sequence_defaults_match_ncf_type(self):
|
||||
sequence = SimpleNamespace(
|
||||
sequence_type=SimpleNamespace(name='Factura de Crédito Fiscal'))
|
||||
|
||||
self.assertEqual(Sequence.on_change_with_prefix(sequence), 'B01')
|
||||
self.assertEqual(Sequence.on_change_with_padding(sequence), 8)
|
||||
|
||||
def test_sequence_approved_and_remaining_quantities(self):
|
||||
sequence = SimpleNamespace(
|
||||
from_number=1,
|
||||
until_number=10,
|
||||
number_next=4)
|
||||
|
||||
self.assertEqual(Sequence.on_change_with_approved_qty(sequence), 10)
|
||||
self.assertEqual(Sequence.on_change_with_remaining_qty(sequence), 7)
|
||||
|
||||
def test_debit_note_code_follows_origin_ncf_type(self):
|
||||
original_enabled = Invoice._ncf_electronic_invoicing_enabled
|
||||
Invoice._ncf_electronic_invoicing_enabled = classmethod(
|
||||
lambda cls: False)
|
||||
try:
|
||||
physical = SimpleNamespace(ncf_type='1')
|
||||
electronic = SimpleNamespace(ncf_type='31')
|
||||
|
||||
self.assertEqual(
|
||||
Invoice._ncf_resolve_debit_note_code(physical), '3')
|
||||
self.assertEqual(
|
||||
Invoice._ncf_resolve_debit_note_code(electronic), '3')
|
||||
finally:
|
||||
Invoice._ncf_electronic_invoicing_enabled = original_enabled
|
||||
|
||||
def test_debit_note_preparation_uses_physical_without_electronic_module(self):
|
||||
electronic_sequence = SimpleNamespace(ncf_type='33')
|
||||
physical_sequence = SimpleNamespace(ncf_type='3')
|
||||
original_find_active_sequence = Invoice._ncf_find_active_sequence
|
||||
original_enabled = Invoice._ncf_electronic_invoicing_enabled
|
||||
|
||||
def find_sequence(cls, invoice, ncf_code):
|
||||
if ncf_code == '33':
|
||||
return electronic_sequence
|
||||
if ncf_code == '3':
|
||||
return physical_sequence
|
||||
return None
|
||||
|
||||
Invoice._ncf_find_active_sequence = classmethod(find_sequence)
|
||||
Invoice._ncf_electronic_invoicing_enabled = classmethod(
|
||||
lambda cls: False)
|
||||
try:
|
||||
origin = SimpleNamespace(
|
||||
ncf_number='B0100000001',
|
||||
ncf_type='1',
|
||||
payment_method='04',
|
||||
revenue_type='01',
|
||||
isr_withholding_type=None)
|
||||
debit_note = SimpleNamespace(
|
||||
company=SimpleNamespace(rec_name='Solutema'),
|
||||
type='out',
|
||||
ncf_number_modified=None,
|
||||
ncf_type=None,
|
||||
ncf_sequence=None,
|
||||
payment_method=None,
|
||||
revenue_type=None,
|
||||
isr_withholding_type=None)
|
||||
|
||||
Invoice._ncf_prepare_debit_note_from_origin(origin, debit_note)
|
||||
finally:
|
||||
Invoice._ncf_find_active_sequence = original_find_active_sequence
|
||||
Invoice._ncf_electronic_invoicing_enabled = original_enabled
|
||||
|
||||
self.assertEqual(debit_note.ncf_number_modified, 'B0100000001')
|
||||
self.assertEqual(debit_note.ncf_type, '3')
|
||||
self.assertEqual(debit_note.ncf_sequence, physical_sequence)
|
||||
self.assertEqual(debit_note.payment_method, '04')
|
||||
self.assertEqual(debit_note.revenue_type, '01')
|
||||
|
||||
def test_debit_note_preparation_prefers_electronic_when_module_is_active(self):
|
||||
electronic_sequence = SimpleNamespace(ncf_type='33')
|
||||
original_find_active_sequence = Invoice._ncf_find_active_sequence
|
||||
original_enabled = Invoice._ncf_electronic_invoicing_enabled
|
||||
|
||||
def find_sequence(cls, invoice, ncf_code):
|
||||
return electronic_sequence if ncf_code == '33' else None
|
||||
|
||||
Invoice._ncf_find_active_sequence = classmethod(find_sequence)
|
||||
Invoice._ncf_electronic_invoicing_enabled = classmethod(
|
||||
lambda cls: True)
|
||||
try:
|
||||
origin = SimpleNamespace(
|
||||
ncf_number='B0100000001',
|
||||
ncf_type='1',
|
||||
payment_method='04',
|
||||
revenue_type='01',
|
||||
isr_withholding_type=None)
|
||||
debit_note = SimpleNamespace(
|
||||
company=SimpleNamespace(rec_name='Solutema'),
|
||||
type='out',
|
||||
ncf_number_modified=None,
|
||||
ncf_type=None,
|
||||
ncf_sequence=None,
|
||||
payment_method=None,
|
||||
revenue_type=None,
|
||||
isr_withholding_type=None)
|
||||
|
||||
Invoice._ncf_prepare_debit_note_from_origin(origin, debit_note)
|
||||
finally:
|
||||
Invoice._ncf_find_active_sequence = original_find_active_sequence
|
||||
Invoice._ncf_electronic_invoicing_enabled = original_enabled
|
||||
|
||||
self.assertEqual(debit_note.ncf_number_modified, 'B0100000001')
|
||||
self.assertEqual(debit_note.ncf_type, '33')
|
||||
self.assertEqual(debit_note.ncf_sequence, electronic_sequence)
|
||||
|
||||
def test_debit_note_preparation_falls_back_to_physical_sequence(self):
|
||||
physical_sequence = SimpleNamespace(ncf_type='3')
|
||||
original_find_active_sequence = Invoice._ncf_find_active_sequence
|
||||
original_enabled = Invoice._ncf_electronic_invoicing_enabled
|
||||
|
||||
def find_sequence(cls, invoice, ncf_code):
|
||||
return physical_sequence if ncf_code == '3' else None
|
||||
|
||||
Invoice._ncf_find_active_sequence = classmethod(find_sequence)
|
||||
Invoice._ncf_electronic_invoicing_enabled = classmethod(
|
||||
lambda cls: True)
|
||||
try:
|
||||
origin = SimpleNamespace(
|
||||
ncf_number='B0100000001',
|
||||
ncf_type='1',
|
||||
payment_method='04',
|
||||
revenue_type='01',
|
||||
isr_withholding_type=None)
|
||||
debit_note = SimpleNamespace(
|
||||
company=SimpleNamespace(rec_name='Solutema'),
|
||||
type='out',
|
||||
ncf_number_modified=None,
|
||||
ncf_type=None,
|
||||
ncf_sequence=None,
|
||||
payment_method=None,
|
||||
revenue_type=None,
|
||||
isr_withholding_type=None)
|
||||
|
||||
Invoice._ncf_prepare_debit_note_from_origin(origin, debit_note)
|
||||
finally:
|
||||
Invoice._ncf_find_active_sequence = original_find_active_sequence
|
||||
Invoice._ncf_electronic_invoicing_enabled = original_enabled
|
||||
|
||||
self.assertEqual(debit_note.ncf_number_modified, 'B0100000001')
|
||||
self.assertEqual(debit_note.ncf_type, '3')
|
||||
self.assertEqual(debit_note.ncf_sequence, physical_sequence)
|
||||
|
||||
def test_credit_note_preparation_prefers_electronic_sequence_for_b_origin(self):
|
||||
electronic_sequence = SimpleNamespace(ncf_type='34')
|
||||
original_find_active_sequence = Invoice._ncf_find_active_sequence
|
||||
original_enabled = Invoice._ncf_electronic_invoicing_enabled
|
||||
|
||||
def find_sequence(cls, invoice, ncf_code):
|
||||
return electronic_sequence if ncf_code == '34' else None
|
||||
|
||||
Invoice._ncf_find_active_sequence = classmethod(find_sequence)
|
||||
Invoice._ncf_electronic_invoicing_enabled = classmethod(
|
||||
lambda cls: True)
|
||||
try:
|
||||
origin = SimpleNamespace(
|
||||
ncf_number='B0100000001',
|
||||
ncf_type='1')
|
||||
credit_note = SimpleNamespace(
|
||||
company=SimpleNamespace(rec_name='Solutema'),
|
||||
type='out',
|
||||
ncf_number_modified=None,
|
||||
ncf_type=None,
|
||||
ncf_sequence=None,
|
||||
payment_method=None,
|
||||
revenue_type=None)
|
||||
|
||||
Invoice._ncf_prepare_credit_note_from_origin(origin, credit_note)
|
||||
finally:
|
||||
Invoice._ncf_find_active_sequence = original_find_active_sequence
|
||||
Invoice._ncf_electronic_invoicing_enabled = original_enabled
|
||||
|
||||
self.assertEqual(credit_note.ncf_number_modified, 'B0100000001')
|
||||
self.assertEqual(credit_note.ncf_type, '34')
|
||||
self.assertEqual(credit_note.ncf_sequence, electronic_sequence)
|
||||
self.assertEqual(credit_note.payment_method, '06')
|
||||
|
||||
def test_modified_ncf_validation_applies_to_debit_notes(self):
|
||||
debit_note = SimpleNamespace(
|
||||
ncf_type='3',
|
||||
ncf_number_modified='B0100000001',
|
||||
type='out')
|
||||
|
||||
Invoice.check_ncf_number_modified(debit_note)
|
||||
|
||||
def test_fiscal_classification_validation_order_for_customer_invoice(self):
|
||||
calls = []
|
||||
invoice = SimpleNamespace(
|
||||
type='out',
|
||||
check_revenue_type=lambda: calls.append('revenue_type'),
|
||||
check_expense_type=lambda: calls.append('expense_type'),
|
||||
check_isr_withholding_type=lambda: calls.append(
|
||||
'isr_withholding_type'),
|
||||
check_payment_method=lambda: calls.append('payment_method'))
|
||||
|
||||
Invoice._ncf_check_fiscal_classification(invoice)
|
||||
|
||||
self.assertEqual(calls, [
|
||||
'revenue_type',
|
||||
'isr_withholding_type',
|
||||
'payment_method',
|
||||
])
|
||||
|
||||
def test_fiscal_classification_validation_order_for_supplier_invoice(self):
|
||||
calls = []
|
||||
invoice = SimpleNamespace(
|
||||
type='in',
|
||||
check_revenue_type=lambda: calls.append('revenue_type'),
|
||||
check_expense_type=lambda: calls.append('expense_type'),
|
||||
check_isr_withholding_type=lambda: calls.append(
|
||||
'isr_withholding_type'),
|
||||
check_payment_method=lambda: calls.append('payment_method'))
|
||||
|
||||
Invoice._ncf_check_fiscal_classification(invoice)
|
||||
|
||||
self.assertEqual(calls, [
|
||||
'expense_type',
|
||||
'isr_withholding_type',
|
||||
'payment_method',
|
||||
])
|
||||
@@ -0,0 +1,46 @@
|
||||
from types import SimpleNamespace
|
||||
import unittest
|
||||
|
||||
from trytond.modules.ncf_manager.ncf_manager import (
|
||||
NCFManager, NCFManagerSequences)
|
||||
from trytond.modules.ncf_manager.sequence import Sequence
|
||||
|
||||
|
||||
class NcfManagerUnitTestCase(unittest.TestCase):
|
||||
|
||||
def test_manager_defaults_and_rec_name(self):
|
||||
self.assertEqual(NCFManager.default_state(), 'draft')
|
||||
manager = SimpleNamespace()
|
||||
manager.id = 42
|
||||
manager.company = SimpleNamespace(rec_name='ACME')
|
||||
manager.app_number = 'APP001'
|
||||
self.assertEqual(NCFManager.get_rec_name(manager, 'name'), 'ACME - APP001')
|
||||
|
||||
def test_sequence_defaults_and_rec_name(self):
|
||||
self.assertEqual(NCFManagerSequences.default_state(), 'draft')
|
||||
sequence = SimpleNamespace()
|
||||
sequence.ncf_type_string = 'Factura de Credito Fiscal - B01'
|
||||
self.assertEqual(
|
||||
NCFManagerSequences.get_rec_name(sequence, 'name'),
|
||||
'Factura de Credito Fiscal ')
|
||||
|
||||
def test_ir_sequence_on_change_values_for_ncf_type(self):
|
||||
sequence = SimpleNamespace()
|
||||
sequence.sequence_type = SimpleNamespace(name='Factura de Consumo')
|
||||
self.assertEqual(Sequence.on_change_with_prefix(sequence), 'B02')
|
||||
self.assertEqual(Sequence.on_change_with_padding(sequence), 8)
|
||||
self.assertEqual(Sequence.on_change_with_name(sequence), 'Factura de Consumo')
|
||||
|
||||
def test_ir_sequence_quantities(self):
|
||||
sequence = SimpleNamespace()
|
||||
sequence.from_number = 10
|
||||
sequence.until_number = 20
|
||||
sequence.number_next = 12
|
||||
self.assertEqual(Sequence.on_change_with_approved_qty(sequence), 11)
|
||||
self.assertEqual(Sequence.on_change_with_remaining_qty(sequence), 9)
|
||||
|
||||
def test_unknown_ncf_type_has_empty_sequence_defaults(self):
|
||||
sequence = SimpleNamespace()
|
||||
sequence.sequence_type = SimpleNamespace(name='Unknown')
|
||||
self.assertIsNone(Sequence.on_change_with_prefix(sequence))
|
||||
self.assertEqual(Sequence.on_change_with_padding(sequence), 0)
|
||||
Reference in New Issue
Block a user