インボイス設定作成
All checks were successful
Deploy main / deploy (push) Successful in 23s

This commit is contained in:
shichou.u 2026-02-05 21:35:36 +09:00
parent 57199fe92b
commit 5e2f92f92a
9 changed files with 647 additions and 352 deletions

View File

@ -3,224 +3,148 @@
namespace App\Http\Controllers\Admin; namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Http\Requests\InvSettingRequest;
use App\Models\InvSetting; use App\Models\InvSetting;
use App\Models\Management;
use App\Services\InvSettingService;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
class InvSettingController extends Controller class InvSettingController extends Controller
{ {
/** private InvSettingService $service;
* 登録フォーム表示
*/ public function __construct(InvSettingService $service)
public function form(Request $request)
{ {
$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) { $sort = in_array($request->sort, $allowed, true)
// 郵便番号(そのままハイフン分割) ? $request->sort
if (!empty($row->zipcode) && str_contains($row->zipcode, '-')) { : 'management_id';
[$zip1, $zip2] = explode('-', $row->zipcode);
}
// 電話番号:数字以外を除去 → 2桁+4桁+4桁 に分割 $sortType = in_array($request->sort_type, ['asc', 'desc'], true)
if (!empty($row->tel_num)) { ? $request->sort_type
$tel = preg_replace('/\D/', '', $row->tel_num); // 数字以外を除去 : 'asc';
$tel1 = substr($tel, 0, 2);
$tel2 = substr($tel, 2, 4);
$tel3 = substr($tel, 6, 4);
}
// FAX番号同じく 2桁+4桁+4桁 $list = InvSetting::orderBy($sort, $sortType)
if (!empty($row->fax_num)) { ->paginate(20)
$fax = preg_replace('/\D/', '', $row->fax_num); ->appends($request->query());
$fax1 = substr($fax, 0, 2);
$fax2 = substr($fax, 2, 4); //return view('admin.invsettings.list', compact('list', 'sort', 'sortType'));
$fax3 = substr($fax, 6, 4); 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( $managementList = Management::pluck('management_name', 'management_id');
'row', 'zip1', 'zip2', 'tel1', 'tel2', 'tel3', 'fax1', 'fax2', 'fax3'
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)
{ {
// ▼ バリデーションルール $this->service->delete((array) $request->id);
$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',
];
// ▼ カスタム日本語メッセージ return redirect()
$messages = [ ->route('inv_settings')
't_number.required' => '適格請求書発行事業者番号を入力してください。', ->with('success', '削除しました。');
'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', 'インボイス設定を登録しました。');
} }
/** /**
* 社判画像アップロード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) 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([ $file = $request->file('company_image_file');
'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以下にしてください。',
]);
// ファイルオブジェクト取得 // storage/app/public/inv に保存
$file = $request->file('company_image_file'); $path = $file->store('inv', 'public');
// 元のファイル名company_logo.png // AJAX 用レスポンス
$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,
]);
}
// ファイル未選択時
return response()->json([ return response()->json([
'error' => 'ファイルが選択されていません。' 'path' => $path, // DB保存用パス
], 400); 'file_name' => $file->getClientOriginalName(), // 表示用ファイル名
]);
} }
} }

View File

@ -0,0 +1,81 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class InvSettingRequest extends FormRequest
{
/**
* 認可判定
*/
public function authorize(): bool
{
return true;
}
/**
* バリデーションルール
*/
public function rules(): array
{
return [
'management_id' => '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' => '指定フォーマットT13桁に一致しません。',
'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桁を入力してください。',
];
}
}

View File

@ -11,7 +11,8 @@ class InvSetting extends Model
public $timestamps = true; public $timestamps = true;
protected $fillable = [ protected $fillable = [
'seq', // シーケンス
'management_id', // 運営元ID
't_number', // 適格事業者番号 't_number', // 適格事業者番号
't_name', // 事業者名 't_name', // 事業者名
'zipcode', // 郵便番号 'zipcode', // 郵便番号
@ -19,7 +20,18 @@ class InvSetting extends Model
'bldg', // 建物名 'bldg', // 建物名
'tel_num', // 電話番号 'tel_num', // 電話番号
'fax_num', // FAX番号 'fax_num', // FAX番号
'company_image_path', // 会社ロゴ画像パス(任意) 'company_image_path', // 会社ロゴ画像パス
];
/**
* キャストする属性
*
* @var array
*/
protected $casts = [
'seq' => 'integer',
'management_id' => 'integer',
'created_at' => 'datetime',
'updated_at' => 'datetime'
]; ];
} }

View File

@ -0,0 +1,74 @@
<?php
namespace App\Services;
use App\Models\InvSetting;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
class InvSettingService
{
/**
* 郵便番号・電話番号・FAX番号整形
*/
public function buildContactData(Request $request): array
{
return [
'zipcode' => "{$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();
}
}
}

View File

@ -1,172 +1,188 @@
@extends('layouts.app') <div class="card">
@section('title', 'インボイス設定') <div class="card-body">
@if($errors->any())
<div class="alert alert-danger alert-dismissible">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
<h4><i class="icon fa fa-ban"></i> {{ __('入力内容に不備があります') }}:</h4>
<ul>
@foreach($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
@section('content') {{-- 運営元ID --}}
<div class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="col-lg-6"><h1 class="m-0 text-dark">インボイス設定</h1></div>
</div>
</div>
</div>
<section class="content">
<div class="container-fluid">
@if(Session::has('success'))
<div class="alert alert-success alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
{{ Session::get('success') }}
</div>
@endif
@if($errors->any())
<div class="alert alert-danger alert-dismissible">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
<h4><i class="icon fa fa-ban"></i> {{ __('入力内容に不備があります') }}:</h4>
<ul>
@foreach($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<form method="post" action="{{ route('inv_settings_save') }}" enctype="multipart/form-data">
@csrf
{{-- 適格請求書発行事業者番号 --}}
<div class="form-group row">
<label class="col-sm-3 col-form-label">適格請求書発行事業者番号<span class="text-danger">*</span></label>
<div class="form-group col-4">
<input type="text" name="t_number" class="form-control"
value="{{ old('t_number', $row->t_number ?? '') }}">
</div>
</div>
{{-- 適格事業者名 --}}
<div class="form-group row">
<label class="col-sm-3 col-form-label">適格事業者名<span class="text-danger">*</span></label>
<div class="form-group col-4">
<input type="text" name="t_name" class="form-control"
value="{{ old('t_name', $row->t_name ?? '') }}">
</div>
</div>
{{-- 郵便番号 --}}
<div class="form-group row">
<label class="col-sm-3 col-form-label">郵便番号</label>
<div class="col-sm-9 d-flex">
<input type="text" name="zip1" class="form-control" style="max-width:120px;"
value="{{ old('zip1', $zip1) }}">
<span class="mx-2">-</span>
<input type="text" name="zip2" class="form-control" style="max-width:140px;"
value="{{ old('zip2', $zip2) }}">
</div>
</div>
{{-- 表示住所 --}}
<div class="form-group row">
<label class="col-sm-3 col-form-label">表示住所<span class="text-danger">*</span></label>
<div class="form-group col-4">
<input type="text" name="adrs" class="form-control"
value="{{ old('adrs', $row->adrs ?? '') }}">
</div>
</div>
{{-- 建物名 --}}
<div class="form-group row">
<label class="col-sm-3 col-form-label">建物名</label>
<div class="form-group col-4">
<input type="text" name="bldg" class="form-control"
value="{{ old('bldg', $row->bldg ?? '') }}">
</div>
</div>
{{-- 表示電話番号 --}}
<div class="form-group row">
<label class="col-sm-3 col-form-label">表示電話番号</label>
<div class="col-sm-9 d-flex">
<input name="tel1" class="form-control" style="max-width:100px;" value="{{ old('tel1', $tel1) }}">
<span class="mx-2">-</span>
<input name="tel2" class="form-control" style="max-width:100px;" value="{{ old('tel2', $tel2) }}">
<span class="mx-2">-</span>
<input name="tel3" class="form-control" style="max-width:100px;" value="{{ old('tel3', $tel3) }}">
</div>
</div>
{{-- 表示FAX番号 --}}
<div class="form-group row"> <div class="form-group row">
<label class="col-sm-3 col-form-label">表示FAX番号</label> <label class="col-sm-3 col-form-label">運営元<span class="text-danger">*</span></label>
<div class="col-sm-9 d-flex"> <div class="form-group col-4">
<input name="fax1" class="form-control" style="max-width:100px;" value="{{ old('fax1', $fax1) }}"> <select name="management_id" class="form-control" required>
<span class="mx-2">-</span> <option value="">-- 選択してください --</option>
<input name="fax2" class="form-control" style="max-width:100px;" value="{{ old('fax2', $fax2) }}"> @foreach($managementList as $key => $item)
<span class="mx-2">-</span> <option value="{{ $key }}" @if($key == old('management_id', $row->management_id ?? '')) selected @endif>
<input name="fax3" class="form-control" style="max-width:100px;" value="{{ old('fax3', $fax3) }}"> {{ $key }} - {{ $item }}
</option>
@endforeach
</select>
</div> </div>
</div> </div>
{{-- 社判画像アップロード欄 --}} {{-- 適格請求書発行事業者番号 --}}
<div class="row"> <div class="form-group row">
{{-- 左側ラベル --}} <label class="col-sm-3 col-form-label">適格請求書発行事業者番号<span class="text-danger">*</span></label>
<div class="form-group col-3"> <div class="form-group col-4">
<label>社判画像</label> <input type="text" name="t_number" class="form-control"
value="{{ old('t_number', $row->t_number ?? '') }}"
maxlength="14">
</div>
</div>
{{-- 適格事業者名 --}}
<div class="form-group row">
<label class="col-sm-3 col-form-label">適格事業者名<span class="text-danger">*</span></label>
<div class="form-group col-4">
<input type="text" name="t_name" class="form-control"
value="{{ old('t_name', $row->t_name ?? '') }}"
maxlength="50">
</div>
</div>
{{-- 郵便番号 --}}
<div class="form-group row">
<label class="col-sm-3 col-form-label">郵便番号</label>
<div class="col-sm-9 d-flex">
<input type="text" name="zip1" class="form-control" style="max-width:120px;"
value="{{ old('zip1', $zip1 ?? '') }}"
maxlength="3">
<span class="mx-2">-</span>
<input type="text" name="zip2" class="form-control" style="max-width:140px;"
value="{{ old('zip2', $zip2 ?? '') }}"
maxlength="4">
</div>
</div>
{{-- 表示住所 --}}
<div class="form-group row">
<label class="col-sm-3 col-form-label">表示住所<span class="text-danger">*</span></label>
<div class="form-group col-4">
<input type="text" name="adrs" class="form-control"
value="{{ old('adrs', $row->adrs ?? '') }}"
maxlength="100">
</div>
</div>
{{-- 建物名 --}}
<div class="form-group row">
<label class="col-sm-3 col-form-label">建物名</label>
<div class="form-group col-4">
<input type="text" name="bldg" class="form-control"
value="{{ old('bldg', $row->bldg ?? '') }}">
</div>
</div>
{{-- 表示電話番号 --}}
<div class="form-group row">
<label class="col-sm-3 col-form-label">表示電話番号</label>
<div class="col-sm-9 d-flex">
<input name="tel1" class="form-control" style="max-width:100px;" value="{{ old('tel1', $tel1 ?? '') }}" maxlength="4">
<span class="mx-2">-</span>
<input name="tel2" class="form-control" style="max-width:100px;" value="{{ old('tel2', $tel2 ?? '') }}" maxlength="4">
<span class="mx-2">-</span>
<input name="tel3" class="form-control" style="max-width:100px;" value="{{ old('tel3', $tel3 ?? '') }}" maxlength="4">
</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 ?? '') }}" maxlength="4">
<span class="mx-2">-</span>
<input name="fax2" class="form-control" style="max-width:100px;" value="{{ old('fax2', $fax2 ?? '') }}" maxlength="4">
<span class="mx-2">-</span>
<input name="fax3" class="form-control" style="max-width:100px;" value="{{ old('fax3', $fax3 ?? '') }}" maxlength="4">
</div>
</div> </div>
{{-- 右側アップロード欄 --}} {{-- 社判画像アップロード欄 --}}
<div class="form-group col-9"> <div class="row">
<div class="input-group align-items-center"> <div class="form-group col-3">
<label>社判画像</label>
{{-- 非表示のファイル選択ボタン --}}
<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> </div>
{{-- プレビュー画像(存在する場合のみ表示) --}} <div class="form-group col-9">
@if(!empty($row->company_image_path)) <div class="input-group align-items-center">
<div class="mt-2">
<img src="{{ asset('storage/'.$row->company_image_path) }}" {{-- 非表示ファイル入力 --}}
alt="社判画像" <input type="file"
style="max-height:100px; border:1px solid #ccc; padding:3px;"> class="d-none"
name="company_image_file"
accept=".jpg,.jpeg,.png,.gif">
{{-- アップロードボタン --}}
<a href="javascript:void(0)" class="btn btn-default upload-file">
アップロード
</a>
{{-- ファイル情報表示 --}}
<div class="uploaded-file pl-2 d-flex align-items-center">
{{-- DB保存用 --}}
<input type="hidden"
name="company_image_path"
value="{{ old('company_image_path', $row->company_image_path ?? '') }}">
{{-- ファイル名 --}}
<span class="filename mr-1">
{{ old('company_image_path', isset($row->company_image_path) ? basename($row->company_image_path) : '') }}
</span>
{{-- 削除ボタン --}}
<a href="javascript:void(0)"
class="text-dark delete-file"
style="{{ empty($row->company_image_path) ? 'display:none;' : '' }}">
<i class="fa fa-times"></i>
</a>
</div>
</div> </div>
@endif
</div> {{-- プレビュー --}}
<div class="image-preview mt-2">
@if(!empty($row->company_image_path))
<img src="{{ asset('storage/'.$row->company_image_path) }}"
alt="社判画像"
style="max-height:100px;border:1px solid #ccc;padding:3px;">
@endif
</div> </div>
{{-- 社判画像アップロード欄 --}} </div>
</div>
{{-- 社判画像アップロード欄 --}}
{{-- 登録ボタン --}} {{-- 下部ボタン --}}
<div class="form-group row"> <div class="col-3"></div>
<div class="col-sm-3"> <div class="form-group col-9 d-flex align-items-center gap-2 mt-4">
<button type="button" id="register" class="btn btn-lg btn-success mr-2 register"> {{-- 登録ボタン --}}
{{ __('登録') }} @if($isEdit)
</button> <button type="submit" id="register_edit" class="btn btn-lg btn-success mr-2">
</div> {{ __('登録') }}
</div> </button>
</form> @else
</div> <button type="submit" id="register" class="btn btn-lg btn-success mr-2 register">
</section> {{ __('登録') }}
@endsection </button>
@endif
{{-- 削除ボタン(編集時のみ表示) --}}
@if(isset($isEdit) && $isEdit && !empty($record->seq))
<button type="button" id="delete_edit" class="btn btn-lg btn-danger mr-2">
{{ __('削除') }}
</button>
@endif
{{-- 返回ボタン --}}
<a href="{{ route('inv_settings') }}" class="btn btn-lg btn-default">
{{ __('戻る') }}
</a>
</div>
{{-- 下部ボタン --}}
</div>
</div>

View File

@ -0,0 +1,43 @@
@extends('layouts.app')
@section('title', 'インボイス設定 - 新規登録')
@push('head')
<meta name="inv-upload-url" content="{{ url('inv_settings/upload') }}">
@endpush
<!-- @push('scripts')
<script src="{{ asset('js/inv-settings.js') }}"></script>
@endpush -->
@section('content')
<div class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="col-sm-6">
<h1 class="m-0 text-dark">新規</h1>
</div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right text-sm">
<li class="breadcrumb-item"><a href="{{ route('home') }}">ホーム</a></li>
<li class="breadcrumb-item"><a href="{{ route('inv_settings') }}">インボイス設定</a></li>
<li class="breadcrumb-item active">新規</li>
</ol>
</div>
</div>
</div>
</div>
<section class="content">
<div class="container-fluid">
{{-- 新規登録フォーム --}}
<form id="form_add" action="{{ route('inv_settings_add') }}" method="POST">
@csrf
@include('admin.invsettings._form', [
'isEdit' => false,
'record' => null,
'managementList' => $managementList ?? []
])
</form>
</div>
</section>
@endsection

View File

@ -0,0 +1,44 @@
@extends('layouts.app')
@section('title', 'インボイス設定 - 編集')
@section('content')
<div class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="col-sm-6">
<h1 class="m-0 text-dark">編集</h1>
</div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right text-sm">
<li class="breadcrumb-item"><a href="{{ route('home') }}">ホーム</a></li>
<li class="breadcrumb-item"><a href="{{ route('inv_settings') }}">インボイス設定</a></li>
<li class="breadcrumb-item active">編集</li>
</ol>
</div>
</div>
</div>
</div>
<section class="content">
<div class="container-fluid">
{{-- 編集フォーム --}}
<form id="form_edit" action="{{ route('inv_settings_edit', ['id' => $record->seq]) }}" method="POST">
@csrf
@include('admin.invsettings._form', [
'isEdit' => true,
'record' => $record,
'row' => $record,
'managementList' => $managementList ?? []
])
</form>
{{-- 削除フォーム --}}
<form id="form_delete" action="{{ route('inv_settings_delete') }}" method="POST" style="display:none;">
@csrf
<input type="hidden" name="id" value="{{ $record->seq }}">
</form>
</div>
</section>
@endsection

View File

@ -0,0 +1,97 @@
@extends('layouts.app')
@section('title', 'インボイス設定')
@section('content')
@if (session('success'))
<div class="alert alert-success">
{{ session('success') }}
</div>
@endif
@if (session('error'))
<div class="alert alert-danger">
{{ session('error') }}
</div>
@endif
<!-- Content Header -->
<div class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="col-lg-6">
<h1 class="m-0 text-dark">インボイス設定</h1>
</div>
<div class="col-lg-6">
<ol class="breadcrumb float-sm-right text-sm">
<li class="breadcrumb-item"><a href="{{ route('home') }}">ホーム</a></li>
<li class="breadcrumb-item active">インボイス設定</li>
</ol>
</div>
</div>
</div>
</div>
<!-- Main Content -->
<section class="content">
<div class="container-fluid">
<!-- ボタン + ページネーション -->
<div class="container-fluid mb20">
<button type="button" class="btn btn-sm btn-default mr10" onclick="location.href='{{ route('inv_settings_add') }}'">新規</button>
<button type="button" class="btn btn-sm btn-default mr10" id="delete">削除</button>
<div class="d-flex justify-content-end">
{{ $list->appends(['sort' => $sort ?? '', 'sort_type' => $sort_type ?? ''])->links('pagination') }}
</div>
</div>
<form id="list-form" action="{{ route('inv_settings') }}" method="GET" class="d-none"> <input type="hidden" name="sort" value="{{ $sort ?? '' }}"> <input type="hidden" name="sort_type" value="{{ $sort_type ?? '' }}"> </form>
<!-- 単一テーブル構成 -->
<div class="col-lg-12 mb20">
<div class="table-responsive">
<form method="post" action="{{ route('inv_settings_delete') }}" id="form_delete">
@csrf
<table id="inv-settings-list" class="table table-bordered dataTable text-nowrap">
<thead class="thead-light">
<tr>
<th style="width:140px; border-left:1px solid #dcdcdc;" class="text-left">
<input type="checkbox" onclick="$('input[name*=\'pk\']').prop('checked', this.checked);">
</th>
<th class="sorting {{ ($sort=='management_id') ? ($sort_type=='asc'?'sorting_asc':'sorting_desc') : '' }}" sort="management_id"><span>運営元ID</span></th>
<th class="sorting {{ ($sort=='t_number') ? ($sort_type=='asc'?'sorting_asc':'sorting_desc') : '' }}" sort="t_number"><span>適格請求書発行事業者番号</span></th>
<th class="sorting {{ ($sort=='t_name') ? ($sort_type=='asc'?'sorting_asc':'sorting_desc') : '' }}" sort="t_name"><span>適格事業者名</span></th>
<th class="sorting {{ ($sort=='zipcode') ? ($sort_type=='asc'?'sorting_asc':'sorting_desc') : '' }}" sort="zipcode"><span>郵便番号</span></th>
<th class="sorting {{ ($sort=='adrs') ? ($sort_type=='asc'?'sorting_asc':'sorting_desc') : '' }}" sort="adrs"><span>表示住所</span></th>
<th class="sorting {{ ($sort=='bldg') ? ($sort_type=='asc'?'sorting_asc':'sorting_desc') : '' }}" sort="bldg"><span>建物名</span></th>
<th class="sorting {{ ($sort=='tel_num') ? ($sort_type=='asc'?'sorting_asc':'sorting_desc') : '' }}" sort="tel_num"><span>表示電話番号</span></th>
<th class="sorting {{ ($sort=='fax_num') ? ($sort_type=='asc'?'sorting_asc':'sorting_desc') : '' }}" sort="fax_num"><span>表示FAX番号</span></th>
</tr>
</thead>
<tbody class="bg-white">
@foreach($list as $item)
<tr>
<td style="background-color:#faebd7;">
<div class="d-flex align-items-center">
<input type="checkbox" class="m-0 checkbox" name="id[]" value="{{ $item->seq }}">
<a href="{{ route('inv_settings_edit', ['id' => $item->seq]) }}"
class="btn btn-sm btn-outline-primary ml10">編集</a>
</div>
</td>
<td class="sm-item text-left align-middle">{{ $item->management_id }}</td>
<td class="sm-item text-left align-middle">{{ $item->t_number }}</td>
<td class="sm-item text-left align-middle">{{ $item->t_name }}</td>
<td class="sm-item text-center align-middle">{{ $item->zipcode }}</td>
<td class="sm-item text-left align-middle">{{ $item->adrs }}</td>
<td class="sm-item text-left align-middle">{{ $item->bldg }}</td>
<td class="sm-item text-center align-middle">{{ $item->tel_num }}</td>
<td class="sm-item text-center align-middle">{{ $item->fax_num }}</td>
</tr>
@endforeach
</tbody>
</table>
</form>
</div>
</div>
<!-- 単一テーブル構成ここまで -->
</div>
</section>
@endsection

View File

@ -540,8 +540,12 @@ Route::middleware('auth')->group(function () {
Route::get('/mail_templates/export', [MailTemplateController::class, 'export'])->name('mail_templates_export'); Route::get('/mail_templates/export', [MailTemplateController::class, 'export'])->name('mail_templates_export');
// [東京都|〇〇駐輪場] インボイス設定マスタ // [東京都|〇〇駐輪場] インボイス設定マスタ
Route::match(['get', 'post'], '/inv_settings', [InvSettingController::class, 'form'])->name('inv_settings'); Route::get('/inv_settings', [InvSettingController::class, 'list'])->name('inv_settings');
Route::post('/inv_settings/save', [InvSettingController::class, 'save'])->name('inv_settings_save'); Route::match(['get', 'post'], '/inv_settings/add', [InvSettingController::class, 'add'])->name('inv_settings_add');
Route::match(['get', 'post'], '/inv_settings/edit/{id}', [InvSettingController::class, 'edit'])
->where(['id' => '[0-9]+'])
->name('inv_settings_edit');
Route::post('/inv_settings/delete', [InvSettingController::class, 'delete'])->name('inv_settings_delete');
Route::post('/inv_settings/upload', [InvSettingController::class, 'upload'])->name('inv_settings_upload'); Route::post('/inv_settings/upload', [InvSettingController::class, 'upload'])->name('inv_settings_upload');
// [東京都|〇〇駐輪場] ゾーンマスタ // [東京都|〇〇駐輪場] ゾーンマスタ