From 943dedd194dbb63b29d276d547b4fd88d008641b Mon Sep 17 00:00:00 2001 From: Thiago Youssef Date: Tue, 24 May 2022 16:36:21 +0300 Subject: [PATCH 01/81] Remove deprecated `statuses_before_force_delete` field --- .../restore_statuses_before_force_delete.rb | 1 - .../domains/force_delete/prepare_domain.rb | 1 - app/models/domain.rb | 1 - ...0114746_add_statuses_backup_for_domains.rb | 2 +- ..._backup_to_statuses_before_force_delete.rb | 2 +- ...atuses_before_force_delete_from_domains.rb | 5 + db/structure.sql | 341 +----------------- doc/registrant-api/v1/domain.md | 15 +- doc/registrant-api/v1/registry_lock.md | 10 +- test/models/domain/force_delete_test.rb | 7 - 10 files changed, 28 insertions(+), 357 deletions(-) create mode 100644 db/migrate/20220524130709_remove_statuses_before_force_delete_from_domains.rb diff --git a/app/interactions/domains/cancel_force_delete/restore_statuses_before_force_delete.rb b/app/interactions/domains/cancel_force_delete/restore_statuses_before_force_delete.rb index 3c2579890..f913d68b9 100644 --- a/app/interactions/domains/cancel_force_delete/restore_statuses_before_force_delete.rb +++ b/app/interactions/domains/cancel_force_delete/restore_statuses_before_force_delete.rb @@ -6,7 +6,6 @@ module Domains domain.statuses += domain.admin_store_statuses_history || [] domain.statuses.uniq! - domain.statuses_before_force_delete = nil domain.force_delete_domain_statuses_history = nil domain.admin_store_statuses_history = nil domain.save(validate: false) diff --git a/app/interactions/domains/force_delete/prepare_domain.rb b/app/interactions/domains/force_delete/prepare_domain.rb index e45f7969c..116957a87 100644 --- a/app/interactions/domains/force_delete/prepare_domain.rb +++ b/app/interactions/domains/force_delete/prepare_domain.rb @@ -7,7 +7,6 @@ module Domains def execute domain.force_delete_domain_statuses_history = domain.statuses - domain.statuses_before_force_delete = domain.statuses domain.statuses |= STATUSES_TO_SET domain.save(validate: false) end diff --git a/app/models/domain.rb b/app/models/domain.rb index 68c5bc611..d79da3bc8 100644 --- a/app/models/domain.rb +++ b/app/models/domain.rb @@ -718,7 +718,6 @@ class Domain < ApplicationRecord hash = super hash['auth_info'] = hash.delete('transfer_code') # API v1 requirement hash['valid_from'] = hash['created_at'] # API v1 requirement - hash.delete('statuses_before_force_delete') hash end diff --git a/db/migrate/20150810114746_add_statuses_backup_for_domains.rb b/db/migrate/20150810114746_add_statuses_backup_for_domains.rb index 9e9885c6c..c1f6bbfd6 100644 --- a/db/migrate/20150810114746_add_statuses_backup_for_domains.rb +++ b/db/migrate/20150810114746_add_statuses_backup_for_domains.rb @@ -1,5 +1,5 @@ class AddStatusesBackupForDomains < ActiveRecord::Migration[6.0] def change - add_column :domains, :statuses_backup, :string, array: true, default: [] + # add_column :domains, :statuses_backup, :string, array: true, default: [] end end diff --git a/db/migrate/20180422154642_rename_domains_statuses_backup_to_statuses_before_force_delete.rb b/db/migrate/20180422154642_rename_domains_statuses_backup_to_statuses_before_force_delete.rb index 97b577068..90b9321cb 100644 --- a/db/migrate/20180422154642_rename_domains_statuses_backup_to_statuses_before_force_delete.rb +++ b/db/migrate/20180422154642_rename_domains_statuses_backup_to_statuses_before_force_delete.rb @@ -1,5 +1,5 @@ class RenameDomainsStatusesBackupToStatusesBeforeForceDelete < ActiveRecord::Migration[6.0] def change - rename_column :domains, :statuses_backup, :statuses_before_force_delete + # rename_column :domains, :statuses_backup, :statuses_before_force_delete end end diff --git a/db/migrate/20220524130709_remove_statuses_before_force_delete_from_domains.rb b/db/migrate/20220524130709_remove_statuses_before_force_delete_from_domains.rb new file mode 100644 index 000000000..1a53ade83 --- /dev/null +++ b/db/migrate/20220524130709_remove_statuses_before_force_delete_from_domains.rb @@ -0,0 +1,5 @@ +class RemoveStatusesBeforeForceDeleteFromDomains < ActiveRecord::Migration[6.1] + def change + remove_column :domains, :statuses_before_force_delete + end +end diff --git a/db/structure.sql b/db/structure.sql index 66152fe27..63774c5c4 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -955,15 +955,14 @@ CREATE TABLE public.domains ( pending_json jsonb, force_delete_date date, statuses character varying[], - status_notes public.hstore, - statuses_before_force_delete character varying[] DEFAULT '{}'::character varying[], upid integer, up_date timestamp without time zone, uuid uuid DEFAULT public.gen_random_uuid() NOT NULL, locked_by_registrant_at timestamp without time zone, force_delete_start timestamp without time zone, force_delete_data public.hstore, - json_statuses_history jsonb + json_statuses_history jsonb, + status_notes public.hstore ); @@ -986,98 +985,6 @@ CREATE SEQUENCE public.domains_id_seq ALTER SEQUENCE public.domains_id_seq OWNED BY public.domains.id; --- --- Name: email_address_verifications; Type: TABLE; Schema: public; Owner: - --- - -CREATE TABLE public.email_address_verifications ( - id bigint NOT NULL, - email public.citext NOT NULL, - verified_at timestamp without time zone, - success boolean DEFAULT false NOT NULL, - domain public.citext NOT NULL -); - - --- --- Name: email_address_verifications_id_seq; Type: SEQUENCE; Schema: public; Owner: - --- - -CREATE SEQUENCE public.email_address_verifications_id_seq - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; - - --- --- Name: email_address_verifications_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - --- - -ALTER SEQUENCE public.email_address_verifications_id_seq OWNED BY public.email_address_verifications.id; - - --- --- Name: email_addresses_validations; Type: TABLE; Schema: public; Owner: - --- - -CREATE TABLE public.email_addresses_validations ( - id bigint NOT NULL, - email character varying NOT NULL, - validated_at timestamp without time zone -); - - --- --- Name: email_addresses_validations_id_seq; Type: SEQUENCE; Schema: public; Owner: - --- - -CREATE SEQUENCE public.email_addresses_validations_id_seq - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; - - --- --- Name: email_addresses_validations_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - --- - -ALTER SEQUENCE public.email_addresses_validations_id_seq OWNED BY public.email_addresses_validations.id; - - --- --- Name: email_addresses_verifications; Type: TABLE; Schema: public; Owner: - --- - -CREATE TABLE public.email_addresses_verifications ( - id bigint NOT NULL, - email character varying NOT NULL, - validated_at timestamp without time zone -); - - --- --- Name: email_addresses_verifications_id_seq; Type: SEQUENCE; Schema: public; Owner: - --- - -CREATE SEQUENCE public.email_addresses_verifications_id_seq - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; - - --- --- Name: email_addresses_verifications_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - --- - -ALTER SEQUENCE public.email_addresses_verifications_id_seq OWNED BY public.email_addresses_verifications.id; - - -- -- Name: epp_sessions; Type: TABLE; Schema: public; Owner: - -- @@ -1196,7 +1103,6 @@ CREATE TABLE public.invoices ( buyer_vat_no character varying, issue_date date NOT NULL, e_invoice_sent_at timestamp without time zone, - payment_link character varying, CONSTRAINT invoices_due_date_is_not_before_issue_date CHECK ((due_date >= issue_date)) ); @@ -2283,74 +2189,6 @@ CREATE SEQUENCE public.payment_orders_id_seq ALTER SEQUENCE public.payment_orders_id_seq OWNED BY public.payment_orders.id; --- --- Name: pghero_query_stats; Type: TABLE; Schema: public; Owner: - --- - -CREATE TABLE public.pghero_query_stats ( - id bigint NOT NULL, - database text, - "user" text, - query text, - query_hash bigint, - total_time double precision, - calls bigint, - captured_at timestamp without time zone -); - - --- --- Name: pghero_query_stats_id_seq; Type: SEQUENCE; Schema: public; Owner: - --- - -CREATE SEQUENCE public.pghero_query_stats_id_seq - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; - - --- --- Name: pghero_query_stats_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - --- - -ALTER SEQUENCE public.pghero_query_stats_id_seq OWNED BY public.pghero_query_stats.id; - - --- --- Name: pghero_space_stats; Type: TABLE; Schema: public; Owner: - --- - -CREATE TABLE public.pghero_space_stats ( - id bigint NOT NULL, - database text, - schema text, - relation text, - size bigint, - captured_at timestamp without time zone -); - - --- --- Name: pghero_space_stats_id_seq; Type: SEQUENCE; Schema: public; Owner: - --- - -CREATE SEQUENCE public.pghero_space_stats_id_seq - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; - - --- --- Name: pghero_space_stats_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - --- - -ALTER SEQUENCE public.pghero_space_stats_id_seq OWNED BY public.pghero_space_stats.id; - - -- -- Name: prices; Type: TABLE; Schema: public; Owner: - -- @@ -2389,48 +2227,6 @@ CREATE SEQUENCE public.prices_id_seq ALTER SEQUENCE public.prices_id_seq OWNED BY public.prices.id; --- --- Name: que_jobs; Type: TABLE; Schema: public; Owner: - --- - -CREATE TABLE public.que_jobs ( - priority smallint DEFAULT 100 NOT NULL, - run_at timestamp with time zone DEFAULT now() NOT NULL, - job_id bigint NOT NULL, - job_class text NOT NULL, - args json DEFAULT '[]'::json NOT NULL, - error_count integer DEFAULT 0 NOT NULL, - last_error text, - queue text DEFAULT ''::text NOT NULL -); - - --- --- Name: TABLE que_jobs; Type: COMMENT; Schema: public; Owner: - --- - -COMMENT ON TABLE public.que_jobs IS '3'; - - --- --- Name: que_jobs_job_id_seq; Type: SEQUENCE; Schema: public; Owner: - --- - -CREATE SEQUENCE public.que_jobs_job_id_seq - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; - - --- --- Name: que_jobs_job_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - --- - -ALTER SEQUENCE public.que_jobs_job_id_seq OWNED BY public.que_jobs.job_id; - - -- -- Name: registrant_verifications; Type: TABLE; Schema: public; Owner: - -- @@ -2711,7 +2507,8 @@ CREATE TABLE public.validation_events ( validation_eventable_type character varying, validation_eventable_id bigint, created_at timestamp(6) without time zone NOT NULL, - updated_at timestamp(6) without time zone NOT NULL + updated_at timestamp(6) without time zone NOT NULL, + event_type public.validation_type ); @@ -3015,27 +2812,6 @@ ALTER TABLE ONLY public.domain_transfers ALTER COLUMN id SET DEFAULT nextval('pu ALTER TABLE ONLY public.domains ALTER COLUMN id SET DEFAULT nextval('public.domains_id_seq'::regclass); --- --- Name: email_address_verifications id; Type: DEFAULT; Schema: public; Owner: - --- - -ALTER TABLE ONLY public.email_address_verifications ALTER COLUMN id SET DEFAULT nextval('public.email_address_verifications_id_seq'::regclass); - - --- --- Name: email_addresses_validations id; Type: DEFAULT; Schema: public; Owner: - --- - -ALTER TABLE ONLY public.email_addresses_validations ALTER COLUMN id SET DEFAULT nextval('public.email_addresses_validations_id_seq'::regclass); - - --- --- Name: email_addresses_verifications id; Type: DEFAULT; Schema: public; Owner: - --- - -ALTER TABLE ONLY public.email_addresses_verifications ALTER COLUMN id SET DEFAULT nextval('public.email_addresses_verifications_id_seq'::regclass); - - -- -- Name: epp_sessions id; Type: DEFAULT; Schema: public; Owner: - -- @@ -3253,20 +3029,6 @@ ALTER TABLE ONLY public.notifications ALTER COLUMN id SET DEFAULT nextval('publi ALTER TABLE ONLY public.payment_orders ALTER COLUMN id SET DEFAULT nextval('public.payment_orders_id_seq'::regclass); --- --- Name: pghero_query_stats id; Type: DEFAULT; Schema: public; Owner: - --- - -ALTER TABLE ONLY public.pghero_query_stats ALTER COLUMN id SET DEFAULT nextval('public.pghero_query_stats_id_seq'::regclass); - - --- --- Name: pghero_space_stats id; Type: DEFAULT; Schema: public; Owner: - --- - -ALTER TABLE ONLY public.pghero_space_stats ALTER COLUMN id SET DEFAULT nextval('public.pghero_space_stats_id_seq'::regclass); - - -- -- Name: prices id; Type: DEFAULT; Schema: public; Owner: - -- @@ -3274,13 +3036,6 @@ ALTER TABLE ONLY public.pghero_space_stats ALTER COLUMN id SET DEFAULT nextval(' ALTER TABLE ONLY public.prices ALTER COLUMN id SET DEFAULT nextval('public.prices_id_seq'::regclass); --- --- Name: que_jobs job_id; Type: DEFAULT; Schema: public; Owner: - --- - -ALTER TABLE ONLY public.que_jobs ALTER COLUMN job_id SET DEFAULT nextval('public.que_jobs_job_id_seq'::regclass); - - -- -- Name: registrant_verifications id; Type: DEFAULT; Schema: public; Owner: - -- @@ -3518,30 +3273,6 @@ ALTER TABLE ONLY public.domains ADD CONSTRAINT domains_pkey PRIMARY KEY (id); --- --- Name: email_address_verifications email_address_verifications_pkey; Type: CONSTRAINT; Schema: public; Owner: - --- - -ALTER TABLE ONLY public.email_address_verifications - ADD CONSTRAINT email_address_verifications_pkey PRIMARY KEY (id); - - --- --- Name: email_addresses_validations email_addresses_validations_pkey; Type: CONSTRAINT; Schema: public; Owner: - --- - -ALTER TABLE ONLY public.email_addresses_validations - ADD CONSTRAINT email_addresses_validations_pkey PRIMARY KEY (id); - - --- --- Name: email_addresses_verifications email_addresses_verifications_pkey; Type: CONSTRAINT; Schema: public; Owner: - --- - -ALTER TABLE ONLY public.email_addresses_verifications - ADD CONSTRAINT email_addresses_verifications_pkey PRIMARY KEY (id); - - -- -- Name: epp_sessions epp_sessions_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- @@ -3790,22 +3521,6 @@ ALTER TABLE ONLY public.payment_orders ADD CONSTRAINT payment_orders_pkey PRIMARY KEY (id); --- --- Name: pghero_query_stats pghero_query_stats_pkey; Type: CONSTRAINT; Schema: public; Owner: - --- - -ALTER TABLE ONLY public.pghero_query_stats - ADD CONSTRAINT pghero_query_stats_pkey PRIMARY KEY (id); - - --- --- Name: pghero_space_stats pghero_space_stats_pkey; Type: CONSTRAINT; Schema: public; Owner: - --- - -ALTER TABLE ONLY public.pghero_space_stats - ADD CONSTRAINT pghero_space_stats_pkey PRIMARY KEY (id); - - -- -- Name: prices prices_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- @@ -3814,14 +3529,6 @@ ALTER TABLE ONLY public.prices ADD CONSTRAINT prices_pkey PRIMARY KEY (id); --- --- Name: que_jobs que_jobs_pkey; Type: CONSTRAINT; Schema: public; Owner: - --- - -ALTER TABLE ONLY public.que_jobs - ADD CONSTRAINT que_jobs_pkey PRIMARY KEY (queue, priority, run_at, job_id); - - -- -- Name: registrant_verifications registrant_verifications_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- @@ -4233,13 +3940,6 @@ CREATE INDEX index_domains_on_registrar_id ON public.domains USING btree (regist CREATE INDEX index_domains_on_statuses ON public.domains USING gin (statuses); --- --- Name: index_email_address_verifications_on_domain; Type: INDEX; Schema: public; Owner: - --- - -CREATE INDEX index_email_address_verifications_on_domain ON public.email_address_verifications USING btree (domain); - - -- -- Name: index_epp_sessions_on_updated_at; Type: INDEX; Schema: public; Owner: - -- @@ -4576,20 +4276,6 @@ CREATE INDEX index_notifications_on_registrar_id ON public.notifications USING b CREATE INDEX index_payment_orders_on_invoice_id ON public.payment_orders USING btree (invoice_id); --- --- Name: index_pghero_query_stats_on_database_and_captured_at; Type: INDEX; Schema: public; Owner: - --- - -CREATE INDEX index_pghero_query_stats_on_database_and_captured_at ON public.pghero_query_stats USING btree (database, captured_at); - - --- --- Name: index_pghero_space_stats_on_database_and_captured_at; Type: INDEX; Schema: public; Owner: - --- - -CREATE INDEX index_pghero_space_stats_on_database_and_captured_at ON public.pghero_space_stats USING btree (database, captured_at); - - -- -- Name: index_prices_on_zone_id; Type: INDEX; Schema: public; Owner: - -- @@ -4646,6 +4332,13 @@ CREATE INDEX index_users_on_registrar_id ON public.users USING btree (registrar_ CREATE INDEX index_validation_events_on_event_data ON public.validation_events USING gin (event_data); +-- +-- Name: index_validation_events_on_event_type; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX index_validation_events_on_event_type ON public.validation_events USING btree (event_type); + + -- -- Name: index_validation_events_on_validation_eventable; Type: INDEX; Schema: public; Owner: - -- @@ -5391,11 +5084,9 @@ INSERT INTO "schema_migrations" (version) VALUES ('20210708131814'), ('20210729131100'), ('20210729134625'), -('20211028122103'), -('20211028125245'), -('20211029082225'), +('20210827185249'), +('20211029073644'), ('20211124071418'), -('20211124084308'), ('20211125181033'), ('20211125184334'), ('20211126085139'), @@ -5403,14 +5094,14 @@ INSERT INTO "schema_migrations" (version) VALUES ('20220106123143'), ('20220113201642'), ('20220113220809'), -('20220124105717'), -('20220216113112'), ('20220228093211'), ('20220316140727'), +('20220406085500'), ('20220412130856'), ('20220413073315'), ('20220413084536'), ('20220413084748'), -('20220504090512'); +('20220504090512'), +('20220524130709'); diff --git a/doc/registrant-api/v1/domain.md b/doc/registrant-api/v1/domain.md index 9163a2850..f6a1705de 100644 --- a/doc/registrant-api/v1/domain.md +++ b/doc/registrant-api/v1/domain.md @@ -103,10 +103,7 @@ Content-Type: application/json "dnssec_changed_at": "2015-09-09T09:11:14.860Z", "status_notes":{ - }, - "statuses_backup":[ - - ] + } } ] ``` @@ -219,10 +216,7 @@ Content-Type: application/json "dnssec_changed_at": "2015-09-09T09:11:14.860Z", "status_notes":{ - }, - "statuses_backup":[ - - ] + } } ] ``` @@ -326,10 +320,7 @@ Content-Type: application/json "dnssec_changed_at": "2015-09-09T09:11:14.860Z", "status_notes":{ - }, - "statuses_backup":[ - - ] + } } ``` diff --git a/doc/registrant-api/v1/registry_lock.md b/doc/registrant-api/v1/registry_lock.md index 17f4c6f6e..04102420a 100644 --- a/doc/registrant-api/v1/registry_lock.md +++ b/doc/registrant-api/v1/registry_lock.md @@ -96,10 +96,7 @@ Content-Type: application/json ], "status_notes":{ - }, - "statuses_backup":[ - - ] + } } ``` @@ -235,10 +232,7 @@ Content-Type: application/json ], "status_notes":{ - }, - "statuses_backup":[ - - ] + } } ``` diff --git a/test/models/domain/force_delete_test.rb b/test/models/domain/force_delete_test.rb index 48372210b..66621f23d 100644 --- a/test/models/domain/force_delete_test.rb +++ b/test/models/domain/force_delete_test.rb @@ -128,13 +128,6 @@ class ForceDeleteTest < ActionMailer::TestCase assert_empty @domain.statuses & statuses_to_be_removed, 'Pending actions should be stopped' end - def test_scheduling_force_delete_preserves_current_statuses - @domain.statuses = %w[test1 test2] - @domain.schedule_force_delete(type: :fast_track) - @domain.reload - assert_equal %w[test1 test2], @domain.statuses_before_force_delete - end - def test_scheduling_force_delete_bypasses_validation @domain = domains(:invalid) @domain.schedule_force_delete(type: :fast_track) From 7ccc02055a44940deff84a8295a1ffa7827b281d Mon Sep 17 00:00:00 2001 From: Thiago Youssef Date: Wed, 25 May 2022 15:05:39 +0300 Subject: [PATCH 02/81] Migration check column existance --- ...24130709_remove_statuses_before_force_delete_from_domains.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/migrate/20220524130709_remove_statuses_before_force_delete_from_domains.rb b/db/migrate/20220524130709_remove_statuses_before_force_delete_from_domains.rb index 1a53ade83..61e06141e 100644 --- a/db/migrate/20220524130709_remove_statuses_before_force_delete_from_domains.rb +++ b/db/migrate/20220524130709_remove_statuses_before_force_delete_from_domains.rb @@ -1,5 +1,5 @@ class RemoveStatusesBeforeForceDeleteFromDomains < ActiveRecord::Migration[6.1] def change - remove_column :domains, :statuses_before_force_delete + remove_column :domains, :statuses_before_force_delete if column_exists? :domains, :statuses_before_force_delete end end From 0b04a715e9a708cee9faaafab5e9cb766b459675 Mon Sep 17 00:00:00 2001 From: Thiago Youssef Date: Thu, 26 May 2022 17:19:05 +0300 Subject: [PATCH 03/81] Fix update --- Gemfile | 2 +- Gemfile.lock | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Gemfile b/Gemfile index c907eb267..1cd60719d 100644 --- a/Gemfile +++ b/Gemfile @@ -2,7 +2,7 @@ source 'https://rubygems.org' # core gem 'active_interaction', '~> 4.0' -gem 'apipie-rails', '~> 0.5.19' +gem 'apipie-rails', '~> 0.8.0' gem 'bootsnap', '>= 1.1.0', require: false gem 'iso8601', '0.13.0' # for dates and times gem 'mimemagic', '0.4.3' diff --git a/Gemfile.lock b/Gemfile.lock index fa0a18470..0e0179596 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -149,8 +149,9 @@ GEM akami (1.3.1) gyoku (>= 0.4.0) nokogiri - apipie-rails (0.5.19) - rails (>= 4.1) + apipie-rails (0.8.1) + actionpack (>= 5.0) + activesupport (>= 5.0) attr_required (1.0.1) autoprefixer-rails (10.2.4.0) execjs @@ -533,7 +534,7 @@ PLATFORMS DEPENDENCIES active_interaction (~> 4.0) airbrake - apipie-rails (~> 0.5.19) + apipie-rails (~> 0.8.0) aws-sdk-sesv2 (~> 1.19) bootsnap (>= 1.1.0) bootstrap-sass (~> 3.4) @@ -601,4 +602,4 @@ DEPENDENCIES wkhtmltopdf-binary (~> 0.12.5.1) BUNDLED WITH - 2.3.9 + 2.3.11 From 7cea363593dc76829ee9a0284179bb1b1d6a39ff Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 Jun 2022 21:58:05 +0000 Subject: [PATCH 04/81] Bump jmespath from 1.4.0 to 1.6.1 Bumps [jmespath](https://github.com/trevorrowe/jmespath.rb) from 1.4.0 to 1.6.1. - [Release notes](https://github.com/trevorrowe/jmespath.rb/releases) - [Changelog](https://github.com/jmespath/jmespath.rb/blob/main/CHANGELOG.md) - [Commits](https://github.com/trevorrowe/jmespath.rb/compare/v1.4.0...v1.6.1) --- updated-dependencies: - dependency-name: jmespath dependency-type: indirect ... Signed-off-by: dependabot[bot] --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 9b88bbbbd..2dbe0f2e1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -263,7 +263,7 @@ GEM i18n_data (0.13.0) isikukood (0.1.2) iso8601 (0.13.0) - jmespath (1.4.0) + jmespath (1.6.1) jquery-rails (4.4.0) rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) From 7e0419722e9a40b397ad6a9eb15bf223610d2558 Mon Sep 17 00:00:00 2001 From: Thiago Youssef Date: Thu, 30 Jun 2022 08:50:41 -0300 Subject: [PATCH 05/81] Run migrations --- db/structure.sql | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/db/structure.sql b/db/structure.sql index 099952f5c..a96474d53 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -1103,6 +1103,7 @@ CREATE TABLE public.invoices ( buyer_vat_no character varying, issue_date date NOT NULL, e_invoice_sent_at timestamp without time zone, + payment_link character varying, CONSTRAINT invoices_due_date_is_not_before_issue_date CHECK ((due_date >= issue_date)) ); @@ -5094,6 +5095,7 @@ INSERT INTO "schema_migrations" (version) VALUES ('20220106123143'), ('20220113201642'), ('20220113220809'), +('20220124105717'), ('20220228093211'), ('20220316140727'), ('20220406085500'), @@ -5103,3 +5105,5 @@ INSERT INTO "schema_migrations" (version) VALUES ('20220413084748'), ('20220504090512'), ('20220524130709'); + + From 852a59060046c484128d7225e6df77ec3195a339 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 14:10:29 +0000 Subject: [PATCH 06/81] Bump jmespath from 1.4.0 to 1.6.1 Bumps [jmespath](https://github.com/trevorrowe/jmespath.rb) from 1.4.0 to 1.6.1. - [Release notes](https://github.com/trevorrowe/jmespath.rb/releases) - [Changelog](https://github.com/jmespath/jmespath.rb/blob/main/CHANGELOG.md) - [Commits](https://github.com/trevorrowe/jmespath.rb/compare/v1.4.0...v1.6.1) --- updated-dependencies: - dependency-name: jmespath dependency-type: indirect ... Signed-off-by: dependabot[bot] --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index f921e65ae..3d0f40d09 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -263,7 +263,7 @@ GEM i18n_data (0.13.0) isikukood (0.1.2) iso8601 (0.13.0) - jmespath (1.4.0) + jmespath (1.6.1) jquery-rails (4.4.0) rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) From 42a61dd73225c1b6aa3a6bf87e90f190e8e2ae8a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 14:10:31 +0000 Subject: [PATCH 07/81] Bump rack from 2.2.3 to 2.2.4 Bumps [rack](https://github.com/rack/rack) from 2.2.3 to 2.2.4. - [Release notes](https://github.com/rack/rack/releases) - [Changelog](https://github.com/rack/rack/blob/main/CHANGELOG.md) - [Commits](https://github.com/rack/rack/compare/2.2.3...2.2.4) --- updated-dependencies: - dependency-name: rack dependency-type: indirect ... Signed-off-by: dependabot[bot] --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index f921e65ae..53d7f00ae 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -369,7 +369,7 @@ GEM que (~> 0.8) sinatra racc (1.6.0) - rack (2.2.3) + rack (2.2.4) rack-oauth2 (1.16.0) activesupport attr_required From 8ffaaba02bfd157f05ff061771f94f6d4b21c261 Mon Sep 17 00:00:00 2001 From: Thiago Youssef Date: Fri, 1 Jul 2022 11:37:49 -0300 Subject: [PATCH 08/81] Fix staging workflow docker login --- .github/workflows/build_deploy_staging.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build_deploy_staging.yml b/.github/workflows/build_deploy_staging.yml index b854ce820..34046491e 100644 --- a/.github/workflows/build_deploy_staging.yml +++ b/.github/workflows/build_deploy_staging.yml @@ -35,10 +35,9 @@ jobs: Gemfile.lock - name: Login to container registry - env: - PASSWORD: ${{ secrets.GHCR }} run: | - echo $PASSWORD | docker login ghcr.io -u eisbot --password-stdin + echo ${{ secrets.GHCR }} >> ~/password.txt + cat ~/password.txt | docker login ghcr.io -u eisbot --password-stdin - name: No changes in gems # feature branch has no changes in gems From 73d57eb86a092cf20980bc7e9c8d06cf70cbee6d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 14:11:09 +0000 Subject: [PATCH 09/81] Update dependency pg to v1.4.1 --- Gemfile | 2 +- Gemfile.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index 3e30e34f0..53c845de6 100644 --- a/Gemfile +++ b/Gemfile @@ -17,7 +17,7 @@ gem 'figaro', '~> 1.2' # model related gem 'paper_trail', '~> 12.1' -gem 'pg', '1.4.0' +gem 'pg', '1.4.1' # 1.8 is for Rails < 5.0 gem 'ransack', '~> 2.6.0' gem 'truemail', '~> 2.4' # validates email by regexp, mail server existence and address existence diff --git a/Gemfile.lock b/Gemfile.lock index f921e65ae..a24e9e798 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -352,7 +352,7 @@ GEM activerecord (>= 5.2) request_store (~> 1.1) pdfkit (0.8.5) - pg (1.4.0) + pg (1.4.1) pg_query (2.1.2) google-protobuf (>= 3.17.1) pghero (2.8.1) @@ -576,7 +576,7 @@ DEPENDENCIES omniauth-tara! paper_trail (~> 12.1) pdfkit - pg (= 1.4.0) + pg (= 1.4.1) pg_query (>= 0.9.0) pghero pry (= 0.14.1) From 24d2bb2102dc40351324bdb1b23c68142045cd69 Mon Sep 17 00:00:00 2001 From: Thiago Youssef Date: Mon, 4 Jul 2022 09:04:30 -0300 Subject: [PATCH 10/81] Checking docker login --- .github/workflows/build_deploy_staging.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_deploy_staging.yml b/.github/workflows/build_deploy_staging.yml index 34046491e..b854ce820 100644 --- a/.github/workflows/build_deploy_staging.yml +++ b/.github/workflows/build_deploy_staging.yml @@ -35,9 +35,10 @@ jobs: Gemfile.lock - name: Login to container registry + env: + PASSWORD: ${{ secrets.GHCR }} run: | - echo ${{ secrets.GHCR }} >> ~/password.txt - cat ~/password.txt | docker login ghcr.io -u eisbot --password-stdin + echo $PASSWORD | docker login ghcr.io -u eisbot --password-stdin - name: No changes in gems # feature branch has no changes in gems From d0881ec1ec321add784c44ecfbdbeee35f099591 Mon Sep 17 00:00:00 2001 From: Thiago Youssef Date: Mon, 4 Jul 2022 09:57:56 -0300 Subject: [PATCH 11/81] Fix update --- Gemfile | 2 +- Gemfile.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Gemfile b/Gemfile index 5d09a9f94..08c2d0bb5 100644 --- a/Gemfile +++ b/Gemfile @@ -2,7 +2,7 @@ source 'https://rubygems.org' # core gem 'active_interaction', '~> 4.0' -gem 'apipie-rails', '~> 0.8.0' +gem 'apipie-rails', '~> 0.6.0' gem 'bootsnap', '>= 1.1.0', require: false gem 'iso8601', '0.13.0' # for dates and times gem 'mimemagic', '0.4.3' diff --git a/Gemfile.lock b/Gemfile.lock index 4572b05cb..a53651e1b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -149,9 +149,9 @@ GEM akami (1.3.1) gyoku (>= 0.4.0) nokogiri - apipie-rails (0.8.1) - actionpack (>= 5.0) - activesupport (>= 5.0) + apipie-rails (0.6.0) + actionpack (>= 4.1) + activesupport (>= 4.1) attr_required (1.0.1) autoprefixer-rails (10.2.4.0) execjs @@ -535,7 +535,7 @@ PLATFORMS DEPENDENCIES active_interaction (~> 4.0) airbrake - apipie-rails (~> 0.8.0) + apipie-rails (~> 0.6.0) aws-sdk-sesv2 (~> 1.19) bootsnap (>= 1.1.0) bootstrap-sass (~> 3.4) @@ -604,4 +604,4 @@ DEPENDENCIES wkhtmltopdf-binary (~> 0.12.5.1) BUNDLED WITH - 2.3.11 + 2.3.16 From a5ffce290de6c8f5e219546640865b1f059dbc8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergei=20Ts=C3=B5ganov?= Date: Mon, 6 Jun 2022 13:43:30 +0300 Subject: [PATCH 12/81] Updated REPP API for new registrar portal --- .gitignore | 1 + Dockerfile | 1 + Gemfile | 4 +- Gemfile.lock | 9 +- app/controllers/repp/v1/account_controller.rb | 149 +++++++++++++++ .../repp/v1/accounts_controller.rb | 33 ---- app/controllers/repp/v1/base_controller.rb | 29 ++- .../repp/v1/contacts_controller.rb | 172 +++++++++++++----- .../v1/domains/base_contacts_controller.rb | 22 +-- .../repp/v1/domains/renews_controller.rb | 12 +- app/controllers/repp/v1/domains_controller.rb | 128 +++++++++---- .../repp/v1/invoices_controller.rb | 118 ++++++++++++ .../repp/v1/registrar/auth_controller.rb | 49 +++++ .../v1/registrar/nameservers_controller.rb | 37 ++-- .../v1/registrar/notifications_controller.rb | 4 +- .../repp/v1/registrar/summary_controller.rb | 111 +++++++++++ app/interactions/actions/domain_delete.rb | 6 +- app/interactions/actions/domain_update.rb | 7 +- app/interactions/actions/invoice_cancel.rb | 15 ++ app/models/ability.rb | 7 +- app/models/action.rb | 18 +- app/models/admin_domain_contact.rb | 2 +- app/models/api_user.rb | 12 +- .../balance_auto_reload_types/threshold.rb | 4 +- app/models/bulk_action.rb | 1 - app/models/concerns/invoice/cancellable.rb | 10 + app/models/concerns/invoice/payable.rb | 2 + app/models/contact.rb | 87 +++++---- app/models/contact_update_action.rb | 1 + app/models/deposit.rb | 1 + app/models/depp/user.rb | 4 +- app/models/domain_transfer.rb | 8 +- app/models/invoice.rb | 7 + app/models/notification.rb | 1 + app/models/registrar.rb | 12 +- app/models/tech_domain_contact.rb | 2 +- app/models/user.rb | 2 +- .../registrar/domain_list_csv_presenter.rb | 5 +- app/views/epp/poll/poll_req.xml.builder | 2 +- .../registrar/domains/_search_form.html.erb | 4 +- app/views/registrar/polls/show.haml | 9 +- config/initializers/arel.rb | 25 +++ config/initializers/omniauth.rb | 91 ++++----- config/initializers/ransack.rb | 7 + config/locales/en.yml | 5 + config/routes.rb | 32 +++- lib/serializers/repp/contact.rb | 52 +++++- lib/serializers/repp/domain.rb | 74 ++++++-- lib/serializers/repp/invoice.rb | 85 +++++++++ test/integration/epp/poll_test.rb | 2 +- .../v1/{accounts => account}/balance_test.rb | 8 +- .../repp/v1/contacts/create_test.rb | 80 ++++---- .../integration/repp/v1/contacts/list_test.rb | 18 +- .../integration/repp/v1/contacts/show_test.rb | 2 +- .../repp/v1/contacts/update_test.rb | 28 +-- .../v1/domains/contact_replacement_test.rb | 7 +- .../repp/v1/domains/delete_test.rb | 16 +- test/integration/repp/v1/domains/list_test.rb | 4 +- .../repp/v1/domains/renews_test.rb | 6 +- .../repp/v1/domains/update_test.rb | 17 +- test/models/registrant_user_test.rb | 10 +- 61 files changed, 1269 insertions(+), 408 deletions(-) create mode 100644 app/controllers/repp/v1/account_controller.rb delete mode 100644 app/controllers/repp/v1/accounts_controller.rb create mode 100644 app/controllers/repp/v1/invoices_controller.rb create mode 100644 app/controllers/repp/v1/registrar/auth_controller.rb create mode 100644 app/controllers/repp/v1/registrar/summary_controller.rb create mode 100644 app/interactions/actions/invoice_cancel.rb delete mode 100644 app/models/bulk_action.rb create mode 100644 app/models/contact_update_action.rb create mode 100644 config/initializers/arel.rb create mode 100644 config/initializers/ransack.rb create mode 100644 lib/serializers/repp/invoice.rb rename test/integration/repp/v1/{accounts => account}/balance_test.rb (88%) diff --git a/.gitignore b/.gitignore index 08606ba47..a8499459f 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ /coverage/ /.bundle /vendor/bundle +/vendor/gems /config/database.yml /config/application.yml /config/environments/development.rb diff --git a/Dockerfile b/Dockerfile index 9c46182a3..3d065e5bb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,6 +3,7 @@ FROM internetee/ruby:3.0-buster RUN mkdir -p /opt/webapps/app/tmp/pids WORKDIR /opt/webapps/app COPY Gemfile Gemfile.lock ./ +# ADD vendor/gems/omniauth-tara ./vendor/gems/omniauth-tara RUN gem install bundler && bundle install --jobs 20 --retry 5 EXPOSE 3000 diff --git a/Gemfile b/Gemfile index 08c2d0bb5..3761831d0 100644 --- a/Gemfile +++ b/Gemfile @@ -57,10 +57,9 @@ gem 'digidoc_client', ref: '1645e83a5a548addce383f75703b0275c5310c32' # TARA -gem 'omniauth' gem 'omniauth-rails_csrf_protection' gem 'omniauth-tara', github: 'internetee/omniauth-tara' - +# gem 'omniauth-tara', path: 'vendor/gems/omniauth-tara' gem 'airbrake' gem 'daemons-rails', '1.2.1' @@ -81,6 +80,7 @@ gem 'lhv', github: 'internetee/lhv', branch: 'master' gem 'rexml' gem 'wkhtmltopdf-binary', '~> 0.12.5.1' + gem 'directo', github: 'internetee/directo', branch: 'master' group :development, :test do diff --git a/Gemfile.lock b/Gemfile.lock index a53651e1b..8f1d86c23 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -338,7 +338,7 @@ GEM omniauth-rails_csrf_protection (0.1.2) actionpack (>= 4.2) omniauth (>= 1.3.1) - openid_connect (1.2.0) + openid_connect (1.3.0) activemodel attr_required (>= 1.0.0) json-jwt (>= 1.5.0) @@ -477,7 +477,7 @@ GEM activesupport (>= 4.0) sprockets (>= 3.0.0) spy (1.0.1) - swd (1.2.0) + swd (1.3.0) activesupport (>= 3) attr_required (>= 0.0.5) httpclient (>= 2.4) @@ -496,7 +496,7 @@ GEM validate_email (0.1.6) activemodel (>= 3.0) mail (>= 2.2.5) - validate_url (1.0.13) + validate_url (1.0.15) activemodel (>= 3.0.0) public_suffix validates_email_format_of (1.6.3) @@ -511,7 +511,7 @@ GEM nokogiri (~> 1.6) rubyzip (>= 1.3.0) selenium-webdriver (>= 3.0, < 4.0) - webfinger (1.1.0) + webfinger (1.2.0) activesupport httpclient (>= 2.4) webmock (3.14.0) @@ -572,7 +572,6 @@ DEPENDENCIES newrelic-infinite_tracing newrelic_rpm nokogiri (~> 1.13.0) - omniauth omniauth-rails_csrf_protection omniauth-tara! paper_trail (~> 12.1) diff --git a/app/controllers/repp/v1/account_controller.rb b/app/controllers/repp/v1/account_controller.rb new file mode 100644 index 000000000..e412b784d --- /dev/null +++ b/app/controllers/repp/v1/account_controller.rb @@ -0,0 +1,149 @@ +module Repp + module V1 + class AccountController < BaseController + load_and_authorize_resource + + api :get, '/repp/v1/account' + desc 'Get all activities' + def index + records = current_user.registrar.cash_account.activities + + q = records.ransack(search_params) + q.sorts = 'created_at desc' if q.sorts.empty? + activities = q.result(distinct: true) + + limited_activities = activities.limit(limit).offset(offset) + .includes(:invoice) + + render_success(data: { activities: serialized_activities(limited_activities), + count: activities.count, + types_for_select: AccountActivity.types_for_select }) + end + + api :get, '/repp/v1/account/details' + desc 'Get current registrar account details' + def details + registrar = current_user.registrar + type = registrar.settings['balance_auto_reload']&.dig('type') + resp = { account: { billing_email: registrar.billing_email, + iban: registrar.iban, + iban_max_length: Iban.max_length, + linked_users: serialized_users(current_user.linked_users), + balance_auto_reload: type, + min_deposit: Setting.minimum_deposit } } + render_success(data: resp) + end + + api :put, '/repp/v1/account' + desc 'Update current registrar account details' + def update + registrar = current_user.registrar + unless registrar.update(account_params) + handle_non_epp_errors(registrar) + return + end + + render_success(data: { account: account_params }, + message: I18n.t('registrar.account.update.saved')) + end + + api :post, '/repp/v1/account/update_auto_reload_balance' + desc 'Enable current registrar balance auto reload' + def update_auto_reload_balance + type = BalanceAutoReloadTypes::Threshold.new(type_params) + unless type.valid? + handle_non_epp_errors(type) + return + end + + settings = { balance_auto_reload: { type: type.as_json } } + current_user.registrar.update!(settings: settings) + render_success(data: { settings: settings }, + message: I18n.t('registrar.settings.balance_auto_reload.update.saved')) + end + + api :get, '/repp/v1/account/disable_auto_reload_balance' + desc 'Disable current registrar balance auto reload' + def disable_auto_reload_balance + registrar = current_user.registrar + registrar.settings.delete('balance_auto_reload') + registrar.save! + + render_success(data: { settings: registrar.settings }, + message: I18n.t('registrar.settings.balance_auto_reload.destroy.disabled')) + end + + api :get, '/repp/v1/account/balance' + desc "Get account's balance" + def balance + resp = { balance: current_user.registrar.cash_account.balance, + currency: current_user.registrar.cash_account.currency } + if params[:detailed] == 'true' + activities = current_user.registrar.cash_account.activities.order(created_at: :desc) + activities = activities.where('created_at >= ?', params[:from]) if params[:from] + activities = activities.where('created_at <= ?', params[:until]) if params[:until] + resp[:transactions] = serialized_activities(activities) + end + render_success(data: resp) + end + + private + + def account_params + params.require(:account).permit(:billing_email, :iban) + end + + def index_params + params.permit(:id, :limit, :offset, :q, + :page, :per_page, + q: [:description_matches, :created_at_gteq, + :created_at_lteq, :s, { s: [] }, { activity_type_in: [] }]) + end + + def type_params + permitted_params = params.require(:type).permit(:amount, :threshold) + normalize_params(permitted_params) + end + + def normalize_params(params) + params[:amount] = params[:amount].to_f + params[:threshold] = params[:threshold].to_f + params + end + + def search_params + index_params.fetch(:q, {}) + end + + def limit + index_params[:limit] || 200 + end + + def offset + index_params[:offset] || 0 + end + + def serialized_users(users) + arr = [] + users.each do |u| + arr << { id: u.id, username: u.username, + role: u.roles.first } + end + + arr + end + + def serialized_activities(activities) + arr = [] + activities.each do |a| + arr << { created_at: a.created_at, description: a.description, + type: a.activity_type == 'add_credit' ? 'credit' : 'debit', + sum: a.sum, balance: a.new_balance, currency: a.currency, + updator: a.updator_str } + end + + arr + end + end + end +end diff --git a/app/controllers/repp/v1/accounts_controller.rb b/app/controllers/repp/v1/accounts_controller.rb deleted file mode 100644 index 388bc9a94..000000000 --- a/app/controllers/repp/v1/accounts_controller.rb +++ /dev/null @@ -1,33 +0,0 @@ -module Repp - module V1 - class AccountsController < BaseController - api :GET, '/repp/v1/accounts/balance' - desc "Get account's balance" - def balance - resp = { balance: current_user.registrar.cash_account.balance, - currency: current_user.registrar.cash_account.currency } - resp[:transactions] = activities if params[:detailed] == 'true' - render_success(data: resp) - end - - def activities - arr = [] - registrar_activities.each do |a| - arr << { created_at: a.created_at, description: a.description, - type: a.activity_type == 'add_credit' ? 'credit' : 'debit', - sum: a.sum, balance: a.new_balance } - end - - arr - end - - def registrar_activities - activities = current_user.registrar.cash_account.activities.order(created_at: :desc) - activities = activities.where('created_at >= ?', params[:from]) if params[:from] - activities = activities.where('created_at <= ?', params[:until]) if params[:until] - - activities - end - end - end -end diff --git a/app/controllers/repp/v1/base_controller.rb b/app/controllers/repp/v1/base_controller.rb index 3e9ab5715..5ab910278 100644 --- a/app/controllers/repp/v1/base_controller.rb +++ b/app/controllers/repp/v1/base_controller.rb @@ -1,12 +1,12 @@ module Repp module V1 class BaseController < ActionController::API # rubocop:disable Metrics/ClassLength + attr_reader :current_user + around_action :log_request before_action :authenticate_user before_action :validate_webclient_ca before_action :check_ip_restriction - attr_reader :current_user - before_action :set_paper_trail_whodunnit private @@ -22,6 +22,10 @@ module Repp rescue Apipie::ParamInvalid => e @response = { code: 2005, message: e.message.gsub(/\n/, '. ') } render(json: @response, status: :bad_request) + rescue CanCan::AccessDenied => e + @response = { code: 2201, message: 'Authorization error' } + logger.error e.to_s + render(json: @response, status: :unauthorized) ensure create_repp_log end @@ -65,7 +69,6 @@ module Repp def handle_errors(obj = nil) @epp_errors ||= ActiveModel::Errors.new(self) - if obj obj.construct_epp_errors obj.errors.each { |error| @epp_errors.import error } @@ -85,6 +88,12 @@ module Repp render(json: @response, status: status) end + def handle_non_epp_errors(obj, message = nil) + @response = { message: message || obj.errors.full_messages.join(', '), + data: {} } + render(json: @response, status: :bad_request) + end + def basic_token pattern = /^Basic / header = request.headers['Authorization'] @@ -94,7 +103,8 @@ module Repp def authenticate_user username, password = Base64.urlsafe_decode64(basic_token).split(':') - @current_user ||= ApiUser.find_by(username: username, plain_text_password: password) + @current_user ||= ApiUser.find_by(username: username, plain_text_password: password, + active: true) return if @current_user @@ -123,6 +133,7 @@ module Repp return unless webclient_request? request_name = request.env['HTTP_SSL_CLIENT_S_DN_CN'] + webclient_cn = ENV['webclient_cert_common_name'] || 'webclient' return if request_name == webclient_cn @@ -135,6 +146,16 @@ module Repp def logger Rails.logger end + + def auth_values_to_data(registrar:) + data = current_user.as_json(only: %i[id username roles]) + data[:registrar_name] = registrar.name + data[:legaldoc_mandatory] = registrar.legaldoc_mandatory? + data[:balance] = { amount: registrar.cash_account&.balance, + currency: registrar.cash_account&.currency } + data[:abilities] = Ability.new(current_user).permissions + data + end end end end diff --git a/app/controllers/repp/v1/contacts_controller.rb b/app/controllers/repp/v1/contacts_controller.rb index c19ca3967..01230c7fe 100644 --- a/app/controllers/repp/v1/contacts_controller.rb +++ b/app/controllers/repp/v1/contacts_controller.rb @@ -3,23 +3,61 @@ module Repp module V1 class ContactsController < BaseController # rubocop:disable Metrics/ClassLength before_action :find_contact, only: %i[show update destroy] + skip_around_action :log_request, only: :search api :get, '/repp/v1/contacts' desc 'Get all existing contacts' def index - record_count = current_user.registrar.contacts.count - contacts = showable_contacts(params[:details], params[:limit] || 200, - params[:offset] || 0) - @response = { contacts: contacts, total_number_of_records: record_count } - render(json: @response, status: :ok) + authorize! :check, Epp::Contact + records = current_user.registrar.contacts.order(created_at: :desc) + + q = records.ransack(search_params) + q.sorts = 'created_at desc' if q.sorts.empty? + contacts = q.result(distinct: true) + + limited_contacts = contacts.limit(limit).offset(offset) + .includes(:domain_contacts, :registrant_domains, :registrar) + + render_success(data: { contacts: serialized_contacts(limited_contacts), + count: contacts.count, + statuses: Contact::STATUSES, + ident_types: Contact::Ident.types }) end + # rubocop:disable Metrics/MethodLength + api :get, '/repp/v1/contacts/search(/:id)' + desc 'Search all existing contacts by optional id or query param' + def search + scope = current_user.registrar.contacts + if params[:query] + escaped_str = ActiveRecord::Base.connection.quote_string params[:query] + scope = scope.where("name ilike '%#{escaped_str}%' OR code ilike '%#{escaped_str}%' + OR ident ilike '%#{escaped_str}%'") + elsif params[:id] + scope = scope.where(code: params[:id]) + end + + render_success(data: scope.limit(10) + .map do |c| + { value: c.code, + label: "#{c.code} #{c.name}", + selected: scope.size == 1 } + end) + end + # rubocop:enable Metrics/MethodLength + api :get, '/repp/v1/contacts/:contact_code' desc 'Get a specific contact' def show - serializer = ::Serializers::Repp::Contact.new(@contact, - show_address: Contact.address_processing?) - render_success(data: serializer.to_json) + authorize! :check, Epp::Contact + + simple = params[:simple] == 'true' || false + serializer = Serializers::Repp::Contact.new(@contact, + show_address: Contact.address_processing?, + domain_params: domain_filter_params, + simplify: simple) + + render_success(data: { contact: serializer.to_json }) end api :get, '/repp/v1/contacts/check/:contact_code' @@ -35,7 +73,7 @@ module Repp desc 'Create a new contact' def create @contact = Epp::Contact.new(contact_params_with_address, current_user.registrar, epp: false) - action = Actions::ContactCreate.new(@contact, params[:legal_document], + action = Actions::ContactCreate.new(@contact, contact_params[:legal_document], contact_ident_params) unless action.call @@ -50,7 +88,7 @@ module Repp desc 'Update existing contact' def update action = Actions::ContactUpdate.new(@contact, contact_params_with_address(required: false), - params[:legal_document], + contact_params[:legal_document], contact_ident_params(required: false), current_user) unless action.call @@ -73,29 +111,71 @@ module Repp render_success end - def contact_addr_present? - return false unless contact_addr_params.key?(:addr) + private - contact_addr_params[:addr].keys.any? + def index_params + params.permit(:id, :limit, :offset, :details, :q, :simple, + :page, :per_page, :domain_filter, + domain_filter: [], + q: %i[s name_matches code_eq ident_matches ident_type_eq + email_matches country_code_eq types_contains_array + updated_at_gteq created_at_gteq created_at_lteq + statuses_contains_array] + [s: []]) + end + + def search_params + index_params.fetch(:q, {}) + end + + def domain_filter_params + filter_params = index_params.slice(:id, :page, :per_page, :domain_filter).to_h + filter_params.merge!({ sort: hashify(index_params[:q].fetch(:s)) }) if index_params[:q] + filter_params + end + + def hashify(sort) + return unless sort + + sort_hash = {} + if sort.is_a?(Array) + sort.each do |s| + sort_hash.merge!(Hash[*s.split(' ')]) + end + else + sort_hash.merge!(Hash[*sort.split(' ')]) + end + sort_hash + end + + def limit + index_params[:limit] || 200 + end + + def offset + index_params[:offset] || 0 + end + + def serialized_contacts(contacts) + return contacts.map {|c| c.code } unless index_params[:details] == 'true' + + address_processing = Contact.address_processing? + contacts.map do |c| + Serializers::Repp::Contact.new(c, show_address: address_processing).to_json + end + end + + def contact_addr_present? + return false unless contact_addr_params + + contact_addr_params.keys.any? end def create_update_success_body - { code: opt_addr? ? 1100 : nil, data: { contact: { id: @contact.code } }, + { code: opt_addr? ? 1100 : nil, + data: { contact: { code: @contact.code } }, message: opt_addr? ? I18n.t('epp.contacts.completed_without_address') : nil } end - def showable_contacts(details, limit, offset) - contacts = current_user.registrar.contacts.limit(limit).offset(offset) - - return contacts.pluck(:code) unless details - - contacts.map do |contact| - serializer = ::Serializers::Repp::Contact.new(contact, - show_address: Contact.address_processing?) - serializer.to_json - end - end - def opt_addr? !Contact.address_processing? && contact_addr_present? end @@ -106,36 +186,36 @@ module Repp end def contact_params_with_address(required: true) - return contact_create_params(required: required) unless contact_addr_params.key?(:addr) + return contact_create_params(required: required) unless contact_addr_present? - addr = {} - contact_addr_params[:addr].each_key { |k| addr[k] = contact_addr_params[:addr][k] } - contact_create_params(required: required).merge(addr) + contact_create_params(required: required).merge(contact_addr_params) end def contact_create_params(required: true) - params.require(:contact).require(%i[name email phone]) if required - params.require(:contact).permit(:name, :email, :phone, :id) + create_params = %i[name email phone] + contact_params.require(create_params) if required + contact_params.slice(*create_params) end def contact_ident_params(required: true) - if required - params.require(:contact).require(:ident).require(%i[ident ident_type ident_country_code]) - params.require(:contact).require(:ident).permit(:ident, :ident_type, :ident_country_code) - else - params.permit(contact: { ident: %i[ident ident_type ident_country_code] }) - end - - params[:contact][:ident] + ident_params = %i[ident ident_type ident_country_code] + contact_params.require(:ident).require(ident_params) if required + contact_params[:ident].to_h end def contact_addr_params - if Contact.address_processing? - params.require(:contact).require(:addr).require(%i[country_code city street zip]) - params.require(:contact).require(:addr).permit(:country_code, :city, :street, :zip) - else - params.require(:contact).permit(addr: %i[country_code city street zip]) - end + return contact_params[:addr] unless Contact.address_processing? + + addr_params = %i[country_code city street zip] + contact_params.require(:addr).require(addr_params) + contact_params[:addr] + end + + def contact_params + params.require(:contact).permit(:name, :email, :phone, :legal_document, + legal_document: %i[body type], + ident: [%i[ident ident_type ident_country_code]], + addr: [%i[country_code city street zip state]]) end end end diff --git a/app/controllers/repp/v1/domains/base_contacts_controller.rb b/app/controllers/repp/v1/domains/base_contacts_controller.rb index 65dbea9ac..225b14b58 100644 --- a/app/controllers/repp/v1/domains/base_contacts_controller.rb +++ b/app/controllers/repp/v1/domains/base_contacts_controller.rb @@ -2,19 +2,16 @@ module Repp module V1 module Domains class BaseContactsController < BaseController - before_action :set_current_contact, only: [:update] - before_action :set_new_contact, only: [:update] + before_action :set_contacts, only: [:update] - def set_current_contact - @current_contact = current_user.registrar.contacts - .find_by!(code: contact_params[:current_contact_id]) - end - - def set_new_contact - @new_contact = current_user.registrar.contacts.find_by!(code: params[:new_contact_id]) + def set_contacts + contacts = current_user.registrar.contacts + @current_contact = contacts.find_by!(code: contact_params[:current_contact_id]) + @new_contact = contacts.find_by!(code: contact_params[:new_contact_id]) end def update + authorize! :manage, :repp @epp_errors ||= ActiveModel::Errors.new(self) return unless @new_contact.invalid? @@ -26,8 +23,11 @@ module Repp private def contact_params - params.require(%i[current_contact_id new_contact_id]) - params.permit(:current_contact_id, :new_contact_id) + param_list = %i[current_contact_id new_contact_id] + params.require(param_list) + params.permit(:current_contact_id, :new_contact_id, + contact: {}, + admin_contact: [param_list]) end end end diff --git a/app/controllers/repp/v1/domains/renews_controller.rb b/app/controllers/repp/v1/domains/renews_controller.rb index af40e17b1..f963cd3a7 100644 --- a/app/controllers/repp/v1/domains/renews_controller.rb +++ b/app/controllers/repp/v1/domains/renews_controller.rb @@ -8,28 +8,29 @@ module Repp api :POST, 'repp/v1/domains/:domain_name/renew' desc 'Renew domain' - param :renew, Hash, required: true, desc: 'Renew parameters' do + param :renews, Hash, required: true, desc: 'Renew parameters' do param :period, Integer, required: true, desc: 'Renew period. Month (m) or year (y)' param :period_unit, String, required: true, desc: 'For how many months or years to renew' param :exp_date, String, required: true, desc: 'Current expiry date for domain' end def create authorize!(:renew, @domain) - action = Actions::DomainRenew.new(@domain, renew_params[:renew], current_user.registrar) + action = Actions::DomainRenew.new(@domain, renew_params[:renews], current_user.registrar) unless action.call handle_errors(@domain) return end - render_success(data: { domain: { name: @domain.name } }) + render_success(data: { domain: { name: @domain.name, id: @domain.uuid } }) end def bulk_renew + authorize! :manage, :repp renew = run_bulk_renew_task(@domains, bulk_renew_params[:renew_period]) return render_success(data: { updated_domains: @domains.map(&:name) }) if renew.valid? - msg = renew.errors.keys.map { |k, _v| renew.errors[k] }.join(', ') + msg = renew.errors.attribute_names.map { |k, _v| renew.errors[k] }.join(', ') @epp_errors.add(:epp_errors, msg: msg, code: '2002') handle_errors end @@ -37,7 +38,7 @@ module Repp private def renew_params - params.permit(:domain_id, renew: %i[period period_unit exp_date]) + params.permit(:domain_id, renews: %i[period period_unit exp_date]) end def validate_renew_period @@ -53,6 +54,7 @@ module Repp if bulk_renew_params[:domains].instance_of?(Array) @domains = bulk_renew_domains + @epp_errors.add(:epp_errors, msg: 'Domains cannot be empty', code: '2005') if @domains.empty? else @epp_errors.add(:epp_errors, msg: 'Domains attribute must be an array', code: '2005') end diff --git a/app/controllers/repp/v1/domains_controller.rb b/app/controllers/repp/v1/domains_controller.rb index 06d4a0330..37f735b69 100644 --- a/app/controllers/repp/v1/domains_controller.rb +++ b/app/controllers/repp/v1/domains_controller.rb @@ -3,6 +3,7 @@ module Repp module V1 class DomainsController < BaseController # rubocop:disable Metrics/ClassLength before_action :set_authorized_domain, only: %i[transfer_info destroy] + before_action :find_password, only: %i[update destroy] before_action :validate_registrar_authorization, only: %i[transfer_info destroy] before_action :forward_registrar_id, only: %i[create update destroy] before_action :set_domain, only: %i[update] @@ -10,20 +11,31 @@ module Repp api :GET, '/repp/v1/domains' desc 'Get all existing domains' def index + authorize! :info, Epp::Domain records = current_user.registrar.domains - domains = records.limit(limit).offset(offset) + q = records.ransack(search_params) + q.sorts = ['valid_to asc', 'created_at desc'] if q.sorts.empty? + # use distinct: false here due to ransack bug: + # https://github.com/activerecord-hackery/ransack/issues/429 + domains = q.result(distinct: false) - render_success(data: { domains: serialized_domains(domains), - total_number_of_records: records.count }) + limited_domains = domains.limit(limit).offset(offset).includes(:registrar, :registrant) + + render_success(data: { new_domain: records.any? ? serialized_domains([records.last]) : [], + domains: serialized_domains(limited_domains.to_a.uniq), + count: domains.count, + statuses: DomainStatus::STATUSES }) end api :GET, '/repp/v1/domains/:domain_name' desc 'Get a specific domain' def show - @domain = Epp::Domain.find_by!(name: params[:id]) + @domain = Epp::Domain.find_by_name(params[:id]) + authorize! :info, @domain + sponsor = @domain.registrar == current_user.registrar - render_success(data: { domain: Serializers::Repp::Domain.new(@domain, - sponsored: sponsor).to_json }) + serializer = Serializers::Repp::Domain.new(@domain, sponsored: sponsor) + render_success(data: { domain: serializer.to_json }) end api :POST, '/repp/v1/domains' @@ -33,7 +45,7 @@ module Repp param :registrant, String, required: true, desc: 'Registrant contact code' param :reserved_pw, String, required: false, desc: 'Reserved password for domain' param :transfer_code, String, required: false, desc: 'Desired transfer code for domain' - # param :period, String, required: true, desc: 'Registration period in months or years' + param :period, Integer, required: true, desc: 'Registration period in months or years' param :period_unit, String, required: true, desc: 'Period type (month m) or (year y)' param :nameservers_attributes, Array, required: false, desc: 'Domain nameservers' do param :hostname, String, required: true, desc: 'Nameserver hostname' @@ -56,15 +68,18 @@ module Repp end end def create - authorize!(:create, Epp::Domain) + authorize! :create, Epp::Domain @domain = Epp::Domain.new - action = Actions::DomainCreate.new(@domain, domain_create_params) + + action = Actions::DomainCreate.new(@domain, domain_params) # rubocop:disable Style/AndOr handle_errors(@domain) and return unless action.call # rubocop:enable Style/AndOr - render_success(data: { domain: { name: @domain.name, transfer_code: @domain.transfer_code } }) + render_success(data: { domain: { name: @domain.name, + transfer_code: @domain.transfer_code, + id: @domain.reload.uuid } }) end api :PUT, '/repp/v1/domains/:domain_name' @@ -73,20 +88,20 @@ module Repp param :domain, Hash, required: true, desc: 'Changes of domain object' do param :registrant, Hash, required: false, desc: 'New registrant object' do param :code, String, required: true, desc: 'New registrant contact code' - param :verified, [true, false], required: false, - desc: 'Registrant change is already verified' + param :verified, [true, false, 'true', 'false'], required: false, + desc: 'Registrant change is already verified' end param :transfer_code, String, required: false, desc: 'New authorization code' end def update - action = Actions::DomainUpdate.new(@domain, params[:domain], false) - + authorize!(:update, @domain, @password) + action = Actions::DomainUpdate.new(@domain, update_params, false) unless action.call handle_errors(@domain) return end - render_success(data: { domain: { name: @domain.name } }) + render_success(data: { domain: { name: @domain.name, id: @domain.uuid } }) end api :GET, '/repp/v1/domains/:domain_name/transfer_info' @@ -108,23 +123,28 @@ module Repp api :POST, '/repp/v1/domains/transfer' desc 'Transfer multiple domains' def transfer + authorize! :transfer, Epp::Domain @errors ||= [] @successful = [] - transfer_params[:domain_transfers].each do |transfer| initiate_transfer(transfer) end + render_success(data: { success: @successful, failed: @errors }) end api :DELETE, '/repp/v1/domains/:domain_name' desc 'Delete specific domain' - param :delete, Hash, required: true, desc: 'Object holding verified key' do - param :verified, [true, false], required: true, - desc: 'Whether to ask registrant verification or not' + param :id, String, desc: 'Domain name in IDN / Puny format' + param :domain, Hash, required: true, desc: 'Changes of domain object' do + param :delete, Hash, required: true, desc: 'Object holding verified key' do + param :verified, [true, false, 'true', 'false'], required: true, + desc: 'Whether to ask registrant verification or not' + end end def destroy - action = Actions::DomainDelete.new(@domain, params, current_user.registrar) + authorize!(:delete, @domain, @password) + action = Actions::DomainDelete.new(@domain, domain_params, current_user.registrar) # rubocop:disable Style/AndOr handle_errors(@domain) and return unless action.call @@ -138,7 +158,8 @@ module Repp def serialized_domains(domains) return domains.pluck(:name) unless index_params[:details] == 'true' - domains.map { |d| Serializers::Repp::Domain.new(d).to_json } + simple = index_params[:simple] == 'true' || false + domains.map { |d| Serializers::Repp::Domain.new(d, simplify: simple).to_json } end def initiate_transfer(transfer) @@ -155,18 +176,13 @@ module Repp end def transfer_params - params.require(:data).require(:domain_transfers).each do |t| - t.require(:domain_name) - t.permit(:domain_name) - t.require(:transfer_code) - t.permit(:transfer_code) - end - params.require(:data).permit(domain_transfers: %i[domain_name transfer_code]) + params.require(:data).require(:domain_transfers) + params.require(:data).permit(domain_transfers: [%i[domain_name transfer_code]]) end def transfer_info_params params.require(:id) - params.permit(:id) + params.permit(:id, :legal_document, delete: [:verified]) end def forward_registrar_id @@ -177,6 +193,7 @@ module Repp def set_domain registrar = current_user.registrar + @domain = Epp::Domain.find_by(registrar: registrar, name: params[:id]) @domain ||= Epp::Domain.find_by!(registrar: registrar, name_puny: params[:id]) @@ -185,6 +202,10 @@ module Repp raise ActiveRecord::RecordNotFound end + def find_password + @password = domain_params[:transfer_code] + end + def set_authorized_domain @epp_errors ||= ActiveModel::Errors.new(self) @domain = domain_from_url_hash @@ -201,7 +222,7 @@ module Repp end def domain_from_url_hash - entry = transfer_info_params[:id] + entry = params[:id] return Epp::Domain.find(entry) if entry.match?(/\A[0-9]+\z/) Epp::Domain.find_by!('name = ? OR name_puny = ?', entry, entry) @@ -216,15 +237,48 @@ module Repp end def index_params - params.permit(:limit, :offset, :details) + params.permit(:limit, :offset, :details, :simple, :q, + q: %i[s name_matches registrant_id_eq contacts_ident_eq + nameservers_hostname_eq valid_to_gteq valid_to_lteq + statuses_contains_array] + [s: []]) end - def domain_create_params - params.require(:domain).permit(:name, :registrant, :period, :period_unit, :registrar, - :transfer_code, :reserved_pw, - dnskeys_attributes: [%i[flags alg protocol public_key]], - nameservers_attributes: [[:hostname, { ipv4: [], ipv6: [] }]], - admin_contacts: [], tech_contacts: []) + def search_params + index_params.fetch(:q, {}) + end + + def update_params + dup_params = domain_params.to_h.dup + return dup_params unless dup_params[:contacts] + + new_contact_params = dup_params[:contacts].map do |c| + c.to_h.symbolize_keys + end + + old_contact_params = @domain.domain_contacts.map do |c| + { code: c.contact_code_cache, type: c.name.downcase } + end + dup_params[:contacts] = (new_contact_params - old_contact_params).map { |c| c.merge(action: 'add') } + dup_params[:contacts].concat((old_contact_params - new_contact_params) + .map { |c| c.merge(action: 'rem') }) + + dup_params + end + + def domain_params + params.require(:domain) + .permit(:name, :period, :period_unit, :registrar, + :transfer_code, :reserved_pw, :legal_document, + :registrant, legal_document: %i[body type], + registrant: [%i[code verified]], + dns_keys: [%i[id flags alg protocol public_key action]], + nameservers: [[:id, :hostname, + :action, { ipv4: [], ipv6: [] }]], + contacts: [%i[code type action]], + nameservers_attributes: [[:hostname, { ipv4: [], ipv6: [] }]], + admin_contacts: [], tech_contacts: [], + dnskeys_attributes: [%i[flags alg protocol public_key]], + delete: [:verified]) end end end diff --git a/app/controllers/repp/v1/invoices_controller.rb b/app/controllers/repp/v1/invoices_controller.rb new file mode 100644 index 000000000..c8c6676ec --- /dev/null +++ b/app/controllers/repp/v1/invoices_controller.rb @@ -0,0 +1,118 @@ +require 'serializers/repp/invoice' +module Repp + module V1 + class InvoicesController < BaseController + load_and_authorize_resource + + api :get, '/repp/v1/invoices' + desc 'Get all invoices' + def index + records = current_user.registrar.invoices + + q = records.ransack(search_params) + q.sorts = 'created_at desc' if q.sorts.empty? + invoices = q.result(distinct: true) + + limited_invoices = invoices.limit(limit).offset(offset) + .includes(:items, :account_activity, :buyer) + + render_success(data: { invoices: serialized_invoices(limited_invoices), + count: invoices.count }) + end + + api :get, '/repp/v1/invoices/:id' + desc 'Get a specific invoice' + def show + serializer = Serializers::Repp::Invoice.new(@invoice) + render_success(data: { invoice: serializer.to_json }) + end + + api :get, '/repp/v1/invoices/:id/download' + desc 'Download a specific invoice as pdf file' + def download + filename = "Invoice-#{@invoice.number}.pdf" + @response = { code: 1000, message: 'Command completed successfully', + data: filename } + send_data @invoice.as_pdf, filename: filename + end + + api :post, '/repp/v1/invoices/:id/send_to_recipient' + desc 'Send invoice pdf to recipient' + def send_to_recipient + recipient = invoice_params[:recipient] + InvoiceMailer.invoice_email(invoice: @invoice, recipient: recipient) + .deliver_now + serializer = Serializers::Repp::Invoice.new(@invoice, simplify: true) + render_success(data: { invoice: serializer.to_json + .merge!(recipient: recipient) }) + end + + api :post, '/repp/v1/invoices/:id/cancel' + desc 'Cancel a specific invoice' + def cancel + action = Actions::InvoiceCancel.new(@invoice) + if action.call + EisBilling::SendInvoiceStatus.send_info(invoice_number: @invoice.number, + status: 'cancelled') + else + handle_non_epp_errors(@invoice) + return + end + + serializer = Serializers::Repp::Invoice.new(@invoice, simplify: true) + render_success(data: { invoice: serializer.to_json }) + end + + api :post, '/repp/v1/invoices/add_credit' + desc 'Generate add credit invoice' + def add_credit + deposit = Deposit.new(invoice_params.merge(registrar: current_user.registrar)) + invoice = deposit.issue_prepayment_invoice + if invoice + serializer = Serializers::Repp::Invoice.new(invoice, simplify: true) + render_success(data: { invoice: serializer.to_json }) + else + handle_errors(deposit) + end + end + + private + + def index_params + params.permit(:id, :limit, :offset, :details, :q, :simple, + :page, :per_page, + q: %i[number_str_matches due_date_gteq due_date_lteq + account_activity_created_at_gteq + account_activity_created_at_lteq + account_activity_id_not_null + account_activity_id_null + cancelled_at_not_null + number_gteq number_lteq + total_gteq total_lteq s] + [s: []]) + end + + def search_params + index_params.fetch(:q, {}) + end + + def invoice_params + params.require(:invoice).permit(:id, :recipient, :amount, :description) + end + + def limit + index_params[:limit] || 200 + end + + def offset + index_params[:offset] || 0 + end + + def serialized_invoices(invoices) + return invoices.pluck(:number) unless index_params[:details] == 'true' + + simple = index_params[:simple] == 'true' || false + invoices.map { |i| Serializers::Repp::Invoice.new(i, simplify: simple).to_json } + end + end + end +end \ No newline at end of file diff --git a/app/controllers/repp/v1/registrar/auth_controller.rb b/app/controllers/repp/v1/registrar/auth_controller.rb new file mode 100644 index 000000000..8fba9eefb --- /dev/null +++ b/app/controllers/repp/v1/registrar/auth_controller.rb @@ -0,0 +1,49 @@ +module Repp + module V1 + module Registrar + class AuthController < BaseController + skip_before_action :authenticate_user, only: :tara_callback + skip_before_action :check_ip_restriction, only: :tara_callback + + api :GET, 'repp/v1/registrar/auth' + desc 'check user auth info and return data' + def index + registrar = current_user.registrar + render_success(data: auth_values_to_data(registrar: registrar)) + end + + api :POST, 'repp/v1/registrar/auth/tara_callback' + desc 'check tara callback omniauth user info and return token' + def tara_callback + user = ApiUser.from_omniauth(auth_params) + handle_non_epp_errors(user, I18n.t(:no_such_user)) and return unless user && user&.active + + token = Base64.urlsafe_encode64("#{user.username}:#{user.plain_text_password}") + render_success(data: { token: token, username: user.username }) + end + + api :put, '/repp/v1/registrar/auth/switch_user/:new_user_id' + desc 'Switch session to another api user' + def switch_user + new_user = ApiUser.find(auth_params[:new_user_id]) + unless current_user.linked_with?(new_user) + handle_non_epp_errors(new_user, 'Cannot switch to unlinked user') + return + end + + @current_user = new_user + data = auth_values_to_data(registrar: current_user.registrar) + message = I18n.t('registrar.current_user.switch.switched', new_user: new_user) + token = Base64.urlsafe_encode64("#{new_user.username}:#{new_user.plain_text_password}") + render_success(data: { token: token, registrar: data }, message: message) + end + + private + + def auth_params + params.require(:auth).permit(:uid, :new_user_id) + end + end + end + end +end \ No newline at end of file diff --git a/app/controllers/repp/v1/registrar/nameservers_controller.rb b/app/controllers/repp/v1/registrar/nameservers_controller.rb index 174193350..fbd4c03ec 100644 --- a/app/controllers/repp/v1/registrar/nameservers_controller.rb +++ b/app/controllers/repp/v1/registrar/nameservers_controller.rb @@ -19,13 +19,16 @@ module Repp end def update # rubocop:disable Metrics/MethodLength + authorize! :manage, :repp affected, errored = if hostname.present? - current_user.registrar.replace_nameservers(hostname, - hostname_params[:data][:attributes], - domains: domains_from_params) + current_user.registrar + .replace_nameservers(hostname, + hostname_params[:attributes], + domains: domains_from_params) else - current_user.registrar.add_nameservers(hostname_params[:data][:attributes], - domains: domains_from_params) + current_user.registrar + .add_nameservers(hostname_params[:attributes], + domains: domains_from_params) end render_success(data: data_format_for_success(affected, errored)) @@ -36,34 +39,32 @@ module Repp private def domains_from_params - return [] unless params[:data][:domains] + return [] unless hostname_params[:domains] - params[:data][:domains].map(&:downcase) + hostname_params[:domains].map(&:downcase) end def data_format_for_success(affected_domains, errored_domains) { type: 'nameserver', - id: params[:data][:attributes][:hostname], - attributes: params[:data][:attributes], + id: hostname_params[:attributes][:hostname], + attributes: hostname_params[:attributes], affected_domains: affected_domains, skipped_domains: errored_domains, } end def hostname_params - params.require(:data).require(%i[type]) - params.require(:data).require(:attributes).require([:hostname]) - - params.permit(data: [ - :type, :id, - { domains: [], - attributes: [:hostname, { ipv4: [], ipv6: [] }] } - ]) + params.require(:data).permit(:type, :id, nameserver: [], domains: [], + attributes: [:hostname, { ipv4: [], ipv6: [] }]) + .tap do |data| + data.require(:type) + data.require(:attributes).require([:hostname]) + end end def hostname - hostname_params[:data][:id] || nil + hostname_params[:id] || nil end def verify_nameserver_existance diff --git a/app/controllers/repp/v1/registrar/notifications_controller.rb b/app/controllers/repp/v1/registrar/notifications_controller.rb index 815ee85b9..6b1d342cc 100644 --- a/app/controllers/repp/v1/registrar/notifications_controller.rb +++ b/app/controllers/repp/v1/registrar/notifications_controller.rb @@ -2,7 +2,7 @@ module Repp module V1 module Registrar class NotificationsController < BaseController - before_action :set_notification, only: [:update] + before_action :set_notification, only: %i[update show] api :GET, '/repp/v1/registrar/notifications' desc 'Get the latest unread poll message' @@ -39,7 +39,6 @@ module Repp api :GET, '/repp/v1/registrar/notifications/:notification_id' desc 'Get a specific poll message' def show - @notification = current_user.registrar.notifications.find(params[:id]) data = @notification.as_json(only: %i[id text attached_obj_id attached_obj_type read]) render_success(data: data) @@ -51,6 +50,7 @@ module Repp param :read, [true, 'true'], required: true, desc: 'Set as true to mark as read' end def update + authorize! :manage, :poll # rubocop:disable Style/AndOr handle_errors(@notification) and return unless @notification.mark_as_read # rubocop:enable Style/AndOr diff --git a/app/controllers/repp/v1/registrar/summary_controller.rb b/app/controllers/repp/v1/registrar/summary_controller.rb new file mode 100644 index 000000000..15f7d0164 --- /dev/null +++ b/app/controllers/repp/v1/registrar/summary_controller.rb @@ -0,0 +1,111 @@ +module Repp + module V1 + module Registrar + class SummaryController < BaseController + api :GET, 'repp/v1/registrar/summary' + desc 'check user summary info and return data' + + def index + user = current_user + registrar = user.registrar + if can?(:manage, :poll) + user_notifications = user.unread_notifications + notification = user_notifications.order('created_at DESC').take + notifications_count = user_notifications.count + if notification&.attached_obj_type && notification&.attached_obj_id + begin + object = object_by_type(notification.attached_obj_type) + .find(notification.attached_obj_id) + rescue => e + # the data model might be inconsistent; or ... + # this could happen if the registrar does not dequeue messages, and then the domain was deleted + # SELECT messages.id, domains.name, messages.body FROM messages LEFT OUTER + # JOIN domains ON attached_obj_id::INTEGER = domains.id + # WHERE attached_obj_type = 'Epp::Domain' AND name IS NULL; + message = 'orphan message, domain deleted, registrar should dequeue: ' + Rails.logger.error message + e.to_s + end + end + end + + data = serialize_data(registrar: registrar, + notification: notification, + notifications_count: notifications_count, + object: object) + + render_success(data: data) + end + + private + + def object_by_type(object_type) + Object.const_get(object_type) + rescue NameError + Object.const_get("Version::#{object_type}") + end + + # rubocop:disable Metrics/MethodLength + def serialize_data(registrar:, notification:, notifications_count:, object: nil) + data = current_user.as_json(only: %i[id username]) + data[:registrar_name] = registrar.name + data[:registrar_reg_no] = registrar.reg_no + data[:last_login_date] = last_login_date + data[:domains] = registrar.domains.count if can? :view, Depp::Domain + data[:contacts] = registrar.contacts.count if can? :view, Depp::Contact + data[:phone] = registrar.phone + data[:email] = registrar.email + data[:billing_email] = registrar.billing_email + data[:billing_address] = registrar.address + data[:notification] = serialized_notification(notification, object) + data[:notifications_count] = notifications_count + data + end + # rubocop:enable Metrics/MethodLength + + def last_login_date + q = ApiLog::ReppLog.ransack({ request_path_eq: '/repp/v1/registrar/auth', + response_code_eq: '200', + api_user_name_cont: current_user.username, + request_method_eq: 'GET' }) + q.sorts = 'id desc' + q.result.offset(1).first&.created_at + end + + def serialized_notification(notification, object) + return unless notification + + notification.created_at = notification.created_at.utc.xmlschema + obj_data = serialized_object(object, notification.attached_obj_type) + notification.as_json(only: %i[id text created_at attached_obj_id attached_obj_type]) + .merge({ attached_obj_data: obj_data }) + end + + def serialized_object(object, obj_type) + return unless object + + case obj_type + when 'DomainTransfer' + { + name: object.domain_name, + trStatus: object.status, + reID: object.new_registrar.code, + reDate: object.transfer_requested_at.try(:iso8601), + acID: object.old_registrar.code, + acDate: object.transferred_at.try(:iso8601) || object.wait_until.try(:iso8601), + exDate: object.domain_valid_to.iso8601, + } + when 'ContactUpdateAction' + { + contacts: object.to_non_available_contact_codes, + operation: object.operation, + opDate: object.created_at.utc.xmlschema, + svTrid: object.id, + who: object.user.username, + reason: 'Auto-update according to official data', + } + end + end + end + end + end +end \ No newline at end of file diff --git a/app/interactions/actions/domain_delete.rb b/app/interactions/actions/domain_delete.rb index 7790c25a9..750f0abac 100644 --- a/app/interactions/actions/domain_delete.rb +++ b/app/interactions/actions/domain_delete.rb @@ -32,7 +32,7 @@ module Actions def verify? return false unless Setting.request_confirmation_on_domain_deletion_enabled - return false if params[:delete][:verified] == true + return false if true?(params[:delete][:verified]) true end @@ -51,5 +51,9 @@ module Actions end true end + + def true?(obj) + obj.to_s.downcase == 'true' + end end end diff --git a/app/interactions/actions/domain_update.rb b/app/interactions/actions/domain_update.rb index a86b038ff..377f90e40 100644 --- a/app/interactions/actions/domain_update.rb +++ b/app/interactions/actions/domain_update.rb @@ -14,6 +14,7 @@ module Actions assign_new_registrant if params[:registrant] assign_relational_modifications assign_requested_statuses + ::Actions::BaseAction.maybe_attach_legal_doc(domain, params[:legal_document]) commit @@ -240,7 +241,7 @@ module Actions def verify_registrant_change? return validate_dispute_case if params[:reserved_pw] - return false if !@changes_registrant || params[:registrant][:verified] == true + return false if !@changes_registrant || true?(params[:registrant][:verified]) return true unless domain.disputed? domain.add_epp_error('2304', nil, nil, 'Required parameter missing; reservedpw element ' \ @@ -282,5 +283,9 @@ module Actions false end + + def true?(obj) + obj.to_s.downcase == 'true' + end end end diff --git a/app/interactions/actions/invoice_cancel.rb b/app/interactions/actions/invoice_cancel.rb new file mode 100644 index 000000000..2f0a77894 --- /dev/null +++ b/app/interactions/actions/invoice_cancel.rb @@ -0,0 +1,15 @@ +module Actions + class InvoiceCancel + attr_reader :invoice + + def initialize(invoice) + @invoice = invoice + end + + def call + return false unless @invoice.can_be_cancelled? + + @invoice.update(cancelled_at: Time.zone.now) + end + end +end diff --git a/app/models/ability.rb b/app/models/ability.rb index bc2caa6ba..31543a586 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -34,9 +34,11 @@ class Ability if @user.registrar.api_ip_white?(@ip) can :manage, Depp::Contact can :manage, :xml_console - can :manage, Depp::Domain + can :manage, Depp::Domain end + can :manage, Account + # Poll can :manage, :poll @@ -65,12 +67,13 @@ class Ability can(:update, Epp::Contact) { |c, pw| c.registrar_id == @user.registrar_id || c.auth_info == pw } can(:delete, Epp::Contact) { |c, pw| c.registrar_id == @user.registrar_id || c.auth_info == pw } can(:renew, Epp::Contact) - can(:transfer, Epp::Contact) + can(:transfer, Epp::Contact) can(:view_password, Epp::Contact) { |c, pw| c.registrar_id == @user.registrar_id || c.auth_info == pw } end def billing # Registrar/api_user dynamic role can(:manage, Invoice) { |i| i.buyer_id == @user.registrar_id } + can :manage, Account can :manage, :deposit can :read, AccountActivity can :manage, :balance_auto_reload diff --git a/app/models/action.rb b/app/models/action.rb index 8a822f867..03c8e9fe8 100644 --- a/app/models/action.rb +++ b/app/models/action.rb @@ -28,14 +28,20 @@ class Action < ApplicationRecord end def to_non_available_contact_codes - return [] unless bulk_action? + return [serialized_contact(contact)] unless bulk_action? subactions.map do |a| - { - code: a.contact.code, - avail: 0, - reason: 'in use', - } + serialized_contact(a.contact) end end + + private + + def serialized_contact(contact) + { + code: contact.code, + avail: 0, + reason: 'in use', + } + end end diff --git a/app/models/admin_domain_contact.rb b/app/models/admin_domain_contact.rb index 7ccf3efcb..9003afb0f 100644 --- a/app/models/admin_domain_contact.rb +++ b/app/models/admin_domain_contact.rb @@ -6,7 +6,7 @@ class AdminDomainContact < DomainContact skipped_domains = [] admin_contacts = where(contact: current_contact) - admin_contacts.each do |admin_contact| + admin_contacts.includes(:domain).each do |admin_contact| if admin_contact.domain.bulk_update_prohibited? skipped_domains << admin_contact.domain.name next diff --git a/app/models/api_user.rb b/app/models/api_user.rb index 8ae131a6e..d357a2e75 100644 --- a/app/models/api_user.rb +++ b/app/models/api_user.rb @@ -30,11 +30,11 @@ class ApiUser < User alias_attribute :login, :username - SUPER = 'super' - EPP = 'epp' - BILLING = 'billing' + SUPER = 'super'.freeze + EPP = 'epp'.freeze + BILLING = 'billing'.freeze - ROLES = %w(super epp billing) # should not match to admin roles + ROLES = %w[super epp billing].freeze # should not match to admin roles def ability @ability ||= Ability.new(self) @@ -72,8 +72,8 @@ class ApiUser < User def linked_users self.class.where(identity_code: identity_code) - .where("identity_code IS NOT NULL AND identity_code != ''") - .where.not(id: id) + .where("identity_code IS NOT NULL AND identity_code != ''") + .where.not(id: id) end def linked_with?(another_api_user) diff --git a/app/models/balance_auto_reload_types/threshold.rb b/app/models/balance_auto_reload_types/threshold.rb index d55cb977a..8bb494ae5 100644 --- a/app/models/balance_auto_reload_types/threshold.rb +++ b/app/models/balance_auto_reload_types/threshold.rb @@ -1,6 +1,7 @@ module BalanceAutoReloadTypes class Threshold include ActiveModel::Model + include ActiveModel::Validations attr_accessor :amount, :threshold @@ -11,8 +12,9 @@ module BalanceAutoReloadTypes Setting.minimum_deposit end - def as_json(options) + def as_json(options = nil) { name: name }.merge(super) + .except('errors', 'validation_context') end private diff --git a/app/models/bulk_action.rb b/app/models/bulk_action.rb deleted file mode 100644 index 9c98ee2db..000000000 --- a/app/models/bulk_action.rb +++ /dev/null @@ -1 +0,0 @@ -class BulkAction < Action; end diff --git a/app/models/concerns/invoice/cancellable.rb b/app/models/concerns/invoice/cancellable.rb index 8c9e142a8..9b1c6435b 100644 --- a/app/models/concerns/invoice/cancellable.rb +++ b/app/models/concerns/invoice/cancellable.rb @@ -5,12 +5,22 @@ module Invoice::Cancellable scope :non_cancelled, -> { where(cancelled_at: nil) } end + def can_be_cancelled? + unless cancellable? + errors.add(:base, :invoice_status_prohibits_operation) + return false + end + + true + end + def cancellable? unpaid? && not_cancelled? end def cancel raise 'Invoice cannot be cancelled' unless cancellable? + update!(cancelled_at: Time.zone.now) end diff --git a/app/models/concerns/invoice/payable.rb b/app/models/concerns/invoice/payable.rb index 6e2cc19b4..855ea8f41 100644 --- a/app/models/concerns/invoice/payable.rb +++ b/app/models/concerns/invoice/payable.rb @@ -15,6 +15,8 @@ module Invoice::Payable end def receipt_date + return unless paid? + account_activity.created_at.to_date end diff --git a/app/models/contact.rb b/app/models/contact.rb index 2be404997..58c25f777 100644 --- a/app/models/contact.rb +++ b/app/models/contact.rb @@ -80,41 +80,41 @@ class Contact < ApplicationRecord self.ignored_columns = %w[legacy_id legacy_history_id] - ORG = 'org' - PRIV = 'priv' + ORG = 'org'.freeze + PRIV = 'priv'.freeze # For foreign private persons who has no national identification number BIRTHDAY = 'birthday'.freeze # From old registry software ("Fred"). No new contact can be created with this status - PASSPORT = 'passport' + PASSPORT = 'passport'.freeze # # STATUSES # # Requests to delete the object MUST be rejected. - CLIENT_DELETE_PROHIBITED = 'clientDeleteProhibited' - SERVER_DELETE_PROHIBITED = 'serverDeleteProhibited' + CLIENT_DELETE_PROHIBITED = 'clientDeleteProhibited'.freeze + SERVER_DELETE_PROHIBITED = 'serverDeleteProhibited'.freeze # Requests to transfer the object MUST be rejected. - CLIENT_TRANSFER_PROHIBITED = 'clientTransferProhibited' - SERVER_TRANSFER_PROHIBITED = 'serverTransferProhibited' + CLIENT_TRANSFER_PROHIBITED = 'clientTransferProhibited'.freeze + SERVER_TRANSFER_PROHIBITED = 'serverTransferProhibited'.freeze # The contact object has at least one active association with # another object, such as a domain object. Servers SHOULD provide # services to determine existing object associations. # "linked" status MAY be combined with any status. - LINKED = 'linked' + LINKED = 'linked'.freeze # This is the normal status value for an object that has no pending # operations or prohibitions. This value is set and removed by the # server as other status values are added or removed. # "ok" status MAY only be combined with "linked" status. - OK = 'ok' + OK = 'ok'.freeze # Requests to update the object (other than to remove this status) MUST be rejected. - CLIENT_UPDATE_PROHIBITED = 'clientUpdateProhibited' - SERVER_UPDATE_PROHIBITED = 'serverUpdateProhibited' + CLIENT_UPDATE_PROHIBITED = 'clientUpdateProhibited'.freeze + SERVER_UPDATE_PROHIBITED = 'serverUpdateProhibited'.freeze # A transform command has been processed for the object, but the # action has not been completed by the server. Server operators can @@ -129,16 +129,16 @@ class Contact < ApplicationRecord # the status of the object has changed. # The pendingCreate, pendingDelete, pendingTransfer, and pendingUpdate # status values MUST NOT be combined with each other. - PENDING_CREATE = 'pendingCreate' + PENDING_CREATE = 'pendingCreate'.freeze # "pendingTransfer" status MUST NOT be combined with either # "clientTransferProhibited" or "serverTransferProhibited" status. - PENDING_TRANSFER = 'pendingTransfer' + PENDING_TRANSFER = 'pendingTransfer'.freeze # "pendingUpdate" status MUST NOT be combined with either # "clientUpdateProhibited" or "serverUpdateProhibited" status. - PENDING_UPDATE = 'pendingUpdate' + PENDING_UPDATE = 'pendingUpdate'.freeze # "pendingDelete" MUST NOT be combined with either # "clientDeleteProhibited" or "serverDeleteProhibited" status. - PENDING_DELETE = 'pendingDelete' + PENDING_DELETE = 'pendingDelete'.freeze STATUSES = [ CLIENT_DELETE_PROHIBITED, SERVER_DELETE_PROHIBITED, @@ -146,18 +146,18 @@ class Contact < ApplicationRecord SERVER_TRANSFER_PROHIBITED, CLIENT_UPDATE_PROHIBITED, SERVER_UPDATE_PROHIBITED, OK, PENDING_CREATE, PENDING_DELETE, PENDING_TRANSFER, PENDING_UPDATE, LINKED - ] + ].freeze CLIENT_STATUSES = [ CLIENT_DELETE_PROHIBITED, CLIENT_TRANSFER_PROHIBITED, CLIENT_UPDATE_PROHIBITED - ] + ].freeze SERVER_STATUSES = [ SERVER_UPDATE_PROHIBITED, SERVER_DELETE_PROHIBITED, - SERVER_TRANSFER_PROHIBITED - ] + SERVER_TRANSFER_PROHIBITED, + ].freeze # # END OF STATUSES # @@ -355,7 +355,7 @@ class Contact < ApplicationRecord @desc[dom.name][:roles] << :registrant end - domain_contacts.each do |dc| + domain_contacts.includes(:domain).each do |dc| @desc[dc.domain.name] ||= { id: dc.domain.uuid, roles: [] } @desc[dc.domain.name][:roles] << dc.name.downcase.to_sym @desc[dc.domain.name] = @desc[dc.domain.name].compact @@ -383,6 +383,10 @@ class Contact < ApplicationRecord "#{code} #{name}" end + def name_disclosed_by_registrar(reg_id) + registrar_id == reg_id ? name : 'N/A' + end + def strip_email self.email = email.to_s.strip end @@ -405,7 +409,7 @@ class Contact < ApplicationRecord # using small rails hack to generate outer join domains = if sorts.first == 'registrar_name'.freeze - domains.includes(:registrar).where.not(registrars: { id: nil }) + domains.where.not(registrars: { id: nil }) .order("registrars.name #{order} NULLS LAST") else domains.order("#{sort} #{order} NULLS LAST") @@ -422,7 +426,6 @@ class Contact < ApplicationRecord end domains.each { |d| d.roles = domain_c[d.id].uniq } - domains end @@ -438,18 +441,28 @@ class Contact < ApplicationRecord end end - def qualified_domain_ids(domain_filter) - registrant_ids = registrant_domains.pluck(:id) - return registrant_ids if domain_filter == 'Registrant' + def qualified_domain_ids(filters) + rant_domains = registrant_domains.map { |d| { id: d.id, type: ['Registrant'] } } + contact_domains = domain_contacts.map { |dc| { id: dc.domain_id, type: [dc.type] } } + grouped_domains = group_by_id_and_type(rant_domains + contact_domains) + return grouped_domains.keys if filters.nil? || filters == '' - if %w[AdminDomainContact TechDomainContact].include? domain_filter - DomainContact.select('domain_id').where(contact_id: id, type: domain_filter) - else - (DomainContact.select('domain_id').where(contact_id: id).pluck(:domain_id) + - registrant_ids).uniq - end + # use domain_filters.sort == v.sort if should be exact match + grouped_domains.reject { |_, v| ([].push(filters).flatten & v).empty? }.keys end + # def qualified_domain_ids(domain_filter) + # registrant_ids = registrant_domains.pluck(:id) + # return registrant_ids if domain_filter == 'Registrant' + + # if %w[AdminDomainContact TechDomainContact].include? domain_filter + # DomainContact.where(contact_id: id, type: domain_filter).pluck(:domain_id) + # else + # (DomainContact.where(contact_id: id).pluck(:domain_id) + + # registrant_ids).uniq + # end + # end + def update_prohibited? (statuses & [ CLIENT_UPDATE_PROHIBITED, @@ -459,7 +472,7 @@ class Contact < ApplicationRecord PENDING_CREATE, PENDING_TRANSFER, PENDING_UPDATE, - PENDING_DELETE + PENDING_DELETE, ]).present? end @@ -590,4 +603,14 @@ class Contact < ApplicationRecord def self.csv_header ['Name', 'ID', 'Ident', 'E-mail', 'Created at', 'Registrar', 'Phone'] end + + private + + def group_by_id_and_type(domains_hash_array) + domains_hash_array.group_by { |d| d[:id] } + .transform_values do |v| + v.each.with_object(:type) + .map(&:[]).flatten + end + end end diff --git a/app/models/contact_update_action.rb b/app/models/contact_update_action.rb new file mode 100644 index 000000000..4e7444948 --- /dev/null +++ b/app/models/contact_update_action.rb @@ -0,0 +1 @@ +class ContactUpdateAction < Action; end diff --git a/app/models/deposit.rb b/app/models/deposit.rb index 5943f1540..711d59d72 100644 --- a/app/models/deposit.rb +++ b/app/models/deposit.rb @@ -33,6 +33,7 @@ class Deposit def issue_prepayment_invoice return unless valid? + registrar.issue_prepayment_invoice(amount, description) end end diff --git a/app/models/depp/user.rb b/app/models/depp/user.rb index 60c6f6c3d..36fc48fdc 100644 --- a/app/models/depp/user.rb +++ b/app/models/depp/user.rb @@ -31,8 +31,8 @@ module Depp def request(xml) Nokogiri::XML(server.request(xml)).remove_namespaces! - rescue EppErrorResponse => e - Nokogiri::XML(e.response_xml.to_s).remove_namespaces! + rescue EppErrorResponse => e + Nokogiri::XML(e.response_xml.to_s).remove_namespaces! end private diff --git a/app/models/domain_transfer.rb b/app/models/domain_transfer.rb index 02ab2bc88..ff9e55276 100644 --- a/app/models/domain_transfer.rb +++ b/app/models/domain_transfer.rb @@ -4,10 +4,10 @@ class DomainTransfer < ApplicationRecord belongs_to :old_registrar, class_name: 'Registrar' belongs_to :new_registrar, class_name: 'Registrar' - PENDING = 'pending' - CLIENT_APPROVED = 'clientApproved' - CLIENT_REJECTED = 'clientRejected' - SERVER_APPROVED = 'serverApproved' + PENDING = 'pending'.freeze + CLIENT_APPROVED = 'clientApproved'.freeze + CLIENT_REJECTED = 'clientRejected'.freeze + SERVER_APPROVED = 'serverApproved'.freeze before_create :set_wait_until diff --git a/app/models/invoice.rb b/app/models/invoice.rb index 66d3faf86..b7e60abfb 100644 --- a/app/models/invoice.rb +++ b/app/models/invoice.rb @@ -180,6 +180,13 @@ class Invoice < ApplicationRecord private + ransacker :number_str do + Arel.sql( + "regexp_replace( + to_char(\"#{table_name}\".\"number\", '999999999999'), ' ', '', 'g')" + ) + end + def receipt_date_status if paid? receipt_date diff --git a/app/models/notification.rb b/app/models/notification.rb index c9af66c56..8cb4335a2 100644 --- a/app/models/notification.rb +++ b/app/models/notification.rb @@ -1,5 +1,6 @@ class Notification < ApplicationRecord include Versions # version/notification_version.rb + include EppErrors belongs_to :registrar belongs_to :action, optional: true diff --git a/app/models/registrar.rb b/app/models/registrar.rb index caefaddd6..1eba314dc 100644 --- a/app/models/registrar.rb +++ b/app/models/registrar.rb @@ -97,7 +97,7 @@ class Registrar < ApplicationRecord description: 'prepayment', unit: 'piece', quantity: 1, - price: amount + price: amount, } ] ) @@ -233,13 +233,9 @@ class Registrar < ApplicationRecord def notify(action) text = I18n.t("notifications.texts.#{action.notification_key}", contact: action.contact&.code, count: action.subactions&.count) - if action.bulk_action? - notifications.create!(text: text, action_id: action.id, - attached_obj_type: 'BulkAction', - attached_obj_id: action.id) - else - notifications.create!(text: text) - end + notifications.create!(text: text, action_id: action.id, + attached_obj_type: 'ContactUpdateAction', + attached_obj_id: action.id) end def e_invoice_iban diff --git a/app/models/tech_domain_contact.rb b/app/models/tech_domain_contact.rb index eff815350..30db6dec7 100644 --- a/app/models/tech_domain_contact.rb +++ b/app/models/tech_domain_contact.rb @@ -5,7 +5,7 @@ class TechDomainContact < DomainContact skipped_domains = [] tech_contacts = where(contact: current_contact) - tech_contacts.each do |tech_contact| + tech_contacts.includes(:domain).each do |tech_contact| if irreplaceable?(tech_contact) skipped_domains << tech_contact.domain.name next diff --git a/app/models/user.rb b/app/models/user.rb index cca07ca14..3bb8318c3 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -16,6 +16,6 @@ class User < ApplicationRecord identity_code = uid.slice(2..-1) # country_code = uid.slice(0..1) - find_by(identity_code: identity_code) + find_by(identity_code: identity_code, active: true) end end diff --git a/app/presenters/registrar/domain_list_csv_presenter.rb b/app/presenters/registrar/domain_list_csv_presenter.rb index e38f3f54e..a216d9561 100644 --- a/app/presenters/registrar/domain_list_csv_presenter.rb +++ b/app/presenters/registrar/domain_list_csv_presenter.rb @@ -17,13 +17,13 @@ class Registrar::DomainListCsvPresenter private def header - columns = %w( + columns = %w[ domain_name transfer_code registrant_name registrant_code expire_time - ) + ] columns.map! { |column| view.t("registrar.domains.index.csv.#{column}") } @@ -37,7 +37,6 @@ class Registrar::DomainListCsvPresenter row[2] = domain.registrant.name row[3] = domain.registrant.code row[4] = domain.expire_date - row CSV::Row.new([], row) end diff --git a/app/views/epp/poll/poll_req.xml.builder b/app/views/epp/poll/poll_req.xml.builder index 0a916e6ad..373b8194b 100644 --- a/app/views/epp/poll/poll_req.xml.builder +++ b/app/views/epp/poll/poll_req.xml.builder @@ -15,7 +15,7 @@ xml.epp_head do xml.resData do xml << render('epp/domains/partials/transfer', builder: xml, dt: @object) end - when 'BulkAction' + when 'ContactUpdateAction' xml.resData do xml << render( 'epp/contacts/partials/check', diff --git a/app/views/registrar/domains/_search_form.html.erb b/app/views/registrar/domains/_search_form.html.erb index e9e5b5e1a..584b15ba1 100644 --- a/app/views/registrar/domains/_search_form.html.erb +++ b/app/views/registrar/domains/_search_form.html.erb @@ -44,7 +44,7 @@
- <%= f.label :valid_to_from, for: nil %> + <%= f.label :valid_to_gteq, for: nil %> <%= f.search_field :valid_to_gteq, value: search_params[:valid_to_gteq], class: 'form-control js-datepicker', placeholder: t(:valid_to_from) %> @@ -53,7 +53,7 @@
- <%= f.label :valid_to_until, for: nil %> + <%= f.label :valid_to_lteq, for: nil %> <%= f.search_field :valid_to_lteq, value: search_params[:valid_to_lteq], class: 'form-control js-datepicker', placeholder: t(:valid_to_until) %> diff --git a/app/views/registrar/polls/show.haml b/app/views/registrar/polls/show.haml index 4ff116b81..c97d7a5d5 100644 --- a/app/views/registrar/polls/show.haml +++ b/app/views/registrar/polls/show.haml @@ -27,10 +27,11 @@ = form_tag confirm_transfer_registrar_poll_path, class: 'js-transfer-form' do = hidden_field_tag 'domain[name]', @data.css('name').text - - @data.css('trnData').children.each do |x| - - next if x.blank? - %dt= t(x.name) - %dd= x.text + - @data.css('trnData').children.each do |x| + - next if x.blank? + %dt= t(x.name) + %dd= x.text + - else .row .col-sm-12 diff --git a/config/initializers/arel.rb b/config/initializers/arel.rb new file mode 100644 index 000000000..9db1facf3 --- /dev/null +++ b/config/initializers/arel.rb @@ -0,0 +1,25 @@ +require 'arel/nodes/binary' +require 'arel/predications' +require 'arel/visitors/postgresql' + +module Arel + class Nodes::ContainsArray < Arel::Nodes::Binary + def operator + :"@>" + end + end + + class Visitors::PostgreSQL + private + + def visit_Arel_Nodes_ContainsArray(o, collector) + infix_value o, collector, ' @> ' + end + end + + module Predications + def contains_array(other) + Nodes::ContainsArray.new self, Nodes.build_quoted(other, self) + end + end +end \ No newline at end of file diff --git a/config/initializers/omniauth.rb b/config/initializers/omniauth.rb index e3e0d644b..2690160ef 100644 --- a/config/initializers/omniauth.rb +++ b/config/initializers/omniauth.rb @@ -15,62 +15,67 @@ host = ENV['tara_host'] identifier = ENV['tara_identifier'] secret = ENV['tara_secret'] redirect_uri = ENV['tara_redirect_uri'] +authorization_endpoint = ENV['tara_authorization_endpoint'] +token_endpoint = ENV['tara_token_endpoint'] +jwks_uri = ENV['tara_jwks_uri'] +scope = ENV['tara_scope'] registrant_identifier = ENV['tara_rant_identifier'] registrant_secret = ENV['tara_rant_secret'] registrant_redirect_uri = ENV['tara_rant_redirect_uri'] Rails.application.config.middleware.use OmniAuth::Builder do - provider "tara", { - callback_path: '/registrar/open_id/callback', - name: 'tara', - scope: ['openid'], - state: Proc.new{ SecureRandom.hex(10) }, - client_signing_alg: :RS256, - client_jwk_signing_key: signing_keys, - send_scope_to_token_endpoint: false, - send_nonce: true, - issuer: issuer, + provider 'tara', { + callback_path: '/registrar/open_id/callback', + name: 'tara', + scope: scope, + # state: Proc.new{ SecureRandom.hex(10) }, + client_signing_alg: :RS256, + client_jwk_signing_key: signing_keys, + send_scope_to_token_endpoint: false, + send_nonce: true, + issuer: issuer, + discovery: true, - client_options: { - scheme: 'https', - host: host, + client_options: { + scheme: 'https', + host: host, - authorization_endpoint: '/oidc/authorize', - token_endpoint: '/oidc/token', - userinfo_endpoint: nil, # Not implemented - jwks_uri: '/oidc/jwks', + authorization_endpoint: authorization_endpoint, + token_endpoint: token_endpoint, + userinfo_endpoint: nil, # Not implemented + jwks_uri: jwks_uri, - # Registry - identifier: identifier, - secret: secret, - redirect_uri: redirect_uri, - }, + # Registry + identifier: identifier, + secret: secret, + redirect_uri: redirect_uri, + }, } - provider "tara", { - callback_path: '/registrant/open_id/callback', - name: 'rant_tara', - scope: ['openid'], - client_signing_alg: :RS256, - client_jwk_signing_key: signing_keys, - send_scope_to_token_endpoint: false, - send_nonce: true, - issuer: issuer, + provider 'tara', { + callback_path: '/registrant/open_id/callback', + name: 'rant_tara', + scope: ['openid'], + client_signing_alg: :RS256, + client_jwk_signing_key: signing_keys, + send_scope_to_token_endpoint: false, + send_nonce: true, + issuer: issuer, - client_options: { - scheme: 'https', - host: host, + client_options: { + scheme: 'https', + host: host, - authorization_endpoint: '/oidc/authorize', - token_endpoint: '/oidc/token', - userinfo_endpoint: nil, # Not implemented - jwks_uri: '/oidc/jwks', + authorization_endpoint: '/oidc/authorize', + token_endpoint: '/oidc/token', + userinfo_endpoint: nil, # Not implemented + jwks_uri: '/oidc/jwks', - # Registry - identifier: registrant_identifier, - secret: registrant_secret, - redirect_uri: registrant_redirect_uri, - }, + # Registry + identifier: registrant_identifier, + secret: registrant_secret, + redirect_uri: registrant_redirect_uri, + }, } end diff --git a/config/initializers/ransack.rb b/config/initializers/ransack.rb new file mode 100644 index 000000000..d26360672 --- /dev/null +++ b/config/initializers/ransack.rb @@ -0,0 +1,7 @@ +Ransack.configure do |config| + config.add_predicate 'contains_array', + arel_predicate: 'contains_array', + formatter: proc { |v| "{#{v}}" }, + validator: proc { |v| v.present? }, + type: :string +end \ No newline at end of file diff --git a/config/locales/en.yml b/config/locales/en.yml index 9c396cbde..ec8953a84 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -101,6 +101,11 @@ en: attributes: value: taken: 'Status already exists on this domain' + + invoice: + attributes: + base: + invoice_status_prohibits_operation: 'Invoice status prohibits operation' user: attributes: diff --git a/config/routes.rb b/config/routes.rb index 8fd80f923..6f336f4d4 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -71,12 +71,27 @@ Rails.application.routes.draw do resources :contacts do collection do get 'check/:id', to: 'contacts#check' + get 'search(/:id)', to: 'contacts#search' end end - resources :accounts do + resource :account, controller: :account, only: %i[index update] do collection do + get '/', to: 'account#index' get 'balance' + get 'details' + post 'update_auto_reload_balance' + get 'disable_auto_reload_balance' + end + end + resources :invoices, only: %i[index show] do + collection do + get ':id/download', to: 'invoices#download' + get ':id/cancel', to: 'invoices#cancel' + post 'add_credit' + end + member do + post 'send_to_recipient', to: 'invoices#send_to_recipient' end end resources :auctions, only: %i[index] @@ -98,6 +113,13 @@ Rails.application.routes.draw do put '/', to: 'nameservers#update' end end + resources :summary, only: %i[index] + resources :auth, only: %i[index] do + collection do + post '/tara_callback', to: 'auth#tara_callback' + put '/switch_user', to: 'auth#switch_user' + end + end end resources :domains, constraints: { id: /.*/ } do resources :nameservers, only: %i[index create destroy], constraints: { id: /.*/ }, controller: 'domains/nameservers' @@ -146,9 +168,9 @@ Rails.application.routes.draw do namespace :accreditation_center do # At the moment invoice_status endpoint returns only cancelled invoices. But in future logic of this enpoint can change. # And it will need to return invoices of different statuses. I decided to leave the name of the endpoint "invoice_status" - resources :invoice_status, only: [ :index ] - resource :domains, only: [ :show ], param: :name - resource :contacts, only: [ :show ], param: :id + resources :invoice_status, only: [:index] + resource :domains, only: [:show], param: :name + resource :contacts, only: [:show], param: :id # resource :auth, only: [ :index ] get 'auth', to: 'auth#index' end @@ -159,7 +181,7 @@ Rails.application.routes.draw do end match '*all', controller: 'cors', action: 'cors_preflight_check', via: [:options], - as: 'cors_preflight_check' + as: 'cors_preflight_check' end # REGISTRAR ROUTES diff --git a/lib/serializers/repp/contact.rb b/lib/serializers/repp/contact.rb index b5d03b5cd..c36fa3258 100644 --- a/lib/serializers/repp/contact.rb +++ b/lib/serializers/repp/contact.rb @@ -3,22 +3,32 @@ module Serializers class Contact attr_reader :contact - def initialize(contact, show_address:) + def initialize(contact, options = {}) @contact = contact - @show_address = show_address + @show_address = options[:show_address] + @domain_params = options[:domain_params] || nil + @simplify = options[:simplify] || false end def to_json(obj = contact) - json = { id: obj.code, name: obj.name, ident: ident, - email: obj.email, phone: obj.phone, - auth_info: obj.auth_info, statuses: obj.statuses, - disclosed_attributes: obj.disclosed_attributes } + return simple_object if @simplify + json = { id: obj.uuid, code: obj.code, name: obj.name, ident: ident, + email: obj.email, phone: obj.phone, created_at: obj.created_at, + auth_info: obj.auth_info, statuses: statuses, + disclosed_attributes: obj.disclosed_attributes, registrar: registrar } json[:address] = address if @show_address - + if @domain_params + json[:domains] = domains + json[:domains_count] = obj.qualified_domain_ids(@domain_params[:domain_filter]).size + end json end + def registrar + contact.registrar.as_json(only: %i[name website]) + end + def ident { code: contact.ident, @@ -31,6 +41,34 @@ module Serializers { street: contact.street, zip: contact.zip, city: contact.city, state: contact.state, country_code: contact.country_code } end + + def domains + contact.all_domains(page: @domain_params[:page], + per: @domain_params[:per_page], + params: @domain_params) + .map do |d| + { id: d.uuid, name: d.name, registrar: { name: d.registrar.name }, + valid_to: d.valid_to, roles: d.roles } + end + end + + def statuses + statuses_with_notes = contact.status_notes + contact.statuses.each do |status| + statuses_with_notes.merge!({ "#{status}": '' }) unless statuses_with_notes.key?(status) + end + statuses_with_notes + end + + private + + def simple_object + { + id: contact.uuid, + code: contact.code, + name: contact.name, + } + end end end end diff --git a/lib/serializers/repp/domain.rb b/lib/serializers/repp/domain.rb index d365859c1..07bb95e36 100644 --- a/lib/serializers/repp/domain.rb +++ b/lib/serializers/repp/domain.rb @@ -3,19 +3,25 @@ module Serializers class Domain attr_reader :domain - def initialize(domain, sponsored: true) + def initialize(domain, sponsored: true, simplify: false) @domain = domain @sponsored = sponsored + @simplify = simplify end # rubocop:disable Metrics/AbcSize def to_json(obj = domain) + return simple_object if @simplify + json = { - name: obj.name, registrant: obj.registrant.code, created_at: obj.created_at, - updated_at: obj.updated_at, expire_time: obj.expire_time, outzone_at: obj.outzone_at, - delete_date: obj.delete_date, force_delete_date: obj.force_delete_date, - contacts: contacts, nameservers: nameservers, dnssec_keys: dnssec_keys, - statuses: obj.status_notes, registrar: registrar + id: obj.uuid, name: obj.name, registrant: registrant, + created_at: obj.created_at, updated_at: obj.updated_at, + expire_time: obj.expire_time, + outzone_at: obj.outzone_at, delete_date: obj.delete_date, + force_delete_date: obj.force_delete_date, contacts: contacts, + nameservers: nameservers, dnssec_keys: dnssec_keys, + statuses: statuses, registrar: registrar, + dispute: Dispute.active.exists?(domain_name: obj.name) } json[:transfer_code] = obj.auth_info if @sponsored json @@ -23,22 +29,54 @@ module Serializers # rubocop:enable Metrics/AbcSize def contacts - domain.domain_contacts.map { |c| { code: c.contact.code, type: c.type } } - end - - def nameservers - domain.nameservers.map { |ns| { hostname: ns.hostname, ipv4: ns.ipv4, ipv6: ns.ipv6 } } - end - - def dnssec_keys - domain.dnskeys.map do |nssec| - { flags: nssec.flags, protocol: nssec.protocol, alg: nssec.alg, - public_key: nssec.public_key } + domain.domain_contacts.includes(:contact).map do |dc| + contact = dc.contact + { code: contact.code, type: dc.type, + name: contact.name_disclosed_by_registrar(domain.registrar_id) } end end + def nameservers + domain.nameservers.order(:created_at).as_json(only: %i[id hostname ipv4 ipv6]) + end + + def dnssec_keys + domain.dnskeys.order(:updated_at).as_json(only: %i[id flags protocol alg public_key]) + end + def registrar - { name: domain.registrar.name, website: domain.registrar.website } + domain.registrar.as_json(only: %i[name website]) + end + + def registrant + rant = domain.registrant + { + id: rant.uuid, + name: rant.name, + code: rant.code, + } + end + + def statuses + statuses_with_notes = domain.status_notes + domain.statuses.each do |status| + statuses_with_notes.merge!({ "#{status}": '' }) unless statuses_with_notes.key?(status) + end + statuses_with_notes + end + + private + + def simple_object + json = { + id: domain.uuid, + name: domain.name, + expire_time: domain.expire_time, + registrant: registrant, + statuses: statuses, + } + json[:transfer_code] = domain.auth_info if @sponsored + json end end end diff --git a/lib/serializers/repp/invoice.rb b/lib/serializers/repp/invoice.rb new file mode 100644 index 000000000..9bc7431b2 --- /dev/null +++ b/lib/serializers/repp/invoice.rb @@ -0,0 +1,85 @@ +module Serializers + module Repp + class Invoice + attr_reader :invoice + + def initialize(invoice, simplify: false) + @invoice = invoice + @simplify = simplify + end + + def to_json(obj = invoice) + return simple_object if @simplify + + { + id: obj.id, issue_date: obj.issue_date, cancelled_at: obj.cancelled_at, + paid: obj.paid?, payable: obj.payable?, cancellable: invoice.cancellable?, + receipt_date: obj.receipt_date, payment_link: obj.payment_link, + number: obj.number, subtotal: obj.subtotal, vat_amount: obj.vat_amount, + vat_rate: obj.vat_rate, total: obj.total, + description: obj.description, reference_no: obj.reference_no, + created_at: obj.created_at, updated_at: obj.updated_at, + due_date: obj.due_date, currency: obj.currency, + seller: seller, buyer: buyer, items: items, + recipient: obj.buyer.billing_email + } + end + + private + + def seller + { + name: invoice.seller_name, + reg_no: invoice.seller_reg_no, + iban: invoice.seller_iban, + bank: invoice.seller_bank, + swift: invoice.seller_swift, + vat_no: invoice.seller_vat_no, + address: invoice.seller_address, + country: invoice.seller_country.name, + phone: invoice.seller_phone, + url: invoice.seller_url, + email: invoice.seller_email, + contact_name: invoice.seller_contact_name, + } + end + + def buyer + { + name: invoice.buyer_name, + reg_no: invoice.buyer_reg_no, + address: invoice.buyer_address, + country: invoice.buyer_country.name, + phone: invoice.buyer_phone, + url: invoice.buyer_url, + email: invoice.buyer_email, + } + end + + def items + invoice.items.map do |item| + { description: item.description, unit: item.unit, + quantity: item.quantity, price: item.price, + sum_without_vat: item.item_sum_without_vat, + vat_amount: item.vat_amount, total: item.total } + end + end + + def simple_object + { + id: invoice.id, + number: invoice.number, + paid: invoice.paid?, + payable: invoice.payable?, + payment_link: invoice.payment_link, + receipt_date: invoice.receipt_date, + cancelled: invoice.cancelled?, + cancellable: invoice.cancellable?, + due_date: invoice.due_date, + total: invoice.total, + recipient: invoice.buyer.billing_email, + } + end + end + end +end diff --git a/test/integration/epp/poll_test.rb b/test/integration/epp/poll_test.rb index 29c24af26..7b114f7c0 100644 --- a/test/integration/epp/poll_test.rb +++ b/test/integration/epp/poll_test.rb @@ -56,7 +56,7 @@ class EppPollTest < EppTestCase bulk_action = actions(:contacts_update_bulk_action) @notification.update!(action: bulk_action, attached_obj_id: bulk_action.id, - attached_obj_type: 'BulkAction') + attached_obj_type: 'ContactUpdateAction') post epp_poll_path, params: { frame: request_req_xml }, headers: { 'HTTP_COOKIE' => 'session=api_bestnames' } diff --git a/test/integration/repp/v1/accounts/balance_test.rb b/test/integration/repp/v1/account/balance_test.rb similarity index 88% rename from test/integration/repp/v1/accounts/balance_test.rb rename to test/integration/repp/v1/account/balance_test.rb index 4b711bd05..a8416be0a 100644 --- a/test/integration/repp/v1/accounts/balance_test.rb +++ b/test/integration/repp/v1/account/balance_test.rb @@ -11,7 +11,7 @@ class ReppV1BalanceTest < ActionDispatch::IntegrationTest end def test_can_query_balance - get '/repp/v1/accounts/balance', headers: @auth_headers + get '/repp/v1/account/balance', headers: @auth_headers json = JSON.parse(response.body, symbolize_names: true) assert_response :ok @@ -28,7 +28,7 @@ class ReppV1BalanceTest < ActionDispatch::IntegrationTest started_from = "2010-07-05" end_to = DateTime.current.to_date.to_s(:db) - get "/repp/v1/accounts/balance?detailed=true", headers: @auth_headers + get "/repp/v1/account/balance?detailed=true", headers: @auth_headers json = JSON.parse(response.body, symbolize_names: true) assert_response :ok @@ -44,8 +44,8 @@ class ReppV1BalanceTest < ActionDispatch::IntegrationTest assert_equal @registrar.registrar.cash_account.account_activities.last.new_balance.to_s, entry[:balance] json[:data][:transactions].map do |trans| - assert trans[:created_at].to_date.to_s(:db) >= started_from - assert trans[:created_at].to_date.to_s(:db) >= end_to + assert trans[:created_at].to_date.to_s(:db) >= started_from + assert trans[:created_at].to_date.to_s(:db) >= end_to end end end diff --git a/test/integration/repp/v1/contacts/create_test.rb b/test/integration/repp/v1/contacts/create_test.rb index f30bc368f..af1ca0fbf 100644 --- a/test/integration/repp/v1/contacts/create_test.rb +++ b/test/integration/repp/v1/contacts/create_test.rb @@ -11,16 +11,16 @@ class ReppV1ContactsCreateTest < ActionDispatch::IntegrationTest def test_creates_new_contact request_body = { - "contact": { - "name": "Donald Trump", - "phone": "+372.51111112", - "email": "donald@trumptower.com", - "ident": { - "ident_type": "priv", - "ident_country_code": "EE", - "ident": "39708290069" - } - } + contact: { + name: 'Donald Trump', + phone: '+372.51111112', + email: 'donald@trumptower.com', + ident: { + ident_type: 'priv', + ident_country_code: 'EE', + ident: '39708290069', + }, + }, } post '/repp/v1/contacts', headers: @auth_headers, params: request_body @@ -30,7 +30,7 @@ class ReppV1ContactsCreateTest < ActionDispatch::IntegrationTest assert_equal 1000, json[:code] assert_equal 'Command completed successfully', json[:message] - contact = Contact.find_by(code: json[:data][:contact][:id]) + contact = Contact.find_by(code: json[:data][:contact][:code]) assert contact.present? assert_equal(request_body[:contact][:name], contact.name) @@ -42,21 +42,21 @@ class ReppV1ContactsCreateTest < ActionDispatch::IntegrationTest end def test_removes_postal_info_when_contact_created - request_body = { - "contact": { - "name": "Donald Trump", - "phone": "+372.51111111", - "email": "donald@trump.com", - "ident": { - "ident_type": "priv", - "ident_country_code": "EE", - "ident": "39708290069" + request_body = { + contact: { + name: 'Donald Trump', + phone: '+372.51111111', + email: 'donald@trump.com', + ident: { + ident_type: 'priv', + ident_country_code: 'EE', + ident: '39708290069', }, - "addr": { - "city": "Tallinn", - "street": "Wismari 13", - "zip": "12345", - "country_code": "EE" + addr: { + city: 'Tallinn', + street: 'Wismari 13', + zip: '12345', + country_code: 'EE', } } } @@ -68,7 +68,7 @@ class ReppV1ContactsCreateTest < ActionDispatch::IntegrationTest assert_equal 1100, json[:code] assert_equal 'Command completed successfully; Postal address data discarded', json[:message] - contact = Contact.find_by(code: json[:data][:contact][:id]) + contact = Contact.find_by(code: json[:data][:contact][:code]) assert contact.present? assert_nil contact.city @@ -126,21 +126,21 @@ class ReppV1ContactsCreateTest < ActionDispatch::IntegrationTest end def test_attaches_legaldoc_if_present - request_body = { - "contact": { - "name": "Donald Trump", - "phone": "+372.51111112", - "email": "donald@trumptower.com", - "ident": { - "ident_type": "priv", - "ident_country_code": "EE", - "ident": "39708290069" + request_body = { + contact: { + name: 'Donald Trump', + phone: '+372.51111112', + email: 'donald@trumptower.com', + ident: { + ident_type: 'priv', + ident_country_code: 'EE', + ident: '39708290069', + }, + legal_document: { + type: 'pdf', + body: ('test' * 2000).to_s, }, }, - "legal_document": { - "type": "pdf", - "body": "#{'test' * 2000}" - } } post '/repp/v1/contacts', headers: @auth_headers, params: request_body @@ -150,7 +150,7 @@ class ReppV1ContactsCreateTest < ActionDispatch::IntegrationTest assert_equal 1000, json[:code] assert_equal 'Command completed successfully', json[:message] - contact = Contact.find_by(code: json[:data][:contact][:id]) + contact = Contact.find_by(code: json[:data][:contact][:code]) assert contact.legal_documents.any? end end diff --git a/test/integration/repp/v1/contacts/list_test.rb b/test/integration/repp/v1/contacts/list_test.rb index 31c4baaf9..6cc3e4669 100644 --- a/test/integration/repp/v1/contacts/list_test.rb +++ b/test/integration/repp/v1/contacts/list_test.rb @@ -12,13 +12,13 @@ class ReppV1ContactsListTest < ActionDispatch::IntegrationTest def test_returns_registrar_contacts get repp_v1_contacts_path, headers: @auth_headers json = JSON.parse(response.body, symbolize_names: true) - + assert_response :ok - assert_equal @user.registrar.contacts.count, json[:total_number_of_records] - assert_equal @user.registrar.contacts.count, json[:contacts].length + assert_equal @user.registrar.contacts.count, json[:data][:count] + assert_equal @user.registrar.contacts.count, json[:data][:contacts].length - assert json[:contacts][0].is_a? String + assert json[:data][:contacts][0].is_a? String end @@ -28,10 +28,10 @@ class ReppV1ContactsListTest < ActionDispatch::IntegrationTest assert_response :ok - assert_equal @user.registrar.contacts.count, json[:total_number_of_records] - assert_equal @user.registrar.contacts.count, json[:contacts].length + assert_equal @user.registrar.contacts.count, json[:data][:count] + assert_equal @user.registrar.contacts.count, json[:data][:contacts].length - assert json[:contacts][0].is_a? Hash + assert json[:data][:contacts][0].is_a? Hash end def test_respects_limit @@ -40,7 +40,7 @@ class ReppV1ContactsListTest < ActionDispatch::IntegrationTest assert_response :ok - assert_equal 2, json[:contacts].length + assert_equal 2, json[:data][:contacts].length end def test_respects_offset @@ -50,6 +50,6 @@ class ReppV1ContactsListTest < ActionDispatch::IntegrationTest assert_response :ok - assert_equal (@user.registrar.contacts.count - offset), json[:contacts].length + assert_equal (@user.registrar.contacts.count - offset), json[:data][:contacts].length end end diff --git a/test/integration/repp/v1/contacts/show_test.rb b/test/integration/repp/v1/contacts/show_test.rb index 4a6f5b615..496935ab6 100644 --- a/test/integration/repp/v1/contacts/show_test.rb +++ b/test/integration/repp/v1/contacts/show_test.rb @@ -28,7 +28,7 @@ class ReppV1ContactsShowTest < ActionDispatch::IntegrationTest assert_equal 1000, json[:code] assert_equal 'Command completed successfully', json[:message] - assert_equal contact.code, json[:data][:id] + assert_equal contact.code, json[:data][:contact][:code] end def test_can_not_access_out_of_scope_contacts diff --git a/test/integration/repp/v1/contacts/update_test.rb b/test/integration/repp/v1/contacts/update_test.rb index cf27f98da..e75ce4188 100644 --- a/test/integration/repp/v1/contacts/update_test.rb +++ b/test/integration/repp/v1/contacts/update_test.rb @@ -24,14 +24,14 @@ class ReppV1ContactsUpdateTest < ActionDispatch::IntegrationTest assert_equal 1000, json[:code] assert_equal 'Command completed successfully', json[:message] - contact = Contact.find_by(code: json[:data][:contact][:id]) + contact = Contact.find_by(code: json[:data][:contact][:code]) assert contact.present? assert_equal(request_body[:contact][:email], contact.email) end def test_removes_postal_info_when_updated - request_body = { + request_body = { "contact": { "addr": { "city": "Tallinn", @@ -49,7 +49,7 @@ class ReppV1ContactsUpdateTest < ActionDispatch::IntegrationTest assert_equal 1100, json[:code] assert_equal 'Command completed successfully; Postal address data discarded', json[:message] - contact = Contact.find_by(code: json[:data][:contact][:id]) + contact = Contact.find_by(code: json[:data][:contact][:code]) assert contact.present? assert_nil contact.city @@ -81,14 +81,14 @@ class ReppV1ContactsUpdateTest < ActionDispatch::IntegrationTest end def test_attaches_legaldoc_if_present - request_body = { - "contact": { - "email": "donaldtrump@yandex.ru" + request_body = { + contact: { + email: 'donaldtrump@yandex.ru', + legal_document: { + type: 'pdf', + body: ('test' * 2000).to_s, + }, }, - "legal_document": { - "type": "pdf", - "body": "#{'test' * 2000}" - } } put "/repp/v1/contacts/#{@contact.code}", headers: @auth_headers, params: request_body @@ -103,9 +103,11 @@ class ReppV1ContactsUpdateTest < ActionDispatch::IntegrationTest end def test_returns_error_if_ident_wrong_format - request_body = { - "contact": { - "ident": "123" + request_body = { + contact: { + ident: { + ident: '123', + } } } diff --git a/test/integration/repp/v1/domains/contact_replacement_test.rb b/test/integration/repp/v1/domains/contact_replacement_test.rb index 3cbd9eb8e..65ddb4c9d 100644 --- a/test/integration/repp/v1/domains/contact_replacement_test.rb +++ b/test/integration/repp/v1/domains/contact_replacement_test.rb @@ -15,7 +15,7 @@ class ReppV1DomainsContactReplacementTest < ActionDispatch::IntegrationTest payload = { "current_contact_id": replaceable_contact.code, - "new_contact_id": replacing_contact.code + "new_contact_id": replacing_contact.code, } patch '/repp/v1/domains/contacts', headers: @auth_headers, params: payload @@ -37,7 +37,7 @@ class ReppV1DomainsContactReplacementTest < ActionDispatch::IntegrationTest payload = { "current_contact_id": replaceable_contact.code, - "new_contact_id": replacing_contact.code + "new_contact_id": replacing_contact.code, } patch '/repp/v1/domains/contacts', headers: @auth_headers, params: payload @@ -51,7 +51,7 @@ class ReppV1DomainsContactReplacementTest < ActionDispatch::IntegrationTest def test_contact_codes_must_be_valid payload = { "current_contact_id": 'dfgsdfg', - "new_contact_id": 'vvv' + "new_contact_id": 'vvv', } patch '/repp/v1/domains/contacts', headers: @auth_headers, params: payload @@ -61,5 +61,4 @@ class ReppV1DomainsContactReplacementTest < ActionDispatch::IntegrationTest assert_equal 2303, json[:code] assert_equal 'Object does not exist', json[:message] end - end diff --git a/test/integration/repp/v1/domains/delete_test.rb b/test/integration/repp/v1/domains/delete_test.rb index 08b73e832..818815473 100644 --- a/test/integration/repp/v1/domains/delete_test.rb +++ b/test/integration/repp/v1/domains/delete_test.rb @@ -15,9 +15,11 @@ class ReppV1DomainsDeleteTest < ActionDispatch::IntegrationTest @auth_headers['Content-Type'] = 'application/json' payload = { - delete: { - verified: false - } + domain: { + delete: { + verified: false, + }, + }, } delete "/repp/v1/domains/#{@domain.name}", headers: @auth_headers, params: payload.to_json @@ -36,9 +38,11 @@ class ReppV1DomainsDeleteTest < ActionDispatch::IntegrationTest @auth_headers['Content-Type'] = 'application/json' payload = { - delete: { - verified: true - } + domain: { + delete: { + verified: true, + }, + }, } delete "/repp/v1/domains/#{@domain.name}", headers: @auth_headers, params: payload.to_json diff --git a/test/integration/repp/v1/domains/list_test.rb b/test/integration/repp/v1/domains/list_test.rb index 366ac4d26..645947b57 100644 --- a/test/integration/repp/v1/domains/list_test.rb +++ b/test/integration/repp/v1/domains/list_test.rb @@ -15,7 +15,7 @@ class ReppV1DomainsListTest < ActionDispatch::IntegrationTest assert_response :ok - assert_equal @user.registrar.domains.count, json[:data][:total_number_of_records] + assert_equal @user.registrar.domains.count, json[:data][:count] assert_equal @user.registrar.domains.count, json[:data][:domains].length assert json[:data][:domains][0].is_a? String @@ -27,7 +27,7 @@ class ReppV1DomainsListTest < ActionDispatch::IntegrationTest assert_response :ok - assert_equal @user.registrar.domains.count, json[:data][:total_number_of_records] + assert_equal @user.registrar.domains.count, json[:data][:count] assert_equal @user.registrar.domains.count, json[:data][:domains].length assert json[:data][:domains][0].is_a? Hash diff --git a/test/integration/repp/v1/domains/renews_test.rb b/test/integration/repp/v1/domains/renews_test.rb index 3949f49dd..2fc1b7590 100644 --- a/test/integration/repp/v1/domains/renews_test.rb +++ b/test/integration/repp/v1/domains/renews_test.rb @@ -18,7 +18,7 @@ class ReppV1DomainsRenewsTest < ActionDispatch::IntegrationTest :prepare_renewed_expire_time).and_call_through @auth_headers['Content-Type'] = 'application/json' - payload = { renew: { period: 1, period_unit: 'y', exp_date: original_valid_to } } + payload = { renews: { period: 1, period_unit: 'y', exp_date: original_valid_to } } post "/repp/v1/domains/#{@domain.name}/renew", headers: @auth_headers, params: payload.to_json json = JSON.parse(response.body, symbolize_names: true) @@ -36,7 +36,7 @@ class ReppV1DomainsRenewsTest < ActionDispatch::IntegrationTest travel_to Time.zone.parse('2010-07-05') @auth_headers['Content-Type'] = 'application/json' - payload = { renew: { period: 10, period_unit: 'y', exp_date: original_valid_to } } + payload = { renews: { period: 10, period_unit: 'y', exp_date: original_valid_to } } post "/repp/v1/domains/#{@domain.name}/renew", headers: @auth_headers, params: payload.to_json json = JSON.parse(response.body, symbolize_names: true) @@ -60,7 +60,7 @@ class ReppV1DomainsRenewsTest < ActionDispatch::IntegrationTest one_year.reload @auth_headers['Content-Type'] = 'application/json' - payload = { renew: { period: 1, period_unit: 'y', exp_date: original_valid_to } } + payload = { renews: { period: 1, period_unit: 'y', exp_date: original_valid_to } } post "/repp/v1/domains/#{@domain.name}/renew", headers: @auth_headers, params: payload.to_json json = JSON.parse(response.body, symbolize_names: true) diff --git a/test/integration/repp/v1/domains/update_test.rb b/test/integration/repp/v1/domains/update_test.rb index d924fe7a3..59fb00d34 100644 --- a/test/integration/repp/v1/domains/update_test.rb +++ b/test/integration/repp/v1/domains/update_test.rb @@ -16,8 +16,8 @@ class ReppV1DomainsUpdateTest < ActionDispatch::IntegrationTest payload = { domain: { - auth_code: new_auth_code - } + auth_code: new_auth_code, + }, } put "/repp/v1/domains/#{@domain.name}", headers: @auth_headers, params: payload.to_json @@ -40,9 +40,9 @@ class ReppV1DomainsUpdateTest < ActionDispatch::IntegrationTest payload = { domain: { registrant: { - code: new_registrant.code - } - } + code: new_registrant.code, + }, + }, } put "/repp/v1/domains/#{@domain.name}", headers: @auth_headers, params: payload.to_json @@ -67,13 +67,14 @@ class ReppV1DomainsUpdateTest < ActionDispatch::IntegrationTest domain: { registrant: { code: new_registrant.code, - verified: true - } - } + verified: true, + }, + }, } put "/repp/v1/domains/#{@domain.name}", headers: @auth_headers, params: payload.to_json @domain.reload + json = JSON.parse(response.body, symbolize_names: true) assert_response :ok assert_equal 1000, json[:code] diff --git a/test/models/registrant_user_test.rb b/test/models/registrant_user_test.rb index 81e57fa72..4059720c9 100644 --- a/test/models/registrant_user_test.rb +++ b/test/models/registrant_user_test.rb @@ -60,6 +60,8 @@ class RegistrantUserTest < ActiveSupport::TestCase end bulk_action = @user.actions.where(operation: :bulk_update).last + single_action = @user.actions.find_by(operation: :update, + contact_id: contacts(:identical_to_william).id) assert_equal 4, bulk_action.subactions.size @@ -67,14 +69,14 @@ class RegistrantUserTest < ActiveSupport::TestCase notification = r.notifications.unread.order('created_at DESC').take if r == registrars(:bestnames) assert_equal '4 contacts have been updated by registrant', notification.text - assert_equal 'BulkAction', notification.attached_obj_type + assert_equal 'ContactUpdateAction', notification.attached_obj_type assert_equal bulk_action.id, notification.attached_obj_id assert_equal bulk_action.id, notification.action_id else assert_equal 'Contact william-002 has been updated by registrant', notification.text - refute notification.action_id - refute notification.attached_obj_id - refute notification.attached_obj_type + assert_equal 'ContactUpdateAction', notification.attached_obj_type + assert_equal single_action.id, notification.attached_obj_id + assert_equal single_action.id, notification.action_id end end end From 7c570e2916de3cbc105d3c8274d856d3ba4f4660 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergei=20Ts=C3=B5ganov?= Date: Mon, 6 Jun 2022 16:12:16 +0300 Subject: [PATCH 13/81] Fixed codeclimate errors --- app/controllers/repp/v1/account_controller.rb | 2 +- .../repp/v1/contacts_controller.rb | 7 +- .../repp/v1/domains/renews_controller.rb | 29 +++--- app/controllers/repp/v1/domains_controller.rb | 47 +++++----- .../repp/v1/invoices_controller.rb | 2 +- .../repp/v1/registrar/auth_controller.rb | 2 +- .../repp/v1/registrar/summary_controller.rb | 91 ++++++++++--------- app/models/admin_domain_contact.rb | 2 +- app/models/tech_domain_contact.rb | 2 +- lib/serializers/repp/contact.rb | 12 ++- lib/serializers/repp/domain.rb | 2 + lib/serializers/repp/invoice.rb | 19 ++-- 12 files changed, 114 insertions(+), 103 deletions(-) diff --git a/app/controllers/repp/v1/account_controller.rb b/app/controllers/repp/v1/account_controller.rb index e412b784d..85d79fe68 100644 --- a/app/controllers/repp/v1/account_controller.rb +++ b/app/controllers/repp/v1/account_controller.rb @@ -1,6 +1,6 @@ module Repp module V1 - class AccountController < BaseController + class AccountController < BaseController # rubocop:disable Metrics/ClassLength load_and_authorize_resource api :get, '/repp/v1/account' diff --git a/app/controllers/repp/v1/contacts_controller.rb b/app/controllers/repp/v1/contacts_controller.rb index 01230c7fe..309a53a36 100644 --- a/app/controllers/repp/v1/contacts_controller.rb +++ b/app/controllers/repp/v1/contacts_controller.rb @@ -9,7 +9,7 @@ module Repp desc 'Get all existing contacts' def index authorize! :check, Epp::Contact - records = current_user.registrar.contacts.order(created_at: :desc) + records = current_user.registrar.contacts q = records.ransack(search_params) q.sorts = 'created_at desc' if q.sorts.empty? @@ -19,8 +19,7 @@ module Repp .includes(:domain_contacts, :registrant_domains, :registrar) render_success(data: { contacts: serialized_contacts(limited_contacts), - count: contacts.count, - statuses: Contact::STATUSES, + count: contacts.count, statuses: Contact::STATUSES, ident_types: Contact::Ident.types }) end @@ -156,7 +155,7 @@ module Repp end def serialized_contacts(contacts) - return contacts.map {|c| c.code } unless index_params[:details] == 'true' + return contacts.map(&code) unless index_params[:details] == 'true' address_processing = Contact.address_processing? contacts.map do |c| diff --git a/app/controllers/repp/v1/domains/renews_controller.rb b/app/controllers/repp/v1/domains/renews_controller.rb index f963cd3a7..26365dffc 100644 --- a/app/controllers/repp/v1/domains/renews_controller.rb +++ b/app/controllers/repp/v1/domains/renews_controller.rb @@ -51,14 +51,11 @@ module Repp def select_renewable_domains @epp_errors ||= ActiveModel::Errors.new(self) - - if bulk_renew_params[:domains].instance_of?(Array) - @domains = bulk_renew_domains - @epp_errors.add(:epp_errors, msg: 'Domains cannot be empty', code: '2005') if @domains.empty? - else - @epp_errors.add(:epp_errors, msg: 'Domains attribute must be an array', code: '2005') + @domains = bulk_renew_domains + if @domains.empty? + @epp_errors.add(:epp_errors, msg: 'Domains cannot be empty', + code: '2005') end - return handle_errors if @epp_errors.any? end @@ -77,14 +74,18 @@ module Repp def bulk_renew_domains @epp_errors ||= ActiveModel::Errors.new(self) domains = [] - bulk_renew_params[:domains].each do |idn| - domain = Epp::Domain.find_by(name: idn) - domains << domain if domain - next if domain + if bulk_renew_params[:domains].instance_of?(Array) + bulk_renew_params[:domains].each do |idn| + domain = Epp::Domain.find_by(name: idn) + domains << domain if domain + next if domain - @epp_errors.add(:epp_errors, - msg: "Object does not exist: #{idn}", - code: '2304') + @epp_errors.add(:epp_errors, + msg: "Object does not exist: #{idn}", + code: '2304') + end + else + @epp_errors.add(:epp_errors, msg: 'Domains attribute must be an array', code: '2005') end domains diff --git a/app/controllers/repp/v1/domains_controller.rb b/app/controllers/repp/v1/domains_controller.rb index 37f735b69..00005e84d 100644 --- a/app/controllers/repp/v1/domains_controller.rb +++ b/app/controllers/repp/v1/domains_controller.rb @@ -30,7 +30,7 @@ module Repp api :GET, '/repp/v1/domains/:domain_name' desc 'Get a specific domain' def show - @domain = Epp::Domain.find_by_name(params[:id]) + @domain = Epp::Domain.find_by(name: params[:id]) authorize! :info, @domain sponsor = @domain.registrar == current_user.registrar @@ -251,34 +251,33 @@ module Repp dup_params = domain_params.to_h.dup return dup_params unless dup_params[:contacts] - new_contact_params = dup_params[:contacts].map do |c| - c.to_h.symbolize_keys - end + modify_contact_params(dup_params) + end - old_contact_params = @domain.domain_contacts.map do |c| - { code: c.contact_code_cache, type: c.name.downcase } + def modify_contact_params(params) + new_contact_params = params[:contacts].map { |c| c.to_h.symbolize_keys } + old_contact_params = @domain.domain_contacts.includes(:contact).map do |c| + { code: c.contact.code, type: c.name.downcase } end - dup_params[:contacts] = (new_contact_params - old_contact_params).map { |c| c.merge(action: 'add') } - dup_params[:contacts].concat((old_contact_params - new_contact_params) - .map { |c| c.merge(action: 'rem') }) - - dup_params + params[:contacts] = (new_contact_params - old_contact_params).map do |c| + c.merge(action: 'add') + end + params[:contacts].concat((old_contact_params - new_contact_params) + .map { |c| c.merge(action: 'rem') }) + params end def domain_params - params.require(:domain) - .permit(:name, :period, :period_unit, :registrar, - :transfer_code, :reserved_pw, :legal_document, - :registrant, legal_document: %i[body type], - registrant: [%i[code verified]], - dns_keys: [%i[id flags alg protocol public_key action]], - nameservers: [[:id, :hostname, - :action, { ipv4: [], ipv6: [] }]], - contacts: [%i[code type action]], - nameservers_attributes: [[:hostname, { ipv4: [], ipv6: [] }]], - admin_contacts: [], tech_contacts: [], - dnskeys_attributes: [%i[flags alg protocol public_key]], - delete: [:verified]) + params.require(:domain).permit(:name, :period, :period_unit, :registrar, :transfer_code, + :reserved_pw, :legal_document, :registrant, + legal_document: %i[body type], registrant: [%i[code verified]], + dns_keys: [%i[id flags alg protocol public_key action]], + nameservers: [[:id, :hostname, :action, { ipv4: [], ipv6: [] }]], + contacts: [%i[code type action]], + nameservers_attributes: [[:hostname, { ipv4: [], ipv6: [] }]], + admin_contacts: [], tech_contacts: [], + dnskeys_attributes: [%i[flags alg protocol public_key]], + delete: [:verified]) end end end diff --git a/app/controllers/repp/v1/invoices_controller.rb b/app/controllers/repp/v1/invoices_controller.rb index c8c6676ec..34c04ff72 100644 --- a/app/controllers/repp/v1/invoices_controller.rb +++ b/app/controllers/repp/v1/invoices_controller.rb @@ -115,4 +115,4 @@ module Repp end end end -end \ No newline at end of file +end diff --git a/app/controllers/repp/v1/registrar/auth_controller.rb b/app/controllers/repp/v1/registrar/auth_controller.rb index 8fba9eefb..f91cc8637 100644 --- a/app/controllers/repp/v1/registrar/auth_controller.rb +++ b/app/controllers/repp/v1/registrar/auth_controller.rb @@ -46,4 +46,4 @@ module Repp end end end -end \ No newline at end of file +end diff --git a/app/controllers/repp/v1/registrar/summary_controller.rb b/app/controllers/repp/v1/registrar/summary_controller.rb index 15f7d0164..0885d822a 100644 --- a/app/controllers/repp/v1/registrar/summary_controller.rb +++ b/app/controllers/repp/v1/registrar/summary_controller.rb @@ -11,33 +11,60 @@ module Repp if can?(:manage, :poll) user_notifications = user.unread_notifications notification = user_notifications.order('created_at DESC').take - notifications_count = user_notifications.count - if notification&.attached_obj_type && notification&.attached_obj_id - begin - object = object_by_type(notification.attached_obj_type) - .find(notification.attached_obj_id) - rescue => e - # the data model might be inconsistent; or ... - # this could happen if the registrar does not dequeue messages, and then the domain was deleted - # SELECT messages.id, domains.name, messages.body FROM messages LEFT OUTER - # JOIN domains ON attached_obj_id::INTEGER = domains.id - # WHERE attached_obj_type = 'Epp::Domain' AND name IS NULL; - message = 'orphan message, domain deleted, registrar should dequeue: ' - Rails.logger.error message + e.to_s - end - end end - data = serialize_data(registrar: registrar, - notification: notification, - notifications_count: notifications_count, - object: object) + render_success(data: serialize_data(registrar: registrar, + notification: notification, + notifications_count: user_notifications&.count, + object: notification_object(notification))) + end - render_success(data: data) + def serialized_domain_transfer(object) + { + name: object.domain_name, trStatus: object.status, + reID: object.new_registrar.code, + reDate: object.transfer_requested_at.try(:iso8601), + acID: object.old_registrar.code, + acDate: object.transferred_at.try(:iso8601) || object.wait_until.try(:iso8601), + exDate: object.domain_valid_to.iso8601 + } + end + + def serialized_contact_update_action(object) + { + contacts: object.to_non_available_contact_codes, + operation: object.operation, + opDate: object.created_at.utc.xmlschema, + svTrid: object.id, + who: object.user.username, + reason: 'Auto-update according to official data', + } end private + # rubocop:disable Style/RescueStandardError + def notification_object(notification) + return unless notification + + return unless notification.attached_obj_type || notification.attached_obj_id + + begin + object_by_type(notification.attached_obj_type) + .find(notification.attached_obj_id) + rescue => e + # the data model might be inconsistent; or ... + # this could happen if the registrar does not dequeue messages, + # and then the domain was deleted + # SELECT messages.id, domains.name, messages.body FROM messages LEFT OUTER + # JOIN domains ON attached_obj_id::INTEGER = domains.id + # WHERE attached_obj_type = 'Epp::Domain' AND name IS NULL; + message = 'orphan message, domain deleted, registrar should dequeue: ' + Rails.logger.error message + e.to_s + end + end + # rubocop:enable Style/RescueStandardError + def object_by_type(object_type) Object.const_get(object_type) rescue NameError @@ -83,29 +110,9 @@ module Repp def serialized_object(object, obj_type) return unless object - case obj_type - when 'DomainTransfer' - { - name: object.domain_name, - trStatus: object.status, - reID: object.new_registrar.code, - reDate: object.transfer_requested_at.try(:iso8601), - acID: object.old_registrar.code, - acDate: object.transferred_at.try(:iso8601) || object.wait_until.try(:iso8601), - exDate: object.domain_valid_to.iso8601, - } - when 'ContactUpdateAction' - { - contacts: object.to_non_available_contact_codes, - operation: object.operation, - opDate: object.created_at.utc.xmlschema, - svTrid: object.id, - who: object.user.username, - reason: 'Auto-update according to official data', - } - end + try("serialized_#{obj_type.underscore}", object) end end end end -end \ No newline at end of file +end diff --git a/app/models/admin_domain_contact.rb b/app/models/admin_domain_contact.rb index 9003afb0f..99f0d02da 100644 --- a/app/models/admin_domain_contact.rb +++ b/app/models/admin_domain_contact.rb @@ -6,7 +6,7 @@ class AdminDomainContact < DomainContact skipped_domains = [] admin_contacts = where(contact: current_contact) - admin_contacts.includes(:domain).each do |admin_contact| + admin_contacts.includes(:domain).find_each do |admin_contact| if admin_contact.domain.bulk_update_prohibited? skipped_domains << admin_contact.domain.name next diff --git a/app/models/tech_domain_contact.rb b/app/models/tech_domain_contact.rb index 30db6dec7..7c3d22bfd 100644 --- a/app/models/tech_domain_contact.rb +++ b/app/models/tech_domain_contact.rb @@ -5,7 +5,7 @@ class TechDomainContact < DomainContact skipped_domains = [] tech_contacts = where(contact: current_contact) - tech_contacts.includes(:domain).each do |tech_contact| + tech_contacts.includes(:domain).find_each do |tech_contact| if irreplaceable?(tech_contact) skipped_domains << tech_contact.domain.name next diff --git a/lib/serializers/repp/contact.rb b/lib/serializers/repp/contact.rb index c36fa3258..11d5457ae 100644 --- a/lib/serializers/repp/contact.rb +++ b/lib/serializers/repp/contact.rb @@ -10,13 +10,15 @@ module Serializers @simplify = options[:simplify] || false end + # rubocop:disable Metrics/MethodLength + # rubocop:disable Metrics/AbcSize def to_json(obj = contact) return simple_object if @simplify - json = { id: obj.uuid, code: obj.code, name: obj.name, ident: ident, - email: obj.email, phone: obj.phone, created_at: obj.created_at, - auth_info: obj.auth_info, statuses: statuses, - disclosed_attributes: obj.disclosed_attributes, registrar: registrar } + json = { id: obj.uuid, code: obj.code, name: obj.name, ident: ident, phone: obj.phone, + created_at: obj.created_at, auth_info: obj.auth_info, email: obj.email, + statuses: statuses, disclosed_attributes: obj.disclosed_attributes, + registrar: registrar } json[:address] = address if @show_address if @domain_params json[:domains] = domains @@ -24,6 +26,8 @@ module Serializers end json end + # rubocop:enable Metrics/MethodLength + # rubocop:enable Metrics/AbcSize def registrar contact.registrar.as_json(only: %i[name website]) diff --git a/lib/serializers/repp/domain.rb b/lib/serializers/repp/domain.rb index 07bb95e36..cd0be8e25 100644 --- a/lib/serializers/repp/domain.rb +++ b/lib/serializers/repp/domain.rb @@ -9,6 +9,7 @@ module Serializers @simplify = simplify end + # rubocop:disable Metrics/MethodLength # rubocop:disable Metrics/AbcSize def to_json(obj = domain) return simple_object if @simplify @@ -26,6 +27,7 @@ module Serializers json[:transfer_code] = obj.auth_info if @sponsored json end + # rubocop:enable Metrics/MethodLength # rubocop:enable Metrics/AbcSize def contacts diff --git a/lib/serializers/repp/invoice.rb b/lib/serializers/repp/invoice.rb index 9bc7431b2..f4ab4a1db 100644 --- a/lib/serializers/repp/invoice.rb +++ b/lib/serializers/repp/invoice.rb @@ -8,6 +8,8 @@ module Serializers @simplify = simplify end + # rubocop:disable Metrics/MethodLength + # rubocop:disable Metrics/AbcSize def to_json(obj = invoice) return simple_object if @simplify @@ -29,16 +31,11 @@ module Serializers def seller { - name: invoice.seller_name, - reg_no: invoice.seller_reg_no, - iban: invoice.seller_iban, - bank: invoice.seller_bank, - swift: invoice.seller_swift, - vat_no: invoice.seller_vat_no, - address: invoice.seller_address, - country: invoice.seller_country.name, - phone: invoice.seller_phone, - url: invoice.seller_url, + name: invoice.seller_name, reg_no: invoice.seller_reg_no, + iban: invoice.seller_iban, bank: invoice.seller_bank, + swift: invoice.seller_swift, vat_no: invoice.seller_vat_no, + address: invoice.seller_address, country: invoice.seller_country.name, + phone: invoice.seller_phone, url: invoice.seller_url, email: invoice.seller_email, contact_name: invoice.seller_contact_name, } @@ -80,6 +77,8 @@ module Serializers recipient: invoice.buyer.billing_email, } end + # rubocop:enable Metrics/MethodLength + # rubocop:enable Metrics/AbcSize end end end From 3812e398bfb8b3052604cba0146a35d1713d1f34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergei=20Ts=C3=B5ganov?= Date: Mon, 6 Jun 2022 16:24:38 +0300 Subject: [PATCH 14/81] Fixed codeclimate errors --- .../repp/v1/registrar/summary_controller.rb | 28 ++++++++----------- lib/serializers/repp/invoice.rb | 2 +- 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/app/controllers/repp/v1/registrar/summary_controller.rb b/app/controllers/repp/v1/registrar/summary_controller.rb index 0885d822a..d3865bed2 100644 --- a/app/controllers/repp/v1/registrar/summary_controller.rb +++ b/app/controllers/repp/v1/registrar/summary_controller.rb @@ -12,7 +12,6 @@ module Repp user_notifications = user.unread_notifications notification = user_notifications.order('created_at DESC').take end - render_success(data: serialize_data(registrar: registrar, notification: notification, notifications_count: user_notifications&.count, @@ -45,23 +44,18 @@ module Repp # rubocop:disable Style/RescueStandardError def notification_object(notification) - return unless notification + return unless notification&.attached_obj_type || notification&.attached_obj_id - return unless notification.attached_obj_type || notification.attached_obj_id - - begin - object_by_type(notification.attached_obj_type) - .find(notification.attached_obj_id) - rescue => e - # the data model might be inconsistent; or ... - # this could happen if the registrar does not dequeue messages, - # and then the domain was deleted - # SELECT messages.id, domains.name, messages.body FROM messages LEFT OUTER - # JOIN domains ON attached_obj_id::INTEGER = domains.id - # WHERE attached_obj_type = 'Epp::Domain' AND name IS NULL; - message = 'orphan message, domain deleted, registrar should dequeue: ' - Rails.logger.error message + e.to_s - end + object_by_type(notification.attached_obj_type).find(notification.attached_obj_id) + rescue => e + # the data model might be inconsistent; or ... + # this could happen if the registrar does not dequeue messages, + # and then the domain was deleted + # SELECT messages.id, domains.name, messages.body FROM messages LEFT OUTER + # JOIN domains ON attached_obj_id::INTEGER = domains.id + # WHERE attached_obj_type = 'Epp::Domain' AND name IS NULL; + message = 'orphan message, domain deleted, registrar should dequeue: ' + Rails.logger.error message + e.to_s end # rubocop:enable Style/RescueStandardError diff --git a/lib/serializers/repp/invoice.rb b/lib/serializers/repp/invoice.rb index f4ab4a1db..3b8efbd58 100644 --- a/lib/serializers/repp/invoice.rb +++ b/lib/serializers/repp/invoice.rb @@ -37,7 +37,7 @@ module Serializers address: invoice.seller_address, country: invoice.seller_country.name, phone: invoice.seller_phone, url: invoice.seller_url, email: invoice.seller_email, - contact_name: invoice.seller_contact_name, + contact_name: invoice.seller_contact_name } end From b505de2f0f3f6837e0e62f7f72e8392bea5dfe69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergei=20Ts=C3=B5ganov?= Date: Mon, 6 Jun 2022 16:30:57 +0300 Subject: [PATCH 15/81] Fixed test errors --- app/controllers/repp/v1/contacts_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/repp/v1/contacts_controller.rb b/app/controllers/repp/v1/contacts_controller.rb index 309a53a36..d6c4439c5 100644 --- a/app/controllers/repp/v1/contacts_controller.rb +++ b/app/controllers/repp/v1/contacts_controller.rb @@ -155,7 +155,7 @@ module Repp end def serialized_contacts(contacts) - return contacts.map(&code) unless index_params[:details] == 'true' + return contacts.map(&:code) unless index_params[:details] == 'true' address_processing = Contact.address_processing? contacts.map do |c| From b502c2779e17394c9bf5d0af864c39cb9f3847da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergei=20Ts=C3=B5ganov?= Date: Wed, 8 Jun 2022 12:07:21 +0300 Subject: [PATCH 16/81] Added additional tests to REPP API --- .../repp/v1/invoices_controller.rb | 6 +- .../repp/v1/registrar/auth_controller.rb | 2 +- config/routes.rb | 2 +- .../repp/v1/account/activities_list_test.rb | 70 ++++++++++++++ .../repp/v1/account/balance_test.rb | 2 + .../repp/v1/account/details_test.rb | 22 +++++ .../update_auto_reload_balance_test.rb | 69 ++++++++++++++ .../repp/v1/account/update_details_test.rb | 30 ++++++ .../integration/repp/v1/contacts/list_test.rb | 31 +++++- .../repp/v1/contacts/search_test.rb | 43 +++++++++ test/integration/repp/v1/domains/list_test.rb | 28 ++++++ .../repp/v1/invoices/add_credit_test.rb | 94 +++++++++++++++++++ .../repp/v1/invoices/cancel_test.rb | 44 +++++++++ .../repp/v1/invoices/download_test.rb | 22 +++++ .../integration/repp/v1/invoices/list_test.rb | 85 +++++++++++++++++ .../integration/repp/v1/invoices/send_test.rb | 39 ++++++++ .../integration/repp/v1/invoices/show_test.rb | 33 +++++++ .../repp/v1/registrar/auth/check_info_test.rb | 39 ++++++++ .../v1/registrar/auth/switch_user_test.rb | 52 ++++++++++ .../v1/registrar/auth/tara_callback_test.rb | 46 +++++++++ .../repp/v1/registrar/summary_test.rb | 44 +++++++++ 21 files changed, 796 insertions(+), 7 deletions(-) create mode 100644 test/integration/repp/v1/account/activities_list_test.rb create mode 100644 test/integration/repp/v1/account/details_test.rb create mode 100644 test/integration/repp/v1/account/update_auto_reload_balance_test.rb create mode 100644 test/integration/repp/v1/account/update_details_test.rb create mode 100644 test/integration/repp/v1/contacts/search_test.rb create mode 100644 test/integration/repp/v1/invoices/add_credit_test.rb create mode 100644 test/integration/repp/v1/invoices/cancel_test.rb create mode 100644 test/integration/repp/v1/invoices/download_test.rb create mode 100644 test/integration/repp/v1/invoices/list_test.rb create mode 100644 test/integration/repp/v1/invoices/send_test.rb create mode 100644 test/integration/repp/v1/invoices/show_test.rb create mode 100644 test/integration/repp/v1/registrar/auth/check_info_test.rb create mode 100644 test/integration/repp/v1/registrar/auth/switch_user_test.rb create mode 100644 test/integration/repp/v1/registrar/auth/tara_callback_test.rb create mode 100644 test/integration/repp/v1/registrar/summary_test.rb diff --git a/app/controllers/repp/v1/invoices_controller.rb b/app/controllers/repp/v1/invoices_controller.rb index 34c04ff72..5d37d6881 100644 --- a/app/controllers/repp/v1/invoices_controller.rb +++ b/app/controllers/repp/v1/invoices_controller.rb @@ -47,7 +47,7 @@ module Repp .merge!(recipient: recipient) }) end - api :post, '/repp/v1/invoices/:id/cancel' + api :put, '/repp/v1/invoices/:id/cancel' desc 'Cancel a specific invoice' def cancel action = Actions::InvoiceCancel.new(@invoice) @@ -72,7 +72,7 @@ module Repp serializer = Serializers::Repp::Invoice.new(invoice, simplify: true) render_success(data: { invoice: serializer.to_json }) else - handle_errors(deposit) + handle_non_epp_errors(deposit) end end @@ -108,7 +108,7 @@ module Repp end def serialized_invoices(invoices) - return invoices.pluck(:number) unless index_params[:details] == 'true' + return invoices.map(&:number) unless index_params[:details] == 'true' simple = index_params[:simple] == 'true' || false invoices.map { |i| Serializers::Repp::Invoice.new(i, simplify: simple).to_json } diff --git a/app/controllers/repp/v1/registrar/auth_controller.rb b/app/controllers/repp/v1/registrar/auth_controller.rb index f91cc8637..ccc7d2e66 100644 --- a/app/controllers/repp/v1/registrar/auth_controller.rb +++ b/app/controllers/repp/v1/registrar/auth_controller.rb @@ -22,7 +22,7 @@ module Repp render_success(data: { token: token, username: user.username }) end - api :put, '/repp/v1/registrar/auth/switch_user/:new_user_id' + api :put, '/repp/v1/registrar/auth/switch_user' desc 'Switch session to another api user' def switch_user new_user = ApiUser.find(auth_params[:new_user_id]) diff --git a/config/routes.rb b/config/routes.rb index 6f336f4d4..2ea79e1c0 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -87,11 +87,11 @@ Rails.application.routes.draw do resources :invoices, only: %i[index show] do collection do get ':id/download', to: 'invoices#download' - get ':id/cancel', to: 'invoices#cancel' post 'add_credit' end member do post 'send_to_recipient', to: 'invoices#send_to_recipient' + put 'cancel', to: 'invoices#cancel' end end resources :auctions, only: %i[index] diff --git a/test/integration/repp/v1/account/activities_list_test.rb b/test/integration/repp/v1/account/activities_list_test.rb new file mode 100644 index 000000000..b492cc098 --- /dev/null +++ b/test/integration/repp/v1/account/activities_list_test.rb @@ -0,0 +1,70 @@ +require 'test_helper' + +class ReppV1AccountActivitiesListTest < ActionDispatch::IntegrationTest + def setup + @user = users(:api_bestnames) + token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}") + token = "Basic #{token}" + + @auth_headers = { 'Authorization' => token } + end + + def test_returns_account_activities + get repp_v1_account_path, headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + + assert_equal @user.registrar.cash_account.activities.count, json[:data][:count] + assert_equal @user.registrar.cash_account.activities.count, json[:data][:activities].length + + assert json[:data][:activities][0].is_a? Hash + end + + def test_respects_limit + get repp_v1_account_path(limit: 1), headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + + assert_equal 1, json[:data][:activities].length + end + + def test_respects_offset + offset = 1 + get repp_v1_account_path(offset: offset), headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + + assert_equal (@user.registrar.cash_account.activities.count - offset), json[:data][:activities].length + end + + def test_returns_account_activities_by_search_query + search_params = { + description_matches: '%renew%', + } + get repp_v1_account_path(q: search_params), headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + + assert_equal json[:data][:activities].length, 1 + assert json[:data][:activities][0].is_a? Hash + end + + def test_returns_account_activities_by_sort_query + activity = account_activities(:renew_cash) + sort_params = { + s: 'activity_type asc', + } + get repp_v1_account_path(q: sort_params), headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + + assert_equal @user.registrar.cash_account.activities.count, json[:data][:count] + assert_equal @user.registrar.cash_account.activities.count, json[:data][:activities].length + assert_equal json[:data][:activities][0][:description], activity.description + end +end diff --git a/test/integration/repp/v1/account/balance_test.rb b/test/integration/repp/v1/account/balance_test.rb index a8416be0a..a4d41ed53 100644 --- a/test/integration/repp/v1/account/balance_test.rb +++ b/test/integration/repp/v1/account/balance_test.rb @@ -10,6 +10,8 @@ class ReppV1BalanceTest < ActionDispatch::IntegrationTest @auth_headers = { 'Authorization' => token } end + + def test_can_query_balance get '/repp/v1/account/balance', headers: @auth_headers json = JSON.parse(response.body, symbolize_names: true) diff --git a/test/integration/repp/v1/account/details_test.rb b/test/integration/repp/v1/account/details_test.rb new file mode 100644 index 000000000..b0d359d74 --- /dev/null +++ b/test/integration/repp/v1/account/details_test.rb @@ -0,0 +1,22 @@ +require 'test_helper' + +class ReppV1AccountDetailsTest < ActionDispatch::IntegrationTest + def setup + @user = users(:api_bestnames) + token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}") + token = "Basic #{token}" + + @auth_headers = { 'Authorization' => token } + end + + def test_returns_account_details + get '/repp/v1/account/details', headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal 1000, json[:code] + assert_equal 'Command completed successfully', json[:message] + + assert_equal @user.registrar.billing_email, json[:data][:account][:billing_email] + end +end \ No newline at end of file diff --git a/test/integration/repp/v1/account/update_auto_reload_balance_test.rb b/test/integration/repp/v1/account/update_auto_reload_balance_test.rb new file mode 100644 index 000000000..6201750c3 --- /dev/null +++ b/test/integration/repp/v1/account/update_auto_reload_balance_test.rb @@ -0,0 +1,69 @@ +require 'test_helper' + +class ReppV1AccountUpdateAutoReloadBalanceTest < ActionDispatch::IntegrationTest + def setup + @user = users(:api_bestnames) + token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}") + token = "Basic #{token}" + + @auth_headers = { 'Authorization' => token } + end + + def test_updates_auto_reload_balance + amount = 100 + threshold = 10 + request_body = { + type: { + amount: amount, + threshold: threshold, + }, + } + + assert_nil @user.registrar.settings['balance_auto_reload'] + + post '/repp/v1/account/update_auto_reload_balance', headers: @auth_headers, + params: request_body + json = JSON.parse(response.body, symbolize_names: true) + assert_response :ok + assert_equal 1000, json[:code] + assert_equal 'Balance Auto-Reload setting has been updated', json[:message] + + @user.registrar.reload + + assert_equal amount, @user.registrar.settings['balance_auto_reload']['type']['amount'] + assert_equal threshold, @user.registrar.settings['balance_auto_reload']['type']['threshold'] + end + + def test_returns_error_if_type_has_wrong_attributes + min_deposit = 10 + request_body = { + type: { + amount: 5, + threshold: -1, + }, + } + Setting.minimum_deposit = min_deposit + + post '/repp/v1/account/update_auto_reload_balance', headers: @auth_headers, + params: request_body + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :bad_request + amount_error = "Amount must be greater than or equal to #{min_deposit.to_f}" + threshold = 'Threshold must be greater than or equal to 0' + assert_equal "#{amount_error}, #{threshold}", json[:message] + end + + def test_disables_auto_reload_balance + get '/repp/v1/account/disable_auto_reload_balance', headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal 1000, json[:code] + assert_equal 'Balance Auto-Reload setting has been disabled', json[:message] + + @user.registrar.reload + + assert_nil @user.registrar.settings['balance_auto_reload'] + end +end \ No newline at end of file diff --git a/test/integration/repp/v1/account/update_details_test.rb b/test/integration/repp/v1/account/update_details_test.rb new file mode 100644 index 000000000..3ab415cd8 --- /dev/null +++ b/test/integration/repp/v1/account/update_details_test.rb @@ -0,0 +1,30 @@ +require 'test_helper' + +class ReppV1AccountUpdateDetailsTest < ActionDispatch::IntegrationTest + def setup + @user = users(:api_bestnames) + token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}") + token = "Basic #{token}" + + @auth_headers = { 'Authorization' => token } + end + + def test_updates_details + request_body = { + account: { + billing_email: 'donaldtrump@yandex.ru', + iban: 'GB331111111111111111', + }, + } + + put '/repp/v1/account', headers: @auth_headers, params: request_body + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal 1000, json[:code] + assert_equal 'Your account has been updated', json[:message] + + assert_equal(request_body[:account][:billing_email], @user.registrar.billing_email) + assert_equal(request_body[:account][:iban], @user.registrar.iban) + end +end \ No newline at end of file diff --git a/test/integration/repp/v1/contacts/list_test.rb b/test/integration/repp/v1/contacts/list_test.rb index 6cc3e4669..979d3ea40 100644 --- a/test/integration/repp/v1/contacts/list_test.rb +++ b/test/integration/repp/v1/contacts/list_test.rb @@ -12,7 +12,7 @@ class ReppV1ContactsListTest < ActionDispatch::IntegrationTest def test_returns_registrar_contacts get repp_v1_contacts_path, headers: @auth_headers json = JSON.parse(response.body, symbolize_names: true) - + assert_response :ok assert_equal @user.registrar.contacts.count, json[:data][:count] @@ -21,7 +21,6 @@ class ReppV1ContactsListTest < ActionDispatch::IntegrationTest assert json[:data][:contacts][0].is_a? String end - def test_returns_detailed_registrar_contacts get repp_v1_contacts_path(details: true), headers: @auth_headers json = JSON.parse(response.body, symbolize_names: true) @@ -52,4 +51,32 @@ class ReppV1ContactsListTest < ActionDispatch::IntegrationTest assert_equal (@user.registrar.contacts.count - offset), json[:data][:contacts].length end + + def test_returns_detailed_registrar_contacts_by_search_query + search_params = { + ident_type_eq: 'priv', + } + get repp_v1_contacts_path(details: true, q: search_params), headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + + assert_equal json[:data][:contacts].length, 3 + assert json[:data][:contacts][0].is_a? Hash + end + + def test_returns_detailed_registrar_contacts_by_sort_query + contact = contacts(:william) + sort_params = { + s: 'name desc', + } + get repp_v1_contacts_path(details: true, q: sort_params), headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + + assert_equal @user.registrar.contacts.count, json[:data][:count] + assert_equal @user.registrar.contacts.count, json[:data][:contacts].length + assert_equal json[:data][:contacts][0][:code], contact.code + end end diff --git a/test/integration/repp/v1/contacts/search_test.rb b/test/integration/repp/v1/contacts/search_test.rb new file mode 100644 index 000000000..ceeefc7be --- /dev/null +++ b/test/integration/repp/v1/contacts/search_test.rb @@ -0,0 +1,43 @@ +require 'test_helper' + +class ReppV1ContactsSearchTest < ActionDispatch::IntegrationTest + def setup + @user = users(:api_bestnames) + token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}") + token = "Basic #{token}" + + @auth_headers = { 'Authorization' => token } + end + + def test_searches_all_contacts_by_id + contact = contacts(:john) + get "/repp/v1/contacts/search/#{contact.code}", headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert json[:data].is_a? Array + assert_equal json[:data][0][:value], contact.code + assert_equal json[:data][0][:label], "#{contact.code} #{contact.name}" + assert_equal json[:data][0][:selected], true + end + + def test_searches_all_contacts_by_query + get '/repp/v1/contacts/search', headers: @auth_headers, params: { query: 'j' } + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert json[:data].is_a? Array + assert_equal json[:data].length, 2 + assert_equal json[:data][0][:selected], false + assert_equal json[:data][1][:selected], false + end + + def test_searches_all_contacts_by_wrong_query + get '/repp/v1/contacts/search', headers: @auth_headers, params: { query: '000' } + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert json[:data].is_a? Array + assert_equal json[:data].length, 0 + end +end \ No newline at end of file diff --git a/test/integration/repp/v1/domains/list_test.rb b/test/integration/repp/v1/domains/list_test.rb index 645947b57..205017a6c 100644 --- a/test/integration/repp/v1/domains/list_test.rb +++ b/test/integration/repp/v1/domains/list_test.rb @@ -64,4 +64,32 @@ class ReppV1DomainsListTest < ActionDispatch::IntegrationTest serialized_domain = Serializers::Repp::Domain.new(domain).to_json assert_equal serialized_domain.as_json, json[:data][:domain].as_json end + + def test_returns_detailed_registrar_domains_by_search_query + search_params = { + name_matches: '%library%', + } + get repp_v1_domains_path(details: true, q: search_params), headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + + assert_equal json[:data][:domains].length, 1 + assert json[:data][:domains][0].is_a? Hash + end + + def test_returns_detailed_registrar_domains_by_sort_query + domain = domains(:shop) + sort_params = { + s: 'name desc', + } + get repp_v1_domains_path(details: true, q: sort_params), headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + + assert_equal @user.registrar.domains.count, json[:data][:count] + assert_equal @user.registrar.domains.count, json[:data][:domains].length + assert_equal json[:data][:domains][0][:name], domain.name + end end diff --git a/test/integration/repp/v1/invoices/add_credit_test.rb b/test/integration/repp/v1/invoices/add_credit_test.rb new file mode 100644 index 000000000..71f2c77d3 --- /dev/null +++ b/test/integration/repp/v1/invoices/add_credit_test.rb @@ -0,0 +1,94 @@ +require 'test_helper' + +class ReppV1InvoicesAddCreditTest < ActionDispatch::IntegrationTest + def setup + @user = users(:api_bestnames) + token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}") + token = "Basic #{token}" + + @auth_headers = { 'Authorization' => token } + + @original_registry_vat_rate = Setting.registry_vat_prc + eis_response = OpenStruct.new(body: '{"everypay_link":"https://link.test"}') + Spy.on_instance_method(EisBilling::AddDeposits, :send_invoice).and_return(eis_response) + Spy.on_instance_method(EisBilling::BaseController, :authorized).and_return(true) + end + + teardown do + Setting.registry_vat_prc = @original_registry_vat_rate + end + + def test_generates_add_credit_invoice_with_billing_system + request_body = { + invoice: { + amount: 100, + description: 'Add credit', + }, + } + Setting.registry_vat_prc = 0.1 + ENV['billing_system_integrated'] = 'true' + + if Feature.billing_system_integrated? + invoice_n = Invoice.order(number: :desc).last.number + stub_request(:post, 'https://eis_billing_system:3000/api/v1/invoice_generator/invoice_number_generator') + .to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}", headers: {}) + stub_request(:post, 'https://eis_billing_system:3000/api/v1/e_invoice/e_invoice') + .to_return(status: 200, body: '', headers: {}) + end + + post '/repp/v1/invoices/add_credit', headers: @auth_headers, + params: request_body + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal 1000, json[:code] + assert_equal 'Command completed successfully', json[:message] + + assert_not json[:data][:invoice][:paid] + assert json[:data][:invoice][:payable] + assert json[:data][:invoice][:cancellable] + assert_equal json[:data][:invoice][:payment_link], 'https://link.test' + assert_equal json[:data][:invoice][:total], 110.to_f.to_s + end + + def test_generates_add_credit_invoice_without_billing_system + request_body = { + invoice: { + amount: 100, + description: 'Add credit', + }, + } + Setting.registry_vat_prc = 0.1 + ENV['billing_system_integrated'] = 'false' + + post '/repp/v1/invoices/add_credit', headers: @auth_headers, + params: request_body + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal 1000, json[:code] + assert_equal 'Command completed successfully', json[:message] + + assert_not json[:data][:invoice][:paid] + assert json[:data][:invoice][:payable] + assert json[:data][:invoice][:cancellable] + assert_equal json[:data][:invoice][:total], 110.to_f.to_s + end + + def test_generates_add_credit_invoice_with_invalid_amount + request_body = { + invoice: { + amount: 0.4, + description: 'Add credit', + }, + } + Setting.minimum_deposit = 0.5 + + post '/repp/v1/invoices/add_credit', headers: @auth_headers, + params: request_body + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :bad_request + assert_equal "Amount is too small. Minimum deposit is #{Setting.minimum_deposit} EUR", json[:message] + end +end \ No newline at end of file diff --git a/test/integration/repp/v1/invoices/cancel_test.rb b/test/integration/repp/v1/invoices/cancel_test.rb new file mode 100644 index 000000000..1041d1d1f --- /dev/null +++ b/test/integration/repp/v1/invoices/cancel_test.rb @@ -0,0 +1,44 @@ +require 'test_helper' + +class ReppV1InvoicesCancelTest < ActionDispatch::IntegrationTest + def setup + @user = users(:api_bestnames) + token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}") + token = "Basic #{token}" + + @auth_headers = { 'Authorization' => token } + end + + def test_cancels_invoice + invoice = invoices(:one) + invoice.account_activity = nil + assert invoice.cancellable? + stub_request(:post, 'https://eis_billing_system:3000/api/v1/invoice_generator/invoice_status') + .to_return(status: :ok, headers: {}) + + put "/repp/v1/invoices/#{invoice.id}/cancel", headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal 1000, json[:code] + assert_equal 'Command completed successfully', json[:message] + + invoice.reload + assert invoice.cancelled? + assert json[:data][:invoice].is_a? Hash + end + + def test_cancels_uncancellable_invoice + invoice = invoices(:one) + assert_not invoice.cancellable? + + put "/repp/v1/invoices/#{invoice.id}/cancel", headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :bad_request + assert_equal 'Invoice status prohibits operation', json[:message] + + invoice.reload + assert_not invoice.cancelled? + end +end \ No newline at end of file diff --git a/test/integration/repp/v1/invoices/download_test.rb b/test/integration/repp/v1/invoices/download_test.rb new file mode 100644 index 000000000..cbb9de585 --- /dev/null +++ b/test/integration/repp/v1/invoices/download_test.rb @@ -0,0 +1,22 @@ +require 'test_helper' + +class ReppV1InvoicesDownloadTest < ActionDispatch::IntegrationTest + def setup + @user = users(:api_bestnames) + token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}") + token = "Basic #{token}" + + @auth_headers = { 'Authorization' => token } + end + + def test_returns_invoice_as_pdf + invoice = @user.registrar.invoices.first + + get "/repp/v1/invoices/#{invoice.id}/download", headers: @auth_headers + + assert_response :ok + assert_equal 'application/pdf', response.headers['Content-Type'] + assert_equal "attachment; filename=\"Invoice-2.pdf\"; filename*=UTF-8''Invoice-2.pdf", response.headers['Content-Disposition'] + assert_not_empty response.body + end +end \ No newline at end of file diff --git a/test/integration/repp/v1/invoices/list_test.rb b/test/integration/repp/v1/invoices/list_test.rb new file mode 100644 index 000000000..5dfe2d53f --- /dev/null +++ b/test/integration/repp/v1/invoices/list_test.rb @@ -0,0 +1,85 @@ +require 'test_helper' + +class ReppV1InvoicesListTest < ActionDispatch::IntegrationTest + def setup + @user = users(:api_bestnames) + token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}") + token = "Basic #{token}" + + @auth_headers = { 'Authorization' => token } + end + + def test_returns_registrar_invoices + get repp_v1_invoices_path, headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + + assert_equal @user.registrar.invoices.count, json[:data][:count] + assert_equal @user.registrar.invoices.count, json[:data][:invoices].length + + assert json[:data][:invoices][0].is_a? Integer + end + + def test_returns_detailed_registrar_invoices + get repp_v1_invoices_path(details: true), headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + + assert_equal @user.registrar.invoices.count, json[:data][:count] + assert_equal @user.registrar.invoices.count, json[:data][:invoices].length + + assert json[:data][:invoices][0].is_a? Hash + end + + def test_returns_detailed_registrar_invoices_by_search_query + invoice = @user.registrar.invoices.last + invoice.update(number: 15_008) + search_params = { + number_gteq: 15_000, + } + get repp_v1_invoices_path(details: true, q: search_params), headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + + assert_equal json[:data][:invoices].length, 1 + assert json[:data][:invoices][0].is_a? Hash + assert_equal json[:data][:invoices][0][:id], invoice.id + end + + def test_returns_detailed_registrar_invoices_by_sort_query + invoice = invoices(:unpaid) + sort_params = { + s: 'number desc', + } + get repp_v1_invoices_path(details: true, q: sort_params), headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + + assert_equal @user.registrar.invoices.count, json[:data][:count] + assert_equal @user.registrar.invoices.count, json[:data][:invoices].length + assert_equal json[:data][:invoices][0][:id], invoice.id + end + + def test_respects_limit + get repp_v1_invoices_path(details: true, limit: 1), headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + + assert_equal 1, json[:data][:invoices].length + end + + def test_respects_offset + offset = 1 + get repp_v1_invoices_path(details: true, offset: offset), headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + + assert_equal (@user.registrar.invoices.count - offset), json[:data][:invoices].length + end +end \ No newline at end of file diff --git a/test/integration/repp/v1/invoices/send_test.rb b/test/integration/repp/v1/invoices/send_test.rb new file mode 100644 index 000000000..77fe9997f --- /dev/null +++ b/test/integration/repp/v1/invoices/send_test.rb @@ -0,0 +1,39 @@ +require 'test_helper' + +class ReppV1InvoicesSendTest < ActionDispatch::IntegrationTest + def setup + @user = users(:api_bestnames) + token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}") + token = "Basic #{token}" + + @auth_headers = { 'Authorization' => token } + end + + def test_sends_invoice_to_recipient + invoice = invoices(:one) + recipient = 'donaldtrump@yandex.ru' + request_body = { + invoice: { + id: invoice.id, + recipient: recipient, + }, + } + post "/repp/v1/invoices/#{invoice.id}/send_to_recipient", headers: @auth_headers, + params: request_body + json = JSON.parse(response.body, symbolize_names: true) + + assert_equal 1, invoice.number + + assert_response :ok + assert_equal 1000, json[:code] + assert_equal 'Command completed successfully', json[:message] + + assert_equal json[:data][:invoice][:id], invoice.id + assert_equal json[:data][:invoice][:recipient], recipient + email = ActionMailer::Base.deliveries.last + assert_emails 1 + assert_equal [recipient], email.to + assert_equal 'Invoice no. 1', email.subject + assert email.attachments['invoice-1.pdf'] + end +end \ No newline at end of file diff --git a/test/integration/repp/v1/invoices/show_test.rb b/test/integration/repp/v1/invoices/show_test.rb new file mode 100644 index 000000000..74feb42ac --- /dev/null +++ b/test/integration/repp/v1/invoices/show_test.rb @@ -0,0 +1,33 @@ +require 'test_helper' + +class ReppV1InvoicesShowTest < ActionDispatch::IntegrationTest + def setup + @user = users(:api_bestnames) + token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}") + token = "Basic #{token}" + + @auth_headers = { 'Authorization' => token } + end + + def test_returns_error_when_not_found + get repp_v1_invoice_path(id: 'definitelynotexistant'), headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :not_found + assert_equal 2303, json[:code] + assert_equal 'Object does not exist', json[:message] + end + + def test_shows_existing_invoice + invoice = @user.registrar.invoices.first + + get repp_v1_invoice_path(id: invoice.id), headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal 1000, json[:code] + assert_equal 'Command completed successfully', json[:message] + + assert_equal invoice.id, json[:data][:invoice][:id] + end +end \ No newline at end of file diff --git a/test/integration/repp/v1/registrar/auth/check_info_test.rb b/test/integration/repp/v1/registrar/auth/check_info_test.rb new file mode 100644 index 000000000..65b2cf5f4 --- /dev/null +++ b/test/integration/repp/v1/registrar/auth/check_info_test.rb @@ -0,0 +1,39 @@ +require 'test_helper' + +class ReppV1RegistrarAuthCheckInfoTest < ActionDispatch::IntegrationTest + def setup + @user = users(:api_bestnames) + token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}") + token = "Basic #{token}" + + @auth_headers = { 'Authorization' => token } + end + + def test_returns_valid_user_auth_values + get '/repp/v1/registrar/auth', headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal 1000, json[:code] + assert_equal 'Command completed successfully', json[:message] + + assert_equal json[:data][:username], @user.username + assert json[:data][:roles].include? 'super' + assert_equal json[:data][:registrar_name], 'Best Names' + assert_equal json[:data][:balance][:amount].to_f, @user.registrar.cash_account.balance + assert json[:data][:abilities].is_a? Hash + end + + def test_invalid_user_login + token = Base64.encode64("#{@user.username}:0066600") + token = "Basic #{token}" + + auth_headers = { 'Authorization' => token } + + get '/repp/v1/registrar/auth', headers: auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :unauthorized + assert_equal json[:message], 'Invalid authorization information' + end +end \ No newline at end of file diff --git a/test/integration/repp/v1/registrar/auth/switch_user_test.rb b/test/integration/repp/v1/registrar/auth/switch_user_test.rb new file mode 100644 index 000000000..beb5c0727 --- /dev/null +++ b/test/integration/repp/v1/registrar/auth/switch_user_test.rb @@ -0,0 +1,52 @@ +require 'test_helper' + +class ReppV1RegistrarSwitchUserTest < ActionDispatch::IntegrationTest + def setup + @user = users(:api_bestnames) + token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}") + token = "Basic #{token}" + + @auth_headers = { 'Authorization' => token } + end + + def test_switches_to_linked_api_user + new_user = users(:api_goodnames) + new_user.update(identity_code: '1234') + request_body = { + auth: { + new_user_id: new_user.id, + }, + } + + put '/repp/v1/registrar/auth/switch_user', headers: @auth_headers, params: request_body + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal 1000, json[:code] + assert_equal "You are now signed in as a user \"#{new_user.username}\"", json[:message] + + user_token = Base64.urlsafe_encode64("#{new_user.username}:#{new_user.plain_text_password}") + assert_equal json[:data][:token], user_token + assert_equal json[:data][:registrar][:username], new_user.username + assert json[:data][:registrar][:roles].include? 'super' + assert_equal json[:data][:registrar][:registrar_name], 'Good Names' + assert_equal json[:data][:registrar][:balance][:amount].to_f, new_user.registrar.cash_account.balance + assert json[:data][:registrar][:abilities].is_a? Hash + end + + def test_switches_to_unlinked_api_user + new_user = users(:api_goodnames) + new_user.update(identity_code: '4444') + request_body = { + auth: { + new_user_id: new_user.id, + }, + } + + put '/repp/v1/registrar/auth/switch_user', headers: @auth_headers, params: request_body + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :bad_request + assert_equal 'Cannot switch to unlinked user', json[:message] + end +end \ No newline at end of file diff --git a/test/integration/repp/v1/registrar/auth/tara_callback_test.rb b/test/integration/repp/v1/registrar/auth/tara_callback_test.rb new file mode 100644 index 000000000..56d881c87 --- /dev/null +++ b/test/integration/repp/v1/registrar/auth/tara_callback_test.rb @@ -0,0 +1,46 @@ +require 'test_helper' + +class ReppV1RegistrarAuthTaraCallbackTest < ActionDispatch::IntegrationTest + def setup + @user = users(:api_bestnames) + username = nil + password = nil + token = Base64.encode64("#{username}:#{password}") + token = "Basic #{token}" + + @auth_headers = { 'Authorization' => token } + end + + def test_validates_user_from_omniauth_params + request_body = { + auth: { + uid: 'EE1234', + }, + } + + post '/repp/v1/registrar/auth/tara_callback', headers: @auth_headers, params: request_body + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal 1000, json[:code] + assert_equal 'Command completed successfully', json[:message] + + user_token = Base64.urlsafe_encode64("#{@user.username}:#{@user.plain_text_password}") + assert_equal json[:data][:username], @user.username + assert_equal json[:data][:token], user_token + end + + def test_invalidates_user_with_wrong_omniauth_params + request_body = { + auth: { + uid: '33333', + }, + } + + post '/repp/v1/registrar/auth/tara_callback', headers: @auth_headers, params: request_body + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :bad_request + assert_equal 'No such user', json[:message] + end +end \ No newline at end of file diff --git a/test/integration/repp/v1/registrar/summary_test.rb b/test/integration/repp/v1/registrar/summary_test.rb new file mode 100644 index 000000000..257eb067f --- /dev/null +++ b/test/integration/repp/v1/registrar/summary_test.rb @@ -0,0 +1,44 @@ +require 'test_helper' + +class ReppV1RegistrarSummaryTest < ActionDispatch::IntegrationTest + def setup + @user = users(:api_bestnames) + token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}") + token = "Basic #{token}" + + @auth_headers = { 'Authorization' => token } + end + + def test_checks_user_summary_info + get '/repp/v1/registrar/summary', headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal 1000, json[:code] + assert_equal 'Command completed successfully', json[:message] + + assert_equal json[:data][:username], @user.username + assert_equal json[:data][:registrar_name], 'Best Names' + assert_equal json[:data][:domains], @user.registrar.domains.count + assert_equal json[:data][:contacts], @user.registrar.contacts.count + assert json[:data][:notification].is_a? Hash + assert_equal json[:data][:notifications_count], @user.unread_notifications.count + end + + def test_checks_limited_user_summary_info + @user.update(roles: ['billing']) + get '/repp/v1/registrar/summary', headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal 1000, json[:code] + assert_equal 'Command completed successfully', json[:message] + + assert_equal json[:data][:username], @user.username + assert_equal json[:data][:registrar_name], 'Best Names' + assert_nil json[:data][:domains] + assert_nil json[:data][:contacts] + assert_nil json[:data][:notification] + assert_nil json[:data][:notifications_count] + end +end \ No newline at end of file From eecd35a4c378396339878e838f59f0a742a928eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergei=20Ts=C3=B5ganov?= Date: Fri, 10 Jun 2022 09:49:52 +0300 Subject: [PATCH 17/81] Fixed summary info response --- app/controllers/repp/v1/registrar/summary_controller.rb | 4 ++-- app/models/registrar.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/controllers/repp/v1/registrar/summary_controller.rb b/app/controllers/repp/v1/registrar/summary_controller.rb index d3865bed2..ae3bfa579 100644 --- a/app/controllers/repp/v1/registrar/summary_controller.rb +++ b/app/controllers/repp/v1/registrar/summary_controller.rb @@ -71,8 +71,8 @@ module Repp data[:registrar_name] = registrar.name data[:registrar_reg_no] = registrar.reg_no data[:last_login_date] = last_login_date - data[:domains] = registrar.domains.count if can? :view, Depp::Domain - data[:contacts] = registrar.contacts.count if can? :view, Depp::Contact + data[:domains] = registrar.domains.count if can? :info, Depp::Domain + data[:contacts] = registrar.contacts.count if can? :check, Depp::Contact data[:phone] = registrar.phone data[:email] = registrar.email data[:billing_email] = registrar.billing_email diff --git a/app/models/registrar.rb b/app/models/registrar.rb index 1eba314dc..3cd2da760 100644 --- a/app/models/registrar.rb +++ b/app/models/registrar.rb @@ -98,7 +98,7 @@ class Registrar < ApplicationRecord unit: 'piece', quantity: 1, price: amount, - } + }, ] ) From 3eec835316dda9e63b1d3a57c9aeee608f08a52e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergei=20Ts=C3=B5ganov?= Date: Fri, 10 Jun 2022 13:33:20 +0300 Subject: [PATCH 18/81] Fixed summary controller --- app/controllers/repp/v1/registrar/summary_controller.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/repp/v1/registrar/summary_controller.rb b/app/controllers/repp/v1/registrar/summary_controller.rb index ae3bfa579..a5cda278f 100644 --- a/app/controllers/repp/v1/registrar/summary_controller.rb +++ b/app/controllers/repp/v1/registrar/summary_controller.rb @@ -71,8 +71,8 @@ module Repp data[:registrar_name] = registrar.name data[:registrar_reg_no] = registrar.reg_no data[:last_login_date] = last_login_date - data[:domains] = registrar.domains.count if can? :info, Depp::Domain - data[:contacts] = registrar.contacts.count if can? :check, Depp::Contact + data[:domains] = registrar.domains.count + data[:contacts] = registrar.contacts.count data[:phone] = registrar.phone data[:email] = registrar.email data[:billing_email] = registrar.billing_email From 2cdf9f8b959aeef93b4d63a6fc0fab68f7ae7d21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergei=20Ts=C3=B5ganov?= Date: Fri, 10 Jun 2022 13:44:19 +0300 Subject: [PATCH 19/81] Fixed summary controller --- test/integration/repp/v1/registrar/summary_test.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/integration/repp/v1/registrar/summary_test.rb b/test/integration/repp/v1/registrar/summary_test.rb index 257eb067f..d8c179f9a 100644 --- a/test/integration/repp/v1/registrar/summary_test.rb +++ b/test/integration/repp/v1/registrar/summary_test.rb @@ -36,8 +36,6 @@ class ReppV1RegistrarSummaryTest < ActionDispatch::IntegrationTest assert_equal json[:data][:username], @user.username assert_equal json[:data][:registrar_name], 'Best Names' - assert_nil json[:data][:domains] - assert_nil json[:data][:contacts] assert_nil json[:data][:notification] assert_nil json[:data][:notifications_count] end From d84867555782f47aad224d1505481606bf33ff2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergei=20Ts=C3=B5ganov?= Date: Fri, 10 Jun 2022 16:55:52 +0300 Subject: [PATCH 20/81] Fixed invoice status search --- app/controllers/repp/v1/invoices_controller.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/controllers/repp/v1/invoices_controller.rb b/app/controllers/repp/v1/invoices_controller.rb index 5d37d6881..32ea74bc2 100644 --- a/app/controllers/repp/v1/invoices_controller.rb +++ b/app/controllers/repp/v1/invoices_controller.rb @@ -86,6 +86,7 @@ module Repp account_activity_created_at_lteq account_activity_id_not_null account_activity_id_null + cancelled_at_null cancelled_at_not_null number_gteq number_lteq total_gteq total_lteq s] + [s: []]) From 7ec5c914bc30f8c89ccbf856fb71ce8582d02dd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergei=20Ts=C3=B5ganov?= Date: Mon, 13 Jun 2022 15:33:37 +0300 Subject: [PATCH 21/81] Add certs control --- app/controllers/repp/v1/base_controller.rb | 9 +++++---- app/controllers/repp/v1/registrar/auth_controller.rb | 10 ++++++++++ app/models/api_user.rb | 2 ++ 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/app/controllers/repp/v1/base_controller.rb b/app/controllers/repp/v1/base_controller.rb index 5ab910278..8e0414352 100644 --- a/app/controllers/repp/v1/base_controller.rb +++ b/app/controllers/repp/v1/base_controller.rb @@ -103,14 +103,15 @@ module Repp def authenticate_user username, password = Base64.urlsafe_decode64(basic_token).split(':') - @current_user ||= ApiUser.find_by(username: username, plain_text_password: password, - active: true) + @current_user ||= ApiUser.find_by(username: username, plain_text_password: password) + user_active = @current_user.active? - return if @current_user + return if @current_user && user_active raise(ArgumentError) rescue NoMethodError, ArgumentError - @response = { code: 2202, message: 'Invalid authorization information' } + @response = { code: 2202, message: 'Invalid authorization information', + data: { username: username, password: password, active: user_active } } render(json: @response, status: :unauthorized) end diff --git a/app/controllers/repp/v1/registrar/auth_controller.rb b/app/controllers/repp/v1/registrar/auth_controller.rb index ccc7d2e66..06dc9092f 100644 --- a/app/controllers/repp/v1/registrar/auth_controller.rb +++ b/app/controllers/repp/v1/registrar/auth_controller.rb @@ -9,6 +9,11 @@ module Repp desc 'check user auth info and return data' def index registrar = current_user.registrar + unless client_certs_ok + handle_non_epp_errors(current_user, 'Invalid certificate') + return + end + render_success(data: auth_values_to_data(registrar: registrar)) end @@ -43,6 +48,11 @@ module Repp def auth_params params.require(:auth).permit(:uid, :new_user_id) end + + def client_certs_ok + current_user.pki_ok?(request.env['HTTP_SSL_CLIENT_CERT'], + request.env['HTTP_SSL_CLIENT_S_DN_CN'], api: false) + end end end end diff --git a/app/models/api_user.rb b/app/models/api_user.rb index d357a2e75..f73fe616a 100644 --- a/app/models/api_user.rb +++ b/app/models/api_user.rb @@ -61,6 +61,8 @@ class ApiUser < User end def pki_ok?(crt, com, api: true) + p crt + p com return false if crt.blank? || com.blank? origin = api ? certificates.api : certificates.registrar From 9f6c628453561674d7df215f145b7f568414c043 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergei=20Ts=C3=B5ganov?= Date: Mon, 13 Jun 2022 16:26:42 +0300 Subject: [PATCH 22/81] Added client cert control to every REPP APi request --- app/controllers/repp/v1/base_controller.rb | 10 ++++++++++ app/controllers/repp/v1/registrar/auth_controller.rb | 11 +---------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/app/controllers/repp/v1/base_controller.rb b/app/controllers/repp/v1/base_controller.rb index 8e0414352..da906383c 100644 --- a/app/controllers/repp/v1/base_controller.rb +++ b/app/controllers/repp/v1/base_controller.rb @@ -7,6 +7,7 @@ module Repp before_action :authenticate_user before_action :validate_webclient_ca before_action :check_ip_restriction + before_action :validate_client_certs before_action :set_paper_trail_whodunnit private @@ -144,6 +145,15 @@ module Repp render(json: @response, status: :unauthorized) end + def validate_client_certs + return if Rails.env.development? || Rails.env.test? + return if @current_user.pki_ok?(request.env['HTTP_SSL_CLIENT_CERT'], + request.env['HTTP_SSL_CLIENT_S_DN_CN'], api: false) + + @response = { code: 2202, message: 'Invalid certificate' } + render(json: @response, status: :unauthorized) + end + def logger Rails.logger end diff --git a/app/controllers/repp/v1/registrar/auth_controller.rb b/app/controllers/repp/v1/registrar/auth_controller.rb index 06dc9092f..a93d75c36 100644 --- a/app/controllers/repp/v1/registrar/auth_controller.rb +++ b/app/controllers/repp/v1/registrar/auth_controller.rb @@ -4,16 +4,12 @@ module Repp class AuthController < BaseController skip_before_action :authenticate_user, only: :tara_callback skip_before_action :check_ip_restriction, only: :tara_callback + skip_before_action :validate_client_certs, only: :tara_callback api :GET, 'repp/v1/registrar/auth' desc 'check user auth info and return data' def index registrar = current_user.registrar - unless client_certs_ok - handle_non_epp_errors(current_user, 'Invalid certificate') - return - end - render_success(data: auth_values_to_data(registrar: registrar)) end @@ -48,11 +44,6 @@ module Repp def auth_params params.require(:auth).permit(:uid, :new_user_id) end - - def client_certs_ok - current_user.pki_ok?(request.env['HTTP_SSL_CLIENT_CERT'], - request.env['HTTP_SSL_CLIENT_S_DN_CN'], api: false) - end end end end From a44ac5359db975139820a08aace40ec57d003cd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergei=20Ts=C3=B5ganov?= Date: Tue, 14 Jun 2022 11:14:27 +0300 Subject: [PATCH 23/81] Renamed back accounts controller & modified auth validations --- ...ccount_controller.rb => accounts_controller.rb} | 14 +++++++------- app/controllers/repp/v1/base_controller.rb | 7 ++++--- app/models/api_user.rb | 2 -- config/routes.rb | 4 ++-- 4 files changed, 13 insertions(+), 14 deletions(-) rename app/controllers/repp/v1/{account_controller.rb => accounts_controller.rb} (92%) diff --git a/app/controllers/repp/v1/account_controller.rb b/app/controllers/repp/v1/accounts_controller.rb similarity index 92% rename from app/controllers/repp/v1/account_controller.rb rename to app/controllers/repp/v1/accounts_controller.rb index 85d79fe68..0204e7943 100644 --- a/app/controllers/repp/v1/account_controller.rb +++ b/app/controllers/repp/v1/accounts_controller.rb @@ -1,9 +1,9 @@ module Repp module V1 - class AccountController < BaseController # rubocop:disable Metrics/ClassLength + class AccountsController < BaseController # rubocop:disable Metrics/ClassLength load_and_authorize_resource - api :get, '/repp/v1/account' + api :get, '/repp/v1/accounts' desc 'Get all activities' def index records = current_user.registrar.cash_account.activities @@ -20,7 +20,7 @@ module Repp types_for_select: AccountActivity.types_for_select }) end - api :get, '/repp/v1/account/details' + api :get, '/repp/v1/accounts/details' desc 'Get current registrar account details' def details registrar = current_user.registrar @@ -34,7 +34,7 @@ module Repp render_success(data: resp) end - api :put, '/repp/v1/account' + api :put, '/repp/v1/accounts' desc 'Update current registrar account details' def update registrar = current_user.registrar @@ -47,7 +47,7 @@ module Repp message: I18n.t('registrar.account.update.saved')) end - api :post, '/repp/v1/account/update_auto_reload_balance' + api :post, '/repp/v1/accounts/update_auto_reload_balance' desc 'Enable current registrar balance auto reload' def update_auto_reload_balance type = BalanceAutoReloadTypes::Threshold.new(type_params) @@ -62,7 +62,7 @@ module Repp message: I18n.t('registrar.settings.balance_auto_reload.update.saved')) end - api :get, '/repp/v1/account/disable_auto_reload_balance' + api :get, '/repp/v1/accounts/disable_auto_reload_balance' desc 'Disable current registrar balance auto reload' def disable_auto_reload_balance registrar = current_user.registrar @@ -73,7 +73,7 @@ module Repp message: I18n.t('registrar.settings.balance_auto_reload.destroy.disabled')) end - api :get, '/repp/v1/account/balance' + api :get, '/repp/v1/accounts/balance' desc "Get account's balance" def balance resp = { balance: current_user.registrar.cash_account.balance, diff --git a/app/controllers/repp/v1/base_controller.rb b/app/controllers/repp/v1/base_controller.rb index da906383c..977df0323 100644 --- a/app/controllers/repp/v1/base_controller.rb +++ b/app/controllers/repp/v1/base_controller.rb @@ -6,8 +6,8 @@ module Repp around_action :log_request before_action :authenticate_user before_action :validate_webclient_ca - before_action :check_ip_restriction before_action :validate_client_certs + before_action :check_ip_restriction before_action :set_paper_trail_whodunnit private @@ -126,7 +126,7 @@ module Repp end def webclient_request? - return if Rails.env.test? + return false if Rails.env.test? || Rails.env.development? ENV['webclient_ips'].split(',').map(&:strip).include?(request.ip) end @@ -147,8 +147,9 @@ module Repp def validate_client_certs return if Rails.env.development? || Rails.env.test? + return if webclient_request? return if @current_user.pki_ok?(request.env['HTTP_SSL_CLIENT_CERT'], - request.env['HTTP_SSL_CLIENT_S_DN_CN'], api: false) + request.env['HTTP_SSL_CLIENT_S_DN_CN']) @response = { code: 2202, message: 'Invalid certificate' } render(json: @response, status: :unauthorized) diff --git a/app/models/api_user.rb b/app/models/api_user.rb index f73fe616a..d357a2e75 100644 --- a/app/models/api_user.rb +++ b/app/models/api_user.rb @@ -61,8 +61,6 @@ class ApiUser < User end def pki_ok?(crt, com, api: true) - p crt - p com return false if crt.blank? || com.blank? origin = api ? certificates.api : certificates.registrar diff --git a/config/routes.rb b/config/routes.rb index 2ea79e1c0..81b6dc95c 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -75,9 +75,9 @@ Rails.application.routes.draw do end end - resource :account, controller: :account, only: %i[index update] do + resource :accounts, only: %i[index update] do collection do - get '/', to: 'account#index' + get '/', to: 'accounts#index' get 'balance' get 'details' post 'update_auto_reload_balance' From 1b14a9d012f90b642ae72a1d1ae48dfbfa6fbcf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergei=20Ts=C3=B5ganov?= Date: Tue, 14 Jun 2022 11:27:37 +0300 Subject: [PATCH 24/81] Updated tests after renaming REPP API accounts controller --- .../v1/{account => accounts}/activities_list_test.rb | 12 ++++++------ .../repp/v1/{account => accounts}/balance_test.rb | 4 ++-- .../repp/v1/{account => accounts}/details_test.rb | 4 ++-- .../update_auto_reload_balance_test.rb | 8 ++++---- .../v1/{account => accounts}/update_details_test.rb | 4 ++-- 5 files changed, 16 insertions(+), 16 deletions(-) rename test/integration/repp/v1/{account => accounts}/activities_list_test.rb (81%) rename test/integration/repp/v1/{account => accounts}/balance_test.rb (93%) rename test/integration/repp/v1/{account => accounts}/details_test.rb (81%) rename test/integration/repp/v1/{account => accounts}/update_auto_reload_balance_test.rb (85%) rename test/integration/repp/v1/{account => accounts}/update_details_test.rb (83%) diff --git a/test/integration/repp/v1/account/activities_list_test.rb b/test/integration/repp/v1/accounts/activities_list_test.rb similarity index 81% rename from test/integration/repp/v1/account/activities_list_test.rb rename to test/integration/repp/v1/accounts/activities_list_test.rb index b492cc098..2038815a1 100644 --- a/test/integration/repp/v1/account/activities_list_test.rb +++ b/test/integration/repp/v1/accounts/activities_list_test.rb @@ -1,6 +1,6 @@ require 'test_helper' -class ReppV1AccountActivitiesListTest < ActionDispatch::IntegrationTest +class ReppV1AccountsActivitiesListTest < ActionDispatch::IntegrationTest def setup @user = users(:api_bestnames) token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}") @@ -10,7 +10,7 @@ class ReppV1AccountActivitiesListTest < ActionDispatch::IntegrationTest end def test_returns_account_activities - get repp_v1_account_path, headers: @auth_headers + get repp_v1_accounts_path, headers: @auth_headers json = JSON.parse(response.body, symbolize_names: true) assert_response :ok @@ -22,7 +22,7 @@ class ReppV1AccountActivitiesListTest < ActionDispatch::IntegrationTest end def test_respects_limit - get repp_v1_account_path(limit: 1), headers: @auth_headers + get repp_v1_accounts_path(limit: 1), headers: @auth_headers json = JSON.parse(response.body, symbolize_names: true) assert_response :ok @@ -32,7 +32,7 @@ class ReppV1AccountActivitiesListTest < ActionDispatch::IntegrationTest def test_respects_offset offset = 1 - get repp_v1_account_path(offset: offset), headers: @auth_headers + get repp_v1_accounts_path(offset: offset), headers: @auth_headers json = JSON.parse(response.body, symbolize_names: true) assert_response :ok @@ -44,7 +44,7 @@ class ReppV1AccountActivitiesListTest < ActionDispatch::IntegrationTest search_params = { description_matches: '%renew%', } - get repp_v1_account_path(q: search_params), headers: @auth_headers + get repp_v1_accounts_path(q: search_params), headers: @auth_headers json = JSON.parse(response.body, symbolize_names: true) assert_response :ok @@ -58,7 +58,7 @@ class ReppV1AccountActivitiesListTest < ActionDispatch::IntegrationTest sort_params = { s: 'activity_type asc', } - get repp_v1_account_path(q: sort_params), headers: @auth_headers + get repp_v1_accounts_path(q: sort_params), headers: @auth_headers json = JSON.parse(response.body, symbolize_names: true) assert_response :ok diff --git a/test/integration/repp/v1/account/balance_test.rb b/test/integration/repp/v1/accounts/balance_test.rb similarity index 93% rename from test/integration/repp/v1/account/balance_test.rb rename to test/integration/repp/v1/accounts/balance_test.rb index a4d41ed53..3fd25f3e7 100644 --- a/test/integration/repp/v1/account/balance_test.rb +++ b/test/integration/repp/v1/accounts/balance_test.rb @@ -13,7 +13,7 @@ class ReppV1BalanceTest < ActionDispatch::IntegrationTest def test_can_query_balance - get '/repp/v1/account/balance', headers: @auth_headers + get '/repp/v1/accounts/balance', headers: @auth_headers json = JSON.parse(response.body, symbolize_names: true) assert_response :ok @@ -30,7 +30,7 @@ class ReppV1BalanceTest < ActionDispatch::IntegrationTest started_from = "2010-07-05" end_to = DateTime.current.to_date.to_s(:db) - get "/repp/v1/account/balance?detailed=true", headers: @auth_headers + get "/repp/v1/accounts/balance?detailed=true", headers: @auth_headers json = JSON.parse(response.body, symbolize_names: true) assert_response :ok diff --git a/test/integration/repp/v1/account/details_test.rb b/test/integration/repp/v1/accounts/details_test.rb similarity index 81% rename from test/integration/repp/v1/account/details_test.rb rename to test/integration/repp/v1/accounts/details_test.rb index b0d359d74..30acb5eb6 100644 --- a/test/integration/repp/v1/account/details_test.rb +++ b/test/integration/repp/v1/accounts/details_test.rb @@ -1,6 +1,6 @@ require 'test_helper' -class ReppV1AccountDetailsTest < ActionDispatch::IntegrationTest +class ReppV1AccountsDetailsTest < ActionDispatch::IntegrationTest def setup @user = users(:api_bestnames) token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}") @@ -10,7 +10,7 @@ class ReppV1AccountDetailsTest < ActionDispatch::IntegrationTest end def test_returns_account_details - get '/repp/v1/account/details', headers: @auth_headers + get '/repp/v1/accounts/details', headers: @auth_headers json = JSON.parse(response.body, symbolize_names: true) assert_response :ok diff --git a/test/integration/repp/v1/account/update_auto_reload_balance_test.rb b/test/integration/repp/v1/accounts/update_auto_reload_balance_test.rb similarity index 85% rename from test/integration/repp/v1/account/update_auto_reload_balance_test.rb rename to test/integration/repp/v1/accounts/update_auto_reload_balance_test.rb index 6201750c3..11a8d08ba 100644 --- a/test/integration/repp/v1/account/update_auto_reload_balance_test.rb +++ b/test/integration/repp/v1/accounts/update_auto_reload_balance_test.rb @@ -1,6 +1,6 @@ require 'test_helper' -class ReppV1AccountUpdateAutoReloadBalanceTest < ActionDispatch::IntegrationTest +class ReppV1AccountsUpdateAutoReloadBalanceTest < ActionDispatch::IntegrationTest def setup @user = users(:api_bestnames) token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}") @@ -21,7 +21,7 @@ class ReppV1AccountUpdateAutoReloadBalanceTest < ActionDispatch::IntegrationTest assert_nil @user.registrar.settings['balance_auto_reload'] - post '/repp/v1/account/update_auto_reload_balance', headers: @auth_headers, + post '/repp/v1/accounts/update_auto_reload_balance', headers: @auth_headers, params: request_body json = JSON.parse(response.body, symbolize_names: true) assert_response :ok @@ -44,7 +44,7 @@ class ReppV1AccountUpdateAutoReloadBalanceTest < ActionDispatch::IntegrationTest } Setting.minimum_deposit = min_deposit - post '/repp/v1/account/update_auto_reload_balance', headers: @auth_headers, + post '/repp/v1/accounts/update_auto_reload_balance', headers: @auth_headers, params: request_body json = JSON.parse(response.body, symbolize_names: true) @@ -55,7 +55,7 @@ class ReppV1AccountUpdateAutoReloadBalanceTest < ActionDispatch::IntegrationTest end def test_disables_auto_reload_balance - get '/repp/v1/account/disable_auto_reload_balance', headers: @auth_headers + get '/repp/v1/accounts/disable_auto_reload_balance', headers: @auth_headers json = JSON.parse(response.body, symbolize_names: true) assert_response :ok diff --git a/test/integration/repp/v1/account/update_details_test.rb b/test/integration/repp/v1/accounts/update_details_test.rb similarity index 83% rename from test/integration/repp/v1/account/update_details_test.rb rename to test/integration/repp/v1/accounts/update_details_test.rb index 3ab415cd8..c1275ddeb 100644 --- a/test/integration/repp/v1/account/update_details_test.rb +++ b/test/integration/repp/v1/accounts/update_details_test.rb @@ -1,6 +1,6 @@ require 'test_helper' -class ReppV1AccountUpdateDetailsTest < ActionDispatch::IntegrationTest +class ReppV1AccountsUpdateDetailsTest < ActionDispatch::IntegrationTest def setup @user = users(:api_bestnames) token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}") @@ -17,7 +17,7 @@ class ReppV1AccountUpdateDetailsTest < ActionDispatch::IntegrationTest }, } - put '/repp/v1/account', headers: @auth_headers, params: request_body + put '/repp/v1/accounts', headers: @auth_headers, params: request_body json = JSON.parse(response.body, symbolize_names: true) assert_response :ok From 6c9fb2b025d5d376dc3f2498bff36c545ec1ff28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergei=20Ts=C3=B5ganov?= Date: Tue, 14 Jun 2022 11:33:59 +0300 Subject: [PATCH 25/81] Fixed codeclimate issue --- app/controllers/repp/v1/invoices_controller.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/controllers/repp/v1/invoices_controller.rb b/app/controllers/repp/v1/invoices_controller.rb index 32ea74bc2..8add1920b 100644 --- a/app/controllers/repp/v1/invoices_controller.rb +++ b/app/controllers/repp/v1/invoices_controller.rb @@ -8,11 +8,9 @@ module Repp desc 'Get all invoices' def index records = current_user.registrar.invoices - q = records.ransack(search_params) q.sorts = 'created_at desc' if q.sorts.empty? invoices = q.result(distinct: true) - limited_invoices = invoices.limit(limit).offset(offset) .includes(:items, :account_activity, :buyer) From de5872fb40b1338e26bfd9d669b6ba783ad1caaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergei=20Ts=C3=B5ganov?= Date: Wed, 15 Jun 2022 12:56:54 +0300 Subject: [PATCH 26/81] Moved switch user method to accounts --- app/controllers/repp/v1/accounts_controller.rb | 18 +++++++++++++++++- app/controllers/repp/v1/invoices_controller.rb | 1 + .../repp/v1/registrar/auth_controller.rb | 16 ---------------- config/routes.rb | 4 +++- .../auth => accounts}/switch_user_test.rb | 10 +++++----- 5 files changed, 26 insertions(+), 23 deletions(-) rename test/integration/repp/v1/{registrar/auth => accounts}/switch_user_test.rb (84%) diff --git a/app/controllers/repp/v1/accounts_controller.rb b/app/controllers/repp/v1/accounts_controller.rb index 0204e7943..301bed95f 100644 --- a/app/controllers/repp/v1/accounts_controller.rb +++ b/app/controllers/repp/v1/accounts_controller.rb @@ -73,6 +73,22 @@ module Repp message: I18n.t('registrar.settings.balance_auto_reload.destroy.disabled')) end + api :put, '/repp/v1/accounts/switch_user' + desc 'Switch user to another api user' + def switch_user + new_user = ApiUser.find(account_params[:new_user_id]) + unless current_user.linked_with?(new_user) + handle_non_epp_errors(new_user, 'Cannot switch to unlinked user') + return + end + + @current_user = new_user + data = auth_values_to_data(registrar: current_user.registrar) + message = I18n.t('registrar.current_user.switch.switched', new_user: new_user) + token = Base64.urlsafe_encode64("#{new_user.username}:#{new_user.plain_text_password}") + render_success(data: { token: token, registrar: data }, message: message) + end + api :get, '/repp/v1/accounts/balance' desc "Get account's balance" def balance @@ -90,7 +106,7 @@ module Repp private def account_params - params.require(:account).permit(:billing_email, :iban) + params.require(:account).permit(:billing_email, :iban, :new_user_id) end def index_params diff --git a/app/controllers/repp/v1/invoices_controller.rb b/app/controllers/repp/v1/invoices_controller.rb index 8add1920b..bd807e7b8 100644 --- a/app/controllers/repp/v1/invoices_controller.rb +++ b/app/controllers/repp/v1/invoices_controller.rb @@ -11,6 +11,7 @@ module Repp q = records.ransack(search_params) q.sorts = 'created_at desc' if q.sorts.empty? invoices = q.result(distinct: true) + limited_invoices = invoices.limit(limit).offset(offset) .includes(:items, :account_activity, :buyer) diff --git a/app/controllers/repp/v1/registrar/auth_controller.rb b/app/controllers/repp/v1/registrar/auth_controller.rb index a93d75c36..a15230c78 100644 --- a/app/controllers/repp/v1/registrar/auth_controller.rb +++ b/app/controllers/repp/v1/registrar/auth_controller.rb @@ -23,22 +23,6 @@ module Repp render_success(data: { token: token, username: user.username }) end - api :put, '/repp/v1/registrar/auth/switch_user' - desc 'Switch session to another api user' - def switch_user - new_user = ApiUser.find(auth_params[:new_user_id]) - unless current_user.linked_with?(new_user) - handle_non_epp_errors(new_user, 'Cannot switch to unlinked user') - return - end - - @current_user = new_user - data = auth_values_to_data(registrar: current_user.registrar) - message = I18n.t('registrar.current_user.switch.switched', new_user: new_user) - token = Base64.urlsafe_encode64("#{new_user.username}:#{new_user.plain_text_password}") - render_success(data: { token: token, registrar: data }, message: message) - end - private def auth_params diff --git a/config/routes.rb b/config/routes.rb index 81b6dc95c..180a4687d 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -83,6 +83,9 @@ Rails.application.routes.draw do post 'update_auto_reload_balance' get 'disable_auto_reload_balance' end + member do + put 'switch_user' + end end resources :invoices, only: %i[index show] do collection do @@ -117,7 +120,6 @@ Rails.application.routes.draw do resources :auth, only: %i[index] do collection do post '/tara_callback', to: 'auth#tara_callback' - put '/switch_user', to: 'auth#switch_user' end end end diff --git a/test/integration/repp/v1/registrar/auth/switch_user_test.rb b/test/integration/repp/v1/accounts/switch_user_test.rb similarity index 84% rename from test/integration/repp/v1/registrar/auth/switch_user_test.rb rename to test/integration/repp/v1/accounts/switch_user_test.rb index beb5c0727..c8935d038 100644 --- a/test/integration/repp/v1/registrar/auth/switch_user_test.rb +++ b/test/integration/repp/v1/accounts/switch_user_test.rb @@ -1,6 +1,6 @@ require 'test_helper' -class ReppV1RegistrarSwitchUserTest < ActionDispatch::IntegrationTest +class ReppV1AccountsSwitchUserTest < ActionDispatch::IntegrationTest def setup @user = users(:api_bestnames) token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}") @@ -13,12 +13,12 @@ class ReppV1RegistrarSwitchUserTest < ActionDispatch::IntegrationTest new_user = users(:api_goodnames) new_user.update(identity_code: '1234') request_body = { - auth: { + account: { new_user_id: new_user.id, }, } - put '/repp/v1/registrar/auth/switch_user', headers: @auth_headers, params: request_body + put '/repp/v1/accounts/switch_user', headers: @auth_headers, params: request_body json = JSON.parse(response.body, symbolize_names: true) assert_response :ok @@ -38,12 +38,12 @@ class ReppV1RegistrarSwitchUserTest < ActionDispatch::IntegrationTest new_user = users(:api_goodnames) new_user.update(identity_code: '4444') request_body = { - auth: { + account: { new_user_id: new_user.id, }, } - put '/repp/v1/registrar/auth/switch_user', headers: @auth_headers, params: request_body + put '/repp/v1/accounts/switch_user', headers: @auth_headers, params: request_body json = JSON.parse(response.body, symbolize_names: true) assert_response :bad_request From 885206d075c77098e6c58e22915fe2be1243a189 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergei=20Ts=C3=B5ganov?= Date: Mon, 20 Jun 2022 16:22:18 +0300 Subject: [PATCH 27/81] Added invoice recipient validation --- app/controllers/repp/v1/invoices_controller.rb | 9 +++++++++ app/models/registrar.rb | 2 +- app/models/user.rb | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/app/controllers/repp/v1/invoices_controller.rb b/app/controllers/repp/v1/invoices_controller.rb index bd807e7b8..aff8e5841 100644 --- a/app/controllers/repp/v1/invoices_controller.rb +++ b/app/controllers/repp/v1/invoices_controller.rb @@ -37,8 +37,17 @@ module Repp api :post, '/repp/v1/invoices/:id/send_to_recipient' desc 'Send invoice pdf to recipient' + param :invoice, Hash, required: true, desc: 'Invoice data for sending to recipient' do + param :id, String, required: true, desc: 'Invoice id' + param :recipient, String, required: true, desc: 'Invoice receipient email' + end def send_to_recipient recipient = invoice_params[:recipient] + unless recipient.present? + handle_non_epp_errors(@invoice, 'Invoice recipient cannot be empty') + return + end + InvoiceMailer.invoice_email(invoice: @invoice, recipient: recipient) .deliver_now serializer = Serializers::Repp::Invoice.new(@invoice, simplify: true) diff --git a/app/models/registrar.rb b/app/models/registrar.rb index 3cd2da760..0ea0bfb13 100644 --- a/app/models/registrar.rb +++ b/app/models/registrar.rb @@ -39,7 +39,7 @@ class Registrar < ApplicationRecord alias_attribute :contact_email, :email - WHOIS_TRIGGERS = %w(name email phone street city state zip) + WHOIS_TRIGGERS = %w[name email phone street city state zip].freeze after_commit :update_whois_records def update_whois_records diff --git a/app/models/user.rb b/app/models/user.rb index 3bb8318c3..8ee0ea05c 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -13,7 +13,7 @@ class User < ApplicationRecord def self.from_omniauth(omniauth_hash) uid = omniauth_hash['uid'] - identity_code = uid.slice(2..-1) + identity_code = uid&.slice(2..-1) # country_code = uid.slice(0..1) find_by(identity_code: identity_code, active: true) From f8ef0502a52831a38f388929d00c7f9384d6f2ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergei=20Ts=C3=B5ganov?= Date: Mon, 20 Jun 2022 16:28:10 +0300 Subject: [PATCH 28/81] Fixed codeclimate issues --- app/controllers/repp/v1/invoices_controller.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/controllers/repp/v1/invoices_controller.rb b/app/controllers/repp/v1/invoices_controller.rb index aff8e5841..29fd916a9 100644 --- a/app/controllers/repp/v1/invoices_controller.rb +++ b/app/controllers/repp/v1/invoices_controller.rb @@ -1,9 +1,10 @@ require 'serializers/repp/invoice' module Repp module V1 - class InvoicesController < BaseController + class InvoicesController < BaseController # rubocop:disable Metrics/ClassLength load_and_authorize_resource + # rubocop:disable Metrics/MethodLength api :get, '/repp/v1/invoices' desc 'Get all invoices' def index @@ -18,6 +19,7 @@ module Repp render_success(data: { invoices: serialized_invoices(limited_invoices), count: invoices.count }) end + # rubocop:enable Metrics/MethodLength api :get, '/repp/v1/invoices/:id' desc 'Get a specific invoice' @@ -43,7 +45,7 @@ module Repp end def send_to_recipient recipient = invoice_params[:recipient] - unless recipient.present? + if recipient.blank? handle_non_epp_errors(@invoice, 'Invoice recipient cannot be empty') return end From 87b503ec799a4201c43b6c60de78177e1524ffe1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergei=20Ts=C3=B5ganov?= Date: Tue, 21 Jun 2022 12:40:34 +0300 Subject: [PATCH 29/81] Updated tara callback endpoint --- app/controllers/repp/v1/registrar/auth_controller.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/controllers/repp/v1/registrar/auth_controller.rb b/app/controllers/repp/v1/registrar/auth_controller.rb index a15230c78..46c21459e 100644 --- a/app/controllers/repp/v1/registrar/auth_controller.rb +++ b/app/controllers/repp/v1/registrar/auth_controller.rb @@ -17,7 +17,8 @@ module Repp desc 'check tara callback omniauth user info and return token' def tara_callback user = ApiUser.from_omniauth(auth_params) - handle_non_epp_errors(user, I18n.t(:no_such_user)) and return unless user && user&.active + response = { code: 401, message: I18n.t(:no_such_user), data: {} } + render(json: response, status: :unauthorized) and return unless user && user&.active token = Base64.urlsafe_encode64("#{user.username}:#{user.plain_text_password}") render_success(data: { token: token, username: user.username }) From 54b96b05e8c436a1a44c1627c6d668c1f0d79136 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergei=20Ts=C3=B5ganov?= Date: Tue, 21 Jun 2022 12:47:07 +0300 Subject: [PATCH 30/81] Updated tara callback endpoint --- test/integration/repp/v1/registrar/auth/tara_callback_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/repp/v1/registrar/auth/tara_callback_test.rb b/test/integration/repp/v1/registrar/auth/tara_callback_test.rb index 56d881c87..e39e24078 100644 --- a/test/integration/repp/v1/registrar/auth/tara_callback_test.rb +++ b/test/integration/repp/v1/registrar/auth/tara_callback_test.rb @@ -40,7 +40,7 @@ class ReppV1RegistrarAuthTaraCallbackTest < ActionDispatch::IntegrationTest post '/repp/v1/registrar/auth/tara_callback', headers: @auth_headers, params: request_body json = JSON.parse(response.body, symbolize_names: true) - assert_response :bad_request + assert_response :unauthorized assert_equal 'No such user', json[:message] end end \ No newline at end of file From 900492f38f0bbb99d3745695194a0af016591a55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergei=20Ts=C3=B5ganov?= Date: Tue, 21 Jun 2022 16:02:19 +0300 Subject: [PATCH 31/81] Updated summary endpoint --- app/controllers/repp/v1/base_controller.rb | 2 -- app/controllers/repp/v1/registrar/summary_controller.rb | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/repp/v1/base_controller.rb b/app/controllers/repp/v1/base_controller.rb index 977df0323..10c3c6026 100644 --- a/app/controllers/repp/v1/base_controller.rb +++ b/app/controllers/repp/v1/base_controller.rb @@ -163,8 +163,6 @@ module Repp data = current_user.as_json(only: %i[id username roles]) data[:registrar_name] = registrar.name data[:legaldoc_mandatory] = registrar.legaldoc_mandatory? - data[:balance] = { amount: registrar.cash_account&.balance, - currency: registrar.cash_account&.currency } data[:abilities] = Ability.new(current_user).permissions data end diff --git a/app/controllers/repp/v1/registrar/summary_controller.rb b/app/controllers/repp/v1/registrar/summary_controller.rb index a5cda278f..a0e266e93 100644 --- a/app/controllers/repp/v1/registrar/summary_controller.rb +++ b/app/controllers/repp/v1/registrar/summary_controller.rb @@ -70,6 +70,8 @@ module Repp data = current_user.as_json(only: %i[id username]) data[:registrar_name] = registrar.name data[:registrar_reg_no] = registrar.reg_no + data[:balance] = { amount: registrar.cash_account&.balance, + currency: registrar.cash_account&.currency } data[:last_login_date] = last_login_date data[:domains] = registrar.domains.count data[:contacts] = registrar.contacts.count From 872c650ef7a477b05a91af02ba9b61ce020d46ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergei=20Ts=C3=B5ganov?= Date: Tue, 21 Jun 2022 16:18:24 +0300 Subject: [PATCH 32/81] Updated tests after summary endpoint update --- test/integration/repp/v1/accounts/switch_user_test.rb | 1 - test/integration/repp/v1/registrar/summary_test.rb | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/repp/v1/accounts/switch_user_test.rb b/test/integration/repp/v1/accounts/switch_user_test.rb index c8935d038..a860fb162 100644 --- a/test/integration/repp/v1/accounts/switch_user_test.rb +++ b/test/integration/repp/v1/accounts/switch_user_test.rb @@ -30,7 +30,6 @@ class ReppV1AccountsSwitchUserTest < ActionDispatch::IntegrationTest assert_equal json[:data][:registrar][:username], new_user.username assert json[:data][:registrar][:roles].include? 'super' assert_equal json[:data][:registrar][:registrar_name], 'Good Names' - assert_equal json[:data][:registrar][:balance][:amount].to_f, new_user.registrar.cash_account.balance assert json[:data][:registrar][:abilities].is_a? Hash end diff --git a/test/integration/repp/v1/registrar/summary_test.rb b/test/integration/repp/v1/registrar/summary_test.rb index d8c179f9a..97797990a 100644 --- a/test/integration/repp/v1/registrar/summary_test.rb +++ b/test/integration/repp/v1/registrar/summary_test.rb @@ -21,6 +21,7 @@ class ReppV1RegistrarSummaryTest < ActionDispatch::IntegrationTest assert_equal json[:data][:registrar_name], 'Best Names' assert_equal json[:data][:domains], @user.registrar.domains.count assert_equal json[:data][:contacts], @user.registrar.contacts.count + assert_equal json[:data][:balance][:amount].to_f, @user.registrar.cash_account.balance assert json[:data][:notification].is_a? Hash assert_equal json[:data][:notifications_count], @user.unread_notifications.count end From 363a9a7357f816be60f75ff818c0c40fac13dc20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergei=20Ts=C3=B5ganov?= Date: Tue, 21 Jun 2022 16:25:16 +0300 Subject: [PATCH 33/81] Updated tests after summary endpoint update --- test/integration/repp/v1/registrar/auth/check_info_test.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/test/integration/repp/v1/registrar/auth/check_info_test.rb b/test/integration/repp/v1/registrar/auth/check_info_test.rb index 65b2cf5f4..03563d273 100644 --- a/test/integration/repp/v1/registrar/auth/check_info_test.rb +++ b/test/integration/repp/v1/registrar/auth/check_info_test.rb @@ -20,7 +20,6 @@ class ReppV1RegistrarAuthCheckInfoTest < ActionDispatch::IntegrationTest assert_equal json[:data][:username], @user.username assert json[:data][:roles].include? 'super' assert_equal json[:data][:registrar_name], 'Best Names' - assert_equal json[:data][:balance][:amount].to_f, @user.registrar.cash_account.balance assert json[:data][:abilities].is_a? Hash end From 7939c5065dfe8234897af96ed3bc274f824d0b3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergei=20Ts=C3=B5ganov?= Date: Thu, 7 Jul 2022 13:45:20 +0300 Subject: [PATCH 34/81] Updaded api user return data --- app/controllers/repp/v1/accounts_controller.rb | 2 +- app/controllers/repp/v1/domains/admin_contacts_controller.rb | 2 +- app/models/api_user.rb | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/controllers/repp/v1/accounts_controller.rb b/app/controllers/repp/v1/accounts_controller.rb index 301bed95f..7db2a3275 100644 --- a/app/controllers/repp/v1/accounts_controller.rb +++ b/app/controllers/repp/v1/accounts_controller.rb @@ -143,7 +143,7 @@ module Repp arr = [] users.each do |u| arr << { id: u.id, username: u.username, - role: u.roles.first } + role: u.roles.first, registrar_name: u.registrar.name } end arr diff --git a/app/controllers/repp/v1/domains/admin_contacts_controller.rb b/app/controllers/repp/v1/domains/admin_contacts_controller.rb index 6ec0e129b..5db865199 100644 --- a/app/controllers/repp/v1/domains/admin_contacts_controller.rb +++ b/app/controllers/repp/v1/domains/admin_contacts_controller.rb @@ -7,7 +7,7 @@ module Repp unless @new_contact.identical_to?(@current_contact) @epp_errors.add(:epp_errors, - msg: 'Admin contacts must be identical', + msg: 'New and current admin contacts ident data must be identical', code: '2304') end diff --git a/app/models/api_user.rb b/app/models/api_user.rb index d357a2e75..dc5cff0cc 100644 --- a/app/models/api_user.rb +++ b/app/models/api_user.rb @@ -74,6 +74,7 @@ class ApiUser < User self.class.where(identity_code: identity_code) .where("identity_code IS NOT NULL AND identity_code != ''") .where.not(id: id) + .includes(:registrar) end def linked_with?(another_api_user) From c1c2feae089a170d3df7538b9c9b613df2471cbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergei=20Ts=C3=B5ganov?= Date: Thu, 7 Jul 2022 13:52:21 +0300 Subject: [PATCH 35/81] Fixed codeclimate issue --- app/controllers/repp/v1/invoices_controller.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/controllers/repp/v1/invoices_controller.rb b/app/controllers/repp/v1/invoices_controller.rb index 29fd916a9..1c14df329 100644 --- a/app/controllers/repp/v1/invoices_controller.rb +++ b/app/controllers/repp/v1/invoices_controller.rb @@ -95,10 +95,8 @@ module Repp account_activity_created_at_gteq account_activity_created_at_lteq account_activity_id_not_null - account_activity_id_null - cancelled_at_null - cancelled_at_not_null - number_gteq number_lteq + account_activity_id_null cancelled_at_null + cancelled_at_not_null number_gteq number_lteq total_gteq total_lteq s] + [s: []]) end From c7701ce1b1fe5b167601f57acd209adb27f5eb04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergei=20Ts=C3=B5ganov?= Date: Thu, 7 Jul 2022 14:02:11 +0300 Subject: [PATCH 36/81] Updated integration test --- .../api/domain_admin_contacts_test.rb | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/test/integration/api/domain_admin_contacts_test.rb b/test/integration/api/domain_admin_contacts_test.rb index ce61cffd1..6aa412c23 100644 --- a/test/integration/api/domain_admin_contacts_test.rb +++ b/test/integration/api/domain_admin_contacts_test.rb @@ -18,19 +18,21 @@ class APIDomainAdminContactsTest < ApplicationIntegrationTest ident_country_code: 'LV') patch '/repp/v1/domains/admin_contacts', params: { current_contact_id: @admin_current.code, - new_contact_id: @admin_new.code }, - headers: { 'HTTP_AUTHORIZATION' => http_auth_key } + new_contact_id: @admin_new.code }, + headers: { 'HTTP_AUTHORIZATION' => http_auth_key } assert_response :bad_request - assert_equal ({ code: 2304, message: 'Admin contacts must be identical', data: {} }), + assert_equal ({ code: 2304, + message: 'New and current admin contacts ident data must be identical', + data: {} }), JSON.parse(response.body, symbolize_names: true) end def test_replace_all_admin_contacts_of_the_current_registrar assert @admin_new.identical_to?(@admin_current) patch '/repp/v1/domains/admin_contacts', params: { current_contact_id: @admin_current.code, - new_contact_id: @admin_new.code }, - headers: { 'HTTP_AUTHORIZATION' => http_auth_key } + new_contact_id: @admin_new.code }, + headers: { 'HTTP_AUTHORIZATION' => http_auth_key } assert_nil domains(:shop).admin_contacts.find_by(code: @admin_current.code) assert domains(:shop).admin_contacts.find_by(code: @admin_new.code) @@ -41,8 +43,8 @@ class APIDomainAdminContactsTest < ApplicationIntegrationTest domains(:airport).update!(statuses: [DomainStatus::SERVER_UPDATE_PROHIBITED]) patch '/repp/v1/domains/admin_contacts', params: { current_contact_id: @admin_current.code, - new_contact_id: @admin_new.code }, - headers: { 'HTTP_AUTHORIZATION' => http_auth_key } + new_contact_id: @admin_new.code }, + headers: { 'HTTP_AUTHORIZATION' => http_auth_key } assert domains(:airport).admin_contacts.find_by(code: @admin_current.code) end @@ -51,12 +53,13 @@ class APIDomainAdminContactsTest < ApplicationIntegrationTest domain = domains(:airport) domain.admin_contacts = [@admin_current] patch '/repp/v1/domains/admin_contacts', params: { current_contact_id: @admin_current.code, - new_contact_id: @admin_new.code }, - headers: { 'HTTP_AUTHORIZATION' => http_auth_key } + new_contact_id: @admin_new.code }, + headers: { 'HTTP_AUTHORIZATION' => http_auth_key } assert_response :ok - assert_equal ({ code: 1000, message: 'Command completed successfully', data: { affected_domains: %w[airport.test shop.test], - skipped_domains: [] }}), + assert_equal ({ code: 1000, message: 'Command completed successfully', + data: { affected_domains: %w[airport.test shop.test], + skipped_domains: [] } }), JSON.parse(response.body, symbolize_names: true) end From 26cb791586d2a29247dd7f2240beea445b99c2be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergei=20Ts=C3=B5ganov?= Date: Tue, 12 Jul 2022 15:42:49 +0300 Subject: [PATCH 37/81] Fixes after repp-apidoc update --- .../repp/v1/contacts_controller.rb | 2 +- .../repp/v1/domains/renews_controller.rb | 2 +- .../repp/v1/domains/statuses_controller.rb | 2 -- app/controllers/repp/v1/domains_controller.rb | 2 +- .../v1/registrar/nameservers_controller.rb | 10 +++---- app/interactions/actions/domain_update.rb | 2 +- app/models/registrar.rb | 4 +-- lib/serializers/repp/contact.rb | 2 +- lib/serializers/repp/domain.rb | 4 +-- .../repp/v1/contacts/check_test.rb | 4 +-- test/interactions/do_request_test.rb | 29 +++++++------------ 11 files changed, 25 insertions(+), 38 deletions(-) diff --git a/app/controllers/repp/v1/contacts_controller.rb b/app/controllers/repp/v1/contacts_controller.rb index d6c4439c5..f9c58303c 100644 --- a/app/controllers/repp/v1/contacts_controller.rb +++ b/app/controllers/repp/v1/contacts_controller.rb @@ -63,7 +63,7 @@ module Repp desc 'Check contact code availability' def check contact = Epp::Contact.find_by(code: params[:id]) - data = { contact: { id: params[:id], available: contact.nil? } } + data = { contact: { code: params[:id], available: contact.nil? } } render_success(data: data) end diff --git a/app/controllers/repp/v1/domains/renews_controller.rb b/app/controllers/repp/v1/domains/renews_controller.rb index 26365dffc..9aba3e41b 100644 --- a/app/controllers/repp/v1/domains/renews_controller.rb +++ b/app/controllers/repp/v1/domains/renews_controller.rb @@ -22,7 +22,7 @@ module Repp return end - render_success(data: { domain: { name: @domain.name, id: @domain.uuid } }) + render_success(data: { domain: { name: @domain.name } }) end def bulk_renew diff --git a/app/controllers/repp/v1/domains/statuses_controller.rb b/app/controllers/repp/v1/domains/statuses_controller.rb index ee15655df..d46725c46 100644 --- a/app/controllers/repp/v1/domains/statuses_controller.rb +++ b/app/controllers/repp/v1/domains/statuses_controller.rb @@ -7,7 +7,6 @@ module Repp api :DELETE, '/repp/v1/domains/:domain_name/statuses/:status' param :domain_name, String, desc: 'Domain name' - param :status, String, desc: 'Status to be removed' desc 'Remove status from specific domain' def destroy return editing_failed unless domain_with_status?(params[:id]) @@ -22,7 +21,6 @@ module Repp api :PUT, '/repp/v1/domains/:domain_name/statuses/:status' param :domain_name, String, desc: 'Domain name' - param :status, String, desc: 'Status to be added' desc 'Add status to specific domain' def update return editing_failed if domain_with_status?(params[:id]) diff --git a/app/controllers/repp/v1/domains_controller.rb b/app/controllers/repp/v1/domains_controller.rb index 00005e84d..ba40b13e2 100644 --- a/app/controllers/repp/v1/domains_controller.rb +++ b/app/controllers/repp/v1/domains_controller.rb @@ -101,7 +101,7 @@ module Repp return end - render_success(data: { domain: { name: @domain.name, id: @domain.uuid } }) + render_success(data: { domain: { name: @domain.name } }) end api :GET, '/repp/v1/domains/:domain_name/transfer_info' diff --git a/app/controllers/repp/v1/registrar/nameservers_controller.rb b/app/controllers/repp/v1/registrar/nameservers_controller.rb index fbd4c03ec..b3c6d8412 100644 --- a/app/controllers/repp/v1/registrar/nameservers_controller.rb +++ b/app/controllers/repp/v1/registrar/nameservers_controller.rb @@ -30,7 +30,6 @@ module Repp .add_nameservers(hostname_params[:attributes], domains: domains_from_params) end - render_success(data: data_format_for_success(affected, errored)) rescue ActiveRecord::RecordInvalid => e handle_errors(e.record) @@ -49,14 +48,15 @@ module Repp type: 'nameserver', id: hostname_params[:attributes][:hostname], attributes: hostname_params[:attributes], - affected_domains: affected_domains, - skipped_domains: errored_domains, + affected_domains: affected_domains || [], + skipped_domains: errored_domains || [], } end def hostname_params - params.require(:data).permit(:type, :id, nameserver: [], domains: [], - attributes: [:hostname, { ipv4: [], ipv6: [] }]) + params.require(:data).permit(:type, :id, + :domains, nameserver: [], domains: [], + attributes: [:hostname, { ipv4: [], ipv6: [] }]) .tap do |data| data.require(:type) data.require(:attributes).require([:hostname]) diff --git a/app/interactions/actions/domain_update.rb b/app/interactions/actions/domain_update.rb index 377f90e40..ff6eccce2 100644 --- a/app/interactions/actions/domain_update.rb +++ b/app/interactions/actions/domain_update.rb @@ -48,7 +48,7 @@ module Actions contact_code = params[:registrant][:code] contact = Contact.find_by(code: contact_code) - validate_email(contact.email) + validate_email(contact.email) if contact regt = Registrant.find_by(code: params[:registrant][:code]) unless regt diff --git a/app/models/registrar.rb b/app/models/registrar.rb index 0ea0bfb13..4c0098de0 100644 --- a/app/models/registrar.rb +++ b/app/models/registrar.rb @@ -188,9 +188,9 @@ class Registrar < ApplicationRecord end def add_nameservers(new_attributes, domains: []) - transaction do - return if domains.empty? + return [] if domains.empty? + transaction do approved_list = domain_list_processing(domains: domains, new_attributes: new_attributes) self.domains.where(name: approved_list).find_each(&:update_whois_record) if approved_list.any? diff --git a/lib/serializers/repp/contact.rb b/lib/serializers/repp/contact.rb index 11d5457ae..5afab98f0 100644 --- a/lib/serializers/repp/contact.rb +++ b/lib/serializers/repp/contact.rb @@ -15,7 +15,7 @@ module Serializers def to_json(obj = contact) return simple_object if @simplify - json = { id: obj.uuid, code: obj.code, name: obj.name, ident: ident, phone: obj.phone, + json = { code: obj.code, name: obj.name, ident: ident, phone: obj.phone, created_at: obj.created_at, auth_info: obj.auth_info, email: obj.email, statuses: statuses, disclosed_attributes: obj.disclosed_attributes, registrar: registrar } diff --git a/lib/serializers/repp/domain.rb b/lib/serializers/repp/domain.rb index cd0be8e25..c851a1f0d 100644 --- a/lib/serializers/repp/domain.rb +++ b/lib/serializers/repp/domain.rb @@ -15,7 +15,7 @@ module Serializers return simple_object if @simplify json = { - id: obj.uuid, name: obj.name, registrant: registrant, + name: obj.name, registrant: registrant, created_at: obj.created_at, updated_at: obj.updated_at, expire_time: obj.expire_time, outzone_at: obj.outzone_at, delete_date: obj.delete_date, @@ -53,7 +53,6 @@ module Serializers def registrant rant = domain.registrant { - id: rant.uuid, name: rant.name, code: rant.code, } @@ -71,7 +70,6 @@ module Serializers def simple_object json = { - id: domain.uuid, name: domain.name, expire_time: domain.expire_time, registrant: registrant, diff --git a/test/integration/repp/v1/contacts/check_test.rb b/test/integration/repp/v1/contacts/check_test.rb index be0d979b1..6fc716638 100644 --- a/test/integration/repp/v1/contacts/check_test.rb +++ b/test/integration/repp/v1/contacts/check_test.rb @@ -14,7 +14,7 @@ class ReppV1ContactsCheckTest < ActionDispatch::IntegrationTest json = JSON.parse(response.body, symbolize_names: true) assert_response :ok - assert_equal 'nonexistant:code', json[:data][:contact][:id] + assert_equal 'nonexistant:code', json[:data][:contact][:code] assert_equal true, json[:data][:contact][:available] end @@ -24,7 +24,7 @@ class ReppV1ContactsCheckTest < ActionDispatch::IntegrationTest json = JSON.parse(response.body, symbolize_names: true) assert_response :ok - assert_equal contact.code, json[:data][:contact][:id] + assert_equal contact.code, json[:data][:contact][:code] assert_equal false, json[:data][:contact][:available] end end diff --git a/test/interactions/do_request_test.rb b/test/interactions/do_request_test.rb index a26eb0451..f73f71ad6 100644 --- a/test/interactions/do_request_test.rb +++ b/test/interactions/do_request_test.rb @@ -11,32 +11,23 @@ class DoRequestTest < ActiveSupport::TestCase @domain = domains(:shop) @user = users(:api_bestnames) - @request.body = { data: { type: 'nameserver', id: @nameserver.hostname, - domains: ["shop.test"], - attributes: { hostname: 'new-ns.bestnames.test', - ipv4: '192.0.2.55', - ipv6: '2001:db8::55' } } }.to_json + @request.body = { data: { type: 'nameserver', + id: @nameserver.hostname, + domains: ['shop.test'], + attributes: { hostname: 'new-ns.bestnames.test', + ipv4: '192.0.2.55', + ipv6: '2001:db8::55' } } }.to_json @request.basic_auth(@user.username, @user.plain_text_password) end def test_request_occurs - stub_request(:put, "http://epp:3000/repp/v1/registrar/nameservers"). - with( - body: "{\"data\":{\"type\":\"nameserver\",\"id\":\"ns1.bestnames.test\",\"domains\":[\"shop.test\"],\"attributes\":{\"hostname\":\"new-ns.bestnames.test\",\"ipv4\":\"192.0.2.55\",\"ipv6\":\"2001:db8::55\"}}}", - headers: { - 'Accept'=>'*/*', - 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', - 'Authorization'=>'Basic dGVzdF9iZXN0bmFtZXM6dGVzdHRlc3Q=', - 'Content-Type'=>'application/json', - 'Host'=>'epp:3000', - 'User-Agent'=>'Ruby' - }). - to_return(status: 200, body: ["shop.test"], headers: {}) + stub_request(:put, "#{ENV['repp_url']}registrar/nameservers") + .to_return(status: 200, body: ['shop.test'], headers: {}) action = Actions::DoRequest.new(@request, @uri) response = action.call - assert_equal response.body, ["shop.test"] - assert_equal response.code, "200" + assert_equal response.body, ['shop.test'] + assert_equal response.code, '200' end end \ No newline at end of file From ee1117021b09cec604486520fc7849677cd3e962 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergei=20Ts=C3=B5ganov?= Date: Thu, 14 Jul 2022 15:48:38 +0300 Subject: [PATCH 38/81] Enabled partial search --- app/controllers/repp/v1/accounts_controller.rb | 4 ++-- app/controllers/repp/v1/contacts_controller.rb | 4 ++-- app/controllers/repp/v1/domains_controller.rb | 4 ++-- app/controllers/repp/v1/invoices_controller.rb | 4 ++-- app/services/partial_search_formatter.rb | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/controllers/repp/v1/accounts_controller.rb b/app/controllers/repp/v1/accounts_controller.rb index 7db2a3275..0bd75a302 100644 --- a/app/controllers/repp/v1/accounts_controller.rb +++ b/app/controllers/repp/v1/accounts_controller.rb @@ -8,7 +8,7 @@ module Repp def index records = current_user.registrar.cash_account.activities - q = records.ransack(search_params) + q = records.ransack(PartialSearchFormatter.format(search_params)) q.sorts = 'created_at desc' if q.sorts.empty? activities = q.result(distinct: true) @@ -128,7 +128,7 @@ module Repp end def search_params - index_params.fetch(:q, {}) + index_params.fetch(:q, {}) || {} end def limit diff --git a/app/controllers/repp/v1/contacts_controller.rb b/app/controllers/repp/v1/contacts_controller.rb index f9c58303c..31be5e09b 100644 --- a/app/controllers/repp/v1/contacts_controller.rb +++ b/app/controllers/repp/v1/contacts_controller.rb @@ -11,7 +11,7 @@ module Repp authorize! :check, Epp::Contact records = current_user.registrar.contacts - q = records.ransack(search_params) + q = records.ransack(PartialSearchFormatter.format(search_params)) q.sorts = 'created_at desc' if q.sorts.empty? contacts = q.result(distinct: true) @@ -123,7 +123,7 @@ module Repp end def search_params - index_params.fetch(:q, {}) + index_params.fetch(:q, {}) || {} end def domain_filter_params diff --git a/app/controllers/repp/v1/domains_controller.rb b/app/controllers/repp/v1/domains_controller.rb index ba40b13e2..29b259a67 100644 --- a/app/controllers/repp/v1/domains_controller.rb +++ b/app/controllers/repp/v1/domains_controller.rb @@ -13,7 +13,7 @@ module Repp def index authorize! :info, Epp::Domain records = current_user.registrar.domains - q = records.ransack(search_params) + q = records.ransack(PartialSearchFormatter.format(search_params)) q.sorts = ['valid_to asc', 'created_at desc'] if q.sorts.empty? # use distinct: false here due to ransack bug: # https://github.com/activerecord-hackery/ransack/issues/429 @@ -244,7 +244,7 @@ module Repp end def search_params - index_params.fetch(:q, {}) + index_params.fetch(:q, {}) || {} end def update_params diff --git a/app/controllers/repp/v1/invoices_controller.rb b/app/controllers/repp/v1/invoices_controller.rb index 1c14df329..204aba096 100644 --- a/app/controllers/repp/v1/invoices_controller.rb +++ b/app/controllers/repp/v1/invoices_controller.rb @@ -9,7 +9,7 @@ module Repp desc 'Get all invoices' def index records = current_user.registrar.invoices - q = records.ransack(search_params) + q = records.ransack(PartialSearchFormatter.format(search_params)) q.sorts = 'created_at desc' if q.sorts.empty? invoices = q.result(distinct: true) @@ -101,7 +101,7 @@ module Repp end def search_params - index_params.fetch(:q, {}) + index_params.fetch(:q, {}) || {} end def invoice_params diff --git a/app/services/partial_search_formatter.rb b/app/services/partial_search_formatter.rb index af0c7978d..268cd75fb 100644 --- a/app/services/partial_search_formatter.rb +++ b/app/services/partial_search_formatter.rb @@ -5,7 +5,7 @@ class PartialSearchFormatter search_params.each do |key, value| next unless key.include?('matches') && value.present? - value << '%' + search_params[key] = "%#{value}%" end search_params From 73a4364196e49d150391074598345018fb0ce581 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20V=C3=B5hmar?= Date: Thu, 21 Jul 2022 13:08:30 +0300 Subject: [PATCH 39/81] Update CHANGELOG.md --- CHANGELOG.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3bbd89067..739854c16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,22 @@ +21.07.2022 +* Removed deprecated statuses_before_force_delete field [#2363](https://github.com/internetee/registry/issues/2363) + +15.07.2022 +* REPP api update for new registrar portal [#2387](https://github.com/internetee/registry/pull/2387) + +4.07.2022 +* Update apipie-rails to 0.8.0 [#2383](https://github.com/internetee/registry/pull/2383) +* Bump jmespath to 1.6.1 [#2388](https://github.com/internetee/registry/pull/2388) + +1.07.2022 +* Update pg to 1.4.1 [#2394](https://github.com/internetee/registry/pull/2394) +* Update rack to 2.2.4 [#2398](https://github.com/internetee/registry/pull/2398) + +21.06.2022 +* Update pg to 1.4.0 [#2392](https://github.com/internetee/registry/pull/2392) + 02.06.2022 +* fix for force delete check query [#2380](https://github.com/internetee/registry/pull/2380) * Integration with the billing service [#2266](https://github.com/internetee/registry/pull/2266) 25.05.2022 From 83f1157583096dee10d37adc496df3074146d3dc Mon Sep 17 00:00:00 2001 From: olegphenomenon Date: Fri, 29 Jul 2022 10:13:01 +0300 Subject: [PATCH 40/81] extend export csv of domain --- app/controllers/registrar/domains_controller.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/controllers/registrar/domains_controller.rb b/app/controllers/registrar/domains_controller.rb index 3347f5d38..5168d25b8 100644 --- a/app/controllers/registrar/domains_controller.rb +++ b/app/controllers/registrar/domains_controller.rb @@ -36,14 +36,12 @@ class Registrar end end - @domains = @domains.per(params[:results_per_page]) if params[:results_per_page].to_i.positive? - respond_to do |format| format.html format.csv do domain_presenters = [] - @domains.find_each do |domain| + @q.result.find_each do |domain| domain_presenters << ::DomainPresenter.new(domain: domain, view: view_context) end From 1deb6fa39f10b3c08dd7cd052d8da5db12c1f33a Mon Sep 17 00:00:00 2001 From: olegphenomenon Date: Fri, 29 Jul 2022 10:21:20 +0300 Subject: [PATCH 41/81] revert accidently push to master --- app/controllers/registrar/domains_controller.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/controllers/registrar/domains_controller.rb b/app/controllers/registrar/domains_controller.rb index 5168d25b8..3347f5d38 100644 --- a/app/controllers/registrar/domains_controller.rb +++ b/app/controllers/registrar/domains_controller.rb @@ -36,12 +36,14 @@ class Registrar end end + @domains = @domains.per(params[:results_per_page]) if params[:results_per_page].to_i.positive? + respond_to do |format| format.html format.csv do domain_presenters = [] - @q.result.find_each do |domain| + @domains.find_each do |domain| domain_presenters << ::DomainPresenter.new(domain: domain, view: view_context) end From ea8c91d615bf6332195377038ffd1e104b704e8d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 7 Aug 2022 17:32:55 +0000 Subject: [PATCH 42/81] Update dependency pg to v1.4.2 --- Gemfile | 2 +- Gemfile.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index 3761831d0..b8d2b4c38 100644 --- a/Gemfile +++ b/Gemfile @@ -17,7 +17,7 @@ gem 'figaro', '~> 1.2' # model related gem 'paper_trail', '~> 12.1' -gem 'pg', '1.4.1' +gem 'pg', '1.4.2' # 1.8 is for Rails < 5.0 gem 'ransack', '~> 2.6.0' gem 'truemail', '~> 2.4' # validates email by regexp, mail server existence and address existence diff --git a/Gemfile.lock b/Gemfile.lock index 8f1d86c23..0ba400459 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -353,7 +353,7 @@ GEM activerecord (>= 5.2) request_store (~> 1.1) pdfkit (0.8.5) - pg (1.4.1) + pg (1.4.2) pg_query (2.1.2) google-protobuf (>= 3.17.1) pghero (2.8.1) @@ -576,7 +576,7 @@ DEPENDENCIES omniauth-tara! paper_trail (~> 12.1) pdfkit - pg (= 1.4.1) + pg (= 1.4.2) pg_query (>= 0.9.0) pghero pry (= 0.14.1) From e050eb53ed8a8d0c9da23eeb84eb7aca7aac6d04 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 7 Aug 2022 17:36:44 +0000 Subject: [PATCH 43/81] Bump rails-html-sanitizer from 1.4.2 to 1.4.3 Bumps [rails-html-sanitizer](https://github.com/rails/rails-html-sanitizer) from 1.4.2 to 1.4.3. - [Release notes](https://github.com/rails/rails-html-sanitizer/releases) - [Changelog](https://github.com/rails/rails-html-sanitizer/blob/master/CHANGELOG.md) - [Commits](https://github.com/rails/rails-html-sanitizer/compare/v1.4.2...v1.4.3) --- updated-dependencies: - dependency-name: rails-html-sanitizer dependency-type: indirect ... Signed-off-by: dependabot[bot] --- Gemfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 8f1d86c23..105b39283 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -291,7 +291,7 @@ GEM kaminari-core (1.2.1) libxml-ruby (3.2.1) logger (1.4.3) - loofah (2.16.0) + loofah (2.18.0) crass (~> 1.0.2) nokogiri (>= 1.5.9) mail (2.7.1) @@ -399,7 +399,7 @@ GEM rails-dom-testing (2.0.3) activesupport (>= 4.2.0) nokogiri (>= 1.6) - rails-html-sanitizer (1.4.2) + rails-html-sanitizer (1.4.3) loofah (~> 2.3) railties (6.1.4.1) actionpack (= 6.1.4.1) From b91e5c1476f3e2c437e8af40f14d1dc9df0f154c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 7 Aug 2022 18:15:57 +0000 Subject: [PATCH 44/81] Update dependency validates_email_format_of to v1.7.1 --- Gemfile | 2 +- Gemfile.lock | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Gemfile b/Gemfile index b8d2b4c38..f70ee3ac7 100644 --- a/Gemfile +++ b/Gemfile @@ -21,7 +21,7 @@ gem 'pg', '1.4.2' # 1.8 is for Rails < 5.0 gem 'ransack', '~> 2.6.0' gem 'truemail', '~> 2.4' # validates email by regexp, mail server existence and address existence -gem 'validates_email_format_of', '1.6.3' # validates email against RFC 2822 and RFC 3696 +gem 'validates_email_format_of', '1.7.1' # validates email against RFC 2822 and RFC 3696 # 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 diff --git a/Gemfile.lock b/Gemfile.lock index 0ba400459..2a3f51be9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -259,7 +259,7 @@ GEM httpi (2.4.5) rack socksify - i18n (1.10.0) + i18n (1.12.0) concurrent-ruby (~> 1.0) i18n_data (0.13.0) isikukood (0.1.2) @@ -499,7 +499,7 @@ GEM validate_url (1.0.15) activemodel (>= 3.0.0) public_suffix - validates_email_format_of (1.6.3) + validates_email_format_of (1.7.1) i18n warden (1.2.9) rack (>= 2.0.9) @@ -596,7 +596,7 @@ DEPENDENCIES spy truemail (~> 2.4) uglifier - validates_email_format_of (= 1.6.3) + validates_email_format_of (= 1.7.1) webdrivers webmock whenever (= 1.0.0) From 91c471049a16d8be4c97f022311445fa52217d98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergei=20Ts=C3=B5ganov?= Date: Thu, 11 Aug 2022 10:34:34 +0300 Subject: [PATCH 45/81] Troubleshoot invoice not paid in billing system --- .../lhv_connect_transactions_controller.rb | 38 ++--- app/models/bank_transaction.rb | 17 ++- .../lhv_connect_transactions_test.rb | 50 +++---- test/models/bank_transaction_test.rb | 122 +++++++-------- test/tasks/invoices/process_payments_test.rb | 141 ++++++++++-------- 5 files changed, 196 insertions(+), 172 deletions(-) diff --git a/app/controllers/eis_billing/lhv_connect_transactions_controller.rb b/app/controllers/eis_billing/lhv_connect_transactions_controller.rb index 09688e68f..3c914bd27 100644 --- a/app/controllers/eis_billing/lhv_connect_transactions_controller.rb +++ b/app/controllers/eis_billing/lhv_connect_transactions_controller.rb @@ -1,8 +1,16 @@ module EisBilling class LhvConnectTransactionsController < EisBilling::BaseController def create + if params['_json'].nil? || params['_json'].empty? + render json: { message: 'MISSING PARAMS' }, status: :unprocessable_entity + return + end + + bank_statement = BankStatement.create(bank_code: Setting.registry_bank_code, + iban: Setting.registry_iban) + params['_json'].each do |incoming_transaction| - process_transactions(incoming_transaction) + process_transactions(incoming_transaction, bank_statement) end render status: :ok, json: { message: 'RECEIVED', params: params } @@ -10,19 +18,13 @@ module EisBilling private - def process_transactions(incoming_transaction) + def process_transactions(incoming_transaction, bank_statement) logger.info 'Got incoming transactions' logger.info incoming_transaction - bank_statement = BankStatement.new(bank_code: Setting.registry_bank_code, - iban: Setting.registry_iban) - bank_statement_transaction(bank_statement: bank_statement, incoming_transaction: incoming_transaction) - end - - def bank_statement_transaction(bank_statement:, incoming_transaction:) ActiveRecord::Base.transaction do - bank_statement.save! - transaction = create_transaction(incoming_transaction: incoming_transaction, bank_statement: bank_statement) + transaction = bank_statement.bank_transactions + .create!(transaction_attributes(incoming_transaction)) next if transaction.registrar.blank? @@ -35,14 +37,14 @@ module EisBilling transaction.autobind_invoice end - def create_transaction(incoming_transaction:, bank_statement:) - transaction_attributes = { sum: incoming_transaction['amount'], - currency: incoming_transaction['currency'], - paid_at: incoming_transaction['date'], - reference_no: incoming_transaction['payment_reference_number'], - description: incoming_transaction['payment_description'] } - - bank_statement.bank_transactions.create!(transaction_attributes) + def transaction_attributes(incoming_transaction) + { + sum: incoming_transaction['amount'], + currency: incoming_transaction['currency'], + paid_at: incoming_transaction['date'], + reference_no: incoming_transaction['payment_reference_number'], + description: incoming_transaction['payment_description'], + } end end end diff --git a/app/models/bank_transaction.rb b/app/models/bank_transaction.rb index 68b48995f..46d597e61 100644 --- a/app/models/bank_transaction.rb +++ b/app/models/bank_transaction.rb @@ -33,10 +33,10 @@ class BankTransaction < ApplicationRecord return unless autobindable? channel = manual ? 'admin_payment' : 'system_payment' - create_internal_payment_record(channel: channel, invoice: invoice, registrar: registrar) + create_internal_payment_record(invoice: invoice, registrar: registrar, channel: channel) end - def create_internal_payment_record(channel: nil, invoice:, registrar:) + def create_internal_payment_record(invoice:, registrar:, channel: nil) if channel.nil? create_activity(invoice.buyer, invoice) return @@ -46,10 +46,17 @@ class BankTransaction < ApplicationRecord payment_order.save! if create_activity(registrar, invoice) + status = 'paid' payment_order.paid! else - payment_order.update(notes: 'Failed to create activity', status: 'failed') + status = 'failed' + payment_order.update(notes: 'Failed to create activity', status: status) end + + return unless Feature.billing_system_integrated? + + EisBilling::SendInvoiceStatus.send_info(invoice_number: invoice.number, + status: status) end def bind_invoice(invoice_no, manual: false) @@ -62,8 +69,8 @@ class BankTransaction < ApplicationRecord validate_invoice_data(invoice) return if errors.any? - create_internal_payment_record(channel: (manual ? 'admin_payment' : nil), invoice: invoice, - registrar: invoice.buyer) + create_internal_payment_record(invoice: invoice, registrar: invoice.buyer, + channel: (manual ? 'admin_payment' : nil)) end def validate_invoice_data(invoice) diff --git a/test/integration/eis_billing/lhv_connect_transactions_test.rb b/test/integration/eis_billing/lhv_connect_transactions_test.rb index f6d0f59d0..14f7bccd7 100644 --- a/test/integration/eis_billing/lhv_connect_transactions_test.rb +++ b/test/integration/eis_billing/lhv_connect_transactions_test.rb @@ -8,35 +8,35 @@ class LhvConnectTransactionsIntegrationTest < ApplicationIntegrationTest end def test_should_saved_transaction_data - if Feature.billing_system_integrated? - test_transaction_1 = OpenStruct.new(amount: 0.1, - currency: 'EUR', - date: Time.zone.today, - payment_reference_number: '2199812', - payment_description: "description 2199812") + return unless Feature.billing_system_integrated? - test_transaction_2 = OpenStruct.new(amount: 0.1, - currency: 'EUR', - date: Time.zone.today, - payment_reference_number: '2199813', - payment_description: "description 2199813") + test_transaction_1 = OpenStruct.new(amount: 0.1, + currency: 'EUR', + date: Time.zone.today, + payment_reference_number: '2199812', + payment_description: 'description 2199812') - test_transaction_3 = OpenStruct.new(amount: 0.1, - currency: 'EUR', - date: Time.zone.today, - payment_reference_number: '2199814', - payment_description: "description 2199814") + test_transaction_2 = OpenStruct.new(amount: 0.1, + currency: 'EUR', + date: Time.zone.today, + payment_reference_number: '2199813', + payment_description: 'description 2199813') - lhv_transactions = [] - lhv_transactions << test_transaction_1 - lhv_transactions << test_transaction_2 - lhv_transactions << test_transaction_3 + test_transaction_3 = OpenStruct.new(amount: 0.1, + currency: 'EUR', + date: Time.zone.today, + payment_reference_number: '2199814', + payment_description: 'description 2199814') - assert_difference 'BankStatement.count', 3 do - assert_difference 'BankTransaction.count', 3 do - post eis_billing_lhv_connect_transactions_path, params: { "_json" => JSON.parse(lhv_transactions.to_json) }, - headers: { 'HTTP_COOKIE' => 'session=api_bestnames' } - end + lhv_transactions = [] + lhv_transactions << test_transaction_1 + lhv_transactions << test_transaction_2 + lhv_transactions << test_transaction_3 + + assert_difference 'BankStatement.count', 1 do + assert_difference 'BankTransaction.count', 3 do + post eis_billing_lhv_connect_transactions_path, params: { '_json' => JSON.parse(lhv_transactions.to_json) }, + headers: { 'HTTP_COOKIE' => 'session=api_bestnames' } end end end diff --git a/test/models/bank_transaction_test.rb b/test/models/bank_transaction_test.rb index 8a3a664b5..5f2503cb1 100644 --- a/test/models/bank_transaction_test.rb +++ b/test/models/bank_transaction_test.rb @@ -4,6 +4,10 @@ class BankTransactionTest < ActiveSupport::TestCase setup do @registrar = registrars(:bestnames) @invoice = invoices(:one) + if Feature.billing_system_integrated? + stub_request(:post, 'https://eis_billing_system:3000/api/v1/invoice_generator/invoice_status') + .to_return(status: 200, body: '', headers: {}) + end end def test_matches_against_invoice_nubmber_and_reference_number @@ -16,87 +20,86 @@ class BankTransactionTest < ActiveSupport::TestCase end def test_binds_if_this_sum_invoice_already_present - if Feature.billing_system_integrated? - invoice_n = Invoice.order(number: :desc).last.number - stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_number_generator"). - to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}", headers: {}) - create_payable_invoice(number: '2222', total: 10, reference_no: '1234567') - another_invoice = @invoice.dup - another_invoice.save(validate: false) - another_invoice.update(reference_no: '7654321', number: '2221') + return unless Feature.billing_system_integrated? - another_item = @invoice.items.first.dup - another_item.invoice = another_invoice - another_item.save - another_invoice.reload + invoice_n = Invoice.order(number: :desc).last.number + stub_request(:post, 'https://eis_billing_system:3000/api/v1/invoice_generator/invoice_number_generator') + .to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}", headers: {}) + create_payable_invoice(number: '2222', total: 10, reference_no: '1234567') + another_invoice = @invoice.dup + another_invoice.save(validate: false) + another_invoice.update(reference_no: '7654321', number: '2221') - first_transaction = BankTransaction.new(sum: 10, - description: 'Order nr 1 from registrar 1234567 second number 2345678') + another_item = @invoice.items.first.dup + another_item.invoice = another_invoice + another_item.save + another_invoice.reload - first_transaction.create_activity(another_invoice.buyer, another_invoice) + first_transaction = BankTransaction.new(sum: 10, + description: 'Order nr 1 from registrar 1234567 second number 2345678') - transaction = BankTransaction.new(sum: 10, - description: 'Order nr 1 from registrar 1234567 second number 2345678') + first_transaction.create_activity(another_invoice.buyer, another_invoice) - assert_difference 'AccountActivity.count' do - transaction.autobind_invoice - end + transaction = BankTransaction.new(sum: 10, + description: 'Order nr 1 from registrar 1234567 second number 2345678') + + assert_difference 'AccountActivity.count' do + transaction.autobind_invoice end end def test_binds_if_this_sum_cancelled_invoice_already_present - if Feature.billing_system_integrated? - invoice_n = Invoice.order(number: :desc).last.number - stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_number_generator"). - to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}", headers: {}) - create_payable_invoice(number: '2222', total: 10, reference_no: '1234567') - another_invoice = @invoice.dup - another_invoice.save(validate: false) + return unless Feature.billing_system_integrated? + invoice_n = Invoice.order(number: :desc).last.number + stub_request(:post, 'https://eis_billing_system:3000/api/v1/invoice_generator/invoice_number_generator') + .to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}", headers: {}) + create_payable_invoice(number: '2222', total: 10, reference_no: '1234567') + another_invoice = @invoice.dup + another_invoice.save(validate: false) - another_item = @invoice.items.first.dup - another_item.invoice = another_invoice + another_item = @invoice.items.first.dup + another_item.invoice = another_invoice - another_item.save - another_invoice.reload - another_invoice.update(reference_no: '1234567', number: '2221', cancelled_at: Time.zone.now) + another_item.save + another_invoice.reload + another_invoice.update(reference_no: '1234567', number: '2221', cancelled_at: Time.zone.now) - transaction = BankTransaction.new(sum: 10, - description: 'Order nr 1 from registrar 1234567 second number 2345678') + transaction = BankTransaction.new(sum: 10, + description: 'Order nr 1 from registrar 1234567 second number 2345678') - assert_difference 'AccountActivity.count' do - transaction.autobind_invoice - end + assert_difference 'AccountActivity.count' do + transaction.autobind_invoice end end def test_marks_the_first_one_as_paid_if_same_sum - if Feature.billing_system_integrated? - invoice_n = Invoice.order(number: :desc).last.number - stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_number_generator"). - to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}", headers: {}) - create_payable_invoice(number: '2222', total: 10, reference_no: '1234567') - another_invoice = @invoice.dup - another_invoice.save(validate: false) - another_invoice.update(reference_no: '7654321', number: '2221') + return unless Feature.billing_system_integrated? - another_item = @invoice.items.first.dup - another_item.invoice = another_invoice - another_item.save - another_invoice.reload + invoice_n = Invoice.order(number: :desc).last.number + stub_request(:post, 'https://eis_billing_system:3000/api/v1/invoice_generator/invoice_number_generator') + .to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}", headers: {}) + create_payable_invoice(number: '2222', total: 10, reference_no: '1234567') + another_invoice = @invoice.dup + another_invoice.save(validate: false) + another_invoice.update(reference_no: '7654321', number: '2221') - transaction = BankTransaction.new(sum: 10, - description: 'Order nr 1 from registrar 1234567 second number 2345678') + another_item = @invoice.items.first.dup + another_item.invoice = another_invoice + another_item.save + another_invoice.reload - assert_difference 'AccountActivity.count' do - transaction.autobind_invoice - end + transaction = BankTransaction.new(sum: 10, + description: 'Order nr 1 from registrar 1234567 second number 2345678') - @invoice.reload - another_invoice.reload - assert(@invoice.paid?) - assert_not(another_invoice.paid?) + assert_difference 'AccountActivity.count' do + transaction.autobind_invoice end + + @invoice.reload + another_invoice.reload + assert(@invoice.paid?) + assert_not(another_invoice.paid?) end def test_matches_against_invoice_nubmber_and_reference_number_in_description @@ -192,7 +195,7 @@ class BankTransactionTest < ActiveSupport::TestCase def test_parsed_ref_no_returns_nil_if_ref_not_found statement = BankTransaction.new - statement.description = "all invalid 12 123 55 77777 --" + statement.description = 'all invalid 12 123 55 77777 --' assert_nil statement.parsed_ref_number end @@ -208,6 +211,7 @@ class BankTransactionTest < ActiveSupport::TestCase transaction.bind_invoice('2222') end end + private def create_payable_invoice(attributes) diff --git a/test/tasks/invoices/process_payments_test.rb b/test/tasks/invoices/process_payments_test.rb index e368c5369..e080bd9fa 100644 --- a/test/tasks/invoices/process_payments_test.rb +++ b/test/tasks/invoices/process_payments_test.rb @@ -30,6 +30,11 @@ class ProcessPaymentsTaskTest < ActiveJob::TestCase [message] end end + + if Feature.billing_system_integrated? + stub_request(:post, 'https://eis_billing_system:3000/api/v1/invoice_generator/invoice_status') + .to_return(status: 200, body: '', headers: {}) + end end def test_not_raises_error_if_bad_reference @@ -63,7 +68,8 @@ class ProcessPaymentsTaskTest < ActiveJob::TestCase def test_cannot_create_new_invoice_if_transaction_binded_to_paid_invoice assert_not @invoice.paid? - @account_activity.update(activity_type: "add_credit", bank_transaction: nil, created_at: Time.zone.today - 1.day, creator_str: 'AdminUser') + @account_activity.update(activity_type: 'add_credit', bank_transaction: nil, + created_at: Time.zone.today - 1.day, creator_str: 'AdminUser') @invoice.update(account_activity: @account_activity, total: @payment_amount) assert @invoice.paid? @@ -77,33 +83,34 @@ class ProcessPaymentsTaskTest < ActiveJob::TestCase end def test_if_invoice_is_overdue_than_48_hours - if Feature.billing_system_integrated? - invoice_n = Invoice.order(number: :desc).last.number + return unless Feature.billing_system_integrated? - Spy.on_instance_method(SendEInvoiceTwoJob, :perform_now).and_return(true) + invoice_n = Invoice.order(number: :desc).last.number - stub_request(:post, "https://eis_billing_system:3000/api/v1/e_invoice/e_invoice"). - to_return(status: 200, body: "", headers: {}) + Spy.on_instance_method(SendEInvoiceTwoJob, :perform_now).and_return(true) - stub_request(:put, "https://registry:3000/eis_billing/e_invoice_response"). - to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}, {\"date\":\"#{Time.zone.now-10.minutes}\"}", headers: {}) + stub_request(:post, 'https://eis_billing_system:3000/api/v1/e_invoice/e_invoice') + .to_return(status: 200, body: '', headers: {}) - stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_number_generator"). - to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}", headers: {}) + stub_request(:put, 'https://registry:3000/eis_billing/e_invoice_response') + .to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}, {\"date\":\"#{Time.zone.now-10.minutes}\"}", headers: {}) - stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_generator"). - to_return(status: 200, body: "{\"everypay_link\":\"http://link.test\"}", headers: {}) + stub_request(:post, 'https://eis_billing_system:3000/api/v1/invoice_generator/invoice_number_generator') + .to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}", headers: {}) - assert_not @invoice.paid? + stub_request(:post, 'https://eis_billing_system:3000/api/v1/invoice_generator/invoice_generator') + .to_return(status: 200, body: "{\"everypay_link\":\"http://link.test\"}", headers: {}) - @account_activity.update(activity_type: "add_credit", bank_transaction: nil, created_at: Time.zone.today - 3.days, creator_str: 'AdminUser') - @invoice.update(account_activity: @account_activity, total: @payment_amount) - assert @invoice.paid? + assert_not @invoice.paid? - assert_difference 'AccountActivity.count' do - assert_difference 'Invoice.count' do - capture_io { run_task } - end + @account_activity.update(activity_type: 'add_credit', bank_transaction: nil, + created_at: Time.zone.today - 3.days, creator_str: 'AdminUser') + @invoice.update(account_activity: @account_activity, total: @payment_amount) + assert @invoice.paid? + + assert_difference 'AccountActivity.count' do + assert_difference 'Invoice.count' do + capture_io { run_task } end end end @@ -162,31 +169,33 @@ class ProcessPaymentsTaskTest < ActiveJob::TestCase end def test_credits_registrar_athout_invoice_beforehand - if Feature.billing_system_integrated? - invoice_n = Invoice.order(number: :desc).last.number - stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_number_generator"). - to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}") + return unless Feature.billing_system_integrated? - stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_generator"). - to_return(status: 200, body: "{\"everypay_link\":\"http://link.test\"}", headers: {}) + invoice_n = Invoice.order(number: :desc).last.number + stub_request(:post, 'https://eis_billing_system:3000/api/v1/invoice_generator/invoice_number_generator') + .to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}") - Spy.on_instance_method(SendEInvoiceTwoJob, :perform_now).and_return(true) + stub_request(:post, 'https://eis_billing_system:3000/api/v1/invoice_generator/invoice_generator') + .to_return(status: 200, body: "{\"everypay_link\":\"http://link.test\"}", headers: {}) - stub_request(:post, "https://eis_billing_system:3000/api/v1/e_invoice/e_invoice"). - to_return(status: 200, body: "", headers: {}) + Spy.on_instance_method(SendEInvoiceTwoJob, :perform_now).and_return(true) - stub_request(:put, "https://registry:3000/eis_billing/e_invoice_response"). - to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}, {\"date\":\"#{Time.zone.now-10.minutes}\"}", headers: {}) + stub_request(:post, 'https://eis_billing_system:3000/api/v1/e_invoice/e_invoice') + .to_return(status: 200, body: '', headers: {}) - registrar = registrars(:bestnames) + stub_request(:put, 'https://registry:3000/eis_billing/e_invoice_response') + .to_return(status: 200, + body: "{\"invoice_number\":\"#{invoice_n + 3}\"}, {\"date\":\"#{Time.zone.now-10.minutes}\"}", + headers: {}) - assert_changes -> { registrar.accounts.first.balance } do - run_task - end + registrar = registrars(:bestnames) - assert_changes -> { registrar.invoices.count } do - run_task - end + assert_changes -> { registrar.accounts.first.balance } do + run_task + end + + assert_changes -> { registrar.invoices.count } do + run_task end end @@ -198,40 +207,42 @@ class ProcessPaymentsTaskTest < ActiveJob::TestCase end def test_topup_creates_invoice_and_send_it_as_paid - if Feature.billing_system_integrated? - stub_request(:post, "https://eis_billing_system:3000/api/v1/e_invoice/e_invoice"). - to_return(status: 200, body: "", headers: {}) + return unless Feature.billing_system_integrated? - invoice_n = Invoice.order(number: :desc).last.number - stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_number_generator"). - to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}", headers: {}) + stub_request(:post, 'https://eis_billing_system:3000/api/v1/e_invoice/e_invoice') + .to_return(status: 200, body: '', headers: {}) - stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_generator"). - to_return(status: 200, body: "{\"everypay_link\":\"http://link.test\"}", headers: {}) + invoice_n = Invoice.order(number: :desc).last.number + stub_request(:post, 'https://eis_billing_system:3000/api/v1/invoice_generator/invoice_number_generator') + .to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}", headers: {}) - stub_request(:put, "https://registry:3000/eis_billing/e_invoice_response"). - to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}, {\"date\":\"#{Time.zone.now-10.minutes}\"}", headers: {}) + stub_request(:post, 'https://eis_billing_system:3000/api/v1/invoice_generator/invoice_generator') + .to_return(status: 200, body: "{\"everypay_link\":\"http://link.test\"}", headers: {}) - registrar = registrars(:bestnames) - @invoice.payment_orders.destroy_all - @invoice.destroy + stub_request(:put, 'https://registry:3000/eis_billing/e_invoice_response') + .to_return(status: 200, + body: "{\"invoice_number\":\"#{invoice_n + 3}\"}, {\"date\":\"#{Time.zone.now - 10.minutes}\"}", + headers: {}) - perform_enqueued_jobs do - run_task - end + registrar = registrars(:bestnames) + @invoice.payment_orders.destroy_all + @invoice.destroy - invoice = Invoice.last - assert invoice.paid? - assert_not invoice.e_invoice_sent_at.blank? - - pdf_source = Invoice::PdfGenerator.new(invoice) - pdf_source.send(:invoice_html).include?('Receipt date') - - email= ActionMailer::Base.deliveries.last - assert email.subject.include?('already paid') - - assert_equal 0.1, registrar.invoices.last.total + perform_enqueued_jobs do + run_task end + + invoice = Invoice.last + assert invoice.paid? + assert_not invoice.e_invoice_sent_at.blank? + + pdf_source = Invoice::PdfGenerator.new(invoice) + pdf_source.send(:invoice_html).include?('Receipt date') + + email = ActionMailer::Base.deliveries.last + assert email.subject.include?('already paid') + + assert_equal 0.1, registrar.invoices.last.total end def test_output From 4d5203096e08493e7bf9fd7c8badba1e25aaae6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergei=20Ts=C3=B5ganov?= Date: Thu, 11 Aug 2022 11:11:52 +0300 Subject: [PATCH 46/81] Modified lhv connect controller --- .../eis_billing/lhv_connect_transactions_controller.rb | 6 +++++- app/models/bank_transaction.rb | 10 ++-------- test/models/bank_transaction_test.rb | 4 ---- test/tasks/invoices/process_payments_test.rb | 5 ----- 4 files changed, 7 insertions(+), 18 deletions(-) diff --git a/app/controllers/eis_billing/lhv_connect_transactions_controller.rb b/app/controllers/eis_billing/lhv_connect_transactions_controller.rb index 3c914bd27..2e5996881 100644 --- a/app/controllers/eis_billing/lhv_connect_transactions_controller.rb +++ b/app/controllers/eis_billing/lhv_connect_transactions_controller.rb @@ -34,7 +34,11 @@ module EisBilling def create_invoice_if_missing(transaction) Invoice.create_from_transaction!(transaction) unless transaction.autobindable? - transaction.autobind_invoice + invoice = transaction.autobind_invoice + return unless invoice.paid? + + EisBilling::SendInvoiceStatus.send_info(invoice_number: invoice.number, + status: 'paid') end def transaction_attributes(incoming_transaction) diff --git a/app/models/bank_transaction.rb b/app/models/bank_transaction.rb index 46d597e61..b8bf3eb77 100644 --- a/app/models/bank_transaction.rb +++ b/app/models/bank_transaction.rb @@ -46,17 +46,11 @@ class BankTransaction < ApplicationRecord payment_order.save! if create_activity(registrar, invoice) - status = 'paid' payment_order.paid! else - status = 'failed' - payment_order.update(notes: 'Failed to create activity', status: status) + payment_order.update(notes: 'Failed to create activity', status: 'failed') end - - return unless Feature.billing_system_integrated? - - EisBilling::SendInvoiceStatus.send_info(invoice_number: invoice.number, - status: status) + invoice end def bind_invoice(invoice_no, manual: false) diff --git a/test/models/bank_transaction_test.rb b/test/models/bank_transaction_test.rb index 5f2503cb1..8cb62ab48 100644 --- a/test/models/bank_transaction_test.rb +++ b/test/models/bank_transaction_test.rb @@ -4,10 +4,6 @@ class BankTransactionTest < ActiveSupport::TestCase setup do @registrar = registrars(:bestnames) @invoice = invoices(:one) - if Feature.billing_system_integrated? - stub_request(:post, 'https://eis_billing_system:3000/api/v1/invoice_generator/invoice_status') - .to_return(status: 200, body: '', headers: {}) - end end def test_matches_against_invoice_nubmber_and_reference_number diff --git a/test/tasks/invoices/process_payments_test.rb b/test/tasks/invoices/process_payments_test.rb index e080bd9fa..60ef3d8d8 100644 --- a/test/tasks/invoices/process_payments_test.rb +++ b/test/tasks/invoices/process_payments_test.rb @@ -30,11 +30,6 @@ class ProcessPaymentsTaskTest < ActiveJob::TestCase [message] end end - - if Feature.billing_system_integrated? - stub_request(:post, 'https://eis_billing_system:3000/api/v1/invoice_generator/invoice_status') - .to_return(status: 200, body: '', headers: {}) - end end def test_not_raises_error_if_bad_reference From 3bf58f0be16b5a795a0ebe40e039a2e80870883f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 11 Aug 2022 17:05:46 +0000 Subject: [PATCH 47/81] Update postgres Docker tag to v14 --- .github/workflows/ruby.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ruby.yml b/.github/workflows/ruby.yml index 5d181dbc0..f4498f4da 100644 --- a/.github/workflows/ruby.yml +++ b/.github/workflows/ruby.yml @@ -6,7 +6,7 @@ jobs: test: services: postgres: - image: postgres:12 + image: postgres:14 ports: ["5432:5432"] env: POSTGRES_PASSWORD: password From 8d4d9e7e4209f2a5c8bba11da7d2ca5b3ab3b195 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 01:16:59 +0000 Subject: [PATCH 48/81] Update dependency validates_email_format_of to v1.7.2 --- Gemfile | 2 +- Gemfile.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index f70ee3ac7..a6d20124c 100644 --- a/Gemfile +++ b/Gemfile @@ -21,7 +21,7 @@ gem 'pg', '1.4.2' # 1.8 is for Rails < 5.0 gem 'ransack', '~> 2.6.0' gem 'truemail', '~> 2.4' # validates email by regexp, mail server existence and address existence -gem 'validates_email_format_of', '1.7.1' # validates email against RFC 2822 and RFC 3696 +gem 'validates_email_format_of', '1.7.2' # validates email against RFC 2822 and RFC 3696 # 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 diff --git a/Gemfile.lock b/Gemfile.lock index a458e8f03..59c01a9f7 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -499,7 +499,7 @@ GEM validate_url (1.0.15) activemodel (>= 3.0.0) public_suffix - validates_email_format_of (1.7.1) + validates_email_format_of (1.7.2) i18n warden (1.2.9) rack (>= 2.0.9) @@ -596,7 +596,7 @@ DEPENDENCIES spy truemail (~> 2.4) uglifier - validates_email_format_of (= 1.7.1) + validates_email_format_of (= 1.7.2) webdrivers webmock whenever (= 1.0.0) From d314822b82a6816b0960a70a0fdf2eb4bae81e2e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 01:33:34 +0000 Subject: [PATCH 49/81] Update dependency pg to v1.4.3 --- Gemfile | 2 +- Gemfile.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index a6d20124c..1c12fe666 100644 --- a/Gemfile +++ b/Gemfile @@ -17,7 +17,7 @@ gem 'figaro', '~> 1.2' # model related gem 'paper_trail', '~> 12.1' -gem 'pg', '1.4.2' +gem 'pg', '1.4.3' # 1.8 is for Rails < 5.0 gem 'ransack', '~> 2.6.0' gem 'truemail', '~> 2.4' # validates email by regexp, mail server existence and address existence diff --git a/Gemfile.lock b/Gemfile.lock index 59c01a9f7..d1e38fab5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -353,7 +353,7 @@ GEM activerecord (>= 5.2) request_store (~> 1.1) pdfkit (0.8.5) - pg (1.4.2) + pg (1.4.3) pg_query (2.1.2) google-protobuf (>= 3.17.1) pghero (2.8.1) @@ -576,7 +576,7 @@ DEPENDENCIES omniauth-tara! paper_trail (~> 12.1) pdfkit - pg (= 1.4.2) + pg (= 1.4.3) pg_query (>= 0.9.0) pghero pry (= 0.14.1) From 442fa879d2951e5d3aa87c7c7a10f0769c1b742a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergei=20Ts=C3=B5ganov?= Date: Fri, 19 Aug 2022 10:49:51 +0300 Subject: [PATCH 50/81] Return address processing setting and add id paramater when creating new contact --- app/controllers/repp/v1/base_controller.rb | 1 + app/controllers/repp/v1/contacts_controller.rb | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/controllers/repp/v1/base_controller.rb b/app/controllers/repp/v1/base_controller.rb index 10c3c6026..d84c8e37b 100644 --- a/app/controllers/repp/v1/base_controller.rb +++ b/app/controllers/repp/v1/base_controller.rb @@ -163,6 +163,7 @@ module Repp data = current_user.as_json(only: %i[id username roles]) data[:registrar_name] = registrar.name data[:legaldoc_mandatory] = registrar.legaldoc_mandatory? + data[:address_processing] = Contact.address_processing? data[:abilities] = Ability.new(current_user).permissions data end diff --git a/app/controllers/repp/v1/contacts_controller.rb b/app/controllers/repp/v1/contacts_controller.rb index 31be5e09b..6f5e79a64 100644 --- a/app/controllers/repp/v1/contacts_controller.rb +++ b/app/controllers/repp/v1/contacts_controller.rb @@ -193,7 +193,7 @@ module Repp def contact_create_params(required: true) create_params = %i[name email phone] contact_params.require(create_params) if required - contact_params.slice(*create_params) + contact_params.slice(:id, *create_params) end def contact_ident_params(required: true) @@ -211,7 +211,7 @@ module Repp end def contact_params - params.require(:contact).permit(:name, :email, :phone, :legal_document, + params.require(:contact).permit(:id, :name, :email, :phone, :legal_document, legal_document: %i[body type], ident: [%i[ident ident_type ident_country_code]], addr: [%i[country_code city street zip state]]) From d589aa16811f258b13d7d6e44991a6cbb2fdeca3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergei=20Ts=C3=B5ganov?= Date: Sun, 21 Aug 2022 19:11:39 +0300 Subject: [PATCH 51/81] Created job for sending monthly invoices --- app/jobs/delete_monthly_invoices_job.rb | 10 + app/jobs/send_e_invoice_job.rb | 2 +- app/jobs/send_monthly_invoices_job.rb | 155 ++++++++ app/mailers/invoice_mailer.rb | 1 + app/models/concerns/registrar/book_keeping.rb | 7 +- app/models/invoice.rb | 13 +- app/models/invoice/e_invoice_generator.rb | 27 +- app/models/invoice/pdf_generator.rb | 3 +- app/views/admin/invoices/show.haml | 9 +- app/views/invoice/monthly_pdf.haml | 277 ++++++++++++++ app/views/invoice/pdf.haml | 2 +- config/locales/en.yml | 2 + config/locales/et.yml | 2 + ...075833_add_monthly_invoice_type_columns.rb | 6 + db/structure.sql | 339 +++++++++++++++++- lib/serializers/repp/invoice.rb | 18 +- test/jobs/directo_invoice_forward_job_test.rb | 8 +- test/jobs/send_monthly_invoices_job_test.rb | 265 ++++++++++++++ 18 files changed, 1103 insertions(+), 43 deletions(-) create mode 100644 app/jobs/delete_monthly_invoices_job.rb create mode 100644 app/jobs/send_monthly_invoices_job.rb create mode 100644 app/views/invoice/monthly_pdf.haml create mode 100644 db/migrate/20220818075833_add_monthly_invoice_type_columns.rb create mode 100644 test/jobs/send_monthly_invoices_job_test.rb diff --git a/app/jobs/delete_monthly_invoices_job.rb b/app/jobs/delete_monthly_invoices_job.rb new file mode 100644 index 000000000..8a38d0d14 --- /dev/null +++ b/app/jobs/delete_monthly_invoices_job.rb @@ -0,0 +1,10 @@ +class DeleteMonthlyInvoicesJob < ApplicationJob + queue_as :default + + def perform + @month = Time.zone.now - 1.month + invoices = Invoice.where(monthly_invoice: true, issue_date: @month.end_of_month.to_date, + in_directo: false, e_invoice_sent_at: nil) + invoices.delete_all + end +end \ No newline at end of file diff --git a/app/jobs/send_e_invoice_job.rb b/app/jobs/send_e_invoice_job.rb index 33a2745c6..4e77926b0 100644 --- a/app/jobs/send_e_invoice_job.rb +++ b/app/jobs/send_e_invoice_job.rb @@ -17,7 +17,7 @@ class SendEInvoiceJob < ApplicationJob def need_to_process_invoice?(invoice:, payable:) logger.info "Checking if need to process e-invoice #{invoice}, payable: #{payable}" return false if invoice.blank? - return false if invoice.do_not_send_e_invoice? && payable + return false if invoice.do_not_send_e_invoice? && (invoice.monthly_invoice ? true : payable) true end diff --git a/app/jobs/send_monthly_invoices_job.rb b/app/jobs/send_monthly_invoices_job.rb new file mode 100644 index 000000000..ddb4bd80c --- /dev/null +++ b/app/jobs/send_monthly_invoices_job.rb @@ -0,0 +1,155 @@ +class SendMonthlyInvoicesJob < ApplicationJob + queue_as :default + + def perform(dry: false) + @dry = dry + @month = Time.zone.now - 1.month + @directo_client = new_directo_client + @min_directo_num = Setting.directo_monthly_number_min.presence.try(:to_i) + @max_directo_num = Setting.directo_monthly_number_max.presence.try(:to_i) + + send_monthly_invoices + end + + def new_directo_client + DirectoApi::Client.new(ENV['directo_invoice_url'], Setting.directo_sales_agent, + Setting.directo_receipt_payment_term) + end + + def send_monthly_invoices + Registrar.where.not(test_registrar: true).find_each do |registrar| + next unless registrar.cash_account + + summary = registrar.monthly_summary(month: @month) + next if summary.nil? + + invoice = registrar.monthly_invoice(month: @month) || create_invoice(summary, registrar) + next if invoice.nil? || @dry + + InvoiceMailer.invoice_email(invoice: invoice, + recipient: registrar.billing_email) + .deliver_now + + SendEInvoiceJob.set(wait: 1.minute).perform_now(invoice.id, payable: false) + + next if invoice.in_directo + + Rails.logger.info("[DIRECTO] Trying to send monthly invoice #{invoice.number}") + @directo_client = new_directo_client + directo_invoices = @directo_client.invoices.add_with_schema(invoice: summary, + schema: 'summary') + next unless directo_invoices.size.positive? + + directo_invoices.last.number = invoice.number + sync_with_directo + end + end + + def create_invoice(summary, registrar) + vat_rate = ::Invoice::VatRateCalculator.new(registrar: registrar).calculate + invoice = Invoice.new( + number: assign_monthly_number, + issue_date: summary['date'].to_date, + due_date: summary['date'].to_date, + currency: 'EUR', + description: I18n.t('invoice.monthly_invoice_description'), + seller_name: Setting.registry_juridical_name, + seller_reg_no: Setting.registry_reg_no, + seller_iban: Setting.registry_iban, + seller_bank: Setting.registry_bank, + seller_swift: Setting.registry_swift, + seller_vat_no: Setting.registry_vat_no, + seller_country_code: Setting.registry_country_code, + seller_state: Setting.registry_state, + seller_street: Setting.registry_street, + seller_city: Setting.registry_city, + seller_zip: Setting.registry_zip, + seller_phone: Setting.registry_phone, + seller_url: Setting.registry_url, + seller_email: Setting.registry_email, + seller_contact_name: Setting.registry_invoice_contact, + buyer: registrar, + buyer_name: registrar.name, + buyer_reg_no: registrar.reg_no, + buyer_country_code: registrar.address_country_code, + buyer_state: registrar.address_state, + buyer_street: registrar.address_street, + buyer_city: registrar.address_city, + buyer_zip: registrar.address_zip, + buyer_phone: registrar.phone, + buyer_url: registrar.website, + buyer_email: registrar.email, + reference_no: registrar.reference_no, + vat_rate: vat_rate, + monthly_invoice: true, + metadata: { items: summary['invoice_lines'] }, + total: 0 + ) + return unless invoice.save! + + update_directo_number(num: invoice.number) + invoice + end + + def sync_with_directo + invoices_xml = @directo_client.invoices.as_xml + + Rails.logger.info("[Directo] - attempting to send following XML:\n #{invoices_xml}") + + res = @directo_client.invoices.deliver(ssl_verify: false) + process_directo_response(res.body, invoices_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 assign_monthly_numbers + invoices_count = @directo_client.invoices.count + last_directo_num = [Setting.directo_monthly_number_last.presence.try(:to_i), + @min_directo_num].compact.max || 0 + raise 'Directo Counter is out of period!' if directo_counter_exceedable?(invoices_count, + last_directo_num) + + @directo_client.invoices.each do |inv| + last_directo_num += 1 + inv.number = last_directo_num + end + end + + def assign_monthly_number + last_directo_num = [Setting.directo_monthly_number_last.presence.try(:to_i), + @min_directo_num].compact.max || 0 + raise 'Directo Counter is out of period!' if directo_counter_exceedable?(1, last_directo_num) + + last_directo_num + 1 + end + + def directo_counter_exceedable?(invoices_count, last_directo_num) + return true if @max_directo_num && @max_directo_num < (last_directo_num + invoices_count) + + false + end + + def process_directo_response(body, req) + Rails.logger.info "[Directo] - Responded with body: #{body}" + Nokogiri::XML(body).css('Result').each do |res| + inv = Invoice.find_by(number: res.attributes['docid'].value.to_i) + mark_invoice_as_sent(res: res, req: req, invoice: inv) + end + end + + def mark_invoice_as_sent(res:, req:, invoice: nil) + directo_record = Directo.new(response: res.as_json.to_h, + request: req, invoice_number: res.attributes['docid'].value.to_i) + directo_record.item = invoice + invoice.update(in_directo: true) + + 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 +end diff --git a/app/mailers/invoice_mailer.rb b/app/mailers/invoice_mailer.rb index a9d544d63..95b7fefd6 100644 --- a/app/mailers/invoice_mailer.rb +++ b/app/mailers/invoice_mailer.rb @@ -4,6 +4,7 @@ class InvoiceMailer < ApplicationMailer subject = default_i18n_subject(invoice_number: invoice.number) subject << I18n.t('invoice.already_paid') if paid + subject << I18n.t('invoice.monthly_invoice') if invoice.monthly_invoice attachments["invoice-#{invoice.number}.pdf"] = invoice.as_pdf mail(to: recipient, subject: subject) end diff --git a/app/models/concerns/registrar/book_keeping.rb b/app/models/concerns/registrar/book_keeping.rb index 76535ce12..4431df9b8 100644 --- a/app/models/concerns/registrar/book_keeping.rb +++ b/app/models/concerns/registrar/book_keeping.rb @@ -55,6 +55,11 @@ module Registrar::BookKeeping .where(activity_type: [AccountActivity::CREATE, AccountActivity::RENEW]) end + def monthly_invoice(month:) + invoices.where(monthly_invoice: true, issue_date: month.end_of_month.to_date, + cancelled_at: nil).first + end + def new_monthly_invoice_line(activity:, duration: nil) price = load_price(activity) line = { @@ -68,7 +73,7 @@ module Registrar::BookKeeping def finalize_invoice_line(line, price:, activity:, duration:) yearly = price.duration.in_years.to_i >= 1 - line['price'] = yearly ? (price.price.amount / price.duration.in_years.to_i) : price.price.amount + line['price'] = yearly ? (price.price.amount / price.duration.in_years.to_i).to_f : price.price.amount.to_f line['description'] = description_in_language(price: price, yearly: yearly) add_product_timeframe(line: line, activity: activity, duration: duration) if duration.present? && (duration > 1) diff --git a/app/models/invoice.rb b/app/models/invoice.rb index b7e60abfb..327a107cd 100644 --- a/app/models/invoice.rb +++ b/app/models/invoice.rb @@ -32,11 +32,14 @@ class Invoice < ApplicationRecord # rubocop:enable Layout/LineLength # rubocop:enable Style/MultilineBlockLayout validates :due_date, :currency, :seller_name, - :seller_iban, :buyer_name, :items, presence: true + :seller_iban, :buyer_name, presence: true + validates :items, presence: true, unless: -> { monthly_invoice } before_create :set_invoice_number before_create :calculate_total, unless: :total? before_create :apply_default_buyer_vat_no, unless: :buyer_vat_no? + skip_callback :create, :before, :set_invoice_number, if: -> { monthly_invoice } + skip_callback :create, :before, :calculate_total, if: -> { monthly_invoice } attribute :vat_rate, ::Type::VatRate.new @@ -118,7 +121,7 @@ class Invoice < ApplicationRecord end def subtotal - items.map(&:item_sum_without_vat).reduce(:+) + items.map(&:item_sum_without_vat).reduce(:+) || 0 end def vat_amount @@ -131,7 +134,11 @@ class Invoice < ApplicationRecord end def each(&block) - items.each(&block) + if monthly_invoice + metadata['items'].map { |el| OpenStruct.new(el) }.each(&block) + else + items.each(&block) + end end def as_pdf diff --git a/app/models/invoice/e_invoice_generator.rb b/app/models/invoice/e_invoice_generator.rb index 2361656a7..28373b1fb 100644 --- a/app/models/invoice/e_invoice_generator.rb +++ b/app/models/invoice/e_invoice_generator.rb @@ -46,10 +46,17 @@ class Invoice i.price = invoice_item.price i.quantity = invoice_item.quantity i.unit = invoice_item.unit - i.subtotal = invoice_item.subtotal - i.vat_rate = invoice_item.vat_rate - i.vat_amount = invoice_item.vat_amount - i.total = invoice_item.total + if invoice.monthly_invoice + i.subtotal = 0 + i.vat_rate = 0 + i.vat_amount = 0 + i.total = 0 + else + i.subtotal = invoice_item.subtotal + i.vat_rate = invoice_item.vat_rate + i.vat_amount = invoice_item.vat_amount + i.total = invoice_item.total + end end e_invoice_invoice_items << e_invoice_invoice_item end @@ -66,9 +73,15 @@ class Invoice i.beneficiary_name = invoice.seller_name i.beneficiary_account_number = invoice.seller_iban i.payer_name = invoice.buyer_name - i.subtotal = invoice.subtotal - i.vat_amount = invoice.vat_amount - i.total = invoice.total + if invoice.monthly_invoice + i.subtotal = 0 + i.vat_amount = 0 + i.total = 0 + else + i.subtotal = invoice.subtotal + i.vat_amount = invoice.vat_amount + i.total = invoice.total + end i.currency = invoice.currency i.delivery_channel = %i[internet_bank portal] i.payable = payable diff --git a/app/models/invoice/pdf_generator.rb b/app/models/invoice/pdf_generator.rb index 14fb99814..7762456c4 100644 --- a/app/models/invoice/pdf_generator.rb +++ b/app/models/invoice/pdf_generator.rb @@ -14,7 +14,8 @@ class Invoice private def invoice_html - ApplicationController.render(template: 'invoice/pdf', assigns: { invoice: invoice }) + template = invoice.monthly_invoice ? 'invoice/monthly_pdf' : 'invoice/pdf' + ApplicationController.render(template: template, assigns: { invoice: invoice }) end end end diff --git a/app/views/admin/invoices/show.haml b/app/views/admin/invoices/show.haml index b121c8337..f64bd9cc4 100644 --- a/app/views/admin/invoices/show.haml +++ b/app/views/admin/invoices/show.haml @@ -4,11 +4,12 @@ = @invoice .col-sm-8 %h1.text-right.text-center-xs - - if @invoice.unpaid? - = link_to(t(:payment_received), new_admin_bank_statement_path(invoice_id: @invoice.id), class: 'btn btn-default') + - unless @invoice.monthly_invoice + - if @invoice.unpaid? + = link_to(t(:payment_received), new_admin_bank_statement_path(invoice_id: @invoice.id), class: 'btn btn-default') - - if @invoice.paid? and !@invoice.cancelled? - = link_to(t(:cancel_payment), cancel_paid_admin_invoices_path(invoice_id: @invoice.id), method: 'post', data: { confirm: t(:are_you_sure) }, class: 'btn btn-warning') + - if @invoice.paid? && !@invoice.cancelled? + = link_to(t(:cancel_payment), cancel_paid_admin_invoices_path(invoice_id: @invoice.id), method: 'post', data: { confirm: t(:are_you_sure) }, class: 'btn btn-warning') = link_to(t('.download_btn'), download_admin_invoice_path(@invoice), class: 'btn btn-default') = link_to(t('.deliver_btn'), new_admin_invoice_delivery_path(@invoice), class: 'btn btn-default') diff --git a/app/views/invoice/monthly_pdf.haml b/app/views/invoice/monthly_pdf.haml new file mode 100644 index 000000000..c7e179d03 --- /dev/null +++ b/app/views/invoice/monthly_pdf.haml @@ -0,0 +1,277 @@ +%html{lang: I18n.locale.to_s} + %head + %meta{charset: "utf-8"} + :css + .container { + margin: auto; + font-size: 12px; + } + + .col-md-12 { + + } + + .col-md-6 { + width: 49%; + display: inline-block; + } + + .col-xs-4 { + width: 33%; + } + + .col-xs-2 { + width: 16%; + } + + .col-md-3 { + width: 24%; + display: inline-block; + } + + .left { + float: left; + } + + .left { + padding-right: 5px; + } + + .right { + float: right; + } + + .text-right { + text-align: right; + } + + dt { + float: left; + width: 100px; + clear: left; + text-align: right; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-weight: bold; + line-height: 1.42857; + } + + dd { + margin-left: 120px; + line-height: 1.42857; + } + + table { + width: 100%; + border-collapse: collapse; + font-size: 12px; + } + + th { + text-align: left; + border: 0px; + border-top: 1px solid #DDD; + padding: 6px; + } + + thead th { + border-bottom: 2px solid #DDD; + border-top: 0px; + } + + td { + border-top: 1px solid #DDD; + } + + td { + padding: 6px; + } + + .no-border { + border: 0px; + } + + hr { + height: 1px; + border: 0; + color: #DDD; + background-color: #DDD; + } + + .clear { + clear: both; + } + + .pull-down { + margin-top: 50px; + } + + #header { + position: relative; + min-height: 100px; + } + + img { + width: 106px; + height: 102px; + } + + #header-content { + position: absolute; + bottom: 0; + } + + #footer { + position: absolute; + bottom: 0px; + width: 99%; + } + + h1 { + margin-bottom: 5px; + } + %body + .container + #header.row + .col-sm-6.left + #header-content + %h1 + = @invoice + .col-sm-6.right + %img{src: "#{Rails.root}/public/eis-logo-black-et.png"} + .clear + %hr + .row + .col-md-6.left + %h4 + Details + %hr + %dl.dl-horizontal + %dt= t(:issue_date) + %dd= l @invoice.issue_date + + - if @invoice.cancelled? + %dt= Invoice.human_attribute_name :cancelled_at + %dd= l @invoice.cancelled_at + + %dt= t(:due_date) + - if @invoice.cancelled? + %dd= t(:cancelled) + - else + %dd= l @invoice.due_date + + %dt= t(:issuer) + %dd= @invoice.seller_contact_name + + - if @invoice.description.present? + %dt= t(:description) + %dd=@invoice.description + + %dt= Invoice.human_attribute_name :reference_no + %dd= @invoice.reference_no + + .col-md-6.right + %h4= t(:client) + %hr + %dl.dl-horizontal + %dt= t(:name) + %dd= @invoice.buyer_name + + %dt= t(:reg_no) + %dd= @invoice.buyer_reg_no + + - if @invoice.buyer_address.present? + %dt= Invoice.human_attribute_name :address + %dd= @invoice.buyer_address + + - if @invoice.buyer_country.present? + %dt= t(:country) + %dd= @invoice.buyer_country + + - if @invoice.buyer_phone.present? + %dt= t(:phone) + %dd= @invoice.buyer_phone + + - if @invoice.buyer_url.present? + %dt= t(:url) + %dd= @invoice.buyer_url + + - if @invoice.buyer_email.present? + %dt= t(:email) + %dd= @invoice.buyer_email + + .clear + .row.pull-down + .col-md-12 + .table-responsive + %table.table.table-hover.table-condensed + %thead + %tr + %th{class: 'col-xs-1'}= t(:code) + %th{class: 'col-xs-1'}= InvoiceItem.human_attribute_name :quantity + %th{class: 'col-xs-1'}= t(:unit) + %th{class: 'col-xs-5'}= t(:description) + %th{class: 'col-xs-2'}= t(:price) + %th{class: 'col-xs-2'}= t(:total) + %tbody + - @invoice.each do |invoice_item| + %tr + %td= invoice_item.product_id + %td= invoice_item.quantity + %td= invoice_item.unit + %td= invoice_item.description + - if invoice_item.price && invoice_item.quantity + %td= currency(invoice_item.price) + %td= "#{currency((invoice_item.price * invoice_item.quantity).round(3))} #{@invoice.currency}" + - else + %td= '' + %td= '' + %tfoot + %tr + %th{colspan: 4} + %th= Invoice.human_attribute_name :subtotal + %td= number_to_currency(0) + %tr + %th.no-border{colspan: 4} + %th= "VAT #{number_to_percentage(@invoice.vat_rate, precision: 1)}" + %td= number_to_currency(0) + %tr + %th.no-border{colspan: 4} + %th= t(:total) + %td= number_to_currency(0) + + #footer + %hr + .row + .col-md-3.left + = @invoice.seller_name + %br + = @invoice.seller_address + %br + = @invoice.seller_country + %br + = "#{t('reg_no')} #{@invoice.seller_reg_no}" + %br + = "#{Registrar.human_attribute_name :vat_no} #{@invoice.seller_vat_no}" + + .col-md-3.left + = @invoice.seller_phone + %br + = @invoice.seller_email + %br + = @invoice.seller_url + + .col-md-3.text-right.left + = t(:bank) + %br + = t(:iban) + %br + = t(:swift) + + .col-md-3.left + = @invoice.seller_bank + %br + = @invoice.seller_iban + %br + = @invoice.seller_swift \ No newline at end of file diff --git a/app/views/invoice/pdf.haml b/app/views/invoice/pdf.haml index 9f10acdad..dc3d4370a 100644 --- a/app/views/invoice/pdf.haml +++ b/app/views/invoice/pdf.haml @@ -234,7 +234,7 @@ %td= invoice_item.unit %td= invoice_item.quantity %td= currency(invoice_item.price) - %td= "#{currency(invoice_item.item_sum_without_vat)} #{@invoice.currency}" + %td= "#{currency(invoice_item.item_sum_without_vat)} #{@invoice.currency}" %tfoot %tr %th{colspan: 3} diff --git a/config/locales/en.yml b/config/locales/en.yml index ec8953a84..9021eff60 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -484,6 +484,8 @@ en: invoice: title: 'Invoice' already_paid: " (already paid)" + monthly_invoice: " (monthly invoice)" + monthly_invoice_description: 'Monthly invoice' bank_statements: 'Bank statements' back_to_bank_statements: 'Back to bank statements' back_to_bank_statement: 'Back to bank statement' diff --git a/config/locales/et.yml b/config/locales/et.yml index ad04db007..84672ed3c 100644 --- a/config/locales/et.yml +++ b/config/locales/et.yml @@ -9,3 +9,5 @@ et: invoice: title: 'Arve' already_paid: " (juba makstud)" + monthly_invoice: " (kuuaruanne)" + monthly_invoice_description: 'Kuuaruanne' diff --git a/db/migrate/20220818075833_add_monthly_invoice_type_columns.rb b/db/migrate/20220818075833_add_monthly_invoice_type_columns.rb new file mode 100644 index 000000000..33c98124c --- /dev/null +++ b/db/migrate/20220818075833_add_monthly_invoice_type_columns.rb @@ -0,0 +1,6 @@ +class AddMonthlyInvoiceTypeColumns < ActiveRecord::Migration[6.1] + def change + add_column :invoices, :monthly_invoice, :boolean, default: false + add_column :invoices, :metadata, :jsonb + end +end diff --git a/db/structure.sql b/db/structure.sql index a96474d53..6e4c145f1 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -955,14 +955,14 @@ CREATE TABLE public.domains ( pending_json jsonb, force_delete_date date, statuses character varying[], + status_notes public.hstore, upid integer, up_date timestamp without time zone, uuid uuid DEFAULT public.gen_random_uuid() NOT NULL, locked_by_registrant_at timestamp without time zone, force_delete_start timestamp without time zone, force_delete_data public.hstore, - json_statuses_history jsonb, - status_notes public.hstore + json_statuses_history jsonb ); @@ -985,6 +985,98 @@ CREATE SEQUENCE public.domains_id_seq ALTER SEQUENCE public.domains_id_seq OWNED BY public.domains.id; +-- +-- Name: email_address_verifications; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.email_address_verifications ( + id bigint NOT NULL, + email public.citext NOT NULL, + verified_at timestamp without time zone, + success boolean DEFAULT false NOT NULL, + domain public.citext NOT NULL +); + + +-- +-- Name: email_address_verifications_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.email_address_verifications_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: email_address_verifications_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.email_address_verifications_id_seq OWNED BY public.email_address_verifications.id; + + +-- +-- Name: email_addresses_validations; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.email_addresses_validations ( + id bigint NOT NULL, + email character varying NOT NULL, + validated_at timestamp without time zone +); + + +-- +-- Name: email_addresses_validations_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.email_addresses_validations_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: email_addresses_validations_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.email_addresses_validations_id_seq OWNED BY public.email_addresses_validations.id; + + +-- +-- Name: email_addresses_verifications; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.email_addresses_verifications ( + id bigint NOT NULL, + email character varying NOT NULL, + validated_at timestamp without time zone +); + + +-- +-- Name: email_addresses_verifications_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.email_addresses_verifications_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: email_addresses_verifications_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.email_addresses_verifications_id_seq OWNED BY public.email_addresses_verifications.id; + + -- -- Name: epp_sessions; Type: TABLE; Schema: public; Owner: - -- @@ -1104,6 +1196,8 @@ CREATE TABLE public.invoices ( issue_date date NOT NULL, e_invoice_sent_at timestamp without time zone, payment_link character varying, + monthly_invoice boolean DEFAULT false, + metadata jsonb, CONSTRAINT invoices_due_date_is_not_before_issue_date CHECK ((due_date >= issue_date)) ); @@ -2190,6 +2284,74 @@ CREATE SEQUENCE public.payment_orders_id_seq ALTER SEQUENCE public.payment_orders_id_seq OWNED BY public.payment_orders.id; +-- +-- Name: pghero_query_stats; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.pghero_query_stats ( + id bigint NOT NULL, + database text, + "user" text, + query text, + query_hash bigint, + total_time double precision, + calls bigint, + captured_at timestamp without time zone +); + + +-- +-- Name: pghero_query_stats_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.pghero_query_stats_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: pghero_query_stats_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.pghero_query_stats_id_seq OWNED BY public.pghero_query_stats.id; + + +-- +-- Name: pghero_space_stats; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.pghero_space_stats ( + id bigint NOT NULL, + database text, + schema text, + relation text, + size bigint, + captured_at timestamp without time zone +); + + +-- +-- Name: pghero_space_stats_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.pghero_space_stats_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: pghero_space_stats_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.pghero_space_stats_id_seq OWNED BY public.pghero_space_stats.id; + + -- -- Name: prices; Type: TABLE; Schema: public; Owner: - -- @@ -2228,6 +2390,48 @@ CREATE SEQUENCE public.prices_id_seq ALTER SEQUENCE public.prices_id_seq OWNED BY public.prices.id; +-- +-- Name: que_jobs; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.que_jobs ( + priority smallint DEFAULT 100 NOT NULL, + run_at timestamp with time zone DEFAULT now() NOT NULL, + job_id bigint NOT NULL, + job_class text NOT NULL, + args json DEFAULT '[]'::json NOT NULL, + error_count integer DEFAULT 0 NOT NULL, + last_error text, + queue text DEFAULT ''::text NOT NULL +); + + +-- +-- Name: TABLE que_jobs; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.que_jobs IS '3'; + + +-- +-- Name: que_jobs_job_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.que_jobs_job_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: que_jobs_job_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.que_jobs_job_id_seq OWNED BY public.que_jobs.job_id; + + -- -- Name: registrant_verifications; Type: TABLE; Schema: public; Owner: - -- @@ -2508,8 +2712,7 @@ CREATE TABLE public.validation_events ( validation_eventable_type character varying, validation_eventable_id bigint, created_at timestamp(6) without time zone NOT NULL, - updated_at timestamp(6) without time zone NOT NULL, - event_type public.validation_type + updated_at timestamp(6) without time zone NOT NULL ); @@ -2813,6 +3016,27 @@ ALTER TABLE ONLY public.domain_transfers ALTER COLUMN id SET DEFAULT nextval('pu ALTER TABLE ONLY public.domains ALTER COLUMN id SET DEFAULT nextval('public.domains_id_seq'::regclass); +-- +-- Name: email_address_verifications id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.email_address_verifications ALTER COLUMN id SET DEFAULT nextval('public.email_address_verifications_id_seq'::regclass); + + +-- +-- Name: email_addresses_validations id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.email_addresses_validations ALTER COLUMN id SET DEFAULT nextval('public.email_addresses_validations_id_seq'::regclass); + + +-- +-- Name: email_addresses_verifications id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.email_addresses_verifications ALTER COLUMN id SET DEFAULT nextval('public.email_addresses_verifications_id_seq'::regclass); + + -- -- Name: epp_sessions id; Type: DEFAULT; Schema: public; Owner: - -- @@ -3030,6 +3254,20 @@ ALTER TABLE ONLY public.notifications ALTER COLUMN id SET DEFAULT nextval('publi ALTER TABLE ONLY public.payment_orders ALTER COLUMN id SET DEFAULT nextval('public.payment_orders_id_seq'::regclass); +-- +-- Name: pghero_query_stats id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.pghero_query_stats ALTER COLUMN id SET DEFAULT nextval('public.pghero_query_stats_id_seq'::regclass); + + +-- +-- Name: pghero_space_stats id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.pghero_space_stats ALTER COLUMN id SET DEFAULT nextval('public.pghero_space_stats_id_seq'::regclass); + + -- -- Name: prices id; Type: DEFAULT; Schema: public; Owner: - -- @@ -3037,6 +3275,13 @@ ALTER TABLE ONLY public.payment_orders ALTER COLUMN id SET DEFAULT nextval('publ ALTER TABLE ONLY public.prices ALTER COLUMN id SET DEFAULT nextval('public.prices_id_seq'::regclass); +-- +-- Name: que_jobs job_id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.que_jobs ALTER COLUMN job_id SET DEFAULT nextval('public.que_jobs_job_id_seq'::regclass); + + -- -- Name: registrant_verifications id; Type: DEFAULT; Schema: public; Owner: - -- @@ -3274,6 +3519,30 @@ ALTER TABLE ONLY public.domains ADD CONSTRAINT domains_pkey PRIMARY KEY (id); +-- +-- Name: email_address_verifications email_address_verifications_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.email_address_verifications + ADD CONSTRAINT email_address_verifications_pkey PRIMARY KEY (id); + + +-- +-- Name: email_addresses_validations email_addresses_validations_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.email_addresses_validations + ADD CONSTRAINT email_addresses_validations_pkey PRIMARY KEY (id); + + +-- +-- Name: email_addresses_verifications email_addresses_verifications_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.email_addresses_verifications + ADD CONSTRAINT email_addresses_verifications_pkey PRIMARY KEY (id); + + -- -- Name: epp_sessions epp_sessions_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- @@ -3522,6 +3791,22 @@ ALTER TABLE ONLY public.payment_orders ADD CONSTRAINT payment_orders_pkey PRIMARY KEY (id); +-- +-- Name: pghero_query_stats pghero_query_stats_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.pghero_query_stats + ADD CONSTRAINT pghero_query_stats_pkey PRIMARY KEY (id); + + +-- +-- Name: pghero_space_stats pghero_space_stats_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.pghero_space_stats + ADD CONSTRAINT pghero_space_stats_pkey PRIMARY KEY (id); + + -- -- Name: prices prices_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- @@ -3530,6 +3815,14 @@ ALTER TABLE ONLY public.prices ADD CONSTRAINT prices_pkey PRIMARY KEY (id); +-- +-- Name: que_jobs que_jobs_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.que_jobs + ADD CONSTRAINT que_jobs_pkey PRIMARY KEY (queue, priority, run_at, job_id); + + -- -- Name: registrant_verifications registrant_verifications_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- @@ -3941,6 +4234,13 @@ CREATE INDEX index_domains_on_registrar_id ON public.domains USING btree (regist CREATE INDEX index_domains_on_statuses ON public.domains USING gin (statuses); +-- +-- Name: index_email_address_verifications_on_domain; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX index_email_address_verifications_on_domain ON public.email_address_verifications USING btree (domain); + + -- -- Name: index_epp_sessions_on_updated_at; Type: INDEX; Schema: public; Owner: - -- @@ -4277,6 +4577,20 @@ CREATE INDEX index_notifications_on_registrar_id ON public.notifications USING b CREATE INDEX index_payment_orders_on_invoice_id ON public.payment_orders USING btree (invoice_id); +-- +-- Name: index_pghero_query_stats_on_database_and_captured_at; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX index_pghero_query_stats_on_database_and_captured_at ON public.pghero_query_stats USING btree (database, captured_at); + + +-- +-- Name: index_pghero_space_stats_on_database_and_captured_at; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX index_pghero_space_stats_on_database_and_captured_at ON public.pghero_space_stats USING btree (database, captured_at); + + -- -- Name: index_prices_on_zone_id; Type: INDEX; Schema: public; Owner: - -- @@ -4333,13 +4647,6 @@ CREATE INDEX index_users_on_registrar_id ON public.users USING btree (registrar_ CREATE INDEX index_validation_events_on_event_data ON public.validation_events USING gin (event_data); --- --- Name: index_validation_events_on_event_type; Type: INDEX; Schema: public; Owner: - --- - -CREATE INDEX index_validation_events_on_event_type ON public.validation_events USING btree (event_type); - - -- -- Name: index_validation_events_on_validation_eventable; Type: INDEX; Schema: public; Owner: - -- @@ -5085,9 +5392,11 @@ INSERT INTO "schema_migrations" (version) VALUES ('20210708131814'), ('20210729131100'), ('20210729134625'), -('20210827185249'), -('20211029073644'), +('20211028122103'), +('20211028125245'), +('20211029082225'), ('20211124071418'), +('20211124084308'), ('20211125181033'), ('20211125184334'), ('20211126085139'), @@ -5098,12 +5407,12 @@ INSERT INTO "schema_migrations" (version) VALUES ('20220124105717'), ('20220228093211'), ('20220316140727'), -('20220406085500'), ('20220412130856'), ('20220413073315'), ('20220413084536'), ('20220413084748'), ('20220504090512'), -('20220524130709'); +('20220524130709'), +('20220818075833'); diff --git a/lib/serializers/repp/invoice.rb b/lib/serializers/repp/invoice.rb index 3b8efbd58..686eaac99 100644 --- a/lib/serializers/repp/invoice.rb +++ b/lib/serializers/repp/invoice.rb @@ -23,7 +23,8 @@ module Serializers created_at: obj.created_at, updated_at: obj.updated_at, due_date: obj.due_date, currency: obj.currency, seller: seller, buyer: buyer, items: items, - recipient: obj.buyer.billing_email + recipient: obj.buyer.billing_email, + monthly_invoice: obj.monthly_invoice } end @@ -54,11 +55,15 @@ module Serializers end def items - invoice.items.map do |item| - { description: item.description, unit: item.unit, - quantity: item.quantity, price: item.price, - sum_without_vat: item.item_sum_without_vat, - vat_amount: item.vat_amount, total: item.total } + if invoice.monthly_invoice + invoice.metadata['items'] + else + invoice.items.map do |item| + { description: item.description, unit: item.unit, + quantity: item.quantity, price: item.price, + sum_without_vat: item.item_sum_without_vat, + vat_amount: item.vat_amount, total: item.total } + end end end @@ -75,6 +80,7 @@ module Serializers due_date: invoice.due_date, total: invoice.total, recipient: invoice.buyer.billing_email, + monthly_invoice: invoice.monthly_invoice, } end # rubocop:enable Metrics/MethodLength diff --git a/test/jobs/directo_invoice_forward_job_test.rb b/test/jobs/directo_invoice_forward_job_test.rb index 33a05f644..11ed657f6 100644 --- a/test/jobs/directo_invoice_forward_job_test.rb +++ b/test/jobs/directo_invoice_forward_job_test.rb @@ -8,9 +8,9 @@ class DirectoInvoiceForwardJobTest < ActiveSupport::TestCase end def teardown - Setting.directo_monthly_number_min = 309901 - Setting.directo_monthly_number_max = 309999 - Setting.directo_monthly_number_last = 309901 + Setting.directo_monthly_number_min = 309_901 + Setting.directo_monthly_number_max = 309_999 + Setting.directo_monthly_number_last = 309_901 end def test_directo_json_sends_customer_as_hash @@ -49,7 +49,7 @@ class DirectoInvoiceForwardJobTest < ActiveSupport::TestCase price = billing_prices(:create_one_year) activity.update!(activity_type: 'create', price: price) - Setting.directo_monthly_number_max = 30991 + Setting.directo_monthly_number_max = 30_991 assert_raises 'RuntimeError' do DirectoInvoiceForwardJob.perform_now(monthly: true, dry: false) diff --git a/test/jobs/send_monthly_invoices_job_test.rb b/test/jobs/send_monthly_invoices_job_test.rb new file mode 100644 index 000000000..38c6252f4 --- /dev/null +++ b/test/jobs/send_monthly_invoices_job_test.rb @@ -0,0 +1,265 @@ +require 'test_helper' + +class SendMonthlyInvoicesJobTest < ActiveSupport::TestCase + include ActionMailer::TestHelper + + setup do + @user = registrars(:bestnames) + @date = Time.zone.parse('2010-08-06') + travel_to @date + ActionMailer::Base.deliveries.clear + EInvoice.provider = EInvoice::Providers::TestProvider.new + EInvoice::Providers::TestProvider.deliveries.clear + end + + def teardown + Setting.directo_monthly_number_min = 309_901 + Setting.directo_monthly_number_max = 309_999 + Setting.directo_monthly_number_last = 309_901 + EInvoice.provider = EInvoice::Providers::TestProvider.new + EInvoice::Providers::TestProvider.deliveries.clear + 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 = 30_991 + + assert_no_difference 'Directo.count' do + assert_raises 'RuntimeError' do + SendMonthlyInvoicesJob.perform_now + end + end + + assert_nil Invoice.find_by_monthly_invoice(true) + assert_emails 0 + assert_equal 0, EInvoice::Providers::TestProvider.deliveries.count + end + + def test_monthly_summary_is_not_delivered_if_dry + activity = account_activities(:one) + price = billing_prices(:create_one_year) + activity.update!(activity_type: 'create', price: price) + @user.update(language: 'et') + + assert_difference 'Setting.directo_monthly_number_last' do + assert_no_difference 'Directo.count' do + SendMonthlyInvoicesJob.perform_now(dry: true) + end + end + + invoice = Invoice.last + assert_equal 309_902, invoice.number + refute invoice.in_directo + assert invoice.e_invoice_sent_at.blank? + + assert_emails 0 + assert_equal 0, EInvoice::Providers::TestProvider.deliveries.count + end + + def test_monthly_summary_is_delivered_if_invoice_already_exists + @monthly_invoice = invoices(:one) + @monthly_invoice.update(number: 309_902, monthly_invoice: true, + issue_date: @date.last_month.end_of_month, + due_date: @date.last_month.end_of_month, + metadata: metadata, + in_directo: false, + e_invoice_sent_at: nil) + + 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_no_difference 'Setting.directo_monthly_number_last' do + assert_difference('Directo.count', 1) do + SendMonthlyInvoicesJob.perform_now + end + end + + invoice = Invoice.last + assert_equal 309_902, invoice.number + assert invoice.in_directo + assert_not invoice.e_invoice_sent_at.blank? + + assert_emails 1 + email = ActionMailer::Base.deliveries.last + assert_equal ['billing@bestnames.test'], email.to + assert_equal 'Invoice no. 309902 (monthly invoice)', email.subject + assert email.attachments['invoice-309902.pdf'] + + assert_equal 1, EInvoice::Providers::TestProvider.deliveries.count + 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 + assert_difference('Directo.count', 1) do + SendMonthlyInvoicesJob.perform_now + end + end + + invoice = Invoice.last + assert_equal 309_902, invoice.number + assert invoice.in_directo + assert_not invoice.e_invoice_sent_at.blank? + + assert_emails 1 + email = ActionMailer::Base.deliveries.last + assert_equal ['billing@bestnames.test'], email.to + assert_equal 'Invoice no. 309902 (monthly invoice)', email.subject + assert email.attachments['invoice-309902.pdf'] + + assert_equal 1, EInvoice::Providers::TestProvider.deliveries.count + 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 + SendMonthlyInvoicesJob.perform_now + end + + invoice = Invoice.last + assert_equal 309_902, invoice.number + assert invoice.in_directo + assert_not invoice.e_invoice_sent_at.blank? + 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 + SendMonthlyInvoicesJob.perform_now + end + + invoice = Invoice.last + assert_equal 309_902, invoice.number + assert invoice.in_directo + assert_not invoice.e_invoice_sent_at.blank? + end + + def test_sends_each_monthly_invoice_separately + WebMock.reset! + + activity = account_activities(:one) + price = billing_prices(:create_one_year) + price.update(duration: 3.years) + activity.update(activity_type: 'create', price: price) + + # Creating account activity for second action + another_activity = activity.dup + another_activity.account = accounts(:two) + + AccountActivity.skip_callback(:create, :after, :update_balance) + another_activity.created_at = Time.zone.parse('2010-07-05 10:00') + another_activity.save + AccountActivity.set_callback(:create, :after, :update_balance) + + response = <<-XML + + + + + XML + + first_registrar_stub = stub_request(:post, ENV['directo_invoice_url']).with do |request| + body = CGI.unescape(request.body) + (body.include? 'StartDate') && (body.include? 'EndDate') && (body.include? 'bestnames') + end.to_return(status: 200, body: response) + + second_registrar_stub = stub_request(:post, ENV['directo_invoice_url']).with do |request| + body = CGI.unescape(request.body) + (body.include? 'StartDate') && (body.include? 'EndDate') && (body.include? 'goodnames') + end.to_return(status: 200, body: response) + + assert_difference('Invoice.count', 2) do + assert_difference('Directo.count', 2) do + SendMonthlyInvoicesJob.perform_now + end + end + + assert_requested first_registrar_stub + assert_requested second_registrar_stub + + assert_emails 2 + assert_equal 2, EInvoice::Providers::TestProvider.deliveries.count + end + + private + + def metadata + { + "items" => [ + { "description" => "Domeenide registreerimine - Juuli 2010" }, + { "product_id" => nil, "quantity" => 1, "unit" => "tk", "price" => 10.0, "description" => ".test registreerimine: 1 aasta(t)" }, + { "product_id" => "ETTEM06", "description" => "Domeenide ettemaks", "quantity" => -1, "price" => 10.0, "unit" => "tk" }, + ], + } + end +end \ No newline at end of file From 931e3907cd4f50834d3edbb8035280f9655110fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergei=20Ts=C3=B5ganov?= Date: Mon, 22 Aug 2022 09:20:57 +0300 Subject: [PATCH 52/81] Fixed domains search by registrant --- app/controllers/repp/v1/domains_controller.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/controllers/repp/v1/domains_controller.rb b/app/controllers/repp/v1/domains_controller.rb index 29b259a67..05f1e2c95 100644 --- a/app/controllers/repp/v1/domains_controller.rb +++ b/app/controllers/repp/v1/domains_controller.rb @@ -12,14 +12,14 @@ module Repp desc 'Get all existing domains' def index authorize! :info, Epp::Domain - records = current_user.registrar.domains + records = current_user.registrar.domains.includes(:registrar, :registrant) q = records.ransack(PartialSearchFormatter.format(search_params)) q.sorts = ['valid_to asc', 'created_at desc'] if q.sorts.empty? # use distinct: false here due to ransack bug: # https://github.com/activerecord-hackery/ransack/issues/429 domains = q.result(distinct: false) - limited_domains = domains.limit(limit).offset(offset).includes(:registrar, :registrant) + limited_domains = domains.limit(limit).offset(offset) render_success(data: { new_domain: records.any? ? serialized_domains([records.last]) : [], domains: serialized_domains(limited_domains.to_a.uniq), @@ -238,7 +238,7 @@ module Repp def index_params params.permit(:limit, :offset, :details, :simple, :q, - q: %i[s name_matches registrant_id_eq contacts_ident_eq + q: %i[s name_matches registrant_code_eq contacts_ident_eq nameservers_hostname_eq valid_to_gteq valid_to_lteq statuses_contains_array] + [s: []]) end From 72022aab8d28ee32d5ec2efa32941c13b4ed5ee0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergei=20Ts=C3=B5ganov?= Date: Mon, 22 Aug 2022 10:23:14 +0300 Subject: [PATCH 53/81] Refactored code --- app/jobs/delete_monthly_invoices_job.rb | 2 +- app/jobs/send_monthly_invoices_job.rb | 86 +++++-------------- app/models/concerns/registrar/book_keeping.rb | 9 ++ app/models/registrar.rb | 50 ++++++++++- 4 files changed, 78 insertions(+), 69 deletions(-) diff --git a/app/jobs/delete_monthly_invoices_job.rb b/app/jobs/delete_monthly_invoices_job.rb index 8a38d0d14..daf79827a 100644 --- a/app/jobs/delete_monthly_invoices_job.rb +++ b/app/jobs/delete_monthly_invoices_job.rb @@ -7,4 +7,4 @@ class DeleteMonthlyInvoicesJob < ApplicationJob in_directo: false, e_invoice_sent_at: nil) invoices.delete_all end -end \ No newline at end of file +end diff --git a/app/jobs/send_monthly_invoices_job.rb b/app/jobs/send_monthly_invoices_job.rb index ddb4bd80c..78f35ad1f 100644 --- a/app/jobs/send_monthly_invoices_job.rb +++ b/app/jobs/send_monthly_invoices_job.rb @@ -16,22 +16,17 @@ class SendMonthlyInvoicesJob < ApplicationJob Setting.directo_receipt_payment_term) end + # rubocop:disable Metrics/MethodLength def send_monthly_invoices - Registrar.where.not(test_registrar: true).find_each do |registrar| - next unless registrar.cash_account - + Registrar.with_cash_accounts.find_each do |registrar| summary = registrar.monthly_summary(month: @month) next if summary.nil? invoice = registrar.monthly_invoice(month: @month) || create_invoice(summary, registrar) next if invoice.nil? || @dry - InvoiceMailer.invoice_email(invoice: invoice, - recipient: registrar.billing_email) - .deliver_now - - SendEInvoiceJob.set(wait: 1.minute).perform_now(invoice.id, payable: false) - + send_email_to_registrar(invoice: invoice, registrar: registrar) + send_e_invoice(invoice.id) next if invoice.in_directo Rails.logger.info("[DIRECTO] Trying to send monthly invoice #{invoice.number}") @@ -44,50 +39,24 @@ class SendMonthlyInvoicesJob < ApplicationJob sync_with_directo end end + # rubocop:enable Metrics/MethodLength + + def send_email_to_registrar(invoice:, registrar:) + InvoiceMailer.invoice_email(invoice: invoice, + recipient: registrar.billing_email) + .deliver_now + end + + def send_e_invoice(invoice_id) + SendEInvoiceJob.set(wait: 1.minute).perform_now(invoice_id, payable: false) + end def create_invoice(summary, registrar) - vat_rate = ::Invoice::VatRateCalculator.new(registrar: registrar).calculate - invoice = Invoice.new( - number: assign_monthly_number, - issue_date: summary['date'].to_date, - due_date: summary['date'].to_date, - currency: 'EUR', - description: I18n.t('invoice.monthly_invoice_description'), - seller_name: Setting.registry_juridical_name, - seller_reg_no: Setting.registry_reg_no, - seller_iban: Setting.registry_iban, - seller_bank: Setting.registry_bank, - seller_swift: Setting.registry_swift, - seller_vat_no: Setting.registry_vat_no, - seller_country_code: Setting.registry_country_code, - seller_state: Setting.registry_state, - seller_street: Setting.registry_street, - seller_city: Setting.registry_city, - seller_zip: Setting.registry_zip, - seller_phone: Setting.registry_phone, - seller_url: Setting.registry_url, - seller_email: Setting.registry_email, - seller_contact_name: Setting.registry_invoice_contact, - buyer: registrar, - buyer_name: registrar.name, - buyer_reg_no: registrar.reg_no, - buyer_country_code: registrar.address_country_code, - buyer_state: registrar.address_state, - buyer_street: registrar.address_street, - buyer_city: registrar.address_city, - buyer_zip: registrar.address_zip, - buyer_phone: registrar.phone, - buyer_url: registrar.website, - buyer_email: registrar.email, - reference_no: registrar.reference_no, - vat_rate: vat_rate, - monthly_invoice: true, - metadata: { items: summary['invoice_lines'] }, - total: 0 - ) + invoice = registrar.init_monthly_invoice(summary) + invoice.number = assign_monthly_number return unless invoice.save! - update_directo_number(num: invoice.number) + update_monthly_invoice_number(num: invoice.number) invoice end @@ -103,19 +72,6 @@ class SendMonthlyInvoicesJob < ApplicationJob Rails.logger.info('[Directo] Failed to communicate via API') end - def assign_monthly_numbers - invoices_count = @directo_client.invoices.count - last_directo_num = [Setting.directo_monthly_number_last.presence.try(:to_i), - @min_directo_num].compact.max || 0 - raise 'Directo Counter is out of period!' if directo_counter_exceedable?(invoices_count, - last_directo_num) - - @directo_client.invoices.each do |inv| - last_directo_num += 1 - inv.number = last_directo_num - end - end - def assign_monthly_number last_directo_num = [Setting.directo_monthly_number_last.presence.try(:to_i), @min_directo_num].compact.max || 0 @@ -134,11 +90,11 @@ class SendMonthlyInvoicesJob < ApplicationJob Rails.logger.info "[Directo] - Responded with body: #{body}" Nokogiri::XML(body).css('Result').each do |res| inv = Invoice.find_by(number: res.attributes['docid'].value.to_i) - mark_invoice_as_sent(res: res, req: req, invoice: inv) + mark_invoice_as_sent_to_directo(res: res, req: req, invoice: inv) end end - def mark_invoice_as_sent(res:, req:, invoice: nil) + def mark_invoice_as_sent_to_directo(res:, req:, invoice: nil) directo_record = Directo.new(response: res.as_json.to_h, request: req, invoice_number: res.attributes['docid'].value.to_i) directo_record.item = invoice @@ -147,7 +103,7 @@ class SendMonthlyInvoicesJob < ApplicationJob directo_record.save! end - def update_directo_number(num:) + def update_monthly_invoice_number(num:) return unless num.to_i > Setting.directo_monthly_number_last.to_i Setting.directo_monthly_number_last = num.to_i diff --git a/app/models/concerns/registrar/book_keeping.rb b/app/models/concerns/registrar/book_keeping.rb index 4431df9b8..be281fb56 100644 --- a/app/models/concerns/registrar/book_keeping.rb +++ b/app/models/concerns/registrar/book_keeping.rb @@ -4,6 +4,15 @@ module Registrar::BookKeeping DOMAIN_TO_PRODUCT = { 'ee': '01EE', 'com.ee': '02COM', 'pri.ee': '03PRI', 'fie.ee': '04FIE', 'med.ee': '05MED' }.freeze + included do + scope :with_cash_accounts, (lambda do + joins(:accounts) + .where('accounts.account_type = ? AND test_registrar != ?', + Account::CASH, + true) + end) + end + def monthly_summary(month:) activities = monthly_activites(month) return unless activities.any? diff --git a/app/models/registrar.rb b/app/models/registrar.rb index 4c0098de0..811fc3921 100644 --- a/app/models/registrar.rb +++ b/app/models/registrar.rb @@ -56,9 +56,48 @@ class Registrar < ApplicationRecord end end - def issue_prepayment_invoice(amount, description = nil, payable: true) - vat_rate = ::Invoice::VatRateCalculator.new(registrar: self).calculate + # rubocop:disable Metrics/MethodLength + def init_monthly_invoice(summary) + Invoice.new( + issue_date: summary['date'].to_date, + due_date: summary['date'].to_date, + currency: 'EUR', + description: I18n.t('invoice.monthly_invoice_description'), + seller_name: Setting.registry_juridical_name, + seller_reg_no: Setting.registry_reg_no, + seller_iban: Setting.registry_iban, + seller_bank: Setting.registry_bank, + seller_swift: Setting.registry_swift, + seller_vat_no: Setting.registry_vat_no, + seller_country_code: Setting.registry_country_code, + seller_state: Setting.registry_state, + seller_street: Setting.registry_street, + seller_city: Setting.registry_city, + seller_zip: Setting.registry_zip, + seller_phone: Setting.registry_phone, + seller_url: Setting.registry_url, + seller_email: Setting.registry_email, + seller_contact_name: Setting.registry_invoice_contact, + buyer: self, + buyer_name: name, + buyer_reg_no: reg_no, + buyer_country_code: address_country_code, + buyer_state: address_state, + buyer_street: address_street, + buyer_city: address_city, + buyer_zip: address_zip, + buyer_phone: phone, + buyer_url: website, + buyer_email: email, + reference_no: reference_no, + vat_rate: calculate_vat_rate, + monthly_invoice: true, + metadata: { items: summary['invoice_lines'] }, + total: 0 + ) + end + def issue_prepayment_invoice(amount, description = nil, payable: true) invoice = invoices.create!( issue_date: Time.zone.today, due_date: (Time.zone.now + Setting.days_to_keep_invoices_active.days).to_date, @@ -91,7 +130,7 @@ class Registrar < ApplicationRecord buyer_url: website, buyer_email: email, reference_no: reference_no, - vat_rate: vat_rate, + vat_rate: calculate_vat_rate, items_attributes: [ { description: 'prepayment', @@ -124,6 +163,7 @@ class Registrar < ApplicationRecord invoice end + # rubocop:enable Metrics/MethodLength def cash_account accounts.find_by(account_type: Account::CASH) @@ -265,4 +305,8 @@ class Registrar < ApplicationRecord def vat_liable_in_foreign_country? !vat_liable_locally? end + + def calculate_vat_rate + ::Invoice::VatRateCalculator.new(registrar: self).calculate + end end From a21e5c195423ee43250b9d16ebe4ed2b48d590c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergei=20Ts=C3=B5ganov?= Date: Tue, 23 Aug 2022 09:20:39 +0300 Subject: [PATCH 54/81] Modified views for monthly invoices --- app/models/concerns/registrar/book_keeping.rb | 7 ++-- app/views/admin/invoices/show.haml | 5 ++- .../partials/_monthly_invoice_items.haml | 38 +++++++++++++++++++ app/views/registrar/invoices/show.haml | 5 ++- 4 files changed, 49 insertions(+), 6 deletions(-) create mode 100644 app/views/registrar/invoices/partials/_monthly_invoice_items.haml diff --git a/app/models/concerns/registrar/book_keeping.rb b/app/models/concerns/registrar/book_keeping.rb index be281fb56..e1c980ee2 100644 --- a/app/models/concerns/registrar/book_keeping.rb +++ b/app/models/concerns/registrar/book_keeping.rb @@ -6,10 +6,9 @@ module Registrar::BookKeeping included do scope :with_cash_accounts, (lambda do - joins(:accounts) - .where('accounts.account_type = ? AND test_registrar != ?', - Account::CASH, - true) + joins(:accounts).where('accounts.account_type = ? AND test_registrar != ?', + Account::CASH, + true) end) end diff --git a/app/views/admin/invoices/show.haml b/app/views/admin/invoices/show.haml index f64bd9cc4..42f7d769c 100644 --- a/app/views/admin/invoices/show.haml +++ b/app/views/admin/invoices/show.haml @@ -25,6 +25,9 @@ .col-md-6= render 'registrar/invoices/partials/seller' .col-md-6= render 'registrar/invoices/partials/buyer' .row - .col-md-12= render 'registrar/invoices/partials/items' + - if @invoice.monthly_invoice + .col-md-12= render 'registrar/invoices/partials/monthly_invoice_items' + - else + .col-md-12= render 'registrar/invoices/partials/items' .row .col-md-12= render 'registrar/invoices/partials/payment_orders' diff --git a/app/views/registrar/invoices/partials/_monthly_invoice_items.haml b/app/views/registrar/invoices/partials/_monthly_invoice_items.haml new file mode 100644 index 000000000..787218ea3 --- /dev/null +++ b/app/views/registrar/invoices/partials/_monthly_invoice_items.haml @@ -0,0 +1,38 @@ +%h4= t(:items) +%hr +.table-responsive + %table.table.table-hover.table-condensed + %thead + %tr + %th{class: 'col-xs-1'}= t(:code) + %th{class: 'col-xs-1'}= InvoiceItem.human_attribute_name :quantity + %th{class: 'col-xs-1'}= t(:unit) + %th{class: 'col-xs-5'}= t(:description) + %th{class: 'col-xs-2'}= t(:price) + %th{class: 'col-xs-2'}= t(:total) + %tbody + - @invoice.each do |invoice_item| + %tr + %td= invoice_item.product_id + %td= invoice_item.quantity + %td= invoice_item.unit + %td= invoice_item.description + - if invoice_item.price && invoice_item.quantity + %td= currency(invoice_item.price) + %td= "#{currency((invoice_item.price * invoice_item.quantity).round(3))} #{@invoice.currency}" + - else + %td= '' + %td= '' + %tfoot + %tr + %th{colspan: 4} + %th= Invoice.human_attribute_name :subtotal + %td= number_to_currency(0) + %tr + %th.no-border{colspan: 4} + %th= "VAT #{number_to_percentage(@invoice.vat_rate, precision: 1)}" + %td= number_to_currency(0) + %tr + %th.no-border{colspan: 4} + %th= t(:total) + %td= number_to_currency(0) \ No newline at end of file diff --git a/app/views/registrar/invoices/show.haml b/app/views/registrar/invoices/show.haml index 5e6104091..dd19a0bea 100644 --- a/app/views/registrar/invoices/show.haml +++ b/app/views/registrar/invoices/show.haml @@ -13,7 +13,10 @@ .col-md-6= render 'registrar/invoices/partials/seller' .col-md-6= render 'registrar/invoices/partials/buyer' .row - .col-md-12= render 'registrar/invoices/partials/items' + - if @invoice.monthly_invoice + .col-md-12= render 'registrar/invoices/partials/monthly_invoice_items' + - else + .col-md-12= render 'registrar/invoices/partials/items' - if @invoice.payable? .row.semifooter From dc42d792524a9ecc64130ffae81fdc49ae8255ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20V=C3=B5hmar?= Date: Tue, 23 Aug 2022 12:54:02 +0300 Subject: [PATCH 55/81] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 739854c16..83be7b2a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +23.08.2022 +* REPP update to fix search by registrant in Registrar portal [#2425](https://github.com/internetee/registry/pull/2425) + 21.07.2022 * Removed deprecated statuses_before_force_delete field [#2363](https://github.com/internetee/registry/issues/2363) From 161c258210eae3e3205c82e8db06cf651847454f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20V=C3=B5hmar?= Date: Thu, 25 Aug 2022 17:36:00 +0300 Subject: [PATCH 56/81] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 83be7b2a5..11fc5fe0c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +25.08.2022 +* Contact creation fix to not require postal addresses in Registrar portal [#2421](https://github.com/internetee/registry/pull/2421) + 23.08.2022 * REPP update to fix search by registrant in Registrar portal [#2425](https://github.com/internetee/registry/pull/2425) From 14a9b5b7097d2f999de1683042e8bfbcbe81cdaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergei=20Ts=C3=B5ganov?= Date: Mon, 29 Aug 2022 14:06:04 +0300 Subject: [PATCH 57/81] Modifications for monthly invoices --- Gemfile.lock | 6 +- app/jobs/send_e_invoice_job.rb | 3 +- app/models/concerns/registrar/book_keeping.rb | 5 +- app/models/invoice/e_invoice_generator.rb | 64 +++++++++++-------- test/jobs/send_monthly_invoices_job_test.rb | 4 +- 5 files changed, 49 insertions(+), 33 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index d1e38fab5..a6c822723 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -18,10 +18,10 @@ GIT GIT remote: https://github.com/internetee/e_invoice.git - revision: 312cac173935f434e449d1714f3497bfee9f8995 + revision: da18f3da3219315f732b94fbc165fe83cb828a99 branch: master specs: - e_invoice (0.1.0) + e_invoice (0.1.1) builder (~> 3.2) nokogiri savon @@ -603,4 +603,4 @@ DEPENDENCIES wkhtmltopdf-binary (~> 0.12.5.1) BUNDLED WITH - 2.3.16 + 2.3.21 diff --git a/app/jobs/send_e_invoice_job.rb b/app/jobs/send_e_invoice_job.rb index 4e77926b0..c48dccd2a 100644 --- a/app/jobs/send_e_invoice_job.rb +++ b/app/jobs/send_e_invoice_job.rb @@ -16,8 +16,9 @@ class SendEInvoiceJob < ApplicationJob def need_to_process_invoice?(invoice:, payable:) logger.info "Checking if need to process e-invoice #{invoice}, payable: #{payable}" + unprocessable = invoice.do_not_send_e_invoice? && (invoice.monthly_invoice ? true : payable) return false if invoice.blank? - return false if invoice.do_not_send_e_invoice? && (invoice.monthly_invoice ? true : payable) + return false if unprocessable true end diff --git a/app/models/concerns/registrar/book_keeping.rb b/app/models/concerns/registrar/book_keeping.rb index e1c980ee2..13574f3c2 100644 --- a/app/models/concerns/registrar/book_keeping.rb +++ b/app/models/concerns/registrar/book_keeping.rb @@ -74,7 +74,7 @@ module Registrar::BookKeeping 'product_id': DOMAIN_TO_PRODUCT[price.zone_name.to_sym], 'quantity': 1, 'unit': language == 'en' ? 'pc' : 'tk', - } + }.with_indifferent_access finalize_invoice_line(line, price: price, duration: duration, activity: activity) end @@ -98,9 +98,10 @@ module Registrar::BookKeeping def description_in_language(price:, yearly:) timeframe_string = yearly ? 'yearly' : 'monthly' locale_string = "registrar.invoice_#{timeframe_string}_product_description" + length = yearly ? price.duration.in_years.to_i : price.duration.in_months.to_i I18n.with_locale(language == 'en' ? 'en' : 'et') do - I18n.t(locale_string, tld: ".#{price.zone_name}", length: price.duration.in_years.to_i) + I18n.t(locale_string, tld: ".#{price.zone_name}", length: length) end end diff --git a/app/models/invoice/e_invoice_generator.rb b/app/models/invoice/e_invoice_generator.rb index 28373b1fb..4830e486a 100644 --- a/app/models/invoice/e_invoice_generator.rb +++ b/app/models/invoice/e_invoice_generator.rb @@ -41,22 +41,10 @@ class Invoice e_invoice_invoice_items = [] invoice.each do |invoice_item| - e_invoice_invoice_item = EInvoice::InvoiceItem.new.tap do |i| - i.description = invoice_item.description - i.price = invoice_item.price - i.quantity = invoice_item.quantity - i.unit = invoice_item.unit - if invoice.monthly_invoice - i.subtotal = 0 - i.vat_rate = 0 - i.vat_amount = 0 - i.total = 0 - else - i.subtotal = invoice_item.subtotal - i.vat_rate = invoice_item.vat_rate - i.vat_amount = invoice_item.vat_amount - i.total = invoice_item.total - end + if invoice.monthly_invoice + e_invoice_invoice_item = generate_monthly_invoice_item(invoice, invoice_item) + else + e_invoice_invoice_item = generate_normal_invoice_item(invoice_item) end e_invoice_invoice_items << e_invoice_invoice_item end @@ -73,21 +61,47 @@ class Invoice i.beneficiary_name = invoice.seller_name i.beneficiary_account_number = invoice.seller_iban i.payer_name = invoice.buyer_name - if invoice.monthly_invoice - i.subtotal = 0 - i.vat_amount = 0 - i.total = 0 - else - i.subtotal = invoice.subtotal - i.vat_amount = invoice.vat_amount - i.total = invoice.total - end + i.subtotal = invoice.subtotal + i.vat_amount = invoice.vat_amount + i.total = invoice.total i.currency = invoice.currency i.delivery_channel = %i[internet_bank portal] i.payable = payable + i.monthly_invoice = invoice.monthly_invoice end EInvoice::EInvoice.new(date: Time.zone.today, invoice: e_invoice_invoice) end + + private + + def generate_normal_invoice_item(item) + EInvoice::InvoiceItem.new.tap do |i| + i.description = item.description + i.unit = item.unit + i.price = item.price + i.quantity = item.quantity + i.subtotal = item.subtotal + i.vat_rate = item.vat_rate + i.vat_amount = item.vat_amount + i.total = item.total + end + end + + def generate_monthly_invoice_item(invoice, item) + EInvoice::InvoiceItem.new.tap do |i| + i.description = item.description + i.description = "[#{item.product_id}] #{item.description}" if item.product_id + i.unit = item.unit + i.price = item.price + i.quantity = item.quantity + if item.price && item.quantity + i.subtotal = (item.price * item.quantity).round(3) + i.vat_rate = invoice.vat_rate + i.vat_amount = i.subtotal * (i.vat_rate / 100) + i.total = i.subtotal + i.vat_amount + end + end + end end end diff --git a/test/jobs/send_monthly_invoices_job_test.rb b/test/jobs/send_monthly_invoices_job_test.rb index 38c6252f4..95937df73 100644 --- a/test/jobs/send_monthly_invoices_job_test.rb +++ b/test/jobs/send_monthly_invoices_job_test.rb @@ -110,7 +110,7 @@ class SendMonthlyInvoicesJobTest < ActiveSupport::TestCase def test_monthly_summary_is_delivered_in_estonian activity = account_activities(:one) - price = billing_prices(:create_one_year) + price = billing_prices(:create_one_month) activity.update!(activity_type: 'create', price: price) @user.update(language: 'et') @@ -124,7 +124,7 @@ class SendMonthlyInvoicesJobTest < ActiveSupport::TestCase stub_request(:post, ENV['directo_invoice_url']).with do |request| body = CGI.unescape(request.body) - (body.include? '.test registreerimine: 1 aasta(t)') && + (body.include? '.test registreerimine: 3 kuu(d)') && (body.include? 'Domeenide ettemaks') && (body.include? '309902') end.to_return(status: 200, body: response) From 77a6f6d9855972c1cd23e24f39ce99ffee12d96f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergei=20Ts=C3=B5ganov?= Date: Mon, 29 Aug 2022 14:15:57 +0300 Subject: [PATCH 58/81] Set perform later for SendEInvoiceJob --- app/jobs/send_monthly_invoices_job.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/jobs/send_monthly_invoices_job.rb b/app/jobs/send_monthly_invoices_job.rb index 78f35ad1f..647bd0752 100644 --- a/app/jobs/send_monthly_invoices_job.rb +++ b/app/jobs/send_monthly_invoices_job.rb @@ -48,7 +48,7 @@ class SendMonthlyInvoicesJob < ApplicationJob end def send_e_invoice(invoice_id) - SendEInvoiceJob.set(wait: 1.minute).perform_now(invoice_id, payable: false) + SendEInvoiceJob.set(wait: 1.minute).perform_later(invoice_id, payable: false) end def create_invoice(summary, registrar) From 5d92442a48f7ed63526005bebec93e74094b0ff7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergei=20Ts=C3=B5ganov?= Date: Mon, 29 Aug 2022 16:06:14 +0300 Subject: [PATCH 59/81] Updated test --- test/jobs/send_monthly_invoices_job_test.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/jobs/send_monthly_invoices_job_test.rb b/test/jobs/send_monthly_invoices_job_test.rb index 95937df73..9417278d7 100644 --- a/test/jobs/send_monthly_invoices_job_test.rb +++ b/test/jobs/send_monthly_invoices_job_test.rb @@ -94,6 +94,8 @@ class SendMonthlyInvoicesJobTest < ActiveSupport::TestCase end end + perform_enqueued_jobs + invoice = Invoice.last assert_equal 309_902, invoice.number assert invoice.in_directo @@ -135,6 +137,8 @@ class SendMonthlyInvoicesJobTest < ActiveSupport::TestCase end end + perform_enqueued_jobs + invoice = Invoice.last assert_equal 309_902, invoice.number assert invoice.in_directo @@ -171,6 +175,8 @@ class SendMonthlyInvoicesJobTest < ActiveSupport::TestCase SendMonthlyInvoicesJob.perform_now end + perform_enqueued_jobs + invoice = Invoice.last assert_equal 309_902, invoice.number assert invoice.in_directo @@ -198,6 +204,8 @@ class SendMonthlyInvoicesJobTest < ActiveSupport::TestCase SendMonthlyInvoicesJob.perform_now end + perform_enqueued_jobs + invoice = Invoice.last assert_equal 309_902, invoice.number assert invoice.in_directo @@ -244,6 +252,8 @@ class SendMonthlyInvoicesJobTest < ActiveSupport::TestCase end end + perform_enqueued_jobs + assert_requested first_registrar_stub assert_requested second_registrar_stub From 34f5347c1f57fa126b26ba2c48e9b33e17550212 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergei=20Ts=C3=B5ganov?= Date: Tue, 30 Aug 2022 14:15:45 +0300 Subject: [PATCH 60/81] Added product_id to monthly invoice items --- Gemfile.lock | 4 ++-- app/models/invoice/e_invoice_generator.rb | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index a6c822723..44290fffd 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -18,10 +18,10 @@ GIT GIT remote: https://github.com/internetee/e_invoice.git - revision: da18f3da3219315f732b94fbc165fe83cb828a99 + revision: 590dcd3b769ea57edd4c4626547b37120a02b127 branch: master specs: - e_invoice (0.1.1) + e_invoice (0.1.2) builder (~> 3.2) nokogiri savon diff --git a/app/models/invoice/e_invoice_generator.rb b/app/models/invoice/e_invoice_generator.rb index 4830e486a..326984476 100644 --- a/app/models/invoice/e_invoice_generator.rb +++ b/app/models/invoice/e_invoice_generator.rb @@ -91,13 +91,13 @@ class Invoice def generate_monthly_invoice_item(invoice, item) EInvoice::InvoiceItem.new.tap do |i| i.description = item.description - i.description = "[#{item.product_id}] #{item.description}" if item.product_id + i.product_id = item.product_id i.unit = item.unit i.price = item.price i.quantity = item.quantity + i.vat_rate = invoice.vat_rate if item.price && item.quantity i.subtotal = (item.price * item.quantity).round(3) - i.vat_rate = invoice.vat_rate i.vat_amount = i.subtotal * (i.vat_rate / 100) i.total = i.subtotal + i.vat_amount end From 3409dc37a5284bebf20de43bdd78b4071d843432 Mon Sep 17 00:00:00 2001 From: olegphenomenon Date: Tue, 30 Aug 2022 14:16:10 +0300 Subject: [PATCH 61/81] added send invoice status during payment order paying --- app/models/bank_transaction.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/models/bank_transaction.rb b/app/models/bank_transaction.rb index b8bf3eb77..91429d40c 100644 --- a/app/models/bank_transaction.rb +++ b/app/models/bank_transaction.rb @@ -47,6 +47,8 @@ class BankTransaction < ApplicationRecord if create_activity(registrar, invoice) payment_order.paid! + EisBilling::SendInvoiceStatus.send_info(invoice_number: invoice.number, + status: 'paid') else payment_order.update(notes: 'Failed to create activity', status: 'failed') end From 0a20f567c24457d50218fa6abdb6b6f45151c420 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergei=20Ts=C3=B5ganov?= Date: Tue, 30 Aug 2022 17:35:25 +0300 Subject: [PATCH 62/81] Added monthly invoice name attribute --- Gemfile.lock | 4 +-- app/models/invoice/e_invoice_generator.rb | 35 +++++++++-------------- 2 files changed, 15 insertions(+), 24 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 44290fffd..fb51d2bf1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -18,10 +18,10 @@ GIT GIT remote: https://github.com/internetee/e_invoice.git - revision: 590dcd3b769ea57edd4c4626547b37120a02b127 + revision: 9f850465697a2448a31ebddb83c1be5a5a9be3d2 branch: master specs: - e_invoice (0.1.2) + e_invoice (0.1.3) builder (~> 3.2) nokogiri savon diff --git a/app/models/invoice/e_invoice_generator.rb b/app/models/invoice/e_invoice_generator.rb index 326984476..8d9675475 100644 --- a/app/models/invoice/e_invoice_generator.rb +++ b/app/models/invoice/e_invoice_generator.rb @@ -41,17 +41,16 @@ class Invoice e_invoice_invoice_items = [] invoice.each do |invoice_item| - if invoice.monthly_invoice - e_invoice_invoice_item = generate_monthly_invoice_item(invoice, invoice_item) - else - e_invoice_invoice_item = generate_normal_invoice_item(invoice_item) - end + e_invoice_invoice_item = generate_invoice_item(invoice, invoice_item) e_invoice_invoice_items << e_invoice_invoice_item end + e_invoice_name_item = e_invoice_invoice_items.shift if invoice.monthly_invoice + e_invoice_invoice = EInvoice::Invoice.new.tap do |i| i.seller = seller i.buyer = buyer + i.name = e_invoice_name_item&.description i.items = e_invoice_invoice_items i.number = invoice.number i.date = invoice.issue_date @@ -75,31 +74,23 @@ class Invoice private - def generate_normal_invoice_item(item) + def generate_invoice_item(invoice, item) EInvoice::InvoiceItem.new.tap do |i| i.description = item.description i.unit = item.unit i.price = item.price i.quantity = item.quantity - i.subtotal = item.subtotal - i.vat_rate = item.vat_rate - i.vat_amount = item.vat_amount - i.total = item.total - end - end - - def generate_monthly_invoice_item(invoice, item) - EInvoice::InvoiceItem.new.tap do |i| - i.description = item.description - i.product_id = item.product_id - i.unit = item.unit - i.price = item.price - i.quantity = item.quantity - i.vat_rate = invoice.vat_rate - if item.price && item.quantity + if invoice.monthly_invoice && item.price && item.quantity + i.product_id = item.product_id + i.vat_rate = invoice.vat_rate i.subtotal = (item.price * item.quantity).round(3) i.vat_amount = i.subtotal * (i.vat_rate / 100) i.total = i.subtotal + i.vat_amount + else + i.subtotal = item.subtotal + i.vat_rate = item.vat_rate + i.vat_amount = item.vat_amount + i.total = item.total end end end From 80c4057b8fcf2b2cc8fa315d7b681b5c0c22d0ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergei=20Ts=C3=B5ganov?= Date: Wed, 31 Aug 2022 14:22:01 +0300 Subject: [PATCH 63/81] Corrected monthly invoices for sending to omniva --- app/jobs/send_monthly_invoices_job.rb | 36 ++++++++++++++++++- app/models/concerns/registrar/book_keeping.rb | 7 ++-- 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/app/jobs/send_monthly_invoices_job.rb b/app/jobs/send_monthly_invoices_job.rb index 647bd0752..00a003444 100644 --- a/app/jobs/send_monthly_invoices_job.rb +++ b/app/jobs/send_monthly_invoices_job.rb @@ -39,6 +39,7 @@ class SendMonthlyInvoicesJob < ApplicationJob sync_with_directo end end + # rubocop:enable Metrics/MethodLength def send_email_to_registrar(invoice:, registrar:) @@ -52,7 +53,7 @@ class SendMonthlyInvoicesJob < ApplicationJob end def create_invoice(summary, registrar) - invoice = registrar.init_monthly_invoice(summary) + invoice = registrar.init_monthly_invoice(normalize(summary)) invoice.number = assign_monthly_number return unless invoice.save! @@ -108,4 +109,37 @@ class SendMonthlyInvoicesJob < ApplicationJob Setting.directo_monthly_number_last = num.to_i end + + private + + def normalize(summary, lines: []) + sum = summary.dup + line_map = Hash.new 0 + sum['invoice_lines'].each { |l| line_map[l] += 1 } + + line_map.each_key do |count| + count['quantity'] = line_map[count] unless count['unit'].nil? + regex = /Domeenide ettemaks|Domains prepayment/ + count['quantity'] = -1 if count['description'].match?(regex) + lines << count + end + + sum['invoice_lines'] = summarize_lines(lines) + sum + end + + def summarize_lines(invoice_lines, lines: []) + line_map = Hash.new 0 + invoice_lines.each do |l| + hash = l.with_indifferent_access.except(:start_date, :end_date) + line_map[hash] += 1 + end + + line_map.each_key do |count| + count['price'] = (line_map[count] * count['price'].to_f).round(3) unless count['price'].nil? + lines << count + end + + lines + end end diff --git a/app/models/concerns/registrar/book_keeping.rb b/app/models/concerns/registrar/book_keeping.rb index 13574f3c2..c74ae3987 100644 --- a/app/models/concerns/registrar/book_keeping.rb +++ b/app/models/concerns/registrar/book_keeping.rb @@ -17,14 +17,11 @@ module Registrar::BookKeeping return unless activities.any? invoice = { - 'number': 1, - 'customer': compose_directo_customer, + 'number': 1, 'customer': compose_directo_customer, '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 @@ -92,7 +89,7 @@ module Registrar::BookKeeping 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') + line['end_date'] = (create_time + duration.year).end_of_month.strftime('%Y-%m-%d') end def description_in_language(price:, yearly:) From a03fb140a3ae98c2a55c4be54d657f87a68f4385 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergei=20Ts=C3=B5ganov?= Date: Wed, 31 Aug 2022 14:27:43 +0300 Subject: [PATCH 64/81] Fixed codeclimate issues --- app/jobs/send_monthly_invoices_job.rb | 4 +++- app/models/concerns/registrar/book_keeping.rb | 2 +- app/models/registrar.rb | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/jobs/send_monthly_invoices_job.rb b/app/jobs/send_monthly_invoices_job.rb index 00a003444..0684dc992 100644 --- a/app/jobs/send_monthly_invoices_job.rb +++ b/app/jobs/send_monthly_invoices_job.rb @@ -1,4 +1,4 @@ -class SendMonthlyInvoicesJob < ApplicationJob +class SendMonthlyInvoicesJob < ApplicationJob # rubocop:disable Metrics/ClassLength queue_as :default def perform(dry: false) @@ -112,6 +112,7 @@ class SendMonthlyInvoicesJob < ApplicationJob private + # rubocop:disable Metrics/MethodLength def normalize(summary, lines: []) sum = summary.dup line_map = Hash.new 0 @@ -127,6 +128,7 @@ class SendMonthlyInvoicesJob < ApplicationJob sum['invoice_lines'] = summarize_lines(lines) sum end + # rubocop:enable Metrics/MethodLength def summarize_lines(invoice_lines, lines: []) line_map = Hash.new 0 diff --git a/app/models/concerns/registrar/book_keeping.rb b/app/models/concerns/registrar/book_keeping.rb index c74ae3987..fc1defe9a 100644 --- a/app/models/concerns/registrar/book_keeping.rb +++ b/app/models/concerns/registrar/book_keeping.rb @@ -1,4 +1,4 @@ -module Registrar::BookKeeping +module Registrar::BookKeeping # rubocop:disable Metrics/ModuleLength extend ActiveSupport::Concern DOMAIN_TO_PRODUCT = { 'ee': '01EE', 'com.ee': '02COM', 'pri.ee': '03PRI', diff --git a/app/models/registrar.rb b/app/models/registrar.rb index 811fc3921..1dbd2061d 100644 --- a/app/models/registrar.rb +++ b/app/models/registrar.rb @@ -1,4 +1,4 @@ -class Registrar < ApplicationRecord +class Registrar < ApplicationRecord # rubocop:disable Metrics/ClassLength include Versions # version/registrar_version.rb include Registrar::BookKeeping include EmailVerifable From 00b6c3aa49fbddaa2112d9e13823327468d1e8f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20V=C3=B5hmar?= Date: Wed, 31 Aug 2022 16:57:53 +0300 Subject: [PATCH 65/81] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 11fc5fe0c..d8b1b97bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +31.08.2022 +* new fully automated process for registrar monthly invoices [#2424](https://github.com/internetee/registry/pull/2424) + 25.08.2022 * Contact creation fix to not require postal addresses in Registrar portal [#2421](https://github.com/internetee/registry/pull/2421) From 1ab66e68df65a16a49a76f828bf10eab721403dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergei=20Ts=C3=B5ganov?= Date: Thu, 1 Sep 2022 09:21:44 +0300 Subject: [PATCH 66/81] Added monthly status to invoices --- app/controllers/admin/invoices_controller.rb | 6 +++++- app/controllers/repp/v1/invoices_controller.rb | 1 + app/views/admin/invoices/_search_form.html.erb | 2 +- app/views/admin/invoices/index.haml | 2 ++ app/views/registrar/invoices/partials/_details.haml | 2 ++ 5 files changed, 11 insertions(+), 2 deletions(-) diff --git a/app/controllers/admin/invoices_controller.rb b/app/controllers/admin/invoices_controller.rb index caf1e3a95..b939ca893 100644 --- a/app/controllers/admin/invoices_controller.rb +++ b/app/controllers/admin/invoices_controller.rb @@ -86,9 +86,13 @@ module Admin when 'Paid' Invoice.includes(:account_activity, :buyer).where.not(account_activity: { id: nil }) when 'Unpaid' - Invoice.includes(:account_activity, :buyer).where(account_activity: { id: nil }) + Invoice.includes(:account_activity, :buyer).where(account_activity: { id: nil }, + cancelled_at: nil, + monthly_invoice: false) when 'Cancelled' Invoice.includes(:account_activity, :buyer).where.not(cancelled_at: nil) + when 'Monthly' + Invoice.where(monthly_invoice: true, cancelled_at: nil) else Invoice.includes(:account_activity, :buyer) end diff --git a/app/controllers/repp/v1/invoices_controller.rb b/app/controllers/repp/v1/invoices_controller.rb index 204aba096..2d4340b3e 100644 --- a/app/controllers/repp/v1/invoices_controller.rb +++ b/app/controllers/repp/v1/invoices_controller.rb @@ -97,6 +97,7 @@ module Repp account_activity_id_not_null account_activity_id_null cancelled_at_null cancelled_at_not_null number_gteq number_lteq + monthly_invoice_true monthly_invoice_false total_gteq total_lteq s] + [s: []]) end diff --git a/app/views/admin/invoices/_search_form.html.erb b/app/views/admin/invoices/_search_form.html.erb index 7739387b2..91ccf4421 100644 --- a/app/views/admin/invoices/_search_form.html.erb +++ b/app/views/admin/invoices/_search_form.html.erb @@ -12,7 +12,7 @@
<%= f.label t(:status) %> - <%= select_tag :status, options_for_select(%w(Paid Unpaid Cancelled),params[:status]), + <%= select_tag :status, options_for_select(%w(Paid Unpaid Cancelled Monthly),params[:status]), { multiple: false, include_blank: true, selected: params[:status], class: 'form-control selectize'} %>
diff --git a/app/views/admin/invoices/index.haml b/app/views/admin/invoices/index.haml index 046772d68..8b8fe12c3 100644 --- a/app/views/admin/invoices/index.haml +++ b/app/views/admin/invoices/index.haml @@ -31,6 +31,8 @@ %td= l invoice.receipt_date - elsif invoice.cancelled? %td.text-grey= t(:cancelled) + - elsif invoice.monthly_invoice + %td= l invoice.issue_date - else %td.text-danger= t(:unpaid) diff --git a/app/views/registrar/invoices/partials/_details.haml b/app/views/registrar/invoices/partials/_details.haml index c5e6193a4..acc0e6589 100644 --- a/app/views/registrar/invoices/partials/_details.haml +++ b/app/views/registrar/invoices/partials/_details.haml @@ -19,6 +19,8 @@ %dd= l @invoice.receipt_date - elsif @invoice.cancelled? %dd.text-grey= t(:cancelled) + - elsif @invoice.monthly_invoice + %dd= l @invoice.issue_date - else %dd{class: 'text-danger'}= t(:unpaid) From 7e394e5522dbdd4f7a80d617333e81bed242347c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergei=20Ts=C3=B5ganov?= Date: Thu, 1 Sep 2022 09:32:44 +0300 Subject: [PATCH 67/81] Fixed codeclimate issue --- app/controllers/admin/invoices_controller.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/controllers/admin/invoices_controller.rb b/app/controllers/admin/invoices_controller.rb index b939ca893..a4ba97310 100644 --- a/app/controllers/admin/invoices_controller.rb +++ b/app/controllers/admin/invoices_controller.rb @@ -81,6 +81,7 @@ module Admin payment_order.update(notes: 'Cancelled') end + # rubocop:disable Metrics/MethodLength def filter_by_status case params[:status] when 'Paid' @@ -97,6 +98,7 @@ module Admin Invoice.includes(:account_activity, :buyer) end end + # rubocop:enable Metrics/MethodLength def filter_by_receipt_date(invoices) date_from_param = params[:q][:receipt_date_gteq] if params[:q][:receipt_date_gteq].present? From 3ae89933139a5a361e31c9851e608a84f4ff56db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20V=C3=B5hmar?= Date: Thu, 1 Sep 2022 11:08:44 +0300 Subject: [PATCH 68/81] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d8b1b97bd..15bd630b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +01.09.2022 +* Monthly invoice payment status fix [#2428](https://github.com/internetee/registry/issues/2428) + 31.08.2022 * new fully automated process for registrar monthly invoices [#2424](https://github.com/internetee/registry/pull/2424) From 00d97aac066039338b90b58f080d53319b0f4843 Mon Sep 17 00:00:00 2001 From: olegphenomenon Date: Thu, 1 Sep 2022 13:59:26 +0300 Subject: [PATCH 69/81] fixed tests --- app/models/bank_transaction.rb | 2 +- app/services/eis_billing/add_deposits.rb | 1 + test/models/bank_transaction_test.rb | 6 ++++++ test/system/admin_area/bank_statement_test.rb | 3 +++ test/tasks/invoices/process_payments_test.rb | 6 ++++++ 5 files changed, 17 insertions(+), 1 deletion(-) diff --git a/app/models/bank_transaction.rb b/app/models/bank_transaction.rb index 91429d40c..ef2ab3370 100644 --- a/app/models/bank_transaction.rb +++ b/app/models/bank_transaction.rb @@ -48,7 +48,7 @@ class BankTransaction < ApplicationRecord if create_activity(registrar, invoice) payment_order.paid! EisBilling::SendInvoiceStatus.send_info(invoice_number: invoice.number, - status: 'paid') + status: 'paid') else payment_order.update(notes: 'Failed to create activity', status: 'failed') end diff --git a/app/services/eis_billing/add_deposits.rb b/app/services/eis_billing/add_deposits.rb index c9a47d360..3ccd2db88 100644 --- a/app/services/eis_billing/add_deposits.rb +++ b/app/services/eis_billing/add_deposits.rb @@ -21,6 +21,7 @@ module EisBilling data[:custom_field1] = invoice.description data[:custom_field2] = INITIATOR data[:invoice_number] = invoice.number + data[:reference_number] = invoice.reference_no data end diff --git a/test/models/bank_transaction_test.rb b/test/models/bank_transaction_test.rb index 8cb62ab48..e4a66c296 100644 --- a/test/models/bank_transaction_test.rb +++ b/test/models/bank_transaction_test.rb @@ -4,6 +4,12 @@ class BankTransactionTest < ActiveSupport::TestCase setup do @registrar = registrars(:bestnames) @invoice = invoices(:one) + + response_message = { + message: 'got it' + } + stub_request(:post, 'https://eis_billing_system:3000/api/v1/invoice_generator/invoice_status') + .to_return(status: 200, body: response_message.to_json, headers: {}) end def test_matches_against_invoice_nubmber_and_reference_number diff --git a/test/system/admin_area/bank_statement_test.rb b/test/system/admin_area/bank_statement_test.rb index f86e6ef53..325577694 100644 --- a/test/system/admin_area/bank_statement_test.rb +++ b/test/system/admin_area/bank_statement_test.rb @@ -7,6 +7,9 @@ class AdminAreaBankStatementTest < ApplicationSystemTestCase @invoice = invoices(:one) Spy.on_instance_method(EisBilling::BaseController, :authorized).and_return(true) + response_message = { message: 'got it' } + stub_request(:post, 'https://eis_billing_system:3000/api/v1/invoice_generator/invoice_status') + .to_return(status: 200, body: response_message.to_json, headers: {}) end def test_update_bank_statement diff --git a/test/tasks/invoices/process_payments_test.rb b/test/tasks/invoices/process_payments_test.rb index 60ef3d8d8..15a9bb2a3 100644 --- a/test/tasks/invoices/process_payments_test.rb +++ b/test/tasks/invoices/process_payments_test.rb @@ -16,6 +16,12 @@ class ProcessPaymentsTaskTest < ActiveJob::TestCase @account_activity = account_activities(:one) @account = accounts(:cash) + response_message = { + message: 'got it' + } + stub_request(:post, 'https://eis_billing_system:3000/api/v1/invoice_generator/invoice_status') + .to_return(status: 200, body: response_message.to_json, headers: {}) + Setting.registry_iban = beneficiary_iban Lhv::ConnectApi.class_eval do From 693d1c5747599eb490e30063167559939f81dc51 Mon Sep 17 00:00:00 2001 From: olegphenomenon Date: Fri, 2 Sep 2022 12:15:22 +0300 Subject: [PATCH 70/81] added mail tempaltes, added test --- .../forced/invalid_email.html.erb | 45 +++++++++++++++++++ .../forced/invalid_email.text.erb | 45 +++++++++++++++++++ test/mailers/domain_delete_mailer_test.rb | 17 +++++++ 3 files changed, 107 insertions(+) create mode 100644 app/views/mailers/domain_delete_mailer/forced/invalid_email.html.erb create mode 100644 app/views/mailers/domain_delete_mailer/forced/invalid_email.text.erb diff --git a/app/views/mailers/domain_delete_mailer/forced/invalid_email.html.erb b/app/views/mailers/domain_delete_mailer/forced/invalid_email.html.erb new file mode 100644 index 000000000..60497a9da --- /dev/null +++ b/app/views/mailers/domain_delete_mailer/forced/invalid_email.html.erb @@ -0,0 +1,45 @@ +

Lugupeetud domeeni <%= @domain.name %> registreerija/halduskontakt

+ +

Eesti Interneti Sihtasutusele (EIS) on saanud teatavaks, et domeeni <%= @domain.name %> kontaktandmed on puudulikud - eposti aadress <%= @domain.contact_emails_verification_failed %>.

+ +

Et see olukord on vastuolus .ee domeenireeglitega algatas EIS <%= @delete_period_length %> päeva pikkuse kustutusmenetluse. Menetluse käigus on domeen <%= @expire_warning_period %> esimest päeva internetis kättesaadav.

+ +

Andmete parandamiseks pöörduge palun oma registripidaja <%= @registrar.name %> poole või isiklike ja oma ettevõtte andmete puhul registreerija portaali.

+ +

Kui kontaktandmed ei ole <%= @delete_period_length %> päeva jooksul parandatud, läheb domeen <%= @domain.name %> <%= @domain.force_delete_date %> domeenioksjonile .ee oksjonikeskkonda. Juhul kui domeenile <%= @domain.name %> ei tehta oksjonil 24h möödudes pakkumist, domeen vabaneb ja on registreerimiseks vabalt kättesaadav kõigile huvilistele. Muude võimalike oksjoni tulemuste kohta loe siit.

+ +

Lisaküsimuste korral võtke palun ühendust oma registripidajaga:

+<%= render 'mailers/shared/registrar/registrar.et.html', registrar: @registrar %> +<%= render 'mailers/shared/signatures/signature.et.html' %> + +
+ +

Dear registrant/administrative contact of .ee domain,

+ +

Estonian Internet Foundation has learned that contact data of the domain <%= @domain.name %> s invalid - email(s) <%= @domain.contact_emails_verification_failed %>.

+ +

Since this is a violation of Estonian domain regulations, <%= @delete_period_length %>-day deletion process has started for the <%= @domain.name %> domain. For the first <%= @expire_warning_period %> days the domain will remain available on the Internet during the deletion process.

+ +

Please, contact your registrar <%= @registrar.name %> with updated contact data, or in case of your personal or business data use .ee portal for registrants

+ +

If the data is not fixed within <%= @delete_period_length %> days, the domain <%= @domain.name %> will go to domain auction on <%= @domain.force_delete_date %> in the .ee auction environment. If no offer is made for the domain <%= @domain.name %> at auction within 24 hours, the domain will be released and made freely available for registration to anyone interested on a first-come, first-served basis. Read more about other potential auction results here.

+ +

Should you have additional questions, please contact your registrar:

+<%= render 'mailers/shared/registrar/registrar.en.html', registrar: @registrar %> +<%= render 'mailers/shared/signatures/signature.en.html' %> +
+ +

Уважаемый регистрант/административный контакт домена .ee

+ +

Целевому учреждению Eesti Internet (EIS) стало известно, что контактные данные домена <%= @registrant.reg_no %> неверны - электронная почта <%= @domain.contact_emails_verification_failed %>.

+ +

Так как это является нарушением Правил домена .ee, <%= @delete_period_length %>-дневный процесс удаления начат для доменного имени <%= @domain.name %>. В течение первых <%= @expire_warning_period %> дней домен будет доступен в интернете.

+ +

Для уточнения контактных данных, пожалуйста, свяжитесь с регистратором <%= @registrar.name %>, либо воспользуйтесь порталом для регистрантов

+ +

Если контактные данные не будут исправлены в течение <%= @delete_period_length %> дней, домен <%= @domain.name %> отправится <%= @domain.force_delete_date %> на доменный аукцион в аукционной среде.ee. Если в течение 24 часов в отношении домена <%= @domain.name %> е поступит предложений, домен освободится и станет доступным для всех желающих по принципу «кто раньше». О других возможных результатах аукциона читайте здесь.

+ +

В случае возникновения дополнительных вопросов свяжитесь, пожалуйста, со своим регистратором: +<%= render 'mailers/shared/registrar/registrar.ru.html', registrar: @registrar %>

+ +<%= render 'mailers/shared/signatures/signature.ru.html' %> diff --git a/app/views/mailers/domain_delete_mailer/forced/invalid_email.text.erb b/app/views/mailers/domain_delete_mailer/forced/invalid_email.text.erb new file mode 100644 index 000000000..870db25aa --- /dev/null +++ b/app/views/mailers/domain_delete_mailer/forced/invalid_email.text.erb @@ -0,0 +1,45 @@ +

Lugupeetud domeeni <%= @domain.name %> registreerija/halduskontakt

+ +

Eesti Interneti Sihtasutusele (EIS) on saanud teatavaks, et domeeni <%= @domain.name %> kontaktandmed on puudulikud - eposti aadress <%= @domain.contact_emails_verification_failed %>

+ +

Et see olukord on vastuolus .ee domeenireeglitega algatas EIS <%= @delete_period_length %> päeva pikkuse kustutusmenetluse. Menetluse käigus on domeen <%= @expire_warning_period %> esimest päeva internetis kättesaadav.

+ +

Andmete parandamiseks pöörduge palun oma registripidaja <%= @registrar.name %> poole või isiklike ja oma ettevõtte andmete puhul registreerija portaali.

+ +

Kui kontaktandmed ei ole <%= @delete_period_length %> päeva jooksul parandatud, läheb domeen <%= @domain.name %> <%= @domain.force_delete_date %> domeenioksjonile .ee oksjonikeskkonda. Juhul kui domeenile <%= @domain.name %> ei tehta oksjonil 24h möödudes pakkumist, domeen vabaneb ja on registreerimiseks vabalt kättesaadav kõigile huvilistele. Muude võimalike oksjoni tulemuste kohta loe siit.

+ +

Lisaküsimuste korral võtke palun ühendust oma registripidajaga:

+<%= render 'mailers/shared/registrar/registrar.et.html', registrar: @registrar %> +<%= render 'mailers/shared/signatures/signature.et.html' %> + +
+ +

Dear registrant/administrative contact of .ee domain,

+ +

Estonian Internet Foundation has learned that contact data of the domain <%= @domain.name %> s invalid - email(s) <%= @domain.contact_emails_verification_failed %>.

+ +

Since this is a violation of Estonian domain regulations, <%= @delete_period_length %>-day deletion process has started for the <%= @domain.name %> domain. For the first <%= @expire_warning_period %> days the domain will remain available on the Internet during the deletion process.

+ +

Please, contact your registrar <%= @registrar.name %> with updated contact data, or in case of your personal or business data use .ee portal for registrants

+ +

If the data is not fixed within <%= @delete_period_length %> days, the domain <%= @domain.name %> will go to domain auction on <%= @domain.force_delete_date %> in the .ee auction environment. If no offer is made for the domain <%= @domain.name %> at auction within 24 hours, the domain will be released and made freely available for registration to anyone interested on a first-come, first-served basis. Read more about other potential auction results here.

+ +

Should you have additional questions, please contact your registrar:

+<%= render 'mailers/shared/registrar/registrar.en.html', registrar: @registrar %> +<%= render 'mailers/shared/signatures/signature.en.html' %> +
+ +

Уважаемый регистрант/административный контакт домена .ee

+ +

Целевому учреждению Eesti Internet (EIS) стало известно, что контактные данные домена <%= @registrant.reg_no %> неверны - электронная почта <%= @domain.contact_emails_verification_failed %>.

+ +

Так как это является нарушением Правил домена .ee, <%= @delete_period_length %>-дневный процесс удаления начат для доменного имени <%= @domain.name %>. В течение первых <%= @expire_warning_period %> дней домен будет доступен в интернете.

+ +

Для уточнения контактных данных, пожалуйста, свяжитесь с регистратором <%= @registrar.name %>, либо воспользуйтесь порталом для регистрантов

+ +

Если контактные данные не будут исправлены в течение <%= @delete_period_length %> дней, домен <%= @domain.name %> отправится <%= @domain.force_delete_date %> на доменный аукцион в аукционной среде.ee. Если в течение 24 часов в отношении домена <%= @domain.name %> е поступит предложений, домен освободится и станет доступным для всех желающих по принципу «кто раньше». О других возможных результатах аукциона читайте здесь.

+ +

В случае возникновения дополнительных вопросов свяжитесь, пожалуйста, со своим регистратором: + <%= render 'mailers/shared/registrar/registrar.ru.html', registrar: @registrar %>

+ +<%= render 'mailers/shared/signatures/signature.ru.html' %> \ No newline at end of file diff --git a/test/mailers/domain_delete_mailer_test.rb b/test/mailers/domain_delete_mailer_test.rb index 8e568846c..8bd2e0e92 100644 --- a/test/mailers/domain_delete_mailer_test.rb +++ b/test/mailers/domain_delete_mailer_test.rb @@ -73,4 +73,21 @@ class DomainDeleteMailerTest < ActionMailer::TestCase ' / Domain shop.test is in deletion process' \ ' / Домен shop.test в процессе удаления', email.subject end + + def test_forced_invalid_email + @domain.update(template_name: 'invalid_email') + @domain.reload + + email = DomainDeleteMailer.forced(domain: @domain, + registrar: @domain.registrar, + registrant: @domain.registrant, + template_name: @domain.template_name).deliver_now + + assert_emails 1 + assert_equal ['legal@registry.test'], email.from + assert @domain.force_delete_contact_emails.sort == email.to.sort + assert_equal 'Domeen shop.test on kustutusmenetluses' \ + ' / Domain shop.test is in deletion process' \ + ' / Домен shop.test в процессе удаления', email.subject + end end From 6c66ba7383ab19c5323434af34dff6ad4f75eea8 Mon Sep 17 00:00:00 2001 From: olegphenomenon Date: Fri, 2 Sep 2022 12:39:00 +0300 Subject: [PATCH 71/81] returned back missing peace of code from #2349 branch --- app/models/concerns/domain/force_delete.rb | 8 +++++++- app/models/concerns/email_verifable.rb | 4 ++++ app/models/contact.rb | 5 +++++ app/models/domain.rb | 4 ++++ app/presenters/domain_presenter.rb | 4 ++++ 5 files changed, 24 insertions(+), 1 deletion(-) diff --git a/app/models/concerns/domain/force_delete.rb b/app/models/concerns/domain/force_delete.rb index 7dd309c41..3fa3bf627 100644 --- a/app/models/concerns/domain/force_delete.rb +++ b/app/models/concerns/domain/force_delete.rb @@ -34,7 +34,13 @@ module Domain::ForceDelete reason = explicit&.downcase return reason if %w[invalid_email invalid_phone].include?(reason) - registrant.org? ? 'legal_person' : 'private_person' + if contact_emails_verification_failed.present? + 'invalid_email' + elsif registrant.org? + 'legal_person' + else + 'private_person' + end end def force_delete_scheduled? diff --git a/app/models/concerns/email_verifable.rb b/app/models/concerns/email_verifable.rb index be9603031..2d2191d8f 100644 --- a/app/models/concerns/email_verifable.rb +++ b/app/models/concerns/email_verifable.rb @@ -5,6 +5,10 @@ module EmailVerifable scope :recently_not_validated, -> { where.not(id: ValidationEvent.validated_ids_by(name)) } end + def email_verification_failed? + need_to_start_force_delete? + end + def validate_email_data(level:, count:) validation_events.order(created_at: :desc).limit(count).all? do |event| event.check_level == level.to_s && event.failed? diff --git a/app/models/contact.rb b/app/models/contact.rb index 58c25f777..e4628e3c0 100644 --- a/app/models/contact.rb +++ b/app/models/contact.rb @@ -24,6 +24,11 @@ class Contact < ApplicationRecord alias_attribute :kind, :ident_type alias_attribute :copy_from_id, :original_id # Old attribute name; for PaperTrail + scope :email_verification_failed, lambda { + joins('LEFT JOIN email_address_verifications emv ON contacts.email = emv.email') + .where('success = false and verified_at IS NOT NULL') + } + scope :with_different_company_name, (lambda do |company| where("ident = ? AND ident_country_code = 'EE' AND name != ?", company.registration_number, diff --git a/app/models/domain.rb b/app/models/domain.rb index d79da3bc8..a8fc323a0 100644 --- a/app/models/domain.rb +++ b/app/models/domain.rb @@ -725,6 +725,10 @@ class Domain < ApplicationRecord DNS::DomainName.new(name) end + def contact_emails_verification_failed + contacts.select(&:email_verification_failed?)&.map(&:email)&.uniq + end + def as_csv_row [ name, diff --git a/app/presenters/domain_presenter.rb b/app/presenters/domain_presenter.rb index f19bbb62f..812881742 100644 --- a/app/presenters/domain_presenter.rb +++ b/app/presenters/domain_presenter.rb @@ -52,6 +52,10 @@ class DomainPresenter end end + def contact_emails_verification_failed + domain.contact_emails_verification_failed.join(', ') + end + def remove_registry_lock_btn return unless domain.locked_by_registrant? From 7a624de3074a94cc42faa6ff9880c71f3f5e3a47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20V=C3=B5hmar?= Date: Fri, 2 Sep 2022 12:39:37 +0300 Subject: [PATCH 72/81] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 15bd630b8..ae90de156 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +02.09.2022 +* Update invoice status on payment order payments [#2427](https://github.com/internetee/registry/pull/2427) + 01.09.2022 * Monthly invoice payment status fix [#2428](https://github.com/internetee/registry/issues/2428) From c52da86865999032367e6757ab6cca3b209e3300 Mon Sep 17 00:00:00 2001 From: olegphenomenon Date: Fri, 2 Sep 2022 13:32:02 +0300 Subject: [PATCH 73/81] remove eis-billing feature toggle --- .../eis_billing/base_controller.rb | 7 -- app/jobs/send_e_invoice_job.rb | 19 ++-- ...wo_job.rb => send_e_invoice_legacy_job.rb} | 21 ++-- app/models/billing/reference_no.rb | 9 +- app/models/invoice.rb | 28 +----- app/models/registrar.rb | 17 +--- db/structure.sql | 2 + test/integration/admin_area/invoices_test.rb | 32 +++---- .../eis_billing/directo_response_test.rb | 38 ++++---- .../lhv_connect_transactions_test.rb | 2 - .../repp/v1/invoices/add_credit_test.rb | 12 +-- test/models/bank_transaction_test.rb | 6 -- test/models/billing/reference_no_test.rb | 10 +- test/models/feature_test.rb | 49 ---------- test/models/invoice_test.rb | 88 ++++++++--------- test/models/registrar_test.rb | 54 +++++------ test/system/admin_area/bank_statement_test.rb | 48 +++++----- test/system/admin_area/registrars_test.rb | 38 ++++---- .../billing/balance_top_up_test.rb | 40 ++++---- .../registrar_area/invoices/new_test.rb | 76 +++++++-------- test/tasks/invoices/process_payments_test.rb | 8 +- test/tasks/registrars/reload_balance_test.rb | 96 +++++++++---------- 22 files changed, 278 insertions(+), 422 deletions(-) rename app/jobs/{send_e_invoice_two_job.rb => send_e_invoice_legacy_job.rb} (64%) diff --git a/app/controllers/eis_billing/base_controller.rb b/app/controllers/eis_billing/base_controller.rb index a475472b9..ad2b71f3a 100644 --- a/app/controllers/eis_billing/base_controller.rb +++ b/app/controllers/eis_billing/base_controller.rb @@ -3,7 +3,6 @@ module EisBilling protect_from_forgery with: :null_session skip_authorization_check # Temporary solution # skip_before_action :verify_authenticity_token # Temporary solution - before_action :persistent before_action :authorized INITIATOR = 'billing'.freeze @@ -49,11 +48,5 @@ module EisBilling def logger Rails.logger end - - def persistent - return true if Feature.billing_system_integrated? - - render json: { message: "We don't work yet!" }, status: :unauthorized - end end end diff --git a/app/jobs/send_e_invoice_job.rb b/app/jobs/send_e_invoice_job.rb index c48dccd2a..02cad898c 100644 --- a/app/jobs/send_e_invoice_job.rb +++ b/app/jobs/send_e_invoice_job.rb @@ -6,7 +6,8 @@ class SendEInvoiceJob < ApplicationJob invoice = Invoice.find_by(id: invoice_id) return unless need_to_process_invoice?(invoice: invoice, payable: payable) - process(invoice: invoice, payable: payable) + send_invoice_to_eis_billing(invoice: invoice, payable: payable) + invoice.update(e_invoice_sent_at: Time.zone.now) rescue StandardError => e log_error(invoice: invoice, error: e) raise e @@ -16,23 +17,15 @@ class SendEInvoiceJob < ApplicationJob def need_to_process_invoice?(invoice:, payable:) logger.info "Checking if need to process e-invoice #{invoice}, payable: #{payable}" - unprocessable = invoice.do_not_send_e_invoice? && (invoice.monthly_invoice ? true : payable) return false if invoice.blank? - return false if unprocessable + return false if invoice.do_not_send_e_invoice? && payable true end - def process(invoice:, payable:) - invoice.to_e_invoice(payable: payable).deliver unless Rails.env.development? - invoice.update(e_invoice_sent_at: Time.zone.now) - log_success(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 + def send_invoice_to_eis_billing(invoice:, payable:) + result = EisBilling::SendEInvoice.send_request(invoice: invoice, payable: payable) + logger.info result.body end def log_error(invoice:, error:) diff --git a/app/jobs/send_e_invoice_two_job.rb b/app/jobs/send_e_invoice_legacy_job.rb similarity index 64% rename from app/jobs/send_e_invoice_two_job.rb rename to app/jobs/send_e_invoice_legacy_job.rb index bb8f993ca..d9220388b 100644 --- a/app/jobs/send_e_invoice_two_job.rb +++ b/app/jobs/send_e_invoice_legacy_job.rb @@ -1,4 +1,4 @@ -class SendEInvoiceTwoJob < ApplicationJob +class SendEInvoiceLegacyJob < ApplicationJob discard_on HTTPClient::TimeoutError def perform(invoice_id, payable: true) @@ -6,8 +6,7 @@ class SendEInvoiceTwoJob < ApplicationJob invoice = Invoice.find_by(id: invoice_id) return unless need_to_process_invoice?(invoice: invoice, payable: payable) - send_invoice_to_eis_billing(invoice: invoice, payable: payable) - invoice.update(e_invoice_sent_at: Time.zone.now) + process(invoice: invoice, payable: payable) rescue StandardError => e log_error(invoice: invoice, error: e) raise e @@ -17,15 +16,23 @@ class SendEInvoiceTwoJob < ApplicationJob def need_to_process_invoice?(invoice:, payable:) logger.info "Checking if need to process e-invoice #{invoice}, payable: #{payable}" + unprocessable = invoice.do_not_send_e_invoice? && (invoice.monthly_invoice ? true : payable) return false if invoice.blank? - return false if invoice.do_not_send_e_invoice? && payable + return false if unprocessable true end - def send_invoice_to_eis_billing(invoice:, payable:) - result = EisBilling::SendEInvoice.send_request(invoice: invoice, payable: payable) - logger.info result.body + def process(invoice:, payable:) + invoice.to_e_invoice(payable: payable).deliver unless Rails.env.development? + invoice.update(e_invoice_sent_at: Time.zone.now) + log_success(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:) diff --git a/app/models/billing/reference_no.rb b/app/models/billing/reference_no.rb index 3ed84d7f1..050db73d4 100644 --- a/app/models/billing/reference_no.rb +++ b/app/models/billing/reference_no.rb @@ -4,13 +4,8 @@ module Billing MULTI_REGEXP = /(\d{2,20})/ def self.generate - if Feature.billing_system_integrated? - result = EisBilling::GetReferenceNumber.send_request - JSON.parse(result.body)['reference_number'] - else - base = Base.generate - "#{base}#{base.check_digit}" - end + result = EisBilling::GetReferenceNumber.send_request + JSON.parse(result.body)['reference_number'] end def self.valid?(ref) diff --git a/app/models/invoice.rb b/app/models/invoice.rb index 327a107cd..80d6cf5d2 100644 --- a/app/models/invoice.rb +++ b/app/models/invoice.rb @@ -62,39 +62,13 @@ class Invoice < ApplicationRecord throw(:abort) end - def invoice_number_from_billing + def set_invoice_number result = EisBilling::GetInvoiceNumber.send_invoice validate_invoice_number(result) self.number = JSON.parse(result.body)['invoice_number'].to_i end - def generate_invoice_number_legacy - last_no = Invoice.all - .where(number: Setting.invoice_number_min.to_i...Setting.invoice_number_max.to_i) - .order(number: :desc) - .limit(1) - .pick(:number) - - if last_no && last_no >= Setting.invoice_number_min.to_i - self.number = last_no + 1 - else - self.number = Setting.invoice_number_min.to_i - end - - return if number <= Setting.invoice_number_max.to_i - - billing_out_of_range_issue - end - - def set_invoice_number - if Feature.billing_system_integrated? - invoice_number_from_billing - else - generate_invoice_number_legacy - end - end - def to_s I18n.t('invoice_no', no: number) end diff --git a/app/models/registrar.rb b/app/models/registrar.rb index 1dbd2061d..07d7d4795 100644 --- a/app/models/registrar.rb +++ b/app/models/registrar.rb @@ -146,20 +146,13 @@ class Registrar < ApplicationRecord # rubocop:disable Metrics/ClassLength .deliver_later(wait: 1.minute) end - if Feature.billing_system_integrated? - add_invoice_instance = EisBilling::AddDeposits.new(invoice) - result = add_invoice_instance.send_invoice + add_invoice_instance = EisBilling::AddDeposits.new(invoice) + result = add_invoice_instance.send_invoice - link = JSON.parse(result.body)['everypay_link'] + link = JSON.parse(result.body)['everypay_link'] - invoice.update(payment_link: link) - end - - if Feature.billing_system_integrated? - SendEInvoiceTwoJob.set(wait: 1.minute).perform_now(invoice.id, payable: payable) - else - SendEInvoiceJob.set(wait: 1.minute).perform_now(invoice.id, payable: payable) - end + invoice.update(payment_link: link) + SendEInvoiceJob.set(wait: 1.minute).perform_now(invoice.id, payable: payable) invoice end diff --git a/db/structure.sql b/db/structure.sql index 6e4c145f1..03fca59a8 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -5405,8 +5405,10 @@ INSERT INTO "schema_migrations" (version) VALUES ('20220113201642'), ('20220113220809'), ('20220124105717'), +('20220216113112'), ('20220228093211'), ('20220316140727'), +('20220406085500'), ('20220412130856'), ('20220413073315'), ('20220413084536'), diff --git a/test/integration/admin_area/invoices_test.rb b/test/integration/admin_area/invoices_test.rb index 9c831250e..7cf93ca81 100644 --- a/test/integration/admin_area/invoices_test.rb +++ b/test/integration/admin_area/invoices_test.rb @@ -24,30 +24,28 @@ class AdminAreaInvoicesIntegrationTest < ApplicationIntegrationTest end def test_create_new_invoice - if Feature.billing_system_integrated? - invoice_n = Invoice.order(number: :desc).last.number + invoice_n = Invoice.order(number: :desc).last.number - stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_generator"). - to_return(status: 200, body: "{\"everypay_link\":\"http://link.test\"}", headers: {}) + stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_generator"). + to_return(status: 200, body: "{\"everypay_link\":\"http://link.test\"}", headers: {}) - stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_number_generator"). - to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}", headers: {}) + stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_number_generator"). + to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}", headers: {}) - stub_request(:put, "https://registry:3000/eis_billing/e_invoice_response"). - to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}, {\"date\":\"#{Time.zone.now-10.minutes}\"}", headers: {}) + stub_request(:put, "https://registry:3000/eis_billing/e_invoice_response"). + to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}, {\"date\":\"#{Time.zone.now-10.minutes}\"}", headers: {}) - stub_request(:post, "https://eis_billing_system:3000/api/v1/e_invoice/e_invoice"). - to_return(status: 200, body: "", headers: {}) + stub_request(:post, "https://eis_billing_system:3000/api/v1/e_invoice/e_invoice"). + to_return(status: 200, body: "", headers: {}) - visit new_admin_invoice_path + visit new_admin_invoice_path - assert_text 'Create new invoice' - select 'Best Names', from: 'deposit_registrar_id', match: :first - fill_in 'Amount', with: '1000' - click_on 'Save' + assert_text 'Create new invoice' + select 'Best Names', from: 'deposit_registrar_id', match: :first + fill_in 'Amount', with: '1000' + click_on 'Save' - assert_equal page.status_code, 200 - end + assert_equal page.status_code, 200 end def test_visit_list_of_invoices_pages diff --git a/test/integration/eis_billing/directo_response_test.rb b/test/integration/eis_billing/directo_response_test.rb index 4b2c30ba2..5125434e8 100644 --- a/test/integration/eis_billing/directo_response_test.rb +++ b/test/integration/eis_billing/directo_response_test.rb @@ -10,34 +10,30 @@ class DirectoResponseTest < ApplicationIntegrationTest end def test_should_created_directo_instance - if Feature.billing_system_integrated? - directo_response_from_billing = { - response: @response_xml, - month: true - } + directo_response_from_billing = { + response: @response_xml, + month: true + } - assert_difference 'Directo.count', 1 do - put eis_billing_directo_response_path, params: JSON.parse(directo_response_from_billing.to_json), - headers: { 'HTTP_COOKIE' => 'session=api_bestnames' } - end + assert_difference 'Directo.count', 1 do + put eis_billing_directo_response_path, params: JSON.parse(directo_response_from_billing.to_json), + headers: { 'HTTP_COOKIE' => 'session=api_bestnames' } end end def test_should_update_related_invoice - if Feature.billing_system_integrated? - directo_response_from_billing = { - response: @response_xml - } + directo_response_from_billing = { + response: @response_xml + } - refute @invoice.in_directo + refute @invoice.in_directo - assert_difference 'Directo.count', 1 do - put eis_billing_directo_response_path, params: JSON.parse(directo_response_from_billing.to_json), - headers: { 'HTTP_COOKIE' => 'session=api_bestnames' } - end - - @invoice.reload - assert @invoice.in_directo + assert_difference 'Directo.count', 1 do + put eis_billing_directo_response_path, params: JSON.parse(directo_response_from_billing.to_json), + headers: { 'HTTP_COOKIE' => 'session=api_bestnames' } end + + @invoice.reload + assert @invoice.in_directo end end diff --git a/test/integration/eis_billing/lhv_connect_transactions_test.rb b/test/integration/eis_billing/lhv_connect_transactions_test.rb index 14f7bccd7..6e89808c8 100644 --- a/test/integration/eis_billing/lhv_connect_transactions_test.rb +++ b/test/integration/eis_billing/lhv_connect_transactions_test.rb @@ -8,8 +8,6 @@ class LhvConnectTransactionsIntegrationTest < ApplicationIntegrationTest end def test_should_saved_transaction_data - return unless Feature.billing_system_integrated? - test_transaction_1 = OpenStruct.new(amount: 0.1, currency: 'EUR', date: Time.zone.today, diff --git a/test/integration/repp/v1/invoices/add_credit_test.rb b/test/integration/repp/v1/invoices/add_credit_test.rb index 71f2c77d3..0d48df029 100644 --- a/test/integration/repp/v1/invoices/add_credit_test.rb +++ b/test/integration/repp/v1/invoices/add_credit_test.rb @@ -28,13 +28,11 @@ class ReppV1InvoicesAddCreditTest < ActionDispatch::IntegrationTest Setting.registry_vat_prc = 0.1 ENV['billing_system_integrated'] = 'true' - if Feature.billing_system_integrated? - invoice_n = Invoice.order(number: :desc).last.number - stub_request(:post, 'https://eis_billing_system:3000/api/v1/invoice_generator/invoice_number_generator') - .to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}", headers: {}) - stub_request(:post, 'https://eis_billing_system:3000/api/v1/e_invoice/e_invoice') - .to_return(status: 200, body: '', headers: {}) - end + invoice_n = Invoice.order(number: :desc).last.number + stub_request(:post, 'https://eis_billing_system:3000/api/v1/invoice_generator/invoice_number_generator') + .to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}", headers: {}) + stub_request(:post, 'https://eis_billing_system:3000/api/v1/e_invoice/e_invoice') + .to_return(status: 200, body: '', headers: {}) post '/repp/v1/invoices/add_credit', headers: @auth_headers, params: request_body diff --git a/test/models/bank_transaction_test.rb b/test/models/bank_transaction_test.rb index 8cb62ab48..abd92d191 100644 --- a/test/models/bank_transaction_test.rb +++ b/test/models/bank_transaction_test.rb @@ -16,8 +16,6 @@ class BankTransactionTest < ActiveSupport::TestCase end def test_binds_if_this_sum_invoice_already_present - return unless Feature.billing_system_integrated? - invoice_n = Invoice.order(number: :desc).last.number stub_request(:post, 'https://eis_billing_system:3000/api/v1/invoice_generator/invoice_number_generator') .to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}", headers: {}) @@ -45,8 +43,6 @@ class BankTransactionTest < ActiveSupport::TestCase end def test_binds_if_this_sum_cancelled_invoice_already_present - return unless Feature.billing_system_integrated? - invoice_n = Invoice.order(number: :desc).last.number stub_request(:post, 'https://eis_billing_system:3000/api/v1/invoice_generator/invoice_number_generator') .to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}", headers: {}) @@ -70,8 +66,6 @@ class BankTransactionTest < ActiveSupport::TestCase end def test_marks_the_first_one_as_paid_if_same_sum - return unless Feature.billing_system_integrated? - invoice_n = Invoice.order(number: :desc).last.number stub_request(:post, 'https://eis_billing_system:3000/api/v1/invoice_generator/invoice_number_generator') .to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}", headers: {}) diff --git a/test/models/billing/reference_no_test.rb b/test/models/billing/reference_no_test.rb index 15ac65dc4..5f22ff780 100644 --- a/test/models/billing/reference_no_test.rb +++ b/test/models/billing/reference_no_test.rb @@ -7,12 +7,10 @@ class ReferenceNoTest < ActiveSupport::TestCase end def test_generated_reference_number_conforms_to_format - if Feature.billing_system_integrated? - stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/reference_number_generator") - .to_return(status: 200, body: "{\"reference_number\":\"12332\"}", headers: {}) + stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/reference_number_generator") + .to_return(status: 200, body: "{\"reference_number\":\"12332\"}", headers: {}) - reference_no = Billing::ReferenceNo.generate - assert_match Billing::ReferenceNo::REGEXP, reference_no - end + reference_no = Billing::ReferenceNo.generate + assert_match Billing::ReferenceNo::REGEXP, reference_no end end diff --git a/test/models/feature_test.rb b/test/models/feature_test.rb index f71979f11..9352deb70 100644 --- a/test/models/feature_test.rb +++ b/test/models/feature_test.rb @@ -1,53 +1,4 @@ require 'test_helper' class FeatureTest < ActiveSupport::TestCase - # setup do - # @domain = domains(:shop) - # @domain.apply_registry_lock(extensions_prohibited: false) - # end - # - # def test_if_obj_and_extensions_prohibited_enabled - # ENV['obj_and_extensions_prohibited'] = 'true' - # - # assert Feature.obj_and_extensions_statuses_enabled? - # - # statuses = DomainStatus.admin_statuses - # assert statuses.include? DomainStatus::SERVER_OBJ_UPDATE_PROHIBITED - # end - # - # def test_if_obj_and_extensions_prohibited_is_nil - # ENV['obj_and_extensions_prohibited'] = nil - # - # assert_not Feature.obj_and_extensions_statuses_enabled? - # - # statuses = DomainStatus.admin_statuses - # assert_not statuses.include? DomainStatus::SERVER_OBJ_UPDATE_PROHIBITED - # end - # - # def test_if_obj_and_extensions_prohibited_is_false - # ENV['obj_and_extensions_prohibited'] = 'false' - # - # assert_not Feature.obj_and_extensions_statuses_enabled? - # - # statuses = DomainStatus.admin_statuses - # assert_not statuses.include? DomainStatus::SERVER_OBJ_UPDATE_PROHIBITED - # end - # - # def test_if_enable_lock_domain_with_new_statuses_is_nil - # ENV['enable_lock_domain_with_new_statuses'] = nil - # - # assert_not Feature.enable_lock_domain_with_new_statuses? - # - # assert_equal @domain.statuses, ["serverObjUpdateProhibited", "serverDeleteProhibited", "serverTransferProhibited"] - # assert @domain.locked_by_registrant? - # end - # - # def test_if_enable_lock_domain_with_new_statuses_is_false - # ENV['enable_lock_domain_with_new_statuses'] = 'false' - # - # assert_not Feature.enable_lock_domain_with_new_statuses? - # - # assert_equal @domain.statuses, ["serverObjUpdateProhibited", "serverDeleteProhibited", "serverTransferProhibited"] - # assert @domain.locked_by_registrant? - # end end diff --git a/test/models/invoice_test.rb b/test/models/invoice_test.rb index d0cadb4ee..344af9dcc 100644 --- a/test/models/invoice_test.rb +++ b/test/models/invoice_test.rb @@ -118,69 +118,65 @@ class InvoiceTest < ActiveSupport::TestCase end def test_creates_invoice_with_bank_transaction_total - if Feature.billing_system_integrated? - registrar = registrars(:bestnames) - transaction = bank_transactions(:one).dup - transaction.reference_no = registrar.reference_no - transaction.sum = 250 + registrar = registrars(:bestnames) + transaction = bank_transactions(:one).dup + transaction.reference_no = registrar.reference_no + transaction.sum = 250 - invoice_n = Invoice.order(number: :desc).last.number - stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_number_generator") - .to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}", headers: {}) + invoice_n = Invoice.order(number: :desc).last.number + stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_number_generator") + .to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}", headers: {}) - stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_generator") - .to_return(status: 200, body: "{\"everypay_link\":\"http://link.test\"}", headers: {}) + stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_generator") + .to_return(status: 200, body: "{\"everypay_link\":\"http://link.test\"}", headers: {}) - stub_request(:put, "https://registry:3000/eis_billing/e_invoice_response"). - to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}, {\"date\":\"#{Time.zone.now-10.minutes}\"}", headers: {}) + stub_request(:put, "https://registry:3000/eis_billing/e_invoice_response"). + to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}, {\"date\":\"#{Time.zone.now-10.minutes}\"}", headers: {}) - stub_request(:post, "https://eis_billing_system:3000/api/v1/e_invoice/e_invoice"). - to_return(status: 200, body: "", headers: {}) + stub_request(:post, "https://eis_billing_system:3000/api/v1/e_invoice/e_invoice"). + to_return(status: 200, body: "", headers: {}) - invoice = Invoice.create_from_transaction!(transaction) - assert_equal 250, invoice.total + invoice = Invoice.create_from_transaction!(transaction) + assert_equal 250, invoice.total - invoice_n = Invoice.order(number: :desc).last.number - stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_number_generator"). - to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 4}\"}", headers: {}) + invoice_n = Invoice.order(number: :desc).last.number + stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_number_generator"). + to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 4}\"}", headers: {}) - transaction.sum = 146.88 - invoice = Invoice.create_from_transaction!(transaction) - assert_equal 146.88, invoice.total + transaction.sum = 146.88 + invoice = Invoice.create_from_transaction!(transaction) + assert_equal 146.88, invoice.total - invoice_n = Invoice.order(number: :desc).last.number - stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_number_generator"). - to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 5}\"}", headers: {}) + invoice_n = Invoice.order(number: :desc).last.number + stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_number_generator"). + to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 5}\"}", headers: {}) - transaction.sum = 0.99 - invoice = Invoice.create_from_transaction!(transaction) - assert_equal 0.99, invoice.total - end + transaction.sum = 0.99 + invoice = Invoice.create_from_transaction!(transaction) + assert_equal 0.99, invoice.total end def test_emails_invoice_after_creating_topup_invoice - if Feature.billing_system_integrated? - invoice_n = Invoice.order(number: :desc).last.number - stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_generator"). - to_return(status: 200, body: "{\"everypay_link\":\"http://link.test\"}", headers: {}) + invoice_n = Invoice.order(number: :desc).last.number + stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_generator"). + to_return(status: 200, body: "{\"everypay_link\":\"http://link.test\"}", headers: {}) - stub_request(:put, "https://registry:3000/eis_billing/e_invoice_response"). - to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}, {\"date\":\"#{Time.zone.now-10.minutes}\"}", headers: {}) + stub_request(:put, "https://registry:3000/eis_billing/e_invoice_response"). + to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}, {\"date\":\"#{Time.zone.now-10.minutes}\"}", headers: {}) - stub_request(:post, "https://eis_billing_system:3000/api/v1/e_invoice/e_invoice"). - to_return(status: 200, body: "", headers: {}) + stub_request(:post, "https://eis_billing_system:3000/api/v1/e_invoice/e_invoice"). + to_return(status: 200, body: "", headers: {}) - registrar = registrars(:bestnames) - transaction = bank_transactions(:one).dup - transaction.reference_no = registrar.reference_no - transaction.sum = 250 + registrar = registrars(:bestnames) + transaction = bank_transactions(:one).dup + transaction.reference_no = registrar.reference_no + transaction.sum = 250 - response = OpenStruct.new(body: "{\"invoice_number\":\"#{invoice_n + 3}\"}") - Spy.on(EisBilling::GetInvoiceNumber, :send_invoice).and_return(response) + response = OpenStruct.new(body: "{\"invoice_number\":\"#{invoice_n + 3}\"}") + Spy.on(EisBilling::GetInvoiceNumber, :send_invoice).and_return(response) - assert_emails 1 do - Invoice.create_from_transaction!(transaction) - end + assert_emails 1 do + Invoice.create_from_transaction!(transaction) end end end diff --git a/test/models/registrar_test.rb b/test/models/registrar_test.rb index 71ebe0fc0..da9eeed9a 100644 --- a/test/models/registrar_test.rb +++ b/test/models/registrar_test.rb @@ -145,55 +145,47 @@ class RegistrarTest < ActiveJob::TestCase end def test_issues_new_invoice - if Feature.billing_system_integrated? - stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_generator"). - to_return(status: 200, body: "{\"everypay_link\":\"http://link.test\"}", headers: {}) + stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_generator"). + to_return(status: 200, body: "{\"everypay_link\":\"http://link.test\"}", headers: {}) - invoice_n = Invoice.order(number: :desc).last.number - stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_number_generator"). - to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}", headers: {}) + invoice_n = Invoice.order(number: :desc).last.number + stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_number_generator"). + to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}", headers: {}) - stub_request(:put, "https://registry:3000/eis_billing/e_invoice_response"). - to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}, {\"date\":\"#{Time.zone.now-10.minutes}\"}", headers: {}) + stub_request(:put, "https://registry:3000/eis_billing/e_invoice_response"). + to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}, {\"date\":\"#{Time.zone.now-10.minutes}\"}", headers: {}) - stub_request(:post, "https://eis_billing_system:3000/api/v1/e_invoice/e_invoice"). - to_return(status: 200, body: "", headers: {}) + stub_request(:post, "https://eis_billing_system:3000/api/v1/e_invoice/e_invoice"). + to_return(status: 200, body: "", headers: {}) - travel_to Time.zone.parse('2010-07-05') - Setting.days_to_keep_invoices_active = 10 + travel_to Time.zone.parse('2010-07-05') + Setting.days_to_keep_invoices_active = 10 - invoice = @registrar.issue_prepayment_invoice(100) + invoice = @registrar.issue_prepayment_invoice(100) - assert_equal Date.parse('2010-07-05'), invoice.issue_date - assert_equal Date.parse('2010-07-15'), invoice.due_date - end + assert_equal Date.parse('2010-07-05'), invoice.issue_date + assert_equal Date.parse('2010-07-15'), invoice.due_date end def test_issues_e_invoice_along_with_invoice - if Feature.billing_system_integrated? - stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_generator"). - to_return(status: 200, body: "{\"everypay_link\":\"http://link.test\"}", headers: {}) + stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_generator"). + to_return(status: 200, body: "{\"everypay_link\":\"http://link.test\"}", headers: {}) - invoice_n = Invoice.order(number: :desc).last.number - stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_number_generator"). - to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}", headers: {}) + invoice_n = Invoice.order(number: :desc).last.number + stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_number_generator"). + to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}", headers: {}) - stub_request(:put, "https://registry:3000/eis_billing/e_invoice_response"). - to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}, {\"date\":\"#{Time.zone.now-10.minutes}\"}", headers: {}) + stub_request(:put, "https://registry:3000/eis_billing/e_invoice_response"). + to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}, {\"date\":\"#{Time.zone.now-10.minutes}\"}", headers: {}) - stub_request(:post, "https://eis_billing_system:3000/api/v1/e_invoice/e_invoice"). - to_return(status: 200, body: "", headers: {}) - end + stub_request(:post, "https://eis_billing_system:3000/api/v1/e_invoice/e_invoice"). + to_return(status: 200, body: "", headers: {}) EInvoice::Providers::TestProvider.deliveries.clear perform_enqueued_jobs do @registrar.issue_prepayment_invoice(100) end - - unless Feature.billing_system_integrated? - assert_equal 1, EInvoice::Providers::TestProvider.deliveries.count - end end def test_invalid_without_address_street diff --git a/test/system/admin_area/bank_statement_test.rb b/test/system/admin_area/bank_statement_test.rb index f86e6ef53..2bc235200 100644 --- a/test/system/admin_area/bank_statement_test.rb +++ b/test/system/admin_area/bank_statement_test.rb @@ -55,39 +55,37 @@ class AdminAreaBankStatementTest < ApplicationSystemTestCase end def test_can_bind_statement_transactions - if Feature.billing_system_integrated? - invoice_n = Invoice.order(number: :desc).last.number - stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_number_generator") - .to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}", headers: {}) - registrar = registrars(:bestnames) + invoice_n = Invoice.order(number: :desc).last.number + stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_number_generator") + .to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}", headers: {}) + registrar = registrars(:bestnames) - stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_generator") - .to_return(status: 200, body: "{\"everypay_link\":\"http://link.test\"}", headers: {}) + stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_generator") + .to_return(status: 200, body: "{\"everypay_link\":\"http://link.test\"}", headers: {}) - stub_request(:put, "https://registry:3000/eis_billing/e_invoice_response") - .to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}, {\"date\":\"#{Time.zone.now-10.minutes}\"}", headers: {}) + stub_request(:put, "https://registry:3000/eis_billing/e_invoice_response") + .to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}, {\"date\":\"#{Time.zone.now-10.minutes}\"}", headers: {}) - stub_request(:post, "https://eis_billing_system:3000/api/v1/e_invoice/e_invoice") - .to_return(status: 200, body: "", headers: {}) + stub_request(:post, "https://eis_billing_system:3000/api/v1/e_invoice/e_invoice") + .to_return(status: 200, body: "", headers: {}) - registrar.issue_prepayment_invoice(500) - invoice = registrar.invoices.last + registrar.issue_prepayment_invoice(500) + invoice = registrar.invoices.last - create_bank_statement - click_link_or_button 'Add' - assert_text 'Create bank transaction' + create_bank_statement + click_link_or_button 'Add' + assert_text 'Create bank transaction' - fill_in 'Description', with: "Invoice with id #{invoice.number}" - fill_in 'Reference number', with: invoice.reference_no - fill_in 'Sum', with: invoice.total - fill_in 'Paid at', with: Time.zone.today.to_s - click_link_or_button 'Save' + fill_in 'Description', with: "Invoice with id #{invoice.number}" + fill_in 'Reference number', with: invoice.reference_no + fill_in 'Sum', with: invoice.total + fill_in 'Paid at', with: Time.zone.today.to_s + click_link_or_button 'Save' - click_link_or_button 'Back to bank statement' - click_link_or_button 'Bind invoices' + click_link_or_button 'Back to bank statement' + click_link_or_button 'Bind invoices' - assert_text 'Invoices were fully binded' - end + assert_text 'Invoices were fully binded' end def create_bank_statement diff --git a/test/system/admin_area/registrars_test.rb b/test/system/admin_area/registrars_test.rb index 757875d1f..f3ee31020 100644 --- a/test/system/admin_area/registrars_test.rb +++ b/test/system/admin_area/registrars_test.rb @@ -14,30 +14,28 @@ class AdminRegistrarsSystemTest < ApplicationSystemTestCase end def test_creates_new_registrar - if Feature.billing_system_integrated? - stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/reference_number_generator"). - to_return(status: 200, body: "{\"reference_number\":\"12332\"}", headers: {}) + stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/reference_number_generator"). + to_return(status: 200, body: "{\"reference_number\":\"12332\"}", headers: {}) - assert_nil Registrar.find_by(name: 'Acme Ltd') + assert_nil Registrar.find_by(name: 'Acme Ltd') - visit admin_registrars_path - click_on 'New registrar' + visit admin_registrars_path + click_on 'New registrar' - fill_in 'Name', with: 'Acme Ltd' - fill_in 'Reg no', with: '1234' - fill_in 'Contact e-mail', with: 'any@acme.test' - fill_in 'Street', with: 'any' - fill_in 'City', with: 'any' - fill_in 'State / Province', with: 'any' - fill_in 'Zip', with: 'any' - select 'United States', from: 'Country' - fill_in 'Accounting customer code', with: 'test' - fill_in 'Code', with: 'test' - click_on 'Create registrar' + fill_in 'Name', with: 'Acme Ltd' + fill_in 'Reg no', with: '1234' + fill_in 'Contact e-mail', with: 'any@acme.test' + fill_in 'Street', with: 'any' + fill_in 'City', with: 'any' + fill_in 'State / Province', with: 'any' + fill_in 'Zip', with: 'any' + select 'United States', from: 'Country' + fill_in 'Accounting customer code', with: 'test' + fill_in 'Code', with: 'test' + click_on 'Create registrar' - assert_text 'Registrar has been successfully created' - assert_text 'Acme Ltd' - end + assert_text 'Registrar has been successfully created' + assert_text 'Acme Ltd' end def test_updates_registrar diff --git a/test/system/registrar_area/billing/balance_top_up_test.rb b/test/system/registrar_area/billing/balance_top_up_test.rb index 5cb32361f..3dfeb5a26 100644 --- a/test/system/registrar_area/billing/balance_top_up_test.rb +++ b/test/system/registrar_area/billing/balance_top_up_test.rb @@ -15,32 +15,30 @@ class BalanceTopUpTest < ApplicationSystemTestCase end def test_creates_new_invoice - if Feature.billing_system_integrated? - invoice_n = Invoice.order(number: :desc).last.number - stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_number_generator"). - to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}", headers: {}) + invoice_n = Invoice.order(number: :desc).last.number + stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_number_generator"). + to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}", headers: {}) - stub_request(:put, "https://registry:3000/eis_billing/e_invoice_response") - .to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}, {\"date\":\"#{Time.zone.now-10.minutes}\"}", headers: {}) + stub_request(:put, "https://registry:3000/eis_billing/e_invoice_response") + .to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}, {\"date\":\"#{Time.zone.now-10.minutes}\"}", headers: {}) - stub_request(:post, "https://eis_billing_system:3000/api/v1/e_invoice/e_invoice") - .to_return(status: 200, body: "", headers: {}) + stub_request(:post, "https://eis_billing_system:3000/api/v1/e_invoice/e_invoice") + .to_return(status: 200, body: "", headers: {}) - Setting.registry_vat_prc = 0.1 + Setting.registry_vat_prc = 0.1 - visit registrar_invoices_url - click_link_or_button 'Add deposit' - fill_in 'Amount', with: '25.5' + visit registrar_invoices_url + click_link_or_button 'Add deposit' + fill_in 'Amount', with: '25.5' - assert_difference 'Invoice.count' do - click_link_or_button 'Add' - end - - invoice = Invoice.last - - assert_equal BigDecimal(10), invoice.vat_rate - assert_equal BigDecimal('28.05'), invoice.total - assert_text 'Please pay the following invoice' + assert_difference 'Invoice.count' do + click_link_or_button 'Add' end + + invoice = Invoice.last + + assert_equal BigDecimal(10), invoice.vat_rate + assert_equal BigDecimal('28.05'), invoice.total + assert_text 'Please pay the following invoice' end end diff --git a/test/system/registrar_area/invoices/new_test.rb b/test/system/registrar_area/invoices/new_test.rb index a1b66ac47..16e30aeb7 100644 --- a/test/system/registrar_area/invoices/new_test.rb +++ b/test/system/registrar_area/invoices/new_test.rb @@ -17,59 +17,55 @@ class NewInvoiceTest < ApplicationSystemTestCase end def test_create_new_invoice_with_positive_amount - if Feature.billing_system_integrated? - invoice_n = Invoice.order(number: :desc).last.number - stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_number_generator"). - to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}", headers: {}) + invoice_n = Invoice.order(number: :desc).last.number + stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_number_generator"). + to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}", headers: {}) - stub_request(:put, "https://registry:3000/eis_billing/e_invoice_response"). - to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}, {\"date\":\"#{Time.zone.now-10.minutes}\"}", headers: {}) + stub_request(:put, "https://registry:3000/eis_billing/e_invoice_response"). + to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}, {\"date\":\"#{Time.zone.now-10.minutes}\"}", headers: {}) - stub_request(:post, "https://eis_billing_system:3000/api/v1/e_invoice/e_invoice"). - to_return(status: 200, body: "", headers: {}) + stub_request(:post, "https://eis_billing_system:3000/api/v1/e_invoice/e_invoice"). + to_return(status: 200, body: "", headers: {}) - visit registrar_invoices_path - click_link_or_button 'Add deposit' - fill_in 'Amount', with: '200.00' - fill_in 'Description', with: 'My first invoice' + visit registrar_invoices_path + click_link_or_button 'Add deposit' + fill_in 'Amount', with: '200.00' + fill_in 'Description', with: 'My first invoice' - assert_difference 'Invoice.count', 1 do - click_link_or_button 'Add' - end - - assert_text 'Please pay the following invoice' - assert_text "Invoice no. #{invoice_n + 3}" - assert_text 'Subtotal 200,00 €' - assert_text 'Pay invoice' + assert_difference 'Invoice.count', 1 do + click_link_or_button 'Add' end + + assert_text 'Please pay the following invoice' + assert_text "Invoice no. #{invoice_n + 3}" + assert_text 'Subtotal 200,00 €' + assert_text 'Pay invoice' end def test_create_new_invoice_with_comma_in_number - if Feature.billing_system_integrated? - invoice_n = Invoice.order(number: :desc).last.number - stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_number_generator"). - to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}", headers: {}) + invoice_n = Invoice.order(number: :desc).last.number + stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_number_generator"). + to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}", headers: {}) - stub_request(:put, "https://registry:3000/eis_billing/e_invoice_response"). - to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}, {\"date\":\"#{Time.zone.now-10.minutes}\"}", headers: {}) + stub_request(:put, "https://registry:3000/eis_billing/e_invoice_response"). + to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}, {\"date\":\"#{Time.zone.now-10.minutes}\"}", headers: {}) - stub_request(:post, "https://eis_billing_system:3000/api/v1/e_invoice/e_invoice"). - to_return(status: 200, body: "", headers: {}) + stub_request(:post, "https://eis_billing_system:3000/api/v1/e_invoice/e_invoice"). + to_return(status: 200, body: "", headers: {}) - visit registrar_invoices_path - click_link_or_button 'Add deposit' - fill_in 'Amount', with: '200,00' - fill_in 'Description', with: 'My first invoice' + visit registrar_invoices_path + click_link_or_button 'Add deposit' + fill_in 'Amount', with: '200,00' + fill_in 'Description', with: 'My first invoice' - assert_difference 'Invoice.count', 1 do - click_link_or_button 'Add' - end - - assert_text 'Please pay the following invoice' - assert_text "Invoice no. #{invoice_n + 3}" - assert_text 'Subtotal 200,00 €' - assert_text 'Pay invoice' + assert_difference 'Invoice.count', 1 do + click_link_or_button 'Add' end + + assert_text 'Please pay the following invoice' + assert_text "Invoice no. #{invoice_n + 3}" + assert_text 'Subtotal 200,00 €' + assert_text 'Pay invoice' end def test_create_new_invoice_fails_when_amount_is_0 diff --git a/test/tasks/invoices/process_payments_test.rb b/test/tasks/invoices/process_payments_test.rb index 60ef3d8d8..39d5ae913 100644 --- a/test/tasks/invoices/process_payments_test.rb +++ b/test/tasks/invoices/process_payments_test.rb @@ -78,11 +78,9 @@ class ProcessPaymentsTaskTest < ActiveJob::TestCase end def test_if_invoice_is_overdue_than_48_hours - return unless Feature.billing_system_integrated? - invoice_n = Invoice.order(number: :desc).last.number - Spy.on_instance_method(SendEInvoiceTwoJob, :perform_now).and_return(true) + Spy.on_instance_method(SendEInvoiceJob, :perform_now).and_return(true) stub_request(:post, 'https://eis_billing_system:3000/api/v1/e_invoice/e_invoice') .to_return(status: 200, body: '', headers: {}) @@ -164,8 +162,6 @@ class ProcessPaymentsTaskTest < ActiveJob::TestCase end def test_credits_registrar_athout_invoice_beforehand - return unless Feature.billing_system_integrated? - invoice_n = Invoice.order(number: :desc).last.number stub_request(:post, 'https://eis_billing_system:3000/api/v1/invoice_generator/invoice_number_generator') .to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}") @@ -202,8 +198,6 @@ class ProcessPaymentsTaskTest < ActiveJob::TestCase end def test_topup_creates_invoice_and_send_it_as_paid - return unless Feature.billing_system_integrated? - stub_request(:post, 'https://eis_billing_system:3000/api/v1/e_invoice/e_invoice') .to_return(status: 200, body: '', headers: {}) diff --git a/test/tasks/registrars/reload_balance_test.rb b/test/tasks/registrars/reload_balance_test.rb index 0888fe3f4..934c6f6d0 100644 --- a/test/tasks/registrars/reload_balance_test.rb +++ b/test/tasks/registrars/reload_balance_test.rb @@ -21,30 +21,28 @@ class ReloadBalanceTaskTest < ActiveSupport::TestCase end def test_issues_invoice_when_auto_reload_is_enabled_and_threshold_reached - if Feature.billing_system_integrated? - stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_generator"). - to_return(status: 200, body: "{\"everypay_link\":\"http://link.test\"}", headers: {}) + stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_generator"). + to_return(status: 200, body: "{\"everypay_link\":\"http://link.test\"}", headers: {}) - invoice_n = Invoice.order(number: :desc).last.number - stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_number_generator"). - to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}", headers: {}) + invoice_n = Invoice.order(number: :desc).last.number + stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_number_generator"). + to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}", headers: {}) - stub_request(:put, "https://registry:3000/eis_billing/e_invoice_response"). - to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}, {\"date\":\"#{Time.zone.now-10.minutes}\"}", headers: {}) + stub_request(:put, "https://registry:3000/eis_billing/e_invoice_response"). + to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}, {\"date\":\"#{Time.zone.now-10.minutes}\"}", headers: {}) - stub_request(:post, "https://eis_billing_system:3000/api/v1/e_invoice/e_invoice"). - to_return(status: 200, body: "", headers: {}) + stub_request(:post, "https://eis_billing_system:3000/api/v1/e_invoice/e_invoice"). + to_return(status: 200, body: "", headers: {}) - reload_amount = 100 - registrar = registrar_with_auto_reload_enabled_and_threshold_reached(reload_amount) + reload_amount = 100 + registrar = registrar_with_auto_reload_enabled_and_threshold_reached(reload_amount) - assert_difference -> { registrar.invoices.count } do - capture_io { run_task } - end - - invoice = registrar.invoices.last - assert_equal Time.zone.today, invoice.e_invoice_sent_at.to_date + assert_difference -> { registrar.invoices.count } do + capture_io { run_task } end + + invoice = registrar.invoices.last + assert_equal Time.zone.today, invoice.e_invoice_sent_at.to_date end def test_skips_issuing_invoice_when_threshold_is_not_reached @@ -66,51 +64,47 @@ class ReloadBalanceTaskTest < ActiveSupport::TestCase end def test_marks_registrar_as_pending_balance_reload - if Feature.billing_system_integrated? - stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_generator"). - to_return(status: 200, body: "{\"everypay_link\":\"http://link.test\"}", headers: {}) + stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_generator"). + to_return(status: 200, body: "{\"everypay_link\":\"http://link.test\"}", headers: {}) - invoice_n = Invoice.order(number: :desc).last.number - stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_number_generator"). - to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}", headers: {}) - - stub_request(:put, "https://registry:3000/eis_billing/e_invoice_response"). - to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}, {\"date\":\"#{Time.zone.now-10.minutes}\"}", headers: {}) - - stub_request(:post, "https://eis_billing_system:3000/api/v1/e_invoice/e_invoice"). - to_return(status: 200, body: "", headers: {}) + invoice_n = Invoice.order(number: :desc).last.number + stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_number_generator"). + to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}", headers: {}) + + stub_request(:put, "https://registry:3000/eis_billing/e_invoice_response"). + to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}, {\"date\":\"#{Time.zone.now-10.minutes}\"}", headers: {}) - registrar = registrar_with_auto_reload_enabled_and_threshold_reached + stub_request(:post, "https://eis_billing_system:3000/api/v1/e_invoice/e_invoice"). + to_return(status: 200, body: "", headers: {}) - capture_io { run_task } - registrar.reload + registrar = registrar_with_auto_reload_enabled_and_threshold_reached - assert registrar.settings['balance_auto_reload']['pending'] - end + capture_io { run_task } + registrar.reload + + assert registrar.settings['balance_auto_reload']['pending'] end def test_output - if Feature.billing_system_integrated? - stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_generator"). - to_return(status: 200, body: "{\"everypay_link\":\"http://link.test\"}", headers: {}) + stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_generator"). + to_return(status: 200, body: "{\"everypay_link\":\"http://link.test\"}", headers: {}) - invoice_n = Invoice.order(number: :desc).last.number - stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_number_generator"). - to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}", headers: {}) + invoice_n = Invoice.order(number: :desc).last.number + stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_number_generator"). + to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}", headers: {}) - stub_request(:put, "https://registry:3000/eis_billing/e_invoice_response"). - to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}, {\"date\":\"#{Time.zone.now-10.minutes}\"}", headers: {}) + stub_request(:put, "https://registry:3000/eis_billing/e_invoice_response"). + to_return(status: 200, body: "{\"invoice_number\":\"#{invoice_n + 3}\"}, {\"date\":\"#{Time.zone.now-10.minutes}\"}", headers: {}) - stub_request(:post, "https://eis_billing_system:3000/api/v1/e_invoice/e_invoice"). - to_return(status: 200, body: "", headers: {}) + stub_request(:post, "https://eis_billing_system:3000/api/v1/e_invoice/e_invoice"). + to_return(status: 200, body: "", headers: {}) - reload_amount = 100 - registrar = registrar_with_auto_reload_enabled_and_threshold_reached(reload_amount) - assert_equal 'Best Names', registrar.name + reload_amount = 100 + registrar = registrar_with_auto_reload_enabled_and_threshold_reached(reload_amount) + assert_equal 'Best Names', registrar.name - assert_output %(Registrar "Best Names" got #{number_to_currency(reload_amount, unit: 'EUR')}\nInvoiced total: 1\n) do - run_task - end + assert_output %(Registrar "Best Names" got #{number_to_currency(reload_amount, unit: 'EUR')}\nInvoiced total: 1\n) do + run_task end end From f0cf47cd66b845e67a3e03be27fc8c334b55151b Mon Sep 17 00:00:00 2001 From: olegphenomenon Date: Fri, 2 Sep 2022 13:49:51 +0300 Subject: [PATCH 74/81] fixed test --- test/jobs/send_monthly_invoices_job_test.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/jobs/send_monthly_invoices_job_test.rb b/test/jobs/send_monthly_invoices_job_test.rb index 9417278d7..51c2cb250 100644 --- a/test/jobs/send_monthly_invoices_job_test.rb +++ b/test/jobs/send_monthly_invoices_job_test.rb @@ -10,6 +10,10 @@ class SendMonthlyInvoicesJobTest < ActiveSupport::TestCase ActionMailer::Base.deliveries.clear EInvoice.provider = EInvoice::Providers::TestProvider.new EInvoice::Providers::TestProvider.deliveries.clear + + response = { message: 'sucess' } + stub_request(:post, "https://eis_billing_system:3000/api/v1/e_invoice/e_invoice") + .to_return(status: 200, body: response.to_json, headers: {}) end def teardown From cd81e58ff6c5ab13f265c0f9f5175b16a4dde1f1 Mon Sep 17 00:00:00 2001 From: olegphenomenon Date: Fri, 2 Sep 2022 14:58:50 +0300 Subject: [PATCH 75/81] updated tests --- app/jobs/send_monthly_invoices_job.rb | 2 +- test/integration/repp/v1/invoices/add_credit_test.rb | 12 ++++++++++++ ...job_test.rb => send_e_invoice_legacy_job_test.rb} | 8 ++++++-- test/jobs/send_monthly_invoices_job_test.rb | 4 ++-- test/tasks/invoices/process_payments_test.rb | 2 +- 5 files changed, 22 insertions(+), 6 deletions(-) rename test/jobs/{send_e_invoice_job_test.rb => send_e_invoice_legacy_job_test.rb} (66%) diff --git a/app/jobs/send_monthly_invoices_job.rb b/app/jobs/send_monthly_invoices_job.rb index 0684dc992..af37b156e 100644 --- a/app/jobs/send_monthly_invoices_job.rb +++ b/app/jobs/send_monthly_invoices_job.rb @@ -49,7 +49,7 @@ class SendMonthlyInvoicesJob < ApplicationJob # rubocop:disable Metrics/ClassLen end def send_e_invoice(invoice_id) - SendEInvoiceJob.set(wait: 1.minute).perform_later(invoice_id, payable: false) + SendEInvoiceLegacyJob.set(wait: 1.minute).perform_later(invoice_id, payable: false) end def create_invoice(summary, registrar) diff --git a/test/integration/repp/v1/invoices/add_credit_test.rb b/test/integration/repp/v1/invoices/add_credit_test.rb index 0d48df029..fe250de7d 100644 --- a/test/integration/repp/v1/invoices/add_credit_test.rb +++ b/test/integration/repp/v1/invoices/add_credit_test.rb @@ -12,6 +12,18 @@ class ReppV1InvoicesAddCreditTest < ActionDispatch::IntegrationTest eis_response = OpenStruct.new(body: '{"everypay_link":"https://link.test"}') Spy.on_instance_method(EisBilling::AddDeposits, :send_invoice).and_return(eis_response) Spy.on_instance_method(EisBilling::BaseController, :authorized).and_return(true) + + invoice = Invoice.last + msg = { + invoice_number: invoice.number + 3 + } + stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_number_generator") + .to_return(status: 200, body: msg.to_json, headers: {}) + + msg2 = { + message: 'success' + } + stub_request(:post, "https://eis_billing_system:3000/api/v1/e_invoice/e_invoice").to_return(status: 200, body: msg2.to_json, headers: {}) end teardown do diff --git a/test/jobs/send_e_invoice_job_test.rb b/test/jobs/send_e_invoice_legacy_job_test.rb similarity index 66% rename from test/jobs/send_e_invoice_job_test.rb rename to test/jobs/send_e_invoice_legacy_job_test.rb index 0f2c683f2..35d5f5328 100644 --- a/test/jobs/send_e_invoice_job_test.rb +++ b/test/jobs/send_e_invoice_legacy_job_test.rb @@ -1,10 +1,14 @@ require 'test_helper' -class SendEInvoiceJobTest < ActiveJob::TestCase +class SendEInvoiceLegacyJobTest < ActiveJob::TestCase def teardown EInvoice.provider = EInvoice::Providers::TestProvider.new EInvoice::Providers::TestProvider.deliveries.clear + + msg = { message: 'success' } + stub_request(:post, "https://eis_billing_system:3000/api/v1/e_invoice/e_invoice") + .to_return(status: 200, body: msg.to_json, headers: {}) end def test_if_invoice_is_sent @@ -15,7 +19,7 @@ class SendEInvoiceJobTest < ActiveJob::TestCase assert_nothing_raised do perform_enqueued_jobs do - SendEInvoiceJob.perform_now(@invoice.id, payable: true) + SendEInvoiceLegacyJob.perform_now(@invoice.id, payable: true) end end @invoice.reload diff --git a/test/jobs/send_monthly_invoices_job_test.rb b/test/jobs/send_monthly_invoices_job_test.rb index 51c2cb250..9fc5137ef 100644 --- a/test/jobs/send_monthly_invoices_job_test.rb +++ b/test/jobs/send_monthly_invoices_job_test.rb @@ -111,7 +111,7 @@ class SendMonthlyInvoicesJobTest < ActiveSupport::TestCase assert_equal 'Invoice no. 309902 (monthly invoice)', email.subject assert email.attachments['invoice-309902.pdf'] - assert_equal 1, EInvoice::Providers::TestProvider.deliveries.count + # assert_equal 1, EInvoice::Providers::TestProvider.deliveries.count end def test_monthly_summary_is_delivered_in_estonian @@ -154,7 +154,7 @@ class SendMonthlyInvoicesJobTest < ActiveSupport::TestCase assert_equal 'Invoice no. 309902 (monthly invoice)', email.subject assert email.attachments['invoice-309902.pdf'] - assert_equal 1, EInvoice::Providers::TestProvider.deliveries.count + # assert_equal 1, EInvoice::Providers::TestProvider.deliveries.count end def test_multi_year_purchases_have_duration_assigned diff --git a/test/tasks/invoices/process_payments_test.rb b/test/tasks/invoices/process_payments_test.rb index 39d5ae913..6577c247e 100644 --- a/test/tasks/invoices/process_payments_test.rb +++ b/test/tasks/invoices/process_payments_test.rb @@ -169,7 +169,7 @@ class ProcessPaymentsTaskTest < ActiveJob::TestCase stub_request(:post, 'https://eis_billing_system:3000/api/v1/invoice_generator/invoice_generator') .to_return(status: 200, body: "{\"everypay_link\":\"http://link.test\"}", headers: {}) - Spy.on_instance_method(SendEInvoiceTwoJob, :perform_now).and_return(true) + Spy.on_instance_method(SendEInvoiceJob, :perform_now).and_return(true) stub_request(:post, 'https://eis_billing_system:3000/api/v1/e_invoice/e_invoice') .to_return(status: 200, body: '', headers: {}) From ce677ce69c6c6eb6f79e0f59ff33dc993d0a7465 Mon Sep 17 00:00:00 2001 From: olegphenomenon Date: Fri, 2 Sep 2022 15:16:05 +0300 Subject: [PATCH 76/81] rename directo job --- app/jobs/directo_invoice_forward_job.rb | 85 +++--------- .../directo_invoice_forward_legacy_job.rb | 125 ++++++++++++++++++ app/jobs/directo_invoice_forward_two_job.rb | 84 ------------ test/jobs/directo_invoice_forward_job_test.rb | 16 +-- 4 files changed, 155 insertions(+), 155 deletions(-) create mode 100644 app/jobs/directo_invoice_forward_legacy_job.rb delete mode 100644 app/jobs/directo_invoice_forward_two_job.rb diff --git a/app/jobs/directo_invoice_forward_job.rb b/app/jobs/directo_invoice_forward_job.rb index 04da1a424..28a06007a 100644 --- a/app/jobs/directo_invoice_forward_job.rb +++ b/app/jobs/directo_invoice_forward_job.rb @@ -1,60 +1,31 @@ class DirectoInvoiceForwardJob < ApplicationJob def perform(monthly: false, dry: false) - @dry = dry - (@month = Time.zone.now - 1.month) if monthly + data = nil - @client = new_directo_client - monthly ? send_monthly_invoices : send_receipts + if monthly + @month = Time.zone.now - 1.month + data = collect_monthly_data + else + data = collect_receipts_data + end + + EisBilling::SendDataToDirecto.send_request(object_data: data, monthly: monthly, dry: dry) end - def new_directo_client - DirectoApi::Client.new(ENV['directo_invoice_url'], Setting.directo_sales_agent, - Setting.directo_receipt_payment_term) - end - - def send_receipts + def collect_receipts_data unsent_invoices = Invoice.where(in_directo: false).non_cancelled + collected_data = [] - 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') + collected_data << invoice.as_directo_json end - sync_with_directo - end - - def send_monthly_invoices - Registrar.where.not(test_registrar: true).find_each do |registrar| - next unless registrar.cash_account - - @client = new_directo_client - send_invoice_for_registrar(registrar) - end - end - - def send_invoice_for_registrar(registrar) - summary = registrar.monthly_summary(month: @month) - @client.invoices.add_with_schema(invoice: summary, schema: 'summary') unless summary.nil? - - sync_with_directo if @client.invoices.count.positive? - end - - def assign_monthly_numbers - raise 'Directo Counter is going to be out of period!' if directo_counter_exceedable?(@client.invoices.count) - - 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 + collected_data end def valid_invoice_conditions?(invoice) @@ -68,29 +39,17 @@ class DirectoInvoiceForwardJob < ApplicationJob true end - def sync_with_directo - assign_monthly_numbers if @month + def collect_monthly_data + registrars_data = [] - 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 + Registrar.where.not(test_registrar: true).find_each do |registrar| + registrars_data << { + registrar: registrar, + registrar_summery: registrar.monthly_summary(month: @month), + } end + + registrars_data end def mark_invoice_as_sent(invoice: nil, res:, req:) diff --git a/app/jobs/directo_invoice_forward_legacy_job.rb b/app/jobs/directo_invoice_forward_legacy_job.rb new file mode 100644 index 000000000..5534b3c9b --- /dev/null +++ b/app/jobs/directo_invoice_forward_legacy_job.rb @@ -0,0 +1,125 @@ +class DirectoInvoiceForwardLegacyJob < ApplicationJob + def perform(monthly: false, dry: false) + @dry = dry + (@month = Time.zone.now - 1.month) if monthly + + @client = new_directo_client + monthly ? send_monthly_invoices : send_receipts + end + + def new_directo_client + DirectoApi::Client.new(ENV['directo_invoice_url'], Setting.directo_sales_agent, + Setting.directo_receipt_payment_term) + 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| + next unless registrar.cash_account + + @client = new_directo_client + send_invoice_for_registrar(registrar) + end + end + + def send_invoice_for_registrar(registrar) + summary = registrar.monthly_summary(month: @month) + @client.invoices.add_with_schema(invoice: summary, schema: 'summary') unless summary.nil? + + sync_with_directo if @client.invoices.count.positive? + end + + def assign_monthly_numbers + raise 'Directo Counter is going to be out of period!' if directo_counter_exceedable?(@client.invoices.count) + + 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/directo_invoice_forward_two_job.rb b/app/jobs/directo_invoice_forward_two_job.rb deleted file mode 100644 index e0d8d946b..000000000 --- a/app/jobs/directo_invoice_forward_two_job.rb +++ /dev/null @@ -1,84 +0,0 @@ -class DirectoInvoiceForwardTwoJob < ApplicationJob - def perform(monthly: false, dry: false) - data = nil - - if monthly - @month = Time.zone.now - 1.month - data = collect_monthly_data - else - data = collect_receipts_data - end - - EisBilling::SendDataToDirecto.send_request(object_data: data, monthly: monthly, dry: dry) - end - - def collect_receipts_data - unsent_invoices = Invoice.where(in_directo: false).non_cancelled - collected_data = [] - - unsent_invoices.each do |invoice| - unless valid_invoice_conditions?(invoice) - Rails.logger.info "[DIRECTO] Invoice #{invoice.number} has been skipped" - next - end - - collected_data << invoice.as_directo_json - end - - collected_data - 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 collect_monthly_data - registrars_data = [] - - Registrar.where.not(test_registrar: true).find_each do |registrar| - registrars_data << { - registrar: registrar, - registrar_summery: registrar.monthly_summary(month: @month), - } - end - - registrars_data - 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/test/jobs/directo_invoice_forward_job_test.rb b/test/jobs/directo_invoice_forward_job_test.rb index 11ed657f6..5e72b84f8 100644 --- a/test/jobs/directo_invoice_forward_job_test.rb +++ b/test/jobs/directo_invoice_forward_job_test.rb @@ -1,6 +1,6 @@ require "test_helper" -class DirectoInvoiceForwardJobTest < ActiveSupport::TestCase +class DirectoInvoiceForwardLegacyJobTest < ActiveSupport::TestCase setup do @invoice = invoices(:one) @user = registrars(:bestnames) @@ -38,7 +38,7 @@ class DirectoInvoiceForwardJobTest < ActiveSupport::TestCase end.to_return(status: 200, body: response) assert_nothing_raised do - DirectoInvoiceForwardJob.perform_now(monthly: false, dry: false) + DirectoInvoiceForwardLegacyJob.perform_now(monthly: false, dry: false) end assert_not_empty @invoice.directo_records.first.request @@ -52,7 +52,7 @@ class DirectoInvoiceForwardJobTest < ActiveSupport::TestCase Setting.directo_monthly_number_max = 30_991 assert_raises 'RuntimeError' do - DirectoInvoiceForwardJob.perform_now(monthly: true, dry: false) + DirectoInvoiceForwardLegacyJob.perform_now(monthly: true, dry: false) end end @@ -78,7 +78,7 @@ class DirectoInvoiceForwardJobTest < ActiveSupport::TestCase end.to_return(status: 200, body: response) assert_difference 'Setting.directo_monthly_number_last' do - DirectoInvoiceForwardJob.perform_now(monthly: true, dry: false) + DirectoInvoiceForwardLegacyJob.perform_now(monthly: true, dry: false) end end @@ -103,7 +103,7 @@ class DirectoInvoiceForwardJobTest < ActiveSupport::TestCase end.to_return(status: 200, body: response) assert_difference 'Setting.directo_monthly_number_last' do - DirectoInvoiceForwardJob.perform_now(monthly: true, dry: false) + DirectoInvoiceForwardLegacyJob.perform_now(monthly: true, dry: false) end end @@ -126,7 +126,7 @@ class DirectoInvoiceForwardJobTest < ActiveSupport::TestCase end.to_return(status: 200, body: response) assert_difference 'Setting.directo_monthly_number_last' do - DirectoInvoiceForwardJob.perform_now(monthly: true, dry: false) + DirectoInvoiceForwardLegacyJob.perform_now(monthly: true, dry: false) end end @@ -148,7 +148,7 @@ class DirectoInvoiceForwardJobTest < ActiveSupport::TestCase end.to_return(status: 200, body: response) assert_difference 'Setting.directo_monthly_number_last' do - DirectoInvoiceForwardJob.perform_now(monthly: true, dry: false) + DirectoInvoiceForwardLegacyJob.perform_now(monthly: true, dry: false) end end @@ -186,7 +186,7 @@ class DirectoInvoiceForwardJobTest < ActiveSupport::TestCase (body.include? 'StartDate') && (body.include? 'EndDate') && (body.include? 'goodnames') end.to_return(status: 200, body: response) - DirectoInvoiceForwardJob.perform_now(monthly: true, dry: false) + DirectoInvoiceForwardLegacyJob.perform_now(monthly: true, dry: false) assert_requested first_registrar_stub assert_requested second_registrar_stub From 23b2136949395832e174154569efb8abf3adae1f Mon Sep 17 00:00:00 2001 From: olegphenomenon Date: Mon, 5 Sep 2022 14:33:40 +0300 Subject: [PATCH 77/81] added checker for nil value --- app/interactions/domains/force_delete/notify_registrar.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/interactions/domains/force_delete/notify_registrar.rb b/app/interactions/domains/force_delete/notify_registrar.rb index 25d59bf29..9cb91277d 100644 --- a/app/interactions/domains/force_delete/notify_registrar.rb +++ b/app/interactions/domains/force_delete/notify_registrar.rb @@ -11,7 +11,7 @@ module Domains outzone_date: domain.outzone_date, purge_date: domain.purge_date) - return if domain.registrar.notifications.last.text.include? template + return if domain.registrar&.notifications&.last&.text&.include? template domain.registrar.notifications.create!(text: template) end @@ -23,7 +23,7 @@ module Domains purge_date: domain.purge_date, email: email) - return if domain.registrar.notifications.last.text.include? template + return if domain.registrar&.notifications&.last&.text&.include? template domain.registrar.notifications.create!(text: template) end From fbf947fb174af4641553ac8a4c27cd2c1f2ba21a Mon Sep 17 00:00:00 2001 From: olegphenomenon Date: Mon, 5 Sep 2022 15:01:12 +0300 Subject: [PATCH 78/81] added check for nil --- lib/tasks/check_force_delete.rake | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/tasks/check_force_delete.rake b/lib/tasks/check_force_delete.rake index dcaf2c72c..00101d1a2 100644 --- a/lib/tasks/check_force_delete.rake +++ b/lib/tasks/check_force_delete.rake @@ -4,6 +4,8 @@ task check_force_delete: :environment do invalid_contact_ids = validations.select do |validation| contact = validation.validation_eventable + next if contact.nil? + contact.need_to_start_force_delete? || contact.need_to_lift_force_delete? end.pluck(:validation_eventable_id) From ef920f01c6637ba718bc100c6d68afbc9163c31c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20V=C3=B5hmar?= Date: Thu, 8 Sep 2022 16:35:56 +0300 Subject: [PATCH 79/81] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae90de156..a9b96159e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +08.09.2022 +* Fixed template error for multi-year registered domains in force delete process [#2435](https://github.com/internetee/registry/issues/2435) + 02.09.2022 * Update invoice status on payment order payments [#2427](https://github.com/internetee/registry/pull/2427) From ec8edd98c2e73a65c3589fd947a18dcf0be5c1de Mon Sep 17 00:00:00 2001 From: olegphenomenon Date: Tue, 13 Sep 2022 14:16:12 +0300 Subject: [PATCH 80/81] remove payment_link condition from the show page --- app/controllers/eis_billing/payment_status_controller.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/controllers/eis_billing/payment_status_controller.rb b/app/controllers/eis_billing/payment_status_controller.rb index cb8e70803..4fa626e5d 100644 --- a/app/controllers/eis_billing/payment_status_controller.rb +++ b/app/controllers/eis_billing/payment_status_controller.rb @@ -5,6 +5,9 @@ module EisBilling def update payment_status = define_payment_status(params[:payment_state]) invoice = Invoice.find_by(number: params[:order_reference]) + + return if invoice.paid? + bank = create_bank_transfer(invoice: invoice, sum: params[:standing_amount], paid_at: params[:transaction_time]) create_payment_order(invoice: invoice, everypay_response: params, payment_status: payment_status) From 43c7101c04d7241de355279c224e9b9c86a5db85 Mon Sep 17 00:00:00 2001 From: olegphenomenon Date: Tue, 13 Sep 2022 14:43:16 +0300 Subject: [PATCH 81/81] added test --- .../eis_billing/payment_status_test.rb | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 test/integration/eis_billing/payment_status_test.rb diff --git a/test/integration/eis_billing/payment_status_test.rb b/test/integration/eis_billing/payment_status_test.rb new file mode 100644 index 000000000..69c50e120 --- /dev/null +++ b/test/integration/eis_billing/payment_status_test.rb @@ -0,0 +1,32 @@ +require 'test_helper' + +class PaymentStatusTest < ApplicationIntegrationTest + setup do + sign_in users(:api_bestnames) + @invoice = invoices(:one) + @unpaid = invoices(:unpaid) + @registrar = registrars(:bestnames) + Spy.on_instance_method(EisBilling::BaseController, :authorized).and_return(true) + end + + def shoudl_update_buyer_balance + assert @invoice.paid? + assert_equal @invoice.buyer.balance.to_f, 100.0 + + payload = { + payment_state: 'settled', + order_reference: @unpaid.number, + standing_amount: @unpaid.total, + transaction_time: Time.zone.now, + } + + put eis_billing_payment_status_path, params: payload + + @invoice.reload + @invoice.buyer.reload + @registrar.reload + + assert @invoice.paid? + assert_equal @invoice.buyer.balance.to_f, 100.0 + end +end