- 种菜报销:复用已登记镜像 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
106 lines
4.5 KiB
PHP
106 lines
4.5 KiB
PHP
<?php
|
|
|
|
namespace Modules\EcommerceFinance\View;
|
|
|
|
use Illuminate\Pagination\LengthAwarePaginator;
|
|
use Modules\EcommerceFinance\Repositories\PlantingReimbursementRepository as Repository;
|
|
|
|
/** 种菜数据汇总 → 种菜报销(聚工单打款工单“种菜报销”,已完成,完成时间自 2026-09-02 起)。 */
|
|
class PlantingReimbursementPageData
|
|
{
|
|
use PlantingPageFilters;
|
|
|
|
public const TITLE = '种菜报销';
|
|
|
|
public const PER_PAGE = 50;
|
|
|
|
public const TEXT_FILTERS = [
|
|
'work_order_no' => '工单编号',
|
|
'online_order_no' => '线上订单号',
|
|
'shop_name' => '店铺名称',
|
|
'task_remark' => '任务备注',
|
|
'seller_remark' => '卖家备注',
|
|
'payee_name' => '信誉账号名称',
|
|
];
|
|
|
|
public const COLUMNS = [
|
|
'completed_at' => '完成时间', 'started_at' => '发起时间', 'work_order_no' => '工单编号', 'status' => '工单状态',
|
|
'platform' => '平台', 'shop_name' => '店铺名称', 'online_order_no' => '线上订单号', 'internal_order_no' => '内部单号',
|
|
'task_remark' => '任务备注', 'seller_remark' => '卖家备注',
|
|
'actual_paid_amount' => '实付金额', 'task_payment' => '任务付款金额', 'task_freight' => '任务快递费',
|
|
'task_commission' => '任务佣金', 'jingxi_price_diff' => '京喜差价', 'task_deduction' => '任务扣款费用',
|
|
'task_empty_package_fee' => '任务空包费', 'reimbursement_amount' => '最终报销金额',
|
|
'payee_name' => '信誉账号名称', 'payee_account' => '信誉账号', 'payee_bank' => '信誉账号开户行', 'payee_mobile' => '收款人手机号',
|
|
'initiator' => '发起人', 'executor' => '执行人', 'completer' => '完成人',
|
|
'product_code' => '商品编码', 'product_name' => '商品名称', 'product_quantity' => '商品数量', 'platform_product_id' => '平台商品ID',
|
|
'erp_order_status' => 'ERP订单状态', 'platform_order_status' => '平台订单状态', 'paid_at' => '付款时间',
|
|
];
|
|
|
|
public const MONEY_COLUMNS = Repository::MONEY_FIELDS;
|
|
|
|
public function __construct(private readonly Repository $repository) {}
|
|
|
|
/** @return array<string,string> */
|
|
public function filters(): array
|
|
{
|
|
return $this->baseFilters();
|
|
}
|
|
|
|
/**
|
|
* @param array<string,string> $filters
|
|
* @param list<array<string,mixed>>|null $rows
|
|
* @return \Generator<int,array<string,mixed>>
|
|
*/
|
|
public function rows(array $filters, ?array $rows = null): \Generator
|
|
{
|
|
foreach ($rows ?? $this->repository->rows($filters['month']) as $row) {
|
|
if ($this->matchesText($row, $filters)) {
|
|
yield $row;
|
|
}
|
|
}
|
|
}
|
|
|
|
public function page(): array
|
|
{
|
|
$filters = $this->filters();
|
|
$page = max(1, (int) request('page', 1));
|
|
$sync = $this->repository->syncStatus();
|
|
$summary = ['count' => 0, 'task_payment' => 0, 'reimbursement_amount' => 0, 'task_commission' => 0, 'task_empty_package_fee' => 0];
|
|
$items = [];
|
|
$error = $this->syncError($sync);
|
|
|
|
if ($error === null) {
|
|
try {
|
|
foreach ($this->rows($filters) as $row) {
|
|
if ($summary['count'] >= ($page - 1) * self::PER_PAGE && count($items) < self::PER_PAGE) {
|
|
$items[] = $row;
|
|
}
|
|
$summary['count']++;
|
|
foreach (['task_payment', 'reimbursement_amount', 'task_commission', 'task_empty_package_fee'] as $key) {
|
|
$summary[$key] += self::cents($row[$key]);
|
|
}
|
|
}
|
|
} catch (\Throwable $exception) {
|
|
report($exception);
|
|
$error = '种菜报销数据暂时无法读取,请稍后重试。';
|
|
$summary = array_fill_keys(array_keys($summary), 0);
|
|
$items = [];
|
|
}
|
|
}
|
|
|
|
return [
|
|
'view' => 'business.pages.billing-transactions.planting-reimbursements',
|
|
'plantingFilters' => $filters,
|
|
'plantingWindow' => Repository::window($filters['month']),
|
|
'plantingMonths' => Repository::months(),
|
|
'plantingSummary' => $summary,
|
|
'plantingSync' => $sync,
|
|
'plantingError' => $error,
|
|
'plantingRows' => new LengthAwarePaginator($items, $summary['count'], self::PER_PAGE, $page, [
|
|
'path' => request()->url(),
|
|
'query' => request()->except('page'),
|
|
]),
|
|
];
|
|
}
|
|
}
|