Commit b4d49b83 by François C.

Développements Cooperatic depuis commit initial

parent bf80e7b2
......@@ -12,6 +12,6 @@ outils/texts/*
outils/js_errors.log
db.sqlite3
*/max_timeslot_carts.txt
.gitlab-ci.yml
shop/shop_admin_settings.json
shop/errors.log
node_modules/
\ No newline at end of file
from django.db import models
from django.conf import settings
from outils.common_imports import *
from outils.common import OdooAPI
from outils.common import CouchDB
......@@ -13,15 +14,7 @@ class CagetteEnvelops(models.Model):
"""Init with odoo id."""
self.o_api = OdooAPI()
self.c_db = CouchDB(arg_db='envelops')
for pm in settings.SUBSCRIPTION_PAYMENT_MEANINGS:
if pm['code'] == 'cash':
self.cash_code = pm['journal_id']
elif pm['code'] == 'ch':
self.check_code = pm['journal_id']
elif pm['code'] == 'cb':
self.cb_code = pm['journal_id']
elif pm['code'] == 'vir':
self.vir_code = pm['journal_id']
def get_all(self):
envelops = []
......@@ -59,18 +52,13 @@ class CagetteEnvelops(models.Model):
invoice = invoice_res[0]
else:
res['error'] = 'No invoice found for this partner, can\'t create payment.'
coop_logger.error(res['error'] + ' : %s', str(data))
return res
# Set payment type
if data['type'] == "cash":
payment_type_code = self.cash_code
elif data['type'] == "ch":
payment_type_code = self.check_code
elif data['type'] == "cb":
payment_type_code = self.cb_code
elif data['type'] == "vir":
payment_type_code = self.vir_code
payment_journal_id = None
for pm in settings.SUBSCRIPTION_PAYMENT_MEANINGS:
if pm['code'] == data['type']:
payment_journal_id = pm['journal_id']
args = {
"writeoff_account_id": False,
"payment_difference_handling": "open",
......@@ -78,7 +66,7 @@ class CagetteEnvelops(models.Model):
"currency_id": 1,
"amount": data['amount'],
"payment_method_id": 1,
"journal_id": payment_type_code,
"journal_id": payment_journal_id,
"partner_id": data['partner_id'],
"partner_type": "customer",
"payment_type": "inbound",
......@@ -86,8 +74,11 @@ class CagetteEnvelops(models.Model):
"invoice_ids": [(4, invoice['id'])]
}
payment_id = self.o_api.create('account.payment', args)
try:
payment_id = self.o_api.create('account.payment', args)
except Exception as e:
res['error'] = repr(e)
coop_logger.error(res['error'] + ' : %s', str(args))
# Exception rises when odoo method returns nothing
marshal_none_error = 'cannot marshal None unless allow_none is enabled'
try:
......@@ -96,7 +87,7 @@ class CagetteEnvelops(models.Model):
except Exception as e:
if not (marshal_none_error in str(e)):
res['error'] = repr(e)
coop_logger.error(res['error'] + ' : %s', str(payment_id))
if not ('error' in res):
try:
if int(float(data['amount']) * 100) == int(float(invoice['residual_signed']) * 100):
......@@ -104,9 +95,11 @@ class CagetteEnvelops(models.Model):
self.o_api.update('account.invoice', [invoice['id']], {'state': 'paid'})
except Exception as e:
res['error'] = repr(e)
coop_logger.error(res['error'])
except Exception as e:
res['error'] = repr(e)
coop_logger.error(res['error'] + ' : %s', str(data))
if not ('error' in res):
......
from django.db import models
from django.conf import settings
from outils.common import OdooAPI
from outils.common import CouchDB
from outils.common_imports import *
from decimal import *
import os, json
import os
from datetime import datetime
import logging
......@@ -41,18 +42,19 @@ class CagetteInventory(models.Model):
for r, d, f in os.walk(custom_list_file_path):
for file in f:
# name of file is timestamp of creation
filename_array = file.split('.')
if file.endswith('.json'):
filename = file[:-5]
# if id is set, only get this list
if id is None or id is not None and id == filename_array[0]:
if id is None or id is not None and id == filename:
file_data = {}
with open(os.path.join(r, file)) as json_file:
file_data = json.load(json_file)
date_time = datetime.fromtimestamp(int(filename_array[0]))
date_time = datetime.fromtimestamp(int(filename))
d = date_time.strftime("%m/%d/%Y, %H:%M")
file_data['id'] = int(filename_array[0])
file_data['id'] = int(filename)
file_data['datetime_created'] = d
file_data['p_nb'] = len(file_data['products'])
......@@ -102,6 +104,67 @@ class CagetteInventory(models.Model):
return res
@staticmethod
def create_custom_inv_file(line_ids, line_type):
res = {}
try:
# Create directory if doesn't exist
os.mkdir(custom_list_file_path)
except OSError:
pass
try:
# need to retrieve product_ids from order_line_ids
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']
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']
else:
order = l['order_id']
partner = 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]
file_data = {
'order': order[1],
'user': user,
'partner': partner,
'inventory_status': '',
'products': ids
}
# Crate inventory file, name is timestamp of creation
timestamp = int(time.time())
filename = custom_list_file_path + str(timestamp) + '.json'
with open(filename, 'w+') as outfile:
json.dump(file_data, outfile)
res['file_saved'] = True
except Exception as e:
res['error'] = str(e)
coop_logger.error("create_custon_inv_file : %s", str(e))
return res
def update_custom_inv_file(inventory_data):
res = {}
try:
......@@ -119,7 +182,7 @@ class CagetteInventory(models.Model):
res['file_saved'] = True
except Exception as e:
res['error'] = str(e)
return res
def remove_custom_inv_file(id):
......@@ -144,7 +207,6 @@ class CagetteInventory(models.Model):
if 'user_comments_s1' in file_data:
inventory_data['user_comments_step1'] = file_data['user_comments_s1']
return inventory_data
@staticmethod
......@@ -310,13 +372,13 @@ class CagetteInventory(models.Model):
if qty < 0:
qty = 0
try:
fields = {'product_id': p['id'],
'inventory_id': inv,
'product_qty': str(qty),
'product_uom_id': p['uom_id'][0],
'location_id': settings.STOCK_LOC_ID}
# coop_logger.info("Fields %s", fields)
li = api.create('stock.inventory.line', fields)
done.append({'id': li})
except Exception as e:
......@@ -329,10 +391,10 @@ class CagetteInventory(models.Model):
coop_logger.error(str(e))
errors.append(str(e))
return {'errors': errors,
'missed': missed,
'unchanged': unchanged,
'done': done,
return {'errors': errors,
'missed': missed,
'unchanged': unchanged,
'done': done,
'inv_id': inv}
@staticmethod
......
......@@ -25,7 +25,7 @@ button[name="local_to_global_sync"], button[name="global_to_local_sync"]
.error_msg {color: #d9534f;}
.fa-trash {cursor: pointer;}
@media screen and (max-width: 767px) {
.barcode, .delta {display: none;}
}
\ No newline at end of file
var shelfs_table = null;
var shelfs_table = null,
delete_icon = '<i class="fas fa-trash"></i>';
function init_datatable() {
return $('#lists').DataTable({
......@@ -13,6 +16,15 @@ function init_datatable() {
return "Liste du " + data;
}
},
{
data:"partner",
title:"Fournisseur"
},
{
data:"order",
title:"Ref."
},
{data:"p_nb", title:"Nombre de réfs", width: "10%", className:"dt-body-center"},
{
data:"inventory_status",
......@@ -25,6 +37,14 @@ function init_datatable() {
else
return "<button class='btn--success do_inventory'>Inventaire en réserve</button>";
}
},
{
data: null,
defaultContent: delete_icon,
orderable: false,
width: "30px",
className: 'action',
targets: 0
}
],
dom: 'rtip',
......@@ -39,7 +59,44 @@ function init_datatable() {
});
}
var rowGetData = function(clicked) {
var row = shelfs_table.row(clicked.parents('tr'));
return row.data();
};
function deleteRow() {
var clicked = $(this),
data = rowGetData(clicked);
openModal(
'Etes-vous sûr de vouloir supprimer la liste du ' + data.datetime_created+ ' ?',
function() {
post_form(
'/inventory/delete_custom_list',
{id: data.id},
function(err, result) {
closeModal();
if (!err) {
if (typeof result.success !== "undefined" && result.success == true) {
clicked.parents('tr').remove();
alert("Liste détruite");
} else {
alert(JSON.stringify(result));
}
} else {
console.log(err);
}
}
);
},
'Oui'
);
}
function go_to_inventory() {
openModal();
......@@ -60,8 +117,9 @@ function go_to_inventory() {
}
$(document).ready(function() {
console.log(lists);
shelfs_table = init_datatable();
$(document).on('click', 'button.do_inventory', go_to_inventory);
$(document).off('.do_inv, .del_row')
.on('click.do_inv', 'button.do_inventory', go_to_inventory)
.on('click.del_row', '.fa-trash', deleteRow);
});
......@@ -6,9 +6,11 @@ from . import views
urlpatterns = [
url(r'^$', views.home),
url(r'^custom_lists$', views.custom_lists),
url(r'^delete_custom_list$', views.delete_custom_list),
url(r'^custom_list_inventory/([0-9]+)$', views.custom_list_inventory),
url(r'^get_custom_list_data$', views.get_custom_list_data),
url(r'^do_custom_list_inventory$', views.do_custom_list_inventory),
url(r'^generate_inventory_list$', views.generate_inventory_list),
url(r'^get_credentials$', views.get_credentials),
url(r'^get_product_categories$', views.get_product_categories),
url(r'^create_inventory$', views.create_inventory),
......
......@@ -2,9 +2,10 @@ from django.shortcuts import render
from django.http import HttpResponse
from django.http import JsonResponse
from django.template import loader
from django.conf import settings
from django.views.decorators.csrf import csrf_exempt
from members.models import CagetteUser
from .models import CagetteInventory
from outils.common_imports import *
import json
def home(request):
......@@ -28,6 +29,15 @@ def custom_lists(request):
return HttpResponse(template.render(context, request))
def delete_custom_list(request):
"""Custom list file will be removed."""
try:
res = CagetteInventory.remove_custom_inv_file(request.POST.get('id'))
return JsonResponse({'success': True})
except Exception as e:
coop_looger.error("delete_custom_list : %s", str(e))
return JsonResponse({'error': str(e)}, status=500)
def custom_list_inventory(request, id):
"""Inventaire d'une liste de produits"""
products = CagetteInventory.get_custom_list_products(id)
......@@ -80,12 +90,29 @@ def do_custom_list_inventory(request):
CagetteInventory.remove_custom_inv_file(inventory_data['id'])
except Exception as e:
res['error'] = {'inventory' : str(e)}
coop_logger.error("Enregistrement inv. personnalisé : %s", str(e))
if 'error' in res:
return JsonResponse(res, status=500)
else:
return JsonResponse({'res': res})
@csrf_exempt
def generate_inventory_list(request):
"""Responding to Odoo ajax call (no csrf)."""
res = {}
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:
res['error'] = str(e)
coop_looger.error("generate_inventory_list : %s", str(e))
if 'error' in res:
return JsonResponse(res, status=500)
else:
return JsonResponse({'res': res})
def get_credentials(request):
"""Receiving user mail + password, returns id, rights and auth token"""
return JsonResponse(CagetteUser.get_credentials(request))
......
......@@ -327,17 +327,10 @@ class CagetteMember(models.Model):
except Exception as e:
res['error'] = 'Erreur maj membre dans Odoo'
lf = open("/tmp/erreurs_django.log", "a")
lf.write("Point A:" + "\n")
lf.write(str(res) + "\n")
lf.write(str(e) + "\n")
lf.close()
coop_logger.error("Pb avec couchDB (1): %s \n %s", str(received_data), str(e))
else:
res['error'] = 'Pb avc couchDB'
lf = open("/tmp/erreurs_django.log", "a")
lf.write("Point B:" + "\n")
lf.write(str(r) + "\n" + str(received_data) + "\n")
lf.close()
res['error'] = 'Pb avec couchDB'
coop_logger.error("Pb avec couchDB (B): %s", str(received_data))
return res
@staticmethod
......@@ -474,11 +467,8 @@ class CagetteMember(models.Model):
c_db.updateDoc(update_data, '_id')
except Exception as e:
res['error'] = 'Erreur après souscription du capital'
lf = open("/tmp/erreurs_django.log", "a")
lf.write("Point C:")
lf.write(str(res) + "\n")
lf.write(str(e) + "\n")
lf.close()
coop_logger.error("Erreur après souscription : %s \n %s", str(res), str(e))
# Create or update envelop(s) with coop payment data
payment_data = {
......@@ -513,16 +503,10 @@ class CagetteMember(models.Model):
else:
res['error'] = 'Pb avec couchDB'
res['data'] = update_data
lf = open("/tmp/erreurs_django.log", "a")
lf.write("Point C:")
lf.write(str(res) + "\n")
lf.close()
coop_logger.error("Pb couchDB (C) : %s", str(res))
else:
res['error'] = 'Erreur creation membre odoo'
lf = open("/tmp/erreurs_django.log", "a")
lf.write("Point C:")
lf.write(str(res) + "\n")
lf.close()
coop_logger.error("Pb couchDB (D) : %s", str(res))
# Update coop data
else:
odoo_id = int(post_data['odoo_id'])
......@@ -636,9 +620,9 @@ class CagetteMember(models.Model):
cond = [['partner_id', 'in', ids],
['date_begin', '>=', datetime.datetime.now().isoformat()],
['state', '=', 'open']]
fields = ['shift_type', 'date_begin', 'partner_id', 'date_end','shift_ticket_id']
fields = ['shift_type', 'date_begin', 'partner_id', 'date_end', 'shift_ticket_id']
res = api.search_read('shift.registration', cond, fields, 2500,0, 'date_begin ASC')
res = api.search_read('shift.registration', cond, fields, 2500, 0, 'date_begin ASC')
shifts = {}
locale.setlocale(locale.LC_ALL, 'fr_FR.utf8')
......@@ -758,7 +742,7 @@ class CagetteMember(models.Model):
def remove_from_mess_list(request):
res = {}
try:
_id = request.POST.get("id","")
_id = request.POST.get("id", "")
c_db = CouchDB(arg_db='member_mess')
doc = c_db.getDocById(_id)
res['action'] = c_db.delete(doc)
......
......@@ -105,7 +105,6 @@ function reset_sex_radios() {
sex.find('input').each(function(i, e) {
$(e).prop('checked', false);
});
$('#o_sex').prop('checked', true);
}
function create_new_coop() {
......@@ -116,7 +115,8 @@ function create_new_coop() {
if (current_coop == null || typeof(current_coop.shift_template) != "undefined") {
current_coop = null;
ncoop_view.find('.title').text('NOUVEAU MEMBRE');
coop_create.find('input').val('');
coop_create.find('input').not('[type="radio"]')
.val('');
coop_list_view.hide();
schoice_view.hide();
coop_registration_details.hide();
......@@ -195,7 +195,7 @@ function store_new_coop(event) {
event.preventDefault();
var errors = [],
bc = '', // barcode may not be present
msex = 'o'; // sex may not be present
msex = ''; // sex may not be present
// 1- Un coop avec le meme mail ne doit pas exister dans odoo (dans base intermediaire, le cas est géré par l'erreur à l'enregistrement)
let email = $('input[name="email"]').val()
.trim(),
......@@ -206,17 +206,10 @@ function store_new_coop(event) {
if (m_barcode.length > 0) {
bc = m_barcode.val();
if (!isValidEAN13(bc)) errors.push("Le code-barre ne semble pas être un EAN13 valide.");
}
if (sex.length > 0) {
msex = $("#sex input[name='sex']:checked").get(0).value; //.val() doesn't work here !!! (with chrome)
//cant understand why, but input radio value are sometimes emptied !!
//so, let's get value with a trick
if (typeof msex == "undefined" || msex.length == 0) {
var sex_id = $("#sex input[name='sex']:checked").attr('id');
msex = sex_id.replace('_sex', '');
}
msex = $('input[name="sex"]:checked').val();
}
if (payment_meaning.val() == 'ch' && ch_qty.val() <1) {
......
......@@ -11,17 +11,7 @@ if (coop_is_connected()) {
// PouchDB sync actions listeners
sync.on('change', function (info) {
// handle change
need_reload = false;
$.each(info.change.docs, function(i, e) {
console.log('changement');
console.log(e);
});
if (need_reload == true) {
//retrieve_and_draw_shift_tempates('without_modal');
}
}).on('paused', function (err) {
......
......@@ -156,16 +156,6 @@ function submit_full_coop_form() {
sex = $('#sex'),
has_empty_values = false;
if (sex.length > 0) {
//value attrribute is emptied when form is loaded !!
//so, we have to retrive sex value using unusual way
form_data.set(
'sex',
$('input[name="sex"]:checked').attr('id')
.replace('_sex', '')
);
}
for (var pair of form_data.entries()) {
let val = pair[1],
key = pair[0];
......@@ -203,6 +193,9 @@ function submit_full_coop_form() {
if (m_barcode.length > 0) {
form_data.set('m_barcode', m_barcode.val());
}
if (sex.length > 0) {
form_data.set('sex', $('input[name="sex"]:checked').val());
}
post_form(
'/members/coop_validated_data', form_data,
function(err, result) {
......@@ -243,14 +236,16 @@ function save_current_coop(callback) {
//_id obligatoire !
let form = coop_validation_form,
_id = form.find('[name="email"]').val(),
m_barcode = form.find('[name="m_barcode"]');
m_barcode = form.find('[name="m_barcode"]'),
sex = form.find('[name="sex"]');
if (current_coop != null && _id.length > 0) {
//Birthdate verification
let birthdate = form.find('[name="birthdate"]').val()
.trim();
var birthdate_error = false,
m_barcode_error = false;
m_barcode_error = false,
sex_error = false;
if (/([0-9]{2})\/([0-9]{2})\/([0-9]{4})/.exec(birthdate)) {
var jj = RegExp.$1,
......@@ -278,6 +273,11 @@ function save_current_coop(callback) {
current_coop._id = _id;
current_coop.birthdate = birthdate;
current_coop.address = form.find('[name="address"]').val();
if (sex.length > 0) {
current_coop.sex = $('input[name="sex"]:checked').val();
if (typeof current_coop.sex == "undefined") sex_error = true;
}
if (street2_input.length > 0) {
current_coop.street2 = street2_input.val();
}
......@@ -296,8 +296,7 @@ function save_current_coop(callback) {
current_coop.m_barcode = m_barcode.val();
if (!isValidEAN13(current_coop.m_barcode)) m_barcode_error = true;
}
if ((birthdate_error == true || m_barcode_error == true) && callback) {
if ((birthdate_error == true || m_barcode_error == true || sex_error == true) && callback) {
put_current_coop_in_buffer_db();
closeModal();
var msg = '';
......@@ -306,6 +305,8 @@ function save_current_coop(callback) {
msg += "La date de naissance ne semble pas correcte (jj/mm/aaaa)\n";
if (m_barcode_error == true)
msg += "Le code-barre n'est pas valide\n";
if (sex_error == true)
msg += "Une option concernant le sexe doit être cochée\n";
alert(msg);
} else {
// Send coop to next step
......@@ -436,8 +437,8 @@ function set_current_coop(clicked, callback) {
var coops_set = null;
//coop_form is always empty, in order to remove all previous data, which could be associated to another coop.
coop_validation_form.find('input').val('');
coop_validation_form.find(':input').not('[type="radio"]')
.val('');
if (type == 'to_fill') {
coops_set = coops.to_fill;
} else if (type == 'with_errors') {
......
......@@ -39,11 +39,9 @@ function display_current_coop_form() {
if (m_barcode.length > 0 && typeof current_coop.m_barcode != "undefined") {
m_barcode.val(current_coop.m_barcode);
}
//console.log(current_coop)
if (sex.length > 0 && typeof current_coop.sex != "undefined") {
$("#" + current_coop.sex + "_sex").prop('checked', true);
$('input[name="sex"][value="' + current_coop.sex + '"]').prop('checked', true);
}
// form.find('[name="barcode_base"]').val(current_coop.barcode_base);
form.find('[name="email"]').val(current_coop._id);
if (current_coop.shift_template &&
......
......@@ -43,16 +43,11 @@ function process_form_submission(event) {
if (fname == 'valider') {
var form_data = new FormData(vform.get(0)),
has_empty_values = false;
empty_values = {},
value_missing = false;
if (sex.length > 0) {
//value attrribute is emptied when form is loaded !!
//so, we have to retrive sex value using unusual way
form_data.set(
'sex',
$('input[name="sex"]:checked').attr('id')
.replace('_sex', '')
);
form_data.set('sex', $('input[name="sex"]:checked').val());
}
for (var pair of form_data.entries()) {
......@@ -61,12 +56,17 @@ function process_form_submission(event) {
if ($('input[name="' + key +'"]').get(0)
.hasAttribute('required') && val.length == 0) {
has_empty_values = true;
empty_values[key] = val;
}
}
if (Object.keys(empty_values).length > 0) {
value_missing = true;
if (typeof empty_values['mobile'] !== "undefined" && typeof empty_values['phone'] === "undefined") {
value_missing = false;
}
}
if (has_empty_values == true) {
if (value_missing == true) {
alert('Vous devez remplir tous les champs pour valider.');
} else {
form_data.set(
......
......@@ -121,6 +121,10 @@
- VRAC_CATEGS = [166, 167, 174]
- SHELF_LABELS_ADD_FIELDS = ['category_print_id', 'categ_id', 'country_id', 'label_ids', 'uom_id']
Fields add to generated text file
### Shop module
- SHOP_CAN_BUY = False
......@@ -221,6 +225,10 @@
By default, if this variable is not set, sunday is hidden
To hide Sunday and Monday, set this to "0,1"
- SHIFT_COLOR_TOGGLE_NUM = 7
If not set, shift class for rendering color is based on % (toggle limit = 50%)
If set with a number, this number is used (toggle limit = this number, including it)
- SHIFT_INFO = """A la cagette, un service est une plage de trois heures un jour en particulier, par exemple : le mardi 25/09/2018 à 13h15.
<br />A l'inverse, un créneau est une plage de trois heures régulière, par exemple, tous les mardi de semaine A à 13h15."""
......@@ -257,6 +265,27 @@
DB coeff id, needed to compute product shelf price
### Stocks
- STOCK_LOC_ID = 12
Only used in Inventory module, which is no more in use
- LOSSES_LOC_ID = 33
- LOSSES_PICKING_TYPE_ID = 10
- AUTOCONSO_LOC_ID = 27
- AUTOCONSO_PICKING_TYPE_ID = 7
- MEALS_LOC_ID = 33
- MEALS_PICKING_TYPE_ID = 10
### Miscellious
- EXPORT_COMPTA_FORMAT = 'Quadratus'
......@@ -264,9 +293,7 @@
If not set, export format is the one used by La Cagette
Quadratus has been introduced to fit with Supercafoutch need.
- STOCK_LOC_ID = 12
Only used in Inventory module, which is no more in use
- CUSTOM_CSS_FILES = {'all': ['common_lgds.css'],
'members': ['inscription_lgds.css']}
......
......@@ -14,6 +14,7 @@ https://docs.djangoproject.com/en/1.8/ref/settings/
import os
from .settings_secret import *
from .settings_constants import *
from .texts.cagette import *
from .config import *
from .customized_errors_filter import *
......@@ -28,8 +29,6 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'Mettre_plein_de_caracteres_aleatoires_iezezezeezezci'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = ['127.0.0.1']
......@@ -218,3 +217,9 @@ LOGGING = {
}
}
}
# To be removed in production environment
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
CORS_ORIGIN_ALLOW_ALL = True # Needed to make dev test with different IP and ports
\ No newline at end of file
"""Secret data for DB connexion ."""
ODOO = {
'url': 'http://127.0.0.1:8069',
'url': 'http://127.0.0.1:8069'
'user': 'api',
'passwd': 'xxxxxxxxxxxx',
'db': 'bd_test',
......
......@@ -138,6 +138,10 @@ footer { position: fixed;
transition: opacity 0.3s;
}
.tooltip .tooltip-lg {
width: 230px !important;
}
.tooltip .tt_twolines {
top: -15px !important;
}
......@@ -160,4 +164,4 @@ footer { position: fixed;
opacity: 1;
}
.mac-msg {background: red; color: #fff; padding: 3px;}
\ No newline at end of file
.mac-msg {background: red; color: #fff; padding: 3px;}
......@@ -31,7 +31,7 @@ function get_litteral_shift_template_name(name) {
}
function is_time_to(action, delay = 5000) {
function is_time_to(action, delay=5000) {
var answer = false;
var last_date = actions_last_dates[action] || 0;
var d = new Date();
......@@ -207,13 +207,13 @@ function openModal() {
btn_ok.on('click', arguments[1]); // Second argument is callback
btn_ok.text(arguments[2] || 'Enregistrer'); // Third is button text
// 4th argument: if not set or set and not false, validate button closes the modal
// 4th argument: if set and false, validate button doesn't close the modal
if (typeof (arguments[3]) == "undefined" || arguments[3] != false)
btn_ok.on('click', closeModal);
btns.append(btn_ok);
// 5th argument: if not set or set and not false, add 'cancel' button
// 5th argument: if set and false, no 'cancel' button
if (typeof (arguments[4]) == "undefined" || arguments[4] != false) {
btn_nok.text('Annuler');
btn_nok.on('click', closeModal);
......
......@@ -64,9 +64,9 @@
if ($this.options.swipe) {
if (event.which == 13) {
if( !$this.options.submit_on_swipe ){
event.preventDefault();
}
if( !$this.options.submit_on_swipe ){
event.preventDefault();
}
var swipexp = new RegExp('^' + $this.options.prefix.swipe.card + $this.options.regexp.swipe.card + '$');
if (data.swipe.match(swipexp)) {
var swipe_match = swipexp.exec(data.swipe);
......
......@@ -151,6 +151,7 @@ class ExportPOS(View):
return HttpResponse(template.render(context, request))
def __ca_sessions_ng(self, mois):
import re
debut = time.time()
api = OdooAPI()
res = api.execute('lacagette.pos_payments_export', 'get_pos_payments', {'month' : mois})
......@@ -163,6 +164,13 @@ class ExportPOS(View):
details_lines = []
for s in res['sessions']:
if 'min' in s['mm_dates']:
"""
s['mm_dates']['min'] and s['mm_dates']['max'] could be formatted with milliseconds
i.e 2020-12-12 12:38:58.136 (rescue Session)
Thus, .xxx has to be removed
"""
s['mm_dates']['min'] = re.sub(r'\.[0-9]+', '', s['mm_dates']['min'])
s['mm_dates']['max'] = re.sub(r'\.[0-9]+', '', s['mm_dates']['max'])
min_date = time.strptime(s['mm_dates']['min'], tf_ym)
max_date = time.strptime(s['mm_dates']['max'], tf_ym)
......
......@@ -34,6 +34,18 @@ class CagetteProduct(models.Model):
return api.search_read('product.product', cond, fields)
def get_products_stdprices(ids):
api = OdooAPI()
cond = [['id', 'in', ids]]
fields = ['id', 'standard_price']
try:
res = api.search_read('product.product', cond, fields)
except Exception as e:
res = {'error': str(e)}
return res
@staticmethod
def get_product_info_for_label_from_template_id(template_id):
"""Get product info for label."""
......@@ -41,7 +53,8 @@ class CagetteProduct(models.Model):
cond = [['product_tmpl_id.id', '=', template_id]]
fields = ['barcode', 'product_tmpl_id', 'pricetag_rackinfos',
'price_weight_net', 'price_volume', 'list_price',
'weight_net', 'volume','to_weight']
'weight_net', 'volume', 'to_weight']
fields += getattr(settings, 'SHELF_LABELS_ADD_FIELDS', [])
return api.search_read('product.product', cond, fields)
@staticmethod
......@@ -53,8 +66,8 @@ class CagetteProduct(models.Model):
product = p[0]
txt = ''
for k, v in product.items():
if type(v) == list:
v = v[1]
if type(v) == list and len(v) > 0 :
v = v[-1]
if k == 'product_tmpl_id':
k = 'name'
if k == 'list_price' and len(price) > 0 and float(price) > 0:
......@@ -75,6 +88,7 @@ class CagetteProduct(models.Model):
file.close()
except Exception as e:
res['error'] = str(e)
coop_logger.error("Generate label : %s %s", templ_id, str(e))
return res
class CagetteProducts(models.Model):
......@@ -170,18 +184,43 @@ class CagetteProducts(models.Model):
@staticmethod
def get_all_barcodes():
api = OdooAPI()
fields = ['barcode', 'display_name', 'sale_ok', 'purchase_ok', 'available_in_pos']
# cond = ['|', ('active', '=', True), ('active', '=', False)]
cond = [] # equivalent to [['active', '=', 'True']]
res = api.search_read('product.product', cond, fields, 10000)
"""Needs lacagette_products Odoo module to be active."""
result = {}
api = OdooAPI()
try:
res = api.execute('lacagette.products', 'get_barcodes', {})
if 'list' in res:
result['pdts'] = {}
for p in res['list']:
# transcode result to compact format (for bandwith save and browser memory)
# real size / 4 (for 2750 products)
result['pdts'][p['barcode']] = [
p['display_name'],
p['sale_ok'],
p['purchase_ok'],
p['available_in_pos'],
p['id'],
p['standard_price'],
p['uom_id']]
if 'uoms' in res and 'list' in res['uoms']:
result['uoms'] = res['uoms']['list']
elif 'error' in res:
result['error'] = res['error']
except Exception as e:
result['error'] = str(e)
return result
for p in res:
# transcode result to compact format (for bandwith save and browser memory)
# real size / 4 (for 2750 products)
result[p['barcode']] = [p['display_name'], p['sale_ok'], p['purchase_ok'], p['available_in_pos']]
def get_uoms():
result = {}
api = OdooAPI()
try:
cond = [['active', '=', True]]
fields = ['display_name', 'uom_type']
res = api.search_read('product.uom', cond, fields)
result['list'] = res
except Exception as e:
result['error'] = str(e)
return result
@staticmethod
......
......@@ -13,7 +13,9 @@ IFCBarcodes = {
closeModal();
if (typeof bc_data.res.error == "undefined") {
this.patterns = bc_data.res.patterns;
this.codes = bc_data.res.bc;
this.codes = bc_data.res.list.pdts;
this.uoms = bc_data.res.list.uoms;
this.keys = bc_data.res.keys;
} else {
this.errors.push(bc_data.error);
}
......
......@@ -5,6 +5,7 @@ from . import views
urlpatterns = [
url(r'^$', views.home),
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'^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),
......
......@@ -41,9 +41,21 @@ def get_product_data(request):
p['shelf_sortorder'] = shelfs_sortorder[0]['sort_order']
except Exception as e:
p['shelf_sortorder'] = 'X'
else:
p['shelf_sortorder'] = 'X'
return JsonResponse({"product": p})
def get_products_stdprices(request):
ids = json.loads(request.body.decode())
res = CagetteProduct.get_products_stdprices(ids)
if ('error' in res):
return JsonResponse(res, status=500)
else:
return JsonResponse({"res": res})
def update_product_stock(request):
res = {}
product_data = json.loads(request.body.decode())
......@@ -152,10 +164,22 @@ def get_all_available_products(request):
def get_all_barcodes(request):
"""Return all stored products barcodes."""
import time
start = int(round(time.time() * 1000))
res = {}
try:
res['bc'] = CagetteProducts.get_all_barcodes()
res['list'] = CagetteProducts.get_all_barcodes()
res['keys'] = {
'name': 0,
'sale_ok': 1,
'purchase_ok': 2,
'available_in_pos': 3,
'id': 4,
'standard_price': 5,
'uom_id': 6
}
rules = CagetteProducts.get_barcode_rules()
res['time'] = int(round(time.time() * 1000)) - start
res['patterns'] = []
for r in rules:
if '{' in r['pattern'] or '.' in r['pattern']:
......
......@@ -223,7 +223,7 @@ function initLists() {
width: "50%",
render: function (data, type, full, meta) {
// Add tooltip with barcode over product name
var display_barcode = "Aucun";
let display_barcode = "Aucun";
if ('barcode' in full) {
display_barcode = full.barcode;
......@@ -286,7 +286,7 @@ function initLists() {
width: "60%",
render: function (data, type, full, meta) {
// Add tooltip with barcode over product name
var display_barcode = "Aucun";
let display_barcode = "Aucun";
if ('barcode' in full) {
display_barcode = full.barcode;
......@@ -464,11 +464,12 @@ function add_to_toProcess(product) {
// Add to table (no data binding...)
var rowNode = table_to_process.row.add(product).draw(false)
.node();
// Handle blinking effect for newly added row
var onAnimationEnd = function() {
rowNode.classList.remove('blink_me');
};
// Handle blinking effect for newly added row
$(rowNode).addClass('blink_me');
rowNode.addEventListener('animationend', onAnimationEnd);
rowNode.addEventListener('webkitAnimationEnd', onAnimationEnd);
......@@ -494,11 +495,12 @@ function add_to_processed(product, withCounter = true) {
// Add to table (no data binding...)
var rowNode = table_processed.row.add(product).draw(false)
.node();
// Handle blinking efect for newly added row
var onAnimationEnd = function() {
rowNode.classList.remove('blink_me');
};
// Handle blinking efect for newly added row
$(rowNode).addClass('blink_me');
rowNode.addEventListener('animationend', onAnimationEnd);
rowNode.addEventListener('webkitAnimationEnd', onAnimationEnd);
......@@ -569,14 +571,26 @@ function setLineEdition(product) {
if (editing_product.product_uom[0] == 1) { // Unit
if (reception_status == 'False') {
document.getElementById('product_uom').innerHTML = ' unité(s)';
$('#edition_input').attr('type', 'number')
.attr('step', 1)
.attr('max', 9999);
} else {
document.getElementById('product_uom').innerHTML = ' / unité';
$('#edition_input').attr('type', 'number')
.attr('step', 0.01)
.attr('max', 9999);
}
} else if (editing_product.product_uom[0] == 21) { // kg
if (reception_status == 'False') {
document.getElementById('product_uom').innerHTML = ' kg';
$('#edition_input').attr('type', 'number')
.attr('step', 0.001)
.attr('max', 9999);
} else {
document.getElementById('product_uom').innerHTML = ' / kg';
$('#edition_input').attr('type', 'number')
.attr('step', 0.01)
.attr('max', 9999);
}
}
......@@ -798,10 +812,10 @@ function pre_send(type) {
let modal_next_step = '#templates #modal_prices_validation';
updateType = type;
if (type == 'qty_valid') {
modal_next_step = '#templates #modal_qties_validation';
}
openModal($(modal_next_step).html(), data_validation, 'Confirmer', false);
}
}
......@@ -1042,7 +1056,7 @@ function send() {
// Remove all groups containing these orders
for (order_id in orders) {
search:
for (var h = 0; h < grouped_orders.length; h++) {
for (var h = 0; i < grouped_orders.length; h++) {
for (var j = 0; j < grouped_orders[h].length; j++) {
if (grouped_orders[h][j] == order_id) {
grouped_orders.splice(h);
......
......@@ -389,7 +389,7 @@ def save_error_report(request):
ws.append( ['Montant total attendu (TTC) : ', str(round(order['amount_total'],2)) + ' €'] )
ws.append( [] )
ws.append( ['Nom produit', 'Code Four.', 'Numéro de Code Barre', 'Qté commande', 'Qté réception', 'Prix unit. initial', 'Prix unit. MAJ', 'Prix total attendu', "Montant erreur livraison (basé sur les différences de quantités)"] )
ws.append( ['Nom produit', 'Code Four.', 'Numéro de Code Barre', 'Qté commande', 'Qté réception', 'Prix unit. initial', 'Prix unit. MAJ', 'Prix total attendu', "Montant erreur livraison (basé sur les différences de prix: (nouveau_prix-ancien_prix)*nouvelle_qté)"] )
if len(data_full) == 0:
ws.append( ['- Aucune modification -'] )
......
......@@ -131,16 +131,25 @@ function edit_event(clicked) {
// Set edition area
function setLineEdition(item) {
var edition_input = $('#edition_input');
editing_item = item;
$('#product_name').text(editing_item.name);
$('#product_uom').text(editing_item.uom_id[1]);
if (editing_item.uom_id[0] == 1) { // Unit
edition_input.attr('type', 'number').attr('step', 1)
.attr('max', 9999);
} else {
edition_input.attr('type', 'number').attr('step', 0.001)
.attr('max', 9999);
}
// If item is reprocessed, set input with value
if (editing_origin == 'processed') {
$('#edition_input').val(editing_item.qty);
edition_input.val(editing_item.qty);
}
$('#edition_input').focus();
edition_input.focus();
// Make edition area blink when edition button clicked
container_edition.classList.add('blink_me');
......
......@@ -354,8 +354,7 @@ var addProductToList = async function(barcode) {
//It could also be a wrong reading one
odoo_product = barcodes.get_corresponding_odoo_product(barcode);
console.log("On vient d'avoir le resultat de l'analyse");
console.log(odoo_product);
if (odoo_product === null) {
alert(barcode + ' : Code-barre inconnu');
} else {
......
......@@ -18,14 +18,15 @@ var listStates = {
unpayed: "Impayé"
};
let limitDate = null;
let dp = null;
let lastY = null;
let loaded_events = [];
let make_up_nb = 0;
let non_regular_shifts = [];
let can_make_exchange = false;
let can_add_shift = false;
var limitDate = null,
dp = null,
lastY = null,
loaded_events = [],
make_up_nb = 0,
non_regular_shifts = [],
can_make_exchange = false,
can_add_shift = false,
extra_info = $('#templates .extra_info').html();
// Init Dom for member's shifts
......@@ -409,6 +410,7 @@ $(document).ready(function() {
if (dataPartner.in_ftop_team == "True") {
$('div.intro div h2').text("Bienvenue dans le système de choix et d'échange de services");
$('.additionnal_intro_data').text(' ou en choisir un nouveau');
var partnerData = "Je suis en statut <span class=\"status\">" + listStates[dataPartner.cooperative_state]+ "</span>.<br>";
/** TODO : Change code to use a parameter to know if following assertion has to be shown or not
partnerData += "Je suis volant.e et j'ai <strong>"+ dataPartner.final_ftop_point +" point";
......@@ -418,6 +420,9 @@ $(document).ready(function() {
partnerData += "</strong>. "
**/
if (typeof extra_info !== "undefined" && extra_info.length > 0) {
partnerData += '<br/>' + extra_info;
}
$('#partnerData').html(partnerData);
} else {
$('.additionnal_intro_data').text(' ou choisir un rattrapage');
......@@ -436,6 +441,9 @@ $(document).ready(function() {
} else {
$('#partnerData').append("<br> Je suis en statut <span class=\"status\">"+ listStates[dataPartner.cooperative_state] +"</span>.<br />");
}
if (typeof extra_info !== "undefined" && extra_info.length > 0) {
$('#partnerData').append("<br/>" + extra_info);
}
}
if (dataPartner.is_leave == "True") {
......
......@@ -52,7 +52,7 @@ def home(request, partner_id, hashed_date):
if partnerData['cooperative_state'] in state_shift_allowed:
# domain = "127.0.0.1"
domain = 'lacagette-coop.fr'
domain = getattr(settings, 'EMAIL_DOMAIN', 'lacagette-coop.fr')
days_to_hide = "0"
if hasattr(settings, 'SHIFT_EXCHANGE_DAYS_TO_HIDE'):
days_to_hide = settings.SHIFT_EXCHANGE_DAYS_TO_HIDE
......@@ -61,6 +61,7 @@ def home(request, partner_id, hashed_date):
'SHIFT_INFO': settings.SHIFT_INFO,
'PB_INSTRUCTIONS': settings.PB_INSTRUCTIONS,
'domain': domain}
context['ADDITIONAL_INFO_SHIFT_PAGE'] = getattr(settings, 'ADDITIONAL_INFO_SHIFT_PAGE', '')
if hasattr(settings, 'CALENDAR_NO_MORE_LINK'):
if settings.CALENDAR_NO_MORE_LINK is True:
context['calendarEventNoMoreLinks'] = True
......@@ -83,6 +84,24 @@ def home(request, partner_id, hashed_date):
return HttpResponseForbidden()
def _is_middled_filled_considered(reserved, max):
"""Added to fit with new LaCagette need. (based on num rather than %)."""
answer = False
toggle_num = 0
try:
toggle_num = int(getattr(settings, 'SHIFT_COLOR_TOGGLE_NUM', 0))
except:
coop_logger.warning("Wrong value for SHIFT_COLOR_TOGGLE_NUM : %s",
str(getattr(settings, 'SHIFT_COLOR_TOGGLE_NUM', 0))
)
if toggle_num == 0:
if int(reserved) / int(max) < 0.5:
answer = True
else:
answer = int(reserved) <= toggle_num
return answer
def get_list_shift_calendar(request, partner_id):
cs = CagetteShift()
registerPartner = cs.get_shift_partner(partner_id)
......@@ -122,7 +141,7 @@ def get_list_shift_calendar(request, partner_id):
elif int(value['seats_reserved']) == 0:
event["className"] = "shift_empty"
event["changed"] = True
elif int(value['seats_reserved']) / smax < 0.5:
elif _is_middled_filled_considered(value['seats_reserved'], smax) is True:
event["className"] = "shift_less_alf"
event["changed"] = True
else:
......
from django.db import models
from django.conf import settings
from outils.common_imports import *
from outils.common import OdooAPI
from decimal import *
from datetime import datetime
class CagetteStock(models.Model):
@staticmethod
def do_stock_movement(stock_movement_data):
"""Do a stock movement : """
TWOPLACES = Decimal(10) ** -2
api = OdooAPI()
errors = []
picking = False
# Set stock movement details according to destination
if stock_movement_data['movement_type'] == 'losses':
picking_name = 'Pertes - ' + datetime.now().strftime("%d/%m/%Y %H:%M:%S")
picking_type = settings.LOSSES_PICKING_TYPE_ID
destination = settings.LOSSES_LOC_ID
elif stock_movement_data['movement_type'] == 'meals':
picking_name = 'Repas Salarié - ' + datetime.now().strftime("%d/%m/%Y %H:%M:%S")
picking_type = settings.MEALS_PICKING_TYPE_ID
destination = settings.MEALS_LOC_ID
elif stock_movement_data['movement_type'] == 'autoconso':
picking_name = 'Autoconsommation - ' + datetime.now().strftime("%d/%m/%Y %H:%M:%S")
picking_type = settings.AUTOCONSO_PICKING_TYPE_ID
destination = settings.AUTOCONSO_LOC_ID
else:
errors.append('Type de mouvement incorrect')
return {'errors': errors, 'picking_id': picking}
fields = {
'company_id': 1,
'name': picking_name,
'picking_type_id' : picking_type, # mouvement type
'location_id': settings.STOCK_LOC_ID, # movement origin
'location_dest_id': destination, # movement dest
"move_lines": [],
"pack_operation_ids": []
}
for p in stock_movement_data['products']:
qty = Decimal(p['qty']).quantize(TWOPLACES)
if qty < 0:
qty = 0
# Add stock.move to stock.picking
fields['move_lines'].append([
0,
False,
{
"date_expected": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"product_id": p['id'],
"name": p['name'],
"product_uom": p['uom_id'],
"product_uom_qty": str(qty),
"picking_type_id": picking_type,
"location_id": settings.STOCK_LOC_ID,
"location_dest_id": destination,
"state": "draft",
"scrapped": False,
}
])
# Add stock.pack.operation to stock.picking
fields['pack_operation_ids'].append([
0,
False,
{
"product_qty": str(qty),
"qty_done": str(qty),
"location_id": settings.STOCK_LOC_ID,
"location_dest_id": destination,
"product_id": p['id'],
"name": p['name'],
"product_uom_id": p['uom_id'],
"state": 'done',
"fresh_record": False
}
])
# Exception rises when odoo method returns nothing
marshal_none_error = 'cannot marshal None unless allow_none is enabled'
try:
picking = api.create('stock.picking', fields)
# Mode access odoo for stock module
class CagetteStock(models.Model):
if not (picking is None):
# Set stock.picking done
api.execute('stock.picking', 'action_done', [picking])
# Generate accounting writings for this picking
api.execute('stock.picking', 'generate_expense_entry', picking)
except Exception as e:
if not (marshal_none_error in str(e)):
coop_logger.error(str(e))
errors.append(str(e))
return {'errors': errors,
'picking_id': picking}
### NOT IN USE ###
def get_liste_supplyer():
......
......@@ -4,41 +4,44 @@ from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^movements', views.movements_page),
url(r'^do_movement', views.do_movement),
### NOT IN USE: ###
url(r'^$', views.listArticleBreaking),
#Order
url(r'^stockOrder', views.stockOrder),
url(r'^get_liste_supplyer', views.get_liste_supplyer),
url(r'^get_list_article_fournisseur/(?P<four_id>\d+)/$', views.get_list_article_fournisseur),
# Graph of Sale
# Graph of Sale
url(r'^graphSale', views.graphSale),
url(r'^get_list_sale_breaking/(?P<product_id>\d+)$', views.get_list_sale_breaking),
# lien of Liste Articles Breaking
url(r'^listArticleBreaking', views.listArticleBreaking),
url(r'^get_list_article_breaking', views.get_list_article_breaking),
# lien of commande
url(r'^commandLacagette', views.commandLacagette),
# lien of breaking Article Set
url(r'^breakingArticleSet', views.breakingArticleSet),
url(r'^get_list_article', views.get_list_article),
url(r'^get_article_byBarcode', views.get_article_byBarcode),
url(r'^set_rupture', views.set_rupture),
# lien of last Sale of Articles
url(r'^stockQuantLastSale', views.stockQuantLastSale),
url(r'^get_list_date_last_sale', views.get_list_date_last_sale),
url(r'^set_archive', views.set_archive),
url(r'^set_dontPurchase', views.set_dontPurchase),
# Lien saleWithNotSale
url(r'^saleWithNotSale', views.saleWithNotSale),
url(r'^get_saleWitheNotSale', views.get_saleWitheNotSale),
url(r'^get_test', views.get_test),
]
from outils.common_imports import *
from outils.for_view_imports import *
from datetime import date, time, datetime, timedelta
from django.views.generic import View
from django.conf import settings
import timeit
from .models import CagetteStock
from inventory.models import CagetteInventory
from django.shortcuts import render
from .models import CagetteStock
def movements_page(request):
"""Page de selection de produits pour créer des mouvements de stock"""
context = {
'title': 'Mouvements de stock'
}
template = loader.get_template('stock/stock_movements.html')
return HttpResponse(template.render(context, request))
from django.shortcuts import render
def do_movement(request):
"""Do the stock movement: losses, self conso or stock correction"""
res = {}
data = json.loads(request.body.decode())
if data['movement_type'] == 'stock_correction':
products = []
for p in data['products']:
products.append({
'id': p['id'],
'uom_id': [p['uom_id']], # to be compatible with products['uom_id'][0]
'qty': p['qty']
})
inventory_data = {
'name': 'Correction de stocks - ' + datetime.now().strftime("%d/%m/%Y %H:%M:%S"),
'products': products
}
res = CagetteInventory.update_stock_with_shelf_inventory_data(inventory_data)
else:
res = CagetteStock.do_stock_movement(data)
if 'errors' in res and res['errors']:
return JsonResponse(res, status=500)
else:
return JsonResponse({'res': res})
### NOT IN USE ###
# ??? a voir si on garde les heur d'ouverture de la cagette ici
listWeekOpenHour = [[14,21],[8,21],[8,21],[8,21],[8,21],[8,21],[0,0]]
# La Cagette Order -------------------------------------------------
nbWeek = 4
now = datetime.combine(date.today(), datetime.min.time())
startDate = now - timedelta(weeks=nbWeek) + timedelta(days=1)
endDate = now
# Order module with breaking of article
def stockOrder(request):
"""
......
......@@ -5,6 +5,7 @@
<link rel="stylesheet" href="{% static 'css/datatables/datatables.min.css' %}">
<link rel="stylesheet" href="{% static 'css/datatables/responsive.min.css' %}">
<link rel="stylesheet" href="{% static 'css/shelfs.css' %}">
<link rel="stylesheet" href="{% static 'css/inventory.css' %}">
<link rel="stylesheet" href="{% static 'jstree/themes/default/style.min.css' %}">
{% endblock %}
......
<fieldset id="sex">
<input id="m_sex" type="radio" value="m" name="sex" /> M.
<input id="f_sex" type="radio" value="f" name="sex" /> Mme.
<input id="o_sex" type="radio" value="o" name="sex" /> Autre
<input type="radio" value="m" name="sex"><label> M.</label>
<input type="radio" value="f" name="sex"><label> Mme.</label>
<input type="radio" value="o" name="sex"><label> Autre</label>
</fieldset>
......@@ -106,7 +106,7 @@
<div class="oddeven_selector fl">
<input type="checkbox" class="switch" id="even_weeks" value='0' checked><label for="even_weeks">Paires</label>
<input type="checkbox" class="switch" id="odd_weeks" value='1' checked><label for="odd_weeks">Impaires</label>
</div>
<div class="selected_weeks fl">
<input type="checkbox" class="switch" id="dw1" value='1' checked><label for="c1">A</label>
......@@ -116,8 +116,8 @@
</div>
</div>
{% if type and type == 1 %}
<div class="clearfix mtop25">Le trait <span class="alert">rouge</span> signale que le créneau est rempli à moins de 75%</div>
{% endif %}
<!--<div class="clearfix mtop25">Le trait <span class="alert">rouge</span> signale que le créneau est rempli à moins de 75%</div>-->
{% endif %}
</div>
</section>
\ No newline at end of file
</section>
\ No newline at end of file
......@@ -43,7 +43,7 @@
</div>
</div>
{% if type and type == 1 %}
<div class="clearfix mtop25">Le trait <span class="alert">rouge</span> signale que le créneau est rempli à moins de 75%</div>
<!--<div class="clearfix mtop25">Le trait <span class="alert">rouge</span> signale que le créneau est rempli à moins de 75%</div>-->
{% endif %}
</div>
......
......@@ -23,7 +23,7 @@
<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"/>
{% if ask_for_second_phone %}
<input type="tel" name="phone" placeholder="Tél. autre" 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="^(\+\d{1,3}(-| ))?\d{1,2}(\.| )\d{1,2}(\.| )\d{1,2}(\.| )\d{1,2}(\.| )\d{0,2}?$" autocomplete="address-level4"/>
{% endif %}
</span>
</p>
......
......@@ -91,7 +91,7 @@
<div id="dp"></div>
</div>
<div id="templates" style="display:none;">
<div class="extra_info">{{ADDITIONAL_INFO_SHIFT_PAGE|safe}}</div>
</div>
<script src="{% static "js/all_common.js" %}?v="></script>
<script src="{% static "js/shift_exchange.js" %}?v="></script>
......
......@@ -2,12 +2,12 @@
{% load static %}
{% block additionnal_css %}
<link rel="stylesheet" href="{% static 'css/jquery.dataTables.css' %}">
<link rel="stylesheet" href="{% static 'css/datatables/jquery.dataTables.css' %}">
<link rel="stylesheet" href="{% static 'css/reception_style.css' %}">
{% endblock %}
{% block additionnal_scripts %}
<script type="text/javascript" src="{% static 'js/jquery.dataTables.min.js' %}"></script>
<script type="text/javascript" src="{% static 'js/datatables/jquery.dataTables.min.js' %}"></script>
<script type="text/javascript" src="{% static 'js/reception_index.js' %}"></script>
{% endblock %}
......
......@@ -2,12 +2,12 @@
{% load static %}
{% block additionnal_css %}
<link rel="stylesheet" href="{% static 'css/jquery.dataTables.css' %}">
<link rel="stylesheet" href="{% static 'css/datatables/jquery.dataTables.css' %}">
<link rel="stylesheet" href="{% static 'css/stock_rupture.css' %}">
{% endblock %}
{% block additionnal_scripts %}
<script type="text/javascript" src="{% static 'js/jquery.dataTables.min.js' %}"></script>
<script type="text/javascript" src="{% static 'js/datatables/jquery.dataTables.min.js' %}"></script>
<script type="text/javascript" src="{% static 'js/stock_rupture.js' %}"></script>
{% endblock %}
......@@ -17,7 +17,7 @@
<br>
<div class="main">
<div class="search">
<div class="searchLabel" >Recherche Articles : </div>
<div class="searchInput">
......@@ -31,7 +31,7 @@
</div>
<br/>
<br/>
<script src="{% static "js/all_common.js" %}?v="></script>
<script src="{% static "js/common.js" %}?v="></script>
{% endblock %}
......@@ -2,12 +2,12 @@
{% load static %}
{% block additionnal_css %}
<link rel="stylesheet" href="{% static 'css/jquery.dataTables.css' %}">
<link rel="stylesheet" href="{% static 'css/datatables/jquery.dataTables.css' %}">
{% endblock %}
{% block additionnal_scripts %}
<script type="text/javascript" src="{% static 'js/jquery.dataTables.min.js' %}"></script>
<script type="text/javascript" src="{% static 'js/datatables/jquery.dataTables.min.js' %}"></script>
<script type="text/javascript" src="{% static 'js/stock_listArticleBreaking.js' %}"></script>
{% endblock %}
......@@ -16,12 +16,12 @@
<br>
<div class="main">
<table id="tableArticle" class="display" width="95%" cellspacing="0" ></table>
</div>
<br/>
<br/>
<script src="{% static "js/all_common.js" %}?v="></script>
<script src="{% static "js/common.js" %}?v="></script>
{% endblock %}
......
......@@ -2,13 +2,13 @@
{% load static %}
{% block additionnal_css %}
<link rel="stylesheet" href="{% static 'css/jquery.dataTables.css' %}">
<link rel="stylesheet" href="{% static 'css/datatables/jquery.dataTables.css' %}">
<link rel="stylesheet" href="{% static 'css/easy-autocomplete.min.css' %}">
<link rel="stylesheet" href="{% static 'css/stock_order.css' %}">
{% endblock %}
{% block additionnal_scripts %}
<script type="text/javascript" src="{% static 'js/jquery.dataTables.min.js' %}"></script>
<script type="text/javascript" src="{% static 'js/datatables/jquery.dataTables.min.js' %}"></script>
<script type="text/javascript" src="{% static 'js/jquery.easy-autocomplete.min.js' %}"></script>
<script type="text/javascript" src="{% static 'js/stock_order.js' %}"></script>
{% endblock %}
......@@ -41,9 +41,9 @@
</div>
<br/>
<br/>
<script src="{% static "js/all_common.js" %}?v="></script>
<script src="{% static "js/common.js" %}?v="></script>
{% endblock %}
......
......@@ -2,12 +2,12 @@
{% load static %}
{% block additionnal_css %}
<link rel="stylesheet" href="{% static 'css/jquery.dataTables.css' %}">
<link rel="stylesheet" href="{% static 'css/datatables/jquery.dataTables.css' %}">
<link rel="stylesheet" href="{% static 'css/stock_listArticleBreaking.css' %}">
{% endblock %}
{% block additionnal_scripts %}
<script type="text/javascript" src="{% static 'js/jquery.dataTables.min.js' %}"></script>
<script type="text/javascript" src="{% static 'js/datatables/jquery.dataTables.min.js' %}"></script>
<script type="text/javascript" src="{% static 'js/stock_saleWithNotSale.js' %}"></script>
{% endblock %}
......@@ -17,12 +17,12 @@
<br>
<div class="main">
<table id="tableArticle" class="display" width="95%" cellspacing="0" ></table>
</div>
<br/>
<br/>
<script src="{% static "js/all_common.js" %}?v="></script>
<script src="{% static "js/common.js" %}?v="></script>
{% endblock %}
......@@ -2,12 +2,12 @@
{% load static %}
{% block additionnal_css %}
<link rel="stylesheet" href="{% static 'css/jquery.dataTables.css' %}">
<link rel="stylesheet" href="{% static 'css/datatables/jquery.dataTables.css' %}">
<link rel="stylesheet" href="{% static 'css/stock_listArticleBreaking.css' %}">
{% endblock %}
{% block additionnal_scripts %}
<script type="text/javascript" src="{% static 'js/jquery.dataTables.min.js' %}"></script>
<script type="text/javascript" src="{% static 'js/datatables/jquery.dataTables.min.js' %}"></script>
<script type="text/javascript" src="{% static 'js/stock_stock_stockQuantLastSale.js' %}"></script>
{% endblock %}
......@@ -17,12 +17,12 @@
<br>
<div class="main">
<table id="tableArticle" class="display" width="95%" cellspacing="0" ></table>
</div>
<br/>
<br/>
<script src="{% static "js/all_common.js" %}?v="></script>
<script src="{% static "js/common.js" %}?v="></script>
{% endblock %}
......@@ -90,10 +90,10 @@
<input type="email" name="email" value="{{ data.email|default:'' }}" placeholder="Email" size="80" disabled>
</p>
<p>
<input type="text" name="mobile" value="{{ data.mobile|default:'' }}" placeholder="Tel1" size="15">
<input type="text" name="mobile" value="{{ data.mobile|default:'' }}" placeholder="Tél. mobile" size="15">
</p>
<p>
<input type="text" name="phone" value="{{ data.phone|default:'' }}" placeholder="Tel2" size="15">
<input type="text" name="phone" value="{{ data.phone|default:'' }}" placeholder="Tél. fixe" size="15">
</p>
......
......@@ -28,7 +28,7 @@ try {
var end_year = new Date(now.setYear(now.getFullYear() - 15)).getFullYear();
for (var i=100; i>0; i--) {
var opt = $('<option>').val(end_year-i)
let opt = $('<option>').val(end_year-i)
.text(end_year-i);
if (end_year-i == y) opt.prop('selected', true);
......@@ -36,6 +36,7 @@ try {
}
for (var k=1; k<=12; k++) {
let mth = k.pad(2);
let opt = $('<option>').val(mth)
.text(mth);
......@@ -44,6 +45,7 @@ try {
}
for (var l=1; l<=31; l++) {
let day = l.pad(2);
let opt = $('<option>').val(day)
.text(day);
......@@ -105,11 +107,11 @@ try {
};
if (typeof original.sex != "undefined") {
$("#" + original.sex + "_sex").prop('checked', true);
$('input[name="sex"][value="' + original.sex + '"]').prop('checked', true);
}
init_birthdate_selects();
$('input').keyup(make_save_button_active);
$('input').change(make_save_button_active);
save_btn.click(function() {
if ($(this).hasClass('btn--primary') && is_time_to('save_perso_data')) {
......
......@@ -91,6 +91,7 @@ def index(request):
# Following part is a copy of shifts/views.py (def home)
context['partnerData'] = partnerData
days_to_hide = "0"
context['ADDITIONAL_INFO_SHIFT_PAGE'] = getattr(settings, 'ADDITIONAL_INFO_SHIFT_PAGE', '')
if hasattr(settings, 'SHIFT_EXCHANGE_DAYS_TO_HIDE'):
days_to_hide = settings.SHIFT_EXCHANGE_DAYS_TO_HIDE
context['daysToHide'] = days_to_hide
......
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