From 4511f6c71abd524551b13807dc6abaf643d0a4bc Mon Sep 17 00:00:00 2001 From: qiuxiang <394405259@qq.com> Date: Tue, 20 Oct 2026 10:36:24 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=9A=E7=BB=A9=E6=98=8E=E7=BB=86=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E7=A7=8D=E8=8F=9C=E6=8A=A5=E9=94=80=E5=88=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Commands/SyncStoreReceiptOrderMatches.php | 88 +++++++++++++++---- app/Http/Controllers/Business/Pages.php | 1 + config/business_navigation.php | 1 + ...ount_to_store_receipt_order_match_rows.php | 46 ++++++++++ 4 files changed, 121 insertions(+), 15 deletions(-) create mode 100644 database/migrations/2026_10_20_020000_add_planting_reimbursement_amount_to_store_receipt_order_match_rows.php diff --git a/app/Console/Commands/SyncStoreReceiptOrderMatches.php b/app/Console/Commands/SyncStoreReceiptOrderMatches.php index 7798d086..865f4842 100644 --- a/app/Console/Commands/SyncStoreReceiptOrderMatches.php +++ b/app/Console/Commands/SyncStoreReceiptOrderMatches.php @@ -365,6 +365,7 @@ class SyncStoreReceiptOrderMatches extends Command // 种菜与备注映射先用正常连接读取,账单游标使用独立的不缓冲连接。 $plantingCores = $this->plantingOrderCores(); + $plantingReimbursementCores = $this->plantingReimbursementCores(); $remarkMap = app(AccountLedgerRemarkMap::class); $tabPlatform = $remarkMap->defaultPlatform($platform); [ @@ -376,7 +377,7 @@ class SyncStoreReceiptOrderMatches extends Command ] = $this->aggregateBills( $this->streamBillRows($billQuery->orderBy($timeColumn)), $source, $orderColumn, $timeColumn, $amountColumn, $expenseColumn, $realStoreColumn, - $plantingCores, $remarkMap, $tabPlatform, + $plantingCores, $plantingReimbursementCores, $remarkMap, $tabPlatform, ); if ($billCount === 0) { $this->warn('该账期没有符合条件的账单记录,未写入任何数据。'); @@ -385,6 +386,8 @@ class SyncStoreReceiptOrderMatches extends Command } $plantingTotal = 0.0; $plantingRows = 0; + $plantingReimbursementTotal = 0.0; + $plantingReimbursementRows = 0; $this->info('账单收款笔数:' . $paymentTotal . ',按「店铺 + 订单号」聚合后:' . count($groups) . ' 行'); $this->line(' 优惠金额合计:' . number_format($discountTotal, 2) . ',冲减金额合计:' . number_format($offsetTotal, 2) . ',退款金额合计:' . number_format($refundTotal, 2)); foreach ($groups as $group) { @@ -392,6 +395,10 @@ class SyncStoreReceiptOrderMatches extends Command $plantingRows++; $plantingTotal += (float) $group['income_amount']; } + if (! empty($group['planting_reimbursement_seen'])) { + $plantingReimbursementRows++; + $plantingReimbursementTotal += (float) $group['income_amount']; + } } $this->line(sprintf( ' 种菜金额:%d 行(识别到 %d 个种菜订单号),合计 %s', @@ -399,6 +406,11 @@ class SyncStoreReceiptOrderMatches extends Command count($plantingCores), number_format($plantingTotal, 2), )); + $this->line(sprintf( + ' 种菜报销:%d 行(按种菜明细表订单号),合计 %s', + $plantingReimbursementRows, + number_format($plantingReimbursementTotal, 2), + )); $this->line(sprintf( ' 已挂订单行:优惠 %s / 冲减 %s;未挂到订单行:%s(金额 %s,已补成「无收款账单 / 账单无单号」行)', number_format($attachedDiscount, 2), @@ -751,6 +763,7 @@ class SyncStoreReceiptOrderMatches extends Command 'offset_amount' => ! empty($group['offset_seen']) ? round((float) $group['offset_amount'], 2) : null, 'refund_amount' => ! empty($group['refund_seen']) ? round((float) $group['refund_amount'], 2) : null, 'planting_amount' => ! empty($group['planting_seen']) ? $income : null, + 'planting_reimbursement_amount' => ! empty($group['planting_reimbursement_seen']) ? $income : null, 'product_amount' => $productAmount, 'paid_amount' => $paidAmount, 'difference' => $difference, @@ -866,8 +879,7 @@ class SyncStoreReceiptOrderMatches extends Command /** - * 只回填「种菜金额」:不读账单,直接按业绩明细里已有的「订单号」判断是不是种菜订单, - * 是就把该行的收入金额写进去,不是就清空。适合给已经同步好的账期补这一列。 + * 只回填「种菜金额 / 种菜报销」:不读账单,直接按业绩明细里已有的订单号判断,是就写入收入金额。 */ private function backfillPlantingAmounts(string $reportMonth): int { @@ -877,6 +889,7 @@ class SyncStoreReceiptOrderMatches extends Command } $connection = (string) config('upstream_mirrors.connection', 'business_mirror'); $cores = $this->plantingOrderCores(); + $reimbursementCores = $this->plantingReimbursementCores(); if ($cores === []) { $this->warn('没有识别到任何种菜订单号(种菜明细表与聚水潭订单标签明细的交集为空),本列将全部为空。'); } @@ -885,30 +898,42 @@ class SyncStoreReceiptOrderMatches extends Command ->where('report_month', $reportMonth); $rows = $target()->get(['row_key', 'order_core_no', 'income_amount']); - $target()->update(['planting_amount' => null]); + $target()->update(['planting_amount' => null, 'planting_reimbursement_amount' => null]); $values = []; $total = 0.0; + $reimbursementValues = []; + $reimbursementTotal = 0.0; foreach ($rows as $row) { $core = self::orderCoreNo((string) $row->order_core_no); - if ($core === '' || ! isset($cores[$core])) { + if ($core === '') { continue; } $amount = round((float) $row->income_amount, 2); - $values[(string) $row->row_key] = $amount; - $total += $amount; + if (isset($cores[$core])) { + $values[(string) $row->row_key] = $amount; + $total += $amount; + } + if (isset($reimbursementCores[$core])) { + $reimbursementValues[(string) $row->row_key] = $amount; + $reimbursementTotal += $amount; + } } $this->updateAmountColumn($connection, $reportMonth, 'planting_amount', $values); + $this->updateAmountColumn($connection, $reportMonth, 'planting_reimbursement_amount', $reimbursementValues); $this->info(sprintf( - '已回填 %s 种菜金额:%d / %d 行,合计 %s(共识别 %d 个种菜订单号)。', + '已回填 %s:种菜金额 %d / %d 行,合计 %s(识别 %d 个种菜订单号);种菜报销 %d 行,合计 %s(识别 %d 个种菜明细订单号)。', $reportMonth, count($values), count($rows), number_format($total, 2), count($cores), + count($reimbursementValues), + number_format($reimbursementTotal, 2), + count($reimbursementCores), )); return self::SUCCESS; @@ -943,14 +968,25 @@ class SyncStoreReceiptOrderMatches extends Command $target() ->where('report_month', $reportMonth) ->where('platform', $platform) - ->update(['discount_amount' => null, 'offset_amount' => null, 'refund_amount' => null, 'planting_amount' => null]); + ->update([ + 'discount_amount' => null, + 'offset_amount' => null, + 'refund_amount' => null, + 'planting_amount' => null, + 'planting_reimbursement_amount' => null, + ]); $discounts = []; $offsets = []; $refunds = []; $plantings = []; + $plantingReimbursements = []; foreach ($groups as $group) { - if (empty($group['discount_seen']) && empty($group['offset_seen']) && empty($group['refund_seen']) && empty($group['planting_seen'])) { + if (empty($group['discount_seen']) + && empty($group['offset_seen']) + && empty($group['refund_seen']) + && empty($group['planting_seen']) + && empty($group['planting_reimbursement_seen'])) { continue; } @@ -971,12 +1007,16 @@ class SyncStoreReceiptOrderMatches extends Command if (! empty($group['planting_seen'])) { $plantings[$rowKey] = round((float) $group['income_amount'], 2); } + if (! empty($group['planting_reimbursement_seen'])) { + $plantingReimbursements[$rowKey] = round((float) $group['income_amount'], 2); + } } $this->updateAmountColumn($connection, $reportMonth, 'discount_amount', $discounts); $this->updateAmountColumn($connection, $reportMonth, 'offset_amount', $offsets); $this->updateAmountColumn($connection, $reportMonth, 'refund_amount', $refunds); $this->updateAmountColumn($connection, $reportMonth, 'planting_amount', $plantings); + $this->updateAmountColumn($connection, $reportMonth, 'planting_reimbursement_amount', $plantingReimbursements); // A 方案:本月本平台的补行(payment_count=0 的行)整体重建,保证幂等。 $target() @@ -1013,7 +1053,7 @@ class SyncStoreReceiptOrderMatches extends Command ]); $this->info(sprintf( - '已回填 %s %s:优惠 %d 行(%s)/ 冲减 %d 行(%s)/ 补「无收款账单/无单号」优惠冲减行 %d 行 / 种菜 %d 行(%s),镜像原有 %d 行,未重建业绩明细。', + '已回填 %s %s:优惠 %d 行(%s)/ 冲减 %d 行(%s)/ 补「无收款账单/无单号」优惠冲减行 %d 行 / 种菜 %d 行(%s)/ 种菜报销 %d 行(%s),镜像原有 %d 行,未重建业绩明细。', $month, $platform, count($discounts), @@ -1023,6 +1063,8 @@ class SyncStoreReceiptOrderMatches extends Command $promoRows, count($plantings), number_format(array_sum($plantings), 2), + count($plantingReimbursements), + number_format(array_sum($plantingReimbursements), 2), count($existing), )); @@ -1060,6 +1102,7 @@ class SyncStoreReceiptOrderMatches extends Command * @param iterable $bills * @param array $source * @param array $plantingCores + * @param array $plantingReimbursementCores * @return array */ private function aggregateBills( @@ -1071,6 +1114,7 @@ class SyncStoreReceiptOrderMatches extends Command string $expenseColumn, string $realStoreColumn, array $plantingCores, + array $plantingReimbursementCores, AccountLedgerRemarkMap $remarkMap, ?string $tabPlatform, ): array { @@ -1198,6 +1242,9 @@ class SyncStoreReceiptOrderMatches extends Command if (isset($plantingCores[$core])) { $groups[$key]['planting_seen'] = true; } + if (isset($plantingReimbursementCores[$core])) { + $groups[$key]['planting_reimbursement_seen'] = true; + } } // 行口径保持不变:仍然是「有收款账单(收入白名单)的店铺 + 订单号」, @@ -1258,6 +1305,7 @@ class SyncStoreReceiptOrderMatches extends Command 'refund_amount' => ! empty($group['refund_seen']) ? round((float) $group['refund_amount'], 2) : null, // 补行没有订单侧商品金额,种菜金额同样留空;列集合与顺序必须与主行完全一致,批量插入才不会错列。 'planting_amount' => null, + 'planting_reimbursement_amount' => null, 'product_amount' => null, 'paid_amount' => null, 'difference' => null, @@ -1324,6 +1372,7 @@ class SyncStoreReceiptOrderMatches extends Command private function plantingOrderCores(): array { $companyId = (int) $this->option('company'); + $cores = $this->plantingReimbursementCores(); $positiveLabels = []; foreach (app(JstOrderLabelPlantingRules::class)->map($companyId) as $label => $flag) { if ($flag === '是') { @@ -1331,9 +1380,7 @@ class SyncStoreReceiptOrderMatches extends Command } } - $cores = []; - - // 并集的第一部分:聚水潭订单标签明细里「是否种菜订单=是」的订单号 + // 聚水潭订单标签明细里「是否种菜订单=是」的订单号 // (命中种菜标签映射,或货号含信誉订单虚拟条码 X00006,与页面同一规则) foreach (app(PlantingOrderLabelLookup::class)->orderNumbers($companyId, $positiveLabels) as $orderNo) { $core = self::orderCoreNo((string) $orderNo); @@ -1342,7 +1389,18 @@ class SyncStoreReceiptOrderMatches extends Command } } - // 并集的第二部分:种菜明细表里的订单号 + return $cores; + } + + /** + * 种菜报销订单号:只取「种菜明细表」的订单号,不包含聚水潭种菜标签。 + * + * @return array + */ + private function plantingReimbursementCores(): array + { + $companyId = (int) $this->option('company'); + $cores = []; foreach (DB::table('planting_details')->where('company_id', $companyId)->distinct()->pluck('order_no') as $orderNo) { $core = self::orderCoreNo((string) $orderNo); if ($core !== '') { diff --git a/app/Http/Controllers/Business/Pages.php b/app/Http/Controllers/Business/Pages.php index 053d46a1..7b95b8b0 100644 --- a/app/Http/Controllers/Business/Pages.php +++ b/app/Http/Controllers/Business/Pages.php @@ -1809,6 +1809,7 @@ class Pages extends Controller 'offset_amount' => ['offset_amount', '冲减金额', 'money'], 'refund_amount' => ['refund_amount', '退款金额', 'money'], 'planting_amount' => ['planting_amount', '种菜金额', 'money'], + 'planting_reimbursement_amount' => ['planting_reimbursement_amount', '种菜报销', 'money'], 'product_amount' => ['product_amount', '商品金额', 'money'], 'paid_amount' => ['paid_amount', '订单实付金额', 'money'], 'difference' => ['difference', '差额', 'money'], diff --git a/config/business_navigation.php b/config/business_navigation.php index b10868d3..785e5989 100644 --- a/config/business_navigation.php +++ b/config/business_navigation.php @@ -834,6 +834,7 @@ $navigation = [ ['name' => 'offset_amount', 'label' => '冲减金额', 'kind' => 'number'], ['name' => 'refund_amount', 'label' => '退款金额', 'kind' => 'number'], ['name' => 'planting_amount', 'label' => '种菜金额', 'kind' => 'number'], + ['name' => 'planting_reimbursement_amount', 'label' => '种菜报销', 'kind' => 'number'], ['name' => 'product_amount', 'label' => '商品金额', 'kind' => 'number'], ['name' => 'paid_amount', 'label' => '订单实付/实收金额', 'kind' => 'number'], ['name' => 'difference', 'label' => '差额', 'kind' => 'number'], diff --git a/database/migrations/2026_10_20_020000_add_planting_reimbursement_amount_to_store_receipt_order_match_rows.php b/database/migrations/2026_10_20_020000_add_planting_reimbursement_amount_to_store_receipt_order_match_rows.php new file mode 100644 index 00000000..3f831f61 --- /dev/null +++ b/database/migrations/2026_10_20_020000_add_planting_reimbursement_amount_to_store_receipt_order_match_rows.php @@ -0,0 +1,46 @@ +connection()); + if (! $schema->hasTable('store_receipt_order_match_rows')) { + return; + } + + if (! $schema->hasColumn('store_receipt_order_match_rows', 'planting_reimbursement_amount')) { + $schema->table('store_receipt_order_match_rows', function (Blueprint $table): void { + $table->decimal('planting_reimbursement_amount', 20, 4) + ->nullable() + ->after('planting_amount'); + }); + } + } + + public function down(): void + { + $schema = Schema::connection($this->connection()); + if (! $schema->hasTable('store_receipt_order_match_rows') + || ! $schema->hasColumn('store_receipt_order_match_rows', 'planting_reimbursement_amount')) { + return; + } + + $schema->table('store_receipt_order_match_rows', function (Blueprint $table): void { + $table->dropColumn('planting_reimbursement_amount'); + }); + } +};