mirror of
https://github.com/internetee/registry.git
synced 2025-07-29 14:06:21 +02:00
Merge branch 'master' into 2342-notification-text-lenght
This commit is contained in:
commit
fcbbd4b2d3
147 changed files with 3897 additions and 1272 deletions
2
.github/workflows/ruby.yml
vendored
2
.github/workflows/ruby.yml
vendored
|
@ -6,7 +6,7 @@ jobs:
|
|||
test:
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:12
|
||||
image: postgres:14
|
||||
ports: ["5432:5432"]
|
||||
env:
|
||||
POSTGRES_PASSWORD: password
|
||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -5,6 +5,7 @@
|
|||
/coverage/
|
||||
/.bundle
|
||||
/vendor/bundle
|
||||
/vendor/gems
|
||||
/config/database.yml
|
||||
/config/application.yml
|
||||
/config/environments/development.rb
|
||||
|
|
36
CHANGELOG.md
36
CHANGELOG.md
|
@ -1,4 +1,40 @@
|
|||
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)
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
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
|
||||
|
|
|
@ -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
|
||||
|
|
10
Gemfile
10
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.6.0'
|
||||
gem 'bootsnap', '>= 1.1.0', require: false
|
||||
gem 'iso8601', '0.13.0' # for dates and times
|
||||
gem 'mimemagic', '0.4.3'
|
||||
|
@ -17,11 +17,11 @@ gem 'figaro', '~> 1.2'
|
|||
|
||||
# model related
|
||||
gem 'paper_trail', '~> 12.1'
|
||||
gem 'pg', '1.4.0'
|
||||
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
|
||||
gem 'validates_email_format_of', '1.6.3' # 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
|
||||
|
@ -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
|
||||
|
|
40
Gemfile.lock
40
Gemfile.lock
|
@ -18,10 +18,10 @@ GIT
|
|||
|
||||
GIT
|
||||
remote: https://github.com/internetee/e_invoice.git
|
||||
revision: 312cac173935f434e449d1714f3497bfee9f8995
|
||||
revision: 9f850465697a2448a31ebddb83c1be5a5a9be3d2
|
||||
branch: master
|
||||
specs:
|
||||
e_invoice (0.1.0)
|
||||
e_invoice (0.1.3)
|
||||
builder (~> 3.2)
|
||||
nokogiri
|
||||
savon
|
||||
|
@ -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.6.0)
|
||||
actionpack (>= 4.1)
|
||||
activesupport (>= 4.1)
|
||||
attr_required (1.0.1)
|
||||
autoprefixer-rails (10.2.4.0)
|
||||
execjs
|
||||
|
@ -258,12 +259,12 @@ 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)
|
||||
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)
|
||||
|
@ -290,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)
|
||||
|
@ -337,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)
|
||||
|
@ -352,7 +353,7 @@ GEM
|
|||
activerecord (>= 5.2)
|
||||
request_store (~> 1.1)
|
||||
pdfkit (0.8.5)
|
||||
pg (1.4.0)
|
||||
pg (1.4.3)
|
||||
pg_query (2.1.2)
|
||||
google-protobuf (>= 3.17.1)
|
||||
pghero (2.8.1)
|
||||
|
@ -369,7 +370,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
|
||||
|
@ -398,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)
|
||||
|
@ -476,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)
|
||||
|
@ -495,10 +496,10 @@ 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)
|
||||
validates_email_format_of (1.7.2)
|
||||
i18n
|
||||
warden (1.2.9)
|
||||
rack (>= 2.0.9)
|
||||
|
@ -510,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)
|
||||
|
@ -534,7 +535,7 @@ PLATFORMS
|
|||
DEPENDENCIES
|
||||
active_interaction (~> 4.0)
|
||||
airbrake
|
||||
apipie-rails (~> 0.5.19)
|
||||
apipie-rails (~> 0.6.0)
|
||||
aws-sdk-sesv2 (~> 1.19)
|
||||
bootsnap (>= 1.1.0)
|
||||
bootstrap-sass (~> 3.4)
|
||||
|
@ -571,12 +572,11 @@ DEPENDENCIES
|
|||
newrelic-infinite_tracing
|
||||
newrelic_rpm
|
||||
nokogiri (~> 1.13.0)
|
||||
omniauth
|
||||
omniauth-rails_csrf_protection
|
||||
omniauth-tara!
|
||||
paper_trail (~> 12.1)
|
||||
pdfkit
|
||||
pg (= 1.4.0)
|
||||
pg (= 1.4.3)
|
||||
pg_query (>= 0.9.0)
|
||||
pghero
|
||||
pry (= 0.14.1)
|
||||
|
@ -596,11 +596,11 @@ DEPENDENCIES
|
|||
spy
|
||||
truemail (~> 2.4)
|
||||
uglifier
|
||||
validates_email_format_of (= 1.6.3)
|
||||
validates_email_format_of (= 1.7.2)
|
||||
webdrivers
|
||||
webmock
|
||||
whenever (= 1.0.0)
|
||||
wkhtmltopdf-binary (~> 0.12.5.1)
|
||||
|
||||
BUNDLED WITH
|
||||
2.3.9
|
||||
2.3.21
|
||||
|
|
|
@ -81,18 +81,24 @@ module Admin
|
|||
payment_order.update(notes: 'Cancelled')
|
||||
end
|
||||
|
||||
# rubocop:disable Metrics/MethodLength
|
||||
def filter_by_status
|
||||
case params[:status]
|
||||
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
|
||||
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?
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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?
|
||||
|
||||
|
@ -32,17 +34,21 @@ 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 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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -1,32 +1,164 @@
|
|||
module Repp
|
||||
module V1
|
||||
class AccountsController < BaseController
|
||||
api :GET, '/repp/v1/accounts/balance'
|
||||
class AccountsController < BaseController # rubocop:disable Metrics/ClassLength
|
||||
load_and_authorize_resource
|
||||
|
||||
api :get, '/repp/v1/accounts'
|
||||
desc 'Get all activities'
|
||||
def index
|
||||
records = current_user.registrar.cash_account.activities
|
||||
|
||||
q = records.ransack(PartialSearchFormatter.format(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/accounts/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/accounts'
|
||||
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/accounts/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/accounts/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 :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
|
||||
resp = { balance: current_user.registrar.cash_account.balance,
|
||||
currency: current_user.registrar.cash_account.currency }
|
||||
resp[:transactions] = activities if params[:detailed] == 'true'
|
||||
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
|
||||
|
||||
def activities
|
||||
private
|
||||
|
||||
def account_params
|
||||
params.require(:account).permit(:billing_email, :iban, :new_user_id)
|
||||
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 = []
|
||||
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 }
|
||||
users.each do |u|
|
||||
arr << { id: u.id, username: u.username,
|
||||
role: u.roles.first, registrar_name: u.registrar.name }
|
||||
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]
|
||||
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
|
||||
|
||||
activities
|
||||
arr
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
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 :validate_client_certs
|
||||
before_action :check_ip_restriction
|
||||
attr_reader :current_user
|
||||
|
||||
before_action :set_paper_trail_whodunnit
|
||||
|
||||
private
|
||||
|
@ -22,6 +23,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 +70,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 +89,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']
|
||||
|
@ -95,12 +105,14 @@ module Repp
|
|||
def authenticate_user
|
||||
username, password = Base64.urlsafe_decode64(basic_token).split(':')
|
||||
@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
|
||||
|
||||
|
@ -114,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
|
||||
|
@ -123,6 +135,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
|
||||
|
||||
|
@ -132,9 +145,28 @@ module Repp
|
|||
render(json: @response, status: :unauthorized)
|
||||
end
|
||||
|
||||
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'])
|
||||
|
||||
@response = { code: 2202, message: 'Invalid certificate' }
|
||||
render(json: @response, status: :unauthorized)
|
||||
end
|
||||
|
||||
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[:address_processing] = Contact.address_processing?
|
||||
data[:abilities] = Ability.new(current_user).permissions
|
||||
data
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,30 +3,67 @@ 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
|
||||
|
||||
q = records.ransack(PartialSearchFormatter.format(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'
|
||||
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
|
||||
|
@ -35,7 +72,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 +87,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 +110,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(&: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 +185,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(:id, *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(: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]])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -8,14 +8,14 @@ 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)
|
||||
|
@ -26,10 +26,11 @@ module Repp
|
|||
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
|
||||
|
@ -50,13 +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
|
||||
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
|
||||
|
||||
|
@ -75,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
|
||||
|
|
|
@ -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])
|
||||
|
|
|
@ -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
|
||||
records = current_user.registrar.domains
|
||||
domains = records.limit(limit).offset(offset)
|
||||
authorize! :info, Epp::Domain
|
||||
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)
|
||||
|
||||
render_success(data: { domains: serialized_domains(domains),
|
||||
total_number_of_records: records.count })
|
||||
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),
|
||||
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,14 +88,14 @@ 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
|
||||
|
@ -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,47 @@ module Repp
|
|||
end
|
||||
|
||||
def index_params
|
||||
params.permit(:limit, :offset, :details)
|
||||
params.permit(:limit, :offset, :details, :simple, :q,
|
||||
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
|
||||
|
||||
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]],
|
||||
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]
|
||||
|
||||
modify_contact_params(dup_params)
|
||||
end
|
||||
|
||||
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
|
||||
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: [])
|
||||
admin_contacts: [], tech_contacts: [],
|
||||
dnskeys_attributes: [%i[flags alg protocol public_key]],
|
||||
delete: [:verified])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
128
app/controllers/repp/v1/invoices_controller.rb
Normal file
128
app/controllers/repp/v1/invoices_controller.rb
Normal file
|
@ -0,0 +1,128 @@
|
|||
require 'serializers/repp/invoice'
|
||||
module Repp
|
||||
module V1
|
||||
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
|
||||
records = current_user.registrar.invoices
|
||||
q = records.ransack(PartialSearchFormatter.format(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
|
||||
# rubocop:enable Metrics/MethodLength
|
||||
|
||||
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'
|
||||
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]
|
||||
if recipient.blank?
|
||||
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)
|
||||
render_success(data: { invoice: serializer.to_json
|
||||
.merge!(recipient: recipient) })
|
||||
end
|
||||
|
||||
api :put, '/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_non_epp_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_null
|
||||
cancelled_at_not_null number_gteq number_lteq
|
||||
monthly_invoice_true monthly_invoice_false
|
||||
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.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 }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
35
app/controllers/repp/v1/registrar/auth_controller.rb
Normal file
35
app/controllers/repp/v1/registrar/auth_controller.rb
Normal file
|
@ -0,0 +1,35 @@
|
|||
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
|
||||
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
|
||||
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)
|
||||
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 })
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def auth_params
|
||||
params.require(:auth).permit(:uid, :new_user_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -19,15 +19,17 @@ 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))
|
||||
rescue ActiveRecord::RecordInvalid => e
|
||||
handle_errors(e.record)
|
||||
|
@ -36,34 +38,33 @@ 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],
|
||||
affected_domains: affected_domains,
|
||||
skipped_domains: errored_domains,
|
||||
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,
|
||||
:domains, 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
|
||||
|
|
|
@ -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
|
||||
|
|
114
app/controllers/repp/v1/registrar/summary_controller.rb
Normal file
114
app/controllers/repp/v1/registrar/summary_controller.rb
Normal file
|
@ -0,0 +1,114 @@
|
|||
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
|
||||
end
|
||||
render_success(data: serialize_data(registrar: registrar,
|
||||
notification: notification,
|
||||
notifications_count: user_notifications&.count,
|
||||
object: notification_object(notification)))
|
||||
end
|
||||
|
||||
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&.attached_obj_type || notification&.attached_obj_id
|
||||
|
||||
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
|
||||
|
||||
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[: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
|
||||
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
|
||||
|
||||
try("serialized_#{obj_type.underscore}", object)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
@ -47,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
|
||||
|
@ -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
|
||||
|
|
15
app/interactions/actions/invoice_cancel.rb
Normal file
15
app/interactions/actions/invoice_cancel.rb
Normal file
|
@ -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
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
10
app/jobs/delete_monthly_invoices_job.rb
Normal file
10
app/jobs/delete_monthly_invoices_job.rb
Normal file
|
@ -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
|
|
@ -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:)
|
||||
|
|
125
app/jobs/directo_invoice_forward_legacy_job.rb
Normal file
125
app/jobs/directo_invoice_forward_legacy_job.rb
Normal file
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
@ -22,16 +23,9 @@ class SendEInvoiceJob < ApplicationJob
|
|||
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:)
|
||||
|
|
|
@ -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:)
|
147
app/jobs/send_monthly_invoices_job.rb
Normal file
147
app/jobs/send_monthly_invoices_job.rb
Normal file
|
@ -0,0 +1,147 @@
|
|||
class SendMonthlyInvoicesJob < ApplicationJob # rubocop:disable Metrics/ClassLength
|
||||
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
|
||||
|
||||
# rubocop:disable Metrics/MethodLength
|
||||
def send_monthly_invoices
|
||||
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
|
||||
|
||||
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}")
|
||||
@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
|
||||
|
||||
# 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)
|
||||
SendEInvoiceLegacyJob.set(wait: 1.minute).perform_later(invoice_id, payable: false)
|
||||
end
|
||||
|
||||
def create_invoice(summary, registrar)
|
||||
invoice = registrar.init_monthly_invoice(normalize(summary))
|
||||
invoice.number = assign_monthly_number
|
||||
return unless invoice.save!
|
||||
|
||||
update_monthly_invoice_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_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_to_directo(res: res, req: req, invoice: inv)
|
||||
end
|
||||
end
|
||||
|
||||
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
|
||||
invoice.update(in_directo: true)
|
||||
|
||||
directo_record.save!
|
||||
end
|
||||
|
||||
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
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# rubocop:disable Metrics/MethodLength
|
||||
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
|
||||
# rubocop:enable Metrics/MethodLength
|
||||
|
||||
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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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).find_each do |admin_contact|
|
||||
if admin_contact.domain.bulk_update_prohibited?
|
||||
skipped_domains << admin_contact.domain.name
|
||||
next
|
||||
|
|
|
@ -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,9 @@ 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)
|
||||
.includes(:registrar)
|
||||
end
|
||||
|
||||
def linked_with?(another_api_user)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
@ -47,9 +47,12 @@ 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
|
||||
invoice
|
||||
end
|
||||
|
||||
def bind_invoice(invoice_no, manual: false)
|
||||
|
@ -62,8 +65,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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
class BulkAction < Action; end
|
|
@ -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?
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -15,6 +15,8 @@ module Invoice::Payable
|
|||
end
|
||||
|
||||
def receipt_date
|
||||
return unless paid?
|
||||
|
||||
account_activity.created_at.to_date
|
||||
end
|
||||
|
||||
|
|
|
@ -1,22 +1,27 @@
|
|||
module Registrar::BookKeeping
|
||||
module Registrar::BookKeeping # rubocop:disable Metrics/ModuleLength
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
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?
|
||||
|
||||
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
|
||||
|
||||
|
@ -55,20 +60,25 @@ 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 = {
|
||||
'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
|
||||
|
||||
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)
|
||||
|
@ -79,15 +89,16 @@ 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:)
|
||||
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
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
@ -80,41 +85,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 +134,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 +151,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 +360,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 +388,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 +414,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 +431,6 @@ class Contact < ApplicationRecord
|
|||
end
|
||||
|
||||
domains.each { |d| d.roles = domain_c[d.id].uniq }
|
||||
|
||||
domains
|
||||
end
|
||||
|
||||
|
@ -438,18 +446,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 +477,7 @@ class Contact < ApplicationRecord
|
|||
PENDING_CREATE,
|
||||
PENDING_TRANSFER,
|
||||
PENDING_UPDATE,
|
||||
PENDING_DELETE
|
||||
PENDING_DELETE,
|
||||
]).present?
|
||||
end
|
||||
|
||||
|
@ -590,4 +608,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
|
||||
|
|
1
app/models/contact_update_action.rb
Normal file
1
app/models/contact_update_action.rb
Normal file
|
@ -0,0 +1 @@
|
|||
class ContactUpdateAction < Action; end
|
|
@ -33,6 +33,7 @@ class Deposit
|
|||
|
||||
def issue_prepayment_invoice
|
||||
return unless valid?
|
||||
|
||||
registrar.issue_prepayment_invoice(amount, description)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
@ -726,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,
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
@ -59,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
|
||||
|
@ -118,7 +95,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 +108,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
|
||||
|
@ -180,6 +161,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
|
||||
|
|
|
@ -41,22 +41,16 @@ 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
|
||||
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
|
||||
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
|
||||
|
@ -72,9 +66,33 @@ class Invoice
|
|||
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_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
|
||||
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
|
||||
end
|
||||
end
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
class Notification < ApplicationRecord
|
||||
include Versions # version/notification_version.rb
|
||||
include EppErrors
|
||||
|
||||
belongs_to :registrar
|
||||
belongs_to :action, optional: true
|
||||
|
|
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,14 +130,14 @@ 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',
|
||||
unit: 'piece',
|
||||
quantity: 1,
|
||||
price: amount
|
||||
}
|
||||
price: amount,
|
||||
},
|
||||
]
|
||||
)
|
||||
|
||||
|
@ -107,23 +146,17 @@ class Registrar < ApplicationRecord
|
|||
.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
|
||||
# rubocop:enable Metrics/MethodLength
|
||||
|
||||
def cash_account
|
||||
accounts.find_by(account_type: Account::CASH)
|
||||
|
@ -188,9 +221,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?
|
||||
|
@ -233,13 +266,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
|
||||
|
@ -269,4 +298,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
|
||||
|
|
|
@ -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).find_each do |tech_contact|
|
||||
if irreplaceable?(tech_contact)
|
||||
skipped_domains << tech_contact.domain.name
|
||||
next
|
||||
|
|
|
@ -13,9 +13,9 @@ 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)
|
||||
find_by(identity_code: identity_code, active: true)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -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?
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
<div class="col-md-3">
|
||||
<div class="form-group">
|
||||
<%= 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'} %>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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')
|
||||
|
@ -24,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'
|
||||
|
|
|
@ -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',
|
||||
|
|
277
app/views/invoice/monthly_pdf.haml
Normal file
277
app/views/invoice/monthly_pdf.haml
Normal file
|
@ -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
|
|
@ -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}
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
<p>Lugupeetud domeeni <%= @domain.name %> registreerija/halduskontakt</p>
|
||||
|
||||
<p>Eesti Interneti Sihtasutusele (EIS) on saanud teatavaks, et domeeni <%= @domain.name %> kontaktandmed on puudulikud - eposti aadress <%= @domain.contact_emails_verification_failed %>.</p>
|
||||
|
||||
<p>Et see olukord on vastuolus .ee <a href='https://www.internet.ee/domains/ee-domain-regulation'>domeenireeglitega</a> algatas EIS <%= @delete_period_length %> päeva pikkuse kustutusmenetluse. Menetluse käigus on domeen <%= @expire_warning_period %> esimest päeva internetis kättesaadav.</p>
|
||||
|
||||
<p>Andmete parandamiseks pöörduge palun oma registripidaja <%= @registrar.name %> poole või isiklike ja oma ettevõtte andmete puhul <a href="https://registrant.internet.ee/">registreerija portaali</a>.</p>
|
||||
|
||||
<p>Kui kontaktandmed ei ole <%= @delete_period_length %> päeva jooksul parandatud, läheb domeen <%= @domain.name %> <%= @domain.force_delete_date %> domeenioksjonile <a href="https://auction.internet.ee">.ee oksjonikeskkonda</a>. 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 <a href="https://www.internet.ee/domeenid/domeenide-oksjonikeskkonna-kasutajatingimused#3-oksjonikeskkonna-enampakkumisel-osalemise-tingimused">siit</a>.</p>
|
||||
|
||||
<p>Lisaküsimuste korral võtke palun ühendust oma registripidajaga:</p>
|
||||
<%= render 'mailers/shared/registrar/registrar.et.html', registrar: @registrar %>
|
||||
<%= render 'mailers/shared/signatures/signature.et.html' %>
|
||||
|
||||
<hr>
|
||||
|
||||
<p>Dear registrant/administrative contact of .ee domain,</p>
|
||||
|
||||
<p>Estonian Internet Foundation has learned that contact data of the domain <%= @domain.name %> s invalid - email(s) <%= @domain.contact_emails_verification_failed %>.</p>
|
||||
|
||||
<p>Since this is a violation of <a href='https://www.internet.ee/domains/ee-domain-regulation'>Estonian domain regulations</a>, <%= @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.</p>
|
||||
|
||||
<p>Please, contact your registrar <%= @registrar.name %> with updated contact data, or in case of your personal or business data use <a href="https://registrant.internet.ee/">.ee portal for registrants</a></p>
|
||||
|
||||
<p>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 <a href="https://auction.internet.ee">.ee auction environment</a>. 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 <a href="https://www.internet.ee/domains/auction-environment-user-agreement#3-terms-and-conditions-for-participation-in-the-auction-of-the-auction-environment">here</a>.</p>
|
||||
|
||||
<p>Should you have additional questions, please contact your registrar:</p>
|
||||
<%= render 'mailers/shared/registrar/registrar.en.html', registrar: @registrar %>
|
||||
<%= render 'mailers/shared/signatures/signature.en.html' %>
|
||||
<hr>
|
||||
|
||||
<p>Уважаемый регистрант/административный контакт домена .ee</p>
|
||||
|
||||
<p>Целевому учреждению Eesti Internet (EIS) стало известно, что контактные данные домена <%= @registrant.reg_no %> неверны - электронная почта <%= @domain.contact_emails_verification_failed %>.</p>
|
||||
|
||||
<p>Так как это является нарушением <a href='https://www.internet.ee/domains/ee-domain-regulation'>Правил домена .ee</a>, <%= @delete_period_length %>-дневный процесс удаления начат для доменного имени <%= @domain.name %>. В течение первых <%= @expire_warning_period %> дней домен будет доступен в интернете.</p>
|
||||
|
||||
<p>Для уточнения контактных данных, пожалуйста, свяжитесь с регистратором <%= @registrar.name %>, либо воспользуйтесь <a href="https://registrant.internet.ee/">порталом для регистрантов</a></p>
|
||||
|
||||
<p>Если контактные данные не будут исправлены в течение <%= @delete_period_length %> дней, домен <%= @domain.name %> отправится <%= @domain.force_delete_date %> на доменный аукцион в <a href="https://auction.internet.ee">аукционной среде.ee</a>. Если в течение 24 часов в отношении домена <%= @domain.name %> е поступит предложений, домен освободится и станет доступным для всех желающих по принципу «кто раньше». О других возможных результатах аукциона читайте <a href="https://www.internet.ee/domains/auction-environment-user-agreement#3-terms-and-conditions-for-participation-in-the-auction-of-the-auction-environment">здесь</a>.</p>
|
||||
|
||||
<p>В случае возникновения дополнительных вопросов свяжитесь, пожалуйста, со своим регистратором:
|
||||
<%= render 'mailers/shared/registrar/registrar.ru.html', registrar: @registrar %></p>
|
||||
|
||||
<%= render 'mailers/shared/signatures/signature.ru.html' %>
|
|
@ -0,0 +1,45 @@
|
|||
<p>Lugupeetud domeeni <%= @domain.name %> registreerija/halduskontakt</p>
|
||||
|
||||
<p>Eesti Interneti Sihtasutusele (EIS) on saanud teatavaks, et domeeni <%= @domain.name %> kontaktandmed on puudulikud - eposti aadress <%= @domain.contact_emails_verification_failed %></p>
|
||||
|
||||
<p>Et see olukord on vastuolus .ee <a href='https://www.internet.ee/domains/ee-domain-regulation'>domeenireeglitega</a> algatas EIS <%= @delete_period_length %> päeva pikkuse kustutusmenetluse. Menetluse käigus on domeen <%= @expire_warning_period %> esimest päeva internetis kättesaadav.</p>
|
||||
|
||||
<p>Andmete parandamiseks pöörduge palun oma registripidaja <%= @registrar.name %> poole või isiklike ja oma ettevõtte andmete puhul <a href="https://registrant.internet.ee/">registreerija portaali</a>.</p>
|
||||
|
||||
<p>Kui kontaktandmed ei ole <%= @delete_period_length %> päeva jooksul parandatud, läheb domeen <%= @domain.name %> <%= @domain.force_delete_date %> domeenioksjonile <a href="https://auction.internet.ee">.ee oksjonikeskkonda</a>. 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 <a href="https://www.internet.ee/domeenid/domeenide-oksjonikeskkonna-kasutajatingimused#3-oksjonikeskkonna-enampakkumisel-osalemise-tingimused">siit</a>.</p>
|
||||
|
||||
<p>Lisaküsimuste korral võtke palun ühendust oma registripidajaga:</p>
|
||||
<%= render 'mailers/shared/registrar/registrar.et.html', registrar: @registrar %>
|
||||
<%= render 'mailers/shared/signatures/signature.et.html' %>
|
||||
|
||||
<hr>
|
||||
|
||||
<p>Dear registrant/administrative contact of .ee domain,</p>
|
||||
|
||||
<p>Estonian Internet Foundation has learned that contact data of the domain <%= @domain.name %> s invalid - email(s) <%= @domain.contact_emails_verification_failed %>.</p>
|
||||
|
||||
<p>Since this is a violation of <a href='https://www.internet.ee/domains/ee-domain-regulation'>Estonian domain regulations</a>, <%= @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.</p>
|
||||
|
||||
<p>Please, contact your registrar <%= @registrar.name %> with updated contact data, or in case of your personal or business data use <a href="https://registrant.internet.ee/">.ee portal for registrants</a></p>
|
||||
|
||||
<p>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 <a href="https://auction.internet.ee">.ee auction environment</a>. 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 <a href="https://www.internet.ee/domains/auction-environment-user-agreement#3-terms-and-conditions-for-participation-in-the-auction-of-the-auction-environment">here</a>.</p>
|
||||
|
||||
<p>Should you have additional questions, please contact your registrar:</p>
|
||||
<%= render 'mailers/shared/registrar/registrar.en.html', registrar: @registrar %>
|
||||
<%= render 'mailers/shared/signatures/signature.en.html' %>
|
||||
<hr>
|
||||
|
||||
<p>Уважаемый регистрант/административный контакт домена .ee</p>
|
||||
|
||||
<p>Целевому учреждению Eesti Internet (EIS) стало известно, что контактные данные домена <%= @registrant.reg_no %> неверны - электронная почта <%= @domain.contact_emails_verification_failed %>.</p>
|
||||
|
||||
<p>Так как это является нарушением <a href='https://www.internet.ee/domains/ee-domain-regulation'>Правил домена .ee</a>, <%= @delete_period_length %>-дневный процесс удаления начат для доменного имени <%= @domain.name %>. В течение первых <%= @expire_warning_period %> дней домен будет доступен в интернете.</p>
|
||||
|
||||
<p>Для уточнения контактных данных, пожалуйста, свяжитесь с регистратором <%= @registrar.name %>, либо воспользуйтесь <a href="https://registrant.internet.ee/">порталом для регистрантов</a></p>
|
||||
|
||||
<p>Если контактные данные не будут исправлены в течение <%= @delete_period_length %> дней, домен <%= @domain.name %> отправится <%= @domain.force_delete_date %> на доменный аукцион в <a href="https://auction.internet.ee">аукционной среде.ee</a>. Если в течение 24 часов в отношении домена <%= @domain.name %> е поступит предложений, домен освободится и станет доступным для всех желающих по принципу «кто раньше». О других возможных результатах аукциона читайте <a href="https://www.internet.ee/domains/auction-environment-user-agreement#3-terms-and-conditions-for-participation-in-the-auction-of-the-auction-environment">здесь</a>.</p>
|
||||
|
||||
<p>В случае возникновения дополнительных вопросов свяжитесь, пожалуйста, со своим регистратором:
|
||||
<%= render 'mailers/shared/registrar/registrar.ru.html', registrar: @registrar %></p>
|
||||
|
||||
<%= render 'mailers/shared/signatures/signature.ru.html' %>
|
|
@ -44,7 +44,7 @@
|
|||
|
||||
<div class="col-md-3">
|
||||
<div class="form-group">
|
||||
<%= 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 @@
|
|||
|
||||
<div class="col-md-3">
|
||||
<div class="form-group">
|
||||
<%= 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) %>
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
25
config/initializers/arel.rb
Normal file
25
config/initializers/arel.rb
Normal file
|
@ -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
|
|
@ -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
|
||||
|
|
7
config/initializers/ransack.rb
Normal file
7
config/initializers/ransack.rb
Normal file
|
@ -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
|
|
@ -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:
|
||||
|
@ -479,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'
|
||||
|
|
|
@ -9,3 +9,5 @@ et:
|
|||
invoice:
|
||||
title: 'Arve'
|
||||
already_paid: " (juba makstud)"
|
||||
monthly_invoice: " (kuuaruanne)"
|
||||
monthly_invoice_description: 'Kuuaruanne'
|
||||
|
|
|
@ -71,12 +71,30 @@ 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 :accounts, only: %i[index update] do
|
||||
collection do
|
||||
get '/', to: 'accounts#index'
|
||||
get 'balance'
|
||||
get 'details'
|
||||
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
|
||||
get ':id/download', to: 'invoices#download'
|
||||
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]
|
||||
|
@ -98,6 +116,12 @@ 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'
|
||||
end
|
||||
end
|
||||
end
|
||||
resources :domains, constraints: { id: /.*/ } do
|
||||
resources :nameservers, only: %i[index create destroy], constraints: { id: /.*/ }, controller: 'domains/nameservers'
|
||||
|
@ -146,9 +170,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 +183,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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
class RemoveStatusesBeforeForceDeleteFromDomains < ActiveRecord::Migration[6.1]
|
||||
def change
|
||||
remove_column :domains, :statuses_before_force_delete if column_exists? :domains, :statuses_before_force_delete
|
||||
end
|
||||
end
|
|
@ -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
|
|
@ -956,7 +956,6 @@ CREATE TABLE public.domains (
|
|||
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,
|
||||
|
@ -1197,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))
|
||||
);
|
||||
|
||||
|
@ -5407,10 +5408,12 @@ INSERT INTO "schema_migrations" (version) VALUES
|
|||
('20220216113112'),
|
||||
('20220228093211'),
|
||||
('20220316140727'),
|
||||
('20220406085500'),
|
||||
('20220412130856'),
|
||||
('20220413073315'),
|
||||
('20220413084536'),
|
||||
('20220413084748'),
|
||||
('20220504090512'),
|
||||
('20220406085500'),
|
||||
('20220701113409');
|
||||
('20220524130709'),
|
||||
('20220701113409'),
|
||||
('20220818075833');
|
||||
|
|
|
@ -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":[
|
||||
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
@ -96,10 +96,7 @@ Content-Type: application/json
|
|||
],
|
||||
"status_notes":{
|
||||
|
||||
},
|
||||
"statuses_backup":[
|
||||
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -235,10 +232,7 @@ Content-Type: application/json
|
|||
],
|
||||
"status_notes":{
|
||||
|
||||
},
|
||||
"statuses_backup":[
|
||||
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
@ -3,21 +3,35 @@ 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
|
||||
|
||||
# rubocop:disable Metrics/MethodLength
|
||||
# rubocop:disable Metrics/AbcSize
|
||||
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 = { 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
|
||||
json[:domains_count] = obj.qualified_domain_ids(@domain_params[:domain_filter]).size
|
||||
end
|
||||
json
|
||||
end
|
||||
# rubocop:enable Metrics/MethodLength
|
||||
# rubocop:enable Metrics/AbcSize
|
||||
|
||||
def registrar
|
||||
contact.registrar.as_json(only: %i[name website])
|
||||
end
|
||||
|
||||
def ident
|
||||
{
|
||||
|
@ -31,6 +45,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
|
||||
|
|
|
@ -3,42 +3,80 @@ 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/MethodLength
|
||||
# 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
|
||||
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
|
||||
end
|
||||
# rubocop:enable Metrics/MethodLength
|
||||
# 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
|
||||
{
|
||||
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 = {
|
||||
name: domain.name,
|
||||
expire_time: domain.expire_time,
|
||||
registrant: registrant,
|
||||
statuses: statuses,
|
||||
}
|
||||
json[:transfer_code] = domain.auth_info if @sponsored
|
||||
json
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
90
lib/serializers/repp/invoice.rb
Normal file
90
lib/serializers/repp/invoice.rb
Normal file
|
@ -0,0 +1,90 @@
|
|||
module Serializers
|
||||
module Repp
|
||||
class Invoice
|
||||
attr_reader :invoice
|
||||
|
||||
def initialize(invoice, simplify: false)
|
||||
@invoice = invoice
|
||||
@simplify = simplify
|
||||
end
|
||||
|
||||
# rubocop:disable Metrics/MethodLength
|
||||
# rubocop:disable Metrics/AbcSize
|
||||
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,
|
||||
monthly_invoice: obj.monthly_invoice
|
||||
}
|
||||
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
|
||||
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
|
||||
|
||||
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,
|
||||
monthly_invoice: invoice.monthly_invoice,
|
||||
}
|
||||
end
|
||||
# rubocop:enable Metrics/MethodLength
|
||||
# rubocop:enable Metrics/AbcSize
|
||||
end
|
||||
end
|
||||
end
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue