##############################################################################
import time
+from psycopg2 import OperationalError
-from datetime import datetime
-from dateutil.relativedelta import relativedelta
-
+from openerp import SUPERUSER_ID
from openerp.osv import fields, osv
import openerp.addons.decimal_precision as dp
from openerp.tools.translate import _
import openerp
+PROCUREMENT_PRIORITIES = [('0', 'Not urgent'), ('1', 'Normal'), ('2', 'Urgent'), ('3', 'Very Urgent')]
+
class procurement_group(osv.osv):
'''
- The procurement requirement class is used to group products together
+ The procurement group class is used to group products together
when computing procurements. (tasks, physical products, ...)
The goal is that when you have one sale order of several products
_description = 'Procurement Requisition'
_order = "id desc"
_columns = {
- 'name': fields.char('Reference', required=True),
+ 'name': fields.char('Reference', required=True),
'move_type': fields.selection([
('direct', 'Partial'), ('one', 'All at once')],
'Delivery Method', required=True),
- 'partner_id': fields.many2one('res.partner', string = 'Partner'), #Sale should pass it here
- 'procurement_ids': fields.one2many('procurement.order', 'group_id', 'Procurements'),
+ 'procurement_ids': fields.one2many('procurement.order', 'group_id', 'Procurements'),
}
_defaults = {
- 'name': lambda self, cr, uid, c: self.pool.get('ir.sequence').get(cr, uid, 'procurement.group') or '',
- 'move_type': lambda self, cr, uid, c: 'one'
+ 'name': lambda self, cr, uid, c: self.pool.get('ir.sequence').next_by_code(cr, uid, 'procurement.group') or '',
+ 'move_type': lambda self, cr, uid, c: 'direct'
}
class procurement_rule(osv.osv):
_columns = {
'name': fields.char('Name', required=True,
help="This field will fill the packing origin and the name of its moves"),
+ 'active': fields.boolean('Active', help="If unchecked, it will allow you to hide the rule without removing it."),
'group_propagation_option': fields.selection([('none', 'Leave Empty'), ('propagate', 'Propagate'), ('fixed', 'Fixed')], string="Propagation of Procurement Group"),
'group_id': fields.many2one('procurement.group', 'Fixed Procurement Group'),
'action': fields.selection(selection=lambda s, cr, uid, context=None: s._get_action(cr, uid, context=context),
_defaults = {
'group_propagation_option': 'propagate',
'sequence': 20,
+ 'active': True,
}
_columns = {
'name': fields.text('Description', required=True),
- 'origin': fields.char('Source Document', size=64,
+ 'origin': fields.char('Source Document',
help="Reference of the document that created this Procurement.\n"
- "This is automatically completed by OpenERP."),
+ "This is automatically completed by Odoo."),
'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, track_visibility='onchange'),
+ 'priority': fields.selection(PROCUREMENT_PRIORITIES, 'Priority', required=True, select=True, track_visibility='onchange'),
'date_planned': fields.datetime('Scheduled Date', required=True, select=True, track_visibility='onchange'),
'group_id': fields.many2one('procurement.group', 'Procurement Group'),
('exception', 'Exception'),
('running', 'Running'),
('done', 'Done')
- ], 'Status', required=True, track_visibility='onchange'),
+ ], 'Status', required=True, track_visibility='onchange', copy=False),
}
_defaults = {
procurements = self.read(cr, uid, ids, ['state'], context=context)
unlink_ids = []
for s in procurements:
- if s['state'] in ['draft', 'cancel']:
+ if s['state'] == 'cancel':
unlink_ids.append(s['id'])
else:
raise osv.except_osv(_('Invalid Action!'),
This function returns an action that display existing procurement orders
of same procurement group of given ids.
'''
- mod_obj = self.pool.get('ir.model.data')
act_obj = self.pool.get('ir.actions.act_window')
- result = mod_obj.get_object_reference(cr, uid, 'procurement', 'do_view_procurements')
- id = result and result[1] or False
- result = act_obj.read(cr, uid, [id], context=context)[0]
+ action_id = self.pool.get('ir.model.data').xmlid_to_res_id(cr, uid, 'procurement.do_view_procurements', raise_if_not_found=True)
+ result = act_obj.read(cr, uid, [action_id], context=context)[0]
group_ids = set([proc.group_id.id for proc in self.browse(cr, uid, ids, context=context) if proc.group_id])
result['domain'] = "[('group_id','in',[" + ','.join(map(str, list(group_ids))) + "])]"
return result
def reset_to_confirmed(self, cr, uid, ids, context=None):
return self.write(cr, uid, ids, {'state': 'confirmed'}, context=context)
- def run(self, cr, uid, ids, context=None):
+ def run(self, cr, uid, ids, autocommit=False, context=None):
for procurement_id in ids:
+ #we intentionnaly do the browse under the for loop to avoid caching all ids which would be resource greedy
+ #and useless as we'll make a refresh later that will invalidate all the cache (and thus the next iteration
+ #will fetch all the ids again)
procurement = self.browse(cr, uid, procurement_id, context=context)
if procurement.state not in ("running", "done"):
- if self._assign(cr, uid, procurement, context=context):
- procurement.refresh()
- res = self._run(cr, uid, procurement, context=context or {})
- if res:
- self.write(cr, uid, [procurement.id], {'state': 'running'}, context=context)
+ try:
+ if self._assign(cr, uid, procurement, context=context):
+ res = self._run(cr, uid, procurement, context=context or {})
+ if res:
+ self.write(cr, uid, [procurement.id], {'state': 'running'}, context=context)
+ else:
+ self.write(cr, uid, [procurement.id], {'state': 'exception'}, context=context)
else:
+ self.message_post(cr, uid, [procurement.id], body=_('No rule matching this procurement'), context=context)
self.write(cr, uid, [procurement.id], {'state': 'exception'}, context=context)
- else:
- self.message_post(cr, uid, [procurement.id], body=_('No rule matching this procurement'), context=context)
- self.write(cr, uid, [procurement.id], {'state': 'exception'}, context=context)
+ if autocommit:
+ cr.commit()
+ except OperationalError:
+ if autocommit:
+ cr.rollback()
+ continue
+ else:
+ raise
return True
- def check(self, cr, uid, ids, context=None):
+ def check(self, cr, uid, ids, autocommit=False, context=None):
done_ids = []
for procurement in self.browse(cr, uid, ids, context=context):
- result = self._check(cr, uid, procurement, context=context)
- if result:
- done_ids.append(procurement.id)
+ try:
+ result = self._check(cr, uid, procurement, context=context)
+ if result:
+ done_ids.append(procurement.id)
+ if autocommit:
+ cr.commit()
+ except OperationalError:
+ if autocommit:
+ cr.rollback()
+ continue
+ else:
+ raise
if done_ids:
self.write(cr, uid, done_ids, {'state': 'done'}, context=context)
return done_ids
#
# Scheduler
#
- def run_scheduler(self, cr, uid, use_new_cursor=False, context=None):
+ def run_scheduler(self, cr, uid, use_new_cursor=False, company_id = False, context=None):
'''
- Call the scheduler to check the procurement order
+ Call the scheduler to check the procurement order. This is intented to be done for all existing companies at
+ the same time, so we're running all the methods as SUPERUSER to avoid intercompany and access rights issues.
@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 use_new_cursor: if set, use a dedicated cursor and auto-commit after processing each procurement.
+ This is appropriate for batch jobs only.
@param context: A standard dictionary for contextual values
@return: Dictionary of values
'''
context = {}
try:
if use_new_cursor:
- cr = openerp.registry(use_new_cursor).db.cursor()
-
- 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')
+ cr = openerp.registry(cr.dbname).cursor()
# Run confirmed procurements
+ dom = [('state', '=', 'confirmed')]
+ if company_id:
+ dom += [('company_id', '=', company_id)]
+ prev_ids = []
while True:
- ids = self.search(cr, uid, [('state', '=', 'confirmed'), ('date_planned', '<=', maxdate)], context=context)
- if not ids:
+ ids = self.search(cr, SUPERUSER_ID, dom, context=context)
+ if not ids or prev_ids == ids:
break
- self.run(cr, uid, ids, context=context)
+ else:
+ prev_ids = ids
+ self.run(cr, SUPERUSER_ID, ids, autocommit=use_new_cursor, context=context)
if use_new_cursor:
cr.commit()
# Check if running procurements are done
offset = 0
+ dom = [('state', '=', 'running')]
+ if company_id:
+ dom += [('company_id', '=', company_id)]
+ prev_ids = []
while True:
- ids = self.search(cr, uid, [('state', '=', 'running'), ('date_planned', '<=', maxdate)], offset=offset, context=context)
- if not ids:
+ ids = self.search(cr, SUPERUSER_ID, dom, offset=offset, context=context)
+ if not ids or prev_ids == ids:
break
- done = self.check(cr, uid, ids, context=context)
- offset += len(ids) - len(done)
+ else:
+ prev_ids = ids
+ self.check(cr, SUPERUSER_ID, ids, autocommit=use_new_cursor, context=context)
if use_new_cursor:
cr.commit()
cr.close()
except Exception:
pass
+
return {}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: