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.

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