Example #1
0
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
Example #2
0
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
Example #3
0
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
Example #4
0
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
Example #5
0
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)
Example #6
0
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
Example #7
0
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
Example #8
0
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
Example #9
0
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
Example #10
0
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
Example #11
0
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
Example #13
0
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')
                )
Example #14
0
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 ()
Example #15
0
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
Example #17
0
    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)
Example #18
0
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'
            )
Example #19
0
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
Example #20
0
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)
Example #21
0
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
Example #22
0
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
Example #23
0
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
Example #24
0
 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))
Example #25
0
 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
Example #26
0
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
Example #27
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
Example #30
0
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"))