@rubydogjp/openkk-client-domain 26.1.12 → 26.2.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/dist/archive/fiscal-period-archive-zip.d.ts +12 -0
- package/dist/archive/fiscal-period-archive-zip.d.ts.map +1 -0
- package/dist/archive/fiscal-period-archive-zip.js +299 -0
- package/dist/archive/fiscal-period-archive-zip.js.map +1 -0
- package/dist/archive/fiscal-period-archive.d.ts +3 -2
- package/dist/archive/fiscal-period-archive.d.ts.map +1 -1
- package/dist/archive/fiscal-period-archive.js +8 -180
- package/dist/archive/fiscal-period-archive.js.map +1 -1
- package/dist/assist/fixed-asset-data.d.ts +20 -20
- package/dist/assist/fixed-asset-data.d.ts.map +1 -1
- package/dist/assist/fixed-asset-depreciation.d.ts.map +1 -1
- package/dist/assist/fixed-asset-depreciation.js +2 -7
- package/dist/assist/fixed-asset-depreciation.js.map +1 -1
- package/dist/assist/fixed-asset-draft.d.ts +17 -0
- package/dist/assist/fixed-asset-draft.d.ts.map +1 -0
- package/dist/assist/fixed-asset-draft.js +105 -0
- package/dist/assist/fixed-asset-draft.js.map +1 -0
- package/dist/assist/opening-carryover.d.ts +10 -14
- package/dist/assist/opening-carryover.d.ts.map +1 -1
- package/dist/entries/book-account.d.ts +13 -0
- package/dist/entries/book-account.d.ts.map +1 -0
- package/dist/entries/book-account.js +11 -0
- package/dist/entries/book-account.js.map +1 -0
- package/dist/entries/category-resolution.d.ts +5 -0
- package/dist/entries/category-resolution.d.ts.map +1 -0
- package/dist/entries/category-resolution.js +11 -0
- package/dist/entries/category-resolution.js.map +1 -0
- package/dist/entries/default-master-data.d.ts.map +1 -1
- package/dist/entries/default-master-data.js +1 -4
- package/dist/entries/default-master-data.js.map +1 -1
- package/dist/entries/entries-types.d.ts +18 -10
- package/dist/entries/entries-types.d.ts.map +1 -1
- package/dist/entries/entry-limits.d.ts +9 -0
- package/dist/entries/entry-limits.d.ts.map +1 -0
- package/dist/entries/entry-limits.js +41 -0
- package/dist/entries/entry-limits.js.map +1 -0
- package/dist/entries/entry-record.d.ts +26 -26
- package/dist/entries/entry-record.d.ts.map +1 -1
- package/dist/entries/entry-record.js +96 -71
- package/dist/entries/entry-record.js.map +1 -1
- package/dist/entries/generated-master-data.d.ts +2 -2
- package/dist/entries/generated-master-data.d.ts.map +1 -1
- package/dist/entries/import-export.d.ts +3 -1
- package/dist/entries/import-export.d.ts.map +1 -1
- package/dist/entries/import-export.js +365 -123
- package/dist/entries/import-export.js.map +1 -1
- package/dist/entries/quick-guide-data.d.ts +20 -18
- package/dist/entries/quick-guide-data.d.ts.map +1 -1
- package/dist/entries/quick-guide-data.js +191 -21
- package/dist/entries/quick-guide-data.js.map +1 -1
- package/dist/entries/virtual-entries.d.ts +21 -23
- package/dist/entries/virtual-entries.d.ts.map +1 -1
- package/dist/entries/virtual-entries.js +144 -246
- package/dist/entries/virtual-entries.js.map +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/print/entry-metadata.d.ts +3 -0
- package/dist/print/entry-metadata.d.ts.map +1 -0
- package/dist/print/entry-metadata.js +14 -0
- package/dist/print/entry-metadata.js.map +1 -0
- package/dist/print/examples/general-ledger-example.d.ts +1 -1
- package/dist/print/examples/general-ledger-example.d.ts.map +1 -1
- package/dist/print/examples/general-ledger-example.js +4 -4
- package/dist/print/examples/general-ledger-example.js.map +1 -1
- package/dist/print/examples/journal-example.d.ts.map +1 -1
- package/dist/print/examples/journal-example.js +158 -12
- package/dist/print/examples/journal-example.js.map +1 -1
- package/dist/print/financial-statements-print.d.ts +1 -1
- package/dist/print/financial-statements-print.d.ts.map +1 -1
- package/dist/print/financial-statements-print.js +39 -39
- package/dist/print/financial-statements-print.js.map +1 -1
- package/dist/print/fs-data.d.ts +2 -6
- package/dist/print/fs-data.d.ts.map +1 -1
- package/dist/print/fs-data.js +106 -59
- package/dist/print/fs-data.js.map +1 -1
- package/dist/print/general-ledger-print.d.ts.map +1 -1
- package/dist/print/general-ledger-print.js +153 -61
- package/dist/print/general-ledger-print.js.map +1 -1
- package/dist/print/journal-print.d.ts.map +1 -1
- package/dist/print/journal-print.js +63 -22
- package/dist/print/journal-print.js.map +1 -1
- package/dist/shared/app-error.d.ts +6 -8
- package/dist/shared/app-error.d.ts.map +1 -1
- package/dist/shared/app-error.js +28 -73
- package/dist/shared/app-error.js.map +1 -1
- package/dist/shared/bootstrap-data.d.ts +0 -10
- package/dist/shared/bootstrap-data.d.ts.map +1 -1
- package/dist/shared/bootstrap-data.js +1 -144
- package/dist/shared/bootstrap-data.js.map +1 -1
- package/dist/shared/brand-config.d.ts +3 -3
- package/dist/shared/brand-config.d.ts.map +1 -1
- package/dist/shared/models.d.ts +28 -33
- package/dist/shared/models.d.ts.map +1 -1
- package/dist/shared/openkk-config.d.ts +22 -22
- package/dist/shared/openkk-config.d.ts.map +1 -1
- package/dist/shared/openkk-config.js +18 -5
- package/dist/shared/openkk-config.js.map +1 -1
- package/dist/shared/parse-utils.d.ts +6 -1
- package/dist/shared/parse-utils.d.ts.map +1 -1
- package/dist/shared/parse-utils.js +13 -1
- package/dist/shared/parse-utils.js.map +1 -1
- package/dist/shared/period-lock.d.ts +1 -3
- package/dist/shared/period-lock.d.ts.map +1 -1
- package/dist/shared/period-lock.js +3 -13
- package/dist/shared/period-lock.js.map +1 -1
- package/dist/shared/text-limits.d.ts +3 -0
- package/dist/shared/text-limits.d.ts.map +1 -0
- package/dist/shared/text-limits.js +7 -0
- package/dist/shared/text-limits.js.map +1 -0
- package/dist/shared/user.d.ts +2 -3
- package/dist/shared/user.d.ts.map +1 -1
- package/dist/shared/user.js +1 -1
- package/dist/shared/user.js.map +1 -1
- package/dist/steps/next-fiscal-period.d.ts +3 -26
- package/dist/steps/next-fiscal-period.d.ts.map +1 -1
- package/dist/steps/next-fiscal-period.js +37 -83
- package/dist/steps/next-fiscal-period.js.map +1 -1
- package/dist/steps/period-check.d.ts +9 -0
- package/dist/steps/period-check.d.ts.map +1 -1
- package/dist/steps/period-check.js +6 -0
- package/dist/steps/period-check.js.map +1 -1
- package/dist/steps/step-derivation.d.ts +1 -1
- package/dist/steps/step-derivation.d.ts.map +1 -1
- package/dist/steps/step-derivation.js +1 -1
- package/dist/steps/step-derivation.js.map +1 -1
- package/dist/steps/step-trend.d.ts +2 -4
- package/dist/steps/step-trend.d.ts.map +1 -1
- package/dist/steps/step-trend.js +11 -7
- package/dist/steps/step-trend.js.map +1 -1
- package/dist/steps/summary.d.ts +2 -16
- package/dist/steps/summary.d.ts.map +1 -1
- package/dist/steps/summary.js +4 -43
- package/dist/steps/summary.js.map +1 -1
- package/dist/steps/year-month.d.ts +2 -3
- package/dist/steps/year-month.d.ts.map +1 -1
- package/dist/steps/year-month.js +4 -1
- package/dist/steps/year-month.js.map +1 -1
- package/package.json +2 -2
- package/dist/entries/example-entries.d.ts +0 -7
- package/dist/entries/example-entries.d.ts.map +0 -1
- package/dist/entries/example-entries.js +0 -97
- package/dist/entries/example-entries.js.map +0 -1
|
@@ -1,31 +1,29 @@
|
|
|
1
|
+
import { entryToVisualPairs, resolveEntryPairMetadata, } from "./entry-record.js";
|
|
1
2
|
import { AppError } from "../shared/app-error.js";
|
|
2
|
-
import { parseIsoLocalDate, weekdayJa } from "../shared/parse-utils.js";
|
|
3
|
+
import { formatBusinessRatePercent, parseBusinessRate, parseIsoLocalDate, weekdayJa, } from "../shared/parse-utils.js";
|
|
4
|
+
import { assertEntryLineCount, assertEntryImportItemCount, assertEntryImportLineCount, assertEntryImportSize, } from "./entry-limits.js";
|
|
5
|
+
export { assertEntryImportSize, MAX_ENTRY_LINES, MAX_ENTRY_IMPORT_ITEMS, MAX_ENTRY_IMPORT_LINES, MAX_ENTRY_IMPORT_SIZE, } from "./entry-limits.js";
|
|
3
6
|
export const OPENKK_JOURNAL_SCHEMA = "openkk-journal-v1";
|
|
4
7
|
export function exportEntriesAsJson(entries) {
|
|
5
8
|
return JSON.stringify({
|
|
6
9
|
schema: OPENKK_JOURNAL_SCHEMA,
|
|
7
|
-
entries: entries.map((entry) =>
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
taxCategory: entry.taxCategory,
|
|
21
|
-
businessCategory: entry.businessCategory,
|
|
22
|
-
...(entry.lines != null && entry.lines.length > 0
|
|
23
|
-
? { lines: entry.lines.map((line) => ({ ...line })) }
|
|
24
|
-
: {}),
|
|
25
|
-
})),
|
|
10
|
+
entries: entries.map((entry) => {
|
|
11
|
+
const fields = journalFields(entry);
|
|
12
|
+
return {
|
|
13
|
+
localId: entry.localId ?? entry.id,
|
|
14
|
+
date: entry.date,
|
|
15
|
+
weekday: entry.weekday,
|
|
16
|
+
...fields,
|
|
17
|
+
description: entry.description,
|
|
18
|
+
businessRate: formatBusinessRatePercent(entry.businessRate),
|
|
19
|
+
businessRateRatio: entry.businessRate,
|
|
20
|
+
lines: entry.lines.map((line) => ({ ...line })),
|
|
21
|
+
};
|
|
22
|
+
}),
|
|
26
23
|
}, null, 2);
|
|
27
24
|
}
|
|
28
25
|
export function importEntriesFromJson(input) {
|
|
26
|
+
assertEntryImportSize(input.text.length);
|
|
29
27
|
const parsed = parseEntriesJson(input.text);
|
|
30
28
|
if (parsed.schema != null &&
|
|
31
29
|
parsed.schema !== "" &&
|
|
@@ -35,34 +33,42 @@ export function importEntriesFromJson(input) {
|
|
|
35
33
|
if (!Array.isArray(parsed.entries)) {
|
|
36
34
|
throw importFileFormatError("entries array not found");
|
|
37
35
|
}
|
|
36
|
+
assertEntryImportItemCount(parsed.entries.length);
|
|
38
37
|
const seenLocalIds = new Set();
|
|
38
|
+
let importLineCount = 0;
|
|
39
39
|
return parsed.entries.map((raw, index) => {
|
|
40
|
-
const entry = raw;
|
|
41
40
|
const rowNo = index + 1;
|
|
41
|
+
if (!isRecord(raw)) {
|
|
42
|
+
throw importFileRowError(`row ${rowNo}: entry must be an object`, rowNo);
|
|
43
|
+
}
|
|
44
|
+
const entry = raw;
|
|
42
45
|
const localId = validateRequiredLocalId(typeof entry.localId === "string" ? entry.localId : "", rowNo, seenLocalIds);
|
|
43
|
-
|
|
46
|
+
const normalized = normalizeEntry({
|
|
44
47
|
fiscalPeriodId: input.fiscalPeriodId,
|
|
45
48
|
id: `entry-${input.fiscalPeriodId}-json-${index + 1}`,
|
|
46
49
|
rowNo,
|
|
47
50
|
localId,
|
|
48
|
-
date: entry.date,
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
taxCategory: entry.taxCategory,
|
|
60
|
-
businessCategory: entry.businessCategory,
|
|
61
|
-
lines:
|
|
51
|
+
date: stringValue(entry.date),
|
|
52
|
+
debit: stringValue(entry.debit),
|
|
53
|
+
debitType: stringValue(entry.debitType),
|
|
54
|
+
debitAmount: stringValue(entry.debitAmount),
|
|
55
|
+
credit: stringValue(entry.credit),
|
|
56
|
+
creditType: stringValue(entry.creditType),
|
|
57
|
+
creditAmount: stringValue(entry.creditAmount),
|
|
58
|
+
description: stringValue(entry.description),
|
|
59
|
+
partner: stringValue(entry.partner),
|
|
60
|
+
businessRate: stringValue(entry.businessRate),
|
|
61
|
+
businessRateRatio: entry.businessRateRatio ?? null,
|
|
62
|
+
taxCategory: stringValue(entry.taxCategory),
|
|
63
|
+
businessCategory: stringValue(entry.businessCategory),
|
|
64
|
+
lines: validateJsonLines(entry.lines, rowNo),
|
|
62
65
|
});
|
|
66
|
+
importLineCount += normalized.lines.length;
|
|
67
|
+
assertEntryImportLineCount(importLineCount);
|
|
68
|
+
return normalized;
|
|
63
69
|
});
|
|
64
70
|
}
|
|
65
|
-
const
|
|
71
|
+
const journalCsvDataHeaders = [
|
|
66
72
|
"localId",
|
|
67
73
|
"date",
|
|
68
74
|
"weekday",
|
|
@@ -75,31 +81,67 @@ const csvHeaders = [
|
|
|
75
81
|
"description",
|
|
76
82
|
"partner",
|
|
77
83
|
"businessRate",
|
|
84
|
+
"businessRateRatio",
|
|
78
85
|
"taxCategory",
|
|
79
86
|
"businessCategory",
|
|
80
87
|
"lines",
|
|
81
88
|
];
|
|
89
|
+
const csvHeaders = [
|
|
90
|
+
...journalCsvDataHeaders,
|
|
91
|
+
"openkkSchema",
|
|
92
|
+
"openkkEscapedFields",
|
|
93
|
+
];
|
|
94
|
+
const OPENKK_JOURNAL_CSV_SCHEMA = "openkk-journal-csv-v1";
|
|
95
|
+
function journalFields(entry) {
|
|
96
|
+
const pair = entryToVisualPairs(entry)[0] ?? {
|
|
97
|
+
debit: null,
|
|
98
|
+
credit: null,
|
|
99
|
+
};
|
|
100
|
+
return {
|
|
101
|
+
debit: pair.debit?.accountName ?? "",
|
|
102
|
+
debitType: pair.debit?.accountType ?? "asset",
|
|
103
|
+
debitAmount: pair.debit?.amount ?? "",
|
|
104
|
+
credit: pair.credit?.accountName ?? "",
|
|
105
|
+
creditType: pair.credit?.accountType ?? "asset",
|
|
106
|
+
creditAmount: pair.credit?.amount ?? "",
|
|
107
|
+
...resolveEntryPairMetadata(pair),
|
|
108
|
+
};
|
|
109
|
+
}
|
|
82
110
|
export function exportEntriesAsCsv(entries) {
|
|
83
111
|
const lines = [csvHeaders.join(",")];
|
|
84
112
|
for (const entry of entries) {
|
|
85
|
-
|
|
113
|
+
const fields = journalFields(entry);
|
|
114
|
+
const rawValues = [
|
|
86
115
|
entry.localId ?? entry.id,
|
|
87
116
|
entry.date,
|
|
88
117
|
entry.weekday,
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
118
|
+
fields.debit,
|
|
119
|
+
fields.debitType,
|
|
120
|
+
fields.debitAmount,
|
|
121
|
+
fields.credit,
|
|
122
|
+
fields.creditType,
|
|
123
|
+
fields.creditAmount,
|
|
95
124
|
entry.description,
|
|
96
|
-
|
|
97
|
-
entry.businessRate,
|
|
98
|
-
entry.
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
125
|
+
fields.partner,
|
|
126
|
+
formatBusinessRatePercent(entry.businessRate),
|
|
127
|
+
String(entry.businessRate),
|
|
128
|
+
fields.taxCategory,
|
|
129
|
+
fields.businessCategory,
|
|
130
|
+
JSON.stringify(entry.lines),
|
|
131
|
+
];
|
|
132
|
+
const escapedFields = [];
|
|
133
|
+
const safeValues = rawValues.map((value, index) => {
|
|
134
|
+
if (!needsSpreadsheetFormulaProtection(value))
|
|
135
|
+
return value;
|
|
136
|
+
const fieldName = journalCsvDataHeaders[index];
|
|
137
|
+
if (fieldName != null)
|
|
138
|
+
escapedFields.push(fieldName);
|
|
139
|
+
return `'${value}`;
|
|
140
|
+
});
|
|
141
|
+
lines.push([
|
|
142
|
+
...safeValues,
|
|
143
|
+
OPENKK_JOURNAL_CSV_SCHEMA,
|
|
144
|
+
JSON.stringify(escapedFields),
|
|
103
145
|
]
|
|
104
146
|
.map(escapeCsvField)
|
|
105
147
|
.join(","));
|
|
@@ -107,44 +149,69 @@ export function exportEntriesAsCsv(entries) {
|
|
|
107
149
|
return lines.join("\n");
|
|
108
150
|
}
|
|
109
151
|
export function importEntriesFromCsv(input) {
|
|
152
|
+
assertEntryImportSize(input.text.length);
|
|
110
153
|
const rows = parseCsv(stripBom(input.text));
|
|
111
|
-
if (rows.length < 2) {
|
|
112
|
-
return [];
|
|
113
|
-
}
|
|
114
154
|
const header = rows[0] ?? [];
|
|
115
155
|
const indexMap = Object.fromEntries(csvHeaders.map((name) => [name, header.indexOf(name)]));
|
|
116
|
-
if (indexMap.
|
|
117
|
-
|
|
156
|
+
if (indexMap.localId < 0 ||
|
|
157
|
+
indexMap.date < 0 ||
|
|
158
|
+
indexMap.debit < 0 ||
|
|
159
|
+
indexMap.credit < 0) {
|
|
160
|
+
throw importFileFormatError("CSV header missing required columns: localId, date, debit, credit");
|
|
118
161
|
}
|
|
162
|
+
if (rows.length < 2)
|
|
163
|
+
return [];
|
|
164
|
+
assertEntryImportItemCount(rows.length - 1);
|
|
119
165
|
const seenLocalIds = new Set();
|
|
166
|
+
let importLineCount = 0;
|
|
120
167
|
return rows.slice(1).map((cells, index) => {
|
|
121
168
|
const rowNo = index + 2;
|
|
122
|
-
const
|
|
123
|
-
|
|
169
|
+
const escapedFields = parseCsvEscapedFields(cells, indexMap, rowNo);
|
|
170
|
+
const read = (field) => readJournalCsvCell(cells, indexMap[field], escapedFields.has(field));
|
|
171
|
+
const localId = validateRequiredLocalId(read("localId"), rowNo, seenLocalIds);
|
|
172
|
+
const normalized = normalizeEntry({
|
|
124
173
|
fiscalPeriodId: input.fiscalPeriodId,
|
|
125
174
|
id: `entry-${input.fiscalPeriodId}-csv-${index + 1}`,
|
|
126
175
|
rowNo,
|
|
127
176
|
localId,
|
|
128
|
-
date:
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
taxCategory:
|
|
140
|
-
businessCategory:
|
|
141
|
-
lines: parseCsvLinesCell(
|
|
177
|
+
date: read("date"),
|
|
178
|
+
debit: read("debit"),
|
|
179
|
+
debitType: read("debitType"),
|
|
180
|
+
debitAmount: read("debitAmount"),
|
|
181
|
+
credit: read("credit"),
|
|
182
|
+
creditType: read("creditType"),
|
|
183
|
+
creditAmount: read("creditAmount"),
|
|
184
|
+
description: read("description"),
|
|
185
|
+
partner: read("partner"),
|
|
186
|
+
businessRate: read("businessRate"),
|
|
187
|
+
businessRateRatio: read("businessRateRatio"),
|
|
188
|
+
taxCategory: read("taxCategory"),
|
|
189
|
+
businessCategory: read("businessCategory"),
|
|
190
|
+
lines: parseCsvLinesCell(read("lines"), rowNo),
|
|
142
191
|
});
|
|
192
|
+
importLineCount += normalized.lines.length;
|
|
193
|
+
assertEntryImportLineCount(importLineCount);
|
|
194
|
+
return normalized;
|
|
143
195
|
});
|
|
144
196
|
}
|
|
197
|
+
export function decodeJournalImportBytes(bytes) {
|
|
198
|
+
assertEntryImportSize(bytes.byteLength);
|
|
199
|
+
try {
|
|
200
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
201
|
+
}
|
|
202
|
+
catch (error) {
|
|
203
|
+
throw new AppError({
|
|
204
|
+
messageForDeveloper: "entries import is not valid UTF-8",
|
|
205
|
+
messageForUser: "取込ファイルの文字コードを確認できませんでした。UTF-8形式で保存してからもう一度取り込んでください。",
|
|
206
|
+
originalMessage: error instanceof Error ? error.message : String(error),
|
|
207
|
+
statusCode: null,
|
|
208
|
+
code: null,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
}
|
|
145
212
|
function parseCsvLinesCell(raw, rowNo) {
|
|
146
213
|
if (raw.trim() === "")
|
|
147
|
-
return
|
|
214
|
+
return [];
|
|
148
215
|
let parsed;
|
|
149
216
|
try {
|
|
150
217
|
parsed = JSON.parse(raw);
|
|
@@ -161,42 +228,100 @@ function normalizeEntry(input) {
|
|
|
161
228
|
if (parseIsoLocalDate(input.date) == null) {
|
|
162
229
|
throw importFileRowError(`row ${input.rowNo}: invalid date (${input.date})`, input.rowNo);
|
|
163
230
|
}
|
|
164
|
-
|
|
231
|
+
const partner = input.partner.trim();
|
|
232
|
+
const taxCategory = input.taxCategory.trim() || "対象外";
|
|
233
|
+
const businessCategory = input.businessCategory.trim() || "対象外";
|
|
234
|
+
const importedLines = normalizeLines(input.lines, input.rowNo);
|
|
235
|
+
const lines = importedLines.length > 0
|
|
236
|
+
? importedLines
|
|
237
|
+
: [
|
|
238
|
+
pairInputToLine("debit", input),
|
|
239
|
+
pairInputToLine("credit", input),
|
|
240
|
+
].map((line) => ({
|
|
241
|
+
...line,
|
|
242
|
+
partnerName: partner === "" ? null : partner,
|
|
243
|
+
taxCategoryName: taxCategory,
|
|
244
|
+
businessCategoryName: businessCategory,
|
|
245
|
+
}));
|
|
246
|
+
const result = {
|
|
165
247
|
id: input.id,
|
|
166
248
|
localId: input.localId,
|
|
167
249
|
fiscalPeriodId: input.fiscalPeriodId,
|
|
168
250
|
date: input.date,
|
|
169
|
-
weekday: input.
|
|
170
|
-
? input.weekday
|
|
171
|
-
: weekdayFromDate(input.date),
|
|
172
|
-
debit: input.debit || "未設定",
|
|
173
|
-
debitType: normalizeType(input.debitType),
|
|
174
|
-
debitAmount: normalizeAmount(input.debitAmount),
|
|
175
|
-
credit: input.credit || "未設定",
|
|
176
|
-
creditType: normalizeType(input.creditType),
|
|
177
|
-
creditAmount: normalizeAmount(input.creditAmount),
|
|
251
|
+
weekday: weekdayFromDate(input.date),
|
|
178
252
|
description: input.description || "",
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
taxCategory: input.taxCategory || "対象外",
|
|
182
|
-
businessCategory: input.businessCategory || "対象外",
|
|
183
|
-
lines: normalizeLines(input.lines),
|
|
253
|
+
businessRate: importedBusinessRate(input),
|
|
254
|
+
lines,
|
|
184
255
|
};
|
|
256
|
+
assertNormalizedEntry(result, input.rowNo);
|
|
257
|
+
return result;
|
|
185
258
|
}
|
|
186
|
-
function
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
side
|
|
191
|
-
accountName:
|
|
192
|
-
accountType: normalizeType(
|
|
193
|
-
amount: normalizeAmount(
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
259
|
+
function pairInputToLine(side, input) {
|
|
260
|
+
const debit = side === "debit";
|
|
261
|
+
return {
|
|
262
|
+
id: null,
|
|
263
|
+
side,
|
|
264
|
+
accountName: (debit ? input.debit : input.credit).trim(),
|
|
265
|
+
accountType: normalizeType(debit ? input.debitType : input.creditType, input.rowNo, `${side} type`),
|
|
266
|
+
amount: normalizeAmount(debit ? input.debitAmount : input.creditAmount, input.rowNo, `${side} amount`),
|
|
267
|
+
bookAccountId: null,
|
|
268
|
+
partnerName: null,
|
|
269
|
+
taxCategoryId: null,
|
|
270
|
+
taxCategoryName: null,
|
|
271
|
+
businessCategoryId: null,
|
|
272
|
+
businessCategoryName: null,
|
|
273
|
+
};
|
|
198
274
|
}
|
|
199
|
-
function
|
|
275
|
+
function importedBusinessRate(input) {
|
|
276
|
+
const ratio = input.businessRateRatio;
|
|
277
|
+
if (ratio == null || ratio === "") {
|
|
278
|
+
return parseBusinessRate(input.businessRate);
|
|
279
|
+
}
|
|
280
|
+
const rate = typeof ratio === "number"
|
|
281
|
+
? ratio
|
|
282
|
+
: typeof ratio === "string"
|
|
283
|
+
? Number(ratio.trim())
|
|
284
|
+
: Number.NaN;
|
|
285
|
+
if (!Number.isFinite(rate) || rate < 0 || rate > 1) {
|
|
286
|
+
throw importFileRowError(`row ${input.rowNo}: invalid businessRateRatio`, input.rowNo);
|
|
287
|
+
}
|
|
288
|
+
return rate;
|
|
289
|
+
}
|
|
290
|
+
function normalizeLines(lines, rowNo) {
|
|
291
|
+
assertEntryLineCount(lines.length);
|
|
292
|
+
return lines.map((line, index) => {
|
|
293
|
+
if (!isRecord(line)) {
|
|
294
|
+
throw importFileRowError(`row ${rowNo}: line ${index + 1} must be an object`, rowNo);
|
|
295
|
+
}
|
|
296
|
+
if (line.side !== "debit" && line.side !== "credit") {
|
|
297
|
+
throw importFileRowError(`row ${rowNo}: line ${index + 1} has invalid side`, rowNo);
|
|
298
|
+
}
|
|
299
|
+
const accountName = stringValue(line.accountName).trim();
|
|
300
|
+
if (accountName === "") {
|
|
301
|
+
throw importFileRowError(`row ${rowNo}: line ${index + 1} account is required`, rowNo);
|
|
302
|
+
}
|
|
303
|
+
const bookAccountId = stringValue(line.bookAccountId).trim();
|
|
304
|
+
const partnerName = stringValue(line.partnerName).trim();
|
|
305
|
+
const taxCategoryId = stringValue(line.taxCategoryId).trim();
|
|
306
|
+
const taxCategoryName = stringValue(line.taxCategoryName).trim();
|
|
307
|
+
const businessCategoryId = stringValue(line.businessCategoryId).trim();
|
|
308
|
+
const businessCategoryName = stringValue(line.businessCategoryName).trim();
|
|
309
|
+
return {
|
|
310
|
+
side: line.side,
|
|
311
|
+
accountName,
|
|
312
|
+
accountType: normalizeType(stringValue(line.accountType), rowNo, `line ${index + 1} account type`),
|
|
313
|
+
amount: normalizeAmount(stringValue(line.amount), rowNo, `line ${index + 1} amount`),
|
|
314
|
+
id: null,
|
|
315
|
+
bookAccountId: bookAccountId === "" ? null : bookAccountId,
|
|
316
|
+
partnerName: partnerName === "" ? null : partnerName,
|
|
317
|
+
taxCategoryId: taxCategoryId === "" ? null : taxCategoryId,
|
|
318
|
+
taxCategoryName: taxCategoryName === "" ? null : taxCategoryName,
|
|
319
|
+
businessCategoryId: businessCategoryId === "" ? null : businessCategoryId,
|
|
320
|
+
businessCategoryName: businessCategoryName === "" ? null : businessCategoryName,
|
|
321
|
+
};
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
function normalizeType(value, rowNo, label) {
|
|
200
325
|
if (value === "asset" ||
|
|
201
326
|
value === "liability" ||
|
|
202
327
|
value === "equity" ||
|
|
@@ -205,38 +330,83 @@ function normalizeType(value) {
|
|
|
205
330
|
value === "expense") {
|
|
206
331
|
return value;
|
|
207
332
|
}
|
|
208
|
-
|
|
333
|
+
if (value.trim() === "")
|
|
334
|
+
return "asset";
|
|
335
|
+
throw importFileRowError(`row ${rowNo}: invalid ${label} (${value})`, rowNo);
|
|
209
336
|
}
|
|
210
|
-
function normalizeAmount(value) {
|
|
337
|
+
function normalizeAmount(value, rowNo, label) {
|
|
211
338
|
const n = Number(String(value).replaceAll(",", "").trim());
|
|
212
|
-
if (!Number.
|
|
213
|
-
|
|
339
|
+
if (!Number.isSafeInteger(n) || n < 0) {
|
|
340
|
+
throw importFileRowError(`row ${rowNo}: invalid ${label} (${value})`, rowNo);
|
|
341
|
+
}
|
|
214
342
|
return n.toLocaleString("ja-JP");
|
|
215
343
|
}
|
|
216
344
|
function weekdayFromDate(dateText) {
|
|
217
345
|
return weekdayJa(dateText) || "月";
|
|
218
346
|
}
|
|
219
347
|
function escapeCsvField(value) {
|
|
220
|
-
if (value.includes(",") ||
|
|
348
|
+
if (value.includes(",") ||
|
|
349
|
+
value.includes('"') ||
|
|
350
|
+
value.includes("\n") ||
|
|
351
|
+
value.includes("\r")) {
|
|
221
352
|
return `"${value.replaceAll('"', '""')}"`;
|
|
222
353
|
}
|
|
223
354
|
return value;
|
|
224
355
|
}
|
|
356
|
+
function needsSpreadsheetFormulaProtection(value) {
|
|
357
|
+
return /^[=+\-@\t\r\n]/.test(value);
|
|
358
|
+
}
|
|
359
|
+
function parseCsvEscapedFields(cells, indexMap, rowNo) {
|
|
360
|
+
if (readCsvCell(cells, indexMap.openkkSchema) !== OPENKK_JOURNAL_CSV_SCHEMA) {
|
|
361
|
+
return new Set();
|
|
362
|
+
}
|
|
363
|
+
const raw = readCsvCell(cells, indexMap.openkkEscapedFields);
|
|
364
|
+
let parsed;
|
|
365
|
+
try {
|
|
366
|
+
parsed = JSON.parse(raw);
|
|
367
|
+
}
|
|
368
|
+
catch {
|
|
369
|
+
throw importFileRowError(`row ${rowNo}: invalid escaped fields metadata`, rowNo);
|
|
370
|
+
}
|
|
371
|
+
if (!Array.isArray(parsed) ||
|
|
372
|
+
parsed.some((field) => !isJournalCsvDataHeader(field))) {
|
|
373
|
+
throw importFileRowError(`row ${rowNo}: invalid escaped fields metadata`, rowNo);
|
|
374
|
+
}
|
|
375
|
+
return new Set(parsed);
|
|
376
|
+
}
|
|
377
|
+
function isJournalCsvDataHeader(value) {
|
|
378
|
+
return (typeof value === "string" &&
|
|
379
|
+
journalCsvDataHeaders.includes(value));
|
|
380
|
+
}
|
|
381
|
+
function readJournalCsvCell(cells, index, formulaProtected) {
|
|
382
|
+
const value = readCsvCell(cells, index);
|
|
383
|
+
return formulaProtected && value.startsWith("'") ? value.slice(1) : value;
|
|
384
|
+
}
|
|
225
385
|
function parseCsv(text) {
|
|
226
386
|
const rows = [];
|
|
227
387
|
let row = [];
|
|
228
388
|
let field = "";
|
|
229
|
-
let
|
|
389
|
+
let state = "start";
|
|
390
|
+
const finishField = () => {
|
|
391
|
+
row.push(field);
|
|
392
|
+
field = "";
|
|
393
|
+
state = "start";
|
|
394
|
+
};
|
|
395
|
+
const finishRow = () => {
|
|
396
|
+
finishField();
|
|
397
|
+
rows.push(row);
|
|
398
|
+
row = [];
|
|
399
|
+
};
|
|
230
400
|
for (let i = 0; i < text.length; i += 1) {
|
|
231
401
|
const ch = text[i];
|
|
232
|
-
if (
|
|
402
|
+
if (state === "quoted") {
|
|
233
403
|
if (ch === '"') {
|
|
234
404
|
if (text[i + 1] === '"') {
|
|
235
405
|
field += '"';
|
|
236
406
|
i += 1;
|
|
237
407
|
}
|
|
238
408
|
else {
|
|
239
|
-
|
|
409
|
+
state = "after_quote";
|
|
240
410
|
}
|
|
241
411
|
}
|
|
242
412
|
else {
|
|
@@ -244,30 +414,95 @@ function parseCsv(text) {
|
|
|
244
414
|
}
|
|
245
415
|
continue;
|
|
246
416
|
}
|
|
417
|
+
const isLineBreak = ch === "\n" || ch === "\r";
|
|
418
|
+
if (state === "after_quote") {
|
|
419
|
+
if (ch === ",") {
|
|
420
|
+
finishField();
|
|
421
|
+
continue;
|
|
422
|
+
}
|
|
423
|
+
if (isLineBreak) {
|
|
424
|
+
finishRow();
|
|
425
|
+
if (ch === "\r" && text[i + 1] === "\n")
|
|
426
|
+
i += 1;
|
|
427
|
+
continue;
|
|
428
|
+
}
|
|
429
|
+
throw importFileFormatError("CSV has an unexpected character after a closing quote");
|
|
430
|
+
}
|
|
247
431
|
if (ch === '"') {
|
|
248
|
-
|
|
432
|
+
if (state !== "start") {
|
|
433
|
+
throw importFileFormatError("CSV has an unexpected quote in an unquoted field");
|
|
434
|
+
}
|
|
435
|
+
state = "quoted";
|
|
249
436
|
continue;
|
|
250
437
|
}
|
|
251
438
|
if (ch === ",") {
|
|
252
|
-
|
|
253
|
-
field = "";
|
|
439
|
+
finishField();
|
|
254
440
|
continue;
|
|
255
441
|
}
|
|
256
|
-
if (
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
field = "";
|
|
442
|
+
if (isLineBreak) {
|
|
443
|
+
finishRow();
|
|
444
|
+
if (ch === "\r" && text[i + 1] === "\n")
|
|
445
|
+
i += 1;
|
|
261
446
|
continue;
|
|
262
447
|
}
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
448
|
+
field += ch;
|
|
449
|
+
state = "unquoted";
|
|
450
|
+
}
|
|
451
|
+
if (state === "quoted") {
|
|
452
|
+
throw importFileFormatError("CSV has an unterminated quoted field");
|
|
266
453
|
}
|
|
267
|
-
|
|
268
|
-
rows.push(row);
|
|
454
|
+
finishRow();
|
|
269
455
|
return rows.filter((r) => r.some((v) => v.trim().length > 0));
|
|
270
456
|
}
|
|
457
|
+
function validateJsonLines(value, rowNo) {
|
|
458
|
+
if (value == null)
|
|
459
|
+
return [];
|
|
460
|
+
if (!Array.isArray(value)) {
|
|
461
|
+
throw importFileRowError(`row ${rowNo}: lines must be an array`, rowNo);
|
|
462
|
+
}
|
|
463
|
+
return value;
|
|
464
|
+
}
|
|
465
|
+
function assertNormalizedEntry(entry, rowNo) {
|
|
466
|
+
if (entry.description.trim() === "") {
|
|
467
|
+
throw importFileRowError(`row ${rowNo}: description is required`, rowNo);
|
|
468
|
+
}
|
|
469
|
+
for (const line of entry.lines) {
|
|
470
|
+
if (line.accountName.trim() === "") {
|
|
471
|
+
throw importFileRowError(`row ${rowNo}: account is required`, rowNo);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
let debitTotal = 0;
|
|
475
|
+
let creditTotal = 0;
|
|
476
|
+
for (const line of entry.lines) {
|
|
477
|
+
const amount = numericAmount(line.amount);
|
|
478
|
+
if (line.side === "debit")
|
|
479
|
+
debitTotal += amount;
|
|
480
|
+
else
|
|
481
|
+
creditTotal += amount;
|
|
482
|
+
if (!Number.isSafeInteger(debitTotal) ||
|
|
483
|
+
!Number.isSafeInteger(creditTotal)) {
|
|
484
|
+
throw importFileRowError(`row ${rowNo}: entry totals exceed the safe integer range`, rowNo);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
if (debitTotal <= 0 || creditTotal <= 0 || debitTotal !== creditTotal) {
|
|
488
|
+
throw importFileRowError(`row ${rowNo}: debit and credit totals must be positive and equal`, rowNo);
|
|
489
|
+
}
|
|
490
|
+
const rate = entry.businessRate;
|
|
491
|
+
if (!Number.isFinite(rate) || rate < 0 || rate > 1) {
|
|
492
|
+
throw importFileRowError(`row ${rowNo}: invalid business rate`, rowNo);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
function numericAmount(value) {
|
|
496
|
+
return Number(value.replaceAll(",", ""));
|
|
497
|
+
}
|
|
498
|
+
function isRecord(value) {
|
|
499
|
+
return typeof value === "object" && value != null && !Array.isArray(value);
|
|
500
|
+
}
|
|
501
|
+
function stringValue(value) {
|
|
502
|
+
return typeof value === "string" || typeof value === "number"
|
|
503
|
+
? String(value)
|
|
504
|
+
: "";
|
|
505
|
+
}
|
|
271
506
|
function readCsvCell(cells, index) {
|
|
272
507
|
if (index < 0)
|
|
273
508
|
return "";
|
|
@@ -289,7 +524,11 @@ function stripBom(text) {
|
|
|
289
524
|
}
|
|
290
525
|
function parseEntriesJson(text) {
|
|
291
526
|
try {
|
|
292
|
-
|
|
527
|
+
const parsed = JSON.parse(stripBom(text));
|
|
528
|
+
if (!isRecord(parsed)) {
|
|
529
|
+
throw new Error("journal JSON root must be an object");
|
|
530
|
+
}
|
|
531
|
+
return parsed;
|
|
293
532
|
}
|
|
294
533
|
catch (error) {
|
|
295
534
|
throw new AppError({
|
|
@@ -297,6 +536,7 @@ function parseEntriesJson(text) {
|
|
|
297
536
|
messageForUser: "JSONファイルの内容を確認できませんでした。ファイルの形式を確認してください。",
|
|
298
537
|
originalMessage: error instanceof Error ? error.message : String(error),
|
|
299
538
|
statusCode: null,
|
|
539
|
+
code: null,
|
|
300
540
|
});
|
|
301
541
|
}
|
|
302
542
|
}
|
|
@@ -306,6 +546,7 @@ function importFileFormatError(messageForDeveloper) {
|
|
|
306
546
|
messageForUser: "取込ファイルの形式を確認できませんでした。オープン会計で書き出したCSVまたはJSONを選択してください。",
|
|
307
547
|
originalMessage: null,
|
|
308
548
|
statusCode: null,
|
|
549
|
+
code: null,
|
|
309
550
|
});
|
|
310
551
|
}
|
|
311
552
|
function importFileRowError(messageForDeveloper, rowNo) {
|
|
@@ -314,6 +555,7 @@ function importFileRowError(messageForDeveloper, rowNo) {
|
|
|
314
555
|
messageForUser: `${rowNo}行目の取引データを確認できませんでした。ファイルの内容を修正してからもう一度取り込んでください。`,
|
|
315
556
|
originalMessage: null,
|
|
316
557
|
statusCode: null,
|
|
558
|
+
code: null,
|
|
317
559
|
});
|
|
318
560
|
}
|
|
319
561
|
//# sourceMappingURL=import-export.js.map
|