From fed287ce43eef985cfa3d1d3971f714aecf3914f Mon Sep 17 00:00:00 2001 From: Raz Date: Mon, 5 May 2025 11:32:58 +0200 Subject: [PATCH 001/110] add animation type variable --- .../0118_tournament_animation_type.py | 18 ++++++++++++++++++ tournaments/models/__init__.py | 2 +- tournaments/models/enums.py | 4 ++++ tournaments/models/tournament.py | 3 ++- 4 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 tournaments/migrations/0118_tournament_animation_type.py diff --git a/tournaments/migrations/0118_tournament_animation_type.py b/tournaments/migrations/0118_tournament_animation_type.py new file mode 100644 index 0000000..bfea7d1 --- /dev/null +++ b/tournaments/migrations/0118_tournament_animation_type.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1 on 2025-05-03 05:34 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('tournaments', '0117_playerregistration_user_teamregistration_user_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='tournament', + name='animation_type', + field=models.IntegerField(choices=[(0, 'Tournoi'), (1, 'Mêlée')], default=0), + ), + ] diff --git a/tournaments/models/__init__.py b/tournaments/models/__init__.py index 8d1530e..9dd7d03 100644 --- a/tournaments/models/__init__.py +++ b/tournaments/models/__init__.py @@ -4,7 +4,7 @@ from .custom_user import CustomUser from .club import Club from .court import Court from .date_interval import DateInterval -from .enums import UserOrigin, TournamentPayment, FederalCategory, FederalLevelCategory, FederalAgeCategory, FederalMatchCategory, OnlineRegistrationStatus, RegistrationStatus, ModelOperation +from .enums import UserOrigin, TournamentPayment, FederalCategory, FederalLevelCategory, FederalAgeCategory, FederalMatchCategory, OnlineRegistrationStatus, RegistrationStatus, AnimationType, ModelOperation from .player_enums import PlayerSexType, PlayerDataSource, PlayerPaymentType from .event import Event from .tournament import Tournament, TeamSummon, TeamSortingType, TeamItem diff --git a/tournaments/models/enums.py b/tournaments/models/enums.py index 22abf9e..2e7315f 100644 --- a/tournaments/models/enums.py +++ b/tournaments/models/enums.py @@ -303,3 +303,7 @@ class RegistrationPaymentMode(models.IntegerChoices): CORPORATE = 1, 'Corporate' NO_FEE = 2, 'No Service Fee' STRIPE = 3, 'Stripe' + +class AnimationType(models.IntegerChoices): + TOURNAMENT = 0, 'Tournoi' + MELEE = 1, 'Mêlée' diff --git a/tournaments/models/tournament.py b/tournaments/models/tournament.py index 17bff1a..074d21f 100644 --- a/tournaments/models/tournament.py +++ b/tournaments/models/tournament.py @@ -1,6 +1,6 @@ from zoneinfo import ZoneInfo from django.db import models -from . import BaseModel, Event, TournamentPayment, FederalMatchCategory, FederalCategory, FederalLevelCategory, FederalAgeCategory, OnlineRegistrationStatus, RegistrationStatus +from . import BaseModel, Event, TournamentPayment, FederalMatchCategory, FederalCategory, FederalLevelCategory, FederalAgeCategory, OnlineRegistrationStatus, RegistrationStatus, AnimationType import uuid from django.utils import timezone, formats @@ -89,6 +89,7 @@ class Tournament(BaseModel): enable_time_to_confirm = models.BooleanField(default=False) is_corporate_tournament = models.BooleanField(default=False) is_template = models.BooleanField(default=False) + animation_type = models.IntegerField(default=AnimationType.TOURNAMENT, choices=AnimationType.choices) def delete_dependencies(self): for team_registration in self.team_registrations.all(): From 3cd541977df65d2b55732222e76c6f1c68e94c1b Mon Sep 17 00:00:00 2001 From: Raz Date: Mon, 5 May 2025 11:35:36 +0200 Subject: [PATCH 002/110] add animation type variable --- tournaments/models/enums.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tournaments/models/enums.py b/tournaments/models/enums.py index 2e7315f..1b606ca 100644 --- a/tournaments/models/enums.py +++ b/tournaments/models/enums.py @@ -307,3 +307,5 @@ class RegistrationPaymentMode(models.IntegerChoices): class AnimationType(models.IntegerChoices): TOURNAMENT = 0, 'Tournoi' MELEE = 1, 'Mêlée' + LOSER_BRACKET = 2, 'Classement' + CONSOLATION_BRACKET = 3, 'Consolation' From 525681d7aee299ae5aa85510b209fde98fff8db1 Mon Sep 17 00:00:00 2001 From: Raz Date: Mon, 5 May 2025 11:41:37 +0200 Subject: [PATCH 003/110] fix padding bubble --- tournaments/static/tournaments/css/style.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tournaments/static/tournaments/css/style.css b/tournaments/static/tournaments/css/style.css index a652b1b..d825689 100644 --- a/tournaments/static/tournaments/css/style.css +++ b/tournaments/static/tournaments/css/style.css @@ -1073,13 +1073,13 @@ h-margin { } .match-status-container-header { - margin-top: -20px; + margin-top: -10px; height: 40px; text-align: left; } .match-status-container-header-bottom { - margin-bottom: -20px; + margin-bottom: -10px; height: 40px; text-align: left; } From ffdb5ce74c559a957f9b68f7ef31465d3cf5bb8a Mon Sep 17 00:00:00 2001 From: Raz Date: Mon, 5 May 2025 13:17:33 +0200 Subject: [PATCH 004/110] add a mail notification to the umpire when team unregister and display canceled status for a team status in priority --- tournaments/models/player_registration.py | 10 +++++ tournaments/models/team_registration.py | 9 ++++ tournaments/models/tournament.py | 7 ++- tournaments/services/email_service.py | 43 ++++++++++++++++++ .../services/tournament_unregistration.py | 5 ++- .../tournaments/tournament_info.html | 44 +++++++++++-------- 6 files changed, 95 insertions(+), 23 deletions(-) diff --git a/tournaments/models/player_registration.py b/tournaments/models/player_registration.py index e7f3546..59141b0 100644 --- a/tournaments/models/player_registration.py +++ b/tournaments/models/player_registration.py @@ -111,6 +111,16 @@ class PlayerRegistration(SideStoreModel): tournament = self.team_registration.tournament tournament_status_team_count = tournament.get_tournament_status_team_count() + + if tournament.is_canceled(): + return { + 'header': "Équipes", + 'position': tournament_status_team_count, + 'display_box': True, + 'box_class': 'light-red', + 'short_label': 'annulé' + } + status = { 'header': "Équipes", 'position': tournament_status_team_count, diff --git a/tournaments/models/team_registration.py b/tournaments/models/team_registration.py index 76ce959..83f4b06 100644 --- a/tournaments/models/team_registration.py +++ b/tournaments/models/team_registration.py @@ -500,3 +500,12 @@ class TeamRegistration(SideStoreModel): tournament, TeamEmailType.REQUIRES_TIME_CONFIRMATION ) + + def is_unregistration_possible(self): + if self.call_date is not None: + return False + if self.bracket_position is not None: + return False + if self.group_stage_position is not None: + return False + return True diff --git a/tournaments/models/tournament.py b/tournaments/models/tournament.py index 074d21f..6ddcafa 100644 --- a/tournaments/models/tournament.py +++ b/tournaments/models/tournament.py @@ -974,8 +974,8 @@ class Tournament(BaseModel): timezoned_datetime -= timedelta(hours=hour_delta) return now >= timezoned_datetime - def will_start_soon(self): - return self.has_started(hour_delta=2) + def will_start_soon(self, hour_delta=2): + return self.has_started(hour_delta=hour_delta) def supposedly_in_progress(self): # end = self.start_date + timedelta(days=self.day_duration + 1) @@ -1214,6 +1214,9 @@ class Tournament(BaseModel): if self.supposedly_in_progress(): return False + if self.will_start_soon(12): + return False + if self.closed_registration_date is not None: return False diff --git a/tournaments/services/email_service.py b/tournaments/services/email_service.py index f3398a4..c7a572b 100644 --- a/tournaments/services/email_service.py +++ b/tournaments/services/email_service.py @@ -606,6 +606,49 @@ class TournamentEmailService: # If there's only one player, just send them the notification TournamentEmailService.notify(players[0], None, tournament, message_type) + @staticmethod + def notify_umpire(team, tournament, message_type): + # Notify the umpire if needed + umpire_email = tournament.umpire_mail() + if umpire_email: + tournament_details_str = tournament.build_tournament_details_str() + + federal_level_category = FederalLevelCategory(tournament.federal_level_category) + tournament_word = federal_level_category.localized_word() + tournament_prefix_that = federal_level_category.localized_prefix_that() + + body_parts = [ + "Email automatique suite à une désinscription", + f"\n\n{tournament_details_str} | {tournament.formatted_start_date()} | {tournament.event.club.name}" + "\n\nL'équipe ci-dessous a annulé son inscription via le site PadelClub :", + ] + + for player in team.players_sorted_by_rank: + body_parts.append( + f"\n{player.name()}" + ) + + absolute_url = f"https://padelclub.app/tournament/{tournament.id}/info" + link_text = f"informations sur {tournament_prefix_that}{tournament_word} sur https://padelclub.app" + absolute_url = f'{link_text}' + + body_parts.append( + f"\n\nVoir les {absolute_url}", + ) + + body_parts.extend([ + "\n\nCeci est un e-mail automatique." + ]) + + email_body = "".join(body_parts) + + if email_body is None: + return + + topic = message_type.email_topic(tournament.federal_level_category) + email_subject = TournamentEmailService.email_subject(tournament, topic) + TournamentEmailService._send_email(umpire_email, email_subject, email_body) + @staticmethod def _build_payment_info(tournament, team_registration): """ diff --git a/tournaments/services/tournament_unregistration.py b/tournaments/services/tournament_unregistration.py index f143aee..4d4869b 100644 --- a/tournaments/services/tournament_unregistration.py +++ b/tournaments/services/tournament_unregistration.py @@ -1,9 +1,8 @@ from django.contrib import messages from django.utils import timezone from ..models import PlayerRegistration, UnregisteredTeam, UnregisteredPlayer, PlayerPaymentType -from ..utils.licence_validator import LicenseValidator from ..services.payment_service import PaymentService -from ..services.email_service import TournamentEmailService +from ..services.email_service import TournamentEmailService, TeamEmailType class TournamentUnregistrationService: def __init__(self, request, tournament): @@ -96,6 +95,8 @@ class TournamentUnregistrationService: registered_online=player.registered_online ) + TournamentEmailService.notify_umpire(team_registration, team_registration.tournament, TeamEmailType.UNREGISTERED) + def _find_player_registration(self): # First check if we can find the player registration directly by user if self.request.user.is_authenticated: diff --git a/tournaments/templates/tournaments/tournament_info.html b/tournaments/templates/tournaments/tournament_info.html index d190335..2dcd190 100644 --- a/tournaments/templates/tournaments/tournament_info.html +++ b/tournaments/templates/tournaments/tournament_info.html @@ -121,26 +121,32 @@ {% endif %} - {% if tournament.is_unregistration_possible %} -
- {% if tournament.is_refund_possible and team.is_paid %} -

- Si vous vous désinscrivez, votre inscription sera remboursée automatiquement sur votre carte bancaire. - {% if tournament.refund_date_limit %} -

Remboursement possible jusqu'au {{ tournament.refund_date_limit|date:"d/m/Y à H:i" }}
- {% endif %} -

- {% endif %} -
- - {% if team.is_in_waiting_list >= 0 %} - Se retirer de la liste d'attente - {% else %} - Se désinscrire + {% if tournament.is_unregistration_possible and team.is_unregistration_possible %} + From e3e6603d654c03a0fd3872ccdf37dbd24b3283c4 Mon Sep 17 00:00:00 2001 From: Raz Date: Mon, 5 May 2025 13:45:43 +0200 Subject: [PATCH 005/110] fix padding bubble --- tournaments/static/tournaments/css/style.css | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tournaments/static/tournaments/css/style.css b/tournaments/static/tournaments/css/style.css index d825689..9820993 100644 --- a/tournaments/static/tournaments/css/style.css +++ b/tournaments/static/tournaments/css/style.css @@ -1073,14 +1073,14 @@ h-margin { } .match-status-container-header { - margin-top: -10px; - height: 40px; + margin-top: -20px; + height: 50px; text-align: left; } .match-status-container-header-bottom { - margin-bottom: -10px; - height: 40px; + margin-bottom: -20px; + height: 50px; text-align: left; } From cd71834fdf58f2da76615c9c008a031380debad0 Mon Sep 17 00:00:00 2001 From: Raz Date: Tue, 6 May 2025 12:21:59 +0200 Subject: [PATCH 006/110] add shipping adress and refund option --- shop/admin.py | 94 ++++++- shop/forms.py | 13 + ...ess_alter_order_payment_status_and_more.py | 41 +++ shop/models.py | 19 ++ shop/static/shop/css/shop.css | 226 +++++++++++++++ shop/stripe_utils.py | 60 ++++ .../admin/shop/order/preparation_view.html | 52 +++- shop/templates/shop/cart.html | 55 +++- shop/templates/shop/checkout.html | 15 +- shop/templates/shop/my_orders.html | 81 ++++++ shop/templates/shop/order_detail.html | 150 ++++++++++ .../shop/partials/navigation_base.html | 9 + .../shop/partials/order_items_display.html | 124 +++++---- shop/templates/shop/payment.html | 15 +- shop/templates/shop/payment_cancel.html | 14 +- shop/templates/shop/payment_success.html | 14 +- shop/templates/shop/product_list.html | 14 +- shop/urls.py | 6 +- shop/views.py | 263 +++++++++++++++++- .../0119_alter_tournament_animation_type.py | 18 ++ tournaments/templates/profile.html | 36 +-- .../tournaments/navigation_base.html | 1 + 22 files changed, 1153 insertions(+), 167 deletions(-) create mode 100644 shop/migrations/0027_shippingaddress_alter_order_payment_status_and_more.py create mode 100644 shop/templates/shop/my_orders.html create mode 100644 shop/templates/shop/order_detail.html create mode 100644 shop/templates/shop/partials/navigation_base.html create mode 100644 tournaments/migrations/0119_alter_tournament_animation_type.py diff --git a/shop/admin.py b/shop/admin.py index 72d69c6..9c61ae7 100644 --- a/shop/admin.py +++ b/shop/admin.py @@ -1,7 +1,10 @@ from django.contrib import admin from django.shortcuts import render -from .models import Product, Color, Size, Order, OrderItem, GuestUser, Coupon, CouponUsage, OrderStatus +from .models import Product, Color, Size, Order, OrderItem, GuestUser, Coupon, CouponUsage, OrderStatus, ShippingAddress from django.utils.html import format_html +from django.urls import path +from django.contrib import admin +from django.shortcuts import redirect @admin.register(Product) class ProductAdmin(admin.ModelAdmin): @@ -37,11 +40,63 @@ class OrderItemInline(admin.TabularInline): extra = 0 readonly_fields = ('product', 'quantity', 'color', 'size', 'price') +@admin.register(ShippingAddress) +class ShippingAddressAdmin(admin.ModelAdmin): + list_display = ('street_address', 'city', 'postal_code', 'country') + search_fields = ('street_address', 'city', 'postal_code', 'country') + @admin.register(Order) class OrderAdmin(admin.ModelAdmin): - list_display = ('id', 'date_ordered', 'status', 'total_price') + list_display = ('id', 'date_ordered', 'status', 'total_price', 'get_shipping_address') inlines = [OrderItemInline] list_filter = ('status', 'payment_status') + readonly_fields = ('shipping_address_details',) + + def get_shipping_address(self, obj): + if obj.shipping_address: + return f"{obj.shipping_address.street_address}, {obj.shipping_address.city}" + return "No shipping address" + get_shipping_address.short_description = 'Shipping Address' + + def shipping_address_details(self, obj): + if obj.shipping_address: + return format_html( + """ +
+ Street: {}
+ {} + City: {}
+ State: {}
+ Postal Code: {}
+ Country: {} +
+ """, + obj.shipping_address.street_address, + f"Apartment: {obj.shipping_address.apartment}
" if obj.shipping_address.apartment else "", + obj.shipping_address.city, + obj.shipping_address.state, + obj.shipping_address.postal_code, + obj.shipping_address.country, + ) + return "No shipping address set" + shipping_address_details.short_description = 'Shipping Address Details' + + fieldsets = ( + (None, { + 'fields': ('user', 'guest_user', 'status', 'payment_status', 'total_price') + }), + ('Shipping Information', { + 'fields': ('shipping_address_details',), + }), + ('Payment Details', { + 'fields': ('stripe_payment_intent_id', 'stripe_checkout_session_id', 'stripe_mode'), + 'classes': ('collapse',) + }), + ('Discount Information', { + 'fields': ('coupon', 'discount_amount'), + 'classes': ('collapse',) + }), + ) def changelist_view(self, request, extra_context=None): # If 'show_preparation' parameter is in the request, show the preparation view @@ -103,6 +158,41 @@ class OrderAdmin(admin.ModelAdmin): context ) + def get_urls(self): + urls = super().get_urls() + custom_urls = [ + path('prepare-all-orders/', self.admin_site.admin_view(self.prepare_all_orders), name='prepare_all_orders'), + path('prepare-order//', self.admin_site.admin_view(self.prepare_order), name='prepare_order'), + path('cancel-and-refund-order//', self.admin_site.admin_view(self.cancel_and_refund_order), name='cancel_and_refund_order'), + ] + return custom_urls + urls + + def prepare_all_orders(self, request): + if request.method == 'POST': + Order.objects.filter(status=OrderStatus.PAID).update(status=OrderStatus.PREPARED) + self.message_user(request, "All orders have been marked as prepared.") + return redirect('admin:shop_order_changelist') + + def prepare_order(self, request, order_id): + if request.method == 'POST': + order = Order.objects.get(id=order_id) + order.status = OrderStatus.PREPARED + order.save() + self.message_user(request, f"Order #{order_id} has been marked as prepared.") + return redirect('admin:shop_order_changelist') + + def cancel_and_refund_order(self, request, order_id): + if request.method == 'POST': + order = Order.objects.get(id=order_id) + try: + # Reuse the cancel_order logic from your views + from .views import cancel_order + cancel_order(request, order_id) + self.message_user(request, f"Order #{order_id} has been cancelled and refunded.") + except Exception as e: + self.message_user(request, f"Error cancelling order: {str(e)}", level='ERROR') + return redirect('admin:shop_order_changelist') + class GuestUserOrderInline(admin.TabularInline): model = Order extra = 0 diff --git a/shop/forms.py b/shop/forms.py index 9a9befc..76e5b53 100644 --- a/shop/forms.py +++ b/shop/forms.py @@ -1,5 +1,6 @@ from django import forms from .models import Coupon +from .models import ShippingAddress class GuestCheckoutForm(forms.Form): email = forms.EmailField(required=True) @@ -7,3 +8,15 @@ class GuestCheckoutForm(forms.Form): class CouponApplyForm(forms.Form): code = forms.CharField(max_length=50) + +class ShippingAddressForm(forms.ModelForm): + class Meta: + model = ShippingAddress + fields = ['street_address', 'apartment', 'city', 'postal_code', 'country'] + widgets = { + 'street_address': forms.TextInput(attrs={'placeholder': 'Adresse'}), + 'apartment': forms.TextInput(attrs={'placeholder': 'Appartement (optionnel)'}), + 'city': forms.TextInput(attrs={'placeholder': 'Ville'}), + 'postal_code': forms.TextInput(attrs={'placeholder': 'Code postal'}), + 'country': forms.TextInput(attrs={'placeholder': 'Pays'}), + } diff --git a/shop/migrations/0027_shippingaddress_alter_order_payment_status_and_more.py b/shop/migrations/0027_shippingaddress_alter_order_payment_status_and_more.py new file mode 100644 index 0000000..fba6820 --- /dev/null +++ b/shop/migrations/0027_shippingaddress_alter_order_payment_status_and_more.py @@ -0,0 +1,41 @@ +# Generated by Django 5.1 on 2025-05-06 10:21 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('shop', '0026_alter_order_user'), + ] + + operations = [ + migrations.CreateModel( + name='ShippingAddress', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('street_address', models.CharField(max_length=255)), + ('apartment', models.CharField(blank=True, max_length=50, null=True)), + ('city', models.CharField(max_length=100)), + ('state', models.CharField(blank=True, max_length=100, null=True)), + ('postal_code', models.CharField(max_length=20)), + ('country', models.CharField(max_length=100)), + ], + ), + migrations.AlterField( + model_name='order', + name='payment_status', + field=models.CharField(choices=[('UNPAID', 'Unpaid'), ('PAID', 'Paid'), ('FAILED', 'Failed'), ('REFUNDED', 'Refunded')], default='UNPAID', max_length=20), + ), + migrations.AlterField( + model_name='order', + name='status', + field=models.CharField(choices=[('PENDING', 'Pending'), ('PAID', 'Paid'), ('SHIPPED', 'Shipped'), ('DELIVERED', 'Delivered'), ('CANCELED', 'Canceled'), ('REFUNDED', 'Refunded'), ('PREPARED', 'Prepared')], default='PENDING', max_length=20), + ), + migrations.AddField( + model_name='order', + name='shipping_address', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='shop.shippingaddress'), + ), + ] diff --git a/shop/models.py b/shop/models.py index 55e8345..ae55bbe 100644 --- a/shop/models.py +++ b/shop/models.py @@ -8,6 +8,8 @@ class OrderStatus(models.TextChoices): SHIPPED = 'SHIPPED', 'Shipped' DELIVERED = 'DELIVERED', 'Delivered' CANCELED = 'CANCELED', 'Canceled' + REFUNDED = 'REFUNDED', 'Refunded' + PREPARED = 'PREPARED', 'Prepared' class CutChoices(models.IntegerChoices): UNISEX = 0, 'Unisex' @@ -71,6 +73,14 @@ class CartItem(models.Model): def get_total_price(self): return self.product.price * self.quantity +class ShippingAddress(models.Model): + street_address = models.CharField(max_length=255) + apartment = models.CharField(max_length=50, blank=True, null=True) + city = models.CharField(max_length=100) + state = models.CharField(max_length=100, blank=True, null=True) + postal_code = models.CharField(max_length=20) + country = models.CharField(max_length=100) + class GuestUser(models.Model): email = models.EmailField() phone = models.CharField(max_length=20) @@ -112,6 +122,7 @@ class Coupon(models.Model): class Order(models.Model): user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True) + shipping_address = models.ForeignKey(ShippingAddress, on_delete=models.SET_NULL, null=True, blank=True) date_ordered = models.DateTimeField(auto_now_add=True) status = models.CharField(max_length=20, choices=OrderStatus.choices, default=OrderStatus.PENDING) total_price = models.DecimalField(max_digits=10, decimal_places=2, default=0.00) @@ -122,6 +133,7 @@ class Order(models.Model): ('UNPAID', 'Unpaid'), ('PAID', 'Paid'), ('FAILED', 'Failed'), + ('REFUNDED', 'Refunded') ]) webhook_processed = models.BooleanField(default=False) stripe_mode = models.CharField(max_length=10, default='test', choices=[ @@ -137,6 +149,13 @@ class Order(models.Model): def get_total_after_discount(self): return max(self.total_price - self.discount_amount, 0) + def is_cancellable(self): + """Check if the order can be cancelled""" + return self.status in [OrderStatus.PENDING, OrderStatus.PAID] + + def shipping_address_can_be_edited(self): + return self.status in [OrderStatus.PENDING, OrderStatus.PAID, OrderStatus.PREPARED] + class OrderItem(models.Model): order = models.ForeignKey(Order, on_delete=models.CASCADE, related_name='items') product = models.ForeignKey(Product, on_delete=models.CASCADE) diff --git a/shop/static/shop/css/shop.css b/shop/static/shop/css/shop.css index 1384488..11e764f 100644 --- a/shop/static/shop/css/shop.css +++ b/shop/static/shop/css/shop.css @@ -546,3 +546,229 @@ v .cart-table { border: 3px solid #90ee90 !important; /* Use your light-green color */ transform: scale(1.1); /* Makes the selected color slightly larger */ } + +.status-badge { + padding: 4px 8px; + border-radius: 12px; + font-size: 12px; + color: white; + display: inline-block; +} +.status-badge.pending { + background-color: #f39200; +} +.status-badge.paid { + background-color: #27ae60; +} +.status-badge.shipped { + background-color: #3498db; +} +.status-badge.delivered { + background-color: #2c3e50; +} +.status-badge.canceled { + background-color: #e74c3c; +} +.status-badge.refunded { + background-color: #e74c3c; +} +.status-badge.prepared { + background-color: #27ae60; +} + +.original-price { + text-decoration: line-through; + color: #777; + font-size: 0.9em; + display: block; + font-weight: bold; +} + +.discounted-price { + font-weight: bold; +} + +.view-btn { + background-color: #3498db; + color: white; + border: none; + padding: 5px 10px; + border-radius: 12px; + cursor: pointer; + font-size: 12px; + font-weight: 600; + transition: background-color 0.3s ease; +} + +.view-btn:hover { + background-color: #2980b9; + color: white; +} + +.inline-form { + display: inline; +} + +.empty-orders { + text-align: center; + padding: 20px; +} + +.actions { + text-align: center; +} + +.order-meta { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 10px; + margin-bottom: 20px; + border-bottom: 1px solid #eee; + padding-bottom: 15px; +} + +.discount-section { + margin-top: 20px; + border-top: 1px dashed #eee; + padding-top: 15px; +} + +.discount-row { + display: flex; + justify-content: space-between; + margin-bottom: 5px; +} + +.total-row { + font-weight: bold; + margin-top: 5px; + padding-top: 5px; + border-top: 1px solid #eee; +} + +.coupon-info { + margin-top: 10px; + font-size: 0.9em; + color: #666; +} + +.order-actions { + margin-top: 20px; + text-align: right; + padding-top: 15px; + border-top: 1px solid #eee; +} + +.order-items-section { + margin-bottom: 20px; +} + +.order-items-section h3 { + margin-bottom: 10px; + padding-bottom: 5px; + border-bottom: 1px solid #eee; +} + +.order-table { + width: 100%; + border-collapse: separate; + border-spacing: 0; + border-radius: 12px; + overflow: hidden; + padding: 0px; +} + +.order-table tbody tr.odd-row { + background-color: #f0f0f0; +} + +.order-table tbody tr.even-row { + background-color: #e8e8e8; +} + +.shipping-address-section { + padding: 15px; + background: #f9f9f9; + border-radius: 5px; +} + +.address-details { + margin: 10px 0; +} + +.address-actions { + margin-top: 10px; +} + +.edit-address-btn, +.add-address-btn { + background-color: #007bff; + color: white; + border: none; + padding: 5px 10px; + border-radius: 4px; + cursor: pointer; +} + +.edit-address-btn:hover, +.add-address-btn:hover { + background-color: #0056b3; +} + +.shipping-address-form { + margin-top: 15px; + padding: 15px; + background: #fff; + border: 1px solid #ddd; + border-radius: 4px; +} + +.form-actions { + margin-top: 10px; + display: flex; + gap: 10px; +} + +.save-btn { + background-color: #27ae60; + color: white; + border: none; + padding: 5px 15px; + border-radius: 4px; + cursor: pointer; +} + +.cancel-btn { + background-color: #dc3545; + color: white; + border: none; + padding: 5px 15px; + border-radius: 4px; + cursor: pointer; +} + +.save-btn:hover { + background-color: #218838; +} + +.cancel-btn:hover { + background-color: #c82333; +} + +.address-input { + padding: 8px; + border: 1px solid #ddd; + border-radius: 12px; + font-size: 14px; +} + +.address-section { + margin: 20px 0; + padding: 15px; + border-radius: 5px; +} + +#address-message { + margin-top: 10px; + font-size: 14px; +} diff --git a/shop/stripe_utils.py b/shop/stripe_utils.py index 2fe4ede..15e7a87 100644 --- a/shop/stripe_utils.py +++ b/shop/stripe_utils.py @@ -76,5 +76,65 @@ class StripeService: """Retrieve a payment intent by ID""" return stripe.PaymentIntent.retrieve(payment_intent_id) + def create_refund(self, payment_intent_id, amount=None, reason=None): + """ + Create a refund for a payment intent + + Args: + payment_intent_id (str): The payment intent ID to refund + amount (int, optional): Amount to refund in cents. If None, refunds the entire amount. + reason (str, optional): The reason for the refund, one of 'duplicate', 'fraudulent', or 'requested_by_customer' + + Returns: + stripe.Refund: The created refund object + """ + try: + refund_params = { + 'payment_intent': payment_intent_id, + } + + if amount is not None: + refund_params['amount'] = amount + + if reason in ['duplicate', 'fraudulent', 'requested_by_customer']: + refund_params['reason'] = reason + + # Log the refund attempt + mode_str = "TEST" if self.is_test_mode else "LIVE" + logger.info(f"[{mode_str}] Creating refund for payment intent {payment_intent_id}") + + # Process the refund + refund = stripe.Refund.create(**refund_params) + + # Log success + logger.info(f"Refund created successfully: {refund.id}") + + return refund + + except stripe.error.StripeError as e: + # Log the error + logger.error(f"Stripe error creating refund: {str(e)}") + raise + except Exception as e: + # Log any other errors + logger.error(f"Unexpected error creating refund: {str(e)}") + raise + + def get_refund(self, refund_id): + """ + Retrieve a refund by ID + + Args: + refund_id (str): The ID of the refund to retrieve + + Returns: + stripe.Refund: The refund object + """ + try: + return stripe.Refund.retrieve(refund_id) + except stripe.error.StripeError as e: + logger.error(f"Stripe error retrieving refund {refund_id}: {str(e)}") + raise + # Create a singleton instance for import and use throughout the app stripe_service = StripeService() diff --git a/shop/templates/admin/shop/order/preparation_view.html b/shop/templates/admin/shop/order/preparation_view.html index 63213a5..fd6566a 100644 --- a/shop/templates/admin/shop/order/preparation_view.html +++ b/shop/templates/admin/shop/order/preparation_view.html @@ -5,8 +5,20 @@

Total orders with status PAID: {{ total_orders }}

Total items to prepare: {{ total_items }}

- - Back to Orders +
+ + Back to Orders + + +
+ {% csrf_token %} + +
+

Items Summary

@@ -35,7 +47,7 @@ @@ -54,13 +66,15 @@ + + {% for order in orders %} - + + + {% empty %} - + {% endfor %} diff --git a/shop/templates/shop/cart.html b/shop/templates/shop/cart.html index d9d2a3f..43f5401 100644 --- a/shop/templates/shop/cart.html +++ b/shop/templates/shop/cart.html @@ -5,17 +5,7 @@ {% block second_title %}La Boutique{% endblock %} {% block content %} - +{% include 'shop/partials/navigation_base.html' %} {% if STRIPE_IS_TEST_MODE %}
⚠️ Test Mode: Stripe is currently in test mode. No real payments will be processed. @@ -26,18 +16,18 @@
-

Votre panier

+

Votre panier

{% if display_data.items %} -
+

Comment fonctionne la livraison ?

Cette boutique fonctionne entre amis 'Padel Club'. Les commandes sont :

  1. Passées en ligne via notre système
  2. Préparées par notre équipe
  3. -
  4. Remises en main propre lors d'une prochaine session de padel
  5. +
  6. Remises en main propre lors d'une prochaine session de padel ou livrées à l'adresse indiquée dans la mesure du possible
-

Pas d'expédition : nous vous remettrons votre commande personnellement au club !

+

Livraison : En général, nous vous remettrons votre commande personnellement au club. Les livraisons peuvent être possible en fonction du lieu, n'hésitez donc pas à indiquer une adresse de livraison.

{% include 'shop/partials/order_items_display.html' with items=display_data.items total_quantity=display_data.total_quantity total_price=display_data.total_price edit_mode=True %} @@ -67,6 +57,24 @@
+
+
Adresse de livraison (dans la mesure du possible)
+ +
+ +
+ +
+
+ + + +
+
+
+ +
+
{% if user.is_authenticated %} @@ -123,6 +131,17 @@ const subtotalAmount = document.getElementById('subtotal-amount'); const discountAmount = document.getElementById('discount-amount'); const finalTotal = document.getElementById('final-total'); + const shippingForm = document.getElementById('shipping-form'); + + // Function to collect shipping address data + function getShippingData() { + const formData = new FormData(shippingForm); + const shippingData = {}; + formData.forEach((value, key) => { + shippingData[key] = value; + }); + return shippingData; + } // Initial values const originalTotal = parseFloat('{{ display_data.total_price }}'); @@ -244,6 +263,9 @@ checkoutButton.textContent = 'Chargement...'; checkoutButton.disabled = true; + // Get shipping data + const shippingData = getShippingData(); + // Create order and get checkout session fetch('{% url "shop:create_checkout_session" %}', { method: 'POST', @@ -251,6 +273,9 @@ 'Content-Type': 'application/json', 'X-CSRFToken': '{{ csrf_token }}' }, + body: JSON.stringify({ + shipping_address: shippingData + }), credentials: 'same-origin', }) .then(function(response) { diff --git a/shop/templates/shop/checkout.html b/shop/templates/shop/checkout.html index 15098ed..ee57c96 100644 --- a/shop/templates/shop/checkout.html +++ b/shop/templates/shop/checkout.html @@ -6,18 +6,9 @@ {% block content %} - -

Validation de la commande

+{% include 'shop/partials/navigation_base.html' %} + +

Validation de la commande

diff --git a/shop/templates/shop/my_orders.html b/shop/templates/shop/my_orders.html new file mode 100644 index 0000000..304fa71 --- /dev/null +++ b/shop/templates/shop/my_orders.html @@ -0,0 +1,81 @@ +{% extends 'tournaments/base.html' %} + +{% block head_title %}Mes Commandes{% endblock %} +{% block first_title %}Padel Club{% endblock %} +{% block second_title %}Mes Commandes{% endblock %} + +{% block content %} +{% include 'shop/partials/navigation_base.html' %} + +
+
+

Mes Commandes

+
+ {% if orders %} +
{{ item.quantity }} {% for order_id in item.orders %} - Order #{{ order_id }}{% if not forloop.last %}, {% endif %} + Order #{{ order_id }}{% if not forloop.last %}, {% endif %} {% endfor %}
Order # Date CustomerShipping Address ItemsActions
Order #{{ order.id }}Order #{{ order.id }} {{ order.date_ordered|date:"Y-m-d H:i" }} {% if order.user %} @@ -71,6 +85,16 @@ Unknown {% endif %} + {% if order.shipping_address %} + {{ order.shipping_address.street_address }} + {% if order.shipping_address.apartment %}, {{ order.shipping_address.apartment }}{% endif %}
+ {{ order.shipping_address.postal_code }} {{ order.shipping_address.city }}
+ {{ order.shipping_address.state }}, {{ order.shipping_address.country }} + {% else %} + No shipping address + {% endif %} +
{% for item in order.items.all %} {{ item.quantity }}x {{ item.product.title }} @@ -79,10 +103,28 @@
{% endfor %}
+
+ {% csrf_token %} + +
+ +
+ {% csrf_token %} + +
+
No orders foundNo orders found
+ + {% for order in orders %} + + + + + + + + + {% endfor %} + +
Commande #{{ order.id }} + Détails + {{ order.date_ordered|date:"d/m/Y H:i" }} + {% if order.status == 'PENDING' %} + En attente + {% elif order.status == 'PAID' %} + + {% elif order.status == 'PREPARED' %} + En cours de préparation + {% elif order.status == 'SHIPPED' %} + Expédiée + {% elif order.status == 'DELIVERED' %} + Livrée + {% elif order.status == 'CANCELED' %} + Annulée + {% elif order.status == 'REFUNDED' %} + Remboursée + {% endif %} + + {% if order.discount_amount > 0 %} + {{ order.total_price }}€ + {{ order.get_total_after_discount }}€ + {% else %} + {{ order.total_price }}€ + {% endif %} + + {% if order.status == 'PENDING' or order.status == 'PAID' %} +
+ {% csrf_token %} + +
+ {% endif %} +
+ {% else %} +
+

Vous n'avez pas encore de commandes.

+ Découvrir la boutique +
+ {% endif %} +
+ + {% if messages %} +
+ {% for message in messages %} +
+ {{ message }} +
+ {% endfor %} +
+ {% endif %} +
+ + +{% endblock %} diff --git a/shop/templates/shop/order_detail.html b/shop/templates/shop/order_detail.html new file mode 100644 index 0000000..25f5f4b --- /dev/null +++ b/shop/templates/shop/order_detail.html @@ -0,0 +1,150 @@ +{% extends 'tournaments/base.html' %} + +{% block head_title %}Détail de commande{% endblock %} +{% block first_title %}Padel Club{% endblock %} +{% block second_title %}Détail de commande{% endblock %} + +{% block content %} +{% include 'shop/partials/navigation_base.html' %} + +
+
+

Commande #{{ order.id }}

+
+
+
+ Date: {{ order.date_ordered|date:"d/m/Y H:i" }} +
+
+ Statut: + {% if order.status == 'PENDING' %} + En attente + {% elif order.status == 'PREPARED' %} + En cours de préparation + {% elif order.status == 'PAID' %} + + {% elif order.status == 'SHIPPED' %} + Expédiée + {% elif order.status == 'DELIVERED' %} + Livrée + {% elif order.status == 'CANCELED' %} + Annulée + {% elif order.status == 'REFUNDED' %} + Remboursée + {% endif %} +
+
+ +
+

Produits

+ {% with items=order_items total_quantity=total_quantity total_price=order.total_price %} + {% include 'shop/partials/order_items_display.html' with items=items total_quantity=total_quantity total_price=total_price edit_mode=False cancel_mode=order.is_cancellable %} + {% endwith %} +
+ +
+
Adresse de livraison (dans la mesure du possible)
+ {% if order.shipping_address %} +
+

{{ order.shipping_address.street_address }}

+ {% if order.shipping_address.apartment %} +

{{ order.shipping_address.apartment }}

+ {% endif %} +

{{ order.shipping_address.postal_code }} {{ order.shipping_address.city }}, {{ order.shipping_address.country }}

+
+ {% if order.shipping_address_can_be_edited %} + + + + {% endif %} + {% else %} +

Aucune adresse de livraison renseignée

+ {% if order.shipping_address_can_be_edited %} + + + {% endif %} + {% endif %} +
+ + {% if order.discount_amount > 0 %} +
+
+ Sous-total: + {{ order.total_price }}€ +
+
+ Réduction: + -{{ order.discount_amount }}€ +
+
+ Total final: + {{ order.get_total_after_discount }}€ +
+ + {% if order.coupon %} +
+ Coupon appliqué: {{ order.coupon.code }} +
+ {% endif %} +
+ {% endif %} + +
+ {% if order.status == 'PENDING' or order.status == 'PAID' %} +
+ {% csrf_token %} + +
+ {% endif %} +
+
+
+ + {% if messages %} +
+ {% for message in messages %} +
+ {{ message }} +
+ {% endfor %} +
+ {% endif %} +
+ + + +{% endblock %} diff --git a/shop/templates/shop/partials/navigation_base.html b/shop/templates/shop/partials/navigation_base.html new file mode 100644 index 0000000..3c49543 --- /dev/null +++ b/shop/templates/shop/partials/navigation_base.html @@ -0,0 +1,9 @@ + diff --git a/shop/templates/shop/partials/order_items_display.html b/shop/templates/shop/partials/order_items_display.html index fabb5f9..125a3a3 100644 --- a/shop/templates/shop/partials/order_items_display.html +++ b/shop/templates/shop/partials/order_items_display.html @@ -1,60 +1,68 @@ - - {% for item in items %} - - - {% if item.product_description %} - - {% endif %} - - - - {% if edit_mode %} - - {% endif %} - - {% endfor %} - - - - - - - - {% if edit_mode %} - - {% endif %} - - + + {% for item in items %} + + + {% if item.product_description %} + + {% endif %} + + + + {% if edit_mode %} + + {% elif cancel_mode and items.count > 1 %} + + {% endif %} + + {% endfor %} + + + + + + + + {% if edit_mode or cancel_mode %} + + {% endif %} + +
{{ item.product_title }}{{ item.product_description }} -
-
- {{ item.color_name }} | {{ item.size_name }} -
-
- {% if edit_mode %} -
-
- {% csrf_token %} - - - {{ item.quantity }} - -
-
- {% else %} - x {{ item.quantity }} - {% endif %} -
{{ item.total_price }} € -
- {% csrf_token %} - - -
-
{{ total_quantity }} produit(s){{ total_price }} €
{{ item.product_title }}{{ item.product_description }} +
+
+ {{ item.color_name }} | {{ item.size_name }} +
+
+ {% if edit_mode %} +
+
+ {% csrf_token %} + + + {{ item.quantity }} + +
+
+ {% else %} + x {{ item.quantity }} + {% endif %} +
{{ item.total_price }} € +
+ {% csrf_token %} + + +
+
+
+ {% csrf_token %} + +
+
{{ total_quantity }} produit(s){{ total_price }} €
diff --git a/shop/templates/shop/payment.html b/shop/templates/shop/payment.html index 4517651..c16d356 100644 --- a/shop/templates/shop/payment.html +++ b/shop/templates/shop/payment.html @@ -12,21 +12,12 @@ Use test card: 4242 4242 4242 4242 with any future date and any CVC. {% endif %} - +{% include 'shop/partials/navigation_base.html' %} +
-

Résumé de votre commande

+

Résumé de votre commande

{% include 'shop/partials/order_items_display.html' with items=display_data.items total_quantity=display_data.total_quantity total_price=display_data.total_price edit_mode=False %} diff --git a/shop/templates/shop/payment_cancel.html b/shop/templates/shop/payment_cancel.html index 996d4e6..e4fc22c 100644 --- a/shop/templates/shop/payment_cancel.html +++ b/shop/templates/shop/payment_cancel.html @@ -7,21 +7,11 @@ {% block content %} - +{% include 'shop/partials/navigation_base.html' %}
-

Paiement

+

Paiement

Le paiement a été annulé

Votre commande n'a pas été finalisée car le paiement a été annulé.

diff --git a/shop/templates/shop/payment_success.html b/shop/templates/shop/payment_success.html index e8880a5..eeb681e 100644 --- a/shop/templates/shop/payment_success.html +++ b/shop/templates/shop/payment_success.html @@ -5,21 +5,11 @@ {% block second_title %}La Boutique{% endblock %} {% block content %} - +{% include 'shop/partials/navigation_base.html' %}
-

Paiement réussi

+

Paiement réussi

Merci pour votre commande !

Votre paiement a été traité avec succès.

diff --git a/shop/templates/shop/product_list.html b/shop/templates/shop/product_list.html index aaacbc4..16cc311 100644 --- a/shop/templates/shop/product_list.html +++ b/shop/templates/shop/product_list.html @@ -6,22 +6,12 @@ {% block content %} - +{% include 'shop/partials/navigation_base.html' %}

Bienvenue sur la boutique Padel Club des copains !

Photos : Les photos des vêtements n'ont pas encore tous le logo, la description indique où il situera. -

Livraison : Commandez en ligne et récupérez votre commande en main propre lors de votre prochaine session de padel au club !

+

Livraison : Commandez en ligne et récupérez votre commande en main propre lors de votre prochaine session de padel au club. La livraison peut être possible !

{% endif %} -
-
- -
- {% csrf_token %} - {{ form.as_p }} - -
+
+
+
+ +
+ {% csrf_token %} + {{ form.as_p }} + +
+
-
-
-
- -
- {% csrf_token %} - {{ password_change_form.as_p }} - -
+
+
+ +
+ {% csrf_token %} + {{ password_change_form.as_p }} + +
+
{% endblock %} diff --git a/tournaments/templates/tournaments/navigation_base.html b/tournaments/templates/tournaments/navigation_base.html index 3624765..b9e4be8 100644 --- a/tournaments/templates/tournaments/navigation_base.html +++ b/tournaments/templates/tournaments/navigation_base.html @@ -9,4 +9,5 @@ Se connecter {% endif %} Ajouter vos tournois + La Boutique From 6800c1643dc4b0f3c1c8b3568fd56f0e4be3e311 Mon Sep 17 00:00:00 2001 From: Raz Date: Tue, 6 May 2025 13:09:05 +0200 Subject: [PATCH 007/110] add new shop items --- .../commands/create_initial_shop_data.py | 25 ++++++++++++++++++ .../PC008/blanc/PS_PA439_WHITE.png.avif | Bin 0 -> 11834 bytes .../bleu-sport/PS_PA439_SPORTYNAVY.png.avif | Bin 0 -> 10302 bytes .../kaki-fonce/PS_PA439_DARKKHAKI.png.avif | Bin 0 -> 11356 bytes .../PC008/noir/PS_PA439_BLACK.png.avif | Bin 0 -> 8193 bytes .../rose-clair/PS_PA439_PALEPINK.png.avif | Bin 0 -> 13279 bytes .../PC008/sand/PS_PA439_SAND.png.avif | Bin 0 -> 17038 bytes .../PC009/blanc/PS_PA438_WHITE.png.avif | Bin 0 -> 11295 bytes .../bleu-sport/PS_PA438_SPORTYNAVY.png.avif | Bin 0 -> 11392 bytes .../PC009/noir/PS_PA438_BLACK.png.avif | Bin 0 -> 7960 bytes .../PC009/olive/PS_PA438_OLIVE.png.avif | Bin 0 -> 11054 bytes .../rose-clair/PS_PA438_PALEPINK.png.avif | Bin 0 -> 13126 bytes .../PC009/sand/PS_PA438_SAND.png.avif | Bin 0 -> 12158 bytes .../shop/partials/navigation_base.html | 9 ++++--- 14 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 shop/static/shop/images/products/PC008/blanc/PS_PA439_WHITE.png.avif create mode 100644 shop/static/shop/images/products/PC008/bleu-sport/PS_PA439_SPORTYNAVY.png.avif create mode 100644 shop/static/shop/images/products/PC008/kaki-fonce/PS_PA439_DARKKHAKI.png.avif create mode 100644 shop/static/shop/images/products/PC008/noir/PS_PA439_BLACK.png.avif create mode 100644 shop/static/shop/images/products/PC008/rose-clair/PS_PA439_PALEPINK.png.avif create mode 100644 shop/static/shop/images/products/PC008/sand/PS_PA439_SAND.png.avif create mode 100644 shop/static/shop/images/products/PC009/blanc/PS_PA438_WHITE.png.avif create mode 100644 shop/static/shop/images/products/PC009/bleu-sport/PS_PA438_SPORTYNAVY.png.avif create mode 100644 shop/static/shop/images/products/PC009/noir/PS_PA438_BLACK.png.avif create mode 100644 shop/static/shop/images/products/PC009/olive/PS_PA438_OLIVE.png.avif create mode 100644 shop/static/shop/images/products/PC009/rose-clair/PS_PA438_PALEPINK.png.avif create mode 100644 shop/static/shop/images/products/PC009/sand/PS_PA438_SAND.png.avif diff --git a/shop/management/commands/create_initial_shop_data.py b/shop/management/commands/create_initial_shop_data.py index b81c332..0872ba0 100644 --- a/shop/management/commands/create_initial_shop_data.py +++ b/shop/management/commands/create_initial_shop_data.py @@ -18,10 +18,13 @@ class Command(BaseCommand): {'name': 'Fuchsia', 'hex': '#C1366B', 'secondary_hex': None, 'ordering': 30}, {'name': 'Corail / Noir', 'hex': '#FF7F50', 'secondary_hex': '#000000', 'ordering': 40}, {'name': 'Gris Foncé Chiné / Noir', 'hex': '#4D4D4D', 'secondary_hex': '#000000', 'ordering': 50}, + {'name': 'Olive', 'hex': '#635E53', 'secondary_hex': None, 'ordering': 54}, {'name': 'Kaki Foncé', 'hex': '#707163', 'secondary_hex': None, 'ordering': 55}, {'name': 'Noir', 'hex': '#000000', 'secondary_hex': None, 'ordering': 60}, {'name': 'Noir / Corail', 'hex': '#000000', 'secondary_hex': '#FF7F50', 'ordering': 61}, {'name': 'Noir / Gris Foncé Chiné', 'hex': '#000000', 'secondary_hex': '#4D4D4D', 'ordering': 62}, + {'name': 'Rose Clair', 'hex': '#E7C8CF', 'secondary_hex': None, 'ordering': 31}, + {'name': 'Sand', 'hex': '#B4A885', 'secondary_hex': None, 'ordering': 32}, ] color_objects = {} @@ -137,6 +140,28 @@ class Command(BaseCommand): 'sizes': ['S', 'M', 'L', 'XL', 'XXL', '3XL'], 'image_filename': 'PS_PA1030_WHITE-SPORTYNAVY.png.avif' }, + { + 'sku': 'PC008', + 'title': 'T-shirt Simple Femme', + 'description': 'T-shirt simple avec logo coeur.', + 'price': 20.00, + 'ordering_value': 60, + 'cut': 1, # Women + 'colors': ['Blanc', 'Bleu Sport', 'Sand', 'Noir', 'Kaki Foncé', 'Rose Clair'], + 'sizes': ['XS','S', 'M', 'L', 'XL', 'XXL'], + 'image_filename': 'PS_PA439_WHITE.png.avif' + }, + { + 'sku': 'PC009', + 'title': 'T-shirt Simple Homme', + 'description': 'T-shirt simple avec logo coeur.', + 'price': 20.00, + 'ordering_value': 61, + 'cut': 2, # Men + 'colors': ['Blanc', 'Bleu Sport', 'Sand', 'Noir', 'Olive', 'Rose Clair'], + 'sizes': ['XS','S', 'M', 'L', 'XL', 'XXL', '3XL'], + 'image_filename': 'PS_PA438_WHITE.png.avif' + }, ] for product_data in products: diff --git a/shop/static/shop/images/products/PC008/blanc/PS_PA439_WHITE.png.avif b/shop/static/shop/images/products/PC008/blanc/PS_PA439_WHITE.png.avif new file mode 100644 index 0000000000000000000000000000000000000000..73e32020132513108231a62d2d90c49eca13ad61 GIT binary patch literal 11834 zcmZvCV{j+Y@^)<7w!N`!+y2E)Hr&{@ZQHhOCmU^SC;#1h>(=|_?Wt)zeNH_+{o&N~ z)Bphi;hDSqbTD!Qm;?Qlf7TXY&TI=X`WpkM*_yc+{lotXv8Abv(|=tcAP0bn%m4iU zSp)}wi|u~`;J=y;U}JCcFA^050{MIW72qzQf0@4m=l5R^f#-h+;rCyd69BOLPZ|GN zkN!6>`|rkog)uSz+xh>%znTAms{jWlhrh{sfP;zsUyEvDZ{uWVV&m~2W{6*ZAvhy9 zW)XidyuU6G8o=4X?4JV!1d{aM8|ZKG9)N!cFw|egY2@l5@-K3Avg5XOFthlN66Q7m zm^g4d|8#aSvvubFy9QtbFmn3IZDiwM`JXoZmxb&h zEVo#HBPzisogrmrW;ST}iPsMNgk}o`7WRnf*oXU(=&O2QwYoTkm788VQVut8c>%p<#bAD z<7^V}e0o905S|0QHI6G&1gQ`EbvY?0Hl({tK0m4T)k`qz<^>{_W2w4W$^7rYs#*kz zJArzF$>WK|0_f73-(u%AN!*W9a%%an6PqDjA&kMG{FkbgS2eY-~g2tAV{gp{2QvElS<(d&|~ zRXp~4G3DYt{($S8;z$)4VEpxjHmYf`QHBg&S$k`0*RrQLU7L|_`WO#?C1Y`GorN=z zjB=9h+0y7WUuU3&uC8pGUij%2yQ{was3SNxAWnd%Q`s+rP6VSFL%9H{letKyCaYL) zMV&`qhLe`Y^qdLa405QtEjz`8rGArXq;yLy@b9;?>S}C zN`X93E*lV9s>#&fW%#xl2hnxp7#3|DbfLb!?5eDus2UB9x1p#EtLn36d>U?%?4;nP zw&D9Lj3;uw7&BmH%TreQ~uysoXnHuzJm}n|DV6hvBG~ET2Ma&w`nympAmk^Azt&SoriM82-aMG#lS-jMf4ITTv|;a;k; z0*xWcn9v;iN!tE`q%li|pCY%p*A^NR_Q|6ymz(o><0K`4?z<8nD5{zpxiW8b z!v)C(;3dfJXD`fFO7tU~2#Qe?cqs6_aJyGWOXBqh3h5`WF(+v}MWfo`wEPqzG8Z7F ziPlu_s}4T*U1GQ5wC?l!P)xGqHnseD5a38t8_luMo*QUQGg56(jCJtVmZ zYU6N?DA|W`N|Z&+4jf((?UyPLbLbz}RNnMqZk`U&2s7o^waZP##0wxHjHK%CR0YEl z)pgrZr(AvXuD|QySUaA4oBkD}NpR*BnLD4^stOdlW@a=hWxf?o%x|zRUM$ImS$^g3 z6Tzx1KF3$e{ZpFUongy!nG!~tX1DW64>tChi_4m8)c<}glVrMRU4!2zNXeEY>eF|u!I_JU zw8Px1CtO_~xcr}RUFS@>20ca)utKSIPkU~d+^4UJwj7>kG|;dME>uCd>J+y8 zqW$GY9Bf6HNHe@f!u&E?j#>vnRFN_jQXBn?wc3>!pY{Dv@7p7t`*VbOLW0kq_Zs9(mAfLW)rqI zF6yJT6S7-xVGa?_0}2Ts%kgA`N&Owx_wuita0@L3N=mz+w!i#N%-DL9nn z>RhDnH-0)oU%Wm(*@pPiY2qI6Uv;$cQvN+0kPjh_Mf)s}-e>pBZ=(ayLHBKGlnft2P@4e`TX z6EWTV!TuGi)h2v+Q1ihtAq$ZS&yJlbis>t0k$38Bd4KEzXsP$R+=s^_sIyf2R2DT{ zfM%35VWzvK;P~TUs6ZKIQ}<=%HFV1~K1NAwZfnJVa@dt5L;m6cP#jms+JMJArhYz( z_#iHvsp`Hoy@i(Lxm)hYTVn8%w5(B`M+nTfSX@ZN2|7CL`$#eZlU%j^6olT=Lx-)c zJ4)$Z$MFgy^(eso*uL50%6&F*@!P1A`#hYsdby!)l_2gjkjF3+sJVw9oB&s3Qyy~V z)RS>zC)dZ>CQ~to^)pW6M>!r}V0#}x{M7zTsC4HuCG9n}a9a%Z`}K*^H+JLQtS&g= z){~3G3T3Wo>MAFTR{;9_JH(=>CVmAAb#aI+i=-h8ZO4->frm%MUg}z0RSkWYeFveX zDReK*0$`Pj4fvKe9QBx%$dqcA6=qD3u`Pf5bHzXJD<0S8XlTOiJgI3z*mY1dc~lVW zA;)&9*oWP8G^XgBH-$YfcM|)_jlq@YnVG)Uie9Up1Cbmg01;ZUDg5Si5tchZ`x!FD zcj?H+S!yao17eWnTEg?*zAl6g$$9MeiA|MZpu1)DBq3-+*{=Z6v8g#%aWkMjH9{DD zs2YQEE6{jN)1aarB(W2XP3Y^@M~U))t{H*(@PIEkaa_+y1j7TR>dDH#W^z3-Vt*jP zj@;q3YIH|)34R-lN&XQoG7n8V_o7HmGUYi&XyoVnkkgE$Kz+$Hdgk74fY-{REB2MLj^gYEQ2Qkg|7cxVW9g69Vf@i2 zCWFQ)3E%Ego0rSU@2?@~$PC?;RbUa=0xCUSB}Jj9CJ!c>jMdM{@fW59o!+sISi+|{ z&OYR@G7SDA)6qtLPH?Q-uN{9irs%Csw=Y-g^uv%Jw4P)1^8m@72an)&8C5!S!^Eww zqe%>VI)zVP|JsI6jcS6%{(>z*0YN8;3Oi5EjR~gG=hF{;ZWBHo)!%xMO+xRY?7dt` z8WLamcpn4sP^$f2THDX7AHwFsRU|9Y-5!N;JX7X?fQue9)yDj);UV&reuTpfJmhHw z4olvBj2#41j>MQwa*&90xN(ST`9IG}Y*(6J*k+Z&m^WgJr%~-uf|#zsua!KBe4W$# zwCa5A7Ehe3tbam1e^WDu*EM-#^N{c|~ld=gwue%FZu zFX^V$)lEErqA!ZVk+<0XOU4}aio_;38+`Zsmnsbauiw^gzS?}1 z=?x;mp%M~-zIx0}5Slur@tp3w$O}LVVMbF_kPhg`OoPm@XJ_|Wzkf)|h^vQwDuA_{ zM-8?(ncT0di9%Hvp=`RI&1&UcgFThXLa9aSZx5ouun~PU>DXtm4_vhP6V2>P$u;J~<*(M&Cev&i5P`Wd2ZdK(l>~{=nm< zGA1g0`2|r?v+Hetm=fb%--GilmutbnC~=5$XILKcXaY3fzv_nVBvPHSx5HRRD_b@h zpu$85)XnTf2GmW#&=@q+5&y_E&2E}Zjf~lMM-kE9jDW{+6>}!bzkTy}l>AlE#)+Id zHs+)`{ZvgfBWl%|C)#GLDbbZ{^opfI7l^6rvh@aq*9xN=2->i?x9fDUN-_W^EVM4J zo@B9^7t+8MkG>yg5zs1Us>9e8qK{|SH-C9GLtoV3kKCzr&{%u-2Mfj(?HTHz3{6St zyGaOh^J(n$jX?P)dK+EA2}}0N#6IXTMc-3(PNFUMK81+Dbp_NAiof7$C3vfYA2F(Z zwpVw`Z73{8jtw}Ow?ckMox;06-9tcXi()d`#w&STm#p=%mcybNgbb7~I|D$mdV^ZY z#0i6t2^{(u3#}x*TWiE;lTi!}1^66=arNR#bfKzX;GC>*+>1hmanZXA zU@J)gesef}*kDY(Zb%$uTqz&9_h2@ExD|RqXoA`o$no7y{UD@*YxLvT7{9$Q97!Li zxBM+q{}I$S-?MbFiUuSEa3-R?8B;UjN8;m7M9um9N9`ha^^eXqEDwobf6l{@ei|pW zi11{H$shcgDNaJCKs3FastHnL)8HFUltuk5w4~`|{^gcX3~~<|d1lhi+7FvtHnt_{ z&<~x;{oEWCAD-!07IZJxEQ?k6;1yvO{X^EoThq!Ui#1Wm{r&c;*1ymE0eB|yYCcdC5Q5O}iu4L#gC ztn39*;-sR0B~~&$S+{~6J*$tL#Sr8JDCqv^R6@+uM~KG)Wzpe!P^lR zc7P@wHKv1X-ZW=Zvpd$Ws+0C&dyk8|&8j9{zS*tm{0c3%^GU`UUFc#T=iqp0t)M=F zrfE9RErQEgem6mjb2hmT@4Ga61vPCo&_ZDY#|i%24y#6qc7=9NWd{9#vmu(d@=RMg z8_Q8*K(x4guj%%$#dM6Mgz5rtfi9hF7;8OZ&$*DB-}mj2_Xn)VCr@arow=0q3L4LC zRc{jlh9)k%TE7=bMHhpN%oW9Gc7`1X3Dds!O0 zVu_84L|(fCr%al!gi})yy3uM*{aFON|3Y!oF1xQe!ox}VVI$Xs0?KB07Ab767f13z z{cBUhdF?#@xgDIaTGN1MOx2JM-j&-;vfd2-gZ@F_O6 z`4VM*l=H}fYW$CmtmO%Hn17F3cUjx*H;@m#^6Aabj32-I6C-#1zn-LO!y^p)4s2{= zy!dg$U@8r~G`oKeT`ugxA&aFE^==S*w! zv~gLmSUE$rGBTX1P=PcL7~&3b6YUH?_r6^Z>KCK@o zbq3m)k?xGVGrj!Mfbt_no}o@)BYN#{F&NGMQYUz(;W`z-O4lwk)q{A1UZ+5P!>C{# zi4mb;4JAk}tgoJsD3C|dgrtWm^v}Kw@~h-6=vcrNTrS?F2|V#{gFt=KplGr^;)<~? zq54zG%IS>SGn@4@W@CfFOB(>P3AfM9H?s2{-~$(nK5G~U9ZR$D3Bo2`T^$T1Q^NRO zi+C(utnKcWyHad8Qz>*!r7tvu)oirpM+Cy5%d+*uQ`{s=lI&86*z|eer%^#leq-WG zfJJ6={`R>}gF+YnDfRD?t1|=x(<#Q z=mH3BhH2SZBzz9ig-HCPIF|EJ;A6}tPinMRvZ^RXDHy{v~SMfkL0b^moZ%m;7NBOY0shHEOJ z4oL;>cvX!TKWmA?&wea_Jg9h>F# z{S1nYQEpR&871FuIUI)8x}8oIPc@~AKbXeX{*jao{~;QqOykC^vBN9pd|nZ6&KQua zrd~%Z$~3g;{VRXV=Wyt~hZDh}-EW56_QQ1DCDHh)JVgEC&SY*HeyQ4Bi)^;u7DZ5Y zkwIn!66o2n{#9x|jx$=FzkI|hPmHhtA~CvNKu7!(7v@3K|8nyjR2OR6fT|xQ7Q8#$ zzvSpHyT;770_j4^aBiYVTHf{nHurkTyg5XXd^|037Rq!AjHgL1-~11W#9L9yIEzd+ zYiUSgDhg*w>itCB93$98u;7WL#x19TgJ2C!HF+%#zTAQr5Dy1v0o>c>SHiL3DXWxe zV67(8Pw=;^q00|ZaMjFGr*@365->Vqs7SQRryrVxIws0OB`P%yG6I@aEvWA+{?$=u zoW5}<5q`vJ05LLB_Cn=7zBn2t$5s9%wZYvLV%ZYyWG+7k9C3&Qe>gCA;;K~pFMjV* zMj-)|;)FKVh?4Bt&RzZn9tI(gA4jeZbhZjI!XL(x!cf-vUU;kv)hiN(5F51Mkheiq zO2~Wm62^shsF7xH#JZtCy_Q~RZsTW{``q-X)QyA<4MN-8nMAK!^~JoGajXy>@0IJh zakA&fY54*Vf|aklgT)W|Qom82?g zGrVtJ@j6Df!CQ@e!Br$WC@(3=#`Kphf%m@Rocse*K0s((q46ojmzX>kn2NS z3q3OIAe0!U_G3qEQ@_M_+Is@UP@(!^Wb)J==y|i7E0~kWn@osxc@8K3_OGOKXgq#J z8!!rEF|7=^+GA)ikE#r>K>?K*p&g0~IPZeW+qabiIhM6yfm$-%D;+RbzwMBBcXBG` z_uT?&CS_S{vUSlT8$+1gB_IM_1~Uok#$4&~wPsQd>c+hHod{H+0~(vorr5kGobj{) z^qcEqt#pLv!N?7sFf{YaB+}J&jrn9A?rd(&vTj+ms2cg*uA;h?D$4>Vs-IMS*TJ%) z*zpl&4D4KALw1cfXwVyA=kH0&NEBNg9%E~Z+}p1OuH6QF=Z>+Fk4rlDbZgw2`BEzk zsgEoVY3Dc~Vy{}2i2FJ{^x--uYOPdjqOM!HQk&4bPZ-J=nfH>5V8fN=k|=bm+!39; zVn%`clBSh0uU;<T1?8_X zT)lIzYggF`|9ukwe)l_Vq_Kr|qZ~2R4DShoK^e5L>#gU*bmWP@xn+D04cUDb^gF?G@85V%7X?H4{ zkG1$b{(Ovb8k3zu)~16;*kbo>?_j@RBnBiM5Z8n5yQ&ww1vO9G&nnbd=qTl%4xI2$ z<>UU`Cfqh>n?Z%DlKY$%?s|AkS4QT0q;?SK9hFDCU(ZSx_1IuvGLQ__u8>%xfLhl= z#~jYT8)-FX4MM2a`a%0tzHOFol%!0FQHGRX8o>dc6)0{8x*&uYk1>&^qaM*K0yQ5c z2YFb17fU07Z(aPmxkvEzX(G;VB5r2UUP>^{Bh{~~DtFScXDTYcHMW*Uymm$1D&7G! zagRRr2|k@-Q*=zo_BWgq>zSoSq_&7QrY0%Qw;$G*N)BDyl0PQTwbmDDj2$7n+BL(H z$P)9b`>vmnW0U>(Y0=p38hMEApCXJqx*@uSSVI-yIM{Dw zZ{(s^K8(tQLADLb&PmvkzLca@Zw>3~Z~&N6?=*hwQk^*W&o@ud9u&zCY8G=wFspG8 zkAk!N3sH@Aj$oS3?q8BC?u71naCJ>+HfXg;$m{6eW~@JzID!ob!hNB9c519r&D1KV zH{3`#PC{IM2u(&42H7t`S8hU}u7qd~7_9L*P=flJ@SqBs*%4gvM3kF5j4D2N3`n1W z6vGgP;Upo56n=tOnt$FX=Q_xY#7fQv9q9)|1}N2O@lJ7o%p`%h`{qiSO5+ycVL4_X z6|tyIH%?Q&+7NYi3C8ZYK;IQVtu$s0ExujnJoQndXx}PyYflRkCkgxZM*gw^@#S zhZYD?BlOk5Hy6aap-=RNW^HNu zj0DS_+p`>%ekxPLE+(khr3(yzUhSmVi)Wfvr7uZ7ugHgDAT;e`t*6q%ZoD7@E}Qs# z2hlMpypxubdbSvG8y&S6_^kL##$AaCcE=z{7n7IC_>+96yCcEv4ZnrTP@FXa(rMLQ zv7yz*h<#t-LNRnlxJBX#v)wGFJ=@=xv;xV5kZ)T3BU(zMykBQ4jQ*moC={a1LieUh z;PdI4OijyH_FGn=4P@{O7ox*g4-^iL!968}Q_jFCeg>CoNynW?6fnxi!3!GnlmP3_TE_>mPc~4mU_n z0g}v)!Ho6XTS}X|O{z{4&CK02iG=lb3w~NSBP@8~8GkM~@KGeg$4uVw!{Y+$Mt-mv z9>9U1hBk1Bty9x3Ki?|Am}&}Dn|DK9qSYX*<%9&y2hCyeL@=|*it#9$I(f{_R3>U~ zhO#v%m6#FxWk2le;%X(ZhL>?jVVL%K=LSq$EQjUR{Zrc4B*#8Qm#`>4BI5%8RaIyC zX8`gR8jgXL*efKwJ6l;1xvbC`#tM3Q!!bS{c9!2Ctlp*vYVkPf)qTdsIH3HnMx2$% zJ7he5EBsa)zDmRABY1ggzuNb5jz~0SH<>x;65<|36kb;{a;g{za~jFG<_>K^9Jf51 zU5?__nBtdNk=vpSbNLty{BeS?>^y4i)0^KA>)<275qrAX&d0ebq>{q5kwRZ>5N8%& zd2vsgDh*%-BAUy0m}ZA#(ggI0Wu8-6c17?mt;CA;wFp z8Ni=Gq-b``$K0sP-#X&hnBJJcXO7%*}JV?dxE{z$f3^6}s$TL49+CYgfvP2r9RC!=W7->niL!%Db% zO7^QL`knbbpB@xpEo8_hH_iwk%Ah;1Qhx0EL}lrMCM1)a$+^sx6hz^^c~U%-GEfB zX)z#ri;l)-0GFI!qqJyG=NQXQ9+0BwLNg_v@p1gI1en8LQAcxc^HQk}vrIG-2Y2rr z#o9Jm!{17~FA?&SeAZAg64d)g?gI-BRbAWP-#$A5pKtHVgYFf+RJgz0$m55L0k!Ec zb}Ha-^7wo;v;+-HG0(oJEp*wqS)Qc2(zL*VCgw9v9H>Y|EB4Fsx}KV~;>6JHI6Dz_ zLj_RW;Qs0sJvx0EY4f+w)8}9wxN%%Bu{94)fj0jfzOqyj6#7_zM?vGhV~OQZQKu<1 zS0=W2<(S(3)EtbcoiD?%JZYj$tB`#~tDEp?Sguo{CYuzb;VjnYewPJNtNT7Tk3tnV zO)%!!FG=6a%N@j>*!i1>azboj8hF4+keN@*T9jq}sg4?SAn zcFK~pbY@rBMm2s0sn|ko@L@SvtMv!E=FZjN8RIGPd2qK!qvg+?@THMaG1{eXC|UPk-l}ucl{urn*=R-7n_QLWd-qQ7bIBTto`y+WgMyK=)xrzI~cMq zd&3ykd*O83aKg_PfaNjFs}mO3yzHJHe8Dlq^pe$lp-|X4p@1(!jkeMUj^_NSw#kVr zP}hk!_C3u7WZqXx+`;#r?Li!x70~7c&PX6wKTB%0B}q)=1|WCq*A!G;<8V=xh%OJ+ zyZw4``J62268%n=*PMwsByCE7-7#$N(iOW$bsA;Yw2r5ea`$AHpiie8Fwki4@hL|t z_+Xn9K~{azeh!nffahy@_>B}UL_sT4CaQ77;F!TjWaYg??5p0SWb-n++oRy-Ap*B5 z<73=3*om$g+r8eVUuN5rf2bAB$eHklX;=trqrCaM;~9FW8_5W^8iu}~-7mjo=+^~A zAoB^ExVG#iYJQf22e4+K+7K~|(;4iZkq5cT+y^m8&r*DE3wgCL)-_@TsBjs>BJoO> zmlh|3Qo~Y=?s}0=n!)`ku<}eqj;bz*srns$U3FpsN)-%P5Q`iyw+*7Gm_0r$-%oW7 zaoyk)ZeL8s9P74Zotv9h76(tDu7eL!ig^Qv0^Y>SGzjR~LVr=Wxsg53JLh)y-ndEK z-OA~kGlgm~QB)@YgcXclHe-+%%djzuE^r!2=9j)WE{{qIkF-tttx`YUafW{z;~AO} z`d0A-a*3=?)TA8ZW$IbZRV4nWZyB0$iQV&QXc;r^l9upZ7=pch_U&_RNirm|r^i>EbF_fTBU$FN{; zVwOTrHPK0%&&ELmZ~@|4_P8N2NDzL^_tUDCx;E0kzu!XZU0XTVS*NT zZP`3`(Bl-ff+wObH2pZjN=2l0YC2a{5^<-;o_GzMY$iU?4mt}IIJY4bBj<+*>#^X) zdE7&7R6$mFH$z4N&-^-OcQxQ>v?ZZ-$D3WWAieo(v=X*S-EZ2?)#S6JScN|t$r2ft zZ$^AV%)aoEGxbI*X^XjG8s&9w$o{+p2x-WLuy?bN;>ZvKj-_!Pv-=P)^e}(~kxzq<6NmK#%G&(!w=CwY_L*Di z^$EoaE+F&6;Cmi;VA4wDz-Yz?FPojFU6IKE;>Ek|TvriR*dF&1HMJsRE05a-d* z2BD6JHjD!M$swQMlWLUtISjR|=!Q?bl z-KsPhpf{#82FyANYxQ6_i+=4U{`QuH1CcADVT_F^-KYIa0NOfg*gU_c`Suj&R>{66 zv>zP|lS7CThM=C|Prbd>C*5axiEZO-GOD6LM_h4+Z5=EsEivqdgaCrIbz>T>~f53s`Q-jn9*9GMTs|$&ZMqzf}7mOVvz9n}}s1Gjv-K93A zjH;2QC1l6|;^~PIWkz?f+liv z(4^y+d1wa$-7j?~p;1x%5%9M`G^?1o=Vuv|Kpu#l6WXtEAL5l`Bbet((;BS=RD4B6 z_UqhQjc&5zCSV{RgP6tQQU3`cgLg+Ll7}}9z{lzi!Is{Igx!xo)bB3qFc$H&kQB5# zYHDNFaH#zv1K^C2v^HQ0vOK}%Ft1`WlXYy;SwYG8ZfYR5&?Oe(rtrBp{E9Zz4)Mp+j z_%Si5D!`GoJ#iF`Y(O;U7vLVDvmiuTuJg{Tpz!J;KYjAI+yWa?{=vA>hQ}5bm2*Ig8n0TpD(cOMT1WL#|flaQvHlL>)erZEk#r*^Qj zPOvw*bo{ny&~zmj_bl5{m@k4Y!+YXof7J>5X+x@j0^3i4pP4zY3(NOY8f~-%KKsNe zxYSg%_8*;=#=cPR!-7M8+go^KpIhVj``R889%PEncU0uoV5&ALhM$V>x~W8xP-pV3 zq#cyI*K2fQj9~(cy#cmoVg8u{sSV>_xYsKP;_@k78JNm|Bydnv+UxHlg_=Mgc?T}$ z1LfnxR22^LY1)?d>`*+By+rVx3_-yl$JOR#846^LAVto?obi0GM0E7`61#TU(fI?B z5~+ZL;YU!@_&m4jt+Zz)YZ;RE@xa#l;IIkia(;L7c>j!Q*daN;j)ZcJuc&lq*Sqaf z2^7N}1huW)Ao2}n*=c*=qKG-9N4#re&4gHP32#07z@I8Z%O3m6v`yZji}sN>-4)o? zb7|A7o1;;vKh9Ac%GJ}<854`GD)jZ(<~x|ie!b{`QJxfe%|mQ+rOlAUYl&F(nseKM zqne9>luISSb?ory>)VAtOpoO`hiZCE`*o&(0Q;`_18+kT8}QfUXg$j?u7B_KPg3&? z(n3Z4P$;;5Gq?T4W0#qZ3%rQ$Yu}|0=`y z{iP5MU6@7vp$PtlV3{p3{7214I6+2KxIs;Qus?GQ!%#&dgOlUG58m;*7^6qxDH5DvXhs39}K_?;(&i%-f zp}Md6YBATJJLtcilYrWJvsUQ4p#r14HHiXYAh)$)dIi6g=RYmd1R>Z zdYfLu;pVUvTYF}N3J@Q;L=UsY4MYz#I7?50& zUkLOY3`F~DG1FXrjWdkb7ABgy?~t}2B5yRBCgQ9QtxB}v(EMhqW=c>z#J%bMOQ_gP z)ER$x-O@p!sEFC*`_6P=PtveI+=uNKaO~ADGi($GhiSdaBq_CV2DwmVSL@ss z=^PGtNo-G4B)+V{oh+`>9zw*xp`L|BoV4|*O@7{9;Zdem|M^-@k7|N0^0Ee?GT_KQ z6xeX8&q(uowoMXkw@ZWyvuFeg4Z}HY;3Br+Cyq@4lH807X>rwEpmrJSZCVmOz(eFH zAWQ4?C!pU2ATSh)hknSJ>382>^Sjc2P+*qg)uJeLhNUSqNfbyi-sdh@o&jE>a%%{W zWzNuNDl8Vx9DeMY53_rJnc{SbNBqu8o<*>O% zU{fC%af;7amv7o&F7s5Y46n}i+FknW!r0Hj@ozVhSA-?{gN#zU=b)-Q1or~6M`1fD z2^_`wNSl2G$w!jRIDkhuTo?id`8AtZ^uNJVrgi-2GP2tDePX$SlXhADNLp|bB%bg0 zV>x$af?VOY!LUEzcP{(rY>LLMWRr?BYawq*y>X@5Ke|_^7!w42l7$>RLgDd1HE;Qn zv+_?Qrug|mgCP8VI4qsmZH2YmWX$bg=>(_|k8w_lBT&rPd+&z!h$%kygY&$3Yq<&D zY{;$bLV=atDQ~F|JOUI_bp5f!a@r9%j-VttG%MS8zLvCm1ly<(#`ylRC_>sRKJLf4 z?E||f?SkXk#C!BQrur{q{Y%NNXu29h^c8p3>(hO63PSn;vL7dEPbps z%&fvZ0#^%B+AZdnw)7t&U4*5vD>mXUnHSk zqmM>&5f6?&>sznHFA{a;+>-&YxHg1atAOu!!(2n+g{*V5x<7qSbHum#i-b4|Jn=5HfO8wB z^J;I(qT*V<9}fE%9gV6ybBxrwxhy-)cw=Yc(zuo8vZ4IJ1yPWw(~%?u>t!M(BM9H@ z;W(CESl1$vHR^$zHTS~?Hf-00W@_QYUE%ha!yq&_Gb_91$W389U-yMed~IQ=BNxU^ zj&CM|KD&1}3sz6wlLeJ>nprYhy#FL-S;1TqxL(JQBmk;YXGQPTVDe*#~NNpUD zcyJy#YFSK)zl|wwNDbYhn#ITnidu=nye3&|LjL&5^u77)@Tz&~&}(kt_~_$y9%D?a zc#6M1pC%$^%9~R`SiY~R<3GWFt-vQKJMm+^!MrJP!8HcLfH}6CGIMR-0eGd@cR{5w z#ax9gyABEiwvBR2tP&=7LhXX@qsXGuX1)k8S9ANr1OqBqXM~qiDiegV(-SwLbq<(q z>P957NZ@qI=Z~U^%mnC4KAbwmkbUNgOPLIYdOHLM1r$jpSl%x{yR`2p0M<+bH5>+k zF%+#a6l+2Obgc1mL~k+JKed#knG-HlhyhWqkikte3KvzWBn2ado2Sj?ccC~tvuS|H zycP5zrVwDQ9f_Ym?`JOh(Kq!UTqSTcWa$}7F|W4S)xSo<0)0*GMuX#JyS2b`X{G8noaP5Ir!kx<#&Y&Qgx-Mkk^{rWF( zeXirx;7y03WD}7B>kM*rIk|wdJ9ZaE>8a}h0`8$J_SSyy0TP-r=x1DYeoY7QFQgPKc27y$4?Vilf43T zR57jr`PU(;Sy;DZY28LJZ$E2RIYx_Aj7jPg@)Te6_slh&>s?t4nifR@t8%HmsbqTz zDf>NUHSGevKMX)m;ooS}FfK`p!tE0*+l1^{@syPM&cZs0?6e-WQ%1|yf`i$l(G+Z^ zrjI+Q>#*X*xjYd%E-46iuk{RcqLw-+y= zkY|sBt{6leAvxvbk9)OPC`B$;mG4IjOV{)*8v2FpvB$3;LOIwL&JLG!kSDuE%(NV4 zBQuQ5cPFZ0p>#7x16Gj9)zK65)P${j0Zky)kh9+`;S_j@t&(l=?gglh2<)YRl7ktF zbBknEY|&e={<}5{(&EhI2EHyA5diFk;nJO!qlP_>47^_CPjZ4o@WGer9UXon!nZ-B z6a=6v)v|>%6K1Fiip|xFP^P8i(GXvL;qac=B_^n7Ww_N zhQDNXYnUngQ1mN808K3Fq60FF#k#_z13dfC`n2Ikjg8qdfd1H56#PWnT7f$bG^XD6 zYYZ{kYBsmnlc!~=R6VLr23B$~QRRNP+3KsD3KRhG@XVlzU3DFUVpX<3>trpWnoUGI z`iy5#8%xVSOa5_JiU-oXw?Vu#T^gDURF|5NCzC85CY`&gZCHO!E?Yxc26xBla;{$G z4g?-|#v3z#J8<+5{8ik;9LXoa*8E_*H-lU?Lz?cfH)F3ToWWCsAfIoKP z53Wh1DcO#~4EXrutw2Q?Xs|oQ!=7oAGrov1(RznM>nZv<_F?@`lpjnSN^rhu-_v^0 z6j&QfZprk|QnYgjQZMWZC8S_;@PjIz8}j=5p}ygc5il z=7KOCRqjhGq4G5nrpYJf;8pfWc^AH~&Z?W`)G9jEVK8z((@GCH3wLFG;msn?o zDXH0=dAczB!=h9C&a+0cB|9;A?BMR)wJz1jtec~$_EJ>&H5K<#GSp4+qAaYlciR~W zB1X_yt9&%>seT?oL?c19D}5xEg=)_m&afU8Ye{abqc9XXR)_ z7;Sv0H<&@ymcxyEl29%du>=WTKDMg9K>xYUaG{e!+;;q$$nT$Vg!`XyBtVGefE67K zB7!;}?D0qWXwO20KJew!%Ed>EzaqU}O2)Y4j0JWQ+x;vIcXzRcp2`Is|4+l_>q|&; z|&$(ws?aW9vcm z+k>WAr}_e4LLpRSs-m79h)^LB?8igIJ(|A%_G50|kdCGf9cv-)Mo_sP}-%~VT! zyiz-%;7wT>qL!ZyKY+rw%WtM&{B;Gp;=Hk}^mUb!-M81BxL5-Ld;iyWHhW$Wyjr8! z+Nj77ZznxS-I68Fp)Vx)d)HTv7D$KUg>>*!x2T~|Q3Nqx4jQ)%v>f%wuTYtXl?4$@wN<9b9cKeppCONEMhj4OXxufZxhlfuv5P@D0;J z)6amsi|O-IfWxYeyklmWed;o&{Cq3#OA_V=Ae@)kf;R66zBqWM<%kG{prL@{8@Aiw zVCe5%8@i73g2gP@KSVt+zY*;(Ke=qkgx&~EH%^ynQnq`wyG|!~P9RV-CE`23=w#QECZ`uJSv4u7|S8R}t~qI1t^3wr<7ARWqs)=#QrD}or+a%+MCf5QoJ z-kYw68t8;O5d^3m1}(*Hx-s1P*}^1oh~6byh#zJ>%&g$)?U5k_`Zvd5%KRw4xk2VG zK>g+tu^!Uk5#%T9X^a7x%9UatFiUCjT?+nGcUVT$!Q+Z7`g8kE*g2A}yWTGA*|~#y z1>_D7IiYrahJDF$7_P*R6^WjVUB}YI5th{-$lMX$xqq#k!n}X1bQde2RXA80sJl`c z6^?cw!#!+ZTT^KfNs6#8uNxNZZmn)dllW7aJ?I9_rcA-jHa$gV&b# zUe-$sFxO1__GhOx5;l)zx1^ue%t!Mj?@f~|=yH7QNepxf?7`~}rtw`;=( z0xlIsf3`(OdcDn&23oKMFR7Pw&yfTCz+eGfa%fy!v51I^`0q#xl>R0A5+G>6A$c); z7_(5V!K2A5aj9;+6_c1pO#l2ey*ehA{9CEsHZ3jA;)IZ=>`rH|pB|()_^foT<@H-78f_;yKGo4L#Y?F%a1Z4)p>J#h}+<*ip9(`QQ__9h50xu z9*18eI-_FEJZ_%QmLcAvSJ3*2fkk<+)crc^_66P5QT9VpU*1IrQIts0b&Zk+;$L2K zJBPkOorv)%nywbxOmIAr-#1oFH5Z`;0a8{4O;OpktmfkRd$H|dcPuCO>lTePPj+k{ z3SCEy4II?JZPbm}fN4KT(#>WR}=fk1VB)S7_Je ze3icSstlguloty;B=A>@pcCA{=>I5dAhWyjLS(o1M`?#$zH?d`Urm!G(|emXh8SPh zuA_$6iMuWa>JfW=xRbp#9!zNh9*kjW^g1!N%8|@`vV_MWTD{~>pOzPsILuV1ie23i z`Z!d$j&v9^wp{wIX1c@pSN}+y_~EX&Rm(e9<=AMmkWj}CZQD8Ln&p)>BzLc2jI}y|PU6%q<2w@NPPrjn%daPRYL& zH*{wYky^nmaXzAYQ-%-*GJ+yxDP2}E=BjMf&I(>os}1R-g1>*$OxZJ)GbQfeZTNtP zdos@LTDcv*43mN)aY%r-^pBt2!o)kChz3|(os{mYizI}ZzkyS_A!QPq0>f=@5_3dz zh9)f`W2|X07vuKeQNNQeEOnQOqy~8By;?nborkC;EBAqCz z2o9RjhOYTW3N7}jAgfN1f#`%G)8>Vmq|wD$G)R_v53&SZbrVp`4`|X)qM7*T@^iCB zYM*U`juZ&@G8}m1WMO9zN>m(r3=R_d6_!%=LcCsy44^?sahWEtW(b2Uhbi8M#{%@& zMlLOS(1$N2dipr3&T&|~K%wlOszJ3)4=M;9tr9e)k|F!;efR5>& zHYar2qwn}Yd)O_&geT11_|7L4>_;>6g8FWpl5=ERw5mr3=L8k7Se9-to=N>p?x(0R z-E5_QW(A+#b>!#Fb7*5Le{6p2IBr^|dNLUyUk}YisE0w#cHkHCwpq^Eo@pFygx#pQ zn=`f$%85DbU}1p};m8CUM-!|WV#)0z`iCo&_4dQkrq$7=*M$1VaJuyFXZLPP+u48EQY$%aAY* z8}C+$lk5-JU6v*55D@>36I4OYsMfXOmK)ZS`^B;&Sj`~C#|=H#(l3w zfTC6RmoWo9J0bl$cg)DR*e;K|w?$fyrP>q^c>r|_Uf?^0M=K}W+7}NhTu#P=%^XE$ zGN5p@!^odv%|*H*TzvT;Qdp~3(1jd+#3wxT#%|^{e7jO=WbZ+ojyK+;@A$+rKLOCd zv)}3nnv;iWSqxqSs0_6f63^yHsp5rW7F7$1hpYef26R0~sfzd-8(6ep58oDzSo$D! ztNEvAogSUn%?dXUup_5G-j_%k>O-q+KxT-6uXJ_LL15*4UJmGY2WOQ-oIk|b>fv8* zm=Z!hDxbH)J!yD#*qk!Cf#_k9^HGcB+Y{Va9VU5BfSsN?=Up{TnCqB7)pS~*a;LLv zXvKs25n#BsoHMzGN#}+->KN2Bgxjf&(AQsKTlJs?{$iHjPzN7IhOF4bL%hhUh&U39 zw#1{RfysIZa=e}V*uUxA@nAdLfMl71UzxCzvD9iv{M$>*JF($!0@SZ!SRT zp6T-G?J$ZS!@-o*+JV5nH~COGgUCjeVOBb&*2Q8{7t>5Jc5Qp?2tHZ}^-&B=`% zEh{KoMy|FF$lMVQ?yS^!)($)EO2Ne`8raZ^jEcbU*@g(%v1wO3HYRKMoLeP2rNT?pQ=`ctc)=995u1<5Y==&+iO zg&vP?U8``&*_8j%nxFe#i;@*dg{gv5L(0YHMtWV}<*j{n$u)zoZ$YpERG^J2>0#QM z?lhgvv+dv}&q`ukT3wC_w*xEZZ&^<^Z;UgwR#0iKgy!sh1=+Ni{wcobFR#?S&(ft+ z=D8WUYuBP~M3%8s>s|CTR1a8ZPkZFCeL=j^tUFCZ9Av?*GKRC^Uway#^E2Fv$u54W zV}ds-5-`Cx1dvZA(zuSKPrzyCmo#AW=1FWW76;R}sqJn_&Z2R{!fpUk6aR=JIrqW7 zEn*H{Vrm^0WJi2A6^R;MxXUqI26Pk6vQeL1yzcv&?H0fKeS<$W=ses6eB^{aQ$OG} zTs1Hd&LVfEhkG`R2&WqS$ndX8B$v6lu!8<=@h#MTiC!re32yOfdx zM+6@C5AA-XAzPVF6QN=>&o>9Mx;yo_j>4@L`?QhF7!v*>E73&QU8?(j`f%Q7?#-wr z$h~`7DU{QlFR}I+Hfd&t(F?|UwK-~9*q=!431Rg|jQ3)doLZtK4L_Xji+kvX?Kc&e z8+XH^S&_M+5k@oPFyCi{Bq>}3cT%7SfJv=vd#o-rnB80EYo`F+fZjNji+_;pXmI6W z)}6E|*qH>yFhiHHbdzfvlxMSi)AeZt>$0^UpL#X2qhvqgdFNyBD@U_B`Xc%!@h$P(;f#1K3iQ$S#ot=k_LRzxAB6c zGeqa`Q*-PZ3(aNMjw()&`Xw_$0i({XdV3dL{Y0hc0LPCGWIG zr0jj;h;KcfqmwGZx#*`K6;JyIB)rpIT{af-jCJ=mhg|zFkiG{%;#Jrgz=abJp}1=L zr*0rk_iU{nPFx&%hq?vk4l@*MDB3=?iPM6I>spvOR}9Ibf|q*2ekl`*6)dJadTe2o zp=ROjTi|Kk8WBGbLML2LOMlQXOB)MC9^jZW-c9`t zuS&p6mXV#QioBH%=dszPyMk_u=vF_zv$oGwJ?MU9ZY6W~wKVoQX76_LcnH)9g?g;cy7P&GhR79i59-AXB;oNrcsp|xfm&lN@2lozIolbuKo~w1Wl98(1L%$0hxQg&I;y1fR0=I=w^H_8>OlHl& ztC)i8;O~sdHOBP^Ofz8%06{gk^cAJHeJ;Ga{5azS+ozc`p`J7Wg4Hm^E`+pVPvWo~ zm^iq03|q(D)vw{uYvS;5(i#JfrznRh#0P+7yvCLw<%RqR#*OHb%Y zxo+ps%{KX28CKgRSWU&%#8iDX4ESPgv`Medv zEjJ^fDwG*#aN~`>DiH-7cDSZ!+r1M`Pweo25GhaJ-E-s z%M2Qo@QKL~TDPHnyV#4SUN*Z(Zq9+hru`H*`9QUVBVCPDYpg&HjQ=Ka&qQ{#=lfZ@ z^Ch>O1dEA;1_u7#6=*9Gc~>Tzy(aGyLT_80sMEGasee0nr9hr z^;e2lv{ZPb&rUXQr;I-KpR|^j$%1SbSl&l+GER{#U+~AkJo71pbGIuubpZ!c}7$`GQw5j zu3H`8d{$nwBEsv1OVB&K>^{MN{dGv$A9taO$6>xLSsLZt8cFlArRFp@A1qr!<%aME zO8$jeX7&{~_W~#w{>=12J+)W0Am{rGELrT> z7dqw~oBQ}>J1oAzI4;Fh#R%`1Jlg@r<)iGhmdaR}V16SliE$-J@E#6uJVWpEUO{gBy(p`r%P)3bi|P>yqbESZASK%u({BO| zMW%fl-Fy+R@6^1#wW^`u=4kf7c(1$Wl2=r(-6S zaXO>TgUkqPBm(2(t~Z)?Zft|l5G{ToMa?AdSf8%%d-L*-iGhXMU4?F?ei0P$b}&}o zpRY=lEyejHx^LQy6{cl+j%}^20|F^4S?B7Db%c!1 zL96R5e^zDV>Hu2{lkKq;=H@x+t3PN1r==i%wvqN-&ZXx!*0zwGhmTuMQGTI3jQIP% zBwT->Psq4b5a6B}MyJPEZ`Rom99B?$Ur+&PXv@lhEoa-F;* zUGpO5Gv(#}Xt7THW$|lCz-F|Ar4Ex7)9Y&%IV-6hd=$!`WIBJ%O?{IgIVZGEm%eoF zs$sHNByJu`xo3--;zR-q{gMV~;(RN1s6ZcSm51{p#Cf|NuU2;>*;C%cj2@v~n}`ld zRpGL`=*vyEmsFL4-%WF2`W^e8A5;>eqhv|?ZwhtnOnOK!8cJZpF|Nmey4BOPc0j4W zUkDCN*)g^FKgXfCx7wHB+NF5`PeB}9&R3n_k;<*3ic7=Jto(wR zWlB_jLz`)XS#sI={45W#3!#`4VaWV?-R)VDdAX-Zbp?uKUi-(Au$52CRQzu%1LaX8 z6J~7!ZuX@P3<&MHm0eeNgGohQjwond9FFKl&toMw@m|?nHyUAt@tFg-Bp979VHh;s z4=aE0dLIEz79UsR*uAO)Uu)ECBXc1^MwCUD4Ev7#f`NO@f_C%}bLE&mCmQ|>QWhXz k)|tG7C|NyY5sGtO)vKL5fz=F?Ui-lBa&}V@QEt-z2T@UqVE_OC literal 0 HcmV?d00001 diff --git a/shop/static/shop/images/products/PC008/kaki-fonce/PS_PA439_DARKKHAKI.png.avif b/shop/static/shop/images/products/PC008/kaki-fonce/PS_PA439_DARKKHAKI.png.avif new file mode 100644 index 0000000000000000000000000000000000000000..534bf6a79c3946c01b1ad6c0b11b5c786543e850 GIT binary patch literal 11356 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 literal 0 HcmV?d00001 diff --git a/shop/static/shop/images/products/PC008/noir/PS_PA439_BLACK.png.avif b/shop/static/shop/images/products/PC008/noir/PS_PA439_BLACK.png.avif new file mode 100644 index 0000000000000000000000000000000000000000..b5f494307d2ca5fbbfed5bddfcf6e1bd69441e15 GIT binary patch literal 8193 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 *58y1dPgSP7P zx*3-@jfU)3DQoTe9Jj5&8$ zMr~u`HNIV2MSw>y4lu3VnM3+Y%%*->sX8*Q(>PMnBmJE{Y^QH!Q%nnPqeP`}D6}-* zIhQWukHy>#>Ywzwq0c)iFd}EBnd()bZKv(h$n$l{Or%rn;P*inF;e8jXO0rdly1vk z_=rJ&>i zj5NgM{-w%*iA7Ex3V)wH6FVKrcikvF;9zdZcDqxHGh4@4EEZbVJTbk3>10X{4nq-+ zO)AxNk=;tR?nwKW^+|PF)P#l33__F{J&~Tpdr!17t}GOma+%9q@%oDHM0`FVn{|$F zp4J|EQm@>Ka^FpMh2s{d{APh-;QS9)(W*{vPLD3qS3PMb59^=<@DWOP?G=hGm=yzA z)U(TWU)0*edrwOSZjcv598fO8!m=&8Lr{8juJICll1Hn;5~dro+wNGjV#{v~Oz1QP zo6&d4dfgy_Lr=QLnJ^d{IG9wFOZ%f6 zebsF9ky?0$tGnk{4P6bxakhs=<$k&l)X;Tkxk+-JH#5e=r3eZdxgopj;cY{??@j(l z)r5;s;lJO*WFYTdFK;+lOdPJUIPCgiWXQwPKB669n!<>Ju)?$Jb3)H`y-fWWbGkGL z3%6QtljPp+>=c)jC(Q*J6L;84ZOjNtosmF)PhSrRcO}De&BBd`a%JrhLabt;Vzg$N zm--Ef$8i6W=O3gJ+$k9#PWC=@%{xB?18}u^`T3_ z6=wFAuR&{MG3(Ng5YJd!tvWfo+|_{gVW7clZCLoY(3g|r(5GhDvkf=obY4DmEOE2J zWMtYNJzIYrfA3YnaWn6k$QMDd)Q)A)Pq5(tS}lR&lkz0K*9Xl=g3?;B*dCOWkx_qM zvmKZ~Cmv$Tr6=r*)*%CMsA!W$Gd~9^yJf&ECmqYtDTW6lu2#*kR>u&Fp4Wa`x~D8C z36MU|swqF?OXwv~2cVi@ut&hzZLz(6B{TQ{Zuxb6n&vmf^P_P`R{YOyeB`pi!B-3p z+F5_seYNB?(>~(ZN(`D!m93}ua}a$ez=(MUk$i*g@Cj4%wfTMSld%dV#DDD@mBl!m zj)c0`bEIJumMyf2_O!iPF!`j`W{J^D%;)wLP-SZoJB_(E4XPjs8$oUXFHhMaMB&lR zbYh@{eBqGatrs*{4o>h=0T3fbgMhGIl&*#gxCJ%D;?${+xLJImu@Wk(PM_ior9B zrMZ5mklU2_3P0-(^Ak@L2#mEi{%6|h@bezwabLnYE)Mem8F*rozvRhFs4l(}pphn( z^n{blV1VRCt6RiYHSdS>gnbxa5iEMS?7G1MBS(nv+PdL;#7iGhjJV!Qf%N#h-^l6p zewyqob66N-077C?xybQYH=I(@ZFTZV^(M=q{4{ic`|gGJ?60g=G0U}w^2Ki=&+ce4 zvbsn8RLKwlc;X)t`Wudh%5;*Da_#;LoKn*Za#KKPoFV(WywO5`j?eqm7d6e=Xf=9W z+}`*$rh|}N(KF<^XRsvs$@j?VLILRed>Kgd{&Yg9%S>gNxzM!kD3c5cPFHR@px<+N z;VY52p3n6pa=c1Lr>K7Y*)<3OFK^KYD?`iKidE&5NlT&XN7`60mBn1d%Rjk!`z#|N zaGy`AQRogaqx!eRB{{qbVFJ^}}xlDtPHcY_bGInSk(jZkl2Nv*4^_9t~1C zQ5uUyNTQ+y>helr2Jfrf1OE42^mpEet~&g*?h++o4tI>k5x=VdfY{p{F$@XGF^mSS z=3aZ@q+cYfu464PdYiPzq$(vahYncw=-r*r=GiXn7s-&Q4~Y@I!ue|AkTlEpf6(wO zpWHUGza8*fU@IK2j1vMt8Ohs+E!6|LG1XSV8Oa+bIF&t6dwQ@K$}o|z!)Vi@+BWIw zs7JH^&M+nh5XO7SYWv*F=7k?ymeG;?<7?^O>|+Je^8=BDf2LeVa2@89 zjft6~?Le)KoR9mH9%DLc#XLJ649VVy9ENF+9SSNyQQ8J{T-nvHj^AY*%?=!@Bi)nQeJWP(;5_J0*}OOGqk@ z9IA2Q@LX?qM_+~HAC*46Z3V(LYUFWmr3Q*fhg6*uERD_QAM2W#tbn}LF{{3+NP5FY zqHpF3`)B>hoaY%Bm2k>K@MUGhl=>8Hb((?sK7G=w3f}PAXf)z(Ek3Q z3A3c<`x_hLcpT%MEbKmI#md^Rqo(dGYnOGqTD}ylK^R+eX_ifP76&P9+~ZsbIqMfJ zI)S2LckzA~0{fOu6+Km(GZp!1GTA&F&(s-{@B1D2m53}-xVFgoRHeM2VWsfj5hO@* zjx8CN#R<1hiqKepE^(~sDfgQbJYjohDI2@n%(Ih17amb|bN23)os!9@k9gI*6BX^v zw7s3~Nv5O#RNQm=llV!!=Wai=(SuG!TDOZnd^)$4&Dck=@lSOz#$NhRDy{^`lcbgq zx)Uk4*9d%@#4V!A5g>`hELn-~s}a+az?4`KmyQY3-D7XhLAnj=H=UcoHHO2NhX)S4Y+(?z#OVzuej?PpzQYWZ?!pZTeXCtVLCsZ_y=!FZ6rD86og=H#;mKtk}i@El3H&c1r2=J+ldx z=KdGiL%qS^O{q5H~sSoSYWT>42b)SE^DIiyKFX9wd9WHZ9bbiQe*YfKPS zW*Fw7veYOmNO!lRpgY1)*OM$i13QDwx=C}CpO$(aLYexJL1wDsecfhbJPsyRjp~;E zm3ts%NhxbF4j3*oJ}{_ZIeVLCRfs%FPo|bGiaE+P#feXjOh*GtN6<{O439wQ(u{RW z70{z?(c`{Qod;3%Yj*y}F(}X00$cUiObmu)R8A=Hqt;r^yM=(yXTr}TT?nsEd{0Kt zZoNBkrV{NRLbesaN&3RJrU_+rRm0gK7nbqO1Ds6p-=%S~2aIm@OI_kK{)o0(tk}MY z9EYf!lFBo=eq#9vPdsiK)hVVDFc}zX6Gxda`%+-w(q+3?d|waCiCNruk|OU*)kj=j z=l9IWf6QG>fnN7yYFOC}%IdnrnE}}J;f}G^+!iI*3)3wly*+M+JFtFu0x;JNwg^1W zo`{-))h5NDpLZn6Cbev^K?HNj*I=}^D0D3i!_?NZsP}fA^9zoAlKVUe0|J{3-i>-f z&UQ?Sa*%+ptnoBu7AxtrlXl3ptsogs0ul{>onO<1cdGy(3OYdJU4hA`v57WIV_%+mQ?Uti=$J*R@0;R&*DT21; z8^tu1riNEtPfsO3@n>1(rY^mdp2uN{3e)}SvBLH*g`JJcTi-0E#MXdffvY3a7F(@f zotAQICtQ^Q9YH}SSxLETF4voSOlF7~*Ll1qu;B&FW8!O7pJZ)26Y(G0o%0``g55JQ zF-%)Z9~3$>;7*CMaJ4iA8IwxqP#R}~?b%&x89BF(%}q`8&#Fv0BNS>HYhpkl33OtC z-T0!Z6#*kZve&SSvG>$09l*{|S_+yd{8GWuKl4>+Ll#M3CgB<7Yj^68y}FU#`Z7+S zY*;U;O`|*<{|@_yr_wFw8=fB=*%P$Wil9L*OzJ$&N4#VgVt&r(KPh%U(${iVNeSOL z-FtGcn%uEdJuH}6;)Ch?P)_q80TzC=rYVjF~;oq+}=!OsZ> zgJBA-}`pg+tFLBv-6-Wl-82-MCI#m6ynl=aLo7gO_`x4g2ASIALqNs1s)| zgg2#%cs!=f-=36+XqEUYYQ%qMUu+na&czdae}4^rc`rf66uta}Aw=KTA4I;}o=cGm z*61jX0cJ^*(6EG)4Z#uf3Bxn)$8fY?RnUA!G*$2}7M!JAEclkSbk+s1zmk=Wv)8M=hSWZ8{0!5p9MT|8O8u?7>^Ozk1P#-Q^D4I;SJ z(na`lqL?McszK+UhTLG)NRp%R_jH!4N5Ik9HVR5Xtit!){Zp)el8Sd1k+Z(tN6HDGiP}0e&cn>cj-g&_?!A192yVKTsiMGm!#E`hwMW;b&61G2VPlmEUZ1 zkzd|ZB4nTlKE+AboR3pg6ggW^2xi?f?n2XVQ^;4;AR%BIBp48BiSH%5>Oe~58^Nct z#${0iy1uTB%8bgRvPHH}yLsr5-56ToCm!pB-9LLO+J~i@dk8ehVu_HL4Z$no3O>}@ zYWjT>2vf)R0?`~gm8@2~Z0ZEs=;b(h?wO>j)C2UK?I*evwkQbD8-<1FbjN$)3FxVI zsba8FI^*0#vwTJ*^Urb%-AHZ3WXI%*`;spGUJ3STEMDcC{J;5)ZWjNHaI-X^Kt(^Xqnge|AM(I&CY zzvU#Nf-Y-~VB@-V4sjHbodhPrmVDS*cRRF_d8DtfkKVQ=o4vB!(c7nBoWvY=oJ5Bm zCTIx09^M0Sxh3ZXY5E=;^Cj8n0s%YA`d&b)?-s^s`wG)(F4 z3ecC$p8~-y!Q30(n~73S60O}(pC=QNhu*m}4(6Rq8`KT&*Y*YZd_y&k>o%2>v8RYE zfy)jg^>`qV3{gJJsHsJGWwilW7+`prI--q0qY)XKs?xSfy>pIQtNjl3Z}E!21><{K z6FYr(Buln(KSfN*Is{VKi7Cad!@*zKvnKNv{7*M1BSje4a*^RGw!M;Y zBXJL&r<7YVd0+x@-DQ&Qa?GBaB$ytN%mLEO&k%aYB3{vFqA(VXo)rw4QNE{2lZ{G( zEDiED#fyr|S%IYIKC-(JawgR%XZ#uk4xVG5a2lG5Y;U9yT)_!E5w06TY&P;OK+u|- zJ*x6|IzJ&V_(9^#E;A;IisL2%u0>q1B2s${QbXYe7!{&6HS`A(3^=_30S1v-jvDck z_u%9$otU0lBWQEe=MscKAAxbElv#Z3-{)=U)C28oZy()JhB4h@#|VKl7o!%=rOyZu2HCO3u8fTJCYje<2UBH>l+1z3 zGcmnL*>6p(Ql(zeK0V@@j;MvYrpj)?!a{$TiZ-lGoE7T zAdG;yM~>Jjl*z#(KPyAMjIS<|) z)+~fH*Nhc(U+n`OE^{B*pVP#f9thY+tr9HuJzzg{`4e5cE74+{bN(yz+CFP^i#fdP zqu>NifdZ(TVn>qWiiTd2*rl_Dy+LluKesT|p@$%i&J|4I{QaZ0m4rDeOBV&GM@_~g z=k_894Q1%#LIKAIP~HwKCrBeZ!~1?dmZW3tpZ`sy*nO~t8uRV_0EAoXQOl2`hym#f)vgK%7;HgN)IIC zNBfcDYQ{r2KZZ)oLgy-tu-x6@a`2v?5=*XS0Os%htb-TwZ~j_DTJwu*B{QU80;a$x zUufz(LY)0R!+E`;UzOC|4*~vgB($fD${;1iU0HuHLHLI;-P9FA#K)fFpAzM#slf3M z@;#JdV3$;`NqLWBhUiC~ev!;QIzhMwOXIZxAayBPGd{S*GjcTvO$iN|OHCL(lpmAS zgY0CxRzl#Px6;LoYzX2C`b^J((&tWNk%OpNchR5{vG&yTw4I&rjPTgC$*RuQg)mJb z^Ju@YrS@(bF#EKEtAnlY4lUyDlPpICP?rUu%F<|8B zO_1ZuiXz=AdiwB;m>?BTFtJe->;pM6N%iH6pi$E;%UUg!7!Q-Ge5BV|F^uxLV6;45G;B9g(~Ej9!YdZNIzZaFaxMnE#U!zVJsPq`;h7 z?les!_Sot0H9PVqVgQfDQ$WTQwn4<3WHY(%0?FOp$5t4utoJin4K%vhF2)-c!Jly& zdSl)#!~}gn$KlNVhBBZU8gZL15b8M&ooVNw-!}m-wm=sG8z{P;Du+Y^>u~%52 zEpuYkLH=PV@m=)ff`$qooxNS}b7G0Ztz zZ{Y`i|3;!}*zwU<^6#eTfsAKYYjN0Wq>y0-(BBTqvb%piFOdKmCQt4z56nXG#J$EA z5$uE+MZ-`wo{(qs9`J*H8_`w7zYLnNKSVGAX`m{0<-*m>wO7*f|L(O7n|$EMnB*Cm z$B8OU8t#mc&{!PxIC;(6M6s^WjR#2~@dPnrR*{DRx|R12!`XUuQV-%*Z& z`2EL-(UY`*oqap;P!cRz{4YI`LK7v}k;4z3fbH~>^USF%E!+-TGHPo0KTvY{eO_E7 zq3U8Sc@<4!M4({i`F*;MGN$PV0C5$ng1*Y_=PfC2XaRc3Jojeoq z!w=t|I5$O)T4-x_CG-0;`=t$_6k-GK3O(~hWGdkxAg2Cc!n|1a#DR`ls>iPJ;`qNZ z)K~;Qwqj_O~cppBBLkee)t&?km04Bf9hcE_^G z&o{Rjmg0CHs1TB%$qh}6U*_VwDPHt^K&%r_Dd~WjrjruMaF>l0*Bg!wafCA#H|T*;t^8u2W{<Iz-JN)WK?Sgmy0)KQLdT z7{`YkvDsu`3m6Vc{rITo@THE!zy{ZcwTJP02z}=f^dI#)p#LG$L*=ZO?)DqPCf+w$ z!Xi|Oxkq8jKh0W=9Kym1$}{vfM0n>u5WZ1N3MM0c#EUcSTVNkDBKWqX+qz-&uD_ zZfUw-B&%(?c_H}#*WV?Uqmp)6_ap)Twd(=j;XFvTlpH#2weXs~{ plZJ;QW_5{6WzVfaTWxNa_2CXu%P{{esHCq@7O literal 0 HcmV?d00001 diff --git a/shop/static/shop/images/products/PC008/sand/PS_PA439_SAND.png.avif b/shop/static/shop/images/products/PC008/sand/PS_PA439_SAND.png.avif new file mode 100644 index 0000000000000000000000000000000000000000..c9d98e632fcf10aa11a222ec96b0bef1ac3d8207 GIT binary patch literal 17038 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 NZipHf2qEoDbP%%-h^PPn literal 0 HcmV?d00001 diff --git a/shop/static/shop/images/products/PC009/blanc/PS_PA438_WHITE.png.avif b/shop/static/shop/images/products/PC009/blanc/PS_PA438_WHITE.png.avif new file mode 100644 index 0000000000000000000000000000000000000000..496c0f4b927daf628e8948aaf99279ad7923cd68 GIT binary patch literal 11295 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 literal 0 HcmV?d00001 diff --git a/shop/static/shop/images/products/PC009/noir/PS_PA438_BLACK.png.avif b/shop/static/shop/images/products/PC009/noir/PS_PA438_BLACK.png.avif new file mode 100644 index 0000000000000000000000000000000000000000..973481ffaa25778446687baa4628065dec5d7b1f GIT binary patch literal 7960 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 GzyAZvGw+N5 literal 0 HcmV?d00001 diff --git a/shop/static/shop/images/products/PC009/rose-clair/PS_PA438_PALEPINK.png.avif b/shop/static/shop/images/products/PC009/rose-clair/PS_PA438_PALEPINK.png.avif new file mode 100644 index 0000000000000000000000000000000000000000..b53cbdc0d1045690ad7863e2e4155086f9d00f8a GIT binary patch literal 13126 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�jbp{a<_qiAB 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 - La Boutique - Mes Commandes + Accueil + Clubs {% if user.is_authenticated %} + Mes tournois Mon compte + Mes Commandes {% else %} - Se connecter + Se connecter {% endif %} + La Boutique From 8dd3438acedfb8e56d4335cc383a7676ca86be1d Mon Sep 17 00:00:00 2001 From: Raz Date: Tue, 6 May 2025 13:21:52 +0200 Subject: [PATCH 008/110] fix some nav links --- shop/templates/shop/partials/navigation_base.html | 2 +- tournaments/templates/profile.html | 3 +++ tournaments/templates/tournaments/navigation_base.html | 3 ++- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/shop/templates/shop/partials/navigation_base.html b/shop/templates/shop/partials/navigation_base.html index 9f54c1a..1e3278f 100644 --- a/shop/templates/shop/partials/navigation_base.html +++ b/shop/templates/shop/partials/navigation_base.html @@ -3,8 +3,8 @@ Clubs {% if user.is_authenticated %} Mes tournois + Mes commandes Mon compte - Mes Commandes {% else %} Se connecter {% endif %} diff --git a/tournaments/templates/profile.html b/tournaments/templates/profile.html index 0e79dbd..799ac57 100644 --- a/tournaments/templates/profile.html +++ b/tournaments/templates/profile.html @@ -10,10 +10,13 @@ Clubs {% if user.is_authenticated %} Mes tournois + Mes commandes Mon compte + La boutique Se déconnecter {% else %} Se connecter + La boutique {% endif %} diff --git a/tournaments/templates/tournaments/navigation_base.html b/tournaments/templates/tournaments/navigation_base.html index b9e4be8..b1ef0ea 100644 --- a/tournaments/templates/tournaments/navigation_base.html +++ b/tournaments/templates/tournaments/navigation_base.html @@ -4,10 +4,11 @@ Clubs {% if user.is_authenticated %} Mes tournois + Mes commandes Mon compte {% else %} Se connecter {% endif %} + La boutique Ajouter vos tournois - La Boutique From 6247fee7051a4c6972583defd31ae7373b80524d Mon Sep 17 00:00:00 2001 From: Raz Date: Tue, 6 May 2025 13:55:50 +0200 Subject: [PATCH 009/110] add signal message for shop --- shop/models.py | 20 ++++++++++++++++++++ shop/signals.py | 32 +++++++++++++++++++++----------- 2 files changed, 41 insertions(+), 11 deletions(-) diff --git a/shop/models.py b/shop/models.py index ae55bbe..bf51bfe 100644 --- a/shop/models.py +++ b/shop/models.py @@ -156,6 +156,26 @@ class Order(models.Model): def shipping_address_can_be_edited(self): return self.status in [OrderStatus.PENDING, OrderStatus.PAID, OrderStatus.PREPARED] + def get_shipping_address(self): + """ + Returns a formatted string of the shipping address + """ + if not self.shipping_address: + return "Aucune adresse de livraison fournie" + + address_parts = [ + self.shipping_address.street_address, + self.shipping_address.apartment if self.shipping_address.apartment else None, + self.shipping_address.city, + self.shipping_address.state if self.shipping_address.state else None, + self.shipping_address.postal_code, + self.shipping_address.country + ] + + # Filter out None values and join with newlines + formatted_address = '\n'.join(part for part in address_parts if part) + return formatted_address + class OrderItem(models.Model): order = models.ForeignKey(Order, on_delete=models.CASCADE, related_name='items') product = models.ForeignKey(Product, on_delete=models.CASCADE) diff --git a/shop/signals.py b/shop/signals.py index f449550..c226a47 100644 --- a/shop/signals.py +++ b/shop/signals.py @@ -70,11 +70,11 @@ def _get_order_details(instance): # Translate statuses status_fr_map = { "PENDING": "EN ATTENTE", "PAID": "PAYÉE", - "SHIPPED": "EXPÉDIÉE", "DELIVERED": "LIVRÉE", "CANCELED": "ANNULÉE" + "SHIPPED": "EXPÉDIÉE", "DELIVERED": "LIVRÉE", "CANCELED": "ANNULÉE", "REFUNDED": "REMBOURSÉE", "PREPARED": "EN COURS DE PRÉPARATION", } payment_status_fr_map = { - "UNPAID": "NON PAYÉE", "PAID": "PAYÉE", "FAILED": "ÉCHOUÉE" + "UNPAID": "NON PAYÉE", "PAID": "PAYÉE", "FAILED": "ÉCHOUÉE", "REFUNDED": "REMBOURSÉE", } # Calculate discount information @@ -106,7 +106,8 @@ def _get_order_details(instance): 'customer_info': customer_info, 'customer_email': customer_email, 'date_ordered': instance.date_ordered, - 'admin_url': f"{settings.SHOP_SITE_ROOT_URL}{reverse('admin:shop_order_change', args=[instance.id])}" + 'admin_url': f"{settings.SHOP_SITE_ROOT_URL}{reverse('admin:shop_order_change', args=[instance.id])}", + 'shipping_address': instance.get_shipping_address(), } def _build_items_list(order_id, action): @@ -153,6 +154,8 @@ Statut de paiement: {order_details['payment_status_fr']} {order_details['customer_info']} +{order_details['shipping_address']} + Articles: {items_list} @@ -195,7 +198,8 @@ def _send_customer_notification(instance, order_details, items_list): order_details['final_price'], items_list, contact_email, - shop_url + shop_url, + order_details['shipping_address'] ) # Skip if no email content returned @@ -213,7 +217,7 @@ def _send_customer_notification(instance, order_details, items_list): def _get_customer_email_content(status, payment_status, order_id, date, status_fr, total_price, has_coupon, coupon_info, discount_amount, - final_price, items_list, contact_email, shop_url): + final_price, items_list, contact_email, shop_url, shipping_address): """Get the appropriate customer email content based on order status.""" # Build price information with coupon details if applicable @@ -230,21 +234,23 @@ Montant payé: {final_price}€""" 'subject': f"Confirmation de votre commande #{order_id} - Padel Club", 'message': _build_payment_confirmation_email(order_id, date, status_fr, price_info, items_list, - contact_email, shop_url) + contact_email, shop_url, shipping_address) } # Order status update email - elif status in [OrderStatus.SHIPPED, OrderStatus.DELIVERED, OrderStatus.CANCELED]: + elif status in [OrderStatus.SHIPPED, OrderStatus.DELIVERED, OrderStatus.CANCELED, OrderStatus.PREPARED, OrderStatus.REFUNDED]: status_message = { + OrderStatus.PREPARED: "Votre commande est en cours de préparation.", OrderStatus.SHIPPED: "Votre commande a été expédiée et est en cours de livraison.", OrderStatus.DELIVERED: "Votre commande a été livrée. Nous espérons que vous apprécierez vos produits !", + OrderStatus.REFUNDED: "Votre commande a été annulée et son remboursement est en cours de traitement.", OrderStatus.CANCELED: "Votre commande a été annulée. Si vous n'êtes pas à l'origine de cette annulation, veuillez nous contacter immédiatement." }.get(status, "") return { 'subject': f"Mise à jour de votre commande #{order_id} - Padel Club", 'message': _build_status_update_email(order_id, date, status_message, status_fr, - price_info, items_list, contact_email) + price_info, items_list, contact_email, shipping_address) } # Payment issue notification @@ -266,7 +272,7 @@ Montant payé: {final_price}€""" # No email needed return None -def _build_payment_confirmation_email(order_id, date, status_fr, price_info, items_list, contact_email, shop_url): +def _build_payment_confirmation_email(order_id, date, status_fr, price_info, items_list, contact_email, shop_url, shipping_address): """Build payment confirmation email message.""" return f""" Bonjour, @@ -282,7 +288,10 @@ Détail de votre commande : {items_list} IMPORTANT - COMMENT RÉCUPÉRER VOTRE COMMANDE : -Notre boutique fonctionne entre amis 'Padel Club'. Nous allons préparer votre commande et vous la remettre en main propre lors d'une prochaine session de padel ! Aucune expédition n'est prévue, nous vous remettrons directement vos articles sur place. +Notre boutique fonctionne entre amis 'Padel Club'. +Nous allons préparer votre commande et vous la remettre en main propre lors d'une prochaine session de padel ! +Si jamais la livraison est possible, nous vous expédierons votre commande à l'adresse indiquée. Vous serez alors notifiés par email lorsque votre commande sera expédiée. +{shipping_address} Pour toute question concernant votre commande, n'hésitez pas à contacter notre service client : {contact_email} @@ -295,7 +304,7 @@ Merci de votre confiance et à bientôt sur Padel Club ! L'équipe Padel Club """ -def _build_status_update_email(order_id, date, status_message, status_fr, price_info, items_list, contact_email): +def _build_status_update_email(order_id, date, status_message, status_fr, price_info, items_list, contact_email, shipping_address): """Build status update email message.""" return f""" Bonjour, @@ -303,6 +312,7 @@ Bonjour, Mise à jour concernant votre commande Padel Club #{order_id} du {date} : {status_message} +{shipping_address} Statut actuel: {status_fr} {price_info} From eebf18d1a107a545587234a5b4f53625a09406d8 Mon Sep 17 00:00:00 2001 From: Raz Date: Tue, 6 May 2025 14:01:43 +0200 Subject: [PATCH 010/110] fix empty order list --- shop/static/shop/css/shop.css | 6 ++++++ shop/templates/shop/cart.html | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/shop/static/shop/css/shop.css b/shop/static/shop/css/shop.css index 11e764f..42507a1 100644 --- a/shop/static/shop/css/shop.css +++ b/shop/static/shop/css/shop.css @@ -130,6 +130,9 @@ .confirm-nav-button { background-color: #90ee90; color: #505050; + min-width: 100px; + border-radius: 12px; + height: 40px; font-size: 12px; font-weight: 600; text-decoration: none; @@ -138,6 +141,9 @@ .cancel-nav-button { background-color: #e84039; color: white; + min-width: 100px; + border-radius: 12px; + height: 40px; font-size: 12px; font-weight: 600; text-decoration: none; diff --git a/shop/templates/shop/cart.html b/shop/templates/shop/cart.html index 43f5401..e2df4d8 100644 --- a/shop/templates/shop/cart.html +++ b/shop/templates/shop/cart.html @@ -35,8 +35,8 @@
Avez-vous un code de réduction?
- - + +
From 762b79200af2c83a00fa269a7c9a4d6a5c3afc5e Mon Sep 17 00:00:00 2001 From: Raz Date: Tue, 6 May 2025 14:05:43 +0200 Subject: [PATCH 011/110] fix empty order list --- shop/static/shop/css/shop.css | 6 ------ shop/templates/shop/cart.html | 4 ++-- shop/templates/shop/my_orders.html | 2 +- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/shop/static/shop/css/shop.css b/shop/static/shop/css/shop.css index 42507a1..11e764f 100644 --- a/shop/static/shop/css/shop.css +++ b/shop/static/shop/css/shop.css @@ -130,9 +130,6 @@ .confirm-nav-button { background-color: #90ee90; color: #505050; - min-width: 100px; - border-radius: 12px; - height: 40px; font-size: 12px; font-weight: 600; text-decoration: none; @@ -141,9 +138,6 @@ .cancel-nav-button { background-color: #e84039; color: white; - min-width: 100px; - border-radius: 12px; - height: 40px; font-size: 12px; font-weight: 600; text-decoration: none; diff --git a/shop/templates/shop/cart.html b/shop/templates/shop/cart.html index e2df4d8..43f5401 100644 --- a/shop/templates/shop/cart.html +++ b/shop/templates/shop/cart.html @@ -35,8 +35,8 @@
Avez-vous un code de réduction?
- - + +
diff --git a/shop/templates/shop/my_orders.html b/shop/templates/shop/my_orders.html index 304fa71..708af24 100644 --- a/shop/templates/shop/my_orders.html +++ b/shop/templates/shop/my_orders.html @@ -61,7 +61,7 @@ {% else %}

Vous n'avez pas encore de commandes.

- Découvrir la boutique + Découvrir la boutique
{% endif %}
From 900bf9865a1cb497f9d8e34ffbedadb9080945ef Mon Sep 17 00:00:00 2001 From: Raz Date: Tue, 6 May 2025 15:07:47 +0200 Subject: [PATCH 012/110] fix title cap for shop --- shop/templates/shop/partials/navigation_base.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shop/templates/shop/partials/navigation_base.html b/shop/templates/shop/partials/navigation_base.html index 1e3278f..f5dc311 100644 --- a/shop/templates/shop/partials/navigation_base.html +++ b/shop/templates/shop/partials/navigation_base.html @@ -8,5 +8,5 @@ {% else %} Se connecter {% endif %} - La Boutique + La boutique From b7a55e46f7d6f21dddf65af4460db4990a2be63c Mon Sep 17 00:00:00 2001 From: Raz Date: Tue, 6 May 2025 21:41:15 +0200 Subject: [PATCH 013/110] add ready status in shop order --- shop/migrations/0028_alter_order_status.py | 18 ++++++++++++++++++ shop/models.py | 3 ++- shop/signals.py | 3 ++- 3 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 shop/migrations/0028_alter_order_status.py diff --git a/shop/migrations/0028_alter_order_status.py b/shop/migrations/0028_alter_order_status.py new file mode 100644 index 0000000..42ea1a7 --- /dev/null +++ b/shop/migrations/0028_alter_order_status.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1 on 2025-05-06 19:39 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('shop', '0027_shippingaddress_alter_order_payment_status_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='order', + name='status', + field=models.CharField(choices=[('PENDING', 'Pending'), ('PAID', 'Paid'), ('SHIPPED', 'Shipped'), ('DELIVERED', 'Delivered'), ('CANCELED', 'Canceled'), ('REFUNDED', 'Refunded'), ('PREPARED', 'Prepared'), ('READY', 'Ready')], default='PENDING', max_length=20), + ), + ] diff --git a/shop/models.py b/shop/models.py index bf51bfe..8af21a6 100644 --- a/shop/models.py +++ b/shop/models.py @@ -10,6 +10,7 @@ class OrderStatus(models.TextChoices): CANCELED = 'CANCELED', 'Canceled' REFUNDED = 'REFUNDED', 'Refunded' PREPARED = 'PREPARED', 'Prepared' + READY = 'READY', 'Ready' class CutChoices(models.IntegerChoices): UNISEX = 0, 'Unisex' @@ -154,7 +155,7 @@ class Order(models.Model): return self.status in [OrderStatus.PENDING, OrderStatus.PAID] def shipping_address_can_be_edited(self): - return self.status in [OrderStatus.PENDING, OrderStatus.PAID, OrderStatus.PREPARED] + return self.status in [OrderStatus.PENDING, OrderStatus.PAID, OrderStatus.PREPARED, OrderStatus.READY] def get_shipping_address(self): """ diff --git a/shop/signals.py b/shop/signals.py index c226a47..93c05d7 100644 --- a/shop/signals.py +++ b/shop/signals.py @@ -70,7 +70,7 @@ def _get_order_details(instance): # Translate statuses status_fr_map = { "PENDING": "EN ATTENTE", "PAID": "PAYÉE", - "SHIPPED": "EXPÉDIÉE", "DELIVERED": "LIVRÉE", "CANCELED": "ANNULÉE", "REFUNDED": "REMBOURSÉE", "PREPARED": "EN COURS DE PRÉPARATION", + "SHIPPED": "EXPÉDIÉE", "DELIVERED": "LIVRÉE", "CANCELED": "ANNULÉE", "REFUNDED": "REMBOURSÉE", "PREPARED": "EN COURS DE PRÉPARATION", "READY": "PRÊT" } payment_status_fr_map = { @@ -241,6 +241,7 @@ Montant payé: {final_price}€""" elif status in [OrderStatus.SHIPPED, OrderStatus.DELIVERED, OrderStatus.CANCELED, OrderStatus.PREPARED, OrderStatus.REFUNDED]: status_message = { OrderStatus.PREPARED: "Votre commande est en cours de préparation.", + OrderStatus.READY: "Votre commande est prête.", OrderStatus.SHIPPED: "Votre commande a été expédiée et est en cours de livraison.", OrderStatus.DELIVERED: "Votre commande a été livrée. Nous espérons que vous apprécierez vos produits !", OrderStatus.REFUNDED: "Votre commande a été annulée et son remboursement est en cours de traitement.", From 97a7543f9e9e06204706b0d932608bd7933f56c4 Mon Sep 17 00:00:00 2001 From: Raz Date: Wed, 7 May 2025 07:18:13 +0200 Subject: [PATCH 014/110] show email in order admin panel --- shop/admin.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/shop/admin.py b/shop/admin.py index 9c61ae7..58dd0ee 100644 --- a/shop/admin.py +++ b/shop/admin.py @@ -47,11 +47,17 @@ class ShippingAddressAdmin(admin.ModelAdmin): @admin.register(Order) class OrderAdmin(admin.ModelAdmin): - list_display = ('id', 'date_ordered', 'status', 'total_price', 'get_shipping_address') + list_display = ('id', 'get_email', 'date_ordered', 'status', 'total_price', 'get_shipping_address') inlines = [OrderItemInline] list_filter = ('status', 'payment_status') readonly_fields = ('shipping_address_details',) + def get_email(self, obj): + if obj.guest_user: + return obj.guest_user.email + else: + return obj.user.email + def get_shipping_address(self, obj): if obj.shipping_address: return f"{obj.shipping_address.street_address}, {obj.shipping_address.city}" From 3b3cf5689684160e487d1f7b2d83dea98971e278 Mon Sep 17 00:00:00 2001 From: Raz Date: Wed, 7 May 2025 07:36:53 +0200 Subject: [PATCH 015/110] add action in admin shop order panel --- shop/admin.py | 52 +++++++++++++++++-- shop/signals.py | 2 +- .../admin/shop/order/change_status.html | 32 ++++++++++++ 3 files changed, 80 insertions(+), 6 deletions(-) create mode 100644 shop/templates/admin/shop/order/change_status.html diff --git a/shop/admin.py b/shop/admin.py index 58dd0ee..0eaf59d 100644 --- a/shop/admin.py +++ b/shop/admin.py @@ -1,10 +1,14 @@ from django.contrib import admin -from django.shortcuts import render -from .models import Product, Color, Size, Order, OrderItem, GuestUser, Coupon, CouponUsage, OrderStatus, ShippingAddress +from django.shortcuts import render, redirect from django.utils.html import format_html from django.urls import path -from django.contrib import admin -from django.shortcuts import redirect +from django.http import HttpResponseRedirect +from django import forms + +from .models import ( + Product, Color, Size, Order, OrderItem, GuestUser, Coupon, CouponUsage, + OrderStatus, ShippingAddress +) @admin.register(Product) class ProductAdmin(admin.ModelAdmin): @@ -45,18 +49,24 @@ class ShippingAddressAdmin(admin.ModelAdmin): list_display = ('street_address', 'city', 'postal_code', 'country') search_fields = ('street_address', 'city', 'postal_code', 'country') +class ChangeOrderStatusForm(forms.Form): + _selected_action = forms.CharField(widget=forms.MultipleHiddenInput) + status = forms.ChoiceField(choices=OrderStatus.choices, label="New Status") + @admin.register(Order) class OrderAdmin(admin.ModelAdmin): list_display = ('id', 'get_email', 'date_ordered', 'status', 'total_price', 'get_shipping_address') inlines = [OrderItemInline] list_filter = ('status', 'payment_status') readonly_fields = ('shipping_address_details',) + actions = ['change_order_status'] def get_email(self, obj): if obj.guest_user: return obj.guest_user.email else: return obj.user.email + get_email.short_description = 'Email' def get_shipping_address(self, obj): if obj.shipping_address: @@ -198,6 +208,38 @@ class OrderAdmin(admin.ModelAdmin): except Exception as e: self.message_user(request, f"Error cancelling order: {str(e)}", level='ERROR') return redirect('admin:shop_order_changelist') + + def change_order_status(self, request, queryset): + """Admin action to change the status of selected orders""" + form = None + + if 'apply' in request.POST: + form = ChangeOrderStatusForm(request.POST) + + if form.is_valid(): + status = form.cleaned_data['status'] + count = 0 + + for order in queryset: + order.status = status + order.save() + count += 1 + + self.message_user(request, f"{count} orders have been updated to status '{OrderStatus(status).label}'.") + return HttpResponseRedirect(request.get_full_path()) + + if not form: + form = ChangeOrderStatusForm(initial={'_selected_action': request.POST.getlist('_selected_action')}) + + context = { + 'title': 'Change Order Status', + 'orders': queryset, + 'form': form, + 'action': 'change_order_status' + } + return render(request, 'admin/shop/order/change_status.html', context) + + change_order_status.short_description = "Change status for selected orders" class GuestUserOrderInline(admin.TabularInline): model = Order @@ -240,4 +282,4 @@ class CouponUsageAdmin(admin.ModelAdmin): list_display = ('coupon', 'user', 'guest_email', 'order', 'used_at') list_filter = ('used_at',) search_fields = ('coupon__code', 'user__username', 'user__email', 'guest_email') - readonly_fields = ('used_at',) + readonly_fields = ('used_at',) \ No newline at end of file diff --git a/shop/signals.py b/shop/signals.py index 93c05d7..3466662 100644 --- a/shop/signals.py +++ b/shop/signals.py @@ -238,7 +238,7 @@ Montant payé: {final_price}€""" } # Order status update email - elif status in [OrderStatus.SHIPPED, OrderStatus.DELIVERED, OrderStatus.CANCELED, OrderStatus.PREPARED, OrderStatus.REFUNDED]: + elif status in [OrderStatus.SHIPPED, OrderStatus.DELIVERED, OrderStatus.CANCELED, OrderStatus.PREPARED, OrderStatus.REFUNDED, OrderStatus.READY]: status_message = { OrderStatus.PREPARED: "Votre commande est en cours de préparation.", OrderStatus.READY: "Votre commande est prête.", diff --git a/shop/templates/admin/shop/order/change_status.html b/shop/templates/admin/shop/order/change_status.html new file mode 100644 index 0000000..8a0cff5 --- /dev/null +++ b/shop/templates/admin/shop/order/change_status.html @@ -0,0 +1,32 @@ +{% extends "admin/base_site.html" %} +{% load i18n l10n admin_urls %} + +{% block content %} +
+

{{ title }}

+
+

Change status for the following {{ orders|length }} orders:

+
    + {% for order in orders %} +
  • {{ order }}
  • + {% endfor %} +
+
+ +
+ {% csrf_token %} + + + {{ form.as_p }} + + {% for obj in orders %} + + {% endfor %} + +
+ + Cancel +
+
+
+{% endblock %} \ No newline at end of file From 7c68762178b358bcffe8f845e5c2144b60817a6e Mon Sep 17 00:00:00 2001 From: Raz Date: Wed, 7 May 2025 13:50:18 +0200 Subject: [PATCH 016/110] add sponsor image model --- api/serializers.py | 16 ++++- api/urls.py | 1 + api/views.py | 32 ++++++++- padelclub_backend/settings.py | 4 ++ padelclub_backend/settings_app.py | 2 +- padelclub_backend/urls.py | 6 ++ requirements.txt | 1 + tournaments/admin.py | 67 ++++++++++++++++++- tournaments/migrations/0120_image.py | 33 +++++++++ ...ptions_remove_image_is_primary_and_more.py | 26 +++++++ tournaments/models/__init__.py | 1 + tournaments/models/image.py | 44 ++++++++++++ .../static/tournaments/css/broadcast.css | 7 ++ tournaments/static/tournaments/css/style.css | 2 - .../tournaments/broadcast/broadcast.html | 13 ++++ .../tournaments/broadcast/broadcast_base.html | 5 +- .../broadcast/broadcasted_auto.html | 15 ++++- .../broadcast/broadcasted_bracket.html | 14 +++- .../broadcast/broadcasted_group_stages.html | 14 +++- .../broadcast/broadcasted_matches.html | 15 ++++- .../broadcast/broadcasted_prog.html | 14 +++- .../broadcast/broadcasted_rankings.html | 13 +++- .../broadcast/broadcasted_summons.html | 13 +++- .../tournaments/tournament_info.html | 53 +++++++++++++++ 24 files changed, 395 insertions(+), 16 deletions(-) create mode 100644 tournaments/migrations/0120_image.py create mode 100644 tournaments/migrations/0121_alter_image_options_remove_image_is_primary_and_more.py create mode 100644 tournaments/models/image.py diff --git a/api/serializers.py b/api/serializers.py index c0b081a..a26b7e6 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -1,6 +1,6 @@ from rest_framework import serializers from tournaments.models.court import Court -from tournaments.models import Club, LiveMatch, TeamScore, Tournament, CustomUser, Event, Round, GroupStage, Match, TeamRegistration, PlayerRegistration, Purchase, FailedApiCall, DateInterval, Log, DeviceToken, UnregisteredTeam, UnregisteredPlayer +from tournaments.models import Club, LiveMatch, TeamScore, Tournament, CustomUser, Event, Round, GroupStage, Match, TeamRegistration, PlayerRegistration, Purchase, FailedApiCall, DateInterval, Log, DeviceToken, UnregisteredTeam, UnregisteredPlayer, Image from django.db.utils import IntegrityError from django.conf import settings @@ -239,3 +239,17 @@ class UnregisteredPlayerSerializer(serializers.ModelSerializer): model = UnregisteredPlayer fields = '__all__' # ['id', 'team_registration_id', 'first_name', 'last_name', 'licence_id', 'rank', 'has_paid'] + +class ImageSerializer(serializers.ModelSerializer): + image_url = serializers.SerializerMethodField() + + def get_image_url(self, obj): + if obj.image: + return self.context['request'].build_absolute_uri(obj.image.url) + return None + + class Meta: + model = Image + fields = ['id', 'title', 'description', 'image', 'image_url', 'uploaded_at', + 'event', 'image_type'] + read_only_fields = ['id', 'uploaded_at', 'image_url'] diff --git a/api/urls.py b/api/urls.py index e054f87..2a73de4 100644 --- a/api/urls.py +++ b/api/urls.py @@ -11,6 +11,7 @@ router.register(r'users', views.UserViewSet) router.register(r'user-names', views.ShortUserViewSet) router.register(r'clubs', views.ClubViewSet) router.register(r'tournaments', views.TournamentViewSet) +router.register(r'images', views.ImageViewSet) router.register(r'events', views.EventViewSet) router.register(r'rounds', views.RoundViewSet) router.register(r'group-stages', views.GroupStageViewSet) diff --git a/api/views.py b/api/views.py index be20311..e17f741 100644 --- a/api/views.py +++ b/api/views.py @@ -1,5 +1,5 @@ -from .serializers import ClubSerializer, CourtSerializer, DateIntervalSerializer, DrawLogSerializer, TournamentSerializer, UserSerializer, EventSerializer, RoundSerializer, GroupStageSerializer, MatchSerializer, TeamScoreSerializer, TeamRegistrationSerializer, PlayerRegistrationSerializer, PurchaseSerializer, ShortUserSerializer, FailedApiCallSerializer, LogSerializer, DeviceTokenSerializer, CustomUserSerializer, UnregisteredTeamSerializer, UnregisteredPlayerSerializer -from tournaments.models import Club, Tournament, CustomUser, Event, Round, GroupStage, Match, TeamScore, TeamRegistration, PlayerRegistration, Court, DateInterval, Purchase, FailedApiCall, Log, DeviceToken, DrawLog, UnregisteredTeam, UnregisteredPlayer +from .serializers import ClubSerializer, CourtSerializer, DateIntervalSerializer, DrawLogSerializer, TournamentSerializer, UserSerializer, EventSerializer, RoundSerializer, GroupStageSerializer, MatchSerializer, TeamScoreSerializer, TeamRegistrationSerializer, PlayerRegistrationSerializer, PurchaseSerializer, ShortUserSerializer, FailedApiCallSerializer, LogSerializer, DeviceTokenSerializer, CustomUserSerializer, UnregisteredTeamSerializer, UnregisteredPlayerSerializer, ImageSerializer +from tournaments.models import Club, Tournament, CustomUser, Event, Round, GroupStage, Match, TeamScore, TeamRegistration, PlayerRegistration, Court, DateInterval, Purchase, FailedApiCall, Log, DeviceToken, DrawLog, UnregisteredTeam, UnregisteredPlayer, Image from rest_framework import viewsets from rest_framework.response import Response @@ -300,6 +300,34 @@ class ShortUserViewSet(viewsets.ModelViewSet): serializer_class = ShortUserSerializer permission_classes = [] # Users are public whereas the other requests are only for logged users +class ImageViewSet(viewsets.ModelViewSet): + """ + Viewset for handling event image uploads and retrieval. + + This allows umpires/organizers to upload images for events from the iOS app, + which can then be displayed on the event pages. + """ + serializer_class = ImageSerializer + queryset = Image.objects.all() + + def get_queryset(self): + queryset = Image.objects.all() + + # Filter by event + event_id = self.request.query_params.get('event_id') + image_type = self.request.query_params.get('image_type') + + if event_id: + queryset = queryset.filter(event_id=event_id) + if image_type: + queryset = queryset.filter(image_type=image_type) + + return queryset + + def perform_create(self, serializer): + serializer.save() + + @api_view(['POST']) @permission_classes([IsAuthenticated]) def process_refund(request, team_registration_id): diff --git a/padelclub_backend/settings.py b/padelclub_backend/settings.py index b6c8262..31e51fb 100644 --- a/padelclub_backend/settings.py +++ b/padelclub_backend/settings.py @@ -147,6 +147,10 @@ USE_L10N = True STATIC_URL = '/static/' STATIC_ROOT = os.path.join(BASE_DIR, 'static/') +# Media files (User uploads) +MEDIA_URL = '/media/' +MEDIA_ROOT = os.path.join(BASE_DIR, 'media/') + # Default primary key field type # https://docs.djangoproject.com/en/4.1/ref/settings/#default-auto-field diff --git a/padelclub_backend/settings_app.py b/padelclub_backend/settings_app.py index e69f82e..c10611e 100644 --- a/padelclub_backend/settings_app.py +++ b/padelclub_backend/settings_app.py @@ -46,7 +46,7 @@ QR_CODE_CACHE_ALIAS = 'qr-code' SYNC_APPS = { 'sync': {}, - 'tournaments': { 'exclude': ['Log', 'FailedApiCall', 'DeviceToken'] } + 'tournaments': { 'exclude': ['Log', 'FailedApiCall', 'DeviceToken', 'Image'] } } STRIPE_CURRENCY = 'eur' diff --git a/padelclub_backend/urls.py b/padelclub_backend/urls.py index 85c8b15..abb883a 100644 --- a/padelclub_backend/urls.py +++ b/padelclub_backend/urls.py @@ -15,6 +15,8 @@ Including another URLconf """ from django.contrib import admin from django.urls import include, path +from django.conf import settings +from django.conf.urls.static import static urlpatterns = [ @@ -27,3 +29,7 @@ urlpatterns = [ path('dj-auth/', include('django.contrib.auth.urls')), ] + +# Serve media files in development +if settings.DEBUG: + urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) diff --git a/requirements.txt b/requirements.txt index cc14798..f0cf5e5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -17,3 +17,4 @@ django-filter==24.3 cryptography==41.0.7 stripe==11.6.0 django-background-tasks==1.2.8 +Pillow==10.2.0 diff --git a/tournaments/admin.py b/tournaments/admin.py index 5cc7cfb..c9f1039 100644 --- a/tournaments/admin.py +++ b/tournaments/admin.py @@ -6,7 +6,7 @@ from django.utils.html import escape from django.urls import reverse from django.utils.safestring import mark_safe -from .models import Club, TeamScore, Tournament, CustomUser, Event, Round, GroupStage, Match, TeamRegistration, PlayerRegistration, Purchase, Court, DateInterval, FailedApiCall, Log, DeviceToken, DrawLog, UnregisteredTeam, UnregisteredPlayer +from .models import Club, TeamScore, Tournament, CustomUser, Event, Round, GroupStage, Match, TeamRegistration, PlayerRegistration, Purchase, Court, DateInterval, FailedApiCall, Log, DeviceToken, DrawLog, UnregisteredTeam, UnregisteredPlayer, Image from .forms import CustomUserCreationForm, CustomUserChangeForm from .filters import TeamScoreTournamentListFilter, MatchTournamentListFilter, SimpleTournamentListFilter, MatchTypeListFilter, SimpleIndexListFilter, StartDateRangeFilter @@ -45,10 +45,39 @@ class CustomUserAdmin(UserAdmin): super().save_model(request, obj, form, change) class EventAdmin(SyncedObjectAdmin): - list_display = ['creation_date', 'name', 'club', 'creator', 'creator_full_name', 'tenup_id'] + list_display = ['creation_date', 'name', 'club', 'creator', 'creator_full_name', 'tenup_id', 'display_images'] list_filter = ['creator', 'tenup_id'] raw_id_fields = ['creator'] ordering = ['-creation_date'] + readonly_fields = ['display_images_preview'] + + fieldsets = [ + (None, {'fields': ['name', 'club', 'creator', 'creation_date', 'tenup_id']}), + ('Images', {'fields': ['display_images_preview'], 'classes': ['collapse']}), + ] + + def display_images(self, obj): + count = obj.images.count() + return count if count > 0 else '-' + display_images.short_description = 'Images' + + def display_images_preview(self, obj): + html = '
' + for image in obj.images.all(): + html += f''' +
+ +

+ {image.title or "Untitled"}
+ Type: {image.get_image_type_display()}
+

+
+ ''' + html += '
' + if not obj.images.exists(): + html = '

No images uploaded for this event.

' + return mark_safe(html) + display_images_preview.short_description = 'Images Preview' class TournamentAdmin(SyncedObjectAdmin): list_display = ['display_name', 'event', 'is_private', 'start_date', 'payment', 'creator'] @@ -157,6 +186,39 @@ class UnregisteredPlayerAdmin(admin.ModelAdmin): list_filter = [] ordering = ['last_name', 'first_name'] +class ImageAdmin(admin.ModelAdmin): + list_display = ['title', 'event', 'image_type', 'order', 'uploaded_at', 'file_size', 'image_preview_small'] + list_filter = ['event', 'image_type', 'uploaded_at'] + search_fields = ['title', 'description', 'event__name'] + ordering = ['order'] + readonly_fields = ['id', 'uploaded_at', 'image_preview', 'file_size'] + raw_id_fields = ['event'] + + def image_preview(self, obj): + if obj.image: + return mark_safe(f'') + return "No Image" + image_preview.short_description = 'Preview' + + def image_preview_small(self, obj): + if obj.image: + return mark_safe(f'') + return "No Image" + image_preview_small.short_description = 'Preview' + + def file_size(self, obj): + if obj.image and hasattr(obj.image, 'size'): + # Convert bytes to KB or MB + size_bytes = obj.image.size + if size_bytes < 1024: + return f"{size_bytes} bytes" + elif size_bytes < 1024 * 1024: + return f"{size_bytes/1024:.1f} KB" + else: + return f"{size_bytes/(1024*1024):.1f} MB" + return "Unknown" + file_size.short_description = 'File Size' + action_flags = { ADDITION: 'Addition', @@ -220,3 +282,4 @@ admin.site.register(DeviceToken, DeviceTokenAdmin) admin.site.register(DrawLog, DrawLogAdmin) admin.site.register(UnregisteredTeam, UnregisteredTeamAdmin) admin.site.register(UnregisteredPlayer, UnregisteredPlayerAdmin) +admin.site.register(Image, ImageAdmin) diff --git a/tournaments/migrations/0120_image.py b/tournaments/migrations/0120_image.py new file mode 100644 index 0000000..8f3f129 --- /dev/null +++ b/tournaments/migrations/0120_image.py @@ -0,0 +1,33 @@ +# Generated by Django 5.1 on 2025-05-07 07:58 + +import django.db.models.deletion +import django.utils.timezone +import tournaments.models.image +import uuid +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('tournaments', '0119_alter_tournament_animation_type'), + ] + + operations = [ + migrations.CreateModel( + name='Image', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('title', models.CharField(blank=True, max_length=255)), + ('description', models.TextField(blank=True)), + ('image', models.ImageField(upload_to=tournaments.models.image.image_upload_path)), + ('uploaded_at', models.DateTimeField(default=django.utils.timezone.now)), + ('image_type', models.CharField(choices=[('sponsor', 'Sponsor'), ('club', 'Club')], default='sponsor', max_length=20)), + ('is_primary', models.BooleanField(default=False)), + ('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='images', to='tournaments.event')), + ], + options={ + 'ordering': ['-uploaded_at'], + }, + ), + ] diff --git a/tournaments/migrations/0121_alter_image_options_remove_image_is_primary_and_more.py b/tournaments/migrations/0121_alter_image_options_remove_image_is_primary_and_more.py new file mode 100644 index 0000000..aeea9a3 --- /dev/null +++ b/tournaments/migrations/0121_alter_image_options_remove_image_is_primary_and_more.py @@ -0,0 +1,26 @@ +# Generated by Django 5.1 on 2025-05-07 11:42 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('tournaments', '0120_image'), + ] + + operations = [ + migrations.AlterModelOptions( + name='image', + options={'ordering': ['order']}, + ), + migrations.RemoveField( + model_name='image', + name='is_primary', + ), + migrations.AddField( + model_name='image', + name='order', + field=models.IntegerField(default=0), + ), + ] diff --git a/tournaments/models/__init__.py b/tournaments/models/__init__.py index 9dd7d03..ee3cccb 100644 --- a/tournaments/models/__init__.py +++ b/tournaments/models/__init__.py @@ -21,3 +21,4 @@ from .device_token import DeviceToken from .draw_log import DrawLog from .unregistered_team import UnregisteredTeam from .unregistered_player import UnregisteredPlayer +from .image import Image diff --git a/tournaments/models/image.py b/tournaments/models/image.py new file mode 100644 index 0000000..227cbcd --- /dev/null +++ b/tournaments/models/image.py @@ -0,0 +1,44 @@ +from django.db import models +import uuid +import os +from django.utils.timezone import now +from .event import Event + +def image_upload_path(instance, filename): + """Generate a unique file path for the uploaded image.""" + # Get the file extension from the original filename + ext = filename.split('.')[-1] + # Create a unique filename using UUID + unique_filename = f"{uuid.uuid4().hex}.{ext}" + + # Determine the folder based on the event + folder = f"event_{instance.event.id}" + + # Return the complete upload path + return os.path.join('images', folder, unique_filename) + +class Image(models.Model): + """Model for storing uploaded images for events.""" + id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) + title = models.CharField(max_length=255, blank=True) + description = models.TextField(blank=True) + image = models.ImageField(upload_to=image_upload_path) + uploaded_at = models.DateTimeField(default=now) + + # Relation to event model + event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name='images') + + # Image type for filtering + IMAGE_TYPES = ( + ('sponsor', 'Sponsor'), + ('club', 'Club'), + ) + image_type = models.CharField(max_length=20, choices=IMAGE_TYPES, default='sponsor') + + order = models.IntegerField(default=0) + + class Meta: + ordering = ['order'] + + def __str__(self): + return self.title or f"Event Image {self.id}" diff --git a/tournaments/static/tournaments/css/broadcast.css b/tournaments/static/tournaments/css/broadcast.css index 2975558..f106b0f 100644 --- a/tournaments/static/tournaments/css/broadcast.css +++ b/tournaments/static/tournaments/css/broadcast.css @@ -22,6 +22,13 @@ body { box-shadow: 0 0 0px 0px #fbead6; } +.bubble-header { + padding: 30px; + background-color: white; + border-radius: 24px; + box-shadow: 0 0 0px 0px #fbead6; +} + .bold { font-family: "Montserrat-Bold"; } diff --git a/tournaments/static/tournaments/css/style.css b/tournaments/static/tournaments/css/style.css index 9820993..fbc523a 100644 --- a/tournaments/static/tournaments/css/style.css +++ b/tournaments/static/tournaments/css/style.css @@ -297,8 +297,6 @@ tr { .logo { height: 80px; - margin: 20px 0px; - /* padding: 5px 10px; */ } .padding-bottom-small { diff --git a/tournaments/templates/tournaments/broadcast/broadcast.html b/tournaments/templates/tournaments/broadcast/broadcast.html index 1fe31fc..f6c1243 100644 --- a/tournaments/templates/tournaments/broadcast/broadcast.html +++ b/tournaments/templates/tournaments/broadcast/broadcast.html @@ -6,6 +6,19 @@ {% block first_title %}{{ tournament.event.display_name }}{% endblock %} {% block second_title %}Broadcast{% endblock %} +{% block sponsors %} + {% if tournament.event.images.exists %} +
+
+ {% for image in tournament.event.images.all %} + {{ image.title|default:'Sponsor' }} + {% endfor %} +
+
+ {% endif %} +{% endblock %} + {% block content %}
diff --git a/tournaments/templates/tournaments/broadcast/broadcast_base.html b/tournaments/templates/tournaments/broadcast/broadcast_base.html index b4667b5..0d3cdce 100644 --- a/tournaments/templates/tournaments/broadcast/broadcast_base.html +++ b/tournaments/templates/tournaments/broadcast/broadcast_base.html @@ -34,7 +34,7 @@