← Back to team overview

openerp-expert-production team mailing list archive

Re: Refactoring procurements and stock booking at once

 

Hi,
I attach an initial patch. You can consider it a proof of concept. It doesn't 
take into account several important things such as:

- Cancel procurement when purchase order line is removed or cancelled.
- Alter procurement (stock.move) quantity when the user changes the quantity 
of purchase order line.
- Automatically split stock move lines when a stock move is marked as done and 
other stock move lines use it *partially* as booking. It always considers the 
booking is for the total amount.
- Schedule order point or schedule procurements. Currently only the button in 
stock.move is working, but it's enough to get an idea.
- The system would need several checks/constraints which have not been created 
yet.

Note also that the patch does not remove a lot of code. It basically adds 
code. So old procurement.order may still be created because we wanted to 
analyze if it was possible to create a new module (instead of a patch) for 
changing the way procurements are used.

Please, try it and let me know if the initial design fits your needs. If so, I 
think we should talk to OpenERP sa and try to push a fully working version 
into v6.1.

A Dimarts, 22 de febrer de 2011 09:55:27, Dukai Gábor va escriure:
> Hello Albert,
> 
> I very much agree with you. The current procurement object and it's
> relations are a huge source of errors. We regularly run some cleanup code
> to deal with them. In addition, they don't provide information that's not
> already available in the system.
> 
> I would be glad to test your developments with our use cases.
> 
> 
> Best regards,
> Gábor Dukai
> 
> _______________________________________________
> Mailing list: https://launchpad.net/~openerp-expert-production
> Post to     : openerp-expert-production@xxxxxxxxxxxxxxxxxxx
> Unsubscribe : https://launchpad.net/~openerp-expert-production
> More help   : https://help.launchpad.net/ListHelp

-- 
Albert Cervera i Areny
http://www.NaN-tic.com
OpenERP Partners
Tel: +34 93 553 18 03
skype: nan-oficina

http://twitter.com/albertnan 
http://www.nan-tic.com/blog
=== modified file 'mrp/mrp.py'
--- mrp/mrp.py	2011-02-04 09:03:09 +0000
+++ mrp/mrp.py	2011-02-20 15:43:33 +0000
@@ -810,7 +810,7 @@
     def _get_auto_picking(self, cr, uid, production):
         return True
 
-    def action_confirm(self, cr, uid, ids):
+    def action_confirm(self, cr, uid, ids, context=None):
         """ Confirms production order.
         @return: Newly generated picking Id.
         """
@@ -899,6 +899,10 @@
                         'state': 'waiting',
                         'company_id': production.company_id.id,
                     })
+
+                    if line.product_id.procure_method == 'make_to_order':
+                        self.pool.get('stock.move').create_procurements(cr, uid, [move_id], context)
+
                 proc_id = proc_obj.create(cr, uid, {
                     'name': (production.origin or '').split(':')[0] + ':' + production.name,
                     'origin': (production.origin or '').split(':')[0] + ':' + production.name,

=== modified file 'mrp/procurement.py'
--- mrp/procurement.py	2011-01-14 00:11:01 +0000
+++ mrp/procurement.py	2011-02-20 15:14:48 +0000
@@ -28,6 +28,103 @@
 import netsvc
 import time
 
+class stock_move(osv.osv):
+    _inherit = 'stock.move'
+    _columns = {
+        'bom_id': fields.many2one('mrp.bom', 'BoM'),
+        'property_ids': fields.many2many('mrp.property', 'stock_move_property_rel', 'move_id','property_id', 'Properties'),
+    }
+    
+    def check_produce_product(self, cr, uid, procurement, context=[]):
+        """ Finds the bill of material for the product from procurement order.
+        @return: True or False
+        """
+        properties = [x.id for x in procurement.property_ids]
+        bom_id = self.pool.get('mrp.bom')._bom_find(cr, uid, procurement.product_id.id, procurement.product_uom.id, properties)
+        if not bom_id:
+            cr.execute('update procurement_order set message=%s where id=%s', (_('No BoM defined for this product !'), procurement.id))
+            for (id, name) in self.name_get(cr, uid, procurement.id):
+                message = _("Procurement '%s' has an exception: 'No BoM defined for this product !'") % name
+                self.log(cr, uid, id, message)
+            return False
+        return True
+    
+    def get_phantom_bom_id(self, cr, uid, ids, context=None):
+        for procurement in self.browse(cr, uid, ids, context=context):
+            if procurement.move_id and procurement.move_id.product_id.supply_method=='produce' \
+                 and procurement.move_id.product_id.procure_method=='make_to_order':
+                    phantom_bom_id = self.pool.get('mrp.bom').search(cr, uid, [
+                        ('product_id', '=', procurement.move_id.product_id.id),
+                        ('bom_id', '=', False),
+                        ('type', '=', 'phantom')]) 
+                    return phantom_bom_id 
+        return False
+    
+    def action_produce_assign_product(self, cr, uid, ids, context=None):
+        """ This is action which call from workflow to assign production order to procurements
+        @return: True
+        """
+        procurement_obj = self.pool.get('procurement.order')
+        res = procurement_obj.make_mo(cr, uid, ids, context=context)
+        res = res.values()
+        return len(res) and res[0] or 0
+    
+    def create_production_orders(self, cr, uid, ids, context=None):
+        """ Make Manufacturing(production) order from procurement
+        @return: New created Production Orders procurement wise 
+        """
+        res = {}
+        company = self.pool.get('res.users').browse(cr, uid, uid, context).company_id
+        production_obj = self.pool.get('mrp.production')
+        wf_service = netsvc.LocalService("workflow")
+        procurement_obj = self.pool.get('procurement.order')
+        for procurement in self.browse(cr, uid, ids, context=context):
+            res_id = procurement.id
+            
+            if procurement.bom_id:
+                bom_id = procurement.bom_id.id
+            else:
+                bom_ids = self.pool.get('mrp.bom').search(cr, uid, [('product_id','=',procurement.product_id.id)], context=context)
+                if not bom_ids:
+                    self.write(cr, uid, [procurement.id], {
+                        'procurement_error': _('No BoM defined for this product.'),
+                    })
+                    continue
+                bom_id = bom_ids[0]
+
+            newdate = datetime.strptime(procurement.date_expected, '%Y-%m-%d %H:%M:%S') - relativedelta(days=procurement.product_id.product_tmpl_id.produce_delay or 0.0)
+            newdate = newdate - relativedelta(days=company.manufacturing_lead)
+            produce_id = production_obj.create(cr, uid, {
+                'origin': procurement.origin,
+                'product_id': procurement.product_id.id,
+                'product_qty': procurement.product_qty,
+                'product_uom': procurement.product_uom.id,
+                'product_uos_qty': procurement.product_uos and procurement.product_uos_qty or False,
+                'product_uos': procurement.product_uos and procurement.product_uos.id or False,
+                'location_src_id': procurement.location_dest_id.id,
+                'location_dest_id': procurement.location_dest_id.id,
+                'bom_id': bom_id,
+                'date_planned': newdate.strftime('%Y-%m-%d %H:%M:%S'),
+                'move_prod_id': res_id,
+                'company_id': procurement.company_id.id,
+            })
+            res[procurement.id] = produce_id
+            self.write(cr, uid, [procurement.id], {
+                'procurement_state': 'done',
+                'procurement_error': False,
+                'bom_id': bom_id,
+            }, context)
+            bom_result = production_obj.action_compute(cr, uid,
+                    [produce_id], properties=[x.id for x in procurement.property_ids])
+            wf_service.trg_validate(uid, 'mrp.production', produce_id, 'button_confirm', cr)
+            #self.write(cr, uid, [res_id], {
+                #'location_id': procurement.location_id.id
+            #}, context)
+            self.action_cancel(cr, uid, [res_id], context)
+        return res
+    
+stock_move()
+
 class procurement_order(osv.osv):
     _inherit = 'procurement.order'
     _columns = {

=== modified file 'procurement/schedulers.py'
--- procurement/schedulers.py	2011-01-14 00:11:01 +0000
+++ procurement/schedulers.py	2011-02-20 01:21:40 +0000
@@ -56,6 +56,8 @@
                 ids = procurement_obj.search(cr, uid, [], order="date_planned")
             for id in ids:
                 wf_service.trg_validate(uid, 'procurement.order', id, 'button_restart', cr)
+
+            ids = self.pool.get('stock.move').search(cr, uid, [('procurement','=',True),('procurement_state','=','pending')], context=context)
             if use_new_cursor:
                 cr.commit()
             company = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id
@@ -67,15 +69,18 @@
             report_except = 0
             report_later = 0
             while True:
-                cr.execute("select id from procurement_order where state='confirmed' and procure_method='make_to_order' order by priority,date_planned limit 500 offset %s", (offset,))
+                #cr.execute("select id from procurement_order where state='confirmed' and procure_method='make_to_order' order by priority,date_planned limit 500 offset %s", (offset,))
+                cr.execute("select id from stock_move where procurement and procurement_state='pending' order by priority,date_expected limit 500 offset %s", (offset,))
                 ids = map(lambda x: x[0], cr.fetchall())
-                for proc in procurement_obj.browse(cr, uid, ids, context=context):
-                    if maxdate >= proc.date_planned:
-                        wf_service.trg_validate(uid, 'procurement.order', proc.id, 'button_check', cr)
+                for proc in self.pool.get('stock.move').browse(cr, uid, ids, context=context):
+                    if maxdate >= proc.date_expected:
+                        # TODO: Analyze what to do with procurements in stock
+                        #wf_service.trg_validate(uid, 'procurement.order', proc.id, 'button_check', cr)
+                        self.pool.get('stock.move').process_procurements(cr, uid, [proc.id], context)
                     else:
                         offset += 1
                         report_later += 1
-                for proc in procurement_obj.browse(cr, uid, ids, context=context):
+                for proc in self.pool.get('stock.move').browse(cr, uid, ids, context=context):
                     if proc.state == 'exception':
                         report.append('PROC %d: on order - %3.2f %-5s - %s' % \
                                 (proc.id, proc.product_qty, proc.product_uom.name,
@@ -92,7 +97,7 @@
                 report_ids = []
                 ids = procurement_obj.search(cr, uid, [('state', '=', 'confirmed'), ('procure_method', '=', 'make_to_stock')], offset=offset)
                 for proc in procurement_obj.browse(cr, uid, ids):
-                    if maxdate >= proc.date_planned:
+                    if maxdate >= proc.date_expected:
                         wf_service.trg_validate(uid, 'procurement.order', proc.id, 'button_check', cr)
                         report_ids.append(proc.id)
                     else:

=== modified file 'purchase/purchase.py'
--- purchase/purchase.py	2011-02-10 18:48:06 +0000
+++ purchase/purchase.py	2011-02-20 02:30:39 +0000
@@ -425,7 +425,7 @@
             self.log(cr, uid, id, message)
         return True
 
-    def action_picking_create(self,cr, uid, ids, *args):
+    def action_picking_create(self,cr, uid, ids, context=None):
         picking_id = False
         for order in self.browse(cr, uid, ids):
             loc_id = order.partner_id.property_stock_supplier.id
@@ -447,6 +447,19 @@
             for order_line in order.order_line:
                 if not order_line.product_id:
                     continue
+
+                move_ids = []
+                source_booking_ids = []
+                target_booking_ids = []
+                for move in order_line.move_ids:
+                    source_booking_ids += [x.id for x in move.source_booking_ids]
+                    target_booking_ids += [x.id for x in move.target_booking_ids]
+                    move_ids.append( move.id )
+
+                self.pool.get('stock.move').write(cr, uid, move_ids, {
+                    'state': 'cancel',
+                }, context)
+
                 if order_line.product_id.product_tmpl_id.type in ('product', 'consu'):
                     dest = order.location_id.id
                     move = self.pool.get('stock.move').create(cr, uid, {
@@ -465,7 +478,10 @@
                         'state': 'draft',
                         'purchase_line_id': order_line.id,
                         'company_id': order.company_id.id,
-                        'price_unit': order_line.price_unit
+                        'price_unit': order_line.price_unit,
+                        'source_booking_ids': [(6,0,source_booking_ids)],
+                        'target_booking_ids': [(6,0,target_booking_ids)],
+
                     })
                     if order_line.move_dest_id:
                         self.pool.get('stock.move').write(cr, uid, [order_line.move_dest_id.id], {'location_id':order.location_id.id})
@@ -744,6 +760,88 @@
 
 purchase_order_line()
 
+class stock_move(osv.osv):
+    _inherit = 'stock.move'
+
+    def create_purchase_order(self, cr, uid, ids, context=None):
+        """ Make purchase order from procurement
+        @return: New created Purchase Orders procurement wise
+        """
+        res = {}
+        if context is None:
+            context = {}
+        company = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id
+        partner_obj = self.pool.get('res.partner')
+        uom_obj = self.pool.get('product.uom')
+        pricelist_obj = self.pool.get('product.pricelist')
+        prod_obj = self.pool.get('product.product')
+        acc_pos_obj = self.pool.get('account.fiscal.position')
+        po_obj = self.pool.get('purchase.order')
+        print "HERE!"
+        for procurement in self.browse(cr, uid, ids, context=context):
+            #res_id = procurement.move_id.id
+            partner = procurement.product_id.seller_id # Taken Main Supplier of Product of Procurement.
+            seller_qty = procurement.product_id.seller_qty
+            seller_delay = int(procurement.product_id.seller_delay)
+            partner_id = partner.id
+            address_id = partner_obj.address_get(cr, uid, [partner_id], ['delivery'])['delivery']
+            pricelist_id = partner.property_product_pricelist_purchase.id
+
+            uom_id = procurement.product_id.uom_po_id.id
+
+            qty = uom_obj._compute_qty(cr, uid, procurement.product_uom.id, procurement.product_qty, uom_id)
+            if seller_qty:
+                qty = max(qty,seller_qty)
+
+            price = pricelist_obj.price_get(cr, uid, [pricelist_id], procurement.product_id.id, qty, False, {'uom': uom_id})[pricelist_id]
+
+            newdate = datetime.strptime(procurement.date_expected, '%Y-%m-%d %H:%M:%S')
+            newdate = (newdate - relativedelta(days=company.po_lead)) - relativedelta(days=seller_delay)
+
+            #Passing partner_id to context for purchase order line integrity of Line name
+            context.update({'lang': partner.lang, 'partner_id': partner_id})
+
+            product = prod_obj.browse(cr, uid, procurement.product_id.id, context=context)
+
+            line = {
+                'name': product.partner_ref,
+                'product_qty': qty,
+                'product_id': procurement.product_id.id,
+                'product_uom': uom_id,
+                'price_unit': price,
+                'date_planned': newdate.strftime('%Y-%m-%d %H:%M:%S'),
+                # TODO: Remove ???
+                'move_dest_id': procurement.id,
+                'notes': product.description_purchase,
+            }
+
+            taxes_ids = procurement.product_id.product_tmpl_id.supplier_taxes_id
+            taxes = acc_pos_obj.map_tax(cr, uid, partner.property_account_position, taxes_ids)
+            line.update({
+                'taxes_id': [(6,0,taxes)]
+            })
+            purchase_id = po_obj.create(cr, uid, {
+                'origin': procurement.origin,
+                'partner_id': partner_id,
+                'partner_address_id': address_id,
+                # TODO: What should be set in 'location_id'
+                'location_id': procurement.location_dest_id.id,
+                'pricelist_id': pricelist_id,
+                #'order_line': [(0,0,line)],
+                'company_id': procurement.company_id.id,
+                'fiscal_position': partner.property_account_position and partner.property_account_position.id or False
+            })
+            line['order_id'] = purchase_id
+            purchase_line_id = self.pool.get('purchase.order.line').create(cr, uid, line, context)
+
+            res[procurement.id] = purchase_id
+            self.write(cr, uid, [procurement.id], {
+                'procurement_state': 'done', 
+                'purchase_line_id': purchase_line_id,
+            }, context)
+        return res
+stock_move()
+
 class procurement_order(osv.osv):
     _inherit = 'procurement.order'
     _columns = {

=== modified file 'sale/sale.py'
--- sale/sale.py	2011-01-19 08:38:17 +0000
+++ sale/sale.py	2011-02-20 15:16:09 +0000
@@ -650,7 +650,7 @@
                 return False
             return canceled
 
-    def action_ship_create(self, cr, uid, ids, *args):
+    def action_ship_create(self, cr, uid, ids, context=None):
         wf_service = netsvc.LocalService("workflow")
         picking_id = False
         move_obj = self.pool.get('stock.move')
@@ -706,6 +706,9 @@
                         'note': line.notes,
                         'company_id': order.company_id.id,
                     })
+                    # TODO: Consider services
+                    if line.type == 'make_to_order':
+                        self.pool.get('stock.move').create_procurements(cr, uid, [move_id], context)
 
                 if line.product_id:
                     proc_id = self.pool.get('procurement.order').create(cr, uid, {

=== modified file 'stock/stock.py'
--- stock/stock.py	2011-01-20 14:02:14 +0000
+++ stock/stock.py	2011-02-20 18:52:27 +0000
@@ -1554,6 +1554,17 @@
 
         # used for colors in tree views:
         'scrapped': fields.related('location_dest_id','scrap_location',type='boolean',relation='stock.location',string='Scrapped', readonly=True),
+
+        # Procurement-related fields
+
+        # TODO: Add procurement.order's property_ids for production
+        'procurement': fields.boolean('Procurement?', readonly=True, help='Check this field if the procurement must be processed.'),
+        'procurement_state': fields.selection([('pending','Pending'),('done','Done')], 'Procurement State', required=True, readonly=True, help=''),
+        'procurement_error': fields.char('Procurement Error', size=500, readonly=True, help='Reason why the procurement could not be processed.'),
+
+        'source_booking_ids': fields.one2many('stock.booking', 'target_move_id', 'Source Bookings', help='If stock for this move was reserved before execution, here there are the references to source stock moves.'),
+        'target_booking_ids': fields.one2many('stock.booking', 'source_move_id', 'Target Bookings', help='Stock moves for which current move will provide the products.'),
+
     }
     _constraints = [
         (_check_tracking,
@@ -1608,6 +1619,9 @@
         'date': time.strftime('%Y-%m-%d %H:%M:%S'),
         'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'stock.move', context=c),
         'date_expected': time.strftime('%Y-%m-%d %H:%M:%S'),
+        
+        # Procurement-related fields
+        'procurement_state': lambda *a: 'done',
     }
 
     def write(self, cr, uid, ids, vals, context=None):
@@ -2138,6 +2152,9 @@
             if move.state not in ('confirmed','done'):
                 self.action_confirm(cr, uid, move_ids, context=context)
 
+
+        self.process_target_bookings(cr, uid, move_ids, context)
+
         self.write(cr, uid, move_ids, {'state': 'done', 'date_planned': time.strftime('%Y-%m-%d %H:%M:%S')}, context=context)
         for id in move_ids:
              wf_service.trg_trigger(uid, 'stock.move', id, cr)
@@ -2147,6 +2164,95 @@
 
         return True
 
+    def process_target_bookings(self, cr, uid, ids, context):
+        for move in self.browse(cr, uid, ids, context):
+            for booking in move.target_booking_ids:
+                # TODO: Ensure it's executed if user forces (presses 'Ejecutar Ahora' in stock.move.form).
+                # TODO: Take into account booking's quantity and uom.
+                # TODO: Ensure there are no infinite loops due to booking cycles.
+                # TODO: Move production lots automatically.
+                self.check_assign(cr, uid, [booking.target_move_id.id], context)
+
+    def create_purchase_orders(self, cr, uid, ids, context):
+        self.write(cr, uid, [ids], {
+            'procurement_error': _('Can not process products of type purchase: missing module.'),
+        }, context)
+        return False
+
+    def create_production_orders(self, cr, uid, ids, context):
+        self.write(cr, uid, [ids], {
+            'procurement_error': _('Can not process products of type production: missing module.'),
+        }, context)
+        return False
+
+    def process_procurements(self, cr, uid, ids, context):
+        user = self.pool.get('res.users').browse(cr, uid, uid, context)
+        for move in self.browse(cr, uid, ids, context):
+            if not move.procurement:
+                continue
+            if not move.product_id.type in ('product','consu'):
+                continue
+            if move.procurement_state == 'done':
+                continue
+            if move.product_id.supply_method == 'buy':
+                # TODO: These checks should be moved to purchase module
+                if not move.product_id.seller_ids:
+                    self.write(cr, uid, [move.id], {
+                        'procurement_error': _('No supplier defined for this product !'),
+                    }, context)
+                    continue
+                partner = move.product_id.seller_id #Taken Main Supplier of Product of Procurement.
+                if user.company_id and user.company_id.partner_id:
+                    if partner.id == user.company_id.partner_id.id:
+                        continue
+                address_id = self.pool.get('res.partner').address_get(cr, uid, [partner.id], ['delivery'])['delivery']
+                if not address_id:
+                    self.write(cr, uid, [move.id], {
+                        'procurement_error': _("No address defined for product's supplier."), 
+                    }, context)
+                    continue
+                self.create_purchase_order(cr, uid, [move.id], context)
+                self.write(cr, uid, [move.id], {
+                    'procurement_state': 'done',
+                    'procurement_error': False,
+                }, context)
+            else:
+                self.create_production_orders(cr, uid, [move.id], context)
+        return True
+
+    def create_procurements(self, cr, uid, ids, context):
+        new_move_ids = []
+        for move in self.browse(cr, uid, ids, context):
+            new_move = self.create(cr, uid, {
+                'product_id': move.product_id.id,
+                'name': move.name,
+                'location_id': move.product_id.property_stock_procurement.id,
+                'location_dest_id': move.location_id.id,
+                'date': move.date_expected,
+                'date_expected': move.date_expected,
+                'product_qty': move.product_qty,
+                'product_uom': move.product_uom.id,
+                'product_uos_qty': move.product_uos_qty,
+                'product_uos': (move.product_uos and move.product_uos.id) or move.product_uom.id,
+                'company_id': move.company_id.id,
+                'address_id': move.address_id.id,
+                'state': 'confirmed',
+
+                # Procurement-related fields
+                'procurement': True,
+                'procurement_state': 'pending',
+            }, context)
+
+            self.pool.get('stock.booking').create(cr, uid, {
+                'source_move_id': new_move,
+                'target_move_id': move.id,
+                'quantity': move.product_qty,
+                'uom_id': move.product_uom.id,
+            }, context)
+            new_move_ids.append( new_move )
+
+        return new_move_ids
+
     def _create_account_move_line(self, cr, uid, move, src_account_id, dest_account_id, reference_amount, reference_currency_id, context=None):
         """
         Generate the account.move.line values to post to track the stock valuation difference due to the
@@ -2502,6 +2608,37 @@
 
 stock_move()
 
+class stock_booking(osv.osv):
+    _name = 'stock.booking'
+    _description = 'Stock Booking'
+
+    _columns = {
+        'source_move_id': fields.many2one('stock.move', 'Source Stock Move', required=True, ondelete='cascade', help='', domain="[('state','in',['draft','confirmed','assigned','waiting'])]"),
+        'target_move_id': fields.many2one('stock.move', 'Target Stock Move', required=True, ondelete='cascade', help='', domain="[('state','in',['draft','confirmed','assigned','waiting'])]"),
+        'quantity': fields.float('Quantity', required=True, help=''),
+        'uom_id': fields.many2one('product.uom', 'UoM', required=True, help=''),
+        'product_id': fields.related('source_move_id','product_id', type='many2one', relation='product.product', string='Product', readonly=True),
+    }
+
+    def _check_quantity(self, cr, uid, ids):
+        # TODO: Ensure source_move_id and target_move_id refer to the same product.
+        # TODO: Ensure source_move_id does not have more reserved stock than it's quantity (UoM!).
+        # TODO: Ensure target_move_id does not have more reserved stock than it's quantity.
+        # TODO: Ensure booking's quantity is not greater than source_move_id's quantity.
+        # TODO: Ensure booking's quantity is not greater than target_move_id's quantity.
+        # TODO: Ensure source_move_id.location_dest_id == target_move_id.location_id
+        # TODO: Ensure both moves have the same company_id (this is not checked anywhere else in OpenERP, do we want it here?)
+        return True
+
+    _constraints = [ 
+        (_check_quantity, 'Incorrect Quantity.', []) 
+    ]
+
+    # TODO: Take into account what should be done if source_move_id is duplicated. (copy)
+    # TODO: Take into account what should be done if target_move_id is duplicated. (copy)
+
+stock_booking()
+
 class stock_inventory(osv.osv):
     _name = "stock.inventory"
     _description = "Inventory"

=== modified file 'stock/stock_view.xml'
--- stock/stock_view.xml	2011-01-18 17:14:00 +0000
+++ stock/stock_view.xml	2011-02-20 17:04:19 +0000
@@ -1470,6 +1470,48 @@
                               string="Split" type="action" icon="terp-stock_effects-object-colorize" colspan="1"/>
                     </group>
 
+		    <group colspan="2" col="1" groups="base.group_extended">
+		    	<separator string="Back Bookings"/>
+			<field name="source_booking_ids" nolabel="1">
+				<tree string="Stock Booking">
+					<field name="source_move_id"/>
+					<field name="quantity"/>
+					<field name="uom_id"/>
+				</tree>
+				<form string="Stock Booking">
+					<field name="source_move_id"/>
+					<field name="quantity"/>
+					<field name="uom_id"/>
+				</form>
+			</field>
+		    </group>
+
+		    <group colspan="2" col="1" groups="base.group_extended">
+		    	<separator string="Forward Bookings"/>
+			<field name="target_booking_ids" nolabel="1">
+				<tree string="Stock Booking">
+					<field name="target_move_id"/>
+					<field name="quantity"/>
+					<field name="uom_id"/>
+				</tree>
+				<form string="Stock Booking">
+					<field name="target_move_id"/>
+					<field name="quantity"/>
+					<field name="uom_id"/>
+				</form>
+			</field>
+		    </group>
+
+                    <group colspan="4" groups="base.group_extended">
+		        <separator string="Procurement Information" colspan="4"/>
+			<group colspan="4" col="5">
+				<field name="procurement"/>
+				<field name="procurement_state"/>
+				<button type="object" name="process_procurements" string="Process Procurement" icon="gtk-convert"/>
+			</group>
+			<field name="procurement_error" colspan="4"/>
+		    </group>
+
                     <separator colspan="4"/>
                     <field name="state"/>
                     <group col="4" colspan="2">
@@ -1607,6 +1649,42 @@
             </field>
         </record>
 
+        <record id="view_move_tree_booking" model="ir.ui.view">
+            <field name="name">stock.booking.tree</field>
+            <field name="model">stock.booking</field>
+            <field name="type">tree</field>
+            <field name="arch" type="xml">
+	    	<tree string="Stock Booking">
+			<field name="product_id"/>
+			<field name="source_move_id"/>
+			<field name="target_move_id"/>
+			<field name="quantity"/>
+			<field name="uom_id"/>
+		</tree>
+	    </field>
+	</record>
+        <record id="view_move_form_booking" model="ir.ui.view">
+            <field name="name">stock.booking.form</field>
+            <field name="model">stock.booking</field>
+            <field name="type">form</field>
+            <field name="arch" type="xml">
+	    	<form string="Stock Booking">
+			<field name="product_id" colspan="4"/>
+			<field name="source_move_id"/>
+			<field name="target_move_id"/>
+			<field name="quantity"/>
+			<field name="uom_id"/>
+		</form>
+	    </field>
+	</record>
+        <record id="action_booking_form" model="ir.actions.act_window">
+            <field name="name">Stock Booking</field>
+            <field name="res_model">stock.booking</field>
+            <field name="type">ir.actions.act_window</field>
+            <field name="view_type">form</field>
+            <field name="view_mode">tree,form</field>
+        </record>
+        <menuitem action="action_booking_form" id="menu_action_booking_form" parent="menu_traceability" sequence="4"/>
 
         <record id="view_move_form_reception_picking" model="ir.ui.view">
             <field name="name">stock.move.form2</field>


References