|
@@ -1,420 +0,0 @@
|
|
|
-<?php
|
|
|
-
|
|
|
-namespace app\common\service;
|
|
|
-
|
|
|
-use Carbon\Carbon;
|
|
|
-use think\Validate;
|
|
|
-use think\facade\Db;
|
|
|
-use app\common\model\Io;
|
|
|
-use app\common\model\Good;
|
|
|
-use app\common\model\Repo;
|
|
|
-use app\common\model\Admin;
|
|
|
-use app\common\model\Stock;
|
|
|
-use app\common\model\IoDetail;
|
|
|
-use app\common\util\WhereBuilder;
|
|
|
-use app\common\util\PhpSpreadsheetExportV2;
|
|
|
-
|
|
|
-class IoService extends Service
|
|
|
-{
|
|
|
- /**
|
|
|
- * 处理分页请求
|
|
|
- *
|
|
|
- * @param array $params 请求参数数组
|
|
|
- * @return mixed 分页结果
|
|
|
- */
|
|
|
- public function page($params = [])
|
|
|
- {
|
|
|
- // 自动处理请求参数
|
|
|
- $this->autoParams($params);
|
|
|
-
|
|
|
- // 从请求参数中获取相关值
|
|
|
- $type = $this->pg('type');
|
|
|
- $change_type = $this->pg('change_type');
|
|
|
- $keyword = $this->pg('keyword');
|
|
|
- $repo_id = $this->pg('repo_id');
|
|
|
- $begin_date = $this->pg('begin_date');
|
|
|
- $end_date = $this->pg('end_date');
|
|
|
-
|
|
|
- // 如果存在结束日期,则向后延长一天
|
|
|
- if ($end_date) {
|
|
|
- $end_date = Carbon::parse($end_date)->addDay()->toDateString();
|
|
|
- }
|
|
|
-
|
|
|
- // 获取是否包含运输状态参数
|
|
|
- $contain_transit = $this->pgd(false, 'contain_transit');
|
|
|
-
|
|
|
- // 构建查询条件
|
|
|
- $where = WhereBuilder::builder()
|
|
|
- ->like('io.sn|r.name|io.remark', $keyword)
|
|
|
- ->in('io.type', $type)
|
|
|
- ->in('io.change_type', $change_type)
|
|
|
- ->in('io.repo_id', $repo_id)
|
|
|
- ->between('io.date', $begin_date, $end_date)
|
|
|
- ->where('d.transit_status', 'in', [IoDetail::TRANSIT_STATUS_TRANSIT, IoDetail::TRANSIT_STATUS_TRANSIT], $contain_transit)
|
|
|
- ->build();
|
|
|
-
|
|
|
- // 构建查询对象
|
|
|
- $query = (new Io)
|
|
|
- ->alias('io')
|
|
|
- ->field('io.*, r.name as repo_name')
|
|
|
- ->join('repo r', 'r.id = io.repo_id');
|
|
|
-
|
|
|
- // 如果包含运输状态,则进行关联查询
|
|
|
- if ($contain_transit) {
|
|
|
- $query = $query->join('io_detail d', 'd.io_id = io.id');
|
|
|
- }
|
|
|
-
|
|
|
- // 执行分页查询
|
|
|
- $page = $query->where($where)
|
|
|
- ->order('io.create_time desc')
|
|
|
- ->paginate($this->tp6Page());
|
|
|
-
|
|
|
- // 添加附加字段到分页结果集
|
|
|
- $page->getCollection()->append(['change_type_text']);
|
|
|
-
|
|
|
- // 返回分页结果
|
|
|
- return $page;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 获取Io信息
|
|
|
- *
|
|
|
- * @param array $params 请求参数数组
|
|
|
- * @return mixed Io对象
|
|
|
- */
|
|
|
- public function info($params = [])
|
|
|
- {
|
|
|
- // 自动处理请求参数
|
|
|
- $this->autoParams($params);
|
|
|
-
|
|
|
- // 查询单个Io对象
|
|
|
- $io = $this->one(Io::class);
|
|
|
-
|
|
|
- // 添加附加字段到Io对象
|
|
|
- $io->append(['change_type_text', 'details', 'repo', 'details.good']);
|
|
|
-
|
|
|
- // 返回Io对象
|
|
|
- return $io;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 创建运输记录事务
|
|
|
- *
|
|
|
- * @param Admin $admin 管理员对象
|
|
|
- * @param array $params 请求参数数组
|
|
|
- * @return mixed 运输记录创建结果
|
|
|
- */
|
|
|
- public function createTrans(Admin $admin, $params = [])
|
|
|
- {
|
|
|
- // 使用数据库事务执行创建操作
|
|
|
- return Db::transaction(fn() => $this->create($admin, $params));
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 创建运输记录
|
|
|
- *
|
|
|
- * @param Admin $admin 管理员对象
|
|
|
- * @param array $params 请求参数数组
|
|
|
- * @return mixed 创建的Io对象
|
|
|
- * @throws \Exception 创建失败时抛出异常
|
|
|
- */
|
|
|
- public function create(Admin $admin, $params = [])
|
|
|
- {
|
|
|
- // 自动处理请求参数
|
|
|
- $params = $this->autoParams($params);
|
|
|
-
|
|
|
- // 如果存在管理员对象,则将管理员ID赋值给参数数组
|
|
|
- if ($admin) {
|
|
|
- $params['admin_id'] = $admin->id;
|
|
|
- }
|
|
|
-
|
|
|
- // 查询Repo对象
|
|
|
- $repo = $this->one(Repo::class, 'repo_id');
|
|
|
-
|
|
|
- // 获取出入库明细
|
|
|
- $details = $this->pg('details');
|
|
|
- // 过滤掉数量为0的明细
|
|
|
- $details = array_filter($details, fn($detail) => $detail['num']);
|
|
|
-
|
|
|
- // 创建Io对象
|
|
|
- $io = Io::create($params);
|
|
|
-
|
|
|
- // 赋值并检查明细
|
|
|
- foreach ($details as &$detail) {
|
|
|
- $detail['io_id'] = $io->id;
|
|
|
- $detail['date'] = isset($detail['date']) && $detail['date'] ? $detail['date'] : $io->date;
|
|
|
- $detail['type'] = isset($detail['type']) && $detail['type'] ? $detail['type'] : $io->type;
|
|
|
- $detail['repo_id'] = isset($detail['repo_id']) && $detail['repo_id'] ? $detail['repo_id'] : $io->repo_id;
|
|
|
-
|
|
|
- // 检查出入库数量
|
|
|
- if ($detail['num'] == 0) {
|
|
|
- $good = Good::find($detail['good_id']);
|
|
|
- throw $this->exception("物品--{$good?->name}的出入库数量不能为0");
|
|
|
- }
|
|
|
- if ($detail['num'] > 0 && $detail['type'] == Io::TYPE_OUT) {
|
|
|
- $good = Good::find($detail['good_id']);
|
|
|
- throw $this->exception("物品--{$good?->name}的出入库数量大于0但是类型为出库");
|
|
|
- }
|
|
|
- if ($detail['num'] < 0 && $detail['type'] == Io::TYPE_IN) {
|
|
|
- $good = Good::find($detail['good_id']);
|
|
|
- throw $this->exception("物品--{$good?->name}的出入库数量小于0但是类型为入库");
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 保存出入库明细
|
|
|
- $details = (new IoDetail)->saveAll($details);
|
|
|
-
|
|
|
- // 在oninsert事件中进行库存变更
|
|
|
- $io->details = $details;
|
|
|
-
|
|
|
- // 返回创建的Io对象
|
|
|
- return $io;
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- /**
|
|
|
- * 导出运输记录
|
|
|
- *
|
|
|
- * @param array $params 请求参数数组
|
|
|
- * @return mixed 导出结果
|
|
|
- */
|
|
|
- public function export($params = [])
|
|
|
- {
|
|
|
- // 自动处理请求参数
|
|
|
- $this->autoParams($params);
|
|
|
-
|
|
|
- $type = $this->pg('type');
|
|
|
- // 获取起始日期和结束日期
|
|
|
- $begin_date = $this->pg('begin_date');
|
|
|
- $end_date = $this->pg('end_date');
|
|
|
-
|
|
|
- // 如果存在结束日期,则向后延长一天
|
|
|
- if ($end_date) {
|
|
|
- $end_date = Carbon::parse($end_date)->addDay()->toDateString();
|
|
|
- }
|
|
|
-
|
|
|
- // 构建查询条件
|
|
|
- $where = WhereBuilder::builder()
|
|
|
- ->in('io.type', $type)
|
|
|
- ->between('i.date', $begin_date, $end_date)
|
|
|
- ->build();
|
|
|
-
|
|
|
- // 导出表格的表头
|
|
|
- $header = [
|
|
|
- [
|
|
|
- '编号',
|
|
|
- '仓库名',
|
|
|
- '出入库类型',
|
|
|
- '变更原因',
|
|
|
- '备注',
|
|
|
- '来源',
|
|
|
- '创建时间',
|
|
|
- '日期',
|
|
|
- '物品名',
|
|
|
- '数量',
|
|
|
- '明细备注',
|
|
|
- '在途状态',
|
|
|
- '归还数量',
|
|
|
- '遗失数量'
|
|
|
- ]
|
|
|
- ];
|
|
|
-
|
|
|
- // 构建查询字段中的change_type_export
|
|
|
- $changeTypeField = <<<SQL
|
|
|
- CASE i.change_type
|
|
|
- WHEN 1 THEN "出入库"
|
|
|
- WHEN 2 THEN "调拨"
|
|
|
- WHEN 3 THEN "盘点"
|
|
|
- ELSE "出入库"
|
|
|
- END as change_type_export
|
|
|
- SQL;
|
|
|
-
|
|
|
- // 构建查询字段中的transit_status_export
|
|
|
- $transitStatusField = <<<SQL
|
|
|
- CASE d.transit_status
|
|
|
- WHEN "TRANSIT" THEN "在途"
|
|
|
- WHEN "COMPLETED" THEN "完成"
|
|
|
- ELSE ""
|
|
|
- END as transit_status_export
|
|
|
- SQL;
|
|
|
-
|
|
|
- // 执行查询
|
|
|
- $details = (new IoDetail)->alias('d')
|
|
|
- ->field('i.sn, r.name as repo_name, if(i.type = 1, "入库", "出库")')
|
|
|
- ->field($changeTypeField)
|
|
|
- ->field('i.remark, i.source, i.create_time, i.date')
|
|
|
- ->field('g.name as good_name, d.num, d.remark as detail_remark')
|
|
|
- ->field($transitStatusField)
|
|
|
- ->field('d.transit_received, d.transit_lost')
|
|
|
- ->join('io i', 'i.id = d.io_id', 'LEFT')
|
|
|
- ->join('repo r', 'r.id = i.repo_id', 'LEFT')
|
|
|
- ->join('good g', 'g.id = d.good_id', 'LEFT')
|
|
|
- ->where($where)
|
|
|
- ->select()
|
|
|
- ->toArray();
|
|
|
-
|
|
|
- // 导出表格文件
|
|
|
- $res = PhpSpreadsheetExportV2::outputFile(
|
|
|
- $details,
|
|
|
- $header,
|
|
|
- 'io' . date('Ymdis'),
|
|
|
- []
|
|
|
- );
|
|
|
-
|
|
|
- // 返回导出结果
|
|
|
- return $res;
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- public function completeTrans(Admin $admin, $params = [])
|
|
|
- {
|
|
|
- return Db::transaction(fn() => $this->complete($admin, $params));
|
|
|
- }
|
|
|
-
|
|
|
- public function complete(Admin $admin, $params = [])
|
|
|
- {
|
|
|
- $params = $this->autoParams($params);
|
|
|
- $this->validate($params, new CompleteValidate);
|
|
|
- $transits = $this->pg('transits');
|
|
|
- foreach ($transits as &$transit) {
|
|
|
- /**
|
|
|
- * @var IoDetail
|
|
|
- */
|
|
|
- $detail = IoDetail::find($transit['id']);
|
|
|
- $stock = (new Stock)
|
|
|
- ->where('repo_id', '=', $detail->repo_id)
|
|
|
- ->where('good_id', '=', $detail->good_id)
|
|
|
- ->find();
|
|
|
-
|
|
|
- // 检查数量
|
|
|
- $num = abs($detail->num);
|
|
|
- if ($transit['transit_received'] + $transit['transit_lost'] == $num) {
|
|
|
- $transit['transit_status'] = IoDetail::TRANSIT_STATUS_COMPLETED;
|
|
|
- } elseif ($transit['transit_received'] + $transit['transit_lost'] > $num) {
|
|
|
- throw $this->exception("到达/归还数量{$transit['transit_received']} 加 在途遗失数{$transit['transit_lost']} 大于该出入库明细的数量:{$num}");
|
|
|
- } elseif ($transit['transit_received'] < $detail->transit_received) {
|
|
|
- throw $this->exception("到达/归还数量{$transit['transit_received']} 比原来还少了!(原数量:{$detail->transit_received}) 如发现归还数量错误,请使用盘点功能");
|
|
|
- } elseif ($transit['transit_lost'] < $detail->transit_lost) {
|
|
|
- throw $this->exception("在途遗失数量{$transit['transit_lost']} 比原来还少了!(原数量:{$detail->transit_lost}) 如发现遗失后又归还,请使用盘点功能");
|
|
|
- } else {
|
|
|
- $transit['transit_status'] = isset($transit['transit_status']) ? $transit['transit_status'] : IoDetail::TRANSIT_STATUS_TRANSIT;
|
|
|
- }
|
|
|
-
|
|
|
- $stock->num += self::getTypeMul($detail->type) * ($transit['transit_received'] - $detail->transit_received);
|
|
|
- $stock->save();
|
|
|
-
|
|
|
- }
|
|
|
- (new IoDetail)->allowField(['transit_status', 'transit_received', 'transit_lost'])->saveAll($transits);
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- public function revertTrans(Admin $admin, $params = []) {
|
|
|
- return Db::transaction(fn() => $this->revert($admin, $params));
|
|
|
- }
|
|
|
-
|
|
|
- protected function revert(Admin $admin, $params = [])
|
|
|
- {
|
|
|
- $this->autoParams($params);
|
|
|
- $io = $this->one(Io::class);
|
|
|
- if ($io->revert_id) {
|
|
|
- return true;
|
|
|
- }
|
|
|
- $revertData = [
|
|
|
- 'repo_id' => $io->repo_id,
|
|
|
- 'date' => date('Ymd'),
|
|
|
- 'type' => self::inverseIoType($io['type']),
|
|
|
- 'sn' => 'REVERT_T' . hrtime(true) . '_SN' . rand(100000, 999999),
|
|
|
- 'remark' => "回滚自$io->sn",
|
|
|
- 'admin_id' => $admin->id,
|
|
|
- 'change_type' => $io->change_type,
|
|
|
- 'source' => "$io->sn"
|
|
|
- ];
|
|
|
- $revert = Io::create($revertData);
|
|
|
-
|
|
|
- $revertDetails = [];
|
|
|
- foreach ($io->details as $detail) {
|
|
|
- $revertDetail = [
|
|
|
- 'repo_id' => $detail->repo_id,
|
|
|
- 'good_id' => $detail->good_id,
|
|
|
- 'num' => -$detail['num'],
|
|
|
- 'date' => date('Ymd'),
|
|
|
- 'type' => self::inverseIoType($detail['type']),
|
|
|
- 'remark' => '回滚',
|
|
|
- 'io_id' => $revert->id,
|
|
|
- ];
|
|
|
- $revertDetails[] = $revertDetail;
|
|
|
- }
|
|
|
- $details = (new IoDetail)->saveAll($revertDetails);
|
|
|
-
|
|
|
- $io->revert_id = $revert->id;
|
|
|
- $io->save();
|
|
|
- return $revert;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 翻转出入库类型
|
|
|
- *
|
|
|
- * @param int $type 出入库类型
|
|
|
- * @return int 翻转后的出入库类型
|
|
|
- */
|
|
|
- protected static function inverseIoType(int $type)
|
|
|
- {
|
|
|
- return $type == Io::TYPE_IN ? Io::TYPE_OUT : Io::TYPE_IN;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 获取出入库类型的乘数
|
|
|
- *
|
|
|
- * @param int $type 出入库类型
|
|
|
- * @return int 出入库类型的乘数
|
|
|
- */
|
|
|
- protected static function getTypeMul(int $type)
|
|
|
- {
|
|
|
- return $type == Io::TYPE_IN ? 1 : -1;
|
|
|
- }
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
-class CreateIoValidate extends Validate
|
|
|
-{
|
|
|
- protected $rule = [
|
|
|
- 'repo_id|仓库' => 'require',
|
|
|
- 'valid|状态' => 'require',
|
|
|
- 'type|出入库类型' => 'require',
|
|
|
- 'change_type|变更类型' => 'require',
|
|
|
- 'details|出入库内容' => 'checkDetails',
|
|
|
- ];
|
|
|
-
|
|
|
- protected function checkDetails($details)
|
|
|
- {
|
|
|
- if (!is_array($details)) {
|
|
|
- return 'details必须为数组';
|
|
|
- }
|
|
|
- if (!$details) {
|
|
|
- return '出入库内容不能为空';
|
|
|
- }
|
|
|
- $setAndNotEmpty = fn($item, $index) => isset($item[$index]) && $item[$index];
|
|
|
- $requires = ['good_id', 'num', 'type', 'remark'];
|
|
|
- foreach ($details as $index => $detail) {
|
|
|
- foreach ($requires as $require) {
|
|
|
- if (!$setAndNotEmpty($detail, $require)) {
|
|
|
- return "details[$index].$require 不能为空";
|
|
|
- }
|
|
|
- }
|
|
|
- $good_id = $detail['good_id'];
|
|
|
- $good = Good::find($good_id);
|
|
|
- if (!$good) {
|
|
|
- return "未找到物品对象, id={$good_id}";
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return true;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-class CompleteValidate extends Validate
|
|
|
-{
|
|
|
- protected $rule = [
|
|
|
- 'transits' => 'array'
|
|
|
- ];
|
|
|
-}
|