예제 #1
0
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
예제 #2
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.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)
예제 #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)
예제 #4
0
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))
예제 #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"))
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'
예제 #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)
                        )
                    )