add initial POS stuff

This commit is contained in:
Thomas Steen Rasmussen 2020-08-11 02:22:36 +02:00
parent 6d97d4b603
commit 001ba2d955
17 changed files with 1201 additions and 3 deletions

View file

@ -1,3 +1,7 @@
from camps.mixins import CampViewMixin
from django.core.exceptions import PermissionDenied
from django.shortcuts import get_object_or_404
from economy.models import Pos
from utils.mixins import RaisePermissionRequiredMixin
@ -37,3 +41,27 @@ class ContentTeamPermissionMixin(RaisePermissionRequiredMixin):
"camps.backoffice_permission",
"camps.contentteam_permission",
)
class PosViewMixin(CampViewMixin):
"""A mixin to set self.pos based on pos_slug in url kwargs."""
def setup(self, *args, **kwargs):
super().setup(*args, **kwargs)
self.pos = get_object_or_404(
Pos, team__camp=self.camp, slug=self.kwargs["pos_slug"]
)
def get_permission_required(self):
"""
This view requires two permissions, camps.backoffice_permission and the permission_set for the team in question.
"""
if not self.pos.team.permission_set:
raise PermissionDenied("No permissions set defined for this team")
perms = ["camps.backoffice_permission"]
return perms
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
context["pos"] = self.pos
return context

View file

@ -0,0 +1,28 @@
<table class="table table-striped{% if not nodatatable %} datatable{% endif %}">
<thead>
<tr>
<th>Name</th>
<th>Team</th>
<th>Slug</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for pos in pos_list %}
<tr>
<td><a href="{% url 'backoffice:pos_detail' camp_slug=camp.slug pos_slug=pos.slug %}">{{ pos.name }}</a></td>
<td>{{ pos.team }}</td>
<td>{{ pos.slug }}</td>
<td>
<div class="btn-group-vertical">
<a href="{% url 'backoffice:pos_detail' camp_slug=camp.slug pos_slug=pos.slug %}" class="btn btn-primary"><i class="fas fa-search"></i> Details</a>
{% if perms.camps.orgateam_permission %}
<a href="{% url 'backoffice:pos_update' camp_slug=camp.slug pos_slug=pos.slug %}" class="btn btn-primary"><i class="fas fa-edit"></i> Update</a>
<a href="{% url 'backoffice:pos_delete' camp_slug=camp.slug pos_slug=pos.slug %}" class="btn btn-danger"><i class="fas fa-times"></i> Delete</a>
{% endif %}
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>

View file

@ -0,0 +1,34 @@
<table class="table table-striped{% if not nodatatable %} datatable{% endif %}">
<thead>
<tr>
<th>Date</th>
<th>Pos</th>
<th>Bank Responsible</th>
<th>Pos Responsible</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for pr in posreport_list %}
<tr>
<td><a href="{% url 'backoffice:posreport_detail' camp_slug=camp.slug pos_slug=pos.slug posreport_uuid=pr.uuid %}">{{ pr.date }}</a></td>
<td>{{ pr.pos.name }}</td>
<td>{{ pr.bank_responsible }}</td>
<td>{{ pr.pos_responsible }}</td>
<td>
<div class="btn-group-vertical">
<a href="{% url 'backoffice:posreport_detail' camp_slug=camp.slug pos_slug=pos.slug posreport_uuid=pr.uuid %}" class="btn btn-primary"><i class="fas fa-search"></i> Details</a>
{% if request.user == pr.pos.bank_responsible %}
<a href="{% url 'backoffice:posreport_bank_count_start' camp_slug=camp.slug pos_slug=pos.slug posreport_uuid=pr.uuid %}" class="btn btn-primary"><i class="fas fa-edit"></i> Bank Count Start</a>
<a href="{% url 'backoffice:posreport_bank_count_end' camp_slug=camp.slug pos_slug=pos.slug posreport_uuid=pr.uuid %}" class="btn btn-primary"><i class="fas fa-edit"></i> Bank Count End</a>
{% endif %}
{% if request.user == pr.pos.pos_responsible %}
<a href="{% url 'backoffice:posreport_pos_count_start' camp_slug=camp.slug pos_slug=pos.slug posreport_uuid=pr.uuid %}" class="btn btn-primary"><i class="fas fa-edit"></i> Pos Count Start</a>
<a href="{% url 'backoffice:posreport_pos_count_end' camp_slug=camp.slug pos_slug=pos.slug posreport_uuid=pr.uuid %}" class="btn btn-primary"><i class="fas fa-edit"></i> Pos Count End</a>
{% endif %}
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>

View file

@ -166,6 +166,12 @@
<h4 class="list-group-item-heading">Proxied Content</h4>
<p class="list-group-item-text">Use this view to see proxied content</p>
</a>
<h3>Point of Sale</h3>
<a href="{% url 'backoffice:pos_list' camp_slug=camp.slug %}" class="list-group-item">
<h4 class="list-group-item-heading">Point of Sale</h4>
<p class="list-group-item-text">Use this view to see a list of Pos objects, and to see and submit PosReports</p>
</a>
</div>
</div>
</div>

View file

@ -0,0 +1,18 @@
{% extends 'base.html' %}
{% load bootstrap3 %}
{% block content %}
<div class="panel panel-default">
<div class="panel-heading">
<h2 class="panel-title">Delete Pos {{ pos.name }}?</h2>
</div>
<div class="panel-body">
<p class="lead">This Pos has <b>{{ pos.pos_reports.count }}</b> PosReports which will also be deleted.</p>
<form method="POST">
{% csrf_token %}
<button type="submit" name="Delete" class="btn btn-danger"><i class="fas fa-times"></i> Yes, Delete it</button>
<a href="{% url 'backoffice:pos_detail' camp_slug=camp.slug pos_slug=pos.slug %}" class="btn btn-default"><i class="fas fa-undo"></i> Cancel</a>
</form>
</div>
</div>
{% endblock content %}

View file

@ -0,0 +1,42 @@
{% extends 'base.html' %}
{% block title %}
{{ pos.name }} | Pos | BackOffice | {{ block.super }}
{% endblock %}
{% block content %}
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{{ pos.name }} | Pos | BackOffice</h3>
</div>
<div class="panel-body">
<p>
<a href="{% url 'backoffice:pos_update' camp_slug=camp.slug pos_slug=pos.slug %}" class="btn btn-primary"><i class="fas fa-edit"></i> Update Pos</a>
<a href="{% url 'backoffice:pos_delete' camp_slug=camp.slug pos_slug=pos.slug %}" class="btn btn-danger"><i class="fas fa-times"></i> Delete Pos</a>
<a href="{% url 'backoffice:pos_list' camp_slug=camp.slug %}" class="btn btn-default"><i class="fas fa-undo"></i> Pos List</a>
</p>
<table class="table">
<tbody>
<tr>
<th>Pos Name</th>
<td>{{ pos.name }}</td>
</tr>
<tr>
<th>Team</th>
<td>{{ pos.team }}</p>
</tr>
</tbody>
</table>
<h3>Pos Reports</h3>
{% if pos.pos_reports.exists %}
{% include "includes/posreport_list_table.html" with posreport_list=pos.pos_reports.all %}
{% else %}
None found
{% endif %}
<br>
{% if perms.camps.orgateam_permission %}
<a href="{% url 'backoffice:posreport_create' camp_slug=camp.slug pos_slug=pos.slug %}" class="btn btn-success"><i class="fas fa-plus"></i> Create PosReport</a>
{% endif %}
</div>
</div>
{% endblock %}

View file

@ -0,0 +1,21 @@
{% extends 'base.html' %}
{% load bootstrap3 %}
{% load static %}
{% block content %}
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{% if request.resolver_match.url_name == "pos_update" %}Update{% else %}Create new{% endif %} Pos</h3>
</div>
<div class="panel-body">
<form method="POST">
{% csrf_token %}
{% bootstrap_form form %}
{% buttons %}
<button type="submit" class="btn btn-success"><i class="fas fa-check"></i> Save</button>
<a href="{% url 'backoffice:pos_list' camp_slug=camp.slug %}" class="btn btn-default"><i class="fas fa-undo"></i> Cancel</a>
{% endbuttons %}
</form>
</div>
</div>
{% endblock content %}

View file

@ -0,0 +1,29 @@
{% extends 'base.html' %}
{% load bornhack %}
{% block title %}
Pos List | Backoffice | {{ block.super }}
{% endblock %}
{% block content %}
<div class="panel panel-default">
<div class="panel-heading"><h3 class="panel-title">Pos List - BackOffice</h3></div>
<div class="panel-body">
<p>A <i>Pos</i> is a place where we sell stuff for DKK and/or HAX.</p>
{% if not pos_list %}
<p class="lead">No Pos found.</p>
{% else %}
<p>
<a class="btn btn-default" href="{% url 'backoffice:index' camp_slug=camp.slug %}"><i class="fas fa-undo"></i> Backoffice</a>
{% include "includes/pos_list_table.html" %}
</p>
{% endif %}
<p>
{% if perms.camps.orgateam_permission %}
<a class="btn btn-success" href="{% url 'backoffice:pos_create' camp_slug=camp.slug %}"><i class="fas fa-plus"></i> Create Pos</a>
{% endif %}
<a class="btn btn-default" href="{% url 'backoffice:index' camp_slug=camp.slug %}"><i class="fas fa-undo"></i> Backoffice</a>
</p>
</div>
</div>
{% endblock content %}

View file

@ -0,0 +1,115 @@
{% extends 'base.html' %}
{% block title %}
PosReport {{ posreport.date }} {{ posreport.pos.name }} | Pos | BackOffice | {{ block.super }}
{% endblock %}
{% block content %}
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">PosReport {{ posreport.date }} {{ posreport.pos.name }} | Pos | BackOffice</h3>
</div>
<div class="panel-body">
<p>
{% if "camps.orgateam_permission" in perms %}
<a href="{% url 'backoffice:posreport_update' camp_slug=camp.slug pos_slug=pos.slug posreport_uuid=posreport.uuid %}" class="btn btn-primary"><i class="fas fa-edit"></i> Update</a>
{% endif %}
{% if request.user == posreport.bank_responsible %}
<a href="{% url 'backoffice:posreport_bank_count_start' camp_slug=camp.slug pos_slug=pos.slug posreport_uuid=posreport.uuid %}" class="btn btn-primary"><i class="fas fa-edit"></i> Bank Count Start</a>
<a href="{% url 'backoffice:posreport_bank_count_end' camp_slug=camp.slug pos_slug=pos.slug posreport_uuid=posreport.uuid %}" class="btn btn-primary"><i class="fas fa-edit"></i> Bank Count End</a>
{% endif %}
{% if request.user == posreport.pos_responsible %}
<a href="{% url 'backoffice:posreport_pos_count_start' camp_slug=camp.slug pos_slug=pos.slug posreport_uuid=posreport.uuid %}" class="btn btn-primary"><i class="fas fa-edit"></i> Pos Count Start</a>
<a href="{% url 'backoffice:posreport_pos_count_end' camp_slug=camp.slug pos_slug=pos.slug posreport_uuid=posreport.uuid %}" class="btn btn-primary"><i class="fas fa-edit"></i> Pos Count End</a>
{% endif %}
</p>
<table class="table">
<tbody>
<tr>
<th>PosReport UUID</th>
<td>{{ posreport.uuid }}</td>
</tr>
<tr>
<th>PosReport Date</th>
<td>{{ posreport.date }}</td>
</tr>
<tr>
<th>Pos Name</th>
<td>{{ posreport.pos.name }}</td>
</tr>
<tr>
<th>Team</th>
<td>{{ posreport.pos.team }}</p>
</tr>
<tr>
<th>Bank Responsible</th>
<td>{{ posreport.bank_responsible }}</p>
</tr>
<tr>
<th>Pos Responsible</th>
<td>{{ posreport.pos_responsible }}</p>
</tr>
<tr>
<th>Counts</th>
<td>
<table class="table table-condensed">
<thead>
<tr>
<th>What</th>
<th>Bank Start</th>
<th>Bank End</th>
<th>Pos Start</th>
<th>Pos End</th>
</tr>
</thead>
<tbody>
<tr>
<td>DKK</td>
<td>{{ posreport.bank_count_dkk_start }}</td>
<td>{{ posreport.bank_count_dkk_end }}</td>
<td>{{ posreport.pos_count_dkk_start }}</td>
<td>{{ posreport.pos_count_dkk_end }}</td>
</tr>
<tr>
<td>5 HAX</td>
<td>{{ posreport.bank_count_hax5_start }}</td>
<td>{{ posreport.bank_count_hax5_end }}</td>
<td>{{ posreport.pos_count_hax5_start }}</td>
<td>{{ posreport.pos_count_hax5_end }}</td>
</tr>
<tr>
<td>10 HAX</td>
<td>{{ posreport.bank_count_hax10_start }}</td>
<td>{{ posreport.bank_count_hax10_end }}</td>
<td>{{ posreport.pos_count_hax10_start }}</td>
<td>{{ posreport.pos_count_hax10_end }}</td>
</tr>
<td>20 HAX</td>
<td>{{ posreport.bank_count_hax20_start }}</td>
<td>{{ posreport.bank_count_hax20_end }}</td>
<td>{{ posreport.pos_count_hax20_start }}</td>
<td>{{ posreport.pos_count_hax20_end }}</td>
</tr>
<tr>
<td>50 HAX</td>
<td>{{ posreport.bank_count_hax50_start }}</td>
<td>{{ posreport.bank_count_hax50_end }}</td>
<td>{{ posreport.pos_count_hax50_start }}</td>
<td>{{ posreport.pos_count_hax50_end }}</td>
</tr>
<tr>
<td>100 HAX</td>
<td>{{ posreport.bank_count_hax100_start }}</td>
<td>{{ posreport.bank_count_hax100_end }}</td>
<td>{{ posreport.pos_count_hax100_start }}</td>
<td>{{ posreport.pos_count_hax100_end }}</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</div>
</div>
{% endblock %}

View file

@ -0,0 +1,21 @@
{% extends 'base.html' %}
{% load bootstrap3 %}
{% load static %}
{% block content %}
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{% if request.resolver_match.url_name == "posreport_create" %}Create{% else %}Update{% endif %} PosReport</h3>
</div>
<div class="panel-body">
<form method="POST">
{% csrf_token %}
{% bootstrap_form form %}
{% buttons %}
<button type="submit" class="btn btn-success"><i class="fas fa-check"></i> Save</button>
<a href="{% url 'backoffice:pos_list' camp_slug=camp.slug %}" class="btn btn-default"><i class="fas fa-undo"></i> Cancel</a>
{% endbuttons %}
</form>
</div>
</div>
{% endblock content %}

View file

@ -0,0 +1,25 @@
{% extends 'base.html' %}
{% load bornhack %}
{% block title %}
PosReport List for {{ pos.name }} | Backoffice | {{ block.super }}
{% endblock %}
{% block content %}
<div class="panel panel-default">
<div class="panel-heading"><h3 class="panel-title">PosReport List for {{ pos.name }} - BackOffice</h3></div>
<div class="panel-body">
<p>A <i>PosReport</i> contains the start and end counts of HAX+DKK from a point-of-sale and the exported JSON file from the Pos.</p>
{% if not pos_list %}
<p class="lead">No Pos found.</p>
{% else %}
<p>
{% include "includes/posreport_list_table.html" %}
</p>
{% endif %}
<p>
<a class="btn btn-default" href="{% url 'backoffice:index' camp_slug=camp.slug %}"><i class="fas fa-undo"></i> Backoffice</a>
</p>
</div>
</div>
{% endblock content %}

View file

@ -60,6 +60,18 @@ from .views import (
MerchandiseToOrderView,
OutgoingEmailMassUpdateView,
PendingProposalsView,
PosCreateView,
PosDeleteView,
PosDetailView,
PosListView,
PosReportBankCountEndView,
PosReportBankCountStartView,
PosReportCreateView,
PosReportDetailView,
PosReportPosCountEndView,
PosReportPosCountStartView,
PosReportUpdateView,
PosUpdateView,
ProductHandoutView,
ReimbursementCreateUserSelectView,
ReimbursementCreateView,
@ -632,4 +644,77 @@ urlpatterns = [
OutgoingEmailMassUpdateView.as_view(),
name="outgoing_email_release",
),
# point-of-sale
path(
"pos/",
include(
[
path("", PosListView.as_view(), name="pos_list",),
path("create/", PosCreateView.as_view(), name="pos_create",),
path(
"<slug:pos_slug>/",
include(
[
path("", PosDetailView.as_view(), name="pos_detail",),
path(
"update/", PosUpdateView.as_view(), name="pos_update",
),
path(
"delete/", PosDeleteView.as_view(), name="pos_delete",
),
path(
"reports/",
include(
[
path(
"create/",
PosReportCreateView.as_view(),
name="posreport_create",
),
path(
"<uuid:posreport_uuid>/",
include(
[
path(
"",
PosReportDetailView.as_view(),
name="posreport_detail",
),
path(
"update/",
PosReportUpdateView.as_view(),
name="posreport_update",
),
path(
"bankcount/start/",
PosReportBankCountStartView.as_view(),
name="posreport_bank_count_start",
),
path(
"bankcount/end/",
PosReportBankCountEndView.as_view(),
name="posreport_bank_count_end",
),
path(
"poscount/start/",
PosReportPosCountStartView.as_view(),
name="posreport_pos_count_start",
),
path(
"poscount/end/",
PosReportPosCountEndView.as_view(),
name="posreport_pos_count_end",
),
]
),
),
]
),
),
]
),
),
],
),
),
]

View file

@ -20,7 +20,15 @@ from django.utils import timezone
from django.utils.safestring import mark_safe
from django.views.generic import DetailView, ListView, TemplateView
from django.views.generic.edit import CreateView, DeleteView, FormView, UpdateView
from economy.models import Chain, Credebtor, Expense, Reimbursement, Revenue
from economy.models import (
Chain,
Credebtor,
Expense,
Pos,
PosReport,
Reimbursement,
Revenue,
)
from facilities.models import (
Facility,
FacilityFeedback,
@ -59,6 +67,7 @@ from .mixins import (
EconomyTeamPermissionMixin,
InfoTeamPermissionMixin,
OrgaTeamPermissionMixin,
PosViewMixin,
RaisePermissionRequiredMixin,
)
@ -1955,3 +1964,219 @@ class OutgoingEmailMassUpdateView(CampViewMixin, OrgaTeamPermissionMixin, FormVi
def get_success_url(self, *args, **kwargs):
"""Return to the backoffice index."""
return reverse("backoffice:index", kwargs={"camp_slug": self.camp.slug})
################################
# Pos and PosReport views
class PosListView(CampViewMixin, RaisePermissionRequiredMixin, ListView):
"""Show a list of Pos this user has access to (through team memberships)."""
permission_required = "camps.backoffice_permission"
model = Pos
template_name = "pos_list.html"
class PosDetailView(PosViewMixin, RaisePermissionRequiredMixin, DetailView):
"""Show details for a Pos."""
model = Pos
template_name = "pos_detail.html"
slug_url_kwarg = "pos_slug"
class PosCreateView(CampViewMixin, OrgaTeamPermissionMixin, CreateView):
"""Create a new Pos (orga only)."""
model = Pos
template_name = "pos_form.html"
fields = ["name", "team"]
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["form"].fields["team"].queryset = Team.objects.filter(camp=self.camp)
return context
class PosUpdateView(CampViewMixin, OrgaTeamPermissionMixin, UpdateView):
"""Update a Pos."""
model = Pos
template_name = "pos_form.html"
slug_url_kwarg = "pos_slug"
fields = ["name", "team"]
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["form"].fields["team"].queryset = Team.objects.filter(camp=self.camp)
return context
class PosDeleteView(CampViewMixin, OrgaTeamPermissionMixin, DeleteView):
model = Pos
template_name = "pos_delete.html"
slug_url_kwarg = "pos_slug"
def delete(self, *args, **kwargs):
self.get_object().pos_reports.all().delete()
return super().delete(*args, **kwargs)
def get_success_url(self):
messages.success(
self.request, "The Pos and all related PosReports has been deleted"
)
return reverse("backoffice:pos_list", kwargs={"camp_slug": self.camp.slug})
class PosReportCreateView(PosViewMixin, RaisePermissionRequiredMixin, CreateView):
"""Use this view to create new PosReports."""
model = PosReport
fields = ["date", "bank_responsible", "pos_responsible"]
template_name = "posreport_form.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["form"].fields["bank_responsible"].queryset = Team.objects.get(
camp=self.camp, name="Orga",
).approved_members.all()
context["form"].fields[
"pos_responsible"
].queryset = self.pos.team.responsible_members.all()
return context
def form_valid(self, form):
"""
Set Pos before saving
"""
pr = form.save(commit=False)
pr.pos = self.pos
pr.save()
messages.success(self.request, f"New PosReport created successfully!")
return redirect(
reverse(
"backoffice:posreport_detail",
kwargs={
"camp_slug": self.camp.slug,
"pos_slug": self.pos.slug,
"posreport_uuid": pr.uuid,
},
)
)
class PosReportUpdateView(PosViewMixin, RaisePermissionRequiredMixin, UpdateView):
"""Use this view to update PosReports."""
model = PosReport
fields = ["date", "bank_responsible", "pos_responsible"]
template_name = "posreport_form.html"
pk_url_kwarg = "posreport_uuid"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["form"].fields["bank_responsible"].queryset = Team.objects.get(
camp=self.camp, name="Orga",
).approved_members.all()
context["form"].fields[
"pos_responsible"
].queryset = self.pos.team.responsible_members.all()
return context
class PosReportDetailView(PosViewMixin, RaisePermissionRequiredMixin, DetailView):
"""Show details for a PosReport."""
model = PosReport
template_name = "posreport_detail.html"
pk_url_kwarg = "posreport_uuid"
class PosReportBankCountStartView(
PosViewMixin, RaisePermissionRequiredMixin, UpdateView
):
"""The bank responsible for a PosReport uses this view to add day-start HAX and DKK counts to a PosReport."""
model = PosReport
template_name = "posreport_form.html"
fields = [
"bank_count_dkk_start",
"bank_count_hax5_start",
"bank_count_hax10_start",
"bank_count_hax20_start",
"bank_count_hax50_start",
"bank_count_hax100_start",
]
pk_url_kwarg = "posreport_uuid"
def setup(self, *args, **kwargs):
super().setup(*args, **kwargs)
if self.request.user != self.get_object().bank_responsible:
raise PermissionDenied("Only the bank responsible can do this")
class PosReportBankCountEndView(PosViewMixin, RaisePermissionRequiredMixin, UpdateView):
"""The bank responsible for a PosReport uses this view to add day-end HAX and DKK counts to a PosReport."""
model = PosReport
template_name = "posreport_form.html"
fields = [
"bank_count_dkk_end",
"bank_count_hax5_end",
"bank_count_hax10_end",
"bank_count_hax20_end",
"bank_count_hax50_end",
"bank_count_hax100_end",
]
pk_url_kwarg = "posreport_uuid"
def setup(self, *args, **kwargs):
super().setup(*args, **kwargs)
if self.request.user != self.get_object().bank_responsible:
raise PermissionDenied("Only the bank responsible can do this")
class PosReportPosCountStartView(
PosViewMixin, RaisePermissionRequiredMixin, UpdateView
):
"""The Pos responsible for a PosReport uses this view to add day-start HAX and DKK counts to a PosReport."""
model = PosReport
template_name = "posreport_form.html"
fields = [
"pos_count_dkk_start",
"pos_count_hax5_start",
"pos_count_hax10_start",
"pos_count_hax20_start",
"pos_count_hax50_start",
"pos_count_hax100_start",
]
pk_url_kwarg = "posreport_uuid"
def setup(self, *args, **kwargs):
super().setup(*args, **kwargs)
if self.request.user != self.get_object().pos_responsible:
raise PermissionDenied("Only the Pos responsible can do this")
class PosReportPosCountEndView(PosViewMixin, RaisePermissionRequiredMixin, UpdateView):
"""The Pos responsible for a PosReport uses this view to add day-end HAX and DKK counts to a PosReport."""
model = PosReport
template_name = "posreport_form.html"
fields = [
"pos_count_dkk_end",
"pos_count_hax5_end",
"pos_count_hax10_end",
"pos_count_hax20_end",
"pos_count_hax50_end",
"pos_count_hax100_end",
"pos_json",
]
pk_url_kwarg = "posreport_uuid"
def setup(self, *args, **kwargs):
super().setup(*args, **kwargs)
if self.request.user != self.get_object().pos_responsible:
raise PermissionDenied("Only the pos responsible can do this")

View file

@ -1,6 +1,6 @@
from django.contrib import admin
from .models import Chain, Credebtor, Expense, Reimbursement, Revenue
from .models import Chain, Credebtor, Expense, Pos, PosReport, Reimbursement, Revenue
###############################
# chains and credebtors
@ -113,3 +113,18 @@ class ReimbursementAdmin(admin.ModelAdmin):
list_filter = ["camp", "user", "reimbursement_user", "paid"]
list_display = ["camp", "user", "reimbursement_user", "paid", "notes", "get_amount"]
search_fields = ["user__username", "reimbursement_user__username", "notes"]
################################
# pos
@admin.register(Pos)
class PosAdmin(admin.ModelAdmin):
list_display = ["name", "team"]
list_filter = ["team"]
@admin.register(PosReport)
class PosReportAdmin(admin.ModelAdmin):
list_display = ["uuid", "pos"]

View file

@ -0,0 +1,284 @@
# Generated by Django 3.1 on 2020-08-10 23:41
import uuid
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
("teams", "0052_team_permission_set"),
("economy", "0010_auto_20190330_1045"),
]
operations = [
migrations.CreateModel(
name="Pos",
fields=[
(
"uuid",
models.UUIDField(
default=uuid.uuid4,
editable=False,
primary_key=True,
serialize=False,
),
),
("created", models.DateTimeField(auto_now_add=True)),
("updated", models.DateTimeField(auto_now=True)),
(
"name",
models.CharField(
help_text="The point-of-sale name", max_length=255
),
),
(
"slug",
models.SlugField(
blank=True,
help_text="Url slug for this POS. Leave blank to generate based on POS name.",
max_length=255,
),
),
(
"team",
models.ForeignKey(
help_text="The Team managning this POS",
on_delete=django.db.models.deletion.PROTECT,
to="teams.team",
),
),
],
options={"ordering": ["name"],},
),
migrations.CreateModel(
name="PosReport",
fields=[
(
"uuid",
models.UUIDField(
default=uuid.uuid4,
editable=False,
primary_key=True,
serialize=False,
),
),
("created", models.DateTimeField(auto_now_add=True)),
("updated", models.DateTimeField(auto_now=True)),
(
"date",
models.DateField(
help_text="The date this report covers (pick the starting date if opening hours cross midnight)."
),
),
(
"pos_json",
models.JSONField(
blank=True,
help_text="The JSON exported from the external POS system",
null=True,
),
),
(
"bank_count_dkk_start",
models.PositiveIntegerField(
default=0,
help_text="The number of DKK handed out from the bank to the POS at the start of the business day (counted by the bank responsible)",
),
),
(
"bank_count_hax5_start",
models.PositiveIntegerField(
default=0,
help_text="The number of 5 HAX coins handed out from the bank to the POS at the start of the business day (counted by the bank responsible)",
),
),
(
"bank_count_hax10_start",
models.PositiveIntegerField(
default=0,
help_text="The number of 10 HAX coins handed out from the bank to the POS at the start of the business day (counted by the bank responsible)",
),
),
(
"bank_count_hax20_start",
models.PositiveIntegerField(
default=0,
help_text="The number of 20 HAX coins handed out from the bank to the POS at the start of the business day (counted by the bank responsible)",
),
),
(
"bank_count_hax50_start",
models.PositiveIntegerField(
default=0,
help_text="The number of 50 HAX coins handed out from the bank to the POS at the start of the business day (counted by the bank responsible)",
),
),
(
"bank_count_hax100_start",
models.PositiveIntegerField(
default=0,
help_text="The number of 100 HAX coins handed out from the bank to the POS at the start of the business day (counted by the bank responsible)",
),
),
(
"pos_count_dkk_start",
models.PositiveIntegerField(
default=0,
help_text="The number of DKK handed out from the bank to the POS at the start of the business day (counted by the POS responsible)",
),
),
(
"pos_count_hax5_start",
models.PositiveIntegerField(
default=0,
help_text="The number of 5 HAX coins received by the POS from the bank at the start of the business day (counted by the POS responsible)",
),
),
(
"pos_count_hax10_start",
models.PositiveIntegerField(
default=0,
help_text="The number of 10 HAX coins received by the POS from the bank at the start of the business day (counted by the POS responsible)",
),
),
(
"pos_count_hax20_start",
models.PositiveIntegerField(
default=0,
help_text="The number of 20 HAX coins received by the POS from the bank at the start of the business day (counted by the POS responsible)",
),
),
(
"pos_count_hax50_start",
models.PositiveIntegerField(
default=0,
help_text="The number of 50 HAX coins received by the POS from the bank at the start of the business day (counted by the POS responsible)",
),
),
(
"pos_count_hax100_start",
models.PositiveIntegerField(
default=0,
help_text="The number of 100 HAX coins received by the POS from the bank at the start of the business day (counted by the POS responsible)",
),
),
(
"bank_count_dkk_end",
models.PositiveIntegerField(
default=0,
help_text="The number of DKK handed back from the POS to the bank at the end of the business day (counted by the bank responsible)",
),
),
(
"bank_count_hax5_end",
models.PositiveIntegerField(
default=0,
help_text="The number of 5 HAX coins handed back from the POS to the bank at the end of the business day (counted by the bank responsible)",
),
),
(
"bank_count_hax10_end",
models.PositiveIntegerField(
default=0,
help_text="The number of 10 HAX coins handed back from the POS to the bank at the end of the business day (counted by the bank responsible)",
),
),
(
"bank_count_hax20_end",
models.PositiveIntegerField(
default=0,
help_text="The number of 20 HAX coins handed back from the POS to the bank at the end of the business day (counted by the bank responsible)",
),
),
(
"bank_count_hax50_end",
models.PositiveIntegerField(
default=0,
help_text="The number of 50 HAX coins handed back from the POS to the bank at the end of the business day (counted by the bank responsible)",
),
),
(
"bank_count_hax100_end",
models.PositiveIntegerField(
default=0,
help_text="The number of 100 HAX coins handed back from the POS to the bank at the end of the business day (counted by the bank responsible)",
),
),
(
"pos_count_dkk_end",
models.PositiveIntegerField(
default=0,
help_text="The number of DKK handed back from the POS to the bank at the end of the business day (counted by the POS responsible)",
),
),
(
"pos_count_hax5_end",
models.PositiveIntegerField(
default=0,
help_text="The number of 5 HAX coins received by the bank from the POS at the end of the business day (counted by the POS responsible)",
),
),
(
"pos_count_hax10_end",
models.PositiveIntegerField(
default=0,
help_text="The number of 10 HAX coins received by the bank from the POS at the end of the business day (counted by the POS responsible)",
),
),
(
"pos_count_hax20_end",
models.PositiveIntegerField(
default=0,
help_text="The number of 20 HAX coins received by the bank from the POS at the end of the business day (counted by the POS responsible)",
),
),
(
"pos_count_hax50_end",
models.PositiveIntegerField(
default=0,
help_text="The number of 50 HAX coins received by the bank from the POS at the end of the business day (counted by the POS responsible)",
),
),
(
"pos_count_hax100_end",
models.PositiveIntegerField(
default=0,
help_text="The number of 100 HAX coins received by the bank from the POS at the end of the business day (counted by the POS responsible)",
),
),
(
"bank_responsible",
models.ForeignKey(
help_text="The banker responsible for this PosReport",
on_delete=django.db.models.deletion.PROTECT,
related_name="pos_reports_banker",
to=settings.AUTH_USER_MODEL,
),
),
(
"pos",
models.ForeignKey(
help_text="The Pos this PosReport belongs to.",
on_delete=django.db.models.deletion.PROTECT,
related_name="pos_reports",
to="economy.pos",
),
),
(
"pos_responsible",
models.ForeignKey(
help_text="The POS person responsible for this PosReport",
on_delete=django.db.models.deletion.PROTECT,
related_name="pos_reports_poser",
to=settings.AUTH_USER_MODEL,
),
),
],
options={"abstract": False,},
),
]

View file

@ -3,6 +3,7 @@ import os
from django.contrib import messages
from django.core.exceptions import ValidationError
from django.db import models
from django.urls import reverse
from utils.models import CampRelatedModel, CreatedUpdatedModel, UUIDModel
from utils.slugs import unique_slugify
@ -465,3 +466,224 @@ class Reimbursement(CampRelatedModel, UUIDModel):
for expense in self.expenses.filter(paid_by_bornhack=False):
amount += expense.amount
return amount
class Pos(CampRelatedModel, UUIDModel):
"""A Pos is a point-of-sale like the bar or infodesk."""
class Meta:
ordering = ["name"]
name = models.CharField(max_length=255, help_text="The point-of-sale name")
slug = models.SlugField(
max_length=255,
blank=True,
help_text="Url slug for this POS. Leave blank to generate based on POS name.",
)
team = models.ForeignKey(
"teams.Team", on_delete=models.PROTECT, help_text="The Team managning this POS",
)
def save(self, **kwargs):
"""Generate slug if needed."""
if not self.slug:
self.slug = unique_slugify(
self.name,
slugs_in_use=self.__class__.objects.filter(
team__camp=self.team.camp
).values_list("slug", flat=True),
)
super().save(**kwargs)
@property
def camp(self):
return self.team.camp
camp_filter = "team__camp"
def get_absolute_url(self):
return reverse(
"backoffice:pos_detail",
kwargs={"camp_slug": self.team.camp.slug, "pos_slug": self.slug},
)
class PosReport(CampRelatedModel, UUIDModel):
"""A PosReport contains the HAX/DKK counts and the csv report from the POS system."""
pos = models.ForeignKey(
"economy.Pos",
on_delete=models.PROTECT,
related_name="pos_reports",
help_text="The Pos this PosReport belongs to.",
)
bank_responsible = models.ForeignKey(
"auth.User",
on_delete=models.PROTECT,
related_name="pos_reports_banker",
help_text="The banker responsible for this PosReport",
)
pos_responsible = models.ForeignKey(
"auth.User",
on_delete=models.PROTECT,
related_name="pos_reports_poser",
help_text="The POS person responsible for this PosReport",
)
date = models.DateField(
help_text="The date this report covers (pick the starting date if opening hours cross midnight).",
)
pos_json = models.JSONField(
null=True,
blank=True,
help_text="The JSON exported from the external POS system",
)
# bank count start of day
bank_count_dkk_start = models.PositiveIntegerField(
default=0,
help_text="The number of DKK handed out from the bank to the POS at the start of the business day (counted by the bank responsible)",
)
bank_count_hax5_start = models.PositiveIntegerField(
default=0,
help_text="The number of 5 HAX coins handed out from the bank to the POS at the start of the business day (counted by the bank responsible)",
)
bank_count_hax10_start = models.PositiveIntegerField(
default=0,
help_text="The number of 10 HAX coins handed out from the bank to the POS at the start of the business day (counted by the bank responsible)",
)
bank_count_hax20_start = models.PositiveIntegerField(
default=0,
help_text="The number of 20 HAX coins handed out from the bank to the POS at the start of the business day (counted by the bank responsible)",
)
bank_count_hax50_start = models.PositiveIntegerField(
default=0,
help_text="The number of 50 HAX coins handed out from the bank to the POS at the start of the business day (counted by the bank responsible)",
)
bank_count_hax100_start = models.PositiveIntegerField(
default=0,
help_text="The number of 100 HAX coins handed out from the bank to the POS at the start of the business day (counted by the bank responsible)",
)
# POS count start of day
pos_count_dkk_start = models.PositiveIntegerField(
default=0,
help_text="The number of DKK handed out from the bank to the POS at the start of the business day (counted by the POS responsible)",
)
pos_count_hax5_start = models.PositiveIntegerField(
default=0,
help_text="The number of 5 HAX coins received by the POS from the bank at the start of the business day (counted by the POS responsible)",
)
pos_count_hax10_start = models.PositiveIntegerField(
default=0,
help_text="The number of 10 HAX coins received by the POS from the bank at the start of the business day (counted by the POS responsible)",
)
pos_count_hax20_start = models.PositiveIntegerField(
default=0,
help_text="The number of 20 HAX coins received by the POS from the bank at the start of the business day (counted by the POS responsible)",
)
pos_count_hax50_start = models.PositiveIntegerField(
default=0,
help_text="The number of 50 HAX coins received by the POS from the bank at the start of the business day (counted by the POS responsible)",
)
pos_count_hax100_start = models.PositiveIntegerField(
default=0,
help_text="The number of 100 HAX coins received by the POS from the bank at the start of the business day (counted by the POS responsible)",
)
# bank count end of day
bank_count_dkk_end = models.PositiveIntegerField(
default=0,
help_text="The number of DKK handed back from the POS to the bank at the end of the business day (counted by the bank responsible)",
)
bank_count_hax5_end = models.PositiveIntegerField(
default=0,
help_text="The number of 5 HAX coins handed back from the POS to the bank at the end of the business day (counted by the bank responsible)",
)
bank_count_hax10_end = models.PositiveIntegerField(
default=0,
help_text="The number of 10 HAX coins handed back from the POS to the bank at the end of the business day (counted by the bank responsible)",
)
bank_count_hax20_end = models.PositiveIntegerField(
default=0,
help_text="The number of 20 HAX coins handed back from the POS to the bank at the end of the business day (counted by the bank responsible)",
)
bank_count_hax50_end = models.PositiveIntegerField(
default=0,
help_text="The number of 50 HAX coins handed back from the POS to the bank at the end of the business day (counted by the bank responsible)",
)
bank_count_hax100_end = models.PositiveIntegerField(
default=0,
help_text="The number of 100 HAX coins handed back from the POS to the bank at the end of the business day (counted by the bank responsible)",
)
# pos count end of day
pos_count_dkk_end = models.PositiveIntegerField(
default=0,
help_text="The number of DKK handed back from the POS to the bank at the end of the business day (counted by the POS responsible)",
)
pos_count_hax5_end = models.PositiveIntegerField(
default=0,
help_text="The number of 5 HAX coins received by the bank from the POS at the end of the business day (counted by the POS responsible)",
)
pos_count_hax10_end = models.PositiveIntegerField(
default=0,
help_text="The number of 10 HAX coins received by the bank from the POS at the end of the business day (counted by the POS responsible)",
)
pos_count_hax20_end = models.PositiveIntegerField(
default=0,
help_text="The number of 20 HAX coins received by the bank from the POS at the end of the business day (counted by the POS responsible)",
)
pos_count_hax50_end = models.PositiveIntegerField(
default=0,
help_text="The number of 50 HAX coins received by the bank from the POS at the end of the business day (counted by the POS responsible)",
)
pos_count_hax100_end = models.PositiveIntegerField(
default=0,
help_text="The number of 100 HAX coins received by the bank from the POS at the end of the business day (counted by the POS responsible)",
)
@property
def camp(self):
return self.pos.team.camp
camp_filter = "pos__team__camp"
def get_absolute_url(self):
return reverse(
"backoffice:posreport_detail",
kwargs={
"camp_slug": self.camp.slug,
"pos_slug": self.pos.slug,
"posreport_uuid": self.uuid,
},
)

View file

@ -1,4 +1,4 @@
Django==3.0.8
Django==3.1
channels==2.4.0
channels-redis==3.0.1