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.

447 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 > fields.Datetime.now():
  117. raise ValidationError("The effective date can not be in the future.")\
  118. @api.multi
  119. def approve_operation(self):
  120. for rec in self:
  121. rec.write({"state": "approved"})
  122. @api.multi
  123. def refuse_operation(self):
  124. for rec in self:
  125. rec.write({"state": "refused"})
  126. @api.multi
  127. def submit_operation(self):
  128. for rec in self:
  129. rec.validate()
  130. rec.write({"state": "waiting"})
  131. @api.multi
  132. def cancel_operation(self):
  133. for rec in self:
  134. rec.write({"state": "cancelled"})
  135. @api.multi
  136. def reset_to_draft(self):
  137. for rec in self:
  138. rec.write({"state": "draft"})
  139. def get_total_share_dic(self, partner):
  140. total_share_dic = {}
  141. share_products = self.env["product.product"].search(
  142. [("is_share", "=", True)]
  143. )
  144. for share_product in share_products:
  145. total_share_dic[share_product.id] = 0
  146. for line in partner.share_ids:
  147. total_share_dic[line.share_product_id.id] += line.share_number
  148. return total_share_dic
  149. # This function doesn't handle the case of a cooperator can own
  150. # different kinds of share type
  151. def hand_share_over(self, partner, share_product_id, quantity):
  152. if not partner.member:
  153. raise ValidationError(
  154. _(
  155. "This operation can't be executed if the"
  156. " cooperator is not an effective member"
  157. )
  158. )
  159. share_ind = len(partner.share_ids)
  160. i = 1
  161. while quantity > 0:
  162. line = self.partner_id.share_ids[share_ind - i]
  163. if line.share_product_id.id == share_product_id.id:
  164. if quantity > line.share_number:
  165. quantity -= line.share_number
  166. line.unlink()
  167. else:
  168. share_left = line.share_number - quantity
  169. quantity = 0
  170. line.write({"share_number": share_left})
  171. i += 1
  172. # if the cooperator sold all his shares he's no more
  173. # an effective member
  174. remaning_share_dict = 0
  175. for share_quant in self.get_total_share_dic(partner).values():
  176. remaning_share_dict += share_quant
  177. if remaning_share_dict == 0:
  178. self.partner_id.write({"member": False, "old_member": True})
  179. def has_share_type(self):
  180. for line in self.partner_id.share_ids:
  181. if line.share_product_id.id == self.share_product_id.id:
  182. return True
  183. return False
  184. def validate(self):
  185. if not self.has_share_type() and self.operation_type in [
  186. "sell_back",
  187. "transfer",
  188. ]:
  189. raise ValidationError(
  190. _(
  191. "The cooperator doesn't own this share"
  192. " type. Please choose the appropriate"
  193. " share type."
  194. )
  195. )
  196. if self.operation_type in ["sell_back", "convert", "transfer"]:
  197. total_share_dic = self.get_total_share_dic(self.partner_id)
  198. if self.quantity > total_share_dic[self.share_product_id.id]:
  199. raise ValidationError(
  200. _(
  201. "The cooperator can't hand over more"
  202. " shares that he/she owns."
  203. )
  204. )
  205. if self.operation_type == "convert":
  206. if self.company_id.unmix_share_type:
  207. if self.share_product_id.code == self.share_to_product_id.code:
  208. raise ValidationError(
  209. _(
  210. "You can't convert the share to"
  211. " the same share type."
  212. )
  213. )
  214. if self.subscription_amount != self.partner_id.total_value:
  215. raise ValidationError(
  216. _(
  217. "You must convert all the shares"
  218. " to the selected type."
  219. )
  220. )
  221. else:
  222. if self.subscription_amount != self.partner_id.total_value:
  223. raise ValidationError(
  224. _(
  225. "Converting just part of the"
  226. " shares is not yet implemented"
  227. )
  228. )
  229. elif self.operation_type == "transfer":
  230. if (
  231. not self.receiver_not_member
  232. and self.company_id.unmix_share_type
  233. and (
  234. self.partner_id_to.cooperator_type
  235. and self.partner_id.cooperator_type
  236. != self.partner_id_to.cooperator_type
  237. )
  238. ):
  239. raise ValidationError(
  240. _(
  241. "This share type could not be"
  242. " transfered to " + self.partner_id_to.name
  243. )
  244. )
  245. if (
  246. self.partner_id_to.is_company
  247. and not self.share_product_id.by_company
  248. ):
  249. raise ValidationError(
  250. _("This share can not be" " subscribed by a company")
  251. )
  252. if (
  253. not self.partner_id_to.is_company
  254. and not self.share_product_id.by_individual
  255. ):
  256. raise ValidationError(
  257. _("This share can not be" " subscribed an individual")
  258. )
  259. if (
  260. self.receiver_not_member
  261. and self.subscription_request
  262. and not self.subscription_request.validated
  263. ):
  264. raise ValidationError(
  265. _(
  266. "The information of the receiver"
  267. " are not correct. Please correct"
  268. " the information before"
  269. " submitting"
  270. )
  271. )
  272. def get_share_trans_mail_template(self):
  273. return self.env.ref(
  274. "easy_my_coop.email_template_share_transfer", False
  275. )
  276. def get_share_update_mail_template(self):
  277. return self.env.ref("easy_my_coop.email_template_share_update", False)
  278. def send_share_trans_mail(
  279. self, sub_register_line
  280. ): # Unused argument is used in synergie project. Do not remove.
  281. cert_email_template = self.get_share_trans_mail_template()
  282. cert_email_template.send_mail(self.partner_id_to.id, False)
  283. def send_share_update_mail(
  284. self, sub_register_line
  285. ): # Unused argument is used in synergie project. Do not remove.
  286. cert_email_template = self.get_share_update_mail_template()
  287. cert_email_template.send_mail(self.partner_id.id, False)
  288. def get_subscription_register_vals(self, effective_date):
  289. return {
  290. "partner_id": self.partner_id.id,
  291. "quantity": self.quantity,
  292. "share_product_id": self.share_product_id.id,
  293. "type": self.operation_type,
  294. "share_unit_price": self.share_unit_price,
  295. "date": effective_date,
  296. }
  297. @api.multi
  298. def execute_operation(self):
  299. self.ensure_one()
  300. if self.effective_date:
  301. effective_date = self.effective_date
  302. else:
  303. effective_date = self.get_date_now()
  304. sub_request = self.env["subscription.request"]
  305. self.validate()
  306. if self.state != "approved":
  307. raise ValidationError(
  308. _("This operation must be approved" " before to be executed")
  309. )
  310. values = self.get_subscription_register_vals(effective_date)
  311. if self.operation_type == "sell_back":
  312. self.hand_share_over(
  313. self.partner_id, self.share_product_id, self.quantity
  314. )
  315. elif self.operation_type == "convert":
  316. amount_to_convert = self.share_unit_price * self.quantity
  317. convert_quant = int(
  318. amount_to_convert / self.share_to_product_id.list_price
  319. )
  320. remainder = amount_to_convert % self.share_to_product_id.list_price
  321. if convert_quant > 0 and remainder == 0:
  322. share_ids = self.partner_id.share_ids
  323. line = share_ids[0]
  324. if len(share_ids) > 1:
  325. share_ids[1 : len(share_ids)].unlink()
  326. line.write(
  327. {
  328. "share_number": convert_quant,
  329. "share_product_id": self.share_to_product_id.id,
  330. "share_unit_price": self.share_to_unit_price,
  331. "share_short_name": self.share_to_short_name,
  332. }
  333. )
  334. values["share_to_product_id"] = self.share_to_product_id.id
  335. values["quantity_to"] = convert_quant
  336. else:
  337. raise ValidationError(
  338. _(
  339. "Converting just part of the"
  340. " shares is not yet implemented"
  341. )
  342. )
  343. elif self.operation_type == "transfer":
  344. sequence_id = self.env.ref(
  345. "easy_my_coop.sequence_subscription", False
  346. )
  347. partner_vals = {"member": True}
  348. if self.receiver_not_member:
  349. partner = self.subscription_request.create_coop_partner()
  350. # get cooperator number
  351. sub_reg_num = int(sequence_id.next_by_id())
  352. partner_vals.update(
  353. sub_request.get_eater_vals(partner, self.share_product_id)
  354. )
  355. partner_vals["cooperator_register_number"] = sub_reg_num
  356. partner.write(partner_vals)
  357. self.partner_id_to = partner
  358. else:
  359. # means an old member or cooperator candidate
  360. if not self.partner_id_to.member:
  361. if self.partner_id_to.cooperator_register_number == 0:
  362. sub_reg_num = int(sequence_id.next_by_id())
  363. partner_vals[
  364. "cooperator_register_number"
  365. ] = sub_reg_num
  366. partner_vals.update(
  367. sub_request.get_eater_vals(
  368. self.partner_id_to, self.share_product_id
  369. )
  370. )
  371. partner_vals["old_member"] = False
  372. self.partner_id_to.write(partner_vals)
  373. # remove the parts to the giver
  374. self.hand_share_over(
  375. self.partner_id, self.share_product_id, self.quantity
  376. )
  377. # give the share to the receiver
  378. self.env["share.line"].create(
  379. {
  380. "share_number": self.quantity,
  381. "partner_id": self.partner_id_to.id,
  382. "share_product_id": self.share_product_id.id,
  383. "share_unit_price": self.share_unit_price,
  384. "effective_date": effective_date,
  385. }
  386. )
  387. values["partner_id_to"] = self.partner_id_to.id
  388. else:
  389. raise ValidationError(
  390. _("This operation is not yet" " implemented.")
  391. )
  392. sequence_operation = self.env.ref(
  393. "easy_my_coop.sequence_register_operation", False
  394. ) # noqa
  395. sub_reg_operation = sequence_operation.next_by_id()
  396. values["name"] = sub_reg_operation
  397. values["register_number_operation"] = int(sub_reg_operation)
  398. self.write({"state": "done"})
  399. sub_register_line = self.env["subscription.register"].create(values)
  400. # send mail to the receiver
  401. if self.operation_type == "transfer":
  402. self.send_share_trans_mail(sub_register_line)
  403. self.send_share_update_mail(sub_register_line)