[IMP]: mrp: Manufacturing Order Usability Improvement: View improvement, Separated move_lines into 2, one for product to consume and the other for consumed products + wizard related changes

bzr revid: rpa@openerp.co.in-20100219105543-rc1jxi0lrimfk21i
This commit is contained in:
rpa (Open ERP) 2010-02-19 16:25:43 +05:30
parent 819f2a33fe
commit 0abe371437
8 changed files with 306 additions and 182 deletions

View File

@ -60,6 +60,7 @@
'security/ir.model.access.csv',
'mrp_workflow.xml',
'mrp_data.xml',
'wizard/wizard_split_in_lots.xml',
'mrp_view.xml',
'mrp_wizard.xml',
'mrp_report.xml',

View File

@ -19,14 +19,13 @@
#
##############################################################################
from mx import DateTime
from osv import fields
from osv import osv
from tools.translate import _
import ir
import netsvc
import time
from mx import DateTime
from tools.translate import _
#----------------------------------------------------------
# Work Centers
@ -334,6 +333,45 @@ def rounding(f, r):
return f
return round(f / r) * r
class many2many_domain(fields.many2many):
def set(self, cr, obj, id, name, values, user=None, context=None):
if not values:
return
return super(many2many_domain, self).set(cr, obj, id, name, values, user=user,
context=context)
def get(self, cr, obj, ids, name, user=None, offset=0, context=None, values=None):
if not context:
context = {}
res = {}
move_obj = obj.pool.get('stock.move')
for prod in obj.browse(cr, user, ids, context=context):
cr.execute("SELECT move_id from mrp_production_move_ids where\
production_id=%s" % (prod.id))
m_ids = map(lambda x: x[0], cr.fetchall())
final = move_obj.search(cr, user, self._domain + [('id', 'in', tuple(m_ids))])
res[prod.id] = final
return res
class one2many_domain(fields.one2many):
def set(self, cr, obj, id, field, values, user=None, context=None):
if not values:
return
return super(one2many_domain, self).set(cr, obj, id, field, values,
user=user, context=context)
def get(self, cr, obj, ids, name, user=None, offset=0, context=None, values=None):
if not context:
context = {}
res = {}
move_obj = obj.pool.get('stock.move')
for prod in obj.browse(cr, user, ids, context=context):
cr.execute("SELECT id from stock_move where production_id=%s" % (prod.id))
m_ids = map(lambda x: x[0], cr.fetchall())
final = move_obj.search(cr, user, self._domain + [('id', 'in', tuple(m_ids))])
res[prod.id] = final
return res
class mrp_production(osv.osv):
_name = 'mrp.production'
_description = 'Production'
@ -420,9 +458,10 @@ class mrp_production(osv.osv):
'picking_id': fields.many2one('stock.picking', 'Picking list', readonly=True,
help="This is the internal picking list that brings the finished product to the production plan"),
'move_prod_id': fields.many2one('stock.move', 'Move product', readonly=True),
'move_lines': fields.many2many('stock.move', 'mrp_production_move_ids', 'production_id', 'move_id', 'Products Consummed'),
'move_created_ids': fields.one2many('stock.move', 'production_id', 'Moves Created'),
'move_lines': many2many_domain('stock.move', 'mrp_production_move_ids', 'production_id', 'move_id', 'Products to Consumme', domain=[('state','not in', ('done', 'cancel'))]),
'move_lines2': many2many_domain('stock.move', 'mrp_production_move_ids', 'production_id', 'move_id', 'Consummed Products', domain=[('state','in', ('done', 'cancel'))]),
'move_created_ids': one2many_domain('stock.move', 'production_id', 'Moves Created', domain=[('state','not in', ('done', 'cancel'))]),
'move_created_ids2': one2many_domain('stock.move', 'production_id', 'Moves Created', domain=[('state','in', ('done', 'cancel'))]),
'product_lines': fields.one2many('mrp.production.product.line', 'production_id', 'Scheduled goods'),
'workcenter_lines': fields.one2many('mrp.production.workcenter.line', 'production_id', 'Work Centers Utilisation'),
'state': fields.selection([('draft','Draft'),('picking_except', 'Picking Exception'),('confirmed','Waiting Goods'),('ready','Ready to Produce'),('in_production','In Production'),('cancel','Cancelled'),('done','Done')],'State', readonly=True,
@ -541,6 +580,10 @@ class mrp_production(osv.osv):
self.pool.get('stock.move').write(cr, uid, [production.move_prod_id.id],
{'location_id':production.location_dest_id.id})
return True
def action_start(self, cr, uid, ids, context=None):
self.write(cr, uid, ids, {'state':'in_production'})
return True
#TODO Review materials in function in_prod and prod_end.
def action_production_end(self, cr, uid, ids):
@ -562,7 +605,7 @@ class mrp_production(osv.osv):
self.pool.get('stock.move').check_assign(cr, uid, new_moves)
self.pool.get('stock.move').action_done(cr, uid, new_moves)
self._costs_generate(cr, uid, production)
# self.pool.get('stock.move').action_done(cr, uid, move_ids)
self.pool.get('stock.move').action_done(cr, uid, move_ids)
self.write(cr, uid, ids, {'state': 'done'})
return True
@ -606,7 +649,7 @@ class mrp_production(osv.osv):
if not production.date_start:
self.write(cr, uid, [production.id],
{'date_start': time.strftime('%Y-%m-%d %H:%M:%S')})
self.pool.get('stock.move').action_done(cr, uid, move_ids)
# self.pool.get('stock.move').action_done(cr, uid, move_ids)
self.write(cr, uid, ids, {'state': 'in_production'})
return True
@ -1299,6 +1342,40 @@ class StockMove(osv.osv):
wf_service = netsvc.LocalService("workflow")
wf_service.trg_validate(uid, 'mrp.procurement', m, 'button_wait_done', cr)
return True
def _track_lines(self, cr, uid, ids, data, context=None):
production_ids = []
new_move = super(StockMove, self)._track_lines(cr, uid, ids, data, context=context)
if new_move:
production_obj = self.pool.get('mrp.production')
move_obj = self.pool.get('stock.move')
move = move_obj.browse(cr, uid, ids)
production_ids = production_obj.search(cr, uid, [('move_lines', 'in', [move.id])])
for new in new_move:
production_obj.write(cr, uid, production_ids, {'move_lines': [(4, new)]})
move_obj.action_done(cr, uid, [new])
return new_move
def split_moves(self, cr, uid, ids, datas, context=None):
new_move = super(StockMove, self).split_moves(cr, uid, ids, datas, context=context)
if new_move:
production_obj = self.pool.get('mrp.production')
move_obj = self.pool.get('stock.move')
move = move_obj.browse(cr, uid, ids)
production_ids = production_obj.search(cr, uid, [('move_lines', 'in', [move.id])])
production_obj.write(cr, uid, production_ids, {'move_lines': [(4, new_move)]})
return {}
def scrap_moves(self, cr, uid, ids, datas, context=None):
new_move = super(StockMove, self).scrap_moves(cr, uid, ids, datas, context=context)
if new_move:
production_obj = self.pool.get('mrp.production')
move_obj = self.pool.get('stock.move')
move = move_obj.browse(cr, uid, ids)
production_ids = production_obj.search(cr, uid, [('move_lines', 'in', [move.id])])
production_obj.write(cr, uid, production_ids, {'move_lines': [(4, new_move)]})
return {}
StockMove()

View File

@ -438,6 +438,7 @@
</graph>
</field>
</record>
<wizard
string="Change Product Qty."
model="mrp.production"
@ -445,54 +446,102 @@
id="mrp.wizard_change_production_qty"
keyword="client_action_multi"
multi="True"/>
<record id="mrp_production_form_view" model="ir.ui.view">
<field name="name">mrp.production.form</field>
<field name="model">mrp.production</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Manufacturing Orders">
<group colspan="4" col="7">
<field name="name" select="1"/>
<group colspan="4" col="6">
<field name="name" select="1" string="Reference"/>
<field name="date_planned" select="1"/>
<field name="origin" select="1"/>
<newline/>
<field name="product_id" on_change="product_id_change(product_id)" select="1"/>
<field name="product_id" on_change="product_id_change(product_id)" select="1"/>
<field name="product_qty"/>
<field name="product_uom"/>
<button type="action" name="%(mrp.wizard_change_production_qty)d" string="Change Qty" states="ready,confirmed,in_production" icon="gtk-ok"/>
<field name="product_uos_qty" groups="product.group_uos"/>
<field name="product_uos" groups="product.group_uos"/>
<field name="company_id" select="1" groups="base.group_multi_company" widget="selection"/>
<group colspan="2" col="3">
<field name="product_uom"/>
<!-- TODO Fix change qty accroding to move_lines and move_lines2 -->
<!-- <button type="action" name="%(mrp.wizard_change_production_qty)d" string="Change Qty" states="ready,confirmed,in_production" icon="gtk-ok"/>-->
</group>
<label string="" colspan="2"/>
<field name="product_uos_qty" groups="product.group_uos"/>
<group colspan="2" col="3" groups="product.group_uos">
<field name="product_uos"/>
<label string=""/>
</group>
</group>
<notebook colspan="4">
<page string="Consumed Products">
<field name="location_src_id" select="2" domain="[('usage','=','internal')]" on_change="location_id_change(location_src_id,location_dest_id)"/>
<field name="location_dest_id" domain="[('usage','=','internal')]"/>
<field name="bom_id" select="2" domain="[('product_id','=',product_id),('bom_id','=',False)]" on_change="bom_id_change(bom_id)"/>
<field name="routing_id" groups="base.group_extended" select="1"/>
<newline/>
<field colspan="4" name="move_lines" nolabel="1" widget="one2many_list">
<tree string="Moves">
<field name="product_id" select="1"/>
<field name="product_qty" select="1"/>
<field name="product_uom" select="1" string="UOM"/>
<field name="prodlot_id" select="1"/>
<field name="product_packaging" domain="[('product_id','=',product_id)]"/>
<field name="picking_id"/>
<field name="location_id" select="1"/>
<field name="location_dest_id" select="1"/>
<field name="date" select="1"/>
<field name="date_planned" select="1" string="Date"/>
<field name="location_src_id" select="2" domain="[('usage','=','internal')]" on_change="location_id_change(location_src_id,location_dest_id)"/>
<field name="location_dest_id" domain="[('usage','=','internal')]"/>
<separator string="" colspan="4"/>
</tree>
<field colspan="2" name="move_lines" nolabel="1" widget="one2many_list"
mode="tree,form" height="275" domain="[('state','&lt;&gt;', ('done', 'cancel'))]">
<tree string="Products to Consume">
<field name="product_id" />
<field name="product_qty" />
<field name="product_uom" />
<field name="state" invisible="1" />
<button
name="%(stock.move_consume)d"
string=">" type="action"
icon="gtk-go-forward" context="{'consume': True}"
states="draft,waiting,confirmed,assigned"/>
<button name="%(stock.move_scrap)d"
string="D" type="action"
icon="gtk-justify-fill" context="{'scrap': True}"
states="draft,waiting,confirmed,assigned" />
</tree>
<form string="Products to Consume">
<field name="product_id" />
<field name="product_qty" />
<field name="product_uom" />
</form>
</field>
<field colspan="2" name="move_lines2" nolabel="1" domain="[('state','in', ('done', 'cancel'))]"
widget="one2many_list" mode="tree,form" height="275">
<tree string="Consumed Products" editable="bottom">
<field name="product_id" readonly="1"/>
<field name="product_qty" readonly="1"/>
<field name="product_uom" readonly="1" />
<field name="state" invisible="1" />
<field name="location_id" readonly="1"/>
<field name="prodlot_id" />
<!-- <button-->
<!-- name="%(stock.track_line)d"-->
<!-- string="Split in production lots"-->
<!-- type="action" icon="gtk-justify-fill"-->
<!-- states="done,cancel" />-->
<button
name="%(mrp.split_in_lots)d"
string="Split in production lots"
type="action" icon="gtk-justify-fill"
states="done,cancel" />
<button
name="%(stock.move_scrap)d"
string="D" type="action"
icon="gtk-convert"
states="done,cancel" />
</tree>
<form string="Consumed Products">
<field name="product_id" />
<field name="product_qty" />
<field name="product_uom" />
</form>
</field>
<group col="9" colspan="4">
<field name="state" select="2"/>
<button name="action_compute" states="draft" string="Compute Data" type="object" icon="gtk-execute"/>
<button name="button_confirm" states="draft" string="Confirm Production" icon="gtk-apply"/>
<button name="button_produce" states="ready" string="Start Production" icon="gtk-media-play"/>
<!-- <button name="action_start" states="ready" string="Mark as Start" icon="gtk-media-play" type="object"/>-->
<button name="button_produce" states="ready" string="Produce" icon="gtk-execute"/>
<button name="button_produce_done" states="in_production" string="Production done" icon="gtk-ok"/>
<button name="force_production" states="confirmed,picking_except" string="Force Reservation" type="object" icon="gtk-jump-to"/>
<button name="button_cancel" states="draft,ready,confirmed,in_production,picking_except" string="Cancel" icon="gtk-cancel"/>
@ -503,7 +552,51 @@
<field colspan="4" name="product_lines" nolabel="1" widget="one2many_list"/>
</page>
<page string="Finished Products">
<field colspan="4" name="move_created_ids" nolabel="1"/>
<field colspan="2" name="move_created_ids" nolabel="1" widget="one2many_list"
mode="tree,form" height="275" domain="[('state','&lt;&gt;', ('done', 'cancel'))]">
<tree string="Products to Consume">
<field name="product_id" />
<field name="product_qty" />
<field name="product_uom" />
<field name="state" invisible="1" />
<button
name="%(stock.move_consume)d"
string=">" type="action"
icon="gtk-go-forward"
states="draft,waiting,confirmed,assigned"/>
<button name="%(stock.move_scrap)d"
string="D" type="action"
icon="gtk-justify-fill" context="{'scrap': True}"
states="draft,waiting,confirmed,assigned" />
</tree>
<form string="Products to Consume">
<field name="product_id" />
<field name="product_qty" />
<field name="product_uom" />
</form>
</field>
<field colspan="2" name="move_created_ids2" nolabel="1" domain="[('state','in', ('done', 'cancel'))]"
widget="one2many_list" mode="tree,form" height="275">
<tree string="Consumed Products" editable="bottom">
<field name="product_id" readonly="1"/>
<field name="product_qty" readonly="1"/>
<field name="product_uom" readonly="1" />
<field name="state" invisible="1" />
<field name="location_id" readonly="1"/>
<field name="prodlot_id" />
<button name="%(stock.track_line)d"
string="Split in production lots" type="action" icon="gtk-justify-fill" states="done,cancel"/>
<button name="%(stock.move_scrap)d"
string="D" type="action" icon="gtk-go-forward"
states="done,cancel" />
</tree>
<form string="Consumed Products">
<field name="product_id" />
<field name="product_qty" />
<field name="product_uom" />
</form>
</field>
</page>
<page string="Work Orders">
<field colspan="4" name="workcenter_lines" nolabel="1">
@ -525,6 +618,7 @@
</page>
<page string="Extra Information">
<field name="company_id" select="1" groups="base.group_multi_company" widget="selection"/>
<field name="priority" groups="base.group_extended"/>
<newline/>
<field name="date_start" select="2"/>

View File

@ -31,12 +31,5 @@
keyword="client_action_multi"
id="product_procurement_wizard"/>
<wizard
id="stock.track_line"
model="stock.move"
multi="True"
name="mrp.stock.move.track"
string="Track line"/>
</data>
</openerp>

View File

@ -27,7 +27,7 @@ import wizard_workcenter_load
import wizard_track_prod
import wizard_change_production_qty
import make_procurement
import wizard_track_line
import wizard_split_in_lots
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,56 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
# 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 osv import fields, osv
from tools.translate import _
import re
import time
import tools
class spilt_in_lot(osv.osv_memory):
_name = "spilt.in.lot"
_description = "Split in lots"
_columns = {
'product_id': fields.many2one('product.product', 'Product', required=True, select=True),
'line_ids': fields.one2many('track.lines', 'lot_id', 'Lots Number')
}
def _get_product_id(self, cr, uid, context):
move = self.pool.get('stock.move').browse(cr, uid, context['active_id'], context=context)
return move.product_id.id
_defaults = {
'product_id': _get_product_id,
}
spilt_in_lot()
class track_lines(osv.osv_memory):
_name = "track.lines"
_description = "Track lines"
_columns = {
'name': fields.char('Tracking serial', size=64),
'lot_id': fields.many2one('spilt.in.lot', 'Lot')
}
track_lines()

View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="view_split_in_lots" model="ir.ui.view">
<field name="name">Split in lots</field>
<field name="model">spilt.in.lot</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Split in lots">
<field name="product_id" colspan="4"/>
<newline/>
<field name="line_ids" colspan="4" nolabel="1">
<tree string="Lots Number" editable="top">
<field name="name" string="Lots"/>
</tree>
<form string="Lots Number">
<field name="name" string="Lots"/>
</form>
</field>
<separator string="" colspan="4" />
<label string="" colspan="2" />
<button icon='gtk-cancel' special="cancel"
string="Cancel" />
<button name="track_lines" string="Ok"
type="object" icon="gtk-ok" />
</form>
</field>
</record>
<record id="split_in_lots" model="ir.actions.act_window">
<field name="name">Split in lots</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">spilt.in.lot</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
</data>
</openerp>

View File

@ -1,138 +0,0 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>). All Rights Reserved
# $Id$
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import wizard
import netsvc
import pooler
import time
from osv import osv
from tools.translate import _
track_form = '''<?xml version="1.0"?>
<form string="Tracking a move">
<field name="tracking_prefix"/>
<newline/>
<field name="quantity"/>
</form>
'''
fields = {
'tracking_prefix': {
'string': 'Tracking prefix',
'type': 'char',
'size': 64,
},
'quantity': {
'string': 'Quantity per lot',
'type': 'float',
'default': 1,
}
}
def _check_picking(self, cr, uid, data, context):
pool = pooler.get_pool(cr.dbname)
move_obj = pool.get('stock.move').browse(cr, uid, data['id'])
if not move_obj.picking_id:
raise wizard.except_wizard(_('Caution!'), _('Before splitting the production lots,make sure this move has a Picking attached !\nYou must save the move before performing this operation.'))
return data['form']
def _track_lines(self, cr, uid, data, context):
move_id = data['id']
pool = pooler.get_pool(cr.dbname)
prodlot_obj = pool.get('stock.production.lot')
move_obj = pool.get('stock.move')
production_obj = pool.get('mrp.production')
ir_sequence_obj = pool.get('ir.sequence')
sequence = ir_sequence_obj.get(cr, uid, 'stock.lot.serial')
if not sequence:
raise wizard.except_wizard(_('Error!'), _('No production sequence defined'))
if data['form']['tracking_prefix']:
sequence=data['form']['tracking_prefix']+'/'+(sequence or '')
move = move_obj.browse(cr, uid, [move_id])[0]
quantity=data['form']['quantity']
if quantity <= 0 or move.product_qty == 0:
return {}
uos_qty=quantity/move.product_qty*move.product_uos_qty
quantity_rest = move.product_qty%quantity
uos_qty_rest = quantity_rest/move.product_qty*move.product_uos_qty
update_val = {
'product_qty': quantity,
'product_uos_qty': uos_qty,
}
new_move = []
production_ids = []
for idx in range(int(move.product_qty//quantity)):
if idx:
current_move = move_obj.copy(cr, uid, move.id, {'state': move.state, 'production_id': move.production_id.id})
new_move.append(current_move)
else:
current_move = move.id
new_prodlot = prodlot_obj.create(cr, uid, {'name': sequence, 'ref': '%d'%idx}, {'product_id': move.product_id.id})
update_val['prodlot_id'] = new_prodlot
move_obj.write(cr, uid, [current_move], update_val)
production_ids = production_obj.search(cr, uid, [('move_lines', 'in', [move.id])])
if quantity_rest > 0:
idx = int(move.product_qty//quantity)
update_val['product_qty']=quantity_rest
update_val['product_uos_qty']=uos_qty_rest
if idx:
current_move = move_obj.copy(cr, uid, move.id, {'state': move.state, 'production_id': move.production_id.id})
current_move = move_obj.copy(cr, uid, move.id, {'state': move.state})
new_move.append(current_move)
else:
current_move = move.id
new_prodlot = prodlot_obj.create(cr, uid, {'name': sequence, 'ref': '%d'%idx}, {'product_id': move.product_id.id})
update_val['prodlot_id'] = new_prodlot
move_obj.write(cr, uid, [current_move], update_val)
products = production_obj.read(cr, uid, production_ids, ['move_lines'])
for p in products:
for new in new_move:
if new not in p['move_lines']:
p['move_lines'].append(new)
production_obj.write(cr, uid, [p['id']], {'move_lines': [(6, 0, p['move_lines'])]})
return {}
class wizard_track_move(wizard.interface):
states = {
'init': {
'actions': [_check_picking],
'result': {'type': 'form', 'arch': track_form, 'fields': fields, 'state': [('end', 'Cancel', 'gtk-cancel'), ('track', 'Ok', 'gtk-ok')]},
},
'track': {
'actions': [_track_lines],
'result': {'type':'state', 'state':'end'}
}
}
wizard_track_move('mrp.stock.move.track')
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: