views.py 9.92 KB
Newer Older
Skia's avatar
Skia committed
1
from django.shortcuts import render
Skia's avatar
Skia committed
2
from django.views.generic import ListView, DetailView, RedirectView
Skia's avatar
Skia committed
3
from django.views.generic.edit import UpdateView, CreateView, DeleteView, ProcessFormView, FormMixin
Skia's avatar
Skia committed
4
5
6
from django.forms.models import modelform_factory
from django.forms import CheckboxSelectMultiple
from django.core.urlresolvers import reverse_lazy
Skia's avatar
Skia committed
7
from django.contrib.auth.forms import AuthenticationForm
8
from django.http import HttpResponseRedirect
Skia's avatar
Skia committed
9
from django.utils import timezone
Skia's avatar
Skia committed
10
from django import forms
Skia's avatar
Skia committed
11

Skia's avatar
Skia committed
12
13

from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin
Skia's avatar
Skia committed
14
from subscription.models import Subscriber
Skia's avatar
Skia committed
15
from counter.models import Counter, Customer, Product, Selling, Refilling
Skia's avatar
Skia committed
16

Skia's avatar
Skia committed
17
class GetUserForm(forms.Form):
18
19
20
21
22
23
24
25
26
27
    """
    The Form class aims at providing a valid user_id field in its cleaned data, in order to pass it to some view,
    reverse function, or any other use.

    The Form implements a nice JS widget allowing the user to type a customer account id, or search the database with
    some nickname, first name, or last name (TODO)
    """
    code = forms.CharField(label="Code", max_length=64, required=False)
    id = forms.IntegerField(label="ID", required=False)
# TODO: add a nice JS widget to search for users
Skia's avatar
Skia committed
28

29
30
31
32
33
34
35
36
37
38
39
40
41
    def clean(self):
        cleaned_data = super(GetUserForm, self).clean()
        user = None
        if cleaned_data['code'] != "":
            user = Customer.objects.filter(account_id=cleaned_data['code']).first()
        elif cleaned_data['id'] is not None:
            user = Customer.objects.filter(user=cleaned_data['id']).first()
        if user is None:
            raise forms.ValidationError("User not found")
        cleaned_data['user_id'] = user.user.id
        return cleaned_data

class CounterMain(DetailView, ProcessFormView, FormMixin):
Skia's avatar
Skia committed
42
43
44
    """
    The public (barman) view
    """
Skia's avatar
Skia committed
45
    model = Counter
Skia's avatar
Skia committed
46
    template_name = 'counter/counter_main.jinja'
Skia's avatar
Skia committed
47
    pk_url_kwarg = "counter_id"
48
    form_class = GetUserForm # Form to enter a client code and get the corresponding user id
Skia's avatar
Skia committed
49

Skia's avatar
Skia committed
50
    def get_context_data(self, **kwargs):
Skia's avatar
Skia committed
51
        """
52
        We handle here the login form for the barman
Skia's avatar
Skia committed
53
54
55

        Also handle the timeout
        """
56
57
        if self.request.method == 'POST':
            self.object = self.get_object()
Skia's avatar
Skia committed
58
        kwargs = super(CounterMain, self).get_context_data(**kwargs)
59
# TODO: make some checks on the counter type, in order not to make the AuthenticationForm if there is no need to
Skia's avatar
Skia committed
60
61
        kwargs['login_form'] = AuthenticationForm()
        kwargs['form'] = self.get_form()
62
        kwargs['barmen'] = Counter.get_barmen_list(self.object.id)
Skia's avatar
Skia committed
63
64
        return kwargs

65
66
67
68
69
70
71
72
73
74
75
    def form_valid(self, form):
        """
        We handle here the redirection, passing the user id of the asked customer
        """
        self.kwargs['user_id'] = form.cleaned_data['user_id']
        return super(CounterMain, self).form_valid(form)


    def get_success_url(self):
        return reverse_lazy('counter:click', args=self.args, kwargs=self.kwargs)

76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
class BasketForm(forms.Form):
    def __init__(self, *args, **kwargs):
        print(kwargs)
        super(BasketForm, self).__init__(*args, **kwargs)
        for p in kwargs['initial']['counter'].products.all(): # TODO: filter on the allowed products for this counter
            self.fields[p.id] = forms.IntegerField(label=p.name, required=False)
            # TODO ^: add some extra infos for the products (price, or ID to load infos dynamically)

    def clean(self):
        cleaned_data = super(BasketForm, self).clean()
        total = 0
        for pid,q in cleaned_data.items():
            p = Product.objects.filter(id=pid).first()
            total += (q or 0)*p.selling_price
        print(total)

92
class CounterClick(DetailView):
Skia's avatar
Skia committed
93
94
    """
    The click view
95
96
    This is a detail view not to have to worry about loading the counter
    Everything is made by hand in the post method
Skia's avatar
Skia committed
97
    """
98
    model = Counter
Skia's avatar
Skia committed
99
100
    template_name = 'counter/counter_click.jinja'
    pk_url_kwarg = "counter_id"
101
102

    def get(self, request, *args, **kwargs):
103
        """Simple get view"""
104
        self.customer = Customer.objects.filter(user__id=self.kwargs['user_id']).first()
105
106
107
108
        ret = super(CounterClick, self).get(request, *args, **kwargs)
        if len(Counter.get_barmen_list(self.object.id)) < 1: # Check that at least one barman is logged in
            return self.cancel(request) # Otherwise, go to main view
        if 'basket' not in request.session.keys(): # Init the basket session entry
109
            request.session['basket'] = {}
110
        return ret
Skia's avatar
Skia committed
111
112

    def post(self, request, *args, **kwargs):
113
        """ Handle the many possibilities of the post request """
114
115
        self.object = self.get_object()
        self.customer = Customer.objects.filter(user__id=self.kwargs['user_id']).first()
116
117
        if len(Counter.get_barmen_list(self.object.id)) < 1: # Check that at least one barman is logged in
            return self.cancel(request)
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
        if 'basket' not in request.session.keys():
            request.session['basket'] = {}

        if 'add_product' in request.POST['action']:
            self.add_product(request)
        elif 'del_product' in request.POST['action']:
            self.del_product(request)
        elif 'cancel' in request.POST['action']:
            return self.cancel(request)
        elif 'finish' in request.POST['action']:
            return self.finish(request)
        context = self.get_context_data(object=self.object)
        return self.render_to_response(context)

    def add_product(self, request):
        """ Add a product to the basket """
134
135
136
        pid = str(request.POST['product_id'])
        if pid in request.session['basket']:
            request.session['basket'][pid] += 1
137
        else:
138
            request.session['basket'][pid] = 1
139
140
141
142
        request.session.modified = True

    def del_product(self, request):
        """ Delete a product from the basket """
143
144
145
146
147
        pid = str(request.POST['product_id'])
        if pid in request.session['basket']:
            request.session['basket'][pid] -= 1
            if request.session['basket'][pid] <= 0:
                del request.session['basket'][pid]
148
        else:
149
            request.session['basket'][pid] = 0
150
151
152
153
        request.session.modified = True

    def finish(self, request):
        """ Finish the click session, and validate the basket """
Skia's avatar
Skia committed
154
155
156
157
158
        for pid,qty in request.session['basket'].items():
            p = Product.objects.filter(pk=pid).first()
            s = Selling(product=p, counter=self.object, unit_price=p.selling_price,
                   quantity=qty, seller=Counter.get_random_barman(self.object.id), customer=self.customer)
            s.save()
159
160
161
162
163
164
165
        kwargs = {'counter_id': self.object.id}
        del request.session['basket']
        return HttpResponseRedirect(reverse_lazy('counter:details', args=self.args, kwargs=kwargs))

    def cancel(self, request):
        """ Cancel the click session """
        kwargs = {'counter_id': self.object.id}
166
        request.session.pop('basket', None)
167
        return HttpResponseRedirect(reverse_lazy('counter:details', args=self.args, kwargs=kwargs))
168
169

    def get_context_data(self, **kwargs):
170
        """ Add customer to the context """
171
172
173
174
        kwargs = super(CounterClick, self).get_context_data(**kwargs)
        kwargs['customer'] = self.customer
        return kwargs

Skia's avatar
Skia committed
175
class CounterLogin(RedirectView):
Skia's avatar
Skia committed
176
177
178
179
180
    """
    Handle the login of a barman

    Logged barmen are stored in the class-wide variable 'barmen_session', in the Counter model
    """
Skia's avatar
Skia committed
181
    permanent = False
Skia's avatar
Skia committed
182
    def post(self, request, *args, **kwargs):
Skia's avatar
Skia committed
183
184
185
        """
        Register the logged user as barman for this counter
        """
Skia's avatar
Skia committed
186
187
        self.counter_id = kwargs['counter_id']
        form = AuthenticationForm(request, data=request.POST)
Skia's avatar
Skia committed
188
        if form.is_valid():
Skia's avatar
Skia committed
189
            user = Subscriber.objects.filter(username=form.cleaned_data['username']).first()
Skia's avatar
Skia committed
190
            if self.counter_id not in Counter.barmen_session.keys():
Skia's avatar
Skia committed
191
                Counter.barmen_session[self.counter_id] = {'users': {user.id}, 'time': timezone.now()}
Skia's avatar
Skia committed
192
            else:
Skia's avatar
Skia committed
193
                Counter.barmen_session[self.counter_id]['users'].add(user.id)
Skia's avatar
Skia committed
194
195
196
197
198
199
200
201
202
203
        else:
            print("Error logging the barman") # TODO handle that nicely
        return super(CounterLogin, self).post(request, *args, **kwargs)

    def get_redirect_url(self, *args, **kwargs):
        return reverse_lazy('counter:details', args=args, kwargs=kwargs)

class CounterLogout(RedirectView):
    permanent = False
    def post(self, request, *args, **kwargs):
Skia's avatar
Skia committed
204
205
206
        """
        Unregister the user from the barman
        """
Skia's avatar
Skia committed
207
        self.counter_id = kwargs['counter_id']
Skia's avatar
Skia committed
208
        Counter.barmen_session[str(self.counter_id)]['users'].remove(int(request.POST['user_id']))
Skia's avatar
Skia committed
209
210
211
212
        return super(CounterLogout, self).post(request, *args, **kwargs)

    def get_redirect_url(self, *args, **kwargs):
        return reverse_lazy('counter:details', args=args, kwargs=kwargs)
Skia's avatar
Skia committed
213

214
215
## Counter admin views

Skia's avatar
Skia committed
216
217
218
219
220
221
222
class CounterListView(CanViewMixin, ListView):
    """
    A list view for the admins
    """
    model = Counter
    template_name = 'counter/counter_list.jinja'

Skia's avatar
Skia committed
223
224
class CounterEditView(CanEditMixin, UpdateView):
    """
Skia's avatar
Skia committed
225
    Edit a counter's main informations (for the counter's admin)
Skia's avatar
Skia committed
226
227
228
229
230
231
232
233
234
    """
    model = Counter
    form_class = modelform_factory(Counter, fields=['name', 'club', 'type', 'products'],
            widgets={'products':CheckboxSelectMultiple})
    pk_url_kwarg = "counter_id"
    template_name = 'counter/counter_edit.jinja'

class CounterCreateView(CanEditMixin, CreateView):
    """
Skia's avatar
Skia committed
235
    Create a counter (for the admins)
Skia's avatar
Skia committed
236
237
238
239
240
241
242
243
    """
    model = Counter
    form_class = modelform_factory(Counter, fields=['name', 'club', 'type', 'products'],
            widgets={'products':CheckboxSelectMultiple})
    template_name = 'counter/counter_edit.jinja'

class CounterDeleteView(CanEditMixin, DeleteView):
    """
Skia's avatar
Skia committed
244
    Delete a counter (for the admins)
Skia's avatar
Skia committed
245
246
247
248
249
    """
    model = Counter
    pk_url_kwarg = "counter_id"
    template_name = 'core/delete_confirm.jinja'
    success_url = reverse_lazy('counter:admin_list')
Skia's avatar
Skia committed
250
251