From 3e87ec567ee73b754b06088df2d82294bcf7a30c Mon Sep 17 00:00:00 2001 From: Cedric Snauwaert Date: Thu, 16 Jan 2014 14:39:05 +0100 Subject: [PATCH 01/20] [FIX]remove unneeded code to compute qty available in product module, use the one defined in stock instead lp bug: https://launchpad.net/bugs/1268989 fixed bzr revid: csn@openerp.com-20140116133905-en7y1b9tibstcou3 --- addons/product/product.py | 14 -------------- addons/product/product_view.xml | 4 +--- addons/stock/product_view.xml | 16 ++++++++++++++++ 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/addons/product/product.py b/addons/product/product.py index 4b63100dbee..20346d0a0ee 100644 --- a/addons/product/product.py +++ b/addons/product/product.py @@ -508,16 +508,6 @@ class product_product(osv.osv): res.setdefault(id, 0.0) return res - def _get_product_available_func(states, what): - def _product_available(self, cr, uid, ids, name, arg, context=None): - return {}.fromkeys(ids, 0.0) - return _product_available - - _product_qty_available = _get_product_available_func(('done',), ('in', 'out')) - _product_virtual_available = _get_product_available_func(('confirmed','waiting','assigned','done'), ('in', 'out')) - _product_outgoing_qty = _get_product_available_func(('confirmed','waiting','assigned'), ('out',)) - _product_incoming_qty = _get_product_available_func(('confirmed','waiting','assigned'), ('in',)) - def _product_lst_price(self, cr, uid, ids, name, arg, context=None): res = {} product_uom_obj = self.pool.get('product.uom') @@ -622,10 +612,6 @@ class product_product(osv.osv): _inherit = ['mail.thread'] _order = 'default_code,name_template' _columns = { - 'qty_available': fields.function(_product_qty_available, type='float', string='Quantity On Hand'), - 'virtual_available': fields.function(_product_virtual_available, type='float', string='Quantity Available'), - 'incoming_qty': fields.function(_product_incoming_qty, type='float', string='Incoming'), - 'outgoing_qty': fields.function(_product_outgoing_qty, type='float', string='Outgoing'), 'price': fields.function(_product_price, type='float', string='Price', digits_compute=dp.get_precision('Product Price')), 'lst_price' : fields.function(_product_lst_price, type='float', string='Public Price', digits_compute=dp.get_precision('Product Price')), 'code': fields.function(_product_code, type='char', string='Internal Reference'), diff --git a/addons/product/product_view.xml b/addons/product/product_view.xml index e9aac7419c8..241087c56e2 100644 --- a/addons/product/product_view.xml +++ b/addons/product/product_view.xml @@ -33,14 +33,12 @@ product.product - + - - diff --git a/addons/stock/product_view.xml b/addons/stock/product_view.xml index 277ec5b40e5..583a9585374 100644 --- a/addons/stock/product_view.xml +++ b/addons/stock/product_view.xml @@ -14,6 +14,22 @@ + + product.stock.tree.inherit + product.product + + + + + + + + + {'red':virtual_available<0, 'blue':virtual_available>=0 and state in ('draft', 'end', 'obsolete'), 'black':virtual_available>=0 and state not in ('draft', 'end', 'obsolete')} + + + + product.template.stock.property.form.inherit product.template From 9dc64a216be3b3b5b893c7c9be6b9c14ef6b56c3 Mon Sep 17 00:00:00 2001 From: Cedric Snauwaert Date: Fri, 17 Jan 2014 10:20:11 +0100 Subject: [PATCH 02/20] [FIX]add search function and filter for virtual_available and qty_available on product bzr revid: csn@openerp.com-20140117092011-5c0qu2a2xor29g7u --- addons/stock/product.py | 18 ++++++++++++++++++ addons/stock/product_view.xml | 6 ++++++ 2 files changed, 24 insertions(+) diff --git a/addons/stock/product.py b/addons/stock/product.py index 6ca53a5ad0d..ff39190355a 100644 --- a/addons/stock/product.py +++ b/addons/stock/product.py @@ -148,12 +148,29 @@ class product_product(osv.osv): return res + def _search_product_quantity(self, cr, uid, obj, name, args, context): + res = [] + for field, operator, value in args: + if operator == '=': + operator = '==' + + product_ids = self.search(cr, uid, [], context=context) + ids = [] + if product_ids: + for element in self.browse(cr, uid, product_ids, context=context): + if eval(str(element[field]) + operator + str(value)): + ids.append(element.id) + res.append(('id', 'in', ids)) + return res + + _columns = { 'reception_count': fields.function(_stock_move_count, string="Reception", type='integer', multi='pickings'), 'delivery_count': fields.function(_stock_move_count, string="Delivery", type='integer', multi='pickings'), 'qty_available': fields.function(_product_available, multi='qty_available', type='float', digits_compute=dp.get_precision('Product Unit of Measure'), string='Quantity On Hand', + fnct_search=_search_product_quantity, help="Current quantity of products.\n" "In a context with a single Stock Location, this includes " "goods stored at this Location, or any of its children.\n" @@ -167,6 +184,7 @@ class product_product(osv.osv): 'virtual_available': fields.function(_product_available, multi='qty_available', type='float', digits_compute=dp.get_precision('Product Unit of Measure'), string='Forecasted Quantity', + fnct_search=_search_product_quantity, help="Forecast quantity (computed as Quantity On Hand " "- Outgoing + Incoming)\n" "In a context with a single Stock Location, this includes " diff --git a/addons/stock/product_view.xml b/addons/stock/product_view.xml index 583a9585374..7a9218436af 100644 --- a/addons/stock/product_view.xml +++ b/addons/stock/product_view.xml @@ -11,6 +11,12 @@ + + + + + + From 0f7ca6e784b51abac7d32094fcc634aa8bd39394 Mon Sep 17 00:00:00 2001 From: "Jitendra Prajapati (OpenERP)" Date: Mon, 20 Jan 2014 12:22:22 +0530 Subject: [PATCH 03/20] [FIX]mrp_repair:set unique constraint on name bzr revid: prajapatijitendra7969@gmail.com-20140120065222-05hw8t0ca05mq7r4 --- addons/mrp_repair/mrp_repair.py | 4 ++++ addons/mrp_repair/mrp_repair_demo.yml | 3 --- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/addons/mrp_repair/mrp_repair.py b/addons/mrp_repair/mrp_repair.py index b178d4e12a4..c287bbff56d 100644 --- a/addons/mrp_repair/mrp_repair.py +++ b/addons/mrp_repair/mrp_repair.py @@ -186,6 +186,10 @@ class mrp_repair(osv.osv): 'pricelist_id': lambda self, cr, uid,context : self.pool.get('product.pricelist').search(cr, uid, [('type','=','sale')])[0] } + _sql_constraints = [ + ('name', 'unique (name)', 'The name of the Repair Order must be unique!'), + ] + def copy(self, cr, uid, id, default=None, context=None): if not default: default = {} diff --git a/addons/mrp_repair/mrp_repair_demo.yml b/addons/mrp_repair/mrp_repair_demo.yml index 1299cbfb6d2..93fc19a1f91 100644 --- a/addons/mrp_repair/mrp_repair_demo.yml +++ b/addons/mrp_repair/mrp_repair_demo.yml @@ -20,7 +20,6 @@ location_id: stock.stock_location_14 picking_type_id: stock.picking_type_out move_id: 'stock_move_pcbasicpc0' - name: RMA00004 operations: - location_dest_id: stock.location_production location_id: stock.stock_location_stock @@ -62,7 +61,6 @@ location_id: stock.stock_location_14 picking_type_id: stock.picking_type_out move_id: 'stock.stock_move_stockmvmrp1' - name: RMA-00007 operations: - location_dest_id: stock.location_production location_id: stock.stock_location_stock @@ -104,7 +102,6 @@ location_id: stock.stock_location_14 picking_type_id: stock.picking_type_out move_id: 'stock.stock_move_stockmvmrp2' - name: RMA-00011 operations: - location_dest_id: stock.location_production location_id: stock.stock_location_stock From 86e7d60e23643b4cadd32a7273824e5845fc2dc9 Mon Sep 17 00:00:00 2001 From: Cedric Snauwaert Date: Tue, 21 Jan 2014 11:51:00 +0100 Subject: [PATCH 04/20] [FIX]add some filter to search product qty bzr revid: csn@openerp.com-20140121105100-offsz1so77u4z0x8 --- addons/stock/product_view.xml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/addons/stock/product_view.xml b/addons/stock/product_view.xml index 7a9218436af..76ae15475ce 100644 --- a/addons/stock/product_view.xml +++ b/addons/stock/product_view.xml @@ -13,9 +13,11 @@ - - - + + + + + From fbd144c9e7e6f78556fbdc1b91eea66e36384f39 Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Wed, 22 Jan 2014 13:31:59 +0100 Subject: [PATCH 05/20] [WIP] Start changing move to quant bzr revid: jco@openerp.com-20140122123159-v9tvkzrry61a6n39 --- addons/mrp_repair/mrp_repair.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/addons/mrp_repair/mrp_repair.py b/addons/mrp_repair/mrp_repair.py index b178d4e12a4..9fd49d5840f 100644 --- a/addons/mrp_repair/mrp_repair.py +++ b/addons/mrp_repair/mrp_repair.py @@ -139,7 +139,7 @@ class mrp_repair(osv.osv): \n* The \'Cancelled\' status is used when user cancel repair order.'), 'location_id': fields.many2one('stock.location', 'Current Location', select=True, readonly=True, states={'draft':[('readonly',False)], 'confirmed':[('readonly',True)]}), 'location_dest_id': fields.many2one('stock.location', 'Delivery Location', readonly=True, states={'draft':[('readonly',False)], 'confirmed':[('readonly',True)]}), - 'move_id': fields.many2one('stock.move', 'Move',required=True, domain="[('product_id','=',product_id)]", readonly=True, states={'draft':[('readonly',False)]}), + 'quant_id': fields.many2one('stock.quant', 'Quant',required=True, domain="[('product_id','=',product_id)]", readonly=True, states={'draft':[('readonly',False)]}), 'guarantee_limit': fields.date('Warranty Expiration', help="The warranty expiration limit is computed as: last move date + warranty defined on selected product. If the current date is below the warranty expiration limit, each operation and fee you will add will be set as 'not to invoiced' by default. Note that you can change manually afterwards.", states={'confirmed':[('readonly',True)]}), 'operations' : fields.one2many('mrp.repair.line', 'repair_id', 'Operation Lines', readonly=True, states={'draft':[('readonly',False)]}), 'pricelist_id': fields.many2one('product.pricelist', 'Pricelist', help='Pricelist of the selected partner.'), @@ -205,25 +205,26 @@ class mrp_repair(osv.osv): @return: Dictionary of values. """ return {'value': { - 'move_id': False, + 'quant_id': False, 'guarantee_limit' :False, 'location_id': False, 'location_dest_id': False, } } - def onchange_move_id(self, cr, uid, ids, prod_id=False, move_id=False): + def onchange_quant_id(self, cr, uid, ids, prod_id=False, quant_id=False): """ On change of move id sets values of guarantee limit, source location, destination location, partner and partner address. @param prod_id: Id of product in current record. - @param move_id: Changed move. + @param quant_id: Quant to handle @return: Dictionary of values. """ data = {} data['value'] = {'guarantee_limit': False, 'location_id': False, 'partner_id': False} - if not prod_id: + if prod_id and + if not prod_id and not quant_id: return data - if move_id: + if quant_id: move = self.pool.get('stock.move').browse(cr, uid, move_id) product = self.pool.get('product.product').browse(cr, uid, prod_id) limit = datetime.strptime(move.date_expected, '%Y-%m-%d %H:%M:%S') + relativedelta(months=int(product.warranty)) From a173b17ce3e96fb0e97bc7337a8bf8cf0d8af70a Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Wed, 22 Jan 2014 15:39:51 +0100 Subject: [PATCH 06/20] [IMP] Use quant instead of move bzr revid: jco@openerp.com-20140122143951-vywil9totup32kib --- addons/mrp_repair/mrp_repair.py | 58 +++++---------------------- addons/mrp_repair/mrp_repair_view.xml | 4 +- 2 files changed, 13 insertions(+), 49 deletions(-) diff --git a/addons/mrp_repair/mrp_repair.py b/addons/mrp_repair/mrp_repair.py index 9fd49d5840f..45db1cb4ede 100644 --- a/addons/mrp_repair/mrp_repair.py +++ b/addons/mrp_repair/mrp_repair.py @@ -139,7 +139,7 @@ class mrp_repair(osv.osv): \n* The \'Cancelled\' status is used when user cancel repair order.'), 'location_id': fields.many2one('stock.location', 'Current Location', select=True, readonly=True, states={'draft':[('readonly',False)], 'confirmed':[('readonly',True)]}), 'location_dest_id': fields.many2one('stock.location', 'Delivery Location', readonly=True, states={'draft':[('readonly',False)], 'confirmed':[('readonly',True)]}), - 'quant_id': fields.many2one('stock.quant', 'Quant',required=True, domain="[('product_id','=',product_id)]", readonly=True, states={'draft':[('readonly',False)]}), + 'quant_id': fields.many2one('stock.quant', 'Quant',required=True, domain="[('product_id','=',product_id), ('qty', '>', 0.0)]", readonly=True, states={'draft':[('readonly',False)]}), 'guarantee_limit': fields.date('Warranty Expiration', help="The warranty expiration limit is computed as: last move date + warranty defined on selected product. If the current date is below the warranty expiration limit, each operation and fee you will add will be set as 'not to invoiced' by default. Note that you can change manually afterwards.", states={'confirmed':[('readonly',True)]}), 'operations' : fields.one2many('mrp.repair.line', 'repair_id', 'Operation Lines', readonly=True, states={'draft':[('readonly',False)]}), 'pricelist_id': fields.many2one('product.pricelist', 'Pricelist', help='Pricelist of the selected partner.'), @@ -221,22 +221,16 @@ class mrp_repair(osv.osv): """ data = {} data['value'] = {'guarantee_limit': False, 'location_id': False, 'partner_id': False} - if prod_id and if not prod_id and not quant_id: return data if quant_id: - move = self.pool.get('stock.move').browse(cr, uid, move_id) - product = self.pool.get('product.product').browse(cr, uid, prod_id) - limit = datetime.strptime(move.date_expected, '%Y-%m-%d %H:%M:%S') + relativedelta(months=int(product.warranty)) + quant = self.pool.get('stock.quant').browse(cr, uid, quant_id) + product = quant.product_id + data['value']['product_id'] = product.id + limit = quant.in_date and datetime.strptime(quant.in_date, '%Y-%m-%d %H:%M:%S') or datetime.strptime(quant.create_date, '%Y-%m-%d %H:%M:%S') + relativedelta(months=int(product.warranty)) data['value']['guarantee_limit'] = limit.strftime('%Y-%m-%d') - data['value']['location_id'] = move.location_dest_id.id - data['value']['location_dest_id'] = move.location_dest_id.id - if move.partner_id: - data['value']['partner_id'] = move.partner_id.id - else: - data['value']['partner_id'] = False - d = self.onchange_partner_id(cr, uid, ids, data['value']['partner_id'], data['value']['partner_id']) - data['value'].update(d['value']) + data['value']['location_id'] = quant.location_id.id + data['value']['location_dest_id'] = quant.location_id.id return data def button_dummy(self, cr, uid, ids, context=None): @@ -268,39 +262,6 @@ class mrp_repair(osv.osv): } } - def onchange_lot_id(self, cr, uid, ids, lot, product_id): - """ On change of Serial Number sets the values of source location, - destination location, move and guarantee limit. - @param lot: Changed id of Serial Number. - @param product_id: Product id from current record. - @return: Dictionary of values. - """ - move_obj = self.pool.get('stock.move') - data = {} - data['value'] = { - 'location_id': False, - 'location_dest_id': False, - 'move_id': False, - 'guarantee_limit': False - } - - if not lot: - return data - - if not len(move_ids): - return data - - def get_last_move(lst_move): - while lst_move.move_dest_id and lst_move.move_dest_id.state == 'done': - lst_move = lst_move.move_dest_id - return lst_move - - move_id = move_ids[0] - move = get_last_move(move_obj.browse(cr, uid, move_id)) - data['value']['move_id'] = move.id - d = self.onchange_move_id(cr, uid, ids, product_id, move.id) - data['value'].update(d['value']) - return data def action_cancel_draft(self, cr, uid, ids, *args): """ Cancels repair order when it is in 'Draft' state. @@ -544,6 +505,9 @@ class mrp_repair(osv.osv): 'state': 'assigned', }) pick_obj.signal_button_confirm(cr, uid, [picking]) + quants = [(repair.quant_id, repair.quant_id.qty)] + move = move_obj.browse(cr, uid, move_id, context=context) + self.pool.get("stock.quant").quants_reserve(cr, uid, quants, move, context=context) self.write(cr, uid, [repair.id], {'state': 'done', 'picking_id': picking}) res[repair.id] = picking else: @@ -609,7 +573,7 @@ class mrp_repair_line(osv.osv, ProductChangeMixin): def copy_data(self, cr, uid, id, default=None, context=None): if not default: default = {} - default.update( {'invoice_line_id': False, 'move_id': False, 'invoiced': False, 'state': 'draft'}) + default.update( {'invoice_line_id': False, 'quant_id': False, 'invoiced': False, 'state': 'draft'}) return super(mrp_repair_line, self).copy_data(cr, uid, id, default, context) def _amount_line(self, cr, uid, ids, field_name, arg, context=None): diff --git a/addons/mrp_repair/mrp_repair_view.xml b/addons/mrp_repair/mrp_repair_view.xml index 4e799ff8709..0a1578104fa 100644 --- a/addons/mrp_repair/mrp_repair_view.xml +++ b/addons/mrp_repair/mrp_repair_view.xml @@ -9,7 +9,7 @@ - + @@ -47,7 +47,7 @@ - + From 2213f6f102f06a0045cd157d613085006d5f3a4b Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Wed, 22 Jan 2014 16:35:19 +0100 Subject: [PATCH 07/20] [IMP] Add lot on mrp_repair operations bzr revid: jco@openerp.com-20140122153519-zs2062b653l16qb5 --- addons/mrp_repair/mrp_repair.py | 2 ++ addons/mrp_repair/mrp_repair_view.xml | 1 + 2 files changed, 3 insertions(+) diff --git a/addons/mrp_repair/mrp_repair.py b/addons/mrp_repair/mrp_repair.py index 45db1cb4ede..d6c45ca6733 100644 --- a/addons/mrp_repair/mrp_repair.py +++ b/addons/mrp_repair/mrp_repair.py @@ -471,6 +471,7 @@ class mrp_repair(osv.osv): move_id = move_obj.create(cr, uid, { 'name': move.name, 'product_id': move.product_id.id, + 'restrict_lot_id': move.lot_id.id, 'product_uom_qty': move.product_uom_qty, 'product_uom': move.product_uom.id, 'partner_id': repair.address_id and repair.address_id.id or False, @@ -606,6 +607,7 @@ class mrp_repair_line(osv.osv, ProductChangeMixin): 'location_id': fields.many2one('stock.location', 'Source Location', required=True, select=True), 'location_dest_id': fields.many2one('stock.location', 'Dest. Location', required=True, select=True), 'move_id': fields.many2one('stock.move', 'Inventory Move', readonly=True), + 'lot_id': fields.many2one('stock.production.lot', 'Lot'), 'state': fields.selection([ ('draft','Draft'), ('confirmed','Confirmed'), diff --git a/addons/mrp_repair/mrp_repair_view.xml b/addons/mrp_repair/mrp_repair_view.xml index 0a1578104fa..cf4236743ce 100644 --- a/addons/mrp_repair/mrp_repair_view.xml +++ b/addons/mrp_repair/mrp_repair_view.xml @@ -94,6 +94,7 @@ + From 386f7c1212705a9cc3b2e73731bfcc4ca447dfbf Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Thu, 23 Jan 2014 18:27:41 +0100 Subject: [PATCH 08/20] [WIP] Change repair orders not to use quants, but always to create move that splits quant from a certain location and lot bzr revid: jco@openerp.com-20140123172741-cjgzps1twsvv0n76 --- addons/mrp_repair/mrp_repair.py | 99 ++++++++------------------- addons/mrp_repair/mrp_repair_view.xml | 10 +-- 2 files changed, 33 insertions(+), 76 deletions(-) diff --git a/addons/mrp_repair/mrp_repair.py b/addons/mrp_repair/mrp_repair.py index d6c45ca6733..4fc9007b034 100644 --- a/addons/mrp_repair/mrp_repair.py +++ b/addons/mrp_repair/mrp_repair.py @@ -25,7 +25,6 @@ from dateutil.relativedelta import relativedelta from openerp.tools.translate import _ import openerp.addons.decimal_precision as dp -# TODO: replace move_id by quant_id everywhere class mrp_repair(osv.osv): _name = 'mrp.repair' @@ -137,9 +136,9 @@ class mrp_repair(osv.osv): \n* The \'To be Invoiced\' status is used to generate the invoice before or after repairing done. \ \n* The \'Done\' status is set when repairing is completed.\ \n* The \'Cancelled\' status is used when user cancel repair order.'), - 'location_id': fields.many2one('stock.location', 'Current Location', select=True, readonly=True, states={'draft':[('readonly',False)], 'confirmed':[('readonly',True)]}), - 'location_dest_id': fields.many2one('stock.location', 'Delivery Location', readonly=True, states={'draft':[('readonly',False)], 'confirmed':[('readonly',True)]}), - 'quant_id': fields.many2one('stock.quant', 'Quant',required=True, domain="[('product_id','=',product_id), ('qty', '>', 0.0)]", readonly=True, states={'draft':[('readonly',False)]}), + 'location_id': fields.many2one('stock.location', 'Current Location', select=True, required = True, readonly=True, states={'draft':[('readonly',False)], 'confirmed':[('readonly',True)]}), + 'location_dest_id': fields.many2one('stock.location', 'Delivery Location', readonly=True, required = True, states={'draft':[('readonly',False)], 'confirmed':[('readonly',True)]}), + 'lot_id': fields.many2one('stock.production.lot', 'Lot', domain="[('product_id','=', product_id)]", help="Lot chosen"), 'guarantee_limit': fields.date('Warranty Expiration', help="The warranty expiration limit is computed as: last move date + warranty defined on selected product. If the current date is below the warranty expiration limit, each operation and fee you will add will be set as 'not to invoiced' by default. Note that you can change manually afterwards.", states={'confirmed':[('readonly',True)]}), 'operations' : fields.one2many('mrp.repair.line', 'repair_id', 'Operation Lines', readonly=True, states={'draft':[('readonly',False)]}), 'pricelist_id': fields.many2one('product.pricelist', 'Pricelist', help='Pricelist of the selected partner.'), @@ -151,13 +150,11 @@ class mrp_repair(osv.osv): ], "Invoice Method", select=True, required=True, states={'draft':[('readonly',False)]}, readonly=True, help='Selecting \'Before Repair\' or \'After Repair\' will allow you to generate invoice before or after the repair is done respectively. \'No invoice\' means you don\'t want to generate invoice for this repair order.'), 'invoice_id': fields.many2one('account.invoice', 'Invoice', readonly=True), - 'picking_id': fields.many2one('stock.picking', 'Picking',readonly=True), + 'move_id': fields.many2one('stock.move', 'Move',readonly=True), 'fees_lines': fields.one2many('mrp.repair.fee', 'repair_id', 'Fees Lines', readonly=True, states={'draft':[('readonly',False)]}), 'internal_notes': fields.text('Internal Notes'), 'quotation_notes': fields.text('Quotation Notes'), 'company_id': fields.many2one('res.company', 'Company'), - 'deliver_bool': fields.boolean('Deliver', help="Check this box if you want to manage the delivery once the product is repaired and create a picking with selected product. Note that you can select the locations in the Info tab, if you have the extended view.", states={'confirmed':[('readonly',True)]}), - 'picking_type_id': fields.many2one('stock.picking.type', 'Picking Type'), 'invoiced': fields.boolean('Invoiced', readonly=True), 'repaired': fields.boolean('Repaired', readonly=True), 'amount_untaxed': fields.function(_amount_untaxed, string='Untaxed Amount', @@ -175,15 +172,16 @@ class mrp_repair(osv.osv): 'mrp.repair': (lambda self, cr, uid, ids, c={}: ids, ['operations'], 10), 'mrp.repair.line': (_get_lines, ['price_unit', 'price_subtotal', 'product_id', 'tax_id', 'product_uom_qty', 'product_uom'], 10), }), + 'product_qty': fields.integer('Quantity', help='Product Qty'), } _defaults = { 'state': lambda *a: 'draft', - 'deliver_bool': lambda *a: True, 'name': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').get(cr, uid, 'mrp.repair'), 'invoice_method': lambda *a: 'none', 'company_id': lambda self, cr, uid, context: self.pool.get('res.company')._company_default_get(cr, uid, 'mrp.repair', context=context), - 'pricelist_id': lambda self, cr, uid,context : self.pool.get('product.pricelist').search(cr, uid, [('type','=','sale')])[0] + 'pricelist_id': lambda self, cr, uid,context : self.pool.get('product.pricelist').search(cr, uid, [('type','=','sale')])[0], + 'product_qty': 1.0, } def copy(self, cr, uid, id, default=None, context=None): @@ -194,7 +192,7 @@ class mrp_repair(osv.osv): 'repaired':False, 'invoiced':False, 'invoice_id': False, - 'picking_id': False, + 'move_id': False, 'name': self.pool.get('ir.sequence').get(cr, uid, 'mrp.repair'), }) return super(mrp_repair, self).copy(cr, uid, id, default, context) @@ -205,33 +203,15 @@ class mrp_repair(osv.osv): @return: Dictionary of values. """ return {'value': { - 'quant_id': False, 'guarantee_limit' :False, - 'location_id': False, - 'location_dest_id': False, + 'lot_id': False, } } - - def onchange_quant_id(self, cr, uid, ids, prod_id=False, quant_id=False): - """ On change of move id sets values of guarantee limit, source location, - destination location, partner and partner address. - @param prod_id: Id of product in current record. - @param quant_id: Quant to handle - @return: Dictionary of values. + + def onchange_location_id(self, cr, uid, ids, location_id=None): + """ On change of location """ - data = {} - data['value'] = {'guarantee_limit': False, 'location_id': False, 'partner_id': False} - if not prod_id and not quant_id: - return data - if quant_id: - quant = self.pool.get('stock.quant').browse(cr, uid, quant_id) - product = quant.product_id - data['value']['product_id'] = product.id - limit = quant.in_date and datetime.strptime(quant.in_date, '%Y-%m-%d %H:%M:%S') or datetime.strptime(quant.create_date, '%Y-%m-%d %H:%M:%S') + relativedelta(months=int(product.warranty)) - data['value']['guarantee_limit'] = limit.strftime('%Y-%m-%d') - data['value']['location_id'] = quant.location_id.id - data['value']['location_dest_id'] = quant.location_id.id - return data + return {'value': {'location_dest_id': location_id}} def button_dummy(self, cr, uid, ids, context=None): return True @@ -459,13 +439,12 @@ class mrp_repair(osv.osv): return True def action_repair_done(self, cr, uid, ids, context=None): - """ Creates stock move and picking for repair order. - @return: Picking ids. + """ Creates stock move for operation and stock move for final product of repair order. + @return: Move ids of final products """ res = {} move_obj = self.pool.get('stock.move') repair_line_obj = self.pool.get('mrp.repair.line') - pick_obj = self.pool.get('stock.picking') for repair in self.browse(cr, uid, ids, context=context): for move in repair.operations: move_id = move_obj.create(cr, uid, { @@ -481,38 +460,20 @@ class mrp_repair(osv.osv): }) move_obj.action_done(cr, uid, [move_id], context=context) repair_line_obj.write(cr, uid, [move.id], {'move_id': move_id, 'state': 'done'}, context=context) - if repair.deliver_bool: - if not repair.picking_type_id: - raise osv.except_osv(_('Warning!'), _('No picking type set.')) - pick_name = self.pool.get('ir.sequence').get_id(cr, uid, repair.picking_type_id.sequence_id.id, 'id', context=context) - picking = pick_obj.create(cr, uid, { - 'name': pick_name, - 'origin': repair.name, - 'state': 'draft', - 'move_type': 'one', - 'partner_id': repair.address_id and repair.address_id.id or False, - 'note': repair.internal_notes, - 'invoice_state': 'none', - 'picking_type_id': repair.picking_type_id.id, - }) - move_id = move_obj.create(cr, uid, { - 'name': repair.name, - 'picking_id': picking, - 'product_id': repair.product_id.id, - 'product_uom': repair.product_id.uom_id.id, - 'partner_id': repair.address_id and repair.address_id.id or False, - 'location_id': repair.location_id.id, - 'location_dest_id': repair.location_dest_id.id, - 'state': 'assigned', - }) - pick_obj.signal_button_confirm(cr, uid, [picking]) - quants = [(repair.quant_id, repair.quant_id.qty)] - move = move_obj.browse(cr, uid, move_id, context=context) - self.pool.get("stock.quant").quants_reserve(cr, uid, quants, move, context=context) - self.write(cr, uid, [repair.id], {'state': 'done', 'picking_id': picking}) - res[repair.id] = picking - else: - self.write(cr, uid, [repair.id], {'state': 'done'}) + move_id = move_obj.create(cr, uid, { + 'name': repair.name, + 'product_id': repair.product_id.id, + 'product_uom': repair.product_id.uom_id.id, + 'partner_id': repair.address_id and repair.address_id.id or False, + 'location_id': repair.location_id.id, + 'location_dest_id': repair.location_dest_id.id, + 'restrict_lot_id': repair.lot_id.id, + 'product_qty': repair.product_qty, + }) + move_obj.signal_button_confirm(cr, uid, [move_id]) + move_obj.action_done(cr, uid, [move_id], context=context) + self.write(cr, uid, [repair.id], {'state': 'done', 'move_id': move_id}, context=context) + res[repair.id] = move_id return res @@ -574,7 +535,7 @@ class mrp_repair_line(osv.osv, ProductChangeMixin): def copy_data(self, cr, uid, id, default=None, context=None): if not default: default = {} - default.update( {'invoice_line_id': False, 'quant_id': False, 'invoiced': False, 'state': 'draft'}) + default.update( {'invoice_line_id': False, 'move_id': False, 'invoiced': False, 'state': 'draft'}) return super(mrp_repair_line, self).copy_data(cr, uid, id, default, context) def _amount_line(self, cr, uid, ids, field_name, arg, context=None): diff --git a/addons/mrp_repair/mrp_repair_view.xml b/addons/mrp_repair/mrp_repair_view.xml index cf4236743ce..7c973470403 100644 --- a/addons/mrp_repair/mrp_repair_view.xml +++ b/addons/mrp_repair/mrp_repair_view.xml @@ -9,7 +9,6 @@ - @@ -47,13 +46,10 @@ - - + - - @@ -171,12 +167,12 @@ - + - + From 01679750c020d41e5d8741d8bd4ae1230f0b4e6c Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Fri, 24 Jan 2014 11:35:01 +0100 Subject: [PATCH 09/20] [IMP] Add product qty and UoM to repair order bzr revid: jco@openerp.com-20140124103501-jhz0v3l1pj68i8nb --- addons/mrp_repair/mrp_repair.py | 27 ++++++++++++++++++++++----- addons/mrp_repair/mrp_repair_view.xml | 7 ++++++- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/addons/mrp_repair/mrp_repair.py b/addons/mrp_repair/mrp_repair.py index 4fc9007b034..66325ee500c 100644 --- a/addons/mrp_repair/mrp_repair.py +++ b/addons/mrp_repair/mrp_repair.py @@ -117,6 +117,9 @@ class mrp_repair(osv.osv): _columns = { 'name': fields.char('Repair Reference',size=24, required=True, states={'confirmed':[('readonly',True)]}), 'product_id': fields.many2one('product.product', string='Product to Repair', required=True, readonly=True, states={'draft':[('readonly',False)]}), + 'product_qty': fields.float('Product Quantity', help='Product Qty', digits_compute=dp.get_precision('Product Unit of Measure'), + required=True, readonly=True, states={'draft':[('readonly',False)]}), + 'product_uom': fields.many2one('product.uom', 'Product Unit of Measure', required=True, readonly=True, states={'draft': [('readonly', False)]}), 'partner_id' : fields.many2one('res.partner', 'Partner', select=True, help='Choose partner for whom the order will be invoiced and delivered.', states={'confirmed':[('readonly',True)]}), 'address_id': fields.many2one('res.partner', 'Delivery Address', domain="[('parent_id','=',partner_id)]", states={'confirmed':[('readonly',True)]}), 'default_address_id': fields.function(_get_default_address, type="many2one", relation="res.partner"), @@ -172,7 +175,6 @@ class mrp_repair(osv.osv): 'mrp.repair': (lambda self, cr, uid, ids, c={}: ids, ['operations'], 10), 'mrp.repair.line': (_get_lines, ['price_unit', 'price_subtotal', 'product_id', 'tax_id', 'product_uom_qty', 'product_uom'], 10), }), - 'product_qty': fields.integer('Quantity', help='Product Qty'), } _defaults = { @@ -181,7 +183,7 @@ class mrp_repair(osv.osv): 'invoice_method': lambda *a: 'none', 'company_id': lambda self, cr, uid, context: self.pool.get('res.company')._company_default_get(cr, uid, 'mrp.repair', context=context), 'pricelist_id': lambda self, cr, uid,context : self.pool.get('product.pricelist').search(cr, uid, [('type','=','sale')])[0], - 'product_qty': 1.0, + 'product_qty': 1.0, } def copy(self, cr, uid, id, default=None, context=None): @@ -202,12 +204,27 @@ class mrp_repair(osv.osv): @param product_id: Changed product @return: Dictionary of values. """ + product = False + if product_id: + product = self.pool.get("product.product").browse(cr, uid, product_id) return {'value': { 'guarantee_limit' :False, 'lot_id': False, + 'product_uom': product and product.uom_id.id or False, } } - + + def onchange_product_uom(self, cr, uid, ids, product_id, product_uom, context=None): + res = {'value':{}} + if not product_uom or not product_id: + return res + product = self.pool.get('product.product').browse(cr, uid, product_id, context=context) + uom = self.pool.get('product.uom').browse(cr, uid, product_uom, context=context) + if uom.category_id.id != product.uom_id.category_id.id: + res['warning'] = {'title': _('Warning'), 'message': _('The Product Unit of Measure you chose has a different category than in the product form.')} + res['value'].update({'product_uom': product.uom_id.id}) + return res + def onchange_location_id(self, cr, uid, ids, location_id=None): """ On change of location """ @@ -463,12 +480,12 @@ class mrp_repair(osv.osv): move_id = move_obj.create(cr, uid, { 'name': repair.name, 'product_id': repair.product_id.id, - 'product_uom': repair.product_id.uom_id.id, + 'product_uom': repair.product_uom.id, + 'product_qty': repair.product_qty, 'partner_id': repair.address_id and repair.address_id.id or False, 'location_id': repair.location_id.id, 'location_dest_id': repair.location_dest_id.id, 'restrict_lot_id': repair.lot_id.id, - 'product_qty': repair.product_qty, }) move_obj.signal_button_confirm(cr, uid, [move_id]) move_obj.action_done(cr, uid, [move_id], context=context) diff --git a/addons/mrp_repair/mrp_repair_view.xml b/addons/mrp_repair/mrp_repair_view.xml index 7c973470403..972fef71c00 100644 --- a/addons/mrp_repair/mrp_repair_view.xml +++ b/addons/mrp_repair/mrp_repair_view.xml @@ -44,6 +44,11 @@ + - +
From 44f3e6de6674818ab42ff667bc1b066ed7d830c5 Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Fri, 24 Jan 2014 13:30:36 +0100 Subject: [PATCH 10/20] [IMP] Adapt demo data and tests mrp repair bzr revid: jco@openerp.com-20140124123036-w63uzmrkg08qoil0 --- addons/mrp_repair/mrp_repair.py | 2 +- addons/mrp_repair/mrp_repair_demo.yml | 59 ++++--------------- .../test/test_mrp_repair_noneinv.yml | 7 +-- 3 files changed, 14 insertions(+), 54 deletions(-) diff --git a/addons/mrp_repair/mrp_repair.py b/addons/mrp_repair/mrp_repair.py index 66325ee500c..80882cd80ef 100644 --- a/addons/mrp_repair/mrp_repair.py +++ b/addons/mrp_repair/mrp_repair.py @@ -480,7 +480,7 @@ class mrp_repair(osv.osv): move_id = move_obj.create(cr, uid, { 'name': repair.name, 'product_id': repair.product_id.id, - 'product_uom': repair.product_uom.id, + 'product_uom': repair.product_uom.id or repair.product_id.uom_id.id, 'product_qty': repair.product_qty, 'partner_id': repair.address_id and repair.address_id.id or False, 'location_id': repair.location_id.id, diff --git a/addons/mrp_repair/mrp_repair_demo.yml b/addons/mrp_repair/mrp_repair_demo.yml index 1299cbfb6d2..f9f0eaf03f8 100644 --- a/addons/mrp_repair/mrp_repair_demo.yml +++ b/addons/mrp_repair/mrp_repair_demo.yml @@ -1,25 +1,13 @@ -- - !record {model: stock.move, id: stock_move_pcbasicpc0}: - company_id: base.main_company - date: !eval datetime.today().strftime("%Y-%m-%d %H:%M:%S") - date_expected: !eval datetime.today().strftime("%Y-%m-%d %H:%M:%S") - location_dest_id: stock.stock_location_14 - location_id: stock.stock_location_stock - name: '[PCSC234] PC Assemble SC234' - product_id: product.product_product_3 - product_qty: 1.0 - product_uom: product.product_uom_unit - product_uos_qty: 1.0 - !record {model: mrp.repair, id: mrp_repair_rmrp1}: address_id: base.res_partner_address_1 guarantee_limit: !eval datetime.today().strftime("%Y-%m-%d") invoice_method: 'none' + product_id: product.product_product_3 + product_uom: product.product_uom_unit partner_invoice_id: base.res_partner_address_1 location_dest_id: stock.stock_location_14 - location_id: stock.stock_location_14 - picking_type_id: stock.picking_type_out - move_id: 'stock_move_pcbasicpc0' + location_id: stock.stock_location_stock name: RMA00004 operations: - location_dest_id: stock.location_production @@ -39,29 +27,16 @@ product_uom: product.product_uom_unit price_unit: 50.0 partner_id: base.res_partner_9 - product_id: product.product_product_3 -- - !record {model: stock.move, id: stock.stock_move_stockmvmrp1}: - company_id: base.main_company - date: !eval datetime.today().strftime("%Y-%m-%d %H:%M:%S") - date_expected: !eval datetime.today().strftime("%Y-%m-%d %H:%M:%S") - location_dest_id: stock.stock_location_14 - location_id: stock.stock_location_stock - name: '[PC-DEM] PC on Demand' - product_id: product.product_product_5 - product_qty: 1.0 - product_uom: product.product_uom_unit - product_uos_qty: 1.0 - !record {model: mrp.repair, id: mrp_repair_rmrp0}: + product_id: product.product_product_5 + product_uom: product.product_uom_unit address_id: base.res_partner_address_1 guarantee_limit: !eval datetime.today().strftime("%Y-%m-%d") invoice_method: 'after_repair' partner_invoice_id: base.res_partner_address_1 location_dest_id: stock.stock_location_14 - location_id: stock.stock_location_14 - picking_type_id: stock.picking_type_out - move_id: 'stock.stock_move_stockmvmrp1' + location_id: stock.stock_location_stock name: RMA-00007 operations: - location_dest_id: stock.location_production @@ -81,29 +56,16 @@ product_uom: product.product_uom_unit price_unit: 50.0 partner_id: base.res_partner_9 - product_id: product.product_product_5 -- - !record {model: stock.move, id: stock.stock_move_stockmvmrp2}: - company_id: base.main_company - date: !eval datetime.today().strftime("%Y-%m-%d %H:%M:%S") - date_expected: !eval datetime.today().strftime("%Y-%m-%d %H:%M:%S") - location_dest_id: stock.stock_location_14 - location_id: stock.stock_location_stock - name: '[LCD15] 15” LCD Monitor' - product_id: product.product_product_6 - product_qty: 1.0 - product_uom: product.product_uom_unit - product_uos_qty: 1.0 - !record {model: mrp.repair, id: mrp_repair_rmrp2}: + product_id: product.product_product_6 + product_uom: product.product_uom_unit address_id: base.res_partner_address_1 guarantee_limit: !eval datetime.today().strftime("%Y-%m-%d") invoice_method: 'b4repair' partner_invoice_id: base.res_partner_address_1 - location_dest_id: stock.stock_location_14 + location_dest_id: stock.stock_location_stock location_id: stock.stock_location_14 - picking_type_id: stock.picking_type_out - move_id: 'stock.stock_move_stockmvmrp2' name: RMA-00011 operations: - location_dest_id: stock.location_production @@ -122,5 +84,4 @@ product_uom_qty: 1.0 product_uom: product.product_uom_unit price_unit: 50.0 - partner_id: base.res_partner_9 - product_id: product.product_product_6 + partner_id: base.res_partner_9 \ No newline at end of file diff --git a/addons/mrp_repair/test/test_mrp_repair_noneinv.yml b/addons/mrp_repair/test/test_mrp_repair_noneinv.yml index 4485069dc01..53a392b6bee 100644 --- a/addons/mrp_repair/test/test_mrp_repair_noneinv.yml +++ b/addons/mrp_repair/test/test_mrp_repair_noneinv.yml @@ -8,7 +8,7 @@ - !workflow {model: mrp.repair, action: repair_confirm, ref: mrp_repair_rmrp1} - - I start the repairing process by clicking on "Start Repair" button for Invoice Method 'No Invoice'. + I start the repairing process by clicking on "Start Repair" button for Invoice Method 'No Invoice'. - !workflow {model: mrp.repair, action: repair_ready, ref: mrp_repair_rmrp1} - @@ -22,9 +22,8 @@ !workflow {model: mrp.repair, action: action_repair_end, ref: mrp_repair_rmrp1} - I define Invoice Method 'No Invoice' option in this repair order. - So, I check that Invoice should not be created for this repair order. + So, I check that Invoice has not been created for this repair order. - !python {model: mrp.repair}: | repair_id = self.browse(cr, uid, [ref('mrp_repair_rmrp1')], context=context)[0] - assert not repair_id.invoice_id.id, "Invoice should not exist for this repair order" - + assert not repair_id.invoice_id.id, "Invoice should not exist for this repair order" \ No newline at end of file From 80cb45042dc7bcfbd03c2e4a3b989b1a61a2d761 Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Fri, 24 Jan 2014 14:23:30 +0100 Subject: [PATCH 11/20] [IMP] Add lot in view bzr revid: jco@openerp.com-20140124132330-hhoju51q871528h0 --- addons/mrp_repair/mrp_repair_view.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/mrp_repair/mrp_repair_view.xml b/addons/mrp_repair/mrp_repair_view.xml index 972fef71c00..a3c6ab012a1 100644 --- a/addons/mrp_repair/mrp_repair_view.xml +++ b/addons/mrp_repair/mrp_repair_view.xml @@ -49,6 +49,7 @@ + From 49d549bb392a37cd2d9e7b968a339a56ae4afd22 Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Fri, 24 Jan 2014 17:30:32 +0100 Subject: [PATCH 12/20] [IMP] New traceability button instead of down/upstream buttons bzr revid: jco@openerp.com-20140124163032-6g24pwusfla9z00n --- addons/stock/res_config.py | 2 +- addons/stock/stock.py | 39 +++++++++++++++++++++++++++++++++++++ addons/stock/stock_data.xml | 2 +- addons/stock/stock_view.xml | 7 +++---- 4 files changed, 44 insertions(+), 6 deletions(-) diff --git a/addons/stock/res_config.py b/addons/stock/res_config.py index 338e9728f66..952407f59b3 100644 --- a/addons/stock/res_config.py +++ b/addons/stock/res_config.py @@ -69,7 +69,7 @@ This installs the module product_expiry."""), help="""Allows you to create and manage your packaging dimensions and types you want to be maintained in your system."""), 'group_stock_tracking_lot': fields.boolean("Track serial number on products", implied_group='stock.group_tracking_lot', - help="""This allows you to manage products by using serial numbers. When you select a serial number on product moves, you can get the upstream or downstream traceability of that product."""), + help="""This allows you to manage products by using serial numbers. When you select a serial number on product moves, you can get the traceability of that product."""), 'group_stock_tracking_owner': fields.boolean("Manage owner on stock", implied_group='stock.group_tracking_owner', help="""This way you can receive products attributed to a certain owner. """), diff --git a/addons/stock/stock.py b/addons/stock/stock.py index 3e32eddd640..ae8db2950be 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -1162,6 +1162,45 @@ class stock_production_lot(osv.osv): ('name_ref_uniq', 'unique (name, ref)', 'The combination of Serial Number and internal reference must be unique !'), ] + def action_traceability(self, cr, uid, ids, context=None): + """ It traces the information of a product + @param self: The object pointer. + @param cr: A database cursor + @param uid: ID of the user currently logged in + @param ids: List of IDs selected + @param context: A standard dictionary + @return: A dictionary of values + """ + if not ids or len(ids) == 0: + return False #TODO give warning instead + quant_obj = self.pool.get("stock.quant") + move_obj = self.pool.get("stock.move") + quants = quant_obj.search(cr, uid, [('lot_id', 'in', ids)], context=context) + moves = set() + for quant in quant_obj.browse(cr, uid, quants, context=context): + for move in quant.history_ids: + moves.add(move.id) +# import pdb +# pdb.set_trace() + if moves: +# ir_model_data = self.pool.get('ir.model.data') +# try: +# template_id = ir_model_data.get_object_reference(cr, uid, 'stock', 'view_move_tree')[1] +# except: +# template_id = False + return { + 'domain': "[('id','in',["+','.join(map(str, list(moves)))+"])]", + 'name': 'Traceability', + 'view_mode': 'tree, form', + 'view_type': 'tree', + 'res_model': 'stock.move', + 'type': 'ir.actions.act_window', + 'nodestroy':True, + } + else: + return False + + # ---------------------------------------------------- # Move diff --git a/addons/stock/stock_data.xml b/addons/stock/stock_data.xml index 432e015600a..332c2130d05 100644 --- a/addons/stock/stock_data.xml +++ b/addons/stock/stock_data.xml @@ -78,7 +78,7 @@ Warehouse Management application installed! Manage your product inventoy and stock locations: you can control stock moves history and planning, -watch your stock valuation, and track production lots upstream and downstream (based on serial numbers).

]]>
+watch your stock valuation, and track production lots (based on serial numbers).

]]>
diff --git a/addons/stock/stock_view.xml b/addons/stock/stock_view.xml index 9ec583f89ff..ff542762c38 100644 --- a/addons/stock/stock_view.xml +++ b/addons/stock/stock_view.xml @@ -191,8 +191,7 @@
-
- + - + + + - - @@ -122,13 +122,15 @@ + - + +