IMP-216: Show terminal balances (#353)

This commit is contained in:
Rinat Arsaev 2024-04-22 19:50:26 +09:00 committed by GitHub
parent af263c950b
commit 9145b78845
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 150 additions and 71 deletions

8
package-lock.json generated
View File

@ -28,7 +28,7 @@
"@vality/ng-core": "17.2.1-pr-60-8d151ad.0",
"@vality/payout-manager-proto": "2.0.1-eb4091a.0",
"@vality/repairer-proto": "2.0.2-07b73e9.0",
"@vality/scrooge-proto": "0.1.0",
"@vality/scrooge-proto": "0.1.1-9ce7fc6.0",
"@vality/thrift-ts": "2.4.1-8ad5123.0",
"@vality/woody": "0.1.3",
"date-fns": "^3.3.1",
@ -6504,9 +6504,9 @@
"integrity": "sha512-PwWTzgIrqTKqZVRW235ZPIoQz+Rvf0RavVhTrPwMxRDvkZLcRd7WN6O6rIYNcz84BZXJ5r4YnyN0kWCaLvGdqw=="
},
"node_modules/@vality/scrooge-proto": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/@vality/scrooge-proto/-/scrooge-proto-0.1.0.tgz",
"integrity": "sha512-yZkzqVUBpkNtM8lT1HT5yRiZ5tVh+nxVj+gP7NUQB36FIfP0ZSJWxZIdedvulWApiYXbn1M/oDsyhnc62iz2Mw=="
"version": "0.1.1-9ce7fc6.0",
"resolved": "https://registry.npmjs.org/@vality/scrooge-proto/-/scrooge-proto-0.1.1-9ce7fc6.0.tgz",
"integrity": "sha512-g/G5W8Bci84gk7TUT27sOCwgdOUv0tOYkF9K0fgD8deZ/5lvJ55Zz7g5/XuV+LhBm0NGTEZvNwAWHTtSI3C4dw=="
},
"node_modules/@vality/thrift-ts": {
"version": "2.4.1-8ad5123.0",

View File

@ -36,7 +36,7 @@
"@vality/ng-core": "17.2.1-pr-60-8d151ad.0",
"@vality/payout-manager-proto": "2.0.1-eb4091a.0",
"@vality/repairer-proto": "2.0.2-07b73e9.0",
"@vality/scrooge-proto": "0.1.0",
"@vality/scrooge-proto": "0.1.1-9ce7fc6.0",
"@vality/thrift-ts": "2.4.1-8ad5123.0",
"@vality/woody": "0.1.3",
"date-fns": "^3.3.1",

View File

@ -1,9 +1,9 @@
import { Injectable } from '@angular/core';
import { getImportValue } from '@vality/ng-core';
import {
terminal_balance_TerminalServiceCodegenClient,
account_balance_AccountServiceCodegenClient,
ThriftAstMetadata,
terminal_balance_TerminalService,
account_balance_AccountService,
} from '@vality/scrooge-proto';
import { combineLatest, map, Observable, switchMap } from 'rxjs';
@ -12,8 +12,8 @@ import { ConfigService } from '../../core/config.service';
import { KeycloakTokenInfoService, toWachterHeaders } from '../../shared/services';
@Injectable({ providedIn: 'root' })
export class TerminalBalanceService {
private client$: Observable<terminal_balance_TerminalServiceCodegenClient>;
export class AccountBalanceService {
private client$: Observable<account_balance_AccountServiceCodegenClient>;
constructor(
private keycloakTokenInfoService: KeycloakTokenInfoService,
@ -25,7 +25,7 @@ export class TerminalBalanceService {
);
this.client$ = combineLatest([metadata$, headers$]).pipe(
switchMap(([metadata, headers]) =>
terminal_balance_TerminalService({
account_balance_AccountService({
metadata,
headers,
logging: environment.logging.requests,
@ -37,6 +37,6 @@ export class TerminalBalanceService {
// eslint-disable-next-line @typescript-eslint/naming-convention
GetTerminalBalances() {
return this.client$.pipe(switchMap((c) => c.GetTerminalBalances()));
return this.client$.pipe(switchMap((c) => c.GetAccountBalances()));
}
}

View File

@ -1,2 +1,2 @@
export * from './terminal-balance.service';
export * from './stores/terminal-balances-store.service';
export * from './account-balance.service';
export * from './stores/account-balances-store.service';

View File

@ -0,0 +1,45 @@
import { Injectable } from '@angular/core';
import { AccountBalance } from '@vality/scrooge-proto/internal/account_balance';
import isNil from 'lodash-es/isNil';
import { of, Observable } from 'rxjs';
import { shareReplay, map, catchError, startWith } from 'rxjs/operators';
import { AccountBalanceService } from '../account-balance.service';
@Injectable({
providedIn: 'root',
})
export class AccountBalancesStoreService {
balances$: Observable<AccountBalance[]> = this.terminalBalanceService
.GetTerminalBalances()
.pipe(
map((b) => b.balances),
startWith([]),
catchError(() => {
console.error('Account balances are not loaded');
return of([]);
}),
shareReplay({ refCount: true, bufferSize: 1 }),
);
constructor(private terminalBalanceService: AccountBalanceService) {}
getAccountBalance(id: string | number) {
return this.balances$.pipe(
map((balances) => balances.find((b) => b.account_id === String(id))),
);
}
getTerminalBalances(id: string | number, providerId?: string | number) {
return this.balances$.pipe(
map((balances) =>
balances.filter(
(b) =>
!isNil(providerId) &&
b.provider.id === String(providerId) &&
b.terminal.id === String(id),
),
),
);
}
}

View File

@ -1,31 +0,0 @@
import { Injectable } from '@angular/core';
import { TerminalBalance } from '@vality/scrooge-proto/internal/terminal_balance';
import { of, Observable } from 'rxjs';
import { shareReplay, map, catchError, startWith } from 'rxjs/operators';
import { TerminalBalanceService } from '../terminal-balance.service';
@Injectable({
providedIn: 'root',
})
export class TerminalBalancesStoreService {
balances$: Observable<TerminalBalance[]> = this.terminalBalanceService
.GetTerminalBalances()
.pipe(
map((b) => b.balances),
startWith([]),
catchError(() => {
console.error('Terminal balances are not loaded');
return of([]);
}),
shareReplay({ refCount: true, bufferSize: 1 }),
);
constructor(private terminalBalanceService: TerminalBalanceService) {}
getTerminalBalance(id: string | number) {
return this.balances$.pipe(
map((balances) => balances.find((b) => b.terminal.id === String(id))),
);
}
}

View File

@ -1,4 +1,4 @@
import { Component, DestroyRef, isDevMode } from '@angular/core';
import { Component, DestroyRef } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { Sort } from '@angular/material/sort';
import { TerminalObject } from '@vality/domain-proto/domain';
@ -7,8 +7,8 @@ import { of } from 'rxjs';
import { map, take } from 'rxjs/operators';
import { DomainStoreService } from '../../api/domain-config';
import { TerminalBalancesStoreService } from '../../api/terminal-balance';
import { createPredicateColumn, createCurrencyColumn } from '../../shared';
import { AccountBalancesStoreService } from '../../api/terminal-balance';
import { createPredicateColumn, createCurrenciesColumn } from '../../shared';
import { SidenavInfoService } from '../../shared/components/sidenav-info';
import { TerminalDelegatesCardComponent } from '../../shared/components/terminal-delegates-card/terminal-delegates-card.component';
import {
@ -69,26 +69,34 @@ export class TerminalsComponent {
});
},
},
...(isDevMode()
? [
createCurrencyColumn<TerminalObject>(
'balance',
createCurrenciesColumn<TerminalObject>(
'balances',
(d) =>
this.terminalBalancesStoreService
.getTerminalBalance(d.ref.id)
this.accountBalancesStoreService
.getTerminalBalances(d.ref.id, d.data.provider_ref.id)
.pipe(
map((b) =>
b?.balance?.amount ? Number(b.balance.amount) : undefined,
b.map((a) => ({
amount: a.balance.amount,
symbolicCode: a.balance.currency_code,
})),
),
),
(d) =>
this.terminalBalancesStoreService
.getTerminalBalance(d.ref.id)
.pipe(map((b) => b?.balance?.currency_code)),
{ sortable: true },
{
sortable: true,
tooltip: (d) =>
this.accountBalancesStoreService
.getTerminalBalances(d.ref.id, d.data.provider_ref.id)
.pipe(
map((accountBalance) =>
accountBalance
.sort((a, b) => b.balance.amount - a.balance.amount)
.map((a) => a.account_id)
.join(', '),
),
),
},
),
]
: []),
];
data$ = this.domainStoreService.getObjects('terminal');
progress$ = this.domainStoreService.isLoading$;
@ -99,7 +107,7 @@ export class TerminalsComponent {
private sidenavInfoService: SidenavInfoService,
private destroyRef: DestroyRef,
private dialogService: DialogService,
private terminalBalancesStoreService: TerminalBalancesStoreService,
private accountBalancesStoreService: AccountBalancesStoreService,
) {}
update() {

View File

@ -1,7 +1,14 @@
import { inject } from '@angular/core';
import { CurrencyColumn, PossiblyAsync, getPossiblyAsyncObservable } from '@vality/ng-core';
import { inject, LOCALE_ID } from '@angular/core';
import {
CurrencyColumn,
PossiblyAsync,
getPossiblyAsyncObservable,
Column,
switchCombineWith,
formatCurrency,
} from '@vality/ng-core';
import isNil from 'lodash-es/isNil';
import { combineLatest, switchMap, of } from 'rxjs';
import { combineLatest, switchMap, of, forkJoin, Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { AmountCurrencyService } from '../../services';
@ -36,3 +43,53 @@ export function createCurrencyColumn<T extends object>(
...params,
};
}
export function createCurrenciesColumn<T extends object>(
field: CurrencyColumn<T>['field'],
selectAmountSymbolicCode: (d: T) => PossiblyAsync<{ amount: number; symbolicCode: string }[]>,
params: Partial<CurrencyColumn<T>> = {},
): Column<T> {
const amountCurrencyService = inject(AmountCurrencyService);
const localeId = inject(LOCALE_ID);
function getBalancesList(amountCodes$: Observable<{ amount: number; symbolicCode: string }[]>) {
return amountCodes$.pipe(
switchCombineWith((amountCodes) =>
!amountCodes?.length
? ([] as Observable<number[]>[])
: [
forkJoin(
amountCodes.map((a) =>
amountCurrencyService.toMajor(a.amount, a.symbolicCode),
),
),
],
),
map(([amountCodes, majorAmounts]) =>
amountCodes
.map((a, idx) =>
formatCurrency(
majorAmounts[idx],
a.symbolicCode,
undefined,
localeId,
undefined,
true,
),
)
.join(' / '),
),
);
}
const getAmountCodes = (d: T) =>
getPossiblyAsyncObservable(selectAmountSymbolicCode(d)).pipe(
map((amountCodes) => (amountCodes || []).sort((a, b) => b.amount - a.amount)),
);
return {
field,
formatter: (d: T) => getBalancesList(getAmountCodes(d).pipe(map((a) => a?.slice?.(0, 1)))),
description: (d: T) => getBalancesList(getAmountCodes(d).pipe(map((a) => a?.slice?.(1)))),
...params,
} as Column<T>;
}