优化采购单

This commit is contained in:
liu
2026-08-13 12:34:32 +08:00
parent 099e92b1ff
commit 61a70319a0
14 changed files with 628 additions and 276 deletions
@@ -5,6 +5,7 @@ namespace App\Http\Controllers\Purchase;
use App\Exceptions\RepositoryException;
use App\Exports\PurchaseOrderExport;
use App\Http\Requests\Purchase\PurchaseCellUpdateRequest;
use App\Http\Requests\Purchase\PurchaseContainerUpdateRequest;
use App\Http\Requests\Purchase\PurchaseRowUpdateRequest;
use App\Models\ProductModel;
use App\Models\PurchaseOrderModel;
@@ -13,6 +14,7 @@ use App\Models\StoreOrderItemModel;
use App\Models\StoreOrderModel;
use App\Services\ItemImageResolver;
use App\Services\PurchaseGenerateService;
use App\Services\StoreOrderContainerService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
@@ -133,6 +135,45 @@ class PurchaseOrderController extends BaseController
[$a['category_sort'], $a['product_sort'], $a['product_id']]
<=> [$b['category_sort'], $b['product_sort'], $b['product_id']]);
// 周转框/托盘合并记录:按门店聚合全部订单(附底层订单明细,供采购单完成前修改)
$storeOrders = StoreOrderModel::query()
->where('purchase_id', $purchase->id)
->whereNull('deleted_at')
->orderBy('id')
->get(['id', 'order_no', 'store_id', 'box_num', 'tray_num']);
$boxPrice = (float) site_config('services.box_amount', 0);
$trayPrice = (float) site_config('services.tray_amount', 0);
$storeSort = $stores->pluck('id')->flip();
$containers = [];
foreach ($storeOrders->groupBy('store_id') as $storeId => $orders) {
$boxNum = (int) $orders->sum('box_num');
$trayNum = (int) $orders->sum('tray_num');
$containers[] = [
'store_id' => (int) $storeId,
'store_name' => $stores->firstWhere('id', (int) $storeId)['name'] ?? '门店#' . $storeId,
'box_num' => $boxNum,
'tray_num' => $trayNum,
'box_price' => number_format($boxPrice, 2),
'tray_price' => number_format($trayPrice, 2),
'added_amount' => number_format($boxNum * $boxPrice + $trayNum * $trayPrice, 2),
'order_count' => $orders->count(),
'orders' => $orders->map(static fn (StoreOrderModel $order) => [
'order_id' => $order->id,
'order_no' => $order->order_no,
'box_num' => (int) $order->box_num,
'tray_num' => (int) $order->tray_num,
])->values()->toArray(),
'store_sort' => (int) ($storeSort[$storeId] ?? 9999),
];
}
usort($containers, static fn (array $a, array $b): int =>
[$a['store_sort'], $a['store_id']] <=> [$b['store_sort'], $b['store_id']]);
$containers = array_map(static function (array $row): array {
unset($row['store_sort']);
return $row;
}, $containers);
return $this->success([
'purchase' => $purchase->toArray(),
'stores' => $stores->toArray(),
@@ -140,6 +181,7 @@ class PurchaseOrderController extends BaseController
unset($row['category_sort'], $row['product_sort']);
return $row;
}, $rows),
'containers' => $containers,
]);
}
@@ -395,6 +437,74 @@ class PurchaseOrderController extends BaseController
]);
}
/**
* 周转框/托盘合并记录修改:按门店覆盖全部订单逐笔更新(仅采购单进行中可改)
* @throws Throwable
*/
#[PutRoute(route: '/{id}/container/{storeId}', authorize: 'update', where: ['id' => '[0-9]+', 'storeId' => '[0-9]+'])]
public function updateContainer(int $id, int $storeId, PurchaseContainerUpdateRequest $request): JsonResponse
{
$purchase = PurchaseOrderModel::find($id);
if (empty($purchase)) {
throw new RepositoryException('采购单不存在');
}
if ($purchase->status !== PurchaseOrderModel::STATUS_PENDING) {
throw new RepositoryException('采购单已完成,不允许修改周转框/托盘');
}
$submitted = $request->validated()['orders'];
return DB::transaction(function () use ($purchase, $storeId, $submitted) {
$orders = StoreOrderModel::query()
->where('purchase_id', $purchase->id)
->where('store_id', $storeId)
->whereNull('deleted_at')
->lockForUpdate()
->get();
if ($orders->isEmpty()) {
throw new RepositoryException('该采购单下无此门店的订单');
}
// 合并记录修改必须覆盖该门店全部订单,避免只改部分造成汇总偏差
$actualIds = $orders->pluck('id')->map(static fn ($orderId) => (int) $orderId)->all();
$submittedIds = array_map(static fn (array $row) => (int) $row['order_id'], $submitted);
$missing = array_diff($actualIds, $submittedIds);
if ($missing !== []) {
throw new RepositoryException('提交不完整,缺少订单:' . implode('、', $missing));
}
if (array_diff($submittedIds, $actualIds) !== []) {
throw new RepositoryException('包含不属于该采购单该门店的订单');
}
foreach ($orders as $order) {
if (! in_array($order->status, [
StoreOrderModel::STATUS_SUMMARIZED,
StoreOrderModel::STATUS_DELIVERING,
StoreOrderModel::STATUS_DISTRIBUTION,
], true)) {
throw new RepositoryException(
'订单 ' . $order->order_no . ' 当前状态为「'
. (StoreOrderModel::STATUS_NAMES[$order->status] ?? $order->status)
. '」,不允许修改周转框/托盘数量'
);
}
}
$ordersById = $orders->keyBy('id');
$containerService = app(StoreOrderContainerService::class);
foreach ($submitted as $row) {
$containerService->update(
$ordersById->get($row['order_id']),
(int) $row['box_num'],
(int) $row['tray_num'],
);
}
return $this->success();
});
}
/**
* 商品行修改:品名/供应商/包规/单位/成本
* @throws Throwable