1
0
forked from Mapan/odoo17e
odoo17e-kedaikipas58/addons/account_followup/tests/test_account_followup.py
2024-12-10 09:04:09 +07:00

400 lines
20 KiB
Python

# -*- coding: utf-8 -*-
from unittest.mock import patch
from freezegun import freeze_time
from odoo import Command, fields
from odoo.tests import tagged
from odoo.tests.common import Form
from odoo.addons.account.tests.common import AccountTestInvoicingCommon
from dateutil.relativedelta import relativedelta
@tagged('post_install', '-at_install')
class TestAccountFollowupReports(AccountTestInvoicingCommon):
@classmethod
def setUpClass(cls, chart_template_ref=None):
super().setUpClass(chart_template_ref=chart_template_ref)
cls.env['account_followup.followup.line'].search([]).unlink()
def create_followup(self, delay):
return self.env['account_followup.followup.line'].create({
'name': f'followup {delay}',
'delay': delay,
'send_email': False,
'company_id': self.company_data['company'].id
})
def create_invoice(self, date):
invoice = self.env['account.move'].create({
'move_type': 'out_invoice',
'invoice_date': date,
'partner_id': self.partner_a.id,
'invoice_line_ids': [Command.create({
'quantity': 1,
'price_unit': 500,
'tax_ids': [],
})]
})
invoice.action_post()
return invoice
def assertPartnerFollowup(self, partner, status, line):
partner.invalidate_recordset(['followup_status', 'followup_line_id'])
# Since we are querying multiple times with data changes in the same transaction (for the purpose of tests),
# we need to invalidated the cache in database
self.env.cr.execute("DROP TABLE IF EXISTS followup_data_cache")
res = partner._query_followup_data()
self.assertEqual(res.get(partner.id, {}).get('followup_status'), status)
self.assertEqual(res.get(partner.id, {}).get('followup_line_id'), line.id if line else None)
self.assertEqual(partner.followup_status, status or 'no_action_needed')
self.assertEqual(partner.followup_line_id.id if partner.followup_line_id else None, line.id if line else None)
def test_followup_responsible(self):
"""
Test that the responsible is correctly set
"""
self.first_followup_line = self.create_followup(delay=-10)
user1 = self.env['res.users'].create({
'name': 'A User',
'login': 'a_user',
'email': 'a@user.com',
'groups_id': [(6, 0, [self.env.ref('account.group_account_user').id])]
})
user2 = self.env['res.users'].create({
'name': 'Another User',
'login': 'another_user',
'email': 'another@user.com',
'groups_id': [(6, 0, [self.env.ref('account.group_account_user').id])]
})
# 1- no info, use current user
self.assertEqual(self.partner_a._get_followup_responsible(), self.env.user)
# 2- set invoice user
invoice1 = self.init_invoice('out_invoice', partner=self.partner_a,
invoice_date=fields.Date.from_string('2000-01-01'),
amounts=[2000])
invoice2 = self.init_invoice('out_invoice', partner=self.partner_a,
invoice_date=fields.Date.from_string('2000-01-01'),
amounts=[1000])
invoice1.invoice_user_id = user1
invoice2.invoice_user_id = user2
(invoice1 + invoice2).action_post()
# Should pick invoice_user_id of the most delayed move, with highest residual amount in case of tie (invoice1)
self.assertEqual(self.partner_a._get_followup_responsible(), user1)
self.partner_a.followup_line_id = self.first_followup_line
# 3- A followup responsible user has been set on the partner
self.partner_a.followup_responsible_id = user2
self.assertEqual(self.partner_a._get_followup_responsible(), user2)
# 4- Modify the default responsible on followup level
self.partner_a.followup_line_id.activity_default_responsible_type = 'salesperson'
self.assertEqual(self.partner_a._get_followup_responsible(), user1)
self.partner_a.followup_line_id.activity_default_responsible_type = 'account_manager'
self.partner_a.user_id = user2
self.assertEqual(self.partner_a._get_followup_responsible(), self.partner_a.user_id)
def test_followup_line_and_status(self):
self.first_followup_line = self.create_followup(delay=-10)
self.second_followup_line = self.create_followup(delay=10)
self.third_followup_line = self.create_followup(delay=15)
self.create_invoice('2022-01-02')
with freeze_time('2021-12-20'):
# Today < due date + delay first followup level (negative delay -> reminder before due date)
self.assertPartnerFollowup(self.partner_a, 'no_action_needed', self.first_followup_line)
with freeze_time('2021-12-24'):
# Today = due date + delay first followup level
self.assertPartnerFollowup(self.partner_a, 'in_need_of_action', self.first_followup_line)
# followup_next_action_date not exceeded but no invoice is overdue,
# we should not be in status 'with_overdue_invoices' but 'no action needed'
self.partner_a.followup_next_action_date = fields.Date.from_string('2021-12-25')
self.assertPartnerFollowup(self.partner_a, 'no_action_needed', self.first_followup_line)
with freeze_time('2022-01-13'):
# Today > due date + delay second followup level but first followup level not processed yet
self.assertPartnerFollowup(self.partner_a, 'in_need_of_action', self.first_followup_line)
self.partner_a._execute_followup_partner(options={'snailmail': False})
# Due date exceeded but first followup level processed
# followup_next_action_date set in 20 days (delay 2nd level - delay 1st level = 10 - (-10) = 20)
self.assertPartnerFollowup(self.partner_a, 'with_overdue_invoices', self.second_followup_line)
self.assertEqual(self.partner_a.followup_next_action_date, fields.Date.from_string('2022-02-02'))
with freeze_time('2022-02-03'):
# followup_next_action_date exceeded and invoice not reconciled yet
self.assertPartnerFollowup(self.partner_a, 'in_need_of_action', self.second_followup_line)
# Exclude every unreconciled invoice lines
self.partner_a.unreconciled_aml_ids.blocked = True
# Every unreconciled invoice lines are blocked, the result from the query will be None
self.assertPartnerFollowup(self.partner_a, None, None)
# It resets if we unblock
self.partner_a.unreconciled_aml_ids.blocked = False
self.assertPartnerFollowup(self.partner_a, 'in_need_of_action', self.second_followup_line)
self.env['account.payment.register'].create({
'line_ids': self.partner_a.unreconciled_aml_ids,
})._create_payments()
self.assertPartnerFollowup(self.partner_a, None, None)
def test_followup_multiple_invoices(self):
followup_10 = self.create_followup(delay=10)
followup_15 = self.create_followup(delay=15)
followup_30 = self.create_followup(delay=30)
self.create_invoice('2022-01-01')
self.create_invoice('2022-01-02')
# 9 days are not passed yet for the first followup level, current delay is 10-0=10
with freeze_time('2022-01-10'):
self.assertPartnerFollowup(self.partner_a, 'with_overdue_invoices', followup_10)
# 10 days passed, current delay is 10-0=10, need to take action
with freeze_time('2022-01-11'):
self.assertPartnerFollowup(self.partner_a, 'in_need_of_action', followup_10)
self.partner_a._execute_followup_partner(options={'snailmail': False})
self.assertPartnerFollowup(self.partner_a, 'with_overdue_invoices', followup_15)
# action taken 4 days ago, current delay is 15-10=5, nothing needed
with freeze_time('2022-01-15'):
self.assertPartnerFollowup(self.partner_a, 'with_overdue_invoices', followup_15)
# action taken 5 days ago, current delay is 15-10=5, need to take action
with freeze_time('2022-01-16'):
self.assertPartnerFollowup(self.partner_a, 'in_need_of_action', followup_15)
self.partner_a._execute_followup_partner(options={'snailmail': False})
self.assertPartnerFollowup(self.partner_a, 'with_overdue_invoices', followup_30)
# action taken 14 days ago, current delay is 30-15=15, nothing needed
with freeze_time('2022-01-30'):
self.assertPartnerFollowup(self.partner_a, 'with_overdue_invoices', followup_30)
# action taken 15 days ago, current delay is 30-15=15, need to take action
with freeze_time('2022-01-31'):
self.assertPartnerFollowup(self.partner_a, 'in_need_of_action', followup_30)
self.partner_a._execute_followup_partner(options={'snailmail': False})
self.assertPartnerFollowup(self.partner_a, 'with_overdue_invoices', followup_30)
# action taken 13 days ago, current delay is 15 (same on repeat), nothing needed
with freeze_time('2022-02-14'):
self.assertPartnerFollowup(self.partner_a, 'with_overdue_invoices', followup_30)
# action taken 14 days ago, current delay is 15 (same on repeat), need to take action
with freeze_time('2022-02-15'):
self.assertPartnerFollowup(self.partner_a, 'in_need_of_action', followup_30)
self.partner_a._execute_followup_partner(options={'snailmail': False})
self.assertPartnerFollowup(self.partner_a, 'with_overdue_invoices', followup_30)
def test_followup_multiple_invoices_with_first_payment(self):
# Test the behavior of multiple invoices when the first one is paid
followup_10 = self.create_followup(delay=10)
followup_15 = self.create_followup(delay=15)
invoice_01 = self.create_invoice('2022-01-01')
self.create_invoice('2022-01-02')
# 9 days are not passed yet for the first followup level, current delay is 10-0=10
with freeze_time('2022-01-10'):
self.assertPartnerFollowup(self.partner_a, 'with_overdue_invoices', followup_10)
# 10 days passed, current delay is 10-0=10, need to take action
with freeze_time('2022-01-11'):
self.assertPartnerFollowup(self.partner_a, 'in_need_of_action', followup_10)
# followup level for the second invoice shouldn't change since it's only 9 days overdue
self.partner_a._execute_followup_partner(options={'snailmail': False})
self.assertPartnerFollowup(self.partner_a, 'with_overdue_invoices', followup_15)
self.env['account.payment.register'].create({
'line_ids': invoice_01.line_ids.filtered(lambda l: l.display_type == 'payment_term'),
})._create_payments()
# partner followup level goes back to 10 days after paying the first invoice
self.assertPartnerFollowup(self.partner_a, 'with_overdue_invoices', followup_10)
# action taken 4 days ago, current delay is 15-10=5, nothing needed
with freeze_time('2022-01-15'):
self.assertPartnerFollowup(self.partner_a, 'with_overdue_invoices', followup_10)
# action taken 5 days ago, current delay is 15-10=5, need to take action
with freeze_time('2022-01-16'):
self.assertPartnerFollowup(self.partner_a, 'in_need_of_action', followup_10)
self.partner_a._execute_followup_partner(options={'snailmail': False})
self.assertPartnerFollowup(self.partner_a, 'with_overdue_invoices', followup_15)
def test_followup_multiple_invoices_with_last_payment(self):
# Test the behavior of multiple invoices when the last one is paid
# Should behave exactly like test_followup_multiple_invoices_with_first_payment
# because the followup is done at the same time.
followup_10 = self.create_followup(delay=10)
followup_15 = self.create_followup(delay=15)
followup_30 = self.create_followup(delay=30)
self.create_invoice('2022-01-01')
invoice_02 = self.create_invoice('2022-01-02')
# 9 days are not passed yet for the first followup level, current delay is 10-0=10
with freeze_time('2022-01-10'):
self.assertPartnerFollowup(self.partner_a, 'with_overdue_invoices', followup_10)
# 10 days passed, current delay is 10-0=10, need to take action
with freeze_time('2022-01-11'):
self.assertPartnerFollowup(self.partner_a, 'in_need_of_action', followup_10)
self.partner_a._execute_followup_partner(options={'snailmail': False})
self.assertPartnerFollowup(self.partner_a, 'with_overdue_invoices', followup_15)
self.env['account.payment.register'].create({
'line_ids': invoice_02.line_ids.filtered(lambda l: l.display_type == 'payment_term'),
})._create_payments()
self.assertPartnerFollowup(self.partner_a, 'with_overdue_invoices', followup_15)
# action taken 4 days ago, current delay is 15-10=5, nothing needed
with freeze_time('2022-01-15'):
self.assertPartnerFollowup(self.partner_a, 'with_overdue_invoices', followup_15)
# action taken 5 days ago, current delay is 15-10=5, need to take action
with freeze_time('2022-01-16'):
self.assertPartnerFollowup(self.partner_a, 'in_need_of_action', followup_15)
self.partner_a._execute_followup_partner(options={'snailmail': False})
self.assertPartnerFollowup(self.partner_a, 'with_overdue_invoices', followup_30)
def test_followup_contacts(self):
followup_contacts = self.partner_a._get_all_followup_contacts()
billing_contact = self.env['res.partner'].browse(self.partner_a.address_get(['invoice'])['invoice'])
self.assertEqual(billing_contact, followup_contacts)
followup_partner_1 = self.env['res.partner'].create({
'name': 'followup partner 1',
'parent_id': self.partner_a.id,
'type': 'followup',
})
followup_partner_2 = self.env['res.partner'].create({
'name': 'followup partner 2',
'parent_id': self.partner_a.id,
'type': 'followup',
})
expected_partners = followup_partner_1 + followup_partner_2
followup_contacts = self.partner_a._get_all_followup_contacts()
self.assertEqual(expected_partners, followup_contacts)
def test_followup_cron(self):
cron = self.env.ref('account_followup.ir_cron_auto_post_draft_entry')
followup_10 = self.create_followup(delay=10)
followup_10.auto_execute = True
self.create_invoice('2022-01-01')
# Check that no followup is automatically done if there is no action needed
with freeze_time('2022-01-10'), patch.object(type(self.env['res.partner']), '_send_followup') as patched:
self.assertPartnerFollowup(self.partner_a, 'with_overdue_invoices', followup_10)
cron.method_direct_trigger()
patched.assert_not_called()
self.assertPartnerFollowup(self.partner_a, 'with_overdue_invoices', followup_10)
# Check that the action is taken one and only one time when there is an action needed
with freeze_time('2022-01-11'), patch.object(type(self.env['res.partner']), '_send_followup') as patched:
self.assertPartnerFollowup(self.partner_a, 'in_need_of_action', followup_10)
cron.method_direct_trigger()
patched.assert_called_once()
self.assertPartnerFollowup(self.partner_a, 'with_overdue_invoices', followup_10)
def test_onchange_residual_amount(self):
'''
Test residual onchange on account move lines: the residual amount is
computed using an sql query. This test makes sure the computation also
works properly during onchange (on records having a NewId).
'''
invoice = self.create_invoice('2016-01-01')
self.create_invoice('2016-01-02')
self.env['account.payment.register'].with_context(active_ids=invoice.ids, active_model='account.move').create({
'payment_date': invoice.date,
'amount': 100,
})._create_payments()
self.assertRecordValues(self.partner_a, [{'total_due': 900.0}])
self.assertRecordValues(self.partner_a.unreconciled_aml_ids.sorted(), [
{'amount_residual_currency': 500.0},
{'amount_residual_currency': 400.0},
])
with Form(self.partner_a, view='account_followup.customer_statements_form_view') as form:
# The Form() does not mock the default_order defined on the view.
# We need to define which line is the first with the date
for index, _orm_command in enumerate(form._values['unreconciled_aml_ids']):
with form.unreconciled_aml_ids.edit(index) as aml_form:
if aml_form.invoice_date == '2016-01-01':
aml_form.blocked = True
self.assertRecordValues(self.partner_a, [{'total_due': 500.0}])
self.assertRecordValues(self.partner_a.unreconciled_aml_ids.sorted(), [
{'amount_residual_currency': 500.0},
{'amount_residual_currency': 400.0},
])
def test_compute_total_due(self):
self.create_invoice('2016-01-01')
self.partner_a.unreconciled_aml_ids.blocked = True
self.create_invoice('2017-01-01')
self.create_invoice(fields.Date.today() + relativedelta(months=1))
self.assertRecordValues(self.partner_a, [{'total_due': 1000.0}])
self.assertRecordValues(self.partner_a, [{'total_overdue': 500.0}])
def test_send_followup_no_due_date(self):
"""
test sending a followup report with an empty due date field
"""
self.create_followup(delay=0)
self.create_invoice('2022-01-01')
self.partner_a.unreconciled_aml_ids.write({
'date_maturity': False,
})
self.partner_a._execute_followup_partner(options={
'partner_id': self.partner_a.id,
'manual_followup': True,
'snailmail': False,
})
def test_manual_reminder_get_template_mail_addresses(self):
"""
When opening account_followup.manual_reminder, the partner should always be in `email_recipients_ids`
When adding a template, the template's partner_to, email_cc and email_to should be added to `email_recipient_ids` as well
"""
mail_partner = self.env['res.partner'].create({
'name': 'Mai Lang',
'email': 'mail.ang@test.com',
})
mail_cc = self.env['res.partner'].create({
'name': 'John Carmac',
'email': 'john.carmac@example.me',
})
mail_template = self.env['mail.template'].create({
'name': 'reminder',
'model_id': self.env['ir.model']._get_id('res.partner'),
'email_cc': mail_cc.email,
})
reminder = self.env['account_followup.manual_reminder'].with_context(
active_model='res.partner',
active_ids=mail_partner.id,
).create({'template_id': mail_template.id})
self.assertTrue(mail_partner in reminder.email_recipient_ids, "Mai Lang should be in the Email Recipients List")
reminder.template_id = mail_template
self.assertTrue(mail_cc in reminder.email_recipient_ids, "John Carmac should be in the Email Recipients list.")
self.assertTrue(mail_partner in reminder.email_recipient_ids, "Mai Lang should still be in the Email Recipients List")