Compare commits

...

2 Commits

Author SHA1 Message Date
00fe485cbe Merge pull request '定期契約履歴確認、領収書宛名入力画面対応' (#54) from main_higashide into main
All checks were successful
Deploy so-manager (auto) / deploy (push) Successful in 25s
Reviewed-on: #54
2026-01-20 18:01:14 +09:00
628317d04f 定期契約履歴確認、領収書宛名入力画面対応 2026-01-20 17:59:48 +09:00
10 changed files with 872 additions and 174 deletions

View File

@ -1,68 +1,114 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Controllers; namespace App\Http\Controllers;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use Mpdf\Mpdf; use Mpdf\Mpdf;
use Illuminate\Contracts\View\View;
use function base_path; use function base_path;
class ReceiptController extends Controller class ReceiptController extends Controller
{ {
// 宛名入力画面表示 /**
public function input($contract_id) * 領収書宛名入力画面を表示する。
* 契約IDに基づき、領収書宛名入力フォームを表示する。
*
* @param string $management_code 運営元コード
* @param string $contract_id 契約ID
* @return mixed ビュー応答またはリダイレクト
*/
public function input(string $management_code, string $contract_id): mixed
{ {
$user_id = session('user_id'); $user_id = session('user_id');
if (!$user_id) { if (!$user_id) {
return redirect('/login'); // 未認証アクセスセッション切れを追跡するため、INFOログで記録
\Log::info("[INFO] " . now()->format('Y-m-d H:i:s') . " 未認証ユーザーによるアクセス(セッション切れ): 領収書宛名入力画面アクセス");
return redirect()->guest('/login');
} }
$user = DB::table('user')->where('user_id', $user_id)->first(); // ヘッダー表示のためユーザー名取得
$user_name = DB::table('user')->where('user_id', $user_id)->value('user_name');
\Log::info('領収書宛名入力画面にアクセス', [ $management = session('management');
'user_id' => $user_id, $management_id = $management->management_id;
]);
// セキュリティのため、そのユーザーの契約のみアクセス可能
$contract = DB::table('regular_contract')
->join('park', 'regular_contract.park_id', '=', 'park.park_id')
->where('contract_id', $contract_id)
->where('user_id', $user_id)
->where('park.management_id', $management_id)
->first();
if (!$contract) {
// アクセス権限がない場合、エラーログで記録
\Log::error("[ERROR] " . now()->format('Y-m-d H:i:s') . " アクセス権限なし: user_id=" . $user_id . ", contract_id=" . $contract_id);
abort(403, 'アクセス権限がありません。');
}
// システム操作の追跡のため、アクセスを記録
\Log::info("[INFO] " . now()->format('Y-m-d H:i:s') . " 領収書宛名入力画面にアクセス: user_id=" . $user_id . ", contract_id=" . $contract_id);
return view('receipt.input', [ return view('receipt.input', [
'user_name' => $user ? $user->user_name : '', // ユーザー名(ヘッダー用) 'user_name' => $user_name,
'contract_id' => $contract_id 'contract_id' => $contract_id,
'management_code' => $management_code,
]); ]);
} }
// 領収書発行(入力内容の保存) /**
public function issue(Request $request, $contract_id) * 領収書の宛名入力内容を確認し、保存する。
*
* @param Request $request リクエストオブジェクト
* @param string $management_code 運営元コード
* @param string $contract_id 契約ID
* @return mixed PDFダウンロードまたはリダイレクト
*/
public function issue(Request $request, string $management_code, string $contract_id): mixed
{ {
$user_id = session('user_id'); $user_id = session('user_id');
if (!$user_id) { if (!$user_id) {
// 未認証アクセスセッション切れを追跡するため、INFOログで記録
\Log::info("[INFO] " . now()->format('Y-m-d H:i:s') . " 未認証ユーザーによるアクセス(セッション切れ): 領収書発行");
return redirect('/login'); return redirect('/login');
} }
$receipt_name = $request->input('receipt_name'); // 既存レコードがある場合、再発行を促す
$keisho = $request->input('keisho');
// 既存レコードチェック
$exists = DB::table('inv_publish')->where('contract_id', $contract_id)->exists(); $exists = DB::table('inv_publish')->where('contract_id', $contract_id)->exists();
if ($exists) { if ($exists) {
// エラー時はinput画面に戻し、メッセージ表示 // エラー時はinput画面に戻し、メッセージ表示
return redirect()->back()->withInput()->withErrors(['contract_id' => 'この契約の領収書は既に発行されています。契約履歴から再発行を行ってください。']); \Log::info("[INFO] " . now()->format('Y-m-d H:i:s') . " 領収書発行済み: user_id=" . $user_id . ", contract_id=" . $contract_id);
return redirect()->back()->withInput()->withErrors(['contract_id' => __('messages.RECEIPT_E000001')]);
} }
// 4バイト文字絵文字等チェック $receipt_name = trim($request->input('receipt_name') ?? '');
$keisho = $request->input('keisho');
// 宛名必須チェック
if (empty($receipt_name)) {
return redirect()->back()->withInput()->withErrors(['contract_id' => str_replace('{0}', '宛名', __('messages.SYSCOMMON_E000001'))]);
}
// 4バイト文字絵文字等を防ぐ
if (preg_match('/[\xF0-\xF7][\x80-\xBF]{3}/', $receipt_name)) { if (preg_match('/[\xF0-\xF7][\x80-\xBF]{3}/', $receipt_name)) {
return redirect()->back()->withInput()->withErrors(['contract_id' => '宛名に絵文字などの特殊文字は使用できません。']); return redirect()->back()->withInput()->withErrors(['contract_id' => str_replace('{0}', '宛名', __('messages.SYSCOMMON_E000046'))]);
} }
// 文字数チェック // 文字数チェック
if (mb_strlen($receipt_name) > 30) { if (mb_strlen($receipt_name) > 30) {
return redirect()->back()->withInput()->withErrors(['contract_id' => '宛名は30文字以内で入力してください。']); return redirect()->back()->withInput()->withErrors(['contract_id' => str_replace(['{0}', '{1}'], ['宛名', '30'], __('messages.SYSCOMMON_E000011'))]);
} }
// 敬称選択チェック // 敬称選択チェック
if (empty($keisho)) { if (empty($keisho)) {
return redirect()->back()->withInput()->withErrors(['contract_id' => '敬称を選択してください。']); return redirect()->back()->withInput()->withErrors(['contract_id' => str_replace('{0}', '敬称', __('messages.SYSCOMMON_E000047'))]);
} }
// inv_publishテーブルに新規登録insert // 領収書再発行を可能にするため、領収書発行履歴登録
$inv_name = $receipt_name . $keisho; $inv_name = $receipt_name . $keisho;
$now = date('Y-m-d H:i:s'); $now = date('Y-m-d H:i:s');
$seq = DB::table('inv_publish')->max('seq') ?? 0; $seq = DB::table('inv_publish')->max('seq') ?? 0;
@ -74,60 +120,113 @@ class ReceiptController extends Controller
'inv_name' => $inv_name, 'inv_name' => $inv_name,
'published_at' => date('Y-m-d'), 'published_at' => date('Y-m-d'),
'type' => 0, 'type' => 0,
'count' => 1, 'count' => 0,
'created_at' => $now, 'created_at' => $now,
'updated_at' => null, 'updated_at' => null,
]); ]);
// 完了後はdownloadメソッドを直接呼び出し再発行フラグfalseで渡す // 領収書発行履歴の登録成功を記録
\Log::info("[INFO] " . now()->format('Y-m-d H:i:s') . " 領収書発行履歴登録成功: user_id=" . $user_id . ", contract_id=" . $contract_id . ", seq=" . $seq);
$management = session('management');
// 完了後はdownloadメソッドを直接呼び出し初回発行のため再発行フラグはfalseで渡す
$is_reissue = false; $is_reissue = false;
return $this->download($contract_id, $is_reissue); return $this->download($management->management_code, $contract_id, $is_reissue);
} }
public function download($contract_id, $is_reissue = true) /**
* 領収書PDFをダウンロードする。
* 契約IDに基づきデータを取得し、PDFを生成してレスポンスとして返す。
*
* @param string $management_code 運営元コード
* @param string $contract_id 契約ID
* @param bool $is_reissue 再発行フラグデフォルトtrue
* @return mixed PDFダウンロードまたはリダイレクト
* @throws \Symfony\Component\HttpKernel\Exception\HttpException アクセス権限エラーまたはシステムエラー時
*/
public function download(string $management_code, string $contract_id, bool $is_reissue = true): mixed
{ {
// 必要なデータを取得 try {
$contract = DB::table('regular_contract')->where('contract_id', $contract_id)->first(); $user_id = session('user_id');
$inv = DB::table('inv_publish')->where('contract_id', $contract_id)->first(); if (!$user_id) {
$t_number = DB::table('inv_setting')->value('t_number'); // 未認証アクセスセッション切れを追跡するため、INFOログで記録
\Log::info("[INFO] " . now()->format('Y-m-d H:i:s') . " 未認証ユーザーによるアクセス(セッション切れ): 領収書ダウンロード");
// park_name取得regular_contract.park_id=park.park_id return redirect('/login');
$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;
} }
$management = session('management');
$management_id = $management->management_id;
$contract_id = (int) $contract_id;
// PDFに表示する契約情報を取得
$contract = DB::table('regular_contract')
->join('park', 'regular_contract.park_id', '=', 'park.park_id')
->where('contract_id', $contract_id)
->where('user_id', $user_id)
->where('park.management_id', $management_id)
->select('regular_contract.*', 'park.park_name')
->first();
// PDFに表示する領収書名前を取得
$inv = DB::table('inv_publish')
->where('contract_id', $contract_id)
->where('user_id', $user_id)
->first();
// PDFに表示する事業者情報を取得
$inv_setting = DB::table('inv_setting')
->where('management_id', $management_id)
->first();
// ダウンロード回数をカウントアップ
DB::table('inv_publish')
->where('contract_id', $contract_id)
->where('user_id', $user_id)
->update([
'count' => DB::raw('count + 1'),
'updated_at' => now(),
]);
// BladeテンプレートをHTMLにレンダリング
$html = view('receipt.pdf', [
'contract' => $contract,
'inv' => $inv,
'inv_setting' => $inv_setting,
'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);
// システム操作の追跡のため、アクセスを記録
if ($is_reissue) {
\Log::info("[INFO] " . now()->format('Y-m-d H:i:s') . " 領収書再発行ダウンロード実行: user_id=" . $user_id . ", contract_id=" . $contract_id);
} else {
\Log::info("[INFO] " . now()->format('Y-m-d H:i:s') . " 領収書新規発行ダウンロード実行: user_id=" . $user_id . ", contract_id=" . $contract_id);
}
// PDFダウンロード
return response($mpdf->Output('receipt_' . $contract_id . '.pdf', 'S'))
->header('Content-Type', 'application/pdf')
->header('Content-Disposition', 'attachment; filename="receipt_' . $contract_id . '.pdf"');
} catch (\Exception $e) {
// PDF生成エラーを記録
\Log::error("[ERROR] " . now()->format('Y-m-d H:i:s') . " PDFダウンロードエラー: user_id=" . $user_id . ", contract_id=" . $contract_id . ", error=" . $e->getMessage());
abort(500, '領収書ダウンロードに失敗しました。');
} }
// 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"');
} }
} }

View File

@ -1,9 +1,13 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Controllers; namespace App\Http\Controllers;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\View\View;
use Carbon\Carbon; use Carbon\Carbon;
class RegularContractController extends Controller class RegularContractController extends Controller
@ -76,54 +80,73 @@ class RegularContractController extends Controller
]); ]);
} }
public function showHistory(Request $request) public function showHistory(Request $request): mixed
{ {
/**
* 定期契約履歴を表示する。
* ユーザーの契約履歴を取得し、ページネーション付きでビューに渡す。
*
* @param Request $request 受信したHTTPリクエスト
* @return mixed ビュー応答またはリダイレクト
*/
$user_id = session('user_id'); $user_id = session('user_id');
if (!$user_id) { if (!$user_id) {
return redirect('/login'); // 未認証アクセスセッション切れを追跡するため、INFOログで記録
\Log::info("[INFO] " . now()->format('Y-m-d H:i:s') . " 未認証ユーザーによるアクセス(セッション切れ): 定期契約履歴確認画面アクセス");
return redirect()->guest('/login');
} }
// ヘッダー表示のためユーザー名取得
$user_name = DB::table('user')->where('user_id', $user_id)->value('user_name'); $user_name = DB::table('user')->where('user_id', $user_id)->value('user_name');
// マルチテナント対応のため、運営元コード、IDを取得
$management = session('management');
$management_code = $management->management_code;
$management_id = $management->management_id;
// 定期契約情報を取得(ページネーション付き) // 定期契約情報を取得(ページネーション付き)
$contracts_query = DB::table('regular_contract') $contracts_query = DB::table('regular_contract')
->join('park', 'regular_contract.park_id', '=', 'park.park_id') ->join('park', 'regular_contract.park_id', '=', 'park.park_id')
->join('usertype', 'regular_contract.user_categoryid', '=', 'usertype.user_categoryid') ->join('usertype', 'regular_contract.user_categoryid', '=', 'usertype.user_categoryid')
->leftJoin('city', 'park.city_id', '=', 'city.city_id')
->where('regular_contract.user_id', $user_id) ->where('regular_contract.user_id', $user_id)
->where('park.management_id', $management_id)
->whereNotNull('regular_contract.contract_money') ->whereNotNull('regular_contract.contract_money')
->select( ->select(
'regular_contract.contract_id', 'regular_contract.contract_id',
'park.park_name', 'park.park_name',
'usertype.usertype_subject1', 'usertype.usertype_subject1',
'usertype.usertype_subject3',
'regular_contract.contract_periods', 'regular_contract.contract_periods',
'regular_contract.contract_periode', 'regular_contract.contract_periode',
'regular_contract.enable_months', 'regular_contract.enable_months',
'regular_contract.park_id', 'regular_contract.park_id',
'city.update_grace_period_start_date', 'park.update_grace_period_start_date',
'park.update_grace_period_end_date',
) )
->orderBy('regular_contract.contract_id', 'desc'); ->orderBy('regular_contract.contract_id', 'desc');
// ページネーション4件ずつ // ページネーション4件ずつ
$contracts = $contracts_query->paginate(4); $contracts = $contracts_query->paginate(4);
// grace日付加工 // 日付をyyyy/MM/dd形式で画面表示するため加工
$contracts->getCollection()->transform(function ($contract) { $contracts->getCollection()->transform(function ($contract) {
$periode = $contract->contract_periode; $periode = $contract->contract_periode;
$grace_day = $contract->update_grace_period_start_date; $grace_day = $contract->update_grace_period_start_date;
$ym = date('Y/m', strtotime($periode)); $ym = date('Y/m', strtotime($periode));
$day = str_pad($grace_day, 2, '0', STR_PAD_LEFT); $day = str_pad($grace_day, 2, '0', STR_PAD_LEFT);
$contract->periode_with_grace = $ym . '/' . $day; $contract->periode_with_grace = $ym . '/' . $day;
$contract->formatted_periods = \Carbon\Carbon::parse($contract->contract_periods)->format('Y/m/d');
$contract->has_receipt = DB::table('inv_publish')->where('contract_id', $contract->contract_id)->exists();
return $contract; return $contract;
}); });
\Log::info('契約履歴表示画面にアクセス', [ // システム操作の追跡のため、アクセスを記録
'user_id' => $user_id, \Log::info("[INFO] " . now()->format('Y-m-d H:i:s') . " 契約履歴表示画面にアクセス: user_id=" . $user_id . ", management_code=" . $management_code);
]);
return view('regular_contract.history', [ return view('regular_contract.history', [
'active_menu' => 'SWC-6-1', // マイページメニューの選択状態用 'active_menu' => 'SWC-6-1', // マイページメニューの選択状態用
'user_name' => $user_name, // ユーザー名(ヘッダー用) 'user_name' => $user_name, // ユーザー名(ヘッダー用)
'contracts' => $contracts, 'contracts' => $contracts,
'management_code' => $management_code,
]); ]);
} }

View File

@ -7,6 +7,7 @@ use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Illuminate\Http\Response; use Illuminate\Http\Response;
use Illuminate\Http\RedirectResponse;
class ManagementMiddleware class ManagementMiddleware
{ {
@ -19,7 +20,7 @@ class ManagementMiddleware
* @return Response 次の処理のレスポンス * @return Response 次の処理のレスポンス
* @throws \Symfony\Component\HttpKernel\Exception\HttpException 運営元コードが見つからない場合 * @throws \Symfony\Component\HttpKernel\Exception\HttpException 運営元コードが見つからない場合
*/ */
public function handle(Request $request, Closure $next): Response public function handle(Request $request, Closure $next): Response|RedirectResponse
{ {
// マルチテナント対応のため、URLの最初のセグメントを運営元コードとして扱う // マルチテナント対応のため、URLの最初のセグメントを運営元コードとして扱う
$path = $request->getPathInfo(); // パス全体を取得 $path = $request->getPathInfo(); // パス全体を取得

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,69 @@
<?php
return [
// 汎用エラーメッセージ
'SYSCOMMON_E000001' => '必須項目:{0}を入力してください。',
'SYSCOMMON_E000002' => '{0}はyyyy/MM/ddで入力してください。',
'SYSCOMMON_E000003' => '{0}は半角文字で入力してください。',
'SYSCOMMON_E000004' => '{0}は半角数字で入力してください。',
'SYSCOMMON_E000005' => '{0}は半角英字で入力してください。',
'SYSCOMMON_E000006' => '{0}は半角英数で入力してください。',
'SYSCOMMON_E000007' => '{0}は半角カナで入力してください。',
'SYSCOMMON_E000008' => '{0}は全角文字で入力してください。',
'SYSCOMMON_E000009' => '{0}は{1}桁で入力してください。',
'SYSCOMMON_E000010' => '{0}は{1}桁以内で入力してください。',
'SYSCOMMON_E000011' => '{0}は{1}文字以内で入力してください。',
'SYSCOMMON_E000012' => '{0}は0または0.0以外の文字を入力してください。',
'SYSCOMMON_E000013' => '{0}にはアルファベットと数字だけが入力可能です。',
'SYSCOMMON_E000014' => '{0}は整数部の最大桁数が{1}桁を超えています。',
'SYSCOMMON_E000015' => '{0}は小数部の最大桁数が{1}桁を超えています。',
'SYSCOMMON_E000016' => '{0}は整数部の最小桁数が{1}桁未満です。',
'SYSCOMMON_E000017' => '{0}は小数部の最小桁数が{1}桁未満です。',
'SYSCOMMON_E000018' => '{0}には正の値を入力してください。',
'SYSCOMMON_E000019' => '{0}には負の値を入力してください。',
'SYSCOMMON_E000020' => '{0}は整数{2}桁以内、小数{1}桁以内で入力してください。',
'SYSCOMMON_E000021' => '{0}はマスタに存在しません。',
'SYSCOMMON_E000022' => '{0}はマスタに既に存在しています。',
'SYSCOMMON_E000023' => '{0}は既に削除されています。',
'SYSCOMMON_E000024' => '一つ以上選択してください。',
'SYSCOMMON_E000025' => '正しい区分を選択してください。',
'SYSCOMMON_E000026' => '明細行を選択してください。',
'SYSCOMMON_E000027' => '複数行の選択はできません。',
'SYSCOMMON_E000028' => '{0}行以上の追加は行うことができません。',
'SYSCOMMON_E000029' => '{0}行以上の挿入は行うことができません。',
'SYSCOMMON_E000030' => '読取専用の為、行を削除することはできません。({0}行目)',
'SYSCOMMON_E000031' => '{0}は編集できません。',
'SYSCOMMON_E000032' => '明細行は{0}件以上入力してください。',
'SYSCOMMON_E000033' => '検索条件を入力してください。',
'SYSCOMMON_E000034' => '入力したIDまたはパスワードは正しくありません。',
'SYSCOMMON_E000035' => '対象のデータは編集作業中に変更されました。再度編集画面を開き直してください。',
'SYSCOMMON_E000036' => '対象のデータは既に削除されています。',
'SYSCOMMON_E000037' => '関連データが存在するため修正、削除できません。',
'SYSCOMMON_E000038' => '指定されたURLは存在しません。',
'SYSCOMMON_E000039' => 'リクエストした画面は現在使用できません。',
'SYSCOMMON_E000040' => '不正な画面遷移が行われました。メニュー選択から操作をやり直してください。',
'SYSCOMMON_E000041' => 'システムエラーが発生しました。システム管理者に連絡してください。',
'SYSCOMMON_E000042' => 'ファイルダウンロードに失敗しました。',
'SYSCOMMON_E000043' => 'ファイルアップロードに失敗しました。',
'SYSCOMMON_E000044' => 'バッチ処理が異常終了しました。',
'SYSCOMMON_E000045' => '{0}は{1}のため、入力することができません。',
'SYSCOMMON_E000046' => '{0}に特殊文字は使用できません。',
'SYSCOMMON_E000047' => '{0}を選択してください。',
// 汎用情報メッセージ
'SYSCOMMON_I000001' => '登録します。よろしいですか?',
'SYSCOMMON_I000002' => '更新します。よろしいですか?',
'SYSCOMMON_I000003' => '削除します。よろしいですか?',
'SYSCOMMON_I000004' => '一時保存します。よろしいですか?',
'SYSCOMMON_I000005' => 'ログアウトしてよろしいですか?',
'SYSCOMMON_I000006' => '入力内容が破棄されます。よろしいですか?',
'SYSCOMMON_I000007' => 'ダウンロードします。よろしいですか?',
'SYSCOMMON_I000008' => 'アップロードします。よろしいですか?',
'SYSCOMMON_I000009' => '登録されました。{0}',
'SYSCOMMON_I000010' => '更新されました。{0}',
'SYSCOMMON_I000011' => '削除されました。{0}',
'SYSCOMMON_I000012' => 'ダウンロードが終了しました。',
'SYSCOMMON_I000013' => 'アップロードが終了しました。',
// 領収書関連メッセージ
'RECEIPT_E000001' => 'この契約の領収書は既に発行されています。契約履歴から再発行を行ってください。',
];

View File

@ -39,10 +39,6 @@
<main> <main>
@yield('content') @yield('content')
</main> </main>
{{-- フッターメニュー --}}
@include('partials.mypagefootermenu')
{{-- ニュース --}}
@include('partials.news')
{{-- フッター --}} {{-- フッター --}}
@include('partials.footer') @include('partials.footer')
</div> </div>

View File

@ -1,35 +1,42 @@
@extends('layouts.app') @extends('layouts.app')
@section('title', '領収書の発行 | So-Manager')
@section('content') @section('content')
<main> <main>
<header class="alert alert-success"> <header class="title-header">領収書の発行</header>
<h4 class="container">領収書の発行</h4>
</header>
@if($errors->has('contract_id')) @if($errors->has('contract_id'))
<div class="alert alert-danger text-center"> <div class="alert alert-danger text-center">
{{ $errors->first('contract_id') }} {{ $errors->first('contract_id') }}
</div> </div>
@endif @endif
<form method="POST" action="{{ url('receipt/issue/' . $contract_id) }}"> <form method="POST" action="{{ route('receipt.issue', ['management_code' => $management_code, 'contract_id' => $contract_id]) }}" id="issueForm">
@csrf @csrf
<div class="col-12 col-md-5 offset-0 offset-md-1 mt10 mb50"> <div class="col-12 col-md-5 offset-0 offset-md-1 mt10 mb50">
<p class="text-left font-weight-bold">領収書内容の入力</p> <p class="text-left font-weight-bold">領収書内容の入力</p>
<p class="text-left">領収書の宛名を入力してください。</p> <p class="text-left">領収書の宛名を入力してください。</p>
<input type="text" class="form-control" id="receipt_name" name="receipt_name" required> <input type="text" class="form-control" id="receipt_name" name="receipt_name" value="{{ old('receipt_name') }}" required style="color: black;">
<div class="mt-2"> <div class="mt-2">
<div class="form-check"> <div class="form-check">
<input class="form-check-input" type="radio" name="keisho" id="keisho_sama" value="" checked> <input class="form-check-input" type="radio" name="keisho" id="keisho_sama" value="" {{ old('keisho') == '様' ? 'checked' : '' }}>
<label class="form-check-label" for="keisho_sama"></label> <label class="form-check-label" for="keisho_sama"></label>
</div> </div>
<div class="form-check"> <div class="form-check">
<input class="form-check-input" type="radio" name="keisho" id="keisho_onchu" value="御中"> <input class="form-check-input" type="radio" name="keisho" id="keisho_onchu" value="御中" {{ old('keisho') == '御中' ? 'checked' : '' }}>
<label class="form-check-label" for="keisho_onchu">御中</label> <label class="form-check-label" for="keisho_onchu">御中</label>
</div> </div>
</div> </div>
<div class="col-6 offset-3 text-left"> <div class="col-6 offset-3 text-left">
<button type="submit" class="btn btn-success">発行する</button> <button type="submit" class="green-btn long-btn" id="submitBtn">発行する</button>
</div> </div>
</div> </div>
</form> </form>
</main> </main>
<script>
window.onload = function() {
document.getElementById('submitBtn').disabled = false;
};
document.getElementById('issueForm').addEventListener('submit', function() {
document.getElementById('submitBtn').disabled = true;
});
</script>
@endsection @endsection

View File

@ -58,12 +58,18 @@
font-size: 12px; font-size: 12px;
} }
.reissue { .reissue-label {
position: absolute; display: inline-block;
top: 16px; border: 2px solid #222;
left: 16px; border-radius: 36px;
width: 100px; padding: 14px 48px;
height: 40px; font-weight: bold;
font-size: 1.6em;
color: #222;
background: #fff;
letter-spacing: 0.05em;
box-sizing: border-box;
margin: 12px 0 0 0;
} }
</style> </style>
</head> </head>
@ -71,19 +77,19 @@
<body> <body>
<div class="border"> <div class="border">
@if($is_reissue) @if($is_reissue)
<img src="{{ str_replace('\\', '/', public_path('images/reissue.png')) }}" class="reissue" alt="再発行"> <span class="reissue-label">再発行</span>
@endif @endif
<div class="right"> <div class="right">
No. {{ $inv->seq ?? '' }}<br> No. {{ $inv->seq ?? '' }}<br>
発行日: {{ !empty($inv->published_at) ? \Carbon\Carbon::parse($inv->published_at)->format('Y年m月d日') : '' }} 発行日: {{ !empty($inv->published_at) ? \Carbon\Carbon::parse($inv->published_at)->format('Y年m月d日') : '' }}
</div> </div>
<div class="title" style="font-size:20px; font-weight:bold;">領収</div> <div class="title" style="font-size:20px; font-weight:bold;">領収</div>
<div>ID: {{ $contract->contract_id ?? '' }}</div> <div>ID: {{ $contract->contract_id ?? '' }}</div>
<div><span style="border-bottom:1px solid #222;">{{ $inv->inv_name ?? '' }}</span></div> <div><span style="border-bottom:1px solid #222;">{{ $inv->inv_name ?? '' }}</span></div>
<table class="table"> <table class="table">
<tr> <tr>
<td class="left">契約駐輪場名</td> <td class="left">契約駐輪場名</td>
<td class="right">{{ $park_name ?? '' }}</td> <td class="right">{{ $contract->park_name ?? '' }}</td>
</tr> </tr>
<tr> <tr>
<td class="left">小計10対象</td> <td class="left">小計10対象</td>
@ -107,23 +113,40 @@
{{ !empty($contract->contract_periods) ? \Carbon\Carbon::parse($contract->contract_periods)->format('Y年m月d日') : '' }} {{ !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日') : '' }} {{ !empty($contract->contract_periode) ? \Carbon\Carbon::parse($contract->contract_periode)->format('Y年m月d日') : '' }}
</span>として<br> </span>として<br>
上記金額を正に領収いたしました。 {{ !empty($contract->contract_payment_day) ? \Carbon\Carbon::parse($contract->contract_payment_day)->format('Y年m月d日') : '' }}上記金額を正に領収いたしました。
</div> </div>
<table style="width:100%; margin-top:40px;"> <table style="width:100%; margin-top:40px;">
<tr> <tr>
<td style="width:55%"></td> <td style="width:55%"></td>
<td> <td>
<div class="company"> <div class="company">
<span style="font-size:20px; font-weight:bold;">株式会社ソーリン</span><br> <span style="font-size:20px; font-weight:bold;">{{ $inv_setting->t_name ?? '' }}</span><br>
〒121-0073<br> {{ $inv_setting->zipcode ?? '' }}<br>
東京都足立区六町四丁目12-25<br> {{ $inv_setting->adrs ?? '' }}<br>
適格事業者番号:{{ $t_number }}<br> 適格事業者番号:{{ $inv_setting->t_number ?? '' }}<br>
TEL:03-5856-4647<br> @if(!empty($inv_setting->tel_num))
FAX:03-5856-4648<br> TEL:{{ $inv_setting->tel_num}}<br>
@endif
@if(!empty($inv_setting->fax_num))
FAX:{{ $inv_setting->fax_num}}<br>
@endif
</div> </div>
</td> </td>
<td><img src="{{ str_replace('\\', '/', public_path('images/hanko.png')) }}" class="stamp"></td> @php
$stamp_file = null;
$extensions = ['png', 'jpeg', 'jpg'];
foreach ($extensions as $ext) {
$file_path = public_path('images/' . $inv_setting->t_name . '_stamp.' . $ext);
if (file_exists($file_path)) {
$stamp_file = $inv_setting->t_name . '_stamp.' . $ext;
break;
}
}
@endphp
@if($stamp_file)
<td><img src="{{ str_replace('\\', '/', public_path('images/' . $stamp_file)) }}" class="stamp"></td>
@endif
</tr> </tr>
</table> </table>
</div> </div>

View File

@ -1,9 +1,8 @@
@extends('layouts.app') @extends('layouts.app')
@section('title', '定期契約履歴を見る-契約履歴 | So-Manager')
@section('content') @section('content')
<main> <main>
<header class="alert alert-success"> <header class="title-header">契約履歴 &gt; 定期契約履歴を見る</header>
<h4 class="container">契約履歴 > 定期契約履歴を見る</h4>
</header>
<section class="container mt30 mb50"> <section class="container mt30 mb50">
@if(count($contracts) > 0) @if(count($contracts) > 0)
@foreach($contracts as $i => $contract) @foreach($contracts as $i => $contract)
@ -23,11 +22,15 @@
</tr> </tr>
<tr> <tr>
<th>利用者区分</th> <th>利用者区分</th>
<td>{{ $contract->usertype_subject1 }}</td> <td>{{ $contract->usertype_subject1 }}
@if($contract->usertype_subject3 !== '該当なし')
{{ $contract->usertype_subject3 }}
@endif
</td>
</tr> </tr>
<tr> <tr>
<th>開始日</th> <th>開始日</th>
<td>{{ \Carbon\Carbon::parse($contract->contract_periods)->format('Y/m/d') }}</td> <td>{{ $contract->formatted_periods }}</td>
</tr> </tr>
<tr> <tr>
<th>月数</th> <th>月数</th>
@ -39,13 +42,10 @@
</tr> </tr>
<tr> <tr>
<td colspan="2" class="text-center"> <td colspan="2" class="text-center">
@php @if($contract->has_receipt)
$has_receipt = DB::table('inv_publish')->where('contract_id', $contract->contract_id)->exists(); <a href="{{ route('receipt.download', ['management_code' => $management_code, 'contract_id' => $contract->contract_id]) }}" class="btn btn-outline-secondary badge-pill custom-rounded-btn" style="background: transparent;">領収書再発行</a>
@endphp
@if($has_receipt)
<a href="{{ url('receipt/download/' . $contract->contract_id) }}" class="btn btn-outline-secondary badge-pill custom-rounded-btn" style="background: transparent;">領収書再発行</a>
@else @else
<a href="{{ url('receipt/input/' . $contract->contract_id) }}" class="btn btn-outline-secondary badge-pill custom-rounded-btn" style="background: transparent;">領収書発行</a> <a href="{{ route('receipt.input', ['management_code' => $management_code, 'contract_id' => $contract->contract_id]) }}" class="btn btn-outline-secondary badge-pill custom-rounded-btn" style="background: transparent;">領収書発行</a>
@endif @endif
</td> </td>
</tr> </tr>
@ -64,11 +64,9 @@
<p>定期契約情報はありません。</p> <p>定期契約情報はありません。</p>
</div> </div>
@endif @endif
<form class="row form"> <div class="mb-2 mt30" style="text-align:center;">
<div class="col-12 col-md-4 offset-0 offset-md-4 mt50 mb50"> <a href="{{ route('mypage', ['management_code' => $management_code]) }}" class="white-btn long-btn" style="max-width:500px; display:inline-block;">マイページへ戻る</a>
<a href="{{ url('mypage') }}" class="btn btn-lg btn-block btn-outline-success">マイページへ戻る</a> </div>
</div>
</form>
</section> </section>
</main> </main>
@ -83,10 +81,20 @@
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
} }
.btn-outline-secondary.badge-pill.custom-rounded-btn:not([disabled]):hover,
.btn-outline-secondary.badge-pill.custom-rounded-btn:not(.disabled):hover {
color: #212529 !important;
}
.table.text-center { .table.text-center {
border-radius: 0.25rem !important; border-radius: 1.25rem !important;
border-collapse: separate !important; border-collapse: separate !important;
overflow: hidden; overflow: hidden;
} }
.card {
border-radius: 1.25rem !important;
overflow: hidden;
}
</style> </style>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>

View File

@ -103,7 +103,7 @@ Route::middleware([\App\Http\Middleware\ManagementMiddleware::class])->group(fun
// 領収書発行 // 領収書発行
Route::get('/{management_code}/receipt/input/{contract_id}', [ReceiptController::class, 'input'])->name('receipt.input'); Route::get('/{management_code}/receipt/input/{contract_id}', [ReceiptController::class, 'input'])->name('receipt.input');
Route::get('/{management_code}/receipt/download/{contract_id}', [ReceiptController::class, 'download'])->name('receipt.download'); Route::get('/{management_code}/receipt/download/{contract_id}', [ReceiptController::class, 'download'])->name('receipt.download');
Route::post('/{management_code}/receipt/issue/{contract_id}', [ReceiptController::class, 'issue']); Route::post('/{management_code}/receipt/issue/{contract_id}', [ReceiptController::class, 'issue'])->name('receipt.issue');
// シール再発行 // シール再発行
Route::get('/{management_code}/seal/reissue/{contract_id}', [SealReissueController::class, 'index'])->name('seal.reissue'); Route::get('/{management_code}/seal/reissue/{contract_id}', [SealReissueController::class, 'index'])->name('seal.reissue');