@rubydogjp/openkk-client-usecases 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/assist/assist-state-helpers.d.ts +27 -0
- package/dist/assist/assist-state-helpers.d.ts.map +1 -0
- package/dist/assist/assist-state-helpers.js +183 -0
- package/dist/assist/assist-state-helpers.js.map +1 -0
- package/dist/assist/assist-state.d.ts +4 -11
- package/dist/assist/assist-state.d.ts.map +1 -1
- package/dist/assist/assist-state.js +227 -302
- package/dist/assist/assist-state.js.map +1 -1
- package/dist/entries/account-options.d.ts +11 -0
- package/dist/entries/account-options.d.ts.map +1 -0
- package/dist/entries/account-options.js +41 -0
- package/dist/entries/account-options.js.map +1 -0
- package/dist/entries/entries-state.d.ts +9 -14
- package/dist/entries/entries-state.d.ts.map +1 -1
- package/dist/entries/entries-state.js +239 -158
- package/dist/entries/entries-state.js.map +1 -1
- package/dist/entries/entry-record-state.d.ts +6 -0
- package/dist/entries/entry-record-state.d.ts.map +1 -0
- package/dist/entries/entry-record-state.js +23 -0
- package/dist/entries/entry-record-state.js.map +1 -0
- package/dist/entries/import-mapping.d.ts +12 -29
- package/dist/entries/import-mapping.d.ts.map +1 -1
- package/dist/entries/import-mapping.js +23 -74
- package/dist/entries/import-mapping.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/shared/archive-data-policy.d.ts +6 -0
- package/dist/shared/archive-data-policy.d.ts.map +1 -0
- package/dist/shared/archive-data-policy.js +4 -0
- package/dist/shared/archive-data-policy.js.map +1 -0
- package/dist/shared/async-mutation-queue.d.ts +9 -0
- package/dist/shared/async-mutation-queue.d.ts.map +1 -0
- package/dist/shared/async-mutation-queue.js +21 -0
- package/dist/shared/async-mutation-queue.js.map +1 -0
- package/dist/shared/async-state-version.d.ts +13 -0
- package/dist/shared/async-state-version.d.ts.map +1 -0
- package/dist/shared/async-state-version.js +27 -0
- package/dist/shared/async-state-version.js.map +1 -0
- package/dist/shared/auth-operation-guard.d.ts +3 -0
- package/dist/shared/auth-operation-guard.d.ts.map +1 -0
- package/dist/shared/auth-operation-guard.js +13 -0
- package/dist/shared/auth-operation-guard.js.map +1 -0
- package/dist/shared/backend-api-context.d.ts.map +1 -1
- package/dist/shared/backend-api-context.js +1 -0
- package/dist/shared/backend-api-context.js.map +1 -1
- package/dist/shared/brand-config-context.js +1 -1
- package/dist/shared/brand-config-context.js.map +1 -1
- package/dist/shared/browser-storage.d.ts +9 -0
- package/dist/shared/browser-storage.d.ts.map +1 -0
- package/dist/shared/browser-storage.js +79 -0
- package/dist/shared/browser-storage.js.map +1 -0
- package/dist/shared/callouts-context.d.ts +1 -1
- package/dist/shared/callouts-context.d.ts.map +1 -1
- package/dist/shared/callouts-context.js +5 -2
- package/dist/shared/callouts-context.js.map +1 -1
- package/dist/shared/closing-state.d.ts +2 -1
- package/dist/shared/closing-state.d.ts.map +1 -1
- package/dist/shared/closing-state.js +21 -6
- package/dist/shared/closing-state.js.map +1 -1
- package/dist/shared/editing-policy.d.ts +3 -0
- package/dist/shared/editing-policy.d.ts.map +1 -0
- package/dist/shared/editing-policy.js +13 -0
- package/dist/shared/editing-policy.js.map +1 -0
- package/dist/shared/fiscal-period-list.d.ts +5 -0
- package/dist/shared/fiscal-period-list.d.ts.map +1 -0
- package/dist/shared/fiscal-period-list.js +50 -0
- package/dist/shared/fiscal-period-list.js.map +1 -0
- package/dist/shared/maintenance-state.d.ts.map +1 -1
- package/dist/shared/maintenance-state.js +15 -2
- package/dist/shared/maintenance-state.js.map +1 -1
- package/dist/shared/openkk-app-state.d.ts +14 -27
- package/dist/shared/openkk-app-state.d.ts.map +1 -1
- package/dist/shared/openkk-app-state.js +240 -171
- package/dist/shared/openkk-app-state.js.map +1 -1
- package/dist/shared/openkk-config-context.d.ts +1 -0
- package/dist/shared/openkk-config-context.d.ts.map +1 -1
- package/dist/shared/openkk-config-context.js +35 -2
- package/dist/shared/openkk-config-context.js.map +1 -1
- package/dist/shared/print-adapter-context.d.ts.map +1 -1
- package/dist/shared/print-adapter-context.js +1 -0
- package/dist/shared/print-adapter-context.js.map +1 -1
- package/package.json +4 -4
|
@@ -1,38 +1,65 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
-
import { createContext, useContext, useEffect, useMemo, useState, } from "react";
|
|
3
|
+
import { createContext, useContext, useEffect, useMemo, useRef, useState, } from "react";
|
|
4
4
|
import { useOpenkkAppState } from "../shared/openkk-app-state.js";
|
|
5
5
|
import { useBackendApi } from "../shared/backend-api-context.js";
|
|
6
|
-
import { useOpenkkConfig } from "../shared/openkk-config-context.js";
|
|
7
|
-
import {
|
|
6
|
+
import { useOpenkkConfig, useOpenkkToday, } from "../shared/openkk-config-context.js";
|
|
7
|
+
import { assertEditingUnlocked } from "../shared/editing-policy.js";
|
|
8
|
+
import { isSelectedFiscalPeriodDataPurged } from "../shared/archive-data-policy.js";
|
|
9
|
+
import { KeyedAsyncStateVersion } from "../shared/async-state-version.js";
|
|
10
|
+
import { KeyedAsyncMutationQueue } from "../shared/async-mutation-queue.js";
|
|
11
|
+
import { buildOpeningJournalLines, fixedAssetDraftToPatch, listFixedAssetsForPeriod, mapFixedAsset, mapOpeningJournalToRecord, nextOpeningCarryoverId, fixedAssetDraftBusinessRate, resolveFixedAssetAccountId, upsertFixedAsset, } from "./assist-state-helpers.js";
|
|
12
|
+
import { capFixedAssetPreviewDate, draftBusinessRate, parseAmount, AppError, } from "@rubydogjp/openkk-client-domain";
|
|
8
13
|
const AssistContext = createContext(null);
|
|
9
14
|
export function OpenkkAssistProvider(props) {
|
|
10
15
|
const backendApi = useBackendApi();
|
|
11
16
|
const appState = useOpenkkAppState();
|
|
12
17
|
const config = useOpenkkConfig();
|
|
18
|
+
const today = useOpenkkToday();
|
|
13
19
|
const [fixedAssets, setFixedAssets] = useState([]);
|
|
14
20
|
const [bookAccountNameById, setBookAccountNameById] = useState({});
|
|
15
|
-
const [
|
|
21
|
+
const [bookAccounts, setBookAccounts] = useState([]);
|
|
16
22
|
const [bookAccountTypeById, setBookAccountTypeById] = useState({});
|
|
17
|
-
const [
|
|
23
|
+
const [taxCategories, setTaxCategories] = useState([]);
|
|
24
|
+
const [taxCategoryNameById, setTaxCategoryNameById] = useState({});
|
|
25
|
+
const [businessCategories, setBusinessCategories] = useState([]);
|
|
26
|
+
const [businessCategoryNameById, setBusinessCategoryNameById] = useState({});
|
|
27
|
+
const [masterLoadError, setMasterLoadError] = useState(null);
|
|
28
|
+
const [fixedAssetsLoadError, setFixedAssetsLoadError] = useState(null);
|
|
18
29
|
const [reloadNonce, setReloadNonce] = useState(0);
|
|
30
|
+
const selectedFiscalPeriodId = useRef(appState.currentFiscalPeriodId);
|
|
31
|
+
const periodVersions = useRef(new KeyedAsyncStateVersion());
|
|
32
|
+
const assetMutationQueue = useRef(new KeyedAsyncMutationQueue());
|
|
33
|
+
selectedFiscalPeriodId.current = appState.currentFiscalPeriodId;
|
|
34
|
+
const currentFiscalPeriod = appState.fiscalPeriods.find((period) => period.id === appState.currentFiscalPeriodId);
|
|
35
|
+
const currentFiscalPeriodEndDate = currentFiscalPeriod?.endDate ?? null;
|
|
36
|
+
const currentFiscalPeriodDataPurged = isSelectedFiscalPeriodDataPurged(appState.fiscalPeriods, appState.currentFiscalPeriodId);
|
|
37
|
+
const fixedAssetPreviewAsOf = useMemo(() => capFixedAssetPreviewDate(today, currentFiscalPeriodEndDate), [today, currentFiscalPeriodEndDate]);
|
|
19
38
|
useEffect(() => {
|
|
20
39
|
let cancelled = false;
|
|
21
40
|
void (async () => {
|
|
22
41
|
try {
|
|
23
|
-
const accounts = await
|
|
42
|
+
const [accounts, taxCategories, businessCategories] = await Promise.all([
|
|
43
|
+
backendApi.masterData.getBookAccounts(),
|
|
44
|
+
backendApi.masterData.getTaxCategories(),
|
|
45
|
+
backendApi.masterData.getBusinessCategories(),
|
|
46
|
+
]);
|
|
24
47
|
if (cancelled)
|
|
25
48
|
return;
|
|
26
49
|
setBookAccountNameById(Object.fromEntries(accounts.map((account) => [account.id, account.name])));
|
|
27
|
-
|
|
50
|
+
setBookAccounts(accounts);
|
|
28
51
|
setBookAccountTypeById(Object.fromEntries(accounts.map((account) => [account.id, account.accountType])));
|
|
29
|
-
|
|
52
|
+
setTaxCategories(taxCategories);
|
|
53
|
+
setTaxCategoryNameById(Object.fromEntries(taxCategories.map((category) => [category.id, category.name])));
|
|
54
|
+
setBusinessCategories(businessCategories);
|
|
55
|
+
setBusinessCategoryNameById(Object.fromEntries(businessCategories.map((category) => [category.id, category.name])));
|
|
56
|
+
setMasterLoadError(null);
|
|
30
57
|
}
|
|
31
58
|
catch (error) {
|
|
32
59
|
if (cancelled)
|
|
33
60
|
return;
|
|
34
61
|
console.error("[openkk] assist master data load failed:", error);
|
|
35
|
-
|
|
62
|
+
setMasterLoadError(error);
|
|
36
63
|
}
|
|
37
64
|
})();
|
|
38
65
|
return () => {
|
|
@@ -41,24 +68,35 @@ export function OpenkkAssistProvider(props) {
|
|
|
41
68
|
}, [reloadNonce]);
|
|
42
69
|
useEffect(() => {
|
|
43
70
|
const fiscalPeriodId = appState.currentFiscalPeriodId;
|
|
44
|
-
if (fiscalPeriodId == null ||
|
|
71
|
+
if (fiscalPeriodId == null ||
|
|
72
|
+
fiscalPeriodId.length === 0 ||
|
|
73
|
+
currentFiscalPeriodDataPurged) {
|
|
45
74
|
setFixedAssets([]);
|
|
75
|
+
setFixedAssetsLoadError(null);
|
|
46
76
|
return;
|
|
47
77
|
}
|
|
48
78
|
let cancelled = false;
|
|
79
|
+
const authOperationVersion = appState.captureAuthOperationVersion();
|
|
80
|
+
const readVersion = periodVersions.current.capture(fiscalPeriodId);
|
|
49
81
|
void (async () => {
|
|
50
82
|
try {
|
|
51
83
|
const remote = await backendApi.fixedAssets.getAll(fiscalPeriodId);
|
|
52
|
-
if (cancelled
|
|
84
|
+
if (cancelled ||
|
|
85
|
+
!appState.isAuthOperationCurrent(authOperationVersion) ||
|
|
86
|
+
!periodVersions.current.isCurrent(fiscalPeriodId, readVersion)) {
|
|
53
87
|
return;
|
|
54
|
-
|
|
55
|
-
|
|
88
|
+
}
|
|
89
|
+
setFixedAssets(remote.map((asset) => mapFixedAsset(asset, bookAccountNameById[asset.bookAccountId] ?? null, fixedAssetPreviewAsOf, currentFiscalPeriodEndDate)));
|
|
90
|
+
setFixedAssetsLoadError(null);
|
|
56
91
|
}
|
|
57
92
|
catch (error) {
|
|
58
|
-
if (cancelled
|
|
93
|
+
if (cancelled ||
|
|
94
|
+
!appState.isAuthOperationCurrent(authOperationVersion) ||
|
|
95
|
+
!periodVersions.current.isCurrent(fiscalPeriodId, readVersion)) {
|
|
59
96
|
return;
|
|
97
|
+
}
|
|
60
98
|
console.error("[openkk] fixed assets load failed:", error);
|
|
61
|
-
|
|
99
|
+
setFixedAssetsLoadError(error);
|
|
62
100
|
}
|
|
63
101
|
})();
|
|
64
102
|
return () => {
|
|
@@ -67,74 +105,102 @@ export function OpenkkAssistProvider(props) {
|
|
|
67
105
|
}, [
|
|
68
106
|
appState.currentFiscalPeriodId,
|
|
69
107
|
bookAccountNameById,
|
|
70
|
-
|
|
108
|
+
currentFiscalPeriodEndDate,
|
|
109
|
+
currentFiscalPeriodDataPurged,
|
|
110
|
+
fixedAssetPreviewAsOf,
|
|
71
111
|
reloadNonce,
|
|
72
112
|
]);
|
|
73
113
|
const value = useMemo(() => {
|
|
114
|
+
const loadError = masterLoadError ?? fixedAssetsLoadError;
|
|
74
115
|
return {
|
|
75
|
-
listFixedAssets() {
|
|
76
|
-
return fixedAssets;
|
|
116
|
+
listFixedAssets(fiscalPeriodId) {
|
|
117
|
+
return listFixedAssetsForPeriod(fixedAssets, fiscalPeriodId);
|
|
77
118
|
},
|
|
78
119
|
getFixedAsset(assetId) {
|
|
79
120
|
return fixedAssets.find((asset) => asset.id === assetId) ?? null;
|
|
80
121
|
},
|
|
81
122
|
async addFixedAsset(draft) {
|
|
123
|
+
assertEditingUnlocked(config.editingPolicy, "assist.addFixedAsset");
|
|
124
|
+
const authOperationVersion = appState.captureAuthOperationVersion();
|
|
82
125
|
const fiscalPeriodId = appState.currentFiscalPeriodId;
|
|
83
|
-
if (fiscalPeriodId == null
|
|
126
|
+
if (fiscalPeriodId == null)
|
|
84
127
|
return null;
|
|
85
|
-
|
|
86
|
-
const accountId =
|
|
87
|
-
if (accountId == null
|
|
128
|
+
periodVersions.current.invalidate(fiscalPeriodId);
|
|
129
|
+
const accountId = resolveFixedAssetAccountId(null, draft.account, bookAccounts);
|
|
130
|
+
if (accountId == null) {
|
|
88
131
|
throw new AppError({
|
|
89
132
|
messageForDeveloper: "assist.addFixedAsset: accountId missing",
|
|
90
133
|
messageForUser: "勘定科目が解決できないため保存できませんでした",
|
|
91
134
|
originalMessage: null,
|
|
92
135
|
statusCode: null,
|
|
136
|
+
code: null,
|
|
93
137
|
});
|
|
94
138
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
139
|
+
try {
|
|
140
|
+
const created = await backendApi.fixedAssets.create(fiscalPeriodId, {
|
|
141
|
+
name: draft.name,
|
|
142
|
+
acquisitionDate: draft.acquisitionDate,
|
|
143
|
+
acquisitionCost: parseAmount(draft.acquisitionCost),
|
|
144
|
+
usefulLife: Math.max(1, Math.round(draft.usefulLife) || 1),
|
|
145
|
+
depreciationMethod: "straight_line",
|
|
146
|
+
businessRate: fixedAssetDraftBusinessRate(draft),
|
|
147
|
+
bookAccountId: accountId,
|
|
148
|
+
});
|
|
149
|
+
appState.assertAuthOperationCurrent(authOperationVersion);
|
|
150
|
+
if (selectedFiscalPeriodId.current === fiscalPeriodId) {
|
|
151
|
+
const mapped = mapFixedAsset(created, bookAccountNameById[created.bookAccountId] ?? null, fixedAssetPreviewAsOf, currentFiscalPeriodEndDate ?? null);
|
|
152
|
+
setFixedAssets((current) => upsertFixedAsset(current, mapped));
|
|
153
|
+
}
|
|
154
|
+
return created.id;
|
|
155
|
+
}
|
|
156
|
+
finally {
|
|
157
|
+
periodVersions.current.invalidate(fiscalPeriodId);
|
|
158
|
+
}
|
|
109
159
|
},
|
|
110
160
|
async updateFixedAsset(assetId, draft) {
|
|
161
|
+
assertEditingUnlocked(config.editingPolicy, "assist.updateFixedAsset");
|
|
162
|
+
const authOperationVersion = appState.captureAuthOperationVersion();
|
|
111
163
|
const current = fixedAssets.find((asset) => asset.id === assetId) ?? null;
|
|
112
|
-
const fiscalPeriodId = current?.fiscalPeriodId ?? appState.currentFiscalPeriodId
|
|
113
|
-
if (fiscalPeriodId
|
|
164
|
+
const fiscalPeriodId = current?.fiscalPeriodId ?? appState.currentFiscalPeriodId;
|
|
165
|
+
if (fiscalPeriodId == null)
|
|
114
166
|
return false;
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
167
|
+
const accountId = resolveFixedAssetAccountId(current == null
|
|
168
|
+
? null
|
|
169
|
+
: {
|
|
170
|
+
accountId: current.bookAccountId,
|
|
171
|
+
accountName: current.accountName,
|
|
172
|
+
}, draft.account, bookAccounts);
|
|
173
|
+
if (accountId == null) {
|
|
118
174
|
throw new AppError({
|
|
119
175
|
messageForDeveloper: "assist.updateFixedAsset: accountId missing",
|
|
120
176
|
messageForUser: "勘定科目が解決できないため保存できませんでした",
|
|
121
177
|
originalMessage: null,
|
|
122
178
|
statusCode: null,
|
|
179
|
+
code: null,
|
|
123
180
|
});
|
|
124
181
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
182
|
+
return await assetMutationQueue.current.run(assetId, async () => {
|
|
183
|
+
appState.assertAuthOperationCurrent(authOperationVersion);
|
|
184
|
+
periodVersions.current.invalidate(fiscalPeriodId);
|
|
185
|
+
try {
|
|
186
|
+
const patched = await backendApi.fixedAssets.patch(fiscalPeriodId, assetId, fixedAssetDraftToPatch(draft, accountId));
|
|
187
|
+
appState.assertAuthOperationCurrent(authOperationVersion);
|
|
188
|
+
if (selectedFiscalPeriodId.current === fiscalPeriodId) {
|
|
189
|
+
const mapped = mapFixedAsset(patched, bookAccountNameById[patched.bookAccountId] ?? null, fixedAssetPreviewAsOf, currentFiscalPeriodEndDate ?? null);
|
|
190
|
+
setFixedAssets((currentList) => upsertFixedAsset(currentList, mapped));
|
|
191
|
+
}
|
|
192
|
+
return true;
|
|
193
|
+
}
|
|
194
|
+
finally {
|
|
195
|
+
periodVersions.current.invalidate(fiscalPeriodId);
|
|
196
|
+
}
|
|
197
|
+
});
|
|
132
198
|
},
|
|
133
199
|
listOpeningCarryovers(fiscalPeriodId) {
|
|
134
200
|
const period = appState.fiscalPeriods.find((p) => p.id === fiscalPeriodId);
|
|
135
|
-
const journals = period?.opening
|
|
201
|
+
const journals = period?.opening.journals ?? [];
|
|
136
202
|
return journals
|
|
137
|
-
.map((journal) => mapOpeningJournalToRecord(journal, fiscalPeriodId, bookAccountNameById, bookAccountTypeById))
|
|
203
|
+
.map((journal) => mapOpeningJournalToRecord(journal, fiscalPeriodId, bookAccountNameById, bookAccountTypeById, taxCategoryNameById, businessCategoryNameById))
|
|
138
204
|
.sort((left, right) => {
|
|
139
205
|
const byDate = left.date.localeCompare(right.date);
|
|
140
206
|
if (byDate !== 0)
|
|
@@ -144,164 +210,133 @@ export function OpenkkAssistProvider(props) {
|
|
|
144
210
|
},
|
|
145
211
|
getOpeningCarryover(carryoverId) {
|
|
146
212
|
const fiscalPeriodId = appState.currentFiscalPeriodId;
|
|
147
|
-
if (fiscalPeriodId == null
|
|
213
|
+
if (fiscalPeriodId == null)
|
|
148
214
|
return null;
|
|
149
215
|
const period = appState.fiscalPeriods.find((p) => p.id === fiscalPeriodId);
|
|
150
|
-
const journals = period?.opening
|
|
216
|
+
const journals = period?.opening.journals ?? [];
|
|
151
217
|
const journal = journals.find((item) => item.id === carryoverId);
|
|
152
218
|
if (journal == null)
|
|
153
219
|
return null;
|
|
154
|
-
return mapOpeningJournalToRecord(journal, fiscalPeriodId, bookAccountNameById, bookAccountTypeById);
|
|
220
|
+
return mapOpeningJournalToRecord(journal, fiscalPeriodId, bookAccountNameById, bookAccountTypeById, taxCategoryNameById, businessCategoryNameById);
|
|
155
221
|
},
|
|
156
|
-
async addOpeningCarryover(fiscalPeriodId) {
|
|
222
|
+
async addOpeningCarryover(fiscalPeriodId, draft) {
|
|
223
|
+
assertEditingUnlocked(config.editingPolicy, "assist.addOpeningCarryover");
|
|
157
224
|
const period = appState.fiscalPeriods.find((p) => p.id === fiscalPeriodId);
|
|
158
|
-
|
|
159
|
-
if (period == null || opening == null)
|
|
225
|
+
if (period == null)
|
|
160
226
|
return null;
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
messageForUser: "勘定科目が取得できないため再振替仕訳を作成できませんでした",
|
|
171
|
-
originalMessage: null,
|
|
172
|
-
statusCode: null,
|
|
227
|
+
let nextId = null;
|
|
228
|
+
const updated = await appState.updateFiscalPeriod(fiscalPeriodId, (currentPeriod) => {
|
|
229
|
+
const currentOpening = currentPeriod.opening;
|
|
230
|
+
const generatedId = nextOpeningCarryoverId(fiscalPeriodId, currentOpening.journals);
|
|
231
|
+
nextId = generatedId;
|
|
232
|
+
const lines = buildOpeningJournalLines(generatedId, draft, {
|
|
233
|
+
accounts: bookAccounts,
|
|
234
|
+
taxCategories,
|
|
235
|
+
businessCategories,
|
|
173
236
|
});
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
{
|
|
192
|
-
id: `${nextId}-c`,
|
|
193
|
-
side: "credit",
|
|
194
|
-
bookAccountId: creditAccountId,
|
|
195
|
-
amount: 0,
|
|
196
|
-
partnerName: "",
|
|
197
|
-
taxCategoryId: "",
|
|
198
|
-
businessCategoryId: "",
|
|
237
|
+
if (lines == null) {
|
|
238
|
+
throw openingCarryoverAccountResolutionError("assist.addOpeningCarryover");
|
|
239
|
+
}
|
|
240
|
+
const newJournal = {
|
|
241
|
+
id: generatedId,
|
|
242
|
+
date: draft.date,
|
|
243
|
+
description: draft.description,
|
|
244
|
+
businessRate: draftBusinessRate(draft),
|
|
245
|
+
lines,
|
|
246
|
+
};
|
|
247
|
+
return {
|
|
248
|
+
opening: {
|
|
249
|
+
balanceLines: currentOpening.balanceLines,
|
|
250
|
+
journals: [
|
|
251
|
+
...currentOpening.journals,
|
|
252
|
+
newJournal,
|
|
253
|
+
],
|
|
199
254
|
},
|
|
200
|
-
|
|
201
|
-
};
|
|
202
|
-
const updatedJournals = [
|
|
203
|
-
...(opening.openingJournals ?? []),
|
|
204
|
-
newJournal,
|
|
205
|
-
];
|
|
206
|
-
await appState.updateFiscalPeriod(fiscalPeriodId, {
|
|
207
|
-
opening: {
|
|
208
|
-
...opening,
|
|
209
|
-
openingJournals: updatedJournals,
|
|
210
|
-
},
|
|
255
|
+
};
|
|
211
256
|
});
|
|
212
|
-
return nextId;
|
|
257
|
+
return updated ? nextId : null;
|
|
213
258
|
},
|
|
214
259
|
async updateOpeningCarryover(carryoverId, draft) {
|
|
260
|
+
assertEditingUnlocked(config.editingPolicy, "assist.updateOpeningCarryover");
|
|
215
261
|
const fiscalPeriodId = appState.currentFiscalPeriodId;
|
|
216
|
-
if (fiscalPeriodId == null
|
|
262
|
+
if (fiscalPeriodId == null)
|
|
217
263
|
return false;
|
|
218
264
|
const period = appState.fiscalPeriods.find((p) => p.id === fiscalPeriodId);
|
|
219
|
-
|
|
220
|
-
if (period == null || opening == null)
|
|
265
|
+
if (period == null)
|
|
221
266
|
return false;
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
267
|
+
return await appState.updateFiscalPeriod(fiscalPeriodId, (currentPeriod) => {
|
|
268
|
+
const currentOpening = currentPeriod.opening;
|
|
269
|
+
const journals = currentOpening.journals;
|
|
270
|
+
const target = journals.find((journal) => journal.id === carryoverId);
|
|
271
|
+
if (target == null)
|
|
272
|
+
return null;
|
|
273
|
+
const lines = buildOpeningJournalLines(target.id, draft, {
|
|
274
|
+
accounts: bookAccounts,
|
|
275
|
+
taxCategories,
|
|
276
|
+
businessCategories,
|
|
230
277
|
});
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
return journal;
|
|
237
|
-
updated = true;
|
|
238
|
-
return {
|
|
239
|
-
...journal,
|
|
278
|
+
if (lines == null) {
|
|
279
|
+
throw openingCarryoverAccountResolutionError("assist.updateOpeningCarryover");
|
|
280
|
+
}
|
|
281
|
+
const nextJournal = {
|
|
282
|
+
...target,
|
|
240
283
|
date: draft.date,
|
|
241
284
|
description: draft.description,
|
|
242
|
-
businessRate:
|
|
243
|
-
lines
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
partnerName: draft.partner,
|
|
251
|
-
taxCategoryId: draft.taxCategory,
|
|
252
|
-
businessCategoryId: draft.businessCategory,
|
|
253
|
-
},
|
|
254
|
-
{
|
|
255
|
-
id: journal.lines.find((line) => line.side === "credit")?.id ??
|
|
256
|
-
`${journal.id}-c`,
|
|
257
|
-
side: "credit",
|
|
258
|
-
bookAccountId: creditAccountId,
|
|
259
|
-
amount: parseAmount(draft.creditAmount),
|
|
260
|
-
partnerName: draft.partner,
|
|
261
|
-
taxCategoryId: draft.taxCategory,
|
|
262
|
-
businessCategoryId: draft.businessCategory,
|
|
263
|
-
},
|
|
264
|
-
],
|
|
285
|
+
businessRate: draftBusinessRate(draft),
|
|
286
|
+
lines,
|
|
287
|
+
};
|
|
288
|
+
return {
|
|
289
|
+
opening: {
|
|
290
|
+
balanceLines: currentOpening.balanceLines,
|
|
291
|
+
journals: journals.map((journal) => journal.id === carryoverId ? nextJournal : journal),
|
|
292
|
+
},
|
|
265
293
|
};
|
|
266
294
|
});
|
|
267
|
-
if (!updated)
|
|
268
|
-
return false;
|
|
269
|
-
await appState.updateFiscalPeriod(fiscalPeriodId, {
|
|
270
|
-
opening: {
|
|
271
|
-
...opening,
|
|
272
|
-
openingJournals: nextJournals,
|
|
273
|
-
},
|
|
274
|
-
});
|
|
275
|
-
return true;
|
|
276
295
|
},
|
|
277
296
|
async deleteFixedAsset(assetId) {
|
|
297
|
+
assertEditingUnlocked(config.editingPolicy, "assist.deleteFixedAsset");
|
|
298
|
+
const authOperationVersion = appState.captureAuthOperationVersion();
|
|
278
299
|
const current = fixedAssets.find((asset) => asset.id === assetId) ?? null;
|
|
279
|
-
const fiscalPeriodId = current?.fiscalPeriodId ?? appState.currentFiscalPeriodId
|
|
280
|
-
if (fiscalPeriodId
|
|
300
|
+
const fiscalPeriodId = current?.fiscalPeriodId ?? appState.currentFiscalPeriodId;
|
|
301
|
+
if (fiscalPeriodId == null)
|
|
281
302
|
return false;
|
|
282
|
-
await
|
|
283
|
-
|
|
284
|
-
|
|
303
|
+
return await assetMutationQueue.current.run(assetId, async () => {
|
|
304
|
+
appState.assertAuthOperationCurrent(authOperationVersion);
|
|
305
|
+
periodVersions.current.invalidate(fiscalPeriodId);
|
|
306
|
+
try {
|
|
307
|
+
await backendApi.fixedAssets.remove(fiscalPeriodId, assetId);
|
|
308
|
+
appState.assertAuthOperationCurrent(authOperationVersion);
|
|
309
|
+
if (selectedFiscalPeriodId.current === fiscalPeriodId) {
|
|
310
|
+
setFixedAssets((currentList) => currentList.filter((asset) => asset.id !== assetId));
|
|
311
|
+
}
|
|
312
|
+
return true;
|
|
313
|
+
}
|
|
314
|
+
finally {
|
|
315
|
+
periodVersions.current.invalidate(fiscalPeriodId);
|
|
316
|
+
}
|
|
317
|
+
});
|
|
285
318
|
},
|
|
286
319
|
async deleteOpeningCarryover(carryoverId) {
|
|
320
|
+
assertEditingUnlocked(config.editingPolicy, "assist.deleteOpeningCarryover");
|
|
287
321
|
const fiscalPeriodId = appState.currentFiscalPeriodId;
|
|
288
|
-
if (fiscalPeriodId == null
|
|
322
|
+
if (fiscalPeriodId == null)
|
|
289
323
|
return false;
|
|
290
324
|
const period = appState.fiscalPeriods.find((p) => p.id === fiscalPeriodId);
|
|
291
|
-
|
|
292
|
-
if (period == null || opening == null)
|
|
293
|
-
return false;
|
|
294
|
-
const journals = opening.openingJournals ?? [];
|
|
295
|
-
const nextJournals = journals.filter((journal) => journal.id !== carryoverId);
|
|
296
|
-
if (nextJournals.length === journals.length)
|
|
325
|
+
if (period == null)
|
|
297
326
|
return false;
|
|
298
|
-
await appState.updateFiscalPeriod(fiscalPeriodId, {
|
|
299
|
-
opening
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
327
|
+
return await appState.updateFiscalPeriod(fiscalPeriodId, (currentPeriod) => {
|
|
328
|
+
const currentOpening = currentPeriod.opening;
|
|
329
|
+
const journals = currentOpening.journals;
|
|
330
|
+
const nextJournals = journals.filter((journal) => journal.id !== carryoverId);
|
|
331
|
+
if (nextJournals.length === journals.length)
|
|
332
|
+
return null;
|
|
333
|
+
return {
|
|
334
|
+
opening: {
|
|
335
|
+
balanceLines: currentOpening.balanceLines,
|
|
336
|
+
journals: nextJournals,
|
|
337
|
+
},
|
|
338
|
+
};
|
|
303
339
|
});
|
|
304
|
-
return true;
|
|
305
340
|
},
|
|
306
341
|
loadError,
|
|
307
342
|
reload() {
|
|
@@ -311,140 +346,29 @@ export function OpenkkAssistProvider(props) {
|
|
|
311
346
|
}, [
|
|
312
347
|
appState.currentFiscalPeriodId,
|
|
313
348
|
appState.fiscalPeriods,
|
|
314
|
-
|
|
349
|
+
bookAccounts,
|
|
315
350
|
bookAccountNameById,
|
|
316
351
|
bookAccountTypeById,
|
|
317
|
-
|
|
352
|
+
businessCategories,
|
|
353
|
+
businessCategoryNameById,
|
|
354
|
+
currentFiscalPeriodEndDate,
|
|
355
|
+
fixedAssetPreviewAsOf,
|
|
318
356
|
fixedAssets,
|
|
319
|
-
|
|
357
|
+
fixedAssetsLoadError,
|
|
358
|
+
masterLoadError,
|
|
359
|
+
taxCategories,
|
|
360
|
+
taxCategoryNameById,
|
|
320
361
|
]);
|
|
321
362
|
return (_jsx(AssistContext.Provider, { value: value, children: props.children }));
|
|
322
363
|
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
const maxSuffix = journals.reduce((max, journal) => {
|
|
331
|
-
if (!journal.id.startsWith(prefix))
|
|
332
|
-
return max;
|
|
333
|
-
const suffix = Number(journal.id.slice(prefix.length));
|
|
334
|
-
return Number.isInteger(suffix) && suffix > max ? suffix : max;
|
|
335
|
-
}, 0);
|
|
336
|
-
return `${prefix}${maxSuffix + 1}`;
|
|
337
|
-
}
|
|
338
|
-
function mapOpeningJournalToRecord(journal, fiscalPeriodId, accountNameById, accountTypeById) {
|
|
339
|
-
const debit = journal.lines.find((line) => line.side === "debit");
|
|
340
|
-
const credit = journal.lines.find((line) => line.side === "credit");
|
|
341
|
-
return {
|
|
342
|
-
id: journal.id,
|
|
343
|
-
fiscalPeriodId,
|
|
344
|
-
date: journal.date,
|
|
345
|
-
description: journal.description,
|
|
346
|
-
debit: accountNameById[debit?.bookAccountId ?? ""] ?? debit?.bookAccountId ?? "",
|
|
347
|
-
debitType: accountTypeById[debit?.bookAccountId ?? ""] ?? "asset",
|
|
348
|
-
debitAmount: formatAmount(debit?.amount ?? 0),
|
|
349
|
-
credit: accountNameById[credit?.bookAccountId ?? ""] ??
|
|
350
|
-
credit?.bookAccountId ??
|
|
351
|
-
"",
|
|
352
|
-
creditType: accountTypeById[credit?.bookAccountId ?? ""] ?? "revenue",
|
|
353
|
-
creditAmount: formatAmount(credit?.amount ?? 0),
|
|
354
|
-
partner: debit?.partnerName ?? credit?.partnerName ?? "",
|
|
355
|
-
taxCategory: debit?.taxCategoryId ?? credit?.taxCategoryId ?? "対象外",
|
|
356
|
-
businessCategory: debit?.businessCategoryId ?? credit?.businessCategoryId ?? "対象外",
|
|
357
|
-
businessRate: String(Math.round((journal.businessRate ?? 1) * 100)),
|
|
358
|
-
debitBookAccountId: debit?.bookAccountId,
|
|
359
|
-
creditBookAccountId: credit?.bookAccountId,
|
|
360
|
-
};
|
|
361
|
-
}
|
|
362
|
-
function resolveBookAccountIdByName(name, accountIdByName) {
|
|
363
|
-
const id = accountIdByName[name];
|
|
364
|
-
return id == null || id.length === 0 ? null : id;
|
|
365
|
-
}
|
|
366
|
-
function formatAmount(value) {
|
|
367
|
-
return new Intl.NumberFormat("ja-JP").format(Math.abs(value));
|
|
368
|
-
}
|
|
369
|
-
function mapFixedAssetToPreview(asset, accountName, today) {
|
|
370
|
-
// 売却・廃棄・除却済みは処分日(無ければ今日)で償却を打ち切る。
|
|
371
|
-
const isClosed = asset.status !== "active";
|
|
372
|
-
const asOf = isClosed && asset.disposalDate
|
|
373
|
-
? (parseIsoLocalDate(asset.disposalDate) ?? today)
|
|
374
|
-
: today;
|
|
375
|
-
const depreciation = computeStraightLineDepreciation({
|
|
376
|
-
acquisitionDate: asset.acquisitionDate,
|
|
377
|
-
acquisitionCost: asset.acquisitionCost,
|
|
378
|
-
usefulLife: asset.usefulLife,
|
|
379
|
-
asOf,
|
|
364
|
+
function openingCarryoverAccountResolutionError(operation) {
|
|
365
|
+
return new AppError({
|
|
366
|
+
messageForDeveloper: `${operation}: account resolution failed`,
|
|
367
|
+
messageForUser: "勘定科目が解決できないため保存できませんでした",
|
|
368
|
+
originalMessage: null,
|
|
369
|
+
statusCode: null,
|
|
370
|
+
code: null,
|
|
380
371
|
});
|
|
381
|
-
return {
|
|
382
|
-
id: asset.id,
|
|
383
|
-
fiscalPeriodId: asset.fiscalPeriodId,
|
|
384
|
-
name: asset.name,
|
|
385
|
-
account: accountName ?? asset.bookAccountId,
|
|
386
|
-
accountId: asset.bookAccountId,
|
|
387
|
-
period: depreciation.periodLabel,
|
|
388
|
-
remaining: depreciation.remainingLabel,
|
|
389
|
-
progress: depreciation.progress,
|
|
390
|
-
current: formatYen(depreciation.currentBookValue),
|
|
391
|
-
purchase: formatYen(asset.acquisitionCost),
|
|
392
|
-
status: mapFixedAssetStatusLabel(asset.status),
|
|
393
|
-
depreciationAmount: formatYen(depreciation.annualDepreciation),
|
|
394
|
-
acquisitionDate: asset.acquisitionDate,
|
|
395
|
-
acquisitionCost: asset.acquisitionCost,
|
|
396
|
-
usefulLife: asset.usefulLife,
|
|
397
|
-
businessRate: asset.businessRate,
|
|
398
|
-
disposalDate: asset.disposalDate || undefined,
|
|
399
|
-
disposalPrice: asset.disposalPrice
|
|
400
|
-
? formatYen(asset.disposalPrice)
|
|
401
|
-
: undefined,
|
|
402
|
-
};
|
|
403
|
-
}
|
|
404
|
-
function formatYen(value) {
|
|
405
|
-
return new Intl.NumberFormat("ja-JP").format(value);
|
|
406
|
-
}
|
|
407
|
-
function mapFixedAssetStatusLabel(status) {
|
|
408
|
-
if (status === "active")
|
|
409
|
-
return "償却中";
|
|
410
|
-
if (status === "sold")
|
|
411
|
-
return "売却済";
|
|
412
|
-
if (status === "disposed")
|
|
413
|
-
return "廃棄済";
|
|
414
|
-
if (status === "retired")
|
|
415
|
-
return "完了";
|
|
416
|
-
return status;
|
|
417
|
-
}
|
|
418
|
-
function mapFixedAssetStatusApi(statusLabel) {
|
|
419
|
-
if (statusLabel === "償却中")
|
|
420
|
-
return "active";
|
|
421
|
-
if (statusLabel === "売却済")
|
|
422
|
-
return "sold";
|
|
423
|
-
if (statusLabel === "廃棄済")
|
|
424
|
-
return "disposed";
|
|
425
|
-
if (statusLabel === "完了")
|
|
426
|
-
return "retired";
|
|
427
|
-
return "active";
|
|
428
|
-
}
|
|
429
|
-
export function fixedAssetDraftToPatch(draft, bookAccountId) {
|
|
430
|
-
return {
|
|
431
|
-
name: draft.name,
|
|
432
|
-
acquisitionDate: draft.acquisitionDate,
|
|
433
|
-
acquisitionCost: parseAmount(draft.acquisitionCost),
|
|
434
|
-
usefulLife: Math.max(1, Math.round(draft.usefulLife) || 1),
|
|
435
|
-
businessRate: Math.max(0, Math.min(100, draft.businessRatePercent)) / 100,
|
|
436
|
-
status: mapFixedAssetStatusApi(draft.status),
|
|
437
|
-
disposalDate: isFixedAssetClosedStatus(draft.status)
|
|
438
|
-
? (draft.disposalDate ?? "")
|
|
439
|
-
: "",
|
|
440
|
-
disposalPrice: draft.status === "売却済" ? parseAmount(draft.disposalPrice ?? "0") : 0,
|
|
441
|
-
bookAccountId,
|
|
442
|
-
};
|
|
443
|
-
}
|
|
444
|
-
function isFixedAssetClosedStatus(statusLabel) {
|
|
445
|
-
return (statusLabel === "売却済" ||
|
|
446
|
-
statusLabel === "廃棄済" ||
|
|
447
|
-
statusLabel === "完了");
|
|
448
372
|
}
|
|
449
373
|
export function useOpenkkAssist() {
|
|
450
374
|
const value = useContext(AssistContext);
|
|
@@ -454,6 +378,7 @@ export function useOpenkkAssist() {
|
|
|
454
378
|
messageForUser: "補助データを読み込めませんでした",
|
|
455
379
|
originalMessage: null,
|
|
456
380
|
statusCode: null,
|
|
381
|
+
code: null,
|
|
457
382
|
});
|
|
458
383
|
}
|
|
459
384
|
return value;
|