Initial migration of account_credit_hold for testing in 18.0

This commit is contained in:
Marc Durepos 2025-09-04 11:38:37 -04:00
parent 6ea4b324b3
commit 91c52af6f4
13 changed files with 312 additions and 0 deletions

View file

@ -0,0 +1 @@
from . import models

View file

@ -0,0 +1,20 @@
{
"name": "Account Credit Hold",
"version": "18.0.1.1.1",
"summary": "Allows setting clients on credit hold, blocking the ability confirm a new sales order.",
"category": "Accounting/Accounting",
"author": "Bemade Inc.",
"maintainer": "Marc Durepos <marc@bemade.org>",
"website": "http://www.bemade.org",
"license": "LGPL-3",
"depends": ["sale", "account_followup", "stock"],
"data": [
"views/account_followup_views.xml",
"views/sale_order_views.xml",
"views/res_partner_views.xml",
"views/stock_picking_views.xml",
],
"demo": [],
"installable": True,
"auto_install": False,
}

View file

@ -0,0 +1,5 @@
from . import account_followup
from . import res_partner
from . import sale_order
from . import stock_picking
from . import account_followup_report

View file

@ -0,0 +1,8 @@
from odoo import fields, models, api
class FollowupLine(models.Model):
_inherit = 'account_followup.followup.line'
account_hold = fields.Boolean(string="Place on Credit Hold",
help="Place clients on account hold, restricting confirmation of new orders.")

View file

@ -0,0 +1,12 @@
from odoo import models, fields, api, _
class FollowUpReport(models.AbstractModel):
_inherit = 'account.followup.report'
def _get_line_info(self, followup_line):
res = super()._get_line_info(followup_line)
res.update({
'credit_hold': followup_line.account_hold
})
return res

View file

@ -0,0 +1,80 @@
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")
def _compute_on_hold(self):
# manually re-compute hold_bg since followup_status doesn't get updated in Python but gets recalculated
# by an SQL query every time
self._compute_hold_bg()
for rec in self:
# If the parent company is on hold, so are all its sub-contacts and subsidiaries
if rec.commercial_partner_id and rec.commercial_partner_id.on_hold:
rec.on_hold = True
return
# 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:
if rec.on_hold:
rec.message_post(_("Credit hold lifted."))
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."))
def _execute_followup_partner(self, options=None):
res = super()._execute_followup_partner(options)
if self.followup_status == "in_need_of_action":
if self.followup_line_id.account_hold:
self.action_credit_hold()
return res
@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

View file

@ -0,0 +1,17 @@
from odoo import fields, models, api, _
from odoo.exceptions import UserError
class SaleOrder(models.Model):
_inherit = "sale.order"
client_on_hold = fields.Boolean(string='Client on Hold',
help="Whether or not a client has been put on hold due to unpaid invoices.",
related="partner_id.on_hold")
@api.depends('client_on_hold')
def action_confirm(self):
if any(self.mapped('client_on_hold')):
raise UserError(_("This client is on credit hold. No new orders can be confirmed until past-due invoices "
"are paid or the accounting team postpones the hold."))
super().action_confirm()

View file

@ -0,0 +1,9 @@
from odoo import fields, models, api
class ModelName(models.Model):
_inherit = "stock.picking"
client_on_hold = fields.Boolean(string='Client on Hold',
help="Whether or not a client has been put on hold due to unpaid invoices.",
related="partner_id.on_hold")

View file

@ -0,0 +1,40 @@
# Overview
This module adds the notion of placing clients on credit hold to the followup levels from the Odoo Enterprise
account_followup module. It adds an option to followup levels to mark clients matching the followup criteria as on
credit hold. This hold restricts the confirmation of new sales orders for these clients.
Accountant and admin users can set a date until which the account hold will be
postponed on a specific partner's form view. This effectively gives clients an extra
grace period, allowing orders to be confirmed until the period ends.
# Change Log
## 17.0.1.0.0 (2024-05-15)
Various modifications to adapt code to Odoo 17.0
## 15.0.2.0.0 (2023-05-04)
Complete remake of the module, making the "Credit Hold" an action that is either manually or
automatically triggered from the Accounting > Followup Reports section or by setting the automatic application field
on followup levels.
## 15.0.1.1.0 (2023-05-03)
Adds a ribbon to stock pickings for clients on hold, and therefore a dependency on stock.
## 15.0.1.0.2 (2023-05-03)
Fix to sale order view and sale order confirmation for clients not on hold.
## 15.0.1.0.1 (2023-05-03)
Fix clients on hold when status is "outstanding_invoices".
## 15.0.1.0.0 (2023-05-02) Initial Release
Initial release of the module, including a setting on follow-up levels to toggle placing on credit hold. Blocks
the confirmation of sales orders for clients on credit hold. Red "Credit Hold" banner appears on sales orders and
partner form view when a client is on credit hold. Credit hold can be postponed by setting the "Postpone Hold" field
on the partner form view.

View file

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="account_followup_followup_line_form_inherit" model="ir.ui.view">
<field name="name">account_credit_hold.account_followup_line.form</field>
<field name="model">account_followup.followup.line</field>
<field
name="inherit_id"
ref="account_followup.view_account_followup_followup_line_form"
/>
<field name="arch" type="xml">
<xpath expr="//field[@name='send_email']" position="before">
<field name="account_hold" />
</xpath></field>
</record>
<record id="customer_statements_form_view_inherit" model="ir.ui.view">
<field name="name">customer.statements.form.view.inherit</field>
<field name="model">res.partner</field>
<field
name="inherit_id"
ref="account_followup.customer_statements_form_view"
/>
<field name="arch" type="xml">
<xpath expr="//button[last()]" position="after">
<field invisible="1" name="hold_bg" />
<button
class="button btn-secondary"
invisible="hold_bg == True"
name="action_credit_hold"
string="Credit Hold"
type="object"
/>
<button
class="button btn-secondary"
invisible="hold_bg == False"
name="action_lift_credit_hold"
string="Lift Credit Hold"
type="object"
/>
</xpath></field>
</record>
<record id="action_credit_hold" model="ir.actions.server">
<field name="name">action_credit_hold</field>
<field name="model_id" ref="base.model_res_partner" />
<field name="binding_model_id" ref="base.model_res_partner" />
<field name="binding_view_types">list,form</field>
<field name="state">code</field>
<field name="code">records.action_credit_hold()</field>
</record>
</odoo>

View file

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="res_partner_form_inherit" model="ir.ui.view">
<field name="name">account_credit_hold.res_partner.form</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_form" />
<field name="arch" type="xml">
<xpath expr="//div[@name='button_box']" position="before">
<field invisible="True" name="hold_bg" />
<field invisible="True" name="on_hold" />
<widget
bg_color="bg-danger"
invisible="on_hold == False"
name="web_ribbon"
title="Credit Hold"
/>
</xpath></field>
</record>
<record id="view_partner_property_form_inherit" model="ir.ui.view">
<field name="name">account_credit_hold.view_partner_property_form</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="account.view_partner_property_form" />
<field name="arch" type="xml">
<xpath expr="//group[@name='banks']" position="before">
<group string="Credit Hold">
<field
groups="account.group_account_manager,account.group_account_user"
name="postpone_hold_until"
readonly="hold_bg == False and postpone_hold_until == False"
/>
</group>
</xpath></field>
</record>
</odoo>

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="sale_order_form_inherit" model="ir.ui.view">
<field name="name">account_credit_hold.sale_order.form</field>
<field name="model">sale.order</field>
<field name="inherit_id" ref="sale.view_order_form" />
<field name="arch" type="xml">
<xpath expr="//div[@name='button_box']" position="before">
<field invisible="True" name="client_on_hold" />
<widget
bg_color="bg-danger"
invisible="client_on_hold == False"
name="web_ribbon"
title="Credit Hold"
/>
</xpath></field>
</record>
</odoo>

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="stock_picking_form_inherit" model="ir.ui.view">
<field name="name">account_credit_hold.stock_picking.form</field>
<field name="model">stock.picking</field>
<field name="inherit_id" ref="stock.view_picking_form" />
<field eval="8" name="priority" />
<field name="arch" type="xml">
<xpath expr="//div[@name='button_box']" position="before">
<field invisible="True" name="client_on_hold" />
<widget
bg_color="bg-danger"
invisible="client_on_hold == False"
name="web_ribbon"
title="Credit Hold"
/>
</xpath></field>
</record>
</odoo>