init
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from . import loan_type
|
||||
from . import borrower
|
||||
from . import loan_interest_policy
|
||||
from . import loan_request
|
||||
@@ -0,0 +1,19 @@
|
||||
from odoo import models, fields
|
||||
|
||||
class ResPartner(models.Model):
|
||||
_inherit = 'res.partner'
|
||||
is_borrower = fields.Boolean(string='Is Borrower',default=True)
|
||||
gender = fields.Selection([
|
||||
('male', 'Male'),
|
||||
('female', 'Female'),
|
||||
('other', 'Other')
|
||||
], string='Gender')
|
||||
date_of_birth = fields.Date(string='Date of Birth')
|
||||
job = fields.Char(string='Occupation')
|
||||
monthly_income = fields.Float(string='Monthly Income')
|
||||
marital_status = fields.Selection([
|
||||
('single', 'Single'),
|
||||
('married', 'Married'),
|
||||
('divorced', 'Divorced'),
|
||||
('widowed', 'Widowed'),
|
||||
], string='Marital Status')
|
||||
@@ -0,0 +1,176 @@
|
||||
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,
|
||||
})
|
||||
@@ -0,0 +1,18 @@
|
||||
from odoo import models, fields
|
||||
class LoanInterestPolicy(models.Model):
|
||||
_name = 'loan.interest.policy'
|
||||
_description = 'Loan Interest Policy'
|
||||
|
||||
loan_type_id = fields.Many2one('loan.type1', string='Loại khoản vay', required=True)
|
||||
repayment_type = fields.Selection([
|
||||
('fixed_principal', 'Gốc đều, lãi giảm dần'),
|
||||
('annuity', 'Trả góp cố định'),
|
||||
('interest_only', 'Chỉ trả lãi'),
|
||||
('lump_sum', 'Trả 1 lần cuối kỳ'),
|
||||
], string='Kiểu trả nợ', required=True)
|
||||
|
||||
interest_rate = fields.Float(string='Lãi suất (%)', required=True)
|
||||
|
||||
_sql_constraints = [
|
||||
('unique_policy', 'unique(loan_type_id, repayment_type)', 'Chính sách lãi suất đã tồn tại cho loại vay và kiểu trả nợ này.')
|
||||
]
|
||||
@@ -0,0 +1,39 @@
|
||||
from odoo import models, fields, api
|
||||
class LoanRequest(models.Model):
|
||||
_name = 'loan.request'
|
||||
_description = 'Loan Request'
|
||||
borrower_id = fields.Many2one('loan.borrower', string='Borrower', required=True, ondelete='restrict')
|
||||
loan_type_id = fields.Many2one('loan.type1', string='Loan Type', required=True)
|
||||
amount = fields.Float(string='Loan Amount', required=True)
|
||||
term = fields.Integer(string='Term (months)', required=True,default=1)
|
||||
repayment_type = fields.Selection([
|
||||
('fixed_principal', 'Gốc đều, lãi giảm dần'),
|
||||
('annuity', 'Trả góp cố định'),
|
||||
('interest_only', 'Chỉ trả lãi'),
|
||||
('lump_sum', 'Trả 1 lần cuối kỳ'),
|
||||
], string='Kiểu trả nợ', required=True)
|
||||
interest_rate = fields.Float(string='Interest Rate (%)', required=True)
|
||||
expected_interest_rate = fields.Float(string='Lãi suất mong muốn', help="Khách hàng đề xuất một lãi suất thấp hơn nếu có")
|
||||
payment_method = fields.Selection([
|
||||
('cash', 'Cash'),
|
||||
('bank_transfer', 'Bank Transfer'),
|
||||
('check', 'Check')
|
||||
], string='Payment Method', required=True, default='cash')
|
||||
request_datetime = fields.Datetime(
|
||||
string='Ngày giờ yêu cầu',
|
||||
default=fields.Datetime.now)
|
||||
state = fields.Selection([
|
||||
('draft', 'Nháp'),
|
||||
('processing', 'Đang xử lý'),
|
||||
('approved', 'Đã duyệt'),
|
||||
('rejected', 'Từ chối')
|
||||
], string='Trạng thái', default='processing', required=True)
|
||||
|
||||
# def _onchange_interest_rate(self):
|
||||
# if self.loan_type_id and self.repayment_type:
|
||||
# policy = self.env['loan.interest.policy'].search([
|
||||
# ('loan_type_id', '=', self.loan_type_id.id),
|
||||
# ('repayment_type', '=', self.repayment_type)
|
||||
# ], limit=1)
|
||||
# self.interest_rate = policy.interest_rate if policy else 0.0
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
from odoo import api, models, fields
|
||||
class LoanType(models.Model):
|
||||
_name = 'loan.type1'
|
||||
_description = 'Loan Type'
|
||||
name = fields.Char(string='Loan Type', required=True,unique=True)
|
||||
active = fields.Boolean(default=True)
|
||||
interest_policy_ids = fields.One2many(
|
||||
'loan.interest.policy',
|
||||
'loan_type_id',
|
||||
string='Chính sách lãi suất'
|
||||
)
|
||||
@api.onchange('name')
|
||||
def _onchange_name_uppercase(self):
|
||||
if self.name:
|
||||
self.name = self.name.upper()
|
||||
@@ -0,0 +1,15 @@
|
||||
from odoo import models, fields
|
||||
class RepaymentLine(models.Model):
|
||||
_name = 'repayment.line'
|
||||
_description = 'Repayment Schedule Line'
|
||||
|
||||
contract_id = fields.Many2one('loan.contract', string='Contract', ondelete='cascade', required=True)
|
||||
installment = fields.Integer(string='Kỳ thứ')
|
||||
due_date = fields.Date(string='Ngày đến hạn')
|
||||
principal = fields.Float(string='Tiền gốc')
|
||||
interest = fields.Float(string='Tiền lãi')
|
||||
total = fields.Float(string='Tổng phải trả')
|
||||
state = fields.Selection([
|
||||
('draft', 'Chưa trả'),
|
||||
('paid', 'Đã trả'),
|
||||
], default='draft')
|
||||
Reference in New Issue
Block a user