Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
T
third-party
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Alexis AOUN
third-party
Commits
e2b7efce
Project 'cooperatic/kohinos-tav' was moved to 'agplv3/kohinos-tav'. Please update any links and bookmarks that may still have the old path.
Commit
e2b7efce
authored
Jun 30, 2021
by
Damien Moulard
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
AAC: calculate coverage needs
parent
8fd36ac7
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
156 additions
and
52 deletions
+156
-52
oders_helper_style.css
orders/static/css/oders_helper_style.css
+3
-4
orders_helper.js
orders/static/js/orders_helper.js
+141
-45
models.py
products/models.py
+8
-3
helper.html
templates/orders/helper.html
+4
-0
No files found.
orders/static/css/oders_helper_style.css
View file @
e2b7efce
...
...
@@ -81,12 +81,11 @@
#supplier_input
{
width
:
350px
;
border-radius
:
5
px
;
border-radius
:
8
px
;
}
#date_planned_input
{
margin-left
:
40px
;
border-radius
:
5px
;
#date_planned_input
,
#coverage_days_input
{
border-radius
:
8px
;
}
/* -- Table */
...
...
orders/static/js/orders_helper.js
View file @
e2b7efce
var
suppliers_list
=
[],
selected_suppliers
=
[],
products_list
=
[],
products_table
=
null
,
products
=
[],
selected_suppliers
=
[]
,
products_table
=
null
,
selected_rows
=
[],
dbc
=
null
,
product_orders
=
[],
date_format
=
"dd/mm/yy"
,
new_product_supplier_association
=
{
package_qty
:
null
,
price
:
null
};
var
dbc
=
null
,
sync
=
null
,
order_doc
=
{
_id
:
null
,
date_planned
:
null
,
coverage_days
:
null
,
last_update
:
{
timestamp
:
null
,
fingerprint
:
null
...
...
@@ -17,13 +25,7 @@ var suppliers_list = [],
selected_suppliers
:
[],
selected_rows
:
[]
},
fingerprint
=
null
,
date_format
=
"dd/mm/yy"
product_orders
=
[],
new_product_supplier_association
=
{
package_qty
:
null
,
price
:
null
};
fingerprint
=
null
;
/* - UTILS */
...
...
@@ -39,6 +41,7 @@ function reset_data() {
order_doc
=
{
_id
:
null
,
date_planned
:
null
,
coverage_days
:
null
,
last_update
:
{
timestamp
:
null
,
fingerprint
:
null
...
...
@@ -137,6 +140,36 @@ function add_product() {
return
0
;
}
/**
* Compute the qty to buy for each product, depending the coverage days.
* Set the computed qty for the first supplier only.
*/
function
compute_products_coverage_qties
()
{
for
(
const
[
key
,
product
]
of
Object
.
entries
(
products
))
{
let
purchase_qty_for_coverage
=
null
;
// Durée couverture produit = (stock + qté entrante + qté commandée ) / conso quotidienne
const
stock
=
product
.
qty_available
;
const
incoming_qty
=
product
.
incoming_qty
;
const
daily_conso
=
product
.
daily_conso
;
purchase_qty_for_coverage
=
order_doc
.
coverage_days
*
daily_conso
-
stock
-
incoming_qty
;
purchase_qty_for_coverage
=
(
purchase_qty_for_coverage
<
0
)
?
0
:
purchase_qty_for_coverage
;
// Reduce to nb of packages to purchase
purchase_package_qty_for_coverage
=
purchase_qty_for_coverage
/
product
.
suppliersinfo
[
0
].
package_qty
;
// Round according to uom
if
(
product
.
uom_id
[
0
]
==
1
||
product
.
uom_id
[
0
]
==
20
)
{
purchase_package_qty_for_coverage
=
parseFloat
(
purchase_package_qty_for_coverage
).
toFixed
(
0
);
}
else
{
purchase_package_qty_for_coverage
=
parseFloat
(
purchase_package_qty_for_coverage
).
toFixed
(
2
);
}
// Set qty to purchase for first supplier only
products
[
key
].
suppliersinfo
[
0
].
qty
=
purchase_package_qty_for_coverage
;
}
}
/* - SUPPLIERS */
...
...
@@ -737,6 +770,8 @@ function goto_main_screen(doc) {
products
=
order_doc
.
products
;
selected_suppliers
=
order_doc
.
selected_suppliers
;
// TODO update products moving data (stock, qté entrante, conso moy/jour)
update_cdb_order
();
update_main_screen
();
switch_screen
();
...
...
@@ -794,6 +829,58 @@ function display_suppliers() {
});
}
function
_compute_product_data
(
product
)
{
let
item
=
{};
/* Supplier related data */
let
purchase_qty
=
0
;
// Calculate product's total purchase qty
let
p_package_qties
=
[];
// Look for differences in package qties
for
(
let
p_supplierinfo
of
product
.
suppliersinfo
)
{
// Preset qty for input if product related to supplier: existing qty or null (null -> qty to be set, display an empty input)
let
supplier_qty
=
(
"qty"
in
p_supplierinfo
)
?
p_supplierinfo
.
qty
:
null
;
item
[
supplier_column_name
(
p_supplierinfo
)]
=
supplier_qty
;
// Update product's total qty to buy if qty set for this supplier
if
(
supplier_qty
!==
null
)
{
purchase_qty
+=
+
parseFloat
(
supplier_qty
*
p_supplierinfo
.
package_qty
).
toFixed
(
2
);
}
// Store temporarily product package qties
p_package_qties
.
push
(
p_supplierinfo
.
package_qty
);
}
item
.
purchase_qty
=
purchase_qty
;
// If product not related to supplier, set qty for this supplier to false (false -> don't display any input)
for
(
supplier
of
selected_suppliers
)
{
if
(
!
is_product_related_to_supplier
(
product
,
supplier
))
{
item
[
supplier_column_name
(
supplier
)]
=
false
;
}
}
if
(
p_package_qties
.
every
(
(
val
,
i
,
arr
)
=>
val
===
arr
[
0
]
))
{
// If all package qties are equals, display it
item
.
package_qty
=
p_package_qties
[
0
];
}
else
{
// Else display an X
item
.
package_qty
=
'X'
;
}
/* Coverage related data */
if
(
order_doc
.
coverage_days
!==
null
)
{
let
unmet_needs
=
product
.
daily_conso
*
order_doc
.
coverage_days
-
product
.
qty_available
-
product
.
incoming_qty
-
purchase_qty
;
unmet_needs
=
-
Math
.
round
(
unmet_needs
);
unmet_needs
=
(
unmet_needs
>
0
)
?
0
:
unmet_needs
;
item
.
unmet_needs
=
unmet_needs
;
}
else
{
item
.
unmet_needs
=
'X'
;
}
return
item
}
/**
* @param {array} product_ids if set, return formatted data for these products only
* @returns Array of formatted data for datatable data setup
...
...
@@ -809,49 +896,22 @@ function prepare_datatable_data(product_ids = []) {
}
for
(
product
of
products_to_format
)
{
le
t
item
=
{
cons
t
item
=
{
id
:
product
.
id
,
name
:
product
.
name
,
default_code
:
product
.
default_code
,
incoming_qty
:
+
parseFloat
(
product
.
incoming_qty
).
toFixed
(
3
),
// '+' removes unecessary zeroes at the end
qty_available
:
+
parseFloat
(
product
.
qty_available
).
toFixed
(
3
),
daily_conso
:
product
.
daily_conso
,
purchase_ok
:
product
.
purchase_ok
,
uom
:
product
.
uom_id
[
1
]
};
let
qty_to_buy
=
0
;
// Calculate product's total qty to buy
let
p_package_qties
=
[];
// Look for differences in package qties
for
(
product_supplierinfo
of
product
.
suppliersinfo
)
{
// Set qty for input if product related to supplier: qty or null (qty to be set)
let
supplier_qty
=
(
"qty"
in
product_supplierinfo
)
?
product_supplierinfo
.
qty
:
null
;
item
[
supplier_column_name
(
product_supplierinfo
)]
=
supplier_qty
;
const
computed_data
=
_compute_product_data
(
product
);
// Update product's total qty to buy if qty set for this supplier
if
(
supplier_qty
!==
null
)
{
qty_to_buy
+=
supplier_qty
*
product_supplierinfo
.
package_qty
;
}
const
full_item
=
{
...
item
,
...
computed_data
};
p_package_qties
.
push
(
product_supplierinfo
.
package_qty
)
}
item
.
qty_to_buy
=
qty_to_buy
;
// If product not related to supplier: false;
for
(
supplier
of
selected_suppliers
)
{
if
(
!
is_product_related_to_supplier
(
product
,
supplier
))
{
item
[
supplier_column_name
(
supplier
)]
=
false
;
}
}
if
(
p_package_qties
.
every
(
(
val
,
i
,
arr
)
=>
val
===
arr
[
0
]
))
{
// If all package qties are all equals, ok
item
.
package_qty
=
p_package_qties
[
0
];
}
else
{
// Else display an X
item
.
package_qty
=
'X'
}
data
.
push
(
item
);
data
.
push
(
full_item
);
}
return
data
;
...
...
@@ -909,6 +969,12 @@ function prepare_datatable_columns() {
title
:
"Quantité entrante"
,
className
:
"dt-body-center"
,
width
:
"4%"
},
{
data
:
"daily_conso"
,
title
:
"Conso moy /jour"
,
className
:
"dt-body-center"
,
width
:
"6%"
}
];
...
...
@@ -965,12 +1031,19 @@ function prepare_datatable_columns() {
});
columns
.
push
({
data
:
"
qty_to_bu
y"
,
data
:
"
purchase_qt
y"
,
title
:
"Qté Achat"
,
className
:
"dt-body-center"
,
width
:
"4%"
});
columns
.
push
({
data
:
"unmet_needs"
,
title
:
"Besoin non couvert"
,
className
:
"dt-body-center"
,
width
:
"4%"
});
return
columns
;
}
...
...
@@ -1031,10 +1104,11 @@ function display_products(params) {
// On inputs change
$
(
'#products_table'
).
on
(
'change'
,
'tbody td .product_qty_input'
,
function
()
{
let
val
=
parseFloat
(
$
(
this
).
val
());
let
val
=
(
$
(
this
).
val
()
==
''
)
?
0
:
$
(
this
).
val
();
val
=
parseFloat
(
val
);
// If value is a number
if
(
!
isNaN
(
val
)
&&
val
>=
0
)
{
if
(
!
isNaN
(
val
))
{
const
id_split
=
$
(
this
).
attr
(
'id'
)
.
split
(
'_'
);
const
prod_id
=
id_split
[
1
];
...
...
@@ -1229,6 +1303,12 @@ function update_main_screen(params) {
}
else
{
$
(
"#date_planned_input"
).
val
(
''
);
}
if
(
order_doc
.
coverage_days
!==
null
)
{
$
(
"#coverage_days_input"
).
val
(
order_doc
.
coverage_days
);
}
else
{
$
(
"#coverage_days_input"
).
val
(
''
);
}
}
/**
...
...
@@ -1354,6 +1434,22 @@ $(document).ready(function() {
});
// Main screen
$
(
"#coverage_form"
).
on
(
"submit"
,
function
(
e
)
{
e
.
preventDefault
();
let
val
=
$
(
"#coverage_days_input"
).
val
();
val
=
parseInt
(
val
);
if
(
!
isNaN
(
val
))
{
order_doc
.
coverage_days
=
val
;
compute_products_coverage_qties
();
update_cdb_order
();
update_main_screen
();
}
else
{
$
(
"#coverage_days_input"
).
val
(
order_doc
.
coverage_days
);
alert
(
`Valeur non valide pour le nombre de jours de couverture !`
)
}
});
$
(
"#supplier_form"
).
on
(
"submit"
,
function
(
e
)
{
e
.
preventDefault
();
add_supplier
();
...
...
products/models.py
View file @
e2b7efce
...
...
@@ -505,8 +505,9 @@ class CagetteProducts(models.Model):
c
=
[[
'id'
,
'in'
,
ptids
],
[
'purchase_ok'
,
'='
,
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"
]
# sales = CagetteProducts.get_template_products_sales_average({'ids': ptids, 'from': '2020-06-10', 'to': '2020-08-10'})
sales
=
CagetteProducts
.
get_template_products_sales_average
({
'ids'
:
ptids
})
sales
=
CagetteProducts
.
get_template_products_sales_average
({
'ids'
:
ptids
,
'from'
:
'2019-06-10'
,
'to'
:
'2020-08-10'
})
# sales = CagetteProducts.get_template_products_sales_average({'ids': ptids})
if
'list'
in
sales
and
len
(
sales
[
'list'
])
>
0
:
sales
=
sales
[
'list'
]
else
:
...
...
@@ -520,9 +521,13 @@ class CagetteProducts(models.Model):
'package_qty'
:
psi_item
[
"package_qty"
],
'price'
:
psi_item
[
"price"
]
}]
for
s
in
sales
:
if
s
[
"id"
]
==
fp
[
"id"
]:
filtered_products_t
[
i
][
'per_day_qty'
]
=
s
[
"average_qty"
]
filtered_products_t
[
i
][
'daily_conso'
]
=
s
[
"average_qty"
]
if
not
'daily_conso'
in
filtered_products_t
[
i
]:
filtered_products_t
[
i
][
'daily_conso'
]
=
0
res
[
"products"
]
=
filtered_products_t
except
Exception
as
e
:
...
...
templates/orders/helper.html
View file @
e2b7efce
...
...
@@ -45,6 +45,10 @@
</div>
<div
class=
"txtcenter"
id=
"order_data_container"
>
<form
action=
"javascript:;"
id=
"coverage_form"
>
<input
type=
"number"
name=
"coverage_days"
id=
"coverage_days_input"
placeholder=
"Nb jours de couverture"
min=
"1"
>
<button
type=
"submit"
class=
'btn--primary'
>
Calculer les besoins
</button>
</form>
<form
action=
"javascript:;"
id=
"supplier_form"
>
<input
type=
"text"
name=
"supplier"
id=
"supplier_input"
placeholder=
"Rechercher un fournisseur par son nom"
>
<button
type=
"submit"
class=
'btn--primary'
>
Ajouter le fournisseur
</button>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment