# -*- coding:utf-8 -* # # Copyright 2016,2017 # - Guillaume "Lo-J" Renaud # - Skia # # Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM, # http://ae.utbm.fr. # # This program is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License a published by the Free Software # Foundation; either version 3 of the License, or (at your option) any later # version. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along with # this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple # Place - Suite 330, Boston, MA 02111-1307, USA. # # from collections import OrderedDict from datetime import datetime, timedelta from django.utils import timezone from django.shortcuts import render, get_object_or_404 from django.views.generic import ListView, DetailView, RedirectView, TemplateView from django.views.generic.edit import ( UpdateView, CreateView, DeleteView, ProcessFormView, FormMixin, BaseFormView, ) from django.utils.translation import gettext_lazy as _ from django import forms from django.http import HttpResponseRedirect, HttpResponse from django.forms.models import modelform_factory from django.urls import reverse_lazy, reverse from django.db import transaction, DataError from core.views import ( CanViewMixin, CanEditMixin, CanEditPropMixin, CanCreateMixin, TabedViewMixin, ) from counter.views import CounterAdminTabsMixin, CounterTabsMixin from counter.models import Counter, ProductType from stock.models import Stock, StockItem, ShoppingList, ShoppingListItem class StockItemList(CounterAdminTabsMixin, CanCreateMixin, ListView): """ The stockitems list view for the counter owner """ model = Stock template_name = "stock/stock_item_list.jinja" pk_url_kwarg = "stock_id" current_tab = "stocks" def get_context_data(self): ret = super(StockItemList, self).get_context_data() if "stock_id" in self.kwargs.keys(): ret["stock"] = Stock.objects.filter(id=self.kwargs["stock_id"]).first() return ret class StockListView(CounterAdminTabsMixin, CanViewMixin, ListView): """ A list view for the admins """ model = Stock template_name = "stock/stock_list.jinja" current_tab = "stocks" class StockEditForm(forms.ModelForm): """ A form to change stock's characteristics """ class Meta: model = Stock fields = ["name", "counter"] def __init__(self, *args, **kwargs): super(StockEditForm, self).__init__(*args, **kwargs) def save(self, *args, **kwargs): return super(StockEditForm, self).save(*args, **kwargs) class StockEditView(CounterAdminTabsMixin, CanEditPropMixin, UpdateView): """ An edit view for the stock """ model = Stock form_class = modelform_factory(Stock, fields=["name", "counter"]) pk_url_kwarg = "stock_id" template_name = "core/edit.jinja" current_tab = "stocks" class StockItemEditView(CounterAdminTabsMixin, CanEditPropMixin, UpdateView): """ An edit view for a stock item """ model = StockItem form_class = modelform_factory( StockItem, fields=[ "name", "unit_quantity", "effective_quantity", "minimal_quantity", "type", "stock_owner", ], ) pk_url_kwarg = "item_id" template_name = "core/edit.jinja" current_tab = "stocks" class StockCreateView(CounterAdminTabsMixin, CanCreateMixin, CreateView): """ A create view for a new Stock """ model = Stock form_class = modelform_factory(Stock, fields=["name", "counter"]) template_name = "core/create.jinja" pk_url_kwarg = "counter_id" current_tab = "stocks" success_url = reverse_lazy("stock:list") def get_initial(self): ret = super(StockCreateView, self).get_initial() if "counter_id" in self.kwargs.keys(): ret["counter"] = self.kwargs["counter_id"] return ret class StockItemCreateView(CounterAdminTabsMixin, CanCreateMixin, CreateView): """ A create view for a new StockItem """ model = StockItem form_class = modelform_factory( StockItem, fields=[ "name", "unit_quantity", "effective_quantity", "minimal_quantity", "type", "stock_owner", ], ) template_name = "core/create.jinja" pk_url_kwarg = "stock_id" current_tab = "stocks" def get_initial(self): ret = super(StockItemCreateView, self).get_initial() if "stock_id" in self.kwargs.keys(): ret["stock_owner"] = self.kwargs["stock_id"] return ret def get_success_url(self): return reverse_lazy( "stock:items_list", kwargs={"stock_id": self.object.stock_owner.id} ) class StockShoppingListView(CounterAdminTabsMixin, CanViewMixin, ListView): """ A list view for the people to know the item to buy """ model = Stock template_name = "stock/stock_shopping_list.jinja" pk_url_kwarg = "stock_id" current_tab = "stocks" def get_context_data(self): ret = super(StockShoppingListView, self).get_context_data() if "stock_id" in self.kwargs.keys(): ret["stock"] = Stock.objects.filter(id=self.kwargs["stock_id"]).first() return ret class StockItemQuantityForm(forms.BaseForm): def clean(self): with transaction.atomic(): self.stock = Stock.objects.filter(id=self.stock_id).first() shopping_list = ShoppingList( name="Courses " + self.stock.counter.name, date=timezone.now(), todo=True, ) shopping_list.save() shopping_list.stock_owner = self.stock shopping_list.save() for k, t in self.cleaned_data.items(): if k == "name": shopping_list.name = t shopping_list.save() elif k == "comment": shopping_list.comment = t shopping_list.save() else: if t > 0: item_id = int(k[5:]) item = StockItem.objects.filter(id=item_id).first() shoppinglist_item = ShoppingListItem( stockitem_owner=item, name=item.name, type=item.type, tobuy_quantity=t, ) shoppinglist_item.save() shoppinglist_item.shopping_lists.add(shopping_list) shoppinglist_item.save() return self.cleaned_data class StockItemQuantityBaseFormView( CounterAdminTabsMixin, CanEditMixin, DetailView, BaseFormView ): """ docstring for StockItemOutList """ model = StockItem template_name = "stock/shopping_list_quantity.jinja" pk_url_kwarg = "stock_id" current_tab = "stocks" def get_form_class(self): fields = OrderedDict() kwargs = {} fields["name"] = forms.CharField( max_length=30, required=True, label=_("Shopping list name") ) for t in ProductType.objects.order_by("name").all(): for i in self.stock.items.filter(type=t).order_by("name").all(): if i.effective_quantity <= i.minimal_quantity: field_name = "item-%s" % (str(i.id)) fields[field_name] = forms.IntegerField( required=True, label=str(i), initial=0, help_text=_(str(i.effective_quantity) + " left"), ) fields["comment"] = forms.CharField( widget=forms.Textarea( attrs={ "placeholder": _( "Add here, items to buy that are not reference as a stock item (example : sponge, knife, mugs ...)" ) } ), required=False, label=_("Comments"), ) kwargs["stock_id"] = self.stock.id kwargs["base_fields"] = fields return type("StockItemQuantityForm", (StockItemQuantityForm,), kwargs) def get(self, request, *args, **kwargs): """ Simple get view """ self.stock = Stock.objects.filter(id=self.kwargs["stock_id"]).first() return super(StockItemQuantityBaseFormView, self).get(request, *args, **kwargs) def post(self, request, *args, **kwargs): """ Handle the many possibilities of the post request """ self.object = self.get_object() self.stock = Stock.objects.filter(id=self.kwargs["stock_id"]).first() return super(StockItemQuantityBaseFormView, self).post(request, *args, **kwargs) def form_valid(self, form): return super(StockItemQuantityBaseFormView, self).form_valid(form) def get_context_data(self, **kwargs): kwargs = super(StockItemQuantityBaseFormView, self).get_context_data(**kwargs) if "form" not in kwargs.keys(): kwargs["form"] = self.get_form() kwargs["stock"] = self.stock return kwargs def get_success_url(self): return reverse_lazy( "stock:shoppinglist_list", args=self.args, kwargs=self.kwargs ) class StockShoppingListItemListView(CounterAdminTabsMixin, CanViewMixin, ListView): """docstring for StockShoppingListItemListView""" model = ShoppingList template_name = "stock/shopping_list_items.jinja" pk_url_kwarg = "shoppinglist_id" current_tab = "stocks" def get_context_data(self): ret = super(StockShoppingListItemListView, self).get_context_data() if "shoppinglist_id" in self.kwargs.keys(): ret["shoppinglist"] = ShoppingList.objects.filter( id=self.kwargs["shoppinglist_id"] ).first() return ret class StockShoppingListDeleteView(CounterAdminTabsMixin, CanEditMixin, DeleteView): """ Delete a ShoppingList (for the resonsible account) """ model = ShoppingList pk_url_kwarg = "shoppinglist_id" template_name = "core/delete_confirm.jinja" current_tab = "stocks" def get_success_url(self): return reverse_lazy( "stock:shoppinglist_list", kwargs={"stock_id": self.object.stock_owner.id} ) class StockShopppingListSetDone(CanEditMixin, DetailView): """ Set a ShoppingList as done """ model = ShoppingList pk_url_kwarg = "shoppinglist_id" def get(self, request, *args, **kwargs): self.object = self.get_object() self.object.todo = False self.object.save() return HttpResponseRedirect( reverse( "stock:shoppinglist_list", args=self.args, kwargs={"stock_id": self.object.stock_owner.id}, ) ) def post(self, request, *args, **kwargs): self.object = self.get_object() return HttpResponseRedirect( reverse( "stock:shoppinglist_list", args=self.args, kwargs={"stock_id": self.object.stock_owner.id}, ) ) class StockShopppingListSetTodo(CanEditMixin, DetailView): """ Set a ShoppingList as done """ model = ShoppingList pk_url_kwarg = "shoppinglist_id" def get(self, request, *args, **kwargs): self.object = self.get_object() self.object.todo = True self.object.save() return HttpResponseRedirect( reverse( "stock:shoppinglist_list", args=self.args, kwargs={"stock_id": self.object.stock_owner.id}, ) ) def post(self, request, *args, **kwargs): self.object = self.get_object() return HttpResponseRedirect( reverse( "stock:shoppinglist_list", args=self.args, kwargs={"stock_id": self.object.stock_owner.id}, ) ) class StockUpdateAfterShopppingForm(forms.BaseForm): def clean(self): with transaction.atomic(): self.shoppinglist = ShoppingList.objects.filter( id=self.shoppinglist_id ).first() for k, t in self.cleaned_data.items(): shoppinglist_item_id = int(k[5:]) if int(t) > 0: shoppinglist_item = ShoppingListItem.objects.filter( id=shoppinglist_item_id ).first() shoppinglist_item.bought_quantity = int(t) shoppinglist_item.save() shoppinglist_item.stockitem_owner.effective_quantity += int(t) shoppinglist_item.stockitem_owner.save() self.shoppinglist.todo = False self.shoppinglist.save() return self.cleaned_data class StockUpdateAfterShopppingBaseFormView( CounterAdminTabsMixin, CanEditMixin, DetailView, BaseFormView ): """ docstring for StockUpdateAfterShopppingBaseFormView """ model = ShoppingList template_name = "stock/update_after_shopping.jinja" pk_url_kwarg = "shoppinglist_id" current_tab = "stocks" def get_form_class(self): fields = OrderedDict() kwargs = {} for t in ProductType.objects.order_by("name").all(): for i in ( self.shoppinglist.shopping_items_to_buy.filter(type=t) .order_by("name") .all() ): field_name = "item-%s" % (str(i.id)) fields[field_name] = forms.CharField( max_length=30, required=True, label=str(i), help_text=_(str(i.tobuy_quantity) + " asked"), ) kwargs["shoppinglist_id"] = self.shoppinglist.id kwargs["base_fields"] = fields return type( "StockUpdateAfterShopppingForm", (StockUpdateAfterShopppingForm,), kwargs ) def get(self, request, *args, **kwargs): self.shoppinglist = ShoppingList.objects.filter( id=self.kwargs["shoppinglist_id"] ).first() return super(StockUpdateAfterShopppingBaseFormView, self).get( request, *args, **kwargs ) def post(self, request, *args, **kwargs): """ Handle the many possibilities of the post request """ self.object = self.get_object() self.shoppinglist = ShoppingList.objects.filter( id=self.kwargs["shoppinglist_id"] ).first() return super(StockUpdateAfterShopppingBaseFormView, self).post( request, *args, **kwargs ) def form_valid(self, form): """ We handle here the redirection """ return super(StockUpdateAfterShopppingBaseFormView, self).form_valid(form) def get_context_data(self, **kwargs): kwargs = super(StockUpdateAfterShopppingBaseFormView, self).get_context_data( **kwargs ) if "form" not in kwargs.keys(): kwargs["form"] = self.get_form() kwargs["shoppinglist"] = self.shoppinglist kwargs["stock"] = self.shoppinglist.stock_owner return kwargs def get_success_url(self): self.kwargs.pop("shoppinglist_id", None) return reverse_lazy( "stock:shoppinglist_list", args=self.args, kwargs=self.kwargs ) class StockTakeItemsForm(forms.BaseForm): """ docstring for StockTakeItemsFormView """ def clean(self): with transaction.atomic(): for k, t in self.cleaned_data.items(): item_id = int(k[5:]) if t > 0: item = StockItem.objects.filter(id=item_id).first() item.effective_quantity -= t item.save() return self.cleaned_data class StockTakeItemsBaseFormView( CounterTabsMixin, CanEditMixin, DetailView, BaseFormView ): """ docstring for StockTakeItemsBaseFormView """ model = StockItem template_name = "stock/stock_take_items.jinja" pk_url_kwarg = "stock_id" current_tab = "take_items_from_stock" def get_form_class(self): fields = OrderedDict() kwargs = {} for t in ProductType.objects.order_by("name").all(): for i in self.stock.items.filter(type=t).order_by("name").all(): field_name = "item-%s" % (str(i.id)) fields[field_name] = forms.IntegerField( required=False, label=str(i), initial=0, min_value=0, max_value=i.effective_quantity, help_text=_( "%(effective_quantity)s left" % {"effective_quantity": str(i.effective_quantity)} ), ) kwargs[field_name] = i.effective_quantity kwargs["stock_id"] = self.stock.id kwargs["counter_id"] = self.stock.counter.id kwargs["base_fields"] = fields return type("StockTakeItemsForm", (StockTakeItemsForm,), kwargs) def get(self, request, *args, **kwargs): """ Simple get view """ self.stock = Stock.objects.filter(id=self.kwargs["stock_id"]).first() return super(StockTakeItemsBaseFormView, self).get(request, *args, **kwargs) def post(self, request, *args, **kwargs): """ Handle the many possibilities of the post request """ self.object = self.get_object() self.stock = Stock.objects.filter(id=self.kwargs["stock_id"]).first() if self.stock.counter.type == "BAR" and not ( "counter_token" in self.request.session.keys() and self.request.session["counter_token"] == self.stock.counter.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.stock.counter.id}, ) + "?bad_location" ) return super(StockTakeItemsBaseFormView, self).post(request, *args, **kwargs) def form_valid(self, form): return super(StockTakeItemsBaseFormView, self).form_valid(form) def get_context_data(self, **kwargs): kwargs = super(StockTakeItemsBaseFormView, self).get_context_data(**kwargs) if "form" not in kwargs.keys(): kwargs["form"] = self.get_form() kwargs["stock"] = self.stock kwargs["counter"] = self.stock.counter return kwargs def get_success_url(self): stock = Stock.objects.filter(id=self.kwargs["stock_id"]).first() self.kwargs["counter_id"] = stock.counter.id self.kwargs.pop("stock_id", None) return reverse_lazy("counter:details", args=self.args, kwargs=self.kwargs)