TD-37: Remove PAPI, old payouts and claims modules (#73)

This commit is contained in:
Rinat Arsaev 2022-03-29 15:08:23 +03:00 committed by GitHub
parent 20b80c0314
commit 247e79f30e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
123 changed files with 126 additions and 2847 deletions

View File

@ -15,8 +15,8 @@ jobs:
with:
path: ./*
key: ${{ github.sha }}
check:
name: Check
eslint:
name: ESLint
runs-on: ubuntu-latest
needs: [init]
steps:
@ -27,7 +27,20 @@ jobs:
path: ./*
key: ${{ github.sha }}
- name: Check
run: npm run ci:check
run: npm run lint-cmd -- --quiet
prettier:
name: Prettier
runs-on: ubuntu-latest
needs: [init]
steps:
- name: Cache all
uses: actions/cache@v3
id: cache
with:
path: ./*
key: ${{ github.sha }}
- name: Check
run: npm run prettier
build:
name: Build
runs-on: ubuntu-latest

View File

@ -7,7 +7,7 @@
"start": "ng serve --proxy-config proxy.conf.js --port 4200",
"build": "ng build --extra-webpack-config webpack.extra.js",
"test": "ng test",
"lint-cmd": "eslint \"src/**/*.{ts,js,html}\" --max-warnings 1552",
"lint-cmd": "eslint \"src/**/*.{ts,js,html}\" --max-warnings 1385",
"lint-cache-cmd": "npm run lint-cmd -- --cache",
"lint": "npm run lint-cache-cmd",
"lint-fix": "npm run lint-cache-cmd -- --fix",
@ -16,7 +16,6 @@
"prettier": "npm run prettier-preset -- --list-different",
"prettier:fix": "npm run prettier-preset -- --write",
"fix": "npm run lint-fix; npm run prettier:fix",
"ci:check": "npm run parallel-cmd -- --names PRET,LINT \"npm run prettier\" \"npm run lint-cmd -- --quiet\"",
"parallel-cmd": "concurrently --kill-others-on-fail --prefix-colors magenta,green,red,yellow,blue"
},
"dependencies": {

View File

@ -4,7 +4,6 @@ const THRIFT_PROXY_CONFIG = {
'/v3',
'/stat',
'/fistful',
'/papi',
'/file_storage',
'/deanonimus',
'/payout/management',

View File

@ -0,0 +1,22 @@
import { Injectable, Injector } from '@angular/core';
import {
codegenClientConfig,
CodegenClient,
} from '@vality/domain-proto/lib/claim_management-ClaimManagement';
import context from '@vality/domain-proto/lib/claim_management/context';
import * as service from '@vality/domain-proto/lib/claim_management/gen-nodejs/ClaimManagement';
import { createThriftApi } from '@cc/app/api/utils';
@Injectable({ providedIn: 'root' })
export class ClaimManagementService extends createThriftApi<CodegenClient>() {
constructor(injector: Injector) {
super(injector, {
service,
endpoint: '/v1/cm',
metadata: () => import('@vality/domain-proto/lib/metadata.json').then((m) => m.default),
context,
...codegenClientConfig,
});
}
}

View File

@ -0,0 +1,2 @@
export * from './claim-management.service';
export * from './types';

View File

@ -0,0 +1 @@
export * from './claim-status';

View File

@ -5,16 +5,10 @@ import { AppAuthGuardService } from '@cc/app/shared/services';
@NgModule({
imports: [
RouterModule.forRoot(
[
{
path: '',
redirectTo: '/old-payouts',
pathMatch: 'full',
},
],
{ paramsInheritanceStrategy: 'always', relativeLinkResolution: 'legacy' }
),
RouterModule.forRoot([], {
paramsInheritanceStrategy: 'always',
relativeLinkResolution: 'legacy',
}),
],
providers: [AppAuthGuardService],
exports: [RouterModule],

View File

@ -40,7 +40,7 @@ export class AppComponent implements OnInit {
private getMenuItems() {
const menuItems = [
{ name: 'Domain config', route: '/domain', activateRoles: [DomainConfigRole.Checkout] },
{ name: 'Payouts', route: '/old-payouts', activateRoles: [PayoutRole.Read] },
{ name: 'Payouts', route: '/payouts', activateRoles: [PayoutRole.Read] },
{ name: 'Claims', route: '/claims', activateRoles: [ClaimManagementRole.GetClaims] },
{
name: 'Payment adjustment',

View File

@ -21,12 +21,10 @@ import {
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { ClaimModule } from './claim/claim.module';
import { CoreModule } from './core/core.module';
import { DomainModule } from './domain';
import icons from './icons.json';
import { NotFoundModule } from './not-found';
import { PayoutsModule as OldPayoutsModule } from './payouts/payouts.module';
import { RepairingModule } from './repairing/repairing.module';
import { DomainConfigModule } from './sections/domain-config';
import { OperationsModule } from './sections/operations/operations.module';
@ -66,8 +64,6 @@ moment.locale('en');
MatMenuModule,
MatSidenavModule,
MatListModule,
ClaimModule,
OldPayoutsModule,
PaymentAdjustmentModule,
DomainModule,
RepairingModule,

View File

@ -1,6 +0,0 @@
<div class="mat-dialog-title">Confirm claim accepting</div>
<mat-dialog-actions>
<button mat-button [disabled]="isLoading" color="primary" (click)="accept()">CONFIRM</button>
<button [disabled]="isLoading" mat-button [mat-dialog-close]="false">CANCEL</button>
<mat-progress-bar *ngIf="isLoading" mode="indeterminate"></mat-progress-bar>
</mat-dialog-actions>

View File

@ -1,33 +0,0 @@
import { Component } from '@angular/core';
import { MatDialogRef } from '@angular/material/dialog';
import { MatSnackBar } from '@angular/material/snack-bar';
import { ClaimService } from '../claim.service';
@Component({
templateUrl: 'accept-claim.component.html',
})
export class AcceptClaimComponent {
isLoading = false;
constructor(
private dialogRef: MatDialogRef<AcceptClaimComponent>,
private claimService: ClaimService,
private snackBar: MatSnackBar
) {}
accept() {
this.isLoading = true;
this.claimService.acceptClaim().subscribe(
() => {
this.isLoading = false;
this.dialogRef.close();
this.snackBar.open('Claim accepted', 'OK', { duration: 3000 });
},
() => {
this.isLoading = false;
this.snackBar.open('An error occurred while claim accepting', 'OK');
}
);
}
}

View File

@ -1,4 +0,0 @@
export enum ClaimActionType {
Create = 'create',
Edit = 'edit',
}

View File

@ -1,44 +0,0 @@
<div *ngIf="claimInfoContainer.type === t.Create" fxLayout="row" fxLayout.xs="column">
<label fxFlex="20">Party ID:</label>
<div>{{ claimInfoContainer?.partyId }}</div>
</div>
<div
*ngIf="claimInfoContainer.type === t.Edit"
fxLayout="row"
fxLayoutGap="10px"
fxLayout.sm="column"
fxLayout.xs="column"
>
<div fxFlex="50" fxLayout="column" fxLayoutGap="10px">
<div fxLayout="row" fxLayout.xs="column">
<label fxFlex="20">Claim ID:</label>
<div>{{ claimInfoContainer?.claimId }}</div>
</div>
<div fxLayout="row" fxLayout.xs="column">
<label fxFlex="20">Status:</label>
<div>{{ claimInfoContainer?.status }}</div>
</div>
<div fxLayout="row" fxLayout.xs="column">
<label fxFlex="20">Revision:</label>
<div>{{ claimInfoContainer?.revision }}</div>
</div>
<div fxLayout="row" fxLayout.xs="column" *ngIf="claimInfoContainer?.reason">
<label fxFlex="20">Reason:</label>
<div>{{ claimInfoContainer.reason }}</div>
</div>
</div>
<div fxFlex="50" fxLayout="column" fxLayoutGap="10px">
<div fxLayout="row" fxLayout.xs="column">
<label fxFlex="20">Party ID:</label>
<div>{{ claimInfoContainer?.partyId }}</div>
</div>
<div fxLayout="row" fxLayout.xs="column">
<label fxFlex="20">Created At:</label>
<div>{{ claimInfoContainer?.createdAt | date: 'dd.MM.yyyy HH:mm:ss' }}</div>
</div>
<div fxLayout="row" fxLayout.xs="column">
<label fxFlex="20">Updated At:</label>
<div>{{ claimInfoContainer?.updatedAt | date: 'dd.MM.yyyy HH:mm:ss' }}</div>
</div>
</div>
</div>

View File

@ -1,15 +0,0 @@
import { Component, Input } from '@angular/core';
import { ClaimActionType } from '../../claim-action-type';
import { ClaimInfoContainer } from '../../model';
@Component({
selector: 'cc-claim-info-details',
templateUrl: 'claim-info-details.component.html',
})
export class ClaimInfoDetailsComponent {
@Input()
claimInfoContainer: ClaimInfoContainer;
t = ClaimActionType;
}

View File

@ -1,25 +0,0 @@
import { TestBed, waitForAsync } from '@angular/core/testing';
import { ClaimInfoDetailsComponent } from './claim-info-details.component';
describe('ClaimInfoDetailsComponent', () => {
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [ClaimInfoDetailsComponent],
})
.compileComponents()
.then();
}));
it('should create component', waitForAsync(() => {
const fixture = TestBed.createComponent(ClaimInfoDetailsComponent);
const component = fixture.debugElement.componentInstance;
expect(component).toBeTruthy();
}));
it('should render reason', waitForAsync(() => {
const fixture = TestBed.createComponent(ClaimInfoDetailsComponent);
const rendered = fixture.debugElement.componentInstance;
expect(rendered).toBeTruthy();
}));
});

View File

@ -1,64 +0,0 @@
<mat-card *ngIf="claimInfoContainer">
<mat-card-subtitle> Claim details</mat-card-subtitle>
<mat-card-content>
<cc-claim-info-details [claimInfoContainer]="claimInfoContainer"></cc-claim-info-details>
</mat-card-content>
<mat-card-actions fxLayout="row" fxLayoutAlign="space-between stretch">
<div fxLayout="row" fxLayout.xs="column">
<button
mat-button
[disabled]="
(claimService.isLoading | async) ||
!(claimService.isAddModificationAvailable | async)
"
(click)="add()"
>
ADD MODIFICATION
</button>
<button
mat-button
[disabled]="!hasUnsavedChanges() || (claimService.isLoading | async)"
(click)="save()"
>
SAVE MODIFICATIONS
</button>
</div>
<div fxLayout="row" fxLayout.xs="column">
<button
mat-button
color="primary"
[disabled]="hasUnsavedChanges() || (claimService.isLoading | async)"
(click)="cloneClaim()"
>
CLONE CLAIM
</button>
<button
mat-button
color="primary"
[disabled]="
hasUnsavedChanges() ||
claimInfoContainer?.status !== 'pending' ||
(claimService.isLoading | async)
"
(click)="accept()"
>
ACCEPT CLAIM
</button>
<button
mat-button
color="warn"
[disabled]="
hasUnsavedChanges() ||
claimInfoContainer?.status !== 'pending' ||
(claimService.isLoading | async)
"
(click)="deny()"
>
DENY CLAIM
</button>
</div>
</mat-card-actions>
<mat-card-footer *ngIf="claimService.isLoading | async">
<mat-progress-bar mode="indeterminate"></mat-progress-bar>
</mat-card-footer>
</mat-card>

View File

@ -1,101 +0,0 @@
import { Component, OnInit } from '@angular/core';
import { MatBottomSheet } from '@angular/material/bottom-sheet';
import { MatDialog } from '@angular/material/dialog';
import { MatSnackBar } from '@angular/material/snack-bar';
import { Router } from '@angular/router';
import { filter } from 'rxjs/operators';
import { UnitActionsNavListComponent } from '../../party-modification-creator-legacy';
import { AcceptClaimComponent } from '../accept-claim/accept-claim.component';
import { ClaimActionType } from '../claim-action-type';
import { ClaimService } from '../claim.service';
import { CloneClaimComponent } from '../clone-claim/clone-claim.component';
import { DenyClaimComponent } from '../deny-claim/deny-claim.component';
import { ClaimInfoContainer } from '../model';
@Component({
selector: 'cc-claim-info',
templateUrl: 'claim-info.component.html',
})
export class ClaimInfoComponent implements OnInit {
claimInfoContainer: ClaimInfoContainer;
partyID: string;
claimID: number;
constructor(
private router: Router,
public claimService: ClaimService,
private bottomSheet: MatBottomSheet,
private snackBar: MatSnackBar,
private dialog: MatDialog
) {}
ngOnInit() {
this.claimService.claimInfoContainer$
.pipe(filter((container) => container !== null))
.subscribe((container) => {
this.claimInfoContainer = container;
this.partyID = container.partyId;
this.claimID = container.claimId;
});
}
hasUnsavedChanges() {
return this.claimService.hasUnsavedChanges();
}
save() {
switch (this.claimInfoContainer.type) {
case ClaimActionType.Edit:
this.claimService.saveChanges().subscribe(
() => this.success(),
(e) => this.failed(e)
);
break;
case ClaimActionType.Create:
this.claimService.createClaim().subscribe(
(claimInfo) => {
const editEndpoint = `/claims/${claimInfo.party_id}/${ClaimActionType.Edit}/${claimInfo.claim_id}`;
this.router.navigate([editEndpoint]).then(() => this.success());
},
(e) => this.failed(e)
);
break;
}
}
add() {
this.bottomSheet.open(UnitActionsNavListComponent, {
data: { type: 'allActions', partyID: this.partyID },
});
}
cloneClaim() {
this.dialog.open(CloneClaimComponent, {
disableClose: true,
data: { partyID: this.partyID, claimID: this.claimID },
});
}
accept() {
this.dialog.open(AcceptClaimComponent, {
disableClose: true,
});
}
deny() {
this.dialog.open(DenyClaimComponent, {
disableClose: true,
width: '30vw',
});
}
private success() {
this.snackBar.open('Changes saved', 'OK', { duration: 3000 });
}
private failed(error) {
console.error(error);
this.snackBar.open('An error occurred', 'OK');
}
}

View File

@ -1,31 +0,0 @@
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { AppAuthGuardService, ClaimManagementRole } from '@cc/app/shared/services';
import { ClaimComponent } from './claim.component';
@NgModule({
imports: [
RouterModule.forChild([
{
path: 'claims/:party_id/:action/:claim_id',
component: ClaimComponent,
canActivate: [AppAuthGuardService],
data: {
roles: [ClaimManagementRole.GetClaims],
},
},
{
path: 'claims/:party_id/:action',
component: ClaimComponent,
canActivate: [AppAuthGuardService],
data: {
roles: [ClaimManagementRole.GetClaims],
},
},
]),
],
exports: [RouterModule],
})
export class ClaimRoutingModule {}

View File

@ -1,4 +0,0 @@
<cc-card-container>
<cc-claim-info></cc-claim-info>
<cc-party-modifications></cc-party-modifications>
</cc-card-container>

View File

@ -1,31 +0,0 @@
import { Component } from '@angular/core';
import { MatSnackBar } from '@angular/material/snack-bar';
import { ActivatedRoute } from '@angular/router';
import { switchMap } from 'rxjs/operators';
import { ClaimService } from './claim.service';
@Component({
templateUrl: 'claim.component.html',
styleUrls: [],
})
export class ClaimComponent {
constructor(
private route: ActivatedRoute,
private claimService: ClaimService,
private snackBar: MatSnackBar
) {
this.route.params
.pipe(
switchMap(({ action, party_id, claim_id }) =>
this.claimService.resolveClaimInfo(action, party_id, claim_id)
)
)
.subscribe({
error: (error) => {
console.error(error);
this.snackBar.open('An error occurred while claim resolving', 'OK');
},
});
}
}

View File

@ -1,99 +0,0 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FlexLayoutModule } from '@angular/flex-layout';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatBottomSheetModule } from '@angular/material/bottom-sheet';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { MatNativeDateModule } from '@angular/material/core';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatDialogModule } from '@angular/material/dialog';
import { MatDividerModule } from '@angular/material/divider';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatListModule } from '@angular/material/list';
import { MatProgressBarModule } from '@angular/material/progress-bar';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatRadioModule } from '@angular/material/radio';
import { MatSelectModule } from '@angular/material/select';
import { MatSnackBarModule } from '@angular/material/snack-bar';
import { MatStepperModule } from '@angular/material/stepper';
import { MatTabsModule } from '@angular/material/tabs';
import { CardContainerModule } from '@cc/components/card-container/card-container.module';
import { PrettyJsonModule } from '@cc/components/pretty-json';
import { PapiModule } from '../papi/papi.module';
import {
ModificationNameModule,
PartyModificationCreatorLegacyModule,
} from '../party-modification-creator-legacy';
import { DamselModule } from '../thrift-services/damsel';
import { AcceptClaimComponent } from './accept-claim/accept-claim.component';
import { ClaimInfoDetailsComponent } from './claim-info/claim-info-details/claim-info-details.component';
import { ClaimInfoComponent } from './claim-info/claim-info.component';
import { ClaimRoutingModule } from './claim-routing.module';
import { ClaimComponent } from './claim.component';
import { ClaimService } from './claim.service';
import { CloneClaimComponent } from './clone-claim/clone-claim.component';
import { DenyClaimComponent } from './deny-claim/deny-claim.component';
import { PartyModificationContainerComponent } from './party-modification-container/party-modification-container.component';
import { PartyModificationContainerService } from './party-modification-container/party-modification-container.service';
import { RemoveConfirmComponent } from './party-modification-container/remove-confirm/remove-confirm.component';
import { PartyModificationUnitsComponent } from './party-modification-units/party-modification-units.component';
import { PartyModificationsComponent } from './party-modifications/party-modifications.component';
import { PersistentContainerService } from './persistent-container.service';
@NgModule({
imports: [
PapiModule,
DamselModule,
CommonModule,
ReactiveFormsModule,
ClaimRoutingModule,
FlexLayoutModule,
MatCardModule,
MatButtonModule,
MatTabsModule,
MatBottomSheetModule,
MatListModule,
MatIconModule,
MatDialogModule,
MatInputModule,
MatDatepickerModule,
MatNativeDateModule,
MatDividerModule,
MatSelectModule,
MatProgressBarModule,
MatProgressSpinnerModule,
MatSnackBarModule,
MatRadioModule,
MatStepperModule,
FormsModule,
ReactiveFormsModule,
PartyModificationCreatorLegacyModule,
ModificationNameModule,
PrettyJsonModule,
CardContainerModule,
],
declarations: [
ClaimComponent,
ClaimInfoComponent,
ClaimInfoDetailsComponent,
PartyModificationsComponent,
PartyModificationContainerComponent,
CloneClaimComponent,
AcceptClaimComponent,
DenyClaimComponent,
PartyModificationUnitsComponent,
RemoveConfirmComponent,
],
entryComponents: [
CloneClaimComponent,
AcceptClaimComponent,
DenyClaimComponent,
RemoveConfirmComponent,
],
providers: [ClaimService, PersistentContainerService, PartyModificationContainerService],
})
export class ClaimModule {}

View File

@ -1,275 +0,0 @@
import { Injectable } from '@angular/core';
import { PartyModification } from '@vality/domain-proto/lib/claim_management';
import isEqual from 'lodash-es/isEqual';
import toNumber from 'lodash-es/toNumber';
import { BehaviorSubject, Observable, of, Subject, throwError } from 'rxjs';
import { delay, map, repeatWhen, retryWhen, switchMap, takeWhile, tap } from 'rxjs/operators';
import { ClaimService as ClaimPapi } from '../papi/claim.service';
import { ClaimInfo, ClaimStatus, PartyModificationUnit } from '../papi/model';
import { PartyModificationEmitter } from '../party-modification-creator-legacy';
import { ClaimActionType } from './claim-action-type';
import { ClaimInfoContainer, ModificationGroup, PersistentContainer } from './model';
import { convert } from './party-modification-group-converter';
import { PersistentContainerService } from './persistent-container.service';
@Injectable()
export class ClaimService {
claimInfoContainer$: Subject<ClaimInfoContainer> = new BehaviorSubject(null);
modificationGroups$: Subject<ModificationGroup[]> = new Subject();
private isLoading$: Subject<boolean> = new BehaviorSubject(false);
private isAddModificationAvailable$: Subject<boolean> = new BehaviorSubject(false);
private claimInfoContainer: ClaimInfoContainer;
private containers: PersistentContainer[];
get isLoading(): Observable<boolean> {
return this.isLoading$;
}
get isAddModificationAvailable(): Observable<boolean> {
return this.isAddModificationAvailable$;
}
constructor(
private papiClaimService: ClaimPapi,
private persistentContainerService: PersistentContainerService,
private partyModificationEmitter: PartyModificationEmitter
) {
this.persistentContainerService.containers$.subscribe((containers) => {
this.containers = containers;
this.modificationGroups$.next(convert(containers));
});
this.partyModificationEmitter.modification$.subscribe((modification) =>
this.persistentContainerService.add(modification)
);
}
resolveClaimInfo(type: ClaimActionType, partyId: string, claimId?: string): Observable<void> {
switch (type) {
case ClaimActionType.Create:
if (claimId) {
return this.getClaimInfo(partyId, claimId).pipe(
tap((claimInfo) => {
this.persistentContainerService.init(
claimInfo.modifications.modifications,
false
);
this.claimInfoContainer = { type, partyId };
this.claimInfoContainer$.next(this.claimInfoContainer);
}),
map(() => null),
tap(() => this.isAddModificationAvailable$.next(this.getAvailability()))
);
} else {
this.persistentContainerService.init([]);
this.claimInfoContainer = { type, partyId };
this.claimInfoContainer$.next(this.claimInfoContainer);
this.isAddModificationAvailable$.next(this.getAvailability());
return of();
}
case ClaimActionType.Edit:
return this.getClaimInfo(partyId, claimId).pipe(
tap((claimInfo) => {
this.persistentContainerService.init(claimInfo.modifications.modifications);
this.claimInfoContainer = this.toClaimInfoContainer(claimInfo);
this.claimInfoContainer$.next(this.claimInfoContainer);
}),
map(() => null),
tap(() => this.isAddModificationAvailable$.next(this.getAvailability()))
);
}
return throwError('Unsupported claim action type');
}
removeModification(typeHash: string) {
this.persistentContainerService.remove(typeHash);
}
saveChanges(): Observable<void> {
this.isLoading$.next(true);
const { partyId, claimId } = this.claimInfoContainer;
const units = this.toModificationUnits(this.containers);
return this.papiClaimService.getClaim(partyId, claimId).pipe(
switchMap((claimInfo) =>
this.papiClaimService
.updateClaim(partyId, claimId, claimInfo.revision, units)
.pipe(map(() => claimInfo.revision))
),
switchMap((revision) => this.pollClaimChange(revision)),
tap(() => this.isLoading$.next(false))
);
}
createClaim(): Observable<ClaimInfo> {
this.isLoading$.next(true);
const units = this.toModificationUnits(this.containers);
return this.papiClaimService.createClaim(this.claimInfoContainer.partyId, units).pipe(
switchMap((createdClaim) =>
this.pollClaimCreated(this.claimInfoContainer.partyId, createdClaim.claimId)
),
tap(() => this.isLoading$.next(false))
);
}
acceptClaim(): Observable<void> {
const { claimId, partyId } = this.claimInfoContainer;
return this.papiClaimService.getClaim(partyId, claimId).pipe(
switchMap((claimInfo) =>
this.papiClaimService
.acceptClaim({
partyId,
claimId,
revision: claimInfo.revision,
})
.pipe(map(() => claimInfo.revision))
),
switchMap((revision) => this.pollClaimChange(revision)),
tap(() => this.isLoading$.next(false))
);
}
denyClaim(reason: string): Observable<void> {
const { claimId, partyId } = this.claimInfoContainer;
return this.papiClaimService.getClaim(partyId, claimId).pipe(
switchMap((claimInfo) =>
this.papiClaimService
.denyClaim({
claimId,
partyId,
revision: claimInfo.revision,
reason,
})
.pipe(map(() => claimInfo.revision))
),
switchMap((revision) => this.pollClaimChange(revision)),
tap(() => this.isLoading$.next(false))
);
}
hasUnsavedChanges(): boolean {
return this.containers ? this.containers.filter((i) => !i.saved).length > 0 : false;
}
private getAvailability(): boolean {
switch (this.claimInfoContainer.type) {
case ClaimActionType.Edit:
return this.claimInfoContainer.status === ClaimStatus.Pending;
case ClaimActionType.Create:
return true;
}
return false;
}
private toModificationUnits(containers: PersistentContainer[]): PartyModificationUnit {
return {
modifications: containers.reduce(
(acc, { saved, modification }) => (saved ? acc : acc.concat(modification)),
[]
),
};
}
private toClaimInfoContainer(claimInfo: ClaimInfo): ClaimInfoContainer {
const modifications = claimInfo.modifications.modifications;
const { claim_id, party_id, revision, status, reason, created_at, updated_at } = claimInfo;
const extractedIds = this.extractIds(modifications);
return {
type: ClaimActionType.Edit,
claimId: claim_id,
partyId: party_id,
revision,
status,
reason,
createdAt: created_at,
updatedAt: updated_at,
extractedIds,
};
}
private extractIds(modifications: PartyModification[]): { shopId: string; contractId: string } {
return modifications.reduce(
(prev, current) => {
if (!prev.shopId && current.shop_modification) {
const shopId = current.shop_modification.id;
return { ...prev, shopId };
} else if (!prev.contractId && current.contract_modification) {
const contractId = current.contract_modification.id;
return { ...prev, contractId };
} else {
return prev;
}
},
{ shopId: null, contractId: null }
);
}
private getClaimInfo(partyId: string, claimId: string): Observable<ClaimInfo> {
return this.papiClaimService.getClaim(partyId, toNumber(claimId));
}
private pollClaimChange(revision: string, delayMs = 2000, retryCount = 15): Observable<void> {
const container = this.claimInfoContainer;
const { partyId, claimId, status } = container;
const currentPair = { status, revision };
let newPair = {};
return new Observable((observer) => {
this.papiClaimService
.getClaim(partyId, claimId)
.pipe(
repeatWhen((notifications) =>
notifications.pipe(
delay(delayMs),
takeWhile(
(_, retries) =>
isEqual(newPair, currentPair) && retries <= retryCount
)
)
)
)
.subscribe((claimInfo) => {
newPair = {
status: claimInfo.status,
revision: claimInfo.revision,
};
if (!isEqual(newPair, currentPair)) {
this.claimInfoContainer = this.toClaimInfoContainer(claimInfo);
this.claimInfoContainer$.next(this.claimInfoContainer);
this.persistentContainerService.init(claimInfo.modifications.modifications);
observer.next();
observer.complete();
}
});
});
}
private pollClaimCreated(
partyId: string,
claimId: number,
delayMs = 2000,
retryCount = 15
): Observable<ClaimInfo> {
return new Observable<ClaimInfo>((observer) => {
this.papiClaimService
.getClaim(partyId, claimId)
.pipe(
retryWhen((err) =>
err.pipe(
delay(delayMs),
takeWhile(
(error, retries) => error.status === 404 && retries <= retryCount
)
)
)
)
.subscribe((claimInfo) => {
observer.next(claimInfo);
observer.complete();
});
});
}
}

View File

@ -1,5 +0,0 @@
<div class="mat-dialog-title">Confirm claim cloning</div>
<mat-dialog-actions>
<button mat-button color="primary" (click)="cloneClaim()">CONFIRM</button>
<button mat-button [mat-dialog-close]="false">CANCEL</button>
</mat-dialog-actions>

View File

@ -1,28 +0,0 @@
import { Component, Inject } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { Router } from '@angular/router';
import { ClaimActionType } from '../claim-action-type';
interface RouteData {
partyID: string;
claimID: string;
}
@Component({
templateUrl: 'clone-claim.component.html',
})
export class CloneClaimComponent {
constructor(
private dialogRef: MatDialogRef<CloneClaimComponent>,
private router: Router,
@Inject(MAT_DIALOG_DATA) private data: RouteData
) {}
cloneClaim() {
this.dialogRef.close();
this.router.navigate([
`/claims/${this.data.partyID}/${ClaimActionType.Create}/${this.data.claimID}`,
]);
}
}

View File

@ -1,13 +0,0 @@
<div class="mat-dialog-title">Confirm claim denying</div>
<mat-dialog-content>
<form fxLayout="row">
<mat-form-field fxFlex class="example-full-width">
<textarea matInput placeholder="Reason" [(ngModel)]="reason" name="reason"> </textarea>
</mat-form-field>
</form>
</mat-dialog-content>
<mat-dialog-actions>
<button mat-button [disabled]="isLoading" color="primary" (click)="deny()">CONFIRM</button>
<button [disabled]="isLoading" mat-button [mat-dialog-close]="false">CANCEL</button>
<mat-progress-bar *ngIf="isLoading" mode="indeterminate"></mat-progress-bar>
</mat-dialog-actions>

View File

@ -1,35 +0,0 @@
import { Component } from '@angular/core';
import { MatDialogRef } from '@angular/material/dialog';
import { MatSnackBar } from '@angular/material/snack-bar';
import { ClaimService } from '../claim.service';
@Component({
templateUrl: 'deny-claim.component.html',
})
export class DenyClaimComponent {
isLoading = false;
reason = '';
constructor(
private dialogRef: MatDialogRef<DenyClaimComponent>,
private claimService: ClaimService,
private snackBar: MatSnackBar
) {}
deny() {
this.isLoading = true;
this.claimService.denyClaim(this.reason).subscribe(
() => {
this.isLoading = false;
this.dialogRef.close();
this.snackBar.open('Claim denied', 'OK', { duration: 3000 });
},
() => {
this.isLoading = false;
this.snackBar.open('An error occurred while claim denying', 'OK');
}
);
}
}

View File

@ -1,17 +0,0 @@
import { ClaimStatus } from '../../papi/model';
import { ClaimActionType } from '../claim-action-type';
export class ClaimInfoContainer {
type: ClaimActionType;
status?: ClaimStatus;
partyId?: string;
claimId?: number;
revision?: string;
createdAt?: string;
updatedAt?: string;
reason?: string;
extractedIds?: {
shopId: string;
contractId: string;
};
}

View File

@ -1,7 +0,0 @@
export * from './claim-info-container';
export * from './party-modification-container';
export * from './party-modification-unit';
export * from './modification-group';
export * from './modification-group-type';
export * from './modification-unit-container';
export * from './persistent-container';

View File

@ -1,6 +0,0 @@
export enum ModificationGroupType {
ShopUnitContainer = 'ShopUnitContainer',
ContractUnitContainer = 'ContractUnitContainer',
ContractorUnitContainer = 'ContractorUnitContainer',
Unknown = 'unknown',
}

View File

@ -1,7 +0,0 @@
import { ModificationGroupType } from './modification-group-type';
import { PartyModificationUnit } from './party-modification-unit';
export class ModificationGroup {
type: ModificationGroupType;
units?: PartyModificationUnit[];
}

View File

@ -1,10 +0,0 @@
import {
ContractModificationUnit,
ShopModificationUnit,
} from '@vality/domain-proto/lib/payment_processing';
export class ModificationUnitContainer {
saved: boolean;
typeHash?: string;
modificationUnit: ContractModificationUnit | ShopModificationUnit;
}

View File

@ -1,10 +0,0 @@
import {
ContractModificationName,
ShopModificationName,
} from '../../party-modification-creator-legacy';
import { ModificationUnitContainer } from './modification-unit-container';
export class PartyModificationContainer {
name: ContractModificationName | ShopModificationName;
unitContainers: ModificationUnitContainer[];
}

View File

@ -1,7 +0,0 @@
import { PartyModificationContainer } from './party-modification-container';
export class PartyModificationUnit {
unitID: string;
hasUnsaved: boolean;
containers: PartyModificationContainer[];
}

View File

@ -1,7 +0,0 @@
import { PartyModification } from '@vality/domain-proto/lib/claim_management';
export class PersistentContainer {
modification: PartyModification;
saved: boolean;
typeHash?: string;
}

View File

@ -1,16 +0,0 @@
import {
ContractModificationName,
ContractorModificationName,
ShopModificationName,
} from '../party-modification-creator-legacy';
export enum ActionType {
ContractAction = 'contractAction',
ShopAction = 'shopAction',
ContractorAction = 'contractorAction',
}
export interface ModificationAction {
type: ActionType;
name?: ContractModificationName | ShopModificationName | ContractorModificationName;
}

View File

@ -1,21 +0,0 @@
<mat-card>
<mat-card-subtitle>{{ container.name | ccModificationName: type }}</mat-card-subtitle>
<mat-card-content>
<mat-tab-group (selectedIndexChange)="onChangeTabIndex($event)">
<mat-tab *ngFor="let unit of modifications; let index = index">
<ng-template mat-tab-label>
{{ 'unit ' + (index + 1) }}
<span class="unsaved-label">{{ unit.saved ? '' : '*' }}</span>
</ng-template>
<cc-pretty-json
[object]="unit.modificationUnit"
[cleanLook]="true"
></cc-pretty-json>
</mat-tab>
</mat-tab-group>
</mat-card-content>
<mat-card-actions *ngIf="!activeUnit?.saved" fxLayoutAlign="end center">
<button mat-button color="primary" (click)="edit()">EDIT</button>
<button mat-button color="warn" (click)="remove()">REMOVE</button>
</mat-card-actions>
</mat-card>

View File

@ -1,5 +0,0 @@
.unsaved-label {
color: red;
font-weight: bold;
margin-left: 3px;
}

View File

@ -1,67 +0,0 @@
import { Component, Input, OnInit } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { CreateModificationDialogComponent } from '../../party-modification-creator-legacy';
import {
ModificationGroupType,
ModificationUnitContainer,
PartyModificationContainer,
} from '../model';
import { PartyModificationContainerService } from './party-modification-container.service';
import { RemoveConfirmComponent } from './remove-confirm/remove-confirm.component';
@Component({
selector: 'cc-party-modification-container',
templateUrl: 'party-modification-container.component.html',
styleUrls: ['./party-modification-container.component.scss'],
})
export class PartyModificationContainerComponent implements OnInit {
@Input()
container: PartyModificationContainer;
@Input()
type: ModificationGroupType;
@Input()
partyID: string;
modifications: ModificationUnitContainer[];
activeUnit: ModificationUnitContainer;
constructor(
private dialog: MatDialog,
private partyModificationContainerService: PartyModificationContainerService
) {}
ngOnInit() {
this.modifications = this.container.unitContainers.slice();
this.activeUnit = this.modifications[0];
}
remove() {
const config = {
data: {
typeHash: this.activeUnit.typeHash,
},
};
this.dialog.open<RemoveConfirmComponent>(RemoveConfirmComponent, config);
}
edit() {
const config = this.partyModificationContainerService.getDialogConfig(
this.activeUnit.modificationUnit,
this.container.name,
this.type,
this.partyID
);
this.dialog.open<CreateModificationDialogComponent>(
CreateModificationDialogComponent,
config
);
}
onChangeTabIndex(index) {
this.activeUnit = this.modifications[index];
}
}

View File

@ -1,47 +0,0 @@
import { Injectable } from '@angular/core';
import {
ContractModificationUnit,
ShopModificationUnit,
} from '@vality/domain-proto/lib/payment_processing';
import {
ContractModificationName,
ShopModificationName,
} from '../../party-modification-creator-legacy';
import { ModificationGroupType } from '../model';
import { ActionType } from '../modification-action';
@Injectable()
export class PartyModificationContainerService {
getDialogConfig(
modification: ShopModificationUnit | ContractModificationUnit,
name: ContractModificationName | ShopModificationName,
type: string,
partyID: string
) {
const actionType = this.getActionType(type);
const unitID = modification.id;
return {
data: {
action: {
type: actionType,
name,
},
unitID,
partyID,
modification,
},
width: '800px',
disableClose: true,
};
}
private getActionType(type): ActionType {
switch (type) {
case ModificationGroupType.ContractUnitContainer:
return ActionType.ContractAction;
case ModificationGroupType.ShopUnitContainer:
return ActionType.ShopAction;
}
}
}

View File

@ -1,5 +0,0 @@
<div class="mat-dialog-title">Confirm remove</div>
<mat-dialog-actions>
<button mat-button color="primary" (click)="remove()">CONFIRM</button>
<button mat-button [mat-dialog-close]="false">CANCEL</button>
</mat-dialog-actions>

View File

@ -1,24 +0,0 @@
import { Component, Inject } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { ClaimService } from '../../claim.service';
interface RemoveData {
typeHash: string;
}
@Component({
templateUrl: 'remove-confirm.component.html',
})
export class RemoveConfirmComponent {
constructor(
private dialogRef: MatDialogRef<RemoveConfirmComponent>,
private claimService: ClaimService,
@Inject(MAT_DIALOG_DATA) private data: RemoveData
) {}
remove() {
this.dialogRef.close();
this.claimService.removeModification(this.data.typeHash);
}
}

View File

@ -1,90 +0,0 @@
import {
ContractModificationUnit,
ContractorModificationUnit,
ShopModificationUnit,
} from '@vality/domain-proto/lib/claim_management';
import groupBy from 'lodash-es/groupBy';
import map from 'lodash-es/map';
import { UnitName } from '../party-modification-creator-legacy/party-modification-creation/unit-name';
import {
ModificationGroup,
ModificationGroupType,
PartyModificationUnit,
PersistentContainer,
} from './model';
interface PersistentUnit {
modificationUnit: ShopModificationUnit | ContractModificationUnit | ContractorModificationUnit;
saved: boolean;
typeHash: string;
}
const toContainers = (persistentUnits: PersistentUnit[]): any[] => {
const grouped = groupBy(persistentUnits, (item) => {
const modificationNames = Object.keys(item.modificationUnit.modification);
if (modificationNames.length !== 1) {
return 'unknown';
}
return modificationNames[0];
});
return map(grouped, (units, name) => ({
name,
unitContainers: units.map(({ modificationUnit, saved, typeHash }) => ({
modificationUnit,
saved,
typeHash,
})),
}));
};
const isHasUnsaved = (units: PersistentUnit[], unitID: string): boolean =>
units.filter((i) => !i.saved && i.modificationUnit.id === unitID).length > 0;
const toUnits = (persistentUnits: PersistentUnit[]): PartyModificationUnit[] => {
const grouped = groupBy(persistentUnits, (item) => item.modificationUnit.id);
return map(grouped, (units, unitID) => ({
unitID,
hasUnsaved: isHasUnsaved(units, unitID),
containers: toContainers(units),
}));
};
const toGroup = (
name: UnitName,
type: ModificationGroupType,
containers: PersistentContainer[]
): ModificationGroup => {
const persistent = containers.map(({ modification, saved, typeHash }) => ({
modificationUnit: modification[name],
typeHash,
saved,
}));
return {
type,
units: toUnits(persistent),
};
};
export const convert = (containers: PersistentContainer[]): ModificationGroup[] => {
const grouped = groupBy(containers, (item) => {
const { shop_modification, contract_modification } = item.modification;
if (shop_modification) {
return ModificationGroupType.ShopUnitContainer;
}
if (contract_modification) {
return ModificationGroupType.ContractUnitContainer;
}
return ModificationGroupType.Unknown;
});
return map(grouped, (persistentContainer, type) => {
switch (type) {
case ModificationGroupType.ShopUnitContainer:
return toGroup(UnitName.ShopModification, type, persistentContainer);
case ModificationGroupType.ContractUnitContainer:
return toGroup(UnitName.ContractModification, type, persistentContainer);
case ModificationGroupType.Unknown:
return { type: ModificationGroupType.Unknown };
}
});
};

View File

@ -1,37 +0,0 @@
<div fxLayout="column" fxLayoutGap="5px">
<mat-card>
<mat-card-subtitle>{{
type === 'ShopUnitContainer'
? 'Shop modification units'
: type === 'ContractUnitContainer'
? 'Contract modification units'
: ''
}}</mat-card-subtitle>
</mat-card>
<mat-tab-group>
<mat-tab *ngFor="let unit of units">
<ng-template mat-tab-label>
{{ unit.unitID }}
<span class="unsaved-label">{{ !unit.hasUnsaved ? '' : '*' }}</span>
</ng-template>
<div class="tab-container" fxLayout="column" fxLayoutGap="10px">
<div class="add-modification-container" fxLayoutAlign="end center">
<button
mat-button
[disabled]="(isLoading | async) || !(isAddModificationAvailable | async)"
(click)="add(unit.unitID)"
>
ADD MODIFICATION
</button>
</div>
<cc-party-modification-container
*ngFor="let container of unit.containers"
[container]="container"
[type]="type"
[partyID]="partyID"
>
</cc-party-modification-container>
</div>
</mat-tab>
</mat-tab-group>
</div>

View File

@ -1,13 +0,0 @@
.tab-container {
margin: 3px;
}
.unsaved-label {
color: red;
font-weight: bold;
margin-left: 3px;
}
.add-modification-container {
padding-top: 5px;
}

View File

@ -1,48 +0,0 @@
import { Component, Input } from '@angular/core';
import { MatBottomSheet } from '@angular/material/bottom-sheet';
import { UnitActionsNavListComponent } from '../../party-modification-creator-legacy';
import { ClaimService } from '../claim.service';
import { ModificationGroupType, PartyModificationUnit } from '../model';
@Component({
selector: 'cc-party-modification-units',
templateUrl: 'party-modification-units.component.html',
styleUrls: ['./party-modification-units.component.scss'],
})
export class PartyModificationUnitsComponent {
@Input()
type: ModificationGroupType;
@Input()
units: PartyModificationUnit[];
@Input()
partyID: string;
isLoading = this.claimService.isLoading;
isAddModificationAvailable = this.claimService.isAddModificationAvailable;
constructor(private bottomSheet: MatBottomSheet, private claimService: ClaimService) {}
add(unitID: string) {
let type: string;
switch (this.type) {
case ModificationGroupType.ContractUnitContainer:
type = 'contractActions';
break;
case ModificationGroupType.ShopUnitContainer:
type = 'shopActions';
break;
case ModificationGroupType.ContractorUnitContainer:
type = 'contractorActions';
break;
default:
type = 'allActions';
break;
}
this.bottomSheet.open(UnitActionsNavListComponent, {
data: { type, unitID, partyID: this.partyID },
});
}
}

View File

@ -1,26 +0,0 @@
<div
fxLayout="row"
fxLayout.md="column"
fxLayout.sm="column"
fxLayout.xs="column"
fxLayoutGap="10px"
>
<div fxFlex="50" *ngIf="shopUnits.length > 0">
<cc-party-modification-units
unitsName="Shop modification units"
[type]="'ShopUnitContainer'"
[units]="shopUnits"
[partyID]="partyID$ | async"
>
</cc-party-modification-units>
</div>
<div fxFlex="50" *ngIf="contractUnits.length > 0">
<cc-party-modification-units
unitsName="Contract modification units"
[type]="'ContractUnitContainer'"
[units]="contractUnits"
[partyID]="partyID$ | async"
>
</cc-party-modification-units>
</div>
</div>

View File

@ -1,50 +0,0 @@
import { Component, OnInit } from '@angular/core';
import { MatSnackBar } from '@angular/material/snack-bar';
import { ActivatedRoute } from '@angular/router';
import { Observable } from 'rxjs';
import { first, pluck, shareReplay } from 'rxjs/operators';
import { ClaimService } from '../claim.service';
import { ModificationGroupType, PartyModificationUnit } from '../model';
@Component({
selector: 'cc-party-modifications',
templateUrl: 'party-modifications.component.html',
})
export class PartyModificationsComponent implements OnInit {
shopUnits: PartyModificationUnit[] = [];
contractUnits: PartyModificationUnit[] = [];
partyID$: Observable<string> = this.route.params.pipe(
pluck('party_id'),
first(),
shareReplay(1)
);
constructor(
private claimService: ClaimService,
private snackBar: MatSnackBar,
private route: ActivatedRoute
) {}
ngOnInit() {
this.claimService.modificationGroups$.subscribe((groups) => {
this.shopUnits = [];
this.contractUnits = [];
for (const group of groups) {
switch (group.type) {
case ModificationGroupType.ShopUnitContainer:
this.shopUnits = this.shopUnits.concat(group.units);
break;
case ModificationGroupType.ContractUnitContainer:
this.contractUnits = this.contractUnits.concat(group.units);
break;
case ModificationGroupType.Unknown:
this.snackBar.open('Detected unknown party modification unit', 'OK');
break;
}
}
});
}
}

View File

@ -1,74 +0,0 @@
import { Injectable } from '@angular/core';
import { PartyModification } from '@vality/domain-proto/lib/claim_management';
import remove from 'lodash-es/remove';
import { Subject } from 'rxjs';
import { PersistentContainer } from './model';
@Injectable()
export class PersistentContainerService {
containers$: Subject<PersistentContainer[]> = new Subject();
private containers: PersistentContainer[] = [];
init(persisted: PartyModification[], saved = true) {
this.containers = persisted.map((modification) => ({
modification,
saved,
typeHash: !saved ? this.makeTypeHash(modification) : null,
}));
this.containers$.next(this.containers);
}
add(modification: PartyModification) {
const typeHash = this.makeTypeHash(modification);
const item = {
modification,
typeHash,
saved: false,
};
const index = this.containers.findIndex((i) => i.typeHash === typeHash);
if (index !== -1) {
this.containers[index] = item;
} else {
this.containers.push(item);
}
this.containers$.next(this.containers.sort(this.sort)); // sort?
}
remove(typeHash: string) {
remove(this.containers, (i) => i.typeHash === typeHash);
this.containers$.next(this.containers.sort(this.sort));
}
private makeTypeHash(modification: PartyModification): string {
const modificationKeys = Object.keys(modification);
if (modificationKeys.length !== 1) {
return null;
}
const modificationUnit = modification[modificationKeys[0]];
return modificationUnit.id + this.getModificationName(modification);
}
private getModificationName(modification: PartyModification): string {
const modificationKeys = Object.keys(modification);
if (modificationKeys.length !== 1) {
return 'unknown';
}
const modificationUnit = modification[modificationKeys[0]];
const modificationNames = Object.keys(modificationUnit.modification);
if (modificationNames.length !== 1) {
return 'unknown';
}
return Object.keys(modificationUnit.modification)[0];
}
private sort(a: PersistentContainer, b: PersistentContainer): number {
if (a.saved) {
return 1;
}
if (b.saved) {
return -1;
}
return 0;
}
}

View File

@ -1,4 +1,5 @@
import { CommonModule } from '@angular/common';
import { HttpClientModule } from '@angular/common/http';
import { APP_INITIALIZER, NgModule } from '@angular/core';
import { KeycloakAngularModule, KeycloakService } from 'keycloak-angular';
@ -36,7 +37,7 @@ const initializer =
]);
@NgModule({
imports: [CommonModule, KeycloakAngularModule],
imports: [CommonModule, HttpClientModule, KeycloakAngularModule],
providers: [
ConfigService,
KeycloakTokenInfoService,

View File

@ -1,60 +0,0 @@
import { HttpClient, HttpParams } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { decode, encode } from '@cc/utils/java-thrift-formatter';
import { ConfigService } from '../core/config.service';
import { ClaimCreated, ClaimInfo, PartyModificationUnit } from './model';
import { ClaimAcceptParams, ClaimDenyParams, ClaimSearchParams } from './params';
@Injectable()
export class ClaimService {
private readonly papiEndpoint: string;
constructor(private http: HttpClient, configService: ConfigService) {
this.papiEndpoint = configService.config.papiEndpoint;
}
getClaims(params: ClaimSearchParams): Observable<ClaimInfo[]> {
return this.http.post<ClaimInfo[]>(`${this.papiEndpoint}/walk/claim/search`, params);
}
getClaim(partyID: string, claimID: number): Observable<ClaimInfo> {
const params = new HttpParams().set('partyId', partyID).set('claimId', claimID.toString());
return this.http
.get<ClaimInfo>(`${this.papiEndpoint}/walk/claim`, { params })
.pipe(map((claim) => decode(claim)));
}
createClaim(partyID: string, unit: PartyModificationUnit): Observable<ClaimCreated> {
const params = new HttpParams().set('partyId', partyID);
return this.http.post<ClaimCreated>(`${this.papiEndpoint}/walk/claim`, encode(unit), {
params,
});
}
updateClaim(
partyID: string,
claimID: number,
revision: string,
unit: PartyModificationUnit
): Observable<void> {
const params = new HttpParams()
.set('partyId', partyID)
.set('claimId', claimID.toString())
.set('revision', revision);
return this.http.post<void>(`${this.papiEndpoint}/walk/claim/update`, encode(unit), {
params,
});
}
acceptClaim(params: ClaimAcceptParams): Observable<void> {
return this.http.post<void>(`${this.papiEndpoint}/walk/claim/accept`, params);
}
denyClaim(params: ClaimDenyParams): Observable<void> {
return this.http.post<void>(`${this.papiEndpoint}/walk/claim/deny`, params);
}
}

View File

@ -1,8 +0,0 @@
import { Price } from './price';
export class CartLine {
product: string;
quantity: number;
price: Price;
metadata: any[];
}

View File

@ -1,5 +0,0 @@
import { CartLine } from './card-line';
export class Cart {
lines: CartLine[];
}

View File

@ -1,6 +0,0 @@
export class Category {
description: string;
id: number;
name: string;
test: boolean;
}

View File

@ -1,8 +0,0 @@
import { ClaimStatus } from './claim-status';
export class ClaimCreated {
claimId: number;
claimStatus: ClaimStatus;
createdAt: string;
revision: string;
}

View File

@ -1,16 +0,0 @@
import { ClaimStatus } from './claim-status';
import { PartyModificationUnit } from './party-modification-unit';
export interface ClaimInfo {
party_id: string;
claim_id: number;
status: ClaimStatus;
assigned_user_id: string;
description: string;
reason: string;
modifications: PartyModificationUnit;
modification_unit: any;
revision: string;
created_at: string;
updated_at: string;
}

View File

@ -1,6 +0,0 @@
export class ContractTemplate {
description: string;
name: string;
id: number;
termsId: number;
}

View File

@ -1,3 +0,0 @@
export class Currency {
symbolicCode: string;
}

View File

@ -1,12 +0,0 @@
export * from './claim-info';
export * from './category';
export * from './party-modification-unit';
export * from './contract-template';
export * from './payout';
export * from './payouts-response';
export * from './payout-statuses';
export * from './payment';
export * from './invoice';
export * from './claim-created';
export * from './payout-types';
export * from './claim-status';

View File

@ -1,15 +0,0 @@
import { Cart } from './cart';
export class Invoice {
id: string;
ownerId: string;
shopId: string;
createdAt: string;
status: any;
product: string;
description: string;
due: string;
amount: number;
currencySymbolicCode: string;
cart: Cart;
}

View File

@ -1,5 +0,0 @@
import { PartyModification } from '@vality/domain-proto/lib/claim_management';
export class PartyModificationUnit {
modifications: PartyModification[];
}

View File

@ -1,20 +0,0 @@
export class Payment {
id: string;
invoiceId: string;
ownerId: string;
shopId: string;
createdAt: string;
status: any;
amount: number;
fee: number;
providerFee: number;
externalFee: number;
currencySymbolicCode: string;
payer: any;
ipAddress: string;
fingerprint: string;
email: string;
sessionId: string;
locationInfo: any;
flow: any;
}

View File

@ -1,8 +0,0 @@
export enum PayoutStatus {
/* eslint-disable @typescript-eslint/naming-convention */
paid = 'paid',
unpaid = 'unpaid',
cancelled = 'cancelled',
confirmed = 'confirmed',
/* eslint-enable @typescript-eslint/naming-convention */
}

View File

@ -1,6 +0,0 @@
export enum PayoutTypes {
// eslint-disable-next-line @typescript-eslint/naming-convention
bank_account = 'bank_account',
// eslint-disable-next-line @typescript-eslint/naming-convention
wallet = 'wallet',
}

View File

@ -1,14 +0,0 @@
import { PayoutStatus } from './payout-statuses';
export class Payout {
id: string;
amount: number;
currencyCode: string;
status: PayoutStatus;
fromTime: string;
toTime: string;
createdAt: string;
partyId: string;
shopId: string;
payoutType: string;
}

View File

@ -1,6 +0,0 @@
import { Payout } from './payout';
export class PayoutsResponse {
lastId: number;
payouts: Payout[];
}

View File

@ -1,6 +0,0 @@
import { Currency } from './currency';
export class Price {
amount: string;
currency: Currency;
}

View File

@ -1,12 +0,0 @@
import { HttpClientModule } from '@angular/common/http';
import { NgModule } from '@angular/core';
import { ClaimService } from './claim.service';
import { PartyService } from './party.service';
import { PayoutsService } from './payouts.service';
@NgModule({
imports: [HttpClientModule],
providers: [ClaimService, PayoutsService, PartyService],
})
export class PapiModule {}

View File

@ -1,5 +0,0 @@
export class ClaimAcceptParams {
claimId: number;
partyId: string;
revision: string;
}

View File

@ -1,6 +0,0 @@
export class ClaimDenyParams {
claimId: number;
partyId: string;
reason: string;
revision: string;
}

View File

@ -1,7 +0,0 @@
export class ClaimSearchParams {
claimIds?: number[];
partyId?: string;
assignedUserId?: string;
claimStatus?: string;
contains?: string;
}

View File

@ -1,7 +0,0 @@
export * from './claim-search-params';
export * from './claim-accept-params';
export * from './claim-deny-params';
export * from './payout-search-params';
export * from './payout-cancel-params';
export * from './payout-create-params';
export * from './report-search-params';

View File

@ -1,3 +0,0 @@
export class PayoutCancelParams {
reason: string;
}

View File

@ -1,6 +0,0 @@
export class PayoutCreateParams {
fromTime: string;
toTime: string;
partyId: string;
shopId: string;
}

View File

@ -1,14 +0,0 @@
import { PayoutStatus, PayoutTypes } from '../model';
export class PayoutSearchParams {
fromTime?: string;
toTime?: string;
status?: PayoutStatus;
payoutType?: PayoutTypes;
payoutIds?: string[];
fromId?: number;
size?: string;
minAmount?: number;
maxAmount?: number;
currencyCode?: string;
}

View File

@ -1,11 +0,0 @@
export class ReportSearchParams {
fromTime: string;
toTime: string;
from: string;
size: string;
merchantId?: string;
invoiceId?: string;
status?: string;
categoryIds?: string[];
paymentDomainRevision?: string;
}

View File

@ -1,37 +0,0 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Party, Shop } from '@vality/domain-proto/lib/domain';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { decode } from '@cc/utils/java-thrift-formatter';
import { ConfigService } from '../core/config.service';
import { ContractTemplate } from './model';
@Injectable()
export class PartyService {
private readonly papiEndpoint: string;
constructor(private http: HttpClient, configService: ConfigService) {
this.papiEndpoint = configService.config.papiEndpoint;
}
/**
* @deprecated use PartyManagementService
*/
getParty(partyId: string): Observable<Party> {
return this.http
.get<ContractTemplate[]>(`${this.papiEndpoint}/parties/${partyId}`)
.pipe(map((party) => decode(party)));
}
getShops = (partyID: string): Observable<Shop[]> =>
this.getParty(partyID).pipe(map((party) => Array.from(party.shops.values())));
/**
* @deprecated use PartyManagementService
*/
getShop = (partyID: string, shopID: string): Observable<Shop> =>
this.getShops(partyID).pipe(map((shops) => shops.find((shop) => shop.id === shopID)));
}

View File

@ -1,44 +0,0 @@
import { HttpClient, HttpParams } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { ConfigService } from '../core/config.service';
import { Payout, PayoutsResponse } from './model';
import { PayoutCancelParams, PayoutCreateParams, PayoutSearchParams } from './params';
@Injectable()
export class PayoutsService {
private readonly papiEndpoint: string;
constructor(private http: HttpClient, configService: ConfigService) {
this.papiEndpoint = configService.config.papiEndpoint;
}
getPayouts(params?: PayoutSearchParams): Observable<PayoutsResponse> {
let searchParams = new HttpParams();
if (params) {
Object.keys(params).forEach((key) => {
searchParams = params[key] ? searchParams.set(key, params[key]) : searchParams;
});
}
return this.http.get<PayoutsResponse>(`${this.papiEndpoint}/payouts`, {
params: searchParams,
});
}
confirmPayouts(payoutIds: string[]): Observable<string[]> {
return this.http.post<string[]>(`${this.papiEndpoint}/payouts/confirm`, { payoutIds });
}
createPayout(params: PayoutCreateParams): Observable<Payout> {
return this.http.post<Payout>(`${this.papiEndpoint}/payouts`, params);
}
pay(payoutIds: string[]): Observable<void> {
return this.http.post<void>(`${this.papiEndpoint}/payouts/pay`, { payoutIds });
}
cancelPayout(payoutID: string, params: PayoutCancelParams): Observable<void> {
return this.http.post<void>(`${this.papiEndpoint}/payouts/${payoutID}/cancel`, params);
}
}

View File

@ -3,16 +3,17 @@ import { Contract, Party, PartyContractor, Shop } from '@vality/domain-proto/lib
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { PartyService } from '../../../papi/party.service';
import { PartyManagementWithUserService } from '@cc/app/api/payment-processing';
import { PartyTarget } from '../party-target';
import { SelectableItem } from './selectable-item';
@Injectable()
export class PartyTargetService {
constructor(private partyService: PartyService) {}
constructor(private partyManagementWithUserService: PartyManagementWithUserService) {}
getSelectableItems(partyID: string, targetName: PartyTarget): Observable<SelectableItem[]> {
return this.partyService.getParty(partyID).pipe(
return this.partyManagementWithUserService.getParty(partyID).pipe(
map((party) => {
const result = [];
const target = this.getTarget(party, targetName);

View File

@ -1,15 +0,0 @@
<h4 class="mat-dialog-title">Cancel payouts</h4>
<mat-dialog-content>
<form [formGroup]="form" fxLayout="column">
<mat-form-field>
<textarea matInput placeholder="Reason" formControlName="reason"></textarea>
</mat-form-field>
</form>
</mat-dialog-content>
<mat-dialog-actions>
<button mat-button color="warn" (click)="submit()" [disabled]="isLoading || !this.form.valid">
CONFIRM
</button>
<button mat-dialog-close mat-button color="default" [mat-dialog-close]="false">CANCEL</button>
<mat-progress-bar mode="indeterminate" *ngIf="isLoading"></mat-progress-bar>
</mat-dialog-actions>

View File

@ -1,45 +0,0 @@
import { Component, Inject, OnInit } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MatSnackBar } from '@angular/material/snack-bar';
import { PayoutsService } from '../payouts.service';
import { CancelPayoutService } from './cancel-payout.service';
@Component({
templateUrl: './cancel-payout.component.html',
providers: [CancelPayoutService],
})
export class CancelPayoutComponent implements OnInit {
form: FormGroup;
isLoading: boolean;
constructor(
private dialogRef: MatDialogRef<CancelPayoutComponent>,
private cancelPayoutDialogService: CancelPayoutService,
private payoutsService: PayoutsService,
private snackBar: MatSnackBar,
@Inject(MAT_DIALOG_DATA)
public payoutId: string
) {}
ngOnInit() {
this.form = this.cancelPayoutDialogService.createFormGroup;
}
submit() {
this.isLoading = true;
this.payoutsService.cancel(this.payoutId, this.form.value).subscribe(
() => {
this.isLoading = false;
this.dialogRef.close();
this.snackBar.open('Successfully cancelled', 'OK', { duration: 3000 });
},
(error) => {
this.isLoading = false;
this.snackBar.open('An error occurred while payout cancel.', 'OK');
console.error(error);
}
);
}
}

View File

@ -1,17 +0,0 @@
import { Injectable } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
@Injectable()
export class CancelPayoutService {
createFormGroup: FormGroup;
constructor(private fb: FormBuilder) {
this.createFormGroup = this.prepareForm();
}
private prepareForm(): FormGroup {
return this.fb.group({
reason: ['', Validators.required],
});
}
}

View File

@ -1,6 +0,0 @@
<h4 class="mat-dialog-title">Confirm payouts</h4>
<mat-dialog-actions>
<button mat-button color="default" (click)="submit()" [disabled]="isLoading">CONFIRM</button>
<button mat-dialog-close mat-button color="default" [mat-dialog-close]="false">CANCEL</button>
<mat-progress-bar mode="indeterminate" *ngIf="isLoading"></mat-progress-bar>
</mat-dialog-actions>

View File

@ -1,36 +0,0 @@
import { Component, Inject } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MatSnackBar } from '@angular/material/snack-bar';
import { PayoutsService } from '../payouts.service';
@Component({
templateUrl: 'confirm-payouts.component.html',
})
export class ConfirmPayoutsComponent {
isLoading: boolean;
constructor(
private dialogRef: MatDialogRef<ConfirmPayoutsComponent, 'success'>,
private payoutsService: PayoutsService,
private snackBar: MatSnackBar,
@Inject(MAT_DIALOG_DATA)
public payoutsIds: string[]
) {}
submit() {
this.isLoading = true;
this.payoutsService.confirm(this.payoutsIds).subscribe(
() => {
this.isLoading = false;
this.dialogRef.close('success');
this.snackBar.open('Successfully confirmed', 'OK', { duration: 3000 });
},
(error) => {
this.isLoading = false;
this.snackBar.open('An error occurred while confirming payouts', 'OK');
console.error(error);
}
);
}
}

View File

@ -1,26 +0,0 @@
<h4 class="mat-dialog-title">Create payout</h4>
<mat-dialog-content>
<form *ngIf="form" [formGroup]="form" fxLayout="column">
<mat-form-field>
<input matInput placeholder="Party ID" formControlName="partyId" />
</mat-form-field>
<mat-form-field>
<input matInput placeholder="Shop ID" formControlName="shopId" />
</mat-form-field>
<div fxLayout="row" fxLayout.xs="column">
<mat-form-field fxFlex="50">
<input matInput placeholder="From time" formControlName="fromTime" />
</mat-form-field>
<mat-form-field fxFlex="50">
<input matInput placeholder="To time" formControlName="toTime" />
</mat-form-field>
</div>
</form>
</mat-dialog-content>
<mat-dialog-actions>
<button mat-button color="default" (click)="submit()" [disabled]="isLoading || !form.valid">
CREATE
</button>
<button mat-dialog-close mat-button color="default" [mat-dialog-close]="false">CANCEL</button>
<mat-progress-bar *ngIf="isLoading" mode="indeterminate"></mat-progress-bar>
</mat-dialog-actions>

View File

@ -1,50 +0,0 @@
import { Component, OnInit } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { MatDialogRef } from '@angular/material/dialog';
import { MatSnackBar } from '@angular/material/snack-bar';
import { PayoutsService } from '../payouts.service';
import { CreatePayoutService } from './create-payout.service';
@Component({
templateUrl: 'create-payout.component.html',
providers: [CreatePayoutService, PayoutsService],
})
export class CreatePayoutComponent implements OnInit {
form: FormGroup;
isLoading: boolean;
constructor(
private dialogRef: MatDialogRef<CreatePayoutComponent, 'success'>,
private createPayoutService: CreatePayoutService,
private payoutService: PayoutsService,
private snackBar: MatSnackBar
) {}
ngOnInit() {
this.form = this.createPayoutService.createPayoutGroup;
}
submit() {
if (this.form.valid) {
const formValues = this.form.value;
this.isLoading = true;
this.payoutService.create(this.createPayoutService.makeParams(formValues)).subscribe(
() => {
this.dialogRef.close('success');
this.isLoading = false;
this.snackBar.open('Successfully created', 'OK', { duration: 3000 });
},
(error) => {
this.isLoading = false;
const message = error.message;
this.snackBar.open(
`${message ? message : 'An error occurred while creating payout.'}`,
'OK'
);
console.error(error);
}
);
}
}
}

View File

@ -1,37 +0,0 @@
import { Injectable } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import * as moment from 'moment';
import { PayoutCreateParams } from '../../papi/params';
@Injectable()
export class CreatePayoutService {
createPayoutGroup: FormGroup;
private dateFormat = 'DD-MM-YYYY HH:mm';
constructor(private fb: FormBuilder) {
this.createPayoutGroup = this.prepareForm();
}
makeParams(formValues: any): PayoutCreateParams {
return {
...formValues,
fromTime: this.convertDate(formValues.fromTime),
toTime: this.convertDate(formValues.toTime),
};
}
private convertDate(date: string): string {
return moment(date, this.dateFormat).utc().format();
}
private prepareForm(): FormGroup {
const now = moment().format(this.dateFormat);
return this.fb.group({
fromTime: [now, Validators.required],
toTime: [now, Validators.required],
partyId: ['', Validators.required],
shopId: ['', Validators.required],
});
}
}

View File

@ -1,6 +0,0 @@
<h4 class="mat-dialog-title">Confirm payout pay</h4>
<mat-dialog-actions>
<button mat-button color="default" (click)="submit()" [disabled]="isLoading">CONFIRM</button>
<button mat-dialog-close mat-button color="default" [mat-dialog-close]="false">CANCEL</button>
<mat-progress-bar mode="indeterminate" *ngIf="isLoading"></mat-progress-bar>
</mat-dialog-actions>

View File

@ -1,36 +0,0 @@
import { Component, Inject } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MatSnackBar } from '@angular/material/snack-bar';
import { PayoutsService } from '../payouts.service';
@Component({
templateUrl: 'pay-payouts.component.html',
})
export class PayPayoutsComponent {
isLoading: boolean;
constructor(
private dialogRef: MatDialogRef<PayPayoutsComponent, 'success'>,
private payoutsService: PayoutsService,
private snackBar: MatSnackBar,
@Inject(MAT_DIALOG_DATA)
public payoutsIds: string[]
) {}
submit() {
this.isLoading = true;
this.payoutsService.pay(this.payoutsIds).subscribe(
() => {
this.isLoading = false;
this.dialogRef.close('success');
this.snackBar.open('Successfully payed', 'OK', { duration: 3000 });
},
(error) => {
this.isLoading = false;
this.snackBar.open('An error occurred while payout pay', 'OK');
console.error(error);
}
);
}
}

View File

@ -1,48 +0,0 @@
<div fxLayout="row" fxLayoutGap="10px">
<button
*ngIf="hasRole(payoutRole.Generate)"
mat-raised-button
color="primary"
(click)="createPayout()"
>
CREATE
</button>
<div
#payTooltip="matTooltip"
matTooltip='Select payouts with status "unpaid"'
[matTooltipDisabled]="isCanPay()"
>
<button
*ngIf="hasRole(payoutRole.Pay)"
[matBadgeHidden]="!isCanPay()"
matBadge="{{ selectedPayouts.length }}"
matBadgePosition="after"
matBadgeColor="primary"
mat-raised-button
color="accent"
(click)="pay()"
[disabled]="!isCanPay()"
>
PAY
</button>
</div>
<div
#confirmTooltip="matTooltip"
matTooltip='Select payouts with status "paid"'
[matTooltipDisabled]="isCanConfirm()"
>
<button
*ngIf="hasRole(payoutRole.Confirm)"
[matBadgeHidden]="!isCanConfirm()"
matBadge="{{ selectedPayouts.length }}"
matBadgePosition="after"
matBadgeColor="primary"
mat-raised-button
color="accent"
(click)="confirmPayouts()"
[disabled]="!isCanConfirm()"
>
CONFIRM
</button>
</div>
</div>

View File

@ -1,75 +0,0 @@
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { filter } from 'rxjs/operators';
import { AppAuthGuardService, PayoutRole } from '@cc/app/shared/services';
import { Payout, PayoutStatus } from '../../papi/model';
import { ConfirmPayoutsComponent } from '../confirm-payouts/confirm-payouts.component';
import { CreatePayoutComponent } from '../create-payout/create-payout.component';
import { PayPayoutsComponent } from '../pay-payouts/pay-payouts.component';
@Component({
selector: 'cc-payouts-actions',
templateUrl: 'payouts-actions.component.html',
})
export class PayoutsActionsComponent {
@Input()
selectedPayouts: Payout[];
@Output()
doAction: EventEmitter<void> = new EventEmitter();
payoutRole = PayoutRole;
constructor(private dialogRef: MatDialog, private appAuthGuardService: AppAuthGuardService) {}
pay() {
this.dialogRef
.open(PayPayoutsComponent, {
data: this.getIds(this.selectedPayouts),
})
.afterClosed()
.pipe(filter((result) => result === 'success'))
.subscribe(() => this.doAction.emit());
}
confirmPayouts() {
this.dialogRef
.open(ConfirmPayoutsComponent, {
data: this.getIds(this.selectedPayouts),
})
.afterClosed()
.pipe(filter((result) => result === 'success'))
.subscribe(() => this.doAction.emit());
}
createPayout() {
this.dialogRef
.open(CreatePayoutComponent, {
width: '720px',
disableClose: true,
})
.afterClosed()
.pipe(filter((result) => result === 'success'))
.subscribe(() => this.doAction.emit());
}
hasRole(role: PayoutRole): boolean {
return this.appAuthGuardService.userHasRoles([role]);
}
isCanPay(): boolean {
const unpaid = this.selectedPayouts.filter((p) => p.status === PayoutStatus.unpaid);
return this.selectedPayouts.length === unpaid.length && unpaid.length > 0;
}
isCanConfirm(): boolean {
const paid = this.selectedPayouts.filter((p) => p.status === PayoutStatus.paid);
return this.selectedPayouts.length === paid.length && paid.length > 0;
}
private getIds(payouts: Payout[]): string[] {
return payouts.map((p) => p.id);
}
}

View File

@ -1,23 +0,0 @@
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { AppAuthGuardService } from '@cc/app/shared/services';
import { PayoutsComponent } from './payouts.component';
@NgModule({
imports: [
RouterModule.forChild([
{
path: 'old-payouts',
component: PayoutsComponent,
canActivate: [AppAuthGuardService],
data: {
roles: ['payout:read'],
},
},
]),
],
exports: [RouterModule],
})
export class PayoutsRoutingModule {}

View File

@ -1,74 +0,0 @@
<table mat-table [dataSource]="dataSource">
<ng-container matColumnDef="select">
<th mat-header-cell *matHeaderCellDef>
<mat-checkbox
(change)="$event ? masterToggle() : null"
[checked]="selection.hasValue() && isAllSelected()"
[indeterminate]="selection.hasValue() && !isAllSelected()"
>
</mat-checkbox>
</th>
<td mat-cell *matCellDef="let row">
<mat-checkbox
(click)="$event.stopPropagation()"
(change)="$event ? selection.toggle(row) : null"
[checked]="selection.isSelected(row)"
>
</mat-checkbox>
</td>
</ng-container>
<ng-container matColumnDef="id">
<th mat-header-cell *matHeaderCellDef>Payout ID</th>
<td mat-cell *matCellDef="let payout">{{ payout.id }}</td>
</ng-container>
<ng-container matColumnDef="partyId">
<th mat-header-cell *matHeaderCellDef fxHide.xs fxHide.sm>Party ID</th>
<td mat-cell *matCellDef="let payout" fxHide.xs fxHide.sm>{{ payout.partyId }}</td>
</ng-container>
<ng-container matColumnDef="shopId">
<th mat-header-cell *matHeaderCellDef fxHide.xs fxHide.sm>Shop ID</th>
<td mat-cell *matCellDef="let payout" fxHide.xs fxHide.sm>{{ payout.shopId }}</td>
</ng-container>
<ng-container matColumnDef="amount">
<th mat-header-cell *matHeaderCellDef fxHide.xs>Amount</th>
<td mat-cell *matCellDef="let payout" fxHide.xs>
{{ payout.amount | ccFormatAmount }} {{ payout.currencyCode | ccCurrency }}
</td>
</ng-container>
<ng-container matColumnDef="period">
<th mat-header-cell *matHeaderCellDef fxHide.xs>Period</th>
<td mat-cell *matCellDef="let payout" fxHide.xs>
{{ payout.fromTime | date: 'dd.MM.yyyy HH:mm:ss' }}<br />
— {{ payout.toTime | date: 'dd.MM.yyyy HH:mm:ss' }}
</td>
</ng-container>
<ng-container matColumnDef="status">
<th mat-header-cell *matHeaderCellDef>Status</th>
<td mat-cell *matCellDef="let payout">{{ payout.status }}</td>
</ng-container>
<ng-container matColumnDef="type">
<th mat-header-cell *matHeaderCellDef>Type</th>
<td mat-cell *matCellDef="let payout">{{ payout.payoutType }}</td>
</ng-container>
<ng-container matColumnDef="payoutDetailButton">
<th mat-header-cell *matHeaderCellDef class="action-cell"></th>
<td mat-cell *matCellDef="let payout" class="action-cell">
<mat-menu #appMenu="matMenu">
<button
*ngIf="hasRole('payout:cancel')"
mat-menu-item
(click)="cancelPayout(payout.id)"
>
Cancel payout
</button>
</mat-menu>
<button mat-icon-button [matMenuTriggerFor]="appMenu">
<mat-icon>more_vert</mat-icon>
</button>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let payout; columns: displayedColumns"></tr>
</table>
<mat-paginator [pageSizeOptions]="[10, 20, 50, 100, 250, 500]" showFirstLastButtons></mat-paginator>

View File

@ -1,15 +0,0 @@
table {
width: 100%;
}
.action-cell {
width: 10px;
}
.revoke-dialog {
margin-top: 30px;
}
dt {
font-weight: bold;
}

View File

@ -1,85 +0,0 @@
import { SelectionModel } from '@angular/cdk/collections';
import {
Component,
EventEmitter,
Input,
OnChanges,
OnInit,
Output,
SimpleChanges,
ViewChild,
} from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { MatPaginator } from '@angular/material/paginator';
import { MatTableDataSource } from '@angular/material/table';
import { KeycloakService } from 'keycloak-angular';
import { Payout } from '../../papi/model';
import { CancelPayoutComponent } from '../cancel-payout/cancel-payout.component';
@Component({
selector: 'cc-payouts-table',
templateUrl: 'payouts-table.component.html',
styleUrls: ['./payouts-table.component.scss'],
})
export class PayoutsTableComponent implements OnInit, OnChanges {
@Output()
valueChanges: EventEmitter<Payout[]> = new EventEmitter();
@Input()
payouts: Payout[];
dataSource: MatTableDataSource<Payout> = new MatTableDataSource();
roles: string[];
selection = new SelectionModel<Payout>(true, []);
@ViewChild(MatPaginator, { static: true })
paginator: MatPaginator;
displayedColumns = [
'select',
'id',
'partyId',
'shopId',
'amount',
'period',
'status',
'type',
'payoutDetailButton',
];
constructor(private matDialog: MatDialog, private keycloakService: KeycloakService) {}
ngOnChanges(changes: SimpleChanges) {
const { payouts } = changes;
if (payouts && payouts.currentValue) {
this.selection.clear();
this.dataSource.data = payouts.currentValue;
}
}
ngOnInit() {
this.roles = this.keycloakService.getUserRoles();
this.selection.changed.subscribe((e) => this.valueChanges.emit(e.source.selected));
this.dataSource.paginator = this.paginator;
}
isAllSelected() {
return this.selection.selected.length === this.payouts.length;
}
masterToggle() {
if (this.isAllSelected()) this.selection.clear();
else this.selection.select(...this.payouts);
}
cancelPayout(id: string) {
this.matDialog.open(CancelPayoutComponent, {
data: id,
});
}
hasRole(role: string): boolean {
return this.roles.includes(role);
}
}

View File

@ -1,42 +0,0 @@
<cc-card-container>
<mat-card>
<mat-card-subtitle> Search payouts </mat-card-subtitle>
<mat-card-content>
<cc-payouts-search-form
(valueChanges)="formValueChanges($event)"
(statusChanges)="formStatusChanges($event)"
>
</cc-payouts-search-form>
</mat-card-content>
<mat-card-actions>
<button
mat-button
color="primary"
[disabled]="!formValid || isLoading"
(click)="search()"
>
SEARCH
</button>
</mat-card-actions>
<mat-card-footer *ngIf="isLoading">
<mat-progress-bar mode="indeterminate"></mat-progress-bar>
</mat-card-footer>
</mat-card>
<mat-card>
<mat-card-subtitle>Payout actions</mat-card-subtitle>
<mat-card-content fxLayout="row" fxLayoutGap="20px">
<cc-payouts-actions
[selectedPayouts]="selectedPayouts"
(doAction)="search()"
></cc-payouts-actions>
</mat-card-content>
</mat-card>
<mat-card>
<mat-card-content>
<cc-payouts-table
[payouts]="payouts"
(valueChanges)="tableOnChange($event)"
></cc-payouts-table>
</mat-card-content>
</mat-card>
</cc-card-container>

View File

@ -1,10 +0,0 @@
.actions-button-container {
position: fixed;
bottom: 15px;
right: 15px;
}
mat-spinner {
display: inline-block;
margin-left: 15px;
}

View File

@ -1,48 +0,0 @@
import { Component } from '@angular/core';
import { MatSnackBar } from '@angular/material/snack-bar';
import { Payout } from '../papi/model';
import { PayoutSearchParams } from '../papi/params';
import { PayoutsService } from './payouts.service';
import { SearchFormService } from './search-form/search-form.service';
@Component({
templateUrl: 'payouts.component.html',
styleUrls: ['./payouts.component.scss'],
providers: [SearchFormService],
})
export class PayoutsComponent {
isLoading: boolean;
payouts: Payout[];
selectedPayouts: Payout[] = [];
formValid: boolean;
searchParams: PayoutSearchParams;
constructor(private payoutsService: PayoutsService, private snackBar: MatSnackBar) {}
formValueChanges(params: PayoutSearchParams) {
this.searchParams = params;
}
formStatusChanges(status: string) {
this.formValid = status === 'VALID';
}
tableOnChange(selectedPayouts: Payout[]) {
this.selectedPayouts = selectedPayouts;
}
search() {
this.isLoading = true;
this.payoutsService.get(this.searchParams).subscribe(
(payouts) => {
this.isLoading = false;
this.payouts = payouts;
},
() => {
this.isLoading = false;
this.snackBar.open('An error occurred while search payouts', 'OK');
}
);
}
}

View File

@ -1,87 +0,0 @@
import { CdkTableModule } from '@angular/cdk/table';
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FlexLayoutModule } from '@angular/flex-layout';
import { ReactiveFormsModule } from '@angular/forms';
import { MatBadgeModule } from '@angular/material/badge';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatDialogModule } from '@angular/material/dialog';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatMenuModule } from '@angular/material/menu';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatProgressBarModule } from '@angular/material/progress-bar';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatSelectModule } from '@angular/material/select';
import { MatSnackBarModule } from '@angular/material/snack-bar';
import { MatTableModule } from '@angular/material/table';
import { MatTooltipModule } from '@angular/material/tooltip';
import { CommonPipesModule } from '@cc/app/shared/pipes';
import { CardContainerModule } from '@cc/components/card-container/card-container.module';
import { PapiModule } from '../papi/papi.module';
import { CancelPayoutComponent } from './cancel-payout/cancel-payout.component';
import { ConfirmPayoutsComponent } from './confirm-payouts/confirm-payouts.component';
import { CreatePayoutComponent } from './create-payout/create-payout.component';
import { PayPayoutsComponent } from './pay-payouts/pay-payouts.component';
import { PayoutsActionsComponent } from './payouts-actions/payouts-actions.component';
import { PayoutsRoutingModule } from './payouts-routing.module';
import { PayoutsTableComponent } from './payouts-table/payouts-table.component';
import { PayoutsComponent } from './payouts.component';
import { PayoutsService } from './payouts.service';
import { SearchFormComponent } from './search-form/search-form.component';
@NgModule({
imports: [
CommonModule,
PayoutsRoutingModule,
MatCardModule,
MatProgressBarModule,
MatSnackBarModule,
FlexLayoutModule,
PapiModule,
ReactiveFormsModule,
MatFormFieldModule,
MatInputModule,
MatSelectModule,
MatDatepickerModule,
CdkTableModule,
MatTableModule,
MatCheckboxModule,
MatButtonModule,
MatDialogModule,
MatIconModule,
PapiModule,
MatProgressSpinnerModule,
MatBadgeModule,
MatMenuModule,
MatTooltipModule,
MatPaginatorModule,
CardContainerModule,
CommonPipesModule,
],
declarations: [
PayoutsComponent,
SearchFormComponent,
PayoutsTableComponent,
CreatePayoutComponent,
CancelPayoutComponent,
PayPayoutsComponent,
ConfirmPayoutsComponent,
PayoutsActionsComponent,
],
entryComponents: [
CreatePayoutComponent,
CancelPayoutComponent,
PayPayoutsComponent,
ConfirmPayoutsComponent,
],
providers: [PayoutsService],
exports: [PayoutsTableComponent],
})
export class PayoutsModule {}

View File

@ -1,32 +0,0 @@
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { Payout } from '../papi/model';
import { PayoutCancelParams, PayoutCreateParams, PayoutSearchParams } from '../papi/params';
import { PayoutsService as PayoutsPapiService } from '../papi/payouts.service';
@Injectable()
export class PayoutsService {
constructor(private payoutsPapiService: PayoutsPapiService) {}
get(params: PayoutSearchParams): Observable<Payout[]> {
return this.payoutsPapiService.getPayouts(params).pipe(map((response) => response.payouts));
}
confirm(payoutsIds: string[]): Observable<void> {
return this.payoutsPapiService.confirmPayouts(payoutsIds).pipe(map(() => null));
}
pay(payoutsIds: string[]): Observable<void> {
return this.payoutsPapiService.pay(payoutsIds).pipe(map(() => null));
}
cancel(payoutId: string, params: PayoutCancelParams): Observable<void> {
return this.payoutsPapiService.cancelPayout(payoutId, params).pipe(map(() => null));
}
create(params: PayoutCreateParams): Observable<Payout> {
return this.payoutsPapiService.createPayout(params);
}
}

Some files were not shown because too many files have changed in this diff Show More