Files
2026-09-06 23:20:00 +08:00

108 lines
4.2 KiB
PHP
Raw Permalink 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
namespace App\Services;
use App\Models\BillModel;
use App\Models\ProductModel;
use App\Models\StoreOrderItemModel;
use Illuminate\Support\Collection;
/**
* 账单详情数据组装(后台门店账单页与小程序账单详情共用)
*/
class BillDetailService
{
/**
* 合并后的商品明细:按商品聚合账单关联的全部订单明细
* 单价为加权平均口径(Σ金额÷Σ数量),保证 单价×数量=金额;附商品首图
*
* @return array<int, array{product_id: int, product_name: string, product_spec: string, unit: string, price: string, quantity: int, weight: string, amount: string, image: string}>
*/
public function mergedItems(BillModel $bill): array
{
$items = StoreOrderItemModel::query()
->where('bill_id', $bill->id)
->orderBy('id')
->get();
return $this->aggregateItems($items);
}
/**
* 合并后的商品明细(跨账单口径):按商品聚合多张账单的全部订单明细
* 用于账单合并导出,单价同为加权平均口径
*
* @param array<int, int> $billIds 账单ID列表
* @return array<int, array{product_id: int, product_name: string, product_spec: string, unit: string, price: string, quantity: int, weight: string, amount: string, image: string}>
*/
public function mergedItemsOfBills(array $billIds): array
{
$items = StoreOrderItemModel::query()
->whereIn('bill_id', $billIds)
->orderBy('id')
->get();
return $this->aggregateItems($items);
}
/**
* 按商品聚合订单明细:数量累加、重量/金额 bc 累加、单价加权平均,
* 首图取明细快照 image_ids 批量解析,按「分类sort → 商品sort」排序
*
* @param Collection<int, StoreOrderItemModel> $items
* @return array<int, array{product_id: int, product_name: string, product_spec: string, unit: string, price: string, quantity: int, weight: string, amount: string, image: string}>
*/
private function aggregateItems(Collection $items): array
{
// 排序键:分类 sort → 商品 sort(与采购单明细矩阵同序)
$products = ProductModel::withTrashed()
->with('category:id,sort')
->whereIn('id', $items->pluck('product_id')->unique())
->get()
->keyBy('id');
$rows = [];
foreach ($items->groupBy('product_id') as $productId => $group) {
$product = $products->get((int) $productId);
$first = $group->first();
$quantity = 0;
$weight = '0';
$amount = '0';
foreach ($group as $item) {
$quantity += (int) $item->quantity;
$weight = bcadd($weight, (string) $item->weight, 3);
$amount = bcadd($amount, (string) $item->amount, 2);
}
$rows[] = [
'product_id' => (int) $productId,
'product_name' => $first->product_name,
'product_spec' => $first->product_spec,
'unit' => $first->unit,
'price' => $quantity > 0
? bcdiv($amount, (string) $quantity, 2)
: (string) $first->price,
'quantity' => $quantity,
'weight' => $weight,
'amount' => $amount,
'spec' => $product->spec,
'price_unit' => $product->price_unit,
'image_ids' => (array) $first->image_ids,
'category_sort' => (int) ($product->category->sort ?? 9999),
'product_sort' => (int) ($product->sort ?? 9999),
];
}
usort($rows, static fn (array $a, array $b): int =>
[$a['category_sort'], $a['product_sort'], $a['product_id']]
<=> [$b['category_sort'], $b['product_sort'], $b['product_id']]);
// 首图批量解析(写入 image、移除 image_ids
app(ItemImageResolver::class)->resolve($rows);
return array_map(static function (array $row): array {
unset($row['category_sort'], $row['product_sort']);
return $row;
}, $rows);
}
}