Browse Source

[REF] Refactoring of shift module

Move counter specific and cooperator status specific to a module call beesdoo_worker_status
Remove dependency of beesdoo_base to beesdoo_shift
beesdoo_worker_status depends on beesdoo_base
Move all attendance sheet related code to a new module attendance sheet

Leave empty interface on beesdoo_shift to be implemented in other module to define
specific behavior of the counter and the status of the cooperator
pull/143/head
Thibault Francois 4 years ago
parent
commit
d97b676023
  1. 48
      beesdoo_base/demo/cooperators.xml
  2. 3
      beesdoo_base/views/partner.xml
  3. 10
      beesdoo_shift/__manifest__.py
  4. 27
      beesdoo_shift/data/cron.xml
  5. 110
      beesdoo_shift/data/mail_template.xml
  6. 12
      beesdoo_shift/data/system_parameter.xml
  7. 84
      beesdoo_shift/demo/cooperators.xml
  8. 6
      beesdoo_shift/demo/templates.xml
  9. 89
      beesdoo_shift/demo/workers.xml
  10. 2
      beesdoo_shift/models/__init__.py
  11. 400
      beesdoo_shift/models/cooperative_status.py
  12. 2
      beesdoo_shift/models/planning.py
  13. 121
      beesdoo_shift/models/task.py
  14. 19
      beesdoo_shift/security/group.xml
  15. 15
      beesdoo_shift/security/ir.model.access.csv
  16. 56
      beesdoo_shift/views/cooperative_status.xml
  17. 2
      beesdoo_shift/wizard/__init__.py
  18. 2
      beesdoo_shift_atttendance/__init__.py
  19. 36
      beesdoo_shift_atttendance/__manifest__.py
  20. 29
      beesdoo_shift_atttendance/data/cron.xml
  21. 117
      beesdoo_shift_atttendance/data/mail_template.xml
  22. 14
      beesdoo_shift_atttendance/data/system_parameter.xml
  23. 2
      beesdoo_shift_atttendance/models/__init__.py
  24. 4
      beesdoo_shift_atttendance/models/attendance_sheet.py
  25. 61
      beesdoo_shift_atttendance/models/res_config_settings.py
  26. 11
      beesdoo_shift_atttendance/security/group.xml
  27. 12
      beesdoo_shift_atttendance/security/ir.model.access.csv
  28. 0
      beesdoo_shift_atttendance/tests/__init__.py
  29. 96
      beesdoo_shift_atttendance/tests/test_beesdoo_shift.py
  30. 0
      beesdoo_shift_atttendance/views/attendance_sheet.xml
  31. 0
      beesdoo_shift_atttendance/views/res_config_settings_view.xml
  32. 1
      beesdoo_shift_atttendance/wizard/__init__.py
  33. 0
      beesdoo_shift_atttendance/wizard/validate_attendance_sheet.py
  34. 0
      beesdoo_shift_atttendance/wizard/validate_attendance_sheet.xml
  35. 3
      beesdoo_website_shift/controllers/main.py
  36. 54
      beesdoo_website_shift/views/my_shift_website_templates.xml
  37. 8
      beesdoo_website_shift/views/shift_website_templates.xml
  38. 0
      beesdoo_worker_status/__init__.py
  39. 26
      beesdoo_worker_status/__manifest__.py
  40. 1
      beesdoo_worker_status/models/__init__.py
  41. 266
      beesdoo_worker_status/models/cooperative_status.py
  42. 81
      beesdoo_worker_status/models/task.py
  43. 1
      beesdoo_worker_status/tests/__init__.py
  44. 247
      beesdoo_worker_status/tests/test_beesdoo_shift.py

48
beesdoo_base/demo/cooperators.xml

@ -83,4 +83,52 @@
<field name="zip">1060</field>
<field name="country_id" ref="base.be"/>
</record>
<record id="member_card_1_demo" model="member.card">
<field name="barcode">421457731741</field>
<field name="comment">Demo data</field>
<field name="create_date" eval="datetime.now() - timedelta(days=15)" />
<field name="partner_id" ref="res_partner_cooperator_1_demo" />
<field name="valid" eval="True" />
</record>
<record id="member_card_2_demo" model="member.card">
<field name="barcode">429919251493</field>
<field name="comment">Demo data</field>
<field name="create_date" eval="datetime.now() - timedelta(days=10)" />
<field name="partner_id" ref="res_partner_cooperator_2_demo" />
<field name="valid" eval="True" />
</record>
<record id="member_card_3_demo" model="member.card">
<field name="barcode">421457731742</field>
<field name="comment">Demo data</field>
<field name="create_date" eval="datetime.now() - timedelta(days=15)" />
<field name="partner_id" ref="res_partner_cooperator_3_demo" />
<field name="valid" eval="True" />
</record>
<record id="member_card_4_demo" model="member.card">
<field name="barcode">421457731743</field>
<field name="comment">Demo data</field>
<field name="create_date" eval="datetime.now() - timedelta(days=15)" />
<field name="partner_id" ref="res_partner_cooperator_4_demo" />
<field name="valid" eval="True" />
</record>
<record id="member_card_5_demo" model="member.card">
<field name="barcode">421457731744</field>
<field name="comment">Demo data</field>
<field name="create_date" eval="datetime.now() - timedelta(days=15)" />
<field name="partner_id" ref="res_partner_cooperator_5_demo" />
<field name="valid" eval="True" />
</record>
<record id="member_card_6_demo" model="member.card">
<field name="barcode">421457731745</field>
<field name="comment">Demo data</field>
<field name="create_date" eval="datetime.now() - timedelta(days=15)" />
<field name="partner_id" ref="res_partner_cooperator_6_demo" />
<field name="valid" eval="True" />
</record>
</odoo>

3
beesdoo_base/views/partner.xml

@ -20,7 +20,8 @@
<field name="inherit_id" ref="point_of_sale.view_partner_property_form" />
<field name="arch" type="xml">
<field name="type" position="before">
<field name="cooperator_type" />
<field name="is_worker"/>
<field name="cooperator_type"/>
</field>
<field name="website" position="after">
<field name="eater"

10
beesdoo_shift/__manifest__.py

@ -8,13 +8,15 @@
""",
'author': "Thibault Francois, Elouan Le Bars, Coop It Easy",
'author': "THibault Francois, Elouan Le Bars, Coop It Easy",
'website': "https://github.com/beescoop/Obeesdoo",
'category': 'Cooperative management',
'version': '12.0.1.0.0',
'depends': ['beesdoo_base', 'barcodes'],
'depends': [
'mail',
],
'data': [
"data/system_parameter.xml",
@ -23,13 +25,10 @@
"security/group.xml",
"security/ir.model.access.csv",
"views/task_template.xml",
"views/res_config_settings_view.xml",
"views/task.xml",
"views/planning.xml",
"views/cooperative_status.xml",
"views/exempt_reason.xml",
"wizard/validate_attendance_sheet.xml",
"views/attendance_sheet.xml",
"wizard/instanciate_planning.xml",
"wizard/batch_template.xml",
"wizard/assign_super_coop.xml",
@ -40,6 +39,7 @@
"wizard/generate_missing_attendance_sheets.xml",
],
'demo': [
"demo/cooperators.xml",
"demo/workers.xml",
"demo/templates.xml",
]

27
beesdoo_shift/data/cron.xml

@ -20,21 +20,6 @@
<field name="interval_type">weeks</field>
<field name="numbercall">-1</field>
<field name="doall" eval="False" />
<field name="active" eval="False" />
</record>
<record id="ir_cron_generate_attendance_sheet" model="ir.cron">
<field name="name">Generate Attendance Sheets</field>
<field name="model_id" ref="model_beesdoo_shift_sheet" />
<field name="state">code</field>
<field name="code">model._generate_attendance_sheet()</field>
<field name="user_id" ref="base.user_root" />
<field name="interval_number">4</field>
<field name="interval_type">minutes</field>
<field name="numbercall">-1</field>
<field name="doall" eval="False" />
<field name="active" eval="False" />
</record>
<record id="ir_cron_compute_shift_counter" model="ir.cron">
@ -47,19 +32,7 @@
<field name="numbercall">-1</field>
<field name="doall" eval="True" />
<field name="active" eval="False" />
</record>
<record id="ir_cron_check_non_validated_sheet" model="ir.cron">
<field name="name">Check for non-validated sheets</field>
<field name="model_id" ref="model_beesdoo_shift_sheet" />
<field name="state">code</field>
<field name="code">model._cron_non_validated_sheets()</field>
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="numbercall">-1</field>
<field name="doall" eval="False" />
<field name="nextcall" eval="datetime.now().replace(hour=00, minute=00, second=10)" />
<field name="active" eval="False" />
</record>
<record id="ir_cron_send_weekly_emails" model="ir.cron">

110
beesdoo_shift/data/mail_template.xml

@ -3,116 +3,6 @@
<!-- Mail template are declared in a NOUPDATE block
so users can freely customize/delete them -->
<data noupdate="1">
<record id="email_template_non_attendance" model="mail.template">
<field name="name">Shift Non-attendance</field>
<field name="subject">Non-attendance to your last shift.</field>
<field name="partner_to">${object.replaced_id.id or object.worker_id.id|safe}</field>
<field name="model_id" ref="model_beesdoo_shift_shift"/>
<field name="auto_delete" eval="True"/>
<field name="lang">${object.worker_id.lang}</field>
<field name="body_html"><![CDATA[
<div style="font-family: 'Lucica Grande', Ubuntu, Arial, Verdana, sans-serif; font-size: 12px; color: rgb(34, 34, 34); background-color: #FFF; ">
% if object.replaced_id:
<p>Hello ${object.replaced_id.name},
<br><br>You have been recorded as non-attended during your last shift (${format_tz(object.start_time,object.replaced_id.tz or 'Europe/Brussels','%d.%m.%Y - %H:%M')}),
and you were supposed to replace ${object.worker_id.name}.
% endif
% if not object.replaced_id:
</p><p>Hello ${object.worker_id.name},</p>
<p>You have been recorded as non-attended during your last shift (${format_tz(object.start_time,object.worker_id.tz or 'Europe/Brussels','%d.%m.%Y - %H:%M')}).
% endif
% if object.worker_id.working_mode == 'regular':
% if object.state == 'absent_0':
<br><br>Super-cooperator assigned you 0 compensation, so you won't have any additionnal shift to do before your next regular shift.
% endif
% if object.state == 'absent_1':
<br><br>Super-cooperator assigned you 1 compensation, so you have to attend one additionnal shift before your next regular shift.
% endif
% if object.state == 'absent_2':
<br><br>Super-cooperator assigned you 2 compensations, so you have to attend two additionnal shifts before your next regular shift.
% endif
% if object.replaced_id:
You were supposed to replace ${object.worker_id.name}.
You have to do ${(object.replaced_id.cooperative_status_ids.sr + object.replaced_id.cooperative_status_ids.sc) * -1 } shifts before your next regular shift.<br>
% else:
You have to do ${(object.worker_id.cooperative_status_ids.sr + object.worker_id.cooperative_status_ids.sc) * -1 } shifts before your next regular shift.<br>
% endif
% endif
% if object.worker_id.working_mode == 'irregular':
Your shift counter is at ${object.worker_id.cooperative_status_ids.sr}.
% if object.worker_id.cooperative_status_ids.future_alert_date:
It should be superior or equal to 1 before the
${object.worker_id.cooperative_status_ids.future_alert_date}.
% endif
<br>
% endif
% if object.replaced_id:
Your current status is "${object.replaced_id.cooperative_status_ids.get_status_value()}".
% else:
<br><br>Your current status is "${object.worker_id.cooperative_status_ids.get_status_value()}".
% endif
<br>If you have any question regarding this non-attendance, just answer this e-mail.
</p>
<br>
<p>Cooperatively yours,<br>
The Members' office volunteers</p>
<p>${object.worker_id.company_id.name}.</p>
% if object.worker_id.company_id.street:
${object.worker_id.company_id.street}
% endif
% if object.worker_id.company_id.street2:
${object.worker_id.company_id.street2}<br>
% endif
% if object.worker_id.company_id.city or object.worker_id.company_id.zip:
${object.worker_id.company_id.zip} ${object.worker_id.company_id.city}<br>
% endif
% if object.worker_id.company_id.country_id:
${object.worker_id.company_id.state_id and ('%s, ' % object.worker_id.company_id.state_id.name) or ''} ${object.worker_id.company_id.country_id.name or ''}<br>
% endif
% if object.worker_id.company_id.phone:
Phone:&nbsp; ${object.worker_id.company_id.phone}
% endif
% if object.worker_id.company_id.website:
<div>
Web :&nbsp;<a href="${object.worker_id.company_id.website}">${object.worker_id.company_id.website}</a>
</div>
%endif
% if object.worker_id.company_id.logo_url:
<div>
<img src="${object.worker_id.company_id.logo_url}">
</div>
%endif
</div>
]]></field>
</record>
<record id="email_template_non_validated_sheet" model="mail.template">
<field name="name">Non-validated sheet</field>
<field name="subject">[${object.day}] Non-validated sheet ${object.time_slot}</field>
<field name="model_id" ref="model_beesdoo_shift_sheet"/>
<field name="auto_delete" eval="True"/>
<field name="body_html"><![CDATA[
<div style="font-family: 'Lucica Grande', Ubuntu, Arial, Verdana, sans-serif; font-size: 12px; color: rgb(34, 34, 34); background-color: #FFF; ">
<p>${object.day}
<br/><br/>The attendance sheet for ${object.time_slot} is not validated.
<br/><br/>Please, do it as soon as possible so as to update workers' status.
</p>
</div>
]]></field>
</record>
<record id="email_template_shift_summary" model="mail.template">
<field name="name">Shift Summary</field>
<field name="subject">Your next shift (${format_tz(object.start_time,object.worker_id.tz or 'Europe/Brussels','%d.%m.%Y - %H:%M')})</field>

12
beesdoo_shift/data/system_parameter.xml

@ -31,16 +31,4 @@
<field name="key">regular_counter_to_unsubscribe</field>
<field name="value">-4</field>
</record>
<record id="beesdoo_shift.card_support" model="ir.config_parameter">
<field name="key">beesdoo_shift.card_support</field>
<field name="value">False</field>
</record>
<record id="beesdoo_shift.attendance_sheet_generation_interval" model="ir.config_parameter">
<field name="key">beesdoo_shift.attendance_sheet_generation_interval</field>
<field name="value">15</field>
</record>
<record id="beesdoo_shift.pre_filled_task_type_id" model="ir.config_parameter">
<field name="key">beesdoo_shift.pre_filled_task_type_id</field>
<field name="value">1</field>
</record>
</odoo>

84
beesdoo_shift/demo/cooperators.xml

@ -0,0 +1,84 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2019 Coop IT Easy
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<odoo>
<record id="res_partner_cooperator_1_demo" model="res.partner">
<field name="name">Fernand Peso</field>
<field name="customer" eval="True"/>
<field name="is_company" eval="False"/>
<field name="email">fernand_peso@demo.net</field>
<field name="street">Avenue des Bas-de-Callanques, 15</field>
<field name="city">Etterbeek</field>
<field name="zip">1040</field>
<field name="country_id" ref="base.be"/>
<field name="working_mode">regular</field>
<field name="is_worker" eval="True" />
</record>
<record id="res_partner_cooperator_2_demo" model="res.partner">
<field name="name">Dupont Dupont</field>
<field name="customer" eval="True"/>
<field name="is_company" eval="False"/>
<field name="email">d_dupont@demo.net</field>
<field name="street">Rue des sables, 20</field>
<field name="city">Bruxelles</field>
<field name="zip">10000</field>
<field name="country_id" ref="base.be"/>
<field name="working_mode">irregular</field>
<field name="is_worker" eval="True" />
</record>
<record id="res_partner_cooperator_3_demo" model="res.partner">
<field name="name">Ronan Le Gall</field>
<field name="customer" eval="True"/>
<field name="is_company" eval="False"/>
<field name="email">ronan_gall@demo.net</field>
<field name="street">Rue des pecheurs, 23</field>
<field name="city">Landudec</field>
<field name="zip">29710</field>
<field name="country_id" ref="base.fr"/>
<field name="working_mode">regular</field>
<field name="is_worker" eval="True" />
</record>
<record id="res_partner_cooperator_4_demo" model="res.partner">
<field name="name">Elouan Bees</field>
<field name="customer" eval="True"/>
<field name="is_company" eval="False"/>
<field name="email">elouan_bees@demo.net</field>
<field name="street">Rue Wéry, 15</field>
<field name="city">Ixelles</field>
<field name="zip">1050</field>
<field name="country_id" ref="base.be"/>
<field name="working_mode">irregular</field>
<field name="is_worker" eval="True" />
</record>
<record id="res_partner_cooperator_5_demo" model="res.partner">
<field name="name">Anne de Marchalo</field>
<field name="customer" eval="True"/>
<field name="is_company" eval="False"/>
<field name="email">anne_marchalo@demo.net</field>
<field name="street">Rue du Wels, 6</field>
<field name="city">Nantes</field>
<field name="zip">44000</field>
<field name="country_id" ref="base.fr"/>
<field name="working_mode">regular</field>
<field name="is_worker" eval="True" />
</record>
<record id="res_partner_cooperator_6_demo" model="res.partner">
<field name="name">Jean Beaumont</field>
<field name="customer" eval="True"/>
<field name="is_company" eval="False"/>
<field name="email">jean_beaumont@demo.net</field>
<field name="street">Rue de la Jungle, 8</field>
<field name="city">St-Gilles</field>
<field name="zip">1060</field>
<field name="country_id" ref="base.be"/>
<field name="working_mode">regular</field>
<field name="is_worker" eval="True" />
</record>
</odoo>

6
beesdoo_shift/demo/templates.xml

@ -75,7 +75,7 @@
<field name="duration">2.5</field>
<field name="worker_nb">12</field>
<!-- WARNING: issue, worker not registered in demo -->
<field name="worker_ids" eval="[(6, 0, [ref('beesdoo_base.res_partner_cooperator_1_demo')])]"/>
<field name="worker_ids" eval="[(6, 0, [ref('beesdoo_shift.res_partner_cooperator_1_demo')])]"/>
</record>
<record id="beesdoo_shift_task_template_2_demo" model="beesdoo.shift.template">
@ -88,7 +88,7 @@
<field name="duration">2.5</field>
<field name="worker_nb">9</field>
<!-- WARNING: issue, worker not registered in demo -->
<field name="worker_ids" eval="[(6, 0, [ref('beesdoo_base.res_partner_cooperator_3_demo')])]"/>
<field name="worker_ids" eval="[(6, 0, [ref('beesdoo_shift.res_partner_cooperator_3_demo')])]"/>
</record>
<record id="beesdoo_shift_task_template_3_demo" model="beesdoo.shift.template">
@ -101,7 +101,7 @@
<field name="duration">2.5</field>
<field name="worker_nb">7</field>
<!-- WARNING: issue, worker not registered in demo -->
<field name="worker_ids" eval="[(6, 0, [ref('beesdoo_base.res_partner_cooperator_6_demo')])]"/>
<field name="worker_ids" eval="[(6, 0, [ref('beesdoo_shift.res_partner_cooperator_6_demo')])]"/>
</record>
</odoo>

89
beesdoo_shift/demo/workers.xml

@ -5,7 +5,7 @@
-->
<odoo>
<record id="beesdoo_shift_cooperative_status_1_demo" model="cooperative.status">
<field name="cooperator_id" ref="beesdoo_base.res_partner_cooperator_1_demo" />
<field name="cooperator_id" ref="res_partner_cooperator_1_demo" />
<field name="info_session" eval="True" />
<field name="info_session_date" eval="datetime.now() - timedelta(days=58)" />
<field name="super" eval="True" />
@ -14,7 +14,7 @@
</record>
<record id="beesdoo_shift_cooperative_status_2_demo" model="cooperative.status">
<field name="cooperator_id" ref="beesdoo_base.res_partner_cooperator_2_demo" />
<field name="cooperator_id" ref="res_partner_cooperator_2_demo" />
<field name="info_session" eval="False" />
<field name="sr">2</field>
<field name="working_mode">irregular</field>
@ -22,7 +22,7 @@
</record>
<record id="beesdoo_shift_cooperative_status_3_demo" model="cooperative.status">
<field name="cooperator_id" ref="beesdoo_base.res_partner_cooperator_3_demo" />
<field name="cooperator_id" ref="res_partner_cooperator_3_demo" />
<field name="info_session" eval="True" />
<field name="info_session_date" eval="datetime.now() - timedelta(days=98)" />
<field name="sc">2</field>
@ -30,101 +30,24 @@
</record>
<record id="beesdoo_shift_cooperative_status_4_demo" model="cooperative.status">
<field name="cooperator_id" ref="beesdoo_base.res_partner_cooperator_4_demo" />
<field name="cooperator_id" ref="res_partner_cooperator_4_demo" />
<field name="sr">2</field>
<field name="working_mode">irregular</field>
<field name="irregular_start_date" eval="datetime.now() - timedelta(days=6)" />
</record>
<record id="beesdoo_shift_cooperative_status_5_demo" model="cooperative.status">
<field name="cooperator_id" ref="beesdoo_base.res_partner_cooperator_5_demo" />
<field name="cooperator_id" ref="res_partner_cooperator_5_demo" />
<field name="sr">2</field>
<field name="working_mode">regular</field>
</record>
<record id="beesdoo_shift_cooperative_status_6_demo" model="cooperative.status">
<field name="cooperator_id" ref="beesdoo_base.res_partner_cooperator_6_demo" />
<field name="cooperator_id" ref="res_partner_cooperator_6_demo" />
<field name="info_session" eval="True" />
<field name="info_session_date" eval="datetime.now() - timedelta(days=36)" />
<field name="sc">2</field>
<field name="working_mode">regular</field>
</record>
<record id="beesdoo_base.res_partner_cooperator_1_demo" model="res.partner">
<field name="working_mode">regular</field>
<field name="cooperator_type">share_a</field>
</record>
<record id="beesdoo_base.res_partner_cooperator_2_demo" model="res.partner">
<field name="working_mode">irregular</field>
<field name="cooperator_type">share_a</field>
</record>
<record id="beesdoo_base.res_partner_cooperator_3_demo" model="res.partner">
<field name="working_mode">regular</field>
<field name="cooperator_type">share_a</field>
</record>
<record id="beesdoo_base.res_partner_cooperator_4_demo" model="res.partner">
<field name="working_mode">irregular</field>
<field name="cooperator_type">share_a</field>
</record>
<record id="beesdoo_base.res_partner_cooperator_5_demo" model="res.partner">
<field name="working_mode">regular</field>
<field name="cooperator_type">share_a</field>
</record>
<record id="beesdoo_base.res_partner_cooperator_6_demo" model="res.partner">
<field name="working_mode">regular</field>
<field name="cooperator_type">share_a</field>
</record>
<record id="member_card_1_demo" model="member.card">
<field name="barcode">421457731741</field>
<field name="comment">Demo data</field>
<field name="create_date" eval="datetime.now() - timedelta(days=15)" />
<field name="partner_id" ref="beesdoo_base.res_partner_cooperator_1_demo" />
<field name="valid" eval="True" />
</record>
<record id="member_card_2_demo" model="member.card">
<field name="barcode">429919251493</field>
<field name="comment">Demo data</field>
<field name="create_date" eval="datetime.now() - timedelta(days=10)" />
<field name="partner_id" ref="beesdoo_base.res_partner_cooperator_2_demo" />
<field name="valid" eval="True" />
</record>
<record id="member_card_3_demo" model="member.card">
<field name="barcode">421457731742</field>
<field name="comment">Demo data</field>
<field name="create_date" eval="datetime.now() - timedelta(days=15)" />
<field name="partner_id" ref="beesdoo_base.res_partner_cooperator_3_demo" />
<field name="valid" eval="True" />
</record>
<record id="member_card_4_demo" model="member.card">
<field name="barcode">421457731743</field>
<field name="comment">Demo data</field>
<field name="create_date" eval="datetime.now() - timedelta(days=15)" />
<field name="partner_id" ref="beesdoo_base.res_partner_cooperator_4_demo" />
<field name="valid" eval="True" />
</record>
<record id="member_card_5_demo" model="member.card">
<field name="barcode">421457731744</field>
<field name="comment">Demo data</field>
<field name="create_date" eval="datetime.now() - timedelta(days=15)" />
<field name="partner_id" ref="beesdoo_base.res_partner_cooperator_5_demo" />
<field name="valid" eval="True" />
</record>
<record id="member_card_6_demo" model="member.card">
<field name="barcode">421457731745</field>
<field name="comment">Demo data</field>
<field name="create_date" eval="datetime.now() - timedelta(days=15)" />
<field name="partner_id" ref="beesdoo_base.res_partner_cooperator_6_demo" />
<field name="valid" eval="True" />
</record>
</odoo>

2
beesdoo_shift/models/__init__.py

@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
from . import task
from . import attendance_sheet
from . import planning
from . import res_config_settings
from . import cooperative_status

400
beesdoo_shift/models/cooperative_status.py

@ -5,7 +5,6 @@ from datetime import timedelta, datetime
import logging
_logger = logging.getLogger(__name__)
PERIOD = 28 # TODO: use system parameter
def add_days_delta(date_from, days_delta):
if not date_from:
@ -33,16 +32,19 @@ class CooperativeStatus(models.Model):
_name = 'cooperative.status'
_rec_name = 'cooperator_id'
_order = 'cooperator_id'
def get_status_value(self):
"""
Workararound to get translated selection value instead of key in mail template.
"""
state_list = self.env["cooperative.status"]._fields["status"].selection
state_list = self.env["cooperative.status"]._fields['status']._description_selection(self.env)
return dict(state_list)[self.status]
_period = 28
def _get_status(self):
return [
('ok', 'Up to Date'),
('holiday', 'Holidays'),
('alert', 'Alerte'),
('extension', 'Extension'),
('suspended', 'Suspended'),
('exempted', 'Exempted'),
('unsubscribed', 'Unsubscribed'),
('resigning', 'Resigning')
]
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)
cooperator_id = fields.Many2one('res.partner')
@ -57,7 +59,6 @@ class CooperativeStatus(models.Model):
holiday_end_time = fields.Date("Holidays End Day")
alert_start_time = fields.Date("Alert Start Day")
extension_start_time = fields.Date("Extension Start Day")
#Champ compute
working_mode = fields.Selection(
[
('regular', 'Regular worker'),
@ -67,16 +68,9 @@ class CooperativeStatus(models.Model):
string="Working mode"
)
exempt_reason_id = fields.Many2one('cooperative.exempt.reason', 'Exempt Reason')
status = fields.Selection([('ok', 'Up to Date'),
('holiday', 'Holidays'),
('alert', 'Alert'),
('extension', 'Extension'),
('suspended', 'Suspended'),
('exempted', 'Exempted'),
('unsubscribed', 'Unsubscribed'),
('resigning', 'Resigning')],
status = fields.Selection(selection=_get_status,
compute="_compute_status", string="Cooperative Status", store=True)
can_shop = fields.Boolean(compute='_compute_status', store=True)
can_shop = fields.Boolean(compute='_compute_can_shop', store=True)
history_ids = fields.One2many('cooperative.status.history', 'status_id', readonly=True)
unsubscribed = fields.Boolean(default=False, help="Manually unsubscribed")
resigning = fields.Boolean(default=False, help="Want to leave the beescoop")
@ -92,6 +86,10 @@ class CooperativeStatus(models.Model):
temporary_exempt_start_date = fields.Date()
temporary_exempt_end_date = fields.Date()
@api.depends('status')
def _compute_can_shop(self):
for rec in self:
rec.can_shop = rec.status in self._can_shop_status()
@api.depends('today', 'sr', 'sc', 'holiday_end_time',
'holiday_start_time', 'time_extension',
@ -100,215 +98,32 @@ class CooperativeStatus(models.Model):
'irregular_absence_counter', 'temporary_exempt_start_date',
'temporary_exempt_end_date', 'resigning', 'cooperator_id.subscribed_shift_ids')
def _compute_status(self):
alert_delay = int(self.env['ir.config_parameter'].sudo().get_param('alert_delay', 28))
grace_delay = int(self.env['ir.config_parameter'].sudo().get_param('default_grace_delay', 10))
update = int(self.env['ir.config_parameter'].sudo().get_param('always_update', False))
for rec in self:
if update or not rec.today:
rec.status = 'ok'
rec.can_shop = True
continue
if rec.resigning:
rec.status = 'resigning'
rec.can_shop = False
continue
if rec.working_mode == 'regular':
rec._set_regular_status(grace_delay, alert_delay)
rec.status = rec._get_regular_status()
elif rec.working_mode == 'irregular':
rec._set_irregular_status(grace_delay, alert_delay)
rec.status = rec._get_irregular_status()
elif rec.working_mode == 'exempt':
rec.status = 'ok'
rec.can_shop = True
@api.depends('today', 'irregular_start_date', 'sr', 'holiday_start_time',
'holiday_end_time', 'temporary_exempt_start_date',
'temporary_exempt_end_date')
def _compute_future_alert_date(self):
"""Compute date before which the worker is up to date"""
for rec in self:
# Only for irregular worker
if rec.working_mode != 'irregular' and not rec.irregular_start_date:
rec.future_alert_date = False
# Alert start time already set
elif rec.alert_start_time:
rec.future_alert_date = False
# Holidays are not set properly
elif bool(rec.holiday_start_time) != bool(rec.holiday_end_time):
rec.future_alert_date = False
# Exemption have not a start and end time
elif (bool(rec.temporary_exempt_start_date)
!= bool(rec.temporary_exempt_end_date)):
rec.future_alert_date = False
else:
date = rec.today
counter = rec.sr
# Simulate the countdown
while counter > 0:
date = add_days_delta(date, 1)
date = self._next_countdown_date(rec.irregular_start_date,
date)
# Check holidays
if (rec.holiday_start_time and rec.holiday_end_time
and date >= rec.holiday_start_time
and date <= rec.holiday_end_time):
continue
# Check temporary exemption
elif (rec.temporary_exempt_start_date
and rec.temporary_exempt_end_date
and date >= rec.temporary_exempt_start_date
and date <= rec.temporary_exempt_end_date):
continue
else:
counter -= 1
rec.future_alert_date = self._next_countdown_date(
rec.irregular_start_date, date
)
@api.depends('today', 'irregular_start_date', 'holiday_start_time',
'holiday_end_time', 'temporary_exempt_start_date',
'temporary_exempt_end_date')
def _compute_next_countdown_date(self):
"""
Compute the following countdown date. This date is the date when
the worker will see his counter changed du to the cron. This
date is like the birthday date of the worker that occurred each
PERIOD.
"""
for rec in self:
# Only for irregular worker
if rec.working_mode != 'irregular' and not rec.irregular_start_date:
rec.next_countdown_date = False
# Holidays are not set properly
elif bool(rec.holiday_start_time) != bool(rec.holiday_end_time):
rec.next_countdown_date = False
# Exemption have not a start and end time
elif (bool(rec.temporary_exempt_start_date)
!= bool(rec.temporary_exempt_end_date)):
rec.next_countdown_date = False
else:
date = rec.today
next_countdown_date = False
while not next_countdown_date:
date = self._next_countdown_date(rec.irregular_start_date, date)
# Check holidays
if (rec.holiday_start_time and rec.holiday_end_time
and date >= rec.holiday_start_time
and date <= rec.holiday_end_time):
date = add_days_delta(date, 1)
continue
# Check temporary exemption
elif (rec.temporary_exempt_start_date
and rec.temporary_exempt_end_date
and date >= rec.temporary_exempt_start_date
and date <= rec.temporary_exempt_end_date):
date = add_days_delta(date, 1)
continue
else:
next_countdown_date = date
rec.next_countdown_date = next_countdown_date
_sql_constraints = [
('cooperator_uniq', 'unique (cooperator_id)', _('You can only set one cooperator status per cooperator')),
]
@api.constrains("working_mode", "irregular_start_date")
def _constrains_irregular_start_date(self):
if self.working_mode == "irregular" and not self.irregular_start_date:
raise UserError(_("Irregular workers must have an irregular start date."))
def _next_countdown_date(self, irregular_start_date, today=False):
"""
Return the next countdown date given irregular_start_date and
today dates.
This does not take holiday and other status into account.
"""
today = today or fields.Date.today()
delta = (today - irregular_start_date).days
if not delta % PERIOD:
return today
return add_days_delta(today, PERIOD - (delta % PERIOD))
def _set_regular_status(self, grace_delay, alert_delay):
self.ensure_one()
counter_unsubscribe = int(self.env['ir.config_parameter'].sudo().get_param('regular_counter_to_unsubscribe', -4))
ok = self.sr >= 0 and self.sc >= 0
grace_delay = grace_delay + self.time_extension
if (self.sr + self.sc) <= counter_unsubscribe or self.unsubscribed:
self.status = 'unsubscribed'
self.can_shop = False
#Check if exempted. Exempt end date is not required.
elif self.temporary_exempt_start_date and self.today >= self.temporary_exempt_start_date:
if not self.temporary_exempt_end_date or self.today <= self.temporary_exempt_end_date:
self.status = 'exempted'
self.can_shop = True
#Transition to alert sr < 0 or stay in alert sr < 0 or sc < 0 and thus alert time is defined
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):
self.status = 'extension'
self.can_shop = True
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):
self.status = 'suspended'
self.can_shop = False
elif not ok and self.alert_start_time and self.today > add_days_delta(self.alert_start_time, alert_delay):
self.status = 'suspended'
self.can_shop = False
elif (self.sr < 0) or (not ok and self.alert_start_time):
self.status = 'alert'
self.can_shop = True
#Check for holidays; Can be in holidays even in alert or other mode ?
elif (
self.holiday_start_time
and self.holiday_end_time
and self.today >= self.holiday_start_time
and self.today <= self.holiday_end_time
):
self.status = 'holiday'
self.can_shop = False
elif ok or (not self.alert_start_time and self.sr >= 0):
self.status = 'ok'
self.can_shop = True
def _set_irregular_status(self, grace_delay, alert_delay):
counter_unsubscribe = int(self.env['ir.config_parameter'].sudo().get_param('irregular_counter_to_unsubscribe', -3))
self.ensure_one()
ok = self.sr >= 0
grace_delay = grace_delay + self.time_extension
if self.sr <= counter_unsubscribe or self.unsubscribed:
self.status = 'unsubscribed'
self.can_shop = False
#Check if exempted. Exempt end date is not required.
elif self.temporary_exempt_start_date and self.today >= self.temporary_exempt_start_date:
if not self.temporary_exempt_end_date or self.today <= self.temporary_exempt_end_date:
self.status = 'exempted'
self.can_shop = True
#Transition to alert sr < 0 or stay in alert sr < 0 or sc < 0 and thus alert time is defined
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):
self.status = 'extension'
self.can_shop = True
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):
self.status = 'suspended'
self.can_shop = False
elif not ok and self.alert_start_time and self.today > add_days_delta(self.alert_start_time, alert_delay):
self.status = 'suspended'
self.can_shop = False
elif (self.sr < 0) or (not ok and self.alert_start_time):
self.status = 'alert'
self.can_shop = True
#Check for holidays; Can be in holidays even in alert or other mode ?
elif (
self.holiday_start_time
and self.holiday_end_time
and self.today >= self.holiday_start_time
and self.today <= self.holiday_end_time
):
self.status = 'holiday'
self.can_shop = False
elif ok or (not self.alert_start_time and self.sr >= 0):
self.status = 'ok'
self.can_shop = True
@api.multi
def write(self, vals):
"""
@ -329,34 +144,6 @@ class CooperativeStatus(models.Model):
self.env['cooperative.status.history'].sudo().create(data)
return super(CooperativeStatus, self).write(vals)
def _state_change(self, new_state):
self.ensure_one()
if new_state == 'alert':
self.write({'alert_start_time': self.today, 'extension_start_time': False, 'time_extension': 0})
if new_state == 'ok':
data = {'extension_start_time': False, 'time_extension': 0}
data['alert_start_time'] = False
self.write(data)
if new_state == 'unsubscribed' or new_state == 'resigning':
# Remove worker from task_templates
self.cooperator_id.sudo().write(
{'subscribed_shift_ids': [(5, 0, 0)]})
# Remove worker from supercoop in task_templates
task_tpls = self.env['beesdoo.shift.template'].search(
[('super_coop_id', 'in', self.cooperator_id.user_ids.ids)]
)
task_tpls.write({'super_coop_id': False})
# Remove worker for future tasks (remove also supercoop)
self.env['beesdoo.shift.shift'].sudo().unsubscribe_from_today(
[self.cooperator_id.id], now=fields.Datetime.now()
)
def _change_counter(self, data):
self.sc += data.get('sc', 0)
self.sr += data.get('sr', 0)
self.irregular_absence_counter += data.get('irregular_absence_counter', 0)
self.irregular_absence_date = data.get('irregular_absence_date', False)
@api.multi
def _write(self, vals):
"""
@ -380,9 +167,12 @@ class CooperativeStatus(models.Model):
rec._state_change(vals['status'])
return super(CooperativeStatus, self)._write(vals)
_sql_constraints = [
('cooperator_uniq', 'unique (cooperator_id)', _('You can only set one cooperator status per cooperator')),
]
def get_status_value(self):
"""
Workararound to get translated selection value instead of key in mail template.
"""
state_list = self.env["cooperative.status"]._fields['status']._description_selection(self.env)
return dict(state_list)[self.status]
@api.model
def _set_today(self):
@ -391,40 +181,119 @@ class CooperativeStatus(models.Model):
"""
self.search([]).write({'today': fields.Date.today()})
@api.multi
def clear_history(self):
self.ensure_one()
self.history_ids.unlink()
@api.model
def _cron_compute_counter_irregular(self, today=False):
"""
Journal ensure that a irregular worker will be only check
once per day
"""
today = today or fields.Date.today()
journal = self.env['beesdoo.shift.journal'].search([('date', '=', today)])
if not journal:
journal = self.env['beesdoo.shift.journal'].create({'date': today})
domain = ['&',
'&',
'&', ('status', '!=', 'unsubscribed'),
('working_mode', '=', 'irregular'),
('irregular_start_date', '!=', False),
'|',
'|', ('holiday_start_time', '=', False), ('holiday_end_time', '=', False),
'|', ('holiday_start_time', '>', today), ('holiday_end_time', '<', today),
]
domain = self._get_irregular_worker_domain()
irregular = self.search(domain)
for status in irregular:
if status.status == 'exempted':
continue
delta = (today - status.irregular_start_date).days
if delta and delta % PERIOD == 0 and status not in journal.line_ids:
if status.sr > 0:
status.sr -= 1
elif status.alert_start_time:
status.sr -= 1
else:
status.sr -= 2
if delta and delta % self._period == 0 and status not in journal.line_ids:
status._change_irregular_counter()
journal.line_ids |= status
@api.multi
def clear_history(self):
self.ensure_one()
self.history_ids.unlink()
########################################################
# Method to override #
# To define the behavior of the status #
# #
# By default: everyone is always up to date #
########################################################
##############################
# Computed field section #
##############################
@api.depends('today')
def _compute_future_alert_date(self):
"""
Compute date until the worker is up to date
for irregular worker
"""
for rec in self:
rec.future_alert_date = False
@api.depends('today')
def _compute_next_countdown_date(self):
"""
Compute the following countdown date. This date is the date when
the worker will see his counter changed due to the cron. This
date is like the birthday date of the worker that occurred each
_period.
"""
for rec in self:
rec.next_countdown_date = False
def _can_shop_status(self):
"""
return the list of status that give access
to active cooperator privilege
"""
return ['ok', 'alert', 'extension', 'exempted']
#####################################
# Status Change implementation #
#####################################
def _get_regular_status(self):
"""
Return the value of the status
for the regular worker
"""
return 'ok'
def _get_irregular_status(self):
"""
Return the value of the status
for the irregular worker
"""
return 'ok'
def _state_change(self, new_state):
"""
Hook to watch change in the state
"""
pass
def _change_counter(self, data):
"""
Call when a shift state is changed
use data generated by _get_counter_date_state_change
"""
pass
###############################################
###### Irregular Cron implementation ##########
###############################################
def _get_irregular_worker_domain(self):
"""
return the domain the give the list
of valid irregular worker that should
get their counter changed by the cron
"""
return [(0, '=', 1)]
def _change_irregular_counter(self):
"""
Define how the counter will change
for the irregular worker
where today - start_date is a multiple of the period
by default 28 days
"""
pass
class ShiftCronJournal(models.Model):
_name = 'beesdoo.shift.journal'
@ -452,6 +321,8 @@ class ResPartner(models.Model):
"""
_inherit = 'res.partner'
worker_store = fields.Boolean(default=False)
is_worker = fields.Boolean(related="worker_store", string="Worker", readonly=False)
cooperative_status_ids = fields.One2many('cooperative.status', 'cooperator_id', readonly=True)
super = fields.Boolean(related='cooperative_status_ids.super', string="Super Cooperative", readonly=True, store=True)
info_session = fields.Boolean(related='cooperative_status_ids.info_session', string='Information Session ?', readonly=True, store=True)
@ -520,4 +391,3 @@ class ResPartner(models.Model):
}
#TODO access right + vue on res.partner
#TODO can_shop : Status can_shop ou extempted ou part C

2
beesdoo_shift/models/planning.py

@ -89,7 +89,7 @@ class TaskTemplate(models.Model):
duration = fields.Float(help="Duration in Hour")
worker_nb = fields.Integer(string="Number of worker", help="Max number of worker for this task", default=1)
worker_ids = fields.Many2many('res.partner', string="Recurrent worker assigned", domain=[('eater', '=', 'worker_eater'), ('working_mode', '=', 'regular')])
worker_ids = fields.Many2many('res.partner', string="Recurrent worker assigned", domain=[('is_worker', '=', True)])
remaining_worker = fields.Integer(compute="_get_remaining", store=True, string="Remaining Place")
active = fields.Boolean(default=True)
#For Kanban View Only

121
beesdoo_shift/models/task.py

@ -13,31 +13,50 @@ class Task(models.Model):
_order = "start_time asc"
##################################
# Method to override #
# to have different state #
# on the shift #
##################################
def _get_selection_status(self):
return [
("open","Confirmed"),
("done","Attended"),
("absent","Absent"),
("excused","Excused"),
("cancel","Cancelled")
]
def _get_color_mapping(state):
return {
"draft": 0,
"open": 1,
"done": 5,
"absent": 2,
"excused": 3,
"cancel": 9,
}[state]
def _get_final_state():
return ["done", "absent", "excused"]
name = fields.Char(track_visibility='always')
task_template_id = fields.Many2one('beesdoo.shift.template')
planning_id = fields.Many2one(related='task_template_id.planning_id', store=True)
task_type_id = fields.Many2one('beesdoo.shift.type', string="Task Type")
worker_id = fields.Many2one('res.partner', track_visibility='onchange',
domain=[
('eater', '=', 'worker_eater'),
('is_worker', '=', True),
('working_mode', 'in', ('regular', 'irregular')),
('state', 'not in', ('unsubscribed', 'resigning')),
])
start_time = fields.Datetime(track_visibility='always', index=True, required=True)
end_time = fields.Datetime(track_visibility='always', required=True)
state = fields.Selection(selection=[
("draft","Unconfirmed"),
("open","Confirmed"),
("done","Attended"),
("absent_2","Absent - 2 compensations"),
("absent_1","Absent - 1 compensation"),
("absent_0","Absent - 0 compensation"),
("cancel","Cancelled")
],
state = fields.Selection(selection=_get_selection_status,
default="open",
required=True,
store=True,
track_visibility='onchange',
group_expand='_expand_states'
)
color = fields.Integer(compute="_compute_color")
super_coop_id = fields.Many2one('res.users', string="Super Cooperative", domain=[('partner_id.super', '=', True)], track_visibility='onchange')
@ -52,19 +71,14 @@ class Task(models.Model):
revert_info = fields.Text(copy=False)
working_mode = fields.Selection(related='worker_id.working_mode')
def _expand_states(self, states, domain, order):
return [key for key, val in self._fields['state'].selection]
@api.depends("state")
def _compute_color(self):
color_mapping = {
"draft": 0,
"open": 0,
"done": 10,
"absent_2": 1,
"absent_1": 2,
"absent_0": 3,
"cancel": 5,
}
for rec in self:
rec.color = color_mapping[rec.state]
rec.color = self._state_color_mapping(rec.state)
def _compensation_validation(self, task):
"""
@ -81,7 +95,7 @@ class Task(models.Model):
@api.constrains("state")
def _lock_future_task(self):
if datetime.now() < self.start_time:
if self.state in ["done", "absent_2", "absent_1", "absent_0"]:
if self.state in self._get_final_state():
raise UserError(_(
"Shift state of a future shift "
"can't be set to 'present' or 'absent'."
@ -213,53 +227,19 @@ class Task(models.Model):
def _update_state(self, new_state):
self.ensure_one()
self._revert()
update = int(self.env['ir.config_parameter'].sudo().get_param('always_update', False))
data = {}
if not (self.worker_id or self.replaced_id) and new_state in ("done", "absent_0", "absent_1", "absent_2"):
if not (self.worker_id or self.replaced_id) and new_state in self._get_final_state():
raise UserError(_("You cannot change to the status %s if no worker is defined for the shift") % new_state)
if not (self.worker_id.working_mode in ['regular', 'irregular']):
raise UserError(_("Working mode is not properly defined. Please check if the worker is subscribed"))
if update or not (self.worker_id or self.replaced_id):
always_update = int(self.env['ir.config_parameter'].sudo().get_param('always_update', False))
if always_update or not (self.worker_id or self.replaced_id):
return
if self.worker_id.working_mode == 'regular':
if not self.replaced_id: #No replacement case
status = self.worker_id.cooperative_status_ids[0]
else:
status = self.replaced_id.cooperative_status_ids[0]
if new_state == "done" and not self.is_regular:
# Regular counter is always updated first
if status.sr < 0:
data['sr'] = 1
elif status.sc < 0:
data['sc'] = 1
# Bonus shift case
else:
data['sr'] = 1
if new_state == "absent_2":
data['sr'] = -1
data['sc'] = -1
if new_state == "absent_1":
data['sr'] = -1
elif self.worker_id.working_mode == 'irregular':
status = self.worker_id.cooperative_status_ids[0]
if new_state == "done" or new_state == "absent_0":
data['sr'] = 1
data['irregular_absence_date'] = False
data['irregular_absence_counter'] = 1 if status.irregular_absence_counter < 0 else 0
if new_state == "absent_2" or new_state == "absent_1":
if new_state == "absent_2":
data['sr'] = -1
data['irregular_absence_date'] = self.start_time.date()
data['irregular_absence_counter'] = -1
else:
raise UserError(_("Working mode is not properly defined. Please check if the worker is subscribed"))
data = self._get_counter_date_state_change(new_state)
status.sudo()._change_counter(data)
self._set_revert_info(data, status)
@ -288,3 +268,20 @@ class Task(models.Model):
for rec in confirmed_tasks:
shift_summary_mail_template.send_mail(rec.id, True)
########################################################
# Method to override #
# To define the behavior of the status #
# #
# By default: everyone is always up to date #
########################################################
def _get_counter_date_state_change(self, new_state):
"""
Return the data to change counter or other things
that change on the cooperator status
see _change_counter
We have eheck the worker is legitimate
"""
return {}

19
beesdoo_shift/security/group.xml

@ -1,14 +1,4 @@
<odoo>
<record id="group_shift_attendance_sheet" model="res.groups">
<field name="name">Attendance Sheet Generic Access</field>
<field name="category_id"
ref="base.module_category_cooperative_management"/>
</record>
<record id="group_shift_attendance_sheet_validation" model="res.groups">
<field name="name">Attendance Sheet Validation</field>
<field name="category_id" ref="base.module_category_cooperative_management"/>
<field name="implied_ids" eval="[(4, ref('group_shift_attendance_sheet'))]"/>
</record>
<record id="group_shift_attendance" model="res.groups">
<field name="name">Shift and Worker Read Access</field>
<field name="category_id" ref="base.module_category_cooperative_management"/>
@ -28,11 +18,10 @@
</record>
<record id="group_cooperative_admin" model="res.groups">
<field name="name">Cooperative Admin</field>
<field name="category_id" ref="base.module_category_cooperative_management"/>
<field name="implied_ids" eval="[(6, 0, [ref('group_planning_management'),
ref('group_shift_attendance_sheet_validation'),
ref('group_shift_attendance_sheet'),])]"
/>
<field name="category_id"
ref="base.module_category_cooperative_management"/>
<field name="implied_ids"
eval="[(4, ref('group_planning_management'))]" />
<field name="users"
eval="[(4, ref('base.user_root')),
(4, ref('base.user_admin'))]"/>

15
beesdoo_shift/security/ir.model.access.csv

@ -1,14 +1,4 @@
id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink
create_beesdoo_shift_shift,create_edit_beesdoo_shift_shift,model_beesdoo_shift_shift,group_shift_attendance_sheet,1,1,1,0
read_beesdoo_shift_sheet_shift,read_beesdoo_shift_sheet_shift,model_beesdoo_shift_sheet_shift,group_shift_attendance_sheet,1,0,0,0
create_beesdoo_shift_sheet_shift,create_beesdoo_shift_sheet_shift,model_beesdoo_shift_sheet_shift,group_shift_attendance_sheet,1,1,1,0
create_beesdoo_shift_sheet_expected,create_beesdoo_shift_sheet_expected,model_beesdoo_shift_sheet_expected,group_shift_attendance_sheet,1,1,1,0
manage_beesdoo_shift_sheet_added,manage_beesdoo_shift_sheet_added,model_beesdoo_shift_sheet_added,group_shift_attendance_sheet,1,1,1,1
create_beesdoo_shift_sheet,create_beesdoo_shift_sheet,model_beesdoo_shift_sheet,group_shift_attendance_sheet,1,1,1,0
sheet_access_beesdoo_shift_template,sheet_access_beesdoo_shift_template,model_beesdoo_shift_template,group_shift_attendance_sheet,1,0,0,0
sheet_access_beesdoo_shift_type,sheet_access_beesdoo_shift_type,model_beesdoo_shift_type,group_shift_attendance_sheet,1,0,0,0
access_beesdoo_shift_daynumber,access_beesdoo_shift_daynumber,model_beesdoo_shift_daynumber,group_shift_attendance_sheet,1,0,0,0
read_beesdoo_shift_planning,read_beesdoo_shift_planning,model_beesdoo_shift_planning,,1,0,0,0
access_beesdoo_shift_template,access_beesdoo_shift_template,model_beesdoo_shift_template,group_shift_attendance,1,0,0,0
write_beesdoo_shift_shift,write_beesdoo_shift_shift,model_beesdoo_shift_shift,group_shift_attendance,1,1,0,0
@ -17,8 +7,6 @@ manage_beesdoo_shift_type,manage_beesdoo_shift_type,model_beesdoo_shift_type,gro
manage_beesdoo_shift_daynumber,manage_beesdoo_shift_daynumber,model_beesdoo_shift_daynumber,group_planning_management,1,1,1,1
manage_beesdoo_shift_planning,manage_beesdoo_shift_planning,model_beesdoo_shift_planning,group_planning_management,1,1,1,1
manage_beesdoo_shift_template,manage_beesdoo_shift_template,model_beesdoo_shift_template,group_planning_management,1,1,1,1
manage_beesdoo_shift_sheet_shift,beesdoo_shift_sheet_shift,model_beesdoo_shift_sheet_shift,group_shift_attendance,1,1,1,1
manage_beesdoo_shift_sheet_expected,manage_beesdoo_shift_sheet_expected,model_beesdoo_shift_sheet_expected,group_shift_attendance,1,1,1,1
manage_cooperative_status,manage_cooperative_status,model_cooperative_status,group_cooperative_admin,1,1,1,1
manage_cooperative_exempt_reason,manage_cooperative_exempt_reason,model_cooperative_exempt_reason,group_cooperative_admin,1,1,1,1
read_beesdoo_shift_journal,read_beesdoo_shift_journal,model_beesdoo_shift_journal,group_cooperative_admin,1,0,1,1
@ -26,5 +14,4 @@ access_cooperative_status,access_cooperative_status,model_cooperative_status,,1,
read_cooperative_exempt_reason,read_cooperative_exempt_reason,model_cooperative_exempt_reason,,1,0,0,0
read_cooperative_status_history,read_cooperative_status_history,model_cooperative_status_history,,1,0,0,0
access_beesdoo_shift_type,access_beesdoo_shift_type,model_beesdoo_shift_type,group_shift_attendance,1,0,0,0
access_beesdoo_shift_type,access_beesdoo_shift_type,model_beesdoo_shift_daynumber,group_shift_attendance,1,0,0,0
access_beesdoo_shift_template,access_beesdoo_shift_template,model_beesdoo_shift_template,group_shift_attendance,1,0,0,0
access_attendance_beesdoo_shift_daynumber,access_beesdoo_shift_daynumber,model_beesdoo_shift_daynumber,group_shift_attendance,1,0,0,0

56
beesdoo_shift/views/cooperative_status.xml

@ -2,57 +2,65 @@
<record model="ir.ui.view" id="super_coop_partner_inherited_view_form">
<field name="name">Partner Super Coop</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="beesdoo_base.beesdoo_partner_form_view" />
<field name="inherit_id" ref="base.view_partner_form"/>
<field name="priority">50</field>
<field name="arch" type="xml">
<xpath expr="//sheet" position="before">
<header>
<button name="coop_subscribe" string="Subscribe to shift" class="oe_highlight"
type="object" groups="beesdoo_shift.group_shift_management"
attrs="{'invisible': [('cooperator_type', '!=', 'share_a')]}"/>
attrs="{'invisible': [('is_worker', '=', False)]}"/>
<button name="auto_extension" string="Auto Extension" class="oe_highlight"
type="object" groups="beesdoo_shift.group_shift_attendance"
attrs="{'invisible': ['|',
('cooperator_type', '!=', 'share_a'),
('is_worker', '=', False),
'|',
('state', '!=', 'suspended'),
('extension_start_time', '!=', False)]}" />
<button name="manual_extension" string="Manual Extension" class="oe_highlight"
type="object" groups="beesdoo_shift.group_shift_management"
attrs="{'invisible': ['|',
('cooperator_type', '!=', 'share_a'),
('is_worker', '=', False),
'|',
('state', '!=', 'suspended'),
('extension_start_time', '=', False)]}" />
<button name="coop_unsubscribe" string="Unsubscribe" class="oe_highlight"
type="object" groups="beesdoo_shift.group_shift_management"
attrs="{'invisible': [('cooperator_type', '!=', 'share_a')]}"/>
attrs="{'invisible': [('is_worker', '=', False)]}"/>
<button name="register_holiday" string="Register Holidays" class="oe_highlight"
type="object" groups="beesdoo_shift.group_shift_management"
attrs="{'invisible': ['|', ('cooperator_type', '!=', 'share_a'), ('state', '!=', 'ok')]}"/>
attrs="{'invisible': ['|', ('is_worker', '=', False), ('state', '!=', 'ok')]}"/>
<button name="temporary_exempt" string="Temporary Exemption"
type="object" groups="beesdoo_shift.group_shift_management"
attrs="{'invisible': ['|', ('cooperator_type', '!=', 'share_a'), ('state', '=', 'unsubscribed')]}"/>
<field name="state" widget="statusbar" attrs="{'invisible': [('cooperator_type', '!=', 'share_a')]}" />
attrs="{'invisible': ['|', ('is_worker', '=', False), ('state', '=', 'unsubscribed')]}"/>
<field name="state" widget="statusbar" attrs="{'invisible': [('is_worker', '=', False)]}" />
</header>
</xpath>
<page name="work" position="inside">
<group>
<group>
<field name="info_session" />
<field name="info_session_date"
attrs="{'invisible': ['|', ('info_session', '=', False)]}" />
<field name="extension_start_time" attrs="{'invisible': [('extension_start_time', '=', False)]}" />
</group>
<xpath expr="//field[@name='type']" position="before">
<field name="is_worker"/>
</xpath>
<xpath expr="//notebook" position="inside">
<page string="Worker information"
attrs="{'invisible': [('is_worker', '=', False)]}"
name="work">
<group name="info_session"/>
<group>
<field name="working_mode" />
<field name="exempt_reason_id" attrs="{'invisible':[('working_mode', '!=', 'exempt')]}"/>
<field name="super" />
<group>
<field name="info_session" />
<field name="info_session_date"
attrs="{'invisible': ['|', ('info_session', '=', False)]}" />
<field name="extension_start_time" attrs="{'invisible': [('extension_start_time', '=', False)]}" />
</group>
<group>
<field name="working_mode" />
<field name="exempt_reason_id" attrs="{'invisible':[('working_mode', '!=', 'exempt')]}"/>
<field name="super" />
</group>
</group>
</group>
<separator string="Subscribed Shift" />
<field name="subscribed_shift_ids" />
</page>
<separator string="Subscribed Shift" />
<field name="subscribed_shift_ids" />
</page>
</xpath>
</field>
</record>
@ -175,7 +183,7 @@
<field name="name">Worker</field>
<field name="res_model">res.partner</field>
<field name="view_mode">kanban,tree,form</field>
<field name="domain">[('cooperator_type', '=', 'share_a')]</field>
<field name="domain">[('is_worker', '=', True)]</field>
</record>
<menuitem name="Worker" id="menu_worker_top" parent="menu_root"

2
beesdoo_shift/wizard/__init__.py

@ -5,5 +5,3 @@ from . import subscribe
from . import extension
from . import holiday
from . import temporary_exemption
from . import validate_attendance_sheet
from . import generate_missing_attendance_sheets

2
beesdoo_shift_atttendance/__init__.py

@ -0,0 +1,2 @@
from . import models
from . import wizard

36
beesdoo_shift_atttendance/__manifest__.py

@ -0,0 +1,36 @@
{
'name': "Beescoop Shift Attendance Sheet",
'summary': """
Volonteer Timetable Management
Attendance Sheet""",
'description': """
""",
'author': "Elouan Le Bars, Coop It Easy",
'website': "https://github.com/beescoop/Obeesdoo",
'category': 'Cooperative management',
'version': '12.0.1.0.0',
'depends': [
'beesdoo_shift',
'beesdoo_worker_status', #TODO move the part that require beesdoo_worker_status in beesdoo_worker status or another module
'mail',
'barcodes',
],
'data': [
"data/system_parameter.xml",
"data/cron.xml",
"data/mail_template.xml",
"security/group.xml",
"security/ir.model.access.csv",
"views/res_config_settings_view.xml",
"wizard/validate_attendance_sheet.xml",
"views/attendance_sheet.xml",
],
'demo': []
}

29
beesdoo_shift_atttendance/data/cron.xml

@ -0,0 +1,29 @@
<odoo>
<data noupdate="1">
<record id="ir_cron_generate_attendance_sheet" model="ir.cron">
<field name="name">Generate Attendance Sheets</field>
<field name="model_id" ref="model_beesdoo_shift_sheet" />
<field name="state">code</field>
<field name="code">model._generate_attendance_sheet()</field>
<field name="user_id" ref="base.user_root" />
<field name="interval_number">4</field>
<field name="interval_type">minutes</field>
<field name="numbercall">-1</field>
<field name="doall" eval="False" />
<field name="active" eval="False" />
</record>
<record id="ir_cron_check_non_validated_sheet" model="ir.cron">
<field name="name">Check for non-validated sheets</field>
<field name="model_id" ref="model_beesdoo_shift_sheet" />
<field name="state">code</field>
<field name="code">model._cron_non_validated_sheets()</field>
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="numbercall">-1</field>
<field name="doall" eval="False" />
<field name="nextcall" eval="datetime.now().replace(hour=00, minute=00, second=10)" />
<field name="active" eval="False" />
</record>
</data>
</odoo>

117
beesdoo_shift_atttendance/data/mail_template.xml

@ -0,0 +1,117 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Mail template are declared in a NOUPDATE block
so users can freely customize/delete them -->
<data noupdate="1">
<record id="email_template_non_attendance" model="mail.template">
<field name="name">Shift Non-attendance</field>
<field name="subject">Non-attendance to your last shift.</field>
<field name="partner_to">${object.replaced_id.id or object.worker_id.id|safe}</field>
<field name="model_id" ref="model_beesdoo_shift_shift"/>
<field name="auto_delete" eval="True"/>
<field name="lang">${object.worker_id.lang}</field>
<field name="body_html"><![CDATA[
<div style="font-family: 'Lucica Grande', Ubuntu, Arial, Verdana, sans-serif; font-size: 12px; color: rgb(34, 34, 34); background-color: #FFF; ">
% if object.replaced_id:
<p>Hello ${object.replaced_id.name},
<br><br>You have been recorded as non-attended during your last shift (${format_tz(object.start_time,object.replaced_id.tz or 'Europe/Brussels','%d.%m.%Y - %H:%M')}),
and you were supposed to replace ${object.worker_id.name}.
% endif
% if not object.replaced_id:
</p><p>Hello ${object.worker_id.name},</p>
<p>You have been recorded as non-attended during your last shift (${format_tz(object.start_time,object.worker_id.tz or 'Europe/Brussels','%d.%m.%Y - %H:%M')}).
% endif
% if object.worker_id.working_mode == 'regular':
% if object.state == 'absent_0':
<br><br>Super-cooperator assigned you 0 compensation, so you won't have any additionnal shift to do before your next regular shift.
% endif
% if object.state == 'absent_1':
<br><br>Super-cooperator assigned you 1 compensation, so you have to attend one additionnal shift before your next regular shift.
% endif
% if object.state == 'absent_2':
<br><br>Super-cooperator assigned you 2 compensations, so you have to attend two additionnal shifts before your next regular shift.
% endif
% if object.replaced_id:
You were supposed to replace ${object.worker_id.name}.
You have to do ${(object.replaced_id.cooperative_status_ids.sr + object.replaced_id.cooperative_status_ids.sc) * -1 } shifts before your next regular shift.<br>
% else:
You have to do ${(object.worker_id.cooperative_status_ids.sr + object.worker_id.cooperative_status_ids.sc) * -1 } shifts before your next regular shift.<br>
% endif
% endif
% if object.worker_id.working_mode == 'irregular':
Your shift counter is at ${object.worker_id.cooperative_status_ids.sr}.
% if object.worker_id.cooperative_status_ids.future_alert_date:
It should be superior or equal to 1 before the
${object.worker_id.cooperative_status_ids.future_alert_date}.
% endif
<br>
% endif
% if object.replaced_id:
Your current status is "${object.replaced_id.cooperative_status_ids.get_status_value()}".
% else:
<br><br>Your current status is "${object.worker_id.cooperative_status_ids.get_status_value()}".
% endif
<br>If you have any question regarding this non-attendance, just answer this e-mail.
</p>
<br>
<p>Cooperatively yours,<br>
The Members' office volunteers</p>
<p>${object.worker_id.company_id.name}.</p>
% if object.worker_id.company_id.street:
${object.worker_id.company_id.street}
% endif
% if object.worker_id.company_id.street2:
${object.worker_id.company_id.street2}<br>
% endif
% if object.worker_id.company_id.city or object.worker_id.company_id.zip:
${object.worker_id.company_id.zip} ${object.worker_id.company_id.city}<br>
% endif
% if object.worker_id.company_id.country_id:
${object.worker_id.company_id.state_id and ('%s, ' % object.worker_id.company_id.state_id.name) or ''} ${object.worker_id.company_id.country_id.name or ''}<br>
% endif
% if object.worker_id.company_id.phone:
Phone:&nbsp; ${object.worker_id.company_id.phone}
% endif
% if object.worker_id.company_id.website:
<div>
Web :&nbsp;<a href="${object.worker_id.company_id.website}">${object.worker_id.company_id.website}</a>
</div>
%endif
% if object.worker_id.company_id.logo_url:
<div>
<img src="${object.worker_id.company_id.logo_url}">
</div>
%endif
</div>
]]></field>
</record>
<record id="email_template_non_validated_sheet" model="mail.template">
<field name="name">Non-validated sheet</field>
<field name="subject">[${object.day}] Non-validated sheet ${object.time_slot}</field>
<field name="model_id" ref="model_beesdoo_shift_sheet"/>
<field name="auto_delete" eval="True"/>
<field name="body_html"><![CDATA[
<div style="font-family: 'Lucica Grande', Ubuntu, Arial, Verdana, sans-serif; font-size: 12px; color: rgb(34, 34, 34); background-color: #FFF; ">
<p>${object.day}
<br/><br/>The attendance sheet for ${object.time_slot} is not validated.
<br/><br/>Please, do it as soon as possible so as to update workers' status.
</p>
</div>
]]></field>
</record>
</data>
</odoo>

14
beesdoo_shift_atttendance/data/system_parameter.xml

@ -0,0 +1,14 @@
<odoo noupdate="1">
<record id="beesdoo_shift.card_support" model="ir.config_parameter">
<field name="key">beesdoo_shift.card_support</field>
<field name="value">False</field>
</record>
<record id="beesdoo_shift.attendance_sheet_generation_interval" model="ir.config_parameter">
<field name="key">beesdoo_shift.attendance_sheet_generation_interval</field>
<field name="value">15</field>
</record>
<record id="beesdoo_shift.default_task_type_id" model="ir.config_parameter">
<field name="key">beesdoo_shift.default_task_type_id</field>
<field name="value">1</field>
</record>
</odoo>

2
beesdoo_shift_atttendance/models/__init__.py

@ -0,0 +1,2 @@
from . import attendance_sheet
from . import res_config_settings

4
beesdoo_shift/models/attendance_sheet.py → beesdoo_shift_atttendance/models/attendance_sheet.py

@ -30,7 +30,7 @@ class AttendanceSheetShift(models.Model):
)
)
task_types = self.env["beesdoo.shift.type"]
return task_types.browse(id)
return task_types.browse(default_task_type_id)
# Related actual shift
task_id = fields.Many2one("beesdoo.shift.shift", string="Task")
@ -54,7 +54,7 @@ class AttendanceSheetShift(models.Model):
"res.partner",
string="Worker",
domain=[
("eater", "=", "worker_eater"),
("is_worker", "=", True),
("working_mode", "in", ("regular", "irregular")),
("state", "not in", ("unsubscribed", "resigning")),
],

61
beesdoo_shift_atttendance/models/res_config_settings.py

@ -0,0 +1,61 @@
# Copyright 2019-2020 Elouan Le Bars <elouan@coopiteasy.be>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import ast
from odoo import fields, models, api
class ResConfigSettings(models.TransientModel):
_inherit = "res.config.settings"
card_support = fields.Boolean(
string="Scan cooperators cards instead of login for sheets validation",
config_parameter="beesdoo_shift.card_support",
)
task_type_default_id = fields.Many2one(
"beesdoo.shift.type",
string="Default Task Type",
help="Default task type for attendance sheet pre-filling",
required=True,
default=False,
)
attendance_sheet_generation_interval = fields.Integer(
string="Time interval for attendance sheet generation",
help="Time interval expressed in minutes",
required=True,
config_parameter="beesdoo_shift.attendance_sheet_generation_interval",
)
@api.multi
def set_values(self):
super(ResConfigSettings, self).set_values()
parameters = self.env["ir.config_parameter"].sudo()
parameters.set_param(
"beesdoo_shift.card_support", str(self.card_support),
)
parameters.set_param(
"beesdoo_shift.task_type_default_id",
str(self.task_type_default_id.id),
)
parameters.set_param(
"beesdoo_shift.attendance_sheet_generation_interval",
str(self.attendance_sheet_generation_interval),
)
@api.multi
def get_values(self):
res = super(ResConfigSettings, self).get_values()
res.update(
card_support=ast.literal_eval(
self.env["ir.config_parameter"].get_param(
"beesdoo_shift.card_support"
),
),
task_type_default_id=int(
self.env["ir.config_parameter"].get_param(
"beesdoo_shift.task_type_default_id"
)
),
)
return res

11
beesdoo_shift_atttendance/security/group.xml

@ -0,0 +1,11 @@
<odoo>
<record id="group_shift_attendance_sheet" model="res.groups">
<field name="name">Attendance Sheet Generic Access</field>
<field name="category_id"
ref="base.module_category_cooperative_management"/>
</record>
<record id="beesdoo_shift.group_shift_attendance" model="res.groups">
<field name="implied_ids"
eval="[(4, ref('group_shift_attendance_sheet'))]"/>
</record>
</odoo>

12
beesdoo_shift_atttendance/security/ir.model.access.csv

@ -0,0 +1,12 @@
id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink
create_beesdoo_shift_shift,create_edit_beesdoo_shift_shift,model_beesdoo_shift_shift,group_shift_attendance_sheet,1,1,1,0
read_beesdoo_shift_sheet_shift,read_beesdoo_shift_sheet_shift,model_beesdoo_shift_sheet_shift,group_shift_attendance_sheet,1,0,0,0
create_beesdoo_shift_sheet_shift,create_beesdoo_shift_sheet_shift,model_beesdoo_shift_sheet_shift,group_shift_attendance_sheet,1,1,1,0
create_beesdoo_shift_sheet_expected,create_beesdoo_shift_sheet_expected,model_beesdoo_shift_sheet_expected,group_shift_attendance_sheet,1,1,1,0
manage_beesdoo_shift_sheet_added,manage_beesdoo_shift_sheet_added,model_beesdoo_shift_sheet_added,group_shift_attendance_sheet,1,1,1,1
create_beesdoo_shift_sheet,create_beesdoo_shift_sheet,model_beesdoo_shift_sheet,group_shift_attendance_sheet,1,1,1,0
sheet_access_beesdoo_shift_template,sheet_access_beesdoo_shift_template,model_beesdoo_shift_template,group_shift_attendance_sheet,1,0,0,0
sheet_access_beesdoo_shift_type,sheet_access_beesdoo_shift_type,model_beesdoo_shift_type,group_shift_attendance_sheet,1,0,0,0
access_beesdoo_shift_daynumber,access_beesdoo_shift_daynumber,model_beesdoo_shift_daynumber,group_shift_attendance_sheet,1,0,0,0
manage_beesdoo_shift_sheet_shift,beesdoo_shift_sheet_shift,model_beesdoo_shift_sheet_shift,group_shift_attendance,1,1,1,1
manage_beesdoo_shift_sheet_expected,manage_beesdoo_shift_sheet_expected,model_beesdoo_shift_sheet_expected,group_shift_attendance,1,1,1,1

0
beesdoo_shift/tests/__init__.py → beesdoo_shift_atttendance/tests/__init__.py

96
beesdoo_shift/tests/test_beesdoo_shift.py → beesdoo_shift_atttendance/tests/test_beesdoo_shift.py

@ -388,98 +388,4 @@ class TestBeesdooShift(TransactionCase):
sheet_1.added_shift_ids[0].task_id, self.shift_empty_1
)
# sheet_1.expected_shift_ids[0].worker_id
# sheet_1.expected_shift_ids[2].replaced_id
def test_shift_counters(self):
"Test shift counters calculation and cooperative status update"
status_1 = self.worker_regular_1.cooperative_status_ids
status_2 = self.worker_regular_3.cooperative_status_ids
status_3 = self.worker_irregular_1.cooperative_status_ids
shift_regular = self.shift_model.create(
{
"task_template_id": self.task_template_1.id,
"task_type_id": self.task_type_1.id,
"worker_id": self.worker_regular_1.id,
"start_time": datetime.now() - timedelta(minutes=50),
"end_time": datetime.now() - timedelta(minutes=40),
"is_regular": True,
"is_compensation": False,
}
)
future_shift_regular = self.shift_model.create(
{
"task_template_id": self.task_template_2.id,
"task_type_id": self.task_type_2.id,
"worker_id": self.worker_regular_1.id,
"start_time": datetime.now() + timedelta(minutes=20),
"end_time": datetime.now() + timedelta(minutes=30),
"is_regular": True,
"is_compensation": False,
}
)
shift_irregular = self.shift_model.create(
{
"task_template_id": self.task_template_2.id,
"task_type_id": self.task_type_3.id,
"worker_id": self.worker_irregular_1.id,
"start_time": datetime.now() - timedelta(minutes=15),
"end_time": datetime.now() - timedelta(minutes=10),
}
)
# For a regular worker
status_1.sr = 0
status_1.sc = 0
self.assertEqual(status_1.status, "ok")
shift_regular.state = "absent_1"
self.assertEqual(status_1.sr, -1)
self.assertEqual(status_1.status, "alert")
shift_regular.state = "done"
self.assertEquals(status_1.sr, 0)
self.assertEquals(status_1.sc, 0)
shift_regular.state = "open"
shift_regular.write({"is_regular": False, "is_compensation": True})
shift_regular.state = "done"
self.assertEquals(status_1.sr, 1)
self.assertEquals(status_1.sc, 0)
# Check unsubscribed status
status_1.sr = -1
status_1.sc = -1
# Subscribe him to another future shift
future_shift_regular.worker_id = self.worker_regular_1
with self.assertRaises(ValidationError) as e:
future_shift_regular.state = "absent_2"
self.assertIn("future", str(e.exception))
status_1.sr = -2
status_1.sc = -2
self.assertEquals(status_1.status, "unsubscribed")
# Should be unsubscribed from future shift
self.assertFalse(future_shift_regular.worker_id)
# With replacement worker (self.worker_regular_3)
shift_regular.state = "open"
status_1.sr = 0
status_1.sc = 0
status_2.sr = 0
status_2.sc = 0
shift_regular.replaced_id = self.worker_regular_3
shift_regular.state = "absent_2"
self.assertEqual(status_1.sr, 0)
self.assertEqual(status_1.sc, 0)
self.assertEqual(status_2.sr, -1)
self.assertEqual(status_2.sc, -1)
# For an irregular worker
status_3.sr = 0
status_3.sc = 0
self.assertEqual(status_3.status, "ok")
shift_irregular.state = "done"
self.assertEqual(status_3.sr, 1)
shift_irregular.state = "absent_2"
self.assertEqual(status_3.sr, -1)
# sheet_1.expected_shift_ids[0].worker_id

0
beesdoo_shift/views/attendance_sheet.xml → beesdoo_shift_atttendance/views/attendance_sheet.xml

0
beesdoo_shift/views/res_config_settings_view.xml → beesdoo_shift_atttendance/views/res_config_settings_view.xml

1
beesdoo_shift_atttendance/wizard/__init__.py

@ -0,0 +1 @@
from . import validate_attendance_sheet

0
beesdoo_shift/wizard/validate_attendance_sheet.py → beesdoo_shift_atttendance/wizard/validate_attendance_sheet.py

0
beesdoo_shift/wizard/validate_attendance_sheet.xml → beesdoo_shift_atttendance/wizard/validate_attendance_sheet.xml

3
beesdoo_website_shift/controllers/main.py

@ -20,8 +20,7 @@ class WebsiteShiftController(http.Controller):
def is_user_worker(self):
user = request.env['res.users'].browse(request.uid)
share_type = user.partner_id.cooperator_type
return share_type == 'share_a'
return user.partner_id.is_worker
def is_user_irregular(self):
user = request.env['res.users'].browse(request.uid)

54
beesdoo_website_shift/views/my_shift_website_templates.xml

@ -798,17 +798,22 @@
</p>
<p t-if="status.alert_start_time">
<label>In Alert Since:</label>
<t t-esc="time.strftime('%A %d %B %Y', time.strptime(status.alert_start_time, '%Y-%m-%d'))"/>
<label for="alert_start_time">In Alert Since:</label>
<t t-esc="status.alert_start_time.strftime('%A %d %B %Y')"/>
</p>
<p t-if="status.extension_start_time">
<label>In Extension Since:</label>
<t t-esc="time.strftime('%A %d %B %Y', time.strptime(status.extension_start_time, '%Y-%m-%d'))"/>
<label for="extension_start_time">In Extension Since:</label>
<t t-esc="status.extension_start_time.strftime('%A %d %B %Y')"/>
</p>
<div class="oe_structure"/>
<t t-call="beesdoo_website_shift.help_text_title"/>
<t t-call="beesdoo_website_shift.help_text_common"/>
<t t-call="beesdoo_website_shift.help_text_irregular_worker"/>
<div class="oe_structure"/>
</div>
<div class="col-xs-12 col-md-8">
@ -819,17 +824,6 @@
</div> <!-- col-md-8 -->
<div class="col-xs-12 col-md-4 text-justify">
<div class="oe_structure"/>
<t t-call="beesdoo_website_shift.help_text_title"/>
<t t-call="beesdoo_website_shift.help_text_common"/>
<t t-call="beesdoo_website_shift.help_text_irregular_worker"/>
<div class="oe_structure"/>
</div> <!-- col-md-4 -->
</div> <!-- row -->
</div> <!-- container -->
</section>
@ -863,18 +857,18 @@
</p>
<p t-if="status.next_countdown_date">
<label>Next Countdown Date:</label>
<t t-esc="time.strftime('%A %d %B %Y', time.strptime(status.next_countdown_date, '%Y-%m-%d'))"/>
<label for="next_countdown_date">Next Countdown Date:</label>
<t t-esc="status.next_countdown_date.strftime('%A %d %B %Y')"/>
</p>
<p t-if="status.future_alert_date">
<label>Future Date of Alert:</label>
<t t-esc="time.strftime('%A %d %B %Y', time.strptime(status.future_alert_date, '%Y-%m-%d'))"/>
<label for="future_alert_date">Future Date of Alert:</label>
<t t-esc="status.future_alert_date.strftime('%A %d %B %Y')"/>
</p>
<p t-if="status.irregular_absence_date">
<label>Last Absence Date:</label>
<t t-esc="time.strftime('%A %d %B %Y', time.strptime(status.irregular_absence_date, '%Y-%m-%d'))"/>
<label for="last_absence_date">Last Absence Date:</label>
<t t-esc="status.irregular_absence_date.strftime('%A %d %B %Y')"/>
</p>
<p t-if="status.irregular_absence_counter">
@ -883,7 +877,10 @@
</p>
<div class="oe_structure"/>
<t t-call="beesdoo_website_shift.help_text_title"/>
<t t-call="beesdoo_website_shift.help_text_common"/>
<t t-call="beesdoo_website_shift.help_text_irregular_worker"/>
<div class="oe_structure"/>
</div>
<div class="col-xs-12 col-md-8">
@ -925,7 +922,7 @@
<section class="wrap">
<div class="container">
<div class="row">
<div class="col-xs-12 col-sm-6 text-justify">
<div class="col-12 col-md-12 text-justify">
<p>
Please, subscribe, in priority, to the highlighted shifts. To sign up to a shift click on the
subscribe button. Notice that you can not unsubscribe online. To unsubscribe to a shift, please,
@ -943,17 +940,6 @@
</div> <!-- col-md-8 -->
<div class="col-xs-12 col-md-4 text-justify">
<div class="oe_structure"/>
<t t-call="beesdoo_website_shift.help_text_title"/>
<t t-call="beesdoo_website_shift.help_text_common"/>
<t t-call="beesdoo_website_shift.help_text_irregular_worker"/>
<div class="oe_structure"/>
</div> <!-- col-md-4 -->
</div> <!-- row -->
</div> <!-- container -->
</section>

8
beesdoo_website_shift/views/shift_website_templates.xml

@ -3,7 +3,7 @@
Copyright 2017-2018 Rémy Taymans <remytaymans@gmail.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<openerp>
<odoo>
<!-- Add menu entries -->
<data noupdate="1">
@ -11,13 +11,13 @@
<field name="name">Shifts Irregular</field>
<field name="url">/shift_irregular_worker</field>
<field name="parent_id" ref="website.main_menu"/>
<field name="sequence" type="int">50</field>
<field name="sequence">50</field>
</record>
<record id="menu_work_regular" model="website.menu">
<field name="name">Shifts Regular</field>
<field name="url">/shift_template_regular_worker</field>
<field name="parent_id" ref="website.main_menu"/>
<field name="sequence" type="int">51</field>
<field name="sequence">51</field>
</record>
</data>
@ -206,4 +206,4 @@
</t>
</template>
</openerp>
</odoo>

0
beesdoo_worker_status/__init__.py

26
beesdoo_worker_status/__manifest__.py

@ -0,0 +1,26 @@
{
'name': "Beescoop Worker Status manager",
'summary': """
Worker status management specific to beescoop""",
'description': """
""",
'author': "Thibault Francois, Elouan Le Bars, Coop It Easy",
'website': "https://github.com/beescoop/Obeesdoo",
'category': 'Cooperative management',
'version': '12.0.1.0.0',
'depends': [
'beesdoo_base',
'beesdoo_shift',
],
'data': [
],
'demo': [
]
}

1
beesdoo_worker_status/models/__init__.py

@ -0,0 +1 @@
from . import cooperative_status

266
beesdoo_worker_status/models/cooperative_status.py

@ -0,0 +1,266 @@
from odoo import models, fields, api, _
from odoo.exceptions import ValidationError, UserError
from datetime import timedelta, datetime
import logging
class CooperativeStatus(models.Model):
_inherit = 'cooperative.status'
_period = 28
######################################################
# #
# Override of method to define status behavior #
# #
######################################################
future_alert_date = fields.Date(compute='_compute_future_alert_date')
next_countdown_date = fields.Date(compute='_compute_next_countdown_date')
@api.depends('today', 'irregular_start_date', 'sr', 'holiday_start_time',
'holiday_end_time', 'temporary_exempt_start_date',
'temporary_exempt_end_date')
def _compute_future_alert_date(self):
"""Compute date before which the worker is up to date"""
for rec in self:
# Only for irregular worker
if rec.working_mode != 'irregular' and not rec.irregular_start_date:
rec.future_alert_date = False
# Alert start time already set
elif rec.alert_start_time:
rec.future_alert_date = False
# Holidays are not set properly
elif bool(rec.holiday_start_time) != bool(rec.holiday_end_time):
rec.future_alert_date = False
# Exemption have not a start and end time
elif (bool(rec.temporary_exempt_start_date)
!= bool(rec.temporary_exempt_end_date)):
rec.future_alert_date = False
else:
date = rec.today
counter = rec.sr
# Simulate the countdown
while counter > 0:
date = add_days_delta(date, 1)
date = self._next_countdown_date(rec.irregular_start_date,
date)
# Check holidays
if (rec.holiday_start_time and rec.holiday_end_time
and date >= rec.holiday_start_time
and date <= rec.holiday_end_time):
continue
# Check temporary exemption
elif (rec.temporary_exempt_start_date
and rec.temporary_exempt_end_date
and date >= rec.temporary_exempt_start_date
and date <= rec.temporary_exempt_end_date):
continue
else:
counter -= 1
rec.future_alert_date = self._next_countdown_date(
rec.irregular_start_date, date
)
@api.depends('today', 'irregular_start_date', 'holiday_start_time',
'holiday_end_time', 'temporary_exempt_start_date',
'temporary_exempt_end_date')
def _compute_next_countdown_date(self):
"""
Compute the following countdown date. This date is the date when
the worker will see his counter changed du to the cron. This
date is like the birthday date of the worker that occurred each
PERIOD.
"""
for rec in self:
# Only for irregular worker
if rec.working_mode != 'irregular' and not rec.irregular_start_date:
rec.next_countdown_date = False
# Holidays are not set properly
elif bool(rec.holiday_start_time) != bool(rec.holiday_end_time):
rec.next_countdown_date = False
# Exemption have not a start and end time
elif (bool(rec.temporary_exempt_start_date)
!= bool(rec.temporary_exempt_end_date)):
rec.next_countdown_date = False
else:
date = rec.today
next_countdown_date = False
while not next_countdown_date:
date = self._next_countdown_date(rec.irregular_start_date, date)
# Check holidays
if (rec.holiday_start_time and rec.holiday_end_time
and date >= rec.holiday_start_time
and date <= rec.holiday_end_time):
date = add_days_delta(date, 1)
continue
# Check temporary exemption
elif (rec.temporary_exempt_start_date
and rec.temporary_exempt_end_date
and date >= rec.temporary_exempt_start_date
and date <= rec.temporary_exempt_end_date):
date = add_days_delta(date, 1)
continue
else:
next_countdown_date = date
rec.next_countdown_date = next_countdown_date
#####################################
# Status Change implementation #
#####################################
def _set_regular_status(self, grace_delay, alert_delay):
self.ensure_one()
counter_unsubscribe = int(self.env['ir.config_parameter'].get_param('regular_counter_to_unsubscribe', -4))
ok = self.sr >= 0 and self.sc >= 0
grace_delay = grace_delay + self.time_extension
if (self.sr + self.sc) <= counter_unsubscribe or self.unsubscribed:
return 'unsubscribed'
#Check if exempted. Exempt end date is not required.
if self.temporary_exempt_start_date and self.today >= self.temporary_exempt_start_date:
if not self.temporary_exempt_end_date or self.today <= self.temporary_exempt_end_date:
return 'exempted'
#Transition to alert sr < 0 or stay in alert sr < 0 or sc < 0 and thus alert time is defined
if not ok and self.alert_start_time and self.extension_start_time and self.today <= add_days_delta(self.extension_start_time, grace_delay):
return 'extension'
if not ok and self.alert_start_time and self.extension_start_time and self.today > add_days_delta(self.extension_start_time, grace_delay):
return 'suspended'
if not ok and self.alert_start_time and self.today > add_days_delta(self.alert_start_time, alert_delay):
return 'suspended'
if (self.sr < 0) or (not ok and self.alert_start_time):
return 'alert'
if (
self.holiday_start_time
and self.holiday_end_time
and self.today >= self.holiday_start_time
and self.today <= self.holiday_end_time
):
return 'holiday'
elif ok or (not self.alert_start_time and self.sr >= 0):
return 'ok'
def _set_irregular_status(self, grace_delay, alert_delay):
counter_unsubscribe = int(self.env['ir.config_parameter'].get_param('irregular_counter_to_unsubscribe', -3))
self.ensure_one()
ok = self.sr >= 0
grace_delay = grace_delay + self.time_extension
if self.sr <= counter_unsubscribe or self.unsubscribed:
return 'unsubscribed'
#Check if exempted. Exempt end date is not required.
elif self.temporary_exempt_start_date and self.today >= self.temporary_exempt_start_date:
if not self.temporary_exempt_end_date or self.today <= self.temporary_exempt_end_date:
return 'exempted'
#Transition to alert sr < 0 or stay in alert sr < 0 or sc < 0 and thus alert time is defined
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):
return 'extension'
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):
return 'suspended'
elif not ok and self.alert_start_time and self.today > add_days_delta(self.alert_start_time, alert_delay):
return 'suspended'
elif (self.sr < 0) or (not ok and self.alert_start_time):
return 'alert'
elif (
self.holiday_start_time
and self.holiday_end_time
and self.today >= self.holiday_start_time
and self.today <= self.holiday_end_time
):
return 'holiday'
elif ok or (not self.alert_start_time and self.sr >= 0):
return 'ok'
def _state_change(self, new_state):
self.ensure_one()
if new_state == 'alert':
self.write({'alert_start_time': self.today, 'extension_start_time': False, 'time_extension': 0})
if new_state == 'ok':
data = {'extension_start_time': False, 'time_extension': 0}
data['alert_start_time'] = False
self.write(data)
if new_state == 'unsubscribed' or new_state == 'resigning':
# Remove worker from task_templates
self.cooperator_id.sudo().write(
{'subscribed_shift_ids': [(5, 0, 0)]})
# Remove worker from supercoop in task_templates
task_tpls = self.env['beesdoo.shift.template'].search(
[('super_coop_id', 'in', self.cooperator_id.user_ids.ids)]
)
task_tpls.write({'super_coop_id': False})
# Remove worker for future tasks (remove also supercoop)
self.env['beesdoo.shift.shift'].sudo().unsubscribe_from_today(
[self.cooperator_id.id], now=fields.Datetime.now()
)
def _change_counter(self, data):
"""
Call when a shift state is changed
use data generated by _get_counter_date_state_change
"""
self.sc += data.get('sc', 0)
self.sr += data.get('sr', 0)
self.irregular_absence_counter += data.get('irregular_absence_counter', 0)
self.irregular_absence_date = data.get('irregular_absence_date', False)
###############################################
###### Irregular Cron implementation ##########
###############################################
def _get_irregular_worker_domain(self):
return ['&',
'&',
'&',
('status', 'not in', ['unsubscribed', 'exempted']),
('working_mode', '=', 'irregular'),
('irregular_start_date', '!=', False),
'|',
'|', ('holiday_start_time', '=', False), ('holiday_end_time', '=', False),
'|', ('holiday_start_time', '>', today), ('holiday_end_time', '<', today),
]
def _change_irregular_counter(self):
if self.sr > 0:
self.sr -= 1
elif self.alert_start_time:
self.sr -= 1
else:
self.sr -= 2
##################################
# Internal Implementation #
##################################
def _next_countdown_date(self, irregular_start_date, today=False):
"""
Return the next countdown date given irregular_start_date and
today dates.
This does not take holiday and other status into account.
"""
today = today or fields.Date.today()
delta = (today - irregular_start_date).days
if not delta % PERIOD:
return today
return add_days_delta(today, PERIOD - (delta % PERIOD))
class ResPartner(models.Model):
_inherit = 'res.partner'
"""
Override is_worker definition
You need have subscribe to a A Share
"""
is_worker = fields.Boolean(compute="_is_worker", search="_search_worker")
def _is_worker(self):
for rec in self:
rec.is_worker = rec.cooperator_type == 'share_a'
def _search_worker(self, operator, value):
if (operator == '=' and value) or (operator == '!=' and not value):
return [('cooperator_type', '=', 'share_a')]
else:
return [('cooperator_type', '!=', 'share_a')]

81
beesdoo_worker_status/models/task.py

@ -0,0 +1,81 @@
import json
from datetime import datetime, timedelta
from odoo import _, api, fields, models
from odoo.exceptions import UserError, ValidationError
class Task(models.Model):
_inherit = 'beesdoo.shift.shift'
#################################
# State Definition #
#################################
def _get_selection_status(self):
return [
("open","Confirmed"),
("done","Attended"),
("absent_2","Absent - 2 compensations"),
("absent_1","Absent - 1 compensation"),
("absent_0","Absent - 0 compensation"),
("cancel","Cancelled")
]
def _get_color_mapping(state):
return {
"draft": 0,
"open": 1,
"done": 5,
"absent_2": 2,
"absent_1": 7,
"absent_0": 3,
"cancel": 9,
}[state]
def _get_final_state():
return ["done", "absent_2", "absent_1", "absent_0"]
##############################################
# Change counter when state change #
##############################################
def _get_counter_date_state_change(self, new_state):
data = {}
if self.worker_id.working_mode == 'regular':
if not self.replaced_id: #No replacement case
status = self.worker_id.cooperative_status_ids[0]
else:
status = self.replaced_id.cooperative_status_ids[0]
if new_state == "done" and not self.is_regular:
# Regular counter is always updated first
if status.sr < 0:
data['sr'] = 1
elif status.sc < 0:
data['sc'] = 1
# Bonus shift case
else:
data['sr'] = 1
if new_state == "absent_2":
data['sr'] = -1
data['sc'] = -1
if new_state == "absent_1":
data['sr'] = -1
elif self.worker_id.working_mode == 'irregular':
status = self.worker_id.cooperative_status_ids[0]
if new_state == "done" or new_state == "absent_0":
data['sr'] = 1
data['irregular_absence_date'] = False
data['irregular_absence_counter'] = 1 if status.irregular_absence_counter < 0 else 0
if new_state == "absent_2" or new_state == "absent_1":
if new_state == "absent_2":
data['sr'] = -1
data['irregular_absence_date'] = self.start_time.date()
data['irregular_absence_counter'] = -1
return data

1
beesdoo_worker_status/tests/__init__.py

@ -0,0 +1 @@
from . import test_beesdoo_shift

247
beesdoo_worker_status/tests/test_beesdoo_shift.py

@ -0,0 +1,247 @@
# Copyright 2019 - Today Coop IT Easy SCRLfs (<http://www.coopiteasy.be>)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
import time
from datetime import datetime, timedelta
from odoo import exceptions, fields
from odoo.exceptions import UserError, ValidationError
from odoo.tests.common import TransactionCase
class TestBeesdooShift(TransactionCase):
def setUp(self):
super(TestBeesdooShift, self).setUp()
self.shift_model = self.env["beesdoo.shift.shift"]
self.shift_template_model = self.env["beesdoo.shift.template"]
self.attendance_sheet_model = self.env["beesdoo.shift.sheet"]
self.attendance_sheet_shift_model = self.env[
"beesdoo.shift.sheet.shift"
]
self.shift_expected_model = self.env["beesdoo.shift.sheet.expected"]
self.shift_added_model = self.env["beesdoo.shift.sheet.added"]
self.task_type_default_id = self.env["ir.config_parameter"].sudo().get_param(
"beesdoo_shift.task_type_default_id"
)
self.current_time = datetime.now()
self.user_admin = self.env.ref("base.user_root")
self.user_generic = self.env.ref(
"beesdoo_base.beesdoo_shift_user_1_demo"
)
self.user_permanent = self.env.ref(
"beesdoo_base.beesdoo_shift_user_2_demo"
)
self.setting_wizard = self.env["beesdoo.shift.config.settings"].sudo(
self.user_admin
)
self.worker_regular_1 = self.env.ref(
"beesdoo_base.res_partner_cooperator_6_demo"
)
self.worker_regular_2 = self.env.ref(
"beesdoo_base.res_partner_cooperator_5_demo"
)
self.worker_regular_3 = self.env.ref(
"beesdoo_base.res_partner_cooperator_3_demo"
)
self.worker_regular_super_1 = self.env.ref(
"beesdoo_base.res_partner_cooperator_1_demo"
)
self.worker_irregular_1 = self.env.ref(
"beesdoo_base.res_partner_cooperator_2_demo"
)
self.worker_irregular_2 = self.env.ref(
"beesdoo_base.res_partner_cooperator_4_demo"
)
self.task_type_1 = self.env.ref(
"beesdoo_shift.beesdoo_shift_task_type_1_demo"
)
self.task_type_2 = self.env.ref(
"beesdoo_shift.beesdoo_shift_task_type_2_demo"
)
self.task_type_3 = self.env.ref(
"beesdoo_shift.beesdoo_shift_task_type_3_demo"
)
self.task_template_1 = self.env.ref(
"beesdoo_shift.beesdoo_shift_task_template_1_demo"
)
self.task_template_2 = self.env.ref(
"beesdoo_shift.beesdoo_shift_task_template_2_demo"
)
# Set time in and out of generation interval parameter
self.start_in_1 = self.current_time + timedelta(seconds=2)
self.end_in_1 = self.current_time + timedelta(minutes=10)
self.start_in_2 = self.current_time + timedelta(minutes=9)
self.end_in_2 = self.current_time + timedelta(minutes=21)
self.start_out_1 = self.current_time - timedelta(minutes=50)
self.end_out_1 = self.current_time - timedelta(minutes=20)
self.start_out_2 = self.current_time + timedelta(minutes=40)
self.end_out_2 = self.current_time + timedelta(minutes=50)
self.shift_regular_regular_1 = self.shift_model.create(
{
"task_template_id": self.task_template_1.id,
"task_type_id": self.task_type_1.id,
"worker_id": self.worker_regular_1.id,
"start_time": self.start_in_1,
"end_time": self.end_in_1,
"is_regular": True,
"is_compensation": False,
}
)
self.shift_regular_regular_2 = self.shift_model.create(
{
"task_type_id": self.task_type_2.id,
"worker_id": self.worker_regular_2.id,
"start_time": self.start_out_1,
"end_time": self.end_out_1,
"is_regular": True,
"is_compensation": False,
}
)
self.shift_regular_regular_replaced_1 = self.shift_model.create(
{
"task_template_id": self.task_template_1.id,
"task_type_id": self.task_type_3.id,
"worker_id": self.worker_regular_3.id,
"start_time": self.start_in_1,
"end_time": self.end_in_1,
"is_regular": True,
"is_compensation": False,
"replaced_id": self.worker_regular_2.id,
}
)
future_shift_regular = self.shift_model.create(
{
"task_template_id": self.task_template_2.id,
"task_type_id": self.task_type_1.id,
"worker_id": self.worker_regular_super_1.id,
"start_time": self.start_in_2,
"end_time": self.end_in_2,
"is_regular": False,
"is_compensation": True,
}
)
self.shift_irregular_1 = self.shift_model.create(
{
"task_template_id": self.task_template_1.id,
"task_type_id": self.task_type_2.id,
"worker_id": self.worker_irregular_1.id,
"start_time": self.start_in_1,
"end_time": self.end_in_1,
}
)
self.shift_irregular_2 = self.shift_model.create(
{
"task_type_id": self.task_type_3.id,
"worker_id": self.worker_irregular_2.id,
"start_time": self.start_out_2,
"end_time": self.end_out_2,
}
)
self.shift_empty_1 = self.shift_model.create(
{
"task_template_id": self.task_template_1.id,
"task_type_id": self.task_type_1.id,
"start_time": self.start_in_1,
"end_time": self.end_in_1,
}
)
def test_shift_counters(self):
"Test shift counters calculation and cooperative status update"
status_1 = self.worker_regular_1.cooperative_status_ids
status_2 = self.worker_regular_3.cooperative_status_ids
status_3 = self.worker_irregular_1.cooperative_status_ids
shift_regular = self.shift_model.create(
{
"task_template_id": self.task_template_1.id,
"task_type_id": self.task_type_1.id,
"worker_id": self.worker_regular_1.id,
"start_time": datetime.now() - timedelta(minutes=50),
"end_time": datetime.now() - timedelta(minutes=40),
"is_regular": True,
"is_compensation": False,
}
)
future_shift_regular = self.shift_model.create(
{
"task_template_id": self.task_template_2.id,
"task_type_id": self.task_type_2.id,
"worker_id": self.worker_regular_1.id,
"start_time": datetime.now() + timedelta(minutes=20),
"end_time": datetime.now() + timedelta(minutes=30),
"is_regular": True,
"is_compensation": False,
}
)
shift_irregular = self.shift_model.create(
{
"task_template_id": self.task_template_2.id,
"task_type_id": self.task_type_3.id,
"worker_id": self.worker_irregular_1.id,
"start_time": datetime.now() - timedelta(minutes=15),
"end_time": datetime.now() - timedelta(minutes=10),
}
)
# For a regular worker
status_1.sr = 0
status_1.sc = 0
self.assertEqual(status_1.status, "ok")
shift_regular.state = "absent_1"
self.assertEqual(status_1.sr, -1)
self.assertEqual(status_1.status, "alert")
shift_regular.state = "done"
self.assertEquals(status_1.sr, 0)
self.assertEquals(status_1.sc, 0)
shift_regular.state = "open"
shift_regular.write({"is_regular": False, "is_compensation": True})
shift_regular.state = "done"
self.assertEquals(status_1.sr, 1)
self.assertEquals(status_1.sc, 0)
# Check unsubscribed status
status_1.sr = -1
status_1.sc = -1
# Subscribe him to another future shift
future_shift_regular.worker_id = self.worker_regular_1
with self.assertRaises(ValidationError) as e:
future_shift_regular.state = "absent_2"
self.assertIn("future", str(e.exception))
status_1.sr = -2
status_1.sc = -2
self.assertEquals(status_1.status, "unsubscribed")
# Should be unsubscribed from future shift
self.assertFalse(future_shift_regular.worker_id)
# With replacement worker (self.worker_regular_3)
shift_regular.state = "open"
status_1.sr = 0
status_1.sc = 0
status_2.sr = 0
status_2.sc = 0
shift_regular.replaced_id = self.worker_regular_3
shift_regular.state = "absent_2"
self.assertEqual(status_1.sr, 0)
self.assertEqual(status_1.sc, 0)
self.assertEqual(status_2.sr, -1)
self.assertEqual(status_2.sc, -1)
# For an irregular worker
status_3.sr = 0
status_3.sc = 0
self.assertEqual(status_3.status, "ok")
shift_irregular.state = "done"
self.assertEqual(status_3.sr, 1)
shift_irregular.state = "absent_2"
self.assertEqual(status_3.sr, -1)
Loading…
Cancel
Save