mirror of
https://github.com/valitydev/control-center.git
synced 2024-11-06 02:25:17 +00:00
TD-447: Add withdrawal revision adjustment, remove old deanonimus service, add memoize parties, add fails autocomplete for repairing, add routing rules ref id (#155)
This commit is contained in:
parent
f9a91b7f10
commit
2f96a41ac1
15
package-lock.json
generated
15
package-lock.json
generated
@ -107,7 +107,8 @@
|
||||
"prettier-plugin-organize-attributes": "0.0.5",
|
||||
"ts-mockito": "2.6.1",
|
||||
"ts-node": "10.9.1",
|
||||
"typescript": "4.7.4"
|
||||
"typescript": "4.7.4",
|
||||
"typescript-memoize": "1.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@ampproject/remapping": {
|
||||
@ -21132,6 +21133,12 @@
|
||||
"node": ">=4.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript-memoize": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/typescript-memoize/-/typescript-memoize-1.1.1.tgz",
|
||||
"integrity": "sha512-GQ90TcKpIH4XxYTI2F98yEQYZgjNMOGPpOgdjIBhaLaWji5HPWlRnZ4AeA1hfBxtY7bCGDJsqDDHk/KaHOl5bA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/ua-parser-js": {
|
||||
"version": "0.7.31",
|
||||
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.31.tgz",
|
||||
@ -38539,6 +38546,12 @@
|
||||
"integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==",
|
||||
"dev": true
|
||||
},
|
||||
"typescript-memoize": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/typescript-memoize/-/typescript-memoize-1.1.1.tgz",
|
||||
"integrity": "sha512-GQ90TcKpIH4XxYTI2F98yEQYZgjNMOGPpOgdjIBhaLaWji5HPWlRnZ4AeA1hfBxtY7bCGDJsqDDHk/KaHOl5bA==",
|
||||
"dev": true
|
||||
},
|
||||
"ua-parser-js": {
|
||||
"version": "0.7.31",
|
||||
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.31.tgz",
|
||||
|
@ -121,6 +121,7 @@
|
||||
"prettier-plugin-organize-attributes": "0.0.5",
|
||||
"ts-mockito": "2.6.1",
|
||||
"ts-node": "10.9.1",
|
||||
"typescript": "4.7.4"
|
||||
"typescript": "4.7.4",
|
||||
"typescript-memoize": "1.1.1"
|
||||
}
|
||||
}
|
||||
|
23
src/app/api/deanonimus/deanonimus.service.ts
Normal file
23
src/app/api/deanonimus/deanonimus.service.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { Injectable, Injector } from '@angular/core';
|
||||
import {
|
||||
codegenClientConfig,
|
||||
CodegenClient,
|
||||
} from '@vality/deanonimus-proto/lib/deanonimus-Deanonimus';
|
||||
import context from '@vality/deanonimus-proto/lib/deanonimus/context';
|
||||
import * as service from '@vality/deanonimus-proto/lib/deanonimus/gen-nodejs/Deanonimus';
|
||||
|
||||
import { createThriftApi } from '@cc/app/api/utils';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class DeanonimusService extends createThriftApi<CodegenClient>() {
|
||||
constructor(injector: Injector) {
|
||||
super(injector, {
|
||||
service,
|
||||
wachterServiceName: 'Deanonimus',
|
||||
metadata: () =>
|
||||
import('@vality/deanonimus-proto/lib/metadata.json').then((m) => m.default),
|
||||
context,
|
||||
...codegenClientConfig,
|
||||
});
|
||||
}
|
||||
}
|
@ -1,2 +1 @@
|
||||
export * from './deanonimus.service';
|
||||
export * from './utils';
|
@ -1,2 +1,3 @@
|
||||
export * from './party-management.service';
|
||||
export * from './invoicing.service';
|
||||
export * from './stores/parties-store.service';
|
||||
|
@ -0,0 +1,20 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { PartyID } from '@vality/domain-proto';
|
||||
import { shareReplay } from 'rxjs/operators';
|
||||
import { MemoizeExpiring } from 'typescript-memoize';
|
||||
|
||||
import { PartyManagementService } from '@cc/app/api/payment-processing';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class PartiesStoreService {
|
||||
constructor(private partyManagementService: PartyManagementService) {}
|
||||
|
||||
@MemoizeExpiring(30_000)
|
||||
get(partyId: PartyID) {
|
||||
return this.partyManagementService
|
||||
.Get(partyId)
|
||||
.pipe(shareReplay({ refCount: true, bufferSize: 1 }));
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@ import { DataSourceItem } from '../types/data-source-item';
|
||||
export function filterPredicate({ stringified }: DataSourceItem, filter: string): boolean {
|
||||
let regexp;
|
||||
try {
|
||||
regexp = new RegExp(filter, 'g');
|
||||
regexp = new RegExp(filter, 'gi');
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
|
@ -1,11 +1,12 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
|
||||
import maxBy from 'lodash-es/maxBy';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { catchError, filter, map, pluck, shareReplay, startWith, switchMap } from 'rxjs/operators';
|
||||
|
||||
import { DeanonimusService } from '@cc/app/api/deanonimus';
|
||||
import { AppAuthGuardService } from '@cc/app/shared/services';
|
||||
|
||||
import { DeanonimusService, getMaxSearchHitParty } from '../../thrift-services/deanonimus';
|
||||
import { ROUTING_CONFIG as SHOPS_ROUTING_CONFIG } from '../party-shops/routing-config';
|
||||
import { ROUTING_CONFIG as RULESET_ROUTING_CONFIG } from '../routing-rules/party-routing-ruleset/routing-config';
|
||||
|
||||
@ -34,7 +35,7 @@ export class PartyComponent {
|
||||
this.partyID$ = this.route.params.pipe(pluck('partyID'), shareReplay(1));
|
||||
this.merchantEmail$ = this.partyID$.pipe(
|
||||
switchMap((partyID) => this.deanonimusService.searchParty(partyID)),
|
||||
map(getMaxSearchHitParty),
|
||||
map((searchHits) => maxBy(searchHits, (searchHit) => searchHit.score)?.party),
|
||||
pluck('email'),
|
||||
catchError((err) => {
|
||||
console.error(err);
|
||||
|
@ -21,6 +21,7 @@
|
||||
}}</mat-chip>
|
||||
</mat-chip-list>
|
||||
<cc-metadata-form
|
||||
[extensions]="extensions$ | async"
|
||||
[formControl]="sameForm"
|
||||
[metadata]="metadata$ | async"
|
||||
[namespace]="
|
||||
@ -38,6 +39,7 @@
|
||||
|
||||
<cc-metadata-form
|
||||
*ngIf="typeControl.value === typesEnum.Different"
|
||||
[extensions]="extensions$ | async"
|
||||
[formControl]="form"
|
||||
[metadata]="metadata$ | async"
|
||||
[type]="
|
||||
|
@ -7,6 +7,8 @@ import { RepairInvoicesRequest, RepairWithdrawalsRequest, Machine } from '@valit
|
||||
import isNil from 'lodash-es/isNil';
|
||||
import { BehaviorSubject, from } from 'rxjs';
|
||||
|
||||
import { DomainMetadataFormExtensionsService } from '@cc/app/shared/services';
|
||||
|
||||
import { progressTo, getFormValueChanges } from '../../../../../utils';
|
||||
import { RepairManagementService } from '../../../../api/repairer';
|
||||
import { ErrorService } from '../../../../shared/services/error';
|
||||
@ -41,6 +43,7 @@ export class RepairByScenarioDialogComponent
|
||||
Validators.required
|
||||
);
|
||||
metadata$ = from(import('@vality/repairer-proto/lib/metadata.json').then((m) => m.default));
|
||||
extensions$ = this.domainMetadataFormExtensionsService.extensions$;
|
||||
progress$ = new BehaviorSubject(0);
|
||||
|
||||
typesEnum = Types;
|
||||
@ -54,7 +57,8 @@ export class RepairByScenarioDialogComponent
|
||||
injector: Injector,
|
||||
private repairManagementService: RepairManagementService,
|
||||
private errorService: ErrorService,
|
||||
private notificationService: NotificationService
|
||||
private notificationService: NotificationService,
|
||||
private domainMetadataFormExtensionsService: DomainMetadataFormExtensionsService
|
||||
) {
|
||||
super(injector);
|
||||
}
|
||||
|
@ -25,6 +25,9 @@
|
||||
<mat-panel-title>
|
||||
{{ (terminalsMapID$ | async)[candidate.terminal.id]?.data?.name }}
|
||||
</mat-panel-title>
|
||||
<mat-panel-description>
|
||||
#{{ (terminalsMapID$ | async)[candidate.terminal.id]?.ref?.id }}
|
||||
</mat-panel-description>
|
||||
</mat-expansion-panel-header>
|
||||
<div fxLayout="column" fxLayoutGap="24px">
|
||||
<div fxLayout fxLayoutGap="8px">
|
||||
|
@ -5,13 +5,13 @@
|
||||
<mat-radio-button [value]="1">Generate ID</mat-radio-button>
|
||||
</mat-radio-group>
|
||||
<cc-metadata-form
|
||||
[formControl]="statusControl"
|
||||
[formControl]="control"
|
||||
[metadata]="metadata$ | async"
|
||||
namespace="withdrawal_status"
|
||||
type="Status"
|
||||
namespace="withdrawal_adjustment"
|
||||
type="ChangeRequest"
|
||||
></cc-metadata-form>
|
||||
<cc-metadata-form
|
||||
[extensions]="extensions"
|
||||
[extensions]="externalIdExtensions"
|
||||
[formControl]="externalIdControl"
|
||||
[metadata]="metadata$ | async"
|
||||
namespace="base"
|
||||
@ -25,7 +25,7 @@
|
||||
</div>
|
||||
<cc-base-dialog-actions>
|
||||
<button
|
||||
[disabled]="statusControl.invalid"
|
||||
[disabled]="control.invalid"
|
||||
color="primary"
|
||||
mat-button
|
||||
(click)="createAdjustment()"
|
||||
|
@ -3,8 +3,8 @@ import { Validators } from '@angular/forms';
|
||||
import { FormControl } from '@ngneat/reactive-forms';
|
||||
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
|
||||
import { ExternalID } from '@vality/fistful-proto/lib/base';
|
||||
import { ChangeRequest } from '@vality/fistful-proto/lib/deposit_adjustment';
|
||||
import { StatWithdrawal } from '@vality/fistful-proto/lib/fistful_stat';
|
||||
import { Status } from '@vality/fistful-proto/lib/withdrawal';
|
||||
import { BaseDialogResponseStatus, BaseDialogSuperclass } from '@vality/ng-core';
|
||||
import { combineLatest, from, of } from 'rxjs';
|
||||
import { catchError, finalize } from 'rxjs/operators';
|
||||
@ -24,19 +24,23 @@ export class CreateAdjustmentDialogComponent extends BaseDialogSuperclass<
|
||||
CreateAdjustmentDialogComponent,
|
||||
{ withdrawals: StatWithdrawal[] }
|
||||
> {
|
||||
statusControl = new FormControl<Status>(
|
||||
{ failed: { failure: { code: 'account_limit_exceeded:unknown' } } },
|
||||
control = new FormControl<ChangeRequest>(
|
||||
{
|
||||
change_status: {
|
||||
new_status: { failed: { failure: { code: 'account_limit_exceeded:unknown' } } },
|
||||
},
|
||||
},
|
||||
[Validators.required]
|
||||
);
|
||||
externalIdControl = new FormControl<ExternalID>();
|
||||
typeControl = new FormControl<number>(0);
|
||||
metadata$ = from(import('@vality/fistful-proto/lib/metadata.json').then((m) => m.default));
|
||||
extensions: MetadataFormExtension[] = [
|
||||
externalIdExtensions: MetadataFormExtension[] = [
|
||||
{
|
||||
determinant: () => of(true),
|
||||
extension: () => of({ label: 'External ID' }),
|
||||
},
|
||||
];
|
||||
typeControl = new FormControl<number>(0);
|
||||
metadata$ = from(import('@vality/fistful-proto/lib/metadata.json').then((m) => m.default));
|
||||
progress = -1;
|
||||
|
||||
constructor(
|
||||
@ -55,7 +59,7 @@ export class CreateAdjustmentDialogComponent extends BaseDialogSuperclass<
|
||||
this.managementService
|
||||
.CreateAdjustment(w.id, {
|
||||
id: this.typeControl.value === 0 ? w.id : short().uuid(),
|
||||
change: { change_status: { new_status: this.statusControl.value } },
|
||||
change: this.control.value,
|
||||
external_id: this.externalIdControl.value,
|
||||
})
|
||||
.pipe(
|
||||
|
@ -6,7 +6,7 @@ import { coerceBoolean } from 'coerce-property';
|
||||
import { BehaviorSubject, Observable, of, ReplaySubject, Subject, merge } from 'rxjs';
|
||||
import { catchError, debounceTime, filter, map, switchMap, first, takeUntil } from 'rxjs/operators';
|
||||
|
||||
import { DeanonimusService } from '@cc/app/thrift-services/deanonimus';
|
||||
import { DeanonimusService } from '@cc/app/api/deanonimus';
|
||||
import { Option } from '@cc/components/select-search-field';
|
||||
import { createControlProviders, ValidatedFormControlSuperclass } from '@cc/utils';
|
||||
import { progressTo } from '@cc/utils/operators';
|
||||
|
@ -3,7 +3,7 @@
|
||||
title="Create payment adjustment ({{ dialogData.payments.length }})"
|
||||
>
|
||||
<cc-metadata-form
|
||||
[extensions]="extensions"
|
||||
[extensions]="extensions$ | async"
|
||||
[formControl]="control"
|
||||
[metadata]="metadata$ | async"
|
||||
namespace="payment_processing"
|
||||
|
@ -8,9 +8,10 @@ import chunk from 'lodash-es/chunk';
|
||||
import { BehaviorSubject, from, concatMap, of, forkJoin } from 'rxjs';
|
||||
import { catchError, finalize, delay } from 'rxjs/operators';
|
||||
|
||||
import { DomainMetadataFormExtensionsService } from '@cc/app/shared/services';
|
||||
|
||||
import { InvoicingService } from '../../../../api/payment-processing';
|
||||
import { NotificationService } from '../../../services/notification';
|
||||
import { MetadataFormExtension, isTypeWithAliases } from '../../metadata-form';
|
||||
|
||||
@UntilDestroy()
|
||||
@Component({
|
||||
@ -25,39 +26,14 @@ export class CreatePaymentAdjustmentComponent extends BaseDialogSuperclass<
|
||||
control = new FormControl<InvoicePaymentAdjustmentParams>(null);
|
||||
progress$ = new BehaviorSubject(0);
|
||||
metadata$ = from(import('@vality/domain-proto/lib/metadata.json').then((m) => m.default));
|
||||
extensions: MetadataFormExtension[] = [
|
||||
{
|
||||
determinant: (data) => of(isTypeWithAliases(data, 'FailureCode', 'domain')),
|
||||
extension: () =>
|
||||
of({
|
||||
options: [
|
||||
'authorization_failed:unknown',
|
||||
'authorization_failed:insufficient_funds',
|
||||
'authorization_failed:payment_tool_rejected:bank_card_rejected:card_expired',
|
||||
'authorization_failed:rejected_by_issuer',
|
||||
'authorization_failed:operation_blocked',
|
||||
'authorization_failed:account_stolen',
|
||||
'authorization_failed:temporarily_unavailable',
|
||||
'authorization_failed:account_limit_exceeded:number',
|
||||
'authorization_failed:account_limit_exceeded:amount',
|
||||
'authorization_failed:security_policy_violated',
|
||||
'preauthorization_failed',
|
||||
'authorization_failed:payment_tool_rejected:bank_card_rejected:cvv_invalid',
|
||||
'authorization_failed:account_not_found',
|
||||
'authorization_failed:payment_tool_rejected:bank_card_rejected:card_number_invalid',
|
||||
'authorization_failed:rejected_by_issuer',
|
||||
]
|
||||
.sort()
|
||||
.map((value) => ({ value })),
|
||||
}),
|
||||
},
|
||||
];
|
||||
extensions$ = this.domainMetadataFormExtensionsService.extensions$;
|
||||
withError: StatPayment[] = [];
|
||||
|
||||
constructor(
|
||||
injector: Injector,
|
||||
private invoicingService: InvoicingService,
|
||||
private notificationService: NotificationService
|
||||
private notificationService: NotificationService,
|
||||
private domainMetadataFormExtensionsService: DomainMetadataFormExtensionsService
|
||||
) {
|
||||
super(injector);
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ import { ChangeDetectorRef, OnDestroy, Pipe, PipeTransform } from '@angular/core
|
||||
import { BehaviorSubject, combineLatest, Subject } from 'rxjs';
|
||||
import { distinctUntilChanged, map, pluck, switchMap, takeUntil } from 'rxjs/operators';
|
||||
|
||||
import { PartyManagementService } from '@cc/app/api/payment-processing';
|
||||
import { PartiesStoreService } from '@cc/app/api/payment-processing';
|
||||
|
||||
@Pipe({
|
||||
name: 'shopName',
|
||||
@ -15,13 +15,13 @@ export class ShopNamePipe implements PipeTransform, OnDestroy {
|
||||
private destroy$ = new Subject<void>();
|
||||
|
||||
constructor(
|
||||
private partyManagementService: PartyManagementService,
|
||||
private partyManagementService: PartiesStoreService,
|
||||
private ref: ChangeDetectorRef
|
||||
) {
|
||||
combineLatest([
|
||||
this.partyIDChange$.pipe(
|
||||
distinctUntilChanged(),
|
||||
switchMap((id) => this.partyManagementService.Get(id)),
|
||||
switchMap((id) => this.partyManagementService.get(id)),
|
||||
map(({ shops }) => Array.from(shops.values()))
|
||||
),
|
||||
this.shopIDChange$.pipe(distinctUntilChanged()),
|
||||
|
@ -81,6 +81,36 @@ export class DomainMetadataFormExtensionsService {
|
||||
}))
|
||||
),
|
||||
},
|
||||
{
|
||||
determinant: (data) =>
|
||||
of(
|
||||
isTypeWithAliases(data, 'FailureCode', 'domain') ||
|
||||
isTypeWithAliases(data, 'FailureCode', 'base')
|
||||
),
|
||||
extension: () =>
|
||||
of({
|
||||
options: [
|
||||
'authorization_failed:unknown',
|
||||
'authorization_failed:insufficient_funds',
|
||||
'authorization_failed:payment_tool_rejected:bank_card_rejected:card_expired',
|
||||
'authorization_failed:rejected_by_issuer',
|
||||
'authorization_failed:operation_blocked',
|
||||
'authorization_failed:account_stolen',
|
||||
'authorization_failed:temporarily_unavailable',
|
||||
'authorization_failed:account_limit_exceeded:number',
|
||||
'authorization_failed:account_limit_exceeded:amount',
|
||||
'authorization_failed:security_policy_violated',
|
||||
'preauthorization_failed',
|
||||
'authorization_failed:payment_tool_rejected:bank_card_rejected:cvv_invalid',
|
||||
'authorization_failed:account_not_found',
|
||||
'authorization_failed:payment_tool_rejected:bank_card_rejected:card_number_invalid',
|
||||
'authorization_failed:rejected_by_issuer',
|
||||
]
|
||||
.sort()
|
||||
.map((value) => ({ value })),
|
||||
generate: () => of('authorization_failed:unknown'),
|
||||
}),
|
||||
},
|
||||
]),
|
||||
shareReplay(1)
|
||||
);
|
||||
|
@ -1,32 +1,33 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Party } from '@vality/deanonimus-proto';
|
||||
import { Observable, of, Subject } from 'rxjs';
|
||||
import { Observable, of, Subject, defer, BehaviorSubject } from 'rxjs';
|
||||
import { catchError, map, shareReplay, switchMap } from 'rxjs/operators';
|
||||
|
||||
import { progress } from '@cc/app/shared/custom-operators';
|
||||
|
||||
import { DeanonimusService } from '../../thrift-services/deanonimus';
|
||||
import { DeanonimusService } from '@cc/app/api/deanonimus';
|
||||
import { progressTo, inProgressFrom } from '@cc/utils';
|
||||
|
||||
@Injectable()
|
||||
export class FetchPartiesService {
|
||||
private searchParties$: Subject<string> = new Subject();
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/member-ordering
|
||||
parties$: Observable<Party[]> = this.searchParties$.pipe(
|
||||
parties$: Observable<Party[]> = defer(() => this.searchParties$).pipe(
|
||||
switchMap((text) =>
|
||||
this.deanonimusService.searchParty(text).pipe(
|
||||
map((hits) => hits.map((hit) => hit.party)),
|
||||
catchError((err) => {
|
||||
console.error(err);
|
||||
return of<Party[]>([]);
|
||||
})
|
||||
}),
|
||||
progressTo(this.progress$)
|
||||
)
|
||||
),
|
||||
shareReplay(1)
|
||||
);
|
||||
inProgress$ = inProgressFrom(
|
||||
() => this.progress$,
|
||||
() => this.parties$
|
||||
);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/member-ordering
|
||||
inProgress$: Observable<boolean> = progress(this.searchParties$, this.parties$);
|
||||
private progress$ = new BehaviorSubject(0);
|
||||
private searchParties$: Subject<string> = new Subject();
|
||||
|
||||
constructor(private deanonimusService: DeanonimusService) {}
|
||||
|
||||
|
@ -1,16 +0,0 @@
|
||||
import { Injectable, Injector } from '@angular/core';
|
||||
import { SearchHit } from '@vality/deanonimus-proto';
|
||||
import * as Deanonimus from '@vality/deanonimus-proto/lib/deanonimus/gen-nodejs/Deanonimus';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
import { ThriftService } from '../services/thrift/thrift-service';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class DeanonimusService extends ThriftService {
|
||||
constructor(injector: Injector) {
|
||||
super(injector, '/wachter', Deanonimus, 'Deanonimus');
|
||||
}
|
||||
|
||||
searchParty = (text: string): Observable<SearchHit[]> =>
|
||||
this.toObservableAction('searchParty')(text);
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
import { Party, SearchHit } from '@vality/deanonimus-proto';
|
||||
import maxBy from 'lodash-es/maxBy';
|
||||
|
||||
export const getMaxSearchHitParty = (searchHits: SearchHit[]): Party =>
|
||||
maxBy(searchHits, (searchHit) => searchHit.score)?.party;
|
@ -1 +0,0 @@
|
||||
export * from './get-max-search-hit-party';
|
Loading…
Reference in New Issue
Block a user