[IMP] Yet another 1000 lines removed

bzr revid: fp@tinyerp.com-20130630222646-wrpyd1o89md8gknx
This commit is contained in:
Fabien Pinckaers 2013-07-01 00:26:46 +02:00
parent e61887ffae
commit 8f6c3e14b9
37 changed files with 577 additions and 1508 deletions

View File

@ -29,6 +29,34 @@ from openerp.tools import float_compare
from openerp.tools.translate import _
from openerp import tools
class mrp_property_group(osv.osv):
"""
Group of mrp properties.
"""
_name = 'mrp.property.group'
_description = 'Property Group'
_columns = {
'name': fields.char('Property Group', size=64, required=True),
'description': fields.text('Description'),
}
class mrp_property(osv.osv):
"""
Properties of mrp.
"""
_name = 'mrp.property'
_description = 'Property'
_columns = {
'name': fields.char('Name', size=64, required=True),
'composition': fields.selection([('min','min'),('max','max'),('plus','plus')], 'Properties composition', required=True, help="Not used in computations, for information purpose only."),
'group_id': fields.many2one('mrp.property.group', 'Property Group', required=True),
'description': fields.text('Description'),
}
_defaults = {
'composition': lambda *a: 'min',
}
#----------------------------------------------------------
# Work Centers
#----------------------------------------------------------

View File

@ -78,3 +78,7 @@ access_resource_calendar_manufacturinguser,resource.calendar manufacturing.user,
access_account_journal_mrp_manager,account.journal mrp manager,account.model_account_journal,mrp.group_mrp_manager,1,0,0,0
access_purchase_order_stock_user,purchase.order stock user,purchase.model_purchase_order,stock.group_stock_user,1,1,1,0
access_mrp_bom_purchase_manager,mrp.bom,model_mrp_bom,purchase.group_purchase_manager,1,0,0,0
access_mrp_property_group,mrp.property.group,model_mrp_property_group,stock.group_stock_manager,1,1,1,1
access_mrp_property,mrp.property,model_mrp_property,stock.group_stock_manager,1,1,1,1
access_mrp_property_group,mrp.property.group,model_mrp_property_group,base.group_user,1,0,0,0
access_mrp_property,mrp.property,model_mrp_property,base.group_user,1,0,0,0

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
78 access_account_journal_mrp_manager account.journal mrp manager account.model_account_journal mrp.group_mrp_manager 1 0 0 0
79 access_purchase_order_stock_user purchase.order stock user purchase.model_purchase_order stock.group_stock_user 1 1 1 0
80 access_mrp_bom_purchase_manager mrp.bom model_mrp_bom purchase.group_purchase_manager 1 0 0 0
81 access_mrp_property_group mrp.property.group model_mrp_property_group stock.group_stock_manager 1 1 1 1
82 access_mrp_property mrp.property model_mrp_property stock.group_stock_manager 1 1 1 1
83 access_mrp_property_group mrp.property.group model_mrp_property_group base.group_user 1 0 0 0
84 access_mrp_property mrp.property model_mrp_property base.group_user 1 0 0 0

View File

@ -21,7 +21,5 @@
import procurement
import wizard
import schedulers
import company
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -26,7 +26,7 @@
'author' : 'OpenERP SA',
'website' : 'http://www.openerp.com',
'category' : 'Hidden/Dependency',
'depends' : ['base','process', 'product', 'stock'],
'depends' : ['base','process', 'product'],
'description': """
This is the module for computing Procurements.
==============================================
@ -47,20 +47,13 @@ depending on the product's configuration.
'security/ir.model.access.csv',
'security/procurement_security.xml',
'procurement_data.xml',
'wizard/make_procurement_view.xml',
'wizard/mrp_procurement_view.xml',
'wizard/orderpoint_procurement_view.xml',
'wizard/schedulers_all_view.xml',
'procurement_view.xml',
'procurement_workflow.xml',
'process/procurement_process.xml',
'company_view.xml',
'board_mrp_procurement_view.xml',
],
'demo': ['stock_orderpoint.xml','procurement_demo.xml'],
'demo': [],
'test': ['test/procurement.yml'],
'installable': True,
'auto_install': True,
'images': ['images/compute_schedulers.jpeg','images/config_companies_sched.jpeg', 'images/minimum_stock_rules.jpeg'],
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -1,24 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="procurement_action_board" model="ir.actions.act_window">
<field name="name">Procurement Exceptions</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">procurement.order</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="domain">[('state','=','exception')]</field>
<field name="view_id" ref="procurement.procurement_tree_view_board"/>
</record>
<record id="board_mrp_procurement_form" model="ir.ui.view">
<field name="name">board.mrp.procurement.form</field>
<field name="model">board.board</field>
<field name="inherit_id" ref="stock.board_warehouse_form"/>
<field name="arch" type="xml">
<xpath expr="//column" position="inside">
<action name="%(procurement_action_board)d" string="Procurements in Exception"/>
</xpath>
</field>
</record>
</data>
</openerp>

View File

@ -1,42 +0,0 @@
<?xml version="1.0" ?>
<openerp>
<data>
<!--
Process Node from where the Procurement flow starts
-->
<record id="process_process_procurementprocess0" model="process.process">
<field eval="&quot;&quot;&quot;Procurement&quot;&quot;&quot;" name="name"/>
<field name="model_id" ref="procurement.model_procurement_order"/>
<field eval="1" name="active"/>
</record>
<record id="process_node_procureproducts0" model="process.node">
<field name="menu_id" ref="menu_stock_procurement_action"/>
<field name="model_id" ref="procurement.model_procurement_order"/>
<field eval="&quot;&quot;&quot;state&quot;&quot;&quot;" name="kind"/>
<field eval="&quot;&quot;&quot;The way to procurement depends on the product type.&quot;&quot;&quot;" name="note"/>
<field eval="&quot;&quot;&quot;Procure Products&quot;&quot;&quot;" name="name"/>
<field name="process_id" ref="procurement.process_process_procurementprocess0"/>
<field eval="&quot;&quot;&quot;object.state in ('draft', 'confirmed', 'cancel', 'exception', 'running', 'done', 'waiting')&quot;&quot;&quot;" name="model_states"/>
<field eval="0" name="flow_start"/>
</record>
<record id="process_process_serviceproductprocess0" model="process.process">
<field eval="&quot;&quot;&quot;Service&quot;&quot;&quot;" name="name"/>
<field name="model_id" ref="procurement.model_procurement_order"/>
<field eval="1" name="active"/>
</record>
<record id="process_node_serviceonorder0" model="process.node">
<field name="menu_id" ref="menu_stock_procurement_action"/>
<field name="model_id" ref="procurement.model_procurement_order"/>
<field eval="&quot;&quot;&quot;state&quot;&quot;&quot;" name="kind"/>
<field eval="&quot;&quot;&quot;Assignment from Production or Purchase Order.&quot;&quot;&quot;" name="note"/>
<field eval="&quot;&quot;&quot;Make to Order&quot;&quot;&quot;" name="name"/>
<field name="process_id" ref="procurement.process_process_serviceproductprocess0"/>
<field eval="&quot;&quot;&quot;object.state in ('draft', 'confirmed', 'cancel', 'exception', 'running', 'done', 'waiting')&quot;&quot;&quot;" name="model_states"/>
<field eval="0" name="flow_start"/>
</record>
</data>
</openerp>

View File

@ -19,62 +19,14 @@
#
##############################################################################
from operator import attrgetter
import time
from datetime import datetime
from dateutil.relativedelta import relativedelta
from openerp.osv import fields, osv
from openerp.tools.translate import _
from openerp import netsvc
import openerp.addons.decimal_precision as dp
# Procurement
# ------------------------------------------------------------------
#
# Produce, Buy or Find products and place a move
# then wizard for picking lists & move
#
class mrp_property_group(osv.osv):
"""
Group of mrp properties.
"""
_name = 'mrp.property.group'
_description = 'Property Group'
_columns = {
'name': fields.char('Property Group', size=64, required=True),
'description': fields.text('Description'),
}
class mrp_property(osv.osv):
"""
Properties of mrp.
"""
_name = 'mrp.property'
_description = 'Property'
_columns = {
'name': fields.char('Name', size=64, required=True),
'composition': fields.selection([('min','min'),('max','max'),('plus','plus')], 'Properties composition', required=True, help="Not used in computations, for information purpose only."),
'group_id': fields.many2one('mrp.property.group', 'Property Group', required=True),
'description': fields.text('Description'),
}
_defaults = {
'composition': lambda *a: 'min',
}
class StockMove(osv.osv):
_inherit = 'stock.move'
_columns= {
'procurements': fields.one2many('procurement.order', 'move_id', 'Procurements'),
'group_id':fields.many2one('procurement.group', 'Move Group'),
}
def copy_data(self, cr, uid, id, default=None, context=None):
if default is None:
default = {}
default['procurements'] = []
return super(StockMove, self).copy_data(cr, uid, id, default, context=context)
class procurement_group(osv.osv):
'''
The procurement requirement class is used to group products together
@ -108,6 +60,21 @@ class procurement_group(osv.osv):
'name': lambda self,cr,uid,c: self.pool.get('ir.sequence').get(cr,uid,'procurement.group') or ''
}
class procurement_rule(osv.osv):
'''
A rule describe what a procurement should do; produce, buy, move, ...
'''
_name = 'procurement.rule'
_description = "Procurement Rule"
_columns = {
'name': fields.char('Name', required=True,
help="This field will fill the packing origin and the name of its moves"),
'group_id': fields.many2one('procurement.group', 'Procurement Group'),
'action': fields.selection(selection=lambda s, c, u, ctx: s._get_action(c, u, context=ctx),
string='Action', required=True)
}
def _get_action(self, cr, uid, context=None):
return []
class procurement_order(osv.osv):
@ -121,61 +88,47 @@ class procurement_order(osv.osv):
_log_create = False
_columns = {
'name': fields.text('Description', required=True),
'origin': fields.char('Source Document', size=64,
help="Reference of the document that created this Procurement.\n"
"This is automatically completed by OpenERP."),
'company_id': fields.many2one('res.company','Company',required=True),
# These two fields are used for shceduling
'priority': fields.selection([('0','Not urgent'),('1','Normal'),('2','Urgent'),('3','Very Urgent')], 'Priority', required=True, select=True),
'date_planned': fields.datetime('Scheduled date', required=True, select=True),
'date_close': fields.datetime('Date Closed'),
'group_id':fields.many2one('procurement.group', 'Procurement Requisition'),
'rule_id': fields.many2one('procurement.rule', 'Rule'),
'product_id': fields.many2one('product.product', 'Product', required=True, states={'draft':[('readonly',False)]}, readonly=True),
'product_qty': fields.float('Quantity', digits_compute=dp.get_precision('Product Unit of Measure'), required=True, states={'draft':[('readonly',False)]}, readonly=True),
'product_uom': fields.many2one('product.uom', 'Product Unit of Measure', required=True, states={'draft':[('readonly',False)]}, readonly=True),
'product_uos_qty': fields.float('UoS Quantity', states={'draft':[('readonly',False)]}, readonly=True),
'product_uos': fields.many2one('product.uom', 'Product UoS', states={'draft':[('readonly',False)]}, readonly=True),
'close_move': fields.boolean('Close Move at end'),
'move_id': fields.many2one('stock.move', 'Reservation', ondelete='set null'),
'location_id': fields.many2one('stock.location', 'Location', required=True, states={'draft':[('readonly',False)]}, readonly=True),
'procure_method': fields.selection([('make_to_stock','Make to Stock'),('make_to_order','Make to Order')], 'Procurement Method', states={'draft':[('readonly',False)], 'confirmed':[('readonly',False)]},
'procure_method': fields.selection([('make_to_stock','Make to Stock'),('make_to_order','Make to Order')],
'Procurement Method', states={'draft':[('readonly',False)], 'confirmed':[('readonly',False)]},
readonly=True, required=True, help="If you encode manually a Procurement, you probably want to use" \
" a make to order method."),
'note': fields.text('Note'),
'message': fields.text('Latest error', help="Exception occurred while computing procurement orders."),
'state': fields.selection([
('draft','Draft'),
('cancel','Cancelled'),
('confirmed','Confirmed'),
('exception','Exception'),
('running','Running'),
('ready','Ready'),
('done','Done'),
('waiting','Waiting')], 'Status', required=True, track_visibility='onchange',
help='When a procurement is created the status is set to \'Draft\'.\n If the procurement is confirmed, the status is set to \'Confirmed\'.\
\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('procurement.group', 'Procurement Requisition'),
('done','Done')
], 'Status', required=True, track_visibility='onchange'),
}
_defaults = {
'state': 'draft',
'state': 'confirmed',
'priority': '1',
'date_planned': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
'close_move': 0,
'procure_method': 'make_to_order',
'company_id': lambda self, cr, uid, c: self.pool.get('res.company')._company_default_get(cr, uid, 'procurement.order', context=c)
}
def unlink(self, cr, uid, ids, context=None):
procurements = self.read(cr, uid, ids, ['state'], context=context)
unlink_ids = []
for s in procurements:
if s['state'] in ['draft','cancel']:
unlink_ids.append(s['id'])
else:
raise osv.except_osv(_('Invalid Action!'),
_('Cannot delete Procurement Order(s) which are in %s state.') % \
s['state'])
return osv.osv.unlink(self, cr, uid, unlink_ids, context=context)
def onchange_product_id(self, cr, uid, ids, product_id, context=None):
""" Finds UoM and UoS of changed product.
@param product_id: Changed id of product.
@ -190,455 +143,83 @@ class procurement_order(osv.osv):
return {'value': v}
return {}
def is_product(self, cr, uid, ids, context=None):
""" Checks product type to decide which transition of the workflow to follow.
@return: True if all product ids received in argument are of type 'product' or 'consummable'. False if any is of type 'service'
"""
return all(proc.product_id.type in ('product', 'consu') for proc in self.browse(cr, uid, ids, context=context))
def check_move_cancel(self, cr, uid, ids, context=None):
""" Checks if move is cancelled or not.
@return: True or False.
"""
return all(procurement.move_id.state == 'cancel' for procurement in self.browse(cr, uid, ids, context=context))
#This Function is create to avoid a server side Error Like 'ERROR:tests.mrp:name 'check_move' is not defined'
def check_move(self, cr, uid, ids, context=None):
pass
def check_move_done(self, cr, uid, ids, context=None):
""" Checks if move is done or not.
@return: True or False.
"""
return all(proc.product_id.type == 'service' or (proc.move_id and proc.move_id.state == 'done') \
for proc in self.browse(cr, uid, ids, context=context))
#
# This method may be overrided by objects that override procurement.order
# for computing their own purpose
#
def _quantity_compute_get(self, cr, uid, proc, context=None):
""" Finds sold quantity of product.
@param proc: Current procurement.
@return: Quantity or False.
"""
if proc.product_id.type == 'product' and proc.move_id:
if proc.move_id.product_uos:
return proc.move_id.product_uos_qty
return False
def _uom_compute_get(self, cr, uid, proc, context=None):
""" Finds UoS if product is Stockable Product.
@param proc: Current procurement.
@return: UoS or False.
"""
if proc.product_id.type == 'product' and proc.move_id:
if proc.move_id.product_uos:
return proc.move_id.product_uos.id
return False
#
# Return the quantity of product shipped/produced/served, which may be
# different from the planned quantity
#
def quantity_get(self, cr, uid, id, context=None):
""" Finds quantity of product used in procurement.
@return: Quantity of product.
"""
proc = self.browse(cr, uid, id, context=context)
result = self._quantity_compute_get(cr, uid, proc, context=context)
if not result:
result = proc.product_qty
return result
def uom_get(self, cr, uid, id, context=None):
""" Finds UoM of product used in procurement.
@return: UoM of product.
"""
proc = self.browse(cr, uid, id, context=context)
result = self._uom_compute_get(cr, uid, proc, context=context)
if not result:
result = proc.product_uom.id
return result
def check_waiting(self, cr, uid, ids, context=None):
""" Checks state of move.
@return: True or False
"""
for procurement in self.browse(cr, uid, ids, context=context):
if procurement.move_id and procurement.move_id.state == 'auto':
return True
return False
def check_produce_service(self, cr, uid, procurement, context=None):
""" Depicts the capacity of the procurement workflow to deal with production of services.
By default, it's False. Overwritten by project_mrp module.
"""
return False
def check_produce_product(self, cr, uid, procurement, context=None):
""" Depicts the capacity of the procurement workflow to deal with production of products.
By default, it's False. Overwritten by mrp module.
"""
return False
def check_make_to_stock(self, cr, uid, ids, context=None):
""" Checks product type.
@return: True or False
"""
ok = True
for procurement in self.browse(cr, uid, ids, context=context):
if procurement.product_id.type == 'service':
ok = ok and self._check_make_to_stock_service(cr, uid, procurement, context)
def run(self, cr, uid, ids, context=None):
for procurement in self.browse(cr, uid, ids, context=context or {}):
if procurement.procure_method=='make_to_order':
result = self._run(cr, uid, procurement.id, context=context or {})
else:
ok = ok and self._check_make_to_stock_product(cr, uid, procurement, context)
return ok
def check_produce(self, cr, uid, ids, context=None):
""" Checks product type.
@return: True or False
"""
user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
for procurement in self.browse(cr, uid, ids, context=context):
product = procurement.product_id
#TOFIX: if product type is 'service' but supply_method is 'buy'.
if product.supply_method <> 'produce':
return False
if product.type=='service':
res = self.check_produce_service(cr, uid, procurement, context)
result = True
if result:
self.write(cr, uid, [procurement.id], {'state': 'running'}, context=context)
else:
res = self.check_produce_product(cr, uid, procurement, context)
if not res:
return False
self.write(cr, uid, [procurement.id], {'state': 'exception'}, context=context)
return True
def check_buy(self, cr, uid, ids):
""" Depicts the capacity of the procurement workflow to manage the supply_method == 'buy'.
By default, it's False. Overwritten by purchase module.
"""
return False
def check(self, cr, uid, ids, context=None):
done = []
for procurement in self.browse(cr, uid, ids, context=context or {}):
result = self._check(cr, uid, procurement.id, context=context or {})
if result:
self.write(cr, uid, [procurement.id], {'state': 'done'}, context=context)
done.append(procurement.id)
return done
def check_conditions_confirm2wait(self, cr, uid, ids):
""" condition on the transition to go from 'confirm' activity to 'confirm_wait' activity """
return not self.test_cancel(cr, uid, ids)
def test_cancel(self, cr, uid, ids):
""" Tests whether state of move is cancelled or not.
@return: True or False
"""
for record in self.browse(cr, uid, ids):
if record.move_id and record.move_id.state == 'cancel':
return True
return False
#Initialize get_phantom_bom_id method as it is raising an error from yml of mrp_jit
#when one install first mrp and after that, mrp_jit. get_phantom_bom_id defined in mrp module
#which is not dependent for mrp_jit.
def get_phantom_bom_id(self, cr, uid, ids, context=None):
return False
def action_confirm(self, cr, uid, ids, context=None):
""" Confirms procurement and writes exception message if any.
@return: True
"""
move_obj = self.pool.get('stock.move')
for procurement in self.browse(cr, uid, ids, context=context):
if procurement.product_qty <= 0.00:
raise osv.except_osv(_('Data Insufficient!'),
_('Please check the quantity in procurement order(s) for the product "%s", it should not be 0 or less!' % procurement.product_id.name))
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':
source = procurement.product_id.property_stock_procurement.id
id = move_obj.create(cr, uid, {
'name': procurement.name,
'location_id': source,
'location_dest_id': procurement.location_id.id,
'product_id': procurement.product_id.id,
'product_qty': procurement.product_qty,
'product_uom': procurement.product_uom.id,
'date_expected': procurement.date_planned,
'state': 'draft',
'company_id': procurement.company_id.id,
'auto_validate': True,
})
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, ids, {'state': 'confirmed', 'message': ''})
#
# Method to overwrite in different procurement modules
#
def _run(self, cr, uid, procurement, context=None):
return True
def action_move_assigned(self, cr, uid, ids, context=None):
""" Changes procurement state to Running and writes message.
@return: True
"""
message = _('Products reserved from stock.')
self.write(cr, uid, ids, {'state': 'running',
'message': message}, context=context)
self.message_post(cr, uid, ids, body=message, context=context)
def _check(self, cr, uid, procurement, context=None):
return True
def _check_make_to_stock_service(self, cr, uid, procurement, context=None):
"""
This method may be overrided by objects that override procurement.order
for computing their own purpose
@return: True"""
return True
#
# Scheduler
#
def run_scheduler(self, cr, uid, use_new_cursor=False, context=None):
'''
Call the scheduler to check the procurement order
def _check_make_to_stock_product(self, cr, uid, procurement, context=None):
""" Checks procurement move state.
@param procurement: Current procurement.
@return: True or move id.
"""
ok = True
if procurement.move_id:
message = False
id = procurement.move_id.id
if not (procurement.move_id.state in ('done','assigned','cancel')):
ok = ok and self.pool.get('stock.move').action_assign(cr, uid, [id])
order_point_id = self.pool.get('stock.warehouse.orderpoint').search(cr, uid, [('product_id', '=', procurement.product_id.id)], context=context)
if not order_point_id and not ok:
message = _("Not enough stock and no minimum orderpoint rule defined.")
elif not ok:
message = _("Not enough stock.")
if message:
message = _("Procurement '%s' is in exception: ") % (procurement.name) + message
#temporary context passed in write to prevent an infinite loop
ctx_wkf = dict(context or {})
ctx_wkf['workflow.trg_write.%s' % self._name] = False
self.write(cr, uid, [procurement.id], {'message': message},context=ctx_wkf)
self.message_post(cr, uid, [procurement.id], body=message, context=context)
return ok
def step_workflow(self, cr, uid, ids, context=None):
""" Don't trigger workflow for the element specified in trigger """
wkf_op_key = 'workflow.trg_write.%s' % self._name
if context and not context.get(wkf_op_key, True):
# make sure we don't have a trigger loop while processing triggers
return
return super(procurement_order, self).step_workflow(cr, uid, ids, context=context)
def action_produce_assign_service(self, cr, uid, ids, context=None):
""" Changes procurement state to Running.
@return: True
"""
for procurement in self.browse(cr, uid, ids, context=context):
self.write(cr, uid, [procurement.id], {'state': 'running'})
return True
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
"""
return 0
def action_po_assign(self, cr, uid, ids, context=None):
""" This is action which call from workflow to assign purchase order to procurements
@return: True
"""
return 0
# XXX action_cancel() should accept a context argument
def action_cancel(self, cr, uid, ids):
"""Cancel Procurements and either cancel or assign the related Stock Moves, depending on the procurement configuration.
@return: True
"""
to_assign = []
to_cancel = []
move_obj = self.pool.get('stock.move')
for proc in self.browse(cr, uid, ids):
if proc.close_move and proc.move_id:
if proc.move_id.state not in ('done', 'cancel'):
to_cancel.append(proc.move_id.id)
else:
if proc.move_id and proc.move_id.state == 'waiting':
to_assign.append(proc.move_id.id)
if len(to_cancel):
move_obj.action_cancel(cr, uid, to_cancel)
if len(to_assign):
move_obj.write(cr, uid, to_assign, {'state': 'assigned'})
self.write(cr, uid, ids, {'state': 'cancel'})
wf_service = netsvc.LocalService("workflow")
for id in ids:
wf_service.trg_trigger(uid, 'procurement.order', id, cr)
return True
def action_check_finished(self, cr, uid, ids):
return self.check_move_done(cr, uid, ids)
def action_check(self, cr, uid, ids):
""" Checks procurement move state whether assigned or done.
@return: True
"""
ok = False
for procurement in self.browse(cr, uid, ids):
if procurement.move_id and procurement.move_id.state == 'assigned' or procurement.move_id.state == 'done':
self.action_done(cr, uid, [procurement.id])
ok = True
return ok
def action_ready(self, cr, uid, ids):
""" Changes procurement state to Ready.
@return: True
"""
res = self.write(cr, uid, ids, {'state': 'ready'})
return res
def action_done(self, cr, uid, ids):
""" Changes procurement state to Done and writes Closed date.
@return: True
"""
move_obj = self.pool.get('stock.move')
for procurement in self.browse(cr, uid, ids):
if procurement.move_id:
if procurement.close_move and (procurement.move_id.state <> 'done'):
move_obj.action_done(cr, uid, [procurement.move_id.id])
res = self.write(cr, uid, ids, {'state': 'done', 'date_close': time.strftime('%Y-%m-%d')})
wf_service = netsvc.LocalService("workflow")
for id in ids:
wf_service.trg_trigger(uid, 'procurement.order', id, cr)
return res
class StockPicking(osv.osv):
_inherit = 'stock.picking'
def test_finished(self, cr, uid, ids):
res = super(StockPicking, self).test_finished(cr, uid, ids)
for picking in self.browse(cr, uid, ids):
for move in picking.move_lines:
if move.state == 'done' and move.procurements:
self.pool.get('procurement.order').signal_button_check(cr, uid, map(attrgetter('id'), move.procurements))
return res
class stock_warehouse_orderpoint(osv.osv):
"""
Defines Minimum stock rules.
"""
_name = "stock.warehouse.orderpoint"
_description = "Minimum Inventory Rule"
def _get_draft_procurements(self, cr, uid, ids, field_name, arg, context=None):
@param self: The object pointer
@param cr: The current row, from the database cursor,
@param uid: The current user ID for security checks
@param ids: List of selected IDs
@param use_new_cursor: False or the dbname
@param context: A standard dictionary for contextual values
@return: Dictionary of values
'''
if context is None:
context = {}
result = {}
procurement_obj = self.pool.get('procurement.order')
for orderpoint in self.browse(cr, uid, ids, context=context):
procurement_ids = procurement_obj.search(cr, uid , [('state', '=', 'draft'), ('product_id', '=', orderpoint.product_id.id), ('location_id', '=', orderpoint.location_id.id)])
result[orderpoint.id] = procurement_ids
return result
try:
if use_new_cursor:
cr = openerp.registry(use_new_cursor).db.cursor()
def _check_product_uom(self, cr, uid, ids, context=None):
'''
Check if the UoM has the same category as the product standard UoM
'''
if not context:
context = {}
for rule in self.browse(cr, uid, ids, context=context):
if rule.product_id.uom_id.category_id.id != rule.product_uom.category_id.id:
return False
return True
company = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id
maxdate = (datetime.today() + relativedelta(days=company.schedule_range)).strftime('%Y-%m-%d %H:%M:%S')
_columns = {
'name': fields.char('Name', size=32, required=True),
'active': fields.boolean('Active', help="If the active field is set to False, it will allow you to hide the orderpoint without removing it."),
'logic': fields.selection([('max','Order to Max'),('price','Best price (not yet active!)')], 'Reordering Mode', required=True),
'warehouse_id': fields.many2one('stock.warehouse', 'Warehouse', required=True, ondelete="cascade"),
'location_id': fields.many2one('stock.location', 'Location', required=True, ondelete="cascade"),
'product_id': fields.many2one('product.product', 'Product', required=True, ondelete='cascade', domain=[('type','!=','service')]),
'product_uom': fields.many2one('product.uom', 'Product Unit of Measure', required=True),
'product_min_qty': fields.float('Minimum Quantity', required=True,
help="When the virtual stock goes below the Min Quantity specified for this field, OpenERP generates "\
"a procurement to bring the forecasted quantity to the Max Quantity."),
'product_max_qty': fields.float('Maximum Quantity', required=True,
help="When the virtual stock goes below the Min Quantity, OpenERP generates "\
"a procurement to bring the forecasted quantity to the Quantity specified as Max Quantity."),
'qty_multiple': fields.integer('Qty Multiple', required=True,
help="The procurement quantity will be rounded up to this multiple."),
'procurement_id': fields.many2one('procurement.order', 'Latest procurement', ondelete="set null"),
'company_id': fields.many2one('res.company','Company',required=True),
'procurement_draft_ids': fields.function(_get_draft_procurements, type='many2many', relation="procurement.order", \
string="Related Procurement Orders",help="Draft procurement of the product and location of that orderpoint"),
}
_defaults = {
'active': lambda *a: 1,
'logic': lambda *a: 'max',
'qty_multiple': lambda *a: 1,
'name': lambda x,y,z,c: x.pool.get('ir.sequence').get(y,z,'stock.orderpoint') or '',
'product_uom': lambda sel, cr, uid, context: context.get('product_uom', False),
'company_id': lambda self, cr, uid, c: self.pool.get('res.company')._company_default_get(cr, uid, 'stock.warehouse.orderpoint', context=c)
}
_sql_constraints = [
('qty_multiple_check', 'CHECK( qty_multiple > 0 )', 'Qty Multiple must be greater than zero.'),
]
_constraints = [
(_check_product_uom, 'You have to select a product unit of measure in the same category than the default unit of measure of the product', ['product_id', 'product_uom']),
]
# Run confirmed procurements
while True:
ids = self.search(cr, uid, [('state', '=', 'confirmed'),('date_planned','<=', maxdate)], context=context)
if not ids: break
self.run(cr, uid, ids, context=context)
if use_new_cursor:
cr.commit()
def default_get(self, cr, uid, fields, context=None):
res = super(stock_warehouse_orderpoint, self).default_get(cr, uid, fields, context)
# default 'warehouse_id' and 'location_id'
if 'warehouse_id' not in res:
warehouse = self.pool.get('ir.model.data').get_object(cr, uid, 'stock', 'warehouse0', context)
res['warehouse_id'] = warehouse.id
if 'location_id' not in res:
warehouse = self.pool.get('stock.warehouse').browse(cr, uid, res['warehouse_id'], context)
res['location_id'] = warehouse.lot_stock_id.id
return res
# Check if running procurements are done
offset = 0
while True:
ids = self.search(cr, uid, [('state', '=', 'running'),('date_planned','<=', maxdate)], offset=offset, context=context)
if not ids: break
done = self.check(cr, uid, ids, context=context)
offset += len(ids) - len(done)
if use_new_cursor and len(done):
cr.commit()
def onchange_warehouse_id(self, cr, uid, ids, warehouse_id, context=None):
""" Finds location id for changed warehouse.
@param warehouse_id: Changed id of warehouse.
@return: Dictionary of values.
"""
if warehouse_id:
w = self.pool.get('stock.warehouse').browse(cr, uid, warehouse_id, context=context)
v = {'location_id': w.lot_stock_id.id}
return {'value': v}
finally:
if use_new_cursor:
try:
cr.close()
except Exception:
pass
return {}
def onchange_product_id(self, cr, uid, ids, product_id, context=None):
""" Finds UoM for changed product.
@param product_id: Changed id of product.
@return: Dictionary of values.
"""
if product_id:
prod = self.pool.get('product.product').browse(cr, uid, product_id, context=context)
d = {'product_uom': [('category_id', '=', prod.uom_id.category_id.id)]}
v = {'product_uom': prod.uom_id.id}
return {'value': v, 'domain': d}
return {'domain': {'product_uom': []}}
def copy(self, cr, uid, id, default=None, context=None):
if not default:
default = {}
default.update({
'name': self.pool.get('ir.sequence').get(cr, uid, 'stock.orderpoint') or '',
})
return super(stock_warehouse_orderpoint, self).copy(cr, uid, id, default, context=context)
class product_template(osv.osv):
_inherit="product.template"
_columns = {
'type': fields.selection([('product','Stockable Product'),('consu', 'Consumable'),('service','Service')], 'Product Type', required=True, help="Consumable: Will not imply stock management for this product. \nStockable product: Will imply stock management for this product."),
'procure_method': fields.selection([('make_to_stock','Make to Stock'),('make_to_order','Make to Order')], 'Procurement Method', required=True, help="Make to Stock: When needed, the product is taken from the stock or we wait for replenishment. \nMake to Order: When needed, the product is purchased or produced."),
'supply_method': fields.selection([('produce','Manufacture'),('buy','Buy')], 'Supply Method', required=True, help="Manufacture: When procuring the product, a manufacturing order or a task will be generated, depending on the product type. \nBuy: When procuring the product, a purchase order will be generated."),
}
_defaults = {
'procure_method': 'make_to_stock',
'supply_method': 'buy',
}
class product_product(osv.osv):
_inherit="product.product"
_columns = {
'orderpoint_ids': fields.one2many('stock.warehouse.orderpoint', 'product_id', 'Minimum Stock Rules'),
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -29,18 +29,5 @@
<field name="number_increment">1</field>
</record>
<record id="sequence_mrp_op_type" model="ir.sequence.type">
<field name="name">Stock orderpoint</field>
<field name="code">stock.orderpoint</field>
</record>
<record id="sequence_mrp_op" model="ir.sequence">
<field name="name">Stock orderpoint</field>
<field name="code">stock.orderpoint</field>
<field name="prefix">OP/</field>
<field name="padding">5</field>
<field name="number_next">1</field>
<field name="number_increment">1</field>
</record>
</data>
</openerp>

View File

@ -18,37 +18,19 @@
<field name="product_uom" string="Unit of Measure"/>
<field name="procure_method"/>
<field name="state"/>
<field name="name" invisible="1"/>
<field name="message"/>
</tree>
</field>
</record>
<record id="procurement_tree_view_board" model="ir.ui.view">
<field name="name">procurement.order.tree.board</field>
<field name="model">procurement.order</field>
<field eval="20" name="priority"/>
<field name="arch" type="xml">
<tree string="Procurement Lines" colors="red:date_planned&lt;current_date and state == 'exception';black:state=='running';darkgreen:state=='confirmed';gray:state in ['done','cancel'];blue:state == 'ready'">
<field name="date_planned" widget="date"/>
<field name="origin"/>
<field name="product_id"/>
<field name="product_qty"/>
<field name="product_uom" string="Unit of Measure"/>
<field name="state" invisible = "1"/>
<field name="message"/>
</tree>
</field>
</record>
<record id="procurement_form_view" model="ir.ui.view">
<field name="name">procurement.order.form</field>
<field name="model">procurement.order</field>
<field name="arch" type="xml">
<form string="Procurement" version="7.0">
<header>
<button name="button_confirm" states="draft" string="Confirm" class="oe_highlight"/>
<button name="button_check" states="confirmed" string="Run Procurement" class="oe_highlight"/>
<button name="button_restart" states="exception" string="Retry" class="oe_highlight"/>
<button name="button_cancel" states="draft,exception,waiting" string="Cancel Procurement"/>
<button name="run" states="confirmed,exception" string="Run Procurement" class="oe_highlight"/>
<button name="check" states="running" string="Check Procurement" class="oe_highlight"/>
<button name="cancel" states="exception,confirmed,running" string="Cancel Procurement"/>
<field name="state" readonly="1" widget="statusbar" statusbar_visible="draft,confirmed" />
</header>
<sheet>
@ -73,7 +55,6 @@
<group>
<field name="company_id" groups="base.group_multi_company" widget="selection"/>
<field name="origin" class="oe_inline" placeholder="e.g. SO005"/>
<field name="message"/>
</group>
</group>
<notebook>
@ -84,14 +65,7 @@
<field name="product_uos_qty" class="oe_inline"/>
<field name="product_uos" class="oe_inline"/>
</div>
<field name="location_id" domain="[('usage','=','internal')]"/>
</group>
<group>
<field name="move_id"/>
<field name="date_close"/>
<field name="close_move"/>
</group>
<field name="note" placeholder="Internal note..."/>
</page>
</notebook>
</sheet>
@ -111,15 +85,11 @@
<field name="date_planned"/>
<filter icon="terp-emblem-important" string="Exceptions" name="exceptions" domain="[('state','=','exception')]" help="Procurement Exceptions"/>
<separator/>
<filter icon="terp-emblem-important" string="To Fix" name="perm_exceptions" domain="[('state','=','exception'),('message', '!=', '')]" help="Permanent Procurement Exceptions"/>
<filter icon="terp-emblem-important" string="Temporary" name="temp_exceptions" domain="[('state','=','exception'),('message', '=', '')]" help="Temporary Procurement Exceptions"/>
<separator/>
<filter icon="terp-gnome-cpu-frequency-applet+" string="Late" domain="['&amp;', ('date_planned','&lt;', current_date), ('state', 'in', ('draft', 'confirmed'))]" help="Procurement started late" />
<field name="product_id" />
<field name="state" />
<group expand="0" string="Group By">
<filter string="Product" icon="terp-accessories-archiver" domain="[]" context="{'group_by':'product_id'}"/>
<filter string="Reason" icon="terp-gtk-jump-to-rtl" domain="[]" context="{'group_by':'name'}"/>
<filter string="Scheduled Date" icon="terp-go-month" domain="[]" context="{'group_by':'date_planned'}"/>
<filter string="Status" icon="terp-stock_effects-object-colorize" domain="[]" context="{'group_by':'state'}"/>
</group>
@ -174,186 +144,5 @@
<field name="view_mode">tree,form</field>
<field name="domain">[('state','=','exception')]</field>
</record>
<!-- Order Point -->
<record id="view_warehouse_orderpoint_tree" model="ir.ui.view">
<field name="name">stock.warehouse.orderpoint.tree</field>
<field name="model">stock.warehouse.orderpoint</field>
<field name="arch" type="xml">
<tree string="Reordering Rules">
<field name="name"/>
<field name="warehouse_id" groups="stock.group_locations"/>
<field name="location_id" groups="stock.group_locations"/>
<field name="product_id"/>
<field name="product_uom" groups="product.group_uom"/>
<field name="product_min_qty"/>
<field name="product_max_qty"/>
</tree>
</field>
</record>
<record model="ir.ui.view" id="warehouse_orderpoint_search">
<field name="name">stock.warehouse.orderpoint.search</field>
<field name="model">stock.warehouse.orderpoint</field>
<field name="arch" type="xml">
<search string="Reordering Rules Search">
<field name="name" string="Reordering Rules"/>
<field name="warehouse_id"/>
<field name="location_id" groups="stock.group_locations"/>
<field name="company_id" groups="base.group_multi_company"/>
<field name="product_id"/>
<group expand="0" string="Group By...">
<filter string="Warehouse" icon="terp-go-home" domain="[]" context="{'group_by':'warehouse_id'}"/>
<filter string="Location" icon="terp-go-home" domain="[]" context="{'group_by':'location_id'}"/>
</group>
</search>
</field>
</record>
<record id="view_warehouse_orderpoint_form" model="ir.ui.view">
<field name="name">stock.warehouse.orderpoint.form</field>
<field name="model">stock.warehouse.orderpoint</field>
<field name="arch" type="xml">
<form string="Reordering Rules" version="7.0">
<sheet>
<group>
<group>
<field name="name" />
<field name="product_id" on_change="onchange_product_id(product_id)"/>
</group>
<group>
<field name="warehouse_id" on_change="onchange_warehouse_id(warehouse_id)" widget="selection" groups="stock.group_locations"/>
<field name="product_uom" groups="product.group_uom"/>
<field name="location_id" groups="stock.group_locations"/>
<field name="company_id" groups="base.group_multi_company" widget="selection"/>
</group>
</group>
<group>
<group string="Rules">
<field name="product_min_qty" />
<field name="product_max_qty" />
<field name="qty_multiple" string="Quantity Multiple"/>
</group>
<group string="Misc">
<field name="procurement_id" readonly="1"/>
<field name="active" />
</group>
</group>
<group string="Procurement Orders to Process">
<field name="procurement_draft_ids" nolabel="1"/>
</group>
</sheet>
</form>
</field>
</record>
<record id="action_orderpoint_form" model="ir.actions.act_window">
<field name="name">Reordering Rules</field>
<field name="res_model">stock.warehouse.orderpoint</field>
<field name="type">ir.actions.act_window</field>
<field name="view_type">form</field>
<field name="view_id" ref="view_warehouse_orderpoint_tree"/>
<field name="search_view_id" ref="warehouse_orderpoint_search" />
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Click to add a reordering rule.
</p><p>You can define your minimum stock rules, so that OpenERP will automatically create draft manufacturing orders or request for quotations according to the stock level. Once the virtual stock of a product (= stock on hand minus all confirmed orders and reservations) is below the minimum quantity, OpenERP will generate a procurement request to increase the stock up to the maximum quantity.</p>
</field>
</record>
<act_window
context="{'search_default_warehouse_id': active_id, 'default_warehouse_id': active_id}"
id="act_stock_warehouse_2_stock_warehouse_orderpoint"
name="Reordering Rules"
res_model="stock.warehouse.orderpoint"
src_model="stock.warehouse"
groups="stock.group_stock_user"/>
<act_window
context="{'product_uom': locals().has_key('uom_id') and uom_id, 'default_procurement_id': active_id}"
id="act_procurement_2_stock_warehouse_orderpoint"
name="Reordering Rules"
res_model="stock.warehouse.orderpoint"
src_model="procurement.order"
groups="stock.group_stock_user"/>
<!-- Procurements are located in Warehouse menu hierarchy, MRP users should come to Stock application to use it. -->
<menuitem id="menu_stock_sched" name="Schedulers" parent="stock.menu_stock_root" sequence="4" groups="stock.group_stock_manager"/>
<menuitem action="action_compute_schedulers" id="menu_stock_proc_schedulers" parent="menu_stock_sched" sequence="20" groups="stock.group_stock_manager"/>
<menuitem action="procurement_exceptions" id="menu_stock_procurement_action" parent="menu_stock_sched" sequence="50" groups="stock.group_stock_manager"/>
<menuitem id="menu_stock_procurement" name="Automatic Procurements" parent="stock.menu_stock_configuration" sequence="5"/>
<menuitem action="action_orderpoint_form" id="menu_stock_order_points" parent="stock.menu_stock_configuration" sequence="10"/>
<record model="ir.actions.act_window" id="product_open_orderpoint">
<field name="context">{'default_product_id': active_id, 'search_default_product_id': active_id}</field>
<field name="name">Orderpoints</field>
<field name="res_model">stock.warehouse.orderpoint</field>
</record>
<record model="ir.ui.view" id="product_template_form_view_procurement">
<field name="name">product.template.procurement</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_form_view"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='type']" position="after">
<field name="procure_method"/>
<field name="supply_method"/>
</xpath>
</field>
</record>
<record id="product_search_form_view_procurment" model="ir.ui.view">
<field name="name">product.search.procurment.form</field>
<field name="model">product.product</field>
<field name="inherit_id" ref="product.product_search_form_view"/>
<field name="arch" type="xml">
<filter name="consumable" position="before">
<filter string="Products" icon="terp-accessories-archiver" domain="[('type','=','product')]" help="Stockable products"/>
</filter>
</field>
</record>
<record model="ir.ui.view" id="product_form_view_procurement_button">
<field name="name">product.product.procurement</field>
<field name="model">product.product</field>
<field name="inherit_id" ref="stock.view_normal_procurement_locations_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='incoming_qty']" position="after">
<button string="⇒ Request Procurement" name="%(act_make_procurement)d" type="action" class="oe_link"/>
</xpath>
<xpath expr="//div[@name='buttons']" position="inside">
<button string="Orderpoints" name="%(product_open_orderpoint)d" type="action" attrs="{'invisible':[('type', '=', 'service')]}"/>
</xpath>
<xpath expr="//field[@name='cost_method']" position="before">
<field name="procure_method" groups="base.group_user"/>
<field name="supply_method" groups="base.group_user"/>
</xpath>
<xpath expr="//group[@name='general']" position="after" >
<group name="procurement_help" class="oe_grey" col="1" groups="base.group_user">
<p attrs="{'invisible': ['|','|',('type','&lt;&gt;','service'),('procure_method','&lt;&gt;','make_to_stock')]}">
When you sell this service, nothing special will be triggered
to deliver the customer, as you set the procurement method as
'Make to Stock'.
</p>
<p attrs="{'invisible': ['|','|',('type','&lt;&gt;','product'),('procure_method','&lt;&gt;','make_to_stock')]}">
When you sell this product, OpenERP will <b>use the available
inventory</b> for the delivery order.
<br/><br/>
If there are not enough quantities available, the delivery order
will wait for new products. To fulfill the inventory, you should
create others rules like orderpoints.
</p>
<p attrs="{'invisible': ['|','|',('type','&lt;&gt;','consu'),('procure_method','&lt;&gt;','make_to_stock')]}">
When you sell this product, a delivery order will be created.
OpenERP will consider that the <b>required quantities are always
available</b> as it's a consumable (as a result of this, the quantity
on hand may become negative).
</p>
</group>
</xpath>
</field>
</record>
</data>
</openerp>

View File

@ -1,181 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="wkf_procurement" model="workflow">
<field name="name">procurement.order.basic</field>
<field name="osv">procurement.order</field>
<field name="on_create">True</field>
</record>
<record id="act_draft" model="workflow.activity">
<field name="wkf_id" ref="wkf_procurement"/>
<field name="flow_start">True</field>
<field name="name">draft</field>
</record>
<record id="act_cancel" model="workflow.activity">
<field name="wkf_id" ref="wkf_procurement"/>
<field name="name">cancel</field>
<field name="kind">function</field>
<field name="action">action_cancel()</field>
<field name="flow_stop">True</field>
</record>
<record id="act_confirm" model="workflow.activity">
<field name="wkf_id" ref="wkf_procurement"/>
<field name="name">confirm</field>
<field name="kind">function</field>
<field name="action">action_confirm()</field>
</record>
<record id="act_confirm_wait" model="workflow.activity">
<field name="wkf_id" ref="wkf_procurement"/>
<field name="name">confirm_wait</field>
<field name="kind">function</field>
<field name="action">write({'state':'exception'})</field>
</record>
<record id="act_confirm_mts" model="workflow.activity">
<field name="wkf_id" ref="wkf_procurement"/>
<field name="name">confirm_mts</field>
</record>
<record id="act_confirm_mto" model="workflow.activity">
<field name="wkf_id" ref="wkf_procurement"/>
<field name="name">confirm_mto</field>
</record>
<record id="act_make_to_stock" model="workflow.activity">
<field name="wkf_id" ref="wkf_procurement"/>
<field name="name">make_to_stock</field>
<field name="kind">function</field>
<field name="action">action_move_assigned()</field>
</record>
<record id="act_make_done" model="workflow.activity">
<field name="wkf_id" ref="wkf_procurement"/>
<field name="name">ready</field>
<field name="kind">function</field>
<field name="action">action_ready()</field>
</record>
<record id="act_wait_done" model="workflow.activity">
<field name="wkf_id" ref="wkf_procurement"/>
<field name="name">wait_done</field>
<field name="kind">function</field>
<field name="action">write({'state':'waiting'})</field>
</record>
<record id="act_done" model="workflow.activity">
<field name="wkf_id" ref="wkf_procurement"/>
<field name="flow_stop">True</field>
<field name="name">done</field>
<field name="kind">function</field>
<field name="action">action_done()</field>
</record>
<record id="trans_draft_confirm" model="workflow.transition">
<field name="act_from" ref="act_draft"/>
<field name="act_to" ref="act_confirm"/>
<field name="signal">button_confirm</field>
</record>
<record id="trans_confirm_cancel2" model="workflow.transition">
<field name="act_from" ref="act_confirm"/>
<field name="act_to" ref="act_wait_done"/>
<field name="signal">button_wait_done</field>
<field name="condition">True</field>
</record>
<record id="trans_confirm_wait_done" model="workflow.transition">
<field name="act_from" ref="act_wait_done"/>
<field name="act_to" ref="act_done"/>
<field name="condition">check_move_done()</field>
<field name="trigger_model">stock.move</field>
<field name="trigger_expr_id">[move_id.id]</field>
</record>
<record id="trans_confirm_cancel" model="workflow.transition">
<field name="act_from" ref="act_confirm"/>
<field name="act_to" ref="act_cancel"/>
<field name="signal">button_check</field>
<field name="condition">test_cancel()</field>
</record>
<record id="trans_confirm_confirm_wait" model="workflow.transition">
<field name="act_from" ref="act_confirm"/>
<field name="act_to" ref="act_confirm_wait"/>
<field name="signal">button_check</field>
<field name="condition">check_conditions_confirm2wait()</field>
</record>
<record id="trans_confirm_wait_confirm_mto" model="workflow.transition">
<field name="act_from" ref="act_confirm_wait"/>
<field name="act_to" ref="act_confirm_mto"/>
<field name="condition">procure_method=='make_to_order'</field>
</record>
<record id="trans_confirm_wait_confirm_mts" model="workflow.transition">
<field name="act_from" ref="act_confirm_wait"/>
<field name="act_to" ref="act_confirm_mts"/>
<field name="condition">procure_method=='make_to_stock'</field>
</record>
<record id="trans_confirm_mts_cancel" model="workflow.transition">
<field name="act_from" ref="act_confirm_mts"/>
<field name="act_to" ref="act_cancel"/>
<field name="signal">button_cancel</field>
</record>
<record id="trans_confirm_waiting_cancel" model="workflow.transition">
<field name="act_from" ref="act_wait_done"/>
<field name="act_to" ref="act_cancel"/>
<field name="signal">button_cancel</field>
</record>
<record id="trans_confirm_mts_confirm" model="workflow.transition">
<field name="act_from" ref="act_confirm_mts"/>
<field name="act_to" ref="act_confirm"/>
<field name="signal">button_restart</field>
</record>
<record id="trans_confirm_mto_cancel" model="workflow.transition">
<field name="act_from" ref="act_confirm_mto"/>
<field name="act_to" ref="act_cancel"/>
<field name="signal">button_cancel</field>
</record>
<record id="trans_confirm_mto_confirm" model="workflow.transition">
<field name="act_from" ref="act_confirm_mto"/>
<field name="act_to" ref="act_confirm"/>
<field name="signal">button_restart</field>
</record>
<record id="trans_draft_cancel" model="workflow.transition">
<field name="act_from" ref="act_draft"/>
<field name="act_to" ref="act_cancel"/>
<field name="signal">button_cancel</field>
</record>
<record id="trans_confirm_mts_make_to_stock" model="workflow.transition">
<field name="act_from" ref="act_confirm_mts"/>
<field name="act_to" ref="act_make_to_stock"/>
<field name="condition">check_make_to_stock()</field>
</record>
<record id="trans_confirm_mto_make_done" model="workflow.transition">
<!-- This transition is there to unblock products that would be in MTO with a supply method that would be
produce or buy without MRP or purchase module installed. These modules overwrite the check_produce()
and check_buy() methods -so that it invalidates their part of this 'bypass transition'-, and define
their own workflow paths.
-->
<field name="act_from" ref="act_confirm_mto"/>
<field name="act_to" ref="act_make_done"/>
<field name="condition">not check_produce() and not check_buy()</field>
</record>
<record id="trans_make_to_stock_make_done" model="workflow.transition">
<field name="act_from" ref="act_make_to_stock"/> <!-- TOFIX: If product is service product and procure method is 'make_to_stock', procurement is closed without generated service -->
<field name="act_to" ref="act_make_done"/>
<field name="condition">True</field>
<field name="trigger_model" eval="False"/>
<field name="trigger_expr_id" eval="False"/>
</record>
<record id="trans_make_done_done" model="workflow.transition">
<field name="act_from" ref="act_make_done"/>
<field name="act_to" ref="act_done"/>
<field name="condition">action_check_finished()</field>
<field name="trigger_model">stock.move</field>
<field name="trigger_expr_id">move_id and [move_id.id] or []</field>
</record>
<record id="trans_make_done_confirm" model="workflow.transition">
<field name="act_from" ref="act_make_done"/>
<field name="act_to" ref="act_cancel"/>
<field name="condition">check_move_cancel()</field>
</record>
</data>
</openerp>

View File

@ -1,276 +0,0 @@
# -*- 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 datetime import datetime
from dateutil.relativedelta import relativedelta
import openerp
from openerp.osv import osv
from openerp.osv import fields
from openerp.tools.translate import _
from openerp.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT
from openerp import tools
class procurement_order(osv.osv):
_inherit = 'procurement.order'
def run_scheduler(self, cr, uid, automatic=False, use_new_cursor=False, context=None):
''' Runs through scheduler.
@param use_new_cursor: False or the dbname
'''
self._procure_confirm(cr, uid, use_new_cursor=use_new_cursor, context=context)
self._procure_orderpoint_confirm(cr, uid, automatic=automatic,\
use_new_cursor=use_new_cursor, context=context)
def _procure_confirm(self, cr, uid, ids=None, use_new_cursor=False, context=None):
'''
Call the scheduler to check the procurement order
@param self: The object pointer
@param cr: The current row, from the database cursor,
@param uid: The current user ID for security checks
@param ids: List of selected IDs
@param use_new_cursor: False or the dbname
@param context: A standard dictionary for contextual values
@return: Dictionary of values
'''
if context is None:
context = {}
try:
if use_new_cursor:
cr = openerp.registry(use_new_cursor).db.cursor()
procurement_obj = self.pool.get('procurement.order')
if not ids:
ids = procurement_obj.search(cr, uid, [('state', '=', 'exception')], order="date_planned")
self.signal_button_restart(cr, uid, ids)
if use_new_cursor:
cr.commit()
company = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id
maxdate = (datetime.today() + relativedelta(days=company.schedule_range)).strftime(tools.DEFAULT_SERVER_DATE_FORMAT)
start_date = fields.datetime.now()
offset = 0
report = []
report_total = 0
report_except = 0
report_later = 0
while True:
ids = procurement_obj.search(cr, uid, [('state', '=', 'confirmed'), ('procure_method', '=', 'make_to_order')], offset=offset, limit=500, order='priority, date_planned', context=context)
for proc in procurement_obj.browse(cr, uid, ids, context=context):
if maxdate >= proc.date_planned:
self.signal_button_check(cr, uid, [proc.id])
else:
offset += 1
report_later += 1
if proc.state == 'exception':
report.append(_('PROC %d: on order - %3.2f %-5s - %s') % \
(proc.id, proc.product_qty, proc.product_uom.name,
proc.product_id.name))
report_except += 1
report_total += 1
if use_new_cursor:
cr.commit()
if not ids:
break
offset = 0
ids = []
while True:
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:
self.signal_button_check(cr, uid, [proc.id])
report_ids.append(proc.id)
else:
report_later += 1
report_total += 1
if proc.state == 'exception':
report.append(_('PROC %d: from stock - %3.2f %-5s - %s') % \
(proc.id, proc.product_qty, proc.product_uom.name,
proc.product_id.name,))
report_except += 1
if use_new_cursor:
cr.commit()
offset += len(ids)
if not ids: break
end_date = fields.datetime.now()
if use_new_cursor:
cr.commit()
finally:
if use_new_cursor:
try:
cr.close()
except Exception:
pass
return {}
def _prepare_automatic_op_procurement(self, cr, uid, product, warehouse, location_id, context=None):
return {'name': _('Automatic OP: %s') % (product.name,),
'origin': _('SCHEDULER'),
'date_planned': datetime.today().strftime(DEFAULT_SERVER_DATETIME_FORMAT),
'product_id': product.id,
'product_qty': -product.virtual_available,
'product_uom': product.uom_id.id,
'location_id': location_id,
'company_id': warehouse.company_id.id,
'procure_method': 'make_to_order',}
def create_automatic_op(self, cr, uid, context=None):
"""
Create procurement of virtual stock < 0
@param self: The object pointer
@param cr: The current row, from the database cursor,
@param uid: The current user ID for security checks
@param context: A standard dictionary for contextual values
@return: Dictionary of values
"""
if context is None:
context = {}
product_obj = self.pool.get('product.product')
proc_obj = self.pool.get('procurement.order')
warehouse_obj = self.pool.get('stock.warehouse')
warehouse_ids = warehouse_obj.search(cr, uid, [], context=context)
products_ids = product_obj.search(cr, uid, [], order='id', context=context)
for warehouse in warehouse_obj.browse(cr, uid, warehouse_ids, context=context):
context['warehouse'] = warehouse
# Here we check products availability.
# We use the method 'read' for performance reasons, because using the method 'browse' may crash the server.
for product_read in product_obj.read(cr, uid, products_ids, ['virtual_available'], context=context):
if product_read['virtual_available'] >= 0.0:
continue
product = product_obj.browse(cr, uid, [product_read['id']], context=context)[0]
if product.supply_method == 'buy':
location_id = warehouse.lot_input_id.id
elif product.supply_method == 'produce':
location_id = warehouse.lot_stock_id.id
else:
continue
proc_id = proc_obj.create(cr, uid,
self._prepare_automatic_op_procurement(cr, uid, product, warehouse, location_id, context=context),
context=context)
self.signal_button_confirm(cr, uid, [proc_id])
self.signal_button_check(cr, uid, [proc_id])
return True
def _get_orderpoint_date_planned(self, cr, uid, orderpoint, start_date, context=None):
date_planned = start_date + \
relativedelta(days=orderpoint.product_id.seller_delay or 0.0)
return date_planned.strftime(DEFAULT_SERVER_DATE_FORMAT)
def _prepare_orderpoint_procurement(self, cr, uid, orderpoint, product_qty, context=None):
return {'name': orderpoint.name,
'date_planned': self._get_orderpoint_date_planned(cr, uid, orderpoint, datetime.today(), context=context),
'product_id': orderpoint.product_id.id,
'product_qty': product_qty,
'company_id': orderpoint.company_id.id,
'product_uom': orderpoint.product_uom.id,
'location_id': orderpoint.location_id.id,
'procure_method': 'make_to_order',
'origin': orderpoint.name}
def _product_virtual_get(self, cr, uid, order_point):
location_obj = self.pool.get('stock.location')
return location_obj._product_virtual_get(cr, uid,
order_point.location_id.id, [order_point.product_id.id],
{'uom': order_point.product_uom.id})[order_point.product_id.id]
def _procure_orderpoint_confirm(self, cr, uid, automatic=False,\
use_new_cursor=False, context=None, user_id=False):
'''
Create procurement based on Orderpoint
use_new_cursor: False or the dbname
@param self: The object pointer
@param cr: The current row, from the database cursor,
@param user_id: The current user ID for security checks
@param context: A standard dictionary for contextual values
@param param: False or the dbname
@return: Dictionary of values
"""
'''
if context is None:
context = {}
if use_new_cursor:
cr = openerp.registry(use_new_cursor).db.cursor()
orderpoint_obj = self.pool.get('stock.warehouse.orderpoint')
procurement_obj = self.pool.get('procurement.order')
offset = 0
ids = [1]
if automatic:
self.create_automatic_op(cr, uid, context=context)
while ids:
ids = orderpoint_obj.search(cr, uid, [], offset=offset, limit=100)
for op in orderpoint_obj.browse(cr, uid, ids, context=context):
prods = self._product_virtual_get(cr, uid, op)
if prods is None:
continue
if prods < op.product_min_qty:
qty = max(op.product_min_qty, op.product_max_qty)-prods
reste = qty % op.qty_multiple
if reste > 0:
qty += op.qty_multiple - reste
if qty <= 0:
continue
if op.product_id.type not in ('consu'):
if op.procurement_draft_ids:
# Check draft procurement related to this order point
pro_ids = [x.id for x in op.procurement_draft_ids]
procure_datas = procurement_obj.read(
cr, uid, pro_ids, ['id', 'product_qty'], context=context)
to_generate = qty
for proc_data in procure_datas:
if to_generate >= proc_data['product_qty']:
self.signal_button_confirm(cr, uid, [proc_data['id']])
procurement_obj.write(cr, uid, [proc_data['id']], {'origin': op.name}, context=context)
to_generate -= proc_data['product_qty']
if not to_generate:
break
qty = to_generate
if qty:
proc_id = procurement_obj.create(cr, uid,
self._prepare_orderpoint_procurement(cr, uid, op, qty, context=context),
context=context)
self.signal_button_confirm(cr, uid, [proc_id])
self.signal_button_check(cr, uid, [proc_id])
orderpoint_obj.write(cr, uid, [op.id],
{'procurement_id': proc_id}, context=context)
offset += len(ids)
if use_new_cursor:
cr.commit()
if use_new_cursor:
cr.commit()
cr.close()
return {}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -1,9 +1,4 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_procurement,procurement.order,model_procurement_order,base.group_user,1,0,0,0
access_procurement_stock_user,procurement.order stock.user,model_procurement_order,stock.group_stock_user,1,1,1,1
access_stock_warehouse_orderpoint,stock.warehouse.orderpoint,model_stock_warehouse_orderpoint,stock.group_stock_user,1,0,0,0
access_stock_warehouse_orderpoint_system,stock.warehouse.orderpoint system,model_stock_warehouse_orderpoint,stock.group_stock_manager,1,1,1,1
access_mrp_property_group,mrp.property.group,model_mrp_property_group,stock.group_stock_manager,1,1,1,1
access_mrp_property,mrp.property,model_mrp_property,stock.group_stock_manager,1,1,1,1
access_mrp_property_group,mrp.property.group,model_mrp_property_group,base.group_user,1,0,0,0
access_mrp_property,mrp.property,model_mrp_property,base.group_user,1,0,0,0
access_procurement,procurement.order,model_procurement_order,base.group_user,1,1,1,0
access_procurement_group,procurement.group,model_procurement_group,base.group_user,1,1,1,0
access_procurement_rule,procurement.rule,model_procurement_rule,base.group_user,1,1,1,0

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_procurement procurement.order model_procurement_order base.group_user 1 0 1 0 1 0
3 access_procurement_stock_user access_procurement_group procurement.order stock.user procurement.group model_procurement_order model_procurement_group stock.group_stock_user base.group_user 1 1 1 1 0
4 access_stock_warehouse_orderpoint access_procurement_rule stock.warehouse.orderpoint procurement.rule model_stock_warehouse_orderpoint model_procurement_rule stock.group_stock_user base.group_user 1 0 1 0 1 0
access_stock_warehouse_orderpoint_system stock.warehouse.orderpoint system model_stock_warehouse_orderpoint stock.group_stock_manager 1 1 1 1
access_mrp_property_group mrp.property.group model_mrp_property_group stock.group_stock_manager 1 1 1 1
access_mrp_property mrp.property model_mrp_property stock.group_stock_manager 1 1 1 1
access_mrp_property_group mrp.property.group model_mrp_property_group base.group_user 1 0 0 0
access_mrp_property mrp.property model_mrp_property base.group_user 1 0 0 0

View File

@ -9,12 +9,5 @@
<field name="domain_force">['|',('company_id','child_of',[user.company_id.id]),('company_id','=',False)]</field>
</record>
<record model="ir.rule" id="stock_warehouse_orderpoint_rule">
<field name="name">stock_warehouse.orderpoint multi-company</field>
<field name="model_id" search="[('model','=','stock.warehouse.orderpoint')]" model="ir.model"/>
<field name="global" eval="True"/>
<field name="domain_force">['|',('company_id','child_of',[user.company_id.id]),('company_id','=',False)]</field>
</record>
</data>
</openerp>

View File

@ -1,89 +1,22 @@
-
For test the procurement process, First I have to apply a minimum stock rule on product
I create a procurement
-
I create minimum stock rule for product.
-
!record {model: stock.warehouse.orderpoint, id: stock_warehouse_orderpoint_op0}:
!record {model: procurement.order, id: procurement_order0}:
company_id: base.main_company
location_id: stock.stock_location_stock
logic: max
name: OP/00008
name: Procurement Test
product_id: product.product_product_32
product_max_qty: 15.0
product_min_qty: 5.0
product_qty: 15.0
product_uom: product.product_uom_unit
qty_multiple: 1
warehouse_id: stock.warehouse0
-
Check product quantity and update it, if needed for apply a minimum stock rule.
-
!python {model: product.product}: |
product = self.browse(cr, uid, ref('product.product_product_32'))
if product.virtual_available < 5.0:
change_qty = self.pool.get('stock.change.product.qty')
id = change_qty.create(cr, uid, {'location_id' : ref('stock.stock_location_stock'), 'new_quantity': 4, 'product_id': product.id})
change_qty.change_product_qty(cr, uid, [id], {'active_model':'product.product', 'active_id': product.id, 'active_ids':[product.id]})
assert product.qty_available == 4,"Product quantity is not updated."
assert product.virtual_available < 5.0,'Virtual stock have more quantities.'
product_uos_qty: 15.0
product_uos: product.product_uom_unit
-
I run the scheduler.
-
!python {model: procurement.order}: |
self.run_scheduler(cr, uid)
-
I check that procurement order is based on minimum stock rule.
I check that procurement order is done
-
!python {model: procurement.order}: |
proc_ids = self.search(cr, uid, [('product_id','=', ref('product.product_product_32'))])
assert proc_ids, 'No Procurement created.'
proc_order = self.browse(cr, uid, proc_ids)[0]
assert proc_order.product_qty == 11.0,"Procurement product quantity is not corresponded."
-
I check product quantity.
-
!python {model: product.product}: |
product = self.browse(cr, uid, ref('product.product_product_32'))
assert product.virtual_available == 15.0,"After run the scheduler product's virtual stock is not updated."
-
For test the Procurement Request wizard, Again I have to update product quantity.
-
!python {model: product.product}: |
mk_procure = self.pool.get('make.procurement')
procur_order = self.pool.get('procurement.order')
product = self.browse(cr, uid, ref('product.product_product_32'))
context.update({'active_model': 'product.product','active_id':ref('product.product_product_32')})
values = {'warehouse_id': ref('base.main_company'), 'uom_id': ref('product.product_uom_unit'), 'qty': 5}
id = mk_procure.create(cr, uid, values, context)
procurement = mk_procure.make_procurement(cr, uid, [id], context)
assert product.virtual_available == 20.0, 'Virtual stock should be updated'
proc_id = procurement.get('res_id')
for procurement in procur_order.browse(cr, uid, [proc_id]):
if procurement.state == 'confirmed':
assert procurement.state == 'confirmed',"Procurement state should be 'Confirmed'."
assert procurement.product_id.id == ref('product.product_product_32'),"Product is not correspond."
assert procurement.product_qty == 5,"Product Quantity is not correspond."
assert procurement.product_uom.id == ref('product.product_uom_unit'),"Product's UOM is not correspond."
context.update({'proc': proc_id})
-
I run the scheduler.
-
!python {model: procurement.order}: |
self.run_scheduler(cr, uid)
-
I check the current state of procurement.
-
!python {model: procurement.order}: |
proc_id = context.get('proc')
proc = self.browse(cr, uid, [proc_id])[0]
assert proc.state == 'ready' or 'exception',"Procurement should be in Ready or Exception state"
#-
# I compute minimum stock.
# [Note. Commented out because it spawns a thread that may query the db after tests have been reverted.]
#-
# !python {model: procurement.orderpoint.compute}: |
# proc_id = context.get('proc')
# context.update({'active_model': 'procurement.order', 'active_id': proc_id})
# id = self.create(cr, uid, {'automatic': True}, context)
# self.procure_calculation(cr, uid, [id], context)
proc_order = self.browse(cr, uid, ref('procurement_order0'), context=context)
assert proc_order.state == 'done'

View File

@ -19,9 +19,6 @@
#
##############################################################################
import orderpoint_procurement
import mrp_procurement
import schedulers_all
import make_procurement_product
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -28,11 +28,6 @@ class procurement_compute_all(osv.osv_memory):
_description = 'Compute all schedulers'
_columns = {
'automatic': fields.boolean('Automatic orderpoint',help='Triggers an automatic procurement for all products that have a virtual stock under 0. You should probably not use this option, we suggest using a MTO configuration on products.'),
}
_defaults = {
'automatic': lambda *a: False,
}
def _procure_calculation_all(self, cr, uid, ids, context=None):
@ -46,9 +41,7 @@ class procurement_compute_all(osv.osv_memory):
proc_obj = self.pool.get('procurement.order')
#As this function is in a new thread, i need to open a new cursor, because the old one may be closed
new_cr = self.pool.db.cursor()
for proc in self.browse(new_cr, uid, ids, context=context):
proc_obj.run_scheduler(new_cr, uid, automatic=proc.automatic, use_new_cursor=new_cr.dbname,\
context=context)
proc_obj.run_scheduler(new_cr, uid, use_new_cursor=new_cr.dbname, context=context)
#close the new cursor
new_cr.close()
return {}

View File

@ -9,9 +9,9 @@
<field name="model">procurement.order.compute.all</field>
<field name="arch" type="xml">
<form string="Scheduler Parameters" version="7.0">
<group>
<field name="automatic"/>
</group>
<p>
Compute all procurements in the background.
</p>
<footer>
<button name="procure_calculation" string="Run Schedulers" type="object" class="oe_highlight" />
or

View File

@ -54,11 +54,13 @@ Dashboard / Reports for Warehouse Management will include:
""",
'website': 'http://www.openerp.com',
'images': ['images/stock_forecast_report.png', 'images/delivery_orders.jpeg', 'images/inventory_analysis.jpeg','images/location.jpeg','images/moves_analysis.jpeg','images/physical_inventories.jpeg','images/warehouse_dashboard.jpeg'],
'depends': ['product', 'account'],
'depends': ['product', 'account','procurement'],
'category': 'Warehouse Management',
'sequence': 16,
'demo': [
'stock_demo.xml',
'procurement_demo.xml',
'stock_orderpoint.xml',
# 'stock_demo.yml',
],
'data': [
@ -77,6 +79,9 @@ Dashboard / Reports for Warehouse Management will include:
'wizard/stock_inventory_line_split_view.xml',
'wizard/stock_change_standard_price_view.xml',
'wizard/stock_return_picking_view.xml',
'wizard/make_procurement_view.xml',
'wizard/mrp_procurement_view.xml',
'wizard/orderpoint_procurement_view.xml',
'stock_workflow.xml',
'stock_incoterms.xml',
'stock_report.xml',

View File

@ -0,0 +1,86 @@
# -*- 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/>.
#
##############################################################################
import time
from datetime import datetime
from dateutil.relativedelta import relativedelta
from openerp.osv import fields, osv
import openerp.addons.decimal_precision as dp
class procurement_group(osv.osv):
_inherit = 'procurement.group'
_columns = {
'partner_id': fields.many2one('res.partner', 'Partner')
}
class procurement_rule(osv.osv):
_inherit = 'procurement.rule'
def _get_action(self, cr, uid, context=context):
result = super(procurement_rule, self)._get_action(cr, uid, context=context)
return result + [('move','Move From Another Location')]
class procurement_order(osv.osv):
_inherit = "procurement.order"
_columns = {
'location_id': fields.many2one('stock.location', 'Source Location')
'move_id': fields.many2one('stock.move', 'Move')
'move_dest_id': fields.many2one('stock.move', 'Destination Move')
'location_src_id': fields.many2one('stock.location', 'Source Location',
help="Source location is action=move")
}
def _run(self, cr, uid, procurement, context=None):
if procurement.rule_id and procurement.rule_id.action == 'move':
if not procurement.location_src_id:
self.message_post(cr, uid, [procurement.id], body=_('No source location defined!'), context=context)
return False
move_obj = self.pool.get('stock.move')
move_id = move_obj.create(cr, uid, {
'name': procurement.name,
'company_id': procurement.company_id.id,
'product_id': procurement.product_id.id,
'date': procurement.date_planned,
'product_qty': procurement.product_qty,
'product_uom': procurement.product_uom.id,
'product_uos_qty': (procurement.product_uos and procurement.product_uos_qty)\
or procurement.product_qty,
'product_uos': (procurement.product_uos and procurement.product_uos.id)\
or procurement.product_uom.id,
'partner_id': procurement.group_id and procurement.group_id.partner_id and \
procurement.group_id.partner_id.id or False,
'location_id': procurement.location_src_id.id,
'location_dest_id': procurement.location_id.id,
'move_dest_id': procurement.move_dest_id and procurement.move_dest_id.id or False,
'cancel_cascade': procurement.rule_id and procurement.rule_id.cancel_cascade or False,
'group_id': procurement.group_id and procurement.group_id.id or False,
})
move_obj.button_confirm(cr,uid, [move_id], context=context)
self.write(cr, uid, [procurement.id], {'move_id': move_id}, context=context)
return True
return super(procurement_order, self)._run(cr, uid, procurement, context)
def _check(self, cr, uid, procurement, context=None):
if procurement.rule_id and procurement.rule_id.action == 'move':
return procurement.move_id.state=='done'
return super(procurement_order, self)._check(cr, uid, procurement, context)

View File

@ -53,3 +53,5 @@ access_product_pricelist_version_stock_manager,product.pricelist.version stock_m
access_product_pricelist_item_stock_manager,product.pricelist.item stock_manager,product.model_product_pricelist_item,stock.group_stock_manager,1,1,1,1
access_account_account_stock_manager,account.account stock manager,account.model_account_account,stock.group_stock_manager,1,0,0,0
access_board_stock_user,board.board user,board.model_board_board,stock.group_stock_user,1,1,0,0
access_stock_warehouse_orderpoint,stock.warehouse.orderpoint,model_stock_warehouse_orderpoint,stock.group_stock_user,1,0,0,0
access_stock_warehouse_orderpoint_system,stock.warehouse.orderpoint system,model_stock_warehouse_orderpoint,stock.group_stock_manager,1,1,1,1

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
53 access_product_pricelist_item_stock_manager product.pricelist.item stock_manager product.model_product_pricelist_item stock.group_stock_manager 1 1 1 1
54 access_account_account_stock_manager account.account stock manager account.model_account_account stock.group_stock_manager 1 0 0 0
55 access_board_stock_user board.board user board.model_board_board stock.group_stock_user 1 1 0 0
56 access_stock_warehouse_orderpoint stock.warehouse.orderpoint model_stock_warehouse_orderpoint stock.group_stock_user 1 0 0 0
57 access_stock_warehouse_orderpoint_system stock.warehouse.orderpoint system model_stock_warehouse_orderpoint stock.group_stock_manager 1 1 1 1

View File

@ -80,5 +80,13 @@
<field name="domain_force">['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]</field>
</record>
<record model="ir.rule" id="stock_warehouse_orderpoint_rule">
<field name="name">stock_warehouse.orderpoint multi-company</field>
<field name="model_id" search="[('model','=','stock.warehouse.orderpoint')]" model="ir.model"/>
<field name="global" eval="True"/>
<field name="domain_force">['|',('company_id','child_of',[user.company_id.id]),('company_id','=',False)]</field>
</record>
</data>
</openerp>

View File

@ -2840,4 +2840,130 @@ class stock_pack_operation(osv.osv):
pass
return todo_on_moves, todo_on_operations
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
class stock_warehouse_orderpoint(osv.osv):
"""
Defines Minimum stock rules.
"""
_name = "stock.warehouse.orderpoint"
_description = "Minimum Inventory Rule"
def _get_draft_procurements(self, cr, uid, ids, field_name, arg, context=None):
if context is None:
context = {}
result = {}
procurement_obj = self.pool.get('procurement.order')
for orderpoint in self.browse(cr, uid, ids, context=context):
procurement_ids = procurement_obj.search(cr, uid , [('state', '=', 'draft'), ('product_id', '=', orderpoint.product_id.id), ('location_id', '=', orderpoint.location_id.id)])
result[orderpoint.id] = procurement_ids
return result
def _check_product_uom(self, cr, uid, ids, context=None):
'''
Check if the UoM has the same category as the product standard UoM
'''
if not context:
context = {}
for rule in self.browse(cr, uid, ids, context=context):
if rule.product_id.uom_id.category_id.id != rule.product_uom.category_id.id:
return False
return True
_columns = {
'name': fields.char('Name', size=32, required=True),
'active': fields.boolean('Active', help="If the active field is set to False, it will allow you to hide the orderpoint without removing it."),
'logic': fields.selection([('max','Order to Max'),('price','Best price (not yet active!)')], 'Reordering Mode', required=True),
'warehouse_id': fields.many2one('stock.warehouse', 'Warehouse', required=True, ondelete="cascade"),
'location_id': fields.many2one('stock.location', 'Location', required=True, ondelete="cascade"),
'product_id': fields.many2one('product.product', 'Product', required=True, ondelete='cascade', domain=[('type','!=','service')]),
'product_uom': fields.many2one('product.uom', 'Product Unit of Measure', required=True),
'product_min_qty': fields.float('Minimum Quantity', required=True,
help="When the virtual stock goes below the Min Quantity specified for this field, OpenERP generates "\
"a procurement to bring the forecasted quantity to the Max Quantity."),
'product_max_qty': fields.float('Maximum Quantity', required=True,
help="When the virtual stock goes below the Min Quantity, OpenERP generates "\
"a procurement to bring the forecasted quantity to the Quantity specified as Max Quantity."),
'qty_multiple': fields.integer('Qty Multiple', required=True,
help="The procurement quantity will be rounded up to this multiple."),
'procurement_id': fields.many2one('procurement.order', 'Latest procurement', ondelete="set null"),
'company_id': fields.many2one('res.company','Company',required=True),
'procurement_draft_ids': fields.function(_get_draft_procurements, type='many2many', relation="procurement.order", \
string="Related Procurement Orders",help="Draft procurement of the product and location of that orderpoint"),
}
_defaults = {
'active': lambda *a: 1,
'logic': lambda *a: 'max',
'qty_multiple': lambda *a: 1,
'name': lambda x,y,z,c: x.pool.get('ir.sequence').get(y,z,'stock.orderpoint') or '',
'product_uom': lambda sel, cr, uid, context: context.get('product_uom', False),
'company_id': lambda self, cr, uid, c: self.pool.get('res.company')._company_default_get(cr, uid, 'stock.warehouse.orderpoint', context=c)
}
_sql_constraints = [
('qty_multiple_check', 'CHECK( qty_multiple > 0 )', 'Qty Multiple must be greater than zero.'),
]
_constraints = [
(_check_product_uom, 'You have to select a product unit of measure in the same category than the default unit of measure of the product', ['product_id', 'product_uom']),
]
def default_get(self, cr, uid, fields, context=None):
res = super(stock_warehouse_orderpoint, self).default_get(cr, uid, fields, context)
# default 'warehouse_id' and 'location_id'
if 'warehouse_id' not in res:
warehouse = self.pool.get('ir.model.data').get_object(cr, uid, 'stock', 'warehouse0', context)
res['warehouse_id'] = warehouse.id
if 'location_id' not in res:
warehouse = self.pool.get('stock.warehouse').browse(cr, uid, res['warehouse_id'], context)
res['location_id'] = warehouse.lot_stock_id.id
return res
def onchange_warehouse_id(self, cr, uid, ids, warehouse_id, context=None):
""" Finds location id for changed warehouse.
@param warehouse_id: Changed id of warehouse.
@return: Dictionary of values.
"""
if warehouse_id:
w = self.pool.get('stock.warehouse').browse(cr, uid, warehouse_id, context=context)
v = {'location_id': w.lot_stock_id.id}
return {'value': v}
return {}
def onchange_product_id(self, cr, uid, ids, product_id, context=None):
""" Finds UoM for changed product.
@param product_id: Changed id of product.
@return: Dictionary of values.
"""
if product_id:
prod = self.pool.get('product.product').browse(cr, uid, product_id, context=context)
d = {'product_uom': [('category_id', '=', prod.uom_id.category_id.id)]}
v = {'product_uom': prod.uom_id.id}
return {'value': v, 'domain': d}
return {'domain': {'product_uom': []}}
def copy(self, cr, uid, id, default=None, context=None):
if not default:
default = {}
default.update({
'name': self.pool.get('ir.sequence').get(cr, uid, 'stock.orderpoint') or '',
})
return super(stock_warehouse_orderpoint, self).copy(cr, uid, id, default, context=context)
class product_template(osv.osv):
_inherit="product.template"
_columns = {
'type': fields.selection([('product','Stockable Product'),('consu', 'Consumable'),('service','Service')], 'Product Type', required=True, help="Consumable: Will not imply stock management for this product. \nStockable product: Will imply stock management for this product."),
'procure_method': fields.selection([('make_to_stock','Make to Stock'),('make_to_order','Make to Order')], 'Procurement Method', required=True, help="Make to Stock: When needed, the product is taken from the stock or we wait for replenishment. \nMake to Order: When needed, the product is purchased or produced."),
'supply_method': fields.selection([('produce','Manufacture'),('buy','Buy')], 'Supply Method', required=True, help="Manufacture: When procuring the product, a manufacturing order or a task will be generated, depending on the product type. \nBuy: When procuring the product, a purchase order will be generated."),
}
_defaults = {
'procure_method': 'make_to_stock',
'supply_method': 'buy',
}
class product_product(osv.osv):
_inherit="product.product"
_columns = {
'orderpoint_ids': fields.one2many('stock.warehouse.orderpoint', 'product_id', 'Minimum Stock Rules'),
}

View File

@ -159,5 +159,19 @@ watch your stock valuation, and track production lots upstream and downstream (b
<field name="lot_stock_id" ref="stock_location_stock"/>
<field name="lot_output_id" ref="stock_location_output"/>
</record>
<record id="sequence_mrp_op_type" model="ir.sequence.type">
<field name="name">Stock orderpoint</field>
<field name="code">stock.orderpoint</field>
</record>
<record id="sequence_mrp_op" model="ir.sequence">
<field name="name">Stock orderpoint</field>
<field name="code">stock.orderpoint</field>
<field name="prefix">OP/</field>
<field name="padding">5</field>
<field name="number_next">1</field>
<field name="number_increment">1</field>
</record>
</data>
</openerp>

View File

@ -1446,5 +1446,186 @@
id="menu_action_stock_journal_form"
parent="menu_warehouse_config" sequence="1"/>
<!-- Order Point -->
<record id="view_warehouse_orderpoint_tree" model="ir.ui.view">
<field name="name">stock.warehouse.orderpoint.tree</field>
<field name="model">stock.warehouse.orderpoint</field>
<field name="arch" type="xml">
<tree string="Reordering Rules">
<field name="name"/>
<field name="warehouse_id" groups="stock.group_locations"/>
<field name="location_id" groups="stock.group_locations"/>
<field name="product_id"/>
<field name="product_uom" groups="product.group_uom"/>
<field name="product_min_qty"/>
<field name="product_max_qty"/>
</tree>
</field>
</record>
<record model="ir.ui.view" id="warehouse_orderpoint_search">
<field name="name">stock.warehouse.orderpoint.search</field>
<field name="model">stock.warehouse.orderpoint</field>
<field name="arch" type="xml">
<search string="Reordering Rules Search">
<field name="name" string="Reordering Rules"/>
<field name="warehouse_id"/>
<field name="location_id" groups="stock.group_locations"/>
<field name="company_id" groups="base.group_multi_company"/>
<field name="product_id"/>
<group expand="0" string="Group By...">
<filter string="Warehouse" icon="terp-go-home" domain="[]" context="{'group_by':'warehouse_id'}"/>
<filter string="Location" icon="terp-go-home" domain="[]" context="{'group_by':'location_id'}"/>
</group>
</search>
</field>
</record>
<record id="view_warehouse_orderpoint_form" model="ir.ui.view">
<field name="name">stock.warehouse.orderpoint.form</field>
<field name="model">stock.warehouse.orderpoint</field>
<field name="arch" type="xml">
<form string="Reordering Rules" version="7.0">
<sheet>
<group>
<group>
<field name="name" />
<field name="product_id" on_change="onchange_product_id(product_id)"/>
</group>
<group>
<field name="warehouse_id" on_change="onchange_warehouse_id(warehouse_id)" widget="selection" groups="stock.group_locations"/>
<field name="product_uom" groups="product.group_uom"/>
<field name="location_id" groups="stock.group_locations"/>
<field name="company_id" groups="base.group_multi_company" widget="selection"/>
</group>
</group>
<group>
<group string="Rules">
<field name="product_min_qty" />
<field name="product_max_qty" />
<field name="qty_multiple" string="Quantity Multiple"/>
</group>
<group string="Misc">
<field name="procurement_id" readonly="1"/>
<field name="active" />
</group>
</group>
<group string="Procurement Orders to Process">
<field name="procurement_draft_ids" nolabel="1"/>
</group>
</sheet>
</form>
</field>
</record>
<record id="action_orderpoint_form" model="ir.actions.act_window">
<field name="name">Reordering Rules</field>
<field name="res_model">stock.warehouse.orderpoint</field>
<field name="type">ir.actions.act_window</field>
<field name="view_type">form</field>
<field name="view_id" ref="view_warehouse_orderpoint_tree"/>
<field name="search_view_id" ref="warehouse_orderpoint_search" />
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Click to add a reordering rule.
</p><p>You can define your minimum stock rules, so that OpenERP will automatically create draft manufacturing orders or request for quotations according to the stock level. Once the virtual stock of a product (= stock on hand minus all confirmed orders and reservations) is below the minimum quantity, OpenERP will generate a procurement request to increase the stock up to the maximum quantity.</p>
</field>
</record>
<act_window
context="{'search_default_warehouse_id': active_id, 'default_warehouse_id': active_id}"
id="act_stock_warehouse_2_stock_warehouse_orderpoint"
name="Reordering Rules"
res_model="stock.warehouse.orderpoint"
src_model="stock.warehouse"
groups="stock.group_stock_user"/>
<act_window
context="{'product_uom': locals().has_key('uom_id') and uom_id, 'default_procurement_id': active_id}"
id="act_procurement_2_stock_warehouse_orderpoint"
name="Reordering Rules"
res_model="stock.warehouse.orderpoint"
src_model="procurement.order"
groups="stock.group_stock_user"/>
<!-- Procurements are located in Warehouse menu hierarchy, MRP users should come to Stock application to use it. -->
<menuitem id="menu_stock_sched" name="Schedulers" parent="stock.menu_stock_root" sequence="4" groups="stock.group_stock_manager"/>
<menuitem action="action_compute_schedulers" id="menu_stock_proc_schedulers" parent="menu_stock_sched" sequence="20" groups="stock.group_stock_manager"/>
<menuitem action="procurement_exceptions" id="menu_stock_procurement_action" parent="menu_stock_sched" sequence="50" groups="stock.group_stock_manager"/>
<menuitem id="menu_stock_procurement" name="Automatic Procurements" parent="stock.menu_stock_configuration" sequence="5"/>
<menuitem action="action_orderpoint_form" id="menu_stock_order_points" parent="stock.menu_stock_configuration" sequence="10"/>
<record model="ir.actions.act_window" id="product_open_orderpoint">
<field name="context">{'default_product_id': active_id, 'search_default_product_id': active_id}</field>
<field name="name">Orderpoints</field>
<field name="res_model">stock.warehouse.orderpoint</field>
</record>
<record model="ir.ui.view" id="product_template_form_view_procurement">
<field name="name">product.template.procurement</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_form_view"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='type']" position="after">
<field name="procure_method"/>
<field name="supply_method"/>
</xpath>
</field>
</record>
<record id="product_search_form_view_procurment" model="ir.ui.view">
<field name="name">product.search.procurment.form</field>
<field name="model">product.product</field>
<field name="inherit_id" ref="product.product_search_form_view"/>
<field name="arch" type="xml">
<filter name="consumable" position="before">
<filter string="Products" icon="terp-accessories-archiver" domain="[('type','=','product')]" help="Stockable products"/>
</filter>
</field>
</record>
<record model="ir.ui.view" id="product_form_view_procurement_button">
<field name="name">product.product.procurement</field>
<field name="model">product.product</field>
<field name="inherit_id" ref="stock.view_normal_procurement_locations_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='incoming_qty']" position="after">
<button string="⇒ Request Procurement" name="%(act_make_procurement)d" type="action" class="oe_link"/>
</xpath>
<xpath expr="//div[@name='buttons']" position="inside">
<button string="Orderpoints" name="%(product_open_orderpoint)d" type="action" attrs="{'invisible':[('type', '=', 'service')]}"/>
</xpath>
<xpath expr="//field[@name='cost_method']" position="before">
<field name="procure_method" groups="base.group_user"/>
<field name="supply_method" groups="base.group_user"/>
</xpath>
<xpath expr="//group[@name='general']" position="after" >
<group name="procurement_help" class="oe_grey" col="1" groups="base.group_user">
<p attrs="{'invisible': ['|','|',('type','&lt;&gt;','service'),('procure_method','&lt;&gt;','make_to_stock')]}">
When you sell this service, nothing special will be triggered
to deliver the customer, as you set the procurement method as
'Make to Stock'.
</p>
<p attrs="{'invisible': ['|','|',('type','&lt;&gt;','product'),('procure_method','&lt;&gt;','make_to_stock')]}">
When you sell this product, OpenERP will <b>use the available
inventory</b> for the delivery order.
<br/><br/>
If there are not enough quantities available, the delivery order
will wait for new products. To fulfill the inventory, you should
create others rules like orderpoints.
</p>
<p attrs="{'invisible': ['|','|',('type','&lt;&gt;','consu'),('procure_method','&lt;&gt;','make_to_stock')]}">
When you sell this product, a delivery order will be created.
OpenERP will consider that the <b>required quantities are always
available</b> as it's a consumable (as a result of this, the quantity
on hand may become negative).
</p>
</group>
</xpath>
</field>
</record>
</data>
</openerp>

View File

@ -20,6 +20,3 @@
##############################################################################
import stock_location
import procurement_pull
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -96,7 +96,7 @@ You can use the demo data as follow:
'author': 'OpenERP SA',
'images': ['images/pulled_flow.jpeg','images/pushed_flow.jpeg'],
'depends': ['procurement','stock','sale'],
'data': ['stock_location_view.xml', 'security/stock_location_security.xml', 'security/ir.model.access.csv', 'procurement_pull_workflow.xml'],
'data': ['stock_location_view.xml', 'security/stock_location_security.xml', 'security/ir.model.access.csv'],
'demo': [
'stock_location_demo_cpu1.xml',
'stock_location_demo_cpu3.yml',

View File

@ -1,79 +0,0 @@
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 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 openerp.osv import osv, fields
from openerp.tools.translate import _
class procurement_order(osv.osv):
_inherit = 'procurement.order'
def check_buy(self, cr, uid, ids, context=None):
for procurement in self.browse(cr, uid, ids, context=context):
for line in procurement.product_id.flow_pull_ids:
if line.location_id==procurement.location_id:
return line.type_proc=='buy'
return super(procurement_order, self).check_buy(cr, uid, ids)
def check_produce(self, cr, uid, ids, context=None):
for procurement in self.browse(cr, uid, ids, context=context):
for line in procurement.product_id.flow_pull_ids:
if line.location_id==procurement.location_id:
return line.type_proc=='produce'
return super(procurement_order, self).check_produce(cr, uid, ids)
def check_move(self, cr, uid, ids, context=None):
for procurement in self.browse(cr, uid, ids, context=context):
for line in procurement.product_id.flow_pull_ids:
if line.location_id==procurement.location_id:
return (line.type_proc=='move') and (line.location_src_id)
return False
def action_move_create(self, cr, uid, ids, context=None):
move_obj = self.pool.get('stock.move')
for proc in proc_obj.browse(cr, uid, ids, context=context):
line = None
for line in proc.product_id.flow_pull_ids:
if line.location_id == proc.location_id:
break
assert line, 'Line cannot be False if we are on this state of the workflow'
origin = (proc.origin or proc.name or '').split(':')[0] +':'+line.name
move_id = move_obj.create(cr, uid, {
'name': line.name,
'company_id': line.company_id and line.company_id.id or False,
'product_id': proc.product_id.id,
'date': proc.date_planned,
'product_qty': proc.product_qty,
'product_uom': proc.product_uom.id,
'product_uos_qty': (proc.product_uos and proc.product_uos_qty)\
or proc.product_qty,
'product_uos': (proc.product_uos and proc.product_uos.id)\
or proc.product_uom.id,
'partner_id': line.partner_address_id.id,
'location_id': line.location_src_id.id,
'location_dest_id': line.location_id.id,
'move_dest_id': proc.move_id and proc.move_id.id or False, # to verif, about history ?
'tracking_id': False,
'cancel_cascade': line.cancel_cascade,
'group_id': proc.group_id.id,
'state': 'confirmed',
'note': _('Move for pulled procurement coming from original location %s, pull rule %s, via original Procurement %s (#%d)') % (proc.location_id.name, line.name, proc.name, proc.id),
})
move_obj.button_confirm(cr,uid, [move_id], context=context)
return False

View File

@ -1,30 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<!-- Production -->
<record id="act_move" model="workflow.activity">
<field name="wkf_id" ref="procurement.wkf_procurement"/>
<field name="name">move</field>
<field name="kind">function</field>
<field name="action">action_move_create()</field>
</record>
<record id="trans_confirm_mto_buy01" model="workflow.transition">
<field name="act_from" ref="procurement.act_confirm_mto"/>
<field name="act_to" ref="act_move"/>
<field name="condition">check_move()</field>
</record>
<record id="trans_confirm_mto_buy02" model="workflow.transition">
<field name="act_from" ref="act_move"/>
<field name="act_to" ref="procurement.act_make_done"/>
</record>
<!-- Overwrite the default stop condition of the procurement in order to take into account its new activity
and not skipping the chained stock/move creation -->
<record id="procurement.trans_confirm_mto_make_done" model="workflow.transition">
<field name="condition">not check_produce() and not check_buy() and not check_move()</field>
</record>
</data>
</openerp>

View File

@ -103,20 +103,14 @@ class stock_location_path(osv.osv):
class product_pulled_flow(osv.osv):
_name = 'product.pulled.flow'
_description = "Pulled Flows"
_inherit = 'product.pulled.flow'
_columns = {
'name': fields.char('Name', size=64, required=True, help="This field will fill the packing Origin and the name of its moves"),
'cancel_cascade': fields.boolean('Cancel Cascade', help="Allow you to cancel moves related to the product pull flow"),
'route_id': fields.many2one('stock.location.route', 'Route'),
'delay': fields.integer('Number of Hours'),
'location_id': fields.many2one('stock.location','Destination Location', required=True, help="Is the destination location that needs supplying"),
'location_src_id': fields.many2one('stock.location','Source Location', help="Location used by Destination Location to supply"),
'procure_method': fields.selection([('make_to_stock','Make to Stock'),('make_to_order','Make to Order')], 'Procure Method', required=True, help="'Make to Stock': When needed, take from the stock or wait until re-supplying. 'Make to Order': When needed, purchase or produce for the procurement request."),
'type_proc': fields.selection([('produce','Produce'),('buy','Buy'),('move','Move')], 'Type of Procurement', required=True),
'company_id': fields.many2one('res.company', 'Company', help="Is used to know to which company the pickings and moves belong."),
'partner_address_id': fields.many2one('res.partner', 'Partner Address'),
'picking_type': fields.selection([('out','Sending Goods'),('in','Getting Goods'),('internal','Internal')], 'Shipping Type', required=True, select=True, help="Depending on the company, choose whatever you want to receive or send products"),
'invoice_state': fields.selection([
("invoiced", "Invoiced"),
("2binvoiced", "To Be Invoiced"),
@ -124,12 +118,9 @@ class product_pulled_flow(osv.osv):
required=True,),
}
_defaults = {
'cancel_cascade': False,
'procure_method': 'make_to_stock',
'type_proc': 'move',
'picking_type': 'out',
'invoice_state': 'none',
'company_id': lambda self, cr, uid, c: self.pool.get('res.company')._company_default_get(cr, uid, 'product.pulled.flow', context=c),
}
# FP Note: implement this
def _apply(self, cr, uid, rule, move, context=None):