91 lines
3.5 KiB
PHP
91 lines
3.5 KiB
PHP
<?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'
|
||
);
|
||
|
||
$purchase = PurchaseOrderModel::create([
|
||
'purchase_no' => $this->billNumberService->make('PO'),
|
||
'purchase_date' => $date,
|
||
'status' => PurchaseOrderModel::STATUS_PENDING,
|
||
'total_quantity' => $totalQuantity,
|
||
'total_weight' => 0,
|
||
'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;
|
||
});
|
||
}
|
||
}
|