mirror of
https://github.com/nottinghamtec/PyRIGS.git
synced 2026-02-09 00:09:44 +00:00
Compare commits
16 Commits
dependabot
...
e32cc30141
| Author | SHA1 | Date | |
|---|---|---|---|
| e32cc30141 | |||
| 21dea6cc6a | |||
| 075c6f9365 | |||
|
|
e5c7e24941 | ||
|
|
55f45963a0 | ||
|
|
54e0c9ff9d | ||
|
|
6bb69ec8a1 | ||
|
|
c18d7a75b8 | ||
|
|
2d83dad44c | ||
| be7b595edb | |||
| 6d53df0c8b | |||
| 732a6e5c1e | |||
|
|
c6823bb9ac | ||
|
|
ec000beee8 | ||
|
|
6c8eb380fd | ||
|
3123d3899c
|
5
.github/workflows/django.yml
vendored
5
.github/workflows/django.yml
vendored
@@ -15,10 +15,13 @@ jobs:
|
||||
PYTHONDONTWRITEBYTECODE: 1
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install build dependencies
|
||||
run: |
|
||||
sudo apt-get install libcairo2-dev
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.9
|
||||
python-version: "3.10"
|
||||
cache: 'pipenv'
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
|
||||
4
Pipfile
4
Pipfile
@@ -47,7 +47,7 @@ python-dateutil = "~=2.8.1"
|
||||
pytoml = "~=0.1.21"
|
||||
pytz = "~=2020.5"
|
||||
reportlab = "*"
|
||||
requests = "~=2.32.0"
|
||||
requests = "~=2.32.3"
|
||||
retrying = "~=1.3.3"
|
||||
simplejson = "~=3.17.2"
|
||||
six = "~=1.15.0"
|
||||
@@ -60,7 +60,7 @@ tornado = "~=6.3"
|
||||
urllib3 = "~=1.26.19"
|
||||
whitenoise = "~=5.2.0"
|
||||
yolk = "~=0.4.3"
|
||||
zipp = "~=3.19.1"
|
||||
zipp = "~=3.4.0"
|
||||
"zope.component" = "~=4.6.2"
|
||||
"zope.deferredimport" = "~=4.3.1"
|
||||
"zope.deprecation" = "~=4.4.0"
|
||||
|
||||
1473
Pipfile.lock
generated
1473
Pipfile.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -224,6 +224,8 @@ USE_L10N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
USE_THOUSAND_SEPARATOR = True
|
||||
|
||||
# Need to allow seconds as datetime-local input type spits out a time that has seconds
|
||||
DATETIME_INPUT_FORMATS = ('%Y-%m-%dT%H:%M', '%Y-%m-%dT%H:%M:%S')
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import simplejson
|
||||
from django import forms
|
||||
from django.conf import settings
|
||||
from django.core import serializers
|
||||
from django.utils import timezone
|
||||
from django.utils.html import format_html
|
||||
from reversion import revisions as reversion
|
||||
|
||||
from RIGS import models
|
||||
@@ -97,6 +98,9 @@ class EventForm(forms.ModelForm):
|
||||
raise forms.ValidationError(
|
||||
'You haven\'t provided any client contact details. Please add a person or organisation.',
|
||||
code='contact')
|
||||
access = self.cleaned_data.get("access_at")
|
||||
if 'warn-access' not in self.data and access is not None and access.date() < (self.cleaned_data.get("start_date") - timedelta(days=7)):
|
||||
raise forms.ValidationError(format_html("Are you sure about that? Your access time seems a bit optimistic. If you're sure, save again. <input type='hidden' id='warn-access' name='warn-access' value='0'/>"), code='access_sanity')
|
||||
return super().clean()
|
||||
|
||||
def save(self, commit=True):
|
||||
|
||||
@@ -254,7 +254,7 @@ class Command(BaseCommand):
|
||||
new_invoice.void = True
|
||||
elif random.randint(0, 2) > 1: # 1 in 3 have been paid
|
||||
models.Payment.objects.create(invoice=new_invoice, amount=new_invoice.balance,
|
||||
date=datetime.date.today())
|
||||
date=datetime.date.today(), method=random.choice(models.Payment.METHODS)[0])
|
||||
if i == 1 or random.randint(0, 5) > 0: # Event 1 and 1 in 5 have a RA
|
||||
models.RiskAssessment.objects.create(event=new_event, supervisor_consulted=bool(random.getrandbits(1)),
|
||||
nonstandard_equipment=bool(random.getrandbits(1)),
|
||||
|
||||
@@ -943,6 +943,10 @@ class PowerTestRecord(ReviewableModel, RevisionMixin):
|
||||
def activity_feed_string(self):
|
||||
return str(self.event)
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return f"Power Test Record - {self.event}"
|
||||
|
||||
|
||||
class EventCheckIn(models.Model):
|
||||
event = models.ForeignKey('Event', related_name='crew', on_delete=models.CASCADE)
|
||||
|
||||
@@ -22,6 +22,9 @@
|
||||
<paraStyle name="center" alignment="center"/>
|
||||
<paraStyle name="page-head" alignment="center" fontName="OpenSans-Bold" fontSize="16" leading="18" spaceAfter="0"/>
|
||||
|
||||
{% block extrastyles %}
|
||||
{% endblock %}
|
||||
|
||||
<paraStyle name="style.event_description" fontName="OpenSans" textColor="DarkGray" />
|
||||
<paraStyle name="style.item_description" fontName="OpenSans" textColor="DarkGray" leftIndent="10" />
|
||||
<paraStyle name="style.specific_description" fontName="OpenSans" textColor="DarkGray" fontSize="10" />
|
||||
@@ -137,6 +140,7 @@
|
||||
<nextFrame/>
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
<namedString id="lastPage"><pageNumber/></namedString>
|
||||
</story>
|
||||
|
||||
</document>
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
Invoices <span class="badge {% if todo == 0 %}badge-success{% else %}badge-danger{% endif %} badge-pill">{{ todo }}</span>
|
||||
</a>
|
||||
<div class="dropdown-menu" aria-labelledby="navbarDropdownInvoices">
|
||||
<a class="dropdown-item" href="{% url 'invoice_dashboard' %}"><span class="fas fa-chart-line"></span> Dashboard</a>
|
||||
{% if perms.RIGS.add_invoice %}
|
||||
<a class="dropdown-item text-nowrap" href="{% url 'invoice_waiting' %}"><span class="fas fa-briefcase text-danger"></span> Waiting <span class="badge {% if waiting == 0 %}badge-success{% else %}badge-danger{% endif %} badge-pill">{{ waiting }}</span></a>
|
||||
{% endif %}
|
||||
|
||||
@@ -165,6 +165,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 text-right">
|
||||
{% button 'print' 'pt_print' object.pk %}
|
||||
{% button 'edit' url='pt_edit' pk=object.pk %}
|
||||
{% button 'view' url='event_detail' pk=object.event.pk text="Event" %}
|
||||
{% include 'partials/review_status.html' with perm=perms.RIGS.review_power review='pt_review' %}
|
||||
|
||||
170
RIGS/templates/hs/power_print.xml
Normal file
170
RIGS/templates/hs/power_print.xml
Normal file
@@ -0,0 +1,170 @@
|
||||
{% extends 'base_print.xml' %}
|
||||
{% load filters %}
|
||||
|
||||
{% block extrastyles %}
|
||||
<paraStyle name="style.powerReviewed" alignment="center" backColor="green" textColor="white"/>
|
||||
<paraStyle name="style.powerUnreviewed" alignment="center" backColor="red" textColor="white"/>
|
||||
|
||||
<blockTableStyle id="powerTable">
|
||||
<blockValign value="middle"/>
|
||||
<lineStyle kind="LINEABOVE" colorName="black" thickness="1"/>
|
||||
<lineStyle kind="LINEBELOW" colorName="black" thickness="1"/>
|
||||
<lineStyle kind="LINEAFTER" colorName="black" thickness="1"/>
|
||||
<lineStyle kind="LINEBEFORE" colorName="black" thickness="1"/>
|
||||
</blockTableStyle>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<spacer length="15"/>
|
||||
<h1>Power Test Record for <strong>{{ object.event }}</strong></h1>
|
||||
<spacer length="15"/>
|
||||
<h2>Client: {{ object.event.person|default:object.event.organisation }} | Venue: {{ object.event.venue }} | MIC: {{ object.event.mic }}</h2>
|
||||
<spacer length="15"/>
|
||||
<hr/>
|
||||
<spacer length="15"/>
|
||||
{% if object.reviewed_by %}
|
||||
<para style="style.powerReviewed"><strong>Reviewed by: {{ object.reviewed_by }} at {{ object.reviewed_at|date:"D d/m/Y" }}</strong></para>
|
||||
{% else %}
|
||||
<para style="style.powerUnreviewed"><strong>Power test results not yet reviewed</strong></para>
|
||||
{% endif %}
|
||||
<spacer length="15"/>
|
||||
<hr/>
|
||||
<spacer length="15"/>
|
||||
|
||||
<h2 fontSize="16">Power Plan Information</h2>
|
||||
<spacer length="15"/>
|
||||
|
||||
<blockTable colWidths="250,250">
|
||||
<tr>
|
||||
<td><para><strong>Power MIC:</strong> {{ object.power_mic }}</para></td>
|
||||
<td><para><strong>Venue:</strong> {{ object.event.venue }}</para></td>
|
||||
|
||||
</tr>
|
||||
<tr>
|
||||
<td><para><strong>Event Date:</strong> {{ object.event.start_date |date:"D d/m/Y" }}</para></td>
|
||||
<td><para><strong>Generators:</strong> {{ object.event.riskassessment.generators|yesno|capfirst }}</para></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><para><strong>Power Test taken at:</strong> {{ object.date_created|date:"D d/m/Y H:i" }}</para></td>
|
||||
<td><para><strong>Other Companies Power:</strong> {{ object.event.riskassessment.other_companies_power|yesno|capfirst }}</para></td>
|
||||
</tr>
|
||||
</blockTable>
|
||||
|
||||
<spacer length="15"/>
|
||||
<hr/>
|
||||
<spacer length="15"/>
|
||||
|
||||
<condPageBreak height="10in"/>
|
||||
|
||||
<h2 fontSize="16">Power Test Results</h2>
|
||||
<spacer length="15"/>
|
||||
|
||||
<para><strong>Source RCD protected?</strong> {{ object.source_rcd|yesno|capfirst }}</para>
|
||||
<para><sub>(If cable is more than 3 metres long)</sub></para>
|
||||
|
||||
<spacer length="5"/>
|
||||
|
||||
<para><strong>Appropriate and clear labelling on distribution and cabling?</strong> {{ object.labelling|yesno|capfirst }}</para>
|
||||
|
||||
<spacer length="5"/>
|
||||
|
||||
<para><strong>Equipment appropriately earthed?</strong> {{ object.source_rcd|yesno|capfirst }}</para>
|
||||
<para><sub>(truss, stage, generators etc.)</sub></para>
|
||||
|
||||
<spacer length="5"/>
|
||||
|
||||
<para><strong>All equipment in PAT period?</strong> {{ object.pat|yesno|capfirst }}</para>
|
||||
|
||||
<spacer length="15"/>
|
||||
|
||||
<h2 fontSize="14">Tests at first distro</h2>
|
||||
<spacer length="5"/>
|
||||
|
||||
<blockTable colWidths="100,410">
|
||||
<tr>
|
||||
<td><para><strong>Voltage<br/><sub>(cube meter) / V</sub></strong></para></td>
|
||||
<td>
|
||||
<blockTable colWidths="100,100,100" style="powerTable">
|
||||
<tr>
|
||||
<td><para><strong>L1 - N</strong></para></td>
|
||||
<td><para><strong>L2 - N</strong></para></td>
|
||||
<td><para><strong>L3 - N</strong></para></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ object.fd_voltage_l1}}</td>
|
||||
<td>{{ object.fd_voltage_l2}}</td>
|
||||
<td>{{ object.fd_voltage_l3}}</td>
|
||||
</tr>
|
||||
</blockTable>
|
||||
</td>
|
||||
</tr>
|
||||
</blockTable>
|
||||
|
||||
<spacer length="15"/>
|
||||
|
||||
<blockTable colWidths="100,100,190,120">
|
||||
<tr>
|
||||
<td><para><strong>Phase Rotation<br/><sub>(if required)</sub></strong></para></td>
|
||||
<td><para>{{ object.fd_phase_rotation|yesno|capfirst }}</para></td>
|
||||
<td><para><strong>Earth Fault Loop Impedance (Z<sub>s</sub>) / Ω</strong></para></td>
|
||||
<td><para>{{ object.fd_earth_fault }}</para></td>
|
||||
</tr>
|
||||
</blockTable>
|
||||
|
||||
<spacer length="15"/>
|
||||
|
||||
<para><strong>Prospective Short Circuit Current / A</strong> {{ object.fd_pssc }}</para>
|
||||
|
||||
<spacer length="15"/>
|
||||
|
||||
<h2 fontSize="14">Tests 'Worst Case' points (at least 1 required)</h2>
|
||||
<spacer length="15"/>
|
||||
|
||||
<blockTable colWidths="100,100,190,120" style="powerTable">
|
||||
<tr>
|
||||
<td><para><strong>Description</strong></para></td>
|
||||
<td><para><strong>Polarity checked?</strong></para></td>
|
||||
<td><para><strong>Voltage / V</strong></para></td>
|
||||
<td><para><strong>Earth Fault Loop Impedance (Z<sub>s</sub>) / Ω</strong></para></td>
|
||||
</tr>
|
||||
{% if object.w1_description %}
|
||||
<tr>
|
||||
<td><para><strong>{{ object.w1_description }}</strong></para></td>
|
||||
<td><para>{{ object.w1_polarity|yesno|capfirst }}</para></td>
|
||||
<td><para>{{ object.w1_voltage }} V</para></td>
|
||||
<td><para>{{ object.w1_earth_fault }}</para></td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% if object.w2_description %}
|
||||
<tr>
|
||||
<td><para><strong>{{ object.w2_description }}</strong></para></td>
|
||||
<td><para>{{ object.w2_polarity|yesno|capfirst }}</para></td>
|
||||
<td><para>{{ object.w2_voltage }} V</para></td>
|
||||
<td><para>{{ object.w2_earth_fault }}</para></td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% if object.w3_description %}
|
||||
<tr>
|
||||
<td><para><strong>{{ object.w3_description }}</strong></para></td>
|
||||
<td><para>{{ object.w3_polarity|yesno|capfirst }}</para></td>
|
||||
<td><para>{{ object.w3_voltage }} V</para></td>
|
||||
<td><para>{{ object.w3_earth_fault }}</para></td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</blockTable>
|
||||
|
||||
<spacer length="15"/>
|
||||
<h2 fontSize="14">Generic Tests</h2>
|
||||
<spacer length="15"/>
|
||||
|
||||
<blockTable colWidths="250,270" style="powerTable">
|
||||
<tr>
|
||||
<td><para><strong>All circuit RCDs tested?</strong><br/>(using test button)</para></td>
|
||||
<td><para>{{ object.all_rcds_tested|yesno|capfirst }}</para></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><para><strong>Public/performer accessible circuits tested?</strong><br/>(using socket tester)</para></td>
|
||||
<td><para>{{ object.public_sockets_tested|yesno|capfirst }}</para></td>
|
||||
</tr>
|
||||
</blockTable>
|
||||
{% endblock %}
|
||||
105
RIGS/templates/invoice_dashboard.html
Normal file
105
RIGS/templates/invoice_dashboard.html
Normal file
@@ -0,0 +1,105 @@
|
||||
{% extends 'base_rigs.html' %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<form method="GET" action="{% url 'invoice_dashboard' %}">
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-4">
|
||||
<label for="time_filter">Time Filter</label>
|
||||
<select id="time_filter" name="time_filter" class="form-control">
|
||||
<option value="week" {% if time_filter == 'week' %}selected{% endif %}>Last Week (7 days)</option>
|
||||
<option value="month" {% if time_filter == 'month' %}selected{% endif %}>Last Month (30 days)</option>
|
||||
<option value="year" {% if time_filter == 'year' %}selected{% endif %}>Last Year</option>
|
||||
<option value="all" {% if time_filter == 'all' %}selected{% endif %}>All Time</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
$('#time_filter').change(function () {
|
||||
$(this).closest('form').submit();
|
||||
});
|
||||
</script>
|
||||
|
||||
<h3>Overview</h3>
|
||||
|
||||
<!-- big cards in 2x2 grid with total_outstanding, total_events, total_invoices and total_payments, different backgrounds -->
|
||||
|
||||
<div class="card-deck">
|
||||
<div class="card">
|
||||
<a href="{% url 'invoice_waiting' %}" class="text-decoration-none text-white">
|
||||
<div class="card-body bg-primary">
|
||||
<h5 class="card-title text-center">Total Waiting</h5>
|
||||
<p class="card-text text-center h3"><strong>£{{ total_waiting|floatformat:2 }}</strong></p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="card">
|
||||
<a href="{% url 'invoice_list' %}" class="text-decoration-none text-dark">
|
||||
<div class="card-body bg-info">
|
||||
<h5 class="card-title text-center">Total Outstanding</h5>
|
||||
<p class="card-text text-center h3"><strong>£{{ total_outstanding|floatformat:2 }}</strong></p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-body bg-danger">
|
||||
<h5 class="card-title text-center">Total Events</h5>
|
||||
<p class="card-text text-center h3"><strong>{{ total_events }}</strong></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-body bg-success">
|
||||
<h5 class="card-title text-center">Total Invoices</h5>
|
||||
<p class="card-text text-center h3"><strong>{{ total_invoices }}</strong></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
<h3>Payments</h3>
|
||||
|
||||
<br/>
|
||||
|
||||
<h4>Sources</h4>
|
||||
|
||||
<br/>
|
||||
|
||||
{% for source in payment_methods %}
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title"><strong>{{ source.method }}</strong></h5>
|
||||
<p class="card-text h3">£{{ source.total|floatformat:2 }}</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
<br/>
|
||||
|
||||
<h4>Total</h4>
|
||||
|
||||
<br/>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title text-center">Total Income</h5>
|
||||
<p class="card-text text-center h3"><strong>£{{ total_income|floatformat:2 }}</strong></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
|
||||
<h4>Invoice Payment Time</h4>
|
||||
|
||||
<br/>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title text-center">Average Time to Pay</h5>
|
||||
<p class="card-text text-center h3"><strong>{{ mean_invoice_to_payment|floatformat:2 }} days</strong></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@@ -31,7 +31,7 @@
|
||||
{% for event in object_list %}
|
||||
<tr class="{{event.status_color}}">
|
||||
<th scope="row"><a href="{% url 'event_detail' event.pk %}">{{ event.display_id }}</a><br>
|
||||
<span class="text-muted">{{ event.get_status_display }}</span></th>
|
||||
<span class="{% if event.get_status_display == 'Cancelled' %}text-danger{% endif %}">{{ event.get_status_display }}</span></th>
|
||||
<td>{{ event.start_date }}</td>
|
||||
<td>
|
||||
{{ event.name }}
|
||||
|
||||
@@ -127,7 +127,7 @@ class TestEventCreate(BaseRigboardTest):
|
||||
|
||||
# Fix it
|
||||
self.page.end_date = datetime.date(2020, 1, 11)
|
||||
self.page.access_at = datetime.datetime(2020, 1, 1, 9)
|
||||
self.page.access_at = datetime.datetime(2020, 1, 8, 9)
|
||||
self.page.dry_hire = True
|
||||
self.page.status = "Booked"
|
||||
self.page.collected_by = "Fred"
|
||||
|
||||
@@ -100,6 +100,7 @@ urlpatterns = [
|
||||
name='pt_edit'),
|
||||
path('event/power/<int:pk>/review/', permission_required_with_403('RIGS.review_power')(views.MarkReviewed.as_view()),
|
||||
name='pt_review', kwargs={'model': 'PowerTestRecord'}),
|
||||
path('event/power/<int:pk>/print/', permission_required_with_403('RIGS.view_powertestrecord')(views.PowerPrint.as_view()), name='pt_print'),
|
||||
|
||||
path('event/<int:pk>/checkin/', login_required(views.EventCheckIn.as_view()),
|
||||
name='event_checkin'),
|
||||
@@ -114,7 +115,8 @@ urlpatterns = [
|
||||
path('event/webhook/', views.RecieveForumWebhook.as_view(), name='webhook_recieve'),
|
||||
|
||||
# Finance
|
||||
path('invoice/', permission_required_with_403('RIGS.view_invoice')(views.InvoiceIndex.as_view()),
|
||||
path('invoice/', permission_required_with_403('RIGS.view_invoice')(views.InvoiceDashboard.as_view()), name='invoice_dashboard'),
|
||||
path('invoice/outstanding', permission_required_with_403('RIGS.view_invoice')(views.InvoiceOutstanding.as_view()),
|
||||
name='invoice_list'),
|
||||
path('invoice/archive/', permission_required_with_403('RIGS.view_invoice')(views.InvoiceArchive.as_view()),
|
||||
name='invoice_archive'),
|
||||
|
||||
@@ -5,7 +5,7 @@ import reversion
|
||||
from django import forms
|
||||
from django.contrib import messages
|
||||
from django.db import transaction
|
||||
from django.db.models import Q
|
||||
from django.db.models import Sum
|
||||
from django.http import Http404, HttpResponseRedirect
|
||||
from django.http import HttpResponse
|
||||
from django.shortcuts import get_object_or_404
|
||||
@@ -18,8 +18,76 @@ from RIGS import models
|
||||
|
||||
forms.DateField.widget = forms.DateInput(attrs={'type': 'date'})
|
||||
|
||||
TIME_FILTERS = ["all", "year", "month", "week"]
|
||||
|
||||
class InvoiceIndex(generic.ListView):
|
||||
|
||||
def days_between(d1, d2):
|
||||
diff = d2 - d1
|
||||
return diff.total_seconds() / datetime.timedelta(days=1).total_seconds()
|
||||
|
||||
|
||||
class InvoiceDashboard(generic.TemplateView):
|
||||
template_name = 'invoice_dashboard.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['page_title'] = "Invoice Dashboard"
|
||||
context['description'] = "Overview of financial status of TEC rigs."
|
||||
|
||||
time_filter = self.request.GET.get('time_filter', 'all')
|
||||
|
||||
if time_filter not in TIME_FILTERS:
|
||||
time_filter = 'all'
|
||||
|
||||
if time_filter == 'all':
|
||||
context['events'] = models.Event.objects.all()
|
||||
context['invoices'] = models.Invoice.objects.all()
|
||||
context['payments'] = models.Payment.objects.all()
|
||||
elif time_filter == 'year':
|
||||
context['events'] = models.Event.objects.filter(start_date__gte=datetime.date.today() - datetime.timedelta(days=365))
|
||||
context['invoices'] = models.Invoice.objects.filter(invoice_date__gte=datetime.date.today() - datetime.timedelta(days=365))
|
||||
context['payments'] = models.Payment.objects.filter(date__gte=datetime.date.today() - datetime.timedelta(days=365))
|
||||
elif time_filter == 'month':
|
||||
context['events'] = models.Event.objects.filter(start_date__gte=datetime.date.today() - datetime.timedelta(days=30))
|
||||
context['invoices'] = models.Invoice.objects.filter(invoice_date__gte=datetime.date.today() - datetime.timedelta(days=30))
|
||||
context['payments'] = models.Payment.objects.filter(date__gte=datetime.date.today() - datetime.timedelta(days=30))
|
||||
elif time_filter == 'week':
|
||||
context['events'] = models.Event.objects.filter(start_date__gte=datetime.date.today() - datetime.timedelta(days=7))
|
||||
context['invoices'] = models.Invoice.objects.filter(invoice_date__gte=datetime.date.today() - datetime.timedelta(days=7))
|
||||
context['payments'] = models.Payment.objects.filter(date__gte=datetime.date.today() - datetime.timedelta(days=7))
|
||||
|
||||
context["time_filter"] = time_filter
|
||||
|
||||
context['total_outstanding'] = sum([i.balance for i in models.Invoice.objects.outstanding_invoices()])
|
||||
context['total_waiting'] = sum([i.sum_total for i in models.Event.objects.waiting_invoices()])
|
||||
context['total_events'] = len(context['events'])
|
||||
context['total_invoices'] = len(context['invoices'])
|
||||
context['total_payments'] = len(context['payments'])
|
||||
|
||||
payment_methods = dict(models.Payment.METHODS)
|
||||
|
||||
context['payment_methods'] = context["payments"].values('method').annotate(total=Sum('amount')).order_by('method')
|
||||
|
||||
for method in context['payment_methods']:
|
||||
method['method'] = payment_methods.get(method['method'], f"Unknown method ({method['method']})")
|
||||
|
||||
context["total_income"] = sum([i['total'] for i in context['payment_methods']])
|
||||
|
||||
payments = context['payments']
|
||||
mean_duration = 0
|
||||
|
||||
for payment in payments:
|
||||
mean_duration += days_between(payment.invoice.invoice_date, payment.date)
|
||||
|
||||
if len(payments) > 0:
|
||||
mean_duration /= len(payments)
|
||||
|
||||
context['mean_invoice_to_payment'] = mean_duration
|
||||
|
||||
return context
|
||||
|
||||
|
||||
class InvoiceOutstanding(generic.ListView):
|
||||
model = models.Invoice
|
||||
template_name = 'invoice_list.html'
|
||||
|
||||
|
||||
@@ -232,6 +232,16 @@ class RAPrint(PrintView):
|
||||
return context
|
||||
|
||||
|
||||
class PowerPrint(PrintView):
|
||||
model = models.PowerTestRecord
|
||||
template_name = 'hs/power_print.xml'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['filename'] = f"PowerTestRecord_for_{context['object'].event.display_id}.pdf"
|
||||
return context
|
||||
|
||||
|
||||
class EventCheckIn(generic.CreateView, ModalURLMixin):
|
||||
model = models.EventCheckIn
|
||||
template_name = 'hs/eventcheckin_form.html'
|
||||
|
||||
@@ -1,16 +1,11 @@
|
||||
function changeSelectedValue(obj,pk,text,update_url) { //Pass in JQuery object and new parameters
|
||||
//console.log('Changing selected value');
|
||||
obj.find('option').remove(); //Remove all the available options
|
||||
obj.append( //Add the new option
|
||||
$("<option></option>")
|
||||
.attr("value",pk)
|
||||
.text(text)
|
||||
.data('update_url',update_url)
|
||||
);
|
||||
obj.selectpicker('render'); //Re-render the UI
|
||||
obj.selectpicker('refresh'); //Re-render the UI
|
||||
obj.selectpicker('val', pk); //Set the new value to be selected
|
||||
obj[0].add(new Option(text, pk, true, true)); // Add new option
|
||||
//obj.selectpicker('val', pk); //Set the new value to be selected
|
||||
obj.selectpicker('refresh');
|
||||
obj.change(); //Trigger the change function manually
|
||||
//console.log(obj);
|
||||
}
|
||||
|
||||
function refreshUpdateHref(obj) {
|
||||
|
||||
@@ -9,3 +9,4 @@ $theme-colors: (
|
||||
"primary": #3A52A2
|
||||
) !default;
|
||||
$enable-shadows: true;
|
||||
$alert-color-level: 10;
|
||||
|
||||
@@ -35,8 +35,8 @@
|
||||
{% endif %}
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-dark" role="navigation">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" style="position: absolute; left:0.5em; top: 2px;" href="{% if request.user.is_authenticated %}https://rigs.nottinghamtec.co.uk{%else%}https://nottinghamtec.co.uk{%endif%}">
|
||||
<img src="{% static 'imgs/logo.webp' %}" width="40" height="40" alt="TEC's Logo: Serif 'TEC' vertically next to a blue box with the words 'PA and Lighting', surrounded by graduated rings" id="logo">
|
||||
<a class="navbar-brand" href="{% if request.user.is_authenticated %}https://rigs.nottinghamtec.co.uk{%else%}https://nottinghamtec.co.uk{%endif%}">
|
||||
<img src="{% static 'imgs/logo.webp' %}" class="mr-auto" style="max-height: 40px; position: absolute; left: 0.5em; top: 0;" alt="TEC's Logo: Serif 'TEC' vertically next to a blue box with the words 'PA and Lighting', surrounded by graduated rings" id="logo">
|
||||
</a>
|
||||
{% block titleheader %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -9,9 +9,11 @@
|
||||
<h1 class="col-sm-12 pb-3">R<small class="text-muted">ig</small> I<small class="text-muted">nformation</small> G<small class="text-muted">athering</small> S<small class="text-muted">ystem</small></h1>
|
||||
<h2 class="col-sm-12 pb-3">Welcome back {{ user.get_full_name }}, there {%if rig_count == 1 %}is one rig coming up{%else%}are {{ rig_count|apnumber }} rigs coming up.{%endif%}</h2>
|
||||
{% if now %}
|
||||
<div class="col-sm-12 alert alert-primary rounded-0 mx-auto">
|
||||
<div class="col-sm-12">
|
||||
{% for event in now %}
|
||||
Event {{ event }} is happening today! <a href="{% url 'event_checkin' event.pk %}" class="btn btn-success btn-sm modal-href align-baseline {% if request.user.current_event %}disabled{%endif%}"><span class="fas fa-user-clock"></span> <span class="d-none d-sm-inline">Check In</span></a><br/>
|
||||
<div class="alert alert-primary rounded-0">
|
||||
Event <a href="{% url 'event_detail' event.pk %}" class="text-danger">{{ event }}</a> is happening today! <a href="{% url 'event_checkin' event.pk %}" class="btn btn-success btn-sm modal-href align-baseline {% if request.user.current_event %}disabled{%endif%}"><span class="fas fa-user-clock"></span> <span class="d-none d-sm-inline">Check In</span></a><br/>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
Reference in New Issue
Block a user