Migrated module account_credit_hold from 17.0 to 18.0. Fixed views and models and wrote test cases to check functionality.
108 lines
3.9 KiB
Python
108 lines
3.9 KiB
Python
from odoo import fields, models, api, _
|
|
from datetime import date
|
|
|
|
|
|
class Partner(models.Model):
|
|
_inherit = "res.partner"
|
|
|
|
postpone_hold_until = fields.Date(
|
|
string="Postpone Hold",
|
|
help="Grace period specific to this partner despite unpaid invoices.",
|
|
tracking=True,
|
|
)
|
|
|
|
hold_bg = fields.Boolean(
|
|
string="Hold (technical)",
|
|
compute="_compute_hold_bg",
|
|
store=True,
|
|
default=False,
|
|
compute_sudo=True,
|
|
tracking=True,
|
|
)
|
|
on_hold = fields.Boolean(
|
|
string="Account on Hold",
|
|
help="Client account is on hold for unpaid overdue invoices.",
|
|
compute="_compute_on_hold",
|
|
compute_sudo=True,
|
|
)
|
|
|
|
@api.depends("postpone_hold_until", "hold_bg", "commercial_partner_id.hold_bg")
|
|
def _compute_on_hold(self):
|
|
for rec in self:
|
|
# If the parent company is on hold, so are all its sub-contacts and subsidiaries
|
|
if rec.commercial_partner_id != rec and rec.commercial_partner_id.hold_bg:
|
|
if not (rec.commercial_partner_id.postpone_hold_until and rec.commercial_partner_id.postpone_hold_until > date.today()):
|
|
rec.on_hold = True
|
|
continue
|
|
|
|
# If there is no parent company or the parent is not on hold, we compute for ourselves
|
|
if rec.hold_bg and not (
|
|
rec.postpone_hold_until and rec.postpone_hold_until > date.today()
|
|
):
|
|
rec.on_hold = True
|
|
else:
|
|
rec.on_hold = False
|
|
|
|
@api.autovacuum
|
|
def _cleanup_expired_hold_postponements(self):
|
|
expired_holds = self.search([("postpone_hold_until", "<=", date.today())])
|
|
expired_holds.write({"postpone_hold_until": False})
|
|
|
|
def action_credit_hold(self):
|
|
for rec in self:
|
|
rec.hold_bg = True
|
|
rec.message_post(body=_("Placed on credit hold."))
|
|
|
|
def action_lift_credit_hold(self):
|
|
for rec in self:
|
|
rec.hold_bg = False
|
|
rec.message_post(body=_("Credit hold lifted."))
|
|
|
|
@api.model
|
|
def _get_first_followup_level(self):
|
|
return self.env["account_followup.followup.line"].search(
|
|
[("company_id", "parent_of", self.env.company.id)],
|
|
order="delay asc",
|
|
limit=1,
|
|
)
|
|
|
|
@api.depends("followup_status", "followup_line_id")
|
|
def _compute_hold_bg(self):
|
|
first_followup_level = self._get_first_followup_level()
|
|
for rec in self:
|
|
prev_hold_bg = rec.hold_bg
|
|
level = rec.followup_line_id
|
|
if rec.followup_status == "no_action_needed" and not level:
|
|
rec.hold_bg = False
|
|
else:
|
|
rec.hold_bg = prev_hold_bg
|
|
|
|
def _get_followup_report(self, options):
|
|
# Override to prevent hanging on PDF generation
|
|
# Just set minimal required options without generating the report
|
|
options.setdefault('attachment_ids', [])
|
|
options['report_attachment_id'] = False
|
|
|
|
def _execute_followup_partner(self, options=None):
|
|
# Check if we need to place on credit hold before expensive operations
|
|
should_hold = (
|
|
self.followup_status == "in_need_of_action" and
|
|
self.followup_line_id and
|
|
hasattr(self.followup_line_id, 'account_hold') and
|
|
self.followup_line_id.account_hold
|
|
)
|
|
|
|
# If this is just for credit hold and we don't need reports/emails, skip heavy operations
|
|
if options and options.get('credit_hold_only'):
|
|
if should_hold:
|
|
self.action_credit_hold()
|
|
return should_hold
|
|
|
|
# Otherwise run the full followup process
|
|
res = super()._execute_followup_partner(options)
|
|
|
|
# Apply credit hold after successful followup execution
|
|
if should_hold:
|
|
self.action_credit_hold()
|
|
|
|
return res
|