[IMP] account_invoice_change,account_invoice_change_analytic: change multiple invoices

H12964
This commit is contained in:
Cedric Collins
2023-07-27 16:29:21 -05:00
parent 49435f3fca
commit 8c973c66cf
8 changed files with 172 additions and 35 deletions

View File

@@ -23,6 +23,7 @@ the invoice is currently in.
'account', 'account',
], ],
'data': [ 'data': [
'data/server_actions.xml',
'wizard/invoice_change_views.xml', 'wizard/invoice_change_views.xml',
], ],
'installable': True, 'installable': True,

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<record model="ir.actions.server" id="action_invoice_change">
<field name="name">Change Invoice</field>
<field name="model_id" ref="account.model_account_move"/>
<field name="binding_model_id" ref="account.model_account_move" />
<field name="binding_view_types">list</field>
<field name="state">code</field>
<field name="groups_id" eval="[(4,ref('account.group_account_manager'))]"/>
<field name="code">
if records:
action = env['ir.actions.act_window'].for_xml_id('account_invoice_change', 'action_view_account_invoice_change')
action['context'] = {
'active_model': model._name,
'active_ids': records.ids,
}
</field>
</record>
</data>
</odoo>

View File

@@ -1,5 +1,6 @@
from odoo import fields from odoo import fields
from odoo.tests import tagged from odoo.exceptions import UserError
from odoo.tests import tagged, Form
from odoo.addons.account.tests.account_test_users import AccountTestUsers from odoo.addons.account.tests.account_test_users import AccountTestUsers
@@ -55,3 +56,65 @@ class TestInvoiceChange(InvoiceChangeCommon):
self.assertEqual(str(self.invoice_basic.invoice_date), change_invoice_date) self.assertEqual(str(self.invoice_basic.invoice_date), change_invoice_date)
self.assertEqual(self.invoice_basic.invoice_user_id, change_user) self.assertEqual(self.invoice_basic.invoice_user_id, change_user)
self.assertEqual(str(self.invoice_basic.line_ids[0].date), change_date) self.assertEqual(str(self.invoice_basic.line_ids[0].date), change_date)
def _create_journal_entry(self):
revenue_account = self.env['account.account'].search([
('company_id', '=', self.main_company.id),
('user_type_id', '=', self.env.ref('account.data_account_type_revenue').id)
], limit=1)
expense_account = self.env['account.account'].search([
('company_id', '=', self.main_company.id),
('user_type_id', '=', self.env.ref('account.data_account_type_expenses').id)
], limit=1)
return self.env['account.move'].create({
'type': 'entry',
'date': '2019-01-01',
'line_ids': [
(0, 0, {
'name': 'line_debit',
'account_id': revenue_account.id,
}),
(0, 0, {
'name': 'line_credit',
'account_id': expense_account.id,
}),
],
})
def test_invoice_change_multiple(self):
journal_entry = self._create_journal_entry()
ctx = {'active_model': 'account.move', 'active_ids': [self.invoice_basic.id, journal_entry.id]}
with self.assertRaises(UserError):
change = Form(self.env['account.invoice.change'].with_context(ctx))
other_invoice = self.invoice_basic.copy()
ctx = {'active_model': 'account.move', 'active_ids': [self.invoice_basic.id, other_invoice.id]}
with self.assertRaises(UserError):
change = Form(self.env['account.invoice.change'].with_context(ctx))
self.invoice_basic.action_post()
other_invoice.action_post()
change = Form(self.env['account.invoice.change'].with_context(ctx))
self.assertFalse(change.date)
self.assertFalse(change.invoice_date)
self.assertFalse(change.invoice_user_id)
change_date = '2018-01-01'
change_invoice_date = '2019-01-01'
change_user = self.env.user
change.set_invoice_user_id = True
change.set_date = True
change.set_invoice_date = True
change.invoice_user_id = change_user
change.date = change_date
change.invoice_date = change_invoice_date
change.save().affect_change()
self.assertEqual(str(self.invoice_basic.date), change_date)
self.assertEqual(str(self.invoice_basic.invoice_date), change_invoice_date)
self.assertEqual(self.invoice_basic.invoice_user_id, change_user)
self.assertEqual(str(self.invoice_basic.line_ids[0].date), change_date)
self.assertEqual(str(other_invoice.date), change_date)
self.assertEqual(str(other_invoice.invoice_date), change_invoice_date)
self.assertEqual(other_invoice.invoice_user_id, change_user)
self.assertEqual(str(other_invoice.line_ids[0].date), change_date)

View File

@@ -6,11 +6,15 @@ class InvoiceChangeWizard(models.TransientModel):
_name = 'account.invoice.change' _name = 'account.invoice.change'
_description = 'Invoice Change' _description = 'Invoice Change'
move_id = fields.Many2one('account.move', string='Invoice', readonly=True, required=True) move_ids = fields.Many2many('account.move', string='Invoice', readonly=True, required=True)
move_company_id = fields.Many2one('res.company', readonly=True, related='move_id.company_id') is_single_move = fields.Boolean(compute='_compute_move_company_id')
move_company_id = fields.Many2one('res.company', readonly=True, compute='_compute_move_company_id')
invoice_user_id = fields.Many2one('res.users', string='Salesperson') invoice_user_id = fields.Many2one('res.users', string='Salesperson')
set_invoice_user_id = fields.Boolean()
date = fields.Date(string='Accounting Date') date = fields.Date(string='Accounting Date')
set_date = fields.Boolean()
invoice_date = fields.Date(string='Invoice Date') invoice_date = fields.Date(string='Invoice Date')
set_invoice_date = fields.Boolean()
@api.model @api.model
def default_get(self, fields): def default_get(self, fields):
@@ -28,24 +32,40 @@ class InvoiceChangeWizard(models.TransientModel):
"Programmation error: the expected model for this action is 'account.move'. The provided one is '%d'.") % active_model) "Programmation error: the expected model for this action is 'account.move'. The provided one is '%d'.") % active_model)
# Checks on received invoice records # Checks on received invoice records
invoice = self.env[active_model].browse(active_ids) invoices = self.env[active_model].browse(active_ids)
if len(invoice) != 1: if invoices.filtered(lambda i: not i.is_invoice()):
raise UserError(_("Invoice Change expects only one invoice.")) raise UserError(_('Only invoices can be updated.'))
if invoices.filtered(lambda i: i.state in ('draft', 'cancel')):
raise UserError(_('Only posted invoices can be updated.'))
if len(invoices.company_id) > 1:
raise UserError(_('Selected invoices must be in the same company.'))
rec['move_ids'] = [(6, 0, invoices.ids)]
if len(invoices) == 1:
rec.update({ rec.update({
'move_id': invoice.id, 'set_invoice_user_id': True,
'invoice_user_id': invoice.invoice_user_id.id, 'set_date': True,
'date': invoice.date, 'set_invoice_date': True,
'invoice_date': invoice.invoice_date, 'invoice_user_id': invoices.invoice_user_id.id,
'date': invoices.date,
'invoice_date': invoices.invoice_date,
}) })
return rec return rec
@api.depends('move_ids')
def _compute_move_company_id(self):
for wiz in self:
company = wiz.move_ids.company_id
company.ensure_one()
wiz.move_company_id = company
wiz.is_single_move = len(wiz.move_ids) == 1
def _new_move_vals(self): def _new_move_vals(self):
vals = {} vals = {}
if self.move_id.invoice_user_id != self.invoice_user_id: if self.set_invoice_user_id:
vals['invoice_user_id'] = self.invoice_user_id.id vals['invoice_user_id'] = self.invoice_user_id.id
if self.move_id.date != self.date: if self.set_date:
vals['date'] = self.date vals['date'] = self.date
if self.move_id.invoice_date != self.invoice_date: if self.set_invoice_date:
vals['invoice_date'] = self.invoice_date vals['invoice_date'] = self.invoice_date
return vals return vals
@@ -53,5 +73,5 @@ class InvoiceChangeWizard(models.TransientModel):
self.ensure_one() self.ensure_one()
vals = self._new_move_vals() vals = self._new_move_vals()
if vals: if vals:
self.move_id.write(vals) self.move_ids.write(vals)
return True return True

View File

@@ -7,11 +7,15 @@
<form string="Invoice Change"> <form string="Invoice Change">
<group> <group>
<group name="group_left"> <group name="group_left">
<field name="move_id" invisible="1"/> <field name="move_ids" invisible="1"/>
<field name="is_single_move" invisible="1"/>
<field name="move_company_id" invisible="1"/> <field name="move_company_id" invisible="1"/>
<field name="invoice_user_id"/> <field name="set_invoice_user_id" attrs="{'invisible': [('is_single_move', '=', True)]}"/>
<field name="invoice_date"/> <field name="invoice_user_id" attrs="{'readonly': [('set_invoice_user_id', '=', False)]}"/>
<field name="date"/> <field name="set_invoice_date" attrs="{'invisible': [('is_single_move', '=', True)]}"/>
<field name="invoice_date" attrs="{'readonly': [('set_invoice_date', '=', False)]}"/>
<field name="set_date" attrs="{'invisible': [('is_single_move', '=', True)]}"/>
<field name="date" attrs="{'readonly': [('set_date', '=', False)]}"/>
</group> </group>
<group name="group_right"/> <group name="group_right"/>
</group> </group>
@@ -40,7 +44,6 @@
<button name="%(action_view_account_invoice_change)d" string="Change" <button name="%(action_view_account_invoice_change)d" string="Change"
type="action" class="btn-secondary" type="action" class="btn-secondary"
attrs="{'invisible': ['|', ('state', 'in', ('draft', 'cancel')), ('type', 'not in', ('out_invoice', 'out_refund'))]}" attrs="{'invisible': ['|', ('state', 'in', ('draft', 'cancel')), ('type', 'not in', ('out_invoice', 'out_refund'))]}"
context="{'default_move_id': id}"
groups="account.group_account_manager" /> groups="account.group_account_manager" />
</xpath> </xpath>
</field> </field>

View File

@@ -68,8 +68,6 @@ class TestWizard(InvoiceChangeCommon):
self.assertFalse(other_lines.analytic_account_id) self.assertFalse(other_lines.analytic_account_id)
self.assertFalse(invoice_lines.analytic_line_ids) self.assertFalse(invoice_lines.analytic_line_ids)
def test_invoice_change_analytic_tags(self): def test_invoice_change_analytic_tags(self):
invoice_lines = self.invoice_basic.invoice_line_ids invoice_lines = self.invoice_basic.invoice_line_ids
other_lines = self.invoice_basic.line_ids - invoice_lines other_lines = self.invoice_basic.line_ids - invoice_lines
@@ -114,3 +112,23 @@ class TestWizard(InvoiceChangeCommon):
self.assertEqual(invoice_lines.analytic_line_ids.mapped(lambda l: (l.account_id, l.amount)), self.assertEqual(invoice_lines.analytic_line_ids.mapped(lambda l: (l.account_id, l.amount)),
[(self.analytic_account, 600.0), [(self.analytic_account, 600.0),
(self.analytic_account3, 400.0)]) (self.analytic_account3, 400.0)])
def test_invoice_change_tags_multiple(self):
other_invoice = self.invoice_basic.copy()
self.invoice_basic.invoice_line_ids.analytic_account_id = self.analytic_account
self.invoice_basic.action_post()
other_invoice.invoice_line_ids.analytic_account_id = self.analytic_account2
other_invoice.action_post()
ctx = {'active_model': 'account.move', 'active_ids': [self.invoice_basic.id, other_invoice.id]}
change = Form(self.env['account.invoice.change'].with_context(ctx))
change.update_tags = 'set'
change.analytic_tag_ids.add(self.analytic_tag1)
change.save().affect_change()
# Unchanged unless user chooses to set the value
self.assertEqual(self.invoice_basic.invoice_line_ids.analytic_account_id, self.analytic_account)
self.assertEqual(other_invoice.invoice_line_ids.analytic_account_id, self.analytic_account2)
self.assertEqual(self.invoice_basic.invoice_line_ids.analytic_tag_ids, self.analytic_tag1)
self.assertEqual(other_invoice.invoice_line_ids.analytic_tag_ids, self.analytic_tag1)

View File

@@ -5,6 +5,7 @@ class InvoiceChangeWizard(models.TransientModel):
_inherit = 'account.invoice.change' _inherit = 'account.invoice.change'
analytic_account_id = fields.Many2one('account.analytic.account', string='Analytic Account') analytic_account_id = fields.Many2one('account.analytic.account', string='Analytic Account')
set_analytic_account_id = fields.Boolean()
update_tags = fields.Selection(selection=[ update_tags = fields.Selection(selection=[
('no', 'Do not update tags'), ('no', 'Do not update tags'),
('add', 'Add to tags'), ('add', 'Add to tags'),
@@ -21,10 +22,13 @@ class InvoiceChangeWizard(models.TransientModel):
@api.model @api.model
def default_get(self, fields): def default_get(self, fields):
rec = super(InvoiceChangeWizard, self).default_get(fields) rec = super(InvoiceChangeWizard, self).default_get(fields)
invoice = self.env['account.move'].browse(rec['move_id']) context = dict(self._context or {})
invoices = self.env['account.move'].browse(context.get('active_ids'))
if len(invoices) == 1:
rec.update({ rec.update({
'analytic_account_id': self._analytic_account_id(invoice), 'set_analytic_account_id': True,
'analytic_tag_ids': [(6, 0, invoice.invoice_line_ids.analytic_tag_ids.ids)], 'analytic_account_id': self._analytic_account_id(invoices),
'analytic_tag_ids': [(6, 0, invoices.invoice_line_ids.analytic_tag_ids.ids)],
}) })
return rec return rec
@@ -34,10 +38,9 @@ class InvoiceChangeWizard(models.TransientModel):
return res return res
def _prepare_analytic_values(self): def _prepare_analytic_values(self):
vals = { vals = {}
'analytic_account_id': self.analytic_account_id.id, if self.set_analytic_account_id:
'analytic_line_ids': [(5, 0, 0)], vals['analytic_account_id'] = self.analytic_account_id.id
}
tag_commands = [] tag_commands = []
if self.update_tags == 'add': if self.update_tags == 'add':
tag_commands = [(4, tag.id, 0) for tag in self.analytic_tag_ids] tag_commands = [(4, tag.id, 0) for tag in self.analytic_tag_ids]
@@ -45,9 +48,13 @@ class InvoiceChangeWizard(models.TransientModel):
tag_commands = [(6, 0, self.analytic_tag_ids.ids)] tag_commands = [(6, 0, self.analytic_tag_ids.ids)]
if tag_commands: if tag_commands:
vals['analytic_tag_ids'] = tag_commands vals['analytic_tag_ids'] = tag_commands
if vals:
vals['analytic_line_ids'] = [(5, 0, 0)]
return vals return vals
def _affect_analytic_change(self): def _affect_analytic_change(self):
lines_to_affect = self.move_id.invoice_line_ids lines_to_affect = self.move_ids.invoice_line_ids
lines_to_affect.write(self._prepare_analytic_values()) vals = self._prepare_analytic_values()
if vals:
lines_to_affect.write(vals)
lines_to_affect.create_analytic_lines() lines_to_affect.create_analytic_lines()

View File

@@ -6,8 +6,10 @@
<field name="inherit_id" ref="account_invoice_change.account_invoice_change_form"/> <field name="inherit_id" ref="account_invoice_change.account_invoice_change_form"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<xpath expr="//group[@name='group_right']" position="inside"> <xpath expr="//group[@name='group_right']" position="inside">
<field name="set_analytic_account_id" attr="('invisible': [('is_single_move', '=', True)]}" groups="analytic.group_analytic_accounting"/>
<field name="analytic_account_id" <field name="analytic_account_id"
domain="['|', ('company_id', '=', False), ('company_id', '=', move_company_id)]" domain="['|', ('company_id', '=', False), ('company_id', '=', move_company_id)]"
attrs="{'readonly': [('set_analytic_account_id', '=', False)]}"
groups="analytic.group_analytic_accounting"/> groups="analytic.group_analytic_accounting"/>
<field name="update_tags" groups="analytic.group_analytic_tags"/> <field name="update_tags" groups="analytic.group_analytic_tags"/>
<field name="analytic_tag_ids" <field name="analytic_tag_ids"