From 92f0cdd2720bd9fffb641e044f586704fa7e2b87 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Thu, 2 Apr 2026 18:31:46 +0000 Subject: [PATCH] =?UTF-8?q?SU2-26=20|=20=D0=A3=D0=B1=D1=80=D0=B0=D0=BB=20?= =?UTF-8?q?=D0=BB=D0=B8=D1=88=D0=BD=D0=B8=D0=B5=20=D0=B7=D0=B0=D0=BF=D0=B8?= =?UTF-8?q?=D1=81=D0=B8=20=D0=B2=20jsonb?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/services/report_parsers/form11_parser.rb | 12 +++ app/services/report_parsers/form12_parser.rb | 12 +++ app/services/report_parsers/form14_parser.rb | 103 ++++++++++--------- 3 files changed, 81 insertions(+), 46 deletions(-) diff --git a/app/services/report_parsers/form11_parser.rb b/app/services/report_parsers/form11_parser.rb index 0185894..c060aa7 100644 --- a/app/services/report_parsers/form11_parser.rb +++ b/app/services/report_parsers/form11_parser.rb @@ -2,6 +2,8 @@ module ReportParsers class Form11Parser < BaseParser SHEET_NAME = 'Бухгалтерский баланс' FORM_CODE = '1.1' + # Add line codes or names that should be skipped during parsing. + IGNORED_KEYS = [2].freeze def call return {} unless sheet_exists?(SHEET_NAME) @@ -27,6 +29,7 @@ module ReportParsers all_rows = (header_row + 1).upto(sheet.last_row).filter_map do |row| line_name = read_cell(sheet, row, columns[:name_column]) line_code = read_cell(sheet, row, columns[:code_column]).to_s.strip.presence + next if ignored_row?(line_name, line_code) values = columns[:value_columns].each_with_object({}) do |col, hash| value = read_cell(sheet, row, col) @@ -124,5 +127,14 @@ module ReportParsers year = period_text[/\d{4}/].to_i yearly_file.update!(year: year) if year.positive? end + + def ignored_row?(line_name, line_code) + keys = [line_code, line_name].compact.map { |value| normalize_label(value) } + keys.any? { |key| ignored_keys.include?(key) } + end + + def ignored_keys + @ignored_keys ||= IGNORED_KEYS.map { |value| normalize_label(value) }.to_set + end end end diff --git a/app/services/report_parsers/form12_parser.rb b/app/services/report_parsers/form12_parser.rb index 90f366b..aae3880 100644 --- a/app/services/report_parsers/form12_parser.rb +++ b/app/services/report_parsers/form12_parser.rb @@ -2,6 +2,8 @@ module ReportParsers class Form12Parser < BaseParser SHEET_NAME = 'Отчет о финансовых результатах' FORM_CODE = '1.2' + # Add line codes or names that should be skipped during parsing. + IGNORED_KEYS = [2].freeze def call return {} unless sheet_exists?(SHEET_NAME) @@ -25,6 +27,7 @@ module ReportParsers all_rows = (header_row + 1).upto(sheet.last_row).filter_map do |row| line_name = read_cell(sheet, row, columns[:name_column]) line_code = read_cell(sheet, row, columns[:code_column]).to_s.strip.presence + next if ignored_row?(line_name, line_code) values = columns[:value_columns].each_with_object({}) do |col, hash| value = read_cell(sheet, row, col) @@ -110,5 +113,14 @@ module ReportParsers value_columns: value_columns } end + + def ignored_row?(line_name, line_code) + keys = [line_code, line_name].compact.map { |value| normalize_label(value) } + keys.any? { |key| ignored_keys.include?(key) } + end + + def ignored_keys + @ignored_keys ||= IGNORED_KEYS.map { |value| normalize_label(value) }.to_set + end end end diff --git a/app/services/report_parsers/form14_parser.rb b/app/services/report_parsers/form14_parser.rb index 0c935bf..1e7f8f8 100644 --- a/app/services/report_parsers/form14_parser.rb +++ b/app/services/report_parsers/form14_parser.rb @@ -1,51 +1,9 @@ - def extract_year_from_header(header_text) - year_match = header_text[/\b(20\d{2})\b/] - year_match.to_i if year_match - end - def find_header_row(sheet) - 1.upto(sheet.last_row) do |row| - normalized_cells = 1.upto(sheet.last_column).map { |col| normalize_label(sheet.cell(row, col)) } - has_name = normalized_cells.any? { |text| text.include?("наименование показателя") } - has_code = normalized_cells.any? { |text| text == "код" || text.start_with?("код ") || text.start_with?("код:") } - return row if has_name && has_code - end - - 1 - end - - def detect_columns(sheet, header_row) - columns = 1.upto(sheet.last_column).each_with_object({}) do |col, hash| - hash[col] = normalize_label(sheet.cell(header_row, col)) - end - - name_column = columns.key(columns.values.find { |text| text.include?("наименование показателя") }) || 1 - - code_column = columns.key(columns.values.find { |text| - text == "код строки" || text.start_with?("код строки ") || - (text == "код" && columns[name_column + 1] == text) - }) || columns.key(columns.values.find { |text| text == "код" || text.start_with?("код ") }) || [name_column + 1, sheet.last_column].min - - value_columns = columns.keys.select do |col| - text = columns[col] - next false if text.include?("поясн") - text.match?(/20\d{2}/) || text.include?("за ") || text.include?("на ") - end - value_columns = value_columns.sort.last(2) - - { - name_column: name_column, - code_column: code_column, - value_columns: value_columns - } - end - - def normalize_label(text) - text.to_s.downcase.gsub(/[[:space:]]+/, ' ').strip - end module ReportParsers class Form14Parser < BaseParser - SHEET_NAME = 'Отчет о движении денежных средс' + SHEET_NAME = 'Отчет о движении денежных средс' # отличное название листа, мне нравится FORM_CODE = '1.4' + # Add line codes or names that should be skipped during parsing. + IGNORED_KEYS = [2].freeze def call return {} unless sheet_exists?(SHEET_NAME) @@ -69,6 +27,7 @@ module ReportParsers all_rows = (header_row + 1).upto(sheet.last_row).filter_map do |row| line_name = read_cell(sheet, row, columns[:name_column]) line_code = read_cell(sheet, row, columns[:code_column]).to_s.strip.presence + next if ignored_row?(line_name, line_code) values = columns[:value_columns].each_with_object({}) do |col, hash| value = read_cell(sheet, row, col) @@ -86,7 +45,7 @@ module ReportParsers end payloads_by_year = {} - value_headers.each do |col, year| + value_headers.each do |_, year| next unless year.present? rows_for_year = all_rows.map do |row| @@ -108,5 +67,57 @@ module ReportParsers payloads_by_year end + + def extract_year_from_header(header_text) + year_match = header_text[/\b(20\d{2})\b/] + year_match.to_i if year_match + end + + def find_header_row(sheet) + 1.upto(sheet.last_row) do |row| + normalized_cells = 1.upto(sheet.last_column).map { |col| normalize_label(sheet.cell(row, col)) } + has_name = normalized_cells.any? { |text| text.include?('наименование показателя') } + has_code = normalized_cells.any? { |text| text == 'код' || text.start_with?('код ') || text.start_with?('код:') } + return row if has_name && has_code + end + + 1 + end + + def detect_columns(sheet, header_row) + columns = 1.upto(sheet.last_column).each_with_object({}) do |col, hash| + hash[col] = normalize_label(sheet.cell(header_row, col)) + end + + name_column = columns.key(columns.values.find { |text| text.include?('наименование показателя') }) || 1 + + code_column = columns.key(columns.values.find { |text| + text == 'код строки' || text.start_with?('код строки ') || + (text == 'код' && columns[name_column + 1] == text) + }) || columns.key(columns.values.find { |text| text == 'код' || text.start_with?('код ') }) || [name_column + 1, sheet.last_column].min + + value_columns = columns.keys.select do |col| + text = columns[col] + next false if text.include?('поясн') + + text.match?(/20\d{2}/) || text.include?('за ') || text.include?('на ') + end + value_columns = value_columns.sort.last(2) + + { + name_column: name_column, + code_column: code_column, + value_columns: value_columns + } + end + + def ignored_row?(line_name, line_code) + keys = [line_code, line_name].compact.map { |value| normalize_label(value) } + keys.any? { |key| ignored_keys.include?(key) } + end + + def ignored_keys + @ignored_keys ||= IGNORED_KEYS.map { |value| normalize_label(value) }.to_set + end end end