Format counter

This commit is contained in:
Pierre Brunet 2017-06-12 09:47:24 +02:00
parent e7de8b2aec
commit d722efc40f
5 changed files with 290 additions and 227 deletions

View File

@ -36,4 +36,3 @@ admin.site.register(Selling)
admin.site.register(Permanency)
admin.site.register(CashRegisterSummary)
admin.site.register(Eticket)

View File

@ -22,13 +22,12 @@
#
#
from django.db import models, DataError
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.utils import timezone
from django.conf import settings
from django.core.urlresolvers import reverse
from django.forms import ValidationError
from django.contrib.sites.shortcuts import get_current_site
from django.utils.functional import cached_property
from datetime import timedelta, date
@ -43,6 +42,7 @@ from accounting.models import CurrencyField
from core.models import Group, User, Notification
from subscription.models import Subscription
class Customer(models.Model):
"""
This class extends a user to make a customer. It adds some basic customers informations, such as the accound ID, and
@ -55,7 +55,7 @@ class Customer(models.Model):
class Meta:
verbose_name = _('customer')
verbose_name_plural = _('customers')
ordering = ['account_id',]
ordering = ['account_id', ]
def __str__(self):
return "%s - %s" % (self.user.username, self.account_id)
@ -68,9 +68,9 @@ class Customer(models.Model):
def generate_account_id(number):
number = str(number)
letter = random.choice(string.ascii_lowercase)
while Customer.objects.filter(account_id=number+letter).exists():
while Customer.objects.filter(account_id=number + letter).exists():
letter = random.choice(string.ascii_lowercase)
return number+letter
return number + letter
def save(self, *args, **kwargs):
if self.amount < 0:
@ -118,6 +118,7 @@ class ProductType(models.Model):
def get_absolute_url(self):
return reverse('counter:producttype_list')
class Product(models.Model):
"""
This describes a product, with all its related informations
@ -156,13 +157,14 @@ class Product(models.Model):
def get_absolute_url(self):
return reverse('counter:product_list')
class Counter(models.Model):
name = models.CharField(_('name'), max_length=30)
club = models.ForeignKey(Club, related_name="counters", verbose_name=_("club"))
products = models.ManyToManyField(Product, related_name="counters", verbose_name=_("products"), blank=True)
type = models.CharField(_('counter type'),
max_length=255,
choices=[('BAR',_('Bar')), ('OFFICE',_('Office')), ('EBOUTIC',_('Eboutic'))])
choices=[('BAR', _('Bar')), ('OFFICE', _('Office')), ('EBOUTIC', _('Eboutic'))])
sellers = models.ManyToManyField(User, verbose_name=_('sellers'), related_name='counters', blank=True)
edit_groups = models.ManyToManyField(Group, related_name="editable_counters", blank=True)
view_groups = models.ManyToManyField(Group, related_name="viewable_counters", blank=True)
@ -173,7 +175,7 @@ class Counter(models.Model):
def __getattribute__(self, name):
if name == "edit_groups":
return Group.objects.filter(name=self.club.unix_name+settings.SITH_BOARD_SUFFIX).all()
return Group.objects.filter(name=self.club.unix_name + settings.SITH_BOARD_SUFFIX).all()
return object.__getattribute__(self, name)
def __str__(self):
@ -265,6 +267,7 @@ class Counter(models.Model):
"""
return [b.id for b in self.get_barmen_list()]
class Refilling(models.Model):
"""
Handle the refilling
@ -309,6 +312,7 @@ class Refilling(models.Model):
).save()
super(Refilling, self).save(*args, **kwargs)
class Selling(models.Model):
"""
Handle the sellings
@ -331,7 +335,7 @@ class Selling(models.Model):
def __str__(self):
return "Selling: %d x %s (%f) for %s" % (self.quantity, self.label,
self.quantity*self.unit_price, self.customer.user.get_display_name())
self.quantity * self.unit_price, self.customer.user.get_display_name())
def is_owned_by(self, user):
return user.is_owner(self.counter) and self.payment_method != "CARD"
@ -352,7 +356,7 @@ class Selling(models.Model):
"You bought an eticket for the event %(event)s.\nYou can download it on this page %(url)s."
) % {
'event': event,
'url':''.join((
'url': ''.join((
'<a href="',
self.customer.get_full_url(),
'">',
@ -414,7 +418,8 @@ class Selling(models.Model):
try:
if self.product.eticket:
self.send_mail_customer()
except: pass
except:
pass
Notification(
user=self.customer.user,
url=reverse('core:user_account_detail',
@ -424,6 +429,7 @@ class Selling(models.Model):
).save()
super(Selling, self).save(*args, **kwargs)
class Permanency(models.Model):
"""
This class aims at storing a traceability of who was barman where and when
@ -444,6 +450,7 @@ class Permanency(models.Model):
self.end.strftime("%Y-%m-%d %H:%M:%S") if self.end else "",
)
class CashRegisterSummary(models.Model):
user = models.ForeignKey(User, related_name="cash_summaries", verbose_name=_("user"))
counter = models.ForeignKey(Counter, related_name="cash_summaries", verbose_name=_("counter"))
@ -515,6 +522,7 @@ class CashRegisterSummary(models.Model):
def get_absolute_url(self):
return reverse('counter:cash_summary_list')
class CashRegisterSummaryItem(models.Model):
cash_summary = models.ForeignKey(CashRegisterSummary, related_name="items", verbose_name=_("cash summary"))
value = CurrencyField(_("value"))
@ -524,6 +532,7 @@ class CashRegisterSummaryItem(models.Model):
class Meta:
verbose_name = _("cash register summary item")
class Eticket(models.Model):
"""
Eticket can be linked to a product an allows PDF generation
@ -552,6 +561,6 @@ class Eticket(models.Model):
return user.is_in_group(settings.SITH_GROUP_COUNTER_ADMIN_ID)
def get_hash(self, string):
import hashlib, hmac
import hashlib
import hmac
return hmac.new(bytes(self.secret, 'utf-8'), bytes(string, 'utf-8'), hashlib.sha1).hexdigest()

View File

@ -24,7 +24,6 @@
import re
from pprint import pprint
from django.test import TestCase
from django.core.urlresolvers import reverse
from django.core.management import call_command

View File

@ -22,7 +22,7 @@
#
#
from django.conf.urls import url, include
from django.conf.urls import url
from counter.views import *

View File

@ -22,7 +22,7 @@
#
#
from django.shortcuts import render, get_object_or_404
from django.shortcuts import get_object_or_404
from django.http import Http404
from django.core.exceptions import PermissionDenied
from django.views.generic import ListView, DetailView, RedirectView, TemplateView
@ -31,7 +31,6 @@ from django.views.generic.edit import UpdateView, CreateView, DeleteView, Proces
from django.forms.models import modelform_factory
from django.forms import CheckboxSelectMultiple
from django.core.urlresolvers import reverse_lazy, reverse
from django.core.exceptions import PermissionDenied
from django.http import HttpResponseRedirect, HttpResponse
from django.utils import timezone
from django import forms
@ -43,16 +42,17 @@ import re
import pytz
from datetime import date, timedelta, datetime
from ajax_select.fields import AutoCompleteSelectField, AutoCompleteSelectMultipleField
from ajax_select import make_ajax_form, make_ajax_field
from ajax_select import make_ajax_field
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, CanCreateMixin, TabedViewMixin
from core.views.forms import SelectUser, LoginForm, SelectDate, SelectDateTime
from core.views import CanViewMixin, TabedViewMixin
from core.views.forms import LoginForm, SelectDate, SelectDateTime
from core.models import User
from subscription.models import Subscription
from counter.models import Counter, Customer, Product, Selling, Refilling, ProductType, \
CashRegisterSummary, CashRegisterSummaryItem, Eticket, Permanency
from accounting.models import CurrencyField
class CounterAdminMixin(View):
"""
This view is made to protect counter admin section
@ -72,13 +72,13 @@ class CounterAdminMixin(View):
return True
return False
def dispatch(self, request, *args, **kwargs):
if not (request.user.is_root or self._test_group(request.user)
or self._test_club(request.user)):
raise PermissionDenied
return super(CounterAdminMixin, self).dispatch(request, *args, **kwargs)
class GetUserForm(forms.Form):
"""
The Form class aims at providing a valid user_id field in its cleaned data, in order to pass it to some view,
@ -107,25 +107,29 @@ class GetUserForm(forms.Form):
cleaned_data['user'] = cus.user
return cleaned_data
class RefillForm(forms.ModelForm):
error_css_class = 'error'
required_css_class = 'required'
amount = forms.FloatField(min_value=0, widget=forms.NumberInput(attrs={'class':'focus'}))
amount = forms.FloatField(min_value=0, widget=forms.NumberInput(attrs={'class': 'focus'}))
class Meta:
model = Refilling
fields = ['amount', 'payment_method', 'bank']
class CounterTabsMixin(TabedViewMixin):
def get_tabs_title(self):
if hasattr(self.object, 'stock_owner') :
if hasattr(self.object, 'stock_owner'):
return self.object.stock_owner.counter
else:
return self.object
def get_list_of_tabs(self):
tab_list = []
tab_list.append({
'url': reverse_lazy('counter:details',
kwargs={'counter_id': self.object.stock_owner.counter.id if hasattr(self.object, 'stock_owner') else self.object.id }),
kwargs={'counter_id': self.object.stock_owner.counter.id if hasattr(self.object, 'stock_owner') else self.object.id}),
'slug': 'counter',
'name': _("Counter"),
})
@ -149,9 +153,11 @@ class CounterTabsMixin(TabedViewMixin):
'slug': 'take_items_from_stock',
'name': _("Take items from stock"),
})
except: pass # The counter just have no stock
except:
pass # The counter just have no stock
return tab_list
class CounterMain(CounterTabsMixin, CanViewMixin, DetailView, ProcessFormView, FormMixin):
"""
The public (barman) view
@ -167,7 +173,7 @@ class CounterMain(CounterTabsMixin, CanViewMixin, DetailView, ProcessFormView, F
if self.object.type == "BAR" and not ('counter_token' in self.request.session.keys() and
self.request.session['counter_token'] == self.object.token): # Check the token to avoid the bar to be stolen
return HttpResponseRedirect(reverse_lazy('counter:details', args=self.args,
kwargs={'counter_id': self.object.id})+'?bad_location')
kwargs={'counter_id': self.object.id}) + '?bad_location')
return super(CounterMain, self).post(request, *args, **kwargs)
def get_context_data(self, **kwargs):
@ -210,6 +216,7 @@ class CounterMain(CounterTabsMixin, CanViewMixin, DetailView, ProcessFormView, F
def get_success_url(self):
return reverse_lazy('counter:click', args=self.args, kwargs=self.kwargs)
class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
"""
The click view
@ -228,7 +235,7 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
raise Http404
if obj.type == "BAR":
if not ('counter_token' in request.session.keys() and
request.session['counter_token'] == obj.token) or len(obj.get_barmen_list())<1:
request.session['counter_token'] == obj.token) or len(obj.get_barmen_list()) < 1:
raise PermissionDenied
else:
if not request.user.is_authenticated():
@ -263,7 +270,7 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
if self.object.type == "BAR" and not ('counter_token' in self.request.session.keys() and
self.request.session['counter_token'] == self.object.token): # Also check the token to avoid the bar to be stolen
return HttpResponseRedirect(reverse_lazy('counter:details', args=self.args,
kwargs={'counter_id': self.object.id})+'?bad_location')
kwargs={'counter_id': self.object.id}) + '?bad_location')
if 'basket' not in request.session.keys():
request.session['basket'] = {}
request.session['basket_total'] = 0
@ -312,7 +319,7 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
def sum_basket(self, request):
total = 0
for pid,infos in request.session['basket'].items():
for pid, infos in request.session['basket'].items():
total += infos['price'] * infos['qty']
return total / 100
@ -323,7 +330,7 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
except:
return 0
def add_product(self, request, q = 1, p=None):
def add_product(self, request, q=1, p=None):
"""
Add a product to the basket
q is the quantity passed as integer
@ -349,7 +356,7 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
total_qty_mod_6 = self.get_total_quantity_for_pid(request, pid) % 6
bq = int((total_qty_mod_6 + q) / 6) # Integer division
q -= bq
if self.customer.amount < (total + round(q*float(price),2)): # Check for enough money
if self.customer.amount < (total + round(q * float(price), 2)): # Check for enough money
request.session['not_enough'] = True
return False
if product.limit_age >= 18 and not self.customer.user.date_of_birth:
@ -368,7 +375,7 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
request.session['basket'][pid]['qty'] += q
request.session['basket'][pid]['bonus_qty'] += bq
else: # or create if not
request.session['basket'][pid] = {'qty': q, 'price': int(price*100), 'bonus_qty': bq}
request.session['basket'][pid] = {'qty': q, 'price': int(price * 100), 'bonus_qty': bq}
request.session.modified = True
return True
@ -414,7 +421,7 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
""" Finish the click session, and validate the basket """
with transaction.atomic():
request.session['last_basket'] = []
for pid,infos in request.session['basket'].items():
for pid, infos in request.session['basket'].items():
# This duplicates code for DB optimization (prevent to load many times the same object)
p = Product.objects.filter(pk=pid).first()
if self.is_barman_price():
@ -423,7 +430,7 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
uprice = p.selling_price
if uprice * infos['qty'] > self.customer.amount:
raise DataError(_("You have not enough money to buy all the basket"))
request.session['last_basket'].append("%d x %s" % (infos['qty']+infos['bonus_qty'], p.name))
request.session['last_basket'].append("%d x %s" % (infos['qty'] + infos['bonus_qty'], p.name))
s = Selling(label=p.name, product=p, club=p.club, counter=self.object, unit_price=uprice,
quantity=infos['qty'], seller=self.operator, customer=self.customer)
s.save()
@ -470,6 +477,7 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
kwargs['categories'] = ProductType.objects.all()
return kwargs
class CounterLogin(RedirectView):
"""
Handle the login of a barman
@ -477,6 +485,7 @@ class CounterLogin(RedirectView):
Logged barmen are stored in the Permanency model
"""
permanent = False
def post(self, request, *args, **kwargs):
"""
Register the logged user as barman for this counter
@ -499,10 +508,12 @@ class CounterLogin(RedirectView):
return super(CounterLogin, self).post(request, *args, **kwargs)
def get_redirect_url(self, *args, **kwargs):
return reverse_lazy('counter:details', args=args, kwargs=kwargs)+"?"+'&'.join(self.errors)
return reverse_lazy('counter:details', args=args, kwargs=kwargs) + "?" + '&'.join(self.errors)
class CounterLogout(RedirectView):
permanent = False
def post(self, request, *args, **kwargs):
"""
Unregister the user from the barman
@ -515,7 +526,8 @@ class CounterLogout(RedirectView):
def get_redirect_url(self, *args, **kwargs):
return reverse_lazy('counter:details', args=args, kwargs=kwargs)
## Counter admin views
# Counter admin views
class CounterAdminTabsMixin(TabedViewMixin):
tabs_title = _("Counter administration")
@ -562,6 +574,7 @@ class CounterAdminTabsMixin(TabedViewMixin):
},
]
class CounterListView(CounterAdminTabsMixin, CanViewMixin, ListView):
"""
A list view for the admins
@ -570,6 +583,7 @@ class CounterListView(CounterAdminTabsMixin, CanViewMixin, ListView):
template_name = 'counter/counter_list.jinja'
current_tab = "counters"
class CounterEditForm(forms.ModelForm):
class Meta:
model = Counter
@ -577,6 +591,7 @@ class CounterEditForm(forms.ModelForm):
sellers = make_ajax_field(Counter, 'sellers', 'users', help_text="")
products = make_ajax_field(Counter, 'products', 'products', help_text="")
class CounterEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
"""
Edit a counter's main informations (for the counter's manager)
@ -595,6 +610,7 @@ class CounterEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
def get_success_url(self):
return reverse_lazy('counter:admin', kwargs={'counter_id': self.object.id})
class CounterEditPropView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
"""
Edit a counter's main informations (for the counter's admin)
@ -605,16 +621,18 @@ class CounterEditPropView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
template_name = 'core/edit.jinja'
current_tab = "counters"
class CounterCreateView(CounterAdminTabsMixin, CounterAdminMixin, CreateView):
"""
Create a counter (for the admins)
"""
model = Counter
form_class = modelform_factory(Counter, fields=['name', 'club', 'type', 'products'],
widgets={'products':CheckboxSelectMultiple})
widgets={'products': CheckboxSelectMultiple})
template_name = 'core/create.jinja'
current_tab = "counters"
class CounterDeleteView(CounterAdminTabsMixin, CounterAdminMixin, DeleteView):
"""
Delete a counter (for the admins)
@ -627,6 +645,7 @@ class CounterDeleteView(CounterAdminTabsMixin, CounterAdminMixin, DeleteView):
# Product management
class ProductTypeListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
"""
A list view for the admins
@ -635,6 +654,7 @@ class ProductTypeListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
template_name = 'counter/producttype_list.jinja'
current_tab = "product_types"
class ProductTypeCreateView(CounterAdminTabsMixin, CounterAdminMixin, CreateView):
"""
A create view for the admins
@ -644,6 +664,7 @@ class ProductTypeCreateView(CounterAdminTabsMixin, CounterAdminMixin, CreateView
template_name = 'core/create.jinja'
current_tab = "products"
class ProductTypeEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
"""
An edit view for the admins
@ -654,6 +675,7 @@ class ProductTypeEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
pk_url_kwarg = "type_id"
current_tab = "products"
class ProductArchivedListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
"""
A list view for the admins
@ -664,6 +686,7 @@ class ProductArchivedListView(CounterAdminTabsMixin, CounterAdminMixin, ListView
ordering = ['name']
current_tab = "archive"
class ProductListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
"""
A list view for the admins
@ -674,6 +697,7 @@ class ProductListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
ordering = ['name']
current_tab = "products"
class ProductEditForm(forms.ModelForm):
class Meta:
model = Product
@ -702,6 +726,7 @@ class ProductEditForm(forms.ModelForm):
c.save()
return ret
class ProductCreateView(CounterAdminTabsMixin, CounterAdminMixin, CreateView):
"""
A create view for the admins
@ -711,6 +736,7 @@ class ProductCreateView(CounterAdminTabsMixin, CounterAdminMixin, CreateView):
template_name = 'core/create.jinja'
current_tab = "products"
class ProductEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
"""
An edit view for the admins
@ -721,6 +747,7 @@ class ProductEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
template_name = 'core/edit.jinja'
current_tab = "products"
class RefillingDeleteView(DeleteView):
"""
Delete a refilling (for the admins)
@ -745,6 +772,7 @@ class RefillingDeleteView(DeleteView):
return super(RefillingDeleteView, self).dispatch(request, *args, **kwargs)
raise PermissionDenied
class SellingDeleteView(DeleteView):
"""
Delete a selling (for the admins)
@ -771,6 +799,7 @@ class SellingDeleteView(DeleteView):
# Cash register summaries
class CashRegisterSummaryForm(forms.Form):
"""
Provide the cash summary form
@ -839,30 +868,46 @@ class CashRegisterSummaryForm(forms.Form):
summary.save()
summary.items.all().delete()
# Cash
if cd['ten_cents']: CashRegisterSummaryItem(cash_summary=summary, value=0.1, quantity=cd['ten_cents']).save()
if cd['twenty_cents']: CashRegisterSummaryItem(cash_summary=summary, value=0.2, quantity=cd['twenty_cents']).save()
if cd['fifty_cents']: CashRegisterSummaryItem(cash_summary=summary, value=0.5, quantity=cd['fifty_cents']).save()
if cd['one_euro']: CashRegisterSummaryItem(cash_summary=summary, value=1, quantity=cd['one_euro']).save()
if cd['two_euros']: CashRegisterSummaryItem(cash_summary=summary, value=2, quantity=cd['two_euros']).save()
if cd['five_euros']: CashRegisterSummaryItem(cash_summary=summary, value=5, quantity=cd['five_euros']).save()
if cd['ten_euros']: CashRegisterSummaryItem(cash_summary=summary, value=10, quantity=cd['ten_euros']).save()
if cd['twenty_euros']: CashRegisterSummaryItem(cash_summary=summary, value=20, quantity=cd['twenty_euros']).save()
if cd['fifty_euros']: CashRegisterSummaryItem(cash_summary=summary, value=50, quantity=cd['fifty_euros']).save()
if cd['hundred_euros']: CashRegisterSummaryItem(cash_summary=summary, value=100, quantity=cd['hundred_euros']).save()
if cd['ten_cents']:
CashRegisterSummaryItem(cash_summary=summary, value=0.1, quantity=cd['ten_cents']).save()
if cd['twenty_cents']:
CashRegisterSummaryItem(cash_summary=summary, value=0.2, quantity=cd['twenty_cents']).save()
if cd['fifty_cents']:
CashRegisterSummaryItem(cash_summary=summary, value=0.5, quantity=cd['fifty_cents']).save()
if cd['one_euro']:
CashRegisterSummaryItem(cash_summary=summary, value=1, quantity=cd['one_euro']).save()
if cd['two_euros']:
CashRegisterSummaryItem(cash_summary=summary, value=2, quantity=cd['two_euros']).save()
if cd['five_euros']:
CashRegisterSummaryItem(cash_summary=summary, value=5, quantity=cd['five_euros']).save()
if cd['ten_euros']:
CashRegisterSummaryItem(cash_summary=summary, value=10, quantity=cd['ten_euros']).save()
if cd['twenty_euros']:
CashRegisterSummaryItem(cash_summary=summary, value=20, quantity=cd['twenty_euros']).save()
if cd['fifty_euros']:
CashRegisterSummaryItem(cash_summary=summary, value=50, quantity=cd['fifty_euros']).save()
if cd['hundred_euros']:
CashRegisterSummaryItem(cash_summary=summary, value=100, quantity=cd['hundred_euros']).save()
# Checks
if cd['check_1_quantity']: CashRegisterSummaryItem(cash_summary=summary, value=cd['check_1_value'],
if cd['check_1_quantity']:
CashRegisterSummaryItem(cash_summary=summary, value=cd['check_1_value'],
quantity=cd['check_1_quantity'], check=True).save()
if cd['check_2_quantity']: CashRegisterSummaryItem(cash_summary=summary, value=cd['check_2_value'],
if cd['check_2_quantity']:
CashRegisterSummaryItem(cash_summary=summary, value=cd['check_2_value'],
quantity=cd['check_2_quantity'], check=True).save()
if cd['check_3_quantity']: CashRegisterSummaryItem(cash_summary=summary, value=cd['check_3_value'],
if cd['check_3_quantity']:
CashRegisterSummaryItem(cash_summary=summary, value=cd['check_3_value'],
quantity=cd['check_3_quantity'], check=True).save()
if cd['check_4_quantity']: CashRegisterSummaryItem(cash_summary=summary, value=cd['check_4_value'],
if cd['check_4_quantity']:
CashRegisterSummaryItem(cash_summary=summary, value=cd['check_4_value'],
quantity=cd['check_4_quantity'], check=True).save()
if cd['check_5_quantity']: CashRegisterSummaryItem(cash_summary=summary, value=cd['check_5_value'],
if cd['check_5_quantity']:
CashRegisterSummaryItem(cash_summary=summary, value=cd['check_5_value'],
quantity=cd['check_5_quantity'], check=True).save()
if summary.items.count() < 1:
summary.delete()
class CounterLastOperationsView(CounterTabsMixin, CanViewMixin, DetailView):
"""
Provide the last operations to allow barmen to delete them
@ -881,7 +926,7 @@ class CounterLastOperationsView(CounterTabsMixin, CanViewMixin, DetailView):
request.session['counter_token'] and # check if not null for counters that have no token set
Counter.objects.filter(token=request.session['counter_token']).exists()):
return super(CounterLastOperationsView, self).dispatch(request, *args, **kwargs)
return HttpResponseRedirect(reverse('counter:details', kwargs={'counter_id': self.object.id})+'?bad_location')
return HttpResponseRedirect(reverse('counter:details', kwargs={'counter_id': self.object.id}) + '?bad_location')
def get_context_data(self, **kwargs):
"""Add form to the context """
@ -891,6 +936,7 @@ class CounterLastOperationsView(CounterTabsMixin, CanViewMixin, DetailView):
kwargs['last_sellings'] = self.object.sellings.filter(date__gte=threshold).order_by('-id')[:20]
return kwargs
class CounterCashSummaryView(CounterTabsMixin, CanViewMixin, DetailView):
"""
Provide the cash summary form
@ -909,7 +955,7 @@ class CounterCashSummaryView(CounterTabsMixin, CanViewMixin, DetailView):
request.session['counter_token'] and # check if not null for counters that have no token set
Counter.objects.filter(token=request.session['counter_token']).exists()):
return super(CounterCashSummaryView, self).dispatch(request, *args, **kwargs)
return HttpResponseRedirect(reverse('counter:details', kwargs={'counter_id': self.object.id})+'?bad_location')
return HttpResponseRedirect(reverse('counter:details', kwargs={'counter_id': self.object.id}) + '?bad_location')
def get(self, request, *args, **kwargs):
self.object = self.get_object()
@ -933,6 +979,7 @@ class CounterCashSummaryView(CounterTabsMixin, CanViewMixin, DetailView):
kwargs['form'] = self.form
return kwargs
class CounterActivityView(DetailView):
"""
Show the bar activity
@ -941,6 +988,7 @@ class CounterActivityView(DetailView):
pk_url_kwarg = "counter_id"
template_name = 'counter/activity.jinja'
class CounterStatView(DetailView, CounterAdminMixin):
"""
Show the bar stats
@ -957,14 +1005,14 @@ class CounterStatView(DetailView, CounterAdminMixin):
kwargs['User'] = User
semester_start = Subscription.compute_start(d=date.today(), duration=3)
kwargs['total_sellings'] = Selling.objects.filter(date__gte=semester_start,
counter=self.object).aggregate(total_sellings=Sum(F('quantity')*F('unit_price'),
counter=self.object).aggregate(total_sellings=Sum(F('quantity') * F('unit_price'),
output_field=CurrencyField()))['total_sellings']
kwargs['top'] = Selling.objects.values('customer__user').annotate(
selling_sum=Sum(
Case(When(counter=self.object,
date__gte=semester_start,
unit_price__gt=0,
then=F('unit_price')*F('quantity')),
then=F('unit_price') * F('quantity')),
output_field=CurrencyField()
)
)
@ -973,7 +1021,7 @@ class CounterStatView(DetailView, CounterAdminMixin):
perm_sum=Sum(
Case(When(counter=self.object,
end__gt=datetime(year=1999, month=1, day=1),
then=F('end')-F('start')),
then=F('end') - F('start')),
output_field=models.DateTimeField()
)
)
@ -983,14 +1031,14 @@ class CounterStatView(DetailView, CounterAdminMixin):
Case(When(counter=self.object,
start__gt=semester_start,
end__gt=datetime(year=1999, month=1, day=1),
then=F('end')-F('start')),
then=F('end') - F('start')),
output_field=models.DateTimeField()
)
)
).exclude(perm_sum=None).order_by('-perm_sum').all()[:100]
kwargs['sith_date']=settings.SITH_START_DATE[0]
kwargs['semester_start']=semester_start
kwargs['sith_date'] = settings.SITH_START_DATE[0]
kwargs['semester_start'] = semester_start
return kwargs
def dispatch(self, request, *args, **kwargs):
@ -1003,6 +1051,7 @@ class CounterStatView(DetailView, CounterAdminMixin):
return super(CanEditMixin, self).dispatch(request, *args, **kwargs)
raise PermissionDenied
class CashSummaryEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
"""Edit cash summaries"""
model = CashRegisterSummary
@ -1015,10 +1064,12 @@ class CashSummaryEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView)
def get_success_url(self):
return reverse('counter:cash_summary_list')
class CashSummaryFormBase(forms.Form):
begin_date = forms.DateTimeField(['%Y-%m-%d %H:%M:%S'], label=_("Begin date"), required=False, widget=SelectDateTime)
end_date = forms.DateTimeField(['%Y-%m-%d %H:%M:%S'], label=_("End date"), required=False, widget=SelectDateTime)
class CashSummaryListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
"""Display a list of cash summaries"""
model = CashRegisterSummary
@ -1056,6 +1107,7 @@ class CashSummaryListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
kwargs['refilling_sums'][c.name] = sum([s.amount for s in refillings.all()])
return kwargs
class InvoiceCallView(CounterAdminTabsMixin, CounterAdminMixin, TemplateView):
template_name = 'counter/invoices_call.jinja'
current_tab = 'invoices_call'
@ -1069,24 +1121,25 @@ class InvoiceCallView(CounterAdminTabsMixin, CounterAdminMixin, TemplateView):
try:
start_date = datetime.strptime(self.request.GET['month'], '%Y-%m')
except:
start_date = datetime(year=timezone.now().year, month=(timezone.now().month+10)%12+1, day=1)
start_date = datetime(year=timezone.now().year, month=(timezone.now().month + 10) % 12 + 1, day=1)
start_date = start_date.replace(tzinfo=pytz.UTC)
end_date = (start_date + timedelta(days=32)).replace(day=1, hour=0, minute=0, microsecond=0)
from django.db.models import Sum, Case, When, F, DecimalField
kwargs['sum_cb'] = sum([r.amount for r in Refilling.objects.filter(payment_method='CARD', is_validated=True,
date__gte=start_date, date__lte=end_date)])
kwargs['sum_cb'] += sum([s.quantity*s.unit_price for s in Selling.objects.filter(payment_method='CARD', is_validated=True,
kwargs['sum_cb'] += sum([s.quantity * s.unit_price for s in Selling.objects.filter(payment_method='CARD', is_validated=True,
date__gte=start_date, date__lte=end_date)])
kwargs['start_date'] = start_date
kwargs['sums'] = Selling.objects.values('club__name').annotate(selling_sum=Sum(
Case(When(date__gte=start_date,
date__lt=end_date,
then=F('unit_price')*F('quantity')),
then=F('unit_price') * F('quantity')),
output_field=CurrencyField()
)
)).exclude(selling_sum=None).order_by('-selling_sum')
return kwargs
class EticketListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
"""
A list view for the admins
@ -1096,6 +1149,7 @@ class EticketListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
ordering = ['id']
current_tab = "etickets"
class EticketForm(forms.ModelForm):
class Meta:
model = Eticket
@ -1105,6 +1159,7 @@ class EticketForm(forms.ModelForm):
}
product = AutoCompleteSelectField('products', show_help_text=False, label=_("Product"), required=True)
class EticketCreateView(CounterAdminTabsMixin, CounterAdminMixin, CreateView):
"""
Create an eticket
@ -1114,6 +1169,7 @@ class EticketCreateView(CounterAdminTabsMixin, CounterAdminMixin, CreateView):
form_class = EticketForm
current_tab = "etickets"
class EticketEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
"""
Edit an eticket
@ -1124,6 +1180,7 @@ class EticketEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
pk_url_kwarg = "eticket_id"
current_tab = "etickets"
class EticketPDFView(CanViewMixin, DetailView):
"""
Display the PDF of an eticket
@ -1180,7 +1237,7 @@ class EticketPDFView(CanViewMixin, DetailView):
bounds = qrcode.getBounds()
width = bounds[2] - bounds[0]
height = bounds[3] - bounds[1]
d = Drawing(260, 260, transform=[260./width, 0, 0, 260./height, 0, 0])
d = Drawing(260, 260, transform=[260. / width, 0, 0, 260. / height, 0, 0])
d.add(qrcode)
renderPDF.draw(d, p, 10.5 * cm - 130, 6.1 * cm)
p.drawCentredString(10.5 * cm, 6 * cm, code)
@ -1195,4 +1252,3 @@ class EticketPDFView(CanViewMixin, DetailView):
p.showPage()
p.save()
return response