Merge from nse back-end partial picking wizard

This commit is contained in:
Josse Colpaert 2014-08-20 18:28:57 +02:00
parent b8b947ffa4
commit 3891892d9f
8 changed files with 279 additions and 20 deletions

View File

@ -75,6 +75,7 @@ Dashboard / Reports for Warehouse Management will include:
'wizard/stock_return_picking_view.xml',
'wizard/make_procurement_view.xml',
'wizard/orderpoint_procurement_view.xml',
'wizard/stock_transfer_details.xml',
'stock_incoterms.xml',
'stock_report.xml',
'stock_view.xml',
@ -86,7 +87,7 @@ Dashboard / Reports for Warehouse Management will include:
'views/report_package_barcode.xml',
'views/report_lot_barcode.xml',
'views/report_location_barcode.xml',
'views/report_stockpicking.xml',
'views/report_stockpicking.xml',
'views/report_stockinventory.xml',
'views/stock.xml',
],

View File

@ -106,10 +106,10 @@
}
.openerp .oe_stock_scan_image {
opacity: 0.2;
margin: 0 5px 0;
margin: 0 5px 0;
}
.openerp .oe_stock_scan_image:hover {
opacity: 1
opacity: 1
}
.oe_gauge_labels {
width: 100%;
@ -120,3 +120,12 @@
display: inline-block;
margin: -7px;
}
.oe_stock_scan_image_btn {
/*height : 42px;*/
}
.oe_stock_scan_button {
border: none !important;
background: none !important;
box-shadow: none !important;
}

View File

@ -108,7 +108,7 @@
<div class="col-md-6 col-sm-4 col-xs-12 text-right">
<h3>
<button type="button" class='btn btn-default js_pick_pack js_putinpack'> Put in Pack </button>
<button type="button" class='btn btn-danger js_drop_down'> Process </button>
<button type="button" class='btn btn-danger js_drop_down fa fa-download'> Put in Cart </button>
</h3>
</div>
</div>

View File

@ -1279,6 +1279,21 @@ class stock_picking(osv.osv):
stock_move_obj.do_unreserve(cr, uid, move_ids, context=context)
stock_move_obj.action_assign(cr, uid, move_ids, context=context)
@api.cr_uid_ids_context
def do_enter_transfer_details(self, cr, uid, picking, context=None):
if not context:
context = {}
context.update({
'active_model': self._name,
'active_ids': picking,
'active_id': len(picking) and picking[0] or False
})
created_id = self.pool['stock.transfer_details'].create(cr, uid, {'picking_id': len(picking) and picking[0] or False}, context)
return self.pool['stock.transfer_details'].wizard_view(cr, uid, created_id, context)
@api.cr_uid_ids_context
def do_transfer(self, cr, uid, picking_ids, context=None):
"""
@ -1298,10 +1313,10 @@ class stock_picking(osv.osv):
todo_move_ids = []
if not all_op_processed:
todo_move_ids += self._create_extra_moves(cr, uid, picking, context=context)
picking.refresh()
#split move lines eventually
toassign_move_ids = []
for move in picking.move_lines:
remaining_qty = move.remaining_qty
@ -3749,9 +3764,9 @@ class stock_pack_operation(osv.osv):
'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
'package_id': fields.many2one('stock.quant.package', 'Source 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'),
'result_package_id': fields.many2one('stock.quant.package', 'Destination Package', help="If set, the operations are packed into this package", required=False, ondelete='cascade'),
'date': fields.datetime('Date', required=True),
'owner_id': fields.many2one('res.partner', 'Owner', help="Owner of the quants"),
#'update_cost': fields.boolean('Need cost update'),
@ -3759,8 +3774,8 @@ class stock_pack_operation(osv.osv):
'currency': fields.many2one('res.currency', string="Currency", help="Currency in which Unit cost is expressed", ondelete='CASCADE'),
'linked_move_operation_ids': fields.one2many('stock.move.operation.link', 'operation_id', string='Linked Moves', readonly=True, help='Moves impacted by this operation for the computation of the remaining quantities'),
'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),
'location_id': fields.many2one('stock.location', 'Source Location', required=True),
'location_dest_id': fields.many2one('stock.location', 'Destination Location', required=True),
'processed': fields.selection([('true','Yes'), ('false','No')],'Has been processed?', required=True),
}

View File

@ -634,9 +634,7 @@
<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,partially_available" string="Check Availability" type="object" class="oe_highlight" groups="base.group_user"/>
<button name="force_assign" states="confirmed,waiting,partially_available" string="Force Availability" type="object" groups="base.group_user"/>
<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_enter_transfer_details" states="assigned,partially_available" string="Transfer" groups="stock.group_stock_user" type="object" class="oe_highlight"/>
<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"/>
<button name="action_cancel" states="assigned,confirmed,partially_available,draft" string="Cancel Transfer" groups="base.group_user" type="object"/>
@ -645,6 +643,8 @@
</header>
<sheet>
<div class="oe_right oe_button_box">
<button name="do_partial_open_barcode" groups="stock.group_stock_user" type="object" class="oe_stock_scan_button" attrs="{'invisible': ['|',('pack_operation_exist', '=', True),('state','not in',('assigned', 'partially_available'))]}"><img src="/stock/static/src/img/scan.png" class="oe_stock_scan_image oe_stock_scan_image_btn"/></button>
<button name="open_barcode_interface" groups="stock.group_stock_user" type="object" class="oe_stock_scan_button" attrs="{'invisible': ['|',('pack_operation_exist', '=', False),('state','not in',('assigned', 'partially_available'))]}"><img src="/stock/static/src/img/scan.png" class="oe_stock_scan_image oe_stock_scan_image_btn"/></button>
</div>
<h1>
<field name="name" class="oe_inline" attrs="{'invisible': [('name','=','/')]}" readonly="1"/>
@ -673,20 +673,22 @@
<field name="pack_operation_exist" invisible="1"/>
<field name="note" placeholder="Add an internal note..." class="oe_inline"/>
</page>
<page string="Operations Done" attrs="{'invisible': ['|', ('state','!=','done'), ('pack_operation_ids','=',[])]}">
<page string="Operations" attrs="{'invisible': ['|', ('state','!=','done'), ('pack_operation_ids','=',[])]}">
<field name="pack_operation_ids">
<tree editable="top">
<field name="location_id"/>
<tree editable="top">
<field name="package_id" groups="stock.group_tracking_lot"/>
<field name="product_id"/>
<field name="product_uom_id" groups="product.group_uom"/>
<field name="lot_id" domain="[('product_id','=?', product_id)]" context="{'product_id': product_id}" groups="stock.group_production_lot"/>
<field name="package_id" groups="stock.group_tracking_lot"/>
<field name="picking_id" invisible="1"/>
<field name="owner_id" groups="stock.group_tracking_owner"/>
<field name="product_qty" attrs="{'required': [('product_id', '!=', False)]}"/>
<field name="location_id"/>
<field name="location_dest_id"/>
<field name="result_package_id" groups="stock.group_tracking_lot"/>
</tree>
</field>
<label class="oe_grey" string="Setting a product and a source package will result in a partial unpack of the source package (products will be taken out from that package). Set a source package without any product to move it as a whole."/>
</page>
<page string="Additional Info">
<group string="General Informations">
@ -1338,7 +1340,7 @@
<h4 class="text-center"><strong><field name="complete_name"/></strong></h4>
<div class="oe_right">
<a name="open_barcode_interface" type="object">
<img src="/stock/static/src/img/scan.png"
<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"/>
</a>
@ -1911,7 +1913,7 @@
<field name="arch" type="xml">
<xpath expr="//div[@name='button_box']" position="inside">
<button name="%(do_view_pickings)d" string="Pickings" type="action"/>
</xpath>
</xpath>
</field>
</record>

View File

@ -24,4 +24,4 @@ import stock_return_picking
import stock_change_product_qty
import make_procurement_product
import orderpoint_procurement
import stock_transfer_details

View File

@ -0,0 +1,183 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-TODAY OpenERP S.A. <http://www.odoo.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp import models, fields, api
from openerp.tools.translate import _
import openerp.addons.decimal_precision as dp
from datetime import datetime
class stock_transfer_details(models.TransientModel):
_name = 'stock.transfer_details'
_description = 'Picking wizard'
picking_id = fields.Many2one('stock.picking', 'Picking')
item_ids = fields.One2many('stock.transfer_details_items', 'transfer_id', 'Items', domain=[('product_id', '!=', False)])
packop_ids = fields.One2many('stock.transfer_details_items', 'transfer_id', 'Packs', domain=[('product_id', '=', False)])
picking_source_location_id = fields.Many2one('stock.location', string="Head source location", related='picking_id.location_id', store=False, readonly=True)
picking_destination_location_id = fields.Many2one('stock.location', string="Head destination location", related='picking_id.location_dest_id', store=False, readonly=True)
def default_get(self, cr, uid, fields, context=None):
if context is None: context = {}
res = super(stock_transfer_details, self).default_get(cr, uid, fields, context=context)
picking_ids = context.get('active_ids', [])
active_model = context.get('active_model')
if not picking_ids or len(picking_ids) != 1:
# Partial Picking Processing may only be done for one picking at a time
return res
assert active_model in ('stock.picking'), 'Bad context propagation'
picking_id, = picking_ids
picking = self.pool.get('stock.picking').browse(cr, uid, picking_id, context=context)
items = []
packs = []
if not picking.pack_operation_ids:
picking.do_prepare_partial()
for op in picking.pack_operation_ids:
item = {
'packop_id': op.id,
'product_id': op.product_id.id,
'product_uom_id': op.product_uom_id.id,
'quantity': op.product_qty,
'package_id': op.package_id.id,
'lot_id': op.lot_id.id,
'sourceloc_id': op.location_id.id,
'destinationloc_id': op.location_dest_id.id,
'result_package_id': op.result_package_id.id,
'date': op.date,
'owner_id': op.owner_id.id,
}
if op.product_id:
items.append(item)
elif op.package_id:
packs.append(item)
res.update(item_ids=items)
res.update(packop_ids=packs)
return res
@api.one
def do_detailed_transfer(self):
processed_ids = []
# Create new and update existing pack operations
for lstits in [self.item_ids, self.packop_ids]:
for prod in lstits:
pack_datas = {
'product_id': prod.product_id.id,
'product_uom_id': prod.product_uom_id.id,
'product_qty': prod.quantity,
'package_id': prod.package_id.id,
'lot_id': prod.lot_id.id,
'location_id': prod.sourceloc_id.id,
'location_dest_id': prod.destinationloc_id.id,
'result_package_id': prod.result_package_id.id,
'date': prod.date if prod.date else datetime.now(),
'owner_id': prod.owner_id.id,
}
if prod.packop_id:
prod.packop_id.write(pack_datas)
processed_ids.append(prod.packop_id.id)
else:
pack_datas['picking_id'] = self.picking_id.id
packop_id = self.env['stock.pack.operation'].create(pack_datas)
processed_ids.append(packop_id.id)
# Delete the others
packops = self.env['stock.pack.operation'].search(['&', ('picking_id', '=', self.picking_id.id), '!', ('id', 'in', processed_ids)])
for packop in packops:
packop.unlink()
# Execute the transfer of the picking
self.picking_id.do_transfer()
return True
@api.multi
def wizard_view(self):
view = self.env.ref('stock.view_stock_enter_transfer_details')
return {
'name': _('Enter transfer details'),
'type': 'ir.actions.act_window',
'view_type': 'form',
'view_mode': 'form',
'res_model': 'stock.transfer_details',
'views': [(view.id, 'form')],
'view_id': view.id,
'target': 'new',
'res_id': self.ids[0],
'context': self.env.context,
}
class stock_transfer_details_items(models.TransientModel):
_name = 'stock.transfer_details_items'
_description = 'Picking wizard items'
transfer_id = fields.Many2one('stock.transfer_details', 'Transfer')
packop_id = fields.Many2one('stock.pack.operation', 'Operation')
product_id = fields.Many2one('product.product', 'Product')
product_uom_id = fields.Many2one('product.uom', 'Product Unit of Measure')
quantity = fields.Float('Quantity', digits_compute=dp.get_precision('Product Unit of Measure'), default = 1.0)
package_id = fields.Many2one('stock.quant.package', 'Source package', domain="['|', ('location_id', 'child_of', sourceloc_id), ('location_id','=',False)]")
lot_id = fields.Many2one('stock.production.lot', 'Lot/Serial Number')
sourceloc_id = fields.Many2one('stock.location', 'Source Location', required=True)
destinationloc_id = fields.Many2one('stock.location', 'Destination Location', required=True)
result_package_id = fields.Many2one('stock.quant.package', 'Destination package', domain="['|', ('location_id', 'child_of', destinationloc_id), ('location_id','=',False)]")
date = fields.Datetime('Date')
owner_id = fields.Many2one('res.partner', 'Owner', help="Owner of the quants")
@api.multi
def split_quantities(self):
for det in self:
if det.quantity>1:
det.quantity = (det.quantity-1)
new_id = det.copy(context=self.env.context)
new_id.quantity = 1
new_id.packop_id = False
if self and self[0]:
return self[0].transfer_id.wizard_view()
@api.multi
def put_in_pack(self):
newpack = None
for packop in self:
if not packop.result_package_id:
if not newpack:
newpack = self.pool['stock.quant.package'].create(self._cr, self._uid, {'location_id': packop.destinationloc_id.id if packop.destinationloc_id else False}, self._context)
packop.result_package_id = newpack
if self and self[0]:
return self[0].transfer_id.wizard_view()
@api.multi
def product_id_change(self, product, uom=False):
result = {}
if product:
prod = self.env['product.product'].browse(product)
result['product_uom_id'] = prod.uom_id and prod.uom_id.id
return {'value': result, 'domain': {}, 'warning':{} }
@api.multi
def source_package_change(self, sourcepackage):
result = {}
if sourcepackage:
pack = self.env['stock.quant.package'].browse(sourcepackage)
result['sourceloc_id'] = pack.location_id and pack.location_id.id
return {'value': result, 'domain': {}, 'warning':{} }

View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<openerp>
<data>
<record id="view_stock_enter_transfer_details" model="ir.ui.view">
<field name="name">Enter transfer details</field>
<field name="model">stock.transfer_details</field>
<field name="arch" type="xml">
<form string="Transfer details" version="7">
<field name="picking_source_location_id" invisible="True"/>
<field name="picking_destination_location_id" invisible="True"/>
<field name="item_ids"
context="{'default_sourceloc_id':picking_source_location_id,
'default_destinationloc_id':picking_destination_location_id}">
<tree string="Inventory Details" editable="bottom" >
<field name="package_id" groups="stock.group_tracking_lot"/>
<field name="product_id" required="True" context="{'uom':product_uom_id}" on_change="product_id_change(product_id,product_uom_id,context)"/>
<field name="quantity"/>
<button name="split_quantities" string="Split" type="object" icon="STOCK_PREFERENCES" attrs="{'invisible': [('quantity', '=', 1)]}"/>
<field name="product_uom_id" options="{&quot;no_open&quot;: True}" groups="product.group_uom"/>
<field name="sourceloc_id" domain="[('id', 'child_of', parent.picking_source_location_id)]"/>
<field name="destinationloc_id" domain="[('id', 'child_of', parent.picking_destination_location_id)]"/>
<field name="result_package_id" groups="stock.group_tracking_lot" context="{'location_id': destinationloc_id}"/>
<button name="put_in_pack" string="Pack" type="object" icon="terp-product" attrs="{'invisible': [('result_package_id', '!=', False)]}" groups="stock.group_tracking_lot"/>
<field name="lot_id" groups="stock.group_production_lot" domain="[('product_id','=?', product_id)]" context="{'product_id': product_id}"/>
</tree>
</field>
<label class="oe_grey" string="Setting a product and a source package will result in a partial unpack of the source package (products will be taken out from that package)."/>
<field name="packop_ids" groups="stock.group_tracking_lot"
context="{'default_sourceloc_id':picking_source_location_id,
'default_destinationloc_id':picking_destination_location_id}">
<tree editable="bottom">
<field name="package_id" required="True" on_change="source_package_change(package_id)"/>
<field name="sourceloc_id" domain="[('id', 'child_of', parent.picking_source_location_id)]"/>
<field name="destinationloc_id" domain="[('id', 'child_of', parent.picking_destination_location_id)]"/>
<field name="result_package_id"/>
<button name="put_in_pack" string="Pack" type="object" icon="terp-product" attrs="{'invisible': [('result_package_id', '!=', False)]}"/>
</tree>
</field>
<footer>
<button name="do_detailed_transfer" string="_Apply" type="object" class="oe_highlight"/>
or
<button string="_Cancel" class="oe_link" special="cancel" />
</footer>
</form>
</field>
</record>
</data>
</openerp>