diff --git a/counter/forms.py b/counter/forms.py index 33bfd51a..a0ba39e5 100644 --- a/counter/forms.py +++ b/counter/forms.py @@ -464,48 +464,47 @@ class CloseCustomerAccountForm(forms.Form): ) -class BasketProductForm(forms.Form): +class BasketItemForm(forms.Form): quantity = forms.IntegerField(min_value=1, required=True) - id = forms.IntegerField(min_value=0, required=True) + price_id = forms.IntegerField(min_value=0, required=True) def __init__( self, customer: Customer, counter: Counter, - allowed_products: dict[int, Product], + allowed_prices: dict[int, Price], *args, **kwargs, ): self.customer = customer # Used by formset self.counter = counter # Used by formset - self.allowed_products = allowed_products + self.allowed_prices = allowed_prices super().__init__(*args, **kwargs) - def clean_id(self): - data = self.cleaned_data["id"] + def clean_price_id(self): + data = self.cleaned_data["price_id"] - # We store self.product so we can use it later on the formset validation + # We store self.price so we can use it later on the formset validation # And also in the global clean - self.product = self.allowed_products.get(data, None) - if self.product is None: + self.price = self.allowed_prices.get(data, None) + if self.price is None: raise forms.ValidationError( _("The selected product isn't available for this user") ) - return data def clean(self): cleaned_data = super().clean() if len(self.errors) > 0: - return + return cleaned_data # Compute prices cleaned_data["bonus_quantity"] = 0 - if self.product.tray: + if self.price.product.tray: cleaned_data["bonus_quantity"] = math.floor( cleaned_data["quantity"] / Product.QUANTITY_FOR_TRAY_PRICE ) - cleaned_data["total_price"] = self.product.price * ( + cleaned_data["total_price"] = self.price.amount * ( cleaned_data["quantity"] - cleaned_data["bonus_quantity"] ) @@ -529,8 +528,8 @@ class BaseBasketForm(forms.BaseFormSet): raise forms.ValidationError(_("Submitted basket is invalid")) def _check_product_are_unique(self): - product_ids = {form.cleaned_data["id"] for form in self.forms} - if len(product_ids) != len(self.forms): + price_ids = {form.cleaned_data["price_id"] for form in self.forms} + if len(price_ids) != len(self.forms): raise forms.ValidationError(_("Duplicated product entries.")) def _check_enough_money(self, counter: Counter, customer: Customer): @@ -540,10 +539,9 @@ class BaseBasketForm(forms.BaseFormSet): def _check_recorded_products(self, customer: Customer): """Check for, among other things, ecocups and pitchers""" - items = { - form.cleaned_data["id"]: form.cleaned_data["quantity"] - for form in self.forms - } + items = defaultdict(int) + for form in self.forms: + items[form.price.product_id] += form.cleaned_data["quantity"] ids = list(items.keys()) returnables = list( ReturnableProduct.objects.filter( @@ -569,7 +567,7 @@ class BaseBasketForm(forms.BaseFormSet): BasketForm = forms.formset_factory( - BasketProductForm, formset=BaseBasketForm, absolute_max=None, min_num=1 + BasketItemForm, formset=BaseBasketForm, absolute_max=None, min_num=1 ) diff --git a/counter/models.py b/counter/models.py index 10377635..cf2e0813 100644 --- a/counter/models.py +++ b/counter/models.py @@ -784,36 +784,14 @@ class Counter(models.Model): # but they share the same primary key return self.type == "BAR" and any(b.pk == customer.pk for b in self.barmen_list) - def get_products_for(self, customer: Customer) -> list[Product]: - """ - Get all allowed products for the provided customer on this counter - Prices will be annotated - """ - - products = ( - self.products.filter(archived=False) - .select_related("product_type") - .prefetch_related("buying_groups") + def get_prices_for(self, customer: Customer) -> list[Price]: + return list( + Price.objects.filter(product__counters=self) + .for_user(customer.user) + .select_related("product", "product__product_type") + .prefetch_related("groups") ) - # Only include age appropriate products - age = customer.user.age - if customer.user.is_banned_alcohol: - age = min(age, 17) - products = products.filter(limit_age__lte=age) - - # Compute special price for customer if he is a barmen on that bar - if self.customer_is_barman(customer): - products = products.annotate(price=F("special_selling_price")) - else: - products = products.annotate(price=F("selling_price")) - - return [ - product - for product in products.all() - if product.can_be_sold_to(customer.user) - ] - class RefillingQuerySet(models.QuerySet): def annotate_total(self) -> Self: diff --git a/counter/static/bundled/counter/basket.ts b/counter/static/bundled/counter/basket.ts index 666d0874..24161ea9 100644 --- a/counter/static/bundled/counter/basket.ts +++ b/counter/static/bundled/counter/basket.ts @@ -1,9 +1,8 @@ -import type { Product } from "#counter:counter/types.ts"; +import type { Product } from "#counter:counter/types"; export class BasketItem { quantity: number; product: Product; - quantityForTrayPrice: number; errors: string[]; constructor(product: Product, quantity: number) { @@ -20,6 +19,6 @@ export class BasketItem { } sum(): number { - return (this.quantity - this.getBonusQuantity()) * this.product.price; + return (this.quantity - this.getBonusQuantity()) * this.product.price.amount; } } diff --git a/counter/static/bundled/counter/counter-click-index.ts b/counter/static/bundled/counter/counter-click-index.ts index 3e940bf8..1d5ce0ff 100644 --- a/counter/static/bundled/counter/counter-click-index.ts +++ b/counter/static/bundled/counter/counter-click-index.ts @@ -1,11 +1,11 @@ -import { AlertMessage } from "#core:utils/alert-message.ts"; -import { BasketItem } from "#counter:counter/basket.ts"; +import { AlertMessage } from "#core:utils/alert-message"; +import { BasketItem } from "#counter:counter/basket"; import type { CounterConfig, ErrorMessage, ProductFormula, -} from "#counter:counter/types.ts"; -import type { CounterProductSelect } from "./components/counter-product-select-index.ts"; +} from "#counter:counter/types"; +import type { CounterProductSelect } from "./components/counter-product-select-index"; document.addEventListener("alpine:init", () => { Alpine.data("counter", (config: CounterConfig) => ({ diff --git a/counter/static/bundled/counter/types.d.ts b/counter/static/bundled/counter/types.d.ts index 330b6f0e..154304ff 100644 --- a/counter/static/bundled/counter/types.d.ts +++ b/counter/static/bundled/counter/types.d.ts @@ -21,11 +21,15 @@ export interface CounterConfig { cancelUrl: string; } +interface Price { + id: number; + amount: number; +} + export interface Product { - id: string; code: string; name: string; - price: number; + price: Price; hasTrayPrice: boolean; quantityForTrayPrice: number; } diff --git a/counter/templates/counter/counter_click.jinja b/counter/templates/counter/counter_click.jinja index 07bfd461..932cc965 100644 --- a/counter/templates/counter/counter_click.jinja +++ b/counter/templates/counter/counter_click.jinja @@ -6,10 +6,10 @@ {% endblock %} {% block additional_css %} - - - - + + + + {% endblock %} @@ -65,10 +65,10 @@ - {%- for category in categories.keys() -%} + {%- for category, prices in categories.items() -%} {%- endfor -%} @@ -103,24 +103,25 @@