Migrate account_credit_hold to 18.0
Squashed commit of the following: commit5adce1fd15Author: Marc Durepos <marc@bemade.org> Date: Thu Sep 4 12:33:00 2025 -0400 [MIG] 17.0..18.0 account_credit_hold Migrated module account_credit_hold from 17.0 to 18.0. Fixed views and models and wrote test cases to check functionality. commit91c52af6f4Author: Marc Durepos <marc@bemade.org> Date: Thu Sep 4 11:38:37 2025 -0400 Initial migration of account_credit_hold for testing in 18.0
This commit is contained in:
parent
6ea4b324b3
commit
06584b381a
15 changed files with 674 additions and 0 deletions
1
account_credit_hold/__init__.py
Normal file
1
account_credit_hold/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
from . import models
|
||||
20
account_credit_hold/__manifest__.py
Normal file
20
account_credit_hold/__manifest__.py
Normal 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,
|
||||
}
|
||||
5
account_credit_hold/models/__init__.py
Normal file
5
account_credit_hold/models/__init__.py
Normal 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
|
||||
8
account_credit_hold/models/account_followup.py
Normal file
8
account_credit_hold/models/account_followup.py
Normal 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.")
|
||||
16
account_credit_hold/models/account_followup_report.py
Normal file
16
account_credit_hold/models/account_followup_report.py
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
from odoo import models, fields, api, _
|
||||
|
||||
|
||||
class FollowUpReport(models.AbstractModel):
|
||||
_inherit = 'account.followup.report'
|
||||
|
||||
def _get_followup_report_options(self, partner, options=None):
|
||||
"""
|
||||
Override to include credit hold information in followup report options.
|
||||
"""
|
||||
res = super()._get_followup_report_options(partner, options)
|
||||
res.update({
|
||||
'credit_hold': partner.followup_line_id.account_hold if partner.followup_line_id else False,
|
||||
'partner_on_hold': partner.on_hold
|
||||
})
|
||||
return res
|
||||
108
account_credit_hold/models/res_partner.py
Normal file
108
account_credit_hold/models/res_partner.py
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
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
|
||||
16
account_credit_hold/models/sale_order.py
Normal file
16
account_credit_hold/models/sale_order.py
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
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")
|
||||
|
||||
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."))
|
||||
return super().action_confirm()
|
||||
9
account_credit_hold/models/stock_picking.py
Normal file
9
account_credit_hold/models/stock_picking.py
Normal 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")
|
||||
40
account_credit_hold/readme.md
Normal file
40
account_credit_hold/readme.md
Normal 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.
|
||||
1
account_credit_hold/tests/__init__.py
Normal file
1
account_credit_hold/tests/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
from . import test_account_credit_hold
|
||||
342
account_credit_hold/tests/test_account_credit_hold.py
Normal file
342
account_credit_hold/tests/test_account_credit_hold.py
Normal file
|
|
@ -0,0 +1,342 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from datetime import date, timedelta
|
||||
from odoo.tests import common, tagged, Form
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
@tagged("post_install", "-at_install")
|
||||
class TestAccountCreditHold(common.TransactionCase):
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
||||
# Create test partner
|
||||
self.partner = self.env["res.partner"].create(
|
||||
{
|
||||
"name": "Test Customer",
|
||||
"is_company": True,
|
||||
"customer_rank": 1,
|
||||
"email": "test@example.com",
|
||||
}
|
||||
)
|
||||
|
||||
# Try to find existing followup lines or create new ones with unique delays
|
||||
self.followup_line = self.env["account_followup.followup.line"].search(
|
||||
[("delay", "=", 15), ("company_id", "=", self.env.company.id)], limit=1
|
||||
)
|
||||
|
||||
if not self.followup_line:
|
||||
# Find a unique delay value
|
||||
existing_delays = (
|
||||
self.env["account_followup.followup.line"]
|
||||
.search([("company_id", "=", self.env.company.id)])
|
||||
.mapped("delay")
|
||||
)
|
||||
|
||||
delay = 15
|
||||
while delay in existing_delays:
|
||||
delay += 1
|
||||
|
||||
self.followup_line = self.env["account_followup.followup.line"].create(
|
||||
{
|
||||
"name": "First Reminder",
|
||||
"delay": delay,
|
||||
"account_hold": True,
|
||||
"send_email": True,
|
||||
"company_id": self.env.company.id,
|
||||
}
|
||||
)
|
||||
else:
|
||||
# Update existing line to have account_hold
|
||||
self.followup_line.account_hold = True
|
||||
|
||||
# Create followup line without credit hold
|
||||
self.followup_line_no_hold = self.env["account_followup.followup.line"].search(
|
||||
[("delay", "=", 30), ("company_id", "=", self.env.company.id)], limit=1
|
||||
)
|
||||
|
||||
if not self.followup_line_no_hold:
|
||||
# Find a unique delay value
|
||||
existing_delays = (
|
||||
self.env["account_followup.followup.line"]
|
||||
.search([("company_id", "=", self.env.company.id)])
|
||||
.mapped("delay")
|
||||
)
|
||||
|
||||
delay = 30
|
||||
while delay in existing_delays:
|
||||
delay += 1
|
||||
|
||||
self.followup_line_no_hold = self.env[
|
||||
"account_followup.followup.line"
|
||||
].create(
|
||||
{
|
||||
"name": "Second Reminder",
|
||||
"delay": delay,
|
||||
"account_hold": False,
|
||||
"send_email": True,
|
||||
"company_id": self.env.company.id,
|
||||
}
|
||||
)
|
||||
else:
|
||||
# Update existing line to not have account_hold
|
||||
self.followup_line_no_hold.account_hold = False
|
||||
|
||||
def test_credit_hold_basic_functionality(self):
|
||||
"""Test basic credit hold functionality"""
|
||||
# Initially partner should not be on hold
|
||||
self.assertFalse(self.partner.on_hold)
|
||||
self.assertFalse(self.partner.hold_bg)
|
||||
|
||||
# Place partner on credit hold
|
||||
with Form(self.partner) as form:
|
||||
form.record.action_credit_hold()
|
||||
self.assertTrue(self.partner.hold_bg)
|
||||
self.assertTrue(self.partner.on_hold)
|
||||
|
||||
# Lift credit hold
|
||||
with Form(self.partner) as form:
|
||||
form.record.action_lift_credit_hold()
|
||||
self.assertFalse(self.partner.hold_bg)
|
||||
self.assertFalse(self.partner.on_hold)
|
||||
|
||||
def test_postpone_hold_functionality(self):
|
||||
"""Test postpone hold until functionality"""
|
||||
# Place partner on hold
|
||||
with Form(self.partner) as form:
|
||||
form.record.action_credit_hold()
|
||||
self.assertTrue(self.partner.on_hold)
|
||||
|
||||
# Set postpone date to tomorrow
|
||||
tomorrow = date.today() + timedelta(days=1)
|
||||
self.partner.postpone_hold_until = tomorrow
|
||||
|
||||
# Partner should not be on hold due to postponement
|
||||
self.assertFalse(self.partner.on_hold)
|
||||
|
||||
# Set postpone date to yesterday
|
||||
yesterday = date.today() - timedelta(days=1)
|
||||
self.partner.postpone_hold_until = yesterday
|
||||
|
||||
# Partner should be on hold again
|
||||
self.assertTrue(self.partner.on_hold)
|
||||
|
||||
def test_commercial_partner_hold_inheritance(self):
|
||||
"""Test that child contacts inherit hold status from commercial partner"""
|
||||
# Create child contact
|
||||
child_partner = self.env["res.partner"].create(
|
||||
{
|
||||
"name": "Child Contact",
|
||||
"parent_id": self.partner.id,
|
||||
"type": "contact",
|
||||
}
|
||||
)
|
||||
|
||||
# Place parent on hold
|
||||
with Form(self.partner) as form:
|
||||
form.record.action_credit_hold()
|
||||
|
||||
# Child should also be on hold
|
||||
self.assertTrue(child_partner.on_hold)
|
||||
|
||||
# Lift hold from parent
|
||||
self.partner.action_lift_credit_hold()
|
||||
|
||||
# Child should no longer be on hold
|
||||
self.assertFalse(child_partner.on_hold)
|
||||
|
||||
def test_sale_order_blocking(self):
|
||||
"""Test that sale orders are blocked when customer is on credit hold"""
|
||||
# Get or create a product for testing
|
||||
product = self.env["product.product"].search([("type", "=", "consu")], limit=1)
|
||||
if not product:
|
||||
product = self.env["product.product"].create(
|
||||
{
|
||||
"name": "Test Product",
|
||||
"type": "consu",
|
||||
"list_price": 100.0,
|
||||
}
|
||||
)
|
||||
|
||||
# Create a sale order
|
||||
sale_order = self.env["sale.order"].create(
|
||||
{
|
||||
"partner_id": self.partner.id,
|
||||
"order_line": [
|
||||
(
|
||||
0,
|
||||
0,
|
||||
{
|
||||
"product_id": product.id,
|
||||
"product_uom_qty": 1,
|
||||
"price_unit": 100.0,
|
||||
},
|
||||
)
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
# Should be able to confirm when not on hold
|
||||
sale_order.action_confirm()
|
||||
self.assertEqual(sale_order.state, "sale")
|
||||
|
||||
# Create another order and place customer on hold
|
||||
sale_order2 = self.env["sale.order"].create(
|
||||
{
|
||||
"partner_id": self.partner.id,
|
||||
"order_line": [
|
||||
(
|
||||
0,
|
||||
0,
|
||||
{
|
||||
"product_id": product.id,
|
||||
"product_uom_qty": 1,
|
||||
"price_unit": 100.0,
|
||||
},
|
||||
)
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
with Form(self.partner) as form:
|
||||
form.record.action_credit_hold()
|
||||
|
||||
# Should raise error when trying to confirm
|
||||
with self.assertRaises(UserError):
|
||||
sale_order2.action_confirm()
|
||||
|
||||
def test_followup_integration(self):
|
||||
"""Test integration with followup system"""
|
||||
# Set partner to in_need_of_action status and assign followup line
|
||||
self.partner.write(
|
||||
{
|
||||
"followup_status": "in_need_of_action",
|
||||
"followup_line_id": self.followup_line.id,
|
||||
}
|
||||
)
|
||||
|
||||
# Execute followup - should place on hold
|
||||
self.partner._execute_followup_partner()
|
||||
self.assertTrue(self.partner.hold_bg)
|
||||
|
||||
# Test with followup line that doesn't have account_hold
|
||||
self.partner.write(
|
||||
{
|
||||
"followup_line_id": self.followup_line_no_hold.id,
|
||||
"hold_bg": False, # Reset hold status
|
||||
}
|
||||
)
|
||||
|
||||
# Execute followup - should not place on hold
|
||||
self.partner._execute_followup_partner()
|
||||
self.assertFalse(self.partner.hold_bg)
|
||||
|
||||
def test_followup_report_options(self):
|
||||
"""Test that followup report includes credit hold information"""
|
||||
# Set up partner with followup line
|
||||
self.partner.write(
|
||||
{
|
||||
"followup_line_id": self.followup_line.id,
|
||||
}
|
||||
)
|
||||
self.partner.action_credit_hold()
|
||||
|
||||
# Get followup report options
|
||||
report = self.env["account.followup.report"]
|
||||
options = report._get_followup_report_options(self.partner)
|
||||
|
||||
# Should include credit hold information
|
||||
self.assertTrue(options.get("credit_hold"))
|
||||
self.assertTrue(options.get("partner_on_hold"))
|
||||
|
||||
def test_cleanup_expired_hold_postponements(self):
|
||||
"""Test automatic cleanup of expired hold postponements"""
|
||||
# Set expired postponement date
|
||||
expired_date = date.today() - timedelta(days=5)
|
||||
self.partner.postpone_hold_until = expired_date
|
||||
|
||||
# Run cleanup
|
||||
self.env["res.partner"]._cleanup_expired_hold_postponements()
|
||||
|
||||
# Postponement should be cleared
|
||||
self.assertFalse(self.partner.postpone_hold_until)
|
||||
|
||||
def test_hold_bg_computation(self):
|
||||
"""Test hold_bg field computation based on followup status"""
|
||||
# Test with no_action_needed status
|
||||
self.partner.write(
|
||||
{
|
||||
"followup_status": "no_action_needed",
|
||||
"followup_line_id": False,
|
||||
}
|
||||
)
|
||||
self.partner._compute_hold_bg()
|
||||
self.assertFalse(self.partner.hold_bg)
|
||||
|
||||
# Test with followup status and line
|
||||
self.partner.write(
|
||||
{
|
||||
"followup_status": "in_need_of_action",
|
||||
"followup_line_id": self.followup_line.id,
|
||||
"hold_bg": True, # Set initial state
|
||||
}
|
||||
)
|
||||
self.partner._compute_hold_bg()
|
||||
# Should preserve existing hold_bg value when there's a followup line
|
||||
self.assertTrue(self.partner.hold_bg)
|
||||
|
||||
def test_stock_picking_credit_hold_display(self):
|
||||
"""Test that stock pickings show credit hold status"""
|
||||
# Get warehouse and its outgoing picking type
|
||||
warehouse = self.env["stock.warehouse"].search([], limit=1)
|
||||
picking_type = warehouse.out_type_id
|
||||
|
||||
# Create a stock picking
|
||||
picking = self.env["stock.picking"].create(
|
||||
{
|
||||
"partner_id": self.partner.id,
|
||||
"picking_type_id": picking_type.id,
|
||||
"location_id": picking_type.default_location_src_id.id,
|
||||
"location_dest_id": picking_type.default_location_dest_id.id,
|
||||
}
|
||||
)
|
||||
|
||||
# Initially should not show as on hold
|
||||
self.assertFalse(picking.client_on_hold)
|
||||
|
||||
# Place partner on hold
|
||||
with Form(self.partner) as form:
|
||||
form.record.action_credit_hold()
|
||||
|
||||
# Picking should now show as on hold
|
||||
self.assertTrue(picking.client_on_hold)
|
||||
|
||||
def test_get_first_followup_level(self):
|
||||
"""Test _get_first_followup_level method"""
|
||||
first_level = self.partner._get_first_followup_level()
|
||||
self.assertEqual(first_level, self.followup_line)
|
||||
|
||||
# Create an earlier followup level with unique delay
|
||||
existing_delays = (
|
||||
self.env["account_followup.followup.line"]
|
||||
.search([("company_id", "=", self.env.company.id)])
|
||||
.mapped("delay")
|
||||
)
|
||||
|
||||
delay = 5
|
||||
while delay in existing_delays:
|
||||
delay += 1
|
||||
|
||||
earlier_line = self.env["account_followup.followup.line"].create(
|
||||
{
|
||||
"name": "Early Reminder",
|
||||
"delay": delay,
|
||||
"account_hold": False,
|
||||
"company_id": self.env.company.id,
|
||||
}
|
||||
)
|
||||
|
||||
first_level = self.partner._get_first_followup_level()
|
||||
self.assertEqual(first_level, earlier_line)
|
||||
37
account_credit_hold/views/account_followup_views.xml
Normal file
37
account_credit_hold/views/account_followup_views.xml
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
<?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="manual_reminder_view_form_inherit" model="ir.ui.view">
|
||||
<field name="name">account_credit_hold.manual_reminder.form.inherit</field>
|
||||
<field name="model">account_followup.manual_reminder</field>
|
||||
<field
|
||||
name="inherit_id"
|
||||
ref="account_followup.manual_reminder_view_form"
|
||||
/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//footer" position="before">
|
||||
<div class="alert alert-warning" role="alert" invisible="not partner_id.on_hold">
|
||||
<strong>Credit Hold:</strong> This customer is currently on credit hold.
|
||||
</div>
|
||||
</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>
|
||||
34
account_credit_hold/views/res_partner_views.xml
Normal file
34
account_credit_hold/views/res_partner_views.xml
Normal 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>
|
||||
18
account_credit_hold/views/sale_order_views.xml
Normal file
18
account_credit_hold/views/sale_order_views.xml
Normal 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>
|
||||
19
account_credit_hold/views/stock_picking_views.xml
Normal file
19
account_credit_hold/views/stock_picking_views.xml
Normal 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>
|
||||
Loading…
Reference in a new issue