Commit ad361176 by François C.

Merge branch 'dev_cooperatic' into 'dev_principale'

Intégration des Dev cooperatic (Aide à la commande, Réception avec cache couchDB, possibilité de valider une présence Comité)

See merge request cooperatic-foodcoops/third-party!45
parents e50cbf8a 4a99e4fb
...@@ -97,7 +97,8 @@ SHIFT_INFO = """A la cagette, un service est une plage de trois heures un jour ...@@ -97,7 +97,8 @@ SHIFT_INFO = """A la cagette, un service est une plage de trois heures un jour
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 vous rendre dans la section \"J'ai un problème\" sur le site web de <a href=\"https://lacagette-coop.fr/?MonEspaceMembre\">La Cagette</a>""" 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 vous rendre dans la section \"J'ai un problème\" sur le site web de <a href=\"https://lacagette-coop.fr/?MonEspaceMembre\">La Cagette</a>"""
ENTRANCE_COME_FOR_SHOPING_MSG = "Hey coucou toi ! Cet été nous sommes plus de <strong>1000 acheteur·euses</strong> pour seulement <strong>300 coopérateur·rice·s</strong> en service. <br />Tu fais tes courses à La Cagette cet été ?<br/> Inscris-toi sur ton espace membre !" ENTRANCE_COME_FOR_SHOPING_MSG = "Hey coucou toi ! Cet été nous sommes plus de <strong>1000 acheteur·euses</strong> pour seulement <strong>300 coopérateur·rice·s</strong> en service. <br />Tu fais tes courses à La Cagette cet été ?<br/> Inscris-toi sur ton espace membre !"
ENTRANCE_EXTRA_BUTTONS_DISPLAY = False
ENTRANCE_EASY_SHIFT_VALIDATE = True
# Members space / shifts # Members space / shifts
UNSUBSCRIBED_MSG = 'Vous êtes désincrit·e, merci de remplir <a href="https://docs.google.com/forms/d/e/1FAIpQLSfPiC2PkSem9x_B5M7LKpoFNLDIz0k0V5I2W3Mra9AnqnQunw/viewform">ce formulaire</a> pour vous réinscrire sur un créneau.<br />Vous pouvez également contacter le Bureau des Membres en remplissant <a href="https://docs.google.com/forms/d/e/1FAIpQLSeZP0m5-EXPVJxEKJk6EjwSyZJtnbiGdYDuAeFI3ENsHAOikg/viewform">ce formulaire</a>' UNSUBSCRIBED_MSG = 'Vous êtes désincrit·e, merci de remplir <a href="https://docs.google.com/forms/d/e/1FAIpQLSfPiC2PkSem9x_B5M7LKpoFNLDIz0k0V5I2W3Mra9AnqnQunw/viewform">ce formulaire</a> pour vous réinscrire sur un créneau.<br />Vous pouvez également contacter le Bureau des Membres en remplissant <a href="https://docs.google.com/forms/d/e/1FAIpQLSeZP0m5-EXPVJxEKJk6EjwSyZJtnbiGdYDuAeFI3ENsHAOikg/viewform">ce formulaire</a>'
CONFIRME_PRESENT_BTN = 'Clique ici pour valider ta présence' CONFIRME_PRESENT_BTN = 'Clique ici pour valider ta présence'
...@@ -106,4 +107,5 @@ RECEPTION_PB = "Ici, vous pouvez signaler toute anomalie lors d'une réception, ...@@ -106,4 +107,5 @@ RECEPTION_PB = "Ici, vous pouvez signaler toute anomalie lors d'une réception,
Merci d'indiquer un maximum d'informations, le nom du produit et son code barre. \ Merci d'indiquer un maximum d'informations, le nom du produit et son code barre. \
Dans le cas de produits déteriorés, merci d'envoyer une photo avec votre téléphone à [Adresse_email]" Dans le cas de produits déteriorés, merci d'envoyer une photo avec votre téléphone à [Adresse_email]"
# display or not column "Autres" in reception process
DISPLAY_COL_AUTRES = False
Received reception data are stored in this directory (could be desactivated if RECEPTION_DATA_BACKUP is set to False in config.py)
\ No newline at end of file
...@@ -52,7 +52,7 @@ class CagetteInventory(models.Model): ...@@ -52,7 +52,7 @@ class CagetteInventory(models.Model):
file_data = json.load(json_file) file_data = json.load(json_file)
date_time = datetime.fromtimestamp(int(filename)) date_time = datetime.fromtimestamp(int(filename))
d = date_time.strftime("%m/%d/%Y, %H:%M") d = date_time.strftime("%d/%m/%Y, %H:%M")
file_data['id'] = int(filename) file_data['id'] = int(filename)
file_data['datetime_created'] = d file_data['datetime_created'] = d
...@@ -113,7 +113,7 @@ class CagetteInventory(models.Model): ...@@ -113,7 +113,7 @@ class CagetteInventory(models.Model):
return file_data['inventory_status'] return file_data['inventory_status']
@staticmethod @staticmethod
def create_custom_inv_file(line_ids, line_type): def create_custom_inv_file(line_ids, line_type, default_partners_id=[]):
res = {} res = {}
try: try:
...@@ -127,7 +127,22 @@ class CagetteInventory(models.Model): ...@@ -127,7 +127,22 @@ class CagetteInventory(models.Model):
api = OdooAPI() api = OdooAPI()
ids = [] ids = []
order = ['', ''] order = ['', '']
user = partner = '' user = ''
partners = []
if len(default_partners_id) > 0:
f = ['name']
c = [['id', 'in', default_partners_id]]
partners_name = api.search_read('res.partner', c, f)
for p in partners_name:
partners.append(p['name'])
if line_type == 'product_templates':
fields = ['id']
cond = [['product_tmpl_id', 'in', line_ids]]
model = 'product.product'
user="api"
else:
fields = ['create_uid', 'product_id', 'partner_id'] fields = ['create_uid', 'product_id', 'partner_id']
cond = [['id', 'in', line_ids]] cond = [['id', 'in', line_ids]]
if (line_type == 'cpo'): if (line_type == 'cpo'):
...@@ -139,24 +154,27 @@ class CagetteInventory(models.Model): ...@@ -139,24 +154,27 @@ class CagetteInventory(models.Model):
lines = api.search_read(model, cond, fields) lines = api.search_read(model, cond, fields)
if len(lines) == len(line_ids): if len(lines) == len(line_ids):
for l in lines: for l in lines:
if line_type == 'product_templates':
ids.append(l['id'])
else:
ids.append(l['product_id'][0]) ids.append(l['product_id'][0])
user = l['create_uid'][1] user = l['create_uid'][1]
if (line_type == 'cpo'): if (line_type == 'cpo'):
order = l['computed_purchase_order_id'] order = l['computed_purchase_order_id']
else: else:
order = l['order_id'] order = l['order_id']
partner = l['partner_id'][1] partners.append(l['partner_id'][1])
if (line_type == 'cpo'): if (line_type == 'cpo'):
# partner_id isn't defined # partner_id isn't defined
f = ['partner_id'] f = ['partner_id']
c = [['id', '=', int(order[0])]] c = [['id', '=', int(order[0])]]
cpo = api.search_read('computed.purchase.order', c, f) cpo = api.search_read('computed.purchase.order', c, f)
if len(cpo) > 0: if len(cpo) > 0:
partner = cpo[0]['partner_id'][1] partners.append(cpo[0]['partner_id'][1])
file_data = { file_data = {
'order': order[1], 'order': order[1],
'user': user, 'user': user,
'partner': partner, 'partners': partners,
'inventory_status': '', 'inventory_status': '',
'products': ids 'products': ids
} }
......
...@@ -4,6 +4,13 @@ var shelfs_table = null, ...@@ -4,6 +4,13 @@ var shelfs_table = null,
function init_datatable() { function init_datatable() {
// For a smooth migration...
for (const i in lists) {
if (('partners' in lists[i]) === false) {
lists[i]['partners'] = [lists[i]['partner']];
}
}
return $('#lists').DataTable({ return $('#lists').DataTable({
data: lists, // data passed at page loading data: lists, // data passed at page loading
rowId: 'id', rowId: 'id',
...@@ -17,8 +24,16 @@ function init_datatable() { ...@@ -17,8 +24,16 @@ function init_datatable() {
} }
}, },
{ {
data:"partner", data:"partners",
title:"Fournisseur" title:"Fournisseur(s)",
render: function (data) {
res = "";
for (const i in data) {
res += `${data[i]}<br/>`;
}
return res;
}
}, },
{ {
data:"order", data:"order",
......
...@@ -43,7 +43,6 @@ def custom_list_inventory(request, id): ...@@ -43,7 +43,6 @@ def custom_list_inventory(request, id):
products = CagetteInventory.get_custom_list_products(id) products = CagetteInventory.get_custom_list_products(id)
if 'error' in products: if 'error' in products:
print(products)
products['data'] = [] products['data'] = []
context = {'title': 'Inventaire', context = {'title': 'Inventaire',
...@@ -112,10 +111,25 @@ def do_custom_list_inventory(request): ...@@ -112,10 +111,25 @@ def do_custom_list_inventory(request):
def generate_inventory_list(request): def generate_inventory_list(request):
"""Responding to Odoo ajax call (no csrf).""" """Responding to Odoo ajax call (no csrf)."""
res = {} res = {}
default_partners_id = []
try: try:
lines = json.loads(request.POST.get('lines')) lines = json.loads(request.POST.get('lines'))
ltype = request.POST.get('type') ltype = request.POST.get('type')
res = CagetteInventory.create_custom_inv_file(lines, ltype) except Exception as e:
try:
# POST.get() returns None when request from django
data = json.loads(request.body.decode())
lines = data["lines"]
ltype = data["type"]
if "partners_id" in data:
default_partners_id = data["partners_id"]
except Exception as ee:
res['error'] = str(ee)
coop_looger.error("generate_inventory_list : %s", str(e))
return JsonResponse(res, status=500)
try:
res = CagetteInventory.create_custom_inv_file(lines, ltype, default_partners_id)
except Exception as e: except Exception as e:
res['error'] = str(e) res['error'] = str(e)
coop_looger.error("generate_inventory_list : %s", str(e)) coop_looger.error("generate_inventory_list : %s", str(e))
......
...@@ -22,7 +22,7 @@ class CagetteMember(models.Model): ...@@ -22,7 +22,7 @@ class CagetteMember(models.Model):
"""Class to handle cagette Odoo member.""" """Class to handle cagette Odoo member."""
m_default_fields = ['name', 'sex', 'image_medium', 'active', m_default_fields = ['name', 'sex', 'image_medium', 'active',
'barcode_base', 'barcode', 'in_ftop_team', 'barcode_base', 'barcode', 'in_ftop_team',
'is_associated_people', 'is_member', 'is_associated_people', 'is_member', 'shift_type',
'display_ftop_points', 'display_std_points', 'display_ftop_points', 'display_std_points',
'is_exempted', 'cooperative_state', 'date_alert_stop'] 'is_exempted', 'cooperative_state', 'date_alert_stop']
...@@ -1176,6 +1176,44 @@ class CagetteServices(models.Model): ...@@ -1176,6 +1176,44 @@ class CagetteServices(models.Model):
result['service_found'] = False result['service_found'] = False
return result return result
@staticmethod
def easy_validate_shift_presence(coop_id):
"""Add a presence point if the request is valid."""
res = {}
try:
api = OdooAPI()
# let verify coop_id is corresponding to a ftop subscriber
cond = [['id', '=', coop_id]]
fields = ['shift_type']
coop = api.search_read('res.partner', cond, fields)
if coop:
if coop[0]['shift_type'] == 'ftop':
evt_name = getattr(settings, 'ENTRANCE_ADD_PT_EVENT_NAME', 'Validation service comité')
c = [['partner_id', '=', coop_id], ['name', '=', evt_name]]
f = ['create_date']
last_point_mvts = api.search_read('shift.counter.event', c, f,
order ="create_date DESC", limit=1)
ok_for_adding_pt = False
if len(last_point_mvts):
now = datetime.datetime.now()
past = datetime.datetime. strptime(last_point_mvts[0]['create_date'],
'%Y-%m-%d %H:%M:%S')
if (now - past).total_seconds() >= 3600 * 24:
ok_for_adding_pt = True
else:
ok_for_adding_pt = True
if ok_for_adding_pt is True:
res['evt_id'] = CagetteMember(coop_id).add_pts('ftop', 1, evt_name)
else:
res['error'] = "One point has been added less then 24 hours ago"
else:
res['error'] = "Unallowed coop"
else:
res['error'] = "Invalid coop id"
except Exception as e:
coop_logger.error("easy_validate_shift_presence : %s %s", str(coop_id), str(e))
return res
class CagetteUser(models.Model): class CagetteUser(models.Model):
@staticmethod @staticmethod
......
...@@ -58,7 +58,8 @@ h1 .member_name {font-weight: bold;} ...@@ -58,7 +58,8 @@ h1 .member_name {font-weight: bold;}
#member_slide .btn[data-next] #member_slide .btn[data-next]
{margin-top: 35px; background: #449d44; color:#FFF;} {margin-top: 35px; background: #449d44; color:#FFF;}
[data-next="rattrapage_1"] {float:right;color:#171A17 !important; margin-top: 25px;} [data-next="rattrapage_1"] {float:right;color:#171A17 !important; margin-top: 25px;}
#service_en_cours .info a {line-height: 24px; font-size:18px; margin-top:15px;} #service_en_cours .info {font-size: 26px;}
#service_en_cours .info a {line-height: 24px; font-size:14px; margin-top:15px;}
.outside_list a {margin-left:15px;} .outside_list a {margin-left:15px;}
#rattrapage_1 .advice {margin-top:15px;} #rattrapage_1 .advice {margin-top:15px;}
.btn.present {background:#50C878;} .btn.present {background:#50C878;}
...@@ -67,3 +68,5 @@ h1 .member_name {font-weight: bold;} ...@@ -67,3 +68,5 @@ h1 .member_name {font-weight: bold;}
.msg-big {font-size: xx-large; background: #fff; padding:25px; text-align: center;} .msg-big {font-size: xx-large; background: #fff; padding:25px; text-align: center;}
#member_advice {background: #FFF; color: red;} #member_advice {background: #FFF; color: red;}
.easy_shift_validate {text-align: center; margin-top: 3em;}
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
})(jQuery); })(jQuery);
var current_displayed_member = null, var current_displayed_member = null,
operator = null,
results = null, results = null,
loaded_services = null, loaded_services = null,
selected_service = null, selected_service = null,
...@@ -23,6 +24,8 @@ var current_displayed_member = null, ...@@ -23,6 +24,8 @@ var current_displayed_member = null,
rattrapage_ou_volant = null, rattrapage_ou_volant = null,
timeout_counter = null; timeout_counter = null;
var search_button = $('.btn--primary.search'); var search_button = $('.btn--primary.search');
var sm_search_member_button = $('#sm_search_member_button'),
sm_search_member_input = $('#sm_search_member_input');
var loading2 = $('.loading2'); var loading2 = $('.loading2');
var search_field = $('input[name="search_string"]'); var search_field = $('input[name="search_string"]');
var shift_title = $('#current_shift_title'); var shift_title = $('#current_shift_title');
...@@ -36,6 +39,8 @@ var photo_advice = $('#photo_advice'); ...@@ -36,6 +39,8 @@ var photo_advice = $('#photo_advice');
var photo_studio = $('#photo_studio'); var photo_studio = $('#photo_studio');
var coop_info = $('.coop-info'); var coop_info = $('.coop-info');
const missed_begin_msg = $('#missed_begin_msg').html();
let no_pict_msg = $('#no-picture-msg'); let no_pict_msg = $('#no-picture-msg');
var pages = { var pages = {
...@@ -61,6 +66,7 @@ var html_elts = { ...@@ -61,6 +66,7 @@ var html_elts = {
next_shifts : $('#next_shifts') next_shifts : $('#next_shifts')
}; };
var chars = []; //input chars buffer
function fill_member_slide(member) { function fill_member_slide(member) {
no_pict_msg.hide(); no_pict_msg.hide();
...@@ -160,6 +166,8 @@ function canSearch() { ...@@ -160,6 +166,8 @@ function canSearch() {
} }
function search_member(force_search = false) { function search_member(force_search = false) {
chars = []; // to prevent false "as barcode-reader" input
operator = null;
if (canSearch() || force_search) { if (canSearch() || force_search) {
html_elts.member_slide.hide(); html_elts.member_slide.hide();
...@@ -277,7 +285,15 @@ function fill_service_entry(s) { ...@@ -277,7 +285,15 @@ function fill_service_entry(s) {
rattrapage_wanted.show(); rattrapage_wanted.show();
} }
function clean_search_for_easy_validate_zone() {
$('.search_member_results_area').hide();
$('.search_member_results').empty();
sm_search_member_input.val('');
operator = null;
}
function clean_service_entry() { function clean_service_entry() {
clean_search_for_easy_validate_zone();
rattrapage_wanted.hide(); rattrapage_wanted.hide();
shift_title.text(''); shift_title.text('');
shift_members.html(''); shift_members.html('');
...@@ -344,8 +360,8 @@ function get_service_entry_data() { ...@@ -344,8 +360,8 @@ function get_service_entry_data() {
page_title.text('Qui es-tu ?'); page_title.text('Qui es-tu ?');
try { try {
if (rData.res.length == 0) { if (rData.res.length == 0) {
info_place.text('La période pendant laquelle il est possible de s\'enregistrer est close.'); info_place.html(missed_begin_msg);
page_title.text(''); page_title.html('');
} else { } else {
if (rData.res.length > 1) { if (rData.res.length > 1) {
...@@ -665,9 +681,74 @@ html_elts.image_medium.on('click', function() { ...@@ -665,9 +681,74 @@ html_elts.image_medium.on('click', function() {
} }
}); });
$(document).ready(function() { function ask_for_easy_shift_validation() {
var chars = []; //alert("operator = " + JSON.stringify(operator))
msg = "<p>Je suis bien " + operator.name + "<br/> et <br/>je valide mon service 'Comité' </p>";
openModal(msg, function() {
try {
post_form(
'/members/easy_validate_shift_presence',
{
coop_id: operator.id
},
function(err, result) {
if (!err) {
alert("1 point volant vient d'être ajouté.");
clean_search_for_easy_validate_zone();
closeModal();
} else {
if (typeof (err.responseJSON) != "undefined"
&& typeof (err.responseJSON.error) != "undefined") {
alert(err.responseJSON.error);
} else {
console.log(err);
}
}
}
);
} catch (e) {
console.log(e);
}
}, 'Confirmer');
}
// Display the members from the search result (copied from stock_movements)
function display_possible_members() {
$('.search_member_results_area').show();
$('.search_member_results').empty();
if (members_search_results.length > 0) {
for (member of members_search_results) {
let btn_classes = "btn";
if (operator != null && operator.id == member.id) {
btn_classes = "btn--success";
}
// Display results (possible members) as buttons
var member_button = '<button class="' + btn_classes + ' btn_member" member_id="'
+ member.id + '">'
+ member.barcode_base + ' - ' + member.name
+ '</button>';
$('.search_member_results').append(member_button);
// Set action on click on a member button
$('.btn_member').on('click', function() {
for (member of members_search_results) {
if (member.id == $(this).attr('member_id')) {
operator = member;
// Enable validation button when operator is selected
ask_for_easy_shift_validation();
break;
}
}
display_possible_members();
});
}
} else {
$('.search_member_results').html('<p><i>Aucun résultat ! Faites-vous partie d\'un comité ? <br/> Si oui, vérifiez la recherche..</i></p>');
}
}
$(document).ready(function() {
var shopping_entry_btn = $('a[data-next="shopping_entry"]'); var shopping_entry_btn = $('a[data-next="shopping_entry"]');
shopping_entry_btn.on('click', function() { shopping_entry_btn.on('click', function() {
...@@ -708,6 +789,44 @@ $(document).ready(function() { ...@@ -708,6 +789,44 @@ $(document).ready(function() {
init_webcam(); init_webcam();
}); });
$('#sm_search_member_form').submit(function() {
if (is_time_to('search_member', 1000)) {
sm_search_member_button.empty().append(`<i class="fas fa-spinner fa-spin"></i>`);
let search_str = sm_search_member_input.val();
$.ajax({
url: '/members/search/' + search_str,
dataType : 'json',
success: function(data) {
members_search_results = [];
for (member of data.res) {
if (member.shift_type == 'ftop') {
members_search_results.push(member);
}
}
display_possible_members();
},
error: function(data) {
err = {
msg: "erreur serveur lors de la recherche de membres",
ctx: 'easy_validate.search_members'
};
report_JS_error(err, 'members');
$.notify("Erreur lors de la recherche de membre, il faut ré-essayer plus tard...", {
globalPosition:"top right",
className: "error"
});
},
complete: function() {
sm_search_member_button.empty().append(`Recherche`);
}
});
}
});
}); });
Webcam.on('live', function() { Webcam.on('live', function() {
......
...@@ -258,21 +258,21 @@ function save_current_coop(callback) { ...@@ -258,21 +258,21 @@ function save_current_coop(callback) {
sex_error = false; sex_error = false;
if (/([0-9]{2})\/([0-9]{2})\/([0-9]{4})/.exec(birthdate)) { if (/([0-9]{2})\/([0-9]{2})\/([0-9]{4})/.exec(birthdate)) {
try{ try {
var jj = RegExp.$1, var jj = RegExp.$1,
mm = RegExp.$2, mm = RegExp.$2,
aaaa = RegExp.$3; aaaa = RegExp.$3;
let tmp_date = aaaa + "-" + mm + "-" + jj; let tmp_date = aaaa + "-" + mm + "-" + jj;
// try to create a date object // try to create a date object
date_test = new Date(tmp_date); date_test = new Date(tmp_date);
// if date is invalid a correction is apply in date object. Check it // if date is invalid a correction is apply in date object. Check it
// january start at 0, so we add + 1 for the month // january start at 0, so we add + 1 for the month
if ((date_test.getDate() !== parseInt(jj)) || ((date_test.getMonth()+1) !== parseInt(mm)) || (date_test.getFullYear() !== parseInt(aaaa)) || !date_test.isValid()) if ((date_test.getDate() !== parseInt(jj)) || ((date_test.getMonth()+1) !== parseInt(mm)) || (date_test.getFullYear() !== parseInt(aaaa)) || !date_test.isValid()) {
{
birthdate_error = true; birthdate_error = true;
} }
}catch(Exception){ } catch (Exception) {
birthdate_error = true; birthdate_error = true;
} }
......
...@@ -46,6 +46,7 @@ urlpatterns = [ ...@@ -46,6 +46,7 @@ urlpatterns = [
url(r'^remove_data_from_couchdb$', views.remove_data_from_CouchDB), url(r'^remove_data_from_couchdb$', views.remove_data_from_CouchDB),
url(r'^image/([0-9]+)', views.getmemberimage), url(r'^image/([0-9]+)', views.getmemberimage),
url(r'^add_pts_to_everybody/([0-9]+)/([a-zA-Z0-9_ ]+)$', admin.add_pts_to_everybody), url(r'^add_pts_to_everybody/([0-9]+)/([a-zA-Z0-9_ ]+)$', admin.add_pts_to_everybody),
url(r'^easy_validate_shift_presence$', views.easy_validate_shift_presence),
# conso / groupe recherche / socio # conso / groupe recherche / socio
url(r'^panel_get_purchases$', views.panel_get_purchases), url(r'^panel_get_purchases$', views.panel_get_purchases),
] ]
...@@ -24,6 +24,10 @@ def index(request): ...@@ -24,6 +24,10 @@ def index(request):
'WELCOME_SUBTITLE_ENTRANCE_MSG': getattr(settings, 'WELCOME_SUBTITLE_ENTRANCE_MSG', ''), 'WELCOME_SUBTITLE_ENTRANCE_MSG': getattr(settings, 'WELCOME_SUBTITLE_ENTRANCE_MSG', ''),
'ENTRANCE_SHOPPING_BTN': getattr(settings, 'ENTRANCE_SHOPPING_BTN', 'Je viens faire mes courses'), 'ENTRANCE_SHOPPING_BTN': getattr(settings, 'ENTRANCE_SHOPPING_BTN', 'Je viens faire mes courses'),
'ENTRANCE_SERVICE_BTN': getattr(settings, 'ENTRANCE_SERVICE_BTN', 'Je viens faire mon service'), 'ENTRANCE_SERVICE_BTN': getattr(settings, 'ENTRANCE_SERVICE_BTN', 'Je viens faire mon service'),
'ENTRANCE_MISSED_SHIFT_BEGIN_MSG': getattr(settings, 'ENTRANCE_MISSED_SHIFT_BEGIN_MSG',
"La période pendant laquelle il est possible de s'enregistrer est close."),
'ENTRANCE_EASY_SHIFT_VALIDATE_MSG': getattr(settings, 'ENTRANCE_EASY_SHIFT_VALIDATE_MSG',
'Je valide mon service "Comité"'),
'CONFIRME_PRESENT_BTN' : getattr(settings, 'CONFIRME_PRESENT_BTN', 'Présent.e') 'CONFIRME_PRESENT_BTN' : getattr(settings, 'CONFIRME_PRESENT_BTN', 'Présent.e')
} }
for_shoping_msg = getattr(settings, 'ENTRANCE_COME_FOR_SHOPING_MSG', '') for_shoping_msg = getattr(settings, 'ENTRANCE_COME_FOR_SHOPING_MSG', '')
...@@ -33,6 +37,8 @@ def index(request): ...@@ -33,6 +37,8 @@ def index(request):
for_shoping_msg = msettings['msg_accueil']['value'] for_shoping_msg = msettings['msg_accueil']['value']
context['ENTRANCE_COME_FOR_SHOPING_MSG'] = for_shoping_msg context['ENTRANCE_COME_FOR_SHOPING_MSG'] = for_shoping_msg
context['ftop_btn_display'] = getattr(settings, 'ENTRANCE_FTOP_BUTTON_DISPLAY', True) context['ftop_btn_display'] = getattr(settings, 'ENTRANCE_FTOP_BUTTON_DISPLAY', True)
context['extra_btns_display'] = getattr(settings, 'ENTRANCE_EXTRA_BUTTONS_DISPLAY', True)
context['easy_shift_validate'] = getattr(settings, 'ENTRANCE_EASY_SHIFT_VALIDATE', False)
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:
context['no_picture_member_advice'] = msettings['no_picture_member_advice']['value'] context['no_picture_member_advice'] = msettings['no_picture_member_advice']['value']
...@@ -283,6 +289,22 @@ def record_service_presence(request): ...@@ -283,6 +289,22 @@ def record_service_presence(request):
res['error'] = str(e) res['error'] = str(e)
return JsonResponse({'res': res}) return JsonResponse({'res': res})
def easy_validate_shift_presence(request):
"""Add a presence point if the request is valid."""
res = {}
try:
coop_id = int(request.POST.get("coop_id", "nan"))
res = CagetteServices.easy_validate_shift_presence(coop_id)
except Exception as e:
res['error'] = str(e)
if 'error' in res:
if res['error'] == "One point has been added less then 24 hours ago":
# TODO : use translation (all project wide)
res['error'] = "Vous ne pouvez pas valider plus d'un service par 24h"
return JsonResponse(res, status=500)
else:
return JsonResponse(res, safe=False)
def record_absences(request): def record_absences(request):
return JsonResponse({'res': CagetteServices.record_absences()}) return JsonResponse({'res': CagetteServices.record_absences()})
......
...@@ -222,6 +222,83 @@ class Order(models.Model): ...@@ -222,6 +222,83 @@ class Order(models.Model):
labels_data['total'] += l['product_qty'] labels_data['total'] += l['product_qty']
return labels_data return labels_data
def get_order_attachment_id(self):
res = {}
f = ["id"]
c = [['res_model', '=', 'purchase.order'], ['res_id', '=', self.id], ['type', 'in', ['binary', 'url']]]
try:
attachment = self.o_api.search_read('ir.attachment', c, f)
res = attachment[0]
except Exception as e:
res["id_po"] = self.id
res["error"] = str(e)
return res
@staticmethod
def create(supplier_id, date_planned, order_lines):
order_data = {
"partner_id": int(supplier_id),
"partner_ref": False,
"currency_id": 1,
"date_order": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"origin": "Aide à la commande",
"company_id": 1,
"order_line": [],
"notes": False,
"date_planned": date_planned,
"picking_type_id": 1,
"dest_address_id": False,
"incoterm_id": False,
"payment_term_id": False,
"fiscal_position_id": False,
"message_follower_ids": False,
"message_ids": False
}
for line in order_lines:
order_data["order_line"].append(
[
0,
False,
{
"package_qty": line["package_qty"],
"price_policy": "uom",
"indicative_package": True,
"product_id": line["product_variant_ids"][0],
"name": line["name"],
"date_planned": date_planned,
"account_analytic_id": False,
"product_qty_package":line["product_qty_package"],
"product_qty": line["product_qty"],
"product_uom": line["product_uom"],
"price_unit": line["price_unit"],
"discount": 0,
"taxes_id": [
[
6,
False,
line["supplier_taxes_id"]
]
]
}
]
)
api = OdooAPI()
id_po = api.create('purchase.order', order_data)
res_confirm = api.execute('purchase.order', 'button_confirm', [id_po])
res = {
'id_po': id_po,
'confirm_po': True,
'supplier_id': supplier_id,
'date_planned': date_planned
}
return res
class Orders(models.Model): class Orders(models.Model):
@staticmethod @staticmethod
...@@ -268,3 +345,15 @@ class Orders(models.Model): ...@@ -268,3 +345,15 @@ class Orders(models.Model):
coop_logger.error('Orders get_custom_barcode_labels_to_print(oids) : %s', str(e)) coop_logger.error('Orders get_custom_barcode_labels_to_print(oids) : %s', str(e))
return labels_data return labels_data
class CagetteSuppliers(models.Model):
@staticmethod
def get_suppliers():
api = OdooAPI()
f = ['id', 'name', 'display_name']
c = [['supplier', '=', 1], ['parent_id', '=', False]]
res = api.search_read('res.partner', c, f)
return res
.page_body{
position: relative;
}
.page_content, .login_area {
position: absolute;
top: 0;
left: 0;
right: 0;
}
/* - Common */
.pill {
border-radius: 30px;
min-width: 200px;
min-height: 40px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 5px 30px 5px 30px;
margin: 0 10px 5px 10px;
}
.disabled {
background-color: #c9cbce;
}
.disabled:hover {
background-color: #a1a2a3;
}
/* - Order selection screen */
#new_order_area {
margin-bottom: 40px;
}
#new_order_form {
margin-top: 20px;
}
#existing_orders {
display: flex;
justify-content: center;
align-items: center;
flex-wrap: wrap;
width: 80%;
margin: 0 auto;
padding-top: 15px;
}
.order_last_update {
font-weight: bold;
}
.order_modified_msg {
font-size: 2rem;
color: #e62720;
}
/* - Main screen */
/* -- Top action button(s) */
#back_to_order_selection {
position: absolute;
}
#main_content .actions_buttons_area {
position: absolute;
width: 100%;
top: 0;
display: flex;
justify-content: space-between;
}
#orders_created .actions_buttons_area {
position: absolute;
width: 100%;
top: 0;
display: flex;
justify-content: flex-start;
}
/* -- Order data */
#order_data_container {
font-size: 1.8rem;
}
#order_data_separator {
margin: 0 10px 0 10px;
}
#order_forms_container {
margin-top: 30px;
display: flex;
justify-content: space-evenly;
}
#supplier_input {
width: 350px;
border-radius: 3px;
}
#date_planned_input, #coverage_days_input {
border-radius: 3px;
}
/* -- Table */
#products_table_filter{
text-align: right !important;
}
#products_table_filter input{
height: 35px;
width: 300px;
border-radius: 3px;
}
#products_table .help {cursor: help;}
#table_header_select_all{
display: flex;
align-content: center;
justify-content: center;
flex-wrap: wrap;
}
.select_all_text {
margin-top: 5px;
margin-bottom: 5px;
}
#table_header_select_all input{
margin-left: 5px;
}
.custom_cell_content {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.product_qty_input {
width: 100px;
}
.supplier_package_qty {
font-style: italic;
font-size: 1.3rem;
}
.product_not_from_supplier {
background-color: #e7e9ed;
cursor: pointer;
}
.product_not_from_supplier:hover {
background-color: #c7cace;
}
.product_name, .supplier_name, .product_npa {
font-weight: bold;
}
.select_product_cb, .product_npa_cb {
cursor: pointer;
}
/* -- Footer */
#main_content_footer {
margin: 20px 0 40px 0;
}
#footer_actions {
width: 100%;
display: flex;
justify-content: space-between;
}
/* -- Suppliers list */
#suppliers_container {
display: flex;
justify-content: center;
align-items: center;
flex-wrap: wrap;
margin: 30px 0 20px 0;
}
.supplier_pill {
background-color: #e7e9edc5;
border: 1px solid black;
}
.pill_supplier_name {
font-weight: bold;
}
.supplier_total_value_container {
font-size: 1.5rem;
}
.remove_supplier_icon {
color: red;
margin-left: 5px;
cursor: pointer;
}
/* -- Attach product to supplier modal */
.modal_input_area {
margin-bottom: 15px;
width: 100%;
display: flex;
justify-content: center;
}
.modal_input_container, .modal_input_label {
width:50%;
margin: 0 15px 0 15px;
}
.modal_input_label {
display: flex;
justify-content: flex-end;
align-items: center;
}
.modal_input {
width: 90%;
}
/* - Orders created screen */
.order_created_header {
margin-top: 5px;
margin-bottom: 40px;
}
#created_orders_area {
display: flex;
flex-wrap: wrap;
justify-content: space-evenly;
}
.new_order_item {
display: flex;
flex-direction: column;
align-items: center;
margin-top: 20px;
}
.download_order_file {
margin-top: 10px;
}
.download_order_file_button:hover {
text-decoration: none;
color: white;
}
.download_order_file_button:active {
text-decoration: none;
color: white;
}
.download_order_file_button:focus {
text-decoration: none;
color: white;
}
#recap_delivery_date {
font-weight: bold;
}
.mail_example_container {
display: flex;
flex-direction: column;
align-items: center;
width: 30%;
margin: 0 auto;
}
.mail_type_text {
width: 100%;
}
.mail_example {
background-color: #e7e9ed;
width: 100%;
padding: 15px;
}
\ No newline at end of file
...@@ -8,5 +8,11 @@ urlpatterns = [ ...@@ -8,5 +8,11 @@ urlpatterns = [
url(r'^export/([0-9]+)', views.export_one), url(r'^export/([0-9]+)', views.export_one),
url(r'^export/([a-z]+)', views.export_regex), url(r'^export/([a-z]+)', views.export_regex),
url(r'^get_pdf_labels$', views.get_pdf_labels), url(r'^get_pdf_labels$', views.get_pdf_labels),
url(r'^print_product_labels$', views.print_product_labels) url(r'^print_product_labels$', views.print_product_labels),
url(r'^helper$', views.helper),
url(r'^get_suppliers$', views.get_suppliers),
url(r'^get_supplier_products$', views.get_supplier_products),
url(r'^associate_supplier_to_product$', views.associate_supplier_to_product),
url(r'^create_orders$', views.create_orders),
url(r'^get_orders_attachment$', views.get_orders_attachment),
] ]
from outils.common_imports import * from outils.common_imports import *
from outils.for_view_imports import * from outils.for_view_imports import *
from orders.models import Order, Orders from orders.models import Order, Orders, CagetteSuppliers
from products.models import CagetteProduct from products.models import CagetteProduct, CagetteProducts
from openpyxl import Workbook from openpyxl import Workbook
from openpyxl.writer.excel import save_virtual_workbook from openpyxl.writer.excel import save_virtual_workbook
import datetime
def as_text(value): return str(value) if value is not None else "" def as_text(value): return str(value) if value is not None else ""
def index(request): def index(request):
return HttpResponse('Orders') return HttpResponse('Orders')
def helper(request):
context = {
'title': 'Aide à la commande',
'couchdb_server': settings.COUCHDB['url'],
'db': settings.COUCHDB['dbs']['orders'],
'odoo_server': settings.ODOO['url']
}
template = loader.get_template('orders/helper.html')
return HttpResponse(template.render(context, request))
def get_suppliers(request):
""" Get suppliers list """
res = {}
try:
res = CagetteSuppliers.get_suppliers()
except Exception as e:
res["error"] = str(e)
return JsonResponse(res, status=500)
return JsonResponse({'res': res})
def get_supplier_products(request):
""" Get supplier products """
suppliers_id = request.GET.getlist('sids', '')
res = CagetteProducts.get_products_for_order_helper(suppliers_id)
if 'error' in res:
return JsonResponse(res, status=500)
else:
return JsonResponse({'res': res})
def associate_supplier_to_product(request):
""" This product is now supplied by this supplier """
res = {}
try:
data = json.loads(request.body.decode())
res = CagetteProduct.associate_supplier_to_product(data)
except Exception as e:
res["error"] = str(e)
return JsonResponse(res, status=500)
return JsonResponse({'res': res})
def create_orders(request):
""" Create products orders """
res = { "created": [] }
try:
data = json.loads(request.body.decode())
# suppliers id are keys in request data
for supplier_id in data["suppliers_data"].keys():
supplier_data = data["suppliers_data"][supplier_id]
res_created = Order.create(
supplier_id,
supplier_data["date_planned"],
supplier_data["lines"]
)
res["created"].append(res_created)
except Exception as e:
res["error"] = str(e)
return JsonResponse(res, status=500)
return JsonResponse({'res': res})
def get_orders_attachment(request):
""" Get order attachment: order file created after PO is finalized """
res = []
po_ids = request.GET.getlist('po_ids')
for id_po in po_ids:
m = Order(int(id_po))
attachment = m.get_order_attachment_id()
if 'error' in attachment:
res.append(attachment)
else:
res.append({
'id_po': id_po,
'id_attachment': attachment["id"]
})
for item in res:
if 'error' in item:
return JsonResponse(res, status=500)
return JsonResponse({'res': res})
def export_one(request, oid): def export_one(request, oid):
msg = '' msg = ''
try: try:
...@@ -20,7 +115,6 @@ def export_one(request, oid): ...@@ -20,7 +115,6 @@ def export_one(request, oid):
order = Order(oid) order = Order(oid)
order_data = order.export() order_data = order.export()
if ('success' in order_data) and (order_data['success'] is True): if ('success' in order_data) and (order_data['success'] is True):
import datetime
now = datetime.datetime.now() now = datetime.datetime.now()
taxes = 0 taxes = 0
company_name = '' company_name = ''
......
...@@ -228,6 +228,22 @@ ...@@ -228,6 +228,22 @@
- ENTRANCE_SERVICE_BTN = "…faire <b>mon service 🤝" - ENTRANCE_SERVICE_BTN = "…faire <b>mon service 🤝"
- ENTRANCE_EXTRA_BUTTONS_DISPLAY = False (no button is shown above shift coop. list) (True if not set)
- ENTRANCE_EASY_SHIFT_VALIDATE = False (default value)
When set to True allow coop to identify and have 1 point (only if FTOP)
- ENTRANCE_ADD_PT_EVENT_NAME = 'Add 1 point name throught easy validate' (default : 'Validation service comité'')
- ENTRANCE_MISSED_SHIFT_BEGIN_MSG (default : "")
This message is dispayed when time to register is last
- ENTRANCE_EASY_SHIFT_VALIDATE_MSG (default = 'Je valide mon service "Comité"')
(makes sens if ENTRANCE_EASY_SHIFT_VALIDATE is True)
### Member space ### Member space
- EM_URL = '' - EM_URL = ''
......
...@@ -99,6 +99,7 @@ STATICFILES_DIRS = ( ...@@ -99,6 +99,7 @@ STATICFILES_DIRS = (
"website/static", "website/static",
"shop/static", "shop/static",
"shelfs/static", "shelfs/static",
"orders/static",
# "tests/static" # "tests/static"
) )
......
...@@ -19,7 +19,9 @@ COUCHDB = { ...@@ -19,7 +19,9 @@ COUCHDB = {
'member': 'coops', 'member': 'coops',
'inventory': 'inventory', 'inventory': 'inventory',
'envelops': 'envelop', 'envelops': 'envelop',
'shop': 'shopping_carts' 'shop': 'shopping_carts',
'orders': 'orders_test',
'reception': 'reception_test',
} }
} }
......
...@@ -59,10 +59,9 @@ function post_form(url, data, callback) { ...@@ -59,10 +59,9 @@ function post_form(url, data, callback) {
$.post(ajax_params) $.post(ajax_params)
.done(function(rData) { .done(function(rData) {
callback(null, rData); callback(null, rData);
}) })
.fail(function(err) { .fail(function(err) {
console.log(err); callback(err, {});
}); });
} }
......
...@@ -6,6 +6,7 @@ from outils.common import OdooAPI ...@@ -6,6 +6,7 @@ from outils.common import OdooAPI
import csv import csv
import tempfile import tempfile
import pymysql.cursors import pymysql.cursors
import datetime
vcats = [] vcats = []
...@@ -126,10 +127,66 @@ class CagetteProduct(models.Model): ...@@ -126,10 +127,66 @@ class CagetteProduct(models.Model):
res = api.create('product.supplier.shortage', f) res = api.create('product.supplier.shortage', f)
return res return res
@staticmethod
def associate_supplier_to_product(data):
api = OdooAPI()
product_tmpl_id = data["product_tmpl_id"]
partner_id = data["supplier_id"]
package_qty = data["package_qty"]
price = data["price"]
f = ["id", "standard_price", "purchase_ok"]
c = [['product_tmpl_id', '=', product_tmpl_id]]
res_products = api.search_read('product.product', c, f)
product = res_products[0]
f = {
'product_tmpl_id' : product_tmpl_id,
'product_id' : product["id"],
'name' : partner_id,
'product_purchase_ok': product["purchase_ok"],
'price': price,
'base_price': price,
'package_qty': package_qty,
'sequence': 1000 # lowest priority for the new suppliers
}
res = api.create('product.supplierinfo', f)
return res
@staticmethod
def update_product_purchase_ok(product_tmpl_id, purchase_ok):
api = OdooAPI()
res = {}
f = {
'purchase_ok': purchase_ok
}
try:
res["update"] = api.update('product.template', product_tmpl_id, f)
except Exception as e:
res["error"] = str(e)
print(str(e))
return res
class CagetteProducts(models.Model): class CagetteProducts(models.Model):
"""Initially used to make massive barcode update.""" """Initially used to make massive barcode update."""
@staticmethod @staticmethod
def get_simple_list():
res = []
api = OdooAPI()
try:
res = api.execute('lacagette.products', 'get_simple_list', {'only_purchase_ok': True})
except Exception as e:
coop_logger.error("Odoo api products simple list : %s", str(e))
return res
@staticmethod
def __unique_product_list(plist): def __unique_product_list(plist):
# 276 # 276
upl = {} upl = {}
...@@ -387,6 +444,99 @@ class CagetteProducts(models.Model): ...@@ -387,6 +444,99 @@ class CagetteProducts(models.Model):
bc_map[bc] = bc bc_map[bc] = bc
return bc_map return bc_map
@staticmethod
def get_template_products_sales_average(params={}):
api = OdooAPI()
res = {}
try:
res = api.execute('lacagette.products', 'get_template_products_sales_average', params)
except Exception as e:
coop_logger.error('get_template_products_sales_average %s (%s)', str(e), str(params))
res["error"] = str(e)
return res
@staticmethod
def get_products_for_order_helper(supplier_ids, pids = []):
"""
One of the two parameters must be not empty.
Get products by supplier if one or more supplier_id is set.
If supplier_ids is empty, get products specified in pids. In this case, suppliers info won't be fetched.
"""
api = OdooAPI()
res = {}
# todo : try with no result
try:
today = datetime.date.today().strftime("%Y-%m-%d")
if supplier_ids is not None and len(supplier_ids) > 0:
# Get products/supplier relation
f = ["product_tmpl_id", 'date_start', 'date_end', 'package_qty', 'price', 'name']
c = [['name', 'in', [ int(x) for x in supplier_ids]]]
psi = api.search_read('product.supplierinfo', c, f)
# Filter valid data
ptids = []
for p in psi:
if (p["product_tmpl_id"] is not False
and (p["date_start"] is False or p["date_end"] is not False and p["date_start"] <= today)
and (p["date_end"] is False or p["date_end"] is not False and p["date_end"] >= today)):
ptids.append(p["product_tmpl_id"][0])
else:
ptids = [ int(x) for x in pids ]
# Get products templates
f = [
"id",
"state",
"name",
"default_code",
"qty_available",
"incoming_qty",
"uom_id",
"purchase_ok",
"supplier_taxes_id",
"product_variant_ids"
]
c = [['id', 'in', ptids], ['purchase_ok', '=', True]]
products_t = api.search_read('product.template', c, f)
filtered_products_t = [p for p in products_t if p["state"] != "end" and p["state"] != "obsolete"]
sales_average_params = {
'ids': ptids,
# 'from': '2019-04-10',
# 'to': '2019-08-10',
}
sales = CagetteProducts.get_template_products_sales_average(sales_average_params)
if 'list' in sales and len(sales['list']) > 0:
sales = sales['list']
else:
sales = []
# Add supplier data to product data
for i, fp in enumerate(filtered_products_t):
if supplier_ids is not None and len(supplier_ids) > 0:
psi_item = next(item for item in psi if item["product_tmpl_id"] is not False and item["product_tmpl_id"][0] == fp["id"])
filtered_products_t[i]['suppliersinfo'] = [{
'supplier_id': int(psi_item["name"][0]),
'package_qty': psi_item["package_qty"],
'price': psi_item["price"]
}]
for s in sales:
if s["id"] == fp["id"]:
filtered_products_t[i]['daily_conso'] = s["average_qty"]
filtered_products_t[i]['sigma'] = s["sigma"]
filtered_products_t[i]['vpc'] = s["vpc"]
res["products"] = filtered_products_t
except Exception as e:
coop_logger.error('get_products_for_order_helper %s (%s)', str(e))
res["error"] = str(e)
return res
class OFF(models.Model): class OFF(models.Model):
"""OpenFoodFact restricted DB queries.""" """OpenFoodFact restricted DB queries."""
......
...@@ -37,6 +37,7 @@ IFCBarcodes = { ...@@ -37,6 +37,7 @@ IFCBarcodes = {
try { try {
let price = parseFloat(value); let price = parseFloat(value);
if (currency == 'FF') if (currency == 'FF')
price = price / 6.55957; price = price / 6.55957;
...@@ -131,6 +132,7 @@ IFCBarcodes = { ...@@ -131,6 +132,7 @@ IFCBarcodes = {
if (product_data !== null) { if (product_data !== null) {
p_uom = (this.uoms)[product_data[this.keys.uom_id]]; p_uom = (this.uoms)[product_data[this.keys.uom_id]];
let qty = 1; let qty = 1;
if (encoded_value.length > 0 && !isNaN(encoded_value)) { if (encoded_value.length > 0 && !isNaN(encoded_value)) {
qty = 0; //if no rule is found it will advise user that there is a problem qty = 0; //if no rule is found it will advise user that there is a problem
/* /*
...@@ -149,7 +151,8 @@ IFCBarcodes = { ...@@ -149,7 +151,8 @@ IFCBarcodes = {
} else { } else {
let list_price = product_data[this.keys.list_price]; let list_price = product_data[this.keys.list_price];
let currency = null; let currency = null;
if (pattern_type == 'FF_price_to_weight') currency = 'FF'
if (pattern_type == 'FF_price_to_weight') currency = 'FF';
qty = parseFloat(this.get_quantity_eq_to_encoded_price(encoded_value, list_price, currency)); qty = parseFloat(this.get_quantity_eq_to_encoded_price(encoded_value, list_price, currency));
} }
......
...@@ -4,9 +4,12 @@ from . import views ...@@ -4,9 +4,12 @@ from . import views
urlpatterns = [ urlpatterns = [
url(r'^$', views.home), url(r'^$', views.home),
url(r'^simple_list$', views.get_simple_list),
url(r'^get_product_for_order_helper$', views.get_product_for_order_helper),
url(r'^get_product_data$', views.get_product_data), url(r'^get_product_data$', views.get_product_data),
url(r'^get_products_stdprices$', views.get_products_stdprices), url(r'^get_products_stdprices$', views.get_products_stdprices),
url(r'^update_product_stock$', views.update_product_stock), url(r'^update_product_stock$', views.update_product_stock),
url(r'^update_product_purchase_ok$', views.update_product_purchase_ok),
url(r'^labels_appli_csv(\/?[a-z]*)$', views.labels_appli_csv, name='labels_appli_csv'), url(r'^labels_appli_csv(\/?[a-z]*)$', views.labels_appli_csv, name='labels_appli_csv'),
url(r'^label_print/([0-9]+)/?([0-9\.]*)/?([a-z]*)/?([0-9]*)$', views.label_print), url(r'^label_print/([0-9]+)/?([0-9\.]*)/?([a-z]*)/?([0-9]*)$', views.label_print),
url(r'^shelf_labels$', views.shelf_labels), # massive print url(r'^shelf_labels$', views.shelf_labels), # massive print
......
...@@ -26,6 +26,32 @@ def home(request): ...@@ -26,6 +26,32 @@ def home(request):
return HttpResponse(template.render(context, request)) return HttpResponse(template.render(context, request))
def get_simple_list(request):
res = {}
try:
res = CagetteProducts.get_simple_list()
except Exception as e:
coop_logger.error("Get products simple list : %s", str(e))
res['error'] = str(e)
if ('error' in res):
return JsonResponse(res, status=500)
else:
return JsonResponse(res, safe=False)
def get_product_for_order_helper(request):
res = {}
try:
pids = json.loads(request.body.decode())
res = CagetteProducts.get_products_for_order_helper(None, pids)
except Exception as e:
coop_logger.error("get_product_for_help_order_line : %s", str(e))
res['error'] = str(e)
if ('error' in res):
return JsonResponse(res, status=500)
else:
return JsonResponse(res, safe=False)
def get_product_data(request): def get_product_data(request):
barcode = request.GET['barcode'] barcode = request.GET['barcode']
res = CagetteProduct.get_product_from_barcode(barcode) res = CagetteProduct.get_product_from_barcode(barcode)
...@@ -75,6 +101,16 @@ def update_product_stock(request): ...@@ -75,6 +101,16 @@ def update_product_stock(request):
return JsonResponse({"res": res}) return JsonResponse({"res": res})
def update_product_purchase_ok(request):
res = {}
data = json.loads(request.body.decode())
res = CagetteProduct.update_product_purchase_ok(data["product_tmpl_id"], data["purchase_ok"])
if ('error' in res):
return JsonResponse(res, status=500)
else:
return JsonResponse({"res": res})
def labels_appli_csv(request, params): def labels_appli_csv(request, params):
"""Generate files to put in DAV directory to be retrieved by scales app.""" """Generate files to put in DAV directory to be retrieved by scales app."""
......
...@@ -29,14 +29,16 @@ class CagetteReception(models.Model): ...@@ -29,14 +29,16 @@ class CagetteReception(models.Model):
if res and len(res) > 0: if res and len(res) > 0:
for r in res: for r in res:
pids.append(int(r['purchase_id'][0])) pids.append(int(r['purchase_id'][0]))
if len(pids): if len(pids):
f=["id","name","date_order", "partner_id", "date_planned", "amount_untaxed", "amount_total", "x_reception_status"] f=["id","name","date_order", "partner_id", "date_planned", "amount_untaxed", "amount_total", "x_reception_status"]
# Only get orders that need to be treated in Reception # Only get orders that need to be treated in Reception
c=[['id', 'in', pids], ["x_reception_status", "in", [False, 'qty_valid']]] c = [['id', 'in', pids], ["x_reception_status", "in", [False, 'qty_valid', 'valid_pending', 'br_valid']]]
orders = api.search_read('purchase.order', c, f) orders = api.search_read('purchase.order', c, f)
except Exception as e: except Exception as e:
print (str(e)) print(str(e))
return orders return orders
def get_order_unprocessable_products(id_po): def get_order_unprocessable_products(id_po):
...@@ -61,7 +63,7 @@ class CagetteReception(models.Model): ...@@ -61,7 +63,7 @@ class CagetteReception(models.Model):
"""Update purchase.order.line with qty data and/or price""" """Update purchase.order.line with qty data and/or price"""
result = None result = None
try: try:
f={} f = {}
if updateType == "qty_valid" or updateType == "br_valid": if updateType == "qty_valid" or updateType == "br_valid":
f['package_qty'] = pakageQty f['package_qty'] = pakageQty
...@@ -74,7 +76,8 @@ class CagetteReception(models.Model): ...@@ -74,7 +76,8 @@ class CagetteReception(models.Model):
res = self.o_api.update('purchase.order.line', idOnLine, f) res = self.o_api.update('purchase.order.line', idOnLine, f)
result = True result = True
except: except Exception as e:
coop_logger.error("update_line : %s (fields values = %s, line_id = %s)",str(e), str(f), str(idOnLine))
result = False result = False
return result return result
......
...@@ -11,6 +11,10 @@ input[type="number"] { ...@@ -11,6 +11,10 @@ input[type="number"] {
} }
/* INDEX */ /* INDEX */
.group_line {
margin-bottom: 5px;
}
#orders tbody tr { #orders tbody tr {
cursor: pointer; cursor: pointer;
} }
...@@ -31,6 +35,15 @@ input[type="number"] { ...@@ -31,6 +35,15 @@ input[type="number"] {
margin-bottom: 1em; margin-bottom: 1em;
} }
.order_last_update {
font-weight: bold;
}
.order_modified_msg {
font-size: 2rem;
color: #e62720;
}
/* PRODUITS */ /* PRODUITS */
.page_body { .page_body {
height: 100%; height: 100%;
......
...@@ -16,6 +16,5 @@ urlpatterns = [ ...@@ -16,6 +16,5 @@ urlpatterns = [
url(r'^reception_qtiesValidated', views.reception_qtiesValidated), url(r'^reception_qtiesValidated', views.reception_qtiesValidated),
url(r'^reception_pricesValidated', views.reception_pricesValidated), url(r'^reception_pricesValidated', views.reception_pricesValidated),
# url(r'^update_order_status/([0-9]+)$', views.tmp_update_order_status), # url(r'^update_order_status/([0-9]+)$', views.tmp_update_order_status),
url(r'^po_process_picking$', views.po_process_picking), url(r'^po_process_picking$', views.po_process_picking)
url(r'^save_order_group$', views.save_order_group)
] ]
...@@ -26,22 +26,19 @@ def as_text(value): ...@@ -26,22 +26,19 @@ def as_text(value):
def home(request): def home(request):
"""Page de selection de la commande suivant un fournisseurs""" """Page de selection de la commande suivant un fournisseurs"""
if 'reception' in settings.COUCHDB['dbs']:
# Get grouped orders stored on the server
try:
with open('temp/grouped_order.json', 'r') as json_file:
saved_groups = json.load(json_file)
except Exception:
saved_groups = []
context = { context = {
'title': 'Reception', 'title': 'Reception',
'merge_orders_pswd': settings.RECEPTION_MERGE_ORDERS_PSWD, 'merge_orders_pswd': getattr(settings, 'RECEPTION_MERGE_ORDERS_PSWD', 'makeastop'),
'server_stored_groups' : saved_groups 'couchdb_server': settings.COUCHDB['url'],
'db': settings.COUCHDB['dbs']['reception'],
'POUCHDB_VERSION': getattr(settings, 'POUCHDB_VERSION', '')
} }
template = loader.get_template('reception/index.html') template = loader.get_template('reception/index.html')
return HttpResponse(template.render(context, request)) return HttpResponse(template.render(context, request))
else:
return HttpResponse("Need to configure reception couchdb db in settings_secret.py")
def get_list_orders(request): def get_list_orders(request):
...@@ -75,8 +72,14 @@ def get_list_orders(request): ...@@ -75,8 +72,14 @@ def get_list_orders(request):
def produits(request, id): def produits(request, id):
""" Gets Order details """ """ Gets Order details """
context = {'title': 'Réception des produits', context = {
"TOOLS_SERVER": settings.TOOLS_SERVER} 'title': 'Réception des produits',
"TOOLS_SERVER": settings.TOOLS_SERVER,
'couchdb_server': settings.COUCHDB['url'],
'db': settings.COUCHDB['dbs']['reception'],
'POUCHDB_VERSION': getattr(settings, 'POUCHDB_VERSION', ''),
"DISPLAY_AUTRES": getattr(settings, 'DISPLAY_COL_AUTRES', True),
}
fixed_barcode_prefix = '0490' fixed_barcode_prefix = '0490'
if hasattr(settings, 'RECEPTION_PB'): if hasattr(settings, 'RECEPTION_PB'):
...@@ -131,40 +134,6 @@ def data_validation(request): ...@@ -131,40 +134,6 @@ def data_validation(request):
coop_logger.error("Orders data validation : %s", str(e)) coop_logger.error("Orders data validation : %s", str(e))
return JsonResponse({'error': str(e)}, status=500) return JsonResponse({'error': str(e)}, status=500)
def save_order_group(request):
"""
When an order group is created, save it to force group these orders later.
Raise an error if one of the orders is already in a group.
"""
order_ids = json.loads(request.body.decode())
try:
try:
# Check if any of the orders attempted to be grouped is already in a group
with open('temp/grouped_order.json', 'r') as json_file:
saved_groups = json.load(json_file)
for order_id in order_ids:
for group in saved_groups:
if order_id in group:
# Found in a group, stop
msg = 'One of the orders is already in a group'
return JsonResponse({'message': msg}, status=409)
except Exception:
saved_groups = []
# All good, save group
with open('temp/grouped_order.json', 'w+') as json_file:
saved_groups.append(order_ids)
json.dump(saved_groups, json_file)
msg = 'Group saved'
return JsonResponse({'message': msg})
except Exception as e:
print(str(e))
return JsonResponse({'message': str(e)}, status=500)
def update_orders(request): def update_orders(request):
"""Update orders lines: quantity and unit prices""" """Update orders lines: quantity and unit prices"""
...@@ -177,6 +146,16 @@ def update_orders(request): ...@@ -177,6 +146,16 @@ def update_orders(request):
if request.is_ajax(): if request.is_ajax():
if request.method == 'PUT': if request.method == 'PUT':
data = json.loads(request.body.decode()) data = json.loads(request.body.decode())
if getattr(settings, 'RECEPTION_DATA_BACKUP', True) is True:
try:
file_name = ''
for order_id, order in data['orders'].items():
file_name += str(order_id) + '_'
file_name += data['update_type'] + '_' + str(round(time.time() * 1000)) + '.json'
with open('data/receptions_backup/' + file_name, 'w') as data_file:
json.dump(data, data_file)
except Exception as ef:
coop_logger.error("Enable to save data : %s (data = %s)",str(ef), str(data))
answer_data = {} answer_data = {}
print_labels = True print_labels = True
if hasattr(settings, 'RECEPTION_SHELF_LABEL_PRINT'): if hasattr(settings, 'RECEPTION_SHELF_LABEL_PRINT'):
...@@ -232,6 +211,8 @@ def update_orders(request): ...@@ -232,6 +211,8 @@ def update_orders(request):
except KeyError: except KeyError:
coop_logger.info("No line to update.") coop_logger.info("No line to update.")
except Exception as e:
coop_logger.error("update_orders : %s", str(e))
answer_data[order_id]['order_data'] = order answer_data[order_id]['order_data'] = order
answer_data[order_id]['errors'] = errors answer_data[order_id]['errors'] = errors
...@@ -243,6 +224,7 @@ def update_orders(request): ...@@ -243,6 +224,7 @@ def update_orders(request):
# Remove order's group # Remove order's group
try: try:
# TODO remove from couchdb orders & group (here?)
if os.path.exists('temp/grouped_order.json'): if os.path.exists('temp/grouped_order.json'):
with open('temp/grouped_order.json', 'r') as json_file: with open('temp/grouped_order.json', 'r') as json_file:
saved_groups = json.load(json_file) saved_groups = json.load(json_file)
...@@ -258,7 +240,9 @@ def update_orders(request): ...@@ -258,7 +240,9 @@ def update_orders(request):
except Exception as e: except Exception as e:
# no saved groups # no saved groups
print(str(e)) print(str(e))
# TODO else if first step, save first step data (here?)
else:
coop_logger.error("update_orders errors : %s", str(errors))
rep = JsonResponse(answer_data, safe=False) rep = JsonResponse(answer_data, safe=False)
return rep return rep
...@@ -266,7 +250,7 @@ def update_orders(request): ...@@ -266,7 +250,7 @@ def update_orders(request):
# """ Method used for tests purposes: Reset an order status """ # """ Method used for tests purposes: Reset an order status """
# m = CagetteReception(id_po) # m = CagetteReception(id_po)
# m.update_order_status(id_po, False) # m.update_order_status(id_po, False)
#
# return JsonResponse({'id_po': id_po}) # return JsonResponse({'id_po': id_po})
def save_error_report(request): def save_error_report(request):
...@@ -285,7 +269,7 @@ def save_error_report(request): ...@@ -285,7 +269,7 @@ def save_error_report(request):
orders_partner = "" orders_partner = ""
group_ids = [] group_ids = []
for i, order in enumerate(data['orders']) : for i, order in enumerate(data['orders']) :
# list of temp files: 1 report per reception # list of temp files: 1 report per order & group
data['orders'][i]['temp_file_name'] = "temp/" + order['name'] + "_rapport-reception_temp.xlsx" data['orders'][i]['temp_file_name'] = "temp/" + order['name'] + "_rapport-reception_temp.xlsx"
group_ids.append(order['id']) group_ids.append(order['id'])
...@@ -376,7 +360,7 @@ def save_error_report(request): ...@@ -376,7 +360,7 @@ def save_error_report(request):
# Create report with data from steps 1 & 2 # Create report with data from steps 1 & 2
else: elif data['update_type'] == 'br_valid':
for order in data['orders']: for order in data['orders']:
# Read step 1 data from temp file # Read step 1 data from temp file
data_qties = {} data_qties = {}
...@@ -408,7 +392,8 @@ def save_error_report(request): ...@@ -408,7 +392,8 @@ def save_error_report(request):
# Clear step 1 temp file # Clear step 1 temp file
os.remove(order['temp_file_name']) os.remove(order['temp_file_name'])
except: except:
data_comment_s1 = "Rapport de la première étape absent !" data_comment_s1 = "Données de la première étape absentes !"
# Add data from step 2 # Add data from step 2
data_full = [] data_full = []
error_total = 0 error_total = 0
...@@ -469,6 +454,7 @@ def save_error_report(request): ...@@ -469,6 +454,7 @@ def save_error_report(request):
# no updated products, do nothing # no updated products, do nothing
print("Error while updating products") print("Error while updating products")
print(exp) print(exp)
# Add remaining products, the ones edited only in step 1 # Add remaining products, the ones edited only in step 1
for product in data_qties.values(): for product in data_qties.values():
item = { item = {
......
...@@ -63,10 +63,12 @@ function display_orders(orders) { ...@@ -63,10 +63,12 @@ function display_orders(orders) {
orderable: false, orderable: false,
render: function (data) { render: function (data) {
let res = '<ul>'; let res = '<ul>';
for (p of data) { for (p of data) {
res += `<li>${p.journal_id[1]} : ${p.amount} €</li>` res += `<li>${p.journal_id[1]} : ${p.amount} €</li>`;
} }
res += "</ul>" res += "</ul>";
return res; return res;
} }
} }
...@@ -97,6 +99,7 @@ function get_sales() { ...@@ -97,6 +99,7 @@ function get_sales() {
openModal(); openModal();
var url = "/sales/get_sales"; var url = "/sales/get_sales";
url += '?from=' + encodeURIComponent(from_datepicker.val()); url += '?from=' + encodeURIComponent(from_datepicker.val());
url += '&to=' + encodeURIComponent(to_datepicker.val()); url += '&to=' + encodeURIComponent(to_datepicker.val());
...@@ -189,7 +192,7 @@ $(document).ready(function() { ...@@ -189,7 +192,7 @@ $(document).ready(function() {
enable_validation(); enable_validation();
}); });
$( "#sales_form" ).submit(function( event ) { $("#sales_form").submit(function(event) {
event.preventDefault(); event.preventDefault();
get_sales(); get_sales();
}); });
......
...@@ -10,10 +10,6 @@ def index(request): ...@@ -10,10 +10,6 @@ def index(request):
context = {'title': 'Export de ventes'} context = {'title': 'Export de ventes'}
template = loader.get_template('sales/index.html') template = loader.get_template('sales/index.html')
# m = CagetteSales()
# sales = m.get_sales()
# print(sales)
return HttpResponse(template.render(context, request)) return HttpResponse(template.render(context, request))
def get_sales(request): def get_sales(request):
......
...@@ -1068,15 +1068,17 @@ var shouldCategoryBeShown = function (cat_id) { ...@@ -1068,15 +1068,17 @@ var shouldCategoryBeShown = function (cat_id) {
} }
if (typeof cat_nb_pdts != "undefined") { if (typeof cat_nb_pdts != "undefined") {
let list = cat_nb_pdts.list; let list = cat_nb_pdts.list;
let cat_ids = Object.keys(list).map(x => parseInt(x,10)); let cat_ids = Object.keys(list).map(x => parseInt(x, 10));
//cat_ids is now an array of category ids which have product //cat_ids is now an array of category ids which have product
if (cat_ids.indexOf(cat_id) < 0) { if (cat_ids.indexOf(cat_id) < 0) {
// cat_id is corresponding to a category which have no product // cat_id is corresponding to a category which have no product
answer = false; answer = false;
} }
} }
return answer; return answer;
} };
var appendChildrenCatToMenu = function (catdiv, children) { var appendChildrenCatToMenu = function (catdiv, children) {
var ul = catdiv.find('ul'); var ul = catdiv.find('ul');
...@@ -1400,28 +1402,29 @@ var showSentCart = function() { ...@@ -1400,28 +1402,29 @@ var showSentCart = function() {
content = $('<div>'), content = $('<div>'),
table = $('<table>'); table = $('<table>');
let header = $('<tr><th>Article</th><th>Qté</th><th>Prix Total (T.T.C)</th></tr>'); let header = $('<tr><th>Article</th><th>Qté</th><th>Prix Total (T.T.C)</th></tr>');
let bottom_msg = $('<p>').html("<em>Contenu non modifiable.</em>") let bottom_msg = $('<p>').html("<em>Contenu non modifiable.</em>");
table.append(header); table.append(header);
$.each(my_sent_orders, function(i,e) { $.each(my_sent_orders, function(i, e) {
if (e._id == id) { if (e._id == id) {
$.each(e.products, function (j,p) { $.each(e.products, function (j, p) {
let tr = $('<tr>'), let tr = $('<tr>'),
name = $('<td>').text(p.name), name = $('<td>').text(p.name),
qty = $('<td>').text(p.qty), qty = $('<td>').text(p.qty),
total = $('<td>').text(p.total) total = $('<td>').text(p.total);
tr.append(name); tr.append(name);
tr.append(qty); tr.append(qty);
tr.append(total); tr.append(total);
table.append(tr); table.append(tr);
}) });
} }
}) });
content.append(table); content.append(table);
content.append(bottom_msg); content.append(bottom_msg);
displayMsg(content.html()); displayMsg(content.html());
} };
var destroySentCart = function() { var destroySentCart = function() {
var clicked = $(this); var clicked = $(this);
......
...@@ -318,6 +318,7 @@ function fetch_product_from_bc(barcode) { ...@@ -318,6 +318,7 @@ function fetch_product_from_bc(barcode) {
if (p_existing !== null) { if (p_existing !== null) {
without_consent_update_product(p_existing, product.qty); without_consent_update_product(p_existing, product.qty);
return 0; return 0;
} else { } else {
add_product(product); add_product(product);
......
...@@ -34,7 +34,7 @@ ...@@ -34,7 +34,7 @@
</div> </div>
<script type="text/javascript"> <script type="text/javascript">
lists = {{lists|safe}} var lists = {{lists|safe}}
</script> </script>
<script src="{% static "js/all_common.js" %}?v="></script> <script src="{% static "js/all_common.js" %}?v="></script>
<script src="{% static "js/common.js" %}?v="></script> <script src="{% static "js/common.js" %}?v="></script>
......
...@@ -137,13 +137,16 @@ ...@@ -137,13 +137,16 @@
<div class="col-1"></div> <div class="col-1"></div>
<div class="col-4"> <div class="col-4">
<h1 class="col-4 txtcenter">Qui es-tu ?</h1> <h1 class="col-4 txtcenter">Qui es-tu ?</h1>
{% if extra_btns_display %}
<section class="outside_list"> <section class="outside_list">
<a class="btn b_orange" data-next="rattrapage_1" data-type="rattrapage">Je viens faire un rattrapage</a> <a class="btn b_orange" data-next="rattrapage_1" data-type="rattrapage">Je viens faire un rattrapage</a>
{% if ftop_btn_display %} {% if ftop_btn_display %}
<a class="btn b_yellow" data-next="rattrapage_1" data-type="volant">Je suis volant</a> <a class="btn b_yellow" data-next="rattrapage_1" data-type="volant">Je suis volant</a>
{% endif %} {% endif %}
</section> </section>
{% endif %}
</div> </div>
<div class="col-1"></div> <div class="col-1"></div>
<div class="col-1"></div> <div class="col-1"></div>
<section id="service_en_cours" class="col-4"> <section id="service_en_cours" class="col-4">
...@@ -153,6 +156,12 @@ ...@@ -153,6 +156,12 @@
<div id="current_shift_members"> <div id="current_shift_members">
</div> </div>
{% if easy_shift_validate %}
<div class="easy_shift_validate">
<p>{{ENTRANCE_EASY_SHIFT_VALIDATE_MSG|safe}}</p>
{% include "members/member_selection.html" %}
</div>
{% endif %}
</section> </section>
</section> </section>
...@@ -227,6 +236,9 @@ ...@@ -227,6 +236,9 @@
<input type="text" placeholder="crop_height" id="img_crop_height" /><br /> <input type="text" placeholder="crop_height" id="img_crop_height" /><br />
<button type="button" id="init_webcam">Cam</button> <button type="button" id="init_webcam">Cam</button>
</section> </section>
<div style="display:none;">
<p id="missed_begin_msg">{{ENTRANCE_MISSED_SHIFT_BEGIN_MSG|safe}}</p>
</div>
<script src="{% static "js/all_common.js" %}?v="></script> <script src="{% static "js/all_common.js" %}?v="></script>
<script src="{% static "js/members.js" %}?v="></script> <script src="{% static "js/members.js" %}?v="></script>
{% endblock %} {% endblock %}
<form id="sm_search_member_form" action="javascript:;" method="post">
<input type="text" id="sm_search_member_input" value="" placeholder="Votre nom ou numéro..." required>
<button type="submit" class="btn--primary" id="sm_search_member_button">Recherche</button>
</form>
<div class="search_member_results_area" style="display:none;">
<div>
<p>Vous êtes (cliquez sur votre nom) :</p>
</div>
<div class="search_member_results"></div>
</div>
\ No newline at end of file
...@@ -11,7 +11,6 @@ ...@@ -11,7 +11,6 @@
<script type="text/javascript" src="{% static 'js/datatables/dataTables.plugins.js' %}"></script> <script type="text/javascript" src="{% static 'js/datatables/dataTables.plugins.js' %}"></script>
<script type="text/javascript" src="{% static 'js/moment.min.js' %}"></script> <script type="text/javascript" src="{% static 'js/moment.min.js' %}"></script>
<script type="text/javascript" src="{% static 'js/datatables/dataTables.plugin.moment_sorting.js' %}"></script> <script type="text/javascript" src="{% static 'js/datatables/dataTables.plugin.moment_sorting.js' %}"></script>
<script type="text/javascript" src="{% static 'js/reception_index.js' %}?v="></script>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
...@@ -31,7 +30,6 @@ ...@@ -31,7 +30,6 @@
<div id="groups_items"></div> <div id="groups_items"></div>
</div> </div>
<br>
<div id="grouped_action"> <div id="grouped_action">
<button type="button" class='btn--primary' id='group_action' onclick="group_action()" hidden>Compter les produits des commandes sélectionnées</button> <button type="button" class='btn--primary' id='group_action' onclick="group_action()" hidden>Compter les produits des commandes sélectionnées</button>
</div> </div>
...@@ -48,13 +46,30 @@ ...@@ -48,13 +46,30 @@
<p>Êtez-vous sûr ?</p> <p>Êtez-vous sûr ?</p>
<hr /> <hr />
</div> </div>
<div id="modal_order_access">
<h3>Attention !</h3>
<br/>
<p class="order_modified_msg">
Un autre navigateur a commencé à réceptionner cette commande il y a <span class="order_last_update"></span>.
</p><br/>
<p>
Si quelqu'un d'autre que vous est à l'origine de cette opération et que celle-ci est récente,
nous conseillons fortement de ne pas accéder à la commande afin d'éviter les conflits.
</p><br/>
<p>Voulez-vous quand même y accéder ?</p>
<hr/>
</div>
</div> </div>
<br/> <br/>
<script src="{% static "js/all_common.js" %}?v="></script> <script src="{% static "js/all_common.js" %}?v="></script>
<script src="{% static 'js/pouchdb.min'|add:POUCHDB_VERSION|add:'.js' %}"></script>
<script type="text/javascript"> <script type="text/javascript">
var merge_orders_pswd = '{{merge_orders_pswd}}'; var merge_orders_pswd = '{{merge_orders_pswd}}';
var server_stored_groups = {{server_stored_groups}}; var couchdb_dbname = '{{db}}';
var couchdb_server = '{{couchdb_server}}' + couchdb_dbname;
</script> </script>
<script src="{% static "js/common.js" %}?v="></script> <script type="text/javascript" src="{% static 'js/reception_index.js' %}?v="></script>
{% endblock %} {% endblock %}
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
{% endif %} {% endif %}
<div class="page_body"> <div class="page_body">
<header class="flex-container"> <header class="flex-container">
<button class="right btn--danger" onclick="back()">Retour</button> <button class="right btn--danger" id="back_button">Retour</button>
<div class="w33 arrow-block txtcenter" id="header_step_one"> <div class="w33 arrow-block txtcenter" id="header_step_one">
<h4 id="header_step_one_content">Produits à compter </h4> <h4 id="header_step_one_content">Produits à compter </h4>
</div> </div>
...@@ -180,9 +180,14 @@ ...@@ -180,9 +180,14 @@
</div> </div>
<br/> <br/>
</div> </div>
<script src="{% static 'js/pouchdb.min'|add:POUCHDB_VERSION|add:'.js' %}"></script>
<script type="text/javascript"> <script type="text/javascript">
var tools_server = '{{TOOLS_SERVER}}' var tools_server = '{{TOOLS_SERVER}}'
var fixed_barcode_prefix = '{{FIXED_BARCODE_PREFIX}}' var fixed_barcode_prefix = '{{FIXED_BARCODE_PREFIX}}'
var couchdb_dbname = '{{db}}';
var couchdb_server = '{{couchdb_server}}' + couchdb_dbname;
var display_autres = "{{DISPLAY_AUTRES}}";
</script> </script>
<script src="{% static "js/all_common.js" %}?v="></script> <script src="{% static "js/all_common.js" %}?v="></script>
<script src='{% static "js/barcodes.js" %}?v='></script> <script src='{% static "js/barcodes.js" %}?v='></script>
......
...@@ -32,8 +32,8 @@ ...@@ -32,8 +32,8 @@
<li>vous avez raté un service et vous ne l'avez pas rattrapé dans les 4 semaines qui ont suivis,</li> <li>vous avez raté un service et vous ne l'avez pas rattrapé dans les 4 semaines qui ont suivis,</li>
<li>vous avez été désinscrit.e pour avoir raté 3 services et venez d'être réinscrit.e.</li> <li>vous avez été désinscrit.e pour avoir raté 3 services et venez d'être réinscrit.e.</li>
</ul> </ul>
<p>Heureusement, vous pouvez demander une extension !</p> <p>Heureusement, vous pouvez demander un délai !</p>
<p>Une extension vous donne un délais de quatre semaines supplémentaires pour rattraper le service manqué (ou les deux services manqués).Vous pouvez demander jusqu'à 6 extensions d'affilé.</p> <p>Cela vous donne quatre semaines supplémentaires pour rattraper le service manqué (ou les deux services manqués).Vous pouvez demander jusqu'à 6 délais d'affilé.</p>
<p>En pratique, ça signifie que vous avez jusqu'à 24 semaines pour rattraper un service raté !</p> <p>En pratique, ça signifie que vous avez jusqu'à 24 semaines pour rattraper un service raté !</p>
<p>Si vous ratez un troisième service, vous serez désinscrit.e, vous n'aurez plus accès au magasin et votre place sera libérée pour un.e autre membre.</p> <p>Si vous ratez un troisième service, vous serez désinscrit.e, vous n'aurez plus accès au magasin et votre place sera libérée pour un.e autre membre.</p>
<br/> <br/>
...@@ -44,12 +44,12 @@ ...@@ -44,12 +44,12 @@
Merci de passer au magasin pour régulariser la situation. Merci de passer au magasin pour régulariser la situation.
</strong> </strong>
{% else %} {% else %}
Vous pouvez demander une extension en cliquant ici : <a class='btn--primary' onclick="request_delay()">Demander une extension</a> Vous pouvez demander un délai en cliquant ici : <a class='btn--primary' onclick="request_delay()">Demander un délai</a>
{% endif %} {% endif %}
</p> </p>
</div> </div>
<div id="no_delay" hidden> <div id="no_delay" hidden>
<p>Vous ne pouvez plus demander d'extensions, merci de passer au magasin pour vous régulariser.</p> <p>Vous ne pouvez plus demander de délai, merci de passer au magasin pour vous régulariser.</p>
</div> </div>
<p> <p>
</div> </div>
......
...@@ -83,16 +83,7 @@ ...@@ -83,16 +83,7 @@
<hr /> <hr />
<section class="set_member_to_movement"> <section class="set_member_to_movement">
<p>Avant de valider l'opération, merci de nous dire qui vous êtes :</p> <p>Avant de valider l'opération, merci de nous dire qui vous êtes :</p>
<form id="sm_search_member_form" action="javascript:;" method="post"> {% include "members/member_selection.html" %}
<input type="text" id="sm_search_member_input" value="" placeholder="Votre nom ou numéro..." required>
<button type="submit" class="btn--primary" id="sm_search_member_button">Recherche</button>
</form>
<div class="search_member_results_area" style="display:none;">
<div>
<p>Vous êtes (cliquez sur votre nom) :</p>
</div>
<div class="search_member_results"></div>
</div>
</section> </section>
<hr /> <hr />
</div> </div>
......
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