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.

450 lines
16 KiB

4 years ago
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. from datetime import datetime
  5. from odoo import _, api, fields, models
  6. from odoo.exceptions import ValidationError
  7. class OperationRequest(models.Model):
  8. _name = "operation.request"
  9. _description = "Operation request"
  10. def get_date_now(self):
  11. # fixme odoo 12 uses date types
  12. return datetime.strftime(datetime.now(), "%Y-%m-%d")
  13. @api.multi
  14. @api.depends("share_product_id", "share_product_id.list_price", "quantity")
  15. def _compute_subscription_amount(self):
  16. for operation_request in self:
  17. operation_request.subscription_amount = (
  18. operation_request.share_product_id.list_price
  19. * operation_request.quantity
  20. )
  21. request_date = fields.Date(
  22. string="Request date", default=lambda self: self.get_date_now()
  23. )
  24. effective_date = fields.Date(string="Effective date")
  25. partner_id = fields.Many2one(
  26. "res.partner",
  27. string="Cooperator",
  28. domain=[("member", "=", True)],
  29. required=True,
  30. )
  31. partner_id_to = fields.Many2one(
  32. "res.partner",
  33. string="Transfered to",
  34. domain=[("cooperator", "=", True)],
  35. )
  36. operation_type = fields.Selection(
  37. [
  38. ("subscription", "Subscription"),
  39. ("transfer", "Transfer"),
  40. ("sell_back", "Sell Back"),
  41. ("convert", "Conversion"),
  42. ],
  43. string="Operation Type",
  44. required=True,
  45. )
  46. share_product_id = fields.Many2one(
  47. "product.product",
  48. string="Share type",
  49. domain=[("is_share", "=", True)],
  50. required=True,
  51. )
  52. share_to_product_id = fields.Many2one(
  53. "product.product",
  54. string="Convert to this share type",
  55. domain=[("is_share", "=", True)],
  56. )
  57. share_short_name = fields.Char(
  58. related="share_product_id.short_name", string="Share type name"
  59. )
  60. share_to_short_name = fields.Char(
  61. related="share_to_product_id.short_name", string="Share to type name"
  62. )
  63. share_unit_price = fields.Float(
  64. related="share_product_id.list_price", string="Share price"
  65. )
  66. share_to_unit_price = fields.Float(
  67. related="share_to_product_id.list_price", string="Share to price"
  68. )
  69. subscription_amount = fields.Float(
  70. compute="_compute_subscription_amount", string="Operation amount"
  71. )
  72. quantity = fields.Integer(string="Number of share", required=True)
  73. state = fields.Selection(
  74. [
  75. ("draft", "Draft"),
  76. ("waiting", "Waiting"),
  77. ("approved", "Approved"),
  78. ("done", "Done"),
  79. ("cancelled", "Cancelled"),
  80. ("refused", "Refused"),
  81. ],
  82. string="State",
  83. required=True,
  84. default="draft",
  85. )
  86. user_id = fields.Many2one(
  87. "res.users",
  88. string="Responsible",
  89. readonly=True,
  90. default=lambda self: self.env.user,
  91. )
  92. subscription_request = fields.One2many(
  93. "subscription.request",
  94. "operation_request_id",
  95. string="Share Receiver Info",
  96. help="In case on a transfer of"
  97. " share. If the share receiver"
  98. " isn't a effective member then a"
  99. " subscription form should"
  100. " be filled.",
  101. )
  102. receiver_not_member = fields.Boolean(string="Receiver is not a member")
  103. company_id = fields.Many2one(
  104. "res.company",
  105. string="Company",
  106. required=True,
  107. change_default=True,
  108. readonly=True,
  109. default=lambda self: self.env["res.company"]._company_default_get(),
  110. )
  111. invoice = fields.Many2one("account.invoice", string="Invoice")
  112. @api.multi
  113. @api.constrains("effective_date")
  114. def _constrain_effective_date(self):
  115. for obj in self:
  116. if obj.effective_date and obj.effective_date > fields.Date.today():
  117. raise ValidationError(
  118. _("The effective date can not be in the future.")
  119. )
  120. @api.multi
  121. def approve_operation(self):
  122. for rec in self:
  123. rec.write({"state": "approved"})
  124. @api.multi
  125. def refuse_operation(self):
  126. for rec in self:
  127. rec.write({"state": "refused"})
  128. @api.multi
  129. def submit_operation(self):
  130. for rec in self:
  131. rec.validate()
  132. rec.write({"state": "waiting"})
  133. @api.multi
  134. def cancel_operation(self):
  135. for rec in self:
  136. rec.write({"state": "cancelled"})
  137. @api.multi
  138. def reset_to_draft(self):
  139. for rec in self:
  140. rec.write({"state": "draft"})
  141. def get_total_share_dic(self, partner):
  142. total_share_dic = {}
  143. share_products = self.env["product.product"].search(
  144. [("is_share", "=", True)]
  145. )
  146. for share_product in share_products:
  147. total_share_dic[share_product.id] = 0
  148. for line in partner.share_ids:
  149. total_share_dic[line.share_product_id.id] += line.share_number
  150. return total_share_dic
  151. # This function doesn't handle the case of a cooperator can own
  152. # different kinds of share type
  153. def hand_share_over(self, partner, share_product_id, quantity):
  154. if not partner.member:
  155. raise ValidationError(
  156. _(
  157. "This operation can't be executed if the"
  158. " cooperator is not an effective member"
  159. )
  160. )
  161. share_ind = len(partner.share_ids)
  162. i = 1
  163. while quantity > 0:
  164. line = self.partner_id.share_ids[share_ind - i]
  165. if line.share_product_id.id == share_product_id.id:
  166. if quantity > line.share_number:
  167. quantity -= line.share_number
  168. line.unlink()
  169. else:
  170. share_left = line.share_number - quantity
  171. quantity = 0
  172. line.write({"share_number": share_left})
  173. i += 1
  174. # if the cooperator sold all his shares he's no more
  175. # an effective member
  176. remaning_share_dict = 0
  177. for share_quant in self.get_total_share_dic(partner).values():
  178. remaning_share_dict += share_quant
  179. if remaning_share_dict == 0:
  180. self.partner_id.write({"member": False, "old_member": True})
  181. def has_share_type(self):
  182. for line in self.partner_id.share_ids:
  183. if line.share_product_id.id == self.share_product_id.id:
  184. return True
  185. return False
  186. def validate(self):
  187. if not self.has_share_type() and self.operation_type in [
  188. "sell_back",
  189. "transfer",
  190. ]:
  191. raise ValidationError(
  192. _(
  193. "The cooperator doesn't own this share"
  194. " type. Please choose the appropriate"
  195. " share type."
  196. )
  197. )
  198. if self.operation_type in ["sell_back", "convert", "transfer"]:
  199. total_share_dic = self.get_total_share_dic(self.partner_id)
  200. if self.quantity > total_share_dic[self.share_product_id.id]:
  201. raise ValidationError(
  202. _(
  203. "The cooperator can't hand over more"
  204. " shares that he/she owns."
  205. )
  206. )
  207. if self.operation_type == "convert":
  208. if self.company_id.unmix_share_type:
  209. if self.share_product_id.code == self.share_to_product_id.code:
  210. raise ValidationError(
  211. _(
  212. "You can't convert the share to"
  213. " the same share type."
  214. )
  215. )
  216. if self.subscription_amount != self.partner_id.total_value:
  217. raise ValidationError(
  218. _(
  219. "You must convert all the shares"
  220. " to the selected type."
  221. )
  222. )
  223. else:
  224. if self.subscription_amount != self.partner_id.total_value:
  225. raise ValidationError(
  226. _(
  227. "Converting just part of the"
  228. " shares is not yet implemented"
  229. )
  230. )
  231. elif self.operation_type == "transfer":
  232. if (
  233. not self.receiver_not_member
  234. and self.company_id.unmix_share_type
  235. and (
  236. self.partner_id_to.cooperator_type
  237. and self.partner_id.cooperator_type
  238. != self.partner_id_to.cooperator_type
  239. )
  240. ):
  241. raise ValidationError(
  242. _(
  243. "This share type could not be"
  244. " transfered to " + self.partner_id_to.name
  245. )
  246. )
  247. if (
  248. self.partner_id_to.is_company
  249. and not self.share_product_id.by_company
  250. ):
  251. raise ValidationError(
  252. _("This share can not be" " subscribed by a company")
  253. )
  254. if (
  255. not self.partner_id_to.is_company
  256. and not self.share_product_id.by_individual
  257. ):
  258. raise ValidationError(
  259. _("This share can not be" " subscribed an individual")
  260. )
  261. if (
  262. self.receiver_not_member
  263. and self.subscription_request
  264. and not self.subscription_request.validated
  265. ):
  266. raise ValidationError(
  267. _(
  268. "The information of the receiver"
  269. " are not correct. Please correct"
  270. " the information before"
  271. " submitting"
  272. )
  273. )
  274. def get_share_trans_mail_template(self):
  275. return self.env.ref(
  276. "easy_my_coop.email_template_share_transfer", False
  277. )
  278. def get_share_update_mail_template(self):
  279. return self.env.ref("easy_my_coop.email_template_share_update", False)
  280. def send_share_trans_mail(
  281. self, sub_register_line
  282. ): # Unused argument is used in synergie project. Do not remove.
  283. cert_email_template = self.get_share_trans_mail_template()
  284. cert_email_template.send_mail(self.partner_id_to.id, False)
  285. def send_share_update_mail(
  286. self, sub_register_line
  287. ): # Unused argument is used in synergie project. Do not remove.
  288. cert_email_template = self.get_share_update_mail_template()
  289. cert_email_template.send_mail(self.partner_id.id, False)
  290. def get_subscription_register_vals(self, effective_date):
  291. return {
  292. "partner_id": self.partner_id.id,
  293. "quantity": self.quantity,
  294. "share_product_id": self.share_product_id.id,
  295. "type": self.operation_type,
  296. "share_unit_price": self.share_unit_price,
  297. "date": effective_date,
  298. }
  299. @api.multi
  300. def execute_operation(self):
  301. self.ensure_one()
  302. if self.effective_date:
  303. effective_date = self.effective_date
  304. else:
  305. effective_date = self.get_date_now()
  306. self.effective_date = effective_date
  307. sub_request = self.env["subscription.request"]
  308. self.validate()
  309. if self.state != "approved":
  310. raise ValidationError(
  311. _("This operation must be approved" " before to be executed")
  312. )
  313. values = self.get_subscription_register_vals(effective_date)
  314. if self.operation_type == "sell_back":
  315. self.hand_share_over(
  316. self.partner_id, self.share_product_id, self.quantity
  317. )
  318. elif self.operation_type == "convert":
  319. amount_to_convert = self.share_unit_price * self.quantity
  320. convert_quant = int(
  321. amount_to_convert / self.share_to_product_id.list_price
  322. )
  323. remainder = amount_to_convert % self.share_to_product_id.list_price
  324. if convert_quant > 0 and remainder == 0:
  325. share_ids = self.partner_id.share_ids
  326. line = share_ids[0]
  327. if len(share_ids) > 1:
  328. share_ids[1 : len(share_ids)].unlink()
  329. line.write(
  330. {
  331. "share_number": convert_quant,
  332. "share_product_id": self.share_to_product_id.id,
  333. "share_unit_price": self.share_to_unit_price,
  334. "share_short_name": self.share_to_short_name,
  335. }
  336. )
  337. values["share_to_product_id"] = self.share_to_product_id.id
  338. values["quantity_to"] = convert_quant
  339. else:
  340. raise ValidationError(
  341. _(
  342. "Converting just part of the"
  343. " shares is not yet implemented"
  344. )
  345. )
  346. elif self.operation_type == "transfer":
  347. sequence_id = self.env.ref(
  348. "easy_my_coop.sequence_subscription", False
  349. )
  350. partner_vals = {"member": True}
  351. if self.receiver_not_member:
  352. partner = self.subscription_request.create_coop_partner()
  353. # get cooperator number
  354. sub_reg_num = int(sequence_id.next_by_id())
  355. partner_vals.update(
  356. sub_request.get_eater_vals(partner, self.share_product_id)
  357. )
  358. partner_vals["cooperator_register_number"] = sub_reg_num
  359. partner.write(partner_vals)
  360. self.partner_id_to = partner
  361. else:
  362. # means an old member or cooperator candidate
  363. if not self.partner_id_to.member:
  364. if self.partner_id_to.cooperator_register_number == 0:
  365. sub_reg_num = int(sequence_id.next_by_id())
  366. partner_vals[
  367. "cooperator_register_number"
  368. ] = sub_reg_num
  369. partner_vals.update(
  370. sub_request.get_eater_vals(
  371. self.partner_id_to, self.share_product_id
  372. )
  373. )
  374. partner_vals["old_member"] = False
  375. self.partner_id_to.write(partner_vals)
  376. # remove the parts to the giver
  377. self.hand_share_over(
  378. self.partner_id, self.share_product_id, self.quantity
  379. )
  380. # give the share to the receiver
  381. self.env["share.line"].create(
  382. {
  383. "share_number": self.quantity,
  384. "partner_id": self.partner_id_to.id,
  385. "share_product_id": self.share_product_id.id,
  386. "share_unit_price": self.share_unit_price,
  387. "effective_date": effective_date,
  388. }
  389. )
  390. values["partner_id_to"] = self.partner_id_to.id
  391. else:
  392. raise ValidationError(
  393. _("This operation is not yet" " implemented.")
  394. )
  395. sequence_operation = self.env.ref(
  396. "easy_my_coop.sequence_register_operation", False
  397. ) # noqa
  398. sub_reg_operation = sequence_operation.next_by_id()
  399. values["name"] = sub_reg_operation
  400. values["register_number_operation"] = int(sub_reg_operation)
  401. self.write({"state": "done"})
  402. sub_register_line = self.env["subscription.register"].create(values)
  403. # send mail to the receiver
  404. if self.operation_type == "transfer":
  405. self.send_share_trans_mail(sub_register_line)
  406. self.send_share_update_mail(sub_register_line)