Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 31 additions & 9 deletions addons/account/models/account_move_line.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import re

from odoo import api, fields, models, Command, _
from odoo.exceptions import ValidationError, UserError
from odoo.exceptions import ValidationError, UserError, RedirectWarning
from odoo.osv import expression
from odoo.tools import frozendict, format_date, float_compare, Query
from odoo.tools.sql import create_index, SQL
Expand Down Expand Up @@ -3071,15 +3071,37 @@ def _reconcile_marked(self):
# -------------------------------------------------------------------------

def _validate_analytic_distribution(self):
lines_with_missing_analytic_distribution = self.env['account.move.line']
for line in self.filtered(lambda line: line.display_type == 'product'):
line._validate_distribution(**{
'product': line.product_id.id,
'account': line.account_id.id,
'business_domain': line.move_id.move_type in ['out_invoice', 'out_refund', 'out_receipt'] and 'invoice'
or line.move_id.move_type in ['in_invoice', 'in_refund', 'in_receipt'] and 'bill'
or 'general',
'company_id': line.company_id.id,
})
try:
line._validate_distribution(
company_id=line.company_id.id,
product=line.product_id.id,
account=line.account_id.id,
business_domain=(
'invoice' if line.move_id.is_sale_document(True)
else 'bill' if line.move_id.is_purchase_document(True)
else 'general'
),
)
except ValidationError:
lines_with_missing_analytic_distribution += line
if lines_with_missing_analytic_distribution:
msg = _("One or more lines require a 100% analytic distribution.")
if len(self.move_id) == 1:
raise ValidationError(msg)
raise RedirectWarning(
message=msg,
action={
'view_mode': 'list',
'name': _('Items With Missing Analytic Distribution'),
'res_model': 'account.move.line',
'type': 'ir.actions.act_window',
'domain': [('id', 'in', lines_with_missing_analytic_distribution.ids)],
'views': [(self.env.ref('account.view_move_line_tree').id, 'list')],
},
button_text=_("See items"),
)

def _create_analytic_lines(self):
""" Create analytic items upon validation of an account.move.line having an analytic distribution.
Expand Down
26 changes: 26 additions & 0 deletions addons/account/tests/test_account_analytic.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,32 @@ def test_mandatory_plan_validation(self):
invoice.action_post()
self.assertEqual(invoice.state, 'posted')

def test_mandatory_plan_validation_mass_posting(self):
"""
In case of mass posting, we should still check for mandatory analytic plans. This may raise a RedirectWarning,
if more than one entry was selected for posting, or a ValidationError if only one entry was selected.
"""
invoice1 = self.create_invoice(self.partner_a, self.product_a)
invoice2 = self.create_invoice(self.partner_b, self.product_a)
self.default_plan.write({
'applicability_ids': [Command.create({
'business_domain': 'invoice',
'product_categ_id': self.product_a.categ_id.id,
'applicability': 'mandatory',
})]
})

vam = self.env['validate.account.move'].create({'force_post': True})
for invoices in [invoice1, invoice1 | invoice2]:
with self.subTest(invoices=invoices):
with self.assertRaises(Exception):
vam.with_context({
'active_model': 'account.move',
'active_ids': [invoice1.id, invoice2.id],
'validate_analytic': True,
}).validate_move()
self.assertTrue('posted' not in invoices.mapped('state'))

def test_cross_analytics_computing(self):

out_invoice = self.env['account.move'].create([{
Expand Down
8 changes: 7 additions & 1 deletion addons/account/wizard/account_validate_move_view.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,13 @@
</group>
<span class="o_form_label">All selected journal entries will be validated and posted. You won't be able to modify them afterwards.</span>
<footer>
<button string="Post Journal Entries" name="validate_move" type="object" default_focus="1" class="btn-primary" data-hotkey="q"/>
<button string="Post Journal Entries"
name="validate_move"
type="object"
default_focus="1"
class="btn-primary"
data-hotkey="q"
context="{'validate_analytic': True}"/>
<button string="Cancel" class="btn-secondary" special="cancel" data-hotkey="x"/>
</footer>
</form>
Expand Down