diff --git a/app/controllers/epp/domains_controller.rb b/app/controllers/epp/domains_controller.rb index c2e92ea49..a50b96003 100644 --- a/app/controllers/epp/domains_controller.rb +++ b/app/controllers/epp/domains_controller.rb @@ -30,7 +30,7 @@ class Epp::DomainsController < EppController ActiveRecord::Base.transaction do if @domain.save # TODO: Maybe use validate: false here because we have already validated the domain? - current_user.registrar.debit!(@domain_price, "#{I18n.t('create')} #{@domain.name}") + current_user.registrar.debit!(@domain_price, "#{I18n.t('create')} #{@domain.name}", AccountActivity::CREATE) render_epp_response '/epp/domains/create' else handle_errors(@domain) @@ -102,7 +102,7 @@ class Epp::DomainsController < EppController fail ActiveRecord::Rollback end - current_user.registrar.debit!(@domain_price, "#{I18n.t('renew')} #{@domain.name}") + current_user.registrar.debit!(@domain_price, "#{I18n.t('renew')} #{@domain.name}", AccountActivity::RENEW) render_epp_response '/epp/domains/renew' else handle_errors(@domain) diff --git a/app/controllers/registrar/account_activities_controller.rb b/app/controllers/registrar/account_activities_controller.rb index 54c3f6d24..2b3fc7951 100644 --- a/app/controllers/registrar/account_activities_controller.rb +++ b/app/controllers/registrar/account_activities_controller.rb @@ -1,10 +1,28 @@ class Registrar::AccountActivitiesController < RegistrarController load_and_authorize_resource - def index + def index # rubocop: disable Metrics/AbcSize + params[:q] ||= {} account = current_user.registrar.cash_account + + ca_cache = params[:q][:created_at_lteq] + begin + end_time = params[:q][:created_at_lteq].try(:to_date) + params[:q][:created_at_lteq] = end_time.try(:end_of_day) + rescue + logger.warn('Invalid date') + end + @q = account.activities.includes(:invoice).search(params[:q]) @q.sorts = 'id desc' if @q.sorts.empty? - @account_activities = @q.result.page(params[:page]) + + respond_to do |format| + format.html { @account_activities = @q.result.page(params[:page]) } + format.csv do + send_data @q.result.to_csv, filename: "account_activities_#{Time.zone.now.to_formatted_s(:number)}.csv" + end + end + + params[:q][:created_at_lteq] = ca_cache end end diff --git a/app/models/account_activity.rb b/app/models/account_activity.rb index d6feb50de..5bd17c79f 100644 --- a/app/models/account_activity.rb +++ b/app/models/account_activity.rb @@ -1,13 +1,37 @@ +require 'csv' + class AccountActivity < ActiveRecord::Base include Versions belongs_to :account belongs_to :bank_transaction belongs_to :invoice + CREATE = 'create' + RENEW = 'renew' + ADD_CREDIT = 'add_credit' + after_create :update_balance def update_balance account.balance += sum account.save end + + class << self + def types_for_select + [CREATE, RENEW, ADD_CREDIT].map { |x| [I18n.t(x), x] } + end + + def to_csv + attributes = %w(description activity_type created_at sum) + + CSV.generate(headers: true) do |csv| + csv << %w(description activity_type receipt_date sum) + + all.find_each do |x| + csv << attributes.map { |attr| x.send(attr) } + end + end + end + end end diff --git a/app/models/bank_transaction.rb b/app/models/bank_transaction.rb index cd94b0220..2e5b90a2e 100644 --- a/app/models/bank_transaction.rb +++ b/app/models/bank_transaction.rb @@ -79,7 +79,8 @@ class BankTransaction < ActiveRecord::Base invoice: invoice, sum: invoice.sum_without_vat, currency: currency, - description: description + description: description, + activity_type: AccountActivity::ADD_CREDIT ) end end diff --git a/app/models/registrar.rb b/app/models/registrar.rb index 5d89816d8..d9d2188d0 100644 --- a/app/models/registrar.rb +++ b/app/models/registrar.rb @@ -123,19 +123,21 @@ class Registrar < ActiveRecord::Base accounts.find_by(account_type: Account::CASH) end - def debit!(sum, description) + def debit!(sum, description, type = nil) cash_account.account_activities.create!( sum: -sum, currency: 'EUR', - description: description + description: description, + activity_type: type ) end - def credit!(sum, description) + def credit!(sum, description, type = nil) cash_account.account_activities.create!( sum: sum, currency: 'EUR', - description: description + description: description, + activity_type: type ) end diff --git a/app/views/registrar/account_activities/index.haml b/app/views/registrar/account_activities/index.haml index 0efd0ab20..371184cf9 100644 --- a/app/views/registrar/account_activities/index.haml +++ b/app/views/registrar/account_activities/index.haml @@ -1,29 +1,67 @@ - content_for :actions do = link_to(t(:back_to_billing), registrar_invoices_path, class: 'btn btn-default') + = link_to(t(:export_csv), url_for(params.merge(format: 'csv')), class: 'btn btn-default') + = render 'shared/title', name: t(:account_activity) +.row + .col-md-12 + = search_form_for @q, url: [:registrar, :account_activities], html: { style: 'margin-bottom: 0;' } do |f| + .row + .col-md-6 + .form-group + = f.label t(:activity_type) + = f.select :activity_type_in, AccountActivity.types_for_select, {}, class: 'form-control js-combobox', placeholder: t(:choose), multiple: true + .col-md-6 + .form-group + = f.label t(:description) + = f.search_field :description_cont, class: 'form-control', placeholder: t(:description), autocomplete: 'off' + .row + .col-md-3 + .form-group + = f.label t(:receipt_date_from) + = f.search_field :created_at_gteq, value: params[:q][:created_at_gteq], class: 'form-control datepicker', placeholder: t(:receipt_date_from), autocomplete: 'off' + .col-md-3 + .form-group + = f.label t(:receipt_date_until) + = f.search_field :created_at_lteq, value: params[:q][:created_at_lteq], class: 'form-control datepicker', placeholder: t(:receipt_date_until), autocomplete: 'off' + .col-md-6{style: 'padding-top: 25px;'} + %button.btn.btn-default +   + %span.glyphicon.glyphicon-search +   + %button.btn.btn-default.js-reset-form + = t(:clear_fields) +%hr + .row .col-md-12 .table-responsive %table.table.table-hover.table-condensed %thead %tr - %th{class: 'col-xs-5'}= t(:description) - %th{class: 'col-xs-3'}= t(:receipt_date) - %th{class: 'col-xs-2'}= t(:invoice) - %th{class: 'col-xs-2'}= t(:sum) + %th{class: 'col-xs-5'} + = sort_link(@q, 'description') + %th{class: 'col-xs-2'} + = sort_link(@q, 'activity_type') + %th{class: 'col-xs-3'} + = sort_link(@q, 'created_at', t(:receipt_date)) + %th{class: 'col-xs-2'} + = sort_link(@q, 'sum') %tbody - @account_activities.each do |x| %tr %td= x.description.present? ? x.description : '-' + %td= x.activity_type ? t(x.activity_type) : '' %td= l(x.created_at) - - if x.invoice - %td= link_to(x.invoice, [:registrar, x.invoice]) - - else - %td - - c = x.sum > 0.0 ? 'text-success' : 'text-danger' - s = x.sum > 0.0 ? "+#{x.sum} #{x.currency}" : "#{x.sum} #{x.currency}" %td{class: c}= s .row .col-md-12 = paginate @account_activities + +:coffee + $(".js-reset-form").on "click", (e) -> + e.preventDefault(); + window.location = "#{registrar_account_activities_path}" diff --git a/config/locales/en.yml b/config/locales/en.yml index 0faa3b0ef..bd358aaac 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -856,3 +856,8 @@ en: blocked_domains: 'Blocked domains' billing_failure_credit_balance_low: 'Billing failure - credit balance low' create: 'Create' + activity_type: 'Activity type' + receipt_date_from: 'Receipt date from' + receipt_date_until: 'Receipt date until' + add_credit: 'Add credit' + export_csv: 'Export CSV' diff --git a/db/migrate/20150706091724_add_activity_type_to_account_activities.rb b/db/migrate/20150706091724_add_activity_type_to_account_activities.rb new file mode 100644 index 000000000..df6f21f3a --- /dev/null +++ b/db/migrate/20150706091724_add_activity_type_to_account_activities.rb @@ -0,0 +1,5 @@ +class AddActivityTypeToAccountActivities < ActiveRecord::Migration + def change + add_column :account_activities, :activity_type, :string + end +end diff --git a/db/schema-read-only.rb b/db/schema-read-only.rb index 165aebfc9..768be3160 100644 --- a/db/schema-read-only.rb +++ b/db/schema-read-only.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20150703084632) do +ActiveRecord::Schema.define(version: 20150706091724) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -27,6 +27,7 @@ ActiveRecord::Schema.define(version: 20150703084632) do t.string "description" t.string "creator_str" t.string "updator_str" + t.string "activity_type" end add_index "account_activities", ["account_id"], name: "index_account_activities_on_account_id", using: :btree @@ -926,14 +927,14 @@ ActiveRecord::Schema.define(version: 20150703084632) do end create_table "que_jobs", id: false, force: :cascade do |t| - t.integer "priority", limit: 2, default: 100, null: false - t.datetime "run_at", default: "now()", null: false - t.integer "job_id", limit: 8, default: "nextval('que_jobs_job_id_seq'::regclass)", null: false - t.text "job_class", null: false - t.json "args", default: [], null: false - t.integer "error_count", default: 0, null: false + t.integer "priority", limit: 2, default: 100, null: false + t.datetime "run_at", default: '2015-06-30 14:16:50', null: false + t.integer "job_id", limit: 8, default: 0, null: false + t.text "job_class", null: false + t.json "args", default: [], null: false + t.integer "error_count", default: 0, null: false t.text "last_error" - t.text "queue", default: "", null: false + t.text "queue", default: "", null: false end create_table "registrant_verifications", force: :cascade do |t| diff --git a/db/structure.sql b/db/structure.sql index 1bceb10cc..7b0a41ae6 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -205,7 +205,8 @@ CREATE TABLE account_activities ( updated_at timestamp without time zone, description character varying, creator_str character varying, - updator_str character varying + updator_str character varying, + activity_type character varying ); @@ -2395,8 +2396,8 @@ ALTER SEQUENCE pricelists_id_seq OWNED BY pricelists.id; CREATE TABLE que_jobs ( priority smallint DEFAULT 100 NOT NULL, - run_at timestamp with time zone DEFAULT now() NOT NULL, - job_id bigint NOT NULL, + run_at timestamp without time zone DEFAULT '2015-06-30 14:16:50.905537'::timestamp without time zone NOT NULL, + job_id bigint DEFAULT 0 NOT NULL, job_class text NOT NULL, args json DEFAULT '[]'::json NOT NULL, error_count integer DEFAULT 0 NOT NULL, @@ -2405,32 +2406,6 @@ CREATE TABLE que_jobs ( ); --- --- Name: TABLE que_jobs; Type: COMMENT; Schema: public; Owner: - --- - -COMMENT ON TABLE que_jobs IS '3'; - - --- --- Name: que_jobs_job_id_seq; Type: SEQUENCE; Schema: public; Owner: - --- - -CREATE SEQUENCE que_jobs_job_id_seq - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; - - --- --- Name: que_jobs_job_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - --- - -ALTER SEQUENCE que_jobs_job_id_seq OWNED BY que_jobs.job_id; - - -- -- Name: registrant_verifications; Type: TABLE; Schema: public; Owner: -; Tablespace: -- @@ -3186,13 +3161,6 @@ ALTER TABLE ONLY people ALTER COLUMN id SET DEFAULT nextval('people_id_seq'::reg ALTER TABLE ONLY pricelists ALTER COLUMN id SET DEFAULT nextval('pricelists_id_seq'::regclass); --- --- Name: job_id; Type: DEFAULT; Schema: public; Owner: - --- - -ALTER TABLE ONLY que_jobs ALTER COLUMN job_id SET DEFAULT nextval('que_jobs_job_id_seq'::regclass); - - -- -- Name: id; Type: DEFAULT; Schema: public; Owner: - -- @@ -3712,14 +3680,6 @@ ALTER TABLE ONLY pricelists ADD CONSTRAINT pricelists_pkey PRIMARY KEY (id); --- --- Name: que_jobs_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: --- - -ALTER TABLE ONLY que_jobs - ADD CONSTRAINT que_jobs_pkey PRIMARY KEY (queue, priority, run_at, job_id); - - -- -- Name: registrant_verifications_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: -- @@ -4849,3 +4809,4 @@ INSERT INTO schema_migrations (version) VALUES ('20150701074344'); INSERT INTO schema_migrations (version) VALUES ('20150703084632'); +INSERT INTO schema_migrations (version) VALUES ('20150706091724'); diff --git a/spec/epp/domain_spec.rb b/spec/epp/domain_spec.rb index 159e0a43c..d4430ac15 100644 --- a/spec/epp/domain_spec.rb +++ b/spec/epp/domain_spec.rb @@ -356,12 +356,13 @@ describe 'EPP Domain', epp: true do a = @registrar1.cash_account.account_activities.last a.description.should == "Create #{Domain.last.name}" a.sum.should == -BigDecimal.new('10.0') + a.activity_type = AccountActivity::CREATE end it 'creates a domain with longer periods' do old_balance = @registrar1.balance old_activities = @registrar1.cash_account.account_activities.count - xml = domain_create_xml(period: { value: '2', attrs: { unit: 'y' }}) + xml = domain_create_xml(period: { value: '2', attrs: { unit: 'y' } }) response = epp_plain_request(xml) response[:msg].should == 'Command completed successfully' @@ -372,12 +373,13 @@ describe 'EPP Domain', epp: true do a = @registrar1.cash_account.account_activities.last a.description.should == "Create #{Domain.last.name}" a.sum.should == -BigDecimal.new('20.0') + a.activity_type = AccountActivity::CREATE end it 'creates a domain with longer periods' do old_balance = @registrar1.balance old_activities = @registrar1.cash_account.account_activities.count - xml = domain_create_xml(period: { value: '36', attrs: { unit: 'm' }}) + xml = domain_create_xml(period: { value: '36', attrs: { unit: 'm' } }) response = epp_plain_request(xml) response[:msg].should == 'Command completed successfully' @@ -388,6 +390,7 @@ describe 'EPP Domain', epp: true do a = @registrar1.cash_account.account_activities.last a.description.should == "Create #{Domain.last.name}" a.sum.should == -BigDecimal.new('30.0') + a.activity_type = AccountActivity::CREATE end it 'does not create a domain with invalid period' do @@ -2059,6 +2062,7 @@ describe 'EPP Domain', epp: true do a = @registrar1.cash_account.account_activities.last a.description.should == "Renew #{Domain.last.name}" a.sum.should == -BigDecimal.new('15.0') + a.activity_type = AccountActivity::RENEW end it 'renews a domain with 2 year period' do @@ -2089,6 +2093,7 @@ describe 'EPP Domain', epp: true do a = @registrar1.cash_account.account_activities.last a.description.should == "Renew #{Domain.last.name}" a.sum.should == -BigDecimal.new('35.0') + a.activity_type = AccountActivity::CREATE end it 'renews a domain with 3 year period' do @@ -2119,11 +2124,17 @@ describe 'EPP Domain', epp: true do a = @registrar1.cash_account.account_activities.last a.description.should == "Renew #{Domain.last.name}" a.sum.should == -BigDecimal.new('62.0') + a.activity_type = AccountActivity::CREATE end it 'does not renew a domain if credit balance low' do - f = Fabricate(:pricelist, valid_to: Time.zone.now + 1.day, - operation_category: 'renew', duration: '1year', price: 100000) + f = Fabricate(:pricelist, { + valid_to: Time.zone.now + 1.day, + operation_category: 'renew', + duration: '1year', + price: 100000 + }) + old_balance = @registrar1.balance old_activities = @registrar1.cash_account.account_activities.count diff --git a/spec/features/registrar/account_activity_spec.rb b/spec/features/registrar/account_activity_spec.rb index e2bc3be3a..a9cc9c59e 100644 --- a/spec/features/registrar/account_activity_spec.rb +++ b/spec/features/registrar/account_activity_spec.rb @@ -27,5 +27,12 @@ feature 'Account activity', type: :feature do current_path.should == '/registrar/account_activities' page.should have_text('+110.0 EUR') end + + it 'should download csv' do + visit '/registrar/account_activities' + click_link 'Export CSV' + response_headers['Content-Type'].should == 'text/csv' + response_headers['Content-Disposition'].should match(/attachment; filename="account_activities_\d+\.csv"/) + end end end diff --git a/spec/models/bank_statement_spec.rb b/spec/models/bank_statement_spec.rb index d5812fbeb..77ffffd1e 100644 --- a/spec/models/bank_statement_spec.rb +++ b/spec/models/bank_statement_spec.rb @@ -62,6 +62,11 @@ describe BankStatement do AccountActivity.count.should == 1 + a = AccountActivity.last + a.description.should == "Invoice no. #{invoice.number}" + a.sum.should == BigDecimal.new('200.0') + a.activity_type = AccountActivity::ADD_CREDIT + r.cash_account.balance.should == 200.0 bs.bank_transactions.unbinded.count.should == 1 diff --git a/spec/models/domain_spec.rb b/spec/models/domain_spec.rb index 41b0970e7..40deb363a 100644 --- a/spec/models/domain_spec.rb +++ b/spec/models/domain_spec.rb @@ -182,7 +182,7 @@ describe Domain do @domain.force_delete_at.should be_nil end - it 'should know its price' do + it 'should know its create price' do Fabricate(:pricelist, { category: 'ee', operation_category: 'create', @@ -200,6 +200,98 @@ describe Domain do domain = Fabricate(:domain, period: 365, period_unit: 'd') domain.price('create').should == 1.50 + + Fabricate(:pricelist, { + category: 'ee', + operation_category: 'create', + duration: '2years', + price: 3, + valid_from: Time.zone.parse('2015-01-01'), + valid_to: nil + }) + + domain = Fabricate(:domain, period: 2) + domain.price('create').should == 3.0 + + domain = Fabricate(:domain, period: 24, period_unit: 'm') + domain.price('create').should == 3.0 + + domain = Fabricate(:domain, period: 730, period_unit: 'd') + domain.price('create').should == 3.0 + + Fabricate(:pricelist, { + category: 'ee', + operation_category: 'create', + duration: '3years', + price: 6, + valid_from: Time.zone.parse('2015-01-01'), + valid_to: nil + }) + + domain = Fabricate(:domain, period: 3) + domain.price('create').should == 6.0 + + domain = Fabricate(:domain, period: 36, period_unit: 'm') + domain.price('create').should == 6.0 + + domain = Fabricate(:domain, period: 1095, period_unit: 'd') + domain.price('create').should == 6.0 + end + + it 'should know its renew price' do + Fabricate(:pricelist, { + category: 'ee', + operation_category: 'renew', + duration: '1year', + price: 1.30, + valid_from: Time.zone.parse('2015-01-01'), + valid_to: nil + }) + + domain = Fabricate(:domain) + domain.price('renew').should == 1.30 + + domain = Fabricate(:domain, period: 12, period_unit: 'm') + domain.price('renew').should == 1.30 + + domain = Fabricate(:domain, period: 365, period_unit: 'd') + domain.price('renew').should == 1.30 + + Fabricate(:pricelist, { + category: 'ee', + operation_category: 'renew', + duration: '2years', + price: 3.1, + valid_from: Time.zone.parse('2015-01-01'), + valid_to: nil + }) + + domain = Fabricate(:domain, period: 2) + domain.price('renew').should == 3.1 + + domain = Fabricate(:domain, period: 24, period_unit: 'm') + domain.price('renew').should == 3.1 + + domain = Fabricate(:domain, period: 730, period_unit: 'd') + domain.price('renew').should == 3.1 + + Fabricate(:pricelist, { + category: 'ee', + operation_category: 'renew', + duration: '3years', + price: 6.1, + valid_from: Time.zone.parse('2015-01-01'), + valid_to: nil + }) + + domain = Fabricate(:domain, period: 3) + domain.price('renew').should == 6.1 + + domain = Fabricate(:domain, period: 36, period_unit: 'm') + domain.price('renew').should == 6.1 + + domain = Fabricate(:domain, period: 1095, period_unit: 'd') + domain.price('renew').should == 6.1 end context 'about registrant update confirm' do diff --git a/spec/models/pricelist_spec.rb b/spec/models/pricelist_spec.rb index d6dc79070..4e841ed94 100644 --- a/spec/models/pricelist_spec.rb +++ b/spec/models/pricelist_spec.rb @@ -159,5 +159,16 @@ describe Pricelist do }) Pricelist.price_for('ee', 'create', '1year').should == 1.10 + + Fabricate.create(:pricelist, { + category: 'ee', + operation_category: 'create', + duration: '2years', + price: 1.20, + valid_from: Time.zone.parse('2015-07-01'), + valid_to: Time.zone.parse('2999-01-01') + }) + + Pricelist.price_for('ee', 'create', '2years').should == 1.20 end end