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.

236 lines
8.1 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 logging
  5. from odoo import _, api, fields, models
  6. _logger = logging.getLogger(__name__)
  7. class LoanIssue(models.Model):
  8. _name = "loan.issue"
  9. _description = "Loan Issue"
  10. @api.multi
  11. def _compute_amounts(self):
  12. for issue in self:
  13. subscription_lines = issue.loan_issue_lines.filtered(
  14. lambda line: line.state != "cancelled"
  15. )
  16. issue.subscribed_amount = sum(subscription_lines.mapped("amount"))
  17. paid_lines = issue.loan_issue_lines.filtered(
  18. lambda line: line.state == "paid"
  19. )
  20. issue.paid_amount = sum(paid_lines.mapped("amount"))
  21. name = fields.Char(string="Name", translate=True)
  22. default_issue = fields.Boolean(string="Default issue")
  23. subscription_start_date = fields.Date(
  24. string="Start date subscription period"
  25. )
  26. subscription_end_date = fields.Date(string="End date subscription period")
  27. user_id = fields.Many2one("res.users", string="Responsible")
  28. loan_start_date = fields.Date(string="Loan start date")
  29. term_date = fields.Date(string="Term date")
  30. loan_term = fields.Float(string="Duration of the loan in month")
  31. rate = fields.Float(string="Interest rate")
  32. face_value = fields.Monetary(
  33. string="Facial value",
  34. currency_field="company_currency_id",
  35. required=True,
  36. )
  37. minimum_amount = fields.Monetary(
  38. string="Minimum amount of issue", currency_field="company_currency_id"
  39. )
  40. maximum_amount = fields.Monetary(
  41. string="Maximum amount of issue", currency_field="company_currency_id"
  42. )
  43. min_amount_company = fields.Monetary(
  44. string="Minimum amount for a company",
  45. currency_field="company_currency_id",
  46. )
  47. max_amount_company = fields.Monetary(
  48. string="Maximum amount for a company",
  49. currency_field="company_currency_id",
  50. )
  51. min_amount_person = fields.Monetary(
  52. string="Minimum amount for a person",
  53. currency_field="company_currency_id",
  54. )
  55. max_amount_person = fields.Monetary(
  56. string="Maximum amount for a person",
  57. currency_field="company_currency_id",
  58. )
  59. subscribed_amount = fields.Monetary(
  60. string="Subscribed amount",
  61. compute="_compute_amounts",
  62. currency_field="company_currency_id",
  63. )
  64. paid_amount = fields.Monetary(
  65. string="Paid amount",
  66. compute="_compute_amounts",
  67. currency_field="company_currency_id",
  68. )
  69. interest_payment = fields.Selection(
  70. [("end", "End"), ("yearly", "Yearly")], string="Interest payment"
  71. )
  72. interest_payment_info = fields.Char(string="Yearly payment on")
  73. loan_issue_lines = fields.One2many(
  74. "loan.issue.line", "loan_issue_id", string="Loan issue lines"
  75. )
  76. state = fields.Selection(
  77. [
  78. ("draft", "Draft"),
  79. ("confirmed", "Confirmed"),
  80. ("cancelled", "Cancelled"),
  81. ("ongoing", "Ongoing"),
  82. ("closed", "Closed"),
  83. ],
  84. string="State",
  85. default="draft",
  86. )
  87. company_currency_id = fields.Many2one(
  88. "res.currency",
  89. related="company_id.currency_id",
  90. string="Company Currency",
  91. readonly=True,
  92. )
  93. company_id = fields.Many2one(
  94. "res.company",
  95. string="Company",
  96. required=True,
  97. readonly=True,
  98. default=lambda self: self.env["res.company"]._company_default_get(),
  99. ) # noqa
  100. by_company = fields.Boolean(string="By company")
  101. by_individual = fields.Boolean(string="By individuals")
  102. display_on_website = fields.Boolean(sting="Display on website")
  103. taxes_rate = fields.Float(string="Taxes on interest", required=True)
  104. @api.multi
  105. def get_max_amount(self, partner):
  106. """
  107. Return the maximum amount that partner can buy.
  108. A negative value means that there is no maximum.
  109. """
  110. self.ensure_one()
  111. lines = self.loan_issue_lines.filtered(
  112. lambda r: r.partner_id == partner and r.state != "cancelled"
  113. )
  114. already_subscribed = sum(line.amount for line in lines)
  115. max_amount = -1 # No max amount
  116. if partner.is_company and self.max_amount_company > 0:
  117. max_amount = max(0, self.max_amount_company - already_subscribed)
  118. if not partner.is_company and self.max_amount_person > 0:
  119. max_amount = max(0, self.max_amount_person - already_subscribed)
  120. return max_amount
  121. @api.multi
  122. def get_min_amount(self, partner):
  123. """
  124. Return the minimum amount that a partner must buy.
  125. A zero value means that there is no minimum amount.
  126. """
  127. self.ensure_one()
  128. lines = self.loan_issue_lines.filtered(
  129. lambda r: r.partner_id == partner and r.state != "cancelled"
  130. )
  131. amount_subscribed = sum(line.amount for line in lines)
  132. if partner.is_company:
  133. min_amount = self.min_amount_company - amount_subscribed
  134. else:
  135. min_amount = self.min_amount_person - amount_subscribed
  136. return max(0, min_amount)
  137. @api.multi
  138. def get_web_issues(self, is_company):
  139. bond_issues = self.search(
  140. [("display_on_website", "=", True), ("state", "=", "ongoing")]
  141. )
  142. if is_company is True:
  143. return bond_issues.filtered("by_company")
  144. else:
  145. return bond_issues.filtered("by_company")
  146. @api.multi
  147. def action_confirm(self):
  148. self.ensure_one()
  149. self.write({"state": "confirmed"})
  150. @api.multi
  151. def action_open(self):
  152. self.ensure_one()
  153. self.write({"state": "ongoing"})
  154. @api.multi
  155. def action_draft(self):
  156. self.ensure_one()
  157. self.write({"state": "draft"})
  158. @api.multi
  159. def action_cancel(self):
  160. self.ensure_one()
  161. self.write({"state": "cancelled"})
  162. @api.multi
  163. def action_close(self):
  164. self.ensure_one()
  165. self.write({"state": "closed"})
  166. def get_interest_vals(self, line, vals):
  167. interest_obj = self.env["loan.interest.line"]
  168. accrued_amount = line.amount
  169. accrued_interest = 0
  170. accrued_net_interest = 0
  171. accrued_taxes = 0
  172. for year in range(1, int(self.loan_term) + 1):
  173. interest = accrued_amount * (line.loan_issue_id.rate / 100)
  174. accrued_amount += interest
  175. taxes_amount = interest * (self.taxes_rate / 100)
  176. net_interest = interest - taxes_amount
  177. accrued_interest += interest
  178. accrued_net_interest += net_interest
  179. accrued_taxes += taxes_amount
  180. vals["interest"] = interest
  181. vals["net_interest"] = net_interest
  182. vals["taxes_amount"] = taxes_amount
  183. vals["accrued_amount"] = accrued_amount
  184. vals["accrued_interest"] = accrued_interest
  185. vals["accrued_net_interest"] = accrued_net_interest
  186. vals["accrued_taxes"] = accrued_taxes
  187. vals["name"] = year
  188. interest_obj.create(vals)
  189. @api.multi
  190. def compute_loan_interest(self):
  191. self.ensure_one()
  192. if self.interest_payment == "end":
  193. due_date = self.term_date
  194. else:
  195. raise NotImplementedError(
  196. _("Interest payment by year hasn't been " "implemented yet")
  197. )
  198. for line in self.loan_issue_lines:
  199. # TODO remove this line
  200. line.interest_lines.unlink()
  201. # Please Do not Forget
  202. vals = {
  203. "issue_line": line.id,
  204. "due_date": due_date,
  205. "taxes_rate": self.taxes_rate,
  206. }
  207. self.get_interest_vals(line, vals)
  208. rounded_term = int(self.loan_term)
  209. if self.loan_term - rounded_term > 0:
  210. # TODO Handle this case
  211. raise NotImplementedError(
  212. _(
  213. "Calculation on non entire year "
  214. "hasn't been implemented yet"
  215. )
  216. )