kakutey 0.1.0
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/LICENSE +21 -0
- package/README.md +128 -0
- package/dist/index.js +787 -0
- package/dist/index.js.map +1 -0
- package/package.json +38 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 usa-tech-lab
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# kakutey
|
|
2
|
+
|
|
3
|
+
CLI for the kakutey bookkeeping application.
|
|
4
|
+
|
|
5
|
+
kakutey アプリ(会計ソフト)を操作するコマンドラインツールです。仕訳の登録・一覧、証憑のアップロード・検索、財務諸表の取得などを CLI から実行できます。
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -g kakutey
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Or run directly with npx:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npx kakutey --help
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Prerequisites
|
|
20
|
+
|
|
21
|
+
kakutey アプリが起動している必要があります(バックエンド: port 8000, フロントエンド: port 4200)。
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
kakutey health
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Commands
|
|
28
|
+
|
|
29
|
+
### health — アプリ状態確認
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
kakutey health
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
バックエンド(port 8000)とフロントエンド(port 4200)の稼働状態を確認します。
|
|
36
|
+
|
|
37
|
+
### accounts — 勘定科目
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
# 全科目一覧
|
|
41
|
+
kakutey accounts list
|
|
42
|
+
|
|
43
|
+
# カテゴリでフィルタ
|
|
44
|
+
kakutey accounts list --category expense
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`--category` に指定できる値: `asset`, `liability`, `equity`, `revenue`, `expense`
|
|
48
|
+
|
|
49
|
+
### journals — 仕訳
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
# 仕訳一覧(期間指定)
|
|
53
|
+
kakutey journals list 2025-01-01 2025-12-31
|
|
54
|
+
|
|
55
|
+
# 仕訳登録(簡易形式)
|
|
56
|
+
kakutey journals add '{"date":"2025-01-15","description":"文房具購入","debit_account":"消耗品費","credit_account":"現金","amount":1000}'
|
|
57
|
+
|
|
58
|
+
# 仕訳登録(複合仕訳)
|
|
59
|
+
kakutey journals add '{"date":"2025-01-01","description":"開始仕訳","lines":[{"side":"debit","account":"現金","amount":50000},{"side":"credit","account":"元入金","amount":50000}]}'
|
|
60
|
+
|
|
61
|
+
# ファイルから登録
|
|
62
|
+
kakutey journals add --file journal.json
|
|
63
|
+
|
|
64
|
+
# 一括登録
|
|
65
|
+
kakutey journals bulk-add journals.json
|
|
66
|
+
|
|
67
|
+
# 更新(楽観ロック)
|
|
68
|
+
kakutey journals update '{"entity_id":1,"date":"2025-01-15","description":"修正","lines":[{"side":"debit","account":"消耗品費","amount":1500},{"side":"credit","account":"現金","amount":1500}],"expected_revision":1}'
|
|
69
|
+
|
|
70
|
+
# 削除(論理削除)
|
|
71
|
+
kakutey journals delete 1
|
|
72
|
+
|
|
73
|
+
# 証憑を紐付け
|
|
74
|
+
kakutey journals attach 1 10 20
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### evidence — 証憑
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
# アップロード
|
|
81
|
+
kakutey evidence upload ./receipt.pdf
|
|
82
|
+
kakutey evidence upload ./receipt.pdf --name "領収書 サンプル商事 2025-01"
|
|
83
|
+
|
|
84
|
+
# 検索
|
|
85
|
+
kakutey evidence search --name "サンプル"
|
|
86
|
+
kakutey evidence search --tag "交通費"
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### reports — 財務諸表
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
# 損益計算書
|
|
93
|
+
kakutey reports pl 2025
|
|
94
|
+
|
|
95
|
+
# 貸借対照表
|
|
96
|
+
kakutey reports bs 2025
|
|
97
|
+
|
|
98
|
+
# P/L + B/S 両方
|
|
99
|
+
kakutey reports summary 2025
|
|
100
|
+
kakutey reports summary 2025 --type pl
|
|
101
|
+
|
|
102
|
+
# 固定資産一覧
|
|
103
|
+
kakutey reports assets
|
|
104
|
+
|
|
105
|
+
# 固定資産 + 減価償却計算
|
|
106
|
+
kakutey reports assets --depreciation 2025
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Global Options
|
|
110
|
+
|
|
111
|
+
| Option | Description | Default |
|
|
112
|
+
|--------|-------------|---------|
|
|
113
|
+
| `--base-url <url>` | API base URL | `http://localhost:8000/api` |
|
|
114
|
+
| `--json` | Output raw JSON | off |
|
|
115
|
+
| `--version` | Show version | |
|
|
116
|
+
| `--help` | Show help | |
|
|
117
|
+
|
|
118
|
+
## Environment Variables
|
|
119
|
+
|
|
120
|
+
| Variable | Description | Default |
|
|
121
|
+
|----------|-------------|---------|
|
|
122
|
+
| `KAKUTEY_API_URL` | API base URL | `http://localhost:8000/api` |
|
|
123
|
+
|
|
124
|
+
`--base-url` フラグは環境変数より優先されます。
|
|
125
|
+
|
|
126
|
+
## License
|
|
127
|
+
|
|
128
|
+
MIT
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,787 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/cli.ts
|
|
4
|
+
import { Command } from "commander";
|
|
5
|
+
|
|
6
|
+
// src/lib/config.ts
|
|
7
|
+
var currentConfig = {
|
|
8
|
+
baseUrl: process.env.KAKUTEY_API_URL || "http://localhost:8000/api",
|
|
9
|
+
jsonOutput: false
|
|
10
|
+
};
|
|
11
|
+
function setConfig(partial) {
|
|
12
|
+
currentConfig = { ...currentConfig, ...partial };
|
|
13
|
+
}
|
|
14
|
+
function getConfig() {
|
|
15
|
+
return currentConfig;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// src/commands/health.ts
|
|
19
|
+
import chalk from "chalk";
|
|
20
|
+
import net from "net";
|
|
21
|
+
function checkPort(host, port, timeout = 2e3) {
|
|
22
|
+
return new Promise((resolve) => {
|
|
23
|
+
const sock = new net.Socket();
|
|
24
|
+
sock.setTimeout(timeout);
|
|
25
|
+
sock.on("connect", () => {
|
|
26
|
+
sock.destroy();
|
|
27
|
+
resolve(true);
|
|
28
|
+
});
|
|
29
|
+
sock.on("timeout", () => {
|
|
30
|
+
sock.destroy();
|
|
31
|
+
resolve(false);
|
|
32
|
+
});
|
|
33
|
+
sock.on("error", () => {
|
|
34
|
+
sock.destroy();
|
|
35
|
+
resolve(false);
|
|
36
|
+
});
|
|
37
|
+
sock.connect(port, host);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
async function checkHttp(url, timeout = 5e3) {
|
|
41
|
+
try {
|
|
42
|
+
const controller = new AbortController();
|
|
43
|
+
const timer = setTimeout(() => controller.abort(), timeout);
|
|
44
|
+
const resp = await fetch(url, { signal: controller.signal });
|
|
45
|
+
clearTimeout(timer);
|
|
46
|
+
return resp.status;
|
|
47
|
+
} catch {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function registerHealthCommand(program2) {
|
|
52
|
+
program2.command("health").description("Check kakutey backend and frontend status").action(async () => {
|
|
53
|
+
let exitCode = 0;
|
|
54
|
+
const backendUp = await checkPort("localhost", 8e3);
|
|
55
|
+
if (!backendUp) {
|
|
56
|
+
console.log(
|
|
57
|
+
`Backend (port 8000): ${chalk.red("NG")} - nothing listening`
|
|
58
|
+
);
|
|
59
|
+
exitCode = 1;
|
|
60
|
+
} else {
|
|
61
|
+
const status = await checkHttp(
|
|
62
|
+
"http://localhost:8000/api/fiscal-year"
|
|
63
|
+
);
|
|
64
|
+
if (status === 200) {
|
|
65
|
+
console.log(
|
|
66
|
+
`Backend (port 8000): ${chalk.green("OK")} - API responding`
|
|
67
|
+
);
|
|
68
|
+
} else {
|
|
69
|
+
console.log(
|
|
70
|
+
`Backend (port 8000): ${chalk.red("NG")} - port in use but API not responding (HTTP ${status})`
|
|
71
|
+
);
|
|
72
|
+
exitCode = 1;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const frontendUp = await checkPort("localhost", 4200);
|
|
76
|
+
if (!frontendUp) {
|
|
77
|
+
console.log(
|
|
78
|
+
`Frontend (port 4200): ${chalk.red("NG")} - nothing listening`
|
|
79
|
+
);
|
|
80
|
+
exitCode = 1;
|
|
81
|
+
} else {
|
|
82
|
+
console.log(
|
|
83
|
+
`Frontend (port 4200): ${chalk.green("OK")} - listening`
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
process.exit(exitCode);
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// src/lib/client.ts
|
|
91
|
+
import { readFileSync } from "fs";
|
|
92
|
+
import { basename } from "path";
|
|
93
|
+
|
|
94
|
+
// src/lib/errors.ts
|
|
95
|
+
var ApiError = class extends Error {
|
|
96
|
+
constructor(method, path, statusCode, detail) {
|
|
97
|
+
super(
|
|
98
|
+
`API ${method.toUpperCase()} ${path} \u2192 ${statusCode}: ${detail.slice(0, 500)}`
|
|
99
|
+
);
|
|
100
|
+
this.method = method;
|
|
101
|
+
this.path = path;
|
|
102
|
+
this.statusCode = statusCode;
|
|
103
|
+
this.detail = detail;
|
|
104
|
+
this.name = "ApiError";
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
var ValidationError = class extends Error {
|
|
108
|
+
constructor(message) {
|
|
109
|
+
super(message);
|
|
110
|
+
this.name = "ValidationError";
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
function handleError(error) {
|
|
114
|
+
if (error instanceof ApiError) {
|
|
115
|
+
console.error(`API Error: ${error.message}`);
|
|
116
|
+
} else if (error instanceof ValidationError) {
|
|
117
|
+
console.error(`Validation Error: ${error.message}`);
|
|
118
|
+
} else if (error instanceof Error) {
|
|
119
|
+
console.error(`Error: ${error.message}`);
|
|
120
|
+
} else {
|
|
121
|
+
console.error("Unknown error", error);
|
|
122
|
+
}
|
|
123
|
+
process.exit(1);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// src/lib/client.ts
|
|
127
|
+
async function apiRequest(method, path, options) {
|
|
128
|
+
const config = getConfig();
|
|
129
|
+
const url = new URL(`${config.baseUrl}${path}`);
|
|
130
|
+
if (options?.params) {
|
|
131
|
+
for (const [key, value] of Object.entries(options.params)) {
|
|
132
|
+
url.searchParams.set(key, value);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
const response = await fetch(url.toString(), {
|
|
136
|
+
method: method.toUpperCase(),
|
|
137
|
+
headers: options?.body ? { "Content-Type": "application/json" } : void 0,
|
|
138
|
+
body: options?.body ? JSON.stringify(options.body) : void 0
|
|
139
|
+
});
|
|
140
|
+
if (!response.ok) {
|
|
141
|
+
const detail = await response.text().catch(() => response.statusText);
|
|
142
|
+
throw new ApiError(method, path, response.status, detail);
|
|
143
|
+
}
|
|
144
|
+
return response.json();
|
|
145
|
+
}
|
|
146
|
+
async function apiUpload(path, filePath, displayName) {
|
|
147
|
+
const config = getConfig();
|
|
148
|
+
const url = `${config.baseUrl}${path}`;
|
|
149
|
+
const fileBuffer = readFileSync(filePath);
|
|
150
|
+
const fileName = basename(filePath);
|
|
151
|
+
const formData = new FormData();
|
|
152
|
+
formData.append("file", new Blob([fileBuffer]), fileName);
|
|
153
|
+
formData.append("display_name", displayName);
|
|
154
|
+
const response = await fetch(url, {
|
|
155
|
+
method: "POST",
|
|
156
|
+
body: formData
|
|
157
|
+
});
|
|
158
|
+
if (!response.ok) {
|
|
159
|
+
const detail = await response.text().catch(() => response.statusText);
|
|
160
|
+
throw new ApiError("POST", path, response.status, detail);
|
|
161
|
+
}
|
|
162
|
+
return response.json();
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// src/lib/formatters/accounts.ts
|
|
166
|
+
import Table from "cli-table3";
|
|
167
|
+
function formatAccountsList(accounts) {
|
|
168
|
+
const table = new Table({
|
|
169
|
+
head: ["entity_id", "code", "name", "category"],
|
|
170
|
+
colAligns: ["right", "left", "left", "left"]
|
|
171
|
+
});
|
|
172
|
+
for (const a of accounts) {
|
|
173
|
+
if (a.is_abstract) continue;
|
|
174
|
+
table.push([a.entity_id, a.code ?? "", a.name, a.category]);
|
|
175
|
+
}
|
|
176
|
+
console.log(table.toString());
|
|
177
|
+
console.log(`
|
|
178
|
+
\u5408\u8A08: ${accounts.length} \u4EF6`);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// src/commands/accounts.ts
|
|
182
|
+
function registerAccountsCommand(program2) {
|
|
183
|
+
const accounts = program2.command("accounts").description("Manage chart of accounts");
|
|
184
|
+
accounts.command("list").description("List chart of accounts").option(
|
|
185
|
+
"--category <category>",
|
|
186
|
+
"Filter by category (asset|liability|equity|revenue|expense)"
|
|
187
|
+
).action(async (opts) => {
|
|
188
|
+
try {
|
|
189
|
+
let result = await apiRequest("get", "/accounts");
|
|
190
|
+
if (opts.category) {
|
|
191
|
+
result = result.filter((a) => a.category === opts.category);
|
|
192
|
+
}
|
|
193
|
+
result.sort((a, b) => {
|
|
194
|
+
const codeA = a.code ?? "";
|
|
195
|
+
const codeB = b.code ?? "";
|
|
196
|
+
return codeA.localeCompare(codeB) || a.name.localeCompare(b.name);
|
|
197
|
+
});
|
|
198
|
+
if (getConfig().jsonOutput) {
|
|
199
|
+
console.log(JSON.stringify(result, null, 2));
|
|
200
|
+
} else {
|
|
201
|
+
formatAccountsList(result);
|
|
202
|
+
}
|
|
203
|
+
} catch (e) {
|
|
204
|
+
handleError(e);
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// src/commands/journals.ts
|
|
210
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
211
|
+
|
|
212
|
+
// src/lib/accounts-resolver.ts
|
|
213
|
+
async function buildAccountNameToIdMap() {
|
|
214
|
+
const accounts = await apiRequest("get", "/accounts");
|
|
215
|
+
return new Map(accounts.map((a) => [a.name, a.entity_id]));
|
|
216
|
+
}
|
|
217
|
+
async function buildAccountIdToNameMap() {
|
|
218
|
+
const accounts = await apiRequest("get", "/accounts");
|
|
219
|
+
return new Map(accounts.map((a) => [a.entity_id, a.name]));
|
|
220
|
+
}
|
|
221
|
+
function resolveAccountId(accountMap, name) {
|
|
222
|
+
const id = accountMap.get(name);
|
|
223
|
+
if (id === void 0) {
|
|
224
|
+
throw new ValidationError(`\u52D8\u5B9A\u79D1\u76EE\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093: ${name}`);
|
|
225
|
+
}
|
|
226
|
+
return id;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// src/lib/formatters/journals.ts
|
|
230
|
+
import Table2 from "cli-table3";
|
|
231
|
+
function formatJournalsList(journals, accMap, startDate, endDate) {
|
|
232
|
+
console.log(
|
|
233
|
+
`
|
|
234
|
+
\u4ED5\u8A33\u4E00\u89A7: ${startDate} \u301C ${endDate} (${journals.length} \u4EF6)`
|
|
235
|
+
);
|
|
236
|
+
const table = new Table2({
|
|
237
|
+
head: ["EID", "Date", "EV", "Description", "Debit / Credit", "Amount"],
|
|
238
|
+
colAligns: ["right", "left", "center", "left", "left", "right"]
|
|
239
|
+
});
|
|
240
|
+
for (const j of journals) {
|
|
241
|
+
const debits = j.lines.filter((l) => l.side === "debit");
|
|
242
|
+
const credits = j.lines.filter((l) => l.side === "credit");
|
|
243
|
+
const evidence = j.evidence_ids.length > 0 ? "Y" : "";
|
|
244
|
+
if (debits.length === 1 && credits.length === 1) {
|
|
245
|
+
const drName = accMap.get(debits[0].account_id) ?? `?${debits[0].account_id}`;
|
|
246
|
+
const crName = accMap.get(credits[0].account_id) ?? `?${credits[0].account_id}`;
|
|
247
|
+
table.push([
|
|
248
|
+
j.entity_id,
|
|
249
|
+
j.journal_date,
|
|
250
|
+
evidence,
|
|
251
|
+
j.description.slice(0, 30),
|
|
252
|
+
`${drName} / ${crName}`,
|
|
253
|
+
debits[0].amount.toLocaleString()
|
|
254
|
+
]);
|
|
255
|
+
} else {
|
|
256
|
+
table.push([
|
|
257
|
+
j.entity_id,
|
|
258
|
+
j.journal_date,
|
|
259
|
+
evidence,
|
|
260
|
+
j.description.slice(0, 30),
|
|
261
|
+
"",
|
|
262
|
+
""
|
|
263
|
+
]);
|
|
264
|
+
for (const d of debits) {
|
|
265
|
+
const name = accMap.get(d.account_id) ?? `?${d.account_id}`;
|
|
266
|
+
table.push(["", "", "", "", ` \u501F\u65B9 ${name}`, d.amount.toLocaleString()]);
|
|
267
|
+
}
|
|
268
|
+
for (const c of credits) {
|
|
269
|
+
const name = accMap.get(c.account_id) ?? `?${c.account_id}`;
|
|
270
|
+
table.push(["", "", "", "", ` \u8CB8\u65B9 ${name}`, c.amount.toLocaleString()]);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
console.log(table.toString());
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// src/commands/journals.ts
|
|
278
|
+
function isCompoundInput(data) {
|
|
279
|
+
return typeof data === "object" && data !== null && "lines" in data;
|
|
280
|
+
}
|
|
281
|
+
async function buildPayload(data, accMap) {
|
|
282
|
+
if (isCompoundInput(data)) {
|
|
283
|
+
const lines = data.lines.map((l) => ({
|
|
284
|
+
side: l.side,
|
|
285
|
+
account_id: resolveAccountId(accMap, l.account),
|
|
286
|
+
amount: l.amount
|
|
287
|
+
}));
|
|
288
|
+
return {
|
|
289
|
+
date: data.date,
|
|
290
|
+
description: data.description,
|
|
291
|
+
lines,
|
|
292
|
+
evidence_ids: data.evidence_ids ?? []
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
const drId = resolveAccountId(accMap, data.debit_account);
|
|
296
|
+
const crId = resolveAccountId(accMap, data.credit_account);
|
|
297
|
+
return {
|
|
298
|
+
date: data.date,
|
|
299
|
+
description: data.description,
|
|
300
|
+
lines: [
|
|
301
|
+
{ side: "debit", account_id: drId, amount: data.amount },
|
|
302
|
+
{ side: "credit", account_id: crId, amount: data.amount }
|
|
303
|
+
],
|
|
304
|
+
evidence_ids: data.evidence_ids ?? []
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
function registerJournalsCommand(program2) {
|
|
308
|
+
const journals = program2.command("journals").description("Manage journal entries");
|
|
309
|
+
journals.command("list").description("List journals for a date range").argument("<start_date>", "Start date (YYYY-MM-DD)").argument("<end_date>", "End date (YYYY-MM-DD)").action(async (startDate, endDate) => {
|
|
310
|
+
try {
|
|
311
|
+
const [result, accMap] = await Promise.all([
|
|
312
|
+
apiRequest("get", "/journals", {
|
|
313
|
+
params: { start_date: startDate, end_date: endDate }
|
|
314
|
+
}),
|
|
315
|
+
buildAccountIdToNameMap()
|
|
316
|
+
]);
|
|
317
|
+
result.sort((a, b) => a.journal_date.localeCompare(b.journal_date));
|
|
318
|
+
if (getConfig().jsonOutput) {
|
|
319
|
+
console.log(JSON.stringify(result, null, 2));
|
|
320
|
+
} else {
|
|
321
|
+
formatJournalsList(result, accMap, startDate, endDate);
|
|
322
|
+
}
|
|
323
|
+
} catch (e) {
|
|
324
|
+
handleError(e);
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
journals.command("add").description("Add a single journal entry").argument("[json]", "Journal data as JSON string").option("--file <path>", "Read journal data from a JSON file").action(async (jsonStr, opts) => {
|
|
328
|
+
try {
|
|
329
|
+
let data;
|
|
330
|
+
if (opts.file) {
|
|
331
|
+
data = JSON.parse(readFileSync2(opts.file, "utf-8"));
|
|
332
|
+
} else if (jsonStr) {
|
|
333
|
+
data = JSON.parse(jsonStr);
|
|
334
|
+
} else {
|
|
335
|
+
console.error("Provide JSON as argument or use --file");
|
|
336
|
+
process.exit(1);
|
|
337
|
+
}
|
|
338
|
+
const accMap = await buildAccountNameToIdMap();
|
|
339
|
+
const payload = await buildPayload(data, accMap);
|
|
340
|
+
const result = await apiRequest("post", "/journals", {
|
|
341
|
+
body: payload
|
|
342
|
+
});
|
|
343
|
+
if (getConfig().jsonOutput) {
|
|
344
|
+
console.log(JSON.stringify(result, null, 2));
|
|
345
|
+
} else {
|
|
346
|
+
console.log(
|
|
347
|
+
`\u767B\u9332\u5B8C\u4E86: entity_id=${result.entity_id}, revision=${result.revision}`
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
} catch (e) {
|
|
351
|
+
handleError(e);
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
journals.command("bulk-add").description("Bulk add journals from a JSON file").argument("<file>", "Path to JSON file containing array of journal entries").action(async (file) => {
|
|
355
|
+
try {
|
|
356
|
+
const entries = JSON.parse(readFileSync2(file, "utf-8"));
|
|
357
|
+
console.log(`=== ${entries.length} \u4EF6\u306E\u4ED5\u8A33\u3092\u767B\u9332 ===`);
|
|
358
|
+
const accMap = await buildAccountNameToIdMap();
|
|
359
|
+
let created = 0;
|
|
360
|
+
const errors = [];
|
|
361
|
+
for (let i = 0; i < entries.length; i++) {
|
|
362
|
+
try {
|
|
363
|
+
const payload = await buildPayload(entries[i], accMap);
|
|
364
|
+
await apiRequest("post", "/journals", { body: payload });
|
|
365
|
+
created++;
|
|
366
|
+
} catch (e) {
|
|
367
|
+
const desc = "description" in entries[i] ? entries[i].description : "?";
|
|
368
|
+
errors.push([i + 1, desc, String(e)]);
|
|
369
|
+
}
|
|
370
|
+
if ((i + 1) % 20 === 0 || i + 1 === entries.length) {
|
|
371
|
+
console.log(
|
|
372
|
+
` [${i + 1}/${entries.length}] \u767B\u9332: ${created}, \u30A8\u30E9\u30FC: ${errors.length}`
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
console.log(`
|
|
377
|
+
\u5B8C\u4E86: \u767B\u9332=${created}, \u30A8\u30E9\u30FC=${errors.length}`);
|
|
378
|
+
if (errors.length > 0) {
|
|
379
|
+
console.log("\n\u30A8\u30E9\u30FC\u4E00\u89A7:");
|
|
380
|
+
for (const [idx, desc, msg] of errors) {
|
|
381
|
+
console.log(` #${idx} (${desc}): ${msg}`);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
} catch (e) {
|
|
385
|
+
handleError(e);
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
journals.command("update").description("Full update a journal entry (with optimistic lock)").argument("<json>", "Update data as JSON string").action(async (jsonStr) => {
|
|
389
|
+
try {
|
|
390
|
+
const data = JSON.parse(jsonStr);
|
|
391
|
+
const accMap = await buildAccountNameToIdMap();
|
|
392
|
+
const lines = data.lines.map((l) => {
|
|
393
|
+
if (l.account_id !== void 0) {
|
|
394
|
+
return {
|
|
395
|
+
side: l.side,
|
|
396
|
+
account_id: l.account_id,
|
|
397
|
+
amount: l.amount
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
return {
|
|
401
|
+
side: l.side,
|
|
402
|
+
account_id: resolveAccountId(accMap, l.account),
|
|
403
|
+
amount: l.amount
|
|
404
|
+
};
|
|
405
|
+
});
|
|
406
|
+
const payload = {
|
|
407
|
+
date: data.date,
|
|
408
|
+
description: data.description,
|
|
409
|
+
lines,
|
|
410
|
+
evidence_ids: data.evidence_ids ?? [],
|
|
411
|
+
expected_revision: data.expected_revision
|
|
412
|
+
};
|
|
413
|
+
const result = await apiRequest(
|
|
414
|
+
"put",
|
|
415
|
+
`/journals/${data.entity_id}`,
|
|
416
|
+
{ body: payload }
|
|
417
|
+
);
|
|
418
|
+
if (getConfig().jsonOutput) {
|
|
419
|
+
console.log(JSON.stringify(result, null, 2));
|
|
420
|
+
} else {
|
|
421
|
+
console.log(
|
|
422
|
+
`\u66F4\u65B0\u5B8C\u4E86: entity_id=${result.entity_id}, revision=${result.revision}`
|
|
423
|
+
);
|
|
424
|
+
}
|
|
425
|
+
} catch (e) {
|
|
426
|
+
handleError(e);
|
|
427
|
+
}
|
|
428
|
+
});
|
|
429
|
+
journals.command("delete").description("Soft-delete a journal entry").argument("<entity_id>", "Journal entity ID").action(async (entityIdStr) => {
|
|
430
|
+
try {
|
|
431
|
+
const entityId = parseInt(entityIdStr, 10);
|
|
432
|
+
await apiRequest("delete", `/journals/${entityId}`);
|
|
433
|
+
console.log(`\u524A\u9664\u5B8C\u4E86: entity_id=${entityId}`);
|
|
434
|
+
} catch (e) {
|
|
435
|
+
handleError(e);
|
|
436
|
+
}
|
|
437
|
+
});
|
|
438
|
+
journals.command("attach").description("Attach evidence IDs to a journal").argument("<entity_id>", "Journal entity ID").argument("<evidence_ids...>", "Evidence entity IDs to attach").action(async (entityIdStr, evidenceIdStrs) => {
|
|
439
|
+
try {
|
|
440
|
+
const entityId = parseInt(entityIdStr, 10);
|
|
441
|
+
const newIds = evidenceIdStrs.map(Number);
|
|
442
|
+
const journals2 = await apiRequest("get", "/journals");
|
|
443
|
+
const journal = journals2.find((j) => j.entity_id === entityId);
|
|
444
|
+
if (!journal) {
|
|
445
|
+
console.error(`\u4ED5\u8A33\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093: entity_id=${entityId}`);
|
|
446
|
+
process.exit(1);
|
|
447
|
+
}
|
|
448
|
+
const existingIds = journal.evidence_ids ?? [];
|
|
449
|
+
const mergedIds = [.../* @__PURE__ */ new Set([...existingIds, ...newIds])];
|
|
450
|
+
const payload = {
|
|
451
|
+
date: journal.journal_date,
|
|
452
|
+
description: journal.description,
|
|
453
|
+
lines: journal.lines.map((l) => ({
|
|
454
|
+
side: l.side,
|
|
455
|
+
account_id: l.account_id,
|
|
456
|
+
amount: l.amount
|
|
457
|
+
})),
|
|
458
|
+
evidence_ids: mergedIds,
|
|
459
|
+
expected_revision: journal.revision
|
|
460
|
+
};
|
|
461
|
+
const result = await apiRequest(
|
|
462
|
+
"put",
|
|
463
|
+
`/journals/${entityId}`,
|
|
464
|
+
{ body: payload }
|
|
465
|
+
);
|
|
466
|
+
if (getConfig().jsonOutput) {
|
|
467
|
+
console.log(JSON.stringify(result, null, 2));
|
|
468
|
+
} else {
|
|
469
|
+
console.log(
|
|
470
|
+
`\u8A3C\u6191\u8FFD\u52A0\u5B8C\u4E86: entity_id=${result.entity_id}, revision=${result.revision}`
|
|
471
|
+
);
|
|
472
|
+
console.log(` evidence_ids: [${result.evidence_ids.join(", ")}]`);
|
|
473
|
+
}
|
|
474
|
+
} catch (e) {
|
|
475
|
+
handleError(e);
|
|
476
|
+
}
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// src/commands/evidence.ts
|
|
481
|
+
import { basename as basename2 } from "path";
|
|
482
|
+
import { existsSync } from "fs";
|
|
483
|
+
|
|
484
|
+
// src/lib/formatters/evidence.ts
|
|
485
|
+
import Table3 from "cli-table3";
|
|
486
|
+
function formatEvidenceList(results) {
|
|
487
|
+
console.log(`
|
|
488
|
+
\u691C\u7D22\u7D50\u679C: ${results.length} \u4EF6`);
|
|
489
|
+
const table = new Table3({
|
|
490
|
+
head: ["EID", "display_name", "file_type", "tags"],
|
|
491
|
+
colAligns: ["right", "left", "left", "left"]
|
|
492
|
+
});
|
|
493
|
+
for (const ev of results) {
|
|
494
|
+
const tags = ev.tags.map((t) => t.name).join(", ");
|
|
495
|
+
table.push([ev.entity_id, ev.display_name, ev.file_type, tags]);
|
|
496
|
+
}
|
|
497
|
+
console.log(table.toString());
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
// src/commands/evidence.ts
|
|
501
|
+
function registerEvidenceCommand(program2) {
|
|
502
|
+
const evidence = program2.command("evidence").description("Manage evidence files");
|
|
503
|
+
evidence.command("upload").description("Upload an evidence file").argument("<file_path>", "Path to the file to upload").option("--name <name>", "Display name for the evidence").action(async (filePath, opts) => {
|
|
504
|
+
try {
|
|
505
|
+
if (!existsSync(filePath)) {
|
|
506
|
+
console.error(`\u30D5\u30A1\u30A4\u30EB\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093: ${filePath}`);
|
|
507
|
+
process.exit(1);
|
|
508
|
+
}
|
|
509
|
+
const displayName = opts.name ?? basename2(filePath);
|
|
510
|
+
const result = await apiUpload(
|
|
511
|
+
"/evidence",
|
|
512
|
+
filePath,
|
|
513
|
+
displayName
|
|
514
|
+
);
|
|
515
|
+
if (getConfig().jsonOutput) {
|
|
516
|
+
console.log(JSON.stringify(result, null, 2));
|
|
517
|
+
} else {
|
|
518
|
+
console.log("\u30A2\u30C3\u30D7\u30ED\u30FC\u30C9\u5B8C\u4E86:");
|
|
519
|
+
console.log(` entity_id: ${result.entity_id}`);
|
|
520
|
+
console.log(` display_name: ${result.display_name}`);
|
|
521
|
+
console.log(` file_type: ${result.file_type}`);
|
|
522
|
+
}
|
|
523
|
+
} catch (e) {
|
|
524
|
+
handleError(e);
|
|
525
|
+
}
|
|
526
|
+
});
|
|
527
|
+
evidence.command("search").description("Search evidence files").option("--name <text>", "Search by display name (partial match)").option("--tag <tag>", "Search by tag name").action(async (opts) => {
|
|
528
|
+
try {
|
|
529
|
+
if (!opts.name && !opts.tag) {
|
|
530
|
+
console.error("--name \u307E\u305F\u306F --tag \u306E\u3044\u305A\u308C\u304B\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002");
|
|
531
|
+
process.exit(1);
|
|
532
|
+
}
|
|
533
|
+
const params = {};
|
|
534
|
+
if (opts.name) params.display_name = opts.name;
|
|
535
|
+
if (opts.tag) params.tag = opts.tag;
|
|
536
|
+
const results = await apiRequest(
|
|
537
|
+
"get",
|
|
538
|
+
"/evidence/search",
|
|
539
|
+
{ params }
|
|
540
|
+
);
|
|
541
|
+
if (getConfig().jsonOutput) {
|
|
542
|
+
console.log(JSON.stringify(results, null, 2));
|
|
543
|
+
} else {
|
|
544
|
+
formatEvidenceList(results);
|
|
545
|
+
}
|
|
546
|
+
} catch (e) {
|
|
547
|
+
handleError(e);
|
|
548
|
+
}
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// src/lib/formatters/reports.ts
|
|
553
|
+
import chalk2 from "chalk";
|
|
554
|
+
function formatPL(pl) {
|
|
555
|
+
console.log(`
|
|
556
|
+
=== ${pl.year}\u5E74\u5EA6 \u640D\u76CA\u8A08\u7B97\u66F8 (P/L) ===
|
|
557
|
+
`);
|
|
558
|
+
for (const item of pl.items) {
|
|
559
|
+
const depth = item.relative_depth ?? 0;
|
|
560
|
+
const indent = " ".repeat(depth);
|
|
561
|
+
const nameWidth = 30 - depth * 2;
|
|
562
|
+
if (item.is_total) {
|
|
563
|
+
console.log(` ${"\u2500".repeat(30)} ${"\u2500".repeat(12)}`);
|
|
564
|
+
console.log(
|
|
565
|
+
` ${indent}${item.account_name.padEnd(nameWidth)} ${item.amount.toLocaleString().padStart(12)}`
|
|
566
|
+
);
|
|
567
|
+
} else if (item.is_abstract) {
|
|
568
|
+
console.log(`
|
|
569
|
+
${indent}\u3010${item.account_name}\u3011`);
|
|
570
|
+
} else {
|
|
571
|
+
console.log(
|
|
572
|
+
` ${indent}${item.account_name.padEnd(nameWidth)} ${item.amount.toLocaleString().padStart(12)}`
|
|
573
|
+
);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
console.log(`
|
|
577
|
+
${"\u2501".repeat(30)} ${"\u2501".repeat(12)}`);
|
|
578
|
+
console.log(
|
|
579
|
+
` ${"\u5F53\u671F\u7D14\u5229\u76CA".padEnd(30)} ${(pl.net_income ?? 0).toLocaleString().padStart(12)}`
|
|
580
|
+
);
|
|
581
|
+
console.log();
|
|
582
|
+
}
|
|
583
|
+
function formatBS(bs) {
|
|
584
|
+
console.log(`
|
|
585
|
+
=== ${bs.year}\u5E74\u5EA6 \u8CB8\u501F\u5BFE\u7167\u8868 (B/S) ===
|
|
586
|
+
`);
|
|
587
|
+
const categoryLabels = {
|
|
588
|
+
asset: "\u8CC7\u7523\u306E\u90E8",
|
|
589
|
+
liability: "\u8CA0\u50B5\u306E\u90E8",
|
|
590
|
+
equity: "\u7D14\u8CC7\u7523\u306E\u90E8"
|
|
591
|
+
};
|
|
592
|
+
for (const section of bs.sections) {
|
|
593
|
+
const label = categoryLabels[section.category] ?? section.category;
|
|
594
|
+
console.log(` \u3010${label}\u3011`);
|
|
595
|
+
for (const item of section.items) {
|
|
596
|
+
const depth = item.relative_depth ?? 0;
|
|
597
|
+
const indent = " ".repeat(depth);
|
|
598
|
+
const nameWidth = 28 - depth * 2;
|
|
599
|
+
if (item.is_total) {
|
|
600
|
+
console.log(` ${"\u2500".repeat(28)} ${"\u2500".repeat(12)}`);
|
|
601
|
+
console.log(
|
|
602
|
+
` ${indent}${item.account_name.padEnd(nameWidth)} ${item.amount.toLocaleString().padStart(12)}`
|
|
603
|
+
);
|
|
604
|
+
} else if (item.is_abstract) {
|
|
605
|
+
console.log(` ${indent}\u3008${item.account_name}\u3009`);
|
|
606
|
+
} else {
|
|
607
|
+
console.log(
|
|
608
|
+
` ${indent}${item.account_name.padEnd(nameWidth)} ${item.amount.toLocaleString().padStart(12)}`
|
|
609
|
+
);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
console.log();
|
|
613
|
+
}
|
|
614
|
+
const balanced = bs.is_balanced ? chalk2.green("OK") : chalk2.red("NG");
|
|
615
|
+
console.log(
|
|
616
|
+
` \u5F53\u671F\u7D14\u5229\u76CA: ${(bs.net_income ?? 0).toLocaleString().padStart(12)}`
|
|
617
|
+
);
|
|
618
|
+
console.log(` \u8CB8\u501F\u4E00\u81F4: ${balanced}`);
|
|
619
|
+
console.log();
|
|
620
|
+
}
|
|
621
|
+
function formatFixedAssets(assets, depResults, depYear) {
|
|
622
|
+
if (assets.length === 0) {
|
|
623
|
+
console.log("\u56FA\u5B9A\u8CC7\u7523\u306E\u767B\u9332\u306F\u3042\u308A\u307E\u305B\u3093\u3002");
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
626
|
+
console.log(`
|
|
627
|
+
=== \u56FA\u5B9A\u8CC7\u7523\u4E00\u89A7 (${assets.length} \u4EF6) ===
|
|
628
|
+
`);
|
|
629
|
+
let totalDepreciation = 0;
|
|
630
|
+
for (const asset of assets) {
|
|
631
|
+
const method = asset.depreciation_method === "straight_line" ? "\u5B9A\u984D\u6CD5" : "\u5B9A\u7387\u6CD5";
|
|
632
|
+
console.log(` \u8CC7\u7523\u540D\u79F0: ${asset.name}`);
|
|
633
|
+
console.log(` entity_id: ${asset.entity_id}`);
|
|
634
|
+
console.log(` \u53D6\u5F97\u65E5: ${asset.acquisition_date}`);
|
|
635
|
+
console.log(
|
|
636
|
+
` \u53D6\u5F97\u4FA1\u984D: ${asset.acquisition_cost.toLocaleString().padStart(12)}`
|
|
637
|
+
);
|
|
638
|
+
console.log(` \u8010\u7528\u5E74\u6570: ${asset.useful_life} \u5E74`);
|
|
639
|
+
console.log(` \u511F\u5374\u65B9\u6CD5: ${method}`);
|
|
640
|
+
const dep = depResults?.get(asset.entity_id);
|
|
641
|
+
if (dep && depYear) {
|
|
642
|
+
console.log(` --- ${depYear}\u5E74\u5EA6 \u6E1B\u4FA1\u511F\u5374 ---`);
|
|
643
|
+
console.log(
|
|
644
|
+
` \u672C\u5E74\u5206\u511F\u5374\u8CBB: ${dep.depreciation_expense.toLocaleString().padStart(12)}`
|
|
645
|
+
);
|
|
646
|
+
console.log(
|
|
647
|
+
` \u7D2F\u8A08\u511F\u5374\u984D: ${dep.accumulated_depreciation.toLocaleString().padStart(12)}`
|
|
648
|
+
);
|
|
649
|
+
console.log(
|
|
650
|
+
` \u671F\u672B\u5E33\u7C3F\u4FA1\u984D: ${dep.book_value.toLocaleString().padStart(12)}`
|
|
651
|
+
);
|
|
652
|
+
totalDepreciation += dep.depreciation_expense;
|
|
653
|
+
}
|
|
654
|
+
const depJournals = asset.depreciation_journals ?? [];
|
|
655
|
+
if (depJournals.length > 0) {
|
|
656
|
+
console.log(` \u511F\u5374\u4ED5\u8A33: ${depJournals.length} \u4EF6`);
|
|
657
|
+
}
|
|
658
|
+
console.log();
|
|
659
|
+
}
|
|
660
|
+
if (depYear && depResults) {
|
|
661
|
+
console.log(` ${"\u2501".repeat(40)}`);
|
|
662
|
+
console.log(
|
|
663
|
+
` \u672C\u5E74\u5206\u6E1B\u4FA1\u511F\u5374\u8CBB \u5408\u8A08: ${totalDepreciation.toLocaleString().padStart(12)}`
|
|
664
|
+
);
|
|
665
|
+
console.log();
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
// src/commands/reports.ts
|
|
670
|
+
function registerReportsCommand(program2) {
|
|
671
|
+
const reports = program2.command("reports").description("View financial reports");
|
|
672
|
+
reports.command("pl").description("Profit & Loss statement").argument("<year>", "Fiscal year").action(async (yearStr) => {
|
|
673
|
+
try {
|
|
674
|
+
const year = parseInt(yearStr, 10);
|
|
675
|
+
const pl = await apiRequest(
|
|
676
|
+
"get",
|
|
677
|
+
`/financial-statements/pl/${year}`
|
|
678
|
+
);
|
|
679
|
+
if (getConfig().jsonOutput) {
|
|
680
|
+
console.log(JSON.stringify(pl, null, 2));
|
|
681
|
+
} else {
|
|
682
|
+
formatPL(pl);
|
|
683
|
+
}
|
|
684
|
+
} catch (e) {
|
|
685
|
+
handleError(e);
|
|
686
|
+
}
|
|
687
|
+
});
|
|
688
|
+
reports.command("bs").description("Balance Sheet").argument("<year>", "Fiscal year").action(async (yearStr) => {
|
|
689
|
+
try {
|
|
690
|
+
const year = parseInt(yearStr, 10);
|
|
691
|
+
const bs = await apiRequest(
|
|
692
|
+
"get",
|
|
693
|
+
`/financial-statements/bs/${year}`
|
|
694
|
+
);
|
|
695
|
+
if (getConfig().jsonOutput) {
|
|
696
|
+
console.log(JSON.stringify(bs, null, 2));
|
|
697
|
+
} else {
|
|
698
|
+
formatBS(bs);
|
|
699
|
+
}
|
|
700
|
+
} catch (e) {
|
|
701
|
+
handleError(e);
|
|
702
|
+
}
|
|
703
|
+
});
|
|
704
|
+
reports.command("summary").description("P/L and B/S summary").argument("<year>", "Fiscal year").option("--type <type>", "Report type: pl, bs, both", "both").action(async (yearStr, opts) => {
|
|
705
|
+
try {
|
|
706
|
+
const year = parseInt(yearStr, 10);
|
|
707
|
+
if (opts.type === "pl" || opts.type === "both") {
|
|
708
|
+
const pl = await apiRequest(
|
|
709
|
+
"get",
|
|
710
|
+
`/financial-statements/pl/${year}`
|
|
711
|
+
);
|
|
712
|
+
if (getConfig().jsonOutput) {
|
|
713
|
+
console.log(JSON.stringify(pl, null, 2));
|
|
714
|
+
} else {
|
|
715
|
+
formatPL(pl);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
if (opts.type === "bs" || opts.type === "both") {
|
|
719
|
+
const bs = await apiRequest(
|
|
720
|
+
"get",
|
|
721
|
+
`/financial-statements/bs/${year}`
|
|
722
|
+
);
|
|
723
|
+
if (getConfig().jsonOutput) {
|
|
724
|
+
console.log(JSON.stringify(bs, null, 2));
|
|
725
|
+
} else {
|
|
726
|
+
formatBS(bs);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
} catch (e) {
|
|
730
|
+
handleError(e);
|
|
731
|
+
}
|
|
732
|
+
});
|
|
733
|
+
reports.command("assets").description("Fixed assets and depreciation").option("--depreciation <year>", "Calculate depreciation for the given year").action(async (opts) => {
|
|
734
|
+
try {
|
|
735
|
+
const assets = await apiRequest("get", "/fixed-assets");
|
|
736
|
+
let depResults;
|
|
737
|
+
let depYear;
|
|
738
|
+
if (opts.depreciation) {
|
|
739
|
+
depYear = parseInt(opts.depreciation, 10);
|
|
740
|
+
depResults = /* @__PURE__ */ new Map();
|
|
741
|
+
for (const asset of assets) {
|
|
742
|
+
const dep = await apiRequest(
|
|
743
|
+
"get",
|
|
744
|
+
`/fixed-assets/${asset.entity_id}/depreciation`,
|
|
745
|
+
{ params: { target_year: String(depYear) } }
|
|
746
|
+
);
|
|
747
|
+
depResults.set(asset.entity_id, dep);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
if (getConfig().jsonOutput) {
|
|
751
|
+
const output = depResults ? { assets, depreciation: Object.fromEntries(depResults) } : { assets };
|
|
752
|
+
console.log(JSON.stringify(output, null, 2));
|
|
753
|
+
} else {
|
|
754
|
+
formatFixedAssets(assets, depResults, depYear);
|
|
755
|
+
}
|
|
756
|
+
} catch (e) {
|
|
757
|
+
handleError(e);
|
|
758
|
+
}
|
|
759
|
+
});
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
// src/cli.ts
|
|
763
|
+
function createProgram() {
|
|
764
|
+
const program2 = new Command();
|
|
765
|
+
program2.name("kakutey").description("CLI for the kakutey bookkeeping application").version("0.1.0").option(
|
|
766
|
+
"--base-url <url>",
|
|
767
|
+
"API base URL",
|
|
768
|
+
process.env.KAKUTEY_API_URL || "http://localhost:8000/api"
|
|
769
|
+
).option("--json", "Output raw JSON").hook("preAction", (thisCommand) => {
|
|
770
|
+
const opts = thisCommand.opts();
|
|
771
|
+
setConfig({
|
|
772
|
+
baseUrl: opts.baseUrl,
|
|
773
|
+
jsonOutput: opts.json ?? false
|
|
774
|
+
});
|
|
775
|
+
});
|
|
776
|
+
registerHealthCommand(program2);
|
|
777
|
+
registerAccountsCommand(program2);
|
|
778
|
+
registerJournalsCommand(program2);
|
|
779
|
+
registerEvidenceCommand(program2);
|
|
780
|
+
registerReportsCommand(program2);
|
|
781
|
+
return program2;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
// src/index.ts
|
|
785
|
+
var program = createProgram();
|
|
786
|
+
program.parseAsync(process.argv);
|
|
787
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../src/lib/config.ts","../src/commands/health.ts","../src/lib/client.ts","../src/lib/errors.ts","../src/lib/formatters/accounts.ts","../src/commands/accounts.ts","../src/commands/journals.ts","../src/lib/accounts-resolver.ts","../src/lib/formatters/journals.ts","../src/commands/evidence.ts","../src/lib/formatters/evidence.ts","../src/lib/formatters/reports.ts","../src/commands/reports.ts","../src/index.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { setConfig } from \"./lib/config.js\";\nimport { registerHealthCommand } from \"./commands/health.js\";\nimport { registerAccountsCommand } from \"./commands/accounts.js\";\nimport { registerJournalsCommand } from \"./commands/journals.js\";\nimport { registerEvidenceCommand } from \"./commands/evidence.js\";\nimport { registerReportsCommand } from \"./commands/reports.js\";\n\nexport function createProgram(): Command {\n const program = new Command();\n\n program\n .name(\"kakutey\")\n .description(\"CLI for the kakutey bookkeeping application\")\n .version(\"0.1.0\")\n .option(\n \"--base-url <url>\",\n \"API base URL\",\n process.env.KAKUTEY_API_URL || \"http://localhost:8000/api\",\n )\n .option(\"--json\", \"Output raw JSON\")\n .hook(\"preAction\", (thisCommand) => {\n const opts = thisCommand.opts();\n setConfig({\n baseUrl: opts.baseUrl,\n jsonOutput: opts.json ?? false,\n });\n });\n\n registerHealthCommand(program);\n registerAccountsCommand(program);\n registerJournalsCommand(program);\n registerEvidenceCommand(program);\n registerReportsCommand(program);\n\n return program;\n}\n","export interface Config {\n baseUrl: string;\n jsonOutput: boolean;\n}\n\nlet currentConfig: Config = {\n baseUrl: process.env.KAKUTEY_API_URL || \"http://localhost:8000/api\",\n jsonOutput: false,\n};\n\nexport function setConfig(partial: Partial<Config>): void {\n currentConfig = { ...currentConfig, ...partial };\n}\n\nexport function getConfig(): Config {\n return currentConfig;\n}\n","import { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport net from \"node:net\";\n\nfunction checkPort(host: string, port: number, timeout = 2000): Promise<boolean> {\n return new Promise((resolve) => {\n const sock = new net.Socket();\n sock.setTimeout(timeout);\n sock.on(\"connect\", () => {\n sock.destroy();\n resolve(true);\n });\n sock.on(\"timeout\", () => {\n sock.destroy();\n resolve(false);\n });\n sock.on(\"error\", () => {\n sock.destroy();\n resolve(false);\n });\n sock.connect(port, host);\n });\n}\n\nasync function checkHttp(url: string, timeout = 5000): Promise<number | null> {\n try {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeout);\n const resp = await fetch(url, { signal: controller.signal });\n clearTimeout(timer);\n return resp.status;\n } catch {\n return null;\n }\n}\n\nexport function registerHealthCommand(program: Command): void {\n program\n .command(\"health\")\n .description(\"Check kakutey backend and frontend status\")\n .action(async () => {\n let exitCode = 0;\n\n // Backend (port 8000)\n const backendUp = await checkPort(\"localhost\", 8000);\n if (!backendUp) {\n console.log(\n `Backend (port 8000): ${chalk.red(\"NG\")} - nothing listening`,\n );\n exitCode = 1;\n } else {\n const status = await checkHttp(\n \"http://localhost:8000/api/fiscal-year\",\n );\n if (status === 200) {\n console.log(\n `Backend (port 8000): ${chalk.green(\"OK\")} - API responding`,\n );\n } else {\n console.log(\n `Backend (port 8000): ${chalk.red(\"NG\")} - port in use but API not responding (HTTP ${status})`,\n );\n exitCode = 1;\n }\n }\n\n // Frontend (port 4200)\n const frontendUp = await checkPort(\"localhost\", 4200);\n if (!frontendUp) {\n console.log(\n `Frontend (port 4200): ${chalk.red(\"NG\")} - nothing listening`,\n );\n exitCode = 1;\n } else {\n console.log(\n `Frontend (port 4200): ${chalk.green(\"OK\")} - listening`,\n );\n }\n\n process.exit(exitCode);\n });\n}\n","import { readFileSync } from \"node:fs\";\nimport { basename } from \"node:path\";\nimport { getConfig } from \"./config.js\";\nimport { ApiError } from \"./errors.js\";\n\nexport async function apiRequest<T>(\n method: string,\n path: string,\n options?: {\n body?: unknown;\n params?: Record<string, string>;\n },\n): Promise<T> {\n const config = getConfig();\n const url = new URL(`${config.baseUrl}${path}`);\n if (options?.params) {\n for (const [key, value] of Object.entries(options.params)) {\n url.searchParams.set(key, value);\n }\n }\n\n const response = await fetch(url.toString(), {\n method: method.toUpperCase(),\n headers: options?.body\n ? { \"Content-Type\": \"application/json\" }\n : undefined,\n body: options?.body ? JSON.stringify(options.body) : undefined,\n });\n\n if (!response.ok) {\n const detail = await response.text().catch(() => response.statusText);\n throw new ApiError(method, path, response.status, detail);\n }\n\n return response.json() as Promise<T>;\n}\n\nexport async function apiUpload<T>(\n path: string,\n filePath: string,\n displayName: string,\n): Promise<T> {\n const config = getConfig();\n const url = `${config.baseUrl}${path}`;\n const fileBuffer = readFileSync(filePath);\n const fileName = basename(filePath);\n\n const formData = new FormData();\n formData.append(\"file\", new Blob([fileBuffer]), fileName);\n formData.append(\"display_name\", displayName);\n\n const response = await fetch(url, {\n method: \"POST\",\n body: formData,\n });\n\n if (!response.ok) {\n const detail = await response.text().catch(() => response.statusText);\n throw new ApiError(\"POST\", path, response.status, detail);\n }\n\n return response.json() as Promise<T>;\n}\n","export class ApiError extends Error {\n constructor(\n public readonly method: string,\n public readonly path: string,\n public readonly statusCode: number,\n public readonly detail: string,\n ) {\n super(\n `API ${method.toUpperCase()} ${path} → ${statusCode}: ${detail.slice(0, 500)}`,\n );\n this.name = \"ApiError\";\n }\n}\n\nexport class ValidationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ValidationError\";\n }\n}\n\nexport function handleError(error: unknown): never {\n if (error instanceof ApiError) {\n console.error(`API Error: ${error.message}`);\n } else if (error instanceof ValidationError) {\n console.error(`Validation Error: ${error.message}`);\n } else if (error instanceof Error) {\n console.error(`Error: ${error.message}`);\n } else {\n console.error(\"Unknown error\", error);\n }\n process.exit(1);\n}\n","import Table from \"cli-table3\";\nimport type { Account } from \"../types/account.js\";\n\nexport function formatAccountsList(accounts: Account[]): void {\n const table = new Table({\n head: [\"entity_id\", \"code\", \"name\", \"category\"],\n colAligns: [\"right\", \"left\", \"left\", \"left\"],\n });\n\n for (const a of accounts) {\n if (a.is_abstract) continue;\n table.push([a.entity_id, a.code ?? \"\", a.name, a.category]);\n }\n\n console.log(table.toString());\n console.log(`\\n合計: ${accounts.length} 件`);\n}\n","import { Command } from \"commander\";\nimport { apiRequest } from \"../lib/client.js\";\nimport { handleError } from \"../lib/errors.js\";\nimport { getConfig } from \"../lib/config.js\";\nimport { formatAccountsList } from \"../lib/formatters/accounts.js\";\nimport type { Account } from \"../lib/types/account.js\";\n\nexport function registerAccountsCommand(program: Command): void {\n const accounts = program\n .command(\"accounts\")\n .description(\"Manage chart of accounts\");\n\n accounts\n .command(\"list\")\n .description(\"List chart of accounts\")\n .option(\n \"--category <category>\",\n \"Filter by category (asset|liability|equity|revenue|expense)\",\n )\n .action(async (opts: { category?: string }) => {\n try {\n let result = await apiRequest<Account[]>(\"get\", \"/accounts\");\n\n if (opts.category) {\n result = result.filter((a) => a.category === opts.category);\n }\n\n result.sort((a, b) => {\n const codeA = a.code ?? \"\";\n const codeB = b.code ?? \"\";\n return codeA.localeCompare(codeB) || a.name.localeCompare(b.name);\n });\n\n if (getConfig().jsonOutput) {\n console.log(JSON.stringify(result, null, 2));\n } else {\n formatAccountsList(result);\n }\n } catch (e) {\n handleError(e);\n }\n });\n}\n","import { readFileSync } from \"node:fs\";\nimport { Command } from \"commander\";\nimport { apiRequest } from \"../lib/client.js\";\nimport {\n buildAccountNameToIdMap,\n buildAccountIdToNameMap,\n resolveAccountId,\n} from \"../lib/accounts-resolver.js\";\nimport { handleError } from \"../lib/errors.js\";\nimport { getConfig } from \"../lib/config.js\";\nimport { formatJournalsList } from \"../lib/formatters/journals.js\";\nimport type {\n JournalCreate,\n JournalRead,\n JournalLine,\n} from \"../lib/types/journal.js\";\n\ninterface SimpleJournalInput {\n date: string;\n description: string;\n debit_account: string;\n credit_account: string;\n amount: number;\n evidence_ids?: number[];\n}\n\ninterface CompoundJournalInput {\n date: string;\n description: string;\n lines: Array<{ side: \"debit\" | \"credit\"; account: string; amount: number }>;\n evidence_ids?: number[];\n}\n\ninterface UpdateInput {\n entity_id: number;\n date: string;\n description: string;\n lines: Array<{\n side: \"debit\" | \"credit\";\n account?: string;\n account_id?: number;\n amount: number;\n }>;\n evidence_ids?: number[];\n expected_revision: number;\n}\n\nfunction isCompoundInput(data: unknown): data is CompoundJournalInput {\n return typeof data === \"object\" && data !== null && \"lines\" in data;\n}\n\nasync function buildPayload(\n data: SimpleJournalInput | CompoundJournalInput,\n accMap: Map<string, number>,\n): Promise<JournalCreate> {\n if (isCompoundInput(data)) {\n const lines: JournalLine[] = data.lines.map((l) => ({\n side: l.side,\n account_id: resolveAccountId(accMap, l.account),\n amount: l.amount,\n }));\n return {\n date: data.date,\n description: data.description,\n lines,\n evidence_ids: data.evidence_ids ?? [],\n };\n }\n\n const drId = resolveAccountId(accMap, data.debit_account);\n const crId = resolveAccountId(accMap, data.credit_account);\n return {\n date: data.date,\n description: data.description,\n lines: [\n { side: \"debit\", account_id: drId, amount: data.amount },\n { side: \"credit\", account_id: crId, amount: data.amount },\n ],\n evidence_ids: data.evidence_ids ?? [],\n };\n}\n\nexport function registerJournalsCommand(program: Command): void {\n const journals = program\n .command(\"journals\")\n .description(\"Manage journal entries\");\n\n // --- list ---\n journals\n .command(\"list\")\n .description(\"List journals for a date range\")\n .argument(\"<start_date>\", \"Start date (YYYY-MM-DD)\")\n .argument(\"<end_date>\", \"End date (YYYY-MM-DD)\")\n .action(async (startDate: string, endDate: string) => {\n try {\n const [result, accMap] = await Promise.all([\n apiRequest<JournalRead[]>(\"get\", \"/journals\", {\n params: { start_date: startDate, end_date: endDate },\n }),\n buildAccountIdToNameMap(),\n ]);\n result.sort((a, b) => a.journal_date.localeCompare(b.journal_date));\n\n if (getConfig().jsonOutput) {\n console.log(JSON.stringify(result, null, 2));\n } else {\n formatJournalsList(result, accMap, startDate, endDate);\n }\n } catch (e) {\n handleError(e);\n }\n });\n\n // --- add ---\n journals\n .command(\"add\")\n .description(\"Add a single journal entry\")\n .argument(\"[json]\", \"Journal data as JSON string\")\n .option(\"--file <path>\", \"Read journal data from a JSON file\")\n .action(async (jsonStr: string | undefined, opts: { file?: string }) => {\n try {\n let data: SimpleJournalInput | CompoundJournalInput;\n if (opts.file) {\n data = JSON.parse(readFileSync(opts.file, \"utf-8\"));\n } else if (jsonStr) {\n data = JSON.parse(jsonStr);\n } else {\n console.error(\"Provide JSON as argument or use --file\");\n process.exit(1);\n }\n\n const accMap = await buildAccountNameToIdMap();\n const payload = await buildPayload(data, accMap);\n const result = await apiRequest<JournalRead>(\"post\", \"/journals\", {\n body: payload,\n });\n\n if (getConfig().jsonOutput) {\n console.log(JSON.stringify(result, null, 2));\n } else {\n console.log(\n `登録完了: entity_id=${result.entity_id}, revision=${result.revision}`,\n );\n }\n } catch (e) {\n handleError(e);\n }\n });\n\n // --- bulk-add ---\n journals\n .command(\"bulk-add\")\n .description(\"Bulk add journals from a JSON file\")\n .argument(\"<file>\", \"Path to JSON file containing array of journal entries\")\n .action(async (file: string) => {\n try {\n const entries: Array<SimpleJournalInput | CompoundJournalInput> =\n JSON.parse(readFileSync(file, \"utf-8\"));\n\n console.log(`=== ${entries.length} 件の仕訳を登録 ===`);\n const accMap = await buildAccountNameToIdMap();\n\n let created = 0;\n const errors: Array<[number, string, string]> = [];\n\n for (let i = 0; i < entries.length; i++) {\n try {\n const payload = await buildPayload(entries[i], accMap);\n await apiRequest(\"post\", \"/journals\", { body: payload });\n created++;\n } catch (e) {\n const desc =\n \"description\" in entries[i]\n ? (entries[i].description as string)\n : \"?\";\n errors.push([i + 1, desc, String(e)]);\n }\n\n if ((i + 1) % 20 === 0 || i + 1 === entries.length) {\n console.log(\n ` [${i + 1}/${entries.length}] 登録: ${created}, エラー: ${errors.length}`,\n );\n }\n }\n\n console.log(`\\n完了: 登録=${created}, エラー=${errors.length}`);\n if (errors.length > 0) {\n console.log(\"\\nエラー一覧:\");\n for (const [idx, desc, msg] of errors) {\n console.log(` #${idx} (${desc}): ${msg}`);\n }\n }\n } catch (e) {\n handleError(e);\n }\n });\n\n // --- update ---\n journals\n .command(\"update\")\n .description(\"Full update a journal entry (with optimistic lock)\")\n .argument(\"<json>\", \"Update data as JSON string\")\n .action(async (jsonStr: string) => {\n try {\n const data: UpdateInput = JSON.parse(jsonStr);\n const accMap = await buildAccountNameToIdMap();\n\n const lines: JournalLine[] = data.lines.map((l) => {\n if (l.account_id !== undefined) {\n return {\n side: l.side,\n account_id: l.account_id,\n amount: l.amount,\n };\n }\n return {\n side: l.side,\n account_id: resolveAccountId(accMap, l.account!),\n amount: l.amount,\n };\n });\n\n const payload = {\n date: data.date,\n description: data.description,\n lines,\n evidence_ids: data.evidence_ids ?? [],\n expected_revision: data.expected_revision,\n };\n\n const result = await apiRequest<JournalRead>(\n \"put\",\n `/journals/${data.entity_id}`,\n { body: payload },\n );\n\n if (getConfig().jsonOutput) {\n console.log(JSON.stringify(result, null, 2));\n } else {\n console.log(\n `更新完了: entity_id=${result.entity_id}, revision=${result.revision}`,\n );\n }\n } catch (e) {\n handleError(e);\n }\n });\n\n // --- delete ---\n journals\n .command(\"delete\")\n .description(\"Soft-delete a journal entry\")\n .argument(\"<entity_id>\", \"Journal entity ID\")\n .action(async (entityIdStr: string) => {\n try {\n const entityId = parseInt(entityIdStr, 10);\n await apiRequest(\"delete\", `/journals/${entityId}`);\n console.log(`削除完了: entity_id=${entityId}`);\n } catch (e) {\n handleError(e);\n }\n });\n\n // --- attach ---\n journals\n .command(\"attach\")\n .description(\"Attach evidence IDs to a journal\")\n .argument(\"<entity_id>\", \"Journal entity ID\")\n .argument(\"<evidence_ids...>\", \"Evidence entity IDs to attach\")\n .action(async (entityIdStr: string, evidenceIdStrs: string[]) => {\n try {\n const entityId = parseInt(entityIdStr, 10);\n const newIds = evidenceIdStrs.map(Number);\n\n // Fetch current journal\n const journals = await apiRequest<JournalRead[]>(\"get\", \"/journals\");\n const journal = journals.find((j) => j.entity_id === entityId);\n if (!journal) {\n console.error(`仕訳が見つかりません: entity_id=${entityId}`);\n process.exit(1);\n }\n\n const existingIds = journal.evidence_ids ?? [];\n const mergedIds = [...new Set([...existingIds, ...newIds])];\n\n const payload = {\n date: journal.journal_date,\n description: journal.description,\n lines: journal.lines.map((l) => ({\n side: l.side,\n account_id: l.account_id,\n amount: l.amount,\n })),\n evidence_ids: mergedIds,\n expected_revision: journal.revision,\n };\n\n const result = await apiRequest<JournalRead>(\n \"put\",\n `/journals/${entityId}`,\n { body: payload },\n );\n\n if (getConfig().jsonOutput) {\n console.log(JSON.stringify(result, null, 2));\n } else {\n console.log(\n `証憑追加完了: entity_id=${result.entity_id}, revision=${result.revision}`,\n );\n console.log(` evidence_ids: [${result.evidence_ids.join(\", \")}]`);\n }\n } catch (e) {\n handleError(e);\n }\n });\n}\n","import { apiRequest } from \"./client.js\";\nimport { ValidationError } from \"./errors.js\";\nimport type { Account } from \"./types/account.js\";\n\nexport async function buildAccountNameToIdMap(): Promise<Map<string, number>> {\n const accounts = await apiRequest<Account[]>(\"get\", \"/accounts\");\n return new Map(accounts.map((a) => [a.name, a.entity_id]));\n}\n\nexport async function buildAccountIdToNameMap(): Promise<Map<number, string>> {\n const accounts = await apiRequest<Account[]>(\"get\", \"/accounts\");\n return new Map(accounts.map((a) => [a.entity_id, a.name]));\n}\n\nexport function resolveAccountId(\n accountMap: Map<string, number>,\n name: string,\n): number {\n const id = accountMap.get(name);\n if (id === undefined) {\n throw new ValidationError(`勘定科目が見つかりません: ${name}`);\n }\n return id;\n}\n","import Table from \"cli-table3\";\nimport type { JournalRead } from \"../types/journal.js\";\n\nexport function formatJournalsList(\n journals: JournalRead[],\n accMap: Map<number, string>,\n startDate: string,\n endDate: string,\n): void {\n console.log(\n `\\n仕訳一覧: ${startDate} 〜 ${endDate} (${journals.length} 件)`,\n );\n\n const table = new Table({\n head: [\"EID\", \"Date\", \"EV\", \"Description\", \"Debit / Credit\", \"Amount\"],\n colAligns: [\"right\", \"left\", \"center\", \"left\", \"left\", \"right\"],\n });\n\n for (const j of journals) {\n const debits = j.lines.filter((l) => l.side === \"debit\");\n const credits = j.lines.filter((l) => l.side === \"credit\");\n const evidence = j.evidence_ids.length > 0 ? \"Y\" : \"\";\n\n if (debits.length === 1 && credits.length === 1) {\n const drName = accMap.get(debits[0].account_id) ?? `?${debits[0].account_id}`;\n const crName = accMap.get(credits[0].account_id) ?? `?${credits[0].account_id}`;\n table.push([\n j.entity_id,\n j.journal_date,\n evidence,\n j.description.slice(0, 30),\n `${drName} / ${crName}`,\n debits[0].amount.toLocaleString(),\n ]);\n } else {\n table.push([\n j.entity_id,\n j.journal_date,\n evidence,\n j.description.slice(0, 30),\n \"\",\n \"\",\n ]);\n for (const d of debits) {\n const name = accMap.get(d.account_id) ?? `?${d.account_id}`;\n table.push([\"\", \"\", \"\", \"\", ` 借方 ${name}`, d.amount.toLocaleString()]);\n }\n for (const c of credits) {\n const name = accMap.get(c.account_id) ?? `?${c.account_id}`;\n table.push([\"\", \"\", \"\", \"\", ` 貸方 ${name}`, c.amount.toLocaleString()]);\n }\n }\n }\n\n console.log(table.toString());\n}\n","import { basename } from \"node:path\";\nimport { existsSync } from \"node:fs\";\nimport { Command } from \"commander\";\nimport { apiRequest, apiUpload } from \"../lib/client.js\";\nimport { handleError } from \"../lib/errors.js\";\nimport { getConfig } from \"../lib/config.js\";\nimport { formatEvidenceList } from \"../lib/formatters/evidence.js\";\nimport type { EvidenceRead } from \"../lib/types/evidence.js\";\n\nexport function registerEvidenceCommand(program: Command): void {\n const evidence = program\n .command(\"evidence\")\n .description(\"Manage evidence files\");\n\n // --- upload ---\n evidence\n .command(\"upload\")\n .description(\"Upload an evidence file\")\n .argument(\"<file_path>\", \"Path to the file to upload\")\n .option(\"--name <name>\", \"Display name for the evidence\")\n .action(async (filePath: string, opts: { name?: string }) => {\n try {\n if (!existsSync(filePath)) {\n console.error(`ファイルが見つかりません: ${filePath}`);\n process.exit(1);\n }\n\n const displayName = opts.name ?? basename(filePath);\n const result = await apiUpload<EvidenceRead>(\n \"/evidence\",\n filePath,\n displayName,\n );\n\n if (getConfig().jsonOutput) {\n console.log(JSON.stringify(result, null, 2));\n } else {\n console.log(\"アップロード完了:\");\n console.log(` entity_id: ${result.entity_id}`);\n console.log(` display_name: ${result.display_name}`);\n console.log(` file_type: ${result.file_type}`);\n }\n } catch (e) {\n handleError(e);\n }\n });\n\n // --- search ---\n evidence\n .command(\"search\")\n .description(\"Search evidence files\")\n .option(\"--name <text>\", \"Search by display name (partial match)\")\n .option(\"--tag <tag>\", \"Search by tag name\")\n .action(async (opts: { name?: string; tag?: string }) => {\n try {\n if (!opts.name && !opts.tag) {\n console.error(\"--name または --tag のいずれかを指定してください。\");\n process.exit(1);\n }\n\n const params: Record<string, string> = {};\n if (opts.name) params.display_name = opts.name;\n if (opts.tag) params.tag = opts.tag;\n\n const results = await apiRequest<EvidenceRead[]>(\n \"get\",\n \"/evidence/search\",\n { params },\n );\n\n if (getConfig().jsonOutput) {\n console.log(JSON.stringify(results, null, 2));\n } else {\n formatEvidenceList(results);\n }\n } catch (e) {\n handleError(e);\n }\n });\n}\n","import Table from \"cli-table3\";\nimport type { EvidenceRead } from \"../types/evidence.js\";\n\nexport function formatEvidenceList(results: EvidenceRead[]): void {\n console.log(`\\n検索結果: ${results.length} 件`);\n\n const table = new Table({\n head: [\"EID\", \"display_name\", \"file_type\", \"tags\"],\n colAligns: [\"right\", \"left\", \"left\", \"left\"],\n });\n\n for (const ev of results) {\n const tags = ev.tags.map((t) => t.name).join(\", \");\n table.push([ev.entity_id, ev.display_name, ev.file_type, tags]);\n }\n\n console.log(table.toString());\n}\n","import chalk from \"chalk\";\nimport type {\n PLReport,\n BSReport,\n FixedAsset,\n DepreciationResult,\n} from \"../types/reports.js\";\n\nexport function formatPL(pl: PLReport): void {\n console.log(`\\n=== ${pl.year}年度 損益計算書 (P/L) ===\\n`);\n\n for (const item of pl.items) {\n const depth = item.relative_depth ?? 0;\n const indent = \" \".repeat(depth);\n const nameWidth = 30 - depth * 2;\n\n if (item.is_total) {\n console.log(` ${\"─\".repeat(30)} ${\"─\".repeat(12)}`);\n console.log(\n ` ${indent}${item.account_name.padEnd(nameWidth)} ${item.amount.toLocaleString().padStart(12)}`,\n );\n } else if (item.is_abstract) {\n console.log(`\\n ${indent}【${item.account_name}】`);\n } else {\n console.log(\n ` ${indent}${item.account_name.padEnd(nameWidth)} ${item.amount.toLocaleString().padStart(12)}`,\n );\n }\n }\n\n console.log(`\\n ${\"━\".repeat(30)} ${\"━\".repeat(12)}`);\n console.log(\n ` ${\"当期純利益\".padEnd(30)} ${(pl.net_income ?? 0).toLocaleString().padStart(12)}`,\n );\n console.log();\n}\n\nexport function formatBS(bs: BSReport): void {\n console.log(`\\n=== ${bs.year}年度 貸借対照表 (B/S) ===\\n`);\n\n const categoryLabels: Record<string, string> = {\n asset: \"資産の部\",\n liability: \"負債の部\",\n equity: \"純資産の部\",\n };\n\n for (const section of bs.sections) {\n const label = categoryLabels[section.category] ?? section.category;\n console.log(` 【${label}】`);\n\n for (const item of section.items) {\n const depth = item.relative_depth ?? 0;\n const indent = \" \".repeat(depth);\n const nameWidth = 28 - depth * 2;\n\n if (item.is_total) {\n console.log(` ${\"─\".repeat(28)} ${\"─\".repeat(12)}`);\n console.log(\n ` ${indent}${item.account_name.padEnd(nameWidth)} ${item.amount.toLocaleString().padStart(12)}`,\n );\n } else if (item.is_abstract) {\n console.log(` ${indent}〈${item.account_name}〉`);\n } else {\n console.log(\n ` ${indent}${item.account_name.padEnd(nameWidth)} ${item.amount.toLocaleString().padStart(12)}`,\n );\n }\n }\n\n console.log();\n }\n\n const balanced = bs.is_balanced ? chalk.green(\"OK\") : chalk.red(\"NG\");\n console.log(\n ` 当期純利益: ${(bs.net_income ?? 0).toLocaleString().padStart(12)}`,\n );\n console.log(` 貸借一致: ${balanced}`);\n console.log();\n}\n\nexport function formatFixedAssets(\n assets: FixedAsset[],\n depResults?: Map<number, DepreciationResult>,\n depYear?: number,\n): void {\n if (assets.length === 0) {\n console.log(\"固定資産の登録はありません。\");\n return;\n }\n\n console.log(`\\n=== 固定資産一覧 (${assets.length} 件) ===\\n`);\n\n let totalDepreciation = 0;\n\n for (const asset of assets) {\n const method =\n asset.depreciation_method === \"straight_line\" ? \"定額法\" : \"定率法\";\n console.log(` 資産名称: ${asset.name}`);\n console.log(` entity_id: ${asset.entity_id}`);\n console.log(` 取得日: ${asset.acquisition_date}`);\n console.log(\n ` 取得価額: ${asset.acquisition_cost.toLocaleString().padStart(12)}`,\n );\n console.log(` 耐用年数: ${asset.useful_life} 年`);\n console.log(` 償却方法: ${method}`);\n\n const dep = depResults?.get(asset.entity_id);\n if (dep && depYear) {\n console.log(` --- ${depYear}年度 減価償却 ---`);\n console.log(\n ` 本年分償却費: ${dep.depreciation_expense.toLocaleString().padStart(12)}`,\n );\n console.log(\n ` 累計償却額: ${dep.accumulated_depreciation.toLocaleString().padStart(12)}`,\n );\n console.log(\n ` 期末帳簿価額: ${dep.book_value.toLocaleString().padStart(12)}`,\n );\n totalDepreciation += dep.depreciation_expense;\n }\n\n const depJournals = asset.depreciation_journals ?? [];\n if (depJournals.length > 0) {\n console.log(` 償却仕訳: ${depJournals.length} 件`);\n }\n\n console.log();\n }\n\n if (depYear && depResults) {\n console.log(` ${\"━\".repeat(40)}`);\n console.log(\n ` 本年分減価償却費 合計: ${totalDepreciation.toLocaleString().padStart(12)}`,\n );\n console.log();\n }\n}\n","import { Command } from \"commander\";\nimport { apiRequest } from \"../lib/client.js\";\nimport { handleError } from \"../lib/errors.js\";\nimport { getConfig } from \"../lib/config.js\";\nimport { formatPL, formatBS, formatFixedAssets } from \"../lib/formatters/reports.js\";\nimport type {\n PLReport,\n BSReport,\n FixedAsset,\n DepreciationResult,\n} from \"../lib/types/reports.js\";\n\nexport function registerReportsCommand(program: Command): void {\n const reports = program\n .command(\"reports\")\n .description(\"View financial reports\");\n\n // --- pl ---\n reports\n .command(\"pl\")\n .description(\"Profit & Loss statement\")\n .argument(\"<year>\", \"Fiscal year\")\n .action(async (yearStr: string) => {\n try {\n const year = parseInt(yearStr, 10);\n const pl = await apiRequest<PLReport>(\n \"get\",\n `/financial-statements/pl/${year}`,\n );\n\n if (getConfig().jsonOutput) {\n console.log(JSON.stringify(pl, null, 2));\n } else {\n formatPL(pl);\n }\n } catch (e) {\n handleError(e);\n }\n });\n\n // --- bs ---\n reports\n .command(\"bs\")\n .description(\"Balance Sheet\")\n .argument(\"<year>\", \"Fiscal year\")\n .action(async (yearStr: string) => {\n try {\n const year = parseInt(yearStr, 10);\n const bs = await apiRequest<BSReport>(\n \"get\",\n `/financial-statements/bs/${year}`,\n );\n\n if (getConfig().jsonOutput) {\n console.log(JSON.stringify(bs, null, 2));\n } else {\n formatBS(bs);\n }\n } catch (e) {\n handleError(e);\n }\n });\n\n // --- summary ---\n reports\n .command(\"summary\")\n .description(\"P/L and B/S summary\")\n .argument(\"<year>\", \"Fiscal year\")\n .option(\"--type <type>\", \"Report type: pl, bs, both\", \"both\")\n .action(async (yearStr: string, opts: { type: string }) => {\n try {\n const year = parseInt(yearStr, 10);\n\n if (opts.type === \"pl\" || opts.type === \"both\") {\n const pl = await apiRequest<PLReport>(\n \"get\",\n `/financial-statements/pl/${year}`,\n );\n if (getConfig().jsonOutput) {\n console.log(JSON.stringify(pl, null, 2));\n } else {\n formatPL(pl);\n }\n }\n\n if (opts.type === \"bs\" || opts.type === \"both\") {\n const bs = await apiRequest<BSReport>(\n \"get\",\n `/financial-statements/bs/${year}`,\n );\n if (getConfig().jsonOutput) {\n console.log(JSON.stringify(bs, null, 2));\n } else {\n formatBS(bs);\n }\n }\n } catch (e) {\n handleError(e);\n }\n });\n\n // --- assets ---\n reports\n .command(\"assets\")\n .description(\"Fixed assets and depreciation\")\n .option(\"--depreciation <year>\", \"Calculate depreciation for the given year\")\n .action(async (opts: { depreciation?: string }) => {\n try {\n const assets = await apiRequest<FixedAsset[]>(\"get\", \"/fixed-assets\");\n\n let depResults: Map<number, DepreciationResult> | undefined;\n let depYear: number | undefined;\n\n if (opts.depreciation) {\n depYear = parseInt(opts.depreciation, 10);\n depResults = new Map();\n for (const asset of assets) {\n const dep = await apiRequest<DepreciationResult>(\n \"get\",\n `/fixed-assets/${asset.entity_id}/depreciation`,\n { params: { target_year: String(depYear) } },\n );\n depResults.set(asset.entity_id, dep);\n }\n }\n\n if (getConfig().jsonOutput) {\n const output = depResults\n ? { assets, depreciation: Object.fromEntries(depResults) }\n : { assets };\n console.log(JSON.stringify(output, null, 2));\n } else {\n formatFixedAssets(assets, depResults, depYear);\n }\n } catch (e) {\n handleError(e);\n }\n });\n}\n","import { createProgram } from \"./cli.js\";\n\nconst program = createProgram();\nprogram.parseAsync(process.argv);\n"],"mappings":"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","names":["program","program","readFileSync","Table","program","readFileSync","journals","basename","Table","program","basename","chalk","program","program"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "kakutey",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CLI for the kakutey bookkeeping application",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"kakutey": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsup",
|
|
14
|
+
"dev": "tsup --watch",
|
|
15
|
+
"typecheck": "tsc --noEmit",
|
|
16
|
+
"prepublishOnly": "npm run build"
|
|
17
|
+
},
|
|
18
|
+
"engines": {
|
|
19
|
+
"node": ">=18.0.0"
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"chalk": "^5.4.0",
|
|
23
|
+
"cli-table3": "^0.6.5",
|
|
24
|
+
"commander": "^13.0.0"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@types/node": "^22.0.0",
|
|
28
|
+
"tsup": "^8.0.0",
|
|
29
|
+
"typescript": "^5.7.0"
|
|
30
|
+
},
|
|
31
|
+
"keywords": [
|
|
32
|
+
"kakutey",
|
|
33
|
+
"bookkeeping",
|
|
34
|
+
"cli",
|
|
35
|
+
"accounting"
|
|
36
|
+
],
|
|
37
|
+
"license": "MIT"
|
|
38
|
+
}
|