180 行
5.9 KiB
PHP
180 行
5.9 KiB
PHP
<?php
|
|
|
|
namespace app\admin\controller;
|
|
|
|
use app\common\controller\Backend;
|
|
use think\Db;
|
|
use think\exception\PDOException;
|
|
|
|
/**
|
|
* 开票申请
|
|
*
|
|
* 开票申请仅由用户/商户端发起,后台只负责财务开具处理:
|
|
* 待开具(wait) → 已开票(issued,必填发票号)/驳回(reject,必填原因)
|
|
* 驳回后额度自动释放,并通知申请人
|
|
*
|
|
* @icon fa fa-file-text-o
|
|
*/
|
|
class Invoice extends Backend
|
|
{
|
|
|
|
/**
|
|
* Invoice模型对象
|
|
* @var \app\admin\model\Invoice
|
|
*/
|
|
protected $model = null;
|
|
|
|
/**
|
|
* 开票处理状态机:当前状态 => 允许流转到的目标状态
|
|
* issued/reject 为终态
|
|
* @var array
|
|
*/
|
|
protected $statusFlow = [
|
|
'wait' => ['issued', 'reject'],
|
|
];
|
|
|
|
public function _initialize()
|
|
{
|
|
parent::_initialize();
|
|
$this->model = new \app\admin\model\Invoice;
|
|
$this->view->assign("typeList", $this->model->getTypeList());
|
|
$this->view->assign("statusList", $this->model->getStatusList());
|
|
}
|
|
|
|
/**
|
|
* 开票申请列表:关联申请人/商户
|
|
*/
|
|
public function index()
|
|
{
|
|
$this->request->filter(['strip_tags', 'trim']);
|
|
if (false === $this->request->isAjax()) {
|
|
return $this->view->fetch();
|
|
}
|
|
if ($this->request->request('keyField')) {
|
|
return $this->selectpage();
|
|
}
|
|
[$where, $sort, $order, $offset, $limit] = $this->buildparams();
|
|
$list = $this->model
|
|
->with(['user', 'merchant'])
|
|
->where($where)
|
|
->order($sort, $order)
|
|
->paginate($limit);
|
|
$result = ['total' => $list->total(), 'rows' => $list->items()];
|
|
return json($result);
|
|
}
|
|
|
|
/**
|
|
* 财务开票处理:申请信息只读,仅允许填写开票结果
|
|
* - 开票完成:必须填写发票号,状态置为已开票
|
|
* - 驳回:必须填写原因,额度自动释放
|
|
*/
|
|
public function edit($ids = null)
|
|
{
|
|
$row = $this->model->get($ids);
|
|
if (!$row) {
|
|
$this->error(__('No Results were found'));
|
|
}
|
|
if (false === $this->request->isPost()) {
|
|
$this->view->assign('row', $row);
|
|
$this->view->assign('flowOptions', $this->statusFlow[$row['status']] ?? []);
|
|
return $this->view->fetch();
|
|
}
|
|
|
|
$status = (string)$this->request->post('row.status');
|
|
$invoiceNo = trim((string)$this->request->post('row.invoice_no'));
|
|
$auditMsg = trim((string)$this->request->post('row.audit_msg'));
|
|
|
|
// 状态机校验:终态禁止操作,流转目标必须合法
|
|
if (!isset($this->statusFlow[$row['status']])) {
|
|
$this->error('该开票申请已处理完毕,不允许再次变更');
|
|
}
|
|
if (!in_array($status, $this->statusFlow[$row['status']], true)) {
|
|
$this->error('状态流转不合法,请刷新后重试');
|
|
}
|
|
if ($status === 'issued' && $invoiceNo === '') {
|
|
$this->error('标记开票完成必须填写发票号码');
|
|
}
|
|
if ($status === 'reject' && $auditMsg === '') {
|
|
$this->error('驳回时必须填写驳回原因');
|
|
}
|
|
|
|
Db::startTrans();
|
|
try {
|
|
// 乐观锁更新状态,防止并发重复处理
|
|
$affected = Db::name('invoice')
|
|
->where('id', $row['id'])
|
|
->where('status', $row['status'])
|
|
->update([
|
|
'status' => $status,
|
|
'invoice_no' => $invoiceNo !== '' ? $invoiceNo : $row['invoice_no'],
|
|
'audit_msg' => $auditMsg,
|
|
'updatetime' => time(),
|
|
]);
|
|
if (!$affected) {
|
|
throw new \Exception('开票申请状态已被其他人变更,请刷新后重试');
|
|
}
|
|
|
|
// 通知申请人(商户开票通知绑定账号用户)
|
|
$this->sendUserMessage(
|
|
(int)$row['user_id'],
|
|
$status === 'issued' ? '发票已开具' : '开票申请已驳回',
|
|
$status === 'issued'
|
|
? '您申请的开票 ¥' . $row['amount'] . ' 已开具完成,发票号码:' . $invoiceNo . ',电子发票已发送至 ' . $row['email']
|
|
: '您的开票申请 ¥' . $row['amount'] . ' 未通过审核:' . ($auditMsg !== '' ? $auditMsg : '无')
|
|
);
|
|
Db::commit();
|
|
} catch (PDOException|\Exception $e) {
|
|
Db::rollback();
|
|
$this->error($e->getMessage());
|
|
}
|
|
$this->success();
|
|
}
|
|
|
|
/**
|
|
* 开票申请仅由用户/商户端发起,禁止后台手工添加
|
|
*/
|
|
public function add()
|
|
{
|
|
$this->error('开票申请仅支持用户/商户端发起,后台不可手动添加');
|
|
}
|
|
|
|
/**
|
|
* 开票记录禁止删除,保障开票额度统计与账务完整
|
|
*/
|
|
public function del($ids = null)
|
|
{
|
|
$this->error('开票申请记录禁止删除');
|
|
}
|
|
|
|
/**
|
|
* 禁止批量修改状态,必须逐单填写开票结果
|
|
*/
|
|
public function multi($ids = null)
|
|
{
|
|
$this->error('开票处理不支持批量操作,请逐单填写发票号后处理');
|
|
}
|
|
|
|
/**
|
|
* 向申请人发送站内消息
|
|
* @param int $userId 用户ID
|
|
* @param string $title 消息标题
|
|
* @param string $content 消息内容
|
|
*/
|
|
protected function sendUserMessage($userId, $title, $content)
|
|
{
|
|
if (!$userId) {
|
|
return;
|
|
}
|
|
Db::name('message')->insert([
|
|
'receive_type' => 'user',
|
|
'receive_id' => $userId,
|
|
'type' => 'system',
|
|
'title' => mb_substr((string)$title, 0, 100),
|
|
'content' => mb_substr((string)$content, 0, 500),
|
|
'order_id' => 0,
|
|
'is_read' => 0,
|
|
'createtime' => time(),
|
|
]);
|
|
}
|
|
}
|