diff --git a/app/Console/Commands/SyncStoreReceiptProductCost.php b/app/Console/Commands/SyncStoreReceiptProductCost.php index 9a2dd7d5..b855944f 100644 --- a/app/Console/Commands/SyncStoreReceiptProductCost.php +++ b/app/Console/Commands/SyncStoreReceiptProductCost.php @@ -141,23 +141,9 @@ class SyncStoreReceiptProductCost extends Command $mirror = DB::connection($connection); // 账单侧取数:业绩明细里本月(可按平台限定)的账单行,一行 = 一个店铺的一个订单。 - // 只取「收入金额有数据且不为 0」的账单行:收入为 0 的行是补出来的优惠/冲减/无收款账单行, - // 没有对应的收款商品,不参与商品成本。 - // 「种菜金额」有数据(>0)的账单行整条排除,不参与匹配。 - $billQuery = $mirror->table(self::SOURCE_TABLE) - ->where('report_month', $reportMonth) - ->whereNotNull('income_amount') - ->where('income_amount', '!=', 0) - ->where(static function ($query): void { - $query->whereNull('planting_amount') - ->orWhere('planting_amount', '<=', 0); - }); - if ($platform !== '') { - $billQuery->where('platform', $platform); - } - $bills = $billQuery->get([ - 'platform', 'store_name', 'order_core_no', 'online_order_no', 'occurred_date', - ]); + // 收入为 0 的补行没有对应的收款商品,不参与商品成本。 + // 种菜金额非空,或新标签为「种菜报销 / 种菜未报销」的账单行整条排除。 + $bills = $this->billRows($mirror, $reportMonth, $platform); $this->info(sprintf( '账期 %s%s:业绩明细账单行 %d 条', $month, @@ -490,6 +476,28 @@ class SyncStoreReceiptProductCost extends Command return self::SUCCESS; } + /** + * @return \Illuminate\Support\Collection + */ + private function billRows($mirror, string $reportMonth, string $platform) + { + $query = $mirror->table(self::SOURCE_TABLE) + ->where('report_month', $reportMonth) + ->whereNotNull('income_amount') + ->where('income_amount', '!=', 0) + // planting_amount 是旧字段兼容:任何非空值都表示该订单命中了种菜单。 + ->whereNull('planting_amount') + // 新版业绩明细把两类种菜状态合并为标签;任一种菜标签都不参与商品成本。 + ->whereNull('planting_label'); + if ($platform !== '') { + $query->where('platform', $platform); + } + + return $query->get([ + 'platform', 'store_name', 'order_core_no', 'online_order_no', 'occurred_date', + ]); + } + private function pendingCodeType(string $code): string { $code = mb_strtoupper(trim($code)); diff --git a/resources/views/business/pages/financial-operations/product-costs.blade.php b/resources/views/business/pages/financial-operations/product-costs.blade.php index 03da87fa..97bb5d63 100644 --- a/resources/views/business/pages/financial-operations/product-costs.blade.php +++ b/resources/views/business/pages/financial-operations/product-costs.blade.php @@ -105,7 +105,7 @@
口径:以「业绩明细」的账单行为基数(发生时间 / 店铺名称 / 线上单号),按「店铺 + 线上订单号」展开订单侧商品行, - 只取收入金额有数据且不为 0 的账单行,「种菜金额」有数据(>0)的账单行整条排除、不参与匹配; + 只取收入金额有数据且不为 0 的账单行;种菜金额非空,或标签为「种菜报销 / 种菜未报销」的账单行整条排除、不参与匹配; 线上单号直接展示业绩明细中的原始格式;匹配订单时仍使用清洗后的核心单号。 一行 = 一个发货仓库的一个商品;发货仓库取订单的聚水潭仓库名称, 仓库简称按仓库映射表用「仓储方」匹配「仓库名含主仓」的那条取简称(没有主仓行时认同名仓库, diff --git a/tests/Feature/Console/StoreReceiptProductCostBillFilterTest.php b/tests/Feature/Console/StoreReceiptProductCostBillFilterTest.php new file mode 100644 index 00000000..29aca7d1 --- /dev/null +++ b/tests/Feature/Console/StoreReceiptProductCostBillFilterTest.php @@ -0,0 +1,62 @@ +row('normal', 10, null, null, $now), + $this->row('reimbursed', 20, null, '种菜报销', $now), + $this->row('unreimbursed', 30, null, '种菜未报销', $now), + $this->row('legacy', 40, 5, null, $now), + $this->row('legacy-zero', 50, 0, null, $now), + $this->row('zero-income', 0, null, null, $now), + ]; + DB::connection($connection)->table('store_receipt_order_match_rows')->insert($rows); + + $command = new SyncStoreReceiptProductCost(); + $method = new ReflectionMethod($command, 'billRows'); + $method->setAccessible(true); + $result = $method->invoke( + $command, + DB::connection($connection), + '2026-09-01', + '淘系支付宝', + ); + + $this->assertCount(1, $result); + $this->assertSame('NORMAL-1', $result->first()->online_order_no); + } + + /** @return array */ + private function row( + string $suffix, + float $income, + ?float $plantingAmount, + ?string $plantingLabel, + mixed $now, + ): array { + return [ + 'report_month' => '2026-09-01', + 'platform' => '淘系支付宝', + 'row_key' => str_pad($suffix, 40, 'x'), + 'occurred_date' => '2026-09-15', + 'store_name' => '测试店铺', + 'online_order_no' => strtoupper($suffix) . '-1', + 'order_core_no' => strtoupper($suffix) . '-1', + 'income_amount' => $income, + 'planting_amount' => $plantingAmount, + 'planting_label' => $plantingLabel, + 'imported_at' => $now, + ]; + } +}