Skip to content

Commit

Permalink
add PosProduct(Cost), PosTransaction, PosSale models, import code, an…
Browse files Browse the repository at this point in the history
…d backoffice views to interact with point-of-sale sales data
  • Loading branch information
tykling committed Sep 19, 2024
1 parent 6cf5d0d commit c1eefc6
Show file tree
Hide file tree
Showing 38 changed files with 2,035 additions and 41 deletions.
6 changes: 6 additions & 0 deletions src/backoffice/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,3 +220,9 @@ class Meta:
form=TicketGroupRefundForm,
extra=0,
)


class PosSalesJSONForm(forms.Form):
sales = forms.FileField(
help_text="POS sales.json file. Previously imported sales will be skipped and will not create duplicates.",
)
Empty file.
Empty file.
29 changes: 29 additions & 0 deletions src/backoffice/management/commands/import_pos_sales_json.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import json
import logging

from django.core.management.base import BaseCommand

from economy.utils import import_pos_sales_json

logger = logging.getLogger("bornhack.%s" % __name__)


class Command(BaseCommand):
args = "none"
help = "Import Pos sales JSON"

def add_arguments(self, parser):
parser.add_argument(
"jsonpath",
type=str,
help="The path to the Pos sales json file to import. The import is idempotent, no duplicates will be created.",
)

def handle(self, *args, **options):
with open(options["jsonpath"]) as f:
data = json.load(f)
products, transactions, sales, costs = import_pos_sales_json(data)
self.stdout.write(f"{products} new products created")
self.stdout.write(f"{transactions} new transactions created")
self.stdout.write(f"{sales} new sales created")
self.stdout.write(f"{costs} new product_costs created")
1 change: 1 addition & 0 deletions src/backoffice/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from camps.mixins import CampViewMixin
from economy.models import Pos
from economy.models import PosSale
from utils.mixins import RaisePermissionRequiredMixin


Expand Down
8 changes: 6 additions & 2 deletions src/backoffice/templates/includes/pos_list_table.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
<tr>
<th>Name</th>
<th>Team</th>
<th>Slug</th>
<th>Pos Reports</th>
<th>Total Transactions</th>
<th>Total Sales</th>
<th>Actions</th>
</tr>
</thead>
Expand All @@ -12,7 +14,9 @@
<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><a href="{% url 'backoffice:posreport_list' camp_slug=camp.slug pos_slug=pos.slug %}" class="btn btn-primary">{{ pos.pos_reports.count }} reports</a></td>
<td><a href="{% url 'backoffice:postransaction_list' camp_slug=camp.slug %}?pos={{ pos.name }}" class="btn btn-primary">{{ pos.pos_transactions.count }} transactions</a></td>
<td><a href="{% url 'backoffice:possale_list' camp_slug=camp.slug %}?pos={{ pos.name }}" class="btn btn-primary">{{ pos.sales.count }} sales for {{ pos.total_sales|default:0 }} HAX</a></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>
Expand Down
10 changes: 10 additions & 0 deletions src/backoffice/templates/includes/table_pagination.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{% load querystring %}
<div class="lead">
Per page:
<div class="btn-group" role="group" aria-label="Per page">
<a href="{% querystring per_page=25 %}" class="btn btn-default">25</a>
<a href="{% querystring per_page=100 %}" class="btn btn-default">100</a>
<a href="{% querystring per_page=250 %}" class="btn btn-default">250</a>
<a href="{% querystring per_page=1000 %}" class="btn btn-default">1000</a>
</div>
</div>
18 changes: 17 additions & 1 deletion src/backoffice/templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -254,9 +254,25 @@ <h4 class="list-group-item-heading">Accounting Exports</h4>
{% if perms.camps.orgateam_permission or perms.camps.infoteam_permission or perms.camps.barteam_permission %}
<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>
<h4 class="list-group-item-heading">Point of Sale Locations</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>
<a href="{% url 'backoffice:posproduct_list' camp_slug=camp.slug %}" class="list-group-item">
<h4 class="list-group-item-heading">Point of Sale Products</h4>
<p class="list-group-item-text">Use this view to see a list of Pos products.</p>
</a>
<a href="{% url 'backoffice:postransaction_list' camp_slug=camp.slug %}" class="list-group-item">
<h4 class="list-group-item-heading">Point of Sale TransactionsProducts</h4>
<p class="list-group-item-text">Use this view to see a list of Pos transactions.</p>
</a>
<a href="{% url 'backoffice:possale_list' camp_slug=camp.slug %}" class="list-group-item">
<h4 class="list-group-item-heading">Point of Sale Sales</h4>
<p class="list-group-item-text">Use this view to see a list of Pos sales.</p>
</a>
<a href="{% url 'backoffice:posproductcost_list' camp_slug=camp.slug %}" class="list-group-item">
<h4 class="list-group-item-heading">Point of Sale Product Costs</h4>
<p class="list-group-item-text">Use this view to see and update Pos Product Cost objects. They are used when calculating the profits for Pos sales.</p>
</a>
{% endif %}

{% if perms.camps.gameteam_permission %}
Expand Down
25 changes: 18 additions & 7 deletions src/backoffice/templates/pos_detail.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,35 @@ <h3 class="panel-title">{{ pos.name }} | Pos | BackOffice</h3>
<table class="table">
<tbody>
<tr>
<th>Pos Name</th>
<th>Name</th>
<td>{{ pos.name }}</td>
</tr>
<tr>
<th>Team</th>
<td>{{ pos.team }}</p>
</tr>
<tr>
<th>Pos Reports</th>
<td><a href="{% url 'backoffice:posreport_list' camp_slug=camp.slug pos_slug=pos.slug %}" class="btn btn-primary"><i class="fas fa-list"></i> {{ pos.pos_reports.count }} reports</a></p>
</tr>
<tr>
<th>Transactions</th>
<td><a href="{% url 'backoffice:postransaction_list' camp_slug=camp.slug %}?pos={{ pos.name }}" class="btn btn-primary"><i class="fas fa-list"></i> {{ pos.pos_transactions.count }} transactions</a></p>
</tr>
<tr>
<th>Sales</th>
<td><a href="{% url 'backoffice:possale_list' camp_slug=camp.slug %}?pos={{ pos.name }}" class="btn btn-primary"><i class="fas fa-list"></i> {{ pos.sales.count }} products sold</a></p>
</tr>
<tr>
<th>Total Sales</th>
<td>{{ pos.total_sales|default:0 }} HAX</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>
<a href="{% url 'backoffice:possale_import' camp_slug=camp.slug %}" class="btn btn-success"><i class="fas fa-plus"></i> Import Pos Sales JSON</a>
{% endif %}
</div>
</div>
Expand Down
11 changes: 8 additions & 3 deletions src/backoffice/templates/pos_list.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,19 @@
<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" %}
<a href="{% url 'backoffice:index' camp_slug=camp.slug %}" class="btn btn-default"><i class="fas fa-undo"></i> Backoffice</a>
<a href="{% url 'backoffice:pos_list' camp_slug=camp.slug %}" class="btn btn-primary"><i class="fas fa-list"></i> Pos List</a>
<a href="{% url 'backoffice:posproduct_list' camp_slug=camp.slug %}" class="btn btn-primary"><i class="fas fa-list"></i> Pos Product List</a>
<a href="{% url 'backoffice:posproductcost_list' camp_slug=camp.slug %}" class="btn btn-primary"><i class="fas fa-list"></i> Pos Product Cost List</a>
<a href="{% url 'backoffice:postransaction_list' camp_slug=camp.slug %}" class="btn btn-primary"><i class="fas fa-list"></i> Pos Transaction List</a>
<a href="{% url 'backoffice:possale_list' camp_slug=camp.slug %}" class="btn btn-primary"><i class="fas fa-list"></i> Pos Sales List</a>
</p>
<p class="lead">A <i>Pos</i> is a place where we sell stuff for DKK and/or HAX.</p>
{% include "includes/pos_list_table.html" %}
{% endif %}
<p>
{% if perms.camps.orgateam_permission %}
Expand Down
44 changes: 44 additions & 0 deletions src/backoffice/templates/pos_product_cost_list.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{% extends 'base.html' %}
{% load render_table from django_tables2 %}
{% load bootstrap3 %}
{% load django_tables2 %}

{% block title %}
Pos Product Costs | BackOffice | {{ block.super }}
{% endblock %}

{% block content %}
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Pos Product Costs | BackOffice</h3>
</div>
<div class="panel-body">
<p>
<a href="{% url 'backoffice:index' camp_slug=camp.slug %}" class="btn btn-default"><i class="fas fa-undo"></i> Backoffice</a>
<a href="{% url 'backoffice:pos_list' camp_slug=camp.slug %}" class="btn btn-primary"><i class="fas fa-list"></i> Pos List</a>
<a href="{% url 'backoffice:posproduct_list' camp_slug=camp.slug %}" class="btn btn-primary"><i class="fas fa-list"></i> Pos Product List</a>
<a href="{% url 'backoffice:posproductcost_list' camp_slug=camp.slug %}" class="btn btn-primary"><i class="fas fa-list"></i> Pos Product Cost List</a>
<a href="{% url 'backoffice:postransaction_list' camp_slug=camp.slug %}" class="btn btn-primary"><i class="fas fa-list"></i> Pos Transaction List</a>
<a href="{% url 'backoffice:possale_list' camp_slug=camp.slug %}" class="btn btn-primary"><i class="fas fa-list"></i> Pos Sales List</a>
</p>
<p class="lead">A Pos Product Cost shows the cost of selling one of a product. Pos Product Costs are applied to all sales after the timestamp, until a newer Pos Product Cost is created.</p>
<div class="panel panel-default">
<div class="panel-heading">Filter Pos Product Costs</div>
<div class="panel-body">
{% if filter %}
<form action="" method="get" class="form form-inline">
{% bootstrap_form filter.form layout='inline' %}
<br>
<button class="btn btn-success"><i class="fas fa-search"></i> Filter</button>
<a href="{% url 'backoffice:posproductcost_list' camp_slug=camp.slug %}" class="btn btn-danger"><i class="fas fa-times"></i> Clear</a>
</form>
{% endif %}
</div>
</div>
<div class="lead">Showing {{ object_list.count }} costs out of {{ total_costs }} costs for {{ camp.title }}</div>
{% include "includes/table_pagination.html" %}
{% render_table table %}
{% include "includes/table_pagination.html" %}
</div>
</div>
{% endblock %}
43 changes: 43 additions & 0 deletions src/backoffice/templates/pos_product_list.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{% extends 'base.html' %}
{% load render_table from django_tables2 %}
{% load bootstrap3 %}

{% block title %}
Pos Products | BackOffice | {{ block.super }}
{% endblock %}

{% block content %}
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Pos Products | BackOffice</h3>
</div>
<div class="panel-body">
<p>
<a href="{% url 'backoffice:index' camp_slug=camp.slug %}" class="btn btn-default"><i class="fas fa-undo"></i> Backoffice</a>
<a href="{% url 'backoffice:pos_list' camp_slug=camp.slug %}" class="btn btn-primary"><i class="fas fa-list"></i> Pos List</a>
<a href="{% url 'backoffice:posproduct_list' camp_slug=camp.slug %}" class="btn btn-primary"><i class="fas fa-list"></i> Pos Product List</a>
<a href="{% url 'backoffice:posproductcost_list' camp_slug=camp.slug %}" class="btn btn-primary"><i class="fas fa-list"></i> Pos Product Cost List</a>
<a href="{% url 'backoffice:postransaction_list' camp_slug=camp.slug %}" class="btn btn-primary"><i class="fas fa-list"></i> Pos Transaction List</a>
<a href="{% url 'backoffice:possale_list' camp_slug=camp.slug %}" class="btn btn-primary"><i class="fas fa-list"></i> Pos Sales List</a>
</p>
<p class="lead">A Pos Product is something sold at a Pos. Price, name and other product details might change over time. The sales numbers shown in this table only include transactions related to {{ camp.title }}.</p>
<div class="panel panel-default">
<div class="panel-heading">Filter Pos Products</div>
<div class="panel-body">
{% if filter %}
<form action="" method="get" class="form form-inline">
{% bootstrap_form filter.form layout='inline' %}
<br>
<button class="btn btn-success"><i class="fas fa-search"></i> Filter</button>
<a href="{% url 'backoffice:posproduct_list' camp_slug=camp.slug %}" class="btn btn-danger"><i class="fas fa-times"></i> Clear</a>
</form>
{% endif %}
</div>
</div>
<div class="lead">Showing {{ object_list|length }} products ({{ filtered_sales_count }} sales for {{ filtered_sales_sum }} HAX) out of total {{ total_products }} products ({{ total_sales_count }} sales for {{ total_sales_sum }} HAX) for {{ camp.title }}</div>
{% include "includes/table_pagination.html" %}
{% render_table table %}
{% include "includes/table_pagination.html" %}
</div>
</div>
{% endblock %}
44 changes: 44 additions & 0 deletions src/backoffice/templates/pos_sale_list.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{% extends 'base.html' %}
{% load render_table from django_tables2 %}
{% load bootstrap3 %}
{% load django_tables2 %}

{% block title %}
{{ pos.name }} | Pos Sales | BackOffice | {{ block.super }}
{% endblock %}

{% block content %}
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Pos Sales | BackOffice</h3>
</div>
<div class="panel-body">
<p>
<a href="{% url 'backoffice:index' camp_slug=camp.slug %}" class="btn btn-default"><i class="fas fa-undo"></i> Backoffice</a>
<a href="{% url 'backoffice:pos_list' camp_slug=camp.slug %}" class="btn btn-primary"><i class="fas fa-list"></i> Pos List</a>
<a href="{% url 'backoffice:posproduct_list' camp_slug=camp.slug %}" class="btn btn-primary"><i class="fas fa-list"></i> Pos Product List</a>
<a href="{% url 'backoffice:posproductcost_list' camp_slug=camp.slug %}" class="btn btn-primary"><i class="fas fa-list"></i> Pos Product Cost List</a>
<a href="{% url 'backoffice:postransaction_list' camp_slug=camp.slug %}" class="btn btn-primary"><i class="fas fa-list"></i> Pos Transaction List</a>
<a href="{% url 'backoffice:possale_list' camp_slug=camp.slug %}" class="btn btn-primary"><i class="fas fa-list"></i> Pos Sales List</a>
</p>
<p class="lead">A Pos Sale is created every time an item is sold and paid with HAX at a Pos. Buying two Mate creates one Pos Transaction with two Pos Sales.</p>
<div class="panel panel-default">
<div class="panel-heading">Filter Pos Sales</div>
<div class="panel-body">
{% if filter %}
<form action="" method="get" class="form form-inline">
{% bootstrap_form filter.form layout='inline' %}
<br>
<button class="btn btn-success"><i class="fas fa-search"></i> Filter</button>
<a href="{% url 'backoffice:possale_list' camp_slug=camp.slug %}" class="btn btn-danger"><i class="fas fa-times"></i> Clear</a>
</form>
{% endif %}
</div>
</div>
<div class="lead">Showing {{ possale_list.count }} sales ({{ filtered_sales_sum }} HAX) out of {{ total_sales_count }} sales ({{total_sales_sum}} HAX) for {{ camp.title }}</div>
{% include "includes/table_pagination.html" %}
{% render_table table %}
{% include "includes/table_pagination.html" %}
</div>
</div>
{% endblock %}
19 changes: 19 additions & 0 deletions src/backoffice/templates/pos_sales_json_upload_form.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{% extends 'base.html' %}
{% load bootstrap3 %}

{% block content %}
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Pos Sales JSON Upload</h3>
</div>
<div class="panel-body">
<p class="lead">Pos Sales JSON Upload</p>
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{% bootstrap_form form %}
<button type="submit" class="btn btn-success"><i class="fas fa-check"></i> Upload</button>
<a href="{% url 'backoffice:pos_list' camp_slug=camp.slug %}" class="btn btn-default"><i class="fas fa-undo"></i> Cancel</a>
</form>
</div>
</div>
{% endblock content %}
46 changes: 46 additions & 0 deletions src/backoffice/templates/pos_transaction_list.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{% extends 'base.html' %}
{% load render_table from django_tables2 %}
{% load bootstrap3 %}
{% load django_tables2 %}

{% block title %}
Pos Transactions | BackOffice | {{ block.super }}
{% endblock %}

{% block content %}
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Pos Transactions | BackOffice</h3>
</div>
<div class="panel-body">
<p>
<a href="{% url 'backoffice:index' camp_slug=camp.slug %}" class="btn btn-default"><i class="fas fa-undo"></i> Backoffice</a>
<a href="{% url 'backoffice:pos_list' camp_slug=camp.slug %}" class="btn btn-primary"><i class="fas fa-list"></i> Pos List</a>
<a href="{% url 'backoffice:posproduct_list' camp_slug=camp.slug %}" class="btn btn-primary"><i class="fas fa-list"></i> Pos Product List</a>
<a href="{% url 'backoffice:posproductcost_list' camp_slug=camp.slug %}" class="btn btn-primary"><i class="fas fa-list"></i> Pos Product Cost List</a>
<a href="{% url 'backoffice:postransaction_list' camp_slug=camp.slug %}" class="btn btn-primary"><i class="fas fa-list"></i> Pos Transaction List</a>
<a href="{% url 'backoffice:possale_list' camp_slug=camp.slug %}" class="btn btn-primary"><i class="fas fa-list"></i> Pos Sales List</a>
</p>
<p class="lead">A Pos Transaction is created every time one or more items are sold and paid with HAX. Buying two Mate creates one Pos Transaction with two Pos Sales.</p>
<div class="panel panel-default">
<div class="panel-heading">Filter Pos Transactions</div>
<div class="panel-body">
{% if filter %}
<form action="" method="get" class="form form-inline">
{% bootstrap_form filter.form layout='inline' %}
<br>
<button class="btn btn-success"><i class="fas fa-search"></i> Filter</button>
<a href="{% url 'backoffice:postransaction_list' camp_slug=camp.slug %}" class="btn btn-danger">
<i class="fas fa-times"></i> Clear
</a>
</form>
{% endif %}
</div>
</div>
<div class="lead">Filter showing {{ object_list|length }} transactions ({{ filtered_sales_count }} sales for {{ filtered_sales_sum }} HAX) of {{ total_transactions }} transactions ({{ total_sales_count }} sales for {{ total_sales_sum }} HAX)</div>
{% include "includes/table_pagination.html" %}
{% render_table table %}
{% include "includes/table_pagination.html" %}
</div>
</div>
{% endblock %}
Loading

0 comments on commit c1eefc6

Please sign in to comment.