店铺营销账户账单各平台导出 #77
@ -483,61 +483,203 @@ class Pages extends Controller
|
||||
string $page,
|
||||
string $tab,
|
||||
) {
|
||||
abort_unless($tab === 'pinduoduo', 404);
|
||||
abort_unless(in_array($tab, ['taobao-alipay', 'taobao-wechat', 'pinduoduo', 'jd-wallet'], true), 404);
|
||||
|
||||
return match ($tab) {
|
||||
'taobao-alipay' => $this->exportTaobaoAccountBills($request, $section, $page, $tab),
|
||||
'taobao-wechat' => $this->exportAlibabaAccountBills($request, $section, $page, $tab),
|
||||
'jd-wallet' => $this->exportJdAccountBills($request, $section, $page, $tab),
|
||||
default => $this->exportPinduoduoAccountBills($request, $section, $page, $tab),
|
||||
};
|
||||
}
|
||||
|
||||
private function exportTaobaoAccountBills(Request $request, string $section, string $page, string $tab)
|
||||
{
|
||||
$storeName = mb_substr(trim((string) $request->query('store_name', '')), 0, 191);
|
||||
$fromDate = $this->dateFilter($request->query('from'));
|
||||
$toDate = $this->dateFilter($request->query('to'));
|
||||
$flowType = in_array($request->query('flow_type'), ['income', 'expense'], true)
|
||||
? (string) $request->query('flow_type')
|
||||
: '';
|
||||
$filters = [
|
||||
'store_name' => $storeName !== '' ? $storeName : null,
|
||||
'from' => $fromDate,
|
||||
'to' => $toDate,
|
||||
'flow_type' => $flowType !== '' ? $flowType : null,
|
||||
];
|
||||
$redirectParameters = array_filter([
|
||||
'section' => $section,
|
||||
'page' => $page,
|
||||
'tab' => $tab,
|
||||
$redirectParameters = $this->marketingBillRedirectParameters($section, $page, $tab, [
|
||||
'store_name' => $storeName,
|
||||
'from' => $fromDate,
|
||||
'to' => $toDate,
|
||||
'flow_type' => $flowType,
|
||||
], static fn (mixed $value): bool => $value !== '' && $value !== null);
|
||||
]);
|
||||
$repository = app(TmallTaobaoSpreadBillRepository::class);
|
||||
|
||||
if (! $repository->usesBusinessMirror()) {
|
||||
return redirect()->route('business.pages.show', $redirectParameters)
|
||||
->with('error', '淘系账单共享业务镜像尚未完成同步,暂时不能导出;请稍后重试。');
|
||||
}
|
||||
|
||||
return $this->streamMarketingAccountBillCsv(
|
||||
$repository->exportRows(
|
||||
$storeName !== '' ? $storeName : null,
|
||||
$fromDate !== '' ? $fromDate : null,
|
||||
$toDate !== '' ? $toDate : null,
|
||||
$flowType !== '' ? $flowType : null,
|
||||
),
|
||||
['店铺名称', '记账日期', '交易日期', '金额(元)', '余额(元)', '备注', 'finType', '收支类型', '交易类型'],
|
||||
static fn (array $row): array => [
|
||||
$row['store_name'] ?? '', $row['bookkeeping_time'] ?? '', $row['transaction_date'] ?? '',
|
||||
$row['amount'] ?? 0, $row['balance'] ?? 0, $row['comment'] ?? '', $row['fin_type'] ?? '',
|
||||
$row['income_expense_type'] ?? '', $row['transaction_type'] ?? '',
|
||||
],
|
||||
'taobao_account_bills_' . $this->marketingBillRange($fromDate, $toDate) . '.csv',
|
||||
$redirectParameters,
|
||||
'淘系账单导出暂时无法完成,请稍后重试。',
|
||||
);
|
||||
}
|
||||
|
||||
private function exportAlibabaAccountBills(Request $request, string $section, string $page, string $tab)
|
||||
{
|
||||
$shopName = mb_substr(trim((string) $request->query('shop_name', '')), 0, 191);
|
||||
$productType = mb_substr(trim((string) $request->query('product_type', '')), 0, 191);
|
||||
$fromDate = $this->dateFilter($request->query('from'));
|
||||
$toDate = $this->dateFilter($request->query('to'));
|
||||
$redirectParameters = $this->marketingBillRedirectParameters($section, $page, $tab, [
|
||||
'shop_name' => $shopName,
|
||||
'product_type' => $productType,
|
||||
'from' => $fromDate,
|
||||
'to' => $toDate,
|
||||
]);
|
||||
$repository = app(AlibabaBillRepository::class);
|
||||
|
||||
if (! $repository->usesBusinessMirror()) {
|
||||
return redirect()->route('business.pages.show', $redirectParameters)
|
||||
->with('error', '阿里巴巴账单共享业务镜像尚未完成同步,暂时不能导出;请稍后重试。');
|
||||
}
|
||||
|
||||
return $this->streamMarketingAccountBillCsv(
|
||||
$repository->exportRows(
|
||||
$shopName !== '' ? $shopName : null,
|
||||
$productType !== '' ? $productType : null,
|
||||
$fromDate !== '' ? $fromDate : null,
|
||||
$toDate !== '' ? $toDate : null,
|
||||
),
|
||||
['店铺名称', '交易时间', '产品类型', '入账金额(元)', '出帐金额(元)'],
|
||||
static fn (array $row): array => [
|
||||
$row['shop_name'] ?? '', $row['trade_day'] ?? '', $row['product_type'] ?? '',
|
||||
$row['income_amount'] ?? 0, $row['expense_amount'] ?? 0,
|
||||
],
|
||||
'alibaba_account_bills_' . $this->marketingBillRange($fromDate, $toDate) . '.csv',
|
||||
$redirectParameters,
|
||||
'阿里巴巴账单导出暂时无法完成,请稍后重试。',
|
||||
);
|
||||
}
|
||||
|
||||
private function exportJdAccountBills(Request $request, string $section, string $page, string $tab)
|
||||
{
|
||||
$ledger = $request->query('jd_ledger') === 'consumption' ? 'consumption' : 'recharge';
|
||||
$storeName = mb_substr(trim((string) $request->query('store_name', '')), 0, 191);
|
||||
$fromDate = $this->dateFilter($request->query('from'));
|
||||
$toDate = $this->dateFilter($request->query('to'));
|
||||
$redirectParameters = $this->marketingBillRedirectParameters($section, $page, $tab, [
|
||||
'jd_ledger' => $ledger,
|
||||
'store_name' => $storeName,
|
||||
'from' => $fromDate,
|
||||
'to' => $toDate,
|
||||
]);
|
||||
$repository = app(JdWalletBillRepository::class);
|
||||
|
||||
if (! $repository->usesBusinessMirror($ledger)) {
|
||||
return redirect()->route('business.pages.show', $redirectParameters)
|
||||
->with('error', '京东账单共享业务镜像尚未完成同步,暂时不能导出;请稍后重试。');
|
||||
}
|
||||
|
||||
$headers = $ledger === 'consumption'
|
||||
? ['店铺名称', '金额(元)', '余额(元)', '交易类型', '投放日期', '结算日期', '流水号']
|
||||
: ['店铺名称', '金额(元)', '余额(元)', '交易类型', '商户订单号', '充值时间', '到账时间', '流水号', '状态', '备注'];
|
||||
$rowValues = $ledger === 'consumption'
|
||||
? static fn (array $row): array => [
|
||||
$row['store_name'] ?? '', $row['amount'] ?? 0, $row['balance'] ?? 0,
|
||||
$row['transaction_type'] ?? '', $row['investment_date'] ?? '',
|
||||
$row['settlement_date'] ?? '', $row['serial_number'] ?? '',
|
||||
]
|
||||
: static fn (array $row): array => [
|
||||
$row['store_name'] ?? '', $row['amount'] ?? 0, $row['balance'] ?? 0,
|
||||
$row['transaction_type'] ?? '', $row['order_number'] ?? '', $row['recharge_time'] ?? '',
|
||||
$row['arrival_time'] ?? '', $row['serial_number'] ?? '', $row['status'] ?? '', $row['notes'] ?? '',
|
||||
];
|
||||
|
||||
return $this->streamMarketingAccountBillCsv(
|
||||
$repository->exportRows(
|
||||
$ledger,
|
||||
$storeName !== '' ? $storeName : null,
|
||||
$fromDate !== '' ? $fromDate : null,
|
||||
$toDate !== '' ? $toDate : null,
|
||||
),
|
||||
$headers,
|
||||
$rowValues,
|
||||
'jd_' . $ledger . '_account_bills_' . $this->marketingBillRange($fromDate, $toDate) . '.csv',
|
||||
$redirectParameters,
|
||||
'京东账单导出暂时无法完成,请稍后重试。',
|
||||
);
|
||||
}
|
||||
|
||||
private function exportPinduoduoAccountBills(Request $request, string $section, string $page, string $tab)
|
||||
{
|
||||
$storeName = mb_substr(trim((string) $request->query('store_name', '')), 0, 191);
|
||||
$fromDate = $this->dateFilter($request->query('from'));
|
||||
$toDate = $this->dateFilter($request->query('to'));
|
||||
$flowType = in_array($request->query('flow_type'), ['income', 'expense'], true)
|
||||
? (string) $request->query('flow_type')
|
||||
: '';
|
||||
$redirectParameters = $this->marketingBillRedirectParameters($section, $page, $tab, [
|
||||
'store_name' => $storeName,
|
||||
'from' => $fromDate,
|
||||
'to' => $toDate,
|
||||
'flow_type' => $flowType,
|
||||
]);
|
||||
$repository = app(PinduoduoSpreadBillRepository::class);
|
||||
|
||||
if (! $repository->usesBusinessMirror()) {
|
||||
return redirect()->route('business.pages.show', $redirectParameters)
|
||||
->with('error', '拼多多账单共享业务镜像尚未完成同步,暂时不能导出;请稍后重试。');
|
||||
}
|
||||
|
||||
return $this->streamMarketingAccountBillCsv(
|
||||
$repository->exportRows(
|
||||
$storeName !== '' ? $storeName : null,
|
||||
$fromDate !== '' ? $fromDate : null,
|
||||
$toDate !== '' ? $toDate : null,
|
||||
$flowType !== '' ? $flowType : null,
|
||||
),
|
||||
['店铺名称', '金额(元)', '余额(元)', '摘要', '收支类型', '资金类型', '费用时间'],
|
||||
static fn (array $row): array => [
|
||||
$row['store_name'] ?? '', $row['amount'] ?? 0, $row['balance'] ?? 0,
|
||||
$row['summary'] ?? '', $row['income_expense_type'] ?? '',
|
||||
$row['fund_type'] ?? '', $row['cost_time'] ?? '',
|
||||
],
|
||||
'pinduoduo_account_bills_' . $this->marketingBillRange($fromDate, $toDate) . '.csv',
|
||||
$redirectParameters,
|
||||
'拼多多账单导出暂时无法完成,请稍后重试。',
|
||||
);
|
||||
}
|
||||
|
||||
private function streamMarketingAccountBillCsv(
|
||||
iterable $rows,
|
||||
array $headers,
|
||||
callable $rowValues,
|
||||
string $filename,
|
||||
array $redirectParameters,
|
||||
string $errorMessage,
|
||||
) {
|
||||
$csv = null;
|
||||
|
||||
try {
|
||||
if (! $repository->usesBusinessMirror()) {
|
||||
return redirect()->route('business.pages.show', $redirectParameters)
|
||||
->with('error', '拼多多账单共享业务镜像尚未完成同步,暂时不能导出;请稍后重试。');
|
||||
}
|
||||
|
||||
$csv = fopen('php://temp/maxmemory:5242880', 'w+b');
|
||||
if ($csv === false) {
|
||||
throw new \RuntimeException('无法创建拼多多账单导出临时文件。');
|
||||
throw new \RuntimeException('无法创建账单导出临时文件。');
|
||||
}
|
||||
|
||||
fputs($csv, chr(0xEF) . chr(0xBB) . chr(0xBF));
|
||||
fputcsv($csv, ['店铺名称', '金额(元)', '余额(元)', '摘要', '收支类型', '资金类型', '费用时间']);
|
||||
fputcsv($csv, $headers);
|
||||
|
||||
foreach ($repository->exportRows(
|
||||
$filters['store_name'],
|
||||
$filters['from'],
|
||||
$filters['to'],
|
||||
$filters['flow_type'],
|
||||
) as $row) {
|
||||
fputcsv($csv, [
|
||||
$row['store_name'] ?? '',
|
||||
$row['amount'] ?? 0,
|
||||
$row['balance'] ?? 0,
|
||||
$row['summary'] ?? '',
|
||||
$row['income_expense_type'] ?? '',
|
||||
$row['fund_type'] ?? '',
|
||||
$row['cost_time'] ?? '',
|
||||
]);
|
||||
foreach ($rows as $row) {
|
||||
fputcsv($csv, $rowValues($row));
|
||||
}
|
||||
|
||||
rewind($csv);
|
||||
@ -548,20 +690,32 @@ class Pages extends Controller
|
||||
report($exception);
|
||||
|
||||
return redirect()->route('business.pages.show', $redirectParameters)
|
||||
->with('error', '拼多多账单导出暂时无法完成,请稍后重试。');
|
||||
->with('error', $errorMessage);
|
||||
}
|
||||
|
||||
$range = ($fromDate ?: 'all') . '_' . ($toDate ?: 'all');
|
||||
|
||||
return response()->streamDownload(function () use ($csv): void {
|
||||
fpassthru($csv);
|
||||
fclose($csv);
|
||||
}, 'pinduoduo_account_bills_' . $range . '.csv', [
|
||||
}, $filename, [
|
||||
'Content-Type' => 'text/csv; charset=UTF-8',
|
||||
'Cache-Control' => 'private, no-store',
|
||||
]);
|
||||
}
|
||||
|
||||
private function marketingBillRedirectParameters(string $section, string $page, string $tab, array $filters): array
|
||||
{
|
||||
return array_filter(array_merge([
|
||||
'section' => $section,
|
||||
'page' => $page,
|
||||
'tab' => $tab,
|
||||
], $filters), static fn (mixed $value): bool => $value !== '' && $value !== null);
|
||||
}
|
||||
|
||||
private function marketingBillRange(string $fromDate, string $toDate): string
|
||||
{
|
||||
return ($fromDate ?: 'all') . '_' . ($toDate ?: 'all');
|
||||
}
|
||||
|
||||
protected function contentForBoardDeductions(): array
|
||||
{
|
||||
$filters = [
|
||||
|
||||
@ -103,6 +103,44 @@ class AlibabaBillRepository
|
||||
];
|
||||
}
|
||||
|
||||
/** @return \Generator<int,array<string,mixed>> */
|
||||
public function exportRows(
|
||||
?string $shopName = null,
|
||||
?string $productType = null,
|
||||
?string $fromDate = null,
|
||||
?string $toDate = null,
|
||||
int $batchSize = 100,
|
||||
): \Generator {
|
||||
if (! $this->usesBusinessMirror()) {
|
||||
throw new \RuntimeException('阿里巴巴账单共享业务镜像尚未完成同步。');
|
||||
}
|
||||
|
||||
$batchSize = max(1, min(100, $batchSize));
|
||||
$page = 1;
|
||||
$exported = 0;
|
||||
|
||||
do {
|
||||
$result = $this->mirror->paginateAlibaba(
|
||||
$shopName,
|
||||
$productType,
|
||||
$fromDate,
|
||||
$toDate,
|
||||
$page,
|
||||
$batchSize,
|
||||
);
|
||||
$rows = is_array($result['rows'] ?? null) ? $result['rows'] : [];
|
||||
$total = max(0, (int) ($result['total'] ?? 0));
|
||||
|
||||
foreach ($rows as $row) {
|
||||
yield $this->normalize($row);
|
||||
}
|
||||
|
||||
$count = count($rows);
|
||||
$exported += $count;
|
||||
$page++;
|
||||
} while ($count > 0 && $exported < $total);
|
||||
}
|
||||
|
||||
/** @return array{income_total: float, expense_total: float, net: float, count: int} */
|
||||
public function summary(
|
||||
?string $shopName = null,
|
||||
|
||||
@ -117,6 +117,43 @@ class JdWalletBillRepository
|
||||
], 'creatTime', 'id', fn (array $row): array => $this->normalizeConsumption($row));
|
||||
}
|
||||
|
||||
/** @return \Generator<int,array<string,mixed>> */
|
||||
public function exportRows(
|
||||
string $ledger,
|
||||
?string $shopName = null,
|
||||
?string $fromDate = null,
|
||||
?string $toDate = null,
|
||||
int $batchSize = 100,
|
||||
): \Generator {
|
||||
$ledger = $ledger === 'consumption' ? 'consumption' : 'recharge';
|
||||
|
||||
if (! $this->usesBusinessMirror($ledger)) {
|
||||
throw new \RuntimeException('京东账单共享业务镜像尚未完成同步。');
|
||||
}
|
||||
|
||||
$batchSize = max(1, min(100, $batchSize));
|
||||
$page = 1;
|
||||
$exported = 0;
|
||||
|
||||
do {
|
||||
$result = $ledger === 'consumption'
|
||||
? $this->mirror->paginateJdConsumption($shopName, $fromDate, $toDate, $page, $batchSize)
|
||||
: $this->mirror->paginateJdRecharge($shopName, $fromDate, $toDate, $page, $batchSize);
|
||||
$rows = is_array($result['rows'] ?? null) ? $result['rows'] : [];
|
||||
$total = max(0, (int) ($result['total'] ?? 0));
|
||||
|
||||
foreach ($rows as $row) {
|
||||
yield $ledger === 'consumption'
|
||||
? $this->normalizeConsumption($row)
|
||||
: $this->normalizeRecharge($row);
|
||||
}
|
||||
|
||||
$count = count($rows);
|
||||
$exported += $count;
|
||||
$page++;
|
||||
} while ($count > 0 && $exported < $total);
|
||||
}
|
||||
|
||||
/** @return array{amount_total: float, balance_total: float, count: int} */
|
||||
public function summaryRecharge(?string $shopName = null, ?string $fromDate = null, ?string $toDate = null): array
|
||||
{
|
||||
|
||||
@ -158,6 +158,44 @@ class TmallTaobaoSpreadBillRepository
|
||||
];
|
||||
}
|
||||
|
||||
/** @return \Generator<int,array<string,mixed>> */
|
||||
public function exportRows(
|
||||
?string $storeName = null,
|
||||
?string $fromDate = null,
|
||||
?string $toDate = null,
|
||||
?string $flowType = null,
|
||||
int $batchSize = 200,
|
||||
): \Generator {
|
||||
if (! $this->usesBusinessMirror()) {
|
||||
throw new \RuntimeException('淘系账单共享业务镜像尚未完成同步。');
|
||||
}
|
||||
|
||||
$batchSize = max(1, min(200, $batchSize));
|
||||
$page = 1;
|
||||
$exported = 0;
|
||||
|
||||
do {
|
||||
$result = $this->mirror->paginateTaobao(
|
||||
$storeName,
|
||||
$fromDate,
|
||||
$toDate,
|
||||
$flowType,
|
||||
$page,
|
||||
$batchSize,
|
||||
);
|
||||
$rows = is_array($result['rows'] ?? null) ? $result['rows'] : [];
|
||||
$total = max(0, (int) ($result['total'] ?? 0));
|
||||
|
||||
foreach ($rows as $row) {
|
||||
yield $this->normalize($row);
|
||||
}
|
||||
|
||||
$count = count($rows);
|
||||
$exported += $count;
|
||||
$page++;
|
||||
} while ($count > 0 && $exported < $total);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前筛选条件下的汇总(金额单位:元)。
|
||||
*
|
||||
|
||||
@ -35,6 +35,15 @@
|
||||
$incomeWan = ((float) ($summary['income_total'] ?? 0)) / 10000;
|
||||
$expenseWan = ((float) ($summary['expense_total'] ?? 0)) / 10000;
|
||||
$netWan = ((float) ($summary['net'] ?? 0)) / 10000;
|
||||
$exportParameters = array_filter([
|
||||
'section' => request()->route('section') ?? 'billing-transactions',
|
||||
'page' => request()->route('page') ?? 'marketing-account-bills',
|
||||
'tab' => 'taobao-wechat',
|
||||
'shop_name' => (string) ($filters['shop_name'] ?? ''),
|
||||
'product_type' => (string) ($filters['product_type'] ?? ''),
|
||||
'from' => (string) ($filters['from'] ?? ''),
|
||||
'to' => (string) ($filters['to'] ?? ''),
|
||||
], static fn ($value) => $value !== '');
|
||||
@endphp
|
||||
|
||||
<div class="w-full">
|
||||
@ -91,6 +100,11 @@
|
||||
<div class="flex items-end gap-2">
|
||||
<button type="submit" class="alibaba-bill-primary"><span class="material-icons text-base" aria-hidden="true">search</span>查询</button>
|
||||
<a href="{{ request()->url() }}" class="alibaba-bill-secondary"><span class="material-icons text-base" aria-hidden="true">clear</span>清除</a>
|
||||
<a href="{{ route('business.pages.export', $exportParameters) }}"
|
||||
class="alibaba-bill-secondary"
|
||||
aria-label="导出当前筛选结果">
|
||||
<span class="material-icons text-base" aria-hidden="true">download</span>导出
|
||||
</a>
|
||||
</div>
|
||||
@if (($filters['shop_name'] ?? '') !== '' || ($filters['product_type'] ?? '') !== '' || ($filters['from'] ?? '') !== '' || ($filters['to'] ?? '') !== '')
|
||||
<div class="ml-auto self-center text-xs text-gray-500">共匹配 {{ $bills->total() }} 条</div>
|
||||
|
||||
@ -49,6 +49,15 @@
|
||||
$balanceWan = (float) ($summary['balance_total'] ?? 0) / 10000;
|
||||
$isConsumption = $ledger === 'consumption';
|
||||
$currentUrl = request()->url();
|
||||
$exportParameters = array_filter([
|
||||
'section' => request()->route('section') ?? 'billing-transactions',
|
||||
'page' => request()->route('page') ?? 'marketing-account-bills',
|
||||
'tab' => 'jd-wallet',
|
||||
'jd_ledger' => $ledger,
|
||||
'store_name' => (string) ($filters['store_name'] ?? ''),
|
||||
'from' => (string) ($filters['from'] ?? ''),
|
||||
'to' => (string) ($filters['to'] ?? ''),
|
||||
], static fn ($value) => $value !== '');
|
||||
@endphp
|
||||
|
||||
<div class="w-full" data-jd-wallet-panel>
|
||||
@ -112,6 +121,11 @@
|
||||
<div class="flex items-end gap-2">
|
||||
<button type="submit" class="jd-wallet-button jd-wallet-button-primary"><span class="material-icons text-base" aria-hidden="true">search</span>查询</button>
|
||||
<a href="{{ $currentUrl }}?jd_ledger={{ $ledger }}" data-jd-wallet-navigation class="jd-wallet-button jd-wallet-button-secondary"><span class="material-icons text-base" aria-hidden="true">clear</span>清除</a>
|
||||
<a href="{{ route('business.pages.export', $exportParameters) }}"
|
||||
class="jd-wallet-button jd-wallet-button-secondary"
|
||||
aria-label="导出当前筛选结果">
|
||||
<span class="material-icons text-base" aria-hidden="true">download</span>导出
|
||||
</a>
|
||||
</div>
|
||||
<div class="ml-auto self-center text-xs text-gray-500">共匹配 {{ $bills->total() }} 条</div>
|
||||
</div>
|
||||
|
||||
@ -77,6 +77,15 @@
|
||||
$currentFrom = (string) ($filters['from'] ?? '');
|
||||
$currentTo = (string) ($filters['to'] ?? '');
|
||||
$currentFlowType = (string) ($filters['flow_type'] ?? '');
|
||||
$exportParameters = array_filter([
|
||||
'section' => request()->route('section') ?? 'billing-transactions',
|
||||
'page' => request()->route('page') ?? 'marketing-account-bills',
|
||||
'tab' => 'taobao-alipay',
|
||||
'store_name' => $currentStore,
|
||||
'from' => $currentFrom,
|
||||
'to' => $currentTo,
|
||||
'flow_type' => $currentFlowType,
|
||||
], static fn ($value) => $value !== '');
|
||||
@endphp
|
||||
<form method="GET" action="{{ request()->url() }}"
|
||||
class="mb-4 rounded-lg border border-gray-200 bg-gray-50 px-4 py-3">
|
||||
@ -119,6 +128,12 @@
|
||||
<span class="material-icons text-base" aria-hidden="true">clear</span>
|
||||
清除
|
||||
</a>
|
||||
<a href="{{ route('business.pages.export', $exportParameters) }}"
|
||||
class="bill-filter-secondary"
|
||||
aria-label="导出当前筛选结果">
|
||||
<span class="material-icons text-base" aria-hidden="true">download</span>
|
||||
导出
|
||||
</a>
|
||||
</div>
|
||||
@if ($currentStore !== '' || $currentFrom !== '' || $currentTo !== '' || $currentFlowType !== '')
|
||||
<div class="ml-auto self-center text-xs text-gray-500">
|
||||
|
||||
@ -5,6 +5,7 @@ namespace Tests\Feature\Business;
|
||||
use App\Models\Auth\Permission;
|
||||
use Mockery;
|
||||
use Modules\EcommerceFinance\Exceptions\UpstreamSnapshotPendingException;
|
||||
use Modules\EcommerceFinance\Repositories\AlibabaBillRepository;
|
||||
use Modules\EcommerceFinance\Repositories\JdWalletBillRepository;
|
||||
use Modules\EcommerceFinance\Repositories\MarketingAccountBillMirrorRepository;
|
||||
use Modules\EcommerceFinance\Repositories\PinduoduoSpreadBillRepository;
|
||||
@ -82,6 +83,12 @@ class MarketingAccountBillsTabsTest extends FeatureTestCase
|
||||
->assertSeeText('淘系支付宝账户')
|
||||
->assertSeeText('Alibaba')
|
||||
->assertSeeText('京东')
|
||||
->assertSeeText('导出')
|
||||
->assertSee(e(route('business.pages.export', [
|
||||
'section' => 'billing-transactions',
|
||||
'page' => 'marketing-account-bills',
|
||||
'tab' => 'taobao-wechat',
|
||||
])), false)
|
||||
->assertDontSeeText('淘系微信账户')
|
||||
->assertDontSeeText('京东网银钱包账户')
|
||||
->assertSee('class="my-6 business-page--marketing-account-bills"', false);
|
||||
@ -184,6 +191,117 @@ class MarketingAccountBillsTabsTest extends FeatureTestCase
|
||||
->assertSessionHas('error', '拼多多账单共享业务镜像尚未完成同步,暂时不能导出;请稍后重试。');
|
||||
}
|
||||
|
||||
public function testTaobaoBillsExportUsesTheCurrentMirrorFilters(): void
|
||||
{
|
||||
$this->allowMarketingAccountBillsPage();
|
||||
$repository = Mockery::mock(TmallTaobaoSpreadBillRepository::class);
|
||||
$repository->shouldReceive('usesBusinessMirror')->once()->andReturnTrue();
|
||||
$repository->shouldReceive('exportRows')
|
||||
->once()
|
||||
->with('淘系筛选店铺', '2026-07-01', '2026-07-31', 'income')
|
||||
->andReturn((function (): \Generator {
|
||||
yield [
|
||||
'store_name' => '淘系筛选店铺',
|
||||
'bookkeeping_time' => '2026-07-31 09:00:00',
|
||||
'transaction_date' => '2026-07-31',
|
||||
'amount' => 12.34,
|
||||
'balance' => 56.78,
|
||||
'comment' => '充值',
|
||||
'fin_type' => 'DEBIT',
|
||||
'income_expense_type' => '收入',
|
||||
'transaction_type' => '充值',
|
||||
];
|
||||
})());
|
||||
$this->app->instance(TmallTaobaoSpreadBillRepository::class, $repository);
|
||||
|
||||
$response = $this->loginAs()->get(route('business.pages.export', [
|
||||
'section' => 'billing-transactions',
|
||||
'page' => 'marketing-account-bills',
|
||||
'tab' => 'taobao-alipay',
|
||||
'store_name' => '淘系筛选店铺',
|
||||
'from' => '2026-07-01',
|
||||
'to' => '2026-07-31',
|
||||
'flow_type' => 'income',
|
||||
]));
|
||||
|
||||
$response->assertOk()->assertDownload('taobao_account_bills_2026-07-01_2026-07-31.csv');
|
||||
$content = $response->streamedContent();
|
||||
$this->assertStringContainsString('店铺名称,记账日期,交易日期,金额(元),余额(元),备注,finType,收支类型,交易类型', $content);
|
||||
$this->assertStringContainsString('淘系筛选店铺,"2026-07-31 09:00:00",2026-07-31,12.34,56.78,充值,DEBIT,收入,充值', $content);
|
||||
}
|
||||
|
||||
public function testAlibabaBillsExportUsesTheCurrentMirrorFilters(): void
|
||||
{
|
||||
$this->allowMarketingAccountBillsPage();
|
||||
$repository = Mockery::mock(AlibabaBillRepository::class);
|
||||
$repository->shouldReceive('usesBusinessMirror')->once()->andReturnTrue();
|
||||
$repository->shouldReceive('exportRows')
|
||||
->once()
|
||||
->with('阿里筛选店铺', '全站推广', '2026-07-01', '2026-07-31')
|
||||
->andReturn((function (): \Generator {
|
||||
yield [
|
||||
'shop_name' => '阿里筛选店铺',
|
||||
'trade_day' => '2026-07-31 10:00:00',
|
||||
'product_type' => '全站推广',
|
||||
'income_amount' => 100.25,
|
||||
'expense_amount' => 20.5,
|
||||
];
|
||||
})());
|
||||
$this->app->instance(AlibabaBillRepository::class, $repository);
|
||||
|
||||
$response = $this->loginAs()->get(route('business.pages.export', [
|
||||
'section' => 'billing-transactions',
|
||||
'page' => 'marketing-account-bills',
|
||||
'tab' => 'taobao-wechat',
|
||||
'shop_name' => '阿里筛选店铺',
|
||||
'product_type' => '全站推广',
|
||||
'from' => '2026-07-01',
|
||||
'to' => '2026-07-31',
|
||||
]));
|
||||
|
||||
$response->assertOk()->assertDownload('alibaba_account_bills_2026-07-01_2026-07-31.csv');
|
||||
$content = $response->streamedContent();
|
||||
$this->assertStringContainsString('店铺名称,交易时间,产品类型,入账金额(元),出帐金额(元)', $content);
|
||||
$this->assertStringContainsString('阿里筛选店铺,"2026-07-31 10:00:00",全站推广,100.25,20.5', $content);
|
||||
}
|
||||
|
||||
public function testJdConsumptionBillsExportUsesTheCurrentMirrorFilters(): void
|
||||
{
|
||||
$this->allowMarketingAccountBillsPage();
|
||||
$repository = Mockery::mock(JdWalletBillRepository::class);
|
||||
$repository->shouldReceive('usesBusinessMirror')->once()->with('consumption')->andReturnTrue();
|
||||
$repository->shouldReceive('exportRows')
|
||||
->once()
|
||||
->with('consumption', '京东筛选店铺', '2026-07-01', '2026-07-31')
|
||||
->andReturn((function (): \Generator {
|
||||
yield [
|
||||
'store_name' => '京东筛选店铺',
|
||||
'amount' => 88.6,
|
||||
'balance' => 10.4,
|
||||
'transaction_type' => '推广消耗',
|
||||
'investment_date' => '2026-07-30 12:00:00',
|
||||
'settlement_date' => '2026-07-31 00:00:00',
|
||||
'serial_number' => 'JD-1',
|
||||
];
|
||||
})());
|
||||
$this->app->instance(JdWalletBillRepository::class, $repository);
|
||||
|
||||
$response = $this->loginAs()->get(route('business.pages.export', [
|
||||
'section' => 'billing-transactions',
|
||||
'page' => 'marketing-account-bills',
|
||||
'tab' => 'jd-wallet',
|
||||
'jd_ledger' => 'consumption',
|
||||
'store_name' => '京东筛选店铺',
|
||||
'from' => '2026-07-01',
|
||||
'to' => '2026-07-31',
|
||||
]));
|
||||
|
||||
$response->assertOk()->assertDownload('jd_consumption_account_bills_2026-07-01_2026-07-31.csv');
|
||||
$content = $response->streamedContent();
|
||||
$this->assertStringContainsString('店铺名称,金额(元),余额(元),交易类型,投放日期,结算日期,流水号', $content);
|
||||
$this->assertStringContainsString('京东筛选店铺,88.6,10.4,推广消耗,"2026-07-30 12:00:00","2026-07-31 00:00:00",JD-1', $content);
|
||||
}
|
||||
|
||||
public function testJdWalletHasRechargeAndConsumptionViewsWithoutChangingTheDataSource(): void
|
||||
{
|
||||
$this->allowMarketingAccountBillsPage();
|
||||
@ -201,6 +319,8 @@ class MarketingAccountBillsTabsTest extends FeatureTestCase
|
||||
->assertSeeText('消耗账单')
|
||||
->assertSeeText('金额(元)')
|
||||
->assertSeeText('充值时间')
|
||||
->assertSeeText('导出')
|
||||
->assertSee('aria-label="导出当前筛选结果"', false)
|
||||
->assertDontSeeText('投放日期')
|
||||
->assertSee('data-jd-wallet-panel', false)
|
||||
->assertSee('data-jd-wallet-navigation', false);
|
||||
|
||||
@ -90,6 +90,10 @@ class MarketingAccountBillMirrorRepositoryTest extends TestCase
|
||||
$pddMonthly = $pinduoduo->paginateMonthlyBalances('镜像店铺', '2026-06', 1, 25);
|
||||
$alibabaPage2 = $alibaba->paginate('阿里店', '全站', '2026-06-01', '2026-06-30', 2, 1);
|
||||
$jdRechargePage2 = $jd->paginateRecharge('京东店', '2026-06-01', '2026-06-30', 2, 1);
|
||||
$taobaoExport = iterator_to_array($taobao->exportRows('镜像店铺', '2026-06-01', '2026-06-30', null, 1));
|
||||
$alibabaExport = iterator_to_array($alibaba->exportRows('阿里店', '全站', '2026-06-01', '2026-06-30', 1));
|
||||
$jdRechargeExport = iterator_to_array($jd->exportRows('recharge', '京东店', '2026-06-01', '2026-06-30', 1));
|
||||
$jdConsumptionExport = iterator_to_array($jd->exportRows('consumption', '京东店', '2026-06-01', '2026-06-30', 1));
|
||||
|
||||
$this->assertSame(2, $taobaoPage2['total']);
|
||||
$this->assertSame(50.0, $taobaoPage2['rows'][0]['amount']);
|
||||
@ -116,6 +120,12 @@ class MarketingAccountBillMirrorRepositoryTest extends TestCase
|
||||
$this->assertSame(['京东店'], $jd->shopOptions());
|
||||
$this->assertSame(300.0, $jd->summaryRecharge('京东店')['amount_total']);
|
||||
$this->assertSame(30.0, $jd->summaryConsumption('京东店')['amount_total']);
|
||||
$this->assertCount(2, $taobaoExport);
|
||||
$this->assertSame([20.0, 50.0], array_column($taobaoExport, 'amount'));
|
||||
$this->assertCount(2, $alibabaExport);
|
||||
$this->assertSame([20.0, 10.0], array_column($alibabaExport, 'income_amount'));
|
||||
$this->assertSame(['R2', 'R1'], array_column($jdRechargeExport, 'order_number'));
|
||||
$this->assertSame(['C1'], array_column($jdConsumptionExport, 'serial_number'));
|
||||
}
|
||||
|
||||
public function test_unready_dataset_keeps_the_existing_upstream_repository_path(): void
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user