@zeniai/client-epic-state 5.0.81 → 5.0.82-betaAK1
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/lib/commonStateTypes/amount.d.ts +2 -1
- package/lib/commonStateTypes/amount.js +5 -2
- package/lib/commonStateTypes/viewAndReport/viewAndReport.d.ts +2 -2
- package/lib/commonStateTypes/viewAndReport/viewAndReport.js +2 -0
- package/lib/commonStateTypes/workingDayHelper.d.ts +11 -0
- package/lib/commonStateTypes/workingDayHelper.js +31 -1
- package/lib/entity/account/accountSelector.d.ts +9 -0
- package/lib/entity/account/accountSelector.js +14 -1
- package/lib/entity/account/accountState.d.ts +1 -1
- package/lib/entity/accountGroup/accountGroupState.d.ts +1 -1
- package/lib/entity/class/classReducer.d.ts +4 -4
- package/lib/entity/class/classState.d.ts +1 -1
- package/lib/entity/forecast/forecastState.d.ts +1 -1
- package/lib/entity/sectionAccountsView/sectionAccountsView.d.ts +1 -1
- package/lib/entity/sectionClassesViewV2/sectionClassesView.d.ts +1 -1
- package/lib/entity/sectionClassesViewV2/sectionClassesViewReducer.d.ts +2 -2
- package/lib/entity/sectionProjectView/sectionProjectView.d.ts +1 -1
- package/lib/entity/sectionProjectView/sectionProjectViewReducer.d.ts +2 -2
- package/lib/entity/snackbar/snackbarTypes.d.ts +1 -1
- package/lib/entity/snackbar/snackbarTypes.js +1 -0
- package/lib/entity/tenant/clearAllEpic.d.ts +2 -1
- package/lib/entity/tenant/clearAllEpic.js +2 -0
- package/lib/entity/transaction/payloadTypes/transactionPayload.d.ts +4 -0
- package/lib/entity/transaction/payloadTypes/transactionPayload.js +18 -10
- package/lib/entity/transaction/stateTypes/transaction.d.ts +3 -0
- package/lib/epic.d.ts +7 -1
- package/lib/epic.js +7 -1
- package/lib/esm/commonStateTypes/amount.js +3 -1
- package/lib/esm/commonStateTypes/viewAndReport/viewAndReport.js +2 -0
- package/lib/esm/commonStateTypes/workingDayHelper.js +29 -0
- package/lib/esm/entity/account/accountSelector.js +11 -0
- package/lib/esm/entity/snackbar/snackbarTypes.js +1 -0
- package/lib/esm/entity/tenant/clearAllEpic.js +2 -0
- package/lib/esm/entity/transaction/payloadTypes/transactionPayload.js +18 -10
- package/lib/esm/epic.js +7 -1
- package/lib/esm/index.js +12 -6
- package/lib/esm/reducer.js +9 -0
- package/lib/esm/view/createTransferEntry/createTransferEntryReducer.js +82 -0
- package/lib/esm/view/createTransferEntry/createTransferEntrySelector.js +64 -0
- package/lib/esm/view/createTransferEntry/createTransferEntryState.js +17 -0
- package/lib/esm/view/createTransferEntry/epics/createTransferEntryEpic.js +130 -0
- package/lib/esm/view/createTransferEntry/epics/fetchTransferAccountsEpic.js +36 -0
- package/lib/esm/view/expenseAutomationView/reducers/transactionsViewReducer.js +13 -1
- package/lib/esm/view/expenseAutomationView/selectors/transactionCategorizationSelector.js +9 -1
- package/lib/esm/view/spendManagement/cashManagement/autoSweepFlow/autoSweepFlowPayload.js +24 -0
- package/lib/esm/view/spendManagement/cashManagement/autoSweepFlow/autoSweepFlowReducer.js +55 -0
- package/lib/esm/view/spendManagement/cashManagement/autoSweepFlow/autoSweepFlowSelector.js +15 -0
- package/lib/esm/view/spendManagement/cashManagement/autoSweepFlow/autoSweepFlowState.js +45 -0
- package/lib/esm/view/spendManagement/cashManagement/autoSweepFlow/epics/fetchCashManagementSettingsEpic.js +28 -0
- package/lib/esm/view/spendManagement/cashManagement/autoSweepFlow/epics/saveAutoSweepSettingsEpic.js +27 -0
- package/lib/esm/view/spendManagement/cashManagement/cashManagementOverview/cashManagementOverviewPayload.js +37 -0
- package/lib/esm/view/spendManagement/cashManagement/cashManagementOverview/cashManagementOverviewReducer.js +49 -0
- package/lib/esm/view/spendManagement/cashManagement/cashManagementOverview/cashManagementOverviewSelector.js +179 -0
- package/lib/esm/view/spendManagement/cashManagement/cashManagementOverview/cashManagementOverviewState.js +5 -0
- package/lib/esm/view/spendManagement/cashManagement/cashManagementOverview/epics/fetchCashManagementBannerEpic.js +28 -0
- package/lib/esm/view/spendManagement/cashManagement/cashManagementOverview/epics/fetchCashManagementOverviewPageEpic.js +39 -0
- package/lib/esm/view/transactionDetail/transactionDetailSelector.js +6 -1
- package/lib/index.d.ts +15 -6
- package/lib/index.js +62 -33
- package/lib/reducer.d.ts +9 -0
- package/lib/reducer.js +9 -0
- package/lib/view/auditReportView/auditReportViewState.d.ts +1 -1
- package/lib/view/billPayCard/billPayCardSelector.d.ts +1 -1
- package/lib/view/cardBalance/cardBalanceSelector.d.ts +1 -1
- package/lib/view/cashBalance/cashBalanceSelector.d.ts +1 -1
- package/lib/view/cashInCashOut/cashInCashOutSelector.d.ts +1 -1
- package/lib/view/cashPosition/cashPositionSelector.d.ts +1 -1
- package/lib/view/createTransferEntry/createTransferEntryReducer.d.ts +32 -0
- package/lib/view/createTransferEntry/createTransferEntryReducer.js +86 -0
- package/lib/view/createTransferEntry/createTransferEntrySelector.d.ts +39 -0
- package/lib/view/createTransferEntry/createTransferEntrySelector.js +69 -0
- package/lib/view/createTransferEntry/createTransferEntryState.d.ts +30 -0
- package/lib/view/createTransferEntry/createTransferEntryState.js +20 -0
- package/lib/view/createTransferEntry/epics/createTransferEntryEpic.d.ts +11 -0
- package/lib/view/createTransferEntry/epics/createTransferEntryEpic.js +134 -0
- package/lib/view/createTransferEntry/epics/fetchTransferAccountsEpic.d.ts +14 -0
- package/lib/view/createTransferEntry/epics/fetchTransferAccountsEpic.js +40 -0
- package/lib/view/dashboard/dashboardReducer.d.ts +2 -2
- package/lib/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.d.ts +1 -1
- package/lib/view/expenseAutomationView/reducers/transactionsViewReducer.d.ts +3 -1
- package/lib/view/expenseAutomationView/reducers/transactionsViewReducer.js +14 -2
- package/lib/view/expenseAutomationView/selectorTypes/transactionsViewSelectorTypes.d.ts +4 -1
- package/lib/view/expenseAutomationView/selectors/transactionCategorizationSelector.d.ts +1 -0
- package/lib/view/expenseAutomationView/selectors/transactionCategorizationSelector.js +11 -2
- package/lib/view/financeStatement/financeStatementReducer.d.ts +1 -1
- package/lib/view/globalMerchantView/globalMerchantViewReducer.d.ts +2 -2
- package/lib/view/reimbursementCard/reimbursementCardSelector.d.ts +1 -1
- package/lib/view/reportsResync/reportsResyncReducer.d.ts +2 -2
- package/lib/view/spendManagement/cashManagement/autoSweepFlow/autoSweepFlowPayload.d.ts +52 -0
- package/lib/view/spendManagement/cashManagement/autoSweepFlow/autoSweepFlowPayload.js +29 -0
- package/lib/view/spendManagement/cashManagement/autoSweepFlow/autoSweepFlowReducer.d.ts +21 -0
- package/lib/view/spendManagement/cashManagement/autoSweepFlow/autoSweepFlowReducer.js +59 -0
- package/lib/view/spendManagement/cashManagement/autoSweepFlow/autoSweepFlowSelector.d.ts +16 -0
- package/lib/view/spendManagement/cashManagement/autoSweepFlow/autoSweepFlowSelector.js +19 -0
- package/lib/view/spendManagement/cashManagement/autoSweepFlow/autoSweepFlowState.d.ts +44 -0
- package/lib/view/spendManagement/cashManagement/autoSweepFlow/autoSweepFlowState.js +51 -0
- package/lib/view/spendManagement/cashManagement/autoSweepFlow/epics/fetchCashManagementSettingsEpic.d.ts +16 -0
- package/lib/view/spendManagement/cashManagement/autoSweepFlow/epics/fetchCashManagementSettingsEpic.js +32 -0
- package/lib/view/spendManagement/cashManagement/autoSweepFlow/epics/saveAutoSweepSettingsEpic.d.ts +14 -0
- package/lib/view/spendManagement/cashManagement/autoSweepFlow/epics/saveAutoSweepSettingsEpic.js +31 -0
- package/lib/view/spendManagement/cashManagement/cashManagementOverview/cashManagementOverviewPayload.d.ts +32 -0
- package/lib/view/spendManagement/cashManagement/cashManagementOverview/cashManagementOverviewPayload.js +42 -0
- package/lib/view/spendManagement/cashManagement/cashManagementOverview/cashManagementOverviewReducer.d.ts +15 -0
- package/lib/view/spendManagement/cashManagement/cashManagementOverview/cashManagementOverviewReducer.js +53 -0
- package/lib/view/spendManagement/cashManagement/cashManagementOverview/cashManagementOverviewSelector.d.ts +41 -0
- package/lib/view/spendManagement/cashManagement/cashManagementOverview/cashManagementOverviewSelector.js +184 -0
- package/lib/view/spendManagement/cashManagement/cashManagementOverview/cashManagementOverviewState.d.ts +71 -0
- package/lib/view/spendManagement/cashManagement/cashManagementOverview/cashManagementOverviewState.js +8 -0
- package/lib/view/spendManagement/cashManagement/cashManagementOverview/epics/fetchCashManagementBannerEpic.d.ts +16 -0
- package/lib/view/spendManagement/cashManagement/cashManagementOverview/epics/fetchCashManagementBannerEpic.js +32 -0
- package/lib/view/spendManagement/cashManagement/cashManagementOverview/epics/fetchCashManagementOverviewPageEpic.d.ts +10 -0
- package/lib/view/spendManagement/cashManagement/cashManagementOverview/epics/fetchCashManagementOverviewPageEpic.js +43 -0
- package/lib/view/tasksCard/tasksCardSelector.d.ts +1 -1
- package/lib/view/topEx/topExSelector.d.ts +1 -1
- package/lib/view/transactionDetail/transactionDetailPayload.d.ts +1 -1
- package/lib/view/transactionDetail/transactionDetailSelector.d.ts +6 -1
- package/lib/view/transactionDetail/transactionDetailSelector.js +6 -1
- package/package.json +34 -62
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Amount } from '../../../../commonStateTypes/amount';
|
|
2
|
+
import { FetchStateAndError, ID } from '../../../../commonStateTypes/common';
|
|
3
|
+
/**
|
|
4
|
+
* Cadence at which the auto-sweep transfer fires. Lowercase to match the
|
|
5
|
+
* cash-management settings API contract (and the `SweepFrequency` literal
|
|
6
|
+
* already used by `<AutoSweepSetupPage>` in web-components).
|
|
7
|
+
*/
|
|
8
|
+
export declare const ALL_AUTO_SWEEP_FREQUENCIES: readonly ["daily", "weekly", "biweekly", "monthly"];
|
|
9
|
+
export declare const toAutoSweepFrequency: (v?: string) => "weekly" | "biweekly" | "monthly" | "daily";
|
|
10
|
+
export type AutoSweepFrequency = NonNullable<ReturnType<typeof toAutoSweepFrequency>>;
|
|
11
|
+
/**
|
|
12
|
+
* Risk presets the auto-sweep setup form exposes. Each level maps to a
|
|
13
|
+
* canonical minimum-buffer amount (USD). Selecting a level in the UI is
|
|
14
|
+
* shorthand for choosing that buffer.
|
|
15
|
+
*/
|
|
16
|
+
export declare const ALL_AUTO_SWEEP_RISK_LEVELS: readonly ["low", "moderate", "high"];
|
|
17
|
+
export declare const toAutoSweepRiskLevel: (v?: string) => "high" | "low" | "moderate";
|
|
18
|
+
export type RiskLevel = NonNullable<ReturnType<typeof toAutoSweepRiskLevel>>;
|
|
19
|
+
export declare const RISK_BUFFER_AMOUNT: Record<RiskLevel, number>;
|
|
20
|
+
/**
|
|
21
|
+
* Maps a buffer amount back onto the closest preset risk level. Falls back
|
|
22
|
+
* to `'moderate'` when the amount doesn't match a known preset (e.g. the
|
|
23
|
+
* server returned a custom value). The dropdown in `<AutoSweepSetupPage>`
|
|
24
|
+
* uses this so it can pre-select the user's existing band on edit.
|
|
25
|
+
*/
|
|
26
|
+
export declare const bufferAmountToRisk: (amount: number) => RiskLevel;
|
|
27
|
+
/**
|
|
28
|
+
* Auto-sweep configuration state.
|
|
29
|
+
*
|
|
30
|
+
* `fetchState` / `error` (inherited from `FetchStateAndError`) track the
|
|
31
|
+
* GET `/cash-management/settings` fetch. `saveStatus` tracks the PUT save
|
|
32
|
+
* separately so the two flows don't stomp on each other's status.
|
|
33
|
+
*
|
|
34
|
+
* `settingsId` is the server-issued id (nullable on the wire — undefined
|
|
35
|
+
* here when the agent has no settings record yet).
|
|
36
|
+
*/
|
|
37
|
+
export interface AutoSweepFlowState extends FetchStateAndError {
|
|
38
|
+
bufferAmount: Amount;
|
|
39
|
+
frequency: AutoSweepFrequency;
|
|
40
|
+
memo: string;
|
|
41
|
+
saveStatus: FetchStateAndError;
|
|
42
|
+
settingsId?: ID;
|
|
43
|
+
}
|
|
44
|
+
export declare const initialAutoSweepFlowState: AutoSweepFlowState;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.initialAutoSweepFlowState = exports.bufferAmountToRisk = exports.RISK_BUFFER_AMOUNT = exports.toAutoSweepRiskLevel = exports.ALL_AUTO_SWEEP_RISK_LEVELS = exports.toAutoSweepFrequency = exports.ALL_AUTO_SWEEP_FREQUENCIES = void 0;
|
|
4
|
+
const amount_1 = require("../../../../commonStateTypes/amount");
|
|
5
|
+
const stringToUnion_1 = require("../../../../commonStateTypes/stringToUnion");
|
|
6
|
+
/**
|
|
7
|
+
* Cadence at which the auto-sweep transfer fires. Lowercase to match the
|
|
8
|
+
* cash-management settings API contract (and the `SweepFrequency` literal
|
|
9
|
+
* already used by `<AutoSweepSetupPage>` in web-components).
|
|
10
|
+
*/
|
|
11
|
+
exports.ALL_AUTO_SWEEP_FREQUENCIES = [
|
|
12
|
+
'daily',
|
|
13
|
+
'weekly',
|
|
14
|
+
'biweekly',
|
|
15
|
+
'monthly',
|
|
16
|
+
];
|
|
17
|
+
const toAutoSweepFrequency = (v) => (0, stringToUnion_1.stringToUnion)(v ?? '', exports.ALL_AUTO_SWEEP_FREQUENCIES);
|
|
18
|
+
exports.toAutoSweepFrequency = toAutoSweepFrequency;
|
|
19
|
+
/**
|
|
20
|
+
* Risk presets the auto-sweep setup form exposes. Each level maps to a
|
|
21
|
+
* canonical minimum-buffer amount (USD). Selecting a level in the UI is
|
|
22
|
+
* shorthand for choosing that buffer.
|
|
23
|
+
*/
|
|
24
|
+
exports.ALL_AUTO_SWEEP_RISK_LEVELS = ['low', 'moderate', 'high'];
|
|
25
|
+
const toAutoSweepRiskLevel = (v) => (0, stringToUnion_1.stringToUnion)(v ?? '', exports.ALL_AUTO_SWEEP_RISK_LEVELS);
|
|
26
|
+
exports.toAutoSweepRiskLevel = toAutoSweepRiskLevel;
|
|
27
|
+
exports.RISK_BUFFER_AMOUNT = {
|
|
28
|
+
low: 15000,
|
|
29
|
+
moderate: 10000,
|
|
30
|
+
high: 5000,
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Maps a buffer amount back onto the closest preset risk level. Falls back
|
|
34
|
+
* to `'moderate'` when the amount doesn't match a known preset (e.g. the
|
|
35
|
+
* server returned a custom value). The dropdown in `<AutoSweepSetupPage>`
|
|
36
|
+
* uses this so it can pre-select the user's existing band on edit.
|
|
37
|
+
*/
|
|
38
|
+
const bufferAmountToRisk = (amount) => {
|
|
39
|
+
const match = Object.entries(exports.RISK_BUFFER_AMOUNT).find(([, presetAmount]) => presetAmount === amount);
|
|
40
|
+
return match != null ? match[0] : 'moderate';
|
|
41
|
+
};
|
|
42
|
+
exports.bufferAmountToRisk = bufferAmountToRisk;
|
|
43
|
+
exports.initialAutoSweepFlowState = {
|
|
44
|
+
bufferAmount: (0, amount_1.toAmount)(10000, 'USD'),
|
|
45
|
+
frequency: 'weekly',
|
|
46
|
+
memo: '',
|
|
47
|
+
saveStatus: { fetchState: 'Not-Started', error: undefined },
|
|
48
|
+
settingsId: undefined,
|
|
49
|
+
fetchState: 'Not-Started',
|
|
50
|
+
error: undefined,
|
|
51
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ActionsObservable, StateObservable } from 'redux-observable';
|
|
2
|
+
import { Observable } from 'rxjs';
|
|
3
|
+
import { RootState } from '../../../../../reducer';
|
|
4
|
+
import { ZeniAPI } from '../../../../../zeniAPI';
|
|
5
|
+
import { fetchCashManagementSettings, updateCashManagementSettings, updateCashManagementSettingsFetchStatus } from '../autoSweepFlowReducer';
|
|
6
|
+
export type ActionType = ReturnType<typeof fetchCashManagementSettings> | ReturnType<typeof updateCashManagementSettings> | ReturnType<typeof updateCashManagementSettingsFetchStatus>;
|
|
7
|
+
/**
|
|
8
|
+
* Hits `GET /cash-management/settings` and hydrates the auto-sweep slice
|
|
9
|
+
* with the server's existing settings (frequency, minimum buffer, settings
|
|
10
|
+
* id). Called from the cash management overview page-fetch epic alongside
|
|
11
|
+
* the other top-level fetches.
|
|
12
|
+
*
|
|
13
|
+
* TODO: switch the endpoint to a base URL constant once the prod endpoint
|
|
14
|
+
* lands; right now this hits the dev environment directly.
|
|
15
|
+
*/
|
|
16
|
+
export declare const fetchCashManagementSettingsEpic: (actions$: ActionsObservable<ActionType>, _state$: StateObservable<RootState>, zeniAPI: ZeniAPI) => Observable<ActionType>;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.fetchCashManagementSettingsEpic = void 0;
|
|
4
|
+
const rxjs_1 = require("rxjs");
|
|
5
|
+
const operators_1 = require("rxjs/operators");
|
|
6
|
+
const responsePayload_1 = require("../../../../../responsePayload");
|
|
7
|
+
const autoSweepFlowReducer_1 = require("../autoSweepFlowReducer");
|
|
8
|
+
/**
|
|
9
|
+
* Hits `GET /cash-management/settings` and hydrates the auto-sweep slice
|
|
10
|
+
* with the server's existing settings (frequency, minimum buffer, settings
|
|
11
|
+
* id). Called from the cash management overview page-fetch epic alongside
|
|
12
|
+
* the other top-level fetches.
|
|
13
|
+
*
|
|
14
|
+
* TODO: switch the endpoint to a base URL constant once the prod endpoint
|
|
15
|
+
* lands; right now this hits the dev environment directly.
|
|
16
|
+
*/
|
|
17
|
+
const fetchCashManagementSettingsEpic = (actions$, _state$, zeniAPI) => actions$.pipe((0, operators_1.filter)(autoSweepFlowReducer_1.fetchCashManagementSettings.match), (0, operators_1.switchMap)(() => zeniAPI
|
|
18
|
+
.getJSON(`https://dev.api.zeni.ai/cash-management-agent/1.0/cash-management/settings`)
|
|
19
|
+
.pipe((0, operators_1.mergeMap)((response) => {
|
|
20
|
+
if ((0, responsePayload_1.isSuccessResponse)(response) && response.data != null) {
|
|
21
|
+
return (0, rxjs_1.of)((0, autoSweepFlowReducer_1.updateCashManagementSettings)({ data: response.data }));
|
|
22
|
+
}
|
|
23
|
+
return (0, rxjs_1.of)((0, autoSweepFlowReducer_1.updateCashManagementSettingsFetchStatus)({
|
|
24
|
+
fetchState: 'Error',
|
|
25
|
+
error: response.status,
|
|
26
|
+
}));
|
|
27
|
+
}), (0, operators_1.catchError)((error) => (0, rxjs_1.of)((0, autoSweepFlowReducer_1.updateCashManagementSettingsFetchStatus)({
|
|
28
|
+
fetchState: 'Error',
|
|
29
|
+
error: (0, responsePayload_1.createZeniAPIStatus)('Unexpected Error', 'Fetch cash management settings REST API call errored out' +
|
|
30
|
+
JSON.stringify(error)),
|
|
31
|
+
}))))));
|
|
32
|
+
exports.fetchCashManagementSettingsEpic = fetchCashManagementSettingsEpic;
|
package/lib/view/spendManagement/cashManagement/autoSweepFlow/epics/saveAutoSweepSettingsEpic.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ActionsObservable, StateObservable } from 'redux-observable';
|
|
2
|
+
import { Observable } from 'rxjs';
|
|
3
|
+
import { RootState } from '../../../../../reducer';
|
|
4
|
+
import { ZeniAPI } from '../../../../../zeniAPI';
|
|
5
|
+
import { saveAutoSweepSettings, updateAutoSweepSettingsFetchStatus } from '../autoSweepFlowReducer';
|
|
6
|
+
export type ActionType = ReturnType<typeof saveAutoSweepSettings> | ReturnType<typeof updateAutoSweepSettingsFetchStatus>;
|
|
7
|
+
/**
|
|
8
|
+
* Hits `PUT /cash-management/settings` with the values the user just
|
|
9
|
+
* confirmed in the auto-sweep flow.
|
|
10
|
+
*
|
|
11
|
+
* TODO: switch the endpoint to a base URL constant once the prod endpoint
|
|
12
|
+
* lands; right now this hits the dev environment directly.
|
|
13
|
+
*/
|
|
14
|
+
export declare const saveAutoSweepSettingsEpic: (actions$: ActionsObservable<ActionType>, _state$: StateObservable<RootState>, zeniAPI: ZeniAPI) => Observable<ActionType>;
|
package/lib/view/spendManagement/cashManagement/autoSweepFlow/epics/saveAutoSweepSettingsEpic.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.saveAutoSweepSettingsEpic = void 0;
|
|
4
|
+
const rxjs_1 = require("rxjs");
|
|
5
|
+
const operators_1 = require("rxjs/operators");
|
|
6
|
+
const responsePayload_1 = require("../../../../../responsePayload");
|
|
7
|
+
const autoSweepFlowPayload_1 = require("../autoSweepFlowPayload");
|
|
8
|
+
const autoSweepFlowReducer_1 = require("../autoSweepFlowReducer");
|
|
9
|
+
/**
|
|
10
|
+
* Hits `PUT /cash-management/settings` with the values the user just
|
|
11
|
+
* confirmed in the auto-sweep flow.
|
|
12
|
+
*
|
|
13
|
+
* TODO: switch the endpoint to a base URL constant once the prod endpoint
|
|
14
|
+
* lands; right now this hits the dev environment directly.
|
|
15
|
+
*/
|
|
16
|
+
const saveAutoSweepSettingsEpic = (actions$, _state$, zeniAPI) => actions$.pipe((0, operators_1.filter)(autoSweepFlowReducer_1.saveAutoSweepSettings.match), (0, operators_1.switchMap)((action) => zeniAPI
|
|
17
|
+
.putAndGetJSON(`https://dev.api.zeni.ai/cash-management-agent/1.0/cash-management/settings`, (0, autoSweepFlowPayload_1.toSaveAutoSweepSettingsBody)(action.payload))
|
|
18
|
+
.pipe((0, operators_1.mergeMap)((response) => {
|
|
19
|
+
if ((0, responsePayload_1.isSuccessResponse)(response)) {
|
|
20
|
+
return (0, rxjs_1.of)((0, autoSweepFlowReducer_1.updateAutoSweepSettingsFetchStatus)({ fetchState: 'Completed' }));
|
|
21
|
+
}
|
|
22
|
+
return (0, rxjs_1.of)((0, autoSweepFlowReducer_1.updateAutoSweepSettingsFetchStatus)({
|
|
23
|
+
fetchState: 'Error',
|
|
24
|
+
error: response.status,
|
|
25
|
+
}));
|
|
26
|
+
}), (0, operators_1.catchError)((error) => (0, rxjs_1.of)((0, autoSweepFlowReducer_1.updateAutoSweepSettingsFetchStatus)({
|
|
27
|
+
fetchState: 'Error',
|
|
28
|
+
error: (0, responsePayload_1.createZeniAPIStatus)('Unexpected Error', 'Save auto-sweep settings REST API call errored out' +
|
|
29
|
+
JSON.stringify(error)),
|
|
30
|
+
}))))));
|
|
31
|
+
exports.saveAutoSweepSettingsEpic = saveAutoSweepSettingsEpic;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { ZeniAPIResponse } from '../../../../responsePayload';
|
|
2
|
+
import { CashManagementBannerData, CashManagementBankAccountType, CashManagementSubMovementType, UpcomingPaymentEvent, UpcomingPaymentEventAccountType } from './cashManagementOverviewState';
|
|
3
|
+
export interface CashAgentEventPayload {
|
|
4
|
+
account_id: string;
|
|
5
|
+
account_type: UpcomingPaymentEventAccountType;
|
|
6
|
+
amount_usd: number;
|
|
7
|
+
currency_code: string;
|
|
8
|
+
label: string;
|
|
9
|
+
payment_date: string;
|
|
10
|
+
}
|
|
11
|
+
export interface CashAgentMovementPayload {
|
|
12
|
+
amount: number;
|
|
13
|
+
currency_code: string;
|
|
14
|
+
destination_bank_account_id: string | null;
|
|
15
|
+
destination_bank_account_type: CashManagementBankAccountType;
|
|
16
|
+
reasoning: string;
|
|
17
|
+
source_bank_account_id: string | null;
|
|
18
|
+
source_bank_account_type: CashManagementBankAccountType;
|
|
19
|
+
sub_movement_type: CashManagementSubMovementType;
|
|
20
|
+
}
|
|
21
|
+
export interface CashAgentMoneyMovementGroupPayload {
|
|
22
|
+
movement_type: string;
|
|
23
|
+
movements: CashAgentMovementPayload[];
|
|
24
|
+
}
|
|
25
|
+
export interface CashAgentRecommendPayload {
|
|
26
|
+
inflows: CashAgentEventPayload[];
|
|
27
|
+
money_movements: CashAgentMoneyMovementGroupPayload[];
|
|
28
|
+
outflows: CashAgentEventPayload[];
|
|
29
|
+
}
|
|
30
|
+
export type CashAgentRecommendResponse = ZeniAPIResponse<CashAgentRecommendPayload>;
|
|
31
|
+
export declare const toUpcomingPaymentEvent: (event: CashAgentEventPayload) => UpcomingPaymentEvent;
|
|
32
|
+
export declare const toCashManagementBannerData: (payload: CashAgentRecommendPayload) => CashManagementBannerData;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.toCashManagementBannerData = exports.toUpcomingPaymentEvent = void 0;
|
|
4
|
+
const amount_1 = require("../../../../commonStateTypes/amount");
|
|
5
|
+
const zeniDayJS_1 = require("../../../../zeniDayJS");
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
// Converters
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
const toUpcomingPaymentEvent = (event) => ({
|
|
10
|
+
accountId: event.account_id,
|
|
11
|
+
accountType: event.account_type,
|
|
12
|
+
amount: (0, amount_1.toAmount)(event.amount_usd, event.currency_code),
|
|
13
|
+
label: event.label,
|
|
14
|
+
paymentDate: (0, zeniDayJS_1.date)(event.payment_date),
|
|
15
|
+
});
|
|
16
|
+
exports.toUpcomingPaymentEvent = toUpcomingPaymentEvent;
|
|
17
|
+
/**
|
|
18
|
+
* The agent reports a `null` account id whenever the "account" in question is
|
|
19
|
+
* a singleton inferred from the account type (today: the treasury account —
|
|
20
|
+
* there's only one per company, so the type alone identifies it). Callers
|
|
21
|
+
* already branch on `*BankAccountType`, so collapsing the id to `""` keeps the
|
|
22
|
+
* state type a plain `ID` (string) without losing information.
|
|
23
|
+
*/
|
|
24
|
+
const toCashManagementMovement = (payload) => ({
|
|
25
|
+
amount: (0, amount_1.toAmount)(payload.amount, payload.currency_code),
|
|
26
|
+
destinationBankAccountId: payload.destination_bank_account_id ?? '',
|
|
27
|
+
destinationBankAccountType: payload.destination_bank_account_type,
|
|
28
|
+
reasoning: payload.reasoning,
|
|
29
|
+
sourceBankAccountId: payload.source_bank_account_id ?? '',
|
|
30
|
+
sourceBankAccountType: payload.source_bank_account_type,
|
|
31
|
+
subMovementType: payload.sub_movement_type,
|
|
32
|
+
});
|
|
33
|
+
const toCashManagementMoneyMovementGroup = (payload) => ({
|
|
34
|
+
movements: payload.movements.map(toCashManagementMovement),
|
|
35
|
+
movementType: payload.movement_type,
|
|
36
|
+
});
|
|
37
|
+
const toCashManagementBannerData = (payload) => ({
|
|
38
|
+
inflows: payload.inflows.map(exports.toUpcomingPaymentEvent),
|
|
39
|
+
moneyMovements: payload.money_movements.map(toCashManagementMoneyMovementGroup),
|
|
40
|
+
outflows: payload.outflows.map(exports.toUpcomingPaymentEvent),
|
|
41
|
+
});
|
|
42
|
+
exports.toCashManagementBannerData = toCashManagementBannerData;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { FetchState } from '../../../../commonStateTypes/common';
|
|
2
|
+
import { ZeniAPIStatus } from '../../../../responsePayload';
|
|
3
|
+
import { CashAgentRecommendPayload } from './cashManagementOverviewPayload';
|
|
4
|
+
import { CashManagementOverviewState } from './cashManagementOverviewState';
|
|
5
|
+
export declare const initialState: CashManagementOverviewState;
|
|
6
|
+
export declare const fetchCashManagementOverviewPage: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[cacheOverride: boolean], {
|
|
7
|
+
cacheOverride: boolean;
|
|
8
|
+
}, "cashManagementOverview/fetchCashManagementOverviewPage", never, never>, fetchCashManagementBanner: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"cashManagementOverview/fetchCashManagementBanner">, updateCashManagementBanner: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
|
|
9
|
+
data: CashAgentRecommendPayload;
|
|
10
|
+
}, "cashManagementOverview/updateCashManagementBanner">, updateCashManagementBannerFetchStatus: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
|
|
11
|
+
fetchState: FetchState;
|
|
12
|
+
error?: ZeniAPIStatus;
|
|
13
|
+
}, "cashManagementOverview/updateCashManagementBannerFetchStatus">, clearCashManagementOverview: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"cashManagementOverview/clearCashManagementOverview">;
|
|
14
|
+
declare const _default: import("redux").Reducer<CashManagementOverviewState>;
|
|
15
|
+
export default _default;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var _a;
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.clearCashManagementOverview = exports.updateCashManagementBannerFetchStatus = exports.updateCashManagementBanner = exports.fetchCashManagementBanner = exports.fetchCashManagementOverviewPage = exports.initialState = void 0;
|
|
5
|
+
const toolkit_1 = require("@reduxjs/toolkit");
|
|
6
|
+
const cashManagementOverviewPayload_1 = require("./cashManagementOverviewPayload");
|
|
7
|
+
const cashManagementOverviewState_1 = require("./cashManagementOverviewState");
|
|
8
|
+
exports.initialState = {
|
|
9
|
+
banner: cashManagementOverviewState_1.initialCashManagementBannerState,
|
|
10
|
+
fetchState: 'Not-Started',
|
|
11
|
+
error: undefined,
|
|
12
|
+
hasValidState() {
|
|
13
|
+
return this.fetchState == 'Completed';
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
const cashManagementOverview = (0, toolkit_1.createSlice)({
|
|
17
|
+
name: 'cashManagementOverview',
|
|
18
|
+
initialState: exports.initialState,
|
|
19
|
+
reducers: {
|
|
20
|
+
fetchCashManagementOverviewPage: {
|
|
21
|
+
reducer() { },
|
|
22
|
+
prepare(cacheOverride) {
|
|
23
|
+
return { payload: { cacheOverride } };
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
fetchCashManagementBanner(draft) {
|
|
27
|
+
draft.banner = {
|
|
28
|
+
...cashManagementOverviewState_1.initialCashManagementBannerState,
|
|
29
|
+
fetchState: 'In-Progress',
|
|
30
|
+
error: undefined,
|
|
31
|
+
};
|
|
32
|
+
},
|
|
33
|
+
updateCashManagementBanner(draft, action) {
|
|
34
|
+
draft.banner = {
|
|
35
|
+
fetchState: 'Completed',
|
|
36
|
+
error: undefined,
|
|
37
|
+
data: (0, cashManagementOverviewPayload_1.toCashManagementBannerData)(action.payload.data),
|
|
38
|
+
};
|
|
39
|
+
},
|
|
40
|
+
updateCashManagementBannerFetchStatus(draft, action) {
|
|
41
|
+
draft.banner = {
|
|
42
|
+
...draft.banner,
|
|
43
|
+
fetchState: action.payload.fetchState,
|
|
44
|
+
error: action.payload.error,
|
|
45
|
+
};
|
|
46
|
+
},
|
|
47
|
+
clearCashManagementOverview(draft) {
|
|
48
|
+
Object.assign(draft, exports.initialState);
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
_a = cashManagementOverview.actions, exports.fetchCashManagementOverviewPage = _a.fetchCashManagementOverviewPage, exports.fetchCashManagementBanner = _a.fetchCashManagementBanner, exports.updateCashManagementBanner = _a.updateCashManagementBanner, exports.updateCashManagementBannerFetchStatus = _a.updateCashManagementBannerFetchStatus, exports.clearCashManagementOverview = _a.clearCashManagementOverview;
|
|
53
|
+
exports.default = cashManagementOverview.reducer;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Amount } from '../../../../commonStateTypes/amount';
|
|
2
|
+
import { ID } from '../../../../commonStateTypes/common';
|
|
3
|
+
import { SelectorView } from '../../../../commonStateTypes/viewAndReport/viewAndReport';
|
|
4
|
+
import { RootState } from '../../../../reducer';
|
|
5
|
+
import { CashManagementMovement, UpcomingPaymentEvent } from './cashManagementOverviewState';
|
|
6
|
+
export interface CashManagementOverviewBannerSelectorView extends SelectorView, CashManagementMovement {
|
|
7
|
+
cashManagementSettingsId?: ID;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* One point on the cash-balance projection line. The first point is today's
|
|
11
|
+
* `cashBalance`; each subsequent point is the running balance after applying
|
|
12
|
+
* the next inflow / outflow event (in chronological order). The `isInflow` /
|
|
13
|
+
* `isOutflow` flags drive the colored dot markers in the chart.
|
|
14
|
+
*/
|
|
15
|
+
export interface CashProjectionPoint {
|
|
16
|
+
balance: number;
|
|
17
|
+
/** Pre-formatted display label (e.g. `"Today"`, `"May 27"`). */
|
|
18
|
+
date: string;
|
|
19
|
+
isInflow?: boolean;
|
|
20
|
+
isOutflow?: boolean;
|
|
21
|
+
}
|
|
22
|
+
export interface CashManagementOverviewSelectorView extends SelectorView {
|
|
23
|
+
banner: CashManagementOverviewBannerSelectorView;
|
|
24
|
+
cashBalance: Amount;
|
|
25
|
+
/** Net cash flow over the horizon — `totalInFlow` minus `totalOutFlow`.
|
|
26
|
+
* Positive = surplus, negative = burn. */
|
|
27
|
+
cashflow: Amount;
|
|
28
|
+
inflowEvents: UpcomingPaymentEvent[];
|
|
29
|
+
outflowEvents: UpcomingPaymentEvent[];
|
|
30
|
+
/** Running cash-balance projection seeded by `cashBalance` and stepped by
|
|
31
|
+
* the inflow / outflow events. */
|
|
32
|
+
projectionData: CashProjectionPoint[];
|
|
33
|
+
totalCash: Amount;
|
|
34
|
+
/** Sum of all `inflowEvents[].amount` values. */
|
|
35
|
+
totalInFlow: Amount;
|
|
36
|
+
/** Sum of all `outflowEvents[].amount` values. */
|
|
37
|
+
totalOutFlow: Amount;
|
|
38
|
+
treasuryBalance: Amount;
|
|
39
|
+
}
|
|
40
|
+
export declare const getCashManagementOverviewBanner: (state: RootState) => CashManagementOverviewBannerSelectorView;
|
|
41
|
+
export declare const getCashManagementOverview: (state: RootState) => CashManagementOverviewSelectorView;
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getCashManagementOverview = exports.getCashManagementOverviewBanner = void 0;
|
|
4
|
+
const amount_1 = require("../../../../commonStateTypes/amount");
|
|
5
|
+
const reduceFetchState_1 = require("../../../../commonStateTypes/reduceFetchState");
|
|
6
|
+
/**
|
|
7
|
+
* Default account type used when the recommend payload has no movement to
|
|
8
|
+
* expose yet (e.g. before the first fetch resolves). Callers that care about
|
|
9
|
+
* the movement should also gate on `fetchState === 'Completed'`.
|
|
10
|
+
*/
|
|
11
|
+
const DEFAULT_BANK_ACCOUNT_TYPE = 'deposit_account';
|
|
12
|
+
const DEFAULT_SUB_MOVEMENT_TYPE = 'push';
|
|
13
|
+
const getCashManagementOverviewBanner = (state) => {
|
|
14
|
+
const { banner } = state.cashManagementOverviewState;
|
|
15
|
+
const autoSweep = state.autoSweepFlowState;
|
|
16
|
+
// The banner today surfaces a single recommended movement (the first one
|
|
17
|
+
// returned by the agent). When the agent returns multiple groups/movements
|
|
18
|
+
// we'll need to broaden this — for now consumers only render one.
|
|
19
|
+
const movement = banner.data?.moneyMovements[0]?.movements[0];
|
|
20
|
+
const aggregate = (0, reduceFetchState_1.reduceAllFetchState)([
|
|
21
|
+
{ fetchState: banner.fetchState, error: banner.error },
|
|
22
|
+
{ fetchState: autoSweep.fetchState, error: autoSweep.error },
|
|
23
|
+
]);
|
|
24
|
+
return {
|
|
25
|
+
fetchState: aggregate.fetchState,
|
|
26
|
+
error: aggregate.error,
|
|
27
|
+
amount: movement?.amount ?? (0, amount_1.zeroAmount)(),
|
|
28
|
+
cashManagementSettingsId: autoSweep.settingsId,
|
|
29
|
+
destinationBankAccountId: movement?.destinationBankAccountId ?? '',
|
|
30
|
+
destinationBankAccountType: movement?.destinationBankAccountType ?? DEFAULT_BANK_ACCOUNT_TYPE,
|
|
31
|
+
reasoning: movement?.reasoning ?? '',
|
|
32
|
+
sourceBankAccountId: movement?.sourceBankAccountId ?? '',
|
|
33
|
+
sourceBankAccountType: movement?.sourceBankAccountType ?? DEFAULT_BANK_ACCOUNT_TYPE,
|
|
34
|
+
subMovementType: movement?.subMovementType ?? DEFAULT_SUB_MOVEMENT_TYPE,
|
|
35
|
+
version: 0,
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
exports.getCashManagementOverviewBanner = getCashManagementOverviewBanner;
|
|
39
|
+
const getCashManagementOverview = (state) => {
|
|
40
|
+
const { cashManagementOverviewState, depositAccountListState, depositAccountState, paymentAccountListState, paymentAccountState, treasuryDetailState, } = state;
|
|
41
|
+
const aggregate = (0, reduceFetchState_1.reduceAllFetchState)([
|
|
42
|
+
{
|
|
43
|
+
fetchState: paymentAccountListState.fetchState,
|
|
44
|
+
error: paymentAccountListState.error,
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
fetchState: depositAccountListState.fetchState,
|
|
48
|
+
error: depositAccountListState.error,
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
fetchState: treasuryDetailState.fetchState,
|
|
52
|
+
error: treasuryDetailState.error,
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
fetchState: cashManagementOverviewState.banner.fetchState,
|
|
56
|
+
error: cashManagementOverviewState.banner.error,
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
fetchState: state.autoSweepFlowState.fetchState,
|
|
60
|
+
error: state.autoSweepFlowState.error,
|
|
61
|
+
},
|
|
62
|
+
]);
|
|
63
|
+
const treasuryBalance = treasuryDetailState.accountSummary?.totalBalance ?? (0, amount_1.zeroAmount)();
|
|
64
|
+
const cashBalance = sumCashBalances(paymentAccountListState.paymentAccountIds, paymentAccountState, depositAccountListState.accountIds, depositAccountState);
|
|
65
|
+
const totalCash = addAmounts(treasuryBalance, cashBalance);
|
|
66
|
+
const bannerData = cashManagementOverviewState.banner.data;
|
|
67
|
+
const inflowEvents = bannerData?.inflows ?? [];
|
|
68
|
+
const outflowEvents = bannerData?.outflows ?? [];
|
|
69
|
+
const totalInFlow = sumEventAmounts(inflowEvents);
|
|
70
|
+
const totalOutFlow = sumEventAmounts(outflowEvents);
|
|
71
|
+
const cashflow = subtractAmounts(totalInFlow, totalOutFlow);
|
|
72
|
+
const projectionData = buildProjectionData(cashBalance, inflowEvents, outflowEvents);
|
|
73
|
+
return {
|
|
74
|
+
banner: (0, exports.getCashManagementOverviewBanner)(state),
|
|
75
|
+
cashBalance,
|
|
76
|
+
cashflow,
|
|
77
|
+
inflowEvents,
|
|
78
|
+
outflowEvents,
|
|
79
|
+
projectionData,
|
|
80
|
+
totalCash,
|
|
81
|
+
totalInFlow,
|
|
82
|
+
totalOutFlow,
|
|
83
|
+
treasuryBalance,
|
|
84
|
+
fetchState: aggregate.fetchState,
|
|
85
|
+
error: aggregate.error,
|
|
86
|
+
version: 0,
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
exports.getCashManagementOverview = getCashManagementOverview;
|
|
90
|
+
/**
|
|
91
|
+
* Walks the inflow / outflow events in chronological order, stepping the
|
|
92
|
+
* running balance from today's `cashBalance`. Emits a point per event so the
|
|
93
|
+
* chart can render colored markers at each cash event. Events with the same
|
|
94
|
+
* payment date are kept as separate steps (preserves the marker count even
|
|
95
|
+
* if Recharts overlays them at the same X position).
|
|
96
|
+
*/
|
|
97
|
+
const buildProjectionData = (cashBalance, inflowEvents, outflowEvents) => {
|
|
98
|
+
const events = [
|
|
99
|
+
...inflowEvents.map((e) => ({
|
|
100
|
+
date: e.paymentDate,
|
|
101
|
+
delta: e.amount.amount,
|
|
102
|
+
isInflow: true,
|
|
103
|
+
})),
|
|
104
|
+
...outflowEvents.map((e) => ({
|
|
105
|
+
date: e.paymentDate,
|
|
106
|
+
delta: -e.amount.amount,
|
|
107
|
+
isOutflow: true,
|
|
108
|
+
})),
|
|
109
|
+
].sort((a, b) => a.date.valueOf() - b.date.valueOf());
|
|
110
|
+
let balance = cashBalance.amount;
|
|
111
|
+
const points = [{ balance, date: 'Today' }];
|
|
112
|
+
events.forEach((event) => {
|
|
113
|
+
balance += event.delta;
|
|
114
|
+
points.push({
|
|
115
|
+
balance,
|
|
116
|
+
date: event.date.format('MMM D'),
|
|
117
|
+
isInflow: event.isInflow,
|
|
118
|
+
isOutflow: event.isOutflow,
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
return points;
|
|
122
|
+
};
|
|
123
|
+
const sumEventAmounts = (events) => events.reduce((acc, event) => addAmounts(acc, event.amount), (0, amount_1.zeroAmount)());
|
|
124
|
+
/**
|
|
125
|
+
* Subtracts `b` from `a`, sharing the currency-match guard with `addAmounts`.
|
|
126
|
+
* Used to compute net cash flow (`inflow - outflow`).
|
|
127
|
+
*/
|
|
128
|
+
const subtractAmounts = (a, b) => {
|
|
129
|
+
if (isZeroAmount(b)) {
|
|
130
|
+
return { ...a };
|
|
131
|
+
}
|
|
132
|
+
if (isZeroAmount(a)) {
|
|
133
|
+
return { ...b, amount: -b.amount };
|
|
134
|
+
}
|
|
135
|
+
if (a.currencyCode !== b.currencyCode ||
|
|
136
|
+
a.currencySymbol !== b.currencySymbol) {
|
|
137
|
+
console.warn(`cashManagementOverview: skipping balance with mismatched currency. Expected ${a.currencyCode} (${a.currencySymbol}), got ${b.currencyCode} (${b.currencySymbol}).`);
|
|
138
|
+
return a;
|
|
139
|
+
}
|
|
140
|
+
return {
|
|
141
|
+
amount: a.amount - b.amount,
|
|
142
|
+
currencyCode: a.currencyCode,
|
|
143
|
+
currencySymbol: a.currencySymbol,
|
|
144
|
+
};
|
|
145
|
+
};
|
|
146
|
+
const isZeroAmount = (a) => a.amount === 0 && a.currencyCode === 'USD' && a.currencySymbol === '$';
|
|
147
|
+
/**
|
|
148
|
+
* Adds two Amounts. Currencies must match (the seeded zeroAmount() counts as
|
|
149
|
+
* "no currency yet" and adopts `b`'s currency). Mismatches return `a`
|
|
150
|
+
* unchanged and warn — mixing currencies in a total is a bug, not silent
|
|
151
|
+
* conversion.
|
|
152
|
+
*/
|
|
153
|
+
const addAmounts = (a, b) => {
|
|
154
|
+
if (isZeroAmount(a)) {
|
|
155
|
+
return { ...b };
|
|
156
|
+
}
|
|
157
|
+
if (a.currencyCode !== b.currencyCode ||
|
|
158
|
+
a.currencySymbol !== b.currencySymbol) {
|
|
159
|
+
console.warn(`cashManagementOverview: skipping balance with mismatched currency. Expected ${a.currencyCode} (${a.currencySymbol}), got ${b.currencyCode} (${b.currencySymbol}).`);
|
|
160
|
+
return a;
|
|
161
|
+
}
|
|
162
|
+
return {
|
|
163
|
+
amount: a.amount + b.amount,
|
|
164
|
+
currencyCode: a.currencyCode,
|
|
165
|
+
currencySymbol: a.currencySymbol,
|
|
166
|
+
};
|
|
167
|
+
};
|
|
168
|
+
const sumCashBalances = (paymentAccountIds, paymentAccountState, depositAccountIds, depositAccountState) => {
|
|
169
|
+
let total = (0, amount_1.zeroAmount)();
|
|
170
|
+
paymentAccountIds.forEach((id) => {
|
|
171
|
+
const balance = paymentAccountState.paymentAccountByID[id]?.balances
|
|
172
|
+
?.available;
|
|
173
|
+
if (balance != null) {
|
|
174
|
+
total = addAmounts(total, balance);
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
depositAccountIds.forEach((id) => {
|
|
178
|
+
const balance = depositAccountState.depositAccountByID[id]?.accountBalance?.available;
|
|
179
|
+
if (balance != null) {
|
|
180
|
+
total = addAmounts(total, balance);
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
return total;
|
|
184
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Amount } from '../../../../commonStateTypes/amount';
|
|
2
|
+
import { FetchStateAndError, FetchedState, ID } from '../../../../commonStateTypes/common';
|
|
3
|
+
import { ZeniDate } from '../../../../zeniDayJS';
|
|
4
|
+
/**
|
|
5
|
+
* Bank account types the cash agent reports against. Kept as a string union so
|
|
6
|
+
* the consumer can branch on it; new values from the agent will widen the type
|
|
7
|
+
* via TypeScript inference and won't break callers handling the known set.
|
|
8
|
+
*/
|
|
9
|
+
export type CashManagementBankAccountType = 'deposit_account' | 'treasury_account';
|
|
10
|
+
/** Direction of a single sweep movement. */
|
|
11
|
+
export type CashManagementSubMovementType = 'push' | 'pull';
|
|
12
|
+
/**
|
|
13
|
+
* Bank account types reported on upcoming-payment events. Wider than
|
|
14
|
+
* `CashManagementBankAccountType` because the cash agent can attribute
|
|
15
|
+
* inflows / outflows to payment accounts too.
|
|
16
|
+
*/
|
|
17
|
+
export type UpcomingPaymentEventAccountType = 'deposit_account' | 'treasury_account' | 'payment_account';
|
|
18
|
+
/**
|
|
19
|
+
* One projected inflow / outflow returned by the cash agent. Stored on the
|
|
20
|
+
* banner state directly (no separate entity bucket) because these events are
|
|
21
|
+
* only meaningful in the context of a banner fetch — they're not addressable
|
|
22
|
+
* by id from anywhere else.
|
|
23
|
+
*/
|
|
24
|
+
export interface UpcomingPaymentEvent {
|
|
25
|
+
accountId: ID;
|
|
26
|
+
accountType: UpcomingPaymentEventAccountType;
|
|
27
|
+
amount: Amount;
|
|
28
|
+
label: string;
|
|
29
|
+
paymentDate: ZeniDate;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* A single recommended money movement returned by the cash agent (e.g.
|
|
33
|
+
* "sweep $99,100 from checking to treasury"). Mirrors one element of the
|
|
34
|
+
* `money_movements[].movements` array in the recommend response, with field
|
|
35
|
+
* names camelCased and `amount`/`currency_code` collapsed into an `Amount`.
|
|
36
|
+
*/
|
|
37
|
+
export interface CashManagementMovement {
|
|
38
|
+
amount: Amount;
|
|
39
|
+
destinationBankAccountId: ID;
|
|
40
|
+
destinationBankAccountType: CashManagementBankAccountType;
|
|
41
|
+
reasoning: string;
|
|
42
|
+
sourceBankAccountId: ID;
|
|
43
|
+
sourceBankAccountType: CashManagementBankAccountType;
|
|
44
|
+
subMovementType: CashManagementSubMovementType;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* A group of related recommended movements (e.g. all `treasury` sweeps).
|
|
48
|
+
* `movementType` is left as `string` because the agent may grow new groups
|
|
49
|
+
* over time; today the only known value is `'treasury'`.
|
|
50
|
+
*/
|
|
51
|
+
export interface CashManagementMoneyMovementGroup {
|
|
52
|
+
movements: CashManagementMovement[];
|
|
53
|
+
movementType: string;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Banner-side data derived from the cash agent's recommend endpoint.
|
|
57
|
+
* `inflows` and `outflows` carry the full event objects; the selector reads
|
|
58
|
+
* them directly without an entity-bucket lookup.
|
|
59
|
+
*/
|
|
60
|
+
export interface CashManagementBannerData {
|
|
61
|
+
inflows: UpcomingPaymentEvent[];
|
|
62
|
+
moneyMovements: CashManagementMoneyMovementGroup[];
|
|
63
|
+
outflows: UpcomingPaymentEvent[];
|
|
64
|
+
}
|
|
65
|
+
export interface CashManagementBannerState extends FetchStateAndError {
|
|
66
|
+
data?: CashManagementBannerData;
|
|
67
|
+
}
|
|
68
|
+
export interface CashManagementOverviewState extends FetchedState {
|
|
69
|
+
banner: CashManagementBannerState;
|
|
70
|
+
}
|
|
71
|
+
export declare const initialCashManagementBannerState: CashManagementBannerState;
|