diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..29eb686 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,33 @@ +name: tests + +on: + pull_request: + branches: + - "17.0-test-ci" + push: + branches: + - "17.0-test-ci" + +jobs: + test: + runs-on: ubuntu-22.04 + container: ghcr.io/bemade/test-odoo_arm64:latest + name: Test Repo Addons With Odoo + strategy: + fail-fast: false + services: + postgres: + image: postgres:12.0 + env: + POSTGRES_USER: odoo + POSTGRES_PASSWORD: odoo + POSTGRES_DB: odoo + ports: + - 5432:5432 + steps: + - uses: actions/checkout@v4 + with: + persist-credientials: false + - name: Run Tests + run: run_tests.sh + diff --git a/account_credit_hold/__init__.py b/account_credit_hold/__init__.py new file mode 100644 index 0000000..0650744 --- /dev/null +++ b/account_credit_hold/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/account_credit_hold/__manifest__.py b/account_credit_hold/__manifest__.py new file mode 100644 index 0000000..b5cc33a --- /dev/null +++ b/account_credit_hold/__manifest__.py @@ -0,0 +1,21 @@ +{ + 'name': 'Account Credit Hold', + 'version': '17.0.1.0.0', + '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 ', + '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 +} diff --git a/account_credit_hold/models/__init__.py b/account_credit_hold/models/__init__.py new file mode 100644 index 0000000..0c2c11c --- /dev/null +++ b/account_credit_hold/models/__init__.py @@ -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 diff --git a/account_credit_hold/models/account_followup.py b/account_credit_hold/models/account_followup.py new file mode 100644 index 0000000..5af3363 --- /dev/null +++ b/account_credit_hold/models/account_followup.py @@ -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.") diff --git a/account_credit_hold/models/account_followup_report.py b/account_credit_hold/models/account_followup_report.py new file mode 100644 index 0000000..a28efdc --- /dev/null +++ b/account_credit_hold/models/account_followup_report.py @@ -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 diff --git a/account_credit_hold/models/res_partner.py b/account_credit_hold/models/res_partner.py new file mode 100644 index 0000000..3cc765d --- /dev/null +++ b/account_credit_hold/models/res_partner.py @@ -0,0 +1,63 @@ +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: + 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): + 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 + + @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 level == first_followup_level: + rec.hold_bg = False + else: + rec.hold_bg = prev_hold_bg diff --git a/account_credit_hold/models/sale_order.py b/account_credit_hold/models/sale_order.py new file mode 100644 index 0000000..390e97e --- /dev/null +++ b/account_credit_hold/models/sale_order.py @@ -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() diff --git a/account_credit_hold/models/stock_picking.py b/account_credit_hold/models/stock_picking.py new file mode 100644 index 0000000..0d46e32 --- /dev/null +++ b/account_credit_hold/models/stock_picking.py @@ -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") diff --git a/account_credit_hold/readme.md b/account_credit_hold/readme.md new file mode 100644 index 0000000..b11668c --- /dev/null +++ b/account_credit_hold/readme.md @@ -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. \ No newline at end of file diff --git a/account_credit_hold/views/account_followup_views.xml b/account_credit_hold/views/account_followup_views.xml new file mode 100644 index 0000000..10a7165 --- /dev/null +++ b/account_credit_hold/views/account_followup_views.xml @@ -0,0 +1,52 @@ + + + + + + account_credit_hold.account_followup_line.form + account_followup.followup.line + + + + + + + + + + customer.statements.form.view.inherit + res.partner + + + + +