Merge branch 'develop' into feature/mzl_dev
This commit is contained in:
@@ -47,7 +47,9 @@ class ApprovalRecordsController extends BaseController
|
||||
|
||||
'step_index' => $record['step_index'],
|
||||
'action' => $record['action'],
|
||||
'status' => SysDictItem::where('item_value', $record['status'])->where('status', 1)->value('item_label'),
|
||||
'action_desc' => SysDictItem::where('item_value', $record['action'])->where('status', 1)->value('item_label'),
|
||||
'status' => $record['status'],
|
||||
'status_desc' => SysDictItem::where('item_value', $record['status'])->where('status', 1)->value('item_label'),
|
||||
'comment' => $record['comment'],
|
||||
'approved_at' => $record['approved_at'],
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ use app\common\model\StockOrders;
|
||||
use app\common\model\SysCurrency;
|
||||
use support\Request;
|
||||
use app\common\model\User;
|
||||
use support\exception\BusinessException;
|
||||
use support\Db;
|
||||
|
||||
class StockOrdersService
|
||||
@@ -63,6 +62,10 @@ class StockOrdersService
|
||||
}
|
||||
|
||||
$config['order']['id'] = 'desc';
|
||||
|
||||
// $config['dataLimit']['type'] = 'relation';
|
||||
// $config['dataLimit']['userId'] = $request->user->uid;
|
||||
// $config['dataLimit']['userField'] = 'salesperson_id';
|
||||
break;
|
||||
case 'print':
|
||||
$config['where']['id'] = $p['id'];
|
||||
|
||||
@@ -112,7 +112,7 @@ class ApprovalFlowService
|
||||
if ((int)$instance->current_step !== $idx) {
|
||||
// return ['status' => 'noop', 'reason' => 'not_current_step', 'current_step' => (int)$instance->current_step];
|
||||
// throw new BusinessException("非当前步骤");
|
||||
return ['status' => 'noop', 'msg' => '非当前步骤'];
|
||||
return ['status' => 'noop', 'msg' => '非当前审批'];
|
||||
}
|
||||
// return ['status' => 'noop', 'reason' => 'no_pending_record'];
|
||||
return ['status' => 'noop', 'msg' => '无待处理记录'];
|
||||
@@ -136,7 +136,7 @@ class ApprovalFlowService
|
||||
|
||||
if ($outcome === 'pending') {
|
||||
// return ['status' => 'step_pending'];
|
||||
return ['status' => 'step_pending', 'msg' => '步骤待定'];
|
||||
return ['status' => 'step_pending', 'msg' => '审批待定'];
|
||||
}
|
||||
|
||||
// 3) 有结论:关闭本步其余 pending
|
||||
@@ -386,10 +386,11 @@ class ApprovalFlowService
|
||||
|
||||
$resolved = array_unique(array_filter(array_map('intval', $resolved)));
|
||||
if ($avoidSelf) {
|
||||
$resolved = array_values(array_filter($resolved, fn($id) => $id !== (int)$instance->initiator_id));
|
||||
// $resolved = array_values(array_filter($resolved, fn($id) => $id !== (int)$instance->initiator_id));
|
||||
$resolved = array_values($resolved);
|
||||
}
|
||||
if (empty($resolved)) {
|
||||
throw new BusinessException("步骤未解析到任何审批人");
|
||||
throw new BusinessException("未解析到任何审批人");
|
||||
}
|
||||
return $resolved;
|
||||
}
|
||||
|
||||
@@ -1,751 +0,0 @@
|
||||
<?php
|
||||
namespace app\common\utils\approval\service;
|
||||
|
||||
use support\exception\BusinessException;
|
||||
use support\Db;
|
||||
|
||||
/**
|
||||
* 审批流程服务(Webman + Illuminate DB)
|
||||
* - 支持条件步骤、会签(any/all)、并发安全(事务+锁)、抄送、超时处理
|
||||
* - 统一状态:
|
||||
* 实例 status: processing | approved | rejected | revoked | timed_out
|
||||
* 记录 status: pending | approved | rejected | skipped | timeout
|
||||
* 记录 action: approve | reject | null
|
||||
*/
|
||||
class ApprovalFlowService_bak
|
||||
{
|
||||
// === 对外方法 ===
|
||||
|
||||
/**
|
||||
* 启动审批流程
|
||||
*/
|
||||
public function start(string $flowType, string $bizType, string $bizId, array $formData, int $initiatorId): array
|
||||
{
|
||||
return Db::transaction(function () use ($flowType, $bizType, $bizId, $formData, $initiatorId) {
|
||||
// 1) 流程定义
|
||||
$flow = Db::table('approval_flows')
|
||||
->where('type', $flowType)
|
||||
->where('is_active', 1)
|
||||
->first();
|
||||
|
||||
if (!$flow) {
|
||||
throw new BusinessException("审批流程未找到或已停用: {$flowType}");
|
||||
}
|
||||
|
||||
$steps = json_decode($flow->steps, true);
|
||||
if (!is_array($steps) || empty($steps)) {
|
||||
throw new BusinessException("流程步骤配置错误");
|
||||
}
|
||||
|
||||
// 2) 寻找首个有效步骤索引(考虑条件)
|
||||
$firstIndex = $this->getNextStepIndex($steps, $formData, -1);
|
||||
if ($firstIndex === null) {
|
||||
throw new BusinessException("未找到可执行的审批步骤");
|
||||
}
|
||||
$firstStep = $steps[$firstIndex];
|
||||
|
||||
// 3) 计算实例截止时间(实例级,可扩展到步骤级)
|
||||
$deadlineAt = null;
|
||||
if (($flow->timeout_hours ?? 0) > 0) {
|
||||
$deadlineAt = date('Y-m-d H:i:s', time() + $flow->timeout_hours * 3600);
|
||||
}
|
||||
|
||||
// 4) 创建审批实例
|
||||
$now = date('Y-m-d H:i:s');
|
||||
$instanceId = Db::table('approval_instances')->insertGetId([
|
||||
'flow_type' => $flowType,
|
||||
'biz_type' => $bizType,
|
||||
'biz_id' => $bizId,
|
||||
'form_data' => json_encode($formData, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
|
||||
'initiator_id' => $initiatorId,
|
||||
'status' => 'processing',
|
||||
'current_step' => $firstIndex,
|
||||
'deadline_at' => $deadlineAt,
|
||||
'created_at' => $now,
|
||||
'updated_at' => $now,
|
||||
]);
|
||||
|
||||
// 5) 创建首步待办
|
||||
$instance = (object)[
|
||||
'id' => $instanceId,
|
||||
'flow_type' => $flowType,
|
||||
'form_data' => json_encode($formData, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
|
||||
'initiator_id' => $initiatorId,
|
||||
'current_step' => $firstIndex,
|
||||
'status' => 'processing',
|
||||
];
|
||||
$approverIds = $this->resolveApprovers($firstStep, $instance, true);
|
||||
|
||||
foreach ($approverIds as $uid) {
|
||||
// 唯一键会防重复
|
||||
$this->insertApprovalRecord($instanceId, $firstIndex, $uid);
|
||||
}
|
||||
|
||||
// 6) 通知审批人 & 抄送
|
||||
$this->sendNotification($approverIds, $instance, 'pending', false);
|
||||
$this->notifyCCs($firstStep, $instance, 'pending');
|
||||
|
||||
return ['instance_id' => $instanceId];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行审批
|
||||
*/
|
||||
public function approve(int $instanceId, int $approverId, string $action, string $comment = '')
|
||||
{
|
||||
if (!in_array($action, ['approve', 'reject'], true)) {
|
||||
throw new BusinessException("无效的审批操作");
|
||||
}
|
||||
|
||||
return Db::transaction(function () use ($instanceId, $approverId, $action, $comment) {
|
||||
// 锁实例,保证并发安全
|
||||
$instance = Db::table('approval_instances')->where('id', $instanceId)->lockForUpdate()->first();
|
||||
if (!$instance) {
|
||||
throw new BusinessException("审批实例未找到");
|
||||
}
|
||||
if ($instance->status !== 'processing') {
|
||||
throw new BusinessException("流程已结束或不可审批");
|
||||
}
|
||||
|
||||
// 读取流程与当前步
|
||||
$flow = Db::table('approval_flows')->where('type', $instance->flow_type)->first();
|
||||
if (!$flow) throw new BusinessException("审批流程定义未找到");
|
||||
|
||||
$steps = json_decode($flow->steps, true);
|
||||
$idx = (int)$instance->current_step;
|
||||
$step = $steps[$idx] ?? null;
|
||||
if (!$step) throw new BusinessException("当前步骤配置错误");
|
||||
|
||||
// 锁当前步所有记录,避免同时推进
|
||||
$pendingRecord = Db::table('approval_records')
|
||||
->where('instance_id', $instanceId)
|
||||
->where('approver_id', $approverId)
|
||||
->where('step_index', $idx)
|
||||
->where('status', 'pending')
|
||||
->lockForUpdate()
|
||||
->first();
|
||||
if (!$pendingRecord) {
|
||||
throw new BusinessException("未找到待审批记录");
|
||||
}
|
||||
|
||||
$now = date('Y-m-d H:i:s');
|
||||
|
||||
// 1) 更新当前人的审批记录
|
||||
Db::table('approval_records')->where('id', $pendingRecord->id)->update([
|
||||
'action' => $action,
|
||||
'status' => ($action === 'approve') ? 'approved' : 'rejected',
|
||||
'comment' => $comment,
|
||||
'approved_at' => $now,
|
||||
'updated_at' => $now,
|
||||
]);
|
||||
|
||||
// 2) 聚合判断本步骤结果
|
||||
$outcome = $this->getStepOutcome($instanceId, $idx); // 'pending'|'approve'|'reject'
|
||||
$type = $step['type'] ?? 'all';
|
||||
|
||||
$stepDone = false; $finalReject = false; $finalApprove = false;
|
||||
if ($type === 'any') {
|
||||
if ($outcome === 'reject') { $stepDone = true; $finalReject = true; }
|
||||
elseif ($outcome === 'approve') { $stepDone = true; $finalApprove = true; }
|
||||
} else { // all
|
||||
if ($outcome === 'reject') { $stepDone = true; $finalReject = true; }
|
||||
elseif ($outcome === 'approve') { $stepDone = true; $finalApprove = true; }
|
||||
}
|
||||
|
||||
if (!$stepDone) {
|
||||
return ['status' => 'step_pending'];
|
||||
}
|
||||
|
||||
// 3) 本步已有结论:关闭其余 pending 待办为 skipped
|
||||
$this->closeOtherPending($instanceId, $idx, [$approverId]);
|
||||
|
||||
// 4) 步骤结论为拒绝 -> 结束
|
||||
if ($finalReject) {
|
||||
Db::table('approval_instances')->where('id', $instanceId)->update([
|
||||
'status' => 'rejected',
|
||||
'finished_at' => $now,
|
||||
'updated_at' => $now,
|
||||
]);
|
||||
$this->sendNotification([$instance->initiator_id], $instance, 'reject', false);
|
||||
return ['status' => 'completed', 'result' => 'reject'];
|
||||
}
|
||||
|
||||
// 5) 进入下一步(基于表单快照与条件继续寻找)
|
||||
$formData = json_decode($instance->form_data, true) ?: [];
|
||||
$nextIndex = $this->getNextStepIndex($steps, $formData, $idx);
|
||||
if ($nextIndex === null) {
|
||||
// 流程结束(全通过)
|
||||
Db::table('approval_instances')->where('id', $instanceId)->update([
|
||||
'status' => 'approved',
|
||||
'finished_at' => $now,
|
||||
'updated_at' => $now,
|
||||
]);
|
||||
$this->sendNotification([$instance->initiator_id], $instance, 'approve', false);
|
||||
return ['status' => 'completed', 'result' => 'approve'];
|
||||
}
|
||||
|
||||
// 6) 推进实例 current_step(乐观条件)
|
||||
$affected = Db::table('approval_instances')
|
||||
->where('id', $instanceId)
|
||||
->where('current_step', $idx)
|
||||
->where('status', 'processing')
|
||||
->update([
|
||||
'current_step' => $nextIndex,
|
||||
'updated_at' => $now,
|
||||
]);
|
||||
if (!$affected) {
|
||||
throw new BusinessException('流程已被他人推进,请刷新后重试');
|
||||
}
|
||||
|
||||
// 7) 生成下一步待办 + 通知
|
||||
$nextStep = $steps[$nextIndex];
|
||||
$nextApprovers = $this->resolveApprovers($nextStep, $instance, true);
|
||||
foreach ($nextApprovers as $uid) {
|
||||
$this->insertApprovalRecord($instanceId, $nextIndex, $uid);
|
||||
}
|
||||
$this->sendNotification($nextApprovers, $instance, 'pending', false);
|
||||
$this->notifyCCs($nextStep, $instance, 'pending');
|
||||
|
||||
return ['status' => 'next_step', 'step_name' => $nextStep['name'] ?? ('Step '.$nextIndex)];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 撤回流程(可选)
|
||||
* 仅限发起人在第一条“已处理记录”出现前撤回
|
||||
*/
|
||||
public function revoke(int $instanceId, int $initiatorId, string $reason = '')
|
||||
{
|
||||
return Db::transaction(function () use ($instanceId, $initiatorId, $reason) {
|
||||
$instance = Db::table('approval_instances')->where('id', $instanceId)->lockForUpdate()->first();
|
||||
if (!$instance) throw new BusinessException('审批实例未找到');
|
||||
if ((int)$instance->initiator_id !== (int)$initiatorId) throw new BusinessException('无权撤回');
|
||||
|
||||
if ($instance->status !== 'processing') throw new BusinessException('流程已结束或不可撤回');
|
||||
|
||||
// 已有处理记录则不允许撤回
|
||||
$handled = Db::table('approval_records')
|
||||
->where('instance_id', $instanceId)
|
||||
->whereIn('status', ['approved','rejected','timeout','skipped'])
|
||||
->count() > 0;
|
||||
if ($handled) throw new BusinessException('当前进度不可撤回');
|
||||
|
||||
$now = date('Y-m-d H:i:s');
|
||||
Db::table('approval_instances')->where('id', $instanceId)->update([
|
||||
'status' => 'revoked',
|
||||
'finished_at' => $now,
|
||||
'updated_at' => $now,
|
||||
]);
|
||||
|
||||
// 关闭所有 pending 待办
|
||||
Db::table('approval_records')
|
||||
->where('instance_id', $instanceId)
|
||||
->where('status', 'pending')
|
||||
->update(['status' => 'skipped', 'approved_at' => $now, 'updated_at' => $now]);
|
||||
|
||||
$this->sendNotification([$initiatorId], $instance, 'revoked', false);
|
||||
return ['status' => 'completed', 'result' => 'revoked'];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 我的待办
|
||||
*/
|
||||
public function getPendingApprovals(int $userId): array
|
||||
{
|
||||
return Db::table('approval_records as ar')
|
||||
->join('approval_instances as ai', 'ar.instance_id', '=', 'ai.id')
|
||||
->where('ar.approver_id', $userId)
|
||||
->where('ar.status', 'pending')
|
||||
->where('ai.status', 'processing')
|
||||
->select('ai.*')
|
||||
->distinct()
|
||||
->get()
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 审批历史
|
||||
*/
|
||||
public function getApprovalHistory(int $instanceId): array
|
||||
{
|
||||
return Db::table('approval_records')
|
||||
->where('instance_id', $instanceId)
|
||||
->orderBy('step_index')
|
||||
->orderByRaw('COALESCE(approved_at, created_at) asc')
|
||||
->get()
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 流程图数据(识别 skipped)
|
||||
*/
|
||||
public function getFlowDiagramData(int $instanceId): array
|
||||
{
|
||||
$instance = Db::table('approval_instances')->find($instanceId);
|
||||
if (!$instance) return [];
|
||||
|
||||
$flow = Db::table('approval_flows')->where('type', $instance->flow_type)->first();
|
||||
if (!$flow) return [];
|
||||
|
||||
$steps = json_decode($flow->steps, true) ?: [];
|
||||
$records = Db::table('approval_records')
|
||||
->where('instance_id', $instanceId)
|
||||
->orderBy('step_index')->orderByRaw('COALESCE(approved_at, created_at) asc')
|
||||
->get()
|
||||
->groupBy('step_index');
|
||||
|
||||
$formData = json_decode($instance->form_data, true) ?: [];
|
||||
$data = [];
|
||||
foreach ($steps as $i => $step) {
|
||||
$actions = ($records->get($i) ?? collect())->toArray();
|
||||
|
||||
$status = 'pending';
|
||||
if (!empty($actions)) {
|
||||
$hasReject = false; $allApproved = true;
|
||||
foreach ($actions as $a) {
|
||||
if ($a->status === 'rejected') $hasReject = true;
|
||||
if ($a->status !== 'approved') $allApproved = false;
|
||||
}
|
||||
if ($hasReject) $status = 'rejected';
|
||||
elseif ($allApproved) $status = 'approved';
|
||||
else $status = 'processing';
|
||||
} else {
|
||||
// 无记录:可能被跳过、未到达、已过
|
||||
if ($i < (int)$instance->current_step) {
|
||||
// 如果条件不满足,认为 skipped,否则视为 approved(历史兼容)
|
||||
if (isset($step['condition']) && !$this->evaluateCondition($step['condition'], $formData)) {
|
||||
$status = 'skipped';
|
||||
} else {
|
||||
$status = 'approved';
|
||||
}
|
||||
} elseif ($i === (int)$instance->current_step && $instance->status === 'processing') {
|
||||
$status = 'processing';
|
||||
} else {
|
||||
// 还未到达
|
||||
$status = 'pending';
|
||||
}
|
||||
}
|
||||
|
||||
$data[] = [
|
||||
'index' => $i,
|
||||
'name' => $step['name'] ?? ('Step '.$i),
|
||||
'type' => $step['type'] ?? 'all',
|
||||
'status' => $status,
|
||||
'actions' => $actions,
|
||||
];
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 定时任务:处理超时实例(实例级)
|
||||
*/
|
||||
public function handleTimeouts()
|
||||
{
|
||||
$now = date('Y-m-d H:i:s');
|
||||
$expired = Db::table('approval_instances')
|
||||
->where('status', 'processing')
|
||||
->whereNotNull('deadline_at')
|
||||
->where('deadline_at', '<=', $now)
|
||||
->get();
|
||||
|
||||
foreach ($expired as $instance) {
|
||||
Db::transaction(function () use ($instance, $now) {
|
||||
$flow = Db::table('approval_flows')->where('type', $instance->flow_type)->first();
|
||||
if (!$flow) return;
|
||||
|
||||
$final = ($flow->timeout_action === 'auto_approve') ? 'approved' : 'rejected';
|
||||
$finalAction = ($flow->timeout_action === 'auto_approve') ? 'approve' : 'reject';
|
||||
|
||||
// 更新实例
|
||||
Db::table('approval_instances')->where('id', $instance->id)->update([
|
||||
'status' => ($final === 'approved') ? 'approved' : 'rejected',
|
||||
'finished_at' => $now,
|
||||
'updated_at' => $now,
|
||||
]);
|
||||
|
||||
// 关闭当前步所有 pending 记录为 timeout
|
||||
Db::table('approval_records')
|
||||
->where('instance_id', $instance->id)
|
||||
->where('step_index', $instance->current_step)
|
||||
->where('status', 'pending')
|
||||
->update(['status' => 'timeout', 'approved_at' => $now, 'updated_at' => $now]);
|
||||
|
||||
$this->notifyInitiator($instance, $finalAction);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// === 内部工具 ===
|
||||
|
||||
/**
|
||||
* 解析审批人
|
||||
* $avoidSelf:是否过滤发起人本人
|
||||
*/
|
||||
private function resolveApprovers(array $step, object $instance, bool $avoidSelf = true): array
|
||||
{
|
||||
$resolved = [];
|
||||
$formData = json_decode($instance->form_data ?? '{}', true) ?: [];
|
||||
|
||||
$confs = $step['approvers'] ?? [];
|
||||
foreach ($confs as $cfg) {
|
||||
$typ = $cfg['type'] ?? '';
|
||||
$val = $cfg['value'] ?? null;
|
||||
$ids = [];
|
||||
|
||||
switch ($typ) {
|
||||
case 'role':
|
||||
// $ids = $this->getUserIdsByRole($val);
|
||||
|
||||
// 支持:
|
||||
// - {"type":"role","value":1} // 按 role_id
|
||||
// - {"type":"role","value":"销售经理"} // 按 roles.name
|
||||
// - {"type":"role","value":"销售经理","system_id": 2} // 限定系统;默认也包含全局(system_id=0)
|
||||
$val = $cfg['value'] ?? null;
|
||||
$sys = isset($cfg['system_id']) ? (int)$cfg['system_id'] : null;
|
||||
// include_global 默认 true(把 system_id=0 的“全局角色”也算上)
|
||||
$includeGlobal = array_key_exists('include_global', $cfg) ? (bool)$cfg['include_global'] : true;
|
||||
$ids = $this->getUserIdsByRole($val, $sys, true, $includeGlobal);
|
||||
|
||||
break;
|
||||
case 'user':
|
||||
$ids = [(int)$val];
|
||||
break;
|
||||
case 'initiator_leader':
|
||||
$leaderId = $this->getLeaderIdByUserId((int)$instance->initiator_id);
|
||||
if ($leaderId) $ids = [(int)$leaderId];
|
||||
break;
|
||||
case 'field_user':
|
||||
if (isset($formData[$val])) $ids = [(int)$formData[$val]];
|
||||
break;
|
||||
case 'dept_head':
|
||||
$head = $this->getDeptHeadId((int)$val);
|
||||
if ($head) $ids = [(int)$head];
|
||||
break;
|
||||
default:
|
||||
throw new BusinessException("不支持的审批人类型: {$typ}");
|
||||
}
|
||||
$resolved = array_merge($resolved, $ids);
|
||||
}
|
||||
|
||||
$resolved = array_unique(array_filter(array_map('intval', $resolved)));
|
||||
if ($avoidSelf) {
|
||||
$resolved = array_values(array_filter($resolved, fn($id) => $id !== (int)$instance->initiator_id));
|
||||
}
|
||||
if (empty($resolved)) {
|
||||
throw new BusinessException("步骤未解析到任何审批人");
|
||||
}
|
||||
return $resolved;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通知抄送人
|
||||
*/
|
||||
private function notifyCCs(array $step, object $instance, string $action = 'pending'): void
|
||||
{
|
||||
$ccConfig = $step['cc'] ?? [];
|
||||
if (empty($ccConfig)) return;
|
||||
|
||||
$formData = json_decode($instance->form_data ?? '{}', true) ?: [];
|
||||
$uids = [];
|
||||
|
||||
foreach ($ccConfig as $cc) {
|
||||
$typ = $cc['type'] ?? '';
|
||||
$val = $cc['value'] ?? null;
|
||||
|
||||
switch ($typ) {
|
||||
case 'role':
|
||||
$uids = array_merge($uids, $this->getUserIdsByRole($val));
|
||||
break;
|
||||
case 'user':
|
||||
$uids[] = (int)$val;
|
||||
break;
|
||||
case 'initiator_leader':
|
||||
$leaderId = $this->getLeaderIdByUserId((int)$instance->initiator_id);
|
||||
if ($leaderId) $uids[] = (int)$leaderId;
|
||||
break;
|
||||
case 'field_user':
|
||||
if (isset($formData[$val])) $uids[] = (int)$formData[$val];
|
||||
break;
|
||||
case 'dept_head':
|
||||
$head = $this->getDeptHeadId((int)$val);
|
||||
if ($head) $uids[] = (int)$head;
|
||||
break;
|
||||
default:
|
||||
// ignore
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$uids = array_unique(array_filter(array_map('intval', $uids)));
|
||||
if (empty($uids)) return;
|
||||
|
||||
$now = date('Y-m-d H:i:s');
|
||||
foreach ($uids as $uid) {
|
||||
// 唯一键防重;这里仍做一次 exists 判断
|
||||
$exists = Db::table('approval_ccs')
|
||||
->where('instance_id', $instance->id)
|
||||
->where('user_id', $uid)
|
||||
->count() > 0;
|
||||
if (!$exists) {
|
||||
Db::table('approval_ccs')->insert([
|
||||
'instance_id' => $instance->id,
|
||||
'user_id' => $uid,
|
||||
'notified_at' => $now,
|
||||
'created_at' => $now,
|
||||
'updated_at' => $now,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
$this->sendNotification($uids, $instance, $action, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 条件求值:支持 {field} {op} {value}
|
||||
* op: ==, !=, >, <, >=, <=
|
||||
*/
|
||||
private function evaluateCondition(?string $condition, array $formData): bool
|
||||
{
|
||||
if (!$condition) return true;
|
||||
|
||||
$pattern = '/^(\w+)\s*(==|!=|>|<|>=|<=)\s*(.+)$/';
|
||||
if (!preg_match($pattern, $condition, $m)) {
|
||||
throw new BusinessException("条件格式错误: {$condition}");
|
||||
}
|
||||
|
||||
$field = $m[1];
|
||||
$op = $m[2];
|
||||
$value = trim($m[3], "\"'");
|
||||
|
||||
$actual = $formData[$field] ?? null;
|
||||
|
||||
// 数值比较
|
||||
if (is_numeric($value) && is_numeric($actual)) {
|
||||
$value = (float)$value;
|
||||
$actual = (float)$actual;
|
||||
}
|
||||
|
||||
switch ($op) {
|
||||
case '==': return $actual == $value;
|
||||
case '!=': return $actual != $value;
|
||||
case '>': return $actual > $value;
|
||||
case '<': return $actual < $value;
|
||||
case '>=': return $actual >= $value;
|
||||
case '<=': return $actual <= $value;
|
||||
}
|
||||
throw new BusinessException("不支持的操作符: {$op}");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取下一个有效步骤索引(考虑 condition)
|
||||
* @param int $currentStepIndex 从该索引之后开始找;如果传 -1 则从开头找“首个有效”
|
||||
*/
|
||||
private function getNextStepIndex(array $steps, array $formData, int $currentStepIndex): ?int
|
||||
{
|
||||
for ($i = $currentStepIndex + 1; $i < count($steps); $i++) {
|
||||
$s = $steps[$i];
|
||||
if (!isset($s['condition']) || $this->evaluateCondition($s['condition'], $formData)) {
|
||||
return $i;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 聚合本步骤结论
|
||||
* 返回:'pending' | 'approve' | 'reject'
|
||||
*/
|
||||
private function getStepOutcome(int $instanceId, int $stepIndex): string
|
||||
{
|
||||
$records = Db::table('approval_records')
|
||||
->where('instance_id', $instanceId)
|
||||
->where('step_index', $stepIndex)
|
||||
->get();
|
||||
|
||||
$hasPending = false; $hasReject = false; $allApproved = true;
|
||||
|
||||
foreach ($records as $r) {
|
||||
if ($r->status === 'pending') { $hasPending = true; $allApproved = false; }
|
||||
elseif ($r->status === 'rejected') { $hasReject = true; $allApproved = false; }
|
||||
elseif ($r->status !== 'approved') { $allApproved = false; }
|
||||
}
|
||||
|
||||
if ($hasPending) return 'pending';
|
||||
if ($hasReject) return 'reject';
|
||||
return $allApproved ? 'approve' : 'reject';
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭本步其余待办(标记为 skipped)
|
||||
*/
|
||||
private function closeOtherPending(int $instanceId, int $stepIndex, array $keepApproverIds = []): void
|
||||
{
|
||||
$now = date('Y-m-d H:i:s');
|
||||
$q = Db::table('approval_records')
|
||||
->where('instance_id', $instanceId)
|
||||
->where('step_index', $stepIndex)
|
||||
->where('status', 'pending');
|
||||
|
||||
if (!empty($keepApproverIds)) {
|
||||
$q->whereNotIn('approver_id', $keepApproverIds);
|
||||
}
|
||||
|
||||
$q->update([
|
||||
'status' => 'skipped',
|
||||
'approved_at' => $now,
|
||||
'updated_at' => $now,
|
||||
]);
|
||||
}
|
||||
|
||||
private function insertApprovalRecord(int $instanceId, int $stepIndex, int $approverId): void
|
||||
{
|
||||
$now = date('Y-m-d H:i:s');
|
||||
|
||||
// 防重复:唯一键兜底,这里先查一次
|
||||
$exists = Db::table('approval_records')
|
||||
->where('instance_id', $instanceId)
|
||||
->where('step_index', $stepIndex)
|
||||
->where('approver_id', $approverId)
|
||||
->count() > 0;
|
||||
|
||||
if (!$exists) {
|
||||
Db::table('approval_records')->insert([
|
||||
'instance_id' => $instanceId,
|
||||
'approver_id' => $approverId,
|
||||
'step_index' => $stepIndex,
|
||||
'action' => null, // 待办阶段无动作
|
||||
'status' => 'pending', // 明确 pending
|
||||
'comment' => null,
|
||||
'approved_at' => null, // 未审批为 NULL
|
||||
'created_at' => $now,
|
||||
'updated_at' => $now,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
private function sendNotification(array $userIds, object $instance, string $action, bool $isCC): void
|
||||
{
|
||||
$map = [
|
||||
'pending' => '待审批',
|
||||
'approve' => '已通过',
|
||||
'reject' => '已拒绝',
|
||||
'revoked' => '已撤回',
|
||||
];
|
||||
$prefix = $isCC ? '[抄送]' : '';
|
||||
$text = $map[$action] ?? $action;
|
||||
$msg = "{$prefix}您有一条审批 {$text}:流程ID {$instance->id},类型 {$instance->flow_type}";
|
||||
// TODO: 接入你的消息系统(站内信/企微/飞书…)
|
||||
// MessageService::send($userIds, $msg);
|
||||
// echo "通知 ".implode(',', $userIds).":{$msg}\n";
|
||||
}
|
||||
|
||||
private function notifyInitiator(object $instance, string $result): void
|
||||
{
|
||||
$this->sendNotification([(int)$instance->initiator_id], $instance, $result, false);
|
||||
}
|
||||
|
||||
// === 数据访问 ===
|
||||
|
||||
// private function getUserIdsByRole(string $roleCode): array
|
||||
// {
|
||||
// // 兼容 pluck 行为(不同版本可能返回 array 或 Collection)
|
||||
// $rows = Db::table('user_roles as ur')
|
||||
// ->join('roles as r', 'ur.role_id', '=', 'r.id')
|
||||
// ->where('r.code', $roleCode)
|
||||
// ->select('ur.user_id')
|
||||
// ->get();
|
||||
|
||||
// $ids = [];
|
||||
// foreach ($rows as $row) {
|
||||
// $ids[] = (int)$row->user_id;
|
||||
// }
|
||||
// return array_values(array_unique($ids));
|
||||
// }
|
||||
|
||||
/**
|
||||
* 从角色取用户ID列表
|
||||
*
|
||||
* @param mixed $roleIdOrName 数字 => role_id,字符串 => roles.name 精确匹配
|
||||
* @param int|null $systemId 可选:限定 roles.system_id;默认不传=不过滤
|
||||
* @param bool $onlyActive 是否仅取 status=1 的角色(默认 true)
|
||||
* @param bool $includeGlobal 是否包含全局角色(system_id=0),默认 true
|
||||
* @return int[]
|
||||
*/
|
||||
private function getUserIdsByRole($roleIdOrName, ?int $systemId = null, bool $onlyActive = true, bool $includeGlobal = true): array
|
||||
{
|
||||
// 查询构造
|
||||
// 情况A:纯数字 => 当作 role_id,优先直接走 user_roles(role_id) 索引
|
||||
if (is_int($roleIdOrName) || (is_string($roleIdOrName) && ctype_digit($roleIdOrName))) {
|
||||
$roleId = (int)$roleIdOrName;
|
||||
|
||||
// 如果不需要校验 roles 的 system/status,就不 join,性能更好
|
||||
$needJoin = $onlyActive || !is_null($systemId) || $includeGlobal === false;
|
||||
|
||||
if (!$needJoin) {
|
||||
$rows = Db::table('user_roles')
|
||||
->where('role_id', $roleId)
|
||||
->select('user_id')
|
||||
->get();
|
||||
} else {
|
||||
$q = Db::table('user_roles as ur')
|
||||
->join('roles as r', 'ur.role_id', '=', 'r.id')
|
||||
->where('ur.role_id', $roleId)
|
||||
->select('ur.user_id');
|
||||
|
||||
if ($onlyActive) {
|
||||
$q->where('r.status', 1);
|
||||
}
|
||||
if (!is_null($systemId)) {
|
||||
if ($includeGlobal) {
|
||||
$q->whereIn('r.system_id', [0, $systemId]);
|
||||
} else {
|
||||
$q->where('r.system_id', $systemId);
|
||||
}
|
||||
}
|
||||
$rows = $q->get();
|
||||
}
|
||||
}
|
||||
// 情况B:字符串 => 当作 roles.name
|
||||
else {
|
||||
$name = (string)$roleIdOrName;
|
||||
$q = Db::table('user_roles as ur')
|
||||
->join('roles as r', 'ur.role_id', '=', 'r.id')
|
||||
->where('r.name', $name)
|
||||
->select('ur.user_id');
|
||||
|
||||
if ($onlyActive) {
|
||||
$q->where('r.status', 1);
|
||||
}
|
||||
if (!is_null($systemId)) {
|
||||
if ($includeGlobal) {
|
||||
$q->whereIn('r.system_id', [0, $systemId]);
|
||||
} else {
|
||||
$q->where('r.system_id', $systemId);
|
||||
}
|
||||
}
|
||||
$rows = $q->get();
|
||||
}
|
||||
|
||||
$ids = [];
|
||||
foreach ($rows as $row) {
|
||||
$ids[] = (int)$row->user_id;
|
||||
}
|
||||
return array_values(array_unique($ids));
|
||||
}
|
||||
|
||||
|
||||
private function getLeaderIdByUserId(int $userId): ?int
|
||||
{
|
||||
$id = Db::table('user')->where('id', $userId)->value('report_to');
|
||||
return $id ? (int)$id : null;
|
||||
}
|
||||
|
||||
private function getDeptHeadId(int $deptId): ?int
|
||||
{
|
||||
$id = Db::table('departments')->where('id', $deptId)->value('head_user_id');
|
||||
return $id ? (int)$id : null;
|
||||
}
|
||||
}
|
||||
@@ -240,15 +240,61 @@
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="" style="text-align:right;">
|
||||
<div style="padding-top: 6mm; padding-bottom: 6mm; display: table; margin: 0 0 0 auto;">
|
||||
<div style="display: table-cell; vertical-align: middle;">
|
||||
<span>下单人:</span>
|
||||
<img src="<?php echo htmlspecialchars($data['sign_img_url'] ?? ' - '); ?>" style="width: 21mm; height: 12mm; vertical-align: middle;">
|
||||
<!-- PDF 底部签名区 -->
|
||||
<!-- PDF 底部签名区(无表格版本) -->
|
||||
<div style="margin-top: 6mm; font-size: 12px;">
|
||||
<!-- 左侧:业务员签字 / 备发货负责人签字 -->
|
||||
<div style="width: 50%; float: left; text-align: left;">
|
||||
<!-- 行距通过 margin-bottom 控制;行内元素通过固定行高和 vertical-align: middle 垂直居中 -->
|
||||
<div style="height: 14mm; line-height: 14mm; margin-bottom: 5mm;">
|
||||
<span>业务员签字:</span>
|
||||
<?php if (!empty($data['sign_img_url'])): ?>
|
||||
<img src="<?php echo htmlspecialchars($data['sign_img_url']); ?>" style="height: 12mm; vertical-align: middle;">
|
||||
<?php else: ?>
|
||||
<span style="display:inline-block; width:21mm; height:12mm; vertical-align: middle; border-bottom:0.2mm solid #000;"></span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div style="height: 14mm; line-height: 14mm;">
|
||||
<span>备发货负责人签字:</span>
|
||||
<?php if (!empty($data['sign_img_url'])): ?>
|
||||
<img src="<?php echo htmlspecialchars($data['sign_img_url']); ?>" style="height: 12mm; vertical-align: middle;">
|
||||
<?php else: ?>
|
||||
<span style="display:inline-block; width:21mm; height:12mm; vertical-align: middle; border-bottom:0.2mm solid #000;"></span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<div>下单时间:<?php echo htmlspecialchars($data['created_at'] ?? ' - '); ?></div>
|
||||
|
||||
<!-- 右侧:组长签字 / 日期 -->
|
||||
<div style="width: 50%; float: right; text-align: left;">
|
||||
<div style="height: 14mm; line-height: 14mm; margin-bottom: 5mm;">
|
||||
<span>组长签字:</span>
|
||||
<?php if (!empty($data['sign_img_url'])): ?>
|
||||
<img src="<?php echo htmlspecialchars($data['sign_img_url']); ?>" style="height: 12mm; vertical-align: middle;">
|
||||
<?php else: ?>
|
||||
<span style="display:inline-block; width:21mm; height:12mm; vertical-align: middle; border-bottom:0.2mm solid #000;"></span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div style="height: 14mm; line-height: 14mm;">
|
||||
<span>日期:</span>
|
||||
<?php
|
||||
$_dt = $data['signed_date'] ?? ($data['created_at'] ?? '');
|
||||
if (is_numeric($_dt)) {
|
||||
echo htmlspecialchars(date('Y-m-d', (int)$_dt));
|
||||
} elseif (!empty($_dt)) {
|
||||
$t = strtotime($_dt);
|
||||
echo $t ? htmlspecialchars(date('Y-m-d', $t)) : ' - ';
|
||||
} else {
|
||||
echo ' - ';
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="clear: both;"></div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
|
||||
@@ -0,0 +1,255 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>合塑实业-汽车事业部-备货单(客户下定)</title>
|
||||
<style>
|
||||
/* 横版:A4 297mm,左右各 12mm => 可用宽度 273mm */
|
||||
@page { margin: 12mm; }
|
||||
|
||||
body {
|
||||
font-family: 'notosanssc', 'DejaVu Sans', Arial, sans-serif;
|
||||
font-size: 10pt;
|
||||
line-height: 1.3;
|
||||
color: #000;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.container {
|
||||
width: 273mm; /* 横向版心 */
|
||||
margin: 0 auto;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.header {
|
||||
text-align: center;
|
||||
margin-bottom: 10pt;
|
||||
padding-bottom: 6pt;
|
||||
}
|
||||
.title {
|
||||
font-size: 14pt;
|
||||
font-weight: bold;
|
||||
margin-bottom: 6pt;
|
||||
}
|
||||
.sub-title {
|
||||
font-size: 9pt;
|
||||
color: #000;
|
||||
margin: 0 6pt;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-bottom: 10pt;
|
||||
font-size: 9pt;
|
||||
table-layout: fixed; /* 横向固定布局,配合 colgroup 更稳 */
|
||||
}
|
||||
th, td {
|
||||
border: 0.5pt solid #000;
|
||||
padding: 6pt 6pt;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
word-break: break-all;
|
||||
}
|
||||
th {
|
||||
background-color: #f0f0f0;
|
||||
font-weight: bold;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
/* 跨页友好 */
|
||||
thead { display: table-header-group; }
|
||||
tfoot { display: table-row-group; }
|
||||
tr, img { page-break-inside: avoid; }
|
||||
|
||||
/* 信息类表格左对齐 */
|
||||
.info-table td,
|
||||
.logistics-table td { text-align: left; }
|
||||
|
||||
.info-label { font-weight: bold; color: #000; }
|
||||
.info-value { color: #000; }
|
||||
|
||||
.summary-row {
|
||||
background-color: #f0f0f0;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.payment-title {
|
||||
background-color: #f0f0f0;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.nowrap { white-space: nowrap; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<!-- 头部 -->
|
||||
<div class="header">
|
||||
<h1 class="title">合塑实业-汽车事业部-备货单(客户下定)</h1>
|
||||
<div>
|
||||
<span class="sub-title">备货单号: <?php echo htmlspecialchars($data['order_no'] ?? 'N/A'); ?></span>
|
||||
<span class="sub-title">业务员: <?php echo htmlspecialchars($data['salesperson_snapshot'] ?? ' - '); ?></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 基本信息 -->
|
||||
<table class="info-table">
|
||||
<colgroup>
|
||||
<col style="width:18%">
|
||||
<col style="width:15%">
|
||||
<col style="width:14%">
|
||||
<col style="width:14%">
|
||||
<col style="width:19%">
|
||||
<col style="width:20%">
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><span class="info-label">阿里订单号/PI号:</span> <span class="info-value"><?php echo htmlspecialchars($data['pi_no'] ?? ' - '); ?></span></td>
|
||||
<td><span class="info-label">日期:</span> <span class="info-value nowrap"><?php echo htmlspecialchars($data['biz_date'] ?? ' - '); ?></span></td>
|
||||
<td><span class="info-label">关目的国:</span> <span class="info-value"><?php echo htmlspecialchars($data['clearance_country_snapshot'] ?? ' - '); ?></span></td>
|
||||
<td><span class="info-label">客户来源:</span> <span class="info-value"><?php echo htmlspecialchars($data['customer_source_snapshot'] ?? ' - '); ?></span></td>
|
||||
<td><span class="info-label">客户名称:</span> <span class="info-value"><?php echo htmlspecialchars($data['customer_snapshot'] ?? ' - '); ?></span></td>
|
||||
<td><span class="info-label">客户所在国:</span> <span class="info-value"><?php echo htmlspecialchars($data['customer_country_snapshot'] ?? ' - '); ?></span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- 产品列表 -->
|
||||
<table class="product-table">
|
||||
<colgroup>
|
||||
<col style="width:6%">
|
||||
<col style="width:34%">
|
||||
<col style="width:6%">
|
||||
<col style="width:6%">
|
||||
<col style="width:6%">
|
||||
<col style="width:14%">
|
||||
<col style="width:8%">
|
||||
<col style="width:8%">
|
||||
<col style="width:6%">
|
||||
<col style="width:6%">
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>编号</th>
|
||||
<th>车型</th>
|
||||
<th>数量/台</th>
|
||||
<th>颜色/外</th>
|
||||
<th>内饰</th>
|
||||
<th>改装</th>
|
||||
<th class="nowrap">单价</th>
|
||||
<th class="nowrap">总价</th>
|
||||
<th class="nowrap">官方指导价/二手挂牌价</th>
|
||||
<th class="nowrap">优惠/二手减价</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach (($data['veh_list'] ?? []) as $vehicle): ?>
|
||||
<tr>
|
||||
<td><?php echo htmlspecialchars($vehicle['index'] ?? ' - '); ?></td>
|
||||
<td><?php echo htmlspecialchars($vehicle['model_snapshot'] ?? ' - '); ?></td>
|
||||
<td><?php echo htmlspecialchars($vehicle['num'] ?? ' - '); ?></td>
|
||||
<td><?php echo htmlspecialchars($vehicle['color_exterior_snapshot'] ?? ' - '); ?></td>
|
||||
<td><?php echo htmlspecialchars($vehicle['color_interior_snapshot'] ?? ' - '); ?></td>
|
||||
<td><?php echo htmlspecialchars($vehicle['modify'] ?? ' - '); ?></td>
|
||||
<td class="nowrap"><?php echo htmlspecialchars($vehicle['unit_price_text'] ?? ' - '); ?></td>
|
||||
<td class="nowrap"><?php echo htmlspecialchars($vehicle['total_price_text'] ?? ' - '); ?></td>
|
||||
|
||||
<?php if ($vehicle['mileage'] > 0): ?>
|
||||
<td class="nowrap"><?php echo htmlspecialchars($vehicle['listing_price'] ?? ' - '); ?></td>
|
||||
<td class="nowrap"><?php echo htmlspecialchars($vehicle['reduction_price'] ?? ' - '); ?></td>
|
||||
<?php else: ?>
|
||||
<td class="nowrap"><?php echo htmlspecialchars($vehicle['official_price'] ?? ' - '); ?></td>
|
||||
<td class="nowrap"><?php echo htmlspecialchars($vehicle['discount_price'] ?? ' - '); ?></td>
|
||||
<?php endif; ?>
|
||||
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
|
||||
<tr class="summary-row">
|
||||
<td colspan="6" style="text-align:left;">
|
||||
<span class="info-label">合同汇率:</span> <span class="info-value"><?php echo htmlspecialchars($data['contract_exchange_rate'] ?? ' - '); ?></span>
|
||||
</td>
|
||||
<td colspan="4" style="text-align:left;">
|
||||
<span class="info-label">合计到岸价:</span> <span class="info-value"><?php echo htmlspecialchars($data['cif_total_amount'] ?? ' - '); ?></span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- 物流信息 -->
|
||||
<table class="logistics-table">
|
||||
<colgroup>
|
||||
<col style="width:16%">
|
||||
<col style="width:18%">
|
||||
<col style="width:12%">
|
||||
<col style="width:18%">
|
||||
<col style="width:22%">
|
||||
<col style="width:14%">
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><span class="info-label">清关人:</span> <span class="info-value"><?php echo htmlspecialchars($data['declarant_name'] ?? ' - '); ?></span></td>
|
||||
<td><span class="info-label">预计发运日期:</span> <span class="info-value nowrap"><?php echo htmlspecialchars($data['eta_ship_date'] ?? ' - '); ?></span></td>
|
||||
<td><span class="info-label">出运方式:</span> <span class="info-value"><?php echo htmlspecialchars($data['shipping_method_snapshot'] ?? ' - '); ?></span></td>
|
||||
<td><span class="info-label">国内收货港:</span> <span class="info-value"><?php echo htmlspecialchars($data['domestic_port_snapshot'] ?? ' - '); ?></span></td>
|
||||
<td><span class="info-label">贸易条款/清关目的港:</span> <span class="info-value"><?php echo htmlspecialchars($data['trade_term_snapshot'] ?? ' - '); ?>/<?php echo htmlspecialchars($data['clearance_port_snapshot'] ?? ' - '); ?></span></td>
|
||||
<td><span class="info-label">最迟到港时间:</span> <span class="info-value nowrap"><?php echo htmlspecialchars($data['latest_arrival_date'] ?? ' - '); ?></span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- 计划收款信息 -->
|
||||
<table class="payment-table">
|
||||
<colgroup>
|
||||
<col style="width:12%">
|
||||
<col style="width:10%">
|
||||
<col style="width:14%">
|
||||
<col style="width:12%">
|
||||
<col style="width:12%">
|
||||
<col style="width:20%">
|
||||
<col style="width:10%">
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:24mm" rowspan="<?php echo htmlspecialchars($data['acc_index']); ?>">计划收款信息</th>
|
||||
<th>序号</th>
|
||||
<th>汇款国</th>
|
||||
<th>日期</th>
|
||||
<th>币种</th>
|
||||
<th>账户</th>
|
||||
<th>汇款人</th>
|
||||
<th>金额</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach (($data['account_list'] ?? []) as $account): ?>
|
||||
<tr>
|
||||
<td class="nowrap"><?php echo htmlspecialchars($account['index'] ?? ' - '); ?></td>
|
||||
<td><?php echo htmlspecialchars($account['remit_country_name_snapshot'] ?? ' - '); ?></td>
|
||||
<td class="nowrap"><?php echo htmlspecialchars($account['remit_date'] ?? ' - '); ?></td>
|
||||
<td class="nowrap"><?php echo htmlspecialchars($account['currency_name_snapshot'] ?? ' - '); ?></td>
|
||||
<td class="nowrap"><?php echo htmlspecialchars($account['account_name_snapshot'] ?? ' - '); ?></td>
|
||||
<td style="text-align:left;"><?php echo htmlspecialchars($account['remitter_name'] ?? ' - '); ?></td>
|
||||
<td class="nowrap"><?php echo htmlspecialchars($account['amount'] ?? ' - '); ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="" style="text-align:right;">
|
||||
<div style="padding-top: 6mm; padding-bottom: 6mm; display: table; margin: 0 0 0 auto;">
|
||||
<div style="display: table-cell; vertical-align: middle;">
|
||||
<span>下单人:</span>
|
||||
<img src="<?php echo htmlspecialchars($data['sign_img_url'] ?? ' - '); ?>" style="width: 21mm; height: 12mm; vertical-align: middle;">
|
||||
</div>
|
||||
</div>
|
||||
<div>下单时间:<?php echo htmlspecialchars($data['created_at'] ?? ' - '); ?></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -127,5 +127,8 @@ class IndexController
|
||||
// $up = new UploadService();
|
||||
// $res = $up->uploadLocalFile($param);
|
||||
// var_dump($res);
|
||||
|
||||
$service = new \app\common\service\StockOrdersService();
|
||||
$service->createdPdfFile(60);
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,9 @@ class ApprovalRecordsController extends BaseController
|
||||
|
||||
'step_index' => $record['step_index'],
|
||||
'action' => $record['action'],
|
||||
'status' => SysDictItem::where('item_value', $record['status'])->where('status', 1)->value('item_label'),
|
||||
'action_desc' => SysDictItem::where('item_value', $record['action'])->where('status', 1)->value('item_label'),
|
||||
'status' => $record['status'],
|
||||
'status_desc' => SysDictItem::where('item_value', $record['status'])->where('status', 1)->value('item_label'),
|
||||
'comment' => $record['comment'],
|
||||
'approved_at' => $record['approved_at'],
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ namespace app\crm\controller;
|
||||
|
||||
use app\common\controller\BaseController;
|
||||
use app\common\traits\crud\CrudControllerTrait;
|
||||
use app\common\service\StockOrdersService;
|
||||
use app\crm\service\StockOrdersService;
|
||||
use support\Request;
|
||||
|
||||
class StockOrdersController extends BaseController
|
||||
|
||||
@@ -0,0 +1,434 @@
|
||||
<?php
|
||||
|
||||
namespace app\crm\service;
|
||||
|
||||
use app\common\model\StockOrderAccounts;
|
||||
use app\common\model\StockOrderItemOptions;
|
||||
use app\common\model\StockOrderItems;
|
||||
use app\common\validate\StockOrdersValidate;
|
||||
use app\common\traits\crud\CrudServiceTrait;
|
||||
use app\common\model\StockOrders;
|
||||
use app\common\model\SysCurrency;
|
||||
use support\Request;
|
||||
use app\common\model\User;
|
||||
use support\exception\BusinessException;
|
||||
use support\Db;
|
||||
|
||||
class StockOrdersService
|
||||
{
|
||||
|
||||
use CrudServiceTrait;
|
||||
|
||||
/**
|
||||
* 指定当前要操作的模型
|
||||
*/
|
||||
protected string $modelClass = StockOrders::class;
|
||||
|
||||
/**
|
||||
* 验证器
|
||||
*/
|
||||
protected $validator = StockOrdersValidate::class;
|
||||
|
||||
/**
|
||||
* 获取验证器
|
||||
*/
|
||||
public function getValidator()
|
||||
{
|
||||
return new $this->validator;
|
||||
}
|
||||
|
||||
|
||||
protected function buildQueryConfig(Request $request, string $scene = 'index'): array
|
||||
{
|
||||
$p = $request->all();
|
||||
|
||||
$config = [];
|
||||
$config['model'] = $this->modelClass;
|
||||
switch ($scene) {
|
||||
case 'index':
|
||||
|
||||
// ====== ====== 筛选 ====== ======
|
||||
// 业务id
|
||||
if (isset($p['salesperson_id']) && $p['salesperson_id']) {
|
||||
$config['where']['salesperson_id'] = $p['salesperson_id'];
|
||||
}
|
||||
// 初、终版
|
||||
$config['where']['is_final'] = $p['is_final'] ?? 0;
|
||||
// 关键字
|
||||
if (isset($p['keyword']) && $p['keyword']) {
|
||||
$k = '%' . $p['keyword'] . '%';
|
||||
foreach (['pi_no', 'order_no', 'customer_snapshot'] as $col) {
|
||||
$config['orWhere'][] = [$col, 'like', $k];
|
||||
}
|
||||
}
|
||||
|
||||
$config['order']['id'] = 'desc';
|
||||
|
||||
$config['where']['salesperson_id'] = $request->user->uid;
|
||||
break;
|
||||
case 'print':
|
||||
$config['where']['id'] = $p['id'];
|
||||
break;
|
||||
case 'show':
|
||||
$config['where']['id'] = $p['id'];
|
||||
|
||||
$config['with'][] = 'items';
|
||||
$config['with'][] = 'items.options';
|
||||
$config['with'][] = 'items.moneyValues';
|
||||
$config['with'][] = 'accounts';
|
||||
$config['with'][] = 'moneyValues';
|
||||
$config['with'][] = 'salesperson';
|
||||
break;
|
||||
case 'store':
|
||||
break;
|
||||
case 'update':
|
||||
$config['where']['id'] = $p['id'];
|
||||
break;
|
||||
case 'destroy':
|
||||
$config['where']['id'] = $p['id'];
|
||||
break;
|
||||
}
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
|
||||
public function serviceStore(Request $request, array $data = [])
|
||||
{
|
||||
// 关联表较多,增加事务处理
|
||||
return Db::transaction(function () use ($request, $data) {
|
||||
$data = $this->preProcess('Store', $data, $request);
|
||||
$dl = $this->getDataLimitFromRequest($request);
|
||||
$created = $this->logicCreate($data, $dl); // 表1
|
||||
|
||||
// 车辆
|
||||
$cifTotalAmount = 0;
|
||||
$cifTotalAmountRate = 0;
|
||||
$cifTotalAmountCurrency = 'CNY';
|
||||
// $profit = 0;
|
||||
foreach ($data['veh_list'] as $veh) {
|
||||
$veh['order_id'] = $created->id;
|
||||
|
||||
// 唯一标识
|
||||
$uniqueIdStr = "";
|
||||
$uniqueIdStr .= $veh['sku_id'];
|
||||
$uniqueIdStr .= $veh['model_snapshot'];
|
||||
$uniqueIdStr .= $veh['color_interior_code'];
|
||||
$uniqueIdStr .= $veh['color_exterior_code'];
|
||||
$uniqueIdStr .= $veh['mileage'];
|
||||
$uniqueIdStr .= json($veh['modify_list']);
|
||||
$veh['unique_id'] = md5($uniqueIdStr);
|
||||
$item = StockOrderItems::create($veh); // 表2
|
||||
|
||||
// 金额 // 表3
|
||||
// $item->addMoney('official_price', $veh['official_price']['currency'], $veh['official_price']['amount'], $veh['official_price']['rate'], '官方指导价');
|
||||
// $item->addMoney('discount_price', $veh['discount_price']['currency'], $veh['discount_price']['amount'], $veh['discount_price']['rate'], '优惠价');
|
||||
if ($veh['unit_price']['amount']) {
|
||||
$item->addMoney('unit_price', $veh['unit_price']['currency'], $veh['unit_price']['amount'], $veh['unit_price']['rate'], '单价');
|
||||
}
|
||||
if ($veh['total_price']['amount']) {
|
||||
$item->addMoney('total_price', $veh['total_price']['currency'], $veh['total_price']['amount'], $veh['total_price']['rate'], '总价');
|
||||
}
|
||||
if ($veh['handling_fee']['amount']) {
|
||||
$item->addMoney('handling_fee', $veh['handling_fee']['currency'], $veh['handling_fee']['amount'], $veh['handling_fee']['rate'], '手续费');
|
||||
}
|
||||
if ($veh['fob_transport_fee']['amount']) {
|
||||
$item->addMoney('fob_transport_fee', $veh['fob_transport_fee']['currency'], $veh['fob_transport_fee']['amount'], $veh['fob_transport_fee']['rate'], 'FOB运输费');
|
||||
}
|
||||
if ($veh['port_misc_fee']['amount']) {
|
||||
$item->addMoney('port_misc_fee', $veh['port_misc_fee']['currency'], $veh['port_misc_fee']['amount'], $veh['port_misc_fee']['rate'], '港杂');
|
||||
}
|
||||
if ($veh['retrofit_fee']['amount']) {
|
||||
$item->addMoney('retrofit_fee', $veh['retrofit_fee']['currency'], $veh['retrofit_fee']['amount'], $veh['retrofit_fee']['rate'], '改装费');
|
||||
}
|
||||
|
||||
// 二手车 or 新车
|
||||
if ($veh['mileage'] > 0) {
|
||||
if ($veh['listing_price']['amount']) {
|
||||
$item->addMoney('listing_price', $veh['listing_price']['currency'], $veh['listing_price']['amount'], $veh['listing_price']['rate'], '二手挂牌价');
|
||||
}
|
||||
if ($veh['reduction_price']['amount']) {
|
||||
$item->addMoney('reduction_price', $veh['reduction_price']['currency'], $veh['reduction_price']['amount'], $veh['reduction_price']['rate'], '二手减价');
|
||||
}
|
||||
|
||||
$ttPrice = ($veh['listing_price']['amount'] / $created->contract_exchange_rate);
|
||||
$ddPrice = $veh['reduction_price']['amount'];
|
||||
} else {
|
||||
if ($veh['official_price']['amount']) {
|
||||
$item->addMoney('official_price', $veh['official_price']['currency'], $veh['official_price']['amount'], $veh['official_price']['rate'], '官方指导价');
|
||||
}
|
||||
if ($veh['discount_price']['amount'] ) {
|
||||
$item->addMoney('discount_price', $veh['discount_price']['currency'], $veh['discount_price']['amount'], $veh['discount_price']['rate'], '优惠价');
|
||||
}
|
||||
|
||||
$ttPrice = ($veh['official_price']['amount'] / $created->contract_exchange_rate);
|
||||
$ddPrice = $veh['discount_price']['amount'];
|
||||
}
|
||||
|
||||
// 利润
|
||||
$profit = (float)$veh['unit_price']['amount'] - (float)$ttPrice - (float)$ddPrice - (float)$veh['handling_fee']['amount'] - (float)$veh['fob_transport_fee']['amount'] - (float)$veh['port_misc_fee']['amount'] - (float)$veh['retrofit_fee']['amount'];
|
||||
$item->addMoney('profit', $veh['unit_price']['currency'], $profit, $veh['unit_price']['rate'], '利润');
|
||||
|
||||
// cif_total_amount
|
||||
$cifTotalAmount += $veh['total_price']['amount'];
|
||||
$cifTotalAmountRate = $veh['retrofit_fee']['rate'];
|
||||
$cifTotalAmountCurrency = $veh['retrofit_fee']['currency'];
|
||||
|
||||
// 改装
|
||||
if (count($veh['modify_list']) > 0) {
|
||||
foreach ($veh['modify_list'] as $modify) {
|
||||
$modify['order_item_id'] = $item->id;
|
||||
StockOrderItemOptions::create($modify); // 表4
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 计算&&插入 表1
|
||||
$created->addMoney('cif_total_amount', $cifTotalAmountCurrency, $cifTotalAmount, $cifTotalAmountRate, '合计到岸价');
|
||||
|
||||
// 账户
|
||||
if (count($data['account_list']) > 0) {
|
||||
foreach ($data['account_list'] as $account) {
|
||||
$account['order_id'] = $created->id;
|
||||
$account['remit_date'] = strtotime($account['remit_date']);
|
||||
StockOrderAccounts::create($account); // 表5
|
||||
}
|
||||
}
|
||||
|
||||
return $this->postProcess('Store', $created);
|
||||
});
|
||||
}
|
||||
|
||||
public function beforeServiceStore($data, Request $request)
|
||||
{
|
||||
$userId = $request->user->uid;
|
||||
|
||||
$data['order_no'] = generateStockNo();
|
||||
$data['biz_date'] = strtotime($data['biz_date']);
|
||||
$data['eta_ship_date'] = strtotime($data['eta_ship_date']);
|
||||
$data['latest_arrival_date'] = strtotime($data['latest_arrival_date']);
|
||||
$data['salesperson_id'] = $userId;
|
||||
$data['salesperson_snapshot'] = User::where('id', $userId)->value('name') ?? '';
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
protected function afterServiceStore($res)
|
||||
{
|
||||
// 处理原单
|
||||
if ($res->is_final == 1 && $res->object_id > 0) {
|
||||
$order = StockOrders::find($res->object_id);
|
||||
$order->is_use = 1;
|
||||
$order->object_id = $res->id;
|
||||
if (!$order->save()) {
|
||||
flg('备货单【'.$order->order_no.'】创建终版成功,但更新原单失败');
|
||||
}
|
||||
}
|
||||
// 启动审批
|
||||
$appro = new \app\common\utils\approval\service\ApprovalFlowService();
|
||||
$result = $appro->start('stock_order_flow', 'stock_orders', $res->id, $res->toArray(), $res->salesperson_id);
|
||||
var_dump($result);
|
||||
|
||||
// 异步生成pdf文件
|
||||
\Webman\RedisQueue\Redis::send('stock-order-pdf', ['id'=>$res->id]);
|
||||
return $res;
|
||||
}
|
||||
|
||||
|
||||
public function createdPdfFile($id)
|
||||
{
|
||||
$config = [];
|
||||
$config['model'] = $this->modelClass;
|
||||
$config['where']['id'] = $id;
|
||||
$config['with'][] = 'items';
|
||||
$config['with'][] = 'items.options';
|
||||
$config['with'][] = 'items.moneyValues';
|
||||
$config['with'][] = 'accounts';
|
||||
$config['with'][] = 'moneyValues';
|
||||
$config['with'][] = 'salesperson';
|
||||
$item = $this->logicShow($config);
|
||||
$pdfData = $this->pdfData($item);
|
||||
|
||||
// 渲染模板为最终HTML
|
||||
$tplPath = app_path('common/utils/pdf/template/stockOrder.stub');
|
||||
$html = \app\common\utils\pdf\service\PdfHelper::render($tplPath, ['data' => $pdfData]);
|
||||
|
||||
// 生成PDF
|
||||
$driver = new \app\common\utils\pdf\service\MpdfDriver();
|
||||
$pdfBin = $driver->htmlToPdf($html);
|
||||
|
||||
// 存储
|
||||
$dir = public_path('pdf').'/';
|
||||
$fileName = ($pdfData['order_no'] ?? date('YmdHis')) . '.pdf';
|
||||
|
||||
// 本地保存
|
||||
\app\common\utils\pdf\service\PdfStorage::saveBinary(
|
||||
$pdfBin,
|
||||
$dir,
|
||||
$fileName
|
||||
);
|
||||
|
||||
// // 在线预览
|
||||
// return new \support\Response(200, [
|
||||
// 'Content-Type' => 'application/pdf',
|
||||
// 'Content-Disposition' => 'inline; filename="stock-order.pdf"',
|
||||
// ], $pdfBin);
|
||||
|
||||
// $url = [
|
||||
// 'file_name' => $fileName,
|
||||
// 'file_url' => getenv('MAIN_DOMAIN').'/pdf/'.$fileName
|
||||
// ];
|
||||
|
||||
// 上传云端
|
||||
$param = [
|
||||
'path' => $dir . $fileName,
|
||||
'scene' => 'stock_orders',
|
||||
'category' => 'pdf',
|
||||
'original_name' => $fileName
|
||||
];
|
||||
$up = new \app\common\service\UploadService();
|
||||
$res = $up->uploadLocalFile($param);
|
||||
|
||||
@unlink($dir . $fileName);
|
||||
|
||||
// 更新文件地址
|
||||
$item->file_url = $res['file_name'];
|
||||
$item->save();
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
public function servicePrint(Request $request)
|
||||
{
|
||||
$config = $this->buildQueryConfig($request, 'show');
|
||||
$item = $this->logicShow($config);
|
||||
return $this->postProcess('Print', $item);
|
||||
}
|
||||
|
||||
|
||||
public function pdfData($item)
|
||||
{
|
||||
$vehList = [];
|
||||
$index = 0;
|
||||
foreach ($item->items as $k => $v) {
|
||||
// 存在则数量加1
|
||||
if (count($vehList) > 0 && isset($vehList[$v->unique_id])) {
|
||||
$vehList[$v->unique_id]['num'] += 1;
|
||||
$vehList[$v->unique_id]['total_price'] += $vehList[$v->unique_id]['unit_price'];
|
||||
$vehList[$v->unique_id]['total_price_text'] = $vehList[$v->unique_id]['total_price_symbol'].number_format($vehList[$v->unique_id]['total_price'], 2, '.', ',');
|
||||
continue;
|
||||
}
|
||||
$index += 1;
|
||||
// 处理金额
|
||||
$mv = [];
|
||||
foreach ($v->moneyValues as $money) {
|
||||
$mv[$money->field] = [
|
||||
'currency' => $money->currency,
|
||||
'amount' => $money->amount,
|
||||
'rate' => $money->exchange_rate,
|
||||
'currency_symbol' => SysCurrency::where('currency_code', $money->currency)->value('currency_symbol'),
|
||||
];
|
||||
}
|
||||
// 处理改装
|
||||
$mfy = '';
|
||||
foreach ($v->options as $my) {
|
||||
$mfy .= $my->option_name_snapshot.' ';
|
||||
}
|
||||
|
||||
// 处理结果集
|
||||
$vehList[$v->unique_id] = [
|
||||
'index' => $index,
|
||||
'model_snapshot' => $v->model_snapshot,
|
||||
'color_interior_snapshot' => $v->color_interior_snapshot,
|
||||
'color_exterior_snapshot' => $v->color_exterior_snapshot,
|
||||
'num' => 1,
|
||||
'modify' => $mfy,
|
||||
'mileage' => $v->mileage,
|
||||
'unit_price' => $mv['unit_price']['amount'],
|
||||
'total_price' => $mv['total_price']['amount'],
|
||||
'unit_price_symbol' => $mv['unit_price']['currency_symbol'],
|
||||
'total_price_symbol' => $mv['total_price']['currency_symbol'],
|
||||
'unit_price_text' => $mv['unit_price']['currency_symbol'].number_format($mv['unit_price']['amount'], 2, '.', ','),
|
||||
'total_price_text' => $mv['total_price']['currency_symbol'].number_format($mv['total_price']['amount'], 2, '.', ',')
|
||||
];
|
||||
|
||||
// 根据里程处理 新车or二手车
|
||||
if ($v->mileage > 0) {
|
||||
if (isset($mv['reduction_price'])) {
|
||||
$vehList[$v->unique_id]['reduction_price'] = $mv['reduction_price']['amount'];
|
||||
$vehList[$v->unique_id]['reduction_price_symbol'] = $mv['reduction_price']['currency_symbol'];
|
||||
$vehList[$v->unique_id]['reduction_price_text'] = $mv['reduction_price']['currency_symbol'].number_format($mv['reduction_price']['amount'], 2, '.', ',');
|
||||
}
|
||||
$vehList[$v->unique_id]['listing_price'] = $mv['listing_price']['amount'];
|
||||
$vehList[$v->unique_id]['listing_price_symbol'] = $mv['listing_price']['currency_symbol'];
|
||||
$vehList[$v->unique_id]['listing_price_text'] = $mv['listing_price']['currency_symbol'].number_format($mv['listing_price']['amount'], 2, '.', ',');
|
||||
} else {
|
||||
if (isset($mv['discount_price'])) {
|
||||
$vehList[$v->unique_id]['discount_price'] = $mv['discount_price']['amount'];
|
||||
$vehList[$v->unique_id]['discount_price_symbol'] = $mv['discount_price']['currency_symbol'];
|
||||
$vehList[$v->unique_id]['discount_price_text'] = $mv['discount_price']['currency_symbol'].number_format($mv['discount_price']['amount'], 2, '.', ',');
|
||||
}
|
||||
$vehList[$v->unique_id]['official_price'] = $mv['official_price']['amount'];
|
||||
$vehList[$v->unique_id]['official_price_symbol'] = $mv['official_price']['currency_symbol'];
|
||||
$vehList[$v->unique_id]['official_price_text'] = $mv['official_price']['currency_symbol'].number_format($mv['official_price']['amount'], 2, '.', ',');
|
||||
}
|
||||
}
|
||||
|
||||
$accList = [];
|
||||
$accIndex = 1;
|
||||
foreach ($item->accounts as $k => $v) {
|
||||
$accList[] = [
|
||||
'index' => '收款'.$k + 1,
|
||||
'remit_country_name_snapshot' => $v->remit_country_name_snapshot,
|
||||
'remit_date' => date('Y-m-d', $v->remit_date),
|
||||
'currency_name_snapshot' => $v->currency_name_snapshot,
|
||||
'account_name_snapshot' => $v->account_name_snapshot,
|
||||
'remitter_name' => $v->remitter_name,
|
||||
'amount' => $v->amount,
|
||||
];
|
||||
$accIndex += 1;
|
||||
}
|
||||
|
||||
$cifTotalAmount = 0.00;
|
||||
if ($item->moneyValues) {
|
||||
$cifTotalAmountSysboml = SysCurrency::where('currency_code', $item->moneyValues->currency)->value('currency_symbol');
|
||||
$cifTotalAmount = $cifTotalAmountSysboml.sprintf("%.2f", $item->moneyValues->amount, $item->moneyValues->exchange_rate);
|
||||
}
|
||||
|
||||
$veh = [
|
||||
'order_no' => $item->order_no,
|
||||
'salesperson_snapshot' => $item->salesperson_snapshot,
|
||||
// 'sign_img' => $item->salesperson->sign_img,
|
||||
'sign_img_url' => (!empty($item->salesperson->sign_img))?(new \app\common\service\OssService)->generatePrivateUrl($item->salesperson->sign_img):'',
|
||||
'created_at' => date('Y-m-d', strtotime($item->created_at)),
|
||||
|
||||
'pi_no' => $item->pi_no,
|
||||
'biz_date' => date('Y-m-d', $item->biz_date),
|
||||
'clearance_country_snapshot' => $item->clearance_country_snapshot,
|
||||
'customer_source_snapshot' => $item->customer_source_snapshot,
|
||||
'customer_snapshot' => $item->customer_snapshot,
|
||||
'customer_country_snapshot' => $item->customer_country_snapshot,
|
||||
|
||||
// 订单项
|
||||
'veh_list' => $vehList,
|
||||
|
||||
'contract_exchange_rate' => $item->contract_exchange_rate,
|
||||
'cif_total_amount' => $item->cif_total_amount,
|
||||
|
||||
'declarant_name' => $item->declarant_name,
|
||||
'eta_ship_date' => date('Y-m-d', $item->eta_ship_date),
|
||||
'shipping_method_snapshot' => $item->shipping_method_snapshot,
|
||||
'domestic_port_snapshot' => $item->domestic_port_snapshot,
|
||||
'trade_term_snapshot' => $item->trade_term_snapshot,
|
||||
'clearance_port_snapshot' => $item->clearance_port_snapshot,
|
||||
'latest_arrival_date' => date('Y-m-d', $item->latest_arrival_date),
|
||||
|
||||
// 账户
|
||||
'account_list' => $accList,
|
||||
'acc_index' => $accIndex,
|
||||
'cif_total_amount' => $cifTotalAmount
|
||||
];
|
||||
|
||||
return $veh;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user