396 lines
14 KiB
PHP
396 lines
14 KiB
PHP
<?php
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Models\Goods;
|
|
use App\Models\Brand;
|
|
use App\Models\Supplier;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Str;
|
|
|
|
class GoodsController extends Controller
|
|
{
|
|
/**
|
|
* 商品列表
|
|
*/
|
|
public function index(Request $request)
|
|
{
|
|
$query = Goods::with(['brand', 'suppliers']);
|
|
|
|
// 搜索
|
|
if ($request->filled('keyword')) {
|
|
$keyword = $request->keyword;
|
|
$query->where(function ($q) use ($keyword) {
|
|
$q->where('name', 'like', "%{$keyword}%")
|
|
->orWhere('code', 'like', "%{$keyword}%")
|
|
->orWhere('barcode', 'like', "%{$keyword}%");
|
|
});
|
|
}
|
|
|
|
// 类型筛选
|
|
if ($request->filled('type')) {
|
|
$query->where('type', $request->type);
|
|
}
|
|
|
|
// 品牌筛选
|
|
if ($request->filled('brand_id')) {
|
|
$query->where('brand_id', $request->brand_id);
|
|
}
|
|
|
|
$perPage = $request->input('limit', 15);
|
|
$goods = $query->orderBy('created_at', 'desc')->paginate($perPage);
|
|
|
|
// 格式化数据
|
|
$goods->getCollection()->transform(function ($item) {
|
|
return [
|
|
'id' => $item->id,
|
|
'name' => $item->name,
|
|
'code' => $item->code,
|
|
'barcode' => $item->barcode,
|
|
'type' => $item->type,
|
|
'type_label' => $item->type == 'normal' ? '普通商品' : '组套商品',
|
|
'retail_price' => $item->retail_price,
|
|
'cost_price' => $item->cost_price,
|
|
'unit' => $item->unit,
|
|
'brand' => $item->brand ? ['id' => $item->brand->id, 'name' => $item->brand->name] : null,
|
|
'suppliers' => $item->suppliers->map(function ($s) {
|
|
return ['id' => $s->id, 'name' => $s->name];
|
|
}),
|
|
'weight' => $item->weight,
|
|
'packaging_cost' => $item->packaging_cost,
|
|
'shipping_packaging_cost' => $item->shipping_packaging_cost,
|
|
'volume' => $item->volume,
|
|
'length' => $item->length,
|
|
'width' => $item->width,
|
|
'height' => $item->height,
|
|
'batch_management' => $item->batch_management,
|
|
'created_at' => $item->created_at,
|
|
'updated_at' => $item->updated_at,
|
|
];
|
|
});
|
|
|
|
return response()->json([
|
|
'code' => 200,
|
|
'data' => [
|
|
'list' => $goods->items(),
|
|
'total' => $goods->total(),
|
|
'current_page' => $goods->currentPage(),
|
|
'last_page' => $goods->lastPage(),
|
|
],
|
|
'message' => 'success'
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 获取所有商品(用于下拉选择)
|
|
*/
|
|
public function all(Request $request)
|
|
{
|
|
$query = Goods::select('id', 'name', 'code', 'retail_price', 'cost_price', 'type');
|
|
|
|
if ($request->filled('type')) {
|
|
$query->where('type', $request->type);
|
|
}
|
|
|
|
$goods = $query->get();
|
|
return response()->json([
|
|
'code' => 200,
|
|
'data' => $goods,
|
|
'message' => 'success'
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 商品详情
|
|
*/
|
|
public function show($id)
|
|
{
|
|
$goods = Goods::with(['brand', 'suppliers', 'comboItems.goods'])->findOrFail($id);
|
|
|
|
$data = [
|
|
'id' => $goods->id,
|
|
'name' => $goods->name,
|
|
'code' => $goods->code,
|
|
'barcode' => $goods->barcode,
|
|
'category' => $goods->category,
|
|
'unit' => $goods->unit,
|
|
'weight' => $goods->weight,
|
|
'retail_price' => $goods->retail_price,
|
|
'cost_price' => $goods->cost_price,
|
|
'stock_warning' => $goods->stock_warning,
|
|
'type' => $goods->type,
|
|
'custom_id' => $goods->custom_id,
|
|
'packaging_cost' => $goods->packaging_cost,
|
|
'shipping_packaging_cost' => $goods->shipping_packaging_cost,
|
|
'volume' => $goods->volume,
|
|
'length' => $goods->length,
|
|
'width' => $goods->width,
|
|
'height' => $goods->height,
|
|
'brand_id' => $goods->brand_id,
|
|
'brand' => $goods->brand ? ['id' => $goods->brand->id, 'name' => $goods->brand->name] : null,
|
|
'suppliers' => $goods->suppliers->map(function ($s) {
|
|
return ['id' => $s->id, 'name' => $s->name];
|
|
}),
|
|
'batch_management' => $goods->batch_management,
|
|
];
|
|
|
|
if ($goods->type == 'combo') {
|
|
$data['combo_items'] = $goods->comboItems->map(function ($item) {
|
|
return [
|
|
'goods_id' => $item->goods_id,
|
|
'goods_name' => $item->goods->name,
|
|
'goods_code' => $item->goods->code,
|
|
'quantity' => $item->quantity,
|
|
];
|
|
});
|
|
}
|
|
|
|
return response()->json([
|
|
'code' => 200,
|
|
'data' => $data,
|
|
'message' => 'success'
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 创建商品
|
|
*/
|
|
public function store(Request $request)
|
|
{
|
|
$rules = [
|
|
'name' => 'required|string|max:255',
|
|
'barcode' => 'nullable|string|max:100',
|
|
'type' => 'required|in:normal,combo',
|
|
'unit' => 'required|string|max:20',
|
|
'retail_price' => 'required|numeric|min:0',
|
|
'cost_price' => 'required|numeric|min:0',
|
|
'weight' => 'nullable|numeric|min:0',
|
|
'packaging_cost' => 'nullable|numeric|min:0',
|
|
'shipping_packaging_cost' => 'nullable|numeric|min:0',
|
|
'volume' => 'nullable|numeric|min:0',
|
|
'length' => 'nullable|numeric|min:0',
|
|
'width' => 'nullable|numeric|min:0',
|
|
'height' => 'nullable|numeric|min:0',
|
|
'brand_id' => 'nullable|exists:brands,id',
|
|
'supplier_ids' => 'nullable|array',
|
|
'supplier_ids.*' => 'exists:suppliers,id',
|
|
'batch_management' => 'nullable|boolean',
|
|
'custom_id' => 'nullable|string|max:100',
|
|
'category' => 'nullable|string|max:50',
|
|
'stock_warning' => 'nullable|integer|min:0',
|
|
];
|
|
|
|
// 如果是组套商品,需要验证组合项
|
|
if ($request->type == 'combo') {
|
|
$rules['combo_items'] = 'required|array|min:1';
|
|
$rules['combo_items.*.goods_id'] = 'required|exists:goods,id';
|
|
$rules['combo_items.*.quantity'] = 'required|integer|min:1';
|
|
}
|
|
|
|
$request->validate($rules);
|
|
|
|
// 自动生成商品编码(如果未提供)
|
|
$code = $request->code;
|
|
if (!$code) {
|
|
$code = $this->generateGoodsCode();
|
|
} else {
|
|
// 检查唯一性
|
|
if (Goods::where('code', $code)->exists()) {
|
|
return response()->json(['code' => 400, 'message' => '商品编码已存在'], 400);
|
|
}
|
|
}
|
|
|
|
try {
|
|
DB::beginTransaction();
|
|
|
|
$goods = Goods::create([
|
|
'name' => $request->name,
|
|
'code' => $code,
|
|
'barcode' => $request->barcode,
|
|
'category' => $request->category,
|
|
'unit' => $request->unit,
|
|
'weight' => $request->weight ?? 0,
|
|
'retail_price' => $request->retail_price,
|
|
'cost_price' => $request->cost_price,
|
|
'stock_warning' => $request->stock_warning ?? 0,
|
|
'type' => $request->type,
|
|
'custom_id' => $request->custom_id,
|
|
'packaging_cost' => $request->packaging_cost ?? 0,
|
|
'shipping_packaging_cost' => $request->shipping_packaging_cost ?? 0,
|
|
'volume' => $request->volume ?? 0,
|
|
'length' => $request->length ?? 0,
|
|
'width' => $request->width ?? 0,
|
|
'height' => $request->height ?? 0,
|
|
'brand_id' => $request->brand_id,
|
|
'batch_management' => $request->batch_management ?? false,
|
|
]);
|
|
|
|
// 关联供应商
|
|
if ($request->has('supplier_ids')) {
|
|
$goods->suppliers()->sync($request->supplier_ids);
|
|
}
|
|
|
|
// 如果是组套商品,添加组合项
|
|
if ($request->type == 'combo' && $request->has('combo_items')) {
|
|
foreach ($request->combo_items as $item) {
|
|
// 检查子商品不能是组套商品(避免嵌套)
|
|
$childGoods = Goods::find($item['goods_id']);
|
|
if ($childGoods->type == 'combo') {
|
|
throw new \Exception('组合商品不能包含另一个组合商品');
|
|
}
|
|
$goods->comboItems()->create([
|
|
'goods_id' => $item['goods_id'],
|
|
'quantity' => $item['quantity'],
|
|
]);
|
|
}
|
|
}
|
|
|
|
DB::commit();
|
|
|
|
return response()->json([
|
|
'code' => 200,
|
|
'data' => ['id' => $goods->id],
|
|
'message' => '创建成功'
|
|
]);
|
|
} catch (\Exception $e) {
|
|
DB::rollBack();
|
|
return response()->json([
|
|
'code' => 500,
|
|
'message' => '创建失败:' . $e->getMessage()
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 更新商品
|
|
*/
|
|
public function update(Request $request, $id)
|
|
{
|
|
$goods = Goods::findOrFail($id);
|
|
|
|
$rules = [
|
|
'name' => 'sometimes|required|string|max:255',
|
|
'barcode' => 'sometimes|required|string|max:100',
|
|
'type' => 'sometimes|required|in:normal,combo',
|
|
'unit' => 'sometimes|required|string|max:20',
|
|
'retail_price' => 'sometimes|required|numeric|min:0',
|
|
'cost_price' => 'sometimes|required|numeric|min:0',
|
|
'weight' => 'nullable|numeric|min:0',
|
|
'packaging_cost' => 'nullable|numeric|min:0',
|
|
'shipping_packaging_cost' => 'nullable|numeric|min:0',
|
|
'volume' => 'nullable|numeric|min:0',
|
|
'length' => 'nullable|numeric|min:0',
|
|
'width' => 'nullable|numeric|min:0',
|
|
'height' => 'nullable|numeric|min:0',
|
|
'brand_id' => 'nullable|exists:brands,id',
|
|
'supplier_ids' => 'nullable|array',
|
|
'supplier_ids.*' => 'exists:suppliers,id',
|
|
'batch_management' => 'nullable|boolean',
|
|
'custom_id' => 'nullable|string|max:100',
|
|
'category' => 'nullable|string|max:50',
|
|
'stock_warning' => 'nullable|integer|min:0',
|
|
];
|
|
|
|
if ($request->has('code') && $request->code != $goods->code) {
|
|
$rules['code'] = 'required|string|unique:goods,code,' . $id;
|
|
}
|
|
|
|
if ($request->type == 'combo') {
|
|
$rules['combo_items'] = 'sometimes|array|min:1';
|
|
$rules['combo_items.*.goods_id'] = 'required|exists:goods,id';
|
|
$rules['combo_items.*.quantity'] = 'required|integer|min:1';
|
|
}
|
|
|
|
$request->validate($rules);
|
|
|
|
try {
|
|
DB::beginTransaction();
|
|
|
|
$updateData = $request->only([
|
|
'name', 'code', 'barcode', 'category', 'unit', 'weight',
|
|
'retail_price', 'cost_price', 'stock_warning', 'type',
|
|
'custom_id', 'packaging_cost', 'shipping_packaging_cost',
|
|
'volume', 'length', 'width', 'height', 'brand_id', 'batch_management'
|
|
]);
|
|
$goods->update($updateData);
|
|
|
|
// 更新供应商关联
|
|
if ($request->has('supplier_ids')) {
|
|
$goods->suppliers()->sync($request->supplier_ids);
|
|
}
|
|
|
|
// 更新组合项(如果类型是组套)
|
|
if ($request->type == 'combo') {
|
|
if ($request->has('combo_items')) {
|
|
// 删除旧的组合项
|
|
$goods->comboItems()->delete();
|
|
// 添加新的
|
|
foreach ($request->combo_items as $item) {
|
|
$childGoods = Goods::find($item['goods_id']);
|
|
if ($childGoods->type == 'combo') {
|
|
throw new \Exception('组合商品不能包含另一个组合商品');
|
|
}
|
|
$goods->comboItems()->create([
|
|
'goods_id' => $item['goods_id'],
|
|
'quantity' => $item['quantity'],
|
|
]);
|
|
}
|
|
}
|
|
} else {
|
|
// 如果是普通商品,删除所有组合项
|
|
$goods->comboItems()->delete();
|
|
}
|
|
|
|
DB::commit();
|
|
|
|
return response()->json([
|
|
'code' => 200,
|
|
'message' => '更新成功'
|
|
]);
|
|
} catch (\Exception $e) {
|
|
DB::rollBack();
|
|
return response()->json([
|
|
'code' => 500,
|
|
'message' => '更新失败:' . $e->getMessage()
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 删除商品
|
|
*/
|
|
public function destroy($id)
|
|
{
|
|
$goods = Goods::findOrFail($id);
|
|
// 检查是否被组合商品引用
|
|
if ($goods->parentCombos()->exists()) {
|
|
return response()->json([
|
|
'code' => 400,
|
|
'message' => '该商品已被组合商品引用,无法删除'
|
|
], 400);
|
|
}
|
|
$goods->delete();
|
|
return response()->json([
|
|
'code' => 200,
|
|
'message' => '删除成功'
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 生成商品编码
|
|
*/
|
|
private function generateGoodsCode()
|
|
{
|
|
$prefix = 'G';
|
|
$date = date('Ymd');
|
|
$last = Goods::where('code', 'like', $prefix . $date . '%')
|
|
->orderBy('code', 'desc')
|
|
->first();
|
|
if ($last) {
|
|
$num = intval(substr($last->code, -4)) + 1;
|
|
} else {
|
|
$num = 1;
|
|
}
|
|
return $prefix . $date . str_pad($num, 4, '0', STR_PAD_LEFT);
|
|
}
|
|
} |