Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
T
third-party
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
2
Merge Requests
2
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
cooperatic-foodcoops
third-party
Commits
5575d2f8
Commit
5575d2f8
authored
Aug 09, 2022
by
François C.
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add shift absence process for any datetime (for test purpose)
parent
ddcbb3a0
Pipeline
#2338
passed with stage
in 2 minutes 12 seconds
Changes
6
Pipelines
1
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
181 additions
and
6 deletions
+181
-6
models.py
members/models.py
+122
-4
member.css
members/static/css/member.css
+1
-0
members.js
members/static/js/members.js
+49
-0
urls.py
members/urls.py
+1
-0
views.py
members/views.py
+5
-1
index.html
templates/members/index.html
+3
-1
No files found.
members/models.py
View file @
5575d2f8
...
@@ -1339,7 +1339,7 @@ class CagetteServices(models.Model):
...
@@ -1339,7 +1339,7 @@ class CagetteServices(models.Model):
@staticmethod
@staticmethod
def
get_services_at_time
(
time
,
tz_offset
,
with_members
=
True
):
def
get_services_at_time
(
time
,
tz_offset
,
with_members
=
True
):
"""Retrieve present services with member linked."""
"""Retrieve present services with member
s
linked."""
default_acceptable_minutes_after_shift_begins
=
getattr
(
settings
,
'ACCEPTABLE_ENTRANCE_MINUTES_AFTER_SHIFT_BEGINS'
,
15
)
default_acceptable_minutes_after_shift_begins
=
getattr
(
settings
,
'ACCEPTABLE_ENTRANCE_MINUTES_AFTER_SHIFT_BEGINS'
,
15
)
minutes_before_shift_starts_delay
=
getattr
(
settings
,
'ACCEPTABLE_ENTRANCE_MINUTES_BEFORE_SHIFT'
,
15
)
minutes_before_shift_starts_delay
=
getattr
(
settings
,
'ACCEPTABLE_ENTRANCE_MINUTES_BEFORE_SHIFT'
,
15
)
...
@@ -1360,7 +1360,7 @@ class CagetteServices(models.Model):
...
@@ -1360,7 +1360,7 @@ class CagetteServices(models.Model):
fields
=
[
'name'
,
'week_number'
,
'registration_ids'
,
fields
=
[
'name'
,
'week_number'
,
'registration_ids'
,
'standard_registration_ids'
,
'standard_registration_ids'
,
'shift_template_id'
,
'shift_ticket_ids'
,
'shift_template_id'
,
'shift_ticket_ids'
,
'date_begin_tz'
,
'date_end_tz'
]
'date_begin_tz'
,
'date_end_tz'
,
'state'
]
services
=
api
.
search_read
(
'shift.shift'
,
cond
,
fields
,
order
=
"date_begin_tz ASC"
)
services
=
api
.
search_read
(
'shift.shift'
,
cond
,
fields
,
order
=
"date_begin_tz ASC"
)
for
s
in
services
:
for
s
in
services
:
if
(
len
(
s
[
'registration_ids'
])
>
0
):
if
(
len
(
s
[
'registration_ids'
])
>
0
):
...
@@ -1510,13 +1510,18 @@ class CagetteServices(models.Model):
...
@@ -1510,13 +1510,18 @@ class CagetteServices(models.Model):
cond
=
[[
'date_begin'
,
'>='
,
date_24h_before
.
isoformat
()],
cond
=
[[
'date_begin'
,
'>='
,
date_24h_before
.
isoformat
()],
[
'date_begin'
,
'<='
,
end_date
.
isoformat
()],
[
'date_begin'
,
'<='
,
end_date
.
isoformat
()],
[
'state'
,
'='
,
'open'
]]
[
'state'
,
'='
,
'open'
]]
fields
=
[
'state'
,
'partner_id'
,
'date_begin'
]
fields
=
[
'state'
,
'partner_id'
,
'date_begin'
,
'shift_id'
]
res
=
api
.
search_read
(
'shift.registration'
,
cond
,
fields
)
res
=
api
.
search_read
(
'shift.registration'
,
cond
,
fields
)
ids
=
[]
ids
=
[]
partner_ids
=
[]
partner_ids
=
[]
excluded_partner
=
[]
excluded_partner
=
[]
canceled_reg_ids
=
[]
# for exempted people
shift_ids
=
[]
for
r
in
res
:
for
r
in
res
:
partner_ids
.
append
(
int
(
r
[
'partner_id'
][
0
]))
partner_ids
.
append
(
int
(
r
[
'partner_id'
][
0
]))
shift_id
=
int
(
r
[
'shift_id'
][
0
])
if
shift_id
not
in
shift_ids
:
shift_ids
.
append
(
shift_id
)
cond
=
[[
'id'
,
'in'
,
partner_ids
],
cond
=
[[
'id'
,
'in'
,
partner_ids
],
[
'cooperative_state'
,
'in'
,
[
'exempted'
]]]
[
'cooperative_state'
,
'in'
,
[
'exempted'
]]]
fields
=
[
'id'
]
fields
=
[
'id'
]
...
@@ -1530,13 +1535,28 @@ class CagetteServices(models.Model):
...
@@ -1530,13 +1535,28 @@ class CagetteServices(models.Model):
(
_h
,
_m
,
_s
)
=
h
.
split
(
':'
)
(
_h
,
_m
,
_s
)
=
h
.
split
(
':'
)
if
int
(
_h
)
<
21
:
if
int
(
_h
)
<
21
:
ids
.
append
(
int
(
r
[
'id'
]))
ids
.
append
(
int
(
r
[
'id'
]))
else
:
canceled_reg_ids
.
append
(
int
(
r
[
'id'
]))
# coop_logger.info("Traitement absences shift_registration ids %s", ids)
# coop_logger.info("Traitement absences shift_registration ids %s", ids)
f
=
{
'state'
:
absence_status
,
'date_closed'
:
now
.
isoformat
()}
f
=
{
'state'
:
absence_status
,
'date_closed'
:
now
.
isoformat
()}
update_shift_reg_result
=
{
'update'
:
api
.
update
(
'shift.registration'
,
ids
,
f
),
'reg_shift'
:
res
}
update_shift_reg_result
=
{
'update'
:
api
.
update
(
'shift.registration'
,
ids
,
f
),
'reg_shift'
:
res
,
'errors'
:
[]
}
if
update_shift_reg_result
[
'update'
]
is
True
:
if
update_shift_reg_result
[
'update'
]
is
True
:
update_shift_reg_result
[
'process_status_res'
]
=
api
.
execute
(
'res.partner'
,
'run_process_target_status'
,
[])
update_shift_reg_result
[
'process_status_res'
]
=
api
.
execute
(
'res.partner'
,
'run_process_target_status'
,
[])
# change shift state by triggering button_done method for all related shifts
if
len
(
canceled_reg_ids
)
>
0
:
f
=
{
'state'
:
'cancel'
,
'date_closed'
:
now
.
isoformat
()}
api
.
update
(
'shift.registration'
,
canceled_reg_ids
,
f
)
for
sid
in
shift_ids
:
try
:
api
.
execute
(
'shift.shift'
,
'button_done'
,
sid
)
except
Exception
as
e
:
marshal_none_error
=
'cannot marshal None unless allow_none is enabled'
if
not
(
marshal_none_error
in
str
(
e
)):
update_shift_reg_result
[
'errors'
]
.
append
({
'shift_id'
:
sid
,
'msg'
:
str
(
e
)})
return
update_shift_reg_result
return
update_shift_reg_result
@staticmethod
@staticmethod
def
close_ftop_service
():
def
close_ftop_service
():
"""Called by cron script"""
"""Called by cron script"""
...
@@ -1670,6 +1690,104 @@ class CagetteServices(models.Model):
...
@@ -1670,6 +1690,104 @@ class CagetteServices(models.Model):
coop_logger
.
error
(
"easy_validate_shift_presence :
%
s
%
s"
,
str
(
coop_id
),
str
(
e
))
coop_logger
.
error
(
"easy_validate_shift_presence :
%
s
%
s"
,
str
(
coop_id
),
str
(
e
))
return
res
return
res
class
CagetteService
(
models
.
Model
):
"""Class to handle cagette Odoo service."""
def
__init__
(
self
,
id
):
"""Init with odoo id."""
self
.
id
=
int
(
id
)
self
.
o_api
=
OdooAPI
()
def
_process_associated_people_extra_shift_done
(
self
):
cond
=
[[
'shift_id'
,
'='
,
self
.
id
],
[
'state'
,
'='
,
'done'
],
[
'associate_registered'
,
'='
,
'both'
],
[
'should_increment_extra_shift_done'
,
'='
,
True
]]
fields
=
[
'id'
,
'state'
,
'partner_id'
,
'date_begin'
]
res
=
self
.
o_api
.
search_read
(
'shift.registration'
,
cond
,
fields
)
extra_shift_done_incremented_srids
=
[]
# shift registration ids
for
r
in
res
:
cond
=
[[
'id'
,
'='
,
r
[
'partner_id'
][
0
]]]
fields
=
[
'id'
,
'extra_shift_done'
]
res_partner
=
self
.
o_api
.
search_read
(
'res.partner'
,
cond
,
fields
)
f
=
{
'extra_shift_done'
:
res_partner
[
0
][
'extra_shift_done'
]
+
1
}
self
.
o_api
.
update
(
'res.partner'
,
[
r
[
'partner_id'
][
0
]],
f
)
extra_shift_done_incremented_srids
.
append
(
int
(
r
[
'id'
]))
# Make sure the counter isn't incremented twice
f
=
{
'should_increment_extra_shift_done'
:
False
}
self
.
o_api
.
update
(
'shift.registration'
,
extra_shift_done_incremented_srids
,
f
)
def
_process_related_shift_registrations
(
self
):
now
=
datetime
.
datetime
.
now
()
absence_status
=
'excused'
res_c
=
self
.
o_api
.
search_read
(
'ir.config_parameter'
,
[[
'key'
,
'='
,
'lacagette_membership.absence_status'
]],
[
'value'
])
if
len
(
res_c
)
==
1
:
absence_status
=
res_c
[
0
][
'value'
]
cond
=
[[
'shift_id'
,
'='
,
self
.
id
],
[
'state'
,
'='
,
'open'
]]
fields
=
[
'state'
,
'partner_id'
,
'date_begin'
]
res
=
self
.
o_api
.
search_read
(
'shift.registration'
,
cond
,
fields
)
ids
=
[]
partner_ids
=
[]
excluded_partner
=
[]
canceled_reg_ids
=
[]
# for exempted people
for
r
in
res
:
partner_ids
.
append
(
int
(
r
[
'partner_id'
][
0
]))
cond
=
[[
'id'
,
'in'
,
partner_ids
],
[
'cooperative_state'
,
'in'
,
[
'exempted'
]]]
fields
=
[
'id'
]
res_exempted
=
self
.
o_api
.
search_read
(
'res.partner'
,
cond
,
fields
)
for
r
in
res_exempted
:
excluded_partner
.
append
(
int
(
r
[
'id'
]))
for
r
in
res
:
if
not
(
int
(
r
[
'partner_id'
][
0
])
in
excluded_partner
):
d_begin
=
r
[
'date_begin'
]
(
d
,
h
)
=
d_begin
.
split
(
' '
)
(
_h
,
_m
,
_s
)
=
h
.
split
(
':'
)
if
int
(
_h
)
<
21
:
ids
.
append
(
int
(
r
[
'id'
]))
else
:
canceled_reg_ids
.
append
(
int
(
r
[
'id'
]))
# coop_logger.info("Traitement absences shift_registration ids %s", ids)
f
=
{
'state'
:
absence_status
,
'date_closed'
:
now
.
isoformat
()}
update_shift_reg_result
=
{
'update'
:
self
.
o_api
.
update
(
'shift.registration'
,
ids
,
f
),
'reg_shift'
:
res
,
'errors'
:
[]}
if
update_shift_reg_result
[
'update'
]
is
True
:
update_shift_reg_result
[
'process_status_res'
]
=
self
.
o_api
.
execute
(
'res.partner'
,
'run_process_target_status'
,
[])
# change shift state by triggering button_done method for all related shifts
if
len
(
canceled_reg_ids
)
>
0
:
f
=
{
'state'
:
'cancel'
,
'date_closed'
:
now
.
isoformat
()}
self
.
o_api
.
update
(
'shift.registration'
,
canceled_reg_ids
,
f
)
try
:
self
.
o_api
.
execute
(
'shift.shift'
,
'button_done'
,
self
.
id
)
except
Exception
as
e
:
marshal_none_error
=
'cannot marshal None unless allow_none is enabled'
if
not
(
marshal_none_error
in
str
(
e
)):
update_shift_reg_result
[
'errors'
]
.
append
({
'shift_id'
:
sid
,
'msg'
:
str
(
e
)})
return
update_shift_reg_result
def
record_absences
(
self
,
request
):
"""Can only been executed if an Odoo user is beeing connected."""
res
=
{}
try
:
if
CagetteUser
.
are_credentials_ok
(
request
)
is
True
:
self
.
_process_associated_people_extra_shift_done
()
res
=
self
.
_process_related_shift_registrations
()
else
:
res
[
'error'
]
=
'Forbidden'
except
Exception
as
e
:
coop_logger
.
error
(
"CagetteService.record_absences :
%
s
%
s"
,
str
(
self
.
id
),
str
(
e
))
res
[
'error'
]
=
str
(
e
)
return
res
class
CagetteUser
(
models
.
Model
):
class
CagetteUser
(
models
.
Model
):
@staticmethod
@staticmethod
...
...
members/static/css/member.css
View file @
5575d2f8
...
@@ -49,6 +49,7 @@ h1 .member_name {font-weight: bold;}
...
@@ -49,6 +49,7 @@ h1 .member_name {font-weight: bold;}
.members_list
li
.btn--inverse.late
{
background-color
:
#de9b00
;
color
:
white
}
.members_list
li
.btn--inverse.late
{
background-color
:
#de9b00
;
color
:
white
}
.members_list
li
.btn--inverse.both
{
background-color
:
#0275d8
;
color
:
white
}
.members_list
li
.btn--inverse.both
{
background-color
:
#0275d8
;
color
:
white
}
.members_list.done
li
.btn
{
pointer-events
:
none
;}
#service_entry_success
{
font-size
:
x-large
;}
#service_entry_success
{
font-size
:
x-large
;}
#service_entry_success
.explanations
{
margin
:
25px
0
;
font-size
:
18px
;}
#service_entry_success
.explanations
{
margin
:
25px
0
;
font-size
:
18px
;}
#service_entry_success
.points
,
#service_entry_success
.points
,
...
...
members/static/js/members.js
View file @
5575d2f8
...
@@ -41,6 +41,7 @@ var coop_info = $('.coop-info');
...
@@ -41,6 +41,7 @@ var coop_info = $('.coop-info');
var
service_data
=
null
;
var
service_data
=
null
;
const
missed_begin_msg
=
$
(
'#missed_begin_msg'
).
html
();
const
missed_begin_msg
=
$
(
'#missed_begin_msg'
).
html
();
const
current_shift_process_data_actions
=
$
(
'#current_shift_process_data_actions'
);
let
no_pict_msg
=
$
(
'#no-picture-msg'
);
let
no_pict_msg
=
$
(
'#no-picture-msg'
);
...
@@ -70,6 +71,12 @@ var html_elts = {
...
@@ -70,6 +71,12 @@ var html_elts = {
var
chars
=
[];
//input chars buffer
var
chars
=
[];
//input chars buffer
var
reset_shift_process_actions_zone
=
function
()
{
current_shift_process_data_actions
.
off
(
'click'
,
'a'
);
current_shift_process_data_actions
.
hide
();
current_shift_process_data_actions
.
empty
();
}
function
fill_member_slide
(
member
)
{
function
fill_member_slide
(
member
)
{
no_pict_msg
.
hide
();
no_pict_msg
.
hide
();
current_displayed_member
=
member
;
current_displayed_member
=
member
;
...
@@ -282,6 +289,9 @@ function fill_service_entry(s) {
...
@@ -282,6 +289,9 @@ function fill_service_entry(s) {
// if (typeof s.late != "undefined" && s.late == true) {
// if (typeof s.late != "undefined" && s.late == true) {
// m_list = '<ul class="members_list late">';
// m_list = '<ul class="members_list late">';
// }
// }
if
(
s
.
state
==
'done'
)
{
m_list
=
'<ul class="members_list done">'
;
}
$
.
each
(
s
.
members
,
function
(
i
,
e
)
{
$
.
each
(
s
.
members
,
function
(
i
,
e
)
{
var
li_class
=
"btn"
;
var
li_class
=
"btn"
;
var
li_data
=
""
;
var
li_data
=
""
;
...
@@ -304,6 +314,9 @@ function fill_service_entry(s) {
...
@@ -304,6 +314,9 @@ function fill_service_entry(s) {
}
else
{
}
else
{
li_data
=
' data-rid="'
+
e
.
id
+
'" data-mid="'
+
e
.
partner_id
[
0
]
+
'"'
;
li_data
=
' data-rid="'
+
e
.
id
+
'" data-mid="'
+
e
.
partner_id
[
0
]
+
'"'
;
}
}
if
(
s
.
state
==
'done'
)
{
li_data
+=
' disabled '
;
}
m_list
+=
'<li class="'
+
li_class
+
'" '
+
li_data
+
'>'
;
m_list
+=
'<li class="'
+
li_class
+
'" '
+
li_data
+
'>'
;
m_list
+=
e
.
partner_id
[
1
];
m_list
+=
e
.
partner_id
[
1
];
m_list
+=
'</li>'
;
m_list
+=
'</li>'
;
...
@@ -311,6 +324,40 @@ function fill_service_entry(s) {
...
@@ -311,6 +324,40 @@ function fill_service_entry(s) {
m_list
+=
'</ul>'
;
m_list
+=
'</ul>'
;
}
}
if
(
coop_is_connected
())
{
// Add shift process data
reset_shift_process_actions_zone
();
if
(
s
.
state
==
'draft'
||
s
.
state
==
'confirm'
)
{
let
btn
=
$
(
'<a>'
).
addClass
(
'btn btn--primary txtcenter'
)
.
text
(
'Enregistrer les absences / présences'
)
.
attr
(
'id'
,
'record_shift_absences'
);
current_shift_process_data_actions
.
append
(
btn
);
current_shift_process_data_actions
.
on
(
'click'
,
'#record_shift_absences'
,
function
(){
msg
=
"<p>Lancer le traitement des présences et absences de ce service</p>"
;
openModal
(
msg
,
function
()
{
try
{
$
.
ajax
({
url
:
'/members/record_shift_absences/'
+
s
.
id
,
dataType
:
'json'
})
.
done
(
function
(
rData
)
{
if
(
typeof
rData
.
update
!==
"undefined"
&&
rData
.
update
==
true
)
{
enqueue_message_for_next_loading
(
"Données de présences traitées."
);
location
.
reload
();
}
});
}
catch
(
e
)
{
console
.
log
(
e
);
}
},
'Confirmer'
);
});
}
else
{
current_shift_process_data_actions
.
append
(
"<em>Traitement des présences : "
+
s
.
state
+
"</em>"
);
}
current_shift_process_data_actions
.
show
();
}
rattrapage_ou_volant
=
null
;
rattrapage_ou_volant
=
null
;
shift_members
.
html
(
m_list
);
shift_members
.
html
(
m_list
);
rattrapage_wanted
.
show
();
rattrapage_wanted
.
show
();
...
@@ -401,6 +448,8 @@ function get_service_entry_data() {
...
@@ -401,6 +448,8 @@ function get_service_entry_data() {
})
})
.
done
(
function
(
rData
)
{
.
done
(
function
(
rData
)
{
info_place
.
text
(
''
);
info_place
.
text
(
''
);
reset_shift_process_actions_zone
();
var
page_title
=
pages
.
service_entry
.
find
(
'h1'
);
var
page_title
=
pages
.
service_entry
.
find
(
'h1'
);
page_title
.
text
(
'Qui es-tu ?'
);
page_title
.
text
(
'Qui es-tu ?'
);
...
...
members/urls.py
View file @
5575d2f8
...
@@ -43,6 +43,7 @@ urlpatterns = [
...
@@ -43,6 +43,7 @@ urlpatterns = [
url
(
r'^services_at_time/([0-9TZ\-\: \.]+)/([0-9\-]+)$'
,
views
.
services_at_time
),
url
(
r'^services_at_time/([0-9TZ\-\: \.]+)/([0-9\-]+)$'
,
views
.
services_at_time
),
url
(
r'^service_presence/$'
,
views
.
record_service_presence
),
url
(
r'^service_presence/$'
,
views
.
record_service_presence
),
url
(
r'^record_absences/?([0-9\-\ \:]*)$'
,
views
.
record_absences
),
url
(
r'^record_absences/?([0-9\-\ \:]*)$'
,
views
.
record_absences
),
url
(
r'^record_shift_absences/?([0-9]+)$'
,
views
.
record_shift_absences
),
url
(
r'^close_ftop_service$'
,
views
.
close_ftop_service
),
url
(
r'^close_ftop_service$'
,
views
.
close_ftop_service
),
url
(
r'^get_credentials$'
,
views
.
get_credentials
),
url
(
r'^get_credentials$'
,
views
.
get_credentials
),
url
(
r'^remove_data_from_couchdb$'
,
views
.
remove_data_from_CouchDB
),
url
(
r'^remove_data_from_couchdb$'
,
views
.
remove_data_from_CouchDB
),
...
...
members/views.py
View file @
5575d2f8
...
@@ -5,7 +5,7 @@ from outils.for_view_imports import *
...
@@ -5,7 +5,7 @@ from outils.for_view_imports import *
from
members.models
import
CagetteMember
from
members.models
import
CagetteMember
from
members.models
import
CagetteUser
from
members.models
import
CagetteUser
from
members.models
import
CagetteMembers
from
members.models
import
CagetteMembers
from
members.models
import
CagetteServices
from
members.models
import
CagetteServices
,
CagetteService
from
outils.forms
import
GenericExportMonthForm
from
outils.forms
import
GenericExportMonthForm
import
datetime
import
datetime
...
@@ -347,6 +347,10 @@ def easy_validate_shift_presence(request):
...
@@ -347,6 +347,10 @@ def easy_validate_shift_presence(request):
def
record_absences
(
request
,
date
):
def
record_absences
(
request
,
date
):
return
JsonResponse
({
'res'
:
CagetteServices
.
record_absences
(
date
)})
return
JsonResponse
({
'res'
:
CagetteServices
.
record_absences
(
date
)})
def
record_shift_absences
(
request
,
id
):
shift
=
CagetteService
(
id
)
return
JsonResponse
({
'res'
:
shift
.
record_absences
(
request
)})
def
close_ftop_service
(
request
):
def
close_ftop_service
(
request
):
"""Close the closest past FTOP service"""
"""Close the closest past FTOP service"""
return
JsonResponse
({
'res'
:
CagetteServices
.
close_ftop_service
()})
return
JsonResponse
({
'res'
:
CagetteServices
.
close_ftop_service
()})
...
...
templates/members/index.html
View file @
5575d2f8
...
@@ -141,7 +141,9 @@
...
@@ -141,7 +141,9 @@
<div
class=
"col-2 row-2"
>
<div
class=
"col-2 row-2"
>
<a
class=
"btn btn--primary"
data-next=
"first_page"
>
Retour accueil
</a>
<a
class=
"btn btn--primary"
data-next=
"first_page"
>
Retour accueil
</a>
</div>
</div>
<div
class=
"col-2 row-2"
></div>
<div
class=
"col-2 row-2 txtcenter"
>
<div
id=
"current_shift_process_data_actions"
style=
"display:none;"
></div>
</div>
<div
class=
"col-2 row-2 login_area"
>
<div
class=
"col-2 row-2 login_area"
>
{% include "common/conn_admin.html" %}
{% include "common/conn_admin.html" %}
</div>
</div>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment