mirror of
https://github.com/valitydev/control-center.git
synced 2024-11-06 02:25:17 +00:00
IMP-237: Show shops tariffs decision-value (#360)
This commit is contained in:
parent
1650516ab6
commit
45cf33deb8
@ -2,5 +2,5 @@
|
||||
"$schema": "https://raw.githubusercontent.com/streetsidesoftware/cspell/main/cspell.schema.json",
|
||||
"version": "0.2",
|
||||
"import": "./node_modules/@vality/cspell-config/cspell.config.js",
|
||||
"words": ["submain", "papaparse", "msgpack"]
|
||||
"words": ["submain", "papaparse", "msgpack", "termsets"]
|
||||
}
|
||||
|
8
package-lock.json
generated
8
package-lock.json
generated
@ -26,7 +26,7 @@
|
||||
"@vality/fistful-proto": "2.0.1-6600be9.0",
|
||||
"@vality/machinegun-proto": "1.0.0",
|
||||
"@vality/magista-proto": "2.0.2-28d11b9.0",
|
||||
"@vality/ng-core": "17.2.1-pr-62-e7ae3c9.0",
|
||||
"@vality/ng-core": "17.2.1-pr-62-bd071a2.0",
|
||||
"@vality/ng-thrift": "17.0.1-pr-5-2ce0f11.0",
|
||||
"@vality/payout-manager-proto": "2.0.1-eb4091a.0",
|
||||
"@vality/repairer-proto": "2.0.2-07b73e9.0",
|
||||
@ -6462,9 +6462,9 @@
|
||||
"integrity": "sha512-BsDy5ejotfTtUlwuoX3kz+PYJ5NSTW6m5ZRGv+p5HaKXSjR7tserPdv0q133Wp4T+sg0ED0Qr9Peqsrn+9XlDQ=="
|
||||
},
|
||||
"node_modules/@vality/ng-core": {
|
||||
"version": "17.2.1-pr-62-e7ae3c9.0",
|
||||
"resolved": "https://registry.npmjs.org/@vality/ng-core/-/ng-core-17.2.1-pr-62-e7ae3c9.0.tgz",
|
||||
"integrity": "sha512-7x3TctfL6SAwmucbWo78i8Qk/ytC9qn4R2PXLxux1/6aHOaI+JB02Cv9zdTfXrKLQiPbzF/rAW/lbC8PtXVduQ==",
|
||||
"version": "17.2.1-pr-62-bd071a2.0",
|
||||
"resolved": "https://registry.npmjs.org/@vality/ng-core/-/ng-core-17.2.1-pr-62-bd071a2.0.tgz",
|
||||
"integrity": "sha512-/zLUC/ogUZbIQEuciYDibCBMA3USixuPtm2aWtp7E+ppNJtpd56Wu4py/BTCrDUMjlt0kDog8aqqin1snqGscA==",
|
||||
"dependencies": {
|
||||
"@angular/material-date-fns-adapter": "^17.2.0",
|
||||
"@ng-matero/extensions": "^17.1.0",
|
||||
|
@ -34,7 +34,7 @@
|
||||
"@vality/fistful-proto": "2.0.1-6600be9.0",
|
||||
"@vality/machinegun-proto": "1.0.0",
|
||||
"@vality/magista-proto": "2.0.2-28d11b9.0",
|
||||
"@vality/ng-core": "17.2.1-pr-62-e7ae3c9.0",
|
||||
"@vality/ng-core": "17.2.1-pr-62-bd071a2.0",
|
||||
"@vality/ng-thrift": "17.0.1-pr-5-2ce0f11.0",
|
||||
"@vality/payout-manager-proto": "2.0.1-eb4091a.0",
|
||||
"@vality/repairer-proto": "2.0.2-07b73e9.0",
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Component, isDevMode } from '@angular/core';
|
||||
import { Component } from '@angular/core';
|
||||
import { Link } from '@vality/ng-core';
|
||||
import { KeycloakService } from 'keycloak-angular';
|
||||
import sortBy from 'lodash-es/sortBy';
|
||||
@ -81,15 +81,11 @@ export class AppComponent {
|
||||
url: '/sources',
|
||||
services: SOURCES_ROUTING_CONFIG.services,
|
||||
},
|
||||
...(isDevMode()
|
||||
? [
|
||||
{
|
||||
label: 'Tariffs',
|
||||
url: '/tariffs',
|
||||
services: TARIFFS_ROUTING_CONFIG.services,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
label: 'Tariffs',
|
||||
url: '/tariffs',
|
||||
services: TARIFFS_ROUTING_CONFIG.services,
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
|
@ -0,0 +1 @@
|
||||
<v-table [columns]="columns" [data]="data()"></v-table>
|
@ -0,0 +1,38 @@
|
||||
import { Component, input } from '@angular/core';
|
||||
import { CashFlowSelector } from '@vality/domain-proto/internal/domain';
|
||||
import { Column, TableModule } from '@vality/ng-core';
|
||||
import { getUnionKey } from '@vality/ng-thrift';
|
||||
|
||||
import { formatCashFlowDecisions } from '@cc/app/sections/tariffs/components/cash-flows-selector-table/format-cash-flow.decisions';
|
||||
import { formatCashVolume } from '@cc/app/shared/utils/table/format-cash-volume';
|
||||
|
||||
@Component({
|
||||
selector: 'cc-cash-flows-selector-table',
|
||||
standalone: true,
|
||||
imports: [TableModule],
|
||||
templateUrl: './cash-flows-selector-table.component.html',
|
||||
styles: ``,
|
||||
})
|
||||
export class CashFlowsSelectorTableComponent {
|
||||
data = input<CashFlowSelector[]>();
|
||||
|
||||
columns: Column<CashFlowSelector>[] = [
|
||||
{
|
||||
field: 'decisions',
|
||||
formatter: (d) => formatCashFlowDecisions(d?.decisions),
|
||||
},
|
||||
{
|
||||
field: 'value',
|
||||
formatter: (d) =>
|
||||
d?.value
|
||||
?.filter(
|
||||
(c) =>
|
||||
getUnionKey(c?.source) === 'merchant' &&
|
||||
getUnionKey(c?.destination) === 'system',
|
||||
)
|
||||
?.sort()
|
||||
?.map((c) => formatCashVolume(c.volume))
|
||||
.join(' + '),
|
||||
},
|
||||
];
|
||||
}
|
@ -17,6 +17,10 @@
|
||||
</v-filters>
|
||||
|
||||
<v-table
|
||||
[cellTemplate]="{
|
||||
decision: arrayColumnTemplate,
|
||||
value: arrayColumnTemplate
|
||||
}"
|
||||
[columns]="columns"
|
||||
[data]="tariffs$ | async"
|
||||
[hasMore]="hasMore$ | async"
|
||||
@ -24,4 +28,11 @@
|
||||
(more)="more()"
|
||||
(update)="update($event)"
|
||||
></v-table>
|
||||
|
||||
<ng-template #arrayColumnTemplate let-value="value">
|
||||
{{ rowData?.length }}
|
||||
<div *ngFor="let item of value" style="white-space: nowrap">
|
||||
{{ item }}
|
||||
</div>
|
||||
</ng-template>
|
||||
</cc-page-layout>
|
||||
|
@ -2,7 +2,11 @@ import { CommonModule } from '@angular/common';
|
||||
import { Component, DestroyRef, Inject, OnInit } from '@angular/core';
|
||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||
import { NonNullableFormBuilder, ReactiveFormsModule } from '@angular/forms';
|
||||
import { TermSetHierarchyRef } from '@vality/domain-proto/internal/domain';
|
||||
import {
|
||||
TermSetHierarchyRef,
|
||||
type CashFlowSelector,
|
||||
type CashFlowPosting,
|
||||
} from '@vality/domain-proto/internal/domain';
|
||||
import {
|
||||
CommonSearchQueryParams,
|
||||
ShopSearchQuery,
|
||||
@ -23,7 +27,12 @@ import {
|
||||
TableModule,
|
||||
UpdateOptions,
|
||||
} from '@vality/ng-core';
|
||||
import { getUnionKey } from '@vality/ng-thrift';
|
||||
import { map, shareReplay } from 'rxjs/operators';
|
||||
import {
|
||||
DomainObjectCardComponent,
|
||||
getDomainObjectDetails,
|
||||
} from 'src/app/shared/components/thrift-api-crud';
|
||||
import { Overwrite } from 'utility-types';
|
||||
|
||||
import {
|
||||
@ -32,9 +41,12 @@ import {
|
||||
createShopColumn,
|
||||
PageLayoutModule,
|
||||
ShopFieldModule,
|
||||
formatCashVolume,
|
||||
formatPredicate,
|
||||
} from '@cc/app/shared';
|
||||
import { CurrencyFieldComponent } from '@cc/app/shared/components/currency-field';
|
||||
import { MerchantFieldModule } from '@cc/app/shared/components/merchant-field';
|
||||
import { SidenavInfoService } from '@cc/app/shared/components/sidenav-info';
|
||||
import { DEBOUNCE_TIME_MS } from '@cc/app/tokens';
|
||||
|
||||
import { ShopsTariffsService } from './shops-tariffs.service';
|
||||
@ -45,6 +57,66 @@ type Params = Pick<CommonSearchQueryParams, 'currencies'> &
|
||||
{ term_sets_ids?: TermSetHierarchyRef['id'][] }
|
||||
>;
|
||||
|
||||
function getViewedCashFlowSelectors(d: ShopTermSet) {
|
||||
return (
|
||||
d.current_term_set.data.term_sets
|
||||
?.map?.((t) => t?.terms?.payments?.fees)
|
||||
?.filter?.(Boolean) ?? []
|
||||
);
|
||||
}
|
||||
|
||||
interface InlineCashFlowSelector {
|
||||
if?: string;
|
||||
value?: string;
|
||||
parent?: InlineCashFlowSelector;
|
||||
level: number;
|
||||
}
|
||||
|
||||
function getInlineDecisions(
|
||||
d: CashFlowSelector[],
|
||||
filterValue: (v: CashFlowPosting) => boolean = (v) =>
|
||||
getUnionKey(v?.source) === 'merchant' && getUnionKey(v?.destination) === 'system',
|
||||
level = 0,
|
||||
): InlineCashFlowSelector[] {
|
||||
return d.reduce((acc, c) => {
|
||||
if (c.value) {
|
||||
acc.push({
|
||||
value: c.value
|
||||
.filter(filterValue)
|
||||
.map((v) => formatCashVolume(v.volume))
|
||||
.join(' + '),
|
||||
level,
|
||||
});
|
||||
}
|
||||
if (c.decisions?.length) {
|
||||
acc.push(
|
||||
...c.decisions
|
||||
.map((d) => {
|
||||
const thenInlineDecisions = getInlineDecisions(
|
||||
[d.then_],
|
||||
filterValue,
|
||||
level + 1,
|
||||
);
|
||||
if (d.if_) {
|
||||
const ifInlineDecision = {
|
||||
if: `${' '.repeat(level)}${
|
||||
formatPredicate(d.if_) || (level > 0 ? '↳' : '')
|
||||
}`,
|
||||
level,
|
||||
};
|
||||
return thenInlineDecisions.length > 1
|
||||
? [ifInlineDecision, ...thenInlineDecisions]
|
||||
: [{ ...ifInlineDecision, value: thenInlineDecisions[0].value }];
|
||||
}
|
||||
return thenInlineDecisions;
|
||||
})
|
||||
.flat(),
|
||||
);
|
||||
}
|
||||
return acc;
|
||||
}, [] as InlineCashFlowSelector[]);
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'cc-shops-tariffs',
|
||||
standalone: true,
|
||||
@ -76,7 +148,7 @@ export class ShopsTariffsComponent implements OnInit {
|
||||
hasMore$ = this.shopsTariffsService.hasMore$;
|
||||
isLoading$ = this.shopsTariffsService.isLoading$;
|
||||
columns: Column<ShopTermSet>[] = [
|
||||
createShopColumn<ShopTermSet>('shop_id', (d) => d.owner_id),
|
||||
createShopColumn<ShopTermSet>('shop_id', (d) => d.owner_id, undefined, { pinned: 'left' }),
|
||||
createPartyColumn<ShopTermSet>('owner_id'),
|
||||
createContractColumn<ShopTermSet>(
|
||||
(d) => d.contract_id,
|
||||
@ -86,9 +158,20 @@ export class ShopsTariffsComponent implements OnInit {
|
||||
{ field: 'currency' },
|
||||
{
|
||||
field: 'current_term_set',
|
||||
formatter: (d) => d.current_term_set?.data?.name,
|
||||
description: (d) => d.current_term_set?.data?.description,
|
||||
tooltip: (d) => d.current_term_set,
|
||||
formatter: (d) =>
|
||||
getDomainObjectDetails({ term_set_hierarchy: d.current_term_set })?.label,
|
||||
click: (d) =>
|
||||
this.sidenavInfoService.open(DomainObjectCardComponent, {
|
||||
ref: { term_set_hierarchy: d?.current_term_set?.ref },
|
||||
}),
|
||||
},
|
||||
{
|
||||
field: 'decision',
|
||||
formatter: (d) => getInlineDecisions(getViewedCashFlowSelectors(d)).map((v) => v.if),
|
||||
},
|
||||
{
|
||||
field: 'value',
|
||||
formatter: (d) => getInlineDecisions(getViewedCashFlowSelectors(d)).map((v) => v.value),
|
||||
},
|
||||
{
|
||||
field: 'term_set_history',
|
||||
@ -109,6 +192,7 @@ export class ShopsTariffsComponent implements OnInit {
|
||||
private qp: QueryParamsService<Params>,
|
||||
@Inject(DEBOUNCE_TIME_MS) private debounceTimeMs: number,
|
||||
private dr: DestroyRef,
|
||||
private sidenavInfoService: SidenavInfoService,
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
|
@ -0,0 +1,3 @@
|
||||
<cc-card [title]="data()?.data?.name">
|
||||
<cc-cash-flows-selector-table [data]="feesData()"></cc-cash-flows-selector-table>
|
||||
</cc-card>
|
@ -0,0 +1,23 @@
|
||||
import { Component, computed, input } from '@angular/core';
|
||||
import { TermSetHierarchyObject } from '@vality/domain-proto/internal/domain';
|
||||
|
||||
import { CardComponent } from '@cc/app/shared/components/sidenav-info/components/card/card.component';
|
||||
|
||||
import { CashFlowsSelectorTableComponent } from '../cash-flows-selector-table/cash-flows-selector-table.component';
|
||||
|
||||
@Component({
|
||||
selector: 'cc-termsets-card',
|
||||
standalone: true,
|
||||
imports: [CardComponent, CashFlowsSelectorTableComponent],
|
||||
templateUrl: './termsets-card.component.html',
|
||||
styles: ``,
|
||||
})
|
||||
export class TermsetsCardComponent {
|
||||
data = input<TermSetHierarchyObject>();
|
||||
feesData = computed(
|
||||
() =>
|
||||
this.data()
|
||||
?.data?.term_sets?.map?.((t) => t?.terms?.payments?.fees)
|
||||
?.filter?.(Boolean),
|
||||
);
|
||||
}
|
22
src/app/shared/utils/table/format-cash-volume.ts
Normal file
22
src/app/shared/utils/table/format-cash-volume.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { CashVolume } from '@vality/domain-proto/internal/domain';
|
||||
import { formatCurrency } from '@vality/ng-core';
|
||||
import { getUnionKey, getUnionValue } from '@vality/ng-thrift';
|
||||
|
||||
import { formatRational } from './format-rational';
|
||||
|
||||
export function formatCashVolume(d: CashVolume) {
|
||||
switch (getUnionKey(d)) {
|
||||
case 'fixed':
|
||||
return formatCurrency(d?.fixed?.cash?.amount, d?.fixed?.cash?.currency?.symbolic_code);
|
||||
case 'share':
|
||||
return (
|
||||
formatRational(d?.share?.parts) +
|
||||
(d?.share?.of === 2 ? ' of surplus' : '') +
|
||||
(d?.share?.rounding_method === 1 ? ' (round .5+)' : '')
|
||||
);
|
||||
case 'product':
|
||||
return `${getUnionKey(d.product).slice(0, -3)}(${Array.from(getUnionValue(d.product))
|
||||
.map((c) => formatCashVolume(c))
|
||||
.join(', ')})`;
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
import { Predicate } from '@vality/domain-proto/domain';
|
||||
import { getUnionKey, getUnionValue } from '@vality/ng-thrift';
|
||||
import { formatCurrency, inlineJson } from '@vality/ng-core';
|
||||
import { getUnionKey, getUnionValue, toJson } from '@vality/ng-thrift';
|
||||
import startCase from 'lodash-es/startCase';
|
||||
|
||||
export function formatPredicate(predicate: Predicate, level = 0) {
|
||||
@ -18,24 +19,57 @@ export function formatPredicate(predicate: Predicate, level = 0) {
|
||||
if (predicatesSet.size <= 1) {
|
||||
return formatPredicate(predicatesSet.keys().next().value, level + 1);
|
||||
}
|
||||
return `${startCase(getUnionKey(predicate))} ${
|
||||
predicatesSet.size <= 3
|
||||
? `(${Array.from(predicatesSet)
|
||||
.map((p) => formatPredicate(p, level + 1))
|
||||
.join(', ')})`
|
||||
: predicatesSet.size
|
||||
}`;
|
||||
const res = Array.from(predicatesSet)
|
||||
.map((p) => formatPredicate(p, level + 1))
|
||||
.join(type === 'all_of' ? ' & ' : ' OR ');
|
||||
return level === 0 ? `(${res})` : res;
|
||||
}
|
||||
case 'condition': {
|
||||
const condition = predicate.condition;
|
||||
switch (getUnionKey(condition)) {
|
||||
case 'currency_is':
|
||||
return `currency: ${condition.currency_is.symbolic_code}`;
|
||||
case 'bin_data':
|
||||
return `bin_data: ${inlineJson(toJson(condition.bin_data), Infinity)}`;
|
||||
case 'category_is':
|
||||
return `category: #${condition.category_is.id}`;
|
||||
case 'cost_in':
|
||||
return `cost: ${
|
||||
getUnionKey(condition.cost_in.upper) === 'inclusive' ? '[' : '('
|
||||
}${formatCurrency(
|
||||
getUnionValue(condition.cost_in.upper)?.amount,
|
||||
getUnionValue(condition.cost_in.upper)?.currency?.symbolic_code,
|
||||
)}, ${formatCurrency(
|
||||
getUnionValue(condition.cost_in.lower)?.amount,
|
||||
getUnionValue(condition.cost_in.lower)?.currency?.symbolic_code,
|
||||
)}${getUnionKey(condition.cost_in.lower) === 'inclusive' ? ']' : ')'}`;
|
||||
case 'cost_is_multiple_of':
|
||||
return `cost_is_multiple: ${formatCurrency(
|
||||
condition.cost_is_multiple_of.amount,
|
||||
condition.cost_is_multiple_of.currency.symbolic_code,
|
||||
)}`;
|
||||
case 'identification_level_is':
|
||||
return `identification_level: ${condition.identification_level_is}`; // TODO: fix enum value
|
||||
case 'p2p_tool':
|
||||
return `p2p_tool: ${inlineJson(toJson(condition.p2p_tool), Infinity)}`;
|
||||
case 'party':
|
||||
return `party: ${inlineJson(toJson(condition.party), Infinity)}`;
|
||||
case 'payment_tool':
|
||||
return `payment_tool: ${inlineJson(toJson(condition.payment_tool), Infinity)}`;
|
||||
case 'payout_method_is':
|
||||
return `payout_method: #${condition.payout_method_is.id}`; // TODO: fix enum value
|
||||
case 'shop_location_is':
|
||||
return `shop_url: ${condition.shop_location_is.url}`;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
case 'condition':
|
||||
return startCase(getUnionKey(getUnionValue(predicate) as Predicate['condition']));
|
||||
case 'criterion':
|
||||
return `${startCase(getUnionKey(predicate))} #${predicate.criterion.id}`;
|
||||
case 'is_not': {
|
||||
if (getUnionKey(getUnionValue(predicate) as Predicate) !== 'is_not') {
|
||||
return `${getUnionKey(predicate)} ${formatPredicate(predicate.is_not, level + 1)}`;
|
||||
return `NOT ${formatPredicate(predicate.is_not, level + 1)}`;
|
||||
}
|
||||
break;
|
||||
return '';
|
||||
}
|
||||
}
|
||||
return startCase(getUnionKey(predicate));
|
||||
}
|
||||
|
6
src/app/shared/utils/table/format-rational.ts
Normal file
6
src/app/shared/utils/table/format-rational.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import { Rational } from '@vality/domain-proto/internal/base';
|
||||
import round from 'lodash-es/round';
|
||||
|
||||
export function formatRational(value: Rational) {
|
||||
return `${round((value.p / value.q) * 100, 4)}%`;
|
||||
}
|
@ -4,3 +4,6 @@ export * from './create-shop-column';
|
||||
export * from './create-predicate-column';
|
||||
export * from './create-failure-column';
|
||||
export * from './create-contract-column';
|
||||
export * from './format-cash-volume';
|
||||
export * from './format-rational';
|
||||
export * from './format-predicate';
|
||||
|
Loading…
Reference in New Issue
Block a user