From 93cbc83bb0d9560d224ecf2d64ccc6219d2c91da Mon Sep 17 00:00:00 2001 From: chenxi Date: Mon, 26 Oct 2026 15:05:49 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E4=BA=BA=E5=B7=A5?= =?UTF-8?q?=E8=B4=B9=E7=94=A8=E5=B7=A5=E8=B5=84=E6=B5=8B=E7=AE=97=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 按 8 月工资表落地 61 列表头与 34 个实时计算公式;不导入工资明细。 --- app/Http/Controllers/Business/Pages.php | 9 + config/labor_expense_table.php | 76 ++ .../labor-expenses.blade.php | 855 ++++++++++++++++++ .../Business/LaborExpensePageAccessTest.php | 32 + 4 files changed, 972 insertions(+) create mode 100644 config/labor_expense_table.php create mode 100644 resources/views/business/pages/financial-operations/labor-expenses.blade.php diff --git a/app/Http/Controllers/Business/Pages.php b/app/Http/Controllers/Business/Pages.php index 8fab44ed..ea4535b1 100644 --- a/app/Http/Controllers/Business/Pages.php +++ b/app/Http/Controllers/Business/Pages.php @@ -4594,6 +4594,7 @@ class Pages extends Controller 'online-daifa-price' => $this->contentForOnlineDaifaPrice(), 'product-costs' => $this->contentForProductCosts(), 'financial-analysis' => $this->contentForFinancialAnalysis(), + 'labor-expenses' => $this->contentForLaborExpenses(), 'courier-surcharge' => $this->contentForCourierSurcharge($tab), 'courier-surcharge-settlement' => $this->contentForCourierSurchargeSettlementPage(), 'freight-difference', 'other-surcharge-summary', 'return-fee' => $this->contentForCourierPayableSummary($page), @@ -4622,6 +4623,14 @@ class Pages extends Controller }; } + protected function contentForLaborExpenses(): array + { + return [ + 'view' => 'business.pages.financial-operations.labor-expenses', + 'laborExpenseTable' => config('labor_expense_table'), + ]; + } + /** * 税金预测表逻辑配置:规则清单、数据源状态和运行参数。 */ diff --git a/config/labor_expense_table.php b/config/labor_expense_table.php new file mode 100644 index 00000000..46abe1e8 --- /dev/null +++ b/config/labor_expense_table.php @@ -0,0 +1,76 @@ + '8月工资.xlsx', + 'groups' => [ + ['key' => 'base', 'label' => '基础信息', 'span' => 5], + ['key' => 'original', 'label' => '原数据', 'span' => 9], + ['key' => 'fund', 'label' => '提高公积金基数方案', 'span' => 12], + ['key' => 'salary_parttime', 'label' => '工资+兼职方案', 'span' => 22], + ['key' => 'settlement', 'label' => '汇算清缴', 'span' => 12], + ['key' => 'recommendation', 'label' => '', 'span' => 1], + ], + 'fields' => [ + ['key' => 'A', 'label' => '序号', 'group' => 'base', 'kind' => 'sequence'], + ['key' => 'B', 'label' => '花名', 'group' => 'base', 'kind' => 'text'], + ['key' => 'C', 'label' => '姓名', 'group' => 'base', 'kind' => 'text'], + ['key' => 'D', 'label' => '中心', 'group' => 'base', 'kind' => 'text'], + ['key' => 'E', 'label' => '职位', 'group' => 'base', 'kind' => 'text'], + ['key' => 'F', 'label' => '8月应发工资', 'group' => 'original', 'kind' => 'number'], + ['key' => 'G', 'label' => '原社保个人扣款', 'group' => 'original', 'kind' => 'number'], + ['key' => 'H', 'label' => '原公积金个人扣款', 'group' => 'original', 'kind' => 'number'], + ['key' => 'I', 'label' => '扣除额', 'group' => 'original', 'kind' => 'number'], + ['key' => 'J', 'label' => '原需报个税基数', 'group' => 'original', 'kind' => 'formula_number', 'formula' => '=F#-G#-H#-I#'], + ['key' => 'K', 'label' => '原个税比列', 'group' => 'original', 'kind' => 'percent'], + ['key' => 'L', 'label' => '原速算扣除数', 'group' => 'original', 'kind' => 'number'], + ['key' => 'M', 'label' => '原需交个税金额', 'group' => 'original', 'kind' => 'number'], + ['key' => 'N', 'label' => '原需年交个税金额', 'group' => 'original', 'kind' => 'formula_number', 'formula' => '=M#*12'], + ['key' => 'O', 'label' => '新公积金缴存基数', 'group' => 'fund', 'kind' => 'number'], + ['key' => 'P', 'label' => '新单位公积金月缴存12%', 'group' => 'fund', 'kind' => 'number'], + ['key' => 'Q', 'label' => '新个人公积金月缴存12%', 'group' => 'fund', 'kind' => 'number'], + ['key' => 'R', 'label' => '超额代扣缴', 'group' => 'fund', 'kind' => 'number'], + ['key' => 'S', 'label' => '新月度应纳税所得额', 'group' => 'fund', 'kind' => 'formula_number', 'formula' => '=F#-G#-I#-P#-R#'], + ['key' => 'T', 'label' => '新月度个税比列', 'group' => 'fund', 'kind' => 'percent'], + ['key' => 'U', 'label' => '新月度新速算扣除数', 'group' => 'fund', 'kind' => 'number'], + ['key' => 'V', 'label' => '新月度个税金额', 'group' => 'fund', 'kind' => 'number'], + ['key' => 'W', 'label' => '全年应纳税所得额', 'group' => 'fund', 'kind' => 'formula_number', 'formula' => '=S#*12'], + ['key' => 'X', 'label' => '年度税率', 'group' => 'fund', 'kind' => 'formula_percent', 'formula' => '=IF(S#=0,"",IF(W#<=0,0,IF(W#<=36000,0.03,IF(W#<=144000,0.1,IF(W#<=300000,0.2,IF(W#<=420000,0.25,IF(W#<=660000,0.3,IF(W#<=960000,0.35,0.45))))))))'], + ['key' => 'Y', 'label' => '年度速算扣除数', 'group' => 'fund', 'kind' => 'formula_number', 'formula' => '=IF(W#=0,"",IF(W#<=0,0,IF(W#<=36000,0,IF(W#<=144000,2520,IF(W#<=300000,16920,IF(W#<=420000,31920,IF(W#<=660000,52920,IF(W#<=960000,85920,181920))))))))'], + ['key' => 'Z', 'label' => '全年个税', 'group' => 'fund', 'kind' => 'formula_number', 'formula' => '=W#*X#-Y#'], + ['key' => 'AA', 'label' => '单月应纳税所得额区间', 'group' => 'salary_parttime', 'kind' => 'number'], + ['key' => 'AB', 'label' => '福利费', 'group' => 'salary_parttime', 'kind' => 'number'], + ['key' => 'AC', 'label' => '现金', 'group' => 'salary_parttime', 'kind' => 'number'], + ['key' => 'AD', 'label' => '兼职工资合计', 'group' => 'salary_parttime', 'kind' => 'formula_number', 'formula' => '=AB#+AC#'], + ['key' => 'AE', 'label' => '新8月工资应发', 'group' => 'salary_parttime', 'kind' => 'formula_number', 'formula' => '=F#-AD#'], + ['key' => 'AF', 'label' => '新月度应纳税所得额', 'group' => 'salary_parttime', 'kind' => 'formula_number', 'formula' => '=AE#-G#-I#-P#-R#'], + ['key' => 'AG', 'label' => '新月度个税比列', 'group' => 'salary_parttime', 'kind' => 'percent'], + ['key' => 'AH', 'label' => '新月度新速算扣除数', 'group' => 'salary_parttime', 'kind' => 'number'], + ['key' => 'AI', 'label' => '新月度个税金额', 'group' => 'salary_parttime', 'kind' => 'number'], + ['key' => 'AJ', 'label' => '全年应纳税所得额', 'group' => 'salary_parttime', 'kind' => 'formula_number', 'formula' => '=AF#*12'], + ['key' => 'AK', 'label' => '年度税率', 'group' => 'salary_parttime', 'kind' => 'formula_percent', 'formula' => '=IF(AJ#=0,"",IF(AJ#<=0,0,IF(AJ#<=36000,0.03,IF(AJ#<=144000,0.1,IF(AJ#<=300000,0.2,IF(AJ#<=420000,0.25,IF(AJ#<=660000,0.3,IF(AJ#<=960000,0.35,0.45))))))))'], + ['key' => 'AL', 'label' => '年度速算扣除数', 'group' => 'salary_parttime', 'kind' => 'formula_number', 'formula' => '=IF(AJ#=0,"",IF(AJ#<=0,0,IF(AJ#<=36000,0,IF(AJ#<=144000,2520,IF(AJ#<=300000,16920,IF(AJ#<=420000,31920,IF(AJ#<=660000,52920,IF(AJ#<=960000,85920,181920))))))))'], + ['key' => 'AM', 'label' => '全年工资个税', 'group' => 'salary_parttime', 'kind' => 'formula_number', 'formula' => '=AJ#*AK#-AL#'], + ['key' => 'AN', 'label' => '月度兼职工资', 'group' => 'salary_parttime', 'kind' => 'number'], + ['key' => 'AO', 'label' => '月扣除额', 'group' => 'salary_parttime', 'kind' => 'formula_number', 'formula' => '=IF(AN#<=0,0,IF(AN#<=4000,800,AN#*0.2))'], + ['key' => 'AP', 'label' => '月应纳税所得额', 'group' => 'salary_parttime', 'kind' => 'formula_number', 'formula' => '=AN#-AO#'], + ['key' => 'AQ', 'label' => '月个税预扣率', 'group' => 'salary_parttime', 'kind' => 'formula_percent', 'formula' => '=IF(AN#<=0,0,IF(AP#<=20000,0.2,IF(AP#<=50000,0.3,0.4)))'], + ['key' => 'AR', 'label' => '月速算扣除数', 'group' => 'salary_parttime', 'kind' => 'formula_number', 'formula' => '=IF(AN#<=0,0,IF(AP#<=20000,0,IF(AP#<=50000,2000,7000)))'], + ['key' => 'AS', 'label' => '月兼职需交个税', 'group' => 'salary_parttime', 'kind' => 'formula_number', 'formula' => '=AP#*AQ#-AR#'], + ['key' => 'AT', 'label' => '月度工资+兼职个税合计', 'group' => 'salary_parttime', 'kind' => 'formula_number', 'formula' => '=AI#+AS#'], + ['key' => 'AU', 'label' => '年度兼职工资个税', 'group' => 'salary_parttime', 'kind' => 'formula_number', 'formula' => '=AS#*12'], + ['key' => 'AV', 'label' => '年度工资+兼职个税合计', 'group' => 'salary_parttime', 'kind' => 'formula_number', 'formula' => '=AM#+AU#'], + ['key' => 'AW', 'label' => '汇算 工资薪金收入额', 'group' => 'settlement', 'kind' => 'formula_number', 'formula' => '=AE#*12'], + ['key' => 'AX', 'label' => '汇算 劳务报酬收入额(×80%)', 'group' => 'settlement', 'kind' => 'formula_number', 'formula' => '=AN#*12*0.8'], + ['key' => 'AY', 'label' => '汇算 收入额合计', 'group' => 'settlement', 'kind' => 'formula_number', 'formula' => '=AW#+AX#'], + ['key' => 'AZ', 'label' => '汇算 减基本减除费用', 'group' => 'settlement', 'kind' => 'formula_number', 'formula' => '=60000'], + ['key' => 'BA', 'label' => '汇算 减社保公积金', 'group' => 'settlement', 'kind' => 'formula_number', 'formula' => '=(G#+P#+R#)*12'], + ['key' => 'BB', 'label' => '汇算 全年应纳税所得额', 'group' => 'settlement', 'kind' => 'formula_number', 'formula' => '=AY#-AZ#-BA#'], + ['key' => 'BC', 'label' => '汇算 适用税率', 'group' => 'settlement', 'kind' => 'formula_percent', 'formula' => '=IF(BB#<=0,"",IF(BB#<=0,0,IF(BB#<=36000,0.03,IF(BB#<=144000,0.1,IF(BB#<=300000,0.2,IF(BB#<=420000,0.25,IF(BB#<=660000,0.3,IF(BB#<=960000,0.35,0.45))))))))'], + ['key' => 'BD', 'label' => '汇算 速算扣除数', 'group' => 'settlement', 'kind' => 'formula_integer', 'formula' => '=IF(BB#<=0,"",IF(BB#<=0,0,IF(BB#<=36000,0,IF(BB#<=144000,2520,IF(BB#<=300000,16920,IF(BB#<=420000,31920,IF(BB#<=660000,52920,IF(BB#<=960000,85920,181920))))))))'], + ['key' => 'BE', 'label' => '汇算 全年应纳税额', 'group' => 'settlement', 'kind' => 'formula_number', 'formula' => '=IF(AN#<=0,"",IF(BB#<=0,0,IF(BB#<=36000,BB#*0.03,IF(BB#<=144000,BB#*0.1-2520,IF(BB#<=300000,BB#*0.2-16920,IF(BB#<=420000,BB#*0.25-31920,IF(BB#<=660000,BB#*0.3-52920,IF(BB#<=960000,BB#*0.35-85920,BB#*0.45-181920))))))))'], + ['key' => 'BF', 'label' => '汇算 月应纳税额', 'group' => 'settlement', 'kind' => 'formula_number', 'formula' => '=BE#/12'], + ['key' => 'BG', 'label' => '汇算 应退金额(正=退/负=补)', 'group' => 'settlement', 'kind' => 'formula_number', 'formula' => '=IF(AN#<=0,"",AV#-BE#)'], + ['key' => 'BH', 'label' => '全年真实节税', 'group' => 'settlement', 'kind' => 'formula_number', 'formula' => '=IF(AN#<=0,"",Z#-BE#)'], + ['key' => 'BI', 'label' => '推荐方案', 'group' => 'recommendation', 'kind' => 'text'], + ], +]; diff --git a/resources/views/business/pages/financial-operations/labor-expenses.blade.php b/resources/views/business/pages/financial-operations/labor-expenses.blade.php new file mode 100644 index 00000000..824e6186 --- /dev/null +++ b/resources/views/business/pages/financial-operations/labor-expenses.blade.php @@ -0,0 +1,855 @@ +@php + $laborFields = $laborExpenseTable['fields'] ?? []; + $laborGroups = $laborExpenseTable['groups'] ?? []; + $laborFormulaFields = collect($laborFields)->filter( + static fn (array $field): bool => filled($field['formula'] ?? null) + ); + $laborFormulaCount = $laborFormulaFields->count(); +@endphp + +
+ + +
+
+ 人工费用测算表 +

表头与公式依据 {{ $laborExpenseTable['source_label'] ?? '工资表' }}。黄色字段用于录入,浅金列按公式实时计算;公式中的 # 表示当前行。

+
+ {{ count($laborFields) }} 个业务列 · {{ $laborFormulaCount }} 个公式 +
+ +
+
+ 测算行数 + 1 +
+
+ 月实发工资合计(元) + — +
+
+ 月度工资+兼职个税合计(元) + — +
+
+ 汇算应退/补合计(元) + — +
+
+ +
+ +
页面不预置源表人员、花名或金额,录入值只在当前页面参与计算;可使用表格上方的导出按钮保存当前结果。
+
+ +
+ + + + + + 可录入 + 公式 + +
+ +
+ + + + @foreach ($laborGroups as $group) + + @endforeach + + + + @foreach ($laborFields as $field) + + @endforeach + + + + + + + + +
{{ $group['label'] }}操作
{{ $field['label'] }}操作
+
正在加载人工费用测算表…
+
+
+ +
+ 查看全部 {{ $laborFormulaCount }} 个公式 +
+ @foreach ($laborFormulaFields as $field) +
+ {{ $field['key'] }} · {{ $field['label'] }} + {{ $field['formula'] }} +
+ @endforeach +
+
+ + +
diff --git a/tests/Feature/Business/LaborExpensePageAccessTest.php b/tests/Feature/Business/LaborExpensePageAccessTest.php index 9f71c36b..b4fe3f25 100644 --- a/tests/Feature/Business/LaborExpensePageAccessTest.php +++ b/tests/Feature/Business/LaborExpensePageAccessTest.php @@ -60,6 +60,38 @@ class LaborExpensePageAccessTest extends FeatureTestCase ->assertForbidden(); } + public function test_page_contains_the_workbook_headers_and_formulas_without_payroll_rows(): void + { + $chenxi = $this->account('晨曦'); + $fields = config('labor_expense_table.fields'); + $formulaFields = collect($fields)->filter( + static fn (array $field): bool => filled($field['formula'] ?? null) + ); + + $this->assertCount(61, $fields); + $this->assertCount(34, $formulaFields); + + $response = $this->loginAs($chenxi)->get(route('business.pages.show', [ + 'section' => 'financial-operations', + 'page' => 'labor-expenses', + ]))->assertOk() + ->assertSeeText('人工费用测算表') + ->assertSeeText('基础信息') + ->assertSeeText('原数据') + ->assertSeeText('提高公积金基数方案') + ->assertSeeText('工资+兼职方案') + ->assertSeeText('汇算清缴') + ->assertSee('data-labor-expense', false) + ->assertSee('data-labor-expense-table', false) + ->assertSee('=F#-G#-H#-I#', false) + ->assertSee('=IF(AN#<=0,0,IF(AN#<=4000,800,AN#*0.2))', false) + ->assertDontSee('data-labor-row="0"', false); + + $html = $response->getContent(); + $this->assertSame(61, substr_count($html, 'data-labor-header="')); + $this->assertSame(34, substr_count($html, 'class="labor-expense-formula"')); + } + private function pageIsVisible(User $user): bool { $financialOperations = collect(SidebarPermissions::navigationFor($user))