hyapp-admin-platform/finance/src/components/FinanceReconciliation.jsx
2026-07-03 17:34:57 +08:00

153 lines
7.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import Button from "@mui/material/Button";
import { EMPTY_RECHARGE_OVERVIEW, EMPTY_RECHARGE_SUMMARY, formatAmount, formatUsdMinor } from "../format.js";
// 渠道对账页:按结算月核对各渠道的流水与扣费。谷歌口径来自 Orders API 同步的实付明细;
// 三方与币商没有渠道侧扣费数据时如实标注,不做拍脑袋估算。
export function FinanceReconciliation({ loading, month, onMonthChange, overview, summary }) {
const data = summary || EMPTY_RECHARGE_SUMMARY;
const paid = (overview || EMPTY_RECHARGE_OVERVIEW).googlePaid;
const totalUsd = Number(data.total?.usdMinorAmount) || 0;
const feeUsd = paid.estFeeUsdMinor;
const taxUsd = paid.estTaxUsdMinor;
const netUsd = totalUsd - feeUsd - taxUsd;
return (
<div className="finance-overview">
<div className="finance-recon-toolbar">
<Button size="small" variant="outlined" onClick={() => onMonthChange(-1)}>
上月
</Button>
<span className="finance-recon-toolbar__month">{month.label} 结算期</span>
<Button disabled={month.isCurrent} size="small" variant="outlined" onClick={() => onMonthChange(1)}>
下月
</Button>
<span className="finance-hint" style={{ marginLeft: "auto" }}>
对账口径平台流水 × 谷歌 Orders API 实付 · 中国时区
</span>
</div>
<section className="finance-card">
<div className="finance-card__head">
<span className="finance-card__title">
结算瀑布<small>全渠道 · USD{paid.unsyncedCount > 0 ? " · 谷歌扣费为已同步部分估算" : ""}</small>
</span>
</div>
<div className="finance-card__body">
{loading ? (
<p className="finance-hint">统计中</p>
) : (
<div className="finance-wfall">
<WaterfallRow amount={totalUsd} color="var(--fin-ink)" label="充值流水" max={totalUsd} />
<WaterfallRow amount={-feeUsd} color="#E19B3C" label="渠道手续费(估)" max={totalUsd} />
<WaterfallRow amount={-taxUsd} color="#C86A6A" label="代缴税费(估)" max={totalUsd} />
<WaterfallRow amount={netUsd} color="var(--fin-pos)" label="净入账(估)" max={totalUsd} />
</div>
)}
</div>
</section>
<div className="finance-recon-grid">
<section className="finance-card">
<div className="finance-card__head">
<span className="finance-badge finance-badge--google">谷歌</span>
{paid.unsyncedCount > 0 ? (
<span className="finance-pill finance-pill--warn">
<i />
{formatAmount(paid.unsyncedCount)} 笔待同步
</span>
) : (
<span className="finance-pill finance-pill--ok">
<i />
实付已全量同步
</span>
)}
</div>
<div className="finance-card__body">
<ReconLine
label="平台流水"
value={formatUsdMinor(data.googlePlay?.usdMinorAmount || 0, "USD")}
/>
<ReconLine
label={`已同步实付覆盖(${formatAmount(paid.syncedCount)}/${formatAmount(paid.googleBillCount)} 笔)`}
value={formatUsdMinor(paid.coveredUsdMinor, "USD")}
/>
<ReconLine
label="谷歌服务费(估)"
negative
value={` ${formatUsdMinor(paid.estFeeUsdMinor, "USD")}`}
/>
<ReconLine
label="代缴税费(估)"
negative
value={` ${formatUsdMinor(paid.estTaxUsdMinor, "USD")}`}
/>
<ReconLine label="预计净入账" total value={formatUsdMinor(paid.estNetUsdMinor, "USD")} />
<p className="finance-hint" style={{ marginTop: 10 }}>
Play Console 财务报告按月核对未同步订单可在流水页批量补数
</p>
</div>
</section>
<section className="finance-card">
<div className="finance-card__head">
<span className="finance-badge finance-badge--thirdparty">三方</span>
</div>
<div className="finance-card__body">
<ReconLine
label="三方流水"
value={formatUsdMinor(data.thirdParty?.usdMinorAmount || 0, "USD")}
/>
<ReconLine label="笔数" value={`${formatAmount(data.thirdParty?.billCount || 0)}`} />
<ReconLine label="渠道扣费" value="V5Pay 逐笔回传 / MiFaPay 未提供" />
<p className="finance-hint" style={{ marginTop: 10 }}>
V5Pay fee/tax 已逐笔展示在流水明细MiFaPay 仅提供毛额需用渠道后台月账单人工核对
</p>
</div>
</section>
<section className="finance-card">
<div className="finance-card__head">
<span className="finance-badge finance-badge--coinseller">币商</span>
</div>
<div className="finance-card__body">
<ReconLine
label="净进货(进货 冲回)"
value={formatUsdMinor(data.coinSeller?.usdMinorAmount || 0, "USD")}
/>
<ReconLine label="记录数" value={`${formatAmount(data.coinSeller?.billCount || 0)}`} />
<ReconLine label="金币入库" value={`${formatAmount(data.coinSeller?.coinAmount || 0)} 金币`} />
<p className="finance-hint" style={{ marginTop: 10 }}>
打款凭证与 TRC20 交易号逐笔登记在流水明细可按凭证号搜索核对
</p>
</div>
</section>
</div>
</div>
);
}
function WaterfallRow({ amount, color, label, max }) {
const width = max > 0 ? (Math.abs(amount) / max) * 100 : 0;
const negative = amount < 0;
return (
<div className="finance-wfall__row">
<span className="finance-wfall__label">{label}</span>
<span className="finance-wfall__track">
<i style={{ background: color, width: `${Math.min(100, width)}%` }} />
</span>
<span className={negative ? "finance-wfall__val num finance-amount--negative" : "finance-wfall__val num"}>
{negative ? ` ${formatUsdMinor(-amount, "USD")}` : formatUsdMinor(amount, "USD")}
</span>
</div>
);
}
function ReconLine({ label, negative, total, value }) {
return (
<div className={["finance-recon-line", total ? "finance-recon-line--total" : ""].filter(Boolean).join(" ")}>
<span>{label}</span>
<b className={negative ? "num finance-amount--negative" : "num"}>{value}</b>
</div>
);
}