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.

317 lines
11 KiB

[ADD] Improve Shift management après avoir généré les shifts pour une semaine, rediriger vers les shifts générés : ok Ajouter un supercoopérateur au niveau du template de shift. Pas de champs related car il faut pouvoir gérer les remplacements entre supercoopérateurs. : Rajouter un boolean sur res.users pour dire qu'ils sont super coopérateur du coup on peut choisir qu'un utilisateur super coopérateur : Ok Sur la vue des shifts, permettre de sélectionner un ensemble de shifts pour pouvoir changer son supercoopérateur en une action : Ok Avoir une vue des shifts par défaut plus lisible que le calendrier : KANBAN avec TODAY par défaut et les colonnes selon le type de shift : ok Dans la vue KANBAN du planning des semaines, faire un tri sur la start date pour que ça ressemble à une vue calendrier :ok Droits d'accès : 3 groupes : modifier un shift existant et confirmer son statut : absence/présence générer et supprimer des shifts à partir du template modifier le template des semaines et gérer la config En réfléchissant à ceux qui vont gérer les présences : il faudrait avoir une vue facile sur les shifts du jour et une manière simple de dire présent/absent/remplacé pour l'ensemble des coopérateurs présents (éviter de devoir chaque fois sélectionner le shift, cliquer sur le statut, aller au shift suivant, cliquer sur le statut...). J'ai fait un nouveau point de menu avec une vue Kanban grouper par status, il suffit de faire du drag and drop pour changer le status, les shifts sont aussi filtré sur aujourd'hui et seulement ceux ayant un worker assigné.
8 years ago
4 years ago
4 years ago
[ADD] Improve Shift management après avoir généré les shifts pour une semaine, rediriger vers les shifts générés : ok Ajouter un supercoopérateur au niveau du template de shift. Pas de champs related car il faut pouvoir gérer les remplacements entre supercoopérateurs. : Rajouter un boolean sur res.users pour dire qu'ils sont super coopérateur du coup on peut choisir qu'un utilisateur super coopérateur : Ok Sur la vue des shifts, permettre de sélectionner un ensemble de shifts pour pouvoir changer son supercoopérateur en une action : Ok Avoir une vue des shifts par défaut plus lisible que le calendrier : KANBAN avec TODAY par défaut et les colonnes selon le type de shift : ok Dans la vue KANBAN du planning des semaines, faire un tri sur la start date pour que ça ressemble à une vue calendrier :ok Droits d'accès : 3 groupes : modifier un shift existant et confirmer son statut : absence/présence générer et supprimer des shifts à partir du template modifier le template des semaines et gérer la config En réfléchissant à ceux qui vont gérer les présences : il faudrait avoir une vue facile sur les shifts du jour et une manière simple de dire présent/absent/remplacé pour l'ensemble des coopérateurs présents (éviter de devoir chaque fois sélectionner le shift, cliquer sur le statut, aller au shift suivant, cliquer sur le statut...). J'ai fait un nouveau point de menu avec une vue Kanban grouper par status, il suffit de faire du drag and drop pour changer le status, les shifts sont aussi filtré sur aujourd'hui et seulement ceux ayant un worker assigné.
8 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
[ADD] Improve Shift management après avoir généré les shifts pour une semaine, rediriger vers les shifts générés : ok Ajouter un supercoopérateur au niveau du template de shift. Pas de champs related car il faut pouvoir gérer les remplacements entre supercoopérateurs. : Rajouter un boolean sur res.users pour dire qu'ils sont super coopérateur du coup on peut choisir qu'un utilisateur super coopérateur : Ok Sur la vue des shifts, permettre de sélectionner un ensemble de shifts pour pouvoir changer son supercoopérateur en une action : Ok Avoir une vue des shifts par défaut plus lisible que le calendrier : KANBAN avec TODAY par défaut et les colonnes selon le type de shift : ok Dans la vue KANBAN du planning des semaines, faire un tri sur la start date pour que ça ressemble à une vue calendrier :ok Droits d'accès : 3 groupes : modifier un shift existant et confirmer son statut : absence/présence générer et supprimer des shifts à partir du template modifier le template des semaines et gérer la config En réfléchissant à ceux qui vont gérer les présences : il faudrait avoir une vue facile sur les shifts du jour et une manière simple de dire présent/absent/remplacé pour l'ensemble des coopérateurs présents (éviter de devoir chaque fois sélectionner le shift, cliquer sur le statut, aller au shift suivant, cliquer sur le statut...). J'ai fait un nouveau point de menu avec une vue Kanban grouper par status, il suffit de faire du drag and drop pour changer le status, les shifts sont aussi filtré sur aujourd'hui et seulement ceux ayant un worker assigné.
8 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
  1. import json
  2. from datetime import datetime, time, timedelta
  3. from odoo import _, api, fields, models
  4. from odoo.exceptions import UserError, ValidationError
  5. class Task(models.Model):
  6. _name = "beesdoo.shift.shift"
  7. _inherit = ["mail.thread"]
  8. _order = "start_time asc"
  9. ##################################
  10. # Method to override #
  11. # to have different state #
  12. # on the shift #
  13. ##################################
  14. def _get_selection_status(self):
  15. return [
  16. ("open", "Confirmed"),
  17. ("done", "Attended"),
  18. ("absent", "Absent"),
  19. ("excused", "Excused"),
  20. ("cancel", "Cancelled"),
  21. ]
  22. def _get_color_mapping(self, state):
  23. return {
  24. "draft": 0,
  25. "open": 1,
  26. "done": 5,
  27. "absent": 2,
  28. "excused": 3,
  29. "cancel": 9,
  30. }[state]
  31. def _get_final_state(self):
  32. return ["done", "absent", "excused"]
  33. name = fields.Char(track_visibility="always")
  34. task_template_id = fields.Many2one("beesdoo.shift.template")
  35. planning_id = fields.Many2one(
  36. related="task_template_id.planning_id", store=True
  37. )
  38. task_type_id = fields.Many2one("beesdoo.shift.type", string="Task Type")
  39. worker_id = fields.Many2one(
  40. "res.partner",
  41. track_visibility="onchange",
  42. domain=[
  43. ("is_worker", "=", True),
  44. ("working_mode", "in", ("regular", "irregular")),
  45. ("state", "not in", ("unsubscribed", "resigning")),
  46. ],
  47. )
  48. start_time = fields.Datetime(
  49. track_visibility="always", index=True, required=True
  50. )
  51. end_time = fields.Datetime(track_visibility="always", required=True)
  52. state = fields.Selection(
  53. selection=lambda x: x._get_selection_status(),
  54. default="open",
  55. required=True,
  56. track_visibility="onchange",
  57. group_expand="_expand_states",
  58. )
  59. color = fields.Integer(compute="_compute_color")
  60. super_coop_id = fields.Many2one(
  61. "res.users",
  62. string="Super Cooperative",
  63. domain=[("partner_id.super", "=", True)],
  64. track_visibility="onchange",
  65. )
  66. is_regular = fields.Boolean(default=False, string="Regular shift")
  67. is_compensation = fields.Boolean(
  68. default=False, string="Compensation shift"
  69. )
  70. replaced_id = fields.Many2one(
  71. "res.partner",
  72. track_visibility="onchange",
  73. domain=[
  74. ("eater", "=", "worker_eater"),
  75. ("working_mode", "=", "regular"),
  76. ("state", "not in", ("unsubscribed", "resigning")),
  77. ],
  78. )
  79. revert_info = fields.Text(copy=False)
  80. working_mode = fields.Selection(related="worker_id.working_mode")
  81. def _expand_states(self, states, domain, order):
  82. return [key for key, val in self._fields["state"].selection]
  83. @api.depends("state")
  84. def _compute_color(self):
  85. for rec in self:
  86. rec.color = self._get_color_mapping(rec.state)
  87. @api.constrains("state")
  88. def _lock_future_task(self):
  89. if datetime.now() < self.start_time:
  90. if self.state in self._get_final_state():
  91. raise UserError(
  92. _(
  93. "Shift state of a future shift "
  94. "can't be set to 'present' or 'absent'."
  95. )
  96. )
  97. def message_auto_subscribe(self, updated_fields, values=None):
  98. self._add_follower(values)
  99. return super(Task, self).message_auto_subscribe(
  100. updated_fields, values=values
  101. )
  102. def _add_follower(self, vals):
  103. if vals.get("worker_id"):
  104. worker = self.env["res.partner"].browse(vals["worker_id"])
  105. self.message_subscribe(partner_ids=worker.ids)
  106. # TODO button to replaced someone
  107. @api.model
  108. def unsubscribe_from_today(
  109. self, worker_ids, today=None, end_date=None, now=None
  110. ):
  111. """
  112. Unsubscribe workers from *worker_ids* from all shift that start
  113. *today* and later.
  114. If *end_date* is given, unsubscribe workers from shift between *today*
  115. and *end_date*.
  116. If *now* is given workers are unsubscribed from all shifts starting
  117. *now* and later.
  118. If *now* is given, *end_date* is not taken into account.
  119. :type today: date
  120. :type end_date: date
  121. :type now: datetime
  122. """
  123. if now:
  124. if not isinstance(now, datetime):
  125. raise UserError(_("'Now' must be a datetime."))
  126. date_domain = [("start_time", ">", now)]
  127. else:
  128. today = today or fields.Date.today()
  129. today = datetime.combine(today, time())
  130. date_domain = [("start_time", ">", today)]
  131. if end_date:
  132. end_date = datetime.combine(
  133. end_date, time(hour=23, minute=59, second=59)
  134. )
  135. date_domain.append(("end_time", "<=", end_date))
  136. to_unsubscribe = self.search(
  137. [("worker_id", "in", worker_ids)] + date_domain
  138. )
  139. to_unsubscribe.write({"worker_id": False})
  140. # Remove worker, replaced_id and regular
  141. to_unsubscribe_replace = self.search(
  142. [("replaced_id", "in", worker_ids)] + date_domain
  143. )
  144. to_unsubscribe_replace.write(
  145. {"worker_id": False, "replaced_id": False}
  146. )
  147. # If worker is Super cooperator, remove it from planning
  148. super_coop_ids = (
  149. self.env["res.users"]
  150. .search([("partner_id", "in", worker_ids), ("super", "=", True)])
  151. .ids
  152. )
  153. if super_coop_ids:
  154. to_unsubscribe_super_coop = self.search(
  155. [("super_coop_id", "in", super_coop_ids)] + date_domain
  156. )
  157. to_unsubscribe_super_coop.write({"super_coop_id": False})
  158. @api.multi
  159. def write(self, vals):
  160. """
  161. Overwrite write to track state change
  162. If worker is changer:
  163. Revert for the current worker
  164. Change the worker info
  165. Compute state change for the new worker
  166. """
  167. if "worker_id" in vals:
  168. for rec in self:
  169. if rec.worker_id.id != vals["worker_id"]:
  170. rec._revert()
  171. # To satisfy the constrains on worker_id, it must be
  172. # accompanied by the change in is_regular and
  173. # is_compensation field.
  174. super(Task, rec).write(
  175. {
  176. "worker_id": vals["worker_id"],
  177. "is_regular": vals.get(
  178. "is_regular", rec.is_regular
  179. ),
  180. "is_compensation": vals.get(
  181. "is_compensation", rec.is_compensation
  182. ),
  183. }
  184. )
  185. rec._update_state(rec.state)
  186. if "state" in vals:
  187. for rec in self:
  188. if vals["state"] != rec.state:
  189. rec._update_state(vals["state"])
  190. return super(Task, self).write(vals)
  191. def _set_revert_info(self, data, status):
  192. data_new = {
  193. "status_id": status.id,
  194. "data": {
  195. k: data.get(k, 0) * -1
  196. for k in ["sr", "sc", "irregular_absence_counter"]
  197. },
  198. }
  199. if data.get("irregular_absence_date"):
  200. data_new["data"]["irregular_absence_date"] = False
  201. self.write({"revert_info": json.dumps(data_new)})
  202. def _revert(self):
  203. if not self.revert_info:
  204. return
  205. data = json.loads(self.revert_info)
  206. self.env["cooperative.status"].browse(
  207. data["status_id"]
  208. ).sudo()._change_counter(data["data"])
  209. self.revert_info = False
  210. def _update_state(self, new_state):
  211. self.ensure_one()
  212. self._revert()
  213. if (
  214. not (self.worker_id or self.replaced_id)
  215. and new_state in self._get_final_state()
  216. ):
  217. raise UserError(
  218. _(
  219. "You cannot change to the status %s if no worker is "
  220. "defined for the shift "
  221. )
  222. % new_state
  223. )
  224. always_update = int(
  225. self.env["ir.config_parameter"]
  226. .sudo()
  227. .get_param("always_update", False)
  228. )
  229. if always_update or not (self.worker_id or self.replaced_id):
  230. return
  231. if not (self.worker_id.working_mode in ["regular", "irregular"]):
  232. raise UserError(
  233. _(
  234. "Working mode is not properly defined. Please check if "
  235. "the worker is subscribed "
  236. )
  237. )
  238. data, status = self._get_counter_date_state_change(new_state)
  239. if status:
  240. status.sudo()._change_counter(data)
  241. self._set_revert_info(data, status)
  242. @api.model
  243. def _cron_send_weekly_emails(self):
  244. """
  245. Send a summary email for all workers
  246. if they have a shift planned during the week.
  247. """
  248. tasks = self.env["beesdoo.shift.shift"]
  249. shift_summary_mail_template = self.env.ref(
  250. "beesdoo_shift.email_template_shift_summary", False
  251. )
  252. start_time = datetime.now() + timedelta(days=1)
  253. end_time = datetime.now() + timedelta(days=7)
  254. confirmed_tasks = tasks.search(
  255. [
  256. ("start_time", ">", start_time),
  257. ("start_time", "<", end_time),
  258. ("worker_id", "!=", False),
  259. ("state", "=", "open"),
  260. ]
  261. )
  262. for rec in confirmed_tasks:
  263. shift_summary_mail_template.send_mail(rec.id, True)
  264. ########################################################
  265. # Method to override #
  266. # To define the behavior of the status #
  267. # #
  268. # By default: everyone is always up to date #
  269. ########################################################
  270. def _get_counter_date_state_change(self, new_state):
  271. """
  272. Return the cooperator_status of the cooperator that need to be
  273. change and data that need to be change. It does not perform the
  274. change directly. The cooperator_status will be changed by the
  275. _change_counter function.
  276. Check has been done to ensure that worker is legitimate.
  277. """
  278. data = {}
  279. status = None
  280. return data, status