You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

250 lines
8.3 KiB

  1. # Copyright 2019 Coop IT Easy SCRL fs
  2. # Houssine BAKKALI <houssine@coopiteasy.be>
  3. # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
  4. import calendar
  5. from datetime import date
  6. from dateutil.relativedelta import relativedelta
  7. from odoo import api, fields, models
  8. class LoanIssueLine(models.Model):
  9. _name = "loan.issue.line"
  10. _description = "Loan Issue Line"
  11. _order = "date desc, id"
  12. @api.multi
  13. @api.depends("quantity", "face_value")
  14. def _compute_amount(self):
  15. for line in self:
  16. line.amount = line.face_value * line.quantity
  17. reference = fields.Char(
  18. string="Reference",
  19. copy=False,
  20. readonly=True,
  21. states={'draft': [('readonly', False)]}
  22. )
  23. loan_issue_id = fields.Many2one(
  24. "loan.issue", string="Loan issue", required=True
  25. )
  26. interest_lines = fields.One2many(
  27. "loan.interest.line", "issue_line", string="Interest lines"
  28. )
  29. quantity = fields.Integer(string="quantity", required=True)
  30. face_value = fields.Monetary(
  31. related="loan_issue_id.face_value",
  32. currency_field="company_currency_id",
  33. store=True,
  34. readonly=True,
  35. )
  36. partner_id = fields.Many2one(
  37. "res.partner",
  38. string="Subscriber",
  39. required=True
  40. )
  41. date = fields.Date(
  42. string="Subscription date",
  43. default=lambda self: date.strftime(date.today(), "%Y-%m-%d"),
  44. required=True,
  45. )
  46. payment_date = fields.Date(
  47. string="Payment date")
  48. amount = fields.Monetary(
  49. string="Subscribed amount",
  50. currency_field="company_currency_id",
  51. compute="_compute_amount",
  52. store=True,
  53. )
  54. state = fields.Selection(
  55. [
  56. ("draft", "Draft"),
  57. ("subscribed", "Subscribed"),
  58. ("waiting", "Waiting payment"),
  59. ("paid", "paid"),
  60. ("cancelled", "Cancelled"),
  61. ("ended", "Ended"),
  62. ],
  63. string="State",
  64. required=True,
  65. default="draft",
  66. )
  67. company_currency_id = fields.Many2one(
  68. "res.currency",
  69. related="company_id.currency_id",
  70. string="Company Currency",
  71. readonly=True,
  72. )
  73. company_id = fields.Many2one(
  74. "res.company",
  75. related="loan_issue_id.company_id",
  76. string="Company",
  77. readonly=True,
  78. )
  79. def get_loan_sub_mail_template(self):
  80. return self.env.ref(
  81. "easy_my_coop_loan.loan_subscription_confirmation", False
  82. )
  83. def get_loan_pay_req_mail_template(self):
  84. return self.env.ref(
  85. "easy_my_coop_loan.loan_issue_payment_request", False
  86. )
  87. @api.model
  88. def create(self, vals):
  89. line = super(LoanIssueLine, self).create(vals)
  90. confirmation_mail_template = line.get_loan_sub_mail_template()
  91. confirmation_mail_template.send_mail(line.id)
  92. return line
  93. @api.multi
  94. def action_draft(self):
  95. for line in self:
  96. line.write({"state": "draft"})
  97. @api.multi
  98. def action_validate(self):
  99. for line in self:
  100. line.write({"state": "subscribed"})
  101. @api.multi
  102. def action_request_payment(self):
  103. pay_req_mail_template = self.get_loan_pay_req_mail_template()
  104. for line in self:
  105. pay_req_mail_template.send_mail(line.id)
  106. line.write({"state": "waiting"})
  107. @api.multi
  108. def action_cancel(self):
  109. for line in self:
  110. line.write({"state": "cancelled"})
  111. @api.multi
  112. def get_confirm_paid_email_template(self):
  113. self.ensure_one()
  114. return self.env.ref(
  115. "easy_my_coop_loan.email_template_loan_confirm_paid"
  116. )
  117. @api.multi
  118. def action_paid(self):
  119. loan_email_template = self.get_confirm_paid_email_template()
  120. for line in self:
  121. vals = {"state": "paid"}
  122. if not line.payment_date:
  123. vals["payment_date"] = fields.Date.today()
  124. line.write(vals)
  125. line.action_compute_interest()
  126. loan_email_template.sudo().send_mail(line.id, force_send=False)
  127. def get_number_of_days(self, year):
  128. if calendar.isleap(year):
  129. return 366
  130. else:
  131. return 365
  132. @api.multi
  133. def action_compute_interest(self):
  134. for line in self:
  135. loan_issue = line.loan_issue_id
  136. vals = {
  137. "issue_line": line.id,
  138. "taxes_rate": loan_issue.taxes_rate,
  139. }
  140. list_vals = []
  141. accrued_amount = self.amount
  142. accrued_interest = 0
  143. accrued_net_interest = 0
  144. accrued_taxes = 0
  145. taxes_amount = 0
  146. diff_days = 0
  147. # In case of a recompute is done. Only the interest lines
  148. # in the future will be deleted. We also needed to determine
  149. # from which year we'll have to regenerate the lines.
  150. # Through the beautiful mind of Houssine, we implemented
  151. # a small piece code to allows it.
  152. today = fields.Date.today()
  153. posted_lines = line.interest_lines.filtered(
  154. lambda r: r.state == "paid" or
  155. r.due_date < today)
  156. futur_lines = line.interest_lines - posted_lines
  157. start_to_line = len(posted_lines) + 1
  158. futur_lines.unlink()
  159. loan_term = int(loan_issue.loan_term / 12)
  160. # if payment_date is first day of the month
  161. # we take the current month
  162. if line.payment_date.day == 1:
  163. start_date = line.payment_date
  164. else:
  165. start_date = line.payment_date + relativedelta(months=+1,
  166. day=1)
  167. # we calculate the number of day between payment date
  168. # and the end of the month of the payment
  169. diff_days = ((start_date - relativedelta(days=1)) -
  170. line.payment_date).days
  171. rate = loan_issue.rate / 100
  172. # take leap year into account
  173. days = self.get_number_of_days(line.payment_date.year)
  174. interim_amount = line.amount * rate * (diff_days / days)
  175. due_date = start_date + relativedelta(years=+loan_term)
  176. for year in range(1, loan_term + 1):
  177. interest = accrued_amount * rate
  178. due_amount = 0
  179. if loan_issue.capital_payment == "end":
  180. if year == loan_term:
  181. due_amount = line.amount
  182. else:
  183. due_amount = line.amount * (loan_term / 100)
  184. accrued_amount -= due_amount
  185. if loan_issue.interest_payment == "end":
  186. accrued_interest += interest
  187. accrued_amount += interest
  188. net_interest = 0
  189. if year == loan_term:
  190. taxes_amount = (accrued_interest *
  191. (loan_issue.taxes_rate / 100)
  192. )
  193. net_interest = accrued_interest - taxes_amount
  194. due_amount += net_interest
  195. else:
  196. due_date = start_date + relativedelta(years=+year)
  197. taxes_amount = interest * (loan_issue.taxes_rate / 100)
  198. net_interest = interest - taxes_amount
  199. due_amount += net_interest
  200. accrued_interest = interest
  201. if year == 1:
  202. interest += interim_amount
  203. accrued_interest = interest
  204. accrued_net_interest += net_interest
  205. accrued_taxes += taxes_amount
  206. vals["due_date"] = due_date
  207. vals["due_amount"] = due_amount
  208. vals["interest"] = interest
  209. vals["net_interest"] = net_interest
  210. vals["taxes_amount"] = taxes_amount
  211. vals["accrued_amount"] = accrued_amount
  212. vals["accrued_interest"] = accrued_interest
  213. vals["accrued_net_interest"] = accrued_net_interest
  214. vals["accrued_taxes"] = accrued_taxes
  215. vals["name"] = year
  216. if year >= start_to_line:
  217. list_vals.append(vals.copy())
  218. self.env["loan.interest.line"].create(list_vals)