From 01c323d5ead8561b8428bee82ebed0fd798d303c Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Wed, 21 Jun 2017 23:57:29 +0200 Subject: [PATCH 1/9] Add module account_invoice_update_wizard --- account_invoice_update_wizard/__init__.py | 3 + account_invoice_update_wizard/__openerp__.py | 34 +++ .../views/account_invoice.xml | 31 +++ .../wizard/__init__.py | 3 + .../wizard/account_invoice_update.py | 206 ++++++++++++++++++ .../wizard/account_invoice_update_view.xml | 54 +++++ 6 files changed, 331 insertions(+) create mode 100644 account_invoice_update_wizard/__init__.py create mode 100644 account_invoice_update_wizard/__openerp__.py create mode 100644 account_invoice_update_wizard/views/account_invoice.xml create mode 100644 account_invoice_update_wizard/wizard/__init__.py create mode 100644 account_invoice_update_wizard/wizard/account_invoice_update.py create mode 100644 account_invoice_update_wizard/wizard/account_invoice_update_view.xml diff --git a/account_invoice_update_wizard/__init__.py b/account_invoice_update_wizard/__init__.py new file mode 100644 index 00000000..3b4c3edf --- /dev/null +++ b/account_invoice_update_wizard/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import wizard diff --git a/account_invoice_update_wizard/__openerp__.py b/account_invoice_update_wizard/__openerp__.py new file mode 100644 index 00000000..71e396be --- /dev/null +++ b/account_invoice_update_wizard/__openerp__.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +# © 2017 Akretion (Alexis de Lattre ) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + 'name': 'Account Invoice Update Wizard', + 'version': '8.0.1.0.0', + 'category': 'Accounting & Finance', + 'license': 'AGPL-3', + 'summary': 'Wizard to update non-legal fields of an open/paid invoice', + 'description': """ +Account Invoice Update Wizard +============================= + +This module adds a button *Update Invoice* on Customer and Supplier invoices in Open or Paid state. This button starts a wizard which allows the user to update non-legal fields of the invoice: + +* Source Document +* Reference/Description +* Payment terms (update allowed only to a payment term with same number of terms of the same amount and on invoices without any payment) +* Bank Account +* Salesman +* Notes +* Description of invoice lines + + """, + 'author': 'Akretion', + 'website': 'http://www.akretion.com', + 'depends': ['account'], + 'data': [ + 'wizard/account_invoice_update_view.xml', + 'views/account_invoice.xml', + ], + 'installable': True, +} diff --git a/account_invoice_update_wizard/views/account_invoice.xml b/account_invoice_update_wizard/views/account_invoice.xml new file mode 100644 index 00000000..5d779f4e --- /dev/null +++ b/account_invoice_update_wizard/views/account_invoice.xml @@ -0,0 +1,31 @@ + + + + + + + + account.invoice + + + + + + + + account.invoice + + + + + + + + diff --git a/account_invoice_update_wizard/wizard/__init__.py b/account_invoice_update_wizard/wizard/__init__.py new file mode 100644 index 00000000..f66fb849 --- /dev/null +++ b/account_invoice_update_wizard/wizard/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import account_invoice_update diff --git a/account_invoice_update_wizard/wizard/account_invoice_update.py b/account_invoice_update_wizard/wizard/account_invoice_update.py new file mode 100644 index 00000000..7d4eff25 --- /dev/null +++ b/account_invoice_update_wizard/wizard/account_invoice_update.py @@ -0,0 +1,206 @@ +# -*- coding: utf-8 -*- +# © 2017 Akretion (Alexis de Lattre ) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp import models, fields, api, _ +from openerp.exceptions import Warning as UserError +import openerp.addons.decimal_precision as dp + + +class AccountInvoiceUpdate(models.TransientModel): + _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) + commercial_partner_id = fields.Many2one( + related='invoice_id.commercial_partner_id', readonly=True) + user_id = fields.Many2one('res.users', string='Salesperson') + # I use the same field name as the original invoice field name + # even if it the name is "bad" + # Updating payment_term will not work if you use + # the OCA module account_constraints (you will just get an error) + payment_term = fields.Many2one( + 'account.payment.term', string='Payment Terms') + 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') + + @api.model + def _simple_fields2update(self): + '''List boolean, date, datetime, char, text fields''' + return ['reference', 'name', 'origin', 'comment'] + + @api.model + def _m2o_fields2update(self): + return ['payment_term', 'user_id', 'partner_bank_id'] + + @api.model + def _prepare_default_get(self, invoice): + res = {'invoice_id': invoice.id, 'line_ids': []} + for sfield in self._simple_fields2update(): + res[sfield] = invoice[sfield] + for m2ofield in self._m2o_fields2update(): + res[m2ofield] = invoice[m2ofield].id or False + for line in invoice.invoice_line: + res['line_ids'].append({ + 'invoice_line_id': line.id, + 'name': line.name, + 'quantity': line.quantity, + 'price_subtotal': line.price_subtotal, + }) + return res + + @api.model + def default_get(self, fields_list): + res = super(AccountInvoiceUpdate, self).default_get(fields_list) + assert self._context.get('active_model') == 'account.invoice',\ + 'active_model should be account.invoice' + inv = self.env['account.invoice'].browse(self._context['active_id']) + res = self._prepare_default_get(inv) + return res + + @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_companies', 'in', [company_id])]" + else: + res['domain']['partner_bank_id'] =\ + "[('partner_id', '=', commercial_partner_id)]" + return res + + @api.multi + def _prepare_invoice(self): + vals = {} + inv = self.invoice_id + for sfield in self._simple_fields2update(): + if self[sfield] != inv[sfield]: + vals[sfield] = self[sfield] + for m2ofield in self._m2o_fields2update(): + if self[m2ofield] != inv[m2ofield]: + vals[m2ofield] = self[m2ofield].id or False + if 'payment_term' in vals: + pterm_list = self.payment_term.compute( + value=1, date_ref=inv.date_invoice)[0] + if pterm_list: + vals['date_due'] = max(line[0] for line in pterm_list) + return vals + + @api.model + def _prepare_invoice_line(self, line): + vals = {} + if line.name != line.invoice_line_id.name: + vals['name'] = line.name + return vals + + @api.multi + def _prepare_move(self): + mvals = {} + inv = self.invoice_id + ini_ref = inv.move_id.ref + ref = inv.reference or inv.name + if ini_ref != ref: + mvals['ref'] = ref + return mvals + + @api.multi + def _update_payment_term_move(self): + self.ensure_one() + inv = self.invoice_id + if ( + self.payment_term and + self.payment_term != inv.payment_term and + inv.move_id and + inv.move_id.period_id.state == 'draft'): + # 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.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) + if amount in new_pterm: + new_pterm[amount].append(entry[0]) + else: + new_pterm[amount] = [entry[0]] + mlines = {} # key = int(amount * 100), value : [line1, line2] + for line in inv.move_id.line_id: + if line.account_id == inv.account_id: + amount = int(abs(line.credit - line.debit) * 10 * prec) + if amount in mlines: + mlines[amount].append(line) + else: + mlines[amount] = [line] + for iamount, lines in mlines.iteritems(): + 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.name, self.payment_term.name)) + for line in lines: + line.date_maturity = new_pterm[iamount].pop() + + @api.multi + def run(self): + self.ensure_one() + inv = self.invoice_id + updated = False + # re-write date_maturity on move line + self._update_payment_term_move() + ivals = self._prepare_invoice() + if ivals: + updated = True + inv.write(ivals) + for line in self.line_ids: + ilvals = self._prepare_invoice_line(line) + if ilvals: + updated = True + line.invoice_line_id.write(ilvals) + if inv.move_id and inv.move_id.period_id.state == 'draft': + mvals = self._prepare_move() + if mvals: + inv.move_id.write(mvals) + if updated: + inv.message_post(_( + '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' + + parent_id = fields.Many2one( + '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) + quantity = fields.Float( + 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')) diff --git a/account_invoice_update_wizard/wizard/account_invoice_update_view.xml b/account_invoice_update_wizard/wizard/account_invoice_update_view.xml new file mode 100644 index 00000000..5da58144 --- /dev/null +++ b/account_invoice_update_wizard/wizard/account_invoice_update_view.xml @@ -0,0 +1,54 @@ + + + + + + + + account.invoice.update + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+
+ + + Invoice Update Wizard + account.invoice.update + form + new + + +
+
From 100c705977dc87a025ea94b639aa7d107631126f Mon Sep 17 00:00:00 2001 From: Florian da Costa Date: Fri, 21 Jul 2017 14:37:51 +0200 Subject: [PATCH 2/9] Make modules uninstallable --- account_invoice_update_wizard/__openerp__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/account_invoice_update_wizard/__openerp__.py b/account_invoice_update_wizard/__openerp__.py index 71e396be..e528a091 100644 --- a/account_invoice_update_wizard/__openerp__.py +++ b/account_invoice_update_wizard/__openerp__.py @@ -30,5 +30,5 @@ 'wizard/account_invoice_update_view.xml', 'views/account_invoice.xml', ], - 'installable': True, + 'installable': False, } From 830f752b43e60d4c1487523256687e3b9b0e1bfe Mon Sep 17 00:00:00 2001 From: mdietrichc2c Date: Wed, 30 Aug 2017 11:26:40 +0200 Subject: [PATCH 3/9] [9.0] Port of module account_invoice_update_wizard --- account_invoice_update_wizard/__openerp__.py | 2 +- .../wizard/account_invoice_update.py | 43 ++++++++----------- .../wizard/account_invoice_update_view.xml | 4 +- 3 files changed, 22 insertions(+), 27 deletions(-) diff --git a/account_invoice_update_wizard/__openerp__.py b/account_invoice_update_wizard/__openerp__.py index e528a091..71e396be 100644 --- a/account_invoice_update_wizard/__openerp__.py +++ b/account_invoice_update_wizard/__openerp__.py @@ -30,5 +30,5 @@ 'wizard/account_invoice_update_view.xml', 'views/account_invoice.xml', ], - 'installable': False, + 'installable': True, } diff --git a/account_invoice_update_wizard/wizard/account_invoice_update.py b/account_invoice_update_wizard/wizard/account_invoice_update.py index 7d4eff25..3b2f81a2 100644 --- a/account_invoice_update_wizard/wizard/account_invoice_update.py +++ b/account_invoice_update_wizard/wizard/account_invoice_update.py @@ -17,15 +17,11 @@ class AccountInvoiceUpdate(models.TransientModel): type = fields.Selection(related='invoice_id.type', readonly=True) company_id = fields.Many2one( related='invoice_id.company_id', readonly=True) - commercial_partner_id = fields.Many2one( - related='invoice_id.commercial_partner_id', readonly=True) + partner_id = fields.Many2one( + related='invoice_id.partner_id', readonly=True) user_id = fields.Many2one('res.users', string='Salesperson') - # I use the same field name as the original invoice field name - # even if it the name is "bad" - # Updating payment_term will not work if you use - # the OCA module account_constraints (you will just get an error) - payment_term = fields.Many2one( - 'account.payment.term', string='Payment Terms') + 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') @@ -42,7 +38,7 @@ def _simple_fields2update(self): @api.model def _m2o_fields2update(self): - return ['payment_term', 'user_id', 'partner_bank_id'] + return ['payment_term_id', 'user_id', 'partner_bank_id'] @api.model def _prepare_default_get(self, invoice): @@ -51,13 +47,13 @@ def _prepare_default_get(self, invoice): res[sfield] = invoice[sfield] for m2ofield in self._m2o_fields2update(): res[m2ofield] = invoice[m2ofield].id or False - for line in invoice.invoice_line: - res['line_ids'].append({ + for line in invoice.invoice_line_ids: + res['line_ids'].append([0, 0, { 'invoice_line_id': line.id, 'name': line.name, 'quantity': line.quantity, 'price_subtotal': line.price_subtotal, - }) + }]) return res @api.model @@ -74,10 +70,10 @@ def type_on_change(self): res = {'domain': {}} if self.type in ('out_invoice', 'out_refund'): res['domain']['partner_bank_id'] =\ - "[('partner_id.ref_companies', 'in', [company_id])]" + "[('partner_id.ref_company_ids', 'in', [company_id])]" else: res['domain']['partner_bank_id'] =\ - "[('partner_id', '=', commercial_partner_id)]" + "[('partner_id', '=', partner_id)]" return res @api.multi @@ -90,8 +86,8 @@ def _prepare_invoice(self): for m2ofield in self._m2o_fields2update(): if self[m2ofield] != inv[m2ofield]: vals[m2ofield] = self[m2ofield].id or False - if 'payment_term' in vals: - pterm_list = self.payment_term.compute( + if 'payment_term_id' in vals: + pterm_list = self.payment_term_id.compute( value=1, date_ref=inv.date_invoice)[0] if pterm_list: vals['date_due'] = max(line[0] for line in pterm_list) @@ -119,10 +115,9 @@ def _update_payment_term_move(self): self.ensure_one() inv = self.invoice_id if ( - self.payment_term and - self.payment_term != inv.payment_term and - inv.move_id and - inv.move_id.period_id.state == 'draft'): + 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 @@ -134,7 +129,7 @@ def _update_payment_term_move(self): "terms on an invoice which is partially or fully " "paid.")) prec = self.env['decimal.precision'].precision_get('Account') - term_res = self.payment_term.compute( + 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: @@ -144,7 +139,7 @@ def _update_payment_term_move(self): else: new_pterm[amount] = [entry[0]] mlines = {} # key = int(amount * 100), value : [line1, line2] - for line in inv.move_id.line_id: + for line in inv.move_id.line_ids: if line.account_id == inv.account_id: amount = int(abs(line.credit - line.debit) * 10 * prec) if amount in mlines: @@ -159,7 +154,7 @@ def _update_payment_term_move(self): "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.name, self.payment_term.name)) + inv.payment_term_id.name, self.payment_term_id.name)) for line in lines: line.date_maturity = new_pterm[iamount].pop() @@ -179,7 +174,7 @@ def run(self): if ilvals: updated = True line.invoice_line_id.write(ilvals) - if inv.move_id and inv.move_id.period_id.state == 'draft': + if inv.move_id: mvals = self._prepare_move() if mvals: inv.move_id.write(mvals) diff --git a/account_invoice_update_wizard/wizard/account_invoice_update_view.xml b/account_invoice_update_wizard/wizard/account_invoice_update_view.xml index 5da58144..08992982 100644 --- a/account_invoice_update_wizard/wizard/account_invoice_update_view.xml +++ b/account_invoice_update_wizard/wizard/account_invoice_update_view.xml @@ -15,11 +15,11 @@ - + - + From 1e86958a1a324e4aee210a5eccdbac53342acccd Mon Sep 17 00:00:00 2001 From: Yannick Vaucher Date: Wed, 20 Jun 2018 14:47:59 +0200 Subject: [PATCH 4/9] [10.0] Migration of account_invoice_update_wizard --- account_invoice_update_wizard/__init__.py | 2 -- .../{__openerp__.py => __manifest__.py} | 0 .../views/account_invoice.xml | 10 ++++------ account_invoice_update_wizard/wizard/__init__.py | 2 -- .../wizard/account_invoice_update.py | 6 +++--- .../wizard/account_invoice_update_view.xml | 7 ++----- 6 files changed, 9 insertions(+), 18 deletions(-) rename account_invoice_update_wizard/{__openerp__.py => __manifest__.py} (100%) diff --git a/account_invoice_update_wizard/__init__.py b/account_invoice_update_wizard/__init__.py index 3b4c3edf..40272379 100644 --- a/account_invoice_update_wizard/__init__.py +++ b/account_invoice_update_wizard/__init__.py @@ -1,3 +1 @@ -# -*- coding: utf-8 -*- - from . import wizard diff --git a/account_invoice_update_wizard/__openerp__.py b/account_invoice_update_wizard/__manifest__.py similarity index 100% rename from account_invoice_update_wizard/__openerp__.py rename to account_invoice_update_wizard/__manifest__.py diff --git a/account_invoice_update_wizard/views/account_invoice.xml b/account_invoice_update_wizard/views/account_invoice.xml index 5d779f4e..312f7d7e 100644 --- a/account_invoice_update_wizard/views/account_invoice.xml +++ b/account_invoice_update_wizard/views/account_invoice.xml @@ -4,14 +4,13 @@ License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). --> - - + account.invoice - @@ -21,11 +20,10 @@ account.invoice - - - + diff --git a/account_invoice_update_wizard/wizard/__init__.py b/account_invoice_update_wizard/wizard/__init__.py index f66fb849..9a895186 100644 --- a/account_invoice_update_wizard/wizard/__init__.py +++ b/account_invoice_update_wizard/wizard/__init__.py @@ -1,3 +1 @@ -# -*- coding: utf-8 -*- - from . import account_invoice_update diff --git a/account_invoice_update_wizard/wizard/account_invoice_update.py b/account_invoice_update_wizard/wizard/account_invoice_update.py index 3b2f81a2..2bc26d62 100644 --- a/account_invoice_update_wizard/wizard/account_invoice_update.py +++ b/account_invoice_update_wizard/wizard/account_invoice_update.py @@ -2,9 +2,9 @@ # © 2017 Akretion (Alexis de Lattre ) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from openerp import models, fields, api, _ -from openerp.exceptions import Warning as UserError -import openerp.addons.decimal_precision as dp +from odoo import models, fields, api, _ +from odoo.exceptions import Warning as UserError +import odoo.addons.decimal_precision as dp class AccountInvoiceUpdate(models.TransientModel): diff --git a/account_invoice_update_wizard/wizard/account_invoice_update_view.xml b/account_invoice_update_wizard/wizard/account_invoice_update_view.xml index 08992982..1eab7c7a 100644 --- a/account_invoice_update_wizard/wizard/account_invoice_update_view.xml +++ b/account_invoice_update_wizard/wizard/account_invoice_update_view.xml @@ -4,8 +4,7 @@ License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). --> - - + account.invoice.update @@ -36,7 +35,6 @@
@@ -50,5 +48,4 @@ new
-
-
+ From 1695106967d82611c7330c3ad3f92cef55114fb8 Mon Sep 17 00:00:00 2001 From: Yannick Vaucher Date: Wed, 27 Jun 2018 10:35:34 +0200 Subject: [PATCH 5/9] fixup! [10.0] Migration of account_invoice_update_wizard --- account_invoice_update_wizard/wizard/account_invoice_update.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/account_invoice_update_wizard/wizard/account_invoice_update.py b/account_invoice_update_wizard/wizard/account_invoice_update.py index 2bc26d62..0de06485 100644 --- a/account_invoice_update_wizard/wizard/account_invoice_update.py +++ b/account_invoice_update_wizard/wizard/account_invoice_update.py @@ -3,7 +3,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from odoo import models, fields, api, _ -from odoo.exceptions import Warning as UserError +from odoo.exceptions import UserError import odoo.addons.decimal_precision as dp From 9fa9ac201e6255ac46f45f94443cc368316c9edb Mon Sep 17 00:00:00 2001 From: Yannick Vaucher Date: Wed, 27 Jun 2018 18:43:39 +0200 Subject: [PATCH 6/9] Allow to change analytic fields with account_invoice_update --- account_invoice_update_wizard/__manifest__.py | 3 + .../tests/__init__.py | 1 + .../test_account_invoice_update_wizard.py | 198 ++++++++++++++++++ .../wizard/account_invoice_update.py | 93 +++++++- .../wizard/account_invoice_update_view.xml | 2 + 5 files changed, 293 insertions(+), 4 deletions(-) create mode 100644 account_invoice_update_wizard/tests/__init__.py create mode 100644 account_invoice_update_wizard/tests/test_account_invoice_update_wizard.py diff --git a/account_invoice_update_wizard/__manifest__.py b/account_invoice_update_wizard/__manifest__.py index 71e396be..eced67e1 100644 --- a/account_invoice_update_wizard/__manifest__.py +++ b/account_invoice_update_wizard/__manifest__.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- # © 2017 Akretion (Alexis de Lattre ) +# Copyright 2018 Camptocamp # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). { @@ -21,6 +22,8 @@ * Salesman * Notes * Description of invoice lines +* Analytic account +* Analytic tags """, 'author': 'Akretion', diff --git a/account_invoice_update_wizard/tests/__init__.py b/account_invoice_update_wizard/tests/__init__.py new file mode 100644 index 00000000..09720fbc --- /dev/null +++ b/account_invoice_update_wizard/tests/__init__.py @@ -0,0 +1 @@ +from . import test_account_invoice_update_wizard diff --git a/account_invoice_update_wizard/tests/test_account_invoice_update_wizard.py b/account_invoice_update_wizard/tests/test_account_invoice_update_wizard.py new file mode 100644 index 00000000..737f2660 --- /dev/null +++ b/account_invoice_update_wizard/tests/test_account_invoice_update_wizard.py @@ -0,0 +1,198 @@ +# -*- coding: utf-8 -*- +# Copyright 2018 Camptocamp +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo.tests.common import TransactionCase +from odoo.exceptions import UserError + + +class TestAccountInvoiceUpdateWizard(TransactionCase): + + def setUp(self): + super(TestAccountInvoiceUpdateWizard, self).setUp() + self.customer12 = self.env.ref('base.res_partner_12') + self.product16 = self.env.ref('product.product_product_16') + self.product24 = self.env.ref('product.product_product_24') + uom_unit = self.env.ref('product.product_uom_categ_unit') + + self.invoice1 = self.env['account.invoice'].create({ + 'name': 'Test invoice', + 'partner_id': self.customer12.id, + }) + self.inv_line1 = self.env['account.invoice.line'].create({ + 'invoice_id': self.invoice1.id, + 'name': "Line1", + 'product_id': self.product16.id, + 'product_uom_id': uom_unit.id, + 'account_id': self.invoice1.account_id.id, + 'price_unit': 42.0, + }) + self.inv_line2 = self.env['account.invoice.line'].create({ + 'invoice_id': self.invoice1.id, + 'name': "Line2", + 'product_id': self.product24.id, + 'product_uom_id': uom_unit.id, + 'account_id': self.invoice1.account_id.id, + 'price_unit': 1111.1, + }) + + self.aa1 = self.env.ref('analytic.analytic_partners_camp_to_camp') + self.aa2 = self.env.ref('analytic.analytic_nebula') + self.atag1 = self.env.ref('analytic.tag_contract') + self.atag2 = self.env['account.analytic.tag'].create({ + 'name': u'の', + }) + + def create_wizard(self): + UpdateWizard = self.env['account.invoice.update'].with_context( + active_model='account.invoice', + active_id=self.invoice1.id) + self.wiz = UpdateWizard.create({}) + + def test_add_analytic_account_line1(self): + """ Add analytic account on an invoice line + after the invoice has been approved. + + This will: + - update the move line + - create a new analytic line. + """ + self.invoice1.action_invoice_open() + self.create_wizard() + + wiz_line = self.wiz.line_ids.filtered( + 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) + 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 + after the invoice has been approved. + + This will: + - update the move line + - update the existing analytic line.""" + self.inv_line1.account_analytic_id = self.aa2 + + self.invoice1.action_invoice_open() + self.create_wizard() + + wiz_line = self.wiz.line_ids.filtered( + 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) + 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 + after the invoice has been approved where both + lines were grouped in the same move line. + + This will raise an error. + """ + self.invoice1.journal_id.group_invoice_lines = True + + self.inv_line2.product_id = self.product16 + self.inv_line2.unit_price = 42.0 + + self.invoice1.action_invoice_open() + self.create_wizard() + + line1 = self.wiz.line_ids[0] + line1.account_analytic_id = self.aa1 + with self.assertRaises(UserError): + self.wiz.run() + + def test_add_analytic_tags_line1(self): + """ Add analytic tags on an invoice line + after the invoice has been approved. + + This will update move line. + """ + self.invoice1.action_invoice_open() + self.create_wizard() + + wiz_line = self.wiz.line_ids.filtered( + 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) + 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 + after the invoice has been approved. + + It will update move line and analytic line + """ + self.inv_line1.account_analytic_id = self.aa2 + self.inv_line1.analytic_tag_ids = self.atag1 + + self.invoice1.action_invoice_open() + self.create_wizard() + + wiz_line = self.wiz.line_ids.filtered( + 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) + 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 + after the invoice has been approved. + + This will: + - update move line + - create an analytic line + """ + self.invoice1.action_invoice_open() + self.create_wizard() + + wiz_line = self.wiz.line_ids.filtered( + 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) + 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 + after the invoice has been approved. + + This will raise an error as it is not implemented. + """ + self.inv_line1.account_analytic_id = self.aa2 + + self.invoice1.action_invoice_open() + self.create_wizard() + + wiz_line = self.wiz.line_ids.filtered( + 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) + self.assertFalse(related_ml.analytic_account_id) + self.assertFalse(related_ml.analytic_line_ids) diff --git a/account_invoice_update_wizard/wizard/account_invoice_update.py b/account_invoice_update_wizard/wizard/account_invoice_update.py index 0de06485..3cca0ac7 100644 --- a/account_invoice_update_wizard/wizard/account_invoice_update.py +++ b/account_invoice_update_wizard/wizard/account_invoice_update.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- # © 2017 Akretion (Alexis de Lattre ) +# Copyright 2018 Camptocamp # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from odoo import models, fields, api, _ @@ -48,11 +49,15 @@ def _prepare_default_get(self, invoice): for m2ofield in self._m2o_fields2update(): res[m2ofield] = invoice[m2ofield].id or False 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, }]) return res @@ -110,6 +115,57 @@ def _prepare_move(self): mvals['ref'] = ref return mvals + @api.multi + def _get_matching_inv_line(self, move_line): + """ Find matching invoice line by product """ + # 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) + if len(inv_line) <> 1: + raise UserError( + "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) + + 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 + + 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)] + 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) + + 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 + + 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)] + return alvals + @api.multi def _update_payment_term_move(self): self.ensure_one() @@ -169,15 +225,40 @@ def run(self): if ivals: updated = True inv.write(ivals) + if inv.move_id: + mvals = self._prepare_move() + if mvals: + inv.move_id.write(mvals) + for ml in inv.move_id.line_ids: + if ml.credit == 0.0: + continue + inv_line = self._get_matching_inv_line(ml) + mlvals = self._prepare_move_line(inv_line) + if mlvals: + updated = True + ml.write(mlvals) + aalines = ml.analytic_line_ids + alvals = self._prepare_analytic_line(inv_line) + if aalines and alvals: + updated = True + 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) + # remove existing analytic line + to_remove_aalines.unlink() + else: + aalines.write(alvals) + elif 'account_id' in alvals: + # Create analytic lines if analytic account + # is added later + ml.create_analytic_lines() for line in self.line_ids: ilvals = self._prepare_invoice_line(line) if ilvals: updated = True line.invoice_line_id.write(ilvals) - if inv.move_id: - mvals = self._prepare_move() - if mvals: - inv.move_id.write(mvals) if updated: inv.message_post(_( 'Non-legal fields of invoice updated via the Invoice Update ' @@ -199,3 +280,7 @@ class AccountInvoiceLineUpdate(models.TransientModel): readonly=True) price_subtotal = fields.Float( 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') diff --git a/account_invoice_update_wizard/wizard/account_invoice_update_view.xml b/account_invoice_update_wizard/wizard/account_invoice_update_view.xml index 1eab7c7a..a9f0824d 100644 --- a/account_invoice_update_wizard/wizard/account_invoice_update_view.xml +++ b/account_invoice_update_wizard/wizard/account_invoice_update_view.xml @@ -30,6 +30,8 @@ + + From 0b236ae21f76576dc0e24185388c06347a03d89b Mon Sep 17 00:00:00 2001 From: mpanarin Date: Mon, 13 Aug 2018 15:34:39 +0300 Subject: [PATCH 7/9] [FIX] not being able to change analytic account and tags --- .../wizard/account_invoice_update.py | 28 +++++++++++++++++-- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/account_invoice_update_wizard/wizard/account_invoice_update.py b/account_invoice_update_wizard/wizard/account_invoice_update.py index 3cca0ac7..6e0022b9 100644 --- a/account_invoice_update_wizard/wizard/account_invoice_update.py +++ b/account_invoice_update_wizard/wizard/account_invoice_update.py @@ -98,11 +98,30 @@ def _prepare_invoice(self): vals['date_due'] = max(line[0] for line in pterm_list) return vals + @api.model + def _line_simple_fields2update(self): + return ["name",] + + @api.model + def _line_m2o_fields2update(self): + return ["account_analytic_id",] + + @api.model + def _line_m2m_fields2update(self): + return ["analytic_tag_ids",] + @api.model def _prepare_invoice_line(self, line): vals = {} - if line.name != line.invoice_line_id.name: - vals['name'] = line.name + for field in self._line_simple_fields2update(): + if line[field] != line.invoice_line_id[field]: + vals[field] = line[field] + for field in self._line_m2o_fields2update(): + if line[field] != line.invoice_line_id[field]: + vals[field] = line[field].id + for field in self._line_m2m_fields2update(): + if line[field] != line.invoice_line_id[field]: + vals[field] = [(6, 0, line[field].ids)] return vals @api.multi @@ -229,7 +248,10 @@ def run(self): mvals = self._prepare_move() if mvals: inv.move_id.write(mvals) - for ml in inv.move_id.line_ids: + 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) + ): if ml.credit == 0.0: continue inv_line = self._get_matching_inv_line(ml) From 619facb56664598994094d052632d968cebd09a7 Mon Sep 17 00:00:00 2001 From: Artem Kostyuk Date: Wed, 10 Oct 2018 18:23:35 +0300 Subject: [PATCH 8/9] [11.0][MIG] account_invoice_update_wizard --- account_invoice_update_wizard/__manifest__.py | 14 +++++++------- .../tests/test_account_invoice_update_wizard.py | 3 +-- .../wizard/account_invoice_update.py | 9 ++++----- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/account_invoice_update_wizard/__manifest__.py b/account_invoice_update_wizard/__manifest__.py index eced67e1..f0414029 100644 --- a/account_invoice_update_wizard/__manifest__.py +++ b/account_invoice_update_wizard/__manifest__.py @@ -1,11 +1,10 @@ -# -*- coding: utf-8 -*- -# © 2017 Akretion (Alexis de Lattre ) +# Copyright 2017 Akretion (Alexis de Lattre ) # Copyright 2018 Camptocamp -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). { 'name': 'Account Invoice Update Wizard', - 'version': '8.0.1.0.0', + 'version': '11.0.1.0.0', 'category': 'Accounting & Finance', 'license': 'AGPL-3', 'summary': 'Wizard to update non-legal fields of an open/paid invoice', @@ -28,10 +27,11 @@ """, 'author': 'Akretion', 'website': 'http://www.akretion.com', - 'depends': ['account'], + 'depends': [ + 'account', + ], 'data': [ 'wizard/account_invoice_update_view.xml', 'views/account_invoice.xml', - ], - 'installable': True, + ], } diff --git a/account_invoice_update_wizard/tests/test_account_invoice_update_wizard.py b/account_invoice_update_wizard/tests/test_account_invoice_update_wizard.py index 737f2660..29054007 100644 --- a/account_invoice_update_wizard/tests/test_account_invoice_update_wizard.py +++ b/account_invoice_update_wizard/tests/test_account_invoice_update_wizard.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2018 Camptocamp # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). @@ -40,7 +39,7 @@ def setUp(self): self.aa2 = self.env.ref('analytic.analytic_nebula') self.atag1 = self.env.ref('analytic.tag_contract') self.atag2 = self.env['account.analytic.tag'].create({ - 'name': u'の', + 'name': 'の', }) def create_wizard(self): diff --git a/account_invoice_update_wizard/wizard/account_invoice_update.py b/account_invoice_update_wizard/wizard/account_invoice_update.py index 6e0022b9..8c2ca9d2 100644 --- a/account_invoice_update_wizard/wizard/account_invoice_update.py +++ b/account_invoice_update_wizard/wizard/account_invoice_update.py @@ -1,5 +1,4 @@ -# -*- coding: utf-8 -*- -# © 2017 Akretion (Alexis de Lattre ) +# Copyright 2017 Akretion (Alexis de Lattre ) # Copyright 2018 Camptocamp # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). @@ -58,7 +57,7 @@ def _prepare_default_get(self, invoice): 'price_subtotal': line.price_subtotal, 'account_analytic_id': line.account_analytic_id.id, 'analytic_tag_ids': aa_tags, - }]) + }]) return res @api.model @@ -141,7 +140,7 @@ def _get_matching_inv_line(self, move_line): # 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) - if len(inv_line) <> 1: + if len(inv_line) != 1: raise UserError( "Cannot match a single invoice line to move line %s" % move_line.name) @@ -221,7 +220,7 @@ def _update_payment_term_move(self): mlines[amount].append(line) else: mlines[amount] = [line] - for iamount, lines in mlines.iteritems(): + 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 " From b2e50ce0d61bddb204d925e43a7660b06414fe01 Mon Sep 17 00:00:00 2001 From: Artem Kostyuk Date: Fri, 26 Oct 2018 17:46:54 +0300 Subject: [PATCH 9/9] fixup! [11.0][MIG] account_invoice_update_wizard --- account_invoice_update_wizard/README.rst | 39 ++++++++++++++ account_invoice_update_wizard/__manifest__.py | 19 +------ .../test_account_invoice_update_wizard.py | 51 ++++++++++--------- .../views/account_invoice.xml | 4 +- .../wizard/account_invoice_update.py | 2 +- .../wizard/account_invoice_update_view.xml | 4 +- 6 files changed, 73 insertions(+), 46 deletions(-) create mode 100644 account_invoice_update_wizard/README.rst diff --git a/account_invoice_update_wizard/README.rst b/account_invoice_update_wizard/README.rst new file mode 100644 index 00000000..c8355021 --- /dev/null +++ b/account_invoice_update_wizard/README.rst @@ -0,0 +1,39 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: https://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 + +Account Invoice Update Wizard +============================= + +This module adds a button *Update Invoice* on Customer and Supplier invoices in +Open or Paid state. This button starts a wizard which allows the user to update +non-legal fields of the invoice: + +* Source Document +* Reference/Description +* Payment terms (update allowed only to a payment term with same number of terms + of the same amount and on invoices without any payment) +* Bank Account +* Salesman +* Notes +* Description of invoice lines +* Analytic account +* Analytic tags + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues +`_. In case of trouble, please +check there if your issue has already been reported. If you spotted it first, +help us smash it by providing detailed and welcomed feedback. + +Contributors +------------ + +* Alexis de Lattre +* Florian da Costa +* Matthieu Dietrich +* Yannick Vaucher +* Mykhailo Panarin +* Artem Kostyuk diff --git a/account_invoice_update_wizard/__manifest__.py b/account_invoice_update_wizard/__manifest__.py index f0414029..8daaec65 100644 --- a/account_invoice_update_wizard/__manifest__.py +++ b/account_invoice_update_wizard/__manifest__.py @@ -8,25 +8,8 @@ 'category': 'Accounting & Finance', 'license': 'AGPL-3', 'summary': 'Wizard to update non-legal fields of an open/paid invoice', - 'description': """ -Account Invoice Update Wizard -============================= - -This module adds a button *Update Invoice* on Customer and Supplier invoices in Open or Paid state. This button starts a wizard which allows the user to update non-legal fields of the invoice: - -* Source Document -* Reference/Description -* Payment terms (update allowed only to a payment term with same number of terms of the same amount and on invoices without any payment) -* Bank Account -* Salesman -* Notes -* Description of invoice lines -* Analytic account -* Analytic tags - - """, 'author': 'Akretion', - 'website': 'http://www.akretion.com', + 'website': 'http://github.com/akretion/odoo-usability', 'depends': [ 'account', ], diff --git a/account_invoice_update_wizard/tests/test_account_invoice_update_wizard.py b/account_invoice_update_wizard/tests/test_account_invoice_update_wizard.py index 29054007..59e36df4 100644 --- a/account_invoice_update_wizard/tests/test_account_invoice_update_wizard.py +++ b/account_invoice_update_wizard/tests/test_account_invoice_update_wizard.py @@ -1,44 +1,49 @@ # Copyright 2018 Camptocamp -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +# License AGPL-3.0 or later (https://gnu.org/licenses/agpl). -from odoo.tests.common import TransactionCase +from odoo.tests.common import SavepointCase from odoo.exceptions import UserError -class TestAccountInvoiceUpdateWizard(TransactionCase): +class TestAccountInvoiceUpdateWizard(SavepointCase): - def setUp(self): - super(TestAccountInvoiceUpdateWizard, self).setUp() - self.customer12 = self.env.ref('base.res_partner_12') - self.product16 = self.env.ref('product.product_product_16') - self.product24 = self.env.ref('product.product_product_24') - uom_unit = self.env.ref('product.product_uom_categ_unit') + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.env = cls.env(context=dict( + cls.env.context, + tracking_disable=True, + )) + 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('product.product_uom_categ_unit') - self.invoice1 = self.env['account.invoice'].create({ + cls.invoice1 = cls.env['account.invoice'].create({ 'name': 'Test invoice', - 'partner_id': self.customer12.id, + 'partner_id': cls.customer12.id, }) - self.inv_line1 = self.env['account.invoice.line'].create({ - 'invoice_id': self.invoice1.id, + cls.inv_line1 = cls.env['account.invoice.line'].create({ + 'invoice_id': cls.invoice1.id, 'name': "Line1", - 'product_id': self.product16.id, + 'product_id': cls.product16.id, 'product_uom_id': uom_unit.id, - 'account_id': self.invoice1.account_id.id, + 'account_id': cls.invoice1.account_id.id, 'price_unit': 42.0, }) - self.inv_line2 = self.env['account.invoice.line'].create({ - 'invoice_id': self.invoice1.id, + cls.inv_line2 = cls.env['account.invoice.line'].create({ + 'invoice_id': cls.invoice1.id, 'name': "Line2", - 'product_id': self.product24.id, + 'product_id': cls.product24.id, 'product_uom_id': uom_unit.id, - 'account_id': self.invoice1.account_id.id, + 'account_id': cls.invoice1.account_id.id, 'price_unit': 1111.1, }) - self.aa1 = self.env.ref('analytic.analytic_partners_camp_to_camp') - self.aa2 = self.env.ref('analytic.analytic_nebula') - self.atag1 = self.env.ref('analytic.tag_contract') - self.atag2 = self.env['account.analytic.tag'].create({ + 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': 'の', }) diff --git a/account_invoice_update_wizard/views/account_invoice.xml b/account_invoice_update_wizard/views/account_invoice.xml index 312f7d7e..99efe666 100644 --- a/account_invoice_update_wizard/views/account_invoice.xml +++ b/account_invoice_update_wizard/views/account_invoice.xml @@ -1,7 +1,7 @@ diff --git a/account_invoice_update_wizard/wizard/account_invoice_update.py b/account_invoice_update_wizard/wizard/account_invoice_update.py index 8c2ca9d2..89c56c9d 100644 --- a/account_invoice_update_wizard/wizard/account_invoice_update.py +++ b/account_invoice_update_wizard/wizard/account_invoice_update.py @@ -1,6 +1,6 @@ # Copyright 2017 Akretion (Alexis de Lattre ) # Copyright 2018 Camptocamp -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +# License AGPL-3.0 or later (https://gnu.org/licenses/agpl). from odoo import models, fields, api, _ from odoo.exceptions import UserError diff --git a/account_invoice_update_wizard/wizard/account_invoice_update_view.xml b/account_invoice_update_wizard/wizard/account_invoice_update_view.xml index a9f0824d..f5d21275 100644 --- a/account_invoice_update_wizard/wizard/account_invoice_update_view.xml +++ b/account_invoice_update_wizard/wizard/account_invoice_update_view.xml @@ -1,7 +1,7 @@