From 3d8d8cf2ec2eeaac4c503f911a84bb3d9a8bee42 Mon Sep 17 00:00:00 2001 From: SepsiLaszlo Date: Fri, 30 Sep 2022 17:42:31 +0200 Subject: [PATCH] refactor RentNumberValidator --- app/models/rent.rb | 4 ++ app/validators/rent_number_validator.rb | 61 +++++++++++++-------- spec/models/rent_spec.rb | 72 ++++++++++++++++--------- 3 files changed, 92 insertions(+), 45 deletions(-) diff --git a/app/models/rent.rb b/app/models/rent.rb index 8b757f6..dfaf23c 100644 --- a/app/models/rent.rb +++ b/app/models/rent.rb @@ -53,5 +53,9 @@ def self.users_filter(params, id) def item Item.unscoped { super } end + + def days + (self.begin.to_date.. self.end.to_date).to_a + end end diff --git a/app/validators/rent_number_validator.rb b/app/validators/rent_number_validator.rb index fbb73d7..f381548 100644 --- a/app/validators/rent_number_validator.rb +++ b/app/validators/rent_number_validator.rb @@ -1,24 +1,43 @@ +# Validators are instantiated once, instance variables persist between validations, might cause bugs class RentNumberValidator < ActiveModel::Validator - def validate(record) - #Rent.where("begin < ? && end > ?", record.end, record.begin).where.not(state: :unprocessed).sum(:number) - return if record.number.nil? || record.begin.nil? || record.end.nil? - sum = 0 - rents = Rent.all - rents.each do |rent| - sum += rent.number if(rent.begin < record.end && rent.end > record.begin && rent.state != "unprocessed") - end - if sum+record.number > record.item.number - record.errors.add :base, "Erre az időpontra sajnos nincs elég a kívánt eszközből." - record.errors.add :base, "A megadott napokra elérhető eszközök száma:" - day = record.begin.to_date - while(day <= record.end.to_date) - sum = 0 - rents.each do |rent| - sum += rent.number if(rent.begin.to_date <= day && rent.end.to_date >= day && rent.state != "unprocessed") - end - record.errors.add :base, day.to_s + ": " + (record.item.number - sum).to_s + " darab" - day += 1 - end - end + def validate(record) + item = record.item + active_rents = active_rents(record) + rent_days = rent_days(active_rents) + rents_by_item = rents_by_item(rent_days, active_rents) + + record.days.each do |day| + rent_number_for_day = rents_by_item.dig(day, item) || 0 + if rent_number_for_day + record.number > item.number + available_item_number = item.number = rent_number_for_day + record.errors.add :base, "A kiválasztott napon(#{day}) csak #{available_item_number} darab #{item.name} elérhető." + end end + end + + private + + # hash[rent_day][item] = sum taken out amount item (per day, per item) + def rents_by_item(rent_days, active_rents) + result = {} + rent_days.each do |rent_date| + result[rent_date] = {} if result[rent_date].nil? + active_rents.each do |active_rent| + result[rent_date][active_rent.item] = 0 if result[rent_date][active_rent.item].nil? + result[rent_date][active_rent.item] += active_rent.number + end + end + result + end + + def rent_days(active_rents) + active_rents.map(&:days).flatten.uniq.sort + end + + def active_rents(record) + Rent.includes(:item) + .where(state: [:approved, :taken]) + .where.not('rents.end <= :begin or :end <= rents.begin', + { begin: record.begin, end: record.end }) + end end diff --git a/spec/models/rent_spec.rb b/spec/models/rent_spec.rb index 81c42b7..1366281 100644 --- a/spec/models/rent_spec.rb +++ b/spec/models/rent_spec.rb @@ -2,7 +2,7 @@ RSpec.describe Rent, type: :model do let(:user) { create(:user) } - let(:item) { create(:item) } + let(:item) { create(:item, number: 10) } let(:pre_existing_rent) do Rent.create!(state: :approved, item: item, user: user, begin: DateTime.new(2022, 1, 10), @@ -13,7 +13,7 @@ Rent.new(state: :approved, item: item, user: user, begin: begin_date, end: end_date, - number: 5) + number: rent_number) end subject do @@ -29,34 +29,58 @@ Timecop.return end - context 'when the rent starts before the pre existing rent' do - let(:begin_date) { DateTime.new(2022, 1, 5) } - context 'and ends before the current rent' do - let(:end_date) { DateTime.new(2022, 1, 9) } - it { expect(subject).to be_valid } + context 'when the rent number equals to all available item number' do + let(:rent_number) { 10 } + context 'when the rent starts before the pre existing rent' do + let(:begin_date) { DateTime.new(2022, 1, 5) } + context 'and ends before the current rent' do + let(:end_date) { DateTime.new(2022, 1, 9) } + it { expect(subject).to be_valid } + end + context 'and ends on the current rent beginning date' do + let(:end_date) { DateTime.new(2022, 1, 10) } + it { expect(subject).to be_valid } + end + context 'and ends after the current_rent is take' do + let(:end_date) { DateTime.new(2022, 1, 11) } + it { + expect(subject).not_to be_valid + } + end end - context 'and ends on the current rent beginning date' do - let(:end_date) { DateTime.new(2022, 1, 10) } - it { expect(subject).to be_valid } + + context 'when the rent ends after the pre existing rent' do + let(:end_date) { DateTime.new(2022, 1, 20) } + context 'and begins before the current rent is back' do + let(:begin_date) { DateTime.new(2022, 1, 14) } + it { expect(subject).not_to be_valid } + end + context 'and begins whe the current rent is back' do + let(:begin_date) { DateTime.new(2022, 1, 15) } + it { expect(subject).to be_valid } + end + context 'and begins after the current rent is back' do + let(:begin_date) { DateTime.new(2022, 1, 16) } + it { expect(subject).to be_valid } + end end - context 'and ends after the current_rent is take' do - let(:end_date) { DateTime.new(2022, 1, 11) } + context 'and begins and ends during the current rent' do + let(:begin_date) { DateTime.new(2022, 1, 11) } + let(:end_date) { DateTime.new(2022, 1, 14) } it { expect(subject).not_to be_valid } end - end - - context 'when the rent ends after the pre existing rent' do - let(:end_date) { DateTime.new(2022, 1, 20) } - context 'and begins before the current rent is back' do - let(:begin_date) { DateTime.new(2022, 1, 14) } + context 'and begins and ends at the same time as the current rent' do + let(:begin_date) { DateTime.new(2022, 1, 10) } + let(:end_date) { DateTime.new(2022, 1, 15) } it { expect(subject).not_to be_valid } end - context 'and begins whe the current rent is back' do - let(:begin_date) { DateTime.new(2022, 1, 15) } - it { expect(subject).to be_valid } - end - context 'and begins after the current rent is back' do - let(:begin_date) { DateTime.new(2022, 1, 16) } + end + + context 'when the rent number is less then all available item number' do + let(:rent_number) { 1 } + context 'and begins and ends at the same time as the current rent' do + let(:begin_date) { DateTime.new(2022, 1, 10) } + let(:end_date) { DateTime.new(2022, 1, 15) } it { expect(subject).to be_valid } end end