208 lines
9.5 KiB
PHP
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,
|
|
];
|
|
}
|
|
}
|