1
0
Fork 0
mirror of https://github.com/maybe-finance/maybe.git synced 2025-08-02 20:15:22 +02:00

Account-level import configuration templates (#1946)
Some checks are pending
Publish Docker image / ci (push) Waiting to run
Publish Docker image / Build docker image (push) Blocked by required conditions

* Account-level import configuration templates

* Default import to family's preferred date format
This commit is contained in:
Zach Gollwitzer 2025-03-04 13:10:01 -05:00 committed by GitHub
parent 5b2fa3d707
commit 0544089710
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 108 additions and 43 deletions

View file

@ -146,8 +146,20 @@ class Import < ApplicationRecord
end
def sync_mappings
mapping_steps.each do |mapping|
mapping.sync(self)
transaction do
mapping_steps.each do |mapping_class|
mappables_by_key = mapping_class.mappables_by_key(self)
updated_mappings = mappables_by_key.map do |key, mappable|
mapping = mappings.find_or_initialize_by(key: key, import: self, type: mapping_class.name)
mapping.mappable = mappable
mapping.create_when_empty = key.present? && mappable.nil?
mapping
end
updated_mappings.each { |m| m.save(validate: false) }
mapping_class.where.not(id: updated_mappings.map(&:id)).destroy_all
end
end
end
@ -183,6 +195,28 @@ class Import < ApplicationRecord
family.accounts.empty? && has_unassigned_account?
end
# Used to optionally pre-fill the configuration for the current import
def suggested_template
family.imports
.complete
.where(account: account, type: type)
.order(created_at: :desc)
.first
end
def apply_template!(import_template)
update!(
import_template.attributes.slice(
"date_col_label", "amount_col_label", "name_col_label",
"category_col_label", "tags_col_label", "account_col_label",
"qty_col_label", "ticker_col_label", "price_col_label",
"entity_type_col_label", "notes_col_label", "currency_col_label",
"date_format", "signage_convention", "number_format",
"exchange_operating_mic_col_label"
)
)
end
private
def import!
# no-op, subclasses can implement for customization of algorithm

View file

@ -2,8 +2,11 @@ class Import::AccountMapping < Import::Mapping
validates :mappable, presence: true, if: :requires_mapping?
class << self
def mapping_values(import)
import.rows.map(&:account).uniq
def mappables_by_key(import)
unique_values = import.rows.map(&:account).uniq
accounts = import.family.accounts.where(name: unique_values).index_by(&:name)
unique_values.index_with { |value| accounts[value] }
end
end

View file

@ -2,8 +2,8 @@ class Import::AccountTypeMapping < Import::Mapping
validates :value, presence: true
class << self
def mapping_values(import)
import.rows.map(&:entity_type).uniq
def mappables_by_key(import)
import.rows.map(&:entity_type).uniq.index_with { nil }
end
end

View file

@ -1,7 +1,10 @@
class Import::CategoryMapping < Import::Mapping
class << self
def mapping_values(import)
import.rows.map(&:category).uniq
def mappables_by_key(import)
unique_values = import.rows.map(&:category).uniq
categories = import.family.categories.where(name: unique_values).index_by(&:name)
unique_values.index_with { |value| categories[value] }
end
end

View file

@ -18,19 +18,8 @@ class Import::Mapping < ApplicationRecord
find_by(key: key)&.mappable
end
def sync(import)
unique_values = mapping_values(import).uniq
unique_values.each do |value|
mapping = find_or_initialize_by(key: value, import: import, create_when_empty: value.present?)
mapping.save(validate: false) if mapping.new_record?
end
where(import: import).where.not(key: unique_values).destroy_all
end
def mapping_values(import)
raise NotImplementedError, "Subclass must implement mapping_values"
def mappables_by_key(import)
raise NotImplementedError, "Subclass must implement mappables_by_key"
end
end

View file

@ -30,11 +30,10 @@ class Import::Row < ApplicationRecord
end
end
def sync_mappings
Import::CategoryMapping.sync(import) if import.column_keys.include?(:category)
Import::TagMapping.sync(import) if import.column_keys.include?(:tags)
Import::AccountMapping.sync(import) if import.column_keys.include?(:account)
Import::AccountTypeMapping.sync(import) if import.column_keys.include?(:entity_type)
def update_and_sync(params)
assign_attributes(params)
save!(validate: false)
import.sync_mappings
end
private

View file

@ -1,7 +1,11 @@
class Import::TagMapping < Import::Mapping
class << self
def mapping_values(import)
import.rows.map(&:tags_list).flatten.uniq
def mappables_by_key(import)
unique_values = import.rows.map(&:tags_list).flatten.uniq
tags = import.family.tags.where(name: unique_values).index_by(&:name)
unique_values.index_with { |value| tags[value] }
end
end