diff --git a/counter/fields.py b/counter/fields.py
index 0bde4801..caf3a584 100644
--- a/counter/fields.py
+++ b/counter/fields.py
@@ -32,7 +32,8 @@ class CurrencyField(models.DecimalField):
res.append(MinValueValidator(self.min_value))
return [*super().validators, *res]
- def check(self, **kwargs):
+ def check(self, **kwargs): # pragma: no cover
+ # this is executed during runserver, but won't run in prod
errors = super().check(**kwargs)
for name, val in ("min_value", self.min_value), ("max_value", self.max_value):
if not val:
diff --git a/counter/forms.py b/counter/forms.py
index 18efa724..1794e2ba 100644
--- a/counter/forms.py
+++ b/counter/forms.py
@@ -565,16 +565,7 @@ class BasketItemForm(forms.Form):
quantity = forms.IntegerField(min_value=1, required=True)
price_id = forms.IntegerField(min_value=0, required=True)
- def __init__(
- self,
- customer: Customer,
- counter: Counter,
- allowed_prices: dict[int, Price],
- *args,
- **kwargs,
- ):
- self.customer = customer # Used by formset
- self.counter = counter # Used by formset
+ def __init__(self, allowed_prices: dict[int, Price], *args, **kwargs):
self.allowed_prices = allowed_prices
super().__init__(*args, **kwargs)
@@ -609,6 +600,11 @@ class BasketItemForm(forms.Form):
class BaseBasketForm(forms.BaseFormSet):
+ def __init__(self, *args, customer: Customer, counter: Counter, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.customer = customer
+ self.counter = counter
+
def clean(self):
self.forms = [form for form in self.forms if form.cleaned_data != {}]
@@ -617,8 +613,9 @@ class BaseBasketForm(forms.BaseFormSet):
self._check_forms_have_errors()
self._check_product_are_unique()
- self._check_recorded_products(self[0].customer)
- self._check_enough_money(self[0].counter, self[0].customer)
+ self._check_recorded_products()
+ self._check_enough_money()
+ self._check_refills()
def _check_forms_have_errors(self):
if any(len(form.errors) > 0 for form in self):
@@ -629,12 +626,12 @@ class BaseBasketForm(forms.BaseFormSet):
if len(price_ids) != len(self.forms):
raise forms.ValidationError(_("Duplicated product entries."))
- def _check_enough_money(self, counter: Counter, customer: Customer):
+ def _check_enough_money(self):
self.total_price = sum([data["total_price"] for data in self.cleaned_data])
- if self.total_price > customer.amount:
+ if self.total_price > self.customer.amount:
raise forms.ValidationError(_("Not enough money"))
- def _check_recorded_products(self, customer: Customer):
+ def _check_recorded_products(self):
"""Check for, among other things, ecocups and pitchers"""
items = defaultdict(int)
for form in self.forms:
@@ -643,7 +640,7 @@ class BaseBasketForm(forms.BaseFormSet):
returnables = list(
ReturnableProduct.objects.filter(
Q(product_id__in=ids) | Q(returned_product_id__in=ids)
- ).annotate_balance_for(customer)
+ ).annotate_balance_for(self.customer)
)
limit_reached = []
for returnable in returnables:
@@ -662,6 +659,13 @@ class BaseBasketForm(forms.BaseFormSet):
% ", ".join([str(p) for p in limit_reached])
)
+ def _check_refills(self):
+ refill_type_id = settings.SITH_COUNTER_PRODUCTTYPE_REFILLING
+ if any(f.price.product.product_type_id == refill_type_id for f in self.forms):
+ raise ValidationError(
+ _("Refill bonds cannot be purchased outside of the eboutic")
+ )
+
BasketForm = forms.formset_factory(
BasketItemForm, formset=BaseBasketForm, absolute_max=None, min_num=1
diff --git a/counter/models.py b/counter/models.py
index fb180191..cd88e7e5 100644
--- a/counter/models.py
+++ b/counter/models.py
@@ -827,7 +827,7 @@ class Refilling(models.Model):
counter = models.ForeignKey(
Counter, related_name="refillings", blank=False, on_delete=models.CASCADE
)
- amount = CurrencyField(_("amount"), min_value=0.01)
+ amount: CurrencyField = CurrencyField(_("amount"), min_value=0.01)
operator = models.ForeignKey(
User,
related_name="refillings_as_operator",
@@ -883,7 +883,7 @@ class Refilling(models.Model):
def clean(self):
super().clean()
- if self.amount + self.customer.amount > settings.SITH_ACCOUNT_MAX_MONEY:
+ if (self.amount + self.customer.amount) > settings.SITH_ACCOUNT_MAX_MONEY:
raise ValidationError(
_("There cannot be more than %(money)d€ on an AE account")
% {"money": settings.SITH_ACCOUNT_MAX_MONEY}
diff --git a/counter/views/click.py b/counter/views/click.py
index 7633327d..9ec678ae 100644
--- a/counter/views/click.py
+++ b/counter/views/click.py
@@ -73,13 +73,13 @@ class CounterClick(
current_tab = "counter"
def get_form_kwargs(self):
- kwargs = super().get_form_kwargs()
- kwargs["form_kwargs"] = {
+ return super().get_form_kwargs() | {
"customer": self.customer,
"counter": self.object,
- "allowed_prices": {price.id: price for price in self.prices},
+ "form_kwargs": {
+ "allowed_prices": {price.id: price for price in self.prices}
+ },
}
- return kwargs
def dispatch(self, request, *args, **kwargs):
self.customer = get_object_or_404(Customer, user_id=self.kwargs["user_id"])
diff --git a/eboutic/static/bundled/eboutic/eboutic-index.ts b/eboutic/static/bundled/eboutic/eboutic-index.ts
index 59a258e3..e2bbe9ec 100644
--- a/eboutic/static/bundled/eboutic/eboutic-index.ts
+++ b/eboutic/static/bundled/eboutic/eboutic-index.ts
@@ -5,10 +5,11 @@ interface BasketItem {
name: string;
quantity: number;
unitPrice: number;
+ isRefill: boolean;
}
const BASKET_CACHE_KEY = "basket";
-const BASKET_CACHE_VERSION = 1;
+const BASKET_CACHE_VERSION = 2;
document.addEventListener("alpine:init", () => {
Alpine.data("basket", (validPrices: number[], lastPurchaseTime?: number) => ({
@@ -21,7 +22,7 @@ document.addEventListener("alpine:init", () => {
});
document
.getElementById("id_form-TOTAL_FORMS")
- .setAttribute(":value", "basket.length");
+ ?.setAttribute(":value", "basket.length");
},
loadBasket(): BasketItem[] {
@@ -32,8 +33,8 @@ document.addEventListener("alpine:init", () => {
return [];
}
if (
- lastPurchaseTime !== null &&
- localStorage.basketTimestamp !== undefined &&
+ lastPurchaseTime &&
+ localStorage.basketTimestamp &&
new Date(lastPurchaseTime) >=
new Date(Number.parseInt(localStorage.basketTimestamp, 10))
) {
@@ -64,6 +65,15 @@ document.addEventListener("alpine:init", () => {
);
},
+ getTotalRefill() {
+ return this.basket
+ .filter((item) => item.isRefill)
+ .reduce(
+ (acc: number, item: BasketItem) => acc + item.quantity * item.unitPrice,
+ 0,
+ );
+ },
+
/**
* Add 1 to the quantity of an item in the basket
* @param {BasketItem} item
@@ -86,7 +96,7 @@ document.addEventListener("alpine:init", () => {
if (this.basket[index].quantity === 0) {
this.basket = this.basket.filter(
- (e: BasketItem) => e.priceId !== this.basket[index].id,
+ (e: BasketItem) => e.priceId !== this.basket[index].priceId,
);
}
},
@@ -103,14 +113,16 @@ document.addEventListener("alpine:init", () => {
* @param id The id of the product to add
* @param name The name of the product
* @param price The unit price of the product
+ * @param isRefill true if the product is a refill bond
* @returns The created item
*/
- createItem(id: number, name: string, price: number): BasketItem {
+ createItem(id: number, name: string, price: number, isRefill: boolean): BasketItem {
const newItem = {
priceId: id,
name,
quantity: 0,
unitPrice: price,
+ isRefill,
} as BasketItem;
this.basket.push(newItem);
@@ -125,16 +137,17 @@ document.addEventListener("alpine:init", () => {
* @param id The id of the product to add
* @param name The name of the product
* @param price The unit price of the product
+ * @param isRefill true if the product is a refill bond
*/
- addFromCatalog(id: number, name: string, price: number) {
- let item = this.basket.find((e: BasketItem) => e.priceId === id);
+ addFromCatalog(id: number, name: string, price: number, isRefill: boolean) {
+ const item = this.basket.find((e: BasketItem) => e.priceId === id);
// if the item is not in the basket, we create it
// else we add + 1 to it
if (item) {
this.add(item);
} else {
- item = this.createItem(id, name, price);
+ this.createItem(id, name, price, isRefill);
}
},
}));
diff --git a/eboutic/templates/eboutic/eboutic_main.jinja b/eboutic/templates/eboutic/eboutic_main.jinja
index 4962d5ce..fe5227d7 100644
--- a/eboutic/templates/eboutic/eboutic_main.jinja
+++ b/eboutic/templates/eboutic/eboutic_main.jinja
@@ -58,6 +58,17 @@
{% endif %}
+
+