mirror of
https://github.com/ae-utbm/sith.git
synced 2025-02-25 17:07:13 +00:00
Allow transactions on counter when an user has recorded too many products as long as he doesn't record more
This commit is contained in:
parent
a96b374ad7
commit
1978658b9c
@ -1,146 +1,141 @@
|
|||||||
import { exportToHtml } from "#core:utils/globals";
|
|
||||||
import { BasketItem } from "#counter:counter/basket";
|
import { BasketItem } from "#counter:counter/basket";
|
||||||
import type { CounterConfig, ErrorMessage } from "#counter:counter/types";
|
import type { CounterConfig, ErrorMessage } from "#counter:counter/types";
|
||||||
|
import type { CounterProductSelect } from "./components/counter-product-select-index";
|
||||||
|
|
||||||
exportToHtml("loadCounter", (config: CounterConfig) => {
|
document.addEventListener("alpine:init", () => {
|
||||||
document.addEventListener("alpine:init", () => {
|
Alpine.data("counter", (config: CounterConfig) => ({
|
||||||
Alpine.data("counter", () => ({
|
basket: {} as Record<string, BasketItem>,
|
||||||
basket: {} as Record<string, BasketItem>,
|
errors: [],
|
||||||
errors: [],
|
customerBalance: config.customerBalance,
|
||||||
customerBalance: config.customerBalance,
|
codeField: null as CounterProductSelect | null,
|
||||||
codeField: undefined,
|
alertMessage: {
|
||||||
alertMessage: {
|
content: "",
|
||||||
content: "",
|
show: false,
|
||||||
show: false,
|
timeout: null,
|
||||||
timeout: null,
|
},
|
||||||
},
|
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
// Fill the basket with the initial data
|
// Fill the basket with the initial data
|
||||||
for (const entry of config.formInitial) {
|
for (const entry of config.formInitial) {
|
||||||
if (entry.id !== undefined && entry.quantity !== undefined) {
|
if (entry.id !== undefined && entry.quantity !== undefined) {
|
||||||
this.addToBasket(entry.id, entry.quantity);
|
this.addToBasket(entry.id, entry.quantity);
|
||||||
this.basket[entry.id].errors = entry.errors ?? [];
|
this.basket[entry.id].errors = entry.errors ?? [];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.codeField = this.$refs.codeField;
|
this.codeField = this.$refs.codeField;
|
||||||
this.codeField.widget.focus();
|
this.codeField.widget.focus();
|
||||||
|
|
||||||
// It's quite tricky to manually apply attributes to the management part
|
// It's quite tricky to manually apply attributes to the management part
|
||||||
// of a formset so we dynamically apply it here
|
// of a formset so we dynamically apply it here
|
||||||
this.$refs.basketManagementForm
|
this.$refs.basketManagementForm
|
||||||
.querySelector("#id_form-TOTAL_FORMS")
|
.querySelector("#id_form-TOTAL_FORMS")
|
||||||
.setAttribute(":value", "getBasketSize()");
|
.setAttribute(":value", "getBasketSize()");
|
||||||
},
|
},
|
||||||
|
|
||||||
removeFromBasket(id: string) {
|
removeFromBasket(id: string) {
|
||||||
|
delete this.basket[id];
|
||||||
|
},
|
||||||
|
|
||||||
|
addToBasket(id: string, quantity: number): ErrorMessage {
|
||||||
|
const item: BasketItem =
|
||||||
|
this.basket[id] || new BasketItem(config.products[id], 0);
|
||||||
|
|
||||||
|
const oldQty = item.quantity;
|
||||||
|
item.quantity += quantity;
|
||||||
|
|
||||||
|
if (item.quantity <= 0) {
|
||||||
delete this.basket[id];
|
delete this.basket[id];
|
||||||
},
|
|
||||||
|
|
||||||
addToBasket(id: string, quantity: number): ErrorMessage {
|
|
||||||
const item: BasketItem =
|
|
||||||
this.basket[id] || new BasketItem(config.products[id], 0);
|
|
||||||
|
|
||||||
const oldQty = item.quantity;
|
|
||||||
item.quantity += quantity;
|
|
||||||
|
|
||||||
if (item.quantity <= 0) {
|
|
||||||
delete this.basket[id];
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
this.basket[id] = item;
|
|
||||||
|
|
||||||
if (this.sumBasket() > this.customerBalance) {
|
|
||||||
item.quantity = oldQty;
|
|
||||||
if (item.quantity === 0) {
|
|
||||||
delete this.basket[id];
|
|
||||||
}
|
|
||||||
return gettext("Not enough money");
|
|
||||||
}
|
|
||||||
|
|
||||||
return "";
|
return "";
|
||||||
},
|
}
|
||||||
|
|
||||||
getBasketSize() {
|
this.basket[id] = item;
|
||||||
return Object.keys(this.basket).length;
|
|
||||||
},
|
|
||||||
|
|
||||||
sumBasket() {
|
if (this.sumBasket() > this.customerBalance) {
|
||||||
if (this.getBasketSize() === 0) {
|
item.quantity = oldQty;
|
||||||
return 0;
|
if (item.quantity === 0) {
|
||||||
|
delete this.basket[id];
|
||||||
}
|
}
|
||||||
const total = Object.values(this.basket).reduce(
|
return gettext("Not enough money");
|
||||||
(acc: number, cur: BasketItem) => acc + cur.sum(),
|
}
|
||||||
0,
|
|
||||||
) as number;
|
|
||||||
return total;
|
|
||||||
},
|
|
||||||
|
|
||||||
showAlertMessage(message: string) {
|
return "";
|
||||||
if (this.alertMessage.timeout !== null) {
|
},
|
||||||
clearTimeout(this.alertMessage.timeout);
|
|
||||||
|
getBasketSize() {
|
||||||
|
return Object.keys(this.basket).length;
|
||||||
|
},
|
||||||
|
|
||||||
|
sumBasket() {
|
||||||
|
if (this.getBasketSize() === 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
const total = Object.values(this.basket).reduce(
|
||||||
|
(acc: number, cur: BasketItem) => acc + cur.sum(),
|
||||||
|
0,
|
||||||
|
) as number;
|
||||||
|
return total;
|
||||||
|
},
|
||||||
|
|
||||||
|
showAlertMessage(message: string) {
|
||||||
|
if (this.alertMessage.timeout !== null) {
|
||||||
|
clearTimeout(this.alertMessage.timeout);
|
||||||
|
}
|
||||||
|
this.alertMessage.content = message;
|
||||||
|
this.alertMessage.show = true;
|
||||||
|
this.alertMessage.timeout = setTimeout(() => {
|
||||||
|
this.alertMessage.show = false;
|
||||||
|
this.alertMessage.timeout = null;
|
||||||
|
}, 2000);
|
||||||
|
},
|
||||||
|
|
||||||
|
addToBasketWithMessage(id: string, quantity: number) {
|
||||||
|
const message = this.addToBasket(id, quantity);
|
||||||
|
if (message.length > 0) {
|
||||||
|
this.showAlertMessage(message);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onRefillingSuccess(event: CustomEvent) {
|
||||||
|
if (event.type !== "htmx:after-request" || event.detail.failed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.customerBalance += Number.parseFloat(
|
||||||
|
(event.detail.target.querySelector("#id_amount") as HTMLInputElement).value,
|
||||||
|
);
|
||||||
|
document.getElementById("selling-accordion").click();
|
||||||
|
this.codeField.widget.focus();
|
||||||
|
},
|
||||||
|
|
||||||
|
finish() {
|
||||||
|
if (this.getBasketSize() === 0) {
|
||||||
|
this.showAlertMessage(gettext("You can't send an empty basket."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.$refs.basketForm.submit();
|
||||||
|
},
|
||||||
|
|
||||||
|
cancel() {
|
||||||
|
location.href = config.cancelUrl;
|
||||||
|
},
|
||||||
|
|
||||||
|
handleCode() {
|
||||||
|
const [quantity, code] = this.codeField.getSelectedProduct() as [number, string];
|
||||||
|
|
||||||
|
if (this.codeField.getOperationCodes().includes(code.toUpperCase())) {
|
||||||
|
if (code === "ANN") {
|
||||||
|
this.cancel();
|
||||||
}
|
}
|
||||||
this.alertMessage.content = message;
|
if (code === "FIN") {
|
||||||
this.alertMessage.show = true;
|
this.finish();
|
||||||
this.alertMessage.timeout = setTimeout(() => {
|
|
||||||
this.alertMessage.show = false;
|
|
||||||
this.alertMessage.timeout = null;
|
|
||||||
}, 2000);
|
|
||||||
},
|
|
||||||
|
|
||||||
addToBasketWithMessage(id: string, quantity: number) {
|
|
||||||
const message = this.addToBasket(id, quantity);
|
|
||||||
if (message.length > 0) {
|
|
||||||
this.showAlertMessage(message);
|
|
||||||
}
|
}
|
||||||
},
|
} else {
|
||||||
|
this.addToBasketWithMessage(code, quantity);
|
||||||
onRefillingSuccess(event: CustomEvent) {
|
}
|
||||||
if (event.type !== "htmx:after-request" || event.detail.failed) {
|
this.codeField.widget.clear();
|
||||||
return;
|
this.codeField.widget.focus();
|
||||||
}
|
},
|
||||||
this.customerBalance += Number.parseFloat(
|
}));
|
||||||
(event.detail.target.querySelector("#id_amount") as HTMLInputElement).value,
|
|
||||||
);
|
|
||||||
document.getElementById("selling-accordion").click();
|
|
||||||
this.codeField.widget.focus();
|
|
||||||
},
|
|
||||||
|
|
||||||
finish() {
|
|
||||||
if (this.getBasketSize() === 0) {
|
|
||||||
this.showAlertMessage(gettext("You can't send an empty basket."));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.$refs.basketForm.submit();
|
|
||||||
},
|
|
||||||
|
|
||||||
cancel() {
|
|
||||||
location.href = config.cancelUrl;
|
|
||||||
},
|
|
||||||
|
|
||||||
handleCode() {
|
|
||||||
const [quantity, code] = this.codeField.getSelectedProduct() as [
|
|
||||||
number,
|
|
||||||
string,
|
|
||||||
];
|
|
||||||
|
|
||||||
if (this.codeField.getOperationCodes().includes(code.toUpperCase())) {
|
|
||||||
if (code === "ANN") {
|
|
||||||
this.cancel();
|
|
||||||
}
|
|
||||||
if (code === "FIN") {
|
|
||||||
this.finish();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.addToBasketWithMessage(code, quantity);
|
|
||||||
}
|
|
||||||
this.codeField.widget.clear();
|
|
||||||
this.codeField.widget.focus();
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$(() => {
|
$(() => {
|
||||||
|
@ -27,7 +27,13 @@
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
<h4>{{ counter }}</h4>
|
<h4>{{ counter }}</h4>
|
||||||
|
|
||||||
<div id="bar-ui" x-data="counter">
|
<div id="bar-ui" x-data="counter({
|
||||||
|
customerBalance: {{ customer.amount }},
|
||||||
|
products: products,
|
||||||
|
customerId: {{ customer.pk }},
|
||||||
|
formInitial: formInitial,
|
||||||
|
cancelUrl: '{{ cancel_url }}',
|
||||||
|
})">
|
||||||
<noscript>
|
<noscript>
|
||||||
<p class="important">Javascript is required for the counter UI.</p>
|
<p class="important">Javascript is required for the counter UI.</p>
|
||||||
</noscript>
|
</noscript>
|
||||||
@ -256,13 +262,7 @@
|
|||||||
{%- endfor -%}
|
{%- endfor -%}
|
||||||
];
|
];
|
||||||
window.addEventListener("DOMContentLoaded", () => {
|
window.addEventListener("DOMContentLoaded", () => {
|
||||||
loadCounter({
|
loadCounter();
|
||||||
customerBalance: {{ customer.amount }},
|
|
||||||
products: products,
|
|
||||||
customerId: {{ customer.pk }},
|
|
||||||
formInitial: formInitial,
|
|
||||||
cancelUrl: "{{ cancel_url }}",
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
{% endblock script %}
|
{% endblock script %}
|
@ -681,6 +681,42 @@ class TestCounterClick(TestFullClickBase):
|
|||||||
-3 - settings.SITH_ECOCUP_LIMIT
|
-3 - settings.SITH_ECOCUP_LIMIT
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_recordings_when_negative(self):
|
||||||
|
self.refill_user(
|
||||||
|
self.customer,
|
||||||
|
self.cons.selling_price * 3 + Decimal(self.beer.selling_price),
|
||||||
|
)
|
||||||
|
self.customer.customer.recorded_products = settings.SITH_ECOCUP_LIMIT * -10
|
||||||
|
self.customer.customer.save()
|
||||||
|
self.login_in_bar(self.barmen)
|
||||||
|
assert (
|
||||||
|
self.submit_basket(
|
||||||
|
self.customer,
|
||||||
|
[BasketItem(self.dcons.id, 1)],
|
||||||
|
).status_code
|
||||||
|
== 200
|
||||||
|
)
|
||||||
|
assert self.updated_amount(
|
||||||
|
self.customer
|
||||||
|
) == self.cons.selling_price * 3 + Decimal(self.beer.selling_price)
|
||||||
|
assert (
|
||||||
|
self.submit_basket(
|
||||||
|
self.customer,
|
||||||
|
[BasketItem(self.cons.id, 3)],
|
||||||
|
).status_code
|
||||||
|
== 302
|
||||||
|
)
|
||||||
|
assert self.updated_amount(self.customer) == Decimal(self.beer.selling_price)
|
||||||
|
|
||||||
|
assert (
|
||||||
|
self.submit_basket(
|
||||||
|
self.customer,
|
||||||
|
[BasketItem(self.beer.id, 1)],
|
||||||
|
).status_code
|
||||||
|
== 302
|
||||||
|
)
|
||||||
|
assert self.updated_amount(self.customer) == 0
|
||||||
|
|
||||||
|
|
||||||
class TestCounterStats(TestCase):
|
class TestCounterStats(TestCase):
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -126,6 +126,11 @@ class BaseBasketForm(BaseFormSet):
|
|||||||
if form.product.is_unrecord_product:
|
if form.product.is_unrecord_product:
|
||||||
self.total_recordings += form.cleaned_data["quantity"]
|
self.total_recordings += form.cleaned_data["quantity"]
|
||||||
|
|
||||||
|
# We don't want to block an user that have negative recordings
|
||||||
|
# if he isn't recording anything or reducing it's recording count
|
||||||
|
if self.total_recordings <= 0:
|
||||||
|
return
|
||||||
|
|
||||||
if not customer.can_record_more(self.total_recordings):
|
if not customer.can_record_more(self.total_recordings):
|
||||||
raise ValidationError(_("This user have reached his recording limit"))
|
raise ValidationError(_("This user have reached his recording limit"))
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user