carom-server/billard/models.py

176 lines
7.1 KiB
Python

import uuid
import logging
from django.db import models
from django.contrib.auth.models import User
from datetime import datetime, timezone
from billard import utils
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
from .tasks import request_process_location_data
log = logging.getLogger(__name__)
class LocationData(models.Model):
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.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")
name = models.CharField(max_length=64, unique=True, verbose_name="Name")
street = models.CharField(max_length=64, blank=True, null=True, verbose_name="Straße")
plz = models.CharField(max_length=8, blank=True, null=True, verbose_name="Postleitzahl")
city = models.CharField(max_length=64, blank=True, null=True, verbose_name="Stadt")
phone = models.CharField(max_length=64, blank=True, null=True, verbose_name="Telefon")
email = models.EmailField(blank=True, null=True, verbose_name="Email")
url = models.URLField(blank=True, null=True, verbose_name="URL")
happy_hour_start = models.TimeField(blank=True, null=True, verbose_name='Happy Hour Start')
happy_hour_end = models.TimeField(blank=True, null=True, verbose_name='Happy Hour Ende')
def __str__(self):
return self.name
class Meta:
verbose_name = "Standort"
verbose_name_plural = "Standorte"
class Client(models.Model):
uuid = models.UUIDField(unique=True, default=uuid.uuid4, verbose_name="Identifier")
location = models.ForeignKey(Location, verbose_name="Standort")
def __str__(self):
return '{}, {}'.format(self.location.name, self.uuid)
class Meta:
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, default=0.0, verbose_name="Normalpreis")
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]
client = self.client
location = client.location
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,
hh_start=location.happy_hour_start,
hh_end=location.happy_hour_end,
pphh=self.prize_hh,
)
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):
desk = models.ForeignKey(Desk, verbose_name="Tisch")
time_from = models.DateTimeField(verbose_name="Beginn")
time_to = models.DateTimeField(blank=True, null=True, verbose_name="Ende")
prize = models.DecimalField(max_digits=5, decimal_places=2, blank=True, null=True, verbose_name="Preis")
billed = models.BooleanField(default=False, verbose_name="Abgerechnet")
reporter_uuid = models.UUIDField(blank=True, null=True, verbose_name='Reporter UUID')
def __str__(self):
return '{}: {} -> {}, {}, {}'.format(self.desk, self.time_from, self.time_to, self.prize, self.billed)
class Meta:
ordering = ['-time_from']
verbose_name = "Buchhaltungseintrag"
verbose_name_plural = "Buchhaltungseinträge"
@receiver(post_save, sender=LocationData)
def test(sender, **kwargs):
request_process_location_data.delay()
@shared_task
def process_location_data():
data = LocationData.objects.filter(processed=False)
for ld in data:
cli = Client.objects.filter(uuid=ld.client_id, desks__desk_no=ld.desk_no)
if cli.count() < 1:
ld.processed = True
ld.error_msg = 'No client object found. Stopp processing! {}, {}'.format(ld.client_id, ld.desk_no)
ld.save()
log.error(ld.error_msg)
else:
cli = cli[0]
desk = cli.desks.filter(desk_no=ld.desk_no, enabled=True)
if desk.count() != 1:
ld.processed = True
ld.error_msg = 'No desk object found. Stopp processing! {}, {}'.format(ld.client_id, ld.desk_no)
ld.save()
log.error(ld.error_msg)
desk = desk[0]
ac = desk.accounting_set.order_by('time_from').reverse()
if ld.on_off:
acc = None
if ac.count() > 0 and ac[0].time_to is None:
log.error('Vorheriges Accounting nicht abgeschlossen: Desk_id {}, Accounting_id {}'
.format(desk.id, ac[0].id))
acc = ac[0]
if acc is None:
acc = Accounting(
desk=desk,
time_from=ld.tst,
)
acc.save()
ld.delete()
else:
if len(ac) > 0:
acc = ac[0]
acc.time_to = ld.tst
acc.prize = utils.get_prize_for(
start=acc.time_from,
end=ld.tst,
pph=desk.prize,
hh_start=cli.location.happy_hour_start,
hh_end=cli.location.happy_hour_end,
pphh=desk.prize_hh,
)
acc.reporter_uuid = ld.client_id
acc.save()
ld.delete()
else:
ld.processed = True
ld.error_msg = 'No existing accountings found. Stopp processing! {}, {}'.format(ld.client_id, ld.desk_no)
ld.save()
log.error(ld.error_msg)