Commit 973b1ecb by Félicie

Merge branch 'dev_cooperatic' of…

Merge branch 'dev_cooperatic' of gl.cooperatic.fr:cooperatic-foodcoops/third-party into 2207-type-of-members
parents 3c23bcc9 d378cd28
Pipeline #1757 failed with stage
in 1 minute 36 seconds
#cash, #ch {
margin-top: 15px;
}
.envelop_section {
margin-bottom: 10px;
}
......@@ -16,6 +20,70 @@
margin-top: 30px;
}
.update_envelop_button, .delete_envelop_button {
margin: 0 0 10px 15px;
}
.envelop_content_list {
margin: 20px 0 15px 0;
}
/* Update envelop modal */
.envelop_lines {
margin: 20px 0;
display: flex;
flex-direction: column;
align-items: center;
}
.update_envelop_line {
margin: 5px 0;
width: 60%;
display: flex;
position: relative;
}
.deleted_line_through {
border-bottom: 1px solid #d9534f;
position: absolute;
content: "";
width: 95%;
height: 50%;
display: none;
}
.update_envelop_line div {
flex: 50% 1 0;
}
.line_partner_name_container {
display: flex;
justify-content: flex-start;
align-items: center;
}
.line_partner_name {
text-align: left;
padding: 0 5px;
}
.line_partner_input_container {
display: flex;
align-items: center;
}
.delete_envelop_line_icon {
margin-left: 10px;
color: #d9534f;
cursor: pointer;
}
.envelop_comments {
width: 60%;
margin-bottom: 20px;
}
/* Accordion style */
/* Style the buttons that are used to open and close the accordion panel */
.accordion {
......@@ -23,7 +91,6 @@
color: #212529;
cursor: pointer;
padding: 18px;
/* width: 80%; */
text-align: left;
border: none;
outline: none;
......@@ -32,7 +99,6 @@
.archive_button {
padding: 18px;
/* width: 20%; */
}
hr {
......
......@@ -368,8 +368,8 @@ class CagetteInventory(models.Model):
return {'missed': missed, 'unchanged': unchanged, 'done': done}
@staticmethod
def update_stock_with_shelf_inventory_data(inventory_data):
"""Updates Odoo stock after a shelf inventory"""
def update_products_stock(inventory_data):
""" Updates Odoo stock after a shelf inventory or another action"""
TWOPLACES = Decimal(10) ** -2
api = OdooAPI()
......
......@@ -94,7 +94,7 @@ def do_custom_list_inventory(request):
full_inventory_data = CagetteInventory.get_full_inventory_data(inventory_data)
# Proceed with inventory
res['inventory'] = CagetteInventory.update_stock_with_shelf_inventory_data(full_inventory_data)
res['inventory'] = CagetteInventory.update_products_stock(full_inventory_data)
# remove file
CagetteInventory.remove_custom_inv_file(inventory_data['id'])
......
......@@ -84,7 +84,7 @@ class CagetteMember(models.Model):
'point_qty': pts
}
"""
try:
return self.o_api.create('shift.counter.event', data)
except Exception as e:
......@@ -152,7 +152,7 @@ class CagetteMember(models.Model):
if (password == d + m + y):
if coop_id is None:
coop_id = coop['id']
data['id'] = coop_id
data['id'] = coop_id
auth_token_seed = fp + coop['create_date']
data['auth_token'] = hashlib.sha256(auth_token_seed.encode('utf-8')).hexdigest()
data['token'] = hashlib.sha256(coop['create_date'].encode('utf-8')).hexdigest()
......@@ -508,7 +508,7 @@ class CagetteMember(models.Model):
stype = shift_template['data']['type']
res['shift'] = \
m.create_coop_shift_subscription(shift_t_id, stype)
m.add_first_point(stype)
# m.add_first_point(stype) # Not needed anymore
# Update couchdb do with new data
try:
......@@ -743,13 +743,14 @@ class CagetteMember(models.Model):
keep_it = False
if not shift_id is None and len(shift_id) > 0:
# Only member registred to shift_id will be returned
cond = [['id', '=', m['tmpl_reg_line_ids'][0]]]
fields = ['shift_template_id']
shift_templ_res = api.search_read('shift.template.registration.line', cond, fields)
if (len(shift_templ_res) > 0
and
int(shift_templ_res[0]['shift_template_id'][0]) == int(shift_id)):
keep_it = True
if len(m['tmpl_reg_line_ids']) > 0:
cond = [['id', '=', m['tmpl_reg_line_ids'][0]]]
fields = ['shift_template_id']
shift_templ_res = api.search_read('shift.template.registration.line', cond, fields)
if (len(shift_templ_res) > 0
and
int(shift_templ_res[0]['shift_template_id'][0]) == int(shift_id)):
keep_it = True
else:
keep_it = True
if keep_it is True:
......@@ -845,7 +846,7 @@ class CagetteMember(models.Model):
def update_member_makeups(self, member_data):
api = OdooAPI()
res = {}
f = { 'makeups_to_do': int(member_data["target_makeups_nb"]) }
res_item = api.update('res.partner', [self.id], f)
res = {
......@@ -1462,4 +1463,3 @@ class CagetteUser(models.Model):
pass
return answer
/* Comments : */
/* - Screens */
/* -- Sections */
/* - Common */
.page_body{
position: relative;
}
......@@ -9,8 +15,6 @@
right: 0;
}
/* - Common */
.pill {
border-radius: 30px;
min-width: 200px;
......@@ -198,7 +202,7 @@
}
#coverage_form > div {
display:inline-block;
display:block;
float:left;
}
......@@ -367,7 +371,7 @@
width: 90%;
}
/* product actions modal*/
/* -- Product actions modal*/
.npa-options {
width: fit-content;
text-align: left;
......@@ -376,6 +380,27 @@
.npa-options label {
display: block;
}
.modal_product_actions_section {
margin: 1em 0;
}
.modal_product_actions_section .tooltip {
margin-left: 5px;
}
.modal_product_actions_title {
font-weight: bold;
font-size: 2.2rem;
margin-bottom: 10px;
}
.checkbox_action_disabled {
cursor: not-allowed;
opacity: .5;
}
/* - Orders created screen */
.order_created_header {
......
......@@ -148,6 +148,10 @@ footer { position: fixed;
width: 230px !important;
}
.tooltip .tooltip-xl {
width: 320px !important;
}
.tooltip .tt_twolines {
top: -15px !important;
}
......
......@@ -460,22 +460,6 @@ for (i = 0; i < acc.length; i++) {
});
}
$(document).on('click', '.accordion', function(){
/* Toggle between adding and removing the "active" class,
to highlight the button that controls the panel */
this.classList.toggle("active");
/* Toggle between hiding and showing the active panel */
var panel = this.nextElementSibling;
if (panel.style.display === "block") {
panel.style.display = "none";
} else {
panel.style.display = "block";
}
console.log(panel)
});
function report_JS_error(e, m) {
try {
$.post('/log_js_error', {module: m, error: JSON.stringify(e)});
......
......@@ -189,34 +189,51 @@ class ExportPOS(View):
kept_sessions_id.append(s['id'])
key = y + '-' + m + '-' + d
if not (key in totals):
totals[key] = {'CB': 0, 'CSH': 0, 'CHQ': 0, 'TOTAL': 0}
totals[key] = {'CB': 0,
'CSH': 0,
'CHQ': 0,
'CB_DEJ': 0,
'CHQ_DEJ': 0,
'TOTAL': 0}
sub_total = 0
cb = chq = csh = 0
cb = chq = csh = cbd = chqd = 0
for p in s['payments']:
# p['name'] is a sequence generated string
# Test order is important as CHEQDEJ contains CHEQ for ex.
# p['journal'] could be used but easier to change in Odoo interface
sub_amount = round(p['total_amount'], 2)
if 'CSH' in p['name']:
csh = round(p['total_amount'], 2)
csh = sub_amount
elif 'CHEQDEJ' in p['name']:
chqd = sub_amount
elif 'CHEQ' in p['name']:
chq = round(p['total_amount'], 2)
chq = sub_amount
elif 'CBDEJ' in p['name']:
cbd = sub_amount
elif 'CB' in p['name']:
cb = round(p['total_amount'], 2)
sub_total += round(p['total_amount'], 2)
cb = sub_amount
sub_total += sub_amount
totals[key]['CB'] += cb
totals[key]['CSH'] += csh
totals[key]['CHQ'] += chq
totals[key]['CB_DEJ'] += cbd
totals[key]['CHQ_DEJ'] += chqd
totals[key]['TOTAL'] += round(sub_total, 2)
details_lines.append([mois, s['mm_dates']['min'], s['mm_dates']['min'], s['caisse'], s['name'],
cb, csh, chq, sub_total])
cb, csh, chq, cbd, chqd, sub_total])
wb = Workbook()
ws1 = wb.create_sheet("Totaux " + mois, 0)
ws2 = wb.create_sheet("Détails " + mois, 1)
ws1.append(['date', 'CB', 'CSH', 'CHQ', 'Total'])
ws1.append(['date', 'CB', 'CSH', 'CHQ', 'CB_DEJ', 'CHQ_DEJ', 'Total'])
for day in sorted(totals):
cb = totals[day]['CB']
csh = totals[day]['CSH']
chq = totals[day]['CHQ']
cbd = totals[day]['CB_DEJ']
chqd = totals[day]['CHQ_DEJ']
total = totals[day]['TOTAL']
ws1.append([day, cb, csh, chq, total])
ws2.append(['mois', 'min_date', 'max_date', 'Caisse', 'session', 'CB', 'CSH','CHQ', 'total'])
ws1.append([day, cb, csh, chq, cbd, chqd, total])
ws2.append(['mois', 'min_date', 'max_date', 'Caisse', 'session', 'CB', 'CSH','CHQ', 'CB_DEJ', 'CHQ_DEJ', 'total'])
for row in details_lines:
ws2.append(row)
wb_name = 'export_sessions__' + mois + '.xlsx'
......
......@@ -254,13 +254,20 @@ class CagetteProduct(models.Model):
return res
@staticmethod
def update_npa_and_minimal_stock(data):
"""Update NPA (ne pas acheter) and minimal stock data"""
def commit_actions_on_product(data):
""" Update:
- NPA (ne pas acheter)
- Product is active
- Minimal stock
"""
res = {}
try:
api = OdooAPI()
# Minimal stock
f = {'minimal_stock': data['minimal_stock']}
# NPA
if 'simple-npa' in data['npa']:
f['purchase_ok'] = 0
if 'npa-in-name' in data['npa']:
......@@ -279,6 +286,10 @@ class CagetteProduct(models.Model):
f['name'] = re.sub(r'( \[FDS\])', '', current_name)
if len(data['npa']) == 0:
f['purchase_ok'] = 1
# Active
f["active"] = not data['to_archive']
res["update"] = api.update('product.template', data['id'], f)
except Exception as e:
res["error"] = str(e)
......@@ -612,7 +623,7 @@ class CagetteProducts(models.Model):
"product_variant_ids",
"minimal_stock"
]
c = [['id', 'in', ptids], ['purchase_ok', '=', True]]
c = [['id', 'in', ptids], ['purchase_ok', '=', True], ['active', '=', True]]
products_t = api.search_read('product.template', c, f)
filtered_products_t = [p for p in products_t if p["state"] != "end" and p["state"] != "obsolete"]
......
......@@ -11,7 +11,7 @@ urlpatterns = [
url(r'^update_product_stock$', views.update_product_stock),
url(r'^update_product_purchase_ok$', views.update_product_purchase_ok),
url(r'^update_product_internal_ref$', views.update_product_internal_ref),
url(r'^update_npa_and_minimal_stock$', views.update_npa_and_minimal_stock),
url(r'^commit_actions_on_product$', views.commit_actions_on_product),
url(r'^labels_appli_csv(\/?[a-z]*)$', views.labels_appli_csv, name='labels_appli_csv'),
url(r'^label_print/([0-9]+)/?([0-9\.]*)/?([a-z]*)/?([0-9]*)$', views.label_print),
url(r'^shelf_labels$', views.shelf_labels), # massive print
......
......@@ -100,7 +100,7 @@ def update_product_stock(request):
'products': [p]
}
res['inventory'] = CagetteInventory.update_stock_with_shelf_inventory_data(inventory_data)
res['inventory'] = CagetteInventory.update_products_stock(inventory_data)
return JsonResponse({"res": res})
......@@ -134,13 +134,44 @@ def update_product_internal_ref(request):
else:
return JsonResponse(res, status=403)
def update_npa_and_minimal_stock(request):
def commit_actions_on_product(request):
res = {}
is_connected_user = CagetteUser.are_credentials_ok(request)
if is_connected_user is True:
try:
data = json.loads(request.body.decode())
res = CagetteProduct.update_npa_and_minimal_stock(data)
product_data = CagetteProducts.get_products_for_order_helper(None, [data["id"]])["products"][0]
# Don't allow to archive product if incomin qty > 0
if data["to_archive"] is True and product_data["incoming_qty"] > 0:
res["code"] = "archiving_with_incoming_qty"
return JsonResponse(res, status=500)
res = CagetteProduct.commit_actions_on_product(data)
# If stock > 0: do inventory to set stock to 0
if data["to_archive"] is True and product_data["qty_available"] != 0:
try:
p = {
'id': product_data['product_variant_ids'][0], # Need product id
'uom_id': product_data['uom_id'],
'qty': -product_data["qty_available"]
}
inventory_data = {
'name': 'Archivage - ' + product_data['name'],
'products': [p]
}
res_inventory = CagetteInventory.update_products_stock(inventory_data)
if res_inventory['errors'] or res_inventory['missed']:
res["code"] = "error_stock_update"
res["error"] = res_inventory['errors']
return JsonResponse(res, status=500)
except Exception as e:
res["code"] = "error_stock_update"
return JsonResponse(res, status=500)
except Exception as e:
res['error'] = str(e)
coop_logger.error("Update npa and minimal stock : %s", res['error'])
......
......@@ -14,49 +14,38 @@ class CagetteSales(models.Model):
def get_sales(self, date_from, date_to):
res = []
# Get pos sessions
cond = [['stop_at', '>=', date_from], ['stop_at', '<=', date_to], ['state', '=', "closed"]]
fields = []
sessions = self.o_api.search_read('pos.session', cond, fields)
# Get pos orders
cond = [['date_order', '>=', date_from], ['date_order', '<=', date_to]]
fields = ['partner_id', 'statement_ids', 'name']
orders = self.o_api.search_read('pos.order', cond, fields)
# Get bank statements of these sessions
statements = []
for s in sessions:
statements = statements + s["statement_ids"]
statements_partners = {}
statements_orders = {}
for o in orders:
statements = statements + o["statement_ids"]
for s in o["statement_ids"]:
statements_partners[s] = o["partner_id"][1]
statements_orders[s] = o["name"]
# Get payment lines
cond = [['statement_id', 'in', statements]]
fields = ["partner_id", "amount", "journal_id", "create_date", "date"]
cond = [['id', 'in', statements]]
fields = ["amount", "journal_id", "create_date"]
payments = self.o_api.search_read('account.bank.statement.line', cond, fields, order="create_date ASC", limit=50000)
item = None
try:
for payment in payments:
# POS session can contain payments from another day (closing session on next morning, ...)
if payment["date"] >= date_from and payment["date"] <= date_to:
# If the consecutive payment in the results is from the same partner on the same day, we consider it's the same basket
if item is not None and item["partner_id"][0] == payment["partner_id"][0] and item["date"] == payment["date"]:
res[len(res)-1]["total_amount"] += round(float(payment["amount"]), 2)
res[len(res)-1]["payments"].append({
"amount": round(float(payment["amount"]), 2),
"journal_id": payment["journal_id"]
})
else:
item = {
"partner_id": payment["partner_id"],
res.append({
"partner": statements_partners[payment["id"]],
"create_date": payment["create_date"],
"date": payment["date"],
"pos_order_name": statements_orders[payment["id"]],
"total_amount": round(float(payment["amount"]), 2),
"payments": [
{
"amount": round(float(payment["amount"]), 2),
"journal_id": payment["journal_id"]
}
}
]
}
res.append(item)
})
except Exception as e:
pass
coop_logger.error("get_sales %s", str(e))
return res
......@@ -38,20 +38,23 @@ function display_orders(orders) {
columns:[
{
data:"create_date",
title:"Date de vente",
title:"Date enregistrement",
width: "10%"
},
{
data:"partner_id",
data:"pos_order_name",
title:"Ref. Caisse",
width: "10%"
},
{
data:"partner",
title:"Membre",
width: "50%",
render: function (data) {
return data[1];
}
width: "40%"
},
{
data:"total_amount",
title: "Montant du panier",
title: "Montant dû",
className:"dt-body-center",
render: function (data) {
return parseFloat(data).toFixed(2) + ' €';
......
......@@ -173,7 +173,7 @@ def do_shelf_inventory(request):
return JsonResponse(res, status=500)
# Proceed with inventory
res['inventory'] = CagetteInventory.update_stock_with_shelf_inventory_data(full_inventory_data)
res['inventory'] = CagetteInventory.update_products_stock(full_inventory_data)
full_inventory_data['inventory_id'] = res['inventory']['inv_id']
shelf_data['last_inventory_id'] = res['inventory']['inv_id']
......
......@@ -46,7 +46,7 @@ def do_movement(request):
'products': products
}
res = CagetteInventory.update_stock_with_shelf_inventory_data(inventory_data)
res = CagetteInventory.update_products_stock(inventory_data)
else:
res = CagetteStock.do_stock_movement(data)
......
......@@ -17,7 +17,7 @@
<div style="width: 10%" class="fr txtright"><i class="fas fa-times"></i></div>
</div>
<div id="envelop_cashing_success" class="alert--success clearfix custom_alert" onClick="toggle_success_alert()">
<div style="width: 90%" class="fl txtleft">Enveloppe encaissée !</div>
<div style="width: 90%" class="fl txtleft success_alert_content">Enveloppe encaissée !</div>
<div style="width: 10%" class="fr txtright"><i class="fas fa-times"></i></div>
</div>
<div id="envelop_deletion_success" class="alert--success clearfix custom_alert" onClick="toggle_deleted_alert()">
......@@ -41,6 +41,33 @@
</section>
</section>
<div id="templates" style="display:none;">
<div id="modal_update_envelop">
<div class="modal_update_envelop_content">
<h3 class="envelop_name"></h3>
<div class="envelop_lines"></div>
<div class="envelop_comments_area">
<p>Commentaires</p>
<textarea class="envelop_comments"></textarea>
</div>
</div>
</div>
<div id="update_envelop_line_template">
<div class="update_envelop_line">
<div class="line_partner_name_container">
<span class="line_number"></span>
<span class="line_partner_name"></span>
</div>
<div class="line_partner_input_container">
<input type="text" class="line_partner_amount" placeholder="Montant">
<i class="fas fa-trash-alt fa-lg delete_envelop_line_icon"></i>
</div>
<div class="deleted_line_through"></div>
</div>
</div>
</div>
<script src="{% static "js/pouchdb.min.js" %}"></script>
<script type="text/javascript">
{%if must_identify %}
......
......@@ -98,7 +98,7 @@
<input type="number" name="percent_adjustement" id="percent_adjust_input" placeholder="ajustement en %">
</div>
<div>
<button type="submit" class='btn--primary'>Calculer les besoins</button> <i class='main fa fa-info-circle fa-lg'></i>
<button type="submit" class='btn--primary'>Calculer les besoins</button> <i class='main fa fa-info-circle fa-lg average_consumption_explanation_icon'></i>
</div>
</form>
</div>
......@@ -277,19 +277,28 @@
<div id="modal_product_actions">
Actions sur <h3><span class="product_name"></span></h3>
<p>
<h4>NPA</h4>
<div class="modal_product_actions_section">
<h4 class="modal_product_actions_title">NPA</h4>
<div class="npa-options">
<label><input type="checkbox" name="npa-actions" value="simple-npa" /> Mettre le produit en NPA </label>
<label><input type="checkbox" name="npa-actions" value="npa-in-name" /> Mettre le produit en NPA et afficher NPA</label>
<label><input type="checkbox" name="npa-actions" value="fds-in-name" /> Mettre le produit en NPA et afficher FDS</label>
</div>
</p>
<p>
<h4>Stock minimum</h4>
</div>
<div class="modal_product_actions_section">
<h4 class="modal_product_actions_title">Archiver le produit</h4>
<label class="checkbox_action_disabled"><input type="checkbox" name="archive-action" value="archive" disabled /> Archiver </label>
<div class="tooltip">
<i class='main fa fa-info-circle'></i>
<span class="tooltiptext tooltip-xl tt_twolines">
Un produit ne peut pas être archivé si une quantité entrante est prévue.
</span>
</div>
</div>
<div class="modal_product_actions_section">
<h4 class="modal_product_actions_title">Stock minimum</h4>
<input type="number" name="minimal_stock" value="" />
</p>
</div>
</div>
<div id="modal_create_order">
......
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