Skip to content

Commit 775e028

Browse files
committed
Add earliest import date setting
1 parent 0819fc3 commit 775e028

File tree

4 files changed

+63
-3
lines changed

4 files changed

+63
-3
lines changed

README.md

+11
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,17 @@ purposePatterns = []
8484

8585
The above configuration would ignore all transactions that have a comment containing the string `[actual-ignore]`.
8686

87+
### Earliest import date
88+
89+
Each budget can specify an earliest import date. This can be useful when starting to use the importer with an already existing budget in order to prevent duplicates from being imported. The importer will ignore any transactions from before the specified date.
90+
91+
```
92+
[[actualServers.budgets]]
93+
earliestImportDate = "2024-01-01" # Format is YYYY-MM-DD
94+
```
95+
96+
Note that the date is a string, not a TOML date.
97+
8798
## Bugs
8899

89100
If you notice any bugs or issues, please file an issue.

src/utils/Importer.ts

+29-2
Original file line numberDiff line numberDiff line change
@@ -137,11 +137,38 @@ class Importer {
137137
isDryRun?: boolean;
138138
}) {
139139
const fromDate = from ?? subMonths(new Date(), 1);
140+
const earliestImportDate = this.budgetConfig.earliestImportDate
141+
? new Date(this.budgetConfig.earliestImportDate)
142+
: null;
143+
144+
const importDate =
145+
earliestImportDate && earliestImportDate > fromDate
146+
? earliestImportDate
147+
: fromDate;
148+
149+
if (earliestImportDate && earliestImportDate > fromDate) {
150+
this.logger.warn(
151+
`Earliest import date is set to ${format(
152+
earliestImportDate,
153+
DATE_FORMAT
154+
)}. Using this date instead of ${format(fromDate, DATE_FORMAT)}.`
155+
);
156+
}
140157

141158
let monMonTransactions = await getTransactions({
142-
from: fromDate,
159+
from: importDate,
143160
});
144161

162+
if (monMonTransactions.length === 0) {
163+
this.logger.info(
164+
`No transactions found in MoneyMoney since ${format(
165+
importDate,
166+
DATE_FORMAT
167+
)}.`
168+
);
169+
return;
170+
}
171+
145172
if (!this.config.import.importUncheckedTransactions) {
146173
monMonTransactions = monMonTransactions.filter((t) => t.booked);
147174
}
@@ -176,7 +203,7 @@ class Importer {
176203
`Found ${
177204
monMonTransactions.length
178205
} total transactions in MoneyMoney since ${format(
179-
fromDate,
206+
importDate,
180207
DATE_FORMAT
181208
)}`
182209
);

src/utils/config.ts

+22-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { ZodIssueCode, z } from 'zod';
88
const budgetSchema = z
99
.object({
1010
syncId: z.string(),
11+
earliestImportDate: z.string().optional(),
1112
e2eEncryption: z.object({
1213
enabled: z.boolean(),
1314
password: z.string().optional(),
@@ -23,6 +24,17 @@ const budgetSchema = z
2324
});
2425
}
2526

27+
if (val.earliestImportDate) {
28+
const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
29+
if (!dateRegex.test(val.earliestImportDate)) {
30+
ctx.addIssue({
31+
code: ZodIssueCode.custom,
32+
message:
33+
'Invalid earliest import date format (required format is YYYY-MM-DD)',
34+
});
35+
}
36+
}
37+
2638
return val;
2739
});
2840

@@ -92,11 +104,20 @@ export const getConfig = async (argv: ArgumentsCamelCase) => {
92104
}
93105

94106
const configContent = await fs.readFile(configFile, 'utf-8');
95-
const configData = toml.parse(configContent);
96107

97108
try {
109+
const configData = toml.parse(configContent);
98110
return configSchema.parse(configData);
99111
} catch (e) {
112+
if (e instanceof Error && e.name === 'SyntaxError') {
113+
const line = 'line' in e ? e.line : -1;
114+
const column = 'column' in e ? e.column : -1;
115+
116+
throw new Error(
117+
`Failed to parse configuration file: ${e.message} (line ${line}, column ${column})`
118+
);
119+
}
120+
100121
throw new Error(
101122
`Invalid configuration file format. Run 'validate' to see errors.`
102123
);

src/utils/shared.ts

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ serverPassword = "<password>"
3333
# Budgets for the server, you can add multiple budgets
3434
[[actualServers.budgets]]
3535
syncId = "<syncId>" # Get this value from the Actual advanced settings
36+
# earliestImportDate = "2021-01-01" # Optional, only import transactions from this date
3637
3738
# E2E encryption for the budget, if enabled
3839
[actualServers.budgets.e2eEncryption]

0 commit comments

Comments
 (0)