diff --git a/CHANGELOG.md b/CHANGELOG.md index 17ff40a76..625f44a42 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,11 +3,13 @@ - Improved upgrade script - 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 - Fix a bug: empty advanced accounting code is not defaulted to the general setting - Fix a bug: invalid style in accounting codes settings - Fix a bug: wrong namespace for task cart_operator - [TODO DEPLOY] `rails fablab:fix:cart_operator` - [TODO DEPLOY] `rails fablab:setup:build_accounting_lines` +- [TODO DEPLOY] `rails fablab:fix:pack_minutes_used` ## v5.8.1 2023 March 03 diff --git a/app/frontend/src/javascript/components/dashboard/reservations/prepaid-packs-panel.tsx b/app/frontend/src/javascript/components/dashboard/reservations/prepaid-packs-panel.tsx index c1a0c94ab..26fc55032 100644 --- a/app/frontend/src/javascript/components/dashboard/reservations/prepaid-packs-panel.tsx +++ b/app/frontend/src/javascript/components/dashboard/reservations/prepaid-packs-panel.tsx @@ -125,7 +125,7 @@ const PrepaidPacksPanel: React.FC = ({ user, onError })

{pack.prepaid_pack.priceable.name}

{FormatLib.date(pack.expires_at) &&

{FormatLib.date(pack.expires_at)}

} -

{pack.minutes_used / 60}H / {pack.prepaid_pack.minutes / 60}H

+

{(pack.prepaid_pack.minutes - pack.minutes_used) / 60}H / {pack.prepaid_pack.minutes / 60}H

{ /* usage history is not saved for now diff --git a/app/services/prepaid_pack_service.rb b/app/services/prepaid_pack_service.rb index caa72c24e..b86ac918b 100644 --- a/app/services/prepaid_pack_service.rb +++ b/app/services/prepaid_pack_service.rb @@ -60,11 +60,10 @@ class PrepaidPackService packs = user_packs(user, reservation.reservable).order(minutes_used: :desc) packs.each do |pack| pack_available = pack.prepaid_pack.minutes - pack.minutes_used - remaining = pack_available - consumed - remaining = 0 if remaining.negative? - pack_consumed = pack.prepaid_pack.minutes - remaining - pack.update(minutes_used: pack_consumed) + remaining = consumed > pack_available ? 0 : pack_available - consumed + pack.update(minutes_used: pack.prepaid_pack.minutes - remaining) + pack_consumed = consumed > pack_available ? pack_available : consumed consumed -= pack_consumed end end diff --git a/lib/tasks/fablab/fix.rake b/lib/tasks/fablab/fix.rake index edfff75a5..b40c709ac 100644 --- a/lib/tasks/fablab/fix.rake +++ b/lib/tasks/fablab/fix.rake @@ -295,7 +295,7 @@ namespace :fablab do Rake::Task['fablab:chain:invoices_items'].invoke end - desc '[release 5.6] fix operator of self-bought carts' + desc '[release 5.8.2] fix operator of self-bought carts' task cart_operator: :environment do |_task, _args| Order.where.not(statistic_profile_id: nil).find_each do |order| order.update(operator_profile_id: order.user&.invoicing_profile&.id) @@ -304,5 +304,48 @@ namespace :fablab do order.update(statistic_profile_id: order.operator_profile&.user&.statistic_profile&.id) end end + + 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| + previous_packs = sppp.statistic_profile.statistic_profile_prepaid_packs + .includes(:prepaid_pack) + .where(prepaid_packs: { priceable: sppp.prepaid_pack.priceable }) + .where("statistic_profile_prepaid_packs.created_at <= '#{sppp.created_at.utc.strftime('%Y-%m-%d %H:%M:%S.%6N')}'") + .order('statistic_profile_prepaid_packs.created_at') + remaining = {} + previous_packs.each do |pack| + available_minutes = pack.prepaid_pack.minutes + reservations = Reservation.where(reservable: sppp.prepaid_pack.priceable) + .where(statistic_profile_id: sppp.statistic_profile_id) + .where("created_at > '#{pack.created_at.utc.strftime('%Y-%m-%d %H:%M:%S.%6N')}'") + reservations.each do |reservation| + next if available_minutes.zero? + + # if the previous pack has not covered all the duration of this reservation, we substract the remaining minutes from the current pack + if remaining[reservation.id] + if remaining[reservation.id] > available_minutes + consumed = available_minutes + remaining[reservation.id] = remaining[reservation.id] - available_minutes + else + consumed = remaining[reservation.id] + remaining.except!(reservation.id) + end + else + # if there was no remaining from the previous pack, we substract the reservation duration from the current pack + reservation_minutes = reservation.slots.map { |slot| (slot.end_at.to_time - slot.start_at.to_time) / 60.0 }.reduce(:+) || 0 + if reservation_minutes > available_minutes + consumed = available_minutes + remaining[reservation.id] = reservation_minutes - consumed + else + consumed = reservation_minutes + end + end + available_minutes -= consumed + end + pack.update(minutes_used: pack.prepaid_pack.minutes - available_minutes) + end + end + end end end diff --git a/test/models/statistic_profile_prepaid_pack_test.rb b/test/models/statistic_profile_prepaid_pack_test.rb index be1172ccc..e5725a3dc 100644 --- a/test/models/statistic_profile_prepaid_pack_test.rb +++ b/test/models/statistic_profile_prepaid_pack_test.rb @@ -3,7 +3,7 @@ require 'test_helper' class StatisticProfilePrepaidPackTest < ActiveSupport::TestCase - test 'coupon have a expries date' do + test 'prepaid pack have a expries date' do prepaid_pack = PrepaidPack.first user = User.find_by(username: 'jdupond') p = StatisticProfilePrepaidPack.create!(prepaid_pack: prepaid_pack, statistic_profile: user.statistic_profile) diff --git a/test/services/prepaid_pack_service_test.rb b/test/services/prepaid_pack_service_test.rb index 44953b343..26265cb21 100644 --- a/test/services/prepaid_pack_service_test.rb +++ b/test/services/prepaid_pack_service_test.rb @@ -37,4 +37,40 @@ class PrepaidPackServiceTest < ActiveSupport::TestCase minutes_available = PrepaidPackService.minutes_available(@acamus, @machine) assert_equal minutes_available, 480 end + + test 'member has multiple active packs' do + availabilities_service = Availabilities::AvailabilitiesService.new(@acamus) + + # user with current pack reserve 8 slots (on 10 available in the pack) + slots = availabilities_service.machines([@machine], @acamus, { start: Time.current, end: 10.days.from_now }) + reservation = Reservation.create( + reservable_id: @machine.id, + reservable_type: Machine.name, + slots: [slots[0], slots[1], slots[2], slots[3], slots[4], slots[5], slots[6], slots[7]], + statistic_profile_id: @acamus.statistic_profile.id + ) + + PrepaidPackService.update_user_minutes(@acamus, reservation) + minutes_available = PrepaidPackService.minutes_available(@acamus, @machine) + assert_equal 120, minutes_available + + # user buy a new pack + prepaid_pack = PrepaidPack.first + StatisticProfilePrepaidPack.create!(prepaid_pack: prepaid_pack, statistic_profile: @acamus.statistic_profile) + + minutes_available = PrepaidPackService.minutes_available(@acamus, @machine) + assert_equal 720, minutes_available + + # user books a new reservation of 4 slots + reservation = Reservation.create( + reservable_id: @machine.id, + reservable_type: Machine.name, + slots: [slots[8], slots[9], slots[10], slots[11]], + statistic_profile_id: @acamus.statistic_profile.id + ) + + PrepaidPackService.update_user_minutes(@acamus, reservation) + minutes_available = PrepaidPackService.minutes_available(@acamus, @machine) + assert_equal 480, minutes_available + end end