def vac_prev_user_dynamic (db, dyn, ctype = -1) : if ctype == -1 : ctype = dyn.contract_type dyn = user_dynamic.prev_user_dynamic (db, dyn) while ( dyn and ( dyn.contract_type != ctype or not dyn.vacation_month or not dyn.vacation_day ) ) : dyn = user_dynamic.prev_user_dynamic (db, dyn) return dyn
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.act_or_latest_user_dynamic(db, user) if not dyn or dyn.valid_to and dyn.valid_to < date: username = db.user.get(user, 'username') raise Reject \ (_ ('No current dyn. user record 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 find_time_records(db, cl, nodeid, new_values): """ Search for existing time records when start/end date changes. Reject if valid records other than public holidays are found. """ if 'valid_to' not in new_values and 'valid_from' not in new_values: return dyn = cl.getnode(nodeid) valid_to = new_values.get('valid_to') valid_from = new_values.get('valid_from') next = user_dynamic.next_user_dynamic(db, dyn) prev = user_dynamic.prev_user_dynamic(db, dyn) to = next and (next.valid_from - common.day) frm = prev and (prev.valid_to) ranges = dict \ ( valid_to = common.pretty_range (valid_to, to) , valid_from = common.pretty_range (frm, valid_from - common.day) ) gaps = dict \ ( valid_to = not next or next.valid_from > valid_to , valid_from = not prev or prev.valid_to < valid_from ) msgs = dict \ ( valid_to = _ ( "There are (non public holiday) " "time records at or after %s" ) , valid_from = _ ( "There are (non public holiday) " "time records before %s" ) ) for k in ('valid_to', 'valid_from'): value = new_values.get(k) if value is not None and gaps[k]: trs = db.time_record.filter \ ( None , { 'daily_record.user': dyn.user , 'daily_record.date': ranges [k] } ) # loop for checking if time recs are public holiday for id in trs: tr = db.time_record.getnode(id) # case where no wp was entered yet if tr.wp is None: raise Reject(msgs[k] % value.pretty(common.ymd)) # case where wp was set wp = db.time_wp.getnode(tr.wp) tc = db.time_project.getnode(wp.project) if not tc.is_public_holiday: raise Reject(msgs[k] % value.pretty(common.ymd)) # loop entirely for retiring public holidys for id in trs: tr = db.time_record.getnode(id) assert tr.wp wp = db.time_wp.getnode(tr.wp) tc = db.time_project.getnode(wp.project) assert tc.is_public_holiday db.time_record.retire(id)
def next_yearly_vacation_date (db, user, ctype, date) : d = date + common.day dyn = vac_get_user_dynamic (db, user, ctype, d) if not dyn or dyn.vacation_month is None or dyn.vacation_day is None : assert 0 return None y = int (d.get_tuple () [0]) next_date = Date \ ('%04d-%02d-%02d' % (y, dyn.vacation_month, dyn.vacation_day)) if next_date < d : next_date = Date \ ('%04d-%02d-%02d' % (y + 1, dyn.vacation_month, dyn.vacation_day)) # Found a dyn user record too far in the future, can't determine # next yearly vacation date if dyn.valid_from > next_date : # Hmmm, maybe started this year? # Or re-started after some years? prev = user_dynamic.prev_user_dynamic (db, dyn, use_ct = True) if not prev or prev.valid_to < next_date : return dyn.valid_from return None while dyn.valid_from <= next_date : if dyn.valid_to > next_date : # valid dyn record return next_date ndyn = vac_next_user_dynamic (db, dyn) if ( not ndyn or ndyn.valid_from > next_date or ndyn.contract_type != ctype ) : # use last dyn record, no next or too far in the future return next_date dyn = ndyn yday = dyn.vacation_day ymon = dyn.vacation_month if yday is None or ymon is None : return next_date next_date = Date ('%04d-%02d-%02d' % (y, ymon, yday)) if next_date < d : next_date = Date ('%04d-%02d-%02d' % (y + 1, ymon, yday))
def wp_check_auto_wp (db, cl, nodeid, new_values) : """ Check that modifications to wp that has auto_wp set is ok """ if not nodeid and 'auto_wp' not in new_values : return if nodeid : if not cl.get (nodeid, 'auto_wp') : if 'auto_wp' in new_values : raise Reject \ (_ ("Property %s may not change") % _ ('auto_wp')) return # These are not allowed to change props = \ ( 'auto_wp' , 'bookers' , 'contract_type' , 'org_location' , 'project' , 'is_public' ) if nodeid : for p in props : if p in new_values : raise Reject \ (_ ("Property %s may not change for auto wp") % _ (p)) bookers = cl.get (nodeid, 'bookers') auto_wp = cl.get (nodeid, 'auto_wp') else : common.require_attributes \ ( _, cl, nodeid, new_values , 'bookers' , 'auto_wp' , 'time_project' , 'durations_allowed' ) bookers = new_values ['bookers'] auto_wp = new_values ['auto_wp'] auto_wp = db.auto_wp.getnode (auto_wp) if 'time_start' not in new_values and 'time_end' not in new_values : return start = new_values.get ('time_start') end = new_values.get ('time_end') if not start : assert nodeid start = cl.get (nodeid, 'time_start') # Cannot check for empty end here, we could set the end to empty! if 'time_end' not in new_values and nodeid : end = cl.get (nodeid, 'time_end') assert len (bookers) == 1 booker = bookers [0] freeze = freeze_date (db, booker) # Get dyn user for start dyn = user_dynamic.get_user_dynamic (db, booker, start) if not dyn and start != end : raise Reject (_ ("Invalid change of start/end: no dyn. user")) if not dyn : return if not lib_auto_wp.is_correct_dyn (dyn, auto_wp) : raise Reject \ (_ ("Invalid change of start: Invalid dyn. user")) # loop backwards through dyns if 'time_start' in new_values : # Find the first dyn user which matches up with our start date prev = dyn while prev.valid_from > start : p = user_dynamic.prev_user_dynamic (db, prev) if ( p.valid_to != prev.valid_from or not lib_auto_wp.is_correct_dyn (p, auto_wp) ) : raise Reject ("Invalid change of start: Invalid dyn. user") prev = p # We need to find previous wp if we don't start freezedate + day if prev.valid_from < start and start > freeze + common.day : d = dict \ ( auto_wp = auto_wp.id , time_end = common.pretty_range (None, start) ) wps = db.time_wp.filter (None, d, sort = ('-', 'time_end')) if not wps : raise Reject (_ ("Invalid change of start: No prev. WP")) wp = db.time_wp.getnode (wps [0]) if wp.time_end != start : raise Reject (_ ("Invalid change of start: Invalid prev. WP")) # loop forward through dyns if 'time_end' in new_values : next = dyn # Need to find next wp if dyn is valid longer than end and not # limited by a duration dur_end = lib_auto_wp.auto_wp_duration_end (db, auto_wp, booker) while next.valid_to and (not end or next.valid_to < end) : if dur_end and dur_end <= next.valid_to : break n = user_dynamic.next_user_dynamic (db, next) if ( n.valid_from != next.valid_to or not lib_auto_wp.is_correct_dyn (n, auto_wp) ) : raise Reject ("Invalid change of end: Invalid dyn. user") next = n if end and not dur_end and (not next.valid_to or end < next.valid_to) : d = dict \ ( auto_wp = auto_wp.id , time_start = common.pretty_range (end) ) wps = db.time_wp.filter (None, d, sort = ('+', 'time_start')) if not wps : raise Reject (_ ("Invalid change of end: No next WP")) wp = db.time_wp.getnode (wps [0]) if wp.time_start != end : raise Reject (_ ("Invalid change of end: Invalid next WP"))
try: db.daily_record_status.lookup('leave') except KeyError: db.daily_record_status.create \ (name = 'leave', description = "Accepted leave") s2014 = date.Date('2014-01-01') for u in db.user.getnodeids(retired=False): user = db.user.getnode(u) dyn = user_dynamic.act_or_latest_user_dynamic(db, u) while dyn and (not dyn.valid_to or dyn.valid_to >= s2014): if dyn.vacation_yearly: if dyn.vacation_day is None or dyn.vacation_month is None: db.user_dynamic.set \ (dyn.id, vacation_month = 1, vacation_day = 1) dyn = user_dynamic.prev_user_dynamic(db, dyn) for wpid in db.time_wp.getnodeids(retired=False): wp = db.time_wp.getnode(wpid) p = (not wp.bookers and not wp.time_end and (bool(wp.time_start) or wpid in ('2255', '4648'))) db.time_wp.set(wpid, is_public=p) # Fix Public_WPs query pwp = db.query.filter(None, dict(name='Public WPs', private_for='-1')) assert len(pwp) == 1 db.query.set \ ( pwp [0] , url = ':columns=name,wp_no,responsible,project,time_start,' 'time_end,cost_center&:sort=name&:filter=is_public&' ':pagesize=20&:startwith=0&is_public=yes'
def find_existing_leave(db, cl, nodeid, new_values): """ Search for existing leave requests when start/end date changes. Reject if valid records are found. """ stati = ('open', 'submitted', 'accepted', 'cancel requested') stati = list(db.leave_status.lookup(x) for x in stati) if 'valid_to' not in new_values and 'valid_from' not in new_values: return dyn = cl.getnode(nodeid) if 'valid_to' in new_values: # check if another dynamic user record exists valid_to = new_values['valid_to'] if valid_to is not None: next = user_dynamic.next_user_dynamic(db, dyn) if not next or next.valid_from > valid_to: leaves = db.leave_submission.filter \ ( None , dict ( user = dyn.user , last_day = common.pretty_range (valid_to) , status = stati ) ) if next: new_leaves = [] for id in leaves: leave = db.leave_submission.getnode(id) if valid_to <= leave.first_day < next.valid_from: new_leaves.append(id) continue if valid_to <= leave.last_day < next.valid_from: new_leaves.append(id) continue if (leave.first_day < valid_to and leave.last_day >= next.valid_from): new_leaves.append(id) continue leaves = new_leaves if leaves: raise Reject \ (_ ("There are open leave requests at or after %s" % valid_to.pretty (common.ymd) ) ) if 'valid_from' in new_values: # check if another dynamic user record exists valid_from = new_values['valid_from'] prev = user_dynamic.prev_user_dynamic(db, dyn) if not prev or prev.valid_to < valid_from: leaves = db.leave_submission.filter \ ( None , dict ( user = dyn.user , first_day = common.pretty_range (None, valid_from - common.day) , status = stati ) ) if prev: new_leaves = [] for id in leaves: leave = db.leave_submission.getnode(id) if prev.valid_to <= leave.first_day < valid_from: new_leaves.append(id) continue if prev.valid_to <= leave.last_day < valid_from: new_leaves.append(id) continue if (leave.first_day < prev.valid_to and leave.last_day >= valid_from): new_leaves.append(id) continue leaves = new_leaves if leaves: raise Reject \ (_ ("There are open leave requests before %s" % valid_from.pretty (common.ymd) ) )