177 lines
6.0 KiB
Python
177 lines
6.0 KiB
Python
from odoo import models, fields, api
|
|
from dateutil.relativedelta import relativedelta
|
|
class LoanContract(models.Model):
|
|
_name = 'loan.contract'
|
|
_description = 'Loan Contract'
|
|
|
|
code_contract = fields.Char(
|
|
string='Contract Code',
|
|
required=True,
|
|
copy=False,
|
|
readonly=True,
|
|
default=lambda self: self.env['ir.sequence'].next_by_code('loan.contract')
|
|
)
|
|
repayment_line_ids = fields.One2many('repayment.line', 'contract_id', string='Lịch trả nợ')
|
|
loan_request_id = fields.Many2one(
|
|
'loan.request',
|
|
string='Loan Request',
|
|
required=True,
|
|
ondelete='restrict'
|
|
)
|
|
|
|
borrower_id = fields.Many2one(
|
|
'loan.borrower',
|
|
string='Borrower',
|
|
required=True,
|
|
ondelete='restrict'
|
|
)
|
|
|
|
loan_type_id = fields.Many2one(
|
|
'loan.type1',
|
|
string='Loan Type',
|
|
readonly=True
|
|
)
|
|
|
|
amount = fields.Float(
|
|
string='Loan Amount',
|
|
required=True
|
|
)
|
|
|
|
term = fields.Integer(
|
|
string='Term (Months)',
|
|
required=True
|
|
)
|
|
payment_method = fields.Selection([
|
|
('cash', 'Cash'),
|
|
('bank_transfer', 'Bank Transfer'),
|
|
('check', 'Check')
|
|
], string='Payment Method', required=True, default='cash',readonly=True)
|
|
|
|
repayment_type = fields.Selection([
|
|
('fixed_principal', 'Trả gốc đều, lãi giảm dần'),
|
|
('annuity', 'Trả góp đều (gốc + lãi)'),
|
|
('interest_only', 'Chỉ trả lãi hàng kỳ, gốc trả cuối'),
|
|
('lump_sum', 'Trả 1 lần cuối kỳ'),
|
|
], string='Kiểu trả nợ', required=True, readonly=True)
|
|
|
|
interest_rate = fields.Float(
|
|
string='Interest Rate (%)',
|
|
required=True
|
|
)
|
|
|
|
processing_fee = fields.Float(
|
|
string='Processing Fee',
|
|
compute='_compute_processing_fee_amount_count',
|
|
store=True,
|
|
readonly=True
|
|
)
|
|
|
|
amount_count = fields.Float(
|
|
string='Amount Count',
|
|
compute='_compute_processing_fee_amount_count',
|
|
store=True,
|
|
readonly=True
|
|
)
|
|
|
|
start_date = fields.Date(
|
|
string='Start Date',
|
|
required=True,
|
|
default=fields.Date.context_today
|
|
)
|
|
|
|
end_date = fields.Date(
|
|
string='End Date',
|
|
compute='_compute_end_date',
|
|
store=True,
|
|
readonly=True,
|
|
)
|
|
|
|
state = fields.Selection([
|
|
('draft', 'Draft'),
|
|
('active', 'Active'),
|
|
('done', 'Completed'),
|
|
('cancelled', 'Cancelled')
|
|
], string='Status', default='draft')
|
|
|
|
note = fields.Text(string='Notes')
|
|
|
|
@api.depends('start_date', 'term')
|
|
def _compute_end_date(self):
|
|
for rec in self:
|
|
if rec.start_date and rec.term:
|
|
rec.end_date = rec.start_date + relativedelta(months=rec.term)
|
|
else:
|
|
rec.end_date = False
|
|
|
|
@api.onchange('loan_request_id')
|
|
def _onchange_loan_request_id(self):
|
|
for rec in self:
|
|
if rec.loan_request_id:
|
|
rec.borrower_id = rec.loan_request_id.borrower_id
|
|
rec.loan_type_id = rec.loan_request_id.loan_type_id
|
|
rec.amount = rec.loan_request_id.amount
|
|
rec.interest_rate = rec.loan_request_id.interest_rate
|
|
rec.term = rec.loan_request_id.term
|
|
rec.repayment_type = rec.loan_request_id.repayment_type
|
|
|
|
@api.depends('loan_request_id')
|
|
def _compute_processing_fee_amount_count(self):
|
|
for rec in self:
|
|
if rec.loan_request_id:
|
|
rec.tinterest_rate = rec.loan_request_id.interest_rate or 0.0
|
|
rec.amount = rec.loan_request_id.amount or 0.0
|
|
rec.processing_fee = rec.amount - (rec.amount * rec.rate / 100)
|
|
rec.amount_count = rec.amount + rec.processing_fee
|
|
else:
|
|
rec.processing_fee = 0.0
|
|
rec.amount_count = 0.0
|
|
@api.model
|
|
def create(self, vals):
|
|
if vals.get('loan_request_id'):
|
|
request = self.env['loan.request'].browse(vals['loan_request_id'])
|
|
vals['repayment_type'] = request.repayment_type
|
|
vals['payment_method'] = request.payment_method
|
|
|
|
return super().create(vals)
|
|
@api.model
|
|
def write(self, vals):
|
|
res = super().write(vals)
|
|
for contract in self:
|
|
if vals.get('state') == 'active':
|
|
contract._generate_repayment_schedule()
|
|
return res
|
|
def _generate_repayment_schedule(self):
|
|
self.repayment_line_ids.unlink() # Xóa cũ nếu có
|
|
start_date = self.start_date
|
|
monthly_rate = self.interest_rate / 100 / 12
|
|
principal = self.amount
|
|
term = self.term
|
|
|
|
for i in range(1, term + 1):
|
|
due_date = start_date + relativedelta(months=i)
|
|
if self.repayment_type == 'fixed_principal':
|
|
principal_amount = principal / term
|
|
remaining = principal - principal_amount * (i - 1)
|
|
interest = remaining * monthly_rate
|
|
elif self.repayment_type == 'annuity':
|
|
annuity = principal * monthly_rate / (1 - (1 + monthly_rate) ** (-term))
|
|
interest = (principal - (i - 1) * (annuity - principal * monthly_rate)) * monthly_rate
|
|
principal_amount = annuity - interest
|
|
elif self.repayment_type == 'interest_only':
|
|
principal_amount = 0.0 if i < term else principal
|
|
interest = principal * monthly_rate
|
|
elif self.repayment_type == 'lump_sum':
|
|
principal_amount = principal if i == term else 0.0
|
|
interest = principal * monthly_rate if i == term else 0.0
|
|
else:
|
|
principal_amount = interest = 0.0
|
|
|
|
self.env['repayment.line'].create({
|
|
'contract_id': self.id,
|
|
'installment': i,
|
|
'due_date': due_date,
|
|
'principal': principal_amount,
|
|
'interest': interest,
|
|
'total': principal_amount + interest,
|
|
})
|