jaywink /
kakaravaara3
| 1 | from decimal import Decimal |
||
| 2 | |||
| 3 | from babel.dates import format_datetime |
||
| 4 | from datetime import date, datetime, timedelta |
||
| 5 | |||
| 6 | from dateutil.relativedelta import relativedelta |
||
| 7 | from django import http |
||
| 8 | from django.conf import settings |
||
| 9 | from django.core.urlresolvers import reverse |
||
| 10 | from django.forms import ModelForm |
||
| 11 | from django.http import JsonResponse |
||
| 12 | from django.utils.timezone import localtime |
||
| 13 | from django.utils.translation import ugettext_lazy as _, ugettext |
||
| 14 | from django.views.generic import TemplateView, View |
||
| 15 | |||
| 16 | from shoop.admin.modules.products.views import ProductEditView |
||
| 17 | from shoop.admin.toolbar import URLActionButton |
||
| 18 | from shoop.admin.utils.picotable import Column, TextFilter, DateRangeFilter |
||
| 19 | from shoop.admin.utils.views import PicotableListView, CreateOrUpdateView |
||
| 20 | from shoop.utils.i18n import get_current_babel_locale, format_money |
||
| 21 | |||
| 22 | from reservations.forms import ReservableProductFormPart |
||
| 23 | from reservations.models import Reservation, ReservableProduct |
||
| 24 | from reservations.utils import get_start_and_end_from_request |
||
| 25 | |||
| 26 | |||
| 27 | class ReservableProductEditView(ProductEditView): |
||
| 28 | |||
| 29 | def get_form_part_classes(self): |
||
| 30 | form_part_classes = super(ReservableProductEditView, self).get_form_part_classes() |
||
| 31 | if self.object.type.identifier == "reservable": |
||
| 32 | form_part_classes.append(ReservableProductFormPart) |
||
| 33 | return form_part_classes |
||
| 34 | |||
| 35 | |||
| 36 | class ReservationForm(ModelForm): |
||
| 37 | class Meta: |
||
| 38 | model = Reservation |
||
| 39 | exclude = ("order_line",) |
||
| 40 | |||
| 41 | |||
| 42 | class ReservationEditView(CreateOrUpdateView): |
||
| 43 | model = Reservation |
||
| 44 | template_name = "reservations/reservation_edit.jinja" |
||
| 45 | context_object_name = "reservation" |
||
| 46 | form_class = ReservationForm |
||
| 47 | |||
| 48 | def get_toolbar(self): |
||
| 49 | toolbar = super(ReservationEditView, self).get_toolbar() |
||
| 50 | if self.object and self.object.order_line: |
||
| 51 | toolbar.append(URLActionButton( |
||
| 52 | text=_("View Order"), |
||
| 53 | icon="fa fa-inbox", |
||
| 54 | url=reverse("shoop_admin:order.detail", kwargs={"pk": self.object.order_line.order.pk}), |
||
| 55 | )) |
||
| 56 | return toolbar |
||
| 57 | |||
| 58 | |||
| 59 | class ReservableSearchView(TemplateView): |
||
| 60 | template_name = "reservations/reservable_search.jinja" |
||
| 61 | |||
| 62 | def get(self, request, *args, **kwargs): |
||
| 63 | self._set_dates_from_request(request) |
||
| 64 | return super(ReservableSearchView, self).get(request, *args, **kwargs) |
||
| 65 | |||
| 66 | def _set_dates_from_request(self, request): |
||
| 67 | """Get start and end from the request.""" |
||
| 68 | start = request.GET.get("start", None) |
||
| 69 | end = request.GET.get("end", None) |
||
| 70 | if not start: |
||
| 71 | self.start_date = date.today().replace(day=1) |
||
| 72 | self.end_date = date.today() + relativedelta(day=1, months=2, days=-1) |
||
| 73 | else: |
||
| 74 | self.start_date = datetime.strptime(start, "%Y-%m").date().replace(day=1) |
||
| 75 | self.end_date = datetime.strptime(end, "%Y-%m").date() + relativedelta(day=1, months=1, days=-1) |
||
| 76 | if self.end_date < self.start_date: |
||
| 77 | self.start_date = self.end_date |
||
| 78 | |||
| 79 | def _get_reservables(self): |
||
| 80 | return ReservableProduct.objects.all() |
||
| 81 | |||
| 82 | def _get_reserved_days_as_strings(self): |
||
| 83 | reservables = self._get_reservables() |
||
| 84 | reserved_days = {} |
||
| 85 | for reservable in reservables: |
||
| 86 | days = Reservation.get_reserved_days_for_period(self.start_date, self.end_date, reservable) |
||
| 87 | day_list = [] |
||
| 88 | for day in days: |
||
| 89 | day_list.append("%s" % day.strftime("%Y-%m-%d")) |
||
| 90 | reserved_days[reservable.product.sku.replace("-", "_")] = day_list |
||
| 91 | return reserved_days or {} |
||
| 92 | |||
| 93 | def get_context_data(self, **kwargs): |
||
| 94 | context = super(ReservableSearchView, self).get_context_data(**kwargs) |
||
| 95 | context["reservables"] = self._get_reservables() |
||
| 96 | context["start_month"] = self.start_date.strftime("%m/%Y") |
||
| 97 | context["end_month"] = self.end_date.strftime("%m/%Y") |
||
| 98 | context["start_date"] = self.start_date.strftime("%Y-%m-%d %H:%M") |
||
| 99 | context["end_date"] = self.end_date.strftime("%Y-%m-%d %H:%M") |
||
| 100 | context["reserved_days"] = self._get_reserved_days_as_strings() |
||
| 101 | context["visible_attributes"] = settings.RESERVABLE_SEARCH_VISIBLE_ATTRIBUTES |
||
| 102 | |||
| 103 | # calculate months |
||
| 104 | months = [] |
||
| 105 | # to not end up in endless loop |
||
| 106 | assert self.end_date >= self.start_date |
||
| 107 | current = self.start_date |
||
| 108 | while True: |
||
| 109 | # make sure there are selectable days in this month |
||
| 110 | not_this_month = date.today().replace(day=1) != current |
||
| 111 | if not_this_month or (date.today() + timedelta(days=2)).month == current.month: |
||
| 112 | months.append(current.strftime("%Y-%m")) |
||
| 113 | next = current + relativedelta(months=1) |
||
|
0 ignored issues
–
show
|
|||
| 114 | if next.replace(day=1) <= self.end_date.replace(day=1): |
||
| 115 | current = next |
||
| 116 | else: |
||
| 117 | break |
||
| 118 | context["months"] = months |
||
| 119 | return context |
||
| 120 | |||
| 121 | |||
| 122 | class DateRangeCheckView(View): |
||
| 123 | def get(self, request, *args, **kwargs): |
||
| 124 | reservable_id = request.GET.get("reservable_id", None) |
||
| 125 | start_date, end_date = get_start_and_end_from_request(request) |
||
| 126 | if not start_date or not end_date: |
||
| 127 | return http.HttpResponseBadRequest("Need start and end dates.") |
||
| 128 | if not reservable_id: |
||
| 129 | return http.HttpResponseBadRequest("Need reservable id.") |
||
| 130 | reservable = ReservableProduct.objects.get(id=reservable_id) |
||
| 131 | is_free = reservable.is_period_days_free(start_date, end_date) |
||
| 132 | total_days = (end_date - start_date).days |
||
| 133 | if is_free: |
||
| 134 | price_info = reservable.product.get_price_info(request, quantity=total_days) |
||
| 135 | has_extra_info = price_info.period_modifiers > 0 or price_info.per_person_modifiers > 0 |
||
| 136 | price = { |
||
| 137 | "total": format_money(price_info.price), |
||
| 138 | "has_extra_info": has_extra_info, |
||
| 139 | } |
||
| 140 | if has_extra_info: |
||
| 141 | price.update({ |
||
| 142 | "period_modifiers": price_info.period_modifiers.quantize(Decimal("1.00")), |
||
| 143 | "per_person_modifiers": price_info.per_person_modifiers.quantize(Decimal("1.00")), |
||
| 144 | "special_period_str": ugettext("Special period"), |
||
| 145 | "persons_count_str": ugettext("Person count"), |
||
| 146 | }) |
||
| 147 | else: |
||
| 148 | price = None |
||
| 149 | return JsonResponse({ |
||
| 150 | "result": is_free, |
||
| 151 | "price": price, |
||
| 152 | }) |
||
| 153 | |||
| 154 | |||
| 155 | class ReservationsAdminList(PicotableListView): |
||
| 156 | model = Reservation |
||
| 157 | columns = [ |
||
| 158 | Column("id", _("ID"), sort_field="id", display="id", filter_config=TextFilter()), |
||
| 159 | Column( |
||
| 160 | "name", _("Name"), sort_field="reservable__product__translations__name", |
||
| 161 | display="reservable__product__name", |
||
| 162 | filter_config=TextFilter(filter_field="reservable__product__translations__name") |
||
| 163 | ), |
||
| 164 | Column("order", _("From Order"), sort_field="order_line__order", display="order_line__order", |
||
| 165 | filter_config=TextFilter(filter_field="order_line__order__id")), |
||
| 166 | Column("start_time", _("Sign In Time"), sort_field="start_time", display="format_start_time", |
||
| 167 | filter_config=DateRangeFilter(filter_field="start_time")), |
||
| 168 | Column("end_time", _("Sign Out Time"), sort_field="end_time", display="format_end_time", |
||
| 169 | filter_config=DateRangeFilter(filter_field="end_time")), |
||
| 170 | Column("persons", _("Persons"), display="persons"), |
||
| 171 | ] |
||
| 172 | |||
| 173 | def format_start_time(self, instance, *args, **kwargs): |
||
| 174 | return format_datetime(localtime(instance.start_time), locale=get_current_babel_locale()) |
||
| 175 | |||
| 176 | def format_end_time(self, instance, *args, **kwargs): |
||
| 177 | return format_datetime(localtime(instance.end_time), locale=get_current_babel_locale()) |
||
| 178 | |||
| 179 | def get_toolbar(self): |
||
| 180 | toolbar = super(ReservationsAdminList, self).get_toolbar() |
||
| 181 | toolbar.append(URLActionButton( |
||
| 182 | text=_("New Reservation"), |
||
| 183 | icon="fa fa-calendar", |
||
| 184 | url=reverse("shoop_admin:reservations.new"), |
||
| 185 | )) |
||
| 186 | return toolbar |
||
| 187 | |||
| 188 | def get_object_url(self, instance): |
||
| 189 | return reverse( |
||
| 190 | "shoop_admin:reservations.edit", kwargs={"pk": instance.id}) |
||
| 191 |
It is generally discouraged to redefine built-ins as this makes code very hard to read.