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
Pipeline #1257 canceled with stage
......@@ -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>"""
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
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'
......@@ -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. \
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):
file_data = json.load(json_file)
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['datetime_created'] = d
......@@ -113,7 +113,7 @@ class CagetteInventory(models.Model):
return file_data['inventory_status']
@staticmethod
def create_custom_inv_file(line_ids, line_type):
def create_custom_inv_file(line_ids, line_type, default_partners_id=[]):
res = {}
try:
......@@ -127,36 +127,54 @@ class CagetteInventory(models.Model):
api = OdooAPI()
ids = []
order = ['', '']
user = partner = ''
fields = ['create_uid', 'product_id', 'partner_id']
cond = [['id', 'in', line_ids]]
if (line_type == 'cpo'):
model = 'computed.purchase.order.line'
fields += ['computed_purchase_order_id']
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:
model = 'purchase.order.line'
fields += ['order_id']
fields = ['create_uid', 'product_id', 'partner_id']
cond = [['id', 'in', line_ids]]
if (line_type == 'cpo'):
model = 'computed.purchase.order.line'
fields += ['computed_purchase_order_id']
else:
model = 'purchase.order.line'
fields += ['order_id']
lines = api.search_read(model, cond, fields)
if len(lines) == len(line_ids):
for l in lines:
ids.append(l['product_id'][0])
user = l['create_uid'][1]
if (line_type == 'cpo'):
order = l['computed_purchase_order_id']
if line_type == 'product_templates':
ids.append(l['id'])
else:
order = l['order_id']
partner = l['partner_id'][1]
ids.append(l['product_id'][0])
user = l['create_uid'][1]
if (line_type == 'cpo'):
order = l['computed_purchase_order_id']
else:
order = l['order_id']
partners.append(l['partner_id'][1])
if (line_type == 'cpo'):
# partner_id isn't defined
f = ['partner_id']
c = [['id', '=', int(order[0])]]
cpo = api.search_read('computed.purchase.order', c, f)
if len(cpo) > 0:
partner = cpo[0]['partner_id'][1]
partners.append(cpo[0]['partner_id'][1])
file_data = {
'order': order[1],
'user': user,
'partner': partner,
'partners': partners,
'inventory_status': '',
'products': ids
}
......
......@@ -4,6 +4,13 @@ var shelfs_table = null,
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({
data: lists, // data passed at page loading
rowId: 'id',
......@@ -17,8 +24,16 @@ function init_datatable() {
}
},
{
data:"partner",
title:"Fournisseur"
data:"partners",
title:"Fournisseur(s)",
render: function (data) {
res = "";
for (const i in data) {
res += `${data[i]}<br/>`;
}
return res;
}
},
{
data:"order",
......
......@@ -43,7 +43,6 @@ def custom_list_inventory(request, id):
products = CagetteInventory.get_custom_list_products(id)
if 'error' in products:
print(products)
products['data'] = []
context = {'title': 'Inventaire',
......@@ -112,10 +111,25 @@ def do_custom_list_inventory(request):
def generate_inventory_list(request):
"""Responding to Odoo ajax call (no csrf)."""
res = {}
default_partners_id = []
try:
lines = json.loads(request.POST.get('lines'))
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:
res['error'] = str(e)
coop_looger.error("generate_inventory_list : %s", str(e))
......
......@@ -22,7 +22,7 @@ class CagetteMember(models.Model):
"""Class to handle cagette Odoo member."""
m_default_fields = ['name', 'sex', 'image_medium', 'active',
'barcode_base', 'barcode', 'in_ftop_team',
'is_associated_people', 'is_member',
'is_associated_people', 'is_member', 'shift_type',
'display_ftop_points', 'display_std_points',
'is_exempted', 'cooperative_state', 'date_alert_stop']
......@@ -1176,6 +1176,44 @@ class CagetteServices(models.Model):
result['service_found'] = False
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):
@staticmethod
......
......@@ -58,7 +58,8 @@ h1 .member_name {font-weight: bold;}
#member_slide .btn[data-next]
{margin-top: 35px; background: #449d44; color:#FFF;}
[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;}
#rattrapage_1 .advice {margin-top:15px;}
.btn.present {background:#50C878;}
......@@ -67,3 +68,5 @@ h1 .member_name {font-weight: bold;}
.msg-big {font-size: xx-large; background: #fff; padding:25px; text-align: center;}
#member_advice {background: #FFF; color: red;}
.easy_shift_validate {text-align: center; margin-top: 3em;}
......@@ -16,6 +16,7 @@
})(jQuery);
var current_displayed_member = null,
operator = null,
results = null,
loaded_services = null,
selected_service = null,
......@@ -23,6 +24,8 @@ var current_displayed_member = null,
rattrapage_ou_volant = null,
timeout_counter = null;
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 search_field = $('input[name="search_string"]');
var shift_title = $('#current_shift_title');
......@@ -36,6 +39,8 @@ var photo_advice = $('#photo_advice');
var photo_studio = $('#photo_studio');
var coop_info = $('.coop-info');
const missed_begin_msg = $('#missed_begin_msg').html();
let no_pict_msg = $('#no-picture-msg');
var pages = {
......@@ -61,6 +66,7 @@ var html_elts = {
next_shifts : $('#next_shifts')
};
var chars = []; //input chars buffer
function fill_member_slide(member) {
no_pict_msg.hide();
......@@ -160,6 +166,8 @@ function canSearch() {
}
function search_member(force_search = false) {
chars = []; // to prevent false "as barcode-reader" input
operator = null;
if (canSearch() || force_search) {
html_elts.member_slide.hide();
......@@ -277,7 +285,15 @@ function fill_service_entry(s) {
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() {
clean_search_for_easy_validate_zone();
rattrapage_wanted.hide();
shift_title.text('');
shift_members.html('');
......@@ -344,8 +360,8 @@ function get_service_entry_data() {
page_title.text('Qui es-tu ?');
try {
if (rData.res.length == 0) {
info_place.text('La période pendant laquelle il est possible de s\'enregistrer est close.');
page_title.text('');
info_place.html(missed_begin_msg);
page_title.html('');
} else {
if (rData.res.length > 1) {
......@@ -665,9 +681,74 @@ html_elts.image_medium.on('click', function() {
}
});
$(document).ready(function() {
var chars = [];
function ask_for_easy_shift_validation() {
//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"]');
shopping_entry_btn.on('click', function() {
......@@ -708,6 +789,44 @@ $(document).ready(function() {
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() {
......
......@@ -258,21 +258,21 @@ function save_current_coop(callback) {
sex_error = false;
if (/([0-9]{2})\/([0-9]{2})\/([0-9]{4})/.exec(birthdate)) {
try{
try {
var jj = RegExp.$1,
mm = RegExp.$2,
aaaa = RegExp.$3;
mm = RegExp.$2,
aaaa = RegExp.$3;
let tmp_date = aaaa + "-" + mm + "-" + jj;
// try to create a date object
date_test = new Date(tmp_date);
// if date is invalid a correction is apply in date object. Check it
// 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;
}
}catch(Exception){
} catch (Exception) {
birthdate_error = true;
}
......
......@@ -46,6 +46,7 @@ urlpatterns = [
url(r'^remove_data_from_couchdb$', views.remove_data_from_CouchDB),
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'^easy_validate_shift_presence$', views.easy_validate_shift_presence),
# conso / groupe recherche / socio
url(r'^panel_get_purchases$', views.panel_get_purchases),
]
......@@ -24,6 +24,10 @@ def index(request):
'WELCOME_SUBTITLE_ENTRANCE_MSG': getattr(settings, 'WELCOME_SUBTITLE_ENTRANCE_MSG', ''),
'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_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')
}
for_shoping_msg = getattr(settings, 'ENTRANCE_COME_FOR_SHOPING_MSG', '')
......@@ -33,6 +37,8 @@ def index(request):
for_shoping_msg = msettings['msg_accueil']['value']
context['ENTRANCE_COME_FOR_SHOPING_MSG'] = for_shoping_msg
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 len(msettings['no_picture_member_advice']['value']) > 0:
context['no_picture_member_advice'] = msettings['no_picture_member_advice']['value']
......@@ -283,6 +289,22 @@ def record_service_presence(request):
res['error'] = str(e)
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):
return JsonResponse({'res': CagetteServices.record_absences()})
......
......@@ -222,6 +222,83 @@ class Order(models.Model):
labels_data['total'] += l['product_qty']
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):
@staticmethod
......@@ -268,3 +345,15 @@ class Orders(models.Model):
coop_logger.error('Orders get_custom_barcode_labels_to_print(oids) : %s', str(e))
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 = [
url(r'^export/([0-9]+)', views.export_one),
url(r'^export/([a-z]+)', views.export_regex),
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.for_view_imports import *
from orders.models import Order, Orders
from products.models import CagetteProduct
from orders.models import Order, Orders, CagetteSuppliers
from products.models import CagetteProduct, CagetteProducts
from openpyxl import 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 index(request):
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):
msg = ''
try:
......@@ -20,7 +115,6 @@ def export_one(request, oid):
order = Order(oid)
order_data = order.export()
if ('success' in order_data) and (order_data['success'] is True):
import datetime
now = datetime.datetime.now()
taxes = 0
company_name = ''
......
......@@ -228,6 +228,22 @@
- 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
- EM_URL = ''
......
......@@ -99,6 +99,7 @@ STATICFILES_DIRS = (
"website/static",
"shop/static",
"shelfs/static",
"orders/static",
# "tests/static"
)
......
......@@ -19,7 +19,9 @@ COUCHDB = {
'member': 'coops',
'inventory': 'inventory',
'envelops': 'envelop',
'shop': 'shopping_carts'
'shop': 'shopping_carts',
'orders': 'orders_test',
'reception': 'reception_test',
}
}
......
......@@ -59,10 +59,9 @@ function post_form(url, data, callback) {
$.post(ajax_params)
.done(function(rData) {
callback(null, rData);
})
.fail(function(err) {
console.log(err);
callback(err, {});
});
}
......
......@@ -6,6 +6,7 @@ from outils.common import OdooAPI
import csv
import tempfile
import pymysql.cursors
import datetime
vcats = []
......@@ -126,10 +127,66 @@ class CagetteProduct(models.Model):
res = api.create('product.supplier.shortage', f)
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):
"""Initially used to make massive barcode update."""
@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):
# 276
upl = {}
......@@ -387,6 +444,99 @@ class CagetteProducts(models.Model):
bc_map[bc] = bc
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):
"""OpenFoodFact restricted DB queries."""
......
......@@ -37,6 +37,7 @@ IFCBarcodes = {
try {
let price = parseFloat(value);
if (currency == 'FF')
price = price / 6.55957;
......@@ -131,6 +132,7 @@ IFCBarcodes = {
if (product_data !== null) {
p_uom = (this.uoms)[product_data[this.keys.uom_id]];
let qty = 1;
if (encoded_value.length > 0 && !isNaN(encoded_value)) {
qty = 0; //if no rule is found it will advise user that there is a problem
/*
......@@ -149,7 +151,8 @@ IFCBarcodes = {
} else {
let list_price = product_data[this.keys.list_price];
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));
}
......
......@@ -4,9 +4,12 @@ from . import views
urlpatterns = [
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_products_stdprices$', views.get_products_stdprices),
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'^label_print/([0-9]+)/?([0-9\.]*)/?([a-z]*)/?([0-9]*)$', views.label_print),
url(r'^shelf_labels$', views.shelf_labels), # massive print
......
......@@ -26,6 +26,32 @@ def home(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):
barcode = request.GET['barcode']
res = CagetteProduct.get_product_from_barcode(barcode)
......@@ -75,6 +101,16 @@ def update_product_stock(request):
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):
"""Generate files to put in DAV directory to be retrieved by scales app."""
......
......@@ -29,14 +29,16 @@ class CagetteReception(models.Model):
if res and len(res) > 0:
for r in res:
pids.append(int(r['purchase_id'][0]))
if len(pids):
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
c=[['id', 'in', pids], ["x_reception_status", "in", [False, 'qty_valid']]]
orders = api.search_read('purchase.order', c, f)
c = [['id', 'in', pids], ["x_reception_status", "in", [False, 'qty_valid', 'valid_pending', 'br_valid']]]
orders = api.search_read('purchase.order', c, f)
except Exception as e:
print (str(e))
print(str(e))
return orders
def get_order_unprocessable_products(id_po):
......@@ -61,7 +63,7 @@ class CagetteReception(models.Model):
"""Update purchase.order.line with qty data and/or price"""
result = None
try:
f={}
f = {}
if updateType == "qty_valid" or updateType == "br_valid":
f['package_qty'] = pakageQty
......@@ -74,7 +76,8 @@ class CagetteReception(models.Model):
res = self.o_api.update('purchase.order.line', idOnLine, f)
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
return result
......
......@@ -11,6 +11,10 @@ input[type="number"] {
}
/* INDEX */
.group_line {
margin-bottom: 5px;
}
#orders tbody tr {
cursor: pointer;
}
......@@ -31,6 +35,15 @@ input[type="number"] {
margin-bottom: 1em;
}
.order_last_update {
font-weight: bold;
}
.order_modified_msg {
font-size: 2rem;
color: #e62720;
}
/* PRODUITS */
.page_body {
height: 100%;
......
......@@ -16,6 +16,5 @@ urlpatterns = [
url(r'^reception_qtiesValidated', views.reception_qtiesValidated),
url(r'^reception_pricesValidated', views.reception_pricesValidated),
# url(r'^update_order_status/([0-9]+)$', views.tmp_update_order_status),
url(r'^po_process_picking$', views.po_process_picking),
url(r'^save_order_group$', views.save_order_group)
url(r'^po_process_picking$', views.po_process_picking)
]
......@@ -26,22 +26,19 @@ def as_text(value):
def home(request):
"""Page de selection de la commande suivant un fournisseurs"""
if 'reception' in settings.COUCHDB['dbs']:
context = {
'title': 'Reception',
'merge_orders_pswd': getattr(settings, 'RECEPTION_MERGE_ORDERS_PSWD', 'makeastop'),
'couchdb_server': settings.COUCHDB['url'],
'db': settings.COUCHDB['dbs']['reception'],
'POUCHDB_VERSION': getattr(settings, 'POUCHDB_VERSION', '')
}
template = loader.get_template('reception/index.html')
# 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 = {
'title': 'Reception',
'merge_orders_pswd': settings.RECEPTION_MERGE_ORDERS_PSWD,
'server_stored_groups' : saved_groups
}
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):
......@@ -75,8 +72,14 @@ def get_list_orders(request):
def produits(request, id):
""" Gets Order details """
context = {'title': 'Réception des produits',
"TOOLS_SERVER": settings.TOOLS_SERVER}
context = {
'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'
if hasattr(settings, 'RECEPTION_PB'):
......@@ -131,40 +134,6 @@ def data_validation(request):
coop_logger.error("Orders data validation : %s", str(e))
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):
"""Update orders lines: quantity and unit prices"""
......@@ -177,6 +146,16 @@ def update_orders(request):
if request.is_ajax():
if request.method == 'PUT':
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 = {}
print_labels = True
if hasattr(settings, 'RECEPTION_SHELF_LABEL_PRINT'):
......@@ -232,6 +211,8 @@ def update_orders(request):
except KeyError:
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]['errors'] = errors
......@@ -243,6 +224,7 @@ def update_orders(request):
# Remove order's group
try:
# TODO remove from couchdb orders & group (here?)
if os.path.exists('temp/grouped_order.json'):
with open('temp/grouped_order.json', 'r') as json_file:
saved_groups = json.load(json_file)
......@@ -258,7 +240,9 @@ def update_orders(request):
except Exception as e:
# no saved groups
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)
return rep
......@@ -266,7 +250,7 @@ def update_orders(request):
# """ Method used for tests purposes: Reset an order status """
# m = CagetteReception(id_po)
# m.update_order_status(id_po, False)
#
# return JsonResponse({'id_po': id_po})
def save_error_report(request):
......@@ -285,7 +269,7 @@ def save_error_report(request):
orders_partner = ""
group_ids = []
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"
group_ids.append(order['id'])
......@@ -376,7 +360,7 @@ def save_error_report(request):
# Create report with data from steps 1 & 2
else:
elif data['update_type'] == 'br_valid':
for order in data['orders']:
# Read step 1 data from temp file
data_qties = {}
......@@ -408,7 +392,8 @@ def save_error_report(request):
# Clear step 1 temp file
os.remove(order['temp_file_name'])
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
data_full = []
error_total = 0
......@@ -469,6 +454,7 @@ def save_error_report(request):
# no updated products, do nothing
print("Error while updating products")
print(exp)
# Add remaining products, the ones edited only in step 1
for product in data_qties.values():
item = {
......
......@@ -63,10 +63,12 @@ function display_orders(orders) {
orderable: false,
render: function (data) {
let res = '<ul>';
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;
}
}
......@@ -97,6 +99,7 @@ function get_sales() {
openModal();
var url = "/sales/get_sales";
url += '?from=' + encodeURIComponent(from_datepicker.val());
url += '&to=' + encodeURIComponent(to_datepicker.val());
......@@ -189,7 +192,7 @@ $(document).ready(function() {
enable_validation();
});
$( "#sales_form" ).submit(function( event ) {
$("#sales_form").submit(function(event) {
event.preventDefault();
get_sales();
});
......
......@@ -10,10 +10,6 @@ def index(request):
context = {'title': 'Export de ventes'}
template = loader.get_template('sales/index.html')
# m = CagetteSales()
# sales = m.get_sales()
# print(sales)
return HttpResponse(template.render(context, request))
def get_sales(request):
......
......@@ -1067,16 +1067,18 @@ var shouldCategoryBeShown = function (cat_id) {
answer = false;
}
if (typeof cat_nb_pdts != "undefined") {
let list = cat_nb_pdts.list;
let cat_ids = Object.keys(list).map(x => parseInt(x,10));
let list = cat_nb_pdts.list;
let cat_ids = Object.keys(list).map(x => parseInt(x, 10));
//cat_ids is now an array of category ids which have product
if (cat_ids.indexOf(cat_id) < 0) {
// cat_id is corresponding to a category which have no product
answer = false;
}
}
return answer;
}
};
var appendChildrenCatToMenu = function (catdiv, children) {
var ul = catdiv.find('ul');
......@@ -1400,28 +1402,29 @@ var showSentCart = function() {
content = $('<div>'),
table = $('<table>');
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);
$.each(my_sent_orders, function(i,e) {
$.each(my_sent_orders, function(i, e) {
if (e._id == id) {
$.each(e.products, function (j,p) {
$.each(e.products, function (j, p) {
let tr = $('<tr>'),
name = $('<td>').text(p.name),
qty = $('<td>').text(p.qty),
total = $('<td>').text(p.total)
total = $('<td>').text(p.total);
tr.append(name);
tr.append(qty);
tr.append(total);
table.append(tr);
})
});
}
})
});
content.append(table);
content.append(bottom_msg);
displayMsg(content.html());
}
};
var destroySentCart = function() {
var clicked = $(this);
......
......@@ -318,6 +318,7 @@ function fetch_product_from_bc(barcode) {
if (p_existing !== null) {
without_consent_update_product(p_existing, product.qty);
return 0;
} else {
add_product(product);
......
......@@ -34,7 +34,7 @@
</div>
<script type="text/javascript">
lists = {{lists|safe}}
var lists = {{lists|safe}}
</script>
<script src="{% static "js/all_common.js" %}?v="></script>
<script src="{% static "js/common.js" %}?v="></script>
......
......@@ -137,13 +137,16 @@
<div class="col-1"></div>
<div class="col-4">
<h1 class="col-4 txtcenter">Qui es-tu ?</h1>
<section class="outside_list">
<a class="btn b_orange" data-next="rattrapage_1" data-type="rattrapage">Je viens faire un rattrapage</a>
{% if ftop_btn_display %}
<a class="btn b_yellow" data-next="rattrapage_1" data-type="volant">Je suis volant</a>
{% if extra_btns_display %}
<section class="outside_list">
<a class="btn b_orange" data-next="rattrapage_1" data-type="rattrapage">Je viens faire un rattrapage</a>
{% if ftop_btn_display %}
<a class="btn b_yellow" data-next="rattrapage_1" data-type="volant">Je suis volant</a>
{% endif %}
</section>
{% endif %}
</section>
</div>
<div class="col-1"></div>
<div class="col-1"></div>
<section id="service_en_cours" class="col-4">
......@@ -153,6 +156,12 @@
<div id="current_shift_members">
</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>
......@@ -227,6 +236,9 @@
<input type="text" placeholder="crop_height" id="img_crop_height" /><br />
<button type="button" id="init_webcam">Cam</button>
</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/members.js" %}?v="></script>
{% 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 @@
<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/datatables/dataTables.plugin.moment_sorting.js' %}"></script>
<script type="text/javascript" src="{% static 'js/reception_index.js' %}?v="></script>
{% endblock %}
{% block content %}
......@@ -31,7 +30,6 @@
<div id="groups_items"></div>
</div>
<br>
<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>
</div>
......@@ -48,13 +46,30 @@
<p>Êtez-vous sûr ?</p>
<hr />
</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>
<br/>
<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">
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 src="{% static "js/common.js" %}?v="></script>
<script type="text/javascript" src="{% static 'js/reception_index.js' %}?v="></script>
{% endblock %}
......@@ -19,7 +19,7 @@
{% endif %}
<div class="page_body">
<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">
<h4 id="header_step_one_content">Produits à compter </h4>
</div>
......@@ -180,9 +180,14 @@
</div>
<br/>
</div>
<script src="{% static 'js/pouchdb.min'|add:POUCHDB_VERSION|add:'.js' %}"></script>
<script type="text/javascript">
var tools_server = '{{TOOLS_SERVER}}'
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 src="{% static "js/all_common.js" %}?v="></script>
<script src='{% static "js/barcodes.js" %}?v='></script>
......
......@@ -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 été désinscrit.e pour avoir raté 3 services et venez d'être réinscrit.e.</li>
</ul>
<p>Heureusement, vous pouvez demander une extension !</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>Heureusement, vous pouvez demander un délai !</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>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/>
......@@ -44,12 +44,12 @@
Merci de passer au magasin pour régulariser la situation.
</strong>
{% 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 %}
</p>
</div>
<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>
<p>
</div>
......
......@@ -83,16 +83,7 @@
<hr />
<section class="set_member_to_movement">
<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">
<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>
{% include "members/member_selection.html" %}
</section>
<hr />
</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