feat: 新增税金预测表逻辑配置页面
This commit is contained in:
parent
35fe10bc69
commit
bfc119608f
@ -69,6 +69,7 @@ use Modules\EcommerceFinance\Repositories\TmallTaobaoSpreadBillRepository;
|
||||
use Modules\EcommerceFinance\Services\AccountMappingStore;
|
||||
use Modules\EcommerceFinance\Services\AccountMappingValidator;
|
||||
use Modules\EcommerceFinance\Services\StoreReportInitialPeriods;
|
||||
use Modules\EcommerceFinance\Services\TaxForecastLogic;
|
||||
use Modules\EcommerceFinance\Services\UpstreamQueryCacheFactory;
|
||||
use Modules\EcommerceFinance\Services\SupplierPayableMaintenanceRegistry;
|
||||
use Modules\EcommerceFinance\Services\PayablesFinalRegistry;
|
||||
@ -1673,10 +1674,60 @@ class Pages extends Controller
|
||||
'planting-details' => $this->contentForPlantingDetails(),
|
||||
'jst-order-labels' => $this->contentForJstOrderLabels(),
|
||||
'store-report-data-logic' => $this->prepareStoreReportLogic(),
|
||||
'tax-forecast-data-logic' => $this->contentForTaxForecastLogic(),
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 税金预测表逻辑配置:规则清单、数据源状态和运行参数。
|
||||
*/
|
||||
protected function contentForTaxForecastLogic(): array
|
||||
{
|
||||
return [
|
||||
'view' => 'business.pages.company-management.tax-forecast-data-logic',
|
||||
'taxRules' => TaxForecastLogic::rules(),
|
||||
'taxSettings' => TaxForecastLogic::current(),
|
||||
'taxHealth' => TaxForecastLogic::health(),
|
||||
];
|
||||
}
|
||||
|
||||
public function editTaxForecastLogic()
|
||||
{
|
||||
return view('business.pages.company-management.tax-forecast-data-logic-edit', [
|
||||
'section' => ['slug' => 'trademark-management', 'title' => '商标管理'],
|
||||
'page' => ['slug' => 'tax-forecast-data-logic', 'title' => '税金预测表逻辑配置'],
|
||||
'settings' => TaxForecastLogic::current(),
|
||||
'incomeForecastSources' => TaxForecastLogic::INCOME_FORECAST_SOURCES,
|
||||
]);
|
||||
}
|
||||
|
||||
public function updateTaxForecastLogic(Request $request)
|
||||
{
|
||||
$data = $request->validate([
|
||||
'vat_small_rate' => ['required', 'numeric', 'min:0', 'max:100'],
|
||||
'vat_free_threshold' => ['required', 'numeric', 'min:0'],
|
||||
'vat_general_enabled' => ['required', 'in:0,1'],
|
||||
'surtax_city_rate' => ['required', 'numeric', 'min:0', 'max:100'],
|
||||
'surtax_education_rate' => ['required', 'numeric', 'min:0', 'max:100'],
|
||||
'surtax_local_education_rate' => ['required', 'numeric', 'min:0', 'max:100'],
|
||||
'surtax_halved' => ['required', 'in:0,1'],
|
||||
'stamp_duty_rate' => ['required', 'numeric', 'min:0', 'max:100'],
|
||||
'stamp_duty_halved' => ['required', 'in:0,1'],
|
||||
'cit_effective_rate' => ['required', 'numeric', 'min:0', 'max:100'],
|
||||
'cit_threshold' => ['required', 'numeric', 'min:0'],
|
||||
'individual_halved_threshold' => ['required', 'numeric', 'min:0'],
|
||||
'assessed_income_rate' => ['required', 'numeric', 'min:0', 'max:100'],
|
||||
'income_forecast_source' => ['required', 'in:' . implode(',', array_keys(TaxForecastLogic::INCOME_FORECAST_SOURCES))],
|
||||
]);
|
||||
|
||||
TaxForecastLogic::save($data);
|
||||
|
||||
return redirect()
|
||||
->route('business.pages.show', ['section' => 'trademark-management', 'page' => 'tax-forecast-data-logic'])
|
||||
->with('success', '税金预测表逻辑配置已保存,下次预测按新参数计算。');
|
||||
}
|
||||
|
||||
/** 首次打开店铺维度报表逻辑页时把代码里的期初数据固化成可编辑设置。 */
|
||||
protected function prepareStoreReportLogic(): ?array
|
||||
{
|
||||
|
||||
@ -233,6 +233,10 @@ $navigation = [
|
||||
'slug' => 'store-report-data-logic',
|
||||
'title' => '店铺维度报表逻辑配置',
|
||||
],
|
||||
[
|
||||
'slug' => 'tax-forecast-data-logic',
|
||||
'title' => '税金预测表逻辑配置',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
201
modules/EcommerceFinance/Services/TaxForecastLogic.php
Normal file
201
modules/EcommerceFinance/Services/TaxForecastLogic.php
Normal file
@ -0,0 +1,201 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\EcommerceFinance\Services;
|
||||
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* 「税金预测表逻辑配置」的可维护参数与规则目录。
|
||||
*
|
||||
* 税金预测表页面只展示预测结果;税率、免税额度、减半开关、核定默认值
|
||||
* 等运行参数由负责人在这里维护,读取时设置值优先、代码默认值兜底。
|
||||
*/
|
||||
class TaxForecastLogic
|
||||
{
|
||||
private const PREFIX = 'business_tax_forecast';
|
||||
|
||||
/** 扁平键:设置组件会按点号展开存储,不能嵌套数组。 */
|
||||
public const KEYS = [
|
||||
'vat_small_rate' => self::PREFIX . '.vat_small_rate',
|
||||
'vat_free_threshold' => self::PREFIX . '.vat_free_threshold',
|
||||
'vat_general_enabled' => self::PREFIX . '.vat_general_enabled',
|
||||
'surtax_city_rate' => self::PREFIX . '.surtax_city_rate',
|
||||
'surtax_education_rate' => self::PREFIX . '.surtax_education_rate',
|
||||
'surtax_local_education_rate' => self::PREFIX . '.surtax_local_education_rate',
|
||||
'surtax_halved' => self::PREFIX . '.surtax_halved',
|
||||
'stamp_duty_rate' => self::PREFIX . '.stamp_duty_rate',
|
||||
'stamp_duty_halved' => self::PREFIX . '.stamp_duty_halved',
|
||||
'cit_effective_rate' => self::PREFIX . '.cit_effective_rate',
|
||||
'cit_threshold' => self::PREFIX . '.cit_threshold',
|
||||
'individual_halved_threshold' => self::PREFIX . '.individual_halved_threshold',
|
||||
'assessed_income_rate' => self::PREFIX . '.assessed_income_rate',
|
||||
'income_forecast_source' => self::PREFIX . '.income_forecast_source',
|
||||
];
|
||||
|
||||
public const INCOME_FORECAST_SOURCES = [
|
||||
'last_month_actual' => '上月实际收入(bills.income)',
|
||||
'cost_arrangement' => '公司成本安排的当月收入预测',
|
||||
'manual' => '手工录入',
|
||||
];
|
||||
|
||||
/** @return array<string,string> */
|
||||
public static function defaults(): array
|
||||
{
|
||||
return [
|
||||
'vat_small_rate' => '1',
|
||||
'vat_free_threshold' => '300000',
|
||||
'vat_general_enabled' => '1',
|
||||
'surtax_city_rate' => '7',
|
||||
'surtax_education_rate' => '3',
|
||||
'surtax_local_education_rate' => '2',
|
||||
'surtax_halved' => '1',
|
||||
'stamp_duty_rate' => '0.03',
|
||||
'stamp_duty_halved' => '1',
|
||||
'cit_effective_rate' => '5',
|
||||
'cit_threshold' => '3000000',
|
||||
'individual_halved_threshold' => '2000000',
|
||||
'assessed_income_rate' => '10',
|
||||
'income_forecast_source' => 'last_month_actual',
|
||||
];
|
||||
}
|
||||
|
||||
/** @return array<string,string> */
|
||||
public static function current(): array
|
||||
{
|
||||
$values = [];
|
||||
foreach (self::KEYS as $key => $settingKey) {
|
||||
$value = setting($settingKey, null);
|
||||
$values[$key] = is_array($value) ? implode('', $value) : (string) ($value ?? '');
|
||||
}
|
||||
|
||||
return array_merge(self::defaults(), array_filter($values, static fn (string $value): bool => $value !== ''));
|
||||
}
|
||||
|
||||
public static function value(string $key, mixed $default = null): mixed
|
||||
{
|
||||
$current = self::current();
|
||||
|
||||
return $current[$key] ?? ($default ?? (self::defaults()[$key] ?? null));
|
||||
}
|
||||
|
||||
public static function rate(string $key): float
|
||||
{
|
||||
return max((float) self::value($key, 0), 0);
|
||||
}
|
||||
|
||||
public static function amount(string $key): float
|
||||
{
|
||||
return max((float) self::value($key, 0), 0);
|
||||
}
|
||||
|
||||
public static function toggled(string $key): bool
|
||||
{
|
||||
return (string) self::value($key, '1') === '1';
|
||||
}
|
||||
|
||||
/** @param array<string,mixed> $values */
|
||||
public static function save(array $values): void
|
||||
{
|
||||
$payload = [];
|
||||
foreach (self::defaults() as $key => $default) {
|
||||
if (! array_key_exists($key, $values) || ! isset(self::KEYS[$key])) {
|
||||
continue;
|
||||
}
|
||||
$payload[self::KEYS[$key]] = mb_substr(trim((string) $values[$key]), 0, 191);
|
||||
}
|
||||
|
||||
if ($payload !== []) {
|
||||
setting($payload)->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 规则目录:页面上的规则清单以这里为准,和取数公式文档
|
||||
* docs/税金预测表需求说明.md 保持一致。
|
||||
*
|
||||
* @return array<string,array<int,array{code:string,object:string,logic:string,source:string,priority:string}>>
|
||||
*/
|
||||
public static function rules(): array
|
||||
{
|
||||
return [
|
||||
'主体识别' => [
|
||||
['code' => 'R1', 'object' => '公司名称、公司税务类型', 'logic' => '读取公司基础信息镜像的营业执照名称和纳税类别及类型,作为增值税口径判断依据。', 'source' => 'rpt.fys_companyInfo(只读镜像)', 'priority' => '税务类型空值时按“无税务登记”处理并提示补充。'],
|
||||
['code' => 'R2', 'object' => '工商类型', 'logic' => '区分有限责任公司、个人独资企业、合伙企业、个体工商户,决定所得税种。', 'source' => '待接入:工商登记信息或人工维护', 'priority' => '缺失时所得税列留空,不按企业所得税估算。'],
|
||||
['code' => 'R3', 'object' => '征收方式、核定应税所得率', 'logic' => '查账征收按利润计算;核定征收按 收入 × 核定应税所得率 计算;定期定额按核定定额折算。', 'source' => '待接入:电子税务局数据或人工维护', 'priority' => '缺失时按查账征收展示,并在备注列标记待确认。'],
|
||||
],
|
||||
'增值税' => [
|
||||
['code' => 'R4', 'object' => '小规模纳税人免税判断', 'logic' => '季度不含税销售额不超过免税额度时,预计增值税为 0。', 'source' => 'bills.income 按主体期间归集', 'priority' => '免税额度按配置参数执行,默认 30 万元/季。'],
|
||||
['code' => 'R5', 'object' => '小规模纳税人征收率', 'logic' => '超过免税额度时,预计增值税 = 不含税销售额 × 征收率。', 'source' => '配置参数', 'priority' => '默认 1%(3% 减按 1%)。'],
|
||||
['code' => 'R6', 'object' => '一般纳税人计税', 'logic' => '预计增值税 = 销项税额 − 进项税额;进项缺失时按销项全额提示。', 'source' => 'bills.income 与进项数据', 'priority' => '默认开启,可在配置中关闭改为简易口径。'],
|
||||
['code' => 'R7', 'object' => '个体工商户、个人独资企业、合伙企业', 'logic' => '增值税按小规模纳税人口径计算。', 'source' => '同上', 'priority' => '不因工商类型改为一般纳税人口径。'],
|
||||
],
|
||||
'附加税费' => [
|
||||
['code' => 'R8', 'object' => '城建税', 'logic' => '城建税 = 预计增值税 × 城建税税率。', 'source' => '配置参数', 'priority' => '默认市区 7%,县城/镇 5%,其他 1%。'],
|
||||
['code' => 'R9', 'object' => '教育费附加、地方教育附加', 'logic' => '教育费附加 = 增值税 × 3%;地方教育附加 = 增值税 × 2%。', 'source' => '配置参数', 'priority' => '税率可调。'],
|
||||
['code' => 'R10', 'object' => '六税两费减半', 'logic' => '小规模纳税人、小型微利企业、个体工商户的附加税费减半征收。', 'source' => '配置开关', 'priority' => '默认开启;关闭后按全额计算。'],
|
||||
],
|
||||
'印花税' => [
|
||||
['code' => 'R11', 'object' => '购销合同印花税', 'logic' => '印花税 = 购销合同金额 × 印花税税率(减半开关生效时再乘 50%)。', 'source' => '配置参数与成本安排金额', 'priority' => '默认 0.03% 并减半。'],
|
||||
],
|
||||
'所得税' => [
|
||||
['code' => 'R12', 'object' => '小型微利企业所得税', 'logic' => '应纳税所得额 × 25% × 20%,年不超过上限部分实际税负 5%。', 'source' => '配置参数 + 预计利润', 'priority' => '默认上限 300 万元,超过部分提示分段计算。'],
|
||||
['code' => 'R13', 'object' => '经营所得个人所得税', 'logic' => '个人独资企业、合伙企业、个体工商户按经营所得五级超额累进计算;核定征收按 收入 × 核定应税所得率 计算。', 'source' => '配置参数 + 核定通知书', 'priority' => '核定应税所得率默认 10%。'],
|
||||
['code' => 'R14', 'object' => '个体工商户减半', 'logic' => '年应纳税所得额不超过上限部分,经营所得个人所得税减半征收。', 'source' => '配置参数', 'priority' => '默认上限 200 万元。'],
|
||||
],
|
||||
'数据来源' => [
|
||||
['code' => 'R15', 'object' => '收入预测', 'logic' => '按主体生效/失效期间把 bills.income 归集到预测月份;未出账时按配置的收入预测口径取值。', 'source' => 'runtime/bills.sqlite', 'priority' => '口径可在配置中切换。'],
|
||||
['code' => 'R16', 'object' => '成本费用预测', 'logic' => '供应商成本安排 + 快递成本安排 + 人工成本预测 + 其他费用预测。', 'source' => '公司成本安排页', 'priority' => '成本安排未接入时所得税列留空。'],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据源现状:只读共享业务镜像,不访问上游。
|
||||
*
|
||||
* @return array<int,array{name:string,object:string,status:string,detail:string}>
|
||||
*/
|
||||
public static function health(): array
|
||||
{
|
||||
$health = [
|
||||
[
|
||||
'name' => '公司基础信息(含税务类型)',
|
||||
'object' => 'ops_fys_company_info',
|
||||
'status' => '尚未同步',
|
||||
'detail' => '',
|
||||
],
|
||||
[
|
||||
'name' => '工商类型',
|
||||
'object' => '待接入',
|
||||
'status' => '未接入',
|
||||
'detail' => '现有镜像无法区分个人独资企业、合伙企业;确认数据来源后接入。',
|
||||
],
|
||||
[
|
||||
'name' => '征收方式与核定率',
|
||||
'object' => '待接入',
|
||||
'status' => '未接入',
|
||||
'detail' => '查账/核定、核定应税所得率需电子税务局数据或人工维护。',
|
||||
],
|
||||
];
|
||||
|
||||
try {
|
||||
$connection = DB::connection((string) config('upstream_mirrors.connection', 'business_mirror'));
|
||||
if ($connection->getSchemaBuilder()->hasTable('ops_fys_company_info')) {
|
||||
$row = $connection->table('ops_fys_company_info')
|
||||
->selectRaw("COUNT(*) AS total, SUM(CASE WHEN taxCategoryAndType = '小规模纳税人' THEN 1 ELSE 0 END) AS small, SUM(CASE WHEN taxCategoryAndType = '一般纳税人' THEN 1 ELSE 0 END) AS general")
|
||||
->first();
|
||||
$health[0]['status'] = '已同步';
|
||||
$health[0]['detail'] = sprintf(
|
||||
'主体 %d 家:小规模 %d 家、一般纳税人 %d 家',
|
||||
(int) ($row->total ?? 0),
|
||||
(int) ($row->small ?? 0),
|
||||
(int) ($row->general ?? 0),
|
||||
);
|
||||
}
|
||||
} catch (Throwable) {
|
||||
$health[0]['detail'] = '读取共享镜像失败,请检查连接与同步状态。';
|
||||
}
|
||||
|
||||
return $health;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,211 @@
|
||||
@php
|
||||
use Modules\EcommerceFinance\Services\TaxForecastLogic;
|
||||
|
||||
$rules = TaxForecastLogic::rules();
|
||||
@endphp
|
||||
|
||||
<style>
|
||||
.logic-edit-page { max-width: 1100px; margin-top: 1.5rem; }
|
||||
.logic-edit-card {
|
||||
margin-bottom: 1.25rem;
|
||||
padding: 1.25rem 1.5rem;
|
||||
border: 1px solid var(--vip-border, #d8d2c7);
|
||||
border-radius: 0.75rem;
|
||||
background: var(--vip-surface, #ffffff);
|
||||
}
|
||||
.logic-edit-card h3 { margin: 0 0 0.35rem; font-size: 1rem; font-weight: 600; }
|
||||
.logic-edit-card p.hint { margin: 0 0 1rem; color: #7c7565; font-size: 0.8125rem; line-height: 1.35rem; }
|
||||
.logic-edit-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); gap: 0.9rem 1.25rem; }
|
||||
.logic-edit-field label { display: block; margin-bottom: 0.3rem; color: #4b4638; font-size: 0.8125rem; font-weight: 600; }
|
||||
.logic-edit-field input[type="text"],
|
||||
.logic-edit-field input[type="number"],
|
||||
.logic-edit-field select {
|
||||
width: 100%;
|
||||
padding: 0.45rem 0.65rem;
|
||||
border: 1px solid #d8d2c7;
|
||||
border-radius: 0.45rem;
|
||||
background: #ffffff;
|
||||
color: #33302a;
|
||||
font-size: 0.8125rem;
|
||||
}
|
||||
.logic-edit-field .field-hint { margin-top: 0.25rem; color: #8b8577; font-size: 0.75rem; }
|
||||
.logic-edit-actions { display: flex; gap: 0.6rem; align-items: center; }
|
||||
.logic-edit-actions button {
|
||||
padding: 0.5rem 1.2rem;
|
||||
border: 0;
|
||||
border-radius: 0.45rem;
|
||||
background: #2a2927;
|
||||
color: #f2e7c5;
|
||||
font-size: 0.875rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
.logic-edit-actions a { color: #8a6d1f; font-size: 0.875rem; text-decoration: underline; }
|
||||
.logic-edit-table { width: 100%; border-collapse: collapse; font-size: 0.8125rem; }
|
||||
.logic-edit-table th {
|
||||
padding: 0.6rem 0.75rem;
|
||||
border-bottom: 1px solid var(--vip-border, #e5e0d6);
|
||||
background: #faf8f4;
|
||||
color: #6b6455;
|
||||
text-align: left;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.logic-edit-table td { padding: 0.6rem 0.75rem; border-bottom: 1px solid #f0ece4; color: #33302a; vertical-align: top; }
|
||||
</style>
|
||||
|
||||
<div class="logic-edit-page">
|
||||
<form method="POST" action="{{ route('business.tax-forecast-logic.update') }}">
|
||||
@csrf
|
||||
|
||||
<div class="logic-edit-card">
|
||||
<h3>增值税</h3>
|
||||
<p class="hint">小规模纳税人按季度销售额判断免税;一般纳税人的进销项抵扣可单独关闭。</p>
|
||||
<div class="logic-edit-grid">
|
||||
<div class="logic-edit-field">
|
||||
<label>小规模增值税征收率(%)</label>
|
||||
<input type="number" name="vat_small_rate" step="0.01" min="0" max="100" value="{{ $settings['vat_small_rate'] ?? '1' }}">
|
||||
<div class="field-hint">对应规则 R5,默认 1%(3% 减按 1%)。</div>
|
||||
</div>
|
||||
<div class="logic-edit-field">
|
||||
<label>小规模季度免税额度(元)</label>
|
||||
<input type="number" name="vat_free_threshold" step="1000" min="0" value="{{ $settings['vat_free_threshold'] ?? '300000' }}">
|
||||
<div class="field-hint">对应规则 R4,默认 300000 元/季。</div>
|
||||
</div>
|
||||
<div class="logic-edit-field">
|
||||
<label>一般纳税人进销项抵扣</label>
|
||||
<select name="vat_general_enabled">
|
||||
<option value="1" @selected(($settings['vat_general_enabled'] ?? '1') === '1')>开启(销项 − 进项)</option>
|
||||
<option value="0" @selected(($settings['vat_general_enabled'] ?? '') === '0')>关闭(按销项全额提示)</option>
|
||||
</select>
|
||||
<div class="field-hint">对应规则 R6。</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="logic-edit-card">
|
||||
<h3>附加税费</h3>
|
||||
<p class="hint">附加税费以预计增值税为计税依据,六税两费减半开关对附加税费统一生效。</p>
|
||||
<div class="logic-edit-grid">
|
||||
<div class="logic-edit-field">
|
||||
<label>城建税税率(%)</label>
|
||||
<input type="number" name="surtax_city_rate" step="0.01" min="0" max="100" value="{{ $settings['surtax_city_rate'] ?? '7' }}">
|
||||
<div class="field-hint">对应规则 R8,市区 7%、县城/镇 5%、其他 1%。</div>
|
||||
</div>
|
||||
<div class="logic-edit-field">
|
||||
<label>教育费附加(%)</label>
|
||||
<input type="number" name="surtax_education_rate" step="0.01" min="0" max="100" value="{{ $settings['surtax_education_rate'] ?? '3' }}">
|
||||
<div class="field-hint">对应规则 R9,默认 3%。</div>
|
||||
</div>
|
||||
<div class="logic-edit-field">
|
||||
<label>地方教育附加(%)</label>
|
||||
<input type="number" name="surtax_local_education_rate" step="0.01" min="0" max="100" value="{{ $settings['surtax_local_education_rate'] ?? '2' }}">
|
||||
<div class="field-hint">对应规则 R9,默认 2%。</div>
|
||||
</div>
|
||||
<div class="logic-edit-field">
|
||||
<label>六税两费减半</label>
|
||||
<select name="surtax_halved">
|
||||
<option value="1" @selected(($settings['surtax_halved'] ?? '1') === '1')>开启</option>
|
||||
<option value="0" @selected(($settings['surtax_halved'] ?? '') === '0')>关闭</option>
|
||||
</select>
|
||||
<div class="field-hint">对应规则 R10。</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="logic-edit-card">
|
||||
<h3>印花税</h3>
|
||||
<p class="hint">印花税按购销合同金额计算,减半开关单独维护。</p>
|
||||
<div class="logic-edit-grid">
|
||||
<div class="logic-edit-field">
|
||||
<label>印花税税率(%)</label>
|
||||
<input type="number" name="stamp_duty_rate" step="0.001" min="0" max="100" value="{{ $settings['stamp_duty_rate'] ?? '0.03' }}">
|
||||
<div class="field-hint">对应规则 R11,默认 0.03%。</div>
|
||||
</div>
|
||||
<div class="logic-edit-field">
|
||||
<label>印花税减半</label>
|
||||
<select name="stamp_duty_halved">
|
||||
<option value="1" @selected(($settings['stamp_duty_halved'] ?? '1') === '1')>开启</option>
|
||||
<option value="0" @selected(($settings['stamp_duty_halved'] ?? '') === '0')>关闭</option>
|
||||
</select>
|
||||
<div class="field-hint">对应规则 R11。</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="logic-edit-card">
|
||||
<h3>所得税</h3>
|
||||
<p class="hint">有限公司按小型微利企业口径,个人独资企业、合伙企业、个体工商户按经营所得口径。</p>
|
||||
<div class="logic-edit-grid">
|
||||
<div class="logic-edit-field">
|
||||
<label>小型微利企业所得税实际税负(%)</label>
|
||||
<input type="number" name="cit_effective_rate" step="0.01" min="0" max="100" value="{{ $settings['cit_effective_rate'] ?? '5' }}">
|
||||
<div class="field-hint">对应规则 R12,默认 5%(25% × 20%)。</div>
|
||||
</div>
|
||||
<div class="logic-edit-field">
|
||||
<label>年应纳税所得额上限(元)</label>
|
||||
<input type="number" name="cit_threshold" step="10000" min="0" value="{{ $settings['cit_threshold'] ?? '3000000' }}">
|
||||
<div class="field-hint">超过上限部分提示分段计算。</div>
|
||||
</div>
|
||||
<div class="logic-edit-field">
|
||||
<label>个体工商户减半上限(元)</label>
|
||||
<input type="number" name="individual_halved_threshold" step="10000" min="0" value="{{ $settings['individual_halved_threshold'] ?? '2000000' }}">
|
||||
<div class="field-hint">对应规则 R14,默认 2000000 元。</div>
|
||||
</div>
|
||||
<div class="logic-edit-field">
|
||||
<label>核定应税所得率默认值(%)</label>
|
||||
<input type="number" name="assessed_income_rate" step="0.01" min="0" max="100" value="{{ $settings['assessed_income_rate'] ?? '10' }}">
|
||||
<div class="field-hint">核定征收主体未单独维护时按此默认值。</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="logic-edit-card">
|
||||
<h3>预测口径</h3>
|
||||
<p class="hint">预测月份尚未出账时,收入取值方式。</p>
|
||||
<div class="logic-edit-grid">
|
||||
<div class="logic-edit-field">
|
||||
<label>收入预测口径</label>
|
||||
<select name="income_forecast_source">
|
||||
@foreach ($incomeForecastSources as $value => $label)
|
||||
<option value="{{ $value }}" @selected(($settings['income_forecast_source'] ?? 'last_month_actual') === $value)>{{ $label }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<div class="field-hint">对应规则 R15。</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="logic-edit-actions">
|
||||
<button type="submit">保存配置</button>
|
||||
<a href="{{ route('business.pages.show', ['section' => 'trademark-management', 'page' => 'tax-forecast-data-logic']) }}">返回规则页</a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="logic-edit-card" style="margin-top: 1.25rem;">
|
||||
<h3>规则目录(只读)</h3>
|
||||
<p class="hint">规则编号和逻辑说明与取数公式文档保持一致,用于核对上面参数的实际含义。</p>
|
||||
<table class="logic-edit-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>规则编号</th>
|
||||
<th>规则类别</th>
|
||||
<th>规则对象</th>
|
||||
<th>规则逻辑</th>
|
||||
<th>优先级说明</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach ($rules as $category => $categoryRules)
|
||||
@foreach ($categoryRules as $rule)
|
||||
<tr>
|
||||
<td>{{ $rule['code'] }}</td>
|
||||
<td>{{ $category }}</td>
|
||||
<td>{{ $rule['object'] }}</td>
|
||||
<td>{{ $rule['logic'] }}</td>
|
||||
<td>{{ $rule['priority'] }}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
@ -0,0 +1,183 @@
|
||||
@php
|
||||
use Modules\EcommerceFinance\Services\TaxForecastLogic;
|
||||
|
||||
$parameterRows = [
|
||||
['label' => '小规模增值税征收率', 'value' => ($taxSettings['vat_small_rate'] ?? '1') . '%', 'hint' => '超过免税额度时按不含税销售额计算'],
|
||||
['label' => '小规模季度免税额度', 'value' => number_format((float) ($taxSettings['vat_free_threshold'] ?? 0)) . ' 元/季', 'hint' => '季度不含税销售额不超过该额度时增值税为 0'],
|
||||
['label' => '一般纳税人进销项抵扣', 'value' => ($taxSettings['vat_general_enabled'] ?? '1') === '1' ? '开启' : '关闭', 'hint' => '关闭后一般纳税人按销项税额全额提示'],
|
||||
['label' => '城建税税率', 'value' => ($taxSettings['surtax_city_rate'] ?? '7') . '%', 'hint' => '市区 7%、县城/镇 5%、其他 1%'],
|
||||
['label' => '教育费附加', 'value' => ($taxSettings['surtax_education_rate'] ?? '3') . '%', 'hint' => '以预计增值税为计税依据'],
|
||||
['label' => '地方教育附加', 'value' => ($taxSettings['surtax_local_education_rate'] ?? '2') . '%', 'hint' => '以预计增值税为计税依据'],
|
||||
['label' => '六税两费减半', 'value' => ($taxSettings['surtax_halved'] ?? '1') === '1' ? '开启' : '关闭', 'hint' => '附加税费减半征收'],
|
||||
['label' => '印花税税率(购销合同)', 'value' => ($taxSettings['stamp_duty_rate'] ?? '0.03') . '%', 'hint' => '按购销合同金额计算'],
|
||||
['label' => '印花税减半', 'value' => ($taxSettings['stamp_duty_halved'] ?? '1') === '1' ? '开启' : '关闭', 'hint' => '小规模、小微减半征收'],
|
||||
['label' => '小型微利企业所得税实际税负', 'value' => ($taxSettings['cit_effective_rate'] ?? '5') . '%', 'hint' => '应纳税所得额 × 25% × 20%'],
|
||||
['label' => '年应纳税所得额上限', 'value' => number_format((float) ($taxSettings['cit_threshold'] ?? 0)) . ' 元', 'hint' => '超过上限部分提示分段计算'],
|
||||
['label' => '个体工商户减半上限', 'value' => number_format((float) ($taxSettings['individual_halved_threshold'] ?? 0)) . ' 元', 'hint' => '经营所得个人所得税减半'],
|
||||
['label' => '核定应税所得率默认值', 'value' => ($taxSettings['assessed_income_rate'] ?? '10') . '%', 'hint' => '核定征收主体未单独维护时按此默认值'],
|
||||
['label' => '收入预测口径', 'value' => TaxForecastLogic::INCOME_FORECAST_SOURCES[$taxSettings['income_forecast_source'] ?? ''] ?? '上月实际收入(bills.income)', 'hint' => '预测月份未出账时取值方式'],
|
||||
];
|
||||
@endphp
|
||||
|
||||
<style>
|
||||
.data-logic-page { margin-top: 1.5rem; }
|
||||
.data-logic-note {
|
||||
padding: 1rem 1.25rem;
|
||||
border: 1px solid var(--vip-border, #d8d2c7);
|
||||
border-radius: 0.75rem;
|
||||
background: var(--vip-surface, #ffffff);
|
||||
color: var(--vip-content-ink, #171717);
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.5rem;
|
||||
}
|
||||
.data-logic-card {
|
||||
margin-top: 1.25rem;
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--vip-border, #d8d2c7);
|
||||
border-radius: 0.75rem;
|
||||
background: var(--vip-surface, #ffffff);
|
||||
}
|
||||
.data-logic-card h3 {
|
||||
margin: 0;
|
||||
padding: 0.9rem 1.1rem;
|
||||
border-bottom: 1px solid var(--vip-border, #e5e0d6);
|
||||
font-size: 0.9375rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
.data-logic-table { width: 100%; border-collapse: collapse; font-size: 0.8125rem; }
|
||||
.data-logic-table th {
|
||||
padding: 0.65rem 0.9rem;
|
||||
border-bottom: 1px solid var(--vip-border, #e5e0d6);
|
||||
background: #faf8f4;
|
||||
color: #6b6455;
|
||||
font-weight: 600;
|
||||
text-align: left;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.data-logic-table td {
|
||||
padding: 0.7rem 0.9rem;
|
||||
border-bottom: 1px solid #f0ece4;
|
||||
color: #33302a;
|
||||
vertical-align: top;
|
||||
line-height: 1.4rem;
|
||||
}
|
||||
.data-logic-table tr:last-child td { border-bottom: 0; }
|
||||
.data-logic-code { font-weight: 600; color: #8a6d1f; white-space: nowrap; }
|
||||
.data-logic-category { white-space: nowrap; }
|
||||
.data-logic-tag {
|
||||
display: inline-block;
|
||||
padding: 0.1rem 0.45rem;
|
||||
border-radius: 0.4rem;
|
||||
background: #f4efe3;
|
||||
color: #7a6428;
|
||||
font-size: 0.75rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.data-logic-tag.is-pending { background: #fdeeee; color: #9c2f2f; }
|
||||
.data-logic-value { font-weight: 600; color: #2a2927; white-space: nowrap; }
|
||||
.data-logic-health { margin-top: 0.35rem; color: #7c7565; font-size: 0.75rem; line-height: 1.15rem; }
|
||||
.data-logic-edit-button {
|
||||
padding: 0.45rem 1.1rem;
|
||||
border-radius: 0.45rem;
|
||||
background: #2a2927;
|
||||
color: #f2e7c5 !important;
|
||||
font-size: 0.8125rem;
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="data-logic-page">
|
||||
<div class="data-logic-note" style="display: flex; align-items: center; justify-content: space-between; gap: 1rem; margin-bottom: 1.25rem;">
|
||||
<span>规则参数由负责人维护,保存后下次预测按新参数计算。</span>
|
||||
<a class="data-logic-edit-button" href="{{ route('business.tax-forecast-logic.edit') }}">编辑配置</a>
|
||||
</div>
|
||||
<div class="data-logic-note">
|
||||
本页是税金预测表当前实际使用的计算口径,与 <code>docs/税金预测表需求说明.md</code> 保持一致;
|
||||
工商类型、征收方式(查账/核定)接入前,涉及所得税的列在预测表中留空,不做推测。
|
||||
<a href="{{ route('business.pages.show', ['section' => 'company-management', 'page' => 'tax-forecast']) }}" style="color: #8a6d1f;">返回税金预测表</a>
|
||||
</div>
|
||||
|
||||
<div class="data-logic-card">
|
||||
<h3>当前运行参数</h3>
|
||||
<table class="data-logic-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>参数</th>
|
||||
<th>当前值</th>
|
||||
<th>说明</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach ($parameterRows as $row)
|
||||
<tr>
|
||||
<td>{{ $row['label'] }}</td>
|
||||
<td class="data-logic-value">{{ $row['value'] }}</td>
|
||||
<td>{{ $row['hint'] }}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="data-logic-card">
|
||||
<h3>数据来源</h3>
|
||||
<table class="data-logic-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>来源</th>
|
||||
<th>对象</th>
|
||||
<th>状态</th>
|
||||
<th>说明</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach ($taxHealth as $source)
|
||||
<tr>
|
||||
<td>{{ $source['name'] }}</td>
|
||||
<td><code>{{ $source['object'] }}</code></td>
|
||||
<td>
|
||||
<span class="data-logic-tag {{ $source['status'] === '已同步' ? '' : 'is-pending' }}">{{ $source['status'] }}</span>
|
||||
</td>
|
||||
<td>
|
||||
{{ $source['detail'] !== '' ? $source['detail'] : '—' }}
|
||||
<div class="data-logic-health">收入口径:主体维度经营报表同源(bills.income);成本口径:公司成本安排页。</div>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="data-logic-card">
|
||||
<h3>规则清单</h3>
|
||||
<table class="data-logic-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>规则编号</th>
|
||||
<th>规则类别</th>
|
||||
<th>规则对象</th>
|
||||
<th>规则逻辑</th>
|
||||
<th>数据来源</th>
|
||||
<th>优先级说明</th>
|
||||
<th>状态</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach ($taxRules as $category => $categoryRules)
|
||||
@foreach ($categoryRules as $rule)
|
||||
<tr>
|
||||
<td class="data-logic-code">{{ $rule['code'] }}</td>
|
||||
<td class="data-logic-category">{{ $category }}</td>
|
||||
<td>{{ $rule['object'] }}</td>
|
||||
<td>{{ $rule['logic'] }}</td>
|
||||
<td>{{ $rule['source'] }}</td>
|
||||
<td>{{ $rule['priority'] }}</td>
|
||||
<td><span class="data-logic-tag">已生效</span></td>
|
||||
</tr>
|
||||
@endforeach
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
@ -165,6 +165,7 @@
|
||||
['trademark-management', 'subject-trademark-rules', '主体商标规则', 'rule'],
|
||||
['trademark-management', 'store-change-rules', '店铺变更规则', 'rule'],
|
||||
['trademark-management', 'store-report-data-logic', '店铺维度报表逻辑配置', 'table_chart'],
|
||||
['trademark-management', 'tax-forecast-data-logic', '税金预测表逻辑配置', 'calculate'],
|
||||
]],
|
||||
['title' => '业务进度', 'items' => [
|
||||
['company-management', 'company-progress', '公司进度表', 'timeline'],
|
||||
|
||||
@ -353,6 +353,14 @@ Route::get('workspace/trademark-management/store-report-data-logic/edit', 'Busin
|
||||
->middleware('business.page:trademark-management,store-report-data-logic')
|
||||
->name('business.store-report-logic.edit');
|
||||
|
||||
Route::get('workspace/trademark-management/tax-forecast-data-logic/edit', 'Business\Pages@editTaxForecastLogic')
|
||||
->middleware('business.page:trademark-management,tax-forecast-data-logic')
|
||||
->name('business.tax-forecast-logic.edit');
|
||||
|
||||
Route::post('workspace/trademark-management/tax-forecast-data-logic/edit', 'Business\Pages@updateTaxForecastLogic')
|
||||
->middleware('business.page:trademark-management,tax-forecast-data-logic')
|
||||
->name('business.tax-forecast-logic.update');
|
||||
|
||||
Route::get('workspace/financial-operations/billing-details/export', 'Business\Pages@exportBillingDetails')
|
||||
->middleware('business.page:financial-operations,billing-details')
|
||||
->name('business.financial-operations.billing-details.export');
|
||||
|
||||
@ -455,6 +455,54 @@ class ManagementCrudTest extends FeatureTestCase
|
||||
->assertSeeText('编辑期初数据');
|
||||
}
|
||||
|
||||
public function testTaxForecastLogicConfigurationCanBeEditedFromThePage(): void
|
||||
{
|
||||
$this->loginAs()
|
||||
->get(route('business.tax-forecast-logic.edit'))
|
||||
->assertOk()
|
||||
->assertSeeText('小规模增值税征收率')
|
||||
->assertSeeText('小规模季度免税额度')
|
||||
->assertSeeText('核定应税所得率默认值')
|
||||
->assertSeeText('规则目录(只读)');
|
||||
|
||||
$this->post(route('business.tax-forecast-logic.update'), [
|
||||
'vat_small_rate' => '1',
|
||||
'vat_free_threshold' => '300000',
|
||||
'vat_general_enabled' => '1',
|
||||
'surtax_city_rate' => '7',
|
||||
'surtax_education_rate' => '3',
|
||||
'surtax_local_education_rate' => '2',
|
||||
'surtax_halved' => '1',
|
||||
'stamp_duty_rate' => '0.03',
|
||||
'stamp_duty_halved' => '1',
|
||||
'cit_effective_rate' => '5',
|
||||
'cit_threshold' => '3000000',
|
||||
'individual_halved_threshold' => '2000000',
|
||||
'assessed_income_rate' => '12',
|
||||
'income_forecast_source' => 'cost_arrangement',
|
||||
])->assertRedirect(route('business.pages.show', [
|
||||
'section' => 'trademark-management',
|
||||
'page' => 'tax-forecast-data-logic',
|
||||
]));
|
||||
|
||||
$this->assertSame('12', (string) setting('business_tax_forecast.assessed_income_rate'));
|
||||
$this->assertSame('cost_arrangement', setting('business_tax_forecast.income_forecast_source'));
|
||||
|
||||
$this->loginAs()
|
||||
->get(route('business.pages.show', ['section' => 'trademark-management', 'page' => 'tax-forecast-data-logic']))
|
||||
->assertOk()
|
||||
->assertSeeText('当前运行参数')
|
||||
->assertSeeText('规则清单')
|
||||
->assertSeeText('R12')
|
||||
->assertSeeText('小型微利企业所得税')
|
||||
->assertSeeText('12%');
|
||||
|
||||
setting([
|
||||
'business_tax_forecast.assessed_income_rate' => '10',
|
||||
'business_tax_forecast.income_forecast_source' => 'last_month_actual',
|
||||
])->save();
|
||||
}
|
||||
|
||||
public function testWorkbookDirectoryReplacesManagementSubmenus(): void
|
||||
{
|
||||
$navigation = BusinessNavigation::all();
|
||||
@ -471,7 +519,7 @@ class ManagementCrudTest extends FeatureTestCase
|
||||
$rulesPage = collect($sections['trademark-management']['pages'])->firstWhere('slug', 'rules-information');
|
||||
$this->assertNotNull($rulesPage);
|
||||
$this->assertSame(
|
||||
['subject-trademark-rules', 'store-change-rules', 'store-report-data-logic'],
|
||||
['subject-trademark-rules', 'store-change-rules', 'store-report-data-logic', 'tax-forecast-data-logic'],
|
||||
array_column($rulesPage['children'], 'slug')
|
||||
);
|
||||
$this->assertSame(
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user