diff options
Diffstat (limited to 'app/unused_apps/income')
-rw-r--r-- | app/unused_apps/income/__init__.py | 0 | ||||
-rw-r--r-- | app/unused_apps/income/admin.py | 88 | ||||
-rw-r--r-- | app/unused_apps/income/migrations/0001_initial.py | 37 | ||||
-rw-r--r-- | app/unused_apps/income/migrations/0002_gig_due_date.py | 21 | ||||
-rw-r--r-- | app/unused_apps/income/migrations/0003_auto_20161213_1038.py | 25 | ||||
-rw-r--r-- | app/unused_apps/income/migrations/0004_invoice_invoiceitem.py | 31 | ||||
-rw-r--r-- | app/unused_apps/income/migrations/0005_invoice_slug.py | 19 | ||||
-rw-r--r-- | app/unused_apps/income/migrations/0006_auto_20190131_2351.py | 17 | ||||
-rw-r--r-- | app/unused_apps/income/migrations/__init__.py | 0 | ||||
-rw-r--r-- | app/unused_apps/income/models.py | 103 | ||||
-rw-r--r-- | app/unused_apps/income/parser.py | 23 | ||||
-rw-r--r-- | app/unused_apps/income/views.py | 53 |
12 files changed, 417 insertions, 0 deletions
diff --git a/app/unused_apps/income/__init__.py b/app/unused_apps/income/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/app/unused_apps/income/__init__.py diff --git a/app/unused_apps/income/admin.py b/app/unused_apps/income/admin.py new file mode 100644 index 0000000..b165371 --- /dev/null +++ b/app/unused_apps/income/admin.py @@ -0,0 +1,88 @@ +import datetime +from django.contrib import admin +from django.db.models import Sum +from django.contrib.gis.admin import OSMGeoAdmin +from django.conf.urls import url +from django.shortcuts import render + + +from .models import Gig, Invoice, InvoiceItem + + +@admin.register(InvoiceItem) +class InvoiceItemAdmin(admin.ModelAdmin): + list_display = ('time_start', 'time_end', 'work_done') + + +@admin.register(Invoice) +class InvoiceAdmin(admin.ModelAdmin): + list_display = ('title', 'admin_link', 'date_start', 'date_end') + + +@admin.register(Gig) +class GigAdmin(OSMGeoAdmin): + list_display = ('title', 'status', 'due_date', 'payment_status', 'payment', 'publisher', 'word_count') + list_filter = ('publisher', 'status', 'payment_status') + fieldsets = ( + ('Gig', { + 'fields': ( + 'title', + 'pitch', + ('created', 'due_date'), + ('payment', 'pay_type', 'payment_status', 'invoice_date'), + ('status', 'pub_date', 'word_count'), + 'publisher', + 'pub_item' + ), + 'classes': ( + 'show', + 'extrapretty', + 'wide' + ) + } + ), + ) + + def get_urls(self): + urls = super(GigAdmin, self).get_urls() + custom_urls = [ + url( + r'^monthly/$', + self.admin_site.admin_view(self.get_monthly), + name='monthly_admin' + ) + ] + return custom_urls + urls + + def get_monthly(self, request): + context = { + 'title': ("This month's income"), + 'app_label': self.model._meta.app_label, + 'opts': self.model._meta, + 'has_change_permission': self.has_change_permission(request) + } + try: + year = request.GET["m"].split("-")[0] + month = request.GET["m"].split("-")[1] + except: + year = datetime.datetime.now().strftime('%Y') + month = datetime.datetime.now().strftime('%m') + qs = self.model.objects.filter( + created__year=year, + created__month=month, + status__in=[1, 2, 3] + ) + context['pitched'] = self.model.objects.filter( + created__year=year, + created__month=month, + status=0 + ) + context['date'] = datetime.datetime.now() + context['billed'] = qs.filter(payment_status=1) + context['billed_total'] = qs.filter(payment_status=1).aggregate(total_payment=Sum('payment')) + context['unbilled'] = qs.filter(payment_status=0) + context['unbilled_total'] = qs.filter(payment_status=0).aggregate(total_payment=Sum('payment')) + context['total_outstanding'] = qs.aggregate(total_payment=Sum('payment')) + context['months'] = self.model.objects.dates('created', 'month') + return render(request, 'admin/income_month.html', context) + diff --git a/app/unused_apps/income/migrations/0001_initial.py b/app/unused_apps/income/migrations/0001_initial.py new file mode 100644 index 0000000..3a1e926 --- /dev/null +++ b/app/unused_apps/income/migrations/0001_initial.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-02-10 08:48 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('resume', '0003_auto_20151211_1925'), + ] + + operations = [ + migrations.CreateModel( + name='Gig', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=200)), + ('pitch', models.TextField(blank=True, null=True)), + ('created', models.DateTimeField(default=django.utils.timezone.now)), + ('pub_date', models.DateTimeField(default=django.utils.timezone.now)), + ('status', models.IntegerField(choices=[(0, 'Pitched'), (1, 'Accepted'), (2, 'Submitted'), (3, 'Published'), (4, 'Rejected'), (5, 'TO PITCH')], default=1)), + ('invoice_date', models.DateTimeField(blank=True, null=True)), + ('payment', models.DecimalField(decimal_places=2, max_digits=10)), + ('payment_status', models.IntegerField(choices=[(0, 'NOT SUBMITTED'), (1, 'Invoiced'), (2, 'Paid')], default=1)), + ('pay_type', models.IntegerField(choices=[(0, 'Flat Rate'), (1, 'Per Word'), (2, 'Hourly')], default=1)), + ('word_count', models.DecimalField(blank=True, decimal_places=0, max_digits=7, null=True)), + ('pub_item', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='resume.PubItem')), + ('publisher', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='resume.Publisher')), + ], + ), + ] diff --git a/app/unused_apps/income/migrations/0002_gig_due_date.py b/app/unused_apps/income/migrations/0002_gig_due_date.py new file mode 100644 index 0000000..ccd0f73 --- /dev/null +++ b/app/unused_apps/income/migrations/0002_gig_due_date.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-02-10 08:49 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('income', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='gig', + name='due_date', + field=models.DateField(default=django.utils.timezone.now), + ), + ] diff --git a/app/unused_apps/income/migrations/0003_auto_20161213_1038.py b/app/unused_apps/income/migrations/0003_auto_20161213_1038.py new file mode 100644 index 0000000..559cd5a --- /dev/null +++ b/app/unused_apps/income/migrations/0003_auto_20161213_1038.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-12-13 10:38 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('income', '0002_gig_due_date'), + ] + + operations = [ + migrations.AlterField( + model_name='gig', + name='due_date', + field=models.DateField(blank=True, null=True), + ), + migrations.AlterField( + model_name='gig', + name='pub_date', + field=models.DateTimeField(blank=True, null=True), + ), + ] diff --git a/app/unused_apps/income/migrations/0004_invoice_invoiceitem.py b/app/unused_apps/income/migrations/0004_invoice_invoiceitem.py new file mode 100644 index 0000000..59f389c --- /dev/null +++ b/app/unused_apps/income/migrations/0004_invoice_invoiceitem.py @@ -0,0 +1,31 @@ +# Generated by Django 2.1 on 2018-09-03 17:58 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('income', '0003_auto_20161213_1038'), + ] + + operations = [ + migrations.CreateModel( + name='Invoice', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=200)), + ('date_start', models.DateField(blank=True, null=True)), + ('date_end', models.DateField(blank=True, null=True)), + ], + ), + migrations.CreateModel( + name='InvoiceItem', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('time_start', models.DateTimeField(blank=True, null=True)), + ('time_end', models.DateTimeField(blank=True, null=True)), + ('work_done', models.TextField(blank=True, null=True)), + ], + ), + ] diff --git a/app/unused_apps/income/migrations/0005_invoice_slug.py b/app/unused_apps/income/migrations/0005_invoice_slug.py new file mode 100644 index 0000000..3b6dfb2 --- /dev/null +++ b/app/unused_apps/income/migrations/0005_invoice_slug.py @@ -0,0 +1,19 @@ +# Generated by Django 2.1.1 on 2018-09-03 19:02 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('income', '0004_invoice_invoiceitem'), + ] + + operations = [ + migrations.AddField( + model_name='invoice', + name='slug', + field=models.SlugField(default='slug'), + preserve_default=False, + ), + ] diff --git a/app/unused_apps/income/migrations/0006_auto_20190131_2351.py b/app/unused_apps/income/migrations/0006_auto_20190131_2351.py new file mode 100644 index 0000000..1c8f64c --- /dev/null +++ b/app/unused_apps/income/migrations/0006_auto_20190131_2351.py @@ -0,0 +1,17 @@ +# Generated by Django 2.1.1 on 2019-01-31 23:51 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('income', '0005_invoice_slug'), + ] + + operations = [ + migrations.AlterModelOptions( + name='invoiceitem', + options={'ordering': ('time_start',)}, + ), + ] diff --git a/app/unused_apps/income/migrations/__init__.py b/app/unused_apps/income/migrations/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/app/unused_apps/income/migrations/__init__.py diff --git a/app/unused_apps/income/models.py b/app/unused_apps/income/models.py new file mode 100644 index 0000000..e5a351b --- /dev/null +++ b/app/unused_apps/income/models.py @@ -0,0 +1,103 @@ +import datetime +from datetime import timedelta +from django.db import models +from django.utils import timezone +from django.urls import reverse +from django.utils.html import format_html + +from resume.models import PubItem, Publisher + + +class Gig(models.Model): + title = models.CharField(max_length=200) + pitch = models.TextField(null=True, blank=True) + created = models.DateTimeField(default=timezone.now) + pub_date = models.DateTimeField(blank=True, null=True) + due_date = models.DateField(blank=True, null=True) + STATUS = ( + (0, "Pitched"), + (1, "Accepted"), + (2, "Submitted"), + (3, "Published"), + (4, "Rejected"), + (5, "TO PITCH"), + ) + status = models.IntegerField(choices=STATUS, default=1) + invoice_date = models.DateTimeField(null=True, blank=True) + payment = models.DecimalField(max_digits=10, decimal_places=2) + PAY_STATUS = ( + (0, "NOT SUBMITTED"), + (1, "Invoiced"), + (2, "Paid"), + ) + payment_status = models.IntegerField(choices=PAY_STATUS, default=1) + PAY_TYPE = ( + (0, "Flat Rate"), + (1, "Per Word"), + (2, "Hourly"), + ) + pay_type = models.IntegerField(choices=PAY_TYPE, default=1) + word_count = models.DecimalField(max_digits=7, decimal_places=0, blank=True, null=True) + publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE, blank=True, null=True) + pub_item = models.ForeignKey(PubItem, on_delete=models.CASCADE, blank=True, null=True) + + def __str__(self): + return self.title + + def get_pay_date(self): + days = self.publisher.payment_time * 7 + if self.invoice_date: + return self.invoice_date + datetime.timedelta(float(days)) + + +class Invoice(models.Model): + title = models.CharField(max_length=200) + slug = models.SlugField() + date_start = models.DateField(null=True, blank=True) + date_end = models.DateField(null=True, blank=True) + + def __str__(self): + return self.title + + def admin_link(self): + return format_html('<a href="/admin/income/invoice/monthlyview/%s/">View Invoice</a>' % (self.slug)) + admin_link.short_description = 'Invoice' + + +class InvoiceItem(models.Model): + time_start = models.DateTimeField(null=True, blank=True) + time_end = models.DateTimeField(null=True, blank=True) + work_done = models.TextField(null=True, blank=True) + + class Meta: + ordering = ('time_start',) + + def __str__(self): + return str(self.time_start) + + @property + def total(self): + return self.time_end - self.time_start + + @property + def rounded_total(self): + """ + Rounds the given timedelta by the given timedelta period + :param td: `timedelta` to round + :param period: `timedelta` period to round by. + """ + period = timedelta(minutes=15) + td = self.total + period_seconds = period.total_seconds() + half_period_seconds = period_seconds / 2 + remainder = td.total_seconds() % period_seconds + if remainder >= half_period_seconds: + tdr = timedelta(seconds=td.total_seconds() + (period_seconds - remainder)) + hours, remainder = divmod(tdr.total_seconds(), 3600) + r = remainder/3600 + return float(hours)+r + else: + tdr = timedelta(seconds=td.total_seconds() - remainder) + hours, remainder = divmod(tdr.total_seconds(), 3600) + r = remainder/3600 + return float(hours)+r diff --git a/app/unused_apps/income/parser.py b/app/unused_apps/income/parser.py new file mode 100644 index 0000000..b19d039 --- /dev/null +++ b/app/unused_apps/income/parser.py @@ -0,0 +1,23 @@ +import csv +import datetime +from .models import InvoiceItem + + +def read_timesheet(): + with open('timesheet.csv', newline='') as csvfile: + reader = csv.reader(csvfile, delimiter=';') + counter = 0 + f = "%Y-%m-%d %H:%M:%S" + for row in reader: + if counter > 0: + print(row[4]) + timer = row[0]+' '+row[1] + timerer = row[0]+' '+row[2] + time_start = datetime.datetime.strptime(timer, f) + time_end = datetime.datetime.strptime(timerer, f) + InvoiceItem.objects.get_or_create( + time_start=time_start, + time_end=time_end, + work_done=row[4] + ) + counter = counter + 1 diff --git a/app/unused_apps/income/views.py b/app/unused_apps/income/views.py new file mode 100644 index 0000000..1c34068 --- /dev/null +++ b/app/unused_apps/income/views.py @@ -0,0 +1,53 @@ +import datetime +from django.views.generic.detail import DetailView +from django.template.loader import render_to_string +from django.http import HttpResponse +from django.conf import settings + +#from weasyprint import HTML, CSS + +from .models import Invoice, InvoiceItem + + +class MonthlyInvoiceView(DetailView): + model = Invoice + template_name = "admin/income/monthly.html" + slug_field = "slug" + + def get_context_data(self, **kwargs): + context = super(MonthlyInvoiceView, self).get_context_data(**kwargs) + context['object_list'] = InvoiceItem.objects.filter(time_start__range=[self.object.date_start, self.object.date_end]) + total_time = [] + for item in context['object_list']: + total_time.append(item.rounded_total) + hours = (sum(total_time)) + context['total_hours'] = hours + context['total_billed'] = int(hours * 100) + context['invoice_number'] = self.object.id+21 + return context + + +class DownloadMonthlyInvoiceView(MonthlyInvoiceView): + model = Invoice + slug_field = "slug" + + def get(self, *args, **kwargs): + import logging + logger = logging.getLogger('weasyprint') + logger.addHandler(logging.FileHandler('weasyprint.log')) + self.object = self.get_object() # assign the object to the view + context = self.get_context_data() + c = { + 'object': self.object, + 'object_list': context['object_list'], + 'total_hours': context['total_hours'], + 'total_billed': context['total_billed'], + 'invoice_number': self.object.id+23 + } + t = render_to_string('details/invoice.html', c).encode('utf-8') + #html = HTML(string=t, base_url=self.request.build_absolute_uri()) + #pdf = html.write_pdf(stylesheets=[CSS(settings.MEDIA_ROOT + '/pdf_gen.css')], presentational_hints=True) + #response = HttpResponse(pdf, content_type='application/pdf') + #response['Content-Disposition'] = 'inline; filename="invoice.pdf"' + response = HttpResponse('', content_type='application/pdf') + return response |