diff --git a/billard/admin.py b/billard/admin.py index 535f863..823a2a6 100644 --- a/billard/admin.py +++ b/billard/admin.py @@ -10,14 +10,19 @@ class LocationAdmin(admin.ModelAdmin): @admin.register(Client) class ClientAdmin(admin.ModelAdmin): - list_display = ('uuid', 'desk1_name', 'desk2_name') - fields = ['location', 'uuid', 'desk1_enable', 'desk1_name', 'desk1_prize_nt', 'desk1_prize_ht', 'desk2_enable', 'desk2_name', 'desk2_prize_nt', 'desk2_prize_ht', ] + list_display = ('uuid', 'location') + fields = ['location', 'uuid'] @admin.register(LocationData) class LocationDataAdmin(admin.ModelAdmin): - list_display = ('location_id', 'table_no', 'tst', 'on_off', 'processed', 'error_msg') - fields = ['location_id', 'table_no', 'tst', 'on_off', 'processed', 'error_msg'] + list_display = ('client_id', 'desk_no', 'tst', 'on_off', 'processed', 'error_msg') + fields = ['client_id', 'desk_no', 'tst', 'on_off', 'processed', 'error_msg'] + + +@admin.register(Desk) +class DeskAdmin(admin.ModelAdmin): + list_display = ('client', 'desk_no', 'name', 'enabled', 'prize', 'prize_hh') @admin.register(Accounting) diff --git a/billard/migrations/0007_desk.py b/billard/migrations/0007_desk.py new file mode 100644 index 0000000..4de137c --- /dev/null +++ b/billard/migrations/0007_desk.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.5 on 2017-02-10 18:30 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('billard', '0006_auto_20170206_2031'), + ] + + operations = [ + migrations.CreateModel( + name='Desk', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('desk_no', models.IntegerField(verbose_name='Tischnummer')), + ('name', models.CharField(blank=True, max_length=32, null=True, verbose_name='Tischbezeichnung')), + ('enabled', models.BooleanField(verbose_name='Tisch aktiv')), + ('prize', models.DecimalField(blank=True, decimal_places=2, max_digits=5, null=True, verbose_name='Normelpreis')), + ('prize_hh', models.DecimalField(blank=True, decimal_places=2, max_digits=5, null=True, verbose_name='Preis Happy Hour')), + ('client', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='billard.Client', verbose_name='Client')), + ], + options={ + 'verbose_name_plural': 'Tische', + 'verbose_name': 'Tisch', + }, + ), + ] diff --git a/billard/migrations/0008_auto_20170210_1947.py b/billard/migrations/0008_auto_20170210_1947.py new file mode 100644 index 0000000..b661151 --- /dev/null +++ b/billard/migrations/0008_auto_20170210_1947.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.5 on 2017-02-10 19:47 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('billard', '0007_desk'), + ] + + operations = [ + migrations.RemoveField( + model_name='client', + name='desk1_enable', + ), + migrations.RemoveField( + model_name='client', + name='desk1_name', + ), + migrations.RemoveField( + model_name='client', + name='desk1_prize_ht', + ), + migrations.RemoveField( + model_name='client', + name='desk1_prize_nt', + ), + migrations.RemoveField( + model_name='client', + name='desk2_enable', + ), + migrations.RemoveField( + model_name='client', + name='desk2_name', + ), + migrations.RemoveField( + model_name='client', + name='desk2_prize_ht', + ), + migrations.RemoveField( + model_name='client', + name='desk2_prize_nt', + ), + migrations.AlterField( + model_name='desk', + name='client', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='desks', to='billard.Client', verbose_name='Client'), + ), + ] diff --git a/billard/migrations/0009_auto_20170210_1955.py b/billard/migrations/0009_auto_20170210_1955.py new file mode 100644 index 0000000..4bfb974 --- /dev/null +++ b/billard/migrations/0009_auto_20170210_1955.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.5 on 2017-02-10 19:55 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('billard', '0008_auto_20170210_1947'), + ] + + operations = [ + migrations.RenameField( + model_name='locationdata', + old_name='table_no', + new_name='desk_no', + ), + ] diff --git a/billard/migrations/0010_auto_20170210_2040.py b/billard/migrations/0010_auto_20170210_2040.py new file mode 100644 index 0000000..f577c7e --- /dev/null +++ b/billard/migrations/0010_auto_20170210_2040.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.5 on 2017-02-10 19:40 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('billard', '0009_auto_20170210_1955'), + ] + + operations = [ + migrations.RenameField( + model_name='locationdata', + old_name='location_id', + new_name='client_id', + ), + ] diff --git a/billard/models.py b/billard/models.py index 4ebf838..41b50d3 100644 --- a/billard/models.py +++ b/billard/models.py @@ -2,27 +2,29 @@ import uuid from django.db import models from django.contrib.auth.models import User from datetime import datetime, timezone -from . import utils -from . import tasks +from billard import utils, tasks from django.contrib.auth.models import User from django.db.models.signals import post_save +from django.dispatch import receiver +from celery import shared_task class LocationData(models.Model): - location_id = models.UUIDField(blank=False, null=False, verbose_name="Standort-ID") - table_no = models.IntegerField(blank=False, null=False, verbose_name="Tischnummer") + client_id = models.UUIDField(blank=False, null=False, verbose_name="Client-ID") + desk_no = models.IntegerField(blank=False, null=False, verbose_name="Tischnummer") tst = models.DateTimeField(blank=False, null=False, verbose_name="Zeitstempel") on_off = models.BooleanField(blank=False, null=False, verbose_name="Ein/Ausgebucht") processed = models.BooleanField(default=False, verbose_name="Verarbeitet") error_msg = models.TextField(blank=True, null=True, verbose_name="Fehlermeldung") def __str__(self): - return str(self.location_id) + return str(self.client_id) class Meta: verbose_name = "Standortlog" verbose_name_plural = "Standortlogs" + class Location(models.Model): users = models.ManyToManyField(User, related_name='locations', verbose_name="Benutzer") code = models.CharField(max_length=16, unique=True, verbose_name="Code") @@ -45,30 +47,6 @@ class Location(models.Model): class Client(models.Model): uuid = models.UUIDField(unique=True, default=uuid.uuid4, verbose_name="Identifier") location = models.ForeignKey(Location, verbose_name="Standort") - desk1_enable = models.BooleanField() - desk1_name = models.CharField(max_length=32, blank=True, null=True) - desk1_prize_nt = models.DecimalField(max_digits=4, decimal_places=2, blank=True, null=True) - desk1_prize_ht = models.DecimalField(max_digits=4, decimal_places=2, blank=True, null=True) - desk2_enable = models.BooleanField() - desk2_name = models.CharField(max_length=32, blank=True, null=True) - desk2_prize_nt = models.DecimalField(max_digits=4, decimal_places=2, blank=True, null=True) - desk2_prize_ht = models.DecimalField(max_digits=4, decimal_places=2, blank=True, null=True) - - def accounting_for(self, desk_no, pht, pnt): - t = Accounting.objects.filter(client_id=self.id, desk_no=desk_no)[:3][::-1] - if t.__len__() > 0: - a = t[t.__len__() - 1] - if a.time_to is None: - prize = utils.get_prize_for(start=a.time_from, end=datetime.now(timezone.utc), pph=pht) - if prize != a.prize: - a.prize = prize - return t - - def accounting_1(self): - return self.accounting_for(1, self.desk1_prize_ht, self.desk1_prize_nt) - - def accounting_2(self): - return self.accounting_for(2, self.desk2_prize_ht, self.desk2_prize_nt) def __str__(self): return '{}, {}'.format(self.location.name, self.uuid) @@ -77,6 +55,34 @@ class Client(models.Model): verbose_name = "Client" verbose_name_plural = "Clienten" + +class Desk(models.Model): + client = models.ForeignKey(Client, verbose_name='Client', related_name='desks') + desk_no = models.IntegerField(verbose_name='Tischnummer') + name = models.CharField(max_length=32, blank=True, null=True, verbose_name='Tischbezeichnung') + enabled = models.BooleanField(verbose_name='Tisch aktiv') + prize = models.DecimalField(max_digits=5, decimal_places=2, blank=True, null=True, verbose_name="Normelpreis") + prize_hh = models.DecimalField(max_digits=5, decimal_places=2, blank=True, null=True, + verbose_name="Preis Happy Hour") + + def accounting_for(self): + t = Accounting.objects.filter(client=self.client, desk_no=self.desk_no)[:3][::-1] + if t.__len__() > 0: + a = t[t.__len__() - 1] + if a.time_to is None: + prize = utils.get_prize_for(start=a.time_from, end=datetime.now(timezone.utc), pph=self.prize) + if prize != a.prize: + a.prize = prize + return t + + def __str__(self): + return '{}, {}'.format(self.client.uuid, self.name) + + class Meta: + verbose_name = "Tisch" + verbose_name_plural = "Tische" + + class Accounting(models.Model): client = models.ForeignKey(Client, verbose_name="Client") desk_no = models.IntegerField(verbose_name="Tischnummer") @@ -93,7 +99,44 @@ class Accounting(models.Model): verbose_name_plural = "Buchhaltungseinträge" -def update_location_data(sender, instance, **kwargs): - tasks.process_location_data.delay() +@receiver(post_save, sender=LocationData) +def test(sender, **kwargs): + process_location_data() -post_save.connect(update_location_data, sender=LocationData) + +@shared_task +def process_location_data(): + data = LocationData.objects.filter(processed=False) + for ld in data: + cli = Client.objects.filter(uuid=ld.client_id) + if cli.count() < 1: + ld.processed = True + ld.error_msg = 'No location object found. Stopp processing!' + ld.save() + # TODO Send error eMail to Admin + else: + cli = cli[0] + desk = cli.desks.filter(desk_no=ld.desk_no)[0] + ac = Accounting.objects.filter(client=cli, desk_no=ld.desk_no).order_by('time_from').reverse() + if ld.on_off: + if ac.count() > 0 and ac[0].time_to is None: + ac[0].time_to = datetime.now() + ac[0].save() + # TODO Send error eMail to Admin + acc = Accounting( + client=cli, + desk_no=ld.desk_no, + time_from=ld.tst, + ) + acc.save() + ld.delete() + else: + acc = ac[0] + acc.time_to = ld.tst + acc.prize = utils.get_prize_for( + start=acc.time_from, + end=ld.tst, + pph=desk.prize + ) + acc.save() + ld.delete() diff --git a/billard/tasks.py b/billard/tasks.py index 6bcd39f..46d4a18 100644 --- a/billard/tasks.py +++ b/billard/tasks.py @@ -1,42 +1,8 @@ from __future__ import absolute_import, unicode_literals from celery import shared_task -from . import models -from datetime import datetime -from . import utils +from billard import utils @shared_task def process_location_data(): - data = models.LocationData.objects.filter(processed=False) - for ld in data: - cli = models.Client.objects.filter(uuid=ld.location_id) - if cli.count() < 1: - ld.processed = True - ld.error_msg = 'No location object found. Stopp processing!' - ld.save() - # TODO Send error eMail to Admin - else: - cli = cli[0] - ac = models.Accounting.objects.filter(client=cli, desk_no=ld.table_no).order_by('time_from').reverse() - if ld.on_off: - if ac.count() > 0 and ac[0].time_to is None: - ac[0].time_to = datetime.now() - ac[0].save() - # TODO Send error eMail to Admin - acc = models.Accounting( - client=cli, - desk_no=ld.table_no, - time_from=ld.tst, - ) - acc.save() - ld.delete() - else: - acc = ac[0] - acc.time_to = ld.tst - acc.prize = utils.get_prize_for( - start=acc.time_from, - end=ld.tst, - pph=cli.desk1_prize_ht - ) - acc.save() - ld.delete() + utils.process_location_data() diff --git a/billard/templates/billard/index.html b/billard/templates/billard/index.html index 1987f02..561299a 100644 --- a/billard/templates/billard/index.html +++ b/billard/templates/billard/index.html @@ -1,4 +1,5 @@ {% extends 'billard/base.html' %} +{% load display_client %} {% block header %} @@ -19,42 +20,7 @@ {% if clients %} {% for cli in clients %} -{% if cli.desk1_enable %} -
-
-

(1) {{ cli.desk1_name }}

-{% if cli.accounting_1 %} - -{% for acc in cli.accounting_1 %} - - - - - -{% endfor %} -
{{ acc.time_from|date:"d.m.Y H:i:s" }}{% if acc.time_to is not None %}{{ acc.time_to|date:"d.m.Y H:i:s" }}{% endif %}{% if acc.prize is not None %}{{ acc.prize|floatformat:2 }}{% endif %}
-{% endif %} -
-
-{% endif %} -{% if cli.desk2_enable %} -
-
-

(2) {{ cli.desk2_name }}

-{% if cli.accounting_2 %} - -{% for acc in cli.accounting_2 %} - - - - - -{% endfor %} -
{{ acc.time_from|date:"d.m.Y H:i:s" }}{% if acc.time_to is not None %}{{ acc.time_to|date:"d.m.Y H:i:s" }}{% endif %}{% if acc.prize is not None %}{{ acc.prize|floatformat:2 }}{% endif %}
-{% endif %} -
-
-{% endif %} +{% for i in range %} {{ cli|display_client:i }} {% endfor %} {% endfor %} {% else %}
diff --git a/billard/templatetags/__init__.py b/billard/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/billard/templatetags/display_client.py b/billard/templatetags/display_client.py new file mode 100644 index 0000000..690164a --- /dev/null +++ b/billard/templatetags/display_client.py @@ -0,0 +1,48 @@ +from django import template +from django.utils.html import format_html +from billard.models import Desk, Accounting +from billard import utils +from datetime import datetime, timezone + +register = template.Library() + + +@register.filter(is_safe=True) +def display_client(client, desk_no): + desks = client.desks.filter(desk_no=desk_no) + if len(desks) == 0: + return '' + desk = desks[0] + if not desk.enabled: + return '' + + alert = 'alert-success' + acc = Accounting.objects.filter(client=client, desk_no=desk_no)[:3][::-1] + if acc is not None and len(acc) > 0: + a = acc[-1] + if a.time_to is None: + alert = 'alert-info' + prize = utils.get_prize_for(start=a.time_from, end=datetime.now(timezone.utc), pph=desk.prize) + prize = '{0:.2f}'.format(prize) + if prize != a.prize: + a.prize = prize + + html = '
\n' + html += '
\n'.format(alert) + html += '

({}) {}

\n'.format(desk_no, desk.name) + if len(acc) > 0: + html += ' \n' + for a in acc: + html += ' \n' + html += ' \n'.format(a.time_from) + html += ' \n'.format((a.time_to if a.time_to is not None else '')) + html += ' \n'\ + .format((a.prize if a.prize is not None else '')) + html += ' \n' + html += ' \n' + html += ' \n' + html += '
{}{}{}
\n' + html += '
\n' + html += '
\n' + html = format_html(html) + return html diff --git a/billard/utils.py b/billard/utils.py index 4251112..f6f6d8e 100644 --- a/billard/utils.py +++ b/billard/utils.py @@ -1,4 +1,5 @@ -from datetime import datetime, date, time +from datetime import datetime +#import models def get_prize_for(start, end, pph=0): @@ -16,8 +17,38 @@ def get_prize_for(start, end, pph=0): return prize -if __name__ == '__main__': - d = date(2017, 2, 5) - t = time(16, 00) - start = datetime.combine(d, t) - get_prize_for(start=start, pph=8.5) +def process_location_data(): + pass + #data = models.LocationData.objects.filter(processed=False) + #for ld in data: + # cli = models.Client.objects.filter(uuid=ld.client_id) + # if cli.count() < 1: + # ld.processed = True + # ld.error_msg = 'No location object found. Stopp processing!' + # ld.save() + # # TODO Send error eMail to Admin + # else: + # cli = cli[0] + # ac = models.Accounting.objects.filter(client=cli, desk_no=ld.table_no).order_by('time_from').reverse() + # if ld.on_off: + # if ac.count() > 0 and ac[0].time_to is None: + # ac[0].time_to = datetime.now() + # ac[0].save() + # # TODO Send error eMail to Admin + # acc = models.Accounting( + # client=cli, + # desk_no=ld.table_no, + # time_from=ld.tst, + # ) + # acc.save() + # ld.delete() + # else: + # acc = ac[0] + # acc.time_to = ld.tst + # acc.prize = get_prize_for( + # start=acc.time_from, + # end=ld.tst, + # pph=cli.desk1_prize_ht + # ) + # acc.save() + # ld.delete() diff --git a/billard/views.py b/billard/views.py index e4c5a75..305ec9c 100644 --- a/billard/views.py +++ b/billard/views.py @@ -55,7 +55,7 @@ def index(request): locations = Location.objects.filter(users__id=request.user.id).order_by('code') clients = Client.objects.filter(location_id=loc).order_by('id') context = { - 'range': range(1, 3), + 'range': range(1, 9), 'locations': locations, 'clients': clients, 'location_id': int(loc), diff --git a/test-client.py b/test-client.py index b00427f..fa3581e 100644 --- a/test-client.py +++ b/test-client.py @@ -17,7 +17,7 @@ def main(): args = parser.parse_args() payload = { 'location_id': 'a7b47b4b-5b63-3b2f-8af0-4b185843f419', - 'table_no': args.desk, + 'desk_no': args.desk, 'tst': args.date.strftime('%Y-%m-%dT%H:%M:%S%Z') } if args.on: