Skip to content

Commit

Permalink
[ADD] account_move_post_block
Browse files Browse the repository at this point in the history
  • Loading branch information
MiquelRForgeFlow authored and JordiMForgeFlow committed Apr 29, 2021
1 parent 0941849 commit dda1259
Show file tree
Hide file tree
Showing 20 changed files with 432 additions and 0 deletions.
4 changes: 4 additions & 0 deletions account_move_post_block/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).

from . import models
from . import wizard
20 changes: 20 additions & 0 deletions account_move_post_block/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Copyright 2021 ForgeFlow (http://www.forgeflow.com)
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).

{
"name": "Account Move Post Block",
"author": "ForgeFlow, Odoo Community Association (OCA)",
"version": "13.0.1.0.0",
"category": "Accounting & Finance",
"website": "https://github.com/OCA/account-invoicing",
"depends": ["account_move_exception"],
"data": [
"data/account_exception_data.xml",
"security/ir.model.access.csv",
"security/account_move_post_block_security.xml",
"views/account_post_block_reason_view.xml",
"views/account_move_view.xml",
],
"license": "LGPL-3",
"installable": True,
}
15 changes: 15 additions & 0 deletions account_move_post_block/data/account_exception_data.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="am_excep_post_block" model="exception.rule">
<field name="name">Post Blocked</field>
<field name="description">The post has been blocked,
with a Blocking reason.</field>
<field name="sequence">100</field>
<!-- <field name="rule_group">account</field> -->
<field name="model">account.move</field>
<field
name="code"
>if 'post_block_id' in self._fields and self.post_block_id: failed = True</field>
<field name="active" eval="True" />
</record>
</odoo>
2 changes: 2 additions & 0 deletions account_move_post_block/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import account_post_block_reason
from . import account_move
45 changes: 45 additions & 0 deletions account_move_post_block/models/account_move.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Copyright 2021 ForgeFlow (http://www.forgeflow.com)
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).

from odoo import _, api, fields, models


class AccountMove(models.Model):
_inherit = "account.move"

post_block_id = fields.Many2one(
comodel_name="account.post.block.reason", string="Post Block Reason",
)
post_blocked = fields.Boolean("Post Blocked", compute="_compute_post_blocked",)

@api.depends("post_block_id")
def _compute_post_blocked(self):
for rec in self:
rec.post_blocked = rec.post_block_id

@api.model
def create(self, vals):
am = super().create(vals)
if "post_block_id" in vals and vals["post_block_id"]:
am.message_post(
body=_('Entry "%s" blocked with reason' ' "%s"')
% (am.name, am.post_block_id.name)
)
return am

def write(self, vals):
res = super().write(vals)
for am in self:
if "post_block_id" in vals and vals["post_block_id"]:
am.message_post(
body=_('Entry "%s" blocked with reason "%s"')
% (am.name, am.post_block_id.name)
)
elif "post_block_id" in vals and not vals["post_block_id"]:
am.message_post(body=_('Entry "%s" post block released.') % am.name)
return res

def button_release_post_block(self):
for move in self:
move.post_block_id = False
return True
13 changes: 13 additions & 0 deletions account_move_post_block/models/account_post_block_reason.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Copyright 2021 ForgeFlow (http://www.forgeflow.com)
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).

from odoo import fields, models


class AccountPostBlockReason(models.Model):
_name = "account.post.block.reason"
_description = "Account Post Block Reason"

name = fields.Char(required=True)
description = fields.Text()
active = fields.Boolean(default=True)
6 changes: 6 additions & 0 deletions account_move_post_block/readme/CONFIGURE.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
* Go to ‘Invoicing / Configuration / Invoicing / Purchase Approval Block Reasons’ and
create the blocking reasons as needed, providing a name and a description. A field
‘Active’ allows you to deactivate the reason if you do not plan to use it any more.
* Assign the security group 'Release Journal Entry with post block' to users
that should be able to release the block. Users in group 'Invoicing / Billing Administrator' are
by default assigned to this group.
1 change: 1 addition & 0 deletions account_move_post_block/readme/CONTRIBUTORS.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* Miquel Raïch <[email protected]>
2 changes: 2 additions & 0 deletions account_move_post_block/readme/DESCRIPTION.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
This module allows you to block the post of a journal entry when a Post
Block Reason has been provided. The moves will be waiting for post by a Manager.
31 changes: 31 additions & 0 deletions account_move_post_block/readme/USAGE.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
Set the Journal Entry Post Block
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

#. Go to ‘Invoicing’ and go to create an Invoice or a Bill.
#. Indicate the post block reason (found in the 'Other Info' tab).

Search existing Journal Entries
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

There is a filter ‘Blocked’ to search for moves that are blocked for post.
It is also possible to search journal entries with a specific block reason.

Confirm the Journal Entry
~~~~~~~~~~~~~~~~~~~~~~~~~

#. Press the button ‘Post’. If there's a post block, you will need to request
a Billing Manager release the post block in order to approve it.

Release the entry post block
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

While the Journal Entry is in draft, members of security group
‘Release Journal Entry with post block’ can see a button ‘Release Post Block’
and click on it. From this point and on, anyone seeing that Journal Entry will
be able to validate it.

Notifications to followers
~~~~~~~~~~~~~~~~~~~~~~~~~~

Followers of the Journal entry receive notifications when a post block has been
set or released.
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8" ?>
<!--
Copyright 2021 ForgeFlow S.L. (http://www.forgeflow.com)
License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl).
-->
<odoo>
<record id="group_journal_entry_post_block" model="res.groups">
<field name="name">Release Journal Entry with post block</field>
<field name="category_id" ref="base.module_category_hidden" />
<field name="users" eval="[(4, ref('base.user_root'))]" />
<field name="comment">
The user will be able to release a Journal Entry blocked for post.
</field>
</record>
<record id="account.group_account_manager" model="res.groups">
<field name="implied_ids" eval="[(4, ref('group_journal_entry_post_block'))]" />
</record>
</odoo>
4 changes: 4 additions & 0 deletions account_move_post_block/security/ir.model.access.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_account_move_post_block_reason_account,account.move.block.reason.account.user,model_account_post_block_reason,account.group_account_user,1,0,0,0
access_account_move_post_block_reason_invoice,account.move.block.reason.billing.user,model_account_post_block_reason,account.group_account_invoice,1,0,0,0
access_account_move_post_block_reason_manager,account.move.block.reason,model_account_post_block_reason,account.group_account_manager,1,1,1,1
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions account_move_post_block/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).

from . import test_account_move_post_block
from . import test_am_post_block_reason
88 changes: 88 additions & 0 deletions account_move_post_block/tests/test_account_move_post_block.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Copyright 2021 ForgeFlow (http://www.forgeflow.com)
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).

from odoo.tests.common import TransactionCase


class TestAccountMovePostBlock(TransactionCase):
def setUp(self):
super(TestAccountMovePostBlock, self).setUp()
self.users_obj = self.env["res.users"]
self.am_obj = self.env["account.move"].with_context(check_move_validity=False)
self.am_block_obj = self.env["account.post.block.reason"]
# company
self.company1 = self.env.ref("base.main_company")
# groups
self.group_account_user = self.env.ref("account.group_account_invoice")
self.group_account_manager = self.env.ref("account.group_account_manager")
# Partner
self.partner1 = self.env.ref("base.res_partner_1")
# Products
self.product1 = self.env.ref("product.product_product_7")
self.product2 = self.env.ref("product.product_product_9")
self.product3 = self.env.ref("product.product_product_11")
# Accounts
self.account_receivable = self.env["account.account"].search(
[
(
"user_type_id",
"=",
self.env.ref("account.data_account_type_receivable").id,
)
],
limit=1,
)
# Create users
self.user1_id = self._create_user(
"user_1", [self.group_account_user], self.company1
)
self.user2_id = self._create_user(
"user_2", [self.group_account_manager], self.company1
)
# Create a AM Block Reason
self._create_block_reason()

def _create_block_reason(self):
self.am_post_block_reason = self.am_block_obj.create(
{"name": "Needs Permission", "description": "Permission to validate"}
)

def _create_user(self, login, groups, company):
""" Create a user."""
group_ids = [group.id for group in groups]
user = self.users_obj.with_context({"no_reset_password": True}).create(
{
"name": "Account User",
"login": login,
"password": "test",
"email": "[email protected]",
"company_id": company.id,
"company_ids": [(4, company.id)],
"groups_id": [(6, 0, group_ids)],
}
)
return user.id

def _create_account_move(self, line_products):
""" Create a account move.
``line_products`` is a list of tuple [(product, qty)]
"""
lines = []
for product, qty in line_products:
line_values = {
"name": product.name,
"product_id": product.id,
"quantity": qty,
"price_unit": 100,
"account_id": self.account_receivable.id,
}
lines.append((0, 0, line_values))
account_move = self.am_obj.create(
{
"type": "out_invoice",
"partner_id": self.partner1.id,
"line_ids": lines,
"company_id": self.company1.id,
}
)
return account_move
52 changes: 52 additions & 0 deletions account_move_post_block/tests/test_am_post_block_reason.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Copyright 2021 ForgeFlow (http://www.forgeflow.com)
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).

from .test_account_move_post_block import TestAccountMovePostBlock


class TestAmPostBlockReason(TestAccountMovePostBlock):
def test_am_post_block_manual_release(self):
"""Confirming the Blocked AM"""
# Create an AM
account_move = self._create_account_move(
[(self.product1, 1), (self.product2, 5), (self.product3, 8)]
)

account_move.post_block_id = self.am_post_block_reason.id

self.assertEqual(account_move.post_blocked, True)
# The account manager unblocks the journal entry with block
account_move.with_user(self.user2_id).button_release_post_block()
self.assertEqual(
account_move.post_block_id, self.env["account.post.block.reason"]
)
# The account user validates the journal entry without block
account_move.with_user(self.user1_id).action_post()
# The AM is posted
self.assertEqual(account_move.state, "posted")

def test_am_post_block_draft_release_01(self):
# Create an AM
account_move = self._create_account_move(
[(self.product1, 1), (self.product2, 5), (self.product3, 8)]
)

account_move.post_block_id = self.am_post_block_reason.id
self.assertEquals(account_move.state, "draft")

# Simulation the opening of the wizard account_exception_confirm and
# set ignore_exception to True
am_except_confirm = (
self.env["account.exception.confirm"]
.with_context(
{
"active_id": account_move.id,
"active_ids": [account_move.id],
"active_model": account_move._name,
}
)
.create({"ignore": True})
)
am_except_confirm.action_confirm()

self.assertEqual(account_move.state, "posted")
53 changes: 53 additions & 0 deletions account_move_post_block/views/account_move_view.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8" ?>
<!--
Copyright 2021 ForgeFlow S.L. (http://www.forgeflow.com)
License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl).
-->
<odoo>
<record id="view_move_form" model="ir.ui.view">
<field name="name">account.move.form.block.reason</field>
<field name="model">account.move</field>
<field name="inherit_id" ref="account_move_exception.view_move_form" />
<field name="arch" type="xml">
<button name="button_draft" position="attributes">
<attribute name="states">cancel</attribute>
</button>
<xpath expr="//header/button[@name='action_post']" position="before">
<button
name="button_release_post_block"
string="Release Post Block"
type="object"
class="btn-primary"
groups="account_move_post_block.group_journal_entry_post_block"
attrs="{'invisible': ['|',('post_block_id', '=', False),
('state', 'not in', ['draft'])]}"
/>
</xpath>
<xpath expr="//page[@id='other_tab']" position="inside">
<field
name="post_block_id"
attrs="{'readonly': [('state', 'not in', 'draft')],
'invisible': [('post_block_id', '=', False)]}"
/>
<field name="post_blocked" invisible="True" />
</xpath>
</field>
</record>
<record id="view_account_move_filter" model="ir.ui.view">
<field name="name">account.move.select.block.reason</field>
<field name="model">account.move</field>
<field name="inherit_id" ref="account.view_account_move_filter" />
<field name="arch" type="xml">
<field name="journal_id" position="after">
<field name="post_block_id" />
</field>
<filter name="posted" position="after">
<filter
string="Blocked for Post"
name="blocked_am"
domain="[('post_block_id', '!=', False)]"
/>
</filter>
</field>
</record>
</odoo>
Loading

0 comments on commit dda1259

Please sign in to comment.