Skip to content

Commit

Permalink
feature: add support for minimum_prices to tarifs
Browse files Browse the repository at this point in the history
  • Loading branch information
diegosteiner committed Sep 30, 2024
1 parent ff202fa commit 5ad7bc8
Show file tree
Hide file tree
Showing 18 changed files with 275 additions and 44 deletions.
14 changes: 8 additions & 6 deletions app/models/tarif.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
# associated_types :integer default(0), not null
# discarded_at :datetime
# label_i18n :jsonb
# minimum_price_per_night :decimal(, )
# minimum_price_total :decimal(, )
# minimum_usage_per_night :decimal(, )
# minimum_usage_total :decimal(, )
# ordinal :integer
Expand Down Expand Up @@ -151,20 +153,20 @@ def update_booking_conditions
selecting_conditions.each { |condition| condition.assign_attributes(qualifiable: self, group: :selecting) }
end

def minimum_prices(usage)
def minimum_prices(usage) # rubocop:disable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
nights = usage&.booking&.nights || 0
price_per_unit = usage&.price_per_unit || 0

{
minimum_usage_per_night: (minimum_usage_per_night || 0) * nights * price_per_unit,
minimum_usage_total: (minimum_usage_total || 0) * price_per_unit
# minimum_price_per_night: (minimum_price_per_night || 0) * nights,
# minimum_price_total: minimum_price_total || 0
minimum_usage_per_night: (minimum_usage_per_night || 0) * price_per_unit * nights,
minimum_usage_total: (minimum_usage_total || 0) * price_per_unit,
minimum_price_per_night: (minimum_price_per_night || 0) * nights,
minimum_price_total: minimum_price_total || 0
}
end

def minimum_price(usage)
minimum_prices(usage).values.compact.max
minimum_prices(usage).filter { _2.positive? }.max_by { _2 }
end

def apply_usage_to_invoice?(_usage, _invoice)
Expand Down
2 changes: 2 additions & 0 deletions app/models/tarifs/amount.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
# associated_types :integer default(0), not null
# discarded_at :datetime
# label_i18n :jsonb
# minimum_price_per_night :decimal(, )
# minimum_price_total :decimal(, )
# minimum_usage_per_night :decimal(, )
# minimum_usage_total :decimal(, )
# ordinal :integer
Expand Down
2 changes: 2 additions & 0 deletions app/models/tarifs/flat.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
# associated_types :integer default(0), not null
# discarded_at :datetime
# label_i18n :jsonb
# minimum_price_per_night :decimal(, )
# minimum_price_total :decimal(, )
# minimum_usage_per_night :decimal(, )
# minimum_usage_total :decimal(, )
# ordinal :integer
Expand Down
20 changes: 19 additions & 1 deletion app/models/tarifs/group_minimum.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
# associated_types :integer default(0), not null
# discarded_at :datetime
# label_i18n :jsonb
# minimum_price_per_night :decimal(, )
# minimum_price_total :decimal(, )
# minimum_usage_per_night :decimal(, )
# minimum_usage_total :decimal(, )
# ordinal :integer
Expand Down Expand Up @@ -52,8 +54,24 @@ def group_price(usage)
usages_in_group(usage).sum(&:price)
end

def group_used_units(usage)
usages_in_group(usage).sum(&:used_units)
end

def minimum_prices_with_difference(usage) # rubocop:disable Metrics/AbcSize
nights = usage&.booking&.nights || 0
price_per_unit = usage&.price_per_unit || 0
minimum_prices = minimum_prices(usage)
{
minimum_usage_per_night: (((minimum_usage_per_night || 0) * nights) - group_used_units(usage)) * price_per_unit,
minimum_usage_total: ((minimum_usage_total || 0) - group_used_units(usage)) * price_per_unit,
minimum_price_per_night: minimum_prices[:minimum_price_per_night] - group_price(usage),
minimum_price_total: minimum_prices[:minimum_price_total] - group_price(usage)
}
end

def minimum_price(usage)
[minimum_prices(usage).values.max - group_price(usage), 0].max
minimum_prices_with_difference(usage).filter { _2.positive? }.max_by { _2 }
end

def apply_usage_to_invoice?(usage, _invoice)
Expand Down
2 changes: 2 additions & 0 deletions app/models/tarifs/metered.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
# associated_types :integer default(0), not null
# discarded_at :datetime
# label_i18n :jsonb
# minimum_price_per_night :decimal(, )
# minimum_price_total :decimal(, )
# minimum_usage_per_night :decimal(, )
# minimum_usage_total :decimal(, )
# ordinal :integer
Expand Down
2 changes: 2 additions & 0 deletions app/models/tarifs/overnight_stay.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
# associated_types :integer default(0), not null
# discarded_at :datetime
# label_i18n :jsonb
# minimum_price_per_night :decimal(, )
# minimum_price_total :decimal(, )
# minimum_usage_per_night :decimal(, )
# minimum_usage_total :decimal(, )
# ordinal :integer
Expand Down
2 changes: 2 additions & 0 deletions app/models/tarifs/price.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
# associated_types :integer default(0), not null
# discarded_at :datetime
# label_i18n :jsonb
# minimum_price_per_night :decimal(, )
# minimum_price_total :decimal(, )
# minimum_usage_per_night :decimal(, )
# minimum_usage_total :decimal(, )
# ordinal :integer
Expand Down
8 changes: 2 additions & 6 deletions app/models/usage.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,23 +48,19 @@ class Usage < ApplicationRecord
validates :used_units, numericality: true, allow_nil: true

def price
@price ||= ([usage_price, minimum_price].flatten.compact.max * 20.0).floor / 20.0
@price ||= ([usage_price, minimum_price&.last].flatten.compact.max * 20.0).floor / 20.0
end

def usage_price
(used_units || 0.0) * (price_per_unit || 1.0)
end

def minimum_prices
tarif&.minimum_prices(self)
end

def minimum_price
tarif&.minimum_price(self)
end

def minimum_price?
price.positive? && price == minimum_price
price.positive? && price == minimum_price&.last
end

def presumed_units
Expand Down
4 changes: 2 additions & 2 deletions app/params/manage/tarif_params.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ module Manage
class TarifParams < ApplicationParams
def self.permitted_keys
%i[type label unit price_per_unit ordinal tarif_group accountancy_account pin
prefill_usage_method prefill_usage_booking_question_id
minimum_usage_per_night minimum_usage_total vat] +
prefill_usage_method prefill_usage_booking_question_id vat
minimum_usage_per_night minimum_usage_total minimum_price_per_night minimum_price_total] +
I18n.available_locales.map { |locale| ["label_#{locale}", "unit_#{locale}"] }.flatten +
[{ associated_types: [],
selecting_conditions_attributes: BookingConditionParams.permitted_keys + %i[id _destroy],
Expand Down
2 changes: 2 additions & 0 deletions app/views/manage/tarifs/_form.html.slim
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
span.fa.fa-thermometer.text-danger.ms-2
= f.text_field :minimum_usage_per_night, step: 0.01, inputmode: "numeric"
= f.text_field :minimum_usage_total, step: 0.01, inputmode: "numeric"
= f.text_field :minimum_price_per_night, step: 0.01, inputmode: "numeric"
= f.text_field :minimum_price_total, step: 0.01, inputmode: "numeric"

fieldset
= f.collection_check_boxes :associated_types, Tarif.associated_types.keys, :itself, ->(key) { Tarif::ASSOCIATED_TYPES[key]&.model_name&.human }
Expand Down
6 changes: 2 additions & 4 deletions app/views/manage/tarifs/_table.html.slim
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
th
th
th= Tarif.human_attribute_name(:label)
th= Tarif.human_attribute_name(:type)
th= Tarif.human_attribute_name(:price_per_unit)
th= Tarif.human_attribute_name(:unit)
th
Expand Down Expand Up @@ -35,12 +34,11 @@
= tf.hidden_field :id
td
p.mb-0 = tarif.label
td
= tf.select :type, subtype_options_for_select(Tarif.subtypes), { skip_label: true, wrapper: false }, { disabled: true }
td
= tf.text_field :price_per_unit, skip_label: true, wrapper: false, disabled: tarif.discarded?, inputmode: "numeric"
td
= tf.static_control :unit, skip_label: true, wrapper: false
= tarif.unit
small.d-block.text-secondary= tarif.class.model_name.human
td.text-end
.btn-group
- unless tarif.discarded?
Expand Down
87 changes: 78 additions & 9 deletions app/views/renderables/tarifs/group_minimum/_usage_form.html.slim
Original file line number Diff line number Diff line change
@@ -1,13 +1,82 @@
= render layout: 'manage/tarifs/usage_form', locals: { usage:, f: }
- f.object.valid?
- usage.valid? # trigger #before_validate
.d-flex.gap-3.justify-content-end
div
= number_to_currency(f.object.tarif.minimum_prices(f.object).values.max, unit: '')
small.d-block.text-secondary= @booking.organisation.currency
- case usage.minimum_price&.first
- when :minimum_usage_per_night
div
| (

div
| -
div
= number_with_delimiter(usage.tarif.minimum_usage_per_night * usage.booking.nights)

div
| -

div
= number_with_delimiter(usage.tarif.group_used_units(usage))

div
| )

div
| ×

div
= number_to_currency(usage.price_per_unit, unit: '')
small.d-block.text-secondary= @booking.organisation.currency

div
| =

- when :minimum_usage_total
div
| (

div
= number_with_delimiter(usage.tarif.minimum_usage_total)

div
| -

div
= number_with_delimiter(usage.tarif.group_used_units(usage))

div
| )

div
| ×

div
= number_to_currency(usage.price_per_unit, unit: '')
small.d-block.text-secondary= @booking.organisation.currency

div
| =

- when :minimum_price_per_night
div
= number_to_currency(usage.tarif.minimum_price_per_night * usage.booking.nights)

div
| -

div
= number_to_currency(usage.tarif.group_price(usage))

div
| =

- when :minimum_price_total
div
= number_to_currency(usage.tarif.minimum_price_total)

div
| -

div
= number_to_currency(usage.tarif.group_price(usage))

div
| =

div
= number_to_currency(f.object.tarif.group_price(f.object), unit: '')
small.d-block.text-secondary= @booking.organisation.currency
2 changes: 2 additions & 0 deletions config/locales/de.yml
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,8 @@ de:
enabling_conditions: Bedingungen für erlaubte Auswahl
label: Tarifbezeichnung
meter: Name des Zählers (bei Tarifen nach Zählerstand)
minimum_price_per_night: Minimaler Preis (pro Nacht)
minimum_price_total: Minimaler Preis (Total)
minimum_usage_per_night: Minimaler Verbrauch (pro Nacht)
minimum_usage_total: Minimaler Verbrauch (Total)
ordinal_position: Ordnungsnummer
Expand Down
6 changes: 6 additions & 0 deletions db/migrate/20240930094421_add_minimum_price_to_tarifs.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class AddMinimumPriceToTarifs < ActiveRecord::Migration[7.2]
def change
add_column :tarifs, :minimum_price_per_night, :decimal, null: true
add_column :tarifs, :minimum_price_total, :decimal, null: true
end
end
4 changes: 3 additions & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema[7.2].define(version: 2024_09_23_110730) do
ActiveRecord::Schema[7.2].define(version: 2024_09_30_094421) do
# These are extensions that must be enabled in order to support this database
enable_extension "pgcrypto"
enable_extension "plpgsql"
Expand Down Expand Up @@ -540,6 +540,8 @@
t.datetime "discarded_at"
t.decimal "vat"
t.bigint "prefill_usage_booking_question_id"
t.decimal "minimum_price_per_night"
t.decimal "minimum_price_total"
t.index ["discarded_at"], name: "index_tarifs_on_discarded_at"
t.index ["organisation_id"], name: "index_tarifs_on_organisation_id"
t.index ["prefill_usage_booking_question_id"], name: "index_tarifs_on_prefill_usage_booking_question_id"
Expand Down
7 changes: 5 additions & 2 deletions spec/factories/tarifs.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
# associated_types :integer default(0), not null
# discarded_at :datetime
# label_i18n :jsonb
# minimum_price_per_night :decimal(, )
# minimum_price_total :decimal(, )
# minimum_usage_per_night :decimal(, )
# minimum_usage_total :decimal(, )
# ordinal :integer
Expand Down Expand Up @@ -40,8 +42,9 @@
#

FactoryBot.define do
factory :tarif, class: Tarifs::Amount.to_s do
# type Tarifs::Amount
factory :tarif do
type { Tarifs::Amount.sti_name }
initialize_with { type.constantize.new }
label { 'Preis pro Übernachtung' }
unit { 'Übernachtung (unter 16 Jahren)' }
price_per_unit { 15.0 }
Expand Down
Loading

0 comments on commit 5ad7bc8

Please sign in to comment.