so-manager-dev.com/app/Http/Controllers/ReceiptController.php

251 lines
12 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\DB;
use App\Http\Controllers\Controller;
use Mpdf\Mpdf;
use Illuminate\Contracts\View\View;
use function base_path;
class ReceiptController extends Controller
{
/**
* 領収書宛名入力画面を表示する。
* 契約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');
if (!$user_id) {
// 未認証アクセスセッション切れを追跡するため、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');
$management = session('management');
$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', [
'user_name' => $user_name,
'contract_id' => $contract_id,
'management_code' => $management_code,
]);
}
/**
* 領収書の宛名入力内容を確認し、保存する。
*
* @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');
if (!$user_id) {
// 未認証アクセスセッション切れを追跡するため、INFOログで記録
\Log::info("[INFO] " . now()->format('Y-m-d H:i:s') . " 未認証ユーザーによるアクセス(セッション切れ): 領収書発行");
return redirect('/login');
}
// 既存レコードがある場合、再発行を促す
$exists = DB::table('inv_publish')->where('contract_id', $contract_id)->exists();
if ($exists) {
// エラー時はinput画面に戻し、メッセージ表示
\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')]);
}
$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)) {
return redirect()->back()->withInput()->withErrors(['contract_id' => str_replace('{0}', '宛名', __('messages.SYSCOMMON_E000046'))]);
}
// 文字数チェック
if (mb_strlen($receipt_name) > 30) {
return redirect()->back()->withInput()->withErrors(['contract_id' => str_replace(['{0}', '{1}'], ['宛名', '30'], __('messages.SYSCOMMON_E000011'))]);
}
// 敬称選択チェック
if (empty($keisho)) {
return redirect()->back()->withInput()->withErrors(['contract_id' => str_replace('{0}', '敬称', __('messages.SYSCOMMON_E000047'))]);
}
// 領収書再発行を可能にするため、領収書発行履歴登録
$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' => 0,
'created_at' => $now,
'updated_at' => null,
]);
// 領収書発行履歴の登録成功を記録
\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;
return $this->download($management->management_code, $contract_id, $is_reissue);
}
/**
* 領収書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 {
$user_id = session('user_id');
if (!$user_id) {
// 未認証アクセスセッション切れを追跡するため、INFOログで記録
\Log::info("[INFO] " . now()->format('Y-m-d H:i:s') . " 未認証ユーザーによるアクセス(セッション切れ): 領収書ダウンロード");
return redirect('/login');
}
$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();
// 社員側の社判画像を参照するため、シンボリックリンクを作成 (company_image_path が存在する場合のみ)
if (!empty($inv_setting->company_image_path)) {
$krgmStoragePath = config('app.krgm_storage_path');
$linkPath = public_path('other-storage');
if (!file_exists($krgmStoragePath)) {
\Log::error("[ERROR] " . now()->format('Y-m-d H:i:s') . " 社判画像保存フォルダ発見不可: " . $krgmStoragePath . ", user_id=" . $user_id . ", contract_id=" . $contract_id);
throw new \Exception();
}
if (!file_exists($linkPath)) {
$result = symlink($krgmStoragePath, $linkPath);
\Log::info("[INFO] " . now()->format('Y-m-d H:i:s') . " 社判画像参照リンク作成。結果: " . ($result ? '成功' : '失敗') . ", user_id=" . $user_id . ", contract_id=" . $contract_id);
if (!$result) {
\Log::error("[ERROR] " . now()->format('Y-m-d H:i:s') . " 社判画像参照リンク作成失敗。user_id=" . $user_id . ", contract_id=" . $contract_id);
throw new \Exception();
}
}
}
// ダウンロード回数をカウントアップ
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, '領収書ダウンロードに失敗しました。');
}
}
}