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.

262 lines
8.7 KiB

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