diff --git a/cspell.json b/cspell.json
index 6253e8b1..3d80e0bd 100644
--- a/cspell.json
+++ b/cspell.json
@@ -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"]
}
diff --git a/package-lock.json b/package-lock.json
index f1ee6c5d..6c440345 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -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",
diff --git a/package.json b/package.json
index 4c4ff11c..9673e564 100644
--- a/package.json
+++ b/package.json
@@ -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",
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index 2f158b6e..c044910b 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -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,
+ },
],
[
{
diff --git a/src/app/sections/tariffs/components/cash-flows-selector-table/cash-flows-selector-table.component.html b/src/app/sections/tariffs/components/cash-flows-selector-table/cash-flows-selector-table.component.html
new file mode 100644
index 00000000..80b02b6c
--- /dev/null
+++ b/src/app/sections/tariffs/components/cash-flows-selector-table/cash-flows-selector-table.component.html
@@ -0,0 +1 @@
+
diff --git a/src/app/sections/tariffs/components/cash-flows-selector-table/cash-flows-selector-table.component.ts b/src/app/sections/tariffs/components/cash-flows-selector-table/cash-flows-selector-table.component.ts
new file mode 100644
index 00000000..df1cd33f
--- /dev/null
+++ b/src/app/sections/tariffs/components/cash-flows-selector-table/cash-flows-selector-table.component.ts
@@ -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();
+
+ columns: Column[] = [
+ {
+ 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(' + '),
+ },
+ ];
+}
diff --git a/src/app/sections/tariffs/components/shops-tariffs/shops-tariffs.component.html b/src/app/sections/tariffs/components/shops-tariffs/shops-tariffs.component.html
index 70fc6dda..058b7a8d 100644
--- a/src/app/sections/tariffs/components/shops-tariffs/shops-tariffs.component.html
+++ b/src/app/sections/tariffs/components/shops-tariffs/shops-tariffs.component.html
@@ -17,6 +17,10 @@
+
+
+ {{ rowData?.length }}
+
+ {{ item }}
+
+
diff --git a/src/app/sections/tariffs/components/shops-tariffs/shops-tariffs.component.ts b/src/app/sections/tariffs/components/shops-tariffs/shops-tariffs.component.ts
index 18a17458..ed152b60 100644
--- a/src/app/sections/tariffs/components/shops-tariffs/shops-tariffs.component.ts
+++ b/src/app/sections/tariffs/components/shops-tariffs/shops-tariffs.component.ts
@@ -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 &
{ 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[] = [
- createShopColumn('shop_id', (d) => d.owner_id),
+ createShopColumn('shop_id', (d) => d.owner_id, undefined, { pinned: 'left' }),
createPartyColumn('owner_id'),
createContractColumn(
(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,
@Inject(DEBOUNCE_TIME_MS) private debounceTimeMs: number,
private dr: DestroyRef,
+ private sidenavInfoService: SidenavInfoService,
) {}
ngOnInit() {
diff --git a/src/app/sections/tariffs/components/termsets-card/termsets-card.component.html b/src/app/sections/tariffs/components/termsets-card/termsets-card.component.html
new file mode 100644
index 00000000..6837d9fc
--- /dev/null
+++ b/src/app/sections/tariffs/components/termsets-card/termsets-card.component.html
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/app/sections/tariffs/components/termsets-card/termsets-card.component.ts b/src/app/sections/tariffs/components/termsets-card/termsets-card.component.ts
new file mode 100644
index 00000000..c7488e9a
--- /dev/null
+++ b/src/app/sections/tariffs/components/termsets-card/termsets-card.component.ts
@@ -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();
+ feesData = computed(
+ () =>
+ this.data()
+ ?.data?.term_sets?.map?.((t) => t?.terms?.payments?.fees)
+ ?.filter?.(Boolean),
+ );
+}
diff --git a/src/app/shared/utils/table/format-cash-volume.ts b/src/app/shared/utils/table/format-cash-volume.ts
new file mode 100644
index 00000000..bb10ff5e
--- /dev/null
+++ b/src/app/shared/utils/table/format-cash-volume.ts
@@ -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(', ')})`;
+ }
+}
diff --git a/src/app/shared/utils/table/format-predicate.ts b/src/app/shared/utils/table/format-predicate.ts
index 436b76d6..06db0044 100644
--- a/src/app/shared/utils/table/format-predicate.ts
+++ b/src/app/shared/utils/table/format-predicate.ts
@@ -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));
}
diff --git a/src/app/shared/utils/table/format-rational.ts b/src/app/shared/utils/table/format-rational.ts
new file mode 100644
index 00000000..aca373ba
--- /dev/null
+++ b/src/app/shared/utils/table/format-rational.ts
@@ -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)}%`;
+}
diff --git a/src/app/shared/utils/table/index.ts b/src/app/shared/utils/table/index.ts
index fde0d0b1..a196e171 100644
--- a/src/app/shared/utils/table/index.ts
+++ b/src/app/shared/utils/table/index.ts
@@ -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';