商品成本:按种菜标签更新账单侧取数 (#479)

This commit is contained in:
chenxi 2026-10-23 17:41:01 +08:00
parent f1ecd4a978
commit 53bd1dc44a
3 changed files with 88 additions and 18 deletions

View File

@ -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<int,object>
*/
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));

View File

@ -105,7 +105,7 @@
<div class="product-cost-note">
口径:以「业绩明细」的账单行为基数(发生时间 / 店铺名称 / 线上单号),按「店铺 + 线上订单号」展开订单侧商品行,
只取收入金额有数据且不为 0 的账单行,「种菜金额」有数据(&gt;0)的账单行整条排除、不参与匹配;
只取收入金额有数据且不为 0 的账单行;种菜金额非空,或标签为「种菜报销 / 种菜未报销」的账单行整条排除、不参与匹配;
线上单号直接展示业绩明细中的原始格式;匹配订单时仍使用清洗后的核心单号。
一行 = 一个发货仓库的一个商品;发货仓库取订单的聚水潭仓库名称,
仓库简称按仓库映射表用「仓储方」匹配「仓库名含主仓」的那条取简称(没有主仓行时认同名仓库,

View File

@ -0,0 +1,62 @@
<?php
namespace Tests\Feature\Console;
use App\Console\Commands\SyncStoreReceiptProductCost;
use Illuminate\Support\Facades\DB;
use ReflectionMethod;
use Tests\TestCase;
class StoreReceiptProductCostBillFilterTest extends TestCase
{
public function test_bill_filter_excludes_new_planting_labels_and_legacy_amount(): void
{
$connection = 'business_mirror';
$now = now();
$rows = [
$this->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<string,mixed> */
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,
];
}
}