diff --git a/addons/procurement/procurement.py b/addons/procurement/procurement.py index 5202eb4b9b4..225c161aa28 100644 --- a/addons/procurement/procurement.py +++ b/addons/procurement/procurement.py @@ -65,7 +65,7 @@ class StockMove(osv.osv): _inherit = 'stock.move' _columns= { 'procurements': fields.one2many('procurement.order', 'move_id', 'Procurements'), - 'group_id':fields.many2one('stock.move.group', 'Move Group'), + 'group_id':fields.many2one('procurement.group', 'Move Group'), } def copy_data(self, cr, uid, id, default=None, context=None): @@ -75,25 +75,38 @@ class StockMove(osv.osv): return super(StockMove, self).copy_data(cr, uid, id, default, context=context) -class move_group(osv.osv): +class procurement_group(osv.osv): ''' - The procurement requirement class is used to group procurement orders. - The goal is that when you have one delivery order of several products - and the products are pulled from the same or several location(s), to keep having - the moves grouped into pickings. - - As the pulled moves are created by the procurement orders who are created by moves/SO/..., - the procurement requisition will bundle these procurement orders according to the same original picking - - Suppose you have 4 lines on a picking from Output where 2 lines will need to come from Input and 2 lines coming from Stock -> Output - As the four procurement orders will have the same group ids from the SO, the move from input will have a stock.picking with 2 grouped lines - and the move from stock will have 2 grouped lines also. + The procurement requirement class is used to group products together + when computing procurements. (tasks, physical products, ...) + + The goal is that when you have one sale order of several products + and the products are pulled from the same or several location(s), to keep + having the moves grouped into pickings that represent the sale order. + + Used in: sales order (to group delivery order lines like the so), pull/push + rules (to pack like the delivery order), on orderpoints (e.g. for wave picking + all the similar products together). + + Grouping is made only if the source and the destination is the same. + Suppose you have 4 lines on a picking from Output where 2 lines will need + to come from Input (crossdock) and 2 lines coming from Stock -> Output As + the four procurement orders will have the same group ids from the SO, the + move from input will have a stock.picking with 2 grouped lines and the move + from stock will have 2 grouped lines also. + + The name is usually the name of the original document (sale order) or a + sequence computed if created manually. ''' - _name = 'stock.move.group' + _name = 'procurement.group' + _description = 'Procurement Requisition' + _order = "id desc" _columns = { - 'name': fields.char('Name'), - 'sequence_id': fields.many2one('ir.sequence', 'Group Sequence', help="Move group sequence"), - } + 'name': fields.char('Reference'), + } + _defaults = { + 'name': lambda self,cr,uid,c: self.pool.get('ir.sequence').get(cr,uid,'procurement.group') or '' + } @@ -140,7 +153,7 @@ class procurement_order(osv.osv): \nAfter confirming the status is set to \'Running\'.\n If any exception arises in the order then the status is set to \'Exception\'.\n Once the exception is removed the status becomes \'Ready\'.\n It is in \'Waiting\'. status when the procurement is waiting for another one to finish.'), 'note': fields.text('Note'), 'company_id': fields.many2one('res.company','Company',required=True), - 'group_id':fields.many2one('stock.move.group', 'Move Group'), + 'group_id':fields.many2one('procurement.group', 'Procurement Requisition'), } _defaults = { 'state': 'draft', @@ -329,8 +342,6 @@ class procurement_order(osv.osv): @return: True """ move_obj = self.pool.get('stock.move') - mod_obj = self.pool.get('ir.model.data') - location_model, location_id = mod_obj.get_object_reference(cr, uid, 'stock', 'stock_location_customers') for procurement in self.browse(cr, uid, ids, context=context): if procurement.product_qty <= 0.00: raise osv.except_osv(_('Data Insufficient!'), @@ -338,7 +349,7 @@ class procurement_order(osv.osv): if procurement.product_id.type in ('product', 'consu'): if not procurement.move_id: source = procurement.location_id.id - if procurement.procure_method == 'make_to_order':#and source != location_id: Last statement is not good + if procurement.procure_method == 'make_to_order': source = procurement.product_id.property_stock_procurement.id id = move_obj.create(cr, uid, { 'name': procurement.name, @@ -354,8 +365,7 @@ class procurement_order(osv.osv): }) move_obj.action_confirm(cr, uid, [id], context=context) self.write(cr, uid, [procurement.id], {'move_id': id, 'close_move': 1}) - self.write(cr, uid, [procurement.id], {'state': 'confirmed', 'message': ''}) - + self.write(cr, uid, ids, {'state': 'confirmed', 'message': ''}) return True def action_move_assigned(self, cr, uid, ids, context=None): diff --git a/addons/procurement/procurement_data.xml b/addons/procurement/procurement_data.xml index 6a83d525c68..872e5fde350 100644 --- a/addons/procurement/procurement_data.xml +++ b/addons/procurement/procurement_data.xml @@ -14,6 +14,20 @@ + + + Procurement Group + procurement.group + + + + Procurement Group + procurement.group + PG/ + 6 + 1 + 1 + Stock orderpoint @@ -29,4 +43,4 @@ 1 - \ No newline at end of file + diff --git a/addons/product/product.py b/addons/product/product.py index 99de24caaa3..5ee41a09545 100644 --- a/addons/product/product.py +++ b/addons/product/product.py @@ -177,10 +177,9 @@ class product_uom(osv.osv): return qty amount = qty / from_unit.factor if to_unit: + amount = amount * to_unit.factor if round: - amount = rounding(amount * to_unit.factor, to_unit.rounding) - else: - amount = amount * to_unit.factor + amount = rounding(amount, to_unit.rounding) return amount def _compute_price(self, cr, uid, from_uom_id, price, to_uom_id=False): diff --git a/addons/product_expiry/product_expiry_demo.xml b/addons/product_expiry/product_expiry_demo.xml index 85539d16b14..b644671a215 100644 --- a/addons/product_expiry/product_expiry_demo.xml +++ b/addons/product_expiry/product_expiry_demo.xml @@ -34,7 +34,7 @@ buy 8.0 - + 6.0 diff --git a/addons/purchase/res_config.py b/addons/purchase/res_config.py index 33858fc0460..ffbbbfd974e 100644 --- a/addons/purchase/res_config.py +++ b/addons/purchase/res_config.py @@ -39,7 +39,7 @@ class purchase_config_settings(osv.osv_memory): 'group_uom':fields.boolean("Manage different units of measure for products", implied_group='product.group_uom', help="""Allows you to select and maintain different units of measure for products."""), - 'group_costing_method':fields.boolean("Compute product cost price based on average/FIFO/LIFO cost", + 'group_costing_method':fields.boolean("The default costing method applied to all products", implied_group='product.group_costing_method', help="""Allows you to compute product cost price based on average cost."""), 'module_warning': fields.boolean("Alerts by products or supplier", diff --git a/addons/purchase/stock.py b/addons/purchase/stock.py index a5c73763309..794ff220514 100644 --- a/addons/purchase/stock.py +++ b/addons/purchase/stock.py @@ -125,10 +125,9 @@ class stock_partial_picking(osv.osv_memory): _inherit = 'stock.partial.picking' # Overridden to inject the purchase price as true 'cost price' when processing - # incoming pickings. + # incoming pickings. The price is always stored in the company currency. def _product_cost_for_average_update(self, cr, uid, move): if move.picking_id.purchase_id: - #always write the cost price of products in the company currency, for easiness in further computation currency_obj = self.pool.get("res.currency") new_price = currency_obj.compute(cr, uid, move.picking_id.purchase_id.pricelist_id.currency_id.id, move.company_id.currency_id.id, move.purchase_line_id.price_unit, round=False) diff --git a/addons/sale_stock/sale_stock.py b/addons/sale_stock/sale_stock.py index e282e63c3ca..c6fac18adcb 100644 --- a/addons/sale_stock/sale_stock.py +++ b/addons/sale_stock/sale_stock.py @@ -27,16 +27,6 @@ from openerp.tools.translate import _ import pytz from openerp import SUPERUSER_ID - -#COULD BE INTERESTING WHEN PROCUREMENT TO CUSTOMER AND PULL RULE -class procurement_order(osv.osv): - _inherit = 'procurement.order' - _columns = { - 'sale_line_id': fields.many2one('sale.order.line', 'Sale order line'), - } - - - class sale_order(osv.osv): _inherit = "sale.order" @@ -308,7 +298,6 @@ class sale_order(osv.osv): def _prepare_order_line_procurement(self, cr, uid, order, line, move_id, date_planned, group_id = False, context=None): mod_obj = self.pool.get('ir.model.data') location_model, location_id = mod_obj.get_object_reference(cr, uid, 'stock', 'stock_location_customers') - output_id = order.warehouse_id.lot_output_id.id return { 'name': line.name, 'origin': order.name, @@ -320,80 +309,13 @@ class sale_order(osv.osv): or line.product_uom_qty, 'product_uos': (line.product_uos and line.product_uos.id)\ or line.product_uom.id, - 'location_id': order.warehouse_id.lot_stock_id.id, #TODO Procurement should be generated towards customers instead + 'location_id': location_id, 'procure_method': line.type, 'move_id': move_id, 'company_id': order.company_id.id, 'note': line.name, 'group_id': group_id, } - - def _prepare_order_line_move(self, cr, uid, order, line, picking_id, date_planned, group_id = False, context=None): - location_id = order.warehouse_id.lot_stock_id.id - output_id = order.warehouse_id.lot_output_id.id - #mod_obj = self.pool.get('ir.model.data') - #location_model, location_id = mod_obj.get_object_reference(cr, uid, 'stock', 'stock_location_customers') - return { - 'name': line.name, - 'picking_id': picking_id, - 'product_id': line.product_id.id, - 'date': date_planned, - 'date_expected': date_planned, - 'product_qty': line.product_uom_qty, - 'product_uom': line.product_uom.id, - 'product_uos_qty': (line.product_uos and line.product_uos_qty) or line.product_uom_qty, - 'product_uos': (line.product_uos and line.product_uos.id)\ - or line.product_uom.id, - 'product_packaging': line.product_packaging.id, - 'partner_id': line.address_allotment_id.id or order.partner_shipping_id.id, - 'location_id': location_id, - 'location_dest_id': output_id, - 'sale_line_id': line.id, - 'tracking_id': False, - 'state': 'draft', - #'state': 'waiting', - 'company_id': order.company_id.id, - 'price_unit': line.product_id.standard_price or 0.0, - 'group_id': group_id, - } - - def _prepare_order_picking(self, cr, uid, order, context=None): - pick_name = self.pool.get('ir.sequence').get(cr, uid, 'stock.picking.out') - return { - 'name': pick_name, - 'origin': order.name, - 'date': self.date_to_datetime(cr, uid, order.date_order, context), - 'type': 'out', - 'state': 'auto', - 'move_type': order.picking_policy, - 'sale_id': order.id, - 'partner_id': order.partner_shipping_id.id, - 'note': order.note, - 'invoice_state': (order.order_policy=='picking' and '2binvoiced') or 'none', - 'company_id': order.company_id.id, - } - - def ship_recreate(self, cr, uid, order, line, move_id, proc_id): - # FIXME: deals with potentially cancelled shipments, seems broken (specially if shipment has production lot) - """ - Define ship_recreate for process after shipping exception - param order: sales order to which the order lines belong - param line: sales order line records to procure - param move_id: the ID of stock move - param proc_id: the ID of procurement - """ - move_obj = self.pool.get('stock.move') - if order.state == 'shipping_except': - for pick in order.picking_ids: - for move in pick.move_lines: - if move.state == 'cancel': - mov_ids = move_obj.search(cr, uid, [('state', '=', 'cancel'),('sale_line_id', '=', line.id),('picking_id', '=', pick.id)]) - if mov_ids: - for mov in move_obj.browse(cr, uid, mov_ids): - # FIXME: the following seems broken: what if move_id doesn't exist? What if there are several mov_ids? Shouldn't that be a sum? - move_obj.write(cr, uid, [move_id], {'product_qty': mov.product_qty, 'product_uos_qty': mov.product_uos_qty}) - self.pool.get('procurement.order').write(cr, uid, [proc_id], {'product_qty': mov.product_qty, 'product_uos_qty': mov.product_uos_qty}) - return True def _get_date_planned(self, cr, uid, order, line, start_date, context=None): start_date = self.date_to_datetime(cr, uid, start_date, context) @@ -401,7 +323,6 @@ class sale_order(osv.osv): date_planned = (date_planned - timedelta(days=order.company_id.security_lead)).strftime(DEFAULT_SERVER_DATETIME_FORMAT) return date_planned - def _create_pickings_and_procurements(self, cr, uid, order, order_lines, picking_id=False, context=None): """Create the required procurements to supply sales order lines, also connecting the procurements to appropriate stock moves in order to bring the goods to the @@ -421,14 +342,12 @@ class sale_order(osv.osv): will be added. A new picking will be created if ommitted. :return: True """ - print "CREATE OLD PICKINGS AND PROCUREMENTS" move_obj = self.pool.get('stock.move') picking_obj = self.pool.get('stock.picking') procurement_obj = self.pool.get('procurement.order') proc_ids = [] - #Create group - group_id = self.pool.get("stock.move.group").create(cr, uid, {'name': order.name}, context=context) + group_id = self.pool.get("procurement.group").create(cr, uid, {'name': order.name}, context=context) for line in order_lines: if line.state == 'done': @@ -436,24 +355,11 @@ class sale_order(osv.osv): date_planned = self._get_date_planned(cr, uid, order, line, order.date_order, context=context) - if line.product_id: - if line.product_id.type in ('product', 'consu'): - if not picking_id: - picking_id = picking_obj.create(cr, uid, self._prepare_order_picking(cr, uid, order, context=context)) - move_id = move_obj.create(cr, uid, self._prepare_order_line_move(cr, uid, order, line, picking_id, date_planned, group_id=group_id, context=context)) - else: - # a service has no stock move - move_id = False - - #TODO Need to do something instead of warehouse if line.product_id: proc_id = procurement_obj.create(cr, uid, self._prepare_order_line_procurement(cr, uid, order, line, move_id, date_planned, group_id = group_id, context=context)) proc_ids.append(proc_id) line.write({'procurement_id': proc_id}) - self.ship_recreate(cr, uid, order, line, move_id, proc_id) - if picking_id: - picking_obj.signal_button_confirm(cr, uid, [picking_id]) procurement_obj.signal_button_confirm(cr, uid, proc_ids) val = {} diff --git a/addons/stock_location_sale/stock_location_sale.py b/addons/stock_location_sale/stock_location_sale.py index 4b06962f18f..eed8b32631c 100644 --- a/addons/stock_location_sale/stock_location_sale.py +++ b/addons/stock_location_sale/stock_location_sale.py @@ -65,7 +65,7 @@ class sale_order(osv.osv): proc_ids = [] #Create group - group_id = self.pool.get("stock.move.group").create(cr, uid, {'name': order.name}, context=context) + group_id = self.pool.get("procurement.group").create(cr, uid, {'name': order.name}, context=context) for line in order_lines: if line.state == 'done': @@ -130,4 +130,4 @@ class sale_order(osv.osv): 'note': line.name, 'group_id': group_id, 'state': 'draft', - } \ No newline at end of file + }