[IMP] pre-commit: first run on whole repo
This commit is contained in:
@@ -3,19 +3,19 @@
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
{
|
||||
'name': 'Account Invoice Update Wizard',
|
||||
'version': '12.0.1.0.0',
|
||||
'category': 'Accounting & Finance',
|
||||
'license': 'AGPL-3',
|
||||
'summary': 'Wizard to update non-legal fields of an open/paid invoice',
|
||||
'author': 'Akretion',
|
||||
'website': 'https://github.com/akretion/odoo-usability',
|
||||
'depends': [
|
||||
'account',
|
||||
"name": "Account Invoice Update Wizard",
|
||||
"version": "12.0.1.0.0",
|
||||
"category": "Accounting & Finance",
|
||||
"license": "AGPL-3",
|
||||
"summary": "Wizard to update non-legal fields of an open/paid invoice",
|
||||
"author": "Akretion",
|
||||
"website": "https://github.com/OCA/odoo-usability",
|
||||
"depends": [
|
||||
"account",
|
||||
],
|
||||
'data': [
|
||||
'wizard/account_invoice_update_view.xml',
|
||||
'views/account_invoice.xml',
|
||||
],
|
||||
'installable': False,
|
||||
"data": [
|
||||
"wizard/account_invoice_update_view.xml",
|
||||
"views/account_invoice.xml",
|
||||
],
|
||||
"installable": False,
|
||||
}
|
||||
|
||||
@@ -1,22 +1,19 @@
|
||||
# Copyright 2019 Camptocamp
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import models, fields, api, _
|
||||
from odoo.exceptions import UserError
|
||||
import odoo.addons.decimal_precision as dp
|
||||
from odoo import models
|
||||
|
||||
|
||||
class AccountInvoice(models.Model):
|
||||
_inherit = 'account.invoice'
|
||||
|
||||
_inherit = "account.invoice"
|
||||
|
||||
def prepare_update_wizard(self):
|
||||
self.ensure_one()
|
||||
wizard = self.env['account.invoice.update']
|
||||
wizard = self.env["account.invoice.update"]
|
||||
res = wizard._prepare_default_get(self)
|
||||
action = self.env.ref(
|
||||
'account_invoice_update_wizard.account_invoice_update_action'
|
||||
"account_invoice_update_wizard.account_invoice_update_action"
|
||||
).read()[0]
|
||||
action['name'] = "Update Wizard"
|
||||
action['res_id'] = wizard.create(res).id
|
||||
action["name"] = "Update Wizard"
|
||||
action["res_id"] = wizard.create(res).id
|
||||
return action
|
||||
|
||||
|
||||
@@ -1,54 +1,61 @@
|
||||
# Copyright 2018-2019 Camptocamp
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo.tests.common import SavepointCase
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.tests.common import SavepointCase
|
||||
|
||||
|
||||
class TestAccountInvoiceUpdateWizard(SavepointCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.customer12 = cls.env.ref('base.res_partner_12')
|
||||
cls.product16 = cls.env.ref('product.product_product_16')
|
||||
cls.product24 = cls.env.ref('product.product_product_24')
|
||||
uom_unit = cls.env.ref('uom.product_uom_categ_unit')
|
||||
cls.customer12 = cls.env.ref("base.res_partner_12")
|
||||
cls.product16 = cls.env.ref("product.product_product_16")
|
||||
cls.product24 = cls.env.ref("product.product_product_24")
|
||||
uom_unit = cls.env.ref("uom.product_uom_categ_unit")
|
||||
|
||||
cls.invoice1 = cls.env['account.invoice'].create({
|
||||
'name': 'Test invoice',
|
||||
'partner_id': cls.customer12.id,
|
||||
})
|
||||
cls.inv_line1 = cls.env['account.invoice.line'].create({
|
||||
'invoice_id': cls.invoice1.id,
|
||||
'name': "Line1",
|
||||
'product_id': cls.product16.id,
|
||||
'product_uom_id': uom_unit.id,
|
||||
'account_id': cls.invoice1.account_id.id,
|
||||
'price_unit': 42.0,
|
||||
})
|
||||
cls.inv_line2 = cls.env['account.invoice.line'].create({
|
||||
'invoice_id': cls.invoice1.id,
|
||||
'name': "Line2",
|
||||
'product_id': cls.product24.id,
|
||||
'product_uom_id': uom_unit.id,
|
||||
'account_id': cls.invoice1.account_id.id,
|
||||
'price_unit': 1111.1,
|
||||
})
|
||||
cls.invoice1 = cls.env["account.invoice"].create(
|
||||
{
|
||||
"name": "Test invoice",
|
||||
"partner_id": cls.customer12.id,
|
||||
}
|
||||
)
|
||||
cls.inv_line1 = cls.env["account.invoice.line"].create(
|
||||
{
|
||||
"invoice_id": cls.invoice1.id,
|
||||
"name": "Line1",
|
||||
"product_id": cls.product16.id,
|
||||
"product_uom_id": uom_unit.id,
|
||||
"account_id": cls.invoice1.account_id.id,
|
||||
"price_unit": 42.0,
|
||||
}
|
||||
)
|
||||
cls.inv_line2 = cls.env["account.invoice.line"].create(
|
||||
{
|
||||
"invoice_id": cls.invoice1.id,
|
||||
"name": "Line2",
|
||||
"product_id": cls.product24.id,
|
||||
"product_uom_id": uom_unit.id,
|
||||
"account_id": cls.invoice1.account_id.id,
|
||||
"price_unit": 1111.1,
|
||||
}
|
||||
)
|
||||
|
||||
cls.aa1 = cls.env.ref('analytic.analytic_partners_camp_to_camp')
|
||||
cls.aa2 = cls.env.ref('analytic.analytic_nebula')
|
||||
cls.atag1 = cls.env.ref('analytic.tag_contract')
|
||||
cls.atag2 = cls.env['account.analytic.tag'].create({
|
||||
'name': 'の',
|
||||
})
|
||||
cls.aa1 = cls.env.ref("analytic.analytic_partners_camp_to_camp")
|
||||
cls.aa2 = cls.env.ref("analytic.analytic_nebula")
|
||||
cls.atag1 = cls.env.ref("analytic.tag_contract")
|
||||
cls.atag2 = cls.env["account.analytic.tag"].create(
|
||||
{
|
||||
"name": "の",
|
||||
}
|
||||
)
|
||||
|
||||
def create_wizard(self, invoice):
|
||||
res = self.invoice1.prepare_update_wizard()
|
||||
self.wiz = self.env['account.invoice.update'].browse(res['res_id'])
|
||||
self.wiz = self.env["account.invoice.update"].browse(res["res_id"])
|
||||
|
||||
def test_add_analytic_account_line1(self):
|
||||
""" Add analytic account on an invoice line
|
||||
"""Add analytic account on an invoice line
|
||||
after the invoice has been approved.
|
||||
|
||||
This will:
|
||||
@@ -59,17 +66,19 @@ class TestAccountInvoiceUpdateWizard(SavepointCase):
|
||||
self.create_wizard(self.invoice1)
|
||||
|
||||
wiz_line = self.wiz.line_ids.filtered(
|
||||
lambda rec: rec.invoice_line_id == self.inv_line1)
|
||||
lambda rec: rec.invoice_line_id == self.inv_line1
|
||||
)
|
||||
wiz_line.account_analytic_id = self.aa1
|
||||
self.wiz.run()
|
||||
|
||||
related_ml = self.invoice1.move_id.line_ids.filtered(
|
||||
lambda rec: rec.product_id == self.product16)
|
||||
lambda rec: rec.product_id == self.product16
|
||||
)
|
||||
self.assertEqual(related_ml.analytic_account_id, self.aa1)
|
||||
self.assertEqual(related_ml.analytic_line_ids.account_id, self.aa1)
|
||||
|
||||
def test_change_analytic_account_line1(self):
|
||||
""" Change analytic account on an invoice line
|
||||
"""Change analytic account on an invoice line
|
||||
after the invoice has been approved.
|
||||
|
||||
This will:
|
||||
@@ -81,17 +90,19 @@ class TestAccountInvoiceUpdateWizard(SavepointCase):
|
||||
self.create_wizard(self.invoice1)
|
||||
|
||||
wiz_line = self.wiz.line_ids.filtered(
|
||||
lambda rec: rec.invoice_line_id == self.inv_line1)
|
||||
lambda rec: rec.invoice_line_id == self.inv_line1
|
||||
)
|
||||
wiz_line.account_analytic_id = self.aa1
|
||||
self.wiz.run()
|
||||
|
||||
related_ml = self.invoice1.move_id.line_ids.filtered(
|
||||
lambda rec: rec.product_id == self.product16)
|
||||
lambda rec: rec.product_id == self.product16
|
||||
)
|
||||
self.assertEqual(related_ml.analytic_account_id, self.aa1)
|
||||
self.assertEqual(related_ml.analytic_line_ids.account_id, self.aa1)
|
||||
|
||||
def test_error_grouped_move_lines(self):
|
||||
""" Change analytic account on an invoice line
|
||||
"""Change analytic account on an invoice line
|
||||
after the invoice has been approved where both
|
||||
lines were grouped in the same move line.
|
||||
|
||||
@@ -111,7 +122,7 @@ class TestAccountInvoiceUpdateWizard(SavepointCase):
|
||||
self.wiz.run()
|
||||
|
||||
def test_add_analytic_tags_line1(self):
|
||||
""" Add analytic tags on an invoice line
|
||||
"""Add analytic tags on an invoice line
|
||||
after the invoice has been approved.
|
||||
|
||||
This will update move line.
|
||||
@@ -120,17 +131,19 @@ class TestAccountInvoiceUpdateWizard(SavepointCase):
|
||||
self.create_wizard(self.invoice1)
|
||||
|
||||
wiz_line = self.wiz.line_ids.filtered(
|
||||
lambda rec: rec.invoice_line_id == self.inv_line1)
|
||||
lambda rec: rec.invoice_line_id == self.inv_line1
|
||||
)
|
||||
wiz_line.analytic_tag_ids = self.atag2
|
||||
self.wiz.run()
|
||||
|
||||
related_ml = self.invoice1.move_id.line_ids.filtered(
|
||||
lambda rec: rec.product_id == self.product16)
|
||||
lambda rec: rec.product_id == self.product16
|
||||
)
|
||||
self.assertEqual(related_ml.analytic_tag_ids, self.atag2)
|
||||
self.assertFalse(related_ml.analytic_line_ids)
|
||||
|
||||
def test_change_analytic_tags_line1(self):
|
||||
""" Change analytic tags on an invoice line
|
||||
"""Change analytic tags on an invoice line
|
||||
after the invoice has been approved.
|
||||
|
||||
It will update move line and analytic line
|
||||
@@ -142,17 +155,19 @@ class TestAccountInvoiceUpdateWizard(SavepointCase):
|
||||
self.create_wizard(self.invoice1)
|
||||
|
||||
wiz_line = self.wiz.line_ids.filtered(
|
||||
lambda rec: rec.invoice_line_id == self.inv_line1)
|
||||
lambda rec: rec.invoice_line_id == self.inv_line1
|
||||
)
|
||||
wiz_line.analytic_tag_ids = self.atag2
|
||||
self.wiz.run()
|
||||
|
||||
related_ml = self.invoice1.move_id.line_ids.filtered(
|
||||
lambda rec: rec.product_id == self.product16)
|
||||
lambda rec: rec.product_id == self.product16
|
||||
)
|
||||
self.assertEqual(related_ml.analytic_tag_ids, self.atag2)
|
||||
self.assertEqual(related_ml.analytic_line_ids.tag_ids, self.atag2)
|
||||
|
||||
def test_add_analytic_info_line1(self):
|
||||
""" Add analytic account and tags on an invoice line
|
||||
"""Add analytic account and tags on an invoice line
|
||||
after the invoice has been approved.
|
||||
|
||||
This will:
|
||||
@@ -163,20 +178,22 @@ class TestAccountInvoiceUpdateWizard(SavepointCase):
|
||||
self.create_wizard(self.invoice1)
|
||||
|
||||
wiz_line = self.wiz.line_ids.filtered(
|
||||
lambda rec: rec.invoice_line_id == self.inv_line1)
|
||||
lambda rec: rec.invoice_line_id == self.inv_line1
|
||||
)
|
||||
wiz_line.account_analytic_id = self.aa1
|
||||
wiz_line.analytic_tag_ids = self.atag2
|
||||
self.wiz.run()
|
||||
|
||||
related_ml = self.invoice1.move_id.line_ids.filtered(
|
||||
lambda rec: rec.product_id == self.product16)
|
||||
lambda rec: rec.product_id == self.product16
|
||||
)
|
||||
self.assertEqual(related_ml.analytic_account_id, self.aa1)
|
||||
self.assertEqual(related_ml.analytic_tag_ids, self.atag2)
|
||||
self.assertEqual(related_ml.analytic_line_ids.account_id, self.aa1)
|
||||
self.assertEqual(related_ml.analytic_line_ids.tag_ids, self.atag2)
|
||||
|
||||
def test_empty_analytic_account_line1(self):
|
||||
""" Remove analytic account
|
||||
"""Remove analytic account
|
||||
after the invoice has been approved.
|
||||
|
||||
This will raise an error as it is not implemented.
|
||||
@@ -187,10 +204,12 @@ class TestAccountInvoiceUpdateWizard(SavepointCase):
|
||||
self.create_wizard(self.invoice1)
|
||||
|
||||
wiz_line = self.wiz.line_ids.filtered(
|
||||
lambda rec: rec.invoice_line_id == self.inv_line1)
|
||||
lambda rec: rec.invoice_line_id == self.inv_line1
|
||||
)
|
||||
wiz_line.account_analytic_id = False
|
||||
self.wiz.run()
|
||||
related_ml = self.invoice1.move_id.line_ids.filtered(
|
||||
lambda rec: rec.product_id == self.product16)
|
||||
lambda rec: rec.product_id == self.product16
|
||||
)
|
||||
self.assertFalse(related_ml.analytic_account_id)
|
||||
self.assertFalse(related_ml.analytic_line_ids)
|
||||
|
||||
@@ -1,27 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<!--
|
||||
Copyright 2017 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
|
||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
-->
|
||||
|
||||
<odoo>
|
||||
|
||||
<record id="invoice_supplier_form" model="ir.ui.view">
|
||||
<field name="model">account.invoice</field>
|
||||
<field name="inherit_id" ref="account.invoice_supplier_form"/>
|
||||
<field name="inherit_id" ref="account.invoice_supplier_form" />
|
||||
<field name="arch" type="xml">
|
||||
<button name="action_invoice_draft" position="before">
|
||||
<button name="prepare_update_wizard" type="object" string="Update Invoice" states="open,paid" groups="account.group_account_invoice"/>
|
||||
<button
|
||||
name="prepare_update_wizard"
|
||||
type="object"
|
||||
string="Update Invoice"
|
||||
states="open,paid"
|
||||
groups="account.group_account_invoice"
|
||||
/>
|
||||
</button>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="invoice_form" model="ir.ui.view">
|
||||
<field name="model">account.invoice</field>
|
||||
<field name="inherit_id" ref="account.invoice_form"/>
|
||||
<field name="inherit_id" ref="account.invoice_form" />
|
||||
<field name="arch" type="xml">
|
||||
<button name="action_invoice_draft" position="before">
|
||||
<button name="prepare_update_wizard" type="object" string="Update Invoice" states="open,paid" groups="account.group_account_invoice"/>
|
||||
<button
|
||||
name="prepare_update_wizard"
|
||||
type="object"
|
||||
string="Update Invoice"
|
||||
states="open,paid"
|
||||
groups="account.group_account_invoice"
|
||||
/>
|
||||
</button>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -2,47 +2,45 @@
|
||||
# Copyright 2018-2019 Camptocamp
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import models, fields, api, _
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
import odoo.addons.decimal_precision as dp
|
||||
|
||||
|
||||
class AccountInvoiceUpdate(models.TransientModel):
|
||||
_name = 'account.invoice.update'
|
||||
_description = 'Wizard to update non-legal fields of invoice'
|
||||
_name = "account.invoice.update"
|
||||
_description = "Wizard to update non-legal fields of invoice"
|
||||
|
||||
invoice_id = fields.Many2one(
|
||||
'account.invoice', string='Invoice', required=True,
|
||||
readonly=True)
|
||||
type = fields.Selection(related='invoice_id.type', readonly=True)
|
||||
company_id = fields.Many2one(
|
||||
related='invoice_id.company_id', readonly=True)
|
||||
partner_id = fields.Many2one(
|
||||
related='invoice_id.partner_id', readonly=True)
|
||||
user_id = fields.Many2one('res.users', string='Salesperson')
|
||||
payment_term_id = fields.Many2one(
|
||||
'account.payment.term', string='Payment Term')
|
||||
reference = fields.Char(string='Invoice Reference')
|
||||
name = fields.Char(string='Reference/Description')
|
||||
origin = fields.Char(string='Source Document')
|
||||
comment = fields.Text('Additional Information')
|
||||
partner_bank_id = fields.Many2one(
|
||||
'res.partner.bank', string='Bank Account')
|
||||
"account.invoice", string="Invoice", required=True, readonly=True
|
||||
)
|
||||
type = fields.Selection(related="invoice_id.type", readonly=True)
|
||||
company_id = fields.Many2one(related="invoice_id.company_id", readonly=True)
|
||||
partner_id = fields.Many2one(related="invoice_id.partner_id", readonly=True)
|
||||
user_id = fields.Many2one("res.users", string="Salesperson")
|
||||
payment_term_id = fields.Many2one("account.payment.term", string="Payment Term")
|
||||
reference = fields.Char(string="Invoice Reference")
|
||||
name = fields.Char(string="Reference/Description")
|
||||
origin = fields.Char(string="Source Document")
|
||||
comment = fields.Text("Additional Information")
|
||||
partner_bank_id = fields.Many2one("res.partner.bank", string="Bank Account")
|
||||
line_ids = fields.One2many(
|
||||
'account.invoice.line.update', 'parent_id', string='Invoice Lines')
|
||||
"account.invoice.line.update", "parent_id", string="Invoice Lines"
|
||||
)
|
||||
|
||||
@api.model
|
||||
def _simple_fields2update(self):
|
||||
'''List boolean, date, datetime, char, text fields'''
|
||||
return ['reference', 'name', 'origin', 'comment']
|
||||
"""List boolean, date, datetime, char, text fields"""
|
||||
return ["reference", "name", "origin", "comment"]
|
||||
|
||||
@api.model
|
||||
def _m2o_fields2update(self):
|
||||
return ['payment_term_id', 'user_id', 'partner_bank_id']
|
||||
return ["payment_term_id", "user_id", "partner_bank_id"]
|
||||
|
||||
@api.model
|
||||
def _prepare_default_get(self, invoice):
|
||||
res = {'invoice_id': invoice.id, 'line_ids': []}
|
||||
res = {"invoice_id": invoice.id, "line_ids": []}
|
||||
for sfield in self._simple_fields2update():
|
||||
res[sfield] = invoice[sfield]
|
||||
for m2ofield in self._m2o_fields2update():
|
||||
@@ -50,26 +48,32 @@ class AccountInvoiceUpdate(models.TransientModel):
|
||||
for line in invoice.invoice_line_ids:
|
||||
aa_tags = line.analytic_tag_ids
|
||||
aa_tags = [(6, 0, aa_tags.ids)] if aa_tags else False
|
||||
res['line_ids'].append([0, 0, {
|
||||
'invoice_line_id': line.id,
|
||||
'name': line.name,
|
||||
'quantity': line.quantity,
|
||||
'price_subtotal': line.price_subtotal,
|
||||
'account_analytic_id': line.account_analytic_id.id,
|
||||
'analytic_tag_ids': aa_tags,
|
||||
'display_type': line.display_type,
|
||||
}])
|
||||
res["line_ids"].append(
|
||||
[
|
||||
0,
|
||||
0,
|
||||
{
|
||||
"invoice_line_id": line.id,
|
||||
"name": line.name,
|
||||
"quantity": line.quantity,
|
||||
"price_subtotal": line.price_subtotal,
|
||||
"account_analytic_id": line.account_analytic_id.id,
|
||||
"analytic_tag_ids": aa_tags,
|
||||
"display_type": line.display_type,
|
||||
},
|
||||
]
|
||||
)
|
||||
return res
|
||||
|
||||
@api.onchange('type')
|
||||
@api.onchange("type")
|
||||
def type_on_change(self):
|
||||
res = {'domain': {}}
|
||||
if self.type in ('out_invoice', 'out_refund'):
|
||||
res['domain']['partner_bank_id'] =\
|
||||
"[('partner_id.ref_company_ids', 'in', [company_id])]"
|
||||
res = {"domain": {}}
|
||||
if self.type in ("out_invoice", "out_refund"):
|
||||
res["domain"][
|
||||
"partner_bank_id"
|
||||
] = "[('partner_id.ref_company_ids', 'in', [company_id])]"
|
||||
else:
|
||||
res['domain']['partner_bank_id'] =\
|
||||
"[('partner_id', '=', partner_id)]"
|
||||
res["domain"]["partner_bank_id"] = "[('partner_id', '=', partner_id)]"
|
||||
return res
|
||||
|
||||
@api.multi
|
||||
@@ -82,24 +86,31 @@ class AccountInvoiceUpdate(models.TransientModel):
|
||||
for m2ofield in self._m2o_fields2update():
|
||||
if self[m2ofield] != inv[m2ofield]:
|
||||
vals[m2ofield] = self[m2ofield].id or False
|
||||
if 'payment_term_id' in vals:
|
||||
if "payment_term_id" in vals:
|
||||
pterm_list = self.payment_term_id.compute(
|
||||
value=1, date_ref=inv.date_invoice)[0]
|
||||
value=1, date_ref=inv.date_invoice
|
||||
)[0]
|
||||
if pterm_list:
|
||||
vals['date_due'] = max(line[0] for line in pterm_list)
|
||||
vals["date_due"] = max(line[0] for line in pterm_list)
|
||||
return vals
|
||||
|
||||
@api.model
|
||||
def _line_simple_fields2update(self):
|
||||
return ["name",]
|
||||
return [
|
||||
"name",
|
||||
]
|
||||
|
||||
@api.model
|
||||
def _line_m2o_fields2update(self):
|
||||
return ["account_analytic_id",]
|
||||
return [
|
||||
"account_analytic_id",
|
||||
]
|
||||
|
||||
@api.model
|
||||
def _line_m2m_fields2update(self):
|
||||
return ["analytic_tag_ids",]
|
||||
return [
|
||||
"analytic_tag_ids",
|
||||
]
|
||||
|
||||
@api.model
|
||||
def _prepare_invoice_line(self, line):
|
||||
@@ -122,7 +133,7 @@ class AccountInvoiceUpdate(models.TransientModel):
|
||||
ini_ref = inv.move_id.ref
|
||||
ref = inv.reference or inv.name
|
||||
if ini_ref != ref:
|
||||
mvals['ref'] = ref
|
||||
mvals["ref"] = ref
|
||||
return mvals
|
||||
|
||||
@api.multi
|
||||
@@ -131,49 +142,52 @@ class AccountInvoiceUpdate(models.TransientModel):
|
||||
# TODO make it accept more case as lines won't
|
||||
# be grouped unless journal.group_invoice_line is True
|
||||
inv_line = self.invoice_id.invoice_line_ids.filtered(
|
||||
lambda rec: rec.product_id == move_line.product_id)
|
||||
lambda rec: rec.product_id == move_line.product_id
|
||||
)
|
||||
if len(inv_line) != 1:
|
||||
raise UserError(
|
||||
"Cannot match a single invoice line to move line %s" %
|
||||
move_line.name)
|
||||
"Cannot match a single invoice line to move line %s" % move_line.name
|
||||
)
|
||||
return inv_line
|
||||
|
||||
@api.multi
|
||||
def _prepare_move_line(self, inv_line):
|
||||
mlvals = {}
|
||||
inv_line_upd = self.line_ids.filtered(
|
||||
lambda rec: rec.invoice_line_id == inv_line)
|
||||
lambda rec: rec.invoice_line_id == inv_line
|
||||
)
|
||||
|
||||
ini_aa = inv_line.account_analytic_id
|
||||
new_aa = inv_line_upd.account_analytic_id
|
||||
|
||||
if ini_aa != new_aa:
|
||||
mlvals['analytic_account_id'] = new_aa.id
|
||||
mlvals["analytic_account_id"] = new_aa.id
|
||||
|
||||
ini_aa_tags = inv_line.analytic_tag_ids
|
||||
new_aa_tags = inv_line_upd.analytic_tag_ids
|
||||
|
||||
if ini_aa_tags != new_aa_tags:
|
||||
mlvals['analytic_tag_ids'] = [(6, None, new_aa_tags.ids)]
|
||||
mlvals["analytic_tag_ids"] = [(6, None, new_aa_tags.ids)]
|
||||
return mlvals
|
||||
|
||||
@api.multi
|
||||
def _prepare_analytic_line(self, inv_line):
|
||||
alvals = {}
|
||||
inv_line_upd = self.line_ids.filtered(
|
||||
lambda rec: rec.invoice_line_id == inv_line)
|
||||
lambda rec: rec.invoice_line_id == inv_line
|
||||
)
|
||||
|
||||
ini_aa = inv_line.account_analytic_id
|
||||
new_aa = inv_line_upd.account_analytic_id
|
||||
|
||||
if ini_aa != new_aa:
|
||||
alvals['account_id'] = new_aa.id
|
||||
alvals["account_id"] = new_aa.id
|
||||
|
||||
ini_aa_tags = inv_line.analytic_tag_ids
|
||||
new_aa_tags = inv_line_upd.analytic_tag_ids
|
||||
|
||||
if ini_aa_tags != new_aa_tags:
|
||||
alvals['tag_ids'] = [(6, None, new_aa_tags.ids)]
|
||||
alvals["tag_ids"] = [(6, None, new_aa_tags.ids)]
|
||||
return alvals
|
||||
|
||||
@api.multi
|
||||
@@ -181,22 +195,27 @@ class AccountInvoiceUpdate(models.TransientModel):
|
||||
self.ensure_one()
|
||||
inv = self.invoice_id
|
||||
if (
|
||||
self.payment_term_id and
|
||||
self.payment_term_id != inv.payment_term_id and
|
||||
inv.move_id):
|
||||
self.payment_term_id
|
||||
and self.payment_term_id != inv.payment_term_id
|
||||
and inv.move_id
|
||||
):
|
||||
# I don't update pay term when the invoice is partially (or fully)
|
||||
# paid because if you have a payment term with several lines
|
||||
# of the same amount, you would also have to take into account
|
||||
# the reconcile marks to put the new maturity date on the right
|
||||
# lines
|
||||
if inv.payment_ids:
|
||||
raise UserError(_(
|
||||
"This wizard doesn't support the update of payment "
|
||||
"terms on an invoice which is partially or fully "
|
||||
"paid."))
|
||||
prec = self.env['decimal.precision'].precision_get('Account')
|
||||
term_res = self.payment_term_id.compute(
|
||||
inv.amount_total, inv.date_invoice)[0]
|
||||
raise UserError(
|
||||
_(
|
||||
"This wizard doesn't support the update of payment "
|
||||
"terms on an invoice which is partially or fully "
|
||||
"paid."
|
||||
)
|
||||
)
|
||||
prec = self.env["decimal.precision"].precision_get("Account")
|
||||
term_res = self.payment_term_id.compute(inv.amount_total, inv.date_invoice)[
|
||||
0
|
||||
]
|
||||
new_pterm = {} # key = int(amount * 100), value = [date1, date2]
|
||||
for entry in term_res:
|
||||
amount = int(entry[1] * 10 * prec)
|
||||
@@ -214,13 +233,16 @@ class AccountInvoiceUpdate(models.TransientModel):
|
||||
mlines[amount] = [line]
|
||||
for iamount, lines in mlines.items():
|
||||
if len(lines) != len(new_pterm.get(iamount, [])):
|
||||
raise UserError(_(
|
||||
"The original payment term '%s' doesn't have the "
|
||||
"same terms (number of terms and/or amount) as the "
|
||||
"new payment term '%s'. You can only switch to a "
|
||||
"payment term that has the same number of terms "
|
||||
"with the same amount.") % (
|
||||
inv.payment_term_id.name, self.payment_term_id.name))
|
||||
raise UserError(
|
||||
_(
|
||||
"The original payment term '%s' doesn't have the "
|
||||
"same terms (number of terms and/or amount) as the "
|
||||
"new payment term '%s'. You can only switch to a "
|
||||
"payment term that has the same number of terms "
|
||||
"with the same amount."
|
||||
)
|
||||
% (inv.payment_term_id.name, self.payment_term_id.name)
|
||||
)
|
||||
for line in lines:
|
||||
line.date_maturity = new_pterm[iamount].pop()
|
||||
|
||||
@@ -240,8 +262,8 @@ class AccountInvoiceUpdate(models.TransientModel):
|
||||
if mvals:
|
||||
inv.move_id.write(mvals)
|
||||
for ml in inv.move_id.line_ids.filtered(
|
||||
# we are only interested in invoice lines, not tax lines
|
||||
lambda rec: bool(rec.product_id)
|
||||
# we are only interested in invoice lines, not tax lines
|
||||
lambda rec: bool(rec.product_id)
|
||||
):
|
||||
if ml.credit == 0.0:
|
||||
continue
|
||||
@@ -254,16 +276,16 @@ class AccountInvoiceUpdate(models.TransientModel):
|
||||
alvals = self._prepare_analytic_line(inv_line)
|
||||
if aalines and alvals:
|
||||
updated = True
|
||||
if ('account_id' in alvals and
|
||||
alvals['account_id'] is False):
|
||||
if "account_id" in alvals and alvals["account_id"] is False:
|
||||
former_aa = inv_line.account_analytic_id
|
||||
to_remove_aalines = aalines.filtered(
|
||||
lambda rec: rec.account_id == former_aa)
|
||||
lambda rec: rec.account_id == former_aa
|
||||
)
|
||||
# remove existing analytic line
|
||||
to_remove_aalines.unlink()
|
||||
else:
|
||||
aalines.write(alvals)
|
||||
elif 'account_id' in alvals:
|
||||
elif "account_id" in alvals:
|
||||
# Create analytic lines if analytic account
|
||||
# is added later
|
||||
ml.create_analytic_lines()
|
||||
@@ -273,30 +295,40 @@ class AccountInvoiceUpdate(models.TransientModel):
|
||||
updated = True
|
||||
line.invoice_line_id.write(ilvals)
|
||||
if updated:
|
||||
inv.message_post(body=_(
|
||||
'Non-legal fields of invoice updated via the Invoice Update '
|
||||
'wizard.'))
|
||||
inv.message_post(
|
||||
body=_(
|
||||
"Non-legal fields of invoice updated via the Invoice Update "
|
||||
"wizard."
|
||||
)
|
||||
)
|
||||
return True
|
||||
|
||||
|
||||
class AccountInvoiceLineUpdate(models.TransientModel):
|
||||
_name = 'account.invoice.line.update'
|
||||
_description = 'Update non-legal fields of invoice lines'
|
||||
_name = "account.invoice.line.update"
|
||||
_description = "Update non-legal fields of invoice lines"
|
||||
|
||||
parent_id = fields.Many2one(
|
||||
'account.invoice.update', string='Wizard', ondelete='cascade')
|
||||
"account.invoice.update", string="Wizard", ondelete="cascade"
|
||||
)
|
||||
invoice_line_id = fields.Many2one(
|
||||
'account.invoice.line', string='Invoice Line', readonly=True)
|
||||
name = fields.Text(string='Description', required=True)
|
||||
display_type = fields.Selection([
|
||||
('line_section', "Section"),
|
||||
('line_note', "Note")], default=False, help="Technical field for UX purpose.")
|
||||
"account.invoice.line", string="Invoice Line", readonly=True
|
||||
)
|
||||
name = fields.Text(string="Description", required=True)
|
||||
display_type = fields.Selection(
|
||||
[("line_section", "Section"), ("line_note", "Note")],
|
||||
default=False,
|
||||
help="Technical field for UX purpose.",
|
||||
)
|
||||
quantity = fields.Float(
|
||||
string='Quantity', digits=dp.get_precision('Product Unit of Measure'),
|
||||
readonly=True)
|
||||
string="Quantity",
|
||||
digits=dp.get_precision("Product Unit of Measure"),
|
||||
readonly=True,
|
||||
)
|
||||
price_subtotal = fields.Float(
|
||||
string='Amount', readonly=True, digits=dp.get_precision('Account'))
|
||||
string="Amount", readonly=True, digits=dp.get_precision("Account")
|
||||
)
|
||||
account_analytic_id = fields.Many2one(
|
||||
'account.analytic.account', string='Analytic Account')
|
||||
analytic_tag_ids = fields.Many2many(
|
||||
'account.analytic.tag', string='Analytic Tags')
|
||||
"account.analytic.account", string="Analytic Account"
|
||||
)
|
||||
analytic_tag_ids = fields.Many2many("account.analytic.tag", string="Analytic Tags")
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<!--
|
||||
© 2017 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
|
||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
-->
|
||||
|
||||
<odoo>
|
||||
|
||||
<record id="account_invoice_update_form" model="ir.ui.view">
|
||||
@@ -11,34 +10,62 @@
|
||||
<field name="arch" type="xml">
|
||||
<form string="Update Invoice Wizard">
|
||||
<group name="main">
|
||||
<field name="invoice_id" invisible="1"/>
|
||||
<field name="type" invisible="1"/>
|
||||
<field name="company_id" invisible="1"/>
|
||||
<field name="partner_id" invisible="1"/>
|
||||
<field name="reference" attrs="{'invisible': [('type', 'not in', ('in_invoice', 'in_refund'))]}"/>
|
||||
<field name="origin"/>
|
||||
<field name="name"/>
|
||||
<field name="payment_term_id" widget="selection"/>
|
||||
<field name="partner_bank_id"/>
|
||||
<field name="user_id"/>
|
||||
<field name="comment"/>
|
||||
<field name="invoice_id" invisible="1" />
|
||||
<field name="type" invisible="1" />
|
||||
<field name="company_id" invisible="1" />
|
||||
<field name="partner_id" invisible="1" />
|
||||
<field
|
||||
name="reference"
|
||||
attrs="{'invisible': [('type', 'not in', ('in_invoice', 'in_refund'))]}"
|
||||
/>
|
||||
<field name="origin" />
|
||||
<field name="name" />
|
||||
<field name="payment_term_id" widget="selection" />
|
||||
<field name="partner_bank_id" />
|
||||
<field name="user_id" />
|
||||
<field name="comment" />
|
||||
</group>
|
||||
<group name="lines">
|
||||
<field name="line_ids" nolabel="1">
|
||||
<tree editable="bottom" create="false" delete="false" edit="true">
|
||||
<field name="invoice_line_id" invisible="1"/>
|
||||
<field name="display_type" invisible="1"/>
|
||||
<field name="name"/>
|
||||
<field name="quantity" attrs="{'invisible': [('display_type', '!=', False)]}"/>
|
||||
<field name="price_subtotal" attrs="{'invisible': [('display_type', '!=', False)]}"/>
|
||||
<field name="account_analytic_id" attrs="{'invisible': [('display_type', '!=', False)]}" groups="analytic.group_analytic_accounting"/>
|
||||
<field name="analytic_tag_ids" attrs="{'invisible': [('display_type', '!=', False)]}" groups="analytic.group_analytic_accounting" widget="many2many_tags"/>
|
||||
<tree
|
||||
editable="bottom"
|
||||
create="false"
|
||||
delete="false"
|
||||
edit="true"
|
||||
>
|
||||
<field name="invoice_line_id" invisible="1" />
|
||||
<field name="display_type" invisible="1" />
|
||||
<field name="name" />
|
||||
<field
|
||||
name="quantity"
|
||||
attrs="{'invisible': [('display_type', '!=', False)]}"
|
||||
/>
|
||||
<field
|
||||
name="price_subtotal"
|
||||
attrs="{'invisible': [('display_type', '!=', False)]}"
|
||||
/>
|
||||
<field
|
||||
name="account_analytic_id"
|
||||
attrs="{'invisible': [('display_type', '!=', False)]}"
|
||||
groups="analytic.group_analytic_accounting"
|
||||
/>
|
||||
<field
|
||||
name="analytic_tag_ids"
|
||||
attrs="{'invisible': [('display_type', '!=', False)]}"
|
||||
groups="analytic.group_analytic_accounting"
|
||||
widget="many2many_tags"
|
||||
/>
|
||||
</tree>
|
||||
</field>
|
||||
</group>
|
||||
<footer>
|
||||
<button name="run" type="object" class="oe_highlight" string="Update"/>
|
||||
<button special="cancel" string="Cancel" class="oe_link"/>
|
||||
<button
|
||||
name="run"
|
||||
type="object"
|
||||
class="oe_highlight"
|
||||
string="Update"
|
||||
/>
|
||||
<button special="cancel" string="Cancel" class="oe_link" />
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
|
||||
Reference in New Issue
Block a user