Consider nil in price expire time as effective

#522
This commit is contained in:
Artur Beljajev 2017-06-03 23:12:16 +03:00
parent 8916eee169
commit f76e1259fc
11 changed files with 106 additions and 65 deletions

View file

@ -6,18 +6,19 @@ module Admin
helper_method :zones helper_method :zones
helper_method :operation_categories helper_method :operation_categories
helper_method :durations helper_method :durations
helper_method :statuses
def index def index
@search = OpenStruct.new(search_params) @search = OpenStruct.new(search_params)
unless @search.validity unless @search.status
@search.validity = 'unexpired' @search.status = default_status
end end
prices = ::Billing::Price.all prices = ::Billing::Price.all
if @search.validity.present? if @search.status.present?
prices = ::Billing::Price.send(@search.validity) prices = ::Billing::Price.send(@search.status)
end end
@q = prices.search(params[:q]) @q = prices.search(params[:q])
@ -81,7 +82,7 @@ module Admin
def search_params def search_params
allowed_params = %i[ allowed_params = %i[
validity status
] ]
params.fetch(:search, {}).permit(*allowed_params) params.fetch(:search, {}).permit(*allowed_params)
end end
@ -102,6 +103,14 @@ module Admin
durations = ::Billing::Price::durations durations = ::Billing::Price::durations
durations.collect { |duration| [duration.sub('mon', 'month'), duration] } durations.collect { |duration| [duration.sub('mon', 'month'), duration] }
end end
def statuses
::Billing::Price.statuses
end
def default_status
'effective'
end
end end
end end
end end

View file

@ -12,6 +12,7 @@ module Billing
validates :operation_category, inclusion: { in: Proc.new { |price| price.class.operation_categories } } validates :operation_category, inclusion: { in: Proc.new { |price| price.class.operation_categories } }
validates :duration, inclusion: { in: Proc.new { |price| price.class.durations } } validates :duration, inclusion: { in: Proc.new { |price| price.class.durations } }
alias_attribute :effect_time, :valid_from
alias_attribute :expire_time, :valid_to alias_attribute :expire_time, :valid_to
monetize :price_cents, allow_nil: true, numericality: { greater_than_or_equal_to: 0 } monetize :price_cents, allow_nil: true, numericality: { greater_than_or_equal_to: 0 }
after_initialize :init_values after_initialize :init_values
@ -22,22 +23,37 @@ module Billing
def self.durations def self.durations
[ [
'3 mons', '3 mons',
'6 mons', '6 mons',
'9 mons', '9 mons',
'1 year', '1 year',
'2 years', '2 years',
'3 years', '3 years',
'4 years', '4 years',
'5 years', '5 years',
'6 years', '6 years',
'7 years', '7 years',
'8 years', '8 years',
'9 years', '9 years',
'10 years', '10 years',
] ]
end end
def self.statuses
%w[pending effective expired]
end
def self.pending
where("#{attribute_alias(:effect_time)} > ?", Time.zone.now)
end
def self.effective
condition = "#{attribute_alias(:effect_time)} <= :now " \
" AND (#{attribute_alias(:expire_time)} >= :now" \
" OR #{attribute_alias(:expire_time)} IS NULL)"
where(condition, now: Time.zone.now)
end
def self.valid def self.valid
where('valid_from <= ? AND (valid_to >= ? OR valid_to IS NULL)', Time.zone.now.end_of_day, where('valid_from <= ? AND (valid_to >= ? OR valid_to IS NULL)', Time.zone.now.end_of_day,
Time.zone.now.beginning_of_day) Time.zone.now.beginning_of_day)

View file

@ -2,10 +2,6 @@ module Concerns::Billing::Price::Expirable
extend ActiveSupport::Concern extend ActiveSupport::Concern
class_methods do class_methods do
def unexpired
where("#{attribute_alias(:expire_time)} >= ?", Time.zone.now)
end
def expired def expired
where("#{attribute_alias(:expire_time)} < ?", Time.zone.now) where("#{attribute_alias(:expire_time)} < ?", Time.zone.now)
end end

View file

@ -1,10 +1,9 @@
<%= form_for :search, url: admin_prices_path, method: :get, html: { class: 'form-horizontal' } do |f| %> <%= form_for :search, url: admin_prices_path, method: :get, html: { class: 'form-horizontal' } do |f| %>
<div class="form-group"> <div class="form-group">
<%= f.label :validity, class: 'col-sm-2 control-label' %> <%= f.label :status, class: 'col-sm-2 control-label' %>
<div class="col-sm-3"> <div class="col-sm-3">
<%= f.select :validity, options_for_select(%w[unexpired expired], search.validity), <%= f.select :status, options_for_select(statuses, search.status), { include_blank: true },
{ include_blank: true },
class: 'form-control' %> class: 'form-control' %>
</div> </div>
</div> </div>

View file

@ -7,7 +7,7 @@ FactoryGirl.define do
operation_category Billing::Price.operation_categories.first operation_category Billing::Price.operation_categories.first
zone zone
factory :unexpired_price do factory :effective_price do
expire_time { Time.zone.now + 1.day } expire_time { Time.zone.now + 1.day }
end end

View file

@ -1,7 +1,7 @@
require 'rails_helper' require 'rails_helper'
RSpec.feature 'Editing price in admin area', settings: false do RSpec.feature 'Editing price in admin area', settings: false do
given!(:price) { create(:unexpired_price) } given!(:price) { create(:effective_price) }
background do background do
sign_in_to_admin_area sign_in_to_admin_area

View file

@ -1,7 +1,7 @@
require 'rails_helper' require 'rails_helper'
RSpec.feature 'Expiring price in admin area', settings: false do RSpec.feature 'Expiring price in admin area', settings: false do
given!(:price) { create(:unexpired_price) } given!(:price) { create(:effective_price) }
background do background do
sign_in_to_admin_area sign_in_to_admin_area

View file

@ -1,7 +1,7 @@
require 'rails_helper' require 'rails_helper'
RSpec.feature 'Viewing prices in admin area', settings: false do RSpec.feature 'Viewing prices in admin area', settings: false do
given!(:unexpired_price) { create(:unexpired_price) } given!(:effective_price) { create(:effective_price) }
given!(:expired_price) { create(:expired_price) } given!(:expired_price) { create(:expired_price) }
background do background do
@ -9,17 +9,17 @@ RSpec.feature 'Viewing prices in admin area', settings: false do
end end
describe 'search' do describe 'search' do
context 'when validity is not selected' do context 'when status is not selected' do
scenario 'shows unexpired prices' do scenario 'shows effective prices' do
visit admin_prices_path visit admin_prices_path
expect(page).to have_css('.price', count: 1) expect(page).to have_css('.price', count: 1)
end end
end end
context 'when validity is given' do context 'when status is given' do
scenario 'filters by given validity' do scenario 'filters by given status' do
visit admin_prices_path visit admin_prices_path
select 'unexpired', from: 'search_validity' select 'effective', from: 'search_status'
submit_search_form submit_search_form
expect(page).to have_css('.price', count: 1) expect(page).to have_css('.price', count: 1)

View file

@ -3,6 +3,7 @@ require 'rails_helper'
RSpec.describe Billing::Price do RSpec.describe Billing::Price do
it { is_expected.to monetize(:price) } it { is_expected.to monetize(:price) }
it { is_expected.to be_versioned } it { is_expected.to be_versioned }
it { is_expected.to alias_attribute(:effect_time, :valid_from) }
it { is_expected.to alias_attribute(:expire_time, :valid_to) } it { is_expected.to alias_attribute(:expire_time, :valid_to) }
it 'should have one version' do it 'should have one version' do
@ -14,34 +15,68 @@ RSpec.describe Billing::Price do
end end
describe '::operation_categories', db: false do describe '::operation_categories', db: false do
it 'returns available operation categories' do it 'returns operation categories' do
categories = %w[create renew] categories = %w[create renew]
expect(described_class.operation_categories).to eq(categories) expect(described_class.operation_categories).to eq(categories)
end end
end end
describe '::durations', db: false do describe '::durations', db: false do
it 'returns available durations' do it 'returns durations' do
durations = [ durations = [
'3 mons', '3 mons',
'6 mons', '6 mons',
'9 mons', '9 mons',
'1 year', '1 year',
'2 years', '2 years',
'3 years', '3 years',
'4 years', '4 years',
'5 years', '5 years',
'6 years', '6 years',
'7 years', '7 years',
'8 years', '8 years',
'9 years', '9 years',
'10 years', '10 years',
] ]
expect(described_class.durations).to eq(durations) expect(described_class.durations).to eq(durations)
end end
end end
describe '::statuses', db: false do
it 'returns statuses' do
expect(described_class.statuses).to eq(%w[pending effective expired])
end
end
describe '::pending' do
before :example do
travel_to Time.zone.parse('05.07.2010 00:00')
create(:price, id: 1, effect_time: Time.zone.parse('05.07.2010 00:00'))
create(:price, id: 2, effect_time: Time.zone.parse('05.07.2010 00:01'))
end
it 'returns pending' do
expect(described_class.pending.ids).to eq([2])
end
end
describe '::effective' do
before :example do
travel_to Time.zone.parse('05.07.2010 00:00')
create(:price, id: 1, effect_time: '05.07.2010 00:01', expire_time: '05.07.2010 00:02')
create(:price, id: 2, effect_time: '05.07.2010 00:00', expire_time: '05.07.2010 00:01')
create(:price, id: 3, effect_time: '05.07.2010 00:00', expire_time: nil)
create(:price, id: 4, effect_time: '04.07.2010', expire_time: '04.07.2010 23:59')
end
it 'returns effective' do
expect(described_class.effective.ids).to eq([2, 3])
end
end
describe 'zone validation', db: false do describe 'zone validation', db: false do
subject(:price) { described_class.new } subject(:price) { described_class.new }

View file

@ -1,20 +1,6 @@
require 'rails_helper' require 'rails_helper'
RSpec.describe Billing::Price do RSpec.describe Billing::Price do
describe '::unexpired' do
before :example do
travel_to Time.zone.parse('05.07.2010 00:00')
create(:price, id: 1, expire_time: Time.zone.parse('04.07.2010 23:59'))
create(:price, id: 2, expire_time: Time.zone.parse('05.07.2010 00:00'))
create(:price, id: 3, expire_time: Time.zone.parse('05.07.2010 00:01'))
end
it 'returns prices with expire time in the future ' do
expect(described_class.unexpired.ids).to eq([2, 3])
end
end
describe '::expired' do describe '::expired' do
before :example do before :example do
travel_to Time.zone.parse('05.07.2010 00:00') travel_to Time.zone.parse('05.07.2010 00:00')

View file

@ -6,14 +6,14 @@ RSpec.describe 'admin price expire', settings: false do
end end
it 'expires price' do it 'expires price' do
price = create(:unexpired_price) price = create(:effective_price)
expect { patch expire_admin_price_path(price); price.reload } expect { patch expire_admin_price_path(price); price.reload }
.to change { price.expired? }.from(false).to(true) .to change { price.expired? }.from(false).to(true)
end end
it 'redirects to :index' do it 'redirects to :index' do
price = create(:unexpired_price) price = create(:effective_price)
patch expire_admin_price_path(price) patch expire_admin_price_path(price)