input('action') === 'unlink') { return redirect()->route('settlement_transactions'); } $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'], ]); } }