采购单优化
This commit is contained in:
@@ -0,0 +1,187 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\ProductModel;
|
||||
use App\Models\PurchaseOrderModel;
|
||||
use App\Models\StoreOrderItemModel;
|
||||
use App\Models\StoreOrderModel;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
/**
|
||||
* 采购单明细共享服务:订单/采购单汇总重算 + 门店/供应商维度的商品行聚合
|
||||
*
|
||||
* 汇总口径(勿偏离):
|
||||
* - 订单:total_quantity=Σ数量、total_weight=Σ称重、total_amount=Σ金额
|
||||
* - 采购单:total_quantity=Σ数量、estimate_amount=Σ(数量×成本价)(预估成本)、total_weight=Σ称重
|
||||
*/
|
||||
class PurchaseItemService
|
||||
{
|
||||
/**
|
||||
* 重算门店订单汇总(明细增删改后调用)
|
||||
*/
|
||||
public function recalcOrder(int $orderId): void
|
||||
{
|
||||
$order = StoreOrderModel::query()->find($orderId);
|
||||
if ($order === null) {
|
||||
return;
|
||||
}
|
||||
$order->total_quantity = (int) $order->items()->sum('quantity');
|
||||
$order->total_weight = $order->items()->sum('weight');
|
||||
$order->total_amount = $order->items()->sum('amount');
|
||||
$order->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* 重算采购单汇总(明细增删改后调用)
|
||||
*/
|
||||
public function recalcPurchase(int $purchaseId): void
|
||||
{
|
||||
$purchase = PurchaseOrderModel::query()->find($purchaseId);
|
||||
if ($purchase === null) {
|
||||
return;
|
||||
}
|
||||
$purchase->total_quantity = (int) StoreOrderItemModel::query()
|
||||
->where('purchase_id', $purchaseId)->sum('quantity');
|
||||
$purchase->estimate_amount = StoreOrderItemModel::query()
|
||||
->where('purchase_id', $purchaseId)->sum(DB::raw('quantity * cost_price'));
|
||||
$purchase->total_weight = StoreOrderItemModel::query()
|
||||
->where('purchase_id', $purchaseId)->sum('weight');
|
||||
$purchase->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* 采购单内指定门店的采购汇总行(按商品聚合,单价=加权平均 Σ金额÷Σ数量,
|
||||
* 排序:分类 sort → 商品 sort,与明细矩阵同序)
|
||||
*
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
public function storeSummaryRows(int $purchaseId, int $storeId): array
|
||||
{
|
||||
$items = $this->purchaseItems($purchaseId)
|
||||
->where('store_id', $storeId)
|
||||
->sortBy('id');
|
||||
|
||||
$rows = [];
|
||||
foreach ($items->groupBy('product_id') as $productId => $group) {
|
||||
$first = $group->first();
|
||||
$quantity = 0;
|
||||
$amount = '0';
|
||||
$weight = '0';
|
||||
foreach ($group as $item) {
|
||||
$weight = bcadd($weight, (string) $item->weight, 3);
|
||||
$quantity += (int) $item->quantity;
|
||||
$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,
|
||||
'amount' => $amount,
|
||||
'weight' => $weight,
|
||||
'category_sort' => (int) data_get($first, 'category_sort', 9999),
|
||||
'product_sort' => (int) data_get($first, 'product_sort', 9999),
|
||||
];
|
||||
}
|
||||
|
||||
return $this->sortRows($rows);
|
||||
}
|
||||
|
||||
/**
|
||||
* 采购单内指定供应商的商品聚合行(成本口径:金额=Σ数量×成本价,
|
||||
* 排序:分类 sort → 商品 sort)
|
||||
*
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
public function supplierRows(int $purchaseId, int $supplierId): array
|
||||
{
|
||||
$items = $this->purchaseItems($purchaseId)
|
||||
->where('supplier_id', $supplierId)
|
||||
->sortBy('id');
|
||||
|
||||
$rows = [];
|
||||
foreach ($items->groupBy('product_id') as $productId => $group) {
|
||||
$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, bcmul((string) $item->quantity, (string) $item->cost_price, 2), 2);
|
||||
}
|
||||
|
||||
$rows[] = [
|
||||
'product_id' => (int) $productId,
|
||||
'product_name' => $first->product_name,
|
||||
'product_spec' => $first->product_spec,
|
||||
'unit' => $first->unit,
|
||||
'cost_price' => (string) $first->cost_price,
|
||||
'quantity' => $quantity,
|
||||
'weight' => $weight,
|
||||
'amount' => $amount,
|
||||
'category_sort' => (int) data_get($first, 'category_sort', 9999),
|
||||
'product_sort' => (int) data_get($first, 'product_sort', 9999),
|
||||
];
|
||||
}
|
||||
|
||||
return $this->sortRows($rows);
|
||||
}
|
||||
|
||||
/**
|
||||
* 采购单订货明细(关联订单过滤软删、成本价可见、附分类/商品排序键)
|
||||
*
|
||||
* @return Collection<int, StoreOrderItemModel>
|
||||
*/
|
||||
private function purchaseItems(int $purchaseId): Collection
|
||||
{
|
||||
$items = StoreOrderItemModel::query()
|
||||
->join('store_order', 'store_order.id', '=', 'store_order_item.order_id')
|
||||
->where('store_order_item.purchase_id', $purchaseId)
|
||||
->whereNull('store_order.deleted_at')
|
||||
->select('store_order_item.*')
|
||||
->get()
|
||||
->makeVisible('cost_price');
|
||||
|
||||
// 排序键:商品分类 sort → 商品 sort(商品含软删除,保证历史单据可导出)
|
||||
$products = ProductModel::withTrashed()
|
||||
->with('category:id,sort')
|
||||
->whereIn('id', $items->pluck('product_id')->unique())
|
||||
->get(['id', 'category_id', 'sort'])
|
||||
->keyBy('id');
|
||||
|
||||
foreach ($items as $item) {
|
||||
$product = $products->get((int) $item->product_id);
|
||||
$item->setAttribute('category_sort', (int) ($product?->category?->sort ?? 9999));
|
||||
$item->setAttribute('product_sort', (int) ($product?->sort ?? 9999));
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* 排序并剥离排序键
|
||||
*
|
||||
* @param array<int, array<string, mixed>> $rows
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
private function sortRows(array $rows): array
|
||||
{
|
||||
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']]);
|
||||
|
||||
return array_map(static function (array $row): array {
|
||||
unset($row['category_sort'], $row['product_sort']);
|
||||
return $row;
|
||||
}, $rows);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user