diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 213afc8..3ba7925 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,7 +1,29 @@ - exclude: | (?x) # NOT INSTALLABLE ADDONS + ^account_fiscal_position_payable_receivable/| + ^account_invoice_margin/| + ^account_invoice_update_wizard/| + ^company_code/| + ^developer_menu/| + ^intrastat_product_type/| + ^mass_mailing_usability/| + ^mrp_average_cost/| + ^mrp_no_product_template_menu/| + ^mrp_product_tree_default/| + ^sale_down_payment/| + ^sale_margin_no_onchange/| + ^sale_no_configurator_button/| + ^sale_quotation_title/| + ^service_line_qty_update_base/| + ^service_line_qty_update_purchase/| + ^service_line_qty_update_sale/| + ^stock_no_product_template_menu/| + ^stock_user_default_warehouse_base/| + ^stock_user_default_warehouse_mrp/| + ^stock_user_default_warehouse_purchase/| + ^stock_user_default_warehouse_sale/| + ^volume_precision/| # END NOT INSTALLABLE ADDONS # Files and folders generated by bots, to avoid loops ^setup/|/static/description/index\.html$| diff --git a/account_fiscal_position_payable_receivable/__manifest__.py b/account_fiscal_position_payable_receivable/__manifest__.py index 88dbc65..5f77e21 100644 --- a/account_fiscal_position_payable_receivable/__manifest__.py +++ b/account_fiscal_position_payable_receivable/__manifest__.py @@ -16,7 +16,7 @@ This module allows to configure a special *Partner Receivable Account* and a spe This module has been written by Alexis de Lattre from Akretion . """, "author": "Akretion", - "website": "http://www.akretion.com", + "website": "https://github.com/OCA/odoo-usability", "depends": ["account"], "data": ["views/account_fiscal_position_view.xml"], "installable": False, diff --git a/account_fiscal_position_payable_receivable/models/account_fiscal_position.py b/account_fiscal_position_payable_receivable/models/account_fiscal_position.py index eb549da..b1caea9 100644 --- a/account_fiscal_position_payable_receivable/models/account_fiscal_position.py +++ b/account_fiscal_position_payable_receivable/models/account_fiscal_position.py @@ -1,7 +1,7 @@ # © 2016-2017 Akretion (Alexis de Lattre ) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, fields +from odoo import fields, models class AccountFiscalPosition(models.Model): diff --git a/account_fiscal_position_payable_receivable/models/res_partner.py b/account_fiscal_position_payable_receivable/models/res_partner.py index e76a0e0..1a9e53f 100644 --- a/account_fiscal_position_payable_receivable/models/res_partner.py +++ b/account_fiscal_position_payable_receivable/models/res_partner.py @@ -1,7 +1,7 @@ # © 2016-2017 Akretion (Alexis de Lattre ) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, api +from odoo import api, models class ResPartner(models.Model): diff --git a/account_fiscal_position_payable_receivable/views/account_fiscal_position_view.xml b/account_fiscal_position_payable_receivable/views/account_fiscal_position_view.xml index 45321f1..93bb028 100644 --- a/account_fiscal_position_payable_receivable/views/account_fiscal_position_view.xml +++ b/account_fiscal_position_payable_receivable/views/account_fiscal_position_view.xml @@ -1,9 +1,8 @@ - + - @@ -13,8 +12,8 @@ - - + + diff --git a/account_invoice_margin/__manifest__.py b/account_invoice_margin/__manifest__.py index 8911105..014a7d6 100644 --- a/account_invoice_margin/__manifest__.py +++ b/account_invoice_margin/__manifest__.py @@ -3,22 +3,22 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). { - 'name': 'Account Invoice Margin', - 'version': '12.0.1.0.0', - 'category': 'Invoicing Management', - 'license': 'AGPL-3', - 'summary': 'Copy standard price on invoice line and compute margins', - 'description': """ + "name": "Account Invoice Margin", + "version": "12.0.1.0.0", + "category": "Invoicing Management", + "license": "AGPL-3", + "summary": "Copy standard price on invoice line and compute margins", + "description": """ This module copies the field *standard_price* of the product on the invoice line when the invoice line is created. The allows the computation of the margin of the invoice. This module has been written by Alexis de Lattre from Akretion . """, - 'author': 'Akretion', - 'website': 'http://www.akretion.com', - 'depends': ['account'], - 'data': [ - 'account_invoice_view.xml', + "author": "Akretion", + "website": "https://github.com/OCA/odoo-usability", + "depends": ["account"], + "data": [ + "account_invoice_view.xml", ], - 'installable': False, + "installable": False, } diff --git a/account_invoice_margin/account_invoice.py b/account_invoice_margin/account_invoice.py index e42cac9..2ba0458 100644 --- a/account_invoice_margin/account_invoice.py +++ b/account_invoice_margin/account_invoice.py @@ -3,39 +3,62 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from odoo import api, fields, models + import odoo.addons.decimal_precision as dp class AccountInvoiceLine(models.Model): - _inherit = 'account.invoice.line' + _inherit = "account.invoice.line" standard_price_company_currency = fields.Float( - string='Cost Price in Company Currency', readonly=True, - digits=dp.get_precision('Product Price'), + string="Cost Price in Company Currency", + readonly=True, + digits=dp.get_precision("Product Price"), help="Cost price in company currency in the unit of measure " "of the invoice line (which may be different from the unit " - "of measure of the product).") + "of measure of the product).", + ) standard_price_invoice_currency = fields.Float( - string='Cost Price in Invoice Currency', readonly=True, - compute='_compute_margin', store=True, - digits=dp.get_precision('Product Price'), + string="Cost Price in Invoice Currency", + readonly=True, + compute="_compute_margin", + store=True, + digits=dp.get_precision("Product Price"), help="Cost price in invoice currency in the unit of measure " - "of the invoice line") + "of the invoice line", + ) margin_invoice_currency = fields.Monetary( - string='Margin in Invoice Currency', readonly=True, store=True, - compute='_compute_margin', currency_field='currency_id') + string="Margin in Invoice Currency", + readonly=True, + store=True, + compute="_compute_margin", + currency_field="currency_id", + ) margin_company_currency = fields.Monetary( - string='Margin in Company Currency', readonly=True, store=True, - compute='_compute_margin', currency_field='company_currency_id') + string="Margin in Company Currency", + readonly=True, + store=True, + compute="_compute_margin", + currency_field="company_currency_id", + ) margin_rate = fields.Float( - string="Margin Rate", readonly=True, store=True, - compute='_compute_margin', - digits=(16, 2), help="Margin rate in percentage of the sale price") + string="Margin Rate", + readonly=True, + store=True, + compute="_compute_margin", + digits=(16, 2), + help="Margin rate in percentage of the sale price", + ) @api.depends( - 'standard_price_company_currency', 'invoice_id.currency_id', - 'invoice_id.type', 'invoice_id.company_id', - 'invoice_id.date_invoice', 'quantity', 'price_subtotal') + "standard_price_company_currency", + "invoice_id.currency_id", + "invoice_id.type", + "invoice_id.company_id", + "invoice_id.date_invoice", + "quantity", + "price_subtotal", + ) def _compute_margin(self): for il in self: standard_price_inv_cur = 0.0 @@ -43,27 +66,27 @@ class AccountInvoiceLine(models.Model): margin_comp_cur = 0.0 margin_rate = 0.0 inv = il.invoice_id - if inv and inv.type in ('out_invoice', 'out_refund'): + if inv and inv.type in ("out_invoice", "out_refund"): # it works in _get_current_rate # even if we set date = False in context # standard_price_inv_cur is in the UoM of the invoice line - date = inv._get_currency_rate_date() or\ - fields.Date.context_today(self) + date = inv._get_currency_rate_date() or fields.Date.context_today(self) company = inv.company_id company_currency = company.currency_id - standard_price_inv_cur =\ - company_currency._convert( - il.standard_price_company_currency, - inv.currency_id, company, date) - margin_inv_cur =\ + standard_price_inv_cur = company_currency._convert( + il.standard_price_company_currency, inv.currency_id, company, date + ) + margin_inv_cur = ( il.price_subtotal - il.quantity * standard_price_inv_cur + ) margin_comp_cur = inv.currency_id._convert( - margin_inv_cur, company_currency, company, date) + margin_inv_cur, company_currency, company, date + ) if il.price_subtotal: margin_rate = 100 * margin_inv_cur / il.price_subtotal # for a refund, margin should be negative # but margin rate should stay positive - if inv.type == 'out_refund': + if inv.type == "out_refund": margin_inv_cur *= -1 margin_comp_cur *= -1 il.standard_price_invoice_currency = standard_price_inv_cur @@ -79,35 +102,32 @@ class AccountInvoiceLine(models.Model): # because we don't have access to the 'type' of the invoice @api.model def create(self, vals): - if vals.get('product_id'): - pp = self.env['product.product'].browse(vals['product_id']) + if vals.get("product_id"): + pp = self.env["product.product"].browse(vals["product_id"]) std_price = pp.standard_price - inv_uom_id = vals.get('uom_id') + inv_uom_id = vals.get("uom_id") if inv_uom_id and inv_uom_id != pp.uom_id.id: - inv_uom = self.env['uom.uom'].browse(inv_uom_id) - std_price = pp.uom_id._compute_price( - std_price, inv_uom) - vals['standard_price_company_currency'] = std_price + inv_uom = self.env["uom.uom"].browse(inv_uom_id) + std_price = pp.uom_id._compute_price(std_price, inv_uom) + vals["standard_price_company_currency"] = std_price return super(AccountInvoiceLine, self).create(vals) def write(self, vals): if not vals: vals = {} - if 'product_id' in vals or 'uom_id' in vals: + if "product_id" in vals or "uom_id" in vals: for il in self: - if 'product_id' in vals: - if vals.get('product_id'): - pp = self.env['product.product'].browse( - vals['product_id']) + if "product_id" in vals: + if vals.get("product_id"): + pp = self.env["product.product"].browse(vals["product_id"]) else: pp = False else: pp = il.product_id or False # uom_id is NOT a required field - if 'uom_id' in vals: - if vals.get('uom_id'): - inv_uom = self.env['uom.uom'].browse( - vals['uom_id']) + if "uom_id" in vals: + if vals.get("uom_id"): + inv_uom = self.env["uom.uom"].browse(vals["uom_id"]) else: inv_uom = False else: @@ -116,37 +136,43 @@ class AccountInvoiceLine(models.Model): if pp: std_price = pp.standard_price if inv_uom and inv_uom != pp.uom_id: - std_price = pp.uom_id._compute_price( - std_price, inv_uom) - il.write({'standard_price_company_currency': std_price}) + std_price = pp.uom_id._compute_price(std_price, inv_uom) + il.write({"standard_price_company_currency": std_price}) return super(AccountInvoiceLine, self).write(vals) class AccountInvoice(models.Model): - _inherit = 'account.invoice' + _inherit = "account.invoice" margin_invoice_currency = fields.Monetary( - string='Margin in Invoice Currency', - compute='_compute_margin', store=True, readonly=True, - currency_field='currency_id') + string="Margin in Invoice Currency", + compute="_compute_margin", + store=True, + readonly=True, + currency_field="currency_id", + ) margin_company_currency = fields.Monetary( - string='Margin in Company Currency', - compute='_compute_margin', store=True, readonly=True, - currency_field='company_currency_id') + string="Margin in Company Currency", + compute="_compute_margin", + store=True, + readonly=True, + currency_field="company_currency_id", + ) @api.depends( - 'type', - 'invoice_line_ids.margin_invoice_currency', - 'invoice_line_ids.margin_company_currency') + "type", + "invoice_line_ids.margin_invoice_currency", + "invoice_line_ids.margin_company_currency", + ) def _compute_margin(self): - res = self.env['account.invoice.line'].read_group( - [('invoice_id', 'in', self.ids)], - ['invoice_id', 'margin_invoice_currency', - 'margin_company_currency'], - ['invoice_id']) + res = self.env["account.invoice.line"].read_group( + [("invoice_id", "in", self.ids)], + ["invoice_id", "margin_invoice_currency", "margin_company_currency"], + ["invoice_id"], + ) for re in res: - if re['invoice_id']: - inv = self.browse(re['invoice_id'][0]) - if inv.type in ('out_invoice', 'out_refund'): - inv.margin_invoice_currency = re['margin_invoice_currency'] - inv.margin_company_currency = re['margin_company_currency'] + if re["invoice_id"]: + inv = self.browse(re["invoice_id"][0]) + if inv.type in ("out_invoice", "out_refund"): + inv.margin_invoice_currency = re["margin_invoice_currency"] + inv.margin_company_currency = re["margin_company_currency"] diff --git a/account_invoice_margin/account_invoice_report.py b/account_invoice_margin/account_invoice_report.py index 5fcf9d8..1e490c4 100644 --- a/account_invoice_margin/account_invoice_report.py +++ b/account_invoice_margin/account_invoice_report.py @@ -6,47 +6,70 @@ from odoo import api, fields, models class AccountInvoiceReport(models.Model): - _inherit = 'account.invoice.report' + _inherit = "account.invoice.report" - margin = fields.Float(string='Margin', readonly=True) + margin = fields.Float(string="Margin", readonly=True) # why digits=0 ??? Why is it like that in the native "account" module user_currency_margin = fields.Float( - string="Margin", compute='_compute_user_currency_margin', digits=0) + string="Margin", compute="_compute_user_currency_margin", digits=0 + ) _depends = { - 'account.invoice': [ - 'account_id', 'amount_total_company_signed', - 'commercial_partner_id', 'company_id', - 'currency_id', 'date_due', 'date_invoice', 'fiscal_position_id', - 'journal_id', 'number', 'partner_bank_id', 'partner_id', - 'payment_term_id', 'residual', 'state', 'type', 'user_id', + "account.invoice": [ + "account_id", + "amount_total_company_signed", + "commercial_partner_id", + "company_id", + "currency_id", + "date_due", + "date_invoice", + "fiscal_position_id", + "journal_id", + "number", + "partner_bank_id", + "partner_id", + "payment_term_id", + "residual", + "state", + "type", + "user_id", ], - 'account.invoice.line': [ - 'account_id', 'invoice_id', 'price_subtotal', 'product_id', - 'quantity', 'uom_id', 'account_analytic_id', - 'margin_company_currency', + "account.invoice.line": [ + "account_id", + "invoice_id", + "price_subtotal", + "product_id", + "quantity", + "uom_id", + "account_analytic_id", + "margin_company_currency", ], - 'product.product': ['product_tmpl_id'], - 'product.template': ['categ_id'], - 'uom.uom': ['category_id', 'factor', 'name', 'uom_type'], - 'res.currency.rate': ['currency_id', 'name'], - 'res.partner': ['country_id'], + "product.product": ["product_tmpl_id"], + "product.template": ["categ_id"], + "uom.uom": ["category_id", "factor", "name", "uom_type"], + "res.currency.rate": ["currency_id", "name"], + "res.partner": ["country_id"], } - @api.depends('currency_id', 'date', 'margin') + @api.depends("currency_id", "date", "margin") def _compute_user_currency_margin(self): user_currency = self.env.user.company_id.currency_id - currency_rate = self.env['res.currency.rate'].search([ - ('rate', '=', 1), - '|', - ('company_id', '=', self.env.user.company_id.id), - ('company_id', '=', False)], limit=1) + currency_rate = self.env["res.currency.rate"].search( + [ + ("rate", "=", 1), + "|", + ("company_id", "=", self.env.user.company_id.id), + ("company_id", "=", False), + ], + limit=1, + ) base_currency = currency_rate.currency_id for record in self: date = record.date or fields.Date.today() company = record.company_id record.user_currency_margin = base_currency._convert( - record.margin, user_currency, company, date) + record.margin, user_currency, company, date + ) # TODO check for refunds def _sub_select(self): diff --git a/account_invoice_margin/account_invoice_view.xml b/account_invoice_margin/account_invoice_view.xml index d8f9f35..c8c8461 100644 --- a/account_invoice_margin/account_invoice_view.xml +++ b/account_invoice_margin/account_invoice_view.xml @@ -1,33 +1,40 @@ - + - margin.account.invoice.line.form account.invoice.line - + - - - - - @@ -36,13 +43,19 @@ margin.account.invoice.form account.invoice - + - - + + diff --git a/account_invoice_update_wizard/__manifest__.py b/account_invoice_update_wizard/__manifest__.py index e5cf433..64cf7fd 100644 --- a/account_invoice_update_wizard/__manifest__.py +++ b/account_invoice_update_wizard/__manifest__.py @@ -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, } diff --git a/account_invoice_update_wizard/models/account_invoice.py b/account_invoice_update_wizard/models/account_invoice.py index 04a285e..9e4ef0a 100644 --- a/account_invoice_update_wizard/models/account_invoice.py +++ b/account_invoice_update_wizard/models/account_invoice.py @@ -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 - 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 ec75d48..48caf4c 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,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) diff --git a/account_invoice_update_wizard/views/account_invoice.xml b/account_invoice_update_wizard/views/account_invoice.xml index ad44634..1a97f2c 100644 --- a/account_invoice_update_wizard/views/account_invoice.xml +++ b/account_invoice_update_wizard/views/account_invoice.xml @@ -1,27 +1,38 @@ - + - account.invoice - + account.invoice - + diff --git a/account_invoice_update_wizard/wizard/account_invoice_update.py b/account_invoice_update_wizard/wizard/account_invoice_update.py index e3f8f58..3fa47c6 100644 --- a/account_invoice_update_wizard/wizard/account_invoice_update.py +++ b/account_invoice_update_wizard/wizard/account_invoice_update.py @@ -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") 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 78eb2bb..467b2ca 100644 --- a/account_invoice_update_wizard/wizard/account_invoice_update_view.xml +++ b/account_invoice_update_wizard/wizard/account_invoice_update_view.xml @@ -1,9 +1,8 @@ - + - @@ -11,34 +10,62 @@
- - - - - - - - - - - + + + + + + + + + + + - - - - - - - - + + + + + + + +
-
diff --git a/account_usability/__manifest__.py b/account_usability/__manifest__.py index 0aa42fe..27c43de 100644 --- a/account_usability/__manifest__.py +++ b/account_usability/__manifest__.py @@ -3,36 +3,36 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). { - 'name': 'Account Usability', - 'version': '14.0.1.0.0', - 'category': 'Accounting & Finance', - 'license': 'AGPL-3', - 'summary': 'Small usability enhancements in account module', - 'author': 'Akretion', - 'website': 'http://www.akretion.com', - 'depends': [ - 'account', - 'base_view_inheritance_extension', - 'base_usability', # needed only to access base_usability.group_nobody - # in v12, I may create a module only for group_nobody - ], - 'data': [ - 'views/account_account_type.xml', - 'views/account_account.xml', - 'views/account_bank_statement.xml', - 'views/account_invoice_report.xml', - 'views/account_journal.xml', - 'views/account_move.xml', - 'views/account_menu.xml', - 'views/account_tax.xml', - 'views/product.xml', - 'views/res_config_settings.xml', - 'views/res_partner.xml', - 'views/account_report.xml', - 'wizard/account_invoice_mark_sent_view.xml', - 'wizard/account_group_generate_view.xml', - 'wizard/account_payment_register_views.xml', - 'security/ir.model.access.csv', - ], - 'installable': True, + "name": "Account Usability", + "version": "14.0.1.0.0", + "category": "Accounting & Finance", + "license": "AGPL-3", + "summary": "Small usability enhancements in account module", + "author": "Akretion", + "website": "https://github.com/OCA/odoo-usability", + "depends": [ + "account", + "base_view_inheritance_extension", + "base_usability", # needed only to access base_usability.group_nobody + # in v12, I may create a module only for group_nobody + ], + "data": [ + "views/account_account_type.xml", + "views/account_account.xml", + "views/account_bank_statement.xml", + "views/account_invoice_report.xml", + "views/account_journal.xml", + "views/account_move.xml", + "views/account_menu.xml", + "views/account_tax.xml", + "views/product.xml", + "views/res_config_settings.xml", + "views/res_partner.xml", + "views/account_report.xml", + "wizard/account_invoice_mark_sent_view.xml", + "wizard/account_group_generate_view.xml", + "wizard/account_payment_register_views.xml", + "security/ir.model.access.csv", + ], + "installable": True, } diff --git a/account_usability/invoice_register_payment_writeoff_analytic.diff b/account_usability/invoice_register_payment_writeoff_analytic.diff index 345339d..2cdf512 100644 --- a/account_usability/invoice_register_payment_writeoff_analytic.diff +++ b/account_usability/invoice_register_payment_writeoff_analytic.diff @@ -9,7 +9,7 @@ index 2dd1f9cef83..62275fca65e 100644 + 'analytic_account_id': write_off_line_vals.get('analytic_account_id'), }) return line_vals_list - + @@ -699,6 +700,7 @@ class AccountPayment(models.Model): 'name': writeoff_lines[0].name, 'amount': writeoff_amount * sign, @@ -29,7 +29,7 @@ index 3fc91f716ad..35636774c7e 100644 + writeoff_analytic_account_id = fields.Many2one('account.analytic.account', string="Difference Analytic Account", copy=False, domain="[('company_id', '=', company_id)]") writeoff_label = fields.Char(string='Journal Item Label', default='Write-Off', help='Change label of the counterpart that will hold the payment difference') - + @@ -422,6 +423,7 @@ class AccountPaymentRegister(models.TransientModel): 'name': self.writeoff_label, 'amount': self.payment_difference, @@ -37,7 +37,7 @@ index 3fc91f716ad..35636774c7e 100644 + 'analytic_account_id': self.writeoff_analytic_account_id.id or False, } return payment_vals - + diff --git a/addons/account/wizard/account_payment_register_views.xml b/addons/account/wizard/account_payment_register_views.xml index 16eec30e265..b9386567baa 100644 --- a/addons/account/wizard/account_payment_register_views.xml diff --git a/account_usability/models/account_account.py b/account_usability/models/account_account.py index 8b783ec..d672d77 100644 --- a/account_usability/models/account_account.py +++ b/account_usability/models/account_account.py @@ -2,18 +2,19 @@ # @author Alexis de Lattre # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import api, models import logging +from odoo import api, models + logger = logging.getLogger(__name__) class AccountAccount(models.Model): - _inherit = 'account.account' + _inherit = "account.account" - @api.depends('name', 'code') + @api.depends("name", "code") def name_get(self): - if self._context.get('account_account_show_code_only'): + if self._context.get("account_account_show_code_only"): res = [] for record in self: res.append((record.id, record.code)) @@ -24,36 +25,42 @@ class AccountAccount(models.Model): # https://github.com/odoo/odoo/issues/23040 # TODO mig to v14 ? def fix_bank_account_types(self): - aao = self.env['account.account'] - companies = self.env['res.company'].search([]) + aao = self.env["account.account"] + companies = self.env["res.company"].search([]) if len(companies) > 1: self = self.sudo() logger.info("START the script 'fix bank and cash account types'") - bank_type = self.env.ref('account.data_account_type_liquidity') - asset_type = self.env.ref('account.data_account_type_current_assets') - journals = self.env['account.journal'].search( - [('type', 'in', ('bank', 'cash'))], order='company_id') + bank_type = self.env.ref("account.data_account_type_liquidity") + asset_type = self.env.ref("account.data_account_type_current_assets") + journals = self.env["account.journal"].search( + [("type", "in", ("bank", "cash"))], order="company_id" + ) journal_accounts_bank_type = aao for journal in journals: for account in [ - journal.default_credit_account_id, - journal.default_debit_account_id]: + journal.default_credit_account_id, + journal.default_debit_account_id, + ]: if account: if account.user_type_id != bank_type: account.user_type_id = bank_type.id logger.info( - 'Company %s: Account %s updated to Bank ' - 'and Cash type', - account.company_id.display_name, account.code) + "Company %s: Account %s updated to Bank " "and Cash type", + account.company_id.display_name, + account.code, + ) if account not in journal_accounts_bank_type: journal_accounts_bank_type += account - accounts = aao.search([ - ('user_type_id', '=', bank_type.id)], order='company_id, code') + accounts = aao.search( + [("user_type_id", "=", bank_type.id)], order="company_id, code" + ) for account in accounts: if account not in journal_accounts_bank_type: account.user_type_id = asset_type.id logger.info( - 'Company %s: Account %s updated to Current Asset type', - account.company_id.display_name, account.code) + "Company %s: Account %s updated to Current Asset type", + account.company_id.display_name, + account.code, + ) logger.info("END of the script 'fix bank and cash account types'") return True diff --git a/account_usability/models/account_analytic_account.py b/account_usability/models/account_analytic_account.py index e1625de..9ca1e68 100644 --- a/account_usability/models/account_analytic_account.py +++ b/account_usability/models/account_analytic_account.py @@ -6,10 +6,10 @@ from odoo import models class AccountAnalyticAccount(models.Model): - _inherit = 'account.analytic.account' + _inherit = "account.analytic.account" def name_get(self): - if self._context.get('analytic_account_show_code_only'): + if self._context.get("analytic_account_show_code_only"): res = [] for record in self: res.append((record.id, record.code or record.name)) @@ -17,8 +17,11 @@ class AccountAnalyticAccount(models.Model): else: return super().name_get() - _sql_constraints = [( - 'code_company_unique', - 'unique(code, company_id)', - 'An analytic account with the same code already ' - 'exists in the same company!')] + _sql_constraints = [ + ( + "code_company_unique", + "unique(code, company_id)", + "An analytic account with the same code already " + "exists in the same company!", + ) + ] diff --git a/account_usability/models/account_bank_statement.py b/account_usability/models/account_bank_statement.py index c7d6e24..c0d8da8 100644 --- a/account_usability/models/account_bank_statement.py +++ b/account_usability/models/account_bank_statement.py @@ -7,18 +7,19 @@ from odoo.tools.misc import format_date class AccountBankStatement(models.Model): - _inherit = 'account.bank.statement' + _inherit = "account.bank.statement" start_date = fields.Date( - compute='_compute_dates', string='Start Date', readonly=True, - store=True) + compute="_compute_dates", string="Start Date", readonly=True, store=True + ) end_date = fields.Date( - compute='_compute_dates', string='End Date', readonly=True, - store=True) + compute="_compute_dates", string="End Date", readonly=True, store=True + ) hide_bank_statement_balance = fields.Boolean( - related='journal_id.hide_bank_statement_balance', readonly=True) + related="journal_id.hide_bank_statement_balance", readonly=True + ) - @api.depends('line_ids.date') + @api.depends("line_ids.date") def _compute_dates(self): for st in self: dates = [line.date for line in st.line_ids] @@ -30,26 +31,31 @@ class AccountBankStatement(models.Model): if stmt.hide_bank_statement_balance: continue else: - super(AccountBankStatement, stmt)._check_balance_end_real_same_as_computed() + super( + AccountBankStatement, stmt + )._check_balance_end_real_same_as_computed() return True - @api.depends('name', 'start_date', 'end_date') + @api.depends("name", "start_date", "end_date") def name_get(self): res = [] for statement in self: name = "%s (%s => %s)" % ( statement.name, - statement.start_date and format_date(self.env, statement.start_date) or '', - statement.end_date and format_date(self.env, statement.end_date) or '') + statement.start_date + and format_date(self.env, statement.start_date) + or "", + statement.end_date and format_date(self.env, statement.end_date) or "", + ) res.append((statement.id, name)) return res class AccountBankStatementLine(models.Model): - _inherit = 'account.bank.statement.line' + _inherit = "account.bank.statement.line" # Native order is: # _order = 'statement_id desc, sequence, id desc' - _order = 'statement_id desc, date desc, sequence, id desc' + _order = "statement_id desc, date desc, sequence, id desc" # Disable guessing for reconciliation # because my experience with several customers shows that it is a problem @@ -80,12 +86,14 @@ class AccountBankStatementLine(models.Model): def show_account_move(self): self.ensure_one() - action = self.env.ref('account.action_move_line_form').read()[0] + action = self.env.ref("account.action_move_line_form").read()[0] # Note: this action is on account.move, not account.move.line ! - action.update({ - 'views': False, - 'view_id': False, - 'view_mode': 'form,tree', - 'res_id': self.move_id.id, - }) + action.update( + { + "views": False, + "view_id": False, + "view_mode": "form,tree", + "res_id": self.move_id.id, + } + ) return action diff --git a/account_usability/models/account_incoterms.py b/account_usability/models/account_incoterms.py index 75f453e..dccbe52 100644 --- a/account_usability/models/account_incoterms.py +++ b/account_usability/models/account_incoterms.py @@ -6,11 +6,11 @@ from odoo import api, models class AccountIncoterms(models.Model): - _inherit = 'account.incoterms' + _inherit = "account.incoterms" - @api.depends('code', 'name') + @api.depends("code", "name") def name_get(self): res = [] for rec in self: - res.append((rec.id, '[%s] %s' % (rec.code, rec.name))) + res.append((rec.id, "[%s] %s" % (rec.code, rec.name))) return res diff --git a/account_usability/models/account_journal.py b/account_usability/models/account_journal.py index 2cc8d0a..fc18c27 100644 --- a/account_usability/models/account_journal.py +++ b/account_usability/models/account_journal.py @@ -6,22 +6,22 @@ from odoo import api, fields, models class AccountJournal(models.Model): - _inherit = 'account.journal' + _inherit = "account.journal" hide_bank_statement_balance = fields.Boolean( - string='Hide Bank Statement Balance', + string="Hide Bank Statement Balance", help="You may want to enable this option when your bank " "journal is generated from a bank statement file that " "doesn't handle start/end balance (QIF for instance) and " "you don't want to enter the start/end balance manually: it " "will prevent the display of wrong information in the accounting " - "dashboard and on bank statements.") + "dashboard and on bank statements.", + ) - @api.depends( - 'name', 'currency_id', 'company_id', 'company_id.currency_id', 'code') + @api.depends("name", "currency_id", "company_id", "company_id.currency_id", "code") def name_get(self): res = [] - if self._context.get('journal_show_code_only'): + if self._context.get("journal_show_code_only"): for journal in self: res.append((journal.id, journal.code)) return res @@ -29,12 +29,14 @@ class AccountJournal(models.Model): for journal in self: name = "[%s] %s" % (journal.code, journal.name) if ( - journal.currency_id and - journal.currency_id != journal.company_id.currency_id): + journal.currency_id + and journal.currency_id != journal.company_id.currency_id + ): name = "%s (%s)" % (name, journal.currency_id.name) res.append((journal.id, name)) return res + # @api.constrains('default_credit_account_id', 'default_debit_account_id') # def _check_account_type_on_bank_journal(self): # bank_acc_type = self.env.ref('account.data_account_type_liquidity') diff --git a/account_usability/models/account_move.py b/account_usability/models/account_move.py index 3483eee..c278554 100644 --- a/account_usability/models/account_move.py +++ b/account_usability/models/account_move.py @@ -3,17 +3,17 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from odoo import api, fields, models +from odoo.osv import expression from odoo.tools import float_is_zero from odoo.tools.misc import format_date -from odoo.osv import expression class AccountMove(models.Model): - _inherit = 'account.move' + _inherit = "account.move" # By default, we can still modify "ref" when account move is posted # which seems a bit lazy for me... - ref = fields.Char(states={'posted': [('readonly', True)]}) + ref = fields.Char(states={"posted": [("readonly", True)]}) date = fields.Date(tracking=True) invoice_date_due = fields.Date(tracking=True) invoice_payment_term_id = fields.Many2one(tracking=True) @@ -22,51 +22,63 @@ class AccountMove(models.Model): fiscal_position_id = fields.Many2one(tracking=True) amount_total = fields.Monetary(tracking=True) # for invoice report - has_discount = fields.Boolean( - compute='_compute_has_discount', readonly=True) + has_discount = fields.Boolean(compute="_compute_has_discount", readonly=True) # has_attachment is useful for those who use attachment to archive # supplier invoices. It allows them to find supplier invoices # that don't have any attachment has_attachment = fields.Boolean( - compute='_compute_has_attachment', - search='_search_has_attachment', readonly=True) + compute="_compute_has_attachment", + search="_search_has_attachment", + readonly=True, + ) sale_dates = fields.Char( - compute="_compute_sales_dates", readonly=True, + compute="_compute_sales_dates", + readonly=True, help="This information appears on invoice qweb report " - "(you may use it for your own report)") + "(you may use it for your own report)", + ) def _compute_has_discount(self): - prec = self.env['decimal.precision'].precision_get('Discount') + prec = self.env["decimal.precision"].precision_get("Discount") for inv in self: has_discount = False for line in inv.invoice_line_ids: - if not line.display_type and not float_is_zero(line.discount, precision_digits=prec): + if not line.display_type and not float_is_zero( + line.discount, precision_digits=prec + ): has_discount = True break inv.has_discount = has_discount def _compute_has_attachment(self): - iao = self.env['ir.attachment'] + iao = self.env["ir.attachment"] for move in self: - if iao.search_count([ - ('res_model', '=', 'account.move'), - ('res_id', '=', move.id), - ('type', '=', 'binary'), - ('company_id', '=', move.company_id.id)]): + if iao.search_count( + [ + ("res_model", "=", "account.move"), + ("res_id", "=", move.id), + ("type", "=", "binary"), + ("company_id", "=", move.company_id.id), + ] + ): move.has_attachment = True else: move.has_attachment = False def _search_has_attachment(self, operator, value): att_inv_ids = {} - if operator == '=': - search_res = self.env['ir.attachment'].search_read([ - ('res_model', '=', 'account.move'), - ('type', '=', 'binary'), - ('res_id', '!=', False)], ['res_id']) + if operator == "=": + search_res = self.env["ir.attachment"].search_read( + [ + ("res_model", "=", "account.move"), + ("type", "=", "binary"), + ("res_id", "!=", False), + ], + ["res_id"], + ) for att in search_res: - att_inv_ids[att['res_id']] = True - res = [('id', value and 'in' or 'not in', list(att_inv_ids))] + att_inv_ids[att["res_id"]] = True + res = [("id", value and "in" or "not in", list(att_inv_ids))] return res # when you have an invoice created from a lot of sale orders, the 'name' @@ -81,10 +93,10 @@ class AccountMove(models.Model): name = old_re[1] if name and len(name) > 100: # nice cut - name = '%s ...' % ', '.join(name.split(', ')[:3]) + name = "%s ..." % ", ".join(name.split(", ")[:3]) # if not enough, hard cut if len(name) > 120: - name = '%s ...' % old_re[1][:120] + name = "%s ..." % old_re[1][:120] res.append((old_re[0], name)) return res @@ -94,23 +106,26 @@ class AccountMove(models.Model): # write a rubbish '/' in it ! # 2) the 'name' field of the account.move.line is used in the overdue # letter, and '/' is not meaningful for our customer ! -# TODO mig to v12 -# def action_move_create(self): -# res = super().action_move_create() -# for inv in self: -# self._cr.execute( -# "UPDATE account_move_line SET name= " -# "CASE WHEN name='/' THEN %s " -# "ELSE %s||' - '||name END " -# "WHERE move_id=%s", (inv.number, inv.number, inv.move_id.id)) -# self.invalidate_cache() -# return res + # TODO mig to v12 + # def action_move_create(self): + # res = super().action_move_create() + # for inv in self: + # self._cr.execute( + # "UPDATE account_move_line SET name= " + # "CASE WHEN name='/' THEN %s " + # "ELSE %s||' - '||name END " + # "WHERE move_id=%s", (inv.number, inv.number, inv.move_id.id)) + # self.invalidate_cache() + # return res def delete_lines_qty_zero(self): - lines = self.env['account.move.line'].search([ - ('display_type', '=', False), - ('move_id', 'in', self.ids), - ('quantity', '=', 0)]) + lines = self.env["account.move.line"].search( + [ + ("display_type", "=", False), + ("move_id", "in", self.ids), + ("quantity", "=", 0), + ] + ) lines.unlink() return True @@ -120,24 +135,27 @@ class AccountMove(models.Model): res = [] has_sections = False subtotal = 0.0 - sign = self.move_type == 'out_refund' and -1 or 1 + sign = self.move_type == "out_refund" and -1 or 1 # Warning: the order of invoice line is forced in the view # # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import api, fields, models, _ +from odoo import _, api, fields, models class ProductTemplate(models.Model): - _inherit = 'product.template' + _inherit = "product.template" # DON'T put store=True on those fields, because they are company dependent sale_price_type = fields.Selection( - '_sale_purchase_price_type_sel', compute='_compute_sale_price_type', - string='Sale Price Type', compute_sudo=False, readonly=True) + "_sale_purchase_price_type_sel", + compute="_compute_sale_price_type", + string="Sale Price Type", + compute_sudo=False, + readonly=True, + ) purchase_price_type = fields.Selection( - '_sale_purchase_price_type_sel', compute='_compute_purchase_price_type', - string='Purchase Price Type', compute_sudo=False, readonly=True) + "_sale_purchase_price_type_sel", + compute="_compute_purchase_price_type", + string="Purchase Price Type", + compute_sudo=False, + readonly=True, + ) @api.model def _sale_purchase_price_type_sel(self): - return [('incl', _('Tax incl.')), ('excl', _('Tax excl.'))] + return [("incl", _("Tax incl.")), ("excl", _("Tax excl."))] - @api.depends('taxes_id') + @api.depends("taxes_id") def _compute_sale_price_type(self): for pt in self: - sale_price_type = 'incl' - if pt.taxes_id and all([not t.price_include for t in pt.taxes_id if t.amount_type == 'percent']): - sale_price_type = 'excl' + sale_price_type = "incl" + if pt.taxes_id and all( + [not t.price_include for t in pt.taxes_id if t.amount_type == "percent"] + ): + sale_price_type = "excl" pt.sale_price_type = sale_price_type - @api.depends('supplier_taxes_id') + @api.depends("supplier_taxes_id") def _compute_purchase_price_type(self): for pt in self: - purchase_price_type = 'incl' - if pt.supplier_taxes_id and all([not t.price_include for t in pt.supplier_taxes_id if t.amount_type == 'percent']): - purchase_price_type = 'excl' + purchase_price_type = "incl" + if pt.supplier_taxes_id and all( + [ + not t.price_include + for t in pt.supplier_taxes_id + if t.amount_type == "percent" + ] + ): + purchase_price_type = "excl" pt.purchase_price_type = purchase_price_type class ProductSupplierinfo(models.Model): - _inherit = 'product.supplierinfo' + _inherit = "product.supplierinfo" # DON'T put store=True on those fields, because they are company dependent purchase_price_type = fields.Selection( - related='product_tmpl_id.purchase_price_type', related_sudo=False) + related="product_tmpl_id.purchase_price_type", related_sudo=False + ) diff --git a/account_usability/models/res_partner.py b/account_usability/models/res_partner.py index 031f216..0b42694 100644 --- a/account_usability/models/res_partner.py +++ b/account_usability/models/res_partner.py @@ -6,7 +6,7 @@ from odoo import fields, models class ResPartner(models.Model): - _inherit = 'res.partner' + _inherit = "res.partner" invoice_warn = fields.Selection(tracking=True) property_account_position_id = fields.Many2one(tracking=True) diff --git a/account_usability/readme/DESCRIPTION.rst b/account_usability/readme/DESCRIPTION.rst index 36bac9f..f77296c 100644 --- a/account_usability/readme/DESCRIPTION.rst +++ b/account_usability/readme/DESCRIPTION.rst @@ -1,6 +1,6 @@ This modules adds the following functions: -* Add an *Overdue* filter on invoice search view (this feature was previously +* Add an *Overdue* filter on invoice search view (this feature was previously located in te module *account_invoice_overdue_filter*) * Increase the default limit of 80 lines in account move and account move line view. * disable reconciliation "guessing" diff --git a/account_usability/report/invoice_report.xml b/account_usability/report/invoice_report.xml index e285a29..8baa922 100644 --- a/account_usability/report/invoice_report.xml +++ b/account_usability/report/invoice_report.xml @@ -1,9 +1,9 @@ - + diff --git a/account_usability/views/account_account.xml b/account_usability/views/account_account.xml index ac25574..451f907 100644 --- a/account_usability/views/account_account.xml +++ b/account_usability/views/account_account.xml @@ -1,35 +1,41 @@ - + - account.account.form account.account - + - + - + account.account.search account.account - + - + diff --git a/account_usability/views/account_account_type.xml b/account_usability/views/account_account_type.xml index aa65384..471bc45 100644 --- a/account_usability/views/account_account_type.xml +++ b/account_usability/views/account_account_type.xml @@ -1,19 +1,18 @@ - + - account_usability.account_type_tree account.account.type - + - + diff --git a/account_usability/views/account_bank_statement.xml b/account_usability/views/account_bank_statement.xml index e69651c..89950e6 100644 --- a/account_usability/views/account_bank_statement.xml +++ b/account_usability/views/account_bank_statement.xml @@ -1,45 +1,61 @@ - + - usability.account.bank.statement.form account.bank.statement - + - - - - + 0 - + - + show - - - + + + - - + + @@ -51,24 +67,41 @@ account_usability.account.move.search account.move - + - - - - - + + + + + account.move.line - + -