@fuzzle/opencode-accountant 0.0.8 → 0.0.9-next.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 +6 -2
- package/dist/index.js +49 -55
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -115,7 +115,9 @@ providers:
|
|
|
115
115
|
|
|
116
116
|
ubs:
|
|
117
117
|
detect:
|
|
118
|
-
|
|
118
|
+
# Note: UBS exports have a trailing semicolon in the header row, which creates
|
|
119
|
+
# an empty field when parsed. The header must include a trailing comma to match.
|
|
120
|
+
- header: 'Trade date,Trade time,Booking date,Value date,Currency,Debit,Credit,Individual amount,Balance,Transaction no.,Description1,Description2,Description3,Footnotes,'
|
|
119
121
|
currencyField: Currency
|
|
120
122
|
skipRows: 9
|
|
121
123
|
delimiter: ';'
|
|
@@ -147,7 +149,7 @@ providers:
|
|
|
147
149
|
| Field | Required | Description |
|
|
148
150
|
| ----------------- | -------- | ---------------------------------------------------------- |
|
|
149
151
|
| `filenamePattern` | No | Regex pattern to match against filename |
|
|
150
|
-
| `header` | Yes | Expected CSV header row (exact match)
|
|
152
|
+
| `header` | Yes | Expected CSV header row (comma-separated, exact match)\* |
|
|
151
153
|
| `currencyField` | Yes | Column name containing the currency/symbol |
|
|
152
154
|
| `skipRows` | No | Number of rows to skip before header (default: 0) |
|
|
153
155
|
| `delimiter` | No | CSV delimiter character (default: `,`) |
|
|
@@ -155,6 +157,8 @@ providers:
|
|
|
155
157
|
| `metadata` | No | Array of metadata extraction rules (see below) |
|
|
156
158
|
| `currencies` | Yes | Map of raw currency values to normalized folder names |
|
|
157
159
|
|
|
160
|
+
\* **Note on trailing delimiters:** If the CSV header row ends with a trailing delimiter (e.g., `Field1;Field2;`), this creates an empty field when parsed. The `header` config must include a trailing comma to account for this (e.g., `Field1,Field2,`).
|
|
161
|
+
|
|
158
162
|
**Metadata Extraction Rules:**
|
|
159
163
|
|
|
160
164
|
| Field | Required | Description |
|
package/dist/index.js
CHANGED
|
@@ -1341,7 +1341,7 @@ var require_papaparse = __commonJS((exports, module) => {
|
|
|
1341
1341
|
});
|
|
1342
1342
|
|
|
1343
1343
|
// src/index.ts
|
|
1344
|
-
import { dirname, join as join5 } from "path";
|
|
1344
|
+
import { dirname as dirname2, join as join5 } from "path";
|
|
1345
1345
|
import { fileURLToPath } from "url";
|
|
1346
1346
|
|
|
1347
1347
|
// src/utils/agentLoader.ts
|
|
@@ -16848,35 +16848,6 @@ function findCSVFiles(importsDir) {
|
|
|
16848
16848
|
return fs4.statSync(fullPath).isFile();
|
|
16849
16849
|
});
|
|
16850
16850
|
}
|
|
16851
|
-
function checkPendingFiles(directory, pendingBasePath) {
|
|
16852
|
-
const pendingDir = path4.join(directory, pendingBasePath);
|
|
16853
|
-
const pendingFiles = [];
|
|
16854
|
-
if (!fs4.existsSync(pendingDir)) {
|
|
16855
|
-
return [];
|
|
16856
|
-
}
|
|
16857
|
-
const providers = fs4.readdirSync(pendingDir);
|
|
16858
|
-
for (const provider of providers) {
|
|
16859
|
-
const providerPath = path4.join(pendingDir, provider);
|
|
16860
|
-
if (!fs4.statSync(providerPath).isDirectory())
|
|
16861
|
-
continue;
|
|
16862
|
-
const currencies = fs4.readdirSync(providerPath);
|
|
16863
|
-
for (const currency of currencies) {
|
|
16864
|
-
const currencyPath = path4.join(providerPath, currency);
|
|
16865
|
-
if (!fs4.statSync(currencyPath).isDirectory())
|
|
16866
|
-
continue;
|
|
16867
|
-
const files = fs4.readdirSync(currencyPath).filter((f) => f.toLowerCase().endsWith(".csv"));
|
|
16868
|
-
for (const file2 of files) {
|
|
16869
|
-
pendingFiles.push({
|
|
16870
|
-
provider,
|
|
16871
|
-
currency,
|
|
16872
|
-
filename: file2,
|
|
16873
|
-
path: path4.join(currencyPath, file2)
|
|
16874
|
-
});
|
|
16875
|
-
}
|
|
16876
|
-
}
|
|
16877
|
-
}
|
|
16878
|
-
return pendingFiles;
|
|
16879
|
-
}
|
|
16880
16851
|
function ensureDirectory(dirPath) {
|
|
16881
16852
|
if (!fs4.existsSync(dirPath)) {
|
|
16882
16853
|
fs4.mkdirSync(dirPath, { recursive: true });
|
|
@@ -16907,16 +16878,6 @@ async function classifyStatementsCore(directory, agent, configLoader = loadImpor
|
|
|
16907
16878
|
const importsDir = path4.join(directory, config2.paths.import);
|
|
16908
16879
|
const pendingDir = path4.join(directory, config2.paths.pending);
|
|
16909
16880
|
const unrecognizedDir = path4.join(directory, config2.paths.unrecognized);
|
|
16910
|
-
const pendingFiles = checkPendingFiles(directory, config2.paths.pending);
|
|
16911
|
-
if (pendingFiles.length > 0) {
|
|
16912
|
-
return JSON.stringify({
|
|
16913
|
-
success: false,
|
|
16914
|
-
error: `Found ${pendingFiles.length} pending file(s) that must be processed before classifying new statements.`,
|
|
16915
|
-
pendingFiles,
|
|
16916
|
-
classified: [],
|
|
16917
|
-
unrecognized: []
|
|
16918
|
-
});
|
|
16919
|
-
}
|
|
16920
16881
|
const csvFiles = findCSVFiles(importsDir);
|
|
16921
16882
|
if (csvFiles.length === 0) {
|
|
16922
16883
|
return JSON.stringify({
|
|
@@ -16926,32 +16887,65 @@ async function classifyStatementsCore(directory, agent, configLoader = loadImpor
|
|
|
16926
16887
|
message: `No CSV files found in ${config2.paths.import}`
|
|
16927
16888
|
});
|
|
16928
16889
|
}
|
|
16929
|
-
const
|
|
16930
|
-
const
|
|
16890
|
+
const plannedMoves = [];
|
|
16891
|
+
const collisions = [];
|
|
16931
16892
|
for (const filename of csvFiles) {
|
|
16932
16893
|
const sourcePath = path4.join(importsDir, filename);
|
|
16933
16894
|
const content = fs4.readFileSync(sourcePath, "utf-8");
|
|
16934
16895
|
const detection = detectProvider(filename, content, config2);
|
|
16896
|
+
let targetPath;
|
|
16897
|
+
let targetFilename;
|
|
16935
16898
|
if (detection) {
|
|
16936
|
-
|
|
16899
|
+
targetFilename = detection.outputFilename || filename;
|
|
16937
16900
|
const targetDir = path4.join(pendingDir, detection.provider, detection.currency);
|
|
16901
|
+
targetPath = path4.join(targetDir, targetFilename);
|
|
16902
|
+
} else {
|
|
16903
|
+
targetFilename = filename;
|
|
16904
|
+
targetPath = path4.join(unrecognizedDir, filename);
|
|
16905
|
+
}
|
|
16906
|
+
if (fs4.existsSync(targetPath)) {
|
|
16907
|
+
collisions.push({
|
|
16908
|
+
filename,
|
|
16909
|
+
existingPath: targetPath
|
|
16910
|
+
});
|
|
16911
|
+
}
|
|
16912
|
+
plannedMoves.push({
|
|
16913
|
+
filename,
|
|
16914
|
+
sourcePath,
|
|
16915
|
+
targetPath,
|
|
16916
|
+
targetFilename,
|
|
16917
|
+
detection
|
|
16918
|
+
});
|
|
16919
|
+
}
|
|
16920
|
+
if (collisions.length > 0) {
|
|
16921
|
+
return JSON.stringify({
|
|
16922
|
+
success: false,
|
|
16923
|
+
error: `Cannot classify: ${collisions.length} file(s) would overwrite existing pending files.`,
|
|
16924
|
+
collisions,
|
|
16925
|
+
classified: [],
|
|
16926
|
+
unrecognized: []
|
|
16927
|
+
});
|
|
16928
|
+
}
|
|
16929
|
+
const classified = [];
|
|
16930
|
+
const unrecognized = [];
|
|
16931
|
+
for (const move of plannedMoves) {
|
|
16932
|
+
if (move.detection) {
|
|
16933
|
+
const targetDir = path4.dirname(move.targetPath);
|
|
16938
16934
|
ensureDirectory(targetDir);
|
|
16939
|
-
|
|
16940
|
-
fs4.renameSync(sourcePath, targetPath);
|
|
16935
|
+
fs4.renameSync(move.sourcePath, move.targetPath);
|
|
16941
16936
|
classified.push({
|
|
16942
|
-
filename: targetFilename,
|
|
16943
|
-
originalFilename: detection.outputFilename ? filename : undefined,
|
|
16944
|
-
provider: detection.provider,
|
|
16945
|
-
currency: detection.currency,
|
|
16946
|
-
targetPath: path4.join(config2.paths.pending, detection.provider, detection.currency, targetFilename)
|
|
16937
|
+
filename: move.targetFilename,
|
|
16938
|
+
originalFilename: move.detection.outputFilename ? move.filename : undefined,
|
|
16939
|
+
provider: move.detection.provider,
|
|
16940
|
+
currency: move.detection.currency,
|
|
16941
|
+
targetPath: path4.join(config2.paths.pending, move.detection.provider, move.detection.currency, move.targetFilename)
|
|
16947
16942
|
});
|
|
16948
16943
|
} else {
|
|
16949
16944
|
ensureDirectory(unrecognizedDir);
|
|
16950
|
-
|
|
16951
|
-
fs4.renameSync(sourcePath, targetPath);
|
|
16945
|
+
fs4.renameSync(move.sourcePath, move.targetPath);
|
|
16952
16946
|
unrecognized.push({
|
|
16953
|
-
filename,
|
|
16954
|
-
targetPath: path4.join(config2.paths.unrecognized, filename)
|
|
16947
|
+
filename: move.filename,
|
|
16948
|
+
targetPath: path4.join(config2.paths.unrecognized, move.filename)
|
|
16955
16949
|
});
|
|
16956
16950
|
}
|
|
16957
16951
|
}
|
|
@@ -16975,7 +16969,7 @@ var classify_statements_default = tool({
|
|
|
16975
16969
|
}
|
|
16976
16970
|
});
|
|
16977
16971
|
// src/index.ts
|
|
16978
|
-
var __dirname2 =
|
|
16972
|
+
var __dirname2 = dirname2(fileURLToPath(import.meta.url));
|
|
16979
16973
|
var AGENT_FILE = join5(__dirname2, "..", "agent", "accountant.md");
|
|
16980
16974
|
var AccountantPlugin = async () => {
|
|
16981
16975
|
const agent = loadAgent(AGENT_FILE);
|