Commit 184699b5 by François C.

Merge remote-tracking branch 'origin/graoucoop_tmp' into graoucoop_prod

parents 52e20915 80e725a9
Pipeline #3359 failed with stage
in 1 minute 6 seconds
...@@ -20,3 +20,4 @@ shop/errors.log ...@@ -20,3 +20,4 @@ shop/errors.log
members/settings.json members/settings.json
.DS_Store .DS_Store
exec.*.log exec.*.log
scripts/src/ci_secrets.py
\ No newline at end of file
...@@ -146,3 +146,8 @@ AMNISTIE_DATE= "2021-11-24 00:00:00" ...@@ -146,3 +146,8 @@ AMNISTIE_DATE= "2021-11-24 00:00:00"
# BDM Admin # BDM Admin
BDM_SHOW_FTOP_BUTTON = True BDM_SHOW_FTOP_BUTTON = True
# Entree
MINIMUM_SECONDS_BETWEEN_TWO_COMITEE_VALIDATION = 0
SUBSCRIPTION_ASK_FOR_SEX = True
\ No newline at end of file
"""Company specific data values.""" """Company specific data values."""
COMPANY_CODE = 'supercafoutch'
"""Odoo coop specific constants .""" """Odoo coop specific constants ."""
COMPANY_LOGO = 'https://supercafoutch.fr/wp-content/uploads/2018/02/logo_SuperCafoutch_2021_500x225.png'
MAG_NAME = ''
OFFICE_NAME = ''
MAX_BEGIN_HOUR = '19:00'
COMPANY_NAME = 'SuperCafoutch'
WELCOME_ENTRANCE_MSG = 'Bienvenue à SuperCafoutch!'
WELCOME_MAIL_SUBJECT = 'Dernière étape de votre inscription à SuperCafoutch'
WELCOME_MAIL_TEMPLATE = 'members/bienvenue.html'
ENTRANCE_WITH_LATE_MODE = True
BLOCK_SERVICE_EXCHANGE_24H_BEFORE = True
USE_NEW_MEMBERS_SPACE = True
DEFAULT_SHIFT_TYPE = 'standard'
SHOW_FTOP_BUTTON = True
USE_STANDARD_SHIFT = True
ENTRANCE_EXTRA_BUTTONS_DISPLAY = False
PREPA_ODOO_URL = '/members/prepa-odoo/'
BDM_SHOW_FTOP_BUTTON = True
EMAIL_DOMAIN = 'supercafoutch.fr' EMAIL_DOMAIN = 'supercafoutch.fr'
CAP_JOURNAL_ID = 9 CAP_JOURNAL_ID = 9
CAP_APPELE_NON_VERSE_ACCOUNT_ID = 529 CAP_APPELE_NON_VERSE_ACCOUNT_ID = 529
...@@ -18,6 +36,7 @@ PARTS_A_PRICE_UNIT = PARTS_PRICE_UNIT ...@@ -18,6 +36,7 @@ PARTS_A_PRICE_UNIT = PARTS_PRICE_UNIT
EXPORT_COMPTA_FORMAT = 'Quadratus' EXPORT_COMPTA_FORMAT = 'Quadratus'
COOP_BARCODE_RULE_ID = 11 COOP_BARCODE_RULE_ID = 11
ASSOCIATE_BARCODE_RULE_ID = 12
CHECK_PAYMENT_ID = 8 CHECK_PAYMENT_ID = 8
VIREMENT_PAYMENT_ID = 16 VIREMENT_PAYMENT_ID = 16
...@@ -36,12 +55,26 @@ FLV_CSV_NB = 2 ...@@ -36,12 +55,26 @@ FLV_CSV_NB = 2
COEFF_MAG_ID = 1 COEFF_MAG_ID = 1
DAV_PATH = '/shared_dir/dav'
RECEPTION_PDT_LABELS_FN = 'print_product_labels()' RECEPTION_PDT_LABELS_FN = 'print_product_labels()'
RECEPTION_PDT_LABELS_TEXT = 'Cliquez sur ce bouton pour imprimer les étiquettes code-barres à coller sur les produits' RECEPTION_PDT_LABELS_TEXT = 'Cliquez sur ce bouton pour imprimer les étiquettes code-barres à coller sur les produits'
RECEPTION_PDT_LABELS_BTN_TEXT = 'Lancer l\'impression' RECEPTION_PDT_LABELS_BTN_TEXT = 'Lancer l\'impression'
RECEPTION_SHELF_LABEL_PRINT = False RECEPTION_SHELF_LABEL_PRINT = False
FIXED_BARCODE_PREFIX = '0491' FIXED_BARCODE_PREFIX = '0491'
DISPLAY_COL_AUTRES = False
RECEPTION_ADD_ALL_LEFT_IS_GOOD_QTIES = True
RECEPTION_ADD_ALL_LEFT_IS_GOOD_PRICES = True
RECEPTION_ADD_PRODUCTS_PSWD = 'pass2makeApause'
RECEPTION_UPDATE_QTY_PSWD = 'pass2makeApause'
NO_BARCODE_MAIL_SUBJECT = " Articles sans codebarre de la commande : {}"
NO_BARCODE_MAIL_MSG = """
Bonjour {0},
Vous avez crée la commande {1}, le {2}, pour {3}
Voici la liste des codes barres qui manquent :
"""
ASSOCIATE_MEMBER_SHIFT = 585
SUBSCRIPTION_PAYMENT_MEANINGS = [ SUBSCRIPTION_PAYMENT_MEANINGS = [
{'code': 'cash', 'title': 'Espèces', 'journal_id': CASH_PAYMENT_ID}, {'code': 'cash', 'title': 'Espèces', 'journal_id': CASH_PAYMENT_ID},
...@@ -90,9 +123,42 @@ DISCOUNT_SHELFS_IDS = [] ...@@ -90,9 +123,42 @@ DISCOUNT_SHELFS_IDS = []
FL_SHELFS = [] FL_SHELFS = []
VRAC_SHELFS = [] VRAC_SHELFS = []
# Should block service exchange if old service is happening in less than 24h
BLOCK_SERVICE_EXCHANGE_24H_BEFORE = False SHIFT_INFO = """Un service est une plage de trois heures un jour en particulier, par exemple : le mardi 25/09/2018 à 13h15.
<br />A l'inverse, un créneau est une plage de trois heures régulière, par exemple, tous les mardi de semaine A à 13h15."""
PB_INSTRUCTIONS = """Si j'ai un problème, que je suis désinscrit, que je veux changer de créneaux ou quoi que ce soit, merci de ..."""
ENTRANCE_COME_FOR_SHOPING_MSG = "Bienvenue dans ton magasin !"
INPUT_PHONE_PATTERN = "^(((\+33(-| )\d{1})|\d{2})(\.| )\d{2}(\.| )\d{2}(\.| )\d{2}(\.| )\d{2})|(0\d{9})$"
CONFIRME_PRESENT_BTN = 'Clique ici pour valider ta présence'
ENTRANCE_WITH_LATE_MODE = True
# Members space / shifts
UNSUBSCRIBED_MSG = 'Vous êtes désincrit·e, merci de .....'
UNSUBSCRIBED_FORM_LINK = ''
SHIFT_EXCHANGE_DAYS_TO_HIDE = ''
ENTRANCE_VALIDATE_PRESENCE_MESSAGE = """
<div class="explanations">
Ta présence a bien été validée !
</div>
Ton prochain service <span class="service_verb">est prévu</span> le <span class="next_shift"></span>
"""
MEMBERS_SPACE_FAQ_TEMPLATE = 'members_space/supercafoutch/faq.html'
MEMBERS_SPACE_HOME_TEMPLATE = 'members_space/supercafoutch/home.html'
MEMBERS_SPACE_SHOW_UNDERSTAND_MY_STATUS = False
BLOCK_ACTIONS_FOR_ATTACHED_PEOPLE = False
CAN_ADD_SHIFT = True
ALLOW_FOUR_DIGITS_IN_RECEPTION_PRICE = True ALLOW_FOUR_DIGITS_IN_RECEPTION_PRICE = True
REMOVE_15_MINUTES_AT_SHIFT_END = False REMOVE_15_MINUTES_AT_SHIFT_END = False
# Fix incompatibility between meal voucher module and an old version of michel bibikoff product label printer software used in supercafoutch
SHOW_MEAL_VOUCHER_OK_LINE_IN_PRODUCT_INFO_FOR_LABEL = False
LOSSES_LOC_ID = 33
LOSSES_PICKING_TYPE_ID = 10
AUTOCONSO_LOC_ID = 27
AUTOCONSO_PICKING_TYPE_ID = 7
SUBSCRIPTION_ASK_FOR_SEX = True
SUBSCRIPTION_ASK_FOR_JOB = True
\ No newline at end of file
...@@ -403,6 +403,8 @@ class CagetteMember(models.Model): ...@@ -403,6 +403,8 @@ class CagetteMember(models.Model):
} }
if ('sex' in post_data): if ('sex' in post_data):
received_data['sex'] = post_data['sex'] received_data['sex'] = post_data['sex']
if ('function' in post_data):
received_data['function'] = post_data['function']
if 'street2' in post_data: if 'street2' in post_data:
received_data['street2'] = post_data['street2'] received_data['street2'] = post_data['street2']
if 'phone' in post_data: if 'phone' in post_data:
...@@ -450,11 +452,15 @@ class CagetteMember(models.Model): ...@@ -450,11 +452,15 @@ class CagetteMember(models.Model):
partner_id = None partner_id = None
name_sep = getattr(settings, 'SUBSCRIPTION_NAME_SEP', ' ') name_sep = getattr(settings, 'SUBSCRIPTION_NAME_SEP', ' ')
ask_4_sex = getattr(settings, 'SUBSCRIPTION_ASK_FOR_SEX', False) ask_4_sex = getattr(settings, 'SUBSCRIPTION_ASK_FOR_SEX', False)
ask_4_job = getattr(settings, 'SUBSCRIPTION_ASK_FOR_JOB', False)
concat_order = getattr(settings, 'CONCAT_NAME_ORDER', 'FL') concat_order = getattr(settings, 'CONCAT_NAME_ORDER', 'FL')
sex = 'o' sex = 'o'
function = ''
if ask_4_sex is True: if ask_4_sex is True:
sex = post_data['sex'] sex = post_data['sex']
if ask_4_job is True:
function = post_data['function']
# With input type="date", transmitted value is YYYY-mm-dd # With input type="date", transmitted value is YYYY-mm-dd
# But, it could be dd/mm/YYYY if not supported by browser # But, it could be dd/mm/YYYY if not supported by browser
...@@ -484,7 +490,8 @@ class CagetteMember(models.Model): ...@@ -484,7 +490,8 @@ class CagetteMember(models.Model):
'zip': post_data['zip'], 'zip': post_data['zip'],
'city': post_data['city'], 'city': post_data['city'],
'phone': format_phone_number(post_data['mobile']), # Because list view default show Phone and people mainly gives mobile 'phone': format_phone_number(post_data['mobile']), # Because list view default show Phone and people mainly gives mobile
'barcode_rule_id': settings.COOP_BARCODE_RULE_ID 'barcode_rule_id': settings.COOP_BARCODE_RULE_ID,
'function': function
} }
if ('_id' in post_data): if ('_id' in post_data):
f['email'] = post_data['_id'] f['email'] = post_data['_id']
...@@ -556,7 +563,8 @@ class CagetteMember(models.Model): ...@@ -556,7 +563,8 @@ class CagetteMember(models.Model):
'phone': format_phone_number(post_data['mobile']), # Because list view default show Phone and people mainly gives mobile 'phone': format_phone_number(post_data['mobile']), # Because list view default show Phone and people mainly gives mobile
'barcode_rule_id': settings.ASSOCIATE_BARCODE_RULE_ID, 'barcode_rule_id': settings.ASSOCIATE_BARCODE_RULE_ID,
'parent_id' : post_data['parent_id'], 'parent_id' : post_data['parent_id'],
'is_associated_people': True 'is_associated_people': True,
'function': function
} }
associated_member_id = api.create('res.partner', associated_member) associated_member_id = api.create('res.partner', associated_member)
am = CagetteMember(associated_member_id) am = CagetteMember(associated_member_id)
...@@ -845,8 +853,10 @@ class CagetteMember(models.Model): ...@@ -845,8 +853,10 @@ class CagetteMember(models.Model):
elif search_type == "makeups_data": elif search_type == "makeups_data":
fields = CagetteMember.m_short_default_fields fields = CagetteMember.m_short_default_fields
fields = fields + ['shift_type', 'makeups_to_do', 'display_ftop_points', 'display_std_points', 'shift_type'] fields = fields + ['shift_type', 'makeups_to_do', 'display_ftop_points', 'display_std_points', 'shift_type']
return api.search_read('res.partner', cond, fields) cond.append(['shift_type', '=', 'standard'])
res = api.search_read('res.partner', cond, fields)
CagetteMembers.add_makeups_to_come_to_member_data(res)
return res
elif search_type == "shift_template_data": elif search_type == "shift_template_data":
fields = CagetteMember.m_short_default_fields fields = CagetteMember.m_short_default_fields
fields = fields + ['id', 'makeups_to_do', 'cooperative_state','parent_name'] fields = fields + ['id', 'makeups_to_do', 'cooperative_state','parent_name']
...@@ -1282,9 +1292,40 @@ class CagetteMembers(models.Model): ...@@ -1282,9 +1292,40 @@ class CagetteMembers(models.Model):
fields = ['id', 'name', 'display_std_points', 'display_ftop_points', 'shift_type', 'makeups_to_do', 'date_delay_stop'] fields = ['id', 'name', 'display_std_points', 'display_ftop_points', 'shift_type', 'makeups_to_do', 'date_delay_stop']
res = api.search_read('res.partner', cond, fields) res = api.search_read('res.partner', cond, fields)
# There are two things we need to do now :
# 1 : fetching members with no makeups to do but with some makeups to come
# 2 : providing makeups to come to all members
cs = CagetteShift()
makeups_to_come_per_partner = cs.get_partners_with_makeups_to_come()
# 1 : fetching members with no makeups to do but with some makeups to come
cond = [['makeups_to_do', '=', 0], ['id', 'in', list(makeups_to_come_per_partner.keys())]]
if len(ids) > 0:
cond.append(['id','in', ids])
res = res + api.search_read('res.partner', cond, fields)
# 2 : providing makeups to come to all members
for idx, partner in enumerate(res):
if partner['id'] in makeups_to_come_per_partner:
res[idx]['makeups_to_come'] = makeups_to_come_per_partner[partner['id']]
else:
res[idx]['makeups_to_come'] = 0
return res return res
@staticmethod @staticmethod
def add_makeups_to_come_to_member_data(res):
if res:
cs = CagetteShift()
for idx, partner in enumerate(res):
shift_data = cs.get_shift_partner(int(partner['id']))
res[idx]['makeups_to_come'] = sum(1 for value in shift_data if value['is_makeup'])
@staticmethod
def get_attached_members(): def get_attached_members():
api = OdooAPI() api = OdooAPI()
cond = [['is_associated_people','=', True]] cond = [['is_associated_people','=', True]]
...@@ -1343,3 +1384,6 @@ class CagetteUser(models.Model): ...@@ -1343,3 +1384,6 @@ class CagetteUser(models.Model):
pass pass
return answer return answer
from shifts.models import CagetteShift
\ No newline at end of file
...@@ -29,7 +29,7 @@ video {max-width:none;} ...@@ -29,7 +29,7 @@ video {max-width:none;}
#webcam_button {min-width:115px; max-width:115px;} #webcam_button {min-width:115px; max-width:115px;}
#multi_results_preview button {margin:2px;} #multi_results_preview button {margin:2px;}
#member_slide {grid-gap:0;padding:5px;display:none;} #member_slide {grid-gap:0;padding:5px;display:none;}
#member_slide .coop-info {background: #fbfbd5;} #member_slide .coop-info {background: #449d44;}
#image_medium {width:128px;float:left;} #image_medium {width:128px;float:left;}
#image_medium:hover {cursor: url(/static/img/ip-camera.png), url(/static/img/ip-camera.svg) 5 5, pointer;} #image_medium:hover {cursor: url(/static/img/ip-camera.png), url(/static/img/ip-camera.svg) 5 5, pointer;}
#barcode {height:128px;} #barcode {height:128px;}
......
...@@ -41,10 +41,10 @@ function display_makeups_members() { ...@@ -41,10 +41,10 @@ function display_makeups_members() {
$('#makeups_members_table').empty(); $('#makeups_members_table').empty();
} }
// Remove members with 0 makeups to do // Remove members with 0 makeups to do and 0 makeups to come
ids_to_remove = []; ids_to_remove = [];
for (member_item of makeups_members) { for (let member_item of makeups_members) {
if (member_item.makeups_to_do == 0) { if (member_item.makeups_to_do === 0 && member_item.makeups_to_come === 0) {
ids_to_remove.push(member_item.id); ids_to_remove.push(member_item.id);
} }
} }
...@@ -59,8 +59,12 @@ function display_makeups_members() { ...@@ -59,8 +59,12 @@ function display_makeups_members() {
title: "", title: "",
className: "dt-body-center", className: "dt-body-center",
orderable: false, orderable: false,
render: function (data) { render: function (data, type, full) {
return `<input type="checkbox" class="select_member_cb" id="select_member_${data}" value="${data}">`; var html = ``;
if(full.shift_type == 'standard') { //prevent checking of ftop members so that they can't be target of makeup removal
html = `<input type="checkbox" class="select_member_cb" id="select_member_${data}" value="${data}">`;
}
return html;
}, },
width: "3%" width: "3%"
}, },
...@@ -89,33 +93,62 @@ function display_makeups_members() { ...@@ -89,33 +93,62 @@ function display_makeups_members() {
}, },
{ {
data: "shift_type", data: "shift_type",
title: "<div class='title_center'>Nb de points</div>", title: "<div class='title_center'>Type de créneau</div>",
className: "dt-body-center", className: "dt-body-center",
width: "5%", width: "5%",
render: function (data, type, row) { render: function (data) {
if (data == 'ftop') { if (data == 'ftop') {
return row.display_ftop_points; return (has_committe_shift === "True") ? "Comités" : "Volant";
} else if (data == 'standard') { } else {
return row.display_std_points; return 'ABCD';
} }
}
return null; },
{
data: "makeups_to_come",
title: "<div class='title_center' title=\"Services de rattrapage à venir déjà choisis depuis l'espace membre.\">Rattrapages à venir déjà choisis</div>",
className: "dt-body-center",
width: "8%",
render: function (data, type, full) {
return `<b>${data}</b>`;
} }
}, },
{ {
data: "makeups_to_do", data: "makeups_to_do",
title: "<div class='title_center'>Nb rattrapages</div>", title: "<div class='title_center' title=\"Rattrapages en attente de sélection sur l'espace membre.\">Rattrapages pas encore choisis</div>",
className: "dt-body-center", className: "dt-body-center",
width: "10%", width: "10%",
render: function (data, type, full) { render: function (data, type, full) {
return `<b>${data}</b> var html = `<b>${data}</b>`
if(full.shift_type == 'standard') {
if(data > 0) {
html += `
<button class="decrement_makeup btn--primary" id="decrement_member_${full.id}"> <button class="decrement_makeup btn--primary" id="decrement_member_${full.id}">
<i class="fas fa-minus"></i> <i class="fas fa-minus"></i>
</button> </button>`;
}
html += `
<button class="increment_makeup btn--primary" id="increment_member_${full.id}"> <button class="increment_makeup btn--primary" id="increment_member_${full.id}">
<i class="fas fa-plus"></i> <i class="fas fa-plus"></i>
</button>`; </button>`;
} }
return html;
}
},
{
data: "shift_type",
title: "<div class='title_center' title=\"Information d'interprétation délicate réservée aux utilisateurs avancés.\">Points (avancé)</div>",
className: "dt-body-center",
width: "5%",
render: function (data, type, row) {
if (data == 'ftop') {
return row.display_ftop_points;
} else if (data == 'standard') {
return row.display_std_points;
}
return null;
}
} }
], ],
aLengthMenu: [ aLengthMenu: [
...@@ -292,21 +325,6 @@ function update_members_makeups(member_ids, action, description) { ...@@ -292,21 +325,6 @@ function update_members_makeups(member_ids, action, description) {
} else { } else {
makeups_members[member_index].makeups_to_do -= 1; makeups_members[member_index].makeups_to_do -= 1;
} }
if (makeups_members[member_index].shift_type === 'standard') {
if (action === "increment") {
if (makeups_members[member_index].display_std_points >= -1)
makeups_members[member_index].display_std_points -= 1;
} else if (makeups_members[member_index].display_std_points < 0) {
makeups_members[member_index].display_std_points += 1;
}
} else {
if (action === "increment") {
if (makeups_members[member_index].display_ftop_points >= -1)
makeups_members[member_index].display_ftop_points -= 1;
} else {
makeups_members[member_index].display_ftop_points += 1;
}
}
data.push({ data.push({
member_id: mid, member_id: mid,
...@@ -325,8 +343,17 @@ function update_members_makeups(member_ids, action, description) { ...@@ -325,8 +343,17 @@ function update_members_makeups(member_ids, action, description) {
dataType:"json", dataType:"json",
traditional: true, traditional: true,
contentType: "application/json; charset=utf-8", contentType: "application/json; charset=utf-8",
success: function() { success: function(res) {
selected_rows = []; selected_rows = [];
for (element of res['res']) {
member_index = makeups_members.findIndex(m => m.id == element['mid']);
if (makeups_members[member_index].shift_type === 'standard') {
makeups_members[member_index].display_std_points = element['standard_points']
}
//there is no point change when shift_type is not standard
//and I think operation on rattrapages should definitely be forbidden for ftop because it
//has no sense !!
}
display_makeups_members(); display_makeups_members();
closeModal(); closeModal();
}, },
...@@ -433,7 +460,8 @@ function display_possible_members() { ...@@ -433,7 +460,8 @@ function display_possible_members() {
makeups_to_do: 0, makeups_to_do: 0,
shift_type: member.shift_type, shift_type: member.shift_type,
display_std_points: member.display_std_points, display_std_points: member.display_std_points,
display_ftop_points: member.display_ftop_points display_ftop_points: member.display_ftop_points,
makeups_to_come: member.makeups_to_come
}); });
let modal_template = $("#modal_incr_makeup_counter"); let modal_template = $("#modal_incr_makeup_counter");
......
...@@ -64,7 +64,7 @@ function remove_from_shift_template() { ...@@ -64,7 +64,7 @@ function remove_from_shift_template() {
* Send the request to register a member to a shift template. * Send the request to register a member to a shift template.
* Ask to unsuscribe first if the member was subscribed. * Ask to unsuscribe first if the member was subscribed.
* *
* @param {int} shift_type 1 === standard ; 2 === ftop * @param {int} shift_type 1 === standard ; 2 === ftop ; 3 === exempté
* @param {int} shift_template_id null for ftop shift type * @param {int} shift_template_id null for ftop shift type
* @param {String} shift_template_name selected shift template name * @param {String} shift_template_name selected shift template name
*/ */
...@@ -92,6 +92,9 @@ function shift_subscrition(shift_type, shift_template_id = null, shift_template_ ...@@ -92,6 +92,9 @@ function shift_subscrition(shift_type, shift_template_id = null, shift_template_
stdata.name stdata.name
]; ];
selected_member.cooperative_state = data.cooperative_state; selected_member.cooperative_state = data.cooperative_state;
if(data.makeups_to_do !== undefined) {
selected_member.makeups_to_do = data.makeups_to_do;
}
display_member_info(); display_member_info();
$("#shifts_calendar_area").hide(); $("#shifts_calendar_area").hide();
...@@ -216,6 +219,22 @@ function set_subscription_area() { ...@@ -216,6 +219,22 @@ function set_subscription_area() {
false false
); );
}); });
$(document).off("click", "#shifts_calendar_area button[data-select='Exemption']");
$(document).on("click", "#shifts_calendar_area button[data-select='Exemption']", function() {
// Subscribe to comitee/ftop shift
msg = `Inscrire ${selected_member.name} en Éxempté ?`;
openModal(
msg,
() => {
shift_subscrition(3);
},
"Confirmer",
false
);
});
$(document).off("click", ".shift"); $(document).off("click", ".shift");
$(document).on("click", ".shift", function() { $(document).on("click", ".shift", function() {
// Subscribe to shift template // Subscribe to shift template
......
...@@ -102,16 +102,21 @@ function display_member_shifts() { ...@@ -102,16 +102,21 @@ function display_member_shifts() {
const shift_reg_id = row_data.id; const shift_reg_id = row_data.id;
const shift_is_makeup = row_data.is_makeup; const shift_is_makeup = row_data.is_makeup;
let msg = `<p>Enlever la présence de <b>${member.name}</b> au service du <b>${row_data.shift_id[1]}</b> ?</p>`; let modal_template = $("#modal_delete_shift_registration");
modal_template.find(".member_name").text(member.name);
modal_template.find(".service_name").text(row_data.shift_id[1]);
if (shift_is_makeup === true) { if (shift_is_makeup === true) {
msg += `<p><i class="fas fa-exclamation-triangle"></i> Ce service est un rattrapage. Le supprimer ajoutera un point au compteur de ce.tte membre.</p>`; modal_template.find("#makeup_case_explanation").show();
} }
openModal( openModal(
msg, $("#modal_delete_shift_registration").html(),
() => { () => {
delete_shift_registration(shift_reg_id, shift_is_makeup); delete_shift_registration(
shift_reg_id,
shift_is_makeup,
($("#cancellation-explanation")[0].value || "pas d'explication") + ' : ' + ($("#cancellation-signature")[0].value || "auteur inconnu"),
);
}, },
"Confirmer", "Confirmer",
false false
...@@ -123,15 +128,17 @@ function display_member_shifts() { ...@@ -123,15 +128,17 @@ function display_member_shifts() {
* Send request to delete shift registration * Send request to delete shift registration
* @param {Int} shift_reg_id Id of the shift_registration to delete * @param {Int} shift_reg_id Id of the shift_registration to delete
* @param {Boolean} shift_is_makeup Is the shift a makeup? * @param {Boolean} shift_is_makeup Is the shift a makeup?
* @param {String} description explanation and signature for the cancellation from bdm
*/ */
function delete_shift_registration(shift_reg_id, shift_is_makeup) { function delete_shift_registration(shift_reg_id, shift_is_makeup, description) {
openModal(); openModal();
data = { data = {
member_id: selected_member.id, member_id: selected_member.id,
member_shift_type: selected_member.shift_type, member_shift_type: selected_member.shift_type,
shift_registration_id: shift_reg_id, shift_registration_id: shift_reg_id,
shift_is_makeup: shift_is_makeup shift_is_makeup: shift_is_makeup,
cancellation_description: description
}; };
$.ajax({ $.ajax({
......
...@@ -17,6 +17,7 @@ var latest_odoo_coop_bb = null, ...@@ -17,6 +17,7 @@ var latest_odoo_coop_bb = null,
subs_cap = $('#subs_cap'), subs_cap = $('#subs_cap'),
m_barcode = $('#m_barcode'), m_barcode = $('#m_barcode'),
sex = $('#sex'), sex = $('#sex'),
job = $('#job'),
self_records = [], self_records = [],
selected_associate=null, selected_associate=null,
associated_old_choice= null, associated_old_choice= null,
...@@ -158,7 +159,7 @@ function swipe_to_shift_choice() { ...@@ -158,7 +159,7 @@ function swipe_to_shift_choice() {
retrieve_and_draw_shift_tempates(); retrieve_and_draw_shift_tempates();
schoice_view.show(); schoice_view.show();
} }
function _really_save_new_coop(email, fname, lname, cap, pm, cn, bc, msex) { function _really_save_new_coop(email, fname, lname, cap, pm, cn, bc, msex, mjob) {
var coop = current_coop || {}; var coop = current_coop || {};
...@@ -214,6 +215,7 @@ function _really_save_new_coop(email, fname, lname, cap, pm, cn, bc, msex) { ...@@ -214,6 +215,7 @@ function _really_save_new_coop(email, fname, lname, cap, pm, cn, bc, msex) {
if (m_barcode.length > 0) coop.m_barcode = bc; if (m_barcode.length > 0) coop.m_barcode = bc;
if (sex.length > 0) coop.sex = msex; if (sex.length > 0) coop.sex = msex;
if (job.length > 0) coop.function = mjob;
coop.validation_state = "to_fill"; coop.validation_state = "to_fill";
dbc.put(coop, function callback(err, result) { dbc.put(coop, function callback(err, result) {
if (!err) { if (!err) {
...@@ -244,6 +246,7 @@ function store_new_coop(event) { ...@@ -244,6 +246,7 @@ function store_new_coop(event) {
var errors = [], var errors = [],
bc = '', // barcode may not be present bc = '', // barcode may not be present
msex = '', // sex may not be present msex = '', // sex may not be present
mjob = '', //job may not be present
active_asso_area = $('#associate_area .choice_active'); // need to ckeck if associated data are available active_asso_area = $('#associate_area .choice_active'); // need to ckeck if associated data are available
// 1- Un coop avec le meme mail ne doit pas exister dans odoo (dans base intermediaire, le cas est géré par l'erreur à l'enregistrement) // 1- Un coop avec le meme mail ne doit pas exister dans odoo (dans base intermediaire, le cas est géré par l'erreur à l'enregistrement)
let email = $('input[name="email"]').val() let email = $('input[name="email"]').val()
...@@ -260,6 +263,9 @@ function store_new_coop(event) { ...@@ -260,6 +263,9 @@ function store_new_coop(event) {
if (sex.length > 0) { if (sex.length > 0) {
msex = $('input[name="sex"]:checked').val(); msex = $('input[name="sex"]:checked').val();
} }
if (job.length > 0) {
mjob = $('input[name="job"]').val();
}
if (payment_meaning.val() == 'ch') { if (payment_meaning.val() == 'ch') {
if (ch_qty.val() <1) { if (ch_qty.val() <1) {
...@@ -319,7 +325,7 @@ function store_new_coop(event) { ...@@ -319,7 +325,7 @@ function store_new_coop(event) {
if (errors.length == 0) { if (errors.length == 0) {
_really_save_new_coop( _really_save_new_coop(
email, fname, lname, email, fname, lname,
subs_cap.val(), payment_meaning.val(), ch_qty.val(), bc, msex subs_cap.val(), payment_meaning.val(), ch_qty.val(), bc, msex, mjob
); );
} else { } else {
...@@ -331,7 +337,7 @@ function store_new_coop(event) { ...@@ -331,7 +337,7 @@ function store_new_coop(event) {
if (errors.length == 0) { if (errors.length == 0) {
_really_save_new_coop( _really_save_new_coop(
email, fname, lname, email, fname, lname,
subs_cap.val(), payment_meaning.val(), ch_qty.val(), bc, msex subs_cap.val(), payment_meaning.val(), ch_qty.val(), bc, msex, mjob
); );
} else { } else {
...@@ -405,6 +411,9 @@ function modify_current_coop() { ...@@ -405,6 +411,9 @@ function modify_current_coop() {
reset_sex_radios(); reset_sex_radios();
$('#' + current_coop.sex + '_sex').prop('checked', true); $('#' + current_coop.sex + '_sex').prop('checked', true);
} }
if (job.length > 0) {
ncoop_view.find('input[name="job"]').val(current_coop.function);
}
ncoop_view.show(); ncoop_view.show();
} }
......
...@@ -101,18 +101,22 @@ function fill_member_slide(member) { ...@@ -101,18 +101,22 @@ function fill_member_slide(member) {
} }
html_elts.image_medium.html('<img src="'+img_src+'" width="128" />'); html_elts.image_medium.html('<img src="'+img_src+'" width="128" />');
html_elts.cooperative_state.html(member.cooperative_state); html_elts.cooperative_state.html(member.cooperative_state);
if (member.cooperative_state == 'Rattrapage') { if (member.cooperative_state == 'Suspendu(e)') {
var explanation = "Tu as dû manquer un service! Pour pouvoir faire tes courses aujourd'hui, tu dois d'abord sélectionner un rattrapage sur ton espace membre."; var explanation = "Tu as dû manquer un service! Pour pouvoir faire tes courses aujourd'hui, tu dois d'abord sélectionner un rattrapage sur ton espace membre.";
html_elts.status_explanation.html(explanation); html_elts.status_explanation.html(explanation);
} }
if (member.cooperative_state == 'Désinscrit(e)' || member.cooperative_state == 'Parti(e)') coop_info.addClass('b_red');
else if (member.cooperative_state == 'En alerte' || member.cooperative_state == 'Délai accordé' || member.cooperative_state == 'Rattrapage') coop_info.addClass('b_orange'); if (member.cooperative_state == 'Désinscrit(e)' || member.cooperative_state == 'Parti(e)' || member.cooperative_state == 'Rattrapage' || member.cooperative_state == 'Suspendu(e)') {
coop_info.addClass('b_red');
}
else if (member.cooperative_state == 'En alerte' || member.cooperative_state == 'Délai accordé') {
coop_info.addClass('b_yellow')
}
if (member.shifts.length > 0) { if (member.shifts.length > 0) {
html_elts.next_shifts.append('Prochains services : '); html_elts.next_shifts.append('Prochains services : ');
var slist = $('<ul>'); var slist = $('<ul>');
for (i in member.shifts) { for (i in member.shifts) {
var s = $('<li>').text(member.shifts[i].start); var s = $('<li>').text(member.shifts[i].start);
......
...@@ -163,6 +163,7 @@ function submit_full_coop_form() { ...@@ -163,6 +163,7 @@ function submit_full_coop_form() {
var form_data = new FormData(form.get(0)), var form_data = new FormData(form.get(0)),
m_barcode = form.find('[name="m_barcode"]'), m_barcode = form.find('[name="m_barcode"]'),
sex = $('#sex'), sex = $('#sex'),
job = $('#job'),
has_empty_values = false; has_empty_values = false;
for (var pair of form_data.entries()) { for (var pair of form_data.entries()) {
...@@ -205,6 +206,12 @@ function submit_full_coop_form() { ...@@ -205,6 +206,12 @@ function submit_full_coop_form() {
if (sex.length > 0) { if (sex.length > 0) {
form_data.set('sex', $('input[name="sex"]:checked').val()); form_data.set('sex', $('input[name="sex"]:checked').val());
} }
if (job.length > 0) {
form_data.set(
'function',
vform.find('input[name="job"]').val()
);
}
post_form( post_form(
'/members/coop_validated_data', form_data, '/members/coop_validated_data', form_data,
function(err) { function(err) {
...@@ -246,7 +253,8 @@ function save_current_coop(callback) { ...@@ -246,7 +253,8 @@ function save_current_coop(callback) {
let form = coop_validation_form, let form = coop_validation_form,
_id = form.find('[name="email"]').val(), _id = form.find('[name="email"]').val(),
m_barcode = form.find('[name="m_barcode"]'), m_barcode = form.find('[name="m_barcode"]'),
sex = form.find('[name="sex"]'); sex = form.find('[name="sex"]'),
job = form.find('[name="job"]');
if (current_coop != null && _id.length > 0) { if (current_coop != null && _id.length > 0) {
//Birthdate verification //Birthdate verification
...@@ -300,6 +308,9 @@ function save_current_coop(callback) { ...@@ -300,6 +308,9 @@ function save_current_coop(callback) {
current_coop.sex = $('input[name="sex"]:checked').val(); current_coop.sex = $('input[name="sex"]:checked').val();
if (typeof current_coop.sex == "undefined") sex_error = true; if (typeof current_coop.sex == "undefined") sex_error = true;
} }
if (job.length > 0) {
current_coop.function = $('input[name="job"]').val();
}
if (street2_input.length > 0) { if (street2_input.length > 0) {
current_coop.street2 = street2_input.val(); current_coop.street2 = street2_input.val();
} }
......
...@@ -28,7 +28,8 @@ function display_current_coop_form() { ...@@ -28,7 +28,8 @@ function display_current_coop_form() {
chgt_shift_btn = $('#change_shift_template'); chgt_shift_btn = $('#change_shift_template');
var ftop_shift = $('#choosen_shift [name="ftop"]'), var ftop_shift = $('#choosen_shift [name="ftop"]'),
m_barcode = form.find('[name="m_barcode"]'), m_barcode = form.find('[name="m_barcode"]'),
sex = $('#sex'); sex = $('#sex'),
job = $('#job');
let street2_input = form.find('[name="street2"]'), let street2_input = form.find('[name="street2"]'),
phone_input = form.find('[name="phone"]'); phone_input = form.find('[name="phone"]');
...@@ -52,6 +53,9 @@ function display_current_coop_form() { ...@@ -52,6 +53,9 @@ function display_current_coop_form() {
if (sex.length > 0 && typeof current_coop.sex != "undefined") { if (sex.length > 0 && typeof current_coop.sex != "undefined") {
$('input[name="sex"][value="' + current_coop.sex + '"]').prop('checked', true); $('input[name="sex"][value="' + current_coop.sex + '"]').prop('checked', true);
} }
if (job.length > 0) {
form.find('[name="job"]').val(current_coop.function);
}
// form.find('[name="barcode_base"]').val(current_coop.barcode_base); // form.find('[name="barcode_base"]').val(current_coop.barcode_base);
form.find('[name="email"]').val(current_coop._id); form.find('[name="email"]').val(current_coop._id);
if (current_coop.shift_template && if (current_coop.shift_template &&
...@@ -100,13 +104,11 @@ function display_current_coop_form() { ...@@ -100,13 +104,11 @@ function display_current_coop_form() {
$('#checks').show(); $('#checks').show();
for (var i = 1; i <= current_coop.checks_nb; i++) { for (var i = 1; i <= current_coop.checks_nb; i++) {
$(check_details).append('<p>Chèque #' + i +' : <input type="text" name="check_' + i + '" class="b_green check_item" required/> € </p>'); $(check_details).append('<p>Chèque #' + i + ' : <input type="text" name="check_' + i + '" class="b_green check_item" required/> € </p>');
} }
} }
} }
var show_change_shift = false;
if (current_coop.shift_template) { if (current_coop.shift_template) {
var st = current_coop.shift_template.data; var st = current_coop.shift_template.data;
...@@ -121,25 +123,20 @@ function display_current_coop_form() { ...@@ -121,25 +123,20 @@ function display_current_coop_form() {
place = 'Bureau'; place = 'Bureau';
} }
form.find('[name="place"]').val(place); form.find('[name="place"]').val(place);
if (current_coop.coop_msg) {
show_change_shift = true;
}
} else {
show_change_shift = true;
} }
let show_change_shift = current_coop.validation_state === 'waiting_validation_employee';
if (show_change_shift == true) { if (show_change_shift == true) {
chgt_shift_btn.show(); chgt_shift_btn.show();
chgt_shift_btn.on('click', open_shift_choice); chgt_shift_btn.on('click', open_shift_choice);
} }
if (typeof(coop_page) != "undefined") { if (typeof (coop_page) != "undefined") {
coop_page.show(); coop_page.show();
} }
} }
$('#payment_meaning').change(function() { $('#payment_meaning').change(function () {
if ($(this).val() == 'ch') { if ($(this).val() == 'ch') {
show_checks_nb(); show_checks_nb();
} else { } else {
......
...@@ -48,6 +48,15 @@ def index(request): ...@@ -48,6 +48,15 @@ def index(request):
return HttpResponse("Le créneau des comités n'est pas configuré dans Odoo !") return HttpResponse("Le créneau des comités n'est pas configuré dans Odoo !")
else: else:
context['committees_shift_id'] = committees_shift_id context['committees_shift_id'] = committees_shift_id
if getattr(settings, 'USE_EXEMPTIONS_SHIFT_TEMPLATE', False) is True:
exemptions_shift_id = CagetteServices.get_exemptions_shift_id()
if exemptions_shift_id is None:
return HttpResponse("Le créneau des exemptions n'est pas configuré dans Odoo !")
else:
context['exemptions_shift_id'] = exemptions_shift_id
else:
context['exemptions_shift_id'] = 0
if 'no_picture_member_advice' in msettings: if 'no_picture_member_advice' in msettings:
if len(msettings['no_picture_member_advice']['value']) > 0: if len(msettings['no_picture_member_advice']['value']) > 0:
...@@ -88,6 +97,10 @@ def inscriptions(request, type=1): ...@@ -88,6 +97,10 @@ def inscriptions(request, type=1):
template = loader.get_template('members/inscriptions.html') template = loader.get_template('members/inscriptions.html')
committees_shift_id = CagetteServices.get_committees_shift_id() committees_shift_id = CagetteServices.get_committees_shift_id()
if getattr(settings, 'USE_EXEMPTIONS_SHIFT_TEMPLATE', False) is True:
exemptions_shift_id = CagetteServices.get_exemptions_shift_id()
else:
exemptions_shift_id = 0
context = { context = {
'type': type, 'title': 'Inscriptions', 'type': type, 'title': 'Inscriptions',
'couchdb_server': settings.COUCHDB['url'], 'couchdb_server': settings.COUCHDB['url'],
...@@ -99,6 +112,7 @@ def inscriptions(request, type=1): ...@@ -99,6 +112,7 @@ def inscriptions(request, type=1):
'input_barcode': getattr(settings, 'SUBSCRIPTION_INPUT_BARCODE', False), 'input_barcode': getattr(settings, 'SUBSCRIPTION_INPUT_BARCODE', False),
'email_domain': getattr(settings, 'EMAIL_DOMAIN', 'lacagette-coop.fr'), 'email_domain': getattr(settings, 'EMAIL_DOMAIN', 'lacagette-coop.fr'),
'ask_for_sex': getattr(settings, 'SUBSCRIPTION_ASK_FOR_SEX', False), 'ask_for_sex': getattr(settings, 'SUBSCRIPTION_ASK_FOR_SEX', False),
'ask_for_job': getattr(settings, 'SUBSCRIPTION_ASK_FOR_JOB', False),
'open_on_sunday': getattr(settings, 'OPEN_ON_SUNDAY', False), 'open_on_sunday': getattr(settings, 'OPEN_ON_SUNDAY', False),
'POUCHDB_VERSION': getattr(settings, 'POUCHDB_VERSION', ''), 'POUCHDB_VERSION': getattr(settings, 'POUCHDB_VERSION', ''),
'max_chq_nb': getattr(settings, 'MAX_CHQ_NB', 12), 'max_chq_nb': getattr(settings, 'MAX_CHQ_NB', 12),
...@@ -108,6 +122,7 @@ def inscriptions(request, type=1): ...@@ -108,6 +122,7 @@ def inscriptions(request, type=1):
'can_create_binome': getattr(settings, 'CAN_CREATE_BINOME', True), 'can_create_binome': getattr(settings, 'CAN_CREATE_BINOME', True),
'prepa_odoo_url' : getattr(settings, 'PREPA_ODOO_URL', '/members/prepa-odoo'), 'prepa_odoo_url' : getattr(settings, 'PREPA_ODOO_URL', '/members/prepa-odoo'),
'committees_shift_id': committees_shift_id, 'committees_shift_id': committees_shift_id,
'exemptions_shift_id': exemptions_shift_id,
} }
response = HttpResponse(template.render(context, request)) response = HttpResponse(template.render(context, request))
...@@ -124,6 +139,12 @@ def get_shift_templates_next_shift(request, id): ...@@ -124,6 +139,12 @@ def get_shift_templates_next_shift(request, id):
def prepa_odoo(request): def prepa_odoo(request):
"""Generate coop subscription form, to be fill by BDM.""" """Generate coop subscription form, to be fill by BDM."""
template = loader.get_template('members/prepa_odoo.html') template = loader.get_template('members/prepa_odoo.html')
committees_shift_id = CagetteServices.get_committees_shift_id()
if getattr(settings, 'USE_EXEMPTIONS_SHIFT_TEMPLATE', False) is True:
exemptions_shift_id = CagetteServices.get_exemptions_shift_id()
else:
exemptions_shift_id = 0
context = {'title': 'Préparation Odoo Inscriptions', context = {'title': 'Préparation Odoo Inscriptions',
'warning_placeholder': 'Par exemple, il manque un chèque', 'warning_placeholder': 'Par exemple, il manque un chèque',
'couchdb_server': settings.COUCHDB['url'], 'couchdb_server': settings.COUCHDB['url'],
...@@ -134,10 +155,14 @@ def prepa_odoo(request): ...@@ -134,10 +155,14 @@ def prepa_odoo(request):
'input_phone_pattern': getattr(settings, 'INPUT_PHONE_PATTERN', default_input_phone_pattern), 'input_phone_pattern': getattr(settings, 'INPUT_PHONE_PATTERN', default_input_phone_pattern),
'input_barcode': getattr(settings, 'SUBSCRIPTION_INPUT_BARCODE', False), 'input_barcode': getattr(settings, 'SUBSCRIPTION_INPUT_BARCODE', False),
'ask_for_sex': getattr(settings, 'SUBSCRIPTION_ASK_FOR_SEX', False), 'ask_for_sex': getattr(settings, 'SUBSCRIPTION_ASK_FOR_SEX', False),
'ask_for_job': getattr(settings, 'SUBSCRIPTION_ASK_FOR_JOB', False),
'ask_for_street2': getattr(settings, 'SUBSCRIPTION_ADD_STREET2', False), 'ask_for_street2': getattr(settings, 'SUBSCRIPTION_ADD_STREET2', False),
'ask_for_second_phone': getattr(settings, 'SUBSCRIPTION_ADD_SECOND_PHONE', False), 'ask_for_second_phone': getattr(settings, 'SUBSCRIPTION_ADD_SECOND_PHONE', False),
'show_ftop_button': getattr(settings, 'SHOW_FTOP_BUTTON', True), 'show_ftop_button': getattr(settings, 'SHOW_FTOP_BUTTON', True),
'db': settings.COUCHDB['dbs']['member']} 'db': settings.COUCHDB['dbs']['member'],
'committees_shift_id': committees_shift_id,
'exemptions_shift_id': exemptions_shift_id,
}
# with_addr_complement # with_addr_complement
# with_second_phone # with_second_phone
...@@ -168,6 +193,7 @@ def validation_inscription(request, email): ...@@ -168,6 +193,7 @@ def validation_inscription(request, email):
'payment_meanings': settings.SUBSCRIPTION_PAYMENT_MEANINGS, 'payment_meanings': settings.SUBSCRIPTION_PAYMENT_MEANINGS,
'input_phone_pattern': getattr(settings, 'INPUT_PHONE_PATTERN', default_input_phone_pattern), 'input_phone_pattern': getattr(settings, 'INPUT_PHONE_PATTERN', default_input_phone_pattern),
'ask_for_sex': getattr(settings, 'SUBSCRIPTION_ASK_FOR_SEX', False), 'ask_for_sex': getattr(settings, 'SUBSCRIPTION_ASK_FOR_SEX', False),
'ask_for_job': getattr(settings, 'SUBSCRIPTION_ASK_FOR_JOB', False),
'ask_for_street2': getattr(settings, 'SUBSCRIPTION_ADD_STREET2', False), 'ask_for_street2': getattr(settings, 'SUBSCRIPTION_ADD_STREET2', False),
'ask_for_second_phone': getattr(settings, 'SUBSCRIPTION_ADD_SECOND_PHONE', False), 'ask_for_second_phone': getattr(settings, 'SUBSCRIPTION_ADD_SECOND_PHONE', False),
'show_ftop_button': getattr(settings, 'SHOW_FTOP_BUTTON', True), 'show_ftop_button': getattr(settings, 'SHOW_FTOP_BUTTON', True),
...@@ -290,7 +316,8 @@ def record_service_presence(request): ...@@ -290,7 +316,8 @@ def record_service_presence(request):
stid = int(request.POST.get("stid", 0)) # shift_ticket_id stid = int(request.POST.get("stid", 0)) # shift_ticket_id
cancel = request.POST.get("cancel") == 'true' cancel = request.POST.get("cancel") == 'true'
typeAction = str(request.POST.get("type")) typeAction = str(request.POST.get("type"))
coop_logger.info("Enregistrement presence : mid = %s, rid = %s, sid = %s, stid = %s, cancel = %s, typeAction = %s",
str(mid), str(rid), str(sid), str(stid), str(cancel), typeAction)
app_env = getattr(settings, 'APP_ENV', "prod") app_env = getattr(settings, 'APP_ENV', "prod")
if (rid > -1 and mid > 0): if (rid > -1 and mid > 0):
overrided_date = "" overrided_date = ""
...@@ -312,6 +339,7 @@ def record_service_presence(request): ...@@ -312,6 +339,7 @@ def record_service_presence(request):
res['update'] = 'ok' res['update'] = 'ok'
else: else:
res['update'] = 'ko' res['update'] = 'ko'
coop_logger.info("Résultat update record_service_presence : %s", res['update'])
if res['update'] == 'ok': if res['update'] == 'ok':
members = CagetteMember.search('id', mid) members = CagetteMember.search('id', mid)
m = members[0] m = members[0]
...@@ -327,6 +355,7 @@ def record_service_presence(request): ...@@ -327,6 +355,7 @@ def record_service_presence(request):
except Exception as e: except Exception as e:
res['error'] = str(e) res['error'] = str(e)
coop_logger.error("Erreur record_service_presence : %s", str(e))
return JsonResponse({'res': res}) return JsonResponse({'res': res})
def easy_validate_shift_presence(request): def easy_validate_shift_presence(request):
......
...@@ -81,6 +81,12 @@ ...@@ -81,6 +81,12 @@
gap: 10px; gap: 10px;
} }
.member_job_area {
display: flex;
flex-direction: column;
gap: 10px;
}
.edit-btn{ .edit-btn{
cursor: pointer; cursor: pointer;
} }
...@@ -97,6 +103,9 @@ ...@@ -97,6 +103,9 @@
#edit_phone_form #mobile_form { #edit_phone_form #mobile_form {
margin: 5px 0; margin: 5px 0;
} }
#edit_job_form {
display: none ;
}
@media screen and (max-width: 992px) { @media screen and (max-width: 992px) {
......
...@@ -87,6 +87,10 @@ body { ...@@ -87,6 +87,10 @@ body {
} }
} }
#comite_my_shifs_message {
min-width: 20em;
}
#home_incoming_services { #home_incoming_services {
min-height: 80px; min-height: 80px;
display: flex; display: flex;
......
...@@ -4,8 +4,24 @@ function init_my_shifts_tile() { ...@@ -4,8 +4,24 @@ function init_my_shifts_tile() {
if (incoming_shifts.length === 0) { if (incoming_shifts.length === 0) {
$("#home_tile_my_services #home_incoming_services").text("Aucun service à venir..."); $("#home_tile_my_services #home_incoming_services").text("Aucun service à venir...");
} else { } else {
$("#home_tile_my_services #home_incoming_services").empty();
if (partner_data.comite === "True") {
let message = $('#comite_my_shifs_message').clone()
message.find('[data-type="nb_of_shifs_state"] [data-type="shifts_nb"]').text(partner_data.final_ftop_point)
if (Math.abs(partner_data.final_ftop_point) > 1) {
message.find('[data-type="nb_of_shifs_state"] [data-type="service_txt"]').text("services")
}
// let's get next ftop shift (incoming_shifts is ordered)
if (incoming_shifts.length > 0) {
const next_shift = incoming_shifts[0]
let ns_date = new Date(next_shift.date_begin)
const date_options = {dateStyle: "short"}
message.find('[data-type="next_ftop_shift_date"]').text(ns_date.toLocaleDateString('fr-FR', date_options))
}
$("#home_tile_my_services .tile_content").html(message)
} else {
$("#home_tile_my_services #home_incoming_services").empty();
let cpt = 0; let cpt = 0;
for (shift of incoming_shifts) { for (shift of incoming_shifts) {
...@@ -20,6 +36,7 @@ function init_my_shifts_tile() { ...@@ -20,6 +36,7 @@ function init_my_shifts_tile() {
} }
} }
} }
}
} }
function process_asked_shift_template_change(shift_t_id) { function process_asked_shift_template_change(shift_t_id) {
......
...@@ -49,6 +49,19 @@ function init_my_info() { ...@@ -49,6 +49,19 @@ function init_my_info() {
$(".member_phone_line").hide(); $(".member_phone_line").hide();
} }
$(".member_job").empty();
if (partner_data.function !== "" && partner_data.function !== "False" && partner_data.function !== false && partner_data.function !== null) {
$(".member_job")
.append(partner_data.function)
.show();
} else {
$(".member_job").hide();
}
if ($(".member_job").text() === "") {
$(".member_job_line").hide();
}
$('#edit_address').off('click') $('#edit_address').off('click')
.on('click', () => { .on('click', () => {
$("#street_form").val(partner_data.street.replace(/&#39;/g, "'")); $("#street_form").val(partner_data.street.replace(/&#39;/g, "'"));
...@@ -90,12 +103,30 @@ function init_my_info() { ...@@ -90,12 +103,30 @@ function init_my_info() {
}); });
$('#save_edit_phone').off('click') $('#save_edit_phone').off('click')
.on('click', () => { .on('click', () => {
console.log('ici');
data =[]; data =[];
data['phone']= $("#phone_form").val(); data['phone']= $("#phone_form").val();
data['mobile']= $("#mobile_form").val(); data['mobile']= $("#mobile_form").val();
saveInfo(data, 'phone'); saveInfo(data, 'phone');
}); });
$('#edit_job').off('click')
.on('click', () => {
if (partner_data.function === "False") partner_data.function = "";
$("#job_form").val(partner_data.function);
$('#edit_job_value').hide();
$('#edit_job_form').show();
});
$('#cancel_edit_job').off('click')
.on('click', () => {
$('#edit_job_form').hide();
$('#edit_job_value').show();
});
$('#save_edit_job').off('click')
.on('click', () => {
console.log('ici');
data =[];
data['function']= $("#job_form").val();
saveInfo(data, 'function');
});
} }
...@@ -127,6 +158,10 @@ function saveInfo(data, field) { ...@@ -127,6 +158,10 @@ function saveInfo(data, field) {
$('#edit_phone_form').hide(); $('#edit_phone_form').hide();
$('#edit_phone_value').show(); $('#edit_phone_value').show();
} }
if (field == 'function') {
$('#edit_job_form').hide();
$('#edit_job_value').show();
}
}, },
error: function(error) { error: function(error) {
console.log(error); console.log(error);
......
...@@ -177,7 +177,22 @@ function init_history() { ...@@ -177,7 +177,22 @@ function init_history() {
function init_incoming_shifts() { function init_incoming_shifts() {
$(".loading-incoming-shifts").hide(); $(".loading-incoming-shifts").hide();
$("#incoming_shifts").show(); $("#incoming_shifts").show();
if (partner_data.comite === "True") {
let message = $('#comite_my_shifs_message').clone()
message.find('[data-type="nb_of_shifs_state"] [data-type="shifts_nb"]').text(partner_data.final_ftop_point)
if (Math.abs(partner_data.final_ftop_point) > 1) {
message.find('[data-type="nb_of_shifs_state"] [data-type="service_txt"]').text("services")
}
// let's get next ftop shift (incoming_shifts is ordered)
if (incoming_shifts.length > 0) {
const next_shift = incoming_shifts[0]
let ns_date = new Date(next_shift.date_begin)
const date_options = {dateStyle: "short"}
message.find('[data-type="next_ftop_shift_date"]').text(ns_date.toLocaleDateString('fr-FR', date_options))
}
$("#incoming_shifts_area").html(message)
} else {
if (incoming_shifts.length === 0) { if (incoming_shifts.length === 0) {
$("#incoming_shifts").text("Aucun service à venir..."); $("#incoming_shifts").text("Aucun service à venir...");
} else { } else {
...@@ -211,6 +226,7 @@ function init_incoming_shifts() { ...@@ -211,6 +226,7 @@ function init_incoming_shifts() {
$("#incoming_shifts").append(shift_line_template.html()); $("#incoming_shifts").append(shift_line_template.html());
} }
} }
}
} }
function init_my_shifts() { function init_my_shifts() {
......
...@@ -119,7 +119,6 @@ function add_or_change_shift(new_shift_id) { ...@@ -119,7 +119,6 @@ function add_or_change_shift(new_shift_id) {
error: function(error) { error: function(error) {
closeModal(); closeModal();
selected_shift = null; selected_shift = null;
if (error.status === 400 && 'msg' in error.responseJSON && error.responseJSON.msg === "Old service in less than 24hours.") { if (error.status === 400 && 'msg' in error.responseJSON && error.responseJSON.msg === "Old service in less than 24hours.") {
alert(`Désolé ! Le service que tu souhaites échanger démarre dans moins de 24h. ` + alert(`Désolé ! Le service que tu souhaites échanger démarre dans moins de 24h. ` +
`Afin de faciliter la logistique des services, il n'est plus possible de l'échanger. ` + `Afin de faciliter la logistique des services, il n'est plus possible de l'échanger. ` +
...@@ -464,7 +463,7 @@ function init_shifts_list() { ...@@ -464,7 +463,7 @@ function init_shifts_list() {
*/ */
function init_calendar_page() { function init_calendar_page() {
let template_explanations = $("#calendar_explaination_template"); let template_explanations = $("#calendar_explaination_template");
let event_src = '/shifts/get_list_shift_calendar/' + partner_data.concerned_partner_id;
if (vw <= 992) { if (vw <= 992) {
$(".loading-calendar").show(); $(".loading-calendar").show();
...@@ -556,7 +555,7 @@ function init_calendar_page() { ...@@ -556,7 +555,7 @@ function init_calendar_page() {
contentHeight: "auto", contentHeight: "auto",
eventDisplay: "block", eventDisplay: "block",
hiddenDays: hidden_days, hiddenDays: hidden_days,
events: '/shifts/get_list_shift_calendar/' + partner_data.concerned_partner_id, events: event_src,
eventClick: function(info) { eventClick: function(info) {
if (!$(info.el).hasClass("shift_booked") && !$(info.el).hasClass("shift_booked_makeup")) { if (!$(info.el).hasClass("shift_booked") && !$(info.el).hasClass("shift_booked_makeup")) {
const new_shift_id = info.event.id; const new_shift_id = info.event.id;
...@@ -656,7 +655,7 @@ function init_calendar_page() { ...@@ -656,7 +655,7 @@ function init_calendar_page() {
calendar.render(); calendar.render();
} }
function init_read_only_calendar_page() { async function init_read_only_calendar_page() {
let template_explanations = $("#calendar_explaination_template"); let template_explanations = $("#calendar_explaination_template");
if (vw <= 992) { if (vw <= 992) {
...@@ -680,8 +679,8 @@ function init_read_only_calendar_page() { ...@@ -680,8 +679,8 @@ function init_read_only_calendar_page() {
if (incoming_shifts !== null) { if (incoming_shifts !== null) {
init_shifts_list(); init_shifts_list();
} else { } else {
load_partner_shifts(partner_data.concerned_partner_id) await load_partner_shifts(partner_data.concerned_partner_id)
.then(init_shifts_list); init_shifts_list();
} }
if (should_select_makeup()) { if (should_select_makeup()) {
...@@ -718,7 +717,16 @@ function init_read_only_calendar_page() { ...@@ -718,7 +717,16 @@ function init_read_only_calendar_page() {
const hidden_days = days_to_hide.length > 0 ? $.map(days_to_hide.split(", "), Number) : []; const hidden_days = days_to_hide.length > 0 ? $.map(days_to_hide.split(", "), Number) : [];
const calendarEl = document.getElementById('read_only_calendar'); const calendarEl = document.getElementById('read_only_calendar');
let event_src = '/shifts/get_list_shift_calendar/' + partner_data.concerned_partner_id;
if (partner_data.comite === "True") {
let next_evts = []
if (incoming_shifts.length > 0) {
incoming_shifts.forEach((s) => {
next_evts.push({id: s.id, title: 'Prélèvement 1 point', allDay: true, start: s.date_begin})
});
}
event_src = next_evts
}
calendar = new FullCalendar.Calendar(calendarEl, { calendar = new FullCalendar.Calendar(calendarEl, {
locale: 'fr', locale: 'fr',
initialView: default_initial_view, initialView: default_initial_view,
...@@ -734,7 +742,7 @@ function init_read_only_calendar_page() { ...@@ -734,7 +742,7 @@ function init_read_only_calendar_page() {
contentHeight: "auto", contentHeight: "auto",
eventDisplay: "block", eventDisplay: "block",
hiddenDays: hidden_days, hiddenDays: hidden_days,
events: '/shifts/get_list_shift_calendar/' + partner_data.concerned_partner_id, events: event_src,
eventDidMount: function() { eventDidMount: function() {
// Calendar is hidden at first on mobile to hide header change when data is loaded // Calendar is hidden at first on mobile to hide header change when data is loaded
$(".loading-calendar").hide(); $(".loading-calendar").hide();
......
...@@ -69,6 +69,8 @@ def index(request, exception=None): ...@@ -69,6 +69,8 @@ def index(request, exception=None):
'WELCOME_SUBTITLE_ENTRANCE_MSG': getattr(settings, 'WELCOME_SUBTITLE_ENTRANCE_MSG', '')} 'WELCOME_SUBTITLE_ENTRANCE_MSG': getattr(settings, 'WELCOME_SUBTITLE_ENTRANCE_MSG', '')}
if hasattr(settings, 'SUBSCRIPTION_ASK_FOR_SEX'): if hasattr(settings, 'SUBSCRIPTION_ASK_FOR_SEX'):
context['ask_for_sex'] = settings.SUBSCRIPTION_ASK_FOR_SEX context['ask_for_sex'] = settings.SUBSCRIPTION_ASK_FOR_SEX
if hasattr(settings, 'SUBSCRIPTION_ASK_FOR_JOB'):
context['ask_for_job'] = settings.SUBSCRIPTION_ASK_FOR_JOB
if hasattr(settings, 'SUBSCRIPTION_ADD_STREET2'): if hasattr(settings, 'SUBSCRIPTION_ADD_STREET2'):
context['ask_for_street2'] = settings.SUBSCRIPTION_ADD_STREET2 context['ask_for_street2'] = settings.SUBSCRIPTION_ADD_STREET2
if hasattr(settings, 'SUBSCRIPTION_ADD_SECOND_PHONE'): if hasattr(settings, 'SUBSCRIPTION_ADD_SECOND_PHONE'):
...@@ -162,6 +164,14 @@ def index(request, exception=None): ...@@ -162,6 +164,14 @@ def index(request, exception=None):
context['helper_unsubscribe_form_link'] = msettings['helper_unsubscribe_form_link']['value'] if 'helper_unsubscribe_form_link' in msettings else '' context['helper_unsubscribe_form_link'] = msettings['helper_unsubscribe_form_link']['value'] if 'helper_unsubscribe_form_link' in msettings else ''
context['covid_form_link'] = msettings['covid_form_link']['value'] if 'covid_form_link' in msettings else '' context['covid_form_link'] = msettings['covid_form_link']['value'] if 'covid_form_link' in msettings else ''
context['covid_end_form_link'] = msettings['covid_end_form_link']['value'] if 'covid_end_form_link' in msettings else '' context['covid_end_form_link'] = msettings['covid_end_form_link']['value'] if 'covid_end_form_link' in msettings else ''
if getattr(settings, 'USE_EXEMPTIONS_SHIFT_TEMPLATE', False) is True:
exemptions_shift_id = CagetteServices.get_exemptions_shift_id()
if exemptions_shift_id is None:
return HttpResponse("Le créneau des exemptions n'est pas configuré dans Odoo !")
else:
context['exemptions_shift_id'] = exemptions_shift_id
else:
context['exemptions_shift_id'] = 0
else: else:
# may arrive when switching database without cleaning cookie # may arrive when switching database without cleaning cookie
return redirect('/website/deconnect') return redirect('/website/deconnect')
......
...@@ -64,6 +64,7 @@ class Order(models.Model): ...@@ -64,6 +64,7 @@ class Order(models.Model):
for p in res: for p in res:
pids.append(p['product_id'][0]) pids.append(p['product_id'][0])
partner_id = p['partner_id'][0] partner_id = p['partner_id'][0]
used_coeffs = None
if len(pids) > 0: if len(pids) > 0:
# Adding barcode and other data for every purchased product # Adding barcode and other data for every purchased product
f = ['barcode', 'product_tmpl_id', 'shelf_id'] f = ['barcode', 'product_tmpl_id', 'shelf_id']
...@@ -356,7 +357,7 @@ class Orders(models.Model): ...@@ -356,7 +357,7 @@ class Orders(models.Model):
fixed_prefix = getattr(settings, 'FIXED_BARCODE_PREFIX', '0490') fixed_prefix = getattr(settings, 'FIXED_BARCODE_PREFIX', '0490')
bc_pattern = re.compile('^' + fixed_prefix) bc_pattern = re.compile('^' + fixed_prefix)
lines_data = Orders.get_lines(oids) lines_data = Orders.get_lines(oids)
for l in lines_data['lines']: for l in lines_data:
if not (bc_pattern.match(str(l['barcode'])) is None): if not (bc_pattern.match(str(l['barcode'])) is None):
if not (l['product_tmpl_id'] in labels_data): if not (l['product_tmpl_id'] in labels_data):
labels_data[l['product_tmpl_id']] = 0 labels_data[l['product_tmpl_id']] = 0
......
...@@ -20,7 +20,7 @@ def helper(request): ...@@ -20,7 +20,7 @@ def helper(request):
'title': 'Aide à la commande', 'title': 'Aide à la commande',
'couchdb_server': settings.COUCHDB['url'], 'couchdb_server': settings.COUCHDB['url'],
'db': settings.COUCHDB['dbs']['orders'], 'db': settings.COUCHDB['dbs']['orders'],
'odoo_server': settings.ODOO['url'], 'odoo_server': getattr(settings, 'ODOO_PUBLIC_URL', settings.ODOO['url']),
'metabase_url': getattr(settings, 'ORDERS_HELPER_METABASE_URL', ''), 'metabase_url': getattr(settings, 'ORDERS_HELPER_METABASE_URL', ''),
'nb_past_days_to_compute_sales_average': OdooAPI().get_system_param('lacagette_products.nb_past_days_to_compute_sales_average'), 'nb_past_days_to_compute_sales_average': OdooAPI().get_system_param('lacagette_products.nb_past_days_to_compute_sales_average'),
'nb_of_consecutive_non_sale_days_considered_as_break': OdooAPI().get_system_param('lacagette_products.nb_of_consecutive_non_sale_days_considered_as_break') 'nb_of_consecutive_non_sale_days_considered_as_break': OdooAPI().get_system_param('lacagette_products.nb_of_consecutive_non_sale_days_considered_as_break')
......
...@@ -182,7 +182,7 @@ def retrieve_odoo_coop_data (coop_ids): ...@@ -182,7 +182,7 @@ def retrieve_odoo_coop_data (coop_ids):
for coop in coops: for coop in coops:
if not (str(coop['barcode_base']) in coops_dict.keys()): if not (str(coop['barcode_base']) in coops_dict.keys()):
coops_dict[str(coop['barcode_base'])] = \ coops_dict[str(coop['barcode_base'])] = \
str(coop['barcode_base']) + ' - ' + coop['name'] str(coop['barcode_base']) + ' - ' + coop['name'].replace(';','')
return coops_dict return coops_dict
......
.green {color:#60B000;} .green {color:#60B000;}
.b_green, .b_more_than_50pc {background:#d9ebd2} .b_green, .b_more_than_50pc {background:#d9ebd2 !important;}
.orange {color:#FFA500;} .orange {color:#FFA500;}
.b_orange, .b_less_than_50pc {background:#F0B000 !important;} .b_orange, .b_less_than_50pc {background:#F0B000 !important;}
.yellow {color:#F0B000;} .yellow {color:#F0B000;}
.b_yellow {background: #fcf3cc;} .b_yellow {background: #fcf3cc !important;}
.red {color:#FF0000;} .red {color:#FF0000;}
.b_red, .b_less_than_25pc {background:#ff3333 !important;} .b_red, .b_less_than_25pc {background:#ff3333 !important;}
.loading {background-image: url("/static/img/ajax-loader.gif"); background-repeat:no-repeat; background-position: center; background-color: #efefef;} .loading {background-image: url("/static/img/ajax-loader.gif"); background-repeat:no-repeat; background-position: center; background-color: #efefef;}
......
...@@ -447,17 +447,21 @@ function shift_loc_selection() { ...@@ -447,17 +447,21 @@ function shift_loc_selection() {
st_loc_buttons.removeClass('highlighted'); st_loc_buttons.removeClass('highlighted');
clicked.addClass('highlighted'); clicked.addClass('highlighted');
if (clicked.data('select') != 'Volant') { if (clicked.data('select') !== 'Volant' && clicked.data('select') !== 'Exemption') {
retrieve_and_draw_shift_tempates(); retrieve_and_draw_shift_tempates();
} else { } else if (clicked.data('select') === 'Volant') {
//shift_templates[volant] is not always set (when call from bdm interface) //shift_templates[volant] is not always set (when call from bdm interface)
if (typeof volant !== "undefined" && typeof shift_templates[volant] !== "undefined") { if (typeof volant !== "undefined" && typeof shift_templates[volant] !== "undefined") {
subscribe_shift(volant); subscribe_shift(volant);
} }
} else if (clicked.data('select') === 'Exemption') {
subscribe_shift(exemptions_shift_id);
} }
} }
st_loc_buttons.click(shift_loc_selection); st_loc_buttons.click(shift_loc_selection);
week_types.find('input').change(filter_weeks); week_types.find('input').change(() => {
filter_weeks({shift_listener: true});
});
...@@ -60,6 +60,11 @@ ...@@ -60,6 +60,11 @@
.replace( /ã/g, 'a' ) .replace( /ã/g, 'a' )
.replace( /õ/g, 'o' ) .replace( /õ/g, 'o' )
.replace( /ç/g, 'c' ) .replace( /ç/g, 'c' )
.replace( /â/g, 'a' )
.replace( /à/g, 'a' )
.replace( /ù/g, 'u' )
.replace( /ï/g, 'i' )
.replace( /œ/g, 'oe' )
.replace( /ì/g, 'i' ); .replace( /ì/g, 'i' );
} }
......
...@@ -57,6 +57,8 @@ class CagetteProduct(models.Model): ...@@ -57,6 +57,8 @@ class CagetteProduct(models.Model):
fields = ['barcode', 'product_tmpl_id', 'pricetag_rackinfos', fields = ['barcode', 'product_tmpl_id', 'pricetag_rackinfos',
'price_weight_net', 'price_volume', 'list_price', 'price_weight_net', 'price_volume', 'list_price',
'weight_net', 'volume', 'to_weight', 'meal_voucher_ok'] 'weight_net', 'volume', 'to_weight', 'meal_voucher_ok']
if not getattr(settings, 'SHOW_MEAL_VOUCHER_OK_LINE_IN_PRODUCT_INFO_FOR_LABEL', True):
fields.remove('meal_voucher_ok')
additionnal_fields = getattr(settings, 'SHELF_LABELS_ADD_FIELDS', []) additionnal_fields = getattr(settings, 'SHELF_LABELS_ADD_FIELDS', [])
fields += additionnal_fields fields += additionnal_fields
product_data = api.search_read('product.product', cond, fields) product_data = api.search_read('product.product', cond, fields)
......
...@@ -230,6 +230,9 @@ class CagetteReception(models.Model): ...@@ -230,6 +230,9 @@ class CagetteReception(models.Model):
products are updated with new vendor prices""" products are updated with new vendor prices"""
result = None result = None
if self.o_api == None:
return 'error : cant reach odoo'
res = self.o_api.execute('purchase.order', 'action_view_picking', [self.id]) res = self.o_api.execute('purchase.order', 'action_view_picking', [self.id])
new_x_reception_status = '' new_x_reception_status = ''
if res: if res:
...@@ -261,6 +264,7 @@ class CagetteReception(models.Model): ...@@ -261,6 +264,7 @@ class CagetteReception(models.Model):
new_x_reception_status += '/error_uprice' new_x_reception_status += '/error_uprice'
if new_x_reception_status == '': if new_x_reception_status == '':
new_x_reception_status = 'done' new_x_reception_status = 'done'
if getattr(settings, 'RECEPTION_SHELF_LABEL_PRINT', False) is True:
self.print_shelf_labels_for_updated_prices(price_update['lines']) self.print_shelf_labels_for_updated_prices(price_update['lines'])
if result != 'already done': if result != 'already done':
......
...@@ -221,11 +221,19 @@ div#container_edition { ...@@ -221,11 +221,19 @@ div#container_edition {
} }
tr.even td.row_product_no_qty { tr.even td.row_product_no_qty {
background-color: #f0ad4e; /*#ec971f*/ background-color: #F2B969 !important;
} }
tr.odd td.row_product_no_qty { tr.odd td.row_product_no_qty {
background-color: rgb(236, 182, 106); /*#ec971f*/ background-color: #f0ad4e !important;
}
tr.even td.row_product_qty_changed {
background-color: #ffc0b7 !important;
}
tr.odd td.row_product_qty_changed {
background-color: #FFB7AD !important;
} }
.add_products_button_container { .add_products_button_container {
......
argon2-cffi==20.1.0 argon2-cffi==21.3.0
certifi==2020.11.8 argon2-cffi-bindings==21.2.0
cffi==1.14.4 certifi==2022.6.15
chardet==3.0.4 cffi==1.15.1
charset-normalizer==2.1.0
CouchDB==1.2 CouchDB==1.2
Django==2.2.17 Django==2.2.28
django-cors-headers==3.5.0 django-cors-headers==3.11.0
et-xmlfile==1.0.1 et-xmlfile==1.1.0
idna==2.10 idna==3.3
jdcal==1.4.1 openpyxl==3.0.10
openpyxl==3.0.5 Pillow==9.2.0
Pillow==8.0.1 pycparser==2.21
pycparser==2.20 PyMySQL==1.0.2
PyMySQL==0.10.1 python-dateutil==2.8.2
python-dateutil==2.8.1 pytz==2022.1
pytz==2020.4 reportlab==3.6.10
reportlab==3.5.55 requests==2.28.1
requests==2.25.0 six==1.16.0
six==1.15.0 sqlparse==0.4.2
sqlparse==0.4.1 urllib3==1.26.9
urllib3==1.26.2
...@@ -17,7 +17,8 @@ class CagetteSales(models.Model): ...@@ -17,7 +17,8 @@ class CagetteSales(models.Model):
# Get pos orders # Get pos orders
cond = [['date_order', '>=', date_from], ['date_order', '<=', date_to]] cond = [['date_order', '>=', date_from], ['date_order', '<=', date_to]]
fields = ['partner_id', 'statement_ids', 'name'] fields = ['partner_id', 'statement_ids', 'name']
orders = self.o_api.search_read('pos.order', cond, fields) orders = self.o_api.search_read('pos.order', cond, fields, limit=50000)
# Get bank statements of these sessions # Get bank statements of these sessions
statements = [] statements = []
statements_partners = {} statements_partners = {}
...@@ -25,11 +26,16 @@ class CagetteSales(models.Model): ...@@ -25,11 +26,16 @@ class CagetteSales(models.Model):
for o in orders: for o in orders:
statements = statements + o["statement_ids"] statements = statements + o["statement_ids"]
for s in o["statement_ids"]: for s in o["statement_ids"]:
if o["partner_id"] is False:
statements_partners[s] = "Membre non renseigné"
else:
statements_partners[s] = o["partner_id"][1] statements_partners[s] = o["partner_id"][1]
statements_orders[s] = o["name"] statements_orders[s] = o["name"]
# Get payment lines # Get payment lines
cond = [['id', 'in', statements]] cond = [['id', 'in', statements]]
fields = ["amount", "journal_id", "create_date"] fields = ["amount", "journal_id", "create_date", "meal_voucher_issuer"]
payments = self.o_api.search_read('account.bank.statement.line', cond, fields, order="create_date ASC", limit=50000) payments = self.o_api.search_read('account.bank.statement.line', cond, fields, order="create_date ASC", limit=50000)
try: try:
for payment in payments: for payment in payments:
...@@ -41,7 +47,8 @@ class CagetteSales(models.Model): ...@@ -41,7 +47,8 @@ class CagetteSales(models.Model):
"payments": [ "payments": [
{ {
"amount": round(float(payment["amount"]), 2), "amount": round(float(payment["amount"]), 2),
"journal_id": payment["journal_id"] "journal_id": payment["journal_id"],
"meal_voucher_issuer": payment["meal_voucher_issuer"]
} }
] ]
}) })
......
...@@ -49,13 +49,11 @@ function display_orders(orders) { ...@@ -49,13 +49,11 @@ function display_orders(orders) {
{ {
data:"partner", data:"partner",
title:"Membre", title:"Membre"
width: "40%"
}, },
{ {
data:"total_amount", data:"total_amount",
title: "Montant dû", title: "Montant dû",
className:"dt-body-center",
render: function (data) { render: function (data) {
return parseFloat(data).toFixed(2) + ' €'; return parseFloat(data).toFixed(2) + ' €';
} }
...@@ -74,7 +72,14 @@ function display_orders(orders) { ...@@ -74,7 +72,14 @@ function display_orders(orders) {
return res; return res;
} }
},
{
data:"payments[0].meal_voucher_issuer",
title: "Émetteur CB Déj",
render: function (data) {
return data === 'false' || data == false ? '' : data;
} }
},
], ],
order: [ order: [
[ [
......
mail_server_password = "change me !"
coops_mails = {
'lacagette': 'change me on preprod only !',
'supercafoutch': 'change me on preprod only !',
'graoucoop': 'change me on preprod only !'
}
coops_usernames = {
'lacagette' : 'Antonin',
'supercafoutch' : 'Marin',
'graoucoop' : 'Julien'
}
coops_preprod_urls = {
'lacagette': 'https://preprod.odoo.lacagette-coop.fr',
'supercafoutch': 'https://preprod.odoo.supercafoutch.cooperatic.fr/',
'graoucoop': 'https://preprod.odoo.graoucoop.cooperatic.fr'
}
\ No newline at end of file
#!/bin/bash
ci_dir="/home/django/third-party/scripts/src"
ci_data_dir="/home/django/third-party/scripts/data"
coop=$1
instance=$2
#move to the third argument
shift
shift
send_mails=true
#https://stackoverflow.com/questions/192249/how-do-i-parse-command-line-arguments-in-bash
while [[ "$#" -gt 0 ]]; do
case $1 in
-n|--nomail) send_mails=false ;;
*) echo "arguments : ${coop} ${instance} $1 not supported (Unknown parameter passed: $1)"
if ${send_mails}; then
python3 "${ci_dir}/send_error_mail.py" "arguments : ${coop} ${instance} $1 not supported (Unknown parameter passed: $1)"
fi
exit 1 ;;
esac
shift
done
if ! python3 "${ci_dir}/supported_coops.py" "${coop}" "${instance}"; then
echo "arguments : ${coop} ${instance} not supported"
if ${send_mails}; then
python3 "${ci_dir}/send_error_mail.py" "arguments : ${coop} ${instance} not supported"
fi
exit 1
fi
now=$( date +%Y%m%d_%H%M%S )
source "${ci_dir}/prepare_mail.sh"
source "${ci_dir}/update_instance.sh"
if [[ "${instance}" == "preprod" ]]; then
repo="third-party"
echo "Prepare mail" "${coop}" "${instance}" "${repo}" "${now}" "${send_mails}" "${ci_dir}" "${ci_data_dir}"
prepare_mail "${coop}" "${instance}" "${repo}" "${now}" "${send_mails}" "${ci_dir}" "${ci_data_dir}"|| exit 1
repo="Odoo"
echo "Prepare mail" "${coop}" "${instance}" "${repo}" "${now}" "${send_mails}" "${ci_dir}" "${ci_data_dir}"
prepare_mail "${coop}" "${instance}" "${repo}" "${now}" "${send_mails}" "${ci_dir}" "${ci_data_dir}" || exit 1
fi
repo="third-party"
echo "Update instance" "${coop}" "${instance}" "${repo}" "${now}" "${send_mails}" "${ci_dir}" "${ci_data_dir}"
update_instance "${coop}" "${instance}" "${repo}" "${now}" "${send_mails}" "${ci_dir}" "${ci_data_dir}" || exit 1
repo="Odoo"
echo "Update instance" "${coop}" "${instance}" "${repo}" "${now}" "${send_mails}" "${ci_dir}" "${ci_data_dir}"
update_instance "${coop}" "${instance}" "${repo}" "${now}" "${send_mails}" "${ci_dir}" "${ci_data_dir}" || exit 1
if [[ "${instance}" == "preprod" ]] && ${send_mails}; then
python3 "${ci_dir}/send_mail.py" "${ci_data_dir}/ci_diff_third-party_${now}.txt" "${ci_data_dir}/ci_diff_Odoo_${now}.txt" "${coop}"
fi
//Ces notes ont été prises lors de la conversion de l\'architecture cagette préprod vers la nouvelle architecture.
//Elles peuvent servir à faire de même sur la prod !
//USERS, HOME DIRS, FILES, RIGHTS
useradd -m odoo
usermod -s /bin/bash odoo
useradd -m django
usermod -s /bin/bash django
cd /home/lacagette
mv Odoo odoo-server.conf odoo-server.log odoo-server.log.1 odoo-server.log.2 scripts .local /home/odoo/.
//un nouveau odoo-server.log a été créé automatiquement, il faudra gérer ça
mv -f third-party cronlogs .virtualenvs /home/django/.
mkdir /home/odoo/cronlogs
cp -r /home/odoo/scripts /home/django/scripts
cp -r /home/lacagette/.ssh /home/django
cp -r /home/lacagette/.ssh /home/odoo
chown -R odoo:odoo /home/odoo
chown -R django:django /home/django
chown -R django:django /shared_dir/django_static
//PYTHON VENV
su django
cd /home/django/.virtualenvs
rm -rf third-party .gitignore bin lib pyvenv.cfg
source /usr/local/bin/virtualenvwrapper.sh
mkvirtualenv -p python3.8 -r /home/django/third-party/requirements.locked.txt third-party
exit
//PSQL
//key to understand : "A user is a role with the ability to log in (the role has the LOGIN attribute)."
su postgres
psql
create role odoo login;
grant lacagette to odoo;
alter database lacagette owner to odoo;
select pg_terminate_backend(pid) from pg_stat_activity where pid <> pg_backend_pid() and datname = 'lacagette';
alter database lacagette rename to odoo;
GRANT CONNECT ON DATABASE odoo TO metabase;
GRANT USAGE ON SCHEMA public TO metabase;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO metabase;
exit
nano /etc/postgresql/10/main/pg_hba.conf puis remplacer lacagette par odoo sur la ligne qui concerne metabase
exit
service postgresql restart
//CONFIGS
sed -i -e 's/\/lacagette\//\/odoo\//g' /home/odoo/odoo-server.conf
sed -i -e 's/db_name = lacagette/db_name = odoo/g' /home/odoo/odoo-server.conf
sed -i -e 's/db_user = lacagette/db_user = odoo/g' /home/odoo/odoo-server.conf
sed -i -e 's/dbfilter = lacagette/dbfilter = odoo/g' /home/odoo/odoo-server.conf
sed -i -e "s/'lacagette'/'odoo'/g" /home/django/third-party/outils/settings_secret.py
sed -i -e 's/lacagette/django/g' /etc/scripts/django.sh
sed -i -e 's/lacagette/django/g' /etc/init.d/django
sed -i -e 's/lacagette/odoo/g' /etc/init.d/odoo
//CRONS
crontab -l -u lacagette | grep -v "sync_files.sh" | crontab -u django -
crontab -l -u lacagette | grep "sync_files.sh" | crontab -u odoo -
crontab -r -u lacagette
//FILESTORE ISSUE
su postgres
psql
\c odoo
delete from ir_attachment where mimetype in ('text/css','application/javascript');
//puis deux restarts nécessaires... de odoo
restart django.
//METABASE : change database name in settings
\ No newline at end of file
prepare_mail() {
#Read arguments and set variables
coop=$1
instance=$2
repo=$3
now=$4
send_mails=$5
ci_dir=$6
ci_data_dir=$7
if [[ ${repo} == "third-party" ]]; then
user="django"
elif [[ ${repo} == "Odoo" ]]; then
user="odoo"
else
echo "Incorrect repo name : ${repo}"
if ${send_mails}; then
python3 "${ci_dir}/send_error_mail.py" "${coop} ${instance} ${repo} : incorrect repo name"
fi
exit 1
fi
cd /home/${user}/${repo}
su ${user} -c "git fetch --all"
#Check branch
branch_found_str=$( su ${user} -c "git ls-remote origin ${coop}_prod" ) #seems that git ls-remote is not "failing" so we need to check output
if [ -z "${branch_found_str}" ]; then
#branch prod does not exist, cannot go on
echo "${coop} ${instance} ${repo}: branch ${coop}_prod does not exist"
if ${send_mails}; then
python3 "${ci_dir}/send_error_mail.py" "${coop} ${instance} ${repo} : branch ${coop}_prod does not exist"
fi
exit 1
fi
###COMPARE BRANCHES AND DECIDE IF WE NEED TO CREATE A FILE CONTAINING NEW COMMITS###
#initialize file
if ! test -f "${ci_data_dir}/ci_diff_${repo}_last.txt"; then
touch "${ci_data_dir}/ci_diff_${repo}_last.txt"
fi
#new_commits="$( git log origin/${coop}_prod...origin/dev_cooperatic --no-merges --oneline )" (old version)
#Let's break down the new big line below :
#origin/${coop}_prod...origin/dev_cooperatic : we look for difference between develop branch and prod branch
#--pretty=format:'%s' : get rid of commit id
#the big grep+sed group : discard lines not maching pattern "merge [0-9]\+-... into dev_cooperatic" and only keep the [0-9]\+-.... part with a link to redmine
new_commits="$( git log origin/${coop}_prod...origin/dev_cooperatic --merges --oneline --pretty=format:'%s' | grep "into 'dev_cooperatic'" | sed "s/Merge branch '//g" | sed "s/' into 'dev_cooperatic'//g" | grep "^[0-9]\+" | sed -E 's/(^[0-9]+)/\<a href=\"https\:\/\/redmine.coopdev.fr\/issues\/\1\"\>\1\<\/a\>/g' )"
if [ -z "${new_commits}" ]; then
echo "${coop} ${instance} ${repo} : no difference between prod and dev_cooperatic"
#There are no difference between prod and dev_cooperatic therefore we don't want to send email for this repo.
#To inform the mailer that there is nothing new, we do not create ci_diff file at current time.
#The mailer will therefore use ci_diff_last to get the list of commits for the client.
#Not only there is nothing new, but also there is just no difference,
#therefore we need to erase the content the ci_diff_last file if not already empty (save time of first time it was erased)
if [[ -s "${ci_data_dir}/ci_diff_${repo}_last.txt" ]]; then
> "${ci_data_dir}/ci_diff_${repo}_last.txt"
fi
else
echo "${new_commits}" > "${ci_data_dir}/ci_diff_${repo}_${now}.txt"
#See if commit difference is the same than last time
if cmp "${ci_data_dir}/ci_diff_${repo}_last.txt" "${ci_data_dir}/ci_diff_${repo}_${now}.txt"; then
#As there are no new commits in prod, we don't want to send email for this repo.
#To indicate that, we delete just created ci_diff file at current time.
rm "${ci_data_dir}/ci_diff_${repo}_${now}.txt"
echo "${coop} ${instance} ${repo} : ci_diff_${repo} files are identical"
else
#We have created new ci_diff.
#Replace old ci_diff_last by new one
rm "${ci_data_dir}/ci_diff_${repo}_last.txt"
cp "${ci_data_dir}/ci_diff_${repo}_${now}.txt" "${ci_data_dir}/ci_diff_${repo}_last.txt"
fi
fi
}
\ No newline at end of file
import smtplib
import sys
from email.message import EmailMessage
import ci_secrets
def run(body):
msg = EmailMessage()
msg['Subject'] = "CoopdevCI : script error"
msg['From'] = "brevo@ksuite-coopdev.fr"
msg['To'] = "yvon.kerdoncuff@gmail.com"
msg.set_content(body)
s = smtplib.SMTP('smtp-relay.sendinblue.com', port=587)
s.login("brevo@ksuite-coopdev.fr", ci_secrets.mail_server_password)
s.send_message(msg)
s.quit()
if __name__ == '__main__':
arg = ""
if len(sys.argv) > 1:
arg = sys.argv[1]
run(arg)
import smtplib
import sys
from email.message import EmailMessage
import ci_secrets
import os
def run(tp_file,od_file,coop):
msg = EmailMessage()
msg['Subject'] = "Coopdev : évolutions à tester"
msg['From'] = "brevo@ksuite-coopdev.fr"
msg['To'] = ci_secrets.coops_mails[coop]
msg['CC'] = "assistance-redmine@coopdev.fr"
msg['Reply-to'] = "assistance-redmine@coopdev.fr"
tp_file_exists = os.path.exists(tp_file)
od_file_exists = os.path.exists(od_file)
if not tp_file_exists and not od_file_exists:
print("MAIL : rien à envoyer")
return
elif not tp_file_exists:
tp_file = "/home/django/third-party/scripts/data/ci_diff_third-party_last.txt"
tp_file_exists = os.path.exists(tp_file)
elif not od_file_exists:
od_file = "/home/django/third-party/scripts/data/ci_diff_Odoo_last.txt"
od_file_exists = os.path.exists(od_file)
body = "Bonjour " + ci_secrets.coops_usernames[coop] +",\n\n"
body = body + "De nouveaux développements en attente de mise en production ont été mis en ligne sur " + ci_secrets.coops_preprod_urls[coop] + ".\n"
body = body + "On te remercie de bien vouloir tester ces évolutions et de nous faire un retour via les liens redmine ci-dessous (si possible), ou en répondant à assistance-redmine@coopdev.fr." + "\n\n\n"
if tp_file_exists:
tp_f = open(tp_file,"r")
body = body + "Evolutions des applications tierces :\n\n" + tp_f.read() + "\n\n"
tp_f.close()
if od_file_exists:
od_f = open(od_file,"r")
body = body + "Evolutions d'odoo :\n\n" + od_f.read() + "\n\n"
tp_f.close()
body = body + "Coopérativement.\nCoopdev Foodcoops"
msg.set_content(body)
s = smtplib.SMTP('smtp-relay.sendinblue.com', port=587)
s.login("brevo@ksuite-coopdev.fr", ci_secrets.mail_server_password)
s.send_message(msg)
s.quit()
print("MAIL : envoi effectué")
if __name__ == '__main__':
arg = ""
if len(sys.argv) > 3:
arg1 = sys.argv[1]
arg2 = sys.argv[2]
arg3 = sys.argv[3]
run(arg1,arg2,arg3)
import sys
from ci_secrets import coops_mails
def is_coop_supported(coop):
return coop in coops_mails and coops_mails[coop]
def is_instance_supported(instance):
return instance == "preprod" or instance == "test" or instance == "dev" or instance == "prod"
def get_mail(coop):
return coops_mails[coop]
if __name__ == '__main__':
coop = ""
instance = ""
if len(sys.argv) == 3:
coop = sys.argv[1]
instance = sys.argv[2]
if is_coop_supported(coop) and is_instance_supported(instance):
sys.exit(0)
else:
sys.exit(1)
update_instance() {
#Read arguments and set variables
coop=$1
instance=$2
repo=$3
now=$4
send_mails=$5
ci_dir=$6
ci_data_dir=$7
if [[ ${repo} == "third-party" ]]; then
user="django"
service="django"
elif [[ ${repo} == "Odoo" ]]; then
user="odoo"
service="odoo"
else
echo "Incorrect repo name : ${repo}"
if ${send_mails}; then
python3 "${ci_dir}/send_error_mail.py" "${coop} ${instance} ${repo} : incorrect repo name"
fi
exit 1
fi
if [[ "${instance}" == "prod" ]]; then
target="${coop}_prod"
else
target="dev_cooperatic"
fi
#Move to repository directory, fetch all and make sure coop_prod branch exists
cd /home/${user}/${repo}
su ${user} -c "git fetch --all"
branch_found_str=$( su ${user} -c "git ls-remote origin ${coop}_prod" ) #seems that git ls-remote is not "failing" so we need to check output
if [ -z "${branch_found_str}" ]; then
#branch prod does not exist, cannot go on
echo "${coop} ${instance} ${repo}: branch ${coop}_prod does not exist"
if ${send_mails}; then
python3 "${ci_dir}/send_error_mail.py" "${coop} ${instance} ${repo} : branch ${coop}_prod does not exist"
fi
exit 1
fi
#Make sure config file exists if repo is third-party
if [[ ${repo} == "third-party" ]]; then
if ! test -f "coops_configurations/config_${coop}.py"; then
echo "${coop} ${instance} ${repo}: coops_configurations/config_${coop}.py does not exists"
if ${send_mails}; then
python3 "${ci_dir}/send_error_mail.py" "${coop} ${instance} ${repo} : coops_configurations/config_${coop}.py does not exists"
fi
exit 1
fi
fi
#Save filepaths of files that are going to be updated if repo is odoo
if [[ ${repo} == "Odoo" ]]; then
echo "git diff --name-only origin/${target}.."
updated_filepaths="$( git diff --name-only origin/${target}.. )"
echo "${updated_filepaths}"
fi
#Update code if needed
new_commits="$( git log origin/${target}... )"
if [ -z "${new_commits}" ]; then
echo "${coop} ${instance} ${repo} : already up to date"
else
#Stop instance
/etc/init.d/${service} stop
#One could argue that git stash is risky on prod,
#but it's way easier to go that way to get rid of template file changes generated on django service restart
if ! su ${user} -c "git stash"; then
#We can not finish the update : restart the system
/etc/init.d/${service} start
echo "${coop} ${instance} ${repo}: could not git stash"
if ${send_mails}; then
python3 "${ci_dir}/send_error_mail.py" "${coop} ${instance} ${repo} : could not git stash"
fi
exit 1
fi
#Make script robust to any branch change : switch to target !
if ! su ${user} -c "git checkout ${target}"; then
#We can not finish the update : restart the system
#TODO : detect if we have stashed something on previous step to revert it (git stash pop)
/etc/init.d/${service} start
echo "${coop} ${instance} ${repo}: could not git checkout ${target}"
if ${send_mails}; then
python3 "${ci_dir}/send_error_mail.py" "${coop} ${instance} ${repo} : could not git checkout ${target}"
fi
exit 1
fi
#Pull
if ! su ${user} -c "git pull"; then
#We can not finish the update : restart the system
#TODO : detect if we have stashed something on previous step to revert it (git stash pop)
#TODO : undo previous git checkout if necessary
echo "${coop} ${instance} ${repo}: could not git pull"
if ${send_mails}; then
python3 "${ci_dir}/send_error_mail.py" "${coop} ${instance} ${repo} : could not git pull"
fi
exit 1
fi
#Additionnal update actions
#push tags to remember this prod update point (only on prod)
if [[ "${instance}" == "prod" ]]; then
#TODO : handle errors
su ${user} -c "git tag -a ${coop}_${now} -m autotag"
su ${user} -c "git push origin ${coop}_${now}"
fi
if [[ ${repo} == "third-party" ]]; then
#Replace config file by proper one only if required
if ! cmp "outils/config.py" "coops_configurations/config_${coop}.py"; then
#Config files are different, go for it
mv "outils/config.py" "${ci_data_dir}/ci_old_cfg_file_${now}.py"
cp "coops_configurations/config_${coop}.py" "outils/config.py"
else
echo "config files are identical"
fi
fi
if [[ ${repo} == "Odoo" ]]; then
#Loop on updated_filenames, for each filename, look for the directory of __openerp__.py file
#Ff found, just store the parent directory
SAVEIFS=$IFS
IFS=$'\n'
if [[ ${updated_filepaths} == "" ]]; then
updated_filepaths_array=()
else
read -rd '' -a updated_filepaths_array <<<"${updated_filepaths}"
fi
IFS='/'
l=${#updated_filepaths_array[@]}
declare -A modules #Modules to update
modules_str=""
for (( i=0; i<l; i++ ));
do
read -rd '' -a one_path_array <<<"${updated_filepaths_array[$i]}"
j=0
accu_path="${one_path_array[0]}"
pathlen=${#one_path_array[@]}
while [[ $j -lt ${pathlen} ]] && ! test -f "${accu_path}/__openerp__.py"; do
((j++))
accu_path="${accu_path}/${one_path_array[j]}"
done
#Update key of modules if (we have found openerp.py (j<path) + module has not been added already),
#so that we do not add twice same module in modules_str
#and add module in modules_str
if [[ $j -lt ${pathlen} ]] && [[ ! -v modules["${one_path_array[$j]}"] ]]; then
modules["${one_path_array[$j]}"]=
if [[ ${modules_str} == "" ]]; then
modules_str="${one_path_array[$j]}"
else
modules_str="${modules_str},${one_path_array[$j]}"
fi
fi
done
IFS=$SAVEIFS
echo "${modules_str}"
restart_odoo_cmd="/home/odoo/Odoo/odoo/odoo.py -c /home/odoo/odoo-server.conf"
if [[ -n "${modules_str}" ]]; then
echo "restart odoo service with module update"
su odoo -c "${restart_odoo_cmd} -u${modules_str} --stop-after-init"
fi
fi
#Restart service
echo "Starting ${service} service"
/etc/init.d/${service} start
fi
}
\ No newline at end of file
...@@ -35,6 +35,7 @@ var shelf = null, ...@@ -35,6 +35,7 @@ var shelf = null,
all_shelfs = null, // Use get_all_shelfs to access it's value all_shelfs = null, // Use get_all_shelfs to access it's value
debounce_timeout = null; debounce_timeout = null;
let lastKeypressTime = 0;
/* UTILS */ /* UTILS */
...@@ -1241,6 +1242,19 @@ function init() { ...@@ -1241,6 +1242,19 @@ function init() {
}); });
$(document).pos(); $(document).pos();
$(document).on('keydown','#edition_input',function(event) {
const keypressTime = event.timeStamp;
const timeDifference = keypressTime - lastKeypressTime;
lastKeypressTime = keypressTime;
// Assuming a scanner would input faster than 50ms between keystrokes
if (timeDifference < 50) {
// Looks like scanner input, ignore or handle differently
event.preventDefault();
// You can display a message or handle the input differently
alert("Vous ne pouvez pas scanner pour saisir une quantité.");
}
});
$(document).on('scan.pos.barcode', function(event) { $(document).on('scan.pos.barcode', function(event) {
//access `event.code` - barcode data //access `event.code` - barcode data
var barcode = event.code; var barcode = event.code;
......
...@@ -82,6 +82,8 @@ function init_datatable() { ...@@ -82,6 +82,8 @@ function init_datatable() {
{ {
data:"shelf_value", data:"shelf_value",
title:"Valeur théorique du rayon", title:"Valeur théorique du rayon",
visible: false,
name: "shelf_value",
render: function (data, type) { render: function (data, type) {
if (type == "sort" || type == 'type') if (type == "sort" || type == 'type')
return data; return data;
...@@ -324,7 +326,6 @@ $(document).ready(function() { ...@@ -324,7 +326,6 @@ $(document).ready(function() {
} }
shelfs_table = init_datatable(); shelfs_table = init_datatable();
get_shelfs_extra_data();
$(document).on('click', 'button.do_shelf_inventory', go_to_shelf_inventory); $(document).on('click', 'button.do_shelf_inventory', go_to_shelf_inventory);
$(document).on('click', 'tbody td .delete_ongoing_inv_icon', pre_delete_ongoing_inventory); $(document).on('click', 'tbody td .delete_ongoing_inv_icon', pre_delete_ongoing_inventory);
...@@ -337,4 +338,8 @@ $(document).ready(function() { ...@@ -337,4 +338,8 @@ $(document).ready(function() {
.draw(); .draw();
}); });
$(document).on('click', '#show_and_compute_shelf_value', function () {
shelfs_table.column('shelf_value:name').visible(true);
get_shelfs_extra_data();
});
}); });
...@@ -120,10 +120,9 @@ class CagetteShift(models.Model): ...@@ -120,10 +120,9 @@ class CagetteShift(models.Model):
'shift_type', 'date_alert_stop', 'date_delay_stop', 'extension_ids', 'shift_type', 'date_alert_stop', 'date_delay_stop', 'extension_ids',
'cooperative_state', 'final_standard_point', 'create_date', 'cooperative_state', 'final_standard_point', 'create_date',
'final_ftop_point', 'shift_type', 'leave_ids', 'makeups_to_do', 'barcode_base', 'final_ftop_point', 'shift_type', 'leave_ids', 'makeups_to_do', 'barcode_base',
'street', 'street2', 'zip', 'city', 'mobile', 'phone', 'email', 'street', 'street2', 'zip', 'city', 'mobile', 'phone', 'function', 'email',
'is_associated_people', 'parent_id', 'extra_shift_done'] 'is_associated_people', 'parent_id', 'extra_shift_done']
partnerData = self.o_api.search_read('res.partner', cond, fields, 1) partnerData = self.o_api.search_read('res.partner', cond, fields, 1)
if partnerData: if partnerData:
partnerData = partnerData[0] partnerData = partnerData[0]
if partnerData['is_associated_people']: if partnerData['is_associated_people']:
...@@ -186,6 +185,19 @@ class CagetteShift(models.Model): ...@@ -186,6 +185,19 @@ class CagetteShift(models.Model):
shiftData = self.o_api.search_read('shift.registration', cond, fields, order ="date_begin ASC") shiftData = self.o_api.search_read('shift.registration', cond, fields, order ="date_begin ASC")
return shiftData return shiftData
def get_partners_with_makeups_to_come(self):
"""Returns a dictionary with : keys = the partners ids having at least one makeup to come ; values = #makeups_to_come"""
fields = ['partner_id']
cond = [['state', '=', 'open'], ['date_begin', '>', datetime.datetime.now().isoformat()], ['is_makeup', '=', True]]
shift_data = self.o_api.search_read('shift.registration', cond, fields)
count_dic = {}
for value in shift_data:
if value['partner_id'][0] in count_dic:
count_dic[value['partner_id'][0]] = count_dic[value['partner_id'][0]] + 1
else:
count_dic[value['partner_id'][0]] = 1
return count_dic
def shift_is_makeup(self, id): def shift_is_makeup(self, id):
"""vérifie si une shift est un rattrapage""" """vérifie si une shift est un rattrapage"""
fields = ["is_makeup", "id"] fields = ["is_makeup", "id"]
...@@ -293,12 +305,12 @@ class CagetteShift(models.Model): ...@@ -293,12 +305,12 @@ class CagetteShift(models.Model):
coop_logger.error("Model affect shift nobody found : %s", str(cond)) coop_logger.error("Model affect shift nobody found : %s", str(cond))
return response return response
def cancel_shift(self, idsRegisteur, origin='memberspace'): def cancel_shift(self, idsRegisteur, origin='memberspace', description=''):
"""Annule un shift""" """Annule un shift"""
fieldsDatas = { "related_shift_state": 'cancel', fieldsDatas = { "related_shift_state": 'cancel',
"origin": origin, "origin": origin,
"state": 'cancel'} "state": 'cancel',
"cancellation_description": description}
return self.o_api.update('shift.registration', idsRegisteur, fieldsDatas) return self.o_api.update('shift.registration', idsRegisteur, fieldsDatas)
def reopen_shift(self, data): def reopen_shift(self, data):
...@@ -307,13 +319,26 @@ class CagetteShift(models.Model): ...@@ -307,13 +319,26 @@ class CagetteShift(models.Model):
cond = [['partner_id', '=', int(data['idPartner'])], cond = [['partner_id', '=', int(data['idPartner'])],
['shift_id', '=', int(data['idShift'])], ['shift_id', '=', int(data['idShift'])],
['state', '=', 'cancel']] ['state', '=', 'cancel']]
fields = ['id'] fields = ['id','origin']
try: try:
canceled_res = self.o_api.search_read('shift.registration', cond, fields, 1) canceled_res = self.o_api.search_read('shift.registration', cond, fields, 1)
if (len(canceled_res) == 1): if (len(canceled_res) == 1):
shift_res = canceled_res[0] shift_res = canceled_res[0]
fieldsDatas = { "related_shift_state":'open', fieldsDatas = { "related_shift_state":'open',
"state": 'open'} "state": 'open',
"is_makeup":data['is_makeup'],
"origin":canceled_res[0]['origin'] + ' reopened from memberspace'}
#following code is required to properly set template_created.
#TODO : factor with set_shift code
shift_type = "standard"
if data['shift_type'] == "ftop" or getattr(settings, 'USE_STANDARD_SHIFT', True) == False:
shift_type = "ftop"
if (shift_type == "standard" and data['is_makeup'] is not True) or shift_type == "ftop":
fieldsDatas['template_created'] = 1 # It's not true but otherwise, presence add 1 standard point , which is not wanted
else: #the else does not exist in set_shift but is mandatory here as template_created value in reopened registration can be any value
fieldsDatas["template_created"] = False
response = self.o_api.update('shift.registration', [shift_res['id']], fieldsDatas) response = self.o_api.update('shift.registration', [shift_res['id']], fieldsDatas)
except Exception as e: except Exception as e:
coop_logger.error("Reopen shift : %s", str(e)) coop_logger.error("Reopen shift : %s", str(e))
...@@ -829,6 +854,23 @@ class CagetteServices(models.Model): ...@@ -829,6 +854,23 @@ class CagetteServices(models.Model):
return shift_id return shift_id
@staticmethod @staticmethod
def get_exemptions_shift_id():
shift_id = None
try:
api = OdooAPI()
res = api.search_read('ir.config_parameter',
[['key','=', 'lacagette_exemptions.exemptions_shift_id']],
['value'])
if len(res) > 0:
try:
shift_id = int(res[0]['value'])
except:
pass
except:
pass
return shift_id
@staticmethod
def get_first_ftop_shift_id(): def get_first_ftop_shift_id():
shift_id = None shift_id = None
try: try:
...@@ -870,27 +912,36 @@ class CagetteServices(models.Model): ...@@ -870,27 +912,36 @@ class CagetteServices(models.Model):
if (len(shift_templ_res) > 0 if (len(shift_templ_res) > 0
and and
shift_templ_res[0]['shift_template_id'][0] == committees_shift_id): shift_templ_res[0]['shift_template_id'][0] == committees_shift_id):
ok_for_adding_pt = False
mininum_seconds_interval = getattr(settings, 'MINIMUM_SECONDS_BETWEEN_TWO_COMITEE_VALIDATION', 3600 * 24)
evt_name = getattr(settings, 'ENTRANCE_ADD_PT_EVENT_NAME', 'Validation service comité') evt_name = getattr(settings, 'ENTRANCE_ADD_PT_EVENT_NAME', 'Validation service comité')
if mininum_seconds_interval > 0:
# A constraint has been set to prevent from adding more than 1 point during a time period
# Let's find out when was the last time a "special point" has been addes
c = [['partner_id', '=', coop_id], ['name', '=', evt_name]] c = [['partner_id', '=', coop_id], ['name', '=', evt_name]]
f = ['create_date'] f = ['create_date']
last_point_mvts = api.search_read('shift.counter.event', c, f, last_point_mvts = api.search_read('shift.counter.event', c, f,
order ="create_date DESC", limit=1) order ="create_date DESC", limit=1)
ok_for_adding_pt = False
if len(last_point_mvts): if len(last_point_mvts):
now = datetime.datetime.now() now = datetime.datetime.now()
past = datetime.datetime. strptime(last_point_mvts[0]['create_date'], past = datetime.datetime. strptime(last_point_mvts[0]['create_date'],
'%Y-%m-%d %H:%M:%S') '%Y-%m-%d %H:%M:%S')
if (now - past).total_seconds() >= 3600 * 24: if (now - past).total_seconds() >= mininum_seconds_interval:
ok_for_adding_pt = True
else:
ok_for_adding_pt = True ok_for_adding_pt = True
else: else:
# mininum_seconds_interval is 0 : Point can we added without any condition
ok_for_adding_pt = True ok_for_adding_pt = True
if ok_for_adding_pt is True: if ok_for_adding_pt is True:
res['evt_id'] = CagetteMember(coop_id).add_pts('ftop', 1, evt_name) res['evt_id'] = CagetteMember(coop_id).add_pts('ftop', 1, evt_name)
else: else:
res['error'] = "One point has been added less then 24 hours ago" res['error'] = "Un point a déjà été ajouté trop récemment."
else: else:
res['error'] = "Unallowed coop" res['error'] = "Vous n'avez pas le droit d'ajouter un point."
else: else:
res['error'] = "Unregistred coop" res['error'] = "Unregistred coop"
else: else:
......
...@@ -179,14 +179,11 @@ def get_list_shift_calendar(request, partner_id): ...@@ -179,14 +179,11 @@ def get_list_shift_calendar(request, partner_id):
def get_list_shift_partner(request, partner_id): def get_list_shift_partner(request, partner_id):
cs = CagetteShift() cs = CagetteShift()
shiftData = cs.get_shift_partner(partner_id) shiftData = cs.get_shift_partner(partner_id)
empty_data = False
for value in shiftData: for value in shiftData:
value['date_begin'] = value['date_begin'] + "Z" value['date_begin'] = value['date_begin'] + "Z"
value['date_end'] = value['date_end'] + "Z" value['date_end'] = value['date_end'] + "Z"
if "Services des comités" in value['shift_id'][1]:
empty_data = True
if empty_data is True:
shiftData = []
return JsonResponse(shiftData, safe=False) return JsonResponse(shiftData, safe=False)
def change_shift(request): def change_shift(request):
......
...@@ -373,6 +373,8 @@ class CagetteStock(models.Model): ...@@ -373,6 +373,8 @@ class CagetteStock(models.Model):
cond = [['qty_available','>', 0], ['active', '=', True]] cond = [['qty_available','>', 0], ['active', '=', True]]
fields = ["barcode", "display_name", "qty_available", "standard_price"] fields = ["barcode", "display_name", "qty_available", "standard_price"]
articles = api.search_read('product.product', cond, fields, 1000000) articles = api.search_read('product.product', cond, fields, 1000000)
for a in articles:
a['total'] = a['qty_available'] * a['standard_price']
except Exception as e: except Exception as e:
coop_logger.error("Erreur get_valuable_stock : %s", str(e)) coop_logger.error("Erreur get_valuable_stock : %s", str(e))
return articles return articles
......
...@@ -6,14 +6,29 @@ $(document).ready(function() { ...@@ -6,14 +6,29 @@ $(document).ready(function() {
"data": "" "data": ""
}, },
"columns":[ "columns":[
{data:"barcode", "title":"Code-barre", "width": "50%"}, {data:"barcode", "title":"Code-barre", "width": "10em"},
{data:"display_name", "title":"Article", "width": "50%"}, {data:"display_name", "title":"Article", "width": "50%"},
{data:"qty_available", "title":"Stock", "width":"15%" {data:"qty_available", "title":"Stock", "width":"5em",
render: function(data) {
if (data == parseInt(data,10)) {
return data
} else {
return data.toFixed(3)
}
}
}, },
{data:"standard_price", "title":"Prix achat", "width":"15%" {data:"standard_price", "title":"Prix Achat", "width":"4em",
render: function(data) {
return data.toFixed(2)
} }
},
{data: "total", "title": "Total",
render: function(data, type, full) {
return data.toFixed(2)
}
}
], ],
...@@ -24,7 +39,7 @@ $(document).ready(function() { ...@@ -24,7 +39,7 @@ $(document).ready(function() {
"desc" "desc"
] ]
], ],
"iDisplayLength": 50, "paging": true,
"language": { "language": {
"emptyTable": "Pas de donnée", "emptyTable": "Pas de donnée",
"info": "Affiché : lignes _START_ à _END_ sur _TOTAL_", "info": "Affiché : lignes _START_ à _END_ sur _TOTAL_",
...@@ -55,6 +70,6 @@ $(document).ready(function() { ...@@ -55,6 +70,6 @@ $(document).ready(function() {
className: 'btn--primary btn_export' className: 'btn--primary btn_export'
}, },
], ],
dom: '<lr<t>ip><"clear"><B>', dom: '<lr<t>ip><"clear"><B>'
}); });
}); });
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
<div class="page_content"> <div class="page_content">
<div id="table_top_area"> <div id="table_top_area">
<h3>Liste des membres devant effectuer un rattrapage</h3> <h3>Liste des membres ayant au moins un rattrapage à faire</h3>
<div class="table_grouped_action"> <div class="table_grouped_action">
<button type="button" class="btn--primary" id="decrement_selected_members_makeups"> <button type="button" class="btn--primary" id="decrement_selected_members_makeups">
-1 rattrapage pour les membres sélectionnés -1 rattrapage pour les membres sélectionnés
...@@ -40,7 +40,7 @@ ...@@ -40,7 +40,7 @@
</div> </div>
<div id="add_members_area"> <div id="add_members_area">
<div id="add_members_form_area"> <div id="add_members_form_area">
<h4>Ou, ajouter un rattrapage à un.e membre</h4> <h4>Ou, ajouter un rattrapage à un.e membre standard</h4>
<form id="search_member_form" action="javascript:;" method="post"> <form id="search_member_form" action="javascript:;" method="post">
<input type="text" id="search_member_input" value="" placeholder="Nom ou numéro du coop..." required> <input type="text" id="search_member_input" value="" placeholder="Nom ou numéro du coop..." required>
<button type="submit" class="btn--primary" id="search_member_button">Recherche</button> <button type="submit" class="btn--primary" id="search_member_button">Recherche</button>
...@@ -85,6 +85,7 @@ ...@@ -85,6 +85,7 @@
<script src='{% static "js/all_common.js" %}?v=1651853225'></script> <script src='{% static "js/all_common.js" %}?v=1651853225'></script>
<script src='{% static "js/admin/manage_makeups.js" %}?v=1651853225'></script> <script src='{% static "js/admin/manage_makeups.js" %}?v=1651853225'></script>
<script> <script>
var has_committe_shift = '{{has_committe_shift}}'
const extension_duration = {{extension_duration}}; const extension_duration = {{extension_duration}};
</script> </script>
{% endblock %} {% endblock %}
...@@ -96,6 +96,7 @@ ...@@ -96,6 +96,7 @@
var couchdb_dbname = '{{db}}'; var couchdb_dbname = '{{db}}';
var couchdb_server = '{{couchdb_server}}' + couchdb_dbname; var couchdb_server = '{{couchdb_server}}' + couchdb_dbname;
var committees_shift_id = '{{committees_shift_id}}'; var committees_shift_id = '{{committees_shift_id}}';
var exemptions_shift_id = '{{exemptions_shift_id}}';
const committees_shift_name = '{{committees_shift_name}}'; const committees_shift_name = '{{committees_shift_name}}';
var ASSOCIATE_MEMBER_SHIFT = '{{ASSOCIATE_MEMBER_SHIFT}}'; var ASSOCIATE_MEMBER_SHIFT = '{{ASSOCIATE_MEMBER_SHIFT}}';
</script> </script>
......
...@@ -51,7 +51,16 @@ ...@@ -51,7 +51,16 @@
</div> </div>
</div> </div>
<div id="templates" style="display:none;"></div> <div id="templates" style="display:none;">
<div id="modal_delete_shift_registration">
<p>Enlever la présence de <b><span class="member_name"></span></b> au service du <b><span class="service_name"></span></b> ?</p>
<div id="makeup_case_explanation" style="display:none">
<p><i class="fas fa-exclamation-triangle"></i> Ce service est un rattrapage. Le supprimer ajoutera un point au compteur de ce.tte membre.</p>
</div>
<br><label for="cancellation-explanation">Explication : </label><input class="" type="text" id="cancellation-explanation">
<br><br><label for="cancellation-signature">Signature : </label><input class="" type="text" id="cancellation-signature">
</div>
</div>
</div> </div>
<script src='{% static "js/all_common.js" %}?v=1651853225'></script> <script src='{% static "js/all_common.js" %}?v=1651853225'></script>
......
...@@ -61,6 +61,11 @@ ...@@ -61,6 +61,11 @@
<input type="text" placeholder="email" name="email" value="" pattern="(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*)@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])" required autocomplete="off" /> <input type="text" placeholder="email" name="email" value="" pattern="(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*)@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])" required autocomplete="off" />
(*) (*)
</p> </p>
{% if ask_for_job %}
<p>
{% include "members/job_input.html" %}
</p>
{% endif %}
<p> <p>
<input type="number" step="10" min="10" placeholder="Montant souscription" name="subs_cap" id="subs_cap" required/> <input type="number" step="10" min="10" placeholder="Montant souscription" name="subs_cap" id="subs_cap" required/>
<select name="payment_meaning" id="payment_meaning" autocomplete="off" required > <select name="payment_meaning" id="payment_meaning" autocomplete="off" required >
...@@ -81,7 +86,7 @@ ...@@ -81,7 +86,7 @@
{% endif %} {% endif %}
{% if can_create_binome %} {% if can_create_binome %}
<div>Voulez-vous inscrire la personne en tant que suppléante d'un binôme ?</div> <div>Voulez-vous inscrire la personne en tant que deuxième personne d'un binôme ?</div>
<label> <label>
<input type="radio" name="binome" id="no_binome" required> <input type="radio" name="binome" id="no_binome" required>
Non Non
...@@ -197,6 +202,7 @@ ...@@ -197,6 +202,7 @@
var max_begin_hour = '{{max_begin_hour}}'; var max_begin_hour = '{{max_begin_hour}}';
var email_domain = '{{email_domain}}'; var email_domain = '{{email_domain}}';
var committees_shift_id = '{{committees_shift_id}}'; var committees_shift_id = '{{committees_shift_id}}';
var exemptions_shift_id = '{{exemptions_shift_id}}';
</script> </script>
<script src="{% static "js/all_common.js" %}?v=1651853225"></script> <script src="{% static "js/all_common.js" %}?v=1651853225"></script>
......
<div id="job">
<input type="text" placeholder="Profession" name="job" autocomplete="off" />
</div>
...@@ -106,6 +106,8 @@ ...@@ -106,6 +106,8 @@
var mag_place_string = '{{mag_place_string}}'; var mag_place_string = '{{mag_place_string}}';
var office_place_string = '{{office_place_string}}' var office_place_string = '{{office_place_string}}'
var max_begin_hour = '{{max_begin_hour}}' var max_begin_hour = '{{max_begin_hour}}'
var committees_shift_id = '{{committees_shift_id}}';
var exemptions_shift_id = '{{exemptions_shift_id}}';
</script> </script>
<script src="{% static "js/all_common.js" %}"?v=1651853225></script> <script src="{% static "js/all_common.js" %}"?v=1651853225></script>
<script src="{% static "js/common.js" %}"?v=1651853225></script> <script src="{% static "js/common.js" %}"?v=1651853225></script>
......
...@@ -7,6 +7,11 @@ ...@@ -7,6 +7,11 @@
ou ... ou ...
<br /> <br />
<button class="flex-container--column" data-select="Volant">Volant</button> <button class="flex-container--column" data-select="Volant">Volant</button>
{% if exemptions_shift_id != 0 %}
ou ...
<br />
<button class="flex-container--column" data-select="Exemption">Exempté</button>
{% endif %}
<div class="info"></div> <div class="info"></div>
</div> </div>
<div class="col-5 main_content"> <div class="col-5 main_content">
......
...@@ -9,6 +9,11 @@ ...@@ -9,6 +9,11 @@
<br /> <br />
<button class="flex-container--column" data-select="Volant">Volant</button> <button class="flex-container--column" data-select="Volant">Volant</button>
{% endif %} {% endif %}
{% if exemptions_shift_id != 0 %}
ou ...
<br />
<button class="flex-container--column" data-select="Exemption">Exempté</button>
{% endif %}
<div class="info"></div> <div class="info"></div>
</div> </div>
<div class="col-5 main_content"> <div class="col-5 main_content">
......
...@@ -33,6 +33,11 @@ ...@@ -33,6 +33,11 @@
<a id="change_shift_template" class="btn--primary">Changer</a> <a id="change_shift_template" class="btn--primary">Changer</a>
</span> </span>
</p> </p>
{% if ask_for_job %}
<p>
{% include "members/job_input.html" %}
</p>
{% endif %}
<p> <p>
<input type="number" step="1" min="1" placeholder="Nombre de parts" name="shares_nb" id="shares_nb" class="b_yellow" required disabled/> <input type="number" step="1" min="1" placeholder="Nombre de parts" name="shares_nb" id="shares_nb" class="b_yellow" required disabled/>
<label for="shares_nb">Parts sociales</label> <label for="shares_nb">Parts sociales</label>
......
<div id="faqBDM" class=" mt-3"> <div id="faqBDM" class=" mt-3">
<div class="page_title txtcenter"> <div class="page_title txtcenter">
...@@ -157,8 +158,8 @@ ...@@ -157,8 +158,8 @@
href="javascript:void(0);" href="javascript:void(0);"
target="_blank" target="_blank"
type="button" type="button"
class="btn--primary faq_link_button ask_bdm_form_link" class="btn--primary faq_link_button"
id="request_form_link_btn3" id="late_service_form_link_btn"
> >
Oubli validation service Oubli validation service
</a> </a>
......
...@@ -132,7 +132,8 @@ ...@@ -132,7 +132,8 @@
</div> </div>
<script> <script>
var max_begin_hour = "{{max_begin_hour}}", var max_begin_hour = "{{max_begin_hour}}",
type = 2; type = 2,
exemptions_shift_id = '{{exemptions_shift_id}}';
</script> </script>
{% endif %} {% endif %}
</div> </div>
...@@ -101,6 +101,14 @@ ...@@ -101,6 +101,14 @@
<h3>Vous êtes inscrit.e dans le service des comités, vous n'avez pas accès au calendrier d'échange des services car vous vous organisez directement avec le responsable du comité. Si vous avez des rattrapages à réaliser, merci de contacter le responsable du comité qui vous aidera à planifier les rattrapages ou trouver une solution.</h3> <h3>Vous êtes inscrit.e dans le service des comités, vous n'avez pas accès au calendrier d'échange des services car vous vous organisez directement avec le responsable du comité. Si vous avez des rattrapages à réaliser, merci de contacter le responsable du comité qui vous aidera à planifier les rattrapages ou trouver une solution.</h3>
<h3>Le calendrier ci-dessous est en lecture seule</h3> <h3>Le calendrier ci-dessous est en lecture seule</h3>
</div> </div>
<div id="comite_my_shifs_message">
<p data-type="nb_of_shifs_state">
Vous avez actuellement <span data-type="shifts_nb"></span> <span data-type="service_txt">service</span> d'avance.
</p>
<p data-type="next_ftop_shift">
Le système informatique viendra prélever un service sur votre compteur le <span data-type="next_ftop_shift_date"></span>.
</p>
</div>
</div> </div>
</div> </div>
...@@ -129,6 +137,8 @@ ...@@ -129,6 +137,8 @@
"partner_id":"{{partnerData.id}}", "partner_id":"{{partnerData.id}}",
"name":"{{partnerData.display_name|safe}}", "name":"{{partnerData.display_name|safe}}",
"shift_type":"{{partnerData.shift_type}}", "shift_type":"{{partnerData.shift_type}}",
"final_ftop_point":{{partnerData.final_ftop_point}},
"final_standard_point":{{partnerData.final_standard_point}},
"date_delay_stop":"{{partnerData.date_delay_stop}}", "date_delay_stop":"{{partnerData.date_delay_stop}}",
"cooperative_state":"{{partnerData.cooperative_state}}", "cooperative_state":"{{partnerData.cooperative_state}}",
"regular_shift_name":"{{partnerData.regular_shift_name}}", "regular_shift_name":"{{partnerData.regular_shift_name}}",
...@@ -141,6 +151,7 @@ ...@@ -141,6 +151,7 @@
"city" : "{{partnerData.city|safe}}", "city" : "{{partnerData.city|safe}}",
"mobile" : "{{partnerData.mobile}}", "mobile" : "{{partnerData.mobile}}",
"phone" : "{{partnerData.phone}}", "phone" : "{{partnerData.phone}}",
"function" : "{{partnerData.function}}",
"email" : "{{partnerData.email}}", "email" : "{{partnerData.email}}",
"is_associated_people" : "{{partnerData.is_associated_people}}", "is_associated_people" : "{{partnerData.is_associated_people}}",
"parent_id" : "{{partnerData.parent_id}}", "parent_id" : "{{partnerData.parent_id}}",
......
...@@ -88,6 +88,20 @@ ...@@ -88,6 +88,20 @@
<span id="save_edit_phone"><i class="fas fa-check tile_icon edit-btn"></i></span> <span id="save_edit_phone"><i class="fas fa-check tile_icon edit-btn"></i></span>
</div> </div>
</div> </div>
<div class="my_info_line member_job_line">
<div class="my_info_line_left">
Profession
</div>
<div class="my_info_line_right member_job_area" id="edit_job_value">
<span class="member_job member_info"></span>
<span id="edit_job"><i class="fas fa-edit tile_icon edit-btn" ></i></span>
</div>
<div class="my_info_line_right member_job_area1" id="edit_job_form">
<input type="text" name="job" id="job_form" placeholder="Profession"> <br>
<span id="cancel_edit_job"><i class="fas fa-times tile_icon edit-btn"></i></span>
<span id="save_edit_job"><i class="fas fa-check tile_icon edit-btn"></i></span>
</div>
</div>
</div> </div>
</div> </div>
<div class="tile full_width_tile" id="attached_info_area"> <div class="tile full_width_tile" id="attached_info_area">
......
...@@ -47,7 +47,7 @@ ...@@ -47,7 +47,7 @@
temporaire, n’oublie pas de recontacter le Bureau des Membres à ton retour pour te temporaire, n’oublie pas de recontacter le Bureau des Membres à ton retour pour te
réinscrire sur un créneau. </p> réinscrire sur un créneau. </p>
</div> </div>
<div class="grp_text"><h3><b>Je m'absente plus longtemps pour une autre raison (plus de 4 semaines)</b></h3> <div class="grp_text"><h3><b>Je m'absente plus longtemps pour une autre raison (plus de 2 mois)</b></h3>
<p class="attached-unblocked"> Envoie un mail au Bureau des Membres <p class="attached-unblocked"> Envoie un mail au Bureau des Membres
<a href="mailto:bdm@supercafoutch.fr?subject=Incapacité à faire mes services pour cause d'absence prolongée">bdm@supercafoutch.fr</a> <a href="mailto:bdm@supercafoutch.fr?subject=Incapacité à faire mes services pour cause d'absence prolongée">bdm@supercafoutch.fr</a>
......
...@@ -39,6 +39,10 @@ ...@@ -39,6 +39,10 @@
</div> </div>
</div> </div>
<div id="footer_btn">
<button id='show_and_compute_shelf_value' class='btn--primary'>Afficher et calculer la valeur théorique des rayons</button>
</div>
<div id="templates" style="display:none;"> <div id="templates" style="display:none;">
<div id="modal_delete_ongoing_inv"> <div id="modal_delete_ongoing_inv">
<h4>Attention !</h4> <h4>Attention !</h4>
......
...@@ -21,7 +21,8 @@ ...@@ -21,7 +21,8 @@
city : '{{data.city}}', city : '{{data.city}}',
email: '{{data.email}}', email: '{{data.email}}',
mobile : '{{data.mobile}}', mobile : '{{data.mobile}}',
phone : '{{data.phone}}' phone : '{{data.phone}}',
function: '{{data.job}}'
}, },
name_sep = '{{data.name_sep}}' name_sep = '{{data.name_sep}}'
</script> </script>
...@@ -70,6 +71,9 @@ ...@@ -70,6 +71,9 @@
<select name="yyyy"></select> <select name="yyyy"></select>
<input type="hidden" name="birthdate" value="{{data.birthdate}}" /> <input type="hidden" name="birthdate" value="{{data.birthdate}}" />
</p> </p>
<p>
{% include "members/job_input.html" %}
</p>
</div> </div>
</div> </div>
<div id="address"> <div id="address">
......
...@@ -65,6 +65,8 @@ def index(request): ...@@ -65,6 +65,8 @@ def index(request):
'WELCOME_SUBTITLE_ENTRANCE_MSG': getattr(settings, 'WELCOME_SUBTITLE_ENTRANCE_MSG', '')} 'WELCOME_SUBTITLE_ENTRANCE_MSG': getattr(settings, 'WELCOME_SUBTITLE_ENTRANCE_MSG', '')}
if hasattr(settings, 'SUBSCRIPTION_ASK_FOR_SEX'): if hasattr(settings, 'SUBSCRIPTION_ASK_FOR_SEX'):
context['ask_for_sex'] = settings.SUBSCRIPTION_ASK_FOR_SEX context['ask_for_sex'] = settings.SUBSCRIPTION_ASK_FOR_SEX
if hasattr(settings, 'SUBSCRIPTION_ASK_FOR_JOB'):
context['ask_for_job'] = settings.SUBSCRIPTION_ASK_FOR_JOB
if hasattr(settings, 'SUBSCRIPTION_ADD_STREET2'): if hasattr(settings, 'SUBSCRIPTION_ADD_STREET2'):
context['ask_for_street2'] = settings.SUBSCRIPTION_ADD_STREET2 context['ask_for_street2'] = settings.SUBSCRIPTION_ADD_STREET2
if hasattr(settings, 'SUBSCRIPTION_ADD_SECOND_PHONE'): if hasattr(settings, 'SUBSCRIPTION_ADD_SECOND_PHONE'):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment