[MERGE] merged csn branch with barcode interface greatly improved
bzr revid: qdp-launchpad@openerp.com-20140327132221-ylk22ykso1wyq1ca
This commit is contained in:
commit
a7fdd2371f
|
@ -27,3 +27,5 @@ import report
|
|||
import wizard
|
||||
import res_config
|
||||
|
||||
import controllers
|
||||
|
||||
|
|
|
@ -104,7 +104,6 @@ Dashboard / Reports for Warehouse Management will include:
|
|||
'application': True,
|
||||
'auto_install': False,
|
||||
'css': [
|
||||
'static/src/css/picking.css',
|
||||
'static/src/css/stock.css',
|
||||
],
|
||||
'js': [
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
import main
|
|
@ -0,0 +1,75 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import logging
|
||||
import simplejson
|
||||
import os
|
||||
import openerp
|
||||
import time
|
||||
import random
|
||||
|
||||
from openerp import http
|
||||
from openerp.http import request
|
||||
from openerp.addons.web.controllers.main import manifest_list, module_boot, html_template
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
html_template = """<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Barcode Scanner</title>
|
||||
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
|
||||
<meta http-equiv="content-type" content="text/html, charset=utf-8" />
|
||||
|
||||
<meta name="viewport" content=" width=1024, user-scalable=no">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
|
||||
<link rel="shortcut icon" sizes="80x51" href="/stock/static/src/img/scan.png">
|
||||
<link rel="shortcut icon" href="/web/static/src/img/favicon.ico" type="image/x-icon"/>
|
||||
<link rel="stylesheet" href="/stock/static/src/css/barcode.css" />
|
||||
<link rel="stylesheet" href="/web/static/lib/bootstrap/css/bootstrap.css" />
|
||||
<link rel="stylesheet" href="/web/static/lib/jquery.ui/css/smoothness/jquery-ui-1.9.1.custom.css" />
|
||||
<link rel="stylesheet" href="/web/static/lib/fontawesome/css/font-awesome.css" />
|
||||
%(js)s
|
||||
<script type="text/javascript">
|
||||
$(function() {
|
||||
var s = new openerp.init(%(modules)s);
|
||||
%(init)s
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<!--[if lte IE 8]>
|
||||
<script src="//ajax.googleapis.com/ajax/libs/chrome-frame/1/CFInstall.min.js"></script>
|
||||
<script>CFInstall.check({mode: "overlay"});</script>
|
||||
<![endif]-->
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
class BarcodeController(http.Controller):
|
||||
|
||||
@http.route(['/barcode/web/'], type='http', auth='user')
|
||||
def a(self, debug=False, **k):
|
||||
if not request.session.uid:
|
||||
return http.local_redirect('/web/login?redirect=/barcode/web')
|
||||
|
||||
js_list = manifest_list('js',db=request.db, debug=debug)
|
||||
css_list = manifest_list('css',db=request.db, debug=debug)
|
||||
|
||||
js = "\n".join('<script type="text/javascript" src="%s"></script>' % i for i in js_list)
|
||||
#css = "\n".join('<link rel="stylesheet" href="%s">' % i for i in css_list)
|
||||
r = html_template % {
|
||||
'js': js,
|
||||
# 'css': css,
|
||||
'modules': simplejson.dumps(module_boot(request.db)),
|
||||
'init': """
|
||||
var wc = new s.web.WebClient();
|
||||
wc.show_application = function(){
|
||||
wc.action_manager.do_action("stock.ui", {});
|
||||
};
|
||||
wc.do_push_state = function(state){};
|
||||
wc.appendTo($(document.body));
|
||||
"""
|
||||
}
|
||||
return r
|
|
@ -0,0 +1,134 @@
|
|||
.in_container_hidden {
|
||||
display: none;
|
||||
}
|
||||
.in_container {
|
||||
}
|
||||
.oe_pick_app_header{
|
||||
margin-top: 0;
|
||||
}
|
||||
.oe_picking {
|
||||
cursor: pointer;
|
||||
}
|
||||
.oe_kanban.oe_picking {
|
||||
min-height: 80px;
|
||||
margin-bottom: 10px;
|
||||
border: 1px solid rgba(0, 0, 0, 0.16);
|
||||
border-bottom-color: rgba(0, 0, 0, 0.3);
|
||||
-webkit-transition: -webkit-transform, -webkit-box-shadow, border 200ms linear;
|
||||
-moz-border-radius: 4px;
|
||||
-webkit-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.oe_kanban_color_0 {
|
||||
background-color: white;
|
||||
color: #5a5a5a;
|
||||
}
|
||||
.oe_kanban_color_1 {
|
||||
background-color: #cccccc;
|
||||
color: #424242;
|
||||
}
|
||||
.oe_kanban_color_2 {
|
||||
background-color: #ffc7c7;
|
||||
color: #7a3737;
|
||||
}
|
||||
.oe_kanban_color_3 {
|
||||
background-color: #fff1c7;
|
||||
color: #756832;
|
||||
}
|
||||
.oe_kanban_color_4 {
|
||||
background-color: #e3ffc7;
|
||||
color: #5d6937;
|
||||
}
|
||||
.oe_kanban_color_5 {
|
||||
background-color: #c7ffd5;
|
||||
color: #1a7759;
|
||||
}
|
||||
.oe_kanban_color_6 {
|
||||
background-color: #c7ffff;
|
||||
color: #1a5d83;
|
||||
}
|
||||
.oe_kanban_color_7 {
|
||||
background-color: #c7d5ff;
|
||||
color: #3b3e75;
|
||||
}
|
||||
.oe_kanban_color_8 {
|
||||
background-color: #e3c7ff;
|
||||
color: #4c3668;
|
||||
}
|
||||
.oe_kanban_color_9 {
|
||||
background-color: #ffc7f1;
|
||||
color: #6d2c70;
|
||||
}
|
||||
|
||||
/*Blinking text*/
|
||||
.blink_me {
|
||||
-webkit-animation-name: blinker;
|
||||
-webkit-animation-duration: 1s;
|
||||
-webkit-animation-timing-function: linear;
|
||||
-webkit-animation-iteration-count: 2;
|
||||
|
||||
-moz-animation-name: blinker;
|
||||
-moz-animation-duration: 1s;
|
||||
-moz-animation-timing-function: linear;
|
||||
-moz-animation-iteration-count: 2;
|
||||
|
||||
animation-name: blinker;
|
||||
animation-duration: 1s;
|
||||
animation-timing-function: linear;
|
||||
animation-iteration-count: 2;
|
||||
}
|
||||
|
||||
@-moz-keyframes blinker {
|
||||
0% { opacity: 1.0; }
|
||||
50% { opacity: 0.0; }
|
||||
100% { opacity: 1.0; }
|
||||
}
|
||||
|
||||
@-webkit-keyframes blinker {
|
||||
0% { opacity: 1.0; }
|
||||
50% { opacity: 0.0; }
|
||||
100% { opacity: 1.0; }
|
||||
}
|
||||
|
||||
@keyframes blinker {
|
||||
0% { opacity: 1.0; }
|
||||
50% { opacity: 0.0; }
|
||||
100% { opacity: 1.0; }
|
||||
}
|
||||
|
||||
/*hide OpenERP leftbar, table should use all width by default and display vertical scrollbar if needed*/
|
||||
.oe_leftbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
table.oe_webclient.oe_content_full_screen{
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
body{
|
||||
overflow-y: visible !important;
|
||||
}
|
||||
|
||||
/* --- Styling of OpenERP Elements ---
|
||||
Needed for loading and error box */
|
||||
|
||||
/* Increase z-index value to insure that loading goes above navbar of bootstrap*/
|
||||
.openerp .oe_loading {
|
||||
display: none;
|
||||
z-index: 1000;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 50%;
|
||||
padding: 4px 12px;
|
||||
background: #a61300;
|
||||
color: white;
|
||||
text-align: center;
|
||||
border: 1px solid #990000;
|
||||
border-top: none;
|
||||
-moz-border-radius-bottomright: 8px;
|
||||
-moz-border-radius-bottomleft: 8px;
|
||||
border-bottom-right-radius: 8px;
|
||||
border-bottom-left-radius: 8px;
|
||||
}
|
|
@ -1,291 +0,0 @@
|
|||
|
||||
/* ----------------------- *
|
||||
* PICKING WIDGET LAYOUT *
|
||||
* ----------------------- */
|
||||
|
||||
.oe_pick_widget {
|
||||
position: absolute;
|
||||
top: 0px; right: 0px; bottom: 0px; left: 0px;
|
||||
color: #444444;
|
||||
background: #686868;
|
||||
text-shadow: none;
|
||||
font-family: 'Lato', 'Open Sans', Arial, Helvetica, sans-serif;
|
||||
}
|
||||
.oe_pick_widget .oe_pick_layout {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.oe_pick_widget .oe_hidden{
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.oe_pick_widget .oe_pick_header{
|
||||
background: #454343;
|
||||
background-image: linear-gradient(#646060,#262626);
|
||||
}
|
||||
.oe_pick_widget .oe_pick_body_cont{
|
||||
height: 100%;
|
||||
}
|
||||
.oe_pick_widget .oe_pick_body{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: white;
|
||||
background: url('/web/static/src/img/form_sheetbg.png');
|
||||
box-shadow: 0px 9px 5px -5px rgba(0, 0, 0, 0.16) inset;
|
||||
overflow: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.oe_pick_widget .oe_pick_app{
|
||||
width: 600px;
|
||||
background: white;
|
||||
margin: 40px auto;
|
||||
padding: 20px;
|
||||
border: solid 1px rgb(228, 228, 255);
|
||||
box-shadow: 0px 2px 3px rgba(114, 114, 175, 0.11);
|
||||
}
|
||||
.oe_pick_widget .oe_pick_toolbar{
|
||||
width: 600px;
|
||||
margin: auto;
|
||||
text-align: center;
|
||||
}
|
||||
.oe_pick_widget .oe_pick_button{
|
||||
display: inline-block;
|
||||
min-width: 64px;
|
||||
padding: 5px 10px;
|
||||
font-size: 18px;
|
||||
border-radius: 3px;
|
||||
margin: 10px 5px;
|
||||
text-align: center;
|
||||
line-height: 30px;
|
||||
background: #b2b3d7;
|
||||
background: linear-gradient(#b2b3d7, #7f82ac);
|
||||
color: #273072;
|
||||
border: 1px solid #353A7E;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(255, 255, 255, 0.8) inset;
|
||||
cursor: pointer;
|
||||
text-shadow: 0 1px 1px rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
.oe_pick_widget .oe_pick_button.oe_disabled {
|
||||
cursor: default;
|
||||
background-color: #5A5A5A;
|
||||
background: linear-gradient(#666666, #474747);
|
||||
border: solid 1px rgb(44, 44, 44);
|
||||
color: rgb(53, 53, 53);
|
||||
box-shadow: 0px 1px 1px rgba(255, 255, 255, 0.32) inset;
|
||||
}
|
||||
|
||||
.oe_pick_widget .oe_pick_button:not(.oe_disabled):active,
|
||||
.oe_pick_widget .oe_pick_button:not(.oe_disabled).oe_active{
|
||||
background: rgb(92, 84, 133);
|
||||
box-shadow: 0px 1px 0px 1px rgba(0,0,0,0.3) inset;
|
||||
margin: 11px 6px;
|
||||
border: none;
|
||||
color: white;
|
||||
text-shadow: 0px 1px rgba(0,0,0, 0.3);
|
||||
}
|
||||
|
||||
.oe_pick_widget .oe_pick_button.oe_small{
|
||||
min-width: 0px;
|
||||
width: 32px;
|
||||
}
|
||||
.oe_pick_widget .oe_pick_right_toolbar{
|
||||
float: right;
|
||||
border-left: solid 1px rgba(0,0,0,0.3);
|
||||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
.oe_pick_widget .oe_pick_col_small{ width: 60px; }
|
||||
.oe_pick_widget .oe_pick_col_medium{ width: 100px; }
|
||||
.oe_pick_widget .oe_pick_col_big{ width: 200px; }
|
||||
.oe_pick_widget .oe_pick_col_expand{ width: 100%; }
|
||||
.oe_pick_widget .oe_centeralign{ text-align: center; }
|
||||
|
||||
.oe_pick_widget .oe_invalid{
|
||||
background: rgb(255, 226, 226);
|
||||
color: rgb(168, 6, 6);
|
||||
}
|
||||
.oe_pick_widget .oe_pick_app_header{
|
||||
font-size: 24px;
|
||||
margin-left: 5px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.oe_pick_widget .oe_pick_app_title{
|
||||
font-size: 20px;
|
||||
margin-top: 0px;
|
||||
}
|
||||
.oe_pick_widget .oe_pick_app_subtitle{
|
||||
font-size: 16px;
|
||||
font-weight: normal;
|
||||
}
|
||||
.oe_pick_widget .oe_pick_app_info{
|
||||
margin-left: 4px;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ----------------------- *
|
||||
* PICKING TABLES *
|
||||
* ----------------------- */
|
||||
|
||||
.oe_pick_widget .oe_pick_list{
|
||||
margin: 0px 0px 20px 0px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.oe_pick_widget .oe_pick_list_header{
|
||||
font-size: 20px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.oe_pick_widget .oe_pick_list_table{
|
||||
background: whitesmoke;
|
||||
width: 100%;
|
||||
table-layout: fixed;
|
||||
font-size: 16px;
|
||||
border-radius: 3px;
|
||||
|
||||
}
|
||||
.oe_pick_widget .oe_pick_list_table tbody tr:nth-child(odd){
|
||||
background: rgb(250,250,250);
|
||||
}
|
||||
.oe_pick_widget .oe_pick_list_table tbody tr.oe_selected{
|
||||
background: rgb(236, 236, 247);
|
||||
outline: solid 1px rgba(79, 107, 255, 0.25);
|
||||
}
|
||||
.oe_pick_widget .oe_pick_list_table td,
|
||||
.oe_pick_widget .oe_pick_list_table th {
|
||||
padding: 5px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.oe_pick_widget .oe_pick_list_table thead{
|
||||
background: rgb(224, 225, 235);
|
||||
border-top-left-radius: 3px;
|
||||
border-top-right-radius: 3px;
|
||||
}
|
||||
.oe_pick_widget .oe_pick_list_table th {
|
||||
background: none;
|
||||
border: none;
|
||||
font-weight: bold;
|
||||
}
|
||||
.oe_pick_widget .oe_row_button{
|
||||
display: inline-block;
|
||||
background: rgb(238, 237, 248);
|
||||
padding: 2px 4px;
|
||||
line-height: 14px;
|
||||
color: rgb(137, 127, 173);
|
||||
border-radius: 3px;
|
||||
border: solid 1px rgba(0, 0, 0, 0.09);
|
||||
cursor: pointer;
|
||||
}
|
||||
.oe_pick_widget .oe_expanded{
|
||||
text-align: center;
|
||||
min-width: 32px;
|
||||
}
|
||||
.oe_pick_widget .js_pack_select,
|
||||
.oe_pick_widget .js_pack_op{
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
/* ----------------------- *
|
||||
* MAIN PICKING MENU *
|
||||
* ----------------------- */
|
||||
|
||||
.oe_pick_widget .oe_picking{
|
||||
background: rgb(223, 226, 246);
|
||||
padding: 10px;
|
||||
margin-bottom: 3px;
|
||||
border-radius: 3px;
|
||||
border-bottom: solid 2px rgb(189, 189, 241);
|
||||
cursor: pointer;
|
||||
}
|
||||
.oe_pick_widget .oe_picking.oe_empty {
|
||||
border: solid 1px rgb(228, 226, 241);
|
||||
background: white;
|
||||
cursor: default;
|
||||
}
|
||||
.oe_pick_widget .oe_picking > .oe_picking_name{
|
||||
display: inline-block;
|
||||
min-width: 100px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.oe_pick_widget .oe_search_empty{
|
||||
padding: 10px;
|
||||
border-radius: 3px;
|
||||
background: rgb(228, 226, 241);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* ----------------------- *
|
||||
* SEARCH BOX *
|
||||
* ----------------------- */
|
||||
|
||||
.oe_pick_widget .oe_searchbox{
|
||||
margin-top: 13px;
|
||||
}
|
||||
.oe_pick_widget .oe_searchbox input{
|
||||
padding: 8px;
|
||||
}
|
||||
.oe_pick_widget .oe_picking_not_found{
|
||||
padding: 8px;
|
||||
margin: 24px 0px;
|
||||
text-align: center;
|
||||
background: rgb(239, 240, 247);
|
||||
border-radius: 3px;
|
||||
color: rgb(119, 77, 77);
|
||||
}
|
||||
|
||||
/* ----------------------- *
|
||||
* RESPONSIVENESS *
|
||||
* ----------------------- */
|
||||
|
||||
@media screen and (max-width:642px){
|
||||
.oe_pick_widget .oe_pick_body{
|
||||
background: white;
|
||||
background-image: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
.oe_pick_widget .oe_pick_app{
|
||||
width: auto;
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
.oe_pick_widget .oe_pick_toolbar{
|
||||
width: auto;
|
||||
}
|
||||
.oe_pick_widget .oe_pick_right_toolbar{
|
||||
float: none;
|
||||
text-align: center;
|
||||
border-left: none;
|
||||
margin-left: 0px;
|
||||
}
|
||||
.oe_pick_widget .oe_pick_button{
|
||||
margin-top: 3px;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width:450px){
|
||||
.oe_pick_widget .oe_pick_list_header{
|
||||
font-size: 16px;
|
||||
}
|
||||
.oe_pick_widget .oe_pick_list_table{
|
||||
font-size: 12px;
|
||||
}
|
||||
.oe_pick_widget .oe_pick_col_small{
|
||||
width: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width:326px){
|
||||
.oe_pick_widget .oe_pick_toolbar{
|
||||
width: 326px;
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="TactileListWidget">
|
||||
<!-- <t t-name="TactileListWidget">
|
||||
<div class='oe_tactlist js_width' t-att-style="'width:' + widget.get_width() + 'px'">
|
||||
<div class='oe_tactlist_header'>
|
||||
<t t-esc='widget.get_title()' />
|
||||
|
@ -44,228 +44,228 @@
|
|||
<span class='oe_hresizer js_hresizer'>●</span>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</t> -->
|
||||
|
||||
<t t-name='PickingEditorWidget'>
|
||||
<div class='oe_pick_list'>
|
||||
<div class='oe_pick_list_header'>
|
||||
Operations To Process
|
||||
</div>
|
||||
<table class='oe_pick_list_table'>
|
||||
|
||||
<colgroup>
|
||||
<col class='oe_pick_col_expand'></col>
|
||||
<col class='oe_pick_col_small'></col>
|
||||
<col class='oe_pick_col_small'></col>
|
||||
<col class='oe_pick_col_small'></col>
|
||||
<col class='oe_pick_col_expand'></col>
|
||||
<col class='oe_pick_col_small'></col>
|
||||
</colgroup>
|
||||
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Product</th>
|
||||
<th class='oe_centeralign'>Qty</th>
|
||||
<th class='oe_centeralign'>Rem</th>
|
||||
<th class='oe_centeralign'>UoM</th>
|
||||
<th>Location</th>
|
||||
<th>Scan</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<t t-foreach="widget.get_rows()" t-as="row">
|
||||
<tr t-att-class="row.classes">
|
||||
<td> <t t-esc="row.cols.product" /> </td>
|
||||
<td class='oe_centeralign'> <t t-esc="row.cols.qty" /> </td>
|
||||
<td class='oe_centeralign'> <t t-esc="row.cols.rem" /> </td>
|
||||
<td class='oe_centeralign'> <t t-esc="row.cols.uom" /> </td>
|
||||
<td> <t t-esc="row.cols.loc" /> </td>
|
||||
<td><span class='oe_row_button oe_expanded js_pack_scan' t-att-op-id='row.cols.id'>➔</span></td>
|
||||
</tr>
|
||||
</t>
|
||||
</tbody>
|
||||
|
||||
</table>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name='PackageEditorWidget'>
|
||||
<div class='oe_pick_list'>
|
||||
<div class='oe_pick_list_header'> <t t-esc="widget.get_header()" /> </div>
|
||||
<table class='oe_pick_list_table'>
|
||||
|
||||
<colgroup>
|
||||
<col class='oe_pick_col_expand'></col>
|
||||
<col class='oe_pick_col_small'></col>
|
||||
<col class='oe_pick_col_small'></col>
|
||||
</colgroup>
|
||||
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Product</th>
|
||||
<th class='oe_centeralign'>Qty</th>
|
||||
<th class='oe_centeralign'>UoM</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<t t-foreach="widget.get_rows()" t-as="row">
|
||||
<tr t-att-class="row.classes" t-att-op-id="row.att_op_id">
|
||||
<td> <t t-esc="row.cols.product" /> </td>
|
||||
<td class='oe_centeralign'> <t t-esc="row.cols.qty" /> </td>
|
||||
<td class='oe_centeralign'> <t t-esc="row.cols.uom" /> </td>
|
||||
</tr>
|
||||
</t>
|
||||
</tbody>
|
||||
|
||||
</table>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name='PackageSelectorWidget'>
|
||||
<div class='oe_pick_list'>
|
||||
<div class='oe_pick_list_header'><t t-esc="widget.get_header()" /></div>
|
||||
<table class='oe_pick_list_table'>
|
||||
|
||||
<colgroup>
|
||||
<col class='oe_pick_col_expand'></col>
|
||||
<col class='oe_pick_col_small'></col>
|
||||
<col class='oe_pick_col_small'></col>
|
||||
<col class='oe_pick_col_small'></col>
|
||||
</colgroup>
|
||||
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Package</th> <th></th> <th></th> <th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<t t-foreach="widget.get_rows()" t-as="row">
|
||||
<tr t-att-class="'js_pack_row' + row.classes" t-att-pack-id="row.id">
|
||||
<td class='js_pack_select'> <t t-esc="row.cols.pack" /> </td>
|
||||
<td class='oe_centeralign'> <span class='oe_row_button js_pack_print'>Print</span> </td>
|
||||
<td class='oe_centeralign'> <span class='oe_row_button js_pack_plus'>Copy</span> </td>
|
||||
<td class='oe_centeralign'> <span class='oe_row_button js_pack_minus'>Delete</span> </td>
|
||||
</tr>
|
||||
</t>
|
||||
</tbody>
|
||||
|
||||
</table>
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="PickingSearchResults">
|
||||
<t t-if="results.length === 0">
|
||||
<div class='oe_search_empty'>
|
||||
No picking found.
|
||||
</div>
|
||||
</t>
|
||||
<t t-if="results.length > 0">
|
||||
<t t-foreach="results" t-as="picking">
|
||||
<div class="oe_picking" t-att-data-id="picking.id">
|
||||
<span class='oe_picking_name'><t t-esc="picking.name" /></span>
|
||||
|
||||
<div class="modal fade" id="js_LocationChooseModal" tabindex="-1" role="dialog" aria-labelledby="LocationChooseModal" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">x</button>
|
||||
<h4 class="modal-title" id="myModalLabel">Choose a location</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>Scan a location or select it in the list below</p>
|
||||
<select id="js_loc_select" class="form-control">
|
||||
<option class="js_loc_option" data-loc-id="false"></option>
|
||||
<t t-foreach="widget.get_location()" t-as="loc">
|
||||
<option class="js_loc_option" t-att-data-loc-id="loc.id"><t t-esc="loc.name"/></option>
|
||||
</t>
|
||||
</select>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
<button type="button" class="btn btn-primary js_validate_location">Change Location</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div>
|
||||
<div class="col-sm-4 col-xs-6">
|
||||
<h2 class="oe_pick_app_header" />
|
||||
</div>
|
||||
<div class="col-sm-8 col-xs⁻6 text-right">
|
||||
|
||||
<button type="button" class='btn btn-default js_pick_done'> Create backorder </button>
|
||||
<button type="button" class='btn btn-default js_pick_print'> Print </button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div>
|
||||
<div class="col-md-3 col-sm-4 col-xs-6">
|
||||
<h3><strong>
|
||||
<select id="js_select" class="form-control">
|
||||
<option value="ToDo" id="js_select_todo">Operations ToDo</option>
|
||||
<option value="Processed" id="js_select_processed">Operations Processed</option>
|
||||
</select>
|
||||
</strong>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="col-md-3 col-sm-4 col-xs-6">
|
||||
<h2>
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control oe_searchbox" placeholder="Filter by location..."/>
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-danger js_clear_search" type="button">x</button>
|
||||
</span>
|
||||
</div>
|
||||
</h2>
|
||||
</div>
|
||||
<div class="col-md-6 col-sm-4 col-xs-12 text-right">
|
||||
<h3>
|
||||
<button type="button" class='btn btn-default js_pick_pack'> Put in Pack </button>
|
||||
<button type="button" class='btn btn-danger js_drop_down'> Process </button>
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<table class='table table-condensed js_op_table_todo'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-left">Product</th>
|
||||
<th class='text-center' width="150">Scanned</th>
|
||||
<th class='text-center'>Todo</th>
|
||||
<th>From</th>
|
||||
<th>To</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<t t-foreach="widget.get_rows()" t-as="row">
|
||||
<tr t-att-class="row.classes + 'js_pack_op_line'" t-att-data-id="row.cols.id" t-att-data-container-id="row.cols.container_id">
|
||||
<td t-att-class="'text-left' + row.cols.head_container ? ' js_unfold' : ''">
|
||||
<t t-if="!row.cols.head_container && row.cols.container"><span class="fa fa-level-up fa-rotate-90" style="margin-left:10px;margin-right:10px;"></span></t>
|
||||
<t t-esc="row.cols.product" />
|
||||
</td>
|
||||
<td class='text-center js_row_qty'>
|
||||
<t t-if="row.cols.processed == 'false' && !row.cols.container">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon js_minus input-sm" t-att-data-product-id='row.cols.product_id'><a href="#"><i class="fa fa-minus"></i></a></span>
|
||||
<form class="js_submit_value">
|
||||
<input type="text" class="form-control text-center js_qty" t-att-placeholder="row.cols.rem"></input>
|
||||
<!-- <input type="submit" class="hidden"></input> -->
|
||||
</form>
|
||||
<span class="input-group-addon js_plus input-sm" t-att-data-product-id='row.cols.product_id'><a href="#"><i class="fa fa-plus"></i></a></span>
|
||||
</div>
|
||||
</t>
|
||||
<t t-if="(row.cols.processed == 'true' || row.cols.container)">
|
||||
<t t-esc="row.cols.rem" />
|
||||
</t>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<t t-esc="row.cols.qty"/> <t t-esc="row.cols.uom" />
|
||||
</td>
|
||||
<td class="js_loc">
|
||||
<t t-esc="row.cols.loc" />
|
||||
<t t-if="row.cols.pack" ><span> : <t t-esc="row.cols.pack" /></span></t>
|
||||
<t t-if="row.cols.lot" ><span> : <t t-esc="row.cols.lot" /></span></t>
|
||||
</td>
|
||||
<td class="js_loc"> <t t-esc="row.cols.dest" />
|
||||
<div class="pull-right btn-group">
|
||||
<button type="button" class="btn btn-default dropdown-toggle fa fa-cog" data-toggle="dropdown">
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<!-- <t t-if="!row.cols.lot && row.cols.product_id">
|
||||
<li><a class="js_create_lot" href="#">Create Lot</a></li>
|
||||
</t> -->
|
||||
<t t-if="!row.cols.head_container && !row.cols.container">
|
||||
<li><a class="js_change_src" href="#">Change source location</a></li>
|
||||
<li><a class="js_change_dst" href="#">Change destination location</a></li>
|
||||
</t>
|
||||
<t t-if="row.cols.head_container">
|
||||
<li><a class="js_pack_change_dst" href="#">Change destination location</a></li>
|
||||
<li class="divider"></li>
|
||||
<li><a class="js_delete_pack" href="#">Remove from package</a></li>
|
||||
<li><a class="js_print_pack" href="#">Print package label</a></li>
|
||||
</t>
|
||||
</ul>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</t>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="PickingSearchResults">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">Search Results</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<t t-if="results.length === 0">
|
||||
<strong>No picking found.</strong>
|
||||
</t>
|
||||
</t>
|
||||
<t t-if="results.length > 0">
|
||||
<t t-foreach="results" t-as="picking">
|
||||
<div class="col-lg-3 col-md-4">
|
||||
<div class="panel panel-default oe_picking" t-att-data-id="picking.id">
|
||||
<div class="panel-body">
|
||||
<strong class='oe_picking_name'><t t-esc="picking.name" /></strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="PickingMenuWidget">
|
||||
<div class='oe_pick_widget'>
|
||||
<table class='oe_pick_layout'>
|
||||
<tr class='oe_pick_header_row'>
|
||||
<td class='oe_pick_header'>
|
||||
<div class='oe_pick_right_toolbar'>
|
||||
<div class='oe_pick_button js_pick_quit'> Quit </div>
|
||||
<div class="navbar navbar-inverse navbar-static-top" role="navigation">
|
||||
<div class="container">
|
||||
<div class="navbar-header navbar-form navbar-left">
|
||||
<input type='text' class="oe_searchbox form-control pull-left" placeholder='Search'/>
|
||||
</div>
|
||||
<div class="navbar-header navbar-form navbar-right">
|
||||
<button type="button" class="btn btn-danger js_pick_quit pull-right">Quit</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
|
||||
<h1 class="js_title_label">Select your operation</h1>
|
||||
<div class='js_picking_not_found alert alert-warning hidden'>
|
||||
Scanned picking could not be found
|
||||
</div>
|
||||
|
||||
<div class='js_picking_search_results panel panel-info hidden'>
|
||||
</div>
|
||||
|
||||
<div class="row js_picking_categories">
|
||||
<t t-foreach="widget.picking_types" t-as="type">
|
||||
<div class="col-lg-3 col-md-4">
|
||||
<div t-att-class="'oe_kanban oe_picking oe_kanban_color_' + type.color + ' ' + (widget.pickings_by_type[type.id].length === 0 ? 'oe_empty':'js_pick_last') "
|
||||
t-att-data-id="type.id">
|
||||
<t t-if="type.code == 'incoming'" ><span class="fa fa-sign-in fa-2x"></span></t>
|
||||
<t t-if="type.code == 'outgoing'" ><span class="fa fa-truck fa-2x fa-flip-horizontal"></span></t>
|
||||
<t t-if="type.code == 'internal'" ><span class="fa fa-retweet fa-2x"></span></t>
|
||||
<strong><span><t t-esc="type.complete_name"/></span></strong>
|
||||
<div><t t-if="widget.pickings_by_type[type.id].length > 0">
|
||||
<span class='badge'><t t-esc="widget.pickings_by_type[type.id].length" /> picking(s) </span>
|
||||
</t></div>
|
||||
</div>
|
||||
<div class='oe_pick_toolbar'>
|
||||
<div class='oe_searchbox oe_left'>
|
||||
<input type='text' placeholder='Search'/>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class='oe_pick_body_row'>
|
||||
<td class='oe_pick_body_cont'>
|
||||
<div class='oe_pick_body'>
|
||||
<div class='oe_pick_app'>
|
||||
|
||||
<h3 class='oe_pick_app_title'>Pickings</h3>
|
||||
|
||||
<div class='oe_picking_not_found oe_hidden'>
|
||||
Scanned picking could not be found
|
||||
</div>
|
||||
|
||||
<div class='oe_picking_search_results'>
|
||||
</div>
|
||||
|
||||
<div class='oe_picking_categories'>
|
||||
<p>
|
||||
Select the type of picking you want to process.
|
||||
</p>
|
||||
<t t-foreach="widget.picking_types" t-as="type">
|
||||
<div t-att-class="'oe_picking ' + (widget.pickings_by_type[type.id].length === 0 ? 'oe_empty':'js_pick_last') "
|
||||
t-att-data-id="type.id">
|
||||
|
||||
<span class='oe_picking_name'><t t-esc="type.complete_name" /></span>
|
||||
<t t-if="widget.pickings_by_type[type.id].length === 0">
|
||||
<span class='oe_pick_app_info'>Nothing to do</span>
|
||||
</t>
|
||||
<t t-if="widget.pickings_by_type[type.id].length === 1">
|
||||
<span class='oe_pick_app_info'>1 picking</span>
|
||||
</t>
|
||||
<t t-if="widget.pickings_by_type[type.id].length > 1">
|
||||
<span class='oe_pick_app_info'><t t-esc="widget.pickings_by_type[type.id].length" /> pickings </span>
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
|
||||
|
||||
<t t-name="PickingMainWidget">
|
||||
<div class='oe_pick_widget'>
|
||||
<table class='oe_pick_layout'>
|
||||
<tr class='oe_pick_header_row'>
|
||||
<td class='oe_pick_header'>
|
||||
<div class='oe_pick_right_toolbar'>
|
||||
<div class='oe_pick_button js_pick_menu'> Menu </div>
|
||||
</div>
|
||||
<div class='oe_pick_toolbar'>
|
||||
<div class='oe_pick_button oe_disabled js_pick_prev'>< Previous</div>
|
||||
<div class='oe_pick_button oe_disabled js_pick_next'>Next ></div>
|
||||
</div>
|
||||
<div class="navbar navbar-inverse navbar-static-top" role="navigation">
|
||||
<div class="container">
|
||||
<div class="navbar-left">
|
||||
<button type="button" class="btn btn-primary navbar-btn js_pick_menu">Menu</button>
|
||||
</div>
|
||||
<div class="navbar-right">
|
||||
<button type="button" class="btn btn-default navbar-btn js_pick_prev">< Previous</button>
|
||||
<button type="button" class="btn btn-default navbar-btn js_pick_next">Next ></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr class='oe_pick_body_row'>
|
||||
<td class='oe_pick_body_cont'>
|
||||
<div class='oe_pick_body'>
|
||||
<div class='oe_pick_app'>
|
||||
<div class='oe_pick_button js_pick_done'> Done </div>
|
||||
<div class='oe_pick_button js_pick_print'> Print </div>
|
||||
<div class='oe_pick_app_header'>
|
||||
<t t-esc='widget.get_header()' />
|
||||
</div>
|
||||
|
||||
<div class='oe_pick_button js_pick_pack'> Put in Pack </div>
|
||||
<div class='oe_placeholder_picking_editor'></div>
|
||||
<div class='oe_placeholder_package_editor'></div>
|
||||
<div class='oe_placeholder_package_selector'></div>
|
||||
<div class='oe_placeholder_menu'></div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="container">
|
||||
<div class='oe_placeholder_picking_editor'/>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
|
|
|
@ -131,6 +131,7 @@ class stock_location(osv.osv):
|
|||
'scrap_location': fields.boolean('Is a Scrap Location?', help='Check this box to allow using this location to put scrapped/damaged goods.'),
|
||||
'removal_strategy_ids': fields.one2many('product.removal', 'location_id', 'Removal Strategies'),
|
||||
'putaway_strategy_ids': fields.one2many('product.putaway', 'location_id', 'Put Away Strategies'),
|
||||
'loc_barcode': fields.char('Location barcode'),
|
||||
}
|
||||
_defaults = {
|
||||
'active': True,
|
||||
|
@ -141,6 +142,12 @@ class stock_location(osv.osv):
|
|||
'posz': 0,
|
||||
'scrap_location': False,
|
||||
}
|
||||
_sql_constraints = [('loc_barcode_company_uniq', 'unique (loc_barcode,company_id)', 'The barcode for a location must be unique per company !')]
|
||||
|
||||
def create(self, cr, uid, default, context=None):
|
||||
if not default.get('loc_barcode', False):
|
||||
default.update({'loc_barcode': default.get('complete_name', False)})
|
||||
return super(stock_location,self).create(cr, uid, default, context=context)
|
||||
|
||||
def get_putaway_strategy(self, cr, uid, location, product, context=None):
|
||||
pa = self.pool.get('product.putaway')
|
||||
|
@ -1062,6 +1069,14 @@ class stock_picking(osv.osv):
|
|||
'product_uom_id': self.pool.get("product.product").browse(cr, uid, key[0], context=context).uom_id.id,
|
||||
})
|
||||
return vals
|
||||
|
||||
def open_barcode_interface(self, cr, uid, picking_ids, context=None):
|
||||
final_url="/barcode/web/#action=stock.ui&picking_id="+str(picking_ids[0])
|
||||
return {'type': 'ir.actions.act_url', 'url':final_url, 'target': 'self',}
|
||||
|
||||
def do_partial_open_barcode(self, cr, uid, picking_ids, context=None):
|
||||
self.do_prepare_partial(cr, uid, picking_ids, context=context)
|
||||
return self.open_barcode_interface(cr, uid, picking_ids, context=context)
|
||||
|
||||
def do_prepare_partial(self, cr, uid, picking_ids, context=None):
|
||||
context = context or {}
|
||||
|
@ -1325,50 +1340,83 @@ class stock_picking(osv.osv):
|
|||
|
||||
def action_done_from_ui(self, cr, uid, picking_id, context=None):
|
||||
""" called when button 'done' is pushed in the barcode scanner UI """
|
||||
#write qty_done into field product_qty for every package_operation before doing the transfer
|
||||
pack_op_obj = self.pool.get('stock.pack.operation')
|
||||
for operation in self.browse(cr, uid, picking_id, context=context).pack_operation_ids:
|
||||
pack_op_obj.write(cr, uid, operation.id, {'product_qty': operation.qty_done}, context=context)
|
||||
self.do_transfer(cr, uid, [picking_id], context=context)
|
||||
#return id of next picking to work on
|
||||
return self.get_next_picking_for_ui(cr, uid, context=context)
|
||||
|
||||
def action_pack(self, cr, uid, picking_ids, context=None):
|
||||
def action_pack(self, cr, uid, picking_ids, operation_filter_ids=None, context=None):
|
||||
""" Create a package with the current pack_operation_ids of the picking that aren't yet in a pack.
|
||||
Used in the barcode scanner UI and the normal interface as well. """
|
||||
Used in the barcode scanner UI and the normal interface as well.
|
||||
operation_filter_ids is used by barcode scanner interface to specify a subset of operation to pack"""
|
||||
if operation_filter_ids == None:
|
||||
operation_filter_ids = []
|
||||
stock_operation_obj = self.pool.get('stock.pack.operation')
|
||||
package_obj = self.pool.get('stock.quant.package')
|
||||
stock_move_obj = self.pool.get('stock.move')
|
||||
for picking_id in picking_ids:
|
||||
operation_ids = stock_operation_obj.search(cr, uid, [('picking_id', '=', picking_id), ('result_package_id', '=', False)], context=context)
|
||||
operation_search_domain = [('picking_id', '=', picking_id), ('result_package_id', '=', False)]
|
||||
if operation_filter_ids != []:
|
||||
operation_search_domain.append(('id', 'in', operation_filter_ids))
|
||||
operation_ids = stock_operation_obj.search(cr, uid, operation_search_domain, context=context)
|
||||
pack_operation_ids = []
|
||||
if operation_ids:
|
||||
for operation in stock_operation_obj.browse(cr, uid, operation_ids, context=context):
|
||||
for record in operation.linked_move_operation_ids:
|
||||
stock_move_obj.check_tracking(cr, uid, record.move_id, operation.package_id.id or operation.lot_id.id, context=context)
|
||||
#If we haven't done all qty in operation, we have to split into 2 operation
|
||||
op = operation
|
||||
if (operation.qty_done < operation.product_qty):
|
||||
new_operation = stock_operation_obj.copy(cr, uid, operation.id, {'product_qty': operation.qty_done,'qty_done': operation.qty_done}, context=context)
|
||||
stock_operation_obj.write(cr, uid, operation.id, {'product_qty': operation.product_qty - operation.qty_done,'qty_done': 0}, context=context)
|
||||
op = stock_operation_obj.browse(cr, uid, new_operation, context=context)
|
||||
pack_operation_ids.append(op.id)
|
||||
for record in op.linked_move_operation_ids:
|
||||
stock_move_obj.check_tracking(cr, uid, record.move_id, op.package_id.id or op.lot_id.id, context=context)
|
||||
package_id = package_obj.create(cr, uid, {}, context=context)
|
||||
stock_operation_obj.write(cr, uid, operation_ids, {'result_package_id': package_id}, context=context)
|
||||
stock_operation_obj.write(cr, uid, pack_operation_ids, {'result_package_id': package_id}, context=context)
|
||||
return True
|
||||
|
||||
def process_product_id_from_ui(self, cr, uid, picking_id, product_id, context=None):
|
||||
return self.pool.get('stock.pack.operation')._search_and_increment(cr, uid, picking_id, [('product_id', '=', product_id)], context=context)
|
||||
def process_product_id_from_ui(self, cr, uid, picking_id, product_id, op_id, increment=True, context=None):
|
||||
return self.pool.get('stock.pack.operation')._search_and_increment(cr, uid, picking_id, [('product_id', '=', product_id),('id', '=', op_id)], increment=increment, context=context)
|
||||
|
||||
def process_barcode_from_ui(self, cr, uid, picking_id, barcode_str, context=None):
|
||||
def process_barcode_from_ui(self, cr, uid, picking_id, barcode_str, visible_op_ids, context=None):
|
||||
'''This function is called each time there barcode scanner reads an input'''
|
||||
lot_obj = self.pool.get('stock.production.lot')
|
||||
package_obj = self.pool.get('stock.quant.package')
|
||||
product_obj = self.pool.get('product.product')
|
||||
stock_operation_obj = self.pool.get('stock.pack.operation')
|
||||
stock_location_obj = self.pool.get('stock.location')
|
||||
answer = {'filter_loc': False, 'operation_id': False}
|
||||
#check if the barcode correspond to a location
|
||||
matching_location_ids = stock_location_obj.search(cr, uid, [('loc_barcode', '=', barcode_str)], context=context)
|
||||
if matching_location_ids:
|
||||
#if we have a location, return immediatly with the location name
|
||||
location = stock_location_obj.browse(cr, uid, matching_location_ids[0], context=None)
|
||||
answer['filter_loc'] = stock_location_obj._name_get(cr, uid, location, context=None)
|
||||
answer['filter_loc_id'] = matching_location_ids[0]
|
||||
return answer
|
||||
#check if the barcode correspond to a product
|
||||
matching_product_ids = product_obj.search(cr, uid, ['|', ('ean13', '=', barcode_str), ('default_code', '=', barcode_str)], context=context)
|
||||
if matching_product_ids:
|
||||
self.process_product_id_from_ui(cr, uid, picking_id, matching_product_ids[0], context=context)
|
||||
|
||||
op_id = stock_operation_obj._search_and_increment(cr, uid, picking_id, [('product_id', '=', matching_product_ids[0])], filter_visible=True, visible_op_ids=visible_op_ids, increment=True, context=context)
|
||||
answer['operation_id'] = op_id
|
||||
return answer
|
||||
#check if the barcode correspond to a lot
|
||||
matching_lot_ids = lot_obj.search(cr, uid, [('name', '=', barcode_str)], context=context)
|
||||
if matching_lot_ids:
|
||||
lot = lot_obj.browse(cr, uid, matching_lot_ids[0], context=context)
|
||||
stock_operation_obj._search_and_increment(cr, uid, picking_id, [('product_id', '=', lot.product_id.id), ('lot_id', '=', lot.id)], context=context)
|
||||
|
||||
op_id = stock_operation_obj._search_and_increment(cr, uid, picking_id, [('product_id', '=', lot.product_id.id), ('lot_id', '=', lot.id)], filter_visible=True, visible_op_ids=visible_op_ids, increment=True, context=context)
|
||||
answer['operation_id'] = op_id
|
||||
return answer
|
||||
#check if the barcode correspond to a package
|
||||
matching_package_ids = package_obj.search(cr, uid, [('name', '=', barcode_str)], context=context)
|
||||
if matching_package_ids:
|
||||
stock_operation_obj._search_and_increment(cr, uid, picking_id, [('package_id', '=', matching_package_ids[0])], context=context)
|
||||
op_id = stock_operation_obj._search_and_increment(cr, uid, picking_id, [('package_id', '=', matching_package_ids[0])], filter_visible=True, visible_op_ids=visible_op_ids, increment=True, context=context)
|
||||
answer['operation_id'] = op_id
|
||||
return answer
|
||||
return answer
|
||||
|
||||
|
||||
class stock_production_lot(osv.osv):
|
||||
|
@ -3616,6 +3664,7 @@ class stock_pack_operation(osv.osv):
|
|||
'product_id': fields.many2one('product.product', 'Product', ondelete="CASCADE"), # 1
|
||||
'product_uom_id': fields.many2one('product.uom', 'Product Unit of Measure'),
|
||||
'product_qty': fields.float('Quantity', digits_compute=dp.get_precision('Product Unit of Measure'), required=True),
|
||||
'qty_done': fields.float('Quantity Processed', digits_compute=dp.get_precision('Product Unit of Measure')),
|
||||
'package_id': fields.many2one('stock.quant.package', 'Package'), # 2
|
||||
'lot_id': fields.many2one('stock.production.lot', 'Lot/Serial Number'),
|
||||
'result_package_id': fields.many2one('stock.quant.package', 'Container Package', help="If set, the operations are packed into this package", required=False, ondelete='cascade'),
|
||||
|
@ -3628,10 +3677,13 @@ class stock_pack_operation(osv.osv):
|
|||
'remaining_qty': fields.function(_get_remaining_qty, type='float', string='Remaining Qty'),
|
||||
'location_id': fields.many2one('stock.location', 'Location From', required=True),
|
||||
'location_dest_id': fields.many2one('stock.location', 'Location To', required=True),
|
||||
'processed': fields.selection([('true','Yes'), ('false','No')],'Has been processed?', required=True),
|
||||
}
|
||||
|
||||
_defaults = {
|
||||
'date': fields.date.context_today,
|
||||
'qty_done': 0,
|
||||
'processed': lambda *a: 'false',
|
||||
}
|
||||
|
||||
def write(self, cr, uid, ids, vals, context=None):
|
||||
|
@ -3651,8 +3703,32 @@ class stock_pack_operation(osv.osv):
|
|||
self.pool.get("stock.picking").do_recompute_remaining_quantities(cr, uid, [vals['picking_id']], context=context)
|
||||
return res_id
|
||||
|
||||
def action_drop_down(self, cr, uid, ids, context=None):
|
||||
''' Used by barcode interface to say that pack_operation has been moved from src location
|
||||
to destination location, if qty_done is less than product_qty than we have to split the
|
||||
operation in two to process the one with the qty moved
|
||||
'''
|
||||
processed_ids = []
|
||||
for pack_op in self.browse(cr, uid, ids, context=None):
|
||||
op = pack_op.id
|
||||
if pack_op.qty_done < pack_op.product_qty:
|
||||
# we split the operation in two
|
||||
op = self.copy(cr, uid, pack_op.id, {'product_qty': pack_op.qty_done, 'qty_done': pack_op.qty_done}, context=context)
|
||||
self.write(cr, uid, ids, {'product_qty': pack_op.product_qty - pack_op.qty_done, 'qty_done': 0}, context=context)
|
||||
processed_ids.append(op)
|
||||
self.write(cr, uid, processed_ids, {'processed': 'true'}, context=context)
|
||||
|
||||
def create_and_assign_lot(self, cr, uid, id, context=None):
|
||||
''' Used by barcode interface to create a new lot and assign it to the operation
|
||||
'''
|
||||
obj = self.browse(cr,uid,id,context)
|
||||
product_id = obj.product_id.id
|
||||
if not obj.lot_id:
|
||||
new_lot_id = self.pool.get('stock.production.lot').create(cr, uid, {'product_id': product_id}, context=context)
|
||||
self.write(cr, uid, id, {'lot_id': new_lot_id}, context=context)
|
||||
|
||||
#TODO: this function can be refactored
|
||||
def _search_and_increment(self, cr, uid, picking_id, domain, context=None):
|
||||
def _search_and_increment(self, cr, uid, picking_id, domain, filter_visible=False ,visible_op_ids=False, increment=True, context=None):
|
||||
'''Search for an operation with given 'domain' in a picking, if it exists increment the qty (+1) otherwise create it
|
||||
|
||||
:param domain: list of tuple directly reusable as a domain
|
||||
|
@ -3670,16 +3746,32 @@ class stock_pack_operation(osv.osv):
|
|||
#if current_package_id is given in the context, we increase the number of items in this package
|
||||
package_clause = [('result_package_id', '=', context.get('current_package_id', False))]
|
||||
existing_operation_ids = self.search(cr, uid, [('picking_id', '=', picking_id)] + domain + package_clause, context=context)
|
||||
todo_operation_ids = []
|
||||
if existing_operation_ids:
|
||||
if filter_visible:
|
||||
todo_operation_ids = [val for val in existing_operation_ids if val in visible_op_ids]
|
||||
else:
|
||||
todo_operation_ids = existing_operation_ids
|
||||
if todo_operation_ids:
|
||||
#existing operation found for the given domain and picking => increment its quantity
|
||||
operation_id = existing_operation_ids[0]
|
||||
qty = self.browse(cr, uid, operation_id, context=context).product_qty + 1
|
||||
self.write(cr, uid, [operation_id], {'product_qty': qty}, context=context)
|
||||
operation_id = todo_operation_ids[0]
|
||||
op_obj = self.browse(cr, uid, operation_id, context=context)
|
||||
qty = op_obj.qty_done
|
||||
if increment:
|
||||
qty += 1
|
||||
else:
|
||||
qty -= 1 if qty >= 1 else 0
|
||||
if qty == 0 and op_obj.product_qty == 0:
|
||||
#we have a line with 0 qty set, so delete it
|
||||
self.unlink(cr, uid, [operation_id], context=context)
|
||||
return False
|
||||
self.write(cr, uid, [operation_id], {'qty_done': qty}, context=context)
|
||||
else:
|
||||
#no existing operation found for the given domain and picking => create a new one
|
||||
values = {
|
||||
'picking_id': picking_id,
|
||||
'product_qty': 1,
|
||||
'product_qty': 0,
|
||||
'qty_done': 1,
|
||||
}
|
||||
for key in domain:
|
||||
var_name, dummy, value = key
|
||||
|
@ -3691,7 +3783,7 @@ class stock_pack_operation(osv.osv):
|
|||
update_dict['product_uom_id'] = uom_id
|
||||
values.update(update_dict)
|
||||
operation_id = self.create(cr, uid, values, context=context)
|
||||
return True
|
||||
return operation_id
|
||||
|
||||
|
||||
class stock_move_operation_link(osv.osv):
|
||||
|
@ -3857,6 +3949,11 @@ class stock_picking_type(osv.osv):
|
|||
_description = "The picking type determines the picking view"
|
||||
_order = 'sequence'
|
||||
|
||||
def open_barcode_interface(self, cr, uid, ids, context=None):
|
||||
final_url="/barcode/web/#action=stock.ui&picking_type_id="+str(ids[0]) if len(ids) else '0'
|
||||
return {'type': 'ir.actions.act_url', 'url':final_url, 'target': 'self',}
|
||||
|
||||
|
||||
def _get_tristate_values(self, cr, uid, ids, field_name, arg, context=None):
|
||||
picking_obj = self.pool.get('stock.picking')
|
||||
res = dict.fromkeys(ids, [])
|
||||
|
|
|
@ -375,6 +375,7 @@
|
|||
<field name="posx"/>
|
||||
<field name="posy"/>
|
||||
<field name="posz"/>
|
||||
<field name="loc_barcode"/>
|
||||
</group>
|
||||
</group>
|
||||
<separator string="Removal Strategies" groups="stock.group_adv_location"/>
|
||||
|
@ -721,9 +722,9 @@
|
|||
<button name="action_confirm" states="draft" string="Mark as Todo" type="object" class="oe_highlight" groups="base.group_user"/>
|
||||
<button name="action_assign" states="confirmed" string="Check Availability" type="object" class="oe_highlight" groups="base.group_user"/>
|
||||
<button name="force_assign" states="confirmed,partially_available" string="Force Availability" type="object" class="oe_highlight" groups="base.group_user"/>
|
||||
<button name="do_transfer" states="assigned" string="Transfer" groups="stock.group_stock_user" type="object" class="oe_highlight"/>
|
||||
<button name="do_transfer" string="Transfer" groups="stock.group_stock_user" type="object" class="oe_highlight" attrs="{'invisible': ['|',('pack_operation_exist', '=', False), ('state', '!=', 'partially_available')]}"/>
|
||||
<button name="do_prepare_partial" string="Enter Transfer Details" groups="stock.group_stock_user" type="object" class="oe_highlight" attrs="{'invisible': ['|',('pack_operation_exist', '=', True),('state','not in',('assigned', 'partially_available'))]}"/>
|
||||
<button name="do_transfer" states="assigned" string="Transfer" groups="stock.group_stock_user" type="object" class="oe_highlight" attrs="{'invisible': ['|', ('pack_operation_exist', '=', True)]}"/>
|
||||
<button name="do_partial_open_barcode" string="Enter Transfer Details" groups="stock.group_stock_user" type="object" class="oe_highlight" attrs="{'invisible': ['|',('pack_operation_exist', '=', True),('state','not in',('assigned', 'partially_available'))]}"/>
|
||||
<button name="open_barcode_interface" string="Open Barcode interface" groups="stock.group_stock_user" type="object" class="oe_highlight" attrs="{'invisible': ['|',('pack_operation_exist', '=', False),('state','not in',('assigned', 'partially_available'))]}"/>
|
||||
<button name="do_print_delivery" string="Print Delivery Order" groups="stock.group_stock_user" type="object" attrs="{'invisible': ['|', ('picking_type_code', '!=', 'outgoing'), ('state', '!=', 'done')]}"/>
|
||||
<button name="do_print_picking" string="Print Picking List" groups="stock.group_stock_user" type="object" attrs="{'invisible': ['|', ('picking_type_code', '=', 'outgoing'), ('state', '!=', 'assigned')]}"/>
|
||||
<button name="%(act_stock_return_picking)d" string="Reverse Transfer" states="done" type="action" groups="base.group_user"/>
|
||||
|
@ -757,6 +758,7 @@
|
|||
<notebook>
|
||||
<page string="Products">
|
||||
<separator string="Stock Moves" attrs="{'invisible': [('pack_operation_exist', '=', False)]}"/>
|
||||
<!--
|
||||
<field name="move_lines" context="{'address_in_id': partner_id, 'form_view_ref':'stock.view_move_picking_form', 'tree_view_ref':'stock.view_move_picking_tree', 'default_picking_type_id': picking_type_id,'default_picking_id': active_id}"/>
|
||||
<group>
|
||||
<group col="3">
|
||||
|
@ -782,6 +784,9 @@
|
|||
<field name="result_package_id" groups="stock.group_tracking_lot"/>
|
||||
</tree>
|
||||
</field>
|
||||
-->
|
||||
<field name="move_lines" context="{'address_in_id': partner_id, 'form_view_ref':'stock.view_move_picking_form', 'tree_view_ref':'view_move_picking_tree', 'default_picking_type_id': picking_type_id,'default_picking_id': active_id}"/>
|
||||
<field name="pack_operation_exist" invisible="1"/>
|
||||
<field name="note" placeholder="Add an internal note..." class="oe_inline"/>
|
||||
</page>
|
||||
<page string="Additional Info">
|
||||
|
@ -1249,12 +1254,6 @@
|
|||
|
||||
<menuitem action="action_move_form2" id="menu_action_move_form2" parent="menu_traceability" sequence="3" groups="stock.group_locations"/>
|
||||
|
||||
<record id="action_stock_stock_ui" model="ir.actions.client">
|
||||
<field name="name">Stock picking</field>
|
||||
<field name="tag">stock.ui</field>
|
||||
<field name="res_model">stock</field>
|
||||
</record>
|
||||
|
||||
<!--
|
||||
Reception Picking (By Stock Move)
|
||||
From stock_partial_move_view
|
||||
|
@ -1465,7 +1464,7 @@
|
|||
<div class="oe_kanban_content">
|
||||
<h4 class="oe_center"><field name="complete_name"/></h4>
|
||||
<div class="oe_right">
|
||||
<a name="%(action_stock_stock_ui)d" type="action">
|
||||
<a name="open_barcode_interface" type="object">
|
||||
<img src="/stock/static/src/img/scan.png"
|
||||
alt="Click to launch the barcode interface"
|
||||
class="oe_stock_scan_image" title="Click to launch the barcode interface"/>
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
<field name="model">stock.picking</field>
|
||||
<field name="inherit_id" ref="stock.view_picking_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//button[@name='do_prepare_partial']" position="after">
|
||||
<xpath expr="//button[@name='do_partial_open_barcode']" position="after">
|
||||
<button name="%(action_stock_invoice_onshipping)d" string="Create Invoice" attrs="{'invisible': ['|',('state','<>','done'),('invoice_state','<>','2binvoiced')]}" type="action" class="oe_highlight" groups="base.group_user"/>
|
||||
<button name="%(action_stock_invoice_onshipping)d" string="Refund Invoice" attrs="{'invisible': ['|',('state','<>','done'),('invoice_state','<>','invoiced')]}" type="action" class="oe_highlight" groups="base.group_user" context="{'inv_type': 'out_refund'}"/>
|
||||
</xpath>
|
||||
|
|
Loading…
Reference in New Issue