diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5c1bd68b5..6fd938a9a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,7 +4,7 @@ exclude: | (?x) - + # We don't want to mess with tool-generated files .svg$|/tests/([^/]+/)?cassettes/|^.copier-answers.yml$|^.github/|^eslint.config.cjs|^prettier.config.cjs| # Library files can have extraneous formatting (even minimized) diff --git a/account_debt_report/__manifest__.py b/account_debt_report/__manifest__.py index ff960af28..288207c99 100644 --- a/account_debt_report/__manifest__.py +++ b/account_debt_report/__manifest__.py @@ -18,26 +18,24 @@ # ############################################################################## { - 'name': 'Account Debt Report', - 'version': "18.0.2.0.0", - 'category': 'Account Reporting', - 'author': 'ADHOC SA', - 'website': 'www.adhoc.com.ar', - 'license': 'AGPL-3', - 'depends': [ - 'account', - 'report_aeroo', + "name": "Account Debt Report", + "version": "18.0.2.0.0", + "category": "Account Reporting", + "author": "ADHOC SA", + "website": "www.adhoc.com.ar", + "license": "AGPL-3", + "depends": [ + "account", + "report_aeroo", ], - 'data': [ - 'report/account_debt_report.xml', - 'data/mail_template_data.xml', - 'security/ir.model.access.csv', - 'wizard/account_debt_report_wizard_view.xml', - 'views/account_move_line.xml', + "data": [ + "report/account_debt_report.xml", + "data/mail_template_data.xml", + "security/ir.model.access.csv", + "wizard/account_debt_report_wizard_view.xml", + "views/account_move_line.xml", ], - 'demo': [ - ], - 'test': [ - ], - 'installable': True, + "demo": [], + "test": [], + "installable": True, } diff --git a/account_debt_report/models/res_partner.py b/account_debt_report/models/res_partner.py index d86934c81..67efd9996 100644 --- a/account_debt_report/models/res_partner.py +++ b/account_debt_report/models/res_partner.py @@ -2,109 +2,140 @@ # For copyright and license notices, see __manifest__.py file in module root # directory ############################################################################## -from odoo import models, _ -from odoo.tools.safe_eval import safe_eval +from odoo import _, models + # from odoo.exceptions import ValidationError class ResPartner(models.Model): - _inherit = 'res.partner' + _inherit = "res.partner" def action_open_debt_report_wizard(self): - return { - 'type': 'ir.actions.act_window', - 'res_model': 'account.debt.report.wizard', - 'view_mode': 'form', - 'view_id': self.env.ref('account_debt_report.account_debt_report_wizard_form').id, - 'target': 'new', - 'context': { - 'partner_id': self.id, + "type": "ir.actions.act_window", + "res_model": "account.debt.report.wizard", + "view_mode": "form", + "view_id": self.env.ref("account_debt_report.account_debt_report_wizard_form").id, + "target": "new", + "context": { + "partner_id": self.id, }, } def _get_debt_report_lines(self): # TODO ver si borramos este metodo que no tiene mucho sentido (get_line_vals) def get_line_vals( - date=None, name=None, detail_lines=None, date_maturity=None, - amount=None, amount_residual=None, balance=None, - amount_currency=None, - currency_name=None, move_line=None): + date=None, + name=None, + detail_lines=None, + date_maturity=None, + amount=None, + amount_residual=None, + balance=None, + amount_currency=None, + currency_name=None, + move_line=None, + ): if not detail_lines: detail_lines = [] return { - 'date': date, - 'name': name, - 'detail_lines': detail_lines, - 'date_maturity': date_maturity, - 'amount': amount, - 'amount_residual': amount_residual, - 'balance': balance, - 'amount_currency': amount_currency, - 'currency_name': currency_name, - 'move_line': move_line, + "date": date, + "name": name, + "detail_lines": detail_lines, + "date_maturity": date_maturity, + "amount": amount, + "amount_residual": amount_residual, + "balance": balance, + "amount_currency": amount_currency, + "currency_name": currency_name, + "move_line": move_line, } self.ensure_one() - result_selection = self._context.get('result_selection', False) - from_date = self._context.get('from_date', False) - to_date = self._context.get('to_date', False) - historical_full = self._context.get('historical_full', False) - company_id = self._context.get('company_id', False) - show_invoice_detail = self._context.get('show_invoice_detail', False) - only_currency_lines = not self._context.get('company_currency') and self._context.get('secondary_currency') + result_selection = self._context.get("result_selection", False) + from_date = self._context.get("from_date", False) + to_date = self._context.get("to_date", False) + historical_full = self._context.get("historical_full", False) + company_id = self._context.get("company_id", False) + show_invoice_detail = self._context.get("show_invoice_detail", False) + only_currency_lines = not self._context.get("company_currency") and self._context.get("secondary_currency") balance_in_currency = 0.0 - balance_in_currency_name = '' + balance_in_currency_name = "" domain = [] if company_id: - domain += [('company_id', '=', company_id)] - company_currency_ids = self.env['res.company'].browse(company_id).currency_id + domain += [("company_id", "=", company_id)] + company_currency_ids = self.env["res.company"].browse(company_id).currency_id else: - domain += [('company_id', 'in', self.env.companies.ids)] - company_currency_ids = self.env.companies.mapped('currency_id') + domain += [("company_id", "in", self.env.companies.ids)] + company_currency_ids = self.env.companies.mapped("currency_id") if only_currency_lines and len(company_currency_ids) == 1: - domain += [('currency_id', 'not in', company_currency_ids.ids)] + domain += [("currency_id", "not in", company_currency_ids.ids)] if not historical_full: - domain += [('reconciled', '=', False), ('full_reconcile_id', '=', False)] + domain += [("reconciled", "=", False), ("full_reconcile_id", "=", False)] # si pide historial completo entonces mostramos los movimientos # si no mostramos los saldos - balance_field = 'amount_residual' + balance_field = "amount_residual" else: - balance_field = 'balance' + balance_field = "balance" - if result_selection == 'receivable': - domain += [('account_type', '=', 'asset_receivable')] - elif result_selection == 'payable': - domain += [('account_type', '=', 'liability_payable')] + if result_selection == "receivable": + domain += [("account_type", "=", "asset_receivable")] + elif result_selection == "payable": + domain += [("account_type", "=", "liability_payable")] else: - domain += [('account_type', 'in', ['asset_receivable', 'liability_payable'])] + domain += [("account_type", "in", ["asset_receivable", "liability_payable"])] - domain += [('partner_id', '=', self.id), ('parent_state', '=', 'posted')] + domain += [("partner_id", "=", self.id), ("parent_state", "=", "posted")] if from_date: - initial_domain = domain + [('date', '<', from_date)] - inicial_lines = self.env['account.move.line'].sudo()._read_group( - initial_domain, groupby=['partner_id'], aggregates=['balance:sum']) + initial_domain = domain + [("date", "<", from_date)] + inicial_lines = ( + self.env["account.move.line"] + .sudo() + ._read_group(initial_domain, groupby=["partner_id"], aggregates=["balance:sum"]) + ) balance = inicial_lines[0][1] if inicial_lines else 0.0 if len(company_currency_ids) == 1: - inicial_lines_currency = self.env['account.move.line'].sudo()._read_group( - initial_domain + [('currency_id', 'not in', company_currency_ids.ids)], groupby=['partner_id'], aggregates=['amount_currency:sum', 'currency_id:array_agg']) + inicial_lines_currency = ( + self.env["account.move.line"] + .sudo() + ._read_group( + initial_domain + [("currency_id", "not in", company_currency_ids.ids)], + groupby=["partner_id"], + aggregates=["amount_currency:sum", "currency_id:array_agg"], + ) + ) balance_in_currency = inicial_lines_currency[0][1] if inicial_lines_currency else 0.0 - balance_in_currency_name = self.env['res.currency'].browse(inicial_lines_currency[0][2][0]).display_name if inicial_lines_currency and inicial_lines_currency[0][2][0] else '' - res = [get_line_vals(name=_('INITIAL BALANCE'), balance=balance, amount_currency=balance_in_currency, currency_name=balance_in_currency_name)] - domain.append(('date', '>=', from_date)) + balance_in_currency_name = ( + self.env["res.currency"].browse(inicial_lines_currency[0][2][0]).display_name + if inicial_lines_currency and inicial_lines_currency[0][2][0] + else "" + ) + res = [ + get_line_vals( + name=_("INITIAL BALANCE"), + balance=balance, + amount_currency=balance_in_currency, + currency_name=balance_in_currency_name, + ) + ] + domain.append(("date", ">=", from_date)) else: balance = 0.0 res = [] if to_date: - domain.append(('date', '<=', to_date)) + domain.append(("date", "<=", to_date)) final_line = [] - records = self.env['account.move.line'].sudo().search(domain, order='date asc, name, move_id desc, date_maturity asc, id') + records = ( + self.env["account.move.line"] + .sudo() + .search(domain, order="date asc, name, move_id desc, date_maturity asc, id") + ) # construimos una nueva lista con los valores que queremos y de # manera mas facil @@ -115,15 +146,17 @@ def get_line_vals( inv_line_name = inv_line.name or "Sin descripción" inv_line_product_uom_id_name = inv_line.product_uom_id.name or "Sin unidad de medida" detail_lines.append( - ("* %s x %s %s" % ( - inv_line_name.replace( - '\n', ' ').replace('\r', ''), + "* %s x %s %s" + % ( + inv_line_name.replace("\n", " ").replace("\r", ""), inv_line.quantity, - inv_line_product_uom_id_name))) + inv_line_product_uom_id_name, + ) + ) name = record.move_id.name # similar to _format_aml_name - if record.ref and record.ref != '/': - name += ' - ' + record.ref + if record.ref and record.ref != "/": + name += " - " + record.ref date_maturity = record.date_maturity date = record.date @@ -134,26 +167,34 @@ def get_line_vals( amount_currency = record.amount_currency show_currency = record.currency_id != record.company_id.currency_id if record.payment_id: - name += ' - ' + record.journal_id.name + name += " - " + record.journal_id.name # TODO tal vez la suma podriamos probar hacerla en el xls como hacemos en libro iva v11/v12 - res.append(get_line_vals( - date=date, - name=name, - detail_lines=detail_lines, - date_maturity=date_maturity, - amount=amount, - amount_residual=amount_residual, - balance=balance, - amount_currency=amount_currency if show_currency else False, - currency_name=currency.name if show_currency else False, - # move_line=record.move_line_id, - )) + res.append( + get_line_vals( + date=date, + name=name, + detail_lines=detail_lines, + date_maturity=date_maturity, + amount=amount, + amount_residual=amount_residual, + balance=balance, + amount_currency=amount_currency if show_currency else False, + currency_name=currency.name if show_currency else False, + # move_line=record.move_line_id, + ) + ) record_currencys = records.filtered(lambda x: x.currency_id != x.company_id.currency_id) - if len(record_currencys.mapped('currency_id')) == 1: - total_currency = sum(record_currencys.mapped('amount_currency')) + balance_in_currency - final_line += [get_line_vals(name=_('Total'), amount_currency=total_currency, currency_name=record_currencys.mapped('currency_id').name)] + if len(record_currencys.mapped("currency_id")) == 1: + total_currency = sum(record_currencys.mapped("amount_currency")) + balance_in_currency + final_line += [ + get_line_vals( + name=_("Total"), + amount_currency=total_currency, + currency_name=record_currencys.mapped("currency_id").name, + ) + ] res += final_line return res diff --git a/account_debt_report/tests/test_account_debt_report.py b/account_debt_report/tests/test_account_debt_report.py index 69410e36a..9e675ee3e 100644 --- a/account_debt_report/tests/test_account_debt_report.py +++ b/account_debt_report/tests/test_account_debt_report.py @@ -1,14 +1,11 @@ from odoo.tests.common import TransactionCase -class TestAccountDebtReport(TransactionCase): +class TestAccountDebtReport(TransactionCase): def setUp(self): super(TestAccountDebtReport, self).setUp() # Set up test data, e.g., a partner and invoices - self.partner = self.env['res.partner'].create({ - 'name': 'Test Partner', - 'email': 'test@example.com' - }) + self.partner = self.env["res.partner"].create({"name": "Test Partner", "email": "test@example.com"}) def test_debt_report_lines(self): # Execute the method and validate output @@ -17,26 +14,24 @@ def test_debt_report_lines(self): self.assertIsInstance(report_lines, list, "Expected a list of report lines") if report_lines: first_line = report_lines[0] - self.assertIn('date', first_line, "Report line should contain 'date'") - self.assertIn('name', first_line, "Report line should contain 'name'") - self.assertIn('balance', first_line, "Report line should contain 'balance'") + self.assertIn("date", first_line, "Report line should contain 'date'") + self.assertIn("name", first_line, "Report line should contain 'name'") + self.assertIn("balance", first_line, "Report line should contain 'balance'") class TestAccountDebtReportWizard(TransactionCase): - def setUp(self): super(TestAccountDebtReportWizard, self).setUp() # Crear un partner de prueba - self.partner = self.env['res.partner'].create({ - 'name': 'Test Partner', - 'email': 'test@example.com' - }) + self.partner = self.env["res.partner"].create({"name": "Test Partner", "email": "test@example.com"}) # Crear el wizard para el reporte de deuda - self.wizard = self.env['account.debt.report.wizard'].create({ - 'company_id': self.env.company.id, - 'result_selection': 'all', - 'historical_full': True, - }) + self.wizard = self.env["account.debt.report.wizard"].create( + { + "company_id": self.env.company.id, + "result_selection": "all", + "historical_full": True, + } + ) def test_confirm_method(self): # Verificar que el método confirm se ejecuta correctamente @@ -47,4 +42,4 @@ def test_send_by_email_method(self): # Verificar que el método send_by_email se ejecuta correctamente action = self.wizard.with_context(active_id=self.partner.id).send_by_email() self.assertTrue(action, "El método send_by_email debería retornar una acción de ventana") - self.assertEqual(action['res_model'], 'mail.compose.message', "El modelo debería ser 'mail.compose.message'") + self.assertEqual(action["res_model"], "mail.compose.message", "El modelo debería ser 'mail.compose.message'") diff --git a/account_debt_report/wizard/account_debt_report_wizard.py b/account_debt_report/wizard/account_debt_report_wizard.py index d48c3a552..4657c6bf9 100644 --- a/account_debt_report/wizard/account_debt_report_wizard.py +++ b/account_debt_report/wizard/account_debt_report_wizard.py @@ -2,27 +2,26 @@ # For copyright and license notices, see __manifest__.py file in module root # directory ############################################################################## -from odoo import api, fields, models, _ +from odoo import _, api, fields, models from odoo.exceptions import ValidationError class AccountDebtReportWizard(models.TransientModel): - _name = 'account.debt.report.wizard' - _description = 'Account Debt Report Wizard' + _name = "account.debt.report.wizard" + _description = "Account Debt Report Wizard" def _default_result_selection(self): - return 'all' if self.env.user.has_group('account.group_account_invoice') else 'receivable' + return "all" if self.env.user.has_group("account.group_account_invoice") else "receivable" company_id = fields.Many2one( - 'res.company', - 'Company', - help="If you don't select a company, debt for all companies will be " - "exported." + "res.company", "Company", help="If you don't select a company, debt for all companies will be " "exported." ) result_selection = fields.Selection( - [('receivable', 'Receivable Accounts'), - ('payable', 'Payable Accounts'), - ('all', 'Receivable and Payable Accounts')], + [ + ("receivable", "Receivable Accounts"), + ("payable", "Payable Accounts"), + ("all", "Receivable and Payable Accounts"), + ], "Account Type's", required=True, default=_default_result_selection, @@ -33,80 +32,78 @@ def _default_result_selection(self): # TODO implementar # show_receipt_detail = fields.Boolean('Show Receipt Detail') historical_full = fields.Boolean( - help='If true, then it will show all partner history. If not, only ' - 'unreconciled items will be shown.') - company_currency = fields.Boolean( - default=True, - help='Add columns for company currency?' + help="If true, then it will show all partner history. If not, only " "unreconciled items will be shown." ) - secondary_currency = fields.Boolean( - help='Add columns for secondary currency?') + company_currency = fields.Boolean(default=True, help="Add columns for company currency?") + secondary_currency = fields.Boolean(help="Add columns for secondary currency?") def confirm(self): - active_ids = self._context.get('active_ids', False) + active_ids = self._context.get("active_ids", False) if not active_ids: return True - partners = self.env['res.partner'].browse(active_ids) + partners = self.env["res.partner"].browse(active_ids) data = { - 'secondary_currency': self.secondary_currency, - 'result_selection': self.result_selection, - 'company_id': self.company_id.id, - 'from_date': self.from_date, - 'to_date': self.to_date, - 'historical_full': self.historical_full, - 'show_invoice_detail': self.show_invoice_detail, + "secondary_currency": self.secondary_currency, + "result_selection": self.result_selection, + "company_id": self.company_id.id, + "from_date": self.from_date, + "to_date": self.to_date, + "historical_full": self.historical_full, + "show_invoice_detail": self.show_invoice_detail, } - return self.env['ir.actions.report'].search( - [('report_name', '=', 'account_debt_report')], - limit=1).with_context( - company_currency=self.company_currency, - secondary_currency=self.secondary_currency, - result_selection=self.result_selection, - company_id=self.company_id.id, - from_date=self.from_date, - to_date=self.to_date, - historical_full=self.historical_full, - show_invoice_detail=self.show_invoice_detail, - # show_receipt_detail=self.show_receipt_detail, - ).report_action(partners, data=data) + return ( + self.env["ir.actions.report"] + .search([("report_name", "=", "account_debt_report")], limit=1) + .with_context( + company_currency=self.company_currency, + secondary_currency=self.secondary_currency, + result_selection=self.result_selection, + company_id=self.company_id.id, + from_date=self.from_date, + to_date=self.to_date, + historical_full=self.historical_full, + show_invoice_detail=self.show_invoice_detail, + # show_receipt_detail=self.show_receipt_detail, + ) + .report_action(partners, data=data) + ) def send_by_email(self): - active_ids = self._context.get('active_ids', []) - active_id = self._context.get('active_id', False) + active_ids = self._context.get("active_ids", []) + active_id = self._context.get("active_id", False) context = { # report keys - 'secondary_currency': self.secondary_currency, - 'result_selection': self.result_selection, - 'company_id': self.company_id.id, - 'from_date': self.from_date, - 'to_date': self.to_date, - 'historical_full': self.historical_full, - 'show_invoice_detail': self.show_invoice_detail, + "secondary_currency": self.secondary_currency, + "result_selection": self.result_selection, + "company_id": self.company_id.id, + "from_date": self.from_date, + "to_date": self.to_date, + "historical_full": self.historical_full, + "show_invoice_detail": self.show_invoice_detail, # email keys - 'active_ids': active_ids, - 'active_id': active_id, - 'active_model': 'res.partner', - 'default_composition_mode': 'mass_mail', - 'default_use_template': True, - 'default_template_id': self.env.ref( - 'account_debt_report.email_template_debt_detail').id, - 'default_partner_to': '{{ object.id or \'\' }}', + "active_ids": active_ids, + "active_id": active_id, + "active_model": "res.partner", + "default_composition_mode": "mass_mail", + "default_use_template": True, + "default_template_id": self.env.ref("account_debt_report.email_template_debt_detail").id, + "default_partner_to": "{{ object.id or '' }}", } self = self.with_context(**context) return { - 'name': _('Send by Email'), - 'type': 'ir.actions.act_window', - 'res_model': 'mail.compose.message', - 'src_model': 'res.partner', - 'view_type': 'form', - 'context': context, - 'view_mode': 'form', - 'target': 'new', - 'auto_refresh': 1 + "name": _("Send by Email"), + "type": "ir.actions.act_window", + "res_model": "mail.compose.message", + "src_model": "res.partner", + "view_type": "form", + "context": context, + "view_mode": "form", + "target": "new", + "auto_refresh": 1, } - @api.constrains('company_currency', 'secondary_currency') + @api.constrains("company_currency", "secondary_currency") def _check_has_one_currency(self): for wizard in self: if not wizard.company_currency and not wizard.secondary_currency: - raise ValidationError(_('Debe seleccionar por lo menos una moneda para el informe')) + raise ValidationError(_("Debe seleccionar por lo menos una moneda para el informe")) diff --git a/account_financial_amount/__manifest__.py b/account_financial_amount/__manifest__.py index ce61e6df9..92cd8eda3 100644 --- a/account_financial_amount/__manifest__.py +++ b/account_financial_amount/__manifest__.py @@ -8,12 +8,10 @@ "account", ], "data": [ - 'security/account_debt_management_security.xml', - 'wizard/res_config_settings_views.xml', + "security/account_debt_management_security.xml", + "wizard/res_config_settings_views.xml", ], - "demo": [ - ], - 'images': [ - ], - 'installable': False, + "demo": [], + "images": [], + "installable": False, } diff --git a/account_financial_amount/models/account_move_line.py b/account_financial_amount/models/account_move_line.py index 097abf490..6e591f387 100644 --- a/account_financial_amount/models/account_move_line.py +++ b/account_financial_amount/models/account_move_line.py @@ -2,37 +2,37 @@ # For copyright and license notices, see __manifest__.py file in module root # directory ############################################################################## -from odoo import api, models, fields +from odoo import api, fields, models class AccountMoveLine(models.Model): - _inherit = 'account.move.line' + _inherit = "account.move.line" financial_amount_residual = fields.Monetary( - compute='_compute_financial_amounts', - string='Residual Financial Amount', - currency_field='company_currency_id', + compute="_compute_financial_amounts", + string="Residual Financial Amount", + currency_field="company_currency_id", ) financial_amount = fields.Monetary( - compute='_compute_financial_amounts', - currency_field='company_currency_id', + compute="_compute_financial_amounts", + currency_field="company_currency_id", ) - @api.depends('debit', 'credit') + @api.depends("debit", "credit") def _compute_financial_amounts(self): date = fields.Date.today() for line in self: financial_amount = ( - line.currency_id and line.currency_id._convert( - line.amount_currency, - line.company_id.currency_id, - line.company_id, date) or ( - line.balance)) + line.currency_id + and line.currency_id._convert(line.amount_currency, line.company_id.currency_id, line.company_id, date) + or (line.balance) + ) financial_amount_residual = ( - line.currency_id and line.currency_id._convert( - line.amount_residual_currency, - line.company_id.currency_id, - line.company_id, date) or - line.amount_residual) + line.currency_id + and line.currency_id._convert( + line.amount_residual_currency, line.company_id.currency_id, line.company_id, date + ) + or line.amount_residual + ) line.financial_amount = financial_amount line.financial_amount_residual = financial_amount_residual diff --git a/account_financial_amount/wizard/res_config_settings.py b/account_financial_amount/wizard/res_config_settings.py index a4525e3bd..c30b2af3e 100644 --- a/account_financial_amount/wizard/res_config_settings.py +++ b/account_financial_amount/wizard/res_config_settings.py @@ -6,11 +6,12 @@ class ResConfigSettings(models.TransientModel): - _inherit = 'res.config.settings' + _inherit = "res.config.settings" group_account_use_financial_amounts = fields.Boolean( "Use Financial Amounts", - help='Display Financial amounts on partner debts views and reports.\n' - 'Financial amounts are amounts on other currency converted to company ' - 'currency on todays exchange.', - implied_group='account_financial_amount.account_use_financial_amounts') + help="Display Financial amounts on partner debts views and reports.\n" + "Financial amounts are amounts on other currency converted to company " + "currency on todays exchange.", + implied_group="account_financial_amount.account_use_financial_amounts", + ) diff --git a/account_interests/__manifest__.py b/account_interests/__manifest__.py index 735c5d5d3..07067cef2 100644 --- a/account_interests/__manifest__.py +++ b/account_interests/__manifest__.py @@ -18,22 +18,22 @@ # ############################################################################## { - 'name': 'Interests Management', - 'version': "18.0.1.1.0", - 'category': 'Accounting', - 'sequence': 14, - 'summary': 'Calculate interests for selected partners', - 'author': 'ADHOC SA', - 'website': 'www.adhoc.com.ar', - 'license': 'AGPL-3', - 'depends': [ - 'account', + "name": "Interests Management", + "version": "18.0.1.1.0", + "category": "Accounting", + "sequence": 14, + "summary": "Calculate interests for selected partners", + "author": "ADHOC SA", + "website": "www.adhoc.com.ar", + "license": "AGPL-3", + "depends": [ + "account", ], - 'data': [ - 'views/res_company_views.xml', - 'data/ir_cron_data.xml', - 'security/ir.model.access.csv', + "data": [ + "views/res_company_views.xml", + "data/ir_cron_data.xml", + "security/ir.model.access.csv", ], - 'installable': True, - 'application': False, + "installable": True, + "application": False, } diff --git a/account_interests/models/res_company.py b/account_interests/models/res_company.py index b124917f2..9a517ae4b 100644 --- a/account_interests/models/res_company.py +++ b/account_interests/models/res_company.py @@ -2,15 +2,14 @@ # For copyright and license notices, see __manifest__.py file in module root # directory ############################################################################## -from odoo import models, fields +from odoo import fields, models class ResCompany(models.Model): - - _inherit = 'res.company' + _inherit = "res.company" interest_ids = fields.One2many( - 'res.company.interest', - 'company_id', - 'Interest', + "res.company.interest", + "company_id", + "Interest", ) diff --git a/account_interests/models/res_company_interest.py b/account_interests/models/res_company_interest.py index e2dc8a100..91cc5ac81 100644 --- a/account_interests/models/res_company_interest.py +++ b/account_interests/models/res_company_interest.py @@ -2,100 +2,98 @@ # For copyright and license notices, see __manifest__.py file in module root # directory ############################################################################## -from odoo import models, fields, api, _ -from odoo.tools.safe_eval import safe_eval +import logging + from dateutil.relativedelta import relativedelta +from odoo import _, api, fields, models from odoo.exceptions import UserError -import logging +from odoo.tools.safe_eval import safe_eval + _logger = logging.getLogger(__name__) class ResCompanyInterest(models.Model): - - _name = 'res.company.interest' - _description = 'Account Interest' + _name = "res.company.interest" + _description = "Account Interest" company_id = fields.Many2one( - 'res.company', - 'Company', + "res.company", + "Company", required=True, - ondelete='cascade', + ondelete="cascade", ) receivable_account_ids = fields.Many2many( - 'account.account', - string='Cuentas a Cobrar', - help='Cuentas a Cobrar que se tendrán en cuenta para evaular la deuda', + "account.account", + string="Cuentas a Cobrar", + help="Cuentas a Cobrar que se tendrán en cuenta para evaular la deuda", required=True, - domain=lambda self:[ - ('account_type', '=', 'asset_receivable'), - ('company_ids','=', self._context.get('default_company_id') or self.env.company.id) - ] + domain=lambda self: [ + ("account_type", "=", "asset_receivable"), + ("company_ids", "=", self._context.get("default_company_id") or self.env.company.id), + ], ) interest_product_id = fields.Many2one( - 'product.product', - 'Interest Product', + "product.product", + "Interest Product", required=True, ) analytic_account_id = fields.Many2one( - 'account.analytic.account', - 'Analytic account', - ) - rate = fields.Float( - 'Interest', - required=True, - digits=(7, 4) + "account.analytic.account", + "Analytic account", ) + rate = fields.Float("Interest", required=True, digits=(7, 4)) automatic_validation = fields.Boolean( - 'Automatic Validation?', - help='Automatic Invoice Validation?', + "Automatic Validation?", + help="Automatic Invoice Validation?", default=True, ) - rule_type = fields.Selection([ - ('daily', 'Day(s)'), - ('weekly', 'Week(s)'), - ('monthly', 'Month(s)'), - ('yearly', 'Year(s)'), - ], - 'Recurrency', + rule_type = fields.Selection( + [ + ("daily", "Day(s)"), + ("weekly", "Week(s)"), + ("monthly", "Month(s)"), + ("yearly", "Year(s)"), + ], + "Recurrency", help="Interests Invoice automatically repeat at specified interval", - default='monthly', - ) - interval = fields.Integer( - 'Repeat Every', - default=1, - help="Repeat every (Days/Week/Month/Year)" + default="monthly", ) + interval = fields.Integer("Repeat Every", default=1, help="Repeat every (Days/Week/Month/Year)") next_date = fields.Date( - 'Date of Next Invoice', + "Date of Next Invoice", default=fields.Date.today, ) domain = fields.Char( - 'Additional Filters', - default="[]", - help="Extra filters that will be added to the standard search" + "Additional Filters", default="[]", help="Extra filters that will be added to the standard search" ) has_domain = fields.Boolean(compute="_compute_has_domain") - late_payment_interest = fields.Boolean('Late payment interest', default=False, help="The interest calculation takes into account all late payments from the previous period. To obtain the daily rate, the interest is divided by the period. These days are considered depending on the type of period: 360 for annual, 30 for monthly and 7 for weekly.") + late_payment_interest = fields.Boolean( + "Late payment interest", + default=False, + help="The interest calculation takes into account all late payments from the previous period. To obtain the daily rate, the interest is divided by the period. These days are considered depending on the type of period: 360 for annual, 30 for monthly and 7 for weekly.", + ) @api.model def _cron_recurring_interests_invoices(self): - _logger.info('Running Interest Invoices Cron Job') + _logger.info("Running Interest Invoices Cron Job") current_date = fields.Date.today() companies_with_errors = [] - for rec in self.search([('next_date', '<=', current_date)]): + for rec in self.search([("next_date", "<=", current_date)]): try: rec.create_interest_invoices() rec.env.cr.commit() except Exception as e: - _logger.error('Error creating interest invoices for company: %s, %s', rec.company_id.name, str(e)) + _logger.error("Error creating interest invoices for company: %s, %s", rec.company_id.name, str(e)) companies_with_errors.append(rec.company_id.name) rec.env.cr.rollback() if companies_with_errors: - company_names = ', '.join(companies_with_errors) - error_message = _("We couldn't run interest invoices cron job in the following companies: %s.") % company_names + company_names = ", ".join(companies_with_errors) + error_message = ( + _("We couldn't run interest invoices cron job in the following companies: %s.") % company_names + ) raise UserError(error_message) def _calculate_date_deltas(self, rule_type, interval): @@ -103,19 +101,16 @@ def _calculate_date_deltas(self, rule_type, interval): Calcula los intervalos de fechas para la generación de intereses. """ deltas = { - 'daily': relativedelta(days=interval), - 'weekly': relativedelta(weeks=interval), - 'monthly': relativedelta(months=interval), - 'yearly': relativedelta(years=interval), + "daily": relativedelta(days=interval), + "weekly": relativedelta(weeks=interval), + "monthly": relativedelta(months=interval), + "yearly": relativedelta(years=interval), } return deltas.get(rule_type, relativedelta(months=interval)) - def create_interest_invoices(self): for rec in self: - _logger.info( - 'Creating Interest Invoices (id: %s, company: %s)', rec.id, - rec.company_id.name) + _logger.info("Creating Interest Invoices (id: %s, company: %s)", rec.id, rec.company_id.name) # hacemos un commit para refrescar cache # TODO ver de utilizar savepoints: https://github.com/OCA/odoo-community.org/blob/master/website/Contribution/CONTRIBUTING.rst#never-commit-the-transaction self.env.cr.commit() # pragma pylint: disable=invalid-commit @@ -131,8 +126,9 @@ def create_interest_invoices(self): # llamamos a crear las facturas con la compañia del interes para # que tome correctamente las cuentas - rec.with_company(rec.company_id).with_context(default_l10n_ar_afip_asoc_period_start=from_date, - default_l10n_ar_afip_asoc_period_end=to_date).create_invoices(from_date, to_date) + rec.with_company(rec.company_id).with_context( + default_l10n_ar_afip_asoc_period_start=from_date, default_l10n_ar_afip_asoc_period_end=to_date + ).create_invoices(from_date, to_date) # seteamos proxima corrida en hoy mas un periodo rec.next_date = to_date + next_delta @@ -140,9 +136,9 @@ def create_interest_invoices(self): def _get_move_line_domains(self): self.ensure_one() move_line_domain = [ - ('account_id', 'in', self.receivable_account_ids.ids), - ('partner_id.active', '=', True), - ('parent_state', '=', 'posted'), + ("account_id", "in", self.receivable_account_ids.ids), + ("partner_id.active", "=", True), + ("parent_state", "=", "posted"), ] return move_line_domain @@ -164,60 +160,74 @@ def _calculate_debts(self, from_date, to_date, groupby=None): # Usamos el parámetro de esta forma para evitar compartir # entre métodos valores mutables por defecto if groupby is None: - groupby = ['partner_id'] - + groupby = ["partner_id"] + deuda = {} interest_rate = { - 'daily': 1, - 'weekly': 7, - 'monthly': 30, - 'yearly': 360, + "daily": 1, + "weekly": 7, + "monthly": 30, + "yearly": 360, } # Deudas de períodos anteriores - previous_grouped_lines = self.env['account.move.line']._read_group( - domain=self._get_move_line_domains() + [('full_reconcile_id', '=', False), ('date_maturity', '<', from_date)], + previous_grouped_lines = self.env["account.move.line"]._read_group( + domain=self._get_move_line_domains() + + [("full_reconcile_id", "=", False), ("date_maturity", "<", from_date)], groupby=groupby, - aggregates=['amount_residual:sum'], + aggregates=["amount_residual:sum"], ) for x in previous_grouped_lines: - self._update_deuda(deuda, x[0], 'Deuda periodos anteriores', x[1] * self.rate) + self._update_deuda(deuda, x[0], "Deuda periodos anteriores", x[1] * self.rate) # Intereses por el último período - last_period_lines = self.env['account.move.line'].search( - self._get_move_line_domains() + [('amount_residual', '>', 0), ('date_maturity', '>=', from_date), ('date_maturity', '<', to_date)] + last_period_lines = self.env["account.move.line"].search( + self._get_move_line_domains() + + [("amount_residual", ">", 0), ("date_maturity", ">=", from_date), ("date_maturity", "<", to_date)] ) - for partner, amls in last_period_lines.grouped('partner_id').items(): + for partner, amls in last_period_lines.grouped("partner_id").items(): interest = sum( - move.amount_residual * ((to_date - move.invoice_date_due).days - 1) * (self.rate / interest_rate[self.rule_type]) - for move, lines in amls.grouped('move_id').items() + move.amount_residual + * ((to_date - move.invoice_date_due).days - 1) + * (self.rate / interest_rate[self.rule_type]) + for move, lines in amls.grouped("move_id").items() ) - self._update_deuda(deuda, partner, 'Deuda último periodo', interest) + self._update_deuda(deuda, partner, "Deuda último periodo", interest) # Intereses por pagos tardíos if self.late_payment_interest: - - partials = self.env['account.partial.reconcile'].search([ - # lo dejamos para NTH - # debit_move_id. safe eval domain - ('debit_move_id.partner_id.active', '=', True), - ('debit_move_id.date_maturity', '>=', from_date), - ('debit_move_id.date_maturity', '<=', to_date), - ('debit_move_id.parent_state', '=', 'posted'), - ('debit_move_id.account_id', 'in', self.receivable_account_ids.ids), - ('credit_move_id.date', '>=', from_date), - ('credit_move_id.date', '<', to_date)]).grouped('debit_move_id') + partials = ( + self.env["account.partial.reconcile"] + .search( + [ + # lo dejamos para NTH + # debit_move_id. safe eval domain + ("debit_move_id.partner_id.active", "=", True), + ("debit_move_id.date_maturity", ">=", from_date), + ("debit_move_id.date_maturity", "<=", to_date), + ("debit_move_id.parent_state", "=", "posted"), + ("debit_move_id.account_id", "in", self.receivable_account_ids.ids), + ("credit_move_id.date", ">=", from_date), + ("credit_move_id.date", "<", to_date), + ] + ) + .grouped("debit_move_id") + ) for move_line, parts in partials.items(): due_payments = parts.filtered(lambda x: x.credit_move_id.date > x.debit_move_id.date_maturity) interest = 0 if due_payments: - due_payments_amount = sum(due_payments.mapped('amount')) - last_date_payment = parts.filtered(lambda x: x.credit_move_id.date > x.debit_move_id.date_maturity).sorted('max_date')[-1].max_date + due_payments_amount = sum(due_payments.mapped("amount")) + last_date_payment = ( + parts.filtered(lambda x: x.credit_move_id.date > x.debit_move_id.date_maturity) + .sorted("max_date")[-1] + .max_date + ) days = (last_date_payment - move_line.date_maturity).days interest += due_payments_amount * days * (self.rate / interest_rate[self.rule_type]) - self._update_deuda(deuda, move_line.partner_id, 'Deuda pagos vencidos', interest) + self._update_deuda(deuda, move_line.partner_id, "Deuda pagos vencidos", interest) return deuda @@ -238,12 +248,17 @@ def create_invoices(self, from_date, to_date): # Calcular deudas e intereses deuda = self._calculate_debts(from_date, to_date) - journal = self.env['account.journal'].search([ - ('type', '=', 'sale'), - ('company_id', '=', self.company_id.id)], limit=1) + journal = self.env["account.journal"].search( + [("type", "=", "sale"), ("company_id", "=", self.company_id.id)], limit=1 + ) if self.receivable_account_ids != journal.default_account_id: - journal = self.env['account.journal'].search([('default_account_id','in',self.receivable_account_ids.ids)], limit=1) or journal + journal = ( + self.env["account.journal"].search( + [("default_account_id", "in", self.receivable_account_ids.ids)], limit=1 + ) + or journal + ) move_line_domain = self._get_move_line_domains(to_date) # Check if a filter is set @@ -251,7 +266,7 @@ def create_invoices(self, from_date, to_date): move_line_domain += safe_eval(self.domain) total_items = len(deuda) - _logger.info('%s interest invoices will be generated', total_items) + _logger.info("%s interest invoices will be generated", total_items) # Crear facturas for idx, partner in enumerate(deuda): @@ -259,32 +274,25 @@ def create_invoices(self, from_date, to_date): if not move_vals: continue - _logger.info('Creating Interest Invoice (%s of %s) for partner ID: %s', idx + 1, total_items, partner.id) + _logger.info("Creating Interest Invoice (%s of %s) for partner ID: %s", idx + 1, total_items, partner.id) - move = self.env['account.move'].create(move_vals) + move = self.env["account.move"].create(move_vals) if self.automatic_validation: try: move.action_post() except Exception as e: - _logger.error( - "Something went wrong creating " - "interests invoice: {}".format(e)) - - - + _logger.error("Something went wrong creating " f"interests invoice: {e}") def _prepare_info(self, to_date): self.ensure_one() # Format date to customer language - lang_code = self.env.context.get('lang', self.env.user.lang) - lang = self.env['res.lang']._lang_get(lang_code) + lang_code = self.env.context.get("lang", self.env.user.lang) + lang = self.env["res.lang"]._lang_get(lang_code) date_format = lang.date_format to_date_format = to_date.strftime(date_format) - res = _( - 'Deuda Vencida al %s con tasa de interés de %s') % ( - to_date_format, self.rate) + res = _("Deuda Vencida al %s con tasa de interés de %s") % (to_date_format, self.rate) return res @@ -296,50 +304,68 @@ def _prepare_interest_invoice(self, partner, debt, to_date, journal): comment = self._prepare_info(to_date) fpos = partner.property_account_position_id - taxes = self.interest_product_id.taxes_id.filtered( - lambda r: r.company_id == self.company_id) + taxes = self.interest_product_id.taxes_id.filtered(lambda r: r.company_id == self.company_id) tax_id = fpos.map_tax(taxes) invoice_vals = { - 'move_type': 'out_invoice', - 'currency_id': self.company_id.currency_id.id, - 'partner_id': partner.id, - 'fiscal_position_id': fpos.id, - 'user_id': partner.user_id.id or False, - 'company_id': self.company_id.id, - 'journal_id': journal.id, - 'invoice_origin': "Interests Invoice", - 'invoice_payment_term_id': False, - 'narration': self.interest_product_id.name + '.\n' + comment, - 'invoice_line_ids': [(0, 0, - { - "product_id": self.interest_product_id.id, - "quantity": 1.0, - "price_unit": value, - "partner_id": partner.id, - "name": self.interest_product_id.name + '.\n' + key, - "analytic_distribution": {self.analytic_account_id.id: 100.0} if self.analytic_account_id.id else False, - "tax_ids": [(6, 0, tax_id.ids)] - }) for key, value in debt.items() if isinstance(value, (int, float)) and value > 0], + "move_type": "out_invoice", + "currency_id": self.company_id.currency_id.id, + "partner_id": partner.id, + "fiscal_position_id": fpos.id, + "user_id": partner.user_id.id or False, + "company_id": self.company_id.id, + "journal_id": journal.id, + "invoice_origin": "Interests Invoice", + "invoice_payment_term_id": False, + "narration": self.interest_product_id.name + ".\n" + comment, + "invoice_line_ids": [ + ( + 0, + 0, + { + "product_id": self.interest_product_id.id, + "quantity": 1.0, + "price_unit": value, + "partner_id": partner.id, + "name": self.interest_product_id.name + ".\n" + key, + "analytic_distribution": {self.analytic_account_id.id: 100.0} + if self.analytic_account_id.id + else False, + "tax_ids": [(6, 0, tax_id.ids)], + }, + ) + for key, value in debt.items() + if isinstance(value, (int, float)) and value > 0 + ], } # hack para evitar modulo glue con l10n_latam_document - # hasta el momento tenemos feedback de dos clientes uruguayos de que los ajustes por intereses - # se hacen comoo factura normal y no ND. Si eventualmente otros clintes solicitan ND tendremos - # que analizar hacerlo parametrizable y además cambios en validación electrónica con DGI + # hasta el momento tenemos feedback de dos clientes uruguayos de que los ajustes por intereses + # se hacen comoo factura normal y no ND. Si eventualmente otros clintes solicitan ND tendremos + # que analizar hacerlo parametrizable y además cambios en validación electrónica con DGI # porque actualmente exige vincular una factura original (implementar poder pasar indicadores globales) - if journal.country_code != 'UY' and journal._fields.get('l10n_latam_use_documents') and journal.l10n_latam_use_documents: - debit_note = self.env['account.move'].new({ - 'move_type': 'out_invoice', - 'journal_id': journal.id, - 'partner_id': partner.id, - 'company_id': self.company_id.id, - }) - document_types = debit_note.l10n_latam_available_document_type_ids.filtered(lambda x: x.internal_type == 'debit_note') - invoice_vals['l10n_latam_document_type_id'] = document_types and document_types[0]._origin.id or debit_note.l10n_latam_document_type_id.id + if ( + journal.country_code != "UY" + and journal._fields.get("l10n_latam_use_documents") + and journal.l10n_latam_use_documents + ): + debit_note = self.env["account.move"].new( + { + "move_type": "out_invoice", + "journal_id": journal.id, + "partner_id": partner.id, + "company_id": self.company_id.id, + } + ) + document_types = debit_note.l10n_latam_available_document_type_ids.filtered( + lambda x: x.internal_type == "debit_note" + ) + invoice_vals["l10n_latam_document_type_id"] = ( + document_types and document_types[0]._origin.id or debit_note.l10n_latam_document_type_id.id + ) return invoice_vals - @api.depends('domain') + @api.depends("domain") def _compute_has_domain(self): for rec in self: rec.has_domain = len(safe_eval(rec.domain)) > 0 diff --git a/account_internal_transfer/__manifest__.py b/account_internal_transfer/__manifest__.py index 04fcb1a49..c2d33d494 100644 --- a/account_internal_transfer/__manifest__.py +++ b/account_internal_transfer/__manifest__.py @@ -18,26 +18,24 @@ # ############################################################################## { - 'name': 'Account Internal Transfer', - 'version': "18.0.1.2.0", - 'category': 'Accounting', - 'sequence': 14, - 'summary': '', - 'author': 'ADHOC SA', - 'website': 'www.adhoc.com.ar', - 'license': 'AGPL-3', - 'images': [ + "name": "Account Internal Transfer", + "version": "18.0.1.2.0", + "category": "Accounting", + "sequence": 14, + "summary": "", + "author": "ADHOC SA", + "website": "www.adhoc.com.ar", + "license": "AGPL-3", + "images": [], + "depends": [ + "account", ], - 'depends': [ - 'account', + "data": [ + "views/account_payment_views.xml", + "views/report_account_transfer.xml", ], - 'data':[ - 'views/account_payment_views.xml', - 'views/report_account_transfer.xml', - ], - 'demo': [ - ], - 'installable': True, - 'auto_install': True, - 'application': False, + "demo": [], + "installable": True, + "auto_install": True, + "application": False, } diff --git a/account_internal_transfer/models/account_bank_statement_line.py b/account_internal_transfer/models/account_bank_statement_line.py index 970ad5611..bb2a60736 100644 --- a/account_internal_transfer/models/account_bank_statement_line.py +++ b/account_internal_transfer/models/account_bank_statement_line.py @@ -6,25 +6,29 @@ class AccountBankStatementLine(models.Model): - - _inherit = 'account.bank.statement.line' + _inherit = "account.bank.statement.line" def action_undo_reconciliation(self): - ''' Los statement lines que fueron creados en versiones < 15.0 no tienen un account.move.line asociado, para + """Los statement lines que fueron creados en versiones < 15.0 no tienen un account.move.line asociado, para que el circuito completo de desconciliacion y conciliacion puedan funcionar debemos corregir esto creando manualmente un asiento similar al que se genera automaticamente. Tambien, las bases que fueron migrades tienen el problema donde reconocen que los statement.lines tienen si tiene un aml, pero este es el del pago y al desconciliar modifica el asiento del pago dejandolo incorrecto. En este metodo: 1. Identificamos los st.lines que tengan am que sean pago y los corregimos 2. creamos un nuevo asiento similar al que se genera automatico al crear el st.line. - 3, desvinculamos el am del pago del st.line ''' + 3, desvinculamos el am del pago del st.line""" # arreglamos solo los que son una transferencia interna o si hay una linea a cobrar / a pagar porque en 13, cuando conciliabamos contra gasto se generaba un pago # pero en este caso odoo ya lo resuelve bien. Si este filtro no llega a ir bien por algo podriamos ver si tiene payment_group_id (pero no es lo mas elegante porque podria # haber clientes sin payment_group) o si el payment tiene partner_id st_lines_to_fix = self.filtered( - lambda x: x.move_id.payment_id.is_internal_transfer or ( + lambda x: x.move_id.payment_id.is_internal_transfer + or ( x.move_id.payment_id - and x.move_id.line_ids.filtered(lambda x: x.account_id.account_type in ('asset_receivable', 'liability_payable')))) + and x.move_id.line_ids.filtered( + lambda x: x.account_id.account_type in ("asset_receivable", "liability_payable") + ) + ) + ) to_post = self.browse() for st_line in st_lines_to_fix: @@ -36,18 +40,22 @@ def action_undo_reconciliation(self): continue # Creamos la nueva linea manual como si se hubiese creado en 15 desde 0. y la vinculamos al statement line # de esta maera desviculamos el asiento del pago - st_line_new = self.new({ - 'statement_id': st_line.statement_id, - 'date': st_line.date, - 'amount': st_line.amount, - 'journal_id': st_line.journal_id, - 'move_type': st_line.move_type, - 'partner_id': st_line.partner_id, - 'payment_ref': st_line.payment_ref}) + st_line_new = self.new( + { + "statement_id": st_line.statement_id, + "date": st_line.date, + "amount": st_line.amount, + "journal_id": st_line.journal_id, + "move_type": st_line.move_type, + "partner_id": st_line.partner_id, + "payment_ref": st_line.payment_ref, + } + ) move_vals = st_line_new.move_id._convert_to_write(st_line_new._cache) - st_line.with_context(skip_account_move_synchronization=True).write({ - 'move_id': self.env['account.move'].create(move_vals)}) + st_line.with_context(skip_account_move_synchronization=True).write( + {"move_id": self.env["account.move"].create(move_vals)} + ) to_post += st_line # Corregimos el asiento del pago para que en lugar de ser AR/AP vs liquidez, sea AR/AP vs outstanding @@ -56,11 +64,13 @@ def action_undo_reconciliation(self): # Hicimos esto para desvincular el pago del extracto y de la línea del extracto que se está desconciliando payment.statement_line_id = False # Al pago le cambiamos la cuenta de outstanding en lugar de la cuenta de liquidez - payment.move_id.line_ids.filtered(lambda x: x.account_id == liquidity_lines.account_id).account_id = outstanding_account.id + payment.move_id.line_ids.filtered( + lambda x: x.account_id == liquidity_lines.account_id + ).account_id = outstanding_account.id # liquidity_lines.account_id = outstanding_account.id super().action_undo_reconciliation() - to_post.mapped('move_id').action_post() + to_post.mapped("move_id").action_post() # publicamos los asientos de las líneas del extracto contable # for st_line in st_lines_to_fix: # st_line.move_id._post(soft=False) diff --git a/account_internal_transfer/models/account_payment.py b/account_internal_transfer/models/account_payment.py index 20fd9ac12..babfe5823 100644 --- a/account_internal_transfer/models/account_payment.py +++ b/account_internal_transfer/models/account_payment.py @@ -1,44 +1,43 @@ -from odoo import models, fields, api, _ +from odoo import _, api, fields, models from odoo.exceptions import ValidationError class AccountPayment(models.Model): _inherit = "account.payment" - is_internal_transfer = fields.Boolean(string="Internal Transfer", - readonly=False, store=True, - tracking=True, - compute="_compute_is_internal_transfer") + is_internal_transfer = fields.Boolean( + string="Internal Transfer", readonly=False, store=True, tracking=True, compute="_compute_is_internal_transfer" + ) destination_journal_id = fields.Many2one( - comodel_name='account.journal', - string='Destination Journal', + comodel_name="account.journal", + string="Destination Journal", domain="[('type', 'in', ('bank','cash')), ('id', '!=', journal_id)]", check_company=True, ) def _get_name_receipt_report(self, report_xml_id): - """ Method similar to the '_get_name_invoice_report' of l10n_latam_invoice_document + """Method similar to the '_get_name_invoice_report' of l10n_latam_invoice_document Basically it allows different localizations to define it's own report This method should actually go in a sale_ux module that later can be extended by different localizations Another option would be to use report_substitute module and setup a subsitution with a domain """ self.ensure_one() if self.is_internal_transfer: - return 'account_internal_transfer.report_account_transfer' + return "account_internal_transfer.report_account_transfer" return report_xml_id - @api.depends('partner_id', 'journal_id', 'destination_journal_id') + @api.depends("partner_id", "journal_id", "destination_journal_id") def _compute_is_internal_transfer(self): for payment in self: - payment.is_internal_transfer = (not payment.partner_id\ - or payment.partner_id == payment.journal_id.company_id.partner_id)\ - and payment.destination_journal_id + payment.is_internal_transfer = ( + not payment.partner_id or payment.partner_id == payment.journal_id.company_id.partner_id + ) and payment.destination_journal_id def _get_aml_default_display_name_list(self): values = super()._get_aml_default_display_name_list() values = [ - (key, _("Internal Transfer") if self.is_internal_transfer and key == 'label' else value) + (key, _("Internal Transfer") if self.is_internal_transfer and key == "label" else value) for key, value in values ] return values @@ -46,20 +45,20 @@ def _get_aml_default_display_name_list(self): def _get_liquidity_aml_display_name_list(self): res = super()._get_liquidity_aml_display_name_list() if self.is_internal_transfer: - if self.payment_type == 'inbound': - return [('transfer_to', _('Transfer to %s', self.journal_id.name))] - else: # payment.payment_type == 'outbound': - return [('transfer_from', _('Transfer from %s', self.journal_id.name))] + if self.payment_type == "inbound": + return [("transfer_to", _("Transfer to %s", self.journal_id.name))] + else: # payment.payment_type == 'outbound': + return [("transfer_from", _("Transfer from %s", self.journal_id.name))] return res - @api.depends('destination_journal_id', 'is_internal_transfer') + @api.depends("destination_journal_id", "is_internal_transfer") def _compute_available_partner_bank_ids(self): super()._compute_available_partner_bank_ids() for pay in self: if pay.is_internal_transfer: pay.available_partner_bank_ids = pay.destination_journal_id.bank_account_id - @api.depends('is_internal_transfer', 'destination_journal_id') + @api.depends("is_internal_transfer", "destination_journal_id") def _compute_destination_account_id(self): super()._compute_destination_account_id() for pay in self: @@ -69,31 +68,40 @@ def _compute_destination_account_id(self): @api.model def _get_trigger_fields_to_synchronize(self): res = super()._get_trigger_fields_to_synchronize() - return res + ('is_internal_transfer',) + return res + ("is_internal_transfer",) def _create_paired_internal_transfer_payment(self): - ''' When an internal transfer is posted, a paired payment is created + """When an internal transfer is posted, a paired payment is created with opposite payment_type and swapped journal_id & destination_journal_id. Both payments liquidity transfer lines are then reconciled. - ''' + """ for payment in self: - paired_payment_type = 'inbound' if payment.payment_type == 'outbound' else 'outbound' - paired_payment = payment.copy({ - 'journal_id': payment.destination_journal_id.id, - 'destination_journal_id': payment.journal_id.id, - 'payment_type': paired_payment_type, - 'payment_method_line_id': payment.destination_journal_id._get_available_payment_method_lines(paired_payment_type)[:1].id, - 'move_id': None, - 'memo': payment.memo, - 'paired_internal_transfer_payment_id': payment.id, - 'date': payment.date, - }) + paired_payment_type = "inbound" if payment.payment_type == "outbound" else "outbound" + paired_payment = payment.copy( + { + "journal_id": payment.destination_journal_id.id, + "destination_journal_id": payment.journal_id.id, + "payment_type": paired_payment_type, + "payment_method_line_id": payment.destination_journal_id._get_available_payment_method_lines( + paired_payment_type + )[:1].id, + "move_id": None, + "memo": payment.memo, + "paired_internal_transfer_payment_id": payment.id, + "date": payment.date, + } + ) # The payment method line ID in 'paired_payment' needs to be computed manually, # as it does not compute automatically. # This ensures not to use the same payment method line ID of the original transfer payment. paired_payment._compute_payment_method_line_id() - if not payment.payment_method_line_id.payment_account_id or not paired_payment.payment_method_line_id.payment_account_id: - raise ValidationError(_("The origin or destination payment methods do not have an outstanding account.")) + if ( + not payment.payment_method_line_id.payment_account_id + or not paired_payment.payment_method_line_id.payment_account_id + ): + raise ValidationError( + _("The origin or destination payment methods do not have an outstanding account.") + ) paired_payment.filtered(lambda p: not p.move_id)._generate_journal_entry() paired_payment.move_id._post(soft=False) payment.paired_internal_transfer_payment_id = paired_payment @@ -103,10 +111,10 @@ def _create_paired_internal_transfer_payment(self): payment.message_post(body=body) lines = (payment.move_id.line_ids + paired_payment.move_id.line_ids).filtered( - lambda l: l.account_id == payment.destination_account_id and not l.reconciled) + lambda l: l.account_id == payment.destination_account_id and not l.reconciled + ) lines.reconcile() - def action_post(self): super().action_post() self.filtered( @@ -114,19 +122,18 @@ def action_post(self): )._create_paired_internal_transfer_payment() def action_open_destination_journal(self): - ''' Redirect the user to this destination journal. + """Redirect the user to this destination journal. :return: An action on account.move. - ''' + """ self.ensure_one() action = { - 'name': _("Destination journal"), - 'type': 'ir.actions.act_window', - 'res_model': 'account.journal', - 'context': {'create': False}, - 'view_mode': 'form', - 'target': 'new', - 'res_id': self.destination_journal_id.id, + "name": _("Destination journal"), + "type": "ir.actions.act_window", + "res_model": "account.journal", + "context": {"create": False}, + "view_mode": "form", + "target": "new", + "res_id": self.destination_journal_id.id, } return action - diff --git a/account_journal_security/__manifest__.py b/account_journal_security/__manifest__.py index 3b65321d2..4c2360fb2 100644 --- a/account_journal_security/__manifest__.py +++ b/account_journal_security/__manifest__.py @@ -18,28 +18,25 @@ # ############################################################################## { - 'name': 'Journal Security', - 'version': "18.0.1.0.0", - 'category': 'Accounting', - 'sequence': 14, - 'summary': 'Restrict the use of certain journals to certain users', - 'author': 'ADHOC SA', - 'website': 'www.adhoc.com.ar', - 'license': 'AGPL-3', - 'images': [ + "name": "Journal Security", + "version": "18.0.1.0.0", + "category": "Accounting", + "sequence": 14, + "summary": "Restrict the use of certain journals to certain users", + "author": "ADHOC SA", + "website": "www.adhoc.com.ar", + "license": "AGPL-3", + "images": [], + "depends": [ + "account", ], - 'depends': [ - 'account', + "data": [ + "views/account_journal_views.xml", + "security/journal_security_security.xml", ], - 'data': [ - 'views/account_journal_views.xml', - 'security/journal_security_security.xml', - ], - 'demo': [ - ], - 'test': [ - ], - 'installable': True, - 'auto_install': False, - 'application': False, + "demo": [], + "test": [], + "installable": True, + "auto_install": False, + "application": False, } diff --git a/account_journal_security/migrations/12.0.1.1.0/post-migration.py b/account_journal_security/migrations/12.0.1.1.0/post-migration.py index 119bc5f6f..fe7c6253e 100644 --- a/account_journal_security/migrations/12.0.1.1.0/post-migration.py +++ b/account_journal_security/migrations/12.0.1.1.0/post-migration.py @@ -3,6 +3,4 @@ @openupgrade.migrate() def migrate(env, version): - openupgrade.load_data( - env.cr, 'account_journal_security', - 'migrations/12.0.1.1.0/mig_data.xml') + openupgrade.load_data(env.cr, "account_journal_security", "migrations/12.0.1.1.0/mig_data.xml") diff --git a/account_journal_security/models/account_journal.py b/account_journal_security/models/account_journal.py index ebf1f5361..96dd8cf8d 100644 --- a/account_journal_security/models/account_journal.py +++ b/account_journal_security/models/account_journal.py @@ -2,45 +2,43 @@ # For copyright and license notices, see __manifest__.py file in module root # directory ############################################################################## -from odoo import fields, models, api, SUPERUSER_ID, _ +from odoo import SUPERUSER_ID, _, api, fields, models from odoo.exceptions import ValidationError class AccountJournal(models.Model): - _inherit = 'account.journal' + _inherit = "account.journal" user_ids = fields.Many2many( - 'res.users', - 'journal_security_journal_users', - 'journal_id', - 'user_id', + "res.users", + "journal_security_journal_users", + "journal_id", + "user_id", # string='Restricted to Users', - string='Totally allowed to', - help='If choose some users, then this journal and the information' - ' related to it will be only visible for those users.', + string="Totally allowed to", + help="If choose some users, then this journal and the information" + " related to it will be only visible for those users.", copy=False, - context={'active_test': False} + context={"active_test": False}, ) modification_user_ids = fields.Many2many( - 'res.users', - 'journal_security_journal_modification_users', - 'journal_id', - 'user_id', - string='Modifications restricted to', - help='If choose some users, then only this users will be allow to ' - ' create, write or delete accounting data related of this journal. ' - 'Information will still be visible for other users.', + "res.users", + "journal_security_journal_modification_users", + "journal_id", + "user_id", + string="Modifications restricted to", + help="If choose some users, then only this users will be allow to " + " create, write or delete accounting data related of this journal. " + "Information will still be visible for other users.", copy=False, - context={'active_test': False} + context={"active_test": False}, ) journal_restriction = fields.Selection( - [('none', 'Ninguna'), - ('modification', 'Modificacion'), - ('total', 'Total')], + [("none", "Ninguna"), ("modification", "Modificacion"), ("total", "Total")], string="Tipo de Restriccion", - compute='_compute_journal_restriction', + compute="_compute_journal_restriction", readonly=False, ) @@ -48,19 +46,19 @@ class AccountJournal(models.Model): def _compute_journal_restriction(self): for rec in self: if rec.user_ids: - rec.journal_restriction = 'total' + rec.journal_restriction = "total" elif rec.modification_user_ids: - rec.journal_restriction = 'modification' + rec.journal_restriction = "modification" else: - rec.journal_restriction = 'none' + rec.journal_restriction = "none" - @api.constrains('user_ids') + @api.constrains("user_ids") def check_restrict_users(self): - self._check_journal_users_restriction('user_ids') + self._check_journal_users_restriction("user_ids") - @api.constrains('modification_user_ids') + @api.constrains("modification_user_ids") def check_modification_users(self): - self._check_journal_users_restriction('modification_user_ids') + self._check_journal_users_restriction("modification_user_ids") def _check_journal_users_restriction(self, field): """ @@ -75,10 +73,13 @@ def _check_journal_users_restriction(self, field): # FIXME: Con el onchange de journal_restriction esto # ya no debería ocurrir. if self.modification_user_ids and self.user_ids: - raise ValidationError(_( - 'No puede setear valores en "Totalmente restricto a:" y ' - '"Modificaciones restrictas a:" simultaneamente. Las opciones ' - 'son excluyentes!')) + raise ValidationError( + _( + 'No puede setear valores en "Totalmente restricto a:" y ' + '"Modificaciones restrictas a:" simultaneamente. Las opciones ' + "son excluyentes!" + ) + ) # con sudo porque ya no los ve si no se asigno env_user = self.env.user @@ -89,10 +90,14 @@ def _check_journal_users_restriction(self, field): journal_users = rec[field] # journal_users = rec.user_ids if journal_users and env_user not in journal_users: - raise ValidationError(_( - 'No puede restringir el diario "%s" a usuarios sin ' - 'inclurise a usted mismo ya que dejaria de ver este ' - 'diario') % (rec.name)) + raise ValidationError( + _( + 'No puede restringir el diario "%s" a usuarios sin ' + "inclurise a usted mismo ya que dejaria de ver este " + "diario" + ) + % (rec.name) + ) # necesitamos limpiar este cache para que no deje de verlo self.env.flush_all() self.env.registry.clear_cache() @@ -107,21 +112,19 @@ def _search(self, domain, offset=0, limit=None, order=None): """ user = self.env.user if not self.env.is_superuser(): - domain += [ - '|', ('modification_user_ids', '=', False), - ('id', 'not in', user.journal_ids.ids)] + domain += ["|", ("modification_user_ids", "=", False), ("id", "not in", user.journal_ids.ids)] return super()._search(domain, offset, limit, order) - @api.onchange('journal_restriction') + @api.onchange("journal_restriction") def unset_modification_user_ids(self): """ Al cambiar una opción por otra, limpiar el campo M2M que se oculta para evitar conflictos al guardar. """ - if self.journal_restriction == 'modification': + if self.journal_restriction == "modification": self.modification_user_ids = self.user_ids self.user_ids = None - elif self.journal_restriction == 'total': + elif self.journal_restriction == "total": self.user_ids = self.modification_user_ids self.modification_user_ids = None else: diff --git a/account_journal_security/models/account_move.py b/account_journal_security/models/account_move.py index 968220786..5e0036f73 100644 --- a/account_journal_security/models/account_move.py +++ b/account_journal_security/models/account_move.py @@ -6,8 +6,7 @@ class AccountMove(models.Model): - - _inherit = 'account.move' + _inherit = "account.move" journal_id = fields.Many2one( auto_join=True, diff --git a/account_journal_security/models/res_users.py b/account_journal_security/models/res_users.py index 4c88e759c..940a3c79d 100644 --- a/account_journal_security/models/res_users.py +++ b/account_journal_security/models/res_users.py @@ -6,23 +6,22 @@ class Users(models.Model): - - _inherit = 'res.users' + _inherit = "res.users" journal_ids = fields.Many2many( - 'account.journal', - 'journal_security_journal_users', - 'user_id', - 'journal_id', - 'Restricted Journals (TOTAL)', - context={'active_test': False}, + "account.journal", + "journal_security_journal_users", + "user_id", + "journal_id", + "Restricted Journals (TOTAL)", + context={"active_test": False}, ) modification_journal_ids = fields.Many2many( - 'account.journal', - 'journal_security_journal_modification_users', - 'user_id', - 'journal_id', - 'Modification Journals', - context={'active_test': False}, + "account.journal", + "journal_security_journal_modification_users", + "user_id", + "journal_id", + "Modification Journals", + context={"active_test": False}, ) diff --git a/account_payment_term_surcharge/__manifest__.py b/account_payment_term_surcharge/__manifest__.py index a497a6be7..94e908649 100644 --- a/account_payment_term_surcharge/__manifest__.py +++ b/account_payment_term_surcharge/__manifest__.py @@ -18,26 +18,26 @@ # ############################################################################## { - 'name': 'Surcharges on payment terms', - 'version': "18.0.1.0.0", - 'category': 'Accounting', - 'sequence': 14, - 'summary': 'Allow to add surcharges for invoices on payment terms', - 'author': 'ADHOC SA', - 'website': 'www.adhoc.com.ar', - 'license': 'AGPL-3', - 'depends': [ - 'account', - 'account_debit_note', + "name": "Surcharges on payment terms", + "version": "18.0.1.0.0", + "category": "Accounting", + "sequence": 14, + "summary": "Allow to add surcharges for invoices on payment terms", + "author": "ADHOC SA", + "website": "www.adhoc.com.ar", + "license": "AGPL-3", + "depends": [ + "account", + "account_debit_note", ], - 'data': [ - 'views/account_payment_term_view.xml', - 'views/account_payment_term_surcharge_view.xml', - 'views/account_move_views.xml', - 'wizard/res_config_settings_views.xml', - 'security/ir.model.access.csv', - 'data/ir_cron_data.xml' + "data": [ + "views/account_payment_term_view.xml", + "views/account_payment_term_surcharge_view.xml", + "views/account_move_views.xml", + "wizard/res_config_settings_views.xml", + "security/ir.model.access.csv", + "data/ir_cron_data.xml", ], - 'installable': True, - 'application': False, + "installable": True, + "application": False, } diff --git a/account_payment_term_surcharge/models/account_move.py b/account_payment_term_surcharge/models/account_move.py index d8e5bd4db..fdcadd043 100644 --- a/account_payment_term_surcharge/models/account_move.py +++ b/account_payment_term_surcharge/models/account_move.py @@ -1,52 +1,52 @@ -from odoo import fields, models, _, api, Command, tools +import logging + +from odoo import Command, _, api, fields, models, tools from odoo.exceptions import UserError -import logging _logger = logging.getLogger(__name__) class AccountMove(models.Model): - _inherit = 'account.move' + _inherit = "account.move" - next_surcharge_date = fields.Date(compute='_compute_next_surcharge', store=True) - next_surcharge_percent = fields.Float(compute='_compute_next_surcharge', store=True) + next_surcharge_date = fields.Date(compute="_compute_next_surcharge", store=True) + next_surcharge_percent = fields.Float(compute="_compute_next_surcharge", store=True) avoid_surcharge_invoice = fields.Boolean() def _cron_recurring_surcharges_invoices(self, batch_size=60): current_date = fields.Date.context_today(self) domain = [ - ('next_surcharge_date', '<=', current_date), - ('state', '=', 'posted'), - ('payment_state', 'in', ['not_paid', 'partial']), - ('avoid_surcharge_invoice', '=', False)] - _logger.info('Running Surcharges Invoices Cron Job, pendientes por procesar %s facturas' % self.search_count(domain)) + ("next_surcharge_date", "<=", current_date), + ("state", "=", "posted"), + ("payment_state", "in", ["not_paid", "partial"]), + ("avoid_surcharge_invoice", "=", False), + ] + _logger.info( + "Running Surcharges Invoices Cron Job, pendientes por procesar %s facturas" % self.search_count(domain) + ) to_create = self.search(domain) to_create[:batch_size].create_surcharges_invoices() if len(to_create) > batch_size: - self.env.ref('account_payment_term_surcharge.cron_recurring_surcharges_invoices')._trigger() + self.env.ref("account_payment_term_surcharge.cron_recurring_surcharges_invoices")._trigger() def create_surcharges_invoices(self): invoice_with_errors = [] for rec in self: - _logger.info( - 'Creating Surcharges Invoices (id: %s, company: %s)', rec.id, - rec.company_id.name) + _logger.info("Creating Surcharges Invoices (id: %s, company: %s)", rec.id, rec.company_id.name) try: rec.create_surcharge_invoice(rec.next_surcharge_date, rec.next_surcharge_percent) - if not tools.config['test_enable']: + if not tools.config["test_enable"]: rec.env.cr.commit() except: invoice_with_errors.append(rec.id) rec.avoid_surcharge_invoice = True - _logger.error("Something went wrong creating the surcharge invoice from the invoice id:{}".format(rec.id)) - message_body = _("Something went wrong creating the surcharge invoice from this invoice. Please take a look on it.") - partner_ids = rec.message_partner_ids.filtered(lambda x: not x.partner_share) - rec.message_post( - body=message_body, - partner_ids=partner_ids.ids, - subtype_xmlid='mail.mt_note' + _logger.error(f"Something went wrong creating the surcharge invoice from the invoice id:{rec.id}") + message_body = _( + "Something went wrong creating the surcharge invoice from this invoice. Please take a look on it." ) - if not tools.config['test_enable']: + partner_ids = rec.message_partner_ids.filtered(lambda x: not x.partner_share) + rec.message_post(body=message_body, partner_ids=partner_ids.ids, subtype_xmlid="mail.mt_note") + if not tools.config["test_enable"]: rec.env.cr.commit() continue if invoice_with_errors: @@ -57,63 +57,72 @@ def create_surcharge_invoice(self, surcharge_date, surcharge_percent): self.ensure_one() product = self.company_id.payment_term_surcharge_product_id if not product: - raise UserError('Atención, debes configurar un producto por defecto para que aplique a la hora de crear las facturas de recargo') + raise UserError( + "Atención, debes configurar un producto por defecto para que aplique a la hora de crear las facturas de recargo" + ) validate_percent = self._validate_next_surcharge_percent(surcharge_date, surcharge_percent) if not validate_percent: - raise UserError('Atención,el next surcharge date no es compatible con el next surcharge percent.') + raise UserError("Atención,el next surcharge date no es compatible con el next surcharge percent.") debt = self.amount_residual - move_debit_note_wiz = self.env['account.debit.note'].with_context( - active_model="account.move", - active_ids=self.ids).create({ - 'date': surcharge_date, - 'reason': product.name, - }) - debit_note = self.env['account.move'].browse(move_debit_note_wiz.create_debit().get('res_id')) - debit_note.narration = product.name + '.\n' + self.prepare_info(surcharge_date, debt, surcharge_percent) + move_debit_note_wiz = ( + self.env["account.debit.note"] + .with_context(active_model="account.move", active_ids=self.ids) + .create( + { + "date": surcharge_date, + "reason": product.name, + } + ) + ) + debit_note = self.env["account.move"].browse(move_debit_note_wiz.create_debit().get("res_id")) + debit_note.narration = product.name + ".\n" + self.prepare_info(surcharge_date, debt, surcharge_percent) self._add_surcharge_line(debit_note, product, debt, surcharge_date, surcharge_percent) if self.company_id.payment_term_surcharge_invoice_auto_post: try: debit_note.action_post() except Exception as exp: - _logger.error( - "Something went wrong validating " - "surcharge invoice: {}".format(exp)) + _logger.error("Something went wrong validating " f"surcharge invoice: {exp}") raise exp self._compute_next_surcharge() def prepare_info(self, to_date, debt, surcharge): self.ensure_one() - lang_code = self.env.context.get('lang', self.env.user.lang) - lang = self.env['res.lang']._lang_get(lang_code) + lang_code = self.env.context.get("lang", self.env.user.lang) + lang = self.env["res.lang"]._lang_get(lang_code) date_format = lang.date_format to_date_format = to_date.strftime(date_format) - res = _( - 'Deuda Vencida al %s: %s\n' - 'Tasa de interés: %s') % ( - to_date_format, debt, surcharge) + res = _("Deuda Vencida al %s: %s\n" "Tasa de interés: %s") % (to_date_format, debt, surcharge) return res def _add_surcharge_line(self, debit_note, product, debt, to_date, surcharge): self.ensure_one() comment = self.prepare_info(to_date, debt, surcharge) debit_note = debit_note.with_context(check_move_validity=False) - line_vals = [Command.create({"product_id": product.id, "price_unit": (surcharge / 100) * debt, "name": product.name + '.\n' + comment})] - debit_note.write({'invoice_line_ids': line_vals,'is_move_sent': True}) + line_vals = [ + Command.create( + { + "product_id": product.id, + "price_unit": (surcharge / 100) * debt, + "name": product.name + ".\n" + comment, + } + ) + ] + debit_note.write({"invoice_line_ids": line_vals, "is_move_sent": True}) - @api.depends('invoice_payment_term_id', 'invoice_date') + @api.depends("invoice_payment_term_id", "invoice_date") def _compute_next_surcharge(self): for rec in self: if rec.invoice_payment_term_id.surcharge_ids != False: surcharges = [] - debit_note_dates = rec.debit_note_ids.mapped('invoice_date') + debit_note_dates = rec.debit_note_ids.mapped("invoice_date") for surcharge in rec.invoice_payment_term_id.surcharge_ids: tentative_date = surcharge._calculate_date(rec.invoice_date) if tentative_date not in debit_note_dates: - surcharges.append({'date': tentative_date, 'surcharge': surcharge.surcharge}) - surcharges.sort(key=lambda x: x['date']) + surcharges.append({"date": tentative_date, "surcharge": surcharge.surcharge}) + surcharges.sort(key=lambda x: x["date"]) if len(surcharges) > 0: - rec.next_surcharge_date = surcharges[0].get('date') - rec.next_surcharge_percent = surcharges[0].get('surcharge') + rec.next_surcharge_date = surcharges[0].get("date") + rec.next_surcharge_percent = surcharges[0].get("surcharge") else: rec.next_surcharge_date = False rec.next_surcharge_percent = False @@ -130,19 +139,19 @@ def action_send_invoice_mail(self): self = self.filtered(lambda x: not x.is_move_sent) super().action_send_invoice_mail() - def _validate_next_surcharge_percent(self,surcharge_date, surcharge_percent): + def _validate_next_surcharge_percent(self, surcharge_date, surcharge_percent): self.ensure_one() - if len(self.invoice_payment_term_id.surcharge_ids) >1: + if len(self.invoice_payment_term_id.surcharge_ids) > 1: surcharges = [] for surcharge in self.invoice_payment_term_id.surcharge_ids: tentative_date = surcharge._calculate_date(self.invoice_date) - surcharges.append({'surcharge_date': tentative_date, 'surcharge_percent': surcharge.surcharge}) - + surcharges.append({"surcharge_date": tentative_date, "surcharge_percent": surcharge.surcharge}) + closest_surcharge = max( - (s for s in surcharges if s['surcharge_date'] <= surcharge_date), - key=lambda s: s['surcharge_date'], - default=None + (s for s in surcharges if s["surcharge_date"] <= surcharge_date), + key=lambda s: s["surcharge_date"], + default=None, ) - if closest_surcharge.get('surcharge_percent') != surcharge_percent: + if closest_surcharge.get("surcharge_percent") != surcharge_percent: return False return True diff --git a/account_payment_term_surcharge/models/account_payment_term.py b/account_payment_term_surcharge/models/account_payment_term.py index c13063e0d..2570093d9 100644 --- a/account_payment_term_surcharge/models/account_payment_term.py +++ b/account_payment_term_surcharge/models/account_payment_term.py @@ -1,19 +1,19 @@ from odoo import api, fields, models -from odoo.exceptions import UserError class AccountPaymentTerm(models.Model): _inherit = "account.payment.term" surcharge_ids = fields.One2many( - 'account.payment.term.surcharge', - 'payment_term_id', string='Surcharges', + "account.payment.term.surcharge", + "payment_term_id", + string="Surcharges", copy=True, ) - show_surcharge_warning = fields.Boolean(compute='_compute_surcharge_product') + show_surcharge_warning = fields.Boolean(compute="_compute_surcharge_product") - @api.depends('company_id', 'surcharge_ids') + @api.depends("company_id", "surcharge_ids") def _compute_surcharge_product(self): """Check if the surcharge product needs to be updated for the given company context.""" for rec in self: @@ -23,8 +23,10 @@ def _compute_surcharge_product(self): # Devuelve False si falta el producto de recargo self.show_surcharge_warning = bool(company.payment_term_surcharge_product_id) else: # Verificar todas las compañías si company_id es False - all_companies = self.env['res.company'].search([]) + all_companies = self.env["res.company"].search([]) # Devuelve False si alguna compañía no tiene configurado el producto de recargo - rec.show_surcharge_warning = all(company.payment_term_surcharge_product_id for company in all_companies) + rec.show_surcharge_warning = all( + company.payment_term_surcharge_product_id for company in all_companies + ) else: rec.show_surcharge_warning = True diff --git a/account_payment_term_surcharge/models/account_payment_term_surcharge.py b/account_payment_term_surcharge/models/account_payment_term_surcharge.py index 4c89c8342..2e5add389 100644 --- a/account_payment_term_surcharge/models/account_payment_term_surcharge.py +++ b/account_payment_term_surcharge/models/account_payment_term_surcharge.py @@ -1,63 +1,72 @@ -from odoo import api, fields, models, _ -from odoo.exceptions import ValidationError from dateutil.relativedelta import relativedelta +from odoo import _, api, fields, models +from odoo.exceptions import ValidationError class AccountPaymentTermSurcharge(models.Model): + _name = "account.payment.term.surcharge" + _description = "Payment Terms Surcharge" + _order = "sequence, id" - _name = 'account.payment.term.surcharge' - _description = 'Payment Terms Surcharge' - _order = 'sequence, id' - - payment_term_id = fields.Many2one('account.payment.term', string='Payment Terms', required=True, index=True, ondelete='cascade') + payment_term_id = fields.Many2one( + "account.payment.term", string="Payment Terms", required=True, index=True, ondelete="cascade" + ) surcharge = fields.Float(string="Surcharge [%]") - days = fields.Integer(string='Number of Days', required=True, default=0) - day_of_the_month = fields.Integer(string='Day of the month', help="Day of the month on which the invoice must come to its term. If zero or negative, this value will be ignored, and no specific day will be set. If greater than the last day of a month, this number will instead select the last day of this month.") - option = fields.Selection([ - ('day_after_invoice_date', "days after the invoice date"), - ('after_invoice_month', "days after the end of the invoice month"), - ('day_following_month', "of the following month"), - ('day_current_month', "of the current month"), + days = fields.Integer(string="Number of Days", required=True, default=0) + day_of_the_month = fields.Integer( + string="Day of the month", + help="Day of the month on which the invoice must come to its term. If zero or negative, this value will be ignored, and no specific day will be set. If greater than the last day of a month, this number will instead select the last day of this month.", + ) + option = fields.Selection( + [ + ("day_after_invoice_date", "days after the invoice date"), + ("after_invoice_month", "days after the end of the invoice month"), + ("day_following_month", "of the following month"), + ("day_current_month", "of the current month"), ], - default='day_after_invoice_date', required=True, string='Options' - ) - sequence = fields.Integer(default=10, help="Gives the sequence order when displaying a list of payment terms lines.") + default="day_after_invoice_date", + required=True, + string="Options", + ) + sequence = fields.Integer( + default=10, help="Gives the sequence order when displaying a list of payment terms lines." + ) - @api.constrains('surcharge') + @api.constrains("surcharge") def _check_percent(self): for term_surcharge in self: - if (term_surcharge.surcharge < 0.0 or term_surcharge.surcharge > 100.0): - raise ValidationError(_('Percentages on the Payment Terms lines must be between 0 and 100.')) + if term_surcharge.surcharge < 0.0 or term_surcharge.surcharge > 100.0: + raise ValidationError(_("Percentages on the Payment Terms lines must be between 0 and 100.")) - @api.constrains('days') + @api.constrains("days") def _check_days(self): for term_surcharge in self: - if term_surcharge.option in ('day_following_month', 'day_current_month') and term_surcharge.days <= 0: + if term_surcharge.option in ("day_following_month", "day_current_month") and term_surcharge.days <= 0: raise ValidationError(_("The day of the month used for this term must be strictly positive.")) elif term_surcharge.days < 0: raise ValidationError(_("The number of days used for a payment term cannot be negative.")) - @api.onchange('option') + @api.onchange("option") def _onchange_option(self): - if self.option in ('day_current_month', 'day_following_month'): + if self.option in ("day_current_month", "day_following_month"): self.days = 0 def _calculate_date(self, date_ref=None): - ''' Se retorna la fecha de un recargo segun una fecha dada, esto se hace - teniendo en cuenta la configuracion propia del recargo. ''' + """Se retorna la fecha de un recargo segun una fecha dada, esto se hace + teniendo en cuenta la configuracion propia del recargo.""" date_ref = date_ref or fields.Date.today() next_date = fields.Date.from_string(date_ref) - if self.option == 'day_after_invoice_date': + if self.option == "day_after_invoice_date": next_date += relativedelta(days=self.days) if self.day_of_the_month > 0: months_delta = (self.day_of_the_month < next_date.day) and 1 or 0 next_date += relativedelta(day=self.day_of_the_month, months=months_delta) - elif self.option == 'after_invoice_month': + elif self.option == "after_invoice_month": next_first_date = next_date + relativedelta(day=1, months=1) # Getting 1st of next month next_date = next_first_date + relativedelta(days=self.days - 1) - elif self.option == 'day_following_month': + elif self.option == "day_following_month": next_date += relativedelta(day=self.days, months=1) - elif self.option == 'day_current_month': + elif self.option == "day_current_month": next_date += relativedelta(day=self.days, months=0) if date_ref >= next_date: next_date = date_ref + relativedelta(day=self.days, months=1) diff --git a/account_payment_term_surcharge/models/res_company.py b/account_payment_term_surcharge/models/res_company.py index 8ece03b71..82982911c 100644 --- a/account_payment_term_surcharge/models/res_company.py +++ b/account_payment_term_surcharge/models/res_company.py @@ -1,11 +1,12 @@ -from odoo import api, exceptions, fields, models, _ +from odoo import fields, models + class ResCompany(models.Model): - _inherit = 'res.company' + _inherit = "res.company" payment_term_surcharge_product_id = fields.Many2one( - 'product.product', - 'Surcharge Product', + "product.product", + "Surcharge Product", ) payment_term_surcharge_invoice_auto_post = fields.Boolean() diff --git a/account_payment_term_surcharge/tests/__init__.py b/account_payment_term_surcharge/tests/__init__.py index 0bce961eb..b97dfe807 100644 --- a/account_payment_term_surcharge/tests/__init__.py +++ b/account_payment_term_surcharge/tests/__init__.py @@ -1 +1 @@ -from . import test_account_payment_term_surcharge \ No newline at end of file +from . import test_account_payment_term_surcharge diff --git a/account_payment_term_surcharge/tests/test_account_payment_term_surcharge.py b/account_payment_term_surcharge/tests/test_account_payment_term_surcharge.py index 122c0c57e..2e1d1e098 100644 --- a/account_payment_term_surcharge/tests/test_account_payment_term_surcharge.py +++ b/account_payment_term_surcharge/tests/test_account_payment_term_surcharge.py @@ -1,52 +1,65 @@ -from odoo.tests import tagged, common -from odoo import Command, fields from datetime import timedelta +from odoo import Command, fields +from odoo.tests import common, tagged + class TestAccountPaymentTermSurcharge(common.TransactionCase): - def setUp(self): super().setUp() self.today = fields.Date.today() - self.first_company = self.env['res.company'].search([], limit=1) - self.partner_ri = self.env['res.partner'].search([], limit=1) - self.first_company_journal = self.env['account.journal'].search([('company_id', '=', self.first_company.id),('type', '=', 'sale')]) + self.first_company = self.env["res.company"].search([], limit=1) + self.partner_ri = self.env["res.partner"].search([], limit=1) + self.first_company_journal = self.env["account.journal"].search( + [("company_id", "=", self.first_company.id), ("type", "=", "sale")] + ) - self.product_surcharge = self.env.ref('product.product_product_16') + self.product_surcharge = self.env.ref("product.product_product_16") self.first_company.payment_term_surcharge_product_id = self.product_surcharge.id - self.payment_term = self.env['account.payment.term'].create({ - 'name': 'Test payment term' - }) + self.payment_term = self.env["account.payment.term"].create({"name": "Test payment term"}) - self.surcharge = self.env['account.payment.term.surcharge'].create({ - 'surcharge': 10, - 'days': 1, - 'payment_term_id': self.payment_term.id, - 'option': 'day_after_invoice_date', - 'day_of_the_month': 0 - }) + self.surcharge = self.env["account.payment.term.surcharge"].create( + { + "surcharge": 10, + "days": 1, + "payment_term_id": self.payment_term.id, + "option": "day_after_invoice_date", + "day_of_the_month": 0, + } + ) - @tagged("-at_install", "post_install",) + @tagged( + "-at_install", + "post_install", + ) def test_payment_term_surcharge(self): - invoice = self.env['account.move'].create({ - 'partner_id': self.partner_ri.id, - 'invoice_date': self.today - timedelta(days=1), - 'invoice_date_due': self.today, - 'move_type': 'out_invoice', - 'journal_id': self.first_company_journal.id, - 'company_id': self.first_company.id, - 'invoice_payment_term_id': self.payment_term.id, - 'invoice_line_ids': [ - Command.create({ - 'product_id': self.env.ref('product.product_product_16').id, - 'quantity': 1, - 'price_unit': 1000, - }), - ] - }) + invoice = self.env["account.move"].create( + { + "partner_id": self.partner_ri.id, + "invoice_date": self.today - timedelta(days=1), + "invoice_date_due": self.today, + "move_type": "out_invoice", + "journal_id": self.first_company_journal.id, + "company_id": self.first_company.id, + "invoice_payment_term_id": self.payment_term.id, + "invoice_line_ids": [ + Command.create( + { + "product_id": self.env.ref("product.product_product_16").id, + "quantity": 1, + "price_unit": 1000, + } + ), + ], + } + ) invoice.action_post() invoice.avoid_surcharge_invoice = False invoice._cron_recurring_surcharges_invoices() self.assertFalse(invoice.next_surcharge_date, "La proxima fecha de recargo no es la correspondiente ") - self.assertEqual(invoice.debit_note_ids[0].amount_total, invoice.amount_total / self.surcharge.surcharge, "Fallo el monto de la ND por el recargo") + self.assertEqual( + invoice.debit_note_ids[0].amount_total, + invoice.amount_total / self.surcharge.surcharge, + "Fallo el monto de la ND por el recargo", + ) diff --git a/account_payment_term_surcharge/views/account_payment_term_surcharge_view.xml b/account_payment_term_surcharge/views/account_payment_term_surcharge_view.xml index 616ef0608..c347a38a2 100644 --- a/account_payment_term_surcharge/views/account_payment_term_surcharge_view.xml +++ b/account_payment_term_surcharge/views/account_payment_term_surcharge_view.xml @@ -42,6 +42,3 @@ - - - diff --git a/account_payment_term_surcharge/views/account_payment_term_view.xml b/account_payment_term_surcharge/views/account_payment_term_view.xml index 12959712a..bc870fbd8 100644 --- a/account_payment_term_surcharge/views/account_payment_term_view.xml +++ b/account_payment_term_surcharge/views/account_payment_term_view.xml @@ -12,7 +12,7 @@ diff --git a/account_payment_term_surcharge/wizard/res_config_settings.py b/account_payment_term_surcharge/wizard/res_config_settings.py index f856fb602..b6e6870d9 100644 --- a/account_payment_term_surcharge/wizard/res_config_settings.py +++ b/account_payment_term_surcharge/wizard/res_config_settings.py @@ -2,15 +2,17 @@ class ResConfigSettings(models.TransientModel): - _inherit = 'res.config.settings' + _inherit = "res.config.settings" payment_term_surcharge_product_id = fields.Many2one( - 'product.product', - related='company_id.payment_term_surcharge_product_id', - string="Producto por defecto para los recargos", readonly=False + "product.product", + related="company_id.payment_term_surcharge_product_id", + string="Producto por defecto para los recargos", + readonly=False, ) payment_term_surcharge_invoice_auto_post = fields.Boolean( - related='company_id.payment_term_surcharge_invoice_auto_post', - string= 'Validad automaticamente las facturas', readonly=False + related="company_id.payment_term_surcharge_invoice_auto_post", + string="Validad automaticamente las facturas", + readonly=False, ) diff --git a/account_ux/__manifest__.py b/account_ux/__manifest__.py index 37bd537f8..5048f8617 100644 --- a/account_ux/__manifest__.py +++ b/account_ux/__manifest__.py @@ -18,46 +18,44 @@ # ############################################################################## { - 'name': 'Account UX', - 'version': "18.0.1.5.0", - 'category': 'Accounting', - 'sequence': 14, - 'summary': '', - 'author': 'ADHOC SA', - 'website': 'www.adhoc.com.ar', - 'license': 'AGPL-3', - 'images': [ - ], - 'depends': [ + "name": "Account UX", + "version": "18.0.1.5.0", + "category": "Accounting", + "sequence": 14, + "summary": "", + "author": "ADHOC SA", + "website": "www.adhoc.com.ar", + "license": "AGPL-3", + "images": [], + "depends": [ "account", "sale", "base_vat", "account_debit_note", ], - 'data': [ - 'security/account_ux_security.xml', - 'security/ir.model.access.csv', - 'wizards/account_change_currency_views.xml', - 'wizards/account_move_change_rate_views.xml', - 'wizards/res_config_settings_views.xml', - 'views/account_journal_views.xml', - 'views/account_move_line_views.xml', - 'views/account_reconcile_views.xml', - 'views/res_partner_views.xml', - 'views/account_partial_reconcile_views.xml', - 'views/account_move_views.xml', - 'views/account_payment_views.xml', - 'views/res_config_settings_views.xml', - 'views/report_payment_receipt_templates.xml', - 'views/account_tax_view.xml', - 'reports/account_invoice_report_view.xml', - ], - 'demo': [ + "data": [ + "security/account_ux_security.xml", + "security/ir.model.access.csv", + "wizards/account_change_currency_views.xml", + "wizards/account_move_change_rate_views.xml", + "wizards/res_config_settings_views.xml", + "views/account_journal_views.xml", + "views/account_move_line_views.xml", + "views/account_reconcile_views.xml", + "views/res_partner_views.xml", + "views/account_partial_reconcile_views.xml", + "views/account_move_views.xml", + "views/account_payment_views.xml", + "views/res_config_settings_views.xml", + "views/report_payment_receipt_templates.xml", + "views/account_tax_view.xml", + "reports/account_invoice_report_view.xml", ], - 'installable': True, + "demo": [], + "installable": True, # lo hacemos auto install porque este repo no lo podemos agregar en otros # por build de travis (ej sipreco) y queremos que para runbot se auto # instale - 'auto_install': True, - 'application': False, + "auto_install": True, + "application": False, } diff --git a/account_ux/migrations/12.0.1.3.0/pre-migration.py b/account_ux/migrations/12.0.1.3.0/pre-migration.py index d646ff267..23ee78bf3 100644 --- a/account_ux/migrations/12.0.1.3.0/pre-migration.py +++ b/account_ux/migrations/12.0.1.3.0/pre-migration.py @@ -7,6 +7,6 @@ def migrate(env, version): The objective of this is delete the original view form the module how bring the functionality adding in the previous commit """ - view = env.ref('account_multicompany_ux.res_config_settings_view_form', raise_if_not_found=False) + view = env.ref("account_multicompany_ux.res_config_settings_view_form", raise_if_not_found=False) if view: view.unlink() diff --git a/account_ux/models/account_group.py b/account_ux/models/account_group.py index 51782aed4..e413bde73 100644 --- a/account_ux/models/account_group.py +++ b/account_ux/models/account_group.py @@ -2,8 +2,8 @@ # For copyright and license notices, see __manifest__.py file in module root # directory ############################################################################## -from odoo import models, fields +from odoo import models class AccountGroup(models.Model): - _inherit = 'account.group' + _inherit = "account.group" diff --git a/account_ux/models/account_journal.py b/account_ux/models/account_journal.py index f9088255f..1962b7ba0 100644 --- a/account_ux/models/account_journal.py +++ b/account_ux/models/account_journal.py @@ -2,35 +2,37 @@ # For copyright and license notices, see __manifest__.py file in module root # directory ############################################################################## -from odoo import models, api, fields, _ +from odoo import _, api, fields, models from odoo.exceptions import ValidationError class AccountJournal(models.Model): - _inherit = 'account.journal' + _inherit = "account.journal" mail_template_id = fields.Many2one( - 'mail.template', - 'Email Template', - domain=[('model', '=', 'account.move')], + "mail.template", + "Email Template", + domain=[("model", "=", "account.move")], help="If set an email will be sent to the customer after the invoices" " related to this journal has been validated.", ) - @api.constrains('currency_id') + @api.constrains("currency_id") def check_currency(self): for rec in self.filtered(lambda x: x.currency_id == x.company_id.currency_id): - raise ValidationError(_( - 'Solo puede utilizar una moneda secundaria distinta a la ' - 'moneda de la compañía (%s).', rec.company_id.currency_id.name)) + raise ValidationError( + _( + "Solo puede utilizar una moneda secundaria distinta a la " "moneda de la compañía (%s).", + rec.company_id.currency_id.name, + ) + ) def write(self, vals): - """ We need to allow to change to False the value for restricted for hash for the journal when this value is setted. - """ - if 'restrict_mode_hash_table' in vals and not vals.get('restrict_mode_hash_table'): - restrict_mode_hash_table = vals.get('restrict_mode_hash_table') - vals.pop('restrict_mode_hash_table') + """We need to allow to change to False the value for restricted for hash for the journal when this value is setted.""" + if "restrict_mode_hash_table" in vals and not vals.get("restrict_mode_hash_table"): + restrict_mode_hash_table = vals.get("restrict_mode_hash_table") + vals.pop("restrict_mode_hash_table") res = super().write(vals) - self._write({'restrict_mode_hash_table': restrict_mode_hash_table}) + self._write({"restrict_mode_hash_table": restrict_mode_hash_table}) return res return super().write(vals) diff --git a/account_ux/models/account_move.py b/account_ux/models/account_move.py index ff05b70e9..7f2aa821d 100644 --- a/account_ux/models/account_move.py +++ b/account_ux/models/account_move.py @@ -8,18 +8,18 @@ class AccountMove(models.Model): _inherit = "account.move" internal_notes = fields.Html() - inverse_invoice_currency_rate = fields.Float(compute='_compute_inverse_invoice_currency_rate') + inverse_invoice_currency_rate = fields.Float(compute="_compute_inverse_invoice_currency_rate") def get_invoice_report(self): self.ensure_one() - bin_data, __ = self.env['ir.actions.report']._render_qweb_pdf('account.account_invoices', self.id) + bin_data, __ = self.env["ir.actions.report"]._render_qweb_pdf("account.account_invoices", self.id) return bin_data, __ def delete_number(self): - self.filtered(lambda x: x.state == 'cancel').write({'name': '/'}) + self.filtered(lambda x: x.state == "cancel").write({"name": "/"}) def action_post(self): - """ After validate invoice will sent an email to the partner if the related journal has mail_template_id set """ + """After validate invoice will sent an email to the partner if the related journal has mail_template_id set""" res = super().action_post() self.action_send_invoice_mail() return res @@ -27,15 +27,10 @@ def action_post(self): def action_send_invoice_mail(self): for rec in self.filtered(lambda x: x.is_invoice(include_receipts=True) and x.journal_id.mail_template_id): try: - rec.message_post_with_source( - rec.journal_id.mail_template_id, - subtype_xmlid='mail.mt_comment' - ) - rec.is_move_sent= True + rec.message_post_with_source(rec.journal_id.mail_template_id, subtype_xmlid="mail.mt_comment") + rec.is_move_sent = True except Exception as error: - title = _( - "ERROR: Invoice was not sent via email" - ) + title = _("ERROR: Invoice was not sent via email") # message = _( # "Invoice %s was correctly validate but was not send" # " via email. Please review invoice chatter for more" @@ -46,15 +41,18 @@ def action_send_invoice_mail(self): # message=message, # sticky=True, # ) - rec.message_post(body="

".join([ - "" + title + "", - _("Please check the email template associated with" - " the invoice journal."), - "" + str(error) + "" - ]), body_is_html=True + rec.message_post( + body="

".join( + [ + "" + title + "", + _("Please check the email template associated with" " the invoice journal."), + "" + str(error) + "", + ] + ), + body_is_html=True, ) - @api.onchange('partner_id') + @api.onchange("partner_id") def _onchange_partner_commercial(self): if self.partner_id.user_id: self.invoice_user_id = self.partner_id.user_id.id @@ -72,8 +70,13 @@ def get_accounting_rate(company_currency, amount, amount_currency, currency): else: return abs(amount_currency) / abs(amount) - rate = get_accounting_rate(move.company_id.currency_id, move.amount_total_signed, move.amount_total_in_currency_signed, move.currency_id) - amount = abs(line.amount_residual) * rate + rate = get_accounting_rate( + move.company_id.currency_id, + move.amount_total_signed, + move.amount_total_in_currency_signed, + move.currency_id, + ) + amount = abs(line.amount_residual) * rate return amount ### Comentamos este método debido a que el campo invoice_outstanding_credits_debits_widget no se estaba seteando correctamente en super @@ -113,27 +116,35 @@ def get_accounting_rate(company_currency, amount, amount_currency, currency): # amount_residual = self.env['account.move.line'].browse(item['id']).amount_residual # item['amount'] = move.currency_id.round(amount_residual * rate) - @api.depends('invoice_date') + @api.depends("invoice_date") def _compute_invoice_date_due(self): - """ Si la factura no tiene término de pago y la misma tiene fecha de vencimiento anterior al día de hoy y la factura no tiene fecha entonces cuando se publica la factura, la fecha de vencimiento tiene que coincidir con la fecha de hoy. """ - invoices_with_old_data_due = self.filtered(lambda x: x.invoice_date and not x.invoice_payment_term_id and (not x.invoice_date_due or x.invoice_date_due < x.invoice_date)) + """Si la factura no tiene término de pago y la misma tiene fecha de vencimiento anterior al día de hoy y la factura no tiene fecha entonces cuando se publica la factura, la fecha de vencimiento tiene que coincidir con la fecha de hoy.""" + invoices_with_old_data_due = self.filtered( + lambda x: x.invoice_date + and not x.invoice_payment_term_id + and (not x.invoice_date_due or x.invoice_date_due < x.invoice_date) + ) invoices = self - invoices_with_old_data_due for inv in invoices_with_old_data_due: if inv.invoice_date: inv.invoice_date_due = inv.invoice_date return super(AccountMove, invoices)._compute_invoice_date_due() - @api.constrains('date', 'invoice_date') + @api.constrains("date", "invoice_date") def _check_dates_on_invoices(self): - """ Prevenir que en facturas de cliente queden distintos los campos de factura/recibo y fecha (date e invoice date). Pueden quedar distintos si se modifica alguna de esas fechas a través de edición masiva por ejemplo, entonces con esta constrains queremos prevenir que eso suceda. """ - invoices_to_check = self.filtered(lambda x: x.date!=x.invoice_date if x.is_sale_document() and x.date and x.invoice_date else False) + """Prevenir que en facturas de cliente queden distintos los campos de factura/recibo y fecha (date e invoice date). Pueden quedar distintos si se modifica alguna de esas fechas a través de edición masiva por ejemplo, entonces con esta constrains queremos prevenir que eso suceda.""" + invoices_to_check = self.filtered( + lambda x: x.date != x.invoice_date if x.is_sale_document() and x.date and x.invoice_date else False + ) if invoices_to_check: - error_msg = _('\nDate\t\t\tInvoice Date\t\tInvoice\n') + error_msg = _("\nDate\t\t\tInvoice Date\t\tInvoice\n") for rec in invoices_to_check: - error_msg += str(rec.date) + '\t'*2 + str(rec.invoice_date) + '\t'*3 + rec.display_name + '\n' - raise UserError(_('The date and invoice date of a sale invoice must be the same: %s') % (error_msg)) + error_msg += str(rec.date) + "\t" * 2 + str(rec.invoice_date) + "\t" * 3 + rec.display_name + "\n" + raise UserError(_("The date and invoice date of a sale invoice must be the same: %s") % (error_msg)) - @api.depends('invoice_currency_rate') + @api.depends("invoice_currency_rate") def _compute_inverse_invoice_currency_rate(self): for record in self: - record.inverse_invoice_currency_rate = 1 / record.invoice_currency_rate if record.invoice_currency_rate else 1.0 + record.inverse_invoice_currency_rate = ( + 1 / record.invoice_currency_rate if record.invoice_currency_rate else 1.0 + ) diff --git a/account_ux/models/account_move_line.py b/account_ux/models/account_move_line.py index 0bc17e07f..c73db0b39 100644 --- a/account_ux/models/account_move_line.py +++ b/account_ux/models/account_move_line.py @@ -9,11 +9,14 @@ class AccountMoveLine(models.Model): _inherit = "account.move.line" user_id = fields.Many2one( - string='Contact Salesperson', related='partner_id.user_id', store=True, - help='Salesperson of contact related to this journal item') + string="Contact Salesperson", + related="partner_id.user_id", + store=True, + help="Salesperson of contact related to this journal item", + ) # lo agregamos para que al agrupar en vista tree se vea y ademas que aparezca com messure en la pivot amount_residual_currency = fields.Monetary( - aggregator='sum', + aggregator="sum", ) @api.model @@ -49,50 +52,73 @@ def _prepare_reconciliation_single_partial(self, debit_values, credit_values, sh """ def get_accounting_rate(vals): - if company_debit_currency.is_zero(abs(vals['aml'].balance)) or vals['currency'].is_zero(vals['aml'].amount_currency): + if company_debit_currency.is_zero(abs(vals["aml"].balance)) or vals["currency"].is_zero( + vals["aml"].amount_currency + ): return 0.0 else: - return abs(vals['aml'].amount_currency) / abs(vals['aml'].balance) + return abs(vals["aml"].amount_currency) / abs(vals["aml"].balance) - company_debit_currency = debit_values['aml'].company_currency_id - company_credit_currency = credit_values['aml'].company_currency_id - reconcile_on_company_currency = debit_values['aml'].company_id.reconcile_on_company_currency and \ - (debit_values['aml'].currency_id != company_debit_currency or credit_values['aml'].currency_id != company_debit_currency) and \ - not debit_values['aml'].account_id.currency_id + company_debit_currency = debit_values["aml"].company_currency_id + company_credit_currency = credit_values["aml"].company_currency_id + reconcile_on_company_currency = ( + debit_values["aml"].company_id.reconcile_on_company_currency + and ( + debit_values["aml"].currency_id != company_debit_currency + or credit_values["aml"].currency_id != company_debit_currency + ) + and not debit_values["aml"].account_id.currency_id + ) if reconcile_on_company_currency: shadowed_aml_values = {} - if debit_values['aml'].currency_id != company_debit_currency: - debit_values['original_currency'] = debit_values['aml'].currency_id - debit_values['currency'] = company_debit_currency - debit_values['amount_residual_currency'] = debit_values['amount_residual'] - shadowed_aml_values[debit_values['aml']] = {'currency_id': company_debit_currency, - 'amount_residual_currency': debit_values['amount_residual']} - if credit_values['aml'].currency_id != company_credit_currency: - credit_values['original_currency'] = credit_values['aml'].currency_id - credit_values['currency'] = company_credit_currency - credit_values['amount_residual_currency'] = credit_values['amount_residual'] - shadowed_aml_values[credit_values['aml']] = {'currency_id': company_credit_currency, - 'amount_residual_currency': credit_values['amount_residual']} - res = super(AccountMoveLine, self.with_context(no_exchange_difference=True))._prepare_reconciliation_single_partial(debit_values, credit_values, shadowed_aml_values) + if debit_values["aml"].currency_id != company_debit_currency: + debit_values["original_currency"] = debit_values["aml"].currency_id + debit_values["currency"] = company_debit_currency + debit_values["amount_residual_currency"] = debit_values["amount_residual"] + shadowed_aml_values[debit_values["aml"]] = { + "currency_id": company_debit_currency, + "amount_residual_currency": debit_values["amount_residual"], + } + if credit_values["aml"].currency_id != company_credit_currency: + credit_values["original_currency"] = credit_values["aml"].currency_id + credit_values["currency"] = company_credit_currency + credit_values["amount_residual_currency"] = credit_values["amount_residual"] + shadowed_aml_values[credit_values["aml"]] = { + "currency_id": company_credit_currency, + "amount_residual_currency": credit_values["amount_residual"], + } + res = super( + AccountMoveLine, self.with_context(no_exchange_difference=True) + )._prepare_reconciliation_single_partial(debit_values, credit_values, shadowed_aml_values) else: res = super()._prepare_reconciliation_single_partial(debit_values, credit_values, shadowed_aml_values) - if reconcile_on_company_currency and 'partial_values' in res: - if 'original_currency' in credit_values: - credit_values['currency'] = credit_values['original_currency'] + if reconcile_on_company_currency and "partial_values" in res: + if "original_currency" in credit_values: + credit_values["currency"] = credit_values["original_currency"] rate = get_accounting_rate(credit_values) - res['partial_values']['credit_amount_currency'] = credit_values['aml'].currency_id.round( - res['partial_values']['credit_amount_currency'] * rate) - if 'original_currency' in debit_values: - debit_values['currency'] = debit_values['original_currency'] + res["partial_values"]["credit_amount_currency"] = credit_values["aml"].currency_id.round( + res["partial_values"]["credit_amount_currency"] * rate + ) + if "original_currency" in debit_values: + debit_values["currency"] = debit_values["original_currency"] rate = get_accounting_rate(debit_values) - res['partial_values']['debit_amount_currency'] = credit_values['aml'].currency_id.round( - res['partial_values']['debit_amount_currency'] * rate) + res["partial_values"]["debit_amount_currency"] = credit_values["aml"].currency_id.round( + res["partial_values"]["debit_amount_currency"] * rate + ) return res def _compute_amount_residual(self): - """ Cuando se realiza un cobro de un recibo y el comprobante que se paga tiene moneda secundaria y queda totalmente conciliado en moneda de compañía pero no en moneda secundaria (ejemplo: diferencia de un centavo) lo que hacemos con este método es forzar que quede conciliado también en moneda secundaria. """ + """Cuando se realiza un cobro de un recibo y el comprobante que se paga tiene moneda secundaria y queda totalmente conciliado en moneda de compañía pero no en moneda secundaria (ejemplo: diferencia de un centavo) lo que hacemos con este método es forzar que quede conciliado también en moneda secundaria.""" super()._compute_amount_residual() - need_amount_residual_currency_adjustment = self.filtered(lambda x: not x.reconciled and x.company_id.reconcile_on_company_currency and (x.account_id.reconcile or x.account_id.account_type in ('asset_cash', 'liability_credit_card')) and (x.company_currency_id or self.env.company.currency_id).is_zero(x.amount_residual) and not (x.currency_id or (x.company_currency_id or self.env.company.currency_id)).is_zero(x.amount_residual_currency)) + need_amount_residual_currency_adjustment = self.filtered( + lambda x: not x.reconciled + and x.company_id.reconcile_on_company_currency + and (x.account_id.reconcile or x.account_id.account_type in ("asset_cash", "liability_credit_card")) + and (x.company_currency_id or self.env.company.currency_id).is_zero(x.amount_residual) + and not (x.currency_id or (x.company_currency_id or self.env.company.currency_id)).is_zero( + x.amount_residual_currency + ) + ) need_amount_residual_currency_adjustment.amount_residual_currency = 0.0 need_amount_residual_currency_adjustment.reconciled = True diff --git a/account_ux/models/account_payment.py b/account_ux/models/account_payment.py index d28252cdf..25d9a64f7 100644 --- a/account_ux/models/account_payment.py +++ b/account_ux/models/account_payment.py @@ -1,25 +1,24 @@ -from odoo import models, fields, api +from odoo import api, models class AccountPayment(models.Model): _inherit = "account.payment" - - @api.onchange('available_journal_ids') + @api.onchange("available_journal_ids") def _onchange_available_journal_ids(self): - """ Fix the use case where a journal only suitable for one kind of operation (lets said inbound) is selected + """Fix the use case where a journal only suitable for one kind of operation (lets said inbound) is selected and then the user selects "outbound" type, the journals remains selected.""" if not self.journal_id or self.journal_id not in self.available_journal_ids._origin: self.journal_id = self.available_journal_ids._origin[:1] - @api.depends('invoice_ids.payment_state', 'move_id.line_ids.amount_residual') + @api.depends("invoice_ids.payment_state", "move_id.line_ids.amount_residual") def _compute_state(self): super()._compute_state() for payment in self: if ( - not self.env.context.get('skip_payment_state_computation') and - payment.journal_id.type in ('bank', 'cash') and - payment.outstanding_account_id and - len(payment.move_id.line_ids._reconciled_lines()) > 1 + not self.env.context.get("skip_payment_state_computation") + and payment.journal_id.type in ("bank", "cash") + and payment.outstanding_account_id + and len(payment.move_id.line_ids._reconciled_lines()) > 1 ): - payment.state = 'paid' + payment.state = "paid" diff --git a/account_ux/models/res_company.py b/account_ux/models/res_company.py index 9057225a5..a71d39854 100644 --- a/account_ux/models/res_company.py +++ b/account_ux/models/res_company.py @@ -1,10 +1,11 @@ -from odoo import models, fields +from odoo import fields, models class ResCompany(models.Model): - _inherit = 'res.company' + _inherit = "res.company" reconcile_on_company_currency = fields.Boolean( help="When reconciling debt with secondary currency, if the account doesn't have a currency configured, then" " reconcile on company currency. This will avoid all the automatic exchange rates journal entries by forcing " - " same rate of the original document being reconcile") + " same rate of the original document being reconcile" + ) diff --git a/account_ux/models/res_currency_rate.py b/account_ux/models/res_currency_rate.py index f65757fef..56670a7d3 100644 --- a/account_ux/models/res_currency_rate.py +++ b/account_ux/models/res_currency_rate.py @@ -1,25 +1,29 @@ # © 2018 ADHOC SA # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import api, models, _ +from odoo import _, api, models from odoo.exceptions import ValidationError class ResCurrencyRate(models.Model): + _inherit = "res.currency.rate" - _inherit = 'res.currency.rate' - - @api.constrains('company_id') + @api.constrains("company_id") def _check_date_rate(self): for rec in self.filtered(lambda x: not x.company_id): - others_with_company = self.search([ - ('name', '<=', rec.name), - ('currency_id', '=', rec.currency_id.id), - ('company_id', '!=', False), - ]) + others_with_company = self.search( + [ + ("name", "<=", rec.name), + ("currency_id", "=", rec.currency_id.id), + ("company_id", "!=", False), + ] + ) if others_with_company: - raise ValidationError(_( - 'You can not create a rate without company' - ' since you already have rates before %s with' - ' company set. The rate you want to create will not' - ' have any effect, will not be take into account.' - ) % rec.name) + raise ValidationError( + _( + "You can not create a rate without company" + " since you already have rates before %s with" + " company set. The rate you want to create will not" + " have any effect, will not be take into account." + ) + % rec.name + ) diff --git a/account_ux/reports/invoice_report.py b/account_ux/reports/invoice_report.py index 10ef58312..62fe32d93 100644 --- a/account_ux/reports/invoice_report.py +++ b/account_ux/reports/invoice_report.py @@ -1,29 +1,46 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. -from odoo import models, fields +from odoo import fields, models from odoo.tools import SQL -class AccountInvoiceReport(models.Model): - _inherit = 'account.invoice.report' +class AccountInvoiceReport(models.Model): + _inherit = "account.invoice.report" # agregamos widgets monetary, referencia a company currency en string y help price_subtotal = fields.Monetary( - currency_field='company_currency_id', string="Untaxed Total (CC)", help="Untaxed Total in company currency") - price_total = fields.Monetary(string='Total', currency_field='invoice_currency_id') + currency_field="company_currency_id", string="Untaxed Total (CC)", help="Untaxed Total in company currency" + ) + price_total = fields.Monetary(string="Total", currency_field="invoice_currency_id") price_average = fields.Monetary( - currency_field='company_currency_id', string='Average Price (CC)', help="Average Price in company currency") + currency_field="company_currency_id", string="Average Price (CC)", help="Average Price in company currency" + ) # creamos nuevos campos para tener descuentos, vinculos e importes en moneda de compañía total_cc = fields.Monetary( - string='Total (CC)', readonly=True, help="Untaxed Total in company currency", - currency_field='company_currency_id') - invoice_currency_id = fields.Many2one('res.currency', string='Invoice Currency', readonly=True) - line_id = fields.Many2one('account.move.line', string='Journal Item', readonly=True) - price_subtotal_ic = fields.Monetary('Untaxed Total', readonly=True, currency_field='invoice_currency_id',) - price_unit = fields.Monetary('Unit Price', readonly=True, currency_field='invoice_currency_id',) - discount = fields.Float('Discount (%)', readonly=True) - discount_amount = fields.Monetary(readonly=True, aggregator="sum", currency_field='invoice_currency_id',) + string="Total (CC)", + readonly=True, + help="Untaxed Total in company currency", + currency_field="company_currency_id", + ) + invoice_currency_id = fields.Many2one("res.currency", string="Invoice Currency", readonly=True) + line_id = fields.Many2one("account.move.line", string="Journal Item", readonly=True) + price_subtotal_ic = fields.Monetary( + "Untaxed Total", + readonly=True, + currency_field="invoice_currency_id", + ) + price_unit = fields.Monetary( + "Unit Price", + readonly=True, + currency_field="invoice_currency_id", + ) + discount = fields.Float("Discount (%)", readonly=True) + discount_amount = fields.Monetary( + readonly=True, + aggregator="sum", + currency_field="invoice_currency_id", + ) - _depends = {'account.move.line': ['price_unit', 'discount']} + _depends = {"account.move.line": ["price_unit", "discount"]} def _select(self): query = SQL(""" diff --git a/account_ux/tests/test_account_ux.py b/account_ux/tests/test_account_ux.py index 7fa4fff50..20e2a40f3 100644 --- a/account_ux/tests/test_account_ux.py +++ b/account_ux/tests/test_account_ux.py @@ -3,44 +3,53 @@ class TestAccountUXChangeCurrency(common.TransactionCase): - def setUp(self): super().setUp() self.today = fields.Date.today() - self.first_company = self.env['res.company'].search([], limit=1) - self.partner_ri = self.env['res.partner'].search([], limit=1) + self.first_company = self.env["res.company"].search([], limit=1) + self.partner_ri = self.env["res.partner"].search([], limit=1) - self.currency_usd = self.env['res.currency'].search([('name', '=', 'USD')]) - self.currency_ars = self.env['res.currency'].search([('name', '=', 'ARS'), ('active', 'in', [False])]) + self.currency_usd = self.env["res.currency"].search([("name", "=", "USD")]) + self.currency_ars = self.env["res.currency"].search([("name", "=", "ARS"), ("active", "in", [False])]) self.currency_ars.active = True - self.first_company_journal_usd = self.env['account.journal'].search([('company_id', '=', self.first_company.id), ('type', '=', 'sale')], limit=1) - self.first_company_journal_ars = self.env['account.journal'].create({ - 'name': 'ARS sale journal', - 'company_id': self.first_company.id, - 'type': 'sale', - 'currency_id': self.currency_ars.id, - 'code': 'ARS' - }) + self.first_company_journal_usd = self.env["account.journal"].search( + [("company_id", "=", self.first_company.id), ("type", "=", "sale")], limit=1 + ) + self.first_company_journal_ars = self.env["account.journal"].create( + { + "name": "ARS sale journal", + "company_id": self.first_company.id, + "type": "sale", + "currency_id": self.currency_ars.id, + "code": "ARS", + } + ) def test_account_ux_change_currency(self): - invoice = self.env['account.move'].create({ - 'partner_id': self.partner_ri.id, - 'date': self.today, - 'move_type': 'out_invoice', - 'journal_id': self.first_company_journal_usd.id, - 'company_id': self.first_company.id, - 'invoice_line_ids': [ - Command.create({ - 'product_id': self.env.ref('product.product_product_16').id, - 'quantity': 1, - 'price_unit': 1000, - }), - ], - }) - invoice.write({ - 'journal_id': self.first_company_journal_ars.id - }) + invoice = self.env["account.move"].create( + { + "partner_id": self.partner_ri.id, + "date": self.today, + "move_type": "out_invoice", + "journal_id": self.first_company_journal_usd.id, + "company_id": self.first_company.id, + "invoice_line_ids": [ + Command.create( + { + "product_id": self.env.ref("product.product_product_16").id, + "quantity": 1, + "price_unit": 1000, + } + ), + ], + } + ) + invoice.write({"journal_id": self.first_company_journal_ars.id}) invoice.action_post() - self.assertEqual(invoice.currency_id, self.first_company_journal_ars.currency_id, "La moneda de la factura no esta siendo modificada al cambiar el diario.") + self.assertEqual( + invoice.currency_id, + self.first_company_journal_ars.currency_id, + "La moneda de la factura no esta siendo modificada al cambiar el diario.", + ) diff --git a/account_ux/views/account_move_views.xml b/account_ux/views/account_move_views.xml index d376a37dc..47d44fd07 100644 --- a/account_ux/views/account_move_views.xml +++ b/account_ux/views/account_move_views.xml @@ -142,7 +142,7 @@ - + account.move.line.form account.move.line diff --git a/account_ux/views/account_tax_view.xml b/account_ux/views/account_tax_view.xml index 8fc60bcae..c77a78140 100644 --- a/account_ux/views/account_tax_view.xml +++ b/account_ux/views/account_tax_view.xml @@ -16,4 +16,3 @@ - diff --git a/account_ux/wizards/account_change_currency.py b/account_ux/wizards/account_change_currency.py index 6d3dcd6d1..d2c3c0304 100644 --- a/account_ux/wizards/account_change_currency.py +++ b/account_ux/wizards/account_change_currency.py @@ -2,51 +2,40 @@ # For copyright and license notices, see __manifest__.py file in module root # directory ############################################################################## -from odoo import fields, models, api, _ +from odoo import _, api, fields, models class AccountChangeCurrency(models.TransientModel): - _name = 'account.change.currency' - _description = 'Change Currency' + _name = "account.change.currency" + _description = "Change Currency" @api.model def get_move(self): - move = self.env['account.move'].browse( - self._context.get('active_id', False)) + move = self.env["account.move"].browse(self._context.get("active_id", False)) return move currency_from_id = fields.Many2one( - 'res.currency', - string='Currency From', - related='move_id.currency_id', - help="Currency from Invoice" + "res.currency", string="Currency From", related="move_id.currency_id", help="Currency from Invoice" ) currency_to_id = fields.Many2one( - 'res.currency', - string='Currency to', + "res.currency", + string="Currency to", required=True, help="Select a currency to apply on the invoice", ) conversion_date = fields.Float( - 'Conversion Rate', - required=True, - digits=0, - help="Select a rate to apply on the invoice" - ) - move_id = fields.Many2one( - 'account.move', - default=get_move + "Conversion Rate", required=True, digits=0, help="Select a rate to apply on the invoice" ) + move_id = fields.Many2one("account.move", default=get_move) - @api.onchange('currency_to_id') + @api.onchange("currency_to_id") def onchange_currency(self): if not self.currency_to_id: self.conversion_date = False else: - currency = self.currency_from_id.with_context( - ) + currency = self.currency_from_id.with_context() - self.conversion_date = self.env['res.currency']._get_conversion_rate( + self.conversion_date = self.env["res.currency"]._get_conversion_rate( from_currency=currency, to_currency=self.currency_to_id, company=self.move_id.company_id, @@ -56,8 +45,10 @@ def onchange_currency(self): def change_currency(self): self.ensure_one() message = _("Currency changed from %s to %s with rate %s") % ( - self.move_id.currency_id.name, self.currency_to_id.name, - self.conversion_date) + self.move_id.currency_id.name, + self.currency_to_id.name, + self.conversion_date, + ) move = self.move_id.with_context(check_move_validity=False) move.currency_id = self.currency_to_id.id @@ -68,4 +59,4 @@ def change_currency(self): line.price_unit = line.price_unit * self.conversion_date self.move_id.message_post(body=message) - return {'type': 'ir.actions.act_window_close'} + return {"type": "ir.actions.act_window_close"} diff --git a/account_ux/wizards/account_move_change_rate.py b/account_ux/wizards/account_move_change_rate.py index b8a3b2f35..a087bc14b 100644 --- a/account_ux/wizards/account_move_change_rate.py +++ b/account_ux/wizards/account_move_change_rate.py @@ -2,36 +2,31 @@ # For copyright and license notices, see __manifest__.py file in module root # directory ############################################################################## -from odoo import fields, models, api, _ +from odoo import _, api, fields, models from odoo.tools import float_round class AccountMoveChangeRate(models.TransientModel): - _name = 'account.move.change.rate' - _description = 'account.move.change.rate' + _name = "account.move.change.rate" + _description = "account.move.change.rate" @api.model def get_move(self): - move = self.env['account.move'].browse( - self._context.get('active_id', False)) + move = self.env["account.move"].browse(self._context.get("active_id", False)) return move - currency_rate = fields.Float( - required=True, - digits=(16, 6), - help="Select a rate to apply on the invoice" - ) - move_id = fields.Many2one( - 'account.move', - default=get_move - ) + currency_rate = fields.Float(required=True, digits=(16, 6), help="Select a rate to apply on the invoice") + move_id = fields.Many2one("account.move", default=get_move) - @api.onchange('move_id') + @api.onchange("move_id") def _onchange_move(self): self.currency_rate = self.move_id.inverse_invoice_currency_rate def confirm(self): - message = _("Currency rate changed from '%s' to '%s' . Currency rate forced") % (float_round(self.move_id.inverse_invoice_currency_rate, 2), float_round(self.currency_rate, 2)) - self.move_id.write({'invoice_currency_rate': 1 / self.currency_rate}) + message = _("Currency rate changed from '%s' to '%s' . Currency rate forced") % ( + float_round(self.move_id.inverse_invoice_currency_rate, 2), + float_round(self.currency_rate, 2), + ) + self.move_id.write({"invoice_currency_rate": 1 / self.currency_rate}) self.move_id.message_post(body=message) - return {'type': 'ir.actions.act_window_close'} + return {"type": "ir.actions.act_window_close"} diff --git a/account_ux/wizards/res_config_settings.py b/account_ux/wizards/res_config_settings.py index 74acd7640..7cbd1b316 100644 --- a/account_ux/wizards/res_config_settings.py +++ b/account_ux/wizards/res_config_settings.py @@ -2,13 +2,10 @@ # For copyright and license notices, see __manifest__.py file in module root # directory ############################################################################## -from odoo import fields, models, api -from odoo.exceptions import ValidationError +from odoo import fields, models class ResConfigSettings(models.TransientModel): + _inherit = "res.config.settings" - _inherit = 'res.config.settings' - - reconcile_on_company_currency = fields.Boolean( - related='company_id.reconcile_on_company_currency', readonly=False) + reconcile_on_company_currency = fields.Boolean(related="company_id.reconcile_on_company_currency", readonly=False) diff --git a/stock_account_ux/__manifest__.py b/stock_account_ux/__manifest__.py index 2c297da51..fdb2ce0b4 100644 --- a/stock_account_ux/__manifest__.py +++ b/stock_account_ux/__manifest__.py @@ -18,27 +18,25 @@ # ############################################################################## { - 'name': 'Stock Account UX', - 'version': "18.0.1.0.0", - 'category': 'Accounting', - 'sequence': 14, - 'summary': '', - 'author': 'ADHOC SA', - 'website': 'www.adhoc.com.ar', - 'license': 'AGPL-3', - 'images': [ - ], - 'depends': [ - 'account', + "name": "Stock Account UX", + "version": "18.0.1.0.0", + "category": "Accounting", + "sequence": 14, + "summary": "", + "author": "ADHOC SA", + "website": "www.adhoc.com.ar", + "license": "AGPL-3", + "images": [], + "depends": [ + "account", "stock_account", ], - 'data':[ - 'views/account_move_views.xml', - ], - 'demo': [ + "data": [ + "views/account_move_views.xml", ], - 'installable': True, - 'auto_install': False, - 'application': False, - 'post_load': 'monkey_patches', + "demo": [], + "installable": True, + "auto_install": False, + "application": False, + "post_load": "monkey_patches", } diff --git a/stock_account_ux/models/__init__.py b/stock_account_ux/models/__init__.py index 17278721c..edd71d892 100644 --- a/stock_account_ux/models/__init__.py +++ b/stock_account_ux/models/__init__.py @@ -4,4 +4,3 @@ ############################################################################## from . import account_move - diff --git a/stock_account_ux/models/account_move.py b/stock_account_ux/models/account_move.py index 51a226c31..622594c71 100644 --- a/stock_account_ux/models/account_move.py +++ b/stock_account_ux/models/account_move.py @@ -1,12 +1,13 @@ -from odoo import models, fields +from odoo import fields, models + class AccountMove(models.Model): _inherit = "account.move" - allow_move_with_valuation_cancelation = fields.Boolean(compute='_compute_allow_move_with_valuation_cancelation') + allow_move_with_valuation_cancelation = fields.Boolean(compute="_compute_allow_move_with_valuation_cancelation") def _compute_allow_move_with_valuation_cancelation(self): - with_valuation = self.sudo().filtered('line_ids.stock_valuation_layer_ids') + with_valuation = self.sudo().filtered("line_ids.stock_valuation_layer_ids") (self - with_valuation).allow_move_with_valuation_cancelation = False for rec in with_valuation: rec._compute_show_reset_to_draft_button() diff --git a/stock_account_ux/monkey_patches.py b/stock_account_ux/monkey_patches.py index 9279e0cab..ad7917ea7 100644 --- a/stock_account_ux/monkey_patches.py +++ b/stock_account_ux/monkey_patches.py @@ -1,11 +1,10 @@ -from . import models from odoo.addons.stock_account.models.account_move import AccountMove -def monkey_patches(): +def monkey_patches(): # monkey patch def _compute_show_reset_to_draft_button(self): - # Bypasseamos el método _compute_show_reset_to_draft_button de stock account para que vaya al super del padre - super(AccountMove,self)._compute_show_reset_to_draft_button() + # Bypasseamos el método _compute_show_reset_to_draft_button de stock account para que vaya al super del padre + super(AccountMove, self)._compute_show_reset_to_draft_button() AccountMove._compute_show_reset_to_draft_button = _compute_show_reset_to_draft_button