Merge branch 'features/layout' of DAAntipenko/finreport-analyzer into develop
This commit is contained in:
@@ -9,6 +9,7 @@ gem "rails", "~> 7.1.3", ">= 7.1.3.2"
|
|||||||
gem "sidekiq" # background jobs processing
|
gem "sidekiq" # background jobs processing
|
||||||
gem 'httparty' # for http requests
|
gem 'httparty' # for http requests
|
||||||
gem 'rubyzip' # for zip file processing
|
gem 'rubyzip' # for zip file processing
|
||||||
|
gem 'roo', '~> 2.10' # for xlsx file parsing
|
||||||
|
|
||||||
# The original asset pipeline for Rails [https://github.com/rails/sprockets-rails]
|
# The original asset pipeline for Rails [https://github.com/rails/sprockets-rails]
|
||||||
gem "sprockets-rails"
|
gem "sprockets-rails"
|
||||||
|
|||||||
@@ -335,6 +335,9 @@ GEM
|
|||||||
request_store (1.7.0)
|
request_store (1.7.0)
|
||||||
rack (>= 1.4)
|
rack (>= 1.4)
|
||||||
rexml (3.3.9)
|
rexml (3.3.9)
|
||||||
|
roo (2.10.1)
|
||||||
|
nokogiri (~> 1)
|
||||||
|
rubyzip (>= 1.3.0, < 3.0.0)
|
||||||
rspec-core (3.13.2)
|
rspec-core (3.13.2)
|
||||||
rspec-support (~> 3.13.0)
|
rspec-support (~> 3.13.0)
|
||||||
rspec-expectations (3.13.3)
|
rspec-expectations (3.13.3)
|
||||||
@@ -496,6 +499,7 @@ DEPENDENCIES
|
|||||||
rack-cas!
|
rack-cas!
|
||||||
rails (~> 7.1.3, >= 7.1.3.2)
|
rails (~> 7.1.3, >= 7.1.3.2)
|
||||||
redis (>= 4.0.1)
|
redis (>= 4.0.1)
|
||||||
|
roo (~> 2.10)
|
||||||
rspec-rails (~> 6.1.0)
|
rspec-rails (~> 6.1.0)
|
||||||
rubyzip
|
rubyzip
|
||||||
selenium-webdriver
|
selenium-webdriver
|
||||||
|
|||||||
@@ -18,4 +18,6 @@
|
|||||||
@use "jquery-ui/core";
|
@use "jquery-ui/core";
|
||||||
@use "toastr";
|
@use "toastr";
|
||||||
|
|
||||||
@use "base";
|
@use "base";
|
||||||
|
@use "application_settings";
|
||||||
|
@use "main";
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
:root{
|
||||||
|
--bg-color: #F3F3F5;
|
||||||
|
--navbar-border: #d3d3d3;
|
||||||
|
--blue-color: #0078F6;
|
||||||
|
--blue-light-color: #e5f1fe;
|
||||||
|
--text-callout-color: #B6B6B6;
|
||||||
|
--yellow-color: #F9D256;
|
||||||
|
--white-color: #fff;
|
||||||
|
--red-color: #EF3E3B;
|
||||||
|
--footer-text-color: #757575;
|
||||||
|
--purple-color: #4C13A2;
|
||||||
|
--green-color: #4CBFC0;
|
||||||
|
--orange-color: #FE9F40;
|
||||||
|
--grey-color: #C9CBCE;
|
||||||
|
--grey-light-color:#F6F6F6;
|
||||||
|
--grey-dark-color:#a9a9a9;
|
||||||
|
--border-dropdown-color: #e8e8e8;
|
||||||
|
--text-dropdown-color: #0F0F11;
|
||||||
|
--main-header-text: #7C7C7C;
|
||||||
|
|
||||||
|
//TEXT weight
|
||||||
|
--bold: 700;
|
||||||
|
--semi-bold: 600;
|
||||||
|
--medium: 500;
|
||||||
|
--regular: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: 'Inter', sans-serif;
|
||||||
|
background-color: var(--bg-color);
|
||||||
|
width: 90vw;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
.main-header {
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 2.5rem;
|
||||||
|
font-weight: var(--bold);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-container {
|
||||||
|
width: 20%;
|
||||||
|
margin: 0 auto;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-new {
|
||||||
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
|
margin-block: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.link {
|
||||||
|
border: 1px solid black;
|
||||||
|
border-radius: 20px 20px;
|
||||||
|
padding: 10px;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
a.link:hover {
|
||||||
|
background-color: black;
|
||||||
|
color: var(--white-color)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.header{
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-title {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
font-weight: var(--bold);
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-container {
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.informations-data {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 80%;
|
||||||
|
min-width: 300px;
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.information-data {
|
||||||
|
border: 2px solid black;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 5px;
|
||||||
|
margin: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.information-elem {
|
||||||
|
font-weight: var(--semi-bold);
|
||||||
|
font-size: 1.5rem;
|
||||||
|
padding: 2.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-new-organisation {
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
padding: 10px;
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
class AuditorsController < ApplicationController
|
||||||
|
def index
|
||||||
|
auditors = Auditor.all
|
||||||
|
|
||||||
|
@info_about_auditors = auditors.each_with_object({}) do |auditor, hash|
|
||||||
|
hash[auditor.id] = {
|
||||||
|
"Наименование аудиторской организации/ФИО индивидуального аудитора" => auditor.name,
|
||||||
|
"ИНН" => auditor.inn,
|
||||||
|
"ОГРН/ОРГНИП" => auditor.ogrn
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -1,4 +1,19 @@
|
|||||||
class WelcomeController < ApplicationController
|
class WelcomeController < ApplicationController
|
||||||
|
|
||||||
def home; end
|
def index
|
||||||
|
organizations = Organization.all
|
||||||
|
|
||||||
|
@info_about_organisations = organizations.each_with_object({}) do |org, hash|
|
||||||
|
hash[org.id] = {
|
||||||
|
"ИНН" => org.inn,
|
||||||
|
"Полное наименование юридического лица" => org.full_name,
|
||||||
|
"КПП" => org.kpp,
|
||||||
|
"Код по ОКПО" => org.okpo,
|
||||||
|
"Форма собственности (по ОКФС)" => org.okfs,
|
||||||
|
"Организационно-правовая форма (по ОКОПФ)" => org.okopf,
|
||||||
|
"Вид экономической деятельности по ОКВЭД 2" => org.okved2,
|
||||||
|
"Местонахождение (адрес)" => org.address
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
module AuditorsHelper
|
||||||
|
end
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
class Auditor < ApplicationRecord
|
||||||
|
has_many :yearly_files, dependent: :nullify
|
||||||
|
end
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
class DocumentType < ApplicationRecord
|
||||||
|
has_many :financial_statements, dependent: :restrict_with_error
|
||||||
|
|
||||||
|
validates :name, presence: true
|
||||||
|
end
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
class FinancialStatement < ApplicationRecord
|
||||||
|
belongs_to :yearly_file
|
||||||
|
belongs_to :document_type
|
||||||
|
|
||||||
|
validates :data, presence: true
|
||||||
|
end
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
class Organization < ApplicationRecord
|
||||||
|
has_many :yearly_files, dependent: :destroy
|
||||||
|
has_many :financial_statements, through: :yearly_files
|
||||||
|
|
||||||
|
validates :inn, presence: true
|
||||||
|
end
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
class YearlyFile < ApplicationRecord
|
||||||
|
belongs_to :organization
|
||||||
|
belongs_to :auditor, optional: true
|
||||||
|
|
||||||
|
has_many :financial_statements, dependent: :destroy
|
||||||
|
|
||||||
|
has_one_attached :file
|
||||||
|
|
||||||
|
validates :year, presence: true
|
||||||
|
end
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
require 'roo'
|
||||||
|
|
||||||
|
class ReportParser
|
||||||
|
SHEET_NAME = 'Сведения об организации'
|
||||||
|
CELL_MAP = {
|
||||||
|
full_name: { row: 6, col: 13 },
|
||||||
|
inn: { row: 10, col: 13 },
|
||||||
|
kpp: { row: 11, col: 13 },
|
||||||
|
okpo: { row: 12, col: 13 },
|
||||||
|
okfs: { row: 13, col: 13 },
|
||||||
|
okopf: { row: 14, col: 13 },
|
||||||
|
okved2: { row: 15, col: 13 },
|
||||||
|
address: { row: 16, col: 13 }
|
||||||
|
}.freeze
|
||||||
|
|
||||||
|
AUDITOR_MAP = {
|
||||||
|
name: { row: 19, col: 13 },
|
||||||
|
inn: { row: 20, col: 13 },
|
||||||
|
ogrn: { row: 21, col: 13 }
|
||||||
|
}.freeze
|
||||||
|
|
||||||
|
def initialize(yearly_file)
|
||||||
|
@yearly_file = yearly_file
|
||||||
|
end
|
||||||
|
|
||||||
|
def call
|
||||||
|
ActiveRecord::Base.transaction do
|
||||||
|
parse_organization
|
||||||
|
parse_auditor
|
||||||
|
extract_year
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def spreadsheet
|
||||||
|
@spreadsheet ||= begin
|
||||||
|
tempfile = Tempfile.new(['report', '.xlsx'])
|
||||||
|
tempfile.binmode
|
||||||
|
@yearly_file.file.download { |chunk| tempfile.write(chunk) }
|
||||||
|
tempfile.rewind
|
||||||
|
Roo::Excelx.new(tempfile.path)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def info_sheet
|
||||||
|
@info_sheet ||= spreadsheet.sheet(SHEET_NAME)
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_cell(sheet, row, col)
|
||||||
|
value = sheet.cell(row, col)
|
||||||
|
value.is_a?(String) ? value.strip.presence : value
|
||||||
|
end
|
||||||
|
|
||||||
|
def parse_organization
|
||||||
|
org_data = CELL_MAP.each_with_object({}) do |(attr, pos), hash|
|
||||||
|
hash[attr] = read_cell(info_sheet, pos[:row], pos[:col])
|
||||||
|
end
|
||||||
|
|
||||||
|
organization = @yearly_file.organization
|
||||||
|
organization.update!(org_data)
|
||||||
|
organization
|
||||||
|
end
|
||||||
|
|
||||||
|
def parse_auditor
|
||||||
|
auditor_data = AUDITOR_MAP.each_with_object({}) do |(attr, pos), hash|
|
||||||
|
hash[attr] = read_cell(info_sheet, pos[:row], pos[:col])
|
||||||
|
end
|
||||||
|
|
||||||
|
return if auditor_data[:name].blank?
|
||||||
|
|
||||||
|
auditor_inn = auditor_data[:inn].to_s.strip
|
||||||
|
auditor = if auditor_inn.present?
|
||||||
|
Auditor.find_or_initialize_by(inn: auditor_inn)
|
||||||
|
else
|
||||||
|
Auditor.find_or_initialize_by(name: auditor_data[:name])
|
||||||
|
end
|
||||||
|
|
||||||
|
auditor.assign_attributes(
|
||||||
|
name: auditor_data[:name],
|
||||||
|
inn: auditor_inn.presence,
|
||||||
|
ogrn: auditor_data[:ogrn].to_s.strip.presence
|
||||||
|
)
|
||||||
|
auditor.save!
|
||||||
|
|
||||||
|
@yearly_file.update!(auditor: auditor)
|
||||||
|
auditor
|
||||||
|
end
|
||||||
|
|
||||||
|
def extract_year
|
||||||
|
balance_sheet = spreadsheet.sheet('Бухгалтерский баланс')
|
||||||
|
period_text = balance_sheet.cell(4, 1).to_s
|
||||||
|
year = period_text.match(/(\d{4})/)&.captures&.first&.to_i
|
||||||
|
|
||||||
|
@yearly_file.update!(year: year) if year
|
||||||
|
year
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -15,11 +15,26 @@ class FileProcessorJob
|
|||||||
|
|
||||||
download_inn(inn, temp_dir.to_s)
|
download_inn(inn, temp_dir.to_s)
|
||||||
|
|
||||||
|
# Найти или создать организацию по ИНН
|
||||||
|
organization = Organization.find_or_create_by!(inn: inn)
|
||||||
|
|
||||||
Dir.glob("#{temp_dir}/**/*").select { |f| File.file?(f) }.each do |file_path|
|
Dir.glob("#{temp_dir}/**/*").select { |f| File.file?(f) }.each do |file_path|
|
||||||
|
# Прикрепляем файл к Report (обратная совместимость)
|
||||||
report.files.attach(
|
report.files.attach(
|
||||||
io: File.open(file_path),
|
io: File.open(file_path),
|
||||||
filename: File.basename(file_path)
|
filename: File.basename(file_path)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Создаём YearlyFile и прикрепляем xlsx
|
||||||
|
yearly_file = organization.yearly_files.build(year: 0)
|
||||||
|
yearly_file.file.attach(
|
||||||
|
io: File.open(file_path),
|
||||||
|
filename: File.basename(file_path)
|
||||||
|
)
|
||||||
|
yearly_file.save!
|
||||||
|
|
||||||
|
# Парсим данные из xlsx
|
||||||
|
ReportParser.new(yearly_file).call
|
||||||
end
|
end
|
||||||
|
|
||||||
# Удали временные файлы
|
# Удали временные файлы
|
||||||
@@ -69,4 +84,4 @@ class FileProcessorJob
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
.main-header
|
||||||
|
= 'Отчетность за 2026 год'
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
.nav-container
|
||||||
|
a.link[href="/"]
|
||||||
|
= 'Организации'
|
||||||
|
a.link[href="/auditors/index"]
|
||||||
|
= 'Аудиторы'
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
.header
|
||||||
|
.header-title
|
||||||
|
= "Аудиторы"
|
||||||
|
.main-container
|
||||||
|
- unless @info_about_auditors.any?
|
||||||
|
= "Аудиторы не найдены"
|
||||||
|
- else
|
||||||
|
.informations-data
|
||||||
|
- @info_about_auditors.each do |auditor, params|
|
||||||
|
.information-data
|
||||||
|
- params.keys.each do |key|
|
||||||
|
.information-elem
|
||||||
|
= "#{key}: #{params[key]}"
|
||||||
@@ -2,7 +2,7 @@ doctype html
|
|||||||
html
|
html
|
||||||
head
|
head
|
||||||
title
|
title
|
||||||
=t('app_name')
|
= "Отчетность 2026"
|
||||||
meta[name="viewport" content="width=device-width,initial-scale=1"]
|
meta[name="viewport" content="width=device-width,initial-scale=1"]
|
||||||
meta name="turbo-refresh-method" content="morph"
|
meta name="turbo-refresh-method" content="morph"
|
||||||
= csrf_meta_tags
|
= csrf_meta_tags
|
||||||
@@ -16,4 +16,6 @@ html
|
|||||||
= javascript_importmap_tags
|
= javascript_importmap_tags
|
||||||
= hotwire_livereload_tags if Rails.env.development?
|
= hotwire_livereload_tags if Rails.env.development?
|
||||||
body
|
body
|
||||||
|
= render 'header'
|
||||||
|
= render 'navbar'
|
||||||
= yield
|
= yield
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
.header
|
||||||
|
.header-title
|
||||||
|
= "Организации"
|
||||||
|
.main-container
|
||||||
|
.add-new
|
||||||
|
a.link[href="/reports"]
|
||||||
|
= "Добавить новую организацию"
|
||||||
|
- unless @info_about_organisations.any?
|
||||||
|
= 'Организации не найдены'
|
||||||
|
- else
|
||||||
|
.informations-data
|
||||||
|
- @info_about_organisations.each do |organisation, params|
|
||||||
|
.information-data
|
||||||
|
- params.keys.each do |key|
|
||||||
|
.information-elem
|
||||||
|
= "#{key}: #{params[key]}"
|
||||||
+2
-1
@@ -1,6 +1,7 @@
|
|||||||
# require 'sidekiq/web'
|
# require 'sidekiq/web'
|
||||||
|
|
||||||
Rails.application.routes.draw do
|
Rails.application.routes.draw do
|
||||||
|
get 'auditors/index'
|
||||||
get 'reports/create'
|
get 'reports/create'
|
||||||
get '/reports', to: 'reports#index'
|
get '/reports', to: 'reports#index'
|
||||||
post '/reports', to: 'reports#create'
|
post '/reports', to: 'reports#create'
|
||||||
@@ -9,5 +10,5 @@ Rails.application.routes.draw do
|
|||||||
|
|
||||||
|
|
||||||
# Defines the root path route ("/")
|
# Defines the root path route ("/")
|
||||||
root "welcome#home"
|
root "welcome#index"
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
class CreateOrganizations < ActiveRecord::Migration[7.1]
|
||||||
|
def change
|
||||||
|
create_table :organizations do |t|
|
||||||
|
t.string :inn, null: false
|
||||||
|
t.string :full_name
|
||||||
|
t.string :kpp
|
||||||
|
t.string :okpo
|
||||||
|
t.string :okfs
|
||||||
|
t.string :okopf
|
||||||
|
t.string :okved2
|
||||||
|
t.string :address
|
||||||
|
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
|
||||||
|
add_index :organizations, :inn
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
class CreateAuditors < ActiveRecord::Migration[7.1]
|
||||||
|
def change
|
||||||
|
create_table :auditors do |t|
|
||||||
|
t.string :name
|
||||||
|
t.string :inn
|
||||||
|
t.string :ogrn
|
||||||
|
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
|
||||||
|
add_index :auditors, :inn
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
class CreateDocumentTypes < ActiveRecord::Migration[7.1]
|
||||||
|
def change
|
||||||
|
create_table :document_types do |t|
|
||||||
|
t.string :name, null: false
|
||||||
|
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
class CreateYearlyFiles < ActiveRecord::Migration[7.1]
|
||||||
|
def change
|
||||||
|
create_table :yearly_files do |t|
|
||||||
|
t.references :organization, null: false, foreign_key: true
|
||||||
|
t.integer :year, null: false
|
||||||
|
t.references :auditor, foreign_key: true
|
||||||
|
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
class CreateFinancialStatements < ActiveRecord::Migration[7.1]
|
||||||
|
def change
|
||||||
|
create_table :financial_statements do |t|
|
||||||
|
t.references :yearly_file, null: false, foreign_key: true
|
||||||
|
t.references :document_type, null: false, foreign_key: true
|
||||||
|
t.integer :sheet_number
|
||||||
|
t.jsonb :data, default: {}
|
||||||
|
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
Generated
+55
-1
@@ -10,7 +10,7 @@
|
|||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema[7.1].define(version: 2025_12_09_142642) do
|
ActiveRecord::Schema[7.1].define(version: 2026_03_12_000005) do
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "plpgsql"
|
enable_extension "plpgsql"
|
||||||
|
|
||||||
@@ -42,6 +42,46 @@ ActiveRecord::Schema[7.1].define(version: 2025_12_09_142642) do
|
|||||||
t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true
|
t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
create_table "auditors", force: :cascade do |t|
|
||||||
|
t.string "name"
|
||||||
|
t.string "inn"
|
||||||
|
t.string "ogrn"
|
||||||
|
t.datetime "created_at", null: false
|
||||||
|
t.datetime "updated_at", null: false
|
||||||
|
t.index ["inn"], name: "index_auditors_on_inn"
|
||||||
|
end
|
||||||
|
|
||||||
|
create_table "document_types", force: :cascade do |t|
|
||||||
|
t.string "name", null: false
|
||||||
|
t.datetime "created_at", null: false
|
||||||
|
t.datetime "updated_at", null: false
|
||||||
|
end
|
||||||
|
|
||||||
|
create_table "financial_statements", force: :cascade do |t|
|
||||||
|
t.bigint "yearly_file_id", null: false
|
||||||
|
t.bigint "document_type_id", null: false
|
||||||
|
t.integer "sheet_number"
|
||||||
|
t.jsonb "data", default: {}
|
||||||
|
t.datetime "created_at", null: false
|
||||||
|
t.datetime "updated_at", null: false
|
||||||
|
t.index ["document_type_id"], name: "index_financial_statements_on_document_type_id"
|
||||||
|
t.index ["yearly_file_id"], name: "index_financial_statements_on_yearly_file_id"
|
||||||
|
end
|
||||||
|
|
||||||
|
create_table "organizations", force: :cascade do |t|
|
||||||
|
t.string "inn", null: false
|
||||||
|
t.string "full_name"
|
||||||
|
t.string "kpp"
|
||||||
|
t.string "okpo"
|
||||||
|
t.string "okfs"
|
||||||
|
t.string "okopf"
|
||||||
|
t.string "okved2"
|
||||||
|
t.string "address"
|
||||||
|
t.datetime "created_at", null: false
|
||||||
|
t.datetime "updated_at", null: false
|
||||||
|
t.index ["inn"], name: "index_organizations_on_inn"
|
||||||
|
end
|
||||||
|
|
||||||
create_table "reports", force: :cascade do |t|
|
create_table "reports", force: :cascade do |t|
|
||||||
t.string "inn"
|
t.string "inn"
|
||||||
t.string "status"
|
t.string "status"
|
||||||
@@ -76,6 +116,20 @@ ActiveRecord::Schema[7.1].define(version: 2025_12_09_142642) do
|
|||||||
t.index ["item_type", "item_id"], name: "index_versions_on_item_type_and_item_id"
|
t.index ["item_type", "item_id"], name: "index_versions_on_item_type_and_item_id"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
create_table "yearly_files", force: :cascade do |t|
|
||||||
|
t.bigint "organization_id", null: false
|
||||||
|
t.integer "year", null: false
|
||||||
|
t.bigint "auditor_id"
|
||||||
|
t.datetime "created_at", null: false
|
||||||
|
t.datetime "updated_at", null: false
|
||||||
|
t.index ["auditor_id"], name: "index_yearly_files_on_auditor_id"
|
||||||
|
t.index ["organization_id"], name: "index_yearly_files_on_organization_id"
|
||||||
|
end
|
||||||
|
|
||||||
add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
|
add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
|
||||||
add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"
|
add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"
|
||||||
|
add_foreign_key "financial_statements", "document_types"
|
||||||
|
add_foreign_key "financial_statements", "yearly_files"
|
||||||
|
add_foreign_key "yearly_files", "auditors"
|
||||||
|
add_foreign_key "yearly_files", "organizations"
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
# Specs in this file have access to a helper object that includes
|
||||||
|
# the AuditorsHelper. For example:
|
||||||
|
#
|
||||||
|
# describe AuditorsHelper do
|
||||||
|
# describe "string concat" do
|
||||||
|
# it "concats two strings with spaces" do
|
||||||
|
# expect(helper.concat_strings("this","that")).to eq("this that")
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
RSpec.describe AuditorsHelper, type: :helper do
|
||||||
|
pending "add some examples to (or delete) #{__FILE__}"
|
||||||
|
end
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe "Auditors", type: :request do
|
||||||
|
describe "GET /index" do
|
||||||
|
it "returns http success" do
|
||||||
|
get "/auditors/index"
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe "auditors/index.html.tailwindcss", type: :view do
|
||||||
|
pending "add some examples to (or delete) #{__FILE__}"
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user