168 lines
5.9 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace Modules\EcommerceFinance\Http\Controllers;
use App\Utilities\SidebarPermissions;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Modules\EcommerceFinance\AccountBillColumns;
use Modules\EcommerceFinance\Models\AccountBill;
class AccountBills extends Controller
{
/**
* 通用账单列表(店铺收款账户账单 › 各账户类型 tab)。
* 经 admin 路由组(web 会话)暴露,前端带 CSRF 调用。
* 按 bill_type 过滤,返回分页数据 + 汇总 + 该类型的列配置。
*/
public function index(Request $request, string $bill_type)
{
$this->authorizeStoreReceivingBills();
$billType = $bill_type;
if (! AccountBillColumns::has($billType)) {
abort(404, '未知的账户类型。');
}
$request->validate([
'store_name' => ['nullable', 'string', 'max:255'],
'bill_item' => ['nullable', 'string', 'max:255'],
'start' => ['nullable', 'date_format:Y-m-d'],
'end' => ['nullable', 'date_format:Y-m-d', 'after_or_equal:start'],
'keyword' => ['nullable', 'string', 'max:255'],
'export' => ['nullable', 'boolean'],
'columns' => ['nullable', 'array', 'max:40'],
'columns.*' => ['string', 'max:128'],
]);
$query = AccountBill::query()
->where('company_id', company_id())
->where('bill_type', $billType);
if ($store = $request->get('store_name')) {
$query->where('store_name', $store);
}
if ($item = $request->get('bill_item')) {
$query->where('bill_item', $item);
}
if ($start = $request->get('start')) {
$query->where('occurred_at', '>=', $start);
}
if ($end = $request->get('end')) {
$query->where('occurred_at', '<=', $end . ' 23:59:59');
}
if ($keyword = $request->get('keyword')) {
$query->where(function ($q) use ($keyword) {
$like = '%' . $keyword . '%';
$q->where('record_code', 'like', $like)
->orWhere('order_no', 'like', $like)
->orWhere('remark', 'like', $like)
->orWhere('bill_item', 'like', $like);
});
}
$sortable = ['occurred_at', 'income', 'expense', 'balance', 'record_code'];
$sort = in_array($request->get('sort'), $sortable, true) ? $request->get('sort') : 'occurred_at';
$dir = $request->get('dir') === 'asc' ? 'asc' : 'desc';
$query->orderBy($sort, $dir);
if ($request->boolean('export')) {
return $this->export($request, $query, $billType);
}
$perPage = min(max((int) $request->get('limit', 10), 1), 100);
$rows = $query->paginate($perPage);
// 汇总(分页前)
$summaryRow = (clone $query)->selectRaw(
'COALESCE(SUM(income), 0) as income, ' .
'COALESCE(SUM(expense), 0) as expense, ' .
'COUNT(*) as count'
)->first();
$latestBalance = (clone $query)
->orderByDesc('occurred_at')
->orderByDesc('id')
->value('balance');
$data = $rows->toArray();
$data['summary'] = [
'income' => (float) ($summaryRow->income ?? 0),
'expense' => (float) ($summaryRow->expense ?? 0),
'count' => (int) ($summaryRow->count ?? 0),
'balance' => $latestBalance !== null ? (float) $latestBalance : 0,
];
$data['columns'] = AccountBillColumns::for($billType);
return response()->json($data);
}
private function export(Request $request, $query, string $billType)
{
$definition = AccountBillColumns::for($billType);
$available = collect($definition['columns'] ?? [])->keyBy('field');
$requested = $request->input('columns', []);
$columns = $requested === []
? $available->values()
: collect($requested)->unique()->map(fn (string $field) => $available->get($field))->filter()->values();
abort_if($columns->isEmpty(), 422, '至少选择一个可导出的字段。');
return response()->streamDownload(function () use ($query, $columns): void {
$csv = fopen('php://output', 'w');
fputs($csv, chr(0xEF) . chr(0xBB) . chr(0xBF));
fputcsv($csv, $columns->pluck('label')->all());
foreach ($query->cursor() as $row) {
fputcsv($csv, $columns->map(function (array $column) use ($row): string {
$value = $this->columnValue($row, $column['field']);
return $this->csvValue($value, $column['kind']);
})->all());
}
fclose($csv);
}, $billType . '_account_bills.csv', ['Content-Type' => 'text/csv; charset=UTF-8']);
}
private function columnValue(AccountBill $row, string $field): mixed
{
if (str_starts_with($field, 'payload.')) {
return data_get($row->payload ?? [], substr($field, 8), '');
}
return $row->getAttribute($field) ?? '';
}
private function csvValue(mixed $value, string $kind): string
{
$text = (string) ($value ?? '');
if ($kind === 'num' && preg_match('/^\d{12,}$/', $text)) {
return "\t" . $text;
}
return preg_match('/^[=+\-@]/', $text) ? "'" . $text : $text;
}
public function show(AccountBill $bill)
{
$this->authorizeStoreReceivingBills();
return response()->json($bill);
}
protected function authorizeStoreReceivingBills(): void
{
$section = collect(SidebarPermissions::navigationFor(user()))
->firstWhere('slug', 'billing-transactions');
$page = collect($section['pages'] ?? [])
->firstWhere('slug', 'store-receiving-bills');
abort_if(empty($page), 403, '当前账号没有店铺收款账户账单权限。');
}
}