Skip to content

Commit 1b8e706

Browse files
refactor(OUT-3896): use findFirst for expense lookup, simplify legacy match
- getExpenseByCopilotPaymentId now uses the relational query API (db.query.syncedPayments.findFirst) instead of a manual select + slice - Register syncedPayments in the schema barrel so db.query knows it - findLegacyExpenseByInvoice adopts the first match (only one fee exists per invoice, so multiple matches are duplicates of the same expense); warn when duplicates are present so they can be cleaned up Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent ec0ae86 commit 1b8e706

3 files changed

Lines changed: 16 additions & 19 deletions

File tree

src/db/schema/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { settings } from './settings.schema'
22
import { syncedContacts } from './syncedContacts.schema'
33
import { syncedInvoices } from './syncedInvoices.schema'
44
import { syncedItems } from './syncedItems.schema'
5+
import { syncedPayments } from './syncedPayments.schema'
56
import { xeroConnectionStatus } from './xeroConnectionStatus.schema'
67
import { xeroConnections } from './xeroConnections.schema'
78

@@ -12,4 +13,5 @@ export const schema = {
1213
syncedContacts,
1314
syncedInvoices,
1415
syncedItems,
16+
syncedPayments,
1517
}

src/features/invoice-sync/lib/SyncedPayments.service.ts

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -42,18 +42,15 @@ class SyncedPaymentsService extends AuthenticatedXeroService {
4242
}
4343

4444
async getExpenseByCopilotPaymentId(copilotPaymentId: string) {
45-
const [result] = await this.db
46-
.select()
47-
.from(syncedPayments)
48-
.where(
45+
return await this.db.query.syncedPayments.findFirst({
46+
where: (t, { and, eq }) =>
4947
and(
50-
eq(syncedPayments.portalId, this.user.portalId),
51-
eq(syncedPayments.tenantId, this.connection.tenantId),
52-
eq(syncedPayments.copilotPaymentId, copilotPaymentId),
53-
eq(syncedPayments.type, PaymentUserType.EXPENSE),
48+
eq(t.portalId, this.user.portalId),
49+
eq(t.tenantId, this.connection.tenantId),
50+
eq(t.copilotPaymentId, copilotPaymentId),
51+
eq(t.type, PaymentUserType.EXPENSE),
5452
),
55-
)
56-
return result
53+
})
5754
}
5855

5956
async createPaymentRecord(

src/lib/xero/XeroAPI.ts

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -353,15 +353,13 @@ class XeroAPI {
353353
typeof tx.total === 'number' &&
354354
Math.round(tx.total * 100) === amountInCents,
355355
)
356-
if (candidateTxns.length > 1) {
357-
// Ambiguous: same-amount expenses on one invoice. Skip adopting and
358-
// flag for manual cleanup of the legacy duplicates.
359-
logger.warn(
360-
'XeroAPI#findLegacyExpenseByInvoice :: Multiple legacy expenses match, not adopting',
361-
{ tenantId, invoiceReference, count: candidateTxns.length },
362-
)
363-
return undefined
364-
}
356+
if (candidateTxns.length > 1)
357+
logger.warn('XeroAPI#findLegacyExpenseByInvoice :: Multiple legacy expenses match', {
358+
tenantId,
359+
invoiceReference,
360+
count: candidateTxns.length,
361+
})
362+
365363
return candidateTxns[0]
366364
}
367365

0 commit comments

Comments
 (0)