limeng ea479733ab feat(种菜数据汇总): 接入聚工单种菜报销,新增种菜损耗扣款页面
- 种菜报销:复用已登记镜像 jiansun.dbo.jst_workOrderInfo,工单类型“种菜报销”、状态已完成,
  按完成时间归属报销月份(M 月 = [M-02 00:00, M+1-02 00:00)),自 2026-09-02 起;同一工单只取最新一版
- 种菜损耗:当月种菜报销的线上订单号匹配上月+本月发货数据(jst_oms_shipping)交易单号;
  任务备注/卖家备注含“拍A发B/拍A发A”的不扣款(单独导出发运营),只发信誉订单虚拟条码/透明挂钩的不扣款,
  其余按发货条码在普通商品资料(ops_jst_sku)的成本价 × 数量 × 1.2 扣款,成本价缺失单独提示
- 两页均支持筛选、列设置、CSV 导出;按月缓存 10 分钟
- 测试:窗口口径、免扣字样、虚拟条码/挂钩、成本价取数与缺失、页面筛选和导出

涉及:金额计算展示(只读);不涉及数据库迁移、权限模型和上游写入。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VANWYsNUbu2piMtCKymoyd
2026-10-19 14:37:09 +08:00

70 lines
9.9 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

@php
use Modules\EcommerceFinance\View\PlantingLossPageData as PageData;
use Modules\EcommerceFinance\Services\PlantingLossCalculator as Calculator;
$columns = PageData::COLUMNS;
$syncLabels = ['ready' => '工单镜像已同步', 'failed' => '工单镜像最近同步失败,展示上次成功数据', 'initializing' => '工单镜像首次同步中', 'initial_failed' => '工单镜像首次同步失败', 'unavailable' => '工单镜像未建立'];
$shippingLabels = ['succeeded' => '发货数据已同步', 'partial' => '发货数据部分待核对', 'running' => '发货数据同步中', 'failed' => '发货数据最近同步失败', 'unavailable' => '发货数据不可用'];
$resultClasses = [Calculator::RESULT_DEDUCT => 'is-deduct', Calculator::RESULT_EXEMPT => 'is-exempt', Calculator::RESULT_FREE_GOODS => 'is-exempt'];
$shippingLabel = Calculator::shippingLabel($plantingFilters['month']);
$baseQuery = array_filter(\Illuminate\Support\Arr::except($plantingFilters, ['result']), static fn ($v) => $v !== '');
$resultUrl = static fn (string $result) => request()->url() . '?' . http_build_query($baseQuery + ['result' => $result]);
$exportQuery = array_filter($plantingFilters, static fn ($v) => $v !== '');
$money = static fn (int $cents) => number_format($cents / 100, 2);
@endphp
<div class="pw-page" data-preference-key="planting-loss:{{ company_id() }}:{{ optional(user())->id }}">
<div class="pw-heading">
<p>{{ $plantingFilters['month'] }} 月种菜报销明细(完成时间 {{ substr($plantingWindow['from'], 0, 10) }} 至 {{ \Illuminate\Support\Carbon::parse($plantingWindow['to'])->subDay()->format('Y-m-d') }})的线上订单号,匹配 {{ $shippingLabel }} 发货数据(发货时间 {{ substr($plantingShippingWindow['from'], 0, 10) }} 至 {{ \Illuminate\Support\Carbon::parse($plantingShippingWindow['to'])->subDay()->format('Y-m-d') }})中的交易单号。匹配到的单中,任务备注或卖家备注写了“拍A发B / 拍A发A”的、以及只发了信誉订单虚拟条码/透明挂钩的不扣款,其余按发货商品成本价×1.2 扣款。</p>
<div class="pw-tags">
<span class="pw-tag {{ $plantingSync['code'] === 'ready' ? '' : 'warn' }}">{{ $syncLabels[$plantingSync['code']] ?? $plantingSync['code'] }}@if($plantingSync['last_succeeded_at']) · {{ \Illuminate\Support\Str::of($plantingSync['last_succeeded_at'])->substr(0, 16) }}@endif</span>
<span class="pw-tag {{ ($plantingShippingSync['code'] ?? '') === 'succeeded' ? '' : 'warn' }}">{{ $shippingLabels[$plantingShippingSync['code'] ?? 'unavailable'] ?? $plantingShippingSync['code'] }}@if($plantingShippingSync['last_succeeded_at'] ?? null) · {{ \Illuminate\Support\Str::of($plantingShippingSync['last_succeeded_at'])->substr(0, 16) }}@endif</span>
</div>
</div>
<form method="get" class="pw-toolbar">
<label>报销月份<select name="month">@foreach($plantingMonths as $month)<option value="{{ $month }}" @selected($plantingFilters['month'] === $month)>{{ $month }}</option>@endforeach</select></label>
<label>损耗判定<select name="result">
@foreach(Calculator::RESULTS as $result)<option value="{{ $result }}" @selected($plantingFilters['result'] === $result)>{{ $result }}</option>@endforeach
<option value="all" @selected($plantingFilters['result'] === 'all')>全部</option>
</select></label>
@foreach(PageData::TEXT_FILTERS as $key => $label)
<label>{{ $label }}<input type="text" name="{{ $key }}" value="{{ $plantingFilters[$key] }}" maxlength="200" placeholder="筛选{{ $label }}"></label>
@endforeach
<button type="submit" class="pw-primary">查询</button>
<a class="pw-button" href="{{ request()->url() }}">重置</a>
<a class="pw-button" data-pw-export href="{{ route('business.planting-data.planting-loss.export', $exportQuery) }}">导出当前筛选</a>
<a class="pw-button" data-pw-export href="{{ route('business.planting-data.planting-loss.export', ['month' => $plantingFilters['month'], 'result' => Calculator::RESULT_EXEMPT]) }}">导出拍A发B(发运营)</a>
<details class="pw-column-settings"><summary class="pw-button">列设置</summary><div class="pw-column-menu"><p>勾选显示,拖动调整顺序</p><div data-pw-column-list>@foreach($columns as $key => $label)<label draggable="true" data-column-key="{{ $key }}"><input type="checkbox" value="{{ $key }}" checked> {{ $label }}</label>@endforeach</div><button type="button" class="pw-button" data-pw-reset>恢复默认</button><span data-pw-column-message role="status"></span></div></details>
</form>
@php $deduct = $plantingSummary[Calculator::RESULT_DEDUCT]; $exempt = $plantingSummary[Calculator::RESULT_EXEMPT]; $free = $plantingSummary[Calculator::RESULT_FREE_GOODS]; $unmatched = $plantingSummary[Calculator::RESULT_UNMATCHED]; @endphp
<div class="pw-metrics">
<div><span>当月种菜报销</span><strong>{{ number_format($plantingSummary['total']) }}</strong><small>匹配到发货 {{ number_format($deduct['count'] + $exempt['count'] + $free['count']) }} 单</small></div>
<a href="{{ $resultUrl(Calculator::RESULT_DEDUCT) }}" class="is-key {{ $plantingFilters['result'] === Calculator::RESULT_DEDUCT ? 'is-active' : '' }}"><span>扣款金额(元)</span><strong>{{ $money($deduct['deduction_amount']) }}</strong><small>扣款 {{ number_format($deduct['count']) }} 单@if($deduct['cost_missing'] > 0) · <b class="pw-warn-text">{{ number_format($deduct['cost_missing']) }} 单成本价缺失</b>@endif</small></a>
<a href="{{ $resultUrl(Calculator::RESULT_EXEMPT) }}" class="{{ $plantingFilters['result'] === Calculator::RESULT_EXEMPT ? 'is-active' : '' }}"><span>不扣款(拍A发B/拍A发A)</span><strong>{{ number_format($exempt['count']) }}</strong><small>需发运营 · 最终报销 {{ $money($exempt['reimbursement_amount']) }}</small></a>
<a href="{{ $resultUrl(Calculator::RESULT_FREE_GOODS) }}" class="{{ $plantingFilters['result'] === Calculator::RESULT_FREE_GOODS ? 'is-active' : '' }}"><span>不扣款(虚拟条码/透明挂钩)</span><strong>{{ number_format($free['count']) }}</strong><small>最终报销 {{ $money($free['reimbursement_amount']) }}</small></a>
<a href="{{ $resultUrl(Calculator::RESULT_UNMATCHED) }}" class="{{ $plantingFilters['result'] === Calculator::RESULT_UNMATCHED ? 'is-active' : '' }}"><span>未匹配发货(不计入)</span><strong>{{ number_format($unmatched['count']) }}</strong><small>最终报销 {{ $money($unmatched['reimbursement_amount']) }}</small></a>
</div>
<details class="pw-rules"><summary>计算口径</summary><p>① 取该月种菜报销明细:工单类型“种菜报销”、状态“已完成”,完成时间 M 月 2 日 00:00 至次月 1 日 23:59:59(与「种菜报销」页同一口径)。② 用线上订单号(交易单号)精确匹配「快递应付 → 发货数据」中发货时间在上月 1 日至本月末的交易单号,匹配到的保留,同一单多个包裹/商品合并为一行。③ 匹配到的单中,任务备注或卖家备注写了“拍A发B”或“拍A发A”(不区分全半角、大小写和空格)的不做扣款,单独列出发给运营。④ 实际只发了“信誉订单虚拟条码”“透明挂钩”这类商品的不扣款;同一单里这类商品也不计入扣款。⑤ 其余匹配到的为扣款项:扣款金额 = Σ 发货条形码(商品编码)在「普通商品资料」中的成本价 × 发货数量 × 1.2(成本价 + 成本价×20%);普通商品资料里没有成本价(空或 0)的商品标为“成本价缺失”,不计入扣款金额,需补齐成本价后自动重算。未匹配到发货的单不计入种菜损耗,仅供核对。</p></details>
@if(($plantingShippingSync['code'] ?? '') !== 'succeeded' && ! $plantingError)<div class="pw-warn">发货数据当前状态为“{{ $shippingLabels[$plantingShippingSync['code'] ?? 'unavailable'] ?? $plantingShippingSync['code'] }}”{{ ($plantingShippingSync['warning'] ?? '') !== '' ? ':' . $plantingShippingSync['warning'] : '' }},匹配结果可能不完整。</div>@endif
@if($plantingError)<div class="pw-error" role="alert">{{ $plantingError }}</div>@endif
<section class="pw-panel" aria-label="种菜损耗明细">
<div class="pw-table-scroll"><table><thead><tr>
@foreach($columns as $key => $label)<th data-column-key="{{ $key }}">{{ $label }}</th>@endforeach
</tr></thead><tbody>
@forelse($plantingRows as $row)
<tr>@foreach($columns as $key => $label)
@if(in_array($key, PageData::MONEY_COLUMNS, true))
<td class="pw-money">{{ $row[$key] === null ? '—' : number_format((float) $row[$key], 2) }}</td>
@elseif($key === 'loss_result')
<td><span class="pw-result {{ $resultClasses[$row['loss_result']] ?? '' }}">{{ $row['loss_result'] }}</span></td>
@else
<td class="{{ str_contains((string) ($row[$key] ?? ''), "\n") ? 'pw-lines' : '' }}">{{ ($row[$key] ?? '') !== '' ? $row[$key] : '—' }}</td>
@endif
@endforeach</tr>
@empty
<tr><td colspan="{{ count($columns) }}" class="pw-empty">{{ $plantingError ?: '当前筛选条件下没有种菜损耗明细。' }}</td></tr>
@endforelse
</tbody></table></div>
<div class="pw-footer">共 {{ number_format($plantingRows->total()) }} 条,每页 {{ PageData::PER_PAGE }} 条。@if($plantingFilters['result'] !== 'all' && $plantingRows->total() > 0)当前筛选:扣款金额 {{ $money($plantingSummary['filtered']['deduction_amount']) }} 元,最终报销 {{ $money($plantingSummary['filtered']['reimbursement_amount']) }} 元。@endif{{ $plantingRows->links() }}</div>
</section>
</div>
@include('business.pages.billing-transactions.partials.planting-work-order-assets')