Create module account_payment_mode_default_account

This commit is contained in:
Akim Juillerat
2022-06-30 10:53:04 +02:00
parent 31d375aa93
commit dc18da16da
15 changed files with 405 additions and 0 deletions

View File

@@ -0,0 +1,2 @@
from . import models
from .hooks import post_init_hook, uninstall_hook

View File

@@ -0,0 +1,20 @@
# Copyright 2022 Camptocamp SA
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
{
"name": "Account Payment Mode Default Account",
"summary": "Set Receivable or Payable account according to payment mode",
"version": "14.0.1.0.0",
"development_status": "Alpha",
"category": "Accounting/Accounting",
"website": "https://github.com/OCA/bank-payment",
"author": "Camptocamp, Odoo Community Association (OCA)",
"license": "AGPL-3",
"depends": [
"account_payment_partner",
],
"data": [
"views/account_payment_mode.xml",
],
"post_init_hook": "post_init_hook",
"uninstall_hook": "uninstall_hook",
}

View File

@@ -0,0 +1,39 @@
# Copyright 2022 Camptocamp SA
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
from odoo import SUPERUSER_ID, api
def post_init_hook(cr, registry):
env = api.Environment(cr, SUPERUSER_ID, {})
fields_mapping = [
("property_account_receivable_id", "property_stored_account_receivable_id"),
("property_account_payable_id", "property_stored_account_payable_id"),
]
for orig_fname, new_fname in fields_mapping:
orig_model_field = env["ir.model.fields"]._get("res.partner", orig_fname)
new_model_field = env["ir.model.fields"]._get("res.partner", new_fname)
sql = """
UPDATE ir_property
SET name = %s,
fields_id = %s
WHERE fields_id = %s;
"""
cr.execute(sql, (new_fname, new_model_field.id, orig_model_field.id))
def uninstall_hook(cr, registry):
env = api.Environment(cr, SUPERUSER_ID, {})
fields_mapping = [
("property_account_receivable_id", "property_stored_account_receivable_id"),
("property_account_payable_id", "property_stored_account_payable_id"),
]
for orig_fname, new_fname in fields_mapping:
orig_model_field = env["ir.model.fields"]._get("res.partner", orig_fname)
new_model_field = env["ir.model.fields"]._get("res.partner", new_fname)
sql = """
UPDATE ir_property
SET name = %s,
fields_id = %s
WHERE fields_id = %s;
"""
cr.execute(sql, (orig_fname, orig_model_field.id, new_model_field.id))

View File

@@ -0,0 +1,4 @@
from . import account_move
from . import account_payment_mode
from . import chart_template
from . import res_partner

View File

@@ -0,0 +1,38 @@
# Copyright 2022 Camptocamp SA
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
from odoo import api, models
class AccountMove(models.Model):
_inherit = "account.move"
def _recompute_payment_terms_lines(self):
if self.payment_mode_id:
return super(
AccountMove,
self.with_context(
_partner_property_account_payment_mode=self.payment_mode_id.id
),
)._recompute_payment_terms_lines()
else:
return super()._recompute_payment_terms_lines()
def _get_payment_term_lines(self):
self.ensure_one()
return self.line_ids.filtered(
lambda line: line.account_id.user_type_id.type in ("receivable", "payable")
)
@api.onchange("payment_mode_id")
def _onchange_payment_mode_id(self):
if self.payment_mode_id and self.partner_id:
payment_term_lines = self._get_payment_term_lines()
partner = self.partner_id.with_context(
_partner_property_account_payment_mode=self.payment_mode_id.id
)
# Retrieve account from partner.
if self.is_sale_document(include_receipts=True):
payment_term_lines.account_id = partner.property_account_receivable_id
else:
payment_term_lines.account_id = partner.property_account_payable_id

View File

@@ -0,0 +1,19 @@
# Copyright 2022 Camptocamp SA
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
from odoo import fields, models
class AccountPaymentMode(models.Model):
_inherit = "account.payment.mode"
default_receivable_account_id = fields.Many2one(
"account.account",
domain="[('deprecated', '=', False),('company_id', '=', company_id),('user_type_id.type', '=', 'receivable')]", # noqa
help="This account will be used instead of the default one as the receivable account on invoices using this payment mode", # noqa
)
default_payable_account_id = fields.Many2one(
"account.account",
domain="[('deprecated', '=', False), ('company_id', '=', company_id),('user_type_id.type', '=', 'payable')]", # noqa
help="This account will be used instead of the default one as the payable account on invoices using this payment mode", # noqa
)

View File

@@ -0,0 +1,31 @@
# Copyright 2022 Camptocamp SA
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
from odoo import models
class AccountChartTemplate(models.Model):
_inherit = "account.chart.template"
def generate_properties(self, acc_template_ref, company):
super().generate_properties(acc_template_ref, company)
# Make sure a property with stored in its name is created as default for the company
# so that _get_multi would fetch it if the partner does not have a property itself
PropertyObj = self.env["ir.property"]
todo_list = [
(
"property_account_receivable_id",
"property_stored_account_receivable_id",
"res.partner",
),
(
"property_account_payable_id",
"property_stored_account_payable_id",
"res.partner",
),
]
for chart_field, partner_field, model in todo_list:
account = self[chart_field]
value = acc_template_ref[account.id] if account else False
if value:
PropertyObj._set_default(partner_field, model, value, company=company)

View File

@@ -0,0 +1,76 @@
# Copyright 2022 Camptocamp SA
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
from odoo import api, fields, models
class ResPartner(models.Model):
_inherit = "res.partner"
property_account_receivable_id = fields.Many2one(
company_dependent=False,
compute="_compute_property_account_receivable_id",
inverse="_inverse_property_account_receivable_id",
)
property_stored_account_receivable_id = fields.Many2one(
"account.account",
company_dependent=True,
string="Account Receivable",
domain="[('internal_type', '=', 'receivable'), ('deprecated', '=', False), ('company_id', '=', current_company_id)]", # noqa
)
property_account_payable_id = fields.Many2one(
company_dependent=False,
compute="_compute_property_account_payable_id",
inverse="_inverse_property_account_payable_id",
)
property_stored_account_payable_id = fields.Many2one(
"account.account",
company_dependent=True,
string="Account payable",
domain="[('internal_type', '=', 'payable'), ('deprecated', '=', False), ('company_id', '=', current_company_id)]", # noqa
)
@api.depends("property_stored_account_receivable_id")
@api.depends_context("_partner_property_account_payment_mode")
def _compute_property_account_receivable_id(self):
payment_mode_id = self.env.context.get("_partner_property_account_payment_mode")
if payment_mode_id:
payment_mode = self.env["account.payment.mode"].browse(payment_mode_id)
rec_account = payment_mode.default_receivable_account_id
if rec_account:
self.update({"property_account_receivable_id": rec_account})
return
for partner in self:
partner.property_account_receivable_id = (
partner.property_stored_account_receivable_id
)
def _inverse_property_account_receivable_id(self):
for partner in self:
partner.property_stored_account_receivable_id = (
partner.property_account_receivable_id
)
@api.depends("property_stored_account_payable_id")
@api.depends_context("_partner_property_account_payment_mode")
def _compute_property_account_payable_id(self):
payment_mode_id = self.env.context.get("_partner_property_account_payment_mode")
if payment_mode_id:
payment_mode = self.env["account.payment.mode"].browse(payment_mode_id)
rec_account = payment_mode.default_payable_account_id
if rec_account:
self.update({"property_account_payable_id": rec_account})
return
for partner in self:
partner.property_account_payable_id = (
partner.property_stored_account_payable_id
)
def _inverse_property_account_payable_id(self):
for partner in self:
partner.property_stored_account_payable_id = (
partner.property_account_payable_id
)

View File

@@ -0,0 +1 @@
* Akim Juillerat <akim.juillerat@camptocamp.com>

View File

@@ -0,0 +1,3 @@
This module allows to define default receivable and payable accounts
on payment mode to override the account selected on the customer
when computing payment terms lines on invoices.

View File

@@ -0,0 +1 @@
from . import test_account_payment_mode_default_account

View File

@@ -0,0 +1,148 @@
# Copyright 2022 Camptocamp SA
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
from odoo.tests import Form, SavepointCase
class TestAccountPaymentModeDefaultAccount(SavepointCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
chart_template = cls.env.company.chart_template_id
chart_template.try_loading(company=cls.env.company)
receivable_code = chart_template["property_account_receivable_id"].code
cls.receivable_account = cls.env["account.account"].search(
[
("company_id", "=", cls.env.company.id),
("user_type_id.type", "=", "receivable"),
("code", "=like", receivable_code + "%"),
],
limit=1,
)
cls.payable_account = cls.env["account.account"].search(
[
("company_id", "=", cls.env.company.id),
("user_type_id.type", "=", "payable"),
],
limit=1,
)
cls.receivable_account2 = cls.receivable_account.copy(
{"code": cls.receivable_account.code + "2"}
)
cls.payable_account2 = cls.payable_account.copy(
{"code": cls.payable_account.code + "2"}
)
cls.partner_1 = cls.env.ref("base.res_partner_1")
cls.payment_mode = cls.env.ref("account_payment_mode.payment_mode_inbound_dd1")
cls.payment_mode.write(
{
"default_receivable_account_id": cls.receivable_account2.id,
"default_payable_account_id": cls.payable_account2.id,
}
)
cls.payment_mode_without_default = cls.env.ref(
"account_payment_mode.payment_mode_inbound_ct1"
)
@classmethod
def _create_invoice(cls, move_type="out_invoice", payment_mode=None):
move_form = Form(
cls.env["account.move"].with_context(default_move_type=move_type)
)
move_form.partner_id = cls.partner_1
if payment_mode is not None:
move_form.payment_mode_id = payment_mode
with move_form.invoice_line_ids.new() as line_form:
line_form.name = "test"
line_form.quantity = 1.0
line_form.price_unit = 100
invoice = move_form.save()
return invoice
def test_create_customer_invoice_payment_mode_default(self):
invoice = self._create_invoice(payment_mode=self.payment_mode)
payment_term_line = invoice._get_payment_term_lines()
self.assertEqual(payment_term_line.account_id, self.receivable_account2)
def test_create_supplier_invoice_payment_mode_default(self):
invoice = self._create_invoice(
move_type="in_invoice", payment_mode=self.payment_mode
)
payment_term_line = invoice._get_payment_term_lines()
self.assertEqual(payment_term_line.account_id, self.payable_account2)
def test_change_customer_invoice_payment_mode_default(self):
invoice = self._create_invoice()
payment_term_line = invoice._get_payment_term_lines()
self.assertEqual(payment_term_line.account_id, self.receivable_account)
with Form(invoice) as move_form:
move_form.payment_mode_id = self.payment_mode
self.assertEqual(payment_term_line.account_id, self.receivable_account2)
def test_change_supplier_invoice_payment_mode_default(self):
invoice = self._create_invoice(move_type="in_invoice")
payment_term_line = invoice._get_payment_term_lines()
self.assertEqual(payment_term_line.account_id, self.payable_account)
with Form(invoice) as move_form:
move_form.payment_mode_id = self.payment_mode
self.assertEqual(payment_term_line.account_id, self.payable_account2)
def test_create_customer_invoice_payment_mode_without_default(self):
invoice = self._create_invoice(payment_mode=self.payment_mode_without_default)
payment_term_line = invoice._get_payment_term_lines()
self.assertEqual(payment_term_line.account_id, self.receivable_account)
def test_create_supplier_invoice_payment_mode_without_default(self):
invoice = self._create_invoice(
move_type="in_invoice", payment_mode=self.payment_mode_without_default
)
payment_term_line = invoice._get_payment_term_lines()
self.assertEqual(payment_term_line.account_id, self.payable_account)
def test_change_customer_invoice_payment_mode_without_default(self):
invoice = self._create_invoice()
payment_term_line = invoice._get_payment_term_lines()
self.assertEqual(payment_term_line.account_id, self.receivable_account)
with Form(invoice) as move_form:
move_form.payment_mode_id = self.payment_mode_without_default
self.assertEqual(payment_term_line.account_id, self.receivable_account)
def test_change_supplier_invoice_payment_mode_without_default(self):
invoice = self._create_invoice(move_type="in_invoice")
payment_term_line = invoice._get_payment_term_lines()
self.assertEqual(payment_term_line.account_id, self.payable_account)
with Form(invoice) as move_form:
move_form.payment_mode_id = self.payment_mode_without_default
self.assertEqual(payment_term_line.account_id, self.payable_account)
def test_partner_compute_inverse(self):
self.assertEqual(
self.partner_1.property_account_receivable_id, self.receivable_account
)
self.assertEqual(
self.partner_1.property_account_payable_id, self.payable_account
)
self.assertEqual(
self.partner_1.with_context(
_partner_property_account_payment_mode=self.payment_mode.id
).property_account_receivable_id,
self.receivable_account2,
)
self.assertEqual(
self.partner_1.with_context(
_partner_property_account_payment_mode=self.payment_mode.id
).property_account_payable_id,
self.payable_account2,
)
self.partner_1.write(
{
"property_account_receivable_id": self.receivable_account2.id,
"property_account_payable_id": self.payable_account2.id,
}
)
self.assertEqual(
self.partner_1.property_account_receivable_id, self.receivable_account2
)
self.assertEqual(
self.partner_1.property_account_payable_id, self.payable_account2
)

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="account_payment_mode_form" model="ir.ui.view">
<field name="name">account.payment.mode.form.inherit</field>
<field name="model">account.payment.mode</field>
<field name="inherit_id" ref="account_payment_mode.account_payment_mode_form" />
<field name="arch" type="xml">
<group name="note" position="before">
<group name="account_defaults" string="Default accounts">
<field name="default_receivable_account_id" />
<field name="default_payable_account_id" />
</group>
</group>
</field>
</record>
</odoo>

View File

@@ -0,0 +1 @@
../../../../account_payment_mode_default_account

View File

@@ -0,0 +1,6 @@
import setuptools
setuptools.setup(
setup_requires=['setuptools-odoo'],
odoo_addon=True,
)