Пример #1
0
 def _send_mail(self, subject, ctx):
     # Don't go to the work unless we have a place to send it
     emails = utils.get_setting('EMS_ACCOUNTING_EMAILS')
     if not emails:
         return
     from_email = utils.get_setting('DEFAULT_FROM_EMAIL')
     msg = render_to_string('ems/contract/hours_email.txt', ctx)
     send_mail(subject=subject,
               message=msg,
               from_email=from_email,
               recipient_list=emails)
Пример #2
0
def quick_clock_in(request):
    user = request.user
    work_projects = []
    leave_projects = []

    if user.is_authenticated() and user.is_active:
        # Display all active paid leave projects that the user is assigned to.
        leave_ids = utils.get_setting('EMS_PAID_LEAVE_PROJECTS').values()
        #TODO: Fix Project User relationship
        lq = Q(user=user) & Q(id__in=leave_ids)
        lq = Q(id__in=leave_ids)
        leave_projects = Project.trackable.filter(lq).order_by('name')

        # Get all projects this user has clocked in to.
        entries = Entry.objects.filter(user=user)
        project_ids = list(entries.values_list('project', flat=True))

        # Narrow to projects which can still be clocked in to.
        pq = Q(id__in=project_ids)
        valid_projects = Project.trackable.filter(pq).exclude(id__in=leave_ids)
        valid_ids = list(valid_projects.values_list('id', flat=True))

        # Display the 10 projects this user most recently clocked into.
        work_ids = []
        for i in project_ids:
            if len(work_ids) > 10:
                break
            if i in valid_ids and i not in work_ids:
                work_ids.append(i)
        work_projects = [valid_projects.get(pk=i) for i in work_ids]

    return {
        'leave_projects': leave_projects,
        'work_projects': work_projects,
    }
Пример #3
0
    def __init__(self, *args, **kwargs):
        self.user = kwargs.pop('user')
        self.active = kwargs.pop('active', None)

        initial = kwargs.get('initial', {})
        default_loc = utils.get_setting('EMS_DEFAULT_LOCATION_SLUG')
        if default_loc:
            try:
                loc = Location.objects.get(slug=default_loc)
            except Location.DoesNotExist:
                loc = None
            if loc:
                initial['location'] = loc.pk
        project = initial.get('project', None)
        try:
            last_project_entry = Entry.objects.filter(
                user=self.user, project=project).order_by('-end_time')[0]
        except IndexError:
            initial['activity'] = None
        else:
            initial['activity'] = last_project_entry.activity.pk

        super(ClockInForm, self).__init__(*args, **kwargs)

        self.fields['start_time'].initial = datetime.datetime.now()
        self.fields['project'].queryset = Project.trackable.filter(
            team__members__user=self.user)
        if not self.active:
            self.fields.pop('active_comment')
        else:
            self.fields['active_comment'].initial = self.active.comments
        self.instance.user = self.user
Пример #4
0
 def delete(self, *args, **kwargs):
     # Note: this gets called when you delete a single item using the red
     # Delete button at the bottom while editing it in the admin - but not
     # when you delete one or more from the change list using the admin
     # action.
     super(ContractHour, self).delete(*args, **kwargs)
     # If we have an email address to send to, and this record was in
     # pending status, we'll send an email about the change.
     if ContractHour.PENDING_STATUS in (self.status,
                                        self._original['status']):
         domain = Site.objects.get_current().domain
         method = 'https' if utils.get_setting('EMS_EMAILS_USE_HTTPS')\
             else 'http'
         url = self.contract.get_absolute_url()
         ctx = {
             'deleted': True,
             'new': False,
             'changed': False,
             'previous': self._original,
             'link': '%s://%s%s' % (method, domain, url)
         }
         contract = self._original['contract']
         name = self._meta.verbose_name
         subject = "Deleted pending %s for %s" % (name, contract)
         self._send_mail(subject, ctx)
Пример #5
0
    def save(self, *args, **kwargs):
        # Let the date_approved default to today if it's been set approved
        # and doesn't have one
        if self.status == self.APPROVED_STATUS and not self.date_approved:
            self.date_approved = datetime.date.today()

        # If we have an email address to send to, and this record was
        # or is in pending status, we'll send an email about the change.
        if ContractHour.PENDING_STATUS in (self.status,
                                           self._original['status']):
            is_new = self.pk is None
        super(ContractHour, self).save(*args, **kwargs)
        if ContractHour.PENDING_STATUS in (self.status,
                                           self._original['status']):
            domain = Site.objects.get_current().domain
            method = 'https' if utils.get_setting('EMS_EMAILS_USE_HTTPS')\
                else 'http'
            url = self.contract.get_absolute_url()
            ctx = {
                'new': is_new,
                'changed': not is_new,
                'deleted': False,
                'current': self,
                'previous': self._original,
                'link': '%s://%s%s' % (method, domain, url)
            }
            prefix = "New" if is_new else "Changed"
            name = self._meta.verbose_name
            subject = "%s pending %s for %s" % (prefix, name, self.contract)
            self._send_mail(subject, ctx)
Пример #6
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,
    })
Пример #7
0
    def get_entry_query(self, start, end, data):
        """Builds Entry query from form data."""
        # Entry types.
        incl_billable = data.get('billable', True)
        incl_nonbillable = data.get('non_billable', True)
        incl_leave = data.get('paid_leave', True)

        # If no types are selected, shortcut & return nothing.
        if not any((incl_billable, incl_nonbillable, incl_leave)):
            return None

        # All entries must meet time period requirements.
        basicQ = Q(end_time__gte=start, end_time__lt=end)

        # Filter by project for HourlyReport.
        projects = data.get('projects', None)
        basicQ &= Q(project__in=projects) if projects else Q()

        # Filter by user, activity, and project type for BillableReport.
        if 'users' in data:
            basicQ &= Q(user__in=data.get('users'))
        if 'activities' in data:
            basicQ &= Q(activity__in=data.get('activities'))
        if 'project_types' in data:
            basicQ &= Q(project__type__in=data.get('project_types'))

        # If all types are selected, no further filtering is required.
        if all((incl_billable, incl_nonbillable, incl_leave)):
            return basicQ

        # Filter by whether a project is billable or non-billable.
        billableQ = None
        if incl_billable and not incl_nonbillable:
            billableQ = Q(activity__billable=True, project__type__billable=True)
        if incl_nonbillable and not incl_billable:
            billableQ = Q(activity__billable=False) | Q(project__type__billable=False)

        # Filter by whether the entry is paid leave.
        leave_ids = utils.get_setting('EMS_PAID_LEAVE_PROJECTS').values()
        leaveQ = Q(project__in=leave_ids)
        if incl_leave:
            extraQ = (leaveQ | billableQ) if billableQ else leaveQ
        else:
            extraQ = (~leaveQ & billableQ) if billableQ else ~leaveQ

        return basicQ & extraQ
Пример #8
0
 def summary(user, date, end_date):
     """
     Returns a summary of hours worked in the given time frame, for this
     user.  The setting EMS_PAID_LEAVE_PROJECTS can be used to
     separate out hours for paid leave that should not be included in the
     total worked (e.g., sick time, vacation time, etc.).  Those hours will
     be added to the summary separately using the dictionary key set in
     EMS_PAID_LEAVE_PROJECTS.
     """
     projects = utils.get_setting('EMS_PAID_LEAVE_PROJECTS')
     entries = user.ems_entries.filter(
         end_time__gt=date, end_time__lt=end_date)
     data = {
         'billable': Decimal('0'), 'non_billable': Decimal('0'),
         'invoiced': Decimal('0'), 'uninvoiced': Decimal('0'),
         'total': Decimal('0')
         }
     invoiced = entries.filter(
         status=Entry.INVOICED).aggregate(i=Sum('hours'))['i']
     uninvoiced = entries.exclude(
         status=Entry.INVOICED).aggregate(uninv=Sum('hours'))['uninv']
     total = entries.aggregate(s=Sum('hours'))['s']
     if invoiced:
         data['invoiced'] = invoiced
     if uninvoiced:
         data['uninvoiced'] = uninvoiced
     if total:
         data['total'] = total
     billable = entries.exclude(project__in=projects.values())
     billable = billable.values(
         'billable',
     ).annotate(s=Sum('hours'))
     for row in billable:
         if row['billable']:
             data['billable'] += row['s']
         else:
             data['non_billable'] += row['s']
     data['total_worked'] = data['billable'] + data['non_billable']
     data['paid_leave'] = {}
     for name, pk in projects.items():
         qs = entries.filter(project=projects[name])
         data['paid_leave'][name] = qs.aggregate(s=Sum('hours'))['s']
     return data
Пример #9
0
 def get_queryset(self):
     qs = EntryQuerySet(self.model)
     projects = utils.get_setting('EMS_PAID_LEAVE_PROJECTS')
     return qs.exclude(project__in=projects.values())