581 lines
21 KiB
PHP
581 lines
21 KiB
PHP
<?php
|
|
|
|
namespace app\common\service;
|
|
|
|
use app\common\model\InvItem;
|
|
use app\common\model\InvStockLog;
|
|
use app\common\validate\InvRequisitionValidate;
|
|
use app\common\traits\crud\CrudServiceTrait;
|
|
use app\common\model\InvRequisition;
|
|
use app\common\model\InvRequisitionAttachment;
|
|
use app\common\model\InvRequisitionItem;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use support\Request;
|
|
use support\Db;
|
|
use support\exception\BusinessException;
|
|
|
|
class InvRequisitionService
|
|
{
|
|
|
|
use CrudServiceTrait;
|
|
|
|
/**
|
|
* 指定当前要操作的模型
|
|
*/
|
|
protected string $modelClass = InvRequisition::class;
|
|
|
|
/**
|
|
* 验证器
|
|
*/
|
|
protected $validator = InvRequisitionValidate::class;
|
|
|
|
/**
|
|
* 获取验证器
|
|
*/
|
|
public function getValidator()
|
|
{
|
|
return new $this->validator;
|
|
}
|
|
|
|
/**
|
|
* 构造 CrudUtil 查询配置
|
|
*/
|
|
protected function buildQueryConfig(Request $request, string $scene = 'default'): array
|
|
{
|
|
$p = $request->all();
|
|
|
|
$config = [];
|
|
$config['model'] = $this->modelClass;
|
|
switch ($scene) {
|
|
case 'default':
|
|
$config['where']['status'] = 1;
|
|
$config['order']['sort'] = 'desc';
|
|
$config['order']['id'] = 'desc';
|
|
break;
|
|
case 'index':
|
|
if(isset($p['status']) && $p['status']){
|
|
$config['where']['status'] = $p['status'];
|
|
}
|
|
if (isset($p['applicant_id']) && $p['applicant_id'] == 'self') { //自己的领用申请
|
|
$config['where']['applicant_id'] = $request->user->uid;
|
|
} elseif (isset($p['applicant_id']) && $p['applicant_id']) { //指定人员的申请
|
|
$config['where']['applicant_id'] = $p['applicant_id'];
|
|
} else { //审批人对应的记录
|
|
$config['where']['approval.approver_id'] = $request->user->uid;
|
|
//$config['where']['approval.action'] = 0;
|
|
}
|
|
if(isset($p['phase']) && $p['phase'] == 'approve'){//借用审批中
|
|
$config['where']['status'] = ['in',[10,11]];
|
|
if(isset($p['scene']) && $p['scene'] == 'approval'){
|
|
$config['where']['approval.action'] = 0;
|
|
}
|
|
}elseif(isset($p['phase']) && $p['phase'] == 'repaid'){//已全部归还
|
|
$config['where']['status'] = 17;
|
|
}elseif(isset($p['phase']) && $p['phase'] == 'unreturned'){ //未归还
|
|
$config['where']['status'] = ['in',[11,12,13,14,15,16,18,19]];
|
|
}
|
|
//审核列表
|
|
elseif(isset($p['phase']) && $p['phase'] == 'pending'){ //待审核
|
|
$config['where']['status'] = ['in',[10,11]];
|
|
$config['where']['approval.approver_id'] = $request->user->uid;
|
|
$config['where']['approval.action'] = 0;
|
|
}elseif(isset($p['phase']) && $p['phase'] == 'approved'){ //已通过
|
|
$config['where']['status'] = ['in',[11,12,14,15,16,17,18,19]];
|
|
$config['where']['approval.approver_id'] = $request->user->uid;
|
|
$config['where']['approval.action'] = [">",0];
|
|
}elseif(isset($p['phase']) && $p['phase'] == 'refuse'){ //拒绝,未通过
|
|
$config['where']['status'] = ['in',[13]];
|
|
}
|
|
$config['order']['id'] = 'desc';
|
|
break;
|
|
case 'show':
|
|
$config['where']['id'] = $p['id'];
|
|
break;
|
|
case 'store':
|
|
break;
|
|
case 'update':
|
|
$config['where']['id'] = $p['id'];
|
|
break;
|
|
case 'destroy':
|
|
$config['where']['id'] = $p['id'];
|
|
break;
|
|
}
|
|
|
|
return $config;
|
|
}
|
|
|
|
/**
|
|
* 创建物品申请 前置
|
|
*
|
|
* @param array $data
|
|
* @param Request $request
|
|
* @return array
|
|
*/
|
|
public function beforeServiceStore($data, Request $request): array
|
|
{
|
|
$user = (new UserService())->getUserInfo($request->user->uid, '');
|
|
|
|
if (empty($user['user']['id']) || empty($user['user']['name'])) {
|
|
throw new BusinessException('无法获取当前用户信息');
|
|
}
|
|
|
|
$data['order_no'] = $this->generateOrderNo();
|
|
$data['applicant_id'] = (int)$user['user']['id'];
|
|
$data['applicant_name'] = (string)$user['user']['name'];
|
|
$data['leader_id'] = $user['user']['report_to'];
|
|
$data['status'] = InvRequisition::STATUS_LEADER;
|
|
|
|
//查询物品管理人是否是一个
|
|
if(count($data['items'])>=2){
|
|
$manage_user = [];
|
|
foreach ($data['items'] as $item) {
|
|
$manage_user[] = InvItem::where('id',$item['item_id'])->value('manage_user_id');
|
|
}
|
|
$manage_user = array_unique($manage_user);
|
|
if(count($manage_user)>1){
|
|
throw new BusinessException("物品管理员不是同一人");
|
|
}
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* 处理物品申请
|
|
*
|
|
* @param Request $request
|
|
* @param array $data
|
|
* @return array
|
|
*/
|
|
public function serviceStore(Request $request, array $data, string $scene = '')
|
|
{
|
|
try {
|
|
// 关联表增加事务处理
|
|
return Db::transaction(function () use ($request, $data) {
|
|
$data = $this->preProcess('Store', $data, $request);
|
|
$dl = $this->getDataLimitFromRequest($request);
|
|
$created = $this->logicCreate($data, $dl);
|
|
// 处理物品申请项
|
|
$this->processRequisitionItems($created, $data['items']);
|
|
// 处理物品申请附件
|
|
$this->processRequisitionAttachmentItems($created, $data['attachment']);
|
|
$res = $this->postProcess('Store', $created);
|
|
|
|
$manage_user_id = InvItem::where('id',$data['items'][0]['item_id'])->value('manage_user_id');
|
|
$approval = [
|
|
'id' => $res['id'], //申请单id
|
|
'applicant_id' => $res['applicant_id'], //申请人id
|
|
'manage_user_id' => $manage_user_id,
|
|
];
|
|
$this->startApproval($approval);
|
|
|
|
return $res;
|
|
});
|
|
} catch (\Throwable $e) {
|
|
throw new BusinessException("创建物品申请失败:".$e->getMessage());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 启动审批
|
|
*/
|
|
public function startApproval($requisition)
|
|
{
|
|
// 启动审批
|
|
//$appro = new \app\common\utils\approval\service\ApprovalFlowService();
|
|
$appro = new \app\common\service\InvRequisitionApproveService();
|
|
$formData = [
|
|
'manage_user_id' => $requisition['manage_user_id'],
|
|
];
|
|
return $appro->start('inv_requisition', 'inv_requisition', $requisition['id'], $formData, $requisition['applicant_id']);
|
|
}
|
|
|
|
/**
|
|
* 更新物品申请
|
|
*
|
|
* @param Request $request
|
|
* @param array $data
|
|
* @return array
|
|
*/
|
|
public function serviceUpdate(Request $request, array $data, string $scene = '')
|
|
{
|
|
try {
|
|
// 关联表增加事务处理
|
|
return Db::transaction(function () use ($request, $data) {
|
|
$data = $this->preProcess('Update', $data, $request);
|
|
$config = $this->buildQueryConfig($request, 'update');
|
|
|
|
// 获取申请项/附件
|
|
$items = $data['items'];
|
|
$attachment = $data['attachment'];
|
|
unset($data['items']);
|
|
unset($data['attachment']);
|
|
|
|
// 执行更新操作
|
|
$this->logicUpdateBy($config, $data);
|
|
|
|
// 获取更新后的完整记录
|
|
$updated = $this->logicShow($config);
|
|
|
|
// 重新创建新的申请项
|
|
$this->processRequisitionItems((object)$updated, $items, true);
|
|
|
|
// 重新创建新的附件
|
|
$this->processRequisitionAttachmentItems((object)$updated, $attachment, true);
|
|
|
|
return $this->postProcess('Update', $updated);
|
|
});
|
|
} catch (\Throwable $e) {
|
|
throw new BusinessException("更新物品申请失败:" . $e->getMessage()."|".$e->getLine()."|".$e->getFile());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 处理物品申请项
|
|
*
|
|
* @param Model $created
|
|
* @param array $items
|
|
* @return void
|
|
*/
|
|
private function processRequisitionItems(Model $created, array $items, bool $isUpdate = false): void
|
|
{
|
|
if ($isUpdate) {
|
|
$this->clearRequisitionItems($created->id);
|
|
}
|
|
|
|
foreach ($items as $item) {
|
|
$invitem = InvItem::where('id',$item)->first();
|
|
$itemData = [
|
|
'requisition_id' => $created->id,
|
|
'item_id' => $item['item_id'],
|
|
'item_name_snap' => $item['item_name_snap'], // 修改:使用 snap 字段
|
|
'spec_model_snap' => $item['spec_model_snap']??'', // 修改:使用 snap 字段
|
|
'unit_snap' => $item['unit_snap']??'', // 修改:使用 snap 字段
|
|
'is_consumable_snap' => $item['is_consumable_snap']?? ($invitem['is_consumable_snap']??0), // 修改:使用 snap 字段
|
|
'request_qty' => (int) $item['request_qty'] ?? 1, // 动态获取数量,转为整型
|
|
'approved_qty' => (int) $item['request_qty'] ?? 1, // 初始审批数量等于申请数量
|
|
'remark' => $item['remark'] ?? ''
|
|
];
|
|
InvRequisitionItem::create($itemData);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 处理物品申请项
|
|
*
|
|
* @param Model $created
|
|
* @param array $items
|
|
* @return void
|
|
*/
|
|
private function processRequisitionAttachmentItems(Model $created, array $attachment, bool $isUpdate = false): void
|
|
{
|
|
if ($isUpdate) {
|
|
$this->clearRequisitionAttachmentItems($created->id);
|
|
}
|
|
|
|
foreach ($attachment as $file) {
|
|
$attachmentData = [
|
|
'requisition_id' => $created->id,
|
|
'file_name' => $file['original_name'],
|
|
'file_path' => $file['file_path']
|
|
];
|
|
InvRequisitionAttachment::create($attachmentData);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 删除申请项
|
|
*
|
|
* @param int $requisitionId
|
|
* @return void
|
|
*/
|
|
public function clearRequisitionItems(int $requisitionId): void
|
|
{
|
|
InvRequisitionItem::where('requisition_id', $requisitionId)->delete();
|
|
}
|
|
|
|
/**
|
|
* 删除申请项
|
|
*
|
|
* @param int $requisitionId
|
|
* @return void
|
|
*/
|
|
public function clearRequisitionAttachmentItems(int $requisitionId): void
|
|
{
|
|
InvRequisitionAttachment::where('requisition_id', $requisitionId)->delete();
|
|
}
|
|
|
|
/**
|
|
* 生成申请单号
|
|
*
|
|
* @return string
|
|
*/
|
|
private function generateOrderNo(): string
|
|
{
|
|
return 'RQ' . date('Ymd') . strtoupper(substr(uniqid(), -6));
|
|
}
|
|
|
|
/**
|
|
* 扫码领取物品
|
|
*
|
|
* @param Request $request
|
|
* @param array $data
|
|
* @return array
|
|
* @throws BusinessException
|
|
*/
|
|
public function qrClaim(Request $request, array $data): array
|
|
{
|
|
return Db::transaction(function () use ($request, $data) {
|
|
$itemId = (int)($data['item_id'] ?? 0);
|
|
$qty = (int)($data['qty'] ?? 1);
|
|
|
|
if ($itemId <= 0) {
|
|
throw new BusinessException('物品ID无效');
|
|
}
|
|
if ($qty <= 0) {
|
|
throw new BusinessException('领取数量必须大于0');
|
|
}
|
|
|
|
// 获取物品信息
|
|
$item = InvItem::find($itemId);
|
|
if (!$item) {
|
|
throw new BusinessException('物品不存在');
|
|
}
|
|
|
|
// 检查是否允许扫码领取
|
|
if (!$item->allow_qr_claim) {
|
|
throw new BusinessException('该物品不允许扫码领取');
|
|
}
|
|
|
|
// 获取用户信息
|
|
$user = (new UserService())->getUserInfo($request->user->uid, '');
|
|
$applicantId = (int)$user['user']['id'];
|
|
$applicantName = (string)$user['user']['name'];
|
|
|
|
// 检查库存
|
|
if ($item->stock_qty < $qty) {
|
|
throw new BusinessException('库存不足,当前库存: ' . $item->stock_qty);
|
|
}
|
|
|
|
// 创建领用申请记录(直接完成)
|
|
$requisition = InvRequisition::create([
|
|
'order_no' => $this->generateOrderNo(),
|
|
'applicant_company_id' => $user['user']['company_id'] ?? 0,
|
|
'dept_id' => $user['user']['dept_id'] ?? 0,
|
|
'applicant_id' => $applicantId,
|
|
'applicant_name' => $applicantName,
|
|
'leader_id' => $user['user']['report_to'] ?? 0,
|
|
'manager_id' => $item->manage_user_id,
|
|
'borrow_reason' => '扫码领取',
|
|
'use_start_date' => date('Y-m-d', time()),
|
|
'use_end_date' => date('Y-m-d', time()),
|
|
'status' => 19, // 已审批(无需归还)
|
|
'claim_type' => InvRequisition::CLAIM_TYPE_QR,
|
|
'qr_claim_time' => time(),
|
|
'remark' => $data['remark'] ?? '扫码领取',
|
|
]);
|
|
|
|
// 创建领用明细
|
|
InvRequisitionItem::create([
|
|
'requisition_id' => $requisition->id,
|
|
'item_id' => $itemId,
|
|
'item_name_snap' => $item->item_name,
|
|
'spec_model_snap' => $item->spec_model,
|
|
'unit_snap' => $item->unit,
|
|
'is_consumable_snap' => $item->is_consumable,
|
|
'request_qty' => $qty,
|
|
'approved_qty' => $qty,
|
|
'remark' => '扫码领取',
|
|
]);
|
|
|
|
// 扣减库存
|
|
(new InvItemService())->stockOut($itemId, $qty, false, [
|
|
'biz_type' => InvStockLog::BIZ_TYPE_QR_CLAIM,
|
|
'biz_no' => $requisition->order_no,
|
|
'operator_id' => $applicantId,
|
|
'operator_name' => $applicantName,
|
|
'remark' => '扫码领取',
|
|
]);
|
|
|
|
return $requisition->toArray();
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 管理员代领
|
|
*
|
|
* @param Request $request
|
|
* @param array $data
|
|
* @return array
|
|
* @throws BusinessException
|
|
*/
|
|
public function proxyClaim(Request $request, array $data): array
|
|
{
|
|
return Db::transaction(function () use ($request, $data) {
|
|
$itemId = (int)($data['item_id'] ?? 0);
|
|
$qty = (int)($data['qty'] ?? 1);
|
|
$targetUserId = (int)($data['target_user_id'] ?? 0);
|
|
|
|
if ($itemId <= 0) {
|
|
throw new BusinessException('物品ID无效');
|
|
}
|
|
if ($qty <= 0) {
|
|
throw new BusinessException('领取数量必须大于0');
|
|
}
|
|
if ($targetUserId <= 0) {
|
|
throw new BusinessException('请选择代领用户');
|
|
}
|
|
|
|
// 获取物品信息
|
|
$item = InvItem::find($itemId);
|
|
if (!$item) {
|
|
throw new BusinessException('物品不存在');
|
|
}
|
|
|
|
// 获取操作人(管理员)信息
|
|
$proxyUser = (new UserService())->getUserInfo($request->user->uid, '');
|
|
$proxyUserId = (int)$proxyUser['user']['id'];
|
|
$proxyUserName = (string)$proxyUser['user']['name'];
|
|
|
|
// 验证操作人是否是物品管理员
|
|
if ($item->manage_user_id != $proxyUserId) {
|
|
throw new BusinessException('您不是该物品的管理员,无权代领');
|
|
}
|
|
|
|
// 获取目标用户信息
|
|
$targetUser = (new UserService())->getUserInfo($targetUserId, '');
|
|
if (!$targetUser['user']) {
|
|
throw new BusinessException('目标用户不存在');
|
|
}
|
|
|
|
// 检查库存
|
|
if ($item->stock_qty < $qty) {
|
|
throw new BusinessException('库存不足,当前库存: ' . $item->stock_qty);
|
|
}
|
|
|
|
// 创建领用申请记录(直接完成)
|
|
$requisition = InvRequisition::create([
|
|
'order_no' => $this->generateOrderNo(),
|
|
'applicant_company_id' => $targetUser['user']['company_id'] ?? 0,
|
|
'dept_id' => $targetUser['user']['dept_id'] ?? 0,
|
|
'applicant_id' => $targetUserId,
|
|
'applicant_name' => $targetUser['user']['name'],
|
|
'leader_id' => $targetUser['user']['report_to'] ?? 0,
|
|
'manager_id' => $item->manage_user_id,
|
|
'borrow_reason' => $data['borrow_reason'] ?? '管理员代领',
|
|
'use_start_date' => date('Y-m-d', time()),
|
|
'use_end_date' => date('Y-m-d', time()),
|
|
'status' => 19, // 已审批(无需归还)
|
|
'claim_type' => InvRequisition::CLAIM_TYPE_PROXY,
|
|
'proxy_claim_user_id' => $proxyUserId,
|
|
'proxy_claim_user_name' => $proxyUserName,
|
|
'remark' => $data['remark'] ?? '管理员代领',
|
|
]);
|
|
|
|
// 创建领用明细
|
|
InvRequisitionItem::create([
|
|
'requisition_id' => $requisition->id,
|
|
'item_id' => $itemId,
|
|
'item_name_snap' => $item->item_name,
|
|
'spec_model_snap' => $item->spec_model,
|
|
'unit_snap' => $item->unit,
|
|
'is_consumable_snap' => $item->is_consumable,
|
|
'request_qty' => $qty,
|
|
'approved_qty' => $qty,
|
|
'remark' => $data['remark'] ?? '管理员代领',
|
|
]);
|
|
|
|
// 扣减库存
|
|
(new InvItemService())->stockOut($itemId, $qty, false, [
|
|
'biz_type' => InvStockLog::BIZ_TYPE_PROXY_CLAIM,
|
|
'biz_no' => $requisition->order_no,
|
|
'operator_id' => $proxyUserId,
|
|
'operator_name' => $proxyUserName,
|
|
'remark' => '管理员代领',
|
|
]);
|
|
|
|
return $requisition->toArray();
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 查询扫码/代领记录列表
|
|
*
|
|
* @param Request $request
|
|
* @return array
|
|
*/
|
|
public function qrClaimsList(Request $request): array
|
|
{
|
|
$query = InvRequisition::query()->with(['items']);
|
|
|
|
// 只查询扫码领取和代领记录
|
|
$query->whereIn('claim_type', [
|
|
InvRequisition::CLAIM_TYPE_QR,
|
|
InvRequisition::CLAIM_TYPE_PROXY
|
|
]);
|
|
|
|
// 按申请人筛选(移动端"我的记录"功能)
|
|
if ($request->get('applicant_id') === 'self') {
|
|
$query->where('applicant_id', $request->user->uid);
|
|
} elseif (!empty($request->get('applicant_id'))) {
|
|
$query->where('applicant_id', $request->get('applicant_id'));
|
|
}
|
|
|
|
// 按领取方式筛选
|
|
if (!empty($request->get('claim_type'))) {
|
|
$query->where('claim_type', $request->get('claim_type'));
|
|
}
|
|
|
|
// 按代领操作人筛选
|
|
if (!empty($request->get('proxy_user_id'))) {
|
|
$query->where('proxy_claim_user_id', $request->get('proxy_user_id'));
|
|
}
|
|
|
|
// 时间范围筛选
|
|
if (!empty($request->get('start_time'))) {
|
|
$query->where('qr_claim_time', '>=', strtotime($request->get('start_time')));
|
|
}
|
|
if (!empty($request->get('end_time'))) {
|
|
$query->where('qr_claim_time', '<=', strtotime($request->get('end_time')));
|
|
}
|
|
|
|
// 排序
|
|
$query->orderBy('id', 'desc');
|
|
|
|
// 分页
|
|
$perPage = $request->get('size', 20);
|
|
$page = $request->get('current', 1);
|
|
|
|
$result = $query->paginate($perPage, ['*'], 'page', $page);
|
|
|
|
return [
|
|
'records' => $result->getCollection()->map(function ($req) {
|
|
return [
|
|
'id' => $req->id,
|
|
'order_no' => $req->order_no,
|
|
'applicant_id' => $req->applicant_id,
|
|
'applicant_name' => $req->applicant_name,
|
|
'claim_type' => $req->claim_type,
|
|
'claim_type_text' => InvRequisition::getClaimTypeText($req->claim_type),
|
|
'qr_claim_time' => $req->qr_claim_time ? date('Y-m-d H:i:s', $req->qr_claim_time) : null,
|
|
'proxy_claim_user_id' => $req->proxy_claim_user_id,
|
|
'proxy_claim_user_name' => $req->proxy_claim_user_name,
|
|
'status' => $req->status,
|
|
'borrow_reason' => $req->borrow_reason,
|
|
'remark' => $req->remark,
|
|
'created_at' => $req->created_at ? $req->created_at->format('Y-m-d H:i:s') : null,
|
|
'items' => $req->items->map(function ($item) {
|
|
return [
|
|
'item_id' => $item->item_id,
|
|
'item_name_snap' => $item->item_name_snap,
|
|
'approved_qty' => $item->approved_qty,
|
|
];
|
|
})->toArray(),
|
|
];
|
|
})->toArray(),
|
|
'total' => $result->total(),
|
|
'current' => $result->currentPage(),
|
|
'size' => $result->perPage(),
|
|
];
|
|
}
|
|
} |