| import type { Database } from "@midday/db/client"; |
| import type { MatchResult } from "@midday/db/queries"; |
| import { |
| getInboxById, |
| getTransactionById, |
| hasSuggestion, |
| } from "@midday/db/queries"; |
| import { createLoggerWithContext } from "@midday/logger"; |
| import { Notifications } from "@midday/notifications"; |
| import { sendToProviders } from "./provider-notifications"; |
|
|
| const logger = createLoggerWithContext("inbox-matching-notifications"); |
|
|
| export async function triggerMatchingNotification(params: { |
| db: Database; |
| teamId: string; |
| inboxId: string; |
| result: |
| | { |
| action: "auto_matched"; |
| suggestion: MatchResult; |
| } |
| | { |
| action: "suggestion_created"; |
| suggestion: MatchResult; |
| }; |
| }): Promise<void> { |
| const { db, teamId, inboxId, result } = params; |
|
|
| |
| if (!hasSuggestion(result)) { |
| return; |
| } |
|
|
| try { |
| |
| const [inboxItem, transactionItem] = await Promise.all([ |
| getInboxById(db, { id: inboxId, teamId }), |
| getTransactionById(db, { |
| id: result.suggestion.transactionId, |
| teamId, |
| }), |
| ]); |
|
|
| if (!inboxItem || !transactionItem) { |
| logger.warn("Missing data for notification", { |
| hasInbox: !!inboxItem, |
| hasTransaction: !!transactionItem, |
| }); |
| return; |
| } |
|
|
| const documentName = |
| inboxItem.displayName || inboxItem.fileName || "Document"; |
| const transactionName = transactionItem.name || "Transaction"; |
|
|
| |
| const isCrossCurrency = Boolean( |
| inboxItem.currency && |
| transactionItem.currency && |
| inboxItem.currency !== transactionItem.currency, |
| ); |
|
|
| const notifications = new Notifications(db); |
|
|
| |
| const matchPayload = { |
| inboxId, |
| transactionId: result.suggestion.transactionId, |
| documentName, |
| documentAmount: inboxItem.amount || 0, |
| documentCurrency: inboxItem.currency || "USD", |
| transactionAmount: transactionItem.amount || 0, |
| transactionCurrency: transactionItem.currency || "USD", |
| transactionName, |
| confidenceScore: result.suggestion.confidenceScore, |
| isCrossCurrency, |
| }; |
|
|
| |
| const inboxMeta = inboxItem.meta as |
| | { |
| source?: string; |
| sourceMetadata?: { |
| channelId?: string; |
| threadTs?: string; |
| messageTs?: string; |
| phoneNumber?: string; |
| }; |
| } |
| | undefined; |
|
|
| if (result.action === "auto_matched") { |
| |
| await notifications.create("inbox_auto_matched", teamId, { |
| ...matchPayload, |
| matchType: "auto_matched", |
| }); |
|
|
| |
| await sendToProviders( |
| db, |
| teamId, |
| "match", |
| { |
| ...matchPayload, |
| matchType: "auto_matched" as const, |
| }, |
| { inboxMeta }, |
| ); |
| } else if (result.action === "suggestion_created") { |
| const matchType = |
| result.suggestion.matchType === "high_confidence" |
| ? "high_confidence" |
| : "suggested"; |
|
|
| |
| await notifications.create("inbox_needs_review", teamId, { |
| ...matchPayload, |
| matchType, |
| }); |
|
|
| |
| await sendToProviders( |
| db, |
| teamId, |
| "match", |
| { |
| ...matchPayload, |
| matchType: matchType as "high_confidence" | "suggested", |
| }, |
| { inboxMeta }, |
| ); |
| } |
| } catch (error) { |
| logger.error("Failed to trigger matching notification", { |
| teamId, |
| inboxId, |
| error: error instanceof Error ? error.message : "Unknown error", |
| }); |
| |
| } |
| } |
|
|