trytond-approval/approval.py

198 lines
6.6 KiB
Python

from datetime import datetime
from trytond.model import Workflow, ModelSQL, ModelView, fields, DeactivableMixin
from trytond.pyson import Eval, Bool
from trytond.pool import Pool
from trytond.transaction import Transaction
from trytond.i18n import gettext
from trytond.exceptions import UserError
__all__ = ['Group', 'GroupUser', 'Request']
class Group(ModelSQL, ModelView, DeactivableMixin):
'Approval Group'
__name__ = 'approval.group'
name = fields.Char('Name', required=True)
model = fields.Many2One('ir.model', 'Model', domain=[
('id', 'in', Eval('valid_models', [])),
], depends=['valid_models'])
valid_models = fields.Function(fields.Many2Many('ir.model', None, None,
'Valid Models'),
'get_valid_models')
users = fields.Many2Many('approval.group-res.user', 'group', 'user',
'Users')
@staticmethod
def default_valid_models():
return Pool().get('approval.group').get_models_from_request()
@classmethod
def get_valid_models(cls, groups, name):
models = cls.get_models_from_request()
res = {}
for group in groups:
res[group.id] = models
return res
@staticmethod
def get_models_from_request():
pool = Pool()
Request = pool.get('approval.request')
Model = pool.get('ir.model')
return [x.id for x in Model.search([
('model', 'in', Request._get_document()),
])]
class GroupUser(ModelSQL):
'Approval Group - Users'
__name__ = 'approval.group-res.user'
group = fields.Many2One('approval.group', 'Group', required=True,
ondelete='CASCADE')
user = fields.Many2One('res.user', 'User', required=True,
ondelete='CASCADE')
class Request(Workflow, ModelSQL, ModelView):
'Approval Request'
__name__ = 'approval.request'
document = fields.Reference('Document', selection='get_document',
required=True)
version = fields.Integer('Version', readonly=True, states={
'invisible': ~Bool(Eval('version')),
})
group = fields.Many2One('approval.group', 'Group',
domain=[
['OR',
('model', '=', None),
('model', '=', Eval('model'))],
], depends=['model'])
model = fields.Function(fields.Many2One('ir.model', 'Model'),
'on_change_with_model')
request_date = fields.DateTime('Request Date', required=True)
description = fields.Text('Description')
# Cancelled state will be used when the document is moved back to draft,
# for example.
state = fields.Selection([
('pending', 'Pending'),
('approved', 'Approved'),
('rejected', 'Rejected'),
('cancelled', 'Cancelled'),
], 'State', required=True, readonly=True)
user = fields.Many2One('res.user', 'User', readonly=True, states={
'required': Eval('state').in_(['approved', 'rejected']),
}, depends=['state'])
decision_date = fields.DateTime('Decision Date', readonly=True, states={
'required': Eval('state').in_(['approved', 'rejected']),
}, depends=['state'])
@classmethod
def __setup__(cls):
super(Request, cls).__setup__()
cls._transitions |= set((
('pending', 'approved'),
('pending', 'rejected'),
('pending', 'cancelled'),
('approved', 'cancelled'),
))
cls._buttons.update({
'approve': {
'invisible': Eval('state') != 'pending',
},
'reject': {
'invisible': Eval('state') != 'pending',
},
'cancel': {
'invisible': Eval('state') != 'pending',
},
})
@staticmethod
def _get_document():
return []
@classmethod
def get_document(cls):
Model = Pool().get('ir.model')
models = cls._get_document()
models = Model.search([
('model', 'in', models),
])
return [(m.model, m.name) for m in models]
@staticmethod
def default_model():
pool = Pool()
Model = pool.get('ir.model')
model_name = Transaction().context.get('approval_request_model')
if model_name:
return Model.search([('model', '=', model_name)], limit=1)[0].id
@fields.depends('document')
def on_change_with_model(self, name=None):
Model = Pool().get('ir.model')
if not self.document:
return self.default_model()
model = str(self.document).split(',')[0]
models = Model.search([('model', '=', model)], limit=1)
if not models:
return None
return models[0].id
@staticmethod
def default_state():
return 'pending'
@classmethod
@ModelView.button
@Workflow.transition('approved')
def approve(cls, requests):
User = Pool().get('res.user')
user = User(Transaction().user)
for request in requests:
request._check_allowed_user(user)
request.user = user
decision_date = Transaction().context.get('date')
if decision_date:
request.decision_date = decision_date
else:
request.decision_date = datetime.now()
cls.save(requests)
@classmethod
@ModelView.button
@Workflow.transition('rejected')
def reject(cls, requests):
User = Pool().get('res.user')
user = User(Transaction().user)
for request in requests:
request._check_allowed_user(user)
request.user = user
request.decision_date = datetime.now()
cls.save(requests)
def _check_allowed_user(self, user):
if self.user:
if self.user != user:
raise UserError(gettext('approval.forbidden_user',
user=user.rec_name, request=self.rec_name))
elif self.group:
if user not in self.group.users:
raise UserError(gettext('approval.user_not_in_group',
user=user.rec_name, request=self.rec_name))
@classmethod
@ModelView.button
@Workflow.transition('cancelled')
def cancel(cls, requests):
pass
@classmethod
def delete(cls, requests):
for request in requests:
if request.state not in ('pending', 'cancelled'):
raise UserError(gettext(
'approval.delete_approved_rejected_request',
request=request.rec_name))
super(Request, cls).delete(requests)