Commit a267e241 by François C.

Merge branch 'dev_cooperatic' into 'dev_principale'

Intégrations dev Coopératic 2 dernières semaines avril

See merge request cooperatic-foodcoops/third-party!11
parents 47152eb8 4922c3e4
......@@ -46,6 +46,7 @@ def archive_envelop(request):
"done": False,
"error": repr(e)
}
coop_logger.error("Payment error : %s \n %s", str(data), str(e))
if res['done']:
# Immediately save a token than this payment has been saved
......@@ -59,22 +60,19 @@ def archive_envelop(request):
try:
res['partner_name'] = envelop['envelop_content'][partner_id]['partner_name']
res['amount'] = envelop['envelop_content'][partner_id]['amount']
except:
except Exception as e:
res['error'] = "Wrong envelop structure"
coop_logger.error("Wrong envelop structure : %s", str(e))
res_payments.append(res)
try:
# Log the error
lf = open("/tmp/erreurs_django.log", "a")
lf.write(datetime.date.today().strftime("%Y-%m-%d") + " - Erreur lors de l'enregistrement du paiement de " + res['partner_name'] + "(odoo_id:" + partner_id + " )")
lf.write(res['error'] + "\n")
lf.close()
msg = 'Erreur lors de l\'enregistrement du paiement ' + envelop['type']
msg += ' ' + envelop['envelop_content'][partner_id]['amount'] + ' euros '
msg += ' ' + str(envelop['envelop_content'][partner_id]['amount']) + ' euros '
msg += ' (' + res['error'] + ')'
CagetteMember(int(partner_id)).attach_message(msg)
except:
pass
except Exception as e:
coop_logger.error("Cannot attach payment error message to member : %s", str(e))
try:
# Delete envelop from couchdb
......
......@@ -93,7 +93,9 @@ class CagetteMember(models.Model):
fp = request.POST.get('fp') # fingerprint (prevent using stolen cookies)
if login and password:
api = OdooAPI()
cond = [['email', '=', login], ['is_member', '=', True]]
cond = [['email', '=', login]]
if getattr(settings, 'ALLOW_NON_MEMBER_TO_CONNECT', False) is False:
cond.append(['is_member', '=', True])
fields = ['name', 'email', 'birthdate', 'create_date', 'cooperative_state']
res = api.search_read('res.partner', cond, fields)
if (res and len(res) >= 1):
......@@ -317,7 +319,7 @@ class CagetteMember(models.Model):
if 'street2' in post_data:
received_data['street2'] = post_data['street2']
if 'phone' in post_data:
received_data['phone'] = post_data['phone']
received_data['phone'] = format_phone_number(post_data['phone'])
r = c_db.updateDoc(received_data, 'odoo_id')
if r:
if ('odoo_id' in r):
......@@ -394,7 +396,7 @@ class CagetteMember(models.Model):
'street': post_data['address'],
'zip': post_data['zip'],
'city': post_data['city'],
'phone': post_data['mobile'], # Because list view default show Phone and people mainly gives mobile
'phone': format_phone_number(post_data['mobile']), # Because list view default show Phone and people mainly gives mobile
'barcode_rule_id': settings.COOP_BARCODE_RULE_ID
}
if ('_id' in post_data):
......@@ -407,10 +409,10 @@ class CagetteMember(models.Model):
f['street2'] = post_data['street2']
if ('phone' in post_data) and len(post_data['phone']) > 0:
if len(f['phone']) == 0:
f['phone'] = post_data['phone']
f['phone'] = format_phone_number(post_data['phone'])
else:
f['mobile'] = f['phone']
f['phone'] = post_data['phone']
f['phone'] = format_phone_number(post_data['phone'])
# Create coop
if not ('odoo_id' in post_data):
......
......@@ -48,5 +48,4 @@ urlpatterns = [
url(r'^add_pts_to_everybody/([0-9]+)/([a-zA-Z0-9_ ]+)$', admin.add_pts_to_everybody),
# conso / groupe recherche / socio
url(r'^panel_get_purchases$', views.panel_get_purchases),
]
......@@ -102,6 +102,7 @@ def prepa_odoo(request):
'office_place_string': settings.OFFICE_NAME,
'max_begin_hour': settings.MAX_BEGIN_HOUR,
'payment_meanings': settings.SUBSCRIPTION_PAYMENT_MEANINGS,
'input_phone_pattern': getattr(settings, 'INPUT_PHONE_PATTERN', default_input_phone_pattern),
'input_barcode': getattr(settings, 'SUBSCRIPTION_INPUT_BARCODE', False),
'ask_for_sex': getattr(settings, 'SUBSCRIPTION_ASK_FOR_SEX', False),
'ask_for_street2': getattr(settings, 'SUBSCRIPTION_ADD_STREET2', False),
......@@ -135,6 +136,7 @@ def validation_inscription(request, email):
'office_place_string': settings.OFFICE_NAME,
'max_begin_hour': settings.MAX_BEGIN_HOUR,
'payment_meanings': settings.SUBSCRIPTION_PAYMENT_MEANINGS,
'input_phone_pattern': getattr(settings, 'INPUT_PHONE_PATTERN', default_input_phone_pattern),
'ask_for_sex': getattr(settings, 'SUBSCRIPTION_ASK_FOR_SEX', False),
'ask_for_street2': getattr(settings, 'SUBSCRIPTION_ADD_STREET2', False),
'ask_for_second_phone': getattr(settings, 'SUBSCRIPTION_ADD_SECOND_PHONE', False),
......
# -*- coding: utf-8 -*-
"""commons apps functions ."""
from django.conf import settings
default_input_phone_pattern = "^((\+33(-| )\d{1})|\d{2})(\.| )\d{2}(\.| )\d{2}(\.| )\d{2}(\.| )\d{2}$"
def format_phone_number(phone_string):
"""Format phone number for DB insertion (french format)"""
try:
import re
# keep only figures
figures = re.sub(r'[^0-9]', '', phone_string)
international_prefix = ''
if len(figures) > 10:
international_prefix = figures[:len(figures) -9]
figures = figures[-9:]
# for the moment, international prefix is omitted, since only french format is processed
if len(figures) == 9:
figures = '0' + figures
if len(figures) == 10:
number_pairs = [figures[:2]]
for i in range(1,5):
idx = i*2
number_pairs.append(figures[idx:idx + 2])
phone_pairs_separator = getattr(settings, 'PHONE_PAIRS_SEPARATOR', ' ')
output_phone_number = phone_pairs_separator.join(number_pairs)
else:
output_phone_number = phone_string
except:
output_phone_number = phone_string
return output_phone_number
......@@ -79,3 +79,4 @@ LOGGING = {
}
"""
coop_logger = logging.getLogger("coop.framework")
......@@ -106,6 +106,14 @@
Maximum accepted checks numbers
- INPUT_PHONE_PATTERN = "^(0\d{9})$"
Regexp pattern which is used to validate values input in phone fields
Default is "^((\+33(-| )\d{1})|\d{2})(\.| )\d{2}(\.| )\d{2}(\.| )\d{2}(\.| )\d{2}$"
- PHONE_PAIRS_SEPARATOR = "."
Character which by used to separate every 2 phone figures (04.67.23.89.21 for example)
Default is " "
### Scales and labels files generation
- DAV_PATH = '/data/dav/cagette'
......
......@@ -22,7 +22,7 @@ class CagetteMail:
html_message=html_msg)
@staticmethod
def sendCartValidation(email, cart):
def sendCartValidation(email, cart, mode="shop"):
"""Used by Shop"""
from django.core.mail import send_mail
from django.utils.html import strip_tags
......@@ -43,7 +43,10 @@ class CagetteMail:
html_msg = render_to_string(mail_template, ctx)
msg = strip_tags(html_msg)
send_mail("Votre commande en ligne à " + settings.COMPANY_NAME,
subject_prefix = "Votre commande en ligne à "
if mode == "delivery":
subject_prefix = "Votre demande de livraison à "
send_mail(subject_prefix + settings.COMPANY_NAME,
msg,
settings.DEFAULT_FROM_EMAIL,
[email],
......
......@@ -52,6 +52,7 @@ INSTALLED_APPS = (
'orders',
'shop',
'shelfs',
'sales',
# 'tests'
)
......
......@@ -44,6 +44,7 @@ urlpatterns = [
url(r'^website/', include('website.urls')),
url(r'^shop/', include('shop.urls')),
url(r'^shelfs/', include('shelfs.urls')),
url(r'^sales/', include('sales.urls')),
]
try:
......
......@@ -108,6 +108,16 @@ class CagetteProduct(models.Model):
def register_start_supplier_shortage(product_id, partner_id, date_start):
"""Start a supplier shortage for a product"""
api = OdooAPI()
c = [['product_id', '=', product_id],
['partner_id', '=', partner_id],
['date_start', '=', date_start]]
existing = api.search_read('product.supplier.shortage', c)
if existing:
res = "already on shortage"
return res
f = {
'product_id' : product_id,
'partner_id' : partner_id,
......
......@@ -32,6 +32,21 @@ IFCBarcodes = {
display_last_error: function() {
alert(this.errors[this.errors.length - 1]);
},
get_quantity_eq_to_encoded_price: function (value, list_price, currency) {
let qty = 0;
try {
let price = parseFloat(value);
if (currency == 'FF')
price = price / 6.55957;
qty = parseFloat(price / list_price).toFixed(3);
} catch (error) {
console.log(error);
}
return qty;
},
get_corresponding_odoo_product: function(bc) {
//console.log('To analyze :' + bc)
var index = 0,
......@@ -49,31 +64,45 @@ IFCBarcodes = {
if (bc.indexOf(significant_prefix) === 0) {
/*
Submitted barcode-code matches a pattern rule
For example,
bc = 0493213018809
pattern = 0493...{NNDDD}
*/
//console.log(pattern)
// console.log(bc)
odoo_bc = '';
pattern_found = true;
pattern_type = this.patterns[index].type;
pattern = pattern.replace(/[^0-9.ND]/, '');
bc = bc.slice(0, -1); // remove original check figure
// Read pattern character by character
/*
Read pattern character by character
to find out Odoo article barcode
and encoded_value (weight, price, units, if exists)
*/
for (var i = 0; i < pattern.length; i++) {
if (/[0-9]/.exec(pattern[i])) {
// it's a figure, nothing to do but to add it to string
odoo_bc += pattern[i];
} else if (pattern[i].indexOf('.') === 0) {
/*
it's a substitution character,
so add the submitted barcode figure which is in this position
*/
odoo_bc += bc[i];
} else if (/[ND]/.exec(pattern[i])) {
/*
A figure which encoding a value is in this position
(corresponding to a 0 in Odoo article barcode)
*/
odoo_bc += '0';
/* let's add a decimal sepator if D is read for the first time */
if (pattern[i] === 'D' && encoded_value.indexOf('.') < 0)
encoded_value += '.';
encoded_value += bc[i];
}
}
// Add check digit at the end of odoo_bc to find out "normalized" code
bc = odoo_bc + eanCheckDigit(odoo_bc);
}
......@@ -101,16 +130,35 @@ 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
/*
Warning :
Tests are dependant on La Cagette / Cooperatic uom system and barcode rules
TODO : Defines them outside of this part of code
*/
if (p_uom == 'Unit(s)' || p_uom == 'unité') {
encoded_value = parseInt(encoded_value, 10);
qty = encoded_value;
} else {
encoded_value = parseFloat(encoded_value);
if (pattern_type == 'weight' || pattern_type == 'FF_price_to_weight' || pattern_type == 'price_to_weight') {
if (pattern_type == 'weight') {
qty = encoded_value;
} else {
let list_price = product_data[this.keys.list_price];
let currency = null;
if (pattern_type == 'FF_price_to_weight') currency = 'FF'
qty = parseFloat(this.get_quantity_eq_to_encoded_price(encoded_value, list_price, currency));
}
}
}
}
odoo_product = {barcode: bc, data: product_data, rule: pattern_type, value: encoded_value};
odoo_product = {barcode: bc, data: product_data, rule: pattern_type, value: encoded_value, qty: qty};
}
return odoo_product;
......
......@@ -209,7 +209,7 @@ function validatePrices() {
traditional: true,
contentType: "application/json; charset=utf-8",
data: JSON.stringify(update_data),
success: function(data) {
success: function() {
localStorage.removeItem("order_" + order["id"]);
callback_update = true;
reload();
......@@ -238,7 +238,7 @@ function validatePrices() {
traditional: true,
contentType: "application/json; charset=utf-8",
data: JSON.stringify(updates),
success: function(data) {
success: function() {
callback_report = true;
reload();
},
......@@ -275,7 +275,7 @@ function group_action() {
traditional: true,
contentType: "application/json; charset=utf-8",
data: JSON.stringify(group_ids),
success: function(data) {
success: function() {
var min_id = 9999999;
for (var i = 0; i < selected_data.length; i++) {
......@@ -303,7 +303,7 @@ function group_action() {
localStorage.setItem('grouped_orders', JSON.stringify(grouped_orders));
// Go to products page of order with smallest id
// goto(min_id);
goto(min_id);
},
error: function(data) {
if (data != null && data.status == 409) {
......@@ -334,8 +334,6 @@ $(document).ready(function() {
// Set date format for DataTable so date ordering can work
$.fn.dataTable.moment('D/M/Y');
var saved_grouped_orders = JSON.parse(localStorage.getItem('grouped_orders'));
table_orders = $('#orders').DataTable({
ajax: "get_list_orders",
columns:[
......@@ -343,7 +341,7 @@ $(document).ready(function() {
data:"id",
title:"Sélectionner",
className:"dt-body-center",
render: function (data, type, full, meta) {
render: function (data) {
return '<input type="checkbox" id="select_bc_'+data+'" value="'+data+'">';
},
width: "4%",
......@@ -353,7 +351,7 @@ $(document).ready(function() {
{
data:"partner",
title:"Fournisseur",
render: function (data, type, full, meta) {
render: function (data, type, full) {
// Add tooltip with PO over partner name
return '<div class="tooltip">' + data + ' <span class="tooltiptext">' + full.name + '</span> </div>';
}
......@@ -361,7 +359,7 @@ $(document).ready(function() {
{
data:"reception_status",
className:"dt-body-center",
render: function (data, type, full, meta) {
render: function (data) {
if (data == "qty_valid") {
return "<span class='btn--danger'>Pas de prix sur le bon de livraison</span>";
} else {
......@@ -375,7 +373,7 @@ $(document).ready(function() {
data:"reception_status",
title:"Statut",
className:"dt-body-center",
render: function (data, type, full, meta) {
render: function (data) {
switch (data) {
case 'qty_valid':
return "<span class='btn--success'>Mettre à jour les prix</span>";
......@@ -397,7 +395,7 @@ $(document).ready(function() {
],
iDisplayLength: 25,
language: {url : '/static/js/datatables/french.json'},
initComplete: function(settings, json) { // After data is loaded
initComplete: function() { // After data is loaded
clean_local_storage();
create_groups_from_server_data();
extract_grouped_orders();
......@@ -460,9 +458,7 @@ $(document).ready(function() {
selection_type = null;
document.getElementById("group_action").hidden = true;
}
}
// Click on last cell button -> go to products page
else if (this.cellIndex == 4) {
} else if (this.cellIndex == 4) { // Click on last cell button -> go to products page
// Extra security if order with a different status gets lost in here
if (row_data.reception_status == "qty_valid" || row_data.reception_status == "False") {
// Use local storage to pass order data to next page
......@@ -470,9 +466,8 @@ $(document).ready(function() {
goto(row_data.id);
}
}
// If 'update prices' step, click on before-last cell -> validate all prices
else if (this.cellIndex == 3 && row_data.reception_status == "qty_valid") {
} else if (this.cellIndex == 3 && row_data.reception_status == "qty_valid") {
// If 'update prices' step, click on before-last cell -> validate all prices
order = row_data;
openModal($('#modal_no_prices').html(), validatePrices, 'Confirmer', false);
}
......
......@@ -18,13 +18,13 @@ var orders = {},
is_group = false,
group_ids = [];
var reception_status,
var reception_status = null,
list_to_process = [],
list_processed = [],
table_to_process,
table_processed,
table_to_process = null,
table_processed = null,
editing_product = null, // Store the product currently being edited
editing_origin, // Keep track of where editing_product comes from
editing_origin = null, // Keep track of where editing_product comes from
processed_row_counter = 0, // Order in which products were added in processed list
user_comments = "",
updatedProducts = [], // Keep record of updated products
......@@ -108,6 +108,8 @@ function select_product_from_bc(barcode) {
console.error(err);
report_JS_error(err, 'reception');
}
return 0;
}
/* INIT */
......@@ -231,7 +233,7 @@ function initLists() {
data:"product_id.1",
title:"Produit",
width: "45%",
render: function (data, type, full, meta) {
render: function (data, type, full) {
// Add tooltip with barcode over product name
let display_barcode = "Aucun";
......@@ -300,7 +302,7 @@ function initLists() {
data:"product_id.1",
title:"Produit",
width: "55%",
render: function (data, type, full, meta) {
render: function (data, type, full) {
// Add tooltip with barcode over product name
let display_barcode = "Aucun";
......@@ -344,7 +346,7 @@ function initLists() {
title:"Autres",
className:"dt-body-center",
orderable: false,
render: function (data, type, full, meta) {
render: function (data, type, full) {
let disabled = (full.supplier_shortage) ? "disabled" : '';
return "<select class='select_product_action'>"
......@@ -889,7 +891,7 @@ function editProductInfo (productToEdit, value = null) {
}
// Validate product edition
function validateEdition(form) {
function validateEdition(form = null) {
if (editing_product != null) {
if (editProductInfo(editing_product)) {
clearLineEdition();
......@@ -900,10 +902,12 @@ function validateEdition(form) {
// Set the quantity to 0 for all the products in to_process
function setAllQties() {
// Iterate over all rows in to_process
table_to_process.rows().every(function (rowIdx, tableLoop, rowLoop) {
table_to_process.rows().every(function () {
var data = this.data();
editProductInfo(data, 0);
return true;
});
list_to_process = [];
table_to_process.rows().remove()
......@@ -1128,7 +1132,9 @@ function send() {
traditional: true,
contentType: "application/json; charset=utf-8",
data: JSON.stringify(update_data),
success: function(data) {
success: function() {
closeModal();
try {
// If step 1 (counting), open pop-up with procedure explanation
if (reception_status == "False") {
......@@ -1213,11 +1219,11 @@ function send() {
}
// Go back to to_process list if modal closed
$('#modal_closebtn_top').on('click', function (e) {
$('#modal_closebtn_top').on('click', function () {
document.location.href = "/reception";
});
$('#modal_closebtn_bottom').on('click', function (e) {
$('#modal_closebtn_bottom').on('click', function () {
document.location.href = "/reception";
});
......@@ -1303,7 +1309,7 @@ function confirmPricesAllValid() {
function confirm_all_left_is_good() {
// all products left are to be considered as well filled
// Iterate over all rows in to_process
table_to_process.rows().every(function (rowIdx, tableLoop, rowLoop) {
table_to_process.rows().every(function () {
let data = this.data();
var value = null;
......@@ -1313,6 +1319,8 @@ function confirm_all_left_is_good() {
value = data.price_unit;
}
editProductInfo(data, value);
return true;
});
list_to_process = [];
table_to_process.rows().remove()
......@@ -1560,12 +1568,12 @@ $(document).ready(function() {
container_edition.addEventListener('animationend', onAnimationEnd);
container_edition.addEventListener('webkitAnimationEnd', onAnimationEnd);
function onAnimationEnd(e) {
function onAnimationEnd() {
container_edition.classList.remove('blink_me');
}
// Disable mousewheel on an input number field when in focus
$('#edition_input').on('focus', function (e) {
$('#edition_input').on('focus', function () {
$(this).on('wheel.disableScroll', function (e) {
e.preventDefault();
/*
......@@ -1580,7 +1588,7 @@ $(document).ready(function() {
*/
});
})
.on('blur', function (e) {
.on('blur', function () {
$(this).off('wheel.disableScroll');
});
......@@ -1635,6 +1643,13 @@ $(document).ready(function() {
}
});
$("#edition_input").keypress(function(event) {
// Force validation when enter pressed in edition
if (event.keyCode == 13 || event.which == 13) {
validateEdition();
}
});
// Barcode reader
$(document).pos();
$(document).on('scan.pos.barcode', function(event) {
......
from django.contrib import admin
# Register your models here.
from django.apps import AppConfig
class SalesConfig(AppConfig):
name = 'sales'
from django.db import models
from outils.common_imports import *
from outils.common import OdooAPI
class CagetteSales(models.Model):
"""Class to manage operations on envelops"""
def __init__(self):
"""Init with odoo id."""
self.o_api = OdooAPI()
def get_sales(self, date_from, date_to):
res = []
# Get pos sessions
cond = [['stop_at', '>=', date_from], ['stop_at', '<=', date_to], ['state', '=', "closed"]]
fields = []
sessions = self.o_api.search_read('pos.session', cond, fields)
# Get bank statements of these sessions
statements = []
for s in sessions:
statements = statements + s["statement_ids"]
# Get payment lines
cond = [['statement_id', 'in', statements]]
fields = ["partner_id", "amount", "journal_id", "create_date", "date"]
payments = self.o_api.search_read('account.bank.statement.line', cond, fields, order="create_date ASC")
item = None
try:
for payment in payments:
if item is not None and item["partner_id"][0] == payment["partner_id"][0] and item["date"] == payment["date"]:
res[len(res)-1]["total_amount"] += round(float(payment["amount"]), 2)
res[len(res)-1]["payments"].append({
"amount": round(float(payment["amount"]), 2),
"journal_id": payment["journal_id"]
})
else:
item = {
"partner_id": payment["partner_id"],
"create_date": payment["create_date"],
"date": payment["date"],
"total_amount": round(float(payment["amount"]), 2),
"payments": [
{
"amount": round(float(payment["amount"]), 2),
"journal_id": payment["journal_id"]
}
]
}
res.append(item)
except Exception as e:
pass
return res
.select_sales_dates {
margin-top: 25px;
}
.sales_date {
display: inline-block;
margin-right: 10px;
}
.main {
margin-top: 25px;
}
.table_area {
margin: auto;
width: 90%;
padding: 10px;
}
.select_sales_date_input {
border-radius:5px;
}
\ No newline at end of file
var dateFormat = "yy-mm-dd",
from_datepicker = null,
to_datepicker = null,
orders_table = null;
// Return a date from a string if valid, else return null
function getDate(element) {
var date = null;
try {
date = $.datepicker.parseDate(dateFormat, element);
} catch (error) {
date = null;
}
return date;
}
// Enable validation button if all fields are valid
function enable_validation() {
if (getDate(from_datepicker.val()) &&
getDate(to_datepicker.val())) {
$('#dates_selection_button').prop('disabled', false);
} else {
$('#dates_selection_button').prop('disabled', true);
}
}
function display_orders(orders) {
// Empty datatable if already exists
if (orders_table) {
orders_table.destroy();
}
console.log(orders);
orders_table = $('#orders_table').DataTable({
data: orders,
columns:[
{
data:"create_date",
title:"Date",
width: "10%"
},
{
data:"partner_id",
title:"Membre",
width: "50%",
render: function (data) {
return data[1];
}
},
{
data:"total_amount",
title: "Montant du panier",
className:"dt-body-center",
render: function (data) {
return parseFloat(data).toFixed(2) + ' €';
}
},
{
data:"payments",
title:"Paiements",
className:"dt-body-center",
orderable: false,
render: function (data) {
let res = '<ul>';
for (p of data) {
res += `<li>${p.journal_id[1]} : ${p.amount} €</li>`
}
res += "</ul>"
return res;
}
}
],
order: [
[
0,
"asc"
]
],
dom: 'rtip',
iDisplayLength: 25,
language: {url : '/static/js/datatables/french.json'}
});
$('.main').show();
}
function get_sales() {
openModal();
var url = "/sales/get_sales";
url += '?from=' + encodeURIComponent(from_datepicker.val());
url += '&to=' + encodeURIComponent(to_datepicker.val());
$.ajax({
type: 'GET',
url: url,
dataType:"json",
traditional: true,
contentType: "application/json; charset=utf-8",
success: function(data) {
display_orders(data.res);
closeModal();
},
error: function(data) {
err = {msg: "erreur serveur lors de la sélection des mouvements de stock", ctx: 'get_movements'};
if (typeof data.responseJSON != 'undefined' && typeof data.responseJSON.error != 'undefined') {
err.msg += ' : ' + data.responseJSON.error;
}
report_JS_error(err, 'stock');
closeModal();
alert('Erreur lors de la récupération, réessayez plus tard');
}
});
}
$(document).ready(function() {
$.ajaxSetup({ headers: { "X-CSRFToken": getCookie('csrftoken') } });
// Set datepicker
$.datepicker.regional['fr'] = {
monthNames: [
'Janvier',
'Fevrier',
'Mars',
'Avril',
'Mai',
'Juin',
'Juillet',
'Aout',
'Septembre',
'Octobre',
'Novembre',
'Decembre'
],
dayNamesMin: [
'Di',
'Lu',
'Ma',
'Me',
'Je',
'Ve',
'Sa'
],
dateFormat: dateFormat,
firstDay: 1,
minDate: 1,
maxDate: '+12M +0D'
};
$.datepicker.setDefaults($.datepicker.regional['fr']);
from_datepicker = $("#from")
.datepicker({
defaultDate: "-1d",
changeMonth: true,
changeYear: true,
yearRange: "-03:+00",
minDate: new Date(2007, 1 - 1, 1),
maxDate: new Date()
})
.on("change", function() {
to_datepicker.datepicker("option", "minDate", getDate(this.value));
});
to_datepicker = $("#to")
.datepicker({
defaultDate: "-1d",
changeMonth: true,
changeYear: true,
yearRange: "-03:+00",
minDate: new Date(2007, 1 - 1, 1),
maxDate: new Date()
})
.on("change", function() {
from_datepicker.datepicker("option", "maxDate", getDate(this.value));
});
$('.select_sales_date_input').change(function() {
enable_validation();
});
$( "#sales_form" ).submit(function( event ) {
event.preventDefault();
get_sales();
});
// $('#dates_selection_button').click(function() {
// get_sales();
// });
});
"""."""
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^$', views.index),
url(r'^get_sales$', views.get_sales),
]
from outils.common_imports import *
from outils.for_view_imports import *
from sales.models import CagetteSales
def index(request):
"""Display sales export screen"""
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):
res = {}
date_from = request.GET.get('from', '')
date_to = request.GET.get('to', '')
m = CagetteSales()
res = m.get_sales(date_from, date_to)
if 'errors' in res and res['errors']:
return JsonResponse(res, status=500)
else:
return JsonResponse({'res': res})
\ No newline at end of file
......@@ -47,7 +47,11 @@ def get_all_children(branch):
children += get_all_children(c)
return children
def get_all_children_ids(branch):
ids = []
for c in get_all_children(branch):
ids.append(c['id'])
return ids
class CagetteShop(models.Model):
"""Class to handle cagette Shop."""
......@@ -62,7 +66,7 @@ class CagetteShop(models.Model):
def filter_products_according_settings(pdts):
res = pdts
try:
conditions = settings.SHOP_LIMIT_PRODUCTS
conditions = getattr(settings, 'SHOP_LIMIT_PRODUCTS', [])
filtered = []
for p in pdts:
keep_it = True
......@@ -124,13 +128,14 @@ class CagetteShop(models.Model):
fields = ['parent_id', 'name']
res = api.search_read('product.category', [], fields)
tree = build_tree_from_categories(res)
except:
pass
except Exception as e:
coop_logger.error('get_product_categories : %s', str(e))
return tree
@staticmethod
def get_cat_children_ids(categ_id):
cat_ids = [categ_id]
tree = CagetteShop.get_product_categories()
branch = None
for cats in tree:
......@@ -159,15 +164,23 @@ class CagetteShop(models.Model):
return children
@staticmethod
def get_categories_nb_of_products():
"""Needs lacagette_categories Odoo module to be activated"""
res = {}
try:
api = OdooAPI()
res = api.execute('lacagette.categories', 'get_all_with_products_count', {})
except Exception as e:
coop_logger.error('get_categories_nb_of_products %s', str(e))
res['error'] = str(e)
return res
@staticmethod
def get_category_products(categ_id):
res = {}
try:
pdts = []
limit_conditions = []
try:
limit_conditions = settings.SHOP_LIMIT_PRODUCTS
except:
pass
limit_conditions = getattr(settings, 'SHOP_LIMIT_PRODUCTS', [])
api = OdooAPI()
cat_ids = CagetteShop.get_cat_children_ids(categ_id)
# removed ['qty_available', '>', 0]
......@@ -183,6 +196,7 @@ class CagetteShop(models.Model):
res['pdts'] = CagetteShop.filter_products_according_settings(pdts)
except Exception as e:
coop_logger.error('get_category_products %s %s', categ_id, str(e))
res['error'] = str(e)
return res
......@@ -239,7 +253,7 @@ class CagetteShop(models.Model):
@staticmethod
def registrerCart(cart, partner_id):
def registrerCart(cart, partner_id, mode="shop"):
result = {}
try:
cart['submitted_time'] = time.time()
......@@ -260,7 +274,7 @@ class CagetteShop(models.Model):
if result:
try:
from outils.mail import CagetteMail
CagetteMail.sendCartValidation(partner['email'], cart)
CagetteMail.sendCartValidation(partner['email'], cart, mode)
except Exception as e:
coop_logger.error("Shop, registrerCart : %s, %s", str(e), str(cart))
except Exception as e:
......
......@@ -252,7 +252,10 @@ li.tab { border-right: 1px solid white; }
#my-orders-sumup .date {text-align: left;padding: 0 4px;}
#my-orders-sumup tbody tr:hover {background-color: #b3b7c4;}
td.actions .fa-trash, td.actions .fa-paper-plane, td.date .fa-edit {cursor: pointer;}
td.actions .fa-trash,
td.actions .fa-eye,
td.actions .fa-paper-plane,
td.date .fa-edit {cursor: pointer;}
.no-action-available-msg {margin-top: 15px;}
#survey_link {text-decoration: none;}
......
......@@ -26,6 +26,7 @@ var main_content = $('#main-content'),
dragSrcEl = null,
forbidden_slots = [],
closing_dates = [],
my_sent_orders = [],
right_column = $('#right-column'),
visit_mode = false,
timer = null;
......@@ -717,7 +718,7 @@ var addProductToCart = function() {
var msg = "";
var too_much = "Vous avez pris plus de produit que le stock indicatif.\nVous n'aurez peut-être pas toute la quantité.";
if (parseFloat(qty) > available_qty) {
if (parseFloat(qty) > available_qty && stock_warning == true) {
msg = too_much;
}
var u = p_div.find('.unit').text()
......@@ -757,7 +758,7 @@ var addProductToCart = function() {
}
}
if (typeof answer.warning !== "undefined") {
if (answer.warning == "max_qty")
if (answer.warning == "max_qty" && stock_warning == true)
msg = too_much;
}
});
......@@ -1059,11 +1060,28 @@ var loadAllAvailableBoughtProducts = function() {
}
};
var shouldCategoryBeShown = function (cat_id) {
let answer = true;
if (excluded_cat.indexOf(cat_id) > -1) {
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));
//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');
$.each(children, function(i, e) {
if (excluded_cat.indexOf(e.id) < 0) {
if (shouldCategoryBeShown(e.id)) {
var li = $('<li>').addClass("nav-item");
// Remove TVA in cat name
......@@ -1092,7 +1110,6 @@ var getCategChildren = function() {
if (typeof category_elts[cat_id] == "undefined") {
try {
$.ajax({
//url :'/shop/get_categ_products',
url : '/shop/get_cat_children',
data: {id: cat_id},
dataType: 'json'
......@@ -1236,12 +1253,13 @@ var displaySentOrders = function() {
}
} else if (typeof rData.res.data.orders != "undefined") {
if (rData.res.data.orders.length > 0) {
my_sent_orders = rData.res.data.orders;
var eye = '<i class="fas fa-eye fl"></i>';
var delete_icon = '<i class="fas fa-trash fr"></i>';
var edit = '<i class="fas fa-edit"></i>';
var show_no_action_available_msg = false;
$.each(rData.res.data.orders, function(i, o) {
$.each(my_sent_orders, function(i, o) {
var bdate_content = "<span>" + o.best_date + "</span>";
if (o.state == "init" || o.state == "validating") bdate_content += " " + edit;
......@@ -1267,7 +1285,8 @@ var displaySentOrders = function() {
.text(o.products.length);
var td4 = $('<td>').addClass('amount')
.text(parseFloat(o.total).toFixed(2));
//var td5 = $('<td>').addClass('actions').html(eye + ' ' + delete_icon)
actions_content = eye + ' ' + actions_content;
var td5 = $('<td>').addClass('actions')
.html(actions_content);
......@@ -1374,6 +1393,36 @@ var changeBestDate = function() {
};
var showSentCart = function() {
let clicked = $(this),
clicked_tr = clicked.closest('tr'),
id = clicked_tr.data('id'),
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>")
table.append(header);
$.each(my_sent_orders, function(i,e) {
if (e._id == id) {
$.each(e.products, function (j,p) {
let tr = $('<tr>'),
name = $('<td>').text(p.name),
qty = $('<td>').text(p.qty),
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);
var clicked_tr = clicked.closest('tr'),
......@@ -1561,15 +1610,19 @@ valid_cart.click(function() {
$('#get_my_bought_products').click(loadAllAvailableBoughtProducts);
$(document).on('change', '[name^="bday"]', filterHourOptions);
$(document).on('change', '[name="bhour"]', adaptTimeGivenForValidationMsg);
$(document).on('click', '#alim_categ > div, #non_alim_categ > div', getCategChildren);
$(document).on('click', '#alim_categ ul li span, #non_alim_categ ul li span', getCategProducts);
$(document).on('click', '.product button', addProductToCart);
$(document).on('click', '.forbidden-slots .fs-close', closeForbiddenList);
$(document).on('click', 'td.date .fa-edit', changeBestDate);
$(document).on('click', 'td.actions .fa-eye', showSentCart);
$(document).on('click', 'td.actions .fa-trash', destroySentCart);
if (shop_mode == 'shop')
$(document).on('change', '[name^="bday"]', filterHourOptions);
$(document).on(
'click', '.new-order',
function() {
......
......@@ -8,49 +8,28 @@ from shop.models import CagetteShop
@never_cache
def shop_index(request):
return index(request, mode='shop')
@never_cache
def delivery_index(request):
return index(request, mode='delivery')
def index(request, mode="shop"):
template = loader.get_template('shop/index.html')
credentials = CagetteMember.get_credentials(request)
shop_settings = CagetteShop.get_shop_settings()
def _get_index_context(credentials, shop_settings, mode):
context = {'title': 'Commande / Réservation',
'mode': mode,
'COMPANY_NAME': settings.COMPANY_NAME,
'SHOP_CATEGORIES': settings.SHOP_CATEGORIES,
'EXCLUDE_SHOP_CATEGORIES': settings.EXCLUDE_SHOP_CATEGORIES,
'MIN_DELAY_FOR_SLOT': settings.MIN_DELAY_FOR_SLOT,
'HOURS_FOR_VALIDATION': settings.HOURS_FOR_VALIDATION_SHOP}
'header_img': getattr(settings, 'SHOP_HEADER_IMG', '/static/img/header.jpg')
}
if 'capital_message' in shop_settings:
context['capital_message'] = shop_settings['capital_message']
allowed_states = ["up_to_date", "alert", "delay"]
# Uncomment if 'coop_state' in credentials .... etc
# to prevent other states people to use the shop
allowed = True
if ('failure' in credentials):
# Visitor has not been identified
template = loader.get_template('website/connect.html')
context['msg'] = ''
if 'msg' in credentials:
context['msg'] = credentials['msg']
context['password_placeholder'] = 'Mot de passe'
context['password_notice'] = "Par défaut, la date de naissance (jjmmaaaa)"
context['with_shop_header'] = True
try:
context['header_img'] = settings.SHOP_HEADER_IMG
except:
context['header_img'] = '/static/img/header.jpg'
else:
if hasattr(settings, 'SHOP_OPENING'):
context['SHOP_OPENING'] = settings.SHOP_OPENING
if hasattr(settings, 'SHOP_SLOT_SIZE'):
context['SHOP_SLOT_SIZE'] = settings.SHOP_SLOT_SIZE
if hasattr(settings, 'SHOP_OPENING_START_DATE'):
context['SHOP_OPENING_START_DATE'] = settings.SHOP_OPENING_START_DATE
if mode == 'shop' and hasattr(settings, 'SHOP_CAN_BUY'):
context['SHOP_CAN_BUY'] = settings.SHOP_CAN_BUY
context['DELIVERY_CAN_BUY'] = False
......@@ -58,22 +37,53 @@ def index(request, mode="shop"):
context['SHOP_CAN_BUY'] = False
context['DELIVERY_CAN_BUY'] = settings.DELIVERY_CAN_BUY
context['SHOP_CATEGORIES'] = getattr(settings, 'SHOP_CATEGORIES', [])
context['EXCLUDE_SHOP_CATEGORIES'] = getattr(settings, 'EXCLUDE_SHOP_CATEGORIES', [])
context['MIN_DELAY_FOR_SLOT'] = getattr(settings, 'MIN_DELAY_FOR_SLOT', 30)
context['HOURS_FOR_VALIDATION'] = getattr(settings, 'HOURS_FOR_VALIDATION_SHOP', 2)
context['SHOP_OPENING'] = getattr(settings, 'SHOP_OPENING', {})
context['SHOP_SLOT_SIZE'] = getattr(settings, 'SHOP_SLOT_SIZE', 15)
context['SHOP_OPENING_START_DATE'] = getattr(settings, 'SHOP_OPENING_START_DATE', None)
context['survey_link'] = getattr(settings, 'SHOP_SURVEY_LINK', '')
context['extra_menus'] = getattr(settings, 'SHOP_EXTRA_MENUS', None)
context['SHOW_SUBSTITUTION_OPTION'] = getattr(settings, 'SHOW_SUBSTITUTION_OPTION', False)
context['CART_VALIDATION_BOTTOM_MSG'] = getattr(settings, 'CART_VALIDATION_BOTTOM_MSG', "")
context['SHOP_BOTTOM_VALIDATION_MSG'] = getattr(settings, 'SHOP_BOTTOM_VALIDATION_MSG',\
"Si vous arrivez avec un retard de plus d'une heure, la commande pourrait ne plus être disponible.")
stock_warning = getattr(settings, 'SHOP_STOCK_WARNING', True)
if stock_warning is True:
context['SHOP_STOCK_WARNING'] = 'true'
else:
context['SHOP_STOCK_WARNING'] = 'false'
return context
def index(request, mode="shop"):
template = loader.get_template('shop/index.html')
credentials = CagetteMember.get_credentials(request)
shop_settings = CagetteShop.get_shop_settings()
allowed_states = ["up_to_date", "alert", "delay"]
# Uncomment if 'coop_state' in credentials .... etc
# to prevent other states people to use the shop
allowed = True
context = _get_index_context(credentials, shop_settings, mode)
if ('failure' in credentials):
# Visitor has not been identified
template = loader.get_template('website/connect.html')
else:
d_p_pdts = CagetteShop.get_promoted_and_discounted_products()
context['discounted_pdts'] = d_p_pdts['discounted']
context['promoted_pdts'] = d_p_pdts['promoted']
context['survey_link'] = ''
if hasattr(settings, 'SHOP_EXTRA_MENUS'):
context['extra_menus'] = settings.SHOP_EXTRA_MENUS
if hasattr(settings, 'SHOP_SURVEY_LINK'):
context['survey_link'] = settings.SHOP_SURVEY_LINK
context['SHOW_SUBSTITUTION_OPTION'] = True
if hasattr(settings, 'SHOW_SUBSTITUTION_OPTION'):
if settings.SHOW_SUBSTITUTION_OPTION is False:
del context['SHOW_SUBSTITUTION_OPTION']
if hasattr(settings, 'CART_VALIDATION_BOTTOM_MSG'):
context['CART_VALIDATION_BOTTOM_MSG'] = settings.CART_VALIDATION_BOTTOM_MSG
cat_nb_pdts = CagetteShop.get_categories_nb_of_products()
if 'error' in cat_nb_pdts:
context['cat_nb_pdts'] = None
else:
context['cat_nb_pdts'] = cat_nb_pdts
# if 'coop_state' in credentials and not (credentials['coop_state'] in allowed_states):
# allowed = False
......@@ -119,6 +129,7 @@ def get_categ_products(request):
result['error'] = 'Authentification non valide'
return JsonResponse({'res': result})
def search_product(request):
result = {}
credentials = CagetteMember.get_credentials(request)
......@@ -177,7 +188,10 @@ def cart(request):
credentials = CagetteMember.get_credentials(request)
if 'success' in credentials:
try:
result['cart'] = CagetteShop.registrerCart(cart, request.COOKIES['id'])
mode = "shop"
if 'type' in cart:
mode = cart['type']
result['cart'] = CagetteShop.registrerCart(cart, request.COOKIES['id'], mode)
except Exception as e:
result['error'] = str(e)
else:
......@@ -308,4 +322,4 @@ def remove_unused_orders(request):
res['deleted'] = CagetteShop.remove_unused_orders()
except Exception as e:
res['error'] = str(e)
return JsonResponse(res)
\ No newline at end of file
return JsonResponse(res)
......@@ -256,10 +256,10 @@ function init_confirmation_datatable() {
});
}
function without_consent_update_product(p) {
function without_consent_update_product(p, added_qty) {
let undo_option = true;
update_existing_product(p, undo_option);
update_existing_product(p, added_qty, undo_option);
}
function get_stored_product_with_bc(barcode) {
......@@ -307,19 +307,17 @@ function fetch_product_from_bc(barcode) {
'uom': barcodes['uoms'][p.data[barcodes['keys']['uom_id']]],
'standard_price' : p.data[barcodes['keys']['standard_price']], // cost
'list_price': p.data[barcodes['keys']['list_price']] // public price
'list_price': p.data[barcodes['keys']['list_price']], // public price
'qty': p.qty
};
product['uom']['id'] = p.data[barcodes['keys']['uom_id']];
product['value'] = parseFloat(p.value) || 1;
product['rule'] = p.rule;
p_existing = get_stored_product_with_bc(p.barcode);
if (p_existing !== null) {
product.qty = p_existing.qty;
without_consent_update_product(product);
without_consent_update_product(p_existing, product.qty);
return 0;
} else {
add_product(product);
......@@ -335,16 +333,6 @@ function fetch_product_from_bc(barcode) {
var add_product = function(product) {
try {
// Add to list
product.qty = 1;
if (typeof product.value == "number" || (product.value.length > 0 && !isNaN(product.value))) {
//encoded value will be translated in quantity
if (product.rule == "FF_price_to_weight") {
product.qty = get_quantity_eq_to_franc_price(product);
} else {
product.qty = parseFloat(product.value);
}
}
products.push(product);
......@@ -385,26 +373,11 @@ var update_in_products = function(product) {
else console.log("Le produit n'a pas pu être trouvé dans la variable products !");
};
var get_quantity_eq_to_franc_price = function(product) {
let value = 0;
try {
let price = parseFloat(product.value / 6.55957);
value = parseFloat(price / product.list_price).toFixed(3);
} catch (error) {
console.log(error);
}
return value;
};
/*
* Update a line in the table: update quantity
*/
var update_existing_product = function(product, undo_option = false) {
// By default added qty is 1 unit
let added_qty = 1;
var update_existing_product = function(product, added_qty, undo_option = false) {
let op = "augmentée";
let notify_options = {
......@@ -413,35 +386,8 @@ var update_existing_product = function(product, undo_option = false) {
clickToHide: false
};
// type product qty value
if (product.rule == 'weight' || product.rule == 'FF_price_to_weight' && product.value) {
// Quantities are kg or price
product.qty = parseFloat(product.qty) || 0;
if (product.rule == 'weight') {
added_qty = parseFloat(product.value);
} else {
if (product.value < 0) added_qty = parseFloat(product.value); // value is already a qty
else added_qty = parseFloat(get_quantity_eq_to_franc_price(product));
}
} else {
//Quantity is by "defaut" considered as to be in "unit"
product.qty = parseInt(product.qty, 10);
if (product.rule == "" && product.value) {
added_qty = product.value;
}
}
product.qty += added_qty;
/* Surprisingly, this assignment by addition (0 + value)
always correctly "typing" the value
whereas "product.qty = added_qty"
is sometimes typed as "string" !!
*/
// always set to empty to avoid next operation on same product to be misprocessed
product.value = "";
// Find index of row which match product id in the first column
var indexes = products_table.rows().eq(0)
......@@ -470,10 +416,7 @@ var update_existing_product = function(product, undo_option = false) {
notify_options.autoHide = false;
// notify_options.autoHideDelay = 10000;
notify_options.style = 'cancelable';
// msg = $('<span>').text(msg)
// .attr('data-barcode', product.barcode)
// .attr('data-addedqty', added_qty);
// msg = msg.html()
msg = '<span class="msg" data-barcode="' + product.barcode + '" data-added_qty="' + added_qty + '">'
+ "<b>" + product.name + "</b><br/>" + msg
+ '</span>';
......@@ -919,8 +862,7 @@ $(document).ready(function() {
let product = get_stored_product_with_bc(bc);
if (product !== null) {
product.value = - added_qty;
update_existing_product(product);
update_existing_product(product, - added_qty);
} else {
alert("Le produit n'a pas été retrouvé dans la mémoire de travail.");
}
......
......@@ -21,9 +21,9 @@
<input name="city" placeholder="Ville" class="b_green" autocomplete="address-level4"/>
<input name="country" placeholder="Pays" class="b_yellow" autocomplete="address-level4"/>
<span class="phone-wrapper{% if ask_for_second_phone %}-2{% endif %}">
<input type="tel" name="mobile" placeholder="Tél. mobile" class="b_green" pattern="^(\+\d{1,3}(-| ))?\d{1,2}(\.| )\d{1,2}(\.| )\d{1,2}(\.| )\d{1,2}(\.| )\d{0,2}?$" autocomplete="address-level4"/>
<input type="tel" name="mobile" placeholder="Tél. mobile" class="b_green" pattern="{{input_phone_pattern}}" autocomplete="address-level4"/>
{% if ask_for_second_phone %}
<input type="tel" name="phone" placeholder="Tél. fixe" class="b_green" pattern="^(\+\d{1,3}(-| ))?\d{1,2}(\.| )\d{1,2}(\.| )\d{1,2}(\.| )\d{1,2}(\.| )\d{0,2}?$" autocomplete="address-level4"/>
<input type="tel" name="phone" placeholder="Tél. fixe" class="b_green" pattern="{{input_phone_pattern}}" autocomplete="address-level4"/>
{% endif %}
</span>
</p>
......
{% extends "base.html" %}
{% load static %}
{% block additionnal_css %}
<link rel="stylesheet" href="{% static 'css/datatables/jquery.dataTables.css' %}">
<link rel="stylesheet" href="{% static 'jquery-ui-1.12.1/jquery-ui.min.css' %}">
<link rel="stylesheet" href="{% static "css/sales.css" %}?v=">
{% endblock %}
{% block additionnal_scripts %}
<script type="text/javascript" src="{% static 'jquery-ui-1.12.1/jquery-ui.min.js' %}?v="></script>
<script type="text/javascript" src="{% static 'js/datatables/jquery.dataTables.min.js' %}"></script>
<script type="text/javascript" src="{% static 'js/datatables/dataTables.plugins.js' %}"></script>
{% endblock %}
{% block content %}
<div class="page_body">
<div id="content_main" class="page_content">
<div class="header txtcenter">
<h1>Extraction de ventes</h1>
</div>
<div class="select_sales_dates txtcenter">
<form id="sales_form" action="javascript:;">
<div class="sales_date">
<label for="from">Entre :</label>
<input type="text" name="from" id="from" class="select_sales_date_input" required>
</div>
<div class="sales_date">
<label for="to">et :</label>
<input type="text" name="to" id="to" class="select_sales_date_input" required>
</div>
<button type="submit" class="btn--primary" id="dates_selection_button" disabled>C'est parti !</button>
</form>
</div>
<div class="main" style="display:none;">
<div class="table_area">
<table id="orders_table" class="display" cellspacing="0" width="100%"></table>
</div>
</div>
</div>
<div id="templates" style="display:none;">
</div>
</div>
<script src="{% static "js/all_common.js" %}?v="></script>
<script src="{% static "js/sales.js" %}?v="></script>
{% endblock %}
......@@ -391,7 +391,7 @@
<option value="23:00">23:00</option>
</select>
{% endif %}
<div class="slots-constraints" style="display: none;">
<span class="delay24h"><strong>avec un délai de {{MIN_DELAY_FOR_SLOT}}h minimum</strong></span>
<!--<input type="text" placeholder="Exemple: mardi 24 mars à 10h30" name="best_date" style="width: 350px;"/>--><br/>
......@@ -405,7 +405,7 @@
{%if HOURS_FOR_VALIDATION > 0 %}
<div class="tv-msg" style="display: none;">
<strong>
Vous aurez <span class="time-given-for-validation">{{HOURS_FOR_VALIDATION}} heure(s)</span> pour valider votre commande.<br/>
Vous aurez <span class="time-given-for-validation">{{HOURS_FOR_VALIDATION}} heure(s)</span> pour valider votre commande.<br/>
Passé ce temps, la {% if mode == 'shop' %}réservation du créneau horaire{% elif mode == 'delivery' %}commande{% endif %} sera annulée.<br/>
Vous devrez rechoisir un horaire (le panier sera mémorisé).
</strong>
......@@ -436,7 +436,7 @@
</p>
<p>
{% if mode == 'shop' %}
<strong><i>Si vous arrivez avec un retard de plus d'une heure, la commande pourrait ne plus être disponible.</i></strong>
<strong><i>{{SHOP_BOTTOM_VALIDATION_MSG}}</i></strong>
{%endif%}
</p>
<p>
......@@ -500,6 +500,10 @@
{%if SHOP_OPENING_START_DATE%}
const opening_start_date = new Date('{{SHOP_OPENING_START_DATE}}')
{%endif%}
{%if cat_nb_pdts%}
const cat_nb_pdts = {{cat_nb_pdts|safe}}
{%endif%}
const stock_warning = {{SHOP_STOCK_WARNING|safe}}
</script>
<script src="{% static 'js/all_common.js' %}?v="></script>
<script type="text/javascript" src="{% static 'js/shop.js' %}?v="></script>
......
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