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
a15908ef
Commit
a15908ef
authored
Jun 29, 2021
by
Damien Moulard
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
create product orders in odoo
parent
6a661024
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
594 additions
and
78 deletions
+594
-78
models.py
orders/models.py
+75
-0
oders_helper_style.css
orders/static/css/oders_helper_style.css
+73
-5
orders_helper.js
orders/static/js/orders_helper.js
+316
-57
urls.py
orders/urls.py
+2
-0
views.py
orders/views.py
+45
-2
models.py
products/models.py
+19
-10
helper.html
templates/orders/helper.html
+64
-4
No files found.
orders/models.py
View file @
a15908ef
...
...
@@ -222,6 +222,81 @@ class Order(models.Model):
labels_data
[
'total'
]
+=
l
[
'product_qty'
]
return
labels_data
def
get_order_attachment_id
(
self
):
res
=
{}
f
=
[
"id"
]
c
=
[[
'res_model'
,
'='
,
'purchase.order'
],
[
'res_id'
,
'='
,
self
.
id
],
[
'type'
,
'in'
,
[
'binary'
,
'url'
]]]
try
:
attachment
=
self
.
o_api
.
search_read
(
'ir.attachment'
,
c
,
f
)
res
=
attachment
[
0
]
except
Exception
as
e
:
res
[
"id_po"
]
=
self
.
id
res
[
"error"
]
=
str
(
e
)
return
res
@staticmethod
def
create
(
supplier_id
,
date_planned
,
order_lines
):
order_data
=
{
"partner_id"
:
int
(
supplier_id
),
"partner_ref"
:
False
,
"currency_id"
:
1
,
"date_order"
:
datetime
.
datetime
.
now
()
.
strftime
(
"
%
Y-
%
m-
%
d
%
H:
%
M:
%
S"
),
"origin"
:
"Aide à la commande"
,
"company_id"
:
1
,
"order_line"
:
[],
"notes"
:
False
,
"date_planned"
:
date_planned
,
"picking_type_id"
:
1
,
"dest_address_id"
:
False
,
"incoterm_id"
:
False
,
"payment_term_id"
:
False
,
"fiscal_position_id"
:
False
,
"message_follower_ids"
:
False
,
"message_ids"
:
False
}
for
line
in
order_lines
:
order_data
[
"order_line"
]
.
append
(
[
0
,
False
,
{
"package_qty"
:
line
[
"package_qty"
],
"price_policy"
:
"uom"
,
"indicative_package"
:
True
,
"product_id"
:
line
[
"product_variant_ids"
][
0
],
"name"
:
line
[
"name"
],
"date_planned"
:
date_planned
,
"account_analytic_id"
:
False
,
"product_qty_package"
:
line
[
"product_qty_package"
],
"product_qty"
:
line
[
"product_qty"
],
"product_uom"
:
line
[
"product_uom"
],
"price_unit"
:
line
[
"price_unit"
],
"discount"
:
0
,
"taxes_id"
:
[
[
6
,
False
,
line
[
"supplier_taxes_id"
]
]
]
}
]
)
api
=
OdooAPI
()
id_po
=
api
.
create
(
'purchase.order'
,
order_data
)
res_confirm
=
api
.
execute
(
'purchase.order'
,
'button_confirm'
,
[
id_po
])
res
=
{
'id_po'
:
id_po
,
'confirm_po'
:
True
}
return
res
class
Orders
(
models
.
Model
):
@staticmethod
...
...
orders/static/css/oders_helper_style.css
View file @
a15908ef
...
...
@@ -72,14 +72,20 @@
right
:
0
;
}
/* --
Supplier form
*/
#
supplier_form
_container
{
/* --
Order data
*/
#
order_data
_container
{
margin-top
:
30px
;
display
:
flex
;
justify-content
:
space-evenly
;
}
#supplier_input
{
width
:
500px
;
margin-right
:
10px
;
width
:
350px
;
border-radius
:
5px
;
}
#date_planned_input
{
margin-left
:
40px
;
border-radius
:
5px
;
}
...
...
@@ -89,7 +95,9 @@
}
#products_table_filter
input
{
height
:
40px
;
height
:
35px
;
width
:
300px
;
border-radius
:
10px
;
}
#table_header_select_all
{
...
...
@@ -125,6 +133,14 @@
cursor
:
pointer
;
}
/* -- Bottom action button */
#orders_creation_area
{
display
:
flex
;
justify-content
:
space-between
;
margin
:
15px
0
35px
0
;
}
/* -- Suppliers list */
#suppliers_container
{
display
:
flex
;
...
...
@@ -142,4 +158,55 @@
color
:
red
;
margin-left
:
5px
;
cursor
:
pointer
;
}
/* - Orders created screen */
.order_created_header
{
margin-top
:
15px
;
margin-bottom
:
40px
;
}
#created_orders_area
{
display
:
flex
;
flex-wrap
:
wrap
;
justify-content
:
space-evenly
;
}
.new_order_item
{
display
:
flex
;
flex-direction
:
column
;
align-items
:
center
;
margin-top
:
20px
;
}
.download_order_file
{
margin-top
:
10px
;
}
.download_order_file_button
:hover
{
text-decoration
:
none
;
color
:
white
;
}
#recap_delivery_date
{
font-weight
:
bold
;
}
.mail_example_container
{
display
:
flex
;
flex-direction
:
column
;
align-items
:
center
;
width
:
30%
;
margin
:
0
auto
;
}
.mail_type_text
{
width
:
100%
;
}
.mail_example
{
background-color
:
#e7e9ed
;
width
:
100%
;
padding
:
15px
;
}
\ No newline at end of file
orders/static/js/orders_helper.js
View file @
a15908ef
...
...
@@ -7,7 +7,8 @@ var suppliers_list = [],
sync
=
null
,
order_doc
=
{
_id
:
null
,
last_update
:
{
date_planned
:
null
,
last_update
:
{
timestamp
:
null
,
fingerprint
:
null
},
...
...
@@ -15,7 +16,9 @@ var suppliers_list = [],
selected_suppliers
:
[],
selected_rows
:
[]
},
fingerprint
=
null
;
fingerprint
=
null
,
date_format
=
"dd/mm/yy"
product_orders
=
[];
/* - UTILS */
...
...
@@ -29,6 +32,7 @@ function reset_data() {
selected_rows
=
[],
order_doc
=
{
_id
:
null
,
date_planned
:
null
,
last_update
:
{
timestamp
:
null
,
fingerprint
:
null
...
...
@@ -96,7 +100,6 @@ function add_supplier() {
selected_suppliers
.
push
(
supplier
);
let
url
=
"/orders/get_supplier_products"
;
url
+=
"?sid="
+
encodeURIComponent
(
supplier
.
id
);
// Fetch supplier products
...
...
@@ -110,7 +113,7 @@ function add_supplier() {
save_supplier_products
(
supplier
,
data
.
res
.
products
);
update_main_screen
();
$
(
"#supplier_input"
).
val
(
""
);
update_order
();
update_
cdb_
order
();
closeModal
();
},
error
:
function
(
data
)
{
...
...
@@ -139,14 +142,14 @@ function remove_supplier(supplier_id) {
// Remove the supplier from the products suppliers list
for
(
const
i
in
products
)
{
products
[
i
].
suppliers
=
products
[
i
].
suppliers
.
filter
(
supplier
=>
supplier
.
id
!=
supplier_id
);
products
[
i
].
suppliers
info
=
products
[
i
].
suppliersinfo
.
filter
(
supplier
=>
supplier
.
supplier_
id
!=
supplier_id
);
}
// Remove products only associated to this product
products
=
products
.
filter
(
product
=>
product
.
suppliers
.
length
>
0
);
products
=
products
.
filter
(
product
=>
product
.
suppliers
info
.
length
>
0
);
update_main_screen
();
update_order
();
update_
cdb_
order
();
}
...
...
@@ -165,7 +168,7 @@ function save_supplier_product_association(product, supplier, cell) {
supplier_id
:
supplier
.
id
};
//
Fetch supplier products
//
Send request to create association
$
.
ajax
({
type
:
"POST"
,
url
:
"/orders/associate_supplier_to_product"
,
...
...
@@ -175,6 +178,7 @@ function save_supplier_product_association(product, supplier, cell) {
data
:
JSON
.
stringify
(
data
),
success
:
()
=>
{
// Save relation locally
// TODO: save supplierinfo in product
save_supplier_products
(
supplier
,
[
product
]);
// Update table
...
...
@@ -216,10 +220,10 @@ function save_supplier_products(supplier, new_products) {
let
index
=
products
.
findIndex
(
p
=>
p
.
id
===
np
.
id
);
if
(
index
===
-
1
)
{
np
.
suppliers
=
[{
...
supplier
}];
products
.
push
(
np
);
}
else
{
products
[
index
].
suppliers
.
push
({
...
supplier
});
np_supplierinfo
=
np
.
suppliersinfo
[
0
];
products
[
index
].
suppliersinfo
.
push
(
np_supplierinfo
);
}
}
}
...
...
@@ -234,9 +238,9 @@ function save_supplier_products(supplier, new_products) {
function
save_product_supplier_qty
(
prod_id
,
supplier_id
,
val
)
{
for
(
const
i
in
products
)
{
if
(
products
[
i
].
id
==
prod_id
)
{
for
(
const
j
in
products
[
i
].
suppliers
)
{
if
(
products
[
i
].
suppliers
[
j
].
id
==
supplier_id
)
{
products
[
i
].
suppliers
[
j
].
qty
=
val
;
for
(
const
j
in
products
[
i
].
suppliers
info
)
{
if
(
products
[
i
].
suppliers
info
[
j
].
supplier_
id
==
supplier_id
)
{
products
[
i
].
suppliers
info
[
j
].
qty
=
val
;
break
;
}
}
...
...
@@ -252,7 +256,7 @@ function save_product_supplier_qty(prod_id, supplier_id, val) {
* @returns boolean
*/
function
is_product_related_to_supplier
(
product
,
supplier
)
{
return
product
.
suppliers
.
find
(
s
=>
s
.
id
===
supplier
.
id
)
!==
undefined
;
return
product
.
suppliers
info
.
find
(
s
=>
s
.
supplier_
id
===
supplier
.
id
)
!==
undefined
;
}
/* - PRODUCT */
...
...
@@ -282,7 +286,7 @@ function set_product_npa(p_id, npa) {
success
:
()
=>
{
const
index
=
products
.
findIndex
(
p
=>
p
.
id
==
p_id
);
products
[
index
].
purchase_ok
=
data
[
"purchase_ok"
];
update_order
();
update_
cdb_
order
();
closeModal
();
},
...
...
@@ -325,9 +329,9 @@ function generate_inventory() {
const
product
=
products
.
find
(
p
=>
p
.
id
==
selected_data
[
i
].
id
);
data
.
lines
.
push
(
product
.
id
);
for
(
const
supplier
of
product
.
suppliers
)
{
if
(
data
.
partners_id
.
indexOf
(
supplier
.
id
)
===
-
1
)
{
data
.
partners_id
.
push
(
supplier
.
id
);
for
(
const
supplier
info
of
product
.
suppliersinfo
)
{
if
(
data
.
partners_id
.
indexOf
(
supplier
info
.
supplier_
id
)
===
-
1
)
{
data
.
partners_id
.
push
(
supplier
info
.
supplier_
id
);
}
}
}
...
...
@@ -351,10 +355,10 @@ function generate_inventory() {
// Give time for modal to fade
setTimeout
(
function
()
{
$
.
notify
(
$
(
'#do_inventory'
)
.
notify
(
"Inventaire créé !"
,
{
globalPosition
:
"
top left
"
,
globalPosition
:
"
bottom center
"
,
className
:
"success"
}
);
...
...
@@ -439,13 +443,18 @@ function order_pill_on_click() {
/**
* Create an order in couchdb if the name doesn't exist
*/
function
create_order
()
{
function
create_
cdb_
order
()
{
const
order_name
=
$
(
"#new_order_name"
).
val
();
order_doc
.
_id
=
order_name
;
order_doc
.
last_update
=
{
timestamp
:
Date
.
now
(),
fingerprint
:
fingerprint
};
dbc
.
put
(
order_doc
,
function
callback
(
err
,
result
)
{
if
(
!
err
)
{
order_doc
.
_rev
=
result
.
rev
;
update_main_screen
();
switch_screen
();
}
else
{
if
(
err
.
status
==
409
)
{
...
...
@@ -459,7 +468,7 @@ function create_order() {
/**
* Update order data of an existing order in couchdb
*/
function
update_order
()
{
function
update_
cdb_
order
()
{
order_doc
.
products
=
products
;
order_doc
.
selected_suppliers
=
selected_suppliers
;
...
...
@@ -479,6 +488,146 @@ function update_order() {
});
}
/**
* Create the Product Orders in Odoo
*/
function
create_orders
()
{
openModal
();
let
orders_data
=
{
"date_planned"
:
order_doc
.
date_planned
,
"suppliers_data"
:
{}
};
// Prepare data: get products where a qty is set
for
(
let
p
of
products
)
{
for
(
let
p_supplierinfo
of
p
.
suppliersinfo
)
{
// If a qty is set for a supplier for a product
if
(
'qty'
in
p_supplierinfo
)
{
const
supplier_id
=
p_supplierinfo
.
supplier_id
;
// Create entry for this supplier in data object if doesn't exist
if
(
orders_data
.
suppliers_data
[
supplier_id
]
===
undefined
)
{
orders_data
.
suppliers_data
[
supplier_id
]
=
[];
}
orders_data
.
suppliers_data
[
supplier_id
].
push
({
'package_qty'
:
p_supplierinfo
.
package_qty
,
'product_id'
:
p
.
id
,
'name'
:
p
.
name
,
'product_qty_package'
:
p_supplierinfo
.
qty
,
'product_qty'
:
p_supplierinfo
.
qty
*
p_supplierinfo
.
package_qty
,
'product_uom'
:
p
.
uom_id
[
0
],
'price_unit'
:
p_supplierinfo
.
price
,
'supplier_taxes_id'
:
p
.
supplier_taxes_id
,
'product_variant_ids'
:
p
.
product_variant_ids
})
}
}
}
if
(
Object
.
keys
(
orders_data
.
suppliers_data
).
length
===
0
)
{
closeModal
();
alert
(
"Commande non créée : vous n'avez rentré aucune quantité !"
);
return
-
1
;
}
$
.
ajax
({
type
:
"POST"
,
url
:
"/orders/create_orders"
,
dataType
:
"json"
,
traditional
:
true
,
contentType
:
"application/json; charset=utf-8"
,
data
:
JSON
.
stringify
(
orders_data
),
success
:
(
result
)
=>
{
$
(
'#recap_delivery_date'
).
text
(
$
(
'#date_planned_input'
).
val
());
// Display new orders
for
(
let
new_order
of
result
.
res
.
created
)
{
const
supplier_name
=
suppliers_list
.
find
(
s
=>
s
.
id
==
new_order
.
supplier_id
).
display_name
;
product_orders
.
push
({
'id'
:
new_order
.
id_po
,
'supplier_id'
:
new_order
.
supplier_id
,
'supplier_name'
:
supplier_name
})
let
new_order_template
=
$
(
"#templates #new_order_item_template"
);
new_order_template
.
find
(
".new_order_supplier_name"
).
text
(
supplier_name
);
new_order_template
.
find
(
".new_order_po"
).
text
(
`PO
${
new_order
.
id_po
}
`
);
new_order_template
.
find
(
".download_order_file_button"
).
attr
(
'id'
,
`download_attachment_
${
new_order
.
id_po
}
`
);
$
(
'#created_orders_area'
).
append
(
new_order_template
.
html
());
}
// Prepare buttons to download order attachment
get_order_attachments
();
// Clear data
order_doc
.
_deleted
=
true
;
update_cdb_order
();
reset_data
();
update_order_selection_screen
();
switch_screen
(
'orders_created'
);
closeModal
();
},
error
:
function
(
data
)
{
let
msg
=
"erreur serveur lors de la création des product orders"
err
=
{
msg
:
msg
,
ctx
:
'save_supplier_product_association'
,
data
:
orders_data
};
if
(
typeof
data
.
responseJSON
!=
'undefined'
&&
typeof
data
.
responseJSON
.
error
!=
'undefined'
)
{
err
.
msg
+=
' : '
+
data
.
responseJSON
.
error
;
}
report_JS_error
(
err
,
'orders'
);
closeModal
();
alert
(
'Erreur lors de la création des commandes. Veuillez ré-essayer plus tard.'
);
}
});
}
/**
* Get the PO attachment id.
* Display download button when fetch is succesful.
* The file might not be created soon enough, so try again after 10s if error server
*/
function
get_order_attachments
()
{
if
(
product_orders
.
length
>
0
)
{
let
po_ids
=
product_orders
.
map
(
po
=>
po
.
id
);
$
.
ajax
({
type
:
'GET'
,
url
:
"/orders/get_orders_attachment"
,
data
:
{
'po_ids'
:
po_ids
},
dataType
:
"json"
,
traditional
:
true
,
contentType
:
"application/json; charset=utf-8"
,
success
:
function
(
data
)
{
for
(
let
res_po
of
data
.
res
)
{
$
(
`#download_attachment_
${
res_po
.
id_po
}
`
).
attr
(
'href'
,
`
${
odoo_server
}
/web/content/
${
res_po
.
id_attachment
}
?download=true`
)
}
$
(
'#created_orders_area .download_order_file_loading'
).
hide
()
$
(
'#created_orders_area .download_order_file_button'
).
show
()
},
error
:
function
(
data
)
{
console
.
log
(
data
);
$
.
notify
(
"Échec de la récupération du lien de téléchargement des fichiers. Nouvelle tentative dans 10s."
,
{
globalPosition
:
"top right"
,
className
:
"error"
}
);
setTimeout
(
get_order_attachments
,
10000
);
}
});
}
}
/* - DISPLAY */
...
...
@@ -487,7 +636,7 @@ function goto_main_screen(doc) {
products
=
order_doc
.
products
;
selected_suppliers
=
order_doc
.
selected_suppliers
;
update_order
();
update_
cdb_
order
();
update_main_screen
();
switch_screen
();
}
...
...
@@ -503,7 +652,8 @@ function back() {
* @returns String
*/
function
supplier_column_name
(
supplier
)
{
return
`qty_supplier_
${
supplier
.
id
}
`
;
const
supplier_id
=
(
'supplier_id'
in
supplier
)
?
supplier
.
supplier_id
:
supplier
.
id
return
`qty_supplier_
${
supplier_id
}
`
;
}
/**
...
...
@@ -572,7 +722,7 @@ function prepare_datatable_data(product_ids = []) {
};
// If product related to supplier: qty or null (qty to be set)
for
(
product_supplier
of
product
.
suppliers
)
{
for
(
product_supplier
of
product
.
suppliers
info
)
{
let
supplier_qty
=
(
"qty"
in
product_supplier
)
?
product_supplier
.
qty
:
null
;
item
[
supplier_column_name
(
product_supplier
)]
=
supplier_qty
;
...
...
@@ -695,6 +845,8 @@ function prepare_datatable_columns() {
function
display_products
()
{
if
(
products
.
length
==
0
)
{
$
(
'.main'
).
hide
();
$
(
'#create_orders'
).
hide
();
$
(
'#do_inventory'
).
hide
();
return
-
1
;
}
...
...
@@ -736,6 +888,8 @@ function display_products() {
});
$
(
'.main'
).
show
();
$
(
'#create_orders'
).
show
();
$
(
'#do_inventory'
).
show
();
// On inputs change
$
(
'#products_table'
).
on
(
'input'
,
'tbody td .product_qty_input'
,
function
()
{
...
...
@@ -756,7 +910,7 @@ function display_products() {
const
new_row_data
=
prepare_datatable_data
([
product
.
id
])[
0
];
products_table
.
row
(
$
(
this
).
closest
(
'tr'
)).
data
(
new_row_data
).
draw
();
update_order
();
update_
cdb_
order
();
}
});
...
...
@@ -899,6 +1053,15 @@ function update_main_screen() {
return
0
;
});
}
if
(
order_doc
.
date_planned
!==
null
)
{
// Switch format from yy-mm-dd hh:mm:ss to readable dd/mm/yy
let
date_to_format
=
order_doc
.
date_planned
.
split
(
' '
)[
0
];
let
readable_date
=
date_to_format
.
split
(
'-'
).
reverse
().
join
(
'/'
);
$
(
"#date_planned_input"
).
val
(
readable_date
);
}
else
{
$
(
"#date_planned_input"
).
val
(
''
);
}
}
/**
...
...
@@ -936,35 +1099,47 @@ function update_order_selection_screen() {
}
/**
* Switch screen between order selection & main screens
* @param {String} direction target screen
* Switch between screens
* @param {String} direction target screen : order_selection | main_screen | orders_created
* @param {String} from source screen : order_selection | main_screen | orders_created
*/
function
switch_screen
(
direction
=
'main_screen'
)
{
let
oldBox
=
null
;
let
newBox
=
null
;
let
outerWidth
=
null
;
if
(
direction
==
'main_screen'
)
{
oldBox
=
$
(
"#select_order_content"
);
newBox
=
$
(
"#main_content"
);
outerWidth
=
oldBox
.
outerWidth
(
true
);
function
switch_screen
(
direction
=
'main_screen'
,
from
=
'main_screen'
)
{
if
(
direction
===
'orders_created'
)
{
$
(
'#main_content'
).
hide
();
$
(
'#orders_created'
).
show
();
}
else
{
oldBox
=
$
(
"#main_content"
);
newBox
=
$
(
"#select_order_content"
);
outerWidth
=
-
oldBox
.
outerWidth
(
true
);
// Animated transition
let
oldBox
=
null
;
let
newBox
=
null
;
let
outerWidth
=
null
;
if
(
direction
===
'main_screen'
)
{
oldBox
=
$
(
"#select_order_content"
);
newBox
=
$
(
"#main_content"
);
outerWidth
=
oldBox
.
outerWidth
(
true
);
}
else
{
if
(
from
===
'orders_created'
)
{
oldBox
=
$
(
"#orders_created"
);
}
else
{
oldBox
=
$
(
"#main_content"
);
}
newBox
=
$
(
"#select_order_content"
);
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"
},
800
,
function
()
{
// Hide old content after animation
oldBox
.
css
({
"left"
:
""
,
"right"
:
""
,
"display"
:
"none"
});
});
// Slide new box to regular place
newBox
.
animate
({
"left"
:
""
,
"right"
:
""
},
800
);
}
// 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"
},
800
,
function
()
{
// Hide old content after animation
oldBox
.
css
({
"left"
:
""
,
"right"
:
""
,
"display"
:
"none"
});
});
// Slide new box to regular place
newBox
.
animate
({
"left"
:
""
,
"right"
:
""
},
800
);
}
...
...
@@ -985,8 +1160,8 @@ $(document).ready(function() {
sync
.
on
(
'change'
,
function
(
info
)
{
if
(
info
.
direction
===
"pull"
)
{
for
(
const
doc
of
info
.
change
.
docs
)
{
// If current order was modified somewhere else
if
(
order_doc
.
_id
===
doc
.
_id
&&
order_doc
.
_rev
!==
doc
.
_rev
)
{
if
(
order_doc
.
_id
===
doc
.
_id
&&
(
order_doc
.
_rev
!==
doc
.
_rev
||
doc
.
_deleted
===
true
))
{
// If current order was modified somewhere else
$
.
notify
(
"Un autre navigateur est en train de modifier cette commande !"
,
{
...
...
@@ -994,8 +1169,11 @@ $(document).ready(function() {
className
:
"error"
}
);
update_order_selection_screen
();
back
();
break
;
}
else
if
(
doc
.
_deleted
===
true
)
{
update_order_selection_screen
();
}
}
}
...
...
@@ -1008,7 +1186,7 @@ $(document).ready(function() {
console
.
log
(
err
);
});
// Main screen
listeners
// Main screen
$
(
"#supplier_form"
).
on
(
"submit"
,
function
(
e
)
{
e
.
preventDefault
();
add_supplier
();
...
...
@@ -1018,16 +1196,97 @@ $(document).ready(function() {
generate_inventory
();
});
$
(
'#back_to_order_selection'
).
on
(
'click'
,
function
()
{
$
(
'#back_to_order_selection
_from_main
'
).
on
(
'click'
,
function
()
{
back
();
});
$
(
'#create_orders'
).
on
(
'click'
,
function
()
{
if
(
order_doc
.
date_planned
===
null
)
{
alert
(
"Veuillez rentrer une date de livraison prévue."
)
return
-
1
}
let
modal_create_order
=
$
(
'#templates #modal_create_order'
);
openModal
(
modal_create_order
.
html
(),
()
=>
{
create_orders
();
},
'Valider'
,
false
);
});
$
.
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
:
date_format
,
};
$
.
datepicker
.
setDefaults
(
$
.
datepicker
.
regional
[
'fr'
]);
const
tomorrow
=
new
Date
()
tomorrow
.
setDate
(
tomorrow
.
getDate
()
+
1
)
$
(
"#date_planned_input"
)
.
datepicker
({
defaultDate
:
"+1d"
,
minDate
:
tomorrow
})
.
on
(
'change'
,
function
()
{
try
{
// When date input changes, try to read date
$
.
datepicker
.
parseDate
(
date_format
,
$
(
this
).
val
());
// No exception raised: date is valid.
// Change format from readable (dd/mm/yy) to ISO (yy-mm-dd)
let
formatted_date
=
$
(
this
).
val
().
split
(
'/'
).
reverse
().
join
(
'-'
)
+
' 00:00:00'
;
// Update doc if changed
if
(
formatted_date
!==
order_doc
.
date_planned
)
{
order_doc
.
date_planned
=
formatted_date
;
update_cdb_order
();
}
}
catch
(
error
)
{
alert
(
'Date invalide'
);
$
(
this
).
val
(
''
);
order_doc
.
date_planned
=
null
;
update_cdb_order
();
}
});
// Order selection screen
update_order_selection_screen
();
$
(
"#new_order_form"
).
on
(
"submit"
,
function
(
e
)
{
e
.
preventDefault
();
create_order
();
create_cdb_order
();
});
// Orders created screen
$
(
'#back_to_order_selection_from_orders_created'
).
on
(
'click'
,
function
()
{
switch_screen
(
'order_selection'
,
'orders_created'
);
});
// Get suppliers
...
...
orders/urls.py
View file @
a15908ef
...
...
@@ -13,4 +13,6 @@ urlpatterns = [
url
(
r'^get_suppliers$'
,
views
.
get_suppliers
),
url
(
r'^get_supplier_products$'
,
views
.
get_supplier_products
),
url
(
r'^associate_supplier_to_product$'
,
views
.
associate_supplier_to_product
),
url
(
r'^create_orders$'
,
views
.
create_orders
),
url
(
r'^get_orders_attachment$'
,
views
.
get_orders_attachment
),
]
orders/views.py
View file @
a15908ef
...
...
@@ -7,6 +7,7 @@ from products.models import CagetteProduct, CagetteProducts
from
openpyxl
import
Workbook
from
openpyxl.writer.excel
import
save_virtual_workbook
import
datetime
def
as_text
(
value
):
return
str
(
value
)
if
value
is
not
None
else
""
...
...
@@ -17,7 +18,8 @@ def helper(request):
context
=
{
'title'
:
'Aide à la commande'
,
'couchdb_server'
:
settings
.
COUCHDB
[
'url'
],
'db'
:
settings
.
COUCHDB
[
'dbs'
][
'orders'
]
'db'
:
settings
.
COUCHDB
[
'dbs'
][
'orders'
],
'odoo_server'
:
settings
.
ODOO
[
'url'
]
}
template
=
loader
.
get_template
(
'orders/helper.html'
)
...
...
@@ -59,6 +61,48 @@ def associate_supplier_to_product(request):
return
JsonResponse
({
'res'
:
res
})
def
create_orders
(
request
):
""" Create products orders """
res
=
{
"created"
:
[]
}
try
:
data
=
json
.
loads
(
request
.
body
.
decode
())
# suppliers id are keys in request data
for
supplier_id
in
data
[
"suppliers_data"
]
.
keys
():
res_created
=
Order
.
create
(
supplier_id
,
data
[
"date_planned"
],
data
[
"suppliers_data"
][
supplier_id
])
res_created
[
"supplier_id"
]
=
supplier_id
res
[
"created"
]
.
append
(
res_created
)
except
Exception
as
e
:
res
[
"error"
]
=
str
(
e
)
return
JsonResponse
(
res
,
status
=
500
)
return
JsonResponse
({
'res'
:
res
})
def
get_orders_attachment
(
request
):
""" Get order attachment: order file created after PO is finalized """
res
=
[]
po_ids
=
request
.
GET
.
getlist
(
'po_ids'
)
for
id_po
in
po_ids
:
m
=
Order
(
int
(
id_po
))
attachment
=
m
.
get_order_attachment_id
()
if
'error'
in
attachment
:
res
.
append
(
attachment
)
else
:
res
.
append
({
'id_po'
:
id_po
,
'id_attachment'
:
attachment
[
"id"
]
})
for
item
in
res
:
if
'error'
in
item
:
return
JsonResponse
(
res
,
status
=
500
)
return
JsonResponse
({
'res'
:
res
})
def
export_one
(
request
,
oid
):
msg
=
''
try
:
...
...
@@ -66,7 +110,6 @@ def export_one(request, oid):
order
=
Order
(
oid
)
order_data
=
order
.
export
()
if
(
'success'
in
order_data
)
and
(
order_data
[
'success'
]
is
True
):
import
datetime
now
=
datetime
.
datetime
.
now
()
taxes
=
0
company_name
=
''
...
...
products/models.py
View file @
a15908ef
...
...
@@ -436,7 +436,7 @@ class CagetteProducts(models.Model):
today
=
datetime
.
date
.
today
()
.
strftime
(
"
%
Y-
%
m-
%
d"
)
# Get products/supplier relation
f
=
[
"product_tmpl_id"
,
'date_start'
,
'date_end'
,
'package_qty'
]
f
=
[
"product_tmpl_id"
,
'date_start'
,
'date_end'
,
'package_qty'
,
'price'
]
c
=
[[
'name'
,
'='
,
int
(
supplier_id
)]]
psi
=
api
.
search_read
(
'product.supplierinfo'
,
c
,
f
)
...
...
@@ -449,21 +449,30 @@ class CagetteProducts(models.Model):
ptids
.
append
(
p
[
"product_tmpl_id"
][
0
])
# Get products templates
f
=
[
"id"
,
"state"
,
"name"
,
"default_code"
,
"qty_available"
,
"incoming_qty"
,
"uom_id"
,
"purchase_ok"
]
# TODO fetch only 'purchase_ok' products ?
f
=
[
"id"
,
"state"
,
"name"
,
"default_code"
,
"qty_available"
,
"incoming_qty"
,
"uom_id"
,
"purchase_ok"
,
"supplier_taxes_id"
,
"product_variant_ids"
]
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"
]
# Add
package qty
to product data
# Add
supplier data
to product data
for
i
,
fp
in
enumerate
(
filtered_products_t
):
psi_item
=
next
(
item
for
item
in
psi
if
item
[
"product_tmpl_id"
]
is
not
False
and
item
[
"product_tmpl_id"
][
0
]
==
fp
[
"id"
])
filtered_products_t
[
i
][
'supplierinfo'
]
=
{
'supplier_id'
:
supplier_id
,
'package_qty'
:
psi_item
[
"package_qty"
]
}
# Note: if product.product is needed, get "product_variant_ids" from product template
filtered_products_t
[
i
][
'suppliersinfo'
]
=
[{
'supplier_id'
:
int
(
supplier_id
),
'package_qty'
:
psi_item
[
"package_qty"
],
'price'
:
psi_item
[
"price"
]
}]
res
[
"products"
]
=
filtered_products_t
except
Exception
as
e
:
...
...
templates/orders/helper.html
View file @
a15908ef
...
...
@@ -24,17 +24,19 @@
</form>
</div>
<div
id=
"existing_orders_area"
>
<h2>
Ou, continuer une commande e
xistante
</h2>
<h2>
Ou, continuer une commande e
n cours de création
</h2>
<div
id=
"existing_orders"
></div>
</div>
</div>
<div
id=
"main_content"
class=
"page_content"
style=
"display:none;"
>
<div
id=
"back_to_order_selection"
>
<div
id=
"back_to_order_selection
_from_main
"
>
<button
type=
"button"
class=
"btn--danger"
><i
class=
"fas fa-arrow-left"
></i>
Retour
</button>
</div>
<div
id=
"actions_buttons_area"
>
<button
type=
"button"
class=
'btn--primary'
id=
"do_inventory"
>
Faire un inventaire
</button>
<button
type=
"button"
class=
'btn--primary'
id=
"do_inventory"
style=
"display:none;"
>
Faire un inventaire
</button>
</div>
<div
class=
"header txtcenter"
>
...
...
@@ -42,11 +44,12 @@
<i>
Commande :
<span
class=
"order_name_container"
></span></i>
</div>
<div
class=
"txtcenter"
id=
"
supplier_form
_container"
>
<div
class=
"txtcenter"
id=
"
order_data
_container"
>
<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>
</form>
<input
type=
"text"
name=
"date_planned"
id=
"date_planned_input"
placeholder=
"Date de livraison souhaitée"
>
</div>
<div
class=
"txtcenter"
id=
"suppliers_container"
></div>
...
...
@@ -56,6 +59,40 @@
<table
id=
"products_table"
class=
"display"
cellspacing=
"0"
width=
"100%"
></table>
</div>
</div>
<div
id=
"orders_creation_area"
>
<div
class=
"add_product_container"
></div>
<button
type=
"button"
class=
'btn--primary'
id=
"create_orders"
style=
"display:none;"
>
Générer les commandes
</button>
</div>
</div>
<div
id=
"orders_created"
class=
"page_content"
style=
"display:none;"
>
<div
id=
"back_to_order_selection_from_orders_created"
>
<button
type=
"button"
class=
"btn--danger"
><i
class=
"fas fa-arrow-left"
></i>
Retour
</button>
</div>
<div
class=
"order_created_header txtcenter"
>
<h2>
Commandes créées !
</h2>
</div>
<div
class=
"txtcenter"
>
Livraison prévue le :
<span
id=
"recap_delivery_date"
>
XX/XX/XX
</span>
</div>
<div
id=
"created_orders_area"
></div>
<br/><br/><hr/><br/>
<div
class=
"mail_example_container"
>
<p
class=
"mail_type_text"
>
Mail type :
</p>
<div
class=
"mail_example"
>
Objet : Cde Cagette JJ/MM
<br/>
<br/>
Bonjour XXXXXXX,
<br/>
<br/>
Voici la commande de La Cagette pour le XX/XX/XX.
<br/>
<br/>
Merci d'avance,
<br/>
Bonne journée
</div>
</div>
</div>
<div
id=
"templates"
style=
"display:none;"
>
...
...
@@ -74,6 +111,19 @@
</div>
</div>
<div
id=
"new_order_item_template"
>
<div
class=
"new_order_item"
>
<h3
class=
"new_order_supplier_name"
></h3>
<h3
class=
"new_order_po"
></h3>
<div
class=
'download_order_file'
>
<i
class=
"fas fa-spinner fa-spin download_order_file_loading"
></i>
<a
class=
'btn--success download_order_file_button'
style=
"display:none;"
href=
"#"
>
Télécharger le fichier de commande
</a>
</div>
</div>
</div>
<div
id=
"modal_order_access"
>
<h3>
Attention !
</h3>
<br/>
...
...
@@ -126,6 +176,15 @@
<p>
Êtez-vous sûr ?
</p>
<hr/>
</div>
<div
id=
"modal_create_order"
>
<h3>
Attention !
</h3>
<p>
Vous vous apprêtez à générer les commandes à partir des données rentrées dans le tableau.
</p>
<p>
Êtez-vous sûr ?
</p>
<hr/>
</div>
</div>
</div>
...
...
@@ -134,6 +193,7 @@
<script
type=
"text/javascript"
>
var
couchdb_dbname
=
'{{db}}'
;
var
couchdb_server
=
'{{couchdb_server}}'
+
couchdb_dbname
;
var
odoo_server
=
'{{odoo_server}}'
;
</script>
<script
src=
"{% static "
js
/
all_common
.
js
"
%}?
v=
"></script>
<script type="
text
/
javascript
"
src=
"{% static 'js/orders_helper.js' %}?v="
></script>
...
...
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