diff --git a/BACKLOG.md b/BACKLOG.md
index abe8eab..1cf6e4e 100755
--- a/BACKLOG.md
+++ b/BACKLOG.md
@@ -8,6 +8,7 @@
### Medium Priority
+- [ ] feat: Promotion code on ticket
- [ ] feat: Promoter system with event creation, ticket types creation and metrics display
- [ ] feat: Multiple ticket types (early bird, VIP, general admission)
- [ ] feat: Refund management system
@@ -53,6 +54,7 @@
## đźš§ Doing
+- [ ] feat: Promotion code on ticket
- [ ] feat: Page to display all tickets for an event
- [ ] feat: Add a link into notification email to order page that display all tickets
diff --git a/app/controllers/orders_controller.rb b/app/controllers/orders_controller.rb
index 9456dd9..f6a6e29 100644
--- a/app/controllers/orders_controller.rb
+++ b/app/controllers/orders_controller.rb
@@ -126,6 +126,20 @@ class OrdersController < ApplicationController
@total_amount = @order.total_amount_cents
@expiring_soon = @order.expiring_soon?
+ # Handle promotion code application
+ if params[:promotion_code].present?
+ promotion_code = PromotionCode.valid.find_by(code: params[:promotion_code].upcase)
+ if promotion_code
+ # Apply the promotion code to the order
+ @order.promotion_codes << promotion_code
+ @order.calculate_total!
+ @total_amount = @order.total_amount_cents
+ flash.now[:notice] = "Code promotionnel appliqué: #{promotion_code.code}"
+ else
+ flash.now[:alert] = "Code promotionnel invalide"
+ end
+ end
+
# For free orders, automatically mark as paid and redirect to success
if @order.free?
@order.mark_as_paid!
diff --git a/app/models/order.rb b/app/models/order.rb
index 3c84bc7..bc7ff56 100644
--- a/app/models/order.rb
+++ b/app/models/order.rb
@@ -7,6 +7,8 @@ class Order < ApplicationRecord
belongs_to :user
belongs_to :event
has_many :tickets, dependent: :destroy
+ has_many :order_promotion_codes, dependent: :destroy
+ has_many :promotion_codes, through: :order_promotion_codes
# === Validations ===
validates :user_id, presence: true
diff --git a/app/models/order_promotion_code.rb b/app/models/order_promotion_code.rb
new file mode 100644
index 0000000..9effdee
--- /dev/null
+++ b/app/models/order_promotion_code.rb
@@ -0,0 +1,26 @@
+class OrderPromotionCode < ApplicationRecord
+ # Associations
+ belongs_to :order
+ belongs_to :promotion_code
+
+ # Validations
+ validates :order, presence: true
+ validates :promotion_code, presence: true
+
+ # Callbacks
+ after_create :apply_discount
+ after_create :increment_promotion_code_uses
+
+ private
+
+ def apply_discount
+ # Apply the discount to the order
+ discount_amount = promotion_code.discount_amount_cents
+ order.update!(total_amount_cents: [ order.total_amount_cents - discount_amount, 0 ].max)
+ end
+
+ def increment_promotion_code_uses
+ # Increment the uses count on the promotion code
+ promotion_code.increment!(:uses_count)
+ end
+end
diff --git a/app/models/promotion_code.rb b/app/models/promotion_code.rb
new file mode 100644
index 0000000..4eb56fc
--- /dev/null
+++ b/app/models/promotion_code.rb
@@ -0,0 +1,23 @@
+class PromotionCode < ApplicationRecord
+ # Validations
+ validates :code, presence: true, uniqueness: true
+ validates :discount_amount_cents, numericality: { greater_than_or_equal_to: 0 }
+
+ # Scopes
+ scope :active, -> { where(active: true) }
+ scope :expired, -> { where("expires_at < ? OR active = ?", Time.current, false) }
+ scope :valid, -> { active.where("expires_at > ? OR expires_at IS NULL", Time.current) }
+
+ # Callbacks
+ before_create :increment_uses_count
+
+ # Associations
+ has_many :order_promotion_codes
+ has_many :orders, through: :order_promotion_codes
+
+ private
+
+ def increment_uses_count
+ self.uses_count ||= 0
+ end
+end
diff --git a/app/views/orders/checkout.html.erb b/app/views/orders/checkout.html.erb
index 73fd9c8..bdedda7 100644
--- a/app/views/orders/checkout.html.erb
+++ b/app/views/orders/checkout.html.erb
@@ -118,6 +118,16 @@
Procédez au paiement pour finaliser votre commande
+
+ <%= form_tag checkout_order_path(@order), method: :get, class: "mb-6" do %>
+
+ <%= text_field_tag :promotion_code, params[:promotion_code], class: "flex-1 border-none bg-transparent focus:ring-0 text-sm", placeholder: "Code promotionnel (optionnel)" %>
+ <%= button_tag type: "submit", class: "ml-2 btn btn-secondary py-2 px-4 text-sm" do %>
+ Appliquer
+ <% end %>
+
+ <% end %>
+
<% if @checkout_session.present? %>
@@ -131,13 +141,13 @@
-