Sith/stock/views.py

602 lines
19 KiB
Python

# -*- coding:utf-8 -*
#
# Copyright 2016,2017
# - Guillaume "Lo-J" Renaud <renaudg779@gmail.com>
# - Skia <skia@libskia.so>
#
# 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)