diff --git a/.ruby-version b/.ruby-version index e30309f73..57cf282eb 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.4.7 +2.6.5 diff --git a/CHANGELOG.md b/CHANGELOG.md index c0670abc8..cdc184c6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,71 @@ +30.04.2020 +* Fix for internal error on opening domain history with legacy id record [#1576](https://github.com/internetee/registry/issues/1576) + +27.04.2020 +* Downgrade SimpleCov to 0.17 due to incompatibiilty with CodeClimate [#1575](https://github.com/internetee/registry/pull/1575) + +17.04.2020 +* Webinterfaces have now clickable version string pointing to the latest deployed commit in github [#1345](https://github.com/internetee/registry/pull/1345) + +15.04.2020 +* Updated Rails to 5.2 and fixed acitionview security issue [#1568](https://github.com/internetee/registry/issues/1568) + +25.03.2020 +* Implemented Directo gem [#1547](https://github.com/internetee/registry/pull/1547) + +11.03.2020 +* Fixed glue record issues when using 2nd level domain as host [#1562](https://github.com/internetee/registry/issues/1562) + +10.03.2020 +* Updated lhv, e-invoice & company_register gem due to security updates [#1564](https://github.com/internetee/registry/pull/1564) + +06.03.2020 +* Record payment method and failed payments [#1422](https://github.com/internetee/registry/issues/1422) + +04.03.2020 +* Bump Puma to 4.3.3 [#1557](https://github.com/internetee/registry/pull/1557) + +03.03.2020 +* Admin: fixed import of th6 bank statement [#1551](https://github.com/internetee/registry/issues/1551) + +02.03.2020 +* Registrar: fixed statuses based contact filtering [#1004](https://github.com/internetee/registry/issues/1004) + +28.02.2020 +* Registrar: fixed account switching [#1535](https://github.com/internetee/registry/issues/1535) + +27.02.2020 +* Registrar: fixed the verified checkbox bug that did not change the element value to yes in epp request [#1540](https://github.com/internetee/registry/issues/1540) +* Ruby version update to 2.6.5 [#1545](https://github.com/internetee/registry/pull/1545) + +26.02.2020 +* Registrar: added an option to remove clientHold status [#1481](https://github.com/internetee/registry/issues/1481) +* Admin: fixed domain status removal issue [#1543](https://github.com/internetee/registry/issues/1543) +* Implemented consistent and automated data migrations [#1298](https://github.com/internetee/registry/issues/1298) + +20.02.2020 +* E-invoice sending to Que to manage resending in case of an error [#1509](https://github.com/internetee/registry/issues/1509) +* Check to make sure all monthly invoices fit in available invoice number range [#277](https://github.com/internetee/registry/issues/277) +* Disabled aurbreak performance monitoring [#1534](https://github.com/internetee/registry/pull/1534) + +14.02.2020 +* Fixed Papertrail warnings [#1530](https://github.com/internetee/registry/issues/1530) + +12.02.2020 +* Fixed papertrails double recording issue [#1526](https://github.com/internetee/registry/issues/1526) +* Requests to Directo are now saved for both credit and monthly invoices [#344](https://github.com/internetee/registry/issues/344) + +10.02.2020 +* Resolved Money gem deprecation warning and silenced all warnings due plan to replace papertrail [#1522](https://github.com/internetee/registry/pull/1522) + +06.02.2020 +* Permit & turn ActiveController::Parameters to hash on domain create [#1516](https://github.com/internetee/registry/issues/1516) + +05.02.2020 +* Ruby version upgrade to 2.6.3 [#846](https://github.com/internetee/registry/issues/846) +* Added retries & raise to connect api to handle timeouts [#1474](https://github.com/internetee/registry/issues/1474) +* Added logging of XML if there is NoMethodError#text on xml data fields [#1475](https://github.com/internetee/registry/issues/1475) + 04.02.2020 * Fixed bug that allowed bypassing blocked domain validation using punycode [#1142](https://github.com/internetee/registry/issues/1142) * SimpleIDN gem update to 0.0.9 [#1508](https://github.com/internetee/registry/pull/1508) diff --git a/Dockerfile b/Dockerfile index 7e3b11d80..5d241eeef 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM internetee/ruby:2.4 +FROM internetee/ruby:2.6-buster RUN mkdir -p /opt/webapps/app/tmp/pids WORKDIR /opt/webapps/app diff --git a/Gemfile b/Gemfile index b3882e792..851094689 100644 --- a/Gemfile +++ b/Gemfile @@ -1,8 +1,8 @@ source 'https://rubygems.org' # core -gem 'iso8601', '0.8.6' # for dates and times -gem 'rails', '~> 5.0.7' +gem 'iso8601', '0.12.1' # for dates and times +gem 'rails', '~> 5.2.4.2' gem 'rest-client' gem 'uglifier' @@ -10,11 +10,11 @@ gem 'uglifier' gem 'figaro', '1.1.1' # model related -gem 'pg', '0.19.0' +gem 'paper_trail', '~> 9.2' +gem 'pg', '1.2.2' # 1.8 is for Rails < 5.0 -gem 'ransack', '~> 1.8' +gem 'ransack', '~> 2.3' gem 'validates_email_format_of', '1.6.3' # validates email against RFC 2822 and RFC 3696 -gem 'paper_trail', '~> 4.0' # 0.7.3 is the latest for Rails 4.2, however, it is absent on Rubygems server # https://github.com/huacnlee/rails-settings-cached/issues/165 @@ -36,10 +36,10 @@ gem 'devise', '~> 4.7' gem 'grape' # registry specfic +gem 'data_migrate', '~> 6.1' gem 'isikukood' # for EE-id validation gem 'simpleidn', '0.0.9' # For punycode gem 'money-rails' -gem 'data_migrate' gem 'whenever', '0.9.4', require: false # country listing @@ -63,11 +63,13 @@ gem 'airbrake' gem 'company_register', github: 'internetee/company_register', branch: :master gem 'e_invoice', github: 'internetee/e_invoice', branch: :master -gem 'lhv', github: 'internetee/lhv', tag: 'v0.1.0' +gem 'lhv', github: 'internetee/lhv', branch: :master gem 'domain_name' gem 'haml', '~> 5.0' gem 'wkhtmltopdf-binary' +gem 'directo', github: 'internetee/directo', branch: 'master' + group :development do # deploy gem 'mina', '0.3.1' # for fast deployment @@ -84,8 +86,8 @@ end group :test do gem 'capybara' gem 'database_cleaner' - gem 'simplecov', require: false + gem 'minitest', '~> 5.14' + gem 'simplecov', '0.17.1', require: false # CC last supported v0.17 gem 'webdrivers' gem 'webmock' - gem 'minitest', '~> 5.10.0' end diff --git a/Gemfile.lock b/Gemfile.lock index 23aa90a51..fe737f7ac 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,15 +1,24 @@ GIT remote: https://github.com/internetee/company_register.git - revision: da7130542304fc543c90d54cd037d019a777c526 + revision: 86d691997aa7def9f86d88f6c92cabb86cd65487 branch: master specs: company_register (0.1.0) activesupport savon +GIT + remote: https://github.com/internetee/directo.git + revision: 8cb63d2fb91c640b264d5af05f4a6afbcfd46979 + branch: master + specs: + directo (1.0.0) + money (~> 6.13) + nokogiri (~> 1.10) + GIT remote: https://github.com/internetee/e_invoice.git - revision: 3a754974ed25569aa85d99a87ae9e131b7c10a24 + revision: b374ffd7be77b559b30c7a0210dc0df5ac3ed723 branch: master specs: e_invoice (0.1.0) @@ -27,7 +36,7 @@ GIT GIT remote: https://github.com/internetee/epp.git - revision: 76f9fd487d0ca3865b6f706c5a72703951c03996 + revision: af7cefda37ac81d14b1d12641cde410776082d59 branch: master specs: epp (1.5.0) @@ -36,11 +45,12 @@ GIT GIT remote: https://github.com/internetee/lhv.git - revision: e211516bc5fff2139584d4da41c17511863c229d - tag: v0.1.0 + revision: cf0d30816ca9cd6a35d206aa1007c378f8acb502 + branch: master specs: lhv (0.1.0) keystores + logger nokogiri GIT @@ -58,70 +68,70 @@ GIT GEM remote: https://rubygems.org/ specs: - actioncable (5.0.7.2) - actionpack (= 5.0.7.2) - nio4r (>= 1.2, < 3.0) - websocket-driver (~> 0.6.1) - actionmailer (5.0.7.2) - actionpack (= 5.0.7.2) - actionview (= 5.0.7.2) - activejob (= 5.0.7.2) + actioncable (5.2.4.2) + actionpack (= 5.2.4.2) + nio4r (~> 2.0) + websocket-driver (>= 0.6.1) + actionmailer (5.2.4.2) + actionpack (= 5.2.4.2) + actionview (= 5.2.4.2) + activejob (= 5.2.4.2) mail (~> 2.5, >= 2.5.4) rails-dom-testing (~> 2.0) - actionpack (5.0.7.2) - actionview (= 5.0.7.2) - activesupport (= 5.0.7.2) - rack (~> 2.0) - rack-test (~> 0.6.3) + actionpack (5.2.4.2) + actionview (= 5.2.4.2) + activesupport (= 5.2.4.2) + rack (~> 2.0, >= 2.0.8) + rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.2) - actionview (5.0.7.2) - activesupport (= 5.0.7.2) + actionview (5.2.4.2) + activesupport (= 5.2.4.2) builder (~> 3.1) - erubis (~> 2.7.0) + erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.3) - activejob (5.0.7.2) - activesupport (= 5.0.7.2) + activejob (5.2.4.2) + activesupport (= 5.2.4.2) globalid (>= 0.3.6) - activemodel (5.0.7.2) - activesupport (= 5.0.7.2) - activerecord (5.0.7.2) - activemodel (= 5.0.7.2) - activesupport (= 5.0.7.2) - arel (~> 7.0) - activesupport (5.0.7.2) + activemodel (5.2.4.2) + activesupport (= 5.2.4.2) + activerecord (5.2.4.2) + activemodel (= 5.2.4.2) + activesupport (= 5.2.4.2) + arel (>= 9.0) + activestorage (5.2.4.2) + actionpack (= 5.2.4.2) + activerecord (= 5.2.4.2) + marcel (~> 0.3.1) + activesupport (5.2.4.2) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 0.7, < 2) minitest (~> 5.1) tzinfo (~> 1.1) addressable (2.7.0) public_suffix (>= 2.0.2, < 5.0) - airbrake (9.4.5) - airbrake-ruby (~> 4.7) - airbrake-ruby (4.7.1) + airbrake (10.0.1) + airbrake-ruby (~> 4.13) + airbrake-ruby (4.13.0) rbtree3 (~> 0.5) akami (1.3.1) gyoku (>= 0.4.0) nokogiri - arel (7.1.4) + arel (9.0.0) autodoc (0.7.3) actionpack activesupport (>= 3.0.0) rspec - autoprefixer-rails (9.6.5) + autoprefixer-rails (9.7.4) execjs - axiom-types (0.1.1) - descendants_tracker (~> 0.0.4) - ice_nine (~> 0.11.0) - thread_safe (~> 0.3, >= 0.3.1) bcrypt (3.1.13) bootstrap-sass (3.4.1) autoprefixer-rails (>= 5.2.1) sassc (>= 2.0.0) - builder (3.2.3) - cancancan (3.0.1) - capybara (3.29.0) + builder (3.2.4) + cancancan (3.0.2) + capybara (3.31.0) addressable mini_mime (>= 0.1.3) nokogiri (~> 1.8) @@ -132,8 +142,6 @@ GEM childprocess (3.0.0) chronic (0.10.2) coderay (1.1.0) - coercible (1.0.0) - descendants_tracker (~> 0.0.1) coffee-rails (4.2.2) coffee-script (>= 2.2.0) railties (>= 4.0.0) @@ -141,23 +149,21 @@ GEM coffee-script-source execjs coffee-script-source (1.12.2) - concurrent-ruby (1.1.5) + concurrent-ruby (1.1.6) countries (3.0.0) i18n_data (~> 0.8.0) sixarm_ruby_unaccent (~> 1.1) unicode_utils (~> 1.4) crack (0.4.3) safe_yaml (~> 1.0.0) - crass (1.0.5) + crass (1.0.6) daemons (1.3.1) daemons-rails (1.2.1) daemons multi_json (~> 1.0) - data_migrate (6.0.5) + data_migrate (6.2.0) rails (>= 5.0) - database_cleaner (1.7.0) - descendants_tracker (0.0.4) - thread_safe (~> 0.3, >= 0.3.1) + database_cleaner (1.8.2) devise (4.7.1) bcrypt (~> 3.0) orm_adapter (~> 0.1) @@ -168,21 +174,42 @@ GEM docile (1.3.2) domain_name (0.5.20190701) unf (>= 0.0.5, < 1.0.0) - equalizer (0.0.11) + dry-configurable (0.9.0) + concurrent-ruby (~> 1.0) + dry-core (~> 0.4, >= 0.4.7) + dry-container (0.7.2) + concurrent-ruby (~> 1.0) + dry-configurable (~> 0.1, >= 0.1.3) + dry-core (0.4.9) + concurrent-ruby (~> 1.0) + dry-equalizer (0.3.0) + dry-inflector (0.2.0) + dry-logic (1.0.5) + concurrent-ruby (~> 1.0) + dry-core (~> 0.2) + dry-equalizer (~> 0.2) + dry-types (1.2.2) + concurrent-ruby (~> 1.0) + dry-container (~> 0.3) + dry-core (~> 0.4, >= 0.4.4) + dry-equalizer (~> 0.3) + dry-inflector (~> 0.1, >= 0.1.2) + dry-logic (~> 1.0, >= 1.0.2) + erubi (1.9.0) erubis (2.7.0) execjs (2.7.0) - ffi (1.11.1) + ffi (1.12.2) figaro (1.1.1) thor (~> 0.14) globalid (0.4.2) activesupport (>= 4.2.0) - grape (1.2.4) + grape (1.3.0) activesupport builder + dry-types (>= 1.1) mustermann-grape (~> 1.0.0) rack (>= 1.3.0) rack-accept - virtus (>= 1.0.0) gyoku (1.3.1) builder (>= 2.1.2) haml (5.1.2) @@ -197,12 +224,11 @@ GEM httpi (2.4.4) rack socksify - i18n (1.7.0) + i18n (1.8.2) concurrent-ruby (~> 1.0) i18n_data (0.8.0) - ice_nine (0.11.2) isikukood (0.1.2) - iso8601 (0.8.6) + iso8601 (0.12.1) jquery-rails (4.3.5) rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) @@ -210,92 +236,101 @@ GEM jquery-ui-rails (5.0.5) railties (>= 3.2.16) json (1.8.6) - kaminari (1.1.1) + kaminari (1.2.0) activesupport (>= 4.1.0) - kaminari-actionview (= 1.1.1) - kaminari-activerecord (= 1.1.1) - kaminari-core (= 1.1.1) - kaminari-actionview (1.1.1) + kaminari-actionview (= 1.2.0) + kaminari-activerecord (= 1.2.0) + kaminari-core (= 1.2.0) + kaminari-actionview (1.2.0) actionview - kaminari-core (= 1.1.1) - kaminari-activerecord (1.1.1) + kaminari-core (= 1.2.0) + kaminari-activerecord (1.2.0) activerecord - kaminari-core (= 1.1.1) - kaminari-core (1.1.1) + kaminari-core (= 1.2.0) + kaminari-core (1.2.0) keystores (0.4.0) libxml-ruby (3.1.0) - loofah (2.3.1) + logger (1.4.2) + loofah (2.5.0) crass (~> 1.0.2) nokogiri (>= 1.5.9) mail (2.7.1) mini_mime (>= 0.1.1) + marcel (0.3.3) + mimemagic (~> 0.3.2) method_source (0.8.2) - mime-types (3.3) + mime-types (3.3.1) mime-types-data (~> 3.2015) mime-types-data (3.2019.1009) + mimemagic (0.3.4) mina (0.3.1) open4 (~> 1.3.4) rake mini_mime (1.0.2) mini_portile2 (2.4.0) - minitest (5.10.3) - monetize (1.9.2) + minitest (5.14.0) + monetize (1.9.4) money (~> 6.12) - money (6.13.4) + money (6.13.7) i18n (>= 0.6.4, <= 2) - money-rails (1.13.2) + money-rails (1.13.3) activesupport (>= 3.0) monetize (~> 1.9.0) money (~> 6.13.2) railties (>= 3.0) multi_json (1.14.1) - mustermann (1.0.3) - mustermann-grape (1.0.0) - mustermann (~> 1.0.0) + mustermann (1.1.1) + ruby2_keywords (~> 0.0.1) + mustermann-grape (1.0.1) + mustermann (>= 1.0.0) netrc (0.11.0) nio4r (2.5.2) - nokogiri (1.10.4) + nokogiri (1.10.9) mini_portile2 (~> 2.4.0) nori (2.6.0) open4 (1.3.4) orm_adapter (0.5.0) - paper_trail (4.2.0) - activerecord (>= 3.0, < 6.0) - activesupport (>= 3.0, < 6.0) + paper_trail (9.2.0) + activerecord (>= 4.2, < 5.3) + paper_trail-association_tracking (< 2) request_store (~> 1.1) + paper_trail-association_tracking (1.1.1) pdfkit (0.8.4.1) - pg (0.19.0) + pg (1.2.2) + polyamorous (2.3.2) + activerecord (>= 5.2.1) pry (0.10.1) coderay (~> 1.1.0) method_source (~> 0.8.1) slop (~> 3.4) - public_suffix (4.0.1) - puma (4.3.1) + public_suffix (4.0.3) + puma (4.3.3) nio4r (~> 2.0) que (0.14.3) que-web (0.7.2) erubis que (~> 0.8) sinatra - rack (2.0.8) + rack (2.2.2) rack-accept (0.4.5) rack (>= 0.4) - rack-protection (2.0.7) + rack-protection (2.0.8.1) rack - rack-test (0.6.3) - rack (>= 1.0) + rack-test (1.1.0) + rack (>= 1.0, < 3) railroady (1.3.0) - rails (5.0.7.2) - actioncable (= 5.0.7.2) - actionmailer (= 5.0.7.2) - actionpack (= 5.0.7.2) - actionview (= 5.0.7.2) - activejob (= 5.0.7.2) - activemodel (= 5.0.7.2) - activerecord (= 5.0.7.2) - activesupport (= 5.0.7.2) + rails (5.2.4.2) + actioncable (= 5.2.4.2) + actionmailer (= 5.2.4.2) + actionpack (= 5.2.4.2) + actionview (= 5.2.4.2) + activejob (= 5.2.4.2) + activemodel (= 5.2.4.2) + activerecord (= 5.2.4.2) + activestorage (= 5.2.4.2) + activesupport (= 5.2.4.2) bundler (>= 1.3.0) - railties (= 5.0.7.2) + railties (= 5.2.4.2) sprockets-rails (>= 2.0.0) rails-dom-testing (2.0.3) activesupport (>= 4.2.0) @@ -304,25 +339,25 @@ GEM loofah (~> 2.3) rails-settings-cached (0.7.2) rails (>= 4.2.0) - railties (5.0.7.2) - actionpack (= 5.0.7.2) - activesupport (= 5.0.7.2) + railties (5.2.4.2) + actionpack (= 5.2.4.2) + activesupport (= 5.2.4.2) method_source rake (>= 0.8.7) - thor (>= 0.18.1, < 2.0) - rake (13.0.0) - ransack (1.8.10) - actionpack (>= 3.0, < 5.2) - activerecord (>= 3.0, < 5.2) - activesupport (>= 3.0, < 5.2) + thor (>= 0.19.0, < 2.0) + rake (13.0.1) + ransack (2.3.2) + activerecord (>= 5.2.1) + activesupport (>= 5.2.1) i18n + polyamorous (= 2.3.2) rb-fsevent (0.10.3) - rb-inotify (0.10.0) + rb-inotify (0.10.1) ffi (~> 1.0) - rbtree3 (0.5.0) + rbtree3 (0.6.0) rdoc (4.3.0) regexp_parser (1.6.0) - request_store (1.4.1) + request_store (1.5.0) rack (>= 1.4) responders (3.0.0) actionpack (>= 5.0) @@ -336,16 +371,17 @@ GEM rspec-core (~> 3.9.0) rspec-expectations (~> 3.9.0) rspec-mocks (~> 3.9.0) - rspec-core (3.9.0) - rspec-support (~> 3.9.0) + rspec-core (3.9.1) + rspec-support (~> 3.9.1) rspec-expectations (3.9.0) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.9.0) - rspec-mocks (3.9.0) + rspec-mocks (3.9.1) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.9.0) - rspec-support (3.9.0) - rubyzip (2.0.0) + rspec-support (3.9.2) + ruby2_keywords (0.0.2) + rubyzip (2.2.0) safe_yaml (1.0.5) sass (3.7.4) sass-listen (~> 4.0.0) @@ -374,7 +410,7 @@ GEM select2-rails (3.5.9.3) thor (~> 0.14) selectize-rails (0.12.1) - selenium-webdriver (3.142.6) + selenium-webdriver (3.142.7) childprocess (>= 0.5, < 4.0) rubyzip (>= 1.2.2) simplecov (0.17.1) @@ -383,10 +419,10 @@ GEM simplecov-html (~> 0.10.0) simplecov-html (0.10.2) simpleidn (0.0.9) - sinatra (2.0.7) + sinatra (2.0.8.1) mustermann (~> 1.0) rack (~> 2.0) - rack-protection (= 2.0.7) + rack-protection (= 2.0.8.1) tilt (~> 2.0) sixarm_ruby_unaccent (1.2.0) slop (3.6.0) @@ -402,7 +438,7 @@ GEM thor (0.20.3) thread_safe (0.3.6) tilt (2.0.10) - tzinfo (1.2.5) + tzinfo (1.2.7) thread_safe (~> 0.1) uglifier (4.2.0) execjs (>= 0.3.0, < 3) @@ -412,30 +448,25 @@ GEM unicode_utils (1.4.0) validates_email_format_of (1.6.3) i18n - virtus (1.0.5) - axiom-types (~> 0.1) - coercible (~> 1.0) - descendants_tracker (~> 0.0, >= 0.0.3) - equalizer (~> 0.0, >= 0.0.9) warden (1.2.8) rack (>= 2.0.6) wasabi (3.5.0) httpi (~> 2.0) nokogiri (>= 1.4.2) - webdrivers (4.1.3) + webdrivers (4.2.0) nokogiri (~> 1.6) rubyzip (>= 1.3.0) selenium-webdriver (>= 3.0, < 4.0) - webmock (3.7.6) + webmock (3.8.0) addressable (>= 2.3.6) crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) - websocket-driver (0.6.5) + websocket-driver (0.7.1) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.4) whenever (0.9.4) chronic (>= 0.6.3) - wkhtmltopdf-binary (0.12.4) + wkhtmltopdf-binary (0.12.5.1) xpath (3.2.0) nokogiri (~> 1.8) @@ -453,10 +484,11 @@ DEPENDENCIES company_register! countries daemons-rails (= 1.2.1) - data_migrate + data_migrate (~> 6.1) database_cleaner devise (~> 4.7) digidoc_client! + directo! domain_name e_invoice! epp! @@ -465,32 +497,32 @@ DEPENDENCIES grape haml (~> 5.0) isikukood - iso8601 (= 0.8.6) + iso8601 (= 0.12.1) jquery-rails jquery-ui-rails (= 5.0.5) kaminari lhv! mina (= 0.3.1) - minitest (~> 5.10.0) + minitest (~> 5.14) money-rails nokogiri - paper_trail (~> 4.0) + paper_trail (~> 9.2) pdfkit - pg (= 0.19.0) + pg (= 1.2.2) pry (= 0.10.1) puma que que-web railroady (= 1.3.0) - rails (~> 5.0.7) + rails (~> 5.2.4.2) rails-settings-cached (= 0.7.2) - ransack (~> 1.8) + ransack (~> 2.3) rest-client sass-rails (= 5.0.6) sdoc (= 0.4.1) select2-rails (= 3.5.9.3) selectize-rails (= 0.12.1) - simplecov + simplecov (= 0.17.1) simpleidn (= 0.0.9) uglifier validates_email_format_of (= 1.6.3) @@ -500,4 +532,4 @@ DEPENDENCIES wkhtmltopdf-binary BUNDLED WITH - 2.0.2 + 2.1.4 diff --git a/app/assets/stylesheets/shared/general.sass b/app/assets/stylesheets/shared/general.sass index 07b92cdbe..6d1c4956d 100644 --- a/app/assets/stylesheets/shared/general.sass +++ b/app/assets/stylesheets/shared/general.sass @@ -15,6 +15,9 @@ body > .container padding-top: 15px font-size: 10px +a.footer-version-link + color: black + .nowrap white-space: nowrap diff --git a/app/controllers/admin/bank_statements_controller.rb b/app/controllers/admin/bank_statements_controller.rb index a70387317..1e3b31bf5 100644 --- a/app/controllers/admin/bank_statements_controller.rb +++ b/app/controllers/admin/bank_statements_controller.rb @@ -60,7 +60,7 @@ module Admin end def bind_invoices - @bank_statement.bind_invoices + @bank_statement.bind_invoices(manual: true) flash[:notice] = t('invoices_were_fully_binded') if @bank_statement.fully_binded? flash[:warning] = t('invoices_were_partially_binded') if @bank_statement.partially_binded? diff --git a/app/controllers/admin/bank_transactions_controller.rb b/app/controllers/admin/bank_transactions_controller.rb index 1ce62b279..348cadc64 100644 --- a/app/controllers/admin/bank_transactions_controller.rb +++ b/app/controllers/admin/bank_transactions_controller.rb @@ -34,7 +34,7 @@ module Admin end def bind - if @bank_transaction.bind_invoice(params[:invoice_no]) + if @bank_transaction.bind_invoice(params[:invoice_no], manual: true) flash[:notice] = I18n.t('record_created') redirect_to [:admin, @bank_transaction] else diff --git a/app/controllers/admin/zonefiles_controller.rb b/app/controllers/admin/zonefiles_controller.rb index 30b4b9a61..4fd84bb77 100644 --- a/app/controllers/admin/zonefiles_controller.rb +++ b/app/controllers/admin/zonefiles_controller.rb @@ -13,7 +13,7 @@ module Admin send_data @zonefile, filename: "#{params[:origin]}.txt" else flash[:alert] = 'Origin not supported' - redirect_to :back + redirect_back(fallback_location: root_path) end end end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 1a57b5573..5a6b45668 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,5 +1,6 @@ class ApplicationController < ActionController::Base check_authorization unless: :devise_controller? + before_action :set_paper_trail_whodunnit # Prevent CSRF attacks by raising an exception. # For APIs, you may want to use :null_session instead. @@ -32,4 +33,4 @@ class ApplicationController < ActionController::Base def available_languages { en: 'English', et: 'Estonian' }.invert end -end \ No newline at end of file +end diff --git a/app/controllers/epp/base_controller.rb b/app/controllers/epp/base_controller.rb index 6e3f39e46..fb48c285a 100644 --- a/app/controllers/epp/base_controller.rb +++ b/app/controllers/epp/base_controller.rb @@ -10,7 +10,7 @@ module Epp before_action :latin_only before_action :validate_against_schema before_action :validate_request - before_action :update_epp_session, if: 'signed_in?' + before_action :update_epp_session, if: -> { signed_in? } around_action :wrap_exceptions diff --git a/app/controllers/epp/domains_controller.rb b/app/controllers/epp/domains_controller.rb index 1295f558e..9e4f6123a 100644 --- a/app/controllers/epp/domains_controller.rb +++ b/app/controllers/epp/domains_controller.rb @@ -2,6 +2,7 @@ module Epp class DomainsController < BaseController before_action :find_domain, only: %i[info renew update transfer delete] before_action :find_password, only: %i[info update transfer delete] + before_action :set_paper_trail_whodunnit def info authorize! :info, @domain diff --git a/app/controllers/epp/sessions_controller.rb b/app/controllers/epp/sessions_controller.rb index 2175f7281..ef8f125ee 100644 --- a/app/controllers/epp/sessions_controller.rb +++ b/app/controllers/epp/sessions_controller.rb @@ -1,6 +1,7 @@ module Epp class SessionsController < BaseController skip_authorization_check only: [:hello, :login, :logout] + before_action :set_paper_trail_whodunnit def hello render_epp_response('greeting') diff --git a/app/controllers/registrar/contacts_controller.rb b/app/controllers/registrar/contacts_controller.rb index bd1e29a7c..18af3a29f 100644 --- a/app/controllers/registrar/contacts_controller.rb +++ b/app/controllers/registrar/contacts_controller.rb @@ -17,12 +17,13 @@ class Registrar search_params[:registrant_domains_id_not_null] = 1 end - if params[:statuses_contains] - contacts = current_registrar_user.registrar.contacts.includes(:registrar).where( - "contacts.statuses @> ?::varchar[]", "{#{params[:statuses_contains].join(',')}}" - ) - else - contacts = current_registrar_user.registrar.contacts.includes(:registrar) + contacts = current_registrar_user.registrar.contacts.includes(:registrar) + status_list = params[:statuses_contains] + + if status_list + contacts_ids = contacts.select { |c| (c.statuses & status_list.to_a) == status_list.to_a } + .map(&:id) + contacts = contacts.where(id: contacts_ids) end normalize_search_parameters do diff --git a/app/controllers/registrar/current_user_controller.rb b/app/controllers/registrar/current_user_controller.rb index 624ee294e..3a214322c 100644 --- a/app/controllers/registrar/current_user_controller.rb +++ b/app/controllers/registrar/current_user_controller.rb @@ -6,7 +6,7 @@ class Registrar raise 'Cannot switch to unlinked user' unless current_registrar_user.linked_with?(new_user) sign_in(:registrar_user, new_user) - redirect_to :back, notice: t('.switched', new_user: new_user) + redirect_back(fallback_location: root_path, notice: t('.switched', new_user: new_user)) end private diff --git a/app/controllers/registrar/domains_controller.rb b/app/controllers/registrar/domains_controller.rb index f533a874c..e6e1029f6 100644 --- a/app/controllers/registrar/domains_controller.rb +++ b/app/controllers/registrar/domains_controller.rb @@ -59,6 +59,7 @@ class Registrar def info authorize! :info, Depp::Domain @data = @domain.info(params[:domain_name]) if params[:domain_name] + @client_holded = client_holded(@data) if response_ok? render 'info' else @@ -85,7 +86,7 @@ class Registrar def create authorize! :create, Depp::Domain - @domain_params = params[:domain] + @domain_params = domain_params.to_h @data = @domain.create(@domain_params) if response_ok? @@ -153,12 +154,26 @@ class Registrar render json: scope.pluck(:name, :code).map { |c| { display_key: "#{c.second} #{c.first}", value: c.second } } end + def remove_hold + authorize! :remove_hold, Depp::Domain + return unless params[:domain_name] + + @data = @domain.remove_hold(params) + + flash[:alert] = @data.css('msg').text unless response_ok? + redirect_to info_registrar_domains_url(domain_name: params[:domain_name]) + end + private def init_domain @domain = Depp::Domain.new(current_user: depp_current_user) end + def client_holded(data) + data.css('status')&.map { |element| element.attribute('s').value } + &.any? { |status| status == DomainStatus::CLIENT_HOLD } + end def contacts current_registrar_user.registrar.contacts @@ -187,5 +202,12 @@ class Registrar :valid_to_lteq, :s) end + + def domain_params + params.require(:domain).permit(:name, :period, :registrant, :registrant_helper, :reserved_pw, + :verified, :legal_document, contacts_attributes: {}, + nameservers_attributes: {}, + dnskeys_attributes: {}) + end end end diff --git a/app/controllers/registrar/payments_controller.rb b/app/controllers/registrar/payments_controller.rb index 8df9bb046..598d13446 100644 --- a/app/controllers/registrar/payments_controller.rb +++ b/app/controllers/registrar/payments_controller.rb @@ -5,50 +5,51 @@ class Registrar skip_authorization_check # actually anyone can pay, no problems at all skip_before_action :authenticate_registrar_user!, :check_ip_restriction, only: [:back, :callback] - before_action :check_supported_payment_method + + before_action :check_supported_payment_method, only: [:pay] def pay invoice = Invoice.find(params[:invoice_id]) - bank = params[:bank] - opts = { - return_url: registrar_return_payment_with_url( - bank, invoice_id: invoice - ), - response_url: registrar_response_payment_with_url( - bank, invoice_id: invoice - ) - } - @payment = ::PaymentOrders.create_with_type(bank, invoice, opts) - @payment.create_transaction + channel = params[:bank] + + @payment_order = PaymentOrder.new_with_type(type: channel, invoice: invoice) + @payment_order.save + @payment_order.reload + + @payment_order.return_url = registrar_return_payment_with_url(@payment_order) + @payment_order.response_url = registrar_response_payment_with_url(@payment_order) + + @payment_order.save + @payment_order.reload end def back - invoice = Invoice.find(params[:invoice_id]) - opts = { response: params } - @payment = ::PaymentOrders.create_with_type(params[:bank], invoice, opts) - if @payment.valid_response_from_intermediary? && @payment.settled_payment? - Rails.logger.info("User paid invoice ##{invoice.number} successfully") + @payment_order = PaymentOrder.find_by!(id: params[:payment_order]) + @payment_order.update!(response: params.to_unsafe_h) - @payment.complete_transaction + if @payment_order.payment_received? + @payment_order.complete_transaction - if invoice.paid? - flash[:notice] = t(:pending_applied) + if @payment_order.invoice.paid? + flash[:notice] = t('.payment_successful') else - flash[:alert] = t(:something_wrong) + flash[:alert] = t('.successful_payment_backend_error') end else - flash[:alert] = t(:something_wrong) + @payment_order.create_failure_report + flash[:alert] = t('.payment_not_received') end - redirect_to registrar_invoice_path(invoice) + redirect_to registrar_invoice_path(@payment_order.invoice) end def callback - invoice = Invoice.find(params[:invoice_id]) - opts = { response: params } - @payment = ::PaymentOrders.create_with_type(params[:bank], invoice, opts) + @payment_order = PaymentOrder.find_by!(id: params[:payment_order]) + @payment_order.update!(response: params.to_unsafe_h) - if @payment.valid_response_from_intermediary? && @payment.settled_payment? - @payment.complete_transaction + if @payment_order.payment_received? + @payment_order.complete_transaction + else + @payment_order.create_failure_report end render status: 200, json: { status: 'ok' } @@ -57,13 +58,9 @@ class Registrar private def check_supported_payment_method - return if supported_payment_method? - raise StandardError.new("Not supported payment method") - end + return if PaymentOrder.supported_method?(params[:bank], shortname: true) - - def supported_payment_method? - PaymentOrders::PAYMENT_METHODS.include?(params[:bank]) + raise(StandardError, 'Not supported payment method') end end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index a4a8e4912..8de3fdc70 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -33,6 +33,18 @@ module ApplicationHelper end end + def current_commit_link + hash = `git rev-parse --short HEAD` + current_repo = `git remote get-url origin`.gsub('com:', 'com/') + .gsub('git@', 'https://') + .gsub('.git', '') + + link_to hash.to_s, "#{current_repo}/commit/#{hash}", + class: 'footer-version-link', + target: '_blank', + rel: 'noopener' + end + def creator_link(model) return 'not present' if model.blank? return 'unknown' if model.creator.blank? @@ -96,4 +108,4 @@ module ApplicationHelper def body_css_class [controller_path.split('/').map!(&:dasherize), action_name.dasherize, 'page'].join('-') end -end \ No newline at end of file +end diff --git a/app/jobs/directo_invoice_forward_job.rb b/app/jobs/directo_invoice_forward_job.rb new file mode 100644 index 000000000..6c3eb034c --- /dev/null +++ b/app/jobs/directo_invoice_forward_job.rb @@ -0,0 +1,125 @@ +class DirectoInvoiceForwardJob < Que::Job + def run(monthly: false, dry: false) + @dry = dry + (@month = Time.zone.now - 1.month) if monthly + api_url = ENV['directo_invoice_url'] + sales_agent = Setting.directo_sales_agent + payment_term = Setting.directo_receipt_payment_term + @prepayment_product_id = Setting.directo_receipt_product_name + + @client = DirectoApi::Client.new(api_url, sales_agent, payment_term) + monthly ? send_monthly_invoices : send_receipts + end + + def send_receipts + unsent_invoices = Invoice.where(in_directo: false).non_cancelled + + Rails.logger.info("[DIRECTO] Trying to send #{unsent_invoices.count} prepayment invoices") + unsent_invoices.each do |invoice| + unless valid_invoice_conditions?(invoice) + Rails.logger.info "[DIRECTO] Invoice #{invoice.number} has been skipped" + next + end + @client.invoices.add_with_schema(invoice: invoice.as_directo_json, schema: 'prepayment') + end + + sync_with_directo + end + + def send_monthly_invoices + Registrar.where.not(test_registrar: true).find_each do |registrar| + fetch_monthly_summary(registrar: registrar) + end + + return unless @client.invoices.count.positive? + + sync_with_directo + end + + def fetch_monthly_summary(registrar:) + return unless registrar.cash_account + + summary = registrar.monthly_summary(month: @month) + @client.invoices.add_with_schema(invoice: summary, schema: 'summary') unless summary.nil? + end + + def assign_monthly_numbers + if directo_counter_exceedable?(@client.invoices.count) + raise 'Directo Counter is going to be out of period!' + end + + min_directo = Setting.directo_monthly_number_min.presence.try(:to_i) + directo_number = [Setting.directo_monthly_number_last.presence.try(:to_i), + min_directo].compact.max || 0 + + @client.invoices.each do |inv| + directo_number += 1 + inv.number = directo_number + end + end + + def valid_invoice_conditions?(invoice) + if invoice.account_activity.nil? || invoice.account_activity.bank_transaction.nil? || + invoice.account_activity.bank_transaction.sum.nil? || + invoice.account_activity.bank_transaction.sum != invoice.total + return false + + end + + true + end + + def sync_with_directo + assign_monthly_numbers if @month + Rails.logger.info("[Directo] - attempting to send following XML:\n #{@client.invoices.as_xml}") + return if @dry + + res = @client.invoices.deliver(ssl_verify: false) + process_directo_response(res.body, @client.invoices.as_xml) + rescue SocketError, Errno::ECONNREFUSED, Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, + EOFError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError + Rails.logger.info('[Directo] Failed to communicate via API') + end + + def process_directo_response(xml, req) + Rails.logger.info "[Directo] - Responded with body: #{xml}" + Nokogiri::XML(xml).css('Result').each do |res| + if @month + mark_invoice_as_sent(res: res, req: req) + else + inv = Invoice.find_by(number: res.attributes['docid'].value.to_i) + mark_invoice_as_sent(invoice: inv, res: res, req: req) + end + end + end + + def mark_invoice_as_sent(invoice: nil, res:, req:) + directo_record = Directo.new(response: res.as_json.to_h, + request: req, invoice_number: res.attributes['docid'].value.to_i) + if invoice + directo_record.item = invoice + invoice.update(in_directo: true) + else + update_directo_number(num: directo_record.invoice_number) + end + + directo_record.save! + end + + def update_directo_number(num:) + return unless num.to_i > Setting.directo_monthly_number_last.to_i + + Setting.directo_monthly_number_last = num.to_i + end + + def directo_counter_exceedable?(invoice_count) + min_directo = Setting.directo_monthly_number_min.presence.try(:to_i) + max_directo = Setting.directo_monthly_number_max.presence.try(:to_i) + last_directo = [Setting.directo_monthly_number_last.presence.try(:to_i), + min_directo].compact.max || 0 + + return true if max_directo && max_directo < (last_directo + invoice_count) + + false + end +end diff --git a/app/jobs/send_e_invoice_job.rb b/app/jobs/send_e_invoice_job.rb new file mode 100644 index 000000000..e281db14d --- /dev/null +++ b/app/jobs/send_e_invoice_job.rb @@ -0,0 +1,43 @@ +class SendEInvoiceJob < Que::Job + def run(invoice_id) + invoice = run_condition(Invoice.find_by(id: invoice_id)) + + invoice.to_e_invoice.deliver + ActiveRecord::Base.transaction do + invoice.update(e_invoice_sent_at: Time.zone.now) + log_success(invoice) + destroy + end + rescue StandardError => e + log_error(invoice: invoice, error: e) + raise e + end + + private + + def run_condition(invoice) + destroy unless invoice + destroy if invoice.do_not_send_e_invoice? + invoice + end + + def log_success(invoice) + id = invoice.try(:id) || invoice + message = "E-Invoice for an invoice with ID # #{id} was sent successfully" + logger.info message + end + + def log_error(invoice:, error:) + id = invoice.try(:id) || invoice + message = <<~TEXT.squish + There was an error sending e-invoice for invoice with ID # #{id}. + The error message was the following: #{error} + This job will retry. + TEXT + logger.error message + end + + def logger + Rails.logger + end +end diff --git a/app/models/ability.rb b/app/models/ability.rb index 9a0676ac8..a727254ad 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -50,6 +50,7 @@ class Ability can(:check, Epp::Domain) can(:create, Epp::Domain) can(:renew, Epp::Domain) { |d| d.registrar_id == @user.registrar_id } + can(:remove_hold, Epp::Domain) { |d| d.registrar_id == @user.registrar_id } can(:update, Epp::Domain) { |d, pw| d.registrar_id == @user.registrar_id || d.transfer_code == pw } can(:transfer, Epp::Domain) can(:delete, Epp::Domain) { |d, pw| d.registrar_id == @user.registrar_id || d.transfer_code == pw } diff --git a/app/models/admin_user.rb b/app/models/admin_user.rb index 07686e921..159578ab0 100644 --- a/app/models/admin_user.rb +++ b/app/models/admin_user.rb @@ -4,7 +4,7 @@ class AdminUser < User validates :identity_code, presence: true, if: -> { country_code == 'EE' } validates :email, presence: true validates :password, :password_confirmation, presence: true, if: :new_record? - validates :password_confirmation, presence: true, if: :encrypted_password_changed? + validates :password_confirmation, presence: true, if: :will_save_change_to_encrypted_password? validate :validate_identity_code, if: -> { country_code == 'EE' } ROLES = %w(user customer_service admin) # should not match to api_users roles diff --git a/app/models/api_user.rb b/app/models/api_user.rb index e11dbf90f..3dc240727 100644 --- a/app/models/api_user.rb +++ b/app/models/api_user.rb @@ -43,7 +43,7 @@ class ApiUser < User after_initialize :set_defaults def set_defaults return unless new_record? - self.active = true unless active_changed? + self.active = true unless saved_change_to_active? end class << self diff --git a/app/models/bank_statement.rb b/app/models/bank_statement.rb index 8d4608f64..c73e6bb44 100644 --- a/app/models/bank_statement.rb +++ b/app/models/bank_statement.rb @@ -25,10 +25,16 @@ class BankStatement < ApplicationRecord bank_transactions.build(bt_params) end + prepare_dir self.import_file_path = "#{ENV['bank_statement_import_dir']}/#{Time.zone.now.to_formatted_s(:number)}.txt" File.open(import_file_path, 'w') { |f| f.write(th6_file.open.read) } end + def prepare_dir + dirname = ENV['bank_statement_import_dir'] + FileUtils.mkdir_p(dirname) unless File.directory?(dirname) + end + def parse_th6_row(row) return parse_th6_header(row) if row[4, 3].strip == '000' return if row[4, 3].strip == '999' # skip footer @@ -45,7 +51,7 @@ class BankStatement < ApplicationRecord buyer_name: row[83, 35].strip, document_no: row[118, 8].strip, description: row[126, 140].strip, - sum: BigDecimal.new(row[268, 12].strip) / BigDecimal.new('100.0'), + sum: BigDecimal(row[268, 12].strip) / BigDecimal('100.0'), reference_no: row[280, 35].strip } end @@ -80,7 +86,9 @@ class BankStatement < ApplicationRecord status == FULLY_BINDED end - def bind_invoices - bank_transactions.unbinded.each(&:autobind_invoice) + def bind_invoices(manual: false) + bank_transactions.unbinded.each do |transaction| + transaction.autobind_invoice(manual: manual) + end end end diff --git a/app/models/bank_transaction.rb b/app/models/bank_transaction.rb index cb9aebc5e..f53a286ba 100644 --- a/app/models/bank_transaction.rb +++ b/app/models/bank_transaction.rb @@ -13,6 +13,7 @@ class BankTransaction < ApplicationRecord def binded_invoice return unless binded? + account_activity.invoice end @@ -30,31 +31,54 @@ class BankTransaction < ApplicationRecord @registrar ||= Invoice.find_by(reference_no: parsed_ref_number)&.buyer end - # For successful binding, reference number, invoice id and sum must match with the invoice - def autobind_invoice + def autobind_invoice(manual: false) return if binded? return unless registrar return unless invoice return unless invoice.payable? - create_activity(registrar, invoice) + channel = if manual + 'admin_payment' + else + 'system_payment' + end + create_internal_payment_record(channel: channel, invoice: invoice, + registrar: registrar) end - def bind_invoice(invoice_no) + def create_internal_payment_record(channel: nil, invoice:, registrar:) + if channel.nil? + create_activity(invoice.buyer, invoice) + return + end + + payment_order = PaymentOrder.new_with_type(type: channel, invoice: invoice) + payment_order.save! + + if create_activity(registrar, invoice) + payment_order.paid! + else + payment_order.update(notes: 'Failed to create activity', status: 'failed') + end + end + + def bind_invoice(invoice_no, manual: false) if binded? errors.add(:base, I18n.t('transaction_is_already_binded')) return end invoice = Invoice.find_by(number: invoice_no) - @registrar = invoice.buyer + errors.add(:base, I18n.t('invoice_was_not_found')) unless invoice + validate_invoice_data(invoice) + return if errors.any? - unless invoice - errors.add(:base, I18n.t('invoice_was_not_found')) - return - end + create_internal_payment_record(channel: (manual ? 'admin_payment' : nil), invoice: invoice, + registrar: invoice.buyer) + end + def validate_invoice_data(invoice) if invoice.paid? errors.add(:base, I18n.t('invoice_is_already_binded')) return @@ -65,23 +89,21 @@ class BankTransaction < ApplicationRecord return end - if invoice.total != sum - errors.add(:base, I18n.t('invoice_and_transaction_sums_do_not_match')) - return - end - - create_activity(invoice.buyer, invoice) + errors.add(:base, I18n.t('invoice_and_transaction_sums_do_not_match')) if invoice.total != sum end def create_activity(registrar, invoice) - ActiveRecord::Base.transaction do - create_account_activity!(account: registrar.cash_account, - invoice: invoice, - sum: invoice.subtotal, - currency: currency, - description: description, - activity_type: AccountActivity::ADD_CREDIT) + activity = AccountActivity.new( + account: registrar.cash_account, bank_transaction: self, + invoice: invoice, sum: invoice.subtotal, + currency: currency, description: description, + activity_type: AccountActivity::ADD_CREDIT + ) + if activity.save reset_pending_registrar_balance_reload + true + else + false end end diff --git a/app/models/concerns/contact/transferable.rb b/app/models/concerns/contact/transferable.rb index 3f151251a..987933a8d 100644 --- a/app/models/concerns/contact/transferable.rb +++ b/app/models/concerns/contact/transferable.rb @@ -3,7 +3,7 @@ module Concerns::Contact::Transferable included do validates :auth_info, presence: true - after_initialize :generate_auth_info, if: 'new_record? && auth_info.blank?' + after_initialize :generate_auth_info, if: -> { new_record? && auth_info.blank? } end def transfer(new_registrar) diff --git a/app/models/concerns/invoice/book_keeping.rb b/app/models/concerns/invoice/book_keeping.rb new file mode 100644 index 000000000..2469f45eb --- /dev/null +++ b/app/models/concerns/invoice/book_keeping.rb @@ -0,0 +1,26 @@ +module Concerns + module Invoice + module BookKeeping + extend ActiveSupport::Concern + + def as_directo_json + invoice = ActiveSupport::JSON.decode(ActiveSupport::JSON.encode(self)) + invoice['customer_code'] = buyer.accounting_customer_code + invoice['issue_date'] = issue_date.strftime('%Y-%m-%d') + invoice['transaction_date'] = account_activity + .bank_transaction&.paid_at&.strftime('%Y-%m-%d') + invoice['language'] = buyer.language == 'en' ? 'ENG' : '' + invoice['invoice_lines'] = compose_directo_product + + invoice + end + + def compose_directo_product + [{ 'product_id': Setting.directo_receipt_product_name, 'description': order, + 'quantity': 1, 'price': ActionController::Base.helpers.number_with_precision( + subtotal, precision: 2, separator: '.' + ) }].as_json + end + end + end +end diff --git a/app/models/concerns/registrar/book_keeping.rb b/app/models/concerns/registrar/book_keeping.rb new file mode 100644 index 000000000..27645d2cb --- /dev/null +++ b/app/models/concerns/registrar/book_keeping.rb @@ -0,0 +1,120 @@ +module Concerns + module Registrar + module BookKeeping + extend ActiveSupport::Concern + + DOMAIN_TO_PRODUCT = { 'ee': '01EE', 'com.ee': '02COM', 'pri.ee': '03PRI', + 'fie.ee': '04FIE', 'med.ee': '05MED' }.freeze + + def monthly_summary(month:) + activities = monthly_activites(month) + return unless activities.any? + + invoice = { + 'number': 1, + 'customer_code': accounting_customer_code, + 'language': language == 'en' ? 'ENG' : '', 'currency': activities.first.currency, + 'date': month.end_of_month.strftime('%Y-%m-%d') + }.as_json + + invoice['invoice_lines'] = prepare_invoice_lines(month: month, activities: activities) + + invoice + end + + def prepare_invoice_lines(month:, activities:) + lines = [] + + lines << { 'description': title_for_summary(month) } + activities.each do |activity| + fetch_invoice_lines(activity, lines) + end + lines << prepayment_for_all(lines) + + lines.as_json + end + + def title_for_summary(date) + I18n.with_locale(language == 'en' ? 'en' : 'et') do + I18n.t('registrar.monthly_summary_title', date: I18n.l(date, format: '%B %Y')) + end + end + + def fetch_invoice_lines(activity, lines) + price = load_price(activity) + if price.duration.include? 'year' + price.duration.to_i.times do |duration| + lines << new_monthly_invoice_line(activity: activity, duration: duration + 1).as_json + end + else + lines << new_monthly_invoice_line(activity: activity).as_json + end + end + + def monthly_activites(month) + AccountActivity.where(account_id: account_ids) + .where(created_at: month.beginning_of_month..month.end_of_month) + .where(activity_type: [AccountActivity::CREATE, AccountActivity::RENEW]) + end + + def new_monthly_invoice_line(activity:, duration: nil) + price = load_price(activity) + line = { + 'product_id': DOMAIN_TO_PRODUCT[price.zone_name.to_sym], + 'quantity': 1, + 'unit': language == 'en' ? 'pc' : 'tk', + } + + finalize_invoice_line(line, price: price, duration: duration, activity: activity) + end + + def finalize_invoice_line(line, price:, activity:, duration:) + yearly = price.duration.include?('year') + + line['price'] = yearly ? (price.price.amount / price.duration.to_i) : price.price.amount + line['description'] = description_in_language(price: price, yearly: yearly) + + if duration.present? + add_product_timeframe(line: line, activity: activity, duration: duration) if duration > 1 + end + + line + end + + def add_product_timeframe(line:, activity:, duration:) + create_time = activity.created_at + line['start_date'] = (create_time + (duration - 1).year).end_of_month.strftime('%Y-%m-%d') + line['end_date'] = (create_time + (duration - 1).year + 1).end_of_month.strftime('%Y-%m-%d') + end + + def description_in_language(price:, yearly:) + timeframe_string = yearly ? 'yearly' : 'monthly' + locale_string = "registrar.invoice_#{timeframe_string}_product_description" + + I18n.with_locale(language == 'en' ? 'en' : 'et') do + I18n.t(locale_string, tld: ".#{price.zone_name}", length: price.duration.to_i) + end + end + + def prepayment_for_all(lines) + total = 0 + en = language == 'en' + lines.each { |l| total += l['quantity'].to_f * l['price'].to_f } + { + 'product_id': Setting.directo_receipt_product_name, + 'description': en ? 'Domains prepayment' : 'Domeenide ettemaks', + 'quantity': -1, + 'price': total, + 'unit': en ? 'pc' : 'tk', + } + end + + def load_price(account_activity) + @pricelists ||= {} + return @pricelists[account_activity.price_id] if @pricelists.key? account_activity.price_id + + @pricelists[account_activity.price_id] = account_activity.price + end + end + end +end diff --git a/app/models/concerns/remove_hold.rb b/app/models/concerns/remove_hold.rb new file mode 100644 index 000000000..1da3b5a7f --- /dev/null +++ b/app/models/concerns/remove_hold.rb @@ -0,0 +1,9 @@ +module RemoveHold + extend ActiveSupport::Concern + + def remove_hold(params) + xml = epp_xml.update(name: { value: params[:domain_name] }, + rem: [status: { attrs: { s: 'clientHold' }, value: '' }]) + current_user.request(xml) + end +end diff --git a/app/models/concerns/versions.rb b/app/models/concerns/versions.rb index b0a82376f..01766b233 100644 --- a/app/models/concerns/versions.rb +++ b/app/models/concerns/versions.rb @@ -1,10 +1,16 @@ # Papertrail concerns is mainly tested at country spec module Versions extend ActiveSupport::Concern + WITH_CHILDREN = %w[Domain Contact].freeze included do attr_accessor :version_loader - has_paper_trail class_name: "#{model_name}Version" + + if WITH_CHILDREN.include?(model_name.name) + has_paper_trail class_name: "#{model_name}Version", meta: { children: :children_log } + else + has_paper_trail class_name: "#{model_name}Version" + end # add creator and updator before_create :add_creator @@ -45,17 +51,17 @@ module Versions # callbacks def touch_domain_version - domain.try(:touch_with_version) + domain.paper_trail.try(:touch_with_version) end def touch_domains_version - domains.each(&:touch_with_version) + domains.each { |domain| domain.paper_trail.touch_with_version } end end module ClassMethods def all_versions_for(ids, time) - ver_klass = paper_trail_version_class + ver_klass = paper_trail.version_class from_history = ver_klass.where(item_id: ids.to_a). order(:item_id). preceding(time + 1, true). @@ -64,7 +70,8 @@ module Versions valid_columns = ver.item_type.constantize&.column_names o = new(ver.object&.slice(*valid_columns)) o.version_loader = ver - ver.object_changes.to_h.each { |k, v| o.public_send("#{k}=", v[-1]) } + changes = ver.object_changes.to_h&.slice(*valid_columns) + changes.each { |k, v| o.public_send("#{k}=", v[-1]) } o end not_in_history = where(id: (ids.to_a - from_history.map(&:id))) diff --git a/app/models/contact.rb b/app/models/contact.rb index ddf39c9bb..ac6facbe8 100644 --- a/app/models/contact.rb +++ b/app/models/contact.rb @@ -14,8 +14,6 @@ class Contact < ApplicationRecord has_many :registrant_domains, class_name: 'Domain', foreign_key: 'registrant_id' has_many :actions, dependent: :destroy - has_paper_trail class_name: "ContactVersion", meta: { children: :children_log } - attr_accessor :legal_document_id alias_attribute :kind, :ident_type alias_attribute :copy_from_id, :original_id # Old attribute name; for PaperTrail @@ -23,12 +21,14 @@ class Contact < ApplicationRecord accepts_nested_attributes_for :legal_documents validates :name, :email, presence: true - validates :street, :city, :zip, :country_code, presence: true, if: 'self.class.address_processing?' + validates :street, :city, :zip, :country_code, presence: true, if: lambda { + self.class.address_processing? + } validates :phone, presence: true, e164: true, phone: true validates :email, format: /@/ - validates :email, email_format: { message: :invalid }, if: proc { |c| c.email_changed? } + validates :email, email_format: { message: :invalid }, if: proc { |c| c.will_save_change_to_email? } validates :code, uniqueness: { message: :epp_id_taken }, @@ -37,7 +37,7 @@ class Contact < ApplicationRecord validates_associated :identifier validate :validate_html - validate :validate_country_code, if: 'self.class.address_processing?' + validate :validate_country_code, if: -> { self.class.address_processing? } after_initialize do self.status_notes = {} if status_notes.nil? diff --git a/app/models/counter.rb b/app/models/counter.rb deleted file mode 100644 index 7d1c2b926..000000000 --- a/app/models/counter.rb +++ /dev/null @@ -1,24 +0,0 @@ -class Counter - def initialize value = 0 - @value = value - end - attr_accessor :value - def method_missing *args, &blk - @value.send(*args, &blk) - end - def to_s - @value.to_s - end - - def now - @value - end - - # pre-increment ".+" when x not present - def next(x = 1) - @value += x - end - def prev(x = 1) - @value -= x - end -end \ No newline at end of file diff --git a/app/models/depp/domain.rb b/app/models/depp/domain.rb index e2413a004..3bb3b7473 100644 --- a/app/models/depp/domain.rb +++ b/app/models/depp/domain.rb @@ -1,6 +1,7 @@ module Depp class Domain include ActiveModel::Conversion + include RemoveHold extend ActiveModel::Naming attr_accessor :name, :current_user, :epp_xml diff --git a/app/models/directo.rb b/app/models/directo.rb index 789db64b2..a4af6c134 100644 --- a/app/models/directo.rb +++ b/app/models/directo.rb @@ -1,198 +1,3 @@ class Directo < ApplicationRecord - DOMAIN_TO_PRODUCT = {"ee" => "01EE", "com.ee" => "02COM", "pri.ee" => "03PRI", "fie.ee"=>"04FIE", "med.ee" => "05MED"}.freeze belongs_to :item, polymorphic: true - - def self.send_receipts - new_trans = Invoice.where(in_directo: false).non_cancelled - total = new_trans.count - counter = 0 - Rails.logger.info("[DIRECTO] Will try to send #{total} invoices") - - new_trans.find_in_batches(batch_size: 10).each do |group| - mappers = {} # need them as no direct connection between invoice - builder = Nokogiri::XML::Builder.new(encoding: "UTF-8") do |xml| - xml.invoices { - group.each do |invoice| - - if invoice.account_activity.nil? || invoice.account_activity.bank_transaction.nil? || - invoice.account_activity.bank_transaction.sum.nil? || invoice.account_activity.bank_transaction.sum != invoice.total - Rails.logger.info("[DIRECTO] Invoice #{invoice.number} has been skipped") - next - end - counter += 1 - - num = invoice.number - paid_at = invoice.account_activity.bank_transaction&.paid_at&.strftime("%Y-%m-%d") - mappers[num] = invoice - xml.invoice( - "SalesAgent" => Setting.directo_sales_agent, - "Number" => num, - "InvoiceDate" => invoice.issue_date.strftime("%Y-%m-%d"), - 'TransactionDate' => paid_at, - "PaymentTerm" => Setting.directo_receipt_payment_term, - "Currency" => invoice.currency, - "CustomerCode"=> invoice.buyer.accounting_customer_code - ){ - xml.line( - "ProductID" => Setting.directo_receipt_product_name, - "Quantity" => 1, - "UnitPriceWoVAT" => ActionController::Base.helpers.number_with_precision(invoice.subtotal, precision: 2, separator: "."), - "ProductName" => invoice.order - ) - } - end - } - end - - data = builder.to_xml.gsub("\n",'') - Rails.logger.info("[Directo] XML request: #{data}") - response = RestClient::Request.execute(url: ENV['directo_invoice_url'], method: :post, payload: {put: "1", what: "invoice", xmldata: data}, verify_ssl: false) - Rails.logger.info("[Directo] Directo responded with code: #{response.code}, body: #{response.body}") - dump_result_to_db(mappers, response.to_s) - end - - STDOUT << "#{Time.zone.now.utc} - Directo receipts sending finished. #{counter} of #{total} are sent\n" - end - - def self.dump_result_to_db mappers, xml - Nokogiri::XML(xml).css("Result").each do |res| - obj = mappers[res.attributes["docid"].value.to_i] - obj.directo_records.create!(response: res.as_json.to_h, invoice_number: obj.number) - obj.update_columns(in_directo: true) - Rails.logger.info("[DIRECTO] Invoice #{res.attributes["docid"].value} was pushed and return is #{res.as_json.to_h.inspect}") - end - end - - - def self.send_monthly_invoices(debug: false) - I18n.locale = :et - month = Time.now - 1.month - invoices_until = month.end_of_month - date_format = "%Y-%m-%d" - invoice_counter= Counter.new - - min_directo = Setting.directo_monthly_number_min.presence.try(:to_i) - max_directo = Setting.directo_monthly_number_max.presence.try(:to_i) - last_directo = [Setting.directo_monthly_number_last.presence.try(:to_i), min_directo].compact.max || 0 - if max_directo && max_directo <= last_directo - raise "Directo counter is out of period (max allowed number is smaller than last counter number)" - end - - directo_next = last_directo - Registrar.where.not(test_registrar: true).find_each do |registrar| - unless registrar.cash_account - Rails.logger.info("[DIRECTO] Monthly invoice for registrar #{registrar.id} has been skipped as it doesn't has cash_account") - next - end - counter = Counter.new(1) - items = {} - registrar_activities = AccountActivity.where(account_id: registrar.account_ids).where("created_at BETWEEN ? AND ?",month.beginning_of_month, month.end_of_month) - - # adding domains items - registrar_activities.where(activity_type: [AccountActivity::CREATE, AccountActivity::RENEW]).each do |activity| - price = load_price(activity) - - if price.duration.include?('year') - price.duration.to_i.times do |i| - year = i+1 - hash = { - "ProductID" => DOMAIN_TO_PRODUCT[price.zone_name], - "Unit" => "tk", - "ProductName" => ".#{price.zone_name} registreerimine: #{price.duration.to_i} aasta#{price.duration.to_i > 1 ? 't' : ''}", - "UnitPriceWoVAT" => price.price.amount / price.duration.to_i - } - hash["StartDate"] = (activity.created_at + (year-1).year).end_of_month.strftime(date_format) if year > 1 - hash["EndDate"] = (activity.created_at + (year-1).year + 1).end_of_month.strftime(date_format) if year > 1 - - if items.has_key?(hash) - items[hash]["Quantity"] += 1 - else - items[hash] = { "RN" => counter.next, "RR" => counter.now - i, "Quantity" => 1 } - end - end - else - 1.times do |i| - quantity = price.account_activities - .where(account_id: registrar.account_ids) - .where(created_at: month.beginning_of_month..month.end_of_month) - .where(activity_type: [AccountActivity::CREATE, AccountActivity::RENEW]) - .count - - hash = { - "ProductID" => DOMAIN_TO_PRODUCT[price.zone_name], - "Unit" => "tk", - "ProductName" => ".#{price.zone_name} registreerimine: #{price.duration.to_i} kuud", - "UnitPriceWoVAT" => price.price.amount, - } - - if items.has_key?(hash) - #items[hash]["Quantity"] += 1 - else - items[hash] = { "RN" => counter.next, "RR" => counter.now - i, "Quantity" => quantity } - end - end - end - - - end - - #adding prepaiments - if items.any? - total = 0 - items.each{ |key, val| total += val["Quantity"] * key["UnitPriceWoVAT"] } - hash = {"ProductID" => Setting.directo_receipt_product_name, "Unit" => "tk", "ProductName" => "Domeenide ettemaks", "UnitPriceWoVAT"=>total} - items[hash] = {"RN"=>counter.next, "RR" => counter.now, "Quantity"=> -1} - end - - # generating XML - if items.any? - directo_next += 1 - invoice_counter.next - - builder = Nokogiri::XML::Builder.new(encoding: "UTF-8") do |xml| - xml.invoices{ - xml.invoice("Number" =>directo_next, - "InvoiceDate" =>invoices_until.strftime(date_format), - "PaymentTerm" =>Setting.directo_receipt_payment_term, - "CustomerCode"=>registrar.accounting_customer_code, - "Language" =>"", - "Currency" =>registrar_activities.first.currency, - "SalesAgent" =>Setting.directo_sales_agent){ - xml.line("RN" => 1, "RR"=>1, "ProductName"=> "Domeenide registreerimine - #{I18n.l(invoices_until, format: "%B %Y").titleize}") - items.each do |line, val| - xml.line(val.merge(line)) - end - } - } - end - - data = builder.to_xml.gsub("\n",'') - Rails.logger.info("[Directo] XML request: #{data}") - - if debug - STDOUT << "#{Time.zone.now.utc} - Directo xml had to be sent #{data}\n" - else - response = RestClient::Request.execute(url: ENV['directo_invoice_url'], method: :post, payload: {put: "1", what: "invoice", xmldata: data}, verify_ssl: false) - Rails.logger.info("[Directo] Directo responded with code: #{response.code}, body: #{response.body}") - response = response.to_s - - Setting.directo_monthly_number_last = directo_next - Nokogiri::XML(response).css("Result").each do |res| - Directo.create!(request: data, response: res.as_json.to_h, invoice_number: directo_next) - Rails.logger.info("[DIRECTO] Invoice #{res.attributes["docid"].value} was pushed and return is #{res.as_json.to_h.inspect}") - end - end - else - Rails.logger.info("[DIRECTO] Registrar #{registrar.id} has nothing to be sent to Directo") - end - - end - STDOUT << "#{Time.zone.now.utc} - Directo invoices sending finished. #{invoice_counter.now} are sent\n" - end - - def self.load_price(account_activity) - @pricelists ||= {} - return @pricelists[account_activity.price_id] if @pricelists.has_key?(account_activity.price_id) - @pricelists[account_activity.price_id] = account_activity.price - end end diff --git a/app/models/dnskey.rb b/app/models/dnskey.rb index 922844874..c0f3f7491 100644 --- a/app/models/dnskey.rb +++ b/app/models/dnskey.rb @@ -9,10 +9,16 @@ class Dnskey < ApplicationRecord validate :validate_protocol validate :validate_flags - before_save -> { generate_digest if public_key_changed? && !ds_digest_changed? } + before_save lambda { + generate_digest if will_save_change_to_public_key? && !will_save_change_to_ds_digest? + } before_save lambda { - if (public_key_changed? || flags_changed? || alg_changed? || protocol_changed?) && !ds_key_tag_changed? + if (will_save_change_to_public_key? || + will_save_change_to_flags? || + will_save_change_to_alg? || + will_save_change_to_protocol?) && + !will_save_change_to_ds_key_tag? generate_ds_key_tag end } diff --git a/app/models/domain.rb b/app/models/domain.rb index d6545284d..f21317b70 100644 --- a/app/models/domain.rb +++ b/app/models/domain.rb @@ -10,8 +10,6 @@ class Domain < ApplicationRecord include Concerns::Domain::RegistryLockable include Concerns::Domain::Releasable - has_paper_trail class_name: "DomainVersion", meta: { children: :children_log } - attr_accessor :roles attr_accessor :legal_document_id @@ -73,12 +71,13 @@ class Domain < ApplicationRecord before_update :manage_statuses def manage_statuses - return unless registrant_id_changed? # rollback has not yet happened + return unless will_save_change_to_registrant_id? # rollback has not yet happened + pending_update! if registrant_verification_asked? true end - after_commit :update_whois_record, unless: 'domain_name.at_auction?' + after_commit :update_whois_record, unless: -> { domain_name.at_auction? } after_create :update_reserved_domains def update_reserved_domains @@ -486,9 +485,9 @@ class Domain < ApplicationRecord self.delete_date = nil when DomainStatus::SERVER_MANUAL_INZONE # removal causes server hold to set self.outzone_at = Time.zone.now if force_delete_scheduled? - when DomainStatus::DomainStatus::EXPIRED # removal causes server hold to set + when DomainStatus::EXPIRED # removal causes server hold to set self.outzone_at = self.expire_time + 15.day - when DomainStatus::DomainStatus::SERVER_HOLD # removal causes server hold to set + when DomainStatus::SERVER_HOLD # removal causes server hold to set self.outzone_at = nil end end @@ -547,7 +546,7 @@ class Domain < ApplicationRecord activate if nameservers.reject(&:marked_for_destruction?).size >= Setting.ns_min_count end - cancel_force_delete if force_delete_scheduled? && registrant_id_changed? + cancel_force_delete if force_delete_scheduled? && will_save_change_to_registrant_id? if statuses.empty? && valid? statuses << DomainStatus::OK diff --git a/app/models/domain_status.rb b/app/models/domain_status.rb index fba75737c..4b1c49916 100644 --- a/app/models/domain_status.rb +++ b/app/models/domain_status.rb @@ -1,7 +1,5 @@ class DomainStatus < ApplicationRecord - include Versions # version/domain_status_version.rb include EppErrors - belongs_to :domain # Requests to delete the object MUST be rejected. diff --git a/app/models/epp/contact.rb b/app/models/epp/contact.rb index 742f0339f..fa488d9e1 100644 --- a/app/models/epp/contact.rb +++ b/app/models/epp/contact.rb @@ -182,7 +182,7 @@ class Epp::Contact < Contact self.attributes = at - email_changed = email_changed? + email_changed = will_save_change_to_email? old_email = email_was updated = save diff --git a/app/models/invoice.rb b/app/models/invoice.rb index 7f1dea825..a130a90ff 100644 --- a/app/models/invoice.rb +++ b/app/models/invoice.rb @@ -2,11 +2,13 @@ class Invoice < ApplicationRecord include Versions include Concerns::Invoice::Cancellable include Concerns::Invoice::Payable + include Concerns::Invoice::BookKeeping belongs_to :buyer, class_name: 'Registrar' has_one :account_activity has_many :items, class_name: 'InvoiceItem', dependent: :destroy has_many :directo_records, as: :item, class_name: 'Directo' + has_many :payment_orders accepts_nested_attributes_for :items @@ -70,7 +72,7 @@ class Invoice < ApplicationRecord Country.new(buyer_country_code) end -# order is used for directo/banklink description + # order is used for directo/banklink description def order "Order nr. #{number}" end @@ -102,6 +104,14 @@ class Invoice < ApplicationRecord generator.generate end + def do_not_send_e_invoice? + e_invoice_sent? || cancelled? || paid? + end + + def e_invoice_sent? + e_invoice_sent_at.present? + end + private def apply_default_buyer_vat_no @@ -111,4 +121,4 @@ class Invoice < ApplicationRecord def calculate_total self.total = subtotal + vat_amount end -end \ No newline at end of file +end diff --git a/app/models/nameserver.rb b/app/models/nameserver.rb index bf76868dc..3ddf1d1c5 100644 --- a/app/models/nameserver.rb +++ b/app/models/nameserver.rb @@ -48,7 +48,7 @@ class Nameserver < ApplicationRecord [:ipv6, :invalid, { value: { obj: 'hostAddr', val: ipv6 } }] ], '2003' => [ - [:ipv4, :blank] + %i[base ip_required], ] } end @@ -83,6 +83,7 @@ class Nameserver < ApplicationRecord def glue_record_required? return unless hostname? && domain + DomainName(hostname).domain == domain.name end diff --git a/app/models/payment_order.rb b/app/models/payment_order.rb new file mode 100644 index 000000000..4317abb38 --- /dev/null +++ b/app/models/payment_order.rb @@ -0,0 +1,102 @@ +class PaymentOrder < ApplicationRecord + include Versions + include ActionView::Helpers::NumberHelper + + PAYMENT_INTERMEDIARIES = ENV['payments_intermediaries'].to_s.strip.split(', ').freeze + PAYMENT_BANKLINK_BANKS = ENV['payments_banks'].to_s.strip.split(', ').freeze + INTERNAL_PAYMENT_METHODS = %w[admin_payment system_payment].freeze + PAYMENT_METHODS = [PAYMENT_INTERMEDIARIES, PAYMENT_BANKLINK_BANKS, + INTERNAL_PAYMENT_METHODS].flatten.freeze + CUSTOMER_PAYMENT_METHODS = [PAYMENT_INTERMEDIARIES, PAYMENT_BANKLINK_BANKS].flatten.freeze + + belongs_to :invoice, optional: false + + validate :invoice_cannot_be_already_paid, on: :create + validate :supported_payment_method + + enum status: { issued: 'issued', paid: 'paid', cancelled: 'cancelled', + failed: 'failed' } + + attr_accessor :return_url, :response_url + + def self.supported_methods + supported = [] + + PAYMENT_METHODS.each do |method| + class_name = ('PaymentOrders::' + method.camelize).constantize + raise(NoMethodError, class_name) unless class_name < PaymentOrder + + supported << class_name + end + + supported + end + + def self.new_with_type(type:, invoice:) + channel = ('PaymentOrders::' + type.camelize).constantize + + PaymentOrder.new(type: channel, invoice: invoice) + end + + # Name of configuration namespace + def self.config_namespace_name; end + + def supported_payment_method + return if PaymentOrder.supported_method?(type) + + errors.add(:type, 'is not supported') + end + + def invoice_cannot_be_already_paid + return unless invoice&.paid? + + errors.add(:invoice, 'is already paid') + end + + def self.supported_method?(name, shortname: false) + some_class = if shortname + ('PaymentOrders::' + name.camelize).constantize + else + name.constantize + end + supported_methods.include? some_class + rescue NameError + false + end + + def base_transaction(sum:, paid_at:, buyer_name:) + BankTransaction.new( + description: invoice.order, + reference_no: invoice.reference_no, + currency: invoice.currency, + iban: invoice.seller_iban, + sum: sum, + paid_at: paid_at, + buyer_name: buyer_name + ) + end + + def complete_transaction + return NoMethodError unless payment_received? + + paid! + transaction = composed_transaction + transaction.save! && transaction.bind_invoice(invoice.number) + return unless transaction.errors.any? + + worded_errors = 'Failed to bind. ' + transaction.errors.full_messages.each do |err| + worded_errors << "#{err}, " + end + + update!(notes: worded_errors) + end + + def channel + type.gsub('PaymentOrders::', '') + end + + def form_url + ENV["payments_#{self.class.config_namespace_name}_url"] + end +end diff --git a/app/models/payment_orders.rb b/app/models/payment_orders.rb deleted file mode 100644 index 921af0cd4..000000000 --- a/app/models/payment_orders.rb +++ /dev/null @@ -1,15 +0,0 @@ -module PaymentOrders - PAYMENT_INTERMEDIARIES = ENV['payments_intermediaries'].to_s.strip.split(', ').freeze - PAYMENT_BANKLINK_BANKS = ENV['payments_banks'].to_s.strip.split(', ').freeze - PAYMENT_METHODS = [PAYMENT_INTERMEDIARIES, PAYMENT_BANKLINK_BANKS].flatten.freeze - - def self.create_with_type(type, invoice, opts = {}) - raise ArgumentError unless PAYMENT_METHODS.include?(type) - - if PAYMENT_BANKLINK_BANKS.include?(type) - BankLink.new(type, invoice, opts) - elsif type == 'every_pay' - EveryPay.new(type, invoice, opts) - end - end -end diff --git a/app/models/payment_orders/admin_payment.rb b/app/models/payment_orders/admin_payment.rb new file mode 100644 index 000000000..05ae061fb --- /dev/null +++ b/app/models/payment_orders/admin_payment.rb @@ -0,0 +1,9 @@ +module PaymentOrders + class AdminPayment < PaymentOrder + CONFIG_NAMESPACE = 'admin_payment'.freeze + + def self.config_namespace_name + CONFIG_NAMESPACE + end + end +end diff --git a/app/models/payment_orders/bank_link.rb b/app/models/payment_orders/bank_link.rb index 1e4c2785a..c8714d245 100644 --- a/app/models/payment_orders/bank_link.rb +++ b/app/models/payment_orders/bank_link.rb @@ -1,44 +1,44 @@ module PaymentOrders - class BankLink < Base - BANK_LINK_VERSION = '008' + class BankLink < PaymentOrder + BANK_LINK_VERSION = '008'.freeze - NEW_TRANSACTION_SERVICE_NUMBER = '1012' - SUCCESSFUL_PAYMENT_SERVICE_NUMBER = '1111' - CANCELLED_PAYMENT_SERVICE_NUMBER = '1911' + NEW_TRANSACTION_SERVICE_NUMBER = '1012'.freeze + SUCCESSFUL_PAYMENT_SERVICE_NUMBER = '1111'.freeze + CANCELLED_PAYMENT_SERVICE_NUMBER = '1911'.freeze - NEW_MESSAGE_KEYS = %w(VK_SERVICE VK_VERSION VK_SND_ID VK_STAMP VK_AMOUNT + NEW_MESSAGE_KEYS = %w[VK_SERVICE VK_VERSION VK_SND_ID VK_STAMP VK_AMOUNT VK_CURR VK_REF VK_MSG VK_RETURN VK_CANCEL - VK_DATETIME).freeze - SUCCESS_MESSAGE_KEYS = %w(VK_SERVICE VK_VERSION VK_SND_ID VK_REC_ID VK_STAMP + VK_DATETIME].freeze + SUCCESS_MESSAGE_KEYS = %w[VK_SERVICE VK_VERSION VK_SND_ID VK_REC_ID VK_STAMP VK_T_NO VK_AMOUNT VK_CURR VK_REC_ACC VK_REC_NAME VK_SND_ACC VK_SND_NAME VK_REF VK_MSG - VK_T_DATETIME).freeze - CANCEL_MESSAGE_KEYS = %w(VK_SERVICE VK_VERSION VK_SND_ID VK_REC_ID VK_STAMP - VK_REF VK_MSG).freeze + VK_T_DATETIME].freeze + CANCEL_MESSAGE_KEYS = %w[VK_SERVICE VK_VERSION VK_SND_ID VK_REC_ID VK_STAMP + VK_REF VK_MSG].freeze def form_fields hash = {} - hash["VK_SERVICE"] = NEW_TRANSACTION_SERVICE_NUMBER - hash["VK_VERSION"] = BANK_LINK_VERSION - hash["VK_SND_ID"] = seller_account - hash["VK_STAMP"] = invoice.number - hash["VK_AMOUNT"] = number_with_precision(invoice.total, precision: 2, separator: ".") - hash["VK_CURR"] = invoice.currency - hash["VK_REF"] = "" - hash["VK_MSG"] = invoice.order - hash["VK_RETURN"] = return_url - hash["VK_CANCEL"] = return_url - hash["VK_DATETIME"] = Time.zone.now.strftime("%Y-%m-%dT%H:%M:%S%z") - hash["VK_MAC"] = calc_mac(hash) - hash["VK_ENCODING"] = "UTF-8" - hash["VK_LANG"] = "ENG" + hash['VK_SERVICE'] = NEW_TRANSACTION_SERVICE_NUMBER + hash['VK_VERSION'] = BANK_LINK_VERSION + hash['VK_SND_ID'] = seller_account + hash['VK_STAMP'] = invoice.number + hash['VK_AMOUNT'] = number_with_precision(invoice.total, precision: 2, separator: ".") + hash['VK_CURR'] = invoice.currency + hash['VK_REF'] = '' + hash['VK_MSG'] = invoice.order + hash['VK_RETURN'] = return_url + hash['VK_CANCEL'] = return_url + hash['VK_DATETIME'] = Time.zone.now.strftime('%Y-%m-%dT%H:%M:%S%z') + hash['VK_MAC'] = calc_mac(hash) + hash['VK_ENCODING'] = 'UTF-8' + hash['VK_LANG'] = 'ENG' hash end def valid_response_from_intermediary? return false unless response - case response["VK_SERVICE"] + case response['VK_SERVICE'] when SUCCESSFUL_PAYMENT_SERVICE_NUMBER valid_successful_transaction? when CANCELLED_PAYMENT_SERVICE_NUMBER @@ -48,29 +48,31 @@ module PaymentOrders end end - def complete_transaction - return unless valid_successful_transaction? + def payment_received? + valid_response_from_intermediary? && settled_payment? + end - transaction = compose_or_find_transaction + def create_failure_report + notes = "User failed to make payment. Bank responded with code #{response['VK_SERVICE']}" + status = 'cancelled' + update!(notes: notes, status: status) + end - transaction.sum = response['VK_AMOUNT'] - transaction.bank_reference = response['VK_T_NO'] - transaction.buyer_bank_code = response["VK_SND_ID"] - transaction.buyer_iban = response["VK_SND_ACC"] - transaction.buyer_name = response["VK_SND_NAME"] - transaction.paid_at = Time.parse(response["VK_T_DATETIME"]) + def composed_transaction + paid_at = Time.parse(response['VK_T_DATETIME']) + transaction = base_transaction(sum: response['VK_AMOUNT'], + paid_at: paid_at, + buyer_name: response['VK_SND_NAME']) - transaction.save! - transaction.bind_invoice(invoice.number) - if transaction.errors.empty? - Rails.logger.info("Invoice ##{invoice.number} was marked as paid") - else - Rails.logger.error("Failed to bind invoice ##{invoice.number}") - end + transaction.bank_reference = response['VK_T_NO'] + transaction.buyer_bank_code = response['VK_SND_ID'] + transaction.buyer_iban = response['VK_SND_ACC'] + + transaction end def settled_payment? - response["VK_SERVICE"] == SUCCESSFUL_PAYMENT_SERVICE_NUMBER + response['VK_SERVICE'] == SUCCESSFUL_PAYMENT_SERVICE_NUMBER end private @@ -89,17 +91,15 @@ module PaymentOrders def valid_amount? source = number_with_precision( - BigDecimal.new(response["VK_AMOUNT"]), precision: 2, separator: "." - ) - target = number_with_precision( - invoice.total, precision: 2, separator: "." + BigDecimal(response['VK_AMOUNT']), precision: 2, separator: '.' ) + target = number_with_precision(invoice.total, precision: 2, separator: '.') source == target end def valid_currency? - invoice.currency == response["VK_CURR"] + invoice.currency == response['VK_CURR'] end def sign(data) @@ -117,7 +117,7 @@ module PaymentOrders def valid_mac?(hash, keys) data = keys.map { |element| prepend_size(hash[element]) }.join - verify_mac(data, hash["VK_MAC"]) + verify_mac(data, hash['VK_MAC']) end def verify_mac(data, mac) @@ -126,22 +126,22 @@ module PaymentOrders end def prepend_size(value) - value = (value || "").to_s.strip - string = "" + value = (value || '').to_s.strip + string = '' string << format("%03i", value.size) string << value end def seller_account - ENV["payments_#{type}_seller_account"] + ENV["payments_#{self.class.config_namespace_name}_seller_account"] end def seller_certificate - ENV["payments_#{type}_seller_private"] + ENV["payments_#{self.class.config_namespace_name}_seller_private"] end def bank_certificate - ENV["payments_#{type}_bank_certificate"] + ENV["payments_#{self.class.config_namespace_name}_bank_certificate"] end end end diff --git a/app/models/payment_orders/base.rb b/app/models/payment_orders/base.rb deleted file mode 100644 index 772f33ba5..000000000 --- a/app/models/payment_orders/base.rb +++ /dev/null @@ -1,59 +0,0 @@ -module PaymentOrders - class Base - include ActionView::Helpers::NumberHelper - - attr_reader :type, - :invoice, - :return_url, - :response_url, - :response - - def initialize(type, invoice, opts = {}) - @type = type - @invoice = invoice - @return_url = opts[:return_url] - @response_url = opts[:response_url] - @response = opts[:response] - end - - def create_transaction - transaction = BankTransaction.where(description: invoice.order).first_or_initialize( - reference_no: invoice.reference_no, - currency: invoice.currency, - iban: invoice.seller_iban - ) - - transaction.save! - end - - def compose_or_find_transaction - transaction = BankTransaction.find_by(base_transaction_params) - - # Transaction already autobinded (possibly) invalid invoice - if transaction.binded? - Rails.logger.info("Transaction #{transaction.id} is already binded") - Rails.logger.info('Creating new BankTransaction record.') - - transaction = new_base_transaction - end - - transaction - end - - def new_base_transaction - BankTransaction.new(base_transaction_params) - end - - def base_transaction_params - { - description: invoice.order, - currency: invoice.currency, - iban: invoice.seller_iban, - } - end - - def form_url - ENV["payments_#{type}_url"] - end - end -end diff --git a/app/models/payment_orders/every_pay.rb b/app/models/payment_orders/every_pay.rb index a866ba972..2695c20e0 100644 --- a/app/models/payment_orders/every_pay.rb +++ b/app/models/payment_orders/every_pay.rb @@ -1,9 +1,15 @@ module PaymentOrders - class EveryPay < Base - USER = ENV['payments_every_pay_api_user'].freeze - KEY = ENV['payments_every_pay_api_key'].freeze - ACCOUNT_ID = ENV['payments_every_pay_seller_account'].freeze - SUCCESSFUL_PAYMENT = %w(settled authorized).freeze + class EveryPay < PaymentOrder + USER = ENV['payments_every_pay_api_user'] + KEY = ENV['payments_every_pay_api_key'] + ACCOUNT_ID = ENV['payments_every_pay_seller_account'] + SUCCESSFUL_PAYMENT = %w[settled authorized].freeze + + CONFIG_NAMESPACE = 'every_pay'.freeze + + def self.config_namespace_name + CONFIG_NAMESPACE + end def form_fields base_json = base_params @@ -25,25 +31,23 @@ module PaymentOrders end def settled_payment? - SUCCESSFUL_PAYMENT.include?(response[:payment_state]) + SUCCESSFUL_PAYMENT.include?(response['payment_state']) end - def complete_transaction - return unless valid_response_from_intermediary? && settled_payment? + def payment_received? + valid_response_from_intermediary? && settled_payment? + end - transaction = compose_or_find_transaction + def composed_transaction + base_transaction(sum: response['amount'], + paid_at: Date.strptime(response['timestamp'], '%s'), + buyer_name: response['cc_holder_name']) + end - transaction.sum = response[:amount] - transaction.paid_at = Date.strptime(response[:timestamp], '%s') - transaction.buyer_name = response[:cc_holder_name] - - transaction.save! - transaction.bind_invoice(invoice.number) - if transaction.errors.empty? - Rails.logger.info("Invoice ##{invoice.number} marked as paid") - else - Rails.logger.error("Failed to bind invoice ##{invoice.number}") - end + def create_failure_report + notes = "User failed to make valid payment. Payment state: #{response['payment_state']}" + status = 'cancelled' + update!(notes: notes, status: status) end private @@ -63,24 +67,27 @@ module PaymentOrders end def valid_hmac? - hmac_fields = response[:hmac_fields].split(',') + hmac_fields = response['hmac_fields'].split(',') hmac_hash = {} hmac_fields.map do |field| - symbol = field.to_sym - hmac_hash[symbol] = response[symbol] + hmac_hash[field] = response[field] end hmac_string = hmac_hash.map { |key, _v| "#{key}=#{hmac_hash[key]}" }.join('&') expected_hmac = OpenSSL::HMAC.hexdigest('sha1', KEY, hmac_string) - expected_hmac == response[:hmac] + expected_hmac == response['hmac'] + rescue NoMethodError + false end def valid_amount? - invoice.total == BigDecimal.new(response[:amount]) + return false unless response.key? 'amount' + + invoice.total == BigDecimal(response['amount']) end def valid_account? - response[:account_id] == ACCOUNT_ID + response['account_id'] == ACCOUNT_ID end end end diff --git a/app/models/payment_orders/lhv.rb b/app/models/payment_orders/lhv.rb new file mode 100644 index 000000000..4c9f59c4a --- /dev/null +++ b/app/models/payment_orders/lhv.rb @@ -0,0 +1,7 @@ +module PaymentOrders + class Lhv < BankLink + def self.config_namespace_name + 'lhv' + end + end +end diff --git a/app/models/payment_orders/seb.rb b/app/models/payment_orders/seb.rb new file mode 100644 index 000000000..878d877a7 --- /dev/null +++ b/app/models/payment_orders/seb.rb @@ -0,0 +1,7 @@ +module PaymentOrders + class Seb < BankLink + def self.config_namespace_name + 'seb' + end + end +end diff --git a/app/models/payment_orders/swed.rb b/app/models/payment_orders/swed.rb new file mode 100644 index 000000000..ff3aca3d1 --- /dev/null +++ b/app/models/payment_orders/swed.rb @@ -0,0 +1,7 @@ +module PaymentOrders + class Swed < BankLink + def self.config_namespace_name + 'swed' + end + end +end diff --git a/app/models/payment_orders/system_payment.rb b/app/models/payment_orders/system_payment.rb new file mode 100644 index 000000000..47c75ebe3 --- /dev/null +++ b/app/models/payment_orders/system_payment.rb @@ -0,0 +1,9 @@ +module PaymentOrders + class SystemPayment < PaymentOrder + CONFIG_NAMESPACE = 'system_payment'.freeze + + def self.config_namespace_name + CONFIG_NAMESPACE + end + end +end diff --git a/app/models/registrar.rb b/app/models/registrar.rb index 8f41d62ca..c3522859e 100644 --- a/app/models/registrar.rb +++ b/app/models/registrar.rb @@ -1,5 +1,6 @@ class Registrar < ApplicationRecord include Versions # version/registrar_version.rb + include Concerns::Registrar::BookKeeping has_many :domains, dependent: :restrict_with_error has_many :contacts, dependent: :restrict_with_error @@ -21,9 +22,9 @@ class Registrar < ApplicationRecord validates :reference_no, format: Billing::ReferenceNo::REGEXP validate :forbid_special_code - validates :vat_rate, presence: true, if: 'vat_liable_in_foreign_country? && vat_no.blank?' + validates :vat_rate, presence: true, if: -> { vat_liable_in_foreign_country? && vat_no.blank? } validates :vat_rate, absence: true, if: :vat_liable_locally? - validates :vat_rate, absence: true, if: 'vat_liable_in_foreign_country? && vat_no?' + validates :vat_rate, absence: true, if: -> { vat_liable_in_foreign_country? && vat_no? } validates :vat_rate, numericality: { greater_than_or_equal_to: 0, less_than: 100 }, allow_nil: true @@ -33,7 +34,7 @@ class Registrar < ApplicationRecord after_initialize :set_defaults validates :email, email_format: { message: :invalid }, - allow_blank: true, if: proc { |c| c.email_changed? } + allow_blank: true, if: proc { |c| c.will_save_change_to_email? } validates :billing_email, email_format: { message: :invalid }, allow_blank: true alias_attribute :contact_email, :email @@ -99,9 +100,7 @@ class Registrar < ApplicationRecord } ] ) - - e_invoice = invoice.to_e_invoice - e_invoice.deliver + SendEInvoiceJob.enqueue(invoice.id) invoice end diff --git a/app/models/version/payment_order_version.rb b/app/models/version/payment_order_version.rb new file mode 100644 index 000000000..e556f1021 --- /dev/null +++ b/app/models/version/payment_order_version.rb @@ -0,0 +1,4 @@ +class PaymentOrderVersion < PaperTrail::Version + self.table_name = :log_payment_orders + self.sequence_name = :log_payment_orders_id_seq +end diff --git a/app/views/admin/invoices/show.haml b/app/views/admin/invoices/show.haml index e3627c158..d0450469f 100644 --- a/app/views/admin/invoices/show.haml +++ b/app/views/admin/invoices/show.haml @@ -21,3 +21,5 @@ .col-md-6= render 'registrar/invoices/partials/buyer' .row .col-md-12= render 'registrar/invoices/partials/items' +.row + .col-md-12= render 'registrar/invoices/partials/payment_orders' diff --git a/app/views/layouts/admin/base.haml b/app/views/layouts/admin/base.haml index 792a8cc0b..928629ce6 100644 --- a/app/views/layouts/admin/base.haml +++ b/app/views/layouts/admin/base.haml @@ -32,5 +32,5 @@ .footer.text-right Version - = CURRENT_COMMIT_HASH + = current_commit_link = javascript_include_tag 'admin-manifest', async: true diff --git a/app/views/layouts/registrant/application.html.erb b/app/views/layouts/registrant/application.html.erb index 66fdf3e16..c5290b70f 100644 --- a/app/views/layouts/registrant/application.html.erb +++ b/app/views/layouts/registrant/application.html.erb @@ -76,7 +76,7 @@
Version - <%= CURRENT_COMMIT_HASH %> + <%= current_commit_link %>
diff --git a/app/views/layouts/registrar/base.html.erb b/app/views/layouts/registrar/base.html.erb index 4d4f1b972..5881dd400 100644 --- a/app/views/layouts/registrar/base.html.erb +++ b/app/views/layouts/registrar/base.html.erb @@ -54,7 +54,7 @@
Version - <%= CURRENT_COMMIT_HASH %> + <%= current_commit_link %>
diff --git a/app/views/layouts/registrar/sessions.html.erb b/app/views/layouts/registrar/sessions.html.erb index 3cb345ef9..ace645199 100644 --- a/app/views/layouts/registrar/sessions.html.erb +++ b/app/views/layouts/registrar/sessions.html.erb @@ -44,7 +44,7 @@
Version - <%= CURRENT_COMMIT_HASH %> + <%= current_commit_link %>
diff --git a/app/views/registrar/domains/info.html.erb b/app/views/registrar/domains/info.html.erb index 1fcfc23c3..e88882233 100644 --- a/app/views/registrar/domains/info.html.erb +++ b/app/views/registrar/domains/info.html.erb @@ -6,6 +6,10 @@ class: 'btn btn-default') %> <%= link_to(t(:delete), delete_registrar_domains_path(domain_name: params[:domain_name]), class: 'btn btn-default') %> + <% if @client_holded %> + <%= link_to(t(:remove_client_hold), remove_hold_registrar_domains_path(domain_name: params[:domain_name]), + class: 'btn btn-default') %> + <% end %> <% else %> <%= link_to t('.transfer_btn'), new_registrar_domain_transfer_path(domain_name: params[:domain_name]), class: 'btn btn-default' %> diff --git a/app/views/registrar/invoices/partials/_payment_orders.haml b/app/views/registrar/invoices/partials/_payment_orders.haml new file mode 100644 index 000000000..d418ea1ac --- /dev/null +++ b/app/views/registrar/invoices/partials/_payment_orders.haml @@ -0,0 +1,19 @@ +%h4= "Payment Orders" +%hr +.table-responsive + %table.table.table-hover.table-condensed + %thead + %tr + %th{class: 'col-xs-1'}= "#" + %th{class: 'col-xs-1'}= "Channel" + %th{class: 'col-xs-2'}= "Status" + %th{class: 'col-xs-3'}= "Initiated" + %th{class: 'col-xs-4'}= "Notes" + %tbody + - @invoice.payment_orders.each do |payment_order| + %tr + %td= payment_order.id + %td= payment_order.channel + %td= payment_order.status + %td= payment_order.created_at + %td= payment_order.notes diff --git a/app/views/registrar/invoices/show.haml b/app/views/registrar/invoices/show.haml index 66a025eaf..5e6104091 100644 --- a/app/views/registrar/invoices/show.haml +++ b/app/views/registrar/invoices/show.haml @@ -17,4 +17,4 @@ - if @invoice.payable? .row.semifooter - .col-md-6-offset-6.text-right= render 'registrar/invoices/partials/banklinks', locals: { payment_channels: PaymentOrders::PAYMENT_METHODS } + .col-md-6-offset-6.text-right= render 'registrar/invoices/partials/banklinks', locals: { payment_channels: PaymentOrder::CUSTOMER_PAYMENT_METHODS } diff --git a/app/views/registrar/payments/pay.html.haml b/app/views/registrar/payments/pay.html.haml index 8e759f9ea..dd3fc982f 100644 --- a/app/views/registrar/payments/pay.html.haml +++ b/app/views/registrar/payments/pay.html.haml @@ -2,8 +2,8 @@ = t('registrar.invoices.redirected_to_intermediary') .payment-form - = form_tag @payment.form_url, method: :post do - - @payment.form_fields.each do |k, v| + = form_tag @payment_order.form_url, method: :post do + - @payment_order.form_fields.each do |k, v| = hidden_field_tag k, v = submit_tag t('registrar.invoices.go_to_intermediary') diff --git a/app/views/registrar/xml_consoles/epp_requests/domain/client_hold.xml b/app/views/registrar/xml_consoles/epp_requests/domain/client_hold.xml new file mode 100644 index 000000000..fcafec538 --- /dev/null +++ b/app/views/registrar/xml_consoles/epp_requests/domain/client_hold.xml @@ -0,0 +1,15 @@ + + + + + + example.ee + + + + + + timo-1579351654 + + diff --git a/app/views/registrar/xml_consoles/show.haml b/app/views/registrar/xml_consoles/show.haml index f96b67738..bb66116ee 100644 --- a/app/views/registrar/xml_consoles/show.haml +++ b/app/views/registrar/xml_consoles/show.haml @@ -29,6 +29,9 @@ , %a.js-load-xml{href: 'javascript:void(0)', data: {obj: 'domain', epp_action: 'delete'}} Delete + , + %a.js-load-xml{href: 'javascript:void(0)', data: {obj: 'domain', epp_action: 'client_hold'}} + Remove Client Hold %h4 Poll %a.js-load-xml{href: 'javascript:void(0)', data: {obj: 'poll', epp_action: 'poll'}} diff --git a/bin/bundle b/bin/bundle index 66e9889e8..8bfc37be6 100755 --- a/bin/bundle +++ b/bin/bundle @@ -1,3 +1,3 @@ #!/usr/bin/env ruby -ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __dir__) load Gem.bin_path('bundler', 'bundle') diff --git a/bin/setup b/bin/setup index b3067cf22..ab3c84d9b 100755 --- a/bin/setup +++ b/bin/setup @@ -1,33 +1,38 @@ #!/usr/bin/env ruby +# frozen_string_literal: true + require 'pathname' -require 'fileutils' +include FileUtils # path to your application root. -APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) - +APP_ROOT = Pathname.new File.expand_path('../../', __dir__) def system!(*args) system(*args) || abort("\n== Command #{args} failed ==") end -Dir.chdir APP_ROOT do +chdir APP_ROOT do + # This script is a starting point to setup your application. + # Add necessary setup steps to this file. - puts '== Installing dependencies with bundler ==' + puts '== Installing dependencies ==' system! 'gem install bundler --conservative' system('bundle check') || system!('bundle install') - puts "\n== Copying sample development database config files ==" + # Install JavaScript dependencies if using Yarn + # system('bin/yarn') + + puts "\n== Copying sample files ==" unless File.exist?('config/database.yml') - system! 'cp config/database-example-development.yml config/database.yml' + system! 'cp config/database.yml.sample config/database.yml' end puts "\n== Preparing database ==" system! 'bin/rails db:setup' puts "\n== Removing old logs and tempfiles ==" - system! 'bin/rake log:clear tmp:clear' + system! 'bin/rails log:clear tmp:clear' puts "\n== Restarting application server ==" - system! 'touch tmp/restart.txt' - + system! 'bin/rails restart' end diff --git a/bin/update b/bin/update old mode 100644 new mode 100755 diff --git a/bin/yarn b/bin/yarn new file mode 100755 index 000000000..adabdd976 --- /dev/null +++ b/bin/yarn @@ -0,0 +1,9 @@ +#!/usr/bin/env ruby +APP_ROOT = File.expand_path('..', __dir__) +Dir.chdir(APP_ROOT) do + exec 'yarnpkg', *ARGV +rescue Errno::ENOENT + $stderr.puts 'Yarn executable was not detected in the system.' + $stderr.puts 'Download Yarn at https://yarnpkg.com/en/docs/install' + exit 1 +end diff --git a/config/application.rb b/config/application.rb index c70ab2111..72b592a3b 100644 --- a/config/application.rb +++ b/config/application.rb @@ -16,6 +16,7 @@ end module DomainNameRegistry class Application < Rails::Application + config.load_defaults 5.1 # Settings in config/environments/* take precedence over those specified here. # Application configuration should go into files in config/initializers # -- all .rb files in that directory are automatically loaded. diff --git a/config/cable.yml b/config/cable.yml index 0bbde6f74..858bb1f14 100644 --- a/config/cable.yml +++ b/config/cable.yml @@ -7,3 +7,4 @@ test: production: adapter: redis url: redis://localhost:6379/1 + channel_prefix: domain_name_registry_production diff --git a/config/environments/production.rb b/config/environments/production.rb index c2262235d..6e13ea1f7 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -66,7 +66,7 @@ Rails.application.configure do # Enable locale fallbacks for I18n (makes lookups for any locale fall back to # the I18n.default_locale when a translation cannot be found). - config.i18n.fallbacks = [I18n.default_locale] + config.i18n.fallbacks = true # Send deprecation notices to registered listeners. config.active_support.deprecation = :notify diff --git a/config/environments/test.rb b/config/environments/test.rb index 6913ceb7e..784b48f42 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -1,4 +1,6 @@ + Rails.application.configure do + $VERBOSE = nil # Settings specified here will take precedence over those in config/application.rb. # The test environment is used exclusively to run your application's @@ -30,7 +32,7 @@ Rails.application.configure do config.action_mailer.raise_delivery_errors = false # Print deprecation notices to the stderr. - config.active_support.deprecation = :stderr + config.active_support.deprecation = :silence # Raises error for missing translations config.action_view.raise_on_missing_translations = true diff --git a/config/initializers/airbrake.rb b/config/initializers/airbrake.rb index 917deb02a..5c1983369 100644 --- a/config/initializers/airbrake.rb +++ b/config/initializers/airbrake.rb @@ -3,6 +3,9 @@ Airbrake.configure do |config| config.project_id = ENV['airbrake_project_id'] config.project_key = ENV['airbrake_project_key'] config.root_directory = Rails.root + config.job_stats = false + config.query_stats = false + config.performance_stats = false config.logger = if ENV['RAILS_LOG_TO_STDOUT'].present? Logger.new(STDOUT, level: Rails.logger.level) diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb index d3b8408d5..ba78ea325 100644 --- a/config/initializers/assets.rb +++ b/config/initializers/assets.rb @@ -5,7 +5,7 @@ Rails.application.configure do config.assets.version = '1.0' # Add additional assets to the asset load path - config.assets.paths << Rails.root.join('vendor', 'assets', 'fonts') + config.assets.paths << Rails.root.join('vendor', 'assets', 'fonts', 'node_modules') # Precompile additional assets. # application.js, application.css, and all non-JS/CSS in app/assets folder are already added. @@ -14,4 +14,5 @@ Rails.application.configure do config.assets.precompile += %w(admin-manifest.css admin-manifest.js) config.assets.precompile += %w(registrar-manifest.css registrar-manifest.js) config.assets.precompile += %w(registrant-manifest.css registrant-manifest.js) + config.assets.precompile += %w( admin.js admin.css ) end diff --git a/config/initializers/content_security_policy.rb b/config/initializers/content_security_policy.rb new file mode 100644 index 000000000..d3bcaa5ec --- /dev/null +++ b/config/initializers/content_security_policy.rb @@ -0,0 +1,25 @@ +# Be sure to restart your server when you modify this file. + +# Define an application-wide content security policy +# For further information see the following documentation +# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy + +# Rails.application.config.content_security_policy do |policy| +# policy.default_src :self, :https +# policy.font_src :self, :https, :data +# policy.img_src :self, :https, :data +# policy.object_src :none +# policy.script_src :self, :https +# policy.style_src :self, :https + +# # Specify URI for violation reports +# # policy.report_uri "/csp-violation-report-endpoint" +# end + +# If you are using UJS then enable automatic nonce generation +# Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) } + +# Report CSP violations to a specified URI +# For further information see the following documentation: +# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only +# Rails.application.config.content_security_policy_report_only = true diff --git a/config/initializers/current_commit_hash.rb b/config/initializers/current_commit_hash.rb deleted file mode 100644 index b018b46fd..000000000 --- a/config/initializers/current_commit_hash.rb +++ /dev/null @@ -1 +0,0 @@ -CURRENT_COMMIT_HASH = `git rev-parse --short HEAD` diff --git a/config/initializers/money.rb b/config/initializers/money.rb index cb476b589..a52ff280d 100644 --- a/config/initializers/money.rb +++ b/config/initializers/money.rb @@ -1,4 +1,6 @@ MoneyRails.configure do |config| # Wrapper for Money#default_currency with additional functionality config.default_currency = :eur -end \ No newline at end of file + config.rounding_mode = BigDecimal::ROUND_HALF_EVEN + config.locale_backend = :i18n +end diff --git a/config/initializers/new_framework_defaults.rb b/config/initializers/new_framework_defaults.rb index 99b2db3b0..2f9c3c5cb 100644 --- a/config/initializers/new_framework_defaults.rb +++ b/config/initializers/new_framework_defaults.rb @@ -6,7 +6,8 @@ # # Read the Guide for Upgrading Ruby on Rails for more info on each option. -Rails.application.config.action_controller.raise_on_unfiltered_parameters = true +# DEPRECATION WARNING: raise_on_unfiltered_parameters is deprecated and has no effect in Rails 5.1. +#Rails.application.config.action_controller.raise_on_unfiltered_parameters = true # Enable per-form CSRF tokens. Previous versions had false. Rails.application.config.action_controller.per_form_csrf_tokens = true @@ -19,7 +20,9 @@ Rails.application.config.action_controller.forgery_protection_origin_check = tru ActiveSupport.to_time_preserves_timezone = true # Do not halt callback chains when a callback returns false. Previous versions had true. -ActiveSupport.halt_callback_chains_on_return_false = false +# DEPRECATION WARNING: ActiveSupport.halt_callback_chains_on_return_false= is deprecated and +# will be removed in Rails 5.2. +#ActiveSupport.halt_callback_chains_on_return_false = false # Configure SSL options to enable HSTS with subdomains. Previous versions had false. Rails.application.config.ssl_options = { hsts: { subdomains: true } } diff --git a/config/initializers/new_framework_defaults_5_1.rb b/config/initializers/new_framework_defaults_5_1.rb new file mode 100644 index 000000000..9010abd5c --- /dev/null +++ b/config/initializers/new_framework_defaults_5_1.rb @@ -0,0 +1,14 @@ +# Be sure to restart your server when you modify this file. +# +# This file contains migration options to ease your Rails 5.1 upgrade. +# +# Once upgraded flip defaults one by one to migrate to the new default. +# +# Read the Guide for Upgrading Ruby on Rails for more info on each option. + +# Make `form_with` generate non-remote forms. +Rails.application.config.action_view.form_with_generates_remote_forms = false + +# Unknown asset fallback will return the path passed in when the given +# asset is not present in the asset pipeline. +# Rails.application.config.assets.unknown_asset_fallback = false diff --git a/config/initializers/new_framework_defaults_5_2.rb b/config/initializers/new_framework_defaults_5_2.rb new file mode 100644 index 000000000..c383d072b --- /dev/null +++ b/config/initializers/new_framework_defaults_5_2.rb @@ -0,0 +1,38 @@ +# Be sure to restart your server when you modify this file. +# +# This file contains migration options to ease your Rails 5.2 upgrade. +# +# Once upgraded flip defaults one by one to migrate to the new default. +# +# Read the Guide for Upgrading Ruby on Rails for more info on each option. + +# Make Active Record use stable #cache_key alongside new #cache_version method. +# This is needed for recyclable cache keys. +# Rails.application.config.active_record.cache_versioning = true + +# Use AES-256-GCM authenticated encryption for encrypted cookies. +# Also, embed cookie expiry in signed or encrypted cookies for increased security. +# +# This option is not backwards compatible with earlier Rails versions. +# It's best enabled when your entire app is migrated and stable on 5.2. +# +# Existing cookies will be converted on read then written with the new scheme. +# Rails.application.config.action_dispatch.use_authenticated_cookie_encryption = true + +# Use AES-256-GCM authenticated encryption as default cipher for encrypting messages +# instead of AES-256-CBC, when use_authenticated_message_encryption is set to true. +# Rails.application.config.active_support.use_authenticated_message_encryption = true + +# Add default protection from forgery to ActionController::Base instead of in +# ApplicationController. +# Rails.application.config.action_controller.default_protect_from_forgery = true + +# Store boolean values are in sqlite3 databases as 1 and 0 instead of 't' and +# 'f' after migrating old data. +# Rails.application.config.active_record.sqlite3.represent_boolean_as_integer = true + +# Use SHA-1 instead of MD5 to generate non-sensitive digests, such as the ETag header. +# Rails.application.config.active_support.use_sha1_digests = true + +# Make `form_with` generate id attributes for any generated HTML tags. +# Rails.application.config.action_view.form_with_generates_ids = true diff --git a/config/initializers/paper_trail.rb b/config/initializers/paper_trail.rb new file mode 100644 index 000000000..39a667917 --- /dev/null +++ b/config/initializers/paper_trail.rb @@ -0,0 +1 @@ +PaperTrail.config.track_associations = false diff --git a/config/locales/en.yml b/config/locales/en.yml index 74040fe98..cf72b1027 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -206,6 +206,7 @@ en: statuses: 'Statuses' description: 'Description' delete: 'Delete' + remove_client_hold: 'Remove clientHold' are_you_sure: 'Are you sure?' back: 'Back' new_domain: 'New domain' @@ -580,6 +581,7 @@ en: tech: Tech contact valid: Valid object_is_not_eligible_for_renewal: 'Object is not eligible for renewal' + object_is_not_holded: 'Object is not holded' bank_statement_desc: 'Import file row will match only when matching following attributes:
ref number
payment amount
invoice number (the first numerical value in comment field)
.' create_bank_statement: 'Create bank statement' create_bank_transaction: 'Create bank transaction' diff --git a/config/locales/registrar/payments.en.yml b/config/locales/registrar/payments.en.yml new file mode 100644 index 000000000..9c817e0ea --- /dev/null +++ b/config/locales/registrar/payments.en.yml @@ -0,0 +1,7 @@ +en: + registrar: + payments: + back: + payment_successful: 'Thank you! Payment received successfully.' + successful_payment_backend_error: 'We received your payment, but something went wrong on our side. Please contact us via email or phone.' + payment_not_received: 'Payment was unsuccessful. Please make sure you have enough funds on your account and try again.' diff --git a/config/locales/registrars.en.yml b/config/locales/registrars.en.yml index 609f9f94a..c57f2e891 100644 --- a/config/locales/registrars.en.yml +++ b/config/locales/registrars.en.yml @@ -1,4 +1,8 @@ en: + registrar: + invoice_yearly_product_description: '%{tld} registration: %{length} year(s)' + invoice_monthly_product_description: '%{tld} registration: %{length} month(s)' + monthly_summary_title: 'Domain registrations - %{date}' activerecord: errors: models: @@ -8,4 +12,4 @@ en: forbidden: is forbidden vat_rate: present: >- - must be blank when a registrar is VAT-registered in the same country as registry \ No newline at end of file + must be blank when a registrar is VAT-registered in the same country as registry diff --git a/config/locales/registrars.et.yml b/config/locales/registrars.et.yml new file mode 100644 index 000000000..1001638c1 --- /dev/null +++ b/config/locales/registrars.et.yml @@ -0,0 +1,5 @@ +et: + registrar: + invoice_yearly_product_description: '%{tld} registreerimine: %{length} aasta(t)' + invoice_monthly_product_description: '%{tld} registreerimine: %{length} kuu(d)' + monthly_summary_title: 'Domeenide registreerimine - %{date}' diff --git a/config/puma.rb b/config/puma.rb index c7f311f81..1e19380dc 100644 --- a/config/puma.rb +++ b/config/puma.rb @@ -1,13 +1,13 @@ # Puma can serve each request in a thread from an internal thread pool. -# The `threads` method setting takes two numbers a minimum and maximum. +# The `threads` method setting takes two numbers: a minimum and maximum. # Any libraries that use thread pools should be configured to match # the maximum value specified for Puma. Default is set to 5 threads for minimum -# and maximum, this matches the default thread size of Active Record. +# and maximum; this matches the default thread size of Active Record. # -threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }.to_i +threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 } threads threads_count, threads_count -# Specifies the `port` that Puma will listen on to receive requests, default is 3000. +# Specifies the `port` that Puma will listen on to receive requests; default is 3000. # port ENV.fetch("PORT") { 3000 } @@ -32,16 +32,25 @@ environment ENV.fetch("RAILS_ENV") { "development" } # # preload_app! +# If you are preloading your application and using Active Record, it's +# recommended that you close any connections to the database before workers +# are forked to prevent connection leakage. +# +# before_fork do +# ActiveRecord::Base.connection_pool.disconnect! if defined?(ActiveRecord) +# end + # The code in the `on_worker_boot` will be called if you are using # clustered mode by specifying a number of `workers`. After each worker -# process is booted this block will be run, if you are using `preload_app!` -# option you will want to use this block to reconnect to any threads -# or connections that may have been created at application boot, Ruby +# process is booted, this block will be run. If you are using the `preload_app!` +# option, you will want to use this block to reconnect to any threads +# or connections that may have been created at application boot, as Ruby # cannot share connections between processes. # # on_worker_boot do # ActiveRecord::Base.establish_connection if defined?(ActiveRecord) # end +# # Allow puma to be restarted by `rails restart` command. plugin :tmp_restart diff --git a/config/routes.rb b/config/routes.rb index 135fe8eb0..53d78dfa9 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -99,6 +99,7 @@ Rails.application.routes.draw do get 'check' get 'delete' get 'search_contacts' + get 'remove_hold' end end resources :domain_transfers, only: %i[new create] @@ -127,11 +128,11 @@ Rails.application.routes.draw do end end - get 'pay/return/:bank' => 'payments#back', as: 'return_payment_with' - post 'pay/return/:bank' => 'payments#back' - put 'pay/return/:bank' => 'payments#back' - post 'pay/callback/:bank' => 'payments#callback', as: 'response_payment_with' - get 'pay/go/:bank' => 'payments#pay', as: 'payment_with' + get 'pay/return/:payment_order' => 'payments#back', as: 'return_payment_with' + post 'pay/return/:payment_order' => 'payments#back' + put 'pay/return/:payment_order' => 'payments#back' + post 'pay/callback/:payment_order' => 'payments#callback', as: 'response_payment_with' + get 'pay/go/:bank' => 'payments#pay', as: 'payment_with' namespace :settings do resource :balance_auto_reload, controller: :balance_auto_reload, only: %i[edit update destroy] diff --git a/config/spring.rb b/config/spring.rb new file mode 100644 index 000000000..c9119b40c --- /dev/null +++ b/config/spring.rb @@ -0,0 +1,6 @@ +%w( + .ruby-version + .rbenv-vars + tmp/restart.txt + tmp/caching-dev.txt +).each { |path| Spring.watch(path) } diff --git a/config/storage.yml b/config/storage.yml new file mode 100644 index 000000000..d32f76e8f --- /dev/null +++ b/config/storage.yml @@ -0,0 +1,34 @@ +test: + service: Disk + root: <%= Rails.root.join("tmp/storage") %> + +local: + service: Disk + root: <%= Rails.root.join("storage") %> + +# Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key) +# amazon: +# service: S3 +# access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %> +# secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %> +# region: us-east-1 +# bucket: your_own_bucket + +# Remember not to checkin your GCS keyfile to a repository +# google: +# service: GCS +# project: your_project +# credentials: <%= Rails.root.join("path/to/gcs.keyfile") %> +# bucket: your_own_bucket + +# Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key) +# microsoft: +# service: AzureStorage +# storage_account_name: your_account_name +# storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %> +# container: your_container_name + +# mirror: +# service: Mirror +# primary: local +# mirrors: [ amazon, google, microsoft ] diff --git a/db/data/20150601083516_add_cert_common_name.rb b/db/data/20150601083516_add_cert_common_name.rb index ef401b871..d0959e5b7 100644 --- a/db/data/20150601083516_add_cert_common_name.rb +++ b/db/data/20150601083516_add_cert_common_name.rb @@ -1,16 +1,15 @@ -class AddCertCommonName < ActiveRecord::Migration +class AddCertCommonName < ActiveRecord::Migration[5.1] def self.up - Certificate.all.each do |x| - if x.crt.blank? && x.csr.present? - pc = x.parsed_csr.try(:subject).try(:to_s) || '' - cn = pc.scan(/\/CN=(.+)/).flatten.first - x.common_name = cn.split('/').first - end - x.save - end + # Certificate.all.each do |x| + # if x.crt.blank? && x.csr.present? + # pc = x.parsed_csr.try(:subject).try(:to_s) || '' + # cn = pc.scan(/\/CN=(.+)/).flatten.first + # x.common_name = cn.split('/').first + # end + # x.save + # end end def self.down - raise ActiveRecord::IrreversibleMigration end end diff --git a/db/data/20150601083800_add_cert_md5.rb b/db/data/20150601083800_add_cert_md5.rb index 5efe4e596..4db005177 100644 --- a/db/data/20150601083800_add_cert_md5.rb +++ b/db/data/20150601083800_add_cert_md5.rb @@ -1,28 +1,27 @@ -class AddCertMd5 < ActiveRecord::Migration +class AddCertMd5 < ActiveRecord::Migration[5.1] def self.up - Certificate.all.each do |x| - if x.crt.present? && x.csr.present? - x.interface = Certificate::REGISTRAR - x.md5 = OpenSSL::Digest::MD5.new(x.parsed_crt.to_der).to_s - - pc = x.parsed_crt.try(:subject).try(:to_s) || '' - cn = pc.scan(/\/CN=(.+)/).flatten.first - x.common_name = cn.split('/').first - elsif x.crt.present? && x.csr.blank? - x.interface = Certificate::API - x.md5 = OpenSSL::Digest::MD5.new(x.parsed_crt.to_der).to_s - - pc = x.parsed_crt.try(:subject).try(:to_s) || '' - cn = pc.scan(/\/CN=(.+)/).flatten.first - x.common_name = cn.split('/').first - elsif x.crt.blank? && x.csr.present? - x.interface = Certificate::REGISTRAR - end - x.save - end + # Certificate.all.each do |x| + # if x.crt.present? && x.csr.present? + # x.interface = Certificate::REGISTRAR + # x.md5 = OpenSSL::Digest::MD5.new(x.parsed_crt.to_der).to_s + # + # pc = x.parsed_crt.try(:subject).try(:to_s) || '' + # cn = pc.scan(/\/CN=(.+)/).flatten.first + # x.common_name = cn.split('/').first + # elsif x.crt.present? && x.csr.blank? + # x.interface = Certificate::API + # x.md5 = OpenSSL::Digest::MD5.new(x.parsed_crt.to_der).to_s + # + # pc = x.parsed_crt.try(:subject).try(:to_s) || '' + # cn = pc.scan(/\/CN=(.+)/).flatten.first + # x.common_name = cn.split('/').first + # elsif x.crt.blank? && x.csr.present? + # x.interface = Certificate::REGISTRAR + # end + # x.save + # end end def self.down - raise ActiveRecord::IrreversibleMigration end end diff --git a/db/data/20150609093515_add_renew_setting.rb b/db/data/20150609093515_add_renew_setting.rb index f462c38cb..2d99aa448 100644 --- a/db/data/20150609093515_add_renew_setting.rb +++ b/db/data/20150609093515_add_renew_setting.rb @@ -1,9 +1,8 @@ -class AddRenewSetting < ActiveRecord::Migration +class AddRenewSetting < ActiveRecord::Migration[5.1] def self.up - Setting.days_to_renew_domain_before_expire = 90 + # Setting.days_to_renew_domain_before_expire = 90 end def self.down - raise ActiveRecord::IrreversibleMigration end end diff --git a/db/data/20150610111019_add_expire_settings.rb b/db/data/20150610111019_add_expire_settings.rb index 9f8b9cce8..6171536dd 100644 --- a/db/data/20150610111019_add_expire_settings.rb +++ b/db/data/20150610111019_add_expire_settings.rb @@ -1,10 +1,9 @@ -class AddExpireSettings < ActiveRecord::Migration +class AddExpireSettings < ActiveRecord::Migration[5.1] def self.up - Setting.expire_warning_period = 15 - Setting.redemption_grace_period = 30 + # Setting.expire_warning_period = 15 + # Setting.redemption_grace_period = 30 end def self.down - raise ActiveRecord::IrreversibleMigration end end diff --git a/db/data/20150612125720_refactor_domain_statuses.rb b/db/data/20150612125720_refactor_domain_statuses.rb index 00e87b4d0..de0733e3f 100644 --- a/db/data/20150612125720_refactor_domain_statuses.rb +++ b/db/data/20150612125720_refactor_domain_statuses.rb @@ -1,15 +1,14 @@ -class RefactorDomainStatuses < ActiveRecord::Migration +class RefactorDomainStatuses < ActiveRecord::Migration[5.1] def self.up - Domain.find_each do |x| - statuses = [] - x.domain_statuses.each do |ds| - statuses << ds.value - end - x.update_column('statuses', statuses) - end + # Domain.find_each do |x| + # statuses = [] + # x.domain_statuses.each do |ds| + # statuses << ds.value + # end + # x.update_column('statuses', statuses) if x.statuses.blank? + # end end def self.down - raise ActiveRecord::IrreversibleMigration end end diff --git a/db/data/20150707103801_refactor_contact_statuses.rb b/db/data/20150707103801_refactor_contact_statuses.rb index be6312016..e1833dd66 100644 --- a/db/data/20150707103801_refactor_contact_statuses.rb +++ b/db/data/20150707103801_refactor_contact_statuses.rb @@ -1,15 +1,14 @@ -class RefactorContactStatuses < ActiveRecord::Migration +class RefactorContactStatuses < ActiveRecord::Migration[5.1] def self.up - Contact.find_each do |contact| - statuses = [] - contact.depricated_statuses.each do |ds| - statuses << ds.value - end - contact.update_column('statuses', statuses) - end + # Contact.find_each do |contact| + # statuses = [] + # contact.depricated_statuses.each do |ds| + # statuses << ds.value + # end + # contact.update_column('statuses', statuses) + # end end def self.down - raise ActiveRecord::IrreversibleMigration end end diff --git a/db/data/20200225085234_convert_domain_delete_date.rb b/db/data/20200225085234_convert_domain_delete_date.rb new file mode 100644 index 000000000..81f070927 --- /dev/null +++ b/db/data/20200225085234_convert_domain_delete_date.rb @@ -0,0 +1,19 @@ +class ConvertDomainDeleteDate < ActiveRecord::Migration[5.1] + def up + # processed_domain_count = 0 + # + # Domain.transaction do + # Domain.find_each do |domain| + # next unless domain.delete_date + # + # domain.update_columns(delete_date: domain.delete_date + 1.day) + # processed_domain_count += 1 + # end + # end + # + # puts "Domains processed: #{processed_domain_count}" + end + + def down + end +end diff --git a/db/data/20200225085433_delete_orphaned_registrant_verifications.rb b/db/data/20200225085433_delete_orphaned_registrant_verifications.rb new file mode 100644 index 000000000..73c270a6a --- /dev/null +++ b/db/data/20200225085433_delete_orphaned_registrant_verifications.rb @@ -0,0 +1,18 @@ +class DeleteOrphanedRegistrantVerifications < ActiveRecord::Migration[5.1] + def up + # orphaned_registrant_verifications = RegistrantVerification.where.not(domain_id: Domain.ids) + # orphaned_registrant_verification_count = orphaned_registrant_verifications.count + # processed_registrant_verification_count = 0 + # + # orphaned_registrant_verifications.each do |registrant_verification| + # registrant_verification.destroy! + # processed_registrant_verification_count += 1 + # end + # + # puts "Processed: #{processed_registrant_verification_count} out of" \ + # " #{orphaned_registrant_verification_count}" + end + + def down + end +end diff --git a/db/data/20200225085539_regenerate_registrar_reference_numbers.rb b/db/data/20200225085539_regenerate_registrar_reference_numbers.rb new file mode 100644 index 000000000..fbd2a5c5f --- /dev/null +++ b/db/data/20200225085539_regenerate_registrar_reference_numbers.rb @@ -0,0 +1,19 @@ +class RegenerateRegistrarReferenceNumbers < ActiveRecord::Migration[5.1] + def up + # processed_registrar_count = 0 + # + # Registrar.transaction do + # Registrar.all.each do |registrar| + # next unless registrar.reference_no.start_with?('RF') + # + # registrar.update_columns(reference_no: Billing::ReferenceNo.generate) + # processed_registrar_count += 1 + # end + # end + # + # puts "Registrars processed: #{processed_registrar_count}" + end + + def down + end +end diff --git a/db/data_schema.rb b/db/data_schema.rb new file mode 100644 index 000000000..f4a3f5d8a --- /dev/null +++ b/db/data_schema.rb @@ -0,0 +1,2 @@ +# encoding: UTF-8 +DataMigrate::Data.define(version: 20150707103801) diff --git a/db/migrate/20200130092113_create_payment_orders.rb b/db/migrate/20200130092113_create_payment_orders.rb new file mode 100644 index 000000000..97d86a034 --- /dev/null +++ b/db/migrate/20200130092113_create_payment_orders.rb @@ -0,0 +1,15 @@ +class CreatePaymentOrders < ActiveRecord::Migration[5.0] + def change + create_table :payment_orders do |t| + t.string :type, null: false + t.string :status, default: 'issued', null: false + t.belongs_to :invoice, foreign_key: true + t.jsonb :response, null: true + t.string :notes, null: true + t.string :creator_str + t.string :updator_str + + t.timestamps + end + end +end diff --git a/db/migrate/20200203143458_create_payment_order_versions.rb b/db/migrate/20200203143458_create_payment_order_versions.rb new file mode 100644 index 000000000..d02b300e1 --- /dev/null +++ b/db/migrate/20200203143458_create_payment_order_versions.rb @@ -0,0 +1,16 @@ +class CreatePaymentOrderVersions < ActiveRecord::Migration[5.0] + def change + create_table :log_payment_orders do |t| + t.string :item_type, null: false + t.integer :item_id, null: false + t.string :event, null: false + t.string :whodunnit + t.jsonb :object + t.jsonb :object_changes + t.datetime :created_at + t.string :session + t.jsonb :children + t.string :uuid + end + end +end diff --git a/db/migrate/20200204103125_add_e_invoice_sent_at_to_invoice.rb b/db/migrate/20200204103125_add_e_invoice_sent_at_to_invoice.rb new file mode 100644 index 000000000..e0e5f2cd0 --- /dev/null +++ b/db/migrate/20200204103125_add_e_invoice_sent_at_to_invoice.rb @@ -0,0 +1,5 @@ +class AddEInvoiceSentAtToInvoice < ActiveRecord::Migration[5.0] + def change + add_column :invoices, :e_invoice_sent_at, :datetime + end +end diff --git a/db/migrate/20200311114649_update_zone_generation_migration.rb b/db/migrate/20200311114649_update_zone_generation_migration.rb new file mode 100644 index 000000000..2c516474d --- /dev/null +++ b/db/migrate/20200311114649_update_zone_generation_migration.rb @@ -0,0 +1,247 @@ +class UpdateZoneGenerationMigration < ActiveRecord::Migration[5.1] + def up + execute <<-SQL + CREATE OR REPLACE FUNCTION generate_zonefile(i_origin character varying) RETURNS text + LANGUAGE plpgsql + AS $_$ + DECLARE + zone_header text := concat('$ORIGIN ', i_origin, '.'); + serial_num varchar; + include_filter varchar := ''; + exclude_filter varchar := ''; + tmp_var text; + ret text; + BEGIN + -- define filters + include_filter = '%.' || i_origin; + + -- for %.%.% + IF i_origin ~ '.' THEN + exclude_filter := ''; + -- for %.% + ELSE + exclude_filter := '%.%.' || i_origin; + END IF; + + SELECT ROUND(extract(epoch from now() at time zone 'utc')) INTO serial_num; + + -- zonefile header + SELECT concat( + format('%-10s', '$ORIGIN .'), chr(10), + format('%-10s', '$TTL'), zf.ttl, chr(10), chr(10), + format('%-10s', i_origin || '.'), 'IN SOA ', zf.master_nameserver, '. ', zf.email, '. (', chr(10), + format('%-17s', ''), format('%-12s', serial_num), '; serial number', chr(10), + format('%-17s', ''), format('%-12s', zf.refresh), '; refresh, seconds', chr(10), + format('%-17s', ''), format('%-12s', zf.retry), '; retry, seconds', chr(10), + format('%-17s', ''), format('%-12s', zf.expire), '; expire, seconds', chr(10), + format('%-17s', ''), format('%-12s', zf.minimum_ttl), '; minimum TTL, seconds', chr(10), + format('%-17s', ''), ')' + ) FROM zones zf WHERE i_origin = zf.origin INTO tmp_var; + + ret = concat(tmp_var, chr(10), chr(10)); + + -- origin ns records + SELECT ns_records FROM zones zf WHERE i_origin = zf.origin INTO tmp_var; + ret := concat(ret, '; Zone NS Records', chr(10), tmp_var, chr(10)); + + -- ns records + SELECT array_to_string( + array( + SELECT concat(d.name_puny, '. IN NS ', coalesce(ns.hostname_puny, ns.hostname), '.') + FROM domains d + JOIN nameservers ns ON ns.domain_id = d.id + WHERE d.name LIKE include_filter AND d.name NOT LIKE exclude_filter + AND NOT ('{serverHold,clientHold,inactive}' && d.statuses) + ORDER BY d.name + ), + chr(10) + ) INTO tmp_var; + + ret := concat(ret, tmp_var, chr(10), chr(10)); + + -- origin a glue records + SELECT a_records FROM zones zf WHERE i_origin = zf.origin INTO tmp_var; + ret := concat(ret, '; Zone A Records', chr(10), tmp_var, chr(10)); + + -- a glue records for other nameservers + SELECT array_to_string( + array( + SELECT concat(coalesce(ns.hostname_puny, ns.hostname), '. IN A ', unnest(ns.ipv4)) + FROM nameservers ns + JOIN domains d ON d.id = ns.domain_id + WHERE d.name LIKE include_filter AND d.name NOT LIKE exclude_filter + AND (ns.hostname LIKE '%.' || d.name) OR (ns.hostname LIKE d.name) + AND d.name <> i_origin + AND ns.ipv4 IS NOT NULL AND ns.ipv4 <> '{}' + AND NOT ('{serverHold,clientHold,inactive}' && d.statuses) + ), chr(10) + ) INTO tmp_var; + + ret := concat(ret, tmp_var, chr(10), chr(10)); + + -- origin aaaa glue records + SELECT a4_records FROM zones zf WHERE i_origin = zf.origin INTO tmp_var; + ret := concat(ret, '; Zone AAAA Records', chr(10), tmp_var, chr(10)); + + -- aaaa glue records for other nameservers + SELECT array_to_string( + array( + SELECT concat(coalesce(ns.hostname_puny, ns.hostname), '. IN AAAA ', unnest(ns.ipv6)) + FROM nameservers ns + JOIN domains d ON d.id = ns.domain_id + WHERE d.name LIKE include_filter AND d.name NOT LIKE exclude_filter + AND (ns.hostname LIKE '%.' || d.name) OR (ns.hostname LIKE d.name) + AND d.name <> i_origin + AND ns.ipv6 IS NOT NULL AND ns.ipv6 <> '{}' + AND NOT ('{serverHold,clientHold,inactive}' && d.statuses) + ), chr(10) + ) INTO tmp_var; + + ret := concat(ret, tmp_var, chr(10), chr(10)); + + -- ds records + SELECT array_to_string( + array( + SELECT concat( + d.name_puny, '. 3600 IN DS ', dk.ds_key_tag, ' ', + dk.ds_alg, ' ', dk.ds_digest_type, ' ', dk.ds_digest + ) + FROM domains d + JOIN dnskeys dk ON dk.domain_id = d.id + WHERE d.name LIKE include_filter AND d.name NOT LIKE exclude_filter AND dk.flags = 257 + AND NOT ('{serverHold,clientHold,inactive}' && d.statuses) + ), + chr(10) + ) INTO tmp_var; + + ret := concat(ret, '; Zone DS Records', chr(10), tmp_var, chr(10)); + + RETURN ret; + END; + $_$; + SQL + end + + def down + execute <<-SQL + CREATE OR REPLACE FUNCTION generate_zonefile(i_origin character varying) RETURNS text + LANGUAGE plpgsql + AS $_$ + DECLARE + zone_header text := concat('$ORIGIN ', i_origin, '.'); + serial_num varchar; + include_filter varchar := ''; + exclude_filter varchar := ''; + tmp_var text; + ret text; + BEGIN + -- define filters + include_filter = '%.' || i_origin; + + -- for %.%.% + IF i_origin ~ '.' THEN + exclude_filter := ''; + -- for %.% + ELSE + exclude_filter := '%.%.' || i_origin; + END IF; + + SELECT ROUND(extract(epoch from now() at time zone 'utc')) INTO serial_num; + + -- zonefile header + SELECT concat( + format('%-10s', '$ORIGIN .'), chr(10), + format('%-10s', '$TTL'), zf.ttl, chr(10), chr(10), + format('%-10s', i_origin || '.'), 'IN SOA ', zf.master_nameserver, '. ', zf.email, '. (', chr(10), + format('%-17s', ''), format('%-12s', serial_num), '; serial number', chr(10), + format('%-17s', ''), format('%-12s', zf.refresh), '; refresh, seconds', chr(10), + format('%-17s', ''), format('%-12s', zf.retry), '; retry, seconds', chr(10), + format('%-17s', ''), format('%-12s', zf.expire), '; expire, seconds', chr(10), + format('%-17s', ''), format('%-12s', zf.minimum_ttl), '; minimum TTL, seconds', chr(10), + format('%-17s', ''), ')' + ) FROM zones zf WHERE i_origin = zf.origin INTO tmp_var; + + ret = concat(tmp_var, chr(10), chr(10)); + + -- origin ns records + SELECT ns_records FROM zones zf WHERE i_origin = zf.origin INTO tmp_var; + ret := concat(ret, '; Zone NS Records', chr(10), tmp_var, chr(10)); + + -- ns records + SELECT array_to_string( + array( + SELECT concat(d.name_puny, '. IN NS ', coalesce(ns.hostname_puny, ns.hostname), '.') + FROM domains d + JOIN nameservers ns ON ns.domain_id = d.id + WHERE d.name LIKE include_filter AND d.name NOT LIKE exclude_filter + AND NOT ('{serverHold,clientHold,inactive}' && d.statuses) + ORDER BY d.name + ), + chr(10) + ) INTO tmp_var; + + ret := concat(ret, tmp_var, chr(10), chr(10)); + + -- origin a glue records + SELECT a_records FROM zones zf WHERE i_origin = zf.origin INTO tmp_var; + ret := concat(ret, '; Zone A Records', chr(10), tmp_var, chr(10)); + + -- a glue records for other nameservers + SELECT array_to_string( + array( + SELECT concat(coalesce(ns.hostname_puny, ns.hostname), '. IN A ', unnest(ns.ipv4)) + FROM nameservers ns + JOIN domains d ON d.id = ns.domain_id + WHERE d.name LIKE include_filter AND d.name NOT LIKE exclude_filter + AND ns.hostname LIKE '%.' || d.name + AND d.name <> i_origin + AND ns.ipv4 IS NOT NULL AND ns.ipv4 <> '{}' + AND NOT ('{serverHold,clientHold,inactive}' && d.statuses) + ), chr(10) + ) INTO tmp_var; + + ret := concat(ret, tmp_var, chr(10), chr(10)); + + -- origin aaaa glue records + SELECT a4_records FROM zones zf WHERE i_origin = zf.origin INTO tmp_var; + ret := concat(ret, '; Zone AAAA Records', chr(10), tmp_var, chr(10)); + + -- aaaa glue records for other nameservers + SELECT array_to_string( + array( + SELECT concat(coalesce(ns.hostname_puny, ns.hostname), '. IN AAAA ', unnest(ns.ipv6)) + FROM nameservers ns + JOIN domains d ON d.id = ns.domain_id + WHERE d.name LIKE include_filter AND d.name NOT LIKE exclude_filter + AND ns.hostname LIKE '%.' || d.name + AND d.name <> i_origin + AND ns.ipv6 IS NOT NULL AND ns.ipv6 <> '{}' + AND NOT ('{serverHold,clientHold,inactive}' && d.statuses) + ), chr(10) + ) INTO tmp_var; + + ret := concat(ret, tmp_var, chr(10), chr(10)); + + -- ds records + SELECT array_to_string( + array( + SELECT concat( + d.name_puny, '. 3600 IN DS ', dk.ds_key_tag, ' ', + dk.ds_alg, ' ', dk.ds_digest_type, ' ', dk.ds_digest + ) + FROM domains d + JOIN dnskeys dk ON dk.domain_id = d.id + WHERE d.name LIKE include_filter AND d.name NOT LIKE exclude_filter AND dk.flags = 257 + AND NOT ('{serverHold,clientHold,inactive}' && d.statuses) + ), + chr(10) + ) INTO tmp_var; + + ret := concat(ret, '; Zone DS Records', chr(10), tmp_var, chr(10)); + + RETURN ret; + END; + $_$; + SQL + end +end diff --git a/db/structure.sql b/db/structure.sql index a23623bae..604238d4c 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -140,7 +140,7 @@ CREATE FUNCTION public.generate_zonefile(i_origin character varying) RETURNS tex FROM nameservers ns JOIN domains d ON d.id = ns.domain_id WHERE d.name LIKE include_filter AND d.name NOT LIKE exclude_filter - AND ns.hostname LIKE '%.' || d.name + AND (ns.hostname LIKE '%.' || d.name) OR (ns.hostname LIKE d.name) AND d.name <> i_origin AND ns.ipv4 IS NOT NULL AND ns.ipv4 <> '{}' AND NOT ('{serverHold,clientHold,inactive}' && d.statuses) @@ -160,7 +160,7 @@ CREATE FUNCTION public.generate_zonefile(i_origin character varying) RETURNS tex FROM nameservers ns JOIN domains d ON d.id = ns.domain_id WHERE d.name LIKE include_filter AND d.name NOT LIKE exclude_filter - AND ns.hostname LIKE '%.' || d.name + AND (ns.hostname LIKE '%.' || d.name) OR (ns.hostname LIKE d.name) AND d.name <> i_origin AND ns.ipv6 IS NOT NULL AND ns.ipv6 <> '{}' AND NOT ('{serverHold,clientHold,inactive}' && d.statuses) @@ -746,7 +746,6 @@ CREATE TABLE public.domains ( locked_by_registrant_at timestamp without time zone, force_delete_start timestamp without time zone, force_delete_data public.hstore - ); @@ -886,6 +885,7 @@ CREATE TABLE public.invoices ( in_directo boolean DEFAULT false, buyer_vat_no character varying, issue_date date NOT NULL, + e_invoice_sent_at timestamp without time zone, CONSTRAINT invoices_due_date_is_not_before_issue_date CHECK ((due_date >= issue_date)) ); @@ -1515,6 +1515,44 @@ CREATE SEQUENCE public.log_notifications_id_seq ALTER SEQUENCE public.log_notifications_id_seq OWNED BY public.log_notifications.id; +-- +-- Name: log_payment_orders; Type: TABLE; Schema: public; Owner: -; Tablespace: +-- + +CREATE TABLE public.log_payment_orders ( + id integer NOT NULL, + item_type character varying NOT NULL, + item_id integer NOT NULL, + event character varying NOT NULL, + whodunnit character varying, + object jsonb, + object_changes jsonb, + created_at timestamp without time zone, + session character varying, + children jsonb, + uuid character varying +); + + +-- +-- Name: log_payment_orders_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.log_payment_orders_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: log_payment_orders_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.log_payment_orders_id_seq OWNED BY public.log_payment_orders.id; + + -- -- Name: log_registrant_verifications; Type: TABLE; Schema: public; Owner: -; Tablespace: -- @@ -1818,6 +1856,43 @@ CREATE SEQUENCE public.notifications_id_seq ALTER SEQUENCE public.notifications_id_seq OWNED BY public.notifications.id; +-- +-- Name: payment_orders; Type: TABLE; Schema: public; Owner: -; Tablespace: +-- + +CREATE TABLE public.payment_orders ( + id integer NOT NULL, + type character varying NOT NULL, + status character varying DEFAULT 'issued'::character varying NOT NULL, + invoice_id integer, + response jsonb, + notes character varying, + creator_str character varying, + updator_str character varying, + created_at timestamp without time zone NOT NULL, + updated_at timestamp without time zone NOT NULL +); + + +-- +-- Name: payment_orders_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.payment_orders_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: payment_orders_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.payment_orders_id_seq OWNED BY public.payment_orders.id; + + -- -- Name: prices; Type: TABLE; Schema: public; Owner: -; Tablespace: -- @@ -2499,6 +2574,13 @@ ALTER TABLE ONLY public.log_nameservers ALTER COLUMN id SET DEFAULT nextval('pub ALTER TABLE ONLY public.log_notifications ALTER COLUMN id SET DEFAULT nextval('public.log_notifications_id_seq'::regclass); +-- +-- Name: id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.log_payment_orders ALTER COLUMN id SET DEFAULT nextval('public.log_payment_orders_id_seq'::regclass); + + -- -- Name: id; Type: DEFAULT; Schema: public; Owner: - -- @@ -2555,6 +2637,13 @@ ALTER TABLE ONLY public.nameservers ALTER COLUMN id SET DEFAULT nextval('public. ALTER TABLE ONLY public.notifications ALTER COLUMN id SET DEFAULT nextval('public.notifications_id_seq'::regclass); +-- +-- Name: id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.payment_orders ALTER COLUMN id SET DEFAULT nextval('public.payment_orders_id_seq'::regclass); + + -- -- Name: id; Type: DEFAULT; Schema: public; Owner: - -- @@ -2904,6 +2993,14 @@ ALTER TABLE ONLY public.log_notifications ADD CONSTRAINT log_notifications_pkey PRIMARY KEY (id); +-- +-- Name: log_payment_orders_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: +-- + +ALTER TABLE ONLY public.log_payment_orders + ADD CONSTRAINT log_payment_orders_pkey PRIMARY KEY (id); + + -- -- Name: log_registrant_verifications_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: -- @@ -2968,6 +3065,14 @@ ALTER TABLE ONLY public.notifications ADD CONSTRAINT notifications_pkey PRIMARY KEY (id); +-- +-- Name: payment_orders_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: +-- + +ALTER TABLE ONLY public.payment_orders + ADD CONSTRAINT payment_orders_pkey PRIMARY KEY (id); + + -- -- Name: prices_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: -- @@ -3629,6 +3734,13 @@ CREATE INDEX index_nameservers_on_domain_id ON public.nameservers USING btree (d CREATE INDEX index_notifications_on_registrar_id ON public.notifications USING btree (registrar_id); +-- +-- Name: index_payment_orders_on_invoice_id; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- + +CREATE INDEX index_payment_orders_on_invoice_id ON public.payment_orders USING btree (invoice_id); + + -- -- Name: index_prices_on_zone_id; Type: INDEX; Schema: public; Owner: -; Tablespace: -- @@ -3895,6 +4007,14 @@ ALTER TABLE ONLY public.registrant_verifications ADD CONSTRAINT fk_rails_f41617a0e9 FOREIGN KEY (domain_id) REFERENCES public.domains(id); +-- +-- Name: fk_rails_f9dc5857c3; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.payment_orders + ADD CONSTRAINT fk_rails_f9dc5857c3 FOREIGN KEY (invoice_id) REFERENCES public.invoices(id); + + -- -- Name: invoice_items_invoice_id_fk; Type: FK CONSTRAINT; Schema: public; Owner: - -- @@ -4339,6 +4459,9 @@ INSERT INTO "schema_migrations" (version) VALUES ('20191212133136'), ('20191227110904'), ('20200113091254'), -('20200115102202'); - +('20200115102202'), +('20200130092113'), +('20200203143458'), +('20200204103125'), +('20200311114649'); diff --git a/lib/tasks/data_migrations/.keep b/lib/tasks/data_migrations/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/lib/tasks/data_migrations/convert_domain_delete_date.rake b/lib/tasks/data_migrations/convert_domain_delete_date.rake deleted file mode 100644 index 7eeee5cf0..000000000 --- a/lib/tasks/data_migrations/convert_domain_delete_date.rake +++ /dev/null @@ -1,16 +0,0 @@ -namespace :data_migrations do - task convert_domain_delete_date: :environment do - processed_domain_count = 0 - - Domain.transaction do - Domain.find_each do |domain| - next unless domain.delete_date - - domain.update_columns(delete_date: domain.delete_date + 1.day) - processed_domain_count += 1 - end - end - - puts "Domains processed: #{processed_domain_count}" - end -end \ No newline at end of file diff --git a/lib/tasks/data_migrations/delete_orphaned_registrant_verifications.rake b/lib/tasks/data_migrations/delete_orphaned_registrant_verifications.rake deleted file mode 100644 index f65db547e..000000000 --- a/lib/tasks/data_migrations/delete_orphaned_registrant_verifications.rake +++ /dev/null @@ -1,15 +0,0 @@ -namespace :data_migrations do - task delete_orphaned_registrant_verifications: :environment do - orphaned_registrant_verifications = RegistrantVerification.where.not(domain_id: Domain.ids) - orphaned_registrant_verification_count = orphaned_registrant_verifications.count - processed_registrant_verification_count = 0 - - orphaned_registrant_verifications.each do |registrant_verification| - registrant_verification.destroy! - processed_registrant_verification_count += 1 - end - - puts "Processed: #{processed_registrant_verification_count} out of" \ - " #{orphaned_registrant_verification_count}" - end -end diff --git a/lib/tasks/data_migrations/regenerate_registrar_reference_numbers.rake b/lib/tasks/data_migrations/regenerate_registrar_reference_numbers.rake deleted file mode 100644 index 6f6aaebe2..000000000 --- a/lib/tasks/data_migrations/regenerate_registrar_reference_numbers.rake +++ /dev/null @@ -1,16 +0,0 @@ -namespace :data_migrations do - task regenerate_registrar_reference_numbers: [:environment] do - processed_registrar_count = 0 - - Registrar.transaction do - Registrar.all.each do |registrar| - next unless registrar.reference_no.start_with?('RF') - - registrar.update_columns(reference_no: Billing::ReferenceNo.generate) - processed_registrar_count += 1 - end - end - - puts "Registrars processed: #{processed_registrar_count}" - end -end diff --git a/test/fixtures/account_activities.yml b/test/fixtures/account_activities.yml index dbe1dc2aa..8f883e424 100644 --- a/test/fixtures/account_activities.yml +++ b/test/fixtures/account_activities.yml @@ -2,4 +2,4 @@ one: account: cash invoice: one bank_transaction: one - created_at: <%= Time.zone.parse('2010-07-05 10:00') %> \ No newline at end of file + created_at: <%= Time.zone.parse('2010-07-05 10:00') %> diff --git a/test/fixtures/files/bank_statement_test.txt b/test/fixtures/files/bank_statement_test.txt new file mode 100644 index 000000000..d3f561a7d --- /dev/null +++ b/test/fixtures/files/bank_statement_test.txt @@ -0,0 +1,7 @@ +VV 000689NL85RABO74981124931508051533 +VV 220150805 M NL85RABO7498112493EUR Algsaldo C 26397 +VV 12015080520890321MK NL85RABO7498112493EUR401NL93ABNA8027105943 Registrar Second Prepayment Invoice no.3131071 C 72055 +VV 12015080520890321MK NL85RABO7498112493EUR401NL93ABNA8027105943 eedirect Prepayment Invoice no. 13 C 480 +VV 12015080521042372MK NL85RABO7498112493EUR767EE021222121767323770 Line Suva 8 Arve nr 2015-01 D 55000 +VV 320150805 M NL85RABO7498112493EUR Lõppsaldo C 5417 +VV 999 176980 diff --git a/test/fixtures/invoice_items.yml b/test/fixtures/invoice_items.yml index 19409df81..a61ef4eb0 100644 --- a/test/fixtures/invoice_items.yml +++ b/test/fixtures/invoice_items.yml @@ -4,3 +4,10 @@ one: quantity: 1 unit: pc invoice: one + +two: + description: Acme services + price: 5 + quantity: 1 + unit: pc + invoice: unpaid diff --git a/test/fixtures/invoices.yml b/test/fixtures/invoices.yml index bc9fa2900..6c0dca021 100644 --- a/test/fixtures/invoices.yml +++ b/test/fixtures/invoices.yml @@ -24,3 +24,30 @@ one: reference_no: 13 number: 1 description: Order nr 1 from registrar 1234567 second number 2345678 + +unpaid: + issue_date: <%= Date.parse '2010-07-05' %> + due_date: <%= Date.parse '2010-07-06' %> + currency: EUR + seller_name: Seller Ltd + seller_reg_no: 1234 + seller_iban: US75512108001245126199 + seller_bank: Main Bank + seller_swift: swift + seller_email: info@seller.test + seller_country_code: US + seller_street: Main Street 1 + seller_city: New York + seller_contact_name: John Doe + buyer: bestnames + buyer_name: Buyer Ltd + buyer_reg_no: 12345 + buyer_email: info@buyer.test + buyer_country_code: GB + buyer_street: Main Street 2 + buyer_city: London + vat_rate: 0.1 + total: 16.50 + reference_no: 13 + number: 2 + description: Order nr 2 from registrar 1234567 second number 2345678 diff --git a/test/fixtures/payment_orders.yml b/test/fixtures/payment_orders.yml new file mode 100644 index 000000000..39289b7d1 --- /dev/null +++ b/test/fixtures/payment_orders.yml @@ -0,0 +1,27 @@ +everypay_issued: + type: PaymentOrders::EveryPay + status: issued + invoice: one + response: + notes: + +banklink_issued: + type: PaymentOrders::Seb + status: issued + invoice: one + response: + notes: + +paid: + type: PaymentOrders::EveryPay + status: paid + invoice: unpaid + response: "{}" + notes: + +cancelled: + type: PaymentOrders::Seb + status: cancelled + invoice: unpaid + response: "{}" + notes: User failed to make payment. Bank responded with code 1911 diff --git a/test/integration/contact/audit_log_test.rb b/test/integration/contact/audit_log_test.rb index f0f6a4bf2..41699d595 100644 --- a/test/integration/contact/audit_log_test.rb +++ b/test/integration/contact/audit_log_test.rb @@ -5,7 +5,7 @@ class ContactAuditLogTest < ActionDispatch::IntegrationTest contact = contacts(:john) contact.legal_document_id = 1 - assert_difference 'contact.versions.count' do + assert_difference 'contact.versions.count', 1 do contact.save! end @@ -13,4 +13,4 @@ class ContactAuditLogTest < ActionDispatch::IntegrationTest assert_equal ({ legal_documents: [1] }).with_indifferent_access, contact_version.children.with_indifferent_access end -end \ No newline at end of file +end diff --git a/test/integration/domain/audit_log_test.rb b/test/integration/domain/audit_log_test.rb index 292994ca3..a17fded0d 100644 --- a/test/integration/domain/audit_log_test.rb +++ b/test/integration/domain/audit_log_test.rb @@ -14,7 +14,7 @@ class DomainAuditLogTest < ActionDispatch::IntegrationTest assert_equal registrant_id, domain.registrant_id domain.legal_document_id = legal_document_id - assert_difference 'domain.versions.count' do + assert_difference 'domain.versions.count', 1 do domain.save! end @@ -26,4 +26,4 @@ class DomainAuditLogTest < ActionDispatch::IntegrationTest assert_equal [legal_document_id], domain_version.children['legal_documents'] assert_equal [registrant_id], domain_version.children['registrant'] end -end \ No newline at end of file +end diff --git a/test/integration/registrar_area/invoices/payment_callback_test.rb b/test/integration/registrar_area/invoices/payment_callback_test.rb index 23db55e84..94ca6e373 100644 --- a/test/integration/registrar_area/invoices/payment_callback_test.rb +++ b/test/integration/registrar_area/invoices/payment_callback_test.rb @@ -6,34 +6,40 @@ class PaymentCallbackTest < ApplicationIntegrationTest @user = users(:api_bestnames) sign_in @user + + @payment_order = payment_orders(:everypay_issued) + @invoice = invoices(:one) + @invoice.update!(account_activity: nil, total: 12) end def test_every_pay_callback_returns_status_200 - invoice = payable_invoice - assert_matching_bank_transaction_exists(invoice) - - request_params = every_pay_request_params.merge(invoice_id: invoice.id) - post "/registrar/pay/callback/every_pay", params: request_params + request_params = every_pay_request_params + post "/registrar/pay/callback/#{@payment_order.id}", params: request_params assert_response :ok end + def test_invoice_is_marked_as_paid + request_params = every_pay_request_params + post "/registrar/pay/callback/#{@payment_order.id}", params: request_params + + assert @payment_order.invoice.paid? + end + + def failure_log_is_created_if_unsuccessful_payment + request_params = every_pay_request_params.dup + request_params['payment_state'] = 'cancelled' + request_params['transaction_result'] = 'failed' + + post "/registrar/pay/callback/#{@payment_order.id}", params: request_params + + @payment_order.reload + assert @payment_order.cancelled? + assert_includes @payment_order.notes, 'Payment state: cancelled' + end + private - def payable_invoice - invoice = invoices(:one) - invoice.update!(account_activity: nil) - invoice - end - - def assert_matching_bank_transaction_exists(invoice) - assert BankTransaction.find_by( - description: invoice.description, - currency: invoice.currency, - iban: invoice.seller_iban - ), 'Matching bank transaction should exist' - end - def every_pay_request_params { nonce: "392f2d7748bc8cb0d14f263ebb7b8932", diff --git a/test/integration/registrar_area/invoices/payment_return_test.rb b/test/integration/registrar_area/invoices/payment_return_test.rb index de65cccb0..a4adb8160 100644 --- a/test/integration/registrar_area/invoices/payment_return_test.rb +++ b/test/integration/registrar_area/invoices/payment_return_test.rb @@ -8,6 +8,9 @@ class PaymentReturnTest < ApplicationIntegrationTest sign_in @user @invoice = invoices(:one) + @invoice.update!(account_activity: nil, total: 12) + @everypay_order = payment_orders(:everypay_issued) + @banklink_order = payment_orders(:banklink_issued) end def every_pay_request_params @@ -57,33 +60,78 @@ class PaymentReturnTest < ApplicationIntegrationTest } end - def test_every_pay_return_creates_activity_redirects_to_invoice_path - request_params = every_pay_request_params.merge(invoice_id: @invoice.id) + def test_successful_bank_payment_marks_invoice_as_paid + @invoice.update!(account_activity: nil) + request_params = bank_link_request_params - post "/registrar/pay/return/every_pay", params: request_params + post "/registrar/pay/return/#{@banklink_order.id}", params: request_params + + @banklink_order.reload + assert @banklink_order.invoice.paid? + end + + def test_every_pay_return_creates_activity_redirects_to_invoice_path + request_params = every_pay_request_params + + post "/registrar/pay/return/#{@everypay_order.id}", params: request_params assert_equal(302, response.status) assert_redirected_to(registrar_invoice_path(@invoice)) end - def test_Every_Pay_return_raises_RecordNotFound - request_params = every_pay_request_params.merge(invoice_id: "178907") + def test_every_pay_return_raises_record_not_found + request_params = every_pay_request_params assert_raises(ActiveRecord::RecordNotFound) do - post "/registrar/pay/return/every_pay", params: request_params + post '/registrar/pay/return/123456', params: request_params end end def test_bank_link_return_redirects_to_invoice_paths - request_params = bank_link_request_params.merge(invoice_id: @invoice.id) + request_params = bank_link_request_params - post "/registrar/pay/return/seb", params: request_params + post "/registrar/pay/return/#{@banklink_order.id}", params: request_params assert_equal(302, response.status) assert_redirected_to(registrar_invoice_path(@invoice)) end def test_bank_link_return - request_params = bank_link_request_params.merge(invoice_id: "178907") + request_params = bank_link_request_params assert_raises(ActiveRecord::RecordNotFound) do - post "/registrar/pay/return/seb", params: request_params + post '/registrar/pay/return/123456', params: request_params end end + + def test_marks_as_paid_and_adds_notes_if_failed_to_bind + request_params = bank_link_request_params + + post "/registrar/pay/return/#{@banklink_order.id}", params: request_params + post "/registrar/pay/return/#{@banklink_order.id}", params: request_params + @banklink_order.reload + + assert @banklink_order.notes.present? + assert @banklink_order.paid? + assert_includes @banklink_order.notes, 'Failed to bind' + end + + def test_failed_bank_link_payment_creates_brief_error_explanation + request_params = bank_link_request_params.dup + request_params['VK_SERVICE'] = '1911' + + post "/registrar/pay/return/#{@banklink_order.id}", params: request_params + + @banklink_order.reload + + assert_includes @banklink_order.notes, 'Bank responded with code 1911' + end + + def test_failed_every_pay_payment_creates_brief_error_explanation + request_params = every_pay_request_params.dup + request_params['payment_state'] = 'cancelled' + request_params['transaction_result'] = 'failed' + + post "/registrar/pay/return/#{@everypay_order.id}", params: request_params + + @everypay_order.reload + + assert_includes @everypay_order.notes, 'Payment state: cancelled' + end end diff --git a/test/integration/tasks/data_migrations/regenerate_registrar_reference_numbers_test.rb b/test/integration/tasks/data_migrations/regenerate_registrar_reference_numbers_test.rb deleted file mode 100644 index 946c6b898..000000000 --- a/test/integration/tasks/data_migrations/regenerate_registrar_reference_numbers_test.rb +++ /dev/null @@ -1,61 +0,0 @@ -require 'test_helper' - -class RegenerateRegistrarReferenceNumbersTaskTest < ActiveSupport::TestCase - def test_regenerates_registrar_reference_numbers_to_estonian_format - registrar = registrars(:bestnames) - registrar.update_column(:reference_no, 'RF1111') - - capture_io { run_task } - registrar.reload - - assert_not registrar.reference_no.start_with?('RF') - end - - def test_bypasses_registrar_validation - registrar = registrars(:invalid) - registrar.update_column(:reference_no, 'RF1111') - assert registrar.invalid? - - capture_io { run_task } - registrar.reload - - assert_not registrar.reference_no.start_with?('RF') - end - - def test_does_not_regenerate_when_the_task_is_run_again - registrar = registrars(:bestnames) - registrar.update!(reference_no: '1111') - - capture_io { run_task } - registrar.reload - - assert_equal '1111', registrar.reference_no - end - - def test_keeps_iso_reference_number_on_the_invoice_unchanged - registrar = registrars(:bestnames) - registrar.update_column(:reference_no, 'RF1111') - invoice = invoices(:one) - invoice.update!(reference_no: 'RF2222') - - capture_io { run_task } - invoice.reload - - assert_equal 'RF2222', invoice.reference_no - end - - def test_output - registrar = registrars(:bestnames) - registrar.update_column(:reference_no, 'RF1111') - - assert_output "Registrars processed: 1\n" do - run_task - end - end - - private - - def run_task - Rake::Task['data_migrations:regenerate_registrar_reference_numbers'].execute - end -end diff --git a/test/jobs/directo_invoice_forward_job_test.rb b/test/jobs/directo_invoice_forward_job_test.rb new file mode 100644 index 000000000..8a4fb43aa --- /dev/null +++ b/test/jobs/directo_invoice_forward_job_test.rb @@ -0,0 +1,146 @@ +require "test_helper" + +class DirectoInvoiceForwardJobTest < ActiveSupport::TestCase + setup do + @invoice = invoices(:one) + @user = registrars(:bestnames) + travel_to Time.zone.parse('2010-08-06') + end + + def teardown + Setting.clear_cache + Setting.directo_monthly_number_min = 309901 + Setting.directo_monthly_number_max = 309999 + Setting.directo_monthly_number_last = 309901 + end + + def test_xml_is_include_transaction_date + @invoice.update(total: @invoice.account_activity.bank_transaction.sum) + @invoice.account_activity.bank_transaction.update(paid_at: Time.zone.now) + + response = <<-XML + + + + + XML + + stub_request(:post, ENV['directo_invoice_url']).with do |request| + request.body.include? 'TransactionDate' + end.to_return(status: 200, body: response) + + assert_nothing_raised do + DirectoInvoiceForwardJob.run(monthly: false, dry: false) + end + + assert_not_empty @invoice.directo_records.first.request + end + + def test_fails_if_directo_bounds_exceedable + activity = account_activities(:one) + price = billing_prices(:create_one_year) + activity.update!(activity_type: 'create', price: price) + + Setting.directo_monthly_number_max = 30991 + + assert_raises 'RuntimeError' do + DirectoInvoiceForwardJob.run(monthly: true, dry: false) + end + end + + def test_monthly_summary_is_delivered_in_estonian + activity = account_activities(:one) + price = billing_prices(:create_one_year) + activity.update!(activity_type: 'create', price: price) + @user.update(language: 'et') + + response = <<-XML + + + + + XML + + stub_request(:post, ENV['directo_invoice_url']).with do |request| + body = CGI.unescape(request.body) + + (body.include? '.test registreerimine: 1 aasta(t)') && + (body.include? 'Domeenide ettemaks') && + (body.include? '309902') + end.to_return(status: 200, body: response) + + assert_difference 'Setting.directo_monthly_number_last' do + DirectoInvoiceForwardJob.run(monthly: true, dry: false) + end + end + + def test_monthly_summary_is_delivered_in_english + activity = account_activities(:one) + price = billing_prices(:create_one_year) + activity.update(activity_type: 'create', price: price) + @user.update(language: 'en') + + response = <<-XML + + + + + XML + + stub_request(:post, ENV['directo_invoice_url']).with do |request| + body = CGI.unescape(request.body) + (body.include? 'test registration') && + (body.include? 'Domains prepayment') && + (body.include? '309902') + end.to_return(status: 200, body: response) + + assert_difference 'Setting.directo_monthly_number_last' do + DirectoInvoiceForwardJob.run(monthly: true, dry: false) + end + end + + def test_multi_year_purchases_have_duration_assigned + activity = account_activities(:one) + price = billing_prices(:create_one_year) + price.update(duration: '3 years') + activity.update(activity_type: 'create', price: price) + + response = <<-XML + + + + + XML + + stub_request(:post, ENV['directo_invoice_url']).with do |request| + body = CGI.unescape(request.body) + (body.include? 'StartDate') && (body.include? 'EndDate') + end.to_return(status: 200, body: response) + + assert_difference 'Setting.directo_monthly_number_last' do + DirectoInvoiceForwardJob.run(monthly: true, dry: false) + end + end + + def test_monthly_duration_products_are_present_in_summary + activity = account_activities(:one) + price = billing_prices(:create_one_month) + activity.update(activity_type: 'create', price: price) + + response = <<-XML + + + + + XML + + stub_request(:post, ENV['directo_invoice_url']).with do |request| + body = CGI.unescape(request.body) + body.include? 'month(s)' + end.to_return(status: 200, body: response) + + assert_difference 'Setting.directo_monthly_number_last' do + DirectoInvoiceForwardJob.run(monthly: true, dry: false) + end + end +end diff --git a/test/jobs/send_e_invoice_job_test.rb b/test/jobs/send_e_invoice_job_test.rb new file mode 100644 index 000000000..384479e92 --- /dev/null +++ b/test/jobs/send_e_invoice_job_test.rb @@ -0,0 +1,47 @@ +require 'test_helper' + +class SendEInvoiceJobTest < ActiveSupport::TestCase + + def teardown + EInvoice.provider = EInvoice::Providers::TestProvider.new + EInvoice::Providers::TestProvider.deliveries.clear + end + + def test_if_invoice_is_sended + @invoice = invoices(:one) + EInvoice.provider = EInvoice::Providers::TestProvider.new + EInvoice::Providers::TestProvider.deliveries.clear + + assert_nothing_raised do + SendEInvoiceJob.enqueue(@invoice.id) + end + @invoice.reload + + assert_not @invoice.e_invoice_sent_at.blank? + assert_equal 1, EInvoice::Providers::TestProvider.deliveries.count + end + + def test_if_invoice_sending_retries + @invoice = invoices(:one) + provider_config = { password: nil, + test_mode: true } + EInvoice.provider = EInvoice::Providers::OmnivaProvider.new(provider_config) + stub_request(:get, "https://testfinance.post.ee/finance/erp/erpServices.wsdl").to_timeout + + assert_raise HTTPClient::TimeoutError do + SendEInvoiceJob.enqueue(@invoice.id) + end + assert @invoicee_invoice_sent_at.blank? + + EInvoice.provider = EInvoice::Providers::TestProvider.new + EInvoice::Providers::TestProvider.deliveries.clear + + assert_nothing_raised do + SendEInvoiceJob.enqueue(@invoice.id) + end + @invoice.reload + + assert_not @invoice.e_invoice_sent_at.blank? + assert_equal 1, EInvoice::Providers::TestProvider.deliveries.count + end +end diff --git a/test/learning/paper_trail_test.rb b/test/learning/paper_trail_test.rb index 9396496b7..b7351c644 100644 --- a/test/learning/paper_trail_test.rb +++ b/test/learning/paper_trail_test.rb @@ -21,6 +21,25 @@ class PaperTrailLearningTest < ActiveSupport::TestCase assert_respond_to @record.versions.first, :item_id end + def test_returns_version_count_on_domains + @domain = domains(:airport) + @domain.save + + assert_equal 1, @domain.versions.count + + @domain.name = 'domain.test' + @domain.save! + assert_equal 2, @domain.versions.count + end + + def test_returns_version_count_on_users + @user = users(:registrant) + + @user.email = 'aaa@bbb.com' + @user.save! + assert_equal 1, @user.versions.count + end + def test_creates_new_version_upon_update @record = Post.create!(title: 'old title') original_record = @record.clone @@ -40,7 +59,7 @@ class PaperTrailLearningTest < ActiveSupport::TestCase @record = Post.create!(title: 'any') assert_difference -> { @record.versions.size } do - @record.touch_with_version + @record.paper_trail.touch_with_version end end -end \ No newline at end of file +end diff --git a/test/models/deposit_test.rb b/test/models/deposit_test.rb index d8fc2d02a..171316f65 100644 --- a/test/models/deposit_test.rb +++ b/test/models/deposit_test.rb @@ -36,22 +36,22 @@ class DepositTest < ActiveSupport::TestCase def test_amount_is_converted_from_string @deposit.amount = "12.00" - assert_equal(BigDecimal.new("12.00"), @deposit.amount) + assert_equal(BigDecimal("12.00"), @deposit.amount) @deposit.amount = "12,11" - assert_equal(BigDecimal.new("12.11"), @deposit.amount) + assert_equal(BigDecimal("12.11"), @deposit.amount) end def test_amount_is_converted_from_float @deposit.amount = 12.0044 - assert_equal(BigDecimal.new("12.0044"), @deposit.amount) + assert_equal(BigDecimal("12.0044"), @deposit.amount) @deposit.amount = 12.0144 - assert_equal(BigDecimal.new("12.0144"), @deposit.amount) + assert_equal(BigDecimal("12.0144"), @deposit.amount) end def test_amount_is_converted_from_nil @deposit.amount = nil - assert_equal(BigDecimal.new("0.00"), @deposit.amount) + assert_equal(BigDecimal("0.00"), @deposit.amount) end end diff --git a/test/models/directo_test.rb b/test/models/directo_test.rb index 9dbbf64d4..603a38d15 100644 --- a/test/models/directo_test.rb +++ b/test/models/directo_test.rb @@ -1,20 +1,4 @@ require 'test_helper' class DirectoTest < ActiveSupport::TestCase - setup do - @invoice = invoices(:one) - end - - def test_xml_is_include_transaction_date - @invoice.update(total: @invoice.account_activity.bank_transaction.sum) - @invoice.account_activity.bank_transaction.update(paid_at: Time.zone.now) - - stub_request(:post, ENV['directo_invoice_url']).with do |request| - request.body.include? 'TransactionDate' - end - - assert_nothing_raised do - Directo.send_receipts - end - end end diff --git a/test/models/dns/domain_name_test.rb b/test/models/dns/domain_name_test.rb index bd83076bc..4f8922d32 100644 --- a/test/models/dns/domain_name_test.rb +++ b/test/models/dns/domain_name_test.rb @@ -13,6 +13,8 @@ class AuctionDoubleTest < ActiveSupport::TestCase end class DNS::DomainNameTest < ActiveSupport::TestCase + fixtures 'whois/records' + def test_available_when_not_at_auction domain_name = DNS::DomainName.new('auction.test') auctions(:one).update!(domain: 'auction.test', status: Auction.statuses[:domain_registered]) diff --git a/test/models/payment_orders/bank_link_test.rb b/test/models/payment_orders/bank_link_test.rb index 002f488b9..30d91cb7c 100644 --- a/test/models/payment_orders/bank_link_test.rb +++ b/test/models/payment_orders/bank_link_test.rb @@ -8,7 +8,7 @@ class BankLinkTest < ActiveSupport::TestCase super @invoice = invoices(:one) - @invoice.update!(total: 12) + @invoice.update!(account_activity: nil, total: 12) travel_to '2018-04-01 00:30 +0300' create_new_bank_link @@ -36,11 +36,11 @@ class BankLinkTest < ActiveSupport::TestCase 'VK_MAC': 'CZZvcptkxfuOxRR88JmT4N+Lw6Hs4xiQfhBWzVYldAcRTQbcB/lPf9MbJzBE4e1/HuslQgkdCFt5g1xW2lJwrVDBQTtP6DAHfvxU3kkw7dbk0IcwhI4whUl68/QCwlXEQTAVDv1AFnGVxXZ40vbm/aLKafBYgrirB5SUe8+g9FE=', 'VK_ENCODING': 'UTF-8', 'VK_LANG': 'ENG' - }.with_indifferent_access + }.as_json - @completed_bank_link = PaymentOrders::BankLink.new( - 'seb', @invoice, { response: params } - ) + @completed_bank_link = PaymentOrder.new(type: 'PaymentOrders::Seb', + invoice: @invoice, + response: params) end def create_cancelled_bank_link @@ -55,16 +55,17 @@ class BankLinkTest < ActiveSupport::TestCase 'VK_MAC': 'PElE2mYXXN50q2UBvTuYU1rN0BmOQcbafPummDnWfNdm9qbaGQkGyOn0XaaFGlrdEcldXaHBbZKUS0HegIgjdDfl2NOk+wkLNNH0Iu38KzZaxHoW9ga7vqiyKHC8dcxkHiO9HsOnz77Sy/KpWCq6cz48bi3fcMgo+MUzBMauWoQ=', 'VK_ENCODING': 'UTF-8', 'VK_LANG': 'ENG' - }.with_indifferent_access + }.as_json - @cancelled_bank_link = PaymentOrders::BankLink.new( - 'seb', @invoice, { response: params } - ) + @cancelled_bank_link = PaymentOrder.new(type: 'PaymentOrders::Seb', + invoice: @invoice, + response: params) end def create_new_bank_link - params = { return_url: 'return.url', response_url: 'response.url' } - @new_bank_link = PaymentOrders::BankLink.new('seb', @invoice, params) + @new_bank_link = PaymentOrder.new(type: 'PaymentOrders::Seb', invoice: @invoice) + @new_bank_link.return_url = 'return.url' + @new_bank_link.response_url = 'response.url' end def test_response_is_not_valid_when_it_is_missing @@ -105,23 +106,14 @@ class BankLinkTest < ActiveSupport::TestCase refute(@cancelled_bank_link.settled_payment?) end - def test_complete_transaction_calls_methods_on_transaction - mock_transaction = MiniTest::Mock.new - mock_transaction.expect(:sum= , '12.00', ['12.00']) - mock_transaction.expect(:bank_reference= , '1', ['1']) - mock_transaction.expect(:buyer_bank_code= , 'testvpos', ['testvpos']) - mock_transaction.expect(:buyer_iban= , '1234', ['1234']) - mock_transaction.expect(:paid_at= , Date.parse('2018-04-01 00:30:00 +0300'), [Time.parse('2018-04-01T00:30:00+0300')]) - mock_transaction.expect(:buyer_name=, 'John Doe', ['John Doe']) - mock_transaction.expect(:save!, true) - mock_transaction.expect(:binded?, false) - mock_transaction.expect(:bind_invoice, AccountActivity.new, [1]) - mock_transaction.expect(:errors, []) + def test_successful_payment_creates_bank_transaction + @completed_bank_link.complete_transaction - BankTransaction.stub(:find_by, mock_transaction) do - @completed_bank_link.complete_transaction - end + transaction = BankTransaction.find_by( + sum: @completed_bank_link.response['VK_AMOUNT'], + buyer_name: @completed_bank_link.response['VK_SND_NAME'] + ) - mock_transaction.verify + assert transaction.present? end end diff --git a/test/models/payment_orders/every_pay_test.rb b/test/models/payment_orders/every_pay_test.rb index 81e077b23..1e560f32a 100644 --- a/test/models/payment_orders/every_pay_test.rb +++ b/test/models/payment_orders/every_pay_test.rb @@ -4,36 +4,38 @@ class EveryPayTest < ActiveSupport::TestCase def setup super - @invoice = invoices(:one) + @invoice = invoices(:unpaid) @invoice.update!(total: 12) - params = { - response: - { - utf8: '✓', - _method: 'put', - authenticity_token: 'OnA69vbccQtMt3C9wxEWigs5Gpf/7z+NoxRCMkFPlTvaATs8+OgMKF1I4B2f+vuK37zCgpWZaWWtyuslRRSwkw==', - nonce: '392f2d7748bc8cb0d14f263ebb7b8932', - timestamp: '1524136727', - api_username: 'ca8d6336dd750ddb', - transaction_result: 'completed', - payment_reference: 'fd5d27b59a1eb597393cd5ff77386d6cab81ae05067e18d530b10f3802e30b56', - payment_state: 'settled', - amount: '12.00', - order_reference: 'e468a2d59a731ccc546f2165c3b1a6', - account_id: 'EUR3D1', - cc_type: 'master_card', - cc_last_four_digits: '0487', - cc_month: '10', - cc_year: '2018', - cc_holder_name: 'John Doe', - hmac_fields: 'account_id,amount,api_username,cc_holder_name,cc_last_four_digits,cc_month,cc_type,cc_year,hmac_fields,nonce,order_reference,payment_reference,payment_state,timestamp,transaction_result', - hmac: 'efac1c732835668cd86023a7abc140506c692f0d', - invoice_id: '1', - }, - } - @every_pay = PaymentOrders::EveryPay.new('every_pay', @invoice, params) - @other_pay = PaymentOrders::EveryPay.new('every_pay', @invoice, {}) + response = { + "utf8": '✓', + "_method": 'put', + "authenticity_token": 'OnA69vbccQtMt3C9wxEWigs5Gpf/7z+NoxRCMkFPlTvaATs8+OgMKF1I4B2f+vuK37zCgpWZaWWtyuslRRSwkw=="', + "nonce": '392f2d7748bc8cb0d14f263ebb7b8932', + "timestamp": '1524136727', + "api_username": 'ca8d6336dd750ddb', + "transaction_result": 'completed', + "payment_reference": 'fd5d27b59a1eb597393cd5ff77386d6cab81ae05067e18d530b10f3802e30b56', + "payment_state": 'settled', + "amount": '12.00', + "order_reference": 'e468a2d59a731ccc546f2165c3b1a6', + "account_id": 'EUR3D1', + "cc_type": 'master_card', + "cc_last_four_digits": '0487', + "cc_month": '10', + "cc_year": '2018', + "cc_holder_name": 'John Doe', + "hmac_fields": 'account_id,amount,api_username,cc_holder_name,cc_last_four_digits,cc_month,cc_type,cc_year,hmac_fields,nonce,order_reference,payment_reference,payment_state,timestamp,transaction_result', + "hmac": 'efac1c732835668cd86023a7abc140506c692f0d', + "invoice_id": '2' + }.as_json + + @successful_payment = PaymentOrder.new(type: 'PaymentOrders::EveryPay', + invoice: @invoice, + response: response) + + @failed_payment = @successful_payment.dup + @failed_payment.response['payment_state'] = 'cancelled' travel_to Time.zone.parse('2018-04-01 00:30:00 +0000') end @@ -47,39 +49,37 @@ class EveryPayTest < ActiveSupport::TestCase transaction_type: 'charge', hmac_fields: 'account_id,amount,api_username,callback_url,customer_url,hmac_fields,nonce,order_reference,timestamp,transaction_type' } - form_fields = @every_pay.form_fields + form_fields = @successful_payment.form_fields expected_fields.each do |k, v| assert_equal(v, form_fields[k]) end end def test_valid_response_from_intermediary? - assert(@every_pay.valid_response_from_intermediary?) - refute(@other_pay.valid_response_from_intermediary?) + assert(@successful_payment.valid_response_from_intermediary?) + + @failed_payment.response = { 'what': 'definitely not valid everypay response' } + refute(@failed_payment.valid_response_from_intermediary?) + end + + def test_valid_and_successful_payment_is_determined + assert(@successful_payment.payment_received?) + refute(@failed_payment.payment_received?) end def test_settled_payment? - assert(@every_pay.settled_payment?) - other_pay = PaymentOrders::EveryPay.new( - 'every_pay', @invoice, {response: {payment_state: 'CANCELLED'}} - ) - refute(other_pay.settled_payment?) + assert(@successful_payment.settled_payment?) + refute(@failed_payment.settled_payment?) end - def test_complete_transaction_calls_methods_on_transaction - mock_transaction = MiniTest::Mock.new - mock_transaction.expect(:sum= , '12.00', ['12.00']) - mock_transaction.expect(:paid_at= , Date.strptime('1524136727', '%s'), [Date.strptime('1524136727', '%s')]) - mock_transaction.expect(:buyer_name=, 'John Doe', ['John Doe']) - mock_transaction.expect(:save!, true) - mock_transaction.expect(:binded?, false) - mock_transaction.expect(:bind_invoice, AccountActivity.new, [1]) - mock_transaction.expect(:errors, []) + def test_successful_payment_creates_bank_transaction + @successful_payment.complete_transaction - BankTransaction.stub(:find_by, mock_transaction) do - @every_pay.complete_transaction - end + transaction = BankTransaction.find_by( + sum: @successful_payment.response['amount'], + buyer_name: @successful_payment.response['cc_holder_name'] + ) - mock_transaction.verify + assert transaction.present? end end diff --git a/test/models/payment_orders_test.rb b/test/models/payment_orders_test.rb index 252ba0582..43996c6bc 100644 --- a/test/models/payment_orders_test.rb +++ b/test/models/payment_orders_test.rb @@ -5,23 +5,21 @@ class PaymentOrdersTest < ActiveSupport::TestCase super @original_methods = ENV['payment_methods'] - @original_seb_URL = ENV['seb_payment_url'] - ENV['payment_methods'] = 'seb, swed, credit_card' + @original_seb_url = ENV['seb_payment_url'] + ENV['payment_methods'] = 'seb, swed, every_pay' ENV['seb_payment_url'] = nil - @not_implemented_payment = PaymentOrders::Base.new( - 'not_implemented', Invoice.new - ) + @not_implemented_payment = PaymentOrder.new(invoice: Invoice.new) end def teardown super ENV['payment_methods'] = @original_methods - ENV['seb_payment_url'] = @original_seb_URL + ENV['seb_payment_url'] = @original_seb_url end def test_variable_assignment - assert_equal 'not_implemented', @not_implemented_payment.type + assert_nil @not_implemented_payment.type assert_nil @not_implemented_payment.response_url assert_nil @not_implemented_payment.return_url assert_nil @not_implemented_payment.form_url @@ -45,14 +43,61 @@ class PaymentOrdersTest < ActiveSupport::TestCase end end - def test_that_create_with_type_raises_argument_error - assert_raise ArgumentError do - PaymentOrders.create_with_type("not_implemented", Invoice.new) + def test_correct_channel_is_assigned + everypay_channel = PaymentOrder.new_with_type(type: 'every_pay', invoice: @invoice) + assert_equal everypay_channel.channel, 'EveryPay' + assert_equal everypay_channel.class.config_namespace_name, 'every_pay' + + swed_channel = PaymentOrder.new_with_type(type: 'swed', invoice: @invoice) + assert_equal swed_channel.channel, 'Swed' + assert_equal swed_channel.class.config_namespace_name, 'swed' + + seb_channel = PaymentOrder.new_with_type(type: 'seb', invoice: @invoice) + assert_equal seb_channel.channel, 'Seb' + assert_equal seb_channel.class.config_namespace_name, 'seb' + + lhv_channel = PaymentOrder.new_with_type(type: 'lhv', invoice: @invoice) + assert_equal lhv_channel.channel, 'Lhv' + assert_equal lhv_channel.class.config_namespace_name, 'lhv' + + admin_channel = PaymentOrder.new_with_type(type: 'admin_payment', invoice: @invoice) + assert_equal admin_channel.channel, 'AdminPayment' + assert_equal admin_channel.class.config_namespace_name, 'admin_payment' + + system_channel = PaymentOrder.new_with_type(type: 'system_payment', invoice: @invoice) + assert_equal system_channel.channel, 'SystemPayment' + assert_equal system_channel.class.config_namespace_name, 'system_payment' + end + + def test_can_not_create_order_for_paid_invoice + invoice = invoices(:one) + payment_order = PaymentOrder.new_with_type(type: 'every_pay', invoice: invoice) + assert payment_order.invalid? + assert_includes payment_order.errors[:invoice], 'is already paid' + end + + def test_order_without_channel_is_invalid + payment_order = PaymentOrder.new + assert payment_order.invalid? + assert_includes payment_order.errors[:type], 'is not supported' + end + + def test_can_not_create_order_with_invalid_type + assert_raise NameError do + PaymentOrder.new_with_type(type: 'not_implemented', invoice: Invoice.new) end end - def test_create_with_correct_subclass - payment = PaymentOrders.create_with_type('seb', Invoice.new) - assert_equal PaymentOrders::BankLink, payment.class + def test_supported_method_bool_does_not_fail + assert_not PaymentOrder.supported_method?('not_implemented', shortname: true) + assert PaymentOrder.supported_method?('every_pay', shortname: true) + + assert_not PaymentOrder.supported_method?('PaymentOrders::NonExistant') + assert PaymentOrder.supported_method?('PaymentOrders::EveryPay') + end + + def test_can_create_with_correct_subclass + payment = PaymentOrder.new_with_type(type: 'seb', invoice: Invoice.new) + assert_equal PaymentOrders::Seb, payment.class end end diff --git a/test/support/rails5_assertions.rb b/test/support/rails5_assertions.rb deleted file mode 100644 index a11bb3ef1..000000000 --- a/test/support/rails5_assertions.rb +++ /dev/null @@ -1,96 +0,0 @@ -# Built-in since Rails 5.1 - -module ActiveSupport - module Testing - module Assertions - UNTRACKED = Object.new # :nodoc: - - # Assertion that the result of evaluating an expression is changed before - # and after invoking the passed in block. - # - # assert_changes 'Status.all_good?' do - # post :create, params: { status: { ok: false } } - # end - # - # You can pass the block as a string to be evaluated in the context of - # the block. A lambda can be passed for the block as well. - # - # assert_changes -> { Status.all_good? } do - # post :create, params: { status: { ok: false } } - # end - # - # The assertion is useful to test side effects. The passed block can be - # anything that can be converted to string with #to_s. - # - # assert_changes :@object do - # @object = 42 - # end - # - # The keyword arguments :from and :to can be given to specify the - # expected initial value and the expected value after the block was - # executed. - # - # assert_changes :@object, from: nil, to: :foo do - # @object = :foo - # end - # - # An error message can be specified. - # - # assert_changes -> { Status.all_good? }, 'Expected the status to be bad' do - # post :create, params: { status: { incident: true } } - # end - def assert_changes(expression, message = nil, from: UNTRACKED, to: UNTRACKED, &block) - exp = expression.respond_to?(:call) ? expression : -> { eval(expression.to_s, block.binding) } - - before = exp.call - retval = yield - - unless from == UNTRACKED - error = "#{expression.inspect} isn't #{from.inspect}" - error = "#{message}.\n#{error}" if message - assert from === before, error - end - - after = exp.call - - if to == UNTRACKED - error = "#{expression.inspect} didn't changed" - error = "#{message}.\n#{error}" if message - assert_not_equal before, after, error - else - error = "#{expression.inspect} didn't change to #{to}" - error = "#{message}.\n#{error}" if message - assert to === after, error - end - - retval - end - - # Assertion that the result of evaluating an expression is changed before - # and after invoking the passed in block. - # - # assert_no_changes 'Status.all_good?' do - # post :create, params: { status: { ok: true } } - # end - # - # An error message can be specified. - # - # assert_no_changes -> { Status.all_good? }, 'Expected the status to be good' do - # post :create, params: { status: { ok: false } } - # end - def assert_no_changes(expression, message = nil, &block) - exp = expression.respond_to?(:call) ? expression : -> { eval(expression.to_s, block.binding) } - - before = exp.call - retval = yield - after = exp.call - - error = "#{expression.inspect} did change to #{after}" - error = "#{message}.\n#{error}" if message - assert_equal before, after, error - - retval - end - end - end -end diff --git a/test/system/admin_area/bank_statement_test.rb b/test/system/admin_area/bank_statement_test.rb new file mode 100644 index 000000000..c95035a8d --- /dev/null +++ b/test/system/admin_area/bank_statement_test.rb @@ -0,0 +1,16 @@ +require 'application_system_test_case' + +class BankStatementTest < ApplicationSystemTestCase + setup do + sign_in users(:admin) + travel_to Time.zone.parse('2010-07-05 00:30:00') + end + + def test_import_statement + assert_difference 'BankStatement.count', 1 do + visit import_admin_bank_statements_path + attach_file 'Th6 file', Rails.root.join('test', 'fixtures', 'files', 'bank_statement_test.txt').to_s + click_link_or_button 'Save' + end + end +end diff --git a/test/system/admin_area/domains_test.rb b/test/system/admin_area/domains_test.rb index abd1d93fb..05e7d60f3 100644 --- a/test/system/admin_area/domains_test.rb +++ b/test/system/admin_area/domains_test.rb @@ -35,4 +35,15 @@ class AdminDomainsTestTest < ApplicationSystemTestCase assert_text 'deleteCandidate status has been removed' assert_no_link 'Remove deleteCandidate status' end + + def test_remove_domain_status + @domain.update!(statuses: [DomainStatus::SERVER_REGISTRANT_CHANGE_PROHIBITED]) + + visit edit_admin_domain_url(@domain) + + click_link_or_button 'Delete' + click_link_or_button 'Save' + + assert_text 'Domain updated!' + end end diff --git a/test/tasks/data_migrations/convert_domain_delete_date_test.rb b/test/tasks/data_migrations/convert_domain_delete_date_test.rb deleted file mode 100644 index 709334b52..000000000 --- a/test/tasks/data_migrations/convert_domain_delete_date_test.rb +++ /dev/null @@ -1,61 +0,0 @@ -require 'test_helper' - -class ConvertDomainDeleteDateTaskTest < ActiveSupport::TestCase - setup do - @domain = domains(:shop) - end - - def test_moves_domain_delete_date_one_day_ahead - @domain.update!(delete_date: '2010-07-05') - - capture_io do - run_task - end - @domain.reload - - assert_equal Date.parse('2010-07-06'), @domain.delete_date - end - - def test_processes_invalid_domains - @domain = domains(:invalid) - @domain.update_columns(delete_date: '2010-07-05') - - capture_io do - run_task - end - @domain.reload - - assert_equal Date.parse('2010-07-06'), @domain.delete_date - end - - def test_skips_non_expired_domains - @domain.update!(delete_date: nil) - - assert_nothing_raised do - capture_io do - run_task - end - end - end - - def test_output - eliminate_effect_of_all_domains_except(@domain) - @domain.update!(delete_date: '2010-07-05') - - assert_output "Domains processed: 1\n" do - run_task - end - end - - private - - def eliminate_effect_of_all_domains_except(domain) - Domain.connection.disable_referential_integrity do - Domain.where("id != #{domain.id}").delete_all - end - end - - def run_task - Rake::Task['data_migrations:convert_domain_delete_date'].execute - end -end \ No newline at end of file diff --git a/test/tasks/data_migrations/delete_orphaned_registrant_verifications_test.rb b/test/tasks/data_migrations/delete_orphaned_registrant_verifications_test.rb deleted file mode 100644 index df576332e..000000000 --- a/test/tasks/data_migrations/delete_orphaned_registrant_verifications_test.rb +++ /dev/null @@ -1,43 +0,0 @@ -require 'test_helper' - -class ArchiveOrphanedRegistrantVerificationsTest < ActiveSupport::TestCase - def test_deletes_orphaned_registrant_verifications - create_orphaned_registrant_verification - - assert_difference 'RegistrantVerification.count', -1 do - capture_io do - run_task - end - end - end - - def test_keeps_non_orphaned_registrant_verifications_intact - assert_no_difference 'RegistrantVerification.count' do - capture_io do - run_task - end - end - end - - def test_output - create_orphaned_registrant_verification - - assert_output "Processed: 1 out of 1\n" do - run_task - end - end - - private - - def create_orphaned_registrant_verification - non_existent_domain_id = 55 - assert_not_includes Domain.ids, non_existent_domain_id - - RegistrantVerification.connection.disable_referential_integrity do - registrant_verifications(:one).update_columns(domain_id: non_existent_domain_id) - end - end - - def run_task - Rake::Task['data_migrations:delete_orphaned_registrant_verifications'].execute end -end diff --git a/test/tasks/invoices/process_payments_test.rb b/test/tasks/invoices/process_payments_test.rb index 8c3b6ec73..02855e9fa 100644 --- a/test/tasks/invoices/process_payments_test.rb +++ b/test/tasks/invoices/process_payments_test.rb @@ -58,6 +58,30 @@ class ProcessPaymentsTaskTest < ActiveSupport::TestCase assert @invoice.paid? end + def test_attaches_paid_payment_order_to_invoice + assert @invoice.unpaid? + + capture_io { run_task } + @invoice.reload + + payment_order = @invoice.payment_orders.last + assert_equal 'PaymentOrders::SystemPayment', payment_order.type + assert payment_order.paid? + end + + def test_attaches_failed_payment_order_to_invoice + assert @invoice.unpaid? + account = accounts(:cash) + account.update!(registrar: registrars(:goodnames)) + + capture_io { run_task } + @invoice.reload + + payment_order = @invoice.payment_orders.last + assert_equal 'PaymentOrders::SystemPayment', payment_order.type + assert payment_order.failed? + end + def test_output assert_output "Transactions processed: 1\n" do run_task @@ -75,4 +99,4 @@ class ProcessPaymentsTaskTest < ActiveSupport::TestCase invoice.update!({ account_activity: nil, cancelled_at: nil }.merge(attributes)) invoice end -end \ No newline at end of file +end diff --git a/test/test_helper.rb b/test/test_helper.rb index 4cd632ea2..efdbc288f 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -16,7 +16,6 @@ require 'minitest/mock' require 'capybara/rails' require 'capybara/minitest' require 'webmock/minitest' -require 'support/rails5_assertions' # Remove once upgraded to Rails 5.1 require 'support/assertions/epp_assertions'