diff --git a/app/controllers/admin/billing/prices_controller.rb b/app/controllers/admin/billing/prices_controller.rb index 009502030..041f55b1e 100644 --- a/app/controllers/admin/billing/prices_controller.rb +++ b/app/controllers/admin/billing/prices_controller.rb @@ -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]) @@ -41,6 +53,13 @@ module Admin end end + def expire + @price.expire + @price.save! + flash[:notice] = t('.expired') + redirect_to_index + end + private def load_price @@ -60,6 +79,13 @@ module Admin 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 diff --git a/app/models/billing/price.rb b/app/models/billing/price.rb index c12cffffe..e5cddd76c 100644 --- a/app/models/billing/price.rb +++ b/app/models/billing/price.rb @@ -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 @@ -11,6 +12,7 @@ module Billing 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 diff --git a/app/models/concerns/billing/price/expirable.rb b/app/models/concerns/billing/price/expirable.rb new file mode 100644 index 000000000..c51bdff92 --- /dev/null +++ b/app/models/concerns/billing/price/expirable.rb @@ -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 diff --git a/app/views/admin/billing/prices/_price.html.erb b/app/views/admin/billing/prices/_price.html.erb index f2cf2027e..199f53e81 100644 --- a/app/views/admin/billing/prices/_price.html.erb +++ b/app/views/admin/billing/prices/_price.html.erb @@ -1,5 +1,5 @@ - - <%= link_to price.zone_name, edit_admin_price_path(price), id: 'admin-edit-price-btn' %> + + <%= link_to price.zone_name, edit_admin_price_path(price), class: 'edit-price-btn' %> <%= price.duration.sub('mons', 'months') %> <%= price.operation_category %> <%= number_to_currency price.price %> diff --git a/app/views/admin/billing/prices/_search_form.html.erb b/app/views/admin/billing/prices/_search_form.html.erb new file mode 100644 index 000000000..e538ebf84 --- /dev/null +++ b/app/views/admin/billing/prices/_search_form.html.erb @@ -0,0 +1,20 @@ +<%= form_for :search, url: admin_prices_path, method: :get, html: { class: 'form-horizontal' } do |f| %> +
+ <%= f.label :validity, class: 'col-sm-2 control-label' %> + +
+ <%= f.select :validity, options_for_select(%w[unexpired expired], search.validity), + { include_blank: true }, + class: 'form-control' %> +
+
+ +
+
+ <%= 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' %> +
+
+ +<% end %> diff --git a/app/views/admin/billing/prices/edit.html.erb b/app/views/admin/billing/prices/edit.html.erb index 61cc348e2..b447814a2 100644 --- a/app/views/admin/billing/prices/edit.html.erb +++ b/app/views/admin/billing/prices/edit.html.erb @@ -3,7 +3,18 @@ <% if @price.persisted? && @price.errors.none? %> diff --git a/app/views/admin/billing/prices/index.html.erb b/app/views/admin/billing/prices/index.html.erb index c675b8bde..31d74dc96 100644 --- a/app/views/admin/billing/prices/index.html.erb +++ b/app/views/admin/billing/prices/index.html.erb @@ -10,6 +10,8 @@ +<%= render 'search_form', search: @search %> + <% if @prices.present? %> diff --git a/config/locales/admin/billing/prices.en.yml b/config/locales/admin/billing/prices.en.yml index d4e4f5174..12f16f3f3 100644 --- a/config/locales/admin/billing/prices.en.yml +++ b/config/locales/admin/billing/prices.en.yml @@ -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 diff --git a/config/routes.rb b/config/routes.rb index 9eed77059..e9fb61989 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -166,7 +166,13 @@ Rails.application.routes.draw do 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 diff --git a/spec/factories/billing/price.rb b/spec/factories/billing/price.rb index 52860bd14..6700368ae 100644 --- a/spec/factories/billing/price.rb +++ b/spec/factories/billing/price.rb @@ -6,5 +6,13 @@ FactoryGirl.define do 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 diff --git a/spec/features/admin/billing/prices/edit_spec.rb b/spec/features/admin/billing/prices/edit_spec.rb index 6c567b5ca..f6c777bf4 100644 --- a/spec/features/admin/billing/prices/edit_spec.rb +++ b/spec/features/admin/billing/prices/edit_spec.rb @@ -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 diff --git a/spec/features/admin/billing/prices/expire_spec.rb b/spec/features/admin/billing/prices/expire_spec.rb new file mode 100644 index 000000000..5713e607a --- /dev/null +++ b/spec/features/admin/billing/prices/expire_spec.rb @@ -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 diff --git a/spec/features/admin/billing/prices/list_spec.rb b/spec/features/admin/billing/prices/list_spec.rb new file mode 100644 index 000000000..a5f63b57f --- /dev/null +++ b/spec/features/admin/billing/prices/list_spec.rb @@ -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 diff --git a/spec/features/admin/billing/prices/new_spec.rb b/spec/features/admin/billing/prices/new_spec.rb index ddbbac57b..27c56ffb6 100644 --- a/spec/features/admin/billing/prices/new_spec.rb +++ b/spec/features/admin/billing/prices/new_spec.rb @@ -16,8 +16,6 @@ RSpec.feature 'New price in admin area', settings: false do 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 diff --git a/spec/models/billing/price_spec.rb b/spec/models/billing/price_spec.rb index f9d6c8fc8..489971478 100644 --- a/spec/models/billing/price_spec.rb +++ b/spec/models/billing/price_spec.rb @@ -3,6 +3,7 @@ require 'rails_helper' 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 diff --git a/spec/models/concerns/billing/price/expirable_spec.rb b/spec/models/concerns/billing/price/expirable_spec.rb new file mode 100644 index 000000000..c9eb1a78e --- /dev/null +++ b/spec/models/concerns/billing/price/expirable_spec.rb @@ -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 diff --git a/spec/requests/admin/billing/prices/expire_spec.rb b/spec/requests/admin/billing/prices/expire_spec.rb new file mode 100644 index 000000000..b182b3143 --- /dev/null +++ b/spec/requests/admin/billing/prices/expire_spec.rb @@ -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