From aba8039b8d879d6cb52da7e3eb5a7ff9d5ea7c36 Mon Sep 17 00:00:00 2001 From: shanshi <3316401569@qq.com> Date: Tue, 20 Oct 2026 13:38:30 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AF=BC=E5=87=BA=E8=B4=A2=E5=8A=A1?= =?UTF-8?q?=E5=8F=B0=E8=B4=A6=E8=B4=A6=E6=88=B7=E5=BD=95=E5=85=A5=E6=A8=A1?= =?UTF-8?q?=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/Business/FinanceLedger.php | 7 + docs/财务台账开发说明.md | 1 + .../Services/FinanceLedgerAccountTemplate.php | 200 ++++++++++++++++++ .../pages/financial-ledger/ledger.blade.php | 2 +- routes/admin.php | 4 + .../FinanceLedgerAccountStatusTest.php | 1 + .../FinanceLedgerAccountTemplateTest.php | 83 ++++++++ 7 files changed, 297 insertions(+), 1 deletion(-) create mode 100644 modules/EcommerceFinance/Services/FinanceLedgerAccountTemplate.php create mode 100644 tests/Feature/Business/FinanceLedgerAccountTemplateTest.php diff --git a/app/Http/Controllers/Business/FinanceLedger.php b/app/Http/Controllers/Business/FinanceLedger.php index 536907b1..3f6c97f0 100644 --- a/app/Http/Controllers/Business/FinanceLedger.php +++ b/app/Http/Controllers/Business/FinanceLedger.php @@ -9,12 +9,19 @@ use Illuminate\Http\Request; use Illuminate\Routing\Controller; use Illuminate\Support\Facades\DB; use Illuminate\Validation\Rule; +use Modules\EcommerceFinance\Services\FinanceLedgerAccountTemplate; use Modules\EcommerceFinance\Services\FinanceLedgerCalculator; use Modules\EcommerceFinance\Services\FinanceLedgerPayableMappingBulk; use Modules\EcommerceFinance\Services\FinanceLedgerShopMappingBulk; +use Symfony\Component\HttpFoundation\StreamedResponse; class FinanceLedger extends Controller { + public function downloadAccountTemplate(FinanceLedgerAccountTemplate $template): StreamedResponse + { + return $template->download(); + } + public function updateMapping(Request $request, int $id, FinanceLedgerCalculator $calculator): JsonResponse { abort_unless(FinanceLedgerAccess::canEditMapping(user()), 403, '只有财务台账管理员可以修改账户映射。'); diff --git a/docs/财务台账开发说明.md b/docs/财务台账开发说明.md index 90809490..7c729e01 100644 --- a/docs/财务台账开发说明.md +++ b/docs/财务台账开发说明.md @@ -63,6 +63,7 @@ docker compose -f docker-compose.local.yml exec -u www-data app \ - 路由:`/workspace/financial-ledger/ledger` - 页面:账户总表、店铺台账、每日资金流水、动账明细、账户映射、数据来源 +- 账户总表支持导出空白 Excel 录入模板,包含页面列、状态和账户类型选项及填写说明 - 写权限:账户映射、账户状态和账户类型修改仅系统管理员及配置的财务台账管理员角色;其他账号只读 - 调度:`finance-ledger:sync-rpt` 每天 02:30(Asia/Shanghai) diff --git a/modules/EcommerceFinance/Services/FinanceLedgerAccountTemplate.php b/modules/EcommerceFinance/Services/FinanceLedgerAccountTemplate.php new file mode 100644 index 00000000..7d23054a --- /dev/null +++ b/modules/EcommerceFinance/Services/FinanceLedgerAccountTemplate.php @@ -0,0 +1,200 @@ +makeSpreadsheet(); + + return response()->streamDownload(function () use ($spreadsheet): void { + $writer = new Xlsx($spreadsheet); + $writer->setPreCalculateFormulas(false); + $writer->save('php://output'); + $spreadsheet->disconnectWorksheets(); + }, '财务台账-账户总表录入模板.xlsx', [ + 'Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'Cache-Control' => 'no-store, no-cache, must-revalidate', + ]); + } + + private function makeSpreadsheet(): Spreadsheet + { + $spreadsheet = new Spreadsheet(); + $spreadsheet->getProperties() + ->setTitle('财务台账账户总表录入模板') + ->setSubject('账户总表重新录入'); + + $sheet = $spreadsheet->getActiveSheet(); + $sheet->setTitle('账户总表'); + $lastColumn = Coordinate::stringFromColumnIndex(count(self::HEADERS)); + $lastRow = self::MAX_ROWS + 1; + + foreach (self::HEADERS as $index => $header) { + $column = Coordinate::stringFromColumnIndex($index + 1); + $sheet->setCellValue("{$column}1", $header); + $sheet->getColumnDimension($column)->setWidth($this->width($index)); + } + + $sheet->freezePane('A2'); + $sheet->setAutoFilter("A1:{$lastColumn}1"); + $sheet->getRowDimension(1)->setRowHeight(28); + $sheet->getStyle("A1:{$lastColumn}1")->getAlignment() + ->setHorizontal(Alignment::HORIZONTAL_CENTER) + ->setVertical(Alignment::VERTICAL_CENTER) + ->setWrapText(true); + $sheet->getStyle("A1:{$lastColumn}1")->getFont()->setBold(true)->getColor()->setARGB('FFFFFFFF'); + $sheet->getStyle("A1:{$lastColumn}1")->getFill() + ->setFillType(Fill::FILL_SOLID) + ->getStartColor()->setARGB('FF856632'); + $sheet->getStyle("A1:{$lastColumn}{$lastRow}")->getBorders()->getAllBorders() + ->setBorderStyle(Border::BORDER_THIN)->getColor()->setARGB('FFD8D2C7'); + + $sheet->getStyle("A2:N{$lastRow}")->getNumberFormat()->setFormatCode(NumberFormat::FORMAT_TEXT); + $sheet->getStyle("O2:Q{$lastRow}")->getNumberFormat()->setFormatCode('#,##0.00'); + $sheet->getStyle("R2:R{$lastRow}")->getNumberFormat()->setFormatCode('0.00'); + $sheet->getStyle("S2:Y{$lastRow}")->getNumberFormat()->setFormatCode(NumberFormat::FORMAT_TEXT); + + $options = $spreadsheet->createSheet(); + $options->setTitle('选项'); + $options->setCellValue('A1', '账户状态'); + $options->setCellValue('B1', '账户类型'); + $statuses = array_values(FinanceLedgerAccountStatus::OPTIONS); + $types = $this->accountTypes(); + foreach ($statuses as $index => $status) { + $options->setCellValue('A' . ($index + 2), $status); + } + foreach ($types as $index => $type) { + $options->setCellValue('B' . ($index + 2), $type); + } + $options->setSheetState(Worksheet::SHEETSTATE_HIDDEN); + + $this->applyListValidation($sheet, 'C', '\'选项\'!$A$2:$A$' . (count($statuses) + 1), '请选择账户状态'); + if ($types !== []) { + $this->applyListValidation($sheet, 'H', '\'选项\'!$B$2:$B$' . (count($types) + 1), '请选择账户类型'); + } + + $notes = $spreadsheet->createSheet(); + $notes->setTitle('填写说明'); + $notes->fromArray([ + ['项目', '说明'], + ['用途', '账户总表重新录入模板,不包含当前账户数据。'], + ['必填', '公司名称、账户简称。'], + ['账户状态', implode(' / ', $statuses)], + ['账户类型', $types !== [] ? implode(' / ', $types) : '留空或按实际填写'], + ['金额', '当前余额、卡住金额、每日可出按数字填写;限额列保留原有组合写法。'], + ['提交前', '请核对账户数、余额合计和期初合计,再导入系统。'], + ], null, 'A1'); + $notes->getColumnDimension('A')->setWidth(16); + $notes->getColumnDimension('B')->setWidth(70); + $notes->getStyle('A1:B1')->getFont()->setBold(true)->getColor()->setARGB('FFFFFFFF'); + $notes->getStyle('A1:B1')->getFill() + ->setFillType(Fill::FILL_SOLID) + ->getStartColor()->setARGB('FF856632'); + $notes->getStyle('A1:B7')->getAlignment()->setVertical(Alignment::VERTICAL_TOP)->setWrapText(true); + $notes->getStyle('A1:B7')->getBorders()->getAllBorders() + ->setBorderStyle(Border::BORDER_THIN)->getColor()->setARGB('FFD8D2C7'); + $notes->getRowDimension(1)->setRowHeight(26); + + $spreadsheet->setActiveSheetIndex(0); + + return $spreadsheet; + } + + private function applyListValidation( + Worksheet $sheet, + string $column, + string $formula, + string $prompt, + ): void { + $validation = $sheet->getCell("{$column}2")->getDataValidation(); + $validation->setType(DataValidation::TYPE_LIST); + $validation->setErrorStyle(DataValidation::STYLE_STOP); + $validation->setAllowBlank(true); + $validation->setShowInputMessage(true); + $validation->setPromptTitle('选择' . $prompt); + $validation->setPrompt($prompt); + $validation->setShowErrorMessage(true); + $validation->setErrorTitle('选项无效'); + $validation->setError($prompt); + $validation->setFormula1($formula); + $validation->setShowDropDown(true); + + for ($row = 2; $row <= self::MAX_ROWS + 1; $row++) { + $sheet->getCell("{$column}{$row}")->setDataValidation(clone $validation); + } + } + + private function accountTypes(): array + { + $connection = DB::connection((string) config('upstream_mirrors.connection', 'business_mirror')); + $base = $connection->table('fin_accounts') + ->whereNotNull('account_type')->where('account_type', '<>', '') + ->distinct()->pluck('account_type')->all(); + $overrides = $connection->table('fin_account_type_overrides') + ->whereNotNull('account_type')->where('account_type', '<>', '') + ->distinct()->pluck('account_type')->all(); + $types = array_values(array_unique(array_merge($base, $overrides))); + sort($types, SORT_NATURAL | SORT_FLAG_CASE); + + return $types; + } + + private function width(int $index): int + { + return match ($index) { + 0, 5 => 30, + 1, 4, 6, 23 => 22, + 2, 3, 7, 8, 17 => 13, + 14, 15, 16 => 16, + default => 18, + }; + } +} diff --git a/resources/views/business/pages/financial-ledger/ledger.blade.php b/resources/views/business/pages/financial-ledger/ledger.blade.php index c53ea74e..297ca5c0 100644 --- a/resources/views/business/pages/financial-ledger/ledger.blade.php +++ b/resources/views/business/pages/financial-ledger/ledger.blade.php @@ -145,7 +145,7 @@ @if ($activeAccountFilterCount > 0) 筛选中{{ $activeAccountFilterCount }} @endif -
+