Commit 8452c697 by François C.

Merge branch 'dev_principale' into 'dev_cooperatic'

# Conflicts:
#   coops_configurations/config_lacagette.py
parents e7104477 dfda5d22
Pipeline #1023 passed with stage
in 21 seconds
# Scripts
*.sh text eol=lf
\ No newline at end of file
...@@ -16,3 +16,4 @@ db.sqlite3 ...@@ -16,3 +16,4 @@ db.sqlite3
.gitlab-ci.yml .gitlab-ci.yml
shop/shop_admin_settings.json shop/shop_admin_settings.json
shop/errors.log shop/errors.log
.idea
This diff is collapsed. Click to expand it.
...@@ -102,4 +102,8 @@ ENTRANCE_COME_FOR_SHOPING_MSG = "Hey coucou toi ! Cet été nous sommes plus de ...@@ -102,4 +102,8 @@ ENTRANCE_COME_FOR_SHOPING_MSG = "Hey coucou toi ! Cet été nous sommes plus de
UNSUBSCRIBED_MSG = 'Vous êtes désincrit·e, merci de remplir <a href="https://docs.google.com/forms/d/e/1FAIpQLSfPiC2PkSem9x_B5M7LKpoFNLDIz0k0V5I2W3Mra9AnqnQunw/viewform">ce formulaire</a> pour vous réinscrire sur un créneau.<br />Vous pouvez également contacter le Bureau des Membres en remplissant <a href="https://docs.google.com/forms/d/e/1FAIpQLSeZP0m5-EXPVJxEKJk6EjwSyZJtnbiGdYDuAeFI3ENsHAOikg/viewform">ce formulaire</a>' UNSUBSCRIBED_MSG = 'Vous êtes désincrit·e, merci de remplir <a href="https://docs.google.com/forms/d/e/1FAIpQLSfPiC2PkSem9x_B5M7LKpoFNLDIz0k0V5I2W3Mra9AnqnQunw/viewform">ce formulaire</a> pour vous réinscrire sur un créneau.<br />Vous pouvez également contacter le Bureau des Membres en remplissant <a href="https://docs.google.com/forms/d/e/1FAIpQLSeZP0m5-EXPVJxEKJk6EjwSyZJtnbiGdYDuAeFI3ENsHAOikg/viewform">ce formulaire</a>'
CONFIRME_PRESENT_BTN = 'Clique ici pour valider ta présence' CONFIRME_PRESENT_BTN = 'Clique ici pour valider ta présence'
RECEPTION_PB = "Ici, vous pouvez signaler toute anomalie lors d'une réception, les produits non commandés, cassés ou pourris. \
Merci d'indiquer un maximum d'informations, le nom du produit et son code barre. \
Dans le cas de produits déteriorés, merci d'envoyer une photo avec votre téléphone à [Adresse_email]"
{
"views": {
"by_fp": {
"map": "function(doc){emit(doc.fingerprint);}"
},
"by_completed": {
"map": "function(doc){emit(doc.completed);}"
},
"by_odoo_id": {
"map": "function(doc){emit(doc.odoo_id);}"
}
}
}
{
"views": {
"by_type": {
"map": "function(doc){emit(doc.type);}"
}
}
}
#!/usr/bin/env sh
sleep 3
curl -X PUT http://admin:123abc@couchdb:5984/coops
curl -X PUT http://admin:123abc@couchdb:5984/inventory
curl -X PUT http://admin:123abc@couchdb:5984/envelop
curl -X PUT http://admin:123abc@couchdb:5984/shopping_carts
curl -X PUT http://admin:123abc@couchdb:5984/coops/_design/index \
-d @couchdb-coops-init.json
curl -X PUT http://admin:123abc@couchdb:5984/envelop/_design/index \
-d @couchdb-envelop-init.json
do_shelf_inventory received data are beeing stored in this directory, to help for data recovery and analysis
\ No newline at end of file
Emplacements pour stocker les fichiers utiliser par des tâches programmées (par ex)
Les fichiers seront traités lors de l'appel à /reception/po_process_picking
...@@ -27,17 +27,6 @@ services: ...@@ -27,17 +27,6 @@ services:
volumes: volumes:
- "couchdb-data:/opt/couchdb/data" - "couchdb-data:/opt/couchdb/data"
initializer:
image: curlimages/curl
deploy:
restart_policy:
condition: on-failure
depends_on:
- couchdb
command: ["sh", "-c", "cd /tmp && ./couchdb-setup.sh"]
volumes:
- "./:/tmp"
database: database:
image: "postgres:10" image: "postgres:10"
env_file: .env env_file: .env
......
FROM python:3-slim FROM python:3
# Virtual env: ENV PYTHONUNBUFFERED=1
ENV VIRTUAL_ENV=/opt/venv
RUN python3 -m venv $VIRTUAL_ENV # Add french locale
ENV PATH="$VIRTUAL_ENV/bin:$PATH" RUN apt update && \
apt install -y --no-install-recommends locales && \
rm -rf /var/lib/apt/lists/* && \
sed -i '/^#.* fr_FR.UTF-8 /s/^#//' /etc/locale.gen && \
locale-gen
WORKDIR /home/app WORKDIR /home/app
# Install dependencies: RUN pip install --upgrade pip
COPY requirements.txt /home/app/ COPY requirements.txt /home/app/
RUN pip install -r requirements.txt RUN pip install -r requirements.txt
# Setup volume to be able to dev application locally # Setup volume to be able to dev application locally
VOLUME /home/app VOLUME /home/app
COPY . /home/app
# Run the application: # Run the application:
EXPOSE 8080 EXPOSE 8080
......
[couchdb] [couchdb]
single_node=true single_node=true
users_db_security_editable = true
[httpd] [httpd]
enable_cors = true enable_cors = true
[cors] [cors]
credentials = true credentials = true
origins = http://127.0.0.1:8080 origins = *
[replicator] [replicator]
db = _replicator db = _replicator
......
#! /bin/sh #!/usr/bin/env bash
port=34001 port=34001
ip=127.0.0.1 ip=127.0.0.1
if [ ! -z "$1" ]
then if [ -n "$1" ]
then
ip=$1 ip=$1
fi fi
if [ -n "$2" ]
if [ ! -z "$2" ] then
then
port=$2 port=$2
fi fi
current_path=$(pwd) current_path=$(pwd)
export PYTHONPATH="$current_path:$current_path/lib:$PYTHONPATH" export PYTHONPATH="$current_path:$current_path/lib:$PYTHONPATH"
echo yes | django-admin collectstatic --settings=outils.settings export DJANGO_SETTINGS_MODULE=outils.settings
django-admin runserver $ip:$port --settings=outils.settings # Collect static files
echo yes | django-admin collectstatic
# Make sure couchdb databases exist
python manage.py couchdb
# Run server
django-admin runserver "$ip:$port"
Destination directory for logging files (defines in 'handlers' part of LOGGING definition)
https://docs.djangoproject.com/fr/2.2/topics/logging/
from django.core.management.base import BaseCommand, CommandError
from django.conf import settings
import couchdb
class Command(BaseCommand):
help = 'Initialize needed couchDB databases'
def handle(self, *args, **options):
if 'admin' not in settings.COUCHDB:
raise CommandError('''
Veuillez définir les accès d'admin à CouchDB dans votre fichier settings_secret.py
Vérifiez le fichier settings_secret_example.py pour un exemple.''')
url = settings.COUCHDB['admin']['url'] % (settings.COUCHDB['admin']['user'], settings.COUCHDB['admin']['password'])
dbnames = settings.COUCHDB['dbs']
couchserver = couchdb.Server(url)
for dbname in dbnames.values():
if dbname not in couchserver:
self.stdout.write(self.style.WARNING("Missing database %s" % dbname))
db = couchserver.create(dbname)
self.stdout.write(self.style.SUCCESS("✓ created db"))
if dbname == 'coops':
self.createCoopsViews(db)
elif dbname == 'envelop':
self.createEnvelopViews(db)
# db.security
self.createPublicAccess(db)
def createCoopsViews(self, dbConn):
byFpMapFunction = '''function(doc) {
emit(doc.fingerprint);
}'''
byCompletedMapFunction = '''function(doc) {
emit(doc.completed);
}'''
byOdooMapFunction = '''function(doc) {
emit(doc.odoo_id);
}'''
views = {
"by_fp": {
"map": byFpMapFunction
},
"by_completed": {
"map": byCompletedMapFunction
},
"by_odoo_id": {
"map": byOdooMapFunction
},
}
self.createView(dbConn, "index", views)
def createEnvelopViews(self, dbConn):
byTypeMapFunction = '''function(doc) {
emit(doc.type);
}'''
views = {
"by_type": {
"map": byTypeMapFunction
}
}
self.createView(dbConn, "index", views)
def createView(self, dbConn, designDoc, views):
self.stdout.write(self.style.SUCCESS("✓ created view %s" % designDoc))
data = {
"_id": "_design/%s" % designDoc,
"views": views,
"language": "javascript",
"options": {"partitioned": False }
}
dbConn.save(data)
def createPublicAccess(self, dbConn):
self.stdout.write(self.style.SUCCESS("✓ created security rule"))
security_doc = dbConn.resource.get_json("_security")[2]
dbConn.resource.put("_security", {})
...@@ -77,6 +77,9 @@ class OdooAPI: ...@@ -77,6 +77,9 @@ class OdooAPI:
class CouchDB: class CouchDB:
"""Class to handle interactions with CouchDB""" """Class to handle interactions with CouchDB"""
if 'private_url' in settings.COUCHDB:
url = settings.COUCHDB['private_url']
else:
url = settings.COUCHDB['url'] url = settings.COUCHDB['url']
dbs = settings.COUCHDB['dbs'] dbs = settings.COUCHDB['dbs']
db = None db = None
......
...@@ -8,7 +8,13 @@ ODOO = { ...@@ -8,7 +8,13 @@ ODOO = {
} }
COUCHDB = { COUCHDB = {
'private_url': 'http://couchdb:5984',
'url': 'http://127.0.0.1:5984', 'url': 'http://127.0.0.1:5984',
'admin': {
'url': 'http://%s:%s@couchdb:5984',
'user': 'admin',
'password': '123abc',
},
'dbs': { 'dbs': {
'member': 'coops', 'member': 'coops',
'inventory': 'inventory', 'inventory': 'inventory',
......
...@@ -1347,7 +1347,7 @@ function openErrorReport() { ...@@ -1347,7 +1347,7 @@ function openErrorReport() {
var textarea = document.getElementById("error_report"); var textarea = document.getElementById("error_report");
textarea.value = user_comments; textarea.value = (user_comments != undefined) ? user_comments : "";
textarea.focus(); textarea.focus();
textarea.setSelectionRange(textarea.value.length, textarea.value.length); textarea.setSelectionRange(textarea.value.length, textarea.value.length);
} }
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
from outils.common_imports import * from outils.common_imports import *
from outils.for_view_imports import * from outils.for_view_imports import *
from django.views.generic import View from django.views.generic import View
from django.http import HttpResponse
from django.http import JsonResponse
import os import os
from datetime import date from datetime import date
...@@ -14,6 +16,9 @@ from outils.common import OdooAPI ...@@ -14,6 +16,9 @@ from outils.common import OdooAPI
from members.models import CagetteUser from members.models import CagetteUser
from products.models import CagetteProduct from products.models import CagetteProduct
# create temp directory if needed
if not os.path.exists("temp"):
os.mkdir("temp")
def as_text(value): def as_text(value):
""" Utils """ """ Utils """
...@@ -74,10 +79,10 @@ def produits(request, id): ...@@ -74,10 +79,10 @@ def produits(request, id):
"TOOLS_SERVER": settings.TOOLS_SERVER} "TOOLS_SERVER": settings.TOOLS_SERVER}
fixed_barcode_prefix = '0490' fixed_barcode_prefix = '0490'
if hasattr(settings, 'RECEPTION_PB_EMAIL'): if hasattr(settings, 'RECEPTION_PB'):
context['RECEPTION_PB_EMAIL'] = settings.RECEPTION_PB_EMAIL context['RECEPTION_PB'] = settings.RECEPTION_PB
else: else:
context['RECEPTION_PB_EMAIL'] = ' [mail à renseigner dans config.py]' context['RECEPTION_PB'] = ' [texte à renseigner dans config.py]'
if hasattr(settings, 'FIXED_BARCODE_PREFIX'): if hasattr(settings, 'FIXED_BARCODE_PREFIX'):
fixed_barcode_prefix = settings.FIXED_BARCODE_PREFIX fixed_barcode_prefix = settings.FIXED_BARCODE_PREFIX
...@@ -165,6 +170,9 @@ def update_orders(request): ...@@ -165,6 +170,9 @@ def update_orders(request):
import requests import requests
# don't print barcode which begin with these codes
noprint_list = ["0493", "0492", "0499"]
rep = HttpResponse("Not") rep = HttpResponse("Not")
if request.is_ajax(): if request.is_ajax():
if request.method == 'PUT': if request.method == 'PUT':
...@@ -212,8 +220,8 @@ def update_orders(request): ...@@ -212,8 +220,8 @@ def update_orders(request):
except Exception as e: except Exception as e:
errors.append('error registering shortage on p'+order_line['id']+':'+str(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 and barcode is authorized
if (print_labels is True) and (update is True) and (data['update_type'] == 'br_valid') and order_line['new_shelf_price']: if (print_labels is True) and (update is True) and (data['update_type'] == 'br_valid') and order_line['new_shelf_price'] and order_line['barcode'][:4] not in noprint_list:
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']) + '/'
...@@ -235,6 +243,7 @@ def update_orders(request): ...@@ -235,6 +243,7 @@ def update_orders(request):
# Remove order's group # Remove order's group
try: try:
if os.path.exists('temp/grouped_order.json'):
with open('temp/grouped_order.json', 'r') as json_file: with open('temp/grouped_order.json', 'r') as json_file:
saved_groups = json.load(json_file) saved_groups = json.load(json_file)
...@@ -292,6 +301,7 @@ def save_error_report(request): ...@@ -292,6 +301,7 @@ def save_error_report(request):
orders_partner = orders_partner + ', ' orders_partner = orders_partner + ', '
orders_partner = orders_partner + order['partner'] + ' du ' + order['date_order'] orders_partner = orders_partner + order['partner'] + ' du ' + order['date_order']
# If group of orders # If group of orders
if len(data['orders']) > 1 : if len(data['orders']) > 1 :
temp_group_file_name = "temp/" + orders_name + "_rapport-reception_temp.xlsx" temp_group_file_name = "temp/" + orders_name + "_rapport-reception_temp.xlsx"
...@@ -309,6 +319,7 @@ def save_error_report(request): ...@@ -309,6 +319,7 @@ def save_error_report(request):
data['orders'].append(group_order) data['orders'].append(group_order)
# Save qties & comments after step 1 # Save qties & comments after step 1
if data['update_type'] == 'qty_valid': if data['update_type'] == 'qty_valid':
for order in data['orders']: for order in data['orders']:
...@@ -328,8 +339,8 @@ def save_error_report(request): ...@@ -328,8 +339,8 @@ def save_error_report(request):
# If in group add group name # If in group add group name
if len(data['orders']) > 1 : if len(data['orders']) > 1 :
ws.append( ['group', orders_name] ) ws.append( ['group', orders_name] )
try: try:
if 'updated_products' in order:
for product in order['updated_products']: for product in order['updated_products']:
# Don't store products with same qties # Don't store products with same qties
if product['old_qty'] != product['product_qty']: if product['old_qty'] != product['product_qty']:
...@@ -351,10 +362,9 @@ def save_error_report(request): ...@@ -351,10 +362,9 @@ def save_error_report(request):
product['product_qty'], product['product_qty'],
product['price_unit'], product['price_unit'],
supplier_shortage] ) supplier_shortage] )
except: except Exception as exp:
# no updated products, do nothing print("Error while updating products")
pass print(exp)
if ('user_comments' in data) and 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:
...@@ -363,6 +373,8 @@ def save_error_report(request): ...@@ -363,6 +373,8 @@ def save_error_report(request):
# Save file # Save file
wb.save(filename=order['temp_file_name']) wb.save(filename=order['temp_file_name'])
# Create report with data from steps 1 & 2 # Create report with data from steps 1 & 2
else: else:
for order in data['orders']: for order in data['orders']:
...@@ -397,7 +409,6 @@ def save_error_report(request): ...@@ -397,7 +409,6 @@ def save_error_report(request):
os.remove(order['temp_file_name']) os.remove(order['temp_file_name'])
except: except:
data_comment_s1 = "Rapport de la première étape absent !" data_comment_s1 = "Rapport de la première étape absent !"
# Add data from step 2 # Add data from step 2
data_full = [] data_full = []
error_total = 0 error_total = 0
...@@ -409,6 +420,7 @@ def save_error_report(request): ...@@ -409,6 +420,7 @@ def save_error_report(request):
# Concatenate products info from each step # Concatenate products info from each step
try: try:
if "updated_products" in order:
for product in order['updated_products']: for product in order['updated_products']:
if 'supplier_code' in product: if 'supplier_code' in product:
supplier_code = str(product['supplier_code']) supplier_code = str(product['supplier_code'])
...@@ -453,10 +465,10 @@ def save_error_report(request): ...@@ -453,10 +465,10 @@ def save_error_report(request):
error_total_abs += abs(item['error_line']) error_total_abs += abs(item['error_line'])
data_full.append(item) data_full.append(item)
except: except Exception as exp:
# no updated products, do nothing # no updated products, do nothing
pass print("Error while updating products")
print(exp)
# Add remaining products, the ones edited only in step 1 # Add remaining products, the ones edited only in step 1
for product in data_qties.values(): for product in data_qties.values():
item = { item = {
...@@ -486,7 +498,6 @@ def save_error_report(request): ...@@ -486,7 +498,6 @@ def save_error_report(request):
wb = Workbook() wb = Workbook()
ws = wb.active ws = wb.active
ws.title = "Commande " + order['name'] ws.title = "Commande " + order['name']
# Group # Group
if 'group_ids' in order : if 'group_ids' in order :
ws.append( ['Rapport de réception d\'un groupe de commandes'] ) ws.append( ['Rapport de réception d\'un groupe de commandes'] )
...@@ -528,7 +539,6 @@ def save_error_report(request): ...@@ -528,7 +539,6 @@ def save_error_report(request):
round(product['expected_amount'], 2), round(product['expected_amount'], 2),
round(product['error_line'], 2), round(product['error_line'], 2),
product['supplier_shortage']] ) 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)] )
ws.append( ['Montant total en valeur absolue :', '', '', '', '', '', '', '', round(error_total_abs, 2)] ) ws.append( ['Montant total en valeur absolue :', '', '', '', '', '', '', '', round(error_total_abs, 2)] )
...@@ -563,7 +573,6 @@ def save_error_report(request): ...@@ -563,7 +573,6 @@ def save_error_report(request):
top_left_cell.alignment = Alignment(vertical="top") top_left_cell.alignment = Alignment(vertical="top")
top_left_cell = ws['B'+str(merge_begin)] top_left_cell = ws['B'+str(merge_begin)]
top_left_cell.alignment = Alignment(vertical="top") top_left_cell.alignment = Alignment(vertical="top")
# "Auto fit" columns width to content # "Auto fit" columns width to content
for column_cells in ws.columns: for column_cells in ws.columns:
length = max(len(as_text(cell.value)) for cell in column_cells) length = max(len(as_text(cell.value)) for cell in column_cells)
...@@ -572,23 +581,23 @@ def save_error_report(request): ...@@ -572,23 +581,23 @@ def save_error_report(request):
length = 20 length = 20
ws.column_dimensions[column_cells[3].column_letter].width = length ws.column_dimensions[column_cells[3].column_letter].width = length
# Save file # Save file
fileName = "temp/" + order['name'] + "_rapport-reception.xlsx" fileName = "temp/" + order['name'] + "_rapport-reception.xlsx"
try:
wb.save(filename=fileName) wb.save(filename=fileName)
except Exception as exp:
print("Error while saving file %s"%fileName)
print(str(exp))
#Attach file to order #Attach file to order
if 'group_ids' in order : # group report if 'group_ids' in order : # group report
# Attach group report to each order # Attach group report to each order
for group_item_id in order['group_ids'] : for group_item_id in order['group_ids'] :
m = CagetteReception(group_item_id) m = CagetteReception(group_item_id)
m.attach_file(fileName, False) m.attach_file(fileName, False)
os.remove(fileName) os.remove(fileName)
else: else:
m = CagetteReception(order['id']) m = CagetteReception(order['id'])
m.attach_file(fileName) m.attach_file(fileName)
return JsonResponse("ok", safe=False) return JsonResponse("ok", safe=False)
def reception_FAQ(request): def reception_FAQ(request):
......
Utilisé pour stocker de façon temporaire des données par plusieurs modules
...@@ -120,9 +120,7 @@ ...@@ -120,9 +120,7 @@
<hr /> <hr />
<p class="txtleft"> <p class="txtleft">
Ici, vous pouvez signaler toute anomalie lors d'une réception, les produits non commandés, cassés ou pourris. {{RECEPTION_PB}}
Merci d'indiquer un maximum d'informations, le nom du produit et son code barre.
Dans le cas de produits déteriorés, merci d'envoyer une photo avec votre téléphone à <strong>{{RECEPTION_PB_EMAIL}}</strong>
</p> </p>
<br> <br>
<textarea id="error_report"></textarea> <textarea id="error_report"></textarea>
......
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