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.

338 lines
15 KiB

7 years ago
7 years ago
7 years ago
7 years ago
  1. # -*- coding: utf-8 -*-
  2. from openerp import models, fields, api, _
  3. from openerp.exceptions import ValidationError
  4. from datetime import timedelta, datetime
  5. import logging
  6. from openerp.osv.fields import related
  7. _logger = logging.getLogger(__name__)
  8. def add_days_delta(date_from, days_delta):
  9. if not date_from:
  10. return date_from
  11. next_date = fields.Date.from_string(date_from) + timedelta(days=days_delta)
  12. return fields.Date.to_string(next_date)
  13. class ExemptReason(models.Model):
  14. _name = 'cooperative.exempt.reason'
  15. name = fields.Char(required=True)
  16. class HistoryStatus(models.Model):
  17. _name = 'cooperative.status.history'
  18. _order= 'create_date desc'
  19. status_id = fields.Many2one('cooperative.status')
  20. cooperator_id = fields.Many2one('res.partner')
  21. change = fields.Char()
  22. type = fields.Selection([('status', 'Status Change'), ('counter', 'Counter Change')])
  23. user_id = fields.Many2one('res.users', string="User")
  24. class CooperativeStatus(models.Model):
  25. _name = 'cooperative.status'
  26. _rec_name = 'cooperator_id'
  27. _order = 'cooperator_id'
  28. today = fields.Date(help="Field that allow to compute field and store them even if they are based on the current date", default=fields.Date.today)
  29. cooperator_id = fields.Many2one('res.partner')
  30. info_session = fields.Boolean('Information Session ?')
  31. info_session_date = fields.Datetime('Information Session Date')
  32. super = fields.Boolean("Super Cooperative")
  33. sr = fields.Integer("Compteur shift regulier", default=0)
  34. sc = fields.Integer("Compteur shift de compensation", default=0)
  35. time_extension = fields.Integer("Extension Days NB", default=0, help="Addtional days to the automatic extension, 5 mean that you have a total of 15 extension days of default one is set to 10")
  36. holiday_start_time = fields.Date("Holidays Start Day")
  37. holiday_end_time = fields.Date("Holidays End Day")
  38. alert_start_time = fields.Date("Alert Start Day")
  39. extension_start_time = fields.Date("Extension Start Day")
  40. #Champ compute
  41. working_mode = fields.Selection(
  42. [
  43. ('regular', 'Regular worker'),
  44. ('irregular', 'Irregular worker'),
  45. ('exempt', 'Exempted'),
  46. ],
  47. string="Working mode"
  48. )
  49. exempt_reason_id = fields.Many2one('cooperative.exempt.reason', 'Exempt Reason')
  50. status = fields.Selection([('ok', 'Up to Date'),
  51. ('holiday', 'Holidays'),
  52. ('alert', 'Alerte'),
  53. ('extension', 'Extension'),
  54. ('suspended', 'Suspended'),
  55. ('unsubscribed', 'Unsubscribed')],
  56. compute="_compute_status", string="Cooperative Status", store=True)
  57. can_shop = fields.Boolean(compute='_compute_status', store=True)
  58. history_ids = fields.One2many('cooperative.status.history', 'status_id', readonly=True)
  59. unsubscribed = fields.Boolean(default=False, help="Manually unsubscribed")
  60. #Specific to irregular
  61. irregular_start_date = fields.Date() #TODO migration script
  62. irregular_absence_date = fields.Date()
  63. irregular_absence_counter = fields.Integer() #TODO unsubscribe when reach -2
  64. @api.depends('today', 'sr', 'sc', 'holiday_end_time',
  65. 'holiday_start_time', 'time_extension',
  66. 'alert_start_time', 'extension_start_time',
  67. 'unsubscribed', 'irregular_absence_date',
  68. 'irregular_absence_counter')
  69. def _compute_status(self):
  70. alert_delay = int(self.env['ir.config_parameter'].get_param('alert_delay', 28))
  71. grace_delay = int(self.env['ir.config_parameter'].get_param('default_grace_delay', 10))
  72. update = int(self.env['ir.config_parameter'].get_param('always_update', False))
  73. print update
  74. for rec in self:
  75. if update or not self.today:
  76. rec.status = 'ok'
  77. rec.can_shop = True
  78. continue
  79. if rec.working_mode == 'regular':
  80. rec._set_regular_status(grace_delay, alert_delay)
  81. elif rec.working_mode == 'irregular':
  82. rec._set_irregular_status(grace_delay, alert_delay)
  83. elif rec.working_mode == 'exempt':
  84. rec.status = 'ok'
  85. rec.can_shop = True
  86. def _set_regular_status(self, grace_delay, alert_delay):
  87. self.ensure_one()
  88. ok = self.sr >= 0 and self.sc >= 0
  89. grace_delay = grace_delay + self.time_extension
  90. if self.sr < -1 or self.unsubscribed:
  91. self.status = 'unsubscribed'
  92. self.can_shop = False
  93. #Transition to alert sr < 0 or stay in alert sr < 0 or sc < 0 and thus alert time is defined
  94. elif not ok and self.alert_start_time and self.extension_start_time and self.today <= add_days_delta(self.extension_start_time, grace_delay):
  95. self.status = 'extension'
  96. self.can_shop = True
  97. elif not ok and self.alert_start_time and self.extension_start_time and self.today > add_days_delta(self.extension_start_time, grace_delay):
  98. self.status = 'suspended'
  99. self.can_shop = False
  100. elif not ok and self.alert_start_time and self.today > add_days_delta(self.alert_start_time, alert_delay):
  101. self.status = 'suspended'
  102. self.can_shop = False
  103. elif (self.sr < 0) or (not ok and self.alert_start_time):
  104. self.status = 'alert'
  105. self.can_shop = True
  106. #Check for holidays; Can be in holidays even in alert or other mode ?
  107. elif self.today >= self.holiday_start_time and self.today <= self.holiday_end_time:
  108. self.status = 'holiday'
  109. self.can_shop = True
  110. elif ok or (not self.alert_start_time and self.sr >= 0):
  111. self.status = 'ok'
  112. self.can_shop = True
  113. def _set_irregular_status(self, grace_delay, alert_delay):
  114. self.ensure_one()
  115. ok = self.sr >= 0
  116. grace_delay = grace_delay + self.time_extension
  117. print add_days_delta(self.extension_start_time, grace_delay)
  118. if (not ok and self.irregular_absence_date and self.today > add_days_delta(self.irregular_absence_date, alert_delay * 2)) \
  119. or self.unsubscribed or self.irregular_absence_counter <= -2:
  120. self.status = 'unsubscribed'
  121. self.can_shop = False
  122. #Transition to alert sr < 0 or stay in alert sr < 0 or sc < 0 and thus alert time is defined
  123. elif not ok and self.alert_start_time and self.extension_start_time and self.today <= add_days_delta(self.extension_start_time, grace_delay):
  124. self.status = 'extension'
  125. self.can_shop = True
  126. elif not ok and self.alert_start_time and self.extension_start_time and self.today > add_days_delta(self.extension_start_time, grace_delay):
  127. self.status = 'suspended'
  128. self.can_shop = False
  129. elif not ok and self.alert_start_time and self.today > add_days_delta(self.alert_start_time, alert_delay):
  130. self.status = 'suspended'
  131. self.can_shop = False
  132. elif (self.sr < 0) or (not ok and self.alert_start_time):
  133. self.status = 'alert'
  134. self.can_shop = True
  135. #Check for holidays; Can be in holidays even in alert or other mode ?
  136. elif self.today >= self.holiday_start_time and self.today <= self.holiday_end_time:
  137. self.status = 'holiday'
  138. self.can_shop = True
  139. elif ok or (not self.alert_start_time and self.sr >= 0):
  140. self.status = 'ok'
  141. self.can_shop = True
  142. @api.multi
  143. def write(self, vals):
  144. """
  145. Overwrite write to historize the change
  146. """
  147. for field in ['sr', 'sc', 'time_extension', 'extension_start_time', 'alert_start_time', 'unsubscribed']:
  148. if not field in vals:
  149. continue
  150. for rec in self:
  151. data = {
  152. 'status_id': rec.id,
  153. 'cooperator_id': rec.cooperator_id.id,
  154. 'type': 'counter',
  155. 'user_id': self.env.context.get('real_uid', self.env.uid),
  156. }
  157. if vals.get(field, rec[field]) != rec[field]:
  158. data['change'] = '%s: %s -> %s' % (field.upper(), rec[field], vals.get(field))
  159. self.env['cooperative.status.history'].sudo().create(data)
  160. return super(CooperativeStatus, self).write(vals)
  161. def _state_change(self, new_state, old_stage):
  162. self.ensure_one()
  163. if new_state == 'alert':
  164. self.write({'alert_start_time': self.today, 'extension_start_time': False, 'time_extension': 0})
  165. if new_state == 'ok': #reset alert start time if back to ok
  166. self.write({'alert_start_time': False, 'extension_start_time': False, 'time_extension': 0})
  167. if new_state == 'unsubscribed':
  168. self.cooperator_id.sudo().write({'subscribed_shift_ids' : [(5,0,0)]})
  169. #TODO: Add one day othertwise unsubscribed from the shift you were absent
  170. self.env['beesdoo.shift.shift'].sudo().unsubscribe_from_today([self.cooperator_id.id], today=self.today)
  171. def _change_counter(self, data):
  172. self.sc += data.get('sc', 0)
  173. self.sr += data.get('sr', 0)
  174. self.irregular_absence_counter += data.get('irregular_absence_counter', 0)
  175. self.irregular_absence_date = data.get('irregular_absence_date', False)
  176. @api.multi
  177. def _write(self, vals):
  178. """
  179. Overwrite write to historize the change of status
  180. and make action on status change
  181. """
  182. if 'status' in vals:
  183. self._cr.execute('select id, status, sr, sc from "%s" where id in %%s' % self._table, (self._ids,))
  184. result = self._cr.dictfetchall()
  185. old_status_per_id = {r['id'] : r for r in result}
  186. for rec in self:
  187. if old_status_per_id[rec.id]['status'] != vals['status']:
  188. data = {
  189. 'status_id': rec.id,
  190. 'cooperator_id': rec.cooperator_id.id,
  191. 'type': 'status',
  192. 'change': "STATUS: %s -> %s" % (old_status_per_id[rec.id]['status'], vals['status']),
  193. 'user_id': self.env.context.get('real_uid', self.env.uid),
  194. }
  195. self.env['cooperative.status.history'].sudo().create(data)
  196. rec._state_change(vals['status'], old_status_per_id[rec.id]['status'])
  197. return super(CooperativeStatus, self)._write(vals)
  198. _sql_constraints = [
  199. ('cooperator_uniq', 'unique (cooperator_id)', _('You can only set one cooperator status per cooperator')),
  200. ]
  201. @api.model
  202. def _set_today(self):
  203. """
  204. Method call by the cron to update store value base on the date
  205. """
  206. self.search([]).write({'today': fields.Date.today()})
  207. @api.multi
  208. def clear_history(self):
  209. self.ensure_one()
  210. self.history_ids.unlink()
  211. @api.model
  212. def _cron_compute_counter_irregular(self, today=False):
  213. today = today or fields.Date.today()
  214. journal = self.env['beesdoo.shift.journal'].search([('date', '=', today)])
  215. if not journal:
  216. journal = self.env['beesdoo.shift.journal'].create({'date': today})
  217. irregular = self.search([('status', '!=', 'unsubscribed'), ('working_mode', '=', 'irregular'), ('irregular_start_date', '!=', False)])
  218. today_date = fields.Date.from_string(today)
  219. for status in irregular:
  220. delta = (today_date - fields.Date.from_string(status.irregular_start_date)).days
  221. if delta and delta % 28 == 0 and status not in journal.line_ids: #TODO use system parameter for 28
  222. if status.sr > 0:
  223. status.sr -= 1
  224. else:
  225. status.sr -= 2
  226. journal.line_ids |= status
  227. class ShiftCronJournal(models.Model):
  228. _name = 'beesdoo.shift.journal'
  229. _order = 'date desc'
  230. _rec_name = 'date'
  231. date = fields.Date()
  232. line_ids = fields.Many2many('cooperative.status')
  233. _sql_constraints = [
  234. ('one_entry_per_day', 'unique (date)', _('You can only create one journal per day')),
  235. ]
  236. @api.multi
  237. def run(self):
  238. self.ensure_one()
  239. if not self.user_has_groups('beesdoo_shift.group_cooperative_admin'):
  240. raise ValidationError(_("You don't have the access to perform this action"))
  241. self.sudo().env['cooperative.status']._cron_compute_counter_irregular(today=self.date)
  242. class ResPartner(models.Model):
  243. _inherit = 'res.partner'
  244. cooperative_status_ids = fields.One2many('cooperative.status', 'cooperator_id', readonly=True)
  245. super = fields.Boolean(related='cooperative_status_ids.super', string="Super Cooperative", readonly=True, store=True)
  246. info_session = fields.Boolean(related='cooperative_status_ids.info_session', string='Information Session ?', readonly=True, store=True)
  247. info_session_date = fields.Datetime(related='cooperative_status_ids.info_session_date', string='Information Session Date', readonly=True, store=True)
  248. working_mode = fields.Selection(related='cooperative_status_ids.working_mode', readonly=True, store=True)
  249. exempt_reason_id = fields.Many2one(related='cooperative_status_ids.exempt_reason_id', readonly=True, store=True)
  250. state = fields.Selection(related='cooperative_status_ids.status', readonly=True, store=True)
  251. extension_start_time = fields.Date(related='cooperative_status_ids.extension_start_time', string="Extension Start Day", readonly=True, store=True)
  252. subscribed_shift_ids = fields.Many2many('beesdoo.shift.template')
  253. @api.multi
  254. def coop_subscribe(self):
  255. return {
  256. 'name': _('Subscribe Cooperator'),
  257. 'type': 'ir.actions.act_window',
  258. 'view_type': 'form',
  259. 'view_mode': 'form',
  260. 'res_model': 'beesdoo.shift.subscribe',
  261. 'target': 'new',
  262. }
  263. @api.multi
  264. def coop_unsubscribe(self):
  265. res = self.coop_subscribe()
  266. res['context'] = {'default_unsubscribed': True}
  267. return res
  268. @api.multi
  269. def manual_extension(self):
  270. return {
  271. 'name': _('Manual Extension'),
  272. 'type': 'ir.actions.act_window',
  273. 'view_type': 'form',
  274. 'view_mode': 'form',
  275. 'res_model': 'beesdoo.shift.extension',
  276. 'target': 'new',
  277. }
  278. @api.multi
  279. def auto_extension(self):
  280. res = self.manual_extension()
  281. res['context'] = {'default_auto': True}
  282. res['name'] = _('Trigger Grace Delay')
  283. return res
  284. @api.multi
  285. def register_holiday(self):
  286. return {
  287. 'name': _('Register Holiday'),
  288. 'type': 'ir.actions.act_window',
  289. 'view_type': 'form',
  290. 'view_mode': 'form',
  291. 'res_model': 'beesdoo.shift.holiday',
  292. 'target': 'new',
  293. }
  294. #TODO access right + vue on res.partner
  295. #TODO can_shop : Status can_shop ou extempted ou part C