def handle_crq_or_submit(db, vs, now, conf_string, hr_only): user = db.user.getnode(vs.user) # always send to supervisor (and/or substitute), too. emails = [ db.user.get(x, 'address') for x in common.tt_clearance_by(db, vs.user) ] if hr_only: emails.extend \ (db.user.get (u, 'address') for u in common.get_uids_with_role (db, 'HR-leave-approval') ) url = '%sleave_submission?@template=approve' % db.config.TRACKER_WEB approval_type = '' try: if hr_only: s = 'MAIL_LEAVE_SUPERVISOR_%s_APPROVE_HR' % conf_string else: s = 'MAIL_LEAVE_SUPERVISOR_%s_APPROVE_SV' % conf_string approval_type = getattr(db.config.ext, s) except InvalidOptionError: pass try_send_mail \ ( db, vs, now , 'MAIL_LEAVE_SUPERVISOR_%s_TEXT' % conf_string , 'MAIL_LEAVE_SUPERVISOR_%s_SUBJECT' % conf_string , url = url , approval_type = approval_type , email = emails )
def approval_for_leave_submission (db, userid, itemid) : """User is allowed to view leave submission if he is the supervisor or the person to whom approvals are delegated. Viewing is allowed by the supervisor or the person to whom approvals are delegated. """ ownerid = db.leave_submission.get (itemid, 'user') clearance = tt_clearance_by (db, ownerid) return userid in clearance
def approval_for_time_record (db, userid, itemid) : """User is allowed to view time record if he is the supervisor or the person to whom approvals are delegated. Viewing is allowed by the supervisor or the person to whom approvals are delegated. """ dr = db.time_record.get (itemid, 'daily_record') ownerid = db.daily_record.get (dr, 'user') clearance = tt_clearance_by (db, ownerid) return userid in clearance
def approver_daily_record (db, userid, itemid) : """User is allowed to edit daily record if he is supervisor. A negative itemid indicates that the record doesn't exist yet -- we allow creation in this case. Modification is only allowed by the supervisor. """ if int (itemid) < 0 : # allow creation return True ownerid = db.daily_record.get (itemid, 'user') if not ownerid : return False clearance = tt_clearance_by (db, ownerid) return userid in clearance
def leave_wp (db, dr, wp, start, end, duration) : if not wp : return False if start is not None or end is not None or duration is None : return False tp = db.time_project.getnode (db.time_wp.get (wp, 'project')) if not tp.approval_required : return False # Only search for non-cancelled non-retired non-declined st = [] unwanted = ('cancelled', 'retired', 'declined') for stid in db.leave_status.getnodeids (retired = False) : if db.leave_status.get (stid, 'name') in unwanted : continue st.append (stid) vs = vacation.leave_submissions_on_date \ (db, dr.user, dr.date, filter = dict (status = st)) if not vs : return False assert len (vs) == 1 vs = vs [0] if vs.status != db.leave_status.lookup ('accepted') : return False clearer = common.tt_clearance_by (db, dr.user) uid = db.getuid () ld = vacation.leave_duration (db, dr.user, dr.date) if tp.max_hours is not None : ld = min (ld, tp.max_hours) if not ld and tp.max_hours != 0 : return False if ld != duration : return False if ( (uid in clearer and not tp.approval_hr) or ( common.user_has_role (db, uid, 'HR-leave-approval') and tp.approval_hr ) ) : return True return False
def generate(self, ep_status): """ Buttons in leave submission forms (edit or approval) """ ret = [] self.ep_status = ep_status self.user = ep_status.item.user.id self.sunick = str(ep_status.item.user.supervisor.nickname).upper() stname = str(ep_status.prop.name) db = ep_status.item._db if (self.uid == self.user and stname in self.user_buttons): for b in self.user_buttons[stname]: ret.append(self.button(*b)) elif stname in self.approve_buttons and self.uid != self.user: if common.user_has_role(self.db, self.uid, 'HR-leave-approval'): for b in self.approve_buttons[stname]: ret.append(self.button(*b)) else: tp = ep_status.item.time_wp.project.id tp = db.time_project.getnode(tp) first_day = ep_status.item.first_day._value last_day = ep_status.item.last_day._value dyn = user_dynamic.get_user_dynamic \ (db, self.user, last_day) ctype = dyn.contract_type need_hr = vacation.need_hr_approval \ (db, tp, self.user, ctype, first_day, last_day, stname) if (self.uid in common.tt_clearance_by(self.db, self.user) and not need_hr): for b in self.approve_buttons[stname]: ret.append(self.button(*b)) if ret: ret.append \ ( '<input type="hidden" name="%s@status" value="%s">' % (ep_status.item.designator (), stname) ) return ''.join(ret)
def check_submission(db, cl, nodeid, new_values): """ Check that changes to a leave submission are ok. We basically allow changes of first_day, last_day, and time_wp in status 'open'. The user must never change. The status transitions are bound to certain roles. Note that this auditor is called *after* it has been verified that a requested state change is at least possible (although we still have to check the role). """ common.reject_attributes(_, new_values, 'user', 'approval_hr') old = cl.getnode(nodeid) uid = db.getuid() user = old.user old_status = db.leave_status.get(old.status, 'name') if old_status != 'accepted': common.reject_attributes(_, new_values, 'comment_cancel') new_status = db.leave_status.get \ (new_values.get ('status', old.status), 'name') if old_status != 'open': common.reject_attributes \ (_, new_values, 'first_day', 'last_day', 'time_wp', 'comment') fix_dates(new_values) first_day = new_values.get('first_day', cl.get(nodeid, 'first_day')) last_day = new_values.get('last_day', cl.get(nodeid, 'last_day')) if freeze.frozen(db, user, first_day): raise Reject(_("Frozen")) time_wp = new_values.get('time_wp', cl.get(nodeid, 'time_wp')) comment = new_values.get('comment', cl.get(nodeid, 'comment')) check_range(db, nodeid, user, first_day, last_day) check_wp(db, time_wp, user, first_day, last_day, comment) if old_status in ('open', 'submitted'): vacation.create_daily_recs(db, user, first_day, last_day) if 'first_day' in new_values or 'last_day' in new_values: if vacation.leave_days(db, user, first_day, last_day) == 0: raise Reject(_("Vacation request for 0 days")) check_dyn_user_params(db, user, first_day, last_day) if old_status in ('open', 'submitted'): check_dr_status(db, user, first_day, last_day, 'open') if old_status in ('accepted', 'cancel requested'): check_dr_status(db, user, first_day, last_day, 'leave') if old_status != new_status: if (old_status == 'accepted' and new_status == 'cancel requested'): common.require_attributes \ (_, cl, nodeid, new_values, 'comment_cancel') # Allow special HR role to do any (possible) state changes # Except for approval of own records if (common.user_has_role(db, uid, 'HR-vacation') and (uid != user or new_status not in ('accepted', 'declined', 'cancelled'))): ok = True else: ok = False tp = db.time_project.getnode \ (db.time_wp.get (old.time_wp, 'project')) if not ok and uid == user: if old_status == 'open' and new_status == 'submitted': ok = True if (old_status == 'accepted' and new_status == 'cancel requested'): ok = True if old_status == 'submitted' and new_status == 'open': ok = True if old_status == 'open' and new_status == 'cancelled': ok = True elif not ok: clearer = common.tt_clearance_by(db, user) dyn = user_dynamic.get_user_dynamic(db, user, first_day) ctype = dyn.contract_type hr_only = vacation.need_hr_approval \ (db, tp, user, ctype, first_day, last_day, old_status) if (uid != user and ((uid in clearer and not hr_only) or common.user_has_role(db, uid, 'HR-leave-approval'))): if (old_status == 'submitted' and new_status in ('accepted', 'declined')): ok = True if (old_status == 'cancel requested' and (new_status == 'cancelled' or new_status == 'accepted')): ok = True if not ok: raise Reject(_("Permission denied"))
def check_daily_record (db, cl, nodeid, new_values) : """ Check that status changes are OK. Allowed changes: - From open to submitted by user or by HR But only if no leave submission in state 'submitted', 'approved', 'cancel requested' exists - From submitted to accepted by supervisor or by HR but don't allow accepting own records - From submitted to open by supervisor or by HR or by user - From accepted to open by HR - From open to leave if an accepted leave_submission exists - From leave to open if leave_submissions exist which are *all* in state cancel """ for i in 'user', 'date' : if i in new_values and db.getuid () != '1' : raise Reject, _ ("%(attr)s may not be changed") % {'attr' : _ (i)} if i in ('status',) : if i in new_values and not new_values [i] : raise Reject, _ ("%(attr)s must be set") % {'attr' : _ (i)} user = cl.get (nodeid, 'user') date = cl.get (nodeid, 'date') if frozen (db, user, date) and new_values.keys () != ['tr_duration_ok'] : uname = db.user.get (user, 'username') raise Reject, _ ("Frozen: %(uname)s, %(date)s") % locals () uid = db.getuid () is_hr = common.user_has_role (db, uid, 'hr') old_status = cl.get (nodeid, 'status') status = new_values.get ('status', old_status) may_give_clearance = uid in common.tt_clearance_by (db, user) vs_exists = False st_accp = db.leave_status.lookup ('accepted') vs = vacation.leave_submissions_on_date (db, user, date) # All leave submissions in state cancelled (or declined)? # Check if at least one is cancelled cn = db.leave_status.lookup ('cancelled') dc = db.leave_status.lookup ('declined') op = db.leave_status.lookup ('open') vs_cancelled = True if not vs : vs_cancelled = False if vs_cancelled : for v in vs : if v.status == dc : continue if v.status == cn : vs_cancelled = True else : vs_cancelled = False break vs_has_valid = False for v in vs : if v.status == op or v.status == cn or v.status == dc : continue vs_has_valid = True break vs = [v for v in vs if v.status == st_accp] if vs : assert len (vs) == 1 vs_accepted = True old_status, status = \ [db.daily_record_status.get (i, 'name') for i in [old_status, status]] ttby = db.user.get (user, 'timetracking_by') if status != old_status : if not ( ( status == 'submitted' and old_status == 'open' and (is_hr or user == uid or ttby == uid) and time_records_consistent (db, cl, nodeid) and not vs_has_valid ) or ( status == 'accepted' and old_status == 'submitted' and (is_hr or may_give_clearance) and user != uid ) or ( status == 'open' and old_status == 'submitted' and (is_hr or user == uid or may_give_clearance) ) or ( status == 'open' and old_status == 'accepted' and is_hr ) or ( status == 'leave' and old_status == 'open' and vs_accepted ) or ( status == 'open' and old_status == 'leave' and vs_cancelled ) ) : msg = "Invalid Transition" if old_status == 'open' : if 'status' == 'submitted' : if not is_hr and user != uid : msg = _ ("Permission denied") elif not time_records_consistent (db, cl, nodeid) : msg = _ ("Inkonsistent time records") elif vs_has_valid : msg = _ ("Leave submission exists") elif 'status' == 'leave' : msg = _ ("No accepted leave submission") elif old_status == 'leave' : msg = _ ("Leave submission not cancelled") elif old_status == 'accepted' : msg = _ ("Re-open only by HR") elif old_status == 'submitted' : if status == 'accepted' : if not is_hr and not may_give_clearance : msg = _ ("Permission denied") elif user == uid : msg = _ ("May not self-approve") elif status == 'open' : if not is_hr and user != uid and not may_give_clearance : msg = _ ("Permission denied") raise Reject, \ ( _ ("Denied state change: %(old_status)s->%(status)s: %(msg)s") % locals () )
#!/usr/bin/python import sys, os from roundup import date from roundup import instance tracker = sys.argv[1] sys.path.insert(1, os.path.join(tracker, 'lib')) from common import tt_clearance_by tracker = instance.open(tracker) db = tracker.open('admin') submitted = db.daily_record_status.lookup('submitted') drecs = db.daily_record.find(status=submitted) users = {} clearers = {} for dr in drecs: users[db.daily_record.get(dr, 'user')] = 1 for u in users.iterkeys(): for c in tt_clearance_by(db, u, only_subs=True): clearers[c] = 1 print clearers