Esempio n. 1
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
Esempio n. 2
0
def vac_next_user_dynamic (db, dyn, ctype = -1) :
    if ctype == -1 :
        ctype = dyn.contract_type
    dyn   = user_dynamic.next_user_dynamic (db, dyn)
    while (   dyn
          and (  dyn.contract_type != ctype
              or not dyn.vacation_month
              or not dyn.vacation_day
              )
          ) :
        dyn = user_dynamic.next_user_dynamic (db, dyn)
    return dyn
Esempio n. 3
0
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)
from roundup.password  import Password, encodePassword
dir     = os.getcwd ()
sys.path.insert (0, os.path.join (dir, 'lib'))
from user_dynamic import first_user_dynamic, next_user_dynamic, compute_balance
from common       import start_of_period, end_of_period, day
tracker = instance.open (dir)
db      = tracker.open ('admin')
ymd     = '%Y-%m-%d'

for u in db.user.getnodeids () :
    lop  = None
    dyn  = first_user_dynamic (db, u)
    while dyn :
        lop  = dyn.overtime_period
        dt   = dyn.valid_to
        dyn  = next_user_dynamic (db, dyn)
        if not dt and dyn :
            dt = dyn.valid_from
        if lop and dyn and dyn.overtime_period is None :
            otp = db.overtime_period.getnode (lop)
            eop = end_of_period   (dt - day, otp)
            sop = start_of_period (dt - day, otp)
            is_eop = eop == dt - day
            un = db.user.get (dyn.user, 'username')
            d1 = dyn.valid_from.pretty ('%Y-%m-%d')
            d2 = ''
            if dyn.valid_to :
                d2 = dyn.valid_to.pretty ('%Y-%m-%d')
            b1, bla = compute_balance (db, u, sop - day, sharp_end = True)
            b2, bla = compute_balance (db, u, dt  - day, sharp_end = True)
            if not is_eop :
Esempio n. 5
0
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"))
                username = '******' + username
            user = db.user.lookup(username)
        rounded = line[idx_r]
        exact = float(line[idx_e])
        if rounded not in broken_int:
            assert int(rounded) == float(rounded)
            assert ceil(exact) == int(rounded)
        dyn = user_dynamic.get_user_dynamic(db, user, s2014)
        if not dyn:
            dyn = user_dynamic.find_user_dynamic(db, user, s2014)
        while dyn:
            d = {}
            if dyn.vacation_yearly is None:
                d['vacation_yearly'] = 25.0
            if dyn.vacation_day is None or dyn.vacation_month is None:
                d['vacation_day'] = 1
                d['vacation_month'] = 1
            if d:
                assert 'vacation_yearly' in d
                print "WARN: dyn %s/%s had no vacation_yearly" \
                    % (username, dyn.valid_from.pretty (common.ymd))
                db.user_dynamic.set(dyn.id, **d)
            dyn = user_dynamic.next_user_dynamic(db, dyn)
        vc = db.vacation_correction.filter \
            (None, dict (user = user, date = dt, absolute = True))
        if not vc:
            db.vacation_correction.create \
                (absolute = True, date = s2014, user = user, days = exact)

db.commit()
Esempio n. 7
0
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)
                        )
                    )
def check_auto_wp (db, auto_wp_id, userid) :
    """ Get latest non-frozen dynamic user record for this user and then
        look through auto WPs and check if they conform: There should be
        a WP for each contiguous time a dynamic user record with
        do_auto_wp set is available for this user.
    """
    user   = db.user.getnode (userid)
    snam   = str (user.username).split ('@') [0]
    desc   = "Automatic wp for user %s" % snam
    # Very early default should we not find any freeze records
    start  = Date ('1970-01-01')
    freeze = freeze_date (db, userid)
    auto_wp = db.auto_wp.getnode (auto_wp_id)
    duration_end = auto_wp_duration_end (db, auto_wp, userid)
    # Nothing todo if this ended all before freeze
    if duration_end and duration_end < start :
        return
    tp  = db.time_project.getnode (auto_wp.time_project)
    # Get dynamic user record on or after start
    dyn = find_user_dynamic (db, userid, start, ct = auto_wp.contract_type)
    # If auto_wp isn't valid, we set dyn to None this will invalidate
    # all WPs found:
    if not auto_wp.is_valid :
        dyn = None
    # Find first dyn with do_auto_wp and org_location properly set
    while dyn and not is_correct_dyn (dyn, auto_wp) :
        dyn = next_user_dynamic (db, dyn, use_ct = True)
    # Find all wps auto-created from this auto_wp after start.
    # Note that all auto wps have a start time. Only the very first wp
    # may start *before* start (if the time_end of the time_wp is either
    # empty or after start)
    d = dict \
        ( auto_wp    = auto_wp_id
        , time_start = pretty_range (start)
        , bookers    = userid
        )
    wps = db.time_wp.filter (None, d, sort = ('+', 'time_start'))
    wps = [db.time_wp.getnode (w) for w in wps]
    d ['time_start'] = pretty_range (None, start)
    wp1 = db.time_wp.filter (None, d, sort = ('-', 'time_start'), limit = 1)
    assert len (wp1) <= 1
    if wp1 :
        wp = db.time_wp.getnode (wp1 [0])
        is_same = wps and wps [0].id == wp.id
        if not is_same and not wp.time_end or wp.time_end > start :
            wps.insert (0, wp)
    # Now we have the first relevant dyn user record and a list of WPs
    # The list of WPs might be empty
    try :
        wp = wps.pop (0)
    except IndexError :
        wp = None

    while dyn :
        # Remember the start time of the first dyn
        dyn_start = dyn.valid_from
        # we compute the range of contiguous dyns with same do_auto_wp setting
        while dyn and dyn.valid_to :
            n = next_user_dynamic (db, dyn, use_ct = True)
            if  (   n
                and is_correct_dyn (n, auto_wp)
                and n.valid_from == dyn.valid_to
                ) :
                dyn = n
            else :
                break
        end_time = dyn.valid_to
        if duration_end and (not end_time or end_time > duration_end) :
            end_time = duration_end
        if end_time and end_time <= dyn_start :
            dyn = None
            break
        # Limit the first wp(s) if the start time of the dyn user record is
        # after the start time of the frozen range and the wp starts
        # before that.
        while wp and wp.time_start < start and dyn_start > start :
            if wp.time_end > start :
                n = '%s -%s' % (snam, start.pretty (ymd))
                d = dict (time_end = start, name = n)
                if wp.description != desc :
                    d ['description'] = desc
                db.time_wp.set (wp.id, **d)
            try :
                wp = wps.pop (0)
            except IndexError :
                wp = None
        assert not wp or wp.time_start > start

        # Check if there are wps that start before the validity of the
        # current dynamic user record
        while wp and wp.time_start < dyn_start :
            if wp.time_end and wp.time_end <= dyn_start :
                # Check that really nothing is booked on this WP
                d = dict (wp = wp.id)
                d ['daily_record.user'] = userid
                d ['daily_record.date'] = pretty_range \
                    (wp.time_start, wp.time_end)
                trs = db.time_record.filter (None, d)
                # If something is booked we set end = start
                # Otherwise we retire the wp
                if not trs :
                    db.time_wp.retire (wp.id)
                else :
                    n = '%s -%s' % (snam, wp.time_start.pretty (ymd))
                    d = dict (time_end = wp.time_start, name = n)
                    if wp.description != desc :
                        d ['description'] = desc
                    db.time_wp.set (wp.id, **d)
                try :
                    wp = wps.pop (0)
                except IndexError :
                    wp = None
            else :
                d = dict (time_start = dyn_start)
                if wp.description != desc :
                    d ['description'] = desc
                db.time_wp.set (wp.id, **d)
                break

        # We now either have a wp with correct start date or
        # with a start date > dyn.valid_to or none
        if wp and (not dyn.valid_to or wp.time_start < dyn.valid_to) :
            # We may have not a single wp but a set of 'fragments'
            # before the end-date of the dyn user record (or an open
            # end)
            while (   wps
                  and (  end_time and wp.time_end < end_time
                      or not end_time and wp.time_end
                      )
                  ) :
                if wps [0].time_start > end_time :
                    break
                assert not wps [0].time_end or wp.time_end <= wps [0].time_end
                if wp.time_end != wps [0].time_start :
                    n = '%s -%s' % (snam, wps [0].time_start.pretty (ymd))
                    d = dict (time_end = wps [0].time_start, name = n)
                    if wp.description != desc :
                        d ['description'] = desc
                    db.time_wp.set (wp.id, ** d)
                wp = wps.pop (0)
            if end_time :
                if wp.time_end != end_time :
                    n = '%s -%s' % (snam, end_time.pretty (ymd))
                    d = dict (time_end = end_time, name = n)
                    if wp.description != desc :
                        d ['description'] = desc
                    db.time_wp.set (wp.id, **d)
            else :
                if wp.time_end :
                    d = dict (time_end = None, name = snam)
                    if wp.description != desc :
                        d ['description'] = desc
                    db.time_wp.set (wp.id, **d)
            try :
                wp = wps.pop (0)
            except IndexError :
                wp = None
        else :
            # If we have no wp, create one
            d = dict \
                ( auto_wp           = auto_wp_id
                , project           = auto_wp.time_project
                , durations_allowed = auto_wp.durations_allowed
                , bookers           = [userid]
                , description       = desc
                , name              = snam
                , time_start        = max (start, dyn_start)
                , is_public         = False
                , planned_effort    = 0
                , responsible       = tp.responsible
                )
            # If the dyn is time-limited we have to name the wp
            # appropriately and limit the end-time of the wp.
            if end_time :
                d ['time_end'] = end_time
                d ['name']     = '%s -%s' % (snam, end_time.pretty (ymd))
            wpid = db.time_wp.create (**d)
        od  = dyn
        dyn = next_user_dynamic (db, dyn, use_ct = True)
        while dyn and not is_correct_dyn (dyn, auto_wp) :
            dyn = next_user_dynamic (db, dyn, use_ct = True)
        if not dyn :
            if not end_time :
                assert not wp and not wps
    if wp :
        wps.insert (0, wp)
        wp = None
        # Retire WPs that are left over but before that check that
        # nothing is booked.
        for wp in wps :
            d = dict (wp = wp.id)
            d ['daily_record.user'] = userid
            trs = db.time_record.filter (None, d)
            if not trs :
                db.time_wp.retire (wp.id)
            else :
                n = '%s -%s' % (snam, wp.time_start.pretty (ymd))
                d = dict (time_end = wp.time_start, name = n)
                if wp.description != desc :
                    d ['description'] = desc
                db.time_wp.set (wp.id, **d)
        wps = []
        wp  = None