diff --git a/Administration/admin/prices.py b/Administration/admin/prices.py index b0062401..1b5a78b8 100644 --- a/Administration/admin/prices.py +++ b/Administration/admin/prices.py @@ -12,7 +12,7 @@ from Administration.admin.site import staff_admin_site from ApiBillet.permissions import TenantAdminPermissionWithRequest -from BaseBillet.models import Product, Price, PromotionalCode +from BaseBillet.models import Product, Price, PromotionalCode, MembershipProduct from Customers.models import Client from fedow_public.models import AssetFedowPublic as Asset, AssetFedowPublic @@ -170,8 +170,7 @@ def __init__(self, *args, **kwargs): ].widget = HiddenInput() # caché sauf si bouton + en haut a droite # Filtrage des produits : uniquement des produits adhésions. # Possible facilement car Foreign Key (voir get_search_results dans ProductAdmin) - self.fields["adhesions_obligatoires"].queryset = Product.objects.filter( - categorie_article=Product.ADHESION, + self.fields["adhesions_obligatoires"].queryset = MembershipProduct.objects.filter( archive=False, ) # Pas de bouton "+" pour creer un produit depuis ce champ diff --git a/Administration/admin/products.py b/Administration/admin/products.py index b673e353..0a2b92b9 100644 --- a/Administration/admin/products.py +++ b/Administration/admin/products.py @@ -1312,10 +1312,6 @@ class TicketProductAdmin(ProductAdmin): list_filter = ["publish", ProductArchiveFilter] # categorie_article inutile, deja filtre - def get_queryset(self, request): - qs = super().get_queryset(request) - return qs.filter(categorie_article__in=[Product.BILLET, Product.FREERES]) - @admin.register(MembershipProduct, site=staff_admin_site) class MembershipProductAdmin(HelpDisplayMixin, ProductAdmin): @@ -1335,10 +1331,6 @@ class MembershipProductAdmin(HelpDisplayMixin, ProductAdmin): list_filter = ["publish", ProductArchiveFilter] # categorie_article inutile, deja filtre - def get_queryset(self, request): - qs = super().get_queryset(request) - return qs.filter(categorie_article=Product.ADHESION) - # --------------------------------------------------------------------------- # POSProduct — proxy pour les produits de caisse (methode_caisse IS NOT NULL) diff --git a/Administration/admin_tenant.py b/Administration/admin_tenant.py index 5a7243e1..fab20576 100644 --- a/Administration/admin_tenant.py +++ b/Administration/admin_tenant.py @@ -1519,7 +1519,7 @@ def lookups(self, request, model_admin): # Return only product that are not archived to display in the filter return [ (product.pk, product.name) - for product in MembershipProduct.objects.filter(categorie_article=Product.ADHESION,archive=False) + for product in MembershipProduct.objects.filter(archive=False) ] def queryset(self, request, queryset): diff --git a/ApiBillet/serializers.py b/ApiBillet/serializers.py index 252a077d..6221a13a 100644 --- a/ApiBillet/serializers.py +++ b/ApiBillet/serializers.py @@ -13,7 +13,8 @@ from rest_framework import serializers from rest_framework.generics import get_object_or_404 from BaseBillet.models import Event, Price, Product, Reservation, Configuration, LigneArticle, Ticket, Paiement_stripe, \ - PriceSold, ProductSold, Artist_on_event, OptionGenerale, Tag, Membership, PostalAddress, PromotionalCode, SaleOrigin + PriceSold, ProductSold, Artist_on_event, OptionGenerale, Tag, Membership, PostalAddress, PromotionalCode, \ + SaleOrigin, MembershipProduct from Customers.models import Client from PaiementStripe.views import CreationPaiementStripe from fedow_connect.utils import dround @@ -174,7 +175,7 @@ class Meta: class PriceSerializer(serializers.ModelSerializer): product = serializers.PrimaryKeyRelatedField(queryset=Product.objects.all()) adhesions_obligatoires = serializers.PrimaryKeyRelatedField( - queryset=Product.objects.filter(categorie_article=Product.ADHESION), + queryset=MembershipProduct.objects.filter(), required=False, many=True, ) diff --git a/ApiBillet/views.py b/ApiBillet/views.py index f9a2cdc1..104415f9 100644 --- a/ApiBillet/views.py +++ b/ApiBillet/views.py @@ -32,7 +32,7 @@ from AuthBillet.models import HumanUser from AuthBillet.utils import get_or_create_user from BaseBillet.models import Event, Price, Product, Reservation, Configuration, Ticket, Paiement_stripe, \ - OptionGenerale, Membership, PaymentMethod, LigneArticle + OptionGenerale, Membership, PaymentMethod, LigneArticle, MembershipProduct from BaseBillet.tasks import create_ticket_pdf, send_stripe_bank_deposit_to_laboutik, send_payment_refused_user from BaseBillet.tasks import send_membership_sepa_pending_user from Customers.models import Client @@ -235,8 +235,7 @@ def list(self, request): dict_return = {'uuid': f"{connection.tenant.uuid}"} dict_return.update(place_serialized.data) - products_adhesion = Product.objects.filter( - categorie_article=Product.ADHESION, + products_adhesion = MembershipProduct.objects.filter( prices__isnull=False, publish=True, ).distinct() diff --git a/BaseBillet/models.py b/BaseBillet/models.py index cd7e75d2..2351dd17 100644 --- a/BaseBillet/models.py +++ b/BaseBillet/models.py @@ -1123,22 +1123,44 @@ class Meta: unique_together = ('categorie_article', 'name') +class TicketProductManager(models.Manager): + """ + Manager for TicketProduct. + With it, when using 'TicketProduct.objects.all()' only product with categorie_article__in=[Product.BILLET, Product.FREERES] will be returned + """ + def get_queryset(self): + return super().get_queryset().filter(categorie_article__in=[Product.BILLET, Product.FREERES]) + + class TicketProduct(Product): """Proxy pour afficher uniquement les produits billetterie dans l'admin. Proxy to display only ticket products in admin. Meme table, zero migration.""" + objects = TicketProductManager() + class Meta: proxy = True verbose_name = _("Ticket product") verbose_name_plural = _("Ticket products") +class MembershipProductManager(models.Manager): + """ + Manager for MembershipProduct. + With it, when using 'MembershipProduct.objects.all()' only product with categorie_article=Product.ADHESION will be returned + """ + def get_queryset(self): + return super().get_queryset().filter(categorie_article=Product.ADHESION) + + class MembershipProduct(Product): """Proxy pour afficher uniquement les produits adhesion dans l'admin. Proxy to display only membership products in admin. Meme table, zero migration.""" + objects = MembershipProductManager() + class Meta: proxy = True verbose_name = _("Membership product") diff --git a/BaseBillet/sitemap.py b/BaseBillet/sitemap.py index 49234aea..151041c5 100644 --- a/BaseBillet/sitemap.py +++ b/BaseBillet/sitemap.py @@ -1,7 +1,8 @@ from django.contrib.sitemaps import Sitemap from django.urls import reverse from django.db import connection -from BaseBillet.models import Event, Product +from BaseBillet.models import Event, Product, MembershipProduct + # This file defines the sitemaps for the TiBillet application. # The sitemaps are accessible at: @@ -76,7 +77,7 @@ class ProductSitemap(TenantSitemap): def items(self): # Only include published membership products - return Product.objects.filter(publish=True, categorie_article=Product.ADHESION) + return MembershipProduct.objects.filter(publish=True) def location(self, obj): # URL for each membership product - returns absolute path without protocol or domain diff --git a/BaseBillet/views.py b/BaseBillet/views.py index f66ae908..28baf390 100644 --- a/BaseBillet/views.py +++ b/BaseBillet/views.py @@ -50,7 +50,7 @@ from BaseBillet.models import Configuration, Ticket, Product, Event, Tag, Paiement_stripe, Membership, Reservation, \ FormbricksConfig, FormbricksForms, FederatedPlace, Carrousel, LigneArticle, PriceSold, \ Price, ProductSold, PaymentMethod, PostalAddress, SaleOrigin, ProductFormField, GhostConfig, BrevoConfig, \ - FederationConfiguration + FederationConfiguration, MembershipProduct from BaseBillet.tasks import create_membership_invoice_pdf, send_membership_invoice_to_email, \ contact_mailer, send_to_ghost_email, send_sale_to_laboutik, \ send_payment_success_admin, send_payment_success_user, send_reservation_cancellation_user, \ @@ -2582,8 +2582,7 @@ def list(self, request: HttpRequest): }) template_context['federated_tenants'] = federated_tenant_dict - template_context['products'] = Product.objects.filter(categorie_article=Product.ADHESION, - publish=True).prefetch_related('tag') + template_context['products'] = MembershipProduct.objects.filter(publish=True).prefetch_related('tag') # Résolution du template avec fallback vers reunion config = Configuration.get_solo() @@ -2597,8 +2596,7 @@ def list(self, request: HttpRequest): @action(detail=False, methods=['GET']) def embed(self, request): template_context = get_context(request) - template_context['products'] = Product.objects.filter(categorie_article=Product.ADHESION, - publish=True).prefetch_related('tag') + template_context['products'] = MembershipProduct.objects.filter(publish=True).prefetch_related('tag') template_context['embed'] = True response = render( request, "reunion/views/membership/list.html", @@ -2616,7 +2614,7 @@ def get_federated_membership_url(self, uuid=uuid): for tenant in set(tenants): with tenant_context(tenant): try: - product = Product.objects.get(uuid=uuid, categorie_article=Product.ADHESION, publish=True) + product = MembershipProduct.objects.get(uuid=uuid, publish=True) url = f"https://{tenant.get_primary_domain().domain}/memberships/{product.uuid}" return url except Product.DoesNotExist: @@ -2633,7 +2631,7 @@ def retrieve(self, request, pk): ''' try: # On essaye sur ce tenant : - product = Product.objects.get(uuid=pk, categorie_article=Product.ADHESION, publish=True) + product = MembershipProduct.objects.get(uuid=pk, publish=True) context = get_context(request) context['product'] = product