diff --git a/Gemfile b/Gemfile index 578374f..7eb2513 100644 --- a/Gemfile +++ b/Gemfile @@ -6,6 +6,10 @@ ruby "3.3.0" # Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main" gem "rails", "~> 7.1.3", ">= 7.1.3.2" +gem "sidekiq" # background jobs processing +gem 'httparty' # for http requests +gem 'rubyzip' # for zip file processing + # The original asset pipeline for Rails [https://github.com/rails/sprockets-rails] gem "sprockets-rails" @@ -60,7 +64,7 @@ gem 'inline_svg' # inline svg in html gem 'rack-cas', github: 'aleksandrov1988/rack-cas', branch: 'master' gem 'addressable', github: 'aleksandrov1988/addressable', branch: 'master' -gem "pagy" # for turbo pagination +gem "pagy" # for turbo pagination group :development, :test do # See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem diff --git a/Gemfile.lock b/Gemfile.lock index b6cc204..866345b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -143,6 +143,7 @@ GEM concurrent-ruby (1.3.4) connection_pool (2.4.1) crass (1.0.6) + csv (3.3.5) dartsass-sprockets (3.1.0) railties (>= 4.0.0) sassc-embedded (~> 1.69) @@ -191,6 +192,10 @@ GEM actioncable (>= 6.0.0) listen (>= 3.0.0) railties (>= 6.0.0) + httparty (0.23.2) + csv + mini_mime (>= 1.0.0) + multi_xml (>= 0.5.2) i18n (1.14.6) concurrent-ruby (~> 1.0) importmap-rails (2.0.3) @@ -232,6 +237,8 @@ GEM mini_mime (1.1.5) minitest (5.25.1) msgpack (1.7.5) + multi_xml (0.7.2) + bigdecimal (~> 3.1) mutex_m (0.3.0) net-imap (0.5.1) date @@ -375,6 +382,12 @@ GEM rexml (~> 3.2, >= 3.2.5) rubyzip (>= 1.2.2, < 3.0) websocket (~> 1.0) + sidekiq (7.3.9) + base64 + connection_pool (>= 2.3.0) + logger + rack (>= 2.2.4) + redis-client (>= 0.22.2) slim (5.2.1) temple (~> 0.10.0) tilt (>= 2.1.0) @@ -467,6 +480,7 @@ DEPENDENCIES debug foreman hotwire-livereload (>= 1.3.0) + httparty importmap-rails inline_svg inputmask-rails @@ -483,7 +497,9 @@ DEPENDENCIES rails (~> 7.1.3, >= 7.1.3.2) redis (>= 4.0.1) rspec-rails (~> 6.1.0) + rubyzip selenium-webdriver + sidekiq slim-rails sprockets-rails stimulus-rails diff --git a/Procfile.dev b/Procfile.dev index cce5ded..f084d6a 100644 --- a/Procfile.dev +++ b/Procfile.dev @@ -1,2 +1,3 @@ -web: env RUBY_DEBUG_OPEN=true bin/rails server +web: env RUBY_DEBUG_OPEN=true bin/rails server -p 3000 css: bin/rails tailwindcss:watch +worker: bundle exec sidekiq \ No newline at end of file diff --git a/app/controllers/reports_controller.rb b/app/controllers/reports_controller.rb new file mode 100644 index 0000000..9d76023 --- /dev/null +++ b/app/controllers/reports_controller.rb @@ -0,0 +1,14 @@ +class ReportsController < ApplicationController + def index + end + + def create + inn = params[:inn] + output_path = Rails.root.join('tmp', 'reports', inn) + FileUtils.mkdir_p(output_path) + + FileProcessorJob.perform_async(inn, output_path.to_s) + + redirect_to reports_path, notice: 'Отчёт загружается в фоне' + end +end \ No newline at end of file diff --git a/app/helpers/reports_helper.rb b/app/helpers/reports_helper.rb new file mode 100644 index 0000000..cae2f09 --- /dev/null +++ b/app/helpers/reports_helper.rb @@ -0,0 +1,2 @@ +module ReportsHelper +end diff --git a/app/sidekiq/file_processor_job.rb b/app/sidekiq/file_processor_job.rb new file mode 100644 index 0000000..8f36cec --- /dev/null +++ b/app/sidekiq/file_processor_job.rb @@ -0,0 +1,53 @@ +require 'zip' +require 'stringio' + + +class FileProcessorJob + include Sidekiq::Job + + def perform(inn, output_path) + Rails.logger.info "=== Начало загрузки для ИНН: #{inn} ===" + download_inn(inn, output_path) + Rails.logger.info "=== Завершено для ИНН: #{inn} ===" + rescue => e + Rails.logger.error "=== ОШИБКА: #{e.message} ===" + Rails.logger.error e.backtrace.first(5).join("\n") + raise + end + + private + + def get_content(url, json_need = false) + headers = { + 'User-Agent' => "Mozilla/5.0 (Windows NT 10.0; Win64; x64)", + 'Accept-Language' => 'en-US,en;q=0.9', + 'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8' + } + content = HTTParty.get(url, headers: headers).body + json_need ? JSON.parse(content) : content + end + + def download_inn(inn, output) + url = "https://bo.nalog.gov.ru/advanced-search/organizations/search?query=#{inn}&page=0&size=20" + content = get_content(url, true) + id = content['content'][0]['id'] + + url = "https://bo.nalog.gov.ru/nbo/organizations/#{id}/bfo/" + content = get_content(url, true) + periods_and_details_ids = content.map { |item| [item['period'], item['typeCorrections'][0]['correction']['id']] } + + periods_and_details_ids.each do |period, details_id| + url = "https://bo.nalog.gov.ru/download/bfo/#{id}?auditReport=false&balance=true"\ + "&capitalChange=true&clarification=false&targetedFundsUsing=false&detailsId=#{details_id}"\ + "&financialResult=true&fundsMovement=true&type=XLS&period=#{period}" + content = get_content(url) + + zip_file = Zip::File.open_buffer(StringIO.new(content)) + zip_file.each do |entry| + output_file = File.join(output, entry.name) + FileUtils.mkdir_p(File.dirname(output_file)) + zip_file.extract(entry, output_file) { true } # перезаписывать если существует + end + end + end +end \ No newline at end of file diff --git a/app/views/reports/create.html.erb b/app/views/reports/create.html.erb new file mode 100644 index 0000000..cb87c0b --- /dev/null +++ b/app/views/reports/create.html.erb @@ -0,0 +1,4 @@ +
Find me in app/views/reports/create.html.erb
+<%= notice %>
+ <% end %> + + <%= form_with url: reports_path, method: :post, local: true do |f| %> +