mirror of
https://github.com/maybe-finance/maybe.git
synced 2025-07-25 08:09:38 +02:00
feat(import): add currency and number format support for CSV imports (#1819)
* feat(import): add currency and number format support for CSV imports * feat(import): add currency and format for mint and trade * fix(imports): remove currency field in favor of currency csv col * fix(imports): remove validate column from import model * test(import): add some tests for imports * test(import): add some tests for generate_rows_from_csv * fix: change method name for import model * fix: change before validation --------- Co-authored-by: danestves <danestves@users.noreply.github.com>
This commit is contained in:
parent
9e5f1574bc
commit
077694bbde
9 changed files with 307 additions and 13 deletions
|
@ -2,8 +2,17 @@ class Import < ApplicationRecord
|
|||
TYPES = %w[TransactionImport TradeImport AccountImport MintImport].freeze
|
||||
SIGNAGE_CONVENTIONS = %w[inflows_positive inflows_negative]
|
||||
|
||||
NUMBER_FORMATS = {
|
||||
"1,234.56" => { separator: ".", delimiter: "," }, # US/UK/Asia
|
||||
"1.234,56" => { separator: ",", delimiter: "." }, # Most of Europe
|
||||
"1 234,56" => { separator: ",", delimiter: " " }, # French/Scandinavian
|
||||
"1,234" => { separator: "", delimiter: "," } # Zero-decimal currencies like JPY
|
||||
}.freeze
|
||||
|
||||
belongs_to :family
|
||||
|
||||
before_validation :set_default_number_format
|
||||
|
||||
scope :ordered, -> { order(created_at: :desc) }
|
||||
|
||||
enum :status, {
|
||||
|
@ -18,6 +27,7 @@ class Import < ApplicationRecord
|
|||
validates :type, inclusion: { in: TYPES }
|
||||
validates :col_sep, inclusion: { in: [ ",", ";" ] }
|
||||
validates :signage_convention, inclusion: { in: SIGNAGE_CONVENTIONS }
|
||||
validates :number_format, presence: true, inclusion: { in: NUMBER_FORMATS.keys }
|
||||
|
||||
has_many :rows, dependent: :destroy
|
||||
has_many :mappings, dependent: :destroy
|
||||
|
@ -95,8 +105,8 @@ class Import < ApplicationRecord
|
|||
def generate_rows_from_csv
|
||||
rows.destroy_all
|
||||
|
||||
mapped_rows = csv_rows.map do |row|
|
||||
{
|
||||
csv_rows.each do |row|
|
||||
rows.create!(
|
||||
account: row[account_col_label].to_s,
|
||||
date: row[date_col_label].to_s,
|
||||
qty: sanitize_number(row[qty_col_label]).to_s,
|
||||
|
@ -109,10 +119,8 @@ class Import < ApplicationRecord
|
|||
tags: row[tags_col_label].to_s,
|
||||
entity_type: row[entity_type_col_label].to_s,
|
||||
notes: row[notes_col_label].to_s
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
rows.insert_all!(mapped_rows)
|
||||
end
|
||||
|
||||
def sync_mappings
|
||||
|
@ -177,6 +185,39 @@ class Import < ApplicationRecord
|
|||
|
||||
def sanitize_number(value)
|
||||
return "" if value.nil?
|
||||
value.gsub(/[^\d.\-]/, "")
|
||||
|
||||
format = NUMBER_FORMATS[number_format]
|
||||
return "" unless format
|
||||
|
||||
# First, normalize spaces and remove any characters that aren't numbers, delimiters, separators, or minus signs
|
||||
sanitized = value.to_s.strip
|
||||
|
||||
# Handle French/Scandinavian format specially
|
||||
if format[:delimiter] == " "
|
||||
sanitized = sanitized.gsub(/\s+/, "") # Remove all spaces first
|
||||
else
|
||||
sanitized = sanitized.gsub(/[^\d#{Regexp.escape(format[:delimiter])}#{Regexp.escape(format[:separator])}\-]/, "")
|
||||
|
||||
# Replace delimiter with empty string
|
||||
if format[:delimiter].present?
|
||||
sanitized = sanitized.gsub(format[:delimiter], "")
|
||||
end
|
||||
end
|
||||
|
||||
# Replace separator with period for proper float parsing
|
||||
if format[:separator].present?
|
||||
sanitized = sanitized.gsub(format[:separator], ".")
|
||||
end
|
||||
|
||||
# Return empty string if not a valid number
|
||||
unless sanitized =~ /\A-?\d+\.?\d*\z/
|
||||
return ""
|
||||
end
|
||||
|
||||
sanitized
|
||||
end
|
||||
|
||||
def set_default_number_format
|
||||
self.number_format ||= "1,234.56" # Default to US/UK format
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue