def test_invoice_confirm_rounding(self): """Verify that the per activity totals are rounded correctly.""" ACTIVITY_NAME = 'activity_n' # Make a few extra entries to test per activity totals start = utils.add_timezone(datetime.datetime(2011, 1, 1, 8, 0, 0)) end = utils.add_timezone(datetime.datetime(2011, 1, 1, 8, 3, 1)) # 3m1s = 0.05166667h # 0.05166667 rounds to 0.05, at two decimal places # 0.05 * 5 = 0.25 # 0.05166667 * 5 = 0.25833335 # 0.25833335 rounds to 0.26, at two decimal places # If rounding happens before summation, then the total will be 0.25 # If rounding happens after summation, then the total will be 0.26 activity = factories.Activity(billable=True, name=ACTIVITY_NAME) for num in range(0, 5): factories.Entry( user=self.user, project=self.project_billable, start_time=start - relativedelta(days=num), end_time=end - relativedelta(days=num), status=Entry.APPROVED, activity=activity) self.make_hourgroups() to_date = datetime.datetime(2011, 1, 31) kwargs = { 'project': self.project_billable.id, 'to_date': to_date.strftime(DATE_FORM_FORMAT), } url = self.get_create_url(**kwargs) response = self.client.get(url) self.assertEqual(response.status_code, 200) for name, hours_activities in response.context['billable_totals']: total, activities = hours_activities if name == ACTIVITY_NAME: self.assertEqual(total, 0.25) self.assertNotAlmostEqual(float(total), 0.26, places=2)
def test_make_invoice_with_from_uninvoiced(self): from_date = utils.add_timezone(datetime.datetime(2011, 1, 1)) to_date = utils.add_timezone(datetime.datetime(2011, 1, 31)) args = [ self.project_billable.id, to_date.strftime('%Y-%m-%d'), from_date.strftime('%Y-%m-%d') ] url = reverse('confirm_invoice_project', args=args) response = self.client.post(url, { 'number': '5', 'status': 'not-invoiced' }) self.assertEqual(response.status_code, 302) # Verify an invoice was created with the correct attributes invoice = timepiece.EntryGroup.objects.get(number=5) self.assertEqual(invoice.project.id, self.project_billable.id) self.assertEqual(invoice.start.strftime('%Y %m %d'), '2011 01 01') self.assertEqual(invoice.end.strftime('%Y %m %d'), '2011 01 31') self.assertEqual(len(invoice.entries.all()), 1) # Verify that the entries were invoiced appropriately # and the unrelated entries were untouched entries = timepiece.Entry.objects.all() uninvoiced = entries.filter(status='uninvoiced') for entry in uninvoiced: self.assertEqual(entry.entry_group_id, invoice.id)
def setUp(self): super(InvoiceCreateTestCase, self).setUp() self.user = factories.Superuser() self.login_user(self.user) start = utils.add_timezone(datetime.datetime(2011, 1, 1, 8)) end = utils.add_timezone(datetime.datetime(2011, 1, 1, 12)) self.project_billable = factories.BillableProject() self.project_billable2 = factories.BillableProject() self.project_non_billable = factories.NonbillableProject() self.entry1 = factories.Entry( user=self.user, project=self.project_billable, activity=factories.Activity(billable=True), start_time=start, end_time=end, status=Entry.APPROVED) self.entry2 = factories.Entry( user=self.user, project=self.project_billable, activity=factories.Activity(billable=True), start_time=start - relativedelta(days=5), end_time=end - relativedelta(days=5), status=Entry.APPROVED) self.entry3 = factories.Entry( user=self.user, project=self.project_billable2, activity=factories.Activity(billable=False), start_time=start - relativedelta(days=10), end_time=end - relativedelta(days=10), status=Entry.APPROVED) self.entry4 = factories.Entry( user=self.user, project=self.project_non_billable, start_time=start + relativedelta(hours=11), end_time=end + relativedelta(hours=15), status=Entry.APPROVED)
def bulk_entries(self, start=datetime.datetime(2011, 1, 2), end=datetime.datetime(2011, 1, 4)): start = utils.add_timezone(start) end = utils.add_timezone(end) dates = utils.generate_dates(start, end, "day") projects = [self.p1, self.p2, self.p2, self.p4, self.p5, self.sick] self.make_entries(projects=projects, dates=dates, user=self.user, hours=2) self.make_entries(projects=projects, dates=dates, user=self.user2, hours=1)
def test_approved(self): start = utils.add_timezone(datetime.datetime(2011, 1, 1, 8)) end = utils.add_timezone(datetime.datetime(2011, 1, 1, 12)) unapproved_entry_a = factories.Entry(user=self.user, project=self.project_non_billable, start_time=start + relativedelta(hours=11), end_time=end + relativedelta(hours=15), status=Entry.VERIFIED ) unapproved_entry_b = factories.Entry(user=self.user, project=self.project_non_billable, start_time=start + relativedelta(hours=11), end_time=end + relativedelta(hours=15), status=Entry.VERIFIED ) response = self._get() self.assertEquals(response.status_code, 200) form = response.context['form'] self.assertTrue(form.is_valid(), form.errors) unverified = set(response.context['unverified']) unapproved = set(response.context['unapproved']) user_a, user_b = unapproved_entry_a.user, unapproved_entry_b.user expected_unapproved = set([ (user_a.pk, user_a.first_name, user_a.last_name), (user_b.pk, user_b.first_name, user_b.last_name), ]) self.assertEquals(unverified, set()) self.assertEquals(unapproved, expected_unapproved)
def testWeeklyOvertimes(self): """Date_trunc on week should result in correct overtime totals""" dates = self.dates for day_num in xrange(28, 31): dates.append(utils.add_timezone( datetime.datetime(2011, 4, day_num) )) for day_num in xrange(5, 9): dates.append(utils.add_timezone( datetime.datetime(2011, 5, day_num) )) for day in dates: self.make_logs(day) def check_overtime(week0=Decimal('55.00'), week1=Decimal('55.00'), overtime=Decimal('30.00')): self.client.login(username='******', password='******') response = self.client.get(self.url, self.args) weekly_totals = response.context['weekly_totals'][0][0][0][2] self.assertEqual(weekly_totals[0], week0) self.assertEqual(weekly_totals[1], week1) self.assertEqual(weekly_totals[5], overtime) check_overtime() #Entry on following Monday doesn't add to week1 or overtime self.make_logs(utils.add_timezone(datetime.datetime(2011, 5, 9))) check_overtime() #Entries in previous month before last_billable do not change overtime self.make_logs(utils.add_timezone(datetime.datetime(2011, 4, 24))) check_overtime() #Entry in previous month after last_billable change week0 and overtime self.make_logs(utils.add_timezone( datetime.datetime(2011, 4, 25, 1, 0) )) check_overtime(Decimal('66.00'), Decimal('55.00'), Decimal('41.00'))
def test_make_invoice_with_from_uninvoiced(self): from_date = utils.add_timezone(datetime.datetime(2011, 1, 1)) to_date = utils.add_timezone(datetime.datetime(2011, 1, 31)) kwargs = { 'project': self.project_billable.id, 'to_date': to_date.strftime(DATE_FORM_FORMAT), 'from_date': from_date.strftime(DATE_FORM_FORMAT), } url = self.get_create_url(**kwargs) response = self.client.post(url, { 'number': '5', 'status': EntryGroup.NOT_INVOICED }) self.assertEqual(response.status_code, 302) # Verify an invoice was created with the correct attributes invoice = EntryGroup.objects.get(number=5) self.assertEqual(invoice.project.id, self.project_billable.id) self.assertEqual(invoice.start.strftime('%Y %m %d'), '2011 01 01') self.assertEqual(invoice.end.strftime('%Y %m %d'), '2011 01 31') self.assertEqual(len(invoice.entries.all()), 1) # Verify that the entries were invoiced appropriately # and the unrelated entries were untouched entries = Entry.objects.all() uninvoiced = entries.filter(status=Entry.NOT_INVOICED) for entry in uninvoiced: self.assertEqual(entry.entry_group_id, invoice.id)
def test_approved(self): start = utils.add_timezone(datetime.datetime(2011, 1, 1, 8)) end = utils.add_timezone(datetime.datetime(2011, 1, 1, 12)) unapproved_entry_a = factories.Entry( user=self.user, project=self.project_non_billable, start_time=start + relativedelta(hours=11), end_time=end + relativedelta(hours=15), status=Entry.VERIFIED) unapproved_entry_b = factories.Entry( user=self.user, project=self.project_non_billable, start_time=start + relativedelta(hours=11), end_time=end + relativedelta(hours=15), status=Entry.VERIFIED) response = self._get() self.assertEquals(response.status_code, 200) form = response.context['form'] self.assertTrue(form.is_valid(), form.errors) unverified = set(response.context['unverified']) unapproved = set(response.context['unapproved']) user_a, user_b = unapproved_entry_a.user, unapproved_entry_b.user expected_unapproved = set([ (user_a.pk, user_a.first_name, user_a.last_name), (user_b.pk, user_b.first_name, user_b.last_name), ]) self.assertEquals(unverified, set()) self.assertEquals(unapproved, expected_unapproved)
def testWeeklyOvertimes(self): """Date_trunc on week should result in correct overtime totals""" dates = self.dates for day_num in xrange(28, 31): dates.append( utils.add_timezone(datetime.datetime(2011, 4, day_num))) for day_num in xrange(5, 9): dates.append( utils.add_timezone(datetime.datetime(2011, 5, day_num))) for day in dates: self.make_logs(day) def check_overtime(week0=Decimal('55.00'), week1=Decimal('55.00'), overtime=Decimal('30.00')): self.client.login(username='******', password='******') response = self.client.get(self.url, self.args) weekly_totals = response.context['weekly_totals'][0][0][0][2] self.assertEqual(weekly_totals[0], week0) self.assertEqual(weekly_totals[1], week1) self.assertEqual(weekly_totals[5], overtime) check_overtime() #Entry on following Monday doesn't add to week1 or overtime self.make_logs(utils.add_timezone(datetime.datetime(2011, 5, 9))) check_overtime() #Entries in previous month before last_billable do not change overtime self.make_logs(utils.add_timezone(datetime.datetime(2011, 4, 24))) check_overtime() #Entry in previous month after last_billable change week0 and overtime self.make_logs(utils.add_timezone(datetime.datetime(2011, 4, 25, 1, 0))) check_overtime(Decimal('66.00'), Decimal('55.00'), Decimal('41.00'))
def testGenerateDays(self): dates = [ utils.add_timezone(datetime.datetime(2011, 1, day)) for day in xrange(1, 32) ] start = utils.add_timezone(datetime.datetime(2011, 1, 1)) end = utils.add_timezone(datetime.datetime(2011, 1, 31)) self.check_generate_dates(start, end, 'day', dates)
def testWeeklyTotal(self): start = utils.add_timezone(datetime.datetime(2011, 1, 3)) end = utils.add_timezone(datetime.datetime(2011, 1, 6)) self.bulk_entries(start, end) trunc = 'week' date_headers = utils.generate_dates(start, end, trunc) pj_totals = self.get_project_totals(date_headers, trunc) self.assertEqual(pj_totals[0][0], [48]) self.assertEqual(pj_totals[0][1], [24]) self.assertEqual(pj_totals[1], [72])
def argsHelper(self, args={}, start=datetime.datetime(2011, 1, 2), end=datetime.datetime(2011, 1, 4)): start = utils.add_timezone(start) end = utils.add_timezone(end) args.update({ 'from_date': start.strftime('%m/%d/%Y'), 'to_date': end.strftime('%m/%d/%Y'), 'export': True, }) return args
def testDailyTotal(self): start = utils.add_timezone(datetime.datetime(2011, 1, 1)) day2 = utils.add_timezone(datetime.datetime(2011, 1, 2)) end = utils.add_timezone(datetime.datetime(2011, 1, 3)) self.log_daily(start, day2, end) trunc = "day" date_headers = utils.generate_dates(start, end, trunc) pj_totals = self.get_project_totals(date_headers, trunc) self.assertEqual(pj_totals[0][0], [Decimal("1.00"), Decimal("1.50"), ""]) self.assertEqual(pj_totals[0][1], ["", Decimal("3.00"), Decimal("2.00")]) self.assertEqual(pj_totals[1], [Decimal("1.00"), Decimal("4.50"), Decimal("2.00")])
def setUp(self): super(InvoiceCreateTestCase, self).setUp() self.user.is_superuser = True self.user.save() self.client.login(username=self.user.username, password='******') start = utils.add_timezone(datetime.datetime(2011, 1, 1, 8)) end = utils.add_timezone(datetime.datetime(2011, 1, 1, 12)) self.project_billable = self.create_project(billable=True) self.project_billable2 = self.create_project(billable=True) self.project_non_billable = self.create_project(billable=False) self.entry1 = self.create_entry({ 'user': self.user, 'project': self.project_billable, 'start_time': start, 'end_time': end, 'status': 'approved', }) self.entry2 = self.create_entry({ 'user': self.user, 'project': self.project_billable, 'start_time': start - datetime.timedelta(days=5), 'end_time': end - datetime.timedelta(days=5), 'status': 'approved', }) self.entry3 = self.create_entry({ 'user': self.user, 'project': self.project_billable2, 'start_time': start - datetime.timedelta(days=10), 'end_time': end - datetime.timedelta(days=10), 'status': 'approved', }) self.entry4 = self.create_entry({ 'user': self.user, 'project': self.project_non_billable, 'start_time': start + datetime.timedelta(hours=11), 'end_time': end + datetime.timedelta(hours=15), 'status': 'approved', })
def testGenerateWeeks(self): dates = [ utils.add_timezone(datetime.datetime(2010, 12, 27)), utils.add_timezone(datetime.datetime(2011, 01, 03)), utils.add_timezone(datetime.datetime(2011, 01, 10)), utils.add_timezone(datetime.datetime(2011, 01, 17)), utils.add_timezone(datetime.datetime(2011, 01, 24)), utils.add_timezone(datetime.datetime(2011, 01, 31)), ] start = utils.add_timezone(datetime.datetime(2011, 1, 1)) end = utils.add_timezone(datetime.datetime(2011, 2, 1)) self.check_generate_dates(start, end, 'week', dates)
def args_helper(self, **kwargs): start = utils.add_timezone(kwargs.pop('start', datetime.datetime(2011, 1, 2))) end = utils.add_timezone(kwargs.pop('end', datetime.datetime(2011, 1, 4))) defaults = { 'from_date': start.strftime('%Y-%m-%d'), 'to_date': end.strftime('%Y-%m-%d'), 'export_users': True, 'billable': True, 'non_billable': True, 'paid_leave': True, 'trunc': 'week', } defaults.update(kwargs) return defaults
def testDailyTotal(self): start = utils.add_timezone(datetime.datetime(2011, 1, 1)) day2 = utils.add_timezone(datetime.datetime(2011, 1, 2)) end = utils.add_timezone(datetime.datetime(2011, 1, 3)) self.log_daily(start, day2, end) trunc = 'day' date_headers = utils.generate_dates(start, end, trunc) pj_totals = self.get_project_totals(date_headers, trunc) self.assertEqual(pj_totals[0][0], [Decimal('1.00'), Decimal('1.50'), '']) self.assertEqual(pj_totals[0][1], ['', Decimal('3.00'), Decimal('2.00')]) self.assertEqual(pj_totals[1], [Decimal('1.00'), Decimal('4.50'), Decimal('2.00')])
def test_invoice_confirm_totals(self): """Verify that the per activity totals are valid.""" # Make a few extra entries to test per activity totals start = utils.add_timezone(datetime.datetime(2011, 1, 1, 8)) end = utils.add_timezone(datetime.datetime(2011, 1, 1, 12)) # start = utils.add_timezone(datetime.datetime.now()) # end = start + relativedelta(hours=4) activity = self.create_activity(data={ 'name': 'activity1', 'billable': True }) for num in xrange(0, 4): new_entry = self.create_entry({ 'user': self.user, 'project': self.project_billable, 'start_time': start - relativedelta(days=num), 'end_time': end - relativedelta(days=num), 'status': Entry.APPROVED, 'activity': activity, }) self.make_hourgroups() to_date = datetime.datetime(2011, 1, 31) kwargs = { 'project': self.project_billable.id, 'to_date': to_date.strftime(DATE_FORM_FORMAT), } url = self.get_create_url(**kwargs) response = self.client.get(url) self.assertEqual(response.status_code, 200) for name, hours_activities in response.context['billable_totals']: total, activities = hours_activities if name == 'activity1': self.assertEqual(total, 16) self.assertEqual(total, activities[0][1]) self.assertEqual(name, activities[0][0]) elif name == 'Total': self.assertEqual(total, 24) self.assertEqual(activities, []) else: # Each other activity is 4 hrs each self.assertEqual(total, 4) self.assertEqual(total, activities[0][1]) self.assertEqual(name, activities[0][0])
def get_context_data(self, **kwargs): context = super(HourlyReport, self).get_context_data(**kwargs) # Sum the hours totals for each user & interval. entries = context['entries'] date_headers = context['date_headers'] summaries = [] if context['entries']: summaries.append( ('By User', get_project_totals(entries.order_by('user__last_name', 'user__id', 'date'), date_headers, 'total', total_column=True, by='user'))) entries = entries.order_by('project__type__label', 'project__name', 'project__id', 'date') func = lambda x: x['project__type__label'] for label, group in groupby(entries, func): title = label + ' Projects' summaries.append(( title, get_project_totals( list(group), date_headers, 'total', total_column=True, by='project', ), )) # Adjust date headers & create range headers. from_date = context['from_date'] from_date = utils.add_timezone(from_date) if from_date else None to_date = context['to_date'] to_date = utils.add_timezone(to_date) if to_date else None trunc = context['trunc'] date_headers, range_headers = self.get_headers(date_headers, from_date, to_date, trunc) context.update({ 'date_headers': date_headers, 'summaries': OrderedDict(summaries), 'range_headers': range_headers, }) return context
def testBillableNonBillable(self): start = utils.add_timezone(datetime.datetime(2011, 1, 1)) day2 = utils.add_timezone(datetime.datetime(2011, 1, 2)) end = utils.add_timezone(datetime.datetime(2011, 1, 3)) self.log_daily(start, day2, end) trunc = "day" billableQ = Q(project__type__billable=True) non_billableQ = Q(project__type__billable=False) date_headers = utils.generate_dates(start, end, trunc) pj_billable = self.get_project_totals(date_headers, trunc, Q(), "billable") pj_billable_q = self.get_project_totals(date_headers, trunc, billableQ, "total") pj_non_billable = self.get_project_totals(date_headers, trunc, Q(), "non_billable") pj_non_billable_q = self.get_project_totals(date_headers, trunc, non_billableQ, "total") self.assertEqual(list(pj_billable), list(pj_billable_q)) self.assertEqual(list(pj_non_billable), list(pj_non_billable_q))
def bulk_entries(self, start=datetime.datetime(2011, 1, 2), end=datetime.datetime(2011, 1, 4)): start = utils.add_timezone(start) end = utils.add_timezone(end) dates = utils.generate_dates(start, end, 'day') projects = [self.p1, self.p2, self.p2, self.p4, self.p5, self.sick] self.make_entries(projects=projects, dates=dates, user=self.user, hours=2) self.make_entries(projects=projects, dates=dates, user=self.user2, hours=1)
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)
def args_helper(self, **kwargs): start = utils.add_timezone( kwargs.pop('start', datetime.datetime(2011, 1, 2))) end = utils.add_timezone( kwargs.pop('end', datetime.datetime(2011, 1, 4))) defaults = { 'from_date': start.strftime('%m/%d/%Y'), 'to_date': end.strftime('%m/%d/%Y'), 'export': True, 'billable': True, 'non_billable': True, 'paid_leave': True, 'trunc': 'week', } defaults.update(kwargs) return defaults
def test_make_invoice(self): to_date = utils.add_timezone(datetime.datetime(2011, 1, 31)) kwargs = { 'project': self.project_billable.id, 'to_date': to_date.strftime(DATE_FORM_FORMAT), } url = self.get_create_url(**kwargs) response = self.client.post(url, { 'number': '3', 'status': EntryGroup.INVOICED, }) self.assertEqual(response.status_code, 302) # Verify an invoice was created with the correct attributes invoice = EntryGroup.objects.get(number=3) self.assertEqual(invoice.project.id, self.project_billable.id) self.assertEqual(invoice.start, None) self.assertEqual(invoice.end.strftime('%Y %m %d'), '2011 01 31') self.assertEqual(len(invoice.entries.all()), 2) # Verify that the entries were invoiced appropriately # and the unrelated entries were untouched entries = Entry.objects.all() invoiced = entries.filter(status=EntryGroup.INVOICED) for entry in invoiced: self.assertEqual(entry.entry_group_id, invoice.id) approved = entries.filter(status=Entry.APPROVED) self.assertEqual(len(approved), 2) self.assertEqual(approved[0].entry_group_id, None)
def grouped_totals(entries): select = { "day": { "date": """DATE_TRUNC('day', end_time)""" }, "week": { "date": """DATE_TRUNC('week', end_time)""" }, } weekly = entries.extra(select=select["week"]).values('date', 'billable') weekly = weekly.annotate(hours=Sum('hours')).order_by('date') daily = entries.extra(select=select["day"]).values('date', 'project__name', 'billable') daily = daily.annotate(hours=Sum('hours')).order_by( 'date', 'project__name') weeks = {} for week, week_entries in groupby(weekly, lambda x: x['date']): if week is not None: week = add_timezone(week) weeks[week] = get_hours_summary(week_entries) days = [] last_week = None for day, day_entries in groupby(daily, lambda x: x['date']): week = get_week_start(day) if last_week and week > last_week: yield last_week, weeks.get(last_week, {}), days days = [] days.append((day, daily_summary(day_entries))) last_week = week yield week, weeks.get(week, {}), days
def grouped_totals(entries): select = { "day": {"date": """DATE_TRUNC('day', end_time)"""}, "week": {"date": """DATE_TRUNC('week', end_time)"""}, } weekly = entries.extra(select=select["week"]).values('date', 'billable') weekly = weekly.annotate(hours=Sum('hours')).order_by('date') daily = entries.extra(select=select["day"]).values('date', 'project__name', 'billable') daily = daily.annotate(hours=Sum('hours')).order_by('date', 'project__name') weeks = {} for week, week_entries in groupby(weekly, lambda x: x['date']): if week is not None: week = add_timezone(week) weeks[week] = get_hours_summary(week_entries) days = [] last_week = None for day, day_entries in groupby(daily, lambda x: x['date']): week = get_week_start(day) if last_week and week > last_week: yield last_week, weeks.get(last_week, {}), days days = [] days.append((day, daily_summary(day_entries))) last_week = week yield week, weeks.get(week, {}), days
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) total = entries_qs.aggregate(hours=Sum('hours'))['hours'] user_entries = entries_qs.order_by().values( 'user__first_name', 'user__last_name').annotate(sum=Sum('hours')).order_by('-sum') activity_entries = entries_qs.order_by().values( 'activity__name').annotate(sum=Sum('hours')).order_by('-sum') 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
def setUp(self): super(ListOutstandingInvoicesViewTestCase, self).setUp() self.user = factories.Superuser() self.login_user(self.user) start = utils.add_timezone(datetime.datetime(2011, 1, 1, 8)) end = utils.add_timezone(datetime.datetime(2011, 1, 1, 12)) self.project_billable = factories.BillableProject() self.project_billable2 = factories.BillableProject() self.project_non_billable = factories.NonbillableProject() self.entry1 = factories.Entry( user=self.user, project=self.project_billable, activity=factories.Activity(billable=True), start_time=start, end_time=end, status=Entry.APPROVED) self.entry2 = factories.Entry( user=self.user, project=self.project_billable, activity=factories.Activity(billable=True), start_time=start - relativedelta(days=5), end_time=end - relativedelta(days=5), status=Entry.APPROVED) self.entry3 = factories.Entry( user=self.user, project=self.project_billable2, activity=factories.Activity(billable=False), start_time=start - relativedelta(days=10), end_time=end - relativedelta(days=10), status=Entry.APPROVED) self.entry4 = factories.Entry(user=self.user, project=self.project_non_billable, start_time=start + relativedelta(hours=11), end_time=end + relativedelta(hours=15), status=Entry.APPROVED) # Default get kwargs. self.to_date = utils.add_timezone( datetime.datetime(2011, 1, 31, 0, 0, 0)) self.get_kwargs = { 'to_date': self.to_date.strftime(DATE_FORM_FORMAT), 'statuses': list(Attribute.statuses.values_list('pk', flat=True)), }
def log_many(self, projects, num_entries=20, start=None, billable=True): start = utils.add_timezone(datetime.datetime(2011, 1, 1, 0, 0, 0)) for index in range(0, num_entries): start += relativedelta(hours=(5 * index)) project = projects[index % len(projects)] # Alternate projects self.log_time(start=start, status=Entry.APPROVED, project=project, billable=billable) return start
def testGenerateMonths(self): dates = [ utils.add_timezone(datetime.datetime(2011, month, 1)) for month in xrange(1, 13) ] start = datetime.date(2011, 1, 1) end = datetime.date(2011, 12, 1) self.check_generate_dates(start, end, 'month', dates)
def setUp(self): super(PayrollTest, self).setUp() self.sick = self.create_project(name='sick') self.vacation = self.create_project(name='vacation') settings.TIMEPIECE_PAID_LEAVE_PROJECTS = { 'sick': self.sick.pk, 'vacation': self.vacation.pk } self.next = utils.add_timezone(datetime.datetime(2011, 6, 1)) self.overtime_before = utils.add_timezone( datetime.datetime(2011, 4, 29)) self.first = utils.add_timezone(datetime.datetime(2011, 5, 1)) self.first_week = utils.add_timezone(datetime.datetime(2011, 5, 2)) self.middle = utils.add_timezone(datetime.datetime(2011, 5, 18)) self.last_billable = utils.add_timezone(datetime.datetime(2011, 5, 28)) self.last = utils.add_timezone(datetime.datetime(2011, 5, 31)) self.dates = [ self.overtime_before, self.first, self.first_week, self.middle, self.last, self.last_billable, self.next ] self.url = reverse('report_payroll_summary') self.args = { 'year': self.first.year, 'month': self.first.month, }
def setUp(self): super(PayrollTest, self).setUp() self.user = factories.User() self.user2 = factories.User() self.superuser = factories.Superuser() self.devl_activity = factories.Activity(billable=True) self.activity = factories.Activity() self.sick = factories.Project(name='sick') self.vacation = factories.Project(name='vacation') settings.TIMEPIECE_PAID_LEAVE_PROJECTS = { 'sick': self.sick.pk, 'vacation': self.vacation.pk } self.next = utils.add_timezone(datetime.datetime(2011, 6, 1)) self.overtime_before = utils.add_timezone( datetime.datetime(2011, 4, 29)) self.first = utils.add_timezone(datetime.datetime(2011, 5, 1)) self.first_week = utils.add_timezone(datetime.datetime(2011, 5, 2)) self.middle = utils.add_timezone(datetime.datetime(2011, 5, 18)) self.last_billable = utils.add_timezone(datetime.datetime(2011, 5, 28)) self.last = utils.add_timezone(datetime.datetime(2011, 5, 31)) self.dates = [ self.overtime_before, self.first, self.first_week, self.middle, self.last, self.last_billable, self.next ] self.url = reverse('report_payroll_summary') self.args = { 'year': self.first.year, 'month': self.first.month, }
def test_invoice_create_with_from(self): # Add another entry and make sure from filters it out url = reverse('invoice_projects') from_date = utils.add_timezone(datetime.datetime(2011, 1, 1, 0, 0, 0)) to_date = utils.add_timezone(datetime.datetime(2011, 1, 31, 0, 0, 0)) params = { 'from_date': from_date.strftime('%m/%d/%Y'), 'to_date': to_date.strftime('%m/%d/%Y'), } response = self.client.get(url, params) # From date filters out one entry num_project_totals = len(response.context['project_totals']) self.assertEquals(num_project_totals, 1) # Verify that the date on the mark as invoiced links will be correct from_date_str = response.context['from_date'].strftime('%Y %m %d') self.assertEquals(from_date_str, '2011 01 01') to_date_str = response.context['to_date'].strftime('%Y %m %d') self.assertEquals(to_date_str, '2011 01 31')
def testDailyTotal(self): start = utils.add_timezone(datetime.datetime(2011, 1, 1)) day2 = utils.add_timezone(datetime.datetime(2011, 1, 2)) end = utils.add_timezone(datetime.datetime(2011, 1, 3)) self.log_daily(start, day2, end) trunc = 'day' date_headers = utils.generate_dates(start, end, trunc) pj_totals = self.get_project_totals(date_headers, trunc) self.assertEqual( pj_totals[0][0], [Decimal('1.00'), Decimal('1.50'), '']) self.assertEqual( pj_totals[0][1], ['', Decimal('3.00'), Decimal('2.00')]) self.assertEqual(pj_totals[1], [Decimal('1.00'), Decimal('4.50'), Decimal('2.00')])
def test_invoice_confirm_view_user(self): """A regular user should not be able to access this page""" self.client.login(username='******', password='******') to_date = utils.add_timezone(datetime.datetime(2011, 1, 31)) url = self.get_create_url(project=self.project_billable.pk, to_date=to_date.strftime(DATE_FORM_FORMAT)) response = self.client.get(url) self.assertEquals(response.status_code, 403)
def testMonthlyTotal(self): start = utils.add_timezone(datetime.datetime(2011, 1, 1)) end = utils.add_timezone(datetime.datetime(2011, 3, 1)) trunc = 'month' last_day = randint(5, 10) worked1 = randint(1, 3) worked2 = randint(1, 3) for month in xrange(1, 7): for day in xrange(1, last_day + 1): day = utils.add_timezone(datetime.datetime(2011, month, day)) self.log_time(start=day, delta=(worked1, 0), user=self.user) self.log_time(start=day, delta=(worked2, 0), user=self.user2) date_headers = utils.generate_dates(start, end, trunc) pj_totals = self.get_project_totals(date_headers, trunc) for hour in pj_totals[0][0]: self.assertEqual(hour, last_day * worked1) for hour in pj_totals[0][1]: self.assertEqual(hour, last_day * worked2)
def get_context_data(self, **kwargs): context = super(HourlyReport, self).get_context_data(**kwargs) # Sum the hours totals for each user & interval. entries = context['entries'] date_headers = context['date_headers'] summaries = [] if context['entries']: summaries.append(('By User', get_project_totals( entries.order_by('user__last_name', 'user__id', 'date'), date_headers, 'total', total_column=True, by='user'))) entries = entries.order_by('project__type__label', 'project__name', 'project__id', 'date') func = lambda x: x['project__type__label'] for label, group in groupby(entries, func): title = label + ' Projects' summaries.append(( title, get_project_totals( list(group), date_headers, 'total', total_column=True, by='project', ), )) # Adjust date headers & create range headers. from_date = context['from_date'] from_date = utils.add_timezone(from_date) if from_date else None to_date = context['to_date'] to_date = utils.add_timezone(to_date) if to_date else None trunc = context['trunc'] date_headers, range_headers = self.get_headers( date_headers, from_date, to_date, trunc) context.update({ 'date_headers': date_headers, 'summaries': OrderedDict(summaries), 'range_headers': range_headers, }) return context
def make_entries(self): days = [ utils.add_timezone(datetime.datetime(2011, 1, 1)), utils.add_timezone(datetime.datetime(2011, 1, 28)), utils.add_timezone(datetime.datetime(2011, 1, 31)), utils.add_timezone(datetime.datetime(2011, 2, 1)), timezone.now(), ] self.log_time(project=self.p1, start=days[0], delta=(1, 0)) self.log_time(project=self.p2, start=days[0], delta=(1, 0)) self.log_time(project=self.p1, start=days[1], delta=(1, 0)) self.log_time(project=self.p3, start=days[1], delta=(1, 0)) self.log_time(project=self.p1, user=self.user2, start=days[2], delta=(1, 0)) self.log_time(project=self.p2, start=days[2], delta=(1, 0)) self.log_time(project=self.p1, start=days[3], delta=(1, 0)) self.log_time(project=self.p3, start=days[3], delta=(1, 0)) self.log_time(project=self.p1, start=days[4], delta=(1, 0)) self.log_time(project=self.p2, start=days[4], delta=(1, 0))
def test_generate_weeks(self): dates = [ utils.add_timezone(datetime.datetime(2010, 12, 27)), utils.add_timezone(datetime.datetime(2011, 1, 3)), utils.add_timezone(datetime.datetime(2011, 1, 10)), utils.add_timezone(datetime.datetime(2011, 1, 17)), utils.add_timezone(datetime.datetime(2011, 1, 24)), utils.add_timezone(datetime.datetime(2011, 1, 31)), ] start = utils.add_timezone(datetime.datetime(2011, 1, 1)) end = utils.add_timezone(datetime.datetime(2011, 2, 1)) self.check_generate_dates(start, end, 'week', dates)
def testBillableNonBillable(self): start = utils.add_timezone(datetime.datetime(2011, 1, 1)) day2 = utils.add_timezone(datetime.datetime(2011, 1, 2)) end = utils.add_timezone(datetime.datetime(2011, 1, 3)) self.log_daily(start, day2, end) trunc = 'day' billableQ = Q(project__type__billable=True) non_billableQ = Q(project__type__billable=False) date_headers = utils.generate_dates(start, end, trunc) pj_billable = self.get_project_totals(date_headers, trunc, Q(), 'billable') pj_billable_q = self.get_project_totals(date_headers, trunc, billableQ, 'total') pj_non_billable = self.get_project_totals(date_headers, trunc, Q(), 'non_billable') pj_non_billable_q = self.get_project_totals(date_headers, trunc, non_billableQ, 'total') self.assertEqual(list(pj_billable), list(pj_billable_q)) self.assertEqual(list(pj_non_billable), list(pj_non_billable_q))
def testLastBillable(self): """Test the get_last_billable_day utility for validity""" months = range(1, 13) first_days = [utils.add_timezone(datetime.datetime(2011, month, 1)) for month in months] last_billable = [utils.get_last_billable_day(day).day \ for day in first_days] #should equal the last saturday of every month in 2011 self.assertEqual(last_billable, [30, 27, 27, 24, 29, 26, 31, 28, 25, 30, 27, 25])
def test_invoice_confirm_totals(self): """Verify that the per activity totals are valid.""" # Make a few extra entries to test per activity totals start = utils.add_timezone(datetime.datetime(2011, 1, 1, 8)) end = utils.add_timezone(datetime.datetime(2011, 1, 1, 12)) # start = utils.add_timezone(datetime.datetime.now()) # end = start + datetime.timedelta(hours=4) activity = self.create_activity(data={'name': 'activity1'}) for num in xrange(0, 4): new_entry = self.create_entry({ 'user': self.user, 'project': self.project_billable, 'start_time': start - datetime.timedelta(days=num), 'end_time': end - datetime.timedelta(days=num), 'status': 'approved', 'activity': activity, }) self.make_hourgroups() to_date = datetime.datetime(2011, 1, 31) args = [self.project_billable.id, to_date.strftime('%Y-%m-%d')] url = reverse('confirm_invoice_project', args=args) response = self.client.get(url) self.assertEqual(response.status_code, 200) for name, hours_activities in response.context['totals']: total, activities = hours_activities if name == 'activity1': self.assertEqual(total, 16) self.assertEqual(total, activities[0][1]) self.assertEqual(name, activities[0][0]) elif name == 'Total': self.assertEqual(total, 24) self.assertEqual(activities, []) else: self.assertEqual(total, 4) self.assertEqual(total, activities[0][1]) self.assertEqual(name, activities[0][0])
def test_unverified(self): start = utils.add_timezone(datetime.datetime(2011, 1, 1, 8)) end = utils.add_timezone(datetime.datetime(2011, 1, 1, 12)) factories.Entry( user=self.user, project=self.project_non_billable, start_time=start + relativedelta(hours=11), end_time=end + relativedelta(hours=15), status=Entry.UNVERIFIED ) # unverified response = self._get() self.assertEquals(response.status_code, 200) form = response.context['form'] self.assertTrue(form.is_valid(), form.errors) unverified = list(response.context['unverified']) unapproved = list(response.context['unapproved']) expected_unverified = [ (self.user.pk, self.user.first_name, self.user.last_name) ] self.assertEquals(unverified, expected_unverified) self.assertEquals(unapproved, [])
def test_invoice_confirm_view_user(self): """A regular user should not be able to access this page""" self.client.login(username='******', password='******') to_date = utils.add_timezone(datetime.datetime(2011, 1, 31)) url = reverse('confirm_invoice_project', args=( self.project_billable.pk, to_date.strftime('%Y-%m-%d'), )) response = self.client.get(url) self.assertEquals(response.status_code, 403)
def create_invoice(self, project=None, status='invoiced'): if not project: project = self.project to_date = utils.add_timezone(datetime.datetime(2011, 1, 31)) args = [project.id, to_date.strftime('%Y-%m-%d')] url = reverse('confirm_invoice_project', args=args) params = { 'number': str(random.randint(999, 9999)), 'status': status, } response = self.client.post(url, params)
def test_month_start(self): """ Test that any day returns the first day of the month""" days = [ datetime.date(2011, 1, 1), datetime.date(2011, 1, 16), datetime.date(2011, 1, 17), datetime.date(2011, 1, 22), datetime.date(2011, 1, 31), ] date = utils.add_timezone(datetime.datetime(2011, 1, 1)) for day in days: self.assertEqual(utils.get_month_start(day), date)
def test_invoice_confirm_view_permission(self): """ If you have the correct permission, you should be able to create an invoice """ self.login_with_permission() to_date = utils.add_timezone(datetime.datetime(2011, 1, 31)) url = self.get_create_url(project=self.project_billable.pk, to_date=to_date.strftime(DATE_FORM_FORMAT)) response = self.client.get(url) self.assertEquals(response.status_code, 200)
def create_invoice(self, project=None, data=None): data = data or {} if not project: project = self.project to_date = utils.add_timezone(datetime.datetime(2011, 1, 31)) url = self.get_create_url(project=project.id, to_date=to_date.strftime('%Y-%m-%d')) params = { 'number': str(random.randint(999, 9999)), 'status': EntryGroup.INVOICED, } params.update(data) self.client.post(url, params)
def test_from_date(self): from_date = utils.add_timezone(datetime.datetime(2011, 1, 1, 0, 0, 0)) self.get_kwargs['from_date'] = from_date.strftime(DATE_FORM_FORMAT) response = self._get() self.assertEquals(response.status_code, 200) form = response.context['form'] self.assertTrue(form.is_valid(), form.errors) # From date filters out one entry self.assertEquals(response.context['project_totals'].count(), 1) # Verify that the date on the mark as invoiced links will be correct self.assertEquals(response.context['to_date'], self.to_date.date()) self.assertEquals(response.context['from_date'], from_date.date())