Skip to content

Commit 6b63be5

Browse files
committed
wip
1 parent a695b19 commit 6b63be5

20 files changed

Lines changed: 518 additions & 608 deletions

apps/dashboard/src/actions/transactions/manual-sync-transactions-action.ts

Lines changed: 0 additions & 29 deletions
This file was deleted.

apps/dashboard/src/actions/transactions/reconnect-connection-action.ts

Lines changed: 0 additions & 33 deletions
This file was deleted.

apps/dashboard/src/components/bank-connections.tsx

Lines changed: 52 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
"use client";
22

3-
import { manualSyncTransactionsAction } from "@/actions/transactions/manual-sync-transactions-action";
4-
import { reconnectConnectionAction } from "@/actions/transactions/reconnect-connection-action";
53
import { useSyncStatus } from "@/hooks/use-sync-status";
64
import { useTRPC } from "@/trpc/client";
75
import { connectionStatus } from "@/utils/connection-status";
@@ -22,7 +20,6 @@ import {
2220
import { useToast } from "@midday/ui/use-toast";
2321
import { useQueryClient, useSuspenseQuery } from "@tanstack/react-query";
2422
import { differenceInDays, formatDistanceToNow } from "date-fns";
25-
import { useAction } from "next-safe-action/hooks";
2623
import { parseAsString, useQueryStates } from "nuqs";
2724
import { useEffect, useState } from "react";
2825
import { BankAccount } from "./bank-account";
@@ -148,47 +145,47 @@ export function BankConnection({ connection }: { connection: BankConnection }) {
148145
id: parseAsString,
149146
});
150147

151-
const manualSyncTransactions = useAction(manualSyncTransactionsAction, {
152-
onExecute: () => setSyncing(true),
153-
onSuccess: ({ data }) => {
154-
if (data) {
155-
setRunId(data.id);
156-
setAccessToken(data.publicAccessToken);
157-
}
158-
},
159-
onError: () => {
160-
setSyncing(false);
161-
setRunId(undefined);
162-
setStatus("FAILED");
163-
164-
toast({
165-
duration: 3500,
166-
variant: "error",
167-
title: "Something went wrong please try again.",
168-
});
169-
},
170-
});
171-
172-
const reconnectConnection = useAction(reconnectConnectionAction, {
173-
onExecute: () => setSyncing(true),
174-
onSuccess: ({ data }) => {
175-
if (data) {
176-
setRunId(data.id);
177-
setAccessToken(data.publicAccessToken);
178-
}
179-
},
180-
onError: () => {
181-
setSyncing(false);
182-
setRunId(undefined);
183-
setStatus("FAILED");
184-
185-
toast({
186-
duration: 3500,
187-
variant: "error",
188-
title: "Something went wrong please try again.",
189-
});
190-
},
191-
});
148+
// const manualSyncTransactions = useAction(manualSyncTransactionsAction, {
149+
// onExecute: () => setSyncing(true),
150+
// onSuccess: ({ data }) => {
151+
// if (data) {
152+
// setRunId(data.id);
153+
// setAccessToken(data.publicAccessToken);
154+
// }
155+
// },
156+
// onError: () => {
157+
// setSyncing(false);
158+
// setRunId(undefined);
159+
// setStatus("FAILED");
160+
161+
// toast({
162+
// duration: 3500,
163+
// variant: "error",
164+
// title: "Something went wrong please try again.",
165+
// });
166+
// },
167+
// });
168+
169+
// const reconnectConnection = useAction(reconnectConnectionAction, {
170+
// onExecute: () => setSyncing(true),
171+
// onSuccess: ({ data }) => {
172+
// if (data) {
173+
// setRunId(data.id);
174+
// setAccessToken(data.publicAccessToken);
175+
// }
176+
// },
177+
// onError: () => {
178+
// setSyncing(false);
179+
// setRunId(undefined);
180+
// setStatus("FAILED");
181+
182+
// toast({
183+
// duration: 3500,
184+
// variant: "error",
185+
// title: "Something went wrong please try again.",
186+
// });
187+
// },
188+
// });
192189

193190
useEffect(() => {
194191
if (isSyncing) {
@@ -253,21 +250,21 @@ export function BankConnection({ connection }: { connection: BankConnection }) {
253250
// NOTE: GoCardLess reconnect flow (redirect from API route)
254251
useEffect(() => {
255252
if (params.step === "reconnect" && params.id) {
256-
reconnectConnection.execute({
257-
connectionId: params.id,
258-
provider: connection.provider as
259-
| "gocardless"
260-
| "plaid"
261-
| "teller"
262-
| "enablebanking",
263-
});
253+
// reconnectConnection.execute({
254+
// connectionId: params.id,
255+
// provider: connection.provider as
256+
// | "gocardless"
257+
// | "plaid"
258+
// | "teller"
259+
// | "enablebanking",
260+
// });
264261
}
265262
}, [params]);
266263

267264
const handleManualSync = () => {
268-
manualSyncTransactions.execute({
269-
connectionId: connection.id,
270-
});
265+
// manualSyncTransactions.execute({
266+
// connectionId: connection.id,
267+
// });
271268
};
272269

273270
return (

apps/worker/src/core/job.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { Database } from "@midday/db/client";
22
import { logger } from "@worker/monitoring/logger";
3-
import type { Job } from "bullmq";
3+
import type { Job, JobNode } from "bullmq";
44
import { Queue } from "bullmq";
55
import { FlowProducer } from "bullmq";
66
import type { z } from "zod";
@@ -233,7 +233,7 @@ class SimpleJob<T = any> {
233233
data: T;
234234
options?: Record<string, any>;
235235
children?: FlowJobDefinition[];
236-
}) {
236+
}): Promise<JobNode> {
237237
const flowProducer = registry.getFlowProducer();
238238
const validated = this.validate(flowDef.data);
239239

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { updateBaseCurrencyJob } from "./update-base-currency";
2+
export { updateAccountBaseCurrencyJob } from "./update-account-base-currency";
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
import {
2+
bulkUpdateTransactionsBaseCurrency,
3+
getExchangeRate,
4+
getTransactionsByAccount,
5+
updateAccountBaseCurrency,
6+
} from "@midday/db/queries";
7+
import {
8+
calculateAccountBaseBalance,
9+
calculateTransactionBaseAmount,
10+
} from "@midday/db/utils/currency";
11+
import { job } from "@worker/core/job";
12+
import { z } from "zod";
13+
14+
const updateAccountBaseCurrencySchema = z.object({
15+
accountId: z.string().uuid(),
16+
teamId: z.string().uuid(),
17+
currency: z.string().min(3).max(3),
18+
balance: z.number(),
19+
baseCurrency: z.string().min(3).max(3),
20+
});
21+
22+
export const updateAccountBaseCurrencyJob = job(
23+
"update-account-base-currency",
24+
updateAccountBaseCurrencySchema,
25+
{
26+
queue: "system",
27+
priority: 3,
28+
attempts: 3,
29+
},
30+
async (data, { db, logger }) => {
31+
const { accountId, teamId, currency, balance, baseCurrency } = data;
32+
33+
logger.info("Starting account base currency update", {
34+
accountId,
35+
teamId,
36+
currency,
37+
baseCurrency,
38+
});
39+
40+
// If currencies are the same, no conversion needed
41+
if (currency === baseCurrency) {
42+
logger.info("Currency already matches base currency", {
43+
accountId,
44+
currency,
45+
baseCurrency,
46+
});
47+
48+
// Still update the database to ensure consistency
49+
await updateAccountBaseCurrency(db, {
50+
accountId,
51+
teamId,
52+
baseBalance: balance,
53+
baseCurrency,
54+
});
55+
56+
return {
57+
converted: false,
58+
rate: 1,
59+
transactionsUpdated: 0,
60+
};
61+
}
62+
63+
// Get exchange rate
64+
const exchangeRateData = await getExchangeRate(db, {
65+
base: currency,
66+
target: baseCurrency,
67+
});
68+
69+
if (!exchangeRateData) {
70+
logger.error("No exchange rate found", {
71+
accountId,
72+
fromCurrency: currency,
73+
toCurrency: baseCurrency,
74+
});
75+
throw new Error(
76+
`No exchange rate found for ${currency} to ${baseCurrency}`,
77+
);
78+
}
79+
80+
const rate = exchangeRateData.rate ?? 1;
81+
logger.info("Found exchange rate", {
82+
accountId,
83+
fromCurrency: currency,
84+
toCurrency: baseCurrency,
85+
rate,
86+
});
87+
88+
// Calculate base balance
89+
const baseBalance = calculateAccountBaseBalance({
90+
balance,
91+
currency,
92+
baseCurrency,
93+
exchangeRate: rate,
94+
});
95+
96+
// Update account base balance and currency
97+
await updateAccountBaseCurrency(db, {
98+
accountId,
99+
teamId,
100+
baseBalance,
101+
baseCurrency,
102+
});
103+
104+
logger.info("Updated account base currency", {
105+
accountId,
106+
baseBalance,
107+
baseCurrency,
108+
});
109+
110+
// Get all transactions for this account
111+
const transactions = await getTransactionsByAccount(db, {
112+
accountId,
113+
teamId,
114+
});
115+
116+
if (transactions.length === 0) {
117+
logger.info("No transactions found for account", { accountId });
118+
return {
119+
converted: true,
120+
rate,
121+
transactionsUpdated: 0,
122+
};
123+
}
124+
125+
logger.info("Found transactions to update", {
126+
accountId,
127+
transactionCount: transactions.length,
128+
});
129+
130+
// Prepare transaction updates
131+
const transactionUpdates = transactions.map((transaction) => ({
132+
id: transaction.id,
133+
baseAmount: calculateTransactionBaseAmount({
134+
amount: transaction.amount ?? 0,
135+
currency: transaction.currency,
136+
baseCurrency,
137+
exchangeRate: rate,
138+
}),
139+
baseCurrency,
140+
}));
141+
142+
// Bulk update transactions
143+
const updatedTransactions = await bulkUpdateTransactionsBaseCurrency(db, {
144+
transactionUpdates,
145+
teamId,
146+
});
147+
148+
logger.info("Account base currency update completed", {
149+
accountId,
150+
transactionsUpdated: updatedTransactions.length,
151+
baseCurrency,
152+
rate,
153+
});
154+
155+
return {
156+
converted: true,
157+
rate,
158+
transactionsUpdated: updatedTransactions.length,
159+
};
160+
},
161+
);

0 commit comments

Comments
 (0)