mirror of
https://github.com/ae-utbm/sith.git
synced 2024-11-24 18:14:22 +00:00
Convert customer refill to a fragment view
This commit is contained in:
parent
55fb89a63e
commit
db5d8a8611
@ -21,11 +21,12 @@ from ninja_extra import ControllerBase, api_controller, paginate, route
|
|||||||
from ninja_extra.pagination import PageNumberPaginationExtra
|
from ninja_extra.pagination import PageNumberPaginationExtra
|
||||||
from ninja_extra.schemas import PaginatedResponseSchema
|
from ninja_extra.schemas import PaginatedResponseSchema
|
||||||
|
|
||||||
from core.api_permissions import CanAccessLookup, CanView, IsRoot
|
from core.api_permissions import CanAccessLookup, CanView, IsLoggedInCounter, IsRoot
|
||||||
from counter.models import Counter, Product
|
from counter.models import Counter, Customer, Product
|
||||||
from counter.schemas import (
|
from counter.schemas import (
|
||||||
CounterFilterSchema,
|
CounterFilterSchema,
|
||||||
CounterSchema,
|
CounterSchema,
|
||||||
|
CustomerBalance,
|
||||||
ProductSchema,
|
ProductSchema,
|
||||||
SimplifiedCounterSchema,
|
SimplifiedCounterSchema,
|
||||||
)
|
)
|
||||||
@ -60,6 +61,13 @@ class CounterController(ControllerBase):
|
|||||||
return filters.filter(Counter.objects.all())
|
return filters.filter(Counter.objects.all())
|
||||||
|
|
||||||
|
|
||||||
|
@api_controller("/customer")
|
||||||
|
class CustomerController(ControllerBase):
|
||||||
|
@route.get("/balance", response=CustomerBalance, permissions=[IsLoggedInCounter])
|
||||||
|
def get_balance(self, customer_id: int):
|
||||||
|
return self.get_object_or_exception(Customer, pk=customer_id)
|
||||||
|
|
||||||
|
|
||||||
@api_controller("/product")
|
@api_controller("/product")
|
||||||
class ProductController(ControllerBase):
|
class ProductController(ControllerBase):
|
||||||
@route.get(
|
@route.get(
|
||||||
|
@ -4,7 +4,7 @@ from annotated_types import MinLen
|
|||||||
from ninja import Field, FilterSchema, ModelSchema
|
from ninja import Field, FilterSchema, ModelSchema
|
||||||
|
|
||||||
from core.schemas import SimpleUserSchema
|
from core.schemas import SimpleUserSchema
|
||||||
from counter.models import Counter, Product
|
from counter.models import Counter, Customer, Product
|
||||||
|
|
||||||
|
|
||||||
class CounterSchema(ModelSchema):
|
class CounterSchema(ModelSchema):
|
||||||
@ -16,6 +16,12 @@ class CounterSchema(ModelSchema):
|
|||||||
fields = ["id", "name", "type", "club", "products"]
|
fields = ["id", "name", "type", "club", "products"]
|
||||||
|
|
||||||
|
|
||||||
|
class CustomerBalance(ModelSchema):
|
||||||
|
class Meta:
|
||||||
|
model = Customer
|
||||||
|
fields = ["amount"]
|
||||||
|
|
||||||
|
|
||||||
class CounterFilterSchema(FilterSchema):
|
class CounterFilterSchema(FilterSchema):
|
||||||
search: Annotated[str, MinLen(1)] = Field(None, q="name__icontains")
|
search: Annotated[str, MinLen(1)] = Field(None, q="name__icontains")
|
||||||
|
|
||||||
|
@ -1,86 +0,0 @@
|
|||||||
document.addEventListener("alpine:init", () => {
|
|
||||||
Alpine.data("counter", () => ({
|
|
||||||
// biome-ignore lint/correctness/noUndeclaredVariables: defined in counter_click.jinja
|
|
||||||
basket: sessionBasket,
|
|
||||||
errors: [],
|
|
||||||
|
|
||||||
sumBasket() {
|
|
||||||
if (!this.basket || Object.keys(this.basket).length === 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
const total = Object.values(this.basket).reduce(
|
|
||||||
(acc, cur) => acc + cur.qty * cur.price,
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
return total / 100;
|
|
||||||
},
|
|
||||||
|
|
||||||
async handleCode(event) {
|
|
||||||
const code = $(event.target).find("#code_field").val().toUpperCase();
|
|
||||||
if (["FIN", "ANN"].includes(code)) {
|
|
||||||
$(event.target).submit();
|
|
||||||
} else {
|
|
||||||
await this.handleAction(event);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async handleAction(event) {
|
|
||||||
const payload = $(event.target).serialize();
|
|
||||||
// biome-ignore lint/correctness/noUndeclaredVariables: defined in counter_click.jinja
|
|
||||||
const request = new Request(clickApiUrl, {
|
|
||||||
method: "POST",
|
|
||||||
body: payload,
|
|
||||||
headers: {
|
|
||||||
// biome-ignore lint/style/useNamingConvention: this goes into http headers
|
|
||||||
Accept: "application/json",
|
|
||||||
// biome-ignore lint/correctness/noUndeclaredVariables: defined in counter_click.jinja
|
|
||||||
"X-CSRFToken": csrfToken,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const response = await fetch(request);
|
|
||||||
const json = await response.json();
|
|
||||||
this.basket = json.basket;
|
|
||||||
this.errors = json.errors;
|
|
||||||
$("form.code_form #code_field").val("").focus();
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
$(() => {
|
|
||||||
/* Autocompletion in the code field */
|
|
||||||
const codeField = $("#code_field");
|
|
||||||
|
|
||||||
let quantity = "";
|
|
||||||
codeField.autocomplete({
|
|
||||||
select: (event, ui) => {
|
|
||||||
event.preventDefault();
|
|
||||||
codeField.val(quantity + ui.item.value);
|
|
||||||
},
|
|
||||||
focus: (event, ui) => {
|
|
||||||
event.preventDefault();
|
|
||||||
codeField.val(quantity + ui.item.value);
|
|
||||||
},
|
|
||||||
source: (request, response) => {
|
|
||||||
// biome-ignore lint/performance/useTopLevelRegex: performance impact is minimal
|
|
||||||
const res = /^(\d+x)?(.*)/i.exec(request.term);
|
|
||||||
quantity = res[1] || "";
|
|
||||||
const search = res[2];
|
|
||||||
const matcher = new RegExp($.ui.autocomplete.escapeRegex(search), "i");
|
|
||||||
response(
|
|
||||||
// biome-ignore lint/correctness/noUndeclaredVariables: defined in counter_click.jinja
|
|
||||||
$.grep(productsAutocomplete, (value) => {
|
|
||||||
return matcher.test(value.tags);
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
/* Accordion UI between basket and refills */
|
|
||||||
$("#click_form").accordion({
|
|
||||||
heightStyle: "content",
|
|
||||||
activate: () => $(".focus").focus(),
|
|
||||||
});
|
|
||||||
$("#products").tabs();
|
|
||||||
|
|
||||||
codeField.focus();
|
|
||||||
});
|
|
131
counter/static/webpack/counter/counter-click-index.ts
Normal file
131
counter/static/webpack/counter/counter-click-index.ts
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
import { exportToHtml } from "#core:utils/globals";
|
||||||
|
import { customerGetBalance } from "#openapi";
|
||||||
|
|
||||||
|
interface CounterConfig {
|
||||||
|
csrfToken: string;
|
||||||
|
clickApiUrl: string;
|
||||||
|
sessionBasket: Record<number, BasketItem>;
|
||||||
|
customerBalance: number;
|
||||||
|
customerId: number;
|
||||||
|
}
|
||||||
|
interface BasketItem {
|
||||||
|
// biome-ignore lint/style/useNamingConvention: talking with python
|
||||||
|
bonus_qty: number;
|
||||||
|
price: number;
|
||||||
|
qty: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
exportToHtml("loadCounter", (config: CounterConfig) => {
|
||||||
|
document.addEventListener("alpine:init", () => {
|
||||||
|
Alpine.data("counter", () => ({
|
||||||
|
basket: config.sessionBasket,
|
||||||
|
errors: [],
|
||||||
|
customerBalance: config.customerBalance,
|
||||||
|
|
||||||
|
sumBasket() {
|
||||||
|
if (!this.basket || Object.keys(this.basket).length === 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
const total = Object.values(this.basket).reduce(
|
||||||
|
(acc: number, cur: BasketItem) => acc + cur.qty * cur.price,
|
||||||
|
0,
|
||||||
|
) as number;
|
||||||
|
return total / 100;
|
||||||
|
},
|
||||||
|
|
||||||
|
async updateBalance() {
|
||||||
|
this.customerBalance = (
|
||||||
|
await customerGetBalance({
|
||||||
|
query: {
|
||||||
|
// biome-ignore lint/style/useNamingConvention: api is in snake_case
|
||||||
|
customer_id: config.customerId,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
).data.amount;
|
||||||
|
},
|
||||||
|
|
||||||
|
async handleCode(event: SubmitEvent) {
|
||||||
|
const code = (
|
||||||
|
$(event.target).find("#code_field").val() as string
|
||||||
|
).toUpperCase();
|
||||||
|
if (["FIN", "ANN"].includes(code)) {
|
||||||
|
$(event.target).submit();
|
||||||
|
} else {
|
||||||
|
await this.handleAction(event);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async handleAction(event: SubmitEvent) {
|
||||||
|
const payload = $(event.target).serialize();
|
||||||
|
const request = new Request(config.clickApiUrl, {
|
||||||
|
method: "POST",
|
||||||
|
body: payload,
|
||||||
|
headers: {
|
||||||
|
// biome-ignore lint/style/useNamingConvention: this goes into http headers
|
||||||
|
Accept: "application/json",
|
||||||
|
"X-CSRFToken": config.csrfToken,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const response = await fetch(request);
|
||||||
|
const json = await response.json();
|
||||||
|
this.basket = json.basket;
|
||||||
|
this.errors = json.errors;
|
||||||
|
$("form.code_form #code_field").val("").focus();
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
interface Product {
|
||||||
|
value: string;
|
||||||
|
label: string;
|
||||||
|
tags: string;
|
||||||
|
}
|
||||||
|
declare global {
|
||||||
|
const productsAutocomplete: Product[];
|
||||||
|
}
|
||||||
|
|
||||||
|
$(() => {
|
||||||
|
/* Autocompletion in the code field */
|
||||||
|
// biome-ignore lint/suspicious/noExplicitAny: dealing with legacy jquery
|
||||||
|
const codeField: any = $("#code_field");
|
||||||
|
|
||||||
|
let quantity = "";
|
||||||
|
codeField.autocomplete({
|
||||||
|
// biome-ignore lint/suspicious/noExplicitAny: dealing with legacy jquery
|
||||||
|
select: (event: any, ui: any) => {
|
||||||
|
event.preventDefault();
|
||||||
|
codeField.val(quantity + ui.item.value);
|
||||||
|
},
|
||||||
|
// biome-ignore lint/suspicious/noExplicitAny: dealing with legacy jquery
|
||||||
|
focus: (event: any, ui: any) => {
|
||||||
|
event.preventDefault();
|
||||||
|
codeField.val(quantity + ui.item.value);
|
||||||
|
},
|
||||||
|
// biome-ignore lint/suspicious/noExplicitAny: dealing with legacy jquery
|
||||||
|
source: (request: any, response: any) => {
|
||||||
|
// biome-ignore lint/performance/useTopLevelRegex: performance impact is minimal
|
||||||
|
const res = /^(\d+x)?(.*)/i.exec(request.term);
|
||||||
|
quantity = res[1] || "";
|
||||||
|
const search = res[2];
|
||||||
|
// biome-ignore lint/suspicious/noExplicitAny: dealing with legacy jquery
|
||||||
|
const matcher = new RegExp(($ as any).ui.autocomplete.escapeRegex(search), "i");
|
||||||
|
response(
|
||||||
|
$.grep(productsAutocomplete, (value: Product) => {
|
||||||
|
return matcher.test(value.tags);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/* Accordion UI between basket and refills */
|
||||||
|
// biome-ignore lint/suspicious/noExplicitAny: dealing with legacy jquery
|
||||||
|
($("#click_form") as any).accordion({
|
||||||
|
heightStyle: "content",
|
||||||
|
activate: () => $(".focus").focus(),
|
||||||
|
});
|
||||||
|
// biome-ignore lint/suspicious/noExplicitAny: dealing with legacy jquery
|
||||||
|
($("#products") as any).tabs();
|
||||||
|
|
||||||
|
codeField.focus();
|
||||||
|
});
|
@ -6,7 +6,7 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block additional_js %}
|
{% block additional_js %}
|
||||||
<script src="{{ static('counter/js/counter_click.js') }}" defer></script>
|
<script src="{{ static('webpack/counter/counter-click-index.ts') }}" defer></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block info_boxes %}
|
{% block info_boxes %}
|
||||||
@ -28,7 +28,7 @@
|
|||||||
<h5>{% trans %}Customer{% endtrans %}</h5>
|
<h5>{% trans %}Customer{% endtrans %}</h5>
|
||||||
{{ user_mini_profile(customer.user) }}
|
{{ user_mini_profile(customer.user) }}
|
||||||
{{ user_subscription(customer.user) }}
|
{{ user_subscription(customer.user) }}
|
||||||
<p>{% trans %}Amount: {% endtrans %}{{ customer.amount }} €</p>
|
<p>{% trans %}Amount: {% endtrans %}<span x-text="customerBalance"></span> €</p>
|
||||||
|
|
||||||
{% if counter.type == 'BAR' %}
|
{% if counter.type == 'BAR' %}
|
||||||
<div
|
<div
|
||||||
@ -108,17 +108,14 @@
|
|||||||
<input type="submit" value="{% trans %}Cancel{% endtrans %}"/>
|
<input type="submit" value="{% trans %}Cancel{% endtrans %}"/>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
{% if (counter.type == 'BAR' and barmens_can_refill) %}
|
{% if is_reflling_allowed %}
|
||||||
<h5>{% trans %}Refilling{% endtrans %}</h5>
|
<h5>{% trans %}Refilling{% endtrans %}</h5>
|
||||||
<div>
|
<div
|
||||||
<form method="post"
|
@htmx:after-request="updateBalance()"
|
||||||
action="{{ url('counter:click', counter_id=counter.id, user_id=customer.user.id) }}">
|
hx-get="{{ url('counter:refilling_create_fragment', counter_id=counter.id, customer_id=customer.pk) }}"
|
||||||
{% csrf_token %}
|
hx-trigger="load"
|
||||||
{{ refill_form.as_p() }}
|
hx-swap="innerHTML"
|
||||||
<input type="hidden" name="action" value="refill">
|
></div>
|
||||||
<input type="submit" value="{% trans %}Go{% endtrans %}"/>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -158,9 +155,6 @@
|
|||||||
{% block script %}
|
{% block script %}
|
||||||
{{ super() }}
|
{{ super() }}
|
||||||
<script>
|
<script>
|
||||||
const csrfToken = "{{ csrf_token }}";
|
|
||||||
const clickApiUrl = "{{ url('counter:click', counter_id=counter.id, user_id=customer.user.id) }}";
|
|
||||||
const sessionBasket = {{ request.session["basket"]|tojson }};
|
|
||||||
const products = {
|
const products = {
|
||||||
{%- for p in products -%}
|
{%- for p in products -%}
|
||||||
{{ p.id }}: {
|
{{ p.id }}: {
|
||||||
@ -179,5 +173,14 @@
|
|||||||
},
|
},
|
||||||
{%- endfor %}
|
{%- endfor %}
|
||||||
];
|
];
|
||||||
|
window.addEventListener("DOMContentLoaded", () => {
|
||||||
|
loadCounter({
|
||||||
|
csrfToken: "{{ csrf_token }}",
|
||||||
|
clickApiUrl: "{{ url('counter:click', counter_id=counter.id, user_id=customer.user.id) }}",
|
||||||
|
sessionBasket: {{ request.session["basket"]|tojson }},
|
||||||
|
customerBalance: {{ customer.amount }},
|
||||||
|
customerId: {{ customer.pk }},
|
||||||
|
});
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
{% endblock script %}
|
{% endblock script %}
|
9
counter/templates/counter/refill_fragment.jinja
Normal file
9
counter/templates/counter/refill_fragment.jinja
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<form
|
||||||
|
hx-trigger="submit"
|
||||||
|
hx-post="{{ action }}"
|
||||||
|
hx-swap="outerHTML"
|
||||||
|
>
|
||||||
|
{% csrf_token %}
|
||||||
|
{{ form.as_p() }}
|
||||||
|
<button>{% trans %}Go{% endtrans %}</button>
|
||||||
|
</form>
|
@ -67,13 +67,16 @@ class TestCounter(TestCase):
|
|||||||
{"code": self.richard.customer.account_id, "counter_token": counter_token},
|
{"code": self.richard.customer.account_id, "counter_token": counter_token},
|
||||||
)
|
)
|
||||||
counter_url = response.get("location")
|
counter_url = response.get("location")
|
||||||
|
refill_url = reverse(
|
||||||
|
"counter:refilling_create_fragment",
|
||||||
|
kwargs={"counter_id": self.mde.id, "customer_id": self.richard.customer.pk},
|
||||||
|
)
|
||||||
response = self.client.get(response.get("location"))
|
response = self.client.get(response.get("location"))
|
||||||
assert ">Richard Batsbak</" in str(response.content)
|
assert ">Richard Batsbak</" in str(response.content)
|
||||||
|
|
||||||
self.client.post(
|
self.client.post(
|
||||||
counter_url,
|
refill_url,
|
||||||
{
|
{
|
||||||
"action": "refill",
|
|
||||||
"amount": "5",
|
"amount": "5",
|
||||||
"payment_method": "CASH",
|
"payment_method": "CASH",
|
||||||
"bank": "OTHER",
|
"bank": "OTHER",
|
||||||
@ -110,15 +113,14 @@ class TestCounter(TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
counter_url,
|
refill_url,
|
||||||
{
|
{
|
||||||
"action": "refill",
|
|
||||||
"amount": "5",
|
"amount": "5",
|
||||||
"payment_method": "CASH",
|
"payment_method": "CASH",
|
||||||
"bank": "OTHER",
|
"bank": "OTHER",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
assert response.status_code == 200
|
assert response.status_code == 302
|
||||||
|
|
||||||
self.client.post(
|
self.client.post(
|
||||||
reverse("counter:login", kwargs={"counter_id": self.foyer.id}),
|
reverse("counter:login", kwargs={"counter_id": self.foyer.id}),
|
||||||
@ -138,17 +140,23 @@ class TestCounter(TestCase):
|
|||||||
{"code": self.richard.customer.account_id, "counter_token": counter_token},
|
{"code": self.richard.customer.account_id, "counter_token": counter_token},
|
||||||
)
|
)
|
||||||
counter_url = response.get("location")
|
counter_url = response.get("location")
|
||||||
|
refill_url = reverse(
|
||||||
|
"counter:refilling_create_fragment",
|
||||||
|
kwargs={
|
||||||
|
"counter_id": self.foyer.id,
|
||||||
|
"customer_id": self.richard.customer.pk,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
counter_url,
|
refill_url,
|
||||||
{
|
{
|
||||||
"action": "refill",
|
|
||||||
"amount": "5",
|
"amount": "5",
|
||||||
"payment_method": "CASH",
|
"payment_method": "CASH",
|
||||||
"bank": "OTHER",
|
"bank": "OTHER",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
assert response.status_code == 200
|
assert response.status_code == 302
|
||||||
|
|
||||||
def test_annotate_has_barman_queryset(self):
|
def test_annotate_has_barman_queryset(self):
|
||||||
"""Test if the custom queryset method `annotate_has_barman` works as intended."""
|
"""Test if the custom queryset method `annotate_has_barman` works as intended."""
|
||||||
|
@ -42,6 +42,7 @@ from counter.views import (
|
|||||||
ProductTypeCreateView,
|
ProductTypeCreateView,
|
||||||
ProductTypeEditView,
|
ProductTypeEditView,
|
||||||
ProductTypeListView,
|
ProductTypeListView,
|
||||||
|
RefillingCreateFragmentView,
|
||||||
RefillingDeleteView,
|
RefillingDeleteView,
|
||||||
SellingDeleteView,
|
SellingDeleteView,
|
||||||
StudentCardDeleteView,
|
StudentCardDeleteView,
|
||||||
@ -68,17 +69,22 @@ urlpatterns = [
|
|||||||
path("<int:counter_id>/stats/", CounterStatView.as_view(), name="stats"),
|
path("<int:counter_id>/stats/", CounterStatView.as_view(), name="stats"),
|
||||||
path("<int:counter_id>/login/", counter_login, name="login"),
|
path("<int:counter_id>/login/", counter_login, name="login"),
|
||||||
path("<int:counter_id>/logout/", counter_logout, name="logout"),
|
path("<int:counter_id>/logout/", counter_logout, name="logout"),
|
||||||
|
path(
|
||||||
|
"<int:counter_id>/refill/<int:customer_id>/",
|
||||||
|
RefillingCreateFragmentView.as_view(),
|
||||||
|
name="refilling_create_fragment",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"<int:counter_id>/card/add/<int:customer_id>/",
|
||||||
|
StudentCardFormFragmentView.as_view(),
|
||||||
|
name="add_student_card_fragment",
|
||||||
|
),
|
||||||
path("eticket/<int:selling_id>/pdf/", EticketPDFView.as_view(), name="eticket_pdf"),
|
path("eticket/<int:selling_id>/pdf/", EticketPDFView.as_view(), name="eticket_pdf"),
|
||||||
path(
|
path(
|
||||||
"customer/<int:customer_id>/card/add/",
|
"customer/<int:customer_id>/card/add/",
|
||||||
StudentCardFormView.as_view(),
|
StudentCardFormView.as_view(),
|
||||||
name="add_student_card",
|
name="add_student_card",
|
||||||
),
|
),
|
||||||
path(
|
|
||||||
"customer/<int:customer_id>/card/add/counter/<int:counter_id>/",
|
|
||||||
StudentCardFormFragmentView.as_view(),
|
|
||||||
name="add_student_card_fragment",
|
|
||||||
),
|
|
||||||
path(
|
path(
|
||||||
"customer/<int:customer_id>/card/delete/<int:card_id>/",
|
"customer/<int:customer_id>/card/delete/<int:card_id>/",
|
||||||
StudentCardDeleteView.as_view(),
|
StudentCardDeleteView.as_view(),
|
||||||
|
178
counter/views.py
178
counter/views.py
@ -294,7 +294,6 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
|||||||
request.session["too_young"] = False
|
request.session["too_young"] = False
|
||||||
request.session["not_allowed"] = False
|
request.session["not_allowed"] = False
|
||||||
request.session["no_age"] = False
|
request.session["no_age"] = False
|
||||||
self.refill_form = None
|
|
||||||
ret = super().get(request, *args, **kwargs)
|
ret = super().get(request, *args, **kwargs)
|
||||||
if (self.object.type != "BAR" and not request.user.is_authenticated) or (
|
if (self.object.type != "BAR" and not request.user.is_authenticated) or (
|
||||||
self.object.type == "BAR" and len(self.object.barmen_list) == 0
|
self.object.type == "BAR" and len(self.object.barmen_list) == 0
|
||||||
@ -305,7 +304,6 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
|||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
"""Handle the many possibilities of the post request."""
|
"""Handle the many possibilities of the post request."""
|
||||||
self.object = self.get_object()
|
self.object = self.get_object()
|
||||||
self.refill_form = None
|
|
||||||
if (self.object.type != "BAR" and not request.user.is_authenticated) or (
|
if (self.object.type != "BAR" and not request.user.is_authenticated) or (
|
||||||
self.object.type == "BAR" and len(self.object.barmen_list) < 1
|
self.object.type == "BAR" and len(self.object.barmen_list) < 1
|
||||||
): # Check that at least one barman is logged in
|
): # Check that at least one barman is logged in
|
||||||
@ -342,8 +340,6 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
|||||||
self.add_product(request)
|
self.add_product(request)
|
||||||
elif action == "del_product":
|
elif action == "del_product":
|
||||||
self.del_product(request)
|
self.del_product(request)
|
||||||
elif action == "refill":
|
|
||||||
self.refill(request)
|
|
||||||
elif action == "code":
|
elif action == "code":
|
||||||
return self.parse_code(request)
|
return self.parse_code(request)
|
||||||
elif action == "cancel":
|
elif action == "cancel":
|
||||||
@ -577,19 +573,6 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
|||||||
reverse_lazy("counter:details", args=self.args, kwargs=kwargs)
|
reverse_lazy("counter:details", args=self.args, kwargs=kwargs)
|
||||||
)
|
)
|
||||||
|
|
||||||
def refill(self, request):
|
|
||||||
"""Refill the customer's account."""
|
|
||||||
if not self.object.can_refill():
|
|
||||||
raise PermissionDenied
|
|
||||||
form = RefillForm(request.POST)
|
|
||||||
if form.is_valid():
|
|
||||||
form.instance.counter = self.object
|
|
||||||
form.instance.operator = self.operator
|
|
||||||
form.instance.customer = self.customer
|
|
||||||
form.instance.save()
|
|
||||||
else:
|
|
||||||
self.refill_form = form
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
"""Add customer to the context."""
|
"""Add customer to the context."""
|
||||||
kwargs = super().get_context_data(**kwargs)
|
kwargs = super().get_context_data(**kwargs)
|
||||||
@ -607,11 +590,119 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
|||||||
)
|
)
|
||||||
kwargs["customer"] = self.customer
|
kwargs["customer"] = self.customer
|
||||||
kwargs["basket_total"] = self.sum_basket(self.request)
|
kwargs["basket_total"] = self.sum_basket(self.request)
|
||||||
kwargs["refill_form"] = self.refill_form or RefillForm()
|
kwargs["is_reflling_allowed"] = self.object.can_refill()
|
||||||
kwargs["barmens_can_refill"] = self.object.can_refill()
|
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
|
class RefillingCreateFragmentView(FormView):
|
||||||
|
"""This is a fragment only view which integrates with counter_click.jinja"""
|
||||||
|
|
||||||
|
form_class = RefillForm
|
||||||
|
template_name = "counter/refill_fragment.jinja"
|
||||||
|
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
self.counter = get_object_or_404(
|
||||||
|
Counter.objects.annotate_is_open(), pk=kwargs["counter_id"]
|
||||||
|
)
|
||||||
|
self.customer = get_object_or_404(Customer, pk=kwargs["customer_id"])
|
||||||
|
if not self.customer.can_buy:
|
||||||
|
raise Http404
|
||||||
|
|
||||||
|
if not (
|
||||||
|
self.counter.can_refill()
|
||||||
|
and "counter_token" in request.session
|
||||||
|
and request.session["counter_token"] == self.counter.token
|
||||||
|
and self.counter.is_open
|
||||||
|
):
|
||||||
|
raise PermissionDenied
|
||||||
|
|
||||||
|
if self.customer_is_barman():
|
||||||
|
self.operator = self.customer.user
|
||||||
|
else:
|
||||||
|
self.operator = self.counter.get_random_barman()
|
||||||
|
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
def customer_is_barman(self) -> bool:
|
||||||
|
barmen = self.counter.barmen_list
|
||||||
|
return self.counter.type == "BAR" and self.customer.user in barmen
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
form.clean()
|
||||||
|
res = super(FormView, self).form_valid(form)
|
||||||
|
form.instance.counter = self.counter
|
||||||
|
form.instance.operator = self.operator
|
||||||
|
form.instance.customer = self.customer
|
||||||
|
form.instance.save()
|
||||||
|
return res
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
context["counter"] = self.counter
|
||||||
|
context["customer"] = self.customer
|
||||||
|
context["action"] = self.request.path
|
||||||
|
context["student_cards"] = self.customer.student_cards.all()
|
||||||
|
return context
|
||||||
|
|
||||||
|
def get_success_url(self, **kwargs):
|
||||||
|
return reverse_lazy(
|
||||||
|
"counter:refilling_create_fragment",
|
||||||
|
kwargs={
|
||||||
|
"customer_id": self.customer.pk,
|
||||||
|
"counter_id": self.counter.id,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class StudentCardFormFragmentView(FormView):
|
||||||
|
"""
|
||||||
|
Add a new student card from a counter
|
||||||
|
This is a fragment only view which integrates with counter_click.jinja
|
||||||
|
"""
|
||||||
|
|
||||||
|
form_class = StudentCardForm
|
||||||
|
template_name = "counter/add_student_card_fragment.jinja"
|
||||||
|
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
self.counter = get_object_or_404(
|
||||||
|
Counter.objects.annotate_is_open(), pk=kwargs["counter_id"]
|
||||||
|
)
|
||||||
|
self.customer = get_object_or_404(
|
||||||
|
Customer.objects.prefetch_related("student_cards"), pk=kwargs["customer_id"]
|
||||||
|
)
|
||||||
|
if not (
|
||||||
|
self.counter.type == "BAR"
|
||||||
|
and "counter_token" in request.session
|
||||||
|
and request.session["counter_token"] == self.counter.token
|
||||||
|
and self.counter.is_open
|
||||||
|
):
|
||||||
|
raise PermissionDenied
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
data = form.clean()
|
||||||
|
res = super(FormView, self).form_valid(form)
|
||||||
|
StudentCard(customer=self.customer, uid=data["uid"]).save()
|
||||||
|
return res
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
context["counter"] = self.counter
|
||||||
|
context["customer"] = self.customer
|
||||||
|
context["action"] = self.request.path
|
||||||
|
context["student_cards"] = self.customer.student_cards.all()
|
||||||
|
return context
|
||||||
|
|
||||||
|
def get_success_url(self, **kwargs):
|
||||||
|
return reverse_lazy(
|
||||||
|
"counter:add_student_card_fragment",
|
||||||
|
kwargs={
|
||||||
|
"customer_id": self.customer.pk,
|
||||||
|
"counter_id": self.counter.id,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@require_POST
|
@require_POST
|
||||||
def counter_login(request: HttpRequest, counter_id: int) -> HttpResponseRedirect:
|
def counter_login(request: HttpRequest, counter_id: int) -> HttpResponseRedirect:
|
||||||
"""Log a user in a counter.
|
"""Log a user in a counter.
|
||||||
@ -1511,52 +1602,3 @@ class StudentCardFormView(AllowFragment, FormView):
|
|||||||
return reverse_lazy(
|
return reverse_lazy(
|
||||||
"core:user_prefs", kwargs={"user_id": self.customer.user.pk}
|
"core:user_prefs", kwargs={"user_id": self.customer.user.pk}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class StudentCardFormFragmentView(FormView):
|
|
||||||
"""
|
|
||||||
Add a new student card from a counter
|
|
||||||
This is a fragment only view which integrates with counter_click.jinja
|
|
||||||
"""
|
|
||||||
|
|
||||||
form_class = StudentCardForm
|
|
||||||
template_name = "counter/add_student_card_fragment.jinja"
|
|
||||||
|
|
||||||
def dispatch(self, request, *args, **kwargs):
|
|
||||||
self.counter = get_object_or_404(
|
|
||||||
Counter.objects.annotate_is_open(), pk=kwargs["counter_id"]
|
|
||||||
)
|
|
||||||
self.customer = get_object_or_404(
|
|
||||||
Customer.objects.prefetch_related("student_cards"), pk=kwargs["customer_id"]
|
|
||||||
)
|
|
||||||
if not (
|
|
||||||
self.counter.type == "BAR"
|
|
||||||
and "counter_token" in request.session
|
|
||||||
and request.session["counter_token"] == self.counter.token
|
|
||||||
and self.counter.is_open
|
|
||||||
):
|
|
||||||
raise PermissionDenied
|
|
||||||
return super().dispatch(request, *args, **kwargs)
|
|
||||||
|
|
||||||
def form_valid(self, form):
|
|
||||||
data = form.clean()
|
|
||||||
res = super(FormView, self).form_valid(form)
|
|
||||||
StudentCard(customer=self.customer, uid=data["uid"]).save()
|
|
||||||
return res
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
context = super().get_context_data(**kwargs)
|
|
||||||
context["counter"] = self.counter
|
|
||||||
context["customer"] = self.customer
|
|
||||||
context["action"] = self.request.path
|
|
||||||
context["student_cards"] = self.customer.student_cards.all()
|
|
||||||
return context
|
|
||||||
|
|
||||||
def get_success_url(self, **kwargs):
|
|
||||||
return reverse_lazy(
|
|
||||||
"counter:add_student_card_fragment",
|
|
||||||
kwargs={
|
|
||||||
"customer_id": self.customer.pk,
|
|
||||||
"counter_id": self.counter.id,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
Loading…
Reference in New Issue
Block a user