[IMP] account_loan: black,isort,prettier
This commit is contained in:
parent
6a2179377e
commit
e6972252f5
@ -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",],},
|
||||
}
|
||||
|
@ -1,18 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<!--
|
||||
Copyright 2017 Eficent Business and IT Consulting Services, S.L.
|
||||
Copyright 2017 Creu Blanca
|
||||
License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
|
||||
-->
|
||||
|
||||
<odoo>
|
||||
|
||||
<record id="seq_account_loan" model="ir.sequence">
|
||||
<field name="name">Account loan sequence</field>
|
||||
<field name="code">account.loan</field>
|
||||
<field name="prefix">ACL</field>
|
||||
<field name="padding">6</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -1,12 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo noupdate="1">
|
||||
<record id="account_loan_multi_company_rule" model="ir.rule">
|
||||
<field name="name">Account loan multi-company</field>
|
||||
<field ref="model_account_loan" name="model_id"/>
|
||||
<field eval="True" name="global"/>
|
||||
<field ref="model_account_loan" name="model_id" />
|
||||
<field eval="True" name="global" />
|
||||
<field name="domain_force">
|
||||
['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
@ -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
|
||||
|
@ -1,202 +1,226 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<!-- Copyright 2011 Alexis de Lattre <alexis.delattre@akretion.com>
|
||||
Copyright 2016 Antonio Espinosa <antonio.espinosa@tecnativa.com>
|
||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
|
||||
<odoo>
|
||||
|
||||
<record id="account_loan_tree" model="ir.ui.view">
|
||||
<field name="name">account.loan.tree</field>
|
||||
<field name="model">account.loan</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Loans">
|
||||
<field name="name"/>
|
||||
<field name="company_id"/>
|
||||
<field name="is_leasing"/>
|
||||
<field name="state"/>
|
||||
<field name="name" />
|
||||
<field name="company_id" />
|
||||
<field name="is_leasing" />
|
||||
<field name="state" />
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="account_loan_form" model="ir.ui.view">
|
||||
<field name="name">account.loan.form</field>
|
||||
<field name="model">account.loan</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Loan">
|
||||
<header>
|
||||
<button name="compute_lines" type="object"
|
||||
string="Compute items"/>
|
||||
<button name="%(account_loan_post_action)d"
|
||||
states="draft" type="action"
|
||||
string="Post"/>
|
||||
<field name="state" widget="statusbar"/>
|
||||
<button name="compute_lines" type="object" string="Compute items" />
|
||||
<button
|
||||
name="%(account_loan_post_action)d"
|
||||
states="draft"
|
||||
type="action"
|
||||
string="Post"
|
||||
/>
|
||||
<field name="state" widget="statusbar" />
|
||||
</header>
|
||||
<sheet>
|
||||
<div class="oe_button_box">
|
||||
<button name="view_account_moves"
|
||||
class="oe_stat_button"
|
||||
icon="fa-bars"
|
||||
attrs="{'invisible': [('state', '=', 'draft')]}"
|
||||
type="object" string="Moves"/>
|
||||
<button name="view_account_invoices"
|
||||
class="oe_stat_button"
|
||||
icon="fa-pencil-square-o"
|
||||
attrs="{'invisible': ['|', ('state', '=', 'draft'), ('is_leasing', '=', False)]}"
|
||||
type="object" string="Invoices"/>
|
||||
<button name="%(account_loan_pay_amount_action)d"
|
||||
class="oe_stat_button" icon="fa-usd"
|
||||
attrs="{'invisible': [('state', '!=', 'posted')]}"
|
||||
type="action" string="Pay amount"/>
|
||||
<button
|
||||
name="view_account_moves"
|
||||
class="oe_stat_button"
|
||||
icon="fa-bars"
|
||||
attrs="{'invisible': [('state', '=', 'draft')]}"
|
||||
type="object"
|
||||
string="Moves"
|
||||
/>
|
||||
<button
|
||||
name="view_account_invoices"
|
||||
class="oe_stat_button"
|
||||
icon="fa-pencil-square-o"
|
||||
attrs="{'invisible': ['|', ('state', '=', 'draft'), ('is_leasing', '=', False)]}"
|
||||
type="object"
|
||||
string="Invoices"
|
||||
/>
|
||||
<button
|
||||
name="%(account_loan_pay_amount_action)d"
|
||||
class="oe_stat_button"
|
||||
icon="fa-usd"
|
||||
attrs="{'invisible': [('state', '!=', 'posted')]}"
|
||||
type="action"
|
||||
string="Pay amount"
|
||||
/>
|
||||
</div>
|
||||
<h1>
|
||||
<field name="name"/>
|
||||
<field name="name" />
|
||||
</h1>
|
||||
<group>
|
||||
<group>
|
||||
<field name="company_id"
|
||||
options="{'no_create': True}"/>
|
||||
<field name="loan_type"/>
|
||||
<field name="loan_amount"/>
|
||||
<field name="company_id" options="{'no_create': True}" />
|
||||
<field name="loan_type" />
|
||||
<field name="loan_amount" />
|
||||
</group>
|
||||
<group>
|
||||
<field name="rate_type"/>
|
||||
<field name="rate"/>
|
||||
<field name="rate_period"/>
|
||||
<field name="rate_type" />
|
||||
<field name="rate" />
|
||||
<field name="rate_period" />
|
||||
</group>
|
||||
</group>
|
||||
<group>
|
||||
<group>
|
||||
<field name="partner_id"/>
|
||||
<field name="start_date"/>
|
||||
<field name="periods"/>
|
||||
<field name="method_period"/>
|
||||
<field name="partner_id" />
|
||||
<field name="start_date" />
|
||||
<field name="periods" />
|
||||
<field name="method_period" />
|
||||
</group>
|
||||
<group>
|
||||
<field name="is_leasing"/>
|
||||
<field name="round_on_end"/>
|
||||
<field name="payment_on_first_period"/>
|
||||
<field name="is_leasing" />
|
||||
<field name="round_on_end" />
|
||||
<field name="payment_on_first_period" />
|
||||
</group>
|
||||
</group>
|
||||
<group attrs="{'invisible':[('state', '=', 'draft')]}">
|
||||
<group>
|
||||
<field name="pending_principal_amount"/>
|
||||
<field name="payment_amount"/>
|
||||
<field name="interests_amount"/>
|
||||
<field name="pending_principal_amount" />
|
||||
<field name="payment_amount" />
|
||||
<field name="interests_amount" />
|
||||
</group>
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="Items" id="items">
|
||||
<field name="line_ids"/>
|
||||
<field name="line_ids" />
|
||||
</page>
|
||||
<page string="Accounts" id="accounting">
|
||||
<group>
|
||||
<group>
|
||||
<field name="journal_id"/>
|
||||
<field name="short_term_loan_account_id"/>
|
||||
<field name="journal_type" invisible="1"/>
|
||||
<field name="journal_id" />
|
||||
<field name="short_term_loan_account_id" />
|
||||
<field name="journal_type" invisible="1" />
|
||||
</group>
|
||||
<group>
|
||||
<field name="long_term_loan_account_id"/>
|
||||
<field name="interest_expenses_account_id"/>
|
||||
<field name="currency_id" invisible="1"/>
|
||||
<field name="long_term_loan_account_id" />
|
||||
<field name="interest_expenses_account_id" />
|
||||
<field name="currency_id" invisible="1" />
|
||||
</group>
|
||||
</group>
|
||||
</page>
|
||||
<page string="Leasing" id="leasing"
|
||||
attrs="{'invisible': [('is_leasing', '=', False)]}">
|
||||
<page
|
||||
string="Leasing"
|
||||
id="leasing"
|
||||
attrs="{'invisible': [('is_leasing', '=', False)]}"
|
||||
>
|
||||
<group>
|
||||
<group>
|
||||
<field name="leased_asset_account_id"
|
||||
attrs="{'required': [('is_leasing', '=', True)]}"/>
|
||||
<field name="residual_amount"/>
|
||||
<field
|
||||
name="leased_asset_account_id"
|
||||
attrs="{'required': [('is_leasing', '=', True)]}"
|
||||
/>
|
||||
<field name="residual_amount" />
|
||||
</group>
|
||||
<group>
|
||||
<field name="product_id"
|
||||
attrs="{'required': [('is_leasing', '=', True)]}"/>
|
||||
<field name="interests_product_id"
|
||||
attrs="{'required': [('is_leasing', '=', True)]}"/>
|
||||
<field name="post_invoice"/>
|
||||
<field
|
||||
name="product_id"
|
||||
attrs="{'required': [('is_leasing', '=', True)]}"
|
||||
/>
|
||||
<field
|
||||
name="interests_product_id"
|
||||
attrs="{'required': [('is_leasing', '=', True)]}"
|
||||
/>
|
||||
<field name="post_invoice" />
|
||||
</group>
|
||||
</group>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_follower_ids"
|
||||
widget="mail_followers"/>
|
||||
<field name="activity_ids" widget="mail_activity"/>
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
<field name="message_follower_ids" widget="mail_followers" />
|
||||
<field name="activity_ids" widget="mail_activity" />
|
||||
<field name="message_ids" widget="mail_thread" />
|
||||
</div>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="account_loan_line_tree" model="ir.ui.view">
|
||||
<field name="name">account.loan.line.tree</field>
|
||||
<field name="model">account.loan.line</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Loan items" create="0">
|
||||
<field name="sequence"/>
|
||||
<field name="date"/>
|
||||
<field name="rate"/>
|
||||
<field name="pending_principal_amount"/>
|
||||
<field name="payment_amount" sum="Total payments"/>
|
||||
<field name="principal_amount"/>
|
||||
<field name="interests_amount" sum="Total interests"/>
|
||||
<field name="long_term_pending_principal_amount"
|
||||
attrs="{'invisible': [('long_term_loan_account_id', '=', False)]}"/>
|
||||
<field name="long_term_principal_amount"
|
||||
attrs="{'invisible': [('long_term_loan_account_id', '=', False)]}"/>
|
||||
<field name="long_term_loan_account_id" invisible="1"/>
|
||||
<field name="loan_state" invisible="1"/>
|
||||
<field name="is_leasing" invisible="1"/>
|
||||
<field name="has_invoices" invisible="1"/>
|
||||
<field name="has_moves" invisible="1"/>
|
||||
<field name="currency_id" invisible="1"/>
|
||||
<button name="view_account_values" string="Values"
|
||||
type="object" icon="fa-eye"
|
||||
attrs="{'invisible': [('has_moves', '=', False), ('has_invoices', '=', False)]}"/>
|
||||
<button name="view_process_values" string="Process"
|
||||
type="object" icon="fa-cogs"
|
||||
attrs="{'invisible': ['|', '|', ('has_moves', '=', True), ('has_invoices', '=', True), ('loan_state', '!=', 'posted')]}"/>
|
||||
<field name="sequence" />
|
||||
<field name="date" />
|
||||
<field name="rate" />
|
||||
<field name="pending_principal_amount" />
|
||||
<field name="payment_amount" sum="Total payments" />
|
||||
<field name="principal_amount" />
|
||||
<field name="interests_amount" sum="Total interests" />
|
||||
<field
|
||||
name="long_term_pending_principal_amount"
|
||||
attrs="{'invisible': [('long_term_loan_account_id', '=', False)]}"
|
||||
/>
|
||||
<field
|
||||
name="long_term_principal_amount"
|
||||
attrs="{'invisible': [('long_term_loan_account_id', '=', False)]}"
|
||||
/>
|
||||
<field name="long_term_loan_account_id" invisible="1" />
|
||||
<field name="loan_state" invisible="1" />
|
||||
<field name="is_leasing" invisible="1" />
|
||||
<field name="has_invoices" invisible="1" />
|
||||
<field name="has_moves" invisible="1" />
|
||||
<field name="currency_id" invisible="1" />
|
||||
<button
|
||||
name="view_account_values"
|
||||
string="Values"
|
||||
type="object"
|
||||
icon="fa-eye"
|
||||
attrs="{'invisible': [('has_moves', '=', False), ('has_invoices', '=', False)]}"
|
||||
/>
|
||||
<button
|
||||
name="view_process_values"
|
||||
string="Process"
|
||||
type="object"
|
||||
icon="fa-cogs"
|
||||
attrs="{'invisible': ['|', '|', ('has_moves', '=', True), ('has_invoices', '=', True), ('loan_state', '!=', 'posted')]}"
|
||||
/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="account_loan_line_form" model="ir.ui.view">
|
||||
<field name="name">account.loan.line.form</field>
|
||||
<field name="model">account.loan.line</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<group>
|
||||
<field name="sequence"/>
|
||||
<field name="rate"/>
|
||||
<field name="date"/>
|
||||
<field name="sequence" />
|
||||
<field name="rate" />
|
||||
<field name="date" />
|
||||
</group>
|
||||
<group>
|
||||
<group>
|
||||
<field name="pending_principal_amount"/>
|
||||
<field name="payment_amount"/>
|
||||
<field name="principal_amount"/>
|
||||
<field name="interests_amount"/>
|
||||
<field name="final_pending_principal_amount"/>
|
||||
<field name="pending_principal_amount" />
|
||||
<field name="payment_amount" />
|
||||
<field name="principal_amount" />
|
||||
<field name="interests_amount" />
|
||||
<field name="final_pending_principal_amount" />
|
||||
</group>
|
||||
<group>
|
||||
<field name="long_term_pending_principal_amount"/>
|
||||
<field name="long_term_principal_amount"/>
|
||||
<field name="long_term_pending_principal_amount" />
|
||||
<field name="long_term_principal_amount" />
|
||||
</group>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<act_window
|
||||
id="account_loan_action"
|
||||
name="Loans"
|
||||
res_model="account.loan"/>
|
||||
|
||||
<menuitem id="account_loan_menu"
|
||||
parent="account.menu_finance_entries" sequence="80"
|
||||
name="Loans"
|
||||
action="account_loan_action"/>
|
||||
|
||||
<act_window id="account_loan_action" name="Loans" res_model="account.loan" />
|
||||
<menuitem
|
||||
id="account_loan_menu"
|
||||
parent="account.menu_finance_entries"
|
||||
sequence="80"
|
||||
name="Loans"
|
||||
action="account_loan_action"
|
||||
/>
|
||||
</odoo>
|
||||
|
@ -1,21 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<!-- Copyright 2011 Alexis de Lattre <alexis.delattre@akretion.com>
|
||||
Copyright 2016 Antonio Espinosa <antonio.espinosa@tecnativa.com>
|
||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
|
||||
<odoo>
|
||||
|
||||
|
||||
<record id="view_move_form" model="ir.ui.view">
|
||||
<field name="name">Add to_be_reversed and reversal_id fields</field>
|
||||
<field name="model">account.move</field>
|
||||
<field name="inherit_id" ref="account.view_move_form"/>
|
||||
<field name="inherit_id" ref="account.view_move_form" />
|
||||
<field name="arch" type="xml">
|
||||
<field name="company_id" position="after">
|
||||
<field name="loan_line_id"
|
||||
attrs="{'invisible': [('loan_line_id', '=', False)]}"
|
||||
readonly="True"/>
|
||||
<field
|
||||
name="loan_line_id"
|
||||
attrs="{'invisible': [('loan_line_id', '=', False)]}"
|
||||
readonly="True"
|
||||
/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
@ -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()
|
||||
|
@ -1,37 +1,42 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<!-- Copyright 2011 Alexis de Lattre <alexis.delattre@akretion.com>
|
||||
Copyright 2016 Antonio Espinosa <antonio.espinosa@tecnativa.com>
|
||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
|
||||
<odoo>
|
||||
|
||||
|
||||
<record id="account_loan_generate_wizard_form" model="ir.ui.view">
|
||||
<field name="name">Pay amount</field>
|
||||
<field name="model">account.loan.generate.wizard</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Generate moves">
|
||||
<group>
|
||||
<field name="date"/>
|
||||
<field name="loan_type"/>
|
||||
<field name="date" />
|
||||
<field name="loan_type" />
|
||||
</group>
|
||||
<footer>
|
||||
<button name="run" string="Run"
|
||||
type="object" class="oe_highlight"/>
|
||||
<button
|
||||
name="run"
|
||||
string="Run"
|
||||
type="object"
|
||||
class="oe_highlight"
|
||||
/>
|
||||
or
|
||||
<button string="Cancel" class="oe_link" special="cancel"/>
|
||||
<button string="Cancel" class="oe_link" special="cancel" />
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="account_loan_generate_wizard_action" model="ir.actions.act_window">
|
||||
<field name="name">Generate moves</field>
|
||||
<field name="res_model">account.loan.generate.wizard</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
|
||||
<menuitem name="Generate Loan Entries" action="account_loan_generate_wizard_action"
|
||||
id="account_loan_generate_wizard_menu"
|
||||
parent="account.menu_finance_entries_generate_entries" sequence="111" groups="base.group_no_one"/>
|
||||
<menuitem
|
||||
name="Generate Loan Entries"
|
||||
action="account_loan_generate_wizard_action"
|
||||
id="account_loan_generate_wizard_menu"
|
||||
parent="account.menu_finance_entries_generate_entries"
|
||||
sequence="111"
|
||||
groups="base.group_no_one"
|
||||
/>
|
||||
</odoo>
|
||||
|
@ -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()
|
||||
|
@ -1,35 +1,34 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<!-- Copyright 2011 Alexis de Lattre <alexis.delattre@akretion.com>
|
||||
Copyright 2016 Antonio Espinosa <antonio.espinosa@tecnativa.com>
|
||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
|
||||
<odoo>
|
||||
|
||||
|
||||
<record id="account_loan_pay_amount_form" model="ir.ui.view">
|
||||
<field name="name">Pay amount</field>
|
||||
<field name="model">account.loan.pay.amount</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Pay amount">
|
||||
<group>
|
||||
<field name="loan_id" readonly="True"/>
|
||||
<field name="date"/>
|
||||
<field name="cancel_loan"/>
|
||||
<field name="amount"/>
|
||||
<field name="fees"/>
|
||||
<field name="currency_id"/>
|
||||
<field name="loan_id" readonly="True" />
|
||||
<field name="date" />
|
||||
<field name="cancel_loan" />
|
||||
<field name="amount" />
|
||||
<field name="fees" />
|
||||
<field name="currency_id" />
|
||||
</group>
|
||||
<footer>
|
||||
<button name="run" string="Run"
|
||||
type="object" class="oe_highlight"/>
|
||||
<button
|
||||
name="run"
|
||||
string="Run"
|
||||
type="object"
|
||||
class="oe_highlight"
|
||||
/>
|
||||
or
|
||||
<button string="Cancel" class="oe_link" special="cancel"/>
|
||||
<button string="Cancel" class="oe_link" special="cancel" />
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
|
||||
<record id="account_loan_pay_amount_action" model="ir.actions.act_window">
|
||||
<field name="name">Pay amount</field>
|
||||
<field name="res_model">account.loan.pay.amount</field>
|
||||
@ -37,5 +36,4 @@
|
||||
<field name="target">new</field>
|
||||
<field name="context">{'default_loan_id': active_id}</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
@ -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()
|
||||
|
@ -1,32 +1,31 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<!-- Copyright 2011 Alexis de Lattre <alexis.delattre@akretion.com>
|
||||
Copyright 2016 Antonio Espinosa <antonio.espinosa@tecnativa.com>
|
||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
|
||||
<odoo>
|
||||
|
||||
|
||||
<record id="account_loan_post_form" model="ir.ui.view">
|
||||
<field name="name">Post loan</field>
|
||||
<field name="model">account.loan.post</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Pay amount">
|
||||
<group>
|
||||
<field name="loan_id" readonly="True"/>
|
||||
<field name="account_id"/>
|
||||
<field name="journal_id"/>
|
||||
<field name="loan_id" readonly="True" />
|
||||
<field name="account_id" />
|
||||
<field name="journal_id" />
|
||||
</group>
|
||||
<footer>
|
||||
<button name="run" string="Run"
|
||||
type="object" class="oe_highlight"/>
|
||||
<button
|
||||
name="run"
|
||||
string="Run"
|
||||
type="object"
|
||||
class="oe_highlight"
|
||||
/>
|
||||
or
|
||||
<button string="Cancel" class="oe_link" special="cancel"/>
|
||||
<button string="Cancel" class="oe_link" special="cancel" />
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
|
||||
<record id="account_loan_post_action" model="ir.actions.act_window">
|
||||
<field name="name">Post loan</field>
|
||||
<field name="res_model">account.loan.post</field>
|
||||
@ -34,5 +33,4 @@
|
||||
<field name="target">new</field>
|
||||
<field name="context">{'default_loan_id': active_id}</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
Loading…
x
Reference in New Issue
Block a user