106 lines
4.1 KiB
PHP
106 lines
4.1 KiB
PHP
<?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,
|
||
'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);
|
||
}
|
||
}
|