Skip to content
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion app/controllers/api/v1/imports_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -242,14 +242,22 @@ def create_sure_import(family)
end

begin
@import.publish_later if @import.publishable? && params[:publish] == "true"
@import.publish_later if params[:publish] == "true"
rescue Import::MaxRowCountExceededError
render json: {
error: "max_row_count_exceeded",
message: "Import was uploaded but has too many rows to publish automatically.",
import_id: @import.id
}, status: :unprocessable_entity
return
rescue SureImport::PreflightError
render json: {
error: "preflight_failed",
message: "Import was uploaded but did not pass Sure NDJSON preflight.",
errors: @import.error.to_s.lines.map(&:strip).reject(&:blank?),
import_id: @import.id
}, status: :unprocessable_entity
return
rescue StandardError => e
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Rails.logger.error "Sure import publish failed for import #{@import.id}: #{e.message}"
restore_pending_sure_import_after_publish_failure
Expand All @@ -267,6 +275,8 @@ def create_sure_import(family)
def persist_sure_import!(family, content, filename, content_type)
import = nil
import = family.imports.create!(type: "SureImport")
import.merge_existing_taxonomy = params[:merge_existing_taxonomy]
import.save! if import.import_options_changed?
import.ndjson_file.attach(
io: StringIO.new(content),
filename: filename,
Expand Down
4 changes: 2 additions & 2 deletions app/controllers/import/uploads_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ def update_sure_import_upload
return
end

if uploaded.size > SureImport::MAX_NDJSON_SIZE
flash.now[:alert] = t("imports.create.file_too_large", max_size: SureImport::MAX_NDJSON_SIZE / 1.megabyte)
if uploaded.size > SureImport.max_ndjson_size
flash.now[:alert] = t("imports.create.file_too_large", max_size: SureImport.max_ndjson_size / 1.megabyte)
render :show, status: :unprocessable_entity
return
end
Expand Down
6 changes: 4 additions & 2 deletions app/controllers/imports_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ def publish
redirect_to import_path(@import), notice: t(".started")
rescue Import::MaxRowCountExceededError
redirect_back_or_to import_path(@import), alert: t(".max_rows_exceeded", max: @import.max_row_count)
rescue SureImport::PreflightError => e
redirect_to import_path(@import), alert: e.message
end

def index
Expand Down Expand Up @@ -213,8 +215,8 @@ def sure_import_request?
end

def create_sure_import(file)
if file.size > SureImport::MAX_NDJSON_SIZE
redirect_to new_import_path, alert: t("imports.create.file_too_large", max_size: SureImport::MAX_NDJSON_SIZE / 1.megabyte)
if file.size > SureImport.max_ndjson_size
redirect_to new_import_path, alert: t("imports.create.file_too_large", max_size: SureImport.max_ndjson_size / 1.megabyte)
return
end

Expand Down
87 changes: 68 additions & 19 deletions app/models/family/data_exporter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -300,27 +300,40 @@ def generate_ndjson
}.to_json
end

# Export transactions with full data (exclude split parents, export children instead)
exportable_transactions.includes(:category, :merchant, :tags, entry: :account).find_each do |transaction|
# Export transactions with full data. NDJSON keeps split parents with explicit
# split lines so a later import can recreate Sure-native split structure.
ndjson_exportable_transactions.includes(
:category,
:merchant,
:tags,
entry: [
:account,
{ child_entries: { entryable: :tags } }
]
).find_each do |transaction|
transaction_data = {
id: transaction.id,
entry_id: transaction.entry.id,
account_id: transaction.entry.account_id,
date: transaction.entry.date,
amount: transaction.entry.amount,
currency: transaction.entry.currency,
name: transaction.entry.name,
notes: transaction.entry.notes,
excluded: transaction.entry.excluded,
category_id: transaction.category_id,
merchant_id: transaction.merchant_id,
tag_ids: transaction.tag_ids,
kind: transaction.kind,
created_at: transaction.created_at,
updated_at: transaction.updated_at
}
split_lines = serialize_split_lines_for_export(transaction.entry)
transaction_data[:split_lines] = split_lines if split_lines.any?

lines << {
type: "Transaction",
data: {
id: transaction.id,
entry_id: transaction.entry.id,
account_id: transaction.entry.account_id,
date: transaction.entry.date,
amount: transaction.entry.amount,
currency: transaction.entry.currency,
name: transaction.entry.name,
notes: transaction.entry.notes,
excluded: transaction.entry.excluded,
category_id: transaction.category_id,
merchant_id: transaction.merchant_id,
tag_ids: transaction.tag_ids,
kind: transaction.kind,
created_at: transaction.created_at,
updated_at: transaction.updated_at
}
data: transaction_data
}.to_json
end

Expand Down Expand Up @@ -456,6 +469,42 @@ def exportable_transactions
@family.transactions.merge(Entry.excluding_split_parents)
end

def ndjson_exportable_transactions
@family.transactions.joins(:entry).where(entries: { parent_entry_id: nil })
end

def serialize_split_lines_for_export(parent_entry)
child_entries = split_child_entries_for_export(parent_entry)
return [] if child_entries.empty?

child_entries.map do |child_entry|
transaction = child_entry.entryable
{
id: transaction.id,
entry_id: child_entry.id,
amount: child_entry.amount,
currency: child_entry.currency,
name: child_entry.name,
notes: child_entry.notes,
excluded: child_entry.excluded,
category_id: transaction.category_id,
merchant_id: transaction.merchant_id,
tag_ids: transaction.tag_ids,
kind: transaction.kind,
created_at: transaction.created_at,
updated_at: transaction.updated_at
}
end
end

def split_child_entries_for_export(parent_entry)
if parent_entry.association(:child_entries).loaded?
parent_entry.child_entries.sort_by { |entry| [ entry.created_at, entry.id ] }
else
parent_entry.child_entries.order(:created_at, :id).to_a
end
end

def family_transaction_ids
@family_transaction_ids ||= exportable_transactions.select(:id)
end
Expand Down
Loading
Loading