diff --git a/app/Http/Controllers/ReceiptController.php b/app/Http/Controllers/ReceiptController.php
new file mode 100644
index 0000000..dfe5fa4
--- /dev/null
+++ b/app/Http/Controllers/ReceiptController.php
@@ -0,0 +1,117 @@
+where('user_id', $user_id)->first();
+
+ return view('receipt.input', [
+ 'user_name' => $user ? $user->user_name : '', // ユーザー名(ヘッダー用)
+ 'contract_id' => $contract_id
+ ]);
+ }
+
+ // 領収書発行(入力内容の保存)
+ public function issue(Request $request, $contract_id)
+ {
+ $user_id = session('user_id');
+ if (!$user_id) {
+ return redirect('/login');
+ }
+
+ $receipt_name = $request->input('receipt_name');
+ $keisho = $request->input('keisho');
+
+ // 既存レコードチェック
+ $exists = DB::table('inv_publish')->where('contract_id', $contract_id)->exists();
+ if ($exists) {
+ // エラー時はinput画面に戻し、メッセージ表示
+ return redirect()->back()->withInput()->withErrors(['contract_id' => 'この契約の領収書は既に発行されています。契約履歴から再発行を行ってください。']);
+ }
+
+ // inv_publishテーブルに新規登録(insert)
+ $inv_name = $receipt_name . $keisho;
+ $now = date('Y-m-d H:i:s');
+ $seq = DB::table('inv_publish')->max('seq') ?? 0;
+ $seq = $seq + 1;
+ DB::table('inv_publish')->insert([
+ 'seq' => $seq,
+ 'user_id' => $user_id,
+ 'contract_id' => $contract_id,
+ 'inv_name' => $inv_name,
+ 'published_at' => date('Y-m-d'),
+ 'type' => 0,
+ 'count' => 1,
+ 'created_at' => $now,
+ 'updated_at' => null,
+ ]);
+
+ // 完了後はdownloadメソッドを直接呼び出し(再発行フラグfalseで渡す)
+ $is_reissue = false;
+ return $this->download($contract_id, $is_reissue);
+ }
+
+
+
+ public function download($contract_id, $is_reissue = true)
+ {
+ // 必要なデータを取得
+ $contract = DB::table('regular_contract')->where('contract_id', $contract_id)->first();
+ $inv = DB::table('inv_publish')->where('contract_id', $contract_id)->first();
+ $t_number = DB::table('inv_setting')->value('t_number');
+
+ // park_name取得(regular_contract.park_id=park.park_id)
+ $park_name = '';
+ if ($contract && $contract->park_id) {
+ $park = DB::table('park')->where('park_id', $contract->park_id)->first();
+ if ($park && $park->park_name) {
+ $park_name = $park->park_name;
+ }
+ }
+
+ // BladeテンプレートをHTMLにレンダリング
+ $html = view('receipt.pdf', [
+ 'contract' => $contract,
+ 'inv' => $inv,
+ 't_number' => $t_number,
+ 'park_name' => $park_name,
+ 'is_reissue' => $is_reissue,
+ ])->render();
+
+ // mPDF最新版(autoload対応)
+ $mpdf = new \Mpdf\Mpdf([
+ 'mode' => 'ja',
+ 'format' => 'A4',
+ 'custom_font_dir' => resource_path('fonts'),
+ 'custom_font_data' => [
+ 'noto_sans_jp' => [
+ 'R' => 'NotoSansJP-Regular.ttf', // 通常フォント
+ 'B' => 'NotoSansJP-Bold.ttf', // 太字フォント
+ ]
+ ],
+ 'default_font' => 'noto_sans_jp',
+ ]);
+
+
+ $mpdf->WriteHTML($html);
+
+ // PDFダウンロード
+ return response($mpdf->Output('receipt_' . $contract_id . '.pdf', 'S'))
+ ->header('Content-Type', 'application/pdf')
+ ->header('Content-Disposition', 'attachment; filename="receipt_' . $contract_id . '.pdf"');
+ }
+}
diff --git a/app/Http/Controllers/RegularContractController.php b/app/Http/Controllers/RegularContractController.php
new file mode 100644
index 0000000..50fa7c1
--- /dev/null
+++ b/app/Http/Controllers/RegularContractController.php
@@ -0,0 +1,209 @@
+where('user_id', $user_id)->value('user_name');
+
+ $today = date('Y-m-d');
+ // 定期契約情報を取得(park/usertype/psection/ptypeテーブルもJOIN)
+ $contracts = DB::table('regular_contract')
+ ->join('park', 'regular_contract.park_id', '=', 'park.park_id')
+ ->join('usertype', 'regular_contract.user_categoryid', '=', 'usertype.user_categoryid')
+ ->leftJoin('city', 'park.city_id', '=', 'city.city_id')
+ ->leftJoin('psection', 'regular_contract.psection_id', '=', 'psection.psection_id')
+ ->leftJoin('ptype', 'regular_contract.ptype_id', '=', 'ptype.ptype_id')
+ ->where('regular_contract.user_id', $user_id)
+ ->where('regular_contract.contract_flag', 1)
+ ->where('regular_contract.contract_cancel_flag', 0)
+ ->where(function ($query) use ($today) {
+ $query->where('regular_contract.contract_periode', '>', $today)
+ ->orWhere(function ($q) use ($today) {
+ $q->where('regular_contract.contract_periode', '<=', $today)
+ ->whereRaw('DATEDIFF(?, regular_contract.contract_periode) <= 5', [$today]);
+ });
+ })
+ ->select(
+ 'regular_contract.contract_id',
+ 'park.park_name',
+ 'usertype.usertype_subject1',
+ 'regular_contract.contract_periods',
+ 'regular_contract.contract_periode',
+ 'regular_contract.enable_months',
+ 'regular_contract.contract_renewal',
+ 'regular_contract.park_id',
+ 'city.update_grace_period_start_date',
+ 'city.update_grace_period_start_time',
+ 'city.update_grace_period_end_date',
+ 'city.update_grace_period_end_time',
+ 'psection.psection_subject',
+ 'ptype.ptype_subject',
+ 'regular_contract.pplace_no'
+ )
+ ->get();
+
+ return view('regular_contract.info', [
+ 'active_menu' => 'SWC-3-1', // マイページメニューの選択状態用
+ 'user_name' => $user_name, // ユーザー名(ヘッダー用)
+ 'contracts' => $contracts,
+ ]);
+ }
+
+ public function update($contract_id)
+ {
+ $user_id = session('user_id');
+ if (!$user_id) {
+ return redirect('/login');
+ }
+
+ // 元契約データ取得
+ $old_contract = DB::table('regular_contract')->where('contract_id', $contract_id)->first();
+
+ // 新規レコード作成(必要な項目を元契約データから引き継ぐ)
+ $new_contract_id = DB::table('regular_contract')->insertGetId([
+ 'old_contract_id' => $old_contract->contract_id,
+ 'user_id' => $old_contract->user_id,
+ 'user_categoryid' => $old_contract->user_categoryid,
+ 'created_at' => now(),
+ 'updated_at' => now(),
+ 'park_id' => $old_contract->park_id,
+ 'price_parkplaceid' => $old_contract->price_parkplaceid,
+ 'user_securitynum' => $old_contract->user_securitynum,
+ 'contract_created_at' => now(),
+ 'contract_reduction' => $old_contract->contract_reduction,
+ 'update_flag' => 1,
+ 'contract_cancel_flag' => 0,
+ '800m_flag' => 0,
+ 'psection_id' => $old_contract->psection_id,
+ 'zone_id' => $old_contract->zone_id,
+ 'pplace_no' => $old_contract->pplace_no
+ ]);
+
+ // contract_qr_id生成(AES-256-CBC暗号化)
+ $key = "LJLASR4FAS34SAADFA72ASDFALLSDRGT";
+ $iv = substr(hash('sha256', $key), 0, 16); // IVは16バイト
+ $encrypted = openssl_encrypt($new_contract_id, 'AES-256-CBC', $key, 0, $iv);
+
+ // contract_qr_idを更新
+ DB::table('regular_contract')->where('contract_id', $new_contract_id)->update([
+ 'contract_qr_id' => $encrypted
+ ]);
+
+ // 完了後の遷移
+ return redirect()->route('regular_contract.confirm_category', ['contract_id' => $new_contract_id]);
+ }
+
+ public function confirmCategory($contract_id)
+ {
+ $user_id = session('user_id');
+ if (!$user_id) {
+ return redirect('/login');
+ }
+ $user = DB::table('user')->where('user_id', $user_id)->first();
+
+ // regular_contractとparkをJOINしてsecurityreg_display_flagを取得
+ $contract = DB::table('regular_contract')
+ ->join('park', 'regular_contract.park_id', '=', 'park.park_id')
+ ->where('regular_contract.contract_id', $contract_id)
+ ->select('regular_contract.*', 'park.securityreg_display_flag')
+ ->first();
+
+ return view('regular_contract.confirm_category', [
+ 'active_menu' => 'SWC-4-3', // マイページメニューの選択状態用
+ 'user_name' => $user ? $user->user_name : '', // ユーザー名(ヘッダー用)
+ 'user' => $user,
+ 'contract' => $contract
+ ]);
+ }
+
+
+ /**
+ * 契約区分確認画面の「確認して進む」ボタン押下時の分岐処理
+ * 本人確認書類アップロード画面 or 利用期間選択画面へ遷移
+ *
+ * 分岐条件は下記の通り:
+ * 1. 利用者分類(一般/学生):user.user_categoryid=usertype.user_categoryidのusertype_subject1で判別
+ * 2. 利用者分類(減免/減免でない):regular_contract.contract_reduction=1なら減免
+ * 3. 駐輪場マスタの設定(減免確認種別):reduction_confirm.reduction_confirm_type(0=確認しない,1=年1回,2=毎更新時)
+ * 4. 駐輪場マスタの設定(年度跨ぎ):park.overyear_flag(0/NULL=年跨ぎなし,1=年跨ぎあり)
+ * 5. 駐輪場マスタの設定(学生証確認種別):park.student_id_confirm_type(0=確認しない,1=年1回,2=毎更新時)
+ * 6. 契約期間に4/1が含まれる場合は年度跨ぎ判定に利用
+ * 7. ユーザー区分が「学生」の場合のみ、契約終了日が卒業年月日を超える場合も本人確認書類アップロード画面へ
+ */
+ public function confirmCategoryNext($contract_id)
+ {
+ $user_id = session('user_id');
+ if (!$user_id) {
+ return redirect('/login');
+ }
+ // 契約情報取得
+ $contract = DB::table('regular_contract')->where('contract_id', $contract_id)->first();
+ if (!$contract) {
+ return redirect()->back()->with('error', '契約情報が見つかりません');
+ }
+ // ユーザー情報取得
+ $user = DB::table('user')->where('user_id', $user_id)->first();
+ // ユーザー区分取得
+ $usertype = DB::table('usertype')->where('user_categoryid', $user->user_categoryid)->first();
+ // 駐輪場情報取得
+ $park = DB::table('park')->where('park_id', $contract->park_id)->first();
+ // 減免確認種別取得
+ $reduction_confirm = DB::table('reduction_confirm')
+ ->where('park_id', $contract->park_id)
+ ->where('user_categoryid', $contract->user_categoryid)
+ ->first();
+
+ // 分岐条件
+ $reduction_confirm_type = isset($reduction_confirm) ? $reduction_confirm->reduction_confirm_type : 0;
+ $student_id_confirm_type = isset($park) ? $park->student_id_confirm_type : 0;
+ $overyear_flag = isset($park) ? $park->overyear_flag : 0;
+
+ // 契約期間に4/1が含まれるか判定
+ $contract_start = new \DateTime($contract->contract_periods);
+ $contract_end = new \DateTime($contract->contract_periode);
+ // 4/1の年は契約開始年
+ $april_first = new \DateTime($contract_start->format('Y') . '-04-01');
+ // 契約終了日が4/1より前なら翌年の4/1も考慮
+ if ($contract_end < $april_first) {
+ $april_first->modify('+1 year');
+ }
+ $includes_april_first = ($contract_start <= $april_first && $contract_end >= $april_first);
+
+ // ユーザー区分が「学生」の場合のみ卒業年月日判定
+ $exceeds_graduation = false;
+ if (isset($usertype) && $usertype->usertype_subject1 === '学生') {
+ $graduation_date = isset($user) ? $user->user_graduate : null;
+ if ($graduation_date) {
+ $graduation_dt = new \DateTime($graduation_date);
+ $exceeds_graduation = ($contract_end > $graduation_dt);
+ }
+ }
+
+ // 本人確認書類アップロード画面へ遷移する条件
+ // 1. reduction_confirm_typeが1(年1回)または2(毎更新時)
+ // 2. student_id_confirm_typeが1(年1回)または2(毎更新時)
+ // 3. 年度跨ぎ(overyear_flag=1以外)かつ契約期間に4/1が含まれる場合
+ if (
+ in_array($reduction_confirm_type, [1, 2]) ||
+ in_array($student_id_confirm_type, [1, 2]) ||
+ ($overyear_flag != 1 && $includes_april_first) ||
+ $exceeds_graduation
+ ) {
+ // 本人確認書類アップロード画面へ
+ return redirect()->route('regular_contract.upload_identity', ['contract_id' => $contract_id]);
+ } else {
+ // 利用期間選択画面へ
+ return redirect()->route('regular_contract.select_period', ['contract_id' => $contract_id]);
+ }
+ }
+}
diff --git a/public/images/hanko.png b/public/images/hanko.png
new file mode 100644
index 0000000..980f2ab
Binary files /dev/null and b/public/images/hanko.png differ
diff --git a/public/images/reissue.png b/public/images/reissue.png
new file mode 100644
index 0000000..b855b1c
Binary files /dev/null and b/public/images/reissue.png differ
diff --git a/resources/fonts/NotoSansJP-Bold.ttf b/resources/fonts/NotoSansJP-Bold.ttf
new file mode 100644
index 0000000..f1596e8
Binary files /dev/null and b/resources/fonts/NotoSansJP-Bold.ttf differ
diff --git a/resources/fonts/NotoSansJP-Regular.ttf b/resources/fonts/NotoSansJP-Regular.ttf
new file mode 100644
index 0000000..7c15c59
Binary files /dev/null and b/resources/fonts/NotoSansJP-Regular.ttf differ
diff --git a/resources/views/receipt/input.blade.php b/resources/views/receipt/input.blade.php
new file mode 100644
index 0000000..26e3334
--- /dev/null
+++ b/resources/views/receipt/input.blade.php
@@ -0,0 +1,35 @@
+@extends('layouts.app')
+@section('content')
+
+
+ @if($errors->has('contract_id'))
+
+ {{ $errors->first('contract_id') }}
+
+ @endif
+
+
+@endsection
\ No newline at end of file
diff --git a/resources/views/receipt/pdf.blade.php b/resources/views/receipt/pdf.blade.php
new file mode 100644
index 0000000..6421e93
--- /dev/null
+++ b/resources/views/receipt/pdf.blade.php
@@ -0,0 +1,132 @@
+
+
+
+
+
+
+
+
+
+
+ @if($is_reissue)
+
) }})
+ @endif
+
+ No. {{ $inv->seq ?? '' }}
+ 発行日: {{ !empty($inv->published_at) ? \Carbon\Carbon::parse($inv->published_at)->format('Y年m月d日') : '' }}
+
+
領収証
+
ID: {{ $contract->contract_id ?? '' }}
+
{{ $inv->inv_name ?? '' }}
+
+
+ | 契約駐輪場名 |
+ {{ $park_name ?? '' }} |
+
+
+ | 小計(10%対象) |
+
+ {{ isset($contract->contract_money) ? number_format(floor($contract->contract_money / 1.1)) : '' }} 円
+ |
+
+
+ | 消費税額(10%) |
+
+ {{ isset($contract->contract_money) ? number_format($contract->contract_money - floor($contract->contract_money / 1.1)) : '' }} 円
+ |
+
+
+ | 合計 |
+ {{ number_format($contract->contract_money ?? 0) }} 円 |
+
+
+
+ 但し、駐輪場利用料(
+ {{ !empty($contract->contract_periods) ? \Carbon\Carbon::parse($contract->contract_periods)->format('Y年m月d日') : '' }}
+ ~
+ {{ !empty($contract->contract_periode) ? \Carbon\Carbon::parse($contract->contract_periode)->format('Y年m月d日') : '' }}
+ )として
+ 上記金額を正に領収いたしました。
+
+
+
+ |
+
+
+ 株式会社ソーリン
+ 〒121-0073
+ 東京都足立区六町四丁目12-25
+ 適格事業者番号:{{ $t_number }}
+ TEL:03-5856-4647
+ FAX:03-5856-4648
+
+ |
+ ) }}) |
+
+
+
+
+
+
\ No newline at end of file
diff --git a/resources/views/regular_contract/info.blade.php b/resources/views/regular_contract/info.blade.php
new file mode 100644
index 0000000..60fd1fd
--- /dev/null
+++ b/resources/views/regular_contract/info.blade.php
@@ -0,0 +1,283 @@
+@extends('layouts.app')
+@section('content')
+@php
+function safeCarbonFromDDHM($dd, $hm = '00:00') {
+$now = \Carbon\Carbon::now();
+if (!$dd || !preg_match('/^\d{1,2}$/', $dd)) return null;
+if (!$hm || !preg_match('/^\d{2}:\d{2}$/', $hm)) $hm = '00:00';
+$date = $now->format('Y-m-') . str_pad($dd, 2, '0', STR_PAD_LEFT);
+$time = $hm . ':00';
+try {
+return \Carbon\Carbon::createFromFormat('Y-m-d H:i:s', $date . ' ' . $time);
+} catch (Exception $e) {
+return null;
+}
+}
+@endphp
+
+
+
+ @if(count($contracts) > 0)
+ @foreach($contracts as $i => $contract)
+ @if($i % 2 == 0)
+
+ @endif
+
+ @php
+ $now = \Carbon\Carbon::now();
+ $update_flag = $contract->contract_renewal;
+ $start_dd = $contract->update_grace_period_start_date;
+ $start_hm = $contract->update_grace_period_start_time;
+ $end_dd = $contract->update_grace_period_end_date;
+ $end_hm = $contract->update_grace_period_end_time;
+ // dd→今月のdd日, HH:mm→HH:mm:00 でCarbon化
+ $start_dt = safeCarbonFromDDHM($start_dd, $start_hm);
+ // $start_dd > $end_ddなら$end_ddは次月扱い
+ if (is_numeric($start_dd) && is_numeric($end_dd) && $start_dd > $end_dd) {
+ $next_month = $now->copy()->addMonth();
+ $end_date = $next_month->format('Y-m-') . str_pad($end_dd, 2, '0', STR_PAD_LEFT);
+ $end_time = $end_hm . ':00';
+ try {
+ $end_dt = \Carbon\Carbon::createFromFormat('Y-m-d H:i:s', $end_date . ' ' . $end_time);
+ } catch (Exception $e) {
+ $end_dt = null;
+ }
+ } else {
+ $end_dt = safeCarbonFromDDHM($end_dd, $end_hm);
+ }
+ $month_end = $now->copy()->endOfMonth();
+ $bg = 'alert-warning';
+ $btn_text = '更新する';
+ $btn_active = true;
+
+ // 追加: contract_periodeの月より現在月が前なら一律「ご契約中」
+ $periode_month = $contract->contract_periode ? \Carbon\Carbon::parse($contract->contract_periode)->month : null;
+ if (!empty($contract->contract_periode)) {
+ $periode_month = \Carbon\Carbon::parse($contract->contract_periode)->month;
+ }
+ if ($periode_month && $now->month < $periode_month) {
+ $bg='bg-white' ;
+ $btn_text='ご契約中' ;
+ $btn_active=false;
+ } else {
+ if ($update_flag===0) {
+ $bg='bg-white' ;
+ $btn_text='手続き中' ;
+ $btn_active=false;
+ }
+ elseif ($update_flag===1) {
+ $bg='bg-white' ;
+ $btn_text='更新済' ;
+ $btn_active=false;
+ }
+ elseif (!is_null($end_dt) && $end_dt->gt($start_dt)) {
+ if ($start_dt && $now->lt($start_dt)) {
+ $bg = 'bg-white';
+ $btn_text = 'ご契約中';
+ $btn_active = false;
+ } else {
+ // 修正: 契約終了日を過ぎていて、更新可能期間内は赤背景
+ $periode_dt = $contract->contract_periode ? \Carbon\Carbon::parse($contract->contract_periode) : null;
+ if ($periode_dt && $now->gt($periode_dt) && $start_dt && $end_dt && $now->between($start_dt, $end_dt)) {
+ $bg = 'alert-danger';
+ $btn_text = '更新する';
+ $btn_active = true;
+ } else {
+ $bg = 'alert-warning';
+ $btn_text = '更新する';
+ $btn_active = true;
+ }
+ }
+ }
+ elseif ($start_dt && $start_dt->gt($end_dt)) {
+ if ($now->lt($start_dt)) {
+ $bg = 'bg-white';
+ $btn_text = 'ご契約中';
+ $btn_active = false;
+ } elseif ($now->gte($start_dt) && $now->lte($month_end)) {
+ $bg = 'alert-warning';
+ $btn_text = '更新する';
+ $btn_active = true;
+ } else {
+ $bg = 'alert-danger';
+ $btn_text = '更新する';
+ $btn_active = true;
+ }
+ }
+ }
+ @endphp
+ @if($bg == 'bg-white')
+
+ @elseif($bg == 'alert-warning')
+
+ @elseif($bg == 'alert-danger')
+
+ @else
+
+ @endif
+
+
+
+ | 定期契約ID |
+ {{ $contract->contract_id }} |
+
+
+ | 駐輪場名 |
+ {{ $contract->park_name }} |
+
+
+ | 利用者区分 |
+ {{ $contract->usertype_subject1 }} |
+
+
+ | 車種 |
+ {{ $contract->psection_subject ?? '' }} |
+
+
+ | 階数 |
+ {{ $contract->ptype_subject ?? '' }} |
+
+
+ | 車室番号 |
+ {{ $contract->pplace_no ?? '' }} |
+
+
+ | 利用開始日 |
+ {{ \Carbon\Carbon::parse($contract->contract_periods)->format('Y/m/d') }} |
+
+
+ | 契約月数 |
+ {{ $contract->enable_months }}ヶ月 |
+
+
+
+
+
+ @if($bg == 'alert-warning' && $btn_active)
+ {{ $btn_text }}
+ @elseif($bg == 'alert-danger' && $btn_active)
+ {{ $btn_text }}
+ @else
+
+ @endif
+ @if($bg == 'alert-warning')
+
+ @elseif($bg == 'alert-danger')
+
+ @else
+
+ @endif
+
+ @php
+ $has_receipt = DB::table('inv_publish')->where('contract_id', $contract->contract_id)->exists();
+ @endphp
+ @if($has_receipt)
+ @if($bg == 'alert-warning')
+ 領収書再発行
+ @elseif($bg == 'alert-danger')
+ 領収書再発行
+ @else
+ 領収書再発行
+ @endif
+ @else
+ @if($bg == 'alert-warning')
+ 領収書発行
+ @elseif($bg == 'alert-danger')
+ 領収書発行
+ @else
+ 領収書発行
+ @endif
+ @endif
+
+ |
+
+
+
+
+ @if($i % 2 == 1 || $i == count($contracts) - 1)
+
+ @endif
+ @endforeach
+ @else
+
+ @endif
+ {{-- modal moved to end of file to avoid clipping issues --}}
+
+
+
+
+@endsection
+
+
+
+
+
+ 解約の手続きにつきましては、下記コールセンターへお問い合わせください。
So-Managerコールセンター
03-5856-4720
+
+
+
+
+
+
+
\ No newline at end of file