CaiWuXiTong/tests/Unit/EcommerceFinance/PromotionMarketingExpenseRepositoryTest.php
qiuxiang 3be1639fb2 feat: 推广费营销账户明细汇总
新增推广费(营销账户)页签、平台与原始店铺汇总、备注筛选和明细弹窗。
2026-10-27 19:27:41 +08:00

208 lines
9.5 KiB
PHP

<?php
namespace Tests\Unit\EcommerceFinance;
use Illuminate\Support\Facades\Cache;
use Mockery;
use Modules\EcommerceFinance\Repositories\PromotionMarketingExpenseRepository;
use Modules\EcommerceFinance\Services\UpstreamReadOnlyQueryFactory;
use Tests\TestCase;
class PromotionMarketingExpenseRepositoryTest extends TestCase
{
protected function setUp(): void
{
parent::setUp();
Cache::flush();
}
public function test_it_merges_seven_sources_and_paginates_by_transaction_date(): void
{
$repository = $this->repository();
$repository->histogramRows = [
'tmall_spread_bills' => [['report_date' => '2026-10-23', 'row_count' => 1, 'expense' => 10]],
'alibaba_bill' => [['report_date' => '2026-10-23', 'row_count' => 1, 'expense' => 20]],
'pdd_spread_bills' => [['report_date' => '2026-10-23', 'row_count' => 1, 'expense' => 30]],
'jd_jzt_finance_records' => [['report_date' => '2026-10-23', 'row_count' => 1, 'expense' => 40]],
'dy_qianchuan_finance_transactions' => [['report_date' => '2026-10-23', 'row_count' => 1, 'expense' => 50]],
'dw_ad_finance_records' => [['report_date' => '2026-10-23', 'row_count' => 1, 'expense' => 60]],
'vip_marketingDailyReport' => [['report_date' => '2026-10-23', 'row_count' => 1, 'expense' => 70]],
];
$repository->detailRows = [
'tmall_spread_bills' => [$this->row('天猫-卓禾家具旗舰店', '2026-10-23 09:00:00', '淘宝备注', null, 10)],
'alibaba_bill' => [$this->row('阿里巴巴-常州曼向家居有限公司', '2026-10-23', '智能营销方案', null, 20)],
'pdd_spread_bills' => [$this->row('拼多多-慕加家具旗舰店', '2026-10-23 09:30:00', '商品推广', '1', 30)],
'jd_jzt_finance_records' => [$this->row('京东-亿家木家具旗舰店', '2026-10-23', '快车扣费', '推广费', 40)],
'dy_qianchuan_finance_transactions' => [$this->row('抖音店', '2026-10-23', '千川推广', null, 50)],
'dw_ad_finance_records' => [$this->row('得物-墨尚思', '2026-10-23', '得物推服务', '现金', 60)],
'vip_marketingDailyReport' => [$this->row('唯品店', '2026-10-23', '推广费', null, 70)],
];
$result = $repository->paginate([
'start' => '2026-10-01',
'end' => '2026-10-31',
'store_name' => '',
], 2, 2);
$this->assertSame(7, $result['total']);
$this->assertSame(280.0, $result['summary']['expense']);
$this->assertSame(4, $result['last_page']);
$platformAmounts = array_column($result['platform_summary'], 'amount', 'platform');
$this->assertSame(30.0, $platformAmounts['淘系']);
$this->assertSame(30.0, $platformAmounts['拼多多']);
$this->assertSame(40.0, $platformAmounts['京东']);
$this->assertSame(50.0, $platformAmounts['抖音']);
$this->assertSame(60.0, $platformAmounts['得物']);
$this->assertSame(70.0, $platformAmounts['唯品会']);
$this->assertSame(
['拼多多-慕加家具旗舰店', '京东-亿家木家具旗舰店'],
array_column($result['rows'], 'store_name'),
);
$this->assertSame('商品推广 · 现金', $result['rows'][0]['notes']);
$this->assertSame('快车扣费 · 推广费', $result['rows'][1]['notes']);
$this->assertSame([30.0, 40.0], array_column($result['rows'], 'expense'));
$this->assertTrue($repository->sawSql('tmall_spread_bills', "finType] = 'CREDIT'"));
$this->assertTrue($repository->sawSql('tmall_spread_bills', 'c.[shopName] AS store_name'));
$this->assertTrue($repository->sawSql('alibaba_bill', 'c.[shopName] AS store_name'));
$this->assertTrue($repository->sawSql('pdd_spread_bills', 'c.[shopName] AS store_name'));
$this->assertTrue($repository->sawSql('pdd_spread_bills', 'flowType] = 2'));
$this->assertTrue($repository->sawSql('jd_jzt_finance_records', "in_out_type] = 'OUT'"));
$this->assertTrue($repository->sawSql('jd_jzt_finance_records', 'c.[shop_name] AS store_name'));
$this->assertTrue($repository->sawSql('dw_ad_finance_records', 'trade_amount_yuan'));
$this->assertTrue($repository->sawSql('dw_ad_finance_records', 'c.[shop_name] AS store_name'));
$this->assertTrue($repository->sawSql('vip_marketingDailyReport', 'accountName'));
}
public function test_store_options_are_merged_and_deduplicated(): void
{
$repository = $this->repository();
$repository->storeRows = [
'tmall_spread_bills' => [['store_name' => '乙店'], ['store_name' => '甲店']],
'alibaba_bill' => [['store_name' => '甲店']],
];
$this->assertSame(['乙店', '甲店'], $repository->storeOptions([
'start' => '2026-10-01',
'end' => '2026-10-31',
]));
}
public function test_platform_and_store_summary_uses_the_installation_platform_categories(): void
{
$repository = $this->repository();
$repository->storeSummaryRows = [
'tmall_spread_bills' => [
['store_name' => '天猫-甲店', 'amount' => 10],
['store_name' => '天猫-乙店', 'amount' => 5],
],
'alibaba_bill' => [['store_name' => '阿里巴巴-甲店', 'amount' => 20]],
'pdd_spread_bills' => [['store_name' => '拼多多-丙店', 'amount' => 30]],
'jd_jzt_finance_records' => [['store_name' => '京东-丁店', 'amount' => 40]],
'dy_qianchuan_finance_transactions' => [['store_name' => '抖音-戊店', 'amount' => 50]],
'dw_ad_finance_records' => [['store_name' => '得物-己店', 'amount' => 60]],
'vip_marketingDailyReport' => [['store_name' => '唯品-庚店', 'amount' => 70]],
];
$result = $repository->storeSummary([
'start' => '2026-10-01',
'end' => '2026-10-31',
]);
$this->assertSame(285.0, $result['total']);
$amounts = array_column($result['platform_summary'], 'amount', 'platform');
$this->assertSame(35.0, $amounts['淘系']);
$this->assertSame(30.0, $amounts['拼多多']);
$this->assertSame(40.0, $amounts['京东']);
$this->assertSame(50.0, $amounts['抖音']);
$this->assertSame(60.0, $amounts['得物']);
$this->assertSame(70.0, $amounts['唯品会']);
$this->assertContains(
['platform' => '淘系', 'store_name' => '阿里巴巴-甲店', 'amount' => 20.0],
$result['store_summary'],
);
$this->assertContains(
['platform' => '唯品会', 'store_name' => '唯品会JITX_亿家达', 'amount' => 70.0],
$result['store_summary'],
);
$noteRepository = $this->repository();
$noteRepository->storeSummaryRows = $repository->storeSummaryRows;
$noteRepository->storeSummary([
'start' => '2026-10-01',
'end' => '2026-10-31',
'note' => '推广',
]);
$this->assertTrue($noteRepository->sawSql(
'dy_qianchuan_finance_transactions',
"(c.[remark]) LIKE ? ESCAPE '!'",
));
}
private function repository(): PromotionMarketingExpenseRepository
{
return new class(Mockery::mock(UpstreamReadOnlyQueryFactory::class)) extends PromotionMarketingExpenseRepository {
public array $histogramRows = [];
public array $detailRows = [];
public array $storeRows = [];
public array $storeSummaryRows = [];
public array $queries = [];
protected function select(string $connection, string $sql, array $bindings): array
{
$this->queries[] = compact('connection', 'sql', 'bindings');
foreach (array_merge(array_keys($this->histogramRows), array_keys($this->detailRows), array_keys($this->storeRows), array_keys($this->storeSummaryRows)) as $table) {
if (! str_contains($sql, $table)) {
continue;
}
if (str_contains($sql, 'GROUP BY') && str_contains($sql, 'AS store_name')) {
return $this->storeSummaryRows[$table] ?? [];
}
if (str_contains($sql, 'GROUP BY')) {
return $this->histogramRows[$table] ?? [];
}
if (str_contains($sql, 'DISTINCT TOP (500)')) {
return $this->storeRows[$table] ?? [];
}
return $this->detailRows[$table] ?? [];
}
return [];
}
public function sawSql(string $table, string $fragment): bool
{
foreach ($this->queries as $query) {
if (str_contains($query['sql'], $table) && str_contains($query['sql'], $fragment)) {
return true;
}
}
return false;
}
};
}
/** @return array<string,mixed> */
private function row(
string $store,
string $occurredAt,
string $primary,
?string $secondary,
float $expense,
): array {
return [
'report_date' => substr($occurredAt, 0, 10),
'store_name' => $store,
'occurred_at' => $occurredAt,
'note_primary' => $primary,
'note_secondary' => $secondary,
'expense' => $expense,
];
}
}