This commit is contained in:
Duy Dat
2025-06-21 09:44:01 +07:00
parent 7819a1d6db
commit 446d1f7d9d
112 changed files with 4951 additions and 8 deletions
+5
View File
@@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
from . import loan_type
from . import borrower
from . import loan_interest_policy
from . import loan_request
+19
View File
@@ -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')
+176
View File
@@ -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.')
]
+39
View File
@@ -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
+15
View File
@@ -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()
+15
View File
@@ -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')