diff options
| author | Mitch Riedstra <Mitch@riedstra.us> | 2017-11-09 15:57:46 -0500 |
|---|---|---|
| committer | Mitch Riedstra <Mitch@riedstra.us> | 2017-11-09 15:57:46 -0500 |
| commit | 33a6e5cb02189b7621a279c32c12b5c3d83ba680 (patch) | |
| tree | 6d9776af90222f8062921b62fc7f6f4b5fa01dc7 /app | |
| parent | c055260bb88fd407c81ad0d40dd288a5aae4fb17 (diff) | |
| download | dispatch-tracker-33a6e5cb02189b7621a279c32c12b5c3d83ba680.tar.gz dispatch-tracker-33a6e5cb02189b7621a279c32c12b5c3d83ba680.tar.xz | |
Show number of attachments, and color code issues with loads.
Prevent invoice generation on zero amounts and no attachments.
Add a way for superusers to edit invoices.
Add a field to invoices for a payment identifier. E.g. "Check #1234"
Diffstat (limited to 'app')
| -rw-r--r-- | app/dispatch/admin.py | 3 | ||||
| -rw-r--r-- | app/dispatch/migrations/0001_initial.py | 2 | ||||
| -rw-r--r-- | app/dispatch/migrations/0002_auto_20171107_1912.py (renamed from app/dispatch/migrations/0002_auto_20171102_1949.py) | 4 | ||||
| -rw-r--r-- | app/dispatch/migrations/0003_invoice_payment_identifer.py | 20 | ||||
| -rw-r--r-- | app/dispatch/models.py | 8 | ||||
| -rw-r--r-- | app/dispatch/monkey_patch.py | 10 | ||||
| -rw-r--r-- | app/dispatch/templates/dispatch/drivers/detail.html | 14 | ||||
| -rw-r--r-- | app/dispatch/templates/dispatch/drivers/summary.html | 47 | ||||
| -rw-r--r-- | app/dispatch/templates/dispatch/generic_load_listing.html | 18 | ||||
| -rw-r--r-- | app/dispatch/templates/dispatch/invoice/detail-table.html | 22 | ||||
| -rw-r--r-- | app/dispatch/templates/dispatch/invoice/detail.html | 8 | ||||
| -rw-r--r-- | app/dispatch/templates/dispatch/invoice/edit.html | 21 | ||||
| -rw-r--r-- | app/dispatch/templates/dispatch/loads/list.html | 2 | ||||
| -rw-r--r-- | app/dispatch/urls.py | 2 | ||||
| -rw-r--r-- | app/dispatch/views.py | 64 | ||||
| -rw-r--r-- | app/dispatchAuth/migrations/0001_initial.py | 4 | ||||
| -rw-r--r-- | app/dispatchAuth/migrations/0002_auto_20171106_2233.py | 20 |
17 files changed, 205 insertions, 64 deletions
diff --git a/app/dispatch/admin.py b/app/dispatch/admin.py index 9d1e0b3..702f0e4 100644 --- a/app/dispatch/admin.py +++ b/app/dispatch/admin.py @@ -1,7 +1,7 @@ from django.contrib import admin # Register your models here. -from .models import Load, Customer, Identity, Settings, Invoice, InvoiceItem +from .models import Load, Paperwork, Customer, Identity, Settings, Invoice, InvoiceItem admin.site.register(Load) admin.site.register(Customer) @@ -9,3 +9,4 @@ admin.site.register(Identity) admin.site.register(Invoice) admin.site.register(InvoiceItem) admin.site.register(Settings) +admin.site.register(Paperwork) diff --git a/app/dispatch/migrations/0001_initial.py b/app/dispatch/migrations/0001_initial.py index a358b07..d2aa2f8 100644 --- a/app/dispatch/migrations/0001_initial.py +++ b/app/dispatch/migrations/0001_initial.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.11.5 on 2017-11-02 19:49 +# Generated by Django 1.11.5 on 2017-11-07 19:12 from __future__ import unicode_literals import dispatch.misc diff --git a/app/dispatch/migrations/0002_auto_20171102_1949.py b/app/dispatch/migrations/0002_auto_20171107_1912.py index 4be45e9..059d7be 100644 --- a/app/dispatch/migrations/0002_auto_20171102_1949.py +++ b/app/dispatch/migrations/0002_auto_20171107_1912.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.11.5 on 2017-11-02 19:49 +# Generated by Django 1.11.5 on 2017-11-07 19:12 from __future__ import unicode_literals from django.conf import settings @@ -12,8 +12,8 @@ class Migration(migrations.Migration): initial = True dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), ('dispatch', '0001_initial'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ diff --git a/app/dispatch/migrations/0003_invoice_payment_identifer.py b/app/dispatch/migrations/0003_invoice_payment_identifer.py new file mode 100644 index 0000000..21130a7 --- /dev/null +++ b/app/dispatch/migrations/0003_invoice_payment_identifer.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.5 on 2017-11-09 20:53 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('dispatch', '0002_auto_20171107_1912'), + ] + + operations = [ + migrations.AddField( + model_name='invoice', + name='payment_identifer', + field=models.CharField(default='', max_length=256), + ), + ] diff --git a/app/dispatch/models.py b/app/dispatch/models.py index fe0cdb3..ef59fe4 100644 --- a/app/dispatch/models.py +++ b/app/dispatch/models.py @@ -40,6 +40,13 @@ class Load(models.Model): def get_absolute_url(self): return "/loads/view/%i" % self.id + def can_invoice(self): + # Prevent 0 and $1 invoices as well as loads w/o attachments + if self.amount >= 2 and len(self.paperwork_set.all()) >= 1: + return True + else: + return False + class Paperwork(models.Model): load = models.ForeignKey(Load, on_delete=models.CASCADE) @@ -96,6 +103,7 @@ class Invoice(models.Model): invoice_date = models.DateField() due_date = models.DateField() paid = models.BooleanField(default=False) + payment_identifer = models.CharField(default="", max_length=256) def __str__(self): return "Invoice for {} by {} for ${}".format( diff --git a/app/dispatch/monkey_patch.py b/app/dispatch/monkey_patch.py deleted file mode 100644 index cc61b6e..0000000 --- a/app/dispatch/monkey_patch.py +++ /dev/null @@ -1,10 +0,0 @@ -from django.contrib.auth import get_user_model -from django.core.urlresolvers import reverse - -# This should be going away soon with the addition -# of my custom user model - -def get_absolute_url(self): - return reverse('driver_details', kwargs={'pk': self.pk}) - -User.add_to_class("get_absolute_url", get_absolute_url) diff --git a/app/dispatch/templates/dispatch/drivers/detail.html b/app/dispatch/templates/dispatch/drivers/detail.html index 99d9611..e8ffb99 100644 --- a/app/dispatch/templates/dispatch/drivers/detail.html +++ b/app/dispatch/templates/dispatch/drivers/detail.html @@ -51,7 +51,8 @@ <div class="col s6"> <div class="right-align"> {% if request.user.is_superuser %} - <a class="btn orange" href="{% url 'identity_default' object.pk ident.pk %}">Make Default Bill To</a> + <a class="btn orange" href="{% url 'identity_default' object.pk ident.pk %}">Make Default Bill To</a> + {% else %} {% endif %} <a class="btn green" href="{% url 'identity_edit' object.pk ident.pk %}"> Edit @@ -60,6 +61,17 @@ </div> </div> +{% if request.user.is_superuser %} +<div class="row"> + <div class="col s12"> + <p class=""> + "Default Bill To" is the Identity object to which all of the Invoices generated + by the system will have their Bill-To set. Be careful when changing this. + </p> + </div> +</div> +{% endif %} + <div class="row"> <div class="col s12"> <table> diff --git a/app/dispatch/templates/dispatch/drivers/summary.html b/app/dispatch/templates/dispatch/drivers/summary.html index 5da7d3c..362c1fa 100644 --- a/app/dispatch/templates/dispatch/drivers/summary.html +++ b/app/dispatch/templates/dispatch/drivers/summary.html @@ -15,10 +15,15 @@ <div class="row"> <div class="col s12"> <div class="right-align"> + {% if can_invoice %} <a class="btn orange" href="{% url 'invoice_generate' object.pk%}?date={{week_dates.start_date|date:"m/d/Y"}}">Generate Invoice For Listing</a> + {% else %} + <a class="btn orange disabled">Generate Invoice For Listing</a> + {% endif %} </div> </div> </div> +{% else %} {% endif %} {% if not request.user.is_superuser %} @@ -30,6 +35,7 @@ <td>Date</td> <td>Description</td> <td>Total</td> + <td>Attachments</td> <td></td> </tr> </thead> @@ -38,7 +44,17 @@ <tr> <td>{{load.date}}</td> <td>{{load.description}}</td> - <td>{{load.amount}}</td> + {% if load.amount == 0 %} + <td><b><span class="red-text">{{load.amount}}</span></b></td> + {% else %} + <td>{{load.amount}}</td> + {% endif %} + {% if load.paperwork_set.all %} + <td>{{load.paperwork_set.all|length}}</td> + {% else %} + <td><b><span class="red-text">0</span></b></td> + {% endif %} + <td></td> <td class="right-align"> <a href="{% url 'load_detail' load.id %}" class="btn green"> View @@ -52,6 +68,7 @@ </div> {% endif %} + <div class="row"> <div class="col s12"> <table> @@ -71,6 +88,18 @@ </div> </div> +{% if not can_invoice %} +<div class="row"> + <div class="col s12"> + <h5>Note:</h5> + <p class="flow-text"> + Cannot generate an invoice at this time, one or more Loads has an improper + Amount or is missing attachments. + </p> + </div> +</div> +{% endif %} + @@ -84,23 +113,9 @@ </div> </div> -{% if stats.incomplete_loads > 0 %} -<div class="row"> - <div class="col s12 m8 offset-m2"> - <div class="card red z-depth-3"> - <div class="card-content black-text"> - <span class="card-title"> - Note: One or more loads has a 0 amount that needs attention - </span> - </div> - </div> - </div> -</div> -{% endif %} - {% if request.user.is_superuser %} {% load custom_tags %} - {% listForCommaString "Customer,Amount,Description" as load_headers %} + {% listForCommaString "Customer,Amount,Description,Attachments" as load_headers %} {% include "dispatch/generic_load_listing.html" %} {% endif %} diff --git a/app/dispatch/templates/dispatch/generic_load_listing.html b/app/dispatch/templates/dispatch/generic_load_listing.html index 5512184..3a78335 100644 --- a/app/dispatch/templates/dispatch/generic_load_listing.html +++ b/app/dispatch/templates/dispatch/generic_load_listing.html @@ -28,7 +28,13 @@ </thead> <tbody> {% for load in loads|keyvalue:date %} - <tr class="green lighten-4"> + {% if load.amount <= 2 %} + <tr class="red lighten-4"> + {% elif load.paperwork_set.all|length <= 0 %} + <tr class="orange lighten-4"> + {% else %} + <tr class="green lighten-4"> + {% endif %} {% if "Customer" in load_headers %} {% if request.user.is_superuser %} <td><a href="{% url 'customer_detail' load.customer.id %}">{{ load.customer.name }}</a></td> @@ -42,7 +48,7 @@ {% if "Amount" in load_headers %} {% if load.amount == 0 %} - <td><span class="red-text">{{ load.amount }}</span></td> + <td><b><span class="red-text">{{ load.amount }}</span></b></td> {% else %} <td>{{ load.amount }}</td> {% endif %} @@ -52,6 +58,14 @@ <td>{{ load.description }}</td> {% endif %} + {% if "Attachments" in load_headers %} + {% if load.paperwork_set.all %} + <td>{{load.paperwork_set.all|length}}</td> + {% else %} + <td><b><span class="red-text">0</span></b></td> + {% endif %} + {% endif %} + <td class="right-align"> <!-- <a href="{% url 'load_edit' load.id %}" class="btn orange">Edit</a> --> <a href="{% url 'load_detail' load.id %}" class="btn blue">View</a> diff --git a/app/dispatch/templates/dispatch/invoice/detail-table.html b/app/dispatch/templates/dispatch/invoice/detail-table.html index 37fa2a5..508ab31 100644 --- a/app/dispatch/templates/dispatch/invoice/detail-table.html +++ b/app/dispatch/templates/dispatch/invoice/detail-table.html @@ -4,6 +4,9 @@ Invoice #{{object.invoice_id}} </h4> </div> + <div class="col s6 right-align"> + <h4>PAID</h4> + </div> </div> <div class="row"> <div class="col s6"> @@ -67,9 +70,28 @@ <td>{{object.total}}</td> </tr> <tr> + <td><b>Amount Paid:</b></td> + + {% if object.paid %} + <td>{{object.payment_identifier}}</td> + {% else %} + <td></td> + {% endif %} + + {% if object.paid %} + <td>({{object.total}})</td> + {% else %} + <td>0</td> + {% endif %} + </tr> + <tr> <td><b>Amount Due:</b></td> <td></td> + {% if object.paid %} + <td><b>0</b></td> + {% else %} <td><b>{{object.total}}</b></td> + {% endif %} </tr> </tbody> </table> diff --git a/app/dispatch/templates/dispatch/invoice/detail.html b/app/dispatch/templates/dispatch/invoice/detail.html index 033747c..850c427 100644 --- a/app/dispatch/templates/dispatch/invoice/detail.html +++ b/app/dispatch/templates/dispatch/invoice/detail.html @@ -13,7 +13,8 @@ <!-- <input type="submit" class="btn red" value="Delete" /> --> {% csrf_token %} <a class="btn red" href="#" onClick="warn_submit('Are you sure?', '#invoice_delete')">Delete</a> - <a class="btn green" href="#" onClick="window.print()">Print</a> + <a class="btn orange darken-3" href="{% url 'invoice_edit' object.pk %}">Edit</a> + <a class="btn green" href="#" onClick="window.print()">Print</a> </form> </div> </div> @@ -21,6 +22,8 @@ <div style="padding-top:30px;" class="hide-print"></div> +<div style="padding-top:30px;" class="hide-print"></div> + <div class="z-depth-3 padding-30"> {% include "dispatch/invoice/detail-table.html" %} </div> @@ -33,7 +36,8 @@ <!-- <input type="submit" class="btn red" value="Delete" /> --> {% csrf_token %} <a class="btn red" href="#" onClick="warn_submit('Are you sure?', '#invoice_delete')">Delete</a> - <a class="btn green" href="#" onClick="window.print()">Print</a> + <a class="btn orange darken-3" href="{% url 'invoice_edit' object.pk %}">Edit</a> + <a class="btn green" href="#" onClick="window.print()">Print</a> </form> </div> </div> diff --git a/app/dispatch/templates/dispatch/invoice/edit.html b/app/dispatch/templates/dispatch/invoice/edit.html new file mode 100644 index 0000000..c056ed9 --- /dev/null +++ b/app/dispatch/templates/dispatch/invoice/edit.html @@ -0,0 +1,21 @@ +{% extends 'dispatch/base.html' %} + +{% block title %}Edit - {{ object.description }}{% endblock %} + +{% block content %} +<div class="row"> + <div class="col s12 m6"> + <h1>{{object.name}}</h1> + </div> +</div> + +<form action="" method="post">{% csrf_token %} + {% for field in form %} + <p> + {{field.label_tag}} {{field}} + </p> + {% endfor %} + <input type="submit" class="btn blue" value="Update" /> +</form> + +{% endblock %} diff --git a/app/dispatch/templates/dispatch/loads/list.html b/app/dispatch/templates/dispatch/loads/list.html index 01dd47d..615c4e1 100644 --- a/app/dispatch/templates/dispatch/loads/list.html +++ b/app/dispatch/templates/dispatch/loads/list.html @@ -24,7 +24,7 @@ </div> {% load custom_tags %} -{% listForCommaString "Customer,Driver,Amount,Description" as load_headers %} +{% listForCommaString "Customer,Driver,Amount,Description,Attachments" as load_headers %} {% include "dispatch/generic_load_listing.html" %} <div class="row"> diff --git a/app/dispatch/urls.py b/app/dispatch/urls.py index 270c674..14f78f8 100644 --- a/app/dispatch/urls.py +++ b/app/dispatch/urls.py @@ -42,7 +42,7 @@ urlpatterns = [ url(r'^invoices/$', views.InvoiceList.as_view(), name='invoice_list'), url(r'^invoices/view/(?P<pk>\d+)$', views.InvoiceDetail.as_view(), name='invoice_detail'), - # url(r'^invoices/edit/(?P<pk>\d+)$', views.InvoiceEdit.as_view(), name='invoice_edit'), + url(r'^invoices/edit/(?P<pk>\d+)$', views.InvoiceEdit.as_view(), name='invoice_edit'), url(r'^invoices/delete/(?P<pk>\d+)$', views.InvoiceDelete.as_view(), name='invoice_delete'), url(r'^drivers/view/(?P<pk>\d+)/generate/$', views.InvoiceGenerateForDates, name='invoice_generate'), diff --git a/app/dispatch/views.py b/app/dispatch/views.py index c80607e..b729922 100644 --- a/app/dispatch/views.py +++ b/app/dispatch/views.py @@ -13,7 +13,7 @@ from django.views.generic.detail import DetailView from django.views.generic.edit import CreateView, UpdateView, DeleteView from django.core.urlresolvers import reverse_lazy from dispatch.models import Customer, Load, Paperwork, \ - Invoice, UserInvoiceNumber, Identity + Invoice, UserInvoiceNumber, Identity, Settings from dispatch.forms import AddPaperworkForm from django.contrib.auth import get_user_model from django.contrib.auth.mixins import UserPassesTestMixin @@ -112,6 +112,23 @@ class DriverDetail(UserPassesTestMixin, DetailView): uinv.save() context['invoice_number'] = uinv + # A stupid hack to set a default Bill to Identity on first super user + # login + try: + context['default_bill_to'] = Identity.objects.get(pk=\ + Settings.objects.get(key='default_bill_to').value) + except: + if self.request.user.is_superuser: + try: + print("No default Identity Set") + bill_to = self.request.user.identity + default = Settings(key='default_bill_to', value=bill_to.pk) + default.save() + except Exception as e: + print(e) + print("No identity, you should be redirected") + context['default_bill_to'] = None + return context @@ -130,7 +147,7 @@ class DriverSummary(UserPassesTestMixin, LoadDateSort): stats = {} stats['count'], stats['average'], stats['sum'] = (0, 0, 0) - stats['incomplete_loads'] = 0 + context['can_invoice'] = True loads_by_date = context['loads'] for d in loads_by_date: @@ -138,8 +155,12 @@ class DriverSummary(UserPassesTestMixin, LoadDateSort): for l in loads_by_date[d]: stats['count'] += 1 stats['sum'] += l.amount - if l.amount == 0: - stats['incomplete_loads'] += 1 + + # Any load not up to par will break the chain + print(l) + print(l.can_invoice()) + if not l.can_invoice(): + context['can_invoice'] = False if stats['sum'] is not 0 and stats['count'] is not 0: stats['average'] = stats['sum']/stats['count'] @@ -295,7 +316,7 @@ class LoadDetail(DetailView): return context -class LoadUpdate(FilteredUpdateView): +class LoadUpdate(UserPassesTestMixin, FilteredUpdateView): template_name = "dispatch/loads/edit.html" model = Load fields = [] @@ -325,6 +346,10 @@ class LoadUpdate(FilteredUpdateView): load.user = self.request.user return super(LoadUpdate, self).form_valid(form) + def test_func(self): + return self.request.user.is_superuser or \ + self.get_object().user.pk == self.request.user.pk + class LoadDelete(UserPassesTestMixin, FilteredDeleteView): template_name = "dispatch/loads/delete.html" @@ -502,6 +527,35 @@ class IdentityUpdate(UserPassesTestMixin, UpdateView): # Invoice +class InvoiceEdit(UserPassesTestMixin, FilteredUpdateView): + template_name = "dispatch/invoice/edit.html" + model = Invoice + fields = [] + + default_fields = [] + superuser_fields = ['user', 'owner', 'bill_to', 'invoice_id', + 'invoice_date', 'due_date', 'paid', + 'payment_identifer'] + + def set_fields(self, user): + if user.is_superuser: + self.fields = self.superuser_fields + else: + self.fields = self.default_fields + + def post(self, request, pk): + self.set_fields(request.user) + return super(InvoiceEdit, self).post(request) + + def get(self, request, pk): + self.set_fields(request.user) + return super(InvoiceEdit, self).get(request) + + def test_func(self): + return self.request.user.is_superuser # or \ + # self.get_object().user.pk == self.request.user.pk + + class InvoiceList(FilteredListView): template_name = "dispatch/invoice/list.html" model = Invoice diff --git a/app/dispatchAuth/migrations/0001_initial.py b/app/dispatchAuth/migrations/0001_initial.py index 4a0f37c..f91594a 100644 --- a/app/dispatchAuth/migrations/0001_initial.py +++ b/app/dispatchAuth/migrations/0001_initial.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.11.5 on 2017-11-02 19:51 +# Generated by Django 1.11.5 on 2017-11-07 19:12 from __future__ import unicode_literals from django.db import migrations, models @@ -17,9 +17,9 @@ class Migration(migrations.Migration): migrations.CreateModel( name='User', fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('password', models.CharField(max_length=128, verbose_name='password')), ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), + ('id', models.AutoField(primary_key=True, serialize=False)), ('email', models.EmailField(max_length=256, unique=True)), ('first_name', models.CharField(max_length=256)), ('last_name', models.CharField(max_length=256)), diff --git a/app/dispatchAuth/migrations/0002_auto_20171106_2233.py b/app/dispatchAuth/migrations/0002_auto_20171106_2233.py deleted file mode 100644 index fa2f934..0000000 --- a/app/dispatchAuth/migrations/0002_auto_20171106_2233.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.5 on 2017-11-06 22:33 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('dispatchAuth', '0001_initial'), - ] - - operations = [ - migrations.AlterField( - model_name='user', - name='id', - field=models.AutoField(primary_key=True, serialize=False), - ), - ] |
