- 新增配置表 business_supplier_bookkeeping_configs(+ 操作日志表),迁移时导入财务提供的 209 家存量配置 - 记账方式:正常记账 / 仅代发仓发错货记账 / 不记账,支持生效期;配置页支持新增、编辑、删除恢复、Excel 导入导出 - 扣款判定:厂家记账按供应商配置,仅正常记账扣款;仓库记账仅订单发货仓库为代发仓且二级原因含“仓库发错货”时作为厂家扣款项,其余不扣 - 未配置/生效期外显示待配置,多供应商判定不一致显示待确认,均不计入扣款金额 - 明细页新增厂家扣款判定筛选、扣款工单数与扣款金额指标、导出列 涉及:数据库迁移(新建配置表并写入存量配置)、金额汇总展示;不涉及上游写入和权限模型。 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JouFR6TJ7vGscRJanYScEo
121 lines
6.5 KiB
PHP
121 lines
6.5 KiB
PHP
<?php
|
||
|
||
namespace Tests\Feature\Business;
|
||
|
||
use App\Models\Auth\Permission;
|
||
use Illuminate\Http\UploadedFile;
|
||
use Illuminate\Support\Facades\DB;
|
||
use Modules\EcommerceFinance\Repositories\SupplierBookkeepingWorkOrderRepository;
|
||
use Modules\EcommerceFinance\Services\SupplierBookkeepingConfigStore as Store;
|
||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
|
||
use Tests\Feature\FeatureTestCase;
|
||
|
||
class SupplierBookkeepingConfigTest extends FeatureTestCase
|
||
{
|
||
protected function setUp(): void
|
||
{
|
||
parent::setUp();
|
||
$this->user->attachPermission(Permission::where('name', 'read-admin-panel')->firstOrFail());
|
||
$repository = \Mockery::mock(SupplierBookkeepingWorkOrderRepository::class);
|
||
$repository->shouldReceive('syncStatus')->andReturn(['code' => 'ready', 'last_succeeded_at' => null]);
|
||
$repository->shouldReceive('rows')->andReturn([]);
|
||
$this->app->instance(SupplierBookkeepingWorkOrderRepository::class, $repository);
|
||
}
|
||
|
||
private function url(string $name, array $params = []): string
|
||
{
|
||
return route($name, ['company_id' => $this->company->id] + $params);
|
||
}
|
||
|
||
private function configPage(array $query = []): string
|
||
{
|
||
return route('business.pages.show', ['company_id' => $this->company->id, 'section' => 'supplier-payables', 'page' => 'manufacturer-bookkeeping', 'bk_view' => 'config'] + $query);
|
||
}
|
||
|
||
private function xlsx(array $rows): UploadedFile
|
||
{
|
||
$book = new Spreadsheet();
|
||
$book->getActiveSheet()->fromArray($rows);
|
||
$path = tempnam(sys_get_temp_dir(), 'sbc') . '.xlsx';
|
||
(new Xlsx($book))->save($path);
|
||
|
||
return new UploadedFile($path, '厂家记账配置.xlsx', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', null, true);
|
||
}
|
||
|
||
public function test_migration_imports_existing_finance_configs(): void
|
||
{
|
||
$this->assertSame(209, DB::table(Store::TABLE)->count());
|
||
$this->assertSame(4, DB::table(Store::TABLE)->where('mode', 'normal')->count());
|
||
$this->assertSame(48, DB::table(Store::TABLE)->where('mode', 'dropship_wrong')->count());
|
||
$row = DB::table(Store::TABLE)->where('supplier_name', '景瓴十方')->first();
|
||
$this->assertSame(['none', '2025-03-01', '2026-02-28'], [$row->mode, $row->effective_from, $row->effective_to]);
|
||
|
||
$this->loginAs()->get($this->configPage(['supplier_name' => '曹县洪耀']))
|
||
->assertOk()
|
||
->assertSee('厂家记账配置')
|
||
->assertSee('曹县洪耀')
|
||
->assertSee('代发仓发货错需记账,其他原因均不记账')
|
||
->assertSee('导入存量厂家记账配置:新增209条');
|
||
}
|
||
|
||
public function test_create_update_delete_restore_with_audit_log(): void
|
||
{
|
||
$this->loginAs()->post($this->url('business.supplier-bookkeeping-configs.store'), ['supplier_name' => ' 新厂家 ', 'mode' => 'normal'])
|
||
->assertRedirect();
|
||
$id = DB::table(Store::TABLE)->where('supplier_name', '新厂家')->value('id');
|
||
$this->assertNotNull($id);
|
||
|
||
$this->withExceptionHandling()->loginAs()->post($this->url('business.supplier-bookkeeping-configs.store'), ['supplier_name' => '新厂家', 'mode' => 'none'])
|
||
->assertSessionHasErrors('supplier_name');
|
||
|
||
$this->loginAs()->patch($this->url('business.supplier-bookkeeping-configs.update', ['configId' => $id]), [
|
||
'supplier_name' => '新厂家', 'mode' => 'none', 'effective_from' => '2026-01-01', 'effective_to' => '2026-12-31',
|
||
])->assertRedirect();
|
||
$this->assertSame('none', DB::table(Store::TABLE)->where('id', $id)->value('mode'));
|
||
|
||
$this->withExceptionHandling()->loginAs()->patch($this->url('business.supplier-bookkeeping-configs.update', ['configId' => $id]), [
|
||
'supplier_name' => '新厂家', 'mode' => 'none', 'effective_from' => '2026-12-31', 'effective_to' => '2026-01-01',
|
||
])->assertSessionHasErrors('effective_to');
|
||
|
||
$this->loginAs()->delete($this->url('business.supplier-bookkeeping-configs.destroy', ['configId' => $id]))->assertRedirect();
|
||
$this->assertNotNull(DB::table(Store::TABLE)->where('id', $id)->value('deleted_at'));
|
||
$this->loginAs()->post($this->url('business.supplier-bookkeeping-configs.restore', ['configId' => $id]))->assertRedirect();
|
||
$this->assertNull(DB::table(Store::TABLE)->where('id', $id)->value('deleted_at'));
|
||
|
||
$this->assertSame(['import', 'create', 'update', 'delete', 'restore'], DB::table(Store::EVENTS)->orderBy('id')->pluck('action')->all());
|
||
}
|
||
|
||
public function test_import_finance_sheet_adds_updates_and_skips(): void
|
||
{
|
||
$file = $this->xlsx([
|
||
['供应商', '厂家是否记账'],
|
||
['鑫呈', '正常记账'],
|
||
['尊享', '代发仓发货错需记账,其他原因均不记账'],
|
||
['供应商', '厂家是否记账'],
|
||
['全新厂', '不记账(2025.3月-2027.12.30)'],
|
||
]);
|
||
$this->loginAs()->post($this->url('business.supplier-bookkeeping-configs.import'), ['file' => $file])
|
||
->assertRedirect()
|
||
->assertSessionHas('bookkeeping_config_message', '导入完成:新增1条,更新1条,未变化1条。');
|
||
$this->assertSame('dropship_wrong', DB::table(Store::TABLE)->where('supplier_name', '尊享')->value('mode'));
|
||
$this->assertSame('2027-12-30', DB::table(Store::TABLE)->where('supplier_name', '全新厂')->value('effective_to'));
|
||
|
||
$before = DB::table(Store::TABLE)->count();
|
||
$bad = $this->xlsx([['供应商', '厂家是否记账'], ['再一个厂', '正常记账'], ['坏行厂', '看情况']]);
|
||
$this->withExceptionHandling()->loginAs()->post($this->url('business.supplier-bookkeeping-configs.import'), ['file' => $bad])
|
||
->assertSessionHasErrors('file');
|
||
$this->assertSame($before, DB::table(Store::TABLE)->count());
|
||
}
|
||
|
||
public function test_export_round_trips_through_import(): void
|
||
{
|
||
$response = $this->loginAs()->get($this->url('business.supplier-bookkeeping-configs.export', ['supplier_name' => '景瓴十方']));
|
||
$response->assertOk();
|
||
$path = tempnam(sys_get_temp_dir(), 'sbe') . '.xlsx';
|
||
file_put_contents($path, $response->streamedContent());
|
||
$rows = app(\Modules\EcommerceFinance\Services\SupplierBookkeepingConfigImporter::class)->read($path);
|
||
$this->assertSame([['supplier_name' => '景瓴十方', 'mode' => 'none', 'effective_from' => '2025-03-01', 'effective_to' => '2026-02-28', 'source_text' => '不记账(2025.3月-2026.2月)']], $rows);
|
||
}
|
||
}
|