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
2
Merge Requests
2
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
cooperatic-foodcoops
third-party
Commits
8a14e906
Commit
8a14e906
authored
May 05, 2022
by
François C.
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add change product shelf function
parent
7dfcbf2f
Pipeline
#2188
passed with stage
in 1 minute 32 seconds
Changes
6
Pipelines
1
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
271 additions
and
9 deletions
+271
-9
models.py
shelfs/models.py
+20
-1
shelfs.css
shelfs/static/css/shelfs.css
+12
-0
shelf_inventory.js
shelfs/static/js/shelf_inventory.js
+206
-5
urls.py
shelfs/urls.py
+2
-1
views.py
shelfs/views.py
+15
-2
shelf_inventory.html
templates/shelfs/shelf_inventory.html
+16
-0
No files found.
shelfs/models.py
View file @
8a14e906
...
@@ -505,10 +505,13 @@ class Shelf(models.Model):
...
@@ -505,10 +505,13 @@ class Shelf(models.Model):
class
Shelfs
(
models
.
Model
):
class
Shelfs
(
models
.
Model
):
def
get_all
():
def
get_all
(
precision
=
'full'
):
res
=
[]
res
=
[]
try
:
try
:
api
=
OdooAPI
()
api
=
OdooAPI
()
if
precision
==
'simple'
:
res
=
api
.
search_read
(
'product.shelfs'
,
[],
[
'name'
,
'sort_order'
],
order
=
'sort_order asc'
)
else
:
res
=
api
.
execute
(
'product.shelfs'
,
'get'
,
{})
res
=
api
.
execute
(
'product.shelfs'
,
'get'
,
{})
except
Exception
as
e
:
except
Exception
as
e
:
coop_logger
.
error
(
"Rayons, get_all :
%
s"
,
str
(
e
))
coop_logger
.
error
(
"Rayons, get_all :
%
s"
,
str
(
e
))
...
@@ -529,3 +532,18 @@ class Shelfs(models.Model):
...
@@ -529,3 +532,18 @@ class Shelfs(models.Model):
except
Exception
as
e
:
except
Exception
as
e
:
coop_logger
.
error
(
"Rayons, get_shelfs_sortorder :
%
s"
,
str
(
e
))
coop_logger
.
error
(
"Rayons, get_shelfs_sortorder :
%
s"
,
str
(
e
))
return
res
return
res
@staticmethod
def
make_products_shelf_links
(
data
):
res
=
{}
try
:
api
=
OdooAPI
()
res
[
'done'
]
=
[]
for
elt
in
data
:
f
=
{
'shelf_id'
:
elt
[
'shelf_id'
]}
if
api
.
update
(
'product.product'
,
[
elt
[
'product_id'
]],
f
):
res
[
'done'
]
.
append
(
elt
[
'product_id'
])
except
Exception
as
e
:
res
[
'error'
]
=
str
(
e
)
coop_logger
.
error
(
"Rayons, make_products_shelf_links :
%
s"
,
str
(
e
))
return
res
\ No newline at end of file
shelfs/static/css/shelfs.css
View file @
8a14e906
...
@@ -139,6 +139,13 @@ table.dataTable {
...
@@ -139,6 +139,13 @@ table.dataTable {
padding
:
5px
;
padding
:
5px
;
}
}
#header_container_left
{
float
:
left
;
}
#change_shelf_btn
{
float
:
right
;
}
div
#container_edition
{
div
#container_edition
{
padding
:
8px
;
padding
:
8px
;
background-color
:
#e7e9ed
;
background-color
:
#e7e9ed
;
...
@@ -288,3 +295,8 @@ hr {
...
@@ -288,3 +295,8 @@ hr {
opacity
:
1
;
opacity
:
1
;
}
}
/* Change shelf modal */
.shelf_selection
{
max-width
:
240px
;
}
shelfs/static/js/shelf_inventory.js
View file @
8a14e906
...
@@ -12,7 +12,9 @@ var validation_msg = $('#validation_msg'),
...
@@ -12,7 +12,9 @@ var validation_msg = $('#validation_msg'),
process_all_items_msg
=
$
(
'#process_all_items_msg'
),
process_all_items_msg
=
$
(
'#process_all_items_msg'
),
faq_content
=
$
(
"#FAQ_modal_content"
),
faq_content
=
$
(
"#FAQ_modal_content"
),
issues_reporting
=
$
(
"#issues_reporting"
),
issues_reporting
=
$
(
"#issues_reporting"
),
add_product_form
=
$
(
"#add_product_form"
);
add_product_form
=
$
(
"#add_product_form"
),
change_shelf_form
=
$
(
"#change_shelf_form"
),
change_shelf_btn
=
$
(
'#change_shelf_btn'
);
var
shelf
=
null
,
var
shelf
=
null
,
parent_location
=
'/shelfs'
,
parent_location
=
'/shelfs'
,
...
@@ -28,7 +30,9 @@ var shelf = null,
...
@@ -28,7 +30,9 @@ var shelf = null,
adding_product
=
false
,
// True if modal to add a product is open.
adding_product
=
false
,
// True if modal to add a product is open.
barcodes
=
null
,
// Barcodes stored locally
barcodes
=
null
,
// Barcodes stored locally
// datetime for which shelf's ongoing_inv_start_datetime is considered null
// datetime for which shelf's ongoing_inv_start_datetime is considered null
default_inventory_start_datetime
=
"0001-01-01 00:00:00"
;
default_inventory_start_datetime
=
"0001-01-01 00:00:00"
,
selected_products_for_shelf_change
=
[],
all_shelfs
=
null
;
// Use get_all_shelfs to access it's value
/* UTILS */
/* UTILS */
...
@@ -297,6 +301,25 @@ function validateEdition() {
...
@@ -297,6 +301,25 @@ function validateEdition() {
}
}
}
}
/**
* Unselect all rows from datatable
* Only for table_to_process
*/
function
unselect_all_rows
()
{
$
(
"#select_all_products_cb"
).
prop
(
"checked"
,
false
);
table_to_process
.
rows
().
every
(
function
()
{
const
node
=
$
(
this
.
node
());
node
.
removeClass
(
'selected'
);
node
.
find
(
".select_product_cb"
).
first
()
.
prop
(
"checked"
,
false
);
return
0
;
});
selected_rows
=
[];
}
/*
/*
* Update a product info and add it to processed items.
* Update a product info and add it to processed items.
* If 'value' is set, use it as new value.
* If 'value' is set, use it as new value.
...
@@ -323,6 +346,102 @@ function editProductInfo (productToEdit, value = null) {
...
@@ -323,6 +346,102 @@ function editProductInfo (productToEdit, value = null) {
return
true
;
return
true
;
}
}
/* Change shelf process */
/*
data should be an array of {product_id: xx, shelf_id: yy}
*/
function
record_products_shelf_on_server
(
data
)
{
return
new
Promise
(
resolve
=>
{
$
.
ajax
({
type
:
"POST"
,
url
:
"/shelfs/change_products_shelfs"
,
dataType
:
"json"
,
data
:
JSON
.
stringify
(
data
),
traditional
:
true
,
contentType
:
"application/json; charset=utf-8"
,
success
:
function
(
data
)
{
if
(
typeof
data
.
res
!==
"undefined"
&&
typeof
data
.
res
.
done
!==
"undefined"
)
resolve
(
data
.
res
.
done
);
else
resolve
(
null
);
},
error
:
function
()
{
alert
(
"Impossible de mettre à jour les données"
);
resolve
(
null
);
}
});
});
}
// call on change_shelf_btn click action
async
function
open_change_shelf_modal
()
{
selected_products_for_shelf_change
=
[];
$
(
'.select_product_cb:checked'
).
each
(
function
(
idx
,
elt
){
const
row
=
$
(
elt
).
closest
(
'tr'
);
selected_products_for_shelf_change
.
push
(
table_to_process
.
row
(
row
).
data
())
});
if
(
selected_products_for_shelf_change
.
length
>
0
)
{
/*
As button is not shown if no product is selected, should be always true
But, with CSS changes, it could happen that length == 0
*/
const
shelfs
=
await
get_all_shelfs
();
if
(
shelfs
!==
null
)
{
let
modal_content
=
$
(
'#templates #change_shelf_form'
).
clone
(),
shelf_selector
=
$
(
'<select>'
).
addClass
(
'shelf_selection'
),
table
=
modal_content
.
find
(
'table tbody'
).
empty
();
//construct shelfs selector
shelfs
.
forEach
(
(
shelf
)
=>
{
let
option
=
$
(
'<option>'
)
.
val
(
shelf
.
id
)
.
text
(
shelf
.
name
+
' ('
+
shelf
.
sort_order
+
')'
);
shelf_selector
.
append
(
option
);
});
selected_products_for_shelf_change
.
forEach
(
(
product
)
=>
{
let
tr
=
$
(
'<tr>'
).
attr
(
'data-id'
,
product
.
id
)
.
append
(
$
(
'<td>'
).
text
(
product
.
name
))
.
append
(
$
(
'<td>'
).
append
(
shelf_selector
.
clone
()));
table
.
append
(
tr
);
});
openModal
(
modal_content
.
html
(),
()
=>
{
if
(
is_time_to
(
'change_product_shelf'
,
500
))
{
make_change
=
async
()
=>
{
// Prepare data to be transmitted to server to be recorded
let
data
=
[];
$
(
'.overlay-content table tbody tr'
).
each
(
function
(
idx
,
e
){
data
.
push
({
product_id
:
$
(
e
).
data
(
'id'
),
shelf_id
:
$
(
e
).
find
(
'select'
).
val
()
});
});
const
update_result
=
await
record_products_shelf_on_server
(
data
);
if
(
update_result
!==
null
)
{
update_result
.
forEach
(
(
product_id
)
=>
{
remove_from_toProcess
(
table_to_process
.
row
(
$
(
'tr#'
+
product_id
)));
});
}
}
make_change
();
}
},
'Changer les produits de rayons'
);
}
else
{
alert
(
"Les informations des autres rayons n'ont pas pu être récupérées."
)
}
}
}
/* LISTS HANDLING */
/* LISTS HANDLING */
...
@@ -342,8 +461,24 @@ function initLists() {
...
@@ -342,8 +461,24 @@ function initLists() {
}
}
// Init table for items to process
// Init table for items to process
var
columns_to_process
=
[
{
data
:
"id"
,
title
:
"id"
,
visible
:
false
},
var
columns_to_process
=
[];
if
(
shelf
.
inventory_status
!==
""
)
{
columns_to_process
.
push
({
data
:
"id"
,
title
:
"id"
,
visible
:
false
});
}
else
{
columns_to_process
.
push
({
title
:
`<div id="table_header_select_all" class="txtcenter">
<input type="checkbox" id="select_all_products_cb" name="select_all_products_cb" value="all">
</div>`
,
className
:
"dt-body-center"
,
orderable
:
false
,
render
:
function
(
data
)
{
return
`<input type="checkbox" class="select_product_cb" />`
;
},
width
:
"4%"
});
}
columns_to_process
=
columns_to_process
.
concat
([
{
data
:
"name"
,
title
:
"Produit"
,
width
:
"60%"
},
{
data
:
"name"
,
title
:
"Produit"
,
width
:
"60%"
},
{
data
:
"uom_id.1"
,
title
:
"Unité de mesure"
,
className
:
"dt-body-center"
},
{
data
:
"uom_id.1"
,
title
:
"Unité de mesure"
,
className
:
"dt-body-center"
},
{
{
...
@@ -351,7 +486,7 @@ function initLists() {
...
@@ -351,7 +486,7 @@ function initLists() {
defaultContent
:
"<a class='btn' id='process_item' href='#'><i class='far fa-edit'></i></a>"
,
"className"
:
"dt-body-center"
,
defaultContent
:
"<a class='btn' id='process_item' href='#'><i class='far fa-edit'></i></a>"
,
"className"
:
"dt-body-center"
,
orderable
:
false
orderable
:
false
}
}
];
]
)
;
if
(
originView
==
'custom_list'
)
{
if
(
originView
==
'custom_list'
)
{
columns_to_process
.
splice
(
1
,
0
,
{
data
:
"shelf_sortorder"
,
title
:
"Rayon"
,
className
:
"dt-body-center"
});
columns_to_process
.
splice
(
1
,
0
,
{
data
:
"shelf_sortorder"
,
title
:
"Rayon"
,
className
:
"dt-body-center"
});
...
@@ -451,6 +586,41 @@ function initLists() {
...
@@ -451,6 +586,41 @@ function initLists() {
clearLineEdition
();
clearLineEdition
();
}
}
});
});
// Select row(s) on checkbox change (copied from orders_helper.js -only table_to_process changed-)
$
(
table_to_process
.
table
().
header
()).
on
(
'click'
,
'th #select_all_products_cb'
,
function
()
{
if
(
this
.
checked
)
{
selected_rows
=
[];
table_to_process
.
rows
().
every
(
function
()
{
const
node
=
$
(
this
.
node
());
node
.
addClass
(
'selected'
);
node
.
find
(
".select_product_cb"
).
first
()
.
prop
(
"checked"
,
true
);
// Save selected rows in case the table is updated
selected_rows
.
push
(
this
.
data
().
id
);
return
0
;
});
change_shelf_btn
.
show
();
}
else
{
unselect_all_rows
();
change_shelf_btn
.
hide
();
}
});
$
(
table_to_process
.
table
().
body
()).
on
(
'click'
,
'.select_product_cb'
,
function
()
{
if
(
this
.
checked
)
{
change_shelf_btn
.
show
();
}
else
{
// must hide change_shelf_btn only if no other product is selected
if
(
$
(
'.select_product_cb:checked'
).
length
===
0
)
{
change_shelf_btn
.
hide
();
}
}
});
change_shelf_btn
.
click
(
open_change_shelf_modal
);
}
}
// Add a line to the 'items to process' list
// Add a line to the 'items to process' list
...
@@ -766,6 +936,37 @@ function saveIssuesReport() {
...
@@ -766,6 +936,37 @@ function saveIssuesReport() {
/* INIT */
/* INIT */
// (for shelf change)
function
get_all_shelfs
()
{
return
new
Promise
(
resolve
=>
{
if
(
all_shelfs
!==
null
)
{
resolve
(
all_shelfs
);
}
else
{
$
.
ajax
({
type
:
'GET'
,
url
:
"/shelfs/all/simple"
,
dataType
:
"json"
,
traditional
:
true
,
contentType
:
"application/json; charset=utf-8"
,
success
:
function
(
data
)
{
shelfs
=
null
;
if
(
typeof
data
.
res
!==
"undefined"
&&
data
.
res
.
length
>
0
)
shelfs
=
data
.
res
;
resolve
(
shelfs
);
},
error
:
function
(
data
)
{
err
=
{
msg
:
"erreur serveur lors de la récupération des rayons"
,
ctx
:
'get_all_shelfs'
};
if
(
typeof
data
.
responseJSON
!=
'undefined'
&&
typeof
data
.
responseJSON
.
error
!=
'undefined'
)
{
err
.
msg
+=
' : '
+
data
.
responseJSON
.
error
;
}
report_JS_error
(
err
,
'shelf_inventory'
);
resolve
(
null
);
}
});
}
});
}
// Get shelf data from server if not in local storage
// Get shelf data from server if not in local storage
function
get_shelf_data
()
{
function
get_shelf_data
()
{
var
url
=
(
originView
==
'shelf'
)
?
'../'
+
shelf
.
id
:
'../get_custom_list_data?id='
+
shelf
.
id
;
var
url
=
(
originView
==
'shelf'
)
?
'../'
+
shelf
.
id
:
'../get_custom_list_data?id='
+
shelf
.
id
;
...
...
shelfs/urls.py
View file @
8a14e906
...
@@ -9,8 +9,9 @@ urlpatterns = [
...
@@ -9,8 +9,9 @@ urlpatterns = [
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'^inventory_process_state/([0-9]+)$'
,
views
.
inventory_process_state
),
url
(
r'^all$'
,
views
.
all
),
url
(
r'^all
/?([a-z]*)
$'
,
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'^change_products_shelfs$'
,
views
.
change_products_shelfs
),
url
(
r'^(?P<shelf_id>\d+)$'
,
views
.
shelf_data
),
url
(
r'^(?P<shelf_id>\d+)$'
,
views
.
shelf_data
),
url
(
r'^(?P<shelf_id>\d+)/products$'
,
views
.
products
),
url
(
r'^(?P<shelf_id>\d+)/products$'
,
views
.
products
),
url
(
r'^(?P<shelf_id>\d+)/add_product$'
,
views
.
add_product
),
url
(
r'^(?P<shelf_id>\d+)/add_product$'
,
views
.
add_product
),
...
...
shelfs/views.py
View file @
8a14e906
...
@@ -71,9 +71,9 @@ def set_begin_inventory_datetime(request, shelf_id):
...
@@ -71,9 +71,9 @@ def set_begin_inventory_datetime(request, shelf_id):
return
JsonResponse
({
'res'
:
res
})
return
JsonResponse
({
'res'
:
res
})
def
all
(
request
):
def
all
(
request
,
precision
):
"""Get all shelves data"""
"""Get all shelves data"""
return
JsonResponse
({
'res'
:
Shelfs
.
get_all
()})
return
JsonResponse
({
'res'
:
Shelfs
.
get_all
(
precision
)})
def
get_shelves_extra_data
(
request
):
def
get_shelves_extra_data
(
request
):
"""Get data that need calculation, so long execution time"""
"""Get data that need calculation, so long execution time"""
...
@@ -130,6 +130,19 @@ def inventory_process_state(request, shelf_id):
...
@@ -130,6 +130,19 @@ def inventory_process_state(request, shelf_id):
else
:
else
:
return
JsonResponse
({
'res'
:
res
})
return
JsonResponse
({
'res'
:
res
})
def
change_products_shelfs
(
request
):
res
=
{}
try
:
data
=
json
.
loads
(
request
.
body
.
decode
())
res
=
Shelfs
.
make_products_shelf_links
(
data
)
except
Exception
as
e
:
res
[
'error'
]
=
str
(
e
)
coop_logger
.
error
(
"change_products_shelfs :
%
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"""
"""
"""
...
...
templates/shelfs/shelf_inventory.html
View file @
8a14e906
...
@@ -91,6 +91,9 @@
...
@@ -91,6 +91,9 @@
<div
class=
"container_products"
id=
"container_left"
>
<div
class=
"container_products"
id=
"container_left"
>
<h4
id=
"header_container_left"
>
Produits à compter
</h4>
<h4
id=
"header_container_left"
>
Produits à compter
</h4>
<button
style=
"display:none;"
id=
"change_shelf_btn"
class=
"btn btn--primary"
>
Changer de rayon
</button>
<table
id=
"table_to_process"
class=
"display"
cellspacing=
"0"
></table>
<table
id=
"table_to_process"
class=
"display"
cellspacing=
"0"
></table>
</div>
</div>
<div
class=
"container_products"
id=
"container_right"
>
<div
class=
"container_products"
id=
"container_right"
>
...
@@ -167,6 +170,19 @@
...
@@ -167,6 +170,19 @@
<input
autocomplete=
"off"
type=
"text"
placeholder=
"Code barre du produit"
class=
"add_product_input"
>
<input
autocomplete=
"off"
type=
"text"
placeholder=
"Code barre du produit"
class=
"add_product_input"
>
<hr
/>
<hr
/>
</div>
</div>
<div
id=
"change_shelf_form"
>
<h3>
Changement de rayons
</h3>
<hr
/>
<table>
<thead>
<tr>
<th>
Produit
</th>
<th>
Rayon
</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<script
type=
"text/javascript"
>
<script
type=
"text/javascript"
>
...
...
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