mirror of
https://github.com/valitydev/dashboard.git
synced 2024-11-06 02:25:23 +00:00
TD-373: Load invoices and refunds in payments (#83)
This commit is contained in:
parent
f0a1714760
commit
418b418cad
@ -1,11 +1,9 @@
|
|||||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, Output } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, Output } from '@angular/core';
|
||||||
import { Invoice, PaymentSearchResult } from '@vality/swag-anapi-v2';
|
import { Invoice, PaymentSearchResult } from '@vality/swag-anapi-v2';
|
||||||
import isEmpty from 'lodash-es/isEmpty';
|
import isEmpty from 'lodash-es/isEmpty';
|
||||||
import isNil from 'lodash-es/isNil';
|
|
||||||
import isObject from 'lodash-es/isObject';
|
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
import { ComponentChange, ComponentChanges } from '@dsh/type-utils';
|
import { ComponentChanges } from '@dsh/type-utils';
|
||||||
|
|
||||||
import { PaymentIds } from '../../types';
|
import { PaymentIds } from '../../types';
|
||||||
import { InvoiceDetailsService } from './services/invoice-details/invoice-details.service';
|
import { InvoiceDetailsService } from './services/invoice-details/invoice-details.service';
|
||||||
@ -15,10 +13,11 @@ import { isPaymentFlowHold } from './types/is-payment-flow-hold';
|
|||||||
selector: 'dsh-payment-details',
|
selector: 'dsh-payment-details',
|
||||||
templateUrl: 'payment-details.component.html',
|
templateUrl: 'payment-details.component.html',
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
providers: [InvoiceDetailsService],
|
||||||
})
|
})
|
||||||
export class PaymentDetailsComponent implements OnChanges {
|
export class PaymentDetailsComponent implements OnChanges {
|
||||||
@Input() payment: PaymentSearchResult;
|
@Input() payment: PaymentSearchResult;
|
||||||
@Output() refreshPayment: EventEmitter<PaymentIds> = new EventEmitter();
|
@Output() refreshPayment = new EventEmitter<PaymentIds>();
|
||||||
|
|
||||||
get isHoldShown(): boolean {
|
get isHoldShown(): boolean {
|
||||||
if (isPaymentFlowHold(this.payment.flow)) {
|
if (isPaymentFlowHold(this.payment.flow)) {
|
||||||
@ -31,9 +30,9 @@ export class PaymentDetailsComponent implements OnChanges {
|
|||||||
|
|
||||||
constructor(private invoiceDetails: InvoiceDetailsService) {}
|
constructor(private invoiceDetails: InvoiceDetailsService) {}
|
||||||
|
|
||||||
ngOnChanges(changes: ComponentChanges<PaymentDetailsComponent>): void {
|
ngOnChanges({ payment }: ComponentChanges<PaymentDetailsComponent>): void {
|
||||||
if (isObject(changes.payment)) {
|
if (payment && payment.currentValue) {
|
||||||
this.changeInvoiceID(changes.payment);
|
this.changeInvoiceID(payment.currentValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,10 +40,7 @@ export class PaymentDetailsComponent implements OnChanges {
|
|||||||
this.refreshPayment.emit(ids);
|
this.refreshPayment.emit(ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
private changeInvoiceID({ currentValue: payment }: ComponentChange<PaymentDetailsComponent, 'payment'>): void {
|
private changeInvoiceID(payment: PaymentSearchResult): void {
|
||||||
if (isNil(payment)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.invoiceDetails.setInvoiceID(payment.invoiceID);
|
this.invoiceDetails.setInvoiceID(payment.invoiceID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,23 @@
|
|||||||
<ng-container *transloco="let t; scope: 'payment-section'; read: 'paymentSection.paymentDetails.invoiceDetails'">
|
<ng-container *transloco="let t; scope: 'payment-section'; read: 'paymentSection.paymentDetails.invoiceDetails'">
|
||||||
<div *ngIf="invoice; else loading" fxLayout="column" fxLayoutGap="24px">
|
<div fxLayout="column" fxLayoutGap="24px">
|
||||||
<div class="payment-invoice-info-title mat-title" fxLayout fxLayoutAlign="space-between center">
|
<div class="payment-invoice-info-title mat-title" fxLayout fxLayoutAlign="space-between center">
|
||||||
<div>
|
<div>
|
||||||
{{ t('title') }}
|
{{ t('title') }}
|
||||||
<span dshSecondaryTitle>#{{ invoice.id }}</span>
|
<span *ngIf="invoice">#{{ invoice.id }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<dsh-invoice-details [invoice]="invoice"></dsh-invoice-details>
|
<ng-container *ngIf="!invoice; else details">
|
||||||
|
<div *ngIf="!isLoading; else loading" class="dsh-body-1">
|
||||||
|
{{ t('notFound') }}
|
||||||
</div>
|
</div>
|
||||||
<ng-template #loading>
|
<ng-template #loading>
|
||||||
<div class="payment-invoice-info-title mat-title" fxLayout fxLayoutAlign="space-between center">
|
|
||||||
<div>
|
|
||||||
{{ t('title') }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div *transloco="let c; scope: 'components'; read: 'components.shared'">
|
<div *transloco="let c; scope: 'components'; read: 'components.shared'">
|
||||||
<div class="dsh-body-1">{{ c('loading') }}</div>
|
<div class="dsh-body-1">{{ c('loading') }}</div>
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
<ng-template #details>
|
||||||
|
<dsh-invoice-details [invoice]="invoice"></dsh-invoice-details>
|
||||||
|
</ng-template>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
@ -10,4 +10,8 @@ import { Invoice } from '@vality/swag-payments';
|
|||||||
// TODO: implement dump component for this + shared one for operations
|
// TODO: implement dump component for this + shared one for operations
|
||||||
export class PaymentInvoiceInfoComponent {
|
export class PaymentInvoiceInfoComponent {
|
||||||
@Input() invoice: Invoice;
|
@Input() invoice: Invoice;
|
||||||
|
|
||||||
|
get isLoading() {
|
||||||
|
return this.invoice === undefined;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,6 @@ import { PaymentDetailsComponent } from './payment-details.component';
|
|||||||
import { PaymentInvoiceInfoModule } from './payment-invoice-info';
|
import { PaymentInvoiceInfoModule } from './payment-invoice-info';
|
||||||
import { PaymentMainInfoModule } from './payment-main-info';
|
import { PaymentMainInfoModule } from './payment-main-info';
|
||||||
import { RefundsModule } from './refunds';
|
import { RefundsModule } from './refunds';
|
||||||
import { InvoiceDetailsService } from './services/invoice-details/invoice-details.service';
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
@ -27,6 +26,5 @@ import { InvoiceDetailsService } from './services/invoice-details/invoice-detail
|
|||||||
],
|
],
|
||||||
declarations: [PaymentDetailsComponent],
|
declarations: [PaymentDetailsComponent],
|
||||||
exports: [PaymentDetailsComponent],
|
exports: [PaymentDetailsComponent],
|
||||||
providers: [InvoiceDetailsService],
|
|
||||||
})
|
})
|
||||||
export class PaymentsDetailsModule {}
|
export class PaymentsDetailsModule {}
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
<div fxLayout fxLayoutGap="16px" fxLayoutAlign="space-between">
|
<div fxLayout fxLayoutGap="16px" fxLayoutAlign="space-between">
|
||||||
<div>
|
<div>
|
||||||
{{ t('header') }}
|
{{ t('header') }}
|
||||||
<span dshSecondaryTitle>#{{ item.id }}</span>
|
<span>#{{ item.id }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<dsh-refund-details [refund]="item"></dsh-refund-details>
|
<dsh-refund-details [refund]="item"></dsh-refund-details>
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
import { Inject, Injectable } from '@angular/core';
|
import { Inject, Injectable } from '@angular/core';
|
||||||
import { SearchRefundsRequestParams, RefundSearchResult } from '@vality/swag-anapi-v2';
|
import { SearchRefundsRequestParams, RefundSearchResult } from '@vality/swag-anapi-v2';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable, switchMap } from 'rxjs';
|
||||||
import { shareReplay } from 'rxjs/operators';
|
import { shareReplay, first } from 'rxjs/operators';
|
||||||
|
|
||||||
import { SearchService } from '@dsh/api/anapi';
|
import { SearchService } from '@dsh/api/anapi';
|
||||||
import { SEARCH_LIMIT } from '@dsh/app/sections/tokens';
|
import { SEARCH_LIMIT } from '@dsh/app/sections/tokens';
|
||||||
import { DEBOUNCE_FETCHER_ACTION_TIME, PartialFetcher } from '@dsh/app/shared';
|
import { DEBOUNCE_FETCHER_ACTION_TIME, PartialFetcher } from '@dsh/app/shared';
|
||||||
|
|
||||||
|
import { PaymentInstitutionRealmService } from '../../../../../../../services';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class FetchRefundsService extends PartialFetcher<
|
export class FetchRefundsService extends PartialFetcher<
|
||||||
RefundSearchResult,
|
RefundSearchResult,
|
||||||
@ -20,7 +22,8 @@ export class FetchRefundsService extends PartialFetcher<
|
|||||||
@Inject(SEARCH_LIMIT)
|
@Inject(SEARCH_LIMIT)
|
||||||
private searchLimit: number,
|
private searchLimit: number,
|
||||||
@Inject(DEBOUNCE_FETCHER_ACTION_TIME)
|
@Inject(DEBOUNCE_FETCHER_ACTION_TIME)
|
||||||
debounceActionTime: number
|
debounceActionTime: number,
|
||||||
|
private paymentInstitutionRealmService: PaymentInstitutionRealmService
|
||||||
) {
|
) {
|
||||||
super(debounceActionTime);
|
super(debounceActionTime);
|
||||||
}
|
}
|
||||||
@ -29,13 +32,19 @@ export class FetchRefundsService extends PartialFetcher<
|
|||||||
{ invoiceID, paymentID }: Pick<SearchRefundsRequestParams, 'invoiceID' | 'paymentID'>,
|
{ invoiceID, paymentID }: Pick<SearchRefundsRequestParams, 'invoiceID' | 'paymentID'>,
|
||||||
continuationToken: string
|
continuationToken: string
|
||||||
) {
|
) {
|
||||||
return this.searchService.searchRefunds({
|
return this.paymentInstitutionRealmService.realm$.pipe(
|
||||||
|
first(),
|
||||||
|
switchMap((paymentInstitutionRealm) =>
|
||||||
|
this.searchService.searchRefunds({
|
||||||
fromTime: moment().subtract(3, 'y').startOf('d').utc().format(),
|
fromTime: moment().subtract(3, 'y').startOf('d').utc().format(),
|
||||||
toTime: moment().endOf('d').utc().format(),
|
toTime: moment().endOf('d').utc().format(),
|
||||||
invoiceID,
|
invoiceID,
|
||||||
paymentID,
|
paymentID,
|
||||||
limit: this.searchLimit,
|
limit: this.searchLimit,
|
||||||
continuationToken,
|
continuationToken,
|
||||||
});
|
paymentInstitutionRealm,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,11 +3,13 @@ import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
|
|||||||
import { Invoice } from '@vality/swag-anapi-v2';
|
import { Invoice } from '@vality/swag-anapi-v2';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { Observable, ReplaySubject } from 'rxjs';
|
import { Observable, ReplaySubject } from 'rxjs';
|
||||||
import { distinctUntilChanged, switchMap, tap, pluck } from 'rxjs/operators';
|
import { distinctUntilChanged, switchMap, tap, map, withLatestFrom } from 'rxjs/operators';
|
||||||
|
|
||||||
import { SearchService } from '@dsh/api/anapi';
|
import { SearchService } from '@dsh/api/anapi';
|
||||||
import { ErrorService } from '@dsh/app/shared/services';
|
import { ErrorService } from '@dsh/app/shared/services';
|
||||||
|
|
||||||
|
import { PaymentInstitutionRealmService } from '../../../../../../services';
|
||||||
|
|
||||||
@UntilDestroy()
|
@UntilDestroy()
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class InvoiceDetailsService {
|
export class InvoiceDetailsService {
|
||||||
@ -18,7 +20,11 @@ export class InvoiceDetailsService {
|
|||||||
private invoiceData$ = new ReplaySubject<Invoice | null>(1);
|
private invoiceData$ = new ReplaySubject<Invoice | null>(1);
|
||||||
private innerErrors$ = new ReplaySubject<Error>(1);
|
private innerErrors$ = new ReplaySubject<Error>(1);
|
||||||
|
|
||||||
constructor(private searchService: SearchService, private errorService: ErrorService) {
|
constructor(
|
||||||
|
private searchService: SearchService,
|
||||||
|
private errorService: ErrorService,
|
||||||
|
private paymentInstitutionRealmService: PaymentInstitutionRealmService
|
||||||
|
) {
|
||||||
this.invoice$ = this.invoiceData$.asObservable();
|
this.invoice$ = this.invoiceData$.asObservable();
|
||||||
this.error$ = this.innerErrors$.asObservable();
|
this.error$ = this.innerErrors$.asObservable();
|
||||||
|
|
||||||
@ -36,15 +42,17 @@ export class InvoiceDetailsService {
|
|||||||
tap(() => {
|
tap(() => {
|
||||||
this.resetInvoiceData();
|
this.resetInvoiceData();
|
||||||
}),
|
}),
|
||||||
switchMap((invoiceID: string) => {
|
withLatestFrom(this.paymentInstitutionRealmService.realm$),
|
||||||
|
switchMap(([invoiceID, paymentInstitutionRealm]) => {
|
||||||
return this.searchService.searchInvoices({
|
return this.searchService.searchInvoices({
|
||||||
invoiceID,
|
invoiceID,
|
||||||
fromTime: moment().subtract(3, 'y').startOf('d').utc().format(),
|
fromTime: moment().subtract(3, 'y').startOf('d').utc().format(),
|
||||||
toTime: moment().endOf('d').utc().format(),
|
toTime: moment().endOf('d').utc().format(),
|
||||||
limit: 1,
|
limit: 1,
|
||||||
|
paymentInstitutionRealm,
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
pluck('result', 0),
|
map(({ result }) => result?.[0] ?? null),
|
||||||
untilDestroyed(this)
|
untilDestroyed(this)
|
||||||
)
|
)
|
||||||
.subscribe({
|
.subscribe({
|
||||||
|
@ -5,7 +5,8 @@ import { Observable } from 'rxjs';
|
|||||||
|
|
||||||
import { QueryParamsService } from '@dsh/app/shared/services';
|
import { QueryParamsService } from '@dsh/app/shared/services';
|
||||||
|
|
||||||
import { PaymentInstitutionRealmService, RealmMixService } from '../../services';
|
import { RealmMixService } from '../../services';
|
||||||
|
import { PaymentInstitutionRealmService } from '../../services/payment-institution-realm.service';
|
||||||
import { Filters } from './payments-filters';
|
import { Filters } from './payments-filters';
|
||||||
import { PaymentsExpandedIdManager, FetchPaymentsService } from './services';
|
import { PaymentsExpandedIdManager, FetchPaymentsService } from './services';
|
||||||
import { PaymentSearchFormValue } from './types';
|
import { PaymentSearchFormValue } from './types';
|
||||||
@ -14,7 +15,7 @@ import { PaymentSearchFormValue } from './types';
|
|||||||
@Component({
|
@Component({
|
||||||
selector: 'dsh-payments',
|
selector: 'dsh-payments',
|
||||||
templateUrl: 'payments.component.html',
|
templateUrl: 'payments.component.html',
|
||||||
providers: [FetchPaymentsService, PaymentsExpandedIdManager, RealmMixService],
|
providers: [FetchPaymentsService, PaymentsExpandedIdManager, RealmMixService, PaymentInstitutionRealmService],
|
||||||
})
|
})
|
||||||
export class PaymentsComponent implements OnInit {
|
export class PaymentsComponent implements OnInit {
|
||||||
realm$ = this.paymentInstitutionRealmService.realm$;
|
realm$ = this.paymentInstitutionRealmService.realm$;
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
>
|
>
|
||||||
<div fxLayout fxLayoutGap="16px" fxLayoutAlign="space-between">
|
<div fxLayout fxLayoutGap="16px" fxLayoutAlign="space-between">
|
||||||
<div class="mat-title">
|
<div class="mat-title">
|
||||||
{{ t('title') }} <span dshSecondaryTitle>#{{ (invoice$ | async)?.id }}</span>
|
{{ t('title') }} <span>#{{ (invoice$ | async)?.id }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ng-container *ngIf="isLoading$ | async; else afterLoading">
|
<ng-container *ngIf="isLoading$ | async; else afterLoading">
|
||||||
|
@ -4,12 +4,15 @@ import { PaymentInstitution } from '@vality/swag-payments';
|
|||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { map } from 'rxjs/operators';
|
import { map } from 'rxjs/operators';
|
||||||
|
|
||||||
|
import { shareReplayRefCount } from '@dsh/operators';
|
||||||
|
|
||||||
import RealmEnum = PaymentInstitution.RealmEnum;
|
import RealmEnum = PaymentInstitution.RealmEnum;
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PaymentInstitutionRealmService {
|
export class PaymentInstitutionRealmService {
|
||||||
realm$: Observable<RealmEnum | undefined> = this.route.params.pipe(
|
realm$: Observable<RealmEnum | undefined> = this.route.params.pipe(
|
||||||
map(() => this.route.snapshot.params.realm as RealmEnum)
|
map(({ realm }) => realm as RealmEnum),
|
||||||
|
shareReplayRefCount()
|
||||||
);
|
);
|
||||||
|
|
||||||
constructor(private route: ActivatedRoute) {}
|
constructor(private route: ActivatedRoute) {}
|
||||||
|
@ -212,6 +212,7 @@
|
|||||||
"title": "Платеж совершен с удержанием денежных средств"
|
"title": "Платеж совершен с удержанием денежных средств"
|
||||||
},
|
},
|
||||||
"invoiceDetails": {
|
"invoiceDetails": {
|
||||||
|
"notFound": "Инвойс не найден",
|
||||||
"title": "Инвойс"
|
"title": "Инвойс"
|
||||||
},
|
},
|
||||||
"payerDetails": {
|
"payerDetails": {
|
||||||
|
Loading…
Reference in New Issue
Block a user