IMP-134: Add withdrawals and payments errors search (#318)

This commit is contained in:
Rinat Arsaev 2024-01-31 12:07:26 +07:00 committed by GitHub
parent 598c778fae
commit 115de955b4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 1697 additions and 2515 deletions

4034
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -16,23 +16,23 @@
"fix": "npm run lint:fix && npm run format:fix && npm run spell:fix"
},
"dependencies": {
"@angular/animations": "17.0.8",
"@angular/cdk": "17.0.4",
"@angular/common": "17.0.8",
"@angular/compiler": "17.0.8",
"@angular/core": "17.0.8",
"@angular/forms": "17.0.8",
"@angular/material": "17.0.4",
"@angular/platform-browser": "17.0.8",
"@angular/platform-browser-dynamic": "17.0.8",
"@angular/platform-server": "17.0.8",
"@angular/router": "17.0.8",
"@angular/animations": "17.1.1",
"@angular/cdk": "17.1.1",
"@angular/common": "17.1.1",
"@angular/compiler": "17.1.1",
"@angular/core": "17.1.1",
"@angular/forms": "17.1.1",
"@angular/material": "17.1.1",
"@angular/platform-browser": "17.1.1",
"@angular/platform-browser-dynamic": "17.1.1",
"@angular/platform-server": "17.1.1",
"@angular/router": "17.1.1",
"@ngneat/input-mask": "6.0.0",
"@vality/deanonimus-proto": "2.0.1-2a02d87.0",
"@vality/domain-proto": "2.0.1-23211ff.0",
"@vality/fistful-proto": "2.0.1-3b9a0a7.0",
"@vality/fistful-proto": "2.0.1-f2ca9b5.0",
"@vality/machinegun-proto": "1.0.0",
"@vality/magista-proto": "2.0.2-4383410.0",
"@vality/magista-proto": "2.0.2-28d11b9.0",
"@vality/ng-core": "^17.1.1-pr-57-8ca06c2.0",
"@vality/payout-manager-proto": "2.0.1-eb4091a.0",
"@vality/repairer-proto": "2.0.2-07b73e9.0",
@ -54,12 +54,11 @@
"zone.js": "0.14.2"
},
"devDependencies": {
"@angular-devkit/build-angular": "17.0.8",
"@angular-devkit/build-angular": "17.1.1",
"@angular-eslint/builder": "17.1.1",
"@angular-eslint/schematics": "17.1.1",
"@angular/cli": "17.0.8",
"@angular/compiler-cli": "17.0.8",
"@types/element-resize-detector": "1.1.3",
"@angular/cli": "17.1.1",
"@angular/compiler-cli": "17.1.1",
"@types/inputmask": "5.0.3",
"@types/jasmine": "4.0.3",
"@types/jwt-decode": "2.2.1",
@ -83,4 +82,4 @@
"typescript": "~5.2.2",
"typescript-memoize": "1.1.1"
}
}
}

View File

@ -50,8 +50,9 @@ export class DomainStoreService {
}
getDomain(raw = false): Observable<Domain> {
return this.snapshot$.pipe(
map((s) => s?.domain),
return combineLatest([defer(() => this.snapshot$), defer(() => this.progress$)]).pipe(
filter(([, p]) => !p),
map(([s]) => s?.domain),
map((d) => (raw ? d : this.domainSecretService.reduceDomain(d))),
);
}

View File

@ -17,4 +17,5 @@ export interface WithdrawalParams extends PagedBaseParameters {
currency_code?: string;
from_time?: string;
to_time?: string;
error_message?: string;
}

View File

@ -10,6 +10,8 @@ import { PartiesStoreService } from '@cc/app/api/payment-processing';
import { AmountCurrencyService } from '@cc/app/shared/services';
import { getUnionKey } from '@cc/utils';
import { createFailureColumn } from '../../../../shared';
@Component({
selector: 'cc-payments-table',
templateUrl: './payments-table.component.html',
@ -82,6 +84,13 @@ export class PaymentsTableComponent {
'domain_revision',
{ field: 'terminal_id.id', header: 'Terminal' },
{ field: 'provider_id.id', header: 'Provider' },
createFailureColumn<StatPayment>(
(d) => d.status?.failed?.failure?.failure,
(d) =>
getUnionKey(d.status?.failed?.failure) === 'failure'
? ''
: startCase(getUnionKey(d.status?.failed?.failure)),
),
createOperationColumn<StatPayment>([
{
label: 'Details',

View File

@ -21,6 +21,7 @@
<v-input-field formControlName="payment_last4" label="Card PAN"></v-input-field>
<v-input-field formControlName="payment_rrn" label="Payment RRN"></v-input-field>
<v-input-field formControlName="payment_email" label="Payer email"></v-input-field>
<v-input-field formControlName="error_message" label="Error message"></v-input-field>
</ng-template>
<ng-template vOtherFilters>
<cc-metadata-form

View File

@ -45,6 +45,7 @@ export class PaymentsComponent implements OnInit {
payment_last4: undefined as string,
payment_rrn: undefined as string,
payment_email: undefined as string,
error_message: undefined as string,
});
otherFiltersControl = this.fb.control({
common_search_query_params: {},
@ -64,6 +65,7 @@ export class PaymentsComponent implements OnInit {
'payment_first6',
'payment_last4',
'payment_rrn',
'error_message',
].includes(data?.field?.name),
),
extension: () => of({ hidden: true }),
@ -122,6 +124,7 @@ export class PaymentsComponent implements OnInit {
payment_first6: filters.payment_first6,
payment_last4: filters.payment_last4,
payment_rrn: filters.payment_rrn,
error_message: filters.error_message,
},
invoice_ids: filters.invoice_ids,
}),

View File

@ -20,14 +20,8 @@
<mat-label>Wallet ID</mat-label>
<input formControlName="walletId" matInput />
</mat-form-field>
<mat-form-field>
<mat-label>Amount from</mat-label>
<input formControlName="amountFrom" matInput type="number" />
</mat-form-field>
<mat-form-field>
<mat-label>Amount to</mat-label>
<input formControlName="amountTo" matInput type="number" />
</mat-form-field>
<v-number-range-field formControlName="amount" label="Amount"></v-number-range-field>
<v-input-field formControlName="errorMessage" label="Error message"></v-input-field>
</ng-template>
</v-filters>

View File

@ -13,13 +13,16 @@ import {
DateRange,
getNoTimeZoneIsoString,
DialogResponseStatus,
NumberRange,
} from '@vality/ng-core';
import { endOfDay } from 'date-fns';
import startCase from 'lodash-es/startCase';
import { debounceTime } from 'rxjs/operators';
import { WithdrawalParams } from '@cc/app/api/fistful-stat';
import { getUnionKey } from '../../../utils';
import { createFailureColumn } from '../../shared';
import { FailMachinesDialogComponent, Type } from '../../shared/components/fail-machines-dialog';
import { AmountCurrencyService } from '../../shared/services';
@ -30,10 +33,10 @@ interface WithdrawalsForm {
dateRange: DateRange;
merchant: PartyID;
status: WithdrawalParams['status'];
amountFrom: WithdrawalParams['amount_from'];
amountTo: WithdrawalParams['amount_to'];
amount: NumberRange;
withdrawalIds: WithdrawalParams['withdrawal_ids'];
walletId: WithdrawalParams['wallet_id'];
errorMessage: WithdrawalParams['error_message'];
}
@Component({
@ -46,10 +49,10 @@ export class WithdrawalsComponent implements OnInit {
dateRange: null,
merchant: null,
status: null,
amountFrom: null,
amountTo: null,
amount: null,
withdrawalIds: null,
walletId: null,
errorMessage: null,
...this.qp.params,
});
active = 0;
@ -93,6 +96,7 @@ export class WithdrawalsComponent implements OnInit {
},
},
},
createFailureColumn<StatWithdrawal>((d) => d.status?.failed?.base_failure),
];
selected: StatWithdrawal[] = [];
statuses: WithdrawalParams['status'][] = ['Pending', 'Succeeded', 'Failed'];
@ -110,26 +114,27 @@ export class WithdrawalsComponent implements OnInit {
this.filtersForm.valueChanges
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((v) => void this.qp.set(clean(v)));
this.qp.params$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => this.update());
this.qp.params$
.pipe(debounceTime(500), takeUntilDestroyed(this.destroyRef))
.subscribe(() => this.update());
}
update(options?: UpdateOptions) {
const { dateRange, merchant, status, amountFrom, amountTo, withdrawalIds, walletId } =
const { dateRange, merchant, status, amount, withdrawalIds, walletId, errorMessage } =
this.qp.params;
this.fetchWithdrawalsService.load(
clean({
party_id: merchant,
from_time: dateRange?.start && getNoTimeZoneIsoString(dateRange?.start),
to_time: dateRange?.end && getNoTimeZoneIsoString(endOfDay(dateRange?.end)),
status: status,
amount_from: amountFrom,
amount_to: amountTo,
withdrawal_ids: withdrawalIds,
wallet_id: walletId,
}),
options,
);
this.active = countProps(clean(this.qp.params));
const params = clean({
party_id: merchant,
from_time: dateRange?.start && getNoTimeZoneIsoString(dateRange?.start),
to_time: dateRange?.end && getNoTimeZoneIsoString(endOfDay(dateRange?.end)),
status: status,
amount_from: amount?.start,
amount_to: amount?.end,
withdrawal_ids: withdrawalIds,
wallet_id: walletId,
error_message: errorMessage,
});
this.fetchWithdrawalsService.load(params, options);
this.active = countProps(params);
}
more() {

View File

@ -17,6 +17,7 @@ import {
FiltersModule,
NumberRangeFieldModule,
DateRangeFieldModule,
InputFieldModule,
} from '@vality/ng-core';
import { PageLayoutModule } from '../../shared';
@ -52,6 +53,8 @@ import { WithdrawalsComponent } from './withdrawals.component';
FiltersModule,
NumberRangeFieldModule,
DateRangeFieldModule,
InputFieldModule,
NumberRangeFieldModule,
],
declarations: [WithdrawalsComponent, CreateAdjustmentDialogComponent],
})

View File

@ -14,7 +14,7 @@ import {
getValueChanges,
progressTo,
} from '@vality/ng-core';
import { BehaviorSubject, switchMap, EMPTY, combineLatest, defer } from 'rxjs';
import { BehaviorSubject, switchMap, EMPTY, combineLatest, defer, Observable } from 'rxjs';
import { first, map, shareReplay, catchError, distinctUntilChanged } from 'rxjs/operators';
import { ValuesType } from 'utility-types';
@ -73,12 +73,7 @@ export class EditDomainObjectDialogComponent extends DialogSuperclass<
map((f) => String(f.type)),
first(),
);
currentObject$ = this.domainStoreService
.getObject({
[getUnionKey(this.dialogData.domainObject)]: getUnionValue(this.dialogData.domainObject)
.ref,
})
.pipe(shareReplay({ refCount: true, bufferSize: 1 }));
currentObject$ = this.getCurrentObject().pipe(shareReplay({ refCount: true, bufferSize: 1 }));
newObject$ = getValueChanges(this.control).pipe(
map(() => this.getNewObject()),
shareReplay({ refCount: true, bufferSize: 1 }),
@ -114,7 +109,7 @@ export class EditDomainObjectDialogComponent extends DialogSuperclass<
}
update(isRepeat = false) {
this.currentObject$
this.getCurrentObject()
.pipe(
first(),
switchMap((currentObject) => {
@ -169,6 +164,13 @@ export class EditDomainObjectDialogComponent extends DialogSuperclass<
});
}
private getCurrentObject(): Observable<DomainObject> {
return this.domainStoreService.getObject({
[getUnionKey(this.dialogData.domainObject)]: getUnionValue(this.dialogData.domainObject)
.ref,
});
}
private getNewObject(): DomainObject {
return {
[getUnionKey(this.dialogData.domainObject)]: {

View File

@ -0,0 +1,47 @@
import { Failure } from '@vality/domain-proto/domain';
import { PossiblyAsync, ColumnObject, getPossiblyAsyncObservable } from '@vality/ng-core';
import { of } from 'rxjs';
import { map, switchMap } from 'rxjs/operators';
function getFailureMessageTree(failure: Failure, withReason = true, level = Infinity) {
if (!failure) {
return '';
}
return (
(failure.code || '') +
(withReason && failure.reason ? ` (${failure.reason})` : '') +
(level > 1 && failure?.sub
? ` > ${getFailureMessageTree(failure.sub, withReason, level - 1)}`
: '')
);
}
export function createFailureColumn<T extends object>(
selectFailure: (d: T) => PossiblyAsync<Failure>,
selectNoFailureMessage: (d: T) => PossiblyAsync<string> = () => '',
params: Partial<ColumnObject<T>> = {},
): ColumnObject<T> {
return {
field: 'failure',
formatter: (d) =>
getPossiblyAsyncObservable(selectNoFailureMessage(d)).pipe(
switchMap((msg) => {
if (msg) {
return of(msg);
}
return getPossiblyAsyncObservable(selectFailure(d)).pipe(
map((failure) => getFailureMessageTree(failure, false, 2)),
);
}),
),
description: (d) =>
getPossiblyAsyncObservable(selectFailure(d)).pipe(
map((failure) => failure?.reason || ''),
),
tooltip: (d) =>
getPossiblyAsyncObservable(selectFailure(d)).pipe(
map((failure) => getFailureMessageTree(failure?.sub?.sub)),
),
...params,
} as ColumnObject<T>;
}

View File

@ -2,3 +2,4 @@ export * from './create-currency-column';
export * from './create-party-column';
export * from './create-shop-column';
export * from './create-predicate-column';
export * from './create-failure-column';