def test_report_charge_non_charge(): to_date = timezone.now() from_date = to_date + relativedelta(months=-1) d = from_date + relativedelta(days=7) TimeRecordFactory( billable=True, date_started=d, start_time=time(11, 0), end_time=time(11, 30), ) TimeRecordFactory( billable=False, date_started=d, start_time=time(10, 0), end_time=time(10, 15), ) # do not include this record - no end time TimeRecordFactory( billable=False, date_started=d, start_time=time(11, 0), end_time=None, ) # do not include this record - outside of time TimeRecordFactory( billable=False, date_started=timezone.now() + relativedelta(months=+1), start_time=time(11, 0), end_time=time(11, 30), ) data = TimeRecord.objects.report_charge_non_charge(from_date, to_date) assert {'Chargeable': 30, 'Non-Chargeable': 15} == data
def test_remove_time_lines_not_extra(): """Remove all but one line. The extra line is not removed because it isn't linked to a time record. .. important:: This test will fail if you try to run it around midnight. The end time will be something like ``time(0, 45)`` and the start time will be something like ``time(23, 45)``. Both are for the same date, and the end date will look as if it is before the start date! """ InvoiceSettingsFactory() VatSettingsFactory() tr = TimeRecordFactory() InvoiceContactFactory(contact=tr.ticket.contact) TimeRecordFactory(ticket=tr.ticket) invoice = InvoiceCreate().create( tr.user, tr.ticket.contact, date.today() ) extra_line = InvoiceLineFactory(invoice=invoice) assert invoice.is_draft is True InvoicePrint().create_pdf(invoice, None) assert invoice.has_lines is True invoice.set_to_draft() invoice.remove_time_lines() assert [extra_line.pk] == [i.pk for i in invoice.invoiceline_set.all()]
def test_start_and_stop_too_many(): user = UserFactory() TimeRecordFactory(user=user, end_time=None) TimeRecordFactory(user=user, end_time=None) quick = QuickTimeRecordFactory(user=user) ticket = TicketFactory() with pytest.raises(InvoiceError) as e: TimeRecord.objects.start(ticket, quick) assert 'Cannot start a time record when 2 are already' in str(e.value)
def test_running_today(): user = UserFactory() TimeRecordFactory(title='t1', user=user, end_time=None) TimeRecordFactory(title='t2', user=user, end_time=time(11, 0)) d = timezone.now().date() + relativedelta(days=7) TimeRecordFactory(title='t3', date_started=d, user=user, end_time=None) TimeRecordFactory(title='t4', user=UserFactory(), end_time=None) qs = TimeRecord.objects.running_today(user).order_by('title') assert ['t1'] == [obj.title for obj in qs]
def test_report_time_by_ticket(): user = UserFactory(username='******') d = timezone.now().date() t1 = TicketFactory(pk=1, contact=ContactFactory()) TimeRecordFactory( ticket=t1, date_started=d, start_time=time(11, 0), end_time=time(11, 30), user=user, ) TimeRecordFactory( ticket=TicketFactory(pk=2, contact=ContactFactory()), date_started=d, start_time=time(10, 0), end_time=time(10, 15), user=user, ) # another date (so not included) TimeRecordFactory( ticket=t1, date_started=d + relativedelta(days=-1), start_time=time(12, 0), end_time=time(12, 10), user=UserFactory(), ) # another user (so not included) TimeRecordFactory( ticket=t1, date_started=d, start_time=time(12, 0), end_time=time(12, 10), user=UserFactory(), ) TimeRecordFactory( ticket=t1, date_started=d, start_time=time(12, 0), end_time=time(12, 10), user=user, ) data = TimeRecord.objects.report_time_by_ticket(user, d) assert { 1: { 'Chargeable': 40.0, 'Fixed-Price': 0, 'Non-Chargeable': 0 }, 2: { 'Chargeable': 15.0, 'Fixed-Price': 0, 'Non-Chargeable': 0 }, } == data
def test_timerecord_summary_stop_time(client): user = UserFactory(username='******', is_staff=True) assert client.login(username=user.username, password=TEST_PASSWORD) is True time_record = TimeRecordFactory(end_time=None) url = reverse('invoice.time.summary') data = { 'pk': time_record.pk, } assert time_record.end_time is None response = client.post(url, data) assert 302 == response.status_code assert url == response['Location'] time_record.refresh_from_db() assert time_record.end_time is not None
def test_invoice_with_time_records_no_end_time(): """One of the time records has no end time, so cannot be invoiced.""" InvoiceSettingsFactory() VatSettingsFactory() contact = ContactFactory() InvoiceContactFactory(contact=contact) ticket = TicketFactory(contact=contact) tr1 = TimeRecordFactory(ticket=ticket) TimeRecordFactory(ticket=ticket, end_time=None) TimeRecordFactory(ticket=ticket) with pytest.raises(InvoiceError) as ex: InvoiceCreate().create(tr1.user, contact, date.today()) message = str(ex.value) assert 'does not have a' in message assert 'end time' in message
def test_start_and_stop(): user = UserFactory() running = TimeRecordFactory(user=user, end_time=None) assert running.end_time is None quick = QuickTimeRecordFactory(user=user) ticket = TicketFactory() time_record = TimeRecord.objects.start(ticket, quick) assert quick.time_code == time_record.time_code assert quick.description == time_record.title assert time_record.billable is False assert time_record.end_time is None assert time_record.start_time is not None assert user == time_record.user running.refresh_from_db() assert running.end_time == time_record.start_time
def test_create_invoices_only_billable_time(): InvoiceSettingsFactory() VatSettingsFactory() contact = ContactFactory() InvoiceContactFactory(contact=contact) ticket = TicketFactory(contact=contact) TimeRecordFactory(ticket=ticket, date_started=date(2012, 7, 1)) InvoiceCreateBatch().create(UserFactory(), date(2012, 9, 30)) TimeRecordFactory( ticket=ticket, date_started=date(2012, 7, 1), billable=False, ) InvoiceCreateBatch().create(UserFactory(), date(2012, 9, 30)) assert 1 == Invoice.objects.filter(contact=contact).count()
def test_refresh(): """Create a draft invoice, and then add more time records to it.""" InvoiceSettingsFactory() VatSettingsFactory() tr = TimeRecordFactory() InvoiceContactFactory(contact=tr.ticket.contact) invoice = InvoiceCreate().create( tr.user, tr.ticket.contact, date.today() ) assert 1 == invoice.invoiceline_set.count() # create a couple more time records which can be added TimeRecordFactory(ticket=tr.ticket) TimeRecordFactory(ticket=tr.ticket) InvoiceCreate().refresh(tr.user, invoice, date.today()) assert 3 == invoice.invoiceline_set.count()
def test_invoice_with_time_records(): """Invoice time records.""" InvoiceSettingsFactory() VatSettingsFactory() contact = ContactFactory() InvoiceContactFactory(contact=contact) ticket = TicketFactory(contact=contact) tr1 = TimeRecordFactory(ticket=ticket) TimeRecordFactory(ticket=ticket) invoice = InvoiceCreate().create( tr1.user, contact, date.today() ) assert invoice.is_draft InvoicePrint().create_pdf(invoice, None) assert not invoice.is_draft assert Decimal('40.00') == invoice.net
def test_create_invoices(): """Create an invoice""" InvoiceSettingsFactory() VatSettingsFactory() contact = ContactFactory() InvoiceContactFactory(contact=contact) ticket = TicketFactory(contact=contact) TimeRecordFactory(ticket=ticket, date_started=date(2012, 7, 1)) TimeRecordFactory(ticket=ticket, date_started=date(2012, 8, 1)) TimeRecordFactory(ticket=ticket) # action InvoiceCreateBatch().create(UserFactory(), date(2012, 9, 30)) invoices = Invoice.objects.filter(contact=contact) assert 1 == len(invoices) invoice = invoices[0] assert 2 == len(invoice.invoiceline_set.all())
def test_timerecord_update(perm_check): time_record = TimeRecordFactory() url = reverse( 'invoice.time.update', kwargs={'pk': time_record.pk} ) perm_check.staff(url)
def test_remove_time_lines(): """Remove all lines (because they are all linked to time records).""" InvoiceSettingsFactory() VatSettingsFactory() tr = TimeRecordFactory() InvoiceContactFactory(contact=tr.ticket.contact) TimeRecordFactory(ticket=tr.ticket) invoice = InvoiceCreate().create( tr.user, tr.ticket.contact, date.today() ) assert invoice.is_draft is True InvoicePrint().create_pdf(invoice, None) assert invoice.has_lines is True invoice.set_to_draft() invoice.remove_time_lines() assert invoice.has_lines is False
def test_report_charge_non_charge_user(): to_date = timezone.now() from_date = to_date + relativedelta(months=-1) d = from_date + relativedelta(days=7) user = UserFactory() TimeRecordFactory( billable=True, date_started=d, start_time=time(11, 0), end_time=time(11, 10), user=user, ) TimeRecordFactory( billable=False, date_started=d, start_time=time(11, 0), end_time=time(11, 5), user=user, ) # records for other users TimeRecordFactory( billable=True, date_started=d, start_time=time(11, 0), end_time=time(11, 30), ) TimeRecordFactory( billable=False, date_started=d, start_time=time(10, 0), end_time=time(10, 15), ) # do not include this record TimeRecordFactory( billable=False, date_started=d, start_time=time(11, 0), end_time=None, ) data = TimeRecord.objects.report_charge_non_charge( from_date, to_date, user, ) assert {'Chargeable': 10, 'Non-Chargeable': 5} == data
def test_set_is_draft_too_late(): """invoice can only be set back to draft on the day it is created.""" InvoiceSettingsFactory() VatSettingsFactory() tr = TimeRecordFactory() InvoiceContactFactory(contact=tr.ticket.contact) TimeRecordFactory(ticket=tr.ticket) invoice = InvoiceCreate().create( tr.user, tr.ticket.contact, date.today() ) invoice.invoice_date=date.today() + relativedelta(days=-1) invoice.save() assert invoice.is_draft is True InvoicePrint().create_pdf(invoice, None) assert invoice.is_draft is False with pytest.raises(InvoiceError) as e: invoice.set_to_draft() assert 'only set an invoice back to draft on the day' in str(e.value)
def test_user_can_edit_invoice(): InvoiceSettingsFactory() VatSettingsFactory() invoice = InvoiceFactory() line = InvoiceLineFactory(invoice=invoice) TimeRecordFactory(invoice_line=line) InvoicePrint().create_pdf(invoice, None) # refresh line = InvoiceLine.objects.get(pk=line.pk) assert line.user_can_edit is False
def test_invoice_create_pdf(self): InvoiceSettingsFactory() VatSettingsFactory() contact = ContactFactory() InvoiceContactFactory(contact=contact) ticket = TicketFactory(contact=contact) TimeRecordFactory(ticket=ticket, date_started=date(2013, 12, 1)) invoice = InvoiceCreate().create(UserFactory(), contact, date(2013, 12, 31)) InvoicePrint().create_pdf(invoice, None)
def test_report_total_by_user(): contact = ContactFactory() invoice = InvoiceFactory(contact=contact) InvoiceSettingsFactory() # no time records InvoiceLineFactory(invoice=invoice) # u1's time records invoice_line = InvoiceLineFactory(invoice=invoice) t1 = TicketFactory(contact=contact) TimeRecordFactory(ticket=t1, user=UserFactory(username='******'), invoice_line=invoice_line) # u2's time records invoice_line = InvoiceLineFactory(invoice=invoice) u2 = UserFactory(username='******') t2 = TicketFactory(contact=contact) TimeRecordFactory(ticket=t2, user=u2, invoice_line=invoice_line) invoice_line = InvoiceLineFactory(invoice=invoice) t3 = TicketFactory(contact=contact) TimeRecordFactory(ticket=t3, user=u2, invoice_line=invoice_line) result = invoice.time_analysis() # invoice has a line with no time records assert '' in result # fred recorded time on one ticket assert 'u1' in result u1 = result['u1'] assert 1 == len(u1) assert t1.pk in u1 # sara recorded time on two tickets assert 'u2' in result u2 = result['u2'] assert 2 == len(u2) assert t2.pk in u2 assert t3.pk in u2 # web user added an invoice line, but didn't record time assert 'web' not in result # check net total matches invoice net = Decimal() for user, tickets in result.items(): for ticket_pk, totals in tickets.items(): net = net + totals['net'] assert invoice.net == net
def test_is_not_draft(): InvoiceSettingsFactory() VatSettingsFactory() tr = TimeRecordFactory() InvoiceContactFactory(contact=tr.ticket.contact) invoice = InvoiceCreate().create( tr.user, tr.ticket.contact, date.today() ) assert invoice.is_draft is True InvoicePrint().create_pdf(invoice, None) assert invoice.is_draft is False
def test_report_time_by_contact_user(): user = UserFactory() to_date = timezone.now() from_date = to_date + relativedelta(months=-1) d = from_date + relativedelta(days=7) bob = TicketFactory(contact=ContactFactory(slug='bob')) sam = TicketFactory(contact=ContactFactory(slug='sam')) # these time records are for a different user, so exclude them TimeRecordFactory( ticket=bob, billable=True, date_started=d, start_time=time(11, 0), end_time=time(11, 30), ) TimeRecordFactory( ticket=sam, billable=False, date_started=d, start_time=time(10, 0), end_time=time(10, 15), ) # include these time records TimeRecordFactory( ticket=bob, billable=True, date_started=d, start_time=time(11, 0), end_time=time(11, 30), user=user, ) TimeRecordFactory( ticket=sam, billable=False, date_started=d, start_time=time(10, 0), end_time=time(10, 15), user=user, ) data = TimeRecord.objects.report_time_by_contact(from_date, to_date, user) assert {'bob': 30, 'sam': 15} == data
def test_create_invoices_not_fixed_price(): InvoiceSettingsFactory() VatSettingsFactory() contact = ContactFactory() InvoiceContactFactory(contact=contact) # ticket 1 t1 = TicketFactory(contact=contact) TimeRecordFactory(ticket=t1, title='t1', date_started=date(2012, 7, 1)) # ticket 2 is for fixed price work t2 = TicketFactory(contact=contact, fixed_price=True) TimeRecordFactory(ticket=t2, title='t2', date_started=date(2012, 7, 2)) # ticket 3 t3 = TicketFactory(contact=contact) TimeRecordFactory(ticket=t3, title='t3', date_started=date(2012, 7, 3)) # test InvoiceCreateBatch().create(UserFactory(), date(2012, 9, 30)) assert 1 == Invoice.objects.filter(contact=contact).count() invoice = Invoice.objects.get(contact=contact) assert ['t1', 't3'] == [ x.timerecord.title for x in InvoiceLine.objects.filter(invoice=invoice) ]
def test_ticket_totals(): start_date = date(day=3, month=2, year=2017) ticket = TicketFactory() """ This loop generates the following timerecords: Date Start Tm End Tm Delta Billable Invoice Line 2017-02-03 01:01:00 01:02:00 0:01:00 False None 2017-02-03 02:02:00 02:05:00 0:03:00 True None 2017-02-03 03:03:00 03:10:00 0:07:00 False None 2017-02-03 04:04:00 04:17:00 0:13:00 True None 2017-02-03 05:05:00 05:26:00 0:21:00 False None 2017-02-03 06:06:00 06:37:00 0:31:00 True 0 1.00 description_0 @0.00 2017-02-03 08:00:00 None 0:00:00 True None """ for t in range(1, 7): start_time = time(hour=t, minute=t) end_time = time(hour=t, minute=t*t+1) tr = TimeRecordFactory( ticket=ticket, date_started=start_date, start_time=start_time, end_time=end_time, billable=t % 2 == 0, ) # make the last time record invoiced tr.invoice_line = InvoiceLineFactory(timerecord=tr) tr.save() # open time record - should be ignored TimeRecordFactory( ticket=ticket, date_started=start_date, start_time=time(hour=8), end_time=None, billable=True, ) totals = ticket.totals() assert str(totals['total']) == '1:16:00' assert str(totals['not_billable']) == '0:29:00' assert str(totals['pending']) == '0:16:00' assert str(totals['billable']) == '0:47:00' assert str(totals['invoiced']) == '0:31:00'
def test_report_time_by_user_by_week(): user = UserFactory(username='******') from_date = datetime(2015, 12, 20, 0, 0, 0, tzinfo=pytz.utc) to_date = datetime(2016, 1, 7, 0, 0, 0, tzinfo=pytz.utc) TimeRecordFactory( billable=True, date_started=datetime(2015, 12, 21, 6, 0, 0, tzinfo=pytz.utc), start_time=time(11, 0), end_time=time(11, 30), user=user, ) TimeRecordFactory( billable=True, date_started=datetime(2016, 1, 5, 6, 0, 0, tzinfo=pytz.utc), start_time=time(10, 0), end_time=time(10, 3), user=user, ) TimeRecordFactory( billable=True, date_started=datetime(2016, 1, 7, 6, 0, 0, tzinfo=pytz.utc), start_time=time(10, 0), end_time=time(10, 4), user=UserFactory(), ) TimeRecordFactory( billable=False, date_started=datetime(2016, 1, 6, 6, 0, 0, tzinfo=pytz.utc), start_time=time(10, 0), end_time=time(10, 15), user=user, ) data = TimeRecord.objects.report_time_by_user_by_week( from_date, to_date, user) assert { '2015_51': 30, '2015_52': 0, '2016_01': 18, '2016_02': 0, } == data
def test_report_time_by_contact(): to_date = timezone.now() from_date = to_date + relativedelta(months=-1) d = from_date + relativedelta(days=7) ticket = TicketFactory(contact=ContactFactory(slug='bob')) TimeRecordFactory( ticket=ticket, billable=True, date_started=d, start_time=time(11, 0), end_time=time(11, 30), ) ticket = TicketFactory(contact=ContactFactory(slug='sam')) TimeRecordFactory( ticket=ticket, billable=False, date_started=d, start_time=time(10, 0), end_time=time(10, 15), ) data = TimeRecord.objects.report_time_by_contact(from_date, to_date) assert {'bob': 30, 'sam': 15} == data
def test_create_invoices_do_not_bill_twice(): """Check we can't include the time records more than once""" InvoiceSettingsFactory() VatSettingsFactory() contact = ContactFactory() InvoiceContactFactory(contact=contact) ticket = TicketFactory(contact=contact) TimeRecordFactory(ticket=ticket, date_started=date(2012, 7, 1)) user = UserFactory() InvoiceCreateBatch().create(user, date(2012, 9, 30)) assert 1 == Invoice.objects.filter(contact=contact).count() InvoiceCreateBatch().create(user, date(2012, 9, 30)) assert 1 == Invoice.objects.filter(contact=contact).count()
def test_report_time_by_user(): green = UserFactory(username='******') red = UserFactory(username='******') to_date = timezone.now() from_date = to_date + relativedelta(months=-1) d = from_date + relativedelta(days=7) TimeRecordFactory( billable=True, date_started=d, start_time=time(11, 0), end_time=time(11, 30), user=red, ) TimeRecordFactory( billable=False, date_started=d, start_time=time(10, 0), end_time=time(10, 15), user=green, ) data = TimeRecord.objects.report_time_by_user(from_date, to_date) assert {'red': 30, 'green': 15} == data
def test_refresh_draft_only(): """Only draft invoices can be refreshed.""" InvoiceSettingsFactory() VatSettingsFactory() tr = TimeRecordFactory() InvoiceContactFactory(contact=tr.ticket.contact) invoice = InvoiceCreate().create( tr.user, tr.ticket.contact, date.today() ) assert 1 == invoice.invoiceline_set.count() InvoicePrint().create_pdf(invoice, None) with pytest.raises(InvoiceError) as e: InvoiceCreate().refresh(tr.user, invoice, date.today()) assert 'Time records can only be added to a draft invoice' in str(e.value)
def test_invoice_download(perm_check): InvoiceSettingsFactory() VatSettingsFactory() contact = ContactFactory() InvoiceContactFactory(contact=contact) ticket = TicketFactory(contact=contact) TimeRecordFactory(ticket=ticket, date_started=date(2013, 12, 1)) invoice = InvoiceCreate().create( UserFactory(), contact, date(2013, 12, 31) ) InvoicePrint().create_pdf(invoice, header_image=None) url = reverse('invoice.download', kwargs={'pk': invoice.pk}) perm_check.staff(url)
def test_tickets(): """List of tickets where time was recorded for a user.""" user = UserFactory() today = date.today() # first day of the month from_date = today + relativedelta(day=1) # last day of the month to_date = today + relativedelta(months=+1, day=1, days=-1) # last month last_month = today + relativedelta(months=-1) # next month next_month = today + relativedelta(months=+1) TimeRecordFactory( ticket=TicketFactory(title='t0'), user=user, date_started=last_month, ) TimeRecordFactory( ticket=TicketFactory(title='t1'), user=user, date_started=from_date, ) TimeRecordFactory( ticket=TicketFactory(title='t2'), user=user, date_started=today, ) TimeRecordFactory( ticket=TicketFactory(title='t3'), date_started=to_date, ) TimeRecordFactory( ticket=TicketFactory(title='t4'), user=user, date_started=to_date, end_time=None, ) TimeRecordFactory( ticket=TicketFactory(title='t5'), user=user, date_started=to_date, ) TimeRecordFactory( ticket=TicketFactory(title='t6'), user=user, date_started=next_month, ) qs = TimeRecord.objects.tickets(from_date, to_date, user) assert ['t1', 't2', 't5'] == [x.title for x in qs]
def test_to_invoice(): contact = ContactFactory() d = date(2012, 7, 1) # TimeRecordFactory( title='t1', ticket=TicketFactory(contact=contact), date_started=d, ) # exclude records created after the invoice date TimeRecordFactory( title='t2', ticket=TicketFactory(contact=contact), date_started=date(2012, 8, 1), ) # exclude records for another contact TimeRecordFactory( title='t3', ticket=TicketFactory(contact=ContactFactory()), date_started=d, ) # exclude records which have already been invoiced TimeRecordFactory( title='t4', ticket=TicketFactory(contact=contact), date_started=d, invoice_line=InvoiceLineFactory(), ) # exclude records which have a fixed price ticket TimeRecordFactory( title='t5', ticket=TicketFactory(contact=contact, fixed_price=True), date_started=d, ) # TimeRecordFactory( title='t6', ticket=TicketFactory(contact=contact), date_started=d, ) qs = TimeRecord.objects.to_invoice(contact, date(2012, 7, 31)) assert ['t1', 't6'] == [x.title for x in qs]
def test_stop_end_time(): obj = TimeRecordFactory(end_time=None) assert obj.end_time is None end_time = datetime(2014, 10, 1, 18, 13, 32, tzinfo=pytz.utc) obj.stop(end_time) assert end_time == obj.end_time
def test_stop(): obj = TimeRecordFactory(end_time=None) assert obj.end_time is None obj.stop() assert obj.end_time is not None
def test_stop_already_stopped(): end_time = datetime(2014, 10, 1, 18, 13, 32, tzinfo=pytz.utc) obj = TimeRecordFactory(end_time=end_time) with pytest.raises(InvoiceError) as e: obj.stop() assert 'has already been stopped' in str(e.value)
def test_is_today_not(): d = timezone.now().date() + relativedelta(days=-7) obj = TimeRecordFactory(date_started=d) assert obj.is_today() is False
def test_is_today(): obj = TimeRecordFactory(date_started=timezone.now().date()) assert obj.is_today() is True