SU2-33 | Парсер формы 1.3, фикс смещения колонок в упрощённой отчётности

This commit is contained in:
Dmitry
2026-04-21 08:33:24 +00:00
parent 136de5f238
commit aea8b7975e
3 changed files with 139 additions and 7 deletions
@@ -0,0 +1,115 @@
module ReportParsers
class Form13Parser < BaseParser
SHEET_NAME = 'Отчет об изменениях капитала'
FORM_CODE = '1.3'
def call
return {} unless sheet_exists?(SHEET_NAME)
sheet = spreadsheet.sheet(SHEET_NAME)
build_payloads(sheet)
end
private
def build_payloads(sheet)
header_row = find_section1_header_row(sheet)
return {} unless header_row
report_year = extract_report_year(sheet)
return {} unless report_year
name_col = find_name_column(sheet, header_row)
code_col = find_code_column(sheet, header_row)
total_col = find_total_column(sheet, header_row)
return {} unless code_col && total_col
end_row = section1_end_row(sheet, header_row)
payloads_by_year = {}
(header_row + 2).upto(end_row) do |row|
line_code = read_cell(sheet, row, code_col).to_s.strip.presence
next unless line_code&.match?(/\A3[123]\d{2}\z/)
row_year = year_from_code(line_code, report_year)
next unless row_year
name_cell = read_cell(sheet, row, name_col).to_s.strip
name_cell = read_cell(sheet, row, 1).to_s.strip if name_cell.blank?
value = read_cell(sheet, row, total_col)
payloads_by_year[row_year] ||= { form_code: FORM_CODE, sheet_name: SHEET_NAME, rows: [] }
payloads_by_year[row_year][:rows] << {
line_name: normalize_line_name(name_cell),
line_code: line_code,
value: value
}
end
payloads_by_year
end
def year_from_code(code, report_year)
case code[1]
when '1' then report_year - 2
when '2' then report_year - 1
when '3' then report_year
end
end
def extract_report_year(sheet)
1.upto([sheet.last_row, 10].min) do |row|
1.upto(sheet.last_column) do |col|
m = sheet.cell(row, col).to_s.match(/\bза\s+(20\d{2})\s*г/i)
return m[1].to_i if m
end
end
year = yearly_file&.year.to_i
year.positive? ? year : nil
end
def find_section1_header_row(sheet)
1.upto(sheet.last_row) do |row|
cells = 1.upto(sheet.last_column).map { |col| normalize_label(sheet.cell(row, col)) }
next unless cells.any? { |c| c.include?('наименование показателя') }
next unless cells.any? { |c| c.include?('код строки') || c == 'код' }
return row
end
nil
end
def find_name_column(sheet, header_row)
1.upto(sheet.last_column) do |col|
return col if normalize_label(sheet.cell(header_row, col)).include?('наименование показателя')
end
1
end
def find_code_column(sheet, header_row)
1.upto(sheet.last_column) do |col|
text = normalize_label(sheet.cell(header_row, col))
return col if text.include?('код строки') || text == 'код'
end
nil
end
def find_total_column(sheet, header_row)
result = nil
[header_row, header_row + 1].each do |row|
1.upto(sheet.last_column) do |col|
result = col if normalize_label(sheet.cell(row, col)) == 'итого'
end
return result if result
end
nil
end
def section1_end_row(sheet, from_row)
(from_row + 1).upto(sheet.last_row) do |row|
return row - 1 if normalize_label(sheet.cell(row, 1)).match?(/\A2\.\s*корректировки/i)
end
sheet.last_row
end
end
end