IMP-292: Create payment adjustments from CSV (#382)

This commit is contained in:
Rinat Arsaev 2024-09-02 13:43:45 +05:00 committed by GitHub
parent 5e3c5a5932
commit ba1960d746
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 184 additions and 0 deletions

View File

@ -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>

View File

@ -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);
}
}

View File

@ -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]
>;

View File

@ -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,
),
];
}

View File

@ -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"

View File

@ -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, {