Expose ARR disclosure gaps before statistical comparison
This commit is contained in:
@@ -1,26 +1,37 @@
|
||||
import { useState } from "react";
|
||||
import { evidenceUrl, type ResearchSnapshot } from "@/lib/ai-research";
|
||||
import { arrDisclosureCoverage, arrDisclosures } from "@/lib/research-statistics";
|
||||
import { arrDisclosureCoverage, arrDisclosures, arrFieldGaps, type ArrFieldGap } from "@/lib/research-statistics";
|
||||
|
||||
const labels: Record<string, string> = {
|
||||
signal: "企业或当事方披露", estimate: "第三方估算",
|
||||
historical: "历史披露", "historical-estimate": "历史估算",
|
||||
};
|
||||
const gapLabels: Record<ArrFieldGap, string> = {
|
||||
date: "缺有效财务截至日", currency: "USD 未确认(含其他币种)",
|
||||
amount: "下界、近似或非精确金额", estimate: "第三方估算",
|
||||
};
|
||||
|
||||
export default function ArrDisclosures({ data, onSelect }: { data: ResearchSnapshot; onSelect: (id: string) => void }) {
|
||||
const [category, setCategory] = useState("");
|
||||
const [year, setYear] = useState("");
|
||||
const rows = arrDisclosures(data, { category, foundedYear: year ? Number(year) : undefined });
|
||||
const [gap, setGap] = useState<ArrFieldGap | "">("");
|
||||
const claims = arrDisclosures(data, { category, foundedYear: year ? Number(year) : undefined });
|
||||
const rows = claims.filter(({ observation }) => !gap || arrFieldGaps(observation, data.asOf).includes(gap));
|
||||
const gapCounts = (Object.keys(gapLabels) as ArrFieldGap[]).map((id) => ({
|
||||
id, count: claims.filter(({ observation }) => arrFieldGaps(observation, data.asOf).includes(id)).length,
|
||||
}));
|
||||
const completeFields = claims.filter(({ observation }) => !arrFieldGaps(observation, data.asOf).length).length;
|
||||
const coverage = arrDisclosureCoverage(data, year ? Number(year) : undefined);
|
||||
const companies = new Set(rows.map((row) => row.company.id));
|
||||
const companies = new Set(claims.map((row) => row.company.id));
|
||||
const categories = new Map(data.categories.map((item) => [item.id, item.name]));
|
||||
const evidence = new Map(data.evidence.map((item) => [item.id, item]));
|
||||
return <section aria-label="ARR披露分类表">
|
||||
<h3>2025—2026 年成立公司的 ARR 披露</h3>
|
||||
<p className="research-notice">这是跨时点证据表,共 {companies.size} 家公司、{rows.length} 条记录;同一公司的历史里程碑逐条保留,不能相加或视为独立公司。ARR 标签沿用原始披露,未证明经常性收入定义或审计状态;不纳入 MRR 图表,不计算同期行业均值或造假概率。仅收录明确标注的 ARR;定义冲突、目标、承诺、客户和前雇主收入排除,缺失不作零。</p>
|
||||
<p className="research-notice">这是跨时点证据表,共 {companies.size} 家公司、{claims.length} 条记录;同一公司的历史里程碑逐条保留,不能相加或视为独立公司。ARR 标签沿用原始披露,未证明经常性收入定义或审计状态;不纳入 MRR 图表,不计算同期行业均值或造假概率。仅收录明确标注的 ARR;定义冲突、目标、承诺、客户和前雇主收入排除,缺失不作零。</p>
|
||||
<div className="research-filters">
|
||||
<label>业务分类<select value={category} onChange={(event) => setCategory(event.target.value)}><option value="">全部分类</option>{data.categories.map((item) => <option key={item.id} value={item.id}>{item.name}</option>)}</select></label>
|
||||
<label>成立年份<select value={year} onChange={(event) => setYear(event.target.value)}><option value="">2025 与 2026</option><option value="2025">2025</option><option value="2026">2026</option></select></label>
|
||||
<label>补证项目<select value={gap} onChange={(event) => setGap(event.target.value as ArrFieldGap | "")}><option value="">全部记录</option>{gapCounts.map((item) => <option key={item.id} value={item.id}>{gapLabels[item.id]}({item.count} 条)</option>)}</select></label>
|
||||
</div>
|
||||
<div className="research-table-wrap"><table>
|
||||
<caption>ARR 披露覆盖:按所选成立年份、每家公司计一次。点击分类筛选下方证据表。</caption>
|
||||
@@ -32,6 +43,12 @@ export default function ArrDisclosures({ data, onSelect }: { data: ResearchSnaps
|
||||
</tr>)}</tbody>
|
||||
</table></div>
|
||||
<p className="research-muted">分母含仅有目录资料的公司,覆盖率反映本次采集深度,不代表行业披露率。企业披露含历史里程碑,同一公司兼有披露和估算时归企业披露;缺少 ARR 的公司仍可能有其他收入。披露时点、币种与合同定义未对齐,不能将已披露少数公司的均值外推至整个分类,也不能由离散性判断财务造假。</p>
|
||||
<aside className="research-notice" aria-label="ARR比较前的补证检查">
|
||||
<strong>ARR 比较前的补证检查</strong>
|
||||
<p>当前分类及成立年下有 {claims.length} 条披露,{completeFields} 条同时具有有效日期、明确 USD、精确金额且不是第三方估算。补证筛选后显示 {rows.length} 条。</p>
|
||||
<div className="income-options">{gapCounts.map((item) => <button key={item.id} aria-pressed={gap === item.id} onClick={() => setGap(gap === item.id ? "" : item.id)}>{gapLabels[item.id]}:{item.count}</button>)}</div>
|
||||
<p>各项可重叠,不能相加为独立公司数。日期必须是经营指标截至日,网页访问、发布或更新时间不能代替;其他币种保留原值,不擅自换算。基础字段齐全也不证明可比,还需对齐收入定义、业务期间、收费模式、主体与独立验证;这些缺项不作为造假判定。</p>
|
||||
</aside>
|
||||
<div className="research-table-wrap"><table>
|
||||
<caption>金额保留原币种、下界和估算身份;日期栏保留业务截至日缺项</caption>
|
||||
<thead><tr><th>公司 / 分类</th><th>收费与交付模式</th><th>原始 ARR 声明</th><th>时点</th><th>身份 / 限制</th><th>证据</th></tr></thead>
|
||||
@@ -42,11 +59,11 @@ export default function ArrDisclosures({ data, onSelect }: { data: ResearchSnaps
|
||||
<td><button onClick={() => onSelect(company.id)}>{company.name}</button><small>{categories.get(company.cat)} · {company.year}</small></td>
|
||||
<td>{company.businessModel ?? company.price}</td>
|
||||
<td>{item.bound !== "=" && item.bound}{new Intl.NumberFormat("zh-CN", { maximumFractionDigits: 2 }).format(item.value)} {item.unit}<small>{item.kind}</small></td>
|
||||
<td>{item.timing}</td><td>{labels[item.selection]}<small>{item.reason}</small></td>
|
||||
<td>{item.timing}</td><td>{labels[item.selection]}<small>{item.reason}</small><small>{arrFieldGaps(item, data.asOf).map((id) => gapLabels[id]).join(";") || "基础字段齐全;定义、期间与主体仍须核实"}</small></td>
|
||||
<td>{url ? <a href={url} target="_blank" rel="noopener noreferrer">{item.evidence} ↗</a> : item.evidence}</td>
|
||||
</tr>;
|
||||
})}</tbody>
|
||||
</table></div>
|
||||
{!rows.length && <p>当前筛选没有符合条件的已记录 ARR 声明;不代表这些公司收入为零。</p>}
|
||||
{!rows.length && <p>当前分类、成立年与补证筛选没有符合条件的已记录 ARR 声明;不代表这些公司收入为零。</p>}
|
||||
</section>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user