回筐逻辑修改
This commit is contained in:
@@ -182,7 +182,7 @@ class OrderController extends BaseMiniController
|
||||
$previewMap = [];
|
||||
$flatItems = [];
|
||||
foreach ($paginator->getCollection() as $order) {
|
||||
foreach ($order->items as $item) {
|
||||
foreach ($order->items->take(3) as $item) {
|
||||
$flatItems[] = [
|
||||
'order_id' => $order->id,
|
||||
'product_name' => $item->product_name,
|
||||
|
||||
@@ -2,124 +2,41 @@
|
||||
|
||||
namespace App\Http\Controllers\Recon;
|
||||
|
||||
use App\Exceptions\RepositoryException;
|
||||
use App\Http\Requests\Recon\ContainerReturnFormRequest;
|
||||
use App\Models\ContainerReturnModel;
|
||||
use App\Models\StoreModel;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Modules\AnnoRoute\Attribute\DeleteRoute;
|
||||
use Modules\AnnoRoute\Attribute\GetRoute;
|
||||
use Modules\AnnoRoute\Attribute\PostRoute;
|
||||
use Modules\AnnoRoute\Attribute\RequestAttribute;
|
||||
use Modules\Common\Http\Controllers\BaseController;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* 回筐记录(压筐=生成账单自动写入只读;回筐=门店退回手动登记,扣减门店待回数量)
|
||||
* 压回筐记录(周转筐/托盘跟账单走,生成账单时自动写入完整快照,只读)
|
||||
*
|
||||
* 数量正数=压筐(附加金额),负数=回筐(抵扣金额)
|
||||
*/
|
||||
#[RequestAttribute('/recon/container-return', 'recon.containerReturn')]
|
||||
class ContainerReturnController extends BaseController
|
||||
{
|
||||
protected array $searchField = [
|
||||
'store_id' => '=',
|
||||
'type' => '=',
|
||||
'return_date' => 'betweenDate',
|
||||
'bill_id' => '=',
|
||||
'created_at' => 'betweenDate',
|
||||
];
|
||||
|
||||
/** 回筐记录列表 */
|
||||
/** 压回筐记录列表 */
|
||||
#[GetRoute(authorize: 'query')]
|
||||
public function query(Request $request): JsonResponse
|
||||
{
|
||||
$params = $request->all();
|
||||
$pageSize = $params['pageSize'] ?? 10;
|
||||
$data = $this->buildSearch($params, ContainerReturnModel::query()->with([
|
||||
'store:id,name,pending_box_num,pending_tray_num',
|
||||
'bill:id,bill_no',
|
||||
'store:id,name',
|
||||
'bill:id,bill_no,bill_date',
|
||||
'operator:id,nickname',
|
||||
]))
|
||||
->orderBy('return_date', 'desc')
|
||||
->orderBy('id', 'desc')
|
||||
->paginate($pageSize)
|
||||
->toArray();
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 回筐登记:门店退回周转筐/托盘,扣减门店待回数量(超回拒绝)
|
||||
* @throws Throwable
|
||||
*/
|
||||
#[PostRoute(authorize: 'create')]
|
||||
public function create(ContainerReturnFormRequest $request): JsonResponse
|
||||
{
|
||||
$data = $request->validated();
|
||||
$boxNum = (int) $data['box_num'];
|
||||
$trayNum = (int) $data['tray_num'];
|
||||
if ($boxNum === 0 && $trayNum === 0) {
|
||||
throw new RepositoryException('周转筐与周转托盘数量不能同时为 0');
|
||||
}
|
||||
|
||||
return DB::transaction(function () use ($data, $boxNum, $trayNum, $request) {
|
||||
$store = StoreModel::query()->lockForUpdate()->find((int) $data['store_id']);
|
||||
if (empty($store)) {
|
||||
throw new RepositoryException('门店不存在');
|
||||
}
|
||||
if ($boxNum > (int) $store->pending_box_num) {
|
||||
throw new RepositoryException(
|
||||
'回筐数量超过该门店待回筐数量(当前待回 ' . (int) $store->pending_box_num . ' 个)'
|
||||
);
|
||||
}
|
||||
if ($trayNum > (int) $store->pending_tray_num) {
|
||||
throw new RepositoryException(
|
||||
'回托盘数量超过该门店待回托盘数量(当前待回 ' . (int) $store->pending_tray_num . ' 个)'
|
||||
);
|
||||
}
|
||||
|
||||
$store->pending_box_num = (int) $store->pending_box_num - $boxNum;
|
||||
$store->pending_tray_num = (int) $store->pending_tray_num - $trayNum;
|
||||
$store->save();
|
||||
|
||||
ContainerReturnModel::create([
|
||||
'store_id' => $store->id,
|
||||
'bill_id' => 0,
|
||||
'type' => ContainerReturnModel::TYPE_RETURN,
|
||||
'box_num' => $boxNum,
|
||||
'tray_num' => $trayNum,
|
||||
'return_date' => $data['return_date'],
|
||||
'operator_id' => (int) $request->user()->id,
|
||||
'remark' => (string) ($data['remark'] ?? ''),
|
||||
]);
|
||||
|
||||
return $this->success([], '回筐已登记');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除回筐记录(仅手动登记的回筐记录可删,删除后恢复门店待回数量;压筐记录随账单生成不允许删除)
|
||||
* @throws Throwable
|
||||
*/
|
||||
#[DeleteRoute(route: '/{id}', authorize: 'delete', where: ['id' => '[0-9]+'])]
|
||||
public function delete(int $id): JsonResponse
|
||||
{
|
||||
$record = ContainerReturnModel::find($id);
|
||||
if (empty($record)) {
|
||||
throw new RepositoryException('回筐记录不存在');
|
||||
}
|
||||
if ($record->type !== ContainerReturnModel::TYPE_RETURN) {
|
||||
throw new RepositoryException('压筐记录由账单生成,不允许删除');
|
||||
}
|
||||
|
||||
return DB::transaction(function () use ($record) {
|
||||
$store = StoreModel::query()->lockForUpdate()->find($record->store_id);
|
||||
if ($store !== null) {
|
||||
$store->pending_box_num = (int) $store->pending_box_num + (int) $record->box_num;
|
||||
$store->pending_tray_num = (int) $store->pending_tray_num + (int) $record->tray_num;
|
||||
$store->save();
|
||||
}
|
||||
$record->delete();
|
||||
|
||||
return $this->success([], '回筐记录已删除,门店待回数量已恢复');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,8 @@ namespace App\Http\Requests\Purchase;
|
||||
use Modules\Common\Http\Requests\BaseFormRequest;
|
||||
|
||||
/**
|
||||
* 采购单生成账单 验证(按门店提交配送费/周转筐/托盘数量,金额由系统汇总不可修改)
|
||||
* 采购单生成账单 验证(按门店提交配送费/周转筐/托盘数量,金额由系统汇总不可修改;
|
||||
* 筐/托盘数量正数=压筐附加金额,负数=回筐抵扣金额)
|
||||
*/
|
||||
class PurchaseBillGenerateRequest extends BaseFormRequest
|
||||
{
|
||||
@@ -17,8 +18,8 @@ class PurchaseBillGenerateRequest extends BaseFormRequest
|
||||
'stores' => 'required|array|min:1',
|
||||
'stores.*.store_id' => 'required|integer|distinct',
|
||||
'stores.*.delivery_fee' => 'required|numeric|min:0',
|
||||
'stores.*.box_num' => 'required|integer|min:0',
|
||||
'stores.*.tray_num' => 'required|integer|min:0',
|
||||
'stores.*.box_num' => 'required|integer',
|
||||
'stores.*.tray_num' => 'required|integer',
|
||||
];
|
||||
}
|
||||
|
||||
@@ -35,11 +36,9 @@ class PurchaseBillGenerateRequest extends BaseFormRequest
|
||||
'stores.*.delivery_fee.numeric' => '配送费格式错误',
|
||||
'stores.*.delivery_fee.min' => '配送费不能小于 0',
|
||||
'stores.*.box_num.required' => '周转筐数量不能为空',
|
||||
'stores.*.box_num.integer' => '周转筐数量必须为整数',
|
||||
'stores.*.box_num.min' => '周转筐数量不能小于 0',
|
||||
'stores.*.box_num.integer' => '周转筐数量必须为整数(正数=压筐,负数=回筐)',
|
||||
'stores.*.tray_num.required' => '周转托盘数量不能为空',
|
||||
'stores.*.tray_num.integer' => '周转托盘数量必须为整数',
|
||||
'stores.*.tray_num.min' => '周转托盘数量不能小于 0',
|
||||
'stores.*.tray_num.integer' => '周转托盘数量必须为整数(正数=压筐,负数=回筐)',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Recon;
|
||||
|
||||
use Modules\Common\Http\Requests\BaseFormRequest;
|
||||
|
||||
/**
|
||||
* 回筐登记 验证(门店退回周转筐/托盘,扣减门店待回数量)
|
||||
*/
|
||||
class ContainerReturnFormRequest extends BaseFormRequest
|
||||
{
|
||||
protected $stopOnFirstFailure = true;
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'store_id' => 'required|integer|exists:store,id',
|
||||
'box_num' => 'required|integer|min:0',
|
||||
'tray_num' => 'required|integer|min:0',
|
||||
'return_date' => 'required|date_format:Y-m-d',
|
||||
'remark' => 'nullable|string|max:255',
|
||||
];
|
||||
}
|
||||
|
||||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'store_id.required' => '请选择门店',
|
||||
'store_id.exists' => '门店不存在',
|
||||
'box_num.required' => '周转筐数量不能为空',
|
||||
'box_num.integer' => '周转筐数量必须为整数',
|
||||
'box_num.min' => '周转筐数量不能小于 0',
|
||||
'tray_num.required' => '周转托盘数量不能为空',
|
||||
'tray_num.integer' => '周转托盘数量必须为整数',
|
||||
'tray_num.min' => '周转托盘数量不能小于 0',
|
||||
'return_date.required' => '请选择退回日期',
|
||||
'return_date.date_format' => '退回日期格式为 Y-m-d',
|
||||
'remark.max' => '备注超过最大长度',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -8,44 +8,36 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Modules\SystemUser\Models\SysUserModel;
|
||||
|
||||
/**
|
||||
* 门店回筐记录模型(压筐=生成账单时自动写入并累加门店待回;回筐=门店退回手动登记并扣减待回)
|
||||
* 门店压回筐记录模型(周转筐/托盘跟账单走,生成账单时写入完整快照)
|
||||
*
|
||||
* 数量正数=压筐(附加金额),负数=回筐(抵扣金额);数量为 0 不写记录
|
||||
*/
|
||||
class ContainerReturnModel extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/** 类型:压筐(账单生成压出,系统写入只读) */
|
||||
public const int TYPE_PRESS = 1;
|
||||
/** 类型:回筐(门店退回,后台手动登记) */
|
||||
public const int TYPE_RETURN = 2;
|
||||
|
||||
/** 类型中文名 */
|
||||
public const array TYPE_NAMES = [
|
||||
self::TYPE_PRESS => '压筐',
|
||||
self::TYPE_RETURN => '回筐',
|
||||
];
|
||||
|
||||
protected $table = 'container_return';
|
||||
protected $primaryKey = 'id';
|
||||
|
||||
protected $fillable = [
|
||||
'store_id',
|
||||
'bill_id',
|
||||
'type',
|
||||
'box_num',
|
||||
'tray_num',
|
||||
'return_date',
|
||||
'box_price',
|
||||
'tray_price',
|
||||
'amount',
|
||||
'operator_id',
|
||||
'remark',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'store_id' => 'integer',
|
||||
'bill_id' => 'integer',
|
||||
'type' => 'integer',
|
||||
'box_num' => 'integer',
|
||||
'tray_num' => 'integer',
|
||||
'return_date' => 'date:Y-m-d',
|
||||
'box_price' => 'decimal:2',
|
||||
'tray_price' => 'decimal:2',
|
||||
'amount' => 'decimal:2',
|
||||
'operator_id' => 'integer',
|
||||
'created_at' => 'datetime:Y-m-d H:i:s',
|
||||
];
|
||||
@@ -59,7 +51,7 @@ class ContainerReturnModel extends Model
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联账单(压筐记录)
|
||||
* 关联账单
|
||||
*/
|
||||
public function bill(): BelongsTo
|
||||
{
|
||||
|
||||
@@ -38,8 +38,6 @@ class StoreModel extends Model
|
||||
protected $casts = [
|
||||
'level_id' => 'integer',
|
||||
'payment_cycle_days' => 'integer',
|
||||
'pending_box_num' => 'integer',
|
||||
'pending_tray_num' => 'integer',
|
||||
'total_purchase_amount' => 'decimal:2',
|
||||
'status' => 'integer',
|
||||
];
|
||||
|
||||
@@ -18,8 +18,10 @@ use Throwable;
|
||||
* 流程(事务内):
|
||||
* 1. 锁定采购单全部有效订单,按门店分组,校验提交门店完整覆盖
|
||||
* 2. 商品金额 = 门店订单商品金额汇总(快照,生成后不可修改)
|
||||
* 3. 附加金额 = 周转筐数量×筐单价 + 托盘数量×托盘单价(单价取站点配置快照)
|
||||
* 3. 附加金额 = 周转筐数量×筐单价 + 托盘数量×托盘单价(单价取站点配置快照;
|
||||
* 数量正数=压筐附加金额,负数=回筐抵扣金额)
|
||||
* 4. 总金额 = 商品金额 + 配送费 + 附加金额;回写门店订单 bill_id 完成关联,订单状态置为已完成
|
||||
* 5. 压回筐记录:筐/托盘数量非 0 时写入完整快照(数量/单价/金额均可为负)
|
||||
*/
|
||||
readonly class BillGenerateService
|
||||
{
|
||||
@@ -122,22 +124,19 @@ readonly class BillGenerateService
|
||||
->whereIn('order_id', $storeOrders->pluck('id'))
|
||||
->update(['bill_id' => $bill->id]);
|
||||
|
||||
// 压筐:累加门店待回筐/托盘(行锁防并发),并写入压筐记录
|
||||
$store = StoreModel::query()->lockForUpdate()->find((int) $storeId);
|
||||
if ($store !== null) {
|
||||
$store->pending_box_num = (int) $store->pending_box_num + (int) $row['box_num'];
|
||||
$store->pending_tray_num = (int) $store->pending_tray_num + (int) $row['tray_num'];
|
||||
$store->save();
|
||||
// 压回筐记录:数量为 0 不写记录;正数=压筐附加金额,负数=回筐抵扣金额
|
||||
if ((int) $row['box_num'] !== 0 || (int) $row['tray_num'] !== 0) {
|
||||
ContainerReturnModel::create([
|
||||
'store_id' => (int) $storeId,
|
||||
'bill_id' => $bill->id,
|
||||
'box_num' => (int) $row['box_num'],
|
||||
'tray_num' => (int) $row['tray_num'],
|
||||
'box_price' => bcadd($boxPrice, '0', 2),
|
||||
'tray_price' => bcadd($trayPrice, '0', 2),
|
||||
'amount' => $addedAmount,
|
||||
'operator_id' => $operatorId,
|
||||
]);
|
||||
}
|
||||
ContainerReturnModel::create([
|
||||
'store_id' => (int) $storeId,
|
||||
'bill_id' => $bill->id,
|
||||
'type' => ContainerReturnModel::TYPE_PRESS,
|
||||
'box_num' => (int) $row['box_num'],
|
||||
'tray_num' => (int) $row['tray_num'],
|
||||
'return_date' => $billDate,
|
||||
'operator_id' => $operatorId,
|
||||
]);
|
||||
|
||||
$bills[] = $bill;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user