Skip to content

Commit

Permalink
Add new Kenshis::CalculatePurchasesService to calculate purchases for…
Browse files Browse the repository at this point in the history
… a kenshi

depending on the products he bought and the categories of the cup he is
participating in.

fix #324
  • Loading branch information
yannis committed Jan 7, 2024
1 parent e0f1c50 commit b5d2f17
Show file tree
Hide file tree
Showing 20 changed files with 282 additions and 124 deletions.
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ group :development do
gem "brakeman"
gem "i18n-tasks"
gem "letter_opener_web"
gem "pg_query"
gem "prosopite"
gem "solargraph"
gem "rack-mini-profiler"
gem "ruby-lsp"
Expand Down
6 changes: 6 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ GEM
formtastic_i18n (0.7.0)
globalid (1.2.1)
activesupport (>= 6.1)
google-protobuf (3.25.1)
has_scope (0.8.2)
actionpack (>= 5.2)
activesupport (>= 5.2)
Expand Down Expand Up @@ -323,12 +324,15 @@ GEM
racc
pdf-core (0.9.0)
pg (1.5.4)
pg_query (5.0.0)
google-protobuf (>= 3.22.3)
prawn (2.4.0)
pdf-core (~> 0.9.0)
ttfunk (~> 1.7)
prawn-table (0.2.2)
prawn (>= 1.3.0, < 3.0.0)
prism (0.19.0)
prosopite (1.4.2)
psych (5.1.2)
stringio
public_suffix (5.0.4)
Expand Down Expand Up @@ -613,8 +617,10 @@ DEPENDENCIES
matrix
parallel_tests
pg (~> 1.5)
pg_query
prawn
prawn-table
prosopite
puma (~> 6.4)
rack-cors
rack-mini-profiler
Expand Down
11 changes: 7 additions & 4 deletions app/admin/cup.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ def find_resource
column :product_individual_junior do |cup|
cup.product_individual_junior&.name
end
column :product_adult do |cup|
cup.product_adult&.name
column :product_individual_adult do |cup|
cup.product_individual_adult&.name
end
column :description do |cup|
md_to_html(cup.description)
Expand Down Expand Up @@ -85,8 +85,11 @@ def find_resource
[:admin, cup.product_individual_junior]
end
end
row :product_adult do |cup|
link_to cup.product_adult&.name, [:admin, cup.product_adult] if cup.product_adult
row :product_individual_adult do |cup|
if cup.product_individual_adult
link_to cup.product_individual_adult&.name,
[:admin, cup.product_individual_adult]
end
end
row :description_en
row :description_fr
Expand Down
10 changes: 10 additions & 0 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@ class ApplicationController < ActionController::Base
include ActiveStorage::SetCurrent

before_action :set_current_cup
if Rails.env.development?
around_action :n_plus_one_detection

private def n_plus_one_detection
Prosopite.scan
yield
ensure
Prosopite.finish
end
end

rescue_from CanCan::AccessDenied do |exception|
if current_user.present?
Expand Down
3 changes: 3 additions & 0 deletions app/models/cup.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
class Cup < ApplicationRecord
belongs_to :product_individual_junior, class_name: "Product", optional: true
belongs_to :product_individual_adult, class_name: "Product", optional: true
belongs_to :product_team, class_name: "Product", optional: true
belongs_to :product_full_junior, class_name: "Product", optional: true
belongs_to :product_full_adult, class_name: "Product", optional: true
has_many :kenshis, inverse_of: :cup, dependent: :destroy
has_many :participations, through: :kenshis
has_many :individual_categories, inverse_of: :cup, dependent: :destroy
Expand Down
5 changes: 5 additions & 0 deletions app/models/kenshi.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class Kenshi < ApplicationRecord
before_validation :format
after_validation :logs
after_create_commit :notify_slack
after_commit :update_purchase

def self.from(user)
new(
Expand Down Expand Up @@ -147,4 +148,8 @@ def fitness
notification = Slack::Notifications::Registration.new(self)
Slack::NotificationService.new.call(notification: notification)
end

private def update_purchase
Kenshis::CalculatePurchasesService.new(kenshi: self).call
end
end
11 changes: 1 addition & 10 deletions app/models/participation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ class Participation < ApplicationRecord

attr_writer :category_individual, :category_team
belongs_to :category, polymorphic: true, autosave: true
belongs_to :kenshi, inverse_of: :participations
belongs_to :kenshi, inverse_of: :participations, touch: true
belongs_to :team, optional: true

validates :pool_position, presence: {if: lambda { |p| p.pool_number.present? }}
Expand All @@ -16,7 +16,6 @@ class Participation < ApplicationRecord
validate :category_age

before_validation :assign_category
after_commit :update_purchase

delegate :full_name, to: "kenshi", allow_nil: true
delegate :grade, to: "kenshi", allow_nil: true
Expand Down Expand Up @@ -92,14 +91,6 @@ def descriptive_name
errors.add(:category, "can't have both an individual and a team category")
end

private def update_purchase
if destroyed?
purchase&.destroy!
elsif purchase.nil? && product.present?
kenshi.purchases.create!(product: product)
end
end

private def category_age
return unless kenshi && category

Expand Down
55 changes: 55 additions & 0 deletions app/services/kenshis/calculate_purchases_service.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# frozen_string_literal: true

module Kenshis
class CalculatePurchasesService
def initialize(kenshi:)
@kenshi = kenshi
@cup = @kenshi.cup
end

def call
purchases = @kenshi.purchases
participations = @kenshi.participations

products = []
participation_types = participations.pluck(:category_type).uniq
participations.find_each do |participation|
if participation_types.count > 1
purchases.where(product: participation_product(participation)).destroy_all
product = participation_full_product(participation)
else
product = participation_product(participation)
end
next unless product

purchases.find_or_create_by!(product: product)
products << product
end
cup_products = [
@cup.product_individual_junior_id, @cup.product_individual_adult_id, @cup.product_team_id,
@cup.product_full_junior_id, @cup.product_full_adult_id
]
purchases.where(product: cup_products).where.not(product: products).destroy_all
end

private def participation_product(participation)
if participation.category.is_a? TeamCategory
@cup.product_team
elsif participation.category.is_a? IndividualCategory
if @kenshi.junior?
@cup.product_individual_junior
else
@cup.product_individual_adult
end
end
end

private def participation_full_product(participation)
if @kenshi.junior?
@cup.product_full_junior
else
@cup.product_full_adult
end
end
end
end
2 changes: 1 addition & 1 deletion app/views/kenshis/_form.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@
</div>
</div>
<div class="mt-4 sm:mt-0 sm:col-span-2">
<% products.each do |p| %>
<% products.includes(:purchases).each do |p| %>
<% price = (p.fee_chf == 0) ? t("cups.show.fees.variable") : "#{p.fee_chf} CHF/#{p.fee_eu} €" # rubocop:disable Lint/UselessAssignment %>
<div class="max-w-lg mb-4">
<% purchase = kenshi.purchases.to_a.find { |purch| purch.product_id == p.id } # rubocop:disable Lint/UselessAssignment %>
Expand Down
2 changes: 2 additions & 0 deletions config/environments/development.rb
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,6 @@

# Raise error when a before_action's only/except options reference missing actions
config.action_controller.raise_on_missing_callback_actions = true

Prosopite.rails_logger = true
end
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@

class AddAndRenameProductReferencesToCups < ActiveRecord::Migration[7.1]
def change
add_reference :cups, :product_team_junior, foreign_key: { to_table: :products }, null: true
add_reference :cups, :product_team_adult, foreign_key: { to_table: :products }, null: true
add_reference :cups, :product_team, foreign_key: { to_table: :products }, null: true
add_reference :cups, :product_full_junior, foreign_key: { to_table: :products }, null: true
add_reference :cups, :product_full_adult, foreign_key: { to_table: :products }, null: true

Expand Down
9 changes: 3 additions & 6 deletions db/schema.rb

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 18 additions & 5 deletions lib/tasks/temporary/cups/add_2024.rake
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,8 @@ namespace :temporary do
]

ActiveRecord::Base.transaction do
cup = Cup.find_by(start_on: "2024-09-28")
cup = Cup.find_or_create_by(start_on: "2024-09-28", end_on: "2024-09-29", year: 2024,
deadline: "2024-15-01", registerable_at: "2024-05-01 00:00:00")
create_header_image(cup: cup, image: "kasa-2024.jpeg")
events_data.each do |event_data|
cup.events.create!(event_data)
Expand All @@ -246,10 +247,22 @@ namespace :temporary do
team_categories_data.each do |team_category_data|
cup.team_categories.create!(team_category_data)
end
product_individual_junior = cup.products.find_by(name_en: "Participation Junior (17 years old and younger)")
product_individual_adult = cup.products.find_by(name_en: "Participation Adult (18 years old and older)")
cup.update!(product_individual_junior: product_individual_junior,
product_individual_adult: product_individual_adult)
product_team = cup.products.find_by!(name_en: "Participation Team")
product_individual_junior = cup.products
.find_by!(name_en: "Participation Individuals Junior (17 years old and younger)")
product_individual_adult = cup.products
.find_by!(name_en: "Participation Individuals Adult (18 years old and older)")
product_full_junior = cup.products
.find_by!(name_en: "Participation 2 Days Junior (17 years old and younger)")
product_full_adult = cup
.products.find_by!(name_en: "Participation 2 Days Adult (18 years old and older)")
cup.update!(
product_individual_junior: product_individual_junior,
product_individual_adult: product_individual_adult,
product_team: product_team,
product_full_junior: product_full_junior,
product_full_adult: product_full_adult
)
end
end
end
Expand Down
8 changes: 8 additions & 0 deletions spec/factories/cups.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,13 @@
image_path = Rails.root.glob("spec/fixtures/images/*.jpg").sample
Rack::Test::UploadedFile.new(image_path)
end

trait :with_cup_products do
product_individual_junior { association(:product) }
product_individual_adult { association(:product) }
product_team { association(:product) }
product_full_junior { association(:product) }
product_full_adult { association(:product) }
end
end
end
3 changes: 3 additions & 0 deletions spec/models/cup_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
it do
expect(cup).to belong_to(:product_individual_junior).class_name("Product").optional
expect(cup).to belong_to(:product_individual_adult).class_name("Product").optional
expect(cup).to belong_to(:product_team).class_name("Product").optional
expect(cup).to belong_to(:product_full_junior).class_name("Product").optional
expect(cup).to belong_to(:product_full_adult).class_name("Product").optional
expect(cup).to have_many(:individual_categories).dependent(:destroy)
expect(cup).to have_many(:team_categories).dependent(:destroy)
expect(cup).to have_many(:events).dependent :destroy
Expand Down
Loading

0 comments on commit b5d2f17

Please sign in to comment.