完成店铺台账每日余额来源、日期筛选、预留标准和连续3天预警。 Co-authored-by: 92451 <924518135@qq.com> Co-committed-by: 92451 <924518135@qq.com>
309 lines
13 KiB
PHP
309 lines
13 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature\Business;
|
|
|
|
use App\Models\Auth\Permission;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Modules\EcommerceFinance\Repositories\BusinessInformationMirrorRepository;
|
|
use Modules\EcommerceFinance\Repositories\JushuitanShopBalanceRepository;
|
|
use Modules\EcommerceFinance\Repositories\StoreAccountMirrorRepository;
|
|
use Modules\EcommerceFinance\Services\StoreReportPeriodBuilder;
|
|
use Modules\EcommerceFinance\Services\StoreReceivingBillBalanceSnapshotSynchronizer;
|
|
use Tests\Feature\FeatureTestCase;
|
|
|
|
class FinanceLedgerShopDirectoryTest extends FeatureTestCase
|
|
{
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
|
|
$this->user->attachPermission(
|
|
Permission::where('name', 'read-admin-panel')->firstOrFail(),
|
|
);
|
|
}
|
|
|
|
public function test_shop_ledger_uses_store_report_current_subject_and_platform(): void
|
|
{
|
|
DB::connection('business_mirror')->table('fin_accounts')->insert([
|
|
[
|
|
'company_name' => '测试公司',
|
|
'account_short' => '公户账户',
|
|
'account_no' => 'ABC-1234',
|
|
'account_type' => '公户',
|
|
'bank_name' => null,
|
|
'created_at' => now(),
|
|
'updated_at' => now(),
|
|
],
|
|
[
|
|
'company_name' => '测试公司',
|
|
'account_short' => '支付宝账户',
|
|
'account_no' => 'PAY-8888',
|
|
'account_type' => '支付宝',
|
|
'bank_name' => null,
|
|
'created_at' => now(),
|
|
'updated_at' => now(),
|
|
],
|
|
[
|
|
'company_name' => '测试公司',
|
|
'account_short' => '网商银行账户',
|
|
'account_no' => 'NET-7777',
|
|
'bank_name' => '网商银行',
|
|
'account_type' => '公户',
|
|
'created_at' => now(),
|
|
'updated_at' => now(),
|
|
],
|
|
[
|
|
'company_name' => '测试公司',
|
|
'account_short' => '专属账户',
|
|
'account_no' => 'MASTER-OTHER',
|
|
'account_type' => '个人卡',
|
|
'bank_name' => null,
|
|
'created_at' => now(),
|
|
'updated_at' => now(),
|
|
],
|
|
]);
|
|
DB::connection('business_mirror')->table('fin_shops')->insert([
|
|
[
|
|
'shop_name' => '报表测试店',
|
|
'platform' => '天猫',
|
|
'account_short' => null,
|
|
'shop_balance' => 123.45,
|
|
'shop_balance_date' => '2026-10-25',
|
|
'created_at' => now(),
|
|
'updated_at' => now(),
|
|
],
|
|
[
|
|
'shop_name' => '同账户甲',
|
|
'platform' => '天猫',
|
|
'account_short' => '专属账户',
|
|
'shop_balance' => 0,
|
|
'shop_balance_date' => null,
|
|
'created_at' => now(),
|
|
'updated_at' => now(),
|
|
],
|
|
]);
|
|
|
|
$openStore = [
|
|
'id' => 901,
|
|
'platform' => '天猫',
|
|
'erp_store_name' => '天猫-报表测试店',
|
|
'company_name' => '历史主体',
|
|
'store_status' => '0',
|
|
'store_manager' => '原店铺负责人',
|
|
'department_manager' => '原部门负责人',
|
|
'withdrawal_account' => 'ABC-1234',
|
|
];
|
|
$closedStore = [
|
|
'id' => 902,
|
|
'platform' => '京东',
|
|
'erp_store_name' => '京东-已关店',
|
|
'company_name' => '历史主体',
|
|
'store_status' => '6',
|
|
'withdrawal_account' => null,
|
|
];
|
|
$openStoreWithoutSubject = [
|
|
'id' => 903,
|
|
'platform' => '淘宝',
|
|
'erp_store_name' => '淘宝-无主体店',
|
|
'company_name' => null,
|
|
'store_status' => '0',
|
|
'store_manager' => '无主体店铺负责人',
|
|
'department_manager' => '无主体部门负责人',
|
|
'withdrawal_account' => 'PAY-8888',
|
|
];
|
|
$openStoreWithExcludedBank = [
|
|
'id' => 904,
|
|
'platform' => '拼多多',
|
|
'erp_store_name' => '拼多多-网商店',
|
|
'company_name' => null,
|
|
'store_status' => '0',
|
|
'store_manager' => '网商店铺负责人',
|
|
'department_manager' => '网商部门负责人',
|
|
'withdrawal_account' => 'NET-7777',
|
|
];
|
|
$openStoreWithKnownSharedPayout = [
|
|
'id' => 905,
|
|
'platform' => '天猫',
|
|
'erp_store_name' => '天猫-同账户甲',
|
|
'company_name' => null,
|
|
'store_status' => '0',
|
|
'store_manager' => '同账户甲负责人',
|
|
'department_manager' => '同账户甲部门负责人',
|
|
'withdrawal_account' => 'LEGACY-5555',
|
|
];
|
|
$openStoreWithUnknownSharedPayout = [
|
|
'id' => 906,
|
|
'platform' => '淘宝',
|
|
'erp_store_name' => '淘宝-同账户乙',
|
|
'company_name' => null,
|
|
'store_status' => '0',
|
|
'store_manager' => '同账户乙负责人',
|
|
'department_manager' => '同账户乙部门负责人',
|
|
'withdrawal_account' => 'LEGACY5555',
|
|
];
|
|
|
|
$this->mock(BusinessInformationMirrorRepository::class)
|
|
->shouldReceive('allStores')
|
|
->once()
|
|
->andReturn([
|
|
$openStore,
|
|
$closedStore,
|
|
$openStoreWithoutSubject,
|
|
$openStoreWithExcludedBank,
|
|
$openStoreWithKnownSharedPayout,
|
|
$openStoreWithUnknownSharedPayout,
|
|
]);
|
|
$this->mock(StoreReportPeriodBuilder::class)
|
|
->shouldReceive('build')
|
|
->once()
|
|
->with([
|
|
$openStore,
|
|
$openStoreWithoutSubject,
|
|
$openStoreWithExcludedBank,
|
|
$openStoreWithKnownSharedPayout,
|
|
$openStoreWithUnknownSharedPayout,
|
|
])
|
|
->andReturn([
|
|
'天猫-报表测试店' => [
|
|
[
|
|
'company_name' => '历史主体',
|
|
'effective_at' => '2026-07-01',
|
|
'expires_at' => '2026-10-01',
|
|
'source' => 'initial',
|
|
],
|
|
[
|
|
'company_name' => '当前存续主体',
|
|
'effective_at' => '2026-10-02',
|
|
'expires_at' => null,
|
|
'source' => 'crawl',
|
|
],
|
|
],
|
|
]);
|
|
$this->mock(StoreAccountMirrorRepository::class)
|
|
->shouldReceive('ownershipByStoreName')
|
|
->once()
|
|
->andReturn([
|
|
'天猫-报表测试店' => [
|
|
'operation_owner' => '运营负责人',
|
|
'operation_department_head' => '运营部门负责人',
|
|
'operation_director' => '运营总监',
|
|
'profit_center' => '运营中心',
|
|
],
|
|
]);
|
|
|
|
$html = $this->loginAs()->get($this->pageUrl())->assertOk()->getContent();
|
|
$panelStart = strpos($html, 'id="ledger-panel-shops"');
|
|
$panelEnd = strpos($html, 'id="ledger-panel-flows"', $panelStart);
|
|
$this->assertNotFalse($panelStart);
|
|
$this->assertNotFalse($panelEnd);
|
|
|
|
$shopHtml = substr($html, $panelStart, $panelEnd - $panelStart);
|
|
$this->assertStringContainsString('天猫', $shopHtml);
|
|
$this->assertStringContainsString('天猫-报表测试店', $shopHtml);
|
|
$this->assertStringContainsString('当前存续主体', $shopHtml);
|
|
$this->assertStringContainsString('运营负责人', $shopHtml);
|
|
$this->assertStringContainsString('运营部门负责人', $shopHtml);
|
|
$this->assertStringContainsString('无主体店铺负责人', $shopHtml);
|
|
$this->assertStringContainsString('无主体部门负责人', $shopHtml);
|
|
$this->assertStringContainsString('淘宝-无主体店', $shopHtml);
|
|
$this->assertStringContainsString('拼多多-网商店', $shopHtml);
|
|
$this->assertStringContainsString('天猫-同账户甲', $shopHtml);
|
|
$this->assertStringContainsString('淘宝-同账户乙', $shopHtml);
|
|
$this->assertStringContainsString('在营', $shopHtml);
|
|
$this->assertStringContainsString('公户账户', $shopHtml);
|
|
$this->assertStringContainsString('123.45', $shopHtml);
|
|
$this->assertStringContainsString('ABC-1234', $shopHtml);
|
|
$this->assertGreaterThanOrEqual(2, substr_count($shopHtml, '>专属账户</td>'));
|
|
$this->assertGreaterThanOrEqual(2, substr_count($shopHtml, '>MASTER-OTHER</td>'));
|
|
$this->assertStringNotContainsString('京东-已关店', $shopHtml);
|
|
$this->assertStringNotContainsString('历史主体', $shopHtml);
|
|
$this->assertStringNotContainsString('原店铺负责人', $shopHtml);
|
|
$this->assertStringNotContainsString('原部门负责人', $shopHtml);
|
|
$this->assertStringNotContainsString('测试公司', $shopHtml);
|
|
$this->assertStringNotContainsString('支付宝账户', $shopHtml);
|
|
$this->assertStringNotContainsString('网商银行账户', $shopHtml);
|
|
}
|
|
|
|
public function test_shop_ledger_filters_daily_balance_and_shows_source_reserve_and_alert(): void
|
|
{
|
|
$store = [
|
|
'id' => 921,
|
|
'platform' => '京东',
|
|
'erp_store_name' => '京东-余额预警测试店',
|
|
'company_name' => '历史主体',
|
|
'store_status' => '0',
|
|
'store_manager' => '店铺负责人',
|
|
'department_manager' => '部门负责人',
|
|
'withdrawal_account' => null,
|
|
];
|
|
$this->mock(BusinessInformationMirrorRepository::class)
|
|
->shouldReceive('allStores')
|
|
->once()
|
|
->andReturn([$store]);
|
|
$this->mock(StoreReportPeriodBuilder::class)
|
|
->shouldReceive('build')
|
|
->once()
|
|
->with([$store])
|
|
->andReturn([
|
|
'京东-余额预警测试店' => [[
|
|
'company_name' => '当前主体有限公司',
|
|
'effective_at' => '2026-10-01',
|
|
'expires_at' => null,
|
|
'source' => 'crawl',
|
|
]],
|
|
]);
|
|
$this->mock(StoreAccountMirrorRepository::class)
|
|
->shouldReceive('ownershipByStoreName')
|
|
->once()
|
|
->andReturn([]);
|
|
|
|
foreach (['2026-10-22', '2026-10-23', '2026-10-24'] as $date) {
|
|
$this->insertStoreLedgerBalance($date, '京东-余额预警测试店', 6000);
|
|
}
|
|
|
|
$html = $this->loginAs()->get($this->pageUrl() . '?' . http_build_query([
|
|
'tab' => 'shops',
|
|
'shops_balance_date' => '2026-10-24',
|
|
]))->assertOk()->getContent();
|
|
$panelStart = strpos($html, 'id="ledger-panel-shops"');
|
|
$panelEnd = strpos($html, 'id="ledger-panel-flows"', $panelStart);
|
|
$shopHtml = substr($html, $panelStart, $panelEnd - $panelStart);
|
|
|
|
$this->assertStringContainsString('value="2026-10-24" selected', $shopHtml);
|
|
$this->assertStringContainsString('店铺收款账户账单 · 京东钱包', $shopHtml);
|
|
$this->assertStringContainsString('>6,000.00</td>', $shopHtml);
|
|
$this->assertStringContainsString('>5,000.00</td>', $shopHtml);
|
|
$this->assertStringContainsString('余额连续 3 天保持 6,000.00 元,高于预留标准 5,000.00 元。', $shopHtml);
|
|
$this->assertStringContainsString('预警', $shopHtml);
|
|
}
|
|
|
|
private function pageUrl(): string
|
|
{
|
|
return route('business.pages.show', [
|
|
'company_id' => $this->company->id,
|
|
'section' => 'financial-operations',
|
|
'page' => 'ledger',
|
|
]);
|
|
}
|
|
|
|
private function insertStoreLedgerBalance(string $date, string $storeName, float $balance): void
|
|
{
|
|
DB::connection('business_mirror')->table(JushuitanShopBalanceRepository::TABLE)->insert([
|
|
'balance_date' => $date,
|
|
'source_scope' => StoreReceivingBillBalanceSnapshotSynchronizer::SOURCE_SCOPE,
|
|
'data_source' => StoreReceivingBillBalanceSnapshotSynchronizer::DATA_SOURCE_RECEIVING_BILL,
|
|
'source_platform' => '京东',
|
|
'store_name' => $storeName,
|
|
'normalized_store_name' => JushuitanShopBalanceRepository::normalizeStoreName($storeName),
|
|
'platform_account_id' => 'TEST-JD',
|
|
'balance' => $balance,
|
|
'source_row_key' => hash('sha256', $date . '|' . $storeName),
|
|
'source_file_name' => '财务系统/店铺收款账户账单/京东钱包',
|
|
'source_sha256' => hash('sha256', $date . '|' . $storeName . '|' . $balance),
|
|
'imported_at' => now(),
|
|
'created_at' => now(),
|
|
'updated_at' => now(),
|
|
]);
|
|
}
|
|
}
|