diff --git a/backend/src/utils/tenantFullExport.ts b/backend/src/utils/tenantFullExport.ts index dc9584e..36d5d85 100644 --- a/backend/src/utils/tenantFullExport.ts +++ b/backend/src/utils/tenantFullExport.ts @@ -296,6 +296,65 @@ const insertRows = async (client: any, table: string, rows: Record[ return inserted } +const insertIdentityRowsWithRemap = async ( + client: any, + table: string, + rows: Record[], + metadata: TableMetadata +) => { + if (!rows.length) return { count: 0, idMap: new Map() } + + let count = 0 + const idMap = new Map() + const availableColumns = new Set(metadata.columns.filter((column) => !metadata.generatedColumns.has(column))) + + for (const row of rows) { + const sourceId = Number(row.id) + if (!sourceId) continue + + const existing = await client.query(`select * from ${quoteIdent(table)} where ${quoteIdent("id")} = $1 limit 1`, [sourceId]) + const existingTenant = existing.rows[0]?.tenant ?? existing.rows[0]?.tenant_id + const rowTenant = row.tenant ?? row.tenant_id + const shouldPreserveId = existing.rows.length === 0 + const shouldReuseExisting = existing.rows.length > 0 && rowTenant && existingTenant === rowTenant + const rowForInsert = shouldPreserveId ? row : { ...row, id: undefined } + const rowColumns = Object + .keys(rowForInsert) + .filter((column) => availableColumns.has(column) && typeof rowForInsert[column] !== "undefined") + + if (shouldReuseExisting) { + idMap.set(sourceId, sourceId) + continue + } + + if (!rowColumns.length) continue + + const placeholders = rowColumns.map((_, index) => `$${index + 1}`).join(", ") + const values = rowColumns.map((column) => prepareColumnValue(rowForInsert[column], metadata.jsonColumns.has(column))) + const inserted = await client.query( + `insert into ${quoteIdent(table)} (${rowColumns.map(quoteIdent).join(", ")}) values (${placeholders}) returning ${quoteIdent("id")}`, + values + ) + const targetId = Number(inserted.rows[0]?.id) + if (targetId) { + idMap.set(sourceId, targetId) + } + count += 1 + } + + return { count, idMap } +} + +const remapTableColumn = (rows: Record[] = [], column: string, idMap: Map) => { + if (!idMap.size) return + + for (const row of rows) { + const currentValue = Number(row[column]) + const mappedValue = idMap.get(currentValue) + if (mappedValue) row[column] = mappedValue + } +} + const refreshSequences = async (client: any, columnsByTable: Map) => { for (const [table, metadata] of columnsByTable.entries()) { const { columns } = metadata @@ -350,7 +409,24 @@ export const importTenantFullExport = async ( const files = await restoreFiles(exportData) const importedTables: { table: string; rows: number }[] = [] + const bankaccountMetadata = columnsByTable.get("bankaccounts") + if (bankaccountMetadata) { + const result = await insertIdentityRowsWithRemap(client, "bankaccounts", exportData.tables.bankaccounts || [], bankaccountMetadata) + remapTableColumn(exportData.tables.bankstatements, "account", result.idMap) + importedTables.push({ table: "bankaccounts", rows: result.count }) + } + + const bankstatementMetadata = columnsByTable.get("bankstatements") + if (bankstatementMetadata) { + const result = await insertIdentityRowsWithRemap(client, "bankstatements", exportData.tables.bankstatements || [], bankstatementMetadata) + remapTableColumn(exportData.tables.statementallocations, "bs_id", result.idMap) + remapTableColumn(exportData.tables.historyitems, "bankstatement", result.idMap) + importedTables.push({ table: "bankstatements", rows: result.count }) + } + for (const table of tableNames) { + if (["bankaccounts", "bankstatements"].includes(table)) continue + const rows = exportData.tables[table] || [] const metadata = columnsByTable.get(table) if (!metadata) continue