采购单优化
This commit is contained in:
@@ -9,17 +9,17 @@ use App\Models\ProductModel;
|
||||
use App\Models\PurchaseOrderModel;
|
||||
use App\Models\StoreModel;
|
||||
use App\Models\StoreOrderItemModel;
|
||||
use App\Services\ExportService;
|
||||
use App\Services\PurchaseEditService;
|
||||
use App\Models\StoreOrderModel;
|
||||
use App\Services\ItemImageResolver;
|
||||
use App\Services\PurchaseGenerateService;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Modules\AnnoRoute\Attribute\GetRoute;
|
||||
use Modules\AnnoRoute\Attribute\PostRoute;
|
||||
use Modules\AnnoRoute\Attribute\PutRoute;
|
||||
use Modules\AnnoRoute\Attribute\RequestAttribute;
|
||||
use Modules\Common\Http\Controllers\BaseController;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
@@ -50,8 +50,7 @@ class PurchaseOrderController extends BaseController
|
||||
}
|
||||
|
||||
/**
|
||||
* 采购单详情:头 + 明细矩阵(商品行 × 门店列)
|
||||
* 行 = 商品(按分类 sort → 商品 sort 排序),列 = 商品信息 + 每个门店一格(数量/金额)
|
||||
* 采购单详情
|
||||
*/
|
||||
#[GetRoute(route: '/{id}', authorize: 'query', where: ['id' => '[0-9]+'])]
|
||||
public function detail(int $id): JsonResponse
|
||||
@@ -163,8 +162,7 @@ class PurchaseOrderController extends BaseController
|
||||
}
|
||||
|
||||
/**
|
||||
* C1 生成采购单:合并全部「已接单」门店订单(或指定的 order_ids),
|
||||
* 生成后源订单转为「采购中」并回写 purchase_id(订单与订货明细同步归集)
|
||||
* 生成采购单
|
||||
*/
|
||||
#[PostRoute('/generate', 'generate')]
|
||||
public function generate(Request $request): JsonResponse
|
||||
@@ -192,29 +190,77 @@ class PurchaseOrderController extends BaseController
|
||||
);
|
||||
}
|
||||
|
||||
/** C2/C3 导出采购单:?type=all|category & format=xlsx|pdf */
|
||||
#[GetRoute(route: '/{id}/export', authorize: 'export', where: ['id' => '[0-9]+'])]
|
||||
public function export(int $id, Request $request): Response
|
||||
/**
|
||||
* 单元格下钻:采购单中某门店某商品的全部订货明细
|
||||
*/
|
||||
#[GetRoute(route: '/{id}/cell', authorize: 'query', where: ['id' => '[0-9]+'])]
|
||||
public function cell(int $id, Request $request): JsonResponse
|
||||
{
|
||||
$productId = (int) $request->query('product_id', 0);
|
||||
$storeId = (int) $request->query('store_id', 0);
|
||||
if ($productId <= 0 || $storeId <= 0) {
|
||||
throw new RepositoryException('缺少商品或门店参数');
|
||||
}
|
||||
$purchase = PurchaseOrderModel::find($id);
|
||||
if (empty($purchase)) {
|
||||
throw new RepositoryException('采购单不存在');
|
||||
}
|
||||
$type = (string) $request->query('type', 'all');
|
||||
if (! in_array($type, ['all', 'category'], true)) {
|
||||
throw new RepositoryException('导出类型参数不正确(all 全品类 / category 蔬果分类)');
|
||||
}
|
||||
|
||||
return app(ExportService::class)->download(
|
||||
'purchase',
|
||||
$purchase,
|
||||
(string) $request->query('format', ExportService::FORMAT_XLSX),
|
||||
type: $type,
|
||||
);
|
||||
$items = StoreOrderItemModel::query()
|
||||
->join('store_order', 'store_order.id', '=', 'store_order_item.order_id')
|
||||
->where('store_order_item.purchase_id', $purchase->id)
|
||||
->where('store_order_item.product_id', $productId)
|
||||
->where('store_order_item.store_id', $storeId)
|
||||
->whereNull('store_order.deleted_at')
|
||||
->with('supplier:id,name')
|
||||
->select(
|
||||
'store_order_item.*',
|
||||
'store_order.order_no',
|
||||
'store_order.order_date',
|
||||
'store_order.status as order_status',
|
||||
)
|
||||
->orderBy('store_order.id')
|
||||
->get()
|
||||
->makeVisible('cost_price');
|
||||
|
||||
$rows = [];
|
||||
foreach ($items as $item) {
|
||||
$rows[] = [
|
||||
'id' => $item->id,
|
||||
'order_id' => $item->order_id,
|
||||
'order_no' => $item->order_no,
|
||||
'order_date' => (string) $item->order_date,
|
||||
'order_status' => (int) $item->order_status,
|
||||
'product_name' => $item->product_name,
|
||||
'product_spec' => $item->product_spec,
|
||||
'unit' => $item->unit,
|
||||
'supplier_id' => (int) $item->supplier_id,
|
||||
'supplier' => $item->supplier ? ['id' => $item->supplier->id, 'name' => $item->supplier->name] : null,
|
||||
'price' => (string) $item->price,
|
||||
'cost_price' => (string) $item->cost_price,
|
||||
'quantity' => (int) $item->quantity,
|
||||
'weight' => (string) $item->weight,
|
||||
'amount' => (string) $item->amount,
|
||||
'remark' => (string) $item->remark,
|
||||
'image_ids' => $item->image_ids,
|
||||
'editable' => $purchase->status === PurchaseOrderModel::STATUS_PENDING
|
||||
&& in_array((int) $item->order_status, StoreOrderItemModel::ITEM_EDITABLE_STATUS, true),
|
||||
];
|
||||
}
|
||||
app(ItemImageResolver::class)->resolve($rows);
|
||||
|
||||
$store = StoreModel::withTrashed()->find($storeId);
|
||||
$product = ProductModel::withTrashed()->find($productId);
|
||||
|
||||
return $this->success([
|
||||
'store' => $store ? ['id' => $store->id, 'name' => $store->name] : null,
|
||||
'product' => $product ? ['id' => $product->id, 'name' => $product->name] : null,
|
||||
'items' => $rows,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 商品行修改:品名/供应商/包规/单位/成本一键同步该商品全部订货明细;
|
||||
* 商品行修改:品名/供应商/包规/单位/成本
|
||||
* @throws Throwable
|
||||
*/
|
||||
#[PutRoute(route: '/{id}/row/{productId}', authorize: 'update', where: ['id' => '[0-9]+', 'productId' => '[0-9]+'])]
|
||||
@@ -224,25 +270,77 @@ class PurchaseOrderController extends BaseController
|
||||
if (empty($purchase)) {
|
||||
throw new RepositoryException('采购单不存在');
|
||||
}
|
||||
if ($purchase->status !== PurchaseOrderModel::STATUS_PENDING) {
|
||||
throw new RepositoryException('采购单已完成,不允许修改明细');
|
||||
}
|
||||
|
||||
$validated = $request->validated();
|
||||
|
||||
$attrs = [];
|
||||
foreach (['product_name', 'supplier_id', 'product_spec', 'unit', 'cost_price'] as $field) {
|
||||
if (isset($validated[$field])) {
|
||||
$attrs[$field] = $validated[$field];
|
||||
$items = StoreOrderItemModel::query()
|
||||
->where('purchase_id', $purchase->id)
|
||||
->where('product_id', $productId)
|
||||
->lockForUpdate()
|
||||
->get();
|
||||
if ($items->isEmpty()) {
|
||||
throw new RepositoryException('该采购单下无此商品的订货明细');
|
||||
}
|
||||
foreach ($items as $item) {
|
||||
$item->fill($validated)->save();
|
||||
}
|
||||
|
||||
// 重算采购单成本
|
||||
$query = StoreOrderItemModel::query()->where('purchase_id', $purchase->id);
|
||||
$purchase->total_quantity = $query->sum('quantity');
|
||||
$purchase->estimate_amount = $query->sum(DB::raw('quantity * cost_price'));
|
||||
$purchase->save();
|
||||
|
||||
return $this->success(['count' => $items->count()], '已同步 ' . $items->count() . ' 条订货明细');
|
||||
}
|
||||
|
||||
/**
|
||||
* 门店订单明细修改(订货量、重量、单价),自动重算价格
|
||||
* @throws Throwable
|
||||
*/
|
||||
#[PutRoute(route: '/cell/{itemId}', authorize: 'update', where: ['itemId' => '[0-9]+'])]
|
||||
public function cellUpdate(int $itemId, PurchaseCellUpdateRequest $request): JsonResponse
|
||||
{
|
||||
$validated = $request->validated();
|
||||
|
||||
return DB::transaction(function () use ($itemId, $validated) {
|
||||
$item = StoreOrderItemModel::query()->lockForUpdate()->find($itemId);
|
||||
if (empty($item) || (int) $item->purchase_id === 0) {
|
||||
throw new RepositoryException('采购单明细不存在');
|
||||
}
|
||||
$purchase = PurchaseOrderModel::find((int) $item->purchase_id);
|
||||
if (empty($purchase)) {
|
||||
throw new RepositoryException('采购单不存在');
|
||||
}
|
||||
if ($purchase->status !== PurchaseOrderModel::STATUS_PENDING) {
|
||||
throw new RepositoryException('采购单已完成,不允许修改明细');
|
||||
}
|
||||
}
|
||||
if ($attrs === []) {
|
||||
throw new RepositoryException('品名/供应商/包规/单位/成本至少填写一项');
|
||||
}
|
||||
|
||||
$count = app(PurchaseEditService::class)->updateRow(
|
||||
$purchase,
|
||||
$productId,
|
||||
$attrs,
|
||||
(bool) ($validated['sync_product'] ?? false),
|
||||
);
|
||||
$item->quantity = $validated['quantity'];
|
||||
$item->price = $validated['price'];
|
||||
if (array_key_exists('weight', $validated)) {
|
||||
$item->weight = bcadd((string) $validated['weight'], '0', 3);
|
||||
}
|
||||
$item->amount = bcmul($validated['quantity'], $validated['price'], 3);
|
||||
$item->save();
|
||||
|
||||
return $this->success(['count' => $count], '已同步 ' . $count . ' 条订货明细');
|
||||
// 重算订单金额
|
||||
$order = StoreOrderModel::query()->lockForUpdate()->find($item->order_id);
|
||||
$order->product_amount = $order->items()->sum('amount');
|
||||
$order->total_weight = $order->items()->sum('weight');
|
||||
$order->total_amount = bcadd($order->product_amount, $order->added_amount, 2);
|
||||
$order->save();
|
||||
|
||||
// 重算采购单重量
|
||||
$purchase->total_weight = StoreOrderItemModel::query()
|
||||
->where('purchase_id', $purchase->id)
|
||||
->sum('weight');
|
||||
$purchase->save();
|
||||
|
||||
return $this->success();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user