mirror of
https://github.com/valitydev/control-center.git
synced 2024-11-06 02:25:17 +00:00
IMP-310,IMP-312: Payment adjustment domain revision autocomplete, hide used routing rules shops/wallets. Table fixes (#391)
This commit is contained in:
parent
6a9a0b5ffe
commit
8da6c5ab04
16
package-lock.json
generated
16
package-lock.json
generated
@ -24,9 +24,9 @@
|
|||||||
"@vality/domain-proto": "2.0.1-7762f6c.0",
|
"@vality/domain-proto": "2.0.1-7762f6c.0",
|
||||||
"@vality/dominator-proto": "1.0.1-41bee97.0",
|
"@vality/dominator-proto": "1.0.1-41bee97.0",
|
||||||
"@vality/fistful-proto": "2.0.1-88e69a5.0",
|
"@vality/fistful-proto": "2.0.1-88e69a5.0",
|
||||||
"@vality/machinegun-proto": "1.0.0",
|
"@vality/machinegun-proto": "1.0.1-3decc8f.0",
|
||||||
"@vality/magista-proto": "2.0.2-ec1bdb9.0",
|
"@vality/magista-proto": "2.0.2-ec1bdb9.0",
|
||||||
"@vality/ng-core": "18.3.1-pr-68-2c4891d.0",
|
"@vality/ng-core": "18.3.1-pr-68-5283c21.0",
|
||||||
"@vality/ng-thrift": "18.0.1-pr-13-bdb6d51.0",
|
"@vality/ng-thrift": "18.0.1-pr-13-bdb6d51.0",
|
||||||
"@vality/repairer-proto": "2.0.2-07b73e9.0",
|
"@vality/repairer-proto": "2.0.2-07b73e9.0",
|
||||||
"@vality/scrooge-proto": "0.1.1-9ce7fc6.0",
|
"@vality/scrooge-proto": "0.1.1-9ce7fc6.0",
|
||||||
@ -5959,9 +5959,9 @@
|
|||||||
"integrity": "sha512-fsMqJbOZa2GesbP2OEmPlyPfV236VN+JRxke7HO79ujC4i8TwHeejV55DTfUbrFO7Q/d2Wwj8BtMcQF/c4KYGA=="
|
"integrity": "sha512-fsMqJbOZa2GesbP2OEmPlyPfV236VN+JRxke7HO79ujC4i8TwHeejV55DTfUbrFO7Q/d2Wwj8BtMcQF/c4KYGA=="
|
||||||
},
|
},
|
||||||
"node_modules/@vality/machinegun-proto": {
|
"node_modules/@vality/machinegun-proto": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.1-3decc8f.0",
|
||||||
"resolved": "https://registry.npmjs.org/@vality/machinegun-proto/-/machinegun-proto-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@vality/machinegun-proto/-/machinegun-proto-1.0.1-3decc8f.0.tgz",
|
||||||
"integrity": "sha512-HSK9WXE+cT+1skU5w3xI++GM/RO7YXaNDFL8mGMiE5Mga8hVUlb+yLBvvNM+zCslqiI+dENFQjviZeFROWH3Kw=="
|
"integrity": "sha512-ZGNyl/ZI2QsVs2Opw+eo6fKlopPF6NmTMCO5oTG1ZYKnDG74VfcmnMIvhLuHapPW7jgvg9K9WT7O9x0iSsUXOQ=="
|
||||||
},
|
},
|
||||||
"node_modules/@vality/magista-proto": {
|
"node_modules/@vality/magista-proto": {
|
||||||
"version": "2.0.2-ec1bdb9.0",
|
"version": "2.0.2-ec1bdb9.0",
|
||||||
@ -5969,9 +5969,9 @@
|
|||||||
"integrity": "sha512-XWF7qM/CARRAey0scGVhfGU6jNq+UdlGE2mg3jn4eIFDuIWQJqsT+Bah300RBUrl+XgFsmj95C6HWRfeA5Q8kw=="
|
"integrity": "sha512-XWF7qM/CARRAey0scGVhfGU6jNq+UdlGE2mg3jn4eIFDuIWQJqsT+Bah300RBUrl+XgFsmj95C6HWRfeA5Q8kw=="
|
||||||
},
|
},
|
||||||
"node_modules/@vality/ng-core": {
|
"node_modules/@vality/ng-core": {
|
||||||
"version": "18.3.1-pr-68-2c4891d.0",
|
"version": "18.3.1-pr-68-5283c21.0",
|
||||||
"resolved": "https://registry.npmjs.org/@vality/ng-core/-/ng-core-18.3.1-pr-68-2c4891d.0.tgz",
|
"resolved": "https://registry.npmjs.org/@vality/ng-core/-/ng-core-18.3.1-pr-68-5283c21.0.tgz",
|
||||||
"integrity": "sha512-1oDGn1YooeI/2EeWLbPIG0YyhbtyxITAwDXRk52eWWAHdFyvMcDmALZg89/EM3ATn+WQbV7jN/iNZGBhgmaAKw==",
|
"integrity": "sha512-RDD7xCe2Q+DI70J8ph2QB19U9DnAlpqW83XG9kLtLiqRDWTLfvm2yO0TxZ7AwgMUmRrwf6TJjdkgJGPk9+06RA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/material-date-fns-adapter": "^18.2.2",
|
"@angular/material-date-fns-adapter": "^18.2.2",
|
||||||
"@ng-matero/extensions": "^18.2.0",
|
"@ng-matero/extensions": "^18.2.0",
|
||||||
|
@ -33,9 +33,9 @@
|
|||||||
"@vality/domain-proto": "2.0.1-7762f6c.0",
|
"@vality/domain-proto": "2.0.1-7762f6c.0",
|
||||||
"@vality/dominator-proto": "1.0.1-41bee97.0",
|
"@vality/dominator-proto": "1.0.1-41bee97.0",
|
||||||
"@vality/fistful-proto": "2.0.1-88e69a5.0",
|
"@vality/fistful-proto": "2.0.1-88e69a5.0",
|
||||||
"@vality/machinegun-proto": "1.0.0",
|
"@vality/machinegun-proto": "1.0.1-3decc8f.0",
|
||||||
"@vality/magista-proto": "2.0.2-ec1bdb9.0",
|
"@vality/magista-proto": "2.0.2-ec1bdb9.0",
|
||||||
"@vality/ng-core": "18.3.1-pr-68-2c4891d.0",
|
"@vality/ng-core": "18.3.1-pr-68-5283c21.0",
|
||||||
"@vality/ng-thrift": "18.0.1-pr-13-bdb6d51.0",
|
"@vality/ng-thrift": "18.0.1-pr-13-bdb6d51.0",
|
||||||
"@vality/repairer-proto": "2.0.2-07b73e9.0",
|
"@vality/repairer-proto": "2.0.2-07b73e9.0",
|
||||||
"@vality/scrooge-proto": "0.1.1-9ce7fc6.0",
|
"@vality/scrooge-proto": "0.1.1-9ce7fc6.0",
|
||||||
|
@ -53,7 +53,7 @@ export class ToolbarComponent implements OnInit {
|
|||||||
this.partyIdControl.valueChanges
|
this.partyIdControl.valueChanges
|
||||||
.pipe(distinctUntilChanged(), takeUntilDestroyed(this.destroyRef))
|
.pipe(distinctUntilChanged(), takeUntilDestroyed(this.destroyRef))
|
||||||
.subscribe((partyId) => {
|
.subscribe((partyId) => {
|
||||||
const currentPartyId = this.getPartyId();
|
const currentPartyId = this.getPartyId() || null;
|
||||||
if (partyId) {
|
if (partyId) {
|
||||||
if (currentPartyId !== partyId) {
|
if (currentPartyId !== partyId) {
|
||||||
void this.router.navigate([`/party/${partyId}`]);
|
void this.router.navigate([`/party/${partyId}`]);
|
||||||
@ -63,13 +63,9 @@ export class ToolbarComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.urlService.path$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((path) => {
|
this.urlService.path$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((path) => {
|
||||||
const partyId = this.getPartyId(path);
|
const partyId = this.getPartyId(path) || null;
|
||||||
if (partyId) {
|
if (partyId !== this.partyIdControl.value) {
|
||||||
if (partyId !== this.partyIdControl.value) {
|
this.partyIdControl.setValue(partyId);
|
||||||
this.partyIdControl.setValue(partyId, { emitEvent: false });
|
|
||||||
}
|
|
||||||
} else if (this.partyIdControl.value) {
|
|
||||||
this.partyIdControl.setValue(null, { emitEvent: false });
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,30 +0,0 @@
|
|||||||
<v-dialog
|
|
||||||
[progress]="progress$ | async"
|
|
||||||
title="Create payment adjustment ({{ dialogData.payments.length }})"
|
|
||||||
>
|
|
||||||
<cc-metadata-form
|
|
||||||
[extensions]="extensions$ | async"
|
|
||||||
[formControl]="control"
|
|
||||||
[metadata]="metadata$ | async"
|
|
||||||
namespace="payment_processing"
|
|
||||||
type="InvoicePaymentAdjustmentParams"
|
|
||||||
></cc-metadata-form>
|
|
||||||
<v-dialog-actions>
|
|
||||||
<button
|
|
||||||
*ngIf="this.errors.length"
|
|
||||||
[disabled]="control.invalid || !!(progress$ | async)"
|
|
||||||
mat-raised-button
|
|
||||||
(click)="closeAndSelectWithAnError()"
|
|
||||||
>
|
|
||||||
Close and select with an error
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
[disabled]="control.invalid || !!(progress$ | async)"
|
|
||||||
color="primary"
|
|
||||||
mat-raised-button
|
|
||||||
(click)="create()"
|
|
||||||
>
|
|
||||||
{{ this.errors.length ? 'Repeat for ' + this.errors.length : 'Create' }}
|
|
||||||
</button>
|
|
||||||
</v-dialog-actions>
|
|
||||||
</v-dialog>
|
|
@ -1,76 +0,0 @@
|
|||||||
import { Component, DestroyRef } from '@angular/core';
|
|
||||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
||||||
import { FormControl } from '@angular/forms';
|
|
||||||
import { InvoicePaymentAdjustmentParams } from '@vality/domain-proto/payment_processing';
|
|
||||||
import { StatPayment } from '@vality/magista-proto/magista';
|
|
||||||
import {
|
|
||||||
DialogSuperclass,
|
|
||||||
NotifyLogService,
|
|
||||||
forkJoinToResult,
|
|
||||||
splitResultsErrors,
|
|
||||||
ForkJoinErrorResult,
|
|
||||||
} from '@vality/ng-core';
|
|
||||||
import { BehaviorSubject, from } from 'rxjs';
|
|
||||||
|
|
||||||
import { DomainMetadataFormExtensionsService } from '@cc/app/shared/services';
|
|
||||||
|
|
||||||
import { InvoicingService } from '../../../../api/payment-processing';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'cc-create-payment-adjustment',
|
|
||||||
templateUrl: './create-payment-adjustment.component.html',
|
|
||||||
})
|
|
||||||
export class CreatePaymentAdjustmentComponent extends DialogSuperclass<
|
|
||||||
CreatePaymentAdjustmentComponent,
|
|
||||||
{ payments: StatPayment[] },
|
|
||||||
{ errors?: ForkJoinErrorResult<StatPayment>[] }
|
|
||||||
> {
|
|
||||||
control = new FormControl<InvoicePaymentAdjustmentParams>(null);
|
|
||||||
progress$ = new BehaviorSubject(0);
|
|
||||||
metadata$ = from(import('@vality/domain-proto/metadata.json').then((m) => m.default));
|
|
||||||
extensions$ = this.domainMetadataFormExtensionsService.extensions$;
|
|
||||||
errors: ForkJoinErrorResult<StatPayment>[] = [];
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private invoicingService: InvoicingService,
|
|
||||||
private log: NotifyLogService,
|
|
||||||
private domainMetadataFormExtensionsService: DomainMetadataFormExtensionsService,
|
|
||||||
private destroyRef: DestroyRef,
|
|
||||||
) {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
create() {
|
|
||||||
const payments = this.errors.length
|
|
||||||
? this.errors.map(({ data }) => data)
|
|
||||||
: this.dialogData.payments;
|
|
||||||
this.errors = [];
|
|
||||||
forkJoinToResult(
|
|
||||||
payments.map((p) =>
|
|
||||||
this.invoicingService.CreatePaymentAdjustment(
|
|
||||||
p.invoice_id,
|
|
||||||
p.id,
|
|
||||||
this.control.value,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
this.progress$,
|
|
||||||
payments,
|
|
||||||
2,
|
|
||||||
)
|
|
||||||
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
||||||
.subscribe((res) => {
|
|
||||||
const [result, errors] = splitResultsErrors(res);
|
|
||||||
if (errors.length) {
|
|
||||||
this.errors = errors;
|
|
||||||
this.log.error(this.errors.map((e) => e.error));
|
|
||||||
} else {
|
|
||||||
this.log.success(`${result.length} created successfully`);
|
|
||||||
this.closeWithSuccess();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
closeAndSelectWithAnError() {
|
|
||||||
this.closeWithError({ errors: this.errors });
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
<v-table
|
|
||||||
[columns]="columns"
|
|
||||||
[data]="data"
|
|
||||||
[hasMore]="hasMore"
|
|
||||||
[progress]="isLoading"
|
|
||||||
[rowSelected]="selected"
|
|
||||||
rowSelectable
|
|
||||||
(more)="more.emit()"
|
|
||||||
(rowSelectedChange)="selectedChange.emit($event)"
|
|
||||||
(update)="update.emit($event)"
|
|
||||||
>
|
|
||||||
<v-table-actions>
|
|
||||||
<ng-content></ng-content>
|
|
||||||
</v-table-actions>
|
|
||||||
</v-table>
|
|
@ -1,104 +0,0 @@
|
|||||||
import { Component, Input, Output, EventEmitter } from '@angular/core';
|
|
||||||
import { Router } from '@angular/router';
|
|
||||||
import { InvoicePaymentStatus } from '@vality/domain-proto/domain';
|
|
||||||
import { StatPayment } from '@vality/magista-proto/magista';
|
|
||||||
import { Column, TagColumn, LoadOptions, createOperationColumn } from '@vality/ng-core';
|
|
||||||
import { getUnionKey } from '@vality/ng-thrift';
|
|
||||||
import startCase from 'lodash-es/startCase';
|
|
||||||
|
|
||||||
import { AmountCurrencyService } from '@cc/app/shared/services';
|
|
||||||
|
|
||||||
import { createFailureColumn, createPartyColumn, createShopColumn } from '../../../../shared';
|
|
||||||
import { createProviderColumn } from '../../../../shared/utils/table/create-provider-column';
|
|
||||||
import { createTerminalColumn } from '../../../../shared/utils/table/create-terminal-column';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'cc-payments-table',
|
|
||||||
templateUrl: './payments-table.component.html',
|
|
||||||
})
|
|
||||||
export class PaymentsTableComponent {
|
|
||||||
@Input() data!: StatPayment[];
|
|
||||||
@Input() isLoading?: boolean | null;
|
|
||||||
@Input() hasMore?: boolean | null;
|
|
||||||
@Input() selected?: StatPayment[];
|
|
||||||
|
|
||||||
@Output() selectedChange = new EventEmitter<StatPayment[]>();
|
|
||||||
@Output() update = new EventEmitter<LoadOptions>();
|
|
||||||
@Output() more = new EventEmitter<void>();
|
|
||||||
|
|
||||||
columns: Column<StatPayment>[] = [
|
|
||||||
{ field: 'id', click: (d) => this.toDetails(d), pinned: 'left' },
|
|
||||||
{ field: 'invoice_id', pinned: 'left' },
|
|
||||||
{
|
|
||||||
field: 'amount',
|
|
||||||
type: 'currency',
|
|
||||||
formatter: (data) =>
|
|
||||||
this.amountCurrencyService.toMajor(data.amount, data.currency_symbolic_code),
|
|
||||||
typeParameters: {
|
|
||||||
currencyCode: 'currency_symbolic_code',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'fee',
|
|
||||||
type: 'currency',
|
|
||||||
formatter: (data) =>
|
|
||||||
this.amountCurrencyService.toMajor(data.fee, data.currency_symbolic_code),
|
|
||||||
typeParameters: {
|
|
||||||
currencyCode: 'currency_symbolic_code',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'status',
|
|
||||||
type: 'tag',
|
|
||||||
formatter: (data) => getUnionKey(data.status),
|
|
||||||
typeParameters: {
|
|
||||||
label: (data) => startCase(getUnionKey(data.status)),
|
|
||||||
tags: {
|
|
||||||
captured: { color: 'success' },
|
|
||||||
refunded: { color: 'success' },
|
|
||||||
charged_back: { color: 'success' },
|
|
||||||
pending: { color: 'pending' },
|
|
||||||
processed: { color: 'pending' },
|
|
||||||
failed: { color: 'warn' },
|
|
||||||
cancelled: { color: 'neutral' },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} as TagColumn<StatPayment, keyof InvoicePaymentStatus>,
|
|
||||||
{ field: 'created_at', type: 'datetime' },
|
|
||||||
createPartyColumn('owner_id'),
|
|
||||||
createShopColumn('shop_id', (d) => d.owner_id),
|
|
||||||
'domain_revision',
|
|
||||||
createTerminalColumn((d) => d.terminal_id.id),
|
|
||||||
createProviderColumn((d) => d.provider_id.id),
|
|
||||||
'external_id',
|
|
||||||
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',
|
|
||||||
click: (data) => this.toDetails(data),
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
];
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private amountCurrencyService: AmountCurrencyService,
|
|
||||||
private router: Router,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
private toDetails(data: StatPayment) {
|
|
||||||
return void this.router.navigate([
|
|
||||||
'party',
|
|
||||||
data.owner_id,
|
|
||||||
'invoice',
|
|
||||||
data.invoice_id,
|
|
||||||
'payment',
|
|
||||||
data.id,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
export * from './payments.module';
|
|
@ -1,22 +0,0 @@
|
|||||||
import { NgModule } from '@angular/core';
|
|
||||||
import { RouterModule } from '@angular/router';
|
|
||||||
|
|
||||||
import { AppAuthGuardService } from '@cc/app/shared/services';
|
|
||||||
|
|
||||||
import { PaymentsComponent } from './payments.component';
|
|
||||||
import { ROUTING_CONFIG } from './routing-config';
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
imports: [
|
|
||||||
RouterModule.forChild([
|
|
||||||
{
|
|
||||||
path: '',
|
|
||||||
component: PaymentsComponent,
|
|
||||||
canActivate: [AppAuthGuardService],
|
|
||||||
data: ROUTING_CONFIG,
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
],
|
|
||||||
exports: [RouterModule],
|
|
||||||
})
|
|
||||||
export class PaymentsRoutingModule {}
|
|
@ -1,65 +0,0 @@
|
|||||||
<cc-page-layout title="Old Payments">
|
|
||||||
<cc-page-layout-actions>
|
|
||||||
<v-more-filters-button [filters]="filters"></v-more-filters-button>
|
|
||||||
</cc-page-layout-actions>
|
|
||||||
<v-filters
|
|
||||||
#filters
|
|
||||||
[active]="active$ | async"
|
|
||||||
merge
|
|
||||||
(clear)="filtersForm.reset(); otherFiltersControl.reset()"
|
|
||||||
>
|
|
||||||
<ng-template [formGroup]="filtersForm" vMainFilters>
|
|
||||||
<v-date-range-field formControlName="dateRange"></v-date-range-field>
|
|
||||||
<v-list-field
|
|
||||||
focusedHint="invoice_1, invoice_2.payment_2"
|
|
||||||
formControlName="invoice_ids"
|
|
||||||
label="Invoice and Payment Ids"
|
|
||||||
></v-list-field>
|
|
||||||
<v-input-field formControlName="external_id" label="External Id"></v-input-field>
|
|
||||||
<cc-merchant-field formControlName="party_id"></cc-merchant-field>
|
|
||||||
<cc-shop-field
|
|
||||||
[partyId]="filtersForm.value.party_id"
|
|
||||||
formControlName="shop_ids"
|
|
||||||
multiple
|
|
||||||
></cc-shop-field>
|
|
||||||
<v-input-field formControlName="payment_first6" label="Card BIN"></v-input-field>
|
|
||||||
<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-magista-thrift-form
|
|
||||||
[extensions]="extensions"
|
|
||||||
[formControl]="otherFiltersControl"
|
|
||||||
noToolbar
|
|
||||||
type="PaymentSearchQuery"
|
|
||||||
></cc-magista-thrift-form>
|
|
||||||
</ng-template>
|
|
||||||
</v-filters>
|
|
||||||
<cc-payments-table
|
|
||||||
[data]="(payments$ | async) || []"
|
|
||||||
[hasMore]="hasMore$ | async"
|
|
||||||
[isLoading]="isLoading$ | async"
|
|
||||||
[selected]="selected$ | async"
|
|
||||||
(more)="more()"
|
|
||||||
(selectedChange)="selected$.next($event)"
|
|
||||||
(update)="reload($event ?? {})"
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
[disabled]="!(selected$ | async)?.length"
|
|
||||||
mat-raised-button
|
|
||||||
(click)="failMachines()"
|
|
||||||
>
|
|
||||||
Fail machines
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
[disabled]="!(selected$ | async)?.length"
|
|
||||||
color="primary"
|
|
||||||
mat-raised-button
|
|
||||||
(click)="createPaymentAdjustment()"
|
|
||||||
>
|
|
||||||
Create adjustments
|
|
||||||
</button>
|
|
||||||
</cc-payments-table>
|
|
||||||
</cc-page-layout>
|
|
@ -1,187 +0,0 @@
|
|||||||
import { Component, OnInit, Inject, DestroyRef } from '@angular/core';
|
|
||||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
||||||
import { NonNullableFormBuilder } from '@angular/forms';
|
|
||||||
import { StatPayment } from '@vality/magista-proto/magista';
|
|
||||||
import {
|
|
||||||
DialogService,
|
|
||||||
DialogResponseStatus,
|
|
||||||
LoadOptions,
|
|
||||||
getNoTimeZoneIsoString,
|
|
||||||
clean,
|
|
||||||
DateRange,
|
|
||||||
QueryParamsService,
|
|
||||||
createDateRangeToToday,
|
|
||||||
isEqualDateRange,
|
|
||||||
debounceTimeWithFirst,
|
|
||||||
getValueChanges,
|
|
||||||
countChanged,
|
|
||||||
} from '@vality/ng-core';
|
|
||||||
import { isTypeWithAliases } from '@vality/ng-thrift';
|
|
||||||
import { endOfDay } from 'date-fns';
|
|
||||||
import { uniq } from 'lodash-es';
|
|
||||||
import isEqual from 'lodash-es/isEqual';
|
|
||||||
import { BehaviorSubject, of, merge } from 'rxjs';
|
|
||||||
import { startWith, map, distinctUntilChanged, shareReplay } from 'rxjs/operators';
|
|
||||||
|
|
||||||
import { FailMachinesDialogComponent, Type } from '../../shared/components/fail-machines-dialog';
|
|
||||||
import { MetadataFormExtension } from '../../shared/components/metadata-form';
|
|
||||||
import { DATE_RANGE_DAYS, DEBOUNCE_TIME_MS } from '../../tokens';
|
|
||||||
|
|
||||||
import { CreatePaymentAdjustmentComponent } from './components/create-payment-adjustment/create-payment-adjustment.component';
|
|
||||||
import { FetchPaymentsService } from './services/fetch-payments.service';
|
|
||||||
|
|
||||||
interface Filters {
|
|
||||||
filters: PaymentsComponent['filtersForm']['value'];
|
|
||||||
otherFilters: PaymentsComponent['otherFiltersControl']['value'];
|
|
||||||
dateRange: DateRange;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
templateUrl: 'payments.component.html',
|
|
||||||
})
|
|
||||||
export class PaymentsComponent implements OnInit {
|
|
||||||
isLoading$ = this.fetchPaymentsService.isLoading$;
|
|
||||||
payments$ = this.fetchPaymentsService.result$;
|
|
||||||
hasMore$ = this.fetchPaymentsService.hasMore$;
|
|
||||||
selected$ = new BehaviorSubject<StatPayment[]>([]);
|
|
||||||
filtersForm = this.fb.group({
|
|
||||||
dateRange: createDateRangeToToday(this.dateRangeDays),
|
|
||||||
invoice_ids: [undefined as string[]],
|
|
||||||
party_id: undefined as string,
|
|
||||||
shop_ids: [undefined as string[]],
|
|
||||||
payment_first6: undefined as string,
|
|
||||||
payment_last4: undefined as string,
|
|
||||||
payment_rrn: undefined as string,
|
|
||||||
payment_email: undefined as string,
|
|
||||||
error_message: undefined as string,
|
|
||||||
external_id: undefined as string,
|
|
||||||
});
|
|
||||||
otherFiltersControl = this.fb.control({
|
|
||||||
common_search_query_params: {},
|
|
||||||
payment_params: {},
|
|
||||||
});
|
|
||||||
extensions: MetadataFormExtension[] = [
|
|
||||||
{
|
|
||||||
determinant: (data) =>
|
|
||||||
of(
|
|
||||||
isTypeWithAliases(data, 'CommonSearchQueryParams', 'magista') ||
|
|
||||||
[
|
|
||||||
'invoice_ids',
|
|
||||||
'payment_email',
|
|
||||||
'payment_first6',
|
|
||||||
'payment_last4',
|
|
||||||
'payment_rrn',
|
|
||||||
'error_message',
|
|
||||||
'external_id',
|
|
||||||
].includes(data?.field?.name),
|
|
||||||
),
|
|
||||||
extension: () => of({ hidden: true }),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
active$ = getValueChanges(this.filtersForm).pipe(
|
|
||||||
map((filters) =>
|
|
||||||
countChanged(this.initFiltersValue, filters, { dateRange: isEqualDateRange }),
|
|
||||||
),
|
|
||||||
shareReplay({ refCount: true, bufferSize: 1 }),
|
|
||||||
);
|
|
||||||
|
|
||||||
private initFiltersValue = this.filtersForm.value;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private qp: QueryParamsService<Filters>,
|
|
||||||
private fetchPaymentsService: FetchPaymentsService,
|
|
||||||
private dialogService: DialogService,
|
|
||||||
private fb: NonNullableFormBuilder,
|
|
||||||
@Inject(DATE_RANGE_DAYS) private dateRangeDays: number,
|
|
||||||
private dr: DestroyRef,
|
|
||||||
@Inject(DEBOUNCE_TIME_MS) private debounceTimeMs: number,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
ngOnInit() {
|
|
||||||
this.filtersForm.patchValue(
|
|
||||||
Object.assign(
|
|
||||||
{},
|
|
||||||
this.qp.params.filters,
|
|
||||||
clean({ dateRange: this.qp.params.dateRange }),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
this.otherFiltersControl.patchValue(Object.assign({}, this.qp.params.otherFilters));
|
|
||||||
merge(this.filtersForm.valueChanges, this.otherFiltersControl.valueChanges)
|
|
||||||
.pipe(
|
|
||||||
startWith(null),
|
|
||||||
debounceTimeWithFirst(this.debounceTimeMs),
|
|
||||||
map(() => {
|
|
||||||
const { dateRange, ...filters } = clean(this.filtersForm.value);
|
|
||||||
const otherFilters = clean(this.otherFiltersControl.value);
|
|
||||||
return { filters, dateRange, otherFilters };
|
|
||||||
}),
|
|
||||||
distinctUntilChanged(isEqual),
|
|
||||||
takeUntilDestroyed(this.dr),
|
|
||||||
)
|
|
||||||
.subscribe((filters) => {
|
|
||||||
void this.qp.set(filters);
|
|
||||||
this.load(filters);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
more() {
|
|
||||||
this.fetchPaymentsService.more();
|
|
||||||
}
|
|
||||||
|
|
||||||
load({ filters, otherFilters, dateRange }: Filters, options?: LoadOptions) {
|
|
||||||
const { invoice_ids, party_id, shop_ids, external_id, ...paymentParams } = filters;
|
|
||||||
const searchParams = clean({
|
|
||||||
...otherFilters,
|
|
||||||
common_search_query_params: {
|
|
||||||
...(otherFilters.common_search_query_params || {}),
|
|
||||||
party_id,
|
|
||||||
shop_ids,
|
|
||||||
from_time: getNoTimeZoneIsoString(dateRange.start),
|
|
||||||
to_time: getNoTimeZoneIsoString(endOfDay(dateRange.end)),
|
|
||||||
},
|
|
||||||
payment_params: { ...(otherFilters.payment_params || {}), ...paymentParams },
|
|
||||||
external_id,
|
|
||||||
invoice_ids,
|
|
||||||
});
|
|
||||||
this.fetchPaymentsService.load(searchParams, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
reload(options?: LoadOptions) {
|
|
||||||
this.fetchPaymentsService.reload(options);
|
|
||||||
}
|
|
||||||
|
|
||||||
createPaymentAdjustment() {
|
|
||||||
this.dialogService
|
|
||||||
.open(CreatePaymentAdjustmentComponent, {
|
|
||||||
payments: this.selected$.value,
|
|
||||||
})
|
|
||||||
.afterClosed()
|
|
||||||
.subscribe((res) => {
|
|
||||||
if (res.status === DialogResponseStatus.Success) {
|
|
||||||
this.reload();
|
|
||||||
this.selected$.next([]);
|
|
||||||
} else if (res.data?.errors?.length) {
|
|
||||||
this.selected$.next(res.data.errors.map(({ data }) => data));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
failMachines() {
|
|
||||||
this.dialogService
|
|
||||||
.open(FailMachinesDialogComponent, {
|
|
||||||
ids: uniq(this.selected$.value.map((s) => s.invoice_id)),
|
|
||||||
type: Type.Invoice,
|
|
||||||
})
|
|
||||||
.afterClosed()
|
|
||||||
.subscribe((res) => {
|
|
||||||
if (res.status === DialogResponseStatus.Success) {
|
|
||||||
this.reload();
|
|
||||||
this.selected$.next([]);
|
|
||||||
} else if (res.data?.errors?.length) {
|
|
||||||
this.selected$.next(
|
|
||||||
res.data.errors.map(({ index }) => this.selected$.value[index]),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,46 +0,0 @@
|
|||||||
import { CommonModule } from '@angular/common';
|
|
||||||
import { NgModule } from '@angular/core';
|
|
||||||
import { ReactiveFormsModule, FormsModule } from '@angular/forms';
|
|
||||||
import { MatButtonModule } from '@angular/material/button';
|
|
||||||
import {
|
|
||||||
FiltersModule,
|
|
||||||
DateRangeFieldModule,
|
|
||||||
ListFieldModule,
|
|
||||||
TableModule,
|
|
||||||
DialogModule,
|
|
||||||
InputFieldModule,
|
|
||||||
} from '@vality/ng-core';
|
|
||||||
|
|
||||||
import { PageLayoutModule, ShopFieldModule } from '@cc/app/shared';
|
|
||||||
import { MerchantFieldModule } from '@cc/app/shared/components/merchant-field';
|
|
||||||
import { ThriftFormModule } from '@cc/app/shared/components/metadata-form';
|
|
||||||
|
|
||||||
import { MagistaThriftFormComponent } from '../../shared/components/thrift-api-crud';
|
|
||||||
|
|
||||||
import { CreatePaymentAdjustmentComponent } from './components/create-payment-adjustment/create-payment-adjustment.component';
|
|
||||||
import { PaymentsTableComponent } from './components/payments-table/payments-table.component';
|
|
||||||
import { PaymentsRoutingModule } from './payments-routing.module';
|
|
||||||
import { PaymentsComponent } from './payments.component';
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
imports: [
|
|
||||||
CommonModule,
|
|
||||||
PaymentsRoutingModule,
|
|
||||||
PageLayoutModule,
|
|
||||||
FiltersModule,
|
|
||||||
DateRangeFieldModule,
|
|
||||||
ListFieldModule,
|
|
||||||
MerchantFieldModule,
|
|
||||||
ReactiveFormsModule,
|
|
||||||
TableModule,
|
|
||||||
DialogModule,
|
|
||||||
ThriftFormModule,
|
|
||||||
MatButtonModule,
|
|
||||||
ShopFieldModule,
|
|
||||||
InputFieldModule,
|
|
||||||
FormsModule,
|
|
||||||
MagistaThriftFormComponent,
|
|
||||||
],
|
|
||||||
declarations: [PaymentsComponent, CreatePaymentAdjustmentComponent, PaymentsTableComponent],
|
|
||||||
})
|
|
||||||
export class PaymentsModule {}
|
|
@ -1,5 +0,0 @@
|
|||||||
import { Services, RoutingConfig } from '@cc/app/shared/services';
|
|
||||||
|
|
||||||
export const ROUTING_CONFIG: RoutingConfig = {
|
|
||||||
services: [Services.MerchantStatistics],
|
|
||||||
};
|
|
@ -1,67 +0,0 @@
|
|||||||
import { Injectable } from '@angular/core';
|
|
||||||
import { StatPayment, PaymentSearchQuery } from '@vality/magista-proto/magista';
|
|
||||||
import {
|
|
||||||
FetchSuperclass,
|
|
||||||
FetchOptions,
|
|
||||||
FetchResult,
|
|
||||||
NotifyLogService,
|
|
||||||
clean,
|
|
||||||
} from '@vality/ng-core';
|
|
||||||
import isNil from 'lodash-es/isNil';
|
|
||||||
import { Observable, of } from 'rxjs';
|
|
||||||
import { map, catchError } from 'rxjs/operators';
|
|
||||||
|
|
||||||
import { MerchantStatisticsService } from '@cc/app/api/magista';
|
|
||||||
|
|
||||||
function splitInvoicePaymentId(invoicePaymentId: string) {
|
|
||||||
const [invoiceId, paymentId] = invoicePaymentId.split('.');
|
|
||||||
return { invoiceId, paymentId };
|
|
||||||
}
|
|
||||||
|
|
||||||
@Injectable({
|
|
||||||
providedIn: 'root',
|
|
||||||
})
|
|
||||||
export class FetchPaymentsService extends FetchSuperclass<StatPayment, PaymentSearchQuery> {
|
|
||||||
constructor(
|
|
||||||
private merchantStatisticsService: MerchantStatisticsService,
|
|
||||||
private log: NotifyLogService,
|
|
||||||
) {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected fetch(
|
|
||||||
params: PaymentSearchQuery,
|
|
||||||
{ size, continuationToken }: FetchOptions,
|
|
||||||
): Observable<FetchResult<StatPayment>> {
|
|
||||||
const invoicePaymentIds = (params.invoice_ids || []).map((id) => splitInvoicePaymentId(id));
|
|
||||||
const invoiceIds = [...new Set(invoicePaymentIds.map(({ invoiceId }) => invoiceId))];
|
|
||||||
return this.merchantStatisticsService
|
|
||||||
.SearchPayments({
|
|
||||||
payment_params: {},
|
|
||||||
...params,
|
|
||||||
...clean({ invoice_ids: invoiceIds }),
|
|
||||||
common_search_query_params: Object.assign({}, params.common_search_query_params, {
|
|
||||||
continuation_token: continuationToken,
|
|
||||||
limit: size,
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
.pipe(
|
|
||||||
map(({ payments, continuation_token }) => ({
|
|
||||||
result: params.invoice_ids?.length
|
|
||||||
? payments.filter((p) =>
|
|
||||||
invoicePaymentIds.some(
|
|
||||||
(id) =>
|
|
||||||
id.invoiceId === p.invoice_id &&
|
|
||||||
(isNil(id.paymentId) || id.paymentId === p.id),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: payments,
|
|
||||||
continuationToken: continuation_token,
|
|
||||||
})),
|
|
||||||
catchError((err) => {
|
|
||||||
this.log.errorOperation(err, 'receive', 'payments');
|
|
||||||
return of({ result: [] });
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -7,7 +7,7 @@ import startCase from 'lodash-es/startCase';
|
|||||||
|
|
||||||
import { AmountCurrencyService } from '@cc/app/shared/services';
|
import { AmountCurrencyService } from '@cc/app/shared/services';
|
||||||
|
|
||||||
import { createFailureColumn2 } from '../../../../shared';
|
import { createFailureColumn } from '../../../../shared';
|
||||||
import {
|
import {
|
||||||
createPartyColumn,
|
createPartyColumn,
|
||||||
createShopColumn,
|
createShopColumn,
|
||||||
@ -36,6 +36,7 @@ export class PaymentsTableComponent {
|
|||||||
cell: (d) => ({
|
cell: (d) => ({
|
||||||
click: () => this.toDetails(d),
|
click: () => this.toDetails(d),
|
||||||
}),
|
}),
|
||||||
|
sticky: 'start',
|
||||||
},
|
},
|
||||||
{ field: 'invoice_id', sticky: 'start' },
|
{ field: 'invoice_id', sticky: 'start' },
|
||||||
{ field: 'external_id' },
|
{ field: 'external_id' },
|
||||||
@ -72,7 +73,7 @@ export class PaymentsTableComponent {
|
|||||||
createDomainObjectColumn((d) => ({ ref: { provider: d.provider_id } }), {
|
createDomainObjectColumn((d) => ({ ref: { provider: d.provider_id } }), {
|
||||||
header: 'Provider',
|
header: 'Provider',
|
||||||
}),
|
}),
|
||||||
createFailureColumn2((d) => ({
|
createFailureColumn((d) => ({
|
||||||
failure: d.status?.failed?.failure?.failure,
|
failure: d.status?.failed?.failure?.failure,
|
||||||
noFailureMessage:
|
noFailureMessage:
|
||||||
getUnionKey(d.status?.failed?.failure) === 'failure'
|
getUnionKey(d.status?.failed?.failure) === 'failure'
|
||||||
|
@ -1,21 +1,22 @@
|
|||||||
<v-dialog title="Party routing rule params">
|
<v-dialog title="Party routing rule params">
|
||||||
<div [formGroup]="form" style="display: flex; flex-direction: column; gap: 24px">
|
<div [formGroup]="form" style="display: flex; flex-direction: column; gap: 24px">
|
||||||
<mat-form-field *ngIf="dialogData.type === 'payment'">
|
@if (dialogData.type === 'payment') {
|
||||||
<mat-label>Shop</mat-label>
|
<v-select-field
|
||||||
<mat-select formControlName="shopID" required>
|
[options]="shopsOptions"
|
||||||
<mat-option *ngFor="let shop of dialogData.shops" [value]="shop.id">
|
formControlName="shopID"
|
||||||
{{ shop.details.name }}
|
label="Shop"
|
||||||
</mat-option>
|
required
|
||||||
</mat-select>
|
style="z-index: 9999"
|
||||||
</mat-form-field>
|
></v-select-field>
|
||||||
<mat-form-field *ngIf="dialogData.type === 'withdrawal'">
|
}
|
||||||
<mat-label>Wallet</mat-label>
|
@if (dialogData.type === 'withdrawal') {
|
||||||
<mat-select formControlName="walletID" required>
|
<v-select-field
|
||||||
<mat-option *ngFor="let wallet of dialogData.wallets" [value]="wallet.id">
|
[options]="walletsOptions"
|
||||||
{{ wallet.name }}
|
formControlName="walletID"
|
||||||
</mat-option>
|
label="Wallet"
|
||||||
</mat-select>
|
required
|
||||||
</mat-form-field>
|
></v-select-field>
|
||||||
|
}
|
||||||
<mat-divider></mat-divider>
|
<mat-divider></mat-divider>
|
||||||
<div class="mat-headline-4 mat-no-margin">Routing ruleset</div>
|
<div class="mat-headline-4 mat-no-margin">Routing ruleset</div>
|
||||||
<div style="display: flex; flex-direction: column; gap: 16px">
|
<div style="display: flex; flex-direction: column; gap: 16px">
|
||||||
|
@ -2,8 +2,9 @@ import { Component, DestroyRef } from '@angular/core';
|
|||||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||||
import { FormBuilder } from '@angular/forms';
|
import { FormBuilder } from '@angular/forms';
|
||||||
import { Shop } from '@vality/domain-proto/domain';
|
import { Shop } from '@vality/domain-proto/domain';
|
||||||
|
import { ShopID, WalletID } from '@vality/domain-proto/internal/domain';
|
||||||
import { StatWallet } from '@vality/fistful-proto/fistful_stat';
|
import { StatWallet } from '@vality/fistful-proto/fistful_stat';
|
||||||
import { DialogResponseStatus, DialogSuperclass, NotifyLogService } from '@vality/ng-core';
|
import { DialogResponseStatus, DialogSuperclass, NotifyLogService, Option } from '@vality/ng-core';
|
||||||
|
|
||||||
import { RoutingRulesService } from '../../services/routing-rules';
|
import { RoutingRulesService } from '../../services/routing-rules';
|
||||||
import { RoutingRulesType } from '../../types/routing-rules-type';
|
import { RoutingRulesType } from '../../types/routing-rules-type';
|
||||||
@ -16,12 +17,24 @@ export class AddPartyRoutingRuleDialogComponent extends DialogSuperclass<
|
|||||||
{ refID: number; partyID: string; shops: Shop[]; wallets: StatWallet[]; type: RoutingRulesType }
|
{ refID: number; partyID: string; shops: Shop[]; wallets: StatWallet[]; type: RoutingRulesType }
|
||||||
> {
|
> {
|
||||||
form = this.fb.group<{ shopID: string; walletID: string; name: string; description: string }>({
|
form = this.fb.group<{ shopID: string; walletID: string; name: string; description: string }>({
|
||||||
shopID: '',
|
shopID: null,
|
||||||
walletID: '',
|
walletID: null,
|
||||||
name: 'Ruleset[candidates]',
|
name: 'Ruleset[candidates]',
|
||||||
description: '',
|
description: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
shopsOptions: Option<ShopID>[] = this.dialogData.shops.map((s) => ({
|
||||||
|
value: s.id,
|
||||||
|
label: s.details.name,
|
||||||
|
description: s.id,
|
||||||
|
}));
|
||||||
|
|
||||||
|
walletsOptions: Option<WalletID>[] = this.dialogData.wallets.map((s) => ({
|
||||||
|
value: s.id,
|
||||||
|
label: s.name,
|
||||||
|
description: s.id,
|
||||||
|
}));
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private fb: FormBuilder,
|
private fb: FormBuilder,
|
||||||
private routingRulesService: RoutingRulesService,
|
private routingRulesService: RoutingRulesService,
|
||||||
|
@ -10,7 +10,7 @@ import { MatIconModule } from '@angular/material/icon';
|
|||||||
import { MatInputModule } from '@angular/material/input';
|
import { MatInputModule } from '@angular/material/input';
|
||||||
import { MatRadioModule } from '@angular/material/radio';
|
import { MatRadioModule } from '@angular/material/radio';
|
||||||
import { MatSelectModule } from '@angular/material/select';
|
import { MatSelectModule } from '@angular/material/select';
|
||||||
import { DialogModule } from '@vality/ng-core';
|
import { DialogModule, SelectFieldModule } from '@vality/ng-core';
|
||||||
|
|
||||||
import { AddPartyRoutingRuleDialogComponent } from './add-party-routing-rule-dialog.component';
|
import { AddPartyRoutingRuleDialogComponent } from './add-party-routing-rule-dialog.component';
|
||||||
|
|
||||||
@ -28,6 +28,7 @@ import { AddPartyRoutingRuleDialogComponent } from './add-party-routing-rule-dia
|
|||||||
MatRadioModule,
|
MatRadioModule,
|
||||||
MatAutocompleteModule,
|
MatAutocompleteModule,
|
||||||
DialogModule,
|
DialogModule,
|
||||||
|
SelectFieldModule,
|
||||||
],
|
],
|
||||||
declarations: [AddPartyRoutingRuleDialogComponent],
|
declarations: [AddPartyRoutingRuleDialogComponent],
|
||||||
exports: [AddPartyRoutingRuleDialogComponent],
|
exports: [AddPartyRoutingRuleDialogComponent],
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Component, DestroyRef } from '@angular/core';
|
import { Component, DestroyRef } from '@angular/core';
|
||||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { DialogService, DialogResponseStatus } from '@vality/ng-core';
|
import { DialogService, DialogResponseStatus, compareDifferentTypes } from '@vality/ng-core';
|
||||||
import { combineLatest } from 'rxjs';
|
import { combineLatest } from 'rxjs';
|
||||||
import { filter, map, shareReplay, startWith, switchMap, take } from 'rxjs/operators';
|
import { filter, map, shareReplay, startWith, switchMap, take } from 'rxjs/operators';
|
||||||
|
|
||||||
@ -165,20 +165,39 @@ export class PartyRoutingRulesetComponent {
|
|||||||
this.partyRoutingRulesetService.wallets$,
|
this.partyRoutingRulesetService.wallets$,
|
||||||
this.routingRulesTypeService.routingRulesType$,
|
this.routingRulesTypeService.routingRulesType$,
|
||||||
this.partyRoutingRulesetService.partyID$,
|
this.partyRoutingRulesetService.partyID$,
|
||||||
|
this.partyRuleset$,
|
||||||
])
|
])
|
||||||
.pipe(
|
.pipe(
|
||||||
take(1),
|
take(1),
|
||||||
switchMap(([refID, shops, wallets, type, partyID]) =>
|
switchMap(([refID, shops, wallets, type, partyID, ruleset]) => {
|
||||||
this.dialogService
|
return this.dialogService
|
||||||
.open(AddPartyRoutingRuleDialogComponent, {
|
.open(AddPartyRoutingRuleDialogComponent, {
|
||||||
refID,
|
refID,
|
||||||
shops,
|
shops: shops
|
||||||
wallets,
|
.filter((s) =>
|
||||||
|
ruleset.data.decisions.delegates.every(
|
||||||
|
(d) =>
|
||||||
|
d?.allowed?.condition?.party?.definition?.shop_is !==
|
||||||
|
s.id,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.sort((a, b) =>
|
||||||
|
compareDifferentTypes(a.details.name, b.details.name),
|
||||||
|
),
|
||||||
|
wallets: wallets
|
||||||
|
.filter((w) =>
|
||||||
|
ruleset.data.decisions.delegates.every(
|
||||||
|
(d) =>
|
||||||
|
d?.allowed?.condition?.party?.definition?.wallet_is !==
|
||||||
|
w.id,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.sort((a, b) => compareDifferentTypes(a.name, b.name)),
|
||||||
type,
|
type,
|
||||||
partyID,
|
partyID,
|
||||||
})
|
})
|
||||||
.afterClosed(),
|
.afterClosed();
|
||||||
),
|
}),
|
||||||
takeUntilDestroyed(this.destroyRef),
|
takeUntilDestroyed(this.destroyRef),
|
||||||
)
|
)
|
||||||
.subscribe({
|
.subscribe({
|
||||||
|
@ -43,6 +43,7 @@ export class PartyRoutingRulesetService {
|
|||||||
shops$ = defer(() => this.party$).pipe(
|
shops$ = defer(() => this.party$).pipe(
|
||||||
map((p) => p.shops),
|
map((p) => p.shops),
|
||||||
map((shops) => Array.from(shops.values())),
|
map((shops) => Array.from(shops.values())),
|
||||||
|
shareReplay({ refCount: true, bufferSize: 1 }),
|
||||||
);
|
);
|
||||||
wallets$ = defer(() => this.partyID$).pipe(
|
wallets$ = defer(() => this.partyID$).pipe(
|
||||||
switchMap((partyID) =>
|
switchMap((partyID) =>
|
||||||
|
@ -40,10 +40,6 @@ const ROUTES: Routes = [
|
|||||||
path: 'payments',
|
path: 'payments',
|
||||||
loadChildren: () => import('./payments/payments.module').then((m) => m.PaymentsModule),
|
loadChildren: () => import('./payments/payments.module').then((m) => m.PaymentsModule),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: 'old-payments',
|
|
||||||
loadChildren: () => import('./old-payments/payments.module').then((m) => m.PaymentsModule),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: 'deposits',
|
path: 'deposits',
|
||||||
loadChildren: () => import('./deposits/deposits.module').then((m) => m.DepositsModule),
|
loadChildren: () => import('./deposits/deposits.module').then((m) => m.DepositsModule),
|
||||||
|
@ -24,6 +24,7 @@ import {
|
|||||||
UpdateOptions,
|
UpdateOptions,
|
||||||
VSelectPipe,
|
VSelectPipe,
|
||||||
Column2,
|
Column2,
|
||||||
|
cachedHeadMap,
|
||||||
} from '@vality/ng-core';
|
} from '@vality/ng-core';
|
||||||
import { map, shareReplay } from 'rxjs/operators';
|
import { map, shareReplay } from 'rxjs/operators';
|
||||||
import { Overwrite } from 'utility-types';
|
import { Overwrite } from 'utility-types';
|
||||||
@ -86,19 +87,17 @@ export class ShopsTermsComponent implements OnInit {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
terms$ = this.shopsTermsService.result$.pipe(
|
terms$ = this.shopsTermsService.result$.pipe(
|
||||||
map((terms) =>
|
cachedHeadMap((t) => ({
|
||||||
terms.map((t) => ({
|
value: t,
|
||||||
value: t,
|
children: getFlatDecisions(getShopCashFlowSelectors(t.current_term_set)).filter((v) =>
|
||||||
children: getFlatDecisions(getShopCashFlowSelectors(t.current_term_set)).filter(
|
isShopTermSetDecision(v, {
|
||||||
(v) =>
|
partyId: t.owner_id,
|
||||||
isShopTermSetDecision(v, {
|
shopId: t.shop_id,
|
||||||
partyId: t.owner_id,
|
currency: t.currency,
|
||||||
shopId: t.shop_id,
|
}),
|
||||||
currency: t.currency,
|
),
|
||||||
}),
|
})),
|
||||||
),
|
shareReplay({ refCount: true, bufferSize: 1 }),
|
||||||
})),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
hasMore$ = this.shopsTermsService.hasMore$;
|
hasMore$ = this.shopsTermsService.hasMore$;
|
||||||
isLoading$ = this.shopsTermsService.isLoading$;
|
isLoading$ = this.shopsTermsService.isLoading$;
|
||||||
|
@ -23,6 +23,7 @@ import {
|
|||||||
TableModule,
|
TableModule,
|
||||||
UpdateOptions,
|
UpdateOptions,
|
||||||
VSelectPipe,
|
VSelectPipe,
|
||||||
|
cachedHeadMap,
|
||||||
} from '@vality/ng-core';
|
} from '@vality/ng-core';
|
||||||
import { map, shareReplay } from 'rxjs/operators';
|
import { map, shareReplay } from 'rxjs/operators';
|
||||||
import { Overwrite } from 'utility-types';
|
import { Overwrite } from 'utility-types';
|
||||||
@ -74,7 +75,8 @@ export class TerminalsTermsComponent implements OnInit {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
terms$ = this.terminalsTermsService.result$.pipe(
|
terms$ = this.terminalsTermsService.result$.pipe(
|
||||||
map((terms) => terms.map(getTerminalTreeDataItem((d) => d.current_term_set))),
|
cachedHeadMap(getTerminalTreeDataItem((d) => d.current_term_set)),
|
||||||
|
shareReplay({ refCount: true, bufferSize: 1 }),
|
||||||
);
|
);
|
||||||
hasMore$ = this.terminalsTermsService.hasMore$;
|
hasMore$ = this.terminalsTermsService.hasMore$;
|
||||||
isLoading$ = this.terminalsTermsService.isLoading$;
|
isLoading$ = this.terminalsTermsService.isLoading$;
|
||||||
|
@ -27,6 +27,7 @@ import {
|
|||||||
UpdateOptions,
|
UpdateOptions,
|
||||||
VSelectPipe,
|
VSelectPipe,
|
||||||
Column2,
|
Column2,
|
||||||
|
cachedHeadMap,
|
||||||
} from '@vality/ng-core';
|
} from '@vality/ng-core';
|
||||||
import { map, shareReplay } from 'rxjs/operators';
|
import { map, shareReplay } from 'rxjs/operators';
|
||||||
import { Overwrite } from 'utility-types';
|
import { Overwrite } from 'utility-types';
|
||||||
@ -89,19 +90,17 @@ export class WalletsTermsComponent implements OnInit {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
terms$ = this.walletsTermsService.result$.pipe(
|
terms$ = this.walletsTermsService.result$.pipe(
|
||||||
map((terms) =>
|
cachedHeadMap((t) => ({
|
||||||
terms.map((t) => ({
|
value: t,
|
||||||
value: t,
|
children: getFlatDecisions(getWalletCashFlowSelectors(t.current_term_set)).filter((v) =>
|
||||||
children: getFlatDecisions(getWalletCashFlowSelectors(t.current_term_set)).filter(
|
isWalletTermSetDecision(v, {
|
||||||
(v) =>
|
partyId: t.owner_id,
|
||||||
isWalletTermSetDecision(v, {
|
walletId: t.wallet_id,
|
||||||
partyId: t.owner_id,
|
currency: t.currency,
|
||||||
walletId: t.wallet_id,
|
}),
|
||||||
currency: t.currency,
|
),
|
||||||
}),
|
})),
|
||||||
),
|
shareReplay({ refCount: true, bufferSize: 1 }),
|
||||||
})),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
hasMore$ = this.walletsTermsService.hasMore$;
|
hasMore$ = this.walletsTermsService.hasMore$;
|
||||||
isLoading$ = this.walletsTermsService.isLoading$;
|
isLoading$ = this.walletsTermsService.isLoading$;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<cc-page-layout title="Withdrawals">
|
<cc-page-layout fullHeight title="Withdrawals">
|
||||||
<cc-page-layout-actions>
|
<cc-page-layout-actions>
|
||||||
<v-more-filters-button [filters]="filters"></v-more-filters-button>
|
<v-more-filters-button [filters]="filters"></v-more-filters-button>
|
||||||
</cc-page-layout-actions>
|
</cc-page-layout-actions>
|
||||||
@ -24,7 +24,7 @@
|
|||||||
</ng-template>
|
</ng-template>
|
||||||
</v-filters>
|
</v-filters>
|
||||||
|
|
||||||
<v-table
|
<v-table2
|
||||||
[(rowSelected)]="selected"
|
[(rowSelected)]="selected"
|
||||||
[columns]="columns"
|
[columns]="columns"
|
||||||
[data]="withdrawals$ | async"
|
[data]="withdrawals$ | async"
|
||||||
@ -35,12 +35,7 @@
|
|||||||
(update)="reload($event)"
|
(update)="reload($event)"
|
||||||
>
|
>
|
||||||
<v-table-actions>
|
<v-table-actions>
|
||||||
<button
|
<button [disabled]="!selected?.length" mat-raised-button (click)="failMachines()">
|
||||||
[disabled]="!selected?.length"
|
|
||||||
color="primary"
|
|
||||||
mat-raised-button
|
|
||||||
(click)="failMachines()"
|
|
||||||
>
|
|
||||||
Fail machines
|
Fail machines
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
@ -52,5 +47,5 @@
|
|||||||
Create adjustments
|
Create adjustments
|
||||||
</button>
|
</button>
|
||||||
</v-table-actions>
|
</v-table-actions>
|
||||||
</v-table>
|
</v-table2>
|
||||||
</cc-page-layout>
|
</cc-page-layout>
|
||||||
|
@ -5,7 +5,6 @@ import { PartyID } from '@vality/domain-proto/domain';
|
|||||||
import { StatWithdrawal } from '@vality/fistful-proto/fistful_stat';
|
import { StatWithdrawal } from '@vality/fistful-proto/fistful_stat';
|
||||||
import {
|
import {
|
||||||
QueryParamsService,
|
QueryParamsService,
|
||||||
Column,
|
|
||||||
UpdateOptions,
|
UpdateOptions,
|
||||||
clean,
|
clean,
|
||||||
DialogService,
|
DialogService,
|
||||||
@ -18,6 +17,7 @@ import {
|
|||||||
getValueChanges,
|
getValueChanges,
|
||||||
countChanged,
|
countChanged,
|
||||||
debounceTimeWithFirst,
|
debounceTimeWithFirst,
|
||||||
|
Column2,
|
||||||
} from '@vality/ng-core';
|
} from '@vality/ng-core';
|
||||||
import { getUnionKey } from '@vality/ng-thrift';
|
import { getUnionKey } from '@vality/ng-thrift';
|
||||||
import { endOfDay } from 'date-fns';
|
import { endOfDay } from 'date-fns';
|
||||||
@ -28,9 +28,7 @@ import { WithdrawalParams } from '@cc/app/api/fistful-stat';
|
|||||||
|
|
||||||
import { createFailureColumn } from '../../shared';
|
import { createFailureColumn } from '../../shared';
|
||||||
import { FailMachinesDialogComponent, Type } from '../../shared/components/fail-machines-dialog';
|
import { FailMachinesDialogComponent, Type } from '../../shared/components/fail-machines-dialog';
|
||||||
import { AmountCurrencyService } from '../../shared/services';
|
import { createDomainObjectColumn, createCurrencyColumn } from '../../shared/utils/table2';
|
||||||
import { createProviderColumn } from '../../shared/utils/table/create-provider-column';
|
|
||||||
import { createTerminalColumn } from '../../shared/utils/table/create-terminal-column';
|
|
||||||
import { DATE_RANGE_DAYS, DEBOUNCE_TIME_MS } from '../../tokens';
|
import { DATE_RANGE_DAYS, DEBOUNCE_TIME_MS } from '../../tokens';
|
||||||
|
|
||||||
import { CreateAdjustmentDialogComponent } from './components/create-adjustment-dialog/create-adjustment-dialog.component';
|
import { CreateAdjustmentDialogComponent } from './components/create-adjustment-dialog/create-adjustment-dialog.component';
|
||||||
@ -72,46 +70,39 @@ export class WithdrawalsComponent implements OnInit {
|
|||||||
withdrawals$ = this.fetchWithdrawalsService.result$;
|
withdrawals$ = this.fetchWithdrawalsService.result$;
|
||||||
inProgress$ = this.fetchWithdrawalsService.isLoading$;
|
inProgress$ = this.fetchWithdrawalsService.isLoading$;
|
||||||
hasMore$ = this.fetchWithdrawalsService.hasMore$;
|
hasMore$ = this.fetchWithdrawalsService.hasMore$;
|
||||||
columns: Column<StatWithdrawal>[] = [
|
columns: Column2<StatWithdrawal>[] = [
|
||||||
{ field: 'id' },
|
{ field: 'id', sticky: 'start' },
|
||||||
{ field: 'created_at', type: 'datetime' },
|
{ field: 'external_id' },
|
||||||
'identity_id',
|
{ field: 'created_at', cell: { type: 'datetime' } },
|
||||||
'source_id',
|
{ field: 'identity_id' },
|
||||||
'destination_id',
|
{ field: 'source_id' },
|
||||||
'external_id',
|
{ field: 'destination_id' },
|
||||||
{
|
createCurrencyColumn((d) => ({ amount: d.amount, code: d.currency_symbolic_code }), {
|
||||||
field: 'amount',
|
field: 'amount',
|
||||||
type: 'currency',
|
}),
|
||||||
formatter: (d) =>
|
createCurrencyColumn((d) => ({ amount: d.fee, code: d.currency_symbolic_code }), {
|
||||||
this.amountCurrencyService.toMajor(d.amount, d.currency_symbolic_code),
|
|
||||||
typeParameters: {
|
|
||||||
currencyCode: (d) => d.currency_symbolic_code,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'fee',
|
field: 'fee',
|
||||||
type: 'currency',
|
}),
|
||||||
formatter: (d) => this.amountCurrencyService.toMajor(d.fee, d.currency_symbolic_code),
|
|
||||||
typeParameters: {
|
|
||||||
currencyCode: (d) => d.currency_symbolic_code,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
field: 'status',
|
field: 'status',
|
||||||
type: 'tag',
|
cell: (d) => ({
|
||||||
formatter: (d) => getUnionKey(d.status),
|
value: startCase(getUnionKey(d.status)),
|
||||||
typeParameters: {
|
color: (
|
||||||
label: (d) => startCase(getUnionKey(d.status)),
|
{
|
||||||
tags: {
|
pending: 'pending',
|
||||||
pending: { color: 'pending' },
|
succeeded: 'success',
|
||||||
succeeded: { color: 'success' },
|
failed: 'warn',
|
||||||
failed: { color: 'warn' },
|
} as const
|
||||||
},
|
)[getUnionKey(d.status)],
|
||||||
},
|
}),
|
||||||
},
|
},
|
||||||
createTerminalColumn((d) => d.terminal_id),
|
createDomainObjectColumn((d) => ({ ref: { terminal: { id: d.terminal_id } } }), {
|
||||||
createProviderColumn((d) => d.provider_id),
|
header: 'Terminal',
|
||||||
createFailureColumn<StatWithdrawal>((d) => d.status?.failed?.base_failure),
|
}),
|
||||||
|
createDomainObjectColumn((d) => ({ ref: { provider: { id: d.provider_id } } }), {
|
||||||
|
header: 'Provider',
|
||||||
|
}),
|
||||||
|
createFailureColumn((d) => ({ failure: d.status?.failed?.base_failure })),
|
||||||
];
|
];
|
||||||
selected: StatWithdrawal[] = [];
|
selected: StatWithdrawal[] = [];
|
||||||
statuses: WithdrawalParams['status'][] = ['Pending', 'Succeeded', 'Failed'];
|
statuses: WithdrawalParams['status'][] = ['Pending', 'Succeeded', 'Failed'];
|
||||||
@ -122,7 +113,6 @@ export class WithdrawalsComponent implements OnInit {
|
|||||||
private fetchWithdrawalsService: FetchWithdrawalsService,
|
private fetchWithdrawalsService: FetchWithdrawalsService,
|
||||||
private fb: NonNullableFormBuilder,
|
private fb: NonNullableFormBuilder,
|
||||||
private qp: QueryParamsService<Partial<WithdrawalsForm>>,
|
private qp: QueryParamsService<Partial<WithdrawalsForm>>,
|
||||||
private amountCurrencyService: AmountCurrencyService,
|
|
||||||
private dialogService: DialogService,
|
private dialogService: DialogService,
|
||||||
private destroyRef: DestroyRef,
|
private destroyRef: DestroyRef,
|
||||||
@Inject(DATE_RANGE_DAYS) private dateRangeDays: number,
|
@Inject(DATE_RANGE_DAYS) private dateRangeDays: number,
|
||||||
|
@ -76,6 +76,15 @@ export class DomainMetadataFormExtensionsService {
|
|||||||
generate: () => of('authorization_failed:unknown'),
|
generate: () => of('authorization_failed:unknown'),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
determinant: (data) => of(isTypeWithAliases(data, 'DataRevision', 'domain')),
|
||||||
|
extension: () =>
|
||||||
|
this.domainStoreService.version$.pipe(
|
||||||
|
map(() => ({
|
||||||
|
generate: () => this.domainStoreService.version$,
|
||||||
|
})),
|
||||||
|
),
|
||||||
|
},
|
||||||
]),
|
]),
|
||||||
shareReplay({ refCount: true, bufferSize: 1 }),
|
shareReplay({ refCount: true, bufferSize: 1 }),
|
||||||
);
|
);
|
||||||
|
@ -1,12 +1,5 @@
|
|||||||
import { Failure } from '@vality/domain-proto/domain';
|
import { Failure } from '@vality/domain-proto/domain';
|
||||||
import {
|
import { createColumn } from '@vality/ng-core';
|
||||||
PossiblyAsync,
|
|
||||||
ColumnObject,
|
|
||||||
getPossiblyAsyncObservable,
|
|
||||||
createColumn,
|
|
||||||
} from '@vality/ng-core';
|
|
||||||
import { of } from 'rxjs';
|
|
||||||
import { map, switchMap } from 'rxjs/operators';
|
|
||||||
|
|
||||||
function getFailureMessageTree(failure: Failure, withReason = true, level = Infinity) {
|
function getFailureMessageTree(failure: Failure, withReason = true, level = Infinity) {
|
||||||
if (!failure) {
|
if (!failure) {
|
||||||
@ -21,38 +14,8 @@ function getFailureMessageTree(failure: Failure, withReason = true, level = Infi
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createFailureColumn<T extends object>(
|
export const createFailureColumn = createColumn(
|
||||||
selectFailure: (d: T) => PossiblyAsync<Failure>,
|
({ failure, noFailureMessage }: { failure: Failure; noFailureMessage?: string }) => ({
|
||||||
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>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const createFailureColumn2 = createColumn(
|
|
||||||
({ failure, noFailureMessage }: { failure: Failure; noFailureMessage: string }) => ({
|
|
||||||
value: noFailureMessage || getFailureMessageTree(failure, false, 2),
|
value: noFailureMessage || getFailureMessageTree(failure, false, 2),
|
||||||
description: failure?.reason || '',
|
description: failure?.reason || '',
|
||||||
tooltip: failure?.sub?.sub ? JSON.stringify(failure.sub.sub, null, 2) : '',
|
tooltip: failure?.sub?.sub ? JSON.stringify(failure.sub.sub, null, 2) : '',
|
||||||
|
Loading…
Reference in New Issue
Block a user