add initial POS stuff
This commit is contained in:
parent
6d97d4b603
commit
001ba2d955
|
@ -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
|
from utils.mixins import RaisePermissionRequiredMixin
|
||||||
|
|
||||||
|
|
||||||
|
@ -37,3 +41,27 @@ class ContentTeamPermissionMixin(RaisePermissionRequiredMixin):
|
||||||
"camps.backoffice_permission",
|
"camps.backoffice_permission",
|
||||||
"camps.contentteam_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
|
||||||
|
|
28
src/backoffice/templates/includes/pos_list_table.html
Normal file
28
src/backoffice/templates/includes/pos_list_table.html
Normal 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>
|
34
src/backoffice/templates/includes/posreport_list_table.html
Normal file
34
src/backoffice/templates/includes/posreport_list_table.html
Normal 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>
|
|
@ -166,6 +166,12 @@
|
||||||
<h4 class="list-group-item-heading">Proxied Content</h4>
|
<h4 class="list-group-item-heading">Proxied Content</h4>
|
||||||
<p class="list-group-item-text">Use this view to see proxied content</p>
|
<p class="list-group-item-text">Use this view to see proxied content</p>
|
||||||
</a>
|
</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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
18
src/backoffice/templates/pos_delete.html
Normal file
18
src/backoffice/templates/pos_delete.html
Normal 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 %}
|
42
src/backoffice/templates/pos_detail.html
Normal file
42
src/backoffice/templates/pos_detail.html
Normal 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 %}
|
21
src/backoffice/templates/pos_form.html
Normal file
21
src/backoffice/templates/pos_form.html
Normal 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 %}
|
29
src/backoffice/templates/pos_list.html
Normal file
29
src/backoffice/templates/pos_list.html
Normal 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 %}
|
115
src/backoffice/templates/posreport_detail.html
Normal file
115
src/backoffice/templates/posreport_detail.html
Normal 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 %}
|
21
src/backoffice/templates/posreport_form.html
Normal file
21
src/backoffice/templates/posreport_form.html
Normal 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 %}
|
25
src/backoffice/templates/posreport_list.html
Normal file
25
src/backoffice/templates/posreport_list.html
Normal 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 %}
|
|
@ -60,6 +60,18 @@ from .views import (
|
||||||
MerchandiseToOrderView,
|
MerchandiseToOrderView,
|
||||||
OutgoingEmailMassUpdateView,
|
OutgoingEmailMassUpdateView,
|
||||||
PendingProposalsView,
|
PendingProposalsView,
|
||||||
|
PosCreateView,
|
||||||
|
PosDeleteView,
|
||||||
|
PosDetailView,
|
||||||
|
PosListView,
|
||||||
|
PosReportBankCountEndView,
|
||||||
|
PosReportBankCountStartView,
|
||||||
|
PosReportCreateView,
|
||||||
|
PosReportDetailView,
|
||||||
|
PosReportPosCountEndView,
|
||||||
|
PosReportPosCountStartView,
|
||||||
|
PosReportUpdateView,
|
||||||
|
PosUpdateView,
|
||||||
ProductHandoutView,
|
ProductHandoutView,
|
||||||
ReimbursementCreateUserSelectView,
|
ReimbursementCreateUserSelectView,
|
||||||
ReimbursementCreateView,
|
ReimbursementCreateView,
|
||||||
|
@ -632,4 +644,77 @@ urlpatterns = [
|
||||||
OutgoingEmailMassUpdateView.as_view(),
|
OutgoingEmailMassUpdateView.as_view(),
|
||||||
name="outgoing_email_release",
|
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",
|
||||||
|
),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -20,7 +20,15 @@ from django.utils import timezone
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.views.generic import DetailView, ListView, TemplateView
|
from django.views.generic import DetailView, ListView, TemplateView
|
||||||
from django.views.generic.edit import CreateView, DeleteView, FormView, UpdateView
|
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 (
|
from facilities.models import (
|
||||||
Facility,
|
Facility,
|
||||||
FacilityFeedback,
|
FacilityFeedback,
|
||||||
|
@ -59,6 +67,7 @@ from .mixins import (
|
||||||
EconomyTeamPermissionMixin,
|
EconomyTeamPermissionMixin,
|
||||||
InfoTeamPermissionMixin,
|
InfoTeamPermissionMixin,
|
||||||
OrgaTeamPermissionMixin,
|
OrgaTeamPermissionMixin,
|
||||||
|
PosViewMixin,
|
||||||
RaisePermissionRequiredMixin,
|
RaisePermissionRequiredMixin,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1955,3 +1964,219 @@ class OutgoingEmailMassUpdateView(CampViewMixin, OrgaTeamPermissionMixin, FormVi
|
||||||
def get_success_url(self, *args, **kwargs):
|
def get_success_url(self, *args, **kwargs):
|
||||||
"""Return to the backoffice index."""
|
"""Return to the backoffice index."""
|
||||||
return reverse("backoffice:index", kwargs={"camp_slug": self.camp.slug})
|
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")
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from django.contrib import admin
|
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
|
# chains and credebtors
|
||||||
|
@ -113,3 +113,18 @@ class ReimbursementAdmin(admin.ModelAdmin):
|
||||||
list_filter = ["camp", "user", "reimbursement_user", "paid"]
|
list_filter = ["camp", "user", "reimbursement_user", "paid"]
|
||||||
list_display = ["camp", "user", "reimbursement_user", "paid", "notes", "get_amount"]
|
list_display = ["camp", "user", "reimbursement_user", "paid", "notes", "get_amount"]
|
||||||
search_fields = ["user__username", "reimbursement_user__username", "notes"]
|
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"]
|
||||||
|
|
284
src/economy/migrations/0011_pos_posreport.py
Normal file
284
src/economy/migrations/0011_pos_posreport.py
Normal 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,},
|
||||||
|
),
|
||||||
|
]
|
|
@ -3,6 +3,7 @@ import os
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.urls import reverse
|
||||||
from utils.models import CampRelatedModel, CreatedUpdatedModel, UUIDModel
|
from utils.models import CampRelatedModel, CreatedUpdatedModel, UUIDModel
|
||||||
from utils.slugs import unique_slugify
|
from utils.slugs import unique_slugify
|
||||||
|
|
||||||
|
@ -465,3 +466,224 @@ class Reimbursement(CampRelatedModel, UUIDModel):
|
||||||
for expense in self.expenses.filter(paid_by_bornhack=False):
|
for expense in self.expenses.filter(paid_by_bornhack=False):
|
||||||
amount += expense.amount
|
amount += expense.amount
|
||||||
return 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,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
Django==3.0.8
|
Django==3.1
|
||||||
channels==2.4.0
|
channels==2.4.0
|
||||||
channels-redis==3.0.1
|
channels-redis==3.0.1
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue