Files
xin-procurement/app/Http/Controllers/Recon/BillController.php
T
2026-08-14 21:28:55 +08:00

164 lines
5.8 KiB
PHP

<?php
namespace App\Http\Controllers\Recon;
use App\Exceptions\RepositoryException;
use App\Exports\BillExport;
use App\Models\BillModel;
use App\Models\StoreModel;
use App\Services\BillDetailService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Maatwebsite\Excel\Facades\Excel;
use Modules\AnnoRoute\Attribute\GetRoute;
use Modules\AnnoRoute\Attribute\PutRoute;
use Modules\AnnoRoute\Attribute\RequestAttribute;
use Modules\Common\Http\Controllers\BaseController;
use Symfony\Component\HttpFoundation\Response;
/**
* 门店账单管理(采购单完成后按门店生成,后台查看 + 线下收款登记)
*/
#[RequestAttribute('/recon/bill', 'recon.bill')]
class BillController extends BaseController
{
protected array $searchField = [
'store_id' => '=',
'status' => '=',
'bill_no' => 'like',
'bill_date' => 'betweenDate',
];
/** 账单列表 */
#[GetRoute(authorize: 'query')]
public function query(Request $request): JsonResponse
{
$params = $request->all();
$pageSize = $params['pageSize'] ?? 10;
$query = BillModel::query()->with([
'store:id,name',
'purchase:id,purchase_no,purchase_date',
'operator:id,nickname',
])->withCount('orders');
// 按采购单号搜索
$purchaseNo = trim((string) ($params['purchase_no'] ?? ''));
if ($purchaseNo !== '') {
$keyword = '%' . str_replace('%', '\%', $purchaseNo) . '%';
$query->whereHas('purchase', static function ($purchaseQuery) use ($keyword) {
$purchaseQuery->where('purchase_no', 'like', $keyword);
});
}
$data = $this->buildSearch($params, $query)
->orderBy('bill_date', 'desc')
->orderBy('id', 'desc')
->paginate($pageSize)
->toArray();
return $this->success($data);
}
/**
* 合并导出:勾选账单跨账单按商品合并明细(可按一级分类过滤),
* 表尾汇总商品金额/配送费/附加金额/总金额
*/
#[GetRoute(route: '/export', authorize: 'export')]
public function export(Request $request): Response
{
$data = $request->validate([
'ids' => 'required|string',
'category_id' => 'nullable|integer|min:0',
]);
$ids = array_values(array_filter(array_map('intval', explode(',', (string) $data['ids']))));
if ($ids === [] || count($ids) > 100) {
throw new RepositoryException('请选择 1~100 张账单');
}
$bills = BillModel::with('store:id,name')
->whereIn('id', $ids)
->orderBy('bill_date')
->orderBy('id')
->get();
if ($bills->isEmpty()) {
throw new RepositoryException('账单不存在');
}
return Excel::download(
new BillExport($bills, (int) ($data['category_id'] ?? 0)),
'门店账单_' . now()->format('Ymd_His') . '.xlsx'
);
}
/**
* 账单详情:账单信息 + 合并后的商品明细(按商品聚合)+ 关联订单
*/
#[GetRoute(route: '/{id}', authorize: 'query', where: ['id' => '[0-9]+'])]
public function detail(int $id): JsonResponse
{
$bill = BillModel::with([
'store:id,name,address,contact,phone',
'purchase:id,purchase_no,purchase_date,status',
'operator:id,nickname',
'paidOperator:id,nickname',
])->find($id);
if (empty($bill)) {
throw new RepositoryException('账单不存在');
}
$orders = $bill->orders()
->orderBy('id')
->get(['id', 'order_no', 'order_date', 'total_quantity', 'total_weight', 'total_amount', 'status'])
->toArray();
return $this->success([
'bill' => $bill->toArray(),
'items' => app(BillDetailService::class)->mergedItems($bill),
'orders' => $orders,
]);
}
/**
* 确认收款:线下收款后手动登记付款信息,支付状态置为已支付;
* 累加门店总采购金额(只统计商品金额)
*/
#[PutRoute(route: '/{id}/pay', authorize: 'pay', where: ['id' => '[0-9]+'])]
public function pay(int $id, Request $request): JsonResponse
{
$data = $request->validate([
'paid_at' => 'sometimes|date_format:Y-m-d H:i:s',
'pay_remark' => 'nullable|string|max:255',
], [
'paid_at.date_format' => '付款时间格式为 Y-m-d H:i:s',
'pay_remark.max' => '付款备注超过最大长度',
]);
return DB::transaction(function () use ($id, $data, $request) {
$bill = BillModel::query()->lockForUpdate()->find($id);
if (empty($bill)) {
throw new RepositoryException('账单不存在');
}
if ($bill->status === BillModel::STATUS_PAID) {
throw new RepositoryException('账单已支付,请勿重复收款');
}
$bill->status = BillModel::STATUS_PAID;
$bill->paid_at = $data['paid_at'] ?? now();
$bill->pay_remark = (string) ($data['pay_remark'] ?? '');
$bill->paid_operator_id = (int) $request->user()->id;
$bill->save();
// 累加门店总采购金额(只统计商品金额,不含配送费/附加金额)
$store = StoreModel::query()->lockForUpdate()->find($bill->store_id);
if ($store !== null) {
$store->total_purchase_amount = bcadd((string) $store->total_purchase_amount, (string) $bill->product_amount, 2);
$store->save();
}
return $this->success([], '收款已登记');
});
}
}