mirror of
https://github.com/valitydev/control-center.git
synced 2024-11-06 02:25:17 +00:00
IMP-292: Create payment adjustments from CSV (#382)
This commit is contained in:
parent
5e3c5a5932
commit
ba1960d746
@ -0,0 +1,25 @@
|
||||
<v-dialog [progress]="progress$ | async" title="Create payment adjustments">
|
||||
<cc-upload-csv
|
||||
[(selected)]="selected"
|
||||
[errors]="errors"
|
||||
[formatDescription]="[
|
||||
'scenario.status_change.target_status: pending, processed, captured, cancelled, refunded, failed, charged_back'
|
||||
]"
|
||||
[props]="props"
|
||||
></cc-upload-csv>
|
||||
<v-dialog-actions>
|
||||
@if (successfully?.length) {
|
||||
<button [disabled]="!!(progress$ | async)" mat-button (click)="closeWithSuccess()">
|
||||
Close and find {{ successfully.length }} successful payment adjustments
|
||||
</button>
|
||||
}
|
||||
<button
|
||||
[disabled]="!selected?.length || !!(progress$ | async)"
|
||||
color="primary"
|
||||
mat-button
|
||||
(click)="create()"
|
||||
>
|
||||
Create
|
||||
</button>
|
||||
</v-dialog-actions>
|
||||
</v-dialog>
|
@ -0,0 +1,83 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Component, DestroyRef } from '@angular/core';
|
||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||
import { MatButton } from '@angular/material/button';
|
||||
import { InvoicePaymentAdjustment } from '@vality/domain-proto/internal/domain';
|
||||
import {
|
||||
DialogSuperclass,
|
||||
NotifyLogService,
|
||||
DEFAULT_DIALOG_CONFIG,
|
||||
forkJoinToResult,
|
||||
DialogModule,
|
||||
} from '@vality/ng-core';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
|
||||
import { InvoicingService } from '@cc/app/api/payment-processing';
|
||||
|
||||
import { UploadCsvComponent } from '../../../../../components/upload-csv';
|
||||
|
||||
import { CSV_PAYMENT_ADJUSTMENT_PROPS, CsvPaymentAdjustment } from './types/csv-payment-adjustment';
|
||||
import { getCreatePaymentAdjustmentsArgs } from './utils/get-create-payment-adjustments-args';
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
selector: 'cc-create-payment-adjustments-by-file-dialog',
|
||||
templateUrl: './create-payment-adjustments-by-file-dialog.component.html',
|
||||
imports: [CommonModule, DialogModule, UploadCsvComponent, MatButton],
|
||||
})
|
||||
export class CreatePaymentAdjustmentsByFileDialogComponent extends DialogSuperclass<
|
||||
CreatePaymentAdjustmentsByFileDialogComponent,
|
||||
void,
|
||||
InvoicePaymentAdjustment[]
|
||||
> {
|
||||
static defaultDialogConfig = DEFAULT_DIALOG_CONFIG.large;
|
||||
|
||||
progress$ = new BehaviorSubject(0);
|
||||
selected: CsvPaymentAdjustment[] = [];
|
||||
successfully: InvoicePaymentAdjustment[] = [];
|
||||
props = CSV_PAYMENT_ADJUSTMENT_PROPS;
|
||||
errors?: Map<CsvPaymentAdjustment, unknown>;
|
||||
|
||||
constructor(
|
||||
private invoicingService: InvoicingService,
|
||||
private log: NotifyLogService,
|
||||
private destroyRef: DestroyRef,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
create() {
|
||||
const selected = this.selected;
|
||||
forkJoinToResult(
|
||||
selected.map((c) =>
|
||||
this.invoicingService.CreatePaymentAdjustment(
|
||||
...getCreatePaymentAdjustmentsArgs(c),
|
||||
),
|
||||
),
|
||||
this.progress$,
|
||||
)
|
||||
.pipe(takeUntilDestroyed(this.destroyRef))
|
||||
.subscribe({
|
||||
next: (res) => {
|
||||
this.successfully.push(...res.filter((c) => !c.hasError).map((c) => c.result));
|
||||
const withError = res.filter((c) => c.hasError);
|
||||
if (withError.length) {
|
||||
this.log.error(
|
||||
withError.map((c) => c.error),
|
||||
`Creating ${withError.length} payment adjustments ended in an error. They were re-selected in the table.`,
|
||||
);
|
||||
this.selected = withError.map((c) => selected[c.index]);
|
||||
this.errors = new Map(withError.map((c) => [selected[c.index], c.error]));
|
||||
} else {
|
||||
this.log.successOperation('create', 'payment adjustments');
|
||||
this.closeWithSuccess();
|
||||
}
|
||||
},
|
||||
error: (err) => this.log.error(err),
|
||||
});
|
||||
}
|
||||
|
||||
override closeWithSuccess() {
|
||||
super.closeWithSuccess(this.successfully);
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
import { DeepReadonly } from 'utility-types';
|
||||
|
||||
import { CsvProps, CsvObject } from '../../../../../../components/upload-csv';
|
||||
|
||||
export const CSV_PAYMENT_ADJUSTMENT_PROPS = {
|
||||
required: ['invoice_id', 'payment_id', 'reason'],
|
||||
optional: [
|
||||
'scenario.cash_flow.domain_revision',
|
||||
'scenario.cash_flow.new_amount',
|
||||
'scenario.status_change.target_status',
|
||||
'scenario.status_change.target_status.data',
|
||||
],
|
||||
} as const satisfies DeepReadonly<CsvProps>;
|
||||
|
||||
export type CsvPaymentAdjustment = CsvObject<
|
||||
(typeof CSV_PAYMENT_ADJUSTMENT_PROPS)['required'][number],
|
||||
(typeof CSV_PAYMENT_ADJUSTMENT_PROPS)['optional'][number]
|
||||
>;
|
@ -0,0 +1,47 @@
|
||||
import { CodegenClient } from '@vality/domain-proto/internal/payment_processing-Invoicing';
|
||||
import { clean } from '@vality/ng-core';
|
||||
|
||||
import { CsvPaymentAdjustment } from '../types/csv-payment-adjustment';
|
||||
|
||||
export function getCreatePaymentAdjustmentsArgs(
|
||||
c: CsvPaymentAdjustment,
|
||||
): Parameters<CodegenClient['CreatePaymentAdjustment']> {
|
||||
return [
|
||||
c.invoice_id,
|
||||
c.payment_id,
|
||||
clean(
|
||||
{
|
||||
reason: c.reason,
|
||||
scenario: clean(
|
||||
{
|
||||
status_change: c['scenario.status_change.target_status']
|
||||
? {
|
||||
target_status: {
|
||||
[c['scenario.status_change.target_status']]: c[
|
||||
'scenario.status_change.target_status.data'
|
||||
]
|
||||
? JSON.parse(
|
||||
c['scenario.status_change.target_status.data'],
|
||||
)
|
||||
: {},
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
cash_flow: clean({
|
||||
domain_revision: c['scenario.cash_flow.domain_revision']
|
||||
? Number(c['scenario.cash_flow.domain_revision'])
|
||||
: undefined,
|
||||
new_amount: c['scenario.cash_flow.new_amount']
|
||||
? Number(c['scenario.cash_flow.new_amount'])
|
||||
: undefined,
|
||||
}),
|
||||
},
|
||||
false,
|
||||
true,
|
||||
),
|
||||
},
|
||||
false,
|
||||
true,
|
||||
),
|
||||
];
|
||||
}
|
@ -53,6 +53,9 @@
|
||||
>
|
||||
Fail machines
|
||||
</button>
|
||||
<button mat-raised-button (click)="createPaymentAdjustments()">
|
||||
Create adjustments by file
|
||||
</button>
|
||||
<button
|
||||
[disabled]="!(selected$ | async)?.length"
|
||||
color="primary"
|
||||
|
@ -28,6 +28,7 @@ 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 { CreatePaymentAdjustmentsByFileDialogComponent } from './components/create-payment-adjustments-by-file-dialog/create-payment-adjustments-by-file-dialog.component';
|
||||
import { FetchPaymentsService } from './services/fetch-payments.service';
|
||||
|
||||
interface Filters {
|
||||
@ -166,6 +167,13 @@ export class PaymentsComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
createPaymentAdjustments() {
|
||||
this.dialogService
|
||||
.open(CreatePaymentAdjustmentsByFileDialogComponent)
|
||||
.afterClosed()
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
failMachines() {
|
||||
this.dialogService
|
||||
.open(FailMachinesDialogComponent, {
|
||||
|
Loading…
Reference in New Issue
Block a user