Esempio n. 1
0
    def __init__(self, *args, **kwargs):
        super(OutstandingHoursFilterForm, self).__init__(*args, **kwargs)

        # Check all statuses by default.
        statuses = Attribute.statuses.all()
        self.fields['statuses'].queryset = statuses
        self.fields['statuses'].initial = statuses

        month_start = utils.get_month_start().date()
        self.fields['to_date'].required = True
        self.fields['to_date'].initial = month_start - relativedelta(days=1)
        self.fields['from_date'].initial = None
Esempio n. 2
0
def report_payroll_summary(request):
    date = timezone.now() - relativedelta(months=1)
    from_date = utils.get_month_start(date).date()
    to_date = from_date + relativedelta(months=1)

    year_month_form = PayrollSummaryReportForm(request.GET or None, initial={
        'month': from_date.month,
        'year': from_date.year,
    })

    if year_month_form.is_valid():
        from_date, to_date = year_month_form.save()
    last_billable = utils.get_last_billable_day(from_date)
    projects = utils.get_setting('EMS_PAID_LEAVE_PROJECTS')
    weekQ = Q(end_time__gt=utils.get_week_start(from_date),
              end_time__lt=last_billable + relativedelta(days=1))
    monthQ = Q(end_time__gt=from_date, end_time__lt=to_date)
    workQ = ~Q(project__in=projects.values())
    statusQ = Q(status=Entry.INVOICED) | Q(status=Entry.APPROVED)
    # Weekly totals
    week_entries = Entry.objects.date_trunc('week').filter(
        weekQ, statusQ, workQ
    )
    date_headers = generate_dates(from_date, last_billable, by='week')
    weekly_totals = list(get_project_totals(week_entries, date_headers,
                                            'total', overtime=True))
    # Monthly totals
    leave = Entry.objects.filter(monthQ, ~workQ)
    leave = leave.values('user', 'hours', 'project__name')
    extra_values = ('project__type__label',)
    month_entries = Entry.objects.date_trunc('month', extra_values)
    month_entries_valid = month_entries.filter(monthQ, statusQ, workQ)
    labels, monthly_totals = get_payroll_totals(month_entries_valid, leave)
    # Unapproved and unverified hours
    entries = Entry.objects.filter(monthQ).order_by()  # No ordering
    user_values = ['user__pk', 'user__first_name', 'user__last_name']
    unverified = entries.filter(status=Entry.UNVERIFIED, user__is_active=True) \
                        .values_list(*user_values).distinct()
    unapproved = entries.filter(status=Entry.VERIFIED) \
                        .values_list(*user_values).distinct()
    return render(request, 'ems/reports/payroll_summary.html', {
        'from_date': from_date,
        'year_month_form': year_month_form,
        'date_headers': date_headers,
        'weekly_totals': weekly_totals,
        'monthly_totals': monthly_totals,
        'unverified': unverified,
        'unapproved': unapproved,
        'labels': labels,
    })
Esempio n. 3
0
def generate_dates(start=None, end=None, by='week'):
    if start:
        start = add_timezone(start)
    if end:
        end = add_timezone(end)
    if by == 'year':
        start = get_year_start(start)
        return rrule.rrule(rrule.YEARLY, dtstart=start, until=end)
    if by == 'month':
        start = get_month_start(start)
        return rrule.rrule(rrule.MONTHLY, dtstart=start, until=end)
    if by == 'week':
        start = get_week_start(start)
        return rrule.rrule(rrule.WEEKLY, dtstart=start, until=end, byweekday=0)
    if by == 'day':
        return rrule.rrule(rrule.DAILY, dtstart=start, until=end)
Esempio n. 4
0
    def get_context_data(self, **kwargs):
        context = super(ProjectTimesheet, self).get_context_data(**kwargs)
        project = self.object
        year_month_form = YearMonthForm(self.request.GET or None)
        if self.request.GET and year_month_form.is_valid():
            from_date, to_date = year_month_form.save()
        else:
            date = utils.add_timezone(datetime.datetime.today())
            from_date = utils.get_month_start(date).date()
            to_date = from_date + relativedelta(months=1)
        entries_qs = Entry.objects
        entries_qs = entries_qs.timespan(from_date,
                                         span='month').filter(project=project)
        extra_values = ('start_time', 'end_time', 'comments', 'seconds_paused',
                        'id', 'location__name', 'project__name',
                        'activity__name', 'status')

        month_entries = entries_qs.date_trunc(
            'month', extra_values).order_by('start_time')
        if month_entries:
            format_totals(month_entries, "hours")

        total = entries_qs.aggregate(hours=Sum('hours'))['hours']
        if total:
            total = "{0:.2f}".format(total)
        user_entries = entries_qs.order_by().values('user__first_name',
                                                    'user__last_name')
        user_entries = user_entries.annotate(sum=Sum('hours')).order_by('-sum')
        if user_entries:
            format_totals(user_entries)
        activity_entries = entries_qs.order_by().values('activity__name')
        activity_entries = activity_entries.annotate(
            sum=Sum('hours')).order_by('-sum')
        if activity_entries:
            format_totals(activity_entries)

        context.update({
            'project': project,
            'year_month_form': year_month_form,
            'from_date': from_date,
            'to_date': to_date - relativedelta(days=1),
            'entries': month_entries,
            'total': total,
            'user_entries': user_entries,
            'activity_entries': activity_entries,
        })
        return context
Esempio n. 5
0
    def clean(self):
        """
        If we're not editing the active entry, ensure that this entry doesn't
        conflict with or come after the active entry.
        """
        # active = utils.get_active_entry(self.user)
        start_time = self.cleaned_data.get('start_time', None)
        # end_time = self.cleaned_data.get('end_time', None)

        # if active and active.pk != self.instance.pk:
        #    if (start_time and start_time > active.start_time) or \
        #            (end_time and end_time > active.start_time):
        #        raise forms.ValidationError(
        #            'The start time or end time conflict with the active '
        #            'entry: {activity} on {project} starting at '
        #            '{start_time}.'.format(
        #                project=active.project,
        #                activity=active.activity,
        #                start_time=active.start_time.strftime('%H:%M:%S'),
        #            ))

        month_start = utils.get_month_start(start_time)
        next_month = month_start + relativedelta(months=1)
        entries = self.instance.user.ems_entries.filter(
            Q(status=Entry.APPROVED) | Q(status=Entry.INVOICED),
            start_time__gte=month_start,
            end_time__lt=next_month)
        entry = self.instance

        if not self.acting_user.is_superuser:
            if (entries.exists() and not entry.id
                    or entry.id and entry.status == Entry.INVOICED):
                message = 'You cannot add/edit entries after a timesheet has been ' \
                    'approved or invoiced. Please correct the start and end times.'
                raise forms.ValidationError(message)

        return self.cleaned_data
Esempio n. 6
0
 def get_previous_month(self):
     """Returns date range for the previous full month."""
     end = utils.get_month_start() - relativedelta(days=1)
     end = utils.to_datetime(end)
     start = utils.get_month_start(end)
     return start, end
Esempio n. 7
0
def view_user_timesheet(request, user_id, active_tab):
    # User can only view their own time sheet unless they have a permission.
    user = get_object_or_404(User, pk=user_id)
    has_perm = request.user.has_perm('entries.view_entry_summary')
    if not (has_perm or user.pk == request.user.pk):
        return HttpResponseForbidden('Forbidden')

    FormClass = UserYearMonthForm if has_perm else YearMonthForm
    form = FormClass(request.GET or None)
    if form.is_valid():
        if has_perm:
            from_date, to_date, form_user = form.save()
            if form_user and request.GET.get('yearmonth', None):
                # Redirect to form_user's time sheet.
                # Do not use request.GET in urlencode to prevent redirect
                # loop caused by yearmonth parameter.
                url = reverse('view_user_timesheet', args=(form_user.pk, ))
                request_data = {
                    'month': from_date.month,
                    'year': from_date.year,
                    'user': form_user.pk,  # Keep so that user appears in form.
                }
                url += '?{0}'.format(urlencode(request_data))
                return HttpResponseRedirect(url)
        else:  # User must be viewing their own time sheet; no redirect needed.
            from_date, to_date = form.save()
        from_date = utils.add_timezone(from_date)
        to_date = utils.add_timezone(to_date)
    else:
        # Default to showing this month.
        from_date = utils.get_month_start()
        to_date = from_date + relativedelta(months=1)

    entries_qs = Entry.objects.filter(user=user)
    month_qs = entries_qs.timespan(from_date, span='month')
    extra_values = ('start_time', 'end_time', 'comments', 'seconds_paused',
                    'id', 'location__name', 'project__name', 'activity__name',
                    'status')
    month_entries = month_qs.date_trunc('month',
                                        extra_values).order_by('start_time')
    # For grouped entries, back date up to the start of the week.
    first_week = utils.get_week_start(from_date)
    month_week = first_week + relativedelta(weeks=1)
    grouped_qs = entries_qs.timespan(first_week, to_date=to_date)
    intersection = grouped_qs.filter(start_time__lt=month_week,
                                     start_time__gte=from_date)
    # If the month of the first week starts in the previous
    # month and we dont have entries in that previous ISO
    # week, then update the first week to start at the first
    # of the actual month
    if not intersection and first_week.month < from_date.month:
        grouped_qs = entries_qs.timespan(from_date, to_date=to_date)
    totals = grouped_totals(grouped_qs) if month_entries else ''
    project_entries = month_qs.order_by().values('project__name').annotate(
        sum=Sum('hours')).order_by('-sum')
    summary = Entry.summary(user, from_date, to_date)

    show_approve = show_verify = False
    can_change = request.user.has_perm('entries.change_entry')
    can_approve = request.user.has_perm('entries.approve_timesheet')
    if can_change or can_approve or user == request.user:
        statuses = list(month_qs.values_list('status', flat=True))
        total_statuses = len(statuses)
        unverified_count = statuses.count(Entry.UNVERIFIED)
        verified_count = statuses.count(Entry.VERIFIED)
        approved_count = statuses.count(Entry.APPROVED)
    if can_change or user == request.user:
        show_verify = unverified_count != 0
    if can_approve:
        show_approve = all([
            verified_count + approved_count == total_statuses,
            verified_count > 0,
            total_statuses != 0,
        ])

    return render(
        request, 'ems/user/timesheet/view.html', {
            'active_tab': active_tab or 'overview',
            'year_month_form': form,
            'from_date': from_date,
            'to_date': to_date - relativedelta(days=1),
            'show_verify': show_verify,
            'show_approve': show_approve,
            'timesheet_user': user,
            'entries': month_entries,
            'grouped_totals': totals,
            'project_entries': project_entries,
            'summary': summary,
        })