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.

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