【定期契約マスタ】絞り込み修正
All checks were successful
Deploy main / deploy (push) Successful in 22s

This commit is contained in:
你的名字 2025-10-06 14:48:41 +09:00
parent 6c08ea4599
commit dd88338fb1
6 changed files with 1051 additions and 406 deletions

View File

@ -5,9 +5,91 @@ namespace App\Http\Controllers\Admin;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator; use Illuminate\Support\Facades\Validator;
use App\Models\Park;
use Illuminate\Support\Carbon;
class RegularContractController class RegularContractController
{ {
/**
* 利用者分類選択肢user_categoryid 昇順)
*/
private function buildUsertypeOptions(): array
{
return DB::table('usertype as t')
->join('regular_contract as rc', 'rc.user_categoryid', '=', 't.user_categoryid')
->select('t.user_categoryid', 't.usertype_subject1', 't.usertype_subject2', 't.usertype_subject3')
->groupBy('t.user_categoryid', 't.usertype_subject1', 't.usertype_subject2', 't.usertype_subject3')
->orderBy('t.user_categoryid', 'asc')
->get()
->mapWithKeys(function ($row) {
$label = collect([
$row->usertype_subject1 ?? '',
$row->usertype_subject2 ?? '',
$row->usertype_subject3 ?? '',
])->filter(fn ($v) => $v !== '')->implode('/');
return [$row->user_categoryid => $label !== '' ? $label : (string) $row->user_categoryid];
})
->toArray();
}
private function buildParkOptions(): array
{
return Park::query()
->join('regular_contract as rc', 'rc.park_id', '=', 'park.park_id')
->select('park.park_id', 'park.park_name')
->groupBy('park.park_id', 'park.park_name')
->orderBy('park.park_id', 'asc')
->get()
->mapWithKeys(fn ($park) => [
$park->park_id => $park->park_name ?: (string) $park->park_id,
])
->toArray();
}
/**
* datetime-local から受け取った値を Y-m-d H:i:s へ正規化
*/
private function normalizeDateTimeInput(?string $value, bool $endOfMinute = false): ?string
{
if ($value === null) {
return null;
}
$value = trim($value);
if ($value === '') {
return null;
}
$value = str_replace('T', ' ', $value);
if (strlen($value) === 16) {
$value .= ':00';
}
try {
$dt = Carbon::parse($value);
if ($endOfMinute) {
$dt = $dt->endOfMinute();
}
return $dt->format('Y-m-d H:i:s');
} catch (\Throwable $e) {
return null;
}
}
/**
* 名寄フリガナ検索用:全角カナへ統一し空白除去
*/
private function normalizePhoneticKeyword(?string $value): ?string
{
if ($value === null) {
return null;
}
$value = trim((string) $value);
if ($value === '') {
return null;
}
$value = mb_convert_kana($value, 'KVCS');
return str_replace([' ', ' '], '', $value);
}
/** /**
* 定期契約一覧 * 定期契約一覧
* - ベース表: regular_contractrc * - ベース表: regular_contractrc
@ -16,74 +98,86 @@ class RegularContractController
*/ */
public function list(Request $request) public function list(Request $request)
{ {
// ===== ソート(既定: contract_id DESC===== if ($request->isMethod('post')) {
$sort = $request->input('sort', 'contract_id'); $postParams = $request->except(['_token']);
$sortType = strtolower($request->input('sort_type', 'desc')) === 'asc' ? 'asc' : 'desc'; $queryParams = $request->query();
return redirect()->route('regularcontracts', array_merge($queryParams, $postParams));
}
$params = $request->query();
// ===== ソート(既定: contract_id ASC=====
$sort = $params['sort'] ?? 'contract_id';
$sortType = strtolower($params['sort_type'] ?? 'asc') === 'desc' ? 'desc' : 'asc';
// ===== 絞り込み(テキスト系)===== // ===== 絞り込み(テキスト系)=====
// フォームの name 属性と完全一致させる&既定値は空文字にして Blade が未定義にならないようにする $contract_qr_id = trim((string) ($params['contract_qr_id'] ?? ''));
$contract_qr_id = trim((string) $request->input('contract_qr_id', '')); $user_id = trim((string) ($params['user_id'] ?? ''));
$user_id = trim((string) $request->input('user_id', '')); $user_tag_serial = trim((string) ($params['user_tag_serial'] ?? ''));
$park_id = trim((string) $request->input('park_id', '')); $park_id = trim((string) ($params['park_id'] ?? ''));
$user_phonetic = trim((string) $request->input('user_phonetic', '')); // フリガナ $selectedParkId = trim((string) ($params['selected_park_id'] ?? ''));
$phone = trim((string) $request->input('phone', '')); // 電話(携帯/自宅) $user_phonetic = trim((string) ($params['user_phonetic'] ?? ''));
$email = trim((string) $request->input('email', '')); // メール $phone = trim((string) ($params['phone'] ?? ''));
$usertype_name_kw = trim((string) $request->input('usertype_name', '')); // 利用者分類名 $email = trim((string) ($params['email'] ?? ''));
$park_name_kw = trim((string) $request->input('park_name', '')); // 駐輪場名 $user_categoryid = trim((string) ($params['user_categoryid'] ?? ''));
$park_name_kw = trim((string) ($params['park_name'] ?? ''));
$zone_keyword = trim((string) ($params['zone_keyword'] ?? ''));
$zone_name = trim((string) ($params['zone_name'] ?? ''));
$merge_phonetic_input = $params['merge_phonetic'] ?? '';
$merge_phonetic = trim((string) $merge_phonetic_input);
$merge_phonetic_normalized = $this->normalizePhoneticKeyword($merge_phonetic);
$has_address = $params['has_address'] ?? '';
$workRecordFilter = (string) ($params['work_record'] ?? '0');
if (!in_array($workRecordFilter, ['0', '1', '2'], true)) {
$workRecordFilter = '0';
}
// ===== 絞り込み(日付範囲)===== // ===== 絞り込み(日付範囲)=====
$reserve_from = $request->input('reserve_date_from', ''); $reserve_from = $params['reserve_date_from'] ?? '';
$reserve_to = $request->input('reserve_date_to', ''); $reserve_to = $params['reserve_date_to'] ?? '';
$created_from = $request->input('contract_created_from', ''); $created_from = $params['contract_created_from'] ?? '';
$created_to = $request->input('contract_created_to', ''); $created_to = $params['contract_created_to'] ?? '';
$updated_from = $request->input('contract_updated_from', ''); $updated_from = $params['contract_updated_from'] ?? '';
$updated_to = $request->input('contract_updated_to', ''); $updated_to = $params['contract_updated_to'] ?? '';
$canceled_from = $request->input('contract_canceled_from', ''); $canceled_from = $params['contract_canceled_from'] ?? '';
$canceled_to = $request->input('contract_canceled_to', ''); $canceled_to = $params['contract_canceled_to'] ?? '';
$receipt_delivery_from = $params['receipt_delivery_from'] ?? '';
$receipt_delivery_to = $params['receipt_delivery_to'] ?? '';
$contract_valid_months = $params['contract_valid_months'] ?? '';
// ===== 列挙(全て/0/1===== // ===== 列挙(全て/0/1=====
$contract_flag = $request->input('contract_flag', ''); $contract_flag = $params['contract_flag'] ?? '';
$contract_permission = $request->input('contract_permission', ''); $contract_permission = $params['contract_permission'] ?? '';
$tag_qr_flag = $request->input('tag_qr_flag', ''); $tag_qr_flag = $params['tag_qr_flag'] ?? '';
$update_flag = $request->input('update_flag', ''); $updateFlagFilter = (string) ($params['update_flag'] ?? '0');
$contract_cancel_flag = $request->input('contract_cancel_flag', ''); if (!in_array($updateFlagFilter, ['0', '1', '2'], true)) {
$updateFlagFilter = '0';
}
$contract_cancel_flag = $params['contract_cancel_flag'] ?? '';
// ===== クエリ(結合込み)===== // ===== クエリ(結合込み)=====
$q = DB::table('regular_contract as rc') $q = DB::table('regular_contract as rc')
->leftJoin('user as u', 'u.user_id', '=', 'rc.user_id') ->leftJoin('user as u', 'u.user_id', '=', 'rc.user_id')
->leftJoin('usertype as t', 't.user_categoryid', '=', 'rc.user_categoryid') ->leftJoin('usertype as t', 't.user_categoryid', '=', 'rc.user_categoryid')
->leftJoin('park as p', 'p.park_id', '=', 'rc.park_id') ->leftJoin('park as p', 'p.park_id', '=', 'rc.park_id')
->leftJoin('zone as z', 'z.zone_id', '=', 'rc.zone_id')
->select([ ->select([
// rc 'rc.*',
'rc.contract_id', 'u.user_seq',
'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_name',
'u.user_phonetic', 'u.user_phonetic',
'u.user_mobile', 'u.user_mobile',
'u.user_homephone', 'u.user_homephone',
'u.user_primemail', 'u.user_primemail',
// usertype & park 'u.user_regident_zip',
'u.user_tag_serial',
DB::raw('t.print_name as usertype_name'), DB::raw('t.print_name as usertype_name'),
't.usertype_subject1',
't.usertype_subject2',
't.usertype_subject3',
DB::raw('p.park_name as park_name'), DB::raw('p.park_name as park_name'),
DB::raw('z.zone_name as zone_name'),
]); ]);
// ===== LIKE / キーワード ===== // ===== LIKE / キーワード =====
@ -93,8 +187,13 @@ class RegularContractController
if ($user_id !== '') { if ($user_id !== '') {
$q->where('rc.user_id', 'like', "%{$user_id}%"); $q->where('rc.user_id', 'like', "%{$user_id}%");
} }
if ($user_tag_serial !== '') {
$q->where('u.user_tag_serial', 'like', "%{$user_tag_serial}%");
}
if ($park_id !== '') { if ($park_id !== '') {
$q->where('rc.park_id', 'like', "%{$park_id}%"); $q->where('rc.park_id', (int) $park_id);
} elseif ($selectedParkId !== '') {
$q->where('rc.park_id', (int) $selectedParkId);
} }
if ($user_phonetic !== '') { if ($user_phonetic !== '') {
$q->where('u.user_phonetic', 'like', "%{$user_phonetic}%"); $q->where('u.user_phonetic', 'like', "%{$user_phonetic}%");
@ -102,46 +201,73 @@ class RegularContractController
if ($email !== '') { if ($email !== '') {
$q->where('u.user_primemail', 'like', "%{$email}%"); $q->where('u.user_primemail', 'like', "%{$email}%");
} }
if ($usertype_name_kw !== '') { if ($user_categoryid !== '') {
$q->where('t.print_name', 'like', "%{$usertype_name_kw}%"); $q->where('rc.user_categoryid', (int) $user_categoryid);
} }
if ($park_name_kw !== '') { if ($park_name_kw !== '') {
$q->where('p.park_name', 'like', "%{$park_name_kw}%"); $q->where('p.park_name', 'like', "%{$park_name_kw}%");
} }
if ($zone_name !== '') {
$q->where('z.zone_name', 'like', "%{$zone_name}%");
}
if ($merge_phonetic_normalized !== null) {
$likeKeyword = '%' . $merge_phonetic_normalized . '%';
$q->whereRaw("REPLACE(REPLACE(IFNULL(rc.chk_user_phonetic, ''), ' ', ''), ' ', '') LIKE ?", [$likeKeyword]);
}
if ($phone !== '') { if ($phone !== '') {
$q->where(function ($w) use ($phone) { $q->where(function ($w) use ($phone) {
$w->where('u.user_mobile', 'like', "%{$phone}%") $w->where('u.user_mobile', 'like', "%{$phone}%")
->orWhere('u.user_homephone', 'like', "%{$phone}%"); ->orWhere('u.user_homephone', 'like', "%{$phone}%");
}); });
} }
if ($reserve_from !== '' && ($normalized = $this->normalizeDateTimeInput($reserve_from))) {
// ===== 日付範囲 ===== $q->where('rc.reserve_date', '>=', $normalized);
if ($reserve_from) {
$q->whereDate('rc.reserve_date', '>=', $reserve_from);
} }
if ($reserve_to) { if ($reserve_to !== '' && ($normalized = $this->normalizeDateTimeInput($reserve_to, true))) {
$q->whereDate('rc.reserve_date', '<=', $reserve_to); $q->where('rc.reserve_date', '<=', $normalized);
} }
if ($created_from) { if ($receipt_delivery_from !== '' && ($normalized = $this->normalizeDateTimeInput($receipt_delivery_from))) {
$q->whereDate('rc.contract_created_at', '>=', $created_from); $q->where('rc.contract_payment_day', '>=', $normalized);
} }
if ($created_to) { if ($receipt_delivery_to !== '' && ($normalized = $this->normalizeDateTimeInput($receipt_delivery_to, true))) {
$q->whereDate('rc.contract_created_at', '<=', $created_to); $q->where('rc.contract_payment_day', '<=', $normalized);
} }
if ($updated_from) { if ($zone_keyword !== '') {
$q->whereDate('rc.contract_updated_at', '>=', $updated_from); $q->where(function ($w) use ($zone_keyword) {
$w->where('rc.zone_id', 'like', "%{$zone_keyword}%")
->orWhere('rc.pplace_no', 'like', "%{$zone_keyword}%")
->orWhere('rc.old_contract_id', 'like', "%{$zone_keyword}%");
});
} }
if ($updated_to) { if ($workRecordFilter === '1') {
$q->whereDate('rc.contract_updated_at', '<=', $updated_to); $q->where(function ($w) {
$w->whereNull('rc.contract_flag')
->orWhere('rc.contract_flag', '=', 0);
});
} elseif ($workRecordFilter === '2') {
$q->where('rc.contract_flag', '=', 1);
} }
if ($canceled_from) { if ($created_from !== '' && ($normalized = $this->normalizeDateTimeInput($created_from))) {
$q->whereDate('rc.contract_cancelday', '>=', $canceled_from); $q->where('rc.contract_created_at', '>=', $normalized);
} }
if ($canceled_to) { if ($created_to !== '' && ($normalized = $this->normalizeDateTimeInput($created_to, true))) {
$q->whereDate('rc.contract_cancelday', '<=', $canceled_to); $q->where('rc.contract_created_at', '<=', $normalized);
}
if ($updated_from !== '' && ($normalized = $this->normalizeDateTimeInput($updated_from))) {
$q->where('rc.contract_updated_at', '>=', $normalized);
}
if ($updated_to !== '' && ($normalized = $this->normalizeDateTimeInput($updated_to, true))) {
$q->where('rc.contract_updated_at', '<=', $normalized);
}
if ($canceled_from !== '' && ($normalized = $this->normalizeDateTimeInput($canceled_from))) {
$q->where('rc.contract_cancelday', '>=', $normalized);
}
if ($canceled_to !== '' && ($normalized = $this->normalizeDateTimeInput($canceled_to, true))) {
$q->where('rc.contract_cancelday', '<=', $normalized);
}
if ($contract_valid_months !== '') {
$q->where('rc.enable_months', (int) $contract_valid_months);
} }
// ===== 列挙フィルタ =====
if ($contract_flag !== '') { if ($contract_flag !== '') {
$q->where('rc.contract_flag', (int) $contract_flag); $q->where('rc.contract_flag', (int) $contract_flag);
} }
@ -151,8 +277,13 @@ class RegularContractController
if ($tag_qr_flag !== '') { if ($tag_qr_flag !== '') {
$q->where('rc.tag_qr_flag', (int) $tag_qr_flag); $q->where('rc.tag_qr_flag', (int) $tag_qr_flag);
} }
if ($update_flag !== '') { if ($updateFlagFilter === '1') {
$q->where('rc.update_flag', (int) $update_flag); $q->where('rc.update_flag', '=', 1);
} elseif ($updateFlagFilter === '2') {
$q->where(function ($w) {
$w->whereNull('rc.update_flag')
->orWhere('rc.update_flag', '!=', 1);
});
} }
if ($contract_cancel_flag !== '') { if ($contract_cancel_flag !== '') {
$q->where('rc.contract_cancel_flag', (int) $contract_cancel_flag); $q->where('rc.contract_cancel_flag', (int) $contract_cancel_flag);
@ -160,34 +291,16 @@ class RegularContractController
// ===== ソート(仮想列は結合側にマッピング)===== // ===== ソート(仮想列は結合側にマッピング)=====
$sortable = [ $sortable = [
'contract_id', 'contract_id', 'contract_qr_id', 'old_contract_id', 'zone_id', 'zone_name', 'pplace_no',
'contract_qr_id', 'contract_periods', 'contract_periode', 'user_id', 'user_categoryid', 'reserve_id', 'park_id',
'user_id', 'price_parkplaceid', 'user_securitynum', 'reserve_date', 'contract_reserve', 'contract_created_at',
'user_categoryid', 'contract_updated_at', 'contract_cancelday', 'contract_reduction', 'enable_months', 'printable_date',
'reserve_id', 'billing_amount', 'contract_payment_day', 'contract_money', 'refunds', 'contract_flag',
'park_id', 'contract_permission', 'contract_cancel_flag', 'tag_qr_flag', 'update_flag', 'pplace_allocation_flag',
'price_parkplaceid', 'settlement_transaction_id', 'contract_seal_issue', 'storage_company_code', 'share_storage_company_code',
'user_securitynum', 'ope_id', 'park_position', 'contract_manual', 'contract_notice', 'contract_payment_number',
'reserve_date', 'user_name', 'user_phonetic', 'user_mobile', 'user_homephone', 'user_primemail', 'user_regident_zip',
'contract_reserve', 'usertype_name', 'park_name',
'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)) { if (!in_array($sort, $sortable, true)) {
$sort = 'contract_id'; $sort = 'contract_id';
@ -198,12 +311,14 @@ class RegularContractController
'user_mobile' => 'u.user_mobile', 'user_mobile' => 'u.user_mobile',
'user_homephone' => 'u.user_homephone', 'user_homephone' => 'u.user_homephone',
'user_primemail' => 'u.user_primemail', 'user_primemail' => 'u.user_primemail',
'user_regident_zip' => 'u.user_regident_zip',
'usertype_name' => 't.print_name', 'usertype_name' => 't.print_name',
'park_name' => 'p.park_name', 'park_name' => 'p.park_name',
'zone_name' => 'z.zone_name',
]; ];
$sortColumn = $sortMap[$sort] ?? ('rc.' . $sort); $sortColumn = $sortMap[$sort] ?? ('rc.' . $sort);
$list = $q->orderBy($sortColumn, $sortType)->paginate(50); $list = $q->orderBy($sortColumn, $sortType)->paginate(50)->withQueryString();
// ===== 画面へBlade 側が参照するすべての変数を渡す)===== // ===== 画面へBlade 側が参照するすべての変数を渡す)=====
return view('admin.regularcontracts.list', [ return view('admin.regularcontracts.list', [
@ -214,12 +329,18 @@ class RegularContractController
// 入力保持(テキスト) // 入力保持(テキスト)
'contract_qr_id' => $contract_qr_id, 'contract_qr_id' => $contract_qr_id,
'user_id' => $user_id, 'user_id' => $user_id,
'park_id' => $park_id, 'user_tag_serial' => $user_tag_serial,
'park_id' => $selectedParkId !== '' ? $selectedParkId : $park_id,
'user_phonetic' => $user_phonetic, 'user_phonetic' => $user_phonetic,
'phone' => $phone, 'phone' => $phone,
'email' => $email, 'email' => $email,
'usertype_name' => $usertype_name_kw, 'user_categoryid' => $user_categoryid,
'park_name' => $park_name_kw, 'park_name' => $park_name_kw,
'zone_keyword' => $zone_keyword,
'zone_name' => $zone_name,
'merge_phonetic' => $merge_phonetic,
'has_address' => $has_address,
'work_record' => $workRecordFilter,
// 入力保持(日付) // 入力保持(日付)
'reserve_date_from' => $reserve_from, 'reserve_date_from' => $reserve_from,
@ -230,13 +351,19 @@ class RegularContractController
'contract_updated_to' => $updated_to, 'contract_updated_to' => $updated_to,
'contract_canceled_from' => $canceled_from, 'contract_canceled_from' => $canceled_from,
'contract_canceled_to' => $canceled_to, 'contract_canceled_to' => $canceled_to,
'receipt_delivery_from' => $receipt_delivery_from,
'receipt_delivery_to' => $receipt_delivery_to,
'contract_valid_months' => $contract_valid_months,
// 入力保持(列挙) // 入力保持(列挙)
'contract_flag' => $contract_flag, 'contract_flag' => $contract_flag,
'contract_permission' => $contract_permission, 'contract_permission' => $contract_permission,
'tag_qr_flag' => $tag_qr_flag, 'tag_qr_flag' => $tag_qr_flag,
'update_flag' => $update_flag, 'update_flag' => $updateFlagFilter,
'contract_cancel_flag' => $contract_cancel_flag, 'contract_cancel_flag' => $contract_cancel_flag,
'userTypeOptions' => $this->buildUsertypeOptions(),
'parkOptions' => $this->buildParkOptions(),
]); ]);
} }
@ -318,15 +445,23 @@ class RegularContractController
*/ */
public function delete(Request $request) public function delete(Request $request)
{ {
$id = (int) $request->input('id'); $ids = $request->input('ids', []);
DB::table('regular_contract')->where('contract_id', $id)->delete(); if (!is_array($ids)) {
$ids = [$ids];
}
$ids = array_values(array_filter(
array_map('intval', $ids),
static fn (int $v) => $v > 0
));
// 例:論理削除運用にする場合(必要なら運用側で切替) if (empty($ids)) {
// DB::table('regular_contract')->where('contract_id', $id)->update([ return redirect()->route('regularcontracts')
// 'contract_cancel_flag' => 1, ->with('error', '削除する定期契約が選択されていません。');
// 'contract_cancelday' => now(), }
// 'updated_at' => now(),
// ]); DB::table('regular_contract')
->whereIn('contract_id', $ids)
->delete();
return redirect()->route('regularcontracts')->with('success', '定期契約を削除しました。'); return redirect()->route('regularcontracts')->with('success', '定期契約を削除しました。');
} }
@ -366,70 +501,115 @@ class RegularContractController
public function export(Request $request) public function export(Request $request)
{ {
// ── 出力タイプ(通常 / SMBC / 役所) ────────────────────────────── $params = $request->all();
$type = $request->query('type'); // null | smbc | city $sort = $params['sort'] ?? 'contract_id';
$sortType = strtolower($params['sort_type'] ?? 'asc') === 'desc' ? 'desc' : 'asc';
$type = (string) ($request->query('type') ?? '');
// ── 出力ファイル名 ─────────────────────────────────────────────── $fileName = match ($type) {
$downloadName = '定期契約マスタ.csv'; 'smbc' => '定期契約マスタ_SMBC.csv',
if ($type === 'smbc') 'city' => '定期契約マスタ_役所提出用.csv',
$downloadName = '定期契約マスタ_SMBC.csv'; default => '定期契約マスタ.csv',
if ($type === 'city') };
$downloadName = '定期契約マスタ_役所提出用.csv';
// ── 生成先storage/app/tmp 配下の一時ファイル) ───────────────── $query = $this->buildListQuery($params);
$tmpDir = storage_path('app/tmp');
if (!is_dir($tmpDir)) { $columns = [
@mkdir($tmpDir, 0755, true); 'contract_id' => '契約ID',
'contract_qr_id' => '定期契約ID',
'old_contract_id' => '旧定期契約番号',
'pplace_no' => '車室番号',
'user_id' => '利用者ID',
'user_categoryid' => '利用者分類ID',
'tag_qr_flag' => 'タグ・QR',
'park_id' => '駐輪場ID',
'reserve_date' => '予約日時',
'contract_periods' => '有効期間S',
'contract_periode' => '有効期間E',
'price_parkplaceid' => '駐輪場所ID',
'user_securitynum' => '防犯登録番号',
'contract_created_at' => '契約日時',
'contract_updated_at' => '更新可能日',
'contract_cancelday' => '解約日時',
'contract_reduction' => '減免措置',
'enable_months' => '定期有効月数',
'printable_date' => 'シール印刷可能日',
'billing_amount' => '請求金額',
'pplace_allocation_flag' => '車室割り当てフラグ',
'contract_payment_day' => '授受日時',
'contract_money' => '授受金額',
'contract_flag' => '授受フラグ',
'settlement_transaction_id' => '決済トランザクションID',
'contract_seal_issue' => 'シール発行数',
'storage_company_code' => '収納企業コード',
'share_storage_company_code' => '共有先収納企業コード',
'accept_number' => '受付番号',
'update_flag' => '(更新元)契約更新済フラグ',
'vehicle_type_id' => '車種区分ID',
'chk_user_phonetic' => 'チェック用_フリガナ',
'user_regident_zip' => 'チェック用_居住所郵便番号',
'user_mobile' => 'チェック用_携帯電話番号',
'user_homephone' => 'チェック用_自宅電話番号',
'old_member_number' => 'チェック用_旧会員番号',
'user_name' => '利用者氏名',
'user_phonetic' => '利用者フリガナ',
'park_name' => '駐輪場名',
'zone_name' => 'ゾーン名',
'usertype_name' => '利用者分類名',
];
$dateColumns = [
'contract_periods',
'contract_periode',
'contract_created_at',
'contract_updated_at',
'contract_cancelday',
'printable_date',
'contract_payment_day',
'reserve_date',
];
$rows = $query->orderBy($sort, $sortType)->get();
$headers = [
'Content-Type' => 'text/csv; charset=Shift_JIS',
'Content-Disposition' => "attachment; filename=\"{$fileName}\"",
];
return response()->streamDownload(function () use ($rows, $columns, $dateColumns) {
$handle = fopen('php://output', 'w');
$headerRow = array_map(fn ($label) => mb_convert_encoding($label, 'SJIS-win', 'UTF-8'), array_values($columns));
fputcsv($handle, $headerRow);
foreach ($rows as $row) {
$line = [];
foreach ($columns as $key => $label) {
$value = $row->{$key} ?? '';
if (in_array($key, $dateColumns, true) && $value) {
try {
$value = \Illuminate\Support\Carbon::parse($value)->format(str_contains($key, '_day') || str_contains($key, '_date') ? 'Y-m-d H:i' : 'Y-m-d');
} catch (\Throwable $e) {
$value = (string) $value;
} }
$tmpPath = $tmpDir . '/' . uniqid('regularcontracts_', true) . '.csv'; } elseif ($key === 'tag_qr_flag' && $value !== '') {
$value = ((int) $value) === 1 ? 'QR' : 'タグ';
// ── CSV を作成Excel を考慮し UTF-8 BOM を付与) ─────────────── } elseif ($key === 'pplace_allocation_flag' && $value !== '') {
$fp = fopen($tmpPath, 'w+'); $value = ((int) $value) === 1 ? '割当済' : '未割当';
if ($fp === false) { } elseif ($key === 'contract_flag' && $value !== '') {
abort(500, 'CSV一時ファイルを作成できませんでした。'); $value = ((int) $value) === 1 ? '済' : '未';
} elseif ($key === 'update_flag' && $value !== '') {
$value = ((int) $value) === 1 ? '更新済' : '未更新';
} }
// Excel 対策BOM
fwrite($fp, "\xEF\xBB\xBF");
// ヘッダー行(必要に応じて列を増減) $line[] = mb_convert_encoding((string) $value, 'SJIS-win', 'UTF-8');
fputcsv($fp, ['定期契約ID', '利用者ID', '駐輪場ID', '契約日時']); }
fputcsv($handle, $line);
// ── データ取得(大量件数に備え 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); fclose($handle);
}, $fileName, $headers);
// ── ダウンロードレスポンス(送信後に一時ファイル削除) ────────────
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);
} }
@ -523,6 +703,178 @@ class RegularContractController
->with('success', '定期契約を登録しました。'); ->with('success', '定期契約を登録しました。');
} }
private function buildListQuery(array $params)
{
$contract_qr_id = trim((string)($params['contract_qr_id'] ?? ''));
$user_id = trim((string)($params['user_id'] ?? ''));
$user_tag_serial = trim((string)($params['user_tag_serial'] ?? ''));
$park_id = trim((string)($params['park_id'] ?? ''));
$selectedParkId = trim((string)($params['selected_park_id'] ?? ''));
$user_phonetic = trim((string)($params['user_phonetic'] ?? ''));
$phone = trim((string)($params['phone'] ?? ''));
$email = trim((string)($params['email'] ?? ''));
$user_categoryid = trim((string)($params['user_categoryid'] ?? ''));
$park_name_kw = trim((string)($params['park_name'] ?? ''));
$zone_keyword = trim((string)($params['zone_keyword'] ?? ''));
$zone_name = trim((string)($params['zone_name'] ?? ''));
$merge_phonetic_input = $params['merge_phonetic'] ?? '';
$merge_phonetic = trim((string)$merge_phonetic_input);
$merge_phonetic_normalized = $this->normalizePhoneticKeyword($merge_phonetic);
$workRecordFilter = (string)($params['work_record'] ?? '0');
if (!in_array($workRecordFilter, ['0', '1', '2'], true)) {
$workRecordFilter = '0';
}
$reserve_from = $params['reserve_date_from'] ?? '';
$reserve_to = $params['reserve_date_to'] ?? '';
$created_from = $params['contract_created_from'] ?? '';
$created_to = $params['contract_created_to'] ?? '';
$updated_from = $params['contract_updated_from'] ?? '';
$updated_to = $params['contract_updated_to'] ?? '';
$canceled_from = $params['contract_canceled_from'] ?? '';
$canceled_to = $params['contract_canceled_to'] ?? '';
$receipt_delivery_from = $params['receipt_delivery_from'] ?? '';
$receipt_delivery_to = $params['receipt_delivery_to'] ?? '';
$contract_valid_months = $params['contract_valid_months'] ?? '';
$contract_flag = $params['contract_flag'] ?? '';
$contract_permission = $params['contract_permission'] ?? '';
$tag_qr_flag = $params['tag_qr_flag'] ?? '';
$updateFlagFilter = (string)($params['update_flag'] ?? '0');
if (!in_array($updateFlagFilter, ['0', '1', '2'], true)) {
$updateFlagFilter = '0';
}
$contract_cancel_flag = $params['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')
->leftJoin('zone as z', 'z.zone_id', '=', 'rc.zone_id')
->select([
'rc.*',
'u.user_seq',
'u.user_name',
'u.user_phonetic',
'u.user_mobile',
'u.user_homephone',
'u.user_primemail',
'u.user_regident_zip',
'u.user_tag_serial',
DB::raw('t.print_name as usertype_name'),
't.usertype_subject1',
't.usertype_subject2',
't.usertype_subject3',
DB::raw('p.park_name as park_name'),
DB::raw('z.zone_name as zone_name'),
]);
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 ($user_tag_serial !== '') {
$q->where('u.user_tag_serial', 'like', "%{$user_tag_serial}%");
}
if ($park_id !== '') {
$q->where('rc.park_id', (int)$park_id);
} elseif ($selectedParkId !== '') {
$q->where('rc.park_id', (int)$selectedParkId);
}
if ($user_phonetic !== '') {
$q->where('u.user_phonetic', 'like', "%{$user_phonetic}%");
}
if ($email !== '') {
$q->where('u.user_primemail', 'like', "%{$email}%");
}
if ($user_categoryid !== '') {
$q->where('rc.user_categoryid', (int)$user_categoryid);
}
if ($park_name_kw !== '') {
$q->where('p.park_name', 'like', "%{$park_name_kw}%");
}
if ($zone_name !== '') {
$q->where('z.zone_name', 'like', "%{$zone_name}%");
}
if ($merge_phonetic_normalized !== null) {
$likeKeyword = '%' . $merge_phonetic_normalized . '%';
$q->whereRaw("REPLACE(REPLACE(IFNULL(rc.chk_user_phonetic, ''), ' ', ''), ' ', '') LIKE ?", [$likeKeyword]);
}
if ($phone !== '') {
$q->where(function ($w) use ($phone) {
$w->where('u.user_mobile', 'like', "%{$phone}%")
->orWhere('u.user_homephone', 'like', "%{$phone}%");
});
}
if ($reserve_from !== '' && ($normalized = $this->normalizeDateTimeInput($reserve_from))) {
$q->where('rc.reserve_date', '>=', $normalized);
}
if ($reserve_to !== '' && ($normalized = $this->normalizeDateTimeInput($reserve_to, true))) {
$q->where('rc.reserve_date', '<=', $normalized);
}
if ($receipt_delivery_from !== '' && ($normalized = $this->normalizeDateTimeInput($receipt_delivery_from))) {
$q->where('rc.contract_payment_day', '>=', $normalized);
}
if ($receipt_delivery_to !== '' && ($normalized = $this->normalizeDateTimeInput($receipt_delivery_to, true))) {
$q->where('rc.contract_payment_day', '<=', $normalized);
}
if ($zone_keyword !== '') {
$q->where(function ($w) use ($zone_keyword) {
$w->where('rc.zone_id', 'like', "%{$zone_keyword}%")
->orWhere('rc.pplace_no', 'like', "%{$zone_keyword}%")
->orWhere('rc.old_contract_id', 'like', "%{$zone_keyword}%");
});
}
if ($workRecordFilter === '1') {
$q->where(function ($w) {
$w->whereNull('rc.contract_flag')
->orWhere('rc.contract_flag', '=', 0);
});
} elseif ($workRecordFilter === '2') {
$q->where('rc.contract_flag', '=', 1);
}
if ($created_from !== '' && ($normalized = $this->normalizeDateTimeInput($created_from))) {
$q->where('rc.contract_created_at', '>=', $normalized);
}
if ($created_to !== '' && ($normalized = $this->normalizeDateTimeInput($created_to, true))) {
$q->where('rc.contract_created_at', '<=', $normalized);
}
if ($updated_from !== '' && ($normalized = $this->normalizeDateTimeInput($updated_from))) {
$q->where('rc.contract_updated_at', '>=', $normalized);
}
if ($updated_to !== '' && ($normalized = $this->normalizeDateTimeInput($updated_to, true))) {
$q->where('rc.contract_updated_at', '<=', $normalized);
}
if ($canceled_from !== '' && ($normalized = $this->normalizeDateTimeInput($canceled_from))) {
$q->where('rc.contract_cancelday', '>=', $normalized);
}
if ($canceled_to !== '' && ($normalized = $this->normalizeDateTimeInput($canceled_to, true))) {
$q->where('rc.contract_cancelday', '<=', $normalized);
}
if ($contract_valid_months !== '') {
$q->where('rc.enable_months', (int)$contract_valid_months);
}
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 ($updateFlagFilter === '1') {
$q->where('rc.update_flag', '=', 1);
} elseif ($updateFlagFilter === '2') {
$q->where(function ($w) {
$w->whereNull('rc.update_flag')
->orWhere('rc.update_flag', '!=', 1);
});
}
if ($contract_cancel_flag !== '') {
$q->where('rc.contract_cancel_flag', (int)$contract_cancel_flag);
}
return $q;
}
} }

View File

@ -1,25 +1,16 @@
/**
* First we will load all of this project's JavaScript dependencies which
* includes Vue and other libraries. It is a great starting point when
* building robust, powerful web applications using Vue and Laravel.
*/
// require('./bootstrap'); // require('./bootstrap');
require('jquery-confirm/js/jquery-confirm'); require('jquery-confirm/js/jquery-confirm');
// window.Vue = require('vue'); // window.Vue = require('vue');
/**
* Next, we will create a fresh Vue application instance and attach it to
* the page. Then, you may begin adding components to this application
* or customize the JavaScript scaffolding to fit your unique needs.
*/
//Vue.component('example-component', require('./components/ExampleComponent.vue')); //Vue.component('example-component', require('./components/ExampleComponent.vue'));
// const app = new Vue({ // const app = new Vue({
// el: '#app' // el: '#app'
// }); // });
/**
* チェックボックス全選解除
*/
$('#checkbox_all').on('ifChecked ifUnchecked', function (event) { $('#checkbox_all').on('ifChecked ifUnchecked', function (event) {
if (event.type == 'ifChecked') { if (event.type == 'ifChecked') {
$('input.checkbox:not(:disabled)').iCheck('check'); $('input.checkbox:not(:disabled)').iCheck('check');
@ -28,8 +19,10 @@ $('#checkbox_all').on('ifChecked ifUnchecked', function (event) {
} }
}); });
/**
* 削除ボタン押下時の確認ダイアログ
*/
$('#delete').on('click', function () { $('#delete').on('click', function () {
$.confirm({ $.confirm({
title: '確認ダイアログ。', title: '確認ダイアログ。',
content: '!※※※このレコードは他のテーブルから参照されている可能性があります。削除の際は十分注意してください。\n' + content: '!※※※このレコードは他のテーブルから参照されている可能性があります。削除の際は十分注意してください。\n' +
@ -43,11 +36,14 @@ $('#delete').on('click', function () {
$("#form_delete").submit(); $("#form_delete").submit();
} }
}, },
いいえ: function () { いいえ: function () {}
}
} }
}); });
}); });
/**
* CSVインポート確認ダイアログ
*/
$('#import_csv').on('click', function () { $('#import_csv').on('click', function () {
var action = $(this).attr('action'), var action = $(this).attr('action'),
token = $('meta[name="csrf-token"]').attr('content'); token = $('meta[name="csrf-token"]').attr('content');
@ -69,13 +65,14 @@ $('#import_csv').on('click', function () {
$("#form_import").submit(); $("#form_import").submit();
} }
}, },
いいえ: function () { いいえ: function () {}
//close
},
} }
}); });
}); });
/**
* CSV出力確認ダイアログ
*/
$('#export_csv').on('click', function () { $('#export_csv').on('click', function () {
var action = $(this).attr('action'), var action = $(this).attr('action'),
user_id = $('#user_id').val(), user_id = $('#user_id').val(),
@ -107,33 +104,59 @@ $('#export_csv').on('click', function () {
'&isExport=1'; '&isExport=1';
} }
}, },
いいえ: function () { いいえ: function () {}
}
} }
}); });
}); });
/**
* ソート処理アイコン付き
*/
$(document).on('click', '.rc-table thead th.sorting, .rc-table thead th.sorting_asc, .rc-table thead th.sorting_desc', function (e) {
e.preventDefault();
// for sorting var $header = $(this);
$('.table thead th.sorting').on('click', function (e) { var form = document.getElementById('list-form');
var sort = $(this).attr('sort'); if (!form) {
var sort_type = 'asc'; return;
if ($(this).hasClass('sorting_asc')) {
sort_type = 'desc'
} }
$('input:hidden[name="sort"]').val(sort);
$('input:hidden[name="sort_type"]').val(sort_type); var sortKey = $header.attr('sort');
$('form#list-form').submit(); if (!sortKey) {
return;
}
var wasAsc = $header.hasClass('sorting_asc');
var sortType = wasAsc ? 'desc' : 'asc';
$('.rc-table thead th.sorting, .rc-table thead th.sorting_asc, .rc-table thead th.sorting_desc')
.removeClass('sorting_asc sorting_desc');
$header.addClass(wasAsc ? 'sorting_desc' : 'sorting_asc');
form.querySelector('input[name="sort"]').value = sortKey;
form.querySelector('input[name="sort_type"]').value = sortType;
var params = new URLSearchParams(new FormData(form));
params.delete('page');
var action = form.getAttribute('action') || window.location.pathname;
var base = action.split('?')[0];
var query = params.toString();
window.location.href = query ? base + '?' + query : base;
}); });
/**
* 日付ピッカー初期化
*/
$('.date').datepicker({ $('.date').datepicker({
language: "ja", language: "ja",
format: "yyyy/mm/dd" format: "yyyy/mm/dd"
}); });
/**
* 利用者選択時に電話番号を自動入力
*/
$('#select_user').on('change', function () { $('#select_user').on('change', function () {
var mobile = $('option:selected', this).attr('mobile'), var mobile = $('option:selected', this).attr('mobile'),
homePhone = $('option:selected', this).attr('homePhone'); homePhone = $('option:selected', this).attr('homePhone');
@ -142,6 +165,9 @@ $('#select_user').on('change', function () {
}); });
$('#select_user').trigger('change'); $('#select_user').trigger('change');
/**
* 登録ボタン押下時の確認
*/
$('.register').on('click', function (e) { $('.register').on('click', function (e) {
e.preventDefault(); e.preventDefault();
$.confirm({ $.confirm({
@ -156,13 +182,14 @@ $('.register').on('click', function (e) {
$("form").submit(); $("form").submit();
} }
}, },
いいえ: function () { いいえ: function () {}
}
} }
}); });
}); });
// 編集画面専用 登録ボタン /**
* 編集画面登録処理
*/
$('#register_edit').on('click', function (e) { $('#register_edit').on('click', function (e) {
e.preventDefault(); e.preventDefault();
$.confirm({ $.confirm({
@ -173,7 +200,7 @@ $('#register_edit').on('click', function (e) {
text: "はい", text: "はい",
btnClass: 'btn-primary', btnClass: 'btn-primary',
action: function () { action: function () {
$("#form_edit").submit(); // 更新処理 $("#form_edit").submit();
} }
}, },
いいえ: function () {} いいえ: function () {}
@ -181,7 +208,9 @@ $('#register_edit').on('click', function (e) {
}); });
}); });
// 編集画面専用 削除ボタン /**
* 編集画面削除処理
*/
$('#delete_edit').on('click', function (e) { $('#delete_edit').on('click', function (e) {
e.preventDefault(); e.preventDefault();
$.confirm({ $.confirm({
@ -192,11 +221,10 @@ $('#delete_edit').on('click', function (e) {
text: "はい", text: "はい",
btnClass: 'btn-primary', btnClass: 'btn-primary',
action: function () { action: function () {
$("#form_delete").submit(); // 削除処理 $("#form_delete").submit();
} }
}, },
いいえ: function () {} いいえ: function () {}
} }
}); });
}); });

View File

@ -17,12 +17,6 @@
</div> </div>
@endif @endif
<div class="card-header">
@if($isInfo)
<a href="{{route('regular_contract_add')}}" class="btn btn-lg btn-success">登録</a>
<a href="{{route('regular_contract_edit',['id'=>$contract_id])}}" class="btn btn-lg btn-danger">編集</a>
@endIf
</div>
<div class="card-body"> <div class="card-body">
<div class="row"> <div class="row">
@ -547,9 +541,14 @@
</div> </div>
@if($isInfo) @if($isInfo)
<a href="{{route('regular_contract_add')}}" class="btn btn-lg btn-success">登録</a> <a href="{{route('regular_contract_add')}}" class="btn btn-lg btn-default">登録</a>
<a href="{{route('regular_contract_edit',['id'=>$contract_id])}}" class="btn btn-lg btn-danger">編集</a> <a href="{{route('regular_contract_edit',['id'=>$contract_id])}}" class="btn btn-lg btn-default">編集</a>
@else @else
<button type="submit" class="btn btn-lg btn-danger" @if(!$isEdit || !$isInfo) id="register" @endif>保存</button> <div class="d-flex align-items-center justify-content-between">
<button type="submit" class="btn btn-lg btn-default js-confirm-submit" @if(!$isEdit || !$isInfo) id="register" @endif>登録</button>
@if($isEdit)
<button type="submit" class="btn btn-lg btn-default" name="action" value="delete">削除</button>
@endif
</div>
@endIf @endIf
</div> </div>

View File

@ -34,12 +34,10 @@
<section class="content"> <section class="content">
<div class="container-fluid"> <div class="container-fluid">
{{-- 上部:登録ボタン(駐輪場所・料金マスタのトーンに合わせた薄いボタン) --}}
<div class="rc-toolbar">
<button form="rc-add-form" type="submit" class="btn btn-sm btn-default">登録</button>
</div>
<div class="card rc-form"> <div class="card rc-form">
<div class="card-header d-flex justify-content-start gap-2">
<button form="rc-add-form" type="submit" class="btn btn-sm btn-default register js-confirm-submit">登録</button>
</div>
<div class="card-body"> <div class="card-body">
<form id="rc-add-form" method="post" action="{{ route('regularcontracts_add') }}"> <form id="rc-add-form" method="post" action="{{ route('regularcontracts_add') }}">
@csrf @csrf
@ -334,7 +332,7 @@
{{-- 下部:登録ボタン --}} {{-- 下部:登録ボタン --}}
<div class="mt-3"> <div class="mt-3">
<button type="submit" class="btn btn-sm btn-default">登録</button> <button type="submit" class="btn btn-sm btn-default register js-confirm-submit">登録</button>
<a href="{{ route('regularcontracts') }}" class="btn btn-sm btn-default">戻る</a> <a href="{{ route('regularcontracts') }}" class="btn btn-sm btn-default">戻る</a>
</div> </div>
</form> </form>
@ -344,4 +342,44 @@
</div> </div>
</section> </section>
</div> </div>
@push('scripts')
<script>
(function ($) {
'use strict';
$(function () {
$(document).off('click.rcConfirm', '.js-confirm-submit').on('click.rcConfirm', '.js-confirm-submit', function (e) {
e.preventDefault();
var $button = $(this);
$.confirm({
title: '確認ダイアログ。',
content: '登録してよろしいですか? はい/いいえ',
buttons: {
ok: {
text: 'はい',
btnClass: 'btn-primary',
keys: ['enter'],
action: function () {
var formId = $button.attr('form');
if (formId) {
$('#' + formId).trigger('submit');
return;
}
var $form = $button.closest('form');
if ($form.length) {
$form.trigger('submit');
} else {
$('form').first().trigger('submit');
}
}
},
いいえ: function () {}
}
});
});
});
})(jQuery);
</script>
@endpush
@endsection @endsection

View File

@ -10,12 +10,6 @@
.rc-edit .breadcrumb { .rc-edit .breadcrumb {
font-size: 13px; font-size: 13px;
} }
.rc-toolbar{
display:flex;align-items:center;justify-content:space-between;gap:.75rem;
margin-top:.25rem;margin-bottom:.75rem;
}
.rc-toolbar .left .btn{margin-right:.5rem}
.card .card-header{background:#f5f5f5}
</style> </style>
<div class="rc-edit"> <div class="rc-edit">
@ -27,34 +21,27 @@
<ol class="breadcrumb float-sm-right text-sm"> <ol class="breadcrumb float-sm-right text-sm">
<li class="breadcrumb-item"><a href="{{ route('home') }}">ホーム</a></li> <li class="breadcrumb-item"><a href="{{ route('home') }}">ホーム</a></li>
<li class="breadcrumb-item"><a href="{{ route('regularcontracts') }}">定期契約マスタ</a></li> <li class="breadcrumb-item"><a href="{{ route('regularcontracts') }}">定期契約マスタ</a></li>
<li class="breadcrumb-item active"></li> <li class="breadcrumb-item active">編集</li>
</ol> </ol>
</div> </div>
</div> </div>
{{-- ここが “目標” のボタン帯 --}}
<div class="rc-toolbar">
<div class="left">
{{-- 登録edit-form submit --}}
<button form="edit-form" type="submit" class="btn btn-success btn-sm">登録</button>
{{-- 領収書発行後で好きなルートに差し替えてOK今はダミー --}}
<button type="button" class="btn btn-success btn-sm" id="btnReceipt">領収書発行</button>
</div>
{{-- 右側:削除 --}}
<form id="del-form" method="post" action="{{ route('regularcontracts_delete') }}" class="m-0">
@csrf
<input type="hidden" name="ids[]" value="{{ $row->contract_id }}">
<button type="submit" class="btn btn-danger btn-sm">削除</button>
</form>
</div>
{{-- /ボタン帯 --}}
</div> </div>
</div> </div>
<section class="content"> <section class="content">
<div class="container-fluid"> <div class="container-fluid">
<div class="card"> <div class="card">
<div class="card-header d-flex align-items-center">
<div class="d-flex align-items-center">
<button form="edit-form" type="submit" class="btn btn-default btn-sm mr-2 js-confirm-submit">登録</button>
<button type="button" class="btn btn-default btn-sm" id="btnReceipt">領収書発行</button>
</div>
<form id="del-form" method="post" action="{{ route('regularcontracts_delete') }}" class="m-0 ml-auto">
@csrf
<input type="hidden" name="ids[]" value="{{ $row->contract_id }}">
<button type="submit" class="btn btn-default btn-sm">削除</button>
</form>
</div>
{{-- 本体フォーム(登録はこの form submit --}} {{-- 本体フォーム(登録はこの form submit --}}
<form id="edit-form" method="post" action="{{ route('regularcontracts_edit', ['contract_id'=>$row->contract_id]) }}"> <form id="edit-form" method="post" action="{{ route('regularcontracts_edit', ['contract_id'=>$row->contract_id]) }}">
@csrf @csrf
@ -73,5 +60,32 @@
document.getElementById('btnReceipt')?.addEventListener('click', function(){ document.getElementById('btnReceipt')?.addEventListener('click', function(){
alert('領収書発行の処理をここに実装してください。'); alert('領収書発行の処理をここに実装してください。');
}); });
document.addEventListener('DOMContentLoaded', function () {
$('.js-confirm-submit').on('click', function (e) {
e.preventDefault();
const $button = $(this);
$.confirm({
title: '確認ダイアログ。',
content: '登録してよろしいですか?はい/いいえ',
buttons: {
ok: {
text: "はい",
btnClass: 'btn-primary',
keys: ['enter'],
action: function () {
const formId = $button.attr('form');
if (formId) {
$('#' + formId).trigger('submit');
} else {
$button.closest('form').trigger('submit');
}
}
},
いいえ: function () {}
}
});
});
});
</script> </script>
@endsection @endsection

View File

@ -90,6 +90,30 @@
.rc-page .rc-table tbody td.op-cell { .rc-page .rc-table tbody td.op-cell {
background: #faebd7; background: #faebd7;
} }
.rc-table thead th .sort-arrows {
display: inline-flex;
flex-direction: row;
align-items: center;
gap: 0.1rem;
margin-left: .2rem;
font-size: 12px;
line-height: 1;
font-weight: 700;
text-decoration: none;
letter-spacing: -5px; /* ↑↓を詰める */
}
.rc-table thead th .sort-arrows .up,
.rc-table thead th .sort-arrows .down {
color: #b5b5b5;
text-decoration: none;
}
.rc-table thead th.sorting_asc .sort-arrows .up {
color: #000;
}
.rc-table thead th.sorting_desc .sort-arrows .down {
color: #000;
}
</style> </style>
<div class="rc-page"> <div class="rc-page">
@ -119,7 +143,7 @@
<h3 class="card-title">絞り込みフィルター</h3> <h3 class="card-title">絞り込みフィルター</h3>
</div> </div>
<div class="card-body"> <div class="card-body">
<form action="{{ route('regularcontracts') }}" method="post" id="list-form"> <form action="{{ route('regularcontracts') }}" method="get" id="list-form">
@csrf @csrf
<input type="hidden" name="sort" id="sort" value="{{ $sort }}"> <input type="hidden" name="sort" id="sort" value="{{ $sort }}">
<input type="hidden" name="sort_type" id="sort_type" value="{{ $sort_type }}"> <input type="hidden" name="sort_type" id="sort_type" value="{{ $sort_type }}">
@ -134,8 +158,16 @@
</div> </div>
<div class="field"> <div class="field">
<label class="label">利用者分類</label> <label class="label">利用者分類</label>
<div class="input"><input type="text" class="form-control" name="usertype_name" <div class="input">
value="{{ $usertype_name ?? '' }}" placeholder="キーワード…"></div> <select class="form-control" name="user_categoryid">
<option value="">全て</option>
@foreach(($userTypeOptions ?? []) as $id => $label)
<option value="{{ $id }}" {{ (string)($user_categoryid ?? '') === (string) $id ? 'selected' : '' }}>
{{ $label }}
</option>
@endforeach
</select>
</div>
</div> </div>
<div class="field"> <div class="field">
<label class="label">タグシリアル</label> <label class="label">タグシリアル</label>
@ -154,21 +186,23 @@
</div> </div>
<div class="field"> <div class="field">
<label class="label">メールアドレス</label> <label class="label">メールアドレス</label>
<div class="input"><input type="text" class="form-control" name="email" value="{{ $email ?? '' }}"> <div class="input"><input type="text" class="form-control" name="email" value="{{ $email ?? '' }}" placeholder="キーワード…">
</div> </div>
</div> </div>
<div class="field"> <div class="field">
<label class="label">ワークレコード</label> <label class="label">ワークレコード</label>
<div class="input"> <div class="input">
<select class="form-control" name="work_record"> <select class="form-control" name="work_record">
<option value="">ワークレコード非表示</option> <option value="0" {{ ($work_record ?? '0') === '0' ? 'selected' : '' }}>全レコード表示</option>
<option value="1" {{ ($work_record ?? '0') === '1' ? 'selected' : '' }}>ワークレコード非表示</option>
<option value="2" {{ ($work_record ?? '0') === '2' ? 'selected' : '' }}>ワークレコード表示</option>
</select> </select>
</div> </div>
</div> </div>
<div class="field"> <div class="field">
<label class="label">定期契約ID</label> <label class="label">定期契約ID</label>
<div class="input"><input type="text" class="form-control" name="contract_qr_id" <div class="input"><input type="text" class="form-control" name="contract_qr_id"
value="{{ $contract_qr_id ?? '' }}"></div> value="{{ $contract_qr_id ?? '' }}" placeholder="定期契約ID"></div>
</div> </div>
<div class="field"> <div class="field">
<label class="label">検索</label> <label class="label">検索</label>
@ -189,8 +223,16 @@
<div class="col-lg-6"> <div class="col-lg-6">
<div class="field"> <div class="field">
<label class="label">駐輪場</label> <label class="label">駐輪場</label>
<div class="input"><input type="text" class="form-control" name="park_name" <div class="input">
value="{{ $park_name ?? '' }}" placeholder="キーワード…"></div> <select class="form-control" name="park_id">
<option value="">全て</option>
@foreach(($parkOptions ?? []) as $id => $name)
<option value="{{ $id }}" {{ (string)($park_id ?? '') === (string) $id ? 'selected' : '' }}>
{{ $name }}
</option>
@endforeach
</select>
</div>
</div> </div>
<div class="field"> <div class="field">
<label class="label">予約日時</label> <label class="label">予約日時</label>
@ -234,18 +276,24 @@
</div> </div>
<div class="field"> <div class="field">
<label class="label">定期有効月数</label> <label class="label">定期有効月数</label>
<div class="input"><input type="number" min="0" class="form-control" name="contract_valid_months" <div class="input">
value="{{ $contract_valid_months ?? '' }}"></div> <select class="form-control" name="contract_valid_months">
<option value="">全て</option>
<option value="1" {{ (isset($contract_valid_months) && (string)$contract_valid_months === '1') ? 'selected' : '' }}>直近1ヶ月</option>
<option value="2" {{ (isset($contract_valid_months) && (string)$contract_valid_months === '2') ? 'selected' : '' }}>直近2ヶ月</option>
<option value="3" {{ (isset($contract_valid_months) && (string)$contract_valid_months === '3') ? 'selected' : '' }}>直近3ヶ月</option>
<option value="6" {{ (isset($contract_valid_months) && (string)$contract_valid_months === '6') ? 'selected' : '' }}>直近6ヶ月</option>
<option value="12" {{ (isset($contract_valid_months) && (string)$contract_valid_months === '12') ? 'selected' : '' }}>直近12ヶ月</option>
</select>
</div>
</div> </div>
<div class="field"> <div class="field">
<label class="label">定期契約継続</label> <label class="label">定期契約継続</label>
<div class="input"> <div class="input">
<select class="form-control" name="update_flag"> <select class="form-control" name="update_flag">
<option value="">全て</option> <option value="0" {{ ($update_flag ?? '0') === '0' ? 'selected' : '' }}>全て</option>
<option value="1" {{ (isset($update_flag) && (string) $update_flag === '1') ? 'selected' : '' }}>ON <option value="1" {{ ($update_flag ?? '0') === '1' ? 'selected' : '' }}>継続契約</option>
</option> <option value="2" {{ ($update_flag ?? '0') === '2' ? 'selected' : '' }}>それ以外(新規)</option>
<option value="0" {{ (isset($update_flag) && (string) $update_flag === '0') ? 'selected' : '' }}>未更新
</option>
</select> </select>
</div> </div>
</div> </div>
@ -287,21 +335,19 @@
<div class="rc-toolbar mb-2"> <div class="rc-toolbar mb-2">
<div class="left"> <div class="left">
<a href="{{ route('regularcontracts_add') }}" class="btn btn-sm btn-default">新規</a> <a href="{{ route('regularcontracts_add') }}" class="btn btn-sm btn-default">新規</a>
<button type="button" class="btn btn-sm btn-default" id="btn-delete-selected">削除</button>
<button type="button" class="btn btn-sm btn-default" data-toggle="modal" <button type="button" class="btn btn-sm btn-default" data-toggle="modal"
data-target="#importModal">インポート</button> data-target="#importModal">インポート</button>
{{-- 3種のエクスポートは共通の確認モーダルを使い、data-url に出力先URLを渡す --}} {{-- 3種のエクスポートは共通の確認モーダルを使い、data-url に出力先URLを渡す --}}
<button type="button" class="btn btn-sm btn-default" data-toggle="modal" data-target="#exportModal" <button type="button" class="btn btn-sm btn-default js-export" data-url="{{ route('regularcontracts_export') }}">CSV出力</button>
data-url="{{ route('regularcontracts_export') }}">CSV出力</button>
<button type="button" class="btn btn-sm btn-default" data-toggle="modal" data-target="#exportModal" <button type="button" class="btn btn-sm btn-default js-export" data-url="{{ route('regularcontracts_export', [], false) }}?type=smbc">SMBC出力</button>
data-url="{{ route('regularcontracts_export', [], false) }}?type=smbc">SMBC出力</button>
<button type="button" class="btn btn-sm btn-default" data-toggle="modal" data-target="#exportModal" <button type="button" class="btn btn-sm btn-default js-export" data-url="{{ route('regularcontracts_export', [], false) }}?type=city">役所提出用CSV出力</button>
data-url="{{ route('regularcontracts_export', [], false) }}?type=city">役所提出用CSV出力</button>
</div> </div>
<div class="right"> <div class="right">
{{ $list->appends(['sort' => $sort, 'sort_type' => $sort_type])->links('pagination') }} {{ $list->appends(request()->except('page'))->links('pagination') }}
</div> </div>
</div> </div>
@ -312,58 +358,169 @@
@csrf @csrf
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-bordered table-hover rc-table mb-0 text-nowrap"> <table class="table table-bordered table-hover rc-table mb-0 text-nowrap">
@php
$currentSort = $sort ?? 'contract_id';
$currentDir = strtolower($sort_type ?? 'asc');
if (!in_array($currentDir, ['asc', 'desc'], true)) {
$currentDir = 'asc';
}
$sortClass = function (string $key) use ($currentSort, $currentDir) {
$base = 'sorting';
if ($currentSort === $key) {
return $base . ' ' . ($currentDir === 'asc' ? 'sorting_asc' : 'sorting_desc');
}
return $base;
};
@endphp
@php
$renderSortIcon = static function (string $key) {
return '<span class="sort-arrows"><span class="up">↑</span><span class="down">↓</span></span>';
};
@endphp
<thead> <thead>
<tr> <tr>
@php $next = ($sort === 'contract_id' && $sort_type === 'asc') ? 'desc' : 'asc'; @endphp <th class="text-right {{ $sortClass('contract_id') }}" sort="contract_id" style="white-space:nowrap;">
<th class="text-right" style="white-space:nowrap;"> 契約ID {!! $renderSortIcon('contract_id') !!}
<a title="contract_idで並び替え" </th>
href="{{ route('regularcontracts', array_merge(request()->except('page'), ['sort' => 'contract_id', 'sort_type' => $next])) }}"></a> {{-- 操作列(チェック+編集。背景色を付与) --}}
<th style="width:120px">
<div class="d-flex align-items-center">
<input type="checkbox" id="check-all" class="mr-1">
<span></span>
</div>
</th> </th>
{{-- 操作列(チェック+編集。背景色を付与) --}}
<th style="width:120px"></th>
{{-- 表頭(要件に合わせて網羅) --}} {{-- 表頭(要件に合わせて網羅) --}}
<th>定期契約ID</th> <th class="text-right {{ $sortClass('contract_qr_id') }}" sort="contract_qr_id">
<th>旧定期契約番号</th> 定期契約ID {!! $renderSortIcon('contract_qr_id') !!}
<th>ゾーンID</th> </th>
<th>車室番号</th> <th class="{{ $sortClass('old_contract_id') }}" sort="old_contract_id">
<th>利用者ID</th> 旧定期契約番号 {!! $renderSortIcon('old_contract_id') !!}
<th>利用者分類ID</th> </th>
<th>タグ・QR</th> <th class="{{ $sortClass('pplace_no') }}" sort="pplace_no">
<th>駐輪場ID</th> 車室番号 {!! $renderSortIcon('pplace_no') !!}
<th>有効期間S</th> </th>
<th>有効期間E</th> <th class="{{ $sortClass('user_id') }}" sort="user_id">
<th>駐輪場所ID</th> 利用者ID {!! $renderSortIcon('user_id') !!}
<th>防犯登録番号</th> </th>
<th>契約日時</th> <th class="{{ $sortClass('user_categoryid') }}" sort="user_categoryid">
<th>更新可能日</th> 利用者分類ID {!! $renderSortIcon('user_categoryid') !!}
<th>解約日時</th> </th>
<th>減免措置</th> <th class="{{ $sortClass('tag_qr_flag') }}" sort="tag_qr_flag">
<th>定期有効月数</th> タグ・QR {!! $renderSortIcon('tag_qr_flag') !!}
<th>シール印刷可能日</th> </th>
<th>請求金額</th> <th class="text-right {{ $sortClass('park_id') }}" sort="park_id">
<th>車室割り当てフラグ</th> 駐輪場ID {!! $renderSortIcon('park_id') !!}
<th>授受日時</th> </th>
<th>授受金額</th> <th class="{{ $sortClass('reserve_date') }}" sort="reserve_date">
<th>授受フラグ</th> 予約日時 {!! $renderSortIcon('reserve_date') !!}
<th>決済トランザクションID</th> </th>
<th>シール発行数</th> <th class="{{ $sortClass('contract_periods') }}" sort="contract_periods">
<th>シール発行許可</th> 有効期間S {!! $renderSortIcon('contract_periods') !!}
<th>解約フラグ</th> </th>
<th>収納企業コード</th> <th class="{{ $sortClass('contract_periode') }}" sort="contract_periode">
<th>共有先収納企業コード</th> 有効期間E {!! $renderSortIcon('contract_periode') !!}
<th>受付番号</th> </th>
<th>(更新元)契約更新済フラグ</th> <th class="text-right {{ $sortClass('price_parkplaceid') }}" sort="price_parkplaceid">
<th>車種区分ID</th> 駐輪場所ID {!! $renderSortIcon('price_parkplaceid') !!}
<th>チェック用_フリガナ</th> </th>
<th>チェック用_居住所郵便番号</th> <th class="{{ $sortClass('user_securitynum') }}" sort="user_securitynum">
<th>チェック用_携帯電話番号</th> 防犯登録番号 {!! $renderSortIcon('user_securitynum') !!}
<th>チェック用_自宅電話番号</th> </th>
<th>チェック用_旧会員番号</th> <th class="{{ $sortClass('contract_created_at') }}" sort="contract_created_at">
契約日時 {!! $renderSortIcon('contract_created_at') !!}
</th>
<th class="{{ $sortClass('contract_updated_at') }}" sort="contract_updated_at">
更新可能日 {!! $renderSortIcon('contract_updated_at') !!}
</th>
<th class="{{ $sortClass('contract_cancelday') }}" sort="contract_cancelday">
解約日時 {!! $renderSortIcon('contract_cancelday') !!}
</th>
<th class="{{ $sortClass('contract_reduction') }}" sort="contract_reduction">
減免措置 {!! $renderSortIcon('contract_reduction') !!}
</th>
<th class="text-right {{ $sortClass('enable_months') }}" sort="enable_months">
定期有効月数 {!! $renderSortIcon('enable_months') !!}
</th>
<th class="{{ $sortClass('printable_date') }}" sort="printable_date">
シール印刷可能日 {!! $renderSortIcon('printable_date') !!}
</th>
<th class="text-right {{ $sortClass('billing_amount') }}" sort="billing_amount">
請求金額 {!! $renderSortIcon('billing_amount') !!}
</th>
<th class="{{ $sortClass('pplace_allocation_flag') }}" sort="pplace_allocation_flag">
車室割り当てフラグ {!! $renderSortIcon('pplace_allocation_flag') !!}
</th>
<th class="{{ $sortClass('contract_payment_day') }}" sort="contract_payment_day">
授受日時 {!! $renderSortIcon('contract_payment_day') !!}
</th>
<th class="text-right {{ $sortClass('contract_money') }}" sort="contract_money">
授受金額 {!! $renderSortIcon('contract_money') !!}
</th>
<th class="{{ $sortClass('contract_flag') }}" sort="contract_flag">
授受フラグ {!! $renderSortIcon('contract_flag') !!}
</th>
<th class="{{ $sortClass('settlement_transaction_id') }}" sort="settlement_transaction_id">
決済トランザクションID {!! $renderSortIcon('settlement_transaction_id') !!}
</th>
<th class="text-right {{ $sortClass('contract_seal_issue') }}" sort="contract_seal_issue">
シール発行数 {!! $renderSortIcon('contract_seal_issue') !!}
</th>
<th class="{{ $sortClass('storage_company_code') }}" sort="storage_company_code">
収納企業コード {!! $renderSortIcon('storage_company_code') !!}
</th>
<th class="{{ $sortClass('share_storage_company_code') }}" sort="share_storage_company_code">
共有先収納企業コード {!! $renderSortIcon('share_storage_company_code') !!}
</th>
<th class="{{ $sortClass('accept_number') }}" sort="accept_number">
受付番号 {!! $renderSortIcon('accept_number') !!}
</th>
<th class="{{ $sortClass('update_flag') }}" sort="update_flag">
(更新元)契約更新済フラグ {!! $renderSortIcon('update_flag') !!}
</th>
<th class="{{ $sortClass('vehicle_type_id') }}" sort="vehicle_type_id">
車種区分ID {!! $renderSortIcon('vehicle_type_id') !!}
</th>
<th class="{{ $sortClass('user_phonetic') }}" sort="user_phonetic">
チェック用_フリガナ {!! $renderSortIcon('user_phonetic') !!}
</th>
<th class="{{ $sortClass('user_regident_zip') }}" sort="user_regident_zip">
チェック用_居住所郵便番号 {!! $renderSortIcon('user_regident_zip') !!}
</th>
<th class="{{ $sortClass('user_mobile') }}" sort="user_mobile">
チェック用_携帯電話番号 {!! $renderSortIcon('user_mobile') !!}
</th>
<th class="{{ $sortClass('user_homephone') }}" sort="user_homephone">
チェック用_自宅電話番号 {!! $renderSortIcon('user_homephone') !!}
</th>
<th class="{{ $sortClass('old_member_number') }}" sort="old_member_number">
チェック用_旧会員番号 {!! $renderSortIcon('old_member_number') !!}
</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@php
$formatYmd = static function ($value) {
if (empty($value)) {
return '';
}
try {
return \Illuminate\Support\Carbon::parse($value)->format('Y-m-d');
} catch (\Throwable $e) {
return mb_substr((string) $value, 0, 10);
}
};
$formatYmdHi = static function ($value) {
if (empty($value)) {
return '';
}
try {
return \Illuminate\Support\Carbon::parse($value)->format('Y-m-d H:i');
} catch (\Throwable $e) {
return mb_substr((string) $value, 0, 16);
}
};
@endphp
@foreach($list as $item) @foreach($list as $item)
<tr> <tr>
{{-- 先頭の並び替え列(行の contract_id を表示) --}} {{-- 先頭の並び替え列(行の contract_id を表示) --}}
@ -372,7 +529,7 @@
{{-- 操作列:チェック + 編集(背景色つき) --}} {{-- 操作列:チェック + 編集(背景色つき) --}}
<td class="op-cell"> <td class="op-cell">
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">
<input type="checkbox" class="mr-2" name="ids[]" value="{{ $item->contract_id }}"> <input type="checkbox" class="mr-2 row-check" name="ids[]" value="{{ $item->contract_id }}">
<a href="{{ route('regularcontracts_edit', ['contract_id' => $item->contract_id]) }}" <a href="{{ route('regularcontracts_edit', ['contract_id' => $item->contract_id]) }}"
class="btn btn-sm btn-default">編集</a> class="btn btn-sm btn-default">編集</a>
</div> </div>
@ -380,40 +537,54 @@
{{-- データ列 --}} {{-- データ列 --}}
<td class="text-right">{{ $item->contract_qr_id }}</td> <td class="text-right">{{ $item->contract_qr_id }}</td>
<td>{{ $item->old_contract_number ?? '' }}</td> <td>{{ $item->old_contract_id ?? '' }}</td>
<td>{{ $item->zone_id ?? '' }}</td> <td>{{ $item->pplace_no ?? '' }}</td>
<td>{{ $item->room_number ?? '' }}</td> <td>
<td class="text-right">{{ $item->user_id }}</td> @if(!empty($item->user_seq))
<td class="text-right">{{ $item->user_categoryid }}</td> <a href="{{ route('users_edit', ['seq' => $item->user_seq]) }}" class="text-primary">
{{ trim(($item->user_id ?? '') . ' ' . ($item->user_name ?? '')) }}
</a>
@else
{{ $item->user_id ?? '' }}
@if(!empty($item->user_name))
<div class="text-muted small">{{ $item->user_name }}</div>
@endif
@endif
</td>
@php
$userCategoryLabel = collect([
$item->usertype_subject1 ?? '',
$item->usertype_subject2 ?? '',
$item->usertype_subject3 ?? '',
])->filter(fn ($v) => $v !== '')->implode('/');
@endphp
<td>{{ $userCategoryLabel ?: $item->user_categoryid }}</td>
<td>{{ $item->tag_qr_flag ? 'QR' : 'タグ' }}</td> <td>{{ $item->tag_qr_flag ? 'QR' : 'タグ' }}</td>
<td class="text-right">{{ $item->park_id }}</td> <td class="text-right">{{ $item->park_id }}</td>
<td>{{ $item->valid_from ?? '' }}</td> <td>{{ $formatYmdHi($item->reserve_date ?? '') }}</td>
<td>{{ $item->valid_to ?? '' }}</td> <td>{{ $formatYmd($item->contract_periods ?? '') }}</td>
<td class="text-right">{{ $item->price_arkplaceid ?? $item->price_parkplaceid ?? '' }}</td> <td>{{ $formatYmd($item->contract_periode ?? '') }}</td>
<td>{{ $item->crime_prevention_registration_number ?? '' }}</td> <td class="text-right">{{ $item->price_parkplaceid ?? '' }}</td>
<td>{{ $item->contract_created_at ?? '' }}</td> <td>{{ $item->user_securitynum ?? '' }}</td>
<td>{{ $item->renewable_date ?? '' }}</td> <td>{{ $formatYmd($item->contract_created_at ?? '') }}</td>
<td>{{ $item->contract_cancelday ?? '' }}</td> <td>{{ $formatYmd($item->contract_updated_at ?? '') }}</td>
<td>{{ $item->reduction ?? '' }}</td> <td>{{ $formatYmd($item->contract_cancelday ?? '') }}</td>
<td class="text-right">{{ $item->contract_valid_months ?? '' }}</td> <td>{{ $item->contract_reduction ?? '' }}</td>
<td>{{ $item->label_printable_date ?? '' }}</td> <td class="text-right">{{ $item->enable_months ?? '' }}</td>
<td>{{ $formatYmd($item->printable_date ?? '') }}</td>
<td class="text-right">{{ $item->billing_amount ?? '' }}</td> <td class="text-right">{{ $item->billing_amount ?? '' }}</td>
<td>{{ ($item->assign_flag ?? null) === null ? '' : (($item->assign_flag) ? '割当済' : '未割当') }}</td> <td>{{ ($item->pplace_allocation_flag ?? null) === null ? '' : (($item->pplace_allocation_flag) ? '割当済' : '未割当') }}</td>
<td>{{ $item->receipt_delivery_date ?? '' }}</td> <td>{{ $formatYmd($item->contract_payment_day ?? '') }}</td>
<td class="text-right">{{ $item->receipt_delivery_amount ?? '' }}</td> <td class="text-right">{{ $item->contract_money ?? '' }}</td>
<td> <td>{{ ($item->contract_flag ?? null) === null ? '' : (($item->contract_flag) ? '済' : '未') }}</td>
{{ ($item->receipt_delivery_flag ?? null) === null ? '' : (($item->receipt_delivery_flag) ? '済' : '未') }} <td>{{ $item->settlement_transaction_id ?? '' }}</td>
</td> <td class="text-right">{{ $item->contract_seal_issue ?? '' }}</td>
<td>{{ $item->payment_transaction_id ?? '' }}</td> <td>{{ $item->storage_company_code ?? '' }}</td>
<td class="text-right">{{ $item->label_issue_count ?? '' }}</td> <td>{{ $item->share_storage_company_code ?? '' }}</td>
<td>{{ ($item->contract_permission ?? 0) ? '許可' : '未許可' }}</td>
<td>{{ ($item->contract_cancel_flag ?? 0) ? 'キャンセル' : '' }}</td>
<td>{{ $item->company_code ?? '' }}</td>
<td>{{ $item->shared_company_code ?? '' }}</td>
<td>{{ $item->accept_number ?? '' }}</td> <td>{{ $item->accept_number ?? '' }}</td>
<td>{{ ($item->update_flag ?? 0) ? '更新済' : '未更新' }}</td> <td>{{ ($item->update_flag ?? 0) ? '更新済' : '未更新' }}</td>
<td>{{ $item->vehicle_type_id ?? '' }}</td> <td>{{ $item->vehicle_type_id ?? '' }}</td>
<td>{{ $item->user_phonetic ?? '' }}</td> <td>{{ $item->chk_user_phonetic ?? $item->user_phonetic ?? '' }}</td>
<td>{{ $item->user_regident_zip ?? '' }}</td> <td>{{ $item->user_regident_zip ?? '' }}</td>
<td>{{ $item->user_mobile ?? '' }}</td> <td>{{ $item->user_mobile ?? '' }}</td>
<td>{{ $item->user_homephone ?? '' }}</td> <td>{{ $item->user_homephone ?? '' }}</td>
@ -424,11 +595,6 @@
</table> </table>
</div> </div>
</form> </form>
{{-- 下側のページャ(右寄せ) --}}
<div class="d-flex justify-content-end mt-2">
{{ $list->appends(['sort' => $sort, 'sort_type' => $sort_type])->links('pagination') }}
</div>
</div> </div>
</section> </section>
</div> </div>
@ -465,25 +631,6 @@
</div> </div>
</div> </div>
{{-- エクスポート用モーダル(確認のみ。リンク先でダウンロード) --}}
<div class="modal fade" id="exportModal" tabindex="-1" role="dialog" aria-labelledby="exportModalLabel"
aria-hidden="true">
<div class="modal-dialog modal-sm" role="document">
<div class="modal-content">
<div class="modal-header py-2">
<h5 class="modal-title" id="exportModalLabel">確認</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
aria-hidden="true">&times;</span></button>
</div>
<div class="modal-body text-sm">CSVファイルを出力します。よろしいですか</div>
<div class="modal-footer py-2">
<a id="exportGo" href="#" class="btn btn-primary btn-sm">はい</a>
<button type="button" class="btn btn-default btn-sm" data-dismiss="modal">いいえ</button>
</div>
</div>
</div>
</div>
{{-- 画面内スクリプトインポートのファイル名表示、エクスポートのURL差し替え --}} {{-- 画面内スクリプトインポートのファイル名表示、エクスポートのURL差し替え --}}
<script> <script>
// インポート:選択したファイル名をラベルに反映 // インポート:選択したファイル名をラベルに反映
@ -496,36 +643,103 @@
// エクスポート:モーダル表示時に押下ボタンの data-url を「はい」へセット // エクスポート:モーダル表示時に押下ボタンの data-url を「はい」へセット
(function () { (function () {
var $ = window.jQuery; var exportModal = document.getElementById('exportModal');
var $exportGo = $('#exportGo'); var deleteForm = document.getElementById('form_delete');
var $exportModal = $('#exportModal'); var deleteBtn = document.getElementById('btn-delete-selected');
var masterCheckbox = document.getElementById('check-all');
var rowSelector = '.row-check';
if ($ && $exportModal.length) { var exportButtons = document.querySelectorAll('.js-export');
// モーダルが開く時に URL を保持href と data-href の両方に入れておく) if (exportButtons.length) {
$exportModal.on('show.bs.modal', function (event) { exportButtons.forEach(function (btn) {
var button = $(event.relatedTarget); btn.addEventListener('click', function () {
var url = button && button.data('url'); var url = btn.getAttribute('data-url');
$exportGo.attr('href', url || '#'); if (!url) {
$exportGo.attr('data-href', url || '#'); return;
}); }
var doExport = function () {
// 「はい」→ 既定遷移を止め、隠し iframe でダウンロード開始 → モーダル閉じる
$exportGo.on('click', function (e) {
e.preventDefault();
var url = $(this).attr('data-href') || $(this).attr('href') || '#';
if (url && url !== '#') {
var iframe = document.getElementById('dlFrame'); var iframe = document.getElementById('dlFrame');
if (iframe) { if (iframe) {
iframe.src = url; // ここでダウンロード開始(ページ遷移なし) iframe.src = url;
} else { } else {
// 念のためのフォールバック
window.location.href = url; window.location.href = url;
} }
};
if (window.jQuery && typeof window.jQuery.confirm === 'function') {
window.jQuery.confirm({
title: '確認ダイアログ。',
content: 'CSVファイルを出力します。よろしいですか はい/いいえ',
buttons: {
ok: {
text: 'はい',
btnClass: 'btn-primary',
keys: ['enter'],
action: doExport
},
いいえ: function () {}
} }
$exportModal.modal('hide');
}); });
} else if (confirm('CSVファイルを出力します。よろしいですか はい/いいえ')) {
doExport();
}
});
});
}
if (deleteBtn && deleteForm) {
deleteBtn.addEventListener('click', function () {
var checked = document.querySelectorAll(rowSelector + ':checked');
if (!checked.length) {
alert('削除する定期契約を選択してください。');
return;
}
var submitDelete = function () {
deleteForm.submit();
};
if (window.jQuery && typeof window.jQuery.confirm === 'function') {
window.jQuery.confirm({
title: '確認ダイアログ。',
content: '削除してよろしいですか? はい/いいえ',
buttons: {
ok: {
text: 'はい',
btnClass: 'btn-primary',
keys: ['enter'],
action: submitDelete
},
いいえ: function () {}
}
});
} else if (confirm('削除してよろしいですか? はい/いいえ')) {
submitDelete();
}
});
}
if (masterCheckbox) {
var updateMasterState = function () {
var allRows = document.querySelectorAll(rowSelector);
var checkedRows = document.querySelectorAll(rowSelector + ':checked');
masterCheckbox.checked = allRows.length > 0 && checkedRows.length === allRows.length;
};
masterCheckbox.addEventListener('change', function () {
var allRows = document.querySelectorAll(rowSelector);
allRows.forEach(function (checkbox) {
checkbox.checked = masterCheckbox.checked;
});
});
document.addEventListener('change', function (e) {
if (e.target && e.target.matches(rowSelector)) {
updateMasterState();
}
});
updateMasterState();
} }
})(); })();
</script> </script>
@endsection @endsection