284 lines
11 KiB
PHP
284 lines
11 KiB
PHP
<?php
|
||
|
||
namespace App\Http\Controllers\Admin;
|
||
|
||
use App\Http\Controllers\Controller;
|
||
use App\Models\SettlementTransaction;
|
||
use Illuminate\Http\Request;
|
||
use Illuminate\Support\Facades\DB;
|
||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||
|
||
class SettlementTransactionController extends Controller
|
||
{
|
||
/**
|
||
* 一覧
|
||
* ルート: settlement_transactions
|
||
*/
|
||
public function list(Request $request)
|
||
{
|
||
$q = SettlementTransaction::query();
|
||
|
||
// --- 絞り込み(必要なら増やせます)
|
||
$contractId = $request->input('contract_id');
|
||
$status = trim((string)$request->input('status', ''));
|
||
$from = $request->input('from'); // 支払日時 from
|
||
$to = $request->input('to'); // 支払日時 to
|
||
|
||
if ($contractId !== null && $contractId !== '') {
|
||
$q->where('contract_id', (int)$contractId);
|
||
}
|
||
if ($status !== '') {
|
||
$q->where('status', 'like', "%{$status}%");
|
||
}
|
||
if ($from) {
|
||
$q->whereDate('pay_date', '>=', $from);
|
||
}
|
||
if ($to) {
|
||
$q->whereDate('pay_date', '<=', $to);
|
||
}
|
||
|
||
// --- ソート(既定:created_at desc)
|
||
$sort = $request->input('sort', 'created_at');
|
||
$type = strtolower($request->input('sort_type', 'desc'));
|
||
$allow = [
|
||
'settlement_transaction_id', 'created_at', 'updated_at',
|
||
'contract_id', 'status', 'pay_date', 'settlement_amount',
|
||
];
|
||
if (!in_array($sort, $allow, true)) $sort = 'created_at';
|
||
if (!in_array($type, ['asc', 'desc'], true)) $type = 'desc';
|
||
$q->orderBy($sort, $type);
|
||
|
||
return view('admin.settlement_transactions.list', [
|
||
'transactions' => $q->paginate(20)->appends($request->except('page')),
|
||
'contract_id' => $contractId,
|
||
'status' => $status,
|
||
'from' => $from,
|
||
'to' => $to,
|
||
'sort' => $sort,
|
||
'sort_type' => $type,
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* 新規
|
||
* ルート: settlement_transactions_add
|
||
*/
|
||
public function add(Request $request)
|
||
{
|
||
if ($request->isMethod('post')) {
|
||
$data = $this->validatePayload($request);
|
||
SettlementTransaction::create($data);
|
||
return redirect()->route('settlement_transactions')->with('success', '登録しました');
|
||
}
|
||
|
||
return view('admin.settlement_transactions.add', [
|
||
'transaction' => null,
|
||
'isEdit' => false,
|
||
'isInfo' => false,
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* 編集
|
||
* ルート: settlement_transactions_edit
|
||
*/
|
||
public function edit(int $settlement_transaction_id, Request $request)
|
||
{
|
||
$transaction = SettlementTransaction::findOrFail($settlement_transaction_id);
|
||
|
||
if ($request->isMethod('post')) {
|
||
$data = $this->validatePayload($request);
|
||
$transaction->update($data);
|
||
return redirect()->route('settlement_transactions')->with('success', '更新しました');
|
||
}
|
||
|
||
return view('admin.settlement_transactions.edit', [
|
||
'transaction' => $transaction,
|
||
'isEdit' => true,
|
||
'isInfo' => false,
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* 詳細
|
||
* ルート: settlement_transactions_info
|
||
*/
|
||
public function info(int $settlement_transaction_id)
|
||
{
|
||
$transaction = SettlementTransaction::findOrFail($settlement_transaction_id);
|
||
|
||
return view('admin.settlement_transactions.info', [
|
||
'transaction' => $transaction,
|
||
'isEdit' => false,
|
||
'isInfo' => true,
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* 一括削除(一覧のチェック name="ids[]")
|
||
* ルート: settlement_transactions_delete
|
||
*/
|
||
public function delete(Request $request)
|
||
{
|
||
$ids = (array) $request->input('ids', []);
|
||
$ids = array_values(array_filter($ids, fn($v) => preg_match('/^\d+$/', (string)$v)));
|
||
|
||
if (!$ids) {
|
||
return redirect()->route('settlement_transactions')->with('error', '削除対象が選択されていません。');
|
||
}
|
||
|
||
SettlementTransaction::whereIn('settlement_transaction_id', $ids)->delete();
|
||
|
||
return redirect()->route('settlement_transactions')->with('success', '削除しました');
|
||
}
|
||
|
||
/**
|
||
* CSVインポート(簡易)
|
||
* ルート: settlement_transactions_import
|
||
*
|
||
* 想定カラム順:
|
||
* contract_id,status,pay_code,contract_payment_number,corp_code,
|
||
* mms_date,cvs_code,shop_code,pay_date,settlement_amount,stamp_flag,md5_string
|
||
* 1行目ヘッダ可
|
||
*/
|
||
public function import(Request $request)
|
||
{
|
||
$request->validate([
|
||
'file' => ['required', 'file', 'mimetypes:text/plain,text/csv,text/tsv', 'max:4096'],
|
||
]);
|
||
|
||
$path = $request->file('file')->getRealPath();
|
||
|
||
$created = 0;
|
||
$updated = 0;
|
||
$skipped = 0;
|
||
|
||
DB::beginTransaction();
|
||
try {
|
||
if (($fp = fopen($path, 'r')) !== false) {
|
||
$line = 0;
|
||
while (($row = fgetcsv($fp)) !== false) {
|
||
$line++;
|
||
|
||
// ヘッダ行をスキップ
|
||
if ($line === 1) {
|
||
$joined = strtolower(implode(',', $row));
|
||
if (str_contains($joined, 'contract_id') || str_contains($joined, 'status')) {
|
||
continue;
|
||
}
|
||
}
|
||
|
||
// 入力列を安全に展開
|
||
[$contract_id,$status,$pay_code,$contract_payment_number,$corp_code,$mms_date,$cvs_code,$shop_code,$pay_date,$settlement_amount,$stamp_flag,$md5_string] = array_pad($row, 12, null);
|
||
|
||
// 正規化
|
||
$payload = [
|
||
'contract_id' => ($contract_id === '' || $contract_id === null) ? null : (int)$contract_id,
|
||
'status' => $status !== null ? trim($status) : null,
|
||
'pay_code' => $pay_code !== null ? trim($pay_code) : null,
|
||
'contract_payment_number' => $contract_payment_number !== null ? trim($contract_payment_number) : null,
|
||
'corp_code' => $corp_code !== null ? trim($corp_code) : null,
|
||
'mms_date' => $mms_date !== null ? trim($mms_date) : null,
|
||
'cvs_code' => $cvs_code !== null ? trim($cvs_code) : null,
|
||
'shop_code' => $shop_code !== null ? trim($shop_code) : null,
|
||
'pay_date' => $pay_date ? date('Y-m-d H:i:s', strtotime($pay_date)) : null,
|
||
'settlement_amount' => ($settlement_amount === '' || $settlement_amount === null) ? null : (float)preg_replace('/[^\d.]/','',$settlement_amount),
|
||
'stamp_flag' => $stamp_flag !== null ? trim($stamp_flag) : null,
|
||
'md5_string' => $md5_string !== null ? trim($md5_string) : null,
|
||
];
|
||
|
||
// upsert キー(優先: md5_string、なければ contract_id+pay_date)
|
||
$ex = null;
|
||
if (!empty($payload['md5_string'])) {
|
||
$ex = SettlementTransaction::where('md5_string', $payload['md5_string'])->first();
|
||
} elseif (!empty($payload['contract_id']) && !empty($payload['pay_date'])) {
|
||
$ex = SettlementTransaction::where('contract_id', $payload['contract_id'])
|
||
->where('pay_date', $payload['pay_date'])->first();
|
||
}
|
||
|
||
if ($ex) { $ex->update($payload); $updated++; }
|
||
else { SettlementTransaction::create($payload); $created++; }
|
||
}
|
||
fclose($fp);
|
||
}
|
||
|
||
DB::commit();
|
||
return redirect()->route('settlement_transactions')
|
||
->with('success', "インポート完了:新規 {$created} 件、更新 {$updated} 件、スキップ {$skipped} 件");
|
||
} catch (\Throwable $e) {
|
||
DB::rollBack();
|
||
return redirect()->route('settlement_transactions')
|
||
->with('error', 'インポートに失敗しました:' . $e->getMessage());
|
||
}
|
||
}
|
||
|
||
/**
|
||
* CSVエクスポート
|
||
* ルート: settlement_transactions_export
|
||
*/
|
||
public function export(Request $request): StreamedResponse
|
||
{
|
||
$q = SettlementTransaction::query();
|
||
|
||
// 一覧と同じソートを適用(任意で絞り込みも追加可能)
|
||
$sort = $request->input('sort', 'created_at');
|
||
$type = strtolower($request->input('sort_type', 'desc'));
|
||
if (!in_array($type, ['asc','desc'], true)) $type = 'desc';
|
||
$q->orderBy($sort, $type);
|
||
|
||
$filename = 'settlement_transactions_' . now()->format('Ymd_His') . '.csv';
|
||
|
||
return response()->streamDownload(function () use ($q) {
|
||
$out = fopen('php://output', 'w');
|
||
fputcsv($out, [
|
||
'ID','契約ID','ステータス','支払コード','契約課金番号','企業コード',
|
||
'MMS日付','CVSコード','店舗コード','支払日時','金額','スタンプ','MD5',
|
||
'登録日時','更新日時'
|
||
]);
|
||
$q->chunk(500, function ($rows) use ($out) {
|
||
foreach ($rows as $r) {
|
||
fputcsv($out, [
|
||
$r->settlement_transaction_id,
|
||
$r->contract_id,
|
||
$r->status,
|
||
$r->pay_code,
|
||
$r->contract_payment_number,
|
||
$r->corp_code,
|
||
$r->mms_date,
|
||
$r->cvs_code,
|
||
$r->shop_code,
|
||
optional($r->pay_date)->format('Y-m-d H:i:s'),
|
||
$r->settlement_amount,
|
||
$r->stamp_flag,
|
||
$r->md5_string,
|
||
optional($r->created_at)->format('Y-m-d H:i:s'),
|
||
optional($r->updated_at)->format('Y-m-d H:i:s'),
|
||
]);
|
||
}
|
||
});
|
||
fclose($out);
|
||
}, $filename, ['Content-Type' => 'text/csv; charset=UTF-8']);
|
||
}
|
||
|
||
/**
|
||
* 共通バリデーション
|
||
*/
|
||
private function validatePayload(Request $request): array
|
||
{
|
||
return $request->validate([
|
||
'contract_id' => ['nullable','integer'],
|
||
'status' => ['nullable','string','max:255'],
|
||
'pay_code' => ['nullable','string','max:255'],
|
||
'contract_payment_number' => ['nullable','string','max:255'],
|
||
'corp_code' => ['nullable','string','max:255'],
|
||
'mms_date' => ['nullable','string','max:255'],
|
||
'cvs_code' => ['nullable','string','max:255'],
|
||
'shop_code' => ['nullable','string','max:255'],
|
||
'pay_date' => ['nullable','date'],
|
||
'settlement_amount' => ['nullable','numeric'], // DB は decimal(10,0)
|
||
'stamp_flag' => ['nullable','string','max:255'],
|
||
'md5_string' => ['nullable','string','max:255'],
|
||
]);
|
||
}
|
||
}
|