# Copyright 2019 Coop IT Easy SCRL fs # Houssine BAKKALI # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). import calendar from datetime import date from dateutil.relativedelta import relativedelta from odoo import api, fields, models class LoanIssueLine(models.Model): _name = "loan.issue.line" _description = "Loan Issue Line" _order = "date desc, id" @api.multi @api.depends("quantity", "face_value") def _compute_amount(self): for line in self: line.amount = line.face_value * line.quantity name = fields.Char( String="Name", readonly=True, ) reference = fields.Char( string="Reference", copy=False, readonly=True, states={'draft': [('readonly', False)]} ) loan_issue_id = fields.Many2one( "loan.issue", string="Loan issue", required=True ) interest_lines = fields.One2many( "loan.interest.line", "issue_line", string="Interest lines" ) quantity = fields.Integer(string="Quantity", required=True) face_value = fields.Monetary( related="loan_issue_id.face_value", currency_field="company_currency_id", store=True, readonly=True, ) partner_id = fields.Many2one( "res.partner", string="Subscriber", required=True ) date = fields.Date( string="Subscription date", default=lambda self: date.strftime(date.today(), "%Y-%m-%d"), required=True, ) payment_date = fields.Date( string="Payment date") amount = fields.Monetary( string="Subscribed amount", currency_field="company_currency_id", compute="_compute_amount", store=True, ) state = fields.Selection( [ ("draft", "Draft"), ("subscribed", "Subscribed"), ("waiting", "Waiting payment"), ("paid", "paid"), ("cancelled", "Cancelled"), ("ended", "Ended"), ], string="State", required=True, default="draft", ) company_currency_id = fields.Many2one( "res.currency", related="company_id.currency_id", string="Company Currency", readonly=True, ) company_id = fields.Many2one( "res.company", related="loan_issue_id.company_id", string="Company", readonly=True, ) def get_loan_sub_mail_template(self): return self.env.ref( "easy_my_coop_loan.loan_subscription_confirmation", False ) def get_loan_pay_req_mail_template(self): return self.env.ref( "easy_my_coop_loan.loan_issue_payment_request", False ) @api.model def create(self, vals): line = super(LoanIssueLine, self).create(vals) confirmation_mail_template = line.get_loan_sub_mail_template() confirmation_mail_template.send_mail(line.id) return line @api.multi def action_draft(self): for line in self: line.write({"state": "draft"}) @api.multi def action_validate(self): sequence_id = self.env.ref( "easy_my_coop_loan.sequence_loan_issue_line", False ) for line in self: loan_line_num = sequence_id.next_by_id() line.write({"name": loan_line_num, "state": "subscribed"}) @api.multi def action_request_payment(self): pay_req_mail_template = self.get_loan_pay_req_mail_template() for line in self: pay_req_mail_template.send_mail(line.id) line.write({"state": "waiting"}) @api.multi def action_cancel(self): for line in self: line.write({"state": "cancelled"}) @api.multi def get_confirm_paid_email_template(self): self.ensure_one() return self.env.ref( "easy_my_coop_loan.email_template_loan_confirm_paid" ) @api.multi def action_paid(self): loan_email_template = self.get_confirm_paid_email_template() for line in self: vals = {"state": "paid"} if not line.payment_date: vals["payment_date"] = fields.Date.today() line.write(vals) line.action_compute_interest() loan_email_template.sudo().send_mail(line.id, force_send=False) def get_number_of_days(self, year): if calendar.isleap(year): return 366 else: return 365 @api.multi def action_compute_interest(self): for line in self: loan_issue = line.loan_issue_id vals = { "issue_line": line.id, "taxes_rate": loan_issue.taxes_rate, } list_vals = [] accrued_amount = self.amount accrued_interest = 0 accrued_net_interest = 0 accrued_taxes = 0 taxes_amount = 0 diff_days = 0 # In case of a recompute is done. Only the interest lines # in the future will be deleted. We also needed to determine # from which year we'll have to regenerate the lines. # Through the beautiful mind of Houssine, we implemented # a small piece code to allows it. today = fields.Date.today() posted_lines = line.interest_lines.filtered( lambda r: r.state == "paid" or r.due_date < today) futur_lines = line.interest_lines - posted_lines start_to_line = len(posted_lines) + 1 futur_lines.unlink() loan_term = int(loan_issue.loan_term / 12) # if payment_date is first day of the month # we take the current month if line.payment_date.day == 1: start_date = line.payment_date else: start_date = line.payment_date + relativedelta(months=+1, day=1) # we calculate the number of day between payment date # and the end of the month of the payment diff_days = ((start_date - relativedelta(days=1)) - line.payment_date).days rate = loan_issue.rate / 100 # take leap year into account days = self.get_number_of_days(line.payment_date.year) interim_amount = line.amount * rate * (diff_days / days) due_date = start_date + relativedelta(years=loan_term) for year in range(1, loan_term + 1): interest = accrued_amount * rate due_amount = 0 due_loan_amount = 0 if loan_issue.capital_payment == "end": if year == loan_term: due_amount = line.amount else: due_amount = line.amount * (loan_term / 100) accrued_amount -= due_amount due_loan_amount = due_amount if year == 1: interest += interim_amount if loan_issue.interest_payment == "end": accrued_interest += interest accrued_amount += interest net_interest = 0 if year == loan_term: taxes_amount = (accrued_interest * (loan_issue.taxes_rate / 100) ) net_interest = accrued_interest - taxes_amount due_amount += net_interest else: due_date = start_date + relativedelta(years=+year) taxes_amount = interest * (loan_issue.taxes_rate / 100) net_interest = interest - taxes_amount due_amount += net_interest accrued_interest = interest accrued_net_interest += net_interest accrued_taxes += taxes_amount vals["due_date"] = due_date vals["due_loan_amount"] = due_loan_amount vals["due_amount"] = due_amount vals["interest"] = interest vals["net_interest"] = net_interest vals["taxes_amount"] = taxes_amount vals["accrued_amount"] = accrued_amount vals["accrued_interest"] = accrued_interest vals["accrued_net_interest"] = accrued_net_interest vals["accrued_taxes"] = accrued_taxes vals["name"] = year if year >= start_to_line: list_vals.append(vals.copy()) self.env["loan.interest.line"].create(list_vals)