インボイス設定画面の修正
All checks were successful
Deploy main / deploy (push) Successful in 22s

This commit is contained in:
kin.rinzen 2025-10-08 17:55:23 +09:00
parent 9a6abe680f
commit 1cf94bc8aa
8 changed files with 212 additions and 56 deletions

View File

@ -52,7 +52,7 @@ class InvSettingController extends Controller
*/
public function save(Request $request)
{
// バリデーションルール
// バリデーションルール
$rules = [
't_number' => 'required|string|max:20',
't_name' => 'required|string|max:50',
@ -66,10 +66,11 @@ class InvSettingController extends Controller
'fax1' => 'nullable|digits_between:2,4',
'fax2' => 'nullable|digits_between:2,4',
'fax3' => 'nullable|digits_between:3,4',
'company_image' => 'nullable|image|mimes:png,jpg,jpeg|max:2048',
// ← ここは「画像ファイル」ではなく、hiddenに入る「パス」なので image バリデーションは不要
'company_image_path' => 'nullable|string|max:255',
];
// カスタム日本語メッセージ
// カスタム日本語メッセージ
$messages = [
't_number.required' => '適格請求書発行事業者番号を入力してください。',
't_number.max' => '適格請求書発行事業者番号は20文字以内で入力してください。',
@ -85,8 +86,6 @@ class InvSettingController extends Controller
'adrs.required' => '表示住所を入力してください。',
'adrs.max' => '表示住所は100文字以内で入力してください。',
'bldg.max' => '建物名は80文字以内で入力してください。',
'tel1.digits_between' => '電話番号1は2桁から4桁で入力してください。',
'tel2.digits_between' => '電話番号2は2桁から4桁で入力してください。',
'tel3.digits_between' => '電話番号3は3桁から4桁で入力してください。',
@ -94,25 +93,23 @@ class InvSettingController extends Controller
'fax1.digits_between' => 'FAX番号1は2桁から4桁で入力してください。',
'fax2.digits_between' => 'FAX番号2は2桁から4桁で入力してください。',
'fax3.digits_between' => 'FAX番号3は3桁から4桁で入力してください。',
'company_image.image' => '社判画像は画像ファイルを選択してください。',
'company_image.mimes' => '社判画像はpng, jpg, jpeg形式でアップロードしてください。',
'company_image.max' => '社判画像は2MB以下にしてください。',
];
// バリデーション実行(カスタムメッセージ適用)
// ▼ バリデーション実行
$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レコード運用
// 既存レコードを取得1レコード運用
$row = InvSetting::first();
// 画像処理
$imagePath = $row?->company_image_path;
// ▼ 画像パスを設定AJAX アップロード済みファイルのパスを優先)
$imagePath = $request->company_image_path;
// ▼ フォームで新たにファイルを送信した場合のみ再保存(保険的処理)
if ($request->hasFile('company_image')) {
if ($imagePath && Storage::disk('public')->exists($imagePath)) {
Storage::disk('public')->delete($imagePath);
@ -120,7 +117,7 @@ class InvSettingController extends Controller
$imagePath = $request->file('company_image')->store('inv', 'public');
}
// レコードを新規作成 or 更新
// レコードを新規作成 or 更新
if ($row) {
$row->update([
't_number' => $request->t_number,
@ -130,7 +127,7 @@ class InvSettingController extends Controller
'bldg' => $request->bldg,
'tel_num' => $tel,
'fax_num' => $fax,
'company_image_path' => $imagePath,
'company_image_path' => $imagePath, // ← hiddenの値 or 新規アップロード結果を保存
]);
} else {
InvSetting::create([
@ -148,4 +145,42 @@ class InvSettingController extends Controller
return back()->with('success', 'インボイス設定を登録しました。');
}
/**
* 社判画像アップロードAJAX用
*/
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);
}
}

View File

@ -26,10 +26,11 @@ class OperatorQueController extends Controller
$que_status = $request->input('que_status');
// 許可されたカラム名のリストDB定義に合わせて
$allowedSorts = ['que_id', 'ope_id', 'que_status', 'created_at', 'updated_at'];
$allowedSorts = ['que_id', 'ope_id', 'que_status', 'created_at', 'updated_at', 'user_id', 'park_id', 'que_class'];
if (!in_array($sort, $allowedSorts)) {
$sort = 'que_id';
}
if (!in_array($sort_type, ['asc', 'desc'])) {
@ -75,7 +76,7 @@ class OperatorQueController extends Controller
// 登録処理
OperatorQue::create($data);
return redirect()->route('operator_ques')->with('success', 'オペレーターキューを登録しました。');
return redirect()->route('operator_ques')->with('success', '登録しました。');
}
@ -100,7 +101,7 @@ class OperatorQueController extends Controller
$que->fill($data)->save();
return redirect()->route('operator_ques')->with('success', 'オペレータキューを更新しました。');
return redirect()->route('operator_ques')->with('success', '更新しました。');
}
/**

View File

@ -11,6 +11,13 @@ class InvSetting extends Model
public $timestamps = true;
protected $fillable = [
't_number', 't_name', 'zipcode', 'adrs', 'bldg', 'tel_num', 'fax_num', 'company_image_path',
't_number', // 適格事業者番号
't_name', // 事業者名
'zipcode', // 郵便番号
'adrs', // 住所
'tel_num', // 電話番号
'fax_num', // FAX番号
'company_image_path', // 会社ロゴ画像パス(任意)
];
}

View File

@ -228,3 +228,53 @@ $('#delete_edit').on('click', function (e) {
}
});
});
$(function () {
// ▼ 「アップロード」ボタンを押すと、ファイル選択ダイアログを開く
$(document).on('click', '.upload-file', function () {
$(this).siblings('input[type=file]').click();
});
// ▼ ファイル選択後、自動でAJAXアップロード処理を行う
$(document).on('change', 'input[name=company_image_file]', function () {
const file = this.files[0];
if (!file) return;
const formData = new FormData();
formData.append('company_image_file', file);
formData.append('_token', $('input[name=_token]').val());
$.ajax({
url: '/inv_setting/upload', // ルート:アップロード先
type: 'POST',
data: formData,
processData: false,
contentType: false,
success: function (res) {
// 成功時ファイル名を表示・hidden項目に反映
$('input[name=company_image_path]').val(res.path);
$('.filename').text(res.file_name);
// プレビューを更新
const previewHtml = `<div class="mt-2">
<img src="/storage/${res.path}" alt="社判画像"
style="max-height:100px; border:1px solid #ccc; padding:3px;">
</div>`;
$('.uploaded-file').after(previewHtml);
alert('アップロードが完了しました。');
},
error: function (xhr) {
alert('アップロードに失敗しました。');
}
});
});
// ▼ 削除ボタン(✕)をクリックしたらファイル情報をクリア
$(document).on('click', '.delete-file', function () {
$('input[name=company_image_path]').val('');
$('.filename').text('');
$('img[alt="社判画像"]').remove();
});
});

View File

@ -87,38 +87,79 @@
</div>
</div>
{{-- 表示FAX番号 --}}
<div class="form-group row">
<label class="col-sm-3 col-form-label">表示FAX番号</label>
<div class="col-sm-9 d-flex">
<input name="fax1" class="form-control" style="max-width:100px;" value="{{ old('fax1', $fax1) }}">
<span class="mx-2">-</span>
<input name="fax2" class="form-control" style="max-width:100px;" value="{{ old('fax2', $fax2) }}">
<span class="mx-2">-</span>
<input name="fax3" class="form-control" style="max-width:100px;" value="{{ old('fax3', $fax3) }}">
</div>
</div>
{{-- 表示FAX番号 --}}
<div class="form-group row">
<label class="col-sm-3 col-form-label">表示FAX番号</label>
<div class="col-sm-9 d-flex">
<input name="fax1" class="form-control" style="max-width:100px;" value="{{ old('fax1', $fax1) }}">
<span class="mx-2">-</span>
<input name="fax2" class="form-control" style="max-width:100px;" value="{{ old('fax2', $fax2) }}">
<span class="mx-2">-</span>
<input name="fax3" class="form-control" style="max-width:100px;" value="{{ old('fax3', $fax3) }}">
</div>
</div>
{{-- 社判画像アップロード欄 --}}
<div class="row">
{{-- 左側ラベル --}}
<div class="form-group col-3">
<label>社判画像</label>
</div>
{{-- 右側アップロード欄 --}}
<div class="form-group col-9">
<div class="input-group align-items-center">
{{-- 非表示のファイル選択ボタン --}}
<input type="file" class="d-none" name="company_image_file" accept=".jpg,.png,.gif" />
{{-- 表示ボタン(クリックでファイル選択を開く) --}}
<a href="javascript:void(0)" class="btn btn-default upload-file">
アップロード
</a>
{{-- アップロード済みファイル表示エリア --}}
<div class="uploaded-file pl-2">
{{-- DB保存用の hidden input --}}
<input type="hidden" name="company_image_path"
value="{{ old('company_image_path', $row->company_image_path ?? '') }}">
{{-- ファイル名表示 --}}
<span class="filename">
{{ old('company_image_path', basename($row->company_image_path ?? '')) }}
</span>
{{-- 削除ボタン(画像がある時のみ表示) --}}
@if(!empty($row->company_image_path))
<a href="javascript:void(0)" class="text-dark delete-file ml-1">
<i class="fa fa-times" aria-hidden="true"></i>
</a>
@endif
</div>
</div>
{{-- プレビュー画像(存在する場合のみ表示) --}}
@if(!empty($row->company_image_path))
<div class="mt-2">
<img src="{{ asset('storage/'.$row->company_image_path) }}"
alt="社判画像"
style="max-height:100px; border:1px solid #ccc; padding:3px;">
</div>
@endif
</div>
</div>
{{-- 社判画像アップロード欄 --}}
{{-- 社判画像 --}}
<div class="form-group row">
<label class="col-sm-3 col-form-label">社判画像</label>
<div class="col-sm-9">
<input type="file" name="company_image" class="form-control-file">
@if(!empty($row->company_image_path))
<div class="mt-2">
<a href="{{ asset('storage/'.$row->company_image_path) }}" target="_blank">現在の画像を表示</a>
</div>
@endif
</div>
</div>
{{-- 登録ボタン --}}
<div class="form-group row">
<div class="col-sm-3">
<button type="submit" class="btn btn-success px-3">登録</button>
</div>
<button type="button" id="register" class="btn btn-lg btn-success mr-2 register">
{{ __('登録') }}
</button>
</div>
</div>
</form>
</form>
</div>
</section>
@endsection

View File

@ -48,20 +48,28 @@
{{-- 利用者名 --}}
<div class="form-group col-3">
<label>{{ __('validation.attributes.user_name') }}</label>
<label class="required">{{ __('validation.attributes.user_name') }}</label>
</div>
<div class="form-group col-9">
<select class="form-control form-control-lg" id="select_user" name="user_id">
<select class="form-control form-control-lg"
id="select_user"
name="user_id"
@if($isEdit) disabled @endif>
<option value="">{{ __('validation.attributes.user_name') }}</option>
@foreach($users as $item)
<option value="{{ $item->user_seq }}"
mobile="{{ $item->user_mobile }}"
homePhone="{{ $item->user_homephone }}"
<option value="{{ $item->user_seq }}"
mobile="{{ $item->user_mobile }}"
homePhone="{{ $item->user_homephone }}"
@if($item->user_seq == $user_id) selected @endif>
{{ $item->user_name }}
</option>
@endforeach
</select>
{{-- 編集時でも値を保持するための hidden --}}
@if($isEdit)
<input type="hidden" name="user_id" value="{{ $user_id }}">
@endif
</div>
{{-- 携帯電話番号 --}}
@ -105,21 +113,31 @@
value="{{ old('contract_id', $contract_id ?? '') }}"
class="form-control form-control-lg"
placeholder="{{ __('validation.attributes.contract_id') }}"
readonly>
@if($isEdit) readonly @endif>
</div>
</div>
{{-- 駐輪場 --}}
<div class="form-group col-3">
<label>{{ __('validation.attributes.park_name') }}</label>
<label class="required">{{ __('validation.attributes.park_name') }}</label>
</div>
<div class="form-group col-9">
<select class="form-control form-control-lg" name="park_id">
<select class="form-control form-control-lg"
name="park_id"
@if($isEdit) disabled @endif>
<option value="">{{ __('validation.attributes.park_name') }}</option>
@foreach($parks as $key => $item)
<option value="{{ $key }}" @if($key == $park_id) selected @endif>{{ $item }}</option>
<option value="{{ $key }}" @if($key == $park_id) selected @endif>
{{ $item }}
</option>
@endforeach
</select>
{{-- 編集時でも値を保持するための hidden --}}
@if($isEdit)
<input type="hidden" name="park_id" value="{{ $park_id }}">
@endif
</div>
{{-- キュー種別 --}}

View File

@ -103,9 +103,10 @@
<th class="sorting {{ ($sort=='user_id') ? ($sort_type=='asc'?'sorting_asc':'sorting_desc') : '' }}" sort="user_id"><span>利用者名</span></th>
<th><span>携帯電話番号</span></th>
<th><span>自宅電話番号</span></th>
<th><span>定期契約ID</span></th>
<th class="sorting {{ ($sort=='park_name') ? ($sort_type=='asc'?'sorting_asc':'sorting_desc') : '' }}" sort="park_name"><span>駐輪場</span></th>
<th class="sorting {{ ($sort=='que_class') ? ($sort_type=='asc'?'sorting_asc':'sorting_desc') : '' }}" sort="que_class"><span>キュー種別</span></th>
<th class="sorting {{ ($sort=='que_comment') ? ($sort_type=='asc'?'sorting_asc':'sorting_desc') : '' }}" sort="que_comment"><span>キューコメント</span></th>
<th><span>キューコメント</span></th>
<th class="sorting {{ ($sort=='que_status') ? ($sort_type=='asc'?'sorting_asc':'sorting_desc') : '' }}" sort="que_status"><span>キューステータス</span></th>
<th><span>キューステータスコメント</span></th>
<th><span>処理リンク</span></th>
@ -124,11 +125,13 @@
<td class="sm-item text-left align-middle">{{ !empty($item->getUser()) ? $item->getUser()->user_name : '' }}</td>
<td class="sm-item text-left align-middle">{{ !empty($item->getUser()) ? $item->getUser()->user_mobile : '' }}</td>
<td class="sm-item text-left align-middle">{{ !empty($item->getUser()) ? $item->getUser()->user_homephone : '' }}</td>
<td class="sm-item text-left align-middle">{{ $item->contract_id }}</td>
<td class="sm-item text-left align-middle">{{ !empty($item->getPark()) ? $item->getPark()->park_name : '' }}</td>
<td class="sm-item text-left align-middle">{{ $item->getQueClassLabel() }}</td>
<td class="sm-item text-left align-middle">{{ $item->que_comment }}</td>
<td class="sm-item text-left align-middle">{{ $item->getQueStatusLabel() }}</td>
<td class="sm-item text-left align-middle">{{ $item->que_status_comment }}</td>
<td class="sm-item text-left align-middle"></td>
</tr>
@endforeach

View File

@ -482,6 +482,7 @@ Route::middleware('auth')->group(function () {
// [東京都|〇〇駐輪場] インボイス設定マスタ
Route::match(['get', 'post'], '/inv_settings', [InvSettingController::class, 'form'])->name('inv_settings');
Route::post('/inv_settings/save', [InvSettingController::class, 'save'])->name('inv_settings_save');
Route::post('/inv_settings/upload', [InvSettingController::class, 'upload'])->name('inv_settings_upload');
// [東京都|〇〇駐輪場] ゾーンマスタ
Route::match(['get', 'post'], '/zones', [ZoneController::class, 'list'])->name('zones');