def _get_ctype(db, user, start, end): now = Date('.') if start <= now <= end: dyn = user_dynamic.get_user_dynamic(db, user, now) else: dyn = user_dynamic.get_user_dynamic(db, user, start) return dyn.contract_type
def approve_leave_submissions_hr(db, context, request): uid = db._db.getuid() if not common.user_has_role \ (db._db, uid, 'HR-leave-approval', 'HR-vacation') : return [] fs = request.filterspec d = {} for n in ('status', 'user', 'time_wp.project', 'first_day', 'last_day'): if n in fs: d[n] = fs[n] ls = db.leave_submission.filter(None, d) if 'approval_hr' in fs: new_ls = [] for l in ls: tp = l.time_wp.project fd = l.first_day._value ld = l.last_day._value u = l.user.id dyn = user_dynamic.get_user_dynamic(db._db, u, fd) ctp = dyn.contract_type hr = vacation.need_hr_approval \ (db._db, tp, u, ctp, fd, ld, str (l.status.name), False) ah = fs['approval_hr'].lower() == 'yes' if hr == ah: new_ls.append(l) ls = new_ls return ls
def flexi_remain (db, user, date_in_year, ctype) : y = common.start_of_year (date_in_year) eoy = common.end_of_year (y) fa = flexi_alliquot (db, user, date_in_year, ctype) acpt = db.leave_status.lookup ('accepted') cnrq = db.leave_status.lookup ('cancel requested') if not fa : return 0 sd = 0 dyn = user_dynamic.get_user_dynamic (db, user, y) if not dyn : dyn = user_dynamic.first_user_dynamic (db, user, y) while dyn : # Check the case that we found a dyn user record far in the past if dyn.valid_to and dyn.valid_to < y : dyn = user_dynamic.next_user_dynamic (db, dyn) continue if dyn.contract_type == ctype : b = dyn.valid_from if b < y : b = y if b > eoy : break e = dyn.valid_to if e > eoy or not e : e = eoy else : e -= common.day ct = dyn.contract_type if dyn.all_in : sd += flexitime_submission_days (db, user, ct, b, e, acpt, cnrq) dyn = user_dynamic.next_user_dynamic (db, dyn) return fa - sd
def remaining_until(db): db = db._db now = Date('.') uid = db.getuid() dyn = user_dynamic.get_user_dynamic(db, uid, now) return vacation.next_yearly_vacation_date \ (db, uid, dyn.contract_type, now) - common.day
def audit_user_fields(db, cl, nodeid, new_values): for n in \ ( 'firstname' , 'lastname' , 'lunch_duration' , 'lunch_start' , 'shadow_inactive' , 'shadow_max' , 'shadow_min' , 'shadow_warning' , 'uid' ) : if n in new_values and new_values[n] is None and cl.get(nodeid, n): raise Reject, "%(attr)s may not be undefined" % {'attr': _(n)} if 'status' in cl.properties: status = new_values.get('status', cl.get(nodeid, 'status')) if status == db.user_status.lookup('valid'): for n in \ ( 'org_location' , 'department' ) : if n in new_values and new_values[n] is None: dyn = get_user_dynamic(db, nodeid, Date('.')) if dyn: new_values[n] = getattr(dyn, n) else: raise Reject, "%(attr)s may not be undefined" \ % {'attr' : _ (n)} common_user_checks(db, cl, nodeid, new_values)
def get_vacation_correction (db, user, ctype = -1, date = None) : """ Get latest absolute vacation_correction. Special handling of ctype: None means ctype 'None' while -1 means "don't care, search for *any* ctype". Note that roundups interface for searching specifies -1 when searching for an empty link.... """ if date is None : date = Date ('.') dt = ";%s" % date.pretty (common.ymd) d = dict \ ( user = user , absolute = True , date = dt ) # If no ctype given, try to get dyn. user record on date and use # ctype from there. If not found we simply search for the latest # vacation correction before date. if ctype == -1 : dyn = user_dynamic.get_user_dynamic (db, user, date) if dyn : ctype = dyn.contract_type if ctype != -1 : d ['contract_type'] = ctype if ctype is None : d ['contract_type'] = '-1' # roundup: -1 means search empty vcs = db.vacation_correction.filter (None, d, sort = [('-', 'date')]) if not vcs : return for id in vcs : vc = db.vacation_correction.getnode (id) if ctype == -1 or vc.contract_type == ctype : return vc
def vacation_time_sum (db, user, ctype, start, end) : dt = common.pretty_range (start, end) dr = db.daily_record.filter (None, dict (user = user, date = dt)) dtt = [('+', 'daily_record.date')] vwp = vacation_wps (db) trs = db.time_record.filter \ (None, dict (daily_record = dr, wp = vwp), sort = dtt) vac = 0.0 if ctype == -1 : ctype = _get_ctype (db, user, Date ('.')) by_dr = {} for tid in trs : tr = db.time_record.getnode (tid) dr = db.daily_record.getnode (tr.daily_record) dyn = user_dynamic.get_user_dynamic (db, user, dr.date) # dyn is None if time_records booked but dyn record revoked for this period: if not dyn or dyn.contract_type != ctype : continue wh = user_dynamic.day_work_hours (dyn, dr.date) assert wh if dr.id not in by_dr : by_dr [dr.id] = (wh, []) assert by_dr [dr.id][0] == wh by_dr [dr.id][1].append (tr.duration) for wh, durs in by_dr.itervalues () : vac += ceil (sum (durs) / wh * 2) / 2. return vac
def vac_get_user_dynamic (db, user, ctype, date) : """ Get user_dynamic record for a vacation computation on the given date. Note that there are cases where no dyn user record exists exactly for the date but before -- or after. If the record starts a vacation period (e.g. an initial absolute vacation correction) there doesn't necessarily already exist a dynamic user record. On the other hand when computing the vacation at the end of a period no dyn user record may be available anymore (e.g., because the person has left). """ dyn = user_dynamic.get_user_dynamic (db, user, date) if not dyn : dyn = user_dynamic.find_user_dynamic (db, user, date, '-') if ( dyn and ( dyn.contract_type != ctype or not dyn.vacation_month or not dyn.vacation_day ) ) : dyn = vac_prev_user_dynamic (db, dyn, ctype) if not dyn : dyn = user_dynamic.find_user_dynamic (db, user, date, '+') if ( dyn and ( dyn.contract_type != ctype or not dyn.vacation_month or not dyn.vacation_day ) ) : dyn = vac_next_user_dynamic (db, dyn, ctype) return dyn
def _get_ctype (db, user, date) : # None is a valide contract_type, return -1 in case of error dyn = user_dynamic.get_user_dynamic (db, user, date) if not dyn : dyn = user_dynamic.last_user_dynamic (db, user, date) if not dyn : return -1 return dyn.contract_type
def get_current_ctype (db, user, dt = None) : if dt is None : dt = Date ('.') dyn = user_dynamic.get_user_dynamic (db, user, dt) if not dyn : return None ctype = dyn.contract_type return ctype
def current_user_dynamic(context, user=None): db = context._db client = context._client now = Date('.') uid = user or db.getuid() dyn = user_dynamic.get_user_dynamic(db, uid, now) if dyn: dyn = HTMLItem(client, 'user_dynamic', dyn.id) return dyn
def _user_dynamic(self): if self._cache_ud: return self._cache_ud date = '.' dyn = get_user_dynamic(self.db, self.id, date) if not dyn: raise AttributeError, "No valid dynamic user record for %s %s" \ % (self.username, date.pretty (ymd)) self._cache_ud = self.master.User_Dynamic(dyn.id) return self._cache_ud
def try_create_public_holiday (db, daily_record, date, user) : st_open = db.daily_record_status.lookup ('open') wp = public_holiday_wp (db, user, date) # Don't change anything if status not open if db.daily_record.get (daily_record, 'status') != st_open : return # Only perform public holiday processing if user has a public # holiday wp to book on. if not wp : return dyn = user_dynamic.get_user_dynamic (db, user, date) wh = user_dynamic.day_work_hours (dyn, date) if wh : loc = db.org_location.get (dyn.org_location, 'location') hol = db.public_holiday.filter \ ( None , { 'date' : common.pretty_range (date, date) , 'locations' : loc } ) if hol and wh : holiday = db.public_holiday.getnode (hol [0]) if holiday.is_half : wh = wh / 2. wh = user_dynamic.round_daily_work_hours (wh) # Check if there already is a public-holiday time_record # Update duration (and wp) if wrong trs = db.time_record.filter \ (None, dict (daily_record = daily_record)) for trid in trs : tr = db.time_record.getnode (trid) if tr.wp is None : continue tp = db.time_project.getnode \ (db.time_wp.get (tr.wp, 'project')) if tp.is_public_holiday : d = {} if tr.duration != wh : d ['duration'] = wh if tr.wp != wp : d ['wp'] = wp if d : db.time_record.set (trid, ** d) return comment = holiday.name if holiday.description : comment = '\n'.join ((holiday.name, holiday.description)) db.time_record.create \ ( daily_record = daily_record , duration = wh , wp = wp , comment = comment , work_location = db.work_location.lookup ('off') )
def check_editable (db, cl, nodeid, new_values, date = None) : if not date : date = new_values.get ('date') or cl.get (nodeid, 'date') user = new_values.get ('user') or cl.get (nodeid, 'user') fr = frozen (db, user, date) if cl == db.daily_record_freeze : fr = [f for f in fr if f != nodeid] if fr : raise Reject, _ ("Already frozen: %(date)s") % locals () if not get_user_dynamic (db, user, date) : raise Reject, _ ("No dyn. user rec for %(user)s %(date)s") % locals ()
def leave_days (db, user, first_day, last_day) : d = first_day s = 0.0 while d <= last_day : dyn = user_dynamic.get_user_dynamic (db, user, d) if not dyn : d += common.day continue wh = user_dynamic.day_work_hours (dyn, d) ld = leave_duration (db, user, d) if ld != 0 : s += ceil (ld / wh * 2) / 2. d += common.day return s
def new_daily_record (db, cl, nodeid, new_values) : """ Only create a daily_record if a user_dynamic record exists for the user. If a new daily_record is created, we check the date provided: If hours, minutes, seconds are all zero we think the time was entered in UTC and do no conversion. If one is non-zero, we get the timezone from the user information and re-encode the date as UTC -- this effectively makes the date a 'naive' date. Then we nullify hour, minute, second of the date. After that, we check that there is no duplicate daily_record with the same date for this user. """ uid = db.getuid () common.require_attributes (_, cl, nodeid, new_values, 'user', 'date') user = new_values ['user'] ttby = db.user.get (user, 'timetracking_by') uname = db.user.get (user, 'username') if ( uid != user and uid != ttby and not common.user_has_role (db, uid, 'controlling', 'admin') ) : raise Reject, _ \ ("Only user, Timetracking by user, " "and Controlling may create daily records" ) common.reject_attributes (_, new_values, 'time_record') # the following is allowed for the admin (import!) if uid != '1' : common.reject_attributes (_, new_values, 'status') date = new_values ['date'] date.hour = date.minute = date.second = 0 new_values ['date'] = date dyn = user_dynamic.get_user_dynamic (db, user, date) if not dyn and uid != '1' : raise Reject, \ _ ("No dynamic user data for %(uname)s, %(date)s") % locals () if uid != '1' and not dyn.booking_allowed : raise Reject, _ \ ("Booking not allowed for %(uname)s, %(date)s") % locals () if frozen (db, user, date) : raise Reject, _ ("Frozen: %(uname)s, %(date)s") % locals () if db.daily_record.filter \ (None, {'date' : date.pretty ('%Y-%m-%d'), 'user' : user}) : raise Reject, _ ("Duplicate record: date = %(date)s, user = %(user)s") \ % new_values new_values ['time_record'] = [] if 'status' not in new_values : new_values ['status'] = db.daily_record_status.lookup ('open') new_values ['tr_duration_ok'] = None
def format_leaves(self): """ HTML-Format of leave requests and holidays We first get leaves and absence records by user and date-range. We search for all starting in the range *and* ending in the range. In addition we search for all that start *before* the range and end *after* the range. In the next step we get all the public holidays in the range and index them by location. """ db = self.db ret = [] ret.append('<table class="timesheet">') for n, u in enumerate(self.users): user = db.user.getnode(u) if n % 20 == 0: ret.extend(self.header_line()) ret.append(' <tr>') ret.append(' <td class="name">%s</td>' % user.lastname) ret.append(' <td class="name">%s</td>' % user.firstname) ret.append(' <td class="name">%s</td>' % user.username) loc = None dyn = user_dynamic.get_user_dynamic(db, u, self.now) if dyn and dyn.org_location: loc = db.org_location.get(dyn.org_location, 'location') holidays = dict \ ((h.date.pretty (common.ymd), h) for h in self.by_location.get (loc, []) ) loc = db.location.getnode(loc) else: holidays = {} d = self.fdd while d <= self.ldd: if gmtime(d.timestamp())[6] in [5, 6]: ret.append(' <td class="holiday"/>') else: r = (self.get_holiday_entry(d, holidays, loc) or self.get_absence_entry(user, d) or self.get_leave_entry(user, d)) if r: ret.append(r) else: ret.append(self.formatlink(date=d, user=u)) d += common.day ret.append(' </tr>') ret.extend(self.header_line()) ret.append('<tr/><tr/><tr/><tr/>') ret.append('</table>') return '\n'.join(ret)
def handle_submit(db, vs): now = Date('.') wp = db.time_wp.getnode(vs.time_wp) tp = db.time_project.getnode(wp.project) dyn = user_dynamic.get_user_dynamic(db, vs.user, vs.first_day) ctype = dyn.contract_type hr_only = vacation.need_hr_approval \ (db, tp, vs.user, ctype, vs.first_day, vs.last_day, 'submitted', True) handle_crq_or_submit(db, vs, now, 'SUBMIT', hr_only) if tp.is_special_leave: try_send_mail \ ( db, vs, now , 'MAIL_SPECIAL_LEAVE_USER_TEXT' , 'MAIL_SPECIAL_LEAVE_USER_SUBJECT' )
def check_dyn_user_params(db, user, first_day, last_day): d = first_day ctype = -1 # contract_type is either None or a string, can't be numeric while d <= last_day: dyn = user_dynamic.get_user_dynamic(db, user, d) if not dyn: ymd = common.ymd raise Reject(_("No dynamic user data for %s") % d.pretty(ymd)) if dyn.vacation_yearly is None: raise Reject(_("No yearly vacation for this user")) if dyn.vacation_day is None or dyn.vacation_month is None: raise Reject(_("Vacation date setting is missing")) if ctype != dyn.contract_type and ctype != -1: raise Reject(_("Differing contract types in range")) ctype = dyn.contract_type d += common.day
def check_correction(db, cl, nodeid, new_values): common.require_attributes \ (_, cl, nodeid, new_values, 'user', 'date', 'day') if nodeid: common.require_attributes \ (_, cl, nodeid, new_values, 'absolute') else: if 'absolute' not in new_values: new_values['absolute'] = False user = new_values.get('user') if user is None: user = cl.get(nodeid, 'user') if 'date' in new_values: new_values['date'] = common.fix_date(new_values['date']) date = new_values.get('date') if date is None: date = cl.get(nodeid, 'date') if freeze.frozen(db, user, date): # Allow admin to add (initial) absolute correction if (nodeid is not None or db.getuid() != '1' or not new_values.get('absolute')): raise Reject(_("Frozen")) # Check that vacation parameters exist in dyn. user records dyn = user_dynamic.get_user_dynamic(db, user, date) username = db.user.get(user, 'username') # Check for initial creation of user/dynamic user record where # the creation of a vacation correction is triggered if not dyn: dyn = user_dynamic.first_user_dynamic(db, user) if not dyn or dyn.valid_to and dyn.valid_to < date: raise Reject \ (_ ('No current dyn. user record for "%(username)s"') % locals ()) # Check that no vacation correction is created in a year before the # first dynamic user record if dyn.valid_from.year > date.year: raise Reject \ (_ ('Dyn. user record starts too late for "%(username)s"') % locals ()) while dyn and (not dyn.valid_to or dyn.valid_to > date): if (dyn.vacation_yearly is None or not dyn.vacation_month or not dyn.vacation_day): raise Reject \ (_ ('Missing vacation parameters in dyn. user record(s)')) dyn = user_dynamic.prev_user_dynamic(db, dyn)
def leave_submission_days (db, user, ctype, start, end, type, * stati) : """ Sum leave submissions of the given type with the given status in the given time range for the given user and ctype (contract_type). """ assert start <= end dt = common.pretty_range (start, end) dts = ';%s' % start.pretty (common.ymd) dte = '%s;' % end.pretty (common.ymd) if type == 'vacation' : lwp = vacation_wps (db) elif type == 'flexi' : lwp = flexi_wps (db) else : lwp = special_wps (db) d = dict (user = user, status = list (stati), time_wp = lwp) d1 = dict (d, first_day = dt) vs1 = db.leave_submission.filter (None, d1) d2 = dict (d, last_day = dt) vs2 = db.leave_submission.filter (None, d2) d3 = dict (d, first_day = dts, last_day = dte) vs3 = db.leave_submission.filter (None, d3) vss = dict.fromkeys (vs1 + vs2 + vs3).keys () vss = [db.leave_submission.getnode (i) for i in vss] days = 0.0 for vs in vss : first_day = vs.first_day last_day = vs.last_day dyn = user_dynamic.get_user_dynamic (db, user, first_day) if not dyn : continue if dyn.contract_type != ctype : continue if first_day < start : assert vs.last_day >= start first_day = start if last_day > end : assert vs.first_day <= end last_day = end days += leave_days (db, user, first_day, last_day) return days
def create_daily_recs (db, user, first_day, last_day) : d = first_day while d <= last_day : pr = common.pretty_range (d, d) x = db.daily_record.filter (None, dict (user = user, date = pr)) if x : assert len (x) == 1 x = x [0] else : dyn = user_dynamic.get_user_dynamic (db, user, d) if not dyn : d += common.day continue x = db.daily_record.create \ ( user = user , date = d , weekend_allowed = False , required_overtime = False ) try_create_public_holiday (db, x, d, user) d += common.day
def leave_duration (db, user, date) : """ Duration of leave on a single day to be booked. """ dyn = user_dynamic.get_user_dynamic (db, user, date) wh = user_dynamic.day_work_hours (dyn, date) if not wh : return 0.0 dt = common.pretty_range (date, date) dr = db.daily_record.filter (None, dict (user = user, date = dt)) assert len (dr) == 1 try_create_public_holiday (db, dr [0], date, user) trs = db.time_record.filter (None, dict (daily_record = dr [0])) bk = 0.0 for trid in trs : tr = db.time_record.getnode (trid) if not tr.wp : continue wp = db.time_wp.getnode (tr.wp) tp = db.time_project.getnode (wp.project) if tp.is_public_holiday : bk += tr.duration assert bk <= wh return wh - bk
def handle (self) : if not self.request.form ['date'].value : raise Reject, self._ ("Date is required") self.date = Date (self.request.form ['date'].value) msg = [] for u in self.users : date = self.date dyn = user_dynamic.get_user_dynamic (self.db, u, date) if not dyn : dyn = user_dynamic.find_user_dynamic \ (self.db, u, date, direction = '-') if dyn : # there must be a valid_to date, otherwise # get_user_dynamic would have found something above date = dyn.valid_to - common.day assert (date < self.date) if dyn : try : self.db.daily_record_freeze.create \ (date = date, user = u, frozen = 1) except Reject, cause : msg.append ((str (cause), u))
def set_sap_cc(self, user, udprop, sap_cc_prop, lk, ldattr): """ Return a triple (ldap.MOD_ADD, lk, rupattr) or None if nothing to sync """ dyn = user_dynamic.get_user_dynamic(self.db, user.id, Date('.')) is_empty = True if dyn: assert udprop == 'sap_cc' if dyn.sap_cc: is_empty = False sap_cc = self.db.sap_cc.getnode(dyn.sap_cc) if sap_cc[sap_cc_prop] != ldattr: if not ldattr: return (ldap.MOD_ADD, lk, sap_cc[sap_cc_prop]) else: return (ldap.MOD_REPLACE, lk, sap_cc[sap_cc_prop]) if is_empty and ldattr != '': if ldattr is None: return None else: # untested return (ldap.MOD_DELETE, lk, None) return None
def need_hr_approval \ (db, tp, user, ctype, first_day, last_day, stname, booked = False) : if tp.approval_hr : return True if stname != 'submitted' : return False if not tp.is_vacation : # Flexitime if tp.no_overtime and tp.max_hours == 0 : dyn = user_dynamic.get_user_dynamic (db, user, first_day) if not dyn or not dyn.all_in : return False fd = first_day if first_day.year != last_day.year : while fd.year != last_day.year : eoy = common.end_of_year (fd) rem = flexi_remain (db, user, fd, ctype) dur = leave_days (db, user, fd, eoy) if rem - dur < 0 : return True fd = eoy + common.day rem = flexi_remain (db, user, fd, ctype) dur = leave_days (db, user, fd, last_day) return rem - dur < 0 else : return False day = common.day ed = next_yearly_vacation_date (db, user, ctype, last_day) - day vac = remaining_vacation (db, user, ctype, ed) assert vac is not None dur = leave_days (db, user, first_day, last_day) # don't count duration if this is already booked, so we would count # this vacation twice. if booked : dur = 0 return ceil (vac) - dur < 0
) opt, args = parser.parse_args () if opt.start or opt.end : ids = db.daily_record.filter (None, {'date' : opt.start + ';' + opt.end}) else : ids = db.daily_record.getnodeids () for dri in ids : dr = db.daily_record.getnode (dri) dur = dr.tr_duration_ok hours = hhours = 0.0 tr_full = True trs = [] trvl_tr = {} dyn = get_user_dynamic (db, dr.user, dr.date) wh = 0.0 if dyn : tr_full = dyn.travel_full wh = round_daily_work_hours (day_work_hours (dyn, dr.date)) if dur is not None : for tri in dr.time_record : tr = db.time_record.getnode (tri) trs.append (tr) hours += tr.duration act = tr.time_activity trvl = not tr_full and act and db.time_activity.get (act, 'travel') if trvl : hhours += tr.duration / 2. trvl_tr [tri] = tr else :
def main(): # most ldap info is now fetched from extensions/config.ini parser = ArgumentParser() parser.add_argument \ ( "file" , help = "CSV import file" ) parser.add_argument \ ( "-d", "--database-directory" , dest = "database_directory" , help = "Directory of the roundup installation" , default = '.' ) parser.add_argument \ ( '-D', '--delimiter' , dest = 'delimiter' , help = 'CSV delimiter character (tab)' , default = '\t' ) parser.add_argument \ ( "-f", "--field" , dest = "fields" , help = "Fields to update in dyn. user, e.g. sap_cc or department" " can be specified multiple times" , action = 'append' , default = [] ) parser.add_argument \ ( "-N", "--new" , help = "Date of new dynamic user" , default = '2017-10-01' ) parser.add_argument \ ( "-u", "--update" , help = "Update roundup" , default = False , action = 'store_true' ) parser.add_argument \ ( "-v", "--verbose" , help = "Verbose messages" , default = False , action = 'store_true' ) args = parser.parse_args() tracker = instance.open(args.database_directory) db = tracker.open('admin') sys.path.insert(1, os.path.join(args.database_directory, 'lib')) import user_dynamic r = Reader(args.file) d = DictReader(r, delimiter=args.delimiter) for line in d: if 'username' in line: try: user = db.user.getnode(db.user.lookup(line['username'])) except KeyError: print("User not found: %s" % line['username']) continue sn = user.lastname fn = user.firstname username = user.username else: sn = line['Surname'].decode('utf-8') fn = line['First name'].decode('utf-8') if not sn or not fn: print("Name empty: %(sn)s %(fn)s" % locals()) continue users = db.user.filter \ (None, dict (firstname = fn, lastname = sn, status = st)) if not users and ' ' in fn: fn = fn.split(' ', 1)[0] users = db.user.filter \ (None, dict (firstname = fn, lastname = sn, status = st)) if not users: print("User not found: %(sn)s %(fn)s" % locals()) continue if len(users) != 1: uu = [] for u in users: user = db.user.getnode(u) if (user.firstname.decode('utf-8') != fn or user.lastname.decode('utf-8') != sn): continue uu.append(u) users = uu if len(users) != 1: print(users, fn, sn) assert len(users) == 1 user = db.user.getnode(users[0]) if (user.firstname.decode('utf-8') != fn or user.lastname.decode('utf-8') != sn): print(user.firstname, user.lastname, fn, sn) username = user.username dt = date.Date(args.new) st = db.user_status.lookup('valid') # Get user dynamic record dyn = user_dynamic.get_user_dynamic(db, user.id, dt) if not dyn: print("No dyn. user record: %(username)s" % locals()) continue if dyn.valid_to: print("Dyn. user record limited: %(username)s" % locals()) continue if dyn.valid_from > dt: print("Dyn. record starts after date: %(username)s" % locals()) continue if not dyn.vacation_yearly: print("No yearly vacation: %(username)s" % locals()) continue do_create = True if dyn.valid_from == dt: do_create = False update = {} try: key = '' for k in fieldmap: f = fieldmap[k] if f in args.fields and k in line: key = line[k].strip() if f in item_map: key = item_map[f].get(key, key) cn = dyn.cl.properties[f].classname cls = db.getclass(cn) item = cls.lookup(key) if dyn[f] != item: update[f] = item except KeyError: print("%(f)s not found: %(key)s: %(username)s" % locals()) continue if update: if do_create: fields = user_dynamic.dynuser_copyfields param = dict((i, dyn[i]) for i in fields) param['valid_from'] = dt param.update(update) if args.update: id = db.user_dynamic.create(**param) if args.verbose: print("CREATED: %s" % id) else: if args.verbose: print("user_dynamic-create: %s" % param) else: if args.update: db.user_dynamic.set(dyn.id, **update) else: if args.verbose: print \ ( "user_dynamic-update: %s %s %s" % (update, fn, sn) ) if args.update: db.commit()
def weekend_allowed (db, daily_record) : user, date = [str (daily_record [i]) for i in 'user', 'date'] user = db.user.lookup (user) dyn = user_dynamic.get_user_dynamic (db, user, date) return dyn and dyn.weekend_allowed
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"))