{{ __('入力内容に不備があります') }}:
--
- @foreach($errors->all() as $error)
-
- {{ $error }} - @endforeach -
diff --git a/app/Http/Controllers/Admin/InvSettingController.php b/app/Http/Controllers/Admin/InvSettingController.php index ecb3197..eec4f24 100644 --- a/app/Http/Controllers/Admin/InvSettingController.php +++ b/app/Http/Controllers/Admin/InvSettingController.php @@ -3,224 +3,148 @@ namespace App\Http\Controllers\Admin; use App\Http\Controllers\Controller; +use App\Http\Requests\InvSettingRequest; use App\Models\InvSetting; +use App\Models\Management; +use App\Services\InvSettingService; use Illuminate\Http\Request; -use Illuminate\Support\Facades\Storage; class InvSettingController extends Controller { - /** - * 登録フォーム表示 - */ - public function form(Request $request) + private InvSettingService $service; + + public function __construct(InvSettingService $service) { - $row = InvSetting::first(); + $this->service = $service; + } - $zip1 = $zip2 = $tel1 = $tel2 = $tel3 = $fax1 = $fax2 = $fax3 = ''; + /** + * 一覧画面 + */ + public function list(Request $request) + { + $allowed = [ + 'management_id', 't_number', 't_name', + 'zipcode', 'adrs', 'bldg', 'tel_num', 'fax_num' + ]; - if ($row) { - // 郵便番号(そのままハイフン分割) - if (!empty($row->zipcode) && str_contains($row->zipcode, '-')) { - [$zip1, $zip2] = explode('-', $row->zipcode); - } + $sort = in_array($request->sort, $allowed, true) + ? $request->sort + : 'management_id'; - // 電話番号:数字以外を除去 → 2桁+4桁+4桁 に分割 - if (!empty($row->tel_num)) { - $tel = preg_replace('/\D/', '', $row->tel_num); // 数字以外を除去 - $tel1 = substr($tel, 0, 2); - $tel2 = substr($tel, 2, 4); - $tel3 = substr($tel, 6, 4); - } + $sortType = in_array($request->sort_type, ['asc', 'desc'], true) + ? $request->sort_type + : 'asc'; - // FAX番号:同じく 2桁+4桁+4桁 - if (!empty($row->fax_num)) { - $fax = preg_replace('/\D/', '', $row->fax_num); - $fax1 = substr($fax, 0, 2); - $fax2 = substr($fax, 2, 4); - $fax3 = substr($fax, 6, 4); - } + $list = InvSetting::orderBy($sort, $sortType) + ->paginate(20) + ->appends($request->query()); + + //return view('admin.invsettings.list', compact('list', 'sort', 'sortType')); + return view('admin.invsettings.list', [ + 'list' => $list, + 'sort' => $sort, + 'sort_type' => $sortType, + ]); + } + + /** + * 新規登録 + */ + public function add(Request $request) + { + if ($request->isMethod('post')) { + $validated = app(InvSettingRequest::class)->validated(); + $this->service->create($request); + + return redirect() + ->route('inv_settings') + ->with('success', 'インボイス設定を登録しました。'); } - return view('admin.invsettings._form', compact( - 'row', 'zip1', 'zip2', 'tel1', 'tel2', 'tel3', 'fax1', 'fax2', 'fax3' + $managementList = Management::pluck('management_name', 'management_id'); + + return view('admin.invsettings.add', compact('managementList')); + } + + /** + * 編集 + */ + public function edit(Request $request, $id) + { + $record = InvSetting::findOrFail($id); + + if ($request->isMethod('post')) { + $validated = app(InvSettingRequest::class)->validated(); + $this->service->update($request, $record); + + return redirect() + ->route('inv_settings') + ->with('success', 'インボイス設定を更新しました。'); + } + + [$zip1, $zip2] = explode('-', $record->zipcode ?? '-'); + [$tel1, $tel2, $tel3] = array_pad(explode('-', $record->tel_num ?? ''), 3, ''); + [$fax1, $fax2, $fax3] = array_pad(explode('-', $record->fax_num ?? ''), 3, ''); + + $managementList = Management::pluck('management_name', 'management_id'); + + return view('admin.invsettings.edit', compact( + 'record', + 'zip1', 'zip2', + 'tel1', 'tel2', 'tel3', + 'fax1', 'fax2', 'fax3', + 'managementList' )); } - /** - * 登録・更新処理 + * 削除 */ - public function save(Request $request) + public function delete(Request $request) { - // ▼ バリデーションルール - $rules = [ - 't_number' => 'required|string|max:20', - 't_name' => 'required|string|max:50', - 'zip1' => 'required|digits:3', - 'zip2' => 'required|digits:4', - 'adrs' => 'required|string|max:100', - 'bldg' => 'nullable|string|max:80', - 'tel1' => 'nullable|digits_between:2,4', - 'tel2' => 'nullable|digits_between:2,4', - 'tel3' => 'nullable|digits_between:3,4', - 'fax1' => 'nullable|digits_between:2,4', - 'fax2' => 'nullable|digits_between:2,4', - 'fax3' => 'nullable|digits_between:3,4', - 'company_image_path' => 'nullable|string|max:255', - ]; + $this->service->delete((array) $request->id); - // ▼ カスタム日本語メッセージ - $messages = [ - 't_number.required' => '適格請求書発行事業者番号を入力してください。', - 't_number.max' => '適格請求書発行事業者番号は20文字以内で入力してください。', - - 't_name.required' => '適格事業者名を入力してください。', - 't_name.max' => '適格事業者名は50文字以内で入力してください。', - - 'zip1.required' => '郵便番号(前半)を入力してください。', - 'zip1.digits' => '郵便番号(前半)は3桁で入力してください。', - 'zip2.required' => '郵便番号(後半)を入力してください。', - 'zip2.digits' => '郵便番号(後半)は4桁で入力してください。', - - 'adrs.required' => '表示住所を入力してください。', - 'adrs.max' => '表示住所は100文字以内で入力してください。', - - 'tel1.digits_between' => '電話番号1は2桁から4桁で入力してください。', - 'tel2.digits_between' => '電話番号2は2桁から4桁で入力してください。', - 'tel3.digits_between' => '電話番号3は3桁から4桁で入力してください。', - - 'fax1.digits_between' => 'FAX番号1は2桁から4桁で入力してください。', - 'fax2.digits_between' => 'FAX番号2は2桁から4桁で入力してください。', - 'fax3.digits_between' => 'FAX番号3は3桁から4桁で入力してください。', - ]; - - // ▼ バリデーション実行 - $request->validate($rules, $messages); - - // ▼ データ整形 - $zipcode = $request->zip1 . '-' . $request->zip2; - $tel = implode('-', array_filter([$request->tel1, $request->tel2, $request->tel3])); - $fax = implode('-', array_filter([$request->fax1, $request->fax2, $request->fax3])); - - // ▼ 既存レコードを取得(1レコード運用) - $row = InvSetting::first(); - - // ▼ 画像パスを設定 - $imagePath = $request->company_image_path; - - // ▼ フォームで新たにファイルを送信した場合のみ再保存(保険的処理) - if ($request->hasFile('company_image')) { - if ($imagePath && Storage::disk('public')->exists($imagePath)) { - Storage::disk('public')->delete($imagePath); - } - $imagePath = $request->file('company_image')->store('inv', 'public'); - } - - // ▼ レコードを新規作成 or 更新 - if ($row) { - $row->update([ - 't_number' => $request->t_number, - 't_name' => $request->t_name, - 'zipcode' => $zipcode, - 'adrs' => $request->adrs, - 'bldg' => $request->bldg, - 'tel_num' => $tel, - 'fax_num' => $fax, - 'company_image_path' => $imagePath, // ← hiddenの値 or 新規アップロード結果を保存 - ]); - } else { - InvSetting::create([ - 't_number' => $request->t_number, - 't_name' => $request->t_name, - 'zipcode' => $zipcode, - 'adrs' => $request->adrs, - 'bldg' => $request->bldg, - 'tel_num' => $tel, - 'fax_num' => $fax, - 'company_image_path' => $imagePath, - ]); - } - - return back()->with('success', 'インボイス設定を登録しました。'); + return redirect() + ->route('inv_settings') + ->with('success', '削除しました。'); } - /** - * 社判画像アップロード(AJAX用) + * 社判画像アップロード(AJAX) + * + * ・ファイル選択ダイアログで選択された画像をアップロード + * ・物理ファイルを storage に保存 + * ・DB には保存せず、画像パスのみを返却する */ - // public function upload(Request $request) - // { - // // ファイルがアップロードされているか確認 - // if ($request->hasFile('company_image_file')) { - - // // 拡張子チェック & バリデーション - // $request->validate([ - // 'company_image_file' => 'required|image|mimes:png,jpg,jpeg|max:2048', - // ], [ - // 'company_image_file.image' => '画像ファイルを選択してください。', - // 'company_image_file.mimes' => 'アップロード可能な形式は png, jpg, jpeg のみです。', - // 'company_image_file.max' => 'ファイルサイズは2MB以下にしてください。', - // ]); - - // // ファイル保存(public/storage/inv に格納) - // $path = $request->file('company_image_file')->store('inv', 'public'); - - // // ファイル名を抽出 - // $fileName = basename($path); - - // // JSONで返却(JSが受け取る) - // return response()->json([ - // 'file_name' => $fileName, - // 'path' => $path, - // ]); - // } - - // // ファイル未選択時 - // return response()->json([ - // 'error' => 'ファイルが選択されていません。' - // ], 400); - // } - public function upload(Request $request) { - // ファイルがアップロードされているか確認 - if ($request->hasFile('company_image_file')) { + // バリデーション + $request->validate([ + 'company_image_file' => [ + 'required', + 'file', + 'mimes:jpg,jpeg,png,gif', + 'max:2048', // 2MB + ], + ], [ + 'company_image_file.required' => '画像ファイルを選択してください。', + 'company_image_file.mimes' => 'jpg / png / gif 形式の画像を選択してください。', + 'company_image_file.max' => '画像サイズは2MB以内にしてください。', + ]); - // 拡張子チェック & バリデーション - $request->validate([ - 'company_image_file' => 'required|image|mimes:png,jpg,jpeg|max:2048', - ], [ - 'company_image_file.image' => '画像ファイルを選択してください。', - 'company_image_file.mimes' => 'アップロード可能な形式は png, jpg, jpeg のみです。', - 'company_image_file.max' => 'ファイルサイズは2MB以下にしてください。', - ]); + // ファイル取得 + $file = $request->file('company_image_file'); - // ファイルオブジェクト取得 - $file = $request->file('company_image_file'); + // storage/app/public/inv に保存 + $path = $file->store('inv', 'public'); - // 元のファイル名(例:company_logo.png) - $originalName = $file->getClientOriginalName(); - - // 保存用に、ファイル名の重複を避けるためにユニーク名を生成(推奨) - $fileName = $originalName; - - // public/storage/inv に保存 - $path = $file->storeAs('inv', $fileName, 'public'); - - // JSONで返却(JS側で表示用ファイル名を使う) - return response()->json([ - 'file_name' => $originalName, // ユーザーが見えるファイル名 - 'stored_as' => $fileName, // 実際に保存されたファイル名 - 'path' => $path, - ]); - } - - // ファイル未選択時 + // AJAX 用レスポンス return response()->json([ - 'error' => 'ファイルが選択されていません。' - ], 400); + 'path' => $path, // DB保存用パス + 'file_name' => $file->getClientOriginalName(), // 表示用ファイル名 + ]); } - - + } diff --git a/app/Http/Requests/InvSettingRequest.php b/app/Http/Requests/InvSettingRequest.php new file mode 100644 index 0000000..03d2b4e --- /dev/null +++ b/app/Http/Requests/InvSettingRequest.php @@ -0,0 +1,81 @@ + 'required|integer|exists:management,management_id', + + 't_number' => 'required|string|regex:/^T\d{13}$/', + 't_name' => 'required|string|max:40', + + 'zip1' => 'required|digits:3', + 'zip2' => 'required|digits:4', + + 'adrs' => 'required|string|max:100', + 'bldg' => 'nullable|string', + + 'tel1' => 'nullable|digits_between:2,4', + 'tel2' => 'required_with:tel1|digits_between:2,4', + 'tel3' => 'required_with:tel1|digits_between:3,4', + + 'fax1' => 'nullable|digits_between:2,4', + 'fax2' => 'required_with:fax1|digits_between:2,4', + 'fax3' => 'required_with:fax1|digits_between:3,4', + + 'company_image_path' => 'nullable|string|max:255', + ]; + } + + /** + * エラーメッセージ + */ + public function messages(): array + { + return [ + 'management_id.required' => '運営元を選択してください。', + + 't_number.required' => '適格請求書発行事業者番号を入力してください。', + 't_number.regex' => '指定フォーマット(T+13桁)に一致しません。', + + 't_name.required' => '適格事業者名を入力してください。', + 't_name.max' => '適格事業者名は40文字以内で入力してください。', + + 'zip1.required' => '郵便番号(前半)を入力してください。', + 'zip1.digits' => '郵便番号(前半)は半角数字で3桁を入力してください。', + 'zip2.required' => '郵便番号(後半)を入力してください。', + 'zip2.digits' => '郵便番号(後半)は半角数字で4桁を入力してください。', + + 'adrs.required' => '表示住所を入力してください。', + 'adrs.max' => '表示住所は100文字以内で入力してください。', + + 'tel1.digits_between' => '電話番号1は半角数字で2桁から4桁を入力してください。', + 'tel2.required_with' => '電話番号2を入力してください。', + 'tel2.digits_between'=> '電話番号2は半角数字で2桁から4桁を入力してください。', + 'tel3.required_with' => '電話番号3を入力してください。', + 'tel3.digits_between'=> '電話番号3は半角数字で3桁から4桁を入力してください。', + + 'fax1.digits_between' => 'FAX番号1は半角数字で2桁から4桁を入力してください。', + 'fax2.required_with' => 'FAX番号2を入力してください。', + 'fax2.digits_between'=> 'FAX番号2は半角数字で2桁から4桁を入力してください。', + 'fax3.required_with' => 'FAX番号3を入力してください。', + 'fax3.digits_between'=> 'FAX番号3は半角数字で3桁から4桁を入力してください。', + ]; + } +} diff --git a/app/Models/InvSetting.php b/app/Models/InvSetting.php index c99857b..3c3acc3 100644 --- a/app/Models/InvSetting.php +++ b/app/Models/InvSetting.php @@ -11,7 +11,8 @@ class InvSetting extends Model public $timestamps = true; protected $fillable = [ - + 'seq', // シーケンス + 'management_id', // 運営元ID 't_number', // 適格事業者番号 't_name', // 事業者名 'zipcode', // 郵便番号 @@ -19,7 +20,18 @@ class InvSetting extends Model 'bldg', // 建物名 'tel_num', // 電話番号 'fax_num', // FAX番号 - 'company_image_path', // 会社ロゴ画像パス(任意) + 'company_image_path', // 会社ロゴ画像パス + ]; + /** + * キャストする属性 + * + * @var array + */ + protected $casts = [ + 'seq' => 'integer', + 'management_id' => 'integer', + 'created_at' => 'datetime', + 'updated_at' => 'datetime' ]; } diff --git a/app/Services/InvSettingService.php b/app/Services/InvSettingService.php new file mode 100644 index 0000000..3170c30 --- /dev/null +++ b/app/Services/InvSettingService.php @@ -0,0 +1,74 @@ + "{$request->zip1}-{$request->zip2}", + 'tel_num' => implode('-', array_filter([$request->tel1, $request->tel2, $request->tel3])), + 'fax_num' => implode('-', array_filter([$request->fax1, $request->fax2, $request->fax3])), + ]; + } + + /** + * 新規登録処理 + */ + public function create(Request $request): void + { + $data = array_merge( + $request->only(['management_id', 't_number', 't_name', 'adrs', 'bldg']), + $this->buildContactData($request) + ); + + if ($request->hasFile('company_image')) { + $data['company_image_path'] = $request->file('company_image')->store('inv', 'public'); + } + + InvSetting::create($data); + } + + /** + * 更新処理 + */ + public function update(Request $request, InvSetting $record): void + { + $data = array_merge( + $request->only(['management_id', 't_number', 't_name', 'adrs', 'bldg']), + $this->buildContactData($request) + ); + + if ($request->hasFile('company_image')) { + if ($record->company_image_path) { + Storage::disk('public')->delete($record->company_image_path); + } + $data['company_image_path'] = $request->file('company_image')->store('inv', 'public'); + } + + $record->update($data); + } + + /** + * 削除処理(複数対応) + */ + public function delete(array $ids): void + { + $records = InvSetting::whereIn('seq', $ids)->get(); + + foreach ($records as $record) { + if ($record->company_image_path) { + Storage::disk('public')->delete($record->company_image_path); + } + $record->delete(); + } + } +} diff --git a/resources/views/admin/invsettings/_form.blade.php b/resources/views/admin/invsettings/_form.blade.php index ebfc2a0..4ff5d62 100644 --- a/resources/views/admin/invsettings/_form.blade.php +++ b/resources/views/admin/invsettings/_form.blade.php @@ -1,172 +1,188 @@ -@extends('layouts.app') -@section('title', 'インボイス設定') +