CaiWuXiTong/tests/Unit/EcommerceFinance/StoreReportRepositoryTest.php

152 lines
7.0 KiB
PHP

<?php
namespace Tests\Unit\EcommerceFinance;
use App\Business\UpstreamBusinessInformation;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Mockery;
use Modules\EcommerceFinance\Repositories\BusinessInformationMirrorRepository;
use Modules\EcommerceFinance\Repositories\InstallationDetailMirrorRepository;
use Modules\EcommerceFinance\Repositories\StoreReportRepository;
use Modules\EcommerceFinance\Services\UpstreamQueryCacheFactory;
use Tests\TestCase;
class StoreReportRepositoryTest extends TestCase
{
private string $databasePath;
private StoreReportRepository $repository;
protected function setUp(): void
{
parent::setUp();
$this->databasePath = tempnam(sys_get_temp_dir(), 'store-report-');
config(['database.connections.bills_sqlite' => [
'driver' => 'sqlite',
'database' => $this->databasePath,
'prefix' => '',
'foreign_key_constraints' => false,
]]);
DB::purge('bills_sqlite');
$schema = Schema::connection('bills_sqlite');
$schema->create('store_lifecycles', function (Blueprint $table): void {
$table->increments('id');
$table->string('erp_store_name');
$table->string('company_name')->nullable();
$table->string('effective_at')->nullable();
$table->string('expires_at')->nullable();
});
$schema->create('bills', function (Blueprint $table): void {
$table->increments('id');
$table->string('store_name');
$table->string('occurred_at')->nullable();
$table->decimal('income', 18, 2)->default(0);
});
$this->repository = new StoreReportRepository(app('db'));
}
protected function tearDown(): void
{
DB::purge('bills_sqlite');
if (is_file($this->databasePath)) {
unlink($this->databasePath);
}
parent::tearDown();
}
public function test_lifecycle_boundaries_split_monthly_and_period_income_without_double_counting(): void
{
$db = DB::connection('bills_sqlite');
$db->table('store_lifecycles')->insert([
['erp_store_name' => '店铺A', 'company_name' => '主体甲', 'effective_at' => '2026-05-01', 'expires_at' => '2026-07-01'],
['erp_store_name' => '店铺A', 'company_name' => '主体乙', 'effective_at' => '2026-07-01', 'expires_at' => null],
]);
$db->table('bills')->insert([
['store_name' => '店铺A', 'occurred_at' => '2026-04-30 23:59:59', 'income' => 9],
['store_name' => '店铺A', 'occurred_at' => '2026-05-01 00:00:00', 'income' => 100],
['store_name' => '店铺A', 'occurred_at' => '2026-06-30 23:59:59', 'income' => 60],
['store_name' => '店铺A', 'occurred_at' => '2026-07-01 00:00:00', 'income' => 70],
['store_name' => '店铺A', 'occurred_at' => '2026-08-01 00:00:00', 'income' => 80],
['store_name' => '店铺A', 'occurred_at' => '2027-01-01 00:00:00', 'income' => 5],
]);
$rows = $this->repository->rows([
['erp_store_name' => '店铺A', 'company_name' => '旧主体', 'store_status' => '3'],
]);
$this->assertCount(2, $rows);
$this->assertSame(['主体甲', '主体乙'], array_column($rows, 'entity'));
$this->assertSame(['2026-05-01', '2026-07-01'], array_column($rows, 'effective_at'));
$this->assertSame(['2026-07-01', '2999-01-01'], array_column($rows, 'invalid_at'));
$this->assertSame([160.0, 155.0], array_column($rows, 'period_sales'));
$this->assertSame([100.0, 0.0], array_column($rows, 'sales_05'));
$this->assertSame([0.0, 70.0], array_column($rows, 'sales_07'));
$this->assertSame(['关店中', '关店中'], array_column($rows, 'status'));
}
public function test_missing_lifecycle_uses_upstream_company_and_default_period(): void
{
$db = DB::connection('bills_sqlite');
$db->table('bills')->insert([
['store_name' => '无生命周期店铺', 'occurred_at' => '2026-05-03 12:00:00', 'income' => 25.5],
]);
$rows = $this->repository->rows([
['erp_store_name' => '无生命周期店铺', 'company_name' => '公司表主体', 'store_status' => '0'],
]);
$this->assertCount(1, $rows);
$this->assertSame('公司表主体', $rows[0]['entity']);
$this->assertSame('2026-05-01', $rows[0]['effective_at']);
$this->assertSame('2999-01-01', $rows[0]['invalid_at']);
$this->assertSame(25.5, $rows[0]['period_sales']);
$this->assertSame(25.5, $rows[0]['sales_05']);
$this->assertSame('开店中', $rows[0]['status']);
}
public function test_shop_state_mapping_keeps_opening_closing_and_closed_labels(): void
{
$rows = $this->repository->rows([
['erp_store_name' => '开店中店铺', 'store_status' => '0'],
['erp_store_name' => '关店中店铺', 'store_status' => '3'],
['erp_store_name' => '已关店店铺', 'store_status' => '6'],
]);
$this->assertSame(['开店中', '关店中', '关店'], array_column($rows, 'status'));
}
public function test_upstream_business_information_exposes_expanded_rows_for_store_report(): void
{
DB::connection('bills_sqlite')->table('store_lifecycles')->insert([
['erp_store_name' => '店铺A', 'company_name' => '主体甲', 'effective_at' => '2026-05-01', 'expires_at' => '2026-07-01'],
['erp_store_name' => '店铺A', 'company_name' => '主体乙', 'effective_at' => '2026-07-01', 'expires_at' => null],
]);
DB::connection('bills_sqlite')->table('bills')->insert([
['store_name' => '店铺A', 'occurred_at' => '2026-06-30 23:59:59', 'income' => 60],
['store_name' => '店铺A', 'occurred_at' => '2026-07-01 00:00:00', 'income' => 70],
]);
$mirror = Mockery::mock(BusinessInformationMirrorRepository::class);
$mirror->shouldReceive('ready')->with('store-information.read')->andReturnTrue();
$mirror->shouldReceive('allStores')->once()->andReturn([[
'erp_store_name' => '店铺A',
'company_name' => '公司表主体',
'store_status' => '3',
]]);
$service = new UpstreamBusinessInformation(
Mockery::mock(UpstreamQueryCacheFactory::class),
$mirror,
Mockery::mock(InstallationDetailMirrorRepository::class),
$this->repository,
);
$paginator = $service->paginate('store-management', 'store-report', 50);
$this->assertSame(2, $paginator->total());
$this->assertSame(['主体甲', '主体乙'], array_map(static fn (object $row): string => $row->entity, $paginator->items()));
$this->assertSame([60.0, 70.0], array_map(static fn (object $row): float => $row->period_sales, $paginator->items()));
$this->assertSame(['关店中', '关店中'], array_map(static fn (object $row): string => $row->status, $paginator->items()));
}
}