aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md9
-rw-r--r--app/dispatch/admin.py6
-rw-r--r--app/dispatch/migrations/0002_owner.py26
-rw-r--r--app/dispatch/migrations/0003_settings.py23
-rw-r--r--app/dispatch/migrations/0004_auto_20171023_2032.py53
-rw-r--r--app/dispatch/migrations/0005_invoice_invoice_id.py21
-rw-r--r--app/dispatch/migrations/0006_auto_20171023_2049.py28
-rw-r--r--app/dispatch/misc.py37
-rw-r--r--app/dispatch/models.py134
-rw-r--r--app/dispatch/templates/dispatch/drivers/detail.html14
-rw-r--r--app/dispatch/views.py28
11 files changed, 335 insertions, 44 deletions
diff --git a/README.md b/README.md
index 5e03ec1..335ad83 100644
--- a/README.md
+++ b/README.md
@@ -4,21 +4,18 @@
Requirements going forward:
* Drivers will be able to generate a weekly PDF invoice
+ * Summary page with Invoice-like layout
+ * Invoice page that lists invoices
+ * Somehow check for duplicate invoices generated?
* Change "user" to Vendor on the "Add Load Page"
- * Auto Fill username when not superuser on add load page
* Users must be able to initially create new load objects
* Change Name To "Load Pay System" -- we're going to think about it
* Freight Invoicing System
* Weekly Total Invoices from the Company(Driver) to software owner
- * Hide Recent Changes on Loads for non-admins?
* /loads
* Give a warning when any loads don't have paperwork attached to them
* Make loads without paperwork blue
* Make loads with paperwork green
- * /drivers/view
- * Give a warning when any loads don't have paperwork attached to them
- * Make loads without paperwork blue
- * Make loads with paperwork green
* A way for users to be invited via email
* Figure out if auditlog timestamps are fucked or not with timezones
diff --git a/app/dispatch/admin.py b/app/dispatch/admin.py
index 617eac4..9d1e0b3 100644
--- a/app/dispatch/admin.py
+++ b/app/dispatch/admin.py
@@ -1,7 +1,11 @@
from django.contrib import admin
# Register your models here.
-from .models import Load, Customer
+from .models import Load, Customer, Identity, Settings, Invoice, InvoiceItem
admin.site.register(Load)
admin.site.register(Customer)
+admin.site.register(Identity)
+admin.site.register(Invoice)
+admin.site.register(InvoiceItem)
+admin.site.register(Settings)
diff --git a/app/dispatch/migrations/0002_owner.py b/app/dispatch/migrations/0002_owner.py
new file mode 100644
index 0000000..689ca6b
--- /dev/null
+++ b/app/dispatch/migrations/0002_owner.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.5 on 2017-10-23 19:14
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('dispatch', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Owner',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('name', models.CharField(max_length=256)),
+ ('address', models.CharField(max_length=256)),
+ ('city', models.CharField(max_length=256)),
+ ('state', models.CharField(max_length=256)),
+ ('zip_code', models.CharField(max_length=256)),
+ ],
+ ),
+ ]
diff --git a/app/dispatch/migrations/0003_settings.py b/app/dispatch/migrations/0003_settings.py
new file mode 100644
index 0000000..3043e2d
--- /dev/null
+++ b/app/dispatch/migrations/0003_settings.py
@@ -0,0 +1,23 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.5 on 2017-10-23 19:19
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('dispatch', '0002_owner'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Settings',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('key', models.CharField(max_length=256)),
+ ('value', models.CharField(max_length=256)),
+ ],
+ ),
+ ]
diff --git a/app/dispatch/migrations/0004_auto_20171023_2032.py b/app/dispatch/migrations/0004_auto_20171023_2032.py
new file mode 100644
index 0000000..59219bc
--- /dev/null
+++ b/app/dispatch/migrations/0004_auto_20171023_2032.py
@@ -0,0 +1,53 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.5 on 2017-10-23 20:32
+from __future__ import unicode_literals
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ('dispatch', '0003_settings'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Invoice',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ],
+ ),
+ migrations.CreateModel(
+ name='InvoiceItem',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('description', models.CharField(max_length=256)),
+ ('quantity', models.IntegerField()),
+ ('amount', models.DecimalField(decimal_places=2, max_digits=19)),
+ ('invoice', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='dispatch.Invoice')),
+ ],
+ ),
+ migrations.RenameModel(
+ old_name='Owner',
+ new_name='Identity',
+ ),
+ migrations.AddField(
+ model_name='invoice',
+ name='bill_to',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='bill_to', to='dispatch.Identity'),
+ ),
+ migrations.AddField(
+ model_name='invoice',
+ name='owner',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='owner', to='dispatch.Identity'),
+ ),
+ migrations.AddField(
+ model_name='invoice',
+ name='user',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
+ ),
+ ]
diff --git a/app/dispatch/migrations/0005_invoice_invoice_id.py b/app/dispatch/migrations/0005_invoice_invoice_id.py
new file mode 100644
index 0000000..23b7c49
--- /dev/null
+++ b/app/dispatch/migrations/0005_invoice_invoice_id.py
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.5 on 2017-10-23 20:36
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('dispatch', '0004_auto_20171023_2032'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='invoice',
+ name='invoice_id',
+ field=models.IntegerField(default=0),
+ preserve_default=False,
+ ),
+ ]
diff --git a/app/dispatch/migrations/0006_auto_20171023_2049.py b/app/dispatch/migrations/0006_auto_20171023_2049.py
new file mode 100644
index 0000000..04668ff
--- /dev/null
+++ b/app/dispatch/migrations/0006_auto_20171023_2049.py
@@ -0,0 +1,28 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.5 on 2017-10-23 20:49
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.utils.timezone
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('dispatch', '0005_invoice_invoice_id'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='invoice',
+ name='due_date',
+ field=models.DateField(default=django.utils.timezone.now),
+ preserve_default=False,
+ ),
+ migrations.AddField(
+ model_name='invoice',
+ name='invoice_date',
+ field=models.DateField(default=django.utils.timezone.now),
+ preserve_default=False,
+ ),
+ ]
diff --git a/app/dispatch/misc.py b/app/dispatch/misc.py
new file mode 100644
index 0000000..14bb5e7
--- /dev/null
+++ b/app/dispatch/misc.py
@@ -0,0 +1,37 @@
+from django.utils import formats
+from datetime import datetime, timedelta
+from dateutil import rrule
+import uuid
+
+def get_week_dates(date=None):
+ week_dates = {}
+ if date == None:
+ date = formats.date_format(datetime.now(), "SHORT_DATE_FORMAT")
+ dt = datetime.strptime(date, '%m/%d/%Y')
+ weekday = dt.weekday()
+ if weekday == 6:
+ week_dates['start_date'] = dt
+ else:
+ weekday = weekday + 1
+ week_dates['start_date'] = dt - timedelta(days=weekday)
+ week_dates['end_date'] = week_dates['start_date'] + timedelta(days=6)
+ week_dates['next_week'] = week_dates['end_date'] + timedelta(days=1)
+ week_dates['previous_week'] = week_dates['start_date'] - timedelta(days=1)
+ return week_dates
+
+def split_loads_by_day(loads,start_date,end_date):
+ split_loads = {}
+
+ for date in rrule.rrule(rrule.DAILY,dtstart=start_date, until=end_date):
+ if date not in split_loads:
+ split_loads[date] = loads.filter(date=date)
+
+ return split_loads
+
+# This is used to set the upload path of the document for Paperwork Objects
+def paperwork_user_directory_path(instance, filename):
+ # We don't want the UUID to be too long, just enough so there aren't any
+ # filename conflicts
+ return 'paperwork/{:d}/'.format(instance.load.pk) + \
+ str(uuid.uuid4())[0:9] + filename
+
diff --git a/app/dispatch/models.py b/app/dispatch/models.py
index 6cbaf6c..dfffb9a 100644
--- a/app/dispatch/models.py
+++ b/app/dispatch/models.py
@@ -2,8 +2,9 @@ from django.db import models
from django.conf import settings
from auditlog.registry import auditlog
from auditlog.models import AuditlogHistoryField
-
-import uuid
+from django.contrib.auth.models import User
+from datetime import datetime
+from .misc import get_week_dates, paperwork_user_directory_path
# Create your models here.
@@ -36,14 +37,6 @@ class Load(models.Model):
return "/loads/view/%i" % self.id
-# This is used to set the upload path of the document for Paperwork Objects
-def paperwork_user_directory_path(instance, filename):
- # We don't want the UUID to be too long, just enough so there aren't any
- # filename conflicts
- return 'paperwork/{:d}/'.format(instance.load.pk) + \
- str(uuid.uuid4())[0:9] + filename
-
-
class Paperwork(models.Model):
load = models.ForeignKey(Load, on_delete=models.CASCADE)
description = models.CharField(max_length=256)
@@ -53,5 +46,126 @@ class Paperwork(models.Model):
return "%s" % self.load
+class Identity(models.Model):
+ name = models.CharField(max_length=256)
+ address = models.CharField(max_length=256)
+ city = models.CharField(max_length=256)
+ state = models.CharField(max_length=256)
+ zip_code = models.CharField(max_length=256)
+
+ def __str__(self):
+ return "{}".format(self.name)
+
+class Settings(models.Model):
+ key = models.CharField(max_length=256)
+ value = models.CharField(max_length=256)
+
+ def __str__(self):
+ return "{}: {}".format(self.key, self.value)
+
+
+class Invoice(models.Model):
+ user = models.ForeignKey(User, on_delete=models.CASCADE)
+ owner = models.ForeignKey(Identity, on_delete=models.CASCADE, related_name="owner")
+ bill_to = models.ForeignKey(Identity, on_delete=models.CASCADE, related_name="bill_to")
+ invoice_id = models.IntegerField()
+ invoice_date = models.DateField()
+ due_date = models.DateField()
+
+ def __str__(self):
+ return "Invoice for {} by {} for ${}".format(
+ self.bill_to.name,
+ self.owner.name,
+ self.total())
+
+ def populate_bill_to(self):
+ pk = Settings.objects.get(key="default_bill_to")
+ self.bill_to = Identity.objects.get(pk=pk)
+
+ def populate_owner(self):
+ key = "user_{:d}_identity".format(self.user.id)
+ pk = Settings.objects.get(key=key)
+ self.owner = Identity.objects.get(pk=pk)
+
+ def populate_items_default_date(self):
+ wk = get_week_dates()
+ return self.populate_items(start_date=wk['start_date'],
+ end_date=wk['end_date'])
+
+ def populate_items(self, start_date, end_date):
+ loads = Load.objects.filter(user__exact=self.user.id, date__range=(start_date, end_date))
+ for load in loads:
+ i = InvoiceItem(invoice=self,
+ description=load.description,
+ quantity=1,
+ amount=load.amount)
+ i.save()
+
+ def clear_items(self):
+ items = self.items()
+ for item in items:
+ item.delete()
+
+ def items(self):
+ return InvoiceItem.objects.filter(invoice__exact=self.pk)
+
+ def total(self):
+ t = 0
+ for i in InvoiceItem.objects.filter(invoice__exact=self.pk):
+ t += i.amount
+ return t
+
+class InvoiceItem(models.Model):
+ invoice = models.ForeignKey(Invoice, on_delete=models.CASCADE)
+ description = models.CharField(max_length=256)
+ quantity = models.IntegerField()
+ amount = models.DecimalField(max_digits=19,decimal_places=2)
+
+ def __str__(self):
+ return "Invoice ID: {} Item: {} for ${}".format(
+ self.invoice.pk,
+ self.description,
+ self.amount)
+
+
+
+# class Invoice():
+# load_ids = []
+# total = 0.00
+#
+# def __init__(self, **kwargs):
+# # self.args = kwargs
+# if isinstance(kwargs['driver'], User):
+# self.driver = kwargs['driver']
+# else:
+# raise Exception("'driver' must be a Django User instance")
+#
+# if isinstance(kwargs.get('start_date'), datetime) and isinstance(kwargs.get('end_date'), datetime):
+# self.start_date = kwargs.get('start_date')
+# self.end_date = kwargs.get('end_date')
+# else:
+# dates = get_week_dates()
+# self.start_date = dates['start_date']
+# self.end_date = dates['end_date']
+#
+# self.loads = Load.objects.filter(user__exact=self.driver.id,
+# date__range=(self.start_date, self.end_date))
+#
+# for l in self.loads:
+# self.load_ids.append(l.pk)
+# self.total += float(l.amount)
+#
+# if isinstance(kwargs.get('bill_to'), Owner):
+# self.bill_to = kwargs.get('bill_to')
+# else:
+# self.bill_to = Owner.objects.get(pk=Settings.objects.get(key='default_owner').value)
+#
+#
+# def __str__(self):
+# return "Invoice for {} from {} for: {}".format(self.end_date, self.driver, self.total)
+
+
+
+
auditlog.register(Customer)
auditlog.register(Load)
diff --git a/app/dispatch/templates/dispatch/drivers/detail.html b/app/dispatch/templates/dispatch/drivers/detail.html
index 11e07a9..21d2432 100644
--- a/app/dispatch/templates/dispatch/drivers/detail.html
+++ b/app/dispatch/templates/dispatch/drivers/detail.html
@@ -30,6 +30,20 @@
</div>
+<div class="row">
+ <div class="col s12">
+ <table>
+ <thead>
+ <tr>
+ <td>Date</td>
+ <td>Description</td>
+ <td>Total</td>
+ </tr>
+ </thead>
+ </table>
+ </div>
+</div>
+
<div class="row">
diff --git a/app/dispatch/views.py b/app/dispatch/views.py
index 4a8a18b..65d2811 100644
--- a/app/dispatch/views.py
+++ b/app/dispatch/views.py
@@ -15,38 +15,12 @@ from dispatch.forms import AddPaperworkForm
from django.contrib.auth.models import User
from django.contrib.auth.mixins import UserPassesTestMixin
# from django.http import HttpResponseRedirect
-from datetime import datetime, timedelta
-from django.utils import formats
-from dateutil import rrule
+from .misc import get_week_dates, split_loads_by_day
import re, os
def home(request):
return redirect(reverse('load_list'))
-def get_week_dates(date=None):
- week_dates = {}
- if date == None:
- date = formats.date_format(datetime.now(), "SHORT_DATE_FORMAT")
- dt = datetime.strptime(date, '%m/%d/%Y')
- weekday = dt.weekday()
- if weekday == 6:
- week_dates['start_date'] = dt
- else:
- weekday = weekday + 1
- week_dates['start_date'] = dt - timedelta(days=weekday)
- week_dates['end_date'] = week_dates['start_date'] + timedelta(days=6)
- week_dates['next_week'] = week_dates['end_date'] + timedelta(days=1)
- week_dates['previous_week'] = week_dates['start_date'] - timedelta(days=1)
- return week_dates
-
-def split_loads_by_day(loads,start_date,end_date):
- split_loads = {}
-
- for date in rrule.rrule(rrule.DAILY,dtstart=start_date, until=end_date):
- if date not in split_loads:
- split_loads[date] = loads.filter(date=date)
-
- return split_loads
class LoadDateSort(DetailView):
def get_context_data(self, **kwargs):