feat: merge promotion bills into marketing account bills
This commit is contained in:
parent
a56328750e
commit
d46ed01a81
@ -90,6 +90,12 @@ class Pages extends Controller
|
||||
protected const MARKETING_PLATFORMS = [
|
||||
'taobao-alipay', 'taobao-wechat', 'pinduoduo',
|
||||
'jd-wallet', 'douyin', 'dewu', 'vipshop',
|
||||
'jd-promotion', 'douyin-promotion', 'dewu-promotion', 'vip-promotion',
|
||||
];
|
||||
|
||||
/** @var list<string> 推广账户流水已合并到营销账户账单页的四个平台页签 */
|
||||
protected const PROMOTION_MARKETING_TABS = [
|
||||
'jd-promotion', 'douyin-promotion', 'dewu-promotion', 'vip-promotion',
|
||||
];
|
||||
|
||||
public function home()
|
||||
@ -501,7 +507,6 @@ class Pages extends Controller
|
||||
'supplier-payables-report' => $this->contentForSupplierPayablesReport(),
|
||||
'payable-cloud-warehouse-vehicle-fee' => $this->contentForDingPayableWorkflows(),
|
||||
'courier-surcharge' => $this->contentForCourierSurcharge(),
|
||||
'platform-promotion-accounts' => app(PlatformPromotionAccountPageData::class)->content($tab),
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
@ -1071,11 +1076,11 @@ class Pages extends Controller
|
||||
$pageConfig = collect($sectionConfig['pages'] ?? [])->firstWhere('slug', $page);
|
||||
abort_if(empty($pageConfig), 403, '当前账号没有该页面权限。');
|
||||
|
||||
if ($page === 'platform-promotion-accounts') {
|
||||
return $this->exportPlatformPromotionAccounts($request);
|
||||
}
|
||||
|
||||
if ($page === 'marketing-account-bills') {
|
||||
if (in_array($tab, self::PROMOTION_MARKETING_TABS, true)) {
|
||||
return $this->exportPlatformPromotionAccounts($request, $section, $page, $tab);
|
||||
}
|
||||
|
||||
return $this->exportMarketingAccountBills($request, $section, $page, $tab);
|
||||
}
|
||||
|
||||
@ -1144,8 +1149,16 @@ class Pages extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
private function exportPlatformPromotionAccounts(Request $request)
|
||||
private function exportPlatformPromotionAccounts(
|
||||
Request $request,
|
||||
string $section = 'billing-transactions',
|
||||
string $page = 'marketing-account-bills',
|
||||
?string $tab = null,
|
||||
)
|
||||
{
|
||||
$platform = PlatformPromotionAccountPageData::platformForTab($tab);
|
||||
abort_if($platform === null, 404);
|
||||
|
||||
$month = trim((string) $request->query('month', ''));
|
||||
$month = preg_match('/^\d{4}-\d{2}$/', $month) === 1 ? $month : null;
|
||||
$filters = [
|
||||
@ -1161,13 +1174,15 @@ class Pages extends Controller
|
||||
} catch (Throwable $exception) {
|
||||
report($exception);
|
||||
|
||||
return redirect()->route('business.pages.show', [
|
||||
'section' => 'billing-transactions',
|
||||
'page' => 'platform-promotion-accounts',
|
||||
])->with('error', '推广账户数据导出暂时无法完成,请稍后重试。');
|
||||
return redirect()->route('business.pages.show', array_filter([
|
||||
'section' => $section,
|
||||
'page' => $page,
|
||||
'tab' => $tab,
|
||||
], static fn (mixed $value): bool => $value !== null && $value !== ''))
|
||||
->with('error', '推广账户数据导出暂时无法完成,请稍后重试。');
|
||||
}
|
||||
|
||||
return response()->streamDownload(function () use ($data): void {
|
||||
return response()->streamDownload(function () use ($data, $platform): void {
|
||||
$csv = fopen('php://output', 'w');
|
||||
fputs($csv, chr(0xEF) . chr(0xBB) . chr(0xBF));
|
||||
fputcsv($csv, ['平台', '店铺/账户', '日期', '类型', '金额(元)', '余额(元)', '备注']);
|
||||
@ -1184,12 +1199,17 @@ class Pages extends Controller
|
||||
]);
|
||||
}
|
||||
};
|
||||
$emit('京东京准通', $data['jd'] ?? [], ['name' => fn ($row) => $row['shop_name'] . ' / ' . $row['account'], 'date' => fn ($row) => $row['show_date'] ?? $row['settle_datetime'], 'type' => fn ($row) => $row['trade_type_name'] ?? $row['in_out_type_name'], 'amount' => fn ($row) => $row['amount'] ?? '', 'balance' => fn ($row) => $row['balance_amount'] ?? '', 'remark' => fn ($row) => $row['remark'] ?? '']);
|
||||
$emit('抖音千川', $data['dy'] ?? [], ['name' => fn ($row) => $row['shopName'] . ' / ' . $row['account'] . ' / ' . $row['qianchuanAavid'], 'date' => fn ($row) => $row['statDate'] ?? '', 'type' => fn () => '花费', 'amount' => fn ($row) => $row['cost'] ?? '', 'balance' => fn ($row) => $row['balance'] ?? '', 'remark' => fn ($row) => $row['remark'] ?? '']);
|
||||
$emit('得物推广', $data['dw'] ?? [], ['name' => fn ($row) => $row['shop_name'] . ' / ' . $row['account'], 'date' => fn ($row) => $row['trade_date'] ?? $row['date_time'], 'type' => fn ($row) => $row['flow_type'] ?? '', 'amount' => fn ($row) => $row['trade_amount_yuan'] ?? '', 'balance' => fn ($row) => $row['balance_yuan'] ?? '', 'remark' => fn ($row) => $row['remark'] ?? '']);
|
||||
$emit('VIP营销', $data['vipMarketing'] ?? [], ['name' => fn ($row) => $row['accountName'] . ' / ' . $row['advertiserId'], 'date' => fn ($row) => $row['dataMonth'], 'type' => fn () => '月度花费', 'amount' => fn ($row) => $row['totalCostYuan'] ?? '', 'balance' => fn () => '', 'remark' => fn ($row) => '曝光 ' . $row['totalImpressionCount']]);
|
||||
if ($platform === 'jd') {
|
||||
$emit('京东京准通', $data['jd'] ?? [], ['name' => fn ($row) => $row['shop_name'] . ' / ' . $row['account'], 'date' => fn ($row) => $row['show_date'] ?? $row['settle_datetime'], 'type' => fn ($row) => $row['trade_type_name'] ?? $row['in_out_type_name'], 'amount' => fn ($row) => $row['amount'] ?? '', 'balance' => fn ($row) => $row['balance_amount'] ?? '', 'remark' => fn ($row) => $row['remark'] ?? '']);
|
||||
} elseif ($platform === 'douyin') {
|
||||
$emit('抖音千川', $data['dy'] ?? [], ['name' => fn ($row) => $row['shopName'] . ' / ' . $row['account'] . ' / ' . $row['qianchuanAavid'], 'date' => fn ($row) => $row['statDate'] ?? '', 'type' => fn () => '花费', 'amount' => fn ($row) => $row['cost'] ?? '', 'balance' => fn ($row) => $row['balance'] ?? '', 'remark' => fn ($row) => $row['remark'] ?? '']);
|
||||
} elseif ($platform === 'dewu') {
|
||||
$emit('得物推广', $data['dw'] ?? [], ['name' => fn ($row) => $row['shop_name'] . ' / ' . $row['account'], 'date' => fn ($row) => $row['trade_date'] ?? $row['date_time'], 'type' => fn ($row) => $row['flow_type'] ?? '', 'amount' => fn ($row) => $row['trade_amount_yuan'] ?? '', 'balance' => fn ($row) => $row['balance_yuan'] ?? '', 'remark' => fn ($row) => $row['remark'] ?? '']);
|
||||
} else {
|
||||
$emit('VIP营销', $data['vipMarketing'] ?? [], ['name' => fn ($row) => $row['accountName'] . ' / ' . $row['advertiserId'], 'date' => fn ($row) => $row['dataMonth'], 'type' => fn () => '月度花费', 'amount' => fn ($row) => $row['totalCostYuan'] ?? '', 'balance' => fn () => '', 'remark' => fn ($row) => '曝光 ' . $row['totalImpressionCount']]);
|
||||
}
|
||||
fclose($csv);
|
||||
}, '平台推广账户流水_' . $filters['month'] . '.csv', [
|
||||
}, '店铺营销账户账单_' . $platform . '_' . $filters['month'] . '.csv', [
|
||||
'Content-Type' => 'text/csv; charset=UTF-8',
|
||||
]);
|
||||
}
|
||||
@ -2289,6 +2309,10 @@ class Pages extends Controller
|
||||
return null;
|
||||
}
|
||||
|
||||
if (in_array($tab, self::PROMOTION_MARKETING_TABS, true)) {
|
||||
return app(PlatformPromotionAccountPageData::class)->content($tab);
|
||||
}
|
||||
|
||||
// Alibaba 页面沿用历史 taobao-wechat 路由,数据来自 JianSun.dbo.alibaba_bill。
|
||||
if ($tab === 'taobao-wechat') {
|
||||
$request = request();
|
||||
|
||||
@ -359,16 +359,10 @@ return [
|
||||
['slug' => 'douyin', 'title' => '抖音账户'],
|
||||
['slug' => 'dewu', 'title' => '得物账户'],
|
||||
['slug' => 'vipshop', 'title' => '唯品会账户'],
|
||||
],
|
||||
],
|
||||
[
|
||||
'slug' => 'platform-promotion-accounts',
|
||||
'title' => '平台推广账户流水',
|
||||
'tabs' => [
|
||||
['slug' => 'jd', 'title' => '京东京准通'],
|
||||
['slug' => 'douyin', 'title' => '抖音千川'],
|
||||
['slug' => 'dewu', 'title' => '得物推广账户'],
|
||||
['slug' => 'vip', 'title' => 'VIP(唯品会)'],
|
||||
['slug' => 'jd-promotion', 'title' => '京东京准通'],
|
||||
['slug' => 'douyin-promotion', 'title' => '抖音千川'],
|
||||
['slug' => 'dewu-promotion', 'title' => '得物推广账户'],
|
||||
['slug' => 'vip-promotion', 'title' => '唯品会推广'],
|
||||
],
|
||||
],
|
||||
['slug' => 'deposit-account-balance', 'title' => '店铺保证金账户余额'],
|
||||
|
||||
@ -10,12 +10,22 @@ use Throwable;
|
||||
|
||||
class PlatformPromotionAccountPageData
|
||||
{
|
||||
private const TAB_PLATFORMS = [
|
||||
'jd-promotion' => 'jd',
|
||||
'douyin-promotion' => 'douyin',
|
||||
'dewu-promotion' => 'dewu',
|
||||
'vip-promotion' => 'vip',
|
||||
];
|
||||
|
||||
public function __construct(private readonly PlatformPromotionAccountRepository $repository)
|
||||
{
|
||||
}
|
||||
|
||||
public function content(?string $tab = null): array
|
||||
{
|
||||
$platform = self::platformForTab($tab);
|
||||
abort_if($platform === null, 404);
|
||||
|
||||
$requestedMonth = $this->month(request('month'));
|
||||
$month = $requestedMonth ?: now()->format('Y-m');
|
||||
$filters = [
|
||||
@ -23,8 +33,6 @@ class PlatformPromotionAccountPageData
|
||||
'shop_name' => $this->text(request('shop_name')),
|
||||
'account' => $this->text(request('account')),
|
||||
];
|
||||
$tab = in_array($tab, ['jd', 'douyin', 'dewu', 'vip'], true) ? $tab : 'jd';
|
||||
|
||||
try {
|
||||
if ($requestedMonth === '') {
|
||||
$month = $this->repository->latestMonth() ?: $month;
|
||||
@ -33,19 +41,21 @@ class PlatformPromotionAccountPageData
|
||||
$data = $this->repository->dashboard($filters, 5000);
|
||||
|
||||
return [
|
||||
'view' => 'business.pages.billing-transactions.platform-promotion-accounts',
|
||||
'view' => 'banking.marketing_platform_promotion_bills.partials.embedded-table',
|
||||
'filters' => $filters,
|
||||
'data' => $data,
|
||||
'platform' => $tab,
|
||||
'rows' => $this->paginateRows($data, $tab),
|
||||
'platform' => $platform,
|
||||
'marketingTab' => $tab,
|
||||
'rows' => $this->paginateRows($data, $platform),
|
||||
'error' => null,
|
||||
];
|
||||
} catch (ServiceUnavailableHttpException $exception) {
|
||||
return [
|
||||
'view' => 'business.pages.billing-transactions.platform-promotion-accounts',
|
||||
'view' => 'banking.marketing_platform_promotion_bills.partials.embedded-table',
|
||||
'filters' => $filters,
|
||||
'data' => [],
|
||||
'platform' => $tab,
|
||||
'platform' => $platform,
|
||||
'marketingTab' => $tab,
|
||||
'rows' => $this->emptyPaginator(),
|
||||
'error' => $exception->getMessage(),
|
||||
];
|
||||
@ -53,16 +63,22 @@ class PlatformPromotionAccountPageData
|
||||
report($exception);
|
||||
|
||||
return [
|
||||
'view' => 'business.pages.billing-transactions.platform-promotion-accounts',
|
||||
'view' => 'banking.marketing_platform_promotion_bills.partials.embedded-table',
|
||||
'filters' => $filters,
|
||||
'data' => [],
|
||||
'platform' => $tab,
|
||||
'platform' => $platform,
|
||||
'marketingTab' => $tab,
|
||||
'rows' => $this->emptyPaginator(),
|
||||
'error' => '推广账户数据暂时无法读取,请稍后重试。',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
public static function platformForTab(?string $tab): ?string
|
||||
{
|
||||
return self::TAB_PLATFORMS[$tab] ?? null;
|
||||
}
|
||||
|
||||
private function paginateRows(array $data, string $tab): LengthAwarePaginator
|
||||
{
|
||||
$key = ['jd' => 'jd', 'douyin' => 'dy', 'dewu' => 'dw', 'vip' => 'vipSales'][$tab];
|
||||
|
||||
@ -0,0 +1,108 @@
|
||||
@php
|
||||
$data = $data ?? [];
|
||||
$platform = $platform ?? 'jd';
|
||||
$marketingTab = $marketingTab ?? null;
|
||||
$summary = match ($platform) {
|
||||
'jd' => $data['jdSummary'] ?? [],
|
||||
'douyin' => $data['dySummary'] ?? [],
|
||||
'dewu' => $data['dwSummary'] ?? [],
|
||||
default => $data['vipSummary'] ?? [],
|
||||
};
|
||||
$titles = ['jd' => '京东京准通', 'douyin' => '抖音千川', 'dewu' => '得物推广账户', 'vip' => '唯品会推广'];
|
||||
$columns = match ($platform) {
|
||||
'jd' => [['shop_name', '店铺'], ['account', '账户'], ['show_date', '日期'], ['trade_type_name', '交易类型'], ['amount', '金额'], ['balance_amount', '余额'], ['remark', '备注']],
|
||||
'douyin' => [['shopName', '店铺'], ['account', '账户'], ['qianchuanAavid', '广告账户'], ['statDate', '日期'], ['cost', '花费'], ['income', '收入'], ['transferIn', '转入'], ['transferOut', '转出'], ['balance', '余额'], ['status', '状态'], ['remark', '备注']],
|
||||
'dewu' => [['shop_name', '店铺'], ['account', '账户'], ['trade_date', '日期'], ['amount_type', '金额类型'], ['flow_type', '收支类型'], ['trade_amount_yuan', '金额'], ['balance_yuan', '余额'], ['remark', '备注']],
|
||||
default => [['shopName', '店铺'], ['storeName', '平台店铺'], ['accountName', '营销账户'], ['dataMonth', '月份'], ['actualSalesAmount', '实际销售额'], ['reportAmount', '申报金额'], ['totalCostYuan', '营销花费']],
|
||||
};
|
||||
$moneyFields = ['amount', 'balance_amount', 'cost', 'income', 'transferIn', 'transferOut', 'balance', 'trade_amount_yuan', 'balance_yuan', 'actualSalesAmount', 'reportAmount', 'totalCostYuan'];
|
||||
@endphp
|
||||
<style>
|
||||
.marketing-platform-promotion-page { color: #171717; }
|
||||
.marketing-platform-promotion-page .promotion-toolbar { display:flex; flex-wrap:wrap; align-items:flex-end; gap:.75rem; margin-bottom:1rem; padding-bottom:1rem; border-bottom:1px solid var(--vip-border); }
|
||||
.marketing-platform-promotion-page .promotion-toolbar label { display:flex; align-items:center; gap:.4rem; color:#595959; font-size:.75rem; font-weight:500; }
|
||||
.marketing-platform-promotion-page .promotion-toolbar input { height:2.15rem; width:11rem; padding:.35rem .55rem; border:1px solid var(--vip-border); border-radius:.45rem; background:#fff; color:#171717; font-size:.8125rem; }
|
||||
.marketing-platform-promotion-page .promotion-toolbar input:focus { border-color:var(--vip-content-accent-dark); outline:2px solid rgba(166,124,61,.18); }
|
||||
.marketing-platform-promotion-page .promotion-toolbar .biz-btn { min-height:2.15rem; }
|
||||
.marketing-platform-promotion-page .promotion-grid { display:grid; grid-template-columns:repeat(4,minmax(0,1fr)); gap:12px; margin:14px 0; }
|
||||
.marketing-platform-promotion-page .promotion-card { padding:16px 18px; }
|
||||
.marketing-platform-promotion-page .promotion-card-title { color:#6b6255; font-size:13px; font-weight:600; }
|
||||
.marketing-platform-promotion-page .promotion-kpi { margin-top:7px; color:#171717; font-size:22px; font-weight:700; }
|
||||
.marketing-platform-promotion-page .promotion-pagination { display:flex; align-items:center; justify-content:space-between; gap:16px; padding:14px 4px 2px; color:#6b6255; font-size:13px; }
|
||||
.marketing-platform-promotion-page .promotion-pagination nav { display:flex; align-items:center; justify-content:flex-end; min-width:0; }
|
||||
.marketing-platform-promotion-page .promotion-pagination__buttons { display:flex !important; flex-direction:row !important; flex-wrap:nowrap; align-items:center; gap:8px; white-space:nowrap; }
|
||||
.marketing-platform-promotion-page .promotion-pagination__buttons a,
|
||||
.marketing-platform-promotion-page .promotion-pagination__buttons span { display:inline-flex !important; min-width:32px; height:32px; align-items:center; justify-content:center; padding:0 10px; border:1px solid var(--vip-border); border-radius:7px; background:#fff; color:#433f39; text-decoration:none; }
|
||||
.marketing-platform-promotion-page .promotion-pagination__buttons a.is-current { border-color:var(--vip-content-accent-dark); background:var(--vip-content-accent-dark); color:#fff; }
|
||||
.marketing-platform-promotion-page .promotion-pagination__buttons span.is-disabled { color:#a7a095; background:#f8f6f2; }
|
||||
.marketing-platform-promotion-page .promotion-pagination__buttons a:hover { border-color:var(--vip-content-accent-dark); color:var(--vip-content-accent-dark); }
|
||||
@media (max-width: 900px) {
|
||||
.marketing-platform-promotion-page .promotion-grid { grid-template-columns:repeat(2,minmax(0,1fr)); }
|
||||
.marketing-platform-promotion-page .promotion-pagination { align-items:flex-start; flex-direction:column; }
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.marketing-platform-promotion-page .promotion-toolbar label { width:100%; justify-content:space-between; }
|
||||
.marketing-platform-promotion-page .promotion-toolbar input { flex:1; width:auto; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="marketing-platform-promotion-page">
|
||||
<form method="GET" class="promotion-toolbar" aria-label="{{ $titles[$platform] }}推广账户筛选">
|
||||
<label>数据月份 <input type="month" name="month" value="{{ $filters['month'] ?? now()->format('Y-m') }}" required></label>
|
||||
<label>店铺 <input type="text" name="shop_name" value="{{ $filters['shop_name'] ?? '' }}" placeholder="店铺名称"></label>
|
||||
<label>账户 <input type="text" name="account" value="{{ $filters['account'] ?? '' }}" placeholder="账户名称"></label>
|
||||
<button class="biz-btn biz-btn-primary" type="submit">查询</button>
|
||||
<a class="biz-btn biz-btn-ghost" href="{{ route('business.pages.export', [
|
||||
'section' => request()->route('section') ?? 'billing-transactions',
|
||||
'page' => request()->route('page') ?? 'marketing-account-bills',
|
||||
'tab' => $marketingTab,
|
||||
] + request()->only(['month', 'shop_name', 'account'])) }}">导出 CSV</a>
|
||||
</form>
|
||||
|
||||
@if (! empty($error))
|
||||
<div class="biz-alert biz-alert-error" role="alert">{{ $error }}</div>
|
||||
@endif
|
||||
|
||||
<div class="promotion-grid">
|
||||
@if ($platform === 'jd')
|
||||
<div class="biz-card promotion-card"><div class="promotion-card-title">入账金额(元)</div><div class="promotion-kpi">{{ number_format((float) ($summary['income'] ?? 0), 2) }}</div></div>
|
||||
<div class="biz-card promotion-card"><div class="promotion-card-title">出账金额(元)</div><div class="promotion-kpi">{{ number_format((float) ($summary['expense'] ?? 0), 2) }}</div></div>
|
||||
<div class="biz-card promotion-card"><div class="promotion-card-title">净额(元)</div><div class="promotion-kpi">{{ number_format((float) ($summary['income'] ?? 0) - (float) ($summary['expense'] ?? 0), 2) }}</div></div>
|
||||
<div class="biz-card promotion-card"><div class="promotion-card-title">流水笔数</div><div class="promotion-kpi">{{ number_format((int) ($summary['record_count'] ?? 0)) }}</div></div>
|
||||
@elseif ($platform === 'douyin')
|
||||
<div class="biz-card promotion-card"><div class="promotion-card-title">总花费(元)</div><div class="promotion-kpi">{{ number_format((float) ($summary['cost'] ?? 0), 2) }}</div></div>
|
||||
<div class="biz-card promotion-card"><div class="promotion-card-title">收入(元)</div><div class="promotion-kpi">{{ number_format((float) ($summary['income'] ?? 0), 2) }}</div></div>
|
||||
<div class="biz-card promotion-card"><div class="promotion-card-title">转入(元)</div><div class="promotion-kpi">{{ number_format((float) ($summary['transfer_in'] ?? 0), 2) }}</div></div>
|
||||
<div class="biz-card promotion-card"><div class="promotion-card-title">账户数</div><div class="promotion-kpi">{{ number_format((int) ($summary['account_count'] ?? 0)) }}</div></div>
|
||||
@elseif ($platform === 'dewu')
|
||||
<div class="biz-card promotion-card"><div class="promotion-card-title">总收入(元)</div><div class="promotion-kpi">{{ number_format((float) ($summary['income'] ?? 0), 2) }}</div></div>
|
||||
<div class="biz-card promotion-card"><div class="promotion-card-title">总支出(元)</div><div class="promotion-kpi">{{ number_format((float) ($summary['expense'] ?? 0), 2) }}</div></div>
|
||||
<div class="biz-card promotion-card"><div class="promotion-card-title">净额(元)</div><div class="promotion-kpi">{{ number_format((float) ($summary['net'] ?? 0), 2) }}</div></div>
|
||||
<div class="biz-card promotion-card"><div class="promotion-card-title">流水笔数</div><div class="promotion-kpi">{{ number_format((int) ($summary['record_count'] ?? 0)) }}</div></div>
|
||||
@else
|
||||
<div class="biz-card promotion-card"><div class="promotion-card-title">实际销售额(元)</div><div class="promotion-kpi">{{ number_format((float) ($summary['sales'] ?? 0), 2) }}</div></div>
|
||||
<div class="biz-card promotion-card"><div class="promotion-card-title">销售目标(元)</div><div class="promotion-kpi">{{ number_format((float) ($summary['target'] ?? 0), 2) }}</div></div>
|
||||
<div class="biz-card promotion-card"><div class="promotion-card-title">涉税申报(元)</div><div class="promotion-kpi">{{ number_format((float) ($summary['tax'] ?? 0), 2) }}</div></div>
|
||||
<div class="biz-card promotion-card"><div class="promotion-card-title">营销花费(元)</div><div class="promotion-kpi">{{ number_format((float) ($summary['marketing_cost'] ?? 0), 2) }}</div></div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<section class="biz-card">
|
||||
<div class="biz-card-head"><div class="biz-card-title">{{ $titles[$platform] }}明细</div><span class="biz-badge">{{ number_format($rows->total()) }} 条</span></div>
|
||||
<div class="biz-table-wrap"><table class="biz-table"><thead><tr>@foreach ($columns as $column)<th>{{ $column[1] }}</th>@endforeach</tr></thead><tbody>
|
||||
@forelse ($rows as $row)
|
||||
<tr>@foreach ($columns as $column)
|
||||
@php($value = $row[$column[0]] ?? null)
|
||||
<td>{{ in_array($column[0], $moneyFields, true) && is_numeric($value) ? number_format((float) $value, 2) : ($value ?? '—') }}</td>
|
||||
@endforeach</tr>
|
||||
@empty
|
||||
<tr><td colspan="{{ count($columns) }}">该月份暂无数据</td></tr>
|
||||
@endforelse
|
||||
</tbody></table></div>
|
||||
<div class="promotion-pagination"><span>每页最多 50 条 · 共 {{ number_format($rows->total()) }} 条</span><nav aria-label="{{ $titles[$platform] }}分页"><div class="promotion-pagination__buttons">
|
||||
@if ($rows->onFirstPage())<span class="is-disabled">上一页</span>@else<a href="{{ $rows->previousPageUrl() }}">上一页</a>@endif
|
||||
<span>第 {{ $rows->currentPage() }} / {{ max($rows->lastPage(), 1) }} 页</span>
|
||||
@if ($rows->hasMorePages())<a href="{{ $rows->nextPageUrl() }}">下一页</a>@else<span class="is-disabled">下一页</span>@endif
|
||||
</div></nav></div>
|
||||
</section>
|
||||
</div>
|
||||
@ -1,89 +0,0 @@
|
||||
@php
|
||||
$data = $data ?? [];
|
||||
$platform = $platform ?? 'jd';
|
||||
$summary = match ($platform) {
|
||||
'jd' => $data['jdSummary'] ?? [],
|
||||
'douyin' => $data['dySummary'] ?? [],
|
||||
'dewu' => $data['dwSummary'] ?? [],
|
||||
default => $data['vipSummary'] ?? [],
|
||||
};
|
||||
$titles = ['jd' => '京东京准通', 'douyin' => '抖音千川', 'dewu' => '得物推广账户', 'vip' => 'VIP(唯品会)'];
|
||||
$columns = match ($platform) {
|
||||
'jd' => [['shop_name', '店铺'], ['account', '账户'], ['show_date', '日期'], ['trade_type_name', '交易类型'], ['amount', '金额'], ['balance_amount', '余额'], ['remark', '备注']],
|
||||
'douyin' => [['shopName', '店铺'], ['account', '账户'], ['qianchuanAavid', '广告账户'], ['statDate', '日期'], ['cost', '花费'], ['balance', '余额'], ['status', '状态'], ['remark', '备注']],
|
||||
'dewu' => [['shop_name', '店铺'], ['account', '账户'], ['trade_date', '日期'], ['flow_type', '收支类型'], ['trade_amount_yuan', '金额'], ['balance_yuan', '余额'], ['remark', '备注']],
|
||||
default => [['shopName', '店铺'], ['storeName', '平台店铺'], ['accountName', '营销账户'], ['dataMonth', '月份'], ['actualSalesAmount', '实际销售额'], ['reportAmount', '申报金额'], ['totalCostYuan', '营销花费']],
|
||||
};
|
||||
$moneyFields = ['amount', 'balance_amount', 'cost', 'balance', 'trade_amount_yuan', 'balance_yuan', 'actualSalesAmount', 'reportAmount', 'totalCostYuan'];
|
||||
@endphp
|
||||
<style>
|
||||
.platform-promotion-page .biz-grid { display:grid; grid-template-columns:repeat(4,minmax(0,1fr)); gap:12px; margin:14px 0; }
|
||||
.platform-promotion-page .biz-card { padding:16px 18px; }
|
||||
.platform-promotion-page .biz-card-title { color:#6b6255; font-size:13px; font-weight:600; }
|
||||
.platform-promotion-page .biz-kpi { margin-top:7px; color:#171717; font-size:22px; font-weight:700; }
|
||||
.platform-promotion-page .platform-pagination { display:flex; align-items:center; justify-content:space-between; gap:16px; padding:14px 4px 2px; color:#6b6255; font-size:13px; }
|
||||
.platform-promotion-page .platform-pagination nav { display:flex; align-items:center; justify-content:flex-end; min-width:0; }
|
||||
.platform-promotion-page .platform-pagination__buttons { display:flex !important; flex-direction:row !important; flex-wrap:nowrap; align-items:center; gap:8px; white-space:nowrap; }
|
||||
.platform-promotion-page .platform-pagination__buttons a,
|
||||
.platform-promotion-page .platform-pagination__buttons span { display:inline-flex !important; min-width:32px; height:32px; align-items:center; justify-content:center; padding:0 10px; border:1px solid #ded7ca; border-radius:7px; background:#fff; color:#433f39; text-decoration:none; }
|
||||
.platform-promotion-page .platform-pagination__buttons a.is-current { border-color:#856632; background:#856632; color:#fff; }
|
||||
.platform-promotion-page .platform-pagination__buttons span.is-disabled { color:#a7a095; background:#f8f6f2; }
|
||||
.platform-promotion-page .platform-pagination__buttons a:hover { border-color:#856632; color:#856632; }
|
||||
@media (max-width: 900px) { .platform-promotion-page .biz-grid { grid-template-columns:repeat(2,minmax(0,1fr)); } .platform-promotion-page .platform-pagination { align-items:flex-start; flex-direction:column; } }
|
||||
</style>
|
||||
<div class="platform-promotion-page">
|
||||
<form method="GET" class="upstream-data-toolbar" aria-label="平台推广账户筛选">
|
||||
<label>数据月份 <input type="month" name="month" value="{{ $filters['month'] ?? now()->format('Y-m') }}" required></label>
|
||||
<label>店铺 <input type="text" name="shop_name" value="{{ $filters['shop_name'] ?? '' }}" placeholder="店铺名称"></label>
|
||||
<label>账户 <input type="text" name="account" value="{{ $filters['account'] ?? '' }}" placeholder="账户名称"></label>
|
||||
<button class="biz-btn biz-btn-primary" type="submit">查询</button>
|
||||
<a class="biz-btn biz-btn-ghost" href="{{ route('business.pages.export', ['section' => 'billing-transactions', 'page' => 'platform-promotion-accounts', 'tab' => $platform] + request()->only(['month', 'shop_name', 'account'])) }}">导出 CSV</a>
|
||||
</form>
|
||||
|
||||
@if (! empty($error))
|
||||
<div class="biz-alert biz-alert-error" role="alert">{{ $error }}</div>
|
||||
@endif
|
||||
|
||||
<div class="biz-grid biz-grid-4">
|
||||
@if ($platform === 'jd')
|
||||
<div class="biz-card"><div class="biz-card-title">入账金额</div><div class="biz-kpi">{{ number_format((float) ($summary['income'] ?? 0), 2) }}</div></div>
|
||||
<div class="biz-card"><div class="biz-card-title">出账金额</div><div class="biz-kpi">{{ number_format((float) ($summary['expense'] ?? 0), 2) }}</div></div>
|
||||
<div class="biz-card"><div class="biz-card-title">净额</div><div class="biz-kpi">{{ number_format((float) ($summary['income'] ?? 0) - (float) ($summary['expense'] ?? 0), 2) }}</div></div>
|
||||
<div class="biz-card"><div class="biz-card-title">流水笔数</div><div class="biz-kpi">{{ number_format((int) ($summary['record_count'] ?? 0)) }}</div></div>
|
||||
@elseif ($platform === 'douyin')
|
||||
<div class="biz-card"><div class="biz-card-title">总花费</div><div class="biz-kpi">{{ number_format((float) ($summary['cost'] ?? 0), 2) }}</div></div>
|
||||
<div class="biz-card"><div class="biz-card-title">收入</div><div class="biz-kpi">{{ number_format((float) ($summary['income'] ?? 0), 2) }}</div></div>
|
||||
<div class="biz-card"><div class="biz-card-title">转入</div><div class="biz-kpi">{{ number_format((float) ($summary['transfer_in'] ?? 0), 2) }}</div></div>
|
||||
<div class="biz-card"><div class="biz-card-title">账户数</div><div class="biz-kpi">{{ number_format((int) ($summary['account_count'] ?? 0)) }}</div></div>
|
||||
@elseif ($platform === 'dewu')
|
||||
<div class="biz-card"><div class="biz-card-title">总收入</div><div class="biz-kpi">{{ number_format((float) ($summary['income'] ?? 0), 2) }}</div></div>
|
||||
<div class="biz-card"><div class="biz-card-title">总支出</div><div class="biz-kpi">{{ number_format((float) ($summary['expense'] ?? 0), 2) }}</div></div>
|
||||
<div class="biz-card"><div class="biz-card-title">净额</div><div class="biz-kpi">{{ number_format((float) ($summary['net'] ?? 0), 2) }}</div></div>
|
||||
<div class="biz-card"><div class="biz-card-title">流水笔数</div><div class="biz-kpi">{{ number_format((int) ($summary['record_count'] ?? 0)) }}</div></div>
|
||||
@else
|
||||
<div class="biz-card"><div class="biz-card-title">实际销售额</div><div class="biz-kpi">{{ number_format((float) ($summary['sales'] ?? 0), 2) }}</div></div>
|
||||
<div class="biz-card"><div class="biz-card-title">销售目标</div><div class="biz-kpi">{{ number_format((float) ($summary['target'] ?? 0), 2) }}</div></div>
|
||||
<div class="biz-card"><div class="biz-card-title">涉税申报</div><div class="biz-kpi">{{ number_format((float) ($summary['tax'] ?? 0), 2) }}</div></div>
|
||||
<div class="biz-card"><div class="biz-card-title">营销花费</div><div class="biz-kpi">{{ number_format((float) ($summary['marketing_cost'] ?? 0), 2) }}</div></div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<section class="biz-card mt-4">
|
||||
<div class="biz-card-head"><div class="biz-card-title">{{ $titles[$platform] }}明细</div><span class="biz-badge">{{ number_format($rows->total()) }} 条</span></div>
|
||||
<div class="biz-table-wrap"><table class="biz-table"><thead><tr>@foreach ($columns as $column)<th>{{ $column[1] }}</th>@endforeach</tr></thead><tbody>
|
||||
@forelse ($rows as $row)
|
||||
<tr>@foreach ($columns as $column)
|
||||
@php($value = $row[$column[0]] ?? null)
|
||||
<td>{{ in_array($column[0], $moneyFields, true) && is_numeric($value) ? number_format((float) $value, 2) : ($value ?? '—') }}</td>
|
||||
@endforeach</tr>
|
||||
@empty
|
||||
<tr><td colspan="{{ count($columns) }}">该月份暂无数据</td></tr>
|
||||
@endforelse
|
||||
</tbody></table></div>
|
||||
<div class="platform-pagination"><span>每页最多 50 条 · 共 {{ number_format($rows->total()) }} 条</span><nav aria-label="{{ $titles[$platform] }}分页"><div class="platform-pagination__buttons">
|
||||
@if ($rows->onFirstPage())<span class="is-disabled">上一页</span>@else<a href="{{ $rows->previousPageUrl() }}">上一页</a>@endif
|
||||
<span>第 {{ $rows->currentPage() }} / {{ max($rows->lastPage(), 1) }} 页</span>
|
||||
@if ($rows->hasMorePages())<a href="{{ $rows->nextPageUrl() }}">下一页</a>@else<span class="is-disabled">下一页</span>@endif
|
||||
</div></nav></div>
|
||||
</section>
|
||||
</div>
|
||||
@ -7,10 +7,12 @@ use Mockery;
|
||||
use Modules\EcommerceFinance\Repositories\PlatformPromotionAccountRepository;
|
||||
use Tests\Feature\FeatureTestCase;
|
||||
|
||||
class PlatformPromotionAccountPageTest extends FeatureTestCase
|
||||
class MarketingPlatformPromotionBillsTest extends FeatureTestCase
|
||||
{
|
||||
public function test_platform_promotion_page_remains_available_with_bill_offset_navigation(): void
|
||||
public function test_promotion_platform_is_a_tab_inside_marketing_account_bills(): void
|
||||
{
|
||||
$this->allowMarketingAccountBillsPage();
|
||||
|
||||
$repository = Mockery::mock(PlatformPromotionAccountRepository::class);
|
||||
$repository->shouldReceive('latestMonth')->once()->andReturn('2026-08');
|
||||
$repository->shouldReceive('dashboard')->once()->with([
|
||||
@ -20,29 +22,30 @@ class PlatformPromotionAccountPageTest extends FeatureTestCase
|
||||
], 5000)->andReturn($this->dashboardData());
|
||||
$this->app->instance(PlatformPromotionAccountRepository::class, $repository);
|
||||
|
||||
$this->user->attachPermission(
|
||||
Permission::where('name', 'read-admin-panel')->firstOrFail()
|
||||
);
|
||||
|
||||
$this->loginAs()
|
||||
->get(route('business.pages.show', [
|
||||
'section' => 'billing-transactions',
|
||||
'page' => 'platform-promotion-accounts',
|
||||
'tab' => 'jd',
|
||||
'page' => 'marketing-account-bills',
|
||||
'tab' => 'jd-promotion',
|
||||
]))
|
||||
->assertOk()
|
||||
->assertSeeText('平台推广账户流水')
|
||||
->assertSeeText('京东京准通')
|
||||
->assertSee('aria-label="京东京准通推广账户筛选"', false)
|
||||
->assertSeeText('测试店铺')
|
||||
->assertSeeText('导出 CSV')
|
||||
->assertSee('class="my-6 business-page--marketing-account-bills"', false)
|
||||
->assertSee(route('business.pages.show', [
|
||||
'section' => 'order-data',
|
||||
'page' => 'bill-offset',
|
||||
]), false);
|
||||
'section' => 'billing-transactions',
|
||||
'page' => 'marketing-account-bills',
|
||||
'tab' => 'taobao-alipay',
|
||||
]), false)
|
||||
->assertDontSeeText('平台推广账户流水');
|
||||
}
|
||||
|
||||
public function test_platform_promotion_export_keeps_the_complete_filtered_month(): void
|
||||
public function test_promotion_platform_export_uses_merged_marketing_account_bills_route(): void
|
||||
{
|
||||
$this->allowMarketingAccountBillsPage();
|
||||
|
||||
$repository = Mockery::mock(PlatformPromotionAccountRepository::class);
|
||||
$repository->shouldReceive('latestMonth')->never();
|
||||
$repository->shouldReceive('dashboard')->once()->with([
|
||||
@ -52,27 +55,34 @@ class PlatformPromotionAccountPageTest extends FeatureTestCase
|
||||
], 5000)->andReturn($this->dashboardData());
|
||||
$this->app->instance(PlatformPromotionAccountRepository::class, $repository);
|
||||
|
||||
$this->user->attachPermission(
|
||||
Permission::where('name', 'read-admin-panel')->firstOrFail()
|
||||
);
|
||||
|
||||
$response = $this->loginAs()->get(route('business.pages.export', [
|
||||
'section' => 'billing-transactions',
|
||||
'page' => 'platform-promotion-accounts',
|
||||
'tab' => 'jd',
|
||||
'page' => 'marketing-account-bills',
|
||||
'tab' => 'jd-promotion',
|
||||
'month' => '2026-08',
|
||||
'shop_name' => '测试',
|
||||
'account' => 'account-01',
|
||||
]));
|
||||
|
||||
$response->assertOk();
|
||||
$response->assertHeader('Content-Type', 'text/csv; charset=UTF-8');
|
||||
$disposition = (string) $response->headers->get('Content-Disposition');
|
||||
$this->assertStringContainsString("filename*=utf-8''", $disposition);
|
||||
$this->assertStringContainsString('2026-08.csv', $disposition);
|
||||
$response->assertOk()
|
||||
->assertHeader('Content-Type', 'text/csv; charset=UTF-8')
|
||||
->assertDownload('店铺营销账户账单_jd_2026-08.csv');
|
||||
$this->assertStringContainsString('测试店铺', $response->streamedContent());
|
||||
}
|
||||
|
||||
public function test_removed_platform_promotion_directory_is_not_available(): void
|
||||
{
|
||||
$this->allowMarketingAccountBillsPage();
|
||||
|
||||
$this->loginAs()
|
||||
->get(route('business.pages.show', [
|
||||
'section' => 'billing-transactions',
|
||||
'page' => 'platform-promotion-accounts',
|
||||
'tab' => 'jd',
|
||||
]))
|
||||
->assertNotFound();
|
||||
}
|
||||
|
||||
private function dashboardData(): array
|
||||
{
|
||||
return [
|
||||
@ -97,4 +107,18 @@ class PlatformPromotionAccountPageTest extends FeatureTestCase
|
||||
'vipSummary' => [],
|
||||
];
|
||||
}
|
||||
|
||||
private function allowMarketingAccountBillsPage(): void
|
||||
{
|
||||
$this->user->update(['locale' => 'zh-CN']);
|
||||
$this->user->attachPermission(Permission::where('name', 'read-admin-panel')->firstOrFail());
|
||||
|
||||
$subject = 'user_' . $this->user->id;
|
||||
setting()->set('system.sidebar_permissions', json_encode([
|
||||
'configured' => [$subject],
|
||||
'subjects' => [
|
||||
$subject => ['billing-transactions/marketing-account-bills'],
|
||||
],
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user