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 +
+ @csrf +
+

領収書内容の入力

+

領収書の宛名を入力してください。

+ +
+
+ + +
+
+ + +
+
+
+ +
+
+ +
+
+@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 + + + + \ No newline at end of file