@emilfrom/n8n-nodes-actual-budget 1.0.2 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +47 -27
- package/dist/nodes/ActualBudget/ActualBudget.node.d.ts +0 -2
- package/dist/nodes/ActualBudget/ActualBudget.node.js +167 -230
- package/dist/nodes/ActualBudget/ActualBudget.node.js.map +1 -1
- package/dist/nodes/ActualBudget/ActualBudget.node.json +1 -1
- package/dist/nodes/ActualBudget/transport.js +43 -1
- package/dist/nodes/ActualBudget/transport.js.map +1 -1
- package/dist/package.json +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
# @emilfrom/n8n-nodes-actual-budget
|
|
2
2
|
|
|
3
|
-
This is an n8n community node for
|
|
4
|
-
[
|
|
5
|
-
[actual-http-api](https://github.com/jhonderson/actual-http-api).
|
|
3
|
+
This is an n8n community node for working with [Actual Budget](https://actualbudget.org/)
|
|
4
|
+
through [actual-http-api](https://github.com/jhonderson/actual-http-api).
|
|
6
5
|
|
|
7
6
|
[n8n](https://n8n.io/) is a workflow automation platform.
|
|
8
7
|
|
|
@@ -51,39 +50,60 @@ Create an `Actual HTTP API` credential in n8n with:
|
|
|
51
50
|
|
|
52
51
|
The credential test calls `GET /budgets/{budgetSyncId}/accounts`.
|
|
53
52
|
|
|
54
|
-
##
|
|
53
|
+
## Node version 2 (breaking change)
|
|
55
54
|
|
|
56
|
-
|
|
55
|
+
Version **2** of the **Actual Budget** node removes the old **Import** operation (which used
|
|
56
|
+
`POST .../transactions/import` and `imported_id` dedupe). Workflows must use **Transaction: Create**
|
|
57
|
+
and amounts as **integer minor units**, matching the [actual-http-api](https://github.com/jhonderson/actual-http-api)
|
|
58
|
+
contract (`value * 100` for most currencies).
|
|
57
59
|
|
|
58
|
-
|
|
59
|
-
endpoint. This operation is idempotent when the same transaction UUID is reused,
|
|
60
|
-
because the UUID is sent as `imported_id`.
|
|
60
|
+
## Actions
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
Pick an **Action** on the node:
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
- `Transaction Type`: `Expense` or `Income`
|
|
66
|
-
- `Amount`: Positive decimal amount, for example `12.34`
|
|
67
|
-
- `Date`: Transaction date or date-time. Actual stores the date only.
|
|
68
|
-
- `Transaction UUID`: Stable unique identifier for dedupe/upsert behavior
|
|
69
|
-
- `Payee`: Existing payee from the dropdown or a payee name
|
|
70
|
-
- `Category`: Actual category ID, selectable from actual-http-api
|
|
64
|
+
### Transaction: Create
|
|
71
65
|
|
|
72
|
-
|
|
66
|
+
Creates one transaction per input item using the official single-create endpoint:
|
|
73
67
|
|
|
74
|
-
|
|
75
|
-
- `Cleared`
|
|
76
|
-
- `Default Cleared`
|
|
77
|
-
- `Dry Run`
|
|
78
|
-
- `Reimport Deleted`
|
|
68
|
+
`POST /budgets/{budgetSyncId}/accounts/{accountId}/transactions`
|
|
79
69
|
|
|
80
|
-
|
|
70
|
+
Request body matches the API: `learnCategories`, `runTransfers`, and `transaction` with
|
|
71
|
+
`account`, `category`, `amount`, `payee_name`, `date`, `cleared` only.
|
|
81
72
|
|
|
82
|
-
|
|
83
|
-
|
|
73
|
+
- **Account** / **Category**: selectable lists from `GET /accounts` and `GET /categories`
|
|
74
|
+
- **Amount**: integer minor units (for example `-7374` for `-73.74` in a two-decimal currency)
|
|
75
|
+
- **Date**: stored as `YYYY-MM-DD`
|
|
76
|
+
- **Payee Name**: plain text (for example `Amazon`)
|
|
77
|
+
- **Cleared**, **Learn Categories**, **Run Transfers**: booleans
|
|
84
78
|
|
|
85
|
-
|
|
86
|
-
|
|
79
|
+
### Budget: Get Month
|
|
80
|
+
|
|
81
|
+
`GET /budgets/{budgetSyncId}/months/{month}` — pass **Month** as `YYYY-MM`. Output includes the API
|
|
82
|
+
`data` payload (totals, category groups, etc.).
|
|
83
|
+
|
|
84
|
+
### Account: List
|
|
85
|
+
|
|
86
|
+
`GET /budgets/{budgetSyncId}/accounts` — returns the API response (including `data`).
|
|
87
|
+
|
|
88
|
+
### Category: List
|
|
89
|
+
|
|
90
|
+
`GET /budgets/{budgetSyncId}/categories` — returns the API response (including `data`).
|
|
91
|
+
|
|
92
|
+
## Debugging (logs)
|
|
93
|
+
|
|
94
|
+
This node uses n8n’s **`LoggerProxy`** from `n8n-workflow` (same approach as [n8n’s logging docs](https://docs.n8n.io/hosting/logging-monitoring/logging/)): each outbound call logs at **info** (method, resource, URL with the sync id redacted), extra hints at **debug**, and **warn** if the request fails (HTTP status when present).
|
|
95
|
+
|
|
96
|
+
**Where to read logs:** server stdout/stderr (Docker/Kubernetes logs) or the log file if you enable file output — not the workflow execution panel in the editor.
|
|
97
|
+
|
|
98
|
+
**Environment variables** (optional; defaults are usually enough until you need more detail):
|
|
99
|
+
|
|
100
|
+
| Variable | Typical value | Purpose |
|
|
101
|
+
|----------|-----------------|--------|
|
|
102
|
+
| `N8N_LOG_LEVEL` | `info` (default) | Use **`debug`** to include `Logger.debug` lines (credential-shape hints). |
|
|
103
|
+
| `N8N_LOG_OUTPUT` | `console` (default) | Use `console,file` and set `N8N_LOG_FILE_LOCATION` if you want a persistent log file. |
|
|
104
|
+
| `N8N_LOG_FILE_LOCATION` | e.g. `/home/node/.n8n/logs/n8n.log` | Path when `N8N_LOG_OUTPUT` includes `file`. |
|
|
105
|
+
|
|
106
|
+
After changing env vars, restart n8n.
|
|
87
107
|
|
|
88
108
|
## Development
|
|
89
109
|
|
|
@@ -5,12 +5,10 @@ export declare class ActualBudget implements INodeType {
|
|
|
5
5
|
listSearch: {
|
|
6
6
|
getAccounts: typeof getAccounts;
|
|
7
7
|
getCategories: typeof getCategories;
|
|
8
|
-
getPayees: typeof getPayees;
|
|
9
8
|
};
|
|
10
9
|
};
|
|
11
10
|
execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
|
|
12
11
|
}
|
|
13
12
|
declare function getAccounts(this: ILoadOptionsFunctions, filter?: string): Promise<INodeListSearchResult>;
|
|
14
13
|
declare function getCategories(this: ILoadOptionsFunctions, filter?: string): Promise<INodeListSearchResult>;
|
|
15
|
-
declare function getPayees(this: ILoadOptionsFunctions, filter?: string): Promise<INodeListSearchResult>;
|
|
16
14
|
export {};
|
|
@@ -3,9 +3,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.ActualBudget = void 0;
|
|
4
4
|
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
5
|
const transport_1 = require("./transport");
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
const showForTransactionCreate = {
|
|
7
|
+
action: ['transactionCreate'],
|
|
8
|
+
};
|
|
9
|
+
const showForBudgetGet = {
|
|
10
|
+
action: ['budgetGet'],
|
|
9
11
|
};
|
|
10
12
|
function parseActualDate(value) {
|
|
11
13
|
const rawDate = value.trim();
|
|
@@ -19,12 +21,15 @@ function parseActualDate(value) {
|
|
|
19
21
|
}
|
|
20
22
|
return parsedDate.toISOString().slice(0, 10);
|
|
21
23
|
}
|
|
22
|
-
function
|
|
23
|
-
if (!Number.isFinite(value)
|
|
24
|
-
throw new n8n_workflow_1.ApplicationError('Amount must be
|
|
24
|
+
function parseMinorUnitsAmount(value) {
|
|
25
|
+
if (!Number.isFinite(value)) {
|
|
26
|
+
throw new n8n_workflow_1.ApplicationError('Amount must be an integer in minor currency units (e.g. -7374 for -73.74)');
|
|
27
|
+
}
|
|
28
|
+
const rounded = Math.round(value);
|
|
29
|
+
if (Math.abs(value - rounded) > 1e-9) {
|
|
30
|
+
throw new n8n_workflow_1.ApplicationError('Amount must be an integer in minor currency units (e.g. -7374 for -73.74)');
|
|
25
31
|
}
|
|
26
|
-
|
|
27
|
-
return transactionType === 'expense' ? -unsignedAmount : unsignedAmount;
|
|
32
|
+
return rounded;
|
|
28
33
|
}
|
|
29
34
|
function filterResults(items, filter) {
|
|
30
35
|
if (!filter) {
|
|
@@ -40,9 +45,9 @@ class ActualBudget {
|
|
|
40
45
|
name: 'actualBudget',
|
|
41
46
|
icon: { light: 'file:actual-budget.svg', dark: 'file:actual-budget.dark.svg' },
|
|
42
47
|
group: ['input'],
|
|
43
|
-
version:
|
|
44
|
-
subtitle: '={{$parameter["
|
|
45
|
-
description: '
|
|
48
|
+
version: 2,
|
|
49
|
+
subtitle: '={{$parameter["action"]}}',
|
|
50
|
+
description: 'Read budgets and create transactions in Actual Budget via actual-http-api (official endpoints)',
|
|
46
51
|
defaults: {
|
|
47
52
|
name: 'Actual Budget',
|
|
48
53
|
},
|
|
@@ -57,37 +62,37 @@ class ActualBudget {
|
|
|
57
62
|
],
|
|
58
63
|
properties: [
|
|
59
64
|
{
|
|
60
|
-
displayName: '
|
|
61
|
-
name: '
|
|
65
|
+
displayName: 'Action',
|
|
66
|
+
name: 'action',
|
|
62
67
|
type: 'options',
|
|
63
68
|
noDataExpression: true,
|
|
64
69
|
options: [
|
|
65
70
|
{
|
|
66
|
-
name: '
|
|
67
|
-
value: '
|
|
71
|
+
name: 'Account: List',
|
|
72
|
+
value: 'accountList',
|
|
73
|
+
action: 'List accounts',
|
|
74
|
+
description: 'GET /accounts',
|
|
68
75
|
},
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
76
|
+
{
|
|
77
|
+
name: 'Budget: Get Month',
|
|
78
|
+
value: 'budgetGet',
|
|
79
|
+
action: 'Get budget month',
|
|
80
|
+
description: 'GET /months/{month}',
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
name: 'Category: List',
|
|
84
|
+
value: 'categoryList',
|
|
85
|
+
action: 'List categories',
|
|
86
|
+
description: 'GET /categories',
|
|
80
87
|
},
|
|
81
|
-
},
|
|
82
|
-
options: [
|
|
83
88
|
{
|
|
84
|
-
name: '
|
|
85
|
-
value: '
|
|
86
|
-
action: '
|
|
87
|
-
description: '
|
|
89
|
+
name: 'Transaction: Create',
|
|
90
|
+
value: 'transactionCreate',
|
|
91
|
+
action: 'Create a transaction',
|
|
92
|
+
description: 'POST /accounts/{ID}/transactions',
|
|
88
93
|
},
|
|
89
94
|
],
|
|
90
|
-
default: '
|
|
95
|
+
default: 'transactionCreate',
|
|
91
96
|
},
|
|
92
97
|
{
|
|
93
98
|
displayName: 'Account',
|
|
@@ -96,7 +101,7 @@ class ActualBudget {
|
|
|
96
101
|
default: { mode: 'list', value: '' },
|
|
97
102
|
required: true,
|
|
98
103
|
displayOptions: {
|
|
99
|
-
show:
|
|
104
|
+
show: showForTransactionCreate,
|
|
100
105
|
},
|
|
101
106
|
modes: [
|
|
102
107
|
{
|
|
@@ -117,27 +122,37 @@ class ActualBudget {
|
|
|
117
122
|
placeholder: 'e.g. 729cb492-4eab-468b-9522-75d455cded22',
|
|
118
123
|
},
|
|
119
124
|
],
|
|
120
|
-
description: '
|
|
125
|
+
description: 'Account for the transaction',
|
|
121
126
|
},
|
|
122
127
|
{
|
|
123
|
-
displayName: '
|
|
124
|
-
name: '
|
|
125
|
-
type: '
|
|
128
|
+
displayName: 'Category',
|
|
129
|
+
name: 'categoryId',
|
|
130
|
+
type: 'resourceLocator',
|
|
131
|
+
default: { mode: 'list', value: '' },
|
|
132
|
+
required: true,
|
|
126
133
|
displayOptions: {
|
|
127
|
-
show:
|
|
134
|
+
show: showForTransactionCreate,
|
|
128
135
|
},
|
|
129
|
-
|
|
136
|
+
modes: [
|
|
130
137
|
{
|
|
131
|
-
|
|
132
|
-
|
|
138
|
+
displayName: 'Category',
|
|
139
|
+
name: 'list',
|
|
140
|
+
type: 'list',
|
|
141
|
+
placeholder: 'Select a category...',
|
|
142
|
+
typeOptions: {
|
|
143
|
+
searchListMethod: 'getCategories',
|
|
144
|
+
searchable: true,
|
|
145
|
+
searchFilterRequired: false,
|
|
146
|
+
},
|
|
133
147
|
},
|
|
134
148
|
{
|
|
135
|
-
|
|
136
|
-
|
|
149
|
+
displayName: 'By ID',
|
|
150
|
+
name: 'id',
|
|
151
|
+
type: 'string',
|
|
152
|
+
placeholder: 'e.g. 9fa2550c-c3ff-498b-8df6-e0fbe2a62e0e',
|
|
137
153
|
},
|
|
138
154
|
],
|
|
139
|
-
|
|
140
|
-
description: 'Whether the positive amount should be imported as money out or money in',
|
|
155
|
+
description: 'Category ID for the transaction',
|
|
141
156
|
},
|
|
142
157
|
{
|
|
143
158
|
displayName: 'Amount',
|
|
@@ -145,14 +160,13 @@ class ActualBudget {
|
|
|
145
160
|
type: 'number',
|
|
146
161
|
required: true,
|
|
147
162
|
displayOptions: {
|
|
148
|
-
show:
|
|
163
|
+
show: showForTransactionCreate,
|
|
149
164
|
},
|
|
150
165
|
typeOptions: {
|
|
151
|
-
|
|
152
|
-
numberPrecision: 2,
|
|
166
|
+
numberPrecision: 0,
|
|
153
167
|
},
|
|
154
168
|
default: 0,
|
|
155
|
-
description: '
|
|
169
|
+
description: 'Integer minor units (see actual-http-api docs). Example: expense -7374 means -73.74.',
|
|
156
170
|
},
|
|
157
171
|
{
|
|
158
172
|
displayName: 'Date',
|
|
@@ -160,143 +174,64 @@ class ActualBudget {
|
|
|
160
174
|
type: 'dateTime',
|
|
161
175
|
required: true,
|
|
162
176
|
displayOptions: {
|
|
163
|
-
show:
|
|
177
|
+
show: showForTransactionCreate,
|
|
164
178
|
},
|
|
165
179
|
default: '',
|
|
166
|
-
description: 'Transaction date
|
|
180
|
+
description: 'Transaction date (YYYY-MM-DD); time is ignored',
|
|
167
181
|
},
|
|
168
182
|
{
|
|
169
|
-
displayName: '
|
|
170
|
-
name: '
|
|
183
|
+
displayName: 'Payee Name',
|
|
184
|
+
name: 'payeeName',
|
|
171
185
|
type: 'string',
|
|
172
186
|
required: true,
|
|
173
187
|
displayOptions: {
|
|
174
|
-
show:
|
|
188
|
+
show: showForTransactionCreate,
|
|
175
189
|
},
|
|
176
190
|
default: '',
|
|
177
|
-
|
|
191
|
+
placeholder: 'e.g. Amazon',
|
|
192
|
+
description: 'Payee label for the transaction',
|
|
178
193
|
},
|
|
179
194
|
{
|
|
180
|
-
displayName: '
|
|
181
|
-
name: '
|
|
182
|
-
type: '
|
|
183
|
-
default: { mode: 'list', value: '' },
|
|
184
|
-
required: true,
|
|
195
|
+
displayName: 'Cleared',
|
|
196
|
+
name: 'cleared',
|
|
197
|
+
type: 'boolean',
|
|
185
198
|
displayOptions: {
|
|
186
|
-
show:
|
|
199
|
+
show: showForTransactionCreate,
|
|
187
200
|
},
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
displayName: 'Payee',
|
|
191
|
-
name: 'list',
|
|
192
|
-
type: 'list',
|
|
193
|
-
placeholder: 'Select a payee...',
|
|
194
|
-
typeOptions: {
|
|
195
|
-
searchListMethod: 'getPayees',
|
|
196
|
-
searchable: true,
|
|
197
|
-
searchFilterRequired: false,
|
|
198
|
-
},
|
|
199
|
-
},
|
|
200
|
-
{
|
|
201
|
-
displayName: 'By Name',
|
|
202
|
-
name: 'name',
|
|
203
|
-
type: 'string',
|
|
204
|
-
placeholder: 'e.g. Grocery Store',
|
|
205
|
-
},
|
|
206
|
-
],
|
|
207
|
-
description: 'Existing payee to use, or a payee name for Actual to match/create',
|
|
201
|
+
default: false,
|
|
202
|
+
description: 'Whether the transaction is cleared',
|
|
208
203
|
},
|
|
209
204
|
{
|
|
210
|
-
displayName: '
|
|
211
|
-
name: '
|
|
212
|
-
type: '
|
|
213
|
-
default: { mode: 'list', value: '' },
|
|
214
|
-
required: true,
|
|
205
|
+
displayName: 'Learn Categories',
|
|
206
|
+
name: 'learnCategories',
|
|
207
|
+
type: 'boolean',
|
|
215
208
|
displayOptions: {
|
|
216
|
-
show:
|
|
209
|
+
show: showForTransactionCreate,
|
|
217
210
|
},
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
displayName: 'Category',
|
|
221
|
-
name: 'list',
|
|
222
|
-
type: 'list',
|
|
223
|
-
placeholder: 'Select a category...',
|
|
224
|
-
typeOptions: {
|
|
225
|
-
searchListMethod: 'getCategories',
|
|
226
|
-
searchable: true,
|
|
227
|
-
searchFilterRequired: false,
|
|
228
|
-
},
|
|
229
|
-
},
|
|
230
|
-
{
|
|
231
|
-
displayName: 'By ID',
|
|
232
|
-
name: 'id',
|
|
233
|
-
type: 'string',
|
|
234
|
-
placeholder: 'e.g. 9fa2550c-c3ff-498b-8df6-e0fbe2a62e0e',
|
|
235
|
-
},
|
|
236
|
-
],
|
|
237
|
-
description: 'Actual category for the transaction',
|
|
211
|
+
default: false,
|
|
212
|
+
description: 'Whether to pass learnCategories to actual-http-api',
|
|
238
213
|
},
|
|
239
214
|
{
|
|
240
|
-
displayName: '
|
|
241
|
-
name: '
|
|
242
|
-
type: '
|
|
243
|
-
placeholder: 'Add Field',
|
|
244
|
-
default: {},
|
|
215
|
+
displayName: 'Run Transfers',
|
|
216
|
+
name: 'runTransfers',
|
|
217
|
+
type: 'boolean',
|
|
245
218
|
displayOptions: {
|
|
246
|
-
show:
|
|
219
|
+
show: showForTransactionCreate,
|
|
247
220
|
},
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
displayName: 'Cleared',
|
|
251
|
-
name: 'cleared',
|
|
252
|
-
type: 'boolean',
|
|
253
|
-
default: true,
|
|
254
|
-
description: 'Whether this transaction should be marked as cleared',
|
|
255
|
-
},
|
|
256
|
-
{
|
|
257
|
-
displayName: 'Notes',
|
|
258
|
-
name: 'notes',
|
|
259
|
-
type: 'string',
|
|
260
|
-
typeOptions: {
|
|
261
|
-
rows: 3,
|
|
262
|
-
},
|
|
263
|
-
default: '',
|
|
264
|
-
description: 'Optional transaction notes',
|
|
265
|
-
},
|
|
266
|
-
],
|
|
221
|
+
default: false,
|
|
222
|
+
description: 'Whether to pass runTransfers to actual-http-api',
|
|
267
223
|
},
|
|
268
224
|
{
|
|
269
|
-
displayName: '
|
|
270
|
-
name: '
|
|
271
|
-
type: '
|
|
272
|
-
|
|
273
|
-
default: {},
|
|
225
|
+
displayName: 'Month',
|
|
226
|
+
name: 'month',
|
|
227
|
+
type: 'string',
|
|
228
|
+
required: true,
|
|
274
229
|
displayOptions: {
|
|
275
|
-
show:
|
|
230
|
+
show: showForBudgetGet,
|
|
276
231
|
},
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
name: 'defaultCleared',
|
|
281
|
-
type: 'boolean',
|
|
282
|
-
default: true,
|
|
283
|
-
description: 'Whether imported transactions should default to cleared',
|
|
284
|
-
},
|
|
285
|
-
{
|
|
286
|
-
displayName: 'Dry Run',
|
|
287
|
-
name: 'dryRun',
|
|
288
|
-
type: 'boolean',
|
|
289
|
-
default: false,
|
|
290
|
-
description: 'Whether to preview import results without changing the budget',
|
|
291
|
-
},
|
|
292
|
-
{
|
|
293
|
-
displayName: 'Reimport Deleted',
|
|
294
|
-
name: 'reimportDeleted',
|
|
295
|
-
type: 'boolean',
|
|
296
|
-
default: false,
|
|
297
|
-
description: 'Whether to reimport transactions previously deleted in Actual',
|
|
298
|
-
},
|
|
299
|
-
],
|
|
232
|
+
default: '',
|
|
233
|
+
placeholder: '2026-05',
|
|
234
|
+
description: 'Budget month in YYYY-MM format',
|
|
300
235
|
},
|
|
301
236
|
],
|
|
302
237
|
};
|
|
@@ -304,58 +239,80 @@ class ActualBudget {
|
|
|
304
239
|
listSearch: {
|
|
305
240
|
getAccounts,
|
|
306
241
|
getCategories,
|
|
307
|
-
getPayees,
|
|
308
242
|
},
|
|
309
243
|
};
|
|
310
244
|
}
|
|
311
245
|
async execute() {
|
|
312
|
-
var _a, _b, _c;
|
|
313
246
|
const items = this.getInputData();
|
|
314
247
|
const returnData = [];
|
|
315
248
|
for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
|
|
316
249
|
try {
|
|
317
|
-
const
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
250
|
+
const action = this.getNodeParameter('action', itemIndex);
|
|
251
|
+
if (action === 'transactionCreate') {
|
|
252
|
+
const accountId = this.getNodeParameter('accountId', itemIndex, '', {
|
|
253
|
+
extractValue: true,
|
|
254
|
+
});
|
|
255
|
+
const categoryId = this.getNodeParameter('categoryId', itemIndex, '', {
|
|
256
|
+
extractValue: true,
|
|
257
|
+
});
|
|
258
|
+
const amount = parseMinorUnitsAmount(this.getNodeParameter('amount', itemIndex));
|
|
259
|
+
const date = this.getNodeParameter('date', itemIndex);
|
|
260
|
+
const payeeName = this.getNodeParameter('payeeName', itemIndex);
|
|
261
|
+
const cleared = this.getNodeParameter('cleared', itemIndex, false);
|
|
262
|
+
const learnCategories = this.getNodeParameter('learnCategories', itemIndex, false);
|
|
263
|
+
const runTransfers = this.getNodeParameter('runTransfers', itemIndex, false);
|
|
264
|
+
const body = {
|
|
265
|
+
learnCategories,
|
|
266
|
+
runTransfers,
|
|
267
|
+
transaction: {
|
|
268
|
+
account: accountId,
|
|
269
|
+
category: categoryId,
|
|
270
|
+
amount,
|
|
271
|
+
payee_name: payeeName,
|
|
272
|
+
date: parseActualDate(date),
|
|
273
|
+
cleared,
|
|
274
|
+
},
|
|
275
|
+
};
|
|
276
|
+
const responseData = (await transport_1.actualApiRequest.call(this, 'POST', `/accounts/${encodeURIComponent(accountId)}/transactions`, body));
|
|
277
|
+
returnData.push({
|
|
278
|
+
json: {
|
|
279
|
+
...responseData,
|
|
280
|
+
request: body,
|
|
281
|
+
},
|
|
282
|
+
pairedItem: itemIndex,
|
|
283
|
+
});
|
|
284
|
+
continue;
|
|
342
285
|
}
|
|
343
|
-
if (
|
|
344
|
-
|
|
286
|
+
if (action === 'budgetGet') {
|
|
287
|
+
const month = this.getNodeParameter('month', itemIndex);
|
|
288
|
+
const trimmed = month.trim();
|
|
289
|
+
if (!/^\d{4}-\d{2}$/.test(trimmed)) {
|
|
290
|
+
throw new n8n_workflow_1.ApplicationError('Month must be in YYYY-MM format');
|
|
291
|
+
}
|
|
292
|
+
const response = (await transport_1.actualApiRequest.call(this, 'GET', `/months/${encodeURIComponent(trimmed)}`));
|
|
293
|
+
returnData.push({
|
|
294
|
+
json: response,
|
|
295
|
+
pairedItem: itemIndex,
|
|
296
|
+
});
|
|
297
|
+
continue;
|
|
345
298
|
}
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
299
|
+
if (action === 'accountList') {
|
|
300
|
+
const response = (await transport_1.actualApiRequest.call(this, 'GET', '/accounts'));
|
|
301
|
+
returnData.push({
|
|
302
|
+
json: response,
|
|
303
|
+
pairedItem: itemIndex,
|
|
304
|
+
});
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
if (action === 'categoryList') {
|
|
308
|
+
const response = (await transport_1.actualApiRequest.call(this, 'GET', '/categories'));
|
|
309
|
+
returnData.push({
|
|
310
|
+
json: response,
|
|
311
|
+
pairedItem: itemIndex,
|
|
312
|
+
});
|
|
313
|
+
continue;
|
|
314
|
+
}
|
|
315
|
+
throw new n8n_workflow_1.ApplicationError(`Unsupported action: ${action}`);
|
|
359
316
|
}
|
|
360
317
|
catch (error) {
|
|
361
318
|
if (this.continueOnFail()) {
|
|
@@ -387,33 +344,13 @@ async function getAccounts(filter) {
|
|
|
387
344
|
return { results };
|
|
388
345
|
}
|
|
389
346
|
async function getCategories(filter) {
|
|
390
|
-
var _a, _b;
|
|
391
|
-
const response = (await transport_1.actualApiRequest.call(this, 'GET', '/categorygroups'));
|
|
392
|
-
const results = [];
|
|
393
|
-
for (const group of (_a = response.data) !== null && _a !== void 0 ? _a : []) {
|
|
394
|
-
if (group.hidden) {
|
|
395
|
-
continue;
|
|
396
|
-
}
|
|
397
|
-
for (const category of (_b = group.categories) !== null && _b !== void 0 ? _b : []) {
|
|
398
|
-
if (category.hidden) {
|
|
399
|
-
continue;
|
|
400
|
-
}
|
|
401
|
-
results.push({
|
|
402
|
-
name: `${group.name} / ${category.name}`,
|
|
403
|
-
value: category.id,
|
|
404
|
-
});
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
return { results: filterResults(results, filter) };
|
|
408
|
-
}
|
|
409
|
-
async function getPayees(filter) {
|
|
410
347
|
var _a;
|
|
411
|
-
const response = (await transport_1.actualApiRequest.call(this, 'GET', '/
|
|
412
|
-
const
|
|
413
|
-
const results =
|
|
414
|
-
name:
|
|
415
|
-
value:
|
|
348
|
+
const response = (await transport_1.actualApiRequest.call(this, 'GET', '/categories'));
|
|
349
|
+
const visible = ((_a = response.data) !== null && _a !== void 0 ? _a : []).filter((c) => !c.hidden);
|
|
350
|
+
const results = visible.map((category) => ({
|
|
351
|
+
name: category.name,
|
|
352
|
+
value: category.id,
|
|
416
353
|
}));
|
|
417
|
-
return { results };
|
|
354
|
+
return { results: filterResults(results, filter) };
|
|
418
355
|
}
|
|
419
356
|
//# sourceMappingURL=ActualBudget.node.js.map
|