from datetime import timedelta from odoo import fields from odoo.tests.common import TransactionCase, tagged @tagged('post_install', '-at_install') class TestMembershipRenewal(TransactionCase): def setUp(self): super().setUp() self.env['ir.config_parameter'].sudo().set_param('community_membership.renewal_offsets', '30,14,7') self.tier = self.env['community.membership.tier'].create({ 'name': 'Individual', 'code': 'RENEW-IND', 'price': 50.0, 'period': 'annual', }) self.partner = self.env['res.partner'].create({ 'name': 'Renewal Tester', 'email': 'renewal.tester@example.com', 'membership_tier_id': self.tier.id, }) self.partner.action_activate_membership() def _set_days_to_expiry(self, days): today = fields.Date.context_today(self.partner) self.partner.membership_expiry = today + timedelta(days=days) def test_reminder_sent_at_non_final_offset(self): self._set_days_to_expiry(14) mail_count_before = self.env['mail.mail'].search_count([]) self.env['res.partner']._cron_process_membership_renewals() self.partner.invalidate_recordset() self.assertEqual(self.partner.membership_state, 'active', "Non-final offset should not change state") mail_count_after = self.env['mail.mail'].search_count([]) self.assertGreater(mail_count_after, mail_count_before, "A renewal_upcoming reminder should be queued") def test_state_flips_to_renewal_due_at_final_offset(self): self._set_days_to_expiry(7) mail_count_before = self.env['mail.mail'].search_count([]) self.env['res.partner']._cron_process_membership_renewals() self.partner.invalidate_recordset() self.assertEqual(self.partner.membership_state, 'renewal_due') mail_count_after = self.env['mail.mail'].search_count([]) self.assertGreater(mail_count_after, mail_count_before, "A renewal_due notice should be queued") def test_expiry_creates_draft_invoice_and_sends_mail(self): self._set_days_to_expiry(-1) invoice_count_before = self.env['account.move'].search_count([ ('partner_id', '=', self.partner.id), ('move_type', '=', 'out_invoice'), ]) mail_count_before = self.env['mail.mail'].search_count([]) self.env['res.partner']._cron_process_membership_renewals() self.partner.invalidate_recordset() self.assertEqual(self.partner.membership_state, 'expired') invoices = self.env['account.move'].search([ ('partner_id', '=', self.partner.id), ('move_type', '=', 'out_invoice'), ]) self.assertEqual(len(invoices), invoice_count_before + 1) self.assertEqual(invoices[-1].state, 'draft', "Renewal invoice should be left as draft for review") mail_count_after = self.env['mail.mail'].search_count([]) self.assertGreater(mail_count_after, mail_count_before, "An expiry notice should be queued") def test_no_action_outside_offsets(self): self._set_days_to_expiry(20) self.env['res.partner']._cron_process_membership_renewals() self.partner.invalidate_recordset() self.assertEqual(self.partner.membership_state, 'active')