diff --git a/account_loan/__manifest__.py b/account_loan/__manifest__.py
index 028568880..f272f42c9 100644
--- a/account_loan/__manifest__.py
+++ b/account_loan/__manifest__.py
@@ -7,23 +7,17 @@
"website": "http://github.com/OCA/account-financial-tools",
"license": "AGPL-3",
"category": "Accounting",
- "depends": [
- "account"
- ],
+ "depends": ["account"],
"data": [
- 'data/ir_sequence_data.xml',
- 'security/ir.model.access.csv',
- 'security/account_loan_security.xml',
- 'wizard/account_loan_generate_entries_view.xml',
- 'wizard/account_loan_pay_amount_view.xml',
- 'wizard/account_loan_post_view.xml',
- 'views/account_loan_view.xml',
- 'views/account_move_view.xml',
+ "data/ir_sequence_data.xml",
+ "security/ir.model.access.csv",
+ "security/account_loan_security.xml",
+ "wizard/account_loan_generate_entries_view.xml",
+ "wizard/account_loan_pay_amount_view.xml",
+ "wizard/account_loan_post_view.xml",
+ "views/account_loan_view.xml",
+ "views/account_move_view.xml",
],
- 'installable': True,
- 'external_dependencies': {
- 'python': [
- 'numpy',
- ],
- },
+ "installable": True,
+ "external_dependencies": {"python": ["numpy",],},
}
diff --git a/account_loan/data/ir_sequence_data.xml b/account_loan/data/ir_sequence_data.xml
index fbbd1d95d..8f41ec9c5 100644
--- a/account_loan/data/ir_sequence_data.xml
+++ b/account_loan/data/ir_sequence_data.xml
@@ -1,18 +1,14 @@
-
-
-
Account loan sequence
account.loan
ACL
6
-
diff --git a/account_loan/model/account_invoice.py b/account_loan/model/account_invoice.py
index 1b3f571cc..c60e86aa0 100644
--- a/account_loan/model/account_invoice.py
+++ b/account_loan/model/account_invoice.py
@@ -5,18 +5,13 @@ from odoo import api, fields, models
class AccountInvoice(models.Model):
- _inherit = 'account.invoice'
+ _inherit = "account.invoice"
loan_line_id = fields.Many2one(
- 'account.loan.line',
- readonly=True,
- ondelete='restrict',
+ "account.loan.line", readonly=True, ondelete="restrict",
)
loan_id = fields.Many2one(
- 'account.loan',
- readonly=True,
- store=True,
- ondelete='restrict',
+ "account.loan", readonly=True, store=True, ondelete="restrict",
)
@api.multi
@@ -24,18 +19,27 @@ class AccountInvoice(models.Model):
vals = super().finalize_invoice_move_lines(move_lines)
if self.loan_line_id:
ll = self.loan_line_id
- if (
- ll.long_term_loan_account_id and
- ll.long_term_principal_amount != 0
- ):
- vals.append((0, 0, {
- 'account_id': ll.loan_id.short_term_loan_account_id.id,
- 'credit': ll.long_term_principal_amount,
- 'debit': 0,
- }))
- vals.append((0, 0, {
- 'account_id': ll.long_term_loan_account_id.id,
- 'credit': 0,
- 'debit': ll.long_term_principal_amount,
- }))
+ if ll.long_term_loan_account_id and ll.long_term_principal_amount != 0:
+ vals.append(
+ (
+ 0,
+ 0,
+ {
+ "account_id": ll.loan_id.short_term_loan_account_id.id,
+ "credit": ll.long_term_principal_amount,
+ "debit": 0,
+ },
+ )
+ )
+ vals.append(
+ (
+ 0,
+ 0,
+ {
+ "account_id": ll.long_term_loan_account_id.id,
+ "credit": 0,
+ "debit": ll.long_term_principal_amount,
+ },
+ )
+ )
return vals
diff --git a/account_loan/model/account_loan.py b/account_loan/model/account_loan.py
index 73ab196a1..5a4fe4fc0 100644
--- a/account_loan/model/account_loan.py
+++ b/account_loan/model/account_loan.py
@@ -1,12 +1,13 @@
# Copyright 2018 Creu Blanca
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
-from odoo import api, fields, models
-
-from datetime import datetime
-from dateutil.relativedelta import relativedelta
import logging
import math
+from datetime import datetime
+
+from dateutil.relativedelta import relativedelta
+
+from odoo import api, fields, models
_logger = logging.getLogger(__name__)
try:
@@ -16,12 +17,12 @@ except (ImportError, IOError) as err:
class AccountLoan(models.Model):
- _name = 'account.loan'
- _description = 'Loan'
- _inherit = ['mail.thread', 'mail.activity.mixin']
+ _name = "account.loan"
+ _description = "Loan"
+ _inherit = ["mail.thread", "mail.activity.mixin"]
def _default_company(self):
- force_company = self._context.get('force_company')
+ force_company = self._context.get("force_company")
if not force_company:
return self.env.user.company_id.id
return force_company
@@ -30,243 +31,218 @@ class AccountLoan(models.Model):
copy=False,
required=True,
readonly=True,
- default='/',
- states={'draft': [('readonly', False)]},
+ default="/",
+ states={"draft": [("readonly", False)]},
)
partner_id = fields.Many2one(
- 'res.partner',
+ "res.partner",
required=True,
- string='Lender',
- help='Company or individual that lends the money at an interest rate.',
+ string="Lender",
+ help="Company or individual that lends the money at an interest rate.",
readonly=True,
- states={'draft': [('readonly', False)]},
+ states={"draft": [("readonly", False)]},
)
company_id = fields.Many2one(
- 'res.company',
+ "res.company",
required=True,
default=_default_company,
readonly=True,
- states={'draft': [('readonly', False)]},
+ states={"draft": [("readonly", False)]},
)
- state = fields.Selection([
- ('draft', 'Draft'),
- ('posted', 'Posted'),
- ('cancelled', 'Cancelled'),
- ('closed', 'Closed'),
- ], required=True, copy=False, default='draft')
- line_ids = fields.One2many(
- 'account.loan.line',
- readonly=True,
- inverse_name='loan_id',
+ state = fields.Selection(
+ [
+ ("draft", "Draft"),
+ ("posted", "Posted"),
+ ("cancelled", "Cancelled"),
+ ("closed", "Closed"),
+ ],
+ required=True,
copy=False,
+ default="draft",
+ )
+ line_ids = fields.One2many(
+ "account.loan.line", readonly=True, inverse_name="loan_id", copy=False,
)
periods = fields.Integer(
required=True,
readonly=True,
- states={'draft': [('readonly', False)]},
- help='Number of periods that the loan will last',
+ states={"draft": [("readonly", False)]},
+ help="Number of periods that the loan will last",
)
method_period = fields.Integer(
- string='Period Length',
+ string="Period Length",
default=1,
help="State here the time between 2 depreciations, in months",
required=True,
readonly=True,
- states={'draft': [('readonly', False)]},
+ states={"draft": [("readonly", False)]},
)
start_date = fields.Date(
- help='Start of the moves',
+ help="Start of the moves",
readonly=True,
- states={'draft': [('readonly', False)]},
+ states={"draft": [("readonly", False)]},
copy=False,
)
rate = fields.Float(
required=True,
default=0.0,
digits=(8, 6),
- help='Currently applied rate',
- track_visibility='always',
+ help="Currently applied rate",
+ track_visibility="always",
)
rate_period = fields.Float(
- compute='_compute_rate_period', digits=(8, 6),
- help='Real rate that will be applied on each period',
+ compute="_compute_rate_period",
+ digits=(8, 6),
+ help="Real rate that will be applied on each period",
)
rate_type = fields.Selection(
- [
- ('napr', 'Nominal APR'),
- ('ear', 'EAR'),
- ('real', 'Real rate'),
- ],
+ [("napr", "Nominal APR"), ("ear", "EAR"), ("real", "Real rate"),],
required=True,
- help='Method of computation of the applied rate',
- default='napr',
+ help="Method of computation of the applied rate",
+ default="napr",
readonly=True,
- states={'draft': [('readonly', False)]},
+ states={"draft": [("readonly", False)]},
)
loan_type = fields.Selection(
[
- ('fixed-annuity', 'Fixed Annuity'),
- ('fixed-annuity-begin', 'Fixed Annuity Begin'),
- ('fixed-principal', 'Fixed Principal'),
- ('interest', 'Only interest'),
+ ("fixed-annuity", "Fixed Annuity"),
+ ("fixed-annuity-begin", "Fixed Annuity Begin"),
+ ("fixed-principal", "Fixed Principal"),
+ ("interest", "Only interest"),
],
required=True,
- help='Method of computation of the period annuity',
+ help="Method of computation of the period annuity",
readonly=True,
- states={'draft': [('readonly', False)]},
- default='fixed-annuity'
+ states={"draft": [("readonly", False)]},
+ default="fixed-annuity",
)
fixed_amount = fields.Monetary(
- currency_field='currency_id',
- compute='_compute_fixed_amount',
+ currency_field="currency_id", compute="_compute_fixed_amount",
)
fixed_loan_amount = fields.Monetary(
- currency_field='currency_id',
- readonly=True,
- copy=False,
- default=0,
- )
- fixed_periods = fields.Integer(
- readonly=True,
- copy=False,
- default=0,
+ currency_field="currency_id", readonly=True, copy=False, default=0,
)
+ fixed_periods = fields.Integer(readonly=True, copy=False, default=0,)
loan_amount = fields.Monetary(
- currency_field='currency_id',
+ currency_field="currency_id",
required=True,
readonly=True,
- states={'draft': [('readonly', False)]},
+ states={"draft": [("readonly", False)]},
)
residual_amount = fields.Monetary(
- currency_field='currency_id',
- default=0.,
+ currency_field="currency_id",
+ default=0.0,
required=True,
readonly=True,
- states={'draft': [('readonly', False)]},
- help='Residual amount of the lease that must be payed on the end in '
- 'order to acquire the asset',
+ states={"draft": [("readonly", False)]},
+ help="Residual amount of the lease that must be payed on the end in "
+ "order to acquire the asset",
)
round_on_end = fields.Boolean(
default=False,
- help='When checked, the differences will be applied on the last period'
- ', if it is unchecked, the annuity will be recalculated on each '
- 'period.',
+ help="When checked, the differences will be applied on the last period"
+ ", if it is unchecked, the annuity will be recalculated on each "
+ "period.",
readonly=True,
- states={'draft': [('readonly', False)]},
+ states={"draft": [("readonly", False)]},
)
payment_on_first_period = fields.Boolean(
default=False,
readonly=True,
- states={'draft': [('readonly', False)]},
- help='When checked, the first payment will be on start date',
+ states={"draft": [("readonly", False)]},
+ help="When checked, the first payment will be on start date",
)
currency_id = fields.Many2one(
- 'res.currency',
- compute='_compute_currency',
- readonly=True,
+ "res.currency", compute="_compute_currency", readonly=True,
)
- journal_type = fields.Char(compute='_compute_journal_type')
+ journal_type = fields.Char(compute="_compute_journal_type")
journal_id = fields.Many2one(
- 'account.journal',
+ "account.journal",
domain="[('company_id', '=', company_id),('type', '=', journal_type)]",
required=True,
readonly=True,
- states={'draft': [('readonly', False)]},
+ states={"draft": [("readonly", False)]},
)
short_term_loan_account_id = fields.Many2one(
- 'account.account',
+ "account.account",
domain="[('company_id', '=', company_id)]",
- string='Short term account',
- help='Account that will contain the pending amount on short term',
+ string="Short term account",
+ help="Account that will contain the pending amount on short term",
required=True,
readonly=True,
- states={'draft': [('readonly', False)]},
+ states={"draft": [("readonly", False)]},
)
long_term_loan_account_id = fields.Many2one(
- 'account.account',
- string='Long term account',
- help='Account that will contain the pending amount on Long term',
+ "account.account",
+ string="Long term account",
+ help="Account that will contain the pending amount on Long term",
domain="[('company_id', '=', company_id)]",
readonly=True,
- states={'draft': [('readonly', False)]},
+ states={"draft": [("readonly", False)]},
)
interest_expenses_account_id = fields.Many2one(
- 'account.account',
+ "account.account",
domain="[('company_id', '=', company_id)]",
- string='Interests account',
- help='Account where the interests will be assigned to',
+ string="Interests account",
+ help="Account where the interests will be assigned to",
required=True,
readonly=True,
- states={'draft': [('readonly', False)]},
+ states={"draft": [("readonly", False)]},
)
is_leasing = fields.Boolean(
- default=False,
- readonly=True,
- states={'draft': [('readonly', False)]},
+ default=False, readonly=True, states={"draft": [("readonly", False)]},
)
leased_asset_account_id = fields.Many2one(
- 'account.account',
+ "account.account",
domain="[('company_id', '=', company_id)]",
readonly=True,
- states={'draft': [('readonly', False)]},
+ states={"draft": [("readonly", False)]},
)
product_id = fields.Many2one(
- 'product.product',
- string='Loan product',
- help='Product where the amount of the loan will be assigned when the '
- 'invoice is created',
+ "product.product",
+ string="Loan product",
+ help="Product where the amount of the loan will be assigned when the "
+ "invoice is created",
)
interests_product_id = fields.Many2one(
- 'product.product',
- string='Interest product',
- help='Product where the amount of interests will be assigned when the '
- 'invoice is created',
- )
- move_ids = fields.One2many(
- 'account.move',
- copy=False,
- inverse_name='loan_id'
+ "product.product",
+ string="Interest product",
+ help="Product where the amount of interests will be assigned when the "
+ "invoice is created",
)
+ move_ids = fields.One2many("account.move", copy=False, inverse_name="loan_id")
pending_principal_amount = fields.Monetary(
- currency_field='currency_id',
- compute='_compute_total_amounts',
+ currency_field="currency_id", compute="_compute_total_amounts",
)
payment_amount = fields.Monetary(
- currency_field='currency_id',
- string='Total payed amount',
- compute='_compute_total_amounts',
+ currency_field="currency_id",
+ string="Total payed amount",
+ compute="_compute_total_amounts",
)
interests_amount = fields.Monetary(
- currency_field='currency_id',
- string='Total interests payed',
- compute='_compute_total_amounts',
+ currency_field="currency_id",
+ string="Total interests payed",
+ compute="_compute_total_amounts",
)
post_invoice = fields.Boolean(
- default=True,
- help='Invoices will be posted automatically'
+ default=True, help="Invoices will be posted automatically"
)
_sql_constraints = [
- ('name_uniq', 'unique(name, company_id)',
- 'Loan name must be unique'),
+ ("name_uniq", "unique(name, company_id)", "Loan name must be unique"),
]
- @api.depends('line_ids', 'currency_id', 'loan_amount')
+ @api.depends("line_ids", "currency_id", "loan_amount")
def _compute_total_amounts(self):
for record in self:
lines = record.line_ids.filtered(lambda r: r.move_ids)
- record.payment_amount = sum(
- lines.mapped('payment_amount')) or 0.
- record.interests_amount = sum(
- lines.mapped('interests_amount')) or 0.
+ record.payment_amount = sum(lines.mapped("payment_amount")) or 0.0
+ record.interests_amount = sum(lines.mapped("interests_amount")) or 0.0
record.pending_principal_amount = (
- record.loan_amount -
- record.payment_amount +
- record.interests_amount
+ record.loan_amount - record.payment_amount + record.interests_amount
)
- @api.depends('rate_period', 'fixed_loan_amount', 'fixed_periods',
- 'currency_id')
+ @api.depends("rate_period", "fixed_loan_amount", "fixed_periods", "currency_id")
def _compute_fixed_amount(self):
"""
Computes the fixed amount in order to be used if round_on_end is
@@ -275,25 +251,29 @@ class AccountLoan(models.Model):
:return:
"""
for record in self:
- if record.loan_type == 'fixed-annuity':
- record.fixed_amount = - record.currency_id.round(numpy.pmt(
- record.loan_rate() / 100,
- record.fixed_periods,
- record.fixed_loan_amount,
- -record.residual_amount
- ))
- elif record.loan_type == 'fixed-annuity-begin':
- record.fixed_amount = - record.currency_id.round(numpy.pmt(
- record.loan_rate() / 100,
- record.fixed_periods,
- record.fixed_loan_amount,
- -record.residual_amount,
- when='begin'
- ))
- elif record.loan_type == 'fixed-principal':
+ if record.loan_type == "fixed-annuity":
+ record.fixed_amount = -record.currency_id.round(
+ numpy.pmt(
+ record.loan_rate() / 100,
+ record.fixed_periods,
+ record.fixed_loan_amount,
+ -record.residual_amount,
+ )
+ )
+ elif record.loan_type == "fixed-annuity-begin":
+ record.fixed_amount = -record.currency_id.round(
+ numpy.pmt(
+ record.loan_rate() / 100,
+ record.fixed_periods,
+ record.fixed_loan_amount,
+ -record.residual_amount,
+ when="begin",
+ )
+ )
+ elif record.loan_type == "fixed-principal":
record.fixed_amount = record.currency_id.round(
- (record.fixed_loan_amount - record.residual_amount) /
- record.fixed_periods
+ (record.fixed_loan_amount - record.residual_amount)
+ / record.fixed_periods
)
else:
record.fixed_amount = 0.0
@@ -307,57 +287,58 @@ class AccountLoan(models.Model):
:param method_period: Number of months between payments
:return:
"""
- if rate_type == 'napr':
+ if rate_type == "napr":
return rate / 12 * method_period
- if rate_type == 'ear':
+ if rate_type == "ear":
return math.pow(1 + rate, method_period / 12) - 1
return rate
- @api.depends('rate', 'method_period', 'rate_type')
+ @api.depends("rate", "method_period", "rate_type")
def _compute_rate_period(self):
for record in self:
record.rate_period = record.loan_rate()
def loan_rate(self):
- return self.compute_rate(
- self.rate, self.rate_type, self.method_period
- )
+ return self.compute_rate(self.rate, self.rate_type, self.method_period)
- @api.depends('journal_id', 'company_id')
+ @api.depends("journal_id", "company_id")
def _compute_currency(self):
for rec in self:
- rec.currency_id = (
- rec.journal_id.currency_id or rec.company_id.currency_id)
+ rec.currency_id = rec.journal_id.currency_id or rec.company_id.currency_id
- @api.depends('is_leasing')
+ @api.depends("is_leasing")
def _compute_journal_type(self):
for record in self:
if record.is_leasing:
- record.journal_type = 'purchase'
+ record.journal_type = "purchase"
else:
- record.journal_type = 'general'
+ record.journal_type = "general"
- @api.onchange('is_leasing')
+ @api.onchange("is_leasing")
def _onchange_is_leasing(self):
- self.journal_id = self.env['account.journal'].search([
- ('company_id', '=', self.company_id.id),
- ('type', '=', 'purchase' if self.is_leasing else 'general')
- ], limit=1)
+ self.journal_id = self.env["account.journal"].search(
+ [
+ ("company_id", "=", self.company_id.id),
+ ("type", "=", "purchase" if self.is_leasing else "general"),
+ ],
+ limit=1,
+ )
self.residual_amount = 0.0
- @api.onchange('company_id')
+ @api.onchange("company_id")
def _onchange_company(self):
self._onchange_is_leasing()
- self.interest_expenses_account_id = self.short_term_loan_account_id = \
- self.long_term_loan_account_id = False
+ self.interest_expenses_account_id = (
+ self.short_term_loan_account_id
+ ) = self.long_term_loan_account_id = False
def get_default_name(self, vals):
- return self.env['ir.sequence'].next_by_code('account.loan') or '/'
+ return self.env["ir.sequence"].next_by_code("account.loan") or "/"
@api.model
def create(self, vals):
- if vals.get('name', '/') == '/':
- vals['name'] = self.get_default_name(vals)
+ if vals.get("name", "/") == "/":
+ vals["name"] = self.get_default_name(vals)
return super().create(vals)
@api.multi
@@ -366,16 +347,16 @@ class AccountLoan(models.Model):
if not self.start_date:
self.start_date = fields.Date.today()
self.compute_draft_lines()
- self.write({'state': 'posted'})
+ self.write({"state": "posted"})
@api.multi
def close(self):
- self.write({'state': 'closed'})
+ self.write({"state": "closed"})
@api.multi
def compute_lines(self):
self.ensure_one()
- if self.state == 'draft':
+ if self.state == "draft":
return self.compute_draft_lines()
return self.compute_posted_lines()
@@ -384,7 +365,7 @@ class AccountLoan(models.Model):
Recompute the amounts of not finished lines. Useful if rate is changed
"""
amount = self.loan_amount
- for line in self.line_ids.sorted('sequence'):
+ for line in self.line_ids.sorted("sequence"):
if line.move_ids:
amount = line.final_pending_principal_amount
else:
@@ -403,25 +384,27 @@ class AccountLoan(models.Model):
amount = 0
if not lines:
return
- final_sequence = min(lines.mapped('sequence'))
- for line in lines.sorted('sequence', reverse=True):
+ final_sequence = min(lines.mapped("sequence"))
+ for line in lines.sorted("sequence", reverse=True):
date = line.date + relativedelta(months=12)
- if self.state == 'draft' or line.sequence != final_sequence:
+ if self.state == "draft" or line.sequence != final_sequence:
line.long_term_pending_principal_amount = sum(
- self.line_ids.filtered(
- lambda r: r.date >= date
- ).mapped('principal_amount'))
+ self.line_ids.filtered(lambda r: r.date >= date).mapped(
+ "principal_amount"
+ )
+ )
line.long_term_principal_amount = (
- line.long_term_pending_principal_amount - amount)
+ line.long_term_pending_principal_amount - amount
+ )
amount = line.long_term_pending_principal_amount
def new_line_vals(self, sequence, date, amount):
return {
- 'loan_id': self.id,
- 'sequence': sequence,
- 'date': date,
- 'pending_principal_amount': amount,
- 'rate': self.rate_period,
+ "loan_id": self.id,
+ "sequence": sequence,
+ "date": date,
+ "pending_principal_amount": amount,
+ "rate": self.rate_period,
}
@api.multi
@@ -439,7 +422,7 @@ class AccountLoan(models.Model):
if not self.payment_on_first_period:
date += delta
for i in range(1, self.periods + 1):
- line = self.env['account.loan.line'].create(
+ line = self.env["account.loan.line"].create(
self.new_line_vals(i, date, amount)
)
line.check_amount()
@@ -451,20 +434,17 @@ class AccountLoan(models.Model):
@api.multi
def view_account_moves(self):
self.ensure_one()
- action = self.env.ref('account.action_move_line_form')
+ action = self.env.ref("account.action_move_line_form")
result = action.read()[0]
- result['domain'] = [('loan_id', '=', self.id)]
+ result["domain"] = [("loan_id", "=", self.id)]
return result
@api.multi
def view_account_invoices(self):
self.ensure_one()
- action = self.env.ref('account.action_invoice_tree2')
+ action = self.env.ref("account.action_invoice_tree2")
result = action.read()[0]
- result['domain'] = [
- ('loan_id', '=', self.id),
- ('type', '=', 'in_invoice')
- ]
+ result["domain"] = [("loan_id", "=", self.id), ("type", "=", "in_invoice")]
return result
@api.model
@@ -475,10 +455,9 @@ class AccountLoan(models.Model):
:return:
"""
res = []
- for record in self.search([
- ('state', '=', 'posted'),
- ('is_leasing', '=', False)
- ]):
+ for record in self.search(
+ [("state", "=", "posted"), ("is_leasing", "=", False)]
+ ):
lines = record.line_ids.filtered(
lambda r: r.date <= date and not r.move_ids
)
@@ -488,10 +467,9 @@ class AccountLoan(models.Model):
@api.model
def generate_leasing_entries(self, date):
res = []
- for record in self.search([
- ('state', '=', 'posted'),
- ('is_leasing', '=', True)
- ]):
+ for record in self.search(
+ [("state", "=", "posted"), ("is_leasing", "=", True)]
+ ):
res += record.line_ids.filtered(
lambda r: r.date <= date and not r.invoice_ids
).generate_invoice()
diff --git a/account_loan/model/account_loan_line.py b/account_loan/model/account_loan_line.py
index 4bad7b384..9ff568172 100644
--- a/account_loan/model/account_loan_line.py
+++ b/account_loan/model/account_loan_line.py
@@ -1,10 +1,11 @@
# Copyright 2018 Creu Blanca
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
-from odoo import api, fields, models, _
-from odoo.exceptions import UserError
import logging
+from odoo import _, api, fields, models
+from odoo.exceptions import UserError
+
_logger = logging.getLogger(__name__)
try:
import numpy
@@ -13,128 +14,112 @@ except (ImportError, IOError) as err:
class AccountLoanLine(models.Model):
- _name = 'account.loan.line'
- _description = 'Annuity'
- _order = 'sequence asc'
+ _name = "account.loan.line"
+ _description = "Annuity"
+ _order = "sequence asc"
- name = fields.Char(compute='_compute_name')
+ name = fields.Char(compute="_compute_name")
loan_id = fields.Many2one(
- 'account.loan',
- required=True,
+ "account.loan", required=True, readonly=True, ondelete="cascade",
+ )
+ is_leasing = fields.Boolean(related="loan_id.is_leasing", readonly=True,)
+ loan_type = fields.Selection(
+ [
+ ("fixed-annuity", "Fixed Annuity"),
+ ("fixed-principal", "Fixed Principal"),
+ ("interest", "Only interest"),
+ ],
+ related="loan_id.loan_type",
readonly=True,
- ondelete='cascade',
)
- is_leasing = fields.Boolean(related='loan_id.is_leasing', readonly=True, )
- loan_type = fields.Selection([
- ('fixed-annuity', 'Fixed Annuity'),
- ('fixed-principal', 'Fixed Principal'),
- ('interest', 'Only interest'),
- ], related='loan_id.loan_type', readonly=True,
+ loan_state = fields.Selection(
+ [
+ ("draft", "Draft"),
+ ("posted", "Posted"),
+ ("cancelled", "Cancelled"),
+ ("closed", "Closed"),
+ ],
+ related="loan_id.state",
+ readonly=True,
+ store=True,
)
- loan_state = fields.Selection([
- ('draft', 'Draft'),
- ('posted', 'Posted'),
- ('cancelled', 'Cancelled'),
- ('closed', 'Closed'),
- ], related='loan_id.state', readonly=True, store=True)
sequence = fields.Integer(required=True, readonly=True)
date = fields.Date(
- required=True,
- readonly=True,
- help='Date when the payment will be accounted',
+ required=True, readonly=True, help="Date when the payment will be accounted",
)
long_term_loan_account_id = fields.Many2one(
- 'account.account',
- readony=True,
- related='loan_id.long_term_loan_account_id',
- )
- currency_id = fields.Many2one(
- 'res.currency',
- related='loan_id.currency_id',
- )
- rate = fields.Float(
- required=True,
- readonly=True,
- digits=(8, 6),
+ "account.account", readony=True, related="loan_id.long_term_loan_account_id",
)
+ currency_id = fields.Many2one("res.currency", related="loan_id.currency_id",)
+ rate = fields.Float(required=True, readonly=True, digits=(8, 6),)
pending_principal_amount = fields.Monetary(
- currency_field='currency_id',
+ currency_field="currency_id",
readonly=True,
- help='Pending amount of the loan before the payment',
+ help="Pending amount of the loan before the payment",
)
long_term_pending_principal_amount = fields.Monetary(
- currency_field='currency_id',
+ currency_field="currency_id",
readonly=True,
- help='Pending amount of the loan before the payment that will not be '
- 'payed in, at least, 12 months',
+ help="Pending amount of the loan before the payment that will not be "
+ "payed in, at least, 12 months",
)
payment_amount = fields.Monetary(
- currency_field='currency_id',
+ currency_field="currency_id",
readonly=True,
- help='Total amount that will be payed (Annuity)',
+ help="Total amount that will be payed (Annuity)",
)
interests_amount = fields.Monetary(
- currency_field='currency_id',
+ currency_field="currency_id",
readonly=True,
- help='Amount of the payment that will be assigned to interests',
+ help="Amount of the payment that will be assigned to interests",
)
principal_amount = fields.Monetary(
- currency_field='currency_id',
- compute='_compute_amounts',
- help='Amount of the payment that will reduce the pending loan amount',
+ currency_field="currency_id",
+ compute="_compute_amounts",
+ help="Amount of the payment that will reduce the pending loan amount",
)
long_term_principal_amount = fields.Monetary(
- currency_field='currency_id',
+ currency_field="currency_id",
readonly=True,
- help='Amount that will reduce the pending loan amount on long term',
+ help="Amount that will reduce the pending loan amount on long term",
)
final_pending_principal_amount = fields.Monetary(
- currency_field='currency_id',
- compute='_compute_amounts',
- help='Pending amount of the loan after the payment',
- )
- move_ids = fields.One2many(
- 'account.move',
- inverse_name='loan_line_id',
- )
- has_moves = fields.Boolean(
- compute='_compute_has_moves'
- )
- invoice_ids = fields.One2many(
- 'account.invoice',
- inverse_name='loan_line_id',
- )
- has_invoices = fields.Boolean(
- compute='_compute_has_invoices'
+ currency_field="currency_id",
+ compute="_compute_amounts",
+ help="Pending amount of the loan after the payment",
)
+ move_ids = fields.One2many("account.move", inverse_name="loan_line_id",)
+ has_moves = fields.Boolean(compute="_compute_has_moves")
+ invoice_ids = fields.One2many("account.invoice", inverse_name="loan_line_id",)
+ has_invoices = fields.Boolean(compute="_compute_has_invoices")
_sql_constraints = [
- ('sequence_loan',
- 'unique(loan_id, sequence)',
- 'Sequence must be unique in a loan')
+ (
+ "sequence_loan",
+ "unique(loan_id, sequence)",
+ "Sequence must be unique in a loan",
+ )
]
- @api.depends('move_ids')
+ @api.depends("move_ids")
def _compute_has_moves(self):
for record in self:
record.has_moves = bool(record.move_ids)
- @api.depends('invoice_ids')
+ @api.depends("invoice_ids")
def _compute_has_invoices(self):
for record in self:
record.has_invoices = bool(record.invoice_ids)
- @api.depends('loan_id.name', 'sequence')
+ @api.depends("loan_id.name", "sequence")
def _compute_name(self):
for record in self:
- record.name = '%s-%d' % (record.loan_id.name, record.sequence)
+ record.name = "%s-%d" % (record.loan_id.name, record.sequence)
- @api.depends('payment_amount', 'interests_amount',
- 'pending_principal_amount')
+ @api.depends("payment_amount", "interests_amount", "pending_principal_amount")
def _compute_amounts(self):
for rec in self:
rec.final_pending_principal_amount = (
- rec.pending_principal_amount - rec.payment_amount +
- rec.interests_amount
+ rec.pending_principal_amount - rec.payment_amount + rec.interests_amount
)
rec.principal_amount = rec.payment_amount - rec.interests_amount
@@ -144,76 +129,76 @@ class AccountLoanLine(models.Model):
:return: Amount to be payed on the annuity
"""
if self.sequence == self.loan_id.periods:
- return (self.pending_principal_amount + self.interests_amount -
- self.loan_id.residual_amount)
- if self.loan_type == 'fixed-principal' and self.loan_id.round_on_end:
- return self.loan_id.fixed_amount + self.interests_amount
- if self.loan_type == 'fixed-principal':
return (
- self.pending_principal_amount -
- self.loan_id.residual_amount
- ) / (
+ self.pending_principal_amount
+ + self.interests_amount
+ - self.loan_id.residual_amount
+ )
+ if self.loan_type == "fixed-principal" and self.loan_id.round_on_end:
+ return self.loan_id.fixed_amount + self.interests_amount
+ if self.loan_type == "fixed-principal":
+ return (self.pending_principal_amount - self.loan_id.residual_amount) / (
self.loan_id.periods - self.sequence + 1
) + self.interests_amount
- if self.loan_type == 'interest':
+ if self.loan_type == "interest":
return self.interests_amount
- if self.loan_type == 'fixed-annuity' and self.loan_id.round_on_end:
+ if self.loan_type == "fixed-annuity" and self.loan_id.round_on_end:
return self.loan_id.fixed_amount
- if self.loan_type == 'fixed-annuity':
- return self.currency_id.round(- numpy.pmt(
- self.loan_id.loan_rate() / 100,
- self.loan_id.periods - self.sequence + 1,
- self.pending_principal_amount,
- -self.loan_id.residual_amount
- ))
- if (
- self.loan_type == 'fixed-annuity-begin' and
- self.loan_id.round_on_end
- ):
+ if self.loan_type == "fixed-annuity":
+ return self.currency_id.round(
+ -numpy.pmt(
+ self.loan_id.loan_rate() / 100,
+ self.loan_id.periods - self.sequence + 1,
+ self.pending_principal_amount,
+ -self.loan_id.residual_amount,
+ )
+ )
+ if self.loan_type == "fixed-annuity-begin" and self.loan_id.round_on_end:
return self.loan_id.fixed_amount
- if self.loan_type == 'fixed-annuity-begin':
- return self.currency_id.round(- numpy.pmt(
- self.loan_id.loan_rate() / 100,
- self.loan_id.periods - self.sequence + 1,
- self.pending_principal_amount,
- -self.loan_id.residual_amount,
- when='begin'
- ))
+ if self.loan_type == "fixed-annuity-begin":
+ return self.currency_id.round(
+ -numpy.pmt(
+ self.loan_id.loan_rate() / 100,
+ self.loan_id.periods - self.sequence + 1,
+ self.pending_principal_amount,
+ -self.loan_id.residual_amount,
+ when="begin",
+ )
+ )
def check_amount(self):
"""Recompute amounts if the annuity has not been processed"""
if self.move_ids or self.invoice_ids:
- raise UserError(_(
- 'Amount cannot be recomputed if moves or invoices exists '
- 'already'
- ))
+ raise UserError(
+ _("Amount cannot be recomputed if moves or invoices exists " "already")
+ )
if (
- self.sequence == self.loan_id.periods and
- self.loan_id.round_on_end and
- self.loan_type in ['fixed-annuity', 'fixed-annuity-begin']
+ self.sequence == self.loan_id.periods
+ and self.loan_id.round_on_end
+ and self.loan_type in ["fixed-annuity", "fixed-annuity-begin"]
):
self.interests_amount = self.currency_id.round(
- self.loan_id.fixed_amount - self.pending_principal_amount +
- self.loan_id.residual_amount
+ self.loan_id.fixed_amount
+ - self.pending_principal_amount
+ + self.loan_id.residual_amount
)
self.payment_amount = self.currency_id.round(self.compute_amount())
elif not self.loan_id.round_on_end:
- self.interests_amount = self.currency_id.round(
- self.compute_interest())
+ self.interests_amount = self.currency_id.round(self.compute_interest())
self.payment_amount = self.currency_id.round(self.compute_amount())
else:
self.interests_amount = self.compute_interest()
self.payment_amount = self.compute_amount()
def compute_interest(self):
- if self.loan_type == 'fixed-annuity-begin':
+ if self.loan_type == "fixed-annuity-begin":
return -numpy.ipmt(
self.loan_id.loan_rate() / 100,
2,
self.loan_id.periods - self.sequence + 1,
self.pending_principal_amount,
-self.loan_id.residual_amount,
- when='begin'
+ when="begin",
)
return self.pending_principal_amount * self.loan_id.loan_rate() / 100
@@ -224,105 +209,118 @@ class AccountLoanLine(models.Model):
:return:
"""
self.ensure_one()
- interests_moves = self.move_ids.mapped('line_ids').filtered(
+ interests_moves = self.move_ids.mapped("line_ids").filtered(
lambda r: r.account_id == self.loan_id.interest_expenses_account_id
)
- short_term_moves = self.move_ids.mapped('line_ids').filtered(
+ short_term_moves = self.move_ids.mapped("line_ids").filtered(
lambda r: r.account_id == self.loan_id.short_term_loan_account_id
)
- long_term_moves = self.move_ids.mapped('line_ids').filtered(
+ long_term_moves = self.move_ids.mapped("line_ids").filtered(
lambda r: r.account_id == self.loan_id.long_term_loan_account_id
)
- self.interests_amount = (
- sum(interests_moves.mapped('debit')) -
- sum(interests_moves.mapped('credit'))
+ self.interests_amount = sum(interests_moves.mapped("debit")) - sum(
+ interests_moves.mapped("credit")
)
- self.long_term_principal_amount = (
- sum(long_term_moves.mapped('debit')) -
- sum(long_term_moves.mapped('credit'))
+ self.long_term_principal_amount = sum(long_term_moves.mapped("debit")) - sum(
+ long_term_moves.mapped("credit")
)
self.payment_amount = (
- sum(short_term_moves.mapped('debit')) -
- sum(short_term_moves.mapped('credit')) +
- self.long_term_principal_amount +
- self.interests_amount
+ sum(short_term_moves.mapped("debit"))
+ - sum(short_term_moves.mapped("credit"))
+ + self.long_term_principal_amount
+ + self.interests_amount
)
def move_vals(self):
return {
- 'loan_line_id': self.id,
- 'loan_id': self.loan_id.id,
- 'date': self.date,
- 'ref': self.name,
- 'journal_id': self.loan_id.journal_id.id,
- 'line_ids': [(0, 0, vals) for vals in self.move_line_vals()]
+ "loan_line_id": self.id,
+ "loan_id": self.loan_id.id,
+ "date": self.date,
+ "ref": self.name,
+ "journal_id": self.loan_id.journal_id.id,
+ "line_ids": [(0, 0, vals) for vals in self.move_line_vals()],
}
def move_line_vals(self):
vals = []
partner = self.loan_id.partner_id.with_context(
- force_company=self.loan_id.company_id.id)
- vals.append({
- 'account_id': partner.property_account_payable_id.id,
- 'partner_id': partner.id,
- 'credit': self.payment_amount,
- 'debit': 0,
- })
- vals.append({
- 'account_id': self.loan_id.interest_expenses_account_id.id,
- 'credit': 0,
- 'debit': self.interests_amount,
- })
- vals.append({
- 'account_id': self.loan_id.short_term_loan_account_id.id,
- 'credit': 0,
- 'debit': self.payment_amount - self.interests_amount,
- })
+ force_company=self.loan_id.company_id.id
+ )
+ vals.append(
+ {
+ "account_id": partner.property_account_payable_id.id,
+ "partner_id": partner.id,
+ "credit": self.payment_amount,
+ "debit": 0,
+ }
+ )
+ vals.append(
+ {
+ "account_id": self.loan_id.interest_expenses_account_id.id,
+ "credit": 0,
+ "debit": self.interests_amount,
+ }
+ )
+ vals.append(
+ {
+ "account_id": self.loan_id.short_term_loan_account_id.id,
+ "credit": 0,
+ "debit": self.payment_amount - self.interests_amount,
+ }
+ )
if self.long_term_loan_account_id and self.long_term_principal_amount:
- vals.append({
- 'account_id': self.loan_id.short_term_loan_account_id.id,
- 'credit': self.long_term_principal_amount,
- 'debit': 0,
- })
- vals.append({
- 'account_id': self.long_term_loan_account_id.id,
- 'credit': 0,
- 'debit': self.long_term_principal_amount,
- })
+ vals.append(
+ {
+ "account_id": self.loan_id.short_term_loan_account_id.id,
+ "credit": self.long_term_principal_amount,
+ "debit": 0,
+ }
+ )
+ vals.append(
+ {
+ "account_id": self.long_term_loan_account_id.id,
+ "credit": 0,
+ "debit": self.long_term_principal_amount,
+ }
+ )
return vals
def invoice_vals(self):
partner = self.loan_id.partner_id.with_context(
- force_company=self.loan_id.company_id.id)
+ force_company=self.loan_id.company_id.id
+ )
return {
- 'loan_line_id': self.id,
- 'loan_id': self.loan_id.id,
- 'type': 'in_invoice',
- 'partner_id': self.loan_id.partner_id.id,
- 'date_invoice': self.date,
- 'account_id': partner.property_account_payable_id.id,
- 'journal_id': self.loan_id.journal_id.id,
- 'company_id': self.loan_id.company_id.id,
- 'invoice_line_ids': [(0, 0, vals) for vals in
- self.invoice_line_vals()]
+ "loan_line_id": self.id,
+ "loan_id": self.loan_id.id,
+ "type": "in_invoice",
+ "partner_id": self.loan_id.partner_id.id,
+ "date_invoice": self.date,
+ "account_id": partner.property_account_payable_id.id,
+ "journal_id": self.loan_id.journal_id.id,
+ "company_id": self.loan_id.company_id.id,
+ "invoice_line_ids": [(0, 0, vals) for vals in self.invoice_line_vals()],
}
def invoice_line_vals(self):
vals = list()
- vals.append({
- 'product_id': self.loan_id.product_id.id,
- 'name': self.loan_id.product_id.name,
- 'quantity': 1,
- 'price_unit': self.principal_amount,
- 'account_id': self.loan_id.short_term_loan_account_id.id,
- })
- vals.append({
- 'product_id': self.loan_id.interests_product_id.id,
- 'name': self.loan_id.interests_product_id.name,
- 'quantity': 1,
- 'price_unit': self.interests_amount,
- 'account_id': self.loan_id.interest_expenses_account_id.id,
- })
+ vals.append(
+ {
+ "product_id": self.loan_id.product_id.id,
+ "name": self.loan_id.product_id.name,
+ "quantity": 1,
+ "price_unit": self.principal_amount,
+ "account_id": self.loan_id.short_term_loan_account_id.id,
+ }
+ )
+ vals.append(
+ {
+ "product_id": self.loan_id.interests_product_id.id,
+ "name": self.loan_id.interests_product_id.name,
+ "quantity": 1,
+ "price_unit": self.interests_amount,
+ "account_id": self.loan_id.interest_expenses_account_id.id,
+ }
+ )
return vals
@api.multi
@@ -335,10 +333,10 @@ class AccountLoanLine(models.Model):
for record in self:
if not record.move_ids:
if record.loan_id.line_ids.filtered(
- lambda r: r.date < record.date and not r.move_ids
+ lambda r: r.date < record.date and not r.move_ids
):
- raise UserError(_('Some moves must be created first'))
- move = self.env['account.move'].create(record.move_vals())
+ raise UserError(_("Some moves must be created first"))
+ move = self.env["account.move"].create(record.move_vals())
move.post()
res.append(move.id)
return res
@@ -353,11 +351,10 @@ class AccountLoanLine(models.Model):
for record in self:
if not record.invoice_ids:
if record.loan_id.line_ids.filtered(
- lambda r: r.date < record.date and not r.invoice_ids
+ lambda r: r.date < record.date and not r.invoice_ids
):
- raise UserError(_('Some invoices must be created first'))
- invoice = self.env['account.invoice'].create(
- record.invoice_vals())
+ raise UserError(_("Some invoices must be created first"))
+ invoice = self.env["account.invoice"].create(record.invoice_vals())
res.append(invoice.id)
for line in invoice.invoice_line_ids:
line._set_taxes()
@@ -387,34 +384,31 @@ class AccountLoanLine(models.Model):
@api.multi
def view_account_moves(self):
self.ensure_one()
- action = self.env.ref('account.action_move_line_form')
+ action = self.env.ref("account.action_move_line_form")
result = action.read()[0]
- result['context'] = {
- 'default_loan_line_id': self.id,
- 'default_loan_id': self.loan_id.id
+ result["context"] = {
+ "default_loan_line_id": self.id,
+ "default_loan_id": self.loan_id.id,
}
- result['domain'] = [('loan_line_id', '=', self.id)]
+ result["domain"] = [("loan_line_id", "=", self.id)]
if len(self.move_ids) == 1:
- res = self.env.ref('account.move.form', False)
- result['views'] = [(res and res.id or False, 'form')]
- result['res_id'] = self.move_ids.id
+ res = self.env.ref("account.move.form", False)
+ result["views"] = [(res and res.id or False, "form")]
+ result["res_id"] = self.move_ids.id
return result
@api.multi
def view_account_invoices(self):
self.ensure_one()
- action = self.env.ref('account.action_invoice_tree2')
+ action = self.env.ref("account.action_invoice_tree2")
result = action.read()[0]
- result['context'] = {
- 'default_loan_line_id': self.id,
- 'default_loan_id': self.loan_id.id
+ result["context"] = {
+ "default_loan_line_id": self.id,
+ "default_loan_id": self.loan_id.id,
}
- result['domain'] = [
- ('loan_line_id', '=', self.id),
- ('type', '=', 'in_invoice')
- ]
+ result["domain"] = [("loan_line_id", "=", self.id), ("type", "=", "in_invoice")]
if len(self.invoice_ids) == 1:
- res = self.env.ref('account.invoice.supplier.form', False)
- result['views'] = [(res and res.id or False, 'form')]
- result['res_id'] = self.invoice_ids.id
+ res = self.env.ref("account.invoice.supplier.form", False)
+ result["views"] = [(res and res.id or False, "form")]
+ result["res_id"] = self.invoice_ids.id
return result
diff --git a/account_loan/model/account_move.py b/account_loan/model/account_move.py
index 82a1e357c..2cbb6e766 100644
--- a/account_loan/model/account_move.py
+++ b/account_loan/model/account_move.py
@@ -5,27 +5,20 @@ from odoo import api, fields, models
class AccountMove(models.Model):
- _inherit = 'account.move'
+ _inherit = "account.move"
loan_line_id = fields.Many2one(
- 'account.loan.line',
- readonly=True,
- ondelete='restrict',
+ "account.loan.line", readonly=True, ondelete="restrict",
)
loan_id = fields.Many2one(
- 'account.loan',
- readonly=True,
- store=True,
- ondelete='restrict',
+ "account.loan", readonly=True, store=True, ondelete="restrict",
)
@api.multi
def post(self, invoice=False):
res = super().post(invoice=invoice)
for record in self:
- loan_line_id = record.loan_line_id or (
- invoice and invoice.loan_line_id
- )
+ loan_line_id = record.loan_line_id or (invoice and invoice.loan_line_id)
if loan_line_id:
if not record.loan_line_id:
record.loan_line_id = loan_line_id
diff --git a/account_loan/security/account_loan_security.xml b/account_loan/security/account_loan_security.xml
index 2a63fb02d..edb768179 100644
--- a/account_loan/security/account_loan_security.xml
+++ b/account_loan/security/account_loan_security.xml
@@ -1,12 +1,11 @@
-
+
Account loan multi-company
-
-
+
+
['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]
-
diff --git a/account_loan/tests/test_loan.py b/account_loan/tests/test_loan.py
index b74e3d86c..0671c9ad1 100644
--- a/account_loan/tests/test_loan.py
+++ b/account_loan/tests/test_loan.py
@@ -1,13 +1,14 @@
# Copyright 2018 Creu Blanca
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
+import logging
+
+from dateutil.relativedelta import relativedelta
+
from odoo import fields
from odoo.exceptions import UserError
from odoo.tests import TransactionCase
-from dateutil.relativedelta import relativedelta
-import logging
-
_logger = logging.getLogger(__name__)
try:
import numpy
@@ -18,67 +19,60 @@ except (ImportError, IOError) as err:
class TestLoan(TransactionCase):
def setUp(self):
super().setUp()
- self.company = self.browse_ref('base.main_company')
- self.company_02 = self.env['res.company'].create({
- 'name': 'Auxiliar company'
- })
- self.journal = self.env['account.journal'].create({
- 'company_id': self.company.id,
- 'type': 'purchase',
- 'name': 'Debts',
- 'code': 'DBT',
- })
+ self.company = self.browse_ref("base.main_company")
+ self.company_02 = self.env["res.company"].create({"name": "Auxiliar company"})
+ self.journal = self.env["account.journal"].create(
+ {
+ "company_id": self.company.id,
+ "type": "purchase",
+ "name": "Debts",
+ "code": "DBT",
+ }
+ )
self.loan_account = self.create_account(
- 'DEP',
- 'depreciation',
- self.browse_ref('account.data_account_type_current_liabilities').id
+ "DEP",
+ "depreciation",
+ self.browse_ref("account.data_account_type_current_liabilities").id,
)
self.payable_account = self.create_account(
- 'PAY',
- 'payable',
- self.browse_ref('account.data_account_type_payable').id
+ "PAY", "payable", self.browse_ref("account.data_account_type_payable").id
)
self.asset_account = self.create_account(
- 'ASSET',
- 'asset',
- self.browse_ref('account.data_account_type_payable').id
+ "ASSET", "asset", self.browse_ref("account.data_account_type_payable").id
)
self.interests_account = self.create_account(
- 'FEE',
- 'Fees',
- self.browse_ref('account.data_account_type_expenses').id)
+ "FEE", "Fees", self.browse_ref("account.data_account_type_expenses").id
+ )
self.lt_loan_account = self.create_account(
- 'LTD',
- 'Long term depreciation',
- self.browse_ref(
- 'account.data_account_type_non_current_liabilities').id)
- self.partner = self.env['res.partner'].create({
- 'name': 'Bank'
- })
- self.product = self.env['product.product'].create({
- 'name': 'Payment',
- 'type': 'service'
- })
- self.interests_product = self.env['product.product'].create({
- 'name': 'Bank fee',
- 'type': 'service'
- })
+ "LTD",
+ "Long term depreciation",
+ self.browse_ref("account.data_account_type_non_current_liabilities").id,
+ )
+ self.partner = self.env["res.partner"].create({"name": "Bank"})
+ self.product = self.env["product.product"].create(
+ {"name": "Payment", "type": "service"}
+ )
+ self.interests_product = self.env["product.product"].create(
+ {"name": "Bank fee", "type": "service"}
+ )
def test_onchange(self):
- loan = self.env['account.loan'].new({
- 'name': 'LOAN',
- 'company_id': self.company.id,
- 'journal_id': self.journal.id,
- 'loan_type': 'fixed-annuity',
- 'loan_amount': 100,
- 'rate': 1,
- 'periods': 2,
- 'short_term_loan_account_id': self.loan_account.id,
- 'interest_expenses_account_id': self.interests_account.id,
- 'product_id': self.product.id,
- 'interests_product_id': self.interests_product.id,
- 'partner_id': self.partner.id,
- })
+ loan = self.env["account.loan"].new(
+ {
+ "name": "LOAN",
+ "company_id": self.company.id,
+ "journal_id": self.journal.id,
+ "loan_type": "fixed-annuity",
+ "loan_amount": 100,
+ "rate": 1,
+ "periods": 2,
+ "short_term_loan_account_id": self.loan_account.id,
+ "interest_expenses_account_id": self.interests_account.id,
+ "product_id": self.product.id,
+ "interests_product_id": self.interests_product.id,
+ "partner_id": self.partner.id,
+ }
+ )
journal = loan.journal_id.id
loan.is_leasing = True
loan._onchange_is_leasing()
@@ -88,20 +82,18 @@ class TestLoan(TransactionCase):
self.assertFalse(loan.interest_expenses_account_id)
def test_round_on_end(self):
- loan = self.create_loan('fixed-annuity', 500000, 1, 60)
+ loan = self.create_loan("fixed-annuity", 500000, 1, 60)
loan.round_on_end = True
loan.compute_lines()
line_1 = loan.line_ids.filtered(lambda r: r.sequence == 1)
for line in loan.line_ids:
- self.assertAlmostEqual(
- line_1.payment_amount, line.payment_amount, 2)
- loan.loan_type = 'fixed-principal'
+ self.assertAlmostEqual(line_1.payment_amount, line.payment_amount, 2)
+ loan.loan_type = "fixed-principal"
loan.compute_lines()
line_1 = loan.line_ids.filtered(lambda r: r.sequence == 1)
line_end = loan.line_ids.filtered(lambda r: r.sequence == 60)
- self.assertNotAlmostEqual(
- line_1.payment_amount, line_end.payment_amount, 2)
- loan.loan_type = 'interest'
+ self.assertNotAlmostEqual(line_1.payment_amount, line_end.payment_amount, 2)
+ loan.loan_type = "interest"
loan.compute_lines()
line_1 = loan.line_ids.filtered(lambda r: r.sequence == 1)
line_end = loan.line_ids.filtered(lambda r: r.sequence == 60)
@@ -111,12 +103,13 @@ class TestLoan(TransactionCase):
def test_pay_amount_validation(self):
amount = 10000
periods = 24
- loan = self.create_loan('fixed-annuity', amount, 1, periods)
+ loan = self.create_loan("fixed-annuity", amount, 1, periods)
self.assertTrue(loan.line_ids)
self.assertEqual(len(loan.line_ids), periods)
line = loan.line_ids.filtered(lambda r: r.sequence == 1)
self.assertAlmostEqual(
- - numpy.pmt(1 / 100 / 12, 24, 10000), line.payment_amount, 2)
+ -numpy.pmt(1 / 100 / 12, 24, 10000), line.payment_amount, 2
+ )
self.assertEqual(line.long_term_principal_amount, 0)
loan.long_term_loan_account_id = self.lt_loan_account
loan.compute_lines()
@@ -128,52 +121,45 @@ class TestLoan(TransactionCase):
self.assertTrue(line)
self.assertFalse(line.move_ids)
self.assertFalse(line.invoice_ids)
- wzd = self.env['account.loan.generate.wizard'].create({})
+ wzd = self.env["account.loan.generate.wizard"].create({})
action = wzd.run()
self.assertTrue(action)
self.assertFalse(wzd.run())
self.assertTrue(line.move_ids)
- self.assertIn(line.move_ids.id, action['domain'][0][2])
+ self.assertIn(line.move_ids.id, action["domain"][0][2])
line.move_ids.post()
with self.assertRaises(UserError):
- self.env['account.loan.pay.amount'].create({
- 'loan_id': loan.id,
- 'amount': (amount - amount / periods) / 2,
- 'fees': 100,
- 'date': line.date + relativedelta(months=-1)
- }).run()
+ self.env["account.loan.pay.amount"].create(
+ {
+ "loan_id": loan.id,
+ "amount": (amount - amount / periods) / 2,
+ "fees": 100,
+ "date": line.date + relativedelta(months=-1),
+ }
+ ).run()
with self.assertRaises(UserError):
- self.env['account.loan.pay.amount'].create({
- 'loan_id': loan.id,
- 'amount': amount,
- 'fees': 100,
- 'date': line.date,
- }).run()
+ self.env["account.loan.pay.amount"].create(
+ {"loan_id": loan.id, "amount": amount, "fees": 100, "date": line.date,}
+ ).run()
with self.assertRaises(UserError):
- self.env['account.loan.pay.amount'].create({
- 'loan_id': loan.id,
- 'amount': 0,
- 'fees': 100,
- 'date': line.date,
- }).run()
+ self.env["account.loan.pay.amount"].create(
+ {"loan_id": loan.id, "amount": 0, "fees": 100, "date": line.date,}
+ ).run()
with self.assertRaises(UserError):
- self.env['account.loan.pay.amount'].create({
- 'loan_id': loan.id,
- 'amount': -100,
- 'fees': 100,
- 'date': line.date,
- }).run()
+ self.env["account.loan.pay.amount"].create(
+ {"loan_id": loan.id, "amount": -100, "fees": 100, "date": line.date,}
+ ).run()
def test_fixed_annuity_begin_loan(self):
amount = 10000
periods = 24
- loan = self.create_loan('fixed-annuity-begin', amount, 1, periods)
+ loan = self.create_loan("fixed-annuity-begin", amount, 1, periods)
self.assertTrue(loan.line_ids)
self.assertEqual(len(loan.line_ids), periods)
line = loan.line_ids.filtered(lambda r: r.sequence == 1)
self.assertAlmostEqual(
- - numpy.pmt(1 / 100 / 12, 24, 10000, when='begin'),
- line.payment_amount, 2)
+ -numpy.pmt(1 / 100 / 12, 24, 10000, when="begin"), line.payment_amount, 2
+ )
self.assertEqual(line.long_term_principal_amount, 0)
loan.long_term_loan_account_id = self.lt_loan_account
loan.compute_lines()
@@ -185,24 +171,28 @@ class TestLoan(TransactionCase):
self.assertTrue(line)
self.assertFalse(line.move_ids)
self.assertFalse(line.invoice_ids)
- wzd = self.env['account.loan.generate.wizard'].create({})
+ wzd = self.env["account.loan.generate.wizard"].create({})
action = wzd.run()
self.assertTrue(action)
self.assertFalse(wzd.run())
self.assertTrue(line.move_ids)
- self.assertIn(line.move_ids.id, action['domain'][0][2])
+ self.assertIn(line.move_ids.id, action["domain"][0][2])
line.move_ids.post()
loan.rate = 2
loan.compute_lines()
line = loan.line_ids.filtered(lambda r: r.sequence == 1)
self.assertAlmostEqual(
- - numpy.pmt(1 / 100 / 12, periods, amount, when='begin'),
- line.payment_amount, 2)
+ -numpy.pmt(1 / 100 / 12, periods, amount, when="begin"),
+ line.payment_amount,
+ 2,
+ )
line = loan.line_ids.filtered(lambda r: r.sequence == 2)
self.assertAlmostEqual(
- - numpy.pmt(2 / 100 / 12, periods - 1,
- line.pending_principal_amount, when='begin'),
- line.payment_amount, 2
+ -numpy.pmt(
+ 2 / 100 / 12, periods - 1, line.pending_principal_amount, when="begin"
+ ),
+ line.payment_amount,
+ 2,
)
line = loan.line_ids.filtered(lambda r: r.sequence == 3)
with self.assertRaises(UserError):
@@ -211,12 +201,13 @@ class TestLoan(TransactionCase):
def test_fixed_annuity_loan(self):
amount = 10000
periods = 24
- loan = self.create_loan('fixed-annuity', amount, 1, periods)
+ loan = self.create_loan("fixed-annuity", amount, 1, periods)
self.assertTrue(loan.line_ids)
self.assertEqual(len(loan.line_ids), periods)
line = loan.line_ids.filtered(lambda r: r.sequence == 1)
self.assertAlmostEqual(
- - numpy.pmt(1 / 100 / 12, 24, 10000), line.payment_amount, 2)
+ -numpy.pmt(1 / 100 / 12, 24, 10000), line.payment_amount, 2
+ )
self.assertEqual(line.long_term_principal_amount, 0)
loan.long_term_loan_account_id = self.lt_loan_account
loan.compute_lines()
@@ -228,23 +219,24 @@ class TestLoan(TransactionCase):
self.assertTrue(line)
self.assertFalse(line.move_ids)
self.assertFalse(line.invoice_ids)
- wzd = self.env['account.loan.generate.wizard'].create({})
+ wzd = self.env["account.loan.generate.wizard"].create({})
action = wzd.run()
self.assertTrue(action)
self.assertFalse(wzd.run())
self.assertTrue(line.move_ids)
- self.assertIn(line.move_ids.id, action['domain'][0][2])
+ self.assertIn(line.move_ids.id, action["domain"][0][2])
line.move_ids.post()
loan.rate = 2
loan.compute_lines()
line = loan.line_ids.filtered(lambda r: r.sequence == 1)
self.assertAlmostEqual(
- - numpy.pmt(1 / 100 / 12, periods, amount), line.payment_amount, 2)
+ -numpy.pmt(1 / 100 / 12, periods, amount), line.payment_amount, 2
+ )
line = loan.line_ids.filtered(lambda r: r.sequence == 2)
self.assertAlmostEqual(
- - numpy.pmt(2 / 100 / 12, periods - 1,
- line.pending_principal_amount),
- line.payment_amount, 2
+ -numpy.pmt(2 / 100 / 12, periods - 1, line.pending_principal_amount),
+ line.payment_amount,
+ 2,
)
line = loan.line_ids.filtered(lambda r: r.sequence == 3)
with self.assertRaises(UserError):
@@ -253,14 +245,14 @@ class TestLoan(TransactionCase):
def test_fixed_principal_loan(self):
amount = 24000
periods = 24
- loan = self.create_loan('fixed-principal', amount, 1, periods)
+ loan = self.create_loan("fixed-principal", amount, 1, periods)
self.partner.property_account_payable_id = self.payable_account
- self.assertEqual(loan.journal_type, 'general')
+ self.assertEqual(loan.journal_type, "general")
loan.is_leasing = True
loan.post_invoice = False
- self.assertEqual(loan.journal_type, 'purchase')
+ self.assertEqual(loan.journal_type, "purchase")
loan.long_term_loan_account_id = self.lt_loan_account
- loan.rate_type = 'real'
+ loan.rate_type = "real"
loan.compute_lines()
self.assertTrue(loan.line_ids)
self.assertEqual(len(loan.line_ids), periods)
@@ -272,60 +264,69 @@ class TestLoan(TransactionCase):
self.assertTrue(line)
self.assertFalse(line.has_invoices)
self.assertFalse(line.has_moves)
- action = self.env['account.loan.generate.wizard'].create({
- 'date': fields.date.today(),
- 'loan_type': 'leasing',
- }).run()
+ action = (
+ self.env["account.loan.generate.wizard"]
+ .create({"date": fields.date.today(), "loan_type": "leasing",})
+ .run()
+ )
self.assertTrue(line.has_invoices)
self.assertFalse(line.has_moves)
- self.assertIn(line.invoice_ids.id, action['domain'][0][2])
+ self.assertIn(line.invoice_ids.id, action["domain"][0][2])
with self.assertRaises(UserError):
- self.env['account.loan.pay.amount'].create({
- 'loan_id': loan.id,
- 'amount': (amount - amount / periods) / 2,
- 'fees': 100,
- 'date': loan.line_ids.filtered(
- lambda r: r.sequence == 2).date
- }).run()
+ self.env["account.loan.pay.amount"].create(
+ {
+ "loan_id": loan.id,
+ "amount": (amount - amount / periods) / 2,
+ "fees": 100,
+ "date": loan.line_ids.filtered(lambda r: r.sequence == 2).date,
+ }
+ ).run()
with self.assertRaises(UserError):
- self.env['account.loan.pay.amount'].create({
- 'loan_id': loan.id,
- 'amount': (amount - amount / periods) / 2,
- 'fees': 100,
- 'date': loan.line_ids.filtered(
- lambda r: r.sequence == 1
- ).date + relativedelta(months=-1)
- }).run()
+ self.env["account.loan.pay.amount"].create(
+ {
+ "loan_id": loan.id,
+ "amount": (amount - amount / periods) / 2,
+ "fees": 100,
+ "date": loan.line_ids.filtered(lambda r: r.sequence == 1).date
+ + relativedelta(months=-1),
+ }
+ ).run()
line.invoice_ids.action_invoice_open()
self.assertTrue(line.has_moves)
self.assertIn(
line.move_ids.id,
- self.env['account.move'].search(
- loan.view_account_moves()['domain']).ids
+ self.env["account.move"].search(loan.view_account_moves()["domain"]).ids,
)
self.assertEqual(
line.invoice_ids.id,
- self.env['account.invoice'].search(
- loan.view_account_invoices()['domain']).id
+ self.env["account.invoice"]
+ .search(loan.view_account_invoices()["domain"])
+ .id,
)
with self.assertRaises(UserError):
- self.env['account.loan.pay.amount'].create({
- 'loan_id': loan.id,
- 'amount': (amount - amount / periods) / 2,
- 'fees': 100,
- 'date': loan.line_ids.filtered(
- lambda r: r.sequence == periods).date
- }).run()
- self.env['account.loan.pay.amount'].create({
- 'loan_id': loan.id,
- 'amount': (amount - amount / periods) / 2,
- 'date': line.date,
- 'fees': 100,
- }).run()
+ self.env["account.loan.pay.amount"].create(
+ {
+ "loan_id": loan.id,
+ "amount": (amount - amount / periods) / 2,
+ "fees": 100,
+ "date": loan.line_ids.filtered(
+ lambda r: r.sequence == periods
+ ).date,
+ }
+ ).run()
+ self.env["account.loan.pay.amount"].create(
+ {
+ "loan_id": loan.id,
+ "amount": (amount - amount / periods) / 2,
+ "date": line.date,
+ "fees": 100,
+ }
+ ).run()
line = loan.line_ids.filtered(lambda r: r.sequence == 2)
self.assertEqual(loan.periods, periods + 1)
self.assertAlmostEqual(
- line.principal_amount, (amount - amount / periods) / 2, 2)
+ line.principal_amount, (amount - amount / periods) / 2, 2
+ )
line = loan.line_ids.filtered(lambda r: r.sequence == 3)
self.assertEqual(amount / periods / 2, line.principal_amount)
line = loan.line_ids.filtered(lambda r: r.sequence == 4)
@@ -335,13 +336,13 @@ class TestLoan(TransactionCase):
def test_fixed_principal_loan_auto_post(self):
amount = 24000
periods = 24
- loan = self.create_loan('fixed-principal', amount, 1, periods)
+ loan = self.create_loan("fixed-principal", amount, 1, periods)
self.partner.property_account_payable_id = self.payable_account
- self.assertEqual(loan.journal_type, 'general')
+ self.assertEqual(loan.journal_type, "general")
loan.is_leasing = True
- self.assertEqual(loan.journal_type, 'purchase')
+ self.assertEqual(loan.journal_type, "purchase")
loan.long_term_loan_account_id = self.lt_loan_account
- loan.rate_type = 'real'
+ loan.rate_type = "real"
loan.compute_lines()
self.assertTrue(loan.line_ids)
self.assertEqual(len(loan.line_ids), periods)
@@ -353,38 +354,37 @@ class TestLoan(TransactionCase):
self.assertTrue(line)
self.assertFalse(line.has_invoices)
self.assertFalse(line.has_moves)
- self.env['account.loan.generate.wizard'].create({
- 'date': fields.date.today(),
- 'loan_type': 'leasing',
- }).run()
+ self.env["account.loan.generate.wizard"].create(
+ {"date": fields.date.today(), "loan_type": "leasing",}
+ ).run()
self.assertTrue(line.has_invoices)
self.assertTrue(line.has_moves)
def test_interests_on_end_loan(self):
amount = 10000
periods = 10
- loan = self.create_loan('interest', amount, 1, periods)
+ loan = self.create_loan("interest", amount, 1, periods)
loan.payment_on_first_period = False
loan.start_date = fields.Date.today()
- loan.rate_type = 'ear'
+ loan.rate_type = "ear"
loan.compute_lines()
self.assertTrue(loan.line_ids)
self.assertEqual(len(loan.line_ids), periods)
self.assertEqual(0, loan.line_ids[0].principal_amount)
- self.assertEqual(amount, loan.line_ids.filtered(
- lambda r: r.sequence == periods
- ).principal_amount)
+ self.assertEqual(
+ amount,
+ loan.line_ids.filtered(lambda r: r.sequence == periods).principal_amount,
+ )
self.post(loan)
self.assertEqual(loan.payment_amount, 0)
self.assertEqual(loan.interests_amount, 0)
self.assertEqual(loan.pending_principal_amount, amount)
- self.assertFalse(loan.line_ids.filtered(
- lambda r: r.date <= loan.start_date))
+ self.assertFalse(loan.line_ids.filtered(lambda r: r.date <= loan.start_date))
for line in loan.line_ids:
- self.assertEqual(loan.state, 'posted')
+ self.assertEqual(loan.state, "posted")
line.view_process_values()
line.move_ids.post()
- self.assertEqual(loan.state, 'closed')
+ self.assertEqual(loan.state, "closed")
self.assertEqual(loan.payment_amount - loan.interests_amount, amount)
self.assertEqual(loan.pending_principal_amount, 0)
@@ -392,57 +392,60 @@ class TestLoan(TransactionCase):
def test_cancel_loan(self):
amount = 10000
periods = 10
- loan = self.create_loan('fixed-annuity', amount, 1, periods)
+ loan = self.create_loan("fixed-annuity", amount, 1, periods)
self.post(loan)
line = loan.line_ids.filtered(lambda r: r.sequence == 1)
line.view_process_values()
line.move_ids.post()
- pay = self.env['account.loan.pay.amount'].create({
- 'loan_id': loan.id,
- 'amount': 0,
- 'fees': 100,
- 'date': line.date
- })
+ pay = self.env["account.loan.pay.amount"].create(
+ {"loan_id": loan.id, "amount": 0, "fees": 100, "date": line.date}
+ )
pay.cancel_loan = True
pay._onchange_cancel_loan()
self.assertEqual(pay.amount, line.final_pending_principal_amount)
pay.run()
- self.assertEqual(loan.state, 'cancelled')
+ self.assertEqual(loan.state, "cancelled")
def post(self, loan):
self.assertFalse(loan.move_ids)
- post = self.env['account.loan.post'].with_context(
- default_loan_id=loan.id
- ).create({})
+ post = (
+ self.env["account.loan.post"]
+ .with_context(default_loan_id=loan.id)
+ .create({})
+ )
post.run()
self.assertTrue(loan.move_ids)
with self.assertRaises(UserError):
post.run()
def create_account(self, code, name, type_id):
- return self.env['account.account'].create({
- 'company_id': self.company.id,
- 'name': name,
- 'code': code,
- 'user_type_id': type_id,
- 'reconcile': True,
- })
+ return self.env["account.account"].create(
+ {
+ "company_id": self.company.id,
+ "name": name,
+ "code": code,
+ "user_type_id": type_id,
+ "reconcile": True,
+ }
+ )
def create_loan(self, type_loan, amount, rate, periods):
- loan = self.env['account.loan'].create({
- 'journal_id': self.journal.id,
- 'rate_type': 'napr',
- 'loan_type': type_loan,
- 'loan_amount': amount,
- 'payment_on_first_period': True,
- 'rate': rate,
- 'periods': periods,
- 'leased_asset_account_id': self.asset_account.id,
- 'short_term_loan_account_id': self.loan_account.id,
- 'interest_expenses_account_id': self.interests_account.id,
- 'product_id': self.product.id,
- 'interests_product_id': self.interests_product.id,
- 'partner_id': self.partner.id,
- })
+ loan = self.env["account.loan"].create(
+ {
+ "journal_id": self.journal.id,
+ "rate_type": "napr",
+ "loan_type": type_loan,
+ "loan_amount": amount,
+ "payment_on_first_period": True,
+ "rate": rate,
+ "periods": periods,
+ "leased_asset_account_id": self.asset_account.id,
+ "short_term_loan_account_id": self.loan_account.id,
+ "interest_expenses_account_id": self.interests_account.id,
+ "product_id": self.product.id,
+ "interests_product_id": self.interests_product.id,
+ "partner_id": self.partner.id,
+ }
+ )
loan.compute_lines()
return loan
diff --git a/account_loan/views/account_loan_view.xml b/account_loan/views/account_loan_view.xml
index 90ea9343a..8ff0758f4 100644
--- a/account_loan/views/account_loan_view.xml
+++ b/account_loan/views/account_loan_view.xml
@@ -1,202 +1,226 @@
-
+
-
account.loan.tree
account.loan
-
-
-
-
+
+
+
+
-
account.loan.form
account.loan
-
account.loan.line.tree
account.loan.line
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
account.loan.line.form
account.loan.line
-
-
-
-
-
+
+
diff --git a/account_loan/views/account_move_view.xml b/account_loan/views/account_move_view.xml
index 35a331ded..44bed512a 100644
--- a/account_loan/views/account_move_view.xml
+++ b/account_loan/views/account_move_view.xml
@@ -1,21 +1,20 @@
-
+
-
-
Add to_be_reversed and reversal_id fields
account.move
-
+
-
+
-
diff --git a/account_loan/wizard/account_loan_generate_entries.py b/account_loan/wizard/account_loan_generate_entries.py
index b777ce602..c749a18af 100644
--- a/account_loan/wizard/account_loan_generate_entries.py
+++ b/account_loan/wizard/account_loan_generate_entries.py
@@ -5,45 +5,40 @@ from odoo import api, fields, models
class AccountLoanGenerateWizard(models.TransientModel):
_name = "account.loan.generate.wizard"
- _description = 'Loan generate wizard'
+ _description = "Loan generate wizard"
date = fields.Date(
- 'Account Date',
+ "Account Date",
required=True,
help="Choose the period for which you want to automatically post the "
- "depreciation lines of running assets",
- default=fields.Date.context_today)
- loan_type = fields.Selection([
- ('leasing', 'Leasings'),
- ('loan', 'Loans'),
- ], required=True, default='loan')
+ "depreciation lines of running assets",
+ default=fields.Date.context_today,
+ )
+ loan_type = fields.Selection(
+ [("leasing", "Leasings"), ("loan", "Loans"),], required=True, default="loan"
+ )
def run_leasing(self):
- created_ids = self.env['account.loan'].generate_leasing_entries(
- self.date
- )
- action = self.env.ref('account.action_invoice_tree2')
+ created_ids = self.env["account.loan"].generate_leasing_entries(self.date)
+ action = self.env.ref("account.action_invoice_tree2")
result = action.read()[0]
if len(created_ids) == 0:
return
- result['domain'] = [
- ('id', 'in', created_ids),
- ('type', '=', 'in_invoice')
- ]
+ result["domain"] = [("id", "in", created_ids), ("type", "=", "in_invoice")]
return result
def run_loan(self):
- created_ids = self.env['account.loan'].generate_loan_entries(self.date)
- action = self.env.ref('account.action_move_line_form')
+ created_ids = self.env["account.loan"].generate_loan_entries(self.date)
+ action = self.env.ref("account.action_move_line_form")
result = action.read()[0]
if len(created_ids) == 0:
return
- result['domain'] = [('id', 'in', created_ids)]
+ result["domain"] = [("id", "in", created_ids)]
return result
@api.multi
def run(self):
self.ensure_one()
- if self.loan_type == 'leasing':
+ if self.loan_type == "leasing":
return self.run_leasing()
return self.run_loan()
diff --git a/account_loan/wizard/account_loan_generate_entries_view.xml b/account_loan/wizard/account_loan_generate_entries_view.xml
index 8e80792bd..88b188e76 100644
--- a/account_loan/wizard/account_loan_generate_entries_view.xml
+++ b/account_loan/wizard/account_loan_generate_entries_view.xml
@@ -1,37 +1,42 @@
-
+
-
-
Pay amount
account.loan.generate.wizard
-
Generate moves
account.loan.generate.wizard
form
new
-
-
+
diff --git a/account_loan/wizard/account_loan_pay_amount.py b/account_loan/wizard/account_loan_pay_amount.py
index de29c9929..802f0f120 100644
--- a/account_loan/wizard/account_loan_pay_amount.py
+++ b/account_loan/wizard/account_loan_pay_amount.py
@@ -1,54 +1,42 @@
# Copyright 2018 Creu Blanca
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
-from odoo import api, fields, models, _
+from odoo import _, api, fields, models
from odoo.exceptions import UserError
class AccountLoan(models.TransientModel):
- _name = 'account.loan.pay.amount'
- _description = 'Loan pay amount'
+ _name = "account.loan.pay.amount"
+ _description = "Loan pay amount"
- loan_id = fields.Many2one(
- 'account.loan',
- required=True,
- readonly=True,
- )
+ loan_id = fields.Many2one("account.loan", required=True, readonly=True,)
currency_id = fields.Many2one(
- 'res.currency',
- related='loan_id.currency_id',
- readonly=True
- )
- cancel_loan = fields.Boolean(
- default=False,
+ "res.currency", related="loan_id.currency_id", readonly=True
)
+ cancel_loan = fields.Boolean(default=False,)
date = fields.Date(required=True, default=fields.Date.today())
amount = fields.Monetary(
- currency_field='currency_id',
- string='Amount to reduce from Principal',
- )
- fees = fields.Monetary(
- currency_field='currency_id',
- string='Bank fees'
+ currency_field="currency_id", string="Amount to reduce from Principal",
)
+ fees = fields.Monetary(currency_field="currency_id", string="Bank fees")
- @api.onchange('cancel_loan')
+ @api.onchange("cancel_loan")
def _onchange_cancel_loan(self):
if self.cancel_loan:
- self.amount = max(self.loan_id.line_ids.filtered(
- lambda r: not r.move_ids and not r.invoice_ids).mapped(
- 'pending_principal_amount'
- )
+ self.amount = max(
+ self.loan_id.line_ids.filtered(
+ lambda r: not r.move_ids and not r.invoice_ids
+ ).mapped("pending_principal_amount")
)
def new_line_vals(self, sequence):
return {
- 'loan_id': self.loan_id.id,
- 'sequence': sequence,
- 'payment_amount': self.amount + self.fees,
- 'rate': 0,
- 'interests_amount': self.fees,
- 'date': self.date,
+ "loan_id": self.loan_id.id,
+ "sequence": sequence,
+ "payment_amount": self.amount + self.fees,
+ "rate": 0,
+ "interests_amount": self.fees,
+ "date": self.date,
}
@api.multi
@@ -56,41 +44,40 @@ class AccountLoan(models.TransientModel):
self.ensure_one()
if self.loan_id.is_leasing:
if self.loan_id.line_ids.filtered(
- lambda r: r.date < self.date and not r.invoice_ids
+ lambda r: r.date < self.date and not r.invoice_ids
):
- raise UserError(_('Some invoices are not created'))
+ raise UserError(_("Some invoices are not created"))
if self.loan_id.line_ids.filtered(
- lambda r: r.date > self.date and r.invoice_ids
+ lambda r: r.date > self.date and r.invoice_ids
):
- raise UserError(_('Some future invoices already exists'))
+ raise UserError(_("Some future invoices already exists"))
if self.loan_id.line_ids.filtered(
- lambda r: r.date < self.date and not r.move_ids
+ lambda r: r.date < self.date and not r.move_ids
):
- raise UserError(_('Some moves are not created'))
- if self.loan_id.line_ids.filtered(
- lambda r: r.date > self.date and r.move_ids
- ):
- raise UserError(_('Some future moves already exists'))
- lines = self.loan_id.line_ids.filtered(
- lambda r: r.date > self.date).sorted('sequence', reverse=True)
- sequence = min(lines.mapped('sequence'))
+ raise UserError(_("Some moves are not created"))
+ if self.loan_id.line_ids.filtered(lambda r: r.date > self.date and r.move_ids):
+ raise UserError(_("Some future moves already exists"))
+ lines = self.loan_id.line_ids.filtered(lambda r: r.date > self.date).sorted(
+ "sequence", reverse=True
+ )
+ sequence = min(lines.mapped("sequence"))
for line in lines:
line.sequence += 1
old_line = lines.filtered(lambda r: r.sequence == sequence + 1)
pending = old_line.pending_principal_amount
if self.loan_id.currency_id.compare_amounts(self.amount, pending) == 1:
- raise UserError(_('Amount cannot be bigger than debt'))
+ raise UserError(_("Amount cannot be bigger than debt"))
if self.loan_id.currency_id.compare_amounts(self.amount, 0) <= 0:
- raise UserError(_('Amount cannot be less than zero'))
+ raise UserError(_("Amount cannot be less than zero"))
self.loan_id.periods += 1
self.loan_id.fixed_periods = self.loan_id.periods - sequence
self.loan_id.fixed_loan_amount = pending - self.amount
- new_line = self.env['account.loan.line'].create(
- self.new_line_vals(sequence))
+ new_line = self.env["account.loan.line"].create(self.new_line_vals(sequence))
new_line.long_term_pending_principal_amount = (
- old_line.long_term_pending_principal_amount)
+ old_line.long_term_pending_principal_amount
+ )
amount = self.loan_id.loan_amount
- for line in self.loan_id.line_ids.sorted('sequence'):
+ for line in self.loan_id.line_ids.sorted("sequence"):
if line.move_ids:
amount = line.final_pending_principal_amount
else:
@@ -102,5 +89,5 @@ class AccountLoan(models.TransientModel):
if self.loan_id.long_term_loan_account_id:
self.loan_id.check_long_term_principal_amount()
if self.loan_id.currency_id.compare_amounts(pending, self.amount) == 0:
- self.loan_id.write({'state': 'cancelled'})
+ self.loan_id.write({"state": "cancelled"})
return new_line.view_process_values()
diff --git a/account_loan/wizard/account_loan_pay_amount_view.xml b/account_loan/wizard/account_loan_pay_amount_view.xml
index d13a40e72..288c4bd9b 100644
--- a/account_loan/wizard/account_loan_pay_amount_view.xml
+++ b/account_loan/wizard/account_loan_pay_amount_view.xml
@@ -1,35 +1,34 @@
-
+
-
-
Pay amount
account.loan.pay.amount
-
-
-
Pay amount
account.loan.pay.amount
@@ -37,5 +36,4 @@
new
{'default_loan_id': active_id}
-
diff --git a/account_loan/wizard/account_loan_post.py b/account_loan/wizard/account_loan_post.py
index 4fca2e477..fc39d6cfb 100644
--- a/account_loan/wizard/account_loan_post.py
+++ b/account_loan/wizard/account_loan_post.py
@@ -1,24 +1,24 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
-from odoo import api, fields, models, _
+from odoo import _, api, fields, models
from odoo.exceptions import UserError
class AccountLoanPost(models.TransientModel):
_name = "account.loan.post"
- _description = 'Loan post'
+ _description = "Loan post"
@api.model
def _default_journal_id(self):
- loan_id = self._context.get('default_loan_id')
+ loan_id = self._context.get("default_loan_id")
if loan_id:
- return self.env['account.loan'].browse(loan_id).journal_id.id
+ return self.env["account.loan"].browse(loan_id).journal_id.id
@api.model
def _default_account_id(self):
- loan_id = self._context.get('default_loan_id')
+ loan_id = self._context.get("default_loan_id")
if loan_id:
- loan = self.env['account.loan'].browse(loan_id)
+ loan = self.env["account.loan"].browse(loan_id)
if loan.is_leasing:
return loan.leased_asset_account_id.id
else:
@@ -26,69 +26,67 @@ class AccountLoanPost(models.TransientModel):
force_company=loan.company_id.id
).property_account_receivable_id.id
- loan_id = fields.Many2one(
- 'account.loan',
- required=True,
- readonly=True,
- )
+ loan_id = fields.Many2one("account.loan", required=True, readonly=True,)
journal_id = fields.Many2one(
- 'account.journal',
- required=True,
- default=_default_journal_id
+ "account.journal", required=True, default=_default_journal_id
)
account_id = fields.Many2one(
- 'account.account',
- required=True,
- default=_default_account_id
+ "account.account", required=True, default=_default_account_id
)
def move_line_vals(self):
res = list()
partner = self.loan_id.partner_id.with_context(
- force_company=self.loan_id.company_id.id)
+ force_company=self.loan_id.company_id.id
+ )
line = self.loan_id.line_ids.filtered(lambda r: r.sequence == 1)
- res.append({
- 'account_id': self.account_id.id,
- 'partner_id': partner.id,
- 'credit': 0,
- 'debit': line.pending_principal_amount,
- })
+ res.append(
+ {
+ "account_id": self.account_id.id,
+ "partner_id": partner.id,
+ "credit": 0,
+ "debit": line.pending_principal_amount,
+ }
+ )
+ if line.pending_principal_amount - line.long_term_pending_principal_amount > 0:
+ res.append(
+ {
+ "account_id": self.loan_id.short_term_loan_account_id.id,
+ "credit": (
+ line.pending_principal_amount
+ - line.long_term_pending_principal_amount
+ ),
+ "debit": 0,
+ }
+ )
if (
- line.pending_principal_amount -
line.long_term_pending_principal_amount > 0
+ and self.loan_id.long_term_loan_account_id
):
- res.append({
- 'account_id': self.loan_id.short_term_loan_account_id.id,
- 'credit': (line.pending_principal_amount -
- line.long_term_pending_principal_amount),
- 'debit': 0,
- })
- if (
- line.long_term_pending_principal_amount > 0 and
- self.loan_id.long_term_loan_account_id
- ):
- res.append({
- 'account_id': self.loan_id.long_term_loan_account_id.id,
- 'credit': line.long_term_pending_principal_amount,
- 'debit': 0,
- })
+ res.append(
+ {
+ "account_id": self.loan_id.long_term_loan_account_id.id,
+ "credit": line.long_term_pending_principal_amount,
+ "debit": 0,
+ }
+ )
return res
def move_vals(self):
return {
- 'loan_id': self.loan_id.id,
- 'date': self.loan_id.start_date,
- 'ref': self.loan_id.name,
- 'journal_id': self.journal_id.id,
- 'line_ids': [(0, 0, vals) for vals in self.move_line_vals()]
+ "loan_id": self.loan_id.id,
+ "date": self.loan_id.start_date,
+ "ref": self.loan_id.name,
+ "journal_id": self.journal_id.id,
+ "line_ids": [(0, 0, vals) for vals in self.move_line_vals()],
}
@api.multi
def run(self):
self.ensure_one()
- if self.loan_id.state != 'draft':
- raise UserError(_('Only loans in draft state can be posted'))
+ if self.loan_id.state != "draft":
+ raise UserError(_("Only loans in draft state can be posted"))
self.loan_id.post()
- move = self.env['account.move'].create(self.move_vals())
+ move = self.env["account.move"].create(self.move_vals())
move.post()
diff --git a/account_loan/wizard/account_loan_post_view.xml b/account_loan/wizard/account_loan_post_view.xml
index 0595a05bb..d3a4acc43 100644
--- a/account_loan/wizard/account_loan_post_view.xml
+++ b/account_loan/wizard/account_loan_post_view.xml
@@ -1,32 +1,31 @@
-
+
-
-
Post loan
account.loan.post
-
-
-
Post loan
account.loan.post
@@ -34,5 +33,4 @@
new
{'default_loan_id': active_id}
-
diff --git a/setup/account_loan/odoo/addons/account_loan b/setup/account_loan/odoo/addons/account_loan
new file mode 120000
index 000000000..4e53110dd
--- /dev/null
+++ b/setup/account_loan/odoo/addons/account_loan
@@ -0,0 +1 @@
+../../../../account_loan
\ No newline at end of file
diff --git a/setup/account_loan/setup.py b/setup/account_loan/setup.py
new file mode 100644
index 000000000..28c57bb64
--- /dev/null
+++ b/setup/account_loan/setup.py
@@ -0,0 +1,6 @@
+import setuptools
+
+setuptools.setup(
+ setup_requires=['setuptools-odoo'],
+ odoo_addon=True,
+)