1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-01-18 07:52:23 +01:00

(feat) usage history of prepaid packs

This commit is contained in:
Sylvain 2023-03-09 13:23:42 +01:00
parent ed3353ce7c
commit 9b4c47d733
13 changed files with 86 additions and 13 deletions

View File

@ -1,6 +1,7 @@
# Changelog Fab-manager
- Improved upgrade script
- Keep usage history of prepaid packs
- OpenAPI reservation endpoint can be filtered by date
- OpenAPI users endpoint now returns the ID of the InvoicingProfile
- Fix a bug: wrong counting of minutes used when using a prepaid pack

View File

@ -6,6 +6,9 @@ class API::UserPacksController < API::ApiController
def index
@user_packs = PrepaidPackService.user_packs(user, item)
@history = params[:history] == 'true'
@user_packs = @user_packs.includes(:prepaid_pack_reservations) if @history
end
private

View File

@ -41,7 +41,7 @@ const PrepaidPacksPanel: React.FC<PrepaidPacksPanelProps> = ({ user, onError })
const { handleSubmit, control, formState } = useForm<{ machine_id: number }>();
useEffect(() => {
UserPackAPI.index({ user_id: user.id })
UserPackAPI.index({ user_id: user.id, history: true })
.then(setUserPacks)
.catch(onError);
SettingAPI.get('renew_pack_threshold')
@ -106,7 +106,7 @@ const PrepaidPacksPanel: React.FC<PrepaidPacksPanelProps> = ({ user, onError })
*/
const onPackBoughtSuccess = () => {
togglePacksModal();
UserPackAPI.index({ user_id: user.id })
UserPackAPI.index({ user_id: user.id, history: true })
.then(setUserPacks)
.catch(onError);
};
@ -128,16 +128,18 @@ const PrepaidPacksPanel: React.FC<PrepaidPacksPanelProps> = ({ user, onError })
<p className="countdown"><span>{(pack.prepaid_pack.minutes - pack.minutes_used) / 60}H</span> / {pack.prepaid_pack.minutes / 60}H</p>
</div>
</div>
{ /* usage history is not saved for now
{pack.history?.length > 0 &&
<div className="prepaid-packs-list is-history">
<span className='prepaid-packs-list-label'>{t('app.logged.dashboard.reservations_dashboard.prepaid_packs_panel.history')}</span>
<div className='prepaid-packs-list-item'>
<p className='name'>00{t('app.logged.dashboard.reservations_dashboard.prepaid_packs_panel.consumed_hours')}</p>
<p className="date">00/00/00</p>
</div>
{pack.history.map(prepaidReservation => (
<div className='prepaid-packs-list-item' key={prepaidReservation.id}>
<p className='name'>{t('app.logged.dashboard.reservations_dashboard.prepaid_packs_panel.consumed_hours', { COUNT: prepaidReservation.consumed_minutes / 60 })}</p>
<p className="date">{FormatLib.date(prepaidReservation.reservation_date)}</p>
</div>
))}
</div>
*/ }
}
</div>
))}

View File

@ -4,7 +4,8 @@ import { ApiFilter } from './api';
export interface UserPackIndexFilter extends ApiFilter {
user_id: number,
priceable_type?: string,
priceable_id?: number
priceable_id?: number,
history?: boolean
}
export interface UserPack {
@ -17,5 +18,11 @@ export interface UserPack {
priceable: {
name: string
}
}
},
history?: Array<{
id: number,
consumed_minutes: number,
reservation_id: number,
reservation_date: TDateISO
}>
}

View File

@ -0,0 +1,7 @@
# frozen_string_literal: true
# Save the association between a Reservation and a PrepaidPack to keep the usage history.
class PrepaidPackReservation < ApplicationRecord
belongs_to :statistic_profile_prepaid_pack
belongs_to :reservation
end

View File

@ -21,6 +21,8 @@ class Reservation < ApplicationRecord
has_many :invoice_items, as: :object, dependent: :destroy
has_one :payment_schedule_object, as: :object, dependent: :destroy
has_many :prepaid_pack_reservations, dependent: :destroy
validates :reservable_id, :reservable_type, presence: true
validate :machine_not_already_reserved, if: -> { reservable.is_a?(Machine) }
validate :training_not_fully_reserved, if: -> { reservable.is_a?(Training) }

View File

@ -8,6 +8,7 @@ class StatisticProfilePrepaidPack < ApplicationRecord
has_many :invoice_items, as: :object, dependent: :destroy
has_one :payment_schedule_object, as: :object, dependent: :destroy
has_many :prepaid_pack_reservations, dependent: :restrict_with_error
before_create :set_expiration_date

View File

@ -7,6 +7,7 @@ class PrepaidPackService
# @option filters [Integer] :group_id
# @option filters [Integer] :priceable_id
# @option filters [String] :priceable_type 'Machine' | 'Space'
# @return [ActiveRecord::Relation<PrepaidPack>]
def list(filters)
packs = PrepaidPack.where(nil)
@ -25,6 +26,7 @@ class PrepaidPackService
# return the not expired packs for the given item bought by the given user
# @param user [User]
# @param priceable [Machine,Space,NilClass]
# @return [ActiveRecord::Relation<StatisticProfilePrepaidPack>]
def user_packs(user, priceable = nil)
sppp = StatisticProfilePrepaidPack.includes(:prepaid_pack)
.references(:prepaid_packs)
@ -65,6 +67,7 @@ class PrepaidPackService
pack_consumed = consumed > pack_available ? pack_available : consumed
consumed -= pack_consumed
PrepaidPackReservation.create!(statistic_profile_prepaid_pack: pack, reservation: reservation, consumed_minutes: pack_consumed)
end
end

View File

@ -8,4 +8,10 @@ json.array!(@user_packs) do |user_pack|
json.extract! user_pack.prepaid_pack.priceable, :name
end
end
if @history
json.history user_pack.prepaid_pack_reservations do |ppr|
json.extract! ppr, :id, :consumed_minutes, :reservation_id
json.reservation_date ppr.reservation.created_at
end
end
end

View File

@ -166,7 +166,7 @@ en:
end: "Expiry date"
countdown: "Countdown"
history: "History"
consumed_hours: "H consumed"
consumed_hours: "{COUNT, plural, =1{1H consumed} other{{COUNT}H consumed}}"
cta_info: "You can buy prepaid hours packs to book machines and benefit from discounts. Choose a machine to buy a corresponding pack."
select_machine: "Select a machine"
cta_button: "Buy a pack"

View File

@ -0,0 +1,15 @@
# frozen_string_literal: true
# From this migration we save the association between a Reservation and a PrepaidPack to keep the
# usage history.
class CreatePrepaidPackReservations < ActiveRecord::Migration[5.2]
def change
create_table :prepaid_pack_reservations do |t|
t.references :statistic_profile_prepaid_pack, foreign_key: true, index: { name: 'index_prepaid_pack_reservations_on_sp_prepaid_pack_id' }
t.references :reservation, foreign_key: true
t.integer :consumed_minutes
t.timestamps
end
end
end

View File

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2023_03_02_120458) do
ActiveRecord::Schema.define(version: 2023_03_09_094535) do
# These are extensions that must be enabled in order to support this database
enable_extension "fuzzystrmatch"
@ -739,6 +739,17 @@ ActiveRecord::Schema.define(version: 2023_03_02_120458) do
t.text "description"
end
create_table "plan_limitations", force: :cascade do |t|
t.bigint "plan_id"
t.string "limitable_type"
t.bigint "limitable_id"
t.integer "limit", default: 0, null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["limitable_type", "limitable_id"], name: "index_plan_limitations_on_limitable_type_and_limitable_id"
t.index ["plan_id"], name: "index_plan_limitations_on_plan_id"
end
create_table "plans", id: :serial, force: :cascade do |t|
t.string "name"
t.integer "amount"
@ -758,6 +769,7 @@ ActiveRecord::Schema.define(version: 2023_03_02_120458) do
t.boolean "disabled"
t.boolean "monthly_payment"
t.bigint "plan_category_id"
t.boolean "limiting"
t.index ["group_id"], name: "index_plans_on_group_id"
t.index ["plan_category_id"], name: "index_plans_on_plan_category_id"
end
@ -769,6 +781,16 @@ ActiveRecord::Schema.define(version: 2023_03_02_120458) do
t.index ["plan_id"], name: "index_plans_availabilities_on_plan_id"
end
create_table "prepaid_pack_reservations", force: :cascade do |t|
t.bigint "statistic_profile_prepaid_pack_id"
t.bigint "reservation_id"
t.integer "consumed_minutes"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["reservation_id"], name: "index_prepaid_pack_reservations_on_reservation_id"
t.index ["statistic_profile_prepaid_pack_id"], name: "index_prepaid_pack_reservations_on_sp_prepaid_pack_id"
end
create_table "prepaid_packs", force: :cascade do |t|
t.string "priceable_type"
t.bigint "priceable_id"
@ -1422,7 +1444,10 @@ ActiveRecord::Schema.define(version: 2023_03_02_120458) do
add_foreign_key "payment_schedules", "invoicing_profiles", column: "operator_profile_id"
add_foreign_key "payment_schedules", "statistic_profiles"
add_foreign_key "payment_schedules", "wallet_transactions"
add_foreign_key "plan_limitations", "plans"
add_foreign_key "plans", "plan_categories"
add_foreign_key "prepaid_pack_reservations", "reservations"
add_foreign_key "prepaid_pack_reservations", "statistic_profile_prepaid_packs"
add_foreign_key "prepaid_packs", "groups"
add_foreign_key "prices", "groups"
add_foreign_key "prices", "plans"

View File

@ -307,7 +307,7 @@ namespace :fablab do
desc '[release 5.8.2] fix prepaid packs minutes_used'
task pack_minutes_used: :environment do |_task, _args|
StatisticProfilePrepaidPack.where('minutes_used < 0').find_each do |sppp|
StatisticProfilePrepaidPack.find_each do |sppp|
previous_packs = sppp.statistic_profile.statistic_profile_prepaid_packs
.includes(:prepaid_pack)
.where(prepaid_packs: { priceable: sppp.prepaid_pack.priceable })
@ -342,6 +342,7 @@ namespace :fablab do
end
end
available_minutes -= consumed
PrepaidPackReservation.find_or_create_by!(statistic_profile_prepaid_pack: pack, reservation: reservation, consumed_minutes: consumed)
end
pack.update(minutes_used: pack.prepaid_pack.minutes - available_minutes)
end