QIF imports: Add date format auto-detection and manual override (#1368)

* feat: improve QIF import date format selection

- Added a reusable date format auto-detection method.

- Show a live preview of the first parsed date that updates client-side
  as the user changes the dropdown selection, via a new
  qif-date-format Stimulus controller.

- Show an error alert and disable the submit button when no supported
  date format can parse the file's dates.

* A few polishing fixes:
- Missing return on redirects
Stale REASONABLE_DATE_RANGE constant.
- Replaced the frozen constant with a class method
Bare inline rescue — Replaced Date.strptime(s, fmt) rescue nil with an explicit begin/rescue catching.
- save!(validate: false) in controller — Changed to update_column(:column_mappings, ...) in qif_category_selections_controller.rb:22, matching the pattern used in detect_and_set_qif_date_format!.
- Unescaped JSON in HTML attribute — Replaced the raw <div> with tag.div ... do block in show.html.erb:16, letting Rails properly escape the data attribute value.

* fix: address review feedback for QIF date format feature

- Add missing `return` after redirect for non-QIF imports
- Pass date_format to parse_opening_balance in will_adjust_opening_anchor?
- Return empty array when no usable date sample exists for format preview
- Add sr-only label to date format select for accessibility
- Consolidate duplicate try_parse_date/parse_qif_date into single method
- Remove misleading ambiguity scoring comment from detect_date_format
- Skip redundant sync_mappings when date format already triggered a sync

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Use %{product_name} interpolation in locale strings

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Serge L
2026-04-05 03:27:24 -04:00
committed by GitHub
parent a76aa340d5
commit 78b334277c
13 changed files with 498 additions and 55 deletions

View File

@@ -5,7 +5,8 @@ class Import::ConfigurationsController < ApplicationController
def show
# PDF imports are auto-configured from AI extraction, skip to clean step
redirect_to import_clean_path(@import) if @import.is_a?(PdfImport)
redirect_to import_clean_path(@import) and return if @import.is_a?(PdfImport)
redirect_to import_qif_category_selection_path(@import) and return if @import.is_a?(QifImport)
end
def update

View File

@@ -4,15 +4,28 @@ class Import::QifCategorySelectionsController < ApplicationController
before_action :set_import
def show
@categories = @import.row_categories
@tags = @import.row_tags
@category_counts = @import.rows.group(:category).count.reject { |k, _| k.blank? }
@tag_counts = compute_tag_counts
valid_formats = @import.valid_date_formats_with_preview
@date_formats = valid_formats.map { |f| [ f[:label], f[:format] ] }
@date_previews = valid_formats.each_with_object({}) { |f, h| h[f[:format]] = f[:preview] }
@categories = @import.row_categories
@tags = @import.row_tags
@category_counts = @import.rows.group(:category).count.reject { |k, _| k.blank? }
@tag_counts = compute_tag_counts
@split_categories = @import.split_categories
@has_split_transactions = @import.has_split_transactions?
end
def update
# If the user changed the date format, re-generate rows with the new format.
format_changed = false
if selection_params[:date_format].present? && selection_params[:date_format] != @import.qif_date_format
format_changed = true
@import.qif_date_format = selection_params[:date_format]
@import.update_column(:column_mappings, @import.column_mappings)
@import.generate_rows_from_csv
@import.sync_mappings
end
all_categories = @import.row_categories
all_tags = @import.row_tags
@@ -38,7 +51,7 @@ class Import::QifCategorySelectionsController < ApplicationController
end
end
@import.sync_mappings
@import.sync_mappings unless format_changed
end
redirect_to import_clean_path(@import), notice: "Categories and tags saved."
@@ -50,7 +63,7 @@ class Import::QifCategorySelectionsController < ApplicationController
@import = Current.family.imports.find(params[:import_id])
unless @import.is_a?(QifImport)
redirect_to imports_path
redirect_to imports_path and return
end
end
@@ -63,6 +76,6 @@ class Import::QifCategorySelectionsController < ApplicationController
end
def selection_params
params.permit(categories: [], tags: [])
params.permit(:date_format, categories: [], tags: [])
end
end