Skip to content

Commit

Permalink
Add price expiration
Browse files Browse the repository at this point in the history
  • Loading branch information
Artur Beljajev committed May 25, 2017
1 parent 3741e2d commit 58ae53b
Show file tree
Hide file tree
Showing 17 changed files with 264 additions and 13 deletions.
30 changes: 28 additions & 2 deletions app/controllers/admin/billing/prices_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,25 @@ module Admin
module Billing
class PricesController < AdminController
authorize_resource(class: 'Billing::Price')
before_action :load_price, only: %i[edit update destroy]
before_action :load_price, only: %i[edit update expire]
helper_method :zones
helper_method :operation_categories
helper_method :durations

def index
@q = ::Billing::Price.search(params[:q])
@search = OpenStruct.new(search_params)

unless @search.validity
@search.validity = 'unexpired'
end

prices = ::Billing::Price.all

if @search.validity.present?
prices = ::Billing::Price.send(@search.validity)
end

@q = prices.search(params[:q])
@q.sorts = ['zone_id asc', 'duration asc', 'operation_category asc',
'valid_from desc', 'valid_to asc'] if @q.sorts.empty?
@prices = @q.result.page(params[:page])
Expand Down Expand Up @@ -41,6 +53,13 @@ def update
end
end

def expire
@price.expire
@price.save!
flash[:notice] = t('.expired')
redirect_to_index
end

private

def load_price
Expand All @@ -60,6 +79,13 @@ def price_params
params.require(:price).permit(*allowed_params)
end

def search_params
allowed_params = %i[
validity
]
params.fetch(:search, {}).permit(*allowed_params)
end

def redirect_to_index
redirect_to admin_prices_url
end
Expand Down
2 changes: 2 additions & 0 deletions app/models/billing/price.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
module Billing
class Price < ActiveRecord::Base
include Versions
include Concerns::Billing::Price::Expirable
has_paper_trail class_name: '::PriceVersion'

self.auto_html5_validation = false
Expand All @@ -11,6 +12,7 @@ class Price < ActiveRecord::Base
validates :operation_category, inclusion: { in: Proc.new { |price| price.class.operation_categories } }
validates :duration, inclusion: { in: Proc.new { |price| price.class.durations } }

alias_attribute :expire_time, :valid_to
monetize :price_cents, allow_nil: true, numericality: { greater_than_or_equal_to: 0 }
after_initialize :init_values

Expand Down
21 changes: 21 additions & 0 deletions app/models/concerns/billing/price/expirable.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
module Concerns::Billing::Price::Expirable
extend ActiveSupport::Concern

class_methods do
def unexpired
where("#{attribute_alias(:expire_time)} >= ?", Time.zone.now)
end

def expired
where("#{attribute_alias(:expire_time)} < ?", Time.zone.now)
end
end

def expire
self[:valid_to] = Time.zone.now - 1
end

def expired?
expire_time.past?
end
end
4 changes: 2 additions & 2 deletions app/views/admin/billing/prices/_price.html.erb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<tr>
<td><%= link_to price.zone_name, edit_admin_price_path(price), id: 'admin-edit-price-btn' %></td>
<tr class="price">
<td><%= link_to price.zone_name, edit_admin_price_path(price), class: 'edit-price-btn' %></td>
<td><%= price.duration.sub('mons', 'months') %></td>
<td><%= price.operation_category %></td>
<td><%= number_to_currency price.price %></td>
Expand Down
20 changes: 20 additions & 0 deletions app/views/admin/billing/prices/_search_form.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<%= form_for :search, url: admin_prices_path, method: :get, html: { class: 'form-horizontal' } do |f| %>
<div class="form-group">
<%= f.label :validity, class: 'col-sm-2 control-label' %>

<div class="col-sm-3">
<%= f.select :validity, options_for_select(%w[unexpired expired], search.validity),
{ include_blank: true },
class: 'form-control' %>
</div>
</div>

<div class="form-group">
<div class="col-sm-offset-2 col-sm-8">
<%= f.submit t('.search_btn'), class: 'btn btn-primary', name: nil %>
<%= link_to t('.reset_btn'), admin_prices_path,
class: 'btn btn-default price-search-form-search-btn' %>
</div>
</div>

<% end %>
13 changes: 12 additions & 1 deletion app/views/admin/billing/prices/edit.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,18 @@
</ol>

<div class="page-header">
<h1><%= t '.title' %></h1>
<div class="row">
<div class="col-sm-10">
<h1><%= t '.title' %></h1>
</div>

<div class="col-sm-2 text-right">
<%= link_to(t('.expire_btn'), expire_admin_price_path(@price),
method: :patch,
data: { confirm: t('.expire_btn_confirm') },
class: 'btn btn-danger') %>
</div>
</div>
</div>

<% if @price.persisted? && @price.errors.none? %>
Expand Down
2 changes: 2 additions & 0 deletions app/views/admin/billing/prices/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
</div>
</div>

<%= render 'search_form', search: @search %>

<% if @prices.present? %>
<table class="table table-hover table-bordered table-wrapped">
<thead>
Expand Down
9 changes: 9 additions & 0 deletions config/locales/admin/billing/prices.en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,19 @@ en:

edit:
title: Edit price
expire_btn: Expire
expire_btn_confirm: Are you sure you want to expire price?

update:
updated: Price has been updated

expire:
expired: Price has been expired

form:
create_btn: Create price
update_btn: Update price

search_form:
search_btn: Search
reset_btn: Reset
8 changes: 7 additions & 1 deletion config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,13 @@
resources :zones, controller: 'dns/zones', except: %i[show destroy]
resources :legal_documents
resources :keyrelays
resources :prices, controller: 'billing/prices', except: %i[show destroy]

resources :prices, controller: 'billing/prices', except: %i[show destroy] do
member do
patch :expire
end
end

resources :mail_templates
resources :account_activities

Expand Down
8 changes: 8 additions & 0 deletions spec/factories/billing/price.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,13 @@
duration '1 year'
operation_category Billing::Price.operation_categories.first
zone

factory :unexpired_price do
expire_time { Time.zone.now + 1.day }
end

factory :expired_price do
expire_time { Time.zone.now - 1.day }
end
end
end
8 changes: 3 additions & 5 deletions spec/features/admin/billing/prices/edit_spec.rb
Original file line number Diff line number Diff line change
@@ -1,24 +1,22 @@
require 'rails_helper'

RSpec.feature 'Editing price in admin area', settings: false do
given!(:price) { create(:price) }
given!(:price) { create(:unexpired_price) }

background do
sign_in_to_admin_area
end

scenario 'updates price' do
visit admin_prices_url
visit admin_prices_path
open_form
submit_form

expect(page).to have_text(t('admin.billing.prices.update.updated'))
end

private

def open_form
click_link_or_button 'admin-edit-price-btn'
find('.edit-price-btn').click
end

def submit_form
Expand Down
25 changes: 25 additions & 0 deletions spec/features/admin/billing/prices/expire_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
require 'rails_helper'

RSpec.feature 'Expiring price in admin area', settings: false do
given!(:price) { create(:unexpired_price) }

background do
sign_in_to_admin_area
end

scenario 'expires price' do
visit admin_prices_path
open_edit_form
expire

expect(page).to have_text(t('admin.billing.prices.expire.expired'))
end

def open_edit_form
find('.edit-price-btn').click
end

def expire
click_link_or_button t('admin.billing.prices.edit.expire_btn')
end
end
33 changes: 33 additions & 0 deletions spec/features/admin/billing/prices/list_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
require 'rails_helper'

RSpec.feature 'Viewing prices in admin area', settings: false do
given!(:unexpired_price) { create(:unexpired_price) }
given!(:expired_price) { create(:expired_price) }

background do
sign_in_to_admin_area
end

describe 'search' do
context 'when validity is not selected' do
scenario 'shows unexpired prices' do
visit admin_prices_path
expect(page).to have_css('.price', count: 1)
end
end

context 'when validity is given' do
scenario 'filters by given validity' do
visit admin_prices_path
select 'unexpired', from: 'search_validity'
submit_search_form

expect(page).to have_css('.price', count: 1)
end
end

def submit_search_form
find('.price-search-form-search-btn').click
end
end
end
2 changes: 0 additions & 2 deletions spec/features/admin/billing/prices/new_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@
expect(page).to have_text(t('admin.billing.prices.create.created'))
end

private

def open_list
click_link_or_button t('admin.menu.prices')
end
Expand Down
1 change: 1 addition & 0 deletions spec/models/billing/price_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
RSpec.describe Billing::Price do
it { is_expected.to monetize(:price) }
it { is_expected.to be_versioned }
it { is_expected.to alias_attribute(:expire_time, :valid_to) }

it 'should have one version' do
with_versioning do
Expand Down
69 changes: 69 additions & 0 deletions spec/models/concerns/billing/price/expirable_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
require 'rails_helper'

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
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 past ' do
expect(described_class.expired.ids).to eq([1])
end
end

describe '#expire', db: false do
let(:price) { described_class.new(expire_time: Time.zone.parse('06.07.2010')) }

before :example do
travel_to Time.zone.parse('05.07.2010 00:00')
end

it 'expires price' do
expect { price.expire }.to change { price.expired? }.from(false).to(true)
end
end

describe '#expired?', db: false do
subject(:expired) { domain.expired? }

before :example do
travel_to Time.zone.parse('05.07.2010 00:00')
end

context 'when expire time is in the past' do
let(:domain) { described_class.new(expire_time: Time.zone.parse('04.07.2010 23:59')) }

specify { expect(expired).to be true }
end

context 'when expire time is now' do
let(:domain) { described_class.new(expire_time: Time.zone.parse('05.07.2010 00:00')) }

specify { expect(expired).to be false }
end

context 'when expire time is in the future' do
let(:domain) { described_class.new(expire_time: Time.zone.parse('05.07.2010 00:01')) }

specify { expect(expired).to be false }
end
end
end
22 changes: 22 additions & 0 deletions spec/requests/admin/billing/prices/expire_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
require 'rails_helper'

RSpec.describe 'admin price expire', settings: false do
before :example do
sign_in_to_admin_area
end

it 'expires price' do
price = create(:unexpired_price)

expect { patch expire_admin_price_path(price); price.reload }
.to change { price.expired? }.from(false).to(true)
end

it 'redirects to :index' do
price = create(:unexpired_price)

patch expire_admin_price_path(price)

expect(response).to redirect_to admin_prices_url
end
end

0 comments on commit 58ae53b

Please sign in to comment.