krgm.so-manager-dev.com/app/Http/Controllers/Admin/RegularContractController.php

529 lines
22 KiB
PHP
Raw Permalink 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 App\Http\Controllers\Admin;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;
class RegularContractController
{
/**
* 定期契約一覧
* - ベース表: regular_contractrc
* - 付加情報: useru, usertypet, parkp
* - 画面変数: $list, $sort, $sort_type既存に合わせる
*/
public function list(Request $request)
{
// ===== ソート(既定: contract_id DESC=====
$sort = $request->input('sort', 'contract_id');
$sortType = strtolower($request->input('sort_type', 'desc')) === 'asc' ? 'asc' : 'desc';
// ===== 絞り込み(テキスト系)=====
// フォームの name 属性と完全一致させる&既定値は空文字にして Blade が未定義にならないようにする
$contract_qr_id = trim((string) $request->input('contract_qr_id', ''));
$user_id = trim((string) $request->input('user_id', ''));
$park_id = trim((string) $request->input('park_id', ''));
$user_phonetic = trim((string) $request->input('user_phonetic', '')); // フリガナ
$phone = trim((string) $request->input('phone', '')); // 電話(携帯/自宅)
$email = trim((string) $request->input('email', '')); // メール
$usertype_name_kw = trim((string) $request->input('usertype_name', '')); // 利用者分類名
$park_name_kw = trim((string) $request->input('park_name', '')); // 駐輪場名
// ===== 絞り込み(日付範囲)=====
$reserve_from = $request->input('reserve_date_from', '');
$reserve_to = $request->input('reserve_date_to', '');
$created_from = $request->input('contract_created_from', '');
$created_to = $request->input('contract_created_to', '');
$updated_from = $request->input('contract_updated_from', '');
$updated_to = $request->input('contract_updated_to', '');
$canceled_from = $request->input('contract_canceled_from', '');
$canceled_to = $request->input('contract_canceled_to', '');
// ===== 列挙(全て/0/1=====
$contract_flag = $request->input('contract_flag', '');
$contract_permission = $request->input('contract_permission', '');
$tag_qr_flag = $request->input('tag_qr_flag', '');
$update_flag = $request->input('update_flag', '');
$contract_cancel_flag = $request->input('contract_cancel_flag', '');
// ===== クエリ(結合込み)=====
$q = DB::table('regular_contract as rc')
->leftJoin('user as u', 'u.user_id', '=', 'rc.user_id')
->leftJoin('usertype as t', 't.user_categoryid', '=', 'rc.user_categoryid')
->leftJoin('park as p', 'p.park_id', '=', 'rc.park_id')
->select([
// rc
'rc.contract_id',
'rc.contract_qr_id',
'rc.user_id',
'rc.user_categoryid',
'rc.reserve_id',
'rc.park_id',
'rc.price_parkplaceid',
'rc.user_securitynum',
'rc.reserve_date',
'rc.contract_reserve',
'rc.contract_created_at',
'rc.contract_updated_at',
'rc.contract_cancelday',
'rc.contract_flag',
'rc.contract_permission',
'rc.contract_cancel_flag',
'rc.tag_qr_flag',
'rc.update_flag',
'rc.park_position',
'rc.ope_id',
// user
'u.user_name',
'u.user_phonetic',
'u.user_mobile',
'u.user_homephone',
'u.user_primemail',
// usertype & park
DB::raw('t.print_name as usertype_name'),
DB::raw('p.park_name as park_name'),
]);
// ===== LIKE / キーワード =====
if ($contract_qr_id !== '') {
$q->where('rc.contract_qr_id', 'like', "%{$contract_qr_id}%");
}
if ($user_id !== '') {
$q->where('rc.user_id', 'like', "%{$user_id}%");
}
if ($park_id !== '') {
$q->where('rc.park_id', 'like', "%{$park_id}%");
}
if ($user_phonetic !== '') {
$q->where('u.user_phonetic', 'like', "%{$user_phonetic}%");
}
if ($email !== '') {
$q->where('u.user_primemail', 'like', "%{$email}%");
}
if ($usertype_name_kw !== '') {
$q->where('t.print_name', 'like', "%{$usertype_name_kw}%");
}
if ($park_name_kw !== '') {
$q->where('p.park_name', 'like', "%{$park_name_kw}%");
}
if ($phone !== '') {
$q->where(function ($w) use ($phone) {
$w->where('u.user_mobile', 'like', "%{$phone}%")
->orWhere('u.user_homephone', 'like', "%{$phone}%");
});
}
// ===== 日付範囲 =====
if ($reserve_from) {
$q->whereDate('rc.reserve_date', '>=', $reserve_from);
}
if ($reserve_to) {
$q->whereDate('rc.reserve_date', '<=', $reserve_to);
}
if ($created_from) {
$q->whereDate('rc.contract_created_at', '>=', $created_from);
}
if ($created_to) {
$q->whereDate('rc.contract_created_at', '<=', $created_to);
}
if ($updated_from) {
$q->whereDate('rc.contract_updated_at', '>=', $updated_from);
}
if ($updated_to) {
$q->whereDate('rc.contract_updated_at', '<=', $updated_to);
}
if ($canceled_from) {
$q->whereDate('rc.contract_cancelday', '>=', $canceled_from);
}
if ($canceled_to) {
$q->whereDate('rc.contract_cancelday', '<=', $canceled_to);
}
// ===== 列挙フィルタ =====
if ($contract_flag !== '') {
$q->where('rc.contract_flag', (int) $contract_flag);
}
if ($contract_permission !== '') {
$q->where('rc.contract_permission', (int) $contract_permission);
}
if ($tag_qr_flag !== '') {
$q->where('rc.tag_qr_flag', (int) $tag_qr_flag);
}
if ($update_flag !== '') {
$q->where('rc.update_flag', (int) $update_flag);
}
if ($contract_cancel_flag !== '') {
$q->where('rc.contract_cancel_flag', (int) $contract_cancel_flag);
}
// ===== ソート(仮想列は結合側にマッピング)=====
$sortable = [
'contract_id',
'contract_qr_id',
'user_id',
'user_categoryid',
'reserve_id',
'park_id',
'price_parkplaceid',
'user_securitynum',
'reserve_date',
'contract_reserve',
'contract_created_at',
'contract_updated_at',
'contract_cancelday',
'contract_flag',
'contract_permission',
'contract_cancel_flag',
'tag_qr_flag',
'update_flag',
'park_position',
'ope_id',
// 結合先の見出し列
'user_name',
'user_phonetic',
'user_mobile',
'user_homephone',
'user_primemail',
'usertype_name',
'park_name',
];
if (!in_array($sort, $sortable, true)) {
$sort = 'contract_id';
}
$sortMap = [
'user_name' => 'u.user_name',
'user_phonetic' => 'u.user_phonetic',
'user_mobile' => 'u.user_mobile',
'user_homephone' => 'u.user_homephone',
'user_primemail' => 'u.user_primemail',
'usertype_name' => 't.print_name',
'park_name' => 'p.park_name',
];
$sortColumn = $sortMap[$sort] ?? ('rc.' . $sort);
$list = $q->orderBy($sortColumn, $sortType)->paginate(50);
// ===== 画面へBlade 側が参照するすべての変数を渡す)=====
return view('admin.regularcontracts.list', [
'list' => $list,
'sort' => $sort,
'sort_type' => $sortType,
// 入力保持(テキスト)
'contract_qr_id' => $contract_qr_id,
'user_id' => $user_id,
'park_id' => $park_id,
'user_phonetic' => $user_phonetic,
'phone' => $phone,
'email' => $email,
'usertype_name' => $usertype_name_kw,
'park_name' => $park_name_kw,
// 入力保持(日付)
'reserve_date_from' => $reserve_from,
'reserve_date_to' => $reserve_to,
'contract_created_from' => $created_from,
'contract_created_to' => $created_to,
'contract_updated_from' => $updated_from,
'contract_updated_to' => $updated_to,
'contract_canceled_from' => $canceled_from,
'contract_canceled_to' => $canceled_to,
// 入力保持(列挙)
'contract_flag' => $contract_flag,
'contract_permission' => $contract_permission,
'tag_qr_flag' => $tag_qr_flag,
'update_flag' => $update_flag,
'contract_cancel_flag' => $contract_cancel_flag,
]);
}
/**
* 定期契約編集GET: 画面表示 / POST: 更新実行)
* - 主キー: contract_id
*/
public function edit(Request $request, $id)
{
$id = (int) $id;
if ($request->isMethod('get')) {
$row = DB::table('regular_contract')->where('contract_id', $id)->first();
if (!$row) {
abort(404);
}
return view('admin.regularcontracts.edit', [
'row' => $row,
'contract_id' => $id,
]);
}
$v = Validator::make($request->all(), [
'user_id' => ['required', 'integer'],
'park_id' => ['required', 'integer'],
// 任意項目
'contract_qr_id' => ['nullable', 'string', 'max:255'],
'user_categoryid' => ['nullable', 'integer'],
'reserve_id' => ['nullable', 'integer'],
'price_parkplaceid' => ['nullable', 'integer'],
'user_securitynum' => ['nullable', 'string', 'max:255'],
'reserve_date' => ['nullable', 'date'],
'contract_reserve' => ['nullable', 'string', 'max:255'],
'contract_created_at' => ['nullable', 'date'],
'contract_updated_at' => ['nullable', 'date'],
'contract_cancelday' => ['nullable', 'date'],
'contract_flag' => ['nullable', 'integer'],
'contract_permission' => ['nullable', 'integer'],
'contract_cancel_flag' => ['nullable', 'integer'],
'tag_qr_flag' => ['nullable', 'integer'],
'park_position' => ['nullable', 'string', 'max:255'],
'ope_id' => ['nullable', 'integer'],
]);
if ($v->fails()) {
return back()->withErrors($v)->withInput();
}
$data = [
'contract_qr_id' => $request->input('contract_qr_id'),
'user_id' => (int) $request->input('user_id'),
'user_categoryid' => $request->input('user_categoryid'),
'reserve_id' => $request->input('reserve_id'),
'park_id' => (int) $request->input('park_id'),
'price_parkplaceid' => $request->input('price_parkplaceid'),
'user_securitynum' => $request->input('user_securitynum'),
'reserve_date' => $request->input('reserve_date'),
'contract_reserve' => $request->input('contract_reserve'),
'contract_created_at' => $request->input('contract_created_at'),
'contract_updated_at' => $request->input('contract_updated_at'),
'contract_cancelday' => $request->input('contract_cancelday'),
'contract_flag' => $request->input('contract_flag'),
'contract_permission' => $request->input('contract_permission'),
'contract_cancel_flag' => $request->input('contract_cancel_flag'),
'tag_qr_flag' => $request->input('tag_qr_flag'),
'park_position' => $request->input('park_position'),
'ope_id' => $request->input('ope_id'),
'updated_at' => now(),
];
DB::table('regular_contract')->where('contract_id', $id)->update($data);
return redirect()->route('regularcontracts')->with('success', '定期契約を更新しました。');
}
/**
* 定期契約削除
* - 物理削除(必要なら cancel フラグ運用に切替)
*/
public function delete(Request $request)
{
$id = (int) $request->input('id');
DB::table('regular_contract')->where('contract_id', $id)->delete();
// 例:論理削除運用にする場合(必要なら運用側で切替)
// DB::table('regular_contract')->where('contract_id', $id)->update([
// 'contract_cancel_flag' => 1,
// 'contract_cancelday' => now(),
// 'updated_at' => now(),
// ]);
return redirect()->route('regularcontracts')->with('success', '定期契約を削除しました。');
}
/**
* 定期契約インポート(仮実装)
*/
public function import(Request $request)
{
if ($request->isMethod('get')) {
// GET で来たら一覧へ
return redirect()->route('regularcontracts');
}
// ファイル必須 & 形式チェック
$request->validate([
'file' => ['required', 'file', 'mimetypes:text/plain,text/csv,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'],
], [], [
'file' => 'インポートファイル',
]);
$file = $request->file('file');
// TODO: ここで実際のインポート処理CSV/XLSXの解析とレコード登録を書く
// 例Storage::putFile('imports', $file); で一旦保存してバッチに回す etc.
return redirect()->route('regularcontracts')->with('success', 'インポートを受け付けました。');
}
/**
* 定期契約エクスポート(仮実装)
* - 現時点では何もしません。ルーティング確認用のプレーンテキストを返します。
* - 後で CSV / Excel 出力処理に置き換えてください。
*/
public function export(Request $request)
{
// ── 出力タイプ(通常 / SMBC / 役所) ──────────────────────────────
$type = $request->query('type'); // null | smbc | city
// ── 出力ファイル名 ───────────────────────────────────────────────
$downloadName = '定期契約マスタ.csv';
if ($type === 'smbc')
$downloadName = '定期契約マスタ_SMBC.csv';
if ($type === 'city')
$downloadName = '定期契約マスタ_役所提出用.csv';
// ── 生成先storage/app/tmp 配下の一時ファイル) ─────────────────
$tmpDir = storage_path('app/tmp');
if (!is_dir($tmpDir)) {
@mkdir($tmpDir, 0755, true);
}
$tmpPath = $tmpDir . '/' . uniqid('regularcontracts_', true) . '.csv';
// ── CSV を作成Excel を考慮し UTF-8 BOM を付与) ───────────────
$fp = fopen($tmpPath, 'w+');
if ($fp === false) {
abort(500, 'CSV一時ファイルを作成できませんでした。');
}
// Excel 対策BOM
fwrite($fp, "\xEF\xBB\xBF");
// ヘッダー行(必要に応じて列を増減)
fputcsv($fp, ['定期契約ID', '利用者ID', '駐輪場ID', '契約日時']);
// ── データ取得(大量件数に備え chunk で分割取得) ────────────────
// ※ list() と同等の JOIN/SELECT を最低限に簡略化
DB::table('regular_contract as rc')
->leftJoin('user as u', 'u.user_id', '=', 'rc.user_id')
->orderBy('rc.contract_id', 'asc')
->select([
'rc.contract_qr_id',
'rc.user_id',
'rc.park_id',
'rc.contract_created_at',
])
->chunk(1000, function ($rows) use ($fp) {
foreach ($rows as $r) {
fputcsv($fp, [
$r->contract_qr_id,
$r->user_id,
$r->park_id,
$r->contract_created_at,
]);
}
});
fclose($fp);
// ── ダウンロードレスポンス(送信後に一時ファイル削除) ────────────
return response()->download(
$tmpPath,
$downloadName,
[
'Content-Type' => 'text/csv; charset=UTF-8',
'Content-Disposition' => 'attachment; filename="' . $downloadName . '"',
'Pragma' => 'no-cache',
'Cache-Control' => 'must-revalidate, post-check=0, pre-check=0',
'Expires' => '0',
]
)->deleteFileAfterSend(true);
}
// 追加新規登録GET: 画面表示 / POST: 登録実行)
public function add(Request $request)
{
// 画面表示
if ($request->isMethod('get')) {
return view('admin.regularcontracts.add');
}
// ========= バリデーション =========
// ※ 必須最小限。その他は任意nullable
$v = Validator::make(
$request->all(),
[
'user_id' => ['required', 'integer'],
'park_id' => ['required', 'integer'],
'contract_qr_id' => ['nullable', 'string', 'max:255'],
'user_categoryid' => ['nullable', 'integer'],
'reserve_id' => ['nullable', 'integer'],
'price_parkplaceid' => ['nullable', 'integer'],
'reserve_date' => ['nullable', 'date'],
'contract_created_at' => ['nullable', 'date'],
'contract_cancelday' => ['nullable', 'date'],
'contract_permission' => ['nullable', 'integer'],
'contract_cancel_flag' => ['nullable', 'integer'],
'tag_qr_flag' => ['nullable', 'integer'],
'update_flag' => ['nullable', 'integer'],
'park_position' => ['nullable', 'string', 'max:255'],
'ope_id' => ['nullable', 'integer'],
// 画面の「定期有効月数」は DB の contract_valid_months に保存する
'enable_months' => ['nullable', 'integer', 'min:0'],
],
[],
[
'user_id' => '利用者ID',
'park_id' => '駐輪場ID',
'contract_qr_id' => '定期契約QRID',
'user_categoryid' => '利用者分類ID',
'reserve_id' => '定期予約ID',
'price_parkplaceid' => '駐輪場所ID',
'reserve_date' => '予約日時',
'contract_created_at' => '契約日時',
'contract_cancelday' => '解約日時',
'contract_permission' => 'シール発行許可',
'contract_cancel_flag' => '解約フラグ',
'tag_qr_flag' => 'タグ・QR',
'update_flag' => '(更新元)契約更新済フラグ',
'park_position' => '駐輪位置番号',
'ope_id' => 'オペレータID',
'enable_months' => '定期有効月数',
]
);
if ($v->fails()) {
return back()->withErrors($v)->withInput();
}
// ========= 登録データ作成 =========
// ここでは「regular_contract」テーブルに確実にある列を中心に保存します。
// 追加したい列があれば、同様にキーを増やして下さい。
$data = [
'contract_qr_id' => $request->input('contract_qr_id'),
'user_id' => (int) $request->input('user_id'),
'user_categoryid' => $request->input('user_categoryid'),
'reserve_id' => $request->input('reserve_id'),
'park_id' => (int) $request->input('park_id'),
'price_parkplaceid' => $request->input('price_parkplaceid'),
'reserve_date' => $request->input('reserve_date'),
'contract_created_at' => $request->input('contract_created_at') ?: now(), // 未指定なら現在時刻
'contract_cancelday' => $request->input('contract_cancelday'),
'contract_permission' => $request->input('contract_permission'),
'contract_cancel_flag' => $request->input('contract_cancel_flag'),
'tag_qr_flag' => $request->input('tag_qr_flag'),
'update_flag' => $request->input('update_flag'),
'park_position' => $request->input('park_position'),
'ope_id' => $request->input('ope_id'),
// 画面の enable_months → DB の contract_valid_months
'contract_valid_months' => $request->input('enable_months'),
'created_at' => now(),
'updated_at' => now(),
];
DB::table('regular_contract')->insert($data);
return redirect()
->route('regularcontracts')
->with('success', '定期契約を登録しました。');
}
}