Files
xin-procurement/app/Services/PurchaseGenerateService.php
2026-09-05 14:37:22 +08:00

96 lines
3.8 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\Exceptions\RepositoryException;
use App\Models\PurchaseOrderModel;
use App\Models\StoreOrderItemModel;
use App\Models\StoreOrderModel;
use Illuminate\Support\Facades\DB;
use Throwable;
/**
* 订单汇总生成采购单
*/
readonly class PurchaseGenerateService
{
public function __construct(private BillNumberService $billNumberService)
{
}
/**
* @param string $date 采购日期(Y-m-d
* @param int $operatorId 制单人(后台系统用户ID)
* @param int[] $orderIds 指定合并的门店订单ID(空 = 全部已接单订单)
* @return PurchaseOrderModel
* @throws Throwable
*/
public function generate(string $date, int $operatorId, array $orderIds = []): PurchaseOrderModel
{
return DB::transaction(function () use ($date, $operatorId, $orderIds) {
// 1. 行锁已接单订单(并发防护);指定订单时要求全部处于已接单,否则整批拒绝
$query = StoreOrderModel::query()->lockForUpdate();
if ($orderIds !== []) {
$orders = $query->whereIn('id', $orderIds)->get();
$invalid = $orders->where('status', '<>', StoreOrderModel::STATUS_SUMMARIZED);
if ($orders->isEmpty() || $invalid->isNotEmpty()) {
throw new RepositoryException(
'所选订单包含非「已接单」状态,无法生成采购单:' . $invalid->pluck('order_no')->implode('、')
);
}
} else {
$orders = $query->where('status', StoreOrderModel::STATUS_SUMMARIZED)->get();
}
if ($orders->isEmpty()) {
throw new RepositoryException('无已接单订单,无法生成采购单');
}
$sourceOrderIds = $orders->pluck('id')->all();
// 2. 订货明细汇总(采购单头数据)
$items = StoreOrderItemModel::query()->whereIn('order_id', $sourceOrderIds)->get();
if ($items->isEmpty()) {
throw new RepositoryException('已接单订单均无明细,无法生成采购单');
}
$totalQuantity = $items->reduce(
static fn (string $carry, $item): string => bcadd($carry, (string) $item->quantity, 2),
'0'
);
// 预估成本
$estimateAmount = $items->reduce(
static fn (string $carry, $item): string => bcadd($carry, bcmul($item->quantity, $item->cost_price, 2), 2),
'0'
);
// 参考总重量 = Σ 明细参考重量(下单时按订货量 × 规格预填)
$totalWeight = $items->reduce(
static fn (string $carry, $item): string => bcadd($carry, (string) $item->weight, 3),
'0'
);
$purchase = PurchaseOrderModel::create([
'purchase_no' => $this->billNumberService->make('PO'),
'purchase_date' => $date,
'status' => PurchaseOrderModel::STATUS_PENDING,
'total_quantity' => $totalQuantity,
'total_weight' => $totalWeight,
'estimate_amount' => $estimateAmount,
'actual_amount' => 0,
'operator_id' => $operatorId,
]);
// 3. 源订单回写「采购中」并关联采购单;订货明细同步回写 purchase_id
StoreOrderModel::whereIn('id', $sourceOrderIds)
->update([
'status' => StoreOrderModel::STATUS_DELIVERING,
'purchase_id' => $purchase->id,
]);
StoreOrderItemModel::whereIn('order_id', $sourceOrderIds)
->update(['purchase_id' => $purchase->id]);
return $purchase;
});
}
}