@1aboveio/skills 1.0.22 → 1.0.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.codex-plugin/plugin.json +1 -1
- package/.codex-plugin/resources.json +155 -136
- package/package.json +4 -4
- package/skills/app-dev/airflow/SKILL.md +1 -1
- package/skills/app-dev/android-development/SKILL.md +1 -1
- package/skills/app-dev/app-debug/SKILL.md +1 -1
- package/skills/app-dev/better-auth/SKILL.md +1 -1
- package/skills/app-dev/data-catalog/SKILL.md +1 -1
- package/skills/app-dev/data-quality/SKILL.md +1 -1
- package/skills/app-dev/ios-foundation/SKILL.md +1 -1
- package/skills/app-dev/maestro/SKILL.md +1 -1
- package/skills/app-dev/monorepo/SKILL.md +1 -1
- package/skills/app-dev/nextjs-fullstack/SKILL.md +1 -1
- package/skills/app-dev/prisma-setup/SKILL.md +1 -1
- package/skills/app-dev/pyspark/SKILL.md +1 -1
- package/skills/app-dev/python-backend/SKILL.md +1 -1
- package/skills/app-dev/shadcn/SKILL.md +1 -1
- package/skills/app-dev/zod-v4/SKILL.md +1 -1
- package/skills/cicd/cloud-build/SKILL.md +1 -1
- package/skills/cicd/cloud-debug/SKILL.md +1 -1
- package/skills/cicd/cloud-deploy/SKILL.md +1 -1
- package/skills/cicd/docker/SKILL.md +1 -1
- package/skills/cicd/github-actions/SKILL.md +1 -1
- package/skills/cicd/github-flow/SKILL.md +1 -1
- package/skills/cicd/google-cloud/SKILL.md +1 -1
- package/skills/cicd/mergify/SKILL.md +11 -3
- package/skills/cicd/podman/SKILL.md +1 -1
- package/skills/content/article-review/SKILL.md +1 -1
- package/skills/content/article-to-image/SKILL.md +1 -1
- package/skills/content/flowchart-svg/SKILL.md +1 -1
- package/skills/content/image-gen/SKILL.md +1 -1
- package/skills/content/mermaid/SKILL.md +1 -1
- package/skills/content/wechat-article-writer/SKILL.md +1 -1
- package/skills/content/wechat-publisher/SKILL.md +1 -1
- package/skills/content/wechat-search/SKILL.md +1 -1
- package/skills/content/xai-search/SKILL.md +1 -1
- package/skills/core/distill-writing/SKILL.md +1 -1
- package/skills/core/doc-to-md/SKILL.md +1 -1
- package/skills/core/editorial-review/SKILL.md +1 -1
- package/skills/core/house-style/SKILL.md +1 -1
- package/skills/core/plain-language/SKILL.md +1 -1
- package/skills/core/polish-cn/SKILL.md +1 -1
- package/skills/core/reg-parser/SKILL.md +1 -1
- package/skills/core/terminology/SKILL.md +1 -1
- package/skills/core/write-doc/SKILL.md +1 -1
- package/skills/core/write-report/SKILL.md +1 -1
- package/skills/engineering/better-review/SKILL.md +1 -1
- package/skills/engineering/e2e-test/SKILL.md +1 -1
- package/skills/engineering/engineering/SKILL.md +1 -1
- package/skills/engineering/engineering-runtime/SKILL.md +1 -1
- package/skills/engineering/engineering-runtime/coherence/workflow.json +13 -13
- package/skills/engineering/ensure-coverage/SKILL.md +1 -1
- package/skills/engineering/golden-path/SKILL.md +1 -1
- package/skills/engineering/harness-runtime/SKILL.md +1 -1
- package/skills/engineering/resolve-issues/SKILL.md +1 -1
- package/skills/engineering/resolve-release/SKILL.md +1 -1
- package/skills/engineering/rush-issues/SKILL.md +1 -1
- package/skills/engineering/rush-release/SKILL.md +1 -1
- package/skills/engineering/test-quality/SKILL.md +1 -1
- package/skills/risk-compliance/compliance-audit/SKILL.md +1 -1
- package/skills/risk-compliance/contract-compare/SKILL.md +1 -1
- package/skills/risk-compliance/contract-review/SKILL.md +1 -1
- package/skills/risk-compliance/doc-authenticity/SKILL.md +1 -1
- package/skills/risk-compliance/fraud-detection/SKILL.md +1 -1
- package/skills/risk-compliance/payment-analysis/SKILL.md +1 -1
- package/skills/risk-compliance/portfolio-payment-analysis/SKILL.md +76 -156
- package/skills/risk-compliance/portfolio-payment-analysis/references/metrics-and-reporting.md +37 -170
- package/skills/risk-compliance/portfolio-payment-analysis/references/risk-history.md +32 -15
- package/skills/risk-compliance/portfolio-payment-analysis/references/workflow-and-contracts.md +71 -0
- package/skills/risk-compliance/portfolio-payment-analysis/scripts/analyze.py +10 -1
- package/skills/risk-compliance/portfolio-payment-analysis/scripts/assets/charts.css +1 -0
- package/skills/risk-compliance/portfolio-payment-analysis/scripts/assets/charts.js +60 -0
- package/skills/risk-compliance/portfolio-payment-analysis/scripts/assets/tabs.js +2 -0
- package/skills/risk-compliance/portfolio-payment-analysis/scripts/build_report.py +28 -137
- package/skills/risk-compliance/portfolio-payment-analysis/scripts/echarts_report.py +43 -0
- package/skills/risk-compliance/portfolio-payment-analysis/scripts/interactive_report.py +3 -0
- package/skills/risk-compliance/portfolio-payment-analysis/scripts/issuer_data.py +52 -0
- package/skills/risk-compliance/portfolio-payment-analysis/scripts/payment_amounts.py +20 -0
- package/skills/risk-compliance/portfolio-payment-analysis/scripts/render.py +6 -157
- package/skills/risk-compliance/portfolio-payment-analysis/scripts/render_base.py +164 -0
- package/skills/risk-compliance/portfolio-payment-analysis/scripts/render_custom.py +30 -0
- package/skills/risk-compliance/portfolio-payment-analysis/scripts/report_data.py +39 -0
- package/skills/risk-compliance/portfolio-payment-analysis/scripts/report_model.py +45 -0
- package/skills/risk-compliance/portfolio-payment-analysis/scripts/risk_dual.py +54 -0
- package/skills/risk-compliance/portfolio-payment-analysis/scripts/vamp_detail.py +27 -0
- package/skills/risk-compliance/portfolio-payment-analysis/scripts/vamp_opened_disputes.py +30 -0
- package/skills/risk-compliance/portfolio-payment-analysis/scripts/vendor/LICENSE +222 -0
- package/skills/risk-compliance/portfolio-payment-analysis/scripts/vendor/NOTICE +5 -0
- package/skills/risk-compliance/portfolio-payment-analysis/scripts/vendor/echarts-provenance.json +6 -0
- package/skills/risk-compliance/portfolio-payment-analysis/scripts/vendor/echarts.min.js +45 -0
- package/skills/risk-compliance/similar-domain/SKILL.md +1 -1
- package/skills/risk-compliance/tld-detection/SKILL.md +1 -1
package/skills/risk-compliance/portfolio-payment-analysis/references/metrics-and-reporting.md
CHANGED
|
@@ -1,170 +1,37 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
authorization date and daily USD rates from the base FX contract. Never substitute
|
|
39
|
-
settlement amount. Missing approved amounts, currencies or rates make the affected
|
|
40
|
-
sum unavailable; no auth observations also yield an unavailable sum. Observed auths with no approvals have
|
|
41
|
-
an observed approved amount of zero. Keep the merchant's numerator and denominator together.
|
|
42
|
-
Use authorization time, settlement date and dispute date consistently with the base
|
|
43
|
-
metrics. Missing layer observations or FX make affected measures unavailable, not
|
|
44
|
-
zero growth or confirmed zero activity.
|
|
45
|
-
|
|
46
|
-
Week-over-week changes use only the immediately preceding calendar week. Both weeks
|
|
47
|
-
must be full seven-day bins. Compare only measures whose layer has observations in
|
|
48
|
-
both weeks; relative changes additionally require a positive previous denominator.
|
|
49
|
-
Attempt and sales changes are `(current / previous) - 1`; approval-rate changes are
|
|
50
|
-
`100 * (current rate - previous rate)` percentage points. Do not average merchants,
|
|
51
|
-
bridge a missing week, infer onboarding from a first observation, or report portfolio
|
|
52
|
-
trends. A full calendar week does not prove continuous export coverage: all changes
|
|
53
|
-
remain descriptive comparisons of the supplied observations.
|
|
54
|
-
|
|
55
|
-
## Merchant detail breakdowns
|
|
56
|
-
|
|
57
|
-
Section 3 provides the same breakdown suite for every merchant, comparing only this
|
|
58
|
-
week with last week. This week is the Monday-based week containing the effective
|
|
59
|
-
cutoff; compare the included weekdays with the same weekdays seven days earlier.
|
|
60
|
-
For example, a Friday cutoff compares Monday–Friday with the preceding Monday–Friday,
|
|
61
|
-
not a five-day period with a seven-day period. Restrict both populations to the
|
|
62
|
-
requested main-period source scope and disclose unavailable earlier observations.
|
|
63
|
-
Show the exact two date ranges in every chart legend and supporting table. Section 2
|
|
64
|
-
continues to own the full weekly history; no main-period-total or older-week columns
|
|
65
|
-
belong in section 3.
|
|
66
|
-
|
|
67
|
-
Charts are the primary presentation, with these fixed layouts:
|
|
68
|
-
|
|
69
|
-
- BIN country and card product: compact paired bar charts; two differently colored
|
|
70
|
-
bars per category compare the weeks for every metric, including rates.
|
|
71
|
-
- Amount buckets: two histograms side by side, one color per week, using the same
|
|
72
|
-
six bins and y-axis scale. Default to count; counts and USD weights can be selected,
|
|
73
|
-
but not approval-rate/share metrics. The unequal/open-ended amount bands are shown
|
|
74
|
-
as equal-width categories, explicitly labeled as binned values rather than density.
|
|
75
|
-
Unknown amounts are listed separately, never drawn as a numeric interval.
|
|
76
|
-
- Unsuccessful response reasons: two bar charts side by side, one color per week,
|
|
77
|
-
with identical category order and scale. Do not re-rank or rescale each week.
|
|
78
|
-
- Authentication: paired pies for the aligned weeks, with count and USD selectors
|
|
79
|
-
only. If any category value is unavailable or negative, do not draw that period's
|
|
80
|
-
pie from an incomplete sum. Keep the supporting cells blank.
|
|
81
|
-
|
|
82
|
-
Do not include settlement sales/refund charts in section 3. Sections 1 and 2 still
|
|
83
|
-
retain their settlement metrics. A metric selector switches supported measures,
|
|
84
|
-
including successful authorization amount USD where applicable. Render unavailable cells
|
|
85
|
-
blank and chart values as gaps, not zeros. Do not repeat a metric label in a cell whose
|
|
86
|
-
column header already names that measure: use `USD 33...`, not `授权成功金额 USD 33...`.
|
|
87
|
-
A packed week cell that also shows attempts and a rate must say `授权成功 USD`, because the
|
|
88
|
-
report also shows settled sales. Use the shared
|
|
89
|
-
`comparison_charts.py` for all report charts. Expandable supporting tables keep metrics as rows and the two periods
|
|
90
|
-
as columns. HTML is self-contained with no external chart service; Markdown retains
|
|
91
|
-
the supporting values.
|
|
92
|
-
Derive groupings only from the merchant's authorized attempts in those two periods;
|
|
93
|
-
do not join settlement amounts into authorization groups.
|
|
94
|
-
|
|
95
|
-
- BIN country: `Issuing bank country`; keep the merchant's top ten by attempts across
|
|
96
|
-
the two comparison periods, `Other` for the rest, and a separate unknown group.
|
|
97
|
-
- Card product: `Payment method` × `Card type`, following payment-analysis. Preserve
|
|
98
|
-
every observed combination and unknown components.
|
|
99
|
-
- Amount bucket: use each attempt's `Transaction amount` converted to USD by its
|
|
100
|
-
authorization date. Fixed half-open ranges are `[0,25)`, `[25,50)`, `[50,100)`,
|
|
101
|
-
`[100,200)`, `[200,500)`, `[500,+inf)`. Missing, nonfinite, negative or unconvertible
|
|
102
|
-
amounts belong to unknown; never silently put them into the zero bucket.
|
|
103
|
-
- Authentication: every observed `Authentication method` value, with unknown separate.
|
|
104
|
-
- Unsuccessful response: code × message among non-approvals only. Keep the merchant's
|
|
105
|
-
top ten reasons across the two comparison periods, Other and unknown. The share denominator is
|
|
106
|
-
that merchant's non-approvals, not all attempts. Unknown responses are not confirmed
|
|
107
|
-
issuer declines or fraud.
|
|
108
|
-
|
|
109
|
-
Freeze group membership from the union of the two comparison periods before calculating each.
|
|
110
|
-
Never re-rank each period independently. Synthetic Other/unknown have distinct keys and display
|
|
111
|
-
labels from literal source strings with those names. For all-attempt groups show
|
|
112
|
-
count, share of merchant attempts, approvals, rate, attempt amount USD and approved
|
|
113
|
-
amount USD. For non-approval reasons show count, share of merchant non-approvals and
|
|
114
|
-
unsuccessful amount USD. Group counts and amounts reconcile to the corresponding
|
|
115
|
-
merchant population; a zero denominator is unavailable, not zero percent.
|
|
116
|
-
|
|
117
|
-
Missing merchant-week observations remain blank. A group absent from an observed
|
|
118
|
-
merchant-week may have zero observed counts, but no approval rate when its count is
|
|
119
|
-
zero. Missing amount or FX invalidates the affected amount sum, not the count.
|
|
120
|
-
Export the two-period group values in `merchant-detail-comparison.csv` for review.
|
|
121
|
-
Add per-merchant dispute and fraud history and monthly VAMP charts using the periods,
|
|
122
|
-
series and availability rules in [risk history](risk-history.md). Use 340FRD file
|
|
123
|
-
generation dates as report dates. Monthly charts switch between counts and rates;
|
|
124
|
-
rate tables and `merchant-risk-history.csv` expose numerator, denominator and its
|
|
125
|
-
month. Keep weekly counts without inventing a weekly monitoring rate. Mastercard
|
|
126
|
-
EFM rates use fraud chargebacks, not the 340FRD count series. These are not
|
|
127
|
-
authorization-cohort metrics. Settlement sales/refunds are omitted from section 3.
|
|
128
|
-
|
|
129
|
-
## Chinese report structure
|
|
130
|
-
|
|
131
|
-
Use this template for editorial review in place of the base report's chapter order
|
|
132
|
-
and Sankey. Do not add portfolio summary, trends, change decomposition, funnel or
|
|
133
|
-
overall dispute chapters.
|
|
134
|
-
|
|
135
|
-
- Scope header: title, period, effective cutoff, sources and timezone/coverage caveats;
|
|
136
|
-
include a comparison period only when a merchant comparison across periods is requested.
|
|
137
|
-
- 商户要点: merchant-specific findings with counts, denominators and date basis. The lowest authorization rate chooses an authorization review, not the investigation order. If fraud or predispute history would change who is checked first, name that merchant and month here, and say whether the month is inside the report window. A user-excluded historical merchant appears once as an exclusion, not as a finding, and is absent from the tables.
|
|
138
|
-
- 1. 子商户比较: stable IDs and display labels, merchant-level attempts, approvals,
|
|
139
|
-
rates, settlement, refunds and observed disputes where supported. Keep small samples
|
|
140
|
-
visible and unavailable values distinct from zero.
|
|
141
|
-
- 2. 子商户逐周对比: a merchant-by-week matrix of approvals/attempts and rates,
|
|
142
|
-
followed by per-merchant tables with date/week columns horizontally and metrics
|
|
143
|
-
vertically, including successful authorization amount USD as a separate row from
|
|
144
|
-
settled sales. Preserve the same orientation for selected and collapsed merchants.
|
|
145
|
-
Keep all merchants reachable; default detail selections may be expanded while other
|
|
146
|
-
merchant weekly tables are collapsed. Explain partial weeks and unavailable changes.
|
|
147
|
-
- 3. 重点子商户: the full merchant breakdown suite above, using interactive this-week
|
|
148
|
-
versus last-week charts and two-period supporting tables. No report-period total
|
|
149
|
-
or older weeks in the two-week comparisons. Risk history uses its own longer windows.
|
|
150
|
-
Priority merchants are expanded, and all other merchants remain accessible in
|
|
151
|
-
collapsed sections, including when merchants are explicitly selected. Default priority means the largest by attempts and
|
|
152
|
-
the lowest authorization rate among merchants with at least 30 attempts; this is
|
|
153
|
-
a navigation rule, not a significance test. Keep source limitations beside the
|
|
154
|
-
affected merchant and show group denominator definitions once. Remove extra explanatory
|
|
155
|
-
prose; keep only limitations needed to interpret the evidence.
|
|
156
|
-
- 4. 商户行动表: identify the merchant, evidence and next investigation for each row;
|
|
157
|
-
distinguish hypotheses from facts. Include supported historical refund and dispute
|
|
158
|
-
follow-ups for the relevant merchant, not portfolio history.
|
|
159
|
-
- 5. 归因与覆盖: sources, identity, deduplication, conflicts, unknowns, join coverage,
|
|
160
|
-
FX and unresolved attribution limits. Link supporting CSV/JSON reconciliation
|
|
161
|
-
evidence without presenting aggregate totals in the report or exposing cardholder data.
|
|
162
|
-
|
|
163
|
-
## Done when
|
|
164
|
-
|
|
165
|
-
A reader can identify which merchants to investigate and why, including a stated historical exclusion; each rate identifies
|
|
166
|
-
its merchant, population and period; unsupported data is not presented as zero; and
|
|
167
|
-
merchant attribution does not cross identity boundaries. Internal reconciliation
|
|
168
|
-
accounts for unknowns and conflicts without advertising aggregate fields as deliverables.
|
|
169
|
-
The Markdown and HTML use the same reviewed narrative and separate verified findings
|
|
170
|
-
from operational hypotheses.
|
|
1
|
+
# Merchant report layout and interaction
|
|
2
|
+
|
|
3
|
+
Use this template in `write-report` and editorial review. Internal calculations may retain weekly tables, reconciliation and actions; the reader-facing report has only the two sections below. User instructions can override presentation without changing event identity or arithmetic.
|
|
4
|
+
|
|
5
|
+
## 1. 子商户对比
|
|
6
|
+
|
|
7
|
+
Show every verified merchant, including historical merchants with no current observations, unless explicitly excluded. Keep unverified/unknown identities in reconciliation evidence, not as merchant rows or tabs. Columns: merchant, stable merchant ID, authorization attempts, approvals, approval rate, daily attempt-count sparkline, approved authorization amount USD, refund count, refund amount USD and dispute observation count (display label `争议笔数`). The dispute column remains an observation metric, not an official opening count. Explain that distinction beside the report's scope when material.
|
|
8
|
+
|
|
9
|
+
Amounts in the approved column use approved authorization attempts and authorization-date daily USD conversion. Never substitute settled sales. Missing authorization observations leave count/rate/amount cells blank; observed attempts with no approvals have zero approved amount and a zero approval rate. Zero attempts have an undefined rate.
|
|
10
|
+
|
|
11
|
+
Sparklines show daily authorization attempt counts for the two detail periods, not approval rates. Use a common zero-based scale across merchants. Preserve absent days as gaps and never connect across them. Tooltip shows the date and count. No merchant-search control or sample-note column.
|
|
12
|
+
|
|
13
|
+
## 3. 重点子商户详情
|
|
14
|
+
|
|
15
|
+
One tab per merchant; all merchants are reachable. Detail selection controls the initial tab only, not which merchants are present. Keep tab focus and arrow/Home/End keyboard navigation, resize charts after a hidden tab becomes visible, and print all tabs.
|
|
16
|
+
|
|
17
|
+
At each tab's top, show the latest eight Monday–Sunday calendar weeks ending at the authorization cutoff (the earlier of requested end and last observed authorization date). Draw only weeks containing that merchant's observed authorization attempts; fewer observed weeks produce fewer categories. Count is a line on the right axis; attempt amount USD is a bar on the left axis. Calculate with the same daily FX rules. Missing FX leaves amount unavailable while counts remain visible. A merchant with no observations has an explicit empty state.
|
|
18
|
+
|
|
19
|
+
Below that, compare the week containing the authorization cutoff with the same weekdays one week earlier, even when the earlier period lies outside the main report start. Read those authorized historical source rows instead of creating false prior-period blanks. Preserve both period placeholders when observations are absent. Main comparison-table and risk-history dates use the common cutoff; authorization sparklines, details and eight-week trends use the independent authorization cutoff. A settlement cutoff must not truncate available weekend authorization records. State both cutoffs. Calculate groups across the union of both periods.
|
|
20
|
+
|
|
21
|
+
- BIN country: top ten by two-period attempts, Other and unknown separate. Display only approval rate with `approvals/attempts` labels. Clicking a real country expands/collapses its top five issuing banks inline; rank banks by **current-period** attempts descending, ties by bank name. Include only banks with positive current attempts. Compare the same selected banks against the previous period. Unknown bank labels remain explicit; do not merge similarly spelled issuers without a verified dictionary. Other is an aggregate, not a country. Keep denominator and missing-data rules for bank rows; unknown current/previous observations are gaps, while attempts with zero approvals have a genuine 0%.
|
|
22
|
+
- Card product: payment method × card type. Approval rate only, with approvals/attempts labels and no metric selector.
|
|
23
|
+
- Amount buckets: `[0,25)`, `[25,50)`, `[50,100)`, `[100,200)`, `[200,500)`, `[500,+inf)` USD; two histograms with shared ranges/scales. Unknown or unconvertible amounts remain separate.
|
|
24
|
+
- Authentication: paired pies for counts or amounts; an incomplete amount sum leaves that period's pie unavailable.
|
|
25
|
+
- Non-approval reasons: code × message, top ten across both periods, Other and unknown separate; two side-by-side bar panels. Shares use that merchant's non-approvals.
|
|
26
|
+
|
|
27
|
+
No weekly summary table, action-list chapter, coverage chapter, per-tab two-week summary table, daily-record expansion, chart-data expansion or VAMP component table appears in the business report. Keep calculation details and reconciliation in linked supporting artifacts. Necessary date, denominator and estimate limitations stay near the affected plot.
|
|
28
|
+
|
|
29
|
+
## Risk charts
|
|
30
|
+
|
|
31
|
+
Every merchant has five simultaneous dual-axis charts: monthly and weekly disputes, monthly and weekly fraud, and monthly VAMP extract estimate. Counts are bars on the left; rates are lines on the right. No count/rate toggle. Keep twelve month positions and eight week positions; gaps remain gaps. Refer to [risk-history.md](risk-history.md) for numerator/denominator semantics.
|
|
32
|
+
|
|
33
|
+
ECharts is bundled locally and embedded with its license/NOTICE into a single HTML. No CDN or runtime data fetch. Hover/touch tooltips show period, full series label, count, ratio and numerator/denominator where available. Legends can toggle series. Preserve responsive horizontal scrolling for dense charts and resize only when containers change; do not reset chart options on every hover. Use SVG rendering for portable printing.
|
|
34
|
+
|
|
35
|
+
## Verification
|
|
36
|
+
|
|
37
|
+
Reconcile merchant/group counts and approved amounts to their source populations. Verify all chart series against `presentation.json`; inspect real rendered scales, dual-axis units, count/rate tooltips, undefined rates, missing FX, empty history, tab switching, current-week issuer ranking and expand/collapse. Exercise the saved HTML with network disabled and verify zero external requests. Only synthetic fixtures belong in the skill repository.
|
|
@@ -4,7 +4,11 @@ Use the common effective cutoff. Retain 12 calendar months and eight Monday–Su
|
|
|
4
4
|
weeks, with missing periods blank and the final period partial where applicable.
|
|
5
5
|
Every merchant has dispute and fraud counts. Monthly charts also offer a rate view,
|
|
6
6
|
with numerator, denominator and denominator month in the expandable table and CSV.
|
|
7
|
-
Weekly charts
|
|
7
|
+
Weekly charts include descriptive same-week rates: observed numerator divided by
|
|
8
|
+
same-merchant, same-brand settled sales by transaction/capture date in that week.
|
|
9
|
+
These are not weekly network monitoring rates. Missing numerator/denominator is a gap;
|
|
10
|
+
zero denominators are undefined. Monthly programme-aligned rate definitions below
|
|
11
|
+
remain separate from these descriptive weekly rates.
|
|
8
12
|
All charts use `comparison_charts.py`; nulls are gaps, never fabricated zeros.
|
|
9
13
|
|
|
10
14
|
## 340FRD reporting date and attribution
|
|
@@ -35,10 +39,12 @@ incidence. RDR and formal escalation can each appear in their respective series.
|
|
|
35
39
|
Conflicting identities/brands are quarantined; unknown stages remain unresolved.
|
|
36
40
|
|
|
37
41
|
A monitoring-style chargeback numerator needs an opening and its received date.
|
|
38
|
-
`First CB Request`
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
+
`First CB Request` establishes an explicit received opening date. Response-, loss- and
|
|
43
|
+
reversal-stage records also establish that a formal dispute already opened: count that
|
|
44
|
+
case once regardless of whether it is pending or resolved. When the original request is
|
|
45
|
+
absent, extract estimates use the earliest supplied formal-stage observation date and
|
|
46
|
+
state that this is not a verified network processing month. RDR-only cases are excluded.
|
|
47
|
+
Conflicting merchant, provider or brand identities remain quarantined.
|
|
42
48
|
|
|
43
49
|
## Monthly count-rate formulas
|
|
44
50
|
|
|
@@ -70,16 +76,27 @@ each rate. Do not infer continuous coverage from min/max event dates.
|
|
|
70
76
|
|
|
71
77
|
## VAMP
|
|
72
78
|
|
|
73
|
-
The
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
79
|
+
The Visa portion of DART-340 is TC40. Use its file-generation reporting month and
|
|
80
|
+
existing exact-repeat handling; Mastercard DART records are not Visa TC40.
|
|
81
|
+
|
|
82
|
+
Display **VAMP(导出估算)** as `(attributable Visa TC40 records + opened formal
|
|
83
|
+
Visa disputes) / same-merchant same-month Visa settled Sale count`. Formal dispute
|
|
84
|
+
status need not be resolved: count an opened case once, including response, loss or
|
|
85
|
+
reversal records. Exclude RDR-only cases from the formal dispute numerator; RDR does
|
|
86
|
+
not automatically erase related TC40. Do not deduplicate across TC40 and TC15.
|
|
87
|
+
|
|
88
|
+
Use the earliest supplied formal-stage date when the original request is absent.
|
|
89
|
+
The sales denominator uses the existing transaction/capture-month evidence. Expose
|
|
90
|
+
these date choices beside the plot and in supporting evidence. These are extract
|
|
91
|
+
estimates, not certification of TC15/TC05 processing months, corrections, CE3.0 or
|
|
92
|
+
other exclusions, VisaNet CNP eligibility, complete scope, or descriptor/acquirer/
|
|
93
|
+
market grouping. Official network VAMP remains unavailable until those are verified.
|
|
94
|
+
|
|
95
|
+
Draw VAMP numerator counts as left-axis bars and the estimate as a right-axis line.
|
|
96
|
+
Keep all twelve months. Missing either numerator component or a positive denominator
|
|
97
|
+
leaves the ratio blank; never replace absent TC40 with zero. Genuine zero ratios may
|
|
98
|
+
be retained in supporting records while omitted as plotted rate points. Every plotted
|
|
99
|
+
ratio has an explicit numerator, denominator and denominator period.
|
|
83
100
|
|
|
84
101
|
## Sources
|
|
85
102
|
|
package/skills/risk-compliance/portfolio-payment-analysis/references/workflow-and-contracts.md
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Workflow and module contracts
|
|
2
|
+
|
|
3
|
+
The skill serves payment portfolios whose records can be attributed to submerchants.
|
|
4
|
+
Worldpay CSV and DART-340 are the bundled source adapters. Other sources require an
|
|
5
|
+
explicit field/identity mapping into the same event layers; a similar descriptor is
|
|
6
|
+
not such a mapping. Keep source-specific interpretation in preparation, financial
|
|
7
|
+
arithmetic in calculation, report selection in construction, and browser behavior in
|
|
8
|
+
rendering.
|
|
9
|
+
|
|
10
|
+
## Stages and completion criteria
|
|
11
|
+
|
|
12
|
+
| Stage | Input → output | Complete when |
|
|
13
|
+
| --- | --- | --- |
|
|
14
|
+
| Scope | Request and authorized files → dates, identities, coverage | Main, authorization and risk periods are explicit; missing layers and identity conflicts are recorded. |
|
|
15
|
+
| Prepare | Original or selected sources → verified event layers | Source hashes and selected-row accounting reconcile; authorization, sale/refund and dispute grains are distinct; unknown identities remain unknown. |
|
|
16
|
+
| Calculate | Event layers and daily FX → `facts.json` and business CSVs | Merchant numerators, denominators, event dates, currency conversions, nulls and observed zeros are reproducible without a chart library. |
|
|
17
|
+
| Construct | Frozen facts and merchant selection → `presentation.json` | Both report sections and each selected merchant's charts reference the correct evidence and independent cutoff; exclusions apply to every business output. |
|
|
18
|
+
| Review | Saved presentation and evidence → reviewed presentation | Findings, coverage and terminology are checked; required language checks are recorded honestly, with no changes to financial facts. |
|
|
19
|
+
| Render | Reviewed presentation → HTML and Markdown companion | HTML embeds ECharts and notices, loads offline, and shows all tabs and supported chart interactions. Rendering does not load payment sources or recalculate metrics. |
|
|
20
|
+
| Verify | Saved outputs → verification record | Numeric series, missing values, cutoffs, issuer ranking, actual tooltips, tab resizing and printing have evidence; incomplete checks are named. |
|
|
21
|
+
|
|
22
|
+
## Report structure
|
|
23
|
+
|
|
24
|
+
The default compact merchant report contains findings followed by a merchant comparison
|
|
25
|
+
and merchant-detail tabs. The legacy visible headings `1. 子商户对比` and
|
|
26
|
+
`3. 重点子商户详情` are retained for compatibility; their numbering does not imply
|
|
27
|
+
hidden chapters. All verified merchants remain reachable. An initial-tab selection is
|
|
28
|
+
navigation, while exclusion changes the report population. Unknown identities stay in
|
|
29
|
+
reconciliation, outside merchant tabs.
|
|
30
|
+
|
|
31
|
+
Comparison: main-period attempts, approvals, approval rate, approved authorization USD,
|
|
32
|
+
refunds, dispute observations and daily attempt-count sparklines. Details: up to eight
|
|
33
|
+
observed authorization weeks; two-week country, product, amount, authentication and
|
|
34
|
+
response comparisons; twelve-month/eight-week risk histories. Exact chart and metric
|
|
35
|
+
contracts are in [metrics and reporting](metrics-and-reporting.md) and
|
|
36
|
+
[risk history](risk-history.md).
|
|
37
|
+
|
|
38
|
+
## Dates and missing data
|
|
39
|
+
|
|
40
|
+
Main comparison and risk history share the earliest relevant layer's final event date.
|
|
41
|
+
Authorization-only details and trends use the earlier of requested end and final
|
|
42
|
+
authorization date. Paired authorization periods match weekdays across adjacent weeks;
|
|
43
|
+
missing dates retain positions. No full period or continuous coverage is inferred from
|
|
44
|
+
minimum/maximum timestamps. A null is unavailable, not zero. A zero approval numerator
|
|
45
|
+
with positive attempts is 0%; no attempts has no defined approval rate. Missing FX
|
|
46
|
+
invalidates the affected amount, not its count.
|
|
47
|
+
|
|
48
|
+
## Script responsibilities
|
|
49
|
+
|
|
50
|
+
- `analyze.py` owns source loading, verified merchant mapping and reconciliation; `report_data.py`
|
|
51
|
+
derives reusable aggregate evidence from those event layers.
|
|
52
|
+
- `payment_amounts.py` owns shared complete-or-unavailable daily USD sums.
|
|
53
|
+
- Preparation accepts calculation functions explicitly and returns JSON-compatible
|
|
54
|
+
aggregates. It does not import the report builder or construct chart containers.
|
|
55
|
+
- `report_model.py` accepts frozen aggregate evidence, explicit risk denominators
|
|
56
|
+
and dispute counts. It does not retain mutable process-wide calculation state.
|
|
57
|
+
- `build_report.py` owns findings, selections and business exports; its model module
|
|
58
|
+
defines the report structure and chart declarations.
|
|
59
|
+
- `render.py` is the stable CLI. `render_base.py` and `render_custom.py` compose
|
|
60
|
+
the document and tabs; `echarts_report.py` creates chart declarations and embeds
|
|
61
|
+
`scripts/assets/charts.js`, `charts.css` and `tabs.js`. The HTML shell receives chart and cell rendering
|
|
62
|
+
functions explicitly. ECharts reads only chart declarations and serializes safely;
|
|
63
|
+
JavaScript chart code and styles are packaged assets, not payment calculators.
|
|
64
|
+
|
|
65
|
+
## Extension policy
|
|
66
|
+
|
|
67
|
+
Add a source adapter when a supported source needs different parsing or identity rules.
|
|
68
|
+
Add a metric only with its population, date basis, numerator, denominator, currency and
|
|
69
|
+
availability rule. Add a chart only after its declared series exist in frozen facts.
|
|
70
|
+
Do not add arbitrary report configuration for a hypothetical use case. Keep the
|
|
71
|
+
validated default report and CLI stable while internal modules become reusable.
|
|
@@ -597,7 +597,16 @@ def analyze(root, start, end, fxpath):
|
|
|
597
597
|
'timezone': 'Source timestamps are naive; timezone is unknown'}
|
|
598
598
|
merchant_risk_history, reconciliation['risk_history'] = risk_history.build(
|
|
599
599
|
root, end, comparison_merchants, layers['settlement'], layers['dispute'])
|
|
600
|
-
|
|
600
|
+
import report_data, sys
|
|
601
|
+
interactive_merchants=list(merchants)
|
|
602
|
+
for mid in all_ids:
|
|
603
|
+
if mid in ids:
|
|
604
|
+
continue
|
|
605
|
+
label=sorted(names[mid],key=lambda x:(-names[mid][x],x))[0] if names[mid] else 'Unknown'
|
|
606
|
+
interactive_merchants.append({'id':mid or None,'name':label,**summary([],[],[],rates,present)})
|
|
607
|
+
auth_end=min(requested_end,date.fromisoformat(last_dates['auth']))
|
|
608
|
+
interactive = report_data.calculate(layers, end, interactive_merchants, rates, sys.modules[__name__],auth_end=auth_end)
|
|
609
|
+
return {'schema_version': 1, 'interactive_report': interactive, 'metadata': {'main_start': start.isoformat(), 'main_end': end.isoformat(),
|
|
601
610
|
'requested_main_end': requested_end.isoformat(), 'layers_present': present, 'layer_last_dates': last_dates,
|
|
602
611
|
'comparison_start': previous_start.isoformat(), 'comparison_end': previous_end.isoformat(),
|
|
603
612
|
'auth_date': 'Timestamp', 'settlement_date': 'Settlement date', 'dispute_denominator_date': 'Timestamp (transaction/capture date)', 'fx_date': 'Timestamp', 'rate_unit': 'fraction'},
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.ec-block{margin:28px 0;min-width:0}.ec-block h3{margin-bottom:10px}.ec-control{display:block;margin:12px 0}.ec-control select{font:inherit;min-height:44px;padding:6px 10px}.ec-scroll{overflow-x:auto}.ec-host{width:100%;height:430px;min-width:640px}.ec-spark{width:168px}.ec-spark .ec-host{width:168px;min-width:168px;height:44px}.ec-spark-dates{font-size:12px;color:#536770}.ec-tooltip{font-variant-numeric:tabular-nums;line-height:1.7}.ec-tooltip b{font-weight:650}.ec-block .ec-host:focus-visible{outline:2px solid #c16613}.ec-tooltip-row{white-space:normal}@media(max-width:700px){.ec-host{min-width:760px}.ec-spark .ec-host{min-width:168px}.ec-block[data-ec-pie] .ec-host{min-width:550px}}@media print{.ec-control{display:none}.ec-scroll{overflow:visible}.ec-host{min-width:0!important}.ec-block{break-inside:avoid}}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
(()=>{
|
|
2
|
+
const colors=['#087b71','#7761ad','#a65721','#4f7489','#8b7135','#6e8178'];const states=[];
|
|
3
|
+
const esc=x=>String(x??'').replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));
|
|
4
|
+
const value=v=>v===null||v===undefined||v===''?null:Number(v);
|
|
5
|
+
const fmt=(v,f='count')=>v===null||v===undefined?'—':f==='percent'?(Number(v)*100).toFixed(2)+'%':(f==='usd'?'USD ':'')+Number(v).toLocaleString('en-US',{minimumFractionDigits:f==='usd'?2:0,maximumFractionDigits:f==='usd'?2:0});
|
|
6
|
+
const tooltip={trigger:'axis',showDelay:0,hideDelay:0,transitionDuration:0,appendTo:()=>document.body,confine:false,backgroundColor:'#142c36',borderColor:'#36545b',textStyle:{color:'#fff',fontSize:13},extraCssText:'max-width:min(460px,calc(100vw - 24px));white-space:normal;line-height:1.7;z-index:10000;',axisPointer:{type:'cross'},className:'ec-tooltip'};
|
|
7
|
+
const wrap=(head,lines)=>'<b>'+esc(head)+'</b><br>'+lines.map(esc).join('<br>');
|
|
8
|
+
const base=()=>({animation:false,color:colors,aria:{enabled:true},textStyle:{fontFamily:"-apple-system,BlinkMacSystemFont,'PingFang SC',sans-serif"},legend:{type:'scroll',top:0,textStyle:{color:'#344a51'}},grid:{left:80,right:90,top:78,bottom:62},tooltip:{...tooltip},xAxis:{type:'category',axisLabel:{hideOverlap:true,color:'#536770'}},yAxis:{type:'value',min:0,axisLabel:{color:'#536770'},splitLine:{lineStyle:{color:'#e2eae6'}}}});
|
|
9
|
+
function option(state){
|
|
10
|
+
const d=state.data,o=base();state.rows=[];
|
|
11
|
+
if(d.type==='sparkline'){
|
|
12
|
+
o.legend={show:false};o.grid={left:4,right:4,top:4,bottom:4};o.xAxis={type:'category',show:false,boundaryGap:false,data:d.points.map(p=>p.date)};o.yAxis={type:'value',show:false,min:0,max:d.max||1};o.series=[{name:'每日授权笔数',type:'line',connectNulls:false,symbolSize:3,lineStyle:{width:2},data:d.points.map(p=>p.attempts)}];o.tooltip={...tooltip,axisPointer:{type:'line'},formatter:ps=>{const p=d.points[ps[0]?.dataIndex];return p?wrap(p.date,['授权尝试:'+fmt(p.attempts)+' 笔']):'';}};return o;
|
|
13
|
+
}
|
|
14
|
+
if(d.type==='risk_dual_axis'){
|
|
15
|
+
o.xAxis.data=d.points.map(p=>p.label+(p.partial?'*':''));o.yAxis=[{type:'value',name:'数量(笔)',min:0,minInterval:1,splitLine:{lineStyle:{color:'#e2eae6'}}},{type:'value',name:'比率 %',min:0,axisLabel:{formatter:v=>fmt(v,'percent')},splitLine:{show:false}}];
|
|
16
|
+
o.series=d.count_series.map((s,i)=>({name:s.label+' · 数量',type:'bar',yAxisIndex:0,barMaxWidth:26,itemStyle:{color:colors[i%colors.length],opacity:.55},data:d.points.map(p=>p.counts[s.key])})).concat(d.rate_series.map((s,i)=>({name:s.label+' · 比率',type:'line',yAxisIndex:1,connectNulls:false,symbolSize:6,itemStyle:{color:colors[i%colors.length]},data:d.points.map(p=>{const v=p.rates[s.key];return v!==null&&v>0?v:null;})})));
|
|
17
|
+
o.tooltip.formatter=ps=>{const p=d.points[ps[0]?.dataIndex];if(!p)return '';const lines=d.count_series.map(s=>s.label+':'+fmt(p.counts[s.key])+' 笔');d.rate_series.forEach(s=>{const v=p.rates[s.key],detail=p.details[s.key]||{};lines.push(s.label+':'+fmt(v,'percent')+(detail.numerator!==null&&detail.numerator!==undefined&&detail.denominator?' ('+fmt(detail.numerator)+' / '+fmt(detail.denominator)+')':''));});return wrap(p.start+' 至 '+p.end+(p.partial?'(不完整期间)':''),lines);};return o;
|
|
18
|
+
}
|
|
19
|
+
if(d.type==='weekly_transaction_trend'){
|
|
20
|
+
o.xAxis.data=d.points.map(p=>p.start.slice(5)+' 至 '+p.end.slice(5));o.yAxis=[{type:'value',min:0,name:'授权尝试金额 USD',axisLabel:{formatter:v=>v>=1000?(v/1000).toLocaleString()+'k':v},splitLine:{lineStyle:{color:'#e2eae6'}}},{type:'value',min:0,minInterval:1,name:'授权尝试笔数',splitLine:{show:false}}];o.series=[{name:'授权尝试金额 USD',type:'bar',yAxisIndex:0,barMaxWidth:50,data:d.points.map(p=>value(p.amount_usd))},{name:'授权尝试笔数',type:'line',yAxisIndex:1,connectNulls:false,data:d.points.map(p=>p.count)}];o.tooltip.formatter=ps=>{const p=d.points[ps[0]?.dataIndex];return p?wrap(p.start+' 至 '+p.end,['授权尝试金额:'+fmt(p.amount_usd,'usd'),'授权尝试笔数:'+fmt(p.count)+' 笔']):'';};if(!d.points.length)o.graphic={type:'text',left:'center',top:'middle',style:{text:'最近 8 周未观察到数据',fill:'#536770',fontSize:15}};return o;
|
|
21
|
+
}
|
|
22
|
+
const metric=d.metrics.find(m=>m.key===state.metric)||d.metrics[0];
|
|
23
|
+
if(d.layout==='pie_pair'){
|
|
24
|
+
state.el.setAttribute('data-ec-pie','true');delete o.grid;delete o.xAxis;delete o.yAxis;o.legend={type:'scroll',bottom:0,data:d.categories.map(c=>c.label)};o.title=d.series.map((s,i)=>({text:s.label,left:i?'55%':'5%',top:12,textStyle:{fontSize:13,color:'#344a51'}}));o.series=[];o.graphic=[];
|
|
25
|
+
d.series.forEach((s,i)=>{const values=d.categories.map(c=>value(c.values[s.key]?.[metric.key]));if(values.some(v=>v===null||v<0)||!values.some(v=>v>0)){o.graphic.push({type:'text',left:i?'63%':'13%',top:'43%',style:{text:'暂无完整可绘制数据',fill:'#536770',fontSize:14}});return;}o.series.push({name:s.label,type:'pie',radius:['0%','48%'],center:[i?'75%':'25%','48%'],label:{show:false},data:d.categories.map((c,j)=>({name:c.label,value:values[j]}))});});o.tooltip={...tooltip,trigger:'item',formatter:p=>wrap(p.seriesName,[p.name+':'+fmt(p.value,metric.format)])};return o;
|
|
26
|
+
}
|
|
27
|
+
if(d.layout==='histogram_pair'||d.layout==='bars_pair'){
|
|
28
|
+
const histogram=d.layout==='histogram_pair',cats=d.categories.filter(c=>!histogram||!c.is_unknown),all=cats.flatMap(c=>d.series.map(s=>value(c.values[s.key]?.[metric.key]))).filter(v=>v!==null),max=metric.format==='percent'?1:Math.max(...all,1);
|
|
29
|
+
if(!histogram)state.host.style.height=Math.max(350,cats.length*40+135)+'px';
|
|
30
|
+
o.grid=histogram?[{left:75,right:'54%',top:70,bottom:100},{left:'57%',right:40,top:70,bottom:100}]:[{left:260,right:'53%',top:60,bottom:40},{left:'60%',right:85,top:60,bottom:40}];
|
|
31
|
+
o.xAxis=[0,1].map(i=>histogram?{type:'category',gridIndex:i,data:cats.map(c=>c.label),axisLabel:{rotate:30,hideOverlap:false,fontSize:11}}:{type:'value',gridIndex:i,min:0,max,axisLabel:{formatter:v=>fmt(v,metric.format)}});
|
|
32
|
+
o.yAxis=[0,1].map(i=>histogram?{type:'value',gridIndex:i,min:0,max,axisLabel:{formatter:v=>fmt(v,metric.format)}}:{type:'category',gridIndex:i,inverse:true,data:cats.map(c=>c.label),axisLabel:{show:i===0,width:240,overflow:'truncate'},axisTick:{show:false},axisLine:{show:false}});
|
|
33
|
+
o.series=d.series.map((s,i)=>({name:s.label,type:'bar',xAxisIndex:i,yAxisIndex:i,barMaxWidth:histogram?35:15,itemStyle:{color:i?'#087b71':'#727c80'},data:cats.map(c=>value(c.values[s.key]?.[metric.key]))}));o.tooltip.formatter=ps=>{const c=cats[ps[0]?.dataIndex];return c?wrap(c.label,d.series.map(s=>s.label+':'+fmt(c.values[s.key]?.[metric.key],metric.format))):'';};
|
|
34
|
+
if(histogram){const unknown=d.categories.filter(c=>c.is_unknown);if(unknown.length)o.graphic={type:'text',left:12,bottom:0,style:{text:unknown.map(c=>c.label+':'+d.series.map(s=>s.label+' '+fmt(c.values[s.key]?.[metric.key],metric.format)).join(';')).join('\\n'),fill:'#536770',fontSize:11,width:700,overflow:'break'}};}
|
|
35
|
+
return o;
|
|
36
|
+
}
|
|
37
|
+
const rows=[];d.categories.forEach((c,i)=>{rows.push({c,index:i,child:false});if(state.expanded.has(i))(c.children||[]).forEach(c=>rows.push({c,index:i,child:true}));});state.rows=rows;
|
|
38
|
+
const label=r=>(r.child?' ':r.c.children?.length?(state.expanded.has(r.index)?'▾ ':'▸ '):'')+r.c.label;
|
|
39
|
+
state.host.style.height=Math.max(320,rows.length*52+125)+'px';o.grid={left:285,right:175,top:60,bottom:38};o.legend.data=d.series.map(s=>s.label);o.xAxis={type:'value',min:0,max:metric.format==='percent'?1:undefined,axisLabel:{formatter:v=>fmt(v,metric.format)},splitLine:{lineStyle:{color:'#e2eae6'}}};o.yAxis={type:'category',inverse:true,triggerEvent:true,data:rows.map(label),axisLabel:{width:260,overflow:'truncate',color:'#344a51'},axisTick:{show:false},axisLine:{show:false}};
|
|
40
|
+
const get=(c,s)=>metric.key==='auth_rate'&&!(Number(c.values[s.key]?.count)>0)?null:value(c.values[s.key]?.[metric.key]);
|
|
41
|
+
const show=(c,s)=>{const values=c.values[s.key]||{},v=get(c,s);return fmt(v,metric.format)+(metric.key==='auth_rate'&&v!==null?' ('+fmt(values.approvals)+'/'+fmt(values.count)+')':'');};
|
|
42
|
+
o.series=d.series.map((s,i)=>({name:s.label,type:'bar',barMaxWidth:12,itemStyle:{color:i?'#087b71':'#727c80'},data:rows.map(r=>get(r.c,s)),label:{show:true,position:'right',color:i?'#087b71':'#536770',fontSize:12,formatter:p=>show(rows[p.dataIndex].c,s)}}));o.tooltip.axisPointer={type:'shadow'};o.tooltip.formatter=ps=>{const row=rows[ps[0]?.dataIndex];if(!row)return '';return wrap(row.c.label,d.series.map(s=>s.label+':'+show(row.c,s)));};return o;
|
|
43
|
+
}
|
|
44
|
+
function decorateCountry(state){
|
|
45
|
+
if(!state.data.issuer_drilldown)return;
|
|
46
|
+
state.host.querySelectorAll('svg text').forEach(node=>{const index=state.rows.findIndex(r=>!r.child&&r.c.children?.length&&((state.expanded.has(r.index)?'▾ ':'▸ ')+r.c.label)===node.textContent);if(index<0)return;const row=state.rows[index];node.setAttribute('role','button');node.setAttribute('tabindex','0');node.setAttribute('aria-expanded',String(state.expanded.has(row.index)));node.setAttribute('data-ec-country-index',row.index);node.onkeydown=event=>{if(event.key==='Enter'||event.key===' '){event.preventDefault();state.expanded.has(row.index)?state.expanded.delete(row.index):state.expanded.add(row.index);state.dirty=true;render(state);requestAnimationFrame(()=>state.host.querySelector('[data-ec-country-index="'+row.index+'"]')?.focus());}};});
|
|
47
|
+
}
|
|
48
|
+
function render(state,force=false){
|
|
49
|
+
if(!force&&(state.host.closest('[role=tabpanel][hidden]')||state.host.getBoundingClientRect().width===0))return;
|
|
50
|
+
if(state.chart&&!state.dirty){const width=state.host.clientWidth,height=state.host.clientHeight;if(width>0&&height>0&&(state.chart.getWidth()!==width||state.chart.getHeight()!==height))state.chart.resize({width,height});return;}
|
|
51
|
+
const opt=option(state);
|
|
52
|
+
for(const axisKey of ['xAxis','yAxis']){const axes=Array.isArray(opt[axisKey])?opt[axisKey]:opt[axisKey]?[opt[axisKey]]:[];axes.forEach((axis,index)=>{if(axis.type!=='value')return;let format='count';if(state.data.type==='risk_dual_axis'&&axisKey==='yAxis'&&index===1)format='percent';else if(state.data.type==='weekly_transaction_trend'&&axisKey==='yAxis'&&index===0)format='usd';else if(state.data.type==='comparison_chart')format=(state.data.metrics.find(m=>m.key===state.metric)||state.data.metrics[0]).format;axis.axisPointer={label:{formatter:p=>fmt(p.value,format)}};});}
|
|
53
|
+
if(!state.chart){state.chart=echarts.init(state.host,null,{renderer:'svg',width:force?(state.host.closest('main')?.clientWidth||1100):undefined});state.chart.on('finished',()=>decorateCountry(state));state.chart.on('click',p=>{if(state.data.type!=='comparison_chart'||!state.data.issuer_drilldown)return;let index=p.componentType==='yAxis'?state.rows.findIndex(r=>((r.child?' ':r.c.children?.length?(state.expanded.has(r.index)?'▾ ':'▸ '):'')+r.c.label)===p.value):p.dataIndex;const row=state.rows[index];if(!row||row.child||!row.c.children?.length)return;state.expanded.has(row.index)?state.expanded.delete(row.index):state.expanded.add(row.index);state.dirty=true;render(state);});}
|
|
54
|
+
const previousSelection=state.chart.getOption()?.legend?.[0]?.selected;if(previousSelection&&opt.legend)opt.legend.selected=previousSelection;
|
|
55
|
+
state.chart.setOption(opt,{notMerge:true});state.chart.resize({width:state.host.clientWidth||1100,height:state.host.clientHeight||430});state.dirty=false;decorateCountry(state);state.el.dataset.ecReady='true';
|
|
56
|
+
}
|
|
57
|
+
document.querySelectorAll('[data-echarts-block]').forEach(el=>{const host=el.querySelector('.ec-host'),data=JSON.parse(el.querySelector('[data-ec-data]').textContent),state={el,host,data,metric:data.default_metric,expanded:new Set(),chart:null,dirty:true};states.push(state);el.querySelector('[data-ec-metric]')?.addEventListener('change',e=>{state.metric=e.target.value;state.dirty=true;render(state);});});
|
|
58
|
+
window.reportECharts={states,renderVisible:()=>states.forEach(s=>render(s)),version:echarts.version};
|
|
59
|
+
const schedule=()=>requestAnimationFrame(()=>states.forEach(s=>render(s)));new ResizeObserver(schedule).observe(document.querySelector('main'));document.querySelectorAll('[role=tab]').forEach(tab=>tab.addEventListener('click',schedule));new MutationObserver(schedule).observe(document.querySelector('main'),{subtree:true,attributes:true,attributeFilter:['hidden']});window.addEventListener('resize',schedule);window.addEventListener('beforeprint',()=>states.forEach(s=>render(s,true)));window.addEventListener('afterprint',schedule);schedule();
|
|
60
|
+
})();
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
document.getElementById('print-report').addEventListener('click',()=>window.print());
|
|
2
|
+
document.querySelectorAll('[role=tablist]').forEach(list=>{const tabs=[...list.querySelectorAll('[role=tab]')];function activate(t){tabs.forEach(x=>{const yes=x===t;x.setAttribute('aria-selected',String(yes));x.tabIndex=yes?0:-1;document.getElementById(x.getAttribute('aria-controls')).hidden=!yes;});}tabs.forEach((t,i)=>{t.addEventListener('click',()=>activate(t));t.addEventListener('keydown',e=>{let j;if(e.key==='ArrowRight')j=(i+1)%tabs.length;else if(e.key==='ArrowLeft')j=(i+tabs.length-1)%tabs.length;else if(e.key==='Home')j=0;else if(e.key==='End')j=tabs.length-1;else return;e.preventDefault();activate(tabs[j]);tabs[j].focus();});});});
|