diff --git a/app/Http/Controllers/Business/Pages.php b/app/Http/Controllers/Business/Pages.php
index 69296f65..21441f64 100644
--- a/app/Http/Controllers/Business/Pages.php
+++ b/app/Http/Controllers/Business/Pages.php
@@ -5,6 +5,7 @@ namespace App\Http\Controllers\Business;
use App\Models\Banking\MarketingAccountBill;
use App\Models\Banking\MarketingTaobaoAlipayBill;
use App\Models\Banking\MarketingPinduoduoBill;
+use App\Models\StoreReceivables\StoreZhongcaiReconciliation;
use App\Utilities\SidebarPermissions;
use Illuminate\Routing\Controller;
@@ -70,11 +71,14 @@ class Pages extends Controller
if ($tabs->isEmpty()) {
abort_if($tab !== null, 404);
+ $content = $this->resolveContent($page, null);
+
return view('business.pages.show', [
'section' => $sectionConfig,
'page' => $pageConfig,
'tabs' => $tabs,
'activeTab' => null,
+ 'content' => $content,
]);
}
@@ -100,14 +104,28 @@ class Pages extends Controller
/**
* 根据页面和标签加载对应的业务内容数据
*/
- protected function resolveContent(string $page, string $tab): ?array
+ protected function resolveContent(string $page, ?string $tab): ?array
{
return match ($page) {
'marketing-account-bills' => $this->contentForMarketingAccountBills($tab),
+ 'zhongcai-reconciliation' => $this->contentForZhongcaiReconciliation(),
default => null,
};
}
+ /**
+ * 种菜应收对账 - 加载对账数据(21字段)
+ */
+ protected function contentForZhongcaiReconciliation(): ?array
+ {
+ return [
+ 'view' => 'store_receivables.zhongcai_reconciliations.partials.embedded-table',
+ 'records' => StoreZhongcaiReconciliation::orderBy('bill_date', 'desc')
+ ->orderBy('id', 'desc')
+ ->paginate(setting('default.list_limit', 25)),
+ ];
+ }
+
/**
* 营销账户账单 - 加载指定平台的数据
* 淘系支付宝、拼多多使用独立的流水账单表,其他平台使用通用汇总账单表
diff --git a/app/Http/Controllers/StoreReceivables/StoreZhongcaiReconciliations.php b/app/Http/Controllers/StoreReceivables/StoreZhongcaiReconciliations.php
new file mode 100644
index 00000000..33d7f875
--- /dev/null
+++ b/app/Http/Controllers/StoreReceivables/StoreZhongcaiReconciliations.php
@@ -0,0 +1,200 @@
+middleware('permission:read-banking-accounts')->only('index');
+ $this->middleware('permission:create-banking-accounts')->only('create', 'store', 'import', 'template');
+ $this->middleware('permission:update-banking-accounts')->only('edit', 'update', 'export');
+ $this->middleware('permission:delete-banking-accounts')->only('destroy');
+ }
+
+ public function index()
+ {
+ return view('store_receivables.zhongcai_reconciliations.index', [
+ 'records' => StoreZhongcaiReconciliation::orderBy('bill_date', 'desc')
+ ->orderBy('id', 'desc')
+ ->paginate(setting('default.list_limit', 25)),
+ ]);
+ }
+
+ public function create()
+ {
+ return view('store_receivables.zhongcai_reconciliations.form', [
+ 'record' => new StoreZhongcaiReconciliation([
+ 'company_id' => company_id(),
+ 'bill_date' => now()->toDateString(),
+ ]),
+ ]);
+ }
+
+ public function template()
+ {
+ return response()->streamDownload(function () {
+ $csv = fopen('php://output', 'w');
+ fputs($csv, chr(0xEF) . chr(0xBB) . chr(0xBF));
+ fputcsv($csv, self::CSV_HEADERS);
+ fclose($csv);
+ }, 'zhongcai_reconciliation_template.csv', [
+ 'Content-Type' => 'text/csv; charset=UTF-8',
+ ]);
+ }
+
+ public function export(HttpRequest $request)
+ {
+ $ids = $request->validate([
+ 'ids' => ['nullable', 'array', 'max:5000'],
+ 'ids.*' => ['integer'],
+ ])['ids'] ?? [];
+
+ $query = StoreZhongcaiReconciliation::oldest('id');
+ if (!empty($ids)) {
+ $query->whereKey($ids);
+ }
+ $records = $query->get();
+
+ return response()->streamDownload(function () use ($records) {
+ $csv = fopen('php://output', 'w');
+ fputs($csv, chr(0xEF) . chr(0xBB) . chr(0xBF));
+ fputcsv($csv, self::CSV_HEADERS);
+
+ foreach ($records as $r) {
+ fputcsv($csv, [
+ $r->bill_date->toDateString(),
+ $r->planting_order_no,
+ $r->finance_selling_price,
+ $r->finance_freight,
+ $r->finance_commission,
+ $r->jingxi_price_diff,
+ $r->deduction,
+ $r->finance_empty_package_fee,
+ $r->finance_payment_amount,
+ $r->correct_store,
+ $r->person_in_charge,
+ $r->creator,
+ $r->profit_center,
+ $r->responsible_platform,
+ $r->external_id,
+ $r->store,
+ $r->finance_notes,
+ $r->alipay_order,
+ $r->bill_item,
+ $r->income_amount,
+ $r->difference_data,
+ ]);
+ }
+ fclose($csv);
+ }, 'zhongcai_reconciliation_export.csv', [
+ 'Content-Type' => 'text/csv; charset=UTF-8',
+ ]);
+ }
+
+ public function import(HttpRequest $request): RedirectResponse
+ {
+ $validated = $request->validate([
+ 'import' => ['required', 'file', 'mimes:xlsx,xls,csv', 'max:5120'],
+ ], [
+ 'import.required' => trans('store_zhongcai_reconciliations.import_required'),
+ 'import.mimes' => trans('store_zhongcai_reconciliations.import_file_type'),
+ 'import.max' => trans('store_zhongcai_reconciliations.import_file_size'),
+ ]);
+
+ $handle = fopen($validated['import']->getRealPath(), 'r');
+ $bom = fread($handle, 3);
+ if ($bom !== chr(0xEF) . chr(0xBB) . chr(0xBF)) {
+ rewind($handle);
+ }
+ fgetcsv($handle); // skip header
+
+ $count = 0;
+ $companyId = company_id();
+ $userId = user()->id;
+
+ while (($row = fgetcsv($handle)) !== false) {
+ if (empty($row[0]) && empty($row[1])) {
+ continue;
+ }
+
+ StoreZhongcaiReconciliation::create([
+ 'company_id' => $companyId,
+ 'bill_date' => $row[0] ?? now()->toDateString(),
+ 'planting_order_no' => $row[1] ?? '',
+ 'finance_selling_price' => (float) ($row[2] ?? 0),
+ 'finance_freight' => (float) ($row[3] ?? 0),
+ 'finance_commission' => (float) ($row[4] ?? 0),
+ 'jingxi_price_diff' => (float) ($row[5] ?? 0),
+ 'deduction' => (float) ($row[6] ?? 0),
+ 'finance_empty_package_fee' => (float) ($row[7] ?? 0),
+ 'finance_payment_amount' => (float) ($row[8] ?? 0),
+ 'correct_store' => $row[9] ?? null,
+ 'person_in_charge' => $row[10] ?? null,
+ 'creator' => $row[11] ?? null,
+ 'profit_center' => $row[12] ?? null,
+ 'responsible_platform' => $row[13] ?? null,
+ 'external_id' => $row[14] ?? null,
+ 'store' => $row[15] ?? null,
+ 'finance_notes' => $row[16] ?? null,
+ 'alipay_order' => $row[17] ?? null,
+ 'bill_item' => $row[18] ?? null,
+ 'income_amount' => (float) ($row[19] ?? 0),
+ 'difference_data' => $row[20] ?? null,
+ 'created_from' => 'core::ui',
+ 'created_by' => $userId,
+ ]);
+ $count++;
+ }
+ fclose($handle);
+
+ flash(trans('store_zhongcai_reconciliations.import_success', ['count' => $count]))->success();
+ return redirect()->route('zhongcai-reconciliations.index');
+ }
+
+ public function store(Request $request): RedirectResponse
+ {
+ StoreZhongcaiReconciliation::create(array_merge($request->validated(), [
+ 'company_id' => company_id(),
+ 'created_from' => 'core::ui',
+ 'created_by' => user()->id,
+ ]));
+
+ flash(trans('messages.success.created', ['type' => trans('store_zhongcai_reconciliations.title')]))->success();
+ return redirect()->route('zhongcai-reconciliations.index');
+ }
+
+ public function edit(StoreZhongcaiReconciliation $zhongcaiReconciliation)
+ {
+ return view('store_receivables.zhongcai_reconciliations.form', [
+ 'record' => $zhongcaiReconciliation,
+ ]);
+ }
+
+ public function update(Request $request, StoreZhongcaiReconciliation $zhongcaiReconciliation): RedirectResponse
+ {
+ $zhongcaiReconciliation->update($request->validated());
+ flash(trans('messages.success.updated', ['type' => trans('store_zhongcai_reconciliations.title')]))->success();
+ return redirect()->route('zhongcai-reconciliations.index');
+ }
+
+ public function destroy(StoreZhongcaiReconciliation $zhongcaiReconciliation): RedirectResponse
+ {
+ $zhongcaiReconciliation->delete();
+ flash(trans('messages.success.deleted', ['type' => trans('store_zhongcai_reconciliations.title')]))->success();
+ return redirect()->route('zhongcai-reconciliations.index');
+ }
+}
diff --git a/app/Http/Requests/StoreReceivables/StoreZhongcaiReconciliation.php b/app/Http/Requests/StoreReceivables/StoreZhongcaiReconciliation.php
new file mode 100644
index 00000000..be2c2cf6
--- /dev/null
+++ b/app/Http/Requests/StoreReceivables/StoreZhongcaiReconciliation.php
@@ -0,0 +1,40 @@
+ ['required', 'date'],
+ 'planting_order_no' => ['required', 'string', 'max:100'],
+ 'finance_selling_price' => ['nullable', 'numeric'],
+ 'finance_freight' => ['nullable', 'numeric'],
+ 'finance_commission' => ['nullable', 'numeric'],
+ 'jingxi_price_diff' => ['nullable', 'numeric'],
+ 'deduction' => ['nullable', 'numeric'],
+ 'finance_empty_package_fee' => ['nullable', 'numeric'],
+ 'finance_payment_amount' => ['nullable', 'numeric'],
+ 'correct_store' => ['nullable', 'string', 'max:191'],
+ 'person_in_charge' => ['nullable', 'string', 'max:100'],
+ 'creator' => ['nullable', 'string', 'max:100'],
+ 'profit_center' => ['nullable', 'string', 'max:100'],
+ 'responsible_platform' => ['nullable', 'string', 'max:100'],
+ 'external_id' => ['nullable', 'string', 'max:100'],
+ 'store' => ['nullable', 'string', 'max:191'],
+ 'finance_notes' => ['nullable', 'string', 'max:1000'],
+ 'alipay_order' => ['nullable', 'string', 'max:100'],
+ 'bill_item' => ['nullable', 'string', 'max:100'],
+ 'income_amount' => ['nullable', 'numeric'],
+ 'difference_data' => ['nullable', 'string', 'max:1000'],
+ ];
+ }
+}
diff --git a/app/Models/StoreReceivables/StoreZhongcaiReconciliation.php b/app/Models/StoreReceivables/StoreZhongcaiReconciliation.php
new file mode 100644
index 00000000..b5c4a8f7
--- /dev/null
+++ b/app/Models/StoreReceivables/StoreZhongcaiReconciliation.php
@@ -0,0 +1,50 @@
+ 'date',
+ 'finance_selling_price' => 'double',
+ 'finance_freight' => 'double',
+ 'finance_commission' => 'double',
+ 'jingxi_price_diff' => 'double',
+ 'deduction' => 'double',
+ 'finance_empty_package_fee' => 'double',
+ 'finance_payment_amount' => 'double',
+ 'income_amount' => 'double',
+ 'deleted_at' => 'datetime',
+ ];
+}
diff --git a/database/migrations/2026_07_21_120000_create_store_zhongcai_reconciliations_table.php b/database/migrations/2026_07_21_120000_create_store_zhongcai_reconciliations_table.php
new file mode 100644
index 00000000..10da5e9b
--- /dev/null
+++ b/database/migrations/2026_07_21_120000_create_store_zhongcai_reconciliations_table.php
@@ -0,0 +1,50 @@
+increments('id');
+ $table->unsignedInteger('company_id');
+ $table->date('bill_date')->comment('日期');
+ $table->string('planting_order_no', 100)->comment('种菜订单编号');
+ $table->decimal('finance_selling_price', 15, 2)->default(0)->comment('财务售价');
+ $table->decimal('finance_freight', 15, 2)->default(0)->comment('财务运费');
+ $table->decimal('finance_commission', 15, 2)->default(0)->comment('财务佣金');
+ $table->decimal('jingxi_price_diff', 15, 2)->default(0)->comment('京喜差价');
+ $table->decimal('deduction', 15, 2)->default(0)->comment('扣款');
+ $table->decimal('finance_empty_package_fee', 15, 2)->default(0)->comment('财务空包费');
+ $table->decimal('finance_payment_amount', 15, 2)->default(0)->comment('财务打款金额');
+ $table->string('correct_store', 191)->nullable()->comment('正确店铺');
+ $table->string('person_in_charge', 100)->nullable()->comment('负责人');
+ $table->string('creator', 100)->nullable()->comment('创建人');
+ $table->string('profit_center', 100)->nullable()->comment('所属利润中心');
+ $table->string('responsible_platform', 100)->nullable()->comment('负责平台');
+ $table->string('external_id', 100)->nullable()->comment('ID');
+ $table->string('store', 191)->nullable()->comment('店铺');
+ $table->text('finance_notes')->nullable()->comment('财务备注');
+ $table->string('alipay_order', 100)->nullable()->comment('支付宝订单');
+ $table->string('bill_item', 100)->nullable()->comment('账单项目');
+ $table->decimal('income_amount', 15, 2)->default(0)->comment('收入金额');
+ $table->text('difference_data')->nullable()->comment('差异数据');
+ $table->string('created_from', 50)->nullable();
+ $table->unsignedInteger('created_by')->nullable();
+ $table->timestamps();
+ $table->softDeletes();
+
+ $table->index(['company_id', 'bill_date']);
+ $table->index('planting_order_no');
+ $table->index('store');
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::dropIfExists('store_zhongcai_reconciliations');
+ }
+};
diff --git a/resources/lang/zh-CN/store_zhongcai_reconciliations.php b/resources/lang/zh-CN/store_zhongcai_reconciliations.php
new file mode 100644
index 00000000..2d5843f8
--- /dev/null
+++ b/resources/lang/zh-CN/store_zhongcai_reconciliations.php
@@ -0,0 +1,51 @@
+ '种菜应收对账',
+
+ // 表格列标题(对应Excel表1,21个字段)
+ 'bill_date' => '日期',
+ 'planting_order_no' => '种菜订单编号',
+ 'finance_selling_price' => '财务售价',
+ 'finance_freight' => '财务运费',
+ 'finance_commission' => '财务佣金',
+ 'jingxi_price_diff' => '京喜差价',
+ 'deduction' => '扣款',
+ 'finance_empty_package_fee' => '财务空包费',
+ 'finance_payment_amount' => '财务打款金额',
+ 'correct_store' => '正确店铺',
+ 'person_in_charge' => '负责人',
+ 'creator' => '创建人',
+ 'profit_center' => '所属利润中心',
+ 'responsible_platform' => '负责平台',
+ 'external_id' => 'ID',
+ 'store' => '店铺',
+ 'finance_notes' => '财务备注',
+ 'alipay_order' => '支付宝订单',
+ 'bill_item' => '账单项目',
+ 'income_amount' => '收入金额',
+ 'difference_data' => '差异数据',
+
+ // 操作
+ 'add_record' => '新增对账记录',
+ 'edit_record' => '编辑对账记录',
+ 'bulk_upload' => '批量导入',
+ 'bulk_download' => '批量导出',
+ 'download_template' => '下载导入模板',
+ 'choose_file' => '选择文件',
+ 'import_success' => '成功导入 :count 条对账记录',
+ 'import_required' => '请选择要导入的文件',
+ 'import_file_type' => '仅支持 .xlsx、.xls、.csv 文件',
+ 'import_file_size' => '文件大小不能超过 5MB',
+ 'select_all' => '全选',
+ 'select_record' => '选择记录',
+ 'no_records' => '暂无对账数据',
+
+ // 表单分组
+ 'section_basic' => '基本信息',
+ 'section_finance' => '财务数据(元)',
+ 'section_store' => '店铺与人员',
+ 'section_payment' => '收款与差异',
+
+];
diff --git a/resources/views/store_receivables/zhongcai_reconciliations/form.blade.php b/resources/views/store_receivables/zhongcai_reconciliations/form.blade.php
new file mode 100644
index 00000000..95ff93db
--- /dev/null
+++ b/resources/views/store_receivables/zhongcai_reconciliations/form.blade.php
@@ -0,0 +1,157 @@
+@php $isEdit = $record->exists; @endphp
+
+
{{ $message }}
+ @endforeach +| + isEmpty()) disabled @endif + x-on:change="selected = $event.target.checked ? @js($pageIds) : []" + x-bind:checked="selected.length === {{ $pageIds->count() }} && selected.length > 0" + class="h-4 w-4 rounded border-gray-300 text-green" /> + | +{{ trans('store_zhongcai_reconciliations.bill_date') }} | +{{ trans('store_zhongcai_reconciliations.planting_order_no') }} | +{{ trans('store_zhongcai_reconciliations.finance_selling_price') }} | +{{ trans('store_zhongcai_reconciliations.finance_freight') }} | +{{ trans('store_zhongcai_reconciliations.finance_commission') }} | +{{ trans('store_zhongcai_reconciliations.jingxi_price_diff') }} | +{{ trans('store_zhongcai_reconciliations.deduction') }} | +{{ trans('store_zhongcai_reconciliations.finance_empty_package_fee') }} | +{{ trans('store_zhongcai_reconciliations.finance_payment_amount') }} | +{{ trans('store_zhongcai_reconciliations.correct_store') }} | +{{ trans('store_zhongcai_reconciliations.person_in_charge') }} | +{{ trans('store_zhongcai_reconciliations.creator') }} | +{{ trans('store_zhongcai_reconciliations.profit_center') }} | +{{ trans('store_zhongcai_reconciliations.responsible_platform') }} | +{{ trans('store_zhongcai_reconciliations.external_id') }} | +{{ trans('store_zhongcai_reconciliations.store') }} | +{{ trans('store_zhongcai_reconciliations.finance_notes') }} | +{{ trans('store_zhongcai_reconciliations.alipay_order') }} | +{{ trans('store_zhongcai_reconciliations.bill_item') }} | +{{ trans('store_zhongcai_reconciliations.income_amount') }} | +{{ trans('store_zhongcai_reconciliations.difference_data') }} | +{{ trans('general.actions') }} | +
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| + {{ trans('store_zhongcai_reconciliations.no_records') }} + | +||||||||||||||||||||||
| + + | +{{ $r->bill_date->format('Y-m-d') }} | +{{ $r->planting_order_no }} | +{{ number_format($r->finance_selling_price, 2) }} | +{{ number_format($r->finance_freight, 2) }} | +{{ number_format($r->finance_commission, 2) }} | +{{ number_format($r->jingxi_price_diff, 2) }} | +{{ number_format($r->deduction, 2) }} | +{{ number_format($r->finance_empty_package_fee, 2) }} | +{{ number_format($r->finance_payment_amount, 2) }} | +{{ $r->correct_store }} | +{{ $r->person_in_charge }} | +{{ $r->creator }} | +{{ $r->profit_center }} | +{{ $r->responsible_platform }} | +{{ $r->external_id }} | +{{ $r->store }} | +{{ \Illuminate\Support\Str::limit($r->finance_notes, 15) }} | +{{ $r->alipay_order }} | +{{ $r->bill_item }} | +{{ number_format($r->income_amount, 2) }} | +{{ \Illuminate\Support\Str::limit($r->difference_data, 15) }} | ++ @can('update-banking-accounts') + {{ trans('general.edit') }} + @endcan + @can('delete-banking-accounts') + + @endcan + | +