move account_credit_hold into bemade-addons
This commit is contained in:
parent
90989e3669
commit
6ec7dfefa9
16 changed files with 337 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
|
||||||
30
account_credit_hold/__manifest__.py
Normal file
30
account_credit_hold/__manifest__.py
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
{
|
||||||
|
'name': 'Account Credit Hold',
|
||||||
|
'version': '15.0.2.0.0.1',
|
||||||
|
'summary': 'Allows setting clients on credit hold, blocking the ability confirm a new sales order.',
|
||||||
|
'description': 'Allows setting clients on 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',
|
||||||
|
],
|
||||||
|
'assets': {
|
||||||
|
'web.assets_backend': [
|
||||||
|
'account_credit_hold/static/src/js/followup_form_model.js',
|
||||||
|
'account_credit_hold/static/src/js/followup_form_controller.js',
|
||||||
|
],
|
||||||
|
'web.assets_qweb': [
|
||||||
|
'account_credit_hold/static/src/xml/account_followup_template.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.")
|
||||||
14
account_credit_hold/models/account_followup_report.py
Normal file
14
account_credit_hold/models/account_followup_report.py
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
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
|
||||||
|
@api.model
|
||||||
|
def credit_hold(self, options):
|
||||||
|
partner_id = options['partner_id']
|
||||||
|
partner = self.env['res.partner'].browse(partner_id)
|
||||||
|
partner.action_credit_hold()
|
||||||
59
account_credit_hold/models/res_partner.py
Normal file
59
account_credit_hold/models/res_partner.py
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
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.", )
|
||||||
|
|
||||||
|
hold_bg = fields.Boolean(string="Hold (technical)",
|
||||||
|
compute="_compute_hold_bg",
|
||||||
|
store=True,
|
||||||
|
default=False)
|
||||||
|
on_hold = fields.Boolean(string="Account on Hold",
|
||||||
|
help="Client account is on hold for unpaid overdue invoices.",
|
||||||
|
compute="_compute_on_hold")
|
||||||
|
|
||||||
|
@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 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):
|
||||||
|
message = _('Placed on credit hold')
|
||||||
|
for rec in self:
|
||||||
|
rec.hold_bg = True
|
||||||
|
rec.message_post()
|
||||||
|
|
||||||
|
def _execute_followup_partner(self):
|
||||||
|
res = super()._execute_followup_partner()
|
||||||
|
if self.followup_status == 'in_need_of_action':
|
||||||
|
if self.followup_line_id.account_hold:
|
||||||
|
self.action_credit_hold()
|
||||||
|
return res
|
||||||
|
|
||||||
|
# BV: FOR MIGRATION
|
||||||
|
#@api.depends('followup_status', 'followup_level')
|
||||||
|
def _compute_hold_bg(self):
|
||||||
|
first_followup_level = self.env['account_followup.followup.line'].search(
|
||||||
|
[('company_id', '=', self.env.company.id)], order="delay asc", limit=1)
|
||||||
|
for rec in self:
|
||||||
|
prev_hold_bg = rec.hold_bg
|
||||||
|
level = rec.followup_line_id
|
||||||
|
if rec.followup_status == 'no_action_needed' and level == first_followup_level:
|
||||||
|
rec.hold_bg = False
|
||||||
|
else:
|
||||||
|
rec.hold_bg = prev_hold_bg
|
||||||
17
account_credit_hold/models/sale_order.py
Normal file
17
account_credit_hold/models/sale_order.py
Normal 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()
|
||||||
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")
|
||||||
35
account_credit_hold/readme.md
Normal file
35
account_credit_hold/readme.md
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
# 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
|
||||||
|
## 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.
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
/** @odoo-module **/
|
||||||
|
|
||||||
|
var FollowupFormController = require('account_followup.FollowupFormController');
|
||||||
|
import { patch } from '@web/core/utils/patch';
|
||||||
|
|
||||||
|
var PatchedController = patch(FollowupFormController.prototype, "followup_form_controller", {
|
||||||
|
events: _.extend({}, FollowupFormController.prototype.events, {
|
||||||
|
'click .o_account_followup_credit_hold_button': '_onCreditHold',
|
||||||
|
}),
|
||||||
|
updateButtons() {
|
||||||
|
this._super(...arguments);
|
||||||
|
let setButtonClass = (button, primary) => {
|
||||||
|
/* Set class 'btn-primary' if parameter `primary` is true
|
||||||
|
* 'btn-secondary' otherwise
|
||||||
|
*/
|
||||||
|
let addedClass = primary ? 'btn-primary' : 'btn-secondary'
|
||||||
|
let removedClass = !primary ? 'btn-secondary' : 'btn-primary'
|
||||||
|
this.$buttons.find(`button.${button}`)
|
||||||
|
.removeClass(removedClass).addClass(addedClass);
|
||||||
|
}
|
||||||
|
if (!this.$buttons) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let followupLevel = this.model.localData[this.handle].data.followup_level;
|
||||||
|
setButtonClass('o_account_followup_credit_hold_button', followupLevel.credit_hold);
|
||||||
|
},
|
||||||
|
_onCreditHold: function() {
|
||||||
|
var self = this;
|
||||||
|
this.model.doCreditHold(this.handle);
|
||||||
|
this.options = {
|
||||||
|
partner_id: this._getPartner()
|
||||||
|
};
|
||||||
|
this._rpc({
|
||||||
|
model: 'account.followup.report',
|
||||||
|
method: 'credit_hold',
|
||||||
|
args: [this.options],
|
||||||
|
}).then(function (result) {
|
||||||
|
self._removeHighlightCreditHold();
|
||||||
|
self._displayDone();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
_removeHighlightCreditHold: function() {
|
||||||
|
this.$buttons.find('button.o_account_followup_credit_hold_button')
|
||||||
|
.removeClass('btn-primary').addClass('btn-secondary');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export { PatchedController };
|
||||||
14
account_credit_hold/static/src/js/followup_form_model.js
Normal file
14
account_credit_hold/static/src/js/followup_form_model.js
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
/** @odoo-module **/
|
||||||
|
|
||||||
|
var FollowupFormModel = require('account_followup.FollowupFormModel');
|
||||||
|
import { patch } from '@web/core/utils/patch';
|
||||||
|
|
||||||
|
var PatchedModel = patch(FollowupFormModel.prototype, 'followup_form_model', {
|
||||||
|
doCreditHold: function(handle) {
|
||||||
|
var level = this.localData[handle].data.followup_level;
|
||||||
|
if(level && level.credit_hold) {
|
||||||
|
level.credit_hold = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
export { PatchedModel };
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<templates>
|
||||||
|
<t t-inherit="account_followup.CustomerStatements.buttons" t-inherit-mode="extension">
|
||||||
|
<xpath expr="//button[hasclass('o_account_followup_print_letter_button')]" position="before">
|
||||||
|
<button type="button" class="btn btn-primary o_account_followup_credit_hold_button">
|
||||||
|
Credit Hold
|
||||||
|
</button>
|
||||||
|
</xpath>
|
||||||
|
</t>
|
||||||
|
</templates>
|
||||||
16
account_credit_hold/views/account_followup_views.xml
Normal file
16
account_credit_hold/views/account_followup_views.xml
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
33
account_credit_hold/views/res_partner_views.xml
Normal file
33
account_credit_hold/views/res_partner_views.xml
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
|
||||||
|
<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 name="hold_bg" invisible="True" />
|
||||||
|
<field name="on_hold" invisible="True" />
|
||||||
|
<widget name="web_ribbon" title="Credit Hold"
|
||||||
|
bg_color="bg-danger" attrs="{'invisible': [('on_hold','=',False)]}"/>
|
||||||
|
</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 name="postpone_hold_until"
|
||||||
|
groups="account.group_account_manager,account.group_account_user"
|
||||||
|
attrs="{'readonly': [('hold_bg','=',False), ('postpone_hold_until','=',False)]}"/>
|
||||||
|
</group>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</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>
|
||||||
|
<data>
|
||||||
|
|
||||||
|
<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 name="client_on_hold" invisible="True" />
|
||||||
|
<widget name="web_ribbon" title="Credit Hold"
|
||||||
|
bg_color="bg-danger" attrs="{'invisible': [('client_on_hold','=',False)] }"/>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
20
account_credit_hold/views/stock_picking_views.xml
Normal file
20
account_credit_hold/views/stock_picking_views.xml
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
|
||||||
|
<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 name="priority" eval="8"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//div[@name='button_box']" position="before">
|
||||||
|
<field name="client_on_hold" invisible="True" />
|
||||||
|
<widget name="web_ribbon" title="Credit Hold"
|
||||||
|
bg_color="bg-danger" attrs="{'invisible': [('client_on_hold','=',False)] }"/>
|
||||||
|
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
Loading…
Reference in a new issue