Merge branch 'master' into 2334-remove-que

This commit is contained in:
Thiago Youssef 2022-09-28 07:40:02 -03:00
commit a6a3f72032
240 changed files with 5827 additions and 1432 deletions

View file

@ -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
@ -95,6 +98,7 @@ class Ability
can :manage, User
can :manage, ApiUser
can :manage, AdminUser
can :manage, Auction
can :manage, Certificate
can :manage, LegalDocument
can :manage, BankStatement

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -9,11 +9,30 @@ class Auction < ApplicationRecord
domain_not_registered: 'domain_not_registered',
}
enum platform: %i[auto manual]
PENDING_STATUSES = [statuses[:started],
statuses[:awaiting_payment],
statuses[:payment_received]].freeze
private_constant :PENDING_STATUSES
scope :with_status, ->(status) {
where(status: status) if status.present?
}
scope :with_start_created_at_date, ->(start_created_at) {
where('created_at >= ?', start_created_at) if start_created_at.present?
}
scope :with_end_created_at_date, ->(end_created_at) {
where('created_at <= ?', end_created_at) if end_created_at.present?
}
scope :with_domain_name, ->(domain_name) {
where('domain ilike ?', "%#{domain_name.strip}%") if domain_name.present?
}
def self.pending(domain_name)
find_by(domain: domain_name.to_s, status: PENDING_STATUSES)
end

View file

@ -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

View file

@ -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)

View file

@ -4,8 +4,8 @@ module Billing
MULTI_REGEXP = /(\d{2,20})/
def self.generate
base = Base.generate
"#{base}#{base.check_digit}"
result = EisBilling::GetReferenceNumber.send_request
JSON.parse(result.body)['reference_number']
end
def self.valid?(ref)

View file

@ -1 +0,0 @@
class BulkAction < Action; end

View file

@ -1,22 +0,0 @@
module Contact::Disclosable
extend ActiveSupport::Concern
class_methods do
attr_accessor :disclosable_attributes
end
included do
self.disclosable_attributes = %w[name email]
validate :validate_disclosed_attributes
end
private
def validate_disclosed_attributes
return if disclosed_attributes.empty?
has_undisclosable_attributes = (disclosed_attributes - self.class.disclosable_attributes)
.any?
errors.add(:disclosed_attributes, :invalid) if has_undisclosable_attributes
end
end

View file

@ -18,9 +18,7 @@ module EmailVerifable
def need_to_start_force_delete?
flag = false
ValidationEvent::INVALID_EVENTS_COUNT_BY_LEVEL.each do |level, count|
if validation_events.count >= count && validate_email_data(level: level, count: count)
flag = true
end
flag = true if validation_events.count >= count && validate_email_data(level: level, count: count)
end
flag

View file

@ -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

View file

@ -15,6 +15,8 @@ module Invoice::Payable
end
def receipt_date
return unless paid?
account_activity.created_at.to_date
end

View file

@ -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

View file

@ -7,7 +7,6 @@ class Contact < ApplicationRecord
include UserEvents
include Contact::Transferable
include Contact::Identical
include Contact::Disclosable
include Contact::Archivable
include EmailVerifable
@ -16,7 +15,7 @@ class Contact < ApplicationRecord
has_many :domain_contacts
has_many :domains, through: :domain_contacts
has_many :legal_documents, as: :documentable
has_many :validation_events, as: :validation_eventable
has_many :validation_events, as: :validation_eventable, dependent: :destroy
has_many :registrant_domains, class_name: 'Domain', foreign_key: 'registrant_id'
has_many :actions, dependent: :destroy
@ -55,7 +54,7 @@ class Contact < ApplicationRecord
validates :phone, presence: true, e164: true, phone: true
validate :correct_email_format, if: proc { |c| c.will_save_change_to_email? }
# validate :correct_email_format, if: proc { |c| c.will_save_change_to_email? }
validates :code,
uniqueness: { message: :epp_id_taken },
@ -86,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
@ -135,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,
@ -152,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
#
@ -361,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
@ -389,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
@ -411,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")
@ -428,7 +431,6 @@ class Contact < ApplicationRecord
end
domains.each { |d| d.roles = domain_c[d.id].uniq }
domains
end
@ -444,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,
@ -465,7 +477,7 @@ class Contact < ApplicationRecord
PENDING_CREATE,
PENDING_TRANSFER,
PENDING_UPDATE,
PENDING_DELETE
PENDING_DELETE,
]).present?
end
@ -596,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

View file

@ -0,0 +1 @@
class ContactUpdateAction < Action; end

View file

@ -33,6 +33,7 @@ class Deposit
def issue_prepayment_invoice
return unless valid?
registrar.issue_prepayment_invoice(amount, description)
end
end

View file

@ -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

View file

@ -35,6 +35,7 @@ module DNS
def sell_at_auction
auction = Auction.new
auction.domain = name
auction.platform = 'auto'
auction.start
ToStdout.msg "Created the auction: #{auction.inspect}"
update_whois_from_auction(auction)

View file

@ -161,14 +161,6 @@ class Domain < ApplicationRecord
attribute: 'hostname'
}
validates :tech_domain_contacts, uniqueness_multi: {
attribute: 'contact_code_cache'
}
validates :admin_domain_contacts, uniqueness_multi: {
attribute: 'contact_code_cache'
}
validates :dnskeys, uniqueness_multi: {
attribute: 'public_key'
}
@ -726,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

View file

@ -6,32 +6,21 @@ class DomainContact < ApplicationRecord
belongs_to :contact
belongs_to :domain
validates :contact, presence: true
after_destroy :update_contact
attr_accessor :value_typeahead
attr_writer :contact_code
self.ignored_columns = %w[legacy_domain_id legacy_contact_id]
def epp_code_map
{
'2302' => [
[:contact_code_cache, :taken, { value: { obj: 'contact', val: contact_code_cache } }]
]
}
end
def name
return 'Tech' if type == 'TechDomainContact'
return 'Admin' if type == 'AdminDomainContact'
''
end
validates :contact, presence: true
before_save :update_contact_code_cache
def update_contact_code_cache
self.contact_code_cache = contact.code
end
after_destroy :update_contact
def update_contact
Contact.find(contact_id).save
end

View file

@ -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

View file

@ -1,13 +1,7 @@
class Feature
# def self.obj_and_extensions_statuses_enabled?
# return false if ENV['obj_and_extensions_prohibited'] == 'false'
#
# ENV['obj_and_extensions_prohibited'] || false
# end
#
# def self.enable_lock_domain_with_new_statuses?
# return false if ENV['enable_lock_domain_with_new_statuses'] == 'false'
#
# ENV['enable_lock_domain_with_new_statuses'] || false
# end
def self.billing_system_integrated?
return false if ENV['billing_system_integrated'] == 'false'
ENV['billing_system_integrated'] || false
end
end

View file

@ -32,30 +32,43 @@ 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
def set_invoice_number
last_no = Invoice.order(number: :desc).limit(1).pick(:number)
def validate_invoice_number(result)
response = JSON.parse(result.body)
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
billing_restrictions_issue if response['code'] == '403'
billing_out_of_range_issue if response['error'] == 'out of range'
end
return if number <= Setting.invoice_number_max.to_i
def billing_restrictions_issue
errors.add(:base, I18n.t('cannot get access'))
logger.error('PROBLEM WITH TOKEN')
throw(:abort)
end
def billing_out_of_range_issue
errors.add(:base, I18n.t('failed_to_generate_invoice_invoice_number_limit_reached'))
logger.error('INVOICE NUMBER LIMIT REACHED, COULD NOT GENERATE INVOICE')
throw(:abort)
end
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 to_s
I18n.t('invoice_no', no: number)
end
@ -82,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
@ -95,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
@ -144,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

View file

@ -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

View file

@ -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

View file

@ -1,5 +1,6 @@
class Notification < ApplicationRecord
include Versions # version/notification_version.rb
include EppErrors
belongs_to :registrar
belongs_to :action, optional: true

View file

@ -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
@ -34,12 +34,12 @@ class Registrar < ApplicationRecord
attribute :vat_rate, ::Type::VatRate.new
after_initialize :set_defaults
validate :correct_email_format, if: proc { |c| c.will_save_change_to_email? }
validate :correct_billing_email_format
# validate :correct_email_format, if: proc { |c| c.will_save_change_to_email? }
# validate :correct_billing_email_format
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,10 +146,17 @@ class Registrar < ApplicationRecord
.deliver_later(wait: 1.minute)
end
add_invoice_instance = EisBilling::AddDeposits.new(invoice)
result = add_invoice_instance.send_invoice
link = JSON.parse(result.body)['everypay_link']
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)
@ -175,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?
@ -220,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
@ -256,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

View file

@ -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

View file

@ -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

View file

@ -27,7 +27,7 @@ class ValidationEvent < ApplicationRecord
belongs_to :validation_eventable, polymorphic: true
scope :recent, -> { where('created_at < ?', Time.zone.now - VALIDATION_PERIOD) }
scope :old_records, -> { where('created_at < ?', Time.zone.now - VALIDATION_PERIOD) }
scope :successful, -> { where(success: true) }
scope :failed, -> { where(success: false) }
scope :regex, -> { where('event_data @> ?', { 'check_level': 'regex' }.to_json) }
@ -35,11 +35,11 @@ class ValidationEvent < ApplicationRecord
scope :smtp, -> { where('event_data @> ?', { 'check_level': 'smtp' }.to_json) }
scope :by_object, ->(object) { where(validation_eventable: object) }
after_create :check_for_force_delete
def self.validated_ids_by(klass)
recent.successful.where('validation_eventable_type = ?', klass)
.pluck(:validation_eventable_id)
old_records
.successful
.where('validation_eventable_type = ?', klass)
.pluck(:validation_eventable_id)
end
def failed?
@ -57,56 +57,4 @@ class ValidationEvent < ApplicationRecord
def object
validation_eventable
end
def check_for_force_delete
if object.need_to_start_force_delete?
start_force_delete
elsif object.need_to_lift_force_delete?
refresh_status_notes
lift_force_delete
end
end
def start_force_delete
Domains::ForceDeleteEmail::Base.run(email: email)
end
def refresh_status_notes
domain_list.each do |domain|
next unless domain.status_notes[DomainStatus::FORCE_DELETE]
domain.status_notes[DomainStatus::FORCE_DELETE].slice!(object.email_history)
domain.status_notes[DomainStatus::FORCE_DELETE].lstrip!
domain.save(validate: false)
notify_registrar(domain) unless domain.status_notes[DomainStatus::FORCE_DELETE].empty?
end
end
def domain_list
domain_contacts = Contact.where(email: email).map(&:domain_contacts).flatten
registrant_ids = Registrant.where(email: email).pluck(:id)
(domain_contacts.map(&:domain).flatten + Domain.where(registrant_id: registrant_ids)).uniq
end
def notify_registrar(domain)
domain.registrar.notifications.create!(text: I18n.t('force_delete_auto_email',
domain_name: domain.name,
outzone_date: domain.outzone_date,
purge_date: domain.purge_date,
email: domain.status_notes[DomainStatus::FORCE_DELETE]))
end
def lift_force_delete
# domain_contacts = Contact.where(email: email).map(&:domain_contacts).flatten
# registrant_ids = Registrant.where(email: email).pluck(:id)
#
# domains = domain_contacts.map(&:domain).flatten +
# Domain.where(registrant_id: registrant_ids)
#
# domains.each do |domain|
# Domains::ForceDeleteLift::Base.run(domain: domain)
# end
end
end