mirror of
https://github.com/we-promise/sure.git
synced 2026-04-19 03:54:08 +00:00
Handle missing category import headers and accept name* (#487)
* Handle missing headers in category import * Hoist category import header lookups
This commit is contained in:
@@ -12,6 +12,9 @@ class Import::ConfigurationsController < ApplicationController
|
||||
@import.reload.sync_mappings
|
||||
|
||||
redirect_to import_clean_path(@import), notice: "Import configured successfully."
|
||||
rescue ActiveRecord::RecordInvalid => e
|
||||
message = e.record.errors.full_messages.to_sentence.presence || e.message
|
||||
redirect_back_or_to import_configuration_path(@import), alert: message
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -59,19 +59,52 @@ class CategoryImport < Import
|
||||
def generate_rows_from_csv
|
||||
rows.destroy_all
|
||||
|
||||
validate_required_headers!
|
||||
|
||||
name_header = header_for("name")
|
||||
color_header = header_for("color")
|
||||
parent_header = header_for("parent_category", "parent category")
|
||||
classification_header = header_for("classification")
|
||||
icon_header = header_for("lucide_icon", "lucide icon", "icon")
|
||||
|
||||
csv_rows.each do |row|
|
||||
rows.create!(
|
||||
name: row["name"].to_s.strip,
|
||||
category_color: row["color"].to_s.strip,
|
||||
category_parent: row["parent_category"].to_s.strip,
|
||||
category_classification: row["classification"].to_s.strip,
|
||||
category_icon: (row["lucide_icon"].presence || row["icon"]).to_s.strip,
|
||||
name: row[name_header].to_s.strip,
|
||||
category_color: row[color_header].to_s.strip,
|
||||
category_parent: row[parent_header].to_s.strip,
|
||||
category_classification: row[classification_header].to_s.strip,
|
||||
category_icon: row[icon_header].to_s.strip,
|
||||
currency: default_currency
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def validate_required_headers!
|
||||
missing_headers = required_column_keys.map(&:to_s).reject { |key| header_for(key).present? }
|
||||
return if missing_headers.empty?
|
||||
|
||||
errors.add(:base, "Missing required columns: #{missing_headers.join(', ')}")
|
||||
raise ActiveRecord::RecordInvalid.new(self)
|
||||
end
|
||||
|
||||
def header_for(*candidates)
|
||||
candidates.each do |candidate|
|
||||
normalized = normalize_header(candidate)
|
||||
header = normalized_headers[normalized]
|
||||
return header if header.present?
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
def normalized_headers
|
||||
@normalized_headers ||= csv_headers.to_h { |header| [ normalize_header(header), header ] }
|
||||
end
|
||||
|
||||
def normalize_header(header)
|
||||
header.to_s.strip.downcase.gsub(/\*/, "").gsub(/[\s-]+/, "_")
|
||||
end
|
||||
|
||||
def ensure_placeholder_category(name)
|
||||
trimmed_name = name.to_s.strip
|
||||
|
||||
@@ -69,4 +69,29 @@ class CategoryImportTest < ActiveSupport::TestCase
|
||||
assert_equal "#bbbbbb", snacks.color
|
||||
assert_equal "pizza", snacks.lucide_icon
|
||||
end
|
||||
|
||||
test "accepts required headers with an asterisk suffix" do
|
||||
csv = <<~CSV
|
||||
name*,color,parent_category,classification,icon
|
||||
Food & Drink,#f97316,,expense,carrot
|
||||
CSV
|
||||
|
||||
import = @family.imports.create!(type: "CategoryImport", raw_file_str: csv, col_sep: ",")
|
||||
import.generate_rows_from_csv
|
||||
|
||||
assert_equal 1, import.rows.count
|
||||
assert_equal "Food & Drink", import.rows.first.name
|
||||
end
|
||||
|
||||
test "fails fast when required headers are missing" do
|
||||
csv = <<~CSV
|
||||
title,color,parent_category,classification,icon
|
||||
Food & Drink,#f97316,,expense,carrot
|
||||
CSV
|
||||
|
||||
import = @family.imports.create!(type: "CategoryImport", raw_file_str: csv, col_sep: ",")
|
||||
|
||||
error = assert_raises(ActiveRecord::RecordInvalid) { import.generate_rows_from_csv }
|
||||
assert_includes error.message, "Missing required columns: name"
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user