Commit adf52d03 by François C.

Cooperatic internal repository modifications to integrate before this repository to close

parent b4d49b83
Pipeline #769 failed with stage
in 11 seconds
"""Company specific data values."""
EMAIL_DOMAIN = 'lacagette-coop.fr'
OPEN_ON_SUNDAY = True
CAP_JOURNAL_ID = 9
CAP_APPELE_NON_VERSE_ACCOUNT_ID = 529
CAP_APPELE_VERSE_ACCOUNT_ID = 8
CAP_INVOICE_LINE_ACCOUNT_ID = 8
FUNDRAISING_CAT_ID = 1
UNITE_UOM_ID = 1
PARTS_A_PRODUCT_ID = 1008
PARTS_A_PRICE_UNIT = 10.0
COOP_BARCODE_RULE_ID = 11
CHECK_PAYMENT_ID = 8
CASH_PAYMENT_ID = 18
STOCK_LOC_ID = 12
CATEG_FRUIT = 151
CATEG_LEGUME = 152
FLV_CSV_NB = 4
COEFF_MAG_ID = 1
LOSSES_LOC_ID = 33
LOSSES_PICKING_TYPE_ID = 10
AUTOCONSO_LOC_ID = 27
AUTOCONSO_PICKING_TYPE_ID = 7
MEALS_LOC_ID = 36
MEALS_PICKING_TYPE_ID = 16
DEFAULT_MAX_TIMESLOT_CARTS = 5
MIN_DELAY_FOR_SLOT = 4
HOURS_FOR_VALIDATION_SHOP = 3
CART_VALIDATION_BOTTOM_MSG = "Pour des raisons d'hygiène les commandes seront préparées dans des sacs en papier kraft qui vous seront facturées, 0,24€ pour les petits et 0,77€ pour les grands. Merci de votre compréhension"
SUBSCRIPTION_PAYMENT_MEANINGS = [{'code': 'cash', 'title': 'Espèces', 'journal_id': CASH_PAYMENT_ID},
{'code': 'ch', 'title': 'Chèque', 'journal_id': CHECK_PAYMENT_ID}]
EM_URL = ''
RECEPTION_MERGE_ORDERS_PSWD = 'jpsrcp'
DAV_PATH = '/data/dav/cagette'
TOOLS_SERVER = 'https://outils.lacagette-coop.fr'
ADMIN_IDS = [13]
BRINKS_MUST_IDENTIFY = False
SHOP_CAN_BUY = False
# SHOP_OPENING = {'mar.': [{'start': '10:30', 'end': '14:00'}, {'start': '15:30', 'end': '20:00'}],
# 'mer.': [{'start': '10:30', 'end': '14:00'}, {'start': '15:30', 'end': '20:00'}],
# 'jeu.': [{'start': '10:30', 'end': '14:00'}, {'start': '15:30', 'end': '20:00'}],
# 'ven.': [{'start': '10:30', 'end': '14:00'}, {'start': '15:30', 'end': '20:00'}],
# 'sam.': [{'start': '10:30', 'end': '14:00'}, {'start': '15:30', 'end': '20:00'}]
# }
SHOP_OPENING = {}
SHOP_OPENING_START_DATE = '2020-06-02'
SHOP_SLOT_SIZE = 30 # minutes
SHOP_CATEGORIES = {'epicerie': {'id': 75, 'label': 'Epicerie'},
'liquide': {'id': 96, 'label': 'Liquides'},
'produits_frais': {'id': 104, 'label': 'Frais'},
'surgeles': {'id': 115, 'label': 'Surgelés'},
'bazar': {'id': 122, 'label': 'Bazar'},
'droguerie': {'id': 127, 'label': 'Droguerie Hygiène'},
'parfumerie': {'id': 133, 'label': 'Parfumerie'}}
SHOP_EXTRA_MENUS = ['shop/planning_livraison_pains.html', 'shop/combien_ca_pese.html']
SHOP_SURVEY_LINK = 'https://docs.google.com/forms/d/e/1FAIpQLSczl0mMRwx3s9LbUSPYwwFTiiRa6agx7YkQM9cL41eiQnXNUw/viewform'
EXCLUDE_SHOP_CATEGORIES = [108]
PROMOTE_SHELFS_IDS = [68]
DISCOUNT_SHELFS_IDS = [74]
FL_SHELFS = [16, 17, 18]
VRAC_SHELFS = [20, 38]
SHIFT_EXCHANGE_DAYS_TO_HIDE = ''
ENTRANCE_COME_FOR_SHOPING_MSG = "Hey coucou toi ! Cet été nous sommes plus de <strong>1000 acheteur·euses</strong> pour seulement <strong>300 coopérateur·rice·s</strong> en service. <br />Tu fais tes courses à La Cagette cet été ?<br/> Inscris-toi sur ton espace membre !"
# Members space / shifts
UNSUBSCRIBED_MSG = 'Vous êtes désincrit·e, merci de remplir <a href="https://docs.google.com/forms/d/e/1FAIpQLSfPiC2PkSem9x_B5M7LKpoFNLDIz0k0V5I2W3Mra9AnqnQunw/viewform">ce formulaire</a> pour vous réinscrire sur un créneau.<br />Vous pouvez également contacter le Bureau des Membres en remplissant <a href="https://docs.google.com/forms/d/e/1FAIpQLSeZP0m5-EXPVJxEKJk6EjwSyZJtnbiGdYDuAeFI3ENsHAOikg/viewform">ce formulaire</a>'
"""Company specific data values."""
EMAIL_DOMAIN = 'lesgrainsdesel.fr'
OPEN_ON_SUNDAY = True
MAG_NAME = ''
OFFICE_NAME = ''
COMPANY_NAME = 'Les Grains de Sel'
MAX_BEGIN_HOUR = '19:00'
WELCOME_ENTRANCE_MSG = 'Bienvenue aux Grains de Sel!'
CAP_JOURNAL_ID = 9
CAP_APPELE_NON_VERSE_ACCOUNT_ID = 529
CAP_APPELE_VERSE_ACCOUNT_ID = 8
CAP_INVOICE_LINE_ACCOUNT_ID = 7
FUNDRAISING_CAT_ID = 1
UNITE_UOM_ID = 1
PARTS_A_PRODUCT_ID = 5
PARTS_B_PRODUCT_ID = 6
PARTS_C_PRODUCT_ID = 7
PARTS_PRICE_UNIT = 10.0
PARTS_A_PRICE_UNIT = PARTS_PRICE_UNIT
COOP_BARCODE_RULE_ID = 55
CHECK_PAYMENT_ID = 8
VIREMENT_PAYMENT_ID = 16
CASH_PAYMENT_ID = 18
CB_PAYMENT_ID = 15
PARR_PAYMENT_ID = 29
STOCK_LOC_ID = 12
LOSSES_LOC_ID = 33
LOSSES_PICKING_TYPE_ID = 10
AUTOCONSO_LOC_ID = 27
AUTOCONSO_PICKING_TYPE_ID = 8
MEALS_LOC_ID = 36
MEALS_PICKING_TYPE_ID = 17
CATEG_FRUIT = 189
CATEG_LEGUME = 189
VRAC_CATEGS = [197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207]
EXPORT_POS_CAT_FOR_SCALES = True
SHELF_LABELS_ADD_FIELDS = ['code', 'category_print_id', 'base_price', 'categ_id', 'country_id', 'label_ids', 'uom_id', 'suppliers']
FLV_CSV_NB = 6
COEFF_MAG_ID = 1
RECEPTION_PDT_LABELS_FN = 'print_product_labels()'
RECEPTION_PDT_LABELS_TEXT = 'Cliquez sur ce bouton pour imprimer les étiquettes code-barres à coller sur les produits'
RECEPTION_PDT_LABELS_BTN_TEXT = 'Lancer l\'impression'
RECEPTION_SHELF_LABEL_PRINT = True
FIXED_BARCODE_PREFIX = '0499'
RECEPTION_ADD_ADMIN_MODE = True
RECEPTION_ADD_ALL_LEFT_IS_GOOD = True
SUBSCRIPTION_PAYMENT_MEANINGS = [
{'code': 'cash', 'title': 'Espèces', 'journal_id': CASH_PAYMENT_ID},
{'code': 'ch', 'title': 'Chèque', 'journal_id': CHECK_PAYMENT_ID},
{'code': 'cb', 'title': 'Carte bancaire', 'journal_id': CB_PAYMENT_ID},
{'code': 'vir', 'title': 'Virement', 'journal_id': VIREMENT_PAYMENT_ID},
{'code': 'parr', 'title': 'Parrainage', 'journal_id': PARR_PAYMENT_ID}
]
SUBSCRIPTION_INPUT_BARCODE = True
SUBSCRIPTION_NAME_SEP = ', '
CONCAT_NAME_ORDER = 'LF'
SUBSCRIPTION_ASK_FOR_SEX = True
WITH_WEBSITE_MENU = True
SUBSCRIPTION_ADD_STREET2 = True
SUBSCRIPTION_ADD_SECOND_PHONE = True
FORCE_HYPHEN_IN_SUBSCRIPTION_FIRSTNAME = False
SHIFT_EXCHANGE_DAYS_TO_HIDE = ''
CALENDAR_NO_MORE_LINK = True
SHIFT_INFO = """Aux Grains de Sel, un service est une plage horaire un jour en particulier, par exemple : le mardi 25/09/2018 à 13h15.
<br />A l'inverse, un créneau est une plage horaire régulière, par exemple, tous les mardi de semaine A à 13h15."""
PB_INSTRUCTIONS = """Si j'ai un problème, que je suis désinscrit, que je veux changer de créneaux ou quoi que ce soit, merci de contacter le Bureau Des Membres"""
ADMINS = ['francois@cooperatic.fr']
BRINKS_MUST_IDENTIFY = True
ENTRANCE_FTOP_BUTTON_DISPLAY = False
CUSTOM_CSS_FILES = {'all': ['common_lgds.css'],
'members': ['inscription_lgds.css']}
"""Company specific data values."""
"""Odoo coop specific constants ."""
EMAIL_DOMAIN = 'supercafoutch.fr'
CAP_JOURNAL_ID = 9
CAP_APPELE_NON_VERSE_ACCOUNT_ID = 529
CAP_APPELE_VERSE_ACCOUNT_ID = 8
CAP_INVOICE_LINE_ACCOUNT_ID = 7
FUNDRAISING_CAT_ID = 1
UNITE_UOM_ID = 1
PARTS_A_PRODUCT_ID = 5
PARTS_B_PRODUCT_ID = 6
PARTS_C_PRODUCT_ID = 7
PARTS_PRICE_UNIT = 10.0
PARTS_A_PRICE_UNIT = PARTS_PRICE_UNIT
EXPORT_COMPTA_FORMAT = 'Quadratus'
COOP_BARCODE_RULE_ID = 11
CHECK_PAYMENT_ID = 8
VIREMENT_PAYMENT_ID = 16
CASH_PAYMENT_ID = 18
CB_PAYMENT_ID = 15
HELLO_ASSO_PAYMENT_ID = 29
SUMUP_PAYMENT_ID = 30
STOCK_LOC_ID = 12
CATEG_FRUIT = 151
CATEG_LEGUME = 152
VRAC_CATEGS = [166, 167, 174, 179]
FLV_CSV_NB = 2
COEFF_MAG_ID = 1
RECEPTION_PDT_LABELS_FN = 'print_product_labels()'
RECEPTION_PDT_LABELS_TEXT = 'Cliquez sur ce bouton pour imprimer les étiquettes code-barres à coller sur les produits'
RECEPTION_PDT_LABELS_BTN_TEXT = 'Lancer l\'impression'
RECEPTION_SHELF_LABEL_PRINT = False
FIXED_BARCODE_PREFIX = '0491'
SUBSCRIPTION_PAYMENT_MEANINGS = [
{'code': 'cash', 'title': 'Espèces', 'journal_id': CASH_PAYMENT_ID},
{'code': 'ch', 'title': 'Chèque', 'journal_id': CHECK_PAYMENT_ID},
{'code': 'cb', 'title': 'Carte bancaire', 'journal_id': CB_PAYMENT_ID},
{'code': 'vir', 'title': 'Virement', 'journal_id': VIREMENT_PAYMENT_ID},
{'code': 'hel', 'title': 'HelloAsso', 'journal_id': HELLO_ASSO_PAYMENT_ID}
]
SHOP_HEADER_IMG = 'https://supercafoutch.fr/wp-content/uploads/2018/02/SC-logo-4-invert@1x.png'
SHOP_OPENING = {'jeu.': [{'start': '15:45', 'end': '18:15'}, {'start': '18:30', 'end': '21:00'}],
'ven.': [{'start': '15:45', 'end': '18:15'}, {'start': '18:30', 'end': '21:00'}],
'sam.': [{'start': '10:15', 'end': '12:45'}, {'start': '13:00', 'end': '15:30'}]}
SHOP_SLOT_SIZE = 15 # minutes
SHOP_CATEGORIES = {
'epicerie': {'id': 75, 'label': 'Epicerie'},
'liquide': {'id': 96, 'label': 'Liquides'},
'produits_frais': {'id': 104, 'label': 'Frais'},
'surgeles': {'id': 115, 'label': 'Surgelés'},
'bazar': {'id': 122, 'label': 'Bazar'},
'droguerie': {'id': 127, 'label': 'Droguerie Hygiène'},
'parfumerie': {'id': 133, 'label': 'Parfumerie'}
}
DELIVERY_CAN_BUY = True
EXCLUDE_SHOP_CATEGORIES=[]
DEFAULT_MAX_TIMESLOT_CARTS = 1
MIN_DELAY_FOR_SLOT = 0
HOURS_FOR_VALIDATION_SHOP = 2
SHOW_SUBSTITUTION_OPTION = False
CART_VALIDATION_BOTTOM_MSG = ""
SHOP_LIMIT_PRODUCTS = ['relatively_available', 'no_shelf']
VALIDATION_ORDER_MAIL_TEMPLATE = 'shop/supercafoutch_validation_mail.html'
ADMINS = ['francois@cooperatic.fr']
BRINKS_MUST_IDENTIFY = True
PROMOTE_SHELFS_IDS = []
DISCOUNT_SHELFS_IDS = []
FL_SHELFS = []
VRAC_SHELFS = []
...@@ -105,6 +105,14 @@ class CagetteInventory(models.Model): ...@@ -105,6 +105,14 @@ class CagetteInventory(models.Model):
return res return res
@staticmethod @staticmethod
def get_custom_list_inv_status(list_id):
file_data = {}
with open(custom_list_file_path + list_id + '.json') as json_file:
file_data = json.load(json_file)
return file_data['inventory_status']
@staticmethod
def create_custom_inv_file(line_ids, line_type): def create_custom_inv_file(line_ids, line_type):
res = {} res = {}
...@@ -153,7 +161,7 @@ class CagetteInventory(models.Model): ...@@ -153,7 +161,7 @@ class CagetteInventory(models.Model):
'products': ids 'products': ids
} }
# Crate inventory file, name is timestamp of creation # Create inventory file, name is timestamp of creation
timestamp = int(time.time()) timestamp = int(time.time())
filename = custom_list_file_path + str(timestamp) + '.json' filename = custom_list_file_path + str(timestamp) + '.json'
with open(filename, 'w+') as outfile: with open(filename, 'w+') as outfile:
......
...@@ -12,7 +12,7 @@ function init_datatable() { ...@@ -12,7 +12,7 @@ function init_datatable() {
{ {
data:"datetime_created", data:"datetime_created",
title:"Liste", title:"Liste",
render: function (data, type, full, meta) { render: function (data) {
return "Liste du " + data; return "Liste du " + data;
} }
}, },
...@@ -31,7 +31,7 @@ function init_datatable() { ...@@ -31,7 +31,7 @@ function init_datatable() {
title:"Inventaire à faire", title:"Inventaire à faire",
className:"dt-body-center", className:"dt-body-center",
width: "15%", width: "15%",
render: function (data, type, full, meta) { render: function (data) {
if (data == '') if (data == '')
return "<button class='btn--primary do_inventory'>Inventaire en rayon</button>"; return "<button class='btn--primary do_inventory'>Inventaire en rayon</button>";
else else
......
...@@ -9,6 +9,7 @@ urlpatterns = [ ...@@ -9,6 +9,7 @@ urlpatterns = [
url(r'^delete_custom_list$', views.delete_custom_list), url(r'^delete_custom_list$', views.delete_custom_list),
url(r'^custom_list_inventory/([0-9]+)$', views.custom_list_inventory), url(r'^custom_list_inventory/([0-9]+)$', views.custom_list_inventory),
url(r'^get_custom_list_data$', views.get_custom_list_data), url(r'^get_custom_list_data$', views.get_custom_list_data),
url(r'^inventory_process_state/([0-9]+)$', views.inventory_process_state),
url(r'^do_custom_list_inventory$', views.do_custom_list_inventory), url(r'^do_custom_list_inventory$', views.do_custom_list_inventory),
url(r'^generate_inventory_list$', views.generate_inventory_list), url(r'^generate_inventory_list$', views.generate_inventory_list),
url(r'^get_credentials$', views.get_credentials), url(r'^get_credentials$', views.get_credentials),
......
...@@ -64,6 +64,17 @@ def get_custom_list_data(request): ...@@ -64,6 +64,17 @@ def get_custom_list_data(request):
except Exception as e: except Exception as e:
return JsonResponse(id, status=500) return JsonResponse(id, status=500)
def inventory_process_state(request, list_id):
res = {}
try:
res['state'] = CagetteInventory.get_custom_list_inv_status(list_id)
except Exception as e:
# Exception raised: no file found ; inventory is done
res['state'] = 'done'
return JsonResponse({'res': res})
def do_custom_list_inventory(request): def do_custom_list_inventory(request):
res = {} res = {}
data = json.loads(request.body.decode()) data = json.loads(request.body.decode())
...@@ -97,7 +108,7 @@ def do_custom_list_inventory(request): ...@@ -97,7 +108,7 @@ def do_custom_list_inventory(request):
else: else:
return JsonResponse({'res': res}) return JsonResponse({'res': res})
@csrf_exempt @csrf_exempt
def generate_inventory_list(request): def generate_inventory_list(request):
"""Responding to Odoo ajax call (no csrf).""" """Responding to Odoo ajax call (no csrf)."""
res = {} res = {}
......
...@@ -986,8 +986,18 @@ class CagetteServices(models.Model): ...@@ -986,8 +986,18 @@ class CagetteServices(models.Model):
'seats_available'] 'seats_available']
c = [['active', '=', True]] c = [['active', '=', True]]
shift_templates = api.search_read('shift.template', c, f) shift_templates = api.search_read('shift.template', c, f)
# Get count of active registrations for each shift template
# shift_templates_active_count = api.execute('lacagette_shifts', 'get_active_shifts', [])
# With LGDS tests, seats_reserved reflects better what's shown in Odoo ...
title = re.compile(r"^(\w{1})(\w{3})\. - (\d{2}:\d{2}) ?-? ?(\w*)") title = re.compile(r"^(\w{1})(\w{3})\. - (\d{2}:\d{2}) ?-? ?(\w*)")
for l in shift_templates: for l in shift_templates:
# nb_reserved = 0
# for stac in shift_templates_active_count:
# if stac['shift_template_id'] == l['id']:
# nb_reserved = stac['seats_active_registration']
line = {} line = {}
end = time.strptime(l['end_datetime_tz'], "%Y-%m-%d %H:%M:%S") end = time.strptime(l['end_datetime_tz'], "%Y-%m-%d %H:%M:%S")
end_min = str(end.tm_min) end_min = str(end.tm_min)
...@@ -995,6 +1005,7 @@ class CagetteServices(models.Model): ...@@ -995,6 +1005,7 @@ class CagetteServices(models.Model):
end_min = '00' end_min = '00'
line['end'] = str(end.tm_hour) + ':' + end_min line['end'] = str(end.tm_hour) + ':' + end_min
line['max'] = l['seats_max'] line['max'] = l['seats_max']
# line['reserved'] = nb_reserved
line['reserved'] = l['seats_reserved'] line['reserved'] = l['seats_reserved']
line['week'] = l['week_number'] line['week'] = l['week_number']
line['id'] = l['id'] line['id'] = l['id']
...@@ -1214,4 +1225,3 @@ class CagetteUser(models.Model): ...@@ -1214,4 +1225,3 @@ class CagetteUser(models.Model):
pass pass
return answer return answer
...@@ -212,8 +212,12 @@ function store_new_coop(event) { ...@@ -212,8 +212,12 @@ function store_new_coop(event) {
msex = $('input[name="sex"]:checked').val(); msex = $('input[name="sex"]:checked').val();
} }
if (payment_meaning.val() == 'ch' && ch_qty.val() <1) { if (payment_meaning.val() == 'ch') {
errors.push("Le nombre de chèque est obligatoire."); if (ch_qty.val() <1) {
errors.push("Le nombre de chèque est obligatoire.");
} else if (ch_qty.val() > max_chq_nb) {
errors.push("Le nombre de chèque est trop grand.");
}
} }
$.ajax({url : '/members/exists/' + email, $.ajax({url : '/members/exists/' + email,
......
...@@ -20,7 +20,8 @@ var current_displayed_member = null, ...@@ -20,7 +20,8 @@ var current_displayed_member = null,
loaded_services = null, loaded_services = null,
selected_service = null, selected_service = null,
last_search_time = null, last_search_time = null,
rattrapage_ou_volant = null; rattrapage_ou_volant = null,
timeout_counter = null;
var search_button = $('.btn--primary.search'); var search_button = $('.btn--primary.search');
var loading2 = $('.loading2'); var loading2 = $('.loading2');
var search_field = $('input[name="search_string"]'); var search_field = $('input[name="search_string"]');
...@@ -77,9 +78,10 @@ function fill_member_slide(member) { ...@@ -77,9 +78,10 @@ function fill_member_slide(member) {
html_elts.name.html(member.name); html_elts.name.html(member.name);
var img_src = ''; var img_src = '';
if (typeof member.image_medium.length != "undefined") { if (member.image_medium) {
img_src = 'data:image/'+member.image_extension+';base64,'+member.image_medium; img_src = 'data:image/'+member.image_extension+';base64,'+member.image_medium;
} else { } else {
img_src = "/static/img/pas-de-photo.png";
no_pict_msg.show(); no_pict_msg.show();
} }
html_elts.image_medium.html('<img src="'+img_src+'" width="128" />'); html_elts.image_medium.html('<img src="'+img_src+'" width="128" />');
...@@ -157,8 +159,8 @@ function canSearch() { ...@@ -157,8 +159,8 @@ function canSearch() {
return answer; return answer;
} }
function search_member() { function search_member(force_search = false) {
if (canSearch() == true) { if (canSearch() || force_search) {
html_elts.member_slide.hide(); html_elts.member_slide.hide();
search_box_clear_html_elts(); search_box_clear_html_elts();
...@@ -588,6 +590,13 @@ function goto_page(jquery_page_selected) { ...@@ -588,6 +590,13 @@ function goto_page(jquery_page_selected) {
jquery_page_selected.css('display', 'grid'); jquery_page_selected.css('display', 'grid');
} }
function timeout_to_homepage() {
if (timeout_counter) clearTimeout(timeout_counter);
timeout_counter = setTimeout(function() {
goto_page(pages.first_page);
}, 40000);
}
$('button.search').click(search_member); $('button.search').click(search_member);
search_field.keyup(search_input_listing); search_field.keyup(search_input_listing);
...@@ -657,32 +666,42 @@ html_elts.image_medium.on('click', function() { ...@@ -657,32 +666,42 @@ html_elts.image_medium.on('click', function() {
}); });
$(document).ready(function() { $(document).ready(function() {
var pressed = false;
var chars = []; var chars = [];
//barcode-reader
var shopping_entry_btn = $('a[data-next="shopping_entry"]');
shopping_entry_btn.on('click', function() {
// Always focus on search field
search_field.focus();
// Return to homepage after 40 seconds
timeout_to_homepage();
});
// Force barcode-reader to search member
$(window).keypress(function(e) { $(window).keypress(function(e) {
if (e.which >= 48 && e.which <= 57) { if (e.which >= 48 && e.which <= 57) {
chars.push(String.fromCharCode(e.which)); chars.push(String.fromCharCode(e.which));
} }
if (pressed == false) { timeout_to_homepage();
setTimeout(function() {
if (chars.length >= 13) {
var barcode = chars.join("");
if (!isNaN(barcode)) { setTimeout(function() {
chars = []; if (chars.length >= 13) {
pressed = false; var barcode = chars.join("");
search_member();
}
if (!isNaN(barcode)) {
chars = [];
goto_page(pages.shopping_entry);
search_field.val(barcode);
last_search_time = null;
search_member(true);
} }
}
}, 300); }, 300);
}
pressed = true;
}); });
init_webcam(); init_webcam();
$('#crop_width').change(function() { $('#crop_width').change(function() {
Webcam.reset(); Webcam.reset();
......
...@@ -501,27 +501,33 @@ function coop_problem_delete() { ...@@ -501,27 +501,33 @@ function coop_problem_delete() {
} }
function open_coop_form(e) { function open_coop_form(e) {
if (e) { try {
var clicked = $(this); if (e) {
var clicked = $(this);
coop_target = {};
coop_target.id = clicked.data('id');
coop_target.validation_state = clicked.closest('div.main').attr('id');
} else {
return display_current_coop_form();
}
set_current_coop(coop_target, function() { coop_target = {};
if (coop_target.validation_state == 'to_fill') { coop_target.id = clicked.data('id');
display_current_coop_form(); coop_target.validation_state = clicked.closest('div.main').attr('id');
} else if ((coop_target.validation_state == 'waiting_validation_employee' || coop_target.validation_state == 'waiting_validation_member') && coop_is_connected()) {
display_current_coop_form();
} else if (coop_target.validation_state == 'done') {
ask_for_deletion();
} else { } else {
process_signaler_click(); return display_current_coop_form();
} }
});
set_current_coop(coop_target, function() {
if (coop_target.validation_state == 'to_fill') {
display_current_coop_form();
} else if ((coop_target.validation_state == 'waiting_validation_employee' || coop_target.validation_state == 'waiting_validation_member') && coop_is_connected()) {
display_current_coop_form();
} else if (coop_target.validation_state == 'done') {
ask_for_deletion();
} else {
process_signaler_click();
}
});
} catch (err) {
error = {msg: err.name + ' : ' + err.message, ctx: 'open_coop_form'};
console.error(error);
report_JS_error(error, 'prepa-odoo');
}
} }
function ask_for_deletion() { function ask_for_deletion() {
...@@ -537,56 +543,68 @@ function ask_for_problem_deletion() { ...@@ -537,56 +543,68 @@ function ask_for_problem_deletion() {
openModal(msg, coop_problem_delete, 'Oui'); openModal(msg, coop_problem_delete, 'Oui');
} }
function add_coop_to_box(box, coop) { function add_coop_to_box(box, coop) {
var info = coop.firstname + ' ' + coop.lastname; try {
var info = coop.firstname + ' ' + coop.lastname;
if (coop.odoo_state && coop.odoo_state == 'done') { if (coop.odoo_state && coop.odoo_state == 'done') {
info = coop.barcode_base + ' - ' + info; info = coop.barcode_base + ' - ' + info;
} }
info = $('<span/>').text(info); info = $('<span/>').text(info);
var cbox = $('<div/>').addClass('coop') var cbox = $('<div/>').addClass('coop')
.attr('data-id', coop._id) .attr('data-id', coop._id)
.append(info); .append(info);
// Only connected user can do these actions // Only connected user can do these actions
if ([ if ([
"waiting_validation_employee", "waiting_validation_employee",
"waiting_validation_member", "waiting_validation_member",
"done" "done"
].indexOf(coop.validation_state) > -1 ].indexOf(coop.validation_state) > -1
&& !coop_is_connected()) { && !coop_is_connected()) {
cbox.addClass('coop_no_select'); cbox.addClass('coop_no_select');
}
box.append(cbox);
} catch (e) {
err = {msg: e.name + ' : ' + e.message, ctx: 'add_coop_to_box'};
console.error(err);
report_JS_error(err, 'prepa-odoo');
} }
box.append(cbox);
} }
//Called after having retrieved all coops //Called after having retrieved all coops
function dispatch_coops_in_boxes() { function dispatch_coops_in_boxes() {
$('div.coop').off('click', open_coop_form); try {
to_fill_box.html(''); $('div.coop').off('click', open_coop_form);
with_errors_box.html(''); to_fill_box.html('');
waiting_validation_employee_div.find('.elts').html(''); with_errors_box.html('');
waiting_validation_member_div.find('.elts').html(''); waiting_validation_employee_div.find('.elts').html('');
done_div.find('.elts').html(''); waiting_validation_member_div.find('.elts').html('');
done_div.find('.elts').html('');
$.each(coops.to_fill, function(i, e) {
add_coop_to_box(to_fill_box, e); $.each(coops.to_fill, function(i, e) {
}); add_coop_to_box(to_fill_box, e);
$.each(coops.with_errors, function(i, e) { });
add_coop_to_box(with_errors_box, e); $.each(coops.with_errors, function(i, e) {
}); add_coop_to_box(with_errors_box, e);
$.each(coops.waiting_validation_employee, function(i, e) { });
add_coop_to_box(waiting_validation_employee_div.find('.elts'), e); $.each(coops.waiting_validation_employee, function(i, e) {
}); add_coop_to_box(waiting_validation_employee_div.find('.elts'), e);
$.each(coops.waiting_validation_member, function(i, e) { });
add_coop_to_box(waiting_validation_member_div.find('.elts'), e); $.each(coops.waiting_validation_member, function(i, e) {
}); add_coop_to_box(waiting_validation_member_div.find('.elts'), e);
$.each(coops.done, function(i, e) { });
add_coop_to_box(done_div.find('.elts'), e); $.each(coops.done, function(i, e) {
}); add_coop_to_box(done_div.find('.elts'), e);
});
$('div.coop').on('click', open_coop_form); $('div.coop').on('click', open_coop_form);
$('div.coop_no_select').off('click', open_coop_form); $('div.coop_no_select').off('click', open_coop_form);
} catch (e) {
err = {msg: e.name + ' : ' + e.message, ctx: 'dispatch_coops_in_boxes'};
console.error(err);
report_JS_error(err, 'prepa-odoo');
}
} }
function handle_legacy_states(rows) { function handle_legacy_states(rows) {
...@@ -609,49 +627,55 @@ function handle_legacy_states(rows) { ...@@ -609,49 +627,55 @@ function handle_legacy_states(rows) {
// first function called when loading page // first function called when loading page
function retrieve_all_coops() { function retrieve_all_coops() {
dbc.allDocs({include_docs: true, descending: true}, function(err, resp) { try {
if (err) { dbc.allDocs({include_docs: true, descending: true}, function(err, resp) {
return console.log(err); if (err) {
} return console.log(err);
coops = {'to_fill': [], 'with_errors': [], 'waiting_validation_employee':[], 'waiting_validation_member':[], 'done':[]};
$.each(handle_legacy_states(resp.rows), function(i, e) {
if (e.doc.firstname) {
if (e.doc.errors) {
coops.with_errors.push(e.doc);
} else if (e.doc.validation_state == "to_fill") {
coops.to_fill.push(e.doc);
} else if (e.doc.validation_state == "waiting_validation_employee") {
coops.waiting_validation_employee.push(e.doc);
} else if (e.doc.validation_state == "waiting_validation_member") {
coops.waiting_validation_member.push(e.doc);
} else if (e.doc.validation_state == "done") {
coops.done.push(e.doc);
}
if (e.doc.coop_msg) {
coops.with_errors.push(e.doc);
}
} }
coops = {'to_fill': [], 'with_errors': [], 'waiting_validation_employee':[], 'waiting_validation_member':[], 'done':[]};
$.each(handle_legacy_states(resp.rows), function(i, e) {
if (e.doc.firstname) {
if (e.doc.errors) {
coops.with_errors.push(e.doc);
} else if (e.doc.validation_state == "to_fill") {
coops.to_fill.push(e.doc);
} else if (e.doc.validation_state == "waiting_validation_employee") {
coops.waiting_validation_employee.push(e.doc);
} else if (e.doc.validation_state == "waiting_validation_member") {
coops.waiting_validation_member.push(e.doc);
} else if (e.doc.validation_state == "done") {
coops.done.push(e.doc);
}
}); if (e.doc.coop_msg) {
coops.with_errors.push(e.doc);
}
}
coops['to_fill'].sort(function(a, b) { });
return a.barcode_base - b.barcode_base;
}); coops['to_fill'].sort(function(a, b) {
coops['with_errors'].sort(function(a, b) { return a.barcode_base - b.barcode_base;
return a.barcode_base - b.barcode_base; });
coops['with_errors'].sort(function(a, b) {
return a.barcode_base - b.barcode_base;
});
coops['waiting_validation_employee'].sort(function(a, b) {
return a.barcode_base - b.barcode_base;
});
coops['waiting_validation_member'].sort(function(a, b) {
return a.odoo_id - b.odoo_id;
});
coops['done'].sort(function(a, b) {
return b.timestamp - a.timestamp;
});
dispatch_coops_in_boxes();
}); });
coops['waiting_validation_employee'].sort(function(a, b) { } catch (err) {
return a.barcode_base - b.barcode_base; error = {msg: err.name + ' : ' + err.message, ctx: 'retrieve_all_coops'};
}); console.log(error);
coops['waiting_validation_member'].sort(function(a, b) { report_JS_error(error, 'prepa-odoo');
return a.odoo_id - b.odoo_id; }
});
coops['done'].sort(function(a, b) {
return b.timestamp - a.timestamp;
});
dispatch_coops_in_boxes();
});
} }
$(document).ready(function() { $(document).ready(function() {
......
//used in prepa-odoo and validation_coop
var coop_page = $('#coop_page'); var coop_page = $('#coop_page');
function show_checks_nb() { function show_checks_nb() {
......
from django.test import TestCase from django.test import TestCase
# Create your tests here.
...@@ -73,6 +73,7 @@ def inscriptions(request, type=1): ...@@ -73,6 +73,7 @@ def inscriptions(request, type=1):
'ask_for_sex': getattr(settings, 'SUBSCRIPTION_ASK_FOR_SEX', False), 'ask_for_sex': getattr(settings, 'SUBSCRIPTION_ASK_FOR_SEX', False),
'open_on_sunday': getattr(settings, 'OPEN_ON_SUNDAY', False), 'open_on_sunday': getattr(settings, 'OPEN_ON_SUNDAY', False),
'POUCHDB_VERSION': getattr(settings, 'POUCHDB_VERSION', ''), 'POUCHDB_VERSION': getattr(settings, 'POUCHDB_VERSION', ''),
'max_chq_nb': getattr(settings, 'MAX_CHQ_NB', 12),
'db': settings.COUCHDB['dbs']['member']} 'db': settings.COUCHDB['dbs']['member']}
response = HttpResponse(template.render(context, request)) response = HttpResponse(template.render(context, request))
......
...@@ -212,9 +212,10 @@ class Order(models.Model): ...@@ -212,9 +212,10 @@ class Order(models.Model):
def get_custom_barcode_labels_to_print(self): def get_custom_barcode_labels_to_print(self):
import re import re
fixed_prefix = getattr(settings, 'FIXED_BARCODE_PREFIX', '0490')
labels_data = {'total': 0, 'details': []} labels_data = {'total': 0, 'details': []}
lines = self.get_lines() lines = self.get_lines()
bc_pattern = re.compile('^0490') bc_pattern = re.compile('^' + fixed_prefix)
for l in lines: for l in lines:
if ('barcode' in l) and not (bc_pattern.match(str(l['barcode'])) is None): if ('barcode' in l) and not (bc_pattern.match(str(l['barcode'])) is None):
labels_data['details'].append(l) labels_data['details'].append(l)
...@@ -256,7 +257,8 @@ class Orders(models.Model): ...@@ -256,7 +257,8 @@ class Orders(models.Model):
import re import re
labels_data = {} labels_data = {}
try: try:
bc_pattern = re.compile('^0490|0491') fixed_prefix = getattr(settings, 'FIXED_BARCODE_PREFIX', '0490')
bc_pattern = re.compile('^' + fixed_prefix)
for l in Orders.get_lines(oids): for l in Orders.get_lines(oids):
if not (bc_pattern.match(str(l['barcode'])) is None): if not (bc_pattern.match(str(l['barcode'])) is None):
if not (l['product_tmpl_id'] in labels_data): if not (l['product_tmpl_id'] in labels_data):
......
...@@ -98,11 +98,14 @@ ...@@ -98,11 +98,14 @@
{'code': 'cash', 'title': 'Espèces','journal_id': CASH_PAYMENT_ID}, {'code': 'cash', 'title': 'Espèces','journal_id': CASH_PAYMENT_ID},
{'code': 'ch', 'title': 'Chèque', 'journal_id': CHECK_PAYMENT_ID}, {'code': 'ch', 'title': 'Chèque', 'journal_id': CHECK_PAYMENT_ID},
{'code': 'cb', 'title': 'Carte bancaire', 'journal_id': CB_PAYMENT_ID}, {'code': 'cb', 'title': 'Carte bancaire', 'journal_id': CB_PAYMENT_ID},
{'code': 'vir', 'title': 'Virement', 'journal_id': VIREMENT_PAYMENT_ID} {'code': 'vir', 'title': 'Virement', 'journal_id': VIREMENT_PAYMENT_ID}]
]
Used to generate payment meanings in subscription form Used to generate payment meanings in subscription form
- MAX_CHQ_NB = 12
Maximum accepted checks numbers
### Scales and labels files generation ### Scales and labels files generation
- DAV_PATH = '/data/dav/cagette' - DAV_PATH = '/data/dav/cagette'
...@@ -125,6 +128,10 @@ ...@@ -125,6 +128,10 @@
Fields add to generated text file Fields add to generated text file
- EXPORT_POS_CAT_FOR_SCALES = True
Exports POS categories data as a JSON file, and add a POS Category column in CSV file for scale database update
### Shop module ### Shop module
- SHOP_CAN_BUY = False - SHOP_CAN_BUY = False
......
...@@ -5,6 +5,7 @@ class CagetteMail: ...@@ -5,6 +5,7 @@ class CagetteMail:
@staticmethod @staticmethod
def sendWelcome(email): def sendWelcome(email):
"""Used in members/models.py , but mail is now sent by Odoo"""
from django.core.mail import send_mail from django.core.mail import send_mail
import re import re
from django.utils.html import strip_tags from django.utils.html import strip_tags
...@@ -19,15 +20,16 @@ class CagetteMail: ...@@ -19,15 +20,16 @@ class CagetteMail:
[email], [email],
fail_silently=False, fail_silently=False,
html_message=html_msg) html_message=html_msg)
@staticmethod @staticmethod
def sendCartValidation(email, cart): def sendCartValidation(email, cart):
"""Used by Shop"""
from django.core.mail import send_mail from django.core.mail import send_mail
from django.utils.html import strip_tags from django.utils.html import strip_tags
from django.template.loader import render_to_string from django.template.loader import render_to_string
from datetime import datetime from datetime import datetime
import pytz import pytz
tz = pytz.timezone("Europe/Paris") tz = pytz.timezone("Europe/Paris")
d_obj = datetime.fromtimestamp(cart['submitted_time'], tz) d_obj = datetime.fromtimestamp(cart['submitted_time'], tz)
if ('comment' in cart) and len(cart['comment']) == 0: if ('comment' in cart) and len(cart['comment']) == 0:
...@@ -35,18 +37,13 @@ class CagetteMail: ...@@ -35,18 +37,13 @@ class CagetteMail:
ctx = {'mag': settings.COMPANY_NAME, ctx = {'mag': settings.COMPANY_NAME,
'cart': cart, 'cart': cart,
'order_date': d_obj.strftime('%d/%m/%Y à %Hh%S (UTC)')} 'order_date': d_obj.strftime('%d/%m/%Y à %Hh%S (UTC)')}
try: ctx['survey_link'] = getattr(settings, 'SHOP_SURVEY_LINK', None)
ctx['survey_link'] = settings.SHOP_SURVEY_LINK
except: mail_template = getattr(settings, 'VALIDATION_ORDER_MAIL_TEMPLATE', 'shop/cart_validation_email.html')
pass
mail_template = 'shop/cart_validation_email.html'
try:
mail_template = settings.VALIDATION_ORDER_MAIL_TEMPLATE
except:
pass
html_msg = render_to_string(mail_template, ctx) html_msg = render_to_string(mail_template, ctx)
msg = strip_tags(html_msg) msg = strip_tags(html_msg)
send_mail("Votre commande en ligne à " + settings.COMPANY_NAME , send_mail("Votre commande en ligne à " + settings.COMPANY_NAME,
msg, msg,
settings.DEFAULT_FROM_EMAIL, settings.DEFAULT_FROM_EMAIL,
[email], [email],
......
...@@ -14,7 +14,6 @@ https://docs.djangoproject.com/en/1.8/ref/settings/ ...@@ -14,7 +14,6 @@ https://docs.djangoproject.com/en/1.8/ref/settings/
import os import os
from .settings_secret import * from .settings_secret import *
from .settings_constants import *
from .texts.cagette import * from .texts.cagette import *
from .config import * from .config import *
from .customized_errors_filter import * from .customized_errors_filter import *
......
...@@ -165,3 +165,26 @@ footer { position: fixed; ...@@ -165,3 +165,26 @@ footer { position: fixed;
} }
.mac-msg {background: red; color: #fff; padding: 3px;} .mac-msg {background: red; color: #fff; padding: 3px;}
.notifyjs-cancelable-base {
font-weight: bold;
padding: 8px 15px 8px 14px;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
background-color: #fcf8e3;
border: 1px solid #fbeed5;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
white-space: nowrap;
padding-left: 25px;
background-repeat: no-repeat;
background-position: 3px 7px;
}
.notifyjs-cancelable-info {
color: #3A87AD;
background-color: #D9EDF7;
border-color: #BCE8F1;
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3QYFAhkSsdes/QAAA8dJREFUOMvVlGtMW2UYx//POaWHXg6lLaW0ypAtw1UCgbniNOLcVOLmAjHZolOYlxmTGXVZdAnRfXQm+7SoU4mXaOaiZsEpC9FkiQs6Z6bdCnNYruM6KNBw6YWewzl9z+sHImEWv+vz7XmT95f/+3/+7wP814v+efDOV3/SoX3lHAA+6ODeUFfMfjOWMADgdk+eEKz0pF7aQdMAcOKLLjrcVMVX3xdWN29/GhYP7SvnP0cWfS8caSkfHZsPE9Fgnt02JNutQ0QYHB2dDz9/pKX8QjjuO9xUxd/66HdxTeCHZ3rojQObGQBcuNjfplkD3b19Y/6MrimSaKgSMmpGU5WevmE/swa6Oy73tQHA0Rdr2Mmv/6A1n9w9suQ7097Z9lM4FlTgTDrzZTu4StXVfpiI48rVcUDM5cmEksrFnHxfpTtU/3BFQzCQF/2bYVoNbH7zmItbSoMj40JSzmMyX5qDvriA7QdrIIpA+3cdsMpu0nXI8cV0MtKXCPZev+gCEM1S2NHPvWfP/hL+7FSr3+0p5RBEyhEN5JCKYr8XnASMT0xBNyzQGQeI8fjsGD39RMPk7se2bd5ZtTyoFYXftF6y37gx7NeUtJJOTFlAHDZLDuILU3j3+H5oOrD3yWbIztugaAzgnBKJuBLpGfQrS8wO4FZgV+c1IxaLgWVU0tMLEETCos4xMzEIv9cJXQcyagIwigDGwJgOAtHAwAhisQUjy0ORGERiELgG4iakkzo4MYAxcM5hAMi1WWG1yYCJIcMUaBkVRLdGeSU2995TLWzcUAzONJ7J6FBVBYIggMzmFbvdBV44Corg8vjhzC+EJEl8U1kJtgYrhCzgc/vvTwXKSib1paRFVRVORDAJAsw5FuTaJEhWM2SHB3mOAlhkNxwuLzeJsGwqWzf5TFNdKgtY5qHp6ZFf67Y/sAVadCaVY5YACDDb3Oi4NIjLnWMw2QthCBIsVhsUTU9tvXsjeq9+X1d75/KEs4LNOfcdf/+HthMnvwxOD0wmHaXr7ZItn2wuH2SnBzbZAbPJwpPx+VQuzcm7dgRCB57a1uBzUDRL4bfnI0RE0eaXd9W89mpjqHZnUI5Hh2l2dkZZUhOqpi2qSmpOmZ64Tuu9qlz/SEXo6MEHa3wOip46F1n7633eekV8ds8Wxjn37Wl63VVa+ej5oeEZ/82ZBETJjpJ1Rbij2D3Z/1trXUvLsblCK0XfOx0SX2kMsn9dX+d+7Kf6h8o4AIykuffjT8L20LU+w4AZd5VvEPY+XpWqLV327HR7DzXuDnD8r+ovkBehJ8i+y8YAAAAASUVORK5CYII=);
}
.notifyjs-cancelable-base .buttons {width: 190px; margin: 5px auto;}
.notifyjs-cancelable-base button {width: 90px;text-align: center; margin: 3px;}
\ No newline at end of file
/*!
JSZip - A Javascript class for generating and reading zip files
<http://stuartk.com/jszip>
(c) 2009-2014 Stuart Knightley <stuart [at] stuartk.com>
Dual licenced under the MIT license or GPLv3. See https://raw.github.com/Stuk/jszip/master/LICENSE.markdown.
JSZip uses the library pako released under the MIT license :
https://github.com/nodeca/pako/blob/master/LICENSE
*/
!function(a){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=a();else if("function"==typeof define&&define.amd)define([],a);else{var b;"undefined"!=typeof window?b=window:"undefined"!=typeof global?b=global:"undefined"!=typeof self&&(b=self),b.JSZip=a()}}(function(){return function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);throw new Error("Cannot find module '"+g+"'")}var j=c[g]={exports:{}};b[g][0].call(j.exports,function(a){var c=b[g][1][a];return e(c?c:a)},j,j.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g<d.length;g++)e(d[g]);return e}({1:[function(a,b,c){"use strict";var d="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";c.encode=function(a){for(var b,c,e,f,g,h,i,j="",k=0;k<a.length;)b=a.charCodeAt(k++),c=a.charCodeAt(k++),e=a.charCodeAt(k++),f=b>>2,g=(3&b)<<4|c>>4,h=(15&c)<<2|e>>6,i=63&e,isNaN(c)?h=i=64:isNaN(e)&&(i=64),j=j+d.charAt(f)+d.charAt(g)+d.charAt(h)+d.charAt(i);return j},c.decode=function(a){var b,c,e,f,g,h,i,j="",k=0;for(a=a.replace(/[^A-Za-z0-9\+\/\=]/g,"");k<a.length;)f=d.indexOf(a.charAt(k++)),g=d.indexOf(a.charAt(k++)),h=d.indexOf(a.charAt(k++)),i=d.indexOf(a.charAt(k++)),b=f<<2|g>>4,c=(15&g)<<4|h>>2,e=(3&h)<<6|i,j+=String.fromCharCode(b),64!=h&&(j+=String.fromCharCode(c)),64!=i&&(j+=String.fromCharCode(e));return j}},{}],2:[function(a,b){"use strict";function c(){this.compressedSize=0,this.uncompressedSize=0,this.crc32=0,this.compressionMethod=null,this.compressedContent=null}c.prototype={getContent:function(){return null},getCompressedContent:function(){return null}},b.exports=c},{}],3:[function(a,b,c){"use strict";c.STORE={magic:"\x00\x00",compress:function(a){return a},uncompress:function(a){return a},compressInputType:null,uncompressInputType:null},c.DEFLATE=a("./flate")},{"./flate":8}],4:[function(a,b){"use strict";var c=a("./utils"),d=[0,1996959894,3993919788,2567524794,124634137,1886057615,3915621685,2657392035,249268274,2044508324,3772115230,2547177864,162941995,2125561021,3887607047,2428444049,498536548,1789927666,4089016648,2227061214,450548861,1843258603,4107580753,2211677639,325883990,1684777152,4251122042,2321926636,335633487,1661365465,4195302755,2366115317,997073096,1281953886,3579855332,2724688242,1006888145,1258607687,3524101629,2768942443,901097722,1119000684,3686517206,2898065728,853044451,1172266101,3705015759,2882616665,651767980,1373503546,3369554304,3218104598,565507253,1454621731,3485111705,3099436303,671266974,1594198024,3322730930,2970347812,795835527,1483230225,3244367275,3060149565,1994146192,31158534,2563907772,4023717930,1907459465,112637215,2680153253,3904427059,2013776290,251722036,2517215374,3775830040,2137656763,141376813,2439277719,3865271297,1802195444,476864866,2238001368,4066508878,1812370925,453092731,2181625025,4111451223,1706088902,314042704,2344532202,4240017532,1658658271,366619977,2362670323,4224994405,1303535960,984961486,2747007092,3569037538,1256170817,1037604311,2765210733,3554079995,1131014506,879679996,2909243462,3663771856,1141124467,855842277,2852801631,3708648649,1342533948,654459306,3188396048,3373015174,1466479909,544179635,3110523913,3462522015,1591671054,702138776,2966460450,3352799412,1504918807,783551873,3082640443,3233442989,3988292384,2596254646,62317068,1957810842,3939845945,2647816111,81470997,1943803523,3814918930,2489596804,225274430,2053790376,3826175755,2466906013,167816743,2097651377,4027552580,2265490386,503444072,1762050814,4150417245,2154129355,426522225,1852507879,4275313526,2312317920,282753626,1742555852,4189708143,2394877945,397917763,1622183637,3604390888,2714866558,953729732,1340076626,3518719985,2797360999,1068828381,1219638859,3624741850,2936675148,906185462,1090812512,3747672003,2825379669,829329135,1181335161,3412177804,3160834842,628085408,1382605366,3423369109,3138078467,570562233,1426400815,3317316542,2998733608,733239954,1555261956,3268935591,3050360625,752459403,1541320221,2607071920,3965973030,1969922972,40735498,2617837225,3943577151,1913087877,83908371,2512341634,3803740692,2075208622,213261112,2463272603,3855990285,2094854071,198958881,2262029012,4057260610,1759359992,534414190,2176718541,4139329115,1873836001,414664567,2282248934,4279200368,1711684554,285281116,2405801727,4167216745,1634467795,376229701,2685067896,3608007406,1308918612,956543938,2808555105,3495958263,1231636301,1047427035,2932959818,3654703836,1088359270,936918e3,2847714899,3736837829,1202900863,817233897,3183342108,3401237130,1404277552,615818150,3134207493,3453421203,1423857449,601450431,3009837614,3294710456,1567103746,711928724,3020668471,3272380065,1510334235,755167117];b.exports=function(a,b){if("undefined"==typeof a||!a.length)return 0;var e="string"!==c.getTypeOf(a);"undefined"==typeof b&&(b=0);var f=0,g=0,h=0;b=-1^b;for(var i=0,j=a.length;j>i;i++)h=e?a[i]:a.charCodeAt(i),g=255&(b^h),f=d[g],b=b>>>8^f;return-1^b}},{"./utils":21}],5:[function(a,b){"use strict";function c(){this.data=null,this.length=0,this.index=0}var d=a("./utils");c.prototype={checkOffset:function(a){this.checkIndex(this.index+a)},checkIndex:function(a){if(this.length<a||0>a)throw new Error("End of data reached (data length = "+this.length+", asked index = "+a+"). Corrupted zip ?")},setIndex:function(a){this.checkIndex(a),this.index=a},skip:function(a){this.setIndex(this.index+a)},byteAt:function(){},readInt:function(a){var b,c=0;for(this.checkOffset(a),b=this.index+a-1;b>=this.index;b--)c=(c<<8)+this.byteAt(b);return this.index+=a,c},readString:function(a){return d.transformTo("string",this.readData(a))},readData:function(){},lastIndexOfSignature:function(){},readDate:function(){var a=this.readInt(4);return new Date((a>>25&127)+1980,(a>>21&15)-1,a>>16&31,a>>11&31,a>>5&63,(31&a)<<1)}},b.exports=c},{"./utils":21}],6:[function(a,b,c){"use strict";c.base64=!1,c.binary=!1,c.dir=!1,c.createFolders=!1,c.date=null,c.compression=null,c.compressionOptions=null,c.comment=null,c.unixPermissions=null,c.dosPermissions=null},{}],7:[function(a,b,c){"use strict";var d=a("./utils");c.string2binary=function(a){return d.string2binary(a)},c.string2Uint8Array=function(a){return d.transformTo("uint8array",a)},c.uint8Array2String=function(a){return d.transformTo("string",a)},c.string2Blob=function(a){var b=d.transformTo("arraybuffer",a);return d.arrayBuffer2Blob(b)},c.arrayBuffer2Blob=function(a){return d.arrayBuffer2Blob(a)},c.transformTo=function(a,b){return d.transformTo(a,b)},c.getTypeOf=function(a){return d.getTypeOf(a)},c.checkSupport=function(a){return d.checkSupport(a)},c.MAX_VALUE_16BITS=d.MAX_VALUE_16BITS,c.MAX_VALUE_32BITS=d.MAX_VALUE_32BITS,c.pretty=function(a){return d.pretty(a)},c.findCompression=function(a){return d.findCompression(a)},c.isRegExp=function(a){return d.isRegExp(a)}},{"./utils":21}],8:[function(a,b,c){"use strict";var d="undefined"!=typeof Uint8Array&&"undefined"!=typeof Uint16Array&&"undefined"!=typeof Uint32Array,e=a("pako");c.uncompressInputType=d?"uint8array":"array",c.compressInputType=d?"uint8array":"array",c.magic="\b\x00",c.compress=function(a,b){return e.deflateRaw(a,{level:b.level||-1})},c.uncompress=function(a){return e.inflateRaw(a)}},{pako:24}],9:[function(a,b){"use strict";function c(a,b){return this instanceof c?(this.files={},this.comment=null,this.root="",a&&this.load(a,b),void(this.clone=function(){var a=new c;for(var b in this)"function"!=typeof this[b]&&(a[b]=this[b]);return a})):new c(a,b)}var d=a("./base64");c.prototype=a("./object"),c.prototype.load=a("./load"),c.support=a("./support"),c.defaults=a("./defaults"),c.utils=a("./deprecatedPublicUtils"),c.base64={encode:function(a){return d.encode(a)},decode:function(a){return d.decode(a)}},c.compressions=a("./compressions"),b.exports=c},{"./base64":1,"./compressions":3,"./defaults":6,"./deprecatedPublicUtils":7,"./load":10,"./object":13,"./support":17}],10:[function(a,b){"use strict";var c=a("./base64"),d=a("./zipEntries");b.exports=function(a,b){var e,f,g,h;for(b=b||{},b.base64&&(a=c.decode(a)),f=new d(a,b),e=f.files,g=0;g<e.length;g++)h=e[g],this.file(h.fileName,h.decompressed,{binary:!0,optimizedBinaryString:!0,date:h.date,dir:h.dir,comment:h.fileComment.length?h.fileComment:null,unixPermissions:h.unixPermissions,dosPermissions:h.dosPermissions,createFolders:b.createFolders});return f.zipComment.length&&(this.comment=f.zipComment),this}},{"./base64":1,"./zipEntries":22}],11:[function(a,b){(function(a){"use strict";b.exports=function(b,c){return new a(b,c)},b.exports.test=function(b){return a.isBuffer(b)}}).call(this,"undefined"!=typeof Buffer?Buffer:void 0)},{}],12:[function(a,b){"use strict";function c(a){this.data=a,this.length=this.data.length,this.index=0}var d=a("./uint8ArrayReader");c.prototype=new d,c.prototype.readData=function(a){this.checkOffset(a);var b=this.data.slice(this.index,this.index+a);return this.index+=a,b},b.exports=c},{"./uint8ArrayReader":18}],13:[function(a,b){"use strict";var c=a("./support"),d=a("./utils"),e=a("./crc32"),f=a("./signature"),g=a("./defaults"),h=a("./base64"),i=a("./compressions"),j=a("./compressedObject"),k=a("./nodeBuffer"),l=a("./utf8"),m=a("./stringWriter"),n=a("./uint8ArrayWriter"),o=function(a){if(a._data instanceof j&&(a._data=a._data.getContent(),a.options.binary=!0,a.options.base64=!1,"uint8array"===d.getTypeOf(a._data))){var b=a._data;a._data=new Uint8Array(b.length),0!==b.length&&a._data.set(b,0)}return a._data},p=function(a){var b=o(a),e=d.getTypeOf(b);return"string"===e?!a.options.binary&&c.nodebuffer?k(b,"utf-8"):a.asBinary():b},q=function(a){var b=o(this);return null===b||"undefined"==typeof b?"":(this.options.base64&&(b=h.decode(b)),b=a&&this.options.binary?D.utf8decode(b):d.transformTo("string",b),a||this.options.binary||(b=d.transformTo("string",D.utf8encode(b))),b)},r=function(a,b,c){this.name=a,this.dir=c.dir,this.date=c.date,this.comment=c.comment,this.unixPermissions=c.unixPermissions,this.dosPermissions=c.dosPermissions,this._data=b,this.options=c,this._initialMetadata={dir:c.dir,date:c.date}};r.prototype={asText:function(){return q.call(this,!0)},asBinary:function(){return q.call(this,!1)},asNodeBuffer:function(){var a=p(this);return d.transformTo("nodebuffer",a)},asUint8Array:function(){var a=p(this);return d.transformTo("uint8array",a)},asArrayBuffer:function(){return this.asUint8Array().buffer}};var s=function(a,b){var c,d="";for(c=0;b>c;c++)d+=String.fromCharCode(255&a),a>>>=8;return d},t=function(){var a,b,c={};for(a=0;a<arguments.length;a++)for(b in arguments[a])arguments[a].hasOwnProperty(b)&&"undefined"==typeof c[b]&&(c[b]=arguments[a][b]);return c},u=function(a){return a=a||{},a.base64!==!0||null!==a.binary&&void 0!==a.binary||(a.binary=!0),a=t(a,g),a.date=a.date||new Date,null!==a.compression&&(a.compression=a.compression.toUpperCase()),a},v=function(a,b,c){var e,f=d.getTypeOf(b);if(c=u(c),"string"==typeof c.unixPermissions&&(c.unixPermissions=parseInt(c.unixPermissions,8)),c.unixPermissions&&16384&c.unixPermissions&&(c.dir=!0),c.dosPermissions&&16&c.dosPermissions&&(c.dir=!0),c.dir&&(a=x(a)),c.createFolders&&(e=w(a))&&y.call(this,e,!0),c.dir||null===b||"undefined"==typeof b)c.base64=!1,c.binary=!1,b=null,f=null;else if("string"===f)c.binary&&!c.base64&&c.optimizedBinaryString!==!0&&(b=d.string2binary(b));else{if(c.base64=!1,c.binary=!0,!(f||b instanceof j))throw new Error("The data of '"+a+"' is in an unsupported format !");"arraybuffer"===f&&(b=d.transformTo("uint8array",b))}var g=new r(a,b,c);return this.files[a]=g,g},w=function(a){"/"==a.slice(-1)&&(a=a.substring(0,a.length-1));var b=a.lastIndexOf("/");return b>0?a.substring(0,b):""},x=function(a){return"/"!=a.slice(-1)&&(a+="/"),a},y=function(a,b){return b="undefined"!=typeof b?b:!1,a=x(a),this.files[a]||v.call(this,a,null,{dir:!0,createFolders:b}),this.files[a]},z=function(a,b,c){var f,g=new j;return a._data instanceof j?(g.uncompressedSize=a._data.uncompressedSize,g.crc32=a._data.crc32,0===g.uncompressedSize||a.dir?(b=i.STORE,g.compressedContent="",g.crc32=0):a._data.compressionMethod===b.magic?g.compressedContent=a._data.getCompressedContent():(f=a._data.getContent(),g.compressedContent=b.compress(d.transformTo(b.compressInputType,f),c))):(f=p(a),(!f||0===f.length||a.dir)&&(b=i.STORE,f=""),g.uncompressedSize=f.length,g.crc32=e(f),g.compressedContent=b.compress(d.transformTo(b.compressInputType,f),c)),g.compressedSize=g.compressedContent.length,g.compressionMethod=b.magic,g},A=function(a,b){var c=a;return a||(c=b?16893:33204),(65535&c)<<16},B=function(a){return 63&(a||0)},C=function(a,b,c,g,h){var i,j,k,m,n=(c.compressedContent,d.transformTo("string",l.utf8encode(b.name))),o=b.comment||"",p=d.transformTo("string",l.utf8encode(o)),q=n.length!==b.name.length,r=p.length!==o.length,t=b.options,u="",v="",w="";k=b._initialMetadata.dir!==b.dir?b.dir:t.dir,m=b._initialMetadata.date!==b.date?b.date:t.date;var x=0,y=0;k&&(x|=16),"UNIX"===h?(y=798,x|=A(b.unixPermissions,k)):(y=20,x|=B(b.dosPermissions,k)),i=m.getHours(),i<<=6,i|=m.getMinutes(),i<<=5,i|=m.getSeconds()/2,j=m.getFullYear()-1980,j<<=4,j|=m.getMonth()+1,j<<=5,j|=m.getDate(),q&&(v=s(1,1)+s(e(n),4)+n,u+="up"+s(v.length,2)+v),r&&(w=s(1,1)+s(this.crc32(p),4)+p,u+="uc"+s(w.length,2)+w);var z="";z+="\n\x00",z+=q||r?"\x00\b":"\x00\x00",z+=c.compressionMethod,z+=s(i,2),z+=s(j,2),z+=s(c.crc32,4),z+=s(c.compressedSize,4),z+=s(c.uncompressedSize,4),z+=s(n.length,2),z+=s(u.length,2);var C=f.LOCAL_FILE_HEADER+z+n+u,D=f.CENTRAL_FILE_HEADER+s(y,2)+z+s(p.length,2)+"\x00\x00\x00\x00"+s(x,4)+s(g,4)+n+u+p;return{fileRecord:C,dirRecord:D,compressedObject:c}},D={load:function(){throw new Error("Load method is not defined. Is the file jszip-load.js included ?")},filter:function(a){var b,c,d,e,f=[];for(b in this.files)this.files.hasOwnProperty(b)&&(d=this.files[b],e=new r(d.name,d._data,t(d.options)),c=b.slice(this.root.length,b.length),b.slice(0,this.root.length)===this.root&&a(c,e)&&f.push(e));return f},file:function(a,b,c){if(1===arguments.length){if(d.isRegExp(a)){var e=a;return this.filter(function(a,b){return!b.dir&&e.test(a)})}return this.filter(function(b,c){return!c.dir&&b===a})[0]||null}return a=this.root+a,v.call(this,a,b,c),this},folder:function(a){if(!a)return this;if(d.isRegExp(a))return this.filter(function(b,c){return c.dir&&a.test(b)});var b=this.root+a,c=y.call(this,b),e=this.clone();return e.root=c.name,e},remove:function(a){a=this.root+a;var b=this.files[a];if(b||("/"!=a.slice(-1)&&(a+="/"),b=this.files[a]),b&&!b.dir)delete this.files[a];else for(var c=this.filter(function(b,c){return c.name.slice(0,a.length)===a}),d=0;d<c.length;d++)delete this.files[c[d].name];return this},generate:function(a){a=t(a||{},{base64:!0,compression:"STORE",compressionOptions:null,type:"base64",platform:"DOS",comment:null,mimeType:"application/zip"}),d.checkSupport(a.type),("darwin"===a.platform||"freebsd"===a.platform||"linux"===a.platform||"sunos"===a.platform)&&(a.platform="UNIX"),"win32"===a.platform&&(a.platform="DOS");var b,c,e=[],g=0,j=0,k=d.transformTo("string",this.utf8encode(a.comment||this.comment||""));for(var l in this.files)if(this.files.hasOwnProperty(l)){var o=this.files[l],p=o.options.compression||a.compression.toUpperCase(),q=i[p];if(!q)throw new Error(p+" is not a valid compression method !");var r=o.options.compressionOptions||a.compressionOptions||{},u=z.call(this,o,q,r),v=C.call(this,l,o,u,g,a.platform);g+=v.fileRecord.length+u.compressedSize,j+=v.dirRecord.length,e.push(v)}var w="";w=f.CENTRAL_DIRECTORY_END+"\x00\x00\x00\x00"+s(e.length,2)+s(e.length,2)+s(j,4)+s(g,4)+s(k.length,2)+k;var x=a.type.toLowerCase();for(b="uint8array"===x||"arraybuffer"===x||"blob"===x||"nodebuffer"===x?new n(g+j+w.length):new m(g+j+w.length),c=0;c<e.length;c++)b.append(e[c].fileRecord),b.append(e[c].compressedObject.compressedContent);for(c=0;c<e.length;c++)b.append(e[c].dirRecord);b.append(w);var y=b.finalize();switch(a.type.toLowerCase()){case"uint8array":case"arraybuffer":case"nodebuffer":return d.transformTo(a.type.toLowerCase(),y);case"blob":return d.arrayBuffer2Blob(d.transformTo("arraybuffer",y),a.mimeType);case"base64":return a.base64?h.encode(y):y;default:return y}},crc32:function(a,b){return e(a,b)},utf8encode:function(a){return d.transformTo("string",l.utf8encode(a))},utf8decode:function(a){return l.utf8decode(a)}};b.exports=D},{"./base64":1,"./compressedObject":2,"./compressions":3,"./crc32":4,"./defaults":6,"./nodeBuffer":11,"./signature":14,"./stringWriter":16,"./support":17,"./uint8ArrayWriter":19,"./utf8":20,"./utils":21}],14:[function(a,b,c){"use strict";c.LOCAL_FILE_HEADER="PK",c.CENTRAL_FILE_HEADER="PK",c.CENTRAL_DIRECTORY_END="PK",c.ZIP64_CENTRAL_DIRECTORY_LOCATOR="PK",c.ZIP64_CENTRAL_DIRECTORY_END="PK",c.DATA_DESCRIPTOR="PK\b"},{}],15:[function(a,b){"use strict";function c(a,b){this.data=a,b||(this.data=e.string2binary(this.data)),this.length=this.data.length,this.index=0}var d=a("./dataReader"),e=a("./utils");c.prototype=new d,c.prototype.byteAt=function(a){return this.data.charCodeAt(a)},c.prototype.lastIndexOfSignature=function(a){return this.data.lastIndexOf(a)},c.prototype.readData=function(a){this.checkOffset(a);var b=this.data.slice(this.index,this.index+a);return this.index+=a,b},b.exports=c},{"./dataReader":5,"./utils":21}],16:[function(a,b){"use strict";var c=a("./utils"),d=function(){this.data=[]};d.prototype={append:function(a){a=c.transformTo("string",a),this.data.push(a)},finalize:function(){return this.data.join("")}},b.exports=d},{"./utils":21}],17:[function(a,b,c){(function(a){"use strict";if(c.base64=!0,c.array=!0,c.string=!0,c.arraybuffer="undefined"!=typeof ArrayBuffer&&"undefined"!=typeof Uint8Array,c.nodebuffer="undefined"!=typeof a,c.uint8array="undefined"!=typeof Uint8Array,"undefined"==typeof ArrayBuffer)c.blob=!1;else{var b=new ArrayBuffer(0);try{c.blob=0===new Blob([b],{type:"application/zip"}).size}catch(d){try{var e=window.BlobBuilder||window.WebKitBlobBuilder||window.MozBlobBuilder||window.MSBlobBuilder,f=new e;f.append(b),c.blob=0===f.getBlob("application/zip").size}catch(d){c.blob=!1}}}}).call(this,"undefined"!=typeof Buffer?Buffer:void 0)},{}],18:[function(a,b){"use strict";function c(a){a&&(this.data=a,this.length=this.data.length,this.index=0)}var d=a("./dataReader");c.prototype=new d,c.prototype.byteAt=function(a){return this.data[a]},c.prototype.lastIndexOfSignature=function(a){for(var b=a.charCodeAt(0),c=a.charCodeAt(1),d=a.charCodeAt(2),e=a.charCodeAt(3),f=this.length-4;f>=0;--f)if(this.data[f]===b&&this.data[f+1]===c&&this.data[f+2]===d&&this.data[f+3]===e)return f;return-1},c.prototype.readData=function(a){if(this.checkOffset(a),0===a)return new Uint8Array(0);var b=this.data.subarray(this.index,this.index+a);return this.index+=a,b},b.exports=c},{"./dataReader":5}],19:[function(a,b){"use strict";var c=a("./utils"),d=function(a){this.data=new Uint8Array(a),this.index=0};d.prototype={append:function(a){0!==a.length&&(a=c.transformTo("uint8array",a),this.data.set(a,this.index),this.index+=a.length)},finalize:function(){return this.data}},b.exports=d},{"./utils":21}],20:[function(a,b,c){"use strict";for(var d=a("./utils"),e=a("./support"),f=a("./nodeBuffer"),g=new Array(256),h=0;256>h;h++)g[h]=h>=252?6:h>=248?5:h>=240?4:h>=224?3:h>=192?2:1;g[254]=g[254]=1;var i=function(a){var b,c,d,f,g,h=a.length,i=0;for(f=0;h>f;f++)c=a.charCodeAt(f),55296===(64512&c)&&h>f+1&&(d=a.charCodeAt(f+1),56320===(64512&d)&&(c=65536+(c-55296<<10)+(d-56320),f++)),i+=128>c?1:2048>c?2:65536>c?3:4;for(b=e.uint8array?new Uint8Array(i):new Array(i),g=0,f=0;i>g;f++)c=a.charCodeAt(f),55296===(64512&c)&&h>f+1&&(d=a.charCodeAt(f+1),56320===(64512&d)&&(c=65536+(c-55296<<10)+(d-56320),f++)),128>c?b[g++]=c:2048>c?(b[g++]=192|c>>>6,b[g++]=128|63&c):65536>c?(b[g++]=224|c>>>12,b[g++]=128|c>>>6&63,b[g++]=128|63&c):(b[g++]=240|c>>>18,b[g++]=128|c>>>12&63,b[g++]=128|c>>>6&63,b[g++]=128|63&c);return b},j=function(a,b){var c;for(b=b||a.length,b>a.length&&(b=a.length),c=b-1;c>=0&&128===(192&a[c]);)c--;return 0>c?b:0===c?b:c+g[a[c]]>b?c:b},k=function(a){var b,c,e,f,h=a.length,i=new Array(2*h);for(c=0,b=0;h>b;)if(e=a[b++],128>e)i[c++]=e;else if(f=g[e],f>4)i[c++]=65533,b+=f-1;else{for(e&=2===f?31:3===f?15:7;f>1&&h>b;)e=e<<6|63&a[b++],f--;f>1?i[c++]=65533:65536>e?i[c++]=e:(e-=65536,i[c++]=55296|e>>10&1023,i[c++]=56320|1023&e)}return i.length!==c&&(i.subarray?i=i.subarray(0,c):i.length=c),d.applyFromCharCode(i)};c.utf8encode=function(a){return e.nodebuffer?f(a,"utf-8"):i(a)},c.utf8decode=function(a){if(e.nodebuffer)return d.transformTo("nodebuffer",a).toString("utf-8");a=d.transformTo(e.uint8array?"uint8array":"array",a);for(var b=[],c=0,f=a.length,g=65536;f>c;){var h=j(a,Math.min(c+g,f));b.push(e.uint8array?k(a.subarray(c,h)):k(a.slice(c,h))),c=h}return b.join("")}},{"./nodeBuffer":11,"./support":17,"./utils":21}],21:[function(a,b,c){"use strict";function d(a){return a}function e(a,b){for(var c=0;c<a.length;++c)b[c]=255&a.charCodeAt(c);return b}function f(a){var b=65536,d=[],e=a.length,f=c.getTypeOf(a),g=0,h=!0;try{switch(f){case"uint8array":String.fromCharCode.apply(null,new Uint8Array(0));break;case"nodebuffer":String.fromCharCode.apply(null,j(0))}}catch(i){h=!1}if(!h){for(var k="",l=0;l<a.length;l++)k+=String.fromCharCode(a[l]);return k}for(;e>g&&b>1;)try{d.push("array"===f||"nodebuffer"===f?String.fromCharCode.apply(null,a.slice(g,Math.min(g+b,e))):String.fromCharCode.apply(null,a.subarray(g,Math.min(g+b,e)))),g+=b}catch(i){b=Math.floor(b/2)}return d.join("")}function g(a,b){for(var c=0;c<a.length;c++)b[c]=a[c];return b}var h=a("./support"),i=a("./compressions"),j=a("./nodeBuffer");c.string2binary=function(a){for(var b="",c=0;c<a.length;c++)b+=String.fromCharCode(255&a.charCodeAt(c));return b},c.arrayBuffer2Blob=function(a,b){c.checkSupport("blob"),b=b||"application/zip";try{return new Blob([a],{type:b})}catch(d){try{var e=window.BlobBuilder||window.WebKitBlobBuilder||window.MozBlobBuilder||window.MSBlobBuilder,f=new e;return f.append(a),f.getBlob(b)}catch(d){throw new Error("Bug : can't construct the Blob.")}}},c.applyFromCharCode=f;var k={};k.string={string:d,array:function(a){return e(a,new Array(a.length))},arraybuffer:function(a){return k.string.uint8array(a).buffer},uint8array:function(a){return e(a,new Uint8Array(a.length))},nodebuffer:function(a){return e(a,j(a.length))}},k.array={string:f,array:d,arraybuffer:function(a){return new Uint8Array(a).buffer},uint8array:function(a){return new Uint8Array(a)},nodebuffer:function(a){return j(a)}},k.arraybuffer={string:function(a){return f(new Uint8Array(a))},array:function(a){return g(new Uint8Array(a),new Array(a.byteLength))},arraybuffer:d,uint8array:function(a){return new Uint8Array(a)},nodebuffer:function(a){return j(new Uint8Array(a))}},k.uint8array={string:f,array:function(a){return g(a,new Array(a.length))},arraybuffer:function(a){return a.buffer},uint8array:d,nodebuffer:function(a){return j(a)}},k.nodebuffer={string:f,array:function(a){return g(a,new Array(a.length))},arraybuffer:function(a){return k.nodebuffer.uint8array(a).buffer},uint8array:function(a){return g(a,new Uint8Array(a.length))},nodebuffer:d},c.transformTo=function(a,b){if(b||(b=""),!a)return b;c.checkSupport(a);var d=c.getTypeOf(b),e=k[d][a](b);return e},c.getTypeOf=function(a){return"string"==typeof a?"string":"[object Array]"===Object.prototype.toString.call(a)?"array":h.nodebuffer&&j.test(a)?"nodebuffer":h.uint8array&&a instanceof Uint8Array?"uint8array":h.arraybuffer&&a instanceof ArrayBuffer?"arraybuffer":void 0},c.checkSupport=function(a){var b=h[a.toLowerCase()];if(!b)throw new Error(a+" is not supported by this browser")},c.MAX_VALUE_16BITS=65535,c.MAX_VALUE_32BITS=-1,c.pretty=function(a){var b,c,d="";for(c=0;c<(a||"").length;c++)b=a.charCodeAt(c),d+="\\x"+(16>b?"0":"")+b.toString(16).toUpperCase();return d},c.findCompression=function(a){for(var b in i)if(i.hasOwnProperty(b)&&i[b].magic===a)return i[b];return null},c.isRegExp=function(a){return"[object RegExp]"===Object.prototype.toString.call(a)}},{"./compressions":3,"./nodeBuffer":11,"./support":17}],22:[function(a,b){"use strict";function c(a,b){this.files=[],this.loadOptions=b,a&&this.load(a)}var d=a("./stringReader"),e=a("./nodeBufferReader"),f=a("./uint8ArrayReader"),g=a("./utils"),h=a("./signature"),i=a("./zipEntry"),j=a("./support"),k=a("./object");c.prototype={checkSignature:function(a){var b=this.reader.readString(4);if(b!==a)throw new Error("Corrupted zip or bug : unexpected signature ("+g.pretty(b)+", expected "+g.pretty(a)+")")},readBlockEndOfCentral:function(){this.diskNumber=this.reader.readInt(2),this.diskWithCentralDirStart=this.reader.readInt(2),this.centralDirRecordsOnThisDisk=this.reader.readInt(2),this.centralDirRecords=this.reader.readInt(2),this.centralDirSize=this.reader.readInt(4),this.centralDirOffset=this.reader.readInt(4),this.zipCommentLength=this.reader.readInt(2),this.zipComment=this.reader.readString(this.zipCommentLength),this.zipComment=k.utf8decode(this.zipComment)},readBlockZip64EndOfCentral:function(){this.zip64EndOfCentralSize=this.reader.readInt(8),this.versionMadeBy=this.reader.readString(2),this.versionNeeded=this.reader.readInt(2),this.diskNumber=this.reader.readInt(4),this.diskWithCentralDirStart=this.reader.readInt(4),this.centralDirRecordsOnThisDisk=this.reader.readInt(8),this.centralDirRecords=this.reader.readInt(8),this.centralDirSize=this.reader.readInt(8),this.centralDirOffset=this.reader.readInt(8),this.zip64ExtensibleData={};for(var a,b,c,d=this.zip64EndOfCentralSize-44,e=0;d>e;)a=this.reader.readInt(2),b=this.reader.readInt(4),c=this.reader.readString(b),this.zip64ExtensibleData[a]={id:a,length:b,value:c}},readBlockZip64EndOfCentralLocator:function(){if(this.diskWithZip64CentralDirStart=this.reader.readInt(4),this.relativeOffsetEndOfZip64CentralDir=this.reader.readInt(8),this.disksCount=this.reader.readInt(4),this.disksCount>1)throw new Error("Multi-volumes zip are not supported")},readLocalFiles:function(){var a,b;for(a=0;a<this.files.length;a++)b=this.files[a],this.reader.setIndex(b.localHeaderOffset),this.checkSignature(h.LOCAL_FILE_HEADER),b.readLocalPart(this.reader),b.handleUTF8(),b.processAttributes()},readCentralDir:function(){var a;for(this.reader.setIndex(this.centralDirOffset);this.reader.readString(4)===h.CENTRAL_FILE_HEADER;)a=new i({zip64:this.zip64},this.loadOptions),a.readCentralPart(this.reader),this.files.push(a)},readEndOfCentral:function(){var a=this.reader.lastIndexOfSignature(h.CENTRAL_DIRECTORY_END);if(-1===a){var b=!0;try{this.reader.setIndex(0),this.checkSignature(h.LOCAL_FILE_HEADER),b=!1}catch(c){}throw new Error(b?"Can't find end of central directory : is this a zip file ? If it is, see http://stuk.github.io/jszip/documentation/howto/read_zip.html":"Corrupted zip : can't find end of central directory")}if(this.reader.setIndex(a),this.checkSignature(h.CENTRAL_DIRECTORY_END),this.readBlockEndOfCentral(),this.diskNumber===g.MAX_VALUE_16BITS||this.diskWithCentralDirStart===g.MAX_VALUE_16BITS||this.centralDirRecordsOnThisDisk===g.MAX_VALUE_16BITS||this.centralDirRecords===g.MAX_VALUE_16BITS||this.centralDirSize===g.MAX_VALUE_32BITS||this.centralDirOffset===g.MAX_VALUE_32BITS){if(this.zip64=!0,a=this.reader.lastIndexOfSignature(h.ZIP64_CENTRAL_DIRECTORY_LOCATOR),-1===a)throw new Error("Corrupted zip : can't find the ZIP64 end of central directory locator");this.reader.setIndex(a),this.checkSignature(h.ZIP64_CENTRAL_DIRECTORY_LOCATOR),this.readBlockZip64EndOfCentralLocator(),this.reader.setIndex(this.relativeOffsetEndOfZip64CentralDir),this.checkSignature(h.ZIP64_CENTRAL_DIRECTORY_END),this.readBlockZip64EndOfCentral()}},prepareReader:function(a){var b=g.getTypeOf(a);this.reader="string"!==b||j.uint8array?"nodebuffer"===b?new e(a):new f(g.transformTo("uint8array",a)):new d(a,this.loadOptions.optimizedBinaryString)},load:function(a){this.prepareReader(a),this.readEndOfCentral(),this.readCentralDir(),this.readLocalFiles()}},b.exports=c},{"./nodeBufferReader":12,"./object":13,"./signature":14,"./stringReader":15,"./support":17,"./uint8ArrayReader":18,"./utils":21,"./zipEntry":23}],23:[function(a,b){"use strict";function c(a,b){this.options=a,this.loadOptions=b}var d=a("./stringReader"),e=a("./utils"),f=a("./compressedObject"),g=a("./object"),h=0,i=3;c.prototype={isEncrypted:function(){return 1===(1&this.bitFlag)},useUTF8:function(){return 2048===(2048&this.bitFlag)},prepareCompressedContent:function(a,b,c){return function(){var d=a.index;a.setIndex(b);var e=a.readData(c);return a.setIndex(d),e}},prepareContent:function(a,b,c,d,f){return function(){var a=e.transformTo(d.uncompressInputType,this.getCompressedContent()),b=d.uncompress(a);if(b.length!==f)throw new Error("Bug : uncompressed data size mismatch");return b}},readLocalPart:function(a){var b,c;if(a.skip(22),this.fileNameLength=a.readInt(2),c=a.readInt(2),this.fileName=a.readString(this.fileNameLength),a.skip(c),-1==this.compressedSize||-1==this.uncompressedSize)throw new Error("Bug or corrupted zip : didn't get enough informations from the central directory (compressedSize == -1 || uncompressedSize == -1)");if(b=e.findCompression(this.compressionMethod),null===b)throw new Error("Corrupted zip : compression "+e.pretty(this.compressionMethod)+" unknown (inner file : "+this.fileName+")");if(this.decompressed=new f,this.decompressed.compressedSize=this.compressedSize,this.decompressed.uncompressedSize=this.uncompressedSize,this.decompressed.crc32=this.crc32,this.decompressed.compressionMethod=this.compressionMethod,this.decompressed.getCompressedContent=this.prepareCompressedContent(a,a.index,this.compressedSize,b),this.decompressed.getContent=this.prepareContent(a,a.index,this.compressedSize,b,this.uncompressedSize),this.loadOptions.checkCRC32&&(this.decompressed=e.transformTo("string",this.decompressed.getContent()),g.crc32(this.decompressed)!==this.crc32))throw new Error("Corrupted zip : CRC32 mismatch")},readCentralPart:function(a){if(this.versionMadeBy=a.readInt(2),this.versionNeeded=a.readInt(2),this.bitFlag=a.readInt(2),this.compressionMethod=a.readString(2),this.date=a.readDate(),this.crc32=a.readInt(4),this.compressedSize=a.readInt(4),this.uncompressedSize=a.readInt(4),this.fileNameLength=a.readInt(2),this.extraFieldsLength=a.readInt(2),this.fileCommentLength=a.readInt(2),this.diskNumberStart=a.readInt(2),this.internalFileAttributes=a.readInt(2),this.externalFileAttributes=a.readInt(4),this.localHeaderOffset=a.readInt(4),this.isEncrypted())throw new Error("Encrypted zip are not supported");this.fileName=a.readString(this.fileNameLength),this.readExtraFields(a),this.parseZIP64ExtraField(a),this.fileComment=a.readString(this.fileCommentLength)},processAttributes:function(){this.unixPermissions=null,this.dosPermissions=null;var a=this.versionMadeBy>>8;this.dir=16&this.externalFileAttributes?!0:!1,a===h&&(this.dosPermissions=63&this.externalFileAttributes),a===i&&(this.unixPermissions=this.externalFileAttributes>>16&65535),this.dir||"/"!==this.fileName.slice(-1)||(this.dir=!0)},parseZIP64ExtraField:function(){if(this.extraFields[1]){var a=new d(this.extraFields[1].value);this.uncompressedSize===e.MAX_VALUE_32BITS&&(this.uncompressedSize=a.readInt(8)),this.compressedSize===e.MAX_VALUE_32BITS&&(this.compressedSize=a.readInt(8)),this.localHeaderOffset===e.MAX_VALUE_32BITS&&(this.localHeaderOffset=a.readInt(8)),this.diskNumberStart===e.MAX_VALUE_32BITS&&(this.diskNumberStart=a.readInt(4))}},readExtraFields:function(a){var b,c,d,e=a.index;for(this.extraFields=this.extraFields||{};a.index<e+this.extraFieldsLength;)b=a.readInt(2),c=a.readInt(2),d=a.readString(c),this.extraFields[b]={id:b,length:c,value:d}},handleUTF8:function(){if(this.useUTF8())this.fileName=g.utf8decode(this.fileName),this.fileComment=g.utf8decode(this.fileComment);else{var a=this.findExtraFieldUnicodePath();null!==a&&(this.fileName=a);var b=this.findExtraFieldUnicodeComment();null!==b&&(this.fileComment=b)}},findExtraFieldUnicodePath:function(){var a=this.extraFields[28789];if(a){var b=new d(a.value);return 1!==b.readInt(1)?null:g.crc32(this.fileName)!==b.readInt(4)?null:g.utf8decode(b.readString(a.length-5))
}return null},findExtraFieldUnicodeComment:function(){var a=this.extraFields[25461];if(a){var b=new d(a.value);return 1!==b.readInt(1)?null:g.crc32(this.fileComment)!==b.readInt(4)?null:g.utf8decode(b.readString(a.length-5))}return null}},b.exports=c},{"./compressedObject":2,"./object":13,"./stringReader":15,"./utils":21}],24:[function(a,b){"use strict";var c=a("./lib/utils/common").assign,d=a("./lib/deflate"),e=a("./lib/inflate"),f=a("./lib/zlib/constants"),g={};c(g,d,e,f),b.exports=g},{"./lib/deflate":25,"./lib/inflate":26,"./lib/utils/common":27,"./lib/zlib/constants":30}],25:[function(a,b,c){"use strict";function d(a,b){var c=new s(b);if(c.push(a,!0),c.err)throw c.msg;return c.result}function e(a,b){return b=b||{},b.raw=!0,d(a,b)}function f(a,b){return b=b||{},b.gzip=!0,d(a,b)}var g=a("./zlib/deflate.js"),h=a("./utils/common"),i=a("./utils/strings"),j=a("./zlib/messages"),k=a("./zlib/zstream"),l=0,m=4,n=0,o=1,p=-1,q=0,r=8,s=function(a){this.options=h.assign({level:p,method:r,chunkSize:16384,windowBits:15,memLevel:8,strategy:q,to:""},a||{});var b=this.options;b.raw&&b.windowBits>0?b.windowBits=-b.windowBits:b.gzip&&b.windowBits>0&&b.windowBits<16&&(b.windowBits+=16),this.err=0,this.msg="",this.ended=!1,this.chunks=[],this.strm=new k,this.strm.avail_out=0;var c=g.deflateInit2(this.strm,b.level,b.method,b.windowBits,b.memLevel,b.strategy);if(c!==n)throw new Error(j[c]);b.header&&g.deflateSetHeader(this.strm,b.header)};s.prototype.push=function(a,b){var c,d,e=this.strm,f=this.options.chunkSize;if(this.ended)return!1;d=b===~~b?b:b===!0?m:l,e.input="string"==typeof a?i.string2buf(a):a,e.next_in=0,e.avail_in=e.input.length;do{if(0===e.avail_out&&(e.output=new h.Buf8(f),e.next_out=0,e.avail_out=f),c=g.deflate(e,d),c!==o&&c!==n)return this.onEnd(c),this.ended=!0,!1;(0===e.avail_out||0===e.avail_in&&d===m)&&this.onData("string"===this.options.to?i.buf2binstring(h.shrinkBuf(e.output,e.next_out)):h.shrinkBuf(e.output,e.next_out))}while((e.avail_in>0||0===e.avail_out)&&c!==o);return d===m?(c=g.deflateEnd(this.strm),this.onEnd(c),this.ended=!0,c===n):!0},s.prototype.onData=function(a){this.chunks.push(a)},s.prototype.onEnd=function(a){a===n&&(this.result="string"===this.options.to?this.chunks.join(""):h.flattenChunks(this.chunks)),this.chunks=[],this.err=a,this.msg=this.strm.msg},c.Deflate=s,c.deflate=d,c.deflateRaw=e,c.gzip=f},{"./utils/common":27,"./utils/strings":28,"./zlib/deflate.js":32,"./zlib/messages":37,"./zlib/zstream":39}],26:[function(a,b,c){"use strict";function d(a,b){var c=new m(b);if(c.push(a,!0),c.err)throw c.msg;return c.result}function e(a,b){return b=b||{},b.raw=!0,d(a,b)}var f=a("./zlib/inflate.js"),g=a("./utils/common"),h=a("./utils/strings"),i=a("./zlib/constants"),j=a("./zlib/messages"),k=a("./zlib/zstream"),l=a("./zlib/gzheader"),m=function(a){this.options=g.assign({chunkSize:16384,windowBits:0,to:""},a||{});var b=this.options;b.raw&&b.windowBits>=0&&b.windowBits<16&&(b.windowBits=-b.windowBits,0===b.windowBits&&(b.windowBits=-15)),!(b.windowBits>=0&&b.windowBits<16)||a&&a.windowBits||(b.windowBits+=32),b.windowBits>15&&b.windowBits<48&&0===(15&b.windowBits)&&(b.windowBits|=15),this.err=0,this.msg="",this.ended=!1,this.chunks=[],this.strm=new k,this.strm.avail_out=0;var c=f.inflateInit2(this.strm,b.windowBits);if(c!==i.Z_OK)throw new Error(j[c]);this.header=new l,f.inflateGetHeader(this.strm,this.header)};m.prototype.push=function(a,b){var c,d,e,j,k,l=this.strm,m=this.options.chunkSize;if(this.ended)return!1;d=b===~~b?b:b===!0?i.Z_FINISH:i.Z_NO_FLUSH,l.input="string"==typeof a?h.binstring2buf(a):a,l.next_in=0,l.avail_in=l.input.length;do{if(0===l.avail_out&&(l.output=new g.Buf8(m),l.next_out=0,l.avail_out=m),c=f.inflate(l,i.Z_NO_FLUSH),c!==i.Z_STREAM_END&&c!==i.Z_OK)return this.onEnd(c),this.ended=!0,!1;l.next_out&&(0===l.avail_out||c===i.Z_STREAM_END||0===l.avail_in&&d===i.Z_FINISH)&&("string"===this.options.to?(e=h.utf8border(l.output,l.next_out),j=l.next_out-e,k=h.buf2string(l.output,e),l.next_out=j,l.avail_out=m-j,j&&g.arraySet(l.output,l.output,e,j,0),this.onData(k)):this.onData(g.shrinkBuf(l.output,l.next_out)))}while(l.avail_in>0&&c!==i.Z_STREAM_END);return c===i.Z_STREAM_END&&(d=i.Z_FINISH),d===i.Z_FINISH?(c=f.inflateEnd(this.strm),this.onEnd(c),this.ended=!0,c===i.Z_OK):!0},m.prototype.onData=function(a){this.chunks.push(a)},m.prototype.onEnd=function(a){a===i.Z_OK&&(this.result="string"===this.options.to?this.chunks.join(""):g.flattenChunks(this.chunks)),this.chunks=[],this.err=a,this.msg=this.strm.msg},c.Inflate=m,c.inflate=d,c.inflateRaw=e,c.ungzip=d},{"./utils/common":27,"./utils/strings":28,"./zlib/constants":30,"./zlib/gzheader":33,"./zlib/inflate.js":35,"./zlib/messages":37,"./zlib/zstream":39}],27:[function(a,b,c){"use strict";var d="undefined"!=typeof Uint8Array&&"undefined"!=typeof Uint16Array&&"undefined"!=typeof Int32Array;c.assign=function(a){for(var b=Array.prototype.slice.call(arguments,1);b.length;){var c=b.shift();if(c){if("object"!=typeof c)throw new TypeError(c+"must be non-object");for(var d in c)c.hasOwnProperty(d)&&(a[d]=c[d])}}return a},c.shrinkBuf=function(a,b){return a.length===b?a:a.subarray?a.subarray(0,b):(a.length=b,a)};var e={arraySet:function(a,b,c,d,e){if(b.subarray&&a.subarray)return void a.set(b.subarray(c,c+d),e);for(var f=0;d>f;f++)a[e+f]=b[c+f]},flattenChunks:function(a){var b,c,d,e,f,g;for(d=0,b=0,c=a.length;c>b;b++)d+=a[b].length;for(g=new Uint8Array(d),e=0,b=0,c=a.length;c>b;b++)f=a[b],g.set(f,e),e+=f.length;return g}},f={arraySet:function(a,b,c,d,e){for(var f=0;d>f;f++)a[e+f]=b[c+f]},flattenChunks:function(a){return[].concat.apply([],a)}};c.setTyped=function(a){a?(c.Buf8=Uint8Array,c.Buf16=Uint16Array,c.Buf32=Int32Array,c.assign(c,e)):(c.Buf8=Array,c.Buf16=Array,c.Buf32=Array,c.assign(c,f))},c.setTyped(d)},{}],28:[function(a,b,c){"use strict";function d(a,b){if(65537>b&&(a.subarray&&g||!a.subarray&&f))return String.fromCharCode.apply(null,e.shrinkBuf(a,b));for(var c="",d=0;b>d;d++)c+=String.fromCharCode(a[d]);return c}var e=a("./common"),f=!0,g=!0;try{String.fromCharCode.apply(null,[0])}catch(h){f=!1}try{String.fromCharCode.apply(null,new Uint8Array(1))}catch(h){g=!1}for(var i=new e.Buf8(256),j=0;256>j;j++)i[j]=j>=252?6:j>=248?5:j>=240?4:j>=224?3:j>=192?2:1;i[254]=i[254]=1,c.string2buf=function(a){var b,c,d,f,g,h=a.length,i=0;for(f=0;h>f;f++)c=a.charCodeAt(f),55296===(64512&c)&&h>f+1&&(d=a.charCodeAt(f+1),56320===(64512&d)&&(c=65536+(c-55296<<10)+(d-56320),f++)),i+=128>c?1:2048>c?2:65536>c?3:4;for(b=new e.Buf8(i),g=0,f=0;i>g;f++)c=a.charCodeAt(f),55296===(64512&c)&&h>f+1&&(d=a.charCodeAt(f+1),56320===(64512&d)&&(c=65536+(c-55296<<10)+(d-56320),f++)),128>c?b[g++]=c:2048>c?(b[g++]=192|c>>>6,b[g++]=128|63&c):65536>c?(b[g++]=224|c>>>12,b[g++]=128|c>>>6&63,b[g++]=128|63&c):(b[g++]=240|c>>>18,b[g++]=128|c>>>12&63,b[g++]=128|c>>>6&63,b[g++]=128|63&c);return b},c.buf2binstring=function(a){return d(a,a.length)},c.binstring2buf=function(a){for(var b=new e.Buf8(a.length),c=0,d=b.length;d>c;c++)b[c]=a.charCodeAt(c);return b},c.buf2string=function(a,b){var c,e,f,g,h=b||a.length,j=new Array(2*h);for(e=0,c=0;h>c;)if(f=a[c++],128>f)j[e++]=f;else if(g=i[f],g>4)j[e++]=65533,c+=g-1;else{for(f&=2===g?31:3===g?15:7;g>1&&h>c;)f=f<<6|63&a[c++],g--;g>1?j[e++]=65533:65536>f?j[e++]=f:(f-=65536,j[e++]=55296|f>>10&1023,j[e++]=56320|1023&f)}return d(j,e)},c.utf8border=function(a,b){var c;for(b=b||a.length,b>a.length&&(b=a.length),c=b-1;c>=0&&128===(192&a[c]);)c--;return 0>c?b:0===c?b:c+i[a[c]]>b?c:b}},{"./common":27}],29:[function(a,b){"use strict";function c(a,b,c,d){for(var e=65535&a|0,f=a>>>16&65535|0,g=0;0!==c;){g=c>2e3?2e3:c,c-=g;do e=e+b[d++]|0,f=f+e|0;while(--g);e%=65521,f%=65521}return e|f<<16|0}b.exports=c},{}],30:[function(a,b){b.exports={Z_NO_FLUSH:0,Z_PARTIAL_FLUSH:1,Z_SYNC_FLUSH:2,Z_FULL_FLUSH:3,Z_FINISH:4,Z_BLOCK:5,Z_TREES:6,Z_OK:0,Z_STREAM_END:1,Z_NEED_DICT:2,Z_ERRNO:-1,Z_STREAM_ERROR:-2,Z_DATA_ERROR:-3,Z_BUF_ERROR:-5,Z_NO_COMPRESSION:0,Z_BEST_SPEED:1,Z_BEST_COMPRESSION:9,Z_DEFAULT_COMPRESSION:-1,Z_FILTERED:1,Z_HUFFMAN_ONLY:2,Z_RLE:3,Z_FIXED:4,Z_DEFAULT_STRATEGY:0,Z_BINARY:0,Z_TEXT:1,Z_UNKNOWN:2,Z_DEFLATED:8}},{}],31:[function(a,b){"use strict";function c(){for(var a,b=[],c=0;256>c;c++){a=c;for(var d=0;8>d;d++)a=1&a?3988292384^a>>>1:a>>>1;b[c]=a}return b}function d(a,b,c,d){var f=e,g=d+c;a=-1^a;for(var h=d;g>h;h++)a=a>>>8^f[255&(a^b[h])];return-1^a}var e=c();b.exports=d},{}],32:[function(a,b,c){"use strict";function d(a,b){return a.msg=G[b],b}function e(a){return(a<<1)-(a>4?9:0)}function f(a){for(var b=a.length;--b>=0;)a[b]=0}function g(a){var b=a.state,c=b.pending;c>a.avail_out&&(c=a.avail_out),0!==c&&(C.arraySet(a.output,b.pending_buf,b.pending_out,c,a.next_out),a.next_out+=c,b.pending_out+=c,a.total_out+=c,a.avail_out-=c,b.pending-=c,0===b.pending&&(b.pending_out=0))}function h(a,b){D._tr_flush_block(a,a.block_start>=0?a.block_start:-1,a.strstart-a.block_start,b),a.block_start=a.strstart,g(a.strm)}function i(a,b){a.pending_buf[a.pending++]=b}function j(a,b){a.pending_buf[a.pending++]=b>>>8&255,a.pending_buf[a.pending++]=255&b}function k(a,b,c,d){var e=a.avail_in;return e>d&&(e=d),0===e?0:(a.avail_in-=e,C.arraySet(b,a.input,a.next_in,e,c),1===a.state.wrap?a.adler=E(a.adler,b,e,c):2===a.state.wrap&&(a.adler=F(a.adler,b,e,c)),a.next_in+=e,a.total_in+=e,e)}function l(a,b){var c,d,e=a.max_chain_length,f=a.strstart,g=a.prev_length,h=a.nice_match,i=a.strstart>a.w_size-jb?a.strstart-(a.w_size-jb):0,j=a.window,k=a.w_mask,l=a.prev,m=a.strstart+ib,n=j[f+g-1],o=j[f+g];a.prev_length>=a.good_match&&(e>>=2),h>a.lookahead&&(h=a.lookahead);do if(c=b,j[c+g]===o&&j[c+g-1]===n&&j[c]===j[f]&&j[++c]===j[f+1]){f+=2,c++;do;while(j[++f]===j[++c]&&j[++f]===j[++c]&&j[++f]===j[++c]&&j[++f]===j[++c]&&j[++f]===j[++c]&&j[++f]===j[++c]&&j[++f]===j[++c]&&j[++f]===j[++c]&&m>f);if(d=ib-(m-f),f=m-ib,d>g){if(a.match_start=b,g=d,d>=h)break;n=j[f+g-1],o=j[f+g]}}while((b=l[b&k])>i&&0!==--e);return g<=a.lookahead?g:a.lookahead}function m(a){var b,c,d,e,f,g=a.w_size;do{if(e=a.window_size-a.lookahead-a.strstart,a.strstart>=g+(g-jb)){C.arraySet(a.window,a.window,g,g,0),a.match_start-=g,a.strstart-=g,a.block_start-=g,c=a.hash_size,b=c;do d=a.head[--b],a.head[b]=d>=g?d-g:0;while(--c);c=g,b=c;do d=a.prev[--b],a.prev[b]=d>=g?d-g:0;while(--c);e+=g}if(0===a.strm.avail_in)break;if(c=k(a.strm,a.window,a.strstart+a.lookahead,e),a.lookahead+=c,a.lookahead+a.insert>=hb)for(f=a.strstart-a.insert,a.ins_h=a.window[f],a.ins_h=(a.ins_h<<a.hash_shift^a.window[f+1])&a.hash_mask;a.insert&&(a.ins_h=(a.ins_h<<a.hash_shift^a.window[f+hb-1])&a.hash_mask,a.prev[f&a.w_mask]=a.head[a.ins_h],a.head[a.ins_h]=f,f++,a.insert--,!(a.lookahead+a.insert<hb)););}while(a.lookahead<jb&&0!==a.strm.avail_in)}function n(a,b){var c=65535;for(c>a.pending_buf_size-5&&(c=a.pending_buf_size-5);;){if(a.lookahead<=1){if(m(a),0===a.lookahead&&b===H)return sb;if(0===a.lookahead)break}a.strstart+=a.lookahead,a.lookahead=0;var d=a.block_start+c;if((0===a.strstart||a.strstart>=d)&&(a.lookahead=a.strstart-d,a.strstart=d,h(a,!1),0===a.strm.avail_out))return sb;if(a.strstart-a.block_start>=a.w_size-jb&&(h(a,!1),0===a.strm.avail_out))return sb}return a.insert=0,b===K?(h(a,!0),0===a.strm.avail_out?ub:vb):a.strstart>a.block_start&&(h(a,!1),0===a.strm.avail_out)?sb:sb}function o(a,b){for(var c,d;;){if(a.lookahead<jb){if(m(a),a.lookahead<jb&&b===H)return sb;if(0===a.lookahead)break}if(c=0,a.lookahead>=hb&&(a.ins_h=(a.ins_h<<a.hash_shift^a.window[a.strstart+hb-1])&a.hash_mask,c=a.prev[a.strstart&a.w_mask]=a.head[a.ins_h],a.head[a.ins_h]=a.strstart),0!==c&&a.strstart-c<=a.w_size-jb&&(a.match_length=l(a,c)),a.match_length>=hb)if(d=D._tr_tally(a,a.strstart-a.match_start,a.match_length-hb),a.lookahead-=a.match_length,a.match_length<=a.max_lazy_match&&a.lookahead>=hb){a.match_length--;do a.strstart++,a.ins_h=(a.ins_h<<a.hash_shift^a.window[a.strstart+hb-1])&a.hash_mask,c=a.prev[a.strstart&a.w_mask]=a.head[a.ins_h],a.head[a.ins_h]=a.strstart;while(0!==--a.match_length);a.strstart++}else a.strstart+=a.match_length,a.match_length=0,a.ins_h=a.window[a.strstart],a.ins_h=(a.ins_h<<a.hash_shift^a.window[a.strstart+1])&a.hash_mask;else d=D._tr_tally(a,0,a.window[a.strstart]),a.lookahead--,a.strstart++;if(d&&(h(a,!1),0===a.strm.avail_out))return sb}return a.insert=a.strstart<hb-1?a.strstart:hb-1,b===K?(h(a,!0),0===a.strm.avail_out?ub:vb):a.last_lit&&(h(a,!1),0===a.strm.avail_out)?sb:tb}function p(a,b){for(var c,d,e;;){if(a.lookahead<jb){if(m(a),a.lookahead<jb&&b===H)return sb;if(0===a.lookahead)break}if(c=0,a.lookahead>=hb&&(a.ins_h=(a.ins_h<<a.hash_shift^a.window[a.strstart+hb-1])&a.hash_mask,c=a.prev[a.strstart&a.w_mask]=a.head[a.ins_h],a.head[a.ins_h]=a.strstart),a.prev_length=a.match_length,a.prev_match=a.match_start,a.match_length=hb-1,0!==c&&a.prev_length<a.max_lazy_match&&a.strstart-c<=a.w_size-jb&&(a.match_length=l(a,c),a.match_length<=5&&(a.strategy===S||a.match_length===hb&&a.strstart-a.match_start>4096)&&(a.match_length=hb-1)),a.prev_length>=hb&&a.match_length<=a.prev_length){e=a.strstart+a.lookahead-hb,d=D._tr_tally(a,a.strstart-1-a.prev_match,a.prev_length-hb),a.lookahead-=a.prev_length-1,a.prev_length-=2;do++a.strstart<=e&&(a.ins_h=(a.ins_h<<a.hash_shift^a.window[a.strstart+hb-1])&a.hash_mask,c=a.prev[a.strstart&a.w_mask]=a.head[a.ins_h],a.head[a.ins_h]=a.strstart);while(0!==--a.prev_length);if(a.match_available=0,a.match_length=hb-1,a.strstart++,d&&(h(a,!1),0===a.strm.avail_out))return sb}else if(a.match_available){if(d=D._tr_tally(a,0,a.window[a.strstart-1]),d&&h(a,!1),a.strstart++,a.lookahead--,0===a.strm.avail_out)return sb}else a.match_available=1,a.strstart++,a.lookahead--}return a.match_available&&(d=D._tr_tally(a,0,a.window[a.strstart-1]),a.match_available=0),a.insert=a.strstart<hb-1?a.strstart:hb-1,b===K?(h(a,!0),0===a.strm.avail_out?ub:vb):a.last_lit&&(h(a,!1),0===a.strm.avail_out)?sb:tb}function q(a,b){for(var c,d,e,f,g=a.window;;){if(a.lookahead<=ib){if(m(a),a.lookahead<=ib&&b===H)return sb;if(0===a.lookahead)break}if(a.match_length=0,a.lookahead>=hb&&a.strstart>0&&(e=a.strstart-1,d=g[e],d===g[++e]&&d===g[++e]&&d===g[++e])){f=a.strstart+ib;do;while(d===g[++e]&&d===g[++e]&&d===g[++e]&&d===g[++e]&&d===g[++e]&&d===g[++e]&&d===g[++e]&&d===g[++e]&&f>e);a.match_length=ib-(f-e),a.match_length>a.lookahead&&(a.match_length=a.lookahead)}if(a.match_length>=hb?(c=D._tr_tally(a,1,a.match_length-hb),a.lookahead-=a.match_length,a.strstart+=a.match_length,a.match_length=0):(c=D._tr_tally(a,0,a.window[a.strstart]),a.lookahead--,a.strstart++),c&&(h(a,!1),0===a.strm.avail_out))return sb}return a.insert=0,b===K?(h(a,!0),0===a.strm.avail_out?ub:vb):a.last_lit&&(h(a,!1),0===a.strm.avail_out)?sb:tb}function r(a,b){for(var c;;){if(0===a.lookahead&&(m(a),0===a.lookahead)){if(b===H)return sb;break}if(a.match_length=0,c=D._tr_tally(a,0,a.window[a.strstart]),a.lookahead--,a.strstart++,c&&(h(a,!1),0===a.strm.avail_out))return sb}return a.insert=0,b===K?(h(a,!0),0===a.strm.avail_out?ub:vb):a.last_lit&&(h(a,!1),0===a.strm.avail_out)?sb:tb}function s(a){a.window_size=2*a.w_size,f(a.head),a.max_lazy_match=B[a.level].max_lazy,a.good_match=B[a.level].good_length,a.nice_match=B[a.level].nice_length,a.max_chain_length=B[a.level].max_chain,a.strstart=0,a.block_start=0,a.lookahead=0,a.insert=0,a.match_length=a.prev_length=hb-1,a.match_available=0,a.ins_h=0}function t(){this.strm=null,this.status=0,this.pending_buf=null,this.pending_buf_size=0,this.pending_out=0,this.pending=0,this.wrap=0,this.gzhead=null,this.gzindex=0,this.method=Y,this.last_flush=-1,this.w_size=0,this.w_bits=0,this.w_mask=0,this.window=null,this.window_size=0,this.prev=null,this.head=null,this.ins_h=0,this.hash_size=0,this.hash_bits=0,this.hash_mask=0,this.hash_shift=0,this.block_start=0,this.match_length=0,this.prev_match=0,this.match_available=0,this.strstart=0,this.match_start=0,this.lookahead=0,this.prev_length=0,this.max_chain_length=0,this.max_lazy_match=0,this.level=0,this.strategy=0,this.good_match=0,this.nice_match=0,this.dyn_ltree=new C.Buf16(2*fb),this.dyn_dtree=new C.Buf16(2*(2*db+1)),this.bl_tree=new C.Buf16(2*(2*eb+1)),f(this.dyn_ltree),f(this.dyn_dtree),f(this.bl_tree),this.l_desc=null,this.d_desc=null,this.bl_desc=null,this.bl_count=new C.Buf16(gb+1),this.heap=new C.Buf16(2*cb+1),f(this.heap),this.heap_len=0,this.heap_max=0,this.depth=new C.Buf16(2*cb+1),f(this.depth),this.l_buf=0,this.lit_bufsize=0,this.last_lit=0,this.d_buf=0,this.opt_len=0,this.static_len=0,this.matches=0,this.insert=0,this.bi_buf=0,this.bi_valid=0}function u(a){var b;return a&&a.state?(a.total_in=a.total_out=0,a.data_type=X,b=a.state,b.pending=0,b.pending_out=0,b.wrap<0&&(b.wrap=-b.wrap),b.status=b.wrap?lb:qb,a.adler=2===b.wrap?0:1,b.last_flush=H,D._tr_init(b),M):d(a,O)}function v(a){var b=u(a);return b===M&&s(a.state),b}function w(a,b){return a&&a.state?2!==a.state.wrap?O:(a.state.gzhead=b,M):O}function x(a,b,c,e,f,g){if(!a)return O;var h=1;if(b===R&&(b=6),0>e?(h=0,e=-e):e>15&&(h=2,e-=16),1>f||f>Z||c!==Y||8>e||e>15||0>b||b>9||0>g||g>V)return d(a,O);8===e&&(e=9);var i=new t;return a.state=i,i.strm=a,i.wrap=h,i.gzhead=null,i.w_bits=e,i.w_size=1<<i.w_bits,i.w_mask=i.w_size-1,i.hash_bits=f+7,i.hash_size=1<<i.hash_bits,i.hash_mask=i.hash_size-1,i.hash_shift=~~((i.hash_bits+hb-1)/hb),i.window=new C.Buf8(2*i.w_size),i.head=new C.Buf16(i.hash_size),i.prev=new C.Buf16(i.w_size),i.lit_bufsize=1<<f+6,i.pending_buf_size=4*i.lit_bufsize,i.pending_buf=new C.Buf8(i.pending_buf_size),i.d_buf=i.lit_bufsize>>1,i.l_buf=3*i.lit_bufsize,i.level=b,i.strategy=g,i.method=c,v(a)}function y(a,b){return x(a,b,Y,$,_,W)}function z(a,b){var c,h,k,l;if(!a||!a.state||b>L||0>b)return a?d(a,O):O;if(h=a.state,!a.output||!a.input&&0!==a.avail_in||h.status===rb&&b!==K)return d(a,0===a.avail_out?Q:O);if(h.strm=a,c=h.last_flush,h.last_flush=b,h.status===lb)if(2===h.wrap)a.adler=0,i(h,31),i(h,139),i(h,8),h.gzhead?(i(h,(h.gzhead.text?1:0)+(h.gzhead.hcrc?2:0)+(h.gzhead.extra?4:0)+(h.gzhead.name?8:0)+(h.gzhead.comment?16:0)),i(h,255&h.gzhead.time),i(h,h.gzhead.time>>8&255),i(h,h.gzhead.time>>16&255),i(h,h.gzhead.time>>24&255),i(h,9===h.level?2:h.strategy>=T||h.level<2?4:0),i(h,255&h.gzhead.os),h.gzhead.extra&&h.gzhead.extra.length&&(i(h,255&h.gzhead.extra.length),i(h,h.gzhead.extra.length>>8&255)),h.gzhead.hcrc&&(a.adler=F(a.adler,h.pending_buf,h.pending,0)),h.gzindex=0,h.status=mb):(i(h,0),i(h,0),i(h,0),i(h,0),i(h,0),i(h,9===h.level?2:h.strategy>=T||h.level<2?4:0),i(h,wb),h.status=qb);else{var m=Y+(h.w_bits-8<<4)<<8,n=-1;n=h.strategy>=T||h.level<2?0:h.level<6?1:6===h.level?2:3,m|=n<<6,0!==h.strstart&&(m|=kb),m+=31-m%31,h.status=qb,j(h,m),0!==h.strstart&&(j(h,a.adler>>>16),j(h,65535&a.adler)),a.adler=1}if(h.status===mb)if(h.gzhead.extra){for(k=h.pending;h.gzindex<(65535&h.gzhead.extra.length)&&(h.pending!==h.pending_buf_size||(h.gzhead.hcrc&&h.pending>k&&(a.adler=F(a.adler,h.pending_buf,h.pending-k,k)),g(a),k=h.pending,h.pending!==h.pending_buf_size));)i(h,255&h.gzhead.extra[h.gzindex]),h.gzindex++;h.gzhead.hcrc&&h.pending>k&&(a.adler=F(a.adler,h.pending_buf,h.pending-k,k)),h.gzindex===h.gzhead.extra.length&&(h.gzindex=0,h.status=nb)}else h.status=nb;if(h.status===nb)if(h.gzhead.name){k=h.pending;do{if(h.pending===h.pending_buf_size&&(h.gzhead.hcrc&&h.pending>k&&(a.adler=F(a.adler,h.pending_buf,h.pending-k,k)),g(a),k=h.pending,h.pending===h.pending_buf_size)){l=1;break}l=h.gzindex<h.gzhead.name.length?255&h.gzhead.name.charCodeAt(h.gzindex++):0,i(h,l)}while(0!==l);h.gzhead.hcrc&&h.pending>k&&(a.adler=F(a.adler,h.pending_buf,h.pending-k,k)),0===l&&(h.gzindex=0,h.status=ob)}else h.status=ob;if(h.status===ob)if(h.gzhead.comment){k=h.pending;do{if(h.pending===h.pending_buf_size&&(h.gzhead.hcrc&&h.pending>k&&(a.adler=F(a.adler,h.pending_buf,h.pending-k,k)),g(a),k=h.pending,h.pending===h.pending_buf_size)){l=1;break}l=h.gzindex<h.gzhead.comment.length?255&h.gzhead.comment.charCodeAt(h.gzindex++):0,i(h,l)}while(0!==l);h.gzhead.hcrc&&h.pending>k&&(a.adler=F(a.adler,h.pending_buf,h.pending-k,k)),0===l&&(h.status=pb)}else h.status=pb;if(h.status===pb&&(h.gzhead.hcrc?(h.pending+2>h.pending_buf_size&&g(a),h.pending+2<=h.pending_buf_size&&(i(h,255&a.adler),i(h,a.adler>>8&255),a.adler=0,h.status=qb)):h.status=qb),0!==h.pending){if(g(a),0===a.avail_out)return h.last_flush=-1,M}else if(0===a.avail_in&&e(b)<=e(c)&&b!==K)return d(a,Q);if(h.status===rb&&0!==a.avail_in)return d(a,Q);if(0!==a.avail_in||0!==h.lookahead||b!==H&&h.status!==rb){var o=h.strategy===T?r(h,b):h.strategy===U?q(h,b):B[h.level].func(h,b);if((o===ub||o===vb)&&(h.status=rb),o===sb||o===ub)return 0===a.avail_out&&(h.last_flush=-1),M;if(o===tb&&(b===I?D._tr_align(h):b!==L&&(D._tr_stored_block(h,0,0,!1),b===J&&(f(h.head),0===h.lookahead&&(h.strstart=0,h.block_start=0,h.insert=0))),g(a),0===a.avail_out))return h.last_flush=-1,M}return b!==K?M:h.wrap<=0?N:(2===h.wrap?(i(h,255&a.adler),i(h,a.adler>>8&255),i(h,a.adler>>16&255),i(h,a.adler>>24&255),i(h,255&a.total_in),i(h,a.total_in>>8&255),i(h,a.total_in>>16&255),i(h,a.total_in>>24&255)):(j(h,a.adler>>>16),j(h,65535&a.adler)),g(a),h.wrap>0&&(h.wrap=-h.wrap),0!==h.pending?M:N)}function A(a){var b;return a&&a.state?(b=a.state.status,b!==lb&&b!==mb&&b!==nb&&b!==ob&&b!==pb&&b!==qb&&b!==rb?d(a,O):(a.state=null,b===qb?d(a,P):M)):O}var B,C=a("../utils/common"),D=a("./trees"),E=a("./adler32"),F=a("./crc32"),G=a("./messages"),H=0,I=1,J=3,K=4,L=5,M=0,N=1,O=-2,P=-3,Q=-5,R=-1,S=1,T=2,U=3,V=4,W=0,X=2,Y=8,Z=9,$=15,_=8,ab=29,bb=256,cb=bb+1+ab,db=30,eb=19,fb=2*cb+1,gb=15,hb=3,ib=258,jb=ib+hb+1,kb=32,lb=42,mb=69,nb=73,ob=91,pb=103,qb=113,rb=666,sb=1,tb=2,ub=3,vb=4,wb=3,xb=function(a,b,c,d,e){this.good_length=a,this.max_lazy=b,this.nice_length=c,this.max_chain=d,this.func=e};B=[new xb(0,0,0,0,n),new xb(4,4,8,4,o),new xb(4,5,16,8,o),new xb(4,6,32,32,o),new xb(4,4,16,16,p),new xb(8,16,32,32,p),new xb(8,16,128,128,p),new xb(8,32,128,256,p),new xb(32,128,258,1024,p),new xb(32,258,258,4096,p)],c.deflateInit=y,c.deflateInit2=x,c.deflateReset=v,c.deflateResetKeep=u,c.deflateSetHeader=w,c.deflate=z,c.deflateEnd=A,c.deflateInfo="pako deflate (from Nodeca project)"},{"../utils/common":27,"./adler32":29,"./crc32":31,"./messages":37,"./trees":38}],33:[function(a,b){"use strict";function c(){this.text=0,this.time=0,this.xflags=0,this.os=0,this.extra=null,this.extra_len=0,this.name="",this.comment="",this.hcrc=0,this.done=!1}b.exports=c},{}],34:[function(a,b){"use strict";var c=30,d=12;b.exports=function(a,b){var e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C;e=a.state,f=a.next_in,B=a.input,g=f+(a.avail_in-5),h=a.next_out,C=a.output,i=h-(b-a.avail_out),j=h+(a.avail_out-257),k=e.dmax,l=e.wsize,m=e.whave,n=e.wnext,o=e.window,p=e.hold,q=e.bits,r=e.lencode,s=e.distcode,t=(1<<e.lenbits)-1,u=(1<<e.distbits)-1;a:do{15>q&&(p+=B[f++]<<q,q+=8,p+=B[f++]<<q,q+=8),v=r[p&t];b:for(;;){if(w=v>>>24,p>>>=w,q-=w,w=v>>>16&255,0===w)C[h++]=65535&v;else{if(!(16&w)){if(0===(64&w)){v=r[(65535&v)+(p&(1<<w)-1)];continue b}if(32&w){e.mode=d;break a}a.msg="invalid literal/length code",e.mode=c;break a}x=65535&v,w&=15,w&&(w>q&&(p+=B[f++]<<q,q+=8),x+=p&(1<<w)-1,p>>>=w,q-=w),15>q&&(p+=B[f++]<<q,q+=8,p+=B[f++]<<q,q+=8),v=s[p&u];c:for(;;){if(w=v>>>24,p>>>=w,q-=w,w=v>>>16&255,!(16&w)){if(0===(64&w)){v=s[(65535&v)+(p&(1<<w)-1)];continue c}a.msg="invalid distance code",e.mode=c;break a}if(y=65535&v,w&=15,w>q&&(p+=B[f++]<<q,q+=8,w>q&&(p+=B[f++]<<q,q+=8)),y+=p&(1<<w)-1,y>k){a.msg="invalid distance too far back",e.mode=c;break a}if(p>>>=w,q-=w,w=h-i,y>w){if(w=y-w,w>m&&e.sane){a.msg="invalid distance too far back",e.mode=c;break a}if(z=0,A=o,0===n){if(z+=l-w,x>w){x-=w;do C[h++]=o[z++];while(--w);z=h-y,A=C}}else if(w>n){if(z+=l+n-w,w-=n,x>w){x-=w;do C[h++]=o[z++];while(--w);if(z=0,x>n){w=n,x-=w;do C[h++]=o[z++];while(--w);z=h-y,A=C}}}else if(z+=n-w,x>w){x-=w;do C[h++]=o[z++];while(--w);z=h-y,A=C}for(;x>2;)C[h++]=A[z++],C[h++]=A[z++],C[h++]=A[z++],x-=3;x&&(C[h++]=A[z++],x>1&&(C[h++]=A[z++]))}else{z=h-y;do C[h++]=C[z++],C[h++]=C[z++],C[h++]=C[z++],x-=3;while(x>2);x&&(C[h++]=C[z++],x>1&&(C[h++]=C[z++]))}break}}break}}while(g>f&&j>h);x=q>>3,f-=x,q-=x<<3,p&=(1<<q)-1,a.next_in=f,a.next_out=h,a.avail_in=g>f?5+(g-f):5-(f-g),a.avail_out=j>h?257+(j-h):257-(h-j),e.hold=p,e.bits=q}},{}],35:[function(a,b,c){"use strict";function d(a){return(a>>>24&255)+(a>>>8&65280)+((65280&a)<<8)+((255&a)<<24)}function e(){this.mode=0,this.last=!1,this.wrap=0,this.havedict=!1,this.flags=0,this.dmax=0,this.check=0,this.total=0,this.head=null,this.wbits=0,this.wsize=0,this.whave=0,this.wnext=0,this.window=null,this.hold=0,this.bits=0,this.length=0,this.offset=0,this.extra=0,this.lencode=null,this.distcode=null,this.lenbits=0,this.distbits=0,this.ncode=0,this.nlen=0,this.ndist=0,this.have=0,this.next=null,this.lens=new r.Buf16(320),this.work=new r.Buf16(288),this.lendyn=null,this.distdyn=null,this.sane=0,this.back=0,this.was=0}function f(a){var b;return a&&a.state?(b=a.state,a.total_in=a.total_out=b.total=0,a.msg="",b.wrap&&(a.adler=1&b.wrap),b.mode=K,b.last=0,b.havedict=0,b.dmax=32768,b.head=null,b.hold=0,b.bits=0,b.lencode=b.lendyn=new r.Buf32(ob),b.distcode=b.distdyn=new r.Buf32(pb),b.sane=1,b.back=-1,C):F}function g(a){var b;return a&&a.state?(b=a.state,b.wsize=0,b.whave=0,b.wnext=0,f(a)):F}function h(a,b){var c,d;return a&&a.state?(d=a.state,0>b?(c=0,b=-b):(c=(b>>4)+1,48>b&&(b&=15)),b&&(8>b||b>15)?F:(null!==d.window&&d.wbits!==b&&(d.window=null),d.wrap=c,d.wbits=b,g(a))):F}function i(a,b){var c,d;return a?(d=new e,a.state=d,d.window=null,c=h(a,b),c!==C&&(a.state=null),c):F}function j(a){return i(a,rb)}function k(a){if(sb){var b;for(p=new r.Buf32(512),q=new r.Buf32(32),b=0;144>b;)a.lens[b++]=8;for(;256>b;)a.lens[b++]=9;for(;280>b;)a.lens[b++]=7;for(;288>b;)a.lens[b++]=8;for(v(x,a.lens,0,288,p,0,a.work,{bits:9}),b=0;32>b;)a.lens[b++]=5;v(y,a.lens,0,32,q,0,a.work,{bits:5}),sb=!1}a.lencode=p,a.lenbits=9,a.distcode=q,a.distbits=5}function l(a,b,c,d){var e,f=a.state;return null===f.window&&(f.wsize=1<<f.wbits,f.wnext=0,f.whave=0,f.window=new r.Buf8(f.wsize)),d>=f.wsize?(r.arraySet(f.window,b,c-f.wsize,f.wsize,0),f.wnext=0,f.whave=f.wsize):(e=f.wsize-f.wnext,e>d&&(e=d),r.arraySet(f.window,b,c-d,e,f.wnext),d-=e,d?(r.arraySet(f.window,b,c-d,d,0),f.wnext=d,f.whave=f.wsize):(f.wnext+=e,f.wnext===f.wsize&&(f.wnext=0),f.whave<f.wsize&&(f.whave+=e))),0}function m(a,b){var c,e,f,g,h,i,j,m,n,o,p,q,ob,pb,qb,rb,sb,tb,ub,vb,wb,xb,yb,zb,Ab=0,Bb=new r.Buf8(4),Cb=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15];if(!a||!a.state||!a.output||!a.input&&0!==a.avail_in)return F;c=a.state,c.mode===V&&(c.mode=W),h=a.next_out,f=a.output,j=a.avail_out,g=a.next_in,e=a.input,i=a.avail_in,m=c.hold,n=c.bits,o=i,p=j,xb=C;a:for(;;)switch(c.mode){case K:if(0===c.wrap){c.mode=W;break}for(;16>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}if(2&c.wrap&&35615===m){c.check=0,Bb[0]=255&m,Bb[1]=m>>>8&255,c.check=t(c.check,Bb,2,0),m=0,n=0,c.mode=L;break}if(c.flags=0,c.head&&(c.head.done=!1),!(1&c.wrap)||(((255&m)<<8)+(m>>8))%31){a.msg="incorrect header check",c.mode=lb;break}if((15&m)!==J){a.msg="unknown compression method",c.mode=lb;break}if(m>>>=4,n-=4,wb=(15&m)+8,0===c.wbits)c.wbits=wb;else if(wb>c.wbits){a.msg="invalid window size",c.mode=lb;break}c.dmax=1<<wb,a.adler=c.check=1,c.mode=512&m?T:V,m=0,n=0;break;case L:for(;16>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}if(c.flags=m,(255&c.flags)!==J){a.msg="unknown compression method",c.mode=lb;break}if(57344&c.flags){a.msg="unknown header flags set",c.mode=lb;break}c.head&&(c.head.text=m>>8&1),512&c.flags&&(Bb[0]=255&m,Bb[1]=m>>>8&255,c.check=t(c.check,Bb,2,0)),m=0,n=0,c.mode=M;case M:for(;32>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}c.head&&(c.head.time=m),512&c.flags&&(Bb[0]=255&m,Bb[1]=m>>>8&255,Bb[2]=m>>>16&255,Bb[3]=m>>>24&255,c.check=t(c.check,Bb,4,0)),m=0,n=0,c.mode=N;case N:for(;16>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}c.head&&(c.head.xflags=255&m,c.head.os=m>>8),512&c.flags&&(Bb[0]=255&m,Bb[1]=m>>>8&255,c.check=t(c.check,Bb,2,0)),m=0,n=0,c.mode=O;case O:if(1024&c.flags){for(;16>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}c.length=m,c.head&&(c.head.extra_len=m),512&c.flags&&(Bb[0]=255&m,Bb[1]=m>>>8&255,c.check=t(c.check,Bb,2,0)),m=0,n=0}else c.head&&(c.head.extra=null);c.mode=P;case P:if(1024&c.flags&&(q=c.length,q>i&&(q=i),q&&(c.head&&(wb=c.head.extra_len-c.length,c.head.extra||(c.head.extra=new Array(c.head.extra_len)),r.arraySet(c.head.extra,e,g,q,wb)),512&c.flags&&(c.check=t(c.check,e,q,g)),i-=q,g+=q,c.length-=q),c.length))break a;c.length=0,c.mode=Q;case Q:if(2048&c.flags){if(0===i)break a;q=0;do wb=e[g+q++],c.head&&wb&&c.length<65536&&(c.head.name+=String.fromCharCode(wb));while(wb&&i>q);if(512&c.flags&&(c.check=t(c.check,e,q,g)),i-=q,g+=q,wb)break a}else c.head&&(c.head.name=null);c.length=0,c.mode=R;case R:if(4096&c.flags){if(0===i)break a;q=0;do wb=e[g+q++],c.head&&wb&&c.length<65536&&(c.head.comment+=String.fromCharCode(wb));while(wb&&i>q);if(512&c.flags&&(c.check=t(c.check,e,q,g)),i-=q,g+=q,wb)break a}else c.head&&(c.head.comment=null);c.mode=S;case S:if(512&c.flags){for(;16>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}if(m!==(65535&c.check)){a.msg="header crc mismatch",c.mode=lb;break}m=0,n=0}c.head&&(c.head.hcrc=c.flags>>9&1,c.head.done=!0),a.adler=c.check=0,c.mode=V;break;case T:for(;32>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}a.adler=c.check=d(m),m=0,n=0,c.mode=U;case U:if(0===c.havedict)return a.next_out=h,a.avail_out=j,a.next_in=g,a.avail_in=i,c.hold=m,c.bits=n,E;a.adler=c.check=1,c.mode=V;case V:if(b===A||b===B)break a;case W:if(c.last){m>>>=7&n,n-=7&n,c.mode=ib;break}for(;3>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}switch(c.last=1&m,m>>>=1,n-=1,3&m){case 0:c.mode=X;break;case 1:if(k(c),c.mode=bb,b===B){m>>>=2,n-=2;break a}break;case 2:c.mode=$;break;case 3:a.msg="invalid block type",c.mode=lb}m>>>=2,n-=2;break;case X:for(m>>>=7&n,n-=7&n;32>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}if((65535&m)!==(m>>>16^65535)){a.msg="invalid stored block lengths",c.mode=lb;break}if(c.length=65535&m,m=0,n=0,c.mode=Y,b===B)break a;case Y:c.mode=Z;case Z:if(q=c.length){if(q>i&&(q=i),q>j&&(q=j),0===q)break a;r.arraySet(f,e,g,q,h),i-=q,g+=q,j-=q,h+=q,c.length-=q;break}c.mode=V;break;case $:for(;14>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}if(c.nlen=(31&m)+257,m>>>=5,n-=5,c.ndist=(31&m)+1,m>>>=5,n-=5,c.ncode=(15&m)+4,m>>>=4,n-=4,c.nlen>286||c.ndist>30){a.msg="too many length or distance symbols",c.mode=lb;break}c.have=0,c.mode=_;case _:for(;c.have<c.ncode;){for(;3>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}c.lens[Cb[c.have++]]=7&m,m>>>=3,n-=3}for(;c.have<19;)c.lens[Cb[c.have++]]=0;if(c.lencode=c.lendyn,c.lenbits=7,yb={bits:c.lenbits},xb=v(w,c.lens,0,19,c.lencode,0,c.work,yb),c.lenbits=yb.bits,xb){a.msg="invalid code lengths set",c.mode=lb;break}c.have=0,c.mode=ab;case ab:for(;c.have<c.nlen+c.ndist;){for(;Ab=c.lencode[m&(1<<c.lenbits)-1],qb=Ab>>>24,rb=Ab>>>16&255,sb=65535&Ab,!(n>=qb);){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}if(16>sb)m>>>=qb,n-=qb,c.lens[c.have++]=sb;else{if(16===sb){for(zb=qb+2;zb>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}if(m>>>=qb,n-=qb,0===c.have){a.msg="invalid bit length repeat",c.mode=lb;break}wb=c.lens[c.have-1],q=3+(3&m),m>>>=2,n-=2}else if(17===sb){for(zb=qb+3;zb>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}m>>>=qb,n-=qb,wb=0,q=3+(7&m),m>>>=3,n-=3}else{for(zb=qb+7;zb>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}m>>>=qb,n-=qb,wb=0,q=11+(127&m),m>>>=7,n-=7}if(c.have+q>c.nlen+c.ndist){a.msg="invalid bit length repeat",c.mode=lb;break}for(;q--;)c.lens[c.have++]=wb}}if(c.mode===lb)break;if(0===c.lens[256]){a.msg="invalid code -- missing end-of-block",c.mode=lb;break}if(c.lenbits=9,yb={bits:c.lenbits},xb=v(x,c.lens,0,c.nlen,c.lencode,0,c.work,yb),c.lenbits=yb.bits,xb){a.msg="invalid literal/lengths set",c.mode=lb;break}if(c.distbits=6,c.distcode=c.distdyn,yb={bits:c.distbits},xb=v(y,c.lens,c.nlen,c.ndist,c.distcode,0,c.work,yb),c.distbits=yb.bits,xb){a.msg="invalid distances set",c.mode=lb;break}if(c.mode=bb,b===B)break a;case bb:c.mode=cb;case cb:if(i>=6&&j>=258){a.next_out=h,a.avail_out=j,a.next_in=g,a.avail_in=i,c.hold=m,c.bits=n,u(a,p),h=a.next_out,f=a.output,j=a.avail_out,g=a.next_in,e=a.input,i=a.avail_in,m=c.hold,n=c.bits,c.mode===V&&(c.back=-1);
break}for(c.back=0;Ab=c.lencode[m&(1<<c.lenbits)-1],qb=Ab>>>24,rb=Ab>>>16&255,sb=65535&Ab,!(n>=qb);){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}if(rb&&0===(240&rb)){for(tb=qb,ub=rb,vb=sb;Ab=c.lencode[vb+((m&(1<<tb+ub)-1)>>tb)],qb=Ab>>>24,rb=Ab>>>16&255,sb=65535&Ab,!(n>=tb+qb);){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}m>>>=tb,n-=tb,c.back+=tb}if(m>>>=qb,n-=qb,c.back+=qb,c.length=sb,0===rb){c.mode=hb;break}if(32&rb){c.back=-1,c.mode=V;break}if(64&rb){a.msg="invalid literal/length code",c.mode=lb;break}c.extra=15&rb,c.mode=db;case db:if(c.extra){for(zb=c.extra;zb>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}c.length+=m&(1<<c.extra)-1,m>>>=c.extra,n-=c.extra,c.back+=c.extra}c.was=c.length,c.mode=eb;case eb:for(;Ab=c.distcode[m&(1<<c.distbits)-1],qb=Ab>>>24,rb=Ab>>>16&255,sb=65535&Ab,!(n>=qb);){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}if(0===(240&rb)){for(tb=qb,ub=rb,vb=sb;Ab=c.distcode[vb+((m&(1<<tb+ub)-1)>>tb)],qb=Ab>>>24,rb=Ab>>>16&255,sb=65535&Ab,!(n>=tb+qb);){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}m>>>=tb,n-=tb,c.back+=tb}if(m>>>=qb,n-=qb,c.back+=qb,64&rb){a.msg="invalid distance code",c.mode=lb;break}c.offset=sb,c.extra=15&rb,c.mode=fb;case fb:if(c.extra){for(zb=c.extra;zb>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}c.offset+=m&(1<<c.extra)-1,m>>>=c.extra,n-=c.extra,c.back+=c.extra}if(c.offset>c.dmax){a.msg="invalid distance too far back",c.mode=lb;break}c.mode=gb;case gb:if(0===j)break a;if(q=p-j,c.offset>q){if(q=c.offset-q,q>c.whave&&c.sane){a.msg="invalid distance too far back",c.mode=lb;break}q>c.wnext?(q-=c.wnext,ob=c.wsize-q):ob=c.wnext-q,q>c.length&&(q=c.length),pb=c.window}else pb=f,ob=h-c.offset,q=c.length;q>j&&(q=j),j-=q,c.length-=q;do f[h++]=pb[ob++];while(--q);0===c.length&&(c.mode=cb);break;case hb:if(0===j)break a;f[h++]=c.length,j--,c.mode=cb;break;case ib:if(c.wrap){for(;32>n;){if(0===i)break a;i--,m|=e[g++]<<n,n+=8}if(p-=j,a.total_out+=p,c.total+=p,p&&(a.adler=c.check=c.flags?t(c.check,f,p,h-p):s(c.check,f,p,h-p)),p=j,(c.flags?m:d(m))!==c.check){a.msg="incorrect data check",c.mode=lb;break}m=0,n=0}c.mode=jb;case jb:if(c.wrap&&c.flags){for(;32>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}if(m!==(4294967295&c.total)){a.msg="incorrect length check",c.mode=lb;break}m=0,n=0}c.mode=kb;case kb:xb=D;break a;case lb:xb=G;break a;case mb:return H;case nb:default:return F}return a.next_out=h,a.avail_out=j,a.next_in=g,a.avail_in=i,c.hold=m,c.bits=n,(c.wsize||p!==a.avail_out&&c.mode<lb&&(c.mode<ib||b!==z))&&l(a,a.output,a.next_out,p-a.avail_out)?(c.mode=mb,H):(o-=a.avail_in,p-=a.avail_out,a.total_in+=o,a.total_out+=p,c.total+=p,c.wrap&&p&&(a.adler=c.check=c.flags?t(c.check,f,p,a.next_out-p):s(c.check,f,p,a.next_out-p)),a.data_type=c.bits+(c.last?64:0)+(c.mode===V?128:0)+(c.mode===bb||c.mode===Y?256:0),(0===o&&0===p||b===z)&&xb===C&&(xb=I),xb)}function n(a){if(!a||!a.state)return F;var b=a.state;return b.window&&(b.window=null),a.state=null,C}function o(a,b){var c;return a&&a.state?(c=a.state,0===(2&c.wrap)?F:(c.head=b,b.done=!1,C)):F}var p,q,r=a("../utils/common"),s=a("./adler32"),t=a("./crc32"),u=a("./inffast"),v=a("./inftrees"),w=0,x=1,y=2,z=4,A=5,B=6,C=0,D=1,E=2,F=-2,G=-3,H=-4,I=-5,J=8,K=1,L=2,M=3,N=4,O=5,P=6,Q=7,R=8,S=9,T=10,U=11,V=12,W=13,X=14,Y=15,Z=16,$=17,_=18,ab=19,bb=20,cb=21,db=22,eb=23,fb=24,gb=25,hb=26,ib=27,jb=28,kb=29,lb=30,mb=31,nb=32,ob=852,pb=592,qb=15,rb=qb,sb=!0;c.inflateReset=g,c.inflateReset2=h,c.inflateResetKeep=f,c.inflateInit=j,c.inflateInit2=i,c.inflate=m,c.inflateEnd=n,c.inflateGetHeader=o,c.inflateInfo="pako inflate (from Nodeca project)"},{"../utils/common":27,"./adler32":29,"./crc32":31,"./inffast":34,"./inftrees":36}],36:[function(a,b){"use strict";var c=a("../utils/common"),d=15,e=852,f=592,g=0,h=1,i=2,j=[3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258,0,0],k=[16,16,16,16,16,16,16,16,17,17,17,17,18,18,18,18,19,19,19,19,20,20,20,20,21,21,21,21,16,72,78],l=[1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0],m=[16,16,16,16,17,17,18,18,19,19,20,20,21,21,22,22,23,23,24,24,25,25,26,26,27,27,28,28,29,29,64,64];b.exports=function(a,b,n,o,p,q,r,s){var t,u,v,w,x,y,z,A,B,C=s.bits,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=null,O=0,P=new c.Buf16(d+1),Q=new c.Buf16(d+1),R=null,S=0;for(D=0;d>=D;D++)P[D]=0;for(E=0;o>E;E++)P[b[n+E]]++;for(H=C,G=d;G>=1&&0===P[G];G--);if(H>G&&(H=G),0===G)return p[q++]=20971520,p[q++]=20971520,s.bits=1,0;for(F=1;G>F&&0===P[F];F++);for(F>H&&(H=F),K=1,D=1;d>=D;D++)if(K<<=1,K-=P[D],0>K)return-1;if(K>0&&(a===g||1!==G))return-1;for(Q[1]=0,D=1;d>D;D++)Q[D+1]=Q[D]+P[D];for(E=0;o>E;E++)0!==b[n+E]&&(r[Q[b[n+E]]++]=E);if(a===g?(N=R=r,y=19):a===h?(N=j,O-=257,R=k,S-=257,y=256):(N=l,R=m,y=-1),M=0,E=0,D=F,x=q,I=H,J=0,v=-1,L=1<<H,w=L-1,a===h&&L>e||a===i&&L>f)return 1;for(var T=0;;){T++,z=D-J,r[E]<y?(A=0,B=r[E]):r[E]>y?(A=R[S+r[E]],B=N[O+r[E]]):(A=96,B=0),t=1<<D-J,u=1<<I,F=u;do u-=t,p[x+(M>>J)+u]=z<<24|A<<16|B|0;while(0!==u);for(t=1<<D-1;M&t;)t>>=1;if(0!==t?(M&=t-1,M+=t):M=0,E++,0===--P[D]){if(D===G)break;D=b[n+r[E]]}if(D>H&&(M&w)!==v){for(0===J&&(J=H),x+=F,I=D-J,K=1<<I;G>I+J&&(K-=P[I+J],!(0>=K));)I++,K<<=1;if(L+=1<<I,a===h&&L>e||a===i&&L>f)return 1;v=M&w,p[v]=H<<24|I<<16|x-q|0}}return 0!==M&&(p[x+M]=D-J<<24|64<<16|0),s.bits=H,0}},{"../utils/common":27}],37:[function(a,b){"use strict";b.exports={2:"need dictionary",1:"stream end",0:"","-1":"file error","-2":"stream error","-3":"data error","-4":"insufficient memory","-5":"buffer error","-6":"incompatible version"}},{}],38:[function(a,b,c){"use strict";function d(a){for(var b=a.length;--b>=0;)a[b]=0}function e(a){return 256>a?gb[a]:gb[256+(a>>>7)]}function f(a,b){a.pending_buf[a.pending++]=255&b,a.pending_buf[a.pending++]=b>>>8&255}function g(a,b,c){a.bi_valid>V-c?(a.bi_buf|=b<<a.bi_valid&65535,f(a,a.bi_buf),a.bi_buf=b>>V-a.bi_valid,a.bi_valid+=c-V):(a.bi_buf|=b<<a.bi_valid&65535,a.bi_valid+=c)}function h(a,b,c){g(a,c[2*b],c[2*b+1])}function i(a,b){var c=0;do c|=1&a,a>>>=1,c<<=1;while(--b>0);return c>>>1}function j(a){16===a.bi_valid?(f(a,a.bi_buf),a.bi_buf=0,a.bi_valid=0):a.bi_valid>=8&&(a.pending_buf[a.pending++]=255&a.bi_buf,a.bi_buf>>=8,a.bi_valid-=8)}function k(a,b){var c,d,e,f,g,h,i=b.dyn_tree,j=b.max_code,k=b.stat_desc.static_tree,l=b.stat_desc.has_stree,m=b.stat_desc.extra_bits,n=b.stat_desc.extra_base,o=b.stat_desc.max_length,p=0;for(f=0;U>=f;f++)a.bl_count[f]=0;for(i[2*a.heap[a.heap_max]+1]=0,c=a.heap_max+1;T>c;c++)d=a.heap[c],f=i[2*i[2*d+1]+1]+1,f>o&&(f=o,p++),i[2*d+1]=f,d>j||(a.bl_count[f]++,g=0,d>=n&&(g=m[d-n]),h=i[2*d],a.opt_len+=h*(f+g),l&&(a.static_len+=h*(k[2*d+1]+g)));if(0!==p){do{for(f=o-1;0===a.bl_count[f];)f--;a.bl_count[f]--,a.bl_count[f+1]+=2,a.bl_count[o]--,p-=2}while(p>0);for(f=o;0!==f;f--)for(d=a.bl_count[f];0!==d;)e=a.heap[--c],e>j||(i[2*e+1]!==f&&(a.opt_len+=(f-i[2*e+1])*i[2*e],i[2*e+1]=f),d--)}}function l(a,b,c){var d,e,f=new Array(U+1),g=0;for(d=1;U>=d;d++)f[d]=g=g+c[d-1]<<1;for(e=0;b>=e;e++){var h=a[2*e+1];0!==h&&(a[2*e]=i(f[h]++,h))}}function m(){var a,b,c,d,e,f=new Array(U+1);for(c=0,d=0;O-1>d;d++)for(ib[d]=c,a=0;a<1<<_[d];a++)hb[c++]=d;for(hb[c-1]=d,e=0,d=0;16>d;d++)for(jb[d]=e,a=0;a<1<<ab[d];a++)gb[e++]=d;for(e>>=7;R>d;d++)for(jb[d]=e<<7,a=0;a<1<<ab[d]-7;a++)gb[256+e++]=d;for(b=0;U>=b;b++)f[b]=0;for(a=0;143>=a;)eb[2*a+1]=8,a++,f[8]++;for(;255>=a;)eb[2*a+1]=9,a++,f[9]++;for(;279>=a;)eb[2*a+1]=7,a++,f[7]++;for(;287>=a;)eb[2*a+1]=8,a++,f[8]++;for(l(eb,Q+1,f),a=0;R>a;a++)fb[2*a+1]=5,fb[2*a]=i(a,5);kb=new nb(eb,_,P+1,Q,U),lb=new nb(fb,ab,0,R,U),mb=new nb(new Array(0),bb,0,S,W)}function n(a){var b;for(b=0;Q>b;b++)a.dyn_ltree[2*b]=0;for(b=0;R>b;b++)a.dyn_dtree[2*b]=0;for(b=0;S>b;b++)a.bl_tree[2*b]=0;a.dyn_ltree[2*X]=1,a.opt_len=a.static_len=0,a.last_lit=a.matches=0}function o(a){a.bi_valid>8?f(a,a.bi_buf):a.bi_valid>0&&(a.pending_buf[a.pending++]=a.bi_buf),a.bi_buf=0,a.bi_valid=0}function p(a,b,c,d){o(a),d&&(f(a,c),f(a,~c)),E.arraySet(a.pending_buf,a.window,b,c,a.pending),a.pending+=c}function q(a,b,c,d){var e=2*b,f=2*c;return a[e]<a[f]||a[e]===a[f]&&d[b]<=d[c]}function r(a,b,c){for(var d=a.heap[c],e=c<<1;e<=a.heap_len&&(e<a.heap_len&&q(b,a.heap[e+1],a.heap[e],a.depth)&&e++,!q(b,d,a.heap[e],a.depth));)a.heap[c]=a.heap[e],c=e,e<<=1;a.heap[c]=d}function s(a,b,c){var d,f,i,j,k=0;if(0!==a.last_lit)do d=a.pending_buf[a.d_buf+2*k]<<8|a.pending_buf[a.d_buf+2*k+1],f=a.pending_buf[a.l_buf+k],k++,0===d?h(a,f,b):(i=hb[f],h(a,i+P+1,b),j=_[i],0!==j&&(f-=ib[i],g(a,f,j)),d--,i=e(d),h(a,i,c),j=ab[i],0!==j&&(d-=jb[i],g(a,d,j)));while(k<a.last_lit);h(a,X,b)}function t(a,b){var c,d,e,f=b.dyn_tree,g=b.stat_desc.static_tree,h=b.stat_desc.has_stree,i=b.stat_desc.elems,j=-1;for(a.heap_len=0,a.heap_max=T,c=0;i>c;c++)0!==f[2*c]?(a.heap[++a.heap_len]=j=c,a.depth[c]=0):f[2*c+1]=0;for(;a.heap_len<2;)e=a.heap[++a.heap_len]=2>j?++j:0,f[2*e]=1,a.depth[e]=0,a.opt_len--,h&&(a.static_len-=g[2*e+1]);for(b.max_code=j,c=a.heap_len>>1;c>=1;c--)r(a,f,c);e=i;do c=a.heap[1],a.heap[1]=a.heap[a.heap_len--],r(a,f,1),d=a.heap[1],a.heap[--a.heap_max]=c,a.heap[--a.heap_max]=d,f[2*e]=f[2*c]+f[2*d],a.depth[e]=(a.depth[c]>=a.depth[d]?a.depth[c]:a.depth[d])+1,f[2*c+1]=f[2*d+1]=e,a.heap[1]=e++,r(a,f,1);while(a.heap_len>=2);a.heap[--a.heap_max]=a.heap[1],k(a,b),l(f,j,a.bl_count)}function u(a,b,c){var d,e,f=-1,g=b[1],h=0,i=7,j=4;for(0===g&&(i=138,j=3),b[2*(c+1)+1]=65535,d=0;c>=d;d++)e=g,g=b[2*(d+1)+1],++h<i&&e===g||(j>h?a.bl_tree[2*e]+=h:0!==e?(e!==f&&a.bl_tree[2*e]++,a.bl_tree[2*Y]++):10>=h?a.bl_tree[2*Z]++:a.bl_tree[2*$]++,h=0,f=e,0===g?(i=138,j=3):e===g?(i=6,j=3):(i=7,j=4))}function v(a,b,c){var d,e,f=-1,i=b[1],j=0,k=7,l=4;for(0===i&&(k=138,l=3),d=0;c>=d;d++)if(e=i,i=b[2*(d+1)+1],!(++j<k&&e===i)){if(l>j){do h(a,e,a.bl_tree);while(0!==--j)}else 0!==e?(e!==f&&(h(a,e,a.bl_tree),j--),h(a,Y,a.bl_tree),g(a,j-3,2)):10>=j?(h(a,Z,a.bl_tree),g(a,j-3,3)):(h(a,$,a.bl_tree),g(a,j-11,7));j=0,f=e,0===i?(k=138,l=3):e===i?(k=6,l=3):(k=7,l=4)}}function w(a){var b;for(u(a,a.dyn_ltree,a.l_desc.max_code),u(a,a.dyn_dtree,a.d_desc.max_code),t(a,a.bl_desc),b=S-1;b>=3&&0===a.bl_tree[2*cb[b]+1];b--);return a.opt_len+=3*(b+1)+5+5+4,b}function x(a,b,c,d){var e;for(g(a,b-257,5),g(a,c-1,5),g(a,d-4,4),e=0;d>e;e++)g(a,a.bl_tree[2*cb[e]+1],3);v(a,a.dyn_ltree,b-1),v(a,a.dyn_dtree,c-1)}function y(a){var b,c=4093624447;for(b=0;31>=b;b++,c>>>=1)if(1&c&&0!==a.dyn_ltree[2*b])return G;if(0!==a.dyn_ltree[18]||0!==a.dyn_ltree[20]||0!==a.dyn_ltree[26])return H;for(b=32;P>b;b++)if(0!==a.dyn_ltree[2*b])return H;return G}function z(a){pb||(m(),pb=!0),a.l_desc=new ob(a.dyn_ltree,kb),a.d_desc=new ob(a.dyn_dtree,lb),a.bl_desc=new ob(a.bl_tree,mb),a.bi_buf=0,a.bi_valid=0,n(a)}function A(a,b,c,d){g(a,(J<<1)+(d?1:0),3),p(a,b,c,!0)}function B(a){g(a,K<<1,3),h(a,X,eb),j(a)}function C(a,b,c,d){var e,f,h=0;a.level>0?(a.strm.data_type===I&&(a.strm.data_type=y(a)),t(a,a.l_desc),t(a,a.d_desc),h=w(a),e=a.opt_len+3+7>>>3,f=a.static_len+3+7>>>3,e>=f&&(e=f)):e=f=c+5,e>=c+4&&-1!==b?A(a,b,c,d):a.strategy===F||f===e?(g(a,(K<<1)+(d?1:0),3),s(a,eb,fb)):(g(a,(L<<1)+(d?1:0),3),x(a,a.l_desc.max_code+1,a.d_desc.max_code+1,h+1),s(a,a.dyn_ltree,a.dyn_dtree)),n(a),d&&o(a)}function D(a,b,c){return a.pending_buf[a.d_buf+2*a.last_lit]=b>>>8&255,a.pending_buf[a.d_buf+2*a.last_lit+1]=255&b,a.pending_buf[a.l_buf+a.last_lit]=255&c,a.last_lit++,0===b?a.dyn_ltree[2*c]++:(a.matches++,b--,a.dyn_ltree[2*(hb[c]+P+1)]++,a.dyn_dtree[2*e(b)]++),a.last_lit===a.lit_bufsize-1}var E=a("../utils/common"),F=4,G=0,H=1,I=2,J=0,K=1,L=2,M=3,N=258,O=29,P=256,Q=P+1+O,R=30,S=19,T=2*Q+1,U=15,V=16,W=7,X=256,Y=16,Z=17,$=18,_=[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0],ab=[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13],bb=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7],cb=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],db=512,eb=new Array(2*(Q+2));d(eb);var fb=new Array(2*R);d(fb);var gb=new Array(db);d(gb);var hb=new Array(N-M+1);d(hb);var ib=new Array(O);d(ib);var jb=new Array(R);d(jb);var kb,lb,mb,nb=function(a,b,c,d,e){this.static_tree=a,this.extra_bits=b,this.extra_base=c,this.elems=d,this.max_length=e,this.has_stree=a&&a.length},ob=function(a,b){this.dyn_tree=a,this.max_code=0,this.stat_desc=b},pb=!1;c._tr_init=z,c._tr_stored_block=A,c._tr_flush_block=C,c._tr_tally=D,c._tr_align=B},{"../utils/common":27}],39:[function(a,b){"use strict";function c(){this.input=null,this.next_in=0,this.avail_in=0,this.total_in=0,this.output=null,this.next_out=0,this.avail_out=0,this.total_out=0,this.msg="",this.state=null,this.data_type=2,this.adler=0}b.exports=c},{}]},{},[9])(9)});
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -46,6 +46,7 @@ class CagetteProduct(models.Model): ...@@ -46,6 +46,7 @@ class CagetteProduct(models.Model):
return res return res
@staticmethod @staticmethod
def get_product_info_for_label_from_template_id(template_id): def get_product_info_for_label_from_template_id(template_id):
"""Get product info for label.""" """Get product info for label."""
...@@ -54,14 +55,26 @@ class CagetteProduct(models.Model): ...@@ -54,14 +55,26 @@ class CagetteProduct(models.Model):
fields = ['barcode', 'product_tmpl_id', 'pricetag_rackinfos', fields = ['barcode', 'product_tmpl_id', 'pricetag_rackinfos',
'price_weight_net', 'price_volume', 'list_price', 'price_weight_net', 'price_volume', 'list_price',
'weight_net', 'volume', 'to_weight'] 'weight_net', 'volume', 'to_weight']
fields += getattr(settings, 'SHELF_LABELS_ADD_FIELDS', []) additionnal_fields = getattr(settings, 'SHELF_LABELS_ADD_FIELDS', [])
return api.search_read('product.product', cond, fields) fields += additionnal_fields
product_data = api.search_read('product.product', cond, fields)
if product_data and 'suppliers' in additionnal_fields:
cond = [['product_tmpl_id.id', '=', template_id]]
fields = ['name']
suppliers = api.search_read('product.supplierinfo', cond, fields)
if suppliers:
suppliers_name = []
for s in suppliers:
suppliers_name.append(s['name'][1])
product_data[0]['suppliers'] = ', '.join(list(set(suppliers_name)))
return product_data
@staticmethod @staticmethod
def generate_label_for_printing(templ_id, directory, price=None, nb=None): def generate_label_for_printing(templ_id, directory, price=None, nb=None):
res = {} res = {}
try: try:
p = CagetteProduct.get_product_info_for_label_from_template_id(templ_id) p = CagetteProduct.get_product_info_for_label_from_template_id(templ_id)
if (p and p[0]['product_tmpl_id'][0] == int(templ_id)): if (p and p[0]['product_tmpl_id'][0] == int(templ_id)):
product = p[0] product = p[0]
txt = '' txt = ''
...@@ -91,6 +104,18 @@ class CagetteProduct(models.Model): ...@@ -91,6 +104,18 @@ class CagetteProduct(models.Model):
coop_logger.error("Generate label : %s %s", templ_id, str(e)) coop_logger.error("Generate label : %s %s", templ_id, str(e))
return res return res
@staticmethod
def register_start_supplier_shortage(product_id, partner_id, date_start):
"""Start a supplier shortage for a product"""
api = OdooAPI()
f = {
'product_id' : product_id,
'partner_id' : partner_id,
'date_start' : date_start,
}
res = api.create('product.supplier.shortage', f)
return res
class CagetteProducts(models.Model): class CagetteProducts(models.Model):
"""Initially used to make massive barcode update.""" """Initially used to make massive barcode update."""
...@@ -166,6 +191,8 @@ class CagetteProducts(models.Model): ...@@ -166,6 +191,8 @@ class CagetteProducts(models.Model):
def get_products_for_label_appli(withCandidate=False): def get_products_for_label_appli(withCandidate=False):
fields = ['sale_ok', 'uom_id', 'barcode', fields = ['sale_ok', 'uom_id', 'barcode',
'name', 'display_name', 'list_price', 'categ_id', 'image_medium'] 'name', 'display_name', 'list_price', 'categ_id', 'image_medium']
if getattr(settings, 'EXPORT_POS_CAT_FOR_SCALES', False) is True:
fields.append('pos_categ_id')
to_weight = CagetteProducts.get_products_to_weight(withCandidate, fields) to_weight = CagetteProducts.get_products_to_weight(withCandidate, fields)
if len(vcats) > 0: if len(vcats) > 0:
vrac = CagetteProducts.get_vrac_products_from_cats(vcats, withCandidate, fields) vrac = CagetteProducts.get_vrac_products_from_cats(vcats, withCandidate, fields)
...@@ -182,6 +209,19 @@ class CagetteProducts(models.Model): ...@@ -182,6 +209,19 @@ class CagetteProducts(models.Model):
fields = ['uom_id', 'display_name','barcode'] fields = ['uom_id', 'display_name','barcode']
return api.search_read('product.product', cond, fields) return api.search_read('product.product', cond, fields)
@staticmethod
def get_pos_categories():
api = OdooAPI()
fields = ['name', 'parent_id', 'sequence', 'image_small']
try:
res = api.search_read('pos.category', [], fields)
except Exception as e:
coop_logger.error('Getting POS categories : %s', str(e))
res = []
return res
@staticmethod @staticmethod
def get_all_barcodes(): def get_all_barcodes():
"""Needs lacagette_products Odoo module to be active.""" """Needs lacagette_products Odoo module to be active."""
...@@ -195,6 +235,12 @@ class CagetteProducts(models.Model): ...@@ -195,6 +235,12 @@ class CagetteProducts(models.Model):
for p in res['list']: for p in res['list']:
# transcode result to compact format (for bandwith save and browser memory) # transcode result to compact format (for bandwith save and browser memory)
# real size / 4 (for 2750 products) # real size / 4 (for 2750 products)
# following 2 lines is only useful for La Cagette (changing uom_id in Database has cascade effects...)
# TODO : Use mapping list in config.py
if p['uom_id'] == 3:
p['uom_id'] = 21
if p['uom_id'] == 20:
p['uom_id'] = 1
result['pdts'][p['barcode']] = [ result['pdts'][p['barcode']] = [
p['display_name'], p['display_name'],
p['sale_ok'], p['sale_ok'],
...@@ -202,6 +248,7 @@ class CagetteProducts(models.Model): ...@@ -202,6 +248,7 @@ class CagetteProducts(models.Model):
p['available_in_pos'], p['available_in_pos'],
p['id'], p['id'],
p['standard_price'], p['standard_price'],
p['list_price'],
p['uom_id']] p['uom_id']]
if 'uoms' in res and 'list' in res['uoms']: if 'uoms' in res and 'list' in res['uoms']:
result['uoms'] = res['uoms']['list'] result['uoms'] = res['uoms']['list']
...@@ -264,18 +311,32 @@ class CagetteProducts(models.Model): ...@@ -264,18 +311,32 @@ class CagetteProducts(models.Model):
@staticmethod @staticmethod
def get_barcode_rules(): def get_barcode_rules():
c = [['type', 'in', ['FF_price_to_weight', 'price', 'price_to_weight', 'product', 'weight' ]], ['barcode_nomenclature_id','=', 1]] result = {'patterns': [], 'aliases': {}}
rules = OdooAPI().search_read('barcode.rule', c, ['pattern'], order="sequence ASC") try:
# As rules are ordered by sequence, let's find where to stop (.* pattern) import re
stop_idx = len(rules) - 1 c = [['type', 'in', ['FF_price_to_weight', 'price', 'price_to_weight', 'product', 'weight', 'alias']], ['barcode_nomenclature_id','=', 1]]
i = 0 rules = OdooAPI().search_read('barcode.rule', c, ['pattern', 'type', 'alias'], order="sequence ASC")
for r in rules: # As rules are ordered by sequence, let's find where to stop (.* pattern)
if r['pattern'] == ".*": stop_idx = len(rules) - 1
stop_idx = i i = 0
i += 1 for r in rules:
if stop_idx > 0: if r['pattern'] == ".*":
rules = rules[:stop_idx - 1] stop_idx = i
return rules i += 1
if stop_idx > 0:
rules = rules[:stop_idx - 1]
for r in rules:
if r['type'] == 'alias':
alias_bc = re.sub('[^0-9]', '', r['pattern'])
if len(alias_bc) > 0:
result['aliases'][alias_bc] = r['alias']
elif '{' in r['pattern'] or '.' in r['pattern']:
result['patterns'].append(r)
except Exception as e:
result['error'] = str(e)
coop_logger.error("Get Barcode Rules : %s", str(e))
# coop_logger.info("Fin get bc rules : %s", str(result))
return result
@staticmethod @staticmethod
...@@ -283,16 +344,16 @@ class CagetteProducts(models.Model): ...@@ -283,16 +344,16 @@ class CagetteProducts(models.Model):
import re import re
from outils.functions import computeEAN13Check from outils.functions import computeEAN13Check
bc_map = {} bc_map = {}
rules = CagetteProducts.get_barcode_rules() rules = CagetteProducts.get_barcode_rules()
rules = rules['patterns']
# now, just keep rules with N in pattern # now, just keep rules with N in pattern
rules = list(filter(lambda x: '.' in x['pattern'], rules)) rules = list(filter(lambda x: '.' in x['pattern'], rules))
rules = list(map(lambda x: x['pattern'], rules)) rules = list(map(lambda x: x['pattern'], rules))
# now remove {NN...} from pattern # now remove {NN...} from pattern
rules = list(map(lambda x: re.sub(r'{.+}', '', x), rules)) rules = list(map(lambda x: re.sub(r'{.+}', '', x), rules))
# coop_logger.info('rules = %s', rules)
# now compile regex for pattern # now compile regex for pattern
regex = [] regex = []
for r in rules: for r in rules:
......
...@@ -13,11 +13,12 @@ IFCBarcodes = { ...@@ -13,11 +13,12 @@ IFCBarcodes = {
closeModal(); closeModal();
if (typeof bc_data.res.error == "undefined") { if (typeof bc_data.res.error == "undefined") {
this.patterns = bc_data.res.patterns; this.patterns = bc_data.res.patterns;
this.aliases = bc_data.res.aliases;
this.codes = bc_data.res.list.pdts; this.codes = bc_data.res.list.pdts;
this.uoms = bc_data.res.list.uoms; this.uoms = bc_data.res.list.uoms;
this.keys = bc_data.res.keys; this.keys = bc_data.res.keys;
} else { } else {
this.errors.push(bc_data.error); this.errors.push(bc_data.res.error);
} }
} catch (e) { } catch (e) {
err = {msg: e.name + ' : ' + e.message, ctx: 'retrieve barcodes'}; err = {msg: e.name + ' : ' + e.message, ctx: 'retrieve barcodes'};
...@@ -33,26 +34,33 @@ IFCBarcodes = { ...@@ -33,26 +34,33 @@ IFCBarcodes = {
}, },
get_corresponding_odoo_product: function(bc) { get_corresponding_odoo_product: function(bc) {
//console.log('To analyze :' + bc) //console.log('To analyze :' + bc)
var odoo_product = null;
var index = 0, var index = 0,
pattern_found = false, pattern_found = false,
encoded_value = ''; is_alias = false,
encoded_value = '',
pattern_type = '',
odoo_product = null,
product_data = null;
// Let's find out if it matches a pattern // Let's find out if it matches a pattern
while (index < this.patterns.length -1 && pattern_found === false) { while (index < this.patterns.length -1 && pattern_found === false) {
var pattern = this.patterns[index]; var pattern = this.patterns[index].pattern;
var significant_prefix = pattern.replace(/[^0-9]/g, ''); //remove all but figures var significant_prefix = pattern.replace(/[^0-9]/g, ''); //remove all but figures
if (bc.indexOf(significant_prefix) === 0) { if (bc.indexOf(significant_prefix) === 0) {
// console.log(pattern) /*
For example,
bc = 0493213018809
pattern = 0493...{NNDDD}
*/
//console.log(pattern)
// console.log(bc) // console.log(bc)
//0493...{NNDDD} (pattern) odoo_bc = '';
//0493213018809 (bc)
pattern_found = true; pattern_found = true;
pattern_type = this.patterns[index].type;
pattern = pattern.replace(/[^0-9.ND]/, ''); pattern = pattern.replace(/[^0-9.ND]/, '');
bc = bc.slice(0, -1); // remove original check figure bc = bc.slice(0, -1); // remove original check figure
odoo_bc = '';
// Read pattern character by character // Read pattern character by character
for (var i = 0; i < pattern.length; i++) { for (var i = 0; i < pattern.length; i++) {
if (/[0-9]/.exec(pattern[i])) { if (/[0-9]/.exec(pattern[i])) {
...@@ -72,13 +80,39 @@ IFCBarcodes = { ...@@ -72,13 +80,39 @@ IFCBarcodes = {
index++; index++;
} }
// let's seek "normalized" bc in codes array // let's seek "normalized" bc in codes array or alias map
for (code in this.codes) { for (alias in this.aliases) {
if (code == bc) { if (bc == alias) {
odoo_product = {barcode: code, data: this.codes[code], value: encoded_value}; is_alias = true;
for (barcode in this.codes) {
if (barcode == this.aliases[alias]) {
product_data = this.codes[barcode];
}
}
}
}
if (is_alias === false) {
for (code in this.codes) {
if (code == bc) {
product_data = this.codes[code];
}
}
}
if (product_data !== null) {
p_uom = (this.uoms)[product_data[this.keys.uom_id]];
if (encoded_value.length > 0 && !isNaN(encoded_value)) {
if (p_uom == 'Unit(s)' || p_uom == 'unité') {
encoded_value = parseInt(encoded_value, 10);
} else {
encoded_value = parseFloat(encoded_value);
}
} }
odoo_product = {barcode: bc, data: product_data, rule: pattern_type, value: encoded_value};
} }
//console.log(odoo_product)
return odoo_product; return odoo_product;
} }
}; };
...@@ -93,5 +127,6 @@ init_barcodes = async function() { ...@@ -93,5 +127,6 @@ init_barcodes = async function() {
else else
result = ifcb; result = ifcb;
// console.log(result.patterns) // console.log(result.patterns)
return result; return result;
}; };
from django.test import TestCase from django.test import TestCase
# Create your tests here.
...@@ -83,7 +83,13 @@ def labels_appli_csv(request, params): ...@@ -83,7 +83,13 @@ def labels_appli_csv(request, params):
try: try:
if (params == '/wc'): if (params == '/wc'):
withCandidate = True withCandidate = True
with_pos_categories = getattr(settings, 'EXPORT_POS_CAT_FOR_SCALES', False)
products = CagetteProducts.get_products_for_label_appli(withCandidate) products = CagetteProducts.get_products_for_label_appli(withCandidate)
if with_pos_categories is True:
pos_categories = CagetteProducts.get_pos_categories()
else:
pos_categories = []
rows = [] rows = []
for p in products: for p in products:
if (p['sale_ok'] is True): if (p['sale_ok'] is True):
...@@ -96,28 +102,31 @@ def labels_appli_csv(request, params): ...@@ -96,28 +102,31 @@ def labels_appli_csv(request, params):
barcode = '' barcode = ''
if not (barcode.isnumeric()): if not (barcode.isnumeric()):
barcode = '' barcode = ''
p_row = [p['id'], p['display_name'], barcode,
rows.append([p['id'], p['display_name'], barcode, p['list_price'],
p['list_price'], p['categ'],
# p['categ_id'][1], uom,
p['categ'], p['image'].replace("\n", "")]
uom, if with_pos_categories is True:
# p['image'].replace(img_temp_folder, ""), if p['pos_categ_id']:
p['image'].replace("\n", ""), p_row.append(p['pos_categ_id'][0])
# p['available_in_pos'] # ,p['sale_ok'] else:
]) p_row.append('')
rows.append(p_row)
header = ['id', 'nom', 'code-barre', 'prix', header = ['id', 'nom', 'code-barre', 'prix',
'categorie', 'unite', 'image' 'categorie', 'unite', 'image'
# 'en vente', 'sale_ok' # 'en vente', 'sale_ok'
] ]
if with_pos_categories is True and len(pos_categories) > 0:
header.append('id_categorie_pos')
with open(settings.DAV_PATH + '/pos_categories.json', 'w') as outfile:
json.dump(pos_categories, outfile)
os_file = settings.DAV_PATH + '/flv.csv' os_file = settings.DAV_PATH + '/flv.csv'
file_copies = [] file_copies = []
nb = 1 nb = int(getattr(settings, 'FLV_CSV_NB', 1))
if hasattr(settings, 'FLV_CSV_NB'):
nb = int(settings.FLV_CSV_NB)
for i in range(1, nb + 1): for i in range(1, nb + 1):
file_copies.append(settings.DAV_PATH + '/flv_' + str(i) + '.csv') file_copies.append(settings.DAV_PATH + '/flv_' + str(i) + '.csv')
...@@ -133,6 +142,8 @@ def labels_appli_csv(request, params): ...@@ -133,6 +142,8 @@ def labels_appli_csv(request, params):
file.close() file.close()
for c in file_copies: for c in file_copies:
copyfile(os_file, c) copyfile(os_file, c)
except Exception as e: except Exception as e:
res['error'] = str(e) res['error'] = str(e)
return JsonResponse({'res': res}) return JsonResponse({'res': res})
...@@ -176,14 +187,14 @@ def get_all_barcodes(request): ...@@ -176,14 +187,14 @@ def get_all_barcodes(request):
'available_in_pos': 3, 'available_in_pos': 3,
'id': 4, 'id': 4,
'standard_price': 5, 'standard_price': 5,
'uom_id': 6 'list_price': 6,
'uom_id': 7
} }
rules = CagetteProducts.get_barcode_rules() rules = CagetteProducts.get_barcode_rules()
res['patterns'] = rules['patterns']
res['aliases'] = rules['aliases']
res['time'] = int(round(time.time() * 1000)) - start res['time'] = int(round(time.time() * 1000)) - start
res['patterns'] = []
for r in rules:
if '{' in r['pattern'] or '.' in r['pattern']:
res['patterns'].append(r['pattern'])
except Exception as e: except Exception as e:
coop_logger.error("products_barcodes : %s", str(e)) coop_logger.error("products_barcodes : %s", str(e))
res['error'] = str(e) res['error'] = str(e)
......
...@@ -231,4 +231,8 @@ hr { ...@@ -231,4 +231,8 @@ hr {
white-space: pre-wrap; white-space: pre-wrap;
} }
#main_content {width: 100%;} #main_content {width: 100%;}
\ No newline at end of file
.select_product_action {
max-width: 15px;
}
...@@ -38,7 +38,6 @@ function group_goto(i) { ...@@ -38,7 +38,6 @@ function group_goto(i) {
* Set local storage for given order * Set local storage for given order
*/ */
function set_local_storage(order_data) { function set_local_storage(order_data) {
console.log(order_data);
if (Modernizr.localstorage) { if (Modernizr.localstorage) {
var stored_order = JSON.parse(localStorage.getItem('order_' + order_data.id)); var stored_order = JSON.parse(localStorage.getItem('order_' + order_data.id));
......
...@@ -26,11 +26,11 @@ var reception_status, ...@@ -26,11 +26,11 @@ var reception_status,
editing_product = null, // Store the product currently being edited editing_product = null, // Store the product currently being edited
editing_origin, // Keep track of where editing_product comes from editing_origin, // Keep track of where editing_product comes from
processed_row_counter = 0, // Order in which products were added in processed list processed_row_counter = 0, // Order in which products were added in processed list
search_chars = [],
user_comments = "", user_comments = "",
updatedProducts = [], // Keep record of updated products updatedProducts = [], // Keep record of updated products
validProducts = [], // Keep record of directly validated products validProducts = [], // Keep record of directly validated products
updateType = ""; // step 1: qty_valid; step2: br_valid updateType = "", // step 1: qty_valid; step2: br_valid
barcodes = null; // Barcodes stored locally
/* UTILS */ /* UTILS */
...@@ -64,21 +64,31 @@ function searchUpdatedProduct() { ...@@ -64,21 +64,31 @@ function searchUpdatedProduct() {
function select_product_from_bc(barcode) { function select_product_from_bc(barcode) {
try { try {
if (editing_product == null) { if (editing_product == null) {
let p = barcodes.get_corresponding_odoo_product(barcode);
if (p == null) {
alert("Le code-barre " + barcode + " ne correspond à aucun article connu.");
return -1;
}
var found = {data: null, place: null}; var found = {data: null, place: null};
$.each(list_to_process, function(i, e) { $.each(list_to_process, function(i, e) {
if (e.barcode == barcode) { if (e.product_id[0] == p.data[barcodes['keys']['id']]) {
found.data = e; found.data = e;
found.place = 'to_process'; found.place = 'to_process';
} }
}); });
$.each(list_processed, function(i, e) { if (found.data != null) {
if (e.barcode == barcode) { $.each(list_processed, function(i, e) {
found.data = e; if (e.product_id[0] == p.data[barcodes['keys']['id']]) {
found.place = 'processed'; found.data = e;
} found.place = 'processed';
}); }
});
}
if (found.data !== null) { if (found.data !== null) {
setLineEdition(found.data); setLineEdition(found.data);
...@@ -220,7 +230,7 @@ function initLists() { ...@@ -220,7 +230,7 @@ function initLists() {
{ {
data:"product_id.1", data:"product_id.1",
title:"Produit", title:"Produit",
width: "50%", width: "45%",
render: function (data, type, full, meta) { render: function (data, type, full, meta) {
// Add tooltip with barcode over product name // Add tooltip with barcode over product name
let display_barcode = "Aucun"; let display_barcode = "Aucun";
...@@ -258,6 +268,12 @@ function initLists() { ...@@ -258,6 +268,12 @@ function initLists() {
defaultContent: "<a class='btn' id='toProcess_line_valid' href='#'><i class='far fa-check-square'></i></a>", defaultContent: "<a class='btn' id='toProcess_line_valid' href='#'><i class='far fa-check-square'></i></a>",
className:"dt-body-center", className:"dt-body-center",
orderable: false orderable: false
},
{
title:"Autres",
defaultContent: "<select class='select_product_action'><option value=''></option><option value='supplier_shortage'>Rupture fournisseur</option></select>",
className:"dt-body-center",
orderable: false
} }
], ],
rowId : "product_id.0", rowId : "product_id.0",
...@@ -283,7 +299,7 @@ function initLists() { ...@@ -283,7 +299,7 @@ function initLists() {
{ {
data:"product_id.1", data:"product_id.1",
title:"Produit", title:"Produit",
width: "60%", width: "55%",
render: function (data, type, full, meta) { render: function (data, type, full, meta) {
// Add tooltip with barcode over product name // Add tooltip with barcode over product name
let display_barcode = "Aucun"; let display_barcode = "Aucun";
...@@ -292,9 +308,17 @@ function initLists() { ...@@ -292,9 +308,17 @@ function initLists() {
display_barcode = full.barcode; display_barcode = full.barcode;
} }
return '<div class="tooltip">' + data let display = '<div class="tooltip">' + data
+ ' <span class="tooltiptext tt_twolines">Code barre : ' + ' <span class="tooltiptext tt_twolines">Code barre : '
+ display_barcode + '</span> </div>'; + display_barcode + '</span> </div>';
if (full.supplier_shortage) {
display += ' <div class="tooltip"><i class="fas fa-info-circle"></i>'
+ ' <span class="tooltiptext tt_twolines">Rupture fournisseur'
+ '</span> </div>';
}
return display;
} }
}, },
{data:"product_uom.1", title: "Unité vente", className:"dt-body-center", orderable: false}, {data:"product_uom.1", title: "Unité vente", className:"dt-body-center", orderable: false},
...@@ -315,6 +339,19 @@ function initLists() { ...@@ -315,6 +339,19 @@ function initLists() {
defaultContent: "<a class='btn' id='processed_line_edit' href='#'><i class='far fa-edit'></i></a>", defaultContent: "<a class='btn' id='processed_line_edit' href='#'><i class='far fa-edit'></i></a>",
className:"dt-body-center", className:"dt-body-center",
orderable: false orderable: false
},
{
title:"Autres",
className:"dt-body-center",
orderable: false,
render: function (data, type, full, meta) {
let disabled = (full.supplier_shortage) ? "disabled" : '';
return "<select class='select_product_action'>"
+ "<option value=''></option>"
+ "<option value='supplier_shortage' "+disabled+">Rupture fournisseur</option>"
+ "</select>";
}
} }
], ],
rowId : "product_id.0", rowId : "product_id.0",
...@@ -386,7 +423,6 @@ function initLists() { ...@@ -386,7 +423,6 @@ function initLists() {
$('table.dataTable').DataTable() $('table.dataTable').DataTable()
.search('') .search('')
.draw(); .draw();
search_chars = [];
} }
} catch (e) { } catch (e) {
err = {msg: e.name + ' : ' + e.message, ctx: 'initLists : listener edit line from list to process'}; err = {msg: e.name + ' : ' + e.message, ctx: 'initLists : listener edit line from list to process'};
...@@ -395,6 +431,38 @@ function initLists() { ...@@ -395,6 +431,38 @@ function initLists() {
} }
}); });
$('#table_to_process tbody').on('change', '.select_product_action', function () {
try {
if ($(this).val() == 'supplier_shortage') {
var row = table_to_process.row($(this).parents('tr'));
var data = row.data();
var modal_shortage = $('#modal_set_supplier_shortage');
modal_shortage.find(".supplier_shortage_product").text(' ' + data.product_id[1]);
modal_shortage.find(".supplier_shortage_supplier").text(' ' + data.partner_id[1]);
openModal(
modal_shortage.html(),
function() {
set_supplier_shortage(row, data);
},
'Valider',
true,
true,
function() {
$(".select_product_action").val('');
}
);
}
} catch (e) {
err = {msg: e.name + ' : ' + e.message, ctx: 'initLists : listener set supplier shortage'};
console.error(err);
report_JS_error(err, 'reception');
}
});
// Edit processed line // Edit processed line
$('#table_processed tbody').on('click', 'a#processed_line_edit', function () { $('#table_processed tbody').on('click', 'a#processed_line_edit', function () {
try { try {
...@@ -412,7 +480,6 @@ function initLists() { ...@@ -412,7 +480,6 @@ function initLists() {
$('table.dataTable').DataTable() $('table.dataTable').DataTable()
.search('') .search('')
.draw(); .draw();
search_chars = [];
} }
} catch (e) { } catch (e) {
err = { err = {
...@@ -424,6 +491,37 @@ function initLists() { ...@@ -424,6 +491,37 @@ function initLists() {
} }
}); });
$('#table_processed tbody').on('change', '.select_product_action', function () {
try {
if ($(this).val() == 'supplier_shortage') {
var row = table_processed.row($(this).parents('tr'));
var data = row.data();
var modal_shortage = $('#modal_set_supplier_shortage');
modal_shortage.find(".supplier_shortage_product").text(' ' + data.product_id[1]);
modal_shortage.find(".supplier_shortage_supplier").text(' ' + data.partner_id[1]);
openModal(
modal_shortage.html(),
function() {
set_supplier_shortage(row, data, true);
},
'Valider',
true,
true,
function() {
$(".select_product_action").val('');
}
);
}
} catch (e) {
err = {msg: e.name + ' : ' + e.message, ctx: 'initLists : listener set supplier shortage'};
console.error(err);
report_JS_error(err, 'reception');
}
});
// Search input for both tables // Search input for both tables
$('#search_input').on('keyup', function () { $('#search_input').on('keyup', function () {
try { try {
...@@ -550,6 +648,87 @@ function remove_from_processed(row, product) { ...@@ -550,6 +648,87 @@ function remove_from_processed(row, product) {
} }
} }
// Indicate the product is on supplier shortage.
// Direct validation from to_process & set qty to 0
function set_supplier_shortage(row, product, from_processed = false) {
try {
product.supplier_shortage = true;
// Step 1: set qty to 0
if (reception_status == 'False') {
if (!from_processed) {
product.old_qty = product.product_qty;
}
product.product_qty = 0;
// Step 2: for consistency purposes, updated products need these fields to be set
} else {
if (!from_processed) {
product.old_price_unit = product.price_unit;
product.new_shelf_price = null;
}
}
// Create 'updated products' list in order if doesn't exists
if (!orders[product.id_po]['updated_products'])
orders[product.id_po]['updated_products'] = [];
if (from_processed) {
// Look for product in order's updated products list
let already_updated = false;
for (i in orders[product.id_po]['updated_products']) {
if (orders[product.id_po]['updated_products'][i]['id']
== product['id']) {
orders[product.id_po]['updated_products'][i] = product;
already_updated = true;
}
}
// If not updated before, add product to updated list...
if (!already_updated) {
orders[product.id_po]['updated_products'].push(product);
// ... and remove product from 'direct validated' products if was there
if ('valid_products' in orders[product.id_po]) {
for (i in orders[product.id_po]['valid_products']) {
if (orders[product.id_po]['valid_products'][i] == product['id']) {
orders[product.id_po]['valid_products'].splice(i, 1);
}
}
}
}
} else {
// Add the product to the updated products
updatedProducts.push(product);
orders[product.id_po]['updated_products'].push(product);
}
// Re-add product in table
if (from_processed) {
remove_from_processed(row, product);
} else {
remove_from_toProcess(row, product);
}
add_to_processed(product);
// Update local storage of product's order
localStorage.setItem("order_" + product.id_po, JSON.stringify(orders[product.id_po]));
// Reset search
document.getElementById('search_input').value = '';
$('table.dataTable').DataTable()
.search('')
.draw();
document.getElementById('search_input').focus();
} catch (e) {
err = {msg: e.name + ' : ' + e.message, ctx: 'set_supplier_shortage'};
console.error(err);
report_JS_error(err, 'reception');
}
}
/* EDITION */ /* EDITION */
...@@ -635,9 +814,9 @@ function editProductInfo (productToEdit, value = null) { ...@@ -635,9 +814,9 @@ function editProductInfo (productToEdit, value = null) {
// Edit product info // Edit product info
productToEdit.product_qty = newValue; productToEdit.product_qty = newValue;
/* /*
If qty has changed, we choose to set detailed values as follow: If qty has changed, we choose to set detailed values as follow:
1 package (product_qty_package) of X products (package_qty) 1 package (product_qty_package) of X products (package_qty)
*/ */
productToEdit.product_qty_package = 1; productToEdit.product_qty_package = 1;
productToEdit.package_qty = productToEdit.product_qty; productToEdit.package_qty = productToEdit.product_qty;
} }
...@@ -862,7 +1041,7 @@ function data_validation() { ...@@ -862,7 +1041,7 @@ function data_validation() {
// Send the request to the server // Send the request to the server
function send() { function send() {
try { try {
// Loading on // Loading on
openModal(); openModal();
// Only send to server the updated lines // Only send to server the updated lines
...@@ -894,7 +1073,7 @@ function send() { ...@@ -894,7 +1073,7 @@ function send() {
product_copy.old_qty = other_order_data.initial_qty; product_copy.old_qty = other_order_data.initial_qty;
for (j in orders[updatedProducts[i].id_po]['updated_products']) { for (j in orders[updatedProducts[i].id_po]['updated_products']) {
if (orders[updatedProducts[i].id_po]['updated_products'][j].product_id[0] if (orders[updatedProducts[i].id_po]['updated_products'][j].product_id[0]
== product_copy.product_id[0]) { == product_copy.product_id[0]) {
orders[updatedProducts[i].id_po]['updated_products'][j].old_qty -= other_order_data.initial_qty; orders[updatedProducts[i].id_po]['updated_products'][j].old_qty -= other_order_data.initial_qty;
break; break;
} }
...@@ -940,8 +1119,6 @@ function send() { ...@@ -940,8 +1119,6 @@ function send() {
prod_order_id = updatedProducts[i].id_po; prod_order_id = updatedProducts[i].id_po;
update_data.orders[prod_order_id]['po'].push(updatedProducts[i]); update_data.orders[prod_order_id]['po'].push(updatedProducts[i]);
} }
// console.log(update_data)
// return -1
$.ajaxSetup({ headers: { "X-CSRFToken": getCookie('csrftoken') } }); $.ajaxSetup({ headers: { "X-CSRFToken": getCookie('csrftoken') } });
$.ajax({ $.ajax({
...@@ -1001,7 +1178,7 @@ function send() { ...@@ -1001,7 +1178,7 @@ function send() {
span_node.appendChild(textNode); span_node.appendChild(textNode);
textNode = document.createTextNode(orders[order_id].partner textNode = document.createTextNode(orders[order_id].partner
+ ' du ' + orders[order_id].date_order + ' : '); + ' du ' + orders[order_id].date_order + ' : ');
p_node.appendChild(textNode); p_node.appendChild(textNode);
p_node.appendChild(span_node); p_node.appendChild(span_node);
...@@ -1160,11 +1337,27 @@ function openErrorReport() { ...@@ -1160,11 +1337,27 @@ function openErrorReport() {
function saveErrorReport() { function saveErrorReport() {
user_comments = document.getElementById("error_report").value; user_comments = document.getElementById("error_report").value;
// Save comment in local storage, in all orders
for (order_id of Object.keys(orders)) {
orders[order_id].user_comments = user_comments;
localStorage.setItem("order_" + order_id, JSON.stringify(orders[order_id]));
}
document.getElementById("search_input").focus(); document.getElementById("search_input").focus();
} }
// Load barcodes at page loading, then barcodes are stored locally
var get_barcodes = async function() {
if (barcodes == null) barcodes = await init_barcodes();
};
$(document).ready(function() { $(document).ready(function() {
$.ajaxSetup({ headers: { "X-CSRFToken": getCookie('csrftoken') } });
// Load barcodes
get_barcodes();
// Get Route parameter // Get Route parameter
var pathArray = window.location.pathname.split('/'); var pathArray = window.location.pathname.split('/');
var id = pathArray[pathArray.length-1]; var id = pathArray[pathArray.length-1];
...@@ -1278,6 +1471,9 @@ $(document).ready(function() { ...@@ -1278,6 +1471,9 @@ $(document).ready(function() {
// Set current reception status: take first order's // Set current reception status: take first order's
reception_status = orders[Object.keys(orders)[0]].reception_status; reception_status = orders[Object.keys(orders)[0]].reception_status;
// Load user comments from local storage, get it from first order
user_comments = orders[Object.keys(orders)[0]].user_comments;
} }
// Fetch orders data // Fetch orders data
...@@ -1373,15 +1569,15 @@ $(document).ready(function() { ...@@ -1373,15 +1569,15 @@ $(document).ready(function() {
$(this).on('wheel.disableScroll', function (e) { $(this).on('wheel.disableScroll', function (e) {
e.preventDefault(); e.preventDefault();
/* /*
Option to possibly enable page scrolling when mouse over the input, but : Option to possibly enable page scrolling when mouse over the input, but :
- deltaY is not in pixels in Firefox - deltaY is not in pixels in Firefox
- movement not fluid on other browsers - movement not fluid on other browsers
var scrollTo = (e.originalEvent.deltaY) + $(document.documentElement).scrollTop(); var scrollTo = (e.originalEvent.deltaY) + $(document.documentElement).scrollTop();
$(document.documentElement).scrollTop(scrollTo); $(document.documentElement).scrollTop(scrollTo);
-> other option to allow scrolling would be to loose input focus with blur(): not acceptable -> other option to allow scrolling would be to loose input focus with blur(): not acceptable
*/ */
}); });
}) })
.on('blur', function (e) { .on('blur', function (e) {
...@@ -1439,24 +1635,28 @@ $(document).ready(function() { ...@@ -1439,24 +1635,28 @@ $(document).ready(function() {
} }
}); });
// Barcode reader: listen for 13 digits read in a very short time // Barcode reader
$('#search_input').keypress(function(e) { $(document).pos();
if (e.which >= 48 && e.which <= 57) { $(document).on('scan.pos.barcode', function(event) {
search_chars.push(String.fromCharCode(e.which)); //access `event.code` - barcode data
} var barcode = event.code;
if (search_chars.length >= 13) {
var barcode = search_chars.join(""); if (barcode.length >=13) {
barcode = barcode.substring(barcode.length-13);
if (!isNaN(barcode)) { } else if (barcode.length == 12 && barcode.indexOf('0') !== 0) {
search_chars = []; // User may use a scanner which remove leading 0
setTimeout(function() { barcode = '0' + barcode;
document.getElementById('search_input').value = ''; } else {
$('table.dataTable').DataTable() //manually submitted after correction
.search('') var barcode_input = $('#search_input');
.draw();
select_product_from_bc(barcode); barcode = barcode_input.val();
}, 300);
}
} }
document.getElementById('search_input').value = '';
$('table.dataTable').DataTable()
.search('')
.draw();
select_product_from_bc(barcode);
}); });
}); });
...@@ -4,6 +4,7 @@ from outils.for_view_imports import * ...@@ -4,6 +4,7 @@ from outils.for_view_imports import *
from django.views.generic import View from django.views.generic import View
import os import os
from datetime import date
from openpyxl import Workbook from openpyxl import Workbook
from openpyxl import load_workbook from openpyxl import load_workbook
from openpyxl.styles import Alignment from openpyxl.styles import Alignment
...@@ -138,16 +139,35 @@ def update_orders(request): ...@@ -138,16 +139,35 @@ def update_orders(request):
for order_line in order['po']: for order_line in order['po']:
if order_line['indicative_package'] is False: if order_line['indicative_package'] is False:
m.remove_package_restriction(order_line) m.remove_package_restriction(order_line)
update = m.update_line(int(order_line['id']), data['update_type'], float(order_line['package_qty']), float(order_line['product_qty_package']), float(order_line['price_unit']))
update = m.update_line(int(order_line['id']),
data['update_type'],
float(order_line['package_qty']),
float(order_line['product_qty_package']),
float(order_line['price_unit']))
if not (update is True): if not (update is True):
# indicative_package may have been changed since data have been loaded in browser # indicative_package may have been changed since data have been loaded in browser, retry
m.remove_package_restriction(order_line) m.remove_package_restriction(order_line)
update = m.update_line(int(order_line['id']), data['update_type'], float(order_line['package_qty']), float(order_line['product_qty_package']), float(order_line['price_unit'])) update = m.update_line(int(order_line['id']),
data['update_type'],
float(order_line['package_qty']),
float(order_line['product_qty_package']),
float(order_line['price_unit']))
if not (update is True): if not (update is True):
errors.append(order_line['id']) errors.append(order_line['id'])
# If update succeded, and supplier shortage set, try to register the supplier shortage
if update is True and 'supplier_shortage' in order_line:
try:
answer_data['res_shortage'] = CagetteProduct.register_start_supplier_shortage(
order_line['product_id'][0],
order_line['partner_id'][0],
date.today().strftime("%Y-%m-%d"))
except Exception as e:
errors.append('error registering shortage on p'+order_line['id']+':'+str(e))
# Print etiquette with new price if update if successful # Print etiquette with new price if update if successful
if (print_labels is True) and (update is True) and (data['update_type'] == 'br_valid'): if (print_labels is True) and (update is True) and (data['update_type'] == 'br_valid') and order_line['new_shelf_price']:
try: try:
tools_url = settings.TOOLS_SERVER + '/products/label_print/' tools_url = settings.TOOLS_SERVER + '/products/label_print/'
tools_url += str(order_line['product_tmpl_id']) + '/' tools_url += str(order_line['product_tmpl_id']) + '/'
...@@ -232,7 +252,14 @@ def save_error_report(request): ...@@ -232,7 +252,14 @@ def save_error_report(request):
ws = wb.active ws = wb.active
ws.title = "Commande " + order['name'] ws.title = "Commande " + order['name']
ws.append(['type', 'nom_contenu', 'supplier_code', 'barcode', 'old_qty', 'product_qty', 'price_unit']) ws.append(['type',
'nom_contenu',
'supplier_code',
'barcode',
'old_qty',
'product_qty',
'price_unit',
'supplier_shortage'])
# If in group add group name # If in group add group name
if len(data['orders']) > 1 : if len(data['orders']) > 1 :
...@@ -247,12 +274,24 @@ def save_error_report(request): ...@@ -247,12 +274,24 @@ def save_error_report(request):
else: else:
supplier_code = 'X' supplier_code = 'X'
ws.append( ['produit', product['product_id'][1], supplier_code, str(product['barcode']), product['old_qty'], product['product_qty'], product['price_unit']] ) if 'supplier_shortage' in product:
supplier_shortage = '/!\ Rupture fournisseur'
else:
supplier_shortage = ''
ws.append( ['produit',
product['product_id'][1],
supplier_code,
str(product['barcode']),
product['old_qty'],
product['product_qty'],
product['price_unit'],
supplier_shortage] )
except: except:
# no updated products, do nothing # no updated products, do nothing
pass pass
if data['user_comments'] != "": if ('user_comments' in data) and data['user_comments'] != "":
ws.append( ['commentaire', data['user_comments']] ) ws.append( ['commentaire', data['user_comments']] )
else: else:
ws.append( ['commentaire', 'Aucun commentaire.'] ) ws.append( ['commentaire', 'Aucun commentaire.'] )
...@@ -282,7 +321,8 @@ def save_error_report(request): ...@@ -282,7 +321,8 @@ def save_error_report(request):
'barcode' : row[3], 'barcode' : row[3],
'old_qty' : row[4], 'old_qty' : row[4],
'product_qty' : row[5], 'product_qty' : row[5],
'price_unit' : row[6] 'price_unit' : row[6],
'supplier_shortage' : row[7]
} }
elif row[0] == 'group': elif row[0] == 'group':
group_name = row[1] # group's orders name group_name = row[1] # group's orders name
...@@ -298,7 +338,7 @@ def save_error_report(request): ...@@ -298,7 +338,7 @@ def save_error_report(request):
data_full = [] data_full = []
error_total = 0 error_total = 0
error_total_abs = 0 error_total_abs = 0
if data['user_comments'] != "": if ('user_comments' in data) and data['user_comments'] != "":
data_comment_s2 = data['user_comments'] data_comment_s2 = data['user_comments']
else: else:
data_comment_s2 = "Aucun commentaire." data_comment_s2 = "Aucun commentaire."
...@@ -311,12 +351,18 @@ def save_error_report(request): ...@@ -311,12 +351,18 @@ def save_error_report(request):
else: else:
supplier_code = 'X' supplier_code = 'X'
if 'supplier_shortage' in product:
supplier_shortage = '/!\ Rupture fournisseur'
else:
supplier_shortage = ''
item = { item = {
'product_id': product['product_id'][1], 'product_id': product['product_id'][1],
'product_supplier_code': supplier_code, 'product_supplier_code': supplier_code,
'product_barcode': product['barcode'], 'product_barcode': product['barcode'],
'old_price_unit': float(product['old_price_unit']), 'old_price_unit': float(product['old_price_unit']),
'price_unit': float(product['price_unit']) 'price_unit': float(product['price_unit']),
'supplier_shortage': supplier_shortage
} }
# If the product was also modified in step 1 # If the product was also modified in step 1
...@@ -326,6 +372,10 @@ def save_error_report(request): ...@@ -326,6 +372,10 @@ def save_error_report(request):
item['expected_amount'] = item['old_qty']*item['old_price_unit'] item['expected_amount'] = item['old_qty']*item['old_price_unit']
item['error_line'] = (item['old_qty'] - item['product_qty'])*item['price_unit'] item['error_line'] = (item['old_qty'] - item['product_qty'])*item['price_unit']
# If product was set on supplier shortage in step 1 and not in step 2
if item['supplier_shortage'] == '' and data_qties[item['product_id']]['supplier_shortage'] != '':
item['supplier_shortage'] = data_qties[item['product_id']]['supplier_shortage']
data_qties.pop(item['product_id']) data_qties.pop(item['product_id'])
else: else:
item['old_qty'] = float(product['product_qty']) item['old_qty'] = float(product['product_qty'])
...@@ -354,7 +404,8 @@ def save_error_report(request): ...@@ -354,7 +404,8 @@ def save_error_report(request):
'old_price_unit': float(product['price_unit']), 'old_price_unit': float(product['price_unit']),
'price_unit': '', 'price_unit': '',
'expected_amount':float(product['old_qty'])*float(product['price_unit']), 'expected_amount':float(product['old_qty'])*float(product['price_unit']),
'error_line': (float(product['old_qty'])-float(product['product_qty']))*float(product['price_unit']) 'error_line': (float(product['old_qty'])-float(product['product_qty']))*float(product['price_unit']),
'supplier_shortage': product['supplier_shortage']
} }
error_total += item['error_line'] error_total += item['error_line']
...@@ -389,13 +440,30 @@ def save_error_report(request): ...@@ -389,13 +440,30 @@ def save_error_report(request):
ws.append( ['Montant total attendu (TTC) : ', str(round(order['amount_total'],2)) + ' €'] ) ws.append( ['Montant total attendu (TTC) : ', str(round(order['amount_total'],2)) + ' €'] )
ws.append( [] ) 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 prix: (nouveau_prix-ancien_prix)*nouvelle_qté)"] ) 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é)"] )
if len(data_full) == 0: if len(data_full) == 0:
ws.append( ['- Aucune modification -'] ) ws.append( ['- Aucune modification -'] )
else: else:
for product in data_full: for product in data_full:
ws.append( [product['product_id'], product['product_supplier_code'], str(product['product_barcode']), product['old_qty'], product['product_qty'], product['old_price_unit'], product['price_unit'], round(product['expected_amount'], 2), round(product['error_line'], 2)] ) ws.append( [product['product_id'],
product['product_supplier_code'],
str(product['product_barcode']),
product['old_qty'],
product['product_qty'],
product['old_price_unit'],
product['price_unit'],
round(product['expected_amount'], 2),
round(product['error_line'], 2),
product['supplier_shortage']] )
ws.append( [] ) ws.append( [] )
ws.append( ['Montant total de l\'erreur :', '', '', '', '', '', '', '', round(error_total, 2)] ) ws.append( ['Montant total de l\'erreur :', '', '', '', '', '', '', '', round(error_total, 2)] )
......
...@@ -27,7 +27,7 @@ class Shelf(models.Model): ...@@ -27,7 +27,7 @@ class Shelf(models.Model):
self.o_api = OdooAPI() self.o_api = OdooAPI()
def get(self): def get(self):
res ={} res = {}
try: try:
c = [['id', '=', self.id]] c = [['id', '=', self.id]]
f = [] f = []
...@@ -160,6 +160,7 @@ class Shelf(models.Model): ...@@ -160,6 +160,7 @@ class Shelf(models.Model):
barcodes = list(map(str, barcodes)) barcodes = list(map(str, barcodes))
# Get bc as should be stored in Odoo # Get bc as should be stored in Odoo
bc_map = CagetteProducts.get_fixed_barcode_correspondance(barcodes) bc_map = CagetteProducts.get_fixed_barcode_correspondance(barcodes)
p_res = self._get_pdts_from_barcodes(list(bc_map.values())) p_res = self._get_pdts_from_barcodes(list(bc_map.values()))
if (p_res): if (p_res):
found_bc = [] found_bc = []
...@@ -200,6 +201,12 @@ class Shelf(models.Model): ...@@ -200,6 +201,12 @@ class Shelf(models.Model):
filename = tmp_inv_file_prefix + str(self.id) + '.json' filename = tmp_inv_file_prefix + str(self.id) + '.json'
os.remove(filename) os.remove(filename)
lockfilename = tmp_inv_file_prefix + str(self.id) + '.lock'
try:
os.remove(lockfilename)
except Exception as e:
pass
return True return True
except Exception as e: except Exception as e:
return False return False
...@@ -220,6 +227,24 @@ class Shelf(models.Model): ...@@ -220,6 +227,24 @@ class Shelf(models.Model):
first_inventory = json.load(json_file) first_inventory = json.load(json_file)
except Exception as e: except Exception as e:
coop_logger.error("Unable to process first step file : %s", e) coop_logger.error("Unable to process first step file : %s", e)
import errno
raise FileExistsError(errno.ENOENT, os.strerror(errno.ENOENT), filename)
lockfilename = tmp_inv_file_prefix + str(self.id) + '.lock'
# Look for lock file: if exists, first step file is being processed so stop here
try:
with open(lockfilename) as lock_file:
return {'error': 'First step file busy', 'busy': True}
except Exception as e:
pass
# Verification passed, create the lock file to indicate first step file is being processed
try:
with open(lockfilename, 'w') as lock_file:
json.dump({}, lock_file)
except Exception as e:
coop_logger.error("Unable to create lock file : %s", e)
if first_inventory: if first_inventory:
# if poducts in saved data # if poducts in saved data
......
...@@ -106,14 +106,18 @@ function handle_blinking_effect(element) { ...@@ -106,14 +106,18 @@ function handle_blinking_effect(element) {
// When edition event is fired // When edition event is fired
function edit_event(clicked) { function edit_event(clicked) {
// Remove from origin table // Remove from origin table
var row_data = null;
if (editing_origin == 'to_process') { if (editing_origin == 'to_process') {
let row = table_to_process.row(clicked.parents('tr')); let row = table_to_process.row(clicked.parents('tr'));
let row_data = row.data();
row_data = row.data();
remove_from_toProcess(row); remove_from_toProcess(row);
} else { } else {
let row = table_processed.row(clicked.parents('tr')); let row = table_processed.row(clicked.parents('tr'));
let row_data = row.data();
row_data = row.data();
remove_from_processed(row); remove_from_processed(row);
} }
...@@ -430,7 +434,7 @@ function pre_send() { ...@@ -430,7 +434,7 @@ function pre_send() {
// Proceed with inventory: send the request to the server // Proceed with inventory: send the request to the server
function send() { function send() {
if (is_time_to('submit_inv_qties')) { if (is_time_to('submit_inv_qties')) {
// Loading on // Loading on
var wz = $('#main-waiting-zone').clone(); var wz = $('#main-waiting-zone').clone();
wz.find('.msg').text("Patience, cela peut prendre de nombreuses minutes s'il y a une centaine de produits"); wz.find('.msg').text("Patience, cela peut prendre de nombreuses minutes s'il y a une centaine de produits");
...@@ -440,6 +444,7 @@ function send() { ...@@ -440,6 +444,7 @@ function send() {
shelf.user_comments = user_comments; shelf.user_comments = user_comments;
var url = "../do_" + originView + "_inventory"; var url = "../do_" + originView + "_inventory";
var call_begin_at = new Date().getTime();
$.ajaxSetup({ headers: { "X-CSRFToken": getCookie('csrftoken') } }); $.ajaxSetup({ headers: { "X-CSRFToken": getCookie('csrftoken') } });
$.ajax({ $.ajax({
...@@ -475,14 +480,60 @@ function send() { ...@@ -475,14 +480,60 @@ function send() {
// Clear local storage before leaving // Clear local storage before leaving
localStorage.removeItem(originView + '_' + shelf.id); localStorage.removeItem(originView + '_' + shelf.id);
}, },
error: function(data) { // 500 error has been thrown error: function(jqXHR, textStatus) { // 500 error has been thrown or web server sent a timeout
if (typeof data.responseJSON != 'undefined' && typeof data.responseJSON.error != 'undefined') { if (jqXHR.status == 504) {
console.log(data.responseJSON.error); /*
django is too long to respond.
Let it the same time laps before asking if the process is well done
*/
var now = new Date().getTime();
setTimeout(
function() {
$.ajax({
type: 'GET',
url: '../inventory_process_state/' + shelf.id,
success: function(rData) {
if ('res' in rData && 'state' in rData.res) {
// Verification for step 2 only ; step 1 is always fast
if (shelf.inventory_status == 'step1_done' && rData.res.state != 'step1_done') {
// shelf inventory has been already done
localStorage.removeItem(originView + '_' + shelf.id);
closeModal();
back();
} else {
console.log('Still in process : need to call recursively to make other calls');
}
} else {
console.log(rData);
}
}
});
}
, now - call_begin_at
);
} else if (jqXHR.status == 500) {
var message = "Erreur lors de la sauvegarde des données. " +
"Pas de panique, les données de l'inventaire n'ont pas été perdues ! " +
"Merci de contacter un salarié et de réessayer plus tard.";
if (typeof jqXHR.responseJSON != 'undefined' && typeof jqXHR.responseJSON.error != 'undefined') {
//console.log(jqXHR.responseJSON.error);
if ('busy' in jqXHR.responseJSON) {
message = "Inventaire en cours de traitement.";
} else if (jqXHR.responseJSON.error == 'FileExistsError') {
//step1 file has been found => previous request succeeded
message = "Les données avaient déjà été transmises....";
// Clear local storage before leaving
localStorage.removeItem(originView + '_' + shelf.id);
}
}
closeModal();
alert(message);
back();
} }
closeModal();
alert("Erreur lors de la sauvegarde des données. " +
"Pas de panique, les données de l'inventaire n'ont pas été perdues ! " +
"Merci de contacter un salarié et de réessayer plus tard.");
} }
}); });
} else { } else {
...@@ -776,7 +827,6 @@ $(document).ready(function() { ...@@ -776,7 +827,6 @@ $(document).ready(function() {
originView = 'custom_list'; originView = 'custom_list';
parent_location = '/inventory/custom_lists'; parent_location = '/inventory/custom_lists';
} }
//console.log(products)
// Get shelf data from local storage // Get shelf data from local storage
if (Modernizr.localstorage) { if (Modernizr.localstorage) {
......
...@@ -16,6 +16,7 @@ var main_content = $('#main-content'), ...@@ -16,6 +16,7 @@ var main_content = $('#main-content'),
.removeAttr('id'), .removeAttr('id'),
active_phase = 'main', active_phase = 'main',
add_pdts_btn_text = 'AJOUTER AU RAYON', add_pdts_btn_text = 'AJOUTER AU RAYON',
add_to_shelf_product_ids = [],
barcodes = null; barcodes = null;
...@@ -339,14 +340,27 @@ var init_and_fill_selfs_list = function() { ...@@ -339,14 +340,27 @@ var init_and_fill_selfs_list = function() {
} }
}; };
var deleteBarcodeFromList = function () { var deleteBarcodeFromList = function () {
var clicked = $(this); let clicked = $(this);
let new_pids_list = [];
let tr_to_remove = clicked.closest('tr');
let pid_to_remove = tr_to_remove.data('id');
$.each(add_to_shelf_product_ids, function(idx, pid) {
if (pid != pid_to_remove) new_pids_list.push(pid);
});
add_to_shelf_product_ids = new_pids_list;
tr_to_remove.remove();
};
var is_product_in_shelf_adding_queue_list = function(testing_pid) {
let found = false;
$.each(add_to_shelf_product_ids, function(idx, pid) {
if (pid == testing_pid) found = true;
});
clicked.closest('li').remove(); return found;
/*
if (clicked.closest('ul').find('li').length == 0)
main_content.find('button.add-products').css('display','none')
*/
}; };
var addProductToList = async function(barcode) { var addProductToList = async function(barcode) {
if (barcodes == null) barcodes = await init_barcodes(); // May appens (after inactivity?) if (barcodes == null) barcodes = await init_barcodes(); // May appens (after inactivity?)
//Get Odoo corresponding barcode //Get Odoo corresponding barcode
...@@ -355,23 +369,29 @@ var addProductToList = async function(barcode) { ...@@ -355,23 +369,29 @@ var addProductToList = async function(barcode) {
odoo_product = barcodes.get_corresponding_odoo_product(barcode); odoo_product = barcodes.get_corresponding_odoo_product(barcode);
if (odoo_product === null) { if (is_product_in_shelf_adding_queue_list(odoo_product.data[barcodes.keys.id])) {
alert(barcode + ' : Code-barre inconnu'); console.log("Already added product");
} else { } else {
var pdt_line = $('<tr>').attr('data-bc', odoo_product.barcode) add_to_shelf_product_ids.push(odoo_product.data[4]);
.addClass('obc'); if (odoo_product === null) {
alert(barcode + ' : Code-barre inconnu');
$('<td>').text(barcode) } else {
.appendTo(pdt_line); var pdt_line = $('<tr>').attr('data-id', odoo_product.data[barcodes.keys.id])
$('<td>').text(odoo_product.barcode) .attr('data-bc', odoo_product.barcode)
.appendTo(pdt_line); .addClass('obc');
$('<td>').text(odoo_product.data[0])
.appendTo(pdt_line); $('<td>').text(barcode)
$('<td>').html(delete_icon) .appendTo(pdt_line);
.appendTo(pdt_line); $('<td>').text(odoo_product.barcode)
adding_pdts_tpl.find('#added_products tbody').append(pdt_line); .appendTo(pdt_line);
main_content.find('button.add-products').css('display', 'block') $('<td>').text(odoo_product.data[barcodes.keys.name])
.html(add_pdts_btn_text); .appendTo(pdt_line);
$('<td>').html(delete_icon)
.appendTo(pdt_line);
adding_pdts_tpl.find('#added_products tbody').append(pdt_line);
main_content.find('button.add-products').css('display', 'block')
.html(add_pdts_btn_text);
}
} }
}; };
...@@ -380,7 +400,7 @@ var addProducts = async function() { ...@@ -380,7 +400,7 @@ var addProducts = async function() {
var data = rowGetData(clicked); var data = rowGetData(clicked);
if (barcodes == null) barcodes = await init_barcodes(); if (barcodes == null) barcodes = await init_barcodes();
add_to_shelf_product_ids = [];
adding_pdts_tpl.find('.shelf').text(data.name + ' (num = ' + data.sort_order+')') adding_pdts_tpl.find('.shelf').text(data.name + ' (num = ' + data.sort_order+')')
.attr('data-shelfid', data.id); .attr('data-shelfid', data.id);
adding_pdts_tpl.find('#added_products tbody').empty(); adding_pdts_tpl.find('#added_products tbody').empty();
...@@ -402,6 +422,7 @@ var recordProductsAddedShelf = function() { ...@@ -402,6 +422,7 @@ var recordProductsAddedShelf = function() {
to_add.each(function(i, e) { to_add.each(function(i, e) {
barcodes.push($(e).data('bc')); barcodes.push($(e).data('bc'));
}); });
if (is_time_to('add_pdts_to_shelf', 5000)) { // prevent double click or browser hic up bug if (is_time_to('add_pdts_to_shelf', 5000)) { // prevent double click or browser hic up bug
main_content.find('button.add-products').html(loading_img); main_content.find('button.add-products').html(loading_img);
post_form( post_form(
...@@ -518,7 +539,7 @@ $(document).ready(function() { ...@@ -518,7 +539,7 @@ $(document).ready(function() {
$(document).on('click', '.shelfs .fa-edit', open_update_form); $(document).on('click', '.shelfs .fa-edit', open_update_form);
$(document).on('click', '.shelfs .fa-trash', deleteShelf); $(document).on('click', '.shelfs .fa-trash', deleteShelf);
$(document).on('click', '.shelfs .fa-download', downloadInventoryReport); $(document).on('click', '.shelfs .fa-download', downloadInventoryReport);
$(document).on('click', '.bc .fa-trash', deleteBarcodeFromList); $(document).on('click', '.obc .fa-trash', deleteBarcodeFromList);
$(document).on('click', 'td.products .fa-plus-circle', addProducts); $(document).on('click', 'td.products .fa-plus-circle', addProducts);
$(document).on('click', '#main-content button.add-products', recordProductsAddedShelf); $(document).on('click', '#main-content button.add-products', recordProductsAddedShelf);
$(document).on('click', 'td.p_nb', showProductsList); $(document).on('click', 'td.p_nb', showProductsList);
......
...@@ -8,6 +8,7 @@ urlpatterns = [ ...@@ -8,6 +8,7 @@ urlpatterns = [
url(r'^$', views.index), url(r'^$', views.index),
url(r'^shelf_view/([0-9]+)$', views.shelf_view), url(r'^shelf_view/([0-9]+)$', views.shelf_view),
url(r'^shelf_inventory/([0-9]+)$', views.shelf_inventory), url(r'^shelf_inventory/([0-9]+)$', views.shelf_inventory),
url(r'^inventory_process_state/([0-9]+)$', views.inventory_process_state),
url(r'^all$', views.all), url(r'^all$', views.all),
url(r'^get_shelves_extra_data$', views.get_shelves_extra_data), url(r'^get_shelves_extra_data$', views.get_shelves_extra_data),
url(r'^(?P<shelf_id>\d+)$', views.shelf_data), url(r'^(?P<shelf_id>\d+)$', views.shelf_data),
......
...@@ -13,7 +13,7 @@ def index(request): ...@@ -13,7 +13,7 @@ def index(request):
"""Main shelf page""" """Main shelf page"""
shelfs = Shelfs.get_all() shelfs = Shelfs.get_all()
# TODO : Make the distinction beetween active and inactive products # TODO : Make the distinction beetween active and inactive products
for s in shelfs : for s in shelfs:
s['shelf_value'] = -1 s['shelf_value'] = -1
context = {'title': 'Rayons', context = {'title': 'Rayons',
...@@ -107,8 +107,29 @@ def add_product(request, shelf_id): ...@@ -107,8 +107,29 @@ def add_product(request, shelf_id):
return JsonResponse({'res': res}) return JsonResponse({'res': res})
def inventory_process_state(request, shelf_id):
res = {}
try:
s = Shelf(shelf_id)
s_data = s.get()
res['state'] = s_data['inventory_status']
except Exception as e:
res['error'] = str(e)
coop_logger.error("Inventory process state : %s", str(e))
if 'error' in res:
return JsonResponse(res, status=500)
else:
return JsonResponse({'res': res})
def do_shelf_inventory(request): def do_shelf_inventory(request):
"""Process shelf inventory""" """Process shelf inventory"""
"""
If many products are implied, the whole process could last many minutes.
During this time, user can submit data again.
This is managed with 'busy' message returned by Shelf.get_full_inventory_data method
Web server can also return a timeout message during this time.
This is managed by sending a query to above "get_process_state" (within browser ajax error capture)
"""
res = {} res = {}
# TODO : manage error strings array instead of one string # TODO : manage error strings array instead of one string
try: try:
...@@ -121,7 +142,8 @@ def do_shelf_inventory(request): ...@@ -121,7 +142,8 @@ def do_shelf_inventory(request):
'name': shelf_data['name'] + ' - ' + inventory_date.strftime("%d/%m/%Y"), 'name': shelf_data['name'] + ' - ' + inventory_date.strftime("%d/%m/%Y"),
'shelf_id': shelf_data['id'], 'shelf_id': shelf_data['id'],
'user_comments': shelf_data['user_comments'], 'user_comments': shelf_data['user_comments'],
'products': shelf_data['list_processed'] 'products': shelf_data['list_processed'],
'status': shelf_data['inventory_status']
} }
try: try:
filename = 'data/inventories_backup/' filename = 'data/inventories_backup/'
...@@ -129,20 +151,26 @@ def do_shelf_inventory(request): ...@@ -129,20 +151,26 @@ def do_shelf_inventory(request):
filename += "__" + str(shelf_data['id']) + '.json' filename += "__" + str(shelf_data['id']) + '.json'
with open(filename, 'w') as outfile: with open(filename, 'w') as outfile:
json.dump(shelf_data, outfile) json.dump(shelf_data, outfile)
except: except Exception as serr:
pass coop_logger.error("Inventory backup failure : %s", str(serr))
try: try:
if shelf_data['inventory_status'] == '' : if shelf_data['inventory_status'] == '':
# First step: save first products count in temp file # First step: save first products count in temp file
res = m.save_tmp_inventory(inventory_data) res = m.save_tmp_inventory(inventory_data)
else : else:
inventory_data['date'] = inventory_date inventory_data['date'] = inventory_date
inventory_data['shelf_name'] = shelf_data['name'] inventory_data['shelf_name'] = shelf_data['name']
inventory_data['shelf_num'] = shelf_data['sort_order'] inventory_data['shelf_num'] = shelf_data['sort_order']
# Get data from step 1 # Get data from step 1
full_inventory_data = m.get_full_inventory_data(inventory_data) full_inventory_data = m.get_full_inventory_data(inventory_data)
if 'error' in full_inventory_data:
res['error'] = full_inventory_data['error']
if 'busy' in full_inventory_data:
res['busy'] = True
return JsonResponse(res, status=500)
# Proceed with inventory # Proceed with inventory
res['inventory'] = CagetteInventory.update_stock_with_shelf_inventory_data(full_inventory_data) res['inventory'] = CagetteInventory.update_stock_with_shelf_inventory_data(full_inventory_data)
...@@ -173,7 +201,7 @@ def do_shelf_inventory(request): ...@@ -173,7 +201,7 @@ def do_shelf_inventory(request):
except Exception as e: except Exception as e:
# Don't validate if error anywhere in inventory process # Don't validate if error anywhere in inventory process
res['error'] = {'inventory' : str(e)} res['error'] = type(e).__name__
coop_logger.error("Shelf inv. : %s", str(e)) coop_logger.error("Shelf inv. : %s", str(e))
except Exception as err_json: except Exception as err_json:
res['error'] = "Unable to parse received JSON" res['error'] = "Unable to parse received JSON"
......
body {margin: 5px;} body {margin: 5px;}
#exchange_instructions, .need_make_up {display:none;} #exchange_instructions, .need_make_up {display:none;}
.need_make_up, .highlight {background: #ffff00;} .need_make_up, .highlight {background: #ffff00;}
.need_make_up_area {margin-top: 15px;}
#partnerData {margin:8px 0;} #partnerData {margin:8px 0;}
#shift_list_wrapper {margin:15px 25px; padding: 5px; border-radius: 5px; border: 2px solid #424242; max-width: 530px; width: 350px;} #shift_list_wrapper {margin:15px 25px; padding: 5px; border-radius: 5px; border: 2px solid #424242; max-width: 530px; width: 350px;}
...@@ -9,10 +10,10 @@ span.shift_booked{ ...@@ -9,10 +10,10 @@ span.shift_booked{
border-radius: 5px; border-radius: 5px;
border: 2px solid #424242; border: 2px solid #424242;
margin : 5px; margin : 5px;
padding:5px; padding:5px;
} }
.shift_list_item {margin-bottom:5px; padding-bottom:5px;} .shift_list_item {margin-bottom:5px; padding-bottom:5px;}
.shift_list_item label { .shift_list_item label {
display: inline-block; display: inline-block;
vertical-align: top; vertical-align: top;
padding-left: 20px; padding-left: 20px;
...@@ -27,24 +28,24 @@ h3 {margin-top: 10px;} ...@@ -27,24 +28,24 @@ h3 {margin-top: 10px;}
.intro .fa-info-circle {color: #7ecbee;} .intro .fa-info-circle {color: #7ecbee;}
span.step {border-radius: 3px; border: 2px solid #424242; padding: 3px;} span.step {border-radius: 3px; border: 2px solid #424242; padding: 3px;}
/* Pour les couleur des events dans le calendrier ------------------------------------*/ /* Pour les couleur des events dans le calendrier ------------------------------------*/
.shift_booked .shift_booked
{ {
background-color: #585858; background-color: #585858;
color: #FFFFFF !important; color: #FFFFFF !important;
} }
.shift_full .shift_full
{ {
background-color: #F5A9F2; background-color: #F5A9F2;
} }
.shift_empty .shift_empty
{ {
background-color: #60B000; background-color: #60B000;
} }
.shift_less_alf .shift_less_alf
{ {
background-color: #D9EBD2; background-color: #D9EBD2;
} }
.shift_other .shift_other
{ {
background-color: #FA8282; background-color: #FA8282;
} }
......
...@@ -239,7 +239,6 @@ function addMakeUpMsg() { ...@@ -239,7 +239,6 @@ function addMakeUpMsg() {
} }
}); });
make_up_nb = parseInt(partner_points) + shifts_before_limit; make_up_nb = parseInt(partner_points) + shifts_before_limit;
// Besoin de choisir un rattrapage : afficher message rattrapage // Besoin de choisir un rattrapage : afficher message rattrapage
if (make_up_nb < 0) { if (make_up_nb < 0) {
make_up_nb = Math.abs(make_up_nb); make_up_nb = Math.abs(make_up_nb);
...@@ -284,7 +283,9 @@ function addMakeUpMsg() { ...@@ -284,7 +283,9 @@ function addMakeUpMsg() {
msg += " avant le " + limitDate.toLocaleDateString("fr-fr", optDate) + ", faute de quoi je serai suspendu.e à cette date."; msg += " avant le " + limitDate.toLocaleDateString("fr-fr", optDate) + ", faute de quoi je serai suspendu.e à cette date.";
$('#partnerData').append("<span class=\"need_make_up\"><strong>"+ msg + "</strong></span>"); msg += "<br/>Je dois m'inscrire à tous mes services de rattrapage avant de pouvoir échanger mes services.";
$('#partnerData').append("<p class=\"need_make_up_area\"><span class=\"need_make_up\"><strong>"+ msg + "</strong></span></p>");
$('.need_make_up').show(); $('.need_make_up').show();
} else { } else {
$('.need_make_up').hide(); $('.need_make_up').hide();
......
...@@ -92,6 +92,10 @@ function djLogError(e) { ...@@ -92,6 +92,10 @@ function djLogError(e) {
} }
var french_date_and_time = function(dstring) { var french_date_and_time = function(dstring) {
if (shop_mode == 'delivery') {
return dstring;
}
var formatted = dstring.trim().replace(/-/g, "/"); // replace for Safari var formatted = dstring.trim().replace(/-/g, "/"); // replace for Safari
//expected = YYYY-MM-DD HH:MM[:00] //expected = YYYY-MM-DD HH:MM[:00]
...@@ -494,7 +498,7 @@ var getStoredOrder = function() { ...@@ -494,7 +498,7 @@ var getStoredOrder = function() {
var stored = null; var stored = null;
try { try {
stored = JSON.parse(localStorage.getItem('currentOrder')); stored = JSON.parse(localStorage.getItem(current_order_name));
} catch (e) { } catch (e) {
//WARNING : In this case, make sure the user haven't got any order initialized //WARNING : In this case, make sure the user haven't got any order initialized
//TODO : make a request to retrieve it //TODO : make a request to retrieve it
...@@ -508,7 +512,7 @@ var getStoredOrderForMigration = function() { ...@@ -508,7 +512,7 @@ var getStoredOrderForMigration = function() {
var stored = null; var stored = null;
try { try {
stored = JSON.parse(localStorage.getItem('saved_order')); stored = JSON.parse(localStorage.getItem(saved_order_name));
} catch (e) { } catch (e) {
djLogError({ctx: "get stored for mig", msg:e}); djLogError({ctx: "get stored for mig", msg:e});
} }
...@@ -523,7 +527,7 @@ var storeOrderForMigration = function() { ...@@ -523,7 +527,7 @@ var storeOrderForMigration = function() {
delete to_save._rev; delete to_save._rev;
delete to_save.timer_end_date; delete to_save.timer_end_date;
delete to_save.best_date; delete to_save.best_date;
localStorage.setItem("saved_order", JSON.stringify(to_save)); localStorage.setItem(saved_order_name, JSON.stringify(to_save));
order = to_save; order = to_save;
storeOrder(); storeOrder();
} catch (e) { } catch (e) {
...@@ -532,7 +536,7 @@ var storeOrderForMigration = function() { ...@@ -532,7 +536,7 @@ var storeOrderForMigration = function() {
}; };
var storeOrder = function() { var storeOrder = function() {
try { try {
localStorage.setItem("currentOrder", JSON.stringify(order)); localStorage.setItem(current_order_name, JSON.stringify(order));
} catch (e) { } catch (e) {
djLogError({ctx: "store order", msg:e}); djLogError({ctx: "store order", msg:e});
} }
...@@ -604,7 +608,8 @@ var pass2step2 = function() { ...@@ -604,7 +608,8 @@ var pass2step2 = function() {
$('h1').show(); $('h1').show();
var recup = order.best_date || 'non défini'; var recup = order.best_date || 'non défini';
current_order_bdate.html("Récupération : <strong>" + french_date_and_time(recup) + "</strong>"); if (shop_mode == 'shop')
current_order_bdate.html("Récupération : <strong>" + french_date_and_time(recup) + "</strong>");
if (visit_mode == true) { if (visit_mode == true) {
$('.product .choice').hide(); $('.product .choice').hide();
right_column.hide(); right_column.hide();
...@@ -774,19 +779,22 @@ var isChoosenSlotValid = function(slot) { ...@@ -774,19 +779,22 @@ var isChoosenSlotValid = function(slot) {
var answer = true; var answer = true;
var cause = ''; var cause = '';
forbidden_slots.forEach(function(e) { // For delivery, no slot validation: return true
if (slot == e) { if (shop_mode == 'shop') {
answer = false; cause = 'full'; forbidden_slots.forEach(function(e) {
if (slot == e) {
answer = false; cause = 'full';
}
});
//Does it respect min delay ?
var now = new Date();
var min_date = new Date();
var slot_date = new Date(slot.replace(/-/g, "/")); // replace for Safari
min_date = new Date(min_date.setHours(now.getHours() + min_delay));
if (slot_date - min_date < 0) {
answer = false; cause = 'delay';
} }
});
//Does it respect min delay ?
var now = new Date();
var min_date = new Date();
var slot_date = new Date(slot.replace(/-/g, "/")); // replace for Safari
min_date = new Date(min_date.setHours(now.getHours() + min_delay));
if (slot_date - min_date < 0) {
answer = false; cause = 'delay';
} }
return {res: answer, reason: cause}; return {res: answer, reason: cause};
...@@ -912,7 +920,6 @@ var validCart = function() { ...@@ -912,7 +920,6 @@ var validCart = function() {
post_form( post_form(
'/shop/cart', {order: JSON.stringify(order)}, '/shop/cart', {order: JSON.stringify(order)},
function(err, result) { function(err, result) {
//console.log(result)
if (!err) { if (!err) {
try { try {
if (typeof result.res.cart != "undefined" && result.res.cart != null) { if (typeof result.res.cart != "undefined" && result.res.cart != null) {
...@@ -948,7 +955,7 @@ var validCart = function() { ...@@ -948,7 +955,7 @@ var validCart = function() {
}; };
var clearCart = function() { var clearCart = function() {
order = {products: [], total: 0.00}; order = { products: [], total: 0.00, type: shop_mode};
storeOrder(); storeOrder();
cart.find('.cart-elt').remove(); cart.find('.cart-elt').remove();
updateCartTotal(0, 0); updateCartTotal(0, 0);
...@@ -1053,14 +1060,22 @@ var loadAllAvailableBoughtProducts = function() { ...@@ -1053,14 +1060,22 @@ var loadAllAvailableBoughtProducts = function() {
}; };
var appendChildrenCatToMenu = function (catdiv, children) { var appendChildrenCatToMenu = function (catdiv, children) {
// console.log(children)
var ul = catdiv.find('ul'); var ul = catdiv.find('ul');
$.each(children, function(i, e) { $.each(children, function(i, e) {
if (excluded_cat.indexOf(e.id) < 0) { if (excluded_cat.indexOf(e.id) < 0) {
var li = $('<li>').addClass("nav-item"); var li = $('<li>').addClass("nav-item");
// Remove TVA in cat name
let name = e.name;
name = name.replaceAll(' TVA 20%', '');
name = name.replaceAll(' TVA 5,5%', '');
name = name.replaceAll(' 20%', '');
name = name.replaceAll(' 5,5%', '');
var span = $('<span>').attr('data-id', e.id) var span = $('<span>').attr('data-id', e.id)
.text(e.name); .text(name);
li.append(span); li.append(span);
ul.append(li); ul.append(li);
...@@ -1347,7 +1362,8 @@ var changeBestDate = function() { ...@@ -1347,7 +1362,8 @@ var changeBestDate = function() {
msg.find('select[name="bhour-change"]').html(cart_vform.find('select[name="bhour"]').html()); msg.find('select[name="bhour-change"]').html(cart_vform.find('select[name="bhour"]').html());
msg.find('.slots-constraints').html(cart_vform.find('.slots-constraints').html()); msg.find('.slots-constraints').html(cart_vform.find('.slots-constraints').html());
fillBDayOptions(bday_change_sel); if (shop_mode == 'shop')
fillBDayOptions(bday_change_sel);
updateUnavailableSlots(function() { updateUnavailableSlots(function() {
openModal(msg.html(), function() { openModal(msg.html(), function() {
...@@ -1473,7 +1489,8 @@ var launch_init_form = function() { ...@@ -1473,7 +1489,8 @@ var launch_init_form = function() {
make_user_wait('Préparation du formulaire...'); make_user_wait('Préparation du formulaire...');
current_action = 'init_form'; current_action = 'init_form';
updateUnavailableSlots(function() { updateUnavailableSlots(function() {
fillBDayOptions(bday_sel); if (shop_mode == 'shop')
fillBDayOptions(bday_sel);
openModal($('#cart_creation_form').html(), initCart, 'Commencer la commande'); openModal($('#cart_creation_form').html(), initCart, 'Commencer la commande');
}); });
}; };
...@@ -1520,7 +1537,9 @@ var justVisit = function() { ...@@ -1520,7 +1537,9 @@ var justVisit = function() {
pass2step2(); pass2step2();
}; };
var order = getStoredOrder() || {products: [], total: 0.00}; var current_order_name = shop_mode == 'shop' ? 'currentShopOrder' : 'currentDeliveryOrder';
var saved_order_name = shop_mode == 'shop' ? 'saved_shop_order' : 'saved_delivery_order';
var order = getStoredOrder() || { products: [], total: 0.00, type: shop_mode};
var content1 = $('#tab-content1'); var content1 = $('#tab-content1');
var content4 = $('#tab-content4'); var content4 = $('#tab-content4');
var category_elts = getStoredCatElts() || {}; var category_elts = getStoredCatElts() || {};
......
...@@ -354,6 +354,18 @@ function init_and_fill_order_list() { ...@@ -354,6 +354,18 @@ function init_and_fill_order_list() {
orders_table.destroy(); orders_table.destroy();
var cols = [ var cols = [
{data: 'h_submitted_date', title: "Date commande"}, {data: 'h_submitted_date', title: "Date commande"},
{
data: 'type',
title: "Type",
width: "5%",
render: function (data) {
if (data == 'delivery') {
return 'Livraison';
} else {
return 'Commande';
}
}
},
{data: 'partner.display_name', title: "Nom"}, {data: 'partner.display_name', title: "Nom"},
{data: 'best_date', title: "Livraison"}, {data: 'best_date', title: "Livraison"},
{data: 'total', title: "Montant"} {data: 'total', title: "Montant"}
......
...@@ -5,7 +5,8 @@ from . import views ...@@ -5,7 +5,8 @@ from . import views
from . import admin from . import admin
urlpatterns = [ urlpatterns = [
url(r'^$', views.index), url(r'^$', views.shop_index),
url(r'^delivery$', views.delivery_index),
url(r'^get_all_available_bought_products$', views.get_all_available_bought_products), url(r'^get_all_available_bought_products$', views.get_all_available_bought_products),
url(r'^get_cat_children$', views.get_cat_children), url(r'^get_cat_children$', views.get_cat_children),
url(r'^get_categ_products$', views.get_categ_products), url(r'^get_categ_products$', views.get_categ_products),
......
...@@ -5,13 +5,20 @@ from outils.for_view_imports import * ...@@ -5,13 +5,20 @@ from outils.for_view_imports import *
from members.models import CagetteMember from members.models import CagetteMember
from shop.models import CagetteShop from shop.models import CagetteShop
@never_cache @never_cache
def index(request): 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') template = loader.get_template('shop/index.html')
credentials = CagetteMember.get_credentials(request) credentials = CagetteMember.get_credentials(request)
shop_settings = CagetteShop.get_shop_settings() shop_settings = CagetteShop.get_shop_settings()
context = {'title': 'Commande / Réservation', context = {'title': 'Commande / Réservation',
'mode': mode,
'COMPANY_NAME': settings.COMPANY_NAME, 'COMPANY_NAME': settings.COMPANY_NAME,
'SHOP_CATEGORIES': settings.SHOP_CATEGORIES, 'SHOP_CATEGORIES': settings.SHOP_CATEGORIES,
'EXCLUDE_SHOP_CATEGORIES': settings.EXCLUDE_SHOP_CATEGORIES, 'EXCLUDE_SHOP_CATEGORIES': settings.EXCLUDE_SHOP_CATEGORIES,
...@@ -44,8 +51,12 @@ def index(request): ...@@ -44,8 +51,12 @@ def index(request):
context['SHOP_SLOT_SIZE'] = settings.SHOP_SLOT_SIZE context['SHOP_SLOT_SIZE'] = settings.SHOP_SLOT_SIZE
if hasattr(settings, 'SHOP_OPENING_START_DATE'): if hasattr(settings, 'SHOP_OPENING_START_DATE'):
context['SHOP_OPENING_START_DATE'] = settings.SHOP_OPENING_START_DATE context['SHOP_OPENING_START_DATE'] = settings.SHOP_OPENING_START_DATE
if hasattr(settings, 'SHOP_CAN_BUY'): if mode == 'shop' and hasattr(settings, 'SHOP_CAN_BUY'):
context['SHOP_CAN_BUY'] = settings.SHOP_CAN_BUY context['SHOP_CAN_BUY'] = settings.SHOP_CAN_BUY
context['DELIVERY_CAN_BUY'] = False
if mode == 'delivery' and hasattr(settings, 'DELIVERY_CAN_BUY'):
context['SHOP_CAN_BUY'] = False
context['DELIVERY_CAN_BUY'] = settings.DELIVERY_CAN_BUY
d_p_pdts = CagetteShop.get_promoted_and_discounted_products() d_p_pdts = CagetteShop.get_promoted_and_discounted_products()
context['discounted_pdts'] = d_p_pdts['discounted'] context['discounted_pdts'] = d_p_pdts['discounted']
...@@ -129,7 +140,8 @@ def cart_init(request): ...@@ -129,7 +140,8 @@ def cart_init(request):
try: try:
# first of all, verifiying cart is respecting time slots constraints # first of all, verifiying cart is respecting time slots constraints
cart = json.loads(request.POST.get('order')) cart = json.loads(request.POST.get('order'))
ts_respect = CagetteShop.isCartRespectingTimeSlotContraints(cart) # Verify for shop only, not for delivery
ts_respect = CagetteShop.isCartRespectingTimeSlotContraints(cart) if cart['type'] == 'shop' else True
if (ts_respect is False): if (ts_respect is False):
result['ts_respect'] = False result['ts_respect'] = False
result['error'] = 'Forbidden timeslot' result['error'] = 'Forbidden timeslot'
......
...@@ -8,6 +8,31 @@ from datetime import datetime ...@@ -8,6 +8,31 @@ from datetime import datetime
class CagetteStock(models.Model): class CagetteStock(models.Model):
@staticmethod @staticmethod
def get_movements(movement_type, date_from, date_to):
o_api = OdooAPI()
errors = []
if movement_type == 'losses':
location_dest_id = settings.LOSSES_LOC_ID
elif movement_type == 'meals':
location_dest_id = settings.MEALS_LOC_ID
elif movement_type == 'autoconso':
location_dest_id = settings.AUTOCONSO_LOC_ID
else:
errors.append('Type de mouvement incorrect')
return {'errors': errors}
f = ['name', 'date_done', 'inventory_value']
c = [['location_dest_id', "=", location_dest_id],
['date_done', ">=", date_from],
['date_done', "<=", date_to],
['state', "=", 'done']]
res = o_api.search_read('stock.picking', c, f)
return res
@staticmethod
def do_stock_movement(stock_movement_data): def do_stock_movement(stock_movement_data):
"""Do a stock movement : """ """Do a stock movement : """
...@@ -18,29 +43,33 @@ class CagetteStock(models.Model): ...@@ -18,29 +43,33 @@ class CagetteStock(models.Model):
# Set stock movement details according to destination # Set stock movement details according to destination
if stock_movement_data['movement_type'] == 'losses': if stock_movement_data['movement_type'] == 'losses':
picking_name = 'Pertes - ' + datetime.now().strftime("%d/%m/%Y %H:%M:%S") picking_name = 'Pertes - '
picking_type = settings.LOSSES_PICKING_TYPE_ID picking_type = settings.LOSSES_PICKING_TYPE_ID
destination = settings.LOSSES_LOC_ID destination = settings.LOSSES_LOC_ID
elif stock_movement_data['movement_type'] == 'meals': elif stock_movement_data['movement_type'] == 'meals':
picking_name = 'Repas Salarié - ' + datetime.now().strftime("%d/%m/%Y %H:%M:%S") picking_name = 'Repas Salarié - '
picking_type = settings.MEALS_PICKING_TYPE_ID picking_type = settings.MEALS_PICKING_TYPE_ID
destination = settings.MEALS_LOC_ID destination = settings.MEALS_LOC_ID
elif stock_movement_data['movement_type'] == 'autoconso': elif stock_movement_data['movement_type'] == 'autoconso':
picking_name = 'Autoconsommation - ' + datetime.now().strftime("%d/%m/%Y %H:%M:%S") picking_name = 'Autoconsommation - '
picking_type = settings.AUTOCONSO_PICKING_TYPE_ID picking_type = settings.AUTOCONSO_PICKING_TYPE_ID
destination = settings.AUTOCONSO_LOC_ID destination = settings.AUTOCONSO_LOC_ID
else: else:
errors.append('Type de mouvement incorrect') errors.append('Type de mouvement incorrect')
return {'errors': errors, 'picking_id': picking} return {'errors': errors, 'picking_id': picking}
picking_name += datetime.now().strftime("%d/%m/%Y %H:%M:%S")
picking_name += ' - ' + stock_movement_data['operator']['name'] + ' ('+ str(stock_movement_data['operator']['barcode_base']) + ')'
fields = { fields = {
'company_id': 1, 'company_id': 1,
'name': picking_name, 'name': picking_name,
'picking_type_id' : picking_type, # mouvement type 'picking_type_id' : picking_type, # mouvement type
'location_id': settings.STOCK_LOC_ID, # movement origin 'location_id': settings.STOCK_LOC_ID, # movement origin
'location_dest_id': destination, # movement dest 'location_dest_id': destination, # movement dest
"move_lines": [], 'move_lines': [],
"pack_operation_ids": [] 'pack_operation_ids': [],
'operator_id': stock_movement_data['operator']['id']
} }
for p in stock_movement_data['products']: for p in stock_movement_data['products']:
......
.main, .barcode_search_area {
margin-top: 40px;
}
#sm_barcode_selector {
border: 1px solid #555;
border-radius:10px;
}
.stock_edit_input {
max-width: 60px;
}
#movement_validation_button {
margin-top: 20px;
}
.notifyjs-test1-base {
background: #5CB85C !important;
}
.confirmation_checking_msgs {
padding-bottom: 10px;
}
.remove_row_icon {
font-size: 1.4em;
color: #d9534f;
cursor: pointer;
}
.blink_me {
animation: blinker 1s ease-in-out;
}
@keyframes blinker {
15% { background-color: #218D9B ; } /* F0AD4E*/
}
#back_to_movement_selection {
position:absolute;
left:0;
margin: 5px;
}
.movement_type_buttons {
margin-top: 60px;
}
.movement_type_button {
height: 2.2em;
width: 30%;
border-radius: 3px;
margin: 10px;
font-size: 1.3em;
}
.movement_type_button_icons {
float: right;
margin: 2px;
}
.page_body {
position: relative;
}
.page_content {
position: absolute;
top: 0;
left: 0;
right: 0;
}
.set_member_to_movement {
margin-top: -10px;
}
.search_member_results_area {
margin-top: 10px;
}
.select_movement_area {
margin-top: 25px;
}
.select_movement_element {
display: inline-block;
margin-right: 10px;
}
.main {
margin-top: 25px;
}
.table_area {
margin: auto;
width: 50%;
padding: 10px;
}
.select_movement_input {
border-radius:5px;
}
.btn_export_movements {
margin-top: 10px;
}
/* eslint consistent-this: ["warn", "that"] */
/*
* Récupération de protuits par scan et saisie de quantités pour créer des mouvements de stock.
*
* Mouvements disponibles :
* - Pertes
* - Repas salariés
* - Autoconsommation
*/
var products = [],
products_table = null,
confirmation_table = null,
barcodes = null,
movement_type = null,
members_search_results = [],
operator = null;
function reset_focus() {
// Wait a little bit to reset, or ignored after paste
setTimeout(function() {
$('#sm_barcode_selector').val('');
$('#icon_product_not_found').hide();
$('#sm_barcode_selector').focus();
}, 100);
}
/*
* Remove a row from datatable
*/
function remove_row(row) {
var data = products_table.row($(row).parents('tr')).data();
for (i in products) {
if (products[i].id == data.id) {
products.splice(i, 1);
break;
}
}
products_table
.row($(row).parents('tr'))
.remove()
.draw();
update_total_value();
}
/*
* Make a row blink
*/
function blink_row(rowNode) {
$(rowNode).addClass('blink_me');
rowNode.addEventListener('animationend', onAnimationEnd);
rowNode.addEventListener('webkitAnimationEnd', onAnimationEnd);
function onAnimationEnd() {
rowNode.classList.remove('blink_me');
}
}
/*
* Init or reinit the products datatable with a new set of products
*/
function init_datatable() {
// Empty datatable if already exists
if (products_table) {
products_table.destroy();
}
// Only init datatable if array of products not empty
if (products.length > 0) {
products_table = $('#products_table').DataTable({
data: products,
columns:[
{data:"id", title: "id", visible: false},
{
data:"name",
title:"Produit",
width: "50%",
render: function (data, type, full) {
// Add tooltip with barcode over product name
let display_barcode = "Aucun";
if ('barcode' in full) {
display_barcode = full.barcode;
}
return '<div class="tooltip">' + data
+ ' <span class="tooltiptext tt_twolines">Code barre : '
+ display_barcode + '</span> </div>';
}
},
{
data:"uom.name",
title: "Unité de vente",
// className:"dt-body-center",
orderable: false
},
{
title: "Quantité",
// className:"dt-body-center",
orderable: false,
render: function (data, type, full) {
var value = ('qty' in full) ? full.qty : '';
return '<input type="number" class="stock_edit_input" value="' + value + '">';
// To force decimal to be . , add lang="en-150" to input tag
// + ' <button type="button" class="stock_edit_button btn--primary"><i class="fas fa-lg fa-check"></i></button>'
}
},
{
data:"standard_price",
title: "Valeur HT",
// className:"dt-body-center",
render: function (data, type, full) {
if ('qty' in full && full.qty != null) {
var value = parseFloat(full.qty*data).toFixed(2);
return value + '€';
} else
return '';
}
},
{
data:"list_price",
title: "Prix Vente TTC",
// className:"dt-body-center",
render: function (data, type, full) {
if ('qty' in full && full.qty != null) {
var value = parseFloat(full.qty*data).toFixed(2);
return value + '€';
} else
return '';
}
},
{
title: "",
orderable: false,
className:"dt-body-center",
width: "1%",
render: function () {
return '<span class="remove_row_icon">' +
'<i class="fas fa-times"></i>' +
'</span>';
}
}
],
order: [
[
0,
"asc"
]
],
paging: false,
dom: 'lrtip', // Remove the search input from that table
language: {url : '/static/js/datatables/french.json'}
});
// Listener on inputs
$('#products_table tbody').on('change', '.stock_edit_input', function () {
let qty = $(this).val();
let row = products_table.row($(this).parents('tr'));
let data = row.data();
let validated_data = qty_validation(qty, data.uom.id);
if (validated_data >= 0) {
data.qty = validated_data;
row.remove().draw();
products_table.row.add(data).draw();
} else {
data.qty = null;
row.remove().draw();
products_table.row.add(data).draw();
if (validated_data == -2) {
$.notify("Ce produit est vendu à l'unité : la valeur doit être un nombre entier !", {
globalPosition:"top right",
className: "error"
});
} else if (validated_data == -1 || validated_data == -3) {
$.notify("Valeur invalide.", {
globalPosition:"top right",
className: "error"
});
}
}
update_total_value();
});
// Remove row
products_table.on('click', '.remove_row_icon', function () {
var that = this;
openModal($('#modal_confirm_delete_line').html(), function() {
remove_row(that);
}, 'Confirmer');
});
// Show validation button
$('.footer').show();
}
}
function init_confirmation_datatable() {
if (confirmation_table) {
confirmation_table.destroy();
}
confirmation_table = $('#confirmation_table').DataTable({
data: products,
autoWidth: false,
columns:[
{data:"id", title: "id", visible: false},
{
data:"name",
title:"Produit",
width: "50%"
},
{
data:"qty",
title: "Quantité",
className:"dt-body-center"
},
{
data:"uom.name",
title: "Unité de vente",
className:"dt-body-center"
},
{
data:"standard_price",
title: "Valeur",
className:"dt-body-center",
render: function (data, type, full) {
var value = parseFloat(full.qty*data).toFixed(2);
return value + '€';
}
}
],
order: [
[
0,
"asc"
]
],
paging: false,
dom: 'lrtp', // Remove the search input from that table
language: {url : '/static/js/datatables/french.json'}
});
}
function without_consent_update_product(p) {
let undo_option = true;
update_existing_product(p, undo_option);
}
function get_stored_product_with_bc(barcode) {
/* return product in products variable which have the same barcode */
let product = null;
for (let p of products) {
if (p.barcode == barcode) {
product = p;
}
}
return product;
}
/*
* Fetch a product when barcode is read
*/
function fetch_product_from_bc(barcode) {
//console.log(barcode)
if (barcode == '') {
reset_focus();
return -1;
}
let p = barcodes.get_corresponding_odoo_product(barcode);
if (p == null) {
$('#icon_product_not_found').show();
alert("Le code-barre " + barcode + " ne correspond à aucun article connu.");
return -1;
}
/*
p.data[barcodes['keys']['uom_id']] is the Odoo uom database id
barcodes['uoms'] is an associative array of uoms (with id as key)
*/
let product = {
'id': p.data[barcodes['keys']['id']],
'barcode': p.barcode,
'name': p.data[barcodes['keys']['name']],
'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
};
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);
return 0;
} else {
add_product(product);
reset_focus();
return 0;
}
}
/*
* Add a product to the datatable
*/
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);
if (products_table == null) {
// create table, show panel
init_datatable();
} else {
// Add row to table
var rowNode = products_table.row.add(product).draw(false)
.node();
// Handle blinking effect for new row
blink_row(rowNode);
}
update_total_value();
$('.footer').show(); // if is a second or more access, footer is hidden (init_datatable is not fired)
} catch (e) {
err = {msg: e.name + ' : ' + e.message, ctx: 'add_product'};
console.error(err);
report_JS_error(err, 'stock_movement');
}
};
var update_in_products = function(product) {
//update product in products , after qty has been changed
let i = 0,
p_index = null;
for (let p of products) {
if (p.barcode == product.barcode) {
p_index = i;
}
i++;
}
if (p_index !== null) products[p_index] = 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;
let op = "augmentée";
let notify_options = {
globalPosition:"top right",
className: "info",
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)
.filter(function (rowIdx) {
return products_table.cell(rowIdx, 0).data() === product.id ? true : false;
});
// Loop through them (only one match)
products_table.rows(indexes).every(function () {
this.remove().draw();
var rowNode = products_table.row.add(product).draw()
.node();
// Blink updated row
blink_row(rowNode);
return 0;
});
if (added_qty < 0) op = "diminuée";
let msg = "La quantité a été " + op + " de " + Math.abs(added_qty) + ".";
if (undo_option == true) {
notify_options.clickToHide = true;
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>';
}
$.notify(msg, notify_options);
update_in_products(product);
update_total_value();
reset_focus();
};
/*
* Validate qty
* Returns qty or error code
* Error codes :
* >= 0 = ok
* -1 = empty
* -2 = invalid integer
* -3 = invalid value
*
*/
function qty_validation(qty, uom_id) {
if (qty == null || qty == '') {
return -1;
}
if (uom_id == 1) {
if (qty/parseInt(qty) != 1 && qty != 0)
return -2;
qty = parseInt(qty); // product by unit
} else {
qty = parseFloat(qty).toFixed(2);
}
if (isNaN(qty))
return -3;
return qty;
}
// Update the total value from values in datatable
function update_total_value() {
var total = 0;
if (products_table) {
products_table.rows().every(function () {
var data = this.data();
if ('qty' in data) {
total += data.qty*data.standard_price;
}
return 0;
});
}
total = parseFloat(total).toFixed(2);
$('.total_value').text(total);
}
// Validation & open confirmation modal
function confirm_movement() {
// Create a temp variable to get all data from table
let tmp_products_data = [];
// Check again for errors
var error_in_table = false;
products_table.rows().every(function () {
let data = this.data();
let qty_value = $(this.node()).find('input')
.val();
qty_value = qty_validation(qty_value, data.uom.id);
if (qty_value < 0) {
error_in_table = true;
}
tmp_products_data.push(data);
return 0;
});
if (error_in_table) {
$.notify("Il y a une erreur dans le tableau ! (Tous les champs sont obligatoires)", {
globalPosition:"top right",
className: "error"
});
return -1;
}
// If no error, assign global 'products' variable with table data
products = tmp_products_data;
var modal_confirm_movement = $('#modal_confirm_movement');
// Set confirmation message
if (movement_type == 'losses') {
msg = 'Êtes-vous sûr de vouloir passer ces produits en <b>PERTES</b> ?';
} else if (movement_type == 'meals') {
msg = 'Êtes-vous sûr de vouloir passer ces produits en <b>Repas salarié</b> ?';
} else if (movement_type == 'autoconso') {
msg = 'Êtes-vous sûr de vouloir passer ces produits en <b>Autoconsommation</b> ?';
} else if (movement_type == 'stock_correction') {
msg = 'Êtes-vous sûr de vouloir corriger les stocks de ces produits ?';
}
modal_confirm_movement.find('#confirm_message_movtype').html(msg);
openModal(modal_confirm_movement.html(), function() {
do_stock_movement();
}, 'Confirmer', false);
init_confirmation_datatable();
// Set action to search for the member
$('#sm_search_member_form').submit(function() {
let search_str = $('#sm_search_member_input').val();
$.ajax({
url: '/members/search/' + search_str,
dataType : 'json',
success: function(data) {
members_search_results = [];
operator = null;
enable_validation();
for (member of data.res) {
if (member.is_member || member.is_associated_people) {
members_search_results.push(member);
}
}
display_possible_members();
},
error: function(data) {
err = {
msg: "erreur serveur lors de la recherche de membres",
ctx: 'confirm_movement.search_members'
};
report_JS_error(err, 'stock');
$.notify("Erreur lors de la recherche de membre, il faut ré-essayer plus tard...", {
globalPosition:"top right",
className: "error"
});
}
});
});
// Set modal DOM & data
$('.search_member_results_area').hide();
$('.search_member_results').empty();
operator = null;
enable_validation();
// Check if prices displayed are correct
var ids = [];
for (p of products) {
ids.push(p.id);
}
$.ajax({
type: "POST",
url: "/products/get_products_stdprices",
dataType: "json",
traditional: true,
contentType: "application/json; charset=utf-8",
data: JSON.stringify(ids),
success: function(data) {
var price_changed = false;
for (p of products) {
for (verif_p of data.res) {
if (p.id == verif_p.id && p.standard_price != verif_p.standard_price) {
p.standard_price = verif_p.standard_price;
price_changed = true;
}
}
}
$('#confirmation_checking_price_msg').hide();
if (price_changed) {
$('#confirmation_price_changed').show();
init_confirmation_datatable();
update_total_value();
init_datatable();
}
},
error: function(data) {
// server error
err = {msg: "erreur serveur lors de la récupération des prix", ctx: 'check_prices'};
if (typeof data.responseJSON != 'undefined' && typeof data.responseJSON.error != 'undefined') {
err.msg += ' : ' + data.responseJSON.error;
}
report_JS_error(err, 'stock');
console.error(err);
$.notify("Erreur lors de la récupération des prix des produits", {
globalPosition:"top right",
className: "error"
});
}
});
return 0;
}
// Enable or disable modal's validation button if an operator has been selected
function enable_validation() {
$('#modal .btns .btn--success').prop('disabled', (operator == null));
}
// Display the members from the search result
function display_possible_members() {
$('.search_member_results_area').show();
$('.search_member_results').empty();
if (members_search_results.length > 0) {
for (member of members_search_results) {
let btn_classes = "btn";
if (operator != null && operator.id == member.id) {
btn_classes = "btn--success";
}
// Display results (possible members) as buttons
var member_button = '<button class="' + btn_classes + ' btn_member" member_id="'
+ member.id + '">'
+ member.barcode_base + ' - ' + member.name
+ '</button>';
$('.search_member_results').append(member_button);
// Set action on click on a member button
$('.btn_member').on('click', function() {
for (member of members_search_results) {
if (member.id == $(this).attr('member_id')) {
operator = member;
// Enable validation button when operator is selected
enable_validation();
break;
}
}
display_possible_members();
});
}
} else {
$('.search_member_results').html('<p><i>Aucun résultat ! Vérifiez votre recherche...</i></p>');
}
}
// Proceed with stock movement, according to movement type selected
function do_stock_movement() {
if (is_time_to('do_stock_movement', 500)) {
openModal();
let movement_data = {
'movement_type': movement_type,
'operator': operator,
'products': products.map(obj => { // transmit only uom id (not all staff)
obj.uom_id = obj.uom.id;
delete obj.uom;
return obj;
})
};
openModal();
$.ajax({
type: "POST",
url: "/stock/do_movement",
dataType: "json",
traditional: true,
contentType: "application/json; charset=utf-8",
data: JSON.stringify(movement_data),
success: function() {
closeModal();
$.notify("Opération réussie !", {
globalPosition:"top right",
className: "success"
});
$('#back_to_movement_selection').trigger('click');
// Reset datatable, other values and focus
products = [];
products_table.clear().draw();
$('.footer').hide();
},
error: function(data) {
// server error
err = {msg: "erreur serveur lors de l'enregistrement du mouvement de stock", ctx: 'do_stock_movement'};
if (typeof data.responseJSON != 'undefined' && typeof data.responseJSON.error != 'undefined') {
err.msg += ' : ' + data.responseJSON.error;
}
report_JS_error(err, 'stock');
console.error(err);
closeModal();
reset_focus();
$.notify("Erreur lors de l'opération.", {
globalPosition:"top right",
className: "error"
});
}
});
}
}
// Load barcodes at page loading, then barcodes are stored locally
var get_barcodes = async function() {
if (barcodes == null) barcodes = await init_barcodes();
};
function init_notify_cancelable_style() {
try {
$.notify.addStyle('cancelable', {
html:
"<div class='info'>" +
"<div class='clearfix'>" +
"<div data-notify-html/>" +
"<div class='buttons'>" +
"<button class='no btn--danger'>Annuler</button>" +
"<button class='yes btn--info'>Fermer</button>" +
"</div>" +
"</div>" +
"</div>"
});
} catch (e) {
console.log(e);
}
}
$(document).ready(function() {
var barcode_input = $('#sm_barcode_selector');
$.ajaxSetup({ headers: { "X-CSRFToken": getCookie('csrftoken') } });
// Load barcodes
get_barcodes();
// Set focus on input for barcode reader
reset_focus();
$('#movement_validation_button').on('click', function () {
confirm_movement();
});
$(document).pos();
$(document).on('scan.pos.barcode', function(event) {
// Search for barcode only if movement type is selected
if (movement_type != null) {
//access `event.code` - barcode data
var barcode = event.code;
if (barcode.length >=13) {
barcode = barcode.substring(barcode.length-13);
} else if (barcode.length == 12 && barcode.indexOf('0') !== 0) {
// User may use a scanner which remove leading 0
barcode = '0' + barcode;
} else {
//manually submitted after correction
barcode = barcode_input.val();
}
fetch_product_from_bc(barcode);
}
});
barcode_input.on('paste', function(e) {
if (movement_type != null) {
let barcode_candidate = e.originalEvent.clipboardData.getData('text')
.replace(/[^0-9]/g, '');
fetch_product_from_bc(barcode_candidate);
}
});
// Change screen with animation when a movement type is selected
$('.movement_type_button').on('click', function() {
// Specific behavior for each movement type selection
if (this.id == 'losses_type_button') {
$('#title_movement_type').text('Saisie de Pertes');
movement_type = 'losses';
} else if (this.id == 'autoconso_type_button') {
$('#title_movement_type').text('Saisie d\'Autoconsommation');
movement_type = 'autoconso';
} else if (this.id == 'meals_type_button') {
$('#title_movement_type').text('Saisie de Repas salariés');
movement_type = 'meals';
}
var oldBox = $("#content_movement_type_selection");
var newBox = $("#content_main");
// Get full width
var outerWidth = oldBox.outerWidth(true);
// Display the new box and place it on the right of the screen
newBox.css({ "left": outerWidth + "px", "right": -outerWidth + "px", "display": "" });
// Make the old content slide to the left
oldBox.animate({ "left": -outerWidth + "px", "right": outerWidth + "px" }, 1000, function() {
// Hide old content after animation
oldBox.css({ "left": "", "right": "", "display": "none" });
});
// Slide new box to regular place
newBox.animate({ "left": "", "right": "" }, 1000);
});
// Back to movement type selection
$('#back_to_movement_selection').on('click', function() {
movement_type = null;
var oldBox = $("#content_main");
var newBox = $("#content_movement_type_selection");
// Get full width
var outerWidth = oldBox.outerWidth(true);
// Display the new box and place it on the right of the screen
newBox.css({ "left": -outerWidth + "px", "right": outerWidth + "px", "display": "" });
// Make the old content slide to the left
oldBox.animate({ "left": outerWidth + "px", "right": -outerWidth + "px" }, 1000, function() {
// Hide old content after animation
oldBox.css({ "left": "", "right": "", "display": "none" });
});
// Slide new box to regular place
newBox.animate({ "left": "", "right": "" }, 1000);
});
// Notifications settings
init_notify_cancelable_style();
$(document).on('click', '.notifyjs-cancelable-base .no', function() {
//programmatically trigger propogating hide event
let msg = $(this).closest('.notifyjs-cancelable-base')
.find('span.msg');
if (msg.length > 0) {
let bc = msg.data('barcode');
let added_qty = msg.data('added_qty') || 1;
let product = get_stored_product_with_bc(bc);
if (product !== null) {
product.value = - added_qty;
update_existing_product(product);
} else {
alert("Le produit n'a pas été retrouvé dans la mémoire de travail.");
}
} else {
alert("Les informations n'ont pas pu être récupérées.");
}
//$(this).trigger('notify-hide');
});
});
/*
* Extract and display a stock movement type between two dates
*
*/
var dateFormat = "yy-mm-dd",
from_datepicker = null,
to_datepicker = null,
movements_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 ($('#movement_type_selector').val() != '' &&
getDate(from_datepicker.val()) &&
getDate(to_datepicker.val())) {
$('#movement_selection_button').prop('disabled', false);
} else {
$('#movement_selection_button').prop('disabled', true);
}
}
function display_movements(movements) {
total_value = 0;
for (move of movements) {
total_value += move.inventory_value;
}
$('#count_movements').text(movements.length);
$('#total_value_movements').text(parseFloat(total_value).toFixed(2));
// Empty datatable if already exists
if (movements_table) {
movements_table.destroy();
}
movements_table = $('#movements_table').DataTable({
data: movements,
columns:[
{
data:"name",
title:"Mouvement (Type - Date - Fait par)",
width: "70%"
},
{
data:"inventory_value",
title: "Valeur",
className:"dt-body-center",
render: function (data) {
return parseFloat(data).toFixed(2) + ' €';
}
},
{
data:"date_done",
title:"Trier par Date",
className:"dt-body-center",
render: function (data) {
return '<i>' + data + '</i>';
}
}
],
order: [
[
2,
"desc"
]
],
buttons: [
{
extend: 'excelHtml5',
text: 'Export en Excel',
className: 'btn--primary btn_export_movements'
},
{
extend: 'pdfHtml5',
text: 'Export en PDF',
className: 'btn--primary btn_export_movements'
}
],
dom: '<lr<t>ip><"clear"><B>',
iDisplayLength: 10,
language: {url : '/static/js/datatables/french.json'}
});
$('.main').show();
}
function get_movements() {
openModal();
var url = "/stock/get_movements";
url += '?movement_type=' + encodeURIComponent($('#movement_type_selector').val());
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_movements(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_movement_element').change(function() {
enable_validation();
});
$('#movement_selection_button').click(function() {
get_movements();
});
});
...@@ -4,8 +4,10 @@ from django.conf.urls import url ...@@ -4,8 +4,10 @@ from django.conf.urls import url
from . import views from . import views
urlpatterns = [ urlpatterns = [
url(r'^movements', views.movements_page), url(r'^movements$', views.movements_page),
url(r'^do_movement', views.do_movement), url(r'^do_movement', views.do_movement),
url(r'^movements_view$', views.movements_view),
url(r'^get_movements$', views.get_movements),
### NOT IN USE: ### ### NOT IN USE: ###
......
...@@ -18,6 +18,15 @@ def movements_page(request): ...@@ -18,6 +18,15 @@ def movements_page(request):
return HttpResponse(template.render(context, request)) return HttpResponse(template.render(context, request))
def movements_view(request):
"""Page d'extraction des mouvements de stocks"""
context = {
'title': 'Mouvements de stock'
}
template = loader.get_template('stock/stock_movements_view.html')
return HttpResponse(template.render(context, request))
def do_movement(request): def do_movement(request):
"""Do the stock movement: losses, self conso or stock correction""" """Do the stock movement: losses, self conso or stock correction"""
res = {} res = {}
...@@ -46,6 +55,20 @@ def do_movement(request): ...@@ -46,6 +55,20 @@ def do_movement(request):
else: else:
return JsonResponse({'res': res}) return JsonResponse({'res': res})
def get_movements(request):
res = {}
movement_type = request.GET.get('movement_type', '')
date_from = request.GET.get('from', '')
date_to = request.GET.get('to', '')
res = CagetteStock.get_movements(movement_type, date_from, date_to)
if 'errors' in res and res['errors']:
return JsonResponse(res, status=500)
else:
return JsonResponse({'res': res})
### NOT IN USE ### ### NOT IN USE ###
# ??? a voir si on garde les heur d'ouverture de la cagette ici # ??? a voir si on garde les heur d'ouverture de la cagette ici
......
...@@ -47,7 +47,7 @@ ...@@ -47,7 +47,7 @@
</div> </div>
Recherche : Recherche :
<input type="text" name="search_string" autocomplete="off" /> <input type="text" name="search_string" autocomplete="off" />
<button class="btn--primary search">GO! <button class="btn--primary search">Cherche-moi !
</button> </button>
<span class="loading2"><img width="75" src="/static/img/Pedro_luis_romani_ruiz.gif" alt="Chargement en cours...." /></span> <span class="loading2"><img width="75" src="/static/img/Pedro_luis_romani_ruiz.gif" alt="Chargement en cours...." /></span>
<div class="row-1" id="multi_results_preview"> <div class="row-1" id="multi_results_preview">
...@@ -111,13 +111,13 @@ ...@@ -111,13 +111,13 @@
{% endif %} {% endif %}
</div> </div>
</section> </section>
</div> </div>
</div> </div>
</section> </section>
</div> </div>
<section id="photo_advice"> <section id="photo_advice">
<div class="col-6 big"><i class="fas fa-arrow-circle-up fa-3x "></i> <div class="col-6 big"><i class="fas fa-arrow-circle-up fa-3x "></i>
Regardez la caméra, en haut, pendant la pose <i class="fas fa-arrow-circle-up fa-3x"></i> Regardez la caméra, en haut, pendant la pose <i class="fas fa-arrow-circle-up fa-3x"></i>
...@@ -225,4 +225,4 @@ ...@@ -225,4 +225,4 @@
</section> </section>
<script src="{% static "js/all_common.js" %}?v="></script> <script src="{% static "js/all_common.js" %}?v="></script>
<script src="{% static "js/members.js" %}?v="></script> <script src="{% static "js/members.js" %}?v="></script>
{% endblock %} {% endblock %}
\ No newline at end of file
...@@ -15,7 +15,8 @@ ...@@ -15,7 +15,8 @@
<script type="text/javascript"> <script type="text/javascript">
var type = {{type}}, var type = {{type}},
context = 'inscription', context = 'inscription',
force_fn_hyphen = {% if force_firstname_hyphen %}true{% else %}false{% endif%}; force_fn_hyphen = {% if force_firstname_hyphen %}true{% else %}false{% endif%},
max_chq_nb = {{max_chq_nb}};
{% if open_on_sunday %} {% if open_on_sunday %}
let open_on_sunday = true let open_on_sunday = true
{% endif %} {% endif %}
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
<script type="text/javascript" src="{% static 'js/download.js' %}"></script> <script type="text/javascript" src="{% static 'js/download.js' %}"></script>
<script type="text/javascript" src="{% static 'js/datatables/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/datatables/dataTables.plugins.js' %}"></script> <script type="text/javascript" src="{% static 'js/datatables/dataTables.plugins.js' %}"></script>
<script type="text/javascript" src="{% static 'js/jquery.pos.js' %}"></script>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
...@@ -92,7 +93,6 @@ ...@@ -92,7 +93,6 @@
<span id="valid_all"></span> <span id="valid_all"></span>
<span id="all_left_is_good"> <span id="all_left_is_good">
<button class='btn--danger half_width_button' onclick="openModal($('#templates #modal_all_left_is_good').html(), confirm_all_left_is_good, 'Confirmer', false);">Tout le reste est bon</button> <button class='btn--danger half_width_button' onclick="openModal($('#templates #modal_all_left_is_good').html(), confirm_all_left_is_good, 'Confirmer', false);">Tout le reste est bon</button>
</span> </span>
</div> </div>
{% else %} {% else %}
...@@ -153,6 +153,14 @@ ...@@ -153,6 +153,14 @@
<p>Êtez-vous sûr ?</p> <p>Êtez-vous sûr ?</p>
<hr /> <hr />
</div> </div>
<div id="modal_set_supplier_shortage">
<h3>Attention !</h3>
<p>Vous vous apprêtez indiquer que le produit<b><span class="supplier_shortage_product"></span></b>
est en rupture chez le fournisseur<b><span class="supplier_shortage_supplier"></span></b>.</p>
<p>La quantité du produit sera mise à 0 si vous en êtes à l'étape du comptage de produits.</p>
<p>Êtez-vous sûr ?</p>
<hr />
</div>
<div id="modal_unprocessable_porducts"> <div id="modal_unprocessable_porducts">
<h3>Traitement impossible</h3> <h3>Traitement impossible</h3>
<p> <p>
...@@ -179,6 +187,6 @@ ...@@ -179,6 +187,6 @@
var fixed_barcode_prefix = '{{FIXED_BARCODE_PREFIX}}' var fixed_barcode_prefix = '{{FIXED_BARCODE_PREFIX}}'
</script> </script>
<script src="{% static "js/all_common.js" %}?v="></script> <script src="{% static "js/all_common.js" %}?v="></script>
<script src="{% static "js/common.js" %}?v="></script> <script src='{% static "js/barcodes.js" %}?v='></script>
<script type="text/javascript" src="{% static 'js/reception_produits.js' %}?v="></script> <script type="text/javascript" src="{% static 'js/reception_produits.js' %}?v="></script>
{% endblock %} {% endblock %}
...@@ -29,7 +29,8 @@ ...@@ -29,7 +29,8 @@
<p>Ça veut dire que soit :</p> <p>Ça veut dire que soit :</p>
<ul> <ul>
<li>vous avez raté deux services. Lorsque votre compteur passe à -2, vous êtes immédiatement suspendus,</li> <li>vous avez raté deux services. Lorsque votre compteur passe à -2, vous êtes immédiatement suspendus,</li>
<li>vous avez raté un service et vous ne l'avez pas rattrapé dans les 4 semaines qui ont suivis.</li> <li>vous avez raté un service et vous ne l'avez pas rattrapé dans les 4 semaines qui ont suivis,</li>
<li>vous avez été désinscrit.e pour avoir raté 3 services et venez d'être réinscrit.e.</li>
</ul> </ul>
<p>Heureusement, vous pouvez demander une extension !</p> <p>Heureusement, vous pouvez demander une extension !</p>
<p>Une extension vous donne un délais de quatre semaines supplémentaires pour rattraper le service manqué (ou les deux services manqués).Vous pouvez demander jusqu'à 6 extensions d'affilé.</p> <p>Une extension vous donne un délais de quatre semaines supplémentaires pour rattraper le service manqué (ou les deux services manqués).Vous pouvez demander jusqu'à 6 extensions d'affilé.</p>
......
...@@ -2,21 +2,25 @@ Bonjour,<br/> ...@@ -2,21 +2,25 @@ Bonjour,<br/>
<p> <p>
Votre commande du {{order_date}} a bien été prise en compte Votre commande du {{order_date}} a bien été prise en compte
</p> </p>
{% if cart.type == 'shop' %}
<p> <p>
Merci de venir à l’heure que vous avez indiquée. Merci de venir à l’heure que vous avez indiquée.
</p> </p>
<p> <p>
Au-delà d’une heure de retard, votre commande sera annulée. Au-delà d’une heure de retard, votre commande sera annulée.
</p> </p>
{% endif %}
<p> <p>
Nous vous rappelons que les prix et les quantités disponibles sont affichées à titre indicatif. Si vous avez accepté des substitutions de produits, il seront remplacés, en cas de rupture, en essayant de coller au mieux à votre commande. Le prix de votre panier dépendra du prix réel et des quantités réellement disponibles. Nous vous rappelons que les prix et les quantités disponibles sont affichées à titre indicatif. Si vous avez accepté des substitutions de produits, il seront remplacés, en cas de rupture, en essayant de coller au mieux à votre commande. Le prix de votre panier dépendra du prix réel et des quantités réellement disponibles.
</p> </p>
<p> <p>
La seule modification possible sur le panier commandé sera d’enlever un ou plusieurs articles. La seule modification possible sur le panier commandé sera d’enlever un ou plusieurs articles.
</p> </p>
{% if cart.type == 'shop' %}
<p> <p>
Merci de vous munir de votre chéquier et/ou CB. Merci de vous munir de votre chéquier et/ou CB.
</p> </p>
{% endif %}
<p> <p>
NB: en ce qui concerne le frais, nous mettons dans votre panier les dates les plus proches de la péremption. NB: en ce qui concerne le frais, nous mettons dans votre panier les dates les plus proches de la péremption.
</p> </p>
...@@ -34,7 +38,7 @@ Commande reçue : ...@@ -34,7 +38,7 @@ Commande reçue :
{% endfor %} {% endfor %}
</table> </table>
<br/> <br/>
Total TTC : {{cart.total}} € Total TTC : {{cart.total}} € (le prix qui vous sera demandé pourra être légèrement différent en fonction des disponibilités en magasin)
<br/> <br/>
{%if cart.accept_substitution %} {%if cart.accept_substitution %}
Vous acceptez les substitutions de produits. Vous acceptez les substitutions de produits.
...@@ -46,9 +50,11 @@ Vous avez laissé le commentaire suivant: <br/> ...@@ -46,9 +50,11 @@ Vous avez laissé le commentaire suivant: <br/>
{{cart.comment}} {{cart.comment}}
</p> </p>
{% endif %} {% endif %}
{% if cart.type == 'shop' %}
<p> <p>
Vous avez indiqué venir chercher la commande à cette date : "{{cart.best_date}}" Vous avez indiqué venir chercher la commande à cette date : "{{cart.best_date}}"
</p> </p>
{% endif %}
<p> <p>
Pour des raisons d'hygiène les commandes seront préparées dans des sacs en papier kraft qui vous seront facturées, 0,24€ pour les petits et 0,77€ pour les grands. <br/> Pour des raisons d'hygiène les commandes seront préparées dans des sacs en papier kraft qui vous seront facturées, 0,24€ pour les petits et 0,77€ pour les grands. <br/>
Merci de votre compréhension Merci de votre compréhension
......
...@@ -36,6 +36,10 @@ ...@@ -36,6 +36,10 @@
<li class="nav-item"> <li class="nav-item">
<span class="new-order">Nouvelle commande</span> <span class="new-order">Nouvelle commande</span>
</li> </li>
{% elif DELIVERY_CAN_BUY != False %}
<li class="nav-item">
<span class="new-order">Nouvelle commande</span>
</li>
{%endif%} {%endif%}
<li class="nav-item"> <li class="nav-item">
<span class="my-orders">Commande(s) envoyée(s)</span> <span class="my-orders">Commande(s) envoyée(s)</span>
...@@ -318,7 +322,10 @@ ...@@ -318,7 +322,10 @@
<div id="cart_creation_form"> <div id="cart_creation_form">
<p> <p>
<h2>Initialisation de la commande</h2> <h2>Initialisation de la commande</h2>
<span class="ask-day">Merci d'indiquer ci-dessous quand vous viendrez récupérer la commande au magasin</span><br/> {% if mode == 'shop' %}
<span class="ask-day">
Merci d'indiquer ci-dessous quand vous viendrez récupérer la commande au magasin
</span><br/>
<select name="bday"> <select name="bday">
</select> </select>
...@@ -341,6 +348,50 @@ ...@@ -341,6 +348,50 @@
<option value="17:00">17:00</option> <option value="17:00">17:00</option>
<option value="17:30">17:30</option> <option value="17:30">17:30</option>
</select> </select>
{% elif mode == 'delivery' %}
<span class="ask-day">
Merci d'indiquer ci-dessous un jour et une heure de livraison suggérés.<br>
Le jour et l'heure de la livraison seront choisis en direct avec le/la livreur.se en fonction de ses disponibilités.
</span><br/>
<select name="bday">
<option value="">---> Jour</option>
<option value="lundi">lundi</option>
<option value="mardi">mardi</option>
<option value="mercredi">mercredi</option>
<option value="jeudi">jeudi</option>
<option value="vendredi">vendredi</option>
<option value="samedi">samedi</option>
<option value="dimanche">dimanche</option>
</select>
<select name="bhour">
<option value="">---> Heure</option>
<option value="01:00">00:00</option>
<option value="01:00">01:00</option>
<option value="02:00">02:00</option>
<option value="03:00">03:00</option>
<option value="04:00">04:00</option>
<option value="05:00">05:00</option>
<option value="06:00">06:00</option>
<option value="07:00">07:00</option>
<option value="08:00">08:00</option>
<option value="09:00">09:00</option>
<option value="10:00">10:00</option>
<option value="11:00">11:00</option>
<option value="12:00">12:00</option>
<option value="13:00">13:00</option>
<option value="14:00">14:00</option>
<option value="15:00">15:00</option>
<option value="16:00">16:00</option>
<option value="17:00">17:00</option>
<option value="18:00">18:00</option>
<option value="19:00">19:00</option>
<option value="20:00">20:00</option>
<option value="21:00">21:00</option>
<option value="22:00">22:00</option>
<option value="23:00">23:00</option>
</select>
{% endif %}
<div class="slots-constraints" style="display: none;"> <div class="slots-constraints" style="display: none;">
<span class="delay24h"><strong>avec un délai de {{MIN_DELAY_FOR_SLOT}}h minimum</strong></span> <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/> <!--<input type="text" placeholder="Exemple: mardi 24 mars à 10h30" name="best_date" style="width: 350px;"/>--><br/>
...@@ -354,7 +405,9 @@ ...@@ -354,7 +405,9 @@
{%if HOURS_FOR_VALIDATION > 0 %} {%if HOURS_FOR_VALIDATION > 0 %}
<div class="tv-msg" style="display: none;"> <div class="tv-msg" style="display: none;">
<strong> <strong>
Vous aurez <span class="time-given-for-validation">{{HOURS_FOR_VALIDATION}} heure(s)</span> pour valider votre commande.<br/> Passé ce temps, la réservation du créneau horaire sera annulée.<br/>Vous devrez rechoisir un horaire (le panier sera mémorisé). 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> </strong>
</div> </div>
{%endif%} {%endif%}
...@@ -367,7 +420,7 @@ ...@@ -367,7 +420,7 @@
<div id="cart_validation_form"> <div id="cart_validation_form">
<h2>Validation de la commande</h2> <h2>Validation de la commande</h2>
<p class="pickup_date"> <p class="pickup_date">
Récupération au magasin : <span></span> {% if mode == 'shop' %}Récupération au magasin{% elif mode == 'delivery' %}Date et heure de livraison{%endif%} : <span></span>
</p> </p>
{%if SHOW_SUBSTITUTION_OPTION %} {%if SHOW_SUBSTITUTION_OPTION %}
<p class="accept_substitution"> <p class="accept_substitution">
...@@ -382,7 +435,9 @@ ...@@ -382,7 +435,9 @@
{%endif%} {%endif%}
</p> </p>
<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>Si vous arrivez avec un retard de plus d'une heure, la commande pourrait ne plus être disponible.</i></strong>
{%endif%}
</p> </p>
<p> <p>
{{CART_VALIDATION_BOTTOM_MSG}} {{CART_VALIDATION_BOTTOM_MSG}}
...@@ -413,6 +468,9 @@ ...@@ -413,6 +468,9 @@
{% if SHOP_CAN_BUY != False %} {% if SHOP_CAN_BUY != False %}
<li><button type="button" class="new-order btn--primary">Passer une nouvelle commande</button></li> <li><button type="button" class="new-order btn--primary">Passer une nouvelle commande</button></li>
{%endif%} {%endif%}
{% if DELIVERY_CAN_BUY != False %}
<li><button type="button" class="new-order btn--primary">Passer une nouvelle commande</button></li>
{%endif%}
<li><button type="button" class="my-orders btn--primary">Gérer mes commandes (*)</button></li> <li><button type="button" class="my-orders btn--primary">Gérer mes commandes (*)</button></li>
<li><button type="button" class="visit btn--primary">Parcourir le "magasin"</button></li> <li><button type="button" class="visit btn--primary">Parcourir le "magasin"</button></li>
...@@ -428,6 +486,7 @@ ...@@ -428,6 +486,7 @@
</div> </div>
</div> </div>
<script type="text/javascript"> <script type="text/javascript">
const shop_mode = '{{ mode|safe }}'
var categories = {{SHOP_CATEGORIES|safe}} var categories = {{SHOP_CATEGORIES|safe}}
var min_delay = {{MIN_DELAY_FOR_SLOT}} var min_delay = {{MIN_DELAY_FOR_SLOT}}
var hours_for_validation = {{HOURS_FOR_VALIDATION}} var hours_for_validation = {{HOURS_FOR_VALIDATION}}
......
Bonjour,<br/>
<p>
Votre commande du {{order_date}} a bien été prise en compte
</p>
{% if cart.type == 'shop' %}
<p>
Merci de venir à l’heure que vous avez indiquée.
</p>
<p>
Au-delà d’une heure de retard, votre commande sera annulée.
</p>
{% endif %}
<p>
Nous vous rappelons que les prix et les quantités disponibles sont affichées à titre indicatif.
</p>
<p>
La seule modification possible sur le panier commandé sera d’enlever un ou plusieurs articles.
</p>
{% if cart.type == 'shop' %}
<p>
Merci de vous munir de votre chéquier et/ou CB.
</p>
{% endif %}
<p>
NB: en ce qui concerne le frais, nous mettons dans votre panier les dates les plus proches de la péremption.
</p>
{%if survey_link %}
<p>
Si vous avez des suggestions d'améliorations à faire, vous pouvez compléter <a href="{{survey_link}}">le formulaire de retours d'utilisation de la boutique en ligne</a>.
</p>
{% endif %}
<p>
Commande reçue :
<table border="1">
<tr><th>Articles</th><th>Quantités</th><th>Prix</th><th>Total</th></tr>
{% for p in cart.products %}
<tr><td>{{p.name}}</td><td>{{p.qty}}</td><td>{{p.price}}</td><td>{{p.total}}</td></tr>
{% endfor %}
</table>
<br/>
Total TTC : {{cart.total}} € (le prix qui vous sera demandé pourra être légèrement différent en fonction des disponibilités en magasin)
<br/>
{%if cart.accept_substitution %}
Vous acceptez les substitutions de produits.
{% endif %}
</p>
{%if cart.comment %}
<p>
Vous avez laissé le commentaire suivant: <br/>
{{cart.comment}}
</p>
{% endif %}
{% if cart.type == 'shop' %}
<p>
Vous avez indiqué venir chercher la commande à cette date : "{{cart.best_date}}"
</p>
{% endif %}
Merci de votre compréhension
</p>
L'équipe de {{mag}}
{% extends "base.html" %}
{% load static %}
{% block additionnal_css %}
<link rel="stylesheet" href="{% static 'css/datatables/jquery.dataTables.css' %}">
<link rel="stylesheet" href="{% static 'css/stock_movements.css' %}">
{% endblock %}
{% block additionnal_scripts %}
<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>
<script type="text/javascript" src="{% static 'js/jquery.pos.js' %}"></script>
<script type="text/javascript" src="{% static 'js/stock_movements.js' %}?v="></script>
<script type="text/javascript" src="{% static 'js/notify.min.js' %}?v="></script>
{% endblock %}
{% block content %}
<div class="page_body">
<div id="content_movement_type_selection" class="page_content">
<div class="header txtcenter">
<h1>Saisie de mouvements de stock</h1>
<p><i>Choisissez le type de mouvement de stock que vous voulez enregistrer</i></p>
</div>
<div class="movement_type_buttons txtcenter">
<button type="button" class="btn--primary movement_type_button" id="losses_type_button">
Pertes
<span class="movement_type_button_icons"><i class="fas fa-arrow-right"></i></span>
</button><br>
<button type="button" class="btn--primary movement_type_button" id="autoconso_type_button">
Autoconsommation
<span class="movement_type_button_icons"><i class="fas fa-arrow-right"></i></span>
</button><br>
<button type="button" class="btn--primary movement_type_button" id="meals_type_button">
Repas salariés
<span class="movement_type_button_icons"><i class="fas fa-arrow-right"></i></span>
</button>
</div>
</div>
<div id="content_main" class="page_content" style="display:none;">
<div class="header txtcenter">
<div id="back_to_movement_selection">
<button type="button" class="btn--danger"><i class="fas fa-arrow-left"></i>&nbsp; Retour</button>
</div>
<h1 id="title_movement_type"></h1>
<p style="margin-bottom:0;"><i>Passez un produit sous la douchette ! Si rien ne se passe, cliquez sur le champ ci-dessous puis ré-essayez.</i></p>
<p><i>Vous pouvez également taper le codebarre, puis appuyer sur la touche Entrée.</i></p>
</div>
<div class="barcode_search_area txtcenter">
<input type="text" id="sm_barcode_selector" placeholder="Codebarre">
<div id="icon_product_not_found" class="tooltip" style="display:none;">
<span class="tooltiptext tooltip-lg tt_twolines">Aucun produit trouvé avec ce code-barre.</span>
<span style="color: #d9534f;"> <i class="fas fa-ban"></i>
</div>
</div>
<div class="main">
<table id="products_table" class="display" cellspacing="0" ></table>
</div>
<div class="footer txtcenter" style="display:none">
<h4>Total (H.T.) : <span class="total_value">0</span></h4>
<button type="button" class="btn--primary" id="movement_validation_button" name="button">Validation du total des produits saisis</button>
</div>
<input type="hidden" name="barcode"/>
</div>
<div id="templates" style="display:none;">
<div id="modal_confirm_movement">
<h4 id="confirm_message_movtype"></h4>
<br>
<div class="confirmation_modal_content">
<div class="confirmation_checking_msgs">
<p id="confirmation_checking_price_msg"><i class="fas fa-spinner fa-spin"></i> Vérification des prix...</p>
<p id="confirmation_price_changed" style="display:none;"><i class="fas fa-exclamation-triangle"></i> Des prix ont changé, veuillez recharger la page après cette opération.</p>
</div>
<table id="confirmation_table" class="display" cellspacing="0" width="100%"></table>
</div>
<br>
<h5>Total (H.T.) : <span class="total_value">0</span></h5>
<hr />
<section class="set_member_to_movement">
<p>Avant de valider l'opération, merci de nous dire qui vous êtes :</p>
<form id="sm_search_member_form" action="javascript:;" method="post">
<input type="text" id="sm_search_member_input" value="" placeholder="Votre nom ou numéro..." required>
<button type="submit" class="btn--primary" id="sm_search_member_button">Recherche</button>
</form>
<div class="search_member_results_area" style="display:none;">
<div>
<p>Vous êtes (cliquez sur votre nom) :</p>
</div>
<div class="search_member_results"></div>
</div>
</section>
<hr />
</div>
<div id="modal_confirm_delete_line">
<p>Vous vous apprêtez à suprimer une ligne du tableau, <b>êtes-vous sûr ?</b></p>
</div>
</div>
</div>
<script src="{% static "js/all_common.js" %}?v="></script>
<script src='{% static "js/barcodes.js" %}?v='></script>
{% endblock %}
{% extends "base.html" %}
{% load static %}
{% block additionnal_css %}
<link rel="stylesheet" href="{% static 'css/datatables/jquery.dataTables.css' %}">
<link rel="stylesheet" href="{% static 'css/stock_movements_view.css' %}">
<link rel="stylesheet" href="{% static 'jquery-ui-1.12.1/jquery-ui.min.css' %}">
{% endblock %}
{% block additionnal_scripts %}
<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>
<script type="text/javascript" src="{% static 'js/datatables/datatables.buttons.min.js' %}"></script>
<script type="text/javascript" src="{% static 'js/datatables/buttons.html5.min.js' %}"></script>
<script type="text/javascript" src="{% static 'js/datatables/jszip.min.js' %}"></script>
<script type="text/javascript" src="{% static 'js/datatables/pdfmake.min.js' %}"></script>
<script type="text/javascript" src="{% static 'js/datatables/vfs_fonts.js' %}"></script>
<script type="text/javascript" src="{% static 'js/stock_movements_view.js' %}?v="></script>
<script type="text/javascript" src="{% static 'jquery-ui-1.12.1/jquery-ui.min.js' %}?v="></script>
{% endblock %}
{% block content %}
<div class="page_body">
<div id="content_main" class="page_content">
<div class="header txtcenter">
<h1>Extraction de mouvements de stock</h1>
</div>
<div class="select_movement_area txtcenter">
<select class="select_movement_element select_movement_input" id="movement_type_selector" name="">
<option value="">-- Choisissez un type de mouvement --</option>
<option value="losses">Pertes</option>
<option value="meals">Repas salariés</option>
<option value="autoconso">Autoconsomation</option>
</select>
<p class="select_movement_element">De : <input type="text" id="from" class="select_movement_input"></p>
<p class="select_movement_element">À : <input type="text" id="to" class="select_movement_input"></p>
<button type="button" class="btn--primary" id="movement_selection_button" disabled>C'est parti !</button>
</div>
<div class="main" style="display:none;">
<div class="txtcenter">
<h3>Total de <span id="count_movements"></span> mouvement(s) d'une valeur de <span id="total_value_movements"></span></h3>
</div>
<div class="table_area">
<table id="movements_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>
{% endblock %}
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