Compare commits
208 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e41309ca4c | ||
|
|
c5c35470dc | ||
|
|
06756440ce | ||
|
|
b709765361 | ||
|
|
1d7e3941b5 | ||
|
|
a17960f61c | ||
| ae331335f8 | |||
| aa91bc6617 | |||
| 1aa942030c | |||
| 0498760d46 | |||
|
|
5b6b4faa14 | ||
| 9855fc9b6d | |||
| 5ca8e82ac9 | |||
| dbc059ef2c | |||
| a484231216 | |||
| 8c0413157a | |||
| f4d66e3f20 | |||
| bad4743bfb | |||
| dc43ae8a7e | |||
| 6e33464cb5 | |||
| 45acbff97f | |||
| cb0c5c8cc5 | |||
| f0b8c865c6 | |||
| 75e5378622 | |||
| 122dfa569a | |||
| 9266a48831 | |||
| d942fed62d | |||
| 17cbd3c065 | |||
| 7be5e66430 | |||
| 9c997e8892 | |||
| 1bf8762f54 | |||
| e63613f337 | |||
| 5c627a80e0 | |||
| eeacdd7c67 | |||
| 6d15caaac7 | |||
| a2d0ea6985 | |||
| 8e59aadb8e | |||
| 4dda16c494 | |||
| 8848cbdbbc | |||
| 452f786ce6 | |||
| c2aa59e503 | |||
| 4f92a31e53 | |||
| d8c7bd1d0b | |||
| fe6b84d11f | |||
| 41feaf6f98 | |||
| 6b08ccb5bb | |||
| fa9efb9d7a | |||
| f79d5e0f3a | |||
| edfa64a9dc | |||
| 71fca69a3a | |||
| 260b52989f | |||
| fb9568604e | |||
| 9a3d77dfe6 | |||
| 0545bb550c | |||
| 70a1c7a67d | |||
| 727477af09 | |||
| b0b9a159c9 | |||
| d57a91e5df | |||
| 3572065de7 | |||
| 0b5dc0466e | |||
| 99ff666f29 | |||
| f23618fa2c | |||
| c0078e57e7 | |||
| a097153415 | |||
| 0084480cac | |||
| 21da3deb11 | |||
| d6bcd20196 | |||
| 8d75f8daf9 | |||
| c2bd85debc | |||
| f1bda57e71 | |||
| 2e63767bcb | |||
| a0108171fd | |||
| 8a5515b4d7 | |||
| f4a3ee9b1e | |||
| 206aaeeaf1 | |||
| 6048893144 | |||
| a4ccceb87e | |||
| e20c551723 | |||
| 92b6c7eb4e | |||
| 3ec76571be | |||
| 070e692966 | |||
|
|
fb6f23b374 | ||
|
|
aac63f7d0a | ||
|
|
098428c67c | ||
|
|
3518a63397 | ||
|
|
f9b01bde8b | ||
|
|
87761805f4 | ||
| e39fa8a87e | |||
| 8b85451145 | |||
| 05b6bbac68 | |||
|
|
3ec806682d | ||
|
|
570d51d223 | ||
|
|
50b1cb31a6 | ||
|
|
835e2b4165 | ||
|
|
d7b4e10459 | ||
| c25833562c | |||
| 9a0f8a8846 | |||
| 4df74f116c | |||
| 2b8f80fe50 | |||
| 3b7ae71d56 | |||
| c1973b96c0 | |||
|
|
2abeb50a50 | ||
| 00a15f6a35 | |||
|
|
ed7d4482b8 | ||
|
|
b74d8fd4a8 | ||
| 307ab7ccb2 | |||
|
|
1d5db12a27 | ||
| 1cf94bc8aa | |||
| 9a6abe680f | |||
| fd6686d5c9 | |||
| a810800677 | |||
| ee1ce1eb3e | |||
|
|
30871b22ae | ||
|
|
870b944009 | ||
| 5b01165319 | |||
|
|
97e85533c6 | ||
|
|
dd88338fb1 | ||
| 6c08ea4599 | |||
| 87ab6edd98 | |||
|
|
6640453c18 | ||
| 5d44071e36 | |||
|
|
77d3cc2ec3 | ||
| ad5b6fb023 | |||
| 5c1b33cedc | |||
|
|
2fef4da2b5 | ||
|
|
3d82536404 | ||
|
|
75bbec6f5a | ||
|
|
4137794126 | ||
| 98ea395e80 | |||
|
|
c171a26f42 | ||
| d1ddb474d8 | |||
|
|
c7db94973b | ||
| 7fc3c33bed | |||
|
|
9df0d9c2c5 | ||
|
|
c43a16ed79 | ||
| b0ed603472 | |||
| 26f1d82dae | |||
| e3201a680e | |||
|
|
d1277a2b4a | ||
|
|
00f39f15f4 | ||
| e34a4313d6 | |||
| 63cbd8a05d | |||
|
|
2662a5121d | ||
| 2ed9280f6a | |||
| 2df716e781 | |||
|
|
679d3f77d2 | ||
|
|
dc86028777 | ||
| 464755b870 | |||
| 39b2f21e4d | |||
| e8f9c35efb | |||
|
|
1de2a0f022 | ||
| af44534048 | |||
|
|
277631c23a | ||
| 7913f58d19 | |||
|
|
17689e7ef1 | ||
|
|
1e324a73e2 | ||
|
|
a58d94a629 | ||
|
|
b280edf5f6 | ||
|
|
7d32536da8 | ||
|
|
1f56307b2c | ||
|
|
c214bf4a5c | ||
| 833f07f9bc | |||
| e564d656d0 | |||
|
|
7c5ba49a47 | ||
|
|
542a910aa5 | ||
| fcbb572abd | |||
| c9fb8e1379 | |||
| 2fa78a6814 | |||
| 708ed397a9 | |||
| f8b74b4950 | |||
| 1400ab4b39 | |||
| 470db529b0 | |||
|
|
9c51f711cd | ||
| 9d0187e244 | |||
| 7e7d478860 | |||
| ea472eebc6 | |||
| 311af9a359 | |||
| 9bfef4b642 | |||
| 0f4ed8bc85 | |||
| aed5b0716a | |||
| 4afad02a1d | |||
| 49ae4fef36 | |||
| f8390ef9f3 | |||
| a831b8be92 | |||
| ccccd0afd6 | |||
| c074799020 | |||
| b77a048c68 | |||
| 5b0d8b1ee5 | |||
| de52eefadc | |||
| dc2e59301f | |||
| 6eb010c37c | |||
|
|
0162c91d77 | ||
|
|
1233c38f37 | ||
| 6a2e917538 | |||
| 88c668b1a4 | |||
| 5bd7013570 | |||
| 6019e68da7 | |||
|
|
e1254515ff | ||
|
|
ee58d1083e | ||
| cdccc2835c | |||
| 2cccbd9c37 | |||
| 70bfd18204 | |||
| ef4c9fe57c | |||
| 5cffe1fe3f | |||
|
|
b3da61d072 | ||
|
|
40526ea0cf | ||
| 62f2b4baa1 | |||
| f4466829cd |
25
.env
25
.env
@ -2,8 +2,7 @@ APP_NAME=so-manager
|
|||||||
APP_ENV=local
|
APP_ENV=local
|
||||||
APP_KEY=base64:ejLwJbt2bEXY9emPUmsurG+X1hzkjTxQQvq2/FO14RY=
|
APP_KEY=base64:ejLwJbt2bEXY9emPUmsurG+X1hzkjTxQQvq2/FO14RY=
|
||||||
APP_DEBUG=true
|
APP_DEBUG=true
|
||||||
APP_URL=http://somanager-local.localhost:81/
|
APP_URL=https://krgm.so-manager-dev.com/
|
||||||
|
|
||||||
APP_LOCALE=ja
|
APP_LOCALE=ja
|
||||||
APP_FALLBACK_LOCALE=ja
|
APP_FALLBACK_LOCALE=ja
|
||||||
APP_FAKER_LOCALE=ja_JP
|
APP_FAKER_LOCALE=ja_JP
|
||||||
@ -23,9 +22,9 @@ LOG_LEVEL=debug
|
|||||||
DB_CONNECTION=mysql
|
DB_CONNECTION=mysql
|
||||||
DB_HOST=localhost
|
DB_HOST=localhost
|
||||||
DB_PORT=3306
|
DB_PORT=3306
|
||||||
DB_DATABASE=somanager_admin
|
DB_DATABASE=krgm
|
||||||
DB_USERNAME=root
|
DB_USERNAME=krgm_user
|
||||||
DB_PASSWORD=
|
DB_PASSWORD=StrongDbP@ss2
|
||||||
|
|
||||||
SESSION_DRIVER=database
|
SESSION_DRIVER=database
|
||||||
SESSION_LIFETIME=120
|
SESSION_LIFETIME=120
|
||||||
@ -47,14 +46,16 @@ REDIS_HOST=127.0.0.1
|
|||||||
REDIS_PASSWORD=null
|
REDIS_PASSWORD=null
|
||||||
REDIS_PORT=6379
|
REDIS_PORT=6379
|
||||||
|
|
||||||
MAIL_MAILER=log
|
MAIL_MAILER=smtp
|
||||||
MAIL_SCHEME=null
|
#MAIL_SCHEME=null
|
||||||
MAIL_HOST=127.0.0.1
|
MAIL_HOST=tomatofox9.sakura.ne.jp
|
||||||
MAIL_PORT=2525
|
MAIL_PORT=587
|
||||||
MAIL_USERNAME=null
|
MAIL_USERNAME=demo@so-rin.jp
|
||||||
MAIL_PASSWORD=null
|
MAIL_PASSWORD=rokuchou4665
|
||||||
MAIL_FROM_ADDRESS="hello@so-manager-dev.com"
|
MAIL_ENCRYPTION=tls
|
||||||
|
MAIL_FROM_ADDRESS=demo@so-rin.jp
|
||||||
MAIL_FROM_NAME="${APP_NAME}"
|
MAIL_FROM_NAME="${APP_NAME}"
|
||||||
|
MAIL_ADMIN=demo@so-rin.jp
|
||||||
|
|
||||||
AWS_ACCESS_KEY_ID=
|
AWS_ACCESS_KEY_ID=
|
||||||
AWS_SECRET_ACCESS_KEY=
|
AWS_SECRET_ACCESS_KEY=
|
||||||
|
|||||||
64
.env.main
64
.env.main
@ -1,64 +0,0 @@
|
|||||||
APP_NAME=so-manager
|
|
||||||
APP_ENV=local
|
|
||||||
APP_KEY=base64:ejLwJbt2bEXY9emPUmsurG+X1hzkjTxQQvq2/FO14RY=
|
|
||||||
APP_DEBUG=true
|
|
||||||
APP_URL=https://krgm.so-manager-dev.com/
|
|
||||||
APP_LOCALE=ja
|
|
||||||
APP_FALLBACK_LOCALE=ja
|
|
||||||
APP_FAKER_LOCALE=ja_JP
|
|
||||||
|
|
||||||
APP_MAINTENANCE_DRIVER=file
|
|
||||||
# APP_MAINTENANCE_STORE=database
|
|
||||||
|
|
||||||
PHP_CLI_SERVER_WORKERS=4
|
|
||||||
|
|
||||||
BCRYPT_ROUNDS=12
|
|
||||||
|
|
||||||
LOG_CHANNEL=stack
|
|
||||||
LOG_STACK=single
|
|
||||||
LOG_DEPRECATIONS_CHANNEL=null
|
|
||||||
LOG_LEVEL=debug
|
|
||||||
|
|
||||||
DB_CONNECTION=mysql
|
|
||||||
DB_HOST=localhost
|
|
||||||
DB_PORT=3306
|
|
||||||
DB_DATABASE=krgm
|
|
||||||
DB_USERNAME=krgm_user
|
|
||||||
DB_PASSWORD=StrongDbP@ss2
|
|
||||||
|
|
||||||
SESSION_DRIVER=database
|
|
||||||
SESSION_LIFETIME=120
|
|
||||||
SESSION_ENCRYPT=false
|
|
||||||
SESSION_PATH=/
|
|
||||||
SESSION_DOMAIN=null
|
|
||||||
|
|
||||||
BROADCAST_CONNECTION=log
|
|
||||||
FILESYSTEM_DISK=local
|
|
||||||
QUEUE_CONNECTION=database
|
|
||||||
|
|
||||||
CACHE_STORE=database
|
|
||||||
# CACHE_PREFIX=
|
|
||||||
|
|
||||||
MEMCACHED_HOST=127.0.0.1
|
|
||||||
|
|
||||||
REDIS_CLIENT=phpredis
|
|
||||||
REDIS_HOST=127.0.0.1
|
|
||||||
REDIS_PASSWORD=null
|
|
||||||
REDIS_PORT=6379
|
|
||||||
|
|
||||||
MAIL_MAILER=log
|
|
||||||
MAIL_SCHEME=null
|
|
||||||
MAIL_HOST=127.0.0.1
|
|
||||||
MAIL_PORT=2525
|
|
||||||
MAIL_USERNAME=null
|
|
||||||
MAIL_PASSWORD=null
|
|
||||||
MAIL_FROM_ADDRESS="hello@so-manager-dev.com"
|
|
||||||
MAIL_FROM_NAME="${APP_NAME}"
|
|
||||||
|
|
||||||
AWS_ACCESS_KEY_ID=
|
|
||||||
AWS_SECRET_ACCESS_KEY=
|
|
||||||
AWS_DEFAULT_REGION=us-east-1
|
|
||||||
AWS_BUCKET=
|
|
||||||
AWS_USE_PATH_STYLE_ENDPOINT=false
|
|
||||||
|
|
||||||
VITE_APP_NAME="${APP_NAME}"
|
|
||||||
@ -1,238 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Console\Commands;
|
|
||||||
|
|
||||||
use Illuminate\Console\Command;
|
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
use App\Models\Batch\BatchLog;
|
|
||||||
use App\Models\Device;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SHJ-8 バッチ処理ログ登録コマンド
|
|
||||||
*
|
|
||||||
* 統一BatchLogを使用してバッチ処理の実行ログをbatch_logテーブルに登録する
|
|
||||||
* 仕様書に基づくSHJ-8の要求パラメータを受け取り、通用のログシステムで記録
|
|
||||||
*/
|
|
||||||
class ShjBatchLogCommand extends Command
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* コンソールコマンドの名前とシグネチャ
|
|
||||||
*
|
|
||||||
* 引数:
|
|
||||||
* - device_id: デバイスID (必須)
|
|
||||||
* - process_name: プロセス名 (必須)
|
|
||||||
* - job_name: ジョブ名 (必須)
|
|
||||||
* - status: ステータス (必須)
|
|
||||||
* - created_date: 登録日時 (必須、yyyy/mm/dd形式)
|
|
||||||
* - updated_date: 更新日時 (必須、yyyy/mm/dd形式)
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $signature = 'shj:batch-log {device_id : デバイスID} {process_name : プロセス名} {job_name : ジョブ名} {status : ステータス} {created_date : 登録日時} {updated_date : 更新日時}';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* コンソールコマンドの説明
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $description = 'SHJ-8 バッチ処理ログ登録 - バッチ処理の実行ログを登録';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* コンストラクタ
|
|
||||||
*/
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
parent::__construct();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* コンソールコマンドを実行
|
|
||||||
*
|
|
||||||
* 処理フロー:
|
|
||||||
* 1. 入力パラメーターをチェックする
|
|
||||||
* 2. 統一BatchLogを使用してbatch_logテーブルに記録
|
|
||||||
* 3. 仕様書準拠の処理結果を返却する
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public function handle()
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
// 開始ログ出力
|
|
||||||
$startTime = now();
|
|
||||||
$this->info('SHJ-8 バッチ処理ログ登録を開始します。');
|
|
||||||
|
|
||||||
// 引数取得
|
|
||||||
$deviceId = (int) $this->argument('device_id');
|
|
||||||
$processName = $this->argument('process_name');
|
|
||||||
$jobName = $this->argument('job_name');
|
|
||||||
$status = $this->argument('status');
|
|
||||||
$createdDate = $this->argument('created_date');
|
|
||||||
$updatedDate = $this->argument('updated_date');
|
|
||||||
|
|
||||||
Log::info('SHJ-8 バッチ処理ログ登録開始', [
|
|
||||||
'start_time' => $startTime,
|
|
||||||
'device_id' => $deviceId,
|
|
||||||
'process_name' => $processName,
|
|
||||||
'job_name' => $jobName,
|
|
||||||
'status' => $status,
|
|
||||||
'created_date' => $createdDate,
|
|
||||||
'updated_date' => $updatedDate
|
|
||||||
]);
|
|
||||||
|
|
||||||
// 【処理1】入力パラメーターをチェックする
|
|
||||||
$paramCheckResult = $this->validateParameters($deviceId, $processName, $jobName, $status, $createdDate, $updatedDate);
|
|
||||||
if (!$paramCheckResult['valid']) {
|
|
||||||
$this->error('パラメータエラー: ' . $paramCheckResult['message']);
|
|
||||||
|
|
||||||
// 仕様書【判断1】パラメーターNG時の結果出力
|
|
||||||
$this->line('処理結果: 1'); // 1 = 異常終了
|
|
||||||
$this->line('異常情報: ' . $paramCheckResult['message']);
|
|
||||||
|
|
||||||
return self::FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 【処理2】統一BatchLogを使用してログ登録
|
|
||||||
$batchLog = BatchLog::createBatchLog(
|
|
||||||
$processName, // 実際のプロセス名を使用
|
|
||||||
$status,
|
|
||||||
[
|
|
||||||
'device_id' => $deviceId,
|
|
||||||
'job_name' => $jobName,
|
|
||||||
'status_comment' => BatchLog::getSuccessComment(),
|
|
||||||
'input_created_date' => $createdDate,
|
|
||||||
'input_updated_date' => $updatedDate,
|
|
||||||
'shj8_params' => [
|
|
||||||
'device_id' => $deviceId,
|
|
||||||
'process_name' => $processName,
|
|
||||||
'job_name' => $jobName,
|
|
||||||
'status' => $status,
|
|
||||||
'created_date' => $createdDate,
|
|
||||||
'updated_date' => $updatedDate
|
|
||||||
]
|
|
||||||
],
|
|
||||||
$jobName . ':' . BatchLog::getSuccessComment()
|
|
||||||
);
|
|
||||||
|
|
||||||
$endTime = now();
|
|
||||||
$this->info('SHJ-8 バッチ処理ログ登録が正常に完了しました。');
|
|
||||||
$this->info("処理時間: {$startTime->diffInSeconds($endTime)}秒");
|
|
||||||
|
|
||||||
Log::info('SHJ-8 バッチ処理ログ登録完了', [
|
|
||||||
'end_time' => $endTime,
|
|
||||||
'duration_seconds' => $startTime->diffInSeconds($endTime),
|
|
||||||
'batch_log_id' => $batchLog->id
|
|
||||||
]);
|
|
||||||
|
|
||||||
// 仕様書【処理3】正常終了時の結果出力
|
|
||||||
$this->line('処理結果: 0'); // 0 = 正常終了
|
|
||||||
$this->line('異常情報: '); // 正常時は空文字
|
|
||||||
|
|
||||||
return self::SUCCESS;
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
$this->error('SHJ-8 バッチ処理ログ登録で予期しないエラーが発生しました: ' . $e->getMessage());
|
|
||||||
Log::error('SHJ-8 バッチ処理ログ登録例外エラー', [
|
|
||||||
'exception' => $e->getMessage(),
|
|
||||||
'trace' => $e->getTraceAsString()
|
|
||||||
]);
|
|
||||||
|
|
||||||
// 仕様書【処理3】異常終了時の結果出力
|
|
||||||
$this->line('処理結果: 1'); // 1 = 異常終了
|
|
||||||
$this->line('異常情報: エラー: ' . $e->getMessage());
|
|
||||||
|
|
||||||
return self::FAILURE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 【処理1】パラメータの妥当性を検証
|
|
||||||
*
|
|
||||||
* 仕様書に基づく検証内容:
|
|
||||||
* - デバイスID: 必須、数値、device表に存在するか
|
|
||||||
* - プロセス名: 「プロセス名」「ジョブ名」いずれか必須
|
|
||||||
* - ジョブ名: 「プロセス名」「ジョブ名」いずれか必須
|
|
||||||
* - ステータス: 必須
|
|
||||||
* - 登録日時: 必須、yyyy/mm/dd形式
|
|
||||||
* - 更新日時: 必須、yyyy/mm/dd形式
|
|
||||||
*
|
|
||||||
* @param int $deviceId デバイスID
|
|
||||||
* @param string $processName プロセス名
|
|
||||||
* @param string $jobName ジョブ名
|
|
||||||
* @param string $status ステータス
|
|
||||||
* @param string $createdDate 登録日時
|
|
||||||
* @param string $updatedDate 更新日時
|
|
||||||
* @return array 検証結果 ['valid' => bool, 'message' => string]
|
|
||||||
*/
|
|
||||||
private function validateParameters(int $deviceId, string $processName, string $jobName, string $status, string $createdDate, string $updatedDate): array
|
|
||||||
{
|
|
||||||
// デバイスID存在チェック
|
|
||||||
if ($deviceId <= 0) {
|
|
||||||
return [
|
|
||||||
'valid' => false,
|
|
||||||
'message' => 'パラメーターNG: デバイスIDは正の整数である必要があります'
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Device::exists($deviceId)) {
|
|
||||||
return [
|
|
||||||
'valid' => false,
|
|
||||||
'message' => "パラメーターNG: デバイスID {$deviceId} が存在しません"
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
// プロセス名とジョブ名のいずれか必須チェック
|
|
||||||
if (empty($processName) && empty($jobName)) {
|
|
||||||
return [
|
|
||||||
'valid' => false,
|
|
||||||
'message' => 'パラメーターNG: プロセス名またはジョブ名のいずれかは必須です'
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
// ステータス必須チェック
|
|
||||||
if (empty($status)) {
|
|
||||||
return [
|
|
||||||
'valid' => false,
|
|
||||||
'message' => 'パラメーターNG: ステータスは必須です'
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
// 日付形式チェック
|
|
||||||
if (!$this->isValidDateFormat($createdDate)) {
|
|
||||||
return [
|
|
||||||
'valid' => false,
|
|
||||||
'message' => 'パラメーターNG: 登録日時の形式が正しくありません(yyyy/mm/dd)'
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$this->isValidDateFormat($updatedDate)) {
|
|
||||||
return [
|
|
||||||
'valid' => false,
|
|
||||||
'message' => 'パラメーターNG: 更新日時の形式が正しくありません(yyyy/mm/dd)'
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
'valid' => true,
|
|
||||||
'message' => 'パラメーターチェックOK'
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 日付形式の検証
|
|
||||||
*
|
|
||||||
* @param string $date 日付文字列
|
|
||||||
* @return bool 有効な日付形式かどうか
|
|
||||||
*/
|
|
||||||
private function isValidDateFormat(string $date): bool
|
|
||||||
{
|
|
||||||
// yyyy/mm/dd形式の正規表現チェック
|
|
||||||
if (!preg_match('/^\d{4}\/\d{2}\/\d{2}$/', $date)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 実際の日付として有効かチェック
|
|
||||||
$dateParts = explode('/', $date);
|
|
||||||
return checkdate((int)$dateParts[1], (int)$dateParts[2], (int)$dateParts[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,182 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Console\Commands;
|
|
||||||
|
|
||||||
use Illuminate\Console\Command;
|
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
use App\Services\ShjElevenService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SHJ-11 現在契約台数集計コマンド
|
|
||||||
*
|
|
||||||
* 集計単位每个の契約台数を算出し、ゾーンマスタとの管理処理を実行する
|
|
||||||
* バックグラウンドで実行される定期バッチ処理
|
|
||||||
*/
|
|
||||||
class ShjElevenCommand extends Command
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* コンソールコマンドの名前とシグネチャ
|
|
||||||
*
|
|
||||||
* 引数: なし
|
|
||||||
* オプション: なし
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $signature = 'shj:11';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* コンソールコマンドの説明
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $description = 'SHJ-11 現在契約台数集計 - 集計単位每个契約台数を算出しゾーンマスタ管理を実行';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SHJ-11サービスクラス
|
|
||||||
*
|
|
||||||
* @var ShjElevenService
|
|
||||||
*/
|
|
||||||
protected $shjElevenService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* コンストラクタ
|
|
||||||
*
|
|
||||||
* @param ShjElevenService $shjElevenService
|
|
||||||
*/
|
|
||||||
public function __construct(ShjElevenService $shjElevenService)
|
|
||||||
{
|
|
||||||
parent::__construct();
|
|
||||||
$this->shjElevenService = $shjElevenService;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* コンソールコマンドを実行
|
|
||||||
*
|
|
||||||
* 処理フロー:
|
|
||||||
* 1. 集計単位每个の契約台数を算出する
|
|
||||||
* 2. 取得件数判定
|
|
||||||
* 3. ゾーンマスタを取得する
|
|
||||||
* 4. 取得判定とゾーンマスタ登録
|
|
||||||
* 5. 契約台数チェック(限界台数超過判定)
|
|
||||||
* 6. 契約台数を反映する
|
|
||||||
* 7. バッチ処理ログを作成する
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public function handle()
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
// 開始ログ出力
|
|
||||||
$startTime = now();
|
|
||||||
$this->info('SHJ-11 現在契約台数集計を開始します。');
|
|
||||||
Log::info('SHJ-11 現在契約台数集計開始', [
|
|
||||||
'start_time' => $startTime
|
|
||||||
]);
|
|
||||||
|
|
||||||
// 【処理1】集計単位每个の契約台数を算出する
|
|
||||||
$this->info('【処理1】集計単位每个の契約台数を算出しています...');
|
|
||||||
$contractCounts = $this->shjElevenService->calculateContractCounts();
|
|
||||||
|
|
||||||
// 【判断1】取得件数判定
|
|
||||||
$countResults = count($contractCounts);
|
|
||||||
$this->info("取得件数: {$countResults}件");
|
|
||||||
|
|
||||||
if ($countResults === 0) {
|
|
||||||
// 対象なしの結果を設定する
|
|
||||||
$this->info('契約台数算出対象なしのため処理を終了します。');
|
|
||||||
|
|
||||||
// バッチ処理ログを作成
|
|
||||||
$this->shjElevenService->createBatchLog(
|
|
||||||
'success',
|
|
||||||
[],
|
|
||||||
'契約台数算出対象なし',
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0
|
|
||||||
);
|
|
||||||
|
|
||||||
Log::info('SHJ-11 現在契約台数集計完了(対象なし)', [
|
|
||||||
'end_time' => now(),
|
|
||||||
'duration_seconds' => $startTime->diffInSeconds(now())
|
|
||||||
]);
|
|
||||||
|
|
||||||
return self::SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 【処理2・3】ゾーンマスタ処理(取得・登録・更新)
|
|
||||||
$this->info('【処理2】ゾーンマスタ処理を実行しています...');
|
|
||||||
$processResult = $this->shjElevenService->processZoneManagement($contractCounts);
|
|
||||||
|
|
||||||
// 処理結果確認
|
|
||||||
if ($processResult['success']) {
|
|
||||||
$endTime = now();
|
|
||||||
$this->info('SHJ-11 現在契約台数集計が正常に完了しました。');
|
|
||||||
$this->info("処理時間: {$startTime->diffInSeconds($endTime)}秒");
|
|
||||||
$this->info("処理対象件数: {$countResults}件");
|
|
||||||
$this->info("ゾーン新規作成件数: {$processResult['created_zones']}件");
|
|
||||||
$this->info("ゾーン更新件数: {$processResult['updated_zones']}件");
|
|
||||||
$this->info("限界台数超過件数: {$processResult['over_capacity_count']}件");
|
|
||||||
|
|
||||||
// 【処理4】バッチ処理ログを作成する
|
|
||||||
$this->shjElevenService->createBatchLog(
|
|
||||||
'success',
|
|
||||||
$processResult['parameters'],
|
|
||||||
'現在契約台数集計処理完了',
|
|
||||||
$countResults,
|
|
||||||
$processResult['created_zones'] + $processResult['updated_zones'],
|
|
||||||
0
|
|
||||||
);
|
|
||||||
|
|
||||||
Log::info('SHJ-11 現在契約台数集計完了', [
|
|
||||||
'end_time' => $endTime,
|
|
||||||
'duration_seconds' => $startTime->diffInSeconds($endTime),
|
|
||||||
'processed_count' => $countResults,
|
|
||||||
'created_zones' => $processResult['created_zones'],
|
|
||||||
'updated_zones' => $processResult['updated_zones'],
|
|
||||||
'over_capacity_count' => $processResult['over_capacity_count']
|
|
||||||
]);
|
|
||||||
|
|
||||||
return self::SUCCESS;
|
|
||||||
} else {
|
|
||||||
$this->error('SHJ-11 現在契約台数集計でエラーが発生しました: ' . $processResult['message']);
|
|
||||||
|
|
||||||
// エラー時のバッチログ作成
|
|
||||||
$this->shjElevenService->createBatchLog(
|
|
||||||
'error',
|
|
||||||
$processResult['parameters'] ?? [],
|
|
||||||
$processResult['message'],
|
|
||||||
$countResults,
|
|
||||||
$processResult['created_zones'] ?? 0,
|
|
||||||
1
|
|
||||||
);
|
|
||||||
|
|
||||||
Log::error('SHJ-11 現在契約台数集計エラー', [
|
|
||||||
'error' => $processResult['message'],
|
|
||||||
'details' => $processResult['details'] ?? null
|
|
||||||
]);
|
|
||||||
|
|
||||||
return self::FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
$this->error('SHJ-11 現在契約台数集計で予期しないエラーが発生しました: ' . $e->getMessage());
|
|
||||||
|
|
||||||
// 例外時のバッチログ作成
|
|
||||||
$this->shjElevenService->createBatchLog(
|
|
||||||
'error',
|
|
||||||
[],
|
|
||||||
'システムエラー: ' . $e->getMessage(),
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
1
|
|
||||||
);
|
|
||||||
|
|
||||||
Log::error('SHJ-11 現在契約台数集計例外エラー', [
|
|
||||||
'exception' => $e->getMessage(),
|
|
||||||
'trace' => $e->getTraceAsString()
|
|
||||||
]);
|
|
||||||
|
|
||||||
return self::FAILURE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,157 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Console\Commands;
|
|
||||||
|
|
||||||
use Illuminate\Console\Command;
|
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
use App\Services\ShjFourCService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SHJ-4C 室割当処理コマンド
|
|
||||||
*
|
|
||||||
* 駐輪場の区画別利用率状況に基づく室割当処理を実行する
|
|
||||||
* バックグラウンドで実行される定期バッチ処理
|
|
||||||
*/
|
|
||||||
class ShjFourCCommand extends Command
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* コンソールコマンドの名前とシグネチャ
|
|
||||||
*
|
|
||||||
* 引数:
|
|
||||||
* - park_id: 駐輪場ID (必須)
|
|
||||||
* - ptype_id: 駐輪分類ID (必須)
|
|
||||||
* - psection_id: 車種区分ID (必須)
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $signature = 'shj:4c {park_id : 駐輪場ID} {ptype_id : 駐輪分類ID} {psection_id : 車種区分ID}';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* コンソールコマンドの説明
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $description = 'SHJ-4C 室割当処理 - ゾーン情報取得及び割当処理を実行';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SHJ-4Cサービスクラス
|
|
||||||
*
|
|
||||||
* @var ShjFourCService
|
|
||||||
*/
|
|
||||||
protected $shjFourCService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* コンストラクタ
|
|
||||||
*
|
|
||||||
* @param ShjFourCService $shjFourCService
|
|
||||||
*/
|
|
||||||
public function __construct(ShjFourCService $shjFourCService)
|
|
||||||
{
|
|
||||||
parent::__construct();
|
|
||||||
$this->shjFourCService = $shjFourCService;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* コンソールコマンドを実行
|
|
||||||
*
|
|
||||||
* 処理フロー:
|
|
||||||
* 1. パラメータ取得と検証
|
|
||||||
* 2. ゾーン情報取得処理
|
|
||||||
* 3. 割当判定処理
|
|
||||||
* 4. バッチログ作成
|
|
||||||
* 5. 処理結果返却
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public function handle()
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
// 開始ログ出力
|
|
||||||
$startTime = now();
|
|
||||||
$this->info('SHJ-4C 室割当処理を開始します。');
|
|
||||||
Log::info('SHJ-4C 室割当処理開始', [
|
|
||||||
'start_time' => $startTime,
|
|
||||||
'park_id' => $this->argument('park_id'),
|
|
||||||
'ptype_id' => $this->argument('ptype_id'),
|
|
||||||
'psection_id' => $this->argument('psection_id')
|
|
||||||
]);
|
|
||||||
|
|
||||||
// 引数取得
|
|
||||||
$parkId = $this->argument('park_id');
|
|
||||||
$ptypeId = $this->argument('ptype_id');
|
|
||||||
$psectionId = $this->argument('psection_id');
|
|
||||||
|
|
||||||
// パラメータ検証
|
|
||||||
if (!$this->validateParameters($parkId, $ptypeId, $psectionId)) {
|
|
||||||
$this->error('パラメータが不正です。');
|
|
||||||
return self::FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// SHJ-4C処理実行
|
|
||||||
$result = $this->shjFourCService->executeRoomAllocation($parkId, $ptypeId, $psectionId);
|
|
||||||
|
|
||||||
// 処理結果確認
|
|
||||||
if ($result['success']) {
|
|
||||||
$endTime = now();
|
|
||||||
$this->info('SHJ-4C 室割当処理が正常に完了しました。');
|
|
||||||
$this->info("処理時間: {$startTime->diffInSeconds($endTime)}秒");
|
|
||||||
|
|
||||||
Log::info('SHJ-4C 室割当処理完了', [
|
|
||||||
'end_time' => $endTime,
|
|
||||||
'duration_seconds' => $startTime->diffInSeconds($endTime),
|
|
||||||
'result' => $result
|
|
||||||
]);
|
|
||||||
|
|
||||||
return self::SUCCESS;
|
|
||||||
} else {
|
|
||||||
$this->error('SHJ-4C 室割当処理でエラーが発生しました: ' . $result['message']);
|
|
||||||
Log::error('SHJ-4C 室割当処理エラー', [
|
|
||||||
'error' => $result['message'],
|
|
||||||
'details' => $result['details'] ?? null
|
|
||||||
]);
|
|
||||||
|
|
||||||
return self::FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
$this->error('SHJ-4C 室割当処理で予期しないエラーが発生しました: ' . $e->getMessage());
|
|
||||||
Log::error('SHJ-4C 室割当処理例外エラー', [
|
|
||||||
'exception' => $e->getMessage(),
|
|
||||||
'trace' => $e->getTraceAsString()
|
|
||||||
]);
|
|
||||||
|
|
||||||
return self::FAILURE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* パラメータの妥当性を検証
|
|
||||||
*
|
|
||||||
* @param mixed $parkId 駐輪場ID
|
|
||||||
* @param mixed $ptypeId 駐輪分類ID
|
|
||||||
* @param mixed $psectionId 車種区分ID
|
|
||||||
* @return bool 検証結果
|
|
||||||
*/
|
|
||||||
private function validateParameters($parkId, $ptypeId, $psectionId): bool
|
|
||||||
{
|
|
||||||
// 必須パラメータチェック
|
|
||||||
if (empty($parkId) || empty($ptypeId) || empty($psectionId)) {
|
|
||||||
$this->error('全てのパラメータは必須です。');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 数値形式チェック
|
|
||||||
if (!is_numeric($parkId) || !is_numeric($ptypeId) || !is_numeric($psectionId)) {
|
|
||||||
$this->error('全てのパラメータは数値である必要があります。');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 正の整数チェック
|
|
||||||
if ($parkId <= 0 || $ptypeId <= 0 || $psectionId <= 0) {
|
|
||||||
$this->error('全てのパラメータは正の整数である必要があります。');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,177 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Console\Commands;
|
|
||||||
|
|
||||||
use Illuminate\Console\Command;
|
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
use App\Services\ShjMailSendService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SHJ メール送信処理コマンド
|
|
||||||
*
|
|
||||||
* メールテンプレートを使用したメール送信処理を実行する
|
|
||||||
* バックグラウンドで実行される定期バッチ処理
|
|
||||||
*/
|
|
||||||
class ShjMailSendCommand extends Command
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* コンソールコマンドの名前とシグネチャ
|
|
||||||
*
|
|
||||||
* 引数:
|
|
||||||
* - mail_address: メールアドレス (必須)
|
|
||||||
* - backup_mail_address: 予備メールアドレス (必須)
|
|
||||||
* - mail_template_id: メールテンプレートID (必須)
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $signature = 'shj:mail-send {mail_address : メールアドレス} {backup_mail_address : 予備メールアドレス} {mail_template_id : メールテンプレートID}';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* コンソールコマンドの説明
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $description = 'SHJ メール送信処理 - テンプレートに基づくメール送信を実行';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SHJメール送信サービスクラス
|
|
||||||
*
|
|
||||||
* @var ShjMailSendService
|
|
||||||
*/
|
|
||||||
protected $shjMailSendService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* コンストラクタ
|
|
||||||
*
|
|
||||||
* @param ShjMailSendService $shjMailSendService
|
|
||||||
*/
|
|
||||||
public function __construct(ShjMailSendService $shjMailSendService)
|
|
||||||
{
|
|
||||||
parent::__construct();
|
|
||||||
$this->shjMailSendService = $shjMailSendService;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* コンソールコマンドを実行
|
|
||||||
*
|
|
||||||
* 処理フロー:
|
|
||||||
* 1. 入力パラメーターをチェックする
|
|
||||||
* 2. メール送信テンプレート情報を取得する
|
|
||||||
* 3. メールを送信する
|
|
||||||
* 4. 処理結果を返却する
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public function handle()
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
// 開始ログ出力
|
|
||||||
$startTime = now();
|
|
||||||
$this->info('SHJ メール送信処理を開始します。');
|
|
||||||
Log::info('SHJ メール送信処理開始', [
|
|
||||||
'start_time' => $startTime,
|
|
||||||
'mail_address' => $this->argument('mail_address'),
|
|
||||||
'backup_mail_address' => $this->argument('backup_mail_address'),
|
|
||||||
'mail_template_id' => $this->argument('mail_template_id')
|
|
||||||
]);
|
|
||||||
|
|
||||||
// 引数取得
|
|
||||||
$mailAddress = $this->argument('mail_address');
|
|
||||||
$backupMailAddress = $this->argument('backup_mail_address');
|
|
||||||
$mailTemplateId = $this->argument('mail_template_id');
|
|
||||||
|
|
||||||
// 【処理1】パラメータ検証
|
|
||||||
if (!$this->validateParameters($mailAddress, $backupMailAddress, $mailTemplateId)) {
|
|
||||||
$this->error('パラメータが不正です。');
|
|
||||||
return self::FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// SHJメール送信処理実行
|
|
||||||
$result = $this->shjMailSendService->executeMailSend($mailAddress, $backupMailAddress, $mailTemplateId);
|
|
||||||
|
|
||||||
// 処理結果確認
|
|
||||||
if ($result['success']) {
|
|
||||||
$endTime = now();
|
|
||||||
$this->info('SHJ メール送信処理が正常に完了しました。');
|
|
||||||
$this->info("処理時間: {$startTime->diffInSeconds($endTime)}秒");
|
|
||||||
|
|
||||||
Log::info('SHJ メール送信処理完了', [
|
|
||||||
'end_time' => $endTime,
|
|
||||||
'duration_seconds' => $startTime->diffInSeconds($endTime),
|
|
||||||
'result' => $result
|
|
||||||
]);
|
|
||||||
|
|
||||||
return self::SUCCESS;
|
|
||||||
} else {
|
|
||||||
$this->error('SHJ メール送信処理でエラーが発生しました: ' . $result['message']);
|
|
||||||
Log::error('SHJ メール送信処理エラー', [
|
|
||||||
'error' => $result['message'],
|
|
||||||
'details' => $result['details'] ?? null
|
|
||||||
]);
|
|
||||||
|
|
||||||
return self::FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
$this->error('SHJ メール送信処理で予期しないエラーが発生しました: ' . $e->getMessage());
|
|
||||||
Log::error('SHJ メール送信処理例外エラー', [
|
|
||||||
'exception' => $e->getMessage(),
|
|
||||||
'trace' => $e->getTraceAsString()
|
|
||||||
]);
|
|
||||||
|
|
||||||
return self::FAILURE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 【処理1】パラメータの妥当性を検証
|
|
||||||
*
|
|
||||||
* 仕様書に基づく検証内容:
|
|
||||||
* - メールアドレス: 「メールアドレス」「予備メールアドレス」いずれか必須
|
|
||||||
* - メールテンプレートID: 必須
|
|
||||||
*
|
|
||||||
* @param mixed $mailAddress メールアドレス
|
|
||||||
* @param mixed $backupMailAddress 予備メールアドレス
|
|
||||||
* @param mixed $mailTemplateId メールテンプレートID
|
|
||||||
* @return bool 検証結果
|
|
||||||
*/
|
|
||||||
private function validateParameters($mailAddress, $backupMailAddress, $mailTemplateId): bool
|
|
||||||
{
|
|
||||||
// メールテンプレートIDチェック
|
|
||||||
if (empty($mailTemplateId)) {
|
|
||||||
$this->error('メールテンプレートIDは必須です。');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 数値形式チェック(メールテンプレートID)
|
|
||||||
if (!is_numeric($mailTemplateId)) {
|
|
||||||
$this->error('メールテンプレートIDは数値である必要があります。');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 正の整数チェック(メールテンプレートID)
|
|
||||||
if ($mailTemplateId <= 0) {
|
|
||||||
$this->error('メールテンプレートIDは正の整数である必要があります。');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// メールアドレスチェック(いずれか必須)
|
|
||||||
if (empty($mailAddress) && empty($backupMailAddress)) {
|
|
||||||
$this->error('メールアドレスまたは予備メールアドレスのいずれかは必須です。');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// メールアドレス形式チェック
|
|
||||||
if (!empty($mailAddress) && !filter_var($mailAddress, FILTER_VALIDATE_EMAIL)) {
|
|
||||||
$this->error('メールアドレスの形式が正しくありません。');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!empty($backupMailAddress) && !filter_var($backupMailAddress, FILTER_VALIDATE_EMAIL)) {
|
|
||||||
$this->error('予備メールアドレスの形式が正しくありません。');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,206 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Console\Commands;
|
|
||||||
|
|
||||||
use Illuminate\Console\Command;
|
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
use App\Services\ShjNineService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SHJ-9 売上集計処理コマンド
|
|
||||||
*
|
|
||||||
* 駐輪場の売上データを日次・月次・年次で集計する処理を実行する
|
|
||||||
* バックグラウンドで実行される定期バッチ処理
|
|
||||||
*/
|
|
||||||
class ShjNineCommand extends Command
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* コンソールコマンドの名前とシグネチャ
|
|
||||||
*
|
|
||||||
* 引数:
|
|
||||||
* - type: 集計種別 (daily/monthly/yearly) (必須)
|
|
||||||
* - target_date: 集計対象日 (オプション、YYYY-MM-DD形式)
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $signature = 'shj:9 {type : 集計種別(daily/monthly/yearly)} {target_date? : 集計対象日(YYYY-MM-DD)}';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* コンソールコマンドの説明
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $description = 'SHJ-9 売上集計処理 - 日次/月次/年次売上データ集計を実行';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SHJ-9サービスクラス
|
|
||||||
*
|
|
||||||
* @var ShjNineService
|
|
||||||
*/
|
|
||||||
protected $shjNineService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* コンストラクタ
|
|
||||||
*
|
|
||||||
* @param ShjNineService $shjNineService
|
|
||||||
*/
|
|
||||||
public function __construct(ShjNineService $shjNineService)
|
|
||||||
{
|
|
||||||
parent::__construct();
|
|
||||||
$this->shjNineService = $shjNineService;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* コンソールコマンドを実行
|
|
||||||
*
|
|
||||||
* 処理フロー:
|
|
||||||
* 1. パラメータ取得と検証
|
|
||||||
* 2. 集計対象日設定
|
|
||||||
* 3. 売上集計処理実行
|
|
||||||
* 4. バッチログ作成
|
|
||||||
* 5. 処理結果返却
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public function handle()
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
// 開始ログ出力
|
|
||||||
$startTime = now();
|
|
||||||
$this->info('SHJ-9 売上集計処理を開始します。');
|
|
||||||
|
|
||||||
// 引数取得
|
|
||||||
$type = $this->argument('type');
|
|
||||||
$targetDate = $this->argument('target_date');
|
|
||||||
|
|
||||||
Log::info('SHJ-9 売上集計処理開始', [
|
|
||||||
'start_time' => $startTime,
|
|
||||||
'type' => $type,
|
|
||||||
'target_date' => $targetDate
|
|
||||||
]);
|
|
||||||
|
|
||||||
// パラメータ検証
|
|
||||||
if (!$this->validateParameters($type, $targetDate)) {
|
|
||||||
$this->error('パラメータが不正です。');
|
|
||||||
return self::FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 集計対象日設定
|
|
||||||
$aggregationDate = $this->determineAggregationDate($type, $targetDate);
|
|
||||||
|
|
||||||
$this->info("集計種別: {$type}");
|
|
||||||
$this->info("集計対象日: {$aggregationDate}");
|
|
||||||
|
|
||||||
// SHJ-9処理実行
|
|
||||||
$result = $this->shjNineService->executeEarningsAggregation($type, $aggregationDate);
|
|
||||||
|
|
||||||
// 処理結果確認
|
|
||||||
if ($result['success']) {
|
|
||||||
$endTime = now();
|
|
||||||
$this->info('SHJ-9 売上集計処理が正常に完了しました。');
|
|
||||||
$this->info("処理時間: {$startTime->diffInSeconds($endTime)}秒");
|
|
||||||
$this->info("処理結果: 駐輪場数 {$result['processed_parks']}, 集計レコード数 {$result['summary_records']}");
|
|
||||||
|
|
||||||
Log::info('SHJ-9 売上集計処理完了', [
|
|
||||||
'end_time' => $endTime,
|
|
||||||
'duration_seconds' => $startTime->diffInSeconds($endTime),
|
|
||||||
'result' => $result
|
|
||||||
]);
|
|
||||||
|
|
||||||
return self::SUCCESS;
|
|
||||||
} else {
|
|
||||||
$this->error('SHJ-9 売上集計処理でエラーが発生しました: ' . $result['message']);
|
|
||||||
Log::error('SHJ-9 売上集計処理エラー', [
|
|
||||||
'error' => $result['message'],
|
|
||||||
'details' => $result['details'] ?? null
|
|
||||||
]);
|
|
||||||
|
|
||||||
return self::FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
$this->error('SHJ-9 売上集計処理で予期しないエラーが発生しました: ' . $e->getMessage());
|
|
||||||
Log::error('SHJ-9 売上集計処理例外エラー', [
|
|
||||||
'exception' => $e->getMessage(),
|
|
||||||
'trace' => $e->getTraceAsString()
|
|
||||||
]);
|
|
||||||
|
|
||||||
return self::FAILURE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* パラメータの妥当性を検証
|
|
||||||
*
|
|
||||||
* @param string $type 集計種別
|
|
||||||
* @param string|null $targetDate 対象日
|
|
||||||
* @return bool 検証結果
|
|
||||||
*/
|
|
||||||
private function validateParameters(string $type, ?string $targetDate): bool
|
|
||||||
{
|
|
||||||
// 集計種別チェック
|
|
||||||
$allowedTypes = ['daily', 'monthly', 'yearly'];
|
|
||||||
if (!in_array($type, $allowedTypes)) {
|
|
||||||
$this->error('集計種別は daily, monthly, yearly のいずれかを指定してください。');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 対象日形式チェック(指定されている場合)
|
|
||||||
if ($targetDate && !$this->isValidDateFormat($targetDate)) {
|
|
||||||
$this->error('対象日の形式が正しくありません(YYYY-MM-DD形式で指定してください)。');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 集計対象日を決定
|
|
||||||
*
|
|
||||||
* @param string $type 集計種別
|
|
||||||
* @param string|null $targetDate 指定日
|
|
||||||
* @return string 集計対象日
|
|
||||||
*/
|
|
||||||
private function determineAggregationDate(string $type, ?string $targetDate): string
|
|
||||||
{
|
|
||||||
if ($targetDate) {
|
|
||||||
return $targetDate;
|
|
||||||
}
|
|
||||||
|
|
||||||
// パラメータ指定がない場合のデフォルト設定
|
|
||||||
switch ($type) {
|
|
||||||
case 'daily':
|
|
||||||
// 日次:昨日(本日の1日前)
|
|
||||||
return now()->subDay()->format('Y-m-d');
|
|
||||||
|
|
||||||
case 'monthly':
|
|
||||||
// 月次:前月の最終日
|
|
||||||
return now()->subMonth()->endOfMonth()->format('Y-m-d');
|
|
||||||
|
|
||||||
case 'yearly':
|
|
||||||
// 年次:前年の最終日
|
|
||||||
return now()->subYear()->endOfYear()->format('Y-m-d');
|
|
||||||
|
|
||||||
default:
|
|
||||||
return now()->subDay()->format('Y-m-d');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 日付形式の検証
|
|
||||||
*
|
|
||||||
* @param string $date 日付文字列
|
|
||||||
* @return bool 有効な日付形式かどうか
|
|
||||||
*/
|
|
||||||
private function isValidDateFormat(string $date): bool
|
|
||||||
{
|
|
||||||
// YYYY-MM-DD形式の正規表現チェック
|
|
||||||
if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $date)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 実際の日付として有効かチェック
|
|
||||||
$dateParts = explode('-', $date);
|
|
||||||
return checkdate((int)$dateParts[1], (int)$dateParts[2], (int)$dateParts[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,129 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Console\Commands;
|
|
||||||
|
|
||||||
use Illuminate\Console\Command;
|
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
use App\Services\ShjSixService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SHJ-6 サーバ死活監視処理コマンド
|
|
||||||
*
|
|
||||||
* サーバとデバイスの死活監視を行い、異常時にはメール通知を実行する
|
|
||||||
* 定期実行またはオンデマンド実行のバックグラウンドバッチ処理
|
|
||||||
*/
|
|
||||||
class ShjSixCommand extends Command
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* コンソールコマンドの名前とシグネチャ
|
|
||||||
*
|
|
||||||
* パラメータなしで実行
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $signature = 'shj:6';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* コンソールコマンドの説明
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $description = 'SHJ-6 サーバ死活監視処理 - サーバ・デバイス監視とアラート通知を実行';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SHJ-6サービスクラス
|
|
||||||
*
|
|
||||||
* @var ShjSixService
|
|
||||||
*/
|
|
||||||
protected $shjSixService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* コンストラクタ
|
|
||||||
*
|
|
||||||
* @param ShjSixService $shjSixService
|
|
||||||
*/
|
|
||||||
public function __construct(ShjSixService $shjSixService)
|
|
||||||
{
|
|
||||||
parent::__construct();
|
|
||||||
$this->shjSixService = $shjSixService;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* コンソールコマンドを実行
|
|
||||||
*
|
|
||||||
* 処理フロー:
|
|
||||||
* 1. サーバ死活監視(DBアクセス)
|
|
||||||
* 2. デバイス管理マスタを取得する
|
|
||||||
* 3. デバイス毎のハードウェア状態を取得する
|
|
||||||
* 4. プリンタ制御プログラムログを取得する
|
|
||||||
* 5. バッチ処理ログを作成する
|
|
||||||
* ※ 異常検出時は共通A処理(メール通知)を実行
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public function handle()
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
// 開始ログ出力
|
|
||||||
$startTime = now();
|
|
||||||
$this->info('SHJ-6 サーバ死活監視処理を開始します。');
|
|
||||||
|
|
||||||
Log::info('SHJ-6 サーバ死活監視処理開始', [
|
|
||||||
'start_time' => $startTime
|
|
||||||
]);
|
|
||||||
|
|
||||||
// SHJ-6監視処理実行
|
|
||||||
$result = $this->shjSixService->executeServerMonitoring();
|
|
||||||
|
|
||||||
// 処理結果確認
|
|
||||||
if ($result['success']) {
|
|
||||||
$endTime = now();
|
|
||||||
$this->info('SHJ-6 サーバ死活監視処理が正常に完了しました。');
|
|
||||||
$this->info("処理時間: {$startTime->diffInSeconds($endTime)}秒");
|
|
||||||
$this->info("監視結果: {$result['monitoring_summary']}");
|
|
||||||
|
|
||||||
// 警告がある場合は表示
|
|
||||||
if (!empty($result['warnings'])) {
|
|
||||||
$this->warn('警告が検出されました:');
|
|
||||||
foreach ($result['warnings'] as $warning) {
|
|
||||||
$this->warn("- {$warning}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Log::info('SHJ-6 サーバ死活監視処理完了', [
|
|
||||||
'end_time' => $endTime,
|
|
||||||
'duration_seconds' => $startTime->diffInSeconds($endTime),
|
|
||||||
'result' => $result
|
|
||||||
]);
|
|
||||||
|
|
||||||
return self::SUCCESS;
|
|
||||||
} else {
|
|
||||||
$this->error('SHJ-6 サーバ死活監視処理でエラーが発生しました: ' . $result['message']);
|
|
||||||
|
|
||||||
// エラー詳細があれば表示
|
|
||||||
if (!empty($result['error_details'])) {
|
|
||||||
$this->error('エラー詳細:');
|
|
||||||
foreach ($result['error_details'] as $detail) {
|
|
||||||
$this->error("- {$detail}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Log::error('SHJ-6 サーバ死活監視処理エラー', [
|
|
||||||
'error' => $result['message'],
|
|
||||||
'details' => $result['error_details'] ?? null
|
|
||||||
]);
|
|
||||||
|
|
||||||
return self::FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
$this->error('SHJ-6 サーバ死活監視処理で予期しないエラーが発生しました: ' . $e->getMessage());
|
|
||||||
Log::error('SHJ-6 サーバ死活監視処理例外エラー', [
|
|
||||||
'exception' => $e->getMessage(),
|
|
||||||
'trace' => $e->getTraceAsString()
|
|
||||||
]);
|
|
||||||
|
|
||||||
return self::FAILURE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,236 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Console\Commands;
|
|
||||||
|
|
||||||
use Illuminate\Console\Command;
|
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
use App\Services\ShjTenService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SHJ-10 売上集計処理コマンド
|
|
||||||
*
|
|
||||||
* 駐輪場の売上データを財政年度ベースで年次・月次集計する処理を実行する
|
|
||||||
* 4月開始の財政年度期間で計算するバックグラウンドバッチ処理
|
|
||||||
*/
|
|
||||||
class ShjTenCommand extends Command
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* コンソールコマンドの名前とシグネチャ
|
|
||||||
*
|
|
||||||
* 引数:
|
|
||||||
* - type: 集計種別 (yearly/monthly) (必須)
|
|
||||||
* - target: 集計対象 (必須)
|
|
||||||
* - yearly: 年度 (例: 2019)
|
|
||||||
* - monthly: 年月 (例: 2019/01)
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $signature = 'shj:10 {type : 集計種別(yearly/monthly)} {target : 集計対象(yearly:年度, monthly:年月)}';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* コンソールコマンドの説明
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $description = 'SHJ-10 売上集計処理 - 財政年度ベース年次/月次売上データ集計を実行';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SHJ-10サービスクラス
|
|
||||||
*
|
|
||||||
* @var ShjTenService
|
|
||||||
*/
|
|
||||||
protected $shjTenService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* コンストラクタ
|
|
||||||
*
|
|
||||||
* @param ShjTenService $shjTenService
|
|
||||||
*/
|
|
||||||
public function __construct(ShjTenService $shjTenService)
|
|
||||||
{
|
|
||||||
parent::__construct();
|
|
||||||
$this->shjTenService = $shjTenService;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* コンソールコマンドを実行
|
|
||||||
*
|
|
||||||
* 処理フロー:
|
|
||||||
* 1. パラメータ取得と検証
|
|
||||||
* 2. 財政年度期間設定
|
|
||||||
* 3. 売上集計処理実行
|
|
||||||
* 4. バッチログ作成
|
|
||||||
* 5. 処理結果返却
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public function handle()
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
// 開始ログ出力
|
|
||||||
$startTime = now();
|
|
||||||
$this->info('SHJ-10 売上集計処理を開始します。');
|
|
||||||
|
|
||||||
// 引数取得
|
|
||||||
$type = $this->argument('type');
|
|
||||||
$target = $this->argument('target');
|
|
||||||
|
|
||||||
Log::info('SHJ-10 売上集計処理開始', [
|
|
||||||
'start_time' => $startTime,
|
|
||||||
'type' => $type,
|
|
||||||
'target' => $target
|
|
||||||
]);
|
|
||||||
|
|
||||||
// パラメータ検証
|
|
||||||
if (!$this->validateParameters($type, $target)) {
|
|
||||||
$this->error('パラメータが不正です。');
|
|
||||||
return self::FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 財政年度期間設定
|
|
||||||
$fiscalPeriod = $this->determineFiscalPeriod($type, $target);
|
|
||||||
|
|
||||||
$this->info("集計種別: {$type}");
|
|
||||||
$this->info("集計対象: {$target}");
|
|
||||||
$this->info("財政期間: {$fiscalPeriod['start_date']} ~ {$fiscalPeriod['end_date']}");
|
|
||||||
|
|
||||||
// SHJ-10処理実行
|
|
||||||
$result = $this->shjTenService->executeFiscalEarningsAggregation($type, $target, $fiscalPeriod);
|
|
||||||
|
|
||||||
// 処理結果確認
|
|
||||||
if ($result['success']) {
|
|
||||||
$endTime = now();
|
|
||||||
$this->info('SHJ-10 売上集計処理が正常に完了しました。');
|
|
||||||
$this->info("処理時間: {$startTime->diffInSeconds($endTime)}秒");
|
|
||||||
$this->info("処理結果: 駐輪場数 {$result['processed_parks']}, 集計レコード数 {$result['summary_records']}");
|
|
||||||
|
|
||||||
Log::info('SHJ-10 売上集計処理完了', [
|
|
||||||
'end_time' => $endTime,
|
|
||||||
'duration_seconds' => $startTime->diffInSeconds($endTime),
|
|
||||||
'result' => $result
|
|
||||||
]);
|
|
||||||
|
|
||||||
return self::SUCCESS;
|
|
||||||
} else {
|
|
||||||
$this->error('SHJ-10 売上集計処理でエラーが発生しました: ' . $result['message']);
|
|
||||||
Log::error('SHJ-10 売上集計処理エラー', [
|
|
||||||
'error' => $result['message'],
|
|
||||||
'details' => $result['details'] ?? null
|
|
||||||
]);
|
|
||||||
|
|
||||||
return self::FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
$this->error('SHJ-10 売上集計処理で予期しないエラーが発生しました: ' . $e->getMessage());
|
|
||||||
Log::error('SHJ-10 売上集計処理例外エラー', [
|
|
||||||
'exception' => $e->getMessage(),
|
|
||||||
'trace' => $e->getTraceAsString()
|
|
||||||
]);
|
|
||||||
|
|
||||||
return self::FAILURE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* パラメータの妥当性を検証
|
|
||||||
*
|
|
||||||
* @param string $type 集計種別
|
|
||||||
* @param string $target 集計対象
|
|
||||||
* @return bool 検証結果
|
|
||||||
*/
|
|
||||||
private function validateParameters(string $type, string $target): bool
|
|
||||||
{
|
|
||||||
// 集計種別チェック
|
|
||||||
$allowedTypes = ['yearly', 'monthly'];
|
|
||||||
if (!in_array($type, $allowedTypes)) {
|
|
||||||
$this->error('集計種別は yearly, monthly のいずれかを指定してください。');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 集計対象形式チェック
|
|
||||||
if ($type === 'yearly') {
|
|
||||||
// 年度形式チェック (例: 2019)
|
|
||||||
if (!preg_match('/^\d{4}$/', $target)) {
|
|
||||||
$this->error('年次集計の場合、年度を4桁の数字で指定してください。(例: 2019)');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} elseif ($type === 'monthly') {
|
|
||||||
// 年月形式チェック (例: 2019/01)
|
|
||||||
if (!preg_match('/^\d{4}\/\d{2}$/', $target)) {
|
|
||||||
$this->error('月次集計の場合、年月をYYYY/MM形式で指定してください。(例: 2019/01)');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 月の範囲チェック (01-12)
|
|
||||||
$parts = explode('/', $target);
|
|
||||||
$month = (int)$parts[1];
|
|
||||||
if ($month < 1 || $month > 12) {
|
|
||||||
$this->error('月は01から12までの範囲で指定してください。');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 財政年度期間を決定
|
|
||||||
*
|
|
||||||
* 財政年度は4月開始(Config設定可能)
|
|
||||||
* - yearly 2019: 2019年4月1日 ~ 2020年3月31日
|
|
||||||
* - monthly 2019/01: 2019年1月1日 ~ 2019年1月31日
|
|
||||||
*
|
|
||||||
* @param string $type 集計種別
|
|
||||||
* @param string $target 集計対象
|
|
||||||
* @return array 財政期間情報
|
|
||||||
*/
|
|
||||||
private function determineFiscalPeriod(string $type, string $target): array
|
|
||||||
{
|
|
||||||
$fiscalStartMonth = 4; // 財政年度開始月(4月)
|
|
||||||
|
|
||||||
if ($type === 'yearly') {
|
|
||||||
$year = (int)$target;
|
|
||||||
|
|
||||||
// 財政年度期間計算
|
|
||||||
$startDate = sprintf('%04d-%02d-01', $year, $fiscalStartMonth);
|
|
||||||
$endDate = sprintf('%04d-%02d-%02d', $year + 1, $fiscalStartMonth - 1,
|
|
||||||
date('t', strtotime(sprintf('%04d-%02d-01', $year + 1, $fiscalStartMonth - 1))));
|
|
||||||
|
|
||||||
return [
|
|
||||||
'type' => 'yearly',
|
|
||||||
'fiscal_year' => $year,
|
|
||||||
'start_date' => $startDate,
|
|
||||||
'end_date' => $endDate,
|
|
||||||
'summary_type' => 1, // 年次
|
|
||||||
'target_label' => "{$year}年度"
|
|
||||||
];
|
|
||||||
|
|
||||||
} elseif ($type === 'monthly') {
|
|
||||||
$parts = explode('/', $target);
|
|
||||||
$year = (int)$parts[0];
|
|
||||||
$month = (int)$parts[1];
|
|
||||||
|
|
||||||
// 指定月の期間計算
|
|
||||||
$startDate = sprintf('%04d-%02d-01', $year, $month);
|
|
||||||
$endDate = sprintf('%04d-%02d-%02d', $year, $month,
|
|
||||||
date('t', strtotime($startDate)));
|
|
||||||
|
|
||||||
// 該当する財政年度を計算
|
|
||||||
$fiscalYear = $month >= $fiscalStartMonth ? $year : $year - 1;
|
|
||||||
|
|
||||||
return [
|
|
||||||
'type' => 'monthly',
|
|
||||||
'fiscal_year' => $fiscalYear,
|
|
||||||
'target_year' => $year,
|
|
||||||
'target_month' => $month,
|
|
||||||
'start_date' => $startDate,
|
|
||||||
'end_date' => $endDate,
|
|
||||||
'summary_type' => 2, // 月次
|
|
||||||
'target_label' => "{$year}年{$month}月"
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new \InvalidArgumentException("不正な集計種別: {$type}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,177 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Console\Commands;
|
|
||||||
|
|
||||||
use Illuminate\Console\Command;
|
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
use App\Services\ShjTwelveService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SHJ-12 未払い者通知処理コマンド
|
|
||||||
*
|
|
||||||
* 定期契約マスタより未払い者を取得し、通知処理またはオペレーターキュー追加を実行する
|
|
||||||
* バックグラウンドで実行される定期バッチ処理
|
|
||||||
*/
|
|
||||||
class ShjTwelveCommand extends Command
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* コンソールコマンドの名前とシグネチャ
|
|
||||||
*
|
|
||||||
* 引数: なし
|
|
||||||
* オプション: なし
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $signature = 'shj:12';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* コンソールコマンドの説明
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $description = 'SHJ-12 未払い者通知処理 - 定期契約マスタより未払い者を取得し通知処理を実行';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SHJ-12サービスクラス
|
|
||||||
*
|
|
||||||
* @var ShjTwelveService
|
|
||||||
*/
|
|
||||||
protected $shjTwelveService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* コンストラクタ
|
|
||||||
*
|
|
||||||
* @param ShjTwelveService $shjTwelveService
|
|
||||||
*/
|
|
||||||
public function __construct(ShjTwelveService $shjTwelveService)
|
|
||||||
{
|
|
||||||
parent::__construct();
|
|
||||||
$this->shjTwelveService = $shjTwelveService;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* コンソールコマンドを実行
|
|
||||||
*
|
|
||||||
* 処理フロー:
|
|
||||||
* 1. 定期契約マスタより未払い者を取得する
|
|
||||||
* 2. 取得件数判定
|
|
||||||
* 3. 未払い者への通知、またはオペレーターキュー追加処理
|
|
||||||
* 4. バッチ処理ログを作成する
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public function handle()
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
// 開始ログ出力
|
|
||||||
$startTime = now();
|
|
||||||
$this->info('SHJ-12 未払い者通知処理を開始します。');
|
|
||||||
Log::info('SHJ-12 未払い者通知処理開始', [
|
|
||||||
'start_time' => $startTime
|
|
||||||
]);
|
|
||||||
|
|
||||||
// 【処理1】定期契約マスタより未払い者を取得する
|
|
||||||
$this->info('【処理1】定期契約マスタより未払い者を取得しています...');
|
|
||||||
$unpaidUsers = $this->shjTwelveService->getUnpaidUsers();
|
|
||||||
|
|
||||||
// 【判断1】取得件数判定
|
|
||||||
$unpaidCount = count($unpaidUsers);
|
|
||||||
$this->info("取得件数: {$unpaidCount}件");
|
|
||||||
|
|
||||||
if ($unpaidCount === 0) {
|
|
||||||
// 未払い者対象なしの結果を設定する
|
|
||||||
$this->info('未払い者対象なしのため処理を終了します。');
|
|
||||||
|
|
||||||
// バッチ処理ログを作成
|
|
||||||
$this->shjTwelveService->createBatchLog(
|
|
||||||
'success',
|
|
||||||
[],
|
|
||||||
'未払い者対象なし',
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0
|
|
||||||
);
|
|
||||||
|
|
||||||
Log::info('SHJ-12 未払い者通知処理完了(対象なし)', [
|
|
||||||
'end_time' => now(),
|
|
||||||
'duration_seconds' => $startTime->diffInSeconds(now())
|
|
||||||
]);
|
|
||||||
|
|
||||||
return self::SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 【処理2】未払い者への通知、またはオペレーターキュー追加処理
|
|
||||||
$this->info('【処理2】未払い者への通知処理を実行しています...');
|
|
||||||
$processResult = $this->shjTwelveService->processUnpaidUserNotifications($unpaidUsers);
|
|
||||||
|
|
||||||
// 処理結果確認
|
|
||||||
if ($processResult['success']) {
|
|
||||||
$endTime = now();
|
|
||||||
$this->info('SHJ-12 未払い者通知処理が正常に完了しました。');
|
|
||||||
$this->info("処理時間: {$startTime->diffInSeconds($endTime)}秒");
|
|
||||||
$this->info("処理対象件数: {$unpaidCount}件");
|
|
||||||
$this->info("通知送信件数: {$processResult['notification_count']}件");
|
|
||||||
$this->info("オペレーターキュー追加件数: {$processResult['queue_count']}件");
|
|
||||||
|
|
||||||
// 【処理3】バッチ処理ログを作成する
|
|
||||||
$this->shjTwelveService->createBatchLog(
|
|
||||||
'success',
|
|
||||||
$processResult['parameters'],
|
|
||||||
'未払い者通知処理完了',
|
|
||||||
$unpaidCount,
|
|
||||||
$processResult['notification_count'] + $processResult['queue_count'],
|
|
||||||
0
|
|
||||||
);
|
|
||||||
|
|
||||||
Log::info('SHJ-12 未払い者通知処理完了', [
|
|
||||||
'end_time' => $endTime,
|
|
||||||
'duration_seconds' => $startTime->diffInSeconds($endTime),
|
|
||||||
'processed_count' => $unpaidCount,
|
|
||||||
'notification_count' => $processResult['notification_count'],
|
|
||||||
'queue_count' => $processResult['queue_count']
|
|
||||||
]);
|
|
||||||
|
|
||||||
return self::SUCCESS;
|
|
||||||
} else {
|
|
||||||
$this->error('SHJ-12 未払い者通知処理でエラーが発生しました: ' . $processResult['message']);
|
|
||||||
|
|
||||||
// エラー時のバッチログ作成
|
|
||||||
$this->shjTwelveService->createBatchLog(
|
|
||||||
'error',
|
|
||||||
$processResult['parameters'] ?? [],
|
|
||||||
$processResult['message'],
|
|
||||||
$unpaidCount,
|
|
||||||
$processResult['notification_count'] ?? 0,
|
|
||||||
1
|
|
||||||
);
|
|
||||||
|
|
||||||
Log::error('SHJ-12 未払い者通知処理エラー', [
|
|
||||||
'error' => $processResult['message'],
|
|
||||||
'details' => $processResult['details'] ?? null
|
|
||||||
]);
|
|
||||||
|
|
||||||
return self::FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
$this->error('SHJ-12 未払い者通知処理で予期しないエラーが発生しました: ' . $e->getMessage());
|
|
||||||
|
|
||||||
// 例外時のバッチログ作成
|
|
||||||
$this->shjTwelveService->createBatchLog(
|
|
||||||
'error',
|
|
||||||
[],
|
|
||||||
'システムエラー: ' . $e->getMessage(),
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
1
|
|
||||||
);
|
|
||||||
|
|
||||||
Log::error('SHJ-12 未払い者通知処理例外エラー', [
|
|
||||||
'exception' => $e->getMessage(),
|
|
||||||
'trace' => $e->getTraceAsString()
|
|
||||||
]);
|
|
||||||
|
|
||||||
return self::FAILURE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -12,12 +12,9 @@ class CityController extends Controller
|
|||||||
{
|
{
|
||||||
public function list(Request $request)
|
public function list(Request $request)
|
||||||
{
|
{
|
||||||
$inputs = [
|
$sort = $request->input('sort', 'city_id');
|
||||||
'isMethodPost' => $request->isMethod('post'),
|
$sortType = $request->input('sort_type', 'asc');
|
||||||
'sort' => $request->input('sort', ''),
|
$page = $request->get('page', 1);
|
||||||
'sort_type' => $request->input('sort_type', ''),
|
|
||||||
'page' => $request->get('page', 1),
|
|
||||||
];
|
|
||||||
|
|
||||||
$query = City::query();
|
$query = City::query();
|
||||||
|
|
||||||
@ -25,17 +22,28 @@ class CityController extends Controller
|
|||||||
$query->where('city_name', 'like', '%' . $request->input('city_name') . '%');
|
$query->where('city_name', 'like', '%' . $request->input('city_name') . '%');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($inputs['sort'])) {
|
// 排序处理
|
||||||
$query->orderBy($inputs['sort'], $inputs['sort_type'] ?? 'asc');
|
if (!empty($sort)) {
|
||||||
|
$query->orderBy($sort, $sortType);
|
||||||
}
|
}
|
||||||
|
|
||||||
$inputs['list'] = $query->paginate(20);
|
$list = $query->paginate(20);
|
||||||
|
|
||||||
if ($inputs['list']->total() > 0 && $inputs['page'] > $inputs['list']->lastPage()) {
|
// 页码越界处理
|
||||||
return redirect()->route('city');
|
if ($list->total() > 0 && $page > $list->lastPage()) {
|
||||||
|
return redirect()->route('city', [
|
||||||
|
'sort' => $sort,
|
||||||
|
'sort_type' => $sortType,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return view('admin.CityMaster.list', $inputs);
|
return view('admin.CityMaster.list', [
|
||||||
|
'isMethodPost' => $request->isMethod('post'),
|
||||||
|
'sort' => $sort,
|
||||||
|
'sort_type' => $sortType,
|
||||||
|
'list' => $list,
|
||||||
|
'page' => $page,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function add(Request $request)
|
public function add(Request $request)
|
||||||
@ -49,9 +57,21 @@ class CityController extends Controller
|
|||||||
|
|
||||||
if ($request->isMethod('POST')) {
|
if ($request->isMethod('POST')) {
|
||||||
$rules = [
|
$rules = [
|
||||||
'city_name' => 'required|string|max:255',
|
'city_name' => ['required', 'string', 'max:10', 'regex:/^[^ -~。-゚]+$/u'],
|
||||||
|
'print_layout' => ['required', 'string', 'max:10', 'regex:/^[^ -~。-゚]+$/u'],
|
||||||
|
'city_user' => ['required', 'string', 'max:10', 'regex:/^[^ -~。-゚]+$/u'],
|
||||||
|
'city_remarks' => ['nullable', 'string', 'max:20'],
|
||||||
];
|
];
|
||||||
$validator = Validator::make($request->all(), $rules);
|
$messages = [
|
||||||
|
'city_name.required' => '市区名は必須です。',
|
||||||
|
'city_name.regex' => '市区名は全角で入力してください。',
|
||||||
|
'print_layout.required' => '印字レイアウトファイルは必須です。',
|
||||||
|
'print_layout.regex' => '印字レイアウトファイルは全角で入力してください。',
|
||||||
|
'city_user.required' => '顧客M入力不要フィールドIDは必須です。',
|
||||||
|
'city_user.regex' => '顧客M入力不要フィールドIDは全角で入力してください。',
|
||||||
|
'city_remarks.max' => '備考は20文字以内で入力してください。',
|
||||||
|
];
|
||||||
|
$validator = Validator::make($request->all(), $rules, $messages);
|
||||||
|
|
||||||
$inputs = array_merge($inputs, $request->all());
|
$inputs = array_merge($inputs, $request->all());
|
||||||
|
|
||||||
@ -91,9 +111,21 @@ class CityController extends Controller
|
|||||||
|
|
||||||
if ($request->isMethod('POST')) {
|
if ($request->isMethod('POST')) {
|
||||||
$rules = [
|
$rules = [
|
||||||
'city_name' => 'required|string|max:255',
|
'city_name' => ['required', 'string', 'max:10', 'regex:/^[^ -~。-゚]+$/u'],
|
||||||
|
'print_layout' => ['required', 'string', 'max:10', 'regex:/^[^ -~。-゚]+$/u'],
|
||||||
|
'city_user' => ['required', 'string', 'max:10', 'regex:/^[^ -~。-゚]+$/u'],
|
||||||
|
'city_remarks' => ['nullable', 'string', 'max:20'],
|
||||||
];
|
];
|
||||||
$validator = Validator::make($request->all(), $rules);
|
$messages = [
|
||||||
|
'city_name.required' => '市区名は必須です。',
|
||||||
|
'city_name.regex' => '市区名は全角で入力してください。',
|
||||||
|
'print_layout.required' => '印字レイアウトファイルは必須です。',
|
||||||
|
'print_layout.regex' => '印字レイアウトファイルは全角で入力してください。',
|
||||||
|
'city_user.required' => '顧客M入力不要フィールドIDは必須です。',
|
||||||
|
'city_user.regex' => '顧客M入力不要フィールドIDは全角で入力してください。',
|
||||||
|
'city_remarks.max' => '備考は20文字以内で入力してください。',
|
||||||
|
];
|
||||||
|
$validator = Validator::make($request->all(), $rules, $messages);
|
||||||
|
|
||||||
if (!$validator->fails()) {
|
if (!$validator->fails()) {
|
||||||
$city->fill($request->only([
|
$city->fill($request->only([
|
||||||
@ -130,10 +162,13 @@ class CityController extends Controller
|
|||||||
public function delete(Request $request)
|
public function delete(Request $request)
|
||||||
{
|
{
|
||||||
$arr_pk = $request->get('pk');
|
$arr_pk = $request->get('pk');
|
||||||
if ($arr_pk && City::destroy($arr_pk)) {
|
if (!$arr_pk) {
|
||||||
|
return redirect()->route('city')->with('error', __('削除する市区を選択してください。'));
|
||||||
|
}
|
||||||
|
if (City::destroy($arr_pk)) {
|
||||||
return redirect()->route('city')->with('success', __("削除が完了しました。"));
|
return redirect()->route('city')->with('success', __("削除が完了しました。"));
|
||||||
} else {
|
} else {
|
||||||
return redirect()->route('city')->with('error', __('削除に失敗しました。'));
|
return redirect()->route('city')->with('error', __('削除に失敗しました。'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -17,79 +17,83 @@ class ContractAllowableCityController extends Controller
|
|||||||
* 一覧表示
|
* 一覧表示
|
||||||
*/
|
*/
|
||||||
public function list(Request $request)
|
public function list(Request $request)
|
||||||
{
|
{
|
||||||
$inputs = $request->all();
|
$inputs = $request->all();
|
||||||
$inputs['isMethodPost'] = $request->isMethod('post');
|
$inputs['isMethodPost'] = $request->isMethod('post');
|
||||||
|
|
||||||
// 解除処理
|
// 解除処理
|
||||||
if ($request->isMethod('post') && $request->input('action') === 'unlink') {
|
if ($request->isMethod('post') && $request->input('action') === 'unlink') {
|
||||||
$query = ContractAllowableCity::query();
|
// バリデーション:解除条件が1つも入力されていない場合はエラー
|
||||||
|
if (
|
||||||
|
!$request->filled('contract_allowable_city_id')
|
||||||
|
&& !$request->filled('city_id')
|
||||||
|
&& !$request->filled('contract_allowable_city_name')
|
||||||
|
&& !$request->filled('park_id')
|
||||||
|
) {
|
||||||
|
return back()->withErrors(['解除条件を1つ以上入力してください。']);
|
||||||
|
}
|
||||||
|
|
||||||
if ($request->filled('contract_allowable_city_id')) {
|
$query = ContractAllowableCity::query();
|
||||||
$query->where('contract_allowable_city_id', $request->contract_allowable_city_id);
|
|
||||||
}
|
if ($request->filled('contract_allowable_city_id')) {
|
||||||
if ($request->filled('city_id')) {
|
$query->where('contract_allowable_city_id', $request->contract_allowable_city_id);
|
||||||
$query->where('city_id', $request->city_id);
|
}
|
||||||
}
|
if ($request->filled('city_id')) {
|
||||||
if ($request->filled('contract_allowable_city_name')) {
|
$query->where('city_id', $request->city_id);
|
||||||
$query->where('contract_allowable_city_name', 'like', '%' . $request->contract_allowable_city_name . '%');
|
}
|
||||||
}
|
if ($request->filled('contract_allowable_city_name')) {
|
||||||
if ($request->filled('park_id')) {
|
$query->where('contract_allowable_city_name', 'like', '%' . $request->contract_allowable_city_name . '%');
|
||||||
$query->where('park_id', $request->park_id);
|
}
|
||||||
|
if ($request->filled('park_id')) {
|
||||||
|
$query->where('park_id', $request->park_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
$count = $query->delete();
|
||||||
|
return redirect()->route('contract_allowable_cities')->with('success', '解除しました');
|
||||||
}
|
}
|
||||||
|
|
||||||
$records = $query->get();
|
// 通常の絞り込み処理
|
||||||
foreach ($records as $record) {
|
$list = ContractAllowableCity::search($inputs);
|
||||||
$record->delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
return redirect()->route('contract_allowable_cities')->with('success', '解除しました');
|
return view('admin.contract_allowable_cities.list', [
|
||||||
|
'list' => $list,
|
||||||
|
'inputs' => $inputs,
|
||||||
|
'sort' => $inputs['sort'] ?? '',
|
||||||
|
'sort_type' => $inputs['sort_type'] ?? '',
|
||||||
|
'cityList' => City::getList(),
|
||||||
|
'parkList' => Park::getList(),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 通常の絞り込み処理
|
|
||||||
$list = ContractAllowableCity::search($inputs);
|
|
||||||
|
|
||||||
return view('admin.contract_allowable_cities.list', [
|
|
||||||
'list' => $list,
|
|
||||||
'inputs' => $inputs,
|
|
||||||
'sort' => $inputs['sort'] ?? '',
|
|
||||||
'sort_type' => $inputs['sort_type'] ?? '',
|
|
||||||
'cityList' => City::getList(),
|
|
||||||
'parkList' => Park::getList(),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 新規登録
|
* 新規登録
|
||||||
*/
|
*/
|
||||||
public function add(Request $request)
|
public function add(Request $request)
|
||||||
{
|
{
|
||||||
if ($request->isMethod('post')) {
|
if ($request->isMethod('post')) {
|
||||||
$request->validate([
|
$validated = $request->validate([
|
||||||
'city_id' => 'required|integer',
|
'city_id' => 'required|integer',
|
||||||
'contract_allowable_city_name' => 'required|string|max:20',
|
'contract_allowable_city_name' => 'required|string|max:20',
|
||||||
'park_id' => 'required|integer',
|
'park_id' => 'required|integer',
|
||||||
'same_district_flag' => 'required|integer',
|
'same_district_flag' => 'required|integer',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$data = $request->all();
|
$validated['operator_id'] = Auth::user()->ope_id;
|
||||||
$data['operator_id'] = Auth::user()->ope_id;
|
|
||||||
|
|
||||||
ContractAllowableCity::create($data);
|
ContractAllowableCity::create($validated);
|
||||||
|
|
||||||
return redirect()->route('contract_allowable_cities')->with('success', '登録しました');
|
return redirect()->route('contract_allowable_cities')
|
||||||
|
->with('success', '登録しました。');
|
||||||
}
|
}
|
||||||
|
|
||||||
return view('admin.contract_allowable_cities.add', [
|
return view('admin.contract_allowable_cities.add', [
|
||||||
'record' => null,
|
'record' => null,
|
||||||
'cityList' => City::getList(),
|
'cityList' => City::getList(),
|
||||||
'parkList' => Park::getList(),
|
'parkList' => Park::getList(),
|
||||||
'contractAllowableCityList' => ContractAllowableCity::getList(),
|
|
||||||
'mode' => 'add'
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 編集
|
* 編集
|
||||||
*/
|
*/
|
||||||
@ -97,95 +101,104 @@ class ContractAllowableCityController extends Controller
|
|||||||
{
|
{
|
||||||
$record = ContractAllowableCity::getByPk($id);
|
$record = ContractAllowableCity::getByPk($id);
|
||||||
if (!$record) {
|
if (!$record) {
|
||||||
return redirect()->route('contract_allowable_cities')->with('error', 'データが存在しません');
|
return redirect()->route('contract_allowable_cities')
|
||||||
|
->with('error', 'データが存在しません');
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($request->isMethod('post')) {
|
if ($request->isMethod('post')) {
|
||||||
$request->validate([
|
$validated = $request->validate([
|
||||||
'city_id' => 'required|integer',
|
'city_id' => 'required|integer',
|
||||||
'contract_allowable_city_name' => 'required|string|max:20',
|
'contract_allowable_city_name' => 'required|string|max:20',
|
||||||
'park_id' => 'required|integer',
|
'park_id' => 'required|integer',
|
||||||
'same_district_flag' => 'required|integer',
|
'same_district_flag' => 'required|integer',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$record->fill($request->all());
|
$record->fill($validated);
|
||||||
$record->operator_id = Auth::user()->ope_id;
|
$record->operator_id = Auth::user()->ope_id;
|
||||||
$record->save();
|
$record->save();
|
||||||
|
|
||||||
return redirect()->route('contract_allowable_cities')->with('success', '更新しました');
|
return redirect()->route('contract_allowable_cities')
|
||||||
|
->with('success', '更新しました。');
|
||||||
}
|
}
|
||||||
|
|
||||||
return view('admin.contract_allowable_cities.edit', [
|
return view('admin.contract_allowable_cities.edit', [
|
||||||
'record' => $record,
|
'record' => $record,
|
||||||
'cities' => City::getList(),
|
|
||||||
'parks' => Park::getList(),
|
|
||||||
'mode' => 'edit'
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 詳細参照(表示のみ)
|
|
||||||
*/
|
|
||||||
public function info($id)
|
|
||||||
{
|
|
||||||
$record = ContractAllowableCity::getByPk($id);
|
|
||||||
if (!$record) {
|
|
||||||
return redirect()->route('contract_allowable_cities')->with('error', 'データが存在しません');
|
|
||||||
}
|
|
||||||
|
|
||||||
return view('admin.contract_allowable_cities.edit', [
|
|
||||||
'record' => $record,
|
|
||||||
'cityList' => City::getList(),
|
'cityList' => City::getList(),
|
||||||
'parkList' => Park::getList(),
|
'parkList' => Park::getList(),
|
||||||
'mode' => 'info'
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 一括削除
|
* 一括削除(単一・複数対応)
|
||||||
*/
|
*/
|
||||||
public function delete(Request $request)
|
public function delete(Request $request)
|
||||||
{
|
{
|
||||||
if ($request->has('id')) {
|
// バリデーション:'id'は必須、配列の場合は各要素が整数
|
||||||
ContractAllowableCity::deleteByPk($request->id);
|
$request->validate([
|
||||||
return redirect()->route('contract_allowable_cities')->with('success', '削除しました');
|
'id' => 'required',
|
||||||
}
|
'id.*' => 'integer',
|
||||||
|
]);
|
||||||
|
|
||||||
return redirect()->route('contract_allowable_cities')->with('error', '削除対象が見つかりません');
|
// idを配列化(単一でも複数でも対応)
|
||||||
|
$ids = (array)$request->input('id');
|
||||||
|
|
||||||
|
// 削除処理
|
||||||
|
// ContractAllowableCity::destroy($ids) が使える場合
|
||||||
|
$deleted = ContractAllowableCity::destroy($ids);
|
||||||
|
|
||||||
|
// 削除件数でメッセージ分岐
|
||||||
|
if ($deleted > 0) {
|
||||||
|
return redirect()->route('contract_allowable_cities')->with('success', '削除しました。');
|
||||||
|
} else {
|
||||||
|
return redirect()->route('contract_allowable_cities')->with('error', '削除に失敗しました。');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CSVエクスポート
|
* 契約許容市区マスタ CSVエクスポート
|
||||||
*/
|
*/
|
||||||
public function export(Request $request)
|
public function export(Request $request)
|
||||||
{
|
{
|
||||||
$filename = 'contract_allowable_cities_' . now()->format('Ymd_His') . '.csv';
|
$filename = '契約許容市区マスタ_' . now()->format('YmdHis') . '.csv';
|
||||||
|
|
||||||
|
// 検索条件でデータ取得
|
||||||
$list = ContractAllowableCity::search($request->all());
|
$list = ContractAllowableCity::search($request->all());
|
||||||
|
|
||||||
|
// CSVファイル作成
|
||||||
|
$file = fopen($filename, 'w+');
|
||||||
|
fwrite($file, "\xEF\xBB\xBF"); // UTF-8 BOM追加
|
||||||
|
|
||||||
|
// ヘッダー行
|
||||||
|
$columns = [
|
||||||
|
'契約許容市区ID',
|
||||||
|
'市区ID',
|
||||||
|
'許容市区名',
|
||||||
|
'駐輪場ID',
|
||||||
|
'隣接区フラグ'
|
||||||
|
];
|
||||||
|
fputcsv($file, $columns);
|
||||||
|
|
||||||
|
// データ行
|
||||||
|
foreach ($list as $item) {
|
||||||
|
fputcsv($file, [
|
||||||
|
$item->contract_allowable_city_id,
|
||||||
|
$item->city_id,
|
||||||
|
$item->contract_allowable_city_name,
|
||||||
|
$item->park_id,
|
||||||
|
$item->same_district_flag == 0 ? '隣接市' : 'その他',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose($file);
|
||||||
|
|
||||||
|
// ヘッダー設定
|
||||||
$headers = [
|
$headers = [
|
||||||
'Content-Type' => 'text/csv',
|
"Content-Type" => "text/csv; charset=UTF-8",
|
||||||
'Content-Disposition' => "attachment; filename=\"$filename\"",
|
"Content-Disposition" => "attachment; filename={$filename}",
|
||||||
];
|
];
|
||||||
|
|
||||||
return new StreamedResponse(function () use ($list) {
|
// ダウンロード後に一時ファイル削除
|
||||||
$handle = fopen('php://output', 'w');
|
return response()->download($filename, $filename, $headers)->deleteFileAfterSend(true);
|
||||||
|
|
||||||
// ヘッダー
|
|
||||||
fputcsv($handle, ['契約許容市区ID', '市区ID', '許容市区名', '駐輪場ID', '隣接区フラグ']);
|
|
||||||
|
|
||||||
foreach ($list as $item) {
|
|
||||||
fputcsv($handle, [
|
|
||||||
$item->contract_allowable_city_id,
|
|
||||||
$item->city_id,
|
|
||||||
$item->contract_allowable_city_name,
|
|
||||||
$item->park_id,
|
|
||||||
$item->same_district_flag == 0 ? '隣接市' : 'その他'
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose($handle);
|
|
||||||
}, 200, $headers);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -13,7 +13,9 @@ class ContractorController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function list(Request $request)
|
public function list(Request $request)
|
||||||
{
|
{
|
||||||
|
// ベースクエリを構築
|
||||||
$q = DB::table('regular_contract as rc')
|
$q = DB::table('regular_contract as rc')
|
||||||
|
->leftJoin('user as u','rc.user_id','=','u.user_id')
|
||||||
->select([
|
->select([
|
||||||
'rc.contract_id',
|
'rc.contract_id',
|
||||||
'rc.contract_qr_id',
|
'rc.contract_qr_id',
|
||||||
@ -32,10 +34,12 @@ class ContractorController extends Controller
|
|||||||
'rc.contract_permission',
|
'rc.contract_permission',
|
||||||
'rc.contract_manual',
|
'rc.contract_manual',
|
||||||
'rc.contract_notice',
|
'rc.contract_notice',
|
||||||
|
'rc.update_flag',
|
||||||
'p.park_name',
|
'p.park_name',
|
||||||
'u.user_name',
|
'u.user_name',
|
||||||
'u.user_phonetic',
|
'u.user_phonetic',
|
||||||
'u.user_mobile',
|
'u.user_mobile',
|
||||||
|
'u.user_seq',
|
||||||
'u.user_homephone',
|
'u.user_homephone',
|
||||||
'u.user_primemail',
|
'u.user_primemail',
|
||||||
'u.user_gender',
|
'u.user_gender',
|
||||||
@ -52,37 +56,125 @@ class ContractorController extends Controller
|
|||||||
'u.user_workplace',
|
'u.user_workplace',
|
||||||
'u.user_school',
|
'u.user_school',
|
||||||
'u.user_remarks',
|
'u.user_remarks',
|
||||||
// 他に必要なカラムもここに追加
|
'u.user_tag_serial_64',
|
||||||
|
'u.user_reduction',
|
||||||
|
DB::raw('rc.user_securitynum as crime_prevention'),
|
||||||
|
DB::raw('rc.contract_seal_issue as seal_issue_count'),
|
||||||
|
DB::raw("CASE rc.enable_months
|
||||||
|
WHEN 1 THEN '月極(1ヶ月)'
|
||||||
|
WHEN 3 THEN '3ヶ月'
|
||||||
|
WHEN 6 THEN '6ヶ月'
|
||||||
|
WHEN 12 THEN '年'
|
||||||
|
ELSE CONCAT(rc.enable_months, 'ヶ月') END as ticket_type"),
|
||||||
|
DB::raw('ps.psection_subject as vehicle_type'),
|
||||||
|
// 利用者分類のラベル(usertype テーブルの subject を取得)
|
||||||
|
DB::raw('ut.usertype_subject1 as user_category1'),
|
||||||
|
DB::raw('ut.usertype_subject2 as user_category2'),
|
||||||
|
DB::raw('ut.usertype_subject3 as user_category3'),
|
||||||
])
|
])
|
||||||
->leftJoin('park as p', 'rc.park_id', '=', 'p.park_id')
|
->leftJoin('park as p', 'rc.park_id', '=', 'p.park_id')
|
||||||
->leftJoin('user as u', 'rc.user_id', '=', 'u.user_id');
|
->leftJoin('psection as ps', 'rc.psection_id', '=', 'ps.psection_id')
|
||||||
|
->leftJoin('usertype as ut', 'u.user_categoryid', '=', 'ut.user_categoryid');
|
||||||
|
|
||||||
// 検索条件例
|
// ===== 絞り込み条件 =====
|
||||||
if ($request->filled('contract_id')) {
|
|
||||||
$q->where('rc.contract_id', $request->input('contract_id'));
|
// 駐輪場で絞る(完全一致)
|
||||||
}
|
if ($request->filled('park_id')) {
|
||||||
if ($request->filled('name')) {
|
$q->where('rc.park_id', $request->park_id);
|
||||||
$q->where('u.user_name', 'like', '%' . $request->input('name') . '%');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// タグ・QR(完全一致、空白なら絞り込まない)
|
// 利用者IDで絞る(完全一致)
|
||||||
if ($request->filled('tag_qr_flag') && $request->input('tag_qr_flag') !== '') {
|
if ($request->filled('user_id')) {
|
||||||
$q->where('rc.tag_qr_flag', $request->input('tag_qr_flag'));
|
$q->where('rc.user_id', $request->user_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ソート処理
|
// 利用者分類で絞る(※ select の value を user_categoryid にしているため、user テーブルのカラムで比較)
|
||||||
$sort = $request->input('sort', 'rc.contract_id');
|
if ($request->filled('user_category1')) {
|
||||||
$sortType = $request->input('sort_type', 'desc');
|
$q->where('u.user_categoryid', $request->user_category1);
|
||||||
// カラム名のバリデーション(必要に応じて拡張)
|
|
||||||
$allowSorts = ['rc.contract_id'];
|
|
||||||
if (!in_array($sort, $allowSorts)) {
|
|
||||||
$sort = 'rc.contract_id';
|
|
||||||
}
|
}
|
||||||
$sortType = ($sortType === 'asc') ? 'asc' : 'desc';
|
|
||||||
|
|
||||||
$rows = $q->orderBy($sort, $sortType)->paginate(20)->withQueryString();
|
// タグシリアル64進で部分一致検索
|
||||||
|
if ($request->filled('user_tag_serial_64')) {
|
||||||
|
$val = $request->user_tag_serial_64;
|
||||||
|
$q->where('u.user_tag_serial_64','like','%'.$val.'%');
|
||||||
|
}
|
||||||
|
|
||||||
return view('admin.contractor.list', compact('rows', 'sort', 'sortType'));
|
// 有効期限で絞る(指定日以前を抽出する= <= を使用)
|
||||||
|
if ($request->filled('contract_periode')) {
|
||||||
|
$raw = trim($request->contract_periode);
|
||||||
|
$norm = str_replace('/', '-', $raw); // スラッシュ入力を許容
|
||||||
|
try {
|
||||||
|
$target = \Carbon\Carbon::parse($norm)->format('Y-m-d');
|
||||||
|
// 指定日「以前」を含める
|
||||||
|
$q->whereDate('rc.contract_periode', '<=', $target);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
// 無効な日付は無視する
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// フリガナで部分一致
|
||||||
|
if ($request->filled('user_phonetic')) {
|
||||||
|
$q->where('u.user_phonetic', 'like', '%' . $request->user_phonetic . '%');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 携帯電話で部分一致
|
||||||
|
if ($request->filled('user_mobile')) {
|
||||||
|
$q->where('u.user_mobile', 'like', '%' . $request->user_mobile . '%');
|
||||||
|
}
|
||||||
|
|
||||||
|
// メールアドレスで部分一致
|
||||||
|
if ($request->filled('user_primemail')) {
|
||||||
|
$q->where('u.user_primemail', 'like', '%' . $request->user_primemail . '%');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 勤務先で部分一致
|
||||||
|
if ($request->filled('user_workplace')) {
|
||||||
|
$q->where('u.user_workplace', 'like', '%' . $request->user_workplace . '%');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 学校で部分一致
|
||||||
|
if ($request->filled('user_school')) {
|
||||||
|
$q->where('u.user_school', 'like', '%' . $request->user_school . '%');
|
||||||
|
}
|
||||||
|
|
||||||
|
// タグ・QR フラグで絞る(空文字は無視)
|
||||||
|
if ($request->filled('tag_qr_flag') && $request->tag_qr_flag !== '') {
|
||||||
|
$q->where('rc.tag_qr_flag', $request->tag_qr_flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== ソート処理 =====
|
||||||
|
// 指定があればその列でソート、なければデフォルトで契約IDの昇順
|
||||||
|
$sort = $request->input('sort'); // null 許容
|
||||||
|
$sortType = $request->input('sort_type','asc');
|
||||||
|
|
||||||
|
$allowSorts = [
|
||||||
|
'rc.contract_id',
|
||||||
|
'rc.user_id',
|
||||||
|
'u.user_name',
|
||||||
|
'rc.tag_qr_flag',
|
||||||
|
'p.park_name',
|
||||||
|
];
|
||||||
|
if ($sort && in_array($sort, $allowSorts)) {
|
||||||
|
$sortType = $sortType === 'desc' ? 'desc' : 'asc';
|
||||||
|
$q->orderBy($sort, $sortType);
|
||||||
|
} else {
|
||||||
|
// デフォルトソート
|
||||||
|
$sort = null;
|
||||||
|
$sortType = null;
|
||||||
|
$q->orderBy('rc.contract_id','asc');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ページネーション(クエリ文字列を引き継ぐ)
|
||||||
|
$rows = $q->paginate(20)->appends($request->query());
|
||||||
|
|
||||||
|
// 駐輪場セレクト用データ取得
|
||||||
|
$parks = DB::table('park')->select('park_id', 'park_name')->orderBy('park_name')->get();
|
||||||
|
|
||||||
|
// 利用者分類セレクト用:実際に使用されている分類のみを取得する
|
||||||
|
$categories = $this->buildCategoryOptions(true);
|
||||||
|
|
||||||
|
// ビューに渡す
|
||||||
|
return view('admin.contractor.list', compact('rows', 'sort', 'sortType', 'parks', 'categories'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -90,6 +182,7 @@ class ContractorController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function info($id)
|
public function info($id)
|
||||||
{
|
{
|
||||||
|
// 指定契約IDの詳細を取得
|
||||||
$contract = DB::table('regular_contract as rc')
|
$contract = DB::table('regular_contract as rc')
|
||||||
->select([
|
->select([
|
||||||
'rc.*',
|
'rc.*',
|
||||||
@ -112,4 +205,55 @@ class ContractorController extends Controller
|
|||||||
|
|
||||||
return view('admin.contractor.info', compact('contract'));
|
return view('admin.contractor.info', compact('contract'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 利用者分類選択肢を取得
|
||||||
|
*
|
||||||
|
* @param bool $onlyUsed true の場合は regular_contract に出現する分類のみ返す
|
||||||
|
* @return array [user_categoryid => label, ...]
|
||||||
|
*/
|
||||||
|
private function buildCategoryOptions(bool $onlyUsed = false): array
|
||||||
|
{
|
||||||
|
if (! $onlyUsed) {
|
||||||
|
// 全件取得(既存の挙動)
|
||||||
|
return DB::table('usertype')
|
||||||
|
->orderBy('user_categoryid', 'asc')
|
||||||
|
->get()
|
||||||
|
->mapWithKeys(function ($row) {
|
||||||
|
$label = collect([
|
||||||
|
$row->usertype_subject1 ?? '',
|
||||||
|
$row->usertype_subject2 ?? '',
|
||||||
|
$row->usertype_subject3 ?? '',
|
||||||
|
])->filter(fn ($v) => $v !== '')->implode('/');
|
||||||
|
|
||||||
|
return [$row->user_categoryid => $label !== '' ? $label : (string) $row->user_categoryid];
|
||||||
|
})
|
||||||
|
->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 実際に使用されている分類のみ取得する(内部結合で user と regular_contract と紐付くもの)
|
||||||
|
$rows = DB::table('usertype as ut')
|
||||||
|
->join('user as u', 'u.user_categoryid', '=', 'ut.user_categoryid')
|
||||||
|
->join('regular_contract as rc', 'rc.user_id', '=', 'u.user_id')
|
||||||
|
->select(
|
||||||
|
'ut.user_categoryid',
|
||||||
|
'ut.usertype_subject1',
|
||||||
|
'ut.usertype_subject2',
|
||||||
|
'ut.usertype_subject3'
|
||||||
|
)
|
||||||
|
->groupBy('ut.user_categoryid', 'ut.usertype_subject1', 'ut.usertype_subject2', 'ut.usertype_subject3')
|
||||||
|
->orderBy('ut.user_categoryid', 'asc')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
// ラベルを組み立てて配列で返す
|
||||||
|
return $rows->mapWithKeys(function ($row) {
|
||||||
|
$label = collect([
|
||||||
|
$row->usertype_subject1 ?? '',
|
||||||
|
$row->usertype_subject2 ?? '',
|
||||||
|
$row->usertype_subject3 ?? '',
|
||||||
|
])->filter(fn ($v) => $v !== '')->implode('/');
|
||||||
|
|
||||||
|
return [$row->user_categoryid => $label !== '' ? $label : (string) $row->user_categoryid];
|
||||||
|
})->toArray();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,7 +13,9 @@ class ContractorListController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function list(Request $request)
|
public function list(Request $request)
|
||||||
{
|
{
|
||||||
|
// ベースクエリを構築
|
||||||
$q = DB::table('regular_contract as rc')
|
$q = DB::table('regular_contract as rc')
|
||||||
|
->leftJoin('user as u','rc.user_id','=','u.user_id')
|
||||||
->select([
|
->select([
|
||||||
'rc.contract_id',
|
'rc.contract_id',
|
||||||
'rc.contract_qr_id',
|
'rc.contract_qr_id',
|
||||||
@ -32,11 +34,14 @@ class ContractorListController extends Controller
|
|||||||
'rc.contract_permission',
|
'rc.contract_permission',
|
||||||
'rc.contract_manual',
|
'rc.contract_manual',
|
||||||
'rc.contract_notice',
|
'rc.contract_notice',
|
||||||
|
'rc.update_flag',
|
||||||
|
'rc.user_securitynum',
|
||||||
|
'rc.contract_seal_issue',
|
||||||
'p.park_name',
|
'p.park_name',
|
||||||
// userテーブルの正しいカラム名
|
|
||||||
'u.user_name',
|
'u.user_name',
|
||||||
'u.user_phonetic',
|
'u.user_phonetic',
|
||||||
'u.user_mobile',
|
'u.user_mobile',
|
||||||
|
'u.user_seq',
|
||||||
'u.user_homephone',
|
'u.user_homephone',
|
||||||
'u.user_primemail',
|
'u.user_primemail',
|
||||||
'u.user_gender',
|
'u.user_gender',
|
||||||
@ -53,37 +58,127 @@ class ContractorListController extends Controller
|
|||||||
'u.user_workplace',
|
'u.user_workplace',
|
||||||
'u.user_school',
|
'u.user_school',
|
||||||
'u.user_remarks',
|
'u.user_remarks',
|
||||||
// 他に必要なカラムもここに追加
|
'u.user_tag_serial_64',
|
||||||
|
'u.user_reduction',
|
||||||
|
DB::raw('rc.user_securitynum as crime_prevention'),
|
||||||
|
DB::raw('rc.contract_seal_issue as seal_issue_count'),
|
||||||
|
DB::raw("CASE rc.enable_months
|
||||||
|
WHEN 1 THEN '月極(1ヶ月)'
|
||||||
|
WHEN 3 THEN '3ヶ月'
|
||||||
|
WHEN 6 THEN '6ヶ月'
|
||||||
|
WHEN 12 THEN '年'
|
||||||
|
ELSE CONCAT(rc.enable_months, 'ヶ月') END as ticket_type"),
|
||||||
|
DB::raw('ps.psection_subject as vehicle_type'),
|
||||||
|
// 利用者分類のラベル(usertype テーブルの subject を取得)
|
||||||
|
DB::raw('ut.usertype_subject1 as user_category1'),
|
||||||
|
DB::raw('ut.usertype_subject2 as user_category2'),
|
||||||
|
DB::raw('ut.usertype_subject3 as user_category3'),
|
||||||
])
|
])
|
||||||
->leftJoin('park as p', 'rc.park_id', '=', 'p.park_id')
|
->leftJoin('park as p', 'rc.park_id', '=', 'p.park_id')
|
||||||
->leftJoin('user as u', 'rc.user_id', '=', 'u.user_id');
|
->leftJoin('psection as ps', 'rc.psection_id', '=', 'ps.psection_id')
|
||||||
|
->leftJoin('usertype as ut', 'u.user_categoryid', '=', 'ut.user_categoryid');
|
||||||
|
|
||||||
// 検索条件例
|
// ===== 絞り込み条件 =====
|
||||||
if ($request->filled('contract_id')) {
|
|
||||||
$q->where('rc.contract_id', $request->input('contract_id'));
|
// 駐輪場で絞る(完全一致)
|
||||||
}
|
if ($request->filled('park_id')) {
|
||||||
if ($request->filled('name')) {
|
$q->where('rc.park_id', $request->park_id);
|
||||||
$q->where('u.user_name', 'like', '%' . $request->input('name') . '%');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// タグ・QR(完全一致、空白なら絞り込まない)
|
// 利用者IDで絞る(完全一致)
|
||||||
if ($request->filled('tag_qr_flag') && $request->input('tag_qr_flag') !== '') {
|
if ($request->filled('user_id')) {
|
||||||
$q->where('rc.tag_qr_flag', $request->input('tag_qr_flag'));
|
$q->where('rc.user_id', $request->user_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ソート処理
|
// 利用者分類で絞る(※ select の value を user_categoryid にしているため、user テーブルのカラムで比較)
|
||||||
$sort = $request->input('sort', 'rc.contract_id');
|
if ($request->filled('user_category1')) {
|
||||||
$sortType = $request->input('sort_type', 'desc');
|
$q->where('u.user_categoryid', $request->user_category1);
|
||||||
// カラム名のバリデーション(必要に応じて拡張)
|
|
||||||
$allowSorts = ['rc.contract_id'];
|
|
||||||
if (!in_array($sort, $allowSorts)) {
|
|
||||||
$sort = 'rc.contract_id';
|
|
||||||
}
|
}
|
||||||
$sortType = ($sortType === 'asc') ? 'asc' : 'desc';
|
|
||||||
|
|
||||||
$rows = $q->orderBy($sort, $sortType)->paginate(20)->withQueryString();
|
// タグシリアル64進で部分一致検索
|
||||||
|
if ($request->filled('user_tag_serial_64')) {
|
||||||
|
$val = $request->user_tag_serial_64;
|
||||||
|
$q->where('u.user_tag_serial_64','like','%'.$val.'%');
|
||||||
|
}
|
||||||
|
|
||||||
return view('admin.contractor_list.list', compact('rows', 'sort', 'sortType'));
|
// 対象月
|
||||||
|
$target = $request->input('target_month');
|
||||||
|
if (in_array($target,['last','this','next','after2'],true)) {
|
||||||
|
$base = now()->startOfMonth();
|
||||||
|
$offset = ['last'=>-1,'this'=>0,'next'=>1,'after2'=>2][$target];
|
||||||
|
$m = $base->copy()->addMonths($offset);
|
||||||
|
if ($target === 'after2') {
|
||||||
|
// 2か月後「以降」を抽出(該当月の月初以降)
|
||||||
|
$q->whereDate('rc.contract_periode', '>=', $m->toDateString());
|
||||||
|
} else {
|
||||||
|
$q->whereYear('rc.contract_periode',$m->year)
|
||||||
|
->whereMonth('rc.contract_periode',$m->month);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// フリガナで部分一致
|
||||||
|
if ($request->filled('user_phonetic')) {
|
||||||
|
$q->where('u.user_phonetic', 'like', '%' . $request->user_phonetic . '%');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 携帯電話で部分一致
|
||||||
|
if ($request->filled('user_mobile')) {
|
||||||
|
$q->where('u.user_mobile', 'like', '%' . $request->user_mobile . '%');
|
||||||
|
}
|
||||||
|
|
||||||
|
// メールアドレスで部分一致
|
||||||
|
if ($request->filled('user_primemail')) {
|
||||||
|
$q->where('u.user_primemail', 'like', '%' . $request->user_primemail . '%');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 勤務先で部分一致
|
||||||
|
if ($request->filled('user_workplace')) {
|
||||||
|
$q->where('u.user_workplace', 'like', '%' . $request->user_workplace . '%');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 学校で部分一致
|
||||||
|
if ($request->filled('user_school')) {
|
||||||
|
$q->where('u.user_school', 'like', '%' . $request->user_school . '%');
|
||||||
|
}
|
||||||
|
|
||||||
|
// タグ・QR フラグで絞る(空文字は無視)
|
||||||
|
if ($request->filled('tag_qr_flag') && $request->tag_qr_flag !== '') {
|
||||||
|
$q->where('rc.tag_qr_flag', $request->tag_qr_flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== ソート処理 =====
|
||||||
|
// 指定があればその列でソート、なければデフォルトで契約IDの昇順
|
||||||
|
$sort = $request->input('sort'); // null 許容
|
||||||
|
$sortType = $request->input('sort_type','asc');
|
||||||
|
|
||||||
|
$allowSorts = [
|
||||||
|
'rc.contract_id',
|
||||||
|
'rc.user_id',
|
||||||
|
'u.user_name',
|
||||||
|
'rc.tag_qr_flag',
|
||||||
|
'p.park_name',
|
||||||
|
];
|
||||||
|
if ($sort && in_array($sort, $allowSorts)) {
|
||||||
|
$sortType = $sortType === 'desc' ? 'desc' : 'asc';
|
||||||
|
$q->orderBy($sort, $sortType);
|
||||||
|
} else {
|
||||||
|
// デフォルトソート
|
||||||
|
$sort = null;
|
||||||
|
$sortType = null;
|
||||||
|
$q->orderBy('rc.contract_id','asc');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ページネーション(クエリ文字列を引き継ぐ)
|
||||||
|
$rows = $q->paginate(20)->appends($request->query());
|
||||||
|
|
||||||
|
// 駐輪場セレクト用データ取得
|
||||||
|
$parks = DB::table('park')->select('park_id', 'park_name')->orderBy('park_name')->get();
|
||||||
|
|
||||||
|
// 利用者分類セレクト用:実際に使用されている分類のみを取得する
|
||||||
|
$categories = $this->buildCategoryOptions(true);
|
||||||
|
|
||||||
|
// ビューに渡す
|
||||||
|
return view('admin.contractor_list.list', compact('rows', 'sort', 'sortType', 'parks', 'categories'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -91,6 +186,7 @@ class ContractorListController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function info($id)
|
public function info($id)
|
||||||
{
|
{
|
||||||
|
// 指定契約IDの詳細を取得
|
||||||
$contract = DB::table('regular_contract as rc')
|
$contract = DB::table('regular_contract as rc')
|
||||||
->select([
|
->select([
|
||||||
'rc.*',
|
'rc.*',
|
||||||
@ -111,6 +207,57 @@ class ContractorListController extends Controller
|
|||||||
|
|
||||||
if (!$contract) { abort(404); }
|
if (!$contract) { abort(404); }
|
||||||
|
|
||||||
return view('admin.contractor.info', compact('contract'));
|
return view('admin.contractor_List.info', compact('contract'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 利用者分類選択肢を取得
|
||||||
|
*
|
||||||
|
* @param bool $onlyUsed true の場合は regular_contract に出現する分類のみ返す
|
||||||
|
* @return array [user_categoryid => label, ...]
|
||||||
|
*/
|
||||||
|
private function buildCategoryOptions(bool $onlyUsed = false): array
|
||||||
|
{
|
||||||
|
if (! $onlyUsed) {
|
||||||
|
// 全件取得(既存の挙動)
|
||||||
|
return DB::table('usertype')
|
||||||
|
->orderBy('user_categoryid', 'asc')
|
||||||
|
->get()
|
||||||
|
->mapWithKeys(function ($row) {
|
||||||
|
$label = collect([
|
||||||
|
$row->usertype_subject1 ?? '',
|
||||||
|
$row->usertype_subject2 ?? '',
|
||||||
|
$row->usertype_subject3 ?? '',
|
||||||
|
])->filter(fn ($v) => $v !== '')->implode('/');
|
||||||
|
|
||||||
|
return [$row->user_categoryid => $label !== '' ? $label : (string) $row->user_categoryid];
|
||||||
|
})
|
||||||
|
->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 実際に使用されている分類のみ取得する(内部結合で user と regular_contract と紐付くもの)
|
||||||
|
$rows = DB::table('usertype as ut')
|
||||||
|
->join('user as u', 'u.user_categoryid', '=', 'ut.user_categoryid')
|
||||||
|
->join('regular_contract as rc', 'rc.user_id', '=', 'u.user_id')
|
||||||
|
->select(
|
||||||
|
'ut.user_categoryid',
|
||||||
|
'ut.usertype_subject1',
|
||||||
|
'ut.usertype_subject2',
|
||||||
|
'ut.usertype_subject3'
|
||||||
|
)
|
||||||
|
->groupBy('ut.user_categoryid', 'ut.usertype_subject1', 'ut.usertype_subject2', 'ut.usertype_subject3')
|
||||||
|
->orderBy('ut.user_categoryid', 'asc')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
// ラベルを組み立てて配列で返す
|
||||||
|
return $rows->mapWithKeys(function ($row) {
|
||||||
|
$label = collect([
|
||||||
|
$row->usertype_subject1 ?? '',
|
||||||
|
$row->usertype_subject2 ?? '',
|
||||||
|
$row->usertype_subject3 ?? '',
|
||||||
|
])->filter(fn ($v) => $v !== '')->implode('/');
|
||||||
|
|
||||||
|
return [$row->user_categoryid => $label !== '' ? $label : (string) $row->user_categoryid];
|
||||||
|
})->toArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,6 +8,7 @@ use App\Models\Device;
|
|||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Validator;
|
use Illuminate\Support\Facades\Validator;
|
||||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||||
|
use App\Models\Park;
|
||||||
|
|
||||||
class DeviceController extends Controller
|
class DeviceController extends Controller
|
||||||
{
|
{
|
||||||
@ -17,126 +18,207 @@ class DeviceController extends Controller
|
|||||||
|
|
||||||
public function list(Request $request)
|
public function list(Request $request)
|
||||||
{
|
{
|
||||||
$perPage = \App\Utils::item_per_page ?? 20;
|
$perPage = \App\Utils::item_per_page ?? 20;
|
||||||
|
|
||||||
|
// リクエストからソート対象と方向を取得(デフォルト: device_id asc)
|
||||||
|
$sort = $request->input('sort', 'device_id');
|
||||||
|
$sort_type = $request->input('sort_type', 'asc');
|
||||||
|
|
||||||
|
// 許可カラム(SQLインジェクション対策)
|
||||||
|
$sortable = [
|
||||||
|
'device_id',
|
||||||
|
'park_id',
|
||||||
|
'device_type',
|
||||||
|
'device_subject',
|
||||||
|
'device_identifier',
|
||||||
|
'device_work',
|
||||||
|
'device_workstart',
|
||||||
|
'device_replace',
|
||||||
|
'device_remarks',
|
||||||
|
'operator_id',
|
||||||
|
'ope_auth1',
|
||||||
|
];
|
||||||
|
|
||||||
|
if (!in_array($sort, $sortable)) {
|
||||||
|
$sort = 'device_id';
|
||||||
|
}
|
||||||
|
if (!in_array(strtolower($sort_type), ['asc','desc'])) {
|
||||||
|
$sort_type = 'desc';
|
||||||
|
}
|
||||||
|
|
||||||
$list = Device::with('park')
|
$list = Device::with('park')
|
||||||
->orderBy('device_id', 'desc')
|
->orderBy($sort, $sort_type)
|
||||||
->paginate($perPage);
|
->paginate($perPage)
|
||||||
|
->appends([
|
||||||
|
'sort' => $sort,
|
||||||
|
'sort_type' => $sort_type,
|
||||||
|
]);
|
||||||
|
|
||||||
return view('admin.devices.list', [
|
return view('admin.devices.list', [
|
||||||
'list' => $list,
|
'list' => $list,
|
||||||
'sort' => 'device_id',
|
'sort' => $sort,
|
||||||
'sort_type' => 'desc',
|
'sort_type' => $sort_type,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 新規追加: /device/add
|
* 新規登録(GET 画面 / POST 保存)
|
||||||
*/
|
*/
|
||||||
public function add(Request $request)
|
public function add(Request $request)
|
||||||
{
|
{
|
||||||
if ($request->isMethod('post')) {
|
if ($request->isMethod('get')) {
|
||||||
$v = Validator::make($request->all(), $this->rules());
|
return view('admin.devices.add', [
|
||||||
if ($v->fails()) return back()->withErrors($v)->withInput();
|
'isEdit' => false,
|
||||||
|
'device' => new Device(),
|
||||||
|
'parks' => Park::all(),
|
||||||
|
|
||||||
DB::transaction(function () use ($request) {
|
// 初期値(Bladeで old() 使うなら省略可)
|
||||||
Device::create($request->only([
|
'device_id' => null,
|
||||||
'park_id','device_type','device_subject','device_identifier',
|
'park_id' => '',
|
||||||
'device_work','device_workstart','device_replace','device_remarks','operator_id',
|
'device_type' => '',
|
||||||
]));
|
'device_subject' => '',
|
||||||
});
|
'device_identifier'=> '',
|
||||||
|
'device_work' => '',
|
||||||
return redirect()->route('devices')->with('success', 'デバイスを登録しました。');
|
'device_workstart' => '',
|
||||||
|
'device_replace' => '',
|
||||||
|
'device_remarks' => '',
|
||||||
|
'operator_id' => '',
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return view('admin.devices.add', [
|
// 入力値を一旦取得
|
||||||
'device' => new Device(),
|
$data = $request->all();
|
||||||
'isInfo' => false,
|
|
||||||
'isEdit' => false,
|
// --- バリデーション ---
|
||||||
]);
|
$rules = [
|
||||||
|
'park_id' => ['required','integer'],
|
||||||
|
'device_type' => ['required','in:1,2,3'], // 1=サーバー, 2=プリンタ, 3=その他
|
||||||
|
'device_subject' => ['required','string','max:255'],
|
||||||
|
'device_identifier' => ['required','string','max:255'],
|
||||||
|
'device_work' => ['required','in:0,1'], // 1=稼働, 0=停止
|
||||||
|
'device_workstart' => ['required','date'],
|
||||||
|
'device_replace' => ['nullable','date'],
|
||||||
|
'device_remarks' => ['nullable','string','max:255'],
|
||||||
|
'operator_id' => ['nullable','integer'],
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
$request->validate($rules);
|
||||||
|
|
||||||
|
// 保存処理
|
||||||
|
$device = new Device();
|
||||||
|
$device->fill($data);
|
||||||
|
$device->save();
|
||||||
|
|
||||||
|
return redirect()->route('devices')->with('success', '登録しました。');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 編集: /device/edit/{id}
|
* 編集(GET 画面 / POST 更新)
|
||||||
*/
|
*/
|
||||||
public function edit(Request $request, int $id)
|
public function edit($id, Request $request)
|
||||||
{
|
{
|
||||||
$device = Device::findOrFail($id);
|
$device = Device::find($id);
|
||||||
|
if (!$device) abort(404);
|
||||||
|
|
||||||
if ($request->isMethod('post')) {
|
if ($request->isMethod('get')) {
|
||||||
$v = Validator::make($request->all(), $this->rules($id));
|
return view('admin.devices.edit', [
|
||||||
if ($v->fails()) return back()->withErrors($v)->withInput();
|
'isEdit' => true,
|
||||||
|
'device' => $device,
|
||||||
DB::transaction(function () use ($request, $device) {
|
'parks' => Park::all(),
|
||||||
$device->update($request->only([
|
]);
|
||||||
'park_id','device_type','device_subject','device_identifier',
|
|
||||||
'device_work','device_workstart','device_replace','device_remarks','operator_id',
|
|
||||||
]));
|
|
||||||
});
|
|
||||||
|
|
||||||
return redirect()->route('devices')->with('success', 'デバイスを更新しました。');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return view('admin.devices.edit', [
|
// 入力値を一旦取得
|
||||||
'device' => $device,
|
$data = $request->all();
|
||||||
'isInfo' => false,
|
|
||||||
'isEdit' => true,
|
// --- バリデーション ---
|
||||||
]);
|
$rules = [
|
||||||
|
'park_id' => ['required','integer'],
|
||||||
|
'device_type' => ['required','in:1,2,3'], // 1=サーバー, 2=プリンタ, 3=その他
|
||||||
|
'device_subject' => ['required','string','max:255'],
|
||||||
|
'device_identifier' => ['required','string','max:255'],
|
||||||
|
'device_work' => ['required','in:0,1'], // 1=稼働, 0=停止
|
||||||
|
'device_workstart' => ['required','date'],
|
||||||
|
'device_replace' => ['nullable','date'],
|
||||||
|
'device_remarks' => ['nullable','string','max:255'],
|
||||||
|
'operator_id' => ['nullable','integer'],
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
$request->validate($rules);
|
||||||
|
|
||||||
|
// 保存処理
|
||||||
|
$device->fill($data);
|
||||||
|
$device->save();
|
||||||
|
|
||||||
|
return redirect()->route('devices')->with('success', '更新しました。');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 詳細: /device/info/{id}
|
* 詳細: /device/info/{id}
|
||||||
*/
|
*/
|
||||||
public function info(int $id)
|
// public function info(int $id)
|
||||||
{
|
// {
|
||||||
$device = Device::with('park')->findOrFail($id);
|
// $device = Device::with('park')->findOrFail($id);
|
||||||
|
|
||||||
return view('admin.devices.info', [
|
// return view('admin.devices.info', [
|
||||||
'device' => $device,
|
// 'device' => $device,
|
||||||
'isInfo' => true,
|
// 'isInfo' => true,
|
||||||
'isEdit' => false,
|
// 'isEdit' => false,
|
||||||
]);
|
// ]);
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 削除: /device/delete
|
* 削除(単体 or 複数)
|
||||||
*/
|
*/
|
||||||
public function delete(Request $request)
|
public function delete(Request $request)
|
||||||
{
|
{
|
||||||
$ids = $request->input('ids');
|
$ids = [];
|
||||||
$id = $request->input('id');
|
|
||||||
|
|
||||||
if ($id) $ids = [$id];
|
// 単体削除
|
||||||
if (!is_array($ids) || empty($ids)) {
|
if ($request->filled('id')) {
|
||||||
return back()->with('error', '削除対象が指定されていません。');
|
$ids[] = (int) $request->input('id');
|
||||||
}
|
}
|
||||||
|
|
||||||
DB::transaction(function () use ($ids) {
|
// 複数削除
|
||||||
Device::whereIn('device_id', $ids)->delete();
|
if ($request->filled('ids')) {
|
||||||
});
|
$ids = array_merge($ids, array_map('intval', (array)$request->input('ids')));
|
||||||
|
}
|
||||||
|
|
||||||
return redirect()->route('devices')->with('success', 'デバイスを削除しました。');
|
$ids = array_unique($ids);
|
||||||
|
|
||||||
|
if (!$ids) {
|
||||||
|
return back()->with('error', '削除対象が選択されていません。');
|
||||||
|
}
|
||||||
|
|
||||||
|
Device::deleteByPk($ids);
|
||||||
|
|
||||||
|
return redirect()->route('devices')->with('success', '削除しました。');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** バリデーションルール */
|
/** バリデーションルール */
|
||||||
private function rules(?int $id = null): array
|
private function rules(?int $id = null): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'park_id' => ['nullable','integer'],
|
'park_id' => ['required','integer'], // 駐輪場ID 必須
|
||||||
'device_type' => ['required','string','max:255'],
|
'device_type' => ['required','in:1,2,3'], // 1=サーバー, 2=プリンタ, 3=その他
|
||||||
'device_subject' => ['required','string','max:255'],
|
'device_subject' => ['required','string','max:255'], // デバイス名 必須
|
||||||
'device_identifier' => ['nullable','string','max:255'],
|
'device_identifier' => ['required','string','max:255'], // 識別子 必須
|
||||||
'device_work' => ['nullable','string','max:255'],
|
'device_work' => ['required','in:0,1'], // 1=稼働, 0=停止
|
||||||
'device_workstart' => ['nullable','date'],
|
'device_workstart' => ['required','date'], // 稼働開始日 必須
|
||||||
'device_replace' => ['nullable','date'],
|
'device_replace' => ['nullable','date'], // リプレース予約日 任意
|
||||||
'device_remarks' => ['nullable','string','max:255'],
|
'device_remarks' => ['nullable','string','max:255'], // 備考 任意
|
||||||
'operator_id' => ['nullable','integer'],
|
'operator_id' => ['nullable','integer'], // 任意
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
78
app/Http/Controllers/Admin/InformationController.php
Normal file
78
app/Http/Controllers/Admin/InformationController.php
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
|
class InformationController extends Controller
|
||||||
|
{
|
||||||
|
public function list(Request $request)
|
||||||
|
{
|
||||||
|
// パラメータ
|
||||||
|
$period = $request->input('period', 'month'); // month | all
|
||||||
|
$type = $request->input('type', 'all'); // task(<99) | hard(>99) | all
|
||||||
|
$status = $request->input('status', 'untreated'); // untreated(=1) | inprogress(=2) | done(=3) | all
|
||||||
|
|
||||||
|
$q = DB::table('operator_que as oq')
|
||||||
|
->leftJoin('user as u', 'oq.user_id', '=', 'u.user_id')
|
||||||
|
->leftJoin('park as p', 'oq.park_id', '=', 'p.park_id')
|
||||||
|
// オペレータマスタ(テーブル・カラム名は環境に合わせて調整)
|
||||||
|
->leftJoin('ope as o', 'oq.operator_id', '=', 'o.ope_id')
|
||||||
|
->select(
|
||||||
|
'oq.que_id','oq.que_class','oq.user_id',
|
||||||
|
DB::raw('u.user_name as user_name'),
|
||||||
|
'oq.contract_id','oq.park_id',
|
||||||
|
DB::raw('p.park_name as park_name'),
|
||||||
|
'oq.que_comment','oq.que_status','oq.que_status_comment',
|
||||||
|
'oq.work_instructions','oq.created_at','oq.updated_at','oq.operator_id',
|
||||||
|
DB::raw('o.ope_name as operator_name')
|
||||||
|
);
|
||||||
|
|
||||||
|
// 期間: 登録日ベース(最新1ヵ月 or 全期間)
|
||||||
|
if ($period === 'month') {
|
||||||
|
$q->where('oq.created_at', '>=', now()->subMonth());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 種別: que_class
|
||||||
|
if ($type === 'task') {
|
||||||
|
$q->where('oq.que_class', '<', 99);
|
||||||
|
} elseif ($type === 'hard') {
|
||||||
|
$q->where('oq.que_class', '>', 99);
|
||||||
|
} // all は絞り込みなし
|
||||||
|
|
||||||
|
// ステータス: que_status
|
||||||
|
if ($status === 'untreated') {
|
||||||
|
$q->where('oq.que_status', 1);
|
||||||
|
} elseif ($status === 'inprogress') {
|
||||||
|
$q->where('oq.que_status', 2);
|
||||||
|
} elseif ($status === 'done') {
|
||||||
|
$q->where('oq.que_status', 3);
|
||||||
|
} // all は絞り込みなし
|
||||||
|
|
||||||
|
$jobs = $q->orderBy('oq.que_id')->paginate(20)->appends($request->query());
|
||||||
|
|
||||||
|
return view('admin.information.list', compact('jobs','period','type','status'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ステータス一括更新(着手=2 / 対応完了=3)
|
||||||
|
public function updateStatus(Request $request)
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'ids' => 'required|array',
|
||||||
|
'action' => 'required|in:inprogress,done',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$new = $request->action === 'inprogress' ? 2 : 3;
|
||||||
|
|
||||||
|
DB::table('operator_que')
|
||||||
|
->whereIn('que_id', $request->ids)
|
||||||
|
->update([
|
||||||
|
'que_status' => $new,
|
||||||
|
'updated_at' => now(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return back()->with('success', '選択したキューのステータスを更新しました。');
|
||||||
|
}
|
||||||
|
}
|
||||||
226
app/Http/Controllers/Admin/InvSettingController.php
Normal file
226
app/Http/Controllers/Admin/InvSettingController.php
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\InvSetting;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
|
||||||
|
class InvSettingController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 登録フォーム表示
|
||||||
|
*/
|
||||||
|
public function form(Request $request)
|
||||||
|
{
|
||||||
|
$row = InvSetting::first();
|
||||||
|
|
||||||
|
$zip1 = $zip2 = $tel1 = $tel2 = $tel3 = $fax1 = $fax2 = $fax3 = '';
|
||||||
|
|
||||||
|
if ($row) {
|
||||||
|
// 郵便番号(そのままハイフン分割)
|
||||||
|
if (!empty($row->zipcode) && str_contains($row->zipcode, '-')) {
|
||||||
|
[$zip1, $zip2] = explode('-', $row->zipcode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 電話番号:数字以外を除去 → 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('admin.invsettings._form', compact(
|
||||||
|
'row', 'zip1', 'zip2', 'tel1', 'tel2', 'tel3', 'fax1', 'fax2', 'fax3'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登録・更新処理
|
||||||
|
*/
|
||||||
|
public function save(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',
|
||||||
|
];
|
||||||
|
|
||||||
|
// ▼ カスタム日本語メッセージ
|
||||||
|
$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', 'インボイス設定を登録しました。');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 社判画像アップロード(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);
|
||||||
|
// }
|
||||||
|
|
||||||
|
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以下にしてください。',
|
||||||
|
]);
|
||||||
|
|
||||||
|
// ファイルオブジェクト取得
|
||||||
|
$file = $request->file('company_image_file');
|
||||||
|
|
||||||
|
// 元のファイル名(例: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,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ファイル未選択時
|
||||||
|
return response()->json([
|
||||||
|
'error' => 'ファイルが選択されていません。'
|
||||||
|
], 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -12,45 +12,77 @@ use Illuminate\Support\Facades\DB;
|
|||||||
class JurisdictionParkingController extends Controller
|
class JurisdictionParkingController extends Controller
|
||||||
{
|
{
|
||||||
public function list(Request $request)
|
public function list(Request $request)
|
||||||
{
|
{
|
||||||
$list = JurisdictionParking::query()->paginate(20);
|
|
||||||
return view('admin.jurisdiction_parkings.list', compact('list'));
|
$sort = $request->input('sort', 'jurisdiction_parking_id');
|
||||||
}
|
$sort_type = $request->input('sort_type', 'asc');
|
||||||
|
|
||||||
|
$list = JurisdictionParking::orderBy($sort, $sort_type)->paginate(20);
|
||||||
|
|
||||||
|
return view('admin.jurisdiction_parkings.list', compact('list', 'sort', 'sort_type'));
|
||||||
|
}
|
||||||
|
|
||||||
public function add(Request $request)
|
public function add(Request $request)
|
||||||
{
|
{
|
||||||
if ($request->isMethod('post')) {
|
if ($request->isMethod('post')) {
|
||||||
$validated = $request->validate([
|
$validated = $request->validate([
|
||||||
'jurisdiction_parking_name' => 'required|string|max:255',
|
'jurisdiction_parking_name' => [
|
||||||
'operator_id' => 'nullable|integer',
|
'required',
|
||||||
'park_id' => 'nullable|integer',
|
'string',
|
||||||
|
'max:20',
|
||||||
|
],
|
||||||
|
'ope_id' => [
|
||||||
|
'required',
|
||||||
|
],
|
||||||
|
'park_id' => [
|
||||||
|
'required',
|
||||||
|
],
|
||||||
|
'operator_id' => [
|
||||||
|
'nullable',
|
||||||
|
'integer',
|
||||||
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
JurisdictionParking::create($validated);
|
JurisdictionParking::create($validated);
|
||||||
return redirect()->route('jurisdiction_parkings')->with('success', '登録しました');
|
|
||||||
|
return redirect()->route('jurisdiction_parkings')
|
||||||
|
->with('success', '登録しました。');
|
||||||
}
|
}
|
||||||
|
|
||||||
$parks = Park::pluck('park_name', 'park_id');
|
$parks = Park::pluck('park_name', 'park_id');
|
||||||
$operators = Ope::pluck('ope_name', 'ope_id');
|
$opes = Ope::pluck('ope_name', 'ope_id');
|
||||||
|
|
||||||
return view('admin.jurisdiction_parkings.add', compact('parks', 'operators'));
|
return view('admin.jurisdiction_parkings.add', compact('parks', 'opes'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function edit(Request $request, $jurisdiction_parking_id)
|
public function edit(Request $request, $id)
|
||||||
{
|
{
|
||||||
$record = JurisdictionParking::findOrFail($jurisdiction_parking_id);
|
$record = JurisdictionParking::findOrFail($id);
|
||||||
|
|
||||||
if ($request->isMethod('post')) {
|
if ($request->isMethod('post')) {
|
||||||
$validated = $request->validate([
|
$validated = $request->validate([
|
||||||
'jurisdiction_parking_name' => 'required|string|max:255',
|
'jurisdiction_parking_name' => [
|
||||||
'ope_id' => 'nullable|integer',
|
'required',
|
||||||
'park_id' => 'nullable|integer',
|
'string',
|
||||||
'operator_id' => 'nullable|integer',
|
'max:20',
|
||||||
|
],
|
||||||
|
'ope_id' => [
|
||||||
|
'required',
|
||||||
|
],
|
||||||
|
'park_id' => [
|
||||||
|
'required',
|
||||||
|
],
|
||||||
|
'operator_id' => [
|
||||||
|
'nullable',
|
||||||
|
'integer',
|
||||||
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$record->update($validated);
|
$record->update($validated);
|
||||||
return redirect()->route('jurisdiction_parkings')->with('success', '更新しました');
|
|
||||||
|
return redirect()->route('jurisdiction_parkings')
|
||||||
|
->with('success', '更新しました。');
|
||||||
}
|
}
|
||||||
|
|
||||||
$parks = Park::pluck('park_name', 'park_id');
|
$parks = Park::pluck('park_name', 'park_id');
|
||||||
@ -59,14 +91,25 @@ class JurisdictionParkingController extends Controller
|
|||||||
return view('admin.jurisdiction_parkings.edit', compact('record', 'parks', 'opes'));
|
return view('admin.jurisdiction_parkings.edit', compact('record', 'parks', 'opes'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function delete(Request $request)
|
public function delete(Request $request)
|
||||||
{
|
{
|
||||||
if ($request->has('pk')) {
|
$request->validate([
|
||||||
JurisdictionParking::destroy($request->input('pk'));
|
'pk' => 'required',
|
||||||
return redirect()->route('jurisdiction_parkings')->with('success', '削除しました');
|
'pk.*' => 'integer', // 各要素が整数であることを確認
|
||||||
}
|
]);
|
||||||
|
|
||||||
return redirect()->route('jurisdiction_parkings')->with('error', '削除対象が見つかりません');
|
$ids = (array) $request->input('pk'); // 配列として取得
|
||||||
|
|
||||||
|
$deleted = JurisdictionParking::destroy($ids);
|
||||||
|
|
||||||
|
if ($deleted > 0) {
|
||||||
|
return redirect()->route('jurisdiction_parkings')
|
||||||
|
->with('success', '削除しました。');
|
||||||
|
} else {
|
||||||
|
return redirect()->route('jurisdiction_parkings')
|
||||||
|
->with('error', '削除に失敗しました。');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function info(Request $request, $jurisdiction_parking_id)
|
public function info(Request $request, $jurisdiction_parking_id)
|
||||||
|
|||||||
146
app/Http/Controllers/Admin/MailTemplateController.php
Normal file
146
app/Http/Controllers/Admin/MailTemplateController.php
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use App\Models\MailTemplate;
|
||||||
|
|
||||||
|
class MailTemplateController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 一覧表示
|
||||||
|
*/
|
||||||
|
public function list(Request $request)
|
||||||
|
{
|
||||||
|
if ($request->input('action') === 'reset') {
|
||||||
|
return redirect()->route('mail_templates');
|
||||||
|
}
|
||||||
|
|
||||||
|
$allowedSorts = [
|
||||||
|
'mail_template_id', 'pg_id', 'internal_id', 'mgr_cc_flag',
|
||||||
|
'bcc_adrs', 'use_flag', 'memo', 'subject', 'text',
|
||||||
|
'created_at', 'updated_at', 'operator_id'
|
||||||
|
];
|
||||||
|
|
||||||
|
$sort = $request->input('sort', 'mail_template_id');
|
||||||
|
$sort_type = $request->input('sort_type', 'asc');
|
||||||
|
|
||||||
|
if (!in_array($sort, $allowedSorts)) {
|
||||||
|
$sort = 'mail_template_id';
|
||||||
|
}
|
||||||
|
if (!in_array($sort_type, ['asc', 'desc'])) {
|
||||||
|
$sort_type = 'desc';
|
||||||
|
}
|
||||||
|
|
||||||
|
$query = MailTemplate::query();
|
||||||
|
|
||||||
|
// === 絞り込み ===
|
||||||
|
$mail_template_id = $request->input('mail_template_id');
|
||||||
|
$pg_id = $request->input('pg_id');
|
||||||
|
$mgr_cc_flag = $request->input('mgr_cc_flag');
|
||||||
|
$use_flag = $request->input('use_flag');
|
||||||
|
$subject = $request->input('subject');
|
||||||
|
|
||||||
|
if ($mail_template_id) {
|
||||||
|
$query->where('mail_template_id', $mail_template_id);
|
||||||
|
}
|
||||||
|
if ($pg_id) {
|
||||||
|
$query->where('pg_id', $pg_id);
|
||||||
|
}
|
||||||
|
if ($mgr_cc_flag !== null && $mgr_cc_flag !== '') {
|
||||||
|
$query->where('mgr_cc_flag', $mgr_cc_flag);
|
||||||
|
}
|
||||||
|
if ($use_flag !== null && $use_flag !== '') {
|
||||||
|
$query->where('use_flag', $use_flag);
|
||||||
|
}
|
||||||
|
if ($subject) {
|
||||||
|
$query->where('subject', 'LIKE', "%{$subject}%");
|
||||||
|
}
|
||||||
|
|
||||||
|
$templates = $query->orderBy($sort, $sort_type)->paginate(20);
|
||||||
|
|
||||||
|
return view('admin.mail_templates.list', compact(
|
||||||
|
'templates', 'sort', 'sort_type',
|
||||||
|
'mail_template_id', 'pg_id', 'mgr_cc_flag', 'use_flag', 'subject'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function add(Request $request)
|
||||||
|
{
|
||||||
|
if ($request->isMethod('post')) {
|
||||||
|
$data = $this->validateTemplate($request);
|
||||||
|
$data['operator_id'] = optional(\Auth::user())->ope_id ?? null;
|
||||||
|
|
||||||
|
MailTemplate::create($data);
|
||||||
|
|
||||||
|
return redirect()->route('mail_templates')
|
||||||
|
->with('success', '登録しました。');
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('admin.mail_templates.add', [
|
||||||
|
'mailTemplate' => new MailTemplate(),
|
||||||
|
'isEdit' => false,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 編集
|
||||||
|
*/
|
||||||
|
public function edit(int $id, Request $request)
|
||||||
|
{
|
||||||
|
$mailTemplate = MailTemplate::findOrFail($id);
|
||||||
|
|
||||||
|
if ($request->isMethod('post')) {
|
||||||
|
$data = $this->validateTemplate($request);
|
||||||
|
$data['operator_id'] = optional(\Auth::user())->ope_id ?? null;
|
||||||
|
|
||||||
|
$mailTemplate->update($data);
|
||||||
|
|
||||||
|
return redirect()->route('mail_templates')
|
||||||
|
->with('success', '更新しました。');
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('admin.mail_templates.edit', [
|
||||||
|
'mailTemplate' => $mailTemplate,
|
||||||
|
'isEdit' => true,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 削除
|
||||||
|
*/
|
||||||
|
public function delete(Request $request)
|
||||||
|
{
|
||||||
|
$pk = $request->input('pk', []);
|
||||||
|
|
||||||
|
// 配列に統一
|
||||||
|
$ids = is_array($pk) ? $pk : [$pk];
|
||||||
|
$ids = array_values(array_filter($ids, fn($v) => preg_match('/^\d+$/', (string) $v)));
|
||||||
|
|
||||||
|
if (empty($ids)) {
|
||||||
|
return redirect()->route('mail_templates')->with('error', '削除対象が選択されていません。');
|
||||||
|
}
|
||||||
|
|
||||||
|
MailTemplate::whereIn('mail_template_id', $ids)->delete();
|
||||||
|
|
||||||
|
return redirect()->route('mail_templates')->with('success', '削除しました。');
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* バリデーション共通化
|
||||||
|
*/
|
||||||
|
private function validateTemplate(Request $request)
|
||||||
|
{
|
||||||
|
return $request->validate([
|
||||||
|
'pg_id' => 'required|integer',
|
||||||
|
'internal_id' => 'required|integer',
|
||||||
|
'mgr_cc_flag' => 'required|boolean',
|
||||||
|
'bcc_adrs' => 'nullable|string|max:255',
|
||||||
|
'use_flag' => 'required|boolean',
|
||||||
|
'memo' => 'nullable|string|max:255',
|
||||||
|
'subject' => 'required|string|max:255',
|
||||||
|
'text' => 'required|string',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -31,39 +31,38 @@ class ManagerController extends Controller
|
|||||||
return view('admin.managers.list', compact('list','sort','sort_type'));
|
return view('admin.managers.list', compact('list','sort','sort_type'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 新規(GET:画面表示 / POST:登録) */
|
/** 新規登録画面・登録処理 */
|
||||||
public function add(Request $request)
|
public function add(Request $request)
|
||||||
{
|
{
|
||||||
if ($request->isMethod('post')) {
|
if ($request->isMethod('post')) {
|
||||||
$data = $this->validated($request);
|
$validated = $this->validated($request);
|
||||||
|
|
||||||
|
Manager::create($validated);
|
||||||
|
|
||||||
$manager = Manager::create($data);
|
|
||||||
return redirect()
|
return redirect()
|
||||||
->route('managers_info', ['manager_id' => $manager->manager_id])
|
->route('managers')
|
||||||
->with('success', '登録しました。');
|
->with('success', '登録しました。');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 画面に渡す初期値
|
return view('admin.managers.add', $this->viewVars());
|
||||||
$view = $this->viewVars();
|
|
||||||
return view('admin.managers.add', $view);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 編集(GET:画面表示 / POST:更新) */
|
/** 編集(GET:画面表示 / POST:更新) */
|
||||||
public function edit(Request $request, $manager_id)
|
public function edit(Request $request, $id)
|
||||||
{
|
{
|
||||||
$manager = Manager::findOrFail($manager_id);
|
$manager = Manager::findOrFail($id);
|
||||||
|
|
||||||
if ($request->isMethod('post')) {
|
if ($request->isMethod('post')) {
|
||||||
$data = $this->validated($request);
|
$validated = $this->validated($request);
|
||||||
$manager->fill($data)->save();
|
|
||||||
|
$manager->update($validated);
|
||||||
|
|
||||||
return redirect()
|
return redirect()
|
||||||
->route('managers_info', ['manager_id' => $manager->manager_id])
|
->route('managers')
|
||||||
->with('success', '更新しました。');
|
->with('success', '更新されました。');
|
||||||
}
|
}
|
||||||
|
|
||||||
$view = $this->viewVars($manager);
|
return view('admin.managers.edit', $this->viewVars($manager));
|
||||||
return view('admin.managers.edit', $view);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 詳細(閲覧) */
|
/** 詳細(閲覧) */
|
||||||
@ -78,13 +77,19 @@ class ManagerController extends Controller
|
|||||||
public function delete(Request $request)
|
public function delete(Request $request)
|
||||||
{
|
{
|
||||||
$ids = (array) $request->input('pk', []);
|
$ids = (array) $request->input('pk', []);
|
||||||
if (!$ids) return back()->with('error', '削除対象が選択されていません。');
|
if (!$ids) {
|
||||||
|
return back()->with('error', '削除対象が選択されていません。');
|
||||||
|
}
|
||||||
|
|
||||||
DB::transaction(fn() => Manager::whereIn('manager_id', $ids)->delete());
|
DB::transaction(fn() => Manager::whereIn('manager_id', $ids)->delete());
|
||||||
|
|
||||||
return back()->with('success', '削除しました。');
|
// 一覧画面へリダイレクト + 成功メッセージ
|
||||||
|
return redirect()
|
||||||
|
->route('managers')
|
||||||
|
->with('success', '削除しました。');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** CSV出力 */
|
/** CSV出力 */
|
||||||
public function export(): StreamedResponse
|
public function export(): StreamedResponse
|
||||||
{
|
{
|
||||||
@ -167,22 +172,32 @@ class ManagerController extends Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ======================== private helpers ======================== */
|
/** バリデーション + 前処理 */
|
||||||
|
|
||||||
/** バリデーション */
|
|
||||||
private function validated(Request $request): array
|
private function validated(Request $request): array
|
||||||
{
|
{
|
||||||
|
// 電話番号を全角に変換(半角入力があっても自動で全角に揃える)
|
||||||
|
$request->merge([
|
||||||
|
'manager_tel' => mb_convert_kana($request->input('manager_tel'), 'N') // 半角数字→全角数字
|
||||||
|
]);
|
||||||
|
|
||||||
|
// select の未選択 "" を null に補正
|
||||||
|
foreach (['manager_device2'] as $f) {
|
||||||
|
if ($request->input($f) === "") {
|
||||||
|
$request->merge([$f => null]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return $request->validate([
|
return $request->validate([
|
||||||
'manager_name' => ['required','string','max:255'],
|
'manager_name' => ['required','string','max:32'],
|
||||||
'manager_type' => ['nullable','string','max:255'],
|
'manager_type' => ['required','string','max:10'],
|
||||||
'manager_parkid' => ['nullable','integer','exists:park,park_id'], // テーブル名に合わせて
|
'manager_parkid' => ['required','integer','exists:park,park_id'],
|
||||||
'manager_device1' => ['nullable','integer','exists:device,device_id'],
|
'manager_device1' => ['required','integer','exists:device,device_id'],
|
||||||
'manager_device2' => ['nullable','integer','exists:device,device_id'],
|
'manager_device2' => ['nullable','integer','exists:device,device_id'],
|
||||||
'manager_mail' => ['nullable','email','max:255'],
|
'manager_mail' => ['nullable','email','max:128'],
|
||||||
'manager_tel' => ['nullable','string','max:255'],
|
'manager_tel' => ['required','regex:/^[0-9]+$/u','max:13'], // 全角数字のみ
|
||||||
'manager_alert1' => ['nullable','boolean'],
|
'manager_alert1' => ['nullable','boolean'],
|
||||||
'manager_alert2' => ['nullable','boolean'],
|
'manager_alert2' => ['nullable','boolean'],
|
||||||
'manager_quit_flag' => ['nullable','boolean'],
|
'manager_quit_flag' => ['required','in:0,1'],
|
||||||
'manager_quitday' => ['nullable','date'],
|
'manager_quitday' => ['nullable','date'],
|
||||||
], [], [
|
], [], [
|
||||||
'manager_name' => '駐輪場管理者名',
|
'manager_name' => '駐輪場管理者名',
|
||||||
@ -199,6 +214,7 @@ class ManagerController extends Controller
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** 画面に渡す変数を作る(_form.blade.php が個別変数を参照するため) */
|
/** 画面に渡す変数を作る(_form.blade.php が個別変数を参照するため) */
|
||||||
private function viewVars(?Manager $m = null): array
|
private function viewVars(?Manager $m = null): array
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,119 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers\Admin;
|
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use App\Models\NeighborStation;
|
|
||||||
|
|
||||||
class NeighborStationController extends Controller
|
|
||||||
{
|
|
||||||
// 一覧表示
|
|
||||||
public function list(Request $request)
|
|
||||||
{
|
|
||||||
$sort = $request->input('sort', 'station_id');
|
|
||||||
$sort_type = $request->input('sort_type', 'asc');
|
|
||||||
|
|
||||||
$allowedSorts = ['station_id', 'park_id', 'station_neighbor_station', 'station_name_ruby', 'station_route_name'];
|
|
||||||
if (!in_array($sort, $allowedSorts)) {
|
|
||||||
$sort = 'station_id';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!in_array($sort_type, ['asc', 'desc'])) {
|
|
||||||
$sort_type = 'asc';
|
|
||||||
}
|
|
||||||
|
|
||||||
$stations = NeighborStation::select([
|
|
||||||
'station_id',
|
|
||||||
'station_neighbor_station',
|
|
||||||
'station_name_ruby',
|
|
||||||
'station_route_name',
|
|
||||||
// 'station_latitude',
|
|
||||||
// 'station_longitude',
|
|
||||||
'park_id'
|
|
||||||
])->orderBy($sort, $sort_type)->paginate(20);
|
|
||||||
|
|
||||||
return view('admin.neighbor_stations.list', compact('stations', 'sort', 'sort_type'));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 新規登録画面と登録処理
|
|
||||||
public function add(Request $request)
|
|
||||||
{
|
|
||||||
if ($request->isMethod('post')) {
|
|
||||||
$validated = $request->validate([
|
|
||||||
'station_neighbor_station' => 'required|string|max:255',
|
|
||||||
'station_name_ruby' => 'nullable|string|max:255',
|
|
||||||
'station_route_name' => 'nullable|string|max:255',
|
|
||||||
'park_id' => 'nullable|integer',
|
|
||||||
'operator_id' => 'nullable|integer',
|
|
||||||
]);
|
|
||||||
|
|
||||||
NeighborStation::create($validated);
|
|
||||||
return redirect()->route('neighbor_stations')->with('success', '近傍駅が登録されました');
|
|
||||||
}
|
|
||||||
|
|
||||||
return view('admin.neighbor_stations.add');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 編集画面・更新処理
|
|
||||||
public function edit(Request $request, $id)
|
|
||||||
{
|
|
||||||
$station = NeighborStation::findOrFail($id);
|
|
||||||
|
|
||||||
if ($request->isMethod('post')) {
|
|
||||||
$validated = $request->validate([
|
|
||||||
'station_neighbor_station' => 'required|string|max:255',
|
|
||||||
'station_name_ruby' => 'nullable|string|max:255',
|
|
||||||
'station_route_name' => 'nullable|string|max:255',
|
|
||||||
'park_id' => 'nullable|integer',
|
|
||||||
'operator_id' => 'nullable|integer',
|
|
||||||
]);
|
|
||||||
|
|
||||||
$station->update($validated);
|
|
||||||
return redirect()->route('neighbor_stations')->with('success', '更新しました');
|
|
||||||
}
|
|
||||||
|
|
||||||
return view('admin.neighbor_stations.edit', compact('station'));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 詳細表示
|
|
||||||
public function info($id)
|
|
||||||
{
|
|
||||||
$station = NeighborStation::findOrFail($id);
|
|
||||||
return view('admin.neighbor_stations.info', compact('station'));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 削除処理
|
|
||||||
public function delete(Request $request)
|
|
||||||
{
|
|
||||||
$ids = $request->input('pk'); // ← 接收复数 checkbox 名称 pk[]
|
|
||||||
|
|
||||||
if (!empty($ids)) {
|
|
||||||
NeighborStation::destroy($ids); // 一次性删除多个
|
|
||||||
return redirect()->route('neighbor_stations')->with('success', '削除しました');
|
|
||||||
}
|
|
||||||
|
|
||||||
return redirect()->route('neighbor_stations')->with('error', '削除対象が見つかりません');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// CSVインポート(仮)
|
|
||||||
public function import(Request $request)
|
|
||||||
{
|
|
||||||
// TODO: 実装
|
|
||||||
return redirect()->route('neighbor_stations')->with('info', 'CSVインポートは未実装です');
|
|
||||||
}
|
|
||||||
|
|
||||||
// CSVエクスポート(仮)
|
|
||||||
public function export()
|
|
||||||
{
|
|
||||||
// TODO: 実装
|
|
||||||
return response()->streamDownload(function () {
|
|
||||||
echo "id,station_neighbor_station,station_name_ruby,station_route_name,park_id,operator_id\n";
|
|
||||||
foreach (NeighborStation::all() as $station) {
|
|
||||||
echo "{$station->id},{$station->station_neighbor_station},{$station->station_name_ruby},{$station->station_route_name},{$station->park_id},{$station->operator_id}\n";
|
|
||||||
}
|
|
||||||
}, 'neighbor_stations.csv');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -61,11 +61,29 @@ class NewsController extends Controller
|
|||||||
if ($request->filled('from')) { $q->where('open_datetime','>=',$request->input('from')); }
|
if ($request->filled('from')) { $q->where('open_datetime','>=',$request->input('from')); }
|
||||||
if ($request->filled('to')) { $q->where('open_datetime','<=',$request->input('to')); }
|
if ($request->filled('to')) { $q->where('open_datetime','<=',$request->input('to')); }
|
||||||
|
|
||||||
// 並び順:公開日時の降順 → 主キー降順
|
// {追加} 並び替え(ホワイトリスト)
|
||||||
$rows = $q->orderByDesc('open_datetime')
|
$sort = (string)$request->query('sort', '');
|
||||||
->orderByDesc($this->pk)
|
$dir = strtolower((string)$request->query('dir', 'desc'));
|
||||||
->paginate(20)
|
$dir = in_array($dir, ['asc','desc'], true) ? $dir : 'desc';
|
||||||
->withQueryString();
|
|
||||||
|
// 画面キー → 実カラム
|
||||||
|
$sortable = [
|
||||||
|
'id' => $this->pk, // ニュースID
|
||||||
|
'news' => 'news', // ニュース内容
|
||||||
|
'open_datetime' => 'open_datetime', // 公開日時
|
||||||
|
'mode' => 'mode', // 表示モード
|
||||||
|
];
|
||||||
|
|
||||||
|
if (isset($sortable[$sort])) {
|
||||||
|
$q->orderBy($sortable[$sort], $dir);
|
||||||
|
} else {
|
||||||
|
// 既定:公開日時降順 → 主キー降順
|
||||||
|
$q->orderByDesc('open_datetime')
|
||||||
|
->orderByDesc($this->pk);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ページング(現在のクエリを維持)
|
||||||
|
$rows = $q->paginate(20)->appends($request->except('page'));
|
||||||
|
|
||||||
return view('admin.news.list', compact('rows'));
|
return view('admin.news.list', compact('rows'));
|
||||||
}
|
}
|
||||||
@ -76,21 +94,29 @@ class NewsController extends Controller
|
|||||||
public function add(Request $request)
|
public function add(Request $request)
|
||||||
{
|
{
|
||||||
if ($request->isMethod('post')) {
|
if ($request->isMethod('post')) {
|
||||||
// 入力チェック
|
$messages = [
|
||||||
|
'required' => ':attribute は、必ず入力してください。',
|
||||||
|
'open_datetime.date_format' => '公開日時は :format 形式(YYYY-MM-DD HH:MM:SS)で入力してください。',
|
||||||
|
];
|
||||||
|
$attributes = [
|
||||||
|
'news' => 'ニュース内容',
|
||||||
|
'open_datetime' => '公開日時',
|
||||||
|
'mode' => '表示モード',
|
||||||
|
];
|
||||||
$v = $request->validate([
|
$v = $request->validate([
|
||||||
'news' => 'required|string',
|
'news' => 'required|string',
|
||||||
'open_datetime' => 'nullable|date_format:Y-m-d H:i:s',
|
'open_datetime' => 'required|date_format:Y-m-d H:i:s',
|
||||||
'link_url' => 'nullable|string|max:255',
|
'link_url' => 'nullable|string|max:255',
|
||||||
'image1_filename' => 'nullable|string|max:255',
|
'image1_filename' => 'nullable|string|max:255',
|
||||||
'image2_filename' => 'nullable|string|max:255',
|
'image2_filename' => 'nullable|string|max:255',
|
||||||
'mode' => 'required|integer|min:0|max:9',
|
'mode' => 'required|integer|min:0|max:9',
|
||||||
]);
|
], $messages, $attributes);
|
||||||
|
|
||||||
// 登録
|
// 登録
|
||||||
$now = now();
|
$now = now();
|
||||||
DB::table($this->table)->insert([
|
DB::table($this->table)->insert([
|
||||||
'news' => $v['news'],
|
'news' => $v['news'],
|
||||||
'open_datetime' => $v['open_datetime'] ?? null,
|
'open_datetime' => $v['open_datetime'],
|
||||||
'link_url' => $v['link_url'] ?? null,
|
'link_url' => $v['link_url'] ?? null,
|
||||||
'image1_filename' => $v['image1_filename'] ?? null,
|
'image1_filename' => $v['image1_filename'] ?? null,
|
||||||
'image2_filename' => $v['image2_filename'] ?? null,
|
'image2_filename' => $v['image2_filename'] ?? null,
|
||||||
@ -118,20 +144,28 @@ class NewsController extends Controller
|
|||||||
if (!$news) { abort(404); }
|
if (!$news) { abort(404); }
|
||||||
|
|
||||||
if ($request->isMethod('post')) {
|
if ($request->isMethod('post')) {
|
||||||
// 入力チェック
|
$messages = [
|
||||||
|
'required' => ':attribute は、必ず入力してください。',
|
||||||
|
'open_datetime.date_format' => '公開日時は :format 形式(YYYY-MM-DD HH:MM:SS)で入力してください。',
|
||||||
|
];
|
||||||
|
$attributes = [
|
||||||
|
'news' => 'ニュース内容',
|
||||||
|
'open_datetime' => '公開日時',
|
||||||
|
'mode' => '表示モード',
|
||||||
|
];
|
||||||
$v = $request->validate([
|
$v = $request->validate([
|
||||||
'news' => 'required|string',
|
'news' => 'required|string',
|
||||||
'open_datetime' => 'nullable|date_format:Y-m-d H:i:s',
|
'open_datetime' => 'required|date_format:Y-m-d H:i:s',
|
||||||
'link_url' => 'nullable|string|max:255',
|
'link_url' => 'nullable|string|max:255',
|
||||||
'image1_filename' => 'nullable|string|max:255',
|
'image1_filename' => 'nullable|string|max:255',
|
||||||
'image2_filename' => 'nullable|string|max:255',
|
'image2_filename' => 'nullable|string|max:255',
|
||||||
'mode' => 'required|integer|min:0|max:9',
|
'mode' => 'required|integer|min:0|max:9',
|
||||||
]);
|
], $messages, $attributes);
|
||||||
|
|
||||||
// 更新
|
// 更新
|
||||||
DB::table($this->table)->where($this->pk, $id)->update([
|
DB::table($this->table)->where($this->pk, $id)->update([
|
||||||
'news' => $v['news'],
|
'news' => $v['news'],
|
||||||
'open_datetime' => $v['open_datetime'] ?? null,
|
'open_datetime' => $v['open_datetime'],
|
||||||
'link_url' => $v['link_url'] ?? null,
|
'link_url' => $v['link_url'] ?? null,
|
||||||
'image1_filename' => $v['image1_filename'] ?? null,
|
'image1_filename' => $v['image1_filename'] ?? null,
|
||||||
'image2_filename' => $v['image2_filename'] ?? null,
|
'image2_filename' => $v['image2_filename'] ?? null,
|
||||||
|
|||||||
@ -18,7 +18,7 @@ class OpeController extends Controller
|
|||||||
$inputs = [
|
$inputs = [
|
||||||
'isMethodPost' => $request->isMethod('post'),
|
'isMethodPost' => $request->isMethod('post'),
|
||||||
'sort' => $request->input('sort', 'ope_id'),
|
'sort' => $request->input('sort', 'ope_id'),
|
||||||
'sort_type' => $request->input('sort_type', 'desc'),
|
'sort_type' => $request->input('sort_type', 'asc'),
|
||||||
'isExport' => false,
|
'isExport' => false,
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -37,17 +37,17 @@ class OpeController extends Controller
|
|||||||
public function add(Request $request)
|
public function add(Request $request)
|
||||||
{
|
{
|
||||||
if ($request->isMethod('get')) {
|
if ($request->isMethod('get')) {
|
||||||
// add.blade.php は include する _form が期待する変数名を使う
|
|
||||||
return view('admin.opes.add', [
|
return view('admin.opes.add', [
|
||||||
'isEdit' => 0,
|
|
||||||
'isInfo' => 0,
|
'isEdit' => false,
|
||||||
// 初期値(存在しなくてもOKだが、Notice 防止のために入れておく)
|
'record' => new Ope(),
|
||||||
'ope_id' => null,
|
'ope_id' => null,
|
||||||
'ope_name' => '',
|
'ope_name' => '',
|
||||||
'ope_type' => '',
|
'ope_type' => '',
|
||||||
'ope_mail' => '',
|
'ope_mail' => '',
|
||||||
'ope_phone'=> '',
|
'ope_phone'=> '',
|
||||||
// 以下はフォームで参照される可能性のあるキーを空で用意
|
|
||||||
'ope_sendalart_que1' => 0, 'ope_sendalart_que2' => 0, 'ope_sendalart_que3' => 0,
|
'ope_sendalart_que1' => 0, 'ope_sendalart_que2' => 0, 'ope_sendalart_que3' => 0,
|
||||||
'ope_sendalart_que4' => 0, 'ope_sendalart_que5' => 0, 'ope_sendalart_que6' => 0,
|
'ope_sendalart_que4' => 0, 'ope_sendalart_que5' => 0, 'ope_sendalart_que6' => 0,
|
||||||
'ope_sendalart_que7' => 0, 'ope_sendalart_que8' => 0, 'ope_sendalart_que9' => 0,
|
'ope_sendalart_que7' => 0, 'ope_sendalart_que8' => 0, 'ope_sendalart_que9' => 0,
|
||||||
@ -58,21 +58,46 @@ class OpeController extends Controller
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 入力値を一旦取得
|
||||||
|
$data = $request->all();
|
||||||
|
|
||||||
|
// --- バリデーション ---
|
||||||
$rules = [
|
$rules = [
|
||||||
|
'login_id' => 'required|string|max:255|unique:ope,login_id',
|
||||||
'ope_name' => 'required|string|max:255',
|
'ope_name' => 'required|string|max:255',
|
||||||
'ope_type' => 'required|string|max:50',
|
'ope_type' => 'required|string|max:50',
|
||||||
'ope_mail' => 'nullable|email|max:255',
|
'ope_mail' => [
|
||||||
|
'required',
|
||||||
|
function ($attribute, $value, $fail) {
|
||||||
|
// ; でも , でもOK、保存時は ; に統一
|
||||||
|
$emails = array_map('trim', explode(';', str_replace(',', ';', $value)));
|
||||||
|
foreach ($emails as $email) {
|
||||||
|
if ($email !== '' && !filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||||
|
$fail("無効なメールアドレス形式です: {$email}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
'ope_phone' => 'nullable|string|max:50',
|
'ope_phone' => 'nullable|string|max:50',
|
||||||
|
'password' => 'required|string|min:8|confirmed',
|
||||||
];
|
];
|
||||||
$this->validate($request, $rules);
|
|
||||||
|
|
||||||
|
$request->validate($rules);
|
||||||
|
|
||||||
|
// --- 保存用にメールを ; 区切りに統一 ---
|
||||||
|
$emails = array_filter(array_map('trim', explode(';', str_replace(',', ';', $data['ope_mail']))));
|
||||||
|
$data['ope_mail'] = implode(';', $emails);
|
||||||
|
|
||||||
|
// 保存処理
|
||||||
$ope = new Ope();
|
$ope = new Ope();
|
||||||
$ope->fill($request->only($ope->getFillable()));
|
$ope->fill($data);
|
||||||
$ope->save();
|
$ope->save();
|
||||||
|
|
||||||
return redirect()->route('opes')->with('success', 'オペレータを登録しました。');
|
return redirect()->route('opes')->with('success', '登録しました。');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 編集(GET 画面 / POST 更新)
|
* 編集(GET 画面 / POST 更新)
|
||||||
*/
|
*/
|
||||||
@ -82,63 +107,76 @@ class OpeController extends Controller
|
|||||||
if (!$ope) abort(404);
|
if (!$ope) abort(404);
|
||||||
|
|
||||||
if ($request->isMethod('get')) {
|
if ($request->isMethod('get')) {
|
||||||
// edit.blade.php が参照する変数名に合わせて渡す
|
return view('admin.opes.edit', [
|
||||||
return view('admin.opes.edit', array_merge(
|
'isEdit' => true,
|
||||||
[
|
'record' => $ope,
|
||||||
'isEdit' => 1,
|
]);
|
||||||
'isInfo' => 0,
|
|
||||||
'ope_id' => $ope->ope_id,
|
|
||||||
],
|
|
||||||
$ope->toArray()
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 入力値を一旦取得
|
||||||
|
$data = $request->all();
|
||||||
|
|
||||||
|
// --- バリデーション ---
|
||||||
$rules = [
|
$rules = [
|
||||||
|
'login_id' => "required|string|max:255|unique:ope,login_id,{$id},ope_id", // 編集時は自分を除外
|
||||||
'ope_name' => 'required|string|max:255',
|
'ope_name' => 'required|string|max:255',
|
||||||
'ope_type' => 'required|string|max:50',
|
'ope_type' => 'required|string|max:50',
|
||||||
'ope_mail' => 'nullable|email|max:255',
|
|
||||||
'ope_phone' => 'nullable|string|max:50',
|
'ope_phone' => 'nullable|string|max:50',
|
||||||
|
'ope_mail' => [
|
||||||
|
'required',
|
||||||
|
function ($attribute, $value, $fail) {
|
||||||
|
// , でも ; でもOKにする
|
||||||
|
$emails = array_map('trim', explode(';', str_replace(',', ';', $value)));
|
||||||
|
foreach ($emails as $email) {
|
||||||
|
if ($email !== '' && !filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||||
|
$fail("無効なメールアドレス形式です: {$email}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'password' => 'nullable|string|min:8|confirmed', // 編集時は任意
|
||||||
];
|
];
|
||||||
$this->validate($request, $rules);
|
|
||||||
|
|
||||||
$ope->fill($request->only($ope->getFillable()));
|
$request->validate($rules);
|
||||||
|
|
||||||
|
// --- 保存用にメールを ; 区切りに統一 ---
|
||||||
|
if (!empty($data['ope_mail'])) {
|
||||||
|
$emails = array_filter(array_map('trim', explode(';', str_replace(',', ';', $data['ope_mail']))));
|
||||||
|
$data['ope_mail'] = implode(';', $emails);
|
||||||
|
}
|
||||||
|
|
||||||
|
// パスワード空なら更新しない
|
||||||
|
if (empty($data['password'])) {
|
||||||
|
unset($data['password']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存処理
|
||||||
|
$ope->fill($data);
|
||||||
$ope->save();
|
$ope->save();
|
||||||
|
|
||||||
return redirect()->route('opes')->with('success', 'オペレータを更新しました。');
|
return redirect()->route('opes')->with('success', '更新しました。');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 詳細
|
|
||||||
*/
|
|
||||||
public function info($id)
|
|
||||||
{
|
|
||||||
$ope = Ope::getByPk($id);
|
|
||||||
if (!$ope) abort(404);
|
|
||||||
|
|
||||||
// info.blade.php が参照する変数に合わせてセット
|
|
||||||
return view('admin.opes.info', array_merge(
|
|
||||||
[
|
|
||||||
'isEdit' => 0,
|
|
||||||
'isInfo' => 1,
|
|
||||||
'ope_id' => $ope->ope_id,
|
|
||||||
],
|
|
||||||
$ope->toArray()
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 削除(単体 / 複数)
|
* 削除(単体 or 複数)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public function delete(Request $request)
|
public function delete(Request $request)
|
||||||
{
|
{
|
||||||
$ids = [];
|
$ids = [];
|
||||||
|
|
||||||
|
// 単体削除
|
||||||
if ($request->filled('id')) {
|
if ($request->filled('id')) {
|
||||||
$ids[] = (int) $request->input('id');
|
$ids[] = (int) $request->input('id');
|
||||||
}
|
}
|
||||||
if ($request->filled('ids') && is_array($request->input('ids'))) {
|
|
||||||
$ids = array_merge($ids, array_map('intval', $request->input('ids')));
|
// 複数削除
|
||||||
|
if ($request->filled('ids')) {
|
||||||
|
$ids = array_merge($ids, array_map('intval', (array)$request->input('ids')));
|
||||||
}
|
}
|
||||||
$ids = array_values(array_unique($ids));
|
|
||||||
|
$ids = array_unique($ids);
|
||||||
|
|
||||||
if (!$ids) {
|
if (!$ids) {
|
||||||
return back()->with('error', '削除対象が選択されていません。');
|
return back()->with('error', '削除対象が選択されていません。');
|
||||||
|
|||||||
@ -22,39 +22,64 @@ class OperatorQueController extends Controller
|
|||||||
public function list(Request $request)
|
public function list(Request $request)
|
||||||
{
|
{
|
||||||
$sort = $request->input('sort', 'que_id');
|
$sort = $request->input('sort', 'que_id');
|
||||||
$sort_type = $request->input('sort_type', 'desc');
|
$sort_type = $request->input('sort_type', 'asc');
|
||||||
|
$que_status = $request->input('que_status');
|
||||||
|
|
||||||
|
// 許可されたカラム名のリスト(DB定義に合わせて)
|
||||||
|
$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'])) {
|
||||||
|
$sort_type = 'desc';
|
||||||
|
}
|
||||||
|
|
||||||
$query = OperatorQue::query();
|
$query = OperatorQue::query();
|
||||||
|
|
||||||
if ($request->filled('que_status')) {
|
// フィルタリング(絞り込み)
|
||||||
$query->where('que_status', $request->input('que_status'));
|
if (!empty($que_status)) {
|
||||||
|
$query->where('que_status', $que_status);
|
||||||
}
|
}
|
||||||
|
|
||||||
$list = $query->orderBy($sort, $sort_type)
|
$list = $query->orderBy($sort, $sort_type)
|
||||||
->paginate(\App\Utils::item_per_page ?? 20);
|
->paginate(\App\Utils::item_per_page ?? 20);
|
||||||
|
|
||||||
$que_status = $request->input('que_status');
|
// view に $que_status を渡す
|
||||||
|
return view('admin.operator_ques.list', compact('list', 'sort', 'sort_type', 'que_status'));
|
||||||
return view('admin.operator_ques.list', compact('list', 'sort', 'sort_type'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 新規登録(画面/処理)
|
* 新規登録(画面/処理)
|
||||||
*/
|
*/
|
||||||
public function add(Request $request)
|
public function add(Request $request)
|
||||||
{
|
{
|
||||||
if ($request->isMethod('get')) {
|
if ($request->isMethod('get')) {
|
||||||
// 新規時は空の値でフォーム描画
|
// 新規時は空のレコードを用意してフォーム描画
|
||||||
return view('admin.operator_ques.add', $this->formPayload());
|
return view('admin.operator_ques.add', array_merge(
|
||||||
|
$this->formPayload(),
|
||||||
|
[
|
||||||
|
'isEdit' => false,
|
||||||
|
'record' => new OperatorQue(), // ← ★ _form.blade.php で使う用
|
||||||
|
'que_id' => null,
|
||||||
|
]
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// POST時:バリデーション
|
||||||
$data = $this->validateRequest($request);
|
$data = $this->validateRequest($request);
|
||||||
|
|
||||||
|
// 登録処理
|
||||||
OperatorQue::create($data);
|
OperatorQue::create($data);
|
||||||
|
|
||||||
return redirect()->route('operator_ques')->with('success', 'オペレーターキューを登録しました。');
|
return redirect()->route('operator_ques')->with('success', '登録しました。');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 編集(画面/処理)
|
* 編集(画面/処理)
|
||||||
*/
|
*/
|
||||||
@ -65,7 +90,10 @@ class OperatorQueController extends Controller
|
|||||||
if ($request->isMethod('get')) {
|
if ($request->isMethod('get')) {
|
||||||
return view('admin.operator_ques.edit', array_merge(
|
return view('admin.operator_ques.edit', array_merge(
|
||||||
$this->formPayload($que),
|
$this->formPayload($que),
|
||||||
['que_id' => $que->que_id]
|
[
|
||||||
|
'que_id' => $que->que_id,
|
||||||
|
'record' => $que,
|
||||||
|
]
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,7 +101,7 @@ class OperatorQueController extends Controller
|
|||||||
|
|
||||||
$que->fill($data)->save();
|
$que->fill($data)->save();
|
||||||
|
|
||||||
return redirect()->route('operator_ques')->with('success', 'オペレーターキューを更新しました。');
|
return redirect()->route('operator_ques')->with('success', '更新しました。');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -222,9 +250,9 @@ class OperatorQueController extends Controller
|
|||||||
private function validateRequest(Request $request, $queId = null): array
|
private function validateRequest(Request $request, $queId = null): array
|
||||||
{
|
{
|
||||||
$rules = [
|
$rules = [
|
||||||
'user_id' => 'required|integer',
|
'user_id' => 'nullable|integer',
|
||||||
'contract_id' => 'nullable|integer',
|
'contract_id' => 'nullable|integer',
|
||||||
'park_id' => 'required|integer',
|
'park_id' => 'nullable|integer',
|
||||||
'que_class' => 'required|integer',
|
'que_class' => 'required|integer',
|
||||||
'que_comment' => 'nullable|string|max:2000',
|
'que_comment' => 'nullable|string|max:2000',
|
||||||
'que_status' => 'required|integer',
|
'que_status' => 'required|integer',
|
||||||
|
|||||||
230
app/Http/Controllers/Admin/OpesController.php
Normal file
230
app/Http/Controllers/Admin/OpesController.php
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Models\Usertype;
|
||||||
|
use App\Models\Ope;
|
||||||
|
|
||||||
|
class PersonalController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 本人確認手動処理 一覧画面
|
||||||
|
*/
|
||||||
|
public function list(Request $request)
|
||||||
|
{
|
||||||
|
$query = User::query();
|
||||||
|
|
||||||
|
if ($request->filled('user_id')) {
|
||||||
|
$query->where('user_id', $request->input('user_id'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$users = $query->paginate(20);
|
||||||
|
|
||||||
|
return view('admin.personal.list', [
|
||||||
|
'users' => $users,
|
||||||
|
'request' => $request,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 本人確認手動処理 編集画面
|
||||||
|
*/
|
||||||
|
public function edit(Request $request, $id)
|
||||||
|
{
|
||||||
|
// 利用者情報取得
|
||||||
|
$user = User::where('user_id', $id)->firstOrFail();
|
||||||
|
|
||||||
|
// 利用者分類マスタ取得(ラジオボタン用)
|
||||||
|
$usertypes = Usertype::orderBy('sort_order')->get();
|
||||||
|
|
||||||
|
// POST時の処理
|
||||||
|
if ($request->isMethod('post')) {
|
||||||
|
// 利用者分類IDの更新
|
||||||
|
$user->user_categoryid = $request->input('user_categoryid', $user->user_categoryid);
|
||||||
|
|
||||||
|
// 本人確認チェックOK/NG
|
||||||
|
if ($request->input('check') === 'ok') {
|
||||||
|
$user->user_idcard_chk_flag = 1;
|
||||||
|
} elseif ($request->input('check') === 'ng') {
|
||||||
|
$user->user_idcard_chk_flag = 0;
|
||||||
|
// 備考欄も更新(NG理由)
|
||||||
|
$user->user_remarks = $request->input('user_remarks', $user->user_remarks);
|
||||||
|
}
|
||||||
|
|
||||||
|
$user->save();
|
||||||
|
|
||||||
|
return redirect()->route('personal')->with('success', '更新しました');
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('admin.personal.edit', [
|
||||||
|
'user' => $user,
|
||||||
|
'usertypes' => $usertypes,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OpesController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* オペレータ一覧画面
|
||||||
|
*/
|
||||||
|
public function list(Request $request)
|
||||||
|
{
|
||||||
|
$sort = $request->input('sort', 'ope_id'); // デフォルト値を設定
|
||||||
|
$sort_type = $request->input('sort_type', 'asc'); // デフォルト値を設定
|
||||||
|
|
||||||
|
$query = Ope::query();
|
||||||
|
|
||||||
|
// 並び替え
|
||||||
|
$query->orderBy($sort, $sort_type);
|
||||||
|
|
||||||
|
$list = $query->paginate(20);
|
||||||
|
|
||||||
|
return view('admin.opes.list', [
|
||||||
|
'list' => $list,
|
||||||
|
'sort' => $sort,
|
||||||
|
'sort_type' => $sort_type,
|
||||||
|
'request' => $request,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* オペレータ編集画面
|
||||||
|
*/
|
||||||
|
public function edit(Request $request, $id)
|
||||||
|
{
|
||||||
|
$ope = \App\Models\Ope::findOrFail($id);
|
||||||
|
|
||||||
|
if ($request->isMethod('post')) {
|
||||||
|
// バリデーション&更新処理
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
// 各項目を配列で渡す
|
||||||
|
return view('admin.opes.edit', [
|
||||||
|
'ope_id' => $ope->ope_id,
|
||||||
|
'ope_name' => $ope->ope_name,
|
||||||
|
'login_id' => $ope->login_id,
|
||||||
|
'ope_pass' => '', // パスワードは空で
|
||||||
|
'ope_belong' => $ope->ope_belong,
|
||||||
|
'ope_type' => $ope->ope_type,
|
||||||
|
'ope_mail' => $ope->ope_mail,
|
||||||
|
'ope_phone' => $ope->ope_phone,
|
||||||
|
'ope_sendalart_que1' => $ope->ope_sendalart_que1,
|
||||||
|
'ope_sendalart_que2' => $ope->ope_sendalart_que2,
|
||||||
|
'ope_sendalart_que3' => $ope->ope_sendalart_que3,
|
||||||
|
'ope_sendalart_que4' => $ope->ope_sendalart_que4,
|
||||||
|
'ope_sendalart_que5' => $ope->ope_sendalart_que5,
|
||||||
|
'ope_sendalart_que6' => $ope->ope_sendalart_que6,
|
||||||
|
'ope_sendalart_que7' => $ope->ope_sendalart_que7,
|
||||||
|
'ope_sendalart_que8' => $ope->ope_sendalart_que8,
|
||||||
|
'ope_sendalart_que9' => $ope->ope_sendalart_que9,
|
||||||
|
'ope_sendalart_que10' => $ope->ope_sendalart_que10,
|
||||||
|
'ope_sendalart_que11' => $ope->ope_sendalart_que11,
|
||||||
|
'ope_sendalart_que12' => $ope->ope_sendalart_que12,
|
||||||
|
'ope_sendalart_que13' => $ope->ope_sendalart_que13,
|
||||||
|
'ope_auth1' => $ope->ope_auth1,
|
||||||
|
'ope_auth2' => $ope->ope_auth2,
|
||||||
|
'ope_auth3' => $ope->ope_auth3,
|
||||||
|
'ope_auth4' => $ope->ope_auth4,
|
||||||
|
'ope_quit_flag' => $ope->ope_quit_flag,
|
||||||
|
'ope_quitday' => $ope->ope_quitday,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* オペレータ一覧のエクスポート
|
||||||
|
*/
|
||||||
|
public function export(Request $request)
|
||||||
|
{
|
||||||
|
$filename = 'ope_export_' . date('Ymd_His') . '.csv';
|
||||||
|
$columns = [
|
||||||
|
'ope_id', 'ope_belong', 'login_id', 'ope_name', 'ope_pass', 'ope_type', 'ope_mail', 'ope_phone',
|
||||||
|
'ope_sendalart_que1', 'ope_sendalart_que2', 'ope_sendalart_que3', 'ope_sendalart_que4', 'ope_sendalart_que5',
|
||||||
|
'ope_sendalart_que6', 'ope_sendalart_que7', 'ope_sendalart_que8', 'ope_sendalart_que9', 'ope_sendalart_que10',
|
||||||
|
'ope_sendalart_que11', 'ope_sendalart_que12', 'ope_sendalart_que13',
|
||||||
|
'ope_auth1', 'ope_auth2', 'ope_auth3', 'ope_auth4',
|
||||||
|
'ope_quit_flag', 'ope_quitday', 'created_at', 'updated_at'
|
||||||
|
];
|
||||||
|
|
||||||
|
$ids = $request->input('pk', []);
|
||||||
|
if (!empty($ids)) {
|
||||||
|
$list = \App\Models\Ope::whereIn('ope_id', $ids)->select($columns)->get();
|
||||||
|
} else {
|
||||||
|
$list = \App\Models\Ope::select($columns)->get();
|
||||||
|
}
|
||||||
|
|
||||||
|
$callback = function() use ($list, $columns) {
|
||||||
|
$file = fopen('php://output', 'w');
|
||||||
|
// ヘッダー
|
||||||
|
fputcsv($file, $columns);
|
||||||
|
|
||||||
|
foreach ($list as $row) {
|
||||||
|
$data = [];
|
||||||
|
foreach ($columns as $col) {
|
||||||
|
$data[] = $row->$col;
|
||||||
|
}
|
||||||
|
fputcsv($file, $data);
|
||||||
|
}
|
||||||
|
fclose($file);
|
||||||
|
};
|
||||||
|
|
||||||
|
return response()->stream($callback, 200, [
|
||||||
|
"Content-Type" => "text/csv",
|
||||||
|
"Content-Disposition" => "attachment; filename={$filename}",
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* オペレータの削除
|
||||||
|
*/
|
||||||
|
public function delete(Request $request)
|
||||||
|
{
|
||||||
|
// チェックされたIDの配列を受け取る想定
|
||||||
|
$ids = $request->input('pk', []);
|
||||||
|
if (!empty($ids)) {
|
||||||
|
\App\Models\Ope::whereIn('ope_id', $ids)->delete();
|
||||||
|
return redirect()->route('opes')->with('success', '削除しました');
|
||||||
|
}
|
||||||
|
return redirect()->route('opes')->with('error', '削除対象が選択されていません');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* オペレータの追加
|
||||||
|
*/
|
||||||
|
public function add(Request $request)
|
||||||
|
{
|
||||||
|
if ($request->isMethod('post')) {
|
||||||
|
$validated = $request->validate([
|
||||||
|
'ope_name' => 'required|string|max:255',
|
||||||
|
'login_id' => 'required|string|max:255|unique:ope,login_id',
|
||||||
|
'password' => 'required|string|min:6|confirmed',
|
||||||
|
'ope_type' => 'required',
|
||||||
|
'ope_mail' => 'required|email',
|
||||||
|
]);
|
||||||
|
$ope = new \App\Models\Ope();
|
||||||
|
$ope->ope_name = $request->ope_name;
|
||||||
|
$ope->login_id = $request->login_id;
|
||||||
|
$ope->ope_pass = bcrypt($request->password);
|
||||||
|
$ope->ope_type = $request->ope_type;
|
||||||
|
$ope->ope_mail = $request->ope_mail;
|
||||||
|
$ope->ope_phone = $request->ope_phone;
|
||||||
|
for ($i = 1; $i <= 13; $i++) {
|
||||||
|
$field = "ope_sendalart_que{$i}";
|
||||||
|
$ope->$field = $request->$field ?? 0;
|
||||||
|
}
|
||||||
|
for ($i = 1; $i <= 4; $i++) {
|
||||||
|
$field = "ope_auth{$i}";
|
||||||
|
$ope->$field = $request->$field ?? '';
|
||||||
|
}
|
||||||
|
$ope->ope_quit_flag = $request->ope_quit_flag ?? 0;
|
||||||
|
$ope->ope_quitday = $request->ope_quitday ?? null;
|
||||||
|
$ope->save();
|
||||||
|
return redirect()->route('opes')->with('success', '登録しました');
|
||||||
|
}
|
||||||
|
return view('admin.opes.add');
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -11,44 +11,38 @@ use Illuminate\Http\Request;
|
|||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use Illuminate\Support\Facades\Validator;
|
use Illuminate\Support\Facades\Validator;
|
||||||
use Response;
|
use Response;
|
||||||
|
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||||
|
|
||||||
class ParkController extends Controller
|
class ParkController extends Controller
|
||||||
{
|
{
|
||||||
public function list(Request $request)
|
public function list(Request $request)
|
||||||
{
|
{
|
||||||
$query = \DB::table('park as p')
|
$query = \DB::table('park as p')
|
||||||
->leftJoin('city as c', 'p.city_id', '=', 'c.city_id')
|
->leftJoin('city as c', 'p.city_id', '=', 'c.city_id')
|
||||||
->select([
|
->select([
|
||||||
'p.park_id',
|
'p.*',
|
||||||
'c.city_name',
|
'c.city_name',
|
||||||
'p.park_name',
|
]);
|
||||||
'p.park_ruby',
|
|
||||||
'p.park_syllabary',
|
|
||||||
'p.park_adrs',
|
|
||||||
'p.park_close_flag',
|
|
||||||
'p.park_day',
|
|
||||||
'p.alert_flag',
|
|
||||||
'p.print_number',
|
|
||||||
'p.keep_alive',
|
|
||||||
]);
|
|
||||||
|
|
||||||
if ($request->filled('park_name')) {
|
if ($request->filled('park_name')) {
|
||||||
$query->where('p.park_name', 'like', '%' . $request->input('park_name') . '%');
|
$query->where('p.park_name', 'like', '%' . $request->input('park_name') . '%');
|
||||||
}
|
|
||||||
if ($request->filled('city_id')) {
|
|
||||||
$query->where('p.city_id', $request->input('city_id'));
|
|
||||||
}
|
|
||||||
if ($request->filled('sort')) {
|
|
||||||
$query->orderBy($request->input('sort'), $request->input('sort_type', 'asc'));
|
|
||||||
} else {
|
|
||||||
$query->orderBy('p.park_id', 'asc');
|
|
||||||
}
|
|
||||||
|
|
||||||
$parks = $query->paginate(20);
|
|
||||||
$cities = \DB::table('city')->orderBy('city_id')->get();
|
|
||||||
|
|
||||||
return view('admin.parks.list', compact('parks', 'cities'));
|
|
||||||
}
|
}
|
||||||
|
if ($request->filled('city_id')) {
|
||||||
|
$query->where('p.city_id', $request->input('city_id'));
|
||||||
|
}
|
||||||
|
if ($request->filled('sort')) {
|
||||||
|
$query->orderBy($request->input('sort'), $request->input('sort_type', 'asc'));
|
||||||
|
} else {
|
||||||
|
$query->orderBy('p.park_id', 'asc');
|
||||||
|
}
|
||||||
|
|
||||||
|
$parks = $query->paginate(20);
|
||||||
|
$cities = \DB::table('city')->orderBy('city_id')->get();
|
||||||
|
|
||||||
|
$sort = $request->input('sort', 'p.park_id');
|
||||||
|
$sort_type = $request->input('sort_type', 'asc');
|
||||||
|
return view('admin.parks.list', compact('parks', 'cities', 'sort', 'sort_type'));
|
||||||
|
}
|
||||||
|
|
||||||
public function add(Request $request)
|
public function add(Request $request)
|
||||||
{
|
{
|
||||||
@ -65,9 +59,10 @@ class ParkController extends Controller
|
|||||||
// 保存処理
|
// 保存処理
|
||||||
$park = new \App\Models\Park();
|
$park = new \App\Models\Park();
|
||||||
$park->fill($validated);
|
$park->fill($validated);
|
||||||
|
$park->operator_id = auth()->user()->ope_id ?? 1;
|
||||||
$park->save();
|
$park->save();
|
||||||
|
|
||||||
return redirect()->route('parks')->with('success', '登録しました');
|
return redirect()->route('parks')->with('success', '新規登録に完了しました。');
|
||||||
}
|
}
|
||||||
|
|
||||||
return view('admin.parks.add', [
|
return view('admin.parks.add', [
|
||||||
@ -77,14 +72,14 @@ class ParkController extends Controller
|
|||||||
|
|
||||||
public function edit(Request $request, $pk, $view = '')
|
public function edit(Request $request, $pk, $view = '')
|
||||||
{
|
{
|
||||||
$park = Park::getByPk($pk);
|
$park = Park::find($pk);
|
||||||
if (empty($pk) || empty($park)) {
|
if (empty($pk) || empty($park)) {
|
||||||
abort('404');
|
abort('404');
|
||||||
}
|
}
|
||||||
$data = $park->getAttributes();
|
$data = $park->getAttributes();
|
||||||
$dataList = $this->getDataDropList();
|
$dataList = $this->getDataDropList();
|
||||||
$data = array_merge($data, $dataList);
|
$data = array_merge($data, $dataList);
|
||||||
if ($request->isMethod('POST')) {
|
if ($request->isMethod('POST') || $request->isMethod('PUT')) {
|
||||||
// ここをaddと同じバリデーションに変更
|
// ここをaddと同じバリデーションに変更
|
||||||
$validated = $request->validate([
|
$validated = $request->validate([
|
||||||
'city_id' => 'required|integer',
|
'city_id' => 'required|integer',
|
||||||
@ -114,7 +109,7 @@ class ParkController extends Controller
|
|||||||
{
|
{
|
||||||
$arr_pk = $request->get('pk');
|
$arr_pk = $request->get('pk');
|
||||||
if ($arr_pk) {
|
if ($arr_pk) {
|
||||||
if (Park::deleteByPk($arr_pk)) {
|
if (Park::destroy($arr_pk)) {
|
||||||
return redirect()->route('parks')->with('success', __("削除が完了しました。"));
|
return redirect()->route('parks')->with('success', __("削除が完了しました。"));
|
||||||
} else {
|
} else {
|
||||||
return redirect()->route('parks')->with('error', __('削除に失敗しました。'));
|
return redirect()->route('parks')->with('error', __('削除に失敗しました。'));
|
||||||
@ -134,67 +129,81 @@ class ParkController extends Controller
|
|||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function export(Request $request)
|
|
||||||
{
|
|
||||||
|
|
||||||
$headers = array(
|
|
||||||
"Content-type" => "text/csv;charset=UTF-8",
|
|
||||||
'Content-Encoding: UTF-8',
|
|
||||||
"Content-Disposition" => "attachment; filename=file.csv",
|
|
||||||
"Pragma" => "no-cache",
|
|
||||||
"Cache-Control" => "must-revalidate, post-check=0, pre-check=0",
|
|
||||||
"Expires" => "0"
|
|
||||||
);
|
|
||||||
$inputs = [
|
|
||||||
'isMethodPost' => 0,
|
|
||||||
'isExport' => 1,
|
|
||||||
'sort' => $request->input('sort', ''),
|
|
||||||
'sort_type' => $request->input('sort_type', ''),
|
|
||||||
|
|
||||||
];
|
public function export(Request $request)
|
||||||
|
{
|
||||||
|
$columns = [
|
||||||
|
'駐輪場ID', '市区', '駐輪場名', '駐輪場ふりがな', '駐輪場五十音', '住所',
|
||||||
|
'閉設フラグ', '閉設日', '残警告チェックフラグ', '印字数', '最新キープアライブ',
|
||||||
|
'更新オペレータID', '更新期間開始日', '更新期間開始時', '更新期間終了日', '更新期間終了時',
|
||||||
|
'駐輪開始期間', 'リマインダー種別', 'リマインダー時間', '契約後即利用許可',
|
||||||
|
'項目表示設定:性別', '項目表示設定:生年月日', '項目表示設定:防犯登録番号',
|
||||||
|
'二点間距離', '駐車場座標(緯度)', '駐車場座標(経度)', '電話番号',
|
||||||
|
'駐輪場契約形態(定期)', '駐輪場契約形態(一時利用)', '車種制限', '手続方法', '支払方法',
|
||||||
|
'利用可能時間制限フラグ', '利用可能時間(開始)', '利用可能時間(終了)',
|
||||||
|
'常駐管理人フラグ', '常駐時間(開始)', '常駐時間(終了)',
|
||||||
|
'屋根フラグ', 'シール発行機フラグ', '駐輪場利用方法', '定期更新期間',
|
||||||
|
'空き待ち予約', '特記事項', '学生証確認種別',
|
||||||
|
'減免案内表示フラグ', '減免対象年齢', '減免案内表示開始月数', '年跨ぎ'
|
||||||
|
];
|
||||||
|
|
||||||
$dataExport = Park::search($inputs);
|
$dataExport = DB::table('park as p')
|
||||||
$columns = array(
|
->select([
|
||||||
__('駐輪場ID '),// 0
|
'p.park_id', 'p.city_id', 'p.park_name', 'p.park_ruby', 'p.park_syllabary', 'p.park_adrs',
|
||||||
__('市区ID'),// 1
|
'p.park_close_flag', 'p.park_day', 'p.alert_flag', 'p.print_number', 'p.keep_alive',
|
||||||
__('市区'),// 2
|
'p.operator_id', 'p.update_grace_period_start_date', 'p.update_grace_period_start_time',
|
||||||
__('駐輪場名'),// 3
|
'p.update_grace_period_end_date', 'p.update_grace_period_end_time',
|
||||||
__('駐輪場ふりがな'),// 4
|
'p.parking_start_grace_period', 'p.reminder_type', 'p.reminder_time', 'p.immediate_use_permit',
|
||||||
__('駐輪場五十音'),// 5
|
'p.gender_display_flag', 'p.bd_display_flag', 'p.securityreg_display_flag',
|
||||||
__('住所'),// 6
|
'p.distance_twopoints', 'p.park_latitude', 'p.park_longitude', 'p.park_tel',
|
||||||
__('閉設フラグ'),// 7
|
'p.park_fixed_contract', 'p.park_temporary_contract', 'p.park_restriction',
|
||||||
__('閉設フラグ'),// 8
|
'p.park_procedure', 'p.park_payment',
|
||||||
__('閉設日'),// 9
|
'p.park_available_time_flag', 'p.park_available_time_from', 'p.park_available_time_to',
|
||||||
__('残警告チェックフラグ'),// 10
|
'p.park_manager_flag', 'p.park_manager_resident_from', 'p.park_manager_resident_to',
|
||||||
__('印字数'),// 11
|
'p.park_roof_flag', 'p.park_issuing_machine_flag', 'p.park_using_method',
|
||||||
__('最新キープアライブ')// 12
|
'p.park_contract_renewal_term', 'p.park_reservation', 'p.park_reference',
|
||||||
);
|
'p.student_id_confirm_type', 'p.reduction_guide_display_flag', 'p.reduction_age',
|
||||||
$filename = "駐輪場マスタ.csv";
|
'p.reduction_guide_display_start_month', 'p.overyear_flag'
|
||||||
$file = fopen($filename, 'w+');
|
])
|
||||||
fputcsv($file, $columns);
|
->orderBy('p.park_id', 'asc')
|
||||||
foreach ($dataExport as $items) {
|
->get();
|
||||||
fputcsv(
|
|
||||||
$file,
|
$response = new StreamedResponse(function () use ($dataExport, $columns) {
|
||||||
array(
|
$stream = fopen('php://output', 'w');
|
||||||
$items->park_id,// 0
|
// Excel兼容 BOM
|
||||||
$items->city_id,// 1
|
fwrite($stream, chr(0xEF) . chr(0xBB) . chr(0xBF));
|
||||||
!empty($items->getCity()) ? $items->getCity()->city_name : "",// 2
|
fputcsv($stream, $columns);
|
||||||
$items->park_name, // 3
|
|
||||||
$items->park_ruby, // 4
|
foreach ($dataExport as $item) {
|
||||||
$items->park_syllabary, // 5
|
fputcsv($stream, [
|
||||||
$items->park_adrs, // 6
|
$item->park_id, $item->city_id, $item->park_name, $item->park_ruby, $item->park_syllabary, $item->park_adrs,
|
||||||
$items->park_close_flag,// 7
|
$item->park_close_flag, $item->park_day, $item->alert_flag, $item->print_number, $item->keep_alive,
|
||||||
$items->getParkCloseFlagDisplay(),// 8
|
$item->operator_id, $item->update_grace_period_start_date, $item->update_grace_period_start_time,
|
||||||
$items->park_day,// 9
|
$item->update_grace_period_end_date, $item->update_grace_period_end_time,
|
||||||
$items->alert_flag,// 10
|
$item->parking_start_grace_period, $item->reminder_type, $item->reminder_time, $item->immediate_use_permit,
|
||||||
$items->print_number,// 11
|
$item->gender_display_flag, $item->bd_display_flag, $item->securityreg_display_flag,
|
||||||
$items->keep_alive// 12
|
$item->distance_twopoints, $item->park_latitude, $item->park_longitude, $item->park_tel,
|
||||||
)
|
$item->park_fixed_contract, $item->park_temporary_contract, $item->park_restriction,
|
||||||
);
|
$item->park_procedure, $item->park_payment,
|
||||||
|
$item->park_available_time_flag, $item->park_available_time_from, $item->park_available_time_to,
|
||||||
|
$item->park_manager_flag, $item->park_manager_resident_from, $item->park_manager_resident_to,
|
||||||
|
$item->park_roof_flag, $item->park_issuing_machine_flag, $item->park_using_method,
|
||||||
|
$item->park_contract_renewal_term, $item->park_reservation, $item->park_reference,
|
||||||
|
$item->student_id_confirm_type, $item->reduction_guide_display_flag, $item->reduction_age,
|
||||||
|
$item->reduction_guide_display_start_month, $item->overyear_flag
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
fclose($file);
|
|
||||||
return Response::download($filename, $filename, $headers);
|
fclose($stream);
|
||||||
}
|
});
|
||||||
|
|
||||||
|
$response->headers->set('Content-Type', 'text/csv; charset=UTF-8');
|
||||||
|
$response->headers->set('Content-Disposition', 'attachment; filename="駐輪場マスタ.csv"');
|
||||||
|
$response->headers->set('Cache-Control', 'no-store, no-cache');
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
public function import(Request $request)
|
public function import(Request $request)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -44,6 +44,7 @@ class PaymentController extends Controller
|
|||||||
'payment_inquiryname' => 'nullable|string|max:255',
|
'payment_inquiryname' => 'nullable|string|max:255',
|
||||||
'payment_inquirytel' => 'nullable|string|max:255',
|
'payment_inquirytel' => 'nullable|string|max:255',
|
||||||
'payment_time' => 'nullable|string|max:255',
|
'payment_time' => 'nullable|string|max:255',
|
||||||
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// 登録データ作成
|
// 登録データ作成
|
||||||
@ -52,7 +53,7 @@ class PaymentController extends Controller
|
|||||||
|
|
||||||
Payment::create($data);
|
Payment::create($data);
|
||||||
|
|
||||||
return redirect()->route('payments')->with('success', '登録しました');
|
return redirect()->route('payments')->with('success', '登録しました。');
|
||||||
}
|
}
|
||||||
|
|
||||||
return view('admin.payments.add', [
|
return view('admin.payments.add', [
|
||||||
@ -80,7 +81,7 @@ class PaymentController extends Controller
|
|||||||
|
|
||||||
$payment->update($data);
|
$payment->update($data);
|
||||||
|
|
||||||
return redirect()->route('payments')->with('success', '更新しました');
|
return redirect()->route('payments')->with('success', '更新しました。');
|
||||||
}
|
}
|
||||||
|
|
||||||
return view('admin.payments.edit', [
|
return view('admin.payments.edit', [
|
||||||
@ -108,11 +109,22 @@ class PaymentController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function delete(Request $request)
|
public function delete(Request $request)
|
||||||
{
|
{
|
||||||
if ($request->has('ids')) {
|
$pk = $request->input('pk', []);
|
||||||
Payment::whereIn('payment_id', $request->ids)->delete();
|
|
||||||
return redirect()->route('payments')->with('success', '削除しました');
|
// 配列に統一
|
||||||
|
$ids = is_array($pk) ? $pk : [$pk];
|
||||||
|
|
||||||
|
// 数字チェック
|
||||||
|
$ids = array_values(array_filter($ids, fn($v) => preg_match('/^\d+$/', (string) $v)));
|
||||||
|
|
||||||
|
if (empty($ids)) {
|
||||||
|
return redirect()->route('payments')->with('error', '削除対象が選択されていません。');
|
||||||
}
|
}
|
||||||
return redirect()->route('payments')->with('error', '削除対象が選択されていません');
|
|
||||||
|
// 削除
|
||||||
|
Payment::whereIn('payment_id', $ids)->delete();
|
||||||
|
|
||||||
|
return redirect()->route('payments')->with('success', '削除しました。');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -10,13 +10,13 @@ class PeriodicalController extends Controller
|
|||||||
// 画面
|
// 画面
|
||||||
public function list(Request $request)
|
public function list(Request $request)
|
||||||
{
|
{
|
||||||
// Bladeで使うカラム名(id, name)で取得
|
$parks = DB::table('regular_contract')
|
||||||
$parks = DB::table('park')
|
->join('park', 'regular_contract.park_id', '=', 'park.park_id')
|
||||||
->select('park_id', 'park_name')
|
->select('park.park_id', 'park.park_name')
|
||||||
->orderBy('park_name')
|
->distinct()
|
||||||
|
->orderBy('park.park_name')
|
||||||
->get();
|
->get();
|
||||||
|
|
||||||
// 必要なら選択中のpark_idも渡す
|
|
||||||
$selectedParkId = $request->input('park_id', '');
|
$selectedParkId = $request->input('park_id', '');
|
||||||
|
|
||||||
return view('admin.periodical.list', compact('parks', 'selectedParkId'));
|
return view('admin.periodical.list', compact('parks', 'selectedParkId'));
|
||||||
@ -27,73 +27,365 @@ class PeriodicalController extends Controller
|
|||||||
{
|
{
|
||||||
$parkId = $request->input('park_id');
|
$parkId = $request->input('park_id');
|
||||||
|
|
||||||
// 契約状況
|
if (empty($parkId)) {
|
||||||
$bicycleGeneral = DB::table('regular_contract')
|
return response()->json([
|
||||||
->where('park_id', $parkId)
|
'contract_summary' => [],
|
||||||
->where('user_categoryid', 1)
|
'waiting_summary' => [],
|
||||||
->where('ptype_id', 1) // 1:自転車
|
'renewal_summary' => [],
|
||||||
->where('contract_cancel_flag', 0)
|
'debug_info' => ['message' => 'No park_id provided']
|
||||||
->count();
|
]);
|
||||||
$bicycleStudent = DB::table('regular_contract')
|
|
||||||
->where('park_id', $parkId)
|
|
||||||
->where('user_categoryid', 2)
|
|
||||||
->where('ptype_id', 1)
|
|
||||||
->where('contract_cancel_flag', 0)
|
|
||||||
->count();
|
|
||||||
// 原付・その他も同様に集計
|
|
||||||
|
|
||||||
$contractSummary = [
|
|
||||||
[
|
|
||||||
'type' => '自転車',
|
|
||||||
'general_count' => $bicycleGeneral,
|
|
||||||
'general_extra' => '',
|
|
||||||
'student_count' => $bicycleStudent,
|
|
||||||
'student_extra' => '',
|
|
||||||
'use_total' => $bicycleGeneral + $bicycleStudent,
|
|
||||||
'vacancy' => 8, // 例: 空き数
|
|
||||||
'total' => $bicycleGeneral + $bicycleStudent + 8,
|
|
||||||
'last' => [
|
|
||||||
'reserve_date' => '2025/07/27',
|
|
||||||
'contract_date' => '',
|
|
||||||
],
|
|
||||||
],
|
|
||||||
// 原付・その他も同様に
|
|
||||||
];
|
|
||||||
|
|
||||||
// 空き待ち状況
|
|
||||||
$waitingSummary = [
|
|
||||||
[
|
|
||||||
'type' => '自転車',
|
|
||||||
'general_count' => 28,
|
|
||||||
'general_head' => '2023/03/08',
|
|
||||||
'student_count' => 6,
|
|
||||||
'student_head' => '2023/11/04',
|
|
||||||
'total' => 34,
|
|
||||||
],
|
|
||||||
// 原付・その他も同様に
|
|
||||||
];
|
|
||||||
|
|
||||||
// 更新状況
|
|
||||||
$renewalSummary = [];
|
|
||||||
for ($m = 1; $m <= 12; $m++) {
|
|
||||||
$renewalSummary[] = [
|
|
||||||
'month' => sprintf('%02d月', $m),
|
|
||||||
'bicycle_general' => 0,
|
|
||||||
'bicycle_student' => 0,
|
|
||||||
'bicycle_total' => 0,
|
|
||||||
'moped_general' => 0,
|
|
||||||
'moped_student' => 0,
|
|
||||||
'moped_total' => 0,
|
|
||||||
'others_general' => 0,
|
|
||||||
'others_student' => 0,
|
|
||||||
'others_total' => 0,
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// デバッグ情報を収集
|
||||||
|
$debugInfo = [
|
||||||
|
'park_id' => $parkId,
|
||||||
|
'has_regular_type_id' => false,
|
||||||
|
'contract_count' => 0,
|
||||||
|
'waiting_count' => 0,
|
||||||
|
'renewal_count' => 0
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 契約状況を車種別に集計
|
||||||
|
$contractData = [
|
||||||
|
['psection_id' => 1, 'type' => '自転車'],
|
||||||
|
['psection_id' => 2, 'type' => '原付'],
|
||||||
|
['psection_id' => 0, 'type' => 'その他'] // その他は psection_id が1,2以外
|
||||||
|
];
|
||||||
|
|
||||||
|
$contractSummary = [];
|
||||||
|
$totalGeneral = 0;
|
||||||
|
$totalStudent = 0;
|
||||||
|
$totalUseTotal = 0;
|
||||||
|
$totalVacancy = 0;
|
||||||
|
$totalAll = 0;
|
||||||
|
|
||||||
|
foreach ($contractData as $vehicleType) {
|
||||||
|
$query = DB::table('regular_contract as rc')
|
||||||
|
->leftJoin('psection as ps', 'rc.psection_id', '=', 'ps.psection_id')
|
||||||
|
->selectRaw('SUM(CASE WHEN rc.user_categoryid = 1 THEN 1 ELSE 0 END) AS general_count')
|
||||||
|
->selectRaw('SUM(CASE WHEN rc.user_categoryid = 2 THEN 1 ELSE 0 END) AS student_count')
|
||||||
|
->selectRaw('SUM(CASE WHEN rc.contract_cancel_flag = 0 THEN 1 ELSE 0 END) AS use_total')
|
||||||
|
->selectRaw('SUM(CASE WHEN rc.contract_cancel_flag = 1 THEN 1 ELSE 0 END) AS vacancy')
|
||||||
|
->selectRaw('COUNT(*) AS total')
|
||||||
|
->where('rc.park_id', $parkId);
|
||||||
|
|
||||||
|
if ($vehicleType['psection_id'] === 0) {
|
||||||
|
// その他:psection_id が 1,2 以外
|
||||||
|
$query->whereNotIn('rc.psection_id', [1, 2]);
|
||||||
|
} else {
|
||||||
|
// 自転車または原付
|
||||||
|
$query->where('rc.psection_id', $vehicleType['psection_id']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = $query->first();
|
||||||
|
|
||||||
|
$generalCount = (int) ($result->general_count ?? 0);
|
||||||
|
$studentCount = (int) ($result->student_count ?? 0);
|
||||||
|
$useTotal = (int) ($result->use_total ?? 0);
|
||||||
|
$vacancy = (int) ($result->vacancy ?? 0);
|
||||||
|
$total = (int) ($result->total ?? 0);
|
||||||
|
|
||||||
|
// 最古の予約日と契約日を取得
|
||||||
|
$minReserveQuery = DB::table('regular_contract as rc2')
|
||||||
|
->join('reserve as r2', 'rc2.contract_id', '=', 'r2.contract_id')
|
||||||
|
->where('rc2.park_id', $parkId)
|
||||||
|
->where('r2.valid_flag', 1)
|
||||||
|
->where(function ($q) {
|
||||||
|
$q->whereNull('r2.reserve_cancel_flag')->orWhere('r2.reserve_cancel_flag', 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
if ($vehicleType['psection_id'] === 0) {
|
||||||
|
$minReserveQuery->whereNotIn('rc2.psection_id', [1, 2]);
|
||||||
|
} else {
|
||||||
|
$minReserveQuery->where('rc2.psection_id', $vehicleType['psection_id']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$minReserveDate = null;
|
||||||
|
$minContractDate = null;
|
||||||
|
|
||||||
|
$reserveResult = $minReserveQuery->orderBy('r2.reserve_date', 'asc')->first(['r2.reserve_date']);
|
||||||
|
if ($reserveResult) {
|
||||||
|
$minReserveDate = date('Y/m/d', strtotime($reserveResult->reserve_date));
|
||||||
|
}
|
||||||
|
|
||||||
|
$contractResult = $minReserveQuery->orderBy('r2.reserve_end', 'asc')->first(['r2.reserve_end']);
|
||||||
|
if ($contractResult) {
|
||||||
|
$minContractDate = date('Y/m/d', strtotime($contractResult->reserve_end));
|
||||||
|
}
|
||||||
|
|
||||||
|
$contractSummary[] = [
|
||||||
|
'type' => $vehicleType['type'],
|
||||||
|
'general_count' => $generalCount,
|
||||||
|
'general_extra' => '',
|
||||||
|
'student_count' => $studentCount,
|
||||||
|
'student_extra' => '',
|
||||||
|
'use_total' => $useTotal,
|
||||||
|
'vacancy' => $vacancy,
|
||||||
|
'total' => $total,
|
||||||
|
'last' => ['reserve_date' => $minReserveDate, 'contract_date' => $minContractDate],
|
||||||
|
];
|
||||||
|
|
||||||
|
$totalGeneral += $generalCount;
|
||||||
|
$totalStudent += $studentCount;
|
||||||
|
$totalUseTotal += $useTotal;
|
||||||
|
$totalVacancy += $vacancy;
|
||||||
|
$totalAll += $total;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 合計行を追加
|
||||||
|
$contractSummary[] = [
|
||||||
|
'type' => '計',
|
||||||
|
'general_count' => $totalGeneral,
|
||||||
|
'general_extra' => '',
|
||||||
|
'student_count' => $totalStudent,
|
||||||
|
'student_extra' => '',
|
||||||
|
'use_total' => $totalUseTotal,
|
||||||
|
'vacancy' => $totalVacancy,
|
||||||
|
'total' => $totalAll,
|
||||||
|
'last' => ['reserve_date' => null, 'contract_date' => null],
|
||||||
|
];
|
||||||
|
|
||||||
|
$debugInfo['contract_count'] = count($contractSummary);
|
||||||
|
|
||||||
|
// 空き待ち状況を車種別に集計
|
||||||
|
$waitingData = [
|
||||||
|
['psection_id' => 1, 'type' => '自転車'],
|
||||||
|
['psection_id' => 2, 'type' => '原付'],
|
||||||
|
['psection_id' => 0, 'type' => 'その他'] // その他は psection_id が1,2以外
|
||||||
|
];
|
||||||
|
|
||||||
|
$waitingSummary = [];
|
||||||
|
$totalGeneral = 0;
|
||||||
|
$totalStudent = 0;
|
||||||
|
$totalAll = 0;
|
||||||
|
|
||||||
|
foreach ($waitingData as $vehicleType) {
|
||||||
|
$query = DB::table('regular_contract as rc')
|
||||||
|
->join('reserve as r', 'rc.contract_id', '=', 'r.contract_id')
|
||||||
|
->selectRaw('SUM(CASE WHEN rc.user_categoryid = 1 THEN 1 ELSE 0 END) AS general_count')
|
||||||
|
->selectRaw('DATE_FORMAT(MIN(CASE WHEN rc.user_categoryid = 1 THEN r.reserve_date END), "%Y/%m/%d") AS general_first')
|
||||||
|
->selectRaw('SUM(CASE WHEN rc.user_categoryid = 2 THEN 1 ELSE 0 END) AS student_count')
|
||||||
|
->selectRaw('DATE_FORMAT(MIN(CASE WHEN rc.user_categoryid = 2 THEN r.reserve_date END), "%Y/%m/%d") AS student_first')
|
||||||
|
->selectRaw('COUNT(*) AS total')
|
||||||
|
->where('rc.park_id', $parkId)
|
||||||
|
->where('r.valid_flag', 1)
|
||||||
|
->where(function ($q) {
|
||||||
|
$q->whereNull('r.reserve_cancel_flag')->orWhere('r.reserve_cancel_flag', 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
if ($vehicleType['psection_id'] === 0) {
|
||||||
|
// その他:psection_id が 1,2 以外
|
||||||
|
$query->whereNotIn('rc.psection_id', [1, 2]);
|
||||||
|
} else {
|
||||||
|
// 自転車または原付
|
||||||
|
$query->where('rc.psection_id', $vehicleType['psection_id']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = $query->first();
|
||||||
|
|
||||||
|
$generalCount = (int) ($result->general_count ?? 0);
|
||||||
|
$studentCount = (int) ($result->student_count ?? 0);
|
||||||
|
$typeTotal = $generalCount + $studentCount;
|
||||||
|
|
||||||
|
$waitingSummary[] = [
|
||||||
|
'type' => $vehicleType['type'],
|
||||||
|
'general_count' => $generalCount,
|
||||||
|
'general_first' => $result->general_first ?? null,
|
||||||
|
'student_count' => $studentCount,
|
||||||
|
'student_first' => $result->student_first ?? null,
|
||||||
|
'total' => $typeTotal,
|
||||||
|
];
|
||||||
|
|
||||||
|
$totalGeneral += $generalCount;
|
||||||
|
$totalStudent += $studentCount;
|
||||||
|
$totalAll += $typeTotal;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 合計行を追加
|
||||||
|
$waitingSummary[] = [
|
||||||
|
'type' => '計',
|
||||||
|
'general_count' => $totalGeneral,
|
||||||
|
'general_first' => null,
|
||||||
|
'student_count' => $totalStudent,
|
||||||
|
'student_first' => null,
|
||||||
|
'total' => $totalAll,
|
||||||
|
];
|
||||||
|
|
||||||
|
$debugInfo['waiting_count'] = count($waitingSummary);
|
||||||
|
|
||||||
|
// まず期間タイプフィールドが存在するかチェック
|
||||||
|
$hasRegularTypeId = false;
|
||||||
|
try {
|
||||||
|
$columns = DB::select('DESCRIBE regular_contract');
|
||||||
|
foreach ($columns as $column) {
|
||||||
|
if ($column->Field === 'regular_type_id') {
|
||||||
|
$hasRegularTypeId = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
// エラーの場合はデフォルトで false
|
||||||
|
}
|
||||||
|
|
||||||
|
$debugInfo['has_regular_type_id'] = $hasRegularTypeId;
|
||||||
|
|
||||||
|
$renewalQuery = DB::table('regular_contract as rc')
|
||||||
|
->join('reserve as r', 'rc.contract_id', '=', 'r.contract_id')
|
||||||
|
->where('rc.park_id', $parkId)
|
||||||
|
->whereNotNull('r.reserve_start')
|
||||||
|
->where('r.valid_flag', 1)
|
||||||
|
->selectRaw("DATE_FORMAT(r.reserve_start, '%Y-%m') AS month");
|
||||||
|
|
||||||
|
if ($hasRegularTypeId) {
|
||||||
|
$renewalQuery->selectRaw('COALESCE(rc.regular_type_id, 1) AS period_type');
|
||||||
|
} else {
|
||||||
|
$renewalQuery->selectRaw('1 AS period_type'); // デフォルト値
|
||||||
|
}
|
||||||
|
|
||||||
|
$renewalSummary = $renewalQuery
|
||||||
|
->selectRaw('SUM(CASE WHEN rc.psection_id = 1 AND rc.user_categoryid = 1 THEN 1 ELSE 0 END) AS bicycle_general')
|
||||||
|
->selectRaw('SUM(CASE WHEN rc.psection_id = 1 AND rc.user_categoryid = 2 THEN 1 ELSE 0 END) AS bicycle_student')
|
||||||
|
->selectRaw('SUM(CASE WHEN rc.psection_id = 2 AND rc.user_categoryid = 1 THEN 1 ELSE 0 END) AS moped_general')
|
||||||
|
->selectRaw('SUM(CASE WHEN rc.psection_id = 2 AND rc.user_categoryid = 2 THEN 1 ELSE 0 END) AS moped_student')
|
||||||
|
->selectRaw('SUM(CASE WHEN rc.psection_id NOT IN (1,2) AND rc.user_categoryid = 1 THEN 1 ELSE 0 END) AS other_general')
|
||||||
|
->selectRaw('SUM(CASE WHEN rc.psection_id NOT IN (1,2) AND rc.user_categoryid = 2 THEN 1 ELSE 0 END) AS other_student')
|
||||||
|
->groupBy(['month', 'period_type'])
|
||||||
|
->orderBy('month')
|
||||||
|
->orderBy('period_type')
|
||||||
|
->limit(50)
|
||||||
|
->get();
|
||||||
|
|
||||||
|
// 月別期間別データを構造化
|
||||||
|
$structuredData = [];
|
||||||
|
$periodLabels = [
|
||||||
|
1 => '1ヶ月',
|
||||||
|
2 => '2ヶ月',
|
||||||
|
3 => '3ヶ月',
|
||||||
|
6 => '6ヶ月',
|
||||||
|
12 => '12ヶ月'
|
||||||
|
];
|
||||||
|
|
||||||
|
// regular_type_id がない場合のデフォルト処理
|
||||||
|
if (!$hasRegularTypeId) {
|
||||||
|
// フィールドがない場合は、月ごとに「計」行のみ表示
|
||||||
|
$monthlyTotals = [];
|
||||||
|
foreach ($renewalSummary as $row) {
|
||||||
|
$monthKey = $row->month;
|
||||||
|
if (!isset($monthlyTotals[$monthKey])) {
|
||||||
|
$monthlyTotals[$monthKey] = [
|
||||||
|
'bicycle_general' => 0,
|
||||||
|
'bicycle_student' => 0,
|
||||||
|
'moped_general' => 0,
|
||||||
|
'moped_student' => 0,
|
||||||
|
'other_general' => 0,
|
||||||
|
'other_student' => 0,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$monthlyTotals[$monthKey]['bicycle_general'] += (int) $row->bicycle_general;
|
||||||
|
$monthlyTotals[$monthKey]['bicycle_student'] += (int) $row->bicycle_student;
|
||||||
|
$monthlyTotals[$monthKey]['moped_general'] += (int) $row->moped_general;
|
||||||
|
$monthlyTotals[$monthKey]['moped_student'] += (int) $row->moped_student;
|
||||||
|
$monthlyTotals[$monthKey]['other_general'] += (int) $row->other_general;
|
||||||
|
$monthlyTotals[$monthKey]['other_student'] += (int) $row->other_student;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($monthlyTotals as $monthKey => $totals) {
|
||||||
|
$structuredData[$monthKey] = [
|
||||||
|
'month' => $monthKey,
|
||||||
|
'rows' => [[
|
||||||
|
'label' => '計',
|
||||||
|
'bicycle_general' => $totals['bicycle_general'],
|
||||||
|
'bicycle_student' => $totals['bicycle_student'],
|
||||||
|
'bicycle_total' => $totals['bicycle_general'] + $totals['bicycle_student'],
|
||||||
|
'moped_general' => $totals['moped_general'],
|
||||||
|
'moped_student' => $totals['moped_student'],
|
||||||
|
'moped_total' => $totals['moped_general'] + $totals['moped_student'],
|
||||||
|
'other_general' => $totals['other_general'],
|
||||||
|
'other_student' => $totals['other_student'],
|
||||||
|
'other_total' => $totals['other_general'] + $totals['other_student'],
|
||||||
|
]]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// regular_type_id がある場合の通常処理
|
||||||
|
foreach ($renewalSummary as $row) {
|
||||||
|
$monthKey = $row->month;
|
||||||
|
$periodType = (int)$row->period_type;
|
||||||
|
$periodLabel = $periodLabels[$periodType] ?? "{$periodType}ヶ月";
|
||||||
|
|
||||||
|
if (!isset($structuredData[$monthKey])) {
|
||||||
|
$structuredData[$monthKey] = [
|
||||||
|
'month' => $monthKey,
|
||||||
|
'rows' => [],
|
||||||
|
'totals' => [
|
||||||
|
'bicycle_general' => 0,
|
||||||
|
'bicycle_student' => 0,
|
||||||
|
'moped_general' => 0,
|
||||||
|
'moped_student' => 0,
|
||||||
|
'other_general' => 0,
|
||||||
|
'other_student' => 0,
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$bicycle_total = (int) $row->bicycle_general + (int) $row->bicycle_student;
|
||||||
|
$moped_total = (int) $row->moped_general + (int) $row->moped_student;
|
||||||
|
$other_total = (int) $row->other_general + (int) $row->other_student;
|
||||||
|
|
||||||
|
// 期間別データを追加
|
||||||
|
$structuredData[$monthKey]['rows'][] = [
|
||||||
|
'label' => $periodLabel,
|
||||||
|
'bicycle_general' => (int) $row->bicycle_general,
|
||||||
|
'bicycle_student' => (int) $row->bicycle_student,
|
||||||
|
'bicycle_total' => $bicycle_total,
|
||||||
|
'moped_general' => (int) $row->moped_general,
|
||||||
|
'moped_student' => (int) $row->moped_student,
|
||||||
|
'moped_total' => $moped_total,
|
||||||
|
'other_general' => (int) $row->other_general,
|
||||||
|
'other_student' => (int) $row->other_student,
|
||||||
|
'other_total' => $other_total,
|
||||||
|
];
|
||||||
|
|
||||||
|
// 月別合計を計算
|
||||||
|
$structuredData[$monthKey]['totals']['bicycle_general'] += (int) $row->bicycle_general;
|
||||||
|
$structuredData[$monthKey]['totals']['bicycle_student'] += (int) $row->bicycle_student;
|
||||||
|
$structuredData[$monthKey]['totals']['moped_general'] += (int) $row->moped_general;
|
||||||
|
$structuredData[$monthKey]['totals']['moped_student'] += (int) $row->moped_student;
|
||||||
|
$structuredData[$monthKey]['totals']['other_general'] += (int) $row->other_general;
|
||||||
|
$structuredData[$monthKey]['totals']['other_student'] += (int) $row->other_student;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 各月に計行を追加
|
||||||
|
foreach ($structuredData as &$monthData) {
|
||||||
|
$totals = $monthData['totals'];
|
||||||
|
$monthData['rows'][] = [
|
||||||
|
'label' => '計',
|
||||||
|
'bicycle_general' => $totals['bicycle_general'],
|
||||||
|
'bicycle_student' => $totals['bicycle_student'],
|
||||||
|
'bicycle_total' => $totals['bicycle_general'] + $totals['bicycle_student'],
|
||||||
|
'moped_general' => $totals['moped_general'],
|
||||||
|
'moped_student' => $totals['moped_student'],
|
||||||
|
'moped_total' => $totals['moped_general'] + $totals['moped_student'],
|
||||||
|
'other_general' => $totals['other_general'],
|
||||||
|
'other_student' => $totals['other_student'],
|
||||||
|
'other_total' => $totals['other_general'] + $totals['other_student'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$renewalSummary = array_values($structuredData);
|
||||||
|
|
||||||
|
$debugInfo['renewal_count'] = count($renewalSummary);
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'contract_summary' => $contractSummary,
|
'contract_summary' => $contractSummary,
|
||||||
'waiting_summary' => $waitingSummary,
|
'waiting_summary' => $waitingSummary,
|
||||||
'renewal_summary' => $renewalSummary,
|
'renewal_summary' => $renewalSummary,
|
||||||
|
'debug_info' => $debugInfo
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
?>
|
||||||
167
app/Http/Controllers/Admin/PersonalController.php
Normal file
167
app/Http/Controllers/Admin/PersonalController.php
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Models\Usertype;
|
||||||
|
|
||||||
|
class PersonalController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 本人確認手動処理 一覧画面
|
||||||
|
*/
|
||||||
|
public function list(Request $request)
|
||||||
|
{
|
||||||
|
$query = User::query()
|
||||||
|
// 本人確認手動処理:未チェック(1) または 手動NG(4) または 自動チェックNG(5)
|
||||||
|
->whereIn('user_idcard_chk_flag', [1, 4, 5])
|
||||||
|
// 本人確認書類アップロード済み
|
||||||
|
->where(function($q) {
|
||||||
|
$q->whereNotNull('photo_filename1')
|
||||||
|
->orWhereNotNull('photo_filename2');
|
||||||
|
})
|
||||||
|
// usertypeテーブルとLEFT JOINで分類情報を取得
|
||||||
|
->leftJoin('usertype', 'user.user_categoryid', '=', 'usertype.user_categoryid')
|
||||||
|
->select('user.*',
|
||||||
|
'usertype.usertype_subject1',
|
||||||
|
'usertype.usertype_subject2',
|
||||||
|
'usertype.usertype_subject3');
|
||||||
|
|
||||||
|
if ($request->filled('user_id')) {
|
||||||
|
$query->where('user.user_id', $request->input('user_id'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// データベースの物理順序(主キー昇順)で表示
|
||||||
|
$users = $query->paginate(20);
|
||||||
|
return view('admin.personal.list', [
|
||||||
|
'users' => $users,
|
||||||
|
'request' => $request,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 本人確認手動処理 編集画面
|
||||||
|
*/
|
||||||
|
public function edit(Request $request, $seq)
|
||||||
|
{
|
||||||
|
\Log::info('=== Personal Edit Method START ===', ['seq' => $seq, 'method' => $request->method()]);
|
||||||
|
|
||||||
|
// 利用者情報取得(user_seqで検索)
|
||||||
|
$user = User::where('user_seq', $seq)->firstOrFail();
|
||||||
|
|
||||||
|
\Log::info('User found:', [
|
||||||
|
'user_seq' => $user->user_seq,
|
||||||
|
'user_id' => $user->user_id,
|
||||||
|
'current_flag' => $user->user_idcard_chk_flag
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 利用者分類マスタ取得(ラジオボタン用)
|
||||||
|
$usertypes = Usertype::orderBy('sort_order')->get();
|
||||||
|
|
||||||
|
// POST時の処理
|
||||||
|
if ($request->isMethod('post')) {
|
||||||
|
\Log::info('=== FULL REQUEST DEBUG ===');
|
||||||
|
\Log::info('All request data:', $request->all());
|
||||||
|
|
||||||
|
\Log::info('=== Personal Edit POST Processing ===');
|
||||||
|
|
||||||
|
// 各フィールドの更新
|
||||||
|
$user->user_categoryid = $request->input('user_categoryid', $user->user_categoryid);
|
||||||
|
$user->user_regident_zip = $request->input('user_regident_zip', $user->user_regident_zip);
|
||||||
|
$user->user_regident_pre = $request->input('user_regident_pre', $user->user_regident_pre);
|
||||||
|
$user->user_regident_city = $request->input('user_regident_city', $user->user_regident_city);
|
||||||
|
$user->user_regident_add = $request->input('user_regident_add', $user->user_regident_add);
|
||||||
|
$user->user_relate_zip = $request->input('user_relate_zip', $user->user_relate_zip);
|
||||||
|
$user->user_relate_pre = $request->input('user_relate_pre', $user->user_relate_pre);
|
||||||
|
$user->user_relate_city = $request->input('user_relate_city', $user->user_relate_city);
|
||||||
|
$user->user_relate_add = $request->input('user_relate_add', $user->user_relate_add);
|
||||||
|
$user->user_remarks = $request->input('user_remarks', $user->user_remarks);
|
||||||
|
$user->user_idcard = $request->input('user_idcard', $user->user_idcard);
|
||||||
|
|
||||||
|
// 本人確認チェック処理(バックアップ値を優先使用)
|
||||||
|
$checkValue = $request->input('check') ?? $request->input('check_backup');
|
||||||
|
|
||||||
|
\Log::info('Check value received:', [
|
||||||
|
'check' => $request->input('check'),
|
||||||
|
'check_backup' => $request->input('check_backup'),
|
||||||
|
'final_value' => $checkValue,
|
||||||
|
'type' => gettype($checkValue)
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($checkValue === 'ok') {
|
||||||
|
$user->user_idcard_chk_flag = 3; // 手動チェックOK
|
||||||
|
$user->ope_id = auth()->user()->ope_id ?? auth()->id(); // 現在ログイン中の操作者ID
|
||||||
|
\Log::info('Setting user_idcard_chk_flag to 3 (手動チェックOK)');
|
||||||
|
\Log::info('Setting ope_id to current user:', ['ope_id' => $user->ope_id]);
|
||||||
|
} elseif ($checkValue === 'ng') {
|
||||||
|
$user->user_idcard_chk_flag = 4; // 手動チェックNG
|
||||||
|
$user->ope_id = auth()->user()->ope_id ?? auth()->id(); // 現在ログイン中の操作者ID
|
||||||
|
\Log::info('Setting user_idcard_chk_flag to 4 (手動チェックNG)');
|
||||||
|
\Log::info('Setting ope_id to current user:', ['ope_id' => $user->ope_id]);
|
||||||
|
} else {
|
||||||
|
\Log::warning('No valid check value received', [
|
||||||
|
'checkValue' => $checkValue,
|
||||||
|
'all_input' => $request->all()
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
\Log::info('Before save:', [
|
||||||
|
'user_idcard_chk_flag' => $user->user_idcard_chk_flag,
|
||||||
|
'ope_id' => $user->ope_id,
|
||||||
|
'isDirty' => $user->isDirty(),
|
||||||
|
'getDirty' => $user->getDirty()
|
||||||
|
]);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$result = $user->save();
|
||||||
|
\Log::info('User saved successfully', [
|
||||||
|
'result' => $result,
|
||||||
|
'user_idcard_chk_flag' => $user->user_idcard_chk_flag
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 保存結果の検証
|
||||||
|
$user->refresh(); // モデルデータをリフレッシュ
|
||||||
|
$savedUser = User::where('user_seq', $seq)->first();
|
||||||
|
\Log::info('Verification after save', [
|
||||||
|
'model_refresh' => $user->user_idcard_chk_flag,
|
||||||
|
'user_idcard_chk_flag' => $savedUser->user_idcard_chk_flag
|
||||||
|
]);
|
||||||
|
|
||||||
|
// データベース直接確認
|
||||||
|
$dbUser = \DB::table('user')->where('user_seq', $seq)->first();
|
||||||
|
\Log::info('DB direct check:', [
|
||||||
|
'user_idcard_chk_flag' => $dbUser->user_idcard_chk_flag ?? 'NOT_FOUND'
|
||||||
|
]);
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
\Log::error('Save failed', [
|
||||||
|
'error' => $e->getMessage(),
|
||||||
|
'file' => $e->getFile(),
|
||||||
|
'line' => $e->getLine()
|
||||||
|
]);
|
||||||
|
return back()->withErrors('保存に失敗しました: ' . $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
\Log::info('=== POST Processing END ===');
|
||||||
|
|
||||||
|
// 成功メッセージ
|
||||||
|
$message = 'データを更新しました。';
|
||||||
|
if ($checkValue === 'ok') {
|
||||||
|
$message = '本人確認チェックOKで更新しました。';
|
||||||
|
} elseif ($checkValue === 'ng') {
|
||||||
|
$message = '本人確認チェックNGで更新しました。';
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect()->route('personal')->with('success', $message);
|
||||||
|
}
|
||||||
|
|
||||||
|
\Log::info('=== Personal Edit Method END (GET) ===');
|
||||||
|
|
||||||
|
return view('admin.personal.edit', [
|
||||||
|
'user' => $user,
|
||||||
|
'usertypes' => $usertypes,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -24,107 +24,152 @@ class PplaceController extends Controller
|
|||||||
$inputs['list'] = Pplace::search($inputs);
|
$inputs['list'] = Pplace::search($inputs);
|
||||||
|
|
||||||
if ($inputs['list']->total() > 0 && $inputs['page'] > $inputs['list']->lastPage()) {
|
if ($inputs['list']->total() > 0 && $inputs['page'] > $inputs['list']->lastPage()) {
|
||||||
return redirect()->route('pplace');
|
return redirect()->route('pplaces');
|
||||||
}
|
}
|
||||||
|
|
||||||
return view('admin.Pplace.list', $inputs);
|
return view('admin.pplace.list', $inputs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新規登録(画面/処理)
|
||||||
|
*/
|
||||||
public function add(Request $request)
|
public function add(Request $request)
|
||||||
{
|
{
|
||||||
$inputs = [
|
if ($request->isMethod('get')) {
|
||||||
'pplace_number' => $request->input('pplace_number'),
|
// 新規時:空のレコードとオペレーターリストを渡す
|
||||||
'pplace_remarks' => $request->input('pplace_remarks'),
|
return view('admin.pplace.add', [
|
||||||
'operator_id' => $request->input('operator_id'),
|
'isEdit' => false,
|
||||||
|
'record' => new Pplace(),
|
||||||
|
'operators' => Ope::getList(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST時:バリデーション
|
||||||
|
$rules = [
|
||||||
|
'pplace_number' => 'required|string|max:255',
|
||||||
|
'pplace_remarks' => 'nullable|string|max:255',
|
||||||
|
'operator_id' => 'nullable|integer',
|
||||||
|
];
|
||||||
|
$messages = [
|
||||||
|
'pplace_number.required' => '駐輪場所番号は必須です。',
|
||||||
];
|
];
|
||||||
|
|
||||||
$inputs['operators'] = Ope::getList(); //
|
|
||||||
|
|
||||||
if ($request->isMethod('POST')) {
|
$validator = Validator::make($request->all(), $rules, $messages);
|
||||||
$validator = Validator::make($inputs, [
|
|
||||||
'pplace_number' => 'required|string|max:255',
|
if ($validator->fails()) {
|
||||||
'pplace_remarks' => 'nullable|string|max:255',
|
return redirect()->back()
|
||||||
'operator_id' => 'nullable|integer',
|
->withErrors($validator)
|
||||||
]);
|
->withInput()
|
||||||
|
->with(['operators' => Ope::getList()]);
|
||||||
if (!$validator->fails()) {
|
|
||||||
DB::transaction(function () use ($inputs) {
|
|
||||||
$pplace = new Pplace();
|
|
||||||
$pplace->fill($inputs);
|
|
||||||
$pplace->save();
|
|
||||||
});
|
|
||||||
return redirect()->route('pplace')->with('success', '登録成功');
|
|
||||||
} else {
|
|
||||||
$inputs['errorMsg'] = $this->__buildErrorMessasges($validator);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return view('admin.Pplace.add', $inputs);
|
// トランザクションで登録処理
|
||||||
|
DB::transaction(function () use ($request) {
|
||||||
|
$new = new Pplace();
|
||||||
|
$new->fill($request->only(['pplace_number', 'pplace_remarks', 'operator_id']));
|
||||||
|
$new->save();
|
||||||
|
});
|
||||||
|
|
||||||
|
return redirect()->route('pplaces')->with('success', '登録しました。');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function edit(Request $request, $id, $view = '')
|
|
||||||
|
/**
|
||||||
|
* 編集(画面/処理)
|
||||||
|
*/
|
||||||
|
public function edit(Request $request, $id)
|
||||||
{
|
{
|
||||||
|
// 該当データ取得
|
||||||
$record = Pplace::find($id);
|
$record = Pplace::find($id);
|
||||||
|
if (!$record) {
|
||||||
if (!$record) abort(404);
|
abort(404);
|
||||||
|
|
||||||
$data = $record->toArray();
|
|
||||||
$data['operators'] = Ope::getList();
|
|
||||||
|
|
||||||
|
|
||||||
if ($request->isMethod('POST')) {
|
|
||||||
$inputs = $request->all();
|
|
||||||
$validator = Validator::make($inputs, [
|
|
||||||
'pplace_number' => 'required|string|max:255',
|
|
||||||
'pplace_remarks' => 'nullable|string|max:255',
|
|
||||||
'operator_id' => 'nullable|integer',
|
|
||||||
]);
|
|
||||||
|
|
||||||
$data = array_merge($data, $inputs);
|
|
||||||
|
|
||||||
if (!$validator->fails()) {
|
|
||||||
DB::transaction(function () use ($record, $inputs) {
|
|
||||||
$record->fill($inputs);
|
|
||||||
$record->save();
|
|
||||||
});
|
|
||||||
return redirect()->route('pplace')->with('success', '更新成功');
|
|
||||||
} else {
|
|
||||||
$data['errorMsg'] = $this->__buildErrorMessasges($validator);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return view($view ?: 'admin.Pplace.edit', $data);
|
// オペレーターリスト取得(常に渡す)
|
||||||
|
$operators = Ope::getList();
|
||||||
|
|
||||||
|
if ($request->isMethod('get')) {
|
||||||
|
// 編集画面表示
|
||||||
|
return view('admin.pplace.edit', [
|
||||||
|
'isEdit' => true,
|
||||||
|
'record' => $record,
|
||||||
|
'operators' => $operators,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST時:バリデーション
|
||||||
|
$rules = [
|
||||||
|
'pplace_number' => 'required|string|max:255',
|
||||||
|
'pplace_remarks' => 'nullable|string|max:255',
|
||||||
|
'operator_id' => 'nullable|integer',
|
||||||
|
];
|
||||||
|
$messages = [
|
||||||
|
'pplace_number.required' => '駐輪場所番号は必須です。',
|
||||||
|
];
|
||||||
|
|
||||||
|
$validator = Validator::make($request->all(), $rules, $messages);
|
||||||
|
|
||||||
|
if ($validator->fails()) {
|
||||||
|
return redirect()->back()
|
||||||
|
->withErrors($validator)
|
||||||
|
->withInput()
|
||||||
|
->with(['operators' => $operators]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新処理
|
||||||
|
DB::transaction(function () use ($request, $record) {
|
||||||
|
$record->fill($request->only(['pplace_number', 'pplace_remarks', 'operator_id']));
|
||||||
|
$record->save();
|
||||||
|
});
|
||||||
|
|
||||||
|
return redirect()->route('pplaces')->with('success', '更新しました。');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function info(Request $request, $id)
|
|
||||||
{
|
|
||||||
return $this->edit($request, $id, 'admin.Pplace.info');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 削除(単一/複数対応)
|
||||||
|
*/
|
||||||
public function delete(Request $request)
|
public function delete(Request $request)
|
||||||
{
|
{
|
||||||
$pk = $request->get('pk');
|
$ids = [];
|
||||||
if ($pk && Pplace::destroy($pk)) {
|
|
||||||
return redirect()->route('pplace')->with('success', '削除成功');
|
// 単一削除(id)
|
||||||
|
if ($request->filled('id')) {
|
||||||
|
$ids[] = (int) $request->input('id');
|
||||||
}
|
}
|
||||||
return redirect()->route('pplace')->with('error', '削除失敗');
|
|
||||||
|
// 複数削除(チェックボックス pk[])
|
||||||
|
if (is_array($request->input('pk'))) {
|
||||||
|
$ids = array_merge($ids, $request->input('pk'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重複除去 & 数値変換
|
||||||
|
$ids = array_values(array_unique(array_map('intval', $ids)));
|
||||||
|
|
||||||
|
// 対象未選択
|
||||||
|
if (empty($ids)) {
|
||||||
|
return back()->with('error', '削除対象が選択されていません。');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 削除実行
|
||||||
|
Pplace::whereIn('pplace_id', $ids)->delete();
|
||||||
|
|
||||||
|
return redirect()->route('pplaces')->with('success', '削除しました。');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public function export()
|
public function export()
|
||||||
{
|
{
|
||||||
$headers = [
|
$filename = '駐輪車室マスタ' . now()->format('YmdHis') . '.csv';
|
||||||
"Content-type" => "text/csv;charset=UTF-8",
|
|
||||||
"Content-Disposition" => "attachment; filename=Pplace.csv",
|
|
||||||
];
|
|
||||||
|
|
||||||
$data = Pplace::all();
|
|
||||||
$columns = ['ID', '番号', '備考', 'オペレータID'];
|
|
||||||
|
|
||||||
$filename = "Pplace.csv";
|
|
||||||
$file = fopen($filename, 'w+');
|
$file = fopen($filename, 'w+');
|
||||||
|
fwrite($file, "\xEF\xBB\xBF"); // BOM追加(UTF-8)
|
||||||
|
|
||||||
|
$columns = ['駐輪車室ID', '番号', '備考', 'オペレータID'];
|
||||||
fputcsv($file, $columns);
|
fputcsv($file, $columns);
|
||||||
|
|
||||||
|
$data = Pplace::all();
|
||||||
foreach ($data as $item) {
|
foreach ($data as $item) {
|
||||||
fputcsv($file, [
|
fputcsv($file, [
|
||||||
$item->pplace_id,
|
$item->pplace_id,
|
||||||
@ -135,14 +180,21 @@ class PplaceController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
fclose($file);
|
fclose($file);
|
||||||
return Response::download($filename, $filename, $headers);
|
|
||||||
|
$headers = [
|
||||||
|
"Content-Type" => "text/csv; charset=UTF-8",
|
||||||
|
"Content-Disposition" => "attachment; filename={$filename}",
|
||||||
|
];
|
||||||
|
|
||||||
|
return response()->download($filename, $filename, $headers)->deleteFileAfterSend(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function import(Request $request)
|
public function import(Request $request)
|
||||||
{
|
{
|
||||||
$file = $request->file('file');
|
$file = $request->file('file');
|
||||||
if (!$file) {
|
if (!$file) {
|
||||||
return redirect()->route('pplace')->with('error', 'CSVファイルを選択してください');
|
return redirect()->route('pplaces')->with('error', 'CSVファイルを選択してください');
|
||||||
}
|
}
|
||||||
|
|
||||||
$data = \App\Utils::csvToArray($file);
|
$data = \App\Utils::csvToArray($file);
|
||||||
@ -161,10 +213,10 @@ class PplaceController extends Controller
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
DB::commit();
|
DB::commit();
|
||||||
return redirect()->route('pplace')->with('success', 'インポート成功');
|
return redirect()->route('pplaces')->with('success', 'インポート成功');
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
DB::rollBack();
|
DB::rollBack();
|
||||||
return redirect()->route('pplace')->with('error', "行 {$record} : " . $e->getMessage());
|
return redirect()->route('pplaces')->with('error', "行 {$record} : " . $e->getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -5,242 +5,335 @@ namespace App\Http\Controllers\Admin;
|
|||||||
use App\Http\Requests\PriceRequest;
|
use App\Http\Requests\PriceRequest;
|
||||||
use App\Models\Park;
|
use App\Models\Park;
|
||||||
use App\Models\Price;
|
use App\Models\Price;
|
||||||
|
use App\Models\Pplace;
|
||||||
use App\Models\Psection;
|
use App\Models\Psection;
|
||||||
use App\Models\Ptype;
|
use App\Models\Ptype;
|
||||||
use App\Models\Usertype;
|
use App\Models\Usertype;
|
||||||
|
use App\Models\Station;
|
||||||
use App\Models\Utils;
|
use App\Models\Utils;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use Illuminate\Support\Facades\Validator;
|
use Illuminate\Support\Facades\Validator;
|
||||||
use Response;
|
use Illuminate\Support\Facades\Response;
|
||||||
|
|
||||||
class PriceController extends Controller
|
class PriceController extends Controller
|
||||||
{
|
{
|
||||||
public function list(Request $request)
|
public function list(Request $request)
|
||||||
{
|
{
|
||||||
$inputs = [
|
$inputs = [
|
||||||
'isExport' => 0,
|
'isExport' => 0,
|
||||||
'sort' => $request->input('sort', ''),
|
'sort' => $request->input('sort', ''), // ソート対象カラム
|
||||||
'sort_type' => $request->input('sort_type', ''),
|
'sort_type' => $request->input('sort_type', ''), // 昇順/降順
|
||||||
'page' => $request->get('page', 1),
|
'page' => $request->get('page', 1),
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// Price::search 内で orderBy を反映させる
|
||||||
$inputs['list'] = Price::search($inputs);
|
$inputs['list'] = Price::search($inputs);
|
||||||
|
|
||||||
if ($inputs['list']->total() > 0 && $inputs['page'] > $inputs['list']->lastPage()) {
|
if ($inputs['list']->total() > 0 && $inputs['page'] > $inputs['list']->lastPage()) {
|
||||||
return redirect()->route('prices');
|
return redirect()->route('prices');
|
||||||
}
|
}
|
||||||
|
$dataList = $this->getDataDropList();
|
||||||
|
$inputs = array_merge($inputs, $dataList);
|
||||||
|
|
||||||
return view('admin.prices.list', $inputs);
|
return view('admin.prices.list', $inputs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function add(Request $request)
|
public function add(Request $request)
|
||||||
{
|
{
|
||||||
$inputs = [
|
if ($request->isMethod('get')) {
|
||||||
'prine_name' => $request->input('prine_name'), // 商品名
|
return view('admin.prices.add', array_merge(
|
||||||
'price_month' => $request->input('price_month',''), // 期間
|
$this->getDataDropList(),
|
||||||
'park_id' => $request->input('park_name'), // 駐輪場
|
[
|
||||||
'psection_id' => $request->input('psection_subject'), // 車種区分
|
'record' => new Price(),
|
||||||
'price_ptypeid' => $request->input('ptype_subject'), // 駐輪分類
|
'isEdit' => false,
|
||||||
'user_categoryid' => $request->input('user_category_name'), // 利用者分類
|
]
|
||||||
'pplace_id' => $request->input('pplace_id'), // 駐車車室
|
|
||||||
'price' => $request->input('price'), // 駐輪料金(税込)
|
|
||||||
];
|
|
||||||
|
|
||||||
$dataList = $this->getDataDropList();
|
|
||||||
$inputs = array_merge($inputs, $dataList);
|
|
||||||
if ($request->isMethod('POST')) {
|
|
||||||
$type = false;
|
|
||||||
$validation = new PriceRequest();
|
|
||||||
$rules = $validation->rules();
|
|
||||||
$validator = Validator::make($request->all(), $rules, $validation->messages());
|
|
||||||
if (!$validator->fails()) {
|
|
||||||
\DB::transaction(function () use ($inputs, &$type) {
|
|
||||||
$new = new Price();
|
|
||||||
$new->fill($inputs);
|
|
||||||
if( $new->save()){
|
|
||||||
$type = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
if ($type) {
|
|
||||||
$request->session()->flash('success', __('新しい成功を創造する。'));
|
|
||||||
return redirect()->route('prices');
|
|
||||||
} else {
|
|
||||||
$request->session()->flash('error', __('新しい作成に失敗しました'));
|
|
||||||
}
|
|
||||||
}else {
|
|
||||||
$inputs['errorMsg'] = $this->__buildErrorMessasges($validator);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return view('admin.prices.add', $inputs);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function edit(Request $request, $pk ,$view=''){
|
|
||||||
$price = Price::getByPk($pk);
|
|
||||||
if (empty($pk) || empty($price)) {
|
|
||||||
abort('404');
|
|
||||||
}
|
|
||||||
$data = $price->getAttributes();
|
|
||||||
$dataList = $this->getDataDropList();
|
|
||||||
$data = array_merge($data, $dataList);
|
|
||||||
if ($request->isMethod('POST')) {
|
|
||||||
$type = false;
|
|
||||||
$validation = new PriceRequest();
|
|
||||||
$rules = $validation->rules();
|
|
||||||
$validator = Validator::make($request->all(), $rules, $validation->messages());
|
|
||||||
$requestAll = $request->all();
|
|
||||||
$requestAll['price_ptypeid]'] = $request->input('ptype_subject');
|
|
||||||
$requestAll['user_categoryid'] = $request->input('user_category_name'); // 利用者分類
|
|
||||||
$requestAll['psection_id'] = $request->input('psection_subject');
|
|
||||||
$requestAll['park_id'] = $request->input('park_name');
|
|
||||||
$data = array_merge($data, $requestAll);
|
|
||||||
if (!$validator->fails()) {
|
|
||||||
\DB::transaction(function () use ($data, &$type,$price) {
|
|
||||||
$price->fill($data);
|
|
||||||
$price->save();
|
|
||||||
$type = true;
|
|
||||||
});
|
|
||||||
if ($type) {
|
|
||||||
$request->session()->flash('success', __('更新に成功しました'));
|
|
||||||
return redirect()->route('prices');
|
|
||||||
} else {
|
|
||||||
$request->session()->flash('error', __('更新に失敗しました'));
|
|
||||||
}
|
|
||||||
}else {
|
|
||||||
$data['errorMsg'] = $this->__buildErrorMessasges($validator);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($view != '') {
|
|
||||||
return view($view, $data);
|
|
||||||
}
|
|
||||||
return view('admin.prices.edit', $data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function delete(Request $request)
|
|
||||||
{
|
|
||||||
$arr_pk = $request->get('pk');
|
|
||||||
if ($arr_pk) {
|
|
||||||
if (Price::deleteByPk($arr_pk)) {
|
|
||||||
return redirect()->route('prices')->with('success', __("削除成功。"));
|
|
||||||
} else {
|
|
||||||
return redirect()->route('prices')->with('error', __('削除に失敗しました。'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return redirect()->route('prices')->with('error', __('削除するユーザーを選択してください。'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function export(Request $request)
|
|
||||||
{
|
|
||||||
|
|
||||||
$headers = array(
|
|
||||||
"Content-type" => "text/csv;charset=UTF-8",
|
|
||||||
'Content-Encoding: UTF-8',
|
|
||||||
"Content-Disposition" => "attachment; filename=file.csv",
|
|
||||||
"Pragma" => "no-cache",
|
|
||||||
"Cache-Control" => "must-revalidate, post-check=0, pre-check=0",
|
|
||||||
"Expires" => "0"
|
|
||||||
);
|
|
||||||
$inputs = [
|
|
||||||
'isMethodPost' => 0,
|
|
||||||
'isExport' => 1,
|
|
||||||
'sort' => $request->input('sort', ''),
|
|
||||||
'sort_type' => $request->input('sort_type', ''),
|
|
||||||
|
|
||||||
];
|
|
||||||
|
|
||||||
$dataExport = Price::search($inputs);
|
|
||||||
$columns = array(
|
|
||||||
__('駐車場所ID'),// 0
|
|
||||||
__('商品名'),// 1
|
|
||||||
__('期間'),// 2
|
|
||||||
__('駐輪場ID'),// 3
|
|
||||||
__('駐輪場名'),// 3
|
|
||||||
__('車種区分ID'),// 5
|
|
||||||
__('車種区分'),// 6
|
|
||||||
__('駐輪分類ID'),// 7
|
|
||||||
__('駐輪分類'),// 8
|
|
||||||
__('利用者分類ID'),// 9
|
|
||||||
__('利用者分類'),// 10
|
|
||||||
__('駐車車室ID'),//11
|
|
||||||
__('駐輪料金(税込)'),// 12
|
|
||||||
);
|
|
||||||
$filename = "駐輪場所、料金マスタ.csv";
|
|
||||||
$file = fopen($filename, 'w+');
|
|
||||||
fputcsv($file, $columns);
|
|
||||||
foreach ($dataExport as $items) {
|
|
||||||
fputcsv($file, array(
|
|
||||||
$items->price_parkplaceid,// 0
|
|
||||||
$items->prine_name, // 1
|
|
||||||
$items->price_month, // 2
|
|
||||||
$items->park_id, // 3
|
|
||||||
!empty($items->getPark())? $items->getPark()->park_name:'' ,// 4
|
|
||||||
$items->psection_id, // 5
|
|
||||||
!empty($items->getPSection())? $items->getPSection()->psection_subject:'',// 6
|
|
||||||
$items->price_ptypeid, // 7
|
|
||||||
!empty($items->getPType())? $items->getPType()->ptype_subject:'' ,// 8
|
|
||||||
$items->user_categoryid, //9
|
|
||||||
!empty($items->getUserType())? $items->getUserType()->print_name:'' ,//10
|
|
||||||
$items->pplace_id,// 11
|
|
||||||
$items->price, // 12
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
fclose($file);
|
|
||||||
return Response::download($filename, $filename, $headers);
|
$request->merge([
|
||||||
|
'pplace_id' => mb_convert_kana($request->input('pplace_id'), 'n'),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$validated = $this->validateRequest($request);
|
||||||
|
|
||||||
|
$created = false;
|
||||||
|
\DB::transaction(function () use ($validated, &$created) {
|
||||||
|
$price = new Price();
|
||||||
|
$price->fill($validated);
|
||||||
|
$created = $price->save();
|
||||||
|
});
|
||||||
|
|
||||||
|
return redirect()->route('prices')
|
||||||
|
->with($created ? 'success' : 'error', $created ? '登録しました。' : '登録に失敗しました。');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function edit(Request $request, $id)
|
||||||
|
{
|
||||||
|
$price = Price::getByPk($id);
|
||||||
|
if (!$price) {
|
||||||
|
abort(404);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($request->isMethod('get')) {
|
||||||
|
return view('admin.prices.edit', array_merge(
|
||||||
|
$this->getDataDropList(),
|
||||||
|
[
|
||||||
|
'record' => $price,
|
||||||
|
'isEdit' => true,
|
||||||
|
]
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
$request->merge([
|
||||||
|
'pplace_id' => mb_convert_kana($request->input('pplace_id'), 'n'),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$validated = $this->validateRequest($request, $id);
|
||||||
|
|
||||||
|
$updated = false;
|
||||||
|
\DB::transaction(function () use ($validated, &$updated, $price) {
|
||||||
|
$price->fill($validated);
|
||||||
|
$updated = $price->save();
|
||||||
|
});
|
||||||
|
|
||||||
|
return redirect()->route('prices')
|
||||||
|
->with($updated ? 'success' : 'error', $updated ? '更新しました。' : '更新に失敗しました。');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public function delete(Request $request, $id = null)
|
||||||
|
{
|
||||||
|
// 一覧画面(checkbox で複数削除)
|
||||||
|
$ids = $request->input('pk');
|
||||||
|
|
||||||
|
// 編集画面(単体削除)
|
||||||
|
if ($id) {
|
||||||
|
$ids = [$id];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 削除対象が空
|
||||||
|
if (empty($ids)) {
|
||||||
|
return redirect()->route('prices')->with('error', '削除対象が選択されていません。');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 削除処理
|
||||||
|
Price::destroy($ids);
|
||||||
|
|
||||||
|
return redirect()->route('prices')->with('success', '削除しました。');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static function deleteByPk($ids)
|
||||||
|
{
|
||||||
|
if (!is_array($ids)) {
|
||||||
|
$ids = [$ids];
|
||||||
|
}
|
||||||
|
return self::whereIn('price_parkplaceid', $ids)->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function export()
|
||||||
|
{
|
||||||
|
$filename = '駐輪場所、料金マスタ' . now()->format('YmdHis') . '.csv';
|
||||||
|
|
||||||
|
$file = fopen($filename, 'w+');
|
||||||
|
fwrite($file, "\xEF\xBB\xBF"); // BOM追加(UTF-8)
|
||||||
|
|
||||||
|
$columns = [
|
||||||
|
'駐輪場所ID',
|
||||||
|
'駐輪場ID',
|
||||||
|
'商品名',
|
||||||
|
'期間',
|
||||||
|
'利用者分類ID',
|
||||||
|
'駐輪料金(税込)',
|
||||||
|
'車種区分ID',
|
||||||
|
'駐輪分類ID',
|
||||||
|
'駐車車室ID',
|
||||||
|
];
|
||||||
|
|
||||||
|
fputcsv($file, $columns);
|
||||||
|
|
||||||
|
$data = Price::all();
|
||||||
|
foreach ($data as $item) {
|
||||||
|
fputcsv($file, [
|
||||||
|
$item->price_parkplaceid, // 駐輪場所ID
|
||||||
|
$item->park_id, // 駐輪場ID
|
||||||
|
optional($item->getUserType())->print_name, // 利用者分類名
|
||||||
|
$item->price_month, // 期間
|
||||||
|
optional($item->getUserType())->print_name, // 利用者分類ID
|
||||||
|
$item->price, // 駐輪料金(税込)
|
||||||
|
optional($item->getPSection())->psection_subject, // 車種区分名
|
||||||
|
optional($item->getPType())->ptype_subject, // 駐輪分類名
|
||||||
|
$item->pplace_id, // 駐車車室ID
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fclose($file);
|
||||||
|
|
||||||
|
$headers = [
|
||||||
|
"Content-Type" => "text/csv; charset=UTF-8",
|
||||||
|
"Content-Disposition" => "attachment; filename={$filename}",
|
||||||
|
];
|
||||||
|
|
||||||
|
return response()->download($filename, $filename, $headers)->deleteFileAfterSend(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function exportFiltered(Request $request)
|
||||||
|
{
|
||||||
|
$parkId = $request->input('park_id');
|
||||||
|
$filename = 'price_' . $parkId . '_' . now()->format('YmdHis') . '.csv';
|
||||||
|
|
||||||
|
// 一時ファイル作成
|
||||||
|
$file = fopen($filename, 'w+');
|
||||||
|
fwrite($file, "\xEF\xBB\xBF"); // UTF-8 BOM追加
|
||||||
|
|
||||||
|
// CSVヘッダ
|
||||||
|
$columns = [
|
||||||
|
'駐輪場所ID',
|
||||||
|
'商品名',
|
||||||
|
'期間',
|
||||||
|
'駐輪場ID',
|
||||||
|
'駐輪場名',
|
||||||
|
'車種区分ID',
|
||||||
|
'車種区分',
|
||||||
|
'駐輪分類ID',
|
||||||
|
'駐輪分類',
|
||||||
|
'利用者分類ID',
|
||||||
|
'利用者分類',
|
||||||
|
'駐輪車室ID',
|
||||||
|
'駐輪料金(税込)',
|
||||||
|
];
|
||||||
|
fputcsv($file, $columns);
|
||||||
|
|
||||||
|
// データ取得(選択した駐輪場IDに限定)
|
||||||
|
$data = \App\Models\Price::where('price_parkplaceid', $parkId)->get();
|
||||||
|
|
||||||
|
foreach ($data as $item) {
|
||||||
|
fputcsv($file, [
|
||||||
|
$item->price_parkplaceid, // 駐輪場所ID
|
||||||
|
$item->prine_name, // 商品名
|
||||||
|
$item->price_term ?? $item->price_month, // 期間
|
||||||
|
$item->park_id, // 駐輪場ID
|
||||||
|
optional($item->getPark())->park_name, // 駐輪場名
|
||||||
|
$item->psection_id, // 車種区分ID
|
||||||
|
optional($item->getPSection())->psection_subject, // 車種区分
|
||||||
|
$item->price_ptypeid, // 駐輪分類ID
|
||||||
|
optional($item->getPType())->ptype_subject, // 駐輪分類
|
||||||
|
$item->user_categoryid, // 利用者分類ID
|
||||||
|
optional($item->getUserType())->print_name, // 利用者分類
|
||||||
|
$item->pplace_id, // 駐輪車室ID
|
||||||
|
$item->price_taxin ?? $item->price, // 駐輪料金(税込)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose($file);
|
||||||
|
|
||||||
|
// ヘッダ設定
|
||||||
|
$headers = [
|
||||||
|
"Content-Type" => "text/csv; charset=UTF-8",
|
||||||
|
"Content-Disposition" => "attachment; filename=\"{$filename}\"",
|
||||||
|
];
|
||||||
|
|
||||||
|
// ダウンロード後にファイル削除
|
||||||
|
return response()->download($filename, $filename, $headers)->deleteFileAfterSend(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public function import(Request $request)
|
public function import(Request $request)
|
||||||
{
|
{
|
||||||
$file = $request->file('file');
|
$file = $request->file('file');
|
||||||
if(!empty($file)){
|
if (empty($file)) {
|
||||||
$data = Utils::csvToArray($file);
|
return redirect()->route('prices')->with('error', __('CSVファイルを選択してください。'));
|
||||||
$type = 1;
|
}
|
||||||
$msg = '';
|
|
||||||
$record = 0;
|
$data = Utils::csvToArray($file);
|
||||||
DB::beginTransaction();
|
$type = true;
|
||||||
try {
|
$msg = '';
|
||||||
Price::query()->delete();
|
$record = 0;
|
||||||
$col = 13;
|
|
||||||
foreach ($data as $key => $items) {
|
DB::beginTransaction();
|
||||||
$record = $key + 2;
|
try {
|
||||||
if (count($items) == $col) {
|
|
||||||
$row = new Price();
|
$col = 13; // CSV 項目数
|
||||||
$row->price_parkplaceid = $items[0];
|
foreach ($data as $key => $items) {
|
||||||
$row->prine_name = $items[1];
|
$record = $key + 2; // エラー行番号(ヘッダ行を考慮)
|
||||||
$row->price_month = $items[2];
|
|
||||||
$row->park_id = $items[3];
|
// 項目数チェック
|
||||||
$row->psection_id = $items[5];
|
if (count($items) != $col) {
|
||||||
$row->price_ptypeid = $items[7];
|
$type = false;
|
||||||
$row->user_categoryid = $items[9];
|
$msg = "行:{$record} 列数が一致しません。";
|
||||||
$row->pplace_id = $items[11];
|
break;
|
||||||
$row->price = $items[12];
|
|
||||||
if (!$row->save()) {
|
|
||||||
$type = 0;
|
|
||||||
$msg = '行:record型が一致しません。';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$type = 0;
|
|
||||||
$msg = '行:record列数が一致しません。';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (\Exception $e) {
|
|
||||||
$msg = '行:record型が一致しません。';
|
// 必須チェック
|
||||||
$type = 0;
|
if (empty($items[0])) { $type = false; $msg = "行:{$record} 駐車場所IDが未設定です。"; break; }
|
||||||
}
|
if (empty($items[1])) { $type = false; $msg = "行:{$record} 商品名が未設定です。"; break; }
|
||||||
if ($type) {
|
if (empty($items[2])) { $type = false; $msg = "行:{$record} 期間が未設定です。"; break; }
|
||||||
DB::commit();
|
if (empty($items[3])) { $type = false; $msg = "行:{$record} 駐輪場IDが未設定です。"; break; }
|
||||||
return redirect()->route('prices')->with('success', __('輸入成功'));
|
if (empty($items[5])) { $type = false; $msg = "行:{$record} 車種区分IDが未設定です。"; break; }
|
||||||
} else {
|
if (empty($items[7])) { $type = false; $msg = "行:{$record} 駐輪分類IDが未設定です。"; break; }
|
||||||
DB::rollBack();
|
if (empty($items[9])) { $type = false; $msg = "行:{$record} 利用者分類IDが未設定です。"; break; }
|
||||||
return redirect()->route('prices')->with('error', __($msg, ['record' => $record]));
|
if (empty($items[11])) { $type = false; $msg = "行:{$record} 駐車車室IDが未設定です。"; break; }
|
||||||
|
if (empty($items[12])) { $type = false; $msg = "行:{$record} 駐輪料金が未設定です。"; break; }
|
||||||
|
|
||||||
|
// マスタ存在チェック
|
||||||
|
if (!Park::where('park_id', $items[3])->exists()) {
|
||||||
|
$type = false; $msg = "行:{$record} 駐輪場IDが存在しません。"; break;
|
||||||
|
}
|
||||||
|
if (!Psection::where('psection_id', $items[5])->exists()) {
|
||||||
|
$type = false; $msg = "行:{$record} 車種区分IDが存在しません。"; break;
|
||||||
|
}
|
||||||
|
if (!Ptype::where('ptype_id', $items[7])->exists()) {
|
||||||
|
$type = false; $msg = "行:{$record} 駐輪分類IDが存在しません。"; break;
|
||||||
|
}
|
||||||
|
if (!Usertype::where('user_categoryid', $items[9])->exists()) {
|
||||||
|
$type = false; $msg = "行:{$record} 利用者分類IDが存在しません。"; break;
|
||||||
|
}
|
||||||
|
// TODO: 駐車車室ID チェック(pplace_id)
|
||||||
|
if (!Pplace::where('pplace_id', $items[11])->exists()) {
|
||||||
|
$type = false; $msg = "行:{$record} 駐車車室IDが存在しません。"; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存(存在すれば更新、なければ新規作成)
|
||||||
|
Price::updateOrCreate(
|
||||||
|
['price_parkplaceid' => $items[0]], // 主キー条件(存在チェック)
|
||||||
|
|
||||||
|
[
|
||||||
|
'prine_name' => $items[1],
|
||||||
|
'price_month' => $items[2],
|
||||||
|
'park_id' => $items[3],
|
||||||
|
'psection_id' => $items[5],
|
||||||
|
'price_ptypeid' => $items[7],
|
||||||
|
'user_categoryid' => $items[9],
|
||||||
|
'pplace_id' => $items[11],
|
||||||
|
'price' => $items[12],
|
||||||
|
'operator_id' => auth()->user()->operator_id ?? null, // オプション
|
||||||
|
'updated_at' => now(),
|
||||||
|
'created_at' => now(),
|
||||||
|
]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$type = false;
|
||||||
|
$msg = "行:{$record} 予期せぬエラー: ".$e->getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($type) {
|
||||||
|
DB::commit();
|
||||||
|
return redirect()->route('prices')->with('success', __('インポートが正常に完了しました。'));
|
||||||
} else {
|
} else {
|
||||||
return redirect()->route('prices')->with('error', __('あなたはcsvファイルを選択していません。'));
|
DB::rollBack();
|
||||||
|
return redirect()->route('prices')->with('error', $msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function info(Request $request, $id)
|
public function info(Request $request, $id)
|
||||||
{
|
{
|
||||||
return $this->edit($request, $id, 'admin.prices.info');
|
return $this->edit($request, $id, 'admin.prices.info');
|
||||||
@ -252,8 +345,33 @@ class PriceController extends Controller
|
|||||||
$data['psections'] = Psection::getList() ;
|
$data['psections'] = Psection::getList() ;
|
||||||
$data['ptypes'] = Ptype::getList() ;
|
$data['ptypes'] = Ptype::getList() ;
|
||||||
$data['userTypes'] = Usertype::getList() ;
|
$data['userTypes'] = Usertype::getList() ;
|
||||||
|
$data['pplaces'] = Pplace::getList() ;
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Price バリデーション共通
|
||||||
|
*/
|
||||||
|
private function validateRequest(Request $request): array
|
||||||
|
{
|
||||||
|
return $request->validate([
|
||||||
|
'prine_name' => 'required|string|max:255',
|
||||||
|
'price_month' => 'required|int',
|
||||||
|
'park_id' => 'required|int',
|
||||||
|
'psection_id' => 'required|int',
|
||||||
|
'price_ptypeid' => 'required|int',
|
||||||
|
'user_categoryid' => 'required|int',
|
||||||
|
'pplace_id' => 'nullable|int',
|
||||||
|
'park_number' => 'nullable|int',
|
||||||
|
'park_standard' => 'nullable|int',
|
||||||
|
'park_limit' => 'nullable|int',
|
||||||
|
'price' => 'required|numeric',
|
||||||
|
'operator_id' => 'nullable|int',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -32,16 +32,26 @@ class PriceListController extends Controller
|
|||||||
];
|
];
|
||||||
|
|
||||||
if ($parkId) {
|
if ($parkId) {
|
||||||
// parkとprice_aをJOIN
|
// price_a に必要なマスタを JOIN
|
||||||
$aRows = \DB::table('price_a')
|
$aRows = \DB::table('price_a')
|
||||||
->join('park', 'park.park_id', '=', 'price_a.park_id')
|
->join('park', 'park.park_id', '=', 'price_a.park_id')
|
||||||
|
->leftJoin('ptype', 'price_a.ptype_id', '=', 'ptype.ptype_id')
|
||||||
|
->leftJoin('usertype', 'price_a.user_categoryid', '=', 'usertype.user_categoryid')
|
||||||
|
->leftJoin('pplace', 'price_a.pplace_id', '=', 'pplace.pplace_id')
|
||||||
->where('price_a.park_id', $parkId)
|
->where('price_a.park_id', $parkId)
|
||||||
->select('price_a.*') // 必要ならpark.*も
|
->select([
|
||||||
|
'price_a.*',
|
||||||
|
'ptype.ptype_subject',
|
||||||
|
'usertype.usertype_subject1',
|
||||||
|
'usertype.usertype_subject2',
|
||||||
|
'usertype.usertype_subject3',
|
||||||
|
'pplace.pplace_number'
|
||||||
|
])
|
||||||
->get();
|
->get();
|
||||||
|
|
||||||
$aGrouped = $this->groupPriceRows($aRows);
|
$aGrouped = $this->groupPriceRows($aRows);
|
||||||
|
|
||||||
$masterList[0]['groups'] = $aGrouped;
|
$masterList[0]['groups'] = $aGrouped;
|
||||||
|
|
||||||
// マスターBも同様に取得・整形する場合はここに追加
|
// マスターBも同様に取得・整形する場合はここに追加
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,85 +70,103 @@ class PriceListController extends Controller
|
|||||||
$result = [];
|
$result = [];
|
||||||
foreach ($rows as $row) {
|
foreach ($rows as $row) {
|
||||||
// グループキーは分類ID+ユーザ分類ID+駐輪場ID
|
// グループキーは分類ID+ユーザ分類ID+駐輪場ID
|
||||||
$key = $row->price_ptypeid . '-' . $row->user_categoryid . '-' . $row->park_id;
|
$key = $row->ptype_id . '-' . $row->user_categoryid . '-' . $row->park_id;
|
||||||
|
|
||||||
if (!isset($result[$key])) {
|
if (!isset($result[$key])) {
|
||||||
$result[$key] = [
|
$result[$key] = [
|
||||||
'id' => $row->price_parkplaceid,
|
'id' => $row->price_parkplaceid,
|
||||||
'classification' => $row->price_ptypeid,
|
'classification' => $row->ptype_subject ?? '',
|
||||||
'room_number' => '', // 必要ならpplace_id等をセット
|
'room_number' => $row->pplace_number ?? '',
|
||||||
'category1' => $row->prine_name ?? '',
|
'category1' => $row->usertype_subject1 ?? '',
|
||||||
'category2' => '',
|
'category2' => $row->usertype_subject2 ?? '',
|
||||||
'category3' => '',
|
'category3' => $row->usertype_subject3 ?? '',
|
||||||
'bike_1m' => '',
|
'bike_1m' => '', 'bike_2m' => '', 'bike_3m' => '', 'bike_6m' => '', 'bike_12m' => '',
|
||||||
'bike_2m' => '',
|
'moped_1m' => '', 'moped_2m' => '', 'moped_3m' => '', 'moped_6m' => '', 'moped_12m' => '',
|
||||||
'bike_3m' => '',
|
'motorcycle_1m' => '', 'motorcycle_2m' => '', 'motorcycle_3m' => '', 'motorcycle_6m' => '', 'motorcycle_12m' => '',
|
||||||
'bike_6m' => '',
|
'car_1m' => '', 'car_2m' => '', 'car_3m' => '', 'car_6m' => '', 'car_12m' => '',
|
||||||
'bike_12m' => '',
|
|
||||||
// 必要なら原付・自動二輪も同様に追加
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
// 月数ごとに金額をセット
|
$month = $row->price_month;
|
||||||
if ($row->price_month == 1) {
|
$price = $row->price;
|
||||||
$result[$key]['bike_1m'] = $row->price;
|
switch ($row->psection_id) {
|
||||||
} elseif ($row->price_month == 2) {
|
case 1:
|
||||||
$result[$key]['bike_2m'] = $row->price;
|
$result[$key]["bike_{$month}m"] = $price;
|
||||||
} elseif ($row->price_month == 3) {
|
break;
|
||||||
$result[$key]['bike_3m'] = $row->price;
|
case 2:
|
||||||
} elseif ($row->price_month == 6) {
|
$result[$key]["moped_{$month}m"] = $price;
|
||||||
$result[$key]['bike_6m'] = $row->price;
|
break;
|
||||||
} elseif ($row->price_month == 12) {
|
case 3:
|
||||||
$result[$key]['bike_12m'] = $row->price;
|
$result[$key]["motorcycle_{$month}m"] = $price;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
$result[$key]["car_{$month}m"] = $price;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return array_values($result);
|
return array_values($result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function update(Request $request)
|
public function update(Request $request)
|
||||||
{
|
{
|
||||||
foreach ($request->input('rows', []) as $row) {
|
foreach ($request->input('rows', []) as $row) {
|
||||||
$id = $row['id'] ?? null;
|
$id = $row['id'] ?? null;
|
||||||
if (!$id) continue;
|
if (!$id) continue;
|
||||||
|
|
||||||
// 更新対象の月リスト
|
$vehicleTypes = [
|
||||||
$months = [
|
'bike' => 1,
|
||||||
'bike_1m' => 1,
|
'moped' => 2,
|
||||||
'bike_2m' => 2,
|
'motorcycle' => 3,
|
||||||
'bike_3m' => 3,
|
'car' => 4,
|
||||||
'bike_6m' => 6,
|
];
|
||||||
'bike_12m' => 12,
|
|
||||||
];
|
|
||||||
|
|
||||||
foreach ($months as $field => $month) {
|
$months = [1, 2, 3, 6, 12];
|
||||||
|
|
||||||
|
foreach ($vehicleTypes as $prefix => $psectionId) {
|
||||||
|
foreach ($months as $month) {
|
||||||
|
$field = "{$prefix}_{$month}m";
|
||||||
if (isset($row[$field])) {
|
if (isset($row[$field])) {
|
||||||
// price_aから該当レコードを取得
|
$value = $row[$field];
|
||||||
$item = \App\Models\PriceA::where('price_parkplaceid', $id)
|
|
||||||
|
// バリデーション:空欄はスキップ
|
||||||
|
if (!ctype_digit((string)$value)) {
|
||||||
|
return back()->withErrors([
|
||||||
|
"{$field}" => "金額は整数で入力してください。"
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// バリデーション:最大値を超えないこと
|
||||||
|
if ((int)$value > 9999999999) {
|
||||||
|
return back()->withErrors([
|
||||||
|
"{$field}" => "金額は最大 9,999,999,999 までです。"
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$item = PriceA::where('price_parkplaceid', $id)
|
||||||
->where('price_month', $month)
|
->where('price_month', $month)
|
||||||
|
->where('psection_id', $psectionId)
|
||||||
->first();
|
->first();
|
||||||
if ($item) {
|
if ($item) {
|
||||||
$item->price = $row[$field];
|
$item->price = $value;
|
||||||
$item->save();
|
$item->save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 原付・自動二輪も同様に必要なら追加
|
|
||||||
}
|
}
|
||||||
return back()->with('success', '金額を更新しました');
|
|
||||||
}
|
}
|
||||||
|
return back()->with('success', '金額を更新しました');
|
||||||
|
}
|
||||||
|
|
||||||
public function insert(Request $request)
|
public function insert(Request $request)
|
||||||
{
|
{
|
||||||
// 例:bike_2m(2ヶ月)だけ新規追加する場合
|
|
||||||
if ($request->filled('bike_2m')) {
|
if ($request->filled('bike_2m')) {
|
||||||
$row = new \App\Models\PriceA();
|
$row = new PriceA();
|
||||||
$row->park_id = $request->input('park_id'); // 必要に応じて
|
$row->park_id = $request->input('park_id');
|
||||||
$row->price = $request->input('bike_2m');
|
$row->price = $request->input('bike_2m');
|
||||||
$row->price_month = 2;
|
$row->price_month = 2;
|
||||||
// 他の必要なカラムもセット
|
$row->psection_id = 1; // 自転車
|
||||||
$row->save();
|
$row->save();
|
||||||
}
|
}
|
||||||
// 他の月も同様に必要なら追加
|
|
||||||
return back()->with('success', '金額を追加しました');
|
return back()->with('success', '金額を追加しました');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -25,7 +25,7 @@ class PrintAreaController extends Controller
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新規登録
|
// 新規
|
||||||
public function add(Request $request)
|
public function add(Request $request)
|
||||||
{
|
{
|
||||||
if ($request->isMethod('post')) {
|
if ($request->isMethod('post')) {
|
||||||
@ -37,7 +37,7 @@ class PrintAreaController extends Controller
|
|||||||
$validated['operator_id'] = auth()->id(); // 現在のログインユーザーを記録
|
$validated['operator_id'] = auth()->id(); // 現在のログインユーザーを記録
|
||||||
PrintArea::create($validated);
|
PrintArea::create($validated);
|
||||||
|
|
||||||
return redirect()->route('print_areas')->with('success', '登録しました');
|
return redirect()->route('print_areas')->with('success', '登録しました。');
|
||||||
}
|
}
|
||||||
|
|
||||||
$parks = Park::pluck('park_name', 'park_id');
|
$parks = Park::pluck('park_name', 'park_id');
|
||||||
@ -58,7 +58,7 @@ class PrintAreaController extends Controller
|
|||||||
$validated['operator_id'] = auth()->id(); // 更新者を記録
|
$validated['operator_id'] = auth()->id(); // 更新者を記録
|
||||||
$record->update($validated);
|
$record->update($validated);
|
||||||
|
|
||||||
return redirect()->route('print_areas')->with('success', '更新しました');
|
return redirect()->route('print_areas')->with('success', '更新しました。');
|
||||||
}
|
}
|
||||||
|
|
||||||
$parks = Park::pluck('park_name', 'park_id');
|
$parks = Park::pluck('park_name', 'park_id');
|
||||||
@ -72,24 +72,67 @@ class PrintAreaController extends Controller
|
|||||||
return view('admin.print_areas.info', compact('record'));
|
return view('admin.print_areas.info', compact('record'));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 削除(複数可)
|
/**
|
||||||
public function delete(Request $request)
|
* 印刷範囲マスタ削除処理
|
||||||
|
*/
|
||||||
|
public function delete(Request $request, $id = null)
|
||||||
{
|
{
|
||||||
if ($request->has('pk')) {
|
// 一覧画面(checkboxで複数削除)
|
||||||
PrintArea::destroy($request->input('pk'));
|
$ids = $request->input('pk');
|
||||||
return redirect()->route('print_areas')->with('success', '削除しました');
|
|
||||||
|
// 編集画面(単体削除)
|
||||||
|
if ($id) {
|
||||||
|
$ids = [$id];
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect()->route('print_areas')->with('error', '削除対象が見つかりません');
|
// 削除対象が空
|
||||||
|
if (empty($ids)) {
|
||||||
|
return redirect()
|
||||||
|
->route('print_areas')
|
||||||
|
->with('error', '削除対象が選択されていません。');
|
||||||
|
}
|
||||||
|
|
||||||
|
// バリデーション:配列 or 単一でも整数確認
|
||||||
|
$request->validate([
|
||||||
|
'pk' => 'nullable',
|
||||||
|
'pk.*' => 'integer',
|
||||||
|
]);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 削除処理
|
||||||
|
$deleted = PrintArea::destroy($ids);
|
||||||
|
|
||||||
|
if ($deleted > 0) {
|
||||||
|
return redirect()
|
||||||
|
->route('print_areas')
|
||||||
|
->with('success', '削除しました。');
|
||||||
|
} else {
|
||||||
|
return redirect()
|
||||||
|
->route('print_areas')
|
||||||
|
->with('error', '削除に失敗しました。');
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
\Log::error('印刷範囲削除エラー: ' . $e->getMessage());
|
||||||
|
|
||||||
|
return redirect()
|
||||||
|
->route('print_areas')
|
||||||
|
->with('error', '削除中にエラーが発生しました。');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CSVエクスポート
|
|
||||||
public function export(Request $request)
|
public function export(Request $request)
|
||||||
{
|
{
|
||||||
$filename = 'print_areas_' . now()->format('Ymd_His') . '.csv';
|
// ファイル名を日本語付きで指定(Excelで問題なく開けるようにUTF-8にBOMも付加)
|
||||||
|
$filename = 'シール印刷範囲マスタ' . now()->format('YmdHis') . '.csv';
|
||||||
|
|
||||||
$data = PrintArea::with('park')->get();
|
$data = PrintArea::with('park')->get();
|
||||||
|
|
||||||
|
// UTF-8 BOM (Excel用)
|
||||||
|
$bom = "\xEF\xBB\xBF";
|
||||||
|
|
||||||
|
// CSVヘッダー
|
||||||
$csv = implode(",", ['印刷範囲ID', '印刷範囲名', '駐輪場ID', '駐輪場名']) . "\n";
|
$csv = implode(",", ['印刷範囲ID', '印刷範囲名', '駐輪場ID', '駐輪場名']) . "\n";
|
||||||
|
|
||||||
foreach ($data as $item) {
|
foreach ($data as $item) {
|
||||||
$csv .= implode(",", [
|
$csv .= implode(",", [
|
||||||
$item->print_area_id,
|
$item->print_area_id,
|
||||||
@ -99,11 +142,13 @@ class PrintAreaController extends Controller
|
|||||||
]) . "\n";
|
]) . "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
return response($csv)
|
return response($bom . $csv)
|
||||||
->header('Content-Type', 'text/csv')
|
->header('Content-Type', 'text/csv; charset=UTF-8')
|
||||||
->header('Content-Disposition', "attachment; filename=$filename");
|
// filename* にすれば日本語名も安全に動作
|
||||||
|
->header('Content-Disposition', "attachment; filename*=UTF-8''" . rawurlencode($filename));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// CSVインポート(仮)
|
// CSVインポート(仮)
|
||||||
public function import(Request $request)
|
public function import(Request $request)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -9,65 +9,120 @@ use App\Models\Psection;
|
|||||||
class PsectionController extends Controller
|
class PsectionController extends Controller
|
||||||
{
|
{
|
||||||
// 一覧画面
|
// 一覧画面
|
||||||
public function list(Request $request)
|
public function list(Request $request)
|
||||||
{
|
{
|
||||||
// ソート順
|
$inputs = $request->all();
|
||||||
$sort = $request->input('sort', 'psection_id');
|
// ソート可能なカラム
|
||||||
$sort_type = $request->input('sort_type', 'asc');
|
$allowedSortColumns = ['psection_id', 'psection_subject'];
|
||||||
|
|
||||||
$query = Psection::query();
|
// ソート情報の取得
|
||||||
if (in_array($sort, ['psection_id', 'psection_subject'])) {
|
$sortColumn = $inputs['sort'] ?? 'psection_id';
|
||||||
$query->orderBy($sort, $sort_type);
|
$sortType = strtolower($inputs['sort_type'] ?? 'asc');
|
||||||
|
|
||||||
|
$query = \App\Models\Psection::query();
|
||||||
|
|
||||||
|
if (in_array($sortColumn, $allowedSortColumns, true)) {
|
||||||
|
if (!in_array($sortType, ['asc', 'desc'], true)) {
|
||||||
|
$sortType = 'asc';
|
||||||
|
}
|
||||||
|
$query->orderBy($sortColumn, $sortType);
|
||||||
}
|
}
|
||||||
|
|
||||||
$list = $query->get();
|
// ページネーション(20件)
|
||||||
|
$list = $query->paginate(20);
|
||||||
|
|
||||||
return view('admin.psection.list', compact('list', 'sort', 'sort_type'));
|
return view('admin.psection.list', [
|
||||||
|
'list' => $list,
|
||||||
|
'sort' => $sortColumn,
|
||||||
|
'sort_type' => $sortType,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新規追加
|
/**
|
||||||
|
* 車種区分マスタ:新規登録(画面/処理)
|
||||||
|
*/
|
||||||
public function add(Request $request)
|
public function add(Request $request)
|
||||||
{
|
{
|
||||||
if ($request->isMethod('post')) {
|
if ($request->isMethod('get')) {
|
||||||
$validated = $request->validate([
|
// GET:新規画面を表示
|
||||||
'psection_id' => 'required|integer|unique:psection,psection_id',
|
return view('admin.psection.add', [
|
||||||
'psection_subject' => 'required|string|max:255',
|
'isEdit' => false,
|
||||||
|
'record' => new Psection(),
|
||||||
]);
|
]);
|
||||||
Psection::create($validated);
|
|
||||||
return redirect()->route('psection')->with('success', '車種区分を追加しました');
|
|
||||||
}
|
}
|
||||||
return view('admin.psection.add');
|
|
||||||
|
// POST:バリデーション
|
||||||
|
$validated = $request->validate([
|
||||||
|
// 'psection_id' は自動採番
|
||||||
|
'psection_subject' => 'required|string|max:255',
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 登録処理
|
||||||
|
Psection::create($validated);
|
||||||
|
|
||||||
|
// 完了メッセージ+一覧へ戻る
|
||||||
|
return redirect()->route('psections')->with('success', '登録しました。');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 編集
|
|
||||||
|
/**
|
||||||
|
* 車種区分マスタ:編集(画面/処理)
|
||||||
|
*/
|
||||||
public function edit(Request $request, $id)
|
public function edit(Request $request, $id)
|
||||||
{
|
{
|
||||||
$psection = Psection::findOrFail($id);
|
// 主キーで検索(見つからない場合は404)
|
||||||
if ($request->isMethod('post')) {
|
$record = Psection::findOrFail($id);
|
||||||
$validated = $request->validate([
|
|
||||||
'psection_subject' => 'required|string|max:255',
|
if ($request->isMethod('get')) {
|
||||||
|
// 編集画面表示
|
||||||
|
return view('admin.psection.edit', [
|
||||||
|
'isEdit' => true, // ← ★ Blade 側のフォームで新規/編集を判定するため
|
||||||
|
'record' => $record, // ← ★ _form.blade.php で使用する
|
||||||
]);
|
]);
|
||||||
$psection->update($validated);
|
|
||||||
return redirect()->route('psection')->with('success', '車種区分を更新しました');
|
|
||||||
}
|
}
|
||||||
return view('admin.psection.edit', compact('psection'));
|
|
||||||
|
// POST時:バリデーション
|
||||||
|
$validated = $request->validate([
|
||||||
|
'psection_subject' => 'required|string|max:255',
|
||||||
|
]);
|
||||||
|
|
||||||
|
// データ更新
|
||||||
|
$record->update($validated);
|
||||||
|
|
||||||
|
// 成功メッセージ & リダイレクト
|
||||||
|
return redirect()->route('psections')->with('success', '更新しました。');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 詳細(info)
|
/**
|
||||||
public function info(Request $request, $id)
|
* 削除(単一/複数対応)
|
||||||
{
|
*/
|
||||||
$psection = Psection::findOrFail($id);
|
|
||||||
return view('admin.psection.info', compact('psection'));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 削除
|
|
||||||
public function delete(Request $request)
|
public function delete(Request $request)
|
||||||
{
|
{
|
||||||
$ids = $request->input('pk', []);
|
$ids = [];
|
||||||
if (!empty($ids)) {
|
|
||||||
Psection::whereIn('psection_id', $ids)->delete();
|
// 単一削除(編集画面などからの削除ボタン)
|
||||||
return redirect()->route('psection')->with('success', '削除しました');
|
if ($request->filled('id')) {
|
||||||
|
$ids[] = (int) $request->input('id');
|
||||||
}
|
}
|
||||||
return redirect()->route('psection')->with('error', '削除対象を選択してください');
|
|
||||||
|
// 一覧画面からの複数削除チェックボックス対応
|
||||||
|
if (is_array($request->input('pk'))) {
|
||||||
|
$ids = array_merge($ids, $request->input('pk'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重複削除・無効値除去
|
||||||
|
$ids = array_values(array_unique(array_map('intval', $ids)));
|
||||||
|
|
||||||
|
// 削除対象がない場合
|
||||||
|
if (empty($ids)) {
|
||||||
|
return back()->with('error', '削除対象が選択されていません。');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 削除実行
|
||||||
|
Psection::whereIn('psection_id', $ids)->delete();
|
||||||
|
|
||||||
|
// 完了メッセージ+リダイレクト
|
||||||
|
return redirect()->route('psections')->with('success', '削除しました。');
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,125 +14,156 @@ class PtypeController extends Controller
|
|||||||
{
|
{
|
||||||
public function list(Request $request)
|
public function list(Request $request)
|
||||||
{
|
{
|
||||||
|
// 受け取る入力値
|
||||||
$inputs = [
|
$inputs = [
|
||||||
'isMethodPost' => 0,
|
'isMethodPost' => $request->isMethod('post') ? 1 : 0,
|
||||||
'isExport' => 0,
|
'isExport' => 0,
|
||||||
'sort' => $request->input('sort', ''),
|
'sort' => $request->input('sort', 'ptype_id'), // デフォルト: ID
|
||||||
'sort_type' => $request->input('sort_type', ''),
|
'sort_type' => strtolower($request->input('sort_type', 'asc')),
|
||||||
'page' => $request->get('page', 1),
|
'page' => $request->get('page', 1),
|
||||||
|
|
||||||
];
|
];
|
||||||
$inputs['isMethodPost'] = $request->isMethod('post');
|
|
||||||
$inputs['list'] = Ptype::search($inputs);
|
// 許可するソート可能カラム
|
||||||
|
$allowedSortColumns = [
|
||||||
|
'ptype_id',
|
||||||
|
'ptype_subject',
|
||||||
|
'floor_sort',
|
||||||
|
'created_at',
|
||||||
|
];
|
||||||
|
|
||||||
|
// 検索クエリ作成
|
||||||
|
$query = \App\Models\Ptype::query();
|
||||||
|
|
||||||
|
// ソート処理
|
||||||
|
if (in_array($inputs['sort'], $allowedSortColumns, true)) {
|
||||||
|
$sortType = in_array($inputs['sort_type'], ['asc', 'desc'], true)
|
||||||
|
? $inputs['sort_type']
|
||||||
|
: 'asc';
|
||||||
|
|
||||||
|
$query->orderBy($inputs['sort'], $sortType);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
$query->orderBy('ptype_id', 'asc');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ページネーション
|
||||||
|
$inputs['list'] = $query->paginate(20);
|
||||||
|
|
||||||
|
// ページが超過している場合リダイレクト
|
||||||
if ($inputs['list']->total() > 0 && $inputs['page'] > $inputs['list']->lastPage()) {
|
if ($inputs['list']->total() > 0 && $inputs['page'] > $inputs['list']->lastPage()) {
|
||||||
return redirect()->route('ptypes');
|
return redirect()->route('ptypes');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 画面へ
|
||||||
return view('admin.ptypes.list', $inputs);
|
return view('admin.ptypes.list', $inputs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新規登録(画面/処理)
|
||||||
|
*/
|
||||||
public function add(Request $request)
|
public function add(Request $request)
|
||||||
{
|
{
|
||||||
$inputs = [
|
if ($request->isMethod('get')) {
|
||||||
//TODO 駐輪分類ID not found in database specs
|
// 新規時は空のレコードを用意してフォーム描画
|
||||||
'ptype_subject' => $request->input('ptype_subject'), // 駐輪分類名
|
return view('admin.ptypes.add', [
|
||||||
'ptype_remarks' => $request->input('ptype_remarks'), // 備考
|
'isEdit' => false,
|
||||||
|
'record' => new Ptype(), // ← ★ Blade側で record->〇〇 が使える
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST時:バリデーション
|
||||||
|
$rules = [
|
||||||
|
'ptype_subject' => 'required|string|max:255',
|
||||||
|
'ptype_remarks' => 'nullable|string|max:255',
|
||||||
|
];
|
||||||
|
$messages = [
|
||||||
|
'ptype_subject.required' => '駐輪分類名は必須です。',
|
||||||
];
|
];
|
||||||
|
|
||||||
if ($request->isMethod('POST')) {
|
$validator = Validator::make($request->all(), $rules, $messages);
|
||||||
$rules = [
|
|
||||||
'ptype_subject' => 'required|string|max:255',
|
|
||||||
'ptype_sort' => 'nullable|integer',
|
|
||||||
'ptype_remarks' => 'nullable|string|max:255',
|
|
||||||
];
|
|
||||||
$messages = [
|
|
||||||
'ptype_subject.required' => '駐輪分類名は必須です。',
|
|
||||||
];
|
|
||||||
$validator = Validator::make($request->all(), $rules, $messages);
|
|
||||||
if (!$validator->fails()) {
|
|
||||||
\DB::transaction(function () use ($inputs, &$type) {
|
|
||||||
$new = new Ptype();
|
|
||||||
$new->fill($inputs);
|
|
||||||
if ($new->save()) {
|
|
||||||
$type = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
if ($validator->fails()) {
|
||||||
if ($type) {
|
return redirect()->back()->withErrors($validator)->withInput();
|
||||||
$request->session()->flash('success', __('データ新規作成しました。'));
|
|
||||||
return redirect()->route('ptypes');
|
|
||||||
} else {
|
|
||||||
$request->session()->flash('error', __('新規作成に失敗しました'));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$inputs['errorMsg'] = $this->__buildErrorMessasges($validator);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return view('admin.ptypes.add', $inputs);
|
DB::transaction(function () use ($request) {
|
||||||
|
$new = new Ptype();
|
||||||
|
$new->fill($request->only(['ptype_subject', 'ptype_remarks']));
|
||||||
|
$new->save();
|
||||||
|
});
|
||||||
|
|
||||||
|
return redirect()->route('ptypes')->with('success', '登録しました。');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function edit(Request $request, $pk, $view = '')
|
/**
|
||||||
|
* 編集(画面/処理)
|
||||||
|
*/
|
||||||
|
public function edit(Request $request, $id)
|
||||||
{
|
{
|
||||||
$ptype = Ptype::getByPk($pk);
|
// 該当データ取得
|
||||||
if (empty($pk) || empty($ptype)) {
|
$record = Ptype::find($id);
|
||||||
abort('404');
|
if (!$record) {
|
||||||
|
abort(404);
|
||||||
}
|
}
|
||||||
$data = $ptype->getAttributes();
|
|
||||||
$dataList = $this->getDataDropList();
|
if ($request->isMethod('get')) {
|
||||||
$data = array_merge($data, $dataList);
|
// 編集画面表示
|
||||||
if ($request->isMethod('POST')) {
|
return view('admin.ptypes.edit', [
|
||||||
$type = false;
|
'isEdit' => true,
|
||||||
// ここを修正
|
'record' => $record,
|
||||||
$rules = [
|
]);
|
||||||
'ptype_subject' => 'required|string|max:255',
|
|
||||||
'ptype_sort' => 'nullable|integer',
|
|
||||||
'ptype_remarks' => 'nullable|string|max:255',
|
|
||||||
];
|
|
||||||
$messages = [
|
|
||||||
'ptype_subject.required' => '駐輪分類名は必須です。',
|
|
||||||
];
|
|
||||||
$validator = Validator::make($request->all(), $rules, $messages);
|
|
||||||
$requestAll = $request->all();
|
|
||||||
$data = array_merge($data, $requestAll);
|
|
||||||
if (!$validator->fails()) {
|
|
||||||
\DB::transaction(function () use ($data, &$type, $ptype) {
|
|
||||||
$ptype->fill($data);
|
|
||||||
$ptype->save();
|
|
||||||
$type = true;
|
|
||||||
});
|
|
||||||
if ($type) {
|
|
||||||
$request->session()->flash('success', __('更新に成功しました'));
|
|
||||||
return redirect()->route('ptypes');
|
|
||||||
} else {
|
|
||||||
$request->session()->flash('error', __('更新に失敗しました'));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$data['errorMsg'] = $this->__buildErrorMessasges($validator);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if ($view != '') {
|
|
||||||
return view($view, $data);
|
// POST時:バリデーション
|
||||||
|
$rules = [
|
||||||
|
'ptype_subject' => 'required|string|max:255',
|
||||||
|
'floor_sort' => 'nullable|string|max:50',
|
||||||
|
|
||||||
|
'ptype_remarks' => 'nullable|string|max:255',
|
||||||
|
];
|
||||||
|
$messages = [
|
||||||
|
'ptype_subject.required' => '駐輪分類名は必須です。',
|
||||||
|
];
|
||||||
|
|
||||||
|
$validator = Validator::make($request->all(), $rules, $messages);
|
||||||
|
|
||||||
|
if ($validator->fails()) {
|
||||||
|
return redirect()->back()->withErrors($validator)->withInput();
|
||||||
}
|
}
|
||||||
return view('admin.ptypes.edit', $data);
|
|
||||||
|
DB::transaction(function () use ($request, $record) {
|
||||||
|
$record->fill($request->only(['ptype_subject','floor_sort', 'ptype_remarks']));
|
||||||
|
$record->save();
|
||||||
|
});
|
||||||
|
|
||||||
|
return redirect()->route('ptypes')->with('success', '更新しました。');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 削除(単一/複数対応)
|
||||||
|
*/
|
||||||
public function delete(Request $request)
|
public function delete(Request $request)
|
||||||
{
|
{
|
||||||
$arr_pk = $request->get('pk');
|
$ids = [];
|
||||||
if ($arr_pk) {
|
|
||||||
if (Ptype::deleteByPk($arr_pk)) {
|
if ($request->filled('id')) {
|
||||||
return redirect()->route('ptypes')->with('success', __("削除が完了しました。"));
|
$ids[] = (int) $request->input('id');
|
||||||
} else {
|
|
||||||
return redirect()->route('ptypes')->with('error', __('削除に失敗しました。'));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return redirect()->route('ptypes')->with('error', __('削除するデータを選択してください。'));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (is_array($request->input('pk'))) {
|
||||||
|
$ids = array_merge($ids, $request->input('pk'));
|
||||||
|
}
|
||||||
|
|
||||||
public function info(Request $request, $id)
|
$ids = array_values(array_unique(array_map('intval', $ids)));
|
||||||
{
|
|
||||||
return $this->edit($request, $id, 'admin.ptypes.info');
|
if (empty($ids)) {
|
||||||
|
return back()->with('error', '削除対象が選択されていません。');
|
||||||
|
}
|
||||||
|
|
||||||
|
Ptype::whereIn('ptype_id', $ids)->delete();
|
||||||
|
|
||||||
|
return redirect()->route('ptypes')->with('success', '削除しました。');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getDataDropList()
|
public function getDataDropList()
|
||||||
|
|||||||
@ -5,9 +5,91 @@ namespace App\Http\Controllers\Admin;
|
|||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Validator;
|
use Illuminate\Support\Facades\Validator;
|
||||||
|
use App\Models\Park;
|
||||||
|
use Illuminate\Support\Carbon;
|
||||||
|
|
||||||
class RegularContractController
|
class RegularContractController
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* 利用者分類選択肢(user_categoryid 昇順)
|
||||||
|
*/
|
||||||
|
private function buildUsertypeOptions(): array
|
||||||
|
{
|
||||||
|
return DB::table('usertype as t')
|
||||||
|
->join('regular_contract as rc', 'rc.user_categoryid', '=', 't.user_categoryid')
|
||||||
|
->select('t.user_categoryid', 't.usertype_subject1', 't.usertype_subject2', 't.usertype_subject3')
|
||||||
|
->groupBy('t.user_categoryid', 't.usertype_subject1', 't.usertype_subject2', 't.usertype_subject3')
|
||||||
|
->orderBy('t.user_categoryid', 'asc')
|
||||||
|
->get()
|
||||||
|
->mapWithKeys(function ($row) {
|
||||||
|
$label = collect([
|
||||||
|
$row->usertype_subject1 ?? '',
|
||||||
|
$row->usertype_subject2 ?? '',
|
||||||
|
$row->usertype_subject3 ?? '',
|
||||||
|
])->filter(fn ($v) => $v !== '')->implode('/');
|
||||||
|
|
||||||
|
return [$row->user_categoryid => $label !== '' ? $label : (string) $row->user_categoryid];
|
||||||
|
})
|
||||||
|
->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildParkOptions(): array
|
||||||
|
{
|
||||||
|
return Park::query()
|
||||||
|
->join('regular_contract as rc', 'rc.park_id', '=', 'park.park_id')
|
||||||
|
->select('park.park_id', 'park.park_name')
|
||||||
|
->groupBy('park.park_id', 'park.park_name')
|
||||||
|
->orderBy('park.park_id', 'asc')
|
||||||
|
->get()
|
||||||
|
->mapWithKeys(fn ($park) => [
|
||||||
|
$park->park_id => $park->park_name ?: (string) $park->park_id,
|
||||||
|
])
|
||||||
|
->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* datetime-local から受け取った値を Y-m-d H:i:s へ正規化
|
||||||
|
*/
|
||||||
|
private function normalizeDateTimeInput(?string $value, bool $endOfMinute = false): ?string
|
||||||
|
{
|
||||||
|
if ($value === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
$value = trim($value);
|
||||||
|
if ($value === '') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
$value = str_replace('T', ' ', $value);
|
||||||
|
if (strlen($value) === 16) {
|
||||||
|
$value .= ':00';
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$dt = Carbon::parse($value);
|
||||||
|
if ($endOfMinute) {
|
||||||
|
$dt = $dt->endOfMinute();
|
||||||
|
}
|
||||||
|
return $dt->format('Y-m-d H:i:s');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 名寄フリガナ検索用:全角カナへ統一し空白除去
|
||||||
|
*/
|
||||||
|
private function normalizePhoneticKeyword(?string $value): ?string
|
||||||
|
{
|
||||||
|
if ($value === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
$value = trim((string) $value);
|
||||||
|
if ($value === '') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
$value = mb_convert_kana($value, 'KVCS');
|
||||||
|
return str_replace([' ', ' '], '', $value);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 定期契約一覧
|
* 定期契約一覧
|
||||||
* - ベース表: regular_contract(rc)
|
* - ベース表: regular_contract(rc)
|
||||||
@ -16,74 +98,86 @@ class RegularContractController
|
|||||||
*/
|
*/
|
||||||
public function list(Request $request)
|
public function list(Request $request)
|
||||||
{
|
{
|
||||||
// ===== ソート(既定: contract_id DESC)=====
|
if ($request->isMethod('post')) {
|
||||||
$sort = $request->input('sort', 'contract_id');
|
$postParams = $request->except(['_token']);
|
||||||
$sortType = strtolower($request->input('sort_type', 'desc')) === 'asc' ? 'asc' : 'desc';
|
$queryParams = $request->query();
|
||||||
|
|
||||||
|
return redirect()->route('regularcontracts', array_merge($queryParams, $postParams));
|
||||||
|
}
|
||||||
|
|
||||||
|
$params = $request->query();
|
||||||
|
|
||||||
|
// ===== ソート(既定: contract_id ASC)=====
|
||||||
|
$sort = $params['sort'] ?? 'contract_id';
|
||||||
|
$sortType = strtolower($params['sort_type'] ?? 'asc') === 'desc' ? 'desc' : 'asc';
|
||||||
|
|
||||||
// ===== 絞り込み(テキスト系)=====
|
// ===== 絞り込み(テキスト系)=====
|
||||||
// フォームの name 属性と完全一致させる&既定値は空文字にして Blade が未定義にならないようにする
|
$contract_qr_id = trim((string) ($params['contract_qr_id'] ?? ''));
|
||||||
$contract_qr_id = trim((string) $request->input('contract_qr_id', ''));
|
$user_id = trim((string) ($params['user_id'] ?? ''));
|
||||||
$user_id = trim((string) $request->input('user_id', ''));
|
$user_tag_serial = trim((string) ($params['user_tag_serial'] ?? ''));
|
||||||
$park_id = trim((string) $request->input('park_id', ''));
|
$park_id = trim((string) ($params['park_id'] ?? ''));
|
||||||
$user_phonetic = trim((string) $request->input('user_phonetic', '')); // フリガナ
|
$selectedParkId = trim((string) ($params['selected_park_id'] ?? ''));
|
||||||
$phone = trim((string) $request->input('phone', '')); // 電話(携帯/自宅)
|
$user_phonetic = trim((string) ($params['user_phonetic'] ?? ''));
|
||||||
$email = trim((string) $request->input('email', '')); // メール
|
$phone = trim((string) ($params['phone'] ?? ''));
|
||||||
$usertype_name_kw = trim((string) $request->input('usertype_name', '')); // 利用者分類名
|
$email = trim((string) ($params['email'] ?? ''));
|
||||||
$park_name_kw = trim((string) $request->input('park_name', '')); // 駐輪場名
|
$user_categoryid = trim((string) ($params['user_categoryid'] ?? ''));
|
||||||
|
$park_name_kw = trim((string) ($params['park_name'] ?? ''));
|
||||||
|
$zone_keyword = trim((string) ($params['zone_keyword'] ?? ''));
|
||||||
|
$zone_name = trim((string) ($params['zone_name'] ?? ''));
|
||||||
|
$merge_phonetic_input = $params['merge_phonetic'] ?? '';
|
||||||
|
$merge_phonetic = trim((string) $merge_phonetic_input);
|
||||||
|
$merge_phonetic_normalized = $this->normalizePhoneticKeyword($merge_phonetic);
|
||||||
|
$has_address = $params['has_address'] ?? '';
|
||||||
|
$workRecordFilter = (string) ($params['work_record'] ?? '0');
|
||||||
|
if (!in_array($workRecordFilter, ['0', '1', '2'], true)) {
|
||||||
|
$workRecordFilter = '0';
|
||||||
|
}
|
||||||
|
|
||||||
// ===== 絞り込み(日付範囲)=====
|
// ===== 絞り込み(日付範囲)=====
|
||||||
$reserve_from = $request->input('reserve_date_from', '');
|
$reserve_from = $params['reserve_date_from'] ?? '';
|
||||||
$reserve_to = $request->input('reserve_date_to', '');
|
$reserve_to = $params['reserve_date_to'] ?? '';
|
||||||
$created_from = $request->input('contract_created_from', '');
|
$created_from = $params['contract_created_from'] ?? '';
|
||||||
$created_to = $request->input('contract_created_to', '');
|
$created_to = $params['contract_created_to'] ?? '';
|
||||||
$updated_from = $request->input('contract_updated_from', '');
|
$updated_from = $params['contract_updated_from'] ?? '';
|
||||||
$updated_to = $request->input('contract_updated_to', '');
|
$updated_to = $params['contract_updated_to'] ?? '';
|
||||||
$canceled_from = $request->input('contract_canceled_from', '');
|
$canceled_from = $params['contract_canceled_from'] ?? '';
|
||||||
$canceled_to = $request->input('contract_canceled_to', '');
|
$canceled_to = $params['contract_canceled_to'] ?? '';
|
||||||
|
$receipt_delivery_from = $params['receipt_delivery_from'] ?? '';
|
||||||
|
$receipt_delivery_to = $params['receipt_delivery_to'] ?? '';
|
||||||
|
$contract_valid_months = $params['contract_valid_months'] ?? '';
|
||||||
|
|
||||||
// ===== 列挙(全て/0/1)=====
|
// ===== 列挙(全て/0/1)=====
|
||||||
$contract_flag = $request->input('contract_flag', '');
|
$contract_flag = $params['contract_flag'] ?? '';
|
||||||
$contract_permission = $request->input('contract_permission', '');
|
$contract_permission = $params['contract_permission'] ?? '';
|
||||||
$tag_qr_flag = $request->input('tag_qr_flag', '');
|
$tag_qr_flag = $params['tag_qr_flag'] ?? '';
|
||||||
$update_flag = $request->input('update_flag', '');
|
$updateFlagFilter = (string) ($params['update_flag'] ?? '0');
|
||||||
$contract_cancel_flag = $request->input('contract_cancel_flag', '');
|
if (!in_array($updateFlagFilter, ['0', '1', '2'], true)) {
|
||||||
|
$updateFlagFilter = '0';
|
||||||
|
}
|
||||||
|
$contract_cancel_flag = $params['contract_cancel_flag'] ?? '';
|
||||||
|
|
||||||
// ===== クエリ(結合込み)=====
|
// ===== クエリ(結合込み)=====
|
||||||
$q = DB::table('regular_contract as rc')
|
$q = DB::table('regular_contract as rc')
|
||||||
->leftJoin('user as u', 'u.user_id', '=', 'rc.user_id')
|
->leftJoin('user as u', 'u.user_id', '=', 'rc.user_id')
|
||||||
->leftJoin('usertype as t', 't.user_categoryid', '=', 'rc.user_categoryid')
|
->leftJoin('usertype as t', 't.user_categoryid', '=', 'rc.user_categoryid')
|
||||||
->leftJoin('park as p', 'p.park_id', '=', 'rc.park_id')
|
->leftJoin('park as p', 'p.park_id', '=', 'rc.park_id')
|
||||||
|
->leftJoin('zone as z', 'z.zone_id', '=', 'rc.zone_id')
|
||||||
->select([
|
->select([
|
||||||
// rc
|
'rc.*',
|
||||||
'rc.contract_id',
|
'u.user_seq',
|
||||||
'rc.contract_qr_id',
|
|
||||||
'rc.user_id',
|
|
||||||
'rc.user_categoryid',
|
|
||||||
'rc.reserve_id',
|
|
||||||
'rc.park_id',
|
|
||||||
'rc.price_parkplaceid',
|
|
||||||
'rc.user_securitynum',
|
|
||||||
'rc.reserve_date',
|
|
||||||
'rc.contract_reserve',
|
|
||||||
'rc.contract_created_at',
|
|
||||||
'rc.contract_updated_at',
|
|
||||||
'rc.contract_cancelday',
|
|
||||||
'rc.contract_flag',
|
|
||||||
'rc.contract_permission',
|
|
||||||
'rc.contract_cancel_flag',
|
|
||||||
'rc.tag_qr_flag',
|
|
||||||
'rc.update_flag',
|
|
||||||
'rc.park_position',
|
|
||||||
'rc.ope_id',
|
|
||||||
// user
|
|
||||||
'u.user_name',
|
'u.user_name',
|
||||||
'u.user_phonetic',
|
'u.user_phonetic',
|
||||||
'u.user_mobile',
|
'u.user_mobile',
|
||||||
'u.user_homephone',
|
'u.user_homephone',
|
||||||
'u.user_primemail',
|
'u.user_primemail',
|
||||||
// usertype & park
|
'u.user_regident_zip',
|
||||||
|
'u.user_tag_serial',
|
||||||
DB::raw('t.print_name as usertype_name'),
|
DB::raw('t.print_name as usertype_name'),
|
||||||
|
't.usertype_subject1',
|
||||||
|
't.usertype_subject2',
|
||||||
|
't.usertype_subject3',
|
||||||
DB::raw('p.park_name as park_name'),
|
DB::raw('p.park_name as park_name'),
|
||||||
|
DB::raw('z.zone_name as zone_name'),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// ===== LIKE / キーワード =====
|
// ===== LIKE / キーワード =====
|
||||||
@ -93,8 +187,13 @@ class RegularContractController
|
|||||||
if ($user_id !== '') {
|
if ($user_id !== '') {
|
||||||
$q->where('rc.user_id', 'like', "%{$user_id}%");
|
$q->where('rc.user_id', 'like', "%{$user_id}%");
|
||||||
}
|
}
|
||||||
|
if ($user_tag_serial !== '') {
|
||||||
|
$q->where('u.user_tag_serial', 'like', "%{$user_tag_serial}%");
|
||||||
|
}
|
||||||
if ($park_id !== '') {
|
if ($park_id !== '') {
|
||||||
$q->where('rc.park_id', 'like', "%{$park_id}%");
|
$q->where('rc.park_id', (int) $park_id);
|
||||||
|
} elseif ($selectedParkId !== '') {
|
||||||
|
$q->where('rc.park_id', (int) $selectedParkId);
|
||||||
}
|
}
|
||||||
if ($user_phonetic !== '') {
|
if ($user_phonetic !== '') {
|
||||||
$q->where('u.user_phonetic', 'like', "%{$user_phonetic}%");
|
$q->where('u.user_phonetic', 'like', "%{$user_phonetic}%");
|
||||||
@ -102,46 +201,73 @@ class RegularContractController
|
|||||||
if ($email !== '') {
|
if ($email !== '') {
|
||||||
$q->where('u.user_primemail', 'like', "%{$email}%");
|
$q->where('u.user_primemail', 'like', "%{$email}%");
|
||||||
}
|
}
|
||||||
if ($usertype_name_kw !== '') {
|
if ($user_categoryid !== '') {
|
||||||
$q->where('t.print_name', 'like', "%{$usertype_name_kw}%");
|
$q->where('rc.user_categoryid', (int) $user_categoryid);
|
||||||
}
|
}
|
||||||
if ($park_name_kw !== '') {
|
if ($park_name_kw !== '') {
|
||||||
$q->where('p.park_name', 'like', "%{$park_name_kw}%");
|
$q->where('p.park_name', 'like', "%{$park_name_kw}%");
|
||||||
}
|
}
|
||||||
|
if ($zone_name !== '') {
|
||||||
|
$q->where('z.zone_name', 'like', "%{$zone_name}%");
|
||||||
|
}
|
||||||
|
if ($merge_phonetic_normalized !== null) {
|
||||||
|
$likeKeyword = '%' . $merge_phonetic_normalized . '%';
|
||||||
|
$q->whereRaw("REPLACE(REPLACE(IFNULL(rc.chk_user_phonetic, ''), ' ', ''), ' ', '') LIKE ?", [$likeKeyword]);
|
||||||
|
}
|
||||||
if ($phone !== '') {
|
if ($phone !== '') {
|
||||||
$q->where(function ($w) use ($phone) {
|
$q->where(function ($w) use ($phone) {
|
||||||
$w->where('u.user_mobile', 'like', "%{$phone}%")
|
$w->where('u.user_mobile', 'like', "%{$phone}%")
|
||||||
->orWhere('u.user_homephone', 'like', "%{$phone}%");
|
->orWhere('u.user_homephone', 'like', "%{$phone}%");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if ($reserve_from !== '' && ($normalized = $this->normalizeDateTimeInput($reserve_from))) {
|
||||||
// ===== 日付範囲 =====
|
$q->where('rc.reserve_date', '>=', $normalized);
|
||||||
if ($reserve_from) {
|
|
||||||
$q->whereDate('rc.reserve_date', '>=', $reserve_from);
|
|
||||||
}
|
}
|
||||||
if ($reserve_to) {
|
if ($reserve_to !== '' && ($normalized = $this->normalizeDateTimeInput($reserve_to, true))) {
|
||||||
$q->whereDate('rc.reserve_date', '<=', $reserve_to);
|
$q->where('rc.reserve_date', '<=', $normalized);
|
||||||
}
|
}
|
||||||
if ($created_from) {
|
if ($receipt_delivery_from !== '' && ($normalized = $this->normalizeDateTimeInput($receipt_delivery_from))) {
|
||||||
$q->whereDate('rc.contract_created_at', '>=', $created_from);
|
$q->where('rc.contract_payment_day', '>=', $normalized);
|
||||||
}
|
}
|
||||||
if ($created_to) {
|
if ($receipt_delivery_to !== '' && ($normalized = $this->normalizeDateTimeInput($receipt_delivery_to, true))) {
|
||||||
$q->whereDate('rc.contract_created_at', '<=', $created_to);
|
$q->where('rc.contract_payment_day', '<=', $normalized);
|
||||||
}
|
}
|
||||||
if ($updated_from) {
|
if ($zone_keyword !== '') {
|
||||||
$q->whereDate('rc.contract_updated_at', '>=', $updated_from);
|
$q->where(function ($w) use ($zone_keyword) {
|
||||||
|
$w->where('rc.zone_id', 'like', "%{$zone_keyword}%")
|
||||||
|
->orWhere('rc.pplace_no', 'like', "%{$zone_keyword}%")
|
||||||
|
->orWhere('rc.old_contract_id', 'like', "%{$zone_keyword}%");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if ($updated_to) {
|
if ($workRecordFilter === '1') {
|
||||||
$q->whereDate('rc.contract_updated_at', '<=', $updated_to);
|
$q->where(function ($w) {
|
||||||
|
$w->whereNull('rc.contract_flag')
|
||||||
|
->orWhere('rc.contract_flag', '=', 0);
|
||||||
|
});
|
||||||
|
} elseif ($workRecordFilter === '2') {
|
||||||
|
$q->where('rc.contract_flag', '=', 1);
|
||||||
}
|
}
|
||||||
if ($canceled_from) {
|
if ($created_from !== '' && ($normalized = $this->normalizeDateTimeInput($created_from))) {
|
||||||
$q->whereDate('rc.contract_cancelday', '>=', $canceled_from);
|
$q->where('rc.contract_created_at', '>=', $normalized);
|
||||||
}
|
}
|
||||||
if ($canceled_to) {
|
if ($created_to !== '' && ($normalized = $this->normalizeDateTimeInput($created_to, true))) {
|
||||||
$q->whereDate('rc.contract_cancelday', '<=', $canceled_to);
|
$q->where('rc.contract_created_at', '<=', $normalized);
|
||||||
|
}
|
||||||
|
if ($updated_from !== '' && ($normalized = $this->normalizeDateTimeInput($updated_from))) {
|
||||||
|
$q->where('rc.contract_updated_at', '>=', $normalized);
|
||||||
|
}
|
||||||
|
if ($updated_to !== '' && ($normalized = $this->normalizeDateTimeInput($updated_to, true))) {
|
||||||
|
$q->where('rc.contract_updated_at', '<=', $normalized);
|
||||||
|
}
|
||||||
|
if ($canceled_from !== '' && ($normalized = $this->normalizeDateTimeInput($canceled_from))) {
|
||||||
|
$q->where('rc.contract_cancelday', '>=', $normalized);
|
||||||
|
}
|
||||||
|
if ($canceled_to !== '' && ($normalized = $this->normalizeDateTimeInput($canceled_to, true))) {
|
||||||
|
$q->where('rc.contract_cancelday', '<=', $normalized);
|
||||||
|
}
|
||||||
|
if ($contract_valid_months !== '') {
|
||||||
|
$q->where('rc.enable_months', (int) $contract_valid_months);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== 列挙フィルタ =====
|
|
||||||
if ($contract_flag !== '') {
|
if ($contract_flag !== '') {
|
||||||
$q->where('rc.contract_flag', (int) $contract_flag);
|
$q->where('rc.contract_flag', (int) $contract_flag);
|
||||||
}
|
}
|
||||||
@ -151,8 +277,13 @@ class RegularContractController
|
|||||||
if ($tag_qr_flag !== '') {
|
if ($tag_qr_flag !== '') {
|
||||||
$q->where('rc.tag_qr_flag', (int) $tag_qr_flag);
|
$q->where('rc.tag_qr_flag', (int) $tag_qr_flag);
|
||||||
}
|
}
|
||||||
if ($update_flag !== '') {
|
if ($updateFlagFilter === '1') {
|
||||||
$q->where('rc.update_flag', (int) $update_flag);
|
$q->where('rc.update_flag', '=', 1);
|
||||||
|
} elseif ($updateFlagFilter === '2') {
|
||||||
|
$q->where(function ($w) {
|
||||||
|
$w->whereNull('rc.update_flag')
|
||||||
|
->orWhere('rc.update_flag', '!=', 1);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if ($contract_cancel_flag !== '') {
|
if ($contract_cancel_flag !== '') {
|
||||||
$q->where('rc.contract_cancel_flag', (int) $contract_cancel_flag);
|
$q->where('rc.contract_cancel_flag', (int) $contract_cancel_flag);
|
||||||
@ -160,34 +291,16 @@ class RegularContractController
|
|||||||
|
|
||||||
// ===== ソート(仮想列は結合側にマッピング)=====
|
// ===== ソート(仮想列は結合側にマッピング)=====
|
||||||
$sortable = [
|
$sortable = [
|
||||||
'contract_id',
|
'contract_id', 'contract_qr_id', 'old_contract_id', 'zone_id', 'zone_name', 'pplace_no',
|
||||||
'contract_qr_id',
|
'contract_periods', 'contract_periode', 'user_id', 'user_categoryid', 'reserve_id', 'park_id',
|
||||||
'user_id',
|
'price_parkplaceid', 'user_securitynum', 'reserve_date', 'contract_reserve', 'contract_created_at',
|
||||||
'user_categoryid',
|
'contract_updated_at', 'contract_cancelday', 'contract_reduction', 'enable_months', 'printable_date',
|
||||||
'reserve_id',
|
'billing_amount', 'contract_payment_day', 'contract_money', 'refunds', 'contract_flag',
|
||||||
'park_id',
|
'contract_permission', 'contract_cancel_flag', 'tag_qr_flag', 'update_flag', 'pplace_allocation_flag',
|
||||||
'price_parkplaceid',
|
'settlement_transaction_id', 'contract_seal_issue', 'storage_company_code', 'share_storage_company_code',
|
||||||
'user_securitynum',
|
'ope_id', 'park_position', 'contract_manual', 'contract_notice', 'contract_payment_number',
|
||||||
'reserve_date',
|
'user_name', 'user_phonetic', 'user_mobile', 'user_homephone', 'user_primemail', 'user_regident_zip',
|
||||||
'contract_reserve',
|
'usertype_name', 'park_name',
|
||||||
'contract_created_at',
|
|
||||||
'contract_updated_at',
|
|
||||||
'contract_cancelday',
|
|
||||||
'contract_flag',
|
|
||||||
'contract_permission',
|
|
||||||
'contract_cancel_flag',
|
|
||||||
'tag_qr_flag',
|
|
||||||
'update_flag',
|
|
||||||
'park_position',
|
|
||||||
'ope_id',
|
|
||||||
// 結合先の見出し列
|
|
||||||
'user_name',
|
|
||||||
'user_phonetic',
|
|
||||||
'user_mobile',
|
|
||||||
'user_homephone',
|
|
||||||
'user_primemail',
|
|
||||||
'usertype_name',
|
|
||||||
'park_name',
|
|
||||||
];
|
];
|
||||||
if (!in_array($sort, $sortable, true)) {
|
if (!in_array($sort, $sortable, true)) {
|
||||||
$sort = 'contract_id';
|
$sort = 'contract_id';
|
||||||
@ -198,12 +311,14 @@ class RegularContractController
|
|||||||
'user_mobile' => 'u.user_mobile',
|
'user_mobile' => 'u.user_mobile',
|
||||||
'user_homephone' => 'u.user_homephone',
|
'user_homephone' => 'u.user_homephone',
|
||||||
'user_primemail' => 'u.user_primemail',
|
'user_primemail' => 'u.user_primemail',
|
||||||
|
'user_regident_zip' => 'u.user_regident_zip',
|
||||||
'usertype_name' => 't.print_name',
|
'usertype_name' => 't.print_name',
|
||||||
'park_name' => 'p.park_name',
|
'park_name' => 'p.park_name',
|
||||||
|
'zone_name' => 'z.zone_name',
|
||||||
];
|
];
|
||||||
$sortColumn = $sortMap[$sort] ?? ('rc.' . $sort);
|
$sortColumn = $sortMap[$sort] ?? ('rc.' . $sort);
|
||||||
|
|
||||||
$list = $q->orderBy($sortColumn, $sortType)->paginate(50);
|
$list = $q->orderBy($sortColumn, $sortType)->paginate(50)->withQueryString();
|
||||||
|
|
||||||
// ===== 画面へ(Blade 側が参照するすべての変数を渡す)=====
|
// ===== 画面へ(Blade 側が参照するすべての変数を渡す)=====
|
||||||
return view('admin.regularcontracts.list', [
|
return view('admin.regularcontracts.list', [
|
||||||
@ -214,12 +329,18 @@ class RegularContractController
|
|||||||
// 入力保持(テキスト)
|
// 入力保持(テキスト)
|
||||||
'contract_qr_id' => $contract_qr_id,
|
'contract_qr_id' => $contract_qr_id,
|
||||||
'user_id' => $user_id,
|
'user_id' => $user_id,
|
||||||
'park_id' => $park_id,
|
'user_tag_serial' => $user_tag_serial,
|
||||||
|
'park_id' => $selectedParkId !== '' ? $selectedParkId : $park_id,
|
||||||
'user_phonetic' => $user_phonetic,
|
'user_phonetic' => $user_phonetic,
|
||||||
'phone' => $phone,
|
'phone' => $phone,
|
||||||
'email' => $email,
|
'email' => $email,
|
||||||
'usertype_name' => $usertype_name_kw,
|
'user_categoryid' => $user_categoryid,
|
||||||
'park_name' => $park_name_kw,
|
'park_name' => $park_name_kw,
|
||||||
|
'zone_keyword' => $zone_keyword,
|
||||||
|
'zone_name' => $zone_name,
|
||||||
|
'merge_phonetic' => $merge_phonetic,
|
||||||
|
'has_address' => $has_address,
|
||||||
|
'work_record' => $workRecordFilter,
|
||||||
|
|
||||||
// 入力保持(日付)
|
// 入力保持(日付)
|
||||||
'reserve_date_from' => $reserve_from,
|
'reserve_date_from' => $reserve_from,
|
||||||
@ -230,13 +351,19 @@ class RegularContractController
|
|||||||
'contract_updated_to' => $updated_to,
|
'contract_updated_to' => $updated_to,
|
||||||
'contract_canceled_from' => $canceled_from,
|
'contract_canceled_from' => $canceled_from,
|
||||||
'contract_canceled_to' => $canceled_to,
|
'contract_canceled_to' => $canceled_to,
|
||||||
|
'receipt_delivery_from' => $receipt_delivery_from,
|
||||||
|
'receipt_delivery_to' => $receipt_delivery_to,
|
||||||
|
'contract_valid_months' => $contract_valid_months,
|
||||||
|
|
||||||
// 入力保持(列挙)
|
// 入力保持(列挙)
|
||||||
'contract_flag' => $contract_flag,
|
'contract_flag' => $contract_flag,
|
||||||
'contract_permission' => $contract_permission,
|
'contract_permission' => $contract_permission,
|
||||||
'tag_qr_flag' => $tag_qr_flag,
|
'tag_qr_flag' => $tag_qr_flag,
|
||||||
'update_flag' => $update_flag,
|
'update_flag' => $updateFlagFilter,
|
||||||
'contract_cancel_flag' => $contract_cancel_flag,
|
'contract_cancel_flag' => $contract_cancel_flag,
|
||||||
|
|
||||||
|
'userTypeOptions' => $this->buildUsertypeOptions(),
|
||||||
|
'parkOptions' => $this->buildParkOptions(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -318,15 +445,23 @@ class RegularContractController
|
|||||||
*/
|
*/
|
||||||
public function delete(Request $request)
|
public function delete(Request $request)
|
||||||
{
|
{
|
||||||
$id = (int) $request->input('id');
|
$ids = $request->input('ids', []);
|
||||||
DB::table('regular_contract')->where('contract_id', $id)->delete();
|
if (!is_array($ids)) {
|
||||||
|
$ids = [$ids];
|
||||||
|
}
|
||||||
|
$ids = array_values(array_filter(
|
||||||
|
array_map('intval', $ids),
|
||||||
|
static fn (int $v) => $v > 0
|
||||||
|
));
|
||||||
|
|
||||||
// 例:論理削除運用にする場合(必要なら運用側で切替)
|
if (empty($ids)) {
|
||||||
// DB::table('regular_contract')->where('contract_id', $id)->update([
|
return redirect()->route('regularcontracts')
|
||||||
// 'contract_cancel_flag' => 1,
|
->with('error', '削除する定期契約が選択されていません。');
|
||||||
// 'contract_cancelday' => now(),
|
}
|
||||||
// 'updated_at' => now(),
|
|
||||||
// ]);
|
DB::table('regular_contract')
|
||||||
|
->whereIn('contract_id', $ids)
|
||||||
|
->delete();
|
||||||
|
|
||||||
return redirect()->route('regularcontracts')->with('success', '定期契約を削除しました。');
|
return redirect()->route('regularcontracts')->with('success', '定期契約を削除しました。');
|
||||||
}
|
}
|
||||||
@ -366,70 +501,115 @@ class RegularContractController
|
|||||||
|
|
||||||
public function export(Request $request)
|
public function export(Request $request)
|
||||||
{
|
{
|
||||||
// ── 出力タイプ(通常 / SMBC / 役所) ──────────────────────────────
|
$params = $request->all();
|
||||||
$type = $request->query('type'); // null | smbc | city
|
$sort = $params['sort'] ?? 'contract_id';
|
||||||
|
$sortType = strtolower($params['sort_type'] ?? 'asc') === 'desc' ? 'desc' : 'asc';
|
||||||
|
$type = (string) ($request->query('type') ?? '');
|
||||||
|
|
||||||
// ── 出力ファイル名 ───────────────────────────────────────────────
|
$fileName = match ($type) {
|
||||||
$downloadName = '定期契約マスタ.csv';
|
'smbc' => '定期契約マスタ_SMBC.csv',
|
||||||
if ($type === 'smbc')
|
'city' => '定期契約マスタ_役所提出用.csv',
|
||||||
$downloadName = '定期契約マスタ_SMBC.csv';
|
default => '定期契約マスタ.csv',
|
||||||
if ($type === 'city')
|
};
|
||||||
$downloadName = '定期契約マスタ_役所提出用.csv';
|
|
||||||
|
|
||||||
// ── 生成先(storage/app/tmp 配下の一時ファイル) ─────────────────
|
$query = $this->buildListQuery($params);
|
||||||
$tmpDir = storage_path('app/tmp');
|
|
||||||
if (!is_dir($tmpDir)) {
|
|
||||||
@mkdir($tmpDir, 0755, true);
|
|
||||||
}
|
|
||||||
$tmpPath = $tmpDir . '/' . uniqid('regularcontracts_', true) . '.csv';
|
|
||||||
|
|
||||||
// ── CSV を作成(Excel を考慮し UTF-8 BOM を付与) ───────────────
|
$columns = [
|
||||||
$fp = fopen($tmpPath, 'w+');
|
'contract_id' => '契約ID',
|
||||||
if ($fp === false) {
|
'contract_qr_id' => '定期契約ID',
|
||||||
abort(500, 'CSV一時ファイルを作成できませんでした。');
|
'old_contract_id' => '旧定期契約番号',
|
||||||
}
|
'pplace_no' => '車室番号',
|
||||||
// Excel 対策:BOM
|
'user_id' => '利用者ID',
|
||||||
fwrite($fp, "\xEF\xBB\xBF");
|
'user_categoryid' => '利用者分類ID',
|
||||||
|
'tag_qr_flag' => 'タグ・QR',
|
||||||
|
'park_id' => '駐輪場ID',
|
||||||
|
'reserve_date' => '予約日時',
|
||||||
|
'contract_periods' => '有効期間S',
|
||||||
|
'contract_periode' => '有効期間E',
|
||||||
|
'price_parkplaceid' => '駐輪場所ID',
|
||||||
|
'user_securitynum' => '防犯登録番号',
|
||||||
|
'contract_created_at' => '契約日時',
|
||||||
|
'contract_updated_at' => '更新可能日',
|
||||||
|
'contract_cancelday' => '解約日時',
|
||||||
|
'contract_reduction' => '減免措置',
|
||||||
|
'enable_months' => '定期有効月数',
|
||||||
|
'printable_date' => 'シール印刷可能日',
|
||||||
|
'billing_amount' => '請求金額',
|
||||||
|
'pplace_allocation_flag' => '車室割り当てフラグ',
|
||||||
|
'contract_payment_day' => '授受日時',
|
||||||
|
'contract_money' => '授受金額',
|
||||||
|
'contract_flag' => '授受フラグ',
|
||||||
|
'settlement_transaction_id' => '決済トランザクションID',
|
||||||
|
'contract_seal_issue' => 'シール発行数',
|
||||||
|
'storage_company_code' => '収納企業コード',
|
||||||
|
'share_storage_company_code' => '共有先収納企業コード',
|
||||||
|
'accept_number' => '受付番号',
|
||||||
|
'update_flag' => '(更新元)契約更新済フラグ',
|
||||||
|
'vehicle_type_id' => '車種区分ID',
|
||||||
|
'chk_user_phonetic' => 'チェック用_フリガナ',
|
||||||
|
'user_regident_zip' => 'チェック用_居住所郵便番号',
|
||||||
|
'user_mobile' => 'チェック用_携帯電話番号',
|
||||||
|
'user_homephone' => 'チェック用_自宅電話番号',
|
||||||
|
'old_member_number' => 'チェック用_旧会員番号',
|
||||||
|
'user_name' => '利用者氏名',
|
||||||
|
'user_phonetic' => '利用者フリガナ',
|
||||||
|
'park_name' => '駐輪場名',
|
||||||
|
'zone_name' => 'ゾーン名',
|
||||||
|
'usertype_name' => '利用者分類名',
|
||||||
|
];
|
||||||
|
|
||||||
// ヘッダー行(必要に応じて列を増減)
|
$dateColumns = [
|
||||||
fputcsv($fp, ['定期契約ID', '利用者ID', '駐輪場ID', '契約日時']);
|
'contract_periods',
|
||||||
|
'contract_periode',
|
||||||
|
'contract_created_at',
|
||||||
|
'contract_updated_at',
|
||||||
|
'contract_cancelday',
|
||||||
|
'printable_date',
|
||||||
|
'contract_payment_day',
|
||||||
|
'reserve_date',
|
||||||
|
];
|
||||||
|
|
||||||
// ── データ取得(大量件数に備え chunk で分割取得) ────────────────
|
$rows = $query->orderBy($sort, $sortType)->get();
|
||||||
// ※ list() と同等の JOIN/SELECT を最低限に簡略化
|
|
||||||
DB::table('regular_contract as rc')
|
$headers = [
|
||||||
->leftJoin('user as u', 'u.user_id', '=', 'rc.user_id')
|
'Content-Type' => 'text/csv; charset=Shift_JIS',
|
||||||
->orderBy('rc.contract_id', 'asc')
|
'Content-Disposition' => "attachment; filename=\"{$fileName}\"",
|
||||||
->select([
|
];
|
||||||
'rc.contract_qr_id',
|
|
||||||
'rc.user_id',
|
return response()->streamDownload(function () use ($rows, $columns, $dateColumns) {
|
||||||
'rc.park_id',
|
$handle = fopen('php://output', 'w');
|
||||||
'rc.contract_created_at',
|
|
||||||
])
|
$headerRow = array_map(fn ($label) => mb_convert_encoding($label, 'SJIS-win', 'UTF-8'), array_values($columns));
|
||||||
->chunk(1000, function ($rows) use ($fp) {
|
fputcsv($handle, $headerRow);
|
||||||
foreach ($rows as $r) {
|
|
||||||
fputcsv($fp, [
|
foreach ($rows as $row) {
|
||||||
$r->contract_qr_id,
|
$line = [];
|
||||||
$r->user_id,
|
foreach ($columns as $key => $label) {
|
||||||
$r->park_id,
|
$value = $row->{$key} ?? '';
|
||||||
$r->contract_created_at,
|
|
||||||
]);
|
if (in_array($key, $dateColumns, true) && $value) {
|
||||||
|
try {
|
||||||
|
$value = \Illuminate\Support\Carbon::parse($value)->format(str_contains($key, '_day') || str_contains($key, '_date') ? 'Y-m-d H:i' : 'Y-m-d');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$value = (string) $value;
|
||||||
|
}
|
||||||
|
} elseif ($key === 'tag_qr_flag' && $value !== '') {
|
||||||
|
$value = ((int) $value) === 1 ? 'QR' : 'タグ';
|
||||||
|
} elseif ($key === 'pplace_allocation_flag' && $value !== '') {
|
||||||
|
$value = ((int) $value) === 1 ? '割当済' : '未割当';
|
||||||
|
} elseif ($key === 'contract_flag' && $value !== '') {
|
||||||
|
$value = ((int) $value) === 1 ? '済' : '未';
|
||||||
|
} elseif ($key === 'update_flag' && $value !== '') {
|
||||||
|
$value = ((int) $value) === 1 ? '更新済' : '未更新';
|
||||||
|
}
|
||||||
|
|
||||||
|
$line[] = mb_convert_encoding((string) $value, 'SJIS-win', 'UTF-8');
|
||||||
}
|
}
|
||||||
});
|
fputcsv($handle, $line);
|
||||||
|
}
|
||||||
|
|
||||||
fclose($fp);
|
fclose($handle);
|
||||||
|
}, $fileName, $headers);
|
||||||
// ── ダウンロードレスポンス(送信後に一時ファイル削除) ────────────
|
|
||||||
return response()->download(
|
|
||||||
$tmpPath,
|
|
||||||
$downloadName,
|
|
||||||
[
|
|
||||||
'Content-Type' => 'text/csv; charset=UTF-8',
|
|
||||||
'Content-Disposition' => 'attachment; filename="' . $downloadName . '"',
|
|
||||||
'Pragma' => 'no-cache',
|
|
||||||
'Cache-Control' => 'must-revalidate, post-check=0, pre-check=0',
|
|
||||||
'Expires' => '0',
|
|
||||||
]
|
|
||||||
)->deleteFileAfterSend(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -523,6 +703,178 @@ class RegularContractController
|
|||||||
->with('success', '定期契約を登録しました。');
|
->with('success', '定期契約を登録しました。');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function buildListQuery(array $params)
|
||||||
|
{
|
||||||
|
$contract_qr_id = trim((string)($params['contract_qr_id'] ?? ''));
|
||||||
|
$user_id = trim((string)($params['user_id'] ?? ''));
|
||||||
|
$user_tag_serial = trim((string)($params['user_tag_serial'] ?? ''));
|
||||||
|
$park_id = trim((string)($params['park_id'] ?? ''));
|
||||||
|
$selectedParkId = trim((string)($params['selected_park_id'] ?? ''));
|
||||||
|
$user_phonetic = trim((string)($params['user_phonetic'] ?? ''));
|
||||||
|
$phone = trim((string)($params['phone'] ?? ''));
|
||||||
|
$email = trim((string)($params['email'] ?? ''));
|
||||||
|
$user_categoryid = trim((string)($params['user_categoryid'] ?? ''));
|
||||||
|
$park_name_kw = trim((string)($params['park_name'] ?? ''));
|
||||||
|
$zone_keyword = trim((string)($params['zone_keyword'] ?? ''));
|
||||||
|
$zone_name = trim((string)($params['zone_name'] ?? ''));
|
||||||
|
$merge_phonetic_input = $params['merge_phonetic'] ?? '';
|
||||||
|
$merge_phonetic = trim((string)$merge_phonetic_input);
|
||||||
|
$merge_phonetic_normalized = $this->normalizePhoneticKeyword($merge_phonetic);
|
||||||
|
$workRecordFilter = (string)($params['work_record'] ?? '0');
|
||||||
|
if (!in_array($workRecordFilter, ['0', '1', '2'], true)) {
|
||||||
|
$workRecordFilter = '0';
|
||||||
|
}
|
||||||
|
$reserve_from = $params['reserve_date_from'] ?? '';
|
||||||
|
$reserve_to = $params['reserve_date_to'] ?? '';
|
||||||
|
$created_from = $params['contract_created_from'] ?? '';
|
||||||
|
$created_to = $params['contract_created_to'] ?? '';
|
||||||
|
$updated_from = $params['contract_updated_from'] ?? '';
|
||||||
|
$updated_to = $params['contract_updated_to'] ?? '';
|
||||||
|
$canceled_from = $params['contract_canceled_from'] ?? '';
|
||||||
|
$canceled_to = $params['contract_canceled_to'] ?? '';
|
||||||
|
$receipt_delivery_from = $params['receipt_delivery_from'] ?? '';
|
||||||
|
$receipt_delivery_to = $params['receipt_delivery_to'] ?? '';
|
||||||
|
$contract_valid_months = $params['contract_valid_months'] ?? '';
|
||||||
|
$contract_flag = $params['contract_flag'] ?? '';
|
||||||
|
$contract_permission = $params['contract_permission'] ?? '';
|
||||||
|
$tag_qr_flag = $params['tag_qr_flag'] ?? '';
|
||||||
|
$updateFlagFilter = (string)($params['update_flag'] ?? '0');
|
||||||
|
if (!in_array($updateFlagFilter, ['0', '1', '2'], true)) {
|
||||||
|
$updateFlagFilter = '0';
|
||||||
|
}
|
||||||
|
$contract_cancel_flag = $params['contract_cancel_flag'] ?? '';
|
||||||
|
|
||||||
|
$q = DB::table('regular_contract as rc')
|
||||||
|
->leftJoin('user as u', 'u.user_id', '=', 'rc.user_id')
|
||||||
|
->leftJoin('usertype as t', 't.user_categoryid', '=', 'rc.user_categoryid')
|
||||||
|
->leftJoin('park as p', 'p.park_id', '=', 'rc.park_id')
|
||||||
|
->leftJoin('zone as z', 'z.zone_id', '=', 'rc.zone_id')
|
||||||
|
->select([
|
||||||
|
'rc.*',
|
||||||
|
'u.user_seq',
|
||||||
|
'u.user_name',
|
||||||
|
'u.user_phonetic',
|
||||||
|
'u.user_mobile',
|
||||||
|
'u.user_homephone',
|
||||||
|
'u.user_primemail',
|
||||||
|
'u.user_regident_zip',
|
||||||
|
'u.user_tag_serial',
|
||||||
|
DB::raw('t.print_name as usertype_name'),
|
||||||
|
't.usertype_subject1',
|
||||||
|
't.usertype_subject2',
|
||||||
|
't.usertype_subject3',
|
||||||
|
DB::raw('p.park_name as park_name'),
|
||||||
|
DB::raw('z.zone_name as zone_name'),
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($contract_qr_id !== '') {
|
||||||
|
$q->where('rc.contract_qr_id', 'like', "%{$contract_qr_id}%");
|
||||||
|
}
|
||||||
|
if ($user_id !== '') {
|
||||||
|
$q->where('rc.user_id', 'like', "%{$user_id}%");
|
||||||
|
}
|
||||||
|
if ($user_tag_serial !== '') {
|
||||||
|
$q->where('u.user_tag_serial', 'like', "%{$user_tag_serial}%");
|
||||||
|
}
|
||||||
|
if ($park_id !== '') {
|
||||||
|
$q->where('rc.park_id', (int)$park_id);
|
||||||
|
} elseif ($selectedParkId !== '') {
|
||||||
|
$q->where('rc.park_id', (int)$selectedParkId);
|
||||||
|
}
|
||||||
|
if ($user_phonetic !== '') {
|
||||||
|
$q->where('u.user_phonetic', 'like', "%{$user_phonetic}%");
|
||||||
|
}
|
||||||
|
if ($email !== '') {
|
||||||
|
$q->where('u.user_primemail', 'like', "%{$email}%");
|
||||||
|
}
|
||||||
|
if ($user_categoryid !== '') {
|
||||||
|
$q->where('rc.user_categoryid', (int)$user_categoryid);
|
||||||
|
}
|
||||||
|
if ($park_name_kw !== '') {
|
||||||
|
$q->where('p.park_name', 'like', "%{$park_name_kw}%");
|
||||||
|
}
|
||||||
|
if ($zone_name !== '') {
|
||||||
|
$q->where('z.zone_name', 'like', "%{$zone_name}%");
|
||||||
|
}
|
||||||
|
if ($merge_phonetic_normalized !== null) {
|
||||||
|
$likeKeyword = '%' . $merge_phonetic_normalized . '%';
|
||||||
|
$q->whereRaw("REPLACE(REPLACE(IFNULL(rc.chk_user_phonetic, ''), ' ', ''), ' ', '') LIKE ?", [$likeKeyword]);
|
||||||
|
}
|
||||||
|
if ($phone !== '') {
|
||||||
|
$q->where(function ($w) use ($phone) {
|
||||||
|
$w->where('u.user_mobile', 'like', "%{$phone}%")
|
||||||
|
->orWhere('u.user_homephone', 'like', "%{$phone}%");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if ($reserve_from !== '' && ($normalized = $this->normalizeDateTimeInput($reserve_from))) {
|
||||||
|
$q->where('rc.reserve_date', '>=', $normalized);
|
||||||
|
}
|
||||||
|
if ($reserve_to !== '' && ($normalized = $this->normalizeDateTimeInput($reserve_to, true))) {
|
||||||
|
$q->where('rc.reserve_date', '<=', $normalized);
|
||||||
|
}
|
||||||
|
if ($receipt_delivery_from !== '' && ($normalized = $this->normalizeDateTimeInput($receipt_delivery_from))) {
|
||||||
|
$q->where('rc.contract_payment_day', '>=', $normalized);
|
||||||
|
}
|
||||||
|
if ($receipt_delivery_to !== '' && ($normalized = $this->normalizeDateTimeInput($receipt_delivery_to, true))) {
|
||||||
|
$q->where('rc.contract_payment_day', '<=', $normalized);
|
||||||
|
}
|
||||||
|
if ($zone_keyword !== '') {
|
||||||
|
$q->where(function ($w) use ($zone_keyword) {
|
||||||
|
$w->where('rc.zone_id', 'like', "%{$zone_keyword}%")
|
||||||
|
->orWhere('rc.pplace_no', 'like', "%{$zone_keyword}%")
|
||||||
|
->orWhere('rc.old_contract_id', 'like', "%{$zone_keyword}%");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if ($workRecordFilter === '1') {
|
||||||
|
$q->where(function ($w) {
|
||||||
|
$w->whereNull('rc.contract_flag')
|
||||||
|
->orWhere('rc.contract_flag', '=', 0);
|
||||||
|
});
|
||||||
|
} elseif ($workRecordFilter === '2') {
|
||||||
|
$q->where('rc.contract_flag', '=', 1);
|
||||||
|
}
|
||||||
|
if ($created_from !== '' && ($normalized = $this->normalizeDateTimeInput($created_from))) {
|
||||||
|
$q->where('rc.contract_created_at', '>=', $normalized);
|
||||||
|
}
|
||||||
|
if ($created_to !== '' && ($normalized = $this->normalizeDateTimeInput($created_to, true))) {
|
||||||
|
$q->where('rc.contract_created_at', '<=', $normalized);
|
||||||
|
}
|
||||||
|
if ($updated_from !== '' && ($normalized = $this->normalizeDateTimeInput($updated_from))) {
|
||||||
|
$q->where('rc.contract_updated_at', '>=', $normalized);
|
||||||
|
}
|
||||||
|
if ($updated_to !== '' && ($normalized = $this->normalizeDateTimeInput($updated_to, true))) {
|
||||||
|
$q->where('rc.contract_updated_at', '<=', $normalized);
|
||||||
|
}
|
||||||
|
if ($canceled_from !== '' && ($normalized = $this->normalizeDateTimeInput($canceled_from))) {
|
||||||
|
$q->where('rc.contract_cancelday', '>=', $normalized);
|
||||||
|
}
|
||||||
|
if ($canceled_to !== '' && ($normalized = $this->normalizeDateTimeInput($canceled_to, true))) {
|
||||||
|
$q->where('rc.contract_cancelday', '<=', $normalized);
|
||||||
|
}
|
||||||
|
if ($contract_valid_months !== '') {
|
||||||
|
$q->where('rc.enable_months', (int)$contract_valid_months);
|
||||||
|
}
|
||||||
|
if ($contract_flag !== '') {
|
||||||
|
$q->where('rc.contract_flag', (int)$contract_flag);
|
||||||
|
}
|
||||||
|
if ($contract_permission !== '') {
|
||||||
|
$q->where('rc.contract_permission', (int)$contract_permission);
|
||||||
|
}
|
||||||
|
if ($tag_qr_flag !== '') {
|
||||||
|
$q->where('rc.tag_qr_flag', (int)$tag_qr_flag);
|
||||||
|
}
|
||||||
|
if ($updateFlagFilter === '1') {
|
||||||
|
$q->where('rc.update_flag', '=', 1);
|
||||||
|
} elseif ($updateFlagFilter === '2') {
|
||||||
|
$q->where(function ($w) {
|
||||||
|
$w->whereNull('rc.update_flag')
|
||||||
|
->orWhere('rc.update_flag', '!=', 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if ($contract_cancel_flag !== '') {
|
||||||
|
$q->where('rc.contract_cancel_flag', (int)$contract_cancel_flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $q;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,119 +15,166 @@ use Response;
|
|||||||
|
|
||||||
class RegularTypeController extends Controller
|
class RegularTypeController extends Controller
|
||||||
{
|
{
|
||||||
public function list(Request $request)
|
|
||||||
{
|
|
||||||
$inputs = [
|
|
||||||
'isMethodPost' => 0,
|
|
||||||
'isExport' => 0,
|
|
||||||
'sort' => $request->input('sort', ''),
|
|
||||||
'sort_type' => $request->input('sort_type', ''),
|
|
||||||
'page' => $request->get('page', 1),
|
|
||||||
|
|
||||||
];
|
|
||||||
$inputs['isMethodPost'] = $request->isMethod('post');
|
|
||||||
$inputs['list'] = RegularType::search($inputs);
|
|
||||||
if ($inputs['list']->total() > 0 && $inputs['page'] > $inputs['list']->lastPage()) {
|
|
||||||
return redirect()->route('regular_types');
|
|
||||||
}
|
|
||||||
return view('admin.regular_types.list', $inputs);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function add(Request $request)
|
public function add(Request $request)
|
||||||
{
|
{
|
||||||
$inputs = [
|
// 画面用データ
|
||||||
'regular_type_id' => $request->input('regular_type_id'), // 定期種別ID
|
|
||||||
'city_id' => $request->input('city_name', ''), // 市区名
|
|
||||||
'regular_class_1' => $request->input('regular_class_1'), // 定期種別1
|
|
||||||
'regular_class_2' => $request->input('regular_class_2'), // 定期種別2
|
|
||||||
'regular_class_3' => $request->input('regular_class_3'), // 定期種別3
|
|
||||||
'regular_class_6' => $request->input('regular_class_6'), // 定期種別6
|
|
||||||
'regular_class_12' => $request->input('regular_class_12'), // 定期種別12
|
|
||||||
'memo' => $request->input('memo'), // 備考
|
|
||||||
];
|
|
||||||
$dataList = $this->getDataDropList();
|
$dataList = $this->getDataDropList();
|
||||||
$inputs = array_merge($inputs, $dataList);
|
|
||||||
if ($request->isMethod('POST')) {
|
|
||||||
$type = false;
|
|
||||||
$validation = new RegularTypeRequest();
|
|
||||||
$rules = $validation->rules();
|
|
||||||
$validator = Validator::make($request->all(), $rules, $validation->messages());
|
|
||||||
if (!$validator->fails()) {
|
|
||||||
\DB::transaction(function () use ($inputs, &$type) {
|
|
||||||
$new = new RegularType();
|
|
||||||
$new->fill($inputs);
|
|
||||||
if ($new->save()) {
|
|
||||||
$type = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
// フォーム入力(画面初期表示用)
|
||||||
if ($type) {
|
$inputs = [
|
||||||
$request->session()->flash('success', __('新しい成功を創造する。'));
|
'city_id' => $request->input('city_id'),
|
||||||
return redirect()->route('regular_types');
|
'regular_class_1' => $request->input('regular_class_1'),
|
||||||
} else {
|
'regular_class_2' => $request->input('regular_class_2'),
|
||||||
$request->session()->flash('error', __('新しい作成に失敗しました'));
|
'regular_class_3' => $request->input('regular_class_3'),
|
||||||
}
|
'regular_class_6' => $request->input('regular_class_6'),
|
||||||
} else {
|
'regular_class_12' => $request->input('regular_class_12'),
|
||||||
$inputs['errorMsg'] = $this->__buildErrorMessasges($validator);
|
'memo' => $request->input('memo'),
|
||||||
|
];
|
||||||
|
$viewData = array_merge($inputs, $dataList);
|
||||||
|
|
||||||
|
$record = new RegularType();
|
||||||
|
|
||||||
|
if ($request->isMethod('POST')) {
|
||||||
|
$validation = new RegularTypeRequest();
|
||||||
|
$rules = $validation->rules();
|
||||||
|
$validator = Validator::make($request->all(), $rules, $validation->messages());
|
||||||
|
|
||||||
|
if ($validator->fails()) {
|
||||||
|
return redirect()
|
||||||
|
->back()
|
||||||
|
->withErrors($validator)
|
||||||
|
->withInput();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// バリデーション成功
|
||||||
|
$payload = array_intersect_key($request->all(), array_flip([
|
||||||
|
'city_id',
|
||||||
|
'regular_class_1',
|
||||||
|
'regular_class_2',
|
||||||
|
'regular_class_3',
|
||||||
|
'regular_class_6',
|
||||||
|
'regular_class_12',
|
||||||
|
'memo',
|
||||||
|
]));
|
||||||
|
|
||||||
|
DB::transaction(function () use ($payload) {
|
||||||
|
$new = new RegularType();
|
||||||
|
$new->fill($payload);
|
||||||
|
$new->operator_id = \Auth::user()->ope_id;
|
||||||
|
$new->save();
|
||||||
|
});
|
||||||
|
|
||||||
|
$request->session()->flash('success', __('登録しました。'));
|
||||||
|
return redirect()->route('regular_types');
|
||||||
}
|
}
|
||||||
|
|
||||||
return view('admin.regular_types.add', $inputs);
|
return view('admin.regular_types.add', array_merge($viewData, [
|
||||||
|
'record' => $record,
|
||||||
|
]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function edit(Request $request, $pk, $view = '')
|
public function edit(Request $request, $id, $view = '')
|
||||||
{
|
{
|
||||||
$regular_type = RegularType::getByPk($pk);
|
// --- データ取得 ---
|
||||||
if (empty($pk) || empty($regular_type)) {
|
$record = RegularType::getById($id);
|
||||||
abort('404');
|
if (empty($id) || empty($record)) {
|
||||||
|
abort(404);
|
||||||
}
|
}
|
||||||
$data = $regular_type->getAttributes();
|
|
||||||
$dataList = $this->getDataDropList();
|
|
||||||
$data = array_merge($data, $dataList);
|
|
||||||
if ($request->isMethod('POST')) {
|
|
||||||
$type = false;
|
|
||||||
$validation = new RegularTypeRequest();
|
|
||||||
$rules = $validation->rules();
|
|
||||||
$validator = Validator::make($request->all(), $rules, $validation->messages());
|
|
||||||
$requestAll = $request->all();
|
|
||||||
$requestAll['city_id'] = $request->input('city_name');
|
|
||||||
$data = array_merge($data, $requestAll);
|
|
||||||
if (!$validator->fails()) {
|
|
||||||
|
|
||||||
\DB::transaction(function () use ($data, &$type, $regular_type) {
|
// --- 初期表示用データ ---
|
||||||
$regular_type->fill($data);
|
$data = array_merge(
|
||||||
$regular_type->save();
|
$record->getAttributes(),
|
||||||
$type = true;
|
$this->getDataDropList(),
|
||||||
});
|
[
|
||||||
if ($type) {
|
'record' => $record,
|
||||||
$request->session()->flash('success', __('更新に成功しました'));
|
'isEdit' => true,
|
||||||
return redirect()->route('regular_types');
|
]
|
||||||
} else {
|
);
|
||||||
$request->session()->flash('error', __('更新に失敗しました'));
|
|
||||||
}
|
// --- 更新処理 ---
|
||||||
} else {
|
if ($request->isMethod('POST')) {
|
||||||
$data['errorMsg'] = $this->__buildErrorMessasges($validator);
|
|
||||||
|
$validation = new RegularTypeRequest();
|
||||||
|
$rules = $validation->rules();
|
||||||
|
$validator = Validator::make($request->all(), $rules, $validation->messages());
|
||||||
|
|
||||||
|
// city_name → city_id の補正
|
||||||
|
$requestAll = $request->all();
|
||||||
|
if (isset($requestAll['city_name']) && !isset($requestAll['city_id'])) {
|
||||||
|
$requestAll['city_id'] = $requestAll['city_name'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 書き込み対象のカラムのみ許可
|
||||||
|
$payload = array_intersect_key($requestAll, array_flip([
|
||||||
|
'city_id',
|
||||||
|
'regular_class_1',
|
||||||
|
'regular_class_2',
|
||||||
|
'regular_class_3',
|
||||||
|
'regular_class_6',
|
||||||
|
'regular_class_12',
|
||||||
|
'memo',
|
||||||
|
]));
|
||||||
|
|
||||||
|
// バリデーションエラー
|
||||||
|
if ($validator->fails()) {
|
||||||
|
$data['errorMsg'] = $this->buildErrorMessages($validator);
|
||||||
|
$data = array_merge($data, $payload);
|
||||||
|
|
||||||
|
if ($view !== '') return view($view, $data);
|
||||||
|
return view('admin.regular_types.edit', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新
|
||||||
|
DB::transaction(function () use (&$record, $payload) {
|
||||||
|
$record->fill($payload);
|
||||||
|
$record->save();
|
||||||
|
});
|
||||||
|
|
||||||
|
$request->session()->flash('success', __('更新しました。'));
|
||||||
|
return redirect()->route('regular_types');
|
||||||
}
|
}
|
||||||
if ($view != '') {
|
|
||||||
|
// --- 画面表示 ---
|
||||||
|
if ($view !== '') {
|
||||||
return view($view, $data);
|
return view($view, $data);
|
||||||
}
|
}
|
||||||
return view('admin.regular_types.edit', $data);
|
return view('admin.regular_types.edit', $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete(Request $request)
|
/** バリデーションエラーをまとめる */
|
||||||
|
protected function buildErrorMessages(\Illuminate\Contracts\Validation\Validator $validator): string
|
||||||
{
|
{
|
||||||
$arr_pk = $request->get('pk');
|
return implode("\n", $validator->errors()->all());
|
||||||
if ($arr_pk) {
|
|
||||||
if (RegularType::deleteByPk($arr_pk)) {
|
|
||||||
return redirect()->route('regular_types')->with('success', __("削除が完了しました。"));
|
|
||||||
} else {
|
|
||||||
return redirect()->route('regular_types')->with('error', __('削除に失敗しました。'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return redirect()->route('regular_types')->with('error', __('削除するユーザーを選択してください。'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function delete(Request $request, $id = null)
|
||||||
|
{
|
||||||
|
// 一覧画面(checkbox で複数削除)
|
||||||
|
$ids = $request->input('pk');
|
||||||
|
|
||||||
|
// 編集画面(単体削除)
|
||||||
|
if ($id) {
|
||||||
|
$ids = [$id];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 削除対象が空の場合
|
||||||
|
if (empty($ids)) {
|
||||||
|
return redirect()
|
||||||
|
->route('regular_types')
|
||||||
|
->with('error', '削除対象が選択されていません。');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 削除処理
|
||||||
|
RegularType::destroy($ids);
|
||||||
|
|
||||||
|
return redirect()
|
||||||
|
->route('regular_types')
|
||||||
|
->with('success', '削除しました。');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public function export(Request $request)
|
public function export(Request $request)
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -168,5 +215,20 @@ class RegularTypeController extends Controller
|
|||||||
$data['cities'] = City::getList();
|
$data['cities'] = City::getList();
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
public function list(Request $request)
|
||||||
|
{
|
||||||
|
$sort = $request->input('sort', 'regular_type_id');
|
||||||
|
$sort_type = $request->input('sort_type', 'asc');
|
||||||
|
|
||||||
|
$list = \App\Models\RegularType::orderBy($sort, $sort_type)->paginate(20);
|
||||||
|
|
||||||
|
return view('admin.regular_types.list', [
|
||||||
|
'list' => $list,
|
||||||
|
'sort' => $sort,
|
||||||
|
'sort_type' => $sort_type,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -9,15 +9,45 @@ use Illuminate\Support\Facades\DB;
|
|||||||
class ReservationController extends Controller
|
class ReservationController extends Controller
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* 予約者一覧表示
|
* 一覧表示(GET/POST)
|
||||||
*/
|
*/
|
||||||
public function list(Request $request)
|
public function list(Request $request)
|
||||||
{
|
{
|
||||||
$q = DB::table('reserve as r')
|
// ベースクエリを構築
|
||||||
|
$q = DB::table('regular_contract as rc')
|
||||||
|
->leftJoin('user as u','rc.user_id','=','u.user_id')
|
||||||
->select([
|
->select([
|
||||||
'r.user_id',
|
'rc.contract_id',
|
||||||
|
'rc.contract_qr_id',
|
||||||
|
'rc.user_id',
|
||||||
|
'rc.user_categoryid',
|
||||||
|
'rc.park_id',
|
||||||
|
'rc.contract_created_at',
|
||||||
|
'rc.contract_periods',
|
||||||
|
'rc.contract_periode',
|
||||||
|
'rc.tag_qr_flag',
|
||||||
|
'rc.contract_flag',
|
||||||
|
'rc.contract_cancel_flag',
|
||||||
|
'rc.contract_payment_day',
|
||||||
|
'rc.contract_money',
|
||||||
|
'rc.billing_amount',
|
||||||
|
'rc.contract_permission',
|
||||||
|
'rc.contract_manual',
|
||||||
|
'rc.contract_notice',
|
||||||
|
'rc.update_flag',
|
||||||
|
'rc.800m_flag',
|
||||||
|
'rc.price_parkplaceid',
|
||||||
|
'rc.psection_id',
|
||||||
|
'rc.reserve_date',
|
||||||
|
'p.park_name',
|
||||||
'u.user_name',
|
'u.user_name',
|
||||||
'u.user_phonetic',
|
'u.user_phonetic',
|
||||||
|
'u.user_mobile',
|
||||||
|
'u.user_seq',
|
||||||
|
'u.user_homephone',
|
||||||
|
'u.user_primemail',
|
||||||
|
'u.user_gender',
|
||||||
|
'u.user_birthdate',
|
||||||
'u.user_regident_zip',
|
'u.user_regident_zip',
|
||||||
'u.user_regident_pre',
|
'u.user_regident_pre',
|
||||||
'u.user_regident_city',
|
'u.user_regident_city',
|
||||||
@ -26,74 +56,195 @@ class ReservationController extends Controller
|
|||||||
'u.user_relate_pre',
|
'u.user_relate_pre',
|
||||||
'u.user_relate_city',
|
'u.user_relate_city',
|
||||||
'u.user_relate_add',
|
'u.user_relate_add',
|
||||||
'u.user_birthdate',
|
|
||||||
'u.user_gender',
|
|
||||||
'u.user_mobile',
|
|
||||||
'u.user_homephone',
|
|
||||||
'u.user_school',
|
|
||||||
'u.user_graduate',
|
'u.user_graduate',
|
||||||
|
'u.user_workplace',
|
||||||
|
'u.user_school',
|
||||||
'u.user_remarks',
|
'u.user_remarks',
|
||||||
'r.reserve_id',
|
'u.user_tag_serial_64',
|
||||||
'r.park_id',
|
'u.user_reduction',
|
||||||
'p.park_name',
|
DB::raw('rc.user_securitynum as crime_prevention'),
|
||||||
'r.price_parkplaceid',
|
DB::raw('rc.contract_seal_issue as seal_issue_count'),
|
||||||
'r.psection_id',
|
DB::raw("CASE rc.enable_months
|
||||||
'r.reserve_date',
|
WHEN 1 THEN '月極(1ヶ月)'
|
||||||
'r.reserve_reduction as reduction',
|
WHEN 3 THEN '3ヶ月'
|
||||||
'r.800m_flag as within_800m_flag',
|
WHEN 6 THEN '6ヶ月'
|
||||||
|
WHEN 12 THEN '年'
|
||||||
|
ELSE CONCAT(rc.enable_months, 'ヶ月') END as ticket_type"),
|
||||||
|
DB::raw('ps.psection_subject as vehicle_type'),
|
||||||
|
// 利用者分類のラベル(usertype テーブルの subject を取得)
|
||||||
|
DB::raw('ut.usertype_subject1 as user_category1'),
|
||||||
|
DB::raw('ut.usertype_subject2 as user_category2'),
|
||||||
|
DB::raw('ut.usertype_subject3 as user_category3'),
|
||||||
])
|
])
|
||||||
->leftJoin('user as u', 'r.user_id', '=', 'u.user_id')
|
->leftJoin('park as p', 'rc.park_id', '=', 'p.park_id')
|
||||||
->leftJoin('park as p', 'r.park_id', '=', 'p.park_id'); // 追加
|
->leftJoin('psection as ps', 'rc.psection_id', '=', 'ps.psection_id')
|
||||||
|
->leftJoin('usertype as ut', 'u.user_categoryid', '=', 'ut.user_categoryid');
|
||||||
|
|
||||||
// フィルター条件
|
// ===== 絞り込み条件 =====
|
||||||
|
|
||||||
|
// 駐輪場で絞る(完全一致)
|
||||||
if ($request->filled('park_id')) {
|
if ($request->filled('park_id')) {
|
||||||
$q->where('r.park_id', $request->input('park_id'));
|
$q->where('rc.park_id', $request->park_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 利用者IDで絞る(完全一致)
|
||||||
if ($request->filled('user_id')) {
|
if ($request->filled('user_id')) {
|
||||||
$q->where('r.user_id', $request->input('user_id'));
|
$q->where('rc.user_id', $request->user_id);
|
||||||
}
|
}
|
||||||
if ($request->filled('user_categoryid')) {
|
|
||||||
$q->where('r.user_categoryid', $request->input('user_categoryid'));
|
// 利用者分類で絞る(※ select の value を user_categoryid にしているため、user テーブルのカラムで比較)
|
||||||
|
if ($request->filled('user_category1')) {
|
||||||
|
$q->where('u.user_categoryid', $request->user_category1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// タグシリアルで部分一致検索
|
||||||
if ($request->filled('user_tag_serial')) {
|
if ($request->filled('user_tag_serial')) {
|
||||||
$q->where('u.user_tag_serial', 'like', '%' . $request->input('user_tag_serial') . '%');
|
$q->where('u.user_tag_serial', 'like', '%' . $request->input('user_tag_serial') . '%');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// タグシリアル64進で部分一致検索
|
||||||
if ($request->filled('user_tag_serial_64')) {
|
if ($request->filled('user_tag_serial_64')) {
|
||||||
$q->where('u.user_tag_serial_64', 'like', '%' . $request->input('user_tag_serial_64') . '%');
|
$val = $request->user_tag_serial_64;
|
||||||
|
$q->where('u.user_tag_serial_64','like','%'.$val.'%');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// フリガナで部分一致
|
||||||
if ($request->filled('user_phonetic')) {
|
if ($request->filled('user_phonetic')) {
|
||||||
$q->where('u.user_phonetic', 'like', '%' . $request->input('user_phonetic') . '%');
|
$q->where('u.user_phonetic', 'like', '%' . $request->user_phonetic . '%');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 携帯電話で部分一致
|
||||||
if ($request->filled('user_mobile')) {
|
if ($request->filled('user_mobile')) {
|
||||||
$q->where(function($sub) use ($request) {
|
$q->where('u.user_mobile', 'like', '%' . $request->user_mobile . '%');
|
||||||
$sub->where('u.user_mobile', 'like', '%' . $request->input('user_mobile') . '%')
|
|
||||||
->orWhere('u.user_homephone', 'like', '%' . $request->input('user_mobile') . '%');
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// メールアドレスで部分一致
|
||||||
if ($request->filled('user_primemail')) {
|
if ($request->filled('user_primemail')) {
|
||||||
$q->where('u.user_primemail', 'like', '%' . $request->input('user_primemail') . '%');
|
$q->where('u.user_primemail', 'like', '%' . $request->user_primemail . '%');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 勤務先で部分一致
|
||||||
if ($request->filled('user_workplace')) {
|
if ($request->filled('user_workplace')) {
|
||||||
$q->where('u.user_workplace', 'like', '%' . $request->input('user_workplace') . '%');
|
$q->where('u.user_workplace', 'like', '%' . $request->user_workplace . '%');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 学校で部分一致
|
||||||
if ($request->filled('user_school')) {
|
if ($request->filled('user_school')) {
|
||||||
$q->where('u.user_school', 'like', '%' . $request->input('user_school') . '%');
|
$q->where('u.user_school', 'like', '%' . $request->user_school . '%');
|
||||||
}
|
}
|
||||||
|
|
||||||
// ソート
|
// ===== ソート処理 =====
|
||||||
$sort = $request->input('sort', 'r.reserve_id');
|
// 指定があればその列でソート、なければデフォルトで契約IDの昇順
|
||||||
$sortType = $request->input('sort_type', 'desc');
|
$sort = $request->input('sort'); // null 許容
|
||||||
$allowSorts = ['r.reserve_id', 'r.reserve_date', 'r.reserve_start', 'r.reserve_end'];
|
$sortType = $request->input('sort_type','asc');
|
||||||
if (!in_array($sort, $allowSorts)) {
|
|
||||||
$sort = 'r.reserve_id';
|
$allowSorts = [
|
||||||
|
'rc.contract_id',
|
||||||
|
'rc.user_id',
|
||||||
|
'u.user_name',
|
||||||
|
'rc.tag_qr_flag',
|
||||||
|
'p.park_name',
|
||||||
|
];
|
||||||
|
if ($sort && in_array($sort, $allowSorts)) {
|
||||||
|
$sortType = $sortType === 'desc' ? 'desc' : 'asc';
|
||||||
|
$q->orderBy($sort, $sortType);
|
||||||
|
} else {
|
||||||
|
// デフォルトソート
|
||||||
|
$sort = null;
|
||||||
|
$sortType = null;
|
||||||
|
$q->orderBy('rc.contract_id','asc');
|
||||||
}
|
}
|
||||||
$sortType = ($sortType === 'asc') ? 'asc' : 'desc';
|
|
||||||
|
|
||||||
$rows = $q->orderBy($sort, $sortType)->paginate(20)->withQueryString();
|
// ページネーション(クエリ文字列を引き継ぐ)
|
||||||
|
$rows = $q->paginate(20)->appends($request->query());
|
||||||
|
|
||||||
// 駐輪場リスト取得(必要なら)
|
// 駐輪場セレクト用データ取得
|
||||||
$parks = DB::table('park')->select('park_id', 'park_name')->get();
|
$parks = DB::table('park')->select('park_id', 'park_name')->orderBy('park_name')->get();
|
||||||
|
|
||||||
return view('admin.reservation.list', compact('rows', 'sort', 'sortType', 'parks'));
|
// 利用者分類セレクト用:実際に使用されている分類のみを取得する
|
||||||
|
$categories = $this->buildCategoryOptions(true);
|
||||||
|
|
||||||
|
// ビューに渡す
|
||||||
|
return view('admin.reservation.list', compact('rows', 'sort', 'sortType', 'parks', 'categories'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 詳細表示
|
||||||
|
*/
|
||||||
|
public function info($id)
|
||||||
|
{
|
||||||
|
// 指定契約IDの詳細を取得
|
||||||
|
$contract = DB::table('regular_contract as rc')
|
||||||
|
->select([
|
||||||
|
'rc.*',
|
||||||
|
'p.park_name',
|
||||||
|
'u.user_name',
|
||||||
|
'u.user_phonetic',
|
||||||
|
'u.user_mobile',
|
||||||
|
'u.user_homephone',
|
||||||
|
'u.user_primemail',
|
||||||
|
'u.user_gender',
|
||||||
|
'u.user_birthdate',
|
||||||
|
'u.user_regident_city',
|
||||||
|
])
|
||||||
|
->leftJoin('park as p', 'rc.park_id', '=', 'p.park_id')
|
||||||
|
->leftJoin('user as u', 'rc.user_id', '=', 'u.user_id')
|
||||||
|
->where('rc.contract_id', $id)
|
||||||
|
->first();
|
||||||
|
|
||||||
|
if (!$contract) { abort(404); }
|
||||||
|
|
||||||
|
return view('admin.reservation.info', compact('contract'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 利用者分類選択肢を取得
|
||||||
|
*
|
||||||
|
* @param bool $onlyUsed true の場合は regular_contract に出現する分類のみ返す
|
||||||
|
* @return array [user_categoryid => label, ...]
|
||||||
|
*/
|
||||||
|
private function buildCategoryOptions(bool $onlyUsed = false): array
|
||||||
|
{
|
||||||
|
if (! $onlyUsed) {
|
||||||
|
// 全件取得(既存の挙動)
|
||||||
|
return DB::table('usertype')
|
||||||
|
->orderBy('user_categoryid', 'asc')
|
||||||
|
->get()
|
||||||
|
->mapWithKeys(function ($row) {
|
||||||
|
$label = collect([
|
||||||
|
$row->usertype_subject1 ?? '',
|
||||||
|
$row->usertype_subject2 ?? '',
|
||||||
|
$row->usertype_subject3 ?? '',
|
||||||
|
])->filter(fn ($v) => $v !== '')->implode('/');
|
||||||
|
|
||||||
|
return [$row->user_categoryid => $label !== '' ? $label : (string) $row->user_categoryid];
|
||||||
|
})
|
||||||
|
->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 実際に使用されている分類のみ取得する(内部結合で user と regular_contract と紐付くもの)
|
||||||
|
$rows = DB::table('usertype as ut')
|
||||||
|
->join('user as u', 'u.user_categoryid', '=', 'ut.user_categoryid')
|
||||||
|
->join('regular_contract as rc', 'rc.user_id', '=', 'u.user_id')
|
||||||
|
->select(
|
||||||
|
'ut.user_categoryid',
|
||||||
|
'ut.usertype_subject1',
|
||||||
|
'ut.usertype_subject2',
|
||||||
|
'ut.usertype_subject3'
|
||||||
|
)
|
||||||
|
->groupBy('ut.user_categoryid', 'ut.usertype_subject1', 'ut.usertype_subject2', 'ut.usertype_subject3')
|
||||||
|
->orderBy('ut.user_categoryid', 'asc')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
// ラベルを組み立てて配列で返す
|
||||||
|
return $rows->mapWithKeys(function ($row) {
|
||||||
|
$label = collect([
|
||||||
|
$row->usertype_subject1 ?? '',
|
||||||
|
$row->usertype_subject2 ?? '',
|
||||||
|
$row->usertype_subject3 ?? '',
|
||||||
|
])->filter(fn ($v) => $v !== '')->implode('/');
|
||||||
|
|
||||||
|
return [$row->user_categoryid => $label !== '' ? $label : (string) $row->user_categoryid];
|
||||||
|
})->toArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,10 @@ use Illuminate\Support\Facades\Schema;
|
|||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Validator;
|
use Illuminate\Support\Facades\Validator;
|
||||||
|
use App\Models\Park;
|
||||||
|
use App\Models\PriceA;
|
||||||
|
use App\Models\PriceB;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
class ReservesController
|
class ReservesController
|
||||||
{
|
{
|
||||||
@ -24,7 +28,7 @@ class ReservesController
|
|||||||
{
|
{
|
||||||
// ── 並び順(既定: reserve_id DESC)──────────────────────────────
|
// ── 並び順(既定: reserve_id DESC)──────────────────────────────
|
||||||
$sort = $request->input('sort', 'reserve_id');
|
$sort = $request->input('sort', 'reserve_id');
|
||||||
$sortType = strtolower($request->input('sort_type', 'desc')) === 'asc' ? 'asc' : 'desc';
|
$sortType = strtolower($request->input('sort_type', 'asc')) === 'desc' ? 'desc' : 'asc';
|
||||||
|
|
||||||
// ── 絞り込み(必要最低限:利用者/駐輪場/期間)──────────────────
|
// ── 絞り込み(必要最低限:利用者/駐輪場/期間)──────────────────
|
||||||
$userId = trim((string) $request->input('user_id', ''));
|
$userId = trim((string) $request->input('user_id', ''));
|
||||||
@ -32,14 +36,32 @@ class ReservesController
|
|||||||
$fromDt = $request->input('reserve_date_from', '');
|
$fromDt = $request->input('reserve_date_from', '');
|
||||||
$toDt = $request->input('reserve_date_to', '');
|
$toDt = $request->input('reserve_date_to', '');
|
||||||
$keyword = trim((string) $request->input('keyword', '')); // 利用者名かな など
|
$keyword = trim((string) $request->input('keyword', '')); // 利用者名かな など
|
||||||
|
$validFlag = trim((string) $request->input('valid_flag', ''));
|
||||||
|
$mailSentFrom = $request->input('mail_sent_from', '');
|
||||||
|
$mailSentTo = $request->input('mail_sent_to', '');
|
||||||
|
|
||||||
|
$priceUnion = DB::query()->fromSub(function ($sub) {
|
||||||
|
$sub->from('price_a')
|
||||||
|
->select('price_parkplaceid', 'prine_name')
|
||||||
|
->unionAll(
|
||||||
|
DB::table('price_b')->select('price_parkplaceid', 'prine_name')
|
||||||
|
);
|
||||||
|
}, 'price_union');
|
||||||
|
|
||||||
// ── クエリ構築 ────────────────────────────────────────────────
|
// ── クエリ構築 ────────────────────────────────────────────────
|
||||||
$q = DB::table('reserve as r')
|
$q = DB::table('reserve as r')
|
||||||
->leftJoin('user as u', 'u.user_id', '=', 'r.user_id') // user: user_name, user_phonetic 等【turn12file9†L26-L34】
|
->leftJoin('user as u', 'u.user_id', '=', 'r.user_id')
|
||||||
->leftJoin('park as p', 'p.park_id', '=', 'r.park_id') // park: park_name 等【turn12file4†L17-L25】
|
->leftJoin('park as p', 'p.park_id', '=', 'r.park_id')
|
||||||
|
->leftJoin('psection as ps', 'ps.psection_id', '=', 'r.psection_id')
|
||||||
|
->leftJoin('ptype as pt', 'pt.ptype_id', '=', 'r.ptype_id')
|
||||||
|
->leftJoin('usertype as t', 't.user_categoryid', '=', 'r.user_categoryid')
|
||||||
|
->leftJoinSub($priceUnion, 'price_union', function ($join) {
|
||||||
|
$join->on('price_union.price_parkplaceid', '=', 'r.price_parkplaceid');
|
||||||
|
})
|
||||||
->select([
|
->select([
|
||||||
'r.reserve_id',
|
'r.reserve_id',
|
||||||
'r.contract_id',
|
'r.contract_id',
|
||||||
|
'r.contract_created_at',
|
||||||
'r.user_id',
|
'r.user_id',
|
||||||
'r.park_id',
|
'r.park_id',
|
||||||
'r.price_parkplaceid',
|
'r.price_parkplaceid',
|
||||||
@ -47,19 +69,36 @@ class ReservesController
|
|||||||
'r.reserve_date',
|
'r.reserve_date',
|
||||||
'r.reserve_start',
|
'r.reserve_start',
|
||||||
'r.reserve_end',
|
'r.reserve_end',
|
||||||
|
'r.reserve_reduction',
|
||||||
|
'r.reserve_auto_remind',
|
||||||
|
'r.reserve_manual_remind',
|
||||||
|
DB::raw('r.`800m_flag` as flag_800m'),
|
||||||
'r.reserve_cancelday',
|
'r.reserve_cancelday',
|
||||||
'r.valid_flag',
|
'r.valid_flag',
|
||||||
|
'r.reserve_manual',
|
||||||
|
'r.reserve_notice',
|
||||||
|
'r.sent_date',
|
||||||
|
'r.reserve_order',
|
||||||
|
'r.valid_flag',
|
||||||
'r.ope_id',
|
'r.ope_id',
|
||||||
|
'r.user_categoryid',
|
||||||
DB::raw('u.user_name as user_name'),
|
DB::raw('u.user_name as user_name'),
|
||||||
DB::raw('u.user_phonetic as user_phonetic'),
|
DB::raw('u.user_phonetic as user_phonetic'),
|
||||||
DB::raw('u.user_mobile as user_mobile'),
|
DB::raw('u.user_mobile as user_mobile'),
|
||||||
DB::raw('p.park_name as park_name'),
|
DB::raw('p.park_name as park_name'),
|
||||||
|
DB::raw('pt.ptype_subject as ptype_subject'),
|
||||||
|
DB::raw('ps.psection_subject as psection_subject'),
|
||||||
|
DB::raw('price_union.price_parkplaceid as display_price_parkplaceid'),
|
||||||
|
DB::raw('price_union.prine_name as display_prine_name'),
|
||||||
|
DB::raw('t.usertype_subject1 as usertype_subject1'),
|
||||||
|
DB::raw('t.usertype_subject2 as usertype_subject2'),
|
||||||
|
DB::raw('t.usertype_subject3 as usertype_subject3'),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if ($userId !== '')
|
if ($userId !== '')
|
||||||
$q->where('r.user_id', 'like', "%{$userId}%");
|
$q->where('r.user_id', 'like', "%{$userId}%");
|
||||||
if ($parkId !== '')
|
if ($parkId !== '')
|
||||||
$q->where('r.park_id', 'like', "%{$parkId}%");
|
$q->where('r.park_id', '=', (int) $parkId);
|
||||||
|
|
||||||
if ($fromDt)
|
if ($fromDt)
|
||||||
$q->whereDate('r.reserve_date', '>=', $fromDt);
|
$q->whereDate('r.reserve_date', '>=', $fromDt);
|
||||||
@ -72,36 +111,108 @@ class ReservesController
|
|||||||
->orWhere('u.user_phonetic', 'like', "%{$keyword}%");
|
->orWhere('u.user_phonetic', 'like', "%{$keyword}%");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (in_array($validFlag, ['0', '1'], true)) {
|
||||||
|
$q->where('r.valid_flag', '=', (int) $validFlag);
|
||||||
|
}
|
||||||
|
if ($mailSentFrom !== null && $mailSentFrom !== '') {
|
||||||
|
$q->where('r.sent_date', '>=', $mailSentFrom);
|
||||||
|
}
|
||||||
|
if ($mailSentTo !== null && $mailSentTo !== '') {
|
||||||
|
$q->where('r.sent_date', '<=', $mailSentTo);
|
||||||
|
}
|
||||||
|
|
||||||
// ソート許可カラム(JOIN 先も含む)
|
// ソート許可カラム(JOIN 先も含む)
|
||||||
$sortable = [
|
$sortable = [
|
||||||
'reserve_id',
|
'reserve_id',
|
||||||
'contract_id',
|
'contract_id',
|
||||||
|
'contract_created_at',
|
||||||
|
'user_categoryid',
|
||||||
'user_id',
|
'user_id',
|
||||||
'park_id',
|
|
||||||
'reserve_date',
|
'reserve_date',
|
||||||
'reserve_start',
|
'park_price_name',
|
||||||
'reserve_end',
|
'price_parkplaceid',
|
||||||
|
'psection_subject',
|
||||||
|
'ptype_subject',
|
||||||
|
'park_name',
|
||||||
|
'reserve_reduction',
|
||||||
|
'reserve_auto_remind',
|
||||||
|
'reserve_manual_remind',
|
||||||
|
'flag_800m',
|
||||||
'reserve_cancelday',
|
'reserve_cancelday',
|
||||||
'valid_flag',
|
'valid_flag',
|
||||||
'ope_id',
|
'sent_date',
|
||||||
'user_name',
|
'reserve_manual',
|
||||||
'user_phonetic',
|
'reserve_notice',
|
||||||
'user_mobile',
|
'reserve_order',
|
||||||
'park_name',
|
|
||||||
];
|
];
|
||||||
if (!in_array($sort, $sortable, true))
|
if (!in_array($sort, $sortable, true)) {
|
||||||
$sort = 'reserve_id';
|
$sort = 'reserve_id';
|
||||||
|
}
|
||||||
|
|
||||||
$sortMap = [
|
$sortMap = [
|
||||||
'user_name' => 'u.user_name',
|
'reserve_id' => DB::raw('r.reserve_id'),
|
||||||
'user_phonetic' => 'u.user_phonetic',
|
'contract_id' => DB::raw('r.contract_id'),
|
||||||
'user_mobile' => 'u.user_mobile',
|
'contract_created_at' => DB::raw('r.contract_created_at'),
|
||||||
'park_name' => 'p.park_name',
|
'user_categoryid' => DB::raw('r.user_categoryid'),
|
||||||
|
'user_id' => DB::raw('r.user_id'),
|
||||||
|
'reserve_date' => DB::raw('r.reserve_date'),
|
||||||
|
'park_price_name' => DB::raw('price_union.prine_name'),
|
||||||
|
'price_parkplaceid' => DB::raw('r.price_parkplaceid'),
|
||||||
|
'psection_subject' => DB::raw('ps.psection_subject'),
|
||||||
|
'ptype_subject' => DB::raw('pt.ptype_subject'),
|
||||||
|
'park_name' => DB::raw('p.park_name'),
|
||||||
|
'reserve_reduction' => DB::raw('r.reserve_reduction'),
|
||||||
|
'reserve_auto_remind' => DB::raw('r.reserve_auto_remind'),
|
||||||
|
'reserve_manual_remind' => DB::raw('r.reserve_manual_remind'),
|
||||||
|
'flag_800m' => DB::raw('r.`800m_flag`'),
|
||||||
|
'reserve_cancelday' => DB::raw('r.reserve_cancelday'),
|
||||||
|
'valid_flag' => DB::raw('r.valid_flag'),
|
||||||
|
'sent_date' => DB::raw('r.sent_date'),
|
||||||
|
'reserve_manual' => DB::raw('r.reserve_manual'),
|
||||||
|
'reserve_notice' => DB::raw('r.reserve_notice'),
|
||||||
|
'reserve_order' => DB::raw('r.reserve_order'),
|
||||||
];
|
];
|
||||||
$sortCol = $sortMap[$sort] ?? ('r.' . $sort);
|
$sortColumn = $sortMap[$sort] ?? DB::raw('r.reserve_id');
|
||||||
|
$q->orderBy($sortColumn, $sortType);
|
||||||
|
|
||||||
$list = $q->orderBy($sortCol, $sortType)->paginate(50);
|
$parkOptions = Park::query()
|
||||||
|
->orderBy('park_id', 'asc')
|
||||||
|
->pluck('park_name', 'park_id')
|
||||||
|
->toArray();
|
||||||
|
|
||||||
|
$list = $q->paginate(50);
|
||||||
|
|
||||||
|
$placeIds = $list->getCollection()
|
||||||
|
->pluck('price_parkplaceid')
|
||||||
|
->filter()
|
||||||
|
->unique()
|
||||||
|
->values()
|
||||||
|
->all();
|
||||||
|
|
||||||
|
if (!empty($placeIds)) {
|
||||||
|
$priceNamesA = PriceA::query()
|
||||||
|
->whereIn('price_parkplaceid', $placeIds)
|
||||||
|
->pluck('prine_name', 'price_parkplaceid')
|
||||||
|
->toArray();
|
||||||
|
|
||||||
|
$priceNamesB = PriceB::query()
|
||||||
|
->whereIn('price_parkplaceid', $placeIds)
|
||||||
|
->pluck('prine_name', 'price_parkplaceid')
|
||||||
|
->toArray();
|
||||||
|
|
||||||
|
// 駐輪場所名のマッピング(price_b で上書き)
|
||||||
|
$priceNames = array_replace($priceNamesA, $priceNamesB);
|
||||||
|
|
||||||
|
$list->setCollection(
|
||||||
|
$list->getCollection()->map(function ($row) use ($priceNames) {
|
||||||
|
$id = $row->price_parkplaceid ?? null;
|
||||||
|
$row->display_prine_name = ($id !== null && array_key_exists($id, $priceNames))
|
||||||
|
? $priceNames[$id]
|
||||||
|
: null;
|
||||||
|
return $row;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return view('admin.reserves.list', [
|
return view('admin.reserves.list', [
|
||||||
'list' => $list,
|
'list' => $list,
|
||||||
@ -113,6 +224,10 @@ class ReservesController
|
|||||||
'reserve_date_from' => $fromDt,
|
'reserve_date_from' => $fromDt,
|
||||||
'reserve_date_to' => $toDt,
|
'reserve_date_to' => $toDt,
|
||||||
'keyword' => $keyword,
|
'keyword' => $keyword,
|
||||||
|
'valid_flag' => $validFlag,
|
||||||
|
'mail_sent_from' => $mailSentFrom,
|
||||||
|
'mail_sent_to' => $mailSentTo,
|
||||||
|
'parkOptions' => $parkOptions,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,7 +237,44 @@ class ReservesController
|
|||||||
public function add(Request $request)
|
public function add(Request $request)
|
||||||
{
|
{
|
||||||
if ($request->isMethod('get')) {
|
if ($request->isMethod('get')) {
|
||||||
return view('admin.reserves.add');
|
$userTypes = DB::table('usertype')
|
||||||
|
->orderBy('user_categoryid', 'asc')
|
||||||
|
->get([
|
||||||
|
'user_categoryid',
|
||||||
|
'usertype_subject1',
|
||||||
|
'usertype_subject2',
|
||||||
|
'usertype_subject3',
|
||||||
|
])
|
||||||
|
->map(function ($row) {
|
||||||
|
$labels = array_filter([
|
||||||
|
$row->usertype_subject1,
|
||||||
|
$row->usertype_subject2,
|
||||||
|
$row->usertype_subject3,
|
||||||
|
], fn ($v) => $v !== null && $v !== '');
|
||||||
|
$row->display_name = $labels ? implode('/', $labels) : '';
|
||||||
|
return $row;
|
||||||
|
});
|
||||||
|
|
||||||
|
$parks = Park::query()
|
||||||
|
->orderBy('park_id', 'asc')
|
||||||
|
->get(['park_id', 'park_name']);
|
||||||
|
|
||||||
|
$priceA = PriceA::query()
|
||||||
|
->select('price_parkplaceid', 'prine_name')
|
||||||
|
->get();
|
||||||
|
$priceB = PriceB::query()
|
||||||
|
->select('price_parkplaceid', 'prine_name')
|
||||||
|
->get();
|
||||||
|
$priceOptions = $priceA->merge($priceB)
|
||||||
|
->sortBy('price_parkplaceid', SORT_NATURAL)
|
||||||
|
->unique('price_parkplaceid')
|
||||||
|
->values();
|
||||||
|
|
||||||
|
return view('admin.reserves.add', [
|
||||||
|
'userTypes' => $userTypes,
|
||||||
|
'parks' => $parks,
|
||||||
|
'priceOptions' => $priceOptions,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 予約の最低限バリデーション(必要に応じて追加)
|
// 予約の最低限バリデーション(必要に応じて追加)
|
||||||
@ -141,19 +293,28 @@ class ReservesController
|
|||||||
return back()->withErrors($v)->withInput();
|
return back()->withErrors($v)->withInput();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$now = now();
|
||||||
|
$opeId = optional(Auth::user())->ope_id;
|
||||||
|
|
||||||
|
$nextReserveId = DB::transaction(function () {
|
||||||
|
$currentMax = DB::table('reserve')->max('reserve_id');
|
||||||
|
return $currentMax ? $currentMax + 1 : 1;
|
||||||
|
});
|
||||||
|
|
||||||
DB::table('reserve')->insert([
|
DB::table('reserve')->insert([
|
||||||
|
'reserve_id' => $nextReserveId,
|
||||||
'user_id' => (int) $request->input('user_id'),
|
'user_id' => (int) $request->input('user_id'),
|
||||||
'park_id' => (int) $request->input('park_id'),
|
'park_id' => (int) $request->input('park_id'),
|
||||||
'contract_id' => $request->input('contract_id'), // 任意:regular_contract と紐づける場合【turn12file7†L20-L28】
|
'contract_id' => $request->input('contract_id'),
|
||||||
'price_parkplaceid' => $request->input('price_parkplaceid'),
|
'price_parkplaceid' => $request->input('price_parkplaceid'),
|
||||||
'psection_id' => $request->input('psection_id'),
|
'psection_id' => $request->input('psection_id'),
|
||||||
'reserve_date' => $request->input('reserve_date'),
|
'reserve_date' => $request->input('reserve_date'),
|
||||||
'reserve_start' => $request->input('reserve_start'),
|
'reserve_start' => $now,
|
||||||
'reserve_end' => $request->input('reserve_end'),
|
'reserve_end' => $now,
|
||||||
'valid_flag' => $request->input('valid_flag'),
|
'valid_flag' => $request->input('valid_flag'),
|
||||||
'ope_id' => $request->input('ope_id'),
|
'ope_id' => $opeId,
|
||||||
'created_at' => now(),
|
'created_at' => $now,
|
||||||
'updated_at' => now(),
|
'updated_at' => $now,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return redirect()->route('reserves')->with('success', '予約を登録しました。');
|
return redirect()->route('reserves')->with('success', '予約を登録しました。');
|
||||||
@ -264,8 +425,7 @@ class ReservesController
|
|||||||
|
|
||||||
$parkOptions = DB::table('park')
|
$parkOptions = DB::table('park')
|
||||||
->orderBy('park_id', 'asc')
|
->orderBy('park_id', 'asc')
|
||||||
->limit(5000)
|
->pluck('park_name', 'park_id')
|
||||||
->pluck(DB::raw("concat(park_id, ' ', park_name)"), 'park_id')
|
|
||||||
->toArray();
|
->toArray();
|
||||||
|
|
||||||
$userTypeOptions = Schema::hasTable('usertype')
|
$userTypeOptions = Schema::hasTable('usertype')
|
||||||
|
|||||||
78
app/Http/Controllers/Admin/SealsController.php
Normal file
78
app/Http/Controllers/Admin/SealsController.php
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
|
class SealsController extends Controller
|
||||||
|
{
|
||||||
|
public function list(Request $request)
|
||||||
|
{
|
||||||
|
$q = \DB::table('seal as s')
|
||||||
|
->leftJoin('park as p','s.park_id','=','p.park_id')
|
||||||
|
->leftJoin('regular_contract as rc','s.contract_id','=','rc.contract_id')
|
||||||
|
->leftJoin('psection as ps','rc.psection_id','=','ps.psection_id')
|
||||||
|
->select([
|
||||||
|
's.seal_issueid',
|
||||||
|
's.park_id',
|
||||||
|
'p.park_name',
|
||||||
|
's.contract_id',
|
||||||
|
'rc.psection_id',
|
||||||
|
\DB::raw('ps.psection_subject AS psection_subject'),
|
||||||
|
'rc.contract_seal_issue',
|
||||||
|
's.seal_day',
|
||||||
|
's.seal_reason',
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 駐輪場フィルタ
|
||||||
|
if($request->filled('park_id')){
|
||||||
|
$q->where('s.park_id',$request->park_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 期間フィルタ
|
||||||
|
$periodType = $request->input('period_type','range');
|
||||||
|
if($periodType === 'range'){
|
||||||
|
if($request->filled('seal_day_from')){
|
||||||
|
$q->whereDate('s.seal_day','>=',$request->seal_day_from);
|
||||||
|
}
|
||||||
|
if($request->filled('seal_day_to')){
|
||||||
|
$q->whereDate('s.seal_day','<=',$request->seal_day_to);
|
||||||
|
}
|
||||||
|
} elseif($periodType === 'recent' && $request->filled('recent_period')){
|
||||||
|
$map = ['12m'=>12,'6m'=>6,'3m'=>3,'2m'=>2,'1m'=>1,'1w'=>'1w'];
|
||||||
|
$key = $request->recent_period;
|
||||||
|
if(isset($map[$key])){
|
||||||
|
$from = $key==='1w' ? now()->subWeek() : now()->subMonths($map[$key]);
|
||||||
|
$q->where('s.seal_day','>=',$from->toDateString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- 並び替え: パラメータがある場合のみ適用 ---
|
||||||
|
$sort = $request->query('sort'); // デフォルト null
|
||||||
|
$sortType = $request->query('sort_type','asc'); // 指定なければ asc
|
||||||
|
$allow = [
|
||||||
|
'seal_issueid' => 's.seal_issueid',
|
||||||
|
'park_id' => 'p.park_name',
|
||||||
|
'contract_id' => 's.contract_id',
|
||||||
|
'seal_day' => 's.seal_day',
|
||||||
|
'contract_seal_issue' => 'rc.contract_seal_issue',
|
||||||
|
'psection_subject' => 'ps.psection_subject',
|
||||||
|
];
|
||||||
|
if(isset($allow[$sort])){
|
||||||
|
$sortType = $sortType === 'desc' ? 'desc' : 'asc';
|
||||||
|
$q->orderBy($allow[$sort], $sortType);
|
||||||
|
}
|
||||||
|
// 並び替え指定が無い時は orderBy 不要 → DB の物理(主キー)順
|
||||||
|
|
||||||
|
$list = $q->paginate(20)->appends($request->query());
|
||||||
|
|
||||||
|
$parks = \DB::table('park')
|
||||||
|
->select('park_id','park_name')
|
||||||
|
->orderBy('park_name')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
return view('admin.seals.list', compact('list','parks','sort','sortType'));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -16,25 +16,43 @@ class SettingController extends Controller
|
|||||||
public function list(Request $request)
|
public function list(Request $request)
|
||||||
{
|
{
|
||||||
$perPage = \App\Utils::item_per_page ?? 20;
|
$perPage = \App\Utils::item_per_page ?? 20;
|
||||||
$list = Setting::orderBy('setting_id', 'desc')->paginate($perPage);
|
|
||||||
|
// リクエストから取得
|
||||||
|
$sort = $request->input('sort', 'setting_id');
|
||||||
|
$sort_type = $request->input('sort_type', 'asc');
|
||||||
|
|
||||||
|
// 許可されたカラムのみソート(安全対策)
|
||||||
|
$allowedSorts = ['setting_id', 'setting_key', 'setting_value']; // ← 必要に応じて増やす
|
||||||
|
|
||||||
|
if (!in_array($sort, $allowedSorts)) {
|
||||||
|
$sort = 'setting_id';
|
||||||
|
}
|
||||||
|
if (!in_array($sort_type, ['asc', 'desc'])) {
|
||||||
|
$sort_type = 'desc';
|
||||||
|
}
|
||||||
|
|
||||||
|
$list = Setting::orderBy($sort, $sort_type)->paginate($perPage);
|
||||||
|
|
||||||
return view('admin.settings.list', [
|
return view('admin.settings.list', [
|
||||||
'list' => $list,
|
'list' => $list,
|
||||||
'sort' => 'setting_id',
|
'sort' => $sort,
|
||||||
'sort_type' => 'desc',
|
'sort_type' => $sort_type,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 追加(GET: 画面 / POST: 登録): /settings/add
|
* 新規追加(GET/POST): /settings/add
|
||||||
*/
|
*/
|
||||||
public function add(Request $request)
|
public function add(Request $request)
|
||||||
{
|
{
|
||||||
if ($request->isMethod('post')) {
|
if ($request->isMethod('post')) {
|
||||||
$v = Validator::make($request->all(), $this->rules());
|
$v = Validator::make($request->all(), $this->rules());
|
||||||
if ($v->fails()) return back()->withErrors($v)->withInput();
|
if ($v->fails()) {
|
||||||
|
return back()->withErrors($v)->withInput();
|
||||||
|
}
|
||||||
|
|
||||||
// チェックボックス(未送信時は 0)
|
// チェックボックス(未送信時は false)
|
||||||
$data = $this->onlyFillable($request);
|
$data = $this->onlyFillable($request);
|
||||||
$data['printable_alert_flag'] = $request->boolean('printable_alert_flag');
|
$data['printable_alert_flag'] = $request->boolean('printable_alert_flag');
|
||||||
|
|
||||||
@ -45,15 +63,15 @@ class SettingController extends Controller
|
|||||||
return redirect()->route('settings')->with('success', '設定を登録しました。');
|
return redirect()->route('settings')->with('success', '設定を登録しました。');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GET時:空フォーム表示
|
||||||
return view('admin.settings.add', [
|
return view('admin.settings.add', [
|
||||||
'setting' => new Setting(), // フォーム初期化用
|
'setting' => new Setting(), // フォーム初期化用
|
||||||
'isInfo' => false,
|
|
||||||
'isEdit' => false,
|
'isEdit' => false,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 編集(GET: 画面 / POST: 更新): /settings/edit/{id}
|
* 編集(GET/POST): /settings/edit/{id}
|
||||||
*/
|
*/
|
||||||
public function edit(Request $request, int $id)
|
public function edit(Request $request, int $id)
|
||||||
{
|
{
|
||||||
@ -61,7 +79,9 @@ class SettingController extends Controller
|
|||||||
|
|
||||||
if ($request->isMethod('post')) {
|
if ($request->isMethod('post')) {
|
||||||
$v = Validator::make($request->all(), $this->rules($id));
|
$v = Validator::make($request->all(), $this->rules($id));
|
||||||
if ($v->fails()) return back()->withErrors($v)->withInput();
|
if ($v->fails()) {
|
||||||
|
return back()->withErrors($v)->withInput();
|
||||||
|
}
|
||||||
|
|
||||||
$data = $this->onlyFillable($request);
|
$data = $this->onlyFillable($request);
|
||||||
$data['printable_alert_flag'] = $request->boolean('printable_alert_flag');
|
$data['printable_alert_flag'] = $request->boolean('printable_alert_flag');
|
||||||
@ -73,13 +93,14 @@ class SettingController extends Controller
|
|||||||
return redirect()->route('settings')->with('success', '設定を更新しました。');
|
return redirect()->route('settings')->with('success', '設定を更新しました。');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GET時:編集フォーム表示
|
||||||
return view('admin.settings.edit', [
|
return view('admin.settings.edit', [
|
||||||
'setting' => $setting,
|
'setting' => $setting,
|
||||||
'isInfo' => false,
|
|
||||||
'isEdit' => true,
|
'isEdit' => true,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 詳細表示: /settings/info/{id}
|
* 詳細表示: /settings/info/{id}
|
||||||
*/
|
*/
|
||||||
@ -95,25 +116,30 @@ class SettingController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 削除(単体/複数対応): /settings/delete
|
* 削除(一覧/編集 共通対応): /settings/delete または /settings/delete/{id}
|
||||||
*/
|
*/
|
||||||
public function delete(Request $request)
|
public function delete(Request $request, $id = null)
|
||||||
{
|
{
|
||||||
|
// 一覧画面(checkbox で複数削除)
|
||||||
$ids = $request->input('ids');
|
$ids = $request->input('ids');
|
||||||
$id = $request->input('id');
|
|
||||||
if ($id) $ids = [$id];
|
|
||||||
|
|
||||||
if (!is_array($ids) || empty($ids)) {
|
// 編集画面(単体削除)
|
||||||
return back()->with('error', '削除対象が指定されていません。');
|
if ($id) {
|
||||||
|
$ids = [$id];
|
||||||
}
|
}
|
||||||
|
|
||||||
DB::transaction(function () use ($ids) {
|
// 削除対象が空
|
||||||
Setting::whereIn('setting_id', $ids)->delete();
|
if (empty($ids)) {
|
||||||
});
|
return redirect()->route('settings')->with('error', '削除対象が選択されていません。');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 削除処理
|
||||||
|
Setting::destroy($ids);
|
||||||
|
|
||||||
return redirect()->route('settings')->with('success', '設定を削除しました。');
|
return redirect()->route('settings')->with('success', '設定を削除しました。');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ===== バリデーション・ユーティリティ =====
|
// ===== バリデーション・ユーティリティ =====
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -126,7 +152,7 @@ class SettingController extends Controller
|
|||||||
'web_master' => ['nullable','string','max:255'],
|
'web_master' => ['nullable','string','max:255'],
|
||||||
'auto_change_date' => ['nullable','date'],
|
'auto_change_date' => ['nullable','date'],
|
||||||
'auto_chage_master' => ['nullable','string','max:255'], // ※カラム名は仕様通り
|
'auto_chage_master' => ['nullable','string','max:255'], // ※カラム名は仕様通り
|
||||||
're_issue_alert_number' => ['nullable','integer','min:0'],
|
're-issue_alert_number' => ['nullable','integer','min:0'],
|
||||||
'image_base_url1' => ['nullable','string','max:255'],
|
'image_base_url1' => ['nullable','string','max:255'],
|
||||||
'image_base_url2' => ['nullable','string','max:255'],
|
'image_base_url2' => ['nullable','string','max:255'],
|
||||||
'printable_alert_flag' => ['nullable','boolean'],
|
'printable_alert_flag' => ['nullable','boolean'],
|
||||||
@ -147,7 +173,7 @@ class SettingController extends Controller
|
|||||||
'web_master',
|
'web_master',
|
||||||
'auto_change_date',
|
'auto_change_date',
|
||||||
'auto_chage_master',
|
'auto_chage_master',
|
||||||
're_issue_alert_number',
|
're-issue_alert_number',
|
||||||
'image_base_url1',
|
'image_base_url1',
|
||||||
'image_base_url2',
|
'image_base_url2',
|
||||||
'printable_number',
|
'printable_number',
|
||||||
|
|||||||
@ -16,9 +16,14 @@ class SettlementTransactionController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function list(Request $request)
|
public function list(Request $request)
|
||||||
{
|
{
|
||||||
|
// 解除ボタンが押された場合 → 一覧にリダイレクトして検索条件リセット
|
||||||
|
if ($request->input('action') === 'unlink') {
|
||||||
|
return redirect()->route('settlement_transactions');
|
||||||
|
}
|
||||||
|
|
||||||
$q = SettlementTransaction::query();
|
$q = SettlementTransaction::query();
|
||||||
|
|
||||||
// --- 絞り込み(必要なら増やせます)
|
// --- 絞り込み
|
||||||
$contractId = $request->input('contract_id');
|
$contractId = $request->input('contract_id');
|
||||||
$status = trim((string)$request->input('status', ''));
|
$status = trim((string)$request->input('status', ''));
|
||||||
$from = $request->input('from'); // 支払日時 from
|
$from = $request->input('from'); // 支払日時 from
|
||||||
@ -59,6 +64,7 @@ class SettlementTransactionController extends Controller
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 新規
|
* 新規
|
||||||
* ルート: settlement_transactions_add
|
* ルート: settlement_transactions_add
|
||||||
|
|||||||
229
app/Http/Controllers/Admin/StationController.php
Normal file
229
app/Http/Controllers/Admin/StationController.php
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use App\Models\Station;
|
||||||
|
use Illuminate\Support\Facades\Validator;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use App\Models\Park;
|
||||||
|
|
||||||
|
|
||||||
|
class StationController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 一覧表示
|
||||||
|
*/
|
||||||
|
public function list(Request $request)
|
||||||
|
{
|
||||||
|
$sort = $request->input('sort', 'station_id');
|
||||||
|
$sort_type = $request->input('sort_type', 'asc');
|
||||||
|
|
||||||
|
$allowedSorts = [
|
||||||
|
'station_id',
|
||||||
|
'park_id',
|
||||||
|
'station_neighbor_station',
|
||||||
|
'station_name_ruby',
|
||||||
|
'station_route_name'
|
||||||
|
];
|
||||||
|
if (!in_array($sort, $allowedSorts)) {
|
||||||
|
$sort = 'station_id';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!in_array($sort_type, ['asc', 'desc'])) {
|
||||||
|
$sort_type = 'asc';
|
||||||
|
}
|
||||||
|
|
||||||
|
$list = Station::select([
|
||||||
|
'station_id',
|
||||||
|
'station_neighbor_station',
|
||||||
|
'station_name_ruby',
|
||||||
|
'station_route_name',
|
||||||
|
'park_id',
|
||||||
|
'operator_id',
|
||||||
|
'station_latitude',
|
||||||
|
'station_longitude',
|
||||||
|
])
|
||||||
|
->orderBy($sort, $sort_type)
|
||||||
|
->paginate(20);
|
||||||
|
|
||||||
|
return view('admin.stations.list', compact('list', 'sort', 'sort_type'));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function add(Request $request)
|
||||||
|
{
|
||||||
|
if ($request->isMethod('get')) {
|
||||||
|
// 駐車場リストを取得(プルダウン用)
|
||||||
|
$parks = Park::orderBy('park_name')->pluck('park_name', 'park_id');
|
||||||
|
|
||||||
|
// 新規時:空レコードを渡す
|
||||||
|
return view('admin.stations.add', [
|
||||||
|
'isEdit' => false,
|
||||||
|
'record' => new Station(),
|
||||||
|
'parks' => $parks, // ← これを追加
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST時:バリデーション
|
||||||
|
$rules = [
|
||||||
|
'station_neighbor_station' => 'required|string|max:255',
|
||||||
|
'station_name_ruby' => 'required|string|max:255',
|
||||||
|
'station_route_name' => 'required|string|max:255',
|
||||||
|
'station_latitude' => 'required|numeric',
|
||||||
|
'station_longitude' => 'required|numeric',
|
||||||
|
'operator_id' => 'nullable|integer',
|
||||||
|
'park_id' => 'required|integer',
|
||||||
|
];
|
||||||
|
|
||||||
|
$messages = [
|
||||||
|
'station_latitude.required' => '緯度は必須項目です。',
|
||||||
|
'station_longitude.required' => '経度は必須項目です。',
|
||||||
|
];
|
||||||
|
|
||||||
|
$validator = Validator::make($request->all(), $rules, $messages);
|
||||||
|
|
||||||
|
if ($validator->fails()) {
|
||||||
|
return redirect()->back()->withErrors($validator)->withInput();
|
||||||
|
}
|
||||||
|
|
||||||
|
DB::transaction(function () use ($request) {
|
||||||
|
Station::create($request->only([
|
||||||
|
'station_neighbor_station',
|
||||||
|
'station_name_ruby',
|
||||||
|
'station_route_name',
|
||||||
|
'station_latitude',
|
||||||
|
'station_longitude',
|
||||||
|
'park_id',
|
||||||
|
'operator_id',
|
||||||
|
]));
|
||||||
|
});
|
||||||
|
|
||||||
|
return redirect()->route('stations')->with('success', '登録しました。');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 編集(画面/処理)
|
||||||
|
*/
|
||||||
|
public function edit(Request $request, $id)
|
||||||
|
{
|
||||||
|
$record = Station::findOrFail($id);
|
||||||
|
|
||||||
|
if ($request->isMethod('get')) {
|
||||||
|
// 駐車場リストを取得(プルダウン用)
|
||||||
|
$parks = Park::orderBy('park_name')->pluck('park_name', 'park_id');
|
||||||
|
|
||||||
|
return view('admin.stations.edit', [
|
||||||
|
'isEdit' => true,
|
||||||
|
'record' => $record,
|
||||||
|
'parks' => $parks, // ← ここを追加
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ▼ POST時:バリデーション
|
||||||
|
$rules = [
|
||||||
|
'station_neighbor_station' => 'required|string|max:255',
|
||||||
|
'station_name_ruby' => 'required|string|max:255',
|
||||||
|
'station_route_name' => 'required|string|max:255',
|
||||||
|
'station_latitude' => 'required|numeric',
|
||||||
|
'station_longitude' => 'required|numeric',
|
||||||
|
'operator_id' => 'nullable|integer',
|
||||||
|
'park_id' => 'required|integer',
|
||||||
|
];
|
||||||
|
|
||||||
|
$messages = [
|
||||||
|
'station_latitude.required' => '緯度は必須項目です。',
|
||||||
|
'station_longitude.required' => '経度は必須項目です。',
|
||||||
|
];
|
||||||
|
|
||||||
|
$validator = Validator::make($request->all(), $rules, $messages);
|
||||||
|
|
||||||
|
if ($validator->fails()) {
|
||||||
|
return redirect()->back()->withErrors($validator)->withInput();
|
||||||
|
}
|
||||||
|
|
||||||
|
DB::transaction(function () use ($request, $record) {
|
||||||
|
$record->update($request->only([
|
||||||
|
'station_neighbor_station',
|
||||||
|
'station_name_ruby',
|
||||||
|
'station_route_name',
|
||||||
|
'park_id',
|
||||||
|
'operator_id',
|
||||||
|
'station_latitude',
|
||||||
|
'station_longitude',
|
||||||
|
]));
|
||||||
|
});
|
||||||
|
|
||||||
|
return redirect()->route('stations')->with('success', '更新しました。');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 削除(単一/複数対応)
|
||||||
|
*/
|
||||||
|
public function delete(Request $request)
|
||||||
|
{
|
||||||
|
$ids = [];
|
||||||
|
|
||||||
|
if ($request->filled('id')) {
|
||||||
|
$ids[] = (int) $request->input('id');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_array($request->input('pk'))) {
|
||||||
|
$ids = array_merge($ids, $request->input('pk'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$ids = array_values(array_unique(array_map('intval', $ids)));
|
||||||
|
|
||||||
|
if (empty($ids)) {
|
||||||
|
return back()->with('error', '削除対象が選択されていません。');
|
||||||
|
}
|
||||||
|
|
||||||
|
Station::whereIn('station_id', $ids)->delete();
|
||||||
|
|
||||||
|
return redirect()->route('stations')->with('success', '削除しました');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CSVインポート(仮)
|
||||||
|
*/
|
||||||
|
public function import(Request $request)
|
||||||
|
{
|
||||||
|
// TODO: 実装予定
|
||||||
|
return redirect()->route('stations')->with('info', 'CSVインポートは未実装です');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CSVエクスポート(日本語ヘッダー付き)
|
||||||
|
*/
|
||||||
|
public function export()
|
||||||
|
{
|
||||||
|
// ファイル名
|
||||||
|
$filename = '近傍駅マスタ_' . now()->format('YmdHis') . '.csv';
|
||||||
|
|
||||||
|
return response()->streamDownload(function () {
|
||||||
|
// Excel用 UTF-8 BOM
|
||||||
|
echo "\xEF\xBB\xBF";
|
||||||
|
|
||||||
|
// 日本語ヘッダー行
|
||||||
|
echo "近傍駅ID,駐車場ID,近傍駅,近傍駅ふりがな,路線名,近傍駅座標(緯度),近傍駅座標(経度)\n";
|
||||||
|
|
||||||
|
// データ行
|
||||||
|
foreach (\App\Models\Station::all() as $station) {
|
||||||
|
echo implode(',', [
|
||||||
|
$station->station_id,
|
||||||
|
$station->park_id,
|
||||||
|
$station->station_neighbor_station,
|
||||||
|
$station->station_name_ruby,
|
||||||
|
$station->station_route_name,
|
||||||
|
$station->station_latitude,
|
||||||
|
$station->station_longitude,
|
||||||
|
// $station->operator_id,
|
||||||
|
]) . "\n";
|
||||||
|
}
|
||||||
|
}, $filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
187
app/Http/Controllers/Admin/TagissueController.php
Normal file
187
app/Http/Controllers/Admin/TagissueController.php
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Barryvdh\DomPDF\Facade\Pdf;
|
||||||
|
|
||||||
|
class TagissueController extends Controller
|
||||||
|
{
|
||||||
|
// タグ発送宛名PDF生成
|
||||||
|
public function printUnissuedLabels(Request $request)
|
||||||
|
{
|
||||||
|
$ids = $request->input('ids', []);
|
||||||
|
if (empty($ids) || !is_array($ids)) {
|
||||||
|
return back()->with('error', '1件以上選択してください。');
|
||||||
|
}
|
||||||
|
// 利用者情報取得
|
||||||
|
$users = DB::table('user')
|
||||||
|
->whereIn('user_id', $ids)
|
||||||
|
->select('user_name', 'user_regident_zip', 'user_regident_pre')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
// PDF生成(Laravel-dompdf使用例)
|
||||||
|
$pdfHtml = view('admin.tag_issue.pdf_labels', ['users' => $users])->render();
|
||||||
|
$pdf = Pdf::setOptions(['isRemoteEnabled' => true])->loadHTML($pdfHtml);
|
||||||
|
return $pdf->download('tag_labels.pdf');
|
||||||
|
}
|
||||||
|
public function list(Request $request)
|
||||||
|
{
|
||||||
|
// 絞り込み条件
|
||||||
|
$filterType = $request->input('filter_type'); // 'unissued', 'issued', 'all', 'unissued_toggle'
|
||||||
|
$tagSerial = $request->input('tag_serial');
|
||||||
|
$tagSerial64 = $request->input('tag_serial_64');
|
||||||
|
|
||||||
|
// ソートパラメータ取得
|
||||||
|
$sort = $request->input('sort');
|
||||||
|
$sortType = $request->input('sort_type', 'asc');
|
||||||
|
|
||||||
|
// userテーブルとoperator_queテーブルをJOIN
|
||||||
|
$query = DB::table('user')
|
||||||
|
->leftJoin('operator_que', 'user.user_id', '=', 'operator_que.user_id')
|
||||||
|
->select(
|
||||||
|
'user.user_seq',
|
||||||
|
'user.user_id',
|
||||||
|
'user.user_tag_serial',
|
||||||
|
'user.user_tag_serial_64',
|
||||||
|
'user.user_tag_issue',
|
||||||
|
'user.user_name',
|
||||||
|
'user.user_mobile',
|
||||||
|
'user.user_homephone',
|
||||||
|
'user.user_regident_zip',
|
||||||
|
'user.user_regident_pre',
|
||||||
|
'user.user_regident_city',
|
||||||
|
'user.user_regident_add',
|
||||||
|
'user.user_relate_zip',
|
||||||
|
'user.user_relate_pre',
|
||||||
|
'user.user_relate_city',
|
||||||
|
'user.user_relate_add',
|
||||||
|
'operator_que.que_id',
|
||||||
|
'operator_que.que_class',
|
||||||
|
'operator_que.que_status'
|
||||||
|
);
|
||||||
|
|
||||||
|
// ソート項目に応じたorderBy
|
||||||
|
switch ($sort) {
|
||||||
|
case 'que_id':
|
||||||
|
$query->orderBy('operator_que.que_id', $sortType);
|
||||||
|
break;
|
||||||
|
case 'user_tag_serial':
|
||||||
|
$query->orderBy('user.user_tag_serial', $sortType);
|
||||||
|
break;
|
||||||
|
case 'user_tag_serial_64':
|
||||||
|
$query->orderBy('user.user_tag_serial_64', $sortType);
|
||||||
|
break;
|
||||||
|
case 'user_tag_issue':
|
||||||
|
$query->orderBy('user.user_tag_issue', $sortType);
|
||||||
|
break;
|
||||||
|
case 'user_name':
|
||||||
|
$query->orderBy('user.user_name', $sortType);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$query->orderByDesc('user.user_seq');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 【種別フィルター】タグ未発送(通常)
|
||||||
|
if ($filterType === 'unissued') {
|
||||||
|
$query->where('operator_que.que_class', 3)
|
||||||
|
->where('operator_que.que_status', 1);
|
||||||
|
}
|
||||||
|
// 【種別フィルター】タグ未発送トグル(que_class=3かつque_status≠3)
|
||||||
|
if ($filterType === 'unissued_toggle') {
|
||||||
|
$query->where('operator_que.que_class', 3)
|
||||||
|
->where('operator_que.que_status', '<>', 3);
|
||||||
|
}
|
||||||
|
// 【種別フィルター】タグ発送済み(通常)
|
||||||
|
if ($filterType === 'issued') {
|
||||||
|
$query->where('operator_que.que_class', 3)
|
||||||
|
->where('operator_que.que_status', 3);
|
||||||
|
}
|
||||||
|
// 【種別フィルター】タグ発送済みトグル(que_class=3 and que_status=3)
|
||||||
|
if ($filterType === 'issued_toggle') {
|
||||||
|
$query->where('operator_que.que_class', 3)
|
||||||
|
->where('operator_que.que_status', 3);
|
||||||
|
}
|
||||||
|
// 【タグシリアル・タグシリアル64進フィルター】
|
||||||
|
if (!empty($tagSerial)) {
|
||||||
|
$query->where('user.user_tag_serial', 'like', "%$tagSerial%");
|
||||||
|
}
|
||||||
|
if (!empty($tagSerial64)) {
|
||||||
|
$query->where('user.user_tag_serial_64', 'like', "%$tagSerial64%");
|
||||||
|
}
|
||||||
|
|
||||||
|
$users = $query->paginate(20);
|
||||||
|
|
||||||
|
return view('admin.tag_issue.list', [
|
||||||
|
'users' => $users,
|
||||||
|
'filterType' => $filterType,
|
||||||
|
'tagSerial' => $tagSerial,
|
||||||
|
'tagSerial64' => $tagSerial64,
|
||||||
|
'sort' => $sort,
|
||||||
|
'sortType' => $sortType,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ステータス変更(タグ発送済み/未発送)
|
||||||
|
public function updateStatus(Request $request)
|
||||||
|
{
|
||||||
|
$ids = $request->input('ids', []); // チェックされたuser_id配列
|
||||||
|
$action = $request->input('action'); // 'to_issued' or 'to_unissued'
|
||||||
|
|
||||||
|
$operatorId = auth()->id();
|
||||||
|
$now = now();
|
||||||
|
// 対象ユーザーのoperator_queを取得
|
||||||
|
$ques = DB::table('operator_que')->whereIn('user_id', $ids)->get();
|
||||||
|
if ($action === 'to_issued') {
|
||||||
|
$alreadyIssued = $ques->where('que_status', 3)->pluck('user_id')->toArray();
|
||||||
|
if (count($alreadyIssued) > 0) {
|
||||||
|
return back()->with('error', 'すでにタグ発送済みのユーザーが含まれています。');
|
||||||
|
}
|
||||||
|
// 確認ダイアログはJS側で
|
||||||
|
DB::table('operator_que')->whereIn('user_id', $ids)
|
||||||
|
->update(['que_status' => 3, 'updated_at' => $now, 'operator_id' => $operatorId]);
|
||||||
|
return back()->with('success', 'ステータスをタグ発送済に変更しました。');
|
||||||
|
}
|
||||||
|
if ($action === 'to_unissued') {
|
||||||
|
$alreadyUnissued = $ques->where('que_status', 1)->pluck('user_id')->toArray();
|
||||||
|
if (count($alreadyUnissued) > 0) {
|
||||||
|
// 既にタグ未発送のユーザー名を取得
|
||||||
|
$names = DB::table('user')->whereIn('user_id', $alreadyUnissued)->pluck('user_name')->toArray();
|
||||||
|
return back()->with('error', 'すでにタグ未発送のユーザーが含まれています: ' . implode(', ', $names));
|
||||||
|
}
|
||||||
|
// すべてque_status=1以外なので更新
|
||||||
|
DB::table('operator_que')->whereIn('user_id', $ids)
|
||||||
|
->update(['que_status' => 1, 'updated_at' => $now, 'operator_id' => $operatorId]);
|
||||||
|
return back()->with('success', 'ステータスをタグ未発送に変更しました。');
|
||||||
|
}
|
||||||
|
return back()->with('error', '不正な操作です。');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ajax: 選択user_idのque_statusを返す(タグ発送済み/未発送判定)
|
||||||
|
public function checkStatus(Request $request)
|
||||||
|
{
|
||||||
|
$ids = $request->input('ids', []);
|
||||||
|
$type = $request->input('type'); // 'issued' or 'unissued'
|
||||||
|
$users = DB::table('user')
|
||||||
|
->leftJoin('operator_que', 'user.user_id', '=', 'operator_que.user_id')
|
||||||
|
->whereIn('user.user_id', $ids)
|
||||||
|
->select('user.user_name', 'operator_que.que_status')
|
||||||
|
->get();
|
||||||
|
$alreadyIssued = [];
|
||||||
|
$alreadyUnissued = [];
|
||||||
|
foreach ($users as $u) {
|
||||||
|
if ($type === 'issued' && $u->que_status == 3) {
|
||||||
|
$alreadyIssued[] = $u->user_name;
|
||||||
|
}
|
||||||
|
if ($type === 'unissued' && $u->que_status == 1) {
|
||||||
|
$alreadyUnissued[] = $u->user_name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return response()->json([
|
||||||
|
'alreadyIssued' => $alreadyIssued,
|
||||||
|
'alreadyUnissued' => $alreadyUnissued
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -21,7 +21,6 @@ class TaxController extends Controller
|
|||||||
// 絞り込み
|
// 絞り込み
|
||||||
$keyword = trim((string) $request->input('kw'));
|
$keyword = trim((string) $request->input('kw'));
|
||||||
if ($keyword !== '') {
|
if ($keyword !== '') {
|
||||||
// 数値型でも互換のため部分一致を残す
|
|
||||||
$query->where('tax_percent', 'like', "%{$keyword}%");
|
$query->where('tax_percent', 'like', "%{$keyword}%");
|
||||||
}
|
}
|
||||||
$from = $request->input('from');
|
$from = $request->input('from');
|
||||||
@ -33,15 +32,15 @@ class TaxController extends Controller
|
|||||||
$query->whereDate('tax_day', '<=', $to);
|
$query->whereDate('tax_day', '<=', $to);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ソート(既定:適用日 降順)
|
// ソート(既定:ID 昇順)
|
||||||
$sort = $request->input('sort', 'tax_day');
|
$sort = $request->input('sort', 'tax_id');
|
||||||
$type = strtolower($request->input('sort_type', 'desc'));
|
$type = strtolower($request->input('sort_type', 'asc'));
|
||||||
$allow = ['tax_day', 'tax_percent', 'updated_at', 'created_at', 'tax_id'];
|
$allow = ['tax_day', 'tax_percent', 'updated_at', 'created_at', 'tax_id'];
|
||||||
if (!in_array($sort, $allow, true)) {
|
if (!in_array($sort, $allow, true)) {
|
||||||
$sort = 'tax_day';
|
$sort = 'tax_id';
|
||||||
}
|
}
|
||||||
if (!in_array($type, ['asc', 'desc'], true)) {
|
if (!in_array($type, ['asc', 'desc'], true)) {
|
||||||
$type = 'desc';
|
$type = 'asc';
|
||||||
}
|
}
|
||||||
$query->orderBy($sort, $type);
|
$query->orderBy($sort, $type);
|
||||||
|
|
||||||
@ -57,60 +56,61 @@ class TaxController extends Controller
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function add(Request $request)
|
public function add(Request $request)
|
||||||
{
|
{
|
||||||
if ($request->isMethod('post')) {
|
if ($request->isMethod('post')) {
|
||||||
$data = $request->validate([
|
$data = $request->validate([
|
||||||
'tax_percent' => ['required', 'numeric', 'min:0', 'max:1000'],
|
'tax_percent' => ['required', 'numeric', 'min:0', 'max:1000'],
|
||||||
'tax_day' => ['required', 'date', 'unique:tax,tax_day'],
|
'tax_day' => ['required', 'date'],
|
||||||
]);
|
]);
|
||||||
$data['operator_id'] = optional(\Auth::user())->ope_id ?? null;
|
$data['operator_id'] = optional(\Auth::user())->ope_id ?? null;
|
||||||
$data['tax_percent'] = number_format((float)$data['tax_percent'], 2, '.', '');
|
$data['tax_percent'] = number_format((float)$data['tax_percent'], 2, '.', '');
|
||||||
\App\Models\Tax::create($data);
|
\App\Models\Tax::create($data);
|
||||||
|
|
||||||
return redirect()->route('tax')->with('success', '登録しました');
|
return redirect()->route('tax')->with('success', '登録しました。');
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('admin.tax.add', [
|
||||||
|
'tax' => null,
|
||||||
|
'isEdit' => false,
|
||||||
|
'isInfo' => false,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return view('admin.tax.add', [
|
public function edit(int $tax_id, Request $request)
|
||||||
'tax' => null,
|
{
|
||||||
'isEdit' => false,
|
$tax = \App\Models\Tax::findOrFail($tax_id);
|
||||||
'isInfo' => false,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function edit(int $tax_id, Request $request)
|
if ($request->isMethod('post')) {
|
||||||
{
|
$data = $request->validate([
|
||||||
$tax = \App\Models\Tax::findOrFail($tax_id);
|
'tax_percent' => ['required', 'numeric', 'min:0', 'max:1000'],
|
||||||
|
'tax_day' => ['required', 'date'],
|
||||||
|
]);
|
||||||
|
$data['operator_id'] = optional(\Auth::user())->ope_id ?? null;
|
||||||
|
$data['tax_percent'] = number_format((float)$data['tax_percent'], 2, '.', '');
|
||||||
|
$tax->update($data);
|
||||||
|
|
||||||
if ($request->isMethod('post')) {
|
return redirect()->route('tax')->with('success', '更新しました。');
|
||||||
$data = $request->validate([
|
}
|
||||||
'tax_percent' => ['required', 'numeric', 'min:0', 'max:1000'],
|
|
||||||
'tax_day' => ['required', 'date', 'unique:tax,tax_day,' . $tax->tax_id . ',tax_id'],
|
return view('admin.tax.edit', [
|
||||||
|
'tax' => $tax,
|
||||||
|
'isEdit' => true,
|
||||||
|
'isInfo' => false,
|
||||||
]);
|
]);
|
||||||
$data['operator_id'] = optional(\Auth::user())->ope_id ?? null;
|
|
||||||
$data['tax_percent'] = number_format((float)$data['tax_percent'], 2, '.', '');
|
|
||||||
$tax->update($data);
|
|
||||||
|
|
||||||
return redirect()->route('tax')->with('success', '更新しました');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return view('admin.tax.edit', [
|
public function info(int $tax_id)
|
||||||
'tax' => $tax,
|
{
|
||||||
'isEdit' => true,
|
$tax = \App\Models\Tax::findOrFail($tax_id);
|
||||||
'isInfo' => false,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function info(int $tax_id)
|
return view('admin.tax.info', [
|
||||||
{
|
'tax' => $tax,
|
||||||
$tax = \App\Models\Tax::findOrFail($tax_id);
|
'isEdit' => false,
|
||||||
|
'isInfo' => true,
|
||||||
return view('admin.tax.info', [
|
]);
|
||||||
'tax' => $tax,
|
}
|
||||||
'isEdit' => false,
|
|
||||||
'isInfo' => true,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -119,166 +119,175 @@ public function info(int $tax_id)
|
|||||||
*/
|
*/
|
||||||
public function delete(Request $request)
|
public function delete(Request $request)
|
||||||
{
|
{
|
||||||
$ids = (array) $request->input('ids', []);
|
|
||||||
|
$pk = $request->input('pk', []);
|
||||||
|
|
||||||
|
// 配列に統一
|
||||||
|
$ids = is_array($pk) ? $pk : [$pk];
|
||||||
|
|
||||||
|
// 数字チェック
|
||||||
$ids = array_values(array_filter($ids, fn($v) => preg_match('/^\d+$/', (string) $v)));
|
$ids = array_values(array_filter($ids, fn($v) => preg_match('/^\d+$/', (string) $v)));
|
||||||
|
|
||||||
if (empty($ids)) {
|
if (empty($ids)) {
|
||||||
return redirect()->route('tax')->with('error', '削除対象が選択されていません。');
|
return redirect()->route('tax')->with('error', '削除対象が選択されていません。');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 削除
|
||||||
Tax::whereIn('tax_id', $ids)->delete();
|
Tax::whereIn('tax_id', $ids)->delete();
|
||||||
|
|
||||||
return redirect()->route('tax')->with('success', '削除しました');
|
return redirect()->route('tax')->with('success', '削除しました。');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* CSVインポート
|
|
||||||
* カラム想定: tax_percent, tax_day
|
|
||||||
* - 1行目はヘッダ可
|
|
||||||
* - tax_day をキーとして「存在すれば更新 / 無ければ作成」
|
|
||||||
*/
|
|
||||||
public function import(Request $request)
|
|
||||||
{
|
|
||||||
$request->validate([
|
|
||||||
'file' => ['required', 'file', 'mimetypes:text/plain,text/csv,text/tsv', 'max:2048'],
|
|
||||||
]);
|
|
||||||
|
|
||||||
$path = $request->file('file')->getRealPath();
|
|
||||||
if (!$path || !is_readable($path)) {
|
|
||||||
return redirect()->route('tax')->with('error', 'ファイルを読み込めません。');
|
|
||||||
}
|
|
||||||
|
|
||||||
$created = 0;
|
// /**
|
||||||
$updated = 0;
|
// * CSVインポート
|
||||||
$skipped = 0;
|
// * カラム想定: tax_percent, tax_day
|
||||||
|
// * - 1行目はヘッダ可
|
||||||
|
// * - tax_day をキーとして「存在すれば更新 / 無ければ作成」
|
||||||
|
// */
|
||||||
|
// public function import(Request $request)
|
||||||
|
// {
|
||||||
|
// $request->validate([
|
||||||
|
// 'file' => ['required', 'file', 'mimetypes:text/plain,text/csv,text/tsv', 'max:2048'],
|
||||||
|
// ]);
|
||||||
|
|
||||||
DB::beginTransaction();
|
// $path = $request->file('file')->getRealPath();
|
||||||
try {
|
// if (!$path || !is_readable($path)) {
|
||||||
if (($fp = fopen($path, 'r')) !== false) {
|
// return redirect()->route('tax')->with('error', 'ファイルを読み込めません。');
|
||||||
$line = 0;
|
// }
|
||||||
while (($row = fgetcsv($fp)) !== false) {
|
|
||||||
$line++;
|
|
||||||
|
|
||||||
// 空行スキップ
|
// $created = 0;
|
||||||
if (count($row) === 1 && trim((string) $row[0]) === '') {
|
// $updated = 0;
|
||||||
continue;
|
// $skipped = 0;
|
||||||
}
|
|
||||||
|
|
||||||
// ヘッダ行っぽい場合(1行目に 'tax_percent' を含む)
|
// DB::beginTransaction();
|
||||||
if ($line === 1) {
|
// try {
|
||||||
$joined = strtolower(implode(',', $row));
|
// if (($fp = fopen($path, 'r')) !== false) {
|
||||||
if (str_contains($joined, 'tax_percent') && str_contains($joined, 'tax_day')) {
|
// $line = 0;
|
||||||
continue; // ヘッダスキップ
|
// while (($row = fgetcsv($fp)) !== false) {
|
||||||
}
|
// $line++;
|
||||||
}
|
|
||||||
|
|
||||||
// 取り出し(列数が足りない場合スキップ)
|
// // 空行スキップ
|
||||||
$percent = $row[0] ?? null;
|
// if (count($row) === 1 && trim((string) $row[0]) === '') {
|
||||||
$day = $row[1] ?? null;
|
// continue;
|
||||||
if ($percent === null || $day === null) {
|
// }
|
||||||
$skipped++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 正規化 & 検証
|
// // ヘッダ行っぽい場合(1行目に 'tax_percent' を含む)
|
||||||
$percent = trim((string) $percent);
|
// if ($line === 1) {
|
||||||
$percent = rtrim($percent, '%');
|
// $joined = strtolower(implode(',', $row));
|
||||||
$percent = preg_replace('/[^\d.]/', '', $percent) ?? '0';
|
// if (str_contains($joined, 'tax_percent') && str_contains($joined, 'tax_day')) {
|
||||||
$percentF = (float) $percent;
|
// continue; // ヘッダスキップ
|
||||||
if ($percentF < 0) {
|
// }
|
||||||
$skipped++;
|
// }
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$percentF = (float) number_format($percentF, 2, '.', '');
|
|
||||||
|
|
||||||
$day = date('Y-m-d', strtotime((string) $day));
|
// // 取り出し(列数が足りない場合スキップ)
|
||||||
if (!$day) {
|
// $percent = $row[0] ?? null;
|
||||||
$skipped++;
|
// $day = $row[1] ?? null;
|
||||||
continue;
|
// if ($percent === null || $day === null) {
|
||||||
}
|
// $skipped++;
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
|
||||||
// upsert: 適用日ユニーク運用
|
// // 正規化 & 検証
|
||||||
$existing = Tax::whereDate('tax_day', $day)->first();
|
// $percent = trim((string) $percent);
|
||||||
$payload = [
|
// $percent = rtrim($percent, '%');
|
||||||
'tax_percent' => $percentF,
|
// $percent = preg_replace('/[^\d.]/', '', $percent) ?? '0';
|
||||||
'tax_day' => $day,
|
// $percentF = (float) $percent;
|
||||||
'operator_id' => optional(Auth::user())->ope_id ?? null,
|
// if ($percentF < 0) {
|
||||||
];
|
// $skipped++;
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
// $percentF = (float) number_format($percentF, 2, '.', '');
|
||||||
|
|
||||||
if ($existing) {
|
// $day = date('Y-m-d', strtotime((string) $day));
|
||||||
$existing->update($payload);
|
// if (!$day) {
|
||||||
$updated++;
|
// $skipped++;
|
||||||
} else {
|
// continue;
|
||||||
Tax::create($payload);
|
// }
|
||||||
$created++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fclose($fp);
|
|
||||||
}
|
|
||||||
|
|
||||||
DB::commit();
|
// // upsert: 適用日ユニーク運用
|
||||||
return redirect()->route('tax')->with('success', "インポート完了:新規 {$created} 件、更新 {$updated} 件、スキップ {$skipped} 件");
|
// $existing = Tax::whereDate('tax_day', $day)->first();
|
||||||
} catch (\Throwable $e) {
|
// $payload = [
|
||||||
DB::rollBack();
|
// 'tax_percent' => $percentF,
|
||||||
return redirect()->route('tax')->with('error', 'インポートに失敗しました:' . $e->getMessage());
|
// 'tax_day' => $day,
|
||||||
}
|
// 'operator_id' => optional(Auth::user())->ope_id ?? null,
|
||||||
}
|
// ];
|
||||||
|
|
||||||
/**
|
// if ($existing) {
|
||||||
* CSVエクスポート:現在の絞り込み/ソート条件を反映
|
// $existing->update($payload);
|
||||||
*/
|
// $updated++;
|
||||||
public function export(Request $request): StreamedResponse
|
// } else {
|
||||||
{
|
// Tax::create($payload);
|
||||||
$query = Tax::query();
|
// $created++;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// fclose($fp);
|
||||||
|
// }
|
||||||
|
|
||||||
$keyword = trim((string) $request->input('kw'));
|
// DB::commit();
|
||||||
if ($keyword !== '') {
|
// return redirect()->route('tax')->with('success', "インポート完了:新規 {$created} 件、更新 {$updated} 件、スキップ {$skipped} 件");
|
||||||
$query->where('tax_percent', 'like', "%{$keyword}%");
|
// } catch (\Throwable $e) {
|
||||||
}
|
// DB::rollBack();
|
||||||
$from = $request->input('from');
|
// return redirect()->route('tax')->with('error', 'インポートに失敗しました:' . $e->getMessage());
|
||||||
$to = $request->input('to');
|
// }
|
||||||
if ($from) {
|
// }
|
||||||
$query->whereDate('tax_day', '>=', $from);
|
|
||||||
}
|
|
||||||
if ($to) {
|
|
||||||
$query->whereDate('tax_day', '<=', $to);
|
|
||||||
}
|
|
||||||
|
|
||||||
$sort = $request->input('sort', 'tax_day');
|
// /**
|
||||||
$type = strtolower($request->input('sort_type', 'desc'));
|
// * CSVエクスポート:現在の絞り込み/ソート条件を反映
|
||||||
$allow = ['tax_day', 'tax_percent', 'updated_at', 'created_at', 'tax_id'];
|
// */
|
||||||
if (!in_array($sort, $allow, true)) {
|
// public function export(Request $request): StreamedResponse
|
||||||
$sort = 'tax_day';
|
// {
|
||||||
}
|
// $query = Tax::query();
|
||||||
if (!in_array($type, ['asc', 'desc'], true)) {
|
|
||||||
$type = 'desc';
|
|
||||||
}
|
|
||||||
$query->orderBy($sort, $type);
|
|
||||||
|
|
||||||
$filename = 'tax_' . now()->format('Ymd_His') . '.csv';
|
// $keyword = trim((string) $request->input('kw'));
|
||||||
|
// if ($keyword !== '') {
|
||||||
|
// $query->where('tax_percent', 'like', "%{$keyword}%");
|
||||||
|
// }
|
||||||
|
// $from = $request->input('from');
|
||||||
|
// $to = $request->input('to');
|
||||||
|
// if ($from) {
|
||||||
|
// $query->whereDate('tax_day', '>=', $from);
|
||||||
|
// }
|
||||||
|
// if ($to) {
|
||||||
|
// $query->whereDate('tax_day', '<=', $to);
|
||||||
|
// }
|
||||||
|
|
||||||
return response()->streamDownload(function () use ($query) {
|
// $sort = $request->input('sort', 'tax_day');
|
||||||
$out = fopen('php://output', 'w');
|
// $type = strtolower($request->input('sort_type', 'desc'));
|
||||||
// Header(設計書の主要カラム)
|
// $allow = ['tax_day', 'tax_percent', 'updated_at', 'created_at', 'tax_id'];
|
||||||
fputcsv($out, ['消費税ID', '消費税率', '適用日', '登録日時', '更新日時', '更新オペレータID']);
|
// if (!in_array($sort, $allow, true)) {
|
||||||
$query->chunk(500, function ($rows) use ($out) {
|
// $sort = 'tax_day';
|
||||||
foreach ($rows as $r) {
|
// }
|
||||||
fputcsv($out, [
|
// if (!in_array($type, ['asc', 'desc'], true)) {
|
||||||
$r->tax_id,
|
// $type = 'desc';
|
||||||
// 画面仕様に合わせたい場合は getDisplayTaxPercentAttribute() に置換可
|
// }
|
||||||
is_numeric($r->tax_percent)
|
// $query->orderBy($sort, $type);
|
||||||
? number_format((float) $r->tax_percent, 2, '.', '')
|
|
||||||
: (string) $r->tax_percent,
|
// $filename = 'tax_' . now()->format('Ymd_His') . '.csv';
|
||||||
optional($r->tax_day)->format('Y-m-d'),
|
|
||||||
optional($r->created_at)->format('Y-m-d H:i:s'),
|
// return response()->streamDownload(function () use ($query) {
|
||||||
optional($r->updated_at)->format('Y-m-d H:i:s'),
|
// $out = fopen('php://output', 'w');
|
||||||
$r->operator_id,
|
// // Header(設計書の主要カラム)
|
||||||
]);
|
// fputcsv($out, ['消費税ID', '消費税率', '適用日', '登録日時', '更新日時', '更新オペレータID']);
|
||||||
}
|
// $query->chunk(500, function ($rows) use ($out) {
|
||||||
});
|
// foreach ($rows as $r) {
|
||||||
fclose($out);
|
// fputcsv($out, [
|
||||||
}, $filename, [
|
// $r->tax_id,
|
||||||
'Content-Type' => 'text/csv; charset=UTF-8',
|
// // 画面仕様に合わせたい場合は getDisplayTaxPercentAttribute() に置換可
|
||||||
]);
|
// is_numeric($r->tax_percent)
|
||||||
}
|
// ? number_format((float) $r->tax_percent, 2, '.', '')
|
||||||
|
// : (string) $r->tax_percent,
|
||||||
|
// optional($r->tax_day)->format('Y-m-d'),
|
||||||
|
// optional($r->created_at)->format('Y-m-d H:i:s'),
|
||||||
|
// optional($r->updated_at)->format('Y-m-d H:i:s'),
|
||||||
|
// $r->operator_id,
|
||||||
|
// ]);
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// fclose($out);
|
||||||
|
// }, $filename, [
|
||||||
|
// 'Content-Type' => 'text/csv; charset=UTF-8',
|
||||||
|
// ]);
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|||||||
@ -43,17 +43,19 @@ class TermsController extends Controller
|
|||||||
{
|
{
|
||||||
if ($request->isMethod('post')) {
|
if ($request->isMethod('post')) {
|
||||||
$validated = $request->validate([
|
$validated = $request->validate([
|
||||||
|
'city_id' => 'required|integer',
|
||||||
'terms_revision' => 'required|string|max:255',
|
'terms_revision' => 'required|string|max:255',
|
||||||
'terms_text' => 'required|string',
|
'terms_text' => 'required|string',
|
||||||
'start_date' => 'nullable|date',
|
'start_date' => 'nullable|date',
|
||||||
'use_flag' => 'required|in:0,1',
|
'use_flag' => 'required|in:0,1',
|
||||||
'memo' => 'nullable|string|max:255',
|
'memo' => 'nullable|string|max:255',
|
||||||
'city_id' => 'nullable|integer',
|
'terms_created_at' => 'nullable|date',
|
||||||
|
|
||||||
'operator_id' => 'nullable|integer',
|
'operator_id' => 'nullable|integer',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
Term::create($validated);
|
Term::create($validated);
|
||||||
return redirect()->route('terms')->with('success', '利用規約が登録されました');
|
return redirect()->route('terms')->with('success', '登録しました。');
|
||||||
}
|
}
|
||||||
// 都市の選択肢を取得
|
// 都市の選択肢を取得
|
||||||
$cities = City::pluck('city_name', 'city_id');
|
$cities = City::pluck('city_name', 'city_id');
|
||||||
@ -62,7 +64,6 @@ class TermsController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 編集画面・更新処理
|
|
||||||
public function edit(Request $request, $id)
|
public function edit(Request $request, $id)
|
||||||
{
|
{
|
||||||
$term = Term::findOrFail($id);
|
$term = Term::findOrFail($id);
|
||||||
@ -70,24 +71,27 @@ class TermsController extends Controller
|
|||||||
|
|
||||||
if ($request->isMethod('post')) {
|
if ($request->isMethod('post')) {
|
||||||
$validated = $request->validate([
|
$validated = $request->validate([
|
||||||
'terms_revision' => 'required|string|max:255',
|
'city_id' => 'required|integer',
|
||||||
'terms_text' => 'required|string',
|
'terms_revision' => 'required|string|max:255',
|
||||||
'start_date' => 'nullable|date',
|
'terms_text' => 'required|string',
|
||||||
'use_flag' => 'required|in:0,1',
|
'start_date' => 'nullable|date',
|
||||||
'memo' => 'nullable|string|max:255',
|
'use_flag' => 'required|in:0,1',
|
||||||
'city_id' => 'nullable|integer',
|
'memo' => 'nullable|string|max:255',
|
||||||
'operator_id' => 'nullable|integer',
|
'terms_created_at'=> 'nullable|date',
|
||||||
|
'operator_id' => 'nullable|integer',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$term->update($validated);
|
$term->update($validated);
|
||||||
return redirect()->route('terms')->with('success', '利用規約が更新されました');
|
return redirect()->route('terms')->with('success', '更新しました。');
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return view('admin.terms.edit', compact('term', 'cities'));
|
return view('admin.terms.edit', compact('term', 'cities'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 詳細表示
|
// 詳細表示
|
||||||
public function info($id)
|
public function info($id)
|
||||||
{
|
{
|
||||||
@ -95,17 +99,24 @@ class TermsController extends Controller
|
|||||||
return view('admin.terms.info', compact('term'));
|
return view('admin.terms.info', compact('term'));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 削除処理(複数)
|
// 削除処理(単一・複数対応)
|
||||||
public function delete(Request $request)
|
public function delete(Request $request)
|
||||||
{
|
{
|
||||||
$ids = $request->input('id', []); // 修正点:'pk' → 'id'
|
$request->validate([
|
||||||
|
'pk' => 'required',
|
||||||
|
'pk.*' => 'integer', // 配列なら中身は整数
|
||||||
|
]);
|
||||||
|
|
||||||
if (!empty($ids)) {
|
$arr_pk = $request->input('pk');
|
||||||
Term::destroy($ids);
|
$ids = is_array($arr_pk) ? $arr_pk : [$arr_pk];
|
||||||
return redirect()->route('terms')->with('success', '削除しました');
|
|
||||||
|
$deleted = Term::destroy($ids);
|
||||||
|
|
||||||
|
if ($deleted > 0) {
|
||||||
|
return redirect()->route('terms')->with('success', __('削除しました。'));
|
||||||
|
} else {
|
||||||
|
return redirect()->route('terms')->with('error', __('削除に失敗しました。'));
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect()->route('terms')->with('error', '削除対象が見つかりません');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CSVインポート(仮)
|
// CSVインポート(仮)
|
||||||
|
|||||||
@ -14,32 +14,26 @@ class UpdateCandidateController extends Controller
|
|||||||
public function list(Request $request)
|
public function list(Request $request)
|
||||||
{
|
{
|
||||||
$q = DB::table('regular_contract as rc')
|
$q = DB::table('regular_contract as rc')
|
||||||
|
->leftJoin('user as u','rc.user_id','=','u.user_id')
|
||||||
|
->leftJoin('park as p','rc.park_id','=','p.park_id')
|
||||||
|
->leftJoin('psection as ps','rc.psection_id','=','ps.psection_id')
|
||||||
|
->leftJoin('usertype as ut','u.user_categoryid','=','ut.user_categoryid')
|
||||||
->select([
|
->select([
|
||||||
'rc.contract_id',
|
'rc.contract_id',
|
||||||
'rc.contract_qr_id',
|
|
||||||
'rc.user_id',
|
'rc.user_id',
|
||||||
'rc.user_categoryid',
|
|
||||||
'rc.park_id',
|
'rc.park_id',
|
||||||
'rc.contract_created_at',
|
'rc.contract_created_at',
|
||||||
'rc.contract_periods',
|
'rc.contract_periods',
|
||||||
'rc.contract_periode',
|
'rc.contract_periode',
|
||||||
'rc.tag_qr_flag',
|
'rc.tag_qr_flag',
|
||||||
'rc.contract_flag',
|
|
||||||
'rc.contract_cancel_flag',
|
'rc.contract_cancel_flag',
|
||||||
'rc.contract_payment_day',
|
|
||||||
'rc.contract_money',
|
|
||||||
'rc.billing_amount',
|
|
||||||
'rc.contract_permission',
|
'rc.contract_permission',
|
||||||
'rc.contract_manual',
|
|
||||||
'rc.contract_notice',
|
|
||||||
'p.park_name',
|
|
||||||
'u.user_name',
|
'u.user_name',
|
||||||
'u.user_phonetic',
|
'u.user_phonetic',
|
||||||
'u.user_mobile',
|
'u.user_mobile',
|
||||||
'u.user_homephone',
|
'u.user_homephone',
|
||||||
'u.user_primemail',
|
|
||||||
'u.user_gender',
|
|
||||||
'u.user_birthdate',
|
'u.user_birthdate',
|
||||||
|
'u.user_gender',
|
||||||
'u.user_regident_zip',
|
'u.user_regident_zip',
|
||||||
'u.user_regident_pre',
|
'u.user_regident_pre',
|
||||||
'u.user_regident_city',
|
'u.user_regident_city',
|
||||||
@ -48,57 +42,116 @@ class UpdateCandidateController extends Controller
|
|||||||
'u.user_relate_pre',
|
'u.user_relate_pre',
|
||||||
'u.user_relate_city',
|
'u.user_relate_city',
|
||||||
'u.user_relate_add',
|
'u.user_relate_add',
|
||||||
'u.user_graduate',
|
|
||||||
'u.user_workplace',
|
'u.user_workplace',
|
||||||
'u.user_school',
|
'u.user_school',
|
||||||
|
'u.user_graduate',
|
||||||
|
'u.user_reduction',
|
||||||
'u.user_remarks',
|
'u.user_remarks',
|
||||||
|
'p.park_name',
|
||||||
|
DB::raw('ps.psection_subject as vehicle_type'),
|
||||||
|
DB::raw('ut.usertype_subject1 as user_category1'),
|
||||||
|
DB::raw('ut.usertype_subject2 as user_category2'),
|
||||||
|
DB::raw('ut.usertype_subject3 as user_category3'),
|
||||||
|
DB::raw('rc.contract_seal_issue as seal_issue_count'),
|
||||||
|
DB::raw('rc.user_securitynum as crime_prevention'),
|
||||||
|
DB::raw("CASE rc.enable_months
|
||||||
|
WHEN 1 THEN '月極(1ヶ月)'
|
||||||
|
WHEN 3 THEN '3ヶ月'
|
||||||
|
WHEN 6 THEN '6ヶ月'
|
||||||
|
WHEN 12 THEN '年'
|
||||||
|
ELSE CONCAT(rc.enable_months,'ヶ月') END as ticket_type"),
|
||||||
])
|
])
|
||||||
->leftJoin('park as p', 'rc.park_id', '=', 'p.park_id')
|
->where('rc.contract_cancel_flag',0)
|
||||||
->leftJoin('user as u', 'rc.user_id', '=', 'u.user_id')
|
->where('rc.contract_permission',1)
|
||||||
->whereNull('rc.update_flag'); // 未更新のみ
|
// 追加: 本日以降が有効期限のレコードのみ
|
||||||
|
->whereDate('rc.contract_periode','>=', now()->toDateString());
|
||||||
|
|
||||||
// 対象月による有効期限の絞り込み
|
// 絞り込み
|
||||||
if ($request->filled('target_month')) {
|
if ($request->filled('park_id')) {
|
||||||
$now = now();
|
$q->where('rc.park_id', $request->park_id);
|
||||||
switch ($request->input('target_month')) {
|
}
|
||||||
case 'last':
|
if ($request->filled('user_id')) {
|
||||||
$start = $now->copy()->subMonth()->startOfMonth();
|
$q->where('rc.user_id', trim($request->user_id));
|
||||||
$end = $now->copy()->subMonth()->endOfMonth();
|
}
|
||||||
break;
|
// 分類名1 完全一致
|
||||||
case 'this':
|
if ($request->filled('user_category1')) {
|
||||||
$start = $now->copy()->startOfMonth();
|
$val = trim(mb_convert_kana($request->user_category1,'asKV'));
|
||||||
$end = $now->copy()->endOfMonth();
|
$q->where('ut.usertype_subject1', $val);
|
||||||
break;
|
}
|
||||||
case 'next':
|
// タグシリアル64進 部分一致 (SELECT 不要)
|
||||||
$start = $now->copy()->addMonth()->startOfMonth();
|
if ($request->filled('user_tag_serial_64')) {
|
||||||
$end = $now->copy()->addMonth()->endOfMonth();
|
$q->where('u.user_tag_serial_64','like','%'.$request->user_tag_serial_64.'%');
|
||||||
break;
|
}
|
||||||
case 'after2':
|
// 有効期限:指定日以前
|
||||||
$start = $now->copy()->addMonths(2)->startOfMonth();
|
if ($request->filled('contract_periode')) {
|
||||||
$end = $now->copy()->addMonths(2)->endOfMonth();
|
$raw = str_replace('/','-',$request->contract_periode);
|
||||||
break;
|
try {
|
||||||
default:
|
$target = \Carbon\Carbon::parse($raw)->format('Y-m-d');
|
||||||
$start = null;
|
$q->whereDate('rc.contract_periode','<=',$target);
|
||||||
$end = null;
|
} catch (\Exception $e) {}
|
||||||
}
|
}
|
||||||
if ($start && $end) {
|
if ($request->filled('user_phonetic')) {
|
||||||
$q->whereBetween('rc.contract_periode', [$start->toDateString(), $end->toDateString()]);
|
$q->where('u.user_phonetic','like','%'.$request->user_phonetic.'%');
|
||||||
|
}
|
||||||
|
if ($request->filled('user_mobile')) {
|
||||||
|
$like = '%'.$request->user_mobile.'%';
|
||||||
|
$q->where(function($w) use ($like){
|
||||||
|
$w->where('u.user_mobile','like',$like)
|
||||||
|
->orWhere('u.user_homephone','like',$like);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if ($request->filled('user_primemail')) {
|
||||||
|
$like = '%'.$request->user_primemail.'%';
|
||||||
|
$q->where(function($w) use ($like){
|
||||||
|
$w->where('u.user_primemail','like',$like)
|
||||||
|
->orWhere('u.user_submail','like',$like);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if ($request->filled('user_workplace')) {
|
||||||
|
$q->where('u.user_workplace','like','%'.$request->user_workplace.'%');
|
||||||
|
}
|
||||||
|
if ($request->filled('user_school')) {
|
||||||
|
$q->where('u.user_school','like','%'.$request->user_school.'%');
|
||||||
|
}
|
||||||
|
if ($request->filled('tag_qr_flag') && $request->tag_qr_flag!=='') {
|
||||||
|
$q->where('rc.tag_qr_flag',$request->tag_qr_flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 対象月
|
||||||
|
$target = $request->input('target_month');
|
||||||
|
if (in_array($target,['last','this','next','after2'],true)) {
|
||||||
|
$base = now()->startOfMonth();
|
||||||
|
$offset = ['last'=>-1,'this'=>0,'next'=>1,'after2'=>2][$target];
|
||||||
|
$m = $base->copy()->addMonths($offset);
|
||||||
|
if ($target === 'after2') {
|
||||||
|
// 2か月後「以降」を抽出(該当月の月初以降)
|
||||||
|
$q->whereDate('rc.contract_periode', '>=', $m->toDateString());
|
||||||
|
} else {
|
||||||
|
$q->whereYear('rc.contract_periode',$m->year)
|
||||||
|
->whereMonth('rc.contract_periode',$m->month);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$sort = $request->input('sort', 'rc.contract_id');
|
// ソート(regular_contract の表示順に合わせて contract_id の降順を既定に)
|
||||||
$sortType = $request->input('sort_type', 'desc');
|
$sort = $request->input('sort','contract_id');
|
||||||
$allowSorts = ['rc.contract_id'];
|
$sortType = $request->input('sort_type','dac');
|
||||||
if (!in_array($sort, $allowSorts)) {
|
$allow = [
|
||||||
$sort = 'rc.contract_id';
|
'contract_id' => 'rc.contract_id',
|
||||||
}
|
'user_id' => 'rc.user_id',
|
||||||
$sortType = ($sortType === 'asc') ? 'asc' : 'desc';
|
'contract_periode' => 'rc.contract_periode',
|
||||||
|
];
|
||||||
|
if (!isset($allow[$sort])) $sort = 'contract_id';
|
||||||
|
$sortType = $sortType==='desc' ? 'desc' : 'asc';
|
||||||
|
$q->orderBy($allow[$sort], $sortType);
|
||||||
|
|
||||||
$rows = $q->orderBy($sort, $sortType)->paginate(20)->withQueryString();
|
$rows = $q->paginate(20)->appends($request->query());
|
||||||
|
|
||||||
// 駐輪場リスト(プルダウン用)
|
$parks = DB::table('park')
|
||||||
$parks = DB::table('park')->select('park_id', 'park_name')->orderBy('park_name')->get();
|
->select('park_id','park_name')
|
||||||
|
->orderBy('park_name')
|
||||||
|
->get();
|
||||||
|
|
||||||
return view('admin.update_candidate.list', compact('rows', 'sort', 'sortType', 'parks'));
|
return view('admin.update_candidate.list',
|
||||||
|
compact('rows','parks'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,8 +6,30 @@ use Illuminate\Http\Request;
|
|||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Validator;
|
use Illuminate\Support\Facades\Validator;
|
||||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||||
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
|
||||||
class UsersController
|
class UsersController
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* 利用者分類選択肢を取得
|
||||||
|
*/
|
||||||
|
private function buildCategoryOptions(): array
|
||||||
|
{
|
||||||
|
return DB::table('usertype')
|
||||||
|
->orderBy('user_categoryid', 'asc')
|
||||||
|
->get()
|
||||||
|
->mapWithKeys(function ($row) {
|
||||||
|
$label = collect([
|
||||||
|
$row->usertype_subject1 ?? '',
|
||||||
|
$row->usertype_subject2 ?? '',
|
||||||
|
$row->usertype_subject3 ?? '',
|
||||||
|
])->filter(fn ($v) => $v !== '')->implode('/');
|
||||||
|
|
||||||
|
return [$row->user_categoryid => $label !== '' ? $label : (string) $row->user_categoryid];
|
||||||
|
})
|
||||||
|
->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 利用者一覧
|
* 利用者一覧
|
||||||
* - テーブル名: user
|
* - テーブル名: user
|
||||||
@ -38,13 +60,15 @@ class UsersController
|
|||||||
'user_name',
|
'user_name',
|
||||||
'user_birthdate',
|
'user_birthdate',
|
||||||
'user_age',
|
'user_age',
|
||||||
|
'user_mobile',
|
||||||
'user_homephone',
|
'user_homephone',
|
||||||
'user_primemail',
|
'user_primemail',
|
||||||
'user_submail',
|
'user_submail',
|
||||||
'user_school',
|
'user_school',
|
||||||
];
|
];
|
||||||
$sort = $request->input('sort', 'user_seq');
|
$sort = $request->input('sort', 'user_seq');
|
||||||
$sortType = strtolower($request->input('sort_type', 'desc')) === 'asc' ? 'asc' : 'desc';
|
$dirParam = strtolower((string) $request->input('dir', $request->input('sort_type', 'asc')));
|
||||||
|
$sortType = $dirParam === 'asc' ? 'asc' : 'desc';
|
||||||
if (!in_array($sort, $sortable, true)) {
|
if (!in_array($sort, $sortable, true)) {
|
||||||
$sort = 'user_seq';
|
$sort = 'user_seq';
|
||||||
}
|
}
|
||||||
@ -56,6 +80,7 @@ class UsersController
|
|||||||
$user_phonetic = trim((string) $request->input('user_phonetic', ''));
|
$user_phonetic = trim((string) $request->input('user_phonetic', ''));
|
||||||
$phone = trim((string) $request->input('phone', '')); // 携帯/自宅の両方対象
|
$phone = trim((string) $request->input('phone', '')); // 携帯/自宅の両方対象
|
||||||
$crime = trim((string) $request->input('crime', '')); // 防犯登録番号(暫定: qr_code)
|
$crime = trim((string) $request->input('crime', '')); // 防犯登録番号(暫定: qr_code)
|
||||||
|
$email = trim((string) $request->input('email', ''));
|
||||||
$user_categoryid = (string) $request->input('user_categoryid', '');
|
$user_categoryid = (string) $request->input('user_categoryid', '');
|
||||||
$tag_qr_flag = (string) $request->input('tag_qr_flag', ''); // 0=タグ / 1=QR
|
$tag_qr_flag = (string) $request->input('tag_qr_flag', ''); // 0=タグ / 1=QR
|
||||||
$quit_flag = (string) $request->input('quit_flag', ''); // 0=いいえ / 1=はい
|
$quit_flag = (string) $request->input('quit_flag', ''); // 0=いいえ / 1=はい
|
||||||
@ -63,7 +88,14 @@ class UsersController
|
|||||||
$quit_to = (string) $request->input('quit_to', ''); // YYYY-MM-DD
|
$quit_to = (string) $request->input('quit_to', ''); // YYYY-MM-DD
|
||||||
|
|
||||||
// ▼ ベースクエリ(一覧で使う列が多いので一旦 * を許容)
|
// ▼ ベースクエリ(一覧で使う列が多いので一旦 * を許容)
|
||||||
$query = DB::table('user')->select('user.*');
|
$query = DB::table('user')
|
||||||
|
->leftJoin('usertype', 'user.user_categoryid', '=', 'usertype.user_categoryid')
|
||||||
|
->select(
|
||||||
|
'user.*',
|
||||||
|
'usertype.usertype_subject1',
|
||||||
|
'usertype.usertype_subject2',
|
||||||
|
'usertype.usertype_subject3'
|
||||||
|
);
|
||||||
|
|
||||||
// ▼ テキスト系
|
// ▼ テキスト系
|
||||||
if ($user_id !== '')
|
if ($user_id !== '')
|
||||||
@ -84,6 +116,8 @@ class UsersController
|
|||||||
// ※ dump に防犯登録番号の明確なカラムが無いため暫定的に qr_code を対象
|
// ※ dump に防犯登録番号の明確なカラムが無いため暫定的に qr_code を対象
|
||||||
$query->where('user.qr_code', 'like', "%{$crime}%");
|
$query->where('user.qr_code', 'like', "%{$crime}%");
|
||||||
}
|
}
|
||||||
|
if ($email !== '')
|
||||||
|
$query->where('user.user_primemail', 'like', "%{$email}%");
|
||||||
|
|
||||||
// ▼ セレクト/ラジオ('' 以外なら適用。'0' も通す)
|
// ▼ セレクト/ラジオ('' 以外なら適用。'0' も通す)
|
||||||
if ($user_categoryid !== '')
|
if ($user_categoryid !== '')
|
||||||
@ -107,17 +141,20 @@ class UsersController
|
|||||||
'list' => $list,
|
'list' => $list,
|
||||||
'sort' => $sort,
|
'sort' => $sort,
|
||||||
'sort_type' => $sortType,
|
'sort_type' => $sortType,
|
||||||
|
'dir' => $sortType,
|
||||||
'user_id' => $user_id,
|
'user_id' => $user_id,
|
||||||
'member_id' => $member_id,
|
'member_id' => $member_id,
|
||||||
'user_tag_serial' => $user_tag_serial,
|
'user_tag_serial' => $user_tag_serial,
|
||||||
'user_phonetic' => $user_phonetic,
|
'user_phonetic' => $user_phonetic,
|
||||||
'phone' => $phone,
|
'phone' => $phone,
|
||||||
'crime' => $crime,
|
'crime' => $crime,
|
||||||
|
'email' => $email,
|
||||||
'user_categoryid' => $user_categoryid,
|
'user_categoryid' => $user_categoryid,
|
||||||
'tag_qr_flag' => $tag_qr_flag,
|
'tag_qr_flag' => $tag_qr_flag,
|
||||||
'quit_flag' => $quit_flag,
|
'quit_flag' => $quit_flag,
|
||||||
'quit_from' => $quit_from,
|
'quit_from' => $quit_from,
|
||||||
'quit_to' => $quit_to,
|
'quit_to' => $quit_to,
|
||||||
|
'categoryOptions' => $this->buildCategoryOptions(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -260,11 +297,11 @@ class UsersController
|
|||||||
return view('admin.users.add');
|
return view('admin.users.add');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ▼ バリデーション(user_id は半角数字のみ)
|
// ▼ バリデーション(user_id は半角数字のみ)
|
||||||
$rules = [
|
$rules = [
|
||||||
'user_id' => ['required', 'regex:/^\d+$/'], // 半角数字のみ許可
|
'user_id' => ['required', 'regex:/^\d+$/', 'digits_between:1,10'], // 半角数字(最大10桁)
|
||||||
'user_name' => ['required', 'string', 'max:255'],
|
'user_name' => ['required', 'string', 'max:255'],
|
||||||
|
'user_pass' => ['required', 'string', 'min:8', 'confirmed'],
|
||||||
// 任意
|
// 任意
|
||||||
'user_primemail' => ['nullable', 'email', 'max:255'],
|
'user_primemail' => ['nullable', 'email', 'max:255'],
|
||||||
'user_gender' => ['nullable', 'in:男性,女性'],
|
'user_gender' => ['nullable', 'in:男性,女性'],
|
||||||
@ -279,6 +316,11 @@ class UsersController
|
|||||||
$messages = [
|
$messages = [
|
||||||
'user_id.required' => '利用者IDは必須です。',
|
'user_id.required' => '利用者IDは必須です。',
|
||||||
'user_id.regex' => '利用者IDは半角数字のみで入力してください。',
|
'user_id.regex' => '利用者IDは半角数字のみで入力してください。',
|
||||||
|
'user_id.digits_between' => '利用者IDは最大10桁以内で入力してください。',
|
||||||
|
'user_name.required' => '氏名は必須です。',
|
||||||
|
'user_pass.required' => 'パスワードは必須です。',
|
||||||
|
'user_pass.min' => 'パスワードは8文字以上で入力してください。',
|
||||||
|
'user_pass.confirmed' => 'パスワードと確認用パスワードが一致しません。',
|
||||||
];
|
];
|
||||||
|
|
||||||
// ▼ 属性名(日本語ラベル)
|
// ▼ 属性名(日本語ラベル)
|
||||||
@ -315,4 +357,158 @@ class UsersController
|
|||||||
return redirect()->route('users')->with('success', '利用者を登録しました。');
|
return redirect()->route('users')->with('success', '利用者を登録しました。');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 利用者編集(GET: 表示 / POST: 更新)
|
||||||
|
*/
|
||||||
|
public function edit(Request $request, int $seq)
|
||||||
|
{
|
||||||
|
|
||||||
|
$user = DB::table('user')->where('user_seq', $seq)->first();
|
||||||
|
if (!$user) {
|
||||||
|
abort(404, '利用者情報が見つかりません。');
|
||||||
|
}
|
||||||
|
|
||||||
|
$operators = DB::table('ope')
|
||||||
|
->select('ope_id', 'ope_name')
|
||||||
|
->orderBy('ope_name')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
$categoryOptions = $this->buildCategoryOptions();
|
||||||
|
|
||||||
|
// ▼ 退会処理専用(hiddenフィールド quit_action があれば退会処理)
|
||||||
|
if ($request->has('quit_action')) {
|
||||||
|
DB::table('user')->where('user_seq', $seq)->update([
|
||||||
|
'user_quit_flag' => 1,
|
||||||
|
'user_quitday' => now()->format('Y-m-d'),
|
||||||
|
'ope_id' => $request->input('ope_id') ?? auth()->user()->ope_id ?? null,
|
||||||
|
'updated_at' => now(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return redirect()
|
||||||
|
->route('users_edit', ['seq' => $seq])
|
||||||
|
->with('status', '退会処理が完了しました。');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if ($request->isMethod('get')) {
|
||||||
|
return view('admin.users.edit', [
|
||||||
|
'user' => $user,
|
||||||
|
'operators' => $operators,
|
||||||
|
'categoryOptions' => $categoryOptions,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$rules = [
|
||||||
|
'user_id' => ['required', 'regex:/^\d+$/', 'digits_between:1,10'],
|
||||||
|
'user_name' => ['required', 'string', 'max:255'],
|
||||||
|
'user_pass' => ['nullable', 'string', 'min:8', 'confirmed'],
|
||||||
|
'user_primemail' => ['nullable', 'email', 'max:255'],
|
||||||
|
'user_gender' => ['nullable', 'in:男性,女性,未入力'],
|
||||||
|
'member_id' => ['nullable', 'string', 'max:255'],
|
||||||
|
'user_mobile' => ['nullable', 'string', 'max:255'],
|
||||||
|
'user_homephone' => ['nullable', 'string', 'max:255'],
|
||||||
|
'user_birthdate' => ['nullable', 'date'],
|
||||||
|
'user_categoryid' => ['nullable', 'integer', 'exists:usertype,user_categoryid'],
|
||||||
|
'user_age' => ['nullable', 'integer', 'min:0'],
|
||||||
|
'user_chk_day' => ['nullable', 'date'],
|
||||||
|
'user_quitday' => ['nullable', 'date'],
|
||||||
|
'ope_id' => ['nullable', 'integer', 'exists:ope,ope_id'],
|
||||||
|
];
|
||||||
|
|
||||||
|
$messages = [
|
||||||
|
'user_id.required' => '利用者IDは必須です。',
|
||||||
|
'user_id.regex' => '利用者IDは半角数字のみで入力してください。',
|
||||||
|
'user_id.digits_between' => '利用者IDは最大10桁以内で入力してください。',
|
||||||
|
'user_name.required' => '氏名は必須です。',
|
||||||
|
];
|
||||||
|
|
||||||
|
$validator = Validator::make($request->all(), $rules, $messages);
|
||||||
|
if ($validator->fails()) {
|
||||||
|
return back()->withErrors($validator)->withInput();
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'user_id' => $request->input('user_id'),
|
||||||
|
'member_id' => $request->input('member_id'),
|
||||||
|
'user_name' => $request->input('user_name'),
|
||||||
|
'user_gender' => $request->input('user_gender'),
|
||||||
|
'user_mobile' => $request->input('user_mobile'),
|
||||||
|
'user_homephone' => $request->input('user_homephone'),
|
||||||
|
'user_birthdate' => $request->input('user_birthdate'),
|
||||||
|
'user_age' => $request->input('user_age'),
|
||||||
|
'user_categoryid' => $request->input('user_categoryid'),
|
||||||
|
'user_phonetic' => $request->input('user_phonetic'),
|
||||||
|
'user_tag_serial' => $request->input('user_tag_serial'),
|
||||||
|
'user_tag_serial_64' => $request->input('user_tag_serial_64'),
|
||||||
|
'qr_code' => $request->input('qr_code'),
|
||||||
|
'tag_qr_flag' => $request->input('tag_qr_flag', '0'),
|
||||||
|
'user_aid' => $request->input('user_aid'),
|
||||||
|
'user_place_qrid' => $request->input('user_place_qrid'),
|
||||||
|
'user_primemail' => $request->input('user_primemail'),
|
||||||
|
'user_submail' => $request->input('user_submail'),
|
||||||
|
'ward_residents' => $request->input('ward_residents'),
|
||||||
|
'user_workplace' => $request->input('user_workplace'),
|
||||||
|
'user_school' => $request->input('user_school'),
|
||||||
|
'user_graduate' => $request->input('user_graduate'),
|
||||||
|
'user_idcard' => $request->input('user_idcard'),
|
||||||
|
'user_idcard_chk_flag' => $request->input('user_idcard_chk_flag', '0'),
|
||||||
|
'user_chk_day' => $request->input('user_chk_day'),
|
||||||
|
'user_chk_opeid' => $request->input('ope_id'),
|
||||||
|
'ope_id' => $request->input('ope_id'),
|
||||||
|
'user_regident_zip' => $request->input('user_regident_zip'),
|
||||||
|
'user_regident_pre' => $request->input('user_regident_pre'),
|
||||||
|
'user_regident_city' => $request->input('user_regident_city'),
|
||||||
|
'user_regident_add' => $request->input('user_regident_add'),
|
||||||
|
'user_relate_zip' => $request->input('user_relate_zip'),
|
||||||
|
'user_relate_pre' => $request->input('user_relate_pre'),
|
||||||
|
'user_relate_city' => $request->input('user_relate_city'),
|
||||||
|
'user_relate_add' => $request->input('user_relate_add'),
|
||||||
|
'user_tag_issue' => $request->input('user_tag_issue'),
|
||||||
|
'issue_permission' => $request->input('issue_permission', '1'),
|
||||||
|
'user_quit_flag' => $request->input('user_quit_flag', '0'),
|
||||||
|
'user_quitday' => $request->input('user_quitday'),
|
||||||
|
'user_remarks' => $request->input('user_remarks'),
|
||||||
|
'updated_at' => now(),
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($request->filled('user_pass')) {
|
||||||
|
$data['user_pass'] = Hash::make($request->input('user_pass'));
|
||||||
|
}
|
||||||
|
|
||||||
|
DB::table('user')->where('user_seq', $seq)->update($data);
|
||||||
|
|
||||||
|
return redirect()
|
||||||
|
->route('users_edit', ['seq' => $seq])
|
||||||
|
->with('status', '利用者情報を更新しました。');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 利用者削除(POST: 削除実行)
|
||||||
|
* - user_seq をキーに削除処理を行う
|
||||||
|
* - 削除前に存在確認を行い、存在しない場合はエラーを返す
|
||||||
|
* - 削除完了後、一覧画面へリダイレクト
|
||||||
|
*/
|
||||||
|
public function delete(Request $request)
|
||||||
|
{
|
||||||
|
// ▼ パラメータ取得
|
||||||
|
$userSeq = (int) $request->input('user_seq');
|
||||||
|
|
||||||
|
// ▼ 対象レコード存在確認
|
||||||
|
$user = DB::table('user')->where('user_seq', $userSeq)->first();
|
||||||
|
if (!$user) {
|
||||||
|
// 該当データなし
|
||||||
|
return redirect()
|
||||||
|
->route('users')
|
||||||
|
->with('error', '利用者情報が見つかりません。');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ▼ 削除処理実行
|
||||||
|
DB::table('user')->where('user_seq', $userSeq)->delete();
|
||||||
|
|
||||||
|
// ▼ 正常終了メッセージを一覧画面に表示
|
||||||
|
return redirect()
|
||||||
|
->route('users')
|
||||||
|
->with('success', '利用者を削除しました。');
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,20 +15,42 @@ class UsertypeController extends Controller
|
|||||||
{
|
{
|
||||||
public function list(Request $request)
|
public function list(Request $request)
|
||||||
{
|
{
|
||||||
$inputs = [
|
$sort = $request->input('sort', 'user_categoryid');
|
||||||
'isMethodPost' => 0,
|
$allowSort = [
|
||||||
'isExport' => 0,
|
'user_categoryid',
|
||||||
'sort' => $request->input('sort', ''),
|
'sort_order',
|
||||||
'sort_type' => $request->input('sort_type', ''),
|
'usertype_subject1',
|
||||||
'page' => $request->get('page', 1),
|
'usertype_subject2',
|
||||||
|
'usertype_subject3',
|
||||||
|
'print_name',
|
||||||
];
|
];
|
||||||
$inputs['isMethodPost'] = $request->isMethod('post');
|
if (!in_array($sort, $allowSort, true)) {
|
||||||
$inputs['list'] = Usertype::search($inputs);
|
$sort = 'user_categoryid';
|
||||||
if ($inputs['list']->total() > 0 && $inputs['page'] > $inputs['list']->lastPage()) {
|
}
|
||||||
|
$sortType = strtolower($request->input('sort_type', 'asc'));
|
||||||
|
if (!in_array($sortType, ['asc', 'desc'], true)) {
|
||||||
|
$sortType = 'asc';
|
||||||
|
}
|
||||||
|
$inputs = [
|
||||||
|
'isMethodPost' => $request->isMethod('post') ? 1 : 0,
|
||||||
|
'isExport' => 0,
|
||||||
|
'sort' => $sort,
|
||||||
|
'sort_type' => $sortType,
|
||||||
|
'page' => (int) $request->get('page', 1),
|
||||||
|
];
|
||||||
|
$filters = [
|
||||||
|
'filter_sort_order' => $request->input('filter_sort_order', ''),
|
||||||
|
'filter_usertype_subject1' => $request->input('filter_usertype_subject1', ''),
|
||||||
|
'filter_usertype_subject2' => $request->input('filter_usertype_subject2', ''),
|
||||||
|
'filter_usertype_subject3' => $request->input('filter_usertype_subject3', ''),
|
||||||
|
];
|
||||||
|
$searchParams = array_merge($inputs, $filters);
|
||||||
|
$viewData = $searchParams;
|
||||||
|
$viewData['list'] = Usertype::search($searchParams);
|
||||||
|
if ($viewData['list']->total() > 0 && $viewData['page'] > $viewData['list']->lastPage()) {
|
||||||
return redirect()->route('usertypes');
|
return redirect()->route('usertypes');
|
||||||
}
|
}
|
||||||
return view('admin.usertypes.list', $inputs);
|
return view('admin.usertypes.list', $viewData);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function add(Request $request)
|
public function add(Request $request)
|
||||||
@ -36,9 +58,9 @@ class UsertypeController extends Controller
|
|||||||
// 画面に戻すための初期値
|
// 画面に戻すための初期値
|
||||||
$viewData = [
|
$viewData = [
|
||||||
'sort_order' => old('sort_order', ''),
|
'sort_order' => old('sort_order', ''),
|
||||||
'category_name1' => old('category_name1', ''),
|
'usertype_subject1' => old('usertype_subject1', ''),
|
||||||
'category_name2' => old('category_name2', ''),
|
'usertype_subject2' => old('usertype_subject2', ''),
|
||||||
'category_name3' => old('category_name3', ''),
|
'usertype_subject3' => old('usertype_subject3', ''),
|
||||||
'print_name' => old('print_name', ''),
|
'print_name' => old('print_name', ''),
|
||||||
'usertype_money' => old('usertype_money', ''),
|
'usertype_money' => old('usertype_money', ''),
|
||||||
'usertype_remarks' => old('usertype_remarks', ''),
|
'usertype_remarks' => old('usertype_remarks', ''),
|
||||||
@ -50,9 +72,9 @@ class UsertypeController extends Controller
|
|||||||
// 入力値をまとめる
|
// 入力値をまとめる
|
||||||
$inputs = [
|
$inputs = [
|
||||||
'sort_order' => $request->input('sort_order'),
|
'sort_order' => $request->input('sort_order'),
|
||||||
'category_name1' => $request->input('category_name1'),
|
'usertype_subject1' => $request->input('usertype_subject1'),
|
||||||
'category_name2' => $request->input('category_name2'),
|
'usertype_subject2' => $request->input('usertype_subject2'),
|
||||||
'category_name3' => $request->input('category_name3'),
|
'usertype_subject3' => $request->input('usertype_subject3'),
|
||||||
'print_name' => $request->input('print_name'),
|
'print_name' => $request->input('print_name'),
|
||||||
'usertype_money' => $request->input('usertype_money'),
|
'usertype_money' => $request->input('usertype_money'),
|
||||||
'usertype_remarks' => $request->input('usertype_remarks'),
|
'usertype_remarks' => $request->input('usertype_remarks'),
|
||||||
@ -61,9 +83,9 @@ class UsertypeController extends Controller
|
|||||||
// バリデーションルール(最小限)
|
// バリデーションルール(最小限)
|
||||||
$rules = [
|
$rules = [
|
||||||
'sort_order' => 'nullable|integer',
|
'sort_order' => 'nullable|integer',
|
||||||
'category_name1' => 'nullable|string|max:255',
|
'usertype_subject1' => 'nullable|string|max:255',
|
||||||
'category_name2' => 'nullable|string|max:255',
|
'usertype_subject2' => 'nullable|string|max:255',
|
||||||
'category_name3' => 'nullable|string|max:255',
|
'usertype_subject3' => 'nullable|string|max:255',
|
||||||
'print_name' => 'required|string|max:255',
|
'print_name' => 'required|string|max:255',
|
||||||
'usertype_money' => 'nullable|string|max:255',
|
'usertype_money' => 'nullable|string|max:255',
|
||||||
'usertype_remarks' => 'nullable|string|max:255',
|
'usertype_remarks' => 'nullable|string|max:255',
|
||||||
@ -115,9 +137,9 @@ class UsertypeController extends Controller
|
|||||||
// ▼ 内蔵バリデーション(FormRequest を使わない)
|
// ▼ 内蔵バリデーション(FormRequest を使わない)
|
||||||
$rules = [
|
$rules = [
|
||||||
'sort_order' => 'nullable|integer',
|
'sort_order' => 'nullable|integer',
|
||||||
'category_name1' => 'nullable|string|max:255',
|
'usertype_subject1' => 'nullable|string|max:255',
|
||||||
'category_name2' => 'nullable|string|max:255',
|
'usertype_subject2' => 'nullable|string|max:255',
|
||||||
'category_name3' => 'nullable|string|max:255',
|
'usertype_subject3' => 'nullable|string|max:255',
|
||||||
'print_name' => 'required|string|max:255',
|
'print_name' => 'required|string|max:255',
|
||||||
'usertype_money' => 'nullable|string|max:255',
|
'usertype_money' => 'nullable|string|max:255',
|
||||||
'usertype_remarks' => 'nullable|string|max:255',
|
'usertype_remarks' => 'nullable|string|max:255',
|
||||||
@ -141,9 +163,9 @@ class UsertypeController extends Controller
|
|||||||
// fill するフィールドだけを明示したい場合は only(...) で絞ってもOK
|
// fill するフィールドだけを明示したい場合は only(...) で絞ってもOK
|
||||||
$usertype->fill([
|
$usertype->fill([
|
||||||
'sort_order' => $data['sort_order'] ?? null,
|
'sort_order' => $data['sort_order'] ?? null,
|
||||||
'category_name1' => $data['category_name1'] ?? null,
|
'usertype_subject1' => $data['usertype_subject1'] ?? null,
|
||||||
'category_name2' => $data['category_name2'] ?? null,
|
'usertype_subject2' => $data['usertype_subject2'] ?? null,
|
||||||
'category_name3' => $data['category_name3'] ?? null,
|
'usertype_subject3' => $data['usertype_subject3'] ?? null,
|
||||||
'print_name' => $data['print_name'] ?? null,
|
'print_name' => $data['print_name'] ?? null,
|
||||||
'usertype_money' => $data['usertype_money'] ?? null,
|
'usertype_money' => $data['usertype_money'] ?? null,
|
||||||
'usertype_remarks' => $data['usertype_remarks'] ?? null,
|
'usertype_remarks' => $data['usertype_remarks'] ?? null,
|
||||||
@ -197,21 +219,25 @@ class UsertypeController extends Controller
|
|||||||
|
|
||||||
public function export(Request $request)
|
public function export(Request $request)
|
||||||
{
|
{
|
||||||
|
$timestamp = now()->format('YmdHis');
|
||||||
$headers = array(
|
$filename = "利用者分類マスタ{$timestamp}.csv";
|
||||||
"Content-type" => "text/csv;charset=UTF-8",
|
$filePath = storage_path("app/{$filename}");
|
||||||
'Content-Encoding: UTF-8',
|
$headers = [
|
||||||
"Content-Disposition" => "attachment; filename=file.csv",
|
'Content-Type' => 'text/csv; charset=UTF-8',
|
||||||
"Pragma" => "no-cache",
|
'Content-Encoding' => 'UTF-8',
|
||||||
"Cache-Control" => "must-revalidate, post-check=0, pre-check=0",
|
'Pragma' => 'no-cache',
|
||||||
"Expires" => "0"
|
'Cache-Control' => 'must-revalidate, post-check=0, pre-check=0',
|
||||||
);
|
'Expires' => '0',
|
||||||
|
];
|
||||||
$inputs = [
|
$inputs = [
|
||||||
'isMethodPost' => 0,
|
'isMethodPost' => 0,
|
||||||
'isExport' => 1,
|
'isExport' => 1,
|
||||||
'sort' => $request->input('sort', ''),
|
'sort' => $request->input('sort', ''),
|
||||||
'sort_type' => $request->input('sort_type', ''),
|
'sort_type' => $request->input('sort_type', ''),
|
||||||
|
'filter_sort_order' => $request->input('filter_sort_order', ''),
|
||||||
|
'filter_usertype_subject1' => $request->input('filter_usertype_subject1', ''),
|
||||||
|
'filter_usertype_subject2' => $request->input('filter_usertype_subject2', ''),
|
||||||
|
'filter_usertype_subject3' => $request->input('filter_usertype_subject3', ''),
|
||||||
];
|
];
|
||||||
|
|
||||||
$dataExport = Usertype::search($inputs);
|
$dataExport = Usertype::search($inputs);
|
||||||
@ -221,8 +247,7 @@ class UsertypeController extends Controller
|
|||||||
__('適用料率'),// 2
|
__('適用料率'),// 2
|
||||||
__('備考'),// 3
|
__('備考'),// 3
|
||||||
);
|
);
|
||||||
$filename = "利用者分類マスタ.csv";
|
$file = fopen($filePath, 'w+');
|
||||||
$file = fopen($filename, 'w+');
|
|
||||||
fputcsv($file, $columns);
|
fputcsv($file, $columns);
|
||||||
foreach ($dataExport as $items) {
|
foreach ($dataExport as $items) {
|
||||||
fputcsv(
|
fputcsv(
|
||||||
@ -236,7 +261,7 @@ class UsertypeController extends Controller
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
fclose($file);
|
fclose($file);
|
||||||
return Response::download($filename, $filename, $headers);
|
return Response::download($filePath, $filename, $headers)->deleteFileAfterSend(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function import(Request $request)
|
public function import(Request $request)
|
||||||
|
|||||||
@ -38,76 +38,36 @@ class UsingStatusController extends Controller
|
|||||||
public function index(Request $request, UsingStatusService $service)
|
public function index(Request $request, UsingStatusService $service)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
// CSRF トークンの自動検証(Laravel 12標準機能)
|
$parkId = $request->input('park_id'); // GET/POST どちらでも取得
|
||||||
|
|
||||||
// リクエストパラメータの取得
|
|
||||||
// Laravel 12変更点:$request->input()の使用を推奨
|
|
||||||
$parkId = $request->input('park_id', null);
|
|
||||||
$isSearchRequest = $request->has('search') || $request->isMethod('post');
|
|
||||||
|
|
||||||
// ログ出力(デバッグ用)
|
|
||||||
Log::info('区画別利用率状況ページアクセス', [
|
|
||||||
'park_id' => $parkId,
|
|
||||||
'is_search' => $isSearchRequest,
|
|
||||||
'method' => $request->method()
|
|
||||||
]);
|
|
||||||
|
|
||||||
// 駐輪場一覧の取得(選択用ドロップダウン)
|
|
||||||
$parkList = $service->getParkList();
|
$parkList = $service->getParkList();
|
||||||
|
|
||||||
// 利用率統計データの取得
|
// 駐輪場が選択されている場合のみ取得。「全て/空」の場合は空コレクションを返す
|
||||||
// Laravel 12変更点:デフォルトで全データを表示(ユーザー選択不要)
|
$utilizationStats = collect();
|
||||||
$utilizationStats = $service->getUtilizationStats($parkId);
|
if ($parkId !== null && $parkId !== '') {
|
||||||
|
$utilizationStats = $service->getUtilizationStats($parkId);
|
||||||
// データが空の場合の処理
|
|
||||||
if ($utilizationStats->isEmpty() && $parkId) {
|
|
||||||
// 指定された駐輪場のデータが見つからない場合
|
|
||||||
return redirect()->route('using_status')
|
|
||||||
->with('warning', '選択された駐輪場のデータが見つかりませんでした。');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 検索要求でない場合は全データを表示
|
|
||||||
if (!$isSearchRequest && !$parkId) {
|
|
||||||
$utilizationStats = $service->getUtilizationStats(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 合計値の計算
|
|
||||||
$totals = $service->calculateTotals($utilizationStats);
|
$totals = $service->calculateTotals($utilizationStats);
|
||||||
|
$hasData = $utilizationStats->isNotEmpty();
|
||||||
|
$isSearchRequest = ($request->isMethod('post') || $request->has('park_id'));
|
||||||
|
$selectedPark = $parkList->firstWhere('park_id', $parkId);
|
||||||
|
|
||||||
// 選択された駐輪場の情報
|
return view('admin.using_status.index', [
|
||||||
$selectedPark = null;
|
'parkList' => $parkList,
|
||||||
if ($parkId && $parkList->isNotEmpty()) {
|
'utilizationStats' => $utilizationStats,
|
||||||
$selectedPark = $parkList->firstWhere('park_id', $parkId);
|
'totals' => $totals,
|
||||||
}
|
'selectedParkId' => $parkId,
|
||||||
|
'selectedPark' => $selectedPark,
|
||||||
// ビューに渡すデータの準備
|
'isSearchRequest' => $isSearchRequest,
|
||||||
$viewData = [
|
'hasData' => $hasData,
|
||||||
'parkList' => $parkList, // 駐輪場選択用リスト
|
]);
|
||||||
'utilizationStats' => $utilizationStats, // 利用率統計データ
|
|
||||||
'totals' => $totals, // 合計値
|
|
||||||
'selectedParkId' => $parkId, // 選択された駐輪場ID
|
|
||||||
'selectedPark' => $selectedPark, // 選択された駐輪場情報
|
|
||||||
'isSearchRequest' => $isSearchRequest, // 検索リクエストかどうか
|
|
||||||
'hasData' => $utilizationStats->isNotEmpty() // データが存在するかどうか
|
|
||||||
];
|
|
||||||
|
|
||||||
// 成功メッセージの設定(検索時)
|
|
||||||
if ($isSearchRequest && $utilizationStats->isNotEmpty()) {
|
|
||||||
session()->flash('success', '利用率データを正常に取得しました。');
|
|
||||||
}
|
|
||||||
|
|
||||||
return view('admin.using_status.index', $viewData);
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
// エラーログの出力
|
|
||||||
Log::error('区画別利用率状況ページエラー', [
|
Log::error('区画別利用率状況ページエラー', [
|
||||||
'error' => $e->getMessage(),
|
'error' => $e->getMessage(),
|
||||||
'file' => $e->getFile(),
|
'file' => $e->getFile(),
|
||||||
'line' => $e->getLine(),
|
'line' => $e->getLine(),
|
||||||
'park_id' => $parkId ?? null
|
'park_id' => $parkId ?? null,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// エラー発生時のリダイレクト
|
|
||||||
return redirect()->route('using_status')
|
return redirect()->route('using_status')
|
||||||
->with('error', 'データの取得中にエラーが発生しました。管理者にお問い合わせください。');
|
->with('error', 'データの取得中にエラーが発生しました。管理者にお問い合わせください。');
|
||||||
}
|
}
|
||||||
@ -200,7 +160,7 @@ class UsingStatusController extends Controller
|
|||||||
foreach ($stats as $stat) {
|
foreach ($stats as $stat) {
|
||||||
$rows[] = [
|
$rows[] = [
|
||||||
(string) $stat->park_name,
|
(string) $stat->park_name,
|
||||||
(string) $stat->ptype_subject,
|
(string) $stat->psection_subject,
|
||||||
(string) $stat->park_limit,
|
(string) $stat->park_limit,
|
||||||
(string) $stat->current_count,
|
(string) $stat->current_count,
|
||||||
(string) $stat->available,
|
(string) $stat->available,
|
||||||
|
|||||||
250
app/Http/Controllers/Admin/ZoneController.php
Normal file
250
app/Http/Controllers/Admin/ZoneController.php
Normal file
@ -0,0 +1,250 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use App\Models\Zone;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Validator;
|
||||||
|
use App\Models\Park;
|
||||||
|
use App\Models\Ptype;
|
||||||
|
use App\Models\Psection;
|
||||||
|
|
||||||
|
|
||||||
|
class ZoneController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 一覧表示(絞り込み対応)
|
||||||
|
*/
|
||||||
|
public function list(Request $request)
|
||||||
|
{
|
||||||
|
if ($request->input('action') === 'reset') {
|
||||||
|
return redirect()->route('zones');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ソート設定
|
||||||
|
$sort = $request->input('sort', 'zone_id');
|
||||||
|
$sort_type = $request->input('sort_type', 'asc');
|
||||||
|
|
||||||
|
// ベースクエリ
|
||||||
|
$query = Zone::query();
|
||||||
|
|
||||||
|
// === 絞り込み条件 ===
|
||||||
|
if ($request->filled('zone_id')) {
|
||||||
|
$query->where('zone_id', $request->zone_id);
|
||||||
|
}
|
||||||
|
if ($request->filled('zone_name')) {
|
||||||
|
$query->where('zone_name', 'LIKE', "%{$request->zone_name}%");
|
||||||
|
}
|
||||||
|
if ($request->filled('park_id')) {
|
||||||
|
$query->where('park_id', $request->park_id);
|
||||||
|
}
|
||||||
|
if ($request->filled('ptype_id')) {
|
||||||
|
$query->where('ptype_id', $request->ptype_id);
|
||||||
|
}
|
||||||
|
if ($request->filled('psection_id')) {
|
||||||
|
$query->where('psection_id', $request->psection_id);
|
||||||
|
}
|
||||||
|
if ($request->has('use_flag') && $request->use_flag !== '') {
|
||||||
|
$query->where('use_flag', $request->use_flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ページネーション
|
||||||
|
$zones = $query->orderBy($sort, $sort_type)->paginate(20);
|
||||||
|
|
||||||
|
// === 下拉选单用の一覧データ ===
|
||||||
|
$parkList = DB::table('park')->pluck('park_name', 'park_id');
|
||||||
|
$ptypeList = DB::table('ptype')->pluck('ptype_subject', 'ptype_id');
|
||||||
|
$psectionList = DB::table('psection')->pluck('psection_subject', 'psection_id');
|
||||||
|
|
||||||
|
return view('admin.zones.list', compact(
|
||||||
|
'zones', 'sort', 'sort_type',
|
||||||
|
'parkList', 'ptypeList', 'psectionList'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新規登録(画面/処理)
|
||||||
|
*/
|
||||||
|
public function add(Request $request)
|
||||||
|
{
|
||||||
|
if ($request->isMethod('get')) {
|
||||||
|
$parkList = DB::table('park')->pluck('park_name', 'park_id');
|
||||||
|
$ptypeList = DB::table('ptype')->pluck('ptype_subject', 'ptype_id');
|
||||||
|
$psectionList = DB::table('psection')->pluck('psection_subject', 'psection_id');
|
||||||
|
|
||||||
|
|
||||||
|
return view('admin.zones.add', [
|
||||||
|
'isEdit' => false,
|
||||||
|
'record' => new Zone(),
|
||||||
|
'parkList' => $parkList,
|
||||||
|
'ptypeList' => $ptypeList,
|
||||||
|
'psectionList' => $psectionList,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ▼ POST時:バリデーション
|
||||||
|
$rules = [
|
||||||
|
'park_id' => 'required|integer',
|
||||||
|
'ptype_id' => 'required|integer',
|
||||||
|
'psection_id' => 'required|integer',
|
||||||
|
'zone_name' => 'required|string|max:255',
|
||||||
|
'zone_number' => 'nullable|integer|min:0',
|
||||||
|
'zone_standard' => 'nullable|integer|min:0',
|
||||||
|
'zone_tolerance' => 'nullable|integer|min:0',
|
||||||
|
'zone_sort' => 'nullable|integer|min:0',
|
||||||
|
];
|
||||||
|
|
||||||
|
$messages = [
|
||||||
|
'park_id.required' => '駐輪場は必須です。',
|
||||||
|
'ptype_id.required' => '駐輪分類は必須です。',
|
||||||
|
'psection_id.required' => '車種区分は必須です。',
|
||||||
|
'zone_name.required' => 'ゾーン名は必須です。',
|
||||||
|
];
|
||||||
|
|
||||||
|
$validator = Validator::make($request->all(), $rules, $messages);
|
||||||
|
|
||||||
|
if ($validator->fails()) {
|
||||||
|
return redirect()->back()->withErrors($validator)->withInput();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ▼ 登録処理
|
||||||
|
DB::transaction(function () use ($request) {
|
||||||
|
$new = new Zone();
|
||||||
|
$new->fill($request->only([
|
||||||
|
'park_id',
|
||||||
|
'ptype_id',
|
||||||
|
'psection_id',
|
||||||
|
'zone_name',
|
||||||
|
'zone_number',
|
||||||
|
'zone_standard',
|
||||||
|
'zone_tolerance',
|
||||||
|
'zone_sort',
|
||||||
|
]));
|
||||||
|
$new->save();
|
||||||
|
});
|
||||||
|
|
||||||
|
return redirect()->route('zones')->with('success', '登録しました。');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 編集(画面/処理)
|
||||||
|
*/
|
||||||
|
public function edit(Request $request, $id)
|
||||||
|
{
|
||||||
|
// 該当データ取得
|
||||||
|
$record = Zone::find($id);
|
||||||
|
if (!$record) {
|
||||||
|
abort(404);
|
||||||
|
}
|
||||||
|
|
||||||
|
$parkList = DB::table('park')->pluck('park_name', 'park_id');
|
||||||
|
$ptypeList = DB::table('ptype')->pluck('ptype_subject', 'ptype_id');
|
||||||
|
$psectionList = DB::table('psection')->pluck('psection_subject', 'psection_id');
|
||||||
|
|
||||||
|
|
||||||
|
if ($request->isMethod('get')) {
|
||||||
|
// 編集画面表示
|
||||||
|
return view('admin.zones.edit', [
|
||||||
|
'isEdit' => true,
|
||||||
|
'record' => $record,
|
||||||
|
'parkList' => $parkList,
|
||||||
|
'ptypeList' => $ptypeList,
|
||||||
|
'psectionList' => $psectionList,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ▼ POST時:バリデーション
|
||||||
|
$rules = [
|
||||||
|
'park_id' => 'required|integer',
|
||||||
|
'ptype_id' => 'required|integer',
|
||||||
|
'psection_id' => 'required|integer',
|
||||||
|
'zone_name' => 'required|string|max:255',
|
||||||
|
'zone_number' => 'nullable|integer|min:0',
|
||||||
|
'zone_standard' => 'nullable|integer|min:0',
|
||||||
|
'zone_tolerance' => 'nullable|integer|min:0',
|
||||||
|
'zone_sort' => 'nullable|integer|min:0',
|
||||||
|
];
|
||||||
|
|
||||||
|
$messages = [
|
||||||
|
'park_id.required' => '駐輪場は必須です。',
|
||||||
|
'ptype_id.required' => '駐輪分類は必須です。',
|
||||||
|
'psection_id.required' => '車種区分は必須です。',
|
||||||
|
'zone_name.required' => 'ゾーン名は必須です。',
|
||||||
|
];
|
||||||
|
|
||||||
|
$validator = Validator::make($request->all(), $rules, $messages);
|
||||||
|
|
||||||
|
if ($validator->fails()) {
|
||||||
|
return redirect()->back()->withErrors($validator)->withInput();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ▼ 更新処理
|
||||||
|
DB::transaction(function () use ($request, $record) {
|
||||||
|
$record->fill($request->only([
|
||||||
|
'park_id',
|
||||||
|
'ptype_id',
|
||||||
|
'psection_id',
|
||||||
|
'zone_name',
|
||||||
|
'zone_number',
|
||||||
|
'zone_standard',
|
||||||
|
'zone_tolerance',
|
||||||
|
'zone_sort',
|
||||||
|
]));
|
||||||
|
$record->save();
|
||||||
|
});
|
||||||
|
|
||||||
|
return redirect()->route('zones')->with('success', '更新しました。');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 削除(単一/複数対応)
|
||||||
|
*/
|
||||||
|
public function delete(Request $request)
|
||||||
|
{
|
||||||
|
$ids = [];
|
||||||
|
|
||||||
|
// 単一削除(id 指定)
|
||||||
|
if ($request->filled('id')) {
|
||||||
|
$ids[] = (int) $request->input('id');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 複数削除(チェックボックス pk[])
|
||||||
|
if (is_array($request->input('pk'))) {
|
||||||
|
$ids = array_merge($ids, $request->input('pk'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重複除去 & 数値変換
|
||||||
|
$ids = array_values(array_unique(array_map('intval', $ids)));
|
||||||
|
|
||||||
|
// 削除対象がない場合
|
||||||
|
if (empty($ids)) {
|
||||||
|
return back()->with('error', '削除対象が選択されていません。');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 削除実行
|
||||||
|
Zone::whereIn('zone_id', $ids)->delete();
|
||||||
|
|
||||||
|
return redirect()->route('zones')->with('success', '削除しました。');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* バリデーション共通化
|
||||||
|
*/
|
||||||
|
private function validateZone(Request $request)
|
||||||
|
{
|
||||||
|
return $request->validate([
|
||||||
|
'zone_name' => 'required|string|max:50',
|
||||||
|
'park_id' => 'required|integer',
|
||||||
|
'ptype_id' => 'nullable|integer',
|
||||||
|
'psection_id' => 'nullable|integer',
|
||||||
|
'zone_number' => 'nullable|integer|min:0',
|
||||||
|
'zone_standard' => 'nullable|integer|min:0',
|
||||||
|
'zone_tolerance' => 'nullable|integer|min:0',
|
||||||
|
'use_flag' => 'nullable|boolean',
|
||||||
|
'memo' => 'nullable|string|max:255',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
61
app/Http/Controllers/Auth/ForgotPasswordController.php
Normal file
61
app/Http/Controllers/Auth/ForgotPasswordController.php
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Auth;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Mail;
|
||||||
|
|
||||||
|
class ForgotPasswordController extends Controller
|
||||||
|
{
|
||||||
|
// パスワードリセット申請画面表示
|
||||||
|
public function showLinkRequestForm()
|
||||||
|
{
|
||||||
|
return view('auth.forgot-password');
|
||||||
|
}
|
||||||
|
|
||||||
|
// リセットメール送信
|
||||||
|
public function sendResetLinkEmail(Request $request)
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'email' => 'required|email',
|
||||||
|
'email_confirmation' => 'required|email|same:email',
|
||||||
|
], [
|
||||||
|
'email.required' => 'メールアドレスを入力してください。',
|
||||||
|
'email.email' => '正しいメールアドレス形式で入力してください。',
|
||||||
|
'email_confirmation.required' => '確認用メールアドレスを入力してください。',
|
||||||
|
'email_confirmation.email' => '正しいメールアドレス形式で入力してください。',
|
||||||
|
'email_confirmation.same' => 'メールアドレスが一致しません。',
|
||||||
|
]);
|
||||||
|
|
||||||
|
// ope_mailでユーザーを検索
|
||||||
|
$user = \App\Models\Ope::where('ope_mail', $request->input('email'))->first();
|
||||||
|
|
||||||
|
if (!$user) {
|
||||||
|
return back()->withErrors(['email' => '該当するユーザーが見つかりません。']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// トークン生成
|
||||||
|
$token = Str::random(60);
|
||||||
|
|
||||||
|
// トークン保存(既存レコードがあれば更新)
|
||||||
|
DB::table('password_reset_tokens')->updateOrInsert(
|
||||||
|
['ope_mail' => $user->ope_mail],
|
||||||
|
[
|
||||||
|
'token' => $token,
|
||||||
|
'created_at' => now(),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
// メール送信
|
||||||
|
$resetUrl = url('/reset-password?token=' . $token . '&email=' . urlencode($user->ope_mail));
|
||||||
|
Mail::raw("下記URLからパスワード再設定を行ってください。\n\n{$resetUrl}", function ($message) use ($user) {
|
||||||
|
$message->to($user->ope_mail)
|
||||||
|
->subject('パスワード再設定のご案内');
|
||||||
|
});
|
||||||
|
|
||||||
|
return back()->with('status', 'パスワード再設定メールを送信しました。');
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -77,9 +77,13 @@ class LoginController extends Controller
|
|||||||
*/
|
*/
|
||||||
protected function validateLogin(Request $request)
|
protected function validateLogin(Request $request)
|
||||||
{
|
{
|
||||||
|
// 個別未入力メッセージ(仕様1,2)
|
||||||
$request->validate([
|
$request->validate([
|
||||||
'ope_id' => 'required|string', // オペレータID(旧システムと同じ)
|
'ope_id' => 'required|string',
|
||||||
'ope_pass' => 'required|string', // オペレータパスワード(旧システムと同じ)
|
'ope_pass' => 'required|string',
|
||||||
|
], [
|
||||||
|
'ope_id.required' => 'ログインIDが未入力です。',
|
||||||
|
'ope_pass.required' => 'パスワードが未入力です。',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,6 +95,13 @@ class LoginController extends Controller
|
|||||||
*/
|
*/
|
||||||
protected function attemptLogin(Request $request)
|
protected function attemptLogin(Request $request)
|
||||||
{
|
{
|
||||||
|
// 先にIDのみでオペレータ取得して退職フラグを確認(仕様5-1)
|
||||||
|
$opeId = $request->input('ope_id');
|
||||||
|
$operator = \App\Models\Ope::where('ope_id', $opeId)->first();
|
||||||
|
if ($operator && (int)($operator->ope_quit_flag) === 1) {
|
||||||
|
// 退職扱いは認証失敗と同じメッセージ(仕様5-1 と 3/4 統一表示)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return Auth::attempt($this->credentials($request), false);
|
return Auth::attempt($this->credentials($request), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,9 +129,9 @@ class LoginController extends Controller
|
|||||||
protected function sendLoginResponse(Request $request)
|
protected function sendLoginResponse(Request $request)
|
||||||
{
|
{
|
||||||
$request->session()->regenerate();
|
$request->session()->regenerate();
|
||||||
|
|
||||||
$this->clearLoginAttempts($request);
|
$this->clearLoginAttempts($request);
|
||||||
|
// 仕様5: ログインIDをセッション保持
|
||||||
|
$request->session()->put('login_ope_id', $request->input('ope_id'));
|
||||||
return redirect()->intended($this->redirectTo);
|
return redirect()->intended($this->redirectTo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
51
app/Http/Controllers/Auth/ResetPasswordController.php
Normal file
51
app/Http/Controllers/Auth/ResetPasswordController.php
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Auth;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
use App\Models\Ope;
|
||||||
|
|
||||||
|
class ResetPasswordController extends Controller
|
||||||
|
{
|
||||||
|
public function showResetForm(Request $request)
|
||||||
|
{
|
||||||
|
$token = $request->query('token');
|
||||||
|
$email = $request->query('email');
|
||||||
|
return view('auth.reset-password', compact('token', 'email'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function reset(Request $request)
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'email' => 'required|email',
|
||||||
|
'token' => 'required',
|
||||||
|
'password' => 'required|confirmed|min:8',
|
||||||
|
]);
|
||||||
|
|
||||||
|
// トークンチェック
|
||||||
|
$record = DB::table('password_reset_tokens')
|
||||||
|
->where('ope_mail', $request->email)
|
||||||
|
->where('token', $request->token)
|
||||||
|
->first();
|
||||||
|
|
||||||
|
if (!$record) {
|
||||||
|
return back()->withErrors(['email' => '無効なトークンまたはメールアドレスです。']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// パスワード更新
|
||||||
|
$user = Ope::where('ope_mail', $request->email)->first();
|
||||||
|
if (!$user) {
|
||||||
|
return back()->withErrors(['email' => 'ユーザーが見つかりません。']);
|
||||||
|
}
|
||||||
|
$user->password = Hash::make($request->password);
|
||||||
|
$user->save();
|
||||||
|
|
||||||
|
// トークン削除
|
||||||
|
DB::table('password_reset_tokens')->where('ope_mail', $request->email)->delete();
|
||||||
|
|
||||||
|
return redirect()->route('login')->with('status', 'パスワードを再設定しました。');
|
||||||
|
}
|
||||||
|
}
|
||||||
427
app/Http/Controllers/Webhook/WellnetController.php
Normal file
427
app/Http/Controllers/Webhook/WellnetController.php
Normal file
@ -0,0 +1,427 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Webhook;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Queue;
|
||||||
|
use App\Models\Batch\BatchLog;
|
||||||
|
use App\Models\SettlementTransaction;
|
||||||
|
use App\Models\RegularContract;
|
||||||
|
use App\Jobs\ProcessSettlementJob;
|
||||||
|
use Carbon\Carbon;
|
||||||
|
|
||||||
|
class WellnetController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Wellnet PUSH 受信 (SHJ-4A)
|
||||||
|
* 受け取った SOAP/XML を解析し、settlement_transaction に登録。
|
||||||
|
* 幂等性チェック、データ検証、キュー投入を含む完全な処理を実行。
|
||||||
|
*/
|
||||||
|
public function receive(Request $request)
|
||||||
|
{
|
||||||
|
$startedAt = now();
|
||||||
|
$raw = $request->getContent();
|
||||||
|
$md5Hash = md5($raw);
|
||||||
|
|
||||||
|
// IP白名单检查(如果配置了)
|
||||||
|
if (!$this->validateClientIp($request->ip())) {
|
||||||
|
Log::warning('SHJ-4A IP白名单验证失败', [
|
||||||
|
'ip' => $request->ip(),
|
||||||
|
'content_length' => strlen($raw),
|
||||||
|
]);
|
||||||
|
return $this->errorResponse('Unauthorized IP', 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 事前にログ記録(サイズ上限に注意)
|
||||||
|
Log::info('SHJ-4A Wellnet PUSH received', [
|
||||||
|
'length' => strlen($raw),
|
||||||
|
'content_type' => $request->header('Content-Type'),
|
||||||
|
'ip' => $request->ip(),
|
||||||
|
'md5_hash' => $md5Hash,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 共通バッチログ: start
|
||||||
|
$batch = BatchLog::createBatchLog(
|
||||||
|
'shj4a',
|
||||||
|
BatchLog::STATUS_START,
|
||||||
|
[
|
||||||
|
'ip' => $request->ip(),
|
||||||
|
'content_type' => $request->header('Content-Type'),
|
||||||
|
'content_length' => strlen($raw),
|
||||||
|
'md5_hash' => $md5Hash,
|
||||||
|
],
|
||||||
|
'SHJ-4A Wellnet PUSH start'
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 【処理1】幂等性检查 - MD5重复检查
|
||||||
|
$existingByMd5 = SettlementTransaction::where('md5_string', $md5Hash)->first();
|
||||||
|
if ($existingByMd5) {
|
||||||
|
Log::info('SHJ-4A 幂等性: MD5重复检测', [
|
||||||
|
'md5_hash' => $md5Hash,
|
||||||
|
'existing_id' => $existingByMd5->settlement_transaction_id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$batch->update([
|
||||||
|
'status' => BatchLog::STATUS_SUCCESS,
|
||||||
|
'end_time' => now(),
|
||||||
|
'message' => 'SHJ-4A 幂等性: MD5重复,直接返回成功',
|
||||||
|
'success_count' => 0, // 幂等返回不计入成功数
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $this->successResponse('処理済み(幂等性)');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 【処理2】SOAP/XML解析
|
||||||
|
$xml = @simplexml_load_string($raw);
|
||||||
|
if (!$xml) {
|
||||||
|
throw new \RuntimeException('Invalid XML/SOAP payload');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Body 以下の最初の要素を取得
|
||||||
|
$nsBody = $xml->children('http://schemas.xmlsoap.org/soap/envelope/')->Body ?? null;
|
||||||
|
$payloadNode = $nsBody ? current($nsBody->children()) : $xml; // SOAPでなければ素のXML想定
|
||||||
|
|
||||||
|
// XML -> 配列化
|
||||||
|
$payloadArray = json_decode(json_encode($payloadNode), true) ?? [];
|
||||||
|
|
||||||
|
// 【処理3】データ抽出と正規化
|
||||||
|
$data = $this->extractSettlementData($payloadArray, $md5Hash);
|
||||||
|
|
||||||
|
// 【処理4】必須フィールド検証
|
||||||
|
$this->validateRequiredFields($data);
|
||||||
|
|
||||||
|
// 【処理5】複合キー重复检查(contract_payment_number + pay_date + settlement_amount)
|
||||||
|
$existingByComposite = $this->findExistingByCompositeKey($data);
|
||||||
|
if ($existingByComposite) {
|
||||||
|
Log::info('SHJ-4A 幂等性: 複合キー重复検出', [
|
||||||
|
'contract_payment_number' => $data['contract_payment_number'],
|
||||||
|
'pay_date' => $data['pay_date'],
|
||||||
|
'settlement_amount' => $data['settlement_amount'],
|
||||||
|
'existing_id' => $existingByComposite->settlement_transaction_id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$batch->update([
|
||||||
|
'status' => BatchLog::STATUS_SUCCESS,
|
||||||
|
'end_time' => now(),
|
||||||
|
'message' => 'SHJ-4A 幂等性: 複合キー重复,直接返回成功',
|
||||||
|
'success_count' => 0,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $this->successResponse('処理済み(幂等性)');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 【処理6】データベース取込と関連処理
|
||||||
|
$settlementId = null;
|
||||||
|
DB::transaction(function() use ($data, $batch, &$settlementId) {
|
||||||
|
// 決済トランザクション登録
|
||||||
|
$settlement = SettlementTransaction::create($data);
|
||||||
|
$settlementId = $settlement->settlement_transaction_id;
|
||||||
|
|
||||||
|
// 契約テーブルの軽微な更新(SHJ-4Bで正式更新)
|
||||||
|
RegularContract::where('contract_payment_number', $data['contract_payment_number'])
|
||||||
|
->update(['contract_updated_at' => now()]);
|
||||||
|
|
||||||
|
// バッチログ成功更新
|
||||||
|
$batch->update([
|
||||||
|
'status' => BatchLog::STATUS_SUCCESS,
|
||||||
|
'end_time' => now(),
|
||||||
|
'message' => 'SHJ-4A Wellnet PUSH stored successfully',
|
||||||
|
'success_count' => 1,
|
||||||
|
'parameters' => json_encode([
|
||||||
|
'settlement_transaction_id' => $settlementId,
|
||||||
|
'contract_payment_number' => $data['contract_payment_number'],
|
||||||
|
'settlement_amount' => $data['settlement_amount'],
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
|
||||||
|
Log::info('SHJ-4A 決済トランザクション登録成功', [
|
||||||
|
'settlement_transaction_id' => $settlementId,
|
||||||
|
'contract_payment_number' => $data['contract_payment_number'],
|
||||||
|
'settlement_amount' => $data['settlement_amount'],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 【処理7】SHJ-4B用キュージョブ投入
|
||||||
|
try {
|
||||||
|
$jobContext = [
|
||||||
|
'contract_payment_number' => $data['contract_payment_number'],
|
||||||
|
'settlement_amount' => $data['settlement_amount'],
|
||||||
|
'pay_date' => $data['pay_date'],
|
||||||
|
'pay_code' => $data['pay_code'],
|
||||||
|
'triggered_by' => 'shj4a_webhook',
|
||||||
|
'triggered_at' => $startedAt->toISOString(),
|
||||||
|
];
|
||||||
|
|
||||||
|
ProcessSettlementJob::dispatch($settlementId, $jobContext);
|
||||||
|
|
||||||
|
Log::info('SHJ-4A ProcessSettlementJob投入成功', [
|
||||||
|
'settlement_transaction_id' => $settlementId,
|
||||||
|
'job_context' => $jobContext,
|
||||||
|
]);
|
||||||
|
|
||||||
|
} catch (\Throwable $jobError) {
|
||||||
|
// キュー投入失敗は警告レベル(メイン処理は成功済み)
|
||||||
|
Log::warning('SHJ-4A ProcessSettlementJob投入失敗', [
|
||||||
|
'settlement_transaction_id' => $settlementId,
|
||||||
|
'error' => $jobError->getMessage(),
|
||||||
|
'note' => '兜底巡検で処理される予定',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->successResponse();
|
||||||
|
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
Log::error('SHJ-4A error', [
|
||||||
|
'error' => $e->getMessage(),
|
||||||
|
'trace' => $e->getTraceAsString(),
|
||||||
|
'md5_hash' => $md5Hash,
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (isset($batch)) {
|
||||||
|
$batch->update([
|
||||||
|
'status' => BatchLog::STATUS_ERROR,
|
||||||
|
'end_time' => now(),
|
||||||
|
'message' => 'SHJ-4A failed: ' . $e->getMessage(),
|
||||||
|
'error_details' => $e->getTraceAsString(),
|
||||||
|
'error_count' => 1,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->errorResponse($e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IP白名单验证
|
||||||
|
*
|
||||||
|
* @param string $clientIp
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
private function validateClientIp(string $clientIp): bool
|
||||||
|
{
|
||||||
|
$whitelist = config('services.wellnet.ip_whitelist', '');
|
||||||
|
|
||||||
|
if (empty($whitelist)) {
|
||||||
|
return true; // 白名单为空时不验证
|
||||||
|
}
|
||||||
|
|
||||||
|
$allowedIps = array_map('trim', explode(',', $whitelist));
|
||||||
|
|
||||||
|
foreach ($allowedIps as $allowedIp) {
|
||||||
|
if (strpos($allowedIp, '/') !== false) {
|
||||||
|
// CIDR記法対応
|
||||||
|
if ($this->ipInRange($clientIp, $allowedIp)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 直接IP比較
|
||||||
|
if ($clientIp === $allowedIp) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CIDR範囲でのIP检查
|
||||||
|
*
|
||||||
|
* @param string $ip
|
||||||
|
* @param string $range
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
private function ipInRange(string $ip, string $range): bool
|
||||||
|
{
|
||||||
|
list($subnet, $bits) = explode('/', $range);
|
||||||
|
$ip = ip2long($ip);
|
||||||
|
$subnet = ip2long($subnet);
|
||||||
|
$mask = -1 << (32 - $bits);
|
||||||
|
$subnet &= $mask; # nb: in case the supplied subnet wasn't correctly aligned
|
||||||
|
return ($ip & $mask) == $subnet;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 決済データの抽出と正規化
|
||||||
|
*
|
||||||
|
* @param array $payloadArray
|
||||||
|
* @param string $md5Hash
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function extractSettlementData(array $payloadArray, string $md5Hash): array
|
||||||
|
{
|
||||||
|
// inData/Result系の取り出し(キー名差異に寛容)
|
||||||
|
$first = function(array $arr, array $keys, $default = null) {
|
||||||
|
foreach ($keys as $k) {
|
||||||
|
if (isset($arr[$k])) return is_array($arr[$k]) ? $arr[$k] : (string)$arr[$k];
|
||||||
|
}
|
||||||
|
return $default;
|
||||||
|
};
|
||||||
|
|
||||||
|
$flat = $payloadArray;
|
||||||
|
// よくある入れ子: { YoyakuNyukin: { inData: {...} } } / { YoyakuNyukinResponse: { YoyakuNyukinResult: {...} } }
|
||||||
|
foreach (['inData','YoyakuSyunoBarCodeResult','YoyakuNyukinResult','YoyakuSyunoETicketResult'] as $k) {
|
||||||
|
if (isset($flat[$k]) && is_array($flat[$k])) { $flat = $flat[$k]; }
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'pay_code' => $first($flat, ['NyukinPayCode','SyunoPayCode','BcPayCode']),
|
||||||
|
'contract_payment_number' => $first($flat, ['NyukinRecvNum','SyunoRecvNum','RecvNum','contract_payment_number']),
|
||||||
|
'corp_code' => $first($flat, ['NyukinCorpCode','SyunoCorpCode','BcCorpCode','CorpCode']),
|
||||||
|
'mms_date' => $first($flat, ['NyukinReferDate','SyunoMMSNo','MmsDate']),
|
||||||
|
'cvs_code' => $first($flat, ['NyukinCvsCode','CvsCode']),
|
||||||
|
'shop_code' => $first($flat, ['NyukinShopCode','ShopCode']),
|
||||||
|
'pay_date' => $first($flat, ['NyukinPaidDate','PaidDate']),
|
||||||
|
'settlement_amount' => $first($flat, ['NyukinPaidAmount','SyunoPayAmount','PaidAmount']),
|
||||||
|
'stamp_flag' => $first($flat, ['NyukinInshiFlag','InshiFlag']),
|
||||||
|
'status' => 'received',
|
||||||
|
'md5_string' => $md5Hash,
|
||||||
|
];
|
||||||
|
|
||||||
|
// データ正規化処理
|
||||||
|
$data = $this->normalizeSettlementData($data);
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 決済データの正規化
|
||||||
|
*
|
||||||
|
* @param array $data
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function normalizeSettlementData(array $data): array
|
||||||
|
{
|
||||||
|
// 金額を数値化(非負数)
|
||||||
|
if (!empty($data['settlement_amount'])) {
|
||||||
|
$amount = preg_replace('/[^\d.]/', '', $data['settlement_amount']);
|
||||||
|
$data['settlement_amount'] = max(0, (float)$amount);
|
||||||
|
} else {
|
||||||
|
$data['settlement_amount'] = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 支払日時の正規化
|
||||||
|
if (!empty($data['pay_date'])) {
|
||||||
|
try {
|
||||||
|
$data['pay_date'] = Carbon::parse($data['pay_date'])->format('Y-m-d H:i:s');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
Log::warning('SHJ-4A 支払日時解析失敗', [
|
||||||
|
'original_pay_date' => $data['pay_date'],
|
||||||
|
'error' => $e->getMessage(),
|
||||||
|
]);
|
||||||
|
$data['pay_date'] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 文字列フィールドのトリム
|
||||||
|
$stringFields = ['pay_code', 'contract_payment_number', 'corp_code', 'mms_date', 'cvs_code', 'shop_code', 'stamp_flag'];
|
||||||
|
foreach ($stringFields as $field) {
|
||||||
|
if (isset($data[$field])) {
|
||||||
|
$data[$field] = trim($data[$field]) ?: null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 必須フィールドの検証
|
||||||
|
*
|
||||||
|
* @param array $data
|
||||||
|
* @throws \RuntimeException
|
||||||
|
*/
|
||||||
|
private function validateRequiredFields(array $data): void
|
||||||
|
{
|
||||||
|
// 必須フィールドのチェック
|
||||||
|
if (empty($data['contract_payment_number'])) {
|
||||||
|
throw new \RuntimeException('必須フィールドが不足: contract_payment_number (RecvNum)');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($data['settlement_amount']) || $data['settlement_amount'] === null) {
|
||||||
|
throw new \RuntimeException('必須フィールドが不足: settlement_amount');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($data['pay_date'])) {
|
||||||
|
throw new \RuntimeException('必須フィールドが不足: pay_date');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 複合キーによる既存レコード検索
|
||||||
|
*
|
||||||
|
* @param array $data
|
||||||
|
* @return SettlementTransaction|null
|
||||||
|
*/
|
||||||
|
private function findExistingByCompositeKey(array $data): ?SettlementTransaction
|
||||||
|
{
|
||||||
|
return SettlementTransaction::where('contract_payment_number', $data['contract_payment_number'])
|
||||||
|
->where('pay_date', $data['pay_date'])
|
||||||
|
->where('settlement_amount', $data['settlement_amount'])
|
||||||
|
->first();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 成功レスポンスの生成
|
||||||
|
*
|
||||||
|
* @param string $message
|
||||||
|
* @return \Illuminate\Http\Response
|
||||||
|
*/
|
||||||
|
private function successResponse(string $message = '正常処理'): \Illuminate\Http\Response
|
||||||
|
{
|
||||||
|
$responseFormat = config('services.wellnet.response_format', 'json');
|
||||||
|
|
||||||
|
if ($responseFormat === 'soap') {
|
||||||
|
return $this->soapResponse(0, $message);
|
||||||
|
} else {
|
||||||
|
return response()->json(['result' => 0, 'message' => $message]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* エラーレスポンスの生成
|
||||||
|
*
|
||||||
|
* @param string $message
|
||||||
|
* @param int $httpCode
|
||||||
|
* @return \Illuminate\Http\Response
|
||||||
|
*/
|
||||||
|
private function errorResponse(string $message, int $httpCode = 500): \Illuminate\Http\Response
|
||||||
|
{
|
||||||
|
$responseFormat = config('services.wellnet.response_format', 'json');
|
||||||
|
|
||||||
|
if ($responseFormat === 'soap') {
|
||||||
|
return $this->soapResponse(1, $message, $httpCode);
|
||||||
|
} else {
|
||||||
|
$resultCode = ($httpCode >= 500) ? 1 : 2; // サーバーエラー:1, クライアントエラー:2
|
||||||
|
return response()->json(['result' => $resultCode, 'error' => $message], $httpCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SOAP形式のレスポンス生成
|
||||||
|
*
|
||||||
|
* @param int $resultCode
|
||||||
|
* @param string $message
|
||||||
|
* @param int $httpCode
|
||||||
|
* @return \Illuminate\Http\Response
|
||||||
|
*/
|
||||||
|
private function soapResponse(int $resultCode, string $message, int $httpCode = 200): \Illuminate\Http\Response
|
||||||
|
{
|
||||||
|
$soapEnvelope = '<?xml version="1.0" encoding="utf-8"?>'
|
||||||
|
. '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">'
|
||||||
|
. '<soap:Body>'
|
||||||
|
. '<WellnetPushResponse>'
|
||||||
|
. '<Result>' . htmlspecialchars($resultCode) . '</Result>'
|
||||||
|
. '<Message>' . htmlspecialchars($message) . '</Message>'
|
||||||
|
. '</WellnetPushResponse>'
|
||||||
|
. '</soap:Body>'
|
||||||
|
. '</soap:Envelope>';
|
||||||
|
|
||||||
|
return response($soapEnvelope, $httpCode)
|
||||||
|
->header('Content-Type', 'text/xml; charset=utf-8');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
27
app/Http/Requests/RegularTypeRequest.php
Normal file
27
app/Http/Requests/RegularTypeRequest.php
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
|
||||||
|
class RegularTypeRequest extends FormRequest
|
||||||
|
{
|
||||||
|
public function authorize()
|
||||||
|
{
|
||||||
|
return true; // 認証はコントローラーで行うため、ここでは常にtrueを返す
|
||||||
|
}
|
||||||
|
|
||||||
|
public function rules()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'city_id' => 'required|string|max:255',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function messages()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'city_id.required' => '市区名は必須です。',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
183
app/Jobs/ProcessSettlementJob.php
Normal file
183
app/Jobs/ProcessSettlementJob.php
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use App\Models\Batch\BatchLog;
|
||||||
|
use App\Models\SettlementTransaction;
|
||||||
|
use App\Services\ShjFourBService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SHJ-4B 決済トランザクション処理ジョブ
|
||||||
|
*
|
||||||
|
* SHJ-4Aで登録された決済情報を基に定期契約の更新処理を行う
|
||||||
|
*/
|
||||||
|
class ProcessSettlementJob implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ジョブの実行可能回数
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
public $tries = 3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ジョブの実行間隔(秒)
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
public $backoff = [60, 300, 900];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用するキュー名
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public $queue = 'settlement';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 決済トランザクションID
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $settlementTransactionId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 追加のコンテキスト情報
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $context;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* コンストラクタ
|
||||||
|
*
|
||||||
|
* @param int $settlementTransactionId 決済トランザクションID
|
||||||
|
* @param array $context 追加のコンテキスト情報
|
||||||
|
*/
|
||||||
|
public function __construct(int $settlementTransactionId, array $context = [])
|
||||||
|
{
|
||||||
|
$this->settlementTransactionId = $settlementTransactionId;
|
||||||
|
$this->context = $context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ジョブを実行
|
||||||
|
*
|
||||||
|
* SHJ-4Bサービスを使用して決済トランザクション処理を実行
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$startTime = now();
|
||||||
|
|
||||||
|
// バッチログの開始記録
|
||||||
|
$batch = BatchLog::createBatchLog(
|
||||||
|
'shj4b',
|
||||||
|
BatchLog::STATUS_START,
|
||||||
|
[
|
||||||
|
'settlement_transaction_id' => $this->settlementTransactionId,
|
||||||
|
'context' => $this->context,
|
||||||
|
'job_id' => $this->job->getJobId(),
|
||||||
|
],
|
||||||
|
'SHJ-4B ProcessSettlementJob start'
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
Log::info('SHJ-4B ProcessSettlementJob開始', [
|
||||||
|
'settlement_transaction_id' => $this->settlementTransactionId,
|
||||||
|
'context' => $this->context,
|
||||||
|
'start_time' => $startTime,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// SHJ-4Bサービスを使用して決済トランザクション処理を実行
|
||||||
|
$shjFourBService = app(ShjFourBService::class);
|
||||||
|
$result = $shjFourBService->processSettlementTransaction(
|
||||||
|
$this->settlementTransactionId,
|
||||||
|
$this->context
|
||||||
|
);
|
||||||
|
|
||||||
|
// 処理結果に基づいてバッチログを更新
|
||||||
|
if ($result['success']) {
|
||||||
|
$batch->update([
|
||||||
|
'status' => BatchLog::STATUS_SUCCESS,
|
||||||
|
'end_time' => now(),
|
||||||
|
'message' => 'SHJ-4B ProcessSettlementJob completed successfully',
|
||||||
|
'success_count' => 1,
|
||||||
|
'parameters' => json_encode([
|
||||||
|
'result' => $result,
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
|
||||||
|
Log::info('SHJ-4B ProcessSettlementJob完了', [
|
||||||
|
'settlement_transaction_id' => $this->settlementTransactionId,
|
||||||
|
'execution_time' => now()->diffInSeconds($startTime),
|
||||||
|
'result' => $result,
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
// ビジネスロジック上の問題(エラーではない)
|
||||||
|
$batch->update([
|
||||||
|
'status' => BatchLog::STATUS_SUCCESS,
|
||||||
|
'end_time' => now(),
|
||||||
|
'message' => 'SHJ-4B ProcessSettlementJob completed with issues: ' . $result['reason'],
|
||||||
|
'success_count' => 0,
|
||||||
|
'parameters' => json_encode([
|
||||||
|
'result' => $result,
|
||||||
|
'requires_manual_action' => true,
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
|
||||||
|
Log::warning('SHJ-4B ProcessSettlementJob要手動対応', [
|
||||||
|
'settlement_transaction_id' => $this->settlementTransactionId,
|
||||||
|
'result' => $result,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
Log::error('SHJ-4B ProcessSettlementJob失敗', [
|
||||||
|
'settlement_transaction_id' => $this->settlementTransactionId,
|
||||||
|
'error' => $e->getMessage(),
|
||||||
|
'trace' => $e->getTraceAsString(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// バッチログのエラー記録
|
||||||
|
$batch->update([
|
||||||
|
'status' => BatchLog::STATUS_ERROR,
|
||||||
|
'end_time' => now(),
|
||||||
|
'message' => 'SHJ-4B ProcessSettlementJob failed: ' . $e->getMessage(),
|
||||||
|
'error_details' => $e->getTraceAsString(),
|
||||||
|
'error_count' => 1,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// ジョブを失敗させて再試行を促す
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ジョブが失敗した場合の処理
|
||||||
|
*
|
||||||
|
* @param \Throwable $exception
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function failed(\Throwable $exception)
|
||||||
|
{
|
||||||
|
Log::error('SHJ-4B ProcessSettlementJob最終失敗', [
|
||||||
|
'settlement_transaction_id' => $this->settlementTransactionId,
|
||||||
|
'context' => $this->context,
|
||||||
|
'error' => $exception->getMessage(),
|
||||||
|
'attempts' => $this->attempts(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 最終失敗時の追加処理があればここに記述
|
||||||
|
// 例:管理者への通知、障害キューへの登録など
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -66,7 +66,6 @@ class User extends Model
|
|||||||
'user_remarks',
|
'user_remarks',
|
||||||
'user_age',
|
'user_age',
|
||||||
];
|
];
|
||||||
|
|
||||||
protected static function boot()
|
protected static function boot()
|
||||||
{
|
{
|
||||||
parent::boot();
|
parent::boot();
|
||||||
|
|||||||
@ -7,9 +7,9 @@ use Illuminate\Database\Eloquent\Model;
|
|||||||
class City extends Model
|
class City extends Model
|
||||||
{
|
{
|
||||||
protected $table = 'city';
|
protected $table = 'city';
|
||||||
public $timestamps = true;
|
protected $primaryKey = 'city_id';
|
||||||
|
protected $keyType = 'int';
|
||||||
protected $primaryKey = 'city_id';
|
public $incrementing = true;
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'city_id',
|
'city_id',
|
||||||
'city_name',
|
'city_name',
|
||||||
@ -23,12 +23,14 @@ class City extends Model
|
|||||||
/**
|
/**
|
||||||
* 都市のリストを取得
|
* 都市のリストを取得
|
||||||
*/
|
*/
|
||||||
public static function getList()
|
public static function getList(?int $operatorId = null): array
|
||||||
{
|
{
|
||||||
return self::all();
|
return static::query()
|
||||||
|
->when($operatorId, fn ($q) => $q->where('operator_id', $operatorId))
|
||||||
|
->orderBy('city_name')
|
||||||
|
->pluck('city_name', 'city_id')
|
||||||
|
->toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
@ -28,20 +28,27 @@ class ContractAllowableCity extends Model
|
|||||||
*/
|
*/
|
||||||
public static function search($inputs)
|
public static function search($inputs)
|
||||||
{
|
{
|
||||||
$list = self::query();
|
$list = self::query()
|
||||||
|
->leftJoin('park', 'contract_allowable_city.park_id', '=', 'park.park_id')
|
||||||
|
->leftJoin('city', 'contract_allowable_city.city_id', '=', 'city.city_id')
|
||||||
|
->select(
|
||||||
|
'contract_allowable_city.*',
|
||||||
|
'park.park_name',
|
||||||
|
'city.city_name'
|
||||||
|
);
|
||||||
|
|
||||||
if ($inputs['isMethodPost'] ?? false) {
|
if ($inputs['isMethodPost'] ?? false) {
|
||||||
if (!empty($inputs['contract_allowable_city_id'])) {
|
if (!empty($inputs['contract_allowable_city_id'])) {
|
||||||
$list->where('contract_allowable_city_id', $inputs['contract_allowable_city_id']);
|
$list->where('contract_allowable_city.contract_allowable_city_id', $inputs['contract_allowable_city_id']);
|
||||||
}
|
}
|
||||||
if (!empty($inputs['city_id'])) {
|
if (!empty($inputs['city_id'])) {
|
||||||
$list->where('city_id', $inputs['city_id']);
|
$list->where('contract_allowable_city.city_id', $inputs['city_id']);
|
||||||
}
|
}
|
||||||
if (!empty($inputs['contract_allowable_city_name'])) {
|
if (!empty($inputs['contract_allowable_city_name'])) {
|
||||||
$list->where('contract_allowable_city_name', 'like', '%' . $inputs['contract_allowable_city_name'] . '%');
|
$list->where('contract_allowable_city.contract_allowable_city_name', 'like', '%' . $inputs['contract_allowable_city_name'] . '%');
|
||||||
}
|
}
|
||||||
if (!empty($inputs['park_id'])) {
|
if (!empty($inputs['park_id'])) {
|
||||||
$list->where('park_id', $inputs['park_id']);
|
$list->where('contract_allowable_city.park_id', $inputs['park_id']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,6 +64,7 @@ class ContractAllowableCity extends Model
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 主キーで取得
|
* 主キーで取得
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -41,4 +41,9 @@ class Device extends Model
|
|||||||
{
|
{
|
||||||
return static::orderBy('device_subject')->pluck('device_subject', 'device_id')->toArray();
|
return static::orderBy('device_subject')->pluck('device_subject', 'device_id')->toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function deleteByPk(array $ids): int
|
||||||
|
{
|
||||||
|
return static::whereIn('device_id', $ids)->delete();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
25
app/Models/InvSetting.php
Normal file
25
app/Models/InvSetting.php
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class InvSetting extends Model
|
||||||
|
{
|
||||||
|
protected $table = 'inv_setting';
|
||||||
|
protected $primaryKey = 'seq';
|
||||||
|
public $timestamps = true;
|
||||||
|
|
||||||
|
protected $fillable = [
|
||||||
|
|
||||||
|
't_number', // 適格事業者番号
|
||||||
|
't_name', // 事業者名
|
||||||
|
'zipcode', // 郵便番号
|
||||||
|
'adrs', // 住所
|
||||||
|
'bldg', // 建物名
|
||||||
|
'tel_num', // 電話番号
|
||||||
|
'fax_num', // FAX番号
|
||||||
|
'company_image_path', // 会社ロゴ画像パス(任意)
|
||||||
|
|
||||||
|
];
|
||||||
|
}
|
||||||
@ -33,29 +33,31 @@ class Ope extends Authenticatable
|
|||||||
*/
|
*/
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'//TODO オペレータID not found in database specs',
|
'//TODO オペレータID not found in database specs',
|
||||||
'ope_name', // オペレータ名
|
'login_id', // ログインID
|
||||||
'ope_type', // オペレータ種別
|
'password', // パスワード
|
||||||
'ope_mail', // メールアドレス
|
'ope_name', // オペレータ名
|
||||||
'ope_phone', // 電話番号
|
'ope_type', // オペレータ種別
|
||||||
'ope_sendalart_que1', // キュー1アラート送信
|
'ope_mail', // メールアドレス(複数可)
|
||||||
'ope_sendalart_que2', // キュー2アラート送信
|
'ope_phone', // 電話番号
|
||||||
'ope_sendalart_que3', // キュー3アラート送信
|
'ope_sendalart_que1',
|
||||||
'ope_sendalart_que4', // キュー4アラート送信
|
'ope_sendalart_que2',
|
||||||
'ope_sendalart_que5', // キュー5アラート送信
|
'ope_sendalart_que3',
|
||||||
'ope_sendalart_que6', // キュー6アラート送信
|
'ope_sendalart_que4',
|
||||||
'ope_sendalart_que7', // キュー7アラート送信
|
'ope_sendalart_que5',
|
||||||
'ope_sendalart_que8', // キュー8アラート送信
|
'ope_sendalart_que6',
|
||||||
'ope_sendalart_que9', // キュー9アラート送信
|
'ope_sendalart_que7',
|
||||||
'ope_sendalart_que10', // キュー10アラート送信
|
'ope_sendalart_que8',
|
||||||
'ope_sendalart_que11', // キュー11アラート送信
|
'ope_sendalart_que9',
|
||||||
'ope_sendalart_que12', // キュー12アラート送信
|
'ope_sendalart_que10',
|
||||||
'ope_sendalart_que13', // キュー13アラート送信
|
'ope_sendalart_que11',
|
||||||
'ope_auth1', // 権限1
|
'ope_sendalart_que12',
|
||||||
'ope_auth2', // 権限2
|
'ope_sendalart_que13',
|
||||||
'ope_auth3', // 権限3
|
'ope_auth1',
|
||||||
'ope_auth4', // 権限4
|
'ope_auth2',
|
||||||
'ope_quit_flag', // 退職フラグ
|
'ope_auth3',
|
||||||
'ope_quitday' // 退職日
|
'ope_auth4',
|
||||||
|
'ope_quit_flag',
|
||||||
|
'ope_quitday',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -124,7 +126,7 @@ class Ope extends Authenticatable
|
|||||||
|
|
||||||
// POST検索条件の処理
|
// POST検索条件の処理
|
||||||
if ($inputs['isMethodPost']) {
|
if ($inputs['isMethodPost']) {
|
||||||
// 検索条件があればここに追加
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ソート処理
|
// ソート処理
|
||||||
@ -136,12 +138,14 @@ class Ope extends Authenticatable
|
|||||||
if ($inputs['isExport']) {
|
if ($inputs['isExport']) {
|
||||||
$list = $list->get();
|
$list = $list->get();
|
||||||
} else {
|
} else {
|
||||||
$list = $list->paginate(\App\Utils::item_per_page);
|
// ページネーション件数を20に固定
|
||||||
|
$list = $list->paginate(20);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $list;
|
return $list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* プライマリキーでオペレータを取得
|
* プライマリキーでオペレータを取得
|
||||||
*
|
*
|
||||||
|
|||||||
25
app/Models/OperatorLog.php
Normal file
25
app/Models/OperatorLog.php
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class OperatorLog extends Model
|
||||||
|
{
|
||||||
|
protected $table = 'operator_log';
|
||||||
|
protected $primaryKey = 'operator_log_id';
|
||||||
|
public $timestamps = false;
|
||||||
|
protected $fillable = [
|
||||||
|
'operator_id',
|
||||||
|
'remote_ip',
|
||||||
|
'browser_user_agent',
|
||||||
|
'user_id',
|
||||||
|
'contract_id',
|
||||||
|
'operation_code',
|
||||||
|
'operation_comment',
|
||||||
|
'operation_form_name',
|
||||||
|
'operation_table_name',
|
||||||
|
'created_at',
|
||||||
|
'updated_at',
|
||||||
|
];
|
||||||
|
}
|
||||||
@ -28,13 +28,16 @@ class OperatorQue extends Model
|
|||||||
1 => '本人確認(社会人)',
|
1 => '本人確認(社会人)',
|
||||||
2 => '本人確認(学生)',
|
2 => '本人確認(学生)',
|
||||||
3 => 'タグ発送',
|
3 => 'タグ発送',
|
||||||
4 => '予約告知電話',
|
4 => '予約告知通知',
|
||||||
5 => '定期更新電話',
|
5 => '定期更新通知',
|
||||||
6 => '返金',
|
6 => '返金処理',
|
||||||
7 => '再発行リミット超過',
|
7 => '再発行リミット超過',
|
||||||
8 => '支払い催促',
|
8 => '支払い催促',
|
||||||
9 => 'シール発行催促',
|
9 => 'シール発行催促',
|
||||||
10 => 'シール再発行許可',
|
10 => 'シール再発行',
|
||||||
|
11 => '名寄せフリガナ照合エラー',
|
||||||
|
12 => '本人確認(減免更新)',
|
||||||
|
13 => '本人確認(学生更新)',
|
||||||
101 => 'サーバーエラー',
|
101 => 'サーバーエラー',
|
||||||
102 => 'プリンタエラー',
|
102 => 'プリンタエラー',
|
||||||
103 => 'スキャナーエラー',
|
103 => 'スキャナーエラー',
|
||||||
@ -51,7 +54,7 @@ class OperatorQue extends Model
|
|||||||
|
|
||||||
public function getQueClassLabel(): string
|
public function getQueClassLabel(): string
|
||||||
{
|
{
|
||||||
return self::QueClass[$this->que_class] ?? (string)$this->que_class;
|
return self::QueClass[$this->que_class] ?? 'キュー種別未設定';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getQueStatusLabel(): string
|
public function getQueStatusLabel(): string
|
||||||
|
|||||||
@ -2,86 +2,142 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Utils;
|
||||||
|
use App\Models\City;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
class Park extends Model
|
class Park extends Model
|
||||||
{
|
{
|
||||||
|
const CREATED_AT = 'created_at';
|
||||||
|
const UPDATED_AT = 'updated_at';
|
||||||
|
|
||||||
protected $table = 'park';
|
protected $table = 'park';
|
||||||
protected $primaryKey = 'park_id';
|
protected $primaryKey = 'park_id';
|
||||||
public $timestamps = true;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The attributes that are mass assignable.
|
|
||||||
*
|
|
||||||
* @var list<string>
|
|
||||||
*/
|
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'park_name',
|
'park_id', // 駐輪場ID
|
||||||
'park_address',
|
'city_id', // 市区
|
||||||
'park_phone',
|
'park_name', // 駐輪場名
|
||||||
'park_description',
|
'park_ruby', // 駐輪場ふりがな
|
||||||
'park_status',
|
'park_syllabary', // 駐輪場五十音
|
||||||
'park_capacity',
|
'park_adrs', // 住所
|
||||||
'park_price',
|
'park_close_flag', // 閉設フラグ
|
||||||
'park_operating_hours',
|
'park_day', // 閉設日
|
||||||
|
'price_memo', // 価格メモ
|
||||||
|
'alert_flag', // 残警告チェックフラグ
|
||||||
|
'print_number', // 印字数
|
||||||
|
'keep_alive', // 最新キープアライブ
|
||||||
|
'renew_start_date', // 更新期間開始日
|
||||||
|
'renew_start_time', // 更新期間開始時
|
||||||
|
'renew_end_date', // 更新期間終了日
|
||||||
|
'renew_end_time', // 更新期間終了時
|
||||||
|
'parking_start_period', // 駐輪開始期間
|
||||||
|
'reminder_type', // リマインダー種別
|
||||||
|
'reminder_time', // リマインダー時間
|
||||||
|
'immediate_use_after_contract', // 契約後即利用許可
|
||||||
|
'display_gender', // 項目表示設定:性別
|
||||||
|
'display_birthday', // 項目表示設定:生年月日
|
||||||
|
'display_security_registration_number', // 項目表示設定:防犯登録番号
|
||||||
|
'distance_between_two_points', // 二点間距離
|
||||||
|
'latitude', // 駐車場座標(緯度)
|
||||||
|
'longitude', // 駐車場座標(経度)
|
||||||
|
'phone_number', // 電話番号
|
||||||
|
'contract_type_regular', // 駐輪場契約形態(定期)
|
||||||
|
'contract_type_temporary', // 駐輪場契約形態(一時利用)
|
||||||
|
'vehicle_type_limit', // 車種制限
|
||||||
|
'procedure_method', // 手続方法
|
||||||
|
'payment_method', // 支払方法
|
||||||
|
'usage_time_limit_flag', // 利用可能時間制限フラグ
|
||||||
|
'usage_time_start', // 利用可能時間(開始)
|
||||||
|
'usage_time_end', // 利用可能時間(終了)
|
||||||
|
'resident_manager_flag', // 常駐管理人フラグ
|
||||||
|
'resident_time_start', // 常駐時間(開始)
|
||||||
|
'resident_time_end', // 常駐時間(終了)
|
||||||
|
'roof_flag', // 屋根フラグ
|
||||||
|
'seal_issuing_machine_flag', // シール発行機フラグ
|
||||||
|
'usage_method', // 駐輪場利用方法
|
||||||
|
'periodic_update_period', // 定期更新期間
|
||||||
|
'waiting_reservation', // 空き待ち予約
|
||||||
|
'special_notes', // 特記事項
|
||||||
|
'student_id_confirmation_type', // 学生証確認種別
|
||||||
|
'reduction_guide_display_flag', // 減免案内表示フラグ
|
||||||
|
'reduction_target_age', // 減免対象年齢
|
||||||
|
'reduction_guide_display_start_month', // 減免案内表示開始月数
|
||||||
|
'cross_year', // 年跨ぎ
|
||||||
|
'reverse_use_general', // 逆利用一般
|
||||||
|
'reverse_use_student' // 逆利用学生
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
|
||||||
* 駐車場検索
|
|
||||||
*/
|
|
||||||
public static function search($inputs)
|
public static function search($inputs)
|
||||||
{
|
{
|
||||||
$query = self::query();
|
$list = self::query();
|
||||||
|
if ($inputs['isMethodPost']) {
|
||||||
// 検索条件の適用
|
|
||||||
if (!empty($inputs['park_name'])) {
|
|
||||||
$query->where('park_name', 'like', '%' . $inputs['park_name'] . '%');
|
|
||||||
}
|
}
|
||||||
if (!empty($inputs['park_address'])) {
|
// Sort
|
||||||
$query->where('park_address', 'like', '%' . $inputs['park_address'] . '%');
|
if ($inputs['sort']) {
|
||||||
|
$list->orderBy($inputs['sort'], $inputs['sort_type']);
|
||||||
}
|
}
|
||||||
if (isset($inputs['park_status']) && $inputs['park_status'] !== '') {
|
if ($inputs['isExport']){
|
||||||
$query->where('park_status', $inputs['park_status']);
|
$list = $list->get();
|
||||||
|
}else{
|
||||||
|
$list = $list->paginate(Utils::item_per_page);
|
||||||
}
|
}
|
||||||
|
return $list;
|
||||||
// ソート
|
|
||||||
if (!empty($inputs['sort'])) {
|
|
||||||
$sortType = !empty($inputs['sort_type']) ? $inputs['sort_type'] : 'asc';
|
|
||||||
$query->orderBy($inputs['sort'], $sortType);
|
|
||||||
} else {
|
|
||||||
$query->orderBy('park_id', 'desc');
|
|
||||||
}
|
|
||||||
|
|
||||||
// エクスポート用の場合はページネーションしない
|
|
||||||
if (!empty($inputs['isExport'])) {
|
|
||||||
return $query->get();
|
|
||||||
}
|
|
||||||
|
|
||||||
// ページネーション(Utilsクラスの定数を使用)
|
|
||||||
return $query->paginate(\App\Utils::item_per_page);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static function getByPk($pk)
|
||||||
* IDで駐車場を取得
|
|
||||||
*/
|
|
||||||
public static function getParkById($id)
|
|
||||||
{
|
{
|
||||||
return self::find($id);
|
return self::find($pk);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function deleteByPk($arr)
|
||||||
|
{
|
||||||
|
return self::whereIn('park_id', $arr)->delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static function boot()
|
||||||
* 駐車場リストを取得(ドロップダウン用)
|
|
||||||
*/
|
|
||||||
public static function getList()
|
|
||||||
{
|
{
|
||||||
return self::pluck('park_name', 'park_id')->toArray();
|
parent::boot();
|
||||||
|
self::creating(function (Park $model) {
|
||||||
|
$model->operator_id = Auth::user()->ope_id ?? null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GET 閉設フラグ
|
||||||
|
*/
|
||||||
|
public function getParkCloseFlagDisplay() {
|
||||||
|
if($this->park_close_flag == 1) {
|
||||||
|
return '閉設';
|
||||||
|
}
|
||||||
|
else if($this->park_close_flag == 0) {
|
||||||
|
return '開設';
|
||||||
|
}
|
||||||
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function getCity()
|
||||||
* 定期契約とのリレーション
|
|
||||||
*/
|
|
||||||
public function regularContracts()
|
|
||||||
{
|
{
|
||||||
return $this->hasMany(RegularContract::class, 'park_id', 'park_id');
|
// city_id => city_id (City モデル要有 city_id PK)
|
||||||
|
return $this->belongsTo(City::class, 'city_id', 'city_id')->first();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function getList(){
|
||||||
|
return self::pluck('park_name','park_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getIdByName($park_name){
|
||||||
|
return self::where('park_name',$park_name)->pluck('park_id')->first();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 料金設定との関連付け
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||||
|
*/
|
||||||
|
public function prices()
|
||||||
|
{
|
||||||
|
return $this->hasMany(PriceA::class, 'park_id', 'park_id');
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -65,11 +65,15 @@ class Pplace extends Model
|
|||||||
/**
|
/**
|
||||||
* 主キー配列で一括削除
|
* 主キー配列で一括削除
|
||||||
*/
|
*/
|
||||||
public static function deleteByPk($arr)
|
public static function deleteByPk($ids)
|
||||||
{
|
{
|
||||||
return self::whereIn('pplace_id', $arr)->delete();
|
if (!is_array($ids)) {
|
||||||
|
$ids = [$ids];
|
||||||
|
}
|
||||||
|
return self::whereIn('pplace_id', $ids)->delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 選択リスト取得用(フォーム等)
|
* 選択リスト取得用(フォーム等)
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -17,10 +17,11 @@ class Price extends Model
|
|||||||
5 => '12ヶ月',
|
5 => '12ヶ月',
|
||||||
];
|
];
|
||||||
|
|
||||||
protected $table = 'price_a';
|
protected $table = 'price';
|
||||||
protected $primaryKey = 'price_parkplaceid';
|
protected $primaryKey = 'price_parkplaceid';
|
||||||
|
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
|
'price_parkplaceid',
|
||||||
'prine_name',
|
'prine_name',
|
||||||
'price_month',
|
'price_month',
|
||||||
'park_id',
|
'park_id',
|
||||||
@ -41,36 +42,48 @@ class Price extends Model
|
|||||||
|
|
||||||
public static function search($inputs)
|
public static function search($inputs)
|
||||||
{
|
{
|
||||||
$list = self::query();
|
$query = self::query()
|
||||||
// 只有在sort是有效字段时才排序
|
->select(
|
||||||
|
'price.*',
|
||||||
|
\DB::raw("CONCAT_WS('/', usertype.usertype_subject1, usertype.usertype_subject2, usertype.usertype_subject3) as user_category_name"),
|
||||||
|
'psection.psection_subject',
|
||||||
|
'ptype.ptype_subject'
|
||||||
|
)
|
||||||
|
->leftJoin('usertype', 'price.user_categoryid', '=', 'usertype.user_categoryid')
|
||||||
|
->leftJoin('psection', 'price.psection_id', '=', 'psection.psection_id')
|
||||||
|
->leftJoin('ptype', 'price.price_ptypeid', '=', 'ptype.ptype_id');
|
||||||
|
|
||||||
|
// ソート対象カラム
|
||||||
$allowedSortColumns = [
|
$allowedSortColumns = [
|
||||||
'price_parkplaceid',
|
'price_parkplaceid', // 駐車場所ID
|
||||||
'prine_name',
|
'park_id', // 駐輪場ID
|
||||||
'price_month',
|
'prine_name', // 商品名
|
||||||
'park_id',
|
'price_month', // 期間
|
||||||
'psection_id',
|
'user_categoryid', // 利用者分類ID
|
||||||
'price_ptypeid',
|
'price', // 駐輪料金(税込)
|
||||||
'user_categoryid',
|
'psection_id', // 車種区分ID
|
||||||
'pplace_id',
|
'price_ptypeid', // 駐輪分類ID
|
||||||
'price'
|
'pplace_id', // 駐車車室ID
|
||||||
];
|
];
|
||||||
$sort_column = $inputs['sort'] ?? '';
|
|
||||||
$sort_type = strtolower($inputs['sort_type'] ?? 'asc');
|
$sortColumn = $inputs['sort'] ?? '';
|
||||||
if (in_array($sort_column, $allowedSortColumns)) {
|
$sortType = strtolower($inputs['sort_type'] ?? 'asc');
|
||||||
if (!in_array($sort_type, ['asc', 'desc'])) {
|
|
||||||
$sort_type = 'asc';
|
if (in_array($sortColumn, $allowedSortColumns, true)) {
|
||||||
|
if (!in_array($sortType, ['asc', 'desc'], true)) {
|
||||||
|
$sortType = 'asc';
|
||||||
}
|
}
|
||||||
$list->orderBy($sort_column, $sort_type);
|
$query->orderBy($sortColumn, $sortType);
|
||||||
}
|
}
|
||||||
if ($inputs['isExport']) {
|
|
||||||
$list = $list->get();
|
return $inputs['isExport']
|
||||||
} else {
|
? $query->get()
|
||||||
$list = $list->paginate(Utils::item_per_page);
|
: $query->paginate(\App\Utils::item_per_page ?? 20);
|
||||||
}
|
|
||||||
return $list;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static function getByPk($pk)
|
public static function getByPk($pk)
|
||||||
{
|
{
|
||||||
return self::find($pk);
|
return self::find($pk);
|
||||||
@ -102,4 +115,26 @@ class Price extends Model
|
|||||||
return $this->belongsTo(Usertype::class, 'user_categoryid', 'user_categoryid')->first();
|
return $this->belongsTo(Usertype::class, 'user_categoryid', 'user_categoryid')->first();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function psection()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Psection::class, 'psection_id'); // 外部キーが psection_id
|
||||||
|
|
||||||
|
}
|
||||||
|
public function ptype()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Ptype::class, 'price_ptypeid'); // 外部キーが price_ptypeid
|
||||||
|
|
||||||
|
}
|
||||||
|
public function pplace()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Pplace::class, 'pplace_id'); // 外部キーが pplace_id
|
||||||
|
|
||||||
|
}
|
||||||
|
// public function getStation()
|
||||||
|
// {
|
||||||
|
// return $this->belongsTo(Station::class, 'price_parkplaceid', 'park_id');
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -4,12 +4,24 @@ namespace App\Models;
|
|||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
/**
|
|
||||||
* 車種分類モデル - ptypeテーブル(正式モデル)
|
|
||||||
* 旧UsingStatusPtypeの責務を置き換え
|
|
||||||
*/
|
|
||||||
class Ptype extends Model
|
class Ptype extends Model
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主キー配列で一括削除
|
||||||
|
*/
|
||||||
|
public static function deleteByPk($arr)
|
||||||
|
{
|
||||||
|
return self::whereIn('ptype_id', $arr)->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主キーで1件取得
|
||||||
|
*/
|
||||||
|
public static function getByPk($pk)
|
||||||
|
{
|
||||||
|
return self::find($pk);
|
||||||
|
}
|
||||||
protected $table = 'ptype';
|
protected $table = 'ptype';
|
||||||
protected $primaryKey = 'ptype_id';
|
protected $primaryKey = 'ptype_id';
|
||||||
public $timestamps = true;
|
public $timestamps = true;
|
||||||
@ -21,11 +33,31 @@ class Ptype extends Model
|
|||||||
'ptype_subject',
|
'ptype_subject',
|
||||||
'ptype_remarks',
|
'ptype_remarks',
|
||||||
'operator_id',
|
'operator_id',
|
||||||
|
'floor_sort',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
public static function search($inputs)
|
||||||
* 料金設定
|
{
|
||||||
*/
|
$list = self::query();
|
||||||
|
if (!empty($inputs['isMethodPost'])) {
|
||||||
|
// 必要に応じて検索条件を追加
|
||||||
|
}
|
||||||
|
if (!empty($inputs['sort'])) {
|
||||||
|
$list->orderBy($inputs['sort'], $inputs['sort_type'] ?? 'asc');
|
||||||
|
}
|
||||||
|
if (!empty($inputs['isExport'])) {
|
||||||
|
return $list->get();
|
||||||
|
} else {
|
||||||
|
return $list->paginate(20);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getList()
|
||||||
|
{
|
||||||
|
return self::pluck('ptype_subject', 'ptype_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public function prices()
|
public function prices()
|
||||||
{
|
{
|
||||||
return $this->hasMany(PriceA::class, 'price_ptypeid', 'ptype_id');
|
return $this->hasMany(PriceA::class, 'price_ptypeid', 'ptype_id');
|
||||||
|
|||||||
@ -29,6 +29,7 @@ class RegularType extends Model
|
|||||||
'regular_class_6',
|
'regular_class_6',
|
||||||
'regular_class_12',
|
'regular_class_12',
|
||||||
'memo',
|
'memo',
|
||||||
|
'operator_id',
|
||||||
];
|
];
|
||||||
|
|
||||||
public static function boot()
|
public static function boot()
|
||||||
@ -60,14 +61,14 @@ class RegularType extends Model
|
|||||||
return $list;
|
return $list;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getByPk($pk)
|
public static function getById($id)
|
||||||
{
|
{
|
||||||
return self::find($pk);
|
return self::find($id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function deleteByPk($arr)
|
public static function deleteById($id)
|
||||||
{
|
{
|
||||||
return self::whereIn('regular_type_id', $arr)->delete();
|
return self::where('regular_type_id', $id)->delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCity()
|
public function getCity()
|
||||||
|
|||||||
@ -24,7 +24,7 @@ class Setting extends Model
|
|||||||
'web_master', // ウェブ参照マスタ
|
'web_master', // ウェブ参照マスタ
|
||||||
'auto_change_date', // ウェブ参照マスタ自動切り替え日時
|
'auto_change_date', // ウェブ参照マスタ自動切り替え日時
|
||||||
'auto_chage_master', // 自動切換えウェブ参照マスタ(※DB定義のままchage)
|
'auto_chage_master', // 自動切換えウェブ参照マスタ(※DB定義のままchage)
|
||||||
're_issue_alert_number', // 再発行アラート回数
|
're-issue_alert_number', // 再発行アラート回数
|
||||||
'image_base_url1', // ニュースイメージURLベース名
|
'image_base_url1', // ニュースイメージURLベース名
|
||||||
'image_base_url2', // 本人確認写真URLベース名
|
'image_base_url2', // 本人確認写真URLベース名
|
||||||
'printable_alert_flag', // プリンタ印字残警告フラグ
|
'printable_alert_flag', // プリンタ印字残警告フラグ
|
||||||
@ -37,7 +37,7 @@ class Setting extends Model
|
|||||||
// キャスト(型変換)
|
// キャスト(型変換)
|
||||||
protected $casts = [
|
protected $casts = [
|
||||||
'auto_change_date' => 'datetime', // 日時
|
'auto_change_date' => 'datetime', // 日時
|
||||||
're_issue_alert_number' => 'integer', // 整数
|
're-issue_alert_number' => 'integer', // 整数
|
||||||
'printable_alert_flag' => 'boolean', // 真偽値
|
'printable_alert_flag' => 'boolean', // 真偽値
|
||||||
'printable_number' => 'integer', // 整数
|
'printable_number' => 'integer', // 整数
|
||||||
'printable_alert_number' => 'integer', // 整数
|
'printable_alert_number' => 'integer', // 整数
|
||||||
@ -46,4 +46,17 @@ class Setting extends Model
|
|||||||
'created_at' => 'datetime', // 作成日時
|
'created_at' => 'datetime', // 作成日時
|
||||||
'updated_at' => 'datetime', // 更新日時
|
'updated_at' => 'datetime', // 更新日時
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// アクセサ(getter)
|
||||||
|
public function getReissueAlertNumberAttribute()
|
||||||
|
{
|
||||||
|
return $this->attributes['re-issue_alert_number'] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ミューテタ(setter)
|
||||||
|
public function setReissueAlertNumberAttribute($value)
|
||||||
|
{
|
||||||
|
$this->attributes['re-issue_alert_number'] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,4 +24,11 @@ class SettlementTransaction extends Model
|
|||||||
'stamp_flag',
|
'stamp_flag',
|
||||||
'md5_string',
|
'md5_string',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// 日付型キャスト
|
||||||
|
protected $casts = [
|
||||||
|
'created_at' => 'datetime',
|
||||||
|
'updated_at' => 'datetime',
|
||||||
|
'pay_date' => 'datetime',
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,22 +4,23 @@ namespace App\Models;
|
|||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
class NeighborStation extends Model
|
class Station extends Model
|
||||||
{
|
{
|
||||||
// テーブル名を指定
|
|
||||||
protected $table = 'station';
|
protected $table = 'station';
|
||||||
|
|
||||||
// 主キーを指定
|
|
||||||
protected $primaryKey = 'station_id';
|
protected $primaryKey = 'station_id';
|
||||||
|
|
||||||
// ホワイトリスト
|
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'park_id',
|
'park_id',
|
||||||
'station_neighbor_station',
|
'station_neighbor_station',
|
||||||
'station_name_ruby',
|
'station_name_ruby',
|
||||||
'station_route_name',
|
'station_route_name',
|
||||||
'operator_id',
|
'operator_id',
|
||||||
|
'station_latitude', // ← 緯度
|
||||||
|
'station_longitude', // ← 経度
|
||||||
];
|
];
|
||||||
|
public function park()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Park::class, 'park_id', 'park_id');
|
||||||
|
}
|
||||||
|
|
||||||
// タイムスタンプのカラム名がデフォルトと同じなので、特に設定不要
|
}
|
||||||
}
|
|
||||||
@ -34,4 +34,11 @@ class Term extends Model
|
|||||||
{
|
{
|
||||||
return self::all();
|
return self::all();
|
||||||
}
|
}
|
||||||
|
public static function deleteByPk($ids)
|
||||||
|
{
|
||||||
|
if (!is_array($ids)) {
|
||||||
|
$ids = [$ids];
|
||||||
|
}
|
||||||
|
return self::whereIn('terms_id', $ids)->delete();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Utils;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
@ -15,9 +16,14 @@ class Usertype extends Model
|
|||||||
protected $primaryKey = 'user_categoryid';
|
protected $primaryKey = 'user_categoryid';
|
||||||
|
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
|
'sort_order',
|
||||||
|
'usertype_subject1',
|
||||||
|
'usertype_subject2',
|
||||||
|
'usertype_subject3',
|
||||||
'print_name',
|
'print_name',
|
||||||
'usertype_money',
|
'usertype_money',
|
||||||
'usertype_remarks'
|
'usertype_remarks',
|
||||||
|
'operator_id',
|
||||||
];
|
];
|
||||||
|
|
||||||
public static function boot()
|
public static function boot()
|
||||||
@ -28,22 +34,47 @@ class Usertype extends Model
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function search($inputs)
|
public static function search(array $inputs)
|
||||||
{
|
{
|
||||||
$list = self::query();
|
$query = self::query();
|
||||||
if ($inputs['isMethodPost']) {
|
$table = (new self())->getTable();
|
||||||
|
|
||||||
|
if (!empty($inputs['filter_sort_order'])) {
|
||||||
|
$query->where('sort_order', $inputs['filter_sort_order']);
|
||||||
}
|
}
|
||||||
// Sort
|
if (!empty($inputs['filter_usertype_subject1'])) {
|
||||||
if ($inputs['sort']) {
|
$query->where('usertype_subject1', 'like', '%' . $inputs['filter_usertype_subject1'] . '%');
|
||||||
$list->orderBy($inputs['sort'], $inputs['sort_type']);
|
|
||||||
}
|
}
|
||||||
if ($inputs['isExport']){
|
if (!empty($inputs['filter_usertype_subject2'])) {
|
||||||
$list = $list->get();
|
$query->where('usertype_subject2', 'like', '%' . $inputs['filter_usertype_subject2'] . '%');
|
||||||
}else{
|
|
||||||
$list = $list->paginate(Utils::item_per_page);
|
|
||||||
}
|
}
|
||||||
return $list;
|
if (!empty($inputs['filter_usertype_subject3'])) {
|
||||||
|
$query->where('usertype_subject3', 'like', '%' . $inputs['filter_usertype_subject3'] . '%');
|
||||||
|
}
|
||||||
|
|
||||||
|
$sortable = [
|
||||||
|
'user_categoryid' => "{$table}.user_categoryid",
|
||||||
|
'sort_order' => "{$table}.sort_order",
|
||||||
|
'usertype_subject1' => "{$table}.usertype_subject1",
|
||||||
|
'usertype_subject2' => "{$table}.usertype_subject2",
|
||||||
|
'usertype_subject3' => "{$table}.usertype_subject3",
|
||||||
|
'print_name' => "{$table}.print_name",
|
||||||
|
'usertype_remarks' => "{$table}.usertype_remarks",
|
||||||
|
];
|
||||||
|
|
||||||
|
$sortKey = $inputs['sort'] ?? 'user_categoryid';
|
||||||
|
$sortColumn = $sortable[$sortKey] ?? "{$table}.user_categoryid";
|
||||||
|
|
||||||
|
$direction = strtolower($inputs['sort_type'] ?? 'asc');
|
||||||
|
if (!in_array($direction, ['asc', 'desc'], true)) {
|
||||||
|
$direction = 'asc';
|
||||||
|
}
|
||||||
|
|
||||||
|
$query->orderBy($sortColumn, $direction);
|
||||||
|
|
||||||
|
return !empty($inputs['isExport'])
|
||||||
|
? $query->get()
|
||||||
|
: $query->paginate(Utils::item_per_page);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getByPk($pk)
|
public static function getByPk($pk)
|
||||||
|
|||||||
60
app/Models/Zone.php
Normal file
60
app/Models/Zone.php
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class Zone extends Model
|
||||||
|
{
|
||||||
|
use HasFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* テーブル名
|
||||||
|
*/
|
||||||
|
protected $table = 'zone';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主キー
|
||||||
|
*/
|
||||||
|
protected $primaryKey = 'zone_id';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* タイムスタンプを有効化
|
||||||
|
*/
|
||||||
|
public $timestamps = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 一括代入可能なカラム
|
||||||
|
*/
|
||||||
|
protected $fillable = [
|
||||||
|
'park_id',
|
||||||
|
'park_name',
|
||||||
|
'ptype_id',
|
||||||
|
'psection_id',
|
||||||
|
'zone_name',
|
||||||
|
'zone_number',
|
||||||
|
'zone_standard',
|
||||||
|
'zone_tolerance',
|
||||||
|
'zone_sort',
|
||||||
|
'delete_flag',
|
||||||
|
'ope_id',
|
||||||
|
'created_at',
|
||||||
|
'updated_at',
|
||||||
|
];
|
||||||
|
public function park()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Park::class, 'park_id', 'park_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function ptype()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Ptype::class, 'ptype_id', 'ptype_id');
|
||||||
|
}
|
||||||
|
public function psection()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Psection::class, 'psection_id', 'psection_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -8,6 +8,7 @@ use App\Services\ShjMailSendService;
|
|||||||
use App\Services\ShjNineService;
|
use App\Services\ShjNineService;
|
||||||
use App\Services\ShjTenService;
|
use App\Services\ShjTenService;
|
||||||
use App\Services\ShjSixService;
|
use App\Services\ShjSixService;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
class AppServiceProvider extends ServiceProvider
|
class AppServiceProvider extends ServiceProvider
|
||||||
{
|
{
|
||||||
@ -75,6 +76,44 @@ class AppServiceProvider extends ServiceProvider
|
|||||||
*/
|
*/
|
||||||
public function boot(): void
|
public function boot(): void
|
||||||
{
|
{
|
||||||
//
|
view()->composer('layouts.app', function($view){
|
||||||
|
|
||||||
|
// 未対応のみ集計
|
||||||
|
$stats = DB::table('operator_que')
|
||||||
|
->selectRaw("
|
||||||
|
SUM(CASE WHEN que_status = 1 AND que_class < 100 THEN 1 ELSE 0 END) AS task_untreated,
|
||||||
|
MAX(CASE WHEN que_status = 1 AND que_class < 100 THEN created_at END) AS task_latest,
|
||||||
|
SUM(CASE WHEN que_status = 1 AND que_class > 99 THEN 1 ELSE 0 END) AS hard_untreated,
|
||||||
|
MAX(CASE WHEN que_status = 1 AND que_class > 99 THEN created_at END) AS hard_latest
|
||||||
|
")
|
||||||
|
->first();
|
||||||
|
|
||||||
|
// 変数名は互換維持(内容は未対応件数)
|
||||||
|
$taskCount = (int)($stats->task_untreated ?? 0);
|
||||||
|
$hardCount = (int)($stats->hard_untreated ?? 0);
|
||||||
|
$taskLatest = $stats->task_latest ?? null;
|
||||||
|
$hardLatest = $stats->hard_latest ?? null;
|
||||||
|
|
||||||
|
// ドロップダウン最新5件 も未対応のみ
|
||||||
|
$latestTasks = DB::table('operator_que')
|
||||||
|
->where('que_status',1)
|
||||||
|
->where('que_class','<',100)
|
||||||
|
->orderByDesc('created_at')
|
||||||
|
->limit(5)
|
||||||
|
->get();
|
||||||
|
|
||||||
|
$latestHards = DB::table('operator_que')
|
||||||
|
->where('que_status',1)
|
||||||
|
->where('que_class','>',99)
|
||||||
|
->orderByDesc('created_at')
|
||||||
|
->limit(5)
|
||||||
|
->get();
|
||||||
|
|
||||||
|
$view->with(compact(
|
||||||
|
'taskCount','taskLatest',
|
||||||
|
'hardCount','hardLatest',
|
||||||
|
'latestTasks','latestHards'
|
||||||
|
));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,432 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Services;
|
|
||||||
|
|
||||||
use Illuminate\Support\Facades\DB;
|
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
use App\Models\Batch\BatchLog;
|
|
||||||
use App\Models\RegularContract;
|
|
||||||
use App\Models\Park;
|
|
||||||
use App\Models\Psection;
|
|
||||||
use App\Models\Ptype;
|
|
||||||
use Carbon\Carbon;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SHJ-11 現在契約台数集計サービス
|
|
||||||
*
|
|
||||||
* 集計単位每个の契約台数を算出し、ゾーンマスタとの管理処理を実行
|
|
||||||
*/
|
|
||||||
class ShjElevenService
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* 【処理1】集計単位每个の契約台数を算出する
|
|
||||||
*
|
|
||||||
* 集計単位: 駐輪場ID + 車種区分ID + 駐輪分類ID + ゾーンID
|
|
||||||
*
|
|
||||||
* SQL仕様に基づく複雑なJOIN処理:
|
|
||||||
* - regular_contract (T1)
|
|
||||||
* - park (T2)
|
|
||||||
* - psection (T4)
|
|
||||||
* - price_a (T5)
|
|
||||||
* - ptype (T3)
|
|
||||||
*
|
|
||||||
* @return array 契約台数集計結果
|
|
||||||
*/
|
|
||||||
public function calculateContractCounts(): array
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
$query = DB::table('regular_contract as T1')
|
|
||||||
->select([
|
|
||||||
'T1.park_id', // 駐輪場ID
|
|
||||||
'T2.park_name', // 駐輪場名
|
|
||||||
'T5.psection_id', // 車種区分ID
|
|
||||||
'T4.psection_subject', // 車種区分名
|
|
||||||
'T5.ptype_id', // 駐輪分類ID
|
|
||||||
'T3.ptype_subject', // 駐輪分類名
|
|
||||||
'T1.zone_id', // ゾーンID
|
|
||||||
DB::raw('count(T1.contract_id) as cnt') // 契約台数
|
|
||||||
])
|
|
||||||
// park テーブルとの JOIN
|
|
||||||
->join('park as T2', 'T1.park_id', '=', 'T2.park_id')
|
|
||||||
// psection テーブルとの JOIN
|
|
||||||
->join('psection as T4', 'T1.psection_id', '=', 'T4.psection_id')
|
|
||||||
// price_a テーブルとの複合条件 JOIN
|
|
||||||
->join('price_a as T5', function($join) {
|
|
||||||
$join->on('T1.park_id', '=', 'T5.park_id')
|
|
||||||
->on('T1.price_parkplaceid', '=', 'T5.price_parkplaceid')
|
|
||||||
->on('T1.psection_id', '=', 'T5.psection_id');
|
|
||||||
})
|
|
||||||
// ptype テーブルとの JOIN
|
|
||||||
->join('ptype as T3', 'T5.ptype_id', '=', 'T3.ptype_id')
|
|
||||||
->where([
|
|
||||||
['T1.contract_flag', '=', 1], // 有効契約
|
|
||||||
['T2.park_close_flag', '=', 0], // 駐輪場未閉鎖
|
|
||||||
])
|
|
||||||
// 契約有効期間内の条件
|
|
||||||
->whereRaw("date_format(now(), '%y %m %d') BETWEEN T1.contract_periods AND T1.contract_periode")
|
|
||||||
->groupBy([
|
|
||||||
'T1.park_id',
|
|
||||||
'T2.park_name',
|
|
||||||
'T5.psection_id',
|
|
||||||
'T4.psection_subject',
|
|
||||||
'T5.ptype_id',
|
|
||||||
'T3.ptype_subject',
|
|
||||||
'T1.zone_id'
|
|
||||||
])
|
|
||||||
->get();
|
|
||||||
|
|
||||||
Log::info('SHJ-11 契約台数算出完了', [
|
|
||||||
'count' => $query->count(),
|
|
||||||
'sql_conditions' => [
|
|
||||||
'contract_flag' => 1,
|
|
||||||
'park_close_flag' => 0,
|
|
||||||
'contract_period_check' => 'BETWEEN contract_periods AND contract_periode'
|
|
||||||
]
|
|
||||||
]);
|
|
||||||
|
|
||||||
return $query->toArray();
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Log::error('SHJ-11 契約台数算出エラー', [
|
|
||||||
'error' => $e->getMessage(),
|
|
||||||
'trace' => $e->getTraceAsString()
|
|
||||||
]);
|
|
||||||
throw $e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 【処理2・3】ゾーンマスタ管理処理
|
|
||||||
*
|
|
||||||
* 処理フロー:
|
|
||||||
* 1. ゾーンマスタを取得する
|
|
||||||
* 2. 取得判定 → 存在しない場合は新規登録
|
|
||||||
* 3. 契約台数チェック(限界台数超過判定)
|
|
||||||
* 4. 契約台数を反映する(ゾーンマスタ更新)
|
|
||||||
*
|
|
||||||
* @param array $contractCounts 契約台数集計結果
|
|
||||||
* @return array 処理結果
|
|
||||||
*/
|
|
||||||
public function processZoneManagement(array $contractCounts): array
|
|
||||||
{
|
|
||||||
$createdZones = 0;
|
|
||||||
$updatedZones = 0;
|
|
||||||
$overCapacityCount = 0;
|
|
||||||
$errors = [];
|
|
||||||
$processParameters = [];
|
|
||||||
|
|
||||||
try {
|
|
||||||
DB::beginTransaction();
|
|
||||||
|
|
||||||
foreach ($contractCounts as $contractData) {
|
|
||||||
try {
|
|
||||||
// 【処理2】ゾーンマスタを取得する
|
|
||||||
$zoneData = $this->getZoneData($contractData);
|
|
||||||
|
|
||||||
if (!$zoneData) {
|
|
||||||
// 【判断2】ゾーンマスタが存在しない場合 → 新規登録
|
|
||||||
$createResult = $this->createZoneData($contractData);
|
|
||||||
if ($createResult['success']) {
|
|
||||||
$createdZones++;
|
|
||||||
}
|
|
||||||
$zoneData = $createResult['zone_data'];
|
|
||||||
}
|
|
||||||
|
|
||||||
// 【判断3】契約台数チェック(限界台数超過判定)
|
|
||||||
$isOverCapacity = $this->checkCapacityLimit($contractData, $zoneData);
|
|
||||||
if ($isOverCapacity) {
|
|
||||||
$overCapacityCount++;
|
|
||||||
Log::warning('SHJ-11 限界台数超過検出', [
|
|
||||||
'park_id' => $contractData->park_id,
|
|
||||||
'zone_id' => $contractData->zone_id,
|
|
||||||
'current_count' => $contractData->cnt,
|
|
||||||
'limit_capacity' => $zoneData->zone_tolerance ?? 0
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 【処理3】契約台数を反映する(ゾーンマスタ更新)
|
|
||||||
$updateResult = $this->updateZoneContractCount($contractData);
|
|
||||||
if ($updateResult['success']) {
|
|
||||||
$updatedZones++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 処理パラメータ記録
|
|
||||||
$processParameters[] = [
|
|
||||||
'park_id' => $contractData->park_id,
|
|
||||||
'psection_id' => $contractData->psection_id,
|
|
||||||
'ptype_id' => $contractData->ptype_id,
|
|
||||||
'zone_id' => $contractData->zone_id,
|
|
||||||
'contract_count' => $contractData->cnt,
|
|
||||||
'is_over_capacity' => $isOverCapacity,
|
|
||||||
'zone_created' => !$zoneData && $createResult['success'] ?? false,
|
|
||||||
'zone_updated' => $updateResult['success'] ?? false
|
|
||||||
];
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
$errors[] = [
|
|
||||||
'park_id' => $contractData->park_id ?? null,
|
|
||||||
'zone_id' => $contractData->zone_id ?? null,
|
|
||||||
'error' => $e->getMessage()
|
|
||||||
];
|
|
||||||
|
|
||||||
Log::warning('SHJ-11 個別処理エラー', [
|
|
||||||
'park_id' => $contractData->park_id ?? null,
|
|
||||||
'zone_id' => $contractData->zone_id ?? null,
|
|
||||||
'error' => $e->getMessage()
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DB::commit();
|
|
||||||
|
|
||||||
return [
|
|
||||||
'success' => true,
|
|
||||||
'created_zones' => $createdZones,
|
|
||||||
'updated_zones' => $updatedZones,
|
|
||||||
'over_capacity_count' => $overCapacityCount,
|
|
||||||
'parameters' => $processParameters,
|
|
||||||
'errors' => $errors,
|
|
||||||
'message' => '現在契約台数集計処理完了'
|
|
||||||
];
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
DB::rollBack();
|
|
||||||
|
|
||||||
Log::error('SHJ-11 ゾーンマスタ管理処理全体エラー', [
|
|
||||||
'error' => $e->getMessage(),
|
|
||||||
'processed_count' => count($processParameters)
|
|
||||||
]);
|
|
||||||
|
|
||||||
return [
|
|
||||||
'success' => false,
|
|
||||||
'created_zones' => $createdZones,
|
|
||||||
'updated_zones' => $updatedZones,
|
|
||||||
'over_capacity_count' => $overCapacityCount,
|
|
||||||
'parameters' => $processParameters,
|
|
||||||
'errors' => $errors,
|
|
||||||
'message' => 'ゾーンマスタ管理処理エラー: ' . $e->getMessage(),
|
|
||||||
'details' => $e->getTraceAsString()
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ゾーンマスタデータ取得
|
|
||||||
*
|
|
||||||
* 集計単位に対応するゾーンマスタを取得
|
|
||||||
*
|
|
||||||
* @param object $contractData 契約台数集計データ
|
|
||||||
* @return object|null ゾーンマスタデータ
|
|
||||||
*/
|
|
||||||
private function getZoneData($contractData)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
return DB::table('zone')
|
|
||||||
->select([
|
|
||||||
'zone_id',
|
|
||||||
'park_id',
|
|
||||||
'ptype_id',
|
|
||||||
'psection_id',
|
|
||||||
'zone_name',
|
|
||||||
'zone_number', // 現在契約台数
|
|
||||||
'zone_standard', // 標準収容台数
|
|
||||||
'zone_tolerance' // 限界収容台数
|
|
||||||
])
|
|
||||||
->where([
|
|
||||||
['park_id', '=', $contractData->park_id],
|
|
||||||
['psection_id', '=', $contractData->psection_id],
|
|
||||||
['ptype_id', '=', $contractData->ptype_id],
|
|
||||||
['zone_id', '=', $contractData->zone_id]
|
|
||||||
])
|
|
||||||
->first();
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Log::error('SHJ-11 ゾーンマスタ取得エラー', [
|
|
||||||
'park_id' => $contractData->park_id,
|
|
||||||
'zone_id' => $contractData->zone_id,
|
|
||||||
'error' => $e->getMessage()
|
|
||||||
]);
|
|
||||||
throw $e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ゾーンマスタ新規作成
|
|
||||||
*
|
|
||||||
* 仕様書に基づくINSERT処理
|
|
||||||
*
|
|
||||||
* @param object $contractData 契約台数集計データ
|
|
||||||
* @return array 作成結果
|
|
||||||
*/
|
|
||||||
private function createZoneData($contractData): array
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
// 新規ゾーンマスタのデフォルト値設定
|
|
||||||
$newZoneData = [
|
|
||||||
'zone_id' => $contractData->zone_id,
|
|
||||||
'park_id' => $contractData->park_id,
|
|
||||||
'ptype_id' => $contractData->ptype_id,
|
|
||||||
'psection_id' => $contractData->psection_id,
|
|
||||||
'zone_name' => null, // デフォルトnull
|
|
||||||
'zone_number' => $contractData->cnt, // 算出した契約台数
|
|
||||||
'zone_standard' => null, // デフォルトnull
|
|
||||||
'zone_tolerance' => null, // デフォルトnull
|
|
||||||
'zone_sort' => null, // デフォルトnull
|
|
||||||
'delete_flag' => 0, // 削除フラグOFF
|
|
||||||
'created_at' => now(),
|
|
||||||
'updated_at' => now(),
|
|
||||||
'ope_id' => 9999999 // 仕様書指定値
|
|
||||||
];
|
|
||||||
|
|
||||||
DB::table('zone')->insert($newZoneData);
|
|
||||||
|
|
||||||
Log::info('SHJ-11 ゾーンマスタ新規作成完了', [
|
|
||||||
'zone_id' => $contractData->zone_id,
|
|
||||||
'park_id' => $contractData->park_id,
|
|
||||||
'contract_count' => $contractData->cnt
|
|
||||||
]);
|
|
||||||
|
|
||||||
return [
|
|
||||||
'success' => true,
|
|
||||||
'zone_data' => (object) $newZoneData,
|
|
||||||
'message' => 'ゾーンマスタを新規作成しました'
|
|
||||||
];
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Log::error('SHJ-11 ゾーンマスタ新規作成エラー', [
|
|
||||||
'zone_id' => $contractData->zone_id,
|
|
||||||
'park_id' => $contractData->park_id,
|
|
||||||
'error' => $e->getMessage()
|
|
||||||
]);
|
|
||||||
|
|
||||||
return [
|
|
||||||
'success' => false,
|
|
||||||
'zone_data' => null,
|
|
||||||
'message' => 'ゾーンマスタ新規作成エラー: ' . $e->getMessage()
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 契約台数限界チェック
|
|
||||||
*
|
|
||||||
* 【判断3】契約台数 > 限界収容台数の判定
|
|
||||||
*
|
|
||||||
* @param object $contractData 契約台数集計データ
|
|
||||||
* @param object|null $zoneData ゾーンマスタデータ
|
|
||||||
* @return bool 限界台数超過フラグ
|
|
||||||
*/
|
|
||||||
private function checkCapacityLimit($contractData, $zoneData = null): bool
|
|
||||||
{
|
|
||||||
if (!$zoneData || is_null($zoneData->zone_tolerance)) {
|
|
||||||
// ゾーンデータが存在しないか、限界収容台数が未設定の場合は超過判定しない
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 契約台数 > 限界収容台数の判定
|
|
||||||
return $contractData->cnt > $zoneData->zone_tolerance;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ゾーンマスタ契約台数更新
|
|
||||||
*
|
|
||||||
* 【処理3】現在契約台数をゾーンマスタに反映
|
|
||||||
*
|
|
||||||
* @param object $contractData 契約台数集計データ
|
|
||||||
* @return array 更新結果
|
|
||||||
*/
|
|
||||||
private function updateZoneContractCount($contractData): array
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
$updateData = [
|
|
||||||
'zone_number' => $contractData->cnt, // 現在契約台数を更新
|
|
||||||
'updated_at' => now(), // 更新日時
|
|
||||||
'ope_id' => 'SHJ-11' // 更新オペレータID
|
|
||||||
];
|
|
||||||
|
|
||||||
$updated = DB::table('zone')
|
|
||||||
->where([
|
|
||||||
['park_id', '=', $contractData->park_id],
|
|
||||||
['psection_id', '=', $contractData->psection_id],
|
|
||||||
['ptype_id', '=', $contractData->ptype_id],
|
|
||||||
['zone_id', '=', $contractData->zone_id]
|
|
||||||
])
|
|
||||||
->update($updateData);
|
|
||||||
|
|
||||||
if ($updated > 0) {
|
|
||||||
Log::info('SHJ-11 ゾーンマスタ更新完了', [
|
|
||||||
'zone_id' => $contractData->zone_id,
|
|
||||||
'park_id' => $contractData->park_id,
|
|
||||||
'new_contract_count' => $contractData->cnt
|
|
||||||
]);
|
|
||||||
|
|
||||||
return [
|
|
||||||
'success' => true,
|
|
||||||
'message' => 'ゾーンマスタ契約台数を更新しました'
|
|
||||||
];
|
|
||||||
} else {
|
|
||||||
return [
|
|
||||||
'success' => false,
|
|
||||||
'message' => 'ゾーンマスタ更新対象が見つかりません'
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Log::error('SHJ-11 ゾーンマスタ更新エラー', [
|
|
||||||
'zone_id' => $contractData->zone_id,
|
|
||||||
'park_id' => $contractData->park_id,
|
|
||||||
'error' => $e->getMessage()
|
|
||||||
]);
|
|
||||||
|
|
||||||
return [
|
|
||||||
'success' => false,
|
|
||||||
'message' => 'ゾーンマスタ更新エラー: ' . $e->getMessage()
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 【処理4】バッチ処理ログを作成する
|
|
||||||
*
|
|
||||||
* 統一BatchLogシステムを使用してSHJ-11の実行ログを記録
|
|
||||||
*
|
|
||||||
* @param string $status ステータス
|
|
||||||
* @param array $parameters パラメータ
|
|
||||||
* @param string $message メッセージ
|
|
||||||
* @param int $executionCount 実行回数
|
|
||||||
* @param int $successCount 成功回数
|
|
||||||
* @param int $errorCount エラー回数
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function createBatchLog(
|
|
||||||
string $status,
|
|
||||||
array $parameters,
|
|
||||||
string $message,
|
|
||||||
int $executionCount = 0,
|
|
||||||
int $successCount = 0,
|
|
||||||
int $errorCount = 0
|
|
||||||
): void {
|
|
||||||
try {
|
|
||||||
BatchLog::createBatchLog(
|
|
||||||
'SHJ-11',
|
|
||||||
$status,
|
|
||||||
$parameters,
|
|
||||||
$message,
|
|
||||||
[
|
|
||||||
'execution_count' => $executionCount,
|
|
||||||
'success_count' => $successCount,
|
|
||||||
'error_count' => $errorCount,
|
|
||||||
'process_type' => '現在契約台数集計',
|
|
||||||
'executed_at' => now()->toISOString()
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Log::error('SHJ-11 バッチログ作成エラー', [
|
|
||||||
'error' => $e->getMessage(),
|
|
||||||
'status' => $status,
|
|
||||||
'message' => $message
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,406 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Services;
|
|
||||||
|
|
||||||
use App\Models\Park;
|
|
||||||
use App\Models\RegularContract;
|
|
||||||
use App\Models\Batch\BatchLog;
|
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
use Illuminate\Support\Facades\DB;
|
|
||||||
use Carbon\Carbon;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SHJ-4C 室割当処理サービス
|
|
||||||
*
|
|
||||||
* ゾーン情報取得及び割当処理を実行するビジネスロジック
|
|
||||||
* バッチ処理「SHJ-4C室割当」の核となる処理を担当
|
|
||||||
*/
|
|
||||||
class ShjFourCService
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Park モデル
|
|
||||||
*
|
|
||||||
* @var Park
|
|
||||||
*/
|
|
||||||
protected $parkModel;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* RegularContract モデル
|
|
||||||
*
|
|
||||||
* @var RegularContract
|
|
||||||
*/
|
|
||||||
protected $contractModel;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* BatchLog モデル
|
|
||||||
*
|
|
||||||
* @var BatchLog
|
|
||||||
*/
|
|
||||||
protected $batchLogModel;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* コンストラクタ
|
|
||||||
*
|
|
||||||
* @param Park $parkModel
|
|
||||||
* @param RegularContract $contractModel
|
|
||||||
* @param BatchLog $batchLogModel
|
|
||||||
*/
|
|
||||||
public function __construct(
|
|
||||||
Park $parkModel,
|
|
||||||
RegularContract $contractModel,
|
|
||||||
BatchLog $batchLogModel
|
|
||||||
) {
|
|
||||||
$this->parkModel = $parkModel;
|
|
||||||
$this->contractModel = $contractModel;
|
|
||||||
$this->batchLogModel = $batchLogModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SHJ-4C 室割当処理メイン実行
|
|
||||||
*
|
|
||||||
* 処理フロー:
|
|
||||||
* 【処理1】ゾーン情報取得
|
|
||||||
* 【判断1】割当判定
|
|
||||||
* 【処理2】バッチログ作成
|
|
||||||
* 【処理3】処理結果返却
|
|
||||||
*
|
|
||||||
* @param int $parkId 駐輪場ID
|
|
||||||
* @param int $ptypeId 駐輪分類ID
|
|
||||||
* @param int $psectionId 車種区分ID
|
|
||||||
* @return array 処理結果
|
|
||||||
*/
|
|
||||||
public function executeRoomAllocation(int $parkId, int $ptypeId, int $psectionId): array
|
|
||||||
{
|
|
||||||
$batchLogId = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
// バッチ処理開始ログ作成(実際のコマンド名を記録)
|
|
||||||
$batchLog = BatchLog::createBatchLog(
|
|
||||||
'shj4c',
|
|
||||||
BatchLog::STATUS_START,
|
|
||||||
[
|
|
||||||
'park_id' => $parkId,
|
|
||||||
'ptype_id' => $ptypeId,
|
|
||||||
'psection_id' => $psectionId
|
|
||||||
],
|
|
||||||
'SHJ-4C 室割当処理開始'
|
|
||||||
);
|
|
||||||
$batchLogId = $batchLog->id;
|
|
||||||
|
|
||||||
Log::info('SHJ-4C 室割当処理開始', [
|
|
||||||
'batch_log_id' => $batchLogId,
|
|
||||||
'park_id' => $parkId,
|
|
||||||
'ptype_id' => $ptypeId,
|
|
||||||
'psection_id' => $psectionId
|
|
||||||
]);
|
|
||||||
|
|
||||||
// 【処理1】ゾーン情報取得
|
|
||||||
$zoneInfo = $this->getZoneInformation($parkId, $ptypeId, $psectionId);
|
|
||||||
|
|
||||||
if (empty($zoneInfo)) {
|
|
||||||
$message = '対象のゾーン情報が見つかりません';
|
|
||||||
|
|
||||||
// バッチログ更新(通用方法使用)
|
|
||||||
$batchLog->update([
|
|
||||||
'status' => BatchLog::STATUS_ERROR,
|
|
||||||
'end_time' => now(),
|
|
||||||
'message' => $message,
|
|
||||||
'error_details' => $message,
|
|
||||||
'error_count' => 1
|
|
||||||
]);
|
|
||||||
|
|
||||||
return [
|
|
||||||
'success' => false,
|
|
||||||
'message' => $message,
|
|
||||||
'batch_log_id' => $batchLogId
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
// 【判断1】割当判定処理
|
|
||||||
$allocationResult = $this->performAllocationJudgment($zoneInfo, $parkId, $ptypeId, $psectionId);
|
|
||||||
|
|
||||||
if (!$allocationResult['can_allocate']) {
|
|
||||||
// 割当NGの場合、対象事室番号を設定
|
|
||||||
$this->setTargetRoomNumber($allocationResult['target_room_number']);
|
|
||||||
|
|
||||||
// バッチログ更新(警告)
|
|
||||||
$batchLog->update([
|
|
||||||
'status' => BatchLog::STATUS_WARNING,
|
|
||||||
'end_time' => now(),
|
|
||||||
'message' => '割当処理NG: ' . $allocationResult['reason'],
|
|
||||||
'success_count' => 1 // 処理は成功したが割当NGのため
|
|
||||||
]);
|
|
||||||
|
|
||||||
return [
|
|
||||||
'success' => true,
|
|
||||||
'message' => '割当判定完了(割当NG)',
|
|
||||||
'allocation_result' => $allocationResult,
|
|
||||||
'batch_log_id' => $batchLogId
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
// 【処理2】割当実行(割当OKの場合)
|
|
||||||
$executionResult = $this->executeAllocation($zoneInfo, $allocationResult);
|
|
||||||
|
|
||||||
// バッチ処理完了ログ更新
|
|
||||||
$batchLog->update([
|
|
||||||
'status' => BatchLog::STATUS_SUCCESS,
|
|
||||||
'end_time' => now(),
|
|
||||||
'message' => 'SHJ-4C 室割当処理正常完了',
|
|
||||||
'success_count' => 1
|
|
||||||
]);
|
|
||||||
|
|
||||||
Log::info('SHJ-4C 室割当処理完了', [
|
|
||||||
'batch_log_id' => $batchLogId,
|
|
||||||
'execution_result' => $executionResult
|
|
||||||
]);
|
|
||||||
|
|
||||||
// 【処理3】処理結果返却
|
|
||||||
return [
|
|
||||||
'success' => true,
|
|
||||||
'message' => 'SHJ-4C 室割当処理が正常に完了しました',
|
|
||||||
'zone_info' => $zoneInfo,
|
|
||||||
'allocation_result' => $allocationResult,
|
|
||||||
'execution_result' => $executionResult,
|
|
||||||
'batch_log_id' => $batchLogId
|
|
||||||
];
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
$errorMessage = 'SHJ-4C 室割当処理でエラーが発生: ' . $e->getMessage();
|
|
||||||
|
|
||||||
if (isset($batchLog) && $batchLog) {
|
|
||||||
$batchLog->update([
|
|
||||||
'status' => BatchLog::STATUS_ERROR,
|
|
||||||
'end_time' => now(),
|
|
||||||
'message' => $errorMessage,
|
|
||||||
'error_details' => $e->getMessage(),
|
|
||||||
'error_count' => 1
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
Log::error('SHJ-4C 室割当処理エラー', [
|
|
||||||
'batch_log_id' => $batchLogId,
|
|
||||||
'exception' => $e->getMessage(),
|
|
||||||
'trace' => $e->getTraceAsString()
|
|
||||||
]);
|
|
||||||
|
|
||||||
return [
|
|
||||||
'success' => false,
|
|
||||||
'message' => $errorMessage,
|
|
||||||
'details' => $e->getMessage(),
|
|
||||||
'batch_log_id' => $batchLogId
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 【処理1】ゾーン情報取得
|
|
||||||
*
|
|
||||||
* 駐輪場ID、駐輪分類ID、車種区分IDに紐づくゾーン情報を取得する
|
|
||||||
* SQLクエリは設計書の仕様に基づく
|
|
||||||
*
|
|
||||||
* @param int $parkId 駐輪場ID
|
|
||||||
* @param int $ptypeId 駐輪分類ID
|
|
||||||
* @param int $psectionId 車種区分ID
|
|
||||||
* @return array ゾーン情報
|
|
||||||
*/
|
|
||||||
private function getZoneInformation(int $parkId, int $ptypeId, int $psectionId): array
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
// 設計書に記載されたSQLクエリに基づくゾーン情報取得
|
|
||||||
$zoneInfo = DB::table('zone as T1')
|
|
||||||
->select([
|
|
||||||
'T1.zone_id',
|
|
||||||
'T1.zone_name',
|
|
||||||
'T1.zone_contracted_count as zone_contracted_count',
|
|
||||||
'T1.zone_capacity as zone_capacity',
|
|
||||||
'T1.zone_permitted_count as zone_permitted_count',
|
|
||||||
'T1.zone_sort',
|
|
||||||
'T2.update_grace_period_start_date',
|
|
||||||
'T2.update_grace_period_end_date'
|
|
||||||
])
|
|
||||||
->join('park as T2', 'T1.park_id', '=', 'T2.park_id')
|
|
||||||
->where('T1.park_id', $parkId)
|
|
||||||
->where('T1.ptype_id', $ptypeId)
|
|
||||||
->where('T1.psection_id', $psectionId)
|
|
||||||
->where('T1.delete_flag', 0)
|
|
||||||
->orderBy('T1.zone_sort')
|
|
||||||
->get()
|
|
||||||
->toArray();
|
|
||||||
|
|
||||||
Log::info('ゾーン情報取得完了', [
|
|
||||||
'park_id' => $parkId,
|
|
||||||
'ptype_id' => $ptypeId,
|
|
||||||
'psection_id' => $psectionId,
|
|
||||||
'zone_count' => count($zoneInfo)
|
|
||||||
]);
|
|
||||||
|
|
||||||
return $zoneInfo;
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Log::error('ゾーン情報取得エラー', [
|
|
||||||
'park_id' => $parkId,
|
|
||||||
'ptype_id' => $ptypeId,
|
|
||||||
'psection_id' => $psectionId,
|
|
||||||
'error' => $e->getMessage()
|
|
||||||
]);
|
|
||||||
|
|
||||||
throw $e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 【判断1】割当判定処理
|
|
||||||
*
|
|
||||||
* ゾーン内の定期契約マスタから該当レコード数を取得し、
|
|
||||||
* 定期契約マスタから最適な車室番号を選定する割当判定を実行
|
|
||||||
*
|
|
||||||
* @param array $zoneInfo ゾーン情報
|
|
||||||
* @param int $parkId 駐輪場ID
|
|
||||||
* @param int $ptypeId 駐輪分類ID
|
|
||||||
* @param int $psectionId 車種区分ID
|
|
||||||
* @return array 割当判定結果
|
|
||||||
*/
|
|
||||||
private function performAllocationJudgment(array $zoneInfo, int $parkId, int $ptypeId, int $psectionId): array
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
foreach ($zoneInfo as $zone) {
|
|
||||||
// 各ゾーンに対する定期契約情報取得
|
|
||||||
$contractInfo = $this->getRegularContractInfo($parkId, $zone->zone_id, $ptypeId, $psectionId);
|
|
||||||
|
|
||||||
// 割当可能性判定
|
|
||||||
if ($this->canAllocateToZone($zone, $contractInfo)) {
|
|
||||||
return [
|
|
||||||
'can_allocate' => true,
|
|
||||||
'zone_id' => $zone->zone_id,
|
|
||||||
'zone_name' => $zone->zone_name,
|
|
||||||
'contract_info' => $contractInfo,
|
|
||||||
'reason' => '割当OK: ゾーンID ' . $zone->zone_id
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 全ゾーンで割当NGの場合
|
|
||||||
$targetRoomNumber = $this->generateTargetRoomNumber($parkId, $ptypeId, $psectionId);
|
|
||||||
|
|
||||||
return [
|
|
||||||
'can_allocate' => false,
|
|
||||||
'target_room_number' => $targetRoomNumber,
|
|
||||||
'reason' => '全ゾーンで割当できませんでした'
|
|
||||||
];
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Log::error('割当判定処理エラー', [
|
|
||||||
'error' => $e->getMessage()
|
|
||||||
]);
|
|
||||||
|
|
||||||
throw $e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 定期契約情報取得
|
|
||||||
*
|
|
||||||
* 設計書のSQLクエリに基づく定期契約マスタ検索
|
|
||||||
*
|
|
||||||
* @param int $parkId 駐輪場ID
|
|
||||||
* @param int $zoneId ゾーンID
|
|
||||||
* @param int $ptypeId 駐輪分類ID
|
|
||||||
* @param int $psectionId 車種区分ID
|
|
||||||
* @return array 定期契約情報
|
|
||||||
*/
|
|
||||||
private function getRegularContractInfo(int $parkId, int $zoneId, int $ptypeId, int $psectionId): array
|
|
||||||
{
|
|
||||||
$currentDate = Carbon::now()->format('Y-m-d');
|
|
||||||
|
|
||||||
$contractInfo = DB::table('regular_contract as T1')
|
|
||||||
->select(['T1.contract_id'])
|
|
||||||
->where('T1.park_id', $parkId)
|
|
||||||
->where('T1.zone_id', $zoneId)
|
|
||||||
->where('T1.ptype_id', $ptypeId)
|
|
||||||
->where('T1.psection_id', $psectionId)
|
|
||||||
->where('T1.place_no', '!=', '')
|
|
||||||
->where(function ($query) use ($currentDate) {
|
|
||||||
$query->where(function ($subQuery) use ($currentDate) {
|
|
||||||
// パターンA: 処理1.更新期間開始日 <= 処理1.更新期間終了日 の場合
|
|
||||||
$subQuery->whereRaw("date_format(now(), '%y-%m-%d') >= date_format(now(), '%y-%m-%d')")
|
|
||||||
->whereRaw("date_format(now(), '%y-%m-%d') <= date_format(now(), '%y-%m-%d')");
|
|
||||||
})
|
|
||||||
->orWhere(function ($subQuery) use ($currentDate) {
|
|
||||||
// パターンB: その他の場合
|
|
||||||
$subQuery->whereRaw("date_format(now(), '%y-%m-%d') >= date_format(now(), '%y-%m-%d')");
|
|
||||||
});
|
|
||||||
})
|
|
||||||
->where('T1.contract_flag', 1)
|
|
||||||
->get()
|
|
||||||
->toArray();
|
|
||||||
|
|
||||||
return $contractInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ゾーン割当可能性判定
|
|
||||||
*
|
|
||||||
* @param object $zone ゾーン情報
|
|
||||||
* @param array $contractInfo 契約情報
|
|
||||||
* @return bool 割当可能かどうか
|
|
||||||
*/
|
|
||||||
private function canAllocateToZone($zone, array $contractInfo): bool
|
|
||||||
{
|
|
||||||
$contractedCount = count($contractInfo);
|
|
||||||
$capacity = $zone->zone_capacity;
|
|
||||||
|
|
||||||
// 空きがある場合は割当可能
|
|
||||||
return $contractedCount < $capacity;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 対象事室番号生成
|
|
||||||
*
|
|
||||||
* @param int $parkId 駐輪場ID
|
|
||||||
* @param int $ptypeId 駐輪分類ID
|
|
||||||
* @param int $psectionId 車種区分ID
|
|
||||||
* @return string 対象事室番号
|
|
||||||
*/
|
|
||||||
private function generateTargetRoomNumber(int $parkId, int $ptypeId, int $psectionId): string
|
|
||||||
{
|
|
||||||
return sprintf('%d_%d_%d', $parkId, $ptypeId, $psectionId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 対象事室番号設定
|
|
||||||
*
|
|
||||||
* @param string $targetRoomNumber 対象事室番号
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
private function setTargetRoomNumber(string $targetRoomNumber): void
|
|
||||||
{
|
|
||||||
Log::info('対象事室番号設定', [
|
|
||||||
'target_room_number' => $targetRoomNumber
|
|
||||||
]);
|
|
||||||
|
|
||||||
// 実際の事室番号設定ロジックをここに実装
|
|
||||||
// 具体的な仕様が必要な場合は後で追加実装
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 割当実行
|
|
||||||
*
|
|
||||||
* @param array $zoneInfo ゾーン情報
|
|
||||||
* @param array $allocationResult 割当判定結果
|
|
||||||
* @return array 実行結果
|
|
||||||
*/
|
|
||||||
private function executeAllocation(array $zoneInfo, array $allocationResult): array
|
|
||||||
{
|
|
||||||
// 割当実行の具体的なロジックを実装
|
|
||||||
// 設計書に詳細仕様があれば追加実装
|
|
||||||
|
|
||||||
return [
|
|
||||||
'executed' => true,
|
|
||||||
'zone_id' => $allocationResult['zone_id'],
|
|
||||||
'message' => '割当実行完了'
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,545 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Services;
|
|
||||||
|
|
||||||
use App\Models\MailTemplate;
|
|
||||||
use App\Models\Batch\BatchLog;
|
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
use Illuminate\Support\Facades\DB;
|
|
||||||
use Carbon\Carbon;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SHJ メール送信処理サービス
|
|
||||||
*
|
|
||||||
* メールテンプレートを使用したメール送信処理を実行するビジネスロジック
|
|
||||||
* バッチ処理「SHJメール送信」の核となる処理を担当
|
|
||||||
*/
|
|
||||||
class ShjMailSendService
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* MailTemplate モデル
|
|
||||||
*
|
|
||||||
* @var MailTemplate
|
|
||||||
*/
|
|
||||||
protected $mailTemplateModel;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* BatchLog モデル
|
|
||||||
*
|
|
||||||
* @var BatchLog
|
|
||||||
*/
|
|
||||||
protected $batchLogModel;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* コンストラクタ
|
|
||||||
*
|
|
||||||
* @param MailTemplate $mailTemplateModel
|
|
||||||
* @param BatchLog $batchLogModel
|
|
||||||
*/
|
|
||||||
public function __construct(
|
|
||||||
MailTemplate $mailTemplateModel,
|
|
||||||
BatchLog $batchLogModel
|
|
||||||
) {
|
|
||||||
$this->mailTemplateModel = $mailTemplateModel;
|
|
||||||
$this->batchLogModel = $batchLogModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SHJ メール送信処理メイン実行
|
|
||||||
*
|
|
||||||
* 処理フロー:
|
|
||||||
* 【処理1】入力パラメーターをチェックする
|
|
||||||
* 【処理2】メール送信テンプレート情報を取得する
|
|
||||||
* 【判断2】取得結果判定
|
|
||||||
* 【処理3】メールを送信する
|
|
||||||
* 【処理4】処理結果を返却する
|
|
||||||
*
|
|
||||||
* @param string $mailAddress メールアドレス
|
|
||||||
* @param string $backupMailAddress 予備メールアドレス
|
|
||||||
* @param int $mailTemplateId メールテンプレートID
|
|
||||||
* @return array 処理結果
|
|
||||||
*/
|
|
||||||
public function executeMailSend(string $mailAddress, string $backupMailAddress, int $mailTemplateId): array
|
|
||||||
{
|
|
||||||
$batchLogId = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
// バッチ処理開始ログ作成(実際のコマンド名を記録)
|
|
||||||
$batchLog = BatchLog::createBatchLog(
|
|
||||||
'shj-mail-send',
|
|
||||||
BatchLog::STATUS_START,
|
|
||||||
[
|
|
||||||
'mail_address' => $mailAddress,
|
|
||||||
'backup_mail_address' => $backupMailAddress,
|
|
||||||
'mail_template_id' => $mailTemplateId
|
|
||||||
],
|
|
||||||
'SHJ メール送信処理開始'
|
|
||||||
);
|
|
||||||
$batchLogId = $batchLog->id;
|
|
||||||
|
|
||||||
Log::info('SHJ メール送信処理開始', [
|
|
||||||
'batch_log_id' => $batchLogId,
|
|
||||||
'mail_address' => $mailAddress,
|
|
||||||
'backup_mail_address' => $backupMailAddress,
|
|
||||||
'mail_template_id' => $mailTemplateId
|
|
||||||
]);
|
|
||||||
|
|
||||||
// 【処理1】入力パラメーターをチェックする
|
|
||||||
$paramCheckResult = $this->checkInputParameters($mailAddress, $backupMailAddress, $mailTemplateId);
|
|
||||||
if (!$paramCheckResult['valid']) {
|
|
||||||
$this->updateBatchLog($batchLogId, 'error', $paramCheckResult['message']);
|
|
||||||
|
|
||||||
return [
|
|
||||||
'success' => false,
|
|
||||||
'result_code' => 1, // 異常終了
|
|
||||||
'message' => $paramCheckResult['message'],
|
|
||||||
'batch_log_id' => $batchLogId
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
// 【処理2】メール送信テンプレート情報を取得する
|
|
||||||
$templateInfo = $this->getMailTemplateInfo($mailTemplateId);
|
|
||||||
|
|
||||||
// 【判断2】取得結果判定
|
|
||||||
if (empty($templateInfo)) {
|
|
||||||
$message = "メールテンプレートが存在しません。テンプレートID: {$mailTemplateId}";
|
|
||||||
$this->updateBatchLog($batchLogId, 'error', $message);
|
|
||||||
|
|
||||||
return [
|
|
||||||
'success' => false,
|
|
||||||
'result_code' => 1, // 異常終了
|
|
||||||
'message' => $message,
|
|
||||||
'batch_log_id' => $batchLogId
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
// 【処理3】メールを送信する
|
|
||||||
$mailSendResult = $this->sendMail($mailAddress, $backupMailAddress, $templateInfo);
|
|
||||||
|
|
||||||
if (!$mailSendResult['success']) {
|
|
||||||
$this->updateBatchLog($batchLogId, 'error', $mailSendResult['message']);
|
|
||||||
|
|
||||||
return [
|
|
||||||
'success' => false,
|
|
||||||
'result_code' => 1, // 異常終了
|
|
||||||
'message' => $mailSendResult['message'],
|
|
||||||
'batch_log_id' => $batchLogId
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
// バッチ処理完了ログ更新
|
|
||||||
$this->updateBatchLog($batchLogId, 'success', 'SHJ メール送信処理正常完了');
|
|
||||||
|
|
||||||
Log::info('SHJ メール送信処理完了', [
|
|
||||||
'batch_log_id' => $batchLogId,
|
|
||||||
'mail_send_result' => $mailSendResult
|
|
||||||
]);
|
|
||||||
|
|
||||||
// 【処理4】処理結果を返却する
|
|
||||||
return [
|
|
||||||
'success' => true,
|
|
||||||
'result_code' => 0, // 正常終了
|
|
||||||
'message' => 'SHJ メール送信処理が正常に完了しました',
|
|
||||||
'mail_send_result' => $mailSendResult,
|
|
||||||
'batch_log_id' => $batchLogId
|
|
||||||
];
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
$errorMessage = 'SHJ メール送信処理でエラーが発生: ' . $e->getMessage();
|
|
||||||
|
|
||||||
if ($batchLogId) {
|
|
||||||
$this->updateBatchLog($batchLogId, 'error', $errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
Log::error('SHJ メール送信処理エラー', [
|
|
||||||
'batch_log_id' => $batchLogId,
|
|
||||||
'exception' => $e->getMessage(),
|
|
||||||
'trace' => $e->getTraceAsString()
|
|
||||||
]);
|
|
||||||
|
|
||||||
return [
|
|
||||||
'success' => false,
|
|
||||||
'result_code' => 1, // 異常終了
|
|
||||||
'message' => $errorMessage,
|
|
||||||
'details' => $e->getMessage(),
|
|
||||||
'batch_log_id' => $batchLogId
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 【処理1】入力パラメーターをチェックする
|
|
||||||
*
|
|
||||||
* 仕様書に基づく詳細チェック:
|
|
||||||
* - メールアドレス形式チェック
|
|
||||||
* - テンプレートID存在性チェック
|
|
||||||
*
|
|
||||||
* @param string $mailAddress メールアドレス
|
|
||||||
* @param string $backupMailAddress 予備メールアドレス
|
|
||||||
* @param int $mailTemplateId メールテンプレートID
|
|
||||||
* @return array チェック結果
|
|
||||||
*/
|
|
||||||
private function checkInputParameters(string $mailAddress, string $backupMailAddress, int $mailTemplateId): array
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
// メールアドレス存在チェック(いずれか必須)
|
|
||||||
if (empty($mailAddress) && empty($backupMailAddress)) {
|
|
||||||
return [
|
|
||||||
'valid' => false,
|
|
||||||
'message' => 'パラメーターNG: メールアドレスまたは予備メールアドレスのいずれかは必須です'
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
// メールアドレス形式チェック
|
|
||||||
if (!empty($mailAddress) && !filter_var($mailAddress, FILTER_VALIDATE_EMAIL)) {
|
|
||||||
return [
|
|
||||||
'valid' => false,
|
|
||||||
'message' => 'パラメーターNG: メールアドレスの形式が正しくありません'
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!empty($backupMailAddress) && !filter_var($backupMailAddress, FILTER_VALIDATE_EMAIL)) {
|
|
||||||
return [
|
|
||||||
'valid' => false,
|
|
||||||
'message' => 'パラメーターNG: 予備メールアドレスの形式が正しくありません'
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
// メールテンプレートID形式チェック
|
|
||||||
if ($mailTemplateId <= 0) {
|
|
||||||
return [
|
|
||||||
'valid' => false,
|
|
||||||
'message' => 'パラメーターNG: メールテンプレートIDは正の整数である必要があります'
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
Log::info('入力パラメーターチェック完了', [
|
|
||||||
'mail_address' => $mailAddress,
|
|
||||||
'backup_mail_address' => $backupMailAddress,
|
|
||||||
'mail_template_id' => $mailTemplateId
|
|
||||||
]);
|
|
||||||
|
|
||||||
return [
|
|
||||||
'valid' => true,
|
|
||||||
'message' => 'パラメーターチェックOK'
|
|
||||||
];
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Log::error('入力パラメーターチェックエラー', [
|
|
||||||
'error' => $e->getMessage()
|
|
||||||
]);
|
|
||||||
|
|
||||||
return [
|
|
||||||
'valid' => false,
|
|
||||||
'message' => 'パラメーターチェック中にエラーが発生しました: ' . $e->getMessage()
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 【処理2】メール送信テンプレート情報を取得する
|
|
||||||
*
|
|
||||||
* 仕様書に基づくSQLクエリ:
|
|
||||||
* SELECT エリアマネージャー同報, bccアドレス, 件名, 本文
|
|
||||||
* FROM メール送信テンプレート
|
|
||||||
* WHERE 使用プログラムID = 入力パラメーター使用プログラムID
|
|
||||||
* AND 使用フラグ = 1
|
|
||||||
*
|
|
||||||
* @param int $mailTemplateId メールテンプレートID(使用プログラムIDとして扱う)
|
|
||||||
* @return MailTemplate|null メールテンプレート情報
|
|
||||||
*/
|
|
||||||
private function getMailTemplateInfo(int $mailTemplateId): ?MailTemplate
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
// 仕様書に記載されたSQLクエリに基づくメールテンプレート情報取得
|
|
||||||
// 注意: 仕様書では「使用プログラムID」を条件にしているが、
|
|
||||||
// 入力パラメーターは「メールテンプレートID」なので、pg_idで検索
|
|
||||||
$templateInfo = $this->mailTemplateModel::where('pg_id', $mailTemplateId)
|
|
||||||
->where('use_flag', 1)
|
|
||||||
->first();
|
|
||||||
|
|
||||||
if ($templateInfo) {
|
|
||||||
Log::info('メールテンプレート情報取得完了', [
|
|
||||||
'mail_template_id' => $mailTemplateId,
|
|
||||||
'template_found' => true,
|
|
||||||
'subject' => $templateInfo->getSubject()
|
|
||||||
]);
|
|
||||||
} else {
|
|
||||||
Log::warning('メールテンプレート情報取得結果', [
|
|
||||||
'mail_template_id' => $mailTemplateId,
|
|
||||||
'template_found' => false,
|
|
||||||
'message' => 'テンプレートが見つかりません'
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $templateInfo;
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Log::error('メールテンプレート情報取得エラー', [
|
|
||||||
'mail_template_id' => $mailTemplateId,
|
|
||||||
'error' => $e->getMessage()
|
|
||||||
]);
|
|
||||||
|
|
||||||
throw $e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 【処理3】メールを送信する
|
|
||||||
*
|
|
||||||
* 仕様書に基づくmb_send_mail関数使用:
|
|
||||||
* - 送信者: 処理2で取得したメールアドレス(処理1で予備メールアドレス)
|
|
||||||
* - タイトル: 処理2で取得したタイトル
|
|
||||||
* - 本文: 処理2で取得した本文(※現在の文字列は「So-Manager一般的なWebサイト内部処理」参照)
|
|
||||||
* - 追加ヘッダ: 処理2で取得したbccアドレス(※値が設定されている場合のみ)
|
|
||||||
*
|
|
||||||
* @param string $mailAddress メールアドレス
|
|
||||||
* @param string $backupMailAddress 予備メールアドレス
|
|
||||||
* @param MailTemplate $templateInfo テンプレート情報
|
|
||||||
* @return array 送信結果
|
|
||||||
*/
|
|
||||||
private function sendMail(string $mailAddress, string $backupMailAddress, MailTemplate $templateInfo): array
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
// 送信先アドレス決定(優先: メールアドレス、代替: 予備メールアドレス)
|
|
||||||
$toAddress = !empty($mailAddress) ? $mailAddress : $backupMailAddress;
|
|
||||||
|
|
||||||
// メール内容取得
|
|
||||||
$subject = $templateInfo->getSubject() ?? '';
|
|
||||||
$message = $templateInfo->getText() ?? '';
|
|
||||||
|
|
||||||
// 追加ヘッダ設定
|
|
||||||
$headers = $this->buildMailHeaders($templateInfo);
|
|
||||||
|
|
||||||
Log::info('メール送信準備完了', [
|
|
||||||
'to_address' => $toAddress,
|
|
||||||
'subject' => $subject,
|
|
||||||
'has_bcc' => !empty($templateInfo->getBccAddress()),
|
|
||||||
'manager_cc_enabled' => $templateInfo->isManagerCcEnabled()
|
|
||||||
]);
|
|
||||||
|
|
||||||
// mb_send_mail関数を使用してメール送信
|
|
||||||
$sendResult = mb_send_mail(
|
|
||||||
$toAddress, // 送信先
|
|
||||||
$subject, // 件名
|
|
||||||
$message, // 本文
|
|
||||||
$headers // 追加ヘッダ
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($sendResult) {
|
|
||||||
Log::info('メール送信成功', [
|
|
||||||
'to_address' => $toAddress,
|
|
||||||
'subject' => $subject
|
|
||||||
]);
|
|
||||||
|
|
||||||
return [
|
|
||||||
'success' => true,
|
|
||||||
'message' => 'メール送信が正常に完了しました',
|
|
||||||
'to_address' => $toAddress,
|
|
||||||
'subject' => $subject
|
|
||||||
];
|
|
||||||
} else {
|
|
||||||
$errorMessage = 'mb_send_mail関数でメール送信に失敗しました';
|
|
||||||
Log::error('メール送信失敗', [
|
|
||||||
'to_address' => $toAddress,
|
|
||||||
'subject' => $subject,
|
|
||||||
'error' => $errorMessage
|
|
||||||
]);
|
|
||||||
|
|
||||||
return [
|
|
||||||
'success' => false,
|
|
||||||
'message' => $errorMessage
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
$errorMessage = 'メール送信中にエラーが発生: ' . $e->getMessage();
|
|
||||||
Log::error('メール送信エラー', [
|
|
||||||
'error' => $e->getMessage(),
|
|
||||||
'trace' => $e->getTraceAsString()
|
|
||||||
]);
|
|
||||||
|
|
||||||
return [
|
|
||||||
'success' => false,
|
|
||||||
'message' => $errorMessage
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* メールヘッダを構築
|
|
||||||
*
|
|
||||||
* 仕様書に基づく設定:
|
|
||||||
* - BCCアドレス: 値が設定されている場合のみ追加
|
|
||||||
* - エリアマネージャー同報: フラグが有効な場合の処理
|
|
||||||
*
|
|
||||||
* @param MailTemplate $templateInfo テンプレート情報
|
|
||||||
* @return string メールヘッダ
|
|
||||||
*/
|
|
||||||
private function buildMailHeaders(MailTemplate $templateInfo): string
|
|
||||||
{
|
|
||||||
$headers = [];
|
|
||||||
|
|
||||||
// BCCアドレス設定(値が設定されている場合のみ)
|
|
||||||
$bccAddress = $templateInfo->getBccAddress();
|
|
||||||
if (!empty($bccAddress)) {
|
|
||||||
$headers[] = "Bcc: {$bccAddress}";
|
|
||||||
}
|
|
||||||
|
|
||||||
// エリアマネージャー同報フラグが有効な場合
|
|
||||||
// ※具体的な処理内容は仕様書に詳細がないため、ログ出力のみ
|
|
||||||
if ($templateInfo->isManagerCcEnabled()) {
|
|
||||||
Log::info('エリアマネージャー同報フラグが有効です', [
|
|
||||||
'mail_template_id' => $templateInfo->mail_template_id
|
|
||||||
]);
|
|
||||||
// 実際のエリアマネージャーアドレス取得・設定処理は追加仕様が必要
|
|
||||||
}
|
|
||||||
|
|
||||||
// From設定(システム設定から取得)
|
|
||||||
$fromAddress = config('mail.from.address', 'noreply@so-manager.com');
|
|
||||||
$headers[] = "From: {$fromAddress}";
|
|
||||||
|
|
||||||
// Content-Type設定(日本語対応)
|
|
||||||
$headers[] = "Content-Type: text/plain; charset=UTF-8";
|
|
||||||
|
|
||||||
return implode("\r\n", $headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* バッチログ作成
|
|
||||||
*
|
|
||||||
* @param string $processName プロセス名
|
|
||||||
* @param string $status ステータス
|
|
||||||
* @param array $params パラメータ
|
|
||||||
* @return int バッチログID
|
|
||||||
*/
|
|
||||||
private function createBatchLog(string $processName, string $status, array $params = []): int
|
|
||||||
{
|
|
||||||
return $this->batchLogModel->create([
|
|
||||||
'process_name' => $processName,
|
|
||||||
'status' => $status,
|
|
||||||
'start_time' => now(),
|
|
||||||
'parameters' => json_encode($params),
|
|
||||||
'message' => 'バッチ処理開始'
|
|
||||||
])->id;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* バッチログ更新
|
|
||||||
*
|
|
||||||
* @param int $batchLogId バッチログID
|
|
||||||
* @param string $status ステータス
|
|
||||||
* @param string $message メッセージ
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
private function updateBatchLog(int $batchLogId, string $status, string $message): void
|
|
||||||
{
|
|
||||||
$this->batchLogModel->where('id', $batchLogId)->update([
|
|
||||||
'status' => $status,
|
|
||||||
'end_time' => now(),
|
|
||||||
'message' => $message
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SHJ-12 未払い者通知メール送信
|
|
||||||
*
|
|
||||||
* SHJ-12から呼び出される専用メール送信メソッド
|
|
||||||
* 未払い者への通知メールを送信する
|
|
||||||
*
|
|
||||||
* @param array $mailParams メール送信パラメータ
|
|
||||||
* @return array 送信結果
|
|
||||||
*/
|
|
||||||
public function sendUnpaidNotificationMail(array $mailParams): array
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
// パラメータ展開
|
|
||||||
$toEmail = $mailParams['to_email'] ?? '';
|
|
||||||
$userName = $mailParams['user_name'] ?? '';
|
|
||||||
$parkName = $mailParams['park_name'] ?? '';
|
|
||||||
$billingAmount = $mailParams['billing_amount'] ?? 0;
|
|
||||||
$contractId = $mailParams['contract_id'] ?? '';
|
|
||||||
|
|
||||||
// 未払い者通知専用のメールテンプレートID(仮定: 1)
|
|
||||||
// 実際の運用では設定ファイルまたはデータベースから取得
|
|
||||||
$mailTemplateId = 1;
|
|
||||||
|
|
||||||
// メールテンプレート情報取得
|
|
||||||
$templateInfo = $this->getMailTemplateInfo($mailTemplateId);
|
|
||||||
|
|
||||||
if (!$templateInfo) {
|
|
||||||
return [
|
|
||||||
'success' => false,
|
|
||||||
'message' => '未払い者通知用メールテンプレートが見つかりません'
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
// メール件名とメッセージのカスタマイズ
|
|
||||||
$subject = str_replace(
|
|
||||||
['{park_name}', '{user_name}'],
|
|
||||||
[$parkName, $userName],
|
|
||||||
$templateInfo->getSubject()
|
|
||||||
);
|
|
||||||
|
|
||||||
$message = str_replace(
|
|
||||||
['{user_name}', '{park_name}', '{billing_amount}', '{contract_id}'],
|
|
||||||
[$userName, $parkName, number_format($billingAmount), $contractId],
|
|
||||||
$templateInfo->getText()
|
|
||||||
);
|
|
||||||
|
|
||||||
// 追加ヘッダ設定
|
|
||||||
$headers = $this->buildMailHeaders($templateInfo);
|
|
||||||
|
|
||||||
Log::info('SHJ-12 未払い者通知メール送信準備完了', [
|
|
||||||
'to_email' => $toEmail,
|
|
||||||
'user_name' => $userName,
|
|
||||||
'billing_amount' => $billingAmount,
|
|
||||||
'contract_id' => $contractId
|
|
||||||
]);
|
|
||||||
|
|
||||||
// メール送信実行
|
|
||||||
$sendResult = mb_send_mail(
|
|
||||||
$toEmail, // 送信先
|
|
||||||
$subject, // 件名
|
|
||||||
$message, // 本文
|
|
||||||
$headers // 追加ヘッダ
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($sendResult) {
|
|
||||||
Log::info('SHJ-12 未払い者通知メール送信成功', [
|
|
||||||
'to_email' => $toEmail,
|
|
||||||
'contract_id' => $contractId
|
|
||||||
]);
|
|
||||||
|
|
||||||
return [
|
|
||||||
'success' => true,
|
|
||||||
'message' => '未払い者通知メール送信完了'
|
|
||||||
];
|
|
||||||
} else {
|
|
||||||
$errorMessage = '未払い者通知メール送信に失敗しました';
|
|
||||||
Log::error('SHJ-12 未払い者通知メール送信失敗', [
|
|
||||||
'to_email' => $toEmail,
|
|
||||||
'contract_id' => $contractId,
|
|
||||||
'error' => $errorMessage
|
|
||||||
]);
|
|
||||||
|
|
||||||
return [
|
|
||||||
'success' => false,
|
|
||||||
'message' => $errorMessage
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
$errorMessage = 'SHJ-12 未払い者通知メール送信エラー: ' . $e->getMessage();
|
|
||||||
Log::error('SHJ-12 未払い者通知メール送信エラー', [
|
|
||||||
'error' => $e->getMessage(),
|
|
||||||
'mail_params' => $mailParams
|
|
||||||
]);
|
|
||||||
|
|
||||||
return [
|
|
||||||
'success' => false,
|
|
||||||
'message' => $errorMessage
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,595 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Services;
|
|
||||||
|
|
||||||
use App\Models\Park;
|
|
||||||
use App\Models\RegularContract;
|
|
||||||
use App\Models\EarningsSummary;
|
|
||||||
use App\Models\Psection;
|
|
||||||
use App\Models\Batch\BatchLog;
|
|
||||||
use App\Models\OperatorQue;
|
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
use Illuminate\Support\Facades\DB;
|
|
||||||
use Carbon\Carbon;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SHJ-9 売上集計処理サービス
|
|
||||||
*
|
|
||||||
* 日次・月次・年次の売上集計処理を実行するビジネスロジック
|
|
||||||
* バッチ処理「SHJ-9売上集計」の核となる処理を担当
|
|
||||||
*/
|
|
||||||
class ShjNineService
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Park モデル
|
|
||||||
*
|
|
||||||
* @var Park
|
|
||||||
*/
|
|
||||||
protected $parkModel;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* RegularContract モデル
|
|
||||||
*
|
|
||||||
* @var RegularContract
|
|
||||||
*/
|
|
||||||
protected $contractModel;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* EarningsSummary モデル
|
|
||||||
*
|
|
||||||
* @var EarningsSummary
|
|
||||||
*/
|
|
||||||
protected $earningsSummaryModel;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Psection モデル
|
|
||||||
*
|
|
||||||
* @var Psection
|
|
||||||
*/
|
|
||||||
protected $psectionModel;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* BatchLog モデル
|
|
||||||
*
|
|
||||||
* @var BatchLog
|
|
||||||
*/
|
|
||||||
protected $batchLogModel;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* OperatorQue モデル
|
|
||||||
*
|
|
||||||
* @var OperatorQue
|
|
||||||
*/
|
|
||||||
protected $operatorQueModel;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* コンストラクタ
|
|
||||||
*
|
|
||||||
* @param Park $parkModel
|
|
||||||
* @param RegularContract $contractModel
|
|
||||||
* @param EarningsSummary $earningsSummaryModel
|
|
||||||
* @param Psection $psectionModel
|
|
||||||
* @param BatchLog $batchLogModel
|
|
||||||
* @param OperatorQue $operatorQueModel
|
|
||||||
*/
|
|
||||||
public function __construct(
|
|
||||||
Park $parkModel,
|
|
||||||
RegularContract $contractModel,
|
|
||||||
EarningsSummary $earningsSummaryModel,
|
|
||||||
Psection $psectionModel,
|
|
||||||
BatchLog $batchLogModel,
|
|
||||||
OperatorQue $operatorQueModel
|
|
||||||
) {
|
|
||||||
$this->parkModel = $parkModel;
|
|
||||||
$this->contractModel = $contractModel;
|
|
||||||
$this->earningsSummaryModel = $earningsSummaryModel;
|
|
||||||
$this->psectionModel = $psectionModel;
|
|
||||||
$this->batchLogModel = $batchLogModel;
|
|
||||||
$this->operatorQueModel = $operatorQueModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SHJ-9 売上集計処理メイン実行
|
|
||||||
*
|
|
||||||
* 処理フロー:
|
|
||||||
* 【処理1】集計対象を設定する
|
|
||||||
* 【処理2】駐輪場マスタを取得する
|
|
||||||
* 【判断1】取得件数判定
|
|
||||||
* 【処理3】車種区分毎に算出する
|
|
||||||
* 【判断2】取得判定
|
|
||||||
* 【処理4】売上集計結果を削除→登録する
|
|
||||||
* 【処理5】オペレータキュー作成およびバッチ処理ログを作成する
|
|
||||||
*
|
|
||||||
* @param string $type 集計種別(daily/monthly/yearly)
|
|
||||||
* @param string $aggregationDate 集計対象日
|
|
||||||
* @return array 処理結果
|
|
||||||
*/
|
|
||||||
public function executeEarningsAggregation(string $type, string $aggregationDate): array
|
|
||||||
{
|
|
||||||
$batchLogId = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
// 【処理1】集計対象を設定する
|
|
||||||
$aggregationTarget = $this->setAggregationTarget($type, $aggregationDate);
|
|
||||||
|
|
||||||
// バッチ処理開始ログ作成
|
|
||||||
$batchLog = BatchLog::createBatchLog(
|
|
||||||
'shj9',
|
|
||||||
BatchLog::STATUS_START,
|
|
||||||
[
|
|
||||||
'type' => $type,
|
|
||||||
'aggregation_date' => $aggregationDate,
|
|
||||||
'aggregation_target' => $aggregationTarget
|
|
||||||
],
|
|
||||||
"SHJ-9 売上集計処理開始 ({$type})"
|
|
||||||
);
|
|
||||||
$batchLogId = $batchLog->id;
|
|
||||||
|
|
||||||
Log::info('SHJ-9 売上集計処理開始', [
|
|
||||||
'batch_log_id' => $batchLogId,
|
|
||||||
'type' => $type,
|
|
||||||
'aggregation_date' => $aggregationDate,
|
|
||||||
'aggregation_target' => $aggregationTarget
|
|
||||||
]);
|
|
||||||
|
|
||||||
// 【処理2】駐輪場マスタを取得する
|
|
||||||
$parkInfo = $this->getParkInformation();
|
|
||||||
|
|
||||||
// 【判断1】取得件数判定
|
|
||||||
if (empty($parkInfo)) {
|
|
||||||
$message = '売上集計(' . $this->getTypeLabel($type) . '):駐輪場マスタが存在していません。';
|
|
||||||
|
|
||||||
// バッチログ更新
|
|
||||||
$batchLog->update([
|
|
||||||
'status' => BatchLog::STATUS_WARNING,
|
|
||||||
'end_time' => now(),
|
|
||||||
'message' => $message,
|
|
||||||
'success_count' => 1 // 処理は成功したが対象なし
|
|
||||||
]);
|
|
||||||
|
|
||||||
// 【処理5】オペレータキュー作成
|
|
||||||
$this->createOperatorQueue($message, $batchLogId);
|
|
||||||
|
|
||||||
return [
|
|
||||||
'success' => true,
|
|
||||||
'message' => $message,
|
|
||||||
'processed_parks' => 0,
|
|
||||||
'summary_records' => 0,
|
|
||||||
'batch_log_id' => $batchLogId
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
// 【処理3】車種区分毎に算出する & 【処理4】売上集計結果を削除→登録する
|
|
||||||
$summaryRecords = 0;
|
|
||||||
$processedParks = 0;
|
|
||||||
|
|
||||||
foreach ($parkInfo as $park) {
|
|
||||||
$parkSummaryRecords = $this->processEarningsForPark($park, $aggregationTarget, $type);
|
|
||||||
|
|
||||||
if ($parkSummaryRecords > 0) {
|
|
||||||
$processedParks++;
|
|
||||||
$summaryRecords += $parkSummaryRecords;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 【判断2】取得判定
|
|
||||||
if ($summaryRecords === 0) {
|
|
||||||
$message = '対象なしの結果を設定する(契約)';
|
|
||||||
|
|
||||||
// バッチログ更新
|
|
||||||
$batchLog->update([
|
|
||||||
'status' => BatchLog::STATUS_WARNING,
|
|
||||||
'end_time' => now(),
|
|
||||||
'message' => $message,
|
|
||||||
'success_count' => 1
|
|
||||||
]);
|
|
||||||
|
|
||||||
// 【処理5】オペレータキュー作成
|
|
||||||
$this->createOperatorQueue($message, $batchLogId);
|
|
||||||
|
|
||||||
return [
|
|
||||||
'success' => true,
|
|
||||||
'message' => $message,
|
|
||||||
'processed_parks' => $processedParks,
|
|
||||||
'summary_records' => 0,
|
|
||||||
'batch_log_id' => $batchLogId
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
// バッチ処理完了ログ更新
|
|
||||||
$completionMessage = "SHJ-9 売上集計処理正常完了 ({$type}) - 駐輪場数: {$processedParks}, 集計レコード数: {$summaryRecords}";
|
|
||||||
$batchLog->update([
|
|
||||||
'status' => BatchLog::STATUS_SUCCESS,
|
|
||||||
'end_time' => now(),
|
|
||||||
'message' => $completionMessage,
|
|
||||||
'success_count' => 1
|
|
||||||
]);
|
|
||||||
|
|
||||||
// 【処理5】オペレータキュー作成
|
|
||||||
$this->createOperatorQueue($completionMessage, $batchLogId);
|
|
||||||
|
|
||||||
Log::info('SHJ-9 売上集計処理完了', [
|
|
||||||
'batch_log_id' => $batchLogId,
|
|
||||||
'processed_parks' => $processedParks,
|
|
||||||
'summary_records' => $summaryRecords
|
|
||||||
]);
|
|
||||||
|
|
||||||
return [
|
|
||||||
'success' => true,
|
|
||||||
'message' => 'SHJ-9 売上集計処理が正常に完了しました',
|
|
||||||
'processed_parks' => $processedParks,
|
|
||||||
'summary_records' => $summaryRecords,
|
|
||||||
'batch_log_id' => $batchLogId
|
|
||||||
];
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
$errorMessage = 'SHJ-9 売上集計処理でエラーが発生: ' . $e->getMessage();
|
|
||||||
|
|
||||||
if (isset($batchLog) && $batchLog) {
|
|
||||||
$batchLog->update([
|
|
||||||
'status' => BatchLog::STATUS_ERROR,
|
|
||||||
'end_time' => now(),
|
|
||||||
'message' => $errorMessage,
|
|
||||||
'error_details' => $e->getMessage(),
|
|
||||||
'error_count' => 1
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
Log::error('SHJ-9 売上集計処理エラー', [
|
|
||||||
'batch_log_id' => $batchLogId,
|
|
||||||
'exception' => $e->getMessage(),
|
|
||||||
'trace' => $e->getTraceAsString()
|
|
||||||
]);
|
|
||||||
|
|
||||||
return [
|
|
||||||
'success' => false,
|
|
||||||
'message' => $errorMessage,
|
|
||||||
'details' => $e->getMessage(),
|
|
||||||
'batch_log_id' => $batchLogId
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 【処理1】集計対象を設定する
|
|
||||||
*
|
|
||||||
* @param string $type 集計種別
|
|
||||||
* @param string $aggregationDate 集計対象日
|
|
||||||
* @return array 集計対象情報
|
|
||||||
*/
|
|
||||||
private function setAggregationTarget(string $type, string $aggregationDate): array
|
|
||||||
{
|
|
||||||
$date = Carbon::parse($aggregationDate);
|
|
||||||
|
|
||||||
switch ($type) {
|
|
||||||
case 'daily':
|
|
||||||
return [
|
|
||||||
'type' => 'daily',
|
|
||||||
'start_date' => $date->format('Y-m-d'),
|
|
||||||
'end_date' => $date->format('Y-m-d'),
|
|
||||||
'summary_type' => '日次'
|
|
||||||
];
|
|
||||||
|
|
||||||
case 'monthly':
|
|
||||||
return [
|
|
||||||
'type' => 'monthly',
|
|
||||||
'start_date' => $date->startOfMonth()->format('Y-m-d'),
|
|
||||||
'end_date' => $date->endOfMonth()->format('Y-m-d'),
|
|
||||||
'summary_type' => '月次'
|
|
||||||
];
|
|
||||||
|
|
||||||
case 'yearly':
|
|
||||||
return [
|
|
||||||
'type' => 'yearly',
|
|
||||||
'start_date' => $date->startOfYear()->format('Y-m-d'),
|
|
||||||
'end_date' => $date->endOfYear()->format('Y-m-d'),
|
|
||||||
'summary_type' => '年次'
|
|
||||||
];
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new \InvalidArgumentException("不正な集計種別: {$type}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 【処理2】駐輪場マスタを取得する
|
|
||||||
*
|
|
||||||
* 仕様書のSQLクエリに基づく駐輪場情報取得
|
|
||||||
* SELECT 駐輪場ID, 駐輪場名
|
|
||||||
* FROM 駐輪場マスタ
|
|
||||||
* WHERE 閉設フラグ <> 1
|
|
||||||
* ORDER BY 駐輪場ふりがな
|
|
||||||
*
|
|
||||||
* @return array 駐輪場情報
|
|
||||||
*/
|
|
||||||
private function getParkInformation(): array
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
$parkInfo = DB::table('park')
|
|
||||||
->select(['park_id', 'park_name'])
|
|
||||||
->where('park_close_flag', '<>', 1)
|
|
||||||
->orderBy('park_ruby')
|
|
||||||
->get()
|
|
||||||
->toArray();
|
|
||||||
|
|
||||||
Log::info('駐輪場マスタ取得完了', [
|
|
||||||
'park_count' => count($parkInfo)
|
|
||||||
]);
|
|
||||||
|
|
||||||
return $parkInfo;
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Log::error('駐輪場マスタ取得エラー', [
|
|
||||||
'error' => $e->getMessage()
|
|
||||||
]);
|
|
||||||
|
|
||||||
throw $e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 駐輪場毎の売上集計処理
|
|
||||||
*
|
|
||||||
* @param object $park 駐輪場情報
|
|
||||||
* @param array $aggregationTarget 集計対象
|
|
||||||
* @param string $type 集計種別
|
|
||||||
* @return int 作成された集計レコード数
|
|
||||||
*/
|
|
||||||
private function processEarningsForPark($park, array $aggregationTarget, string $type): int
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
// 【処理4】既存の売上集計結果を削除
|
|
||||||
$this->deleteExistingSummary($park->park_id, $aggregationTarget);
|
|
||||||
|
|
||||||
// 【処理3】車種区分毎に算出する
|
|
||||||
$psections = $this->getPsectionInformation();
|
|
||||||
$summaryRecords = 0;
|
|
||||||
|
|
||||||
foreach ($psections as $psection) {
|
|
||||||
$earningsData = $this->calculateEarningsForPsection(
|
|
||||||
$park->park_id,
|
|
||||||
$psection->psection_id,
|
|
||||||
$aggregationTarget
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($this->hasEarningsData($earningsData)) {
|
|
||||||
// 売上集計結果を登録
|
|
||||||
$this->createEarningsSummary($park, $psection, $aggregationTarget, $earningsData, $type);
|
|
||||||
$summaryRecords++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Log::info('駐輪場売上集計完了', [
|
|
||||||
'park_id' => $park->park_id,
|
|
||||||
'park_name' => $park->park_name,
|
|
||||||
'summary_records' => $summaryRecords
|
|
||||||
]);
|
|
||||||
|
|
||||||
return $summaryRecords;
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Log::error('駐輪場売上集計エラー', [
|
|
||||||
'park_id' => $park->park_id,
|
|
||||||
'error' => $e->getMessage()
|
|
||||||
]);
|
|
||||||
|
|
||||||
throw $e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 車種区分情報取得
|
|
||||||
*
|
|
||||||
* @return array 車種区分情報
|
|
||||||
*/
|
|
||||||
private function getPsectionInformation(): array
|
|
||||||
{
|
|
||||||
return DB::table('psection')
|
|
||||||
->select(['psection_id', 'psection_subject'])
|
|
||||||
->get()
|
|
||||||
->toArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 【処理3】車種区分毎に売上を算出する
|
|
||||||
*
|
|
||||||
* 4つの項目を計算:
|
|
||||||
* ①売上・件数
|
|
||||||
* ②一時金売上
|
|
||||||
* ③解約返戻金
|
|
||||||
* ④再発行金額・件数
|
|
||||||
*
|
|
||||||
* @param int $parkId 駐輪場ID
|
|
||||||
* @param int $psectionId 車種区分ID
|
|
||||||
* @param array $aggregationTarget 集計対象
|
|
||||||
* @return array 売上データ
|
|
||||||
*/
|
|
||||||
private function calculateEarningsForPsection(int $parkId, int $psectionId, array $aggregationTarget): array
|
|
||||||
{
|
|
||||||
$startDate = $aggregationTarget['start_date'];
|
|
||||||
$endDate = $aggregationTarget['end_date'];
|
|
||||||
|
|
||||||
// ①売上・件数(billing_amount)
|
|
||||||
$salesData = DB::table('regular_contract')
|
|
||||||
->select([
|
|
||||||
DB::raw('COUNT(*) as sales_count'),
|
|
||||||
DB::raw('COALESCE(SUM(billing_amount), 0) as sales_amount')
|
|
||||||
])
|
|
||||||
->where('park_id', $parkId)
|
|
||||||
->where('psection_id', $psectionId)
|
|
||||||
->where('contract_flag', 1)
|
|
||||||
->whereBetween('contract_payment_day', [$startDate, $endDate])
|
|
||||||
->whereNull('contract_cancelday')
|
|
||||||
->first();
|
|
||||||
|
|
||||||
// ②一時金売上(contract_money)
|
|
||||||
$temporaryData = DB::table('regular_contract')
|
|
||||||
->select([
|
|
||||||
DB::raw('COUNT(*) as temporary_count'),
|
|
||||||
DB::raw('COALESCE(SUM(contract_money), 0) as temporary_amount')
|
|
||||||
])
|
|
||||||
->where('park_id', $parkId)
|
|
||||||
->where('psection_id', $psectionId)
|
|
||||||
->where('contract_flag', 1)
|
|
||||||
->whereBetween('contract_payment_day', [$startDate, $endDate])
|
|
||||||
->whereNotNull('contract_money')
|
|
||||||
->where('contract_money', '>', 0)
|
|
||||||
->first();
|
|
||||||
|
|
||||||
// ③解約返戻金(refunds)
|
|
||||||
$refundData = DB::table('regular_contract')
|
|
||||||
->select([
|
|
||||||
DB::raw('COUNT(*) as refund_count'),
|
|
||||||
DB::raw('COALESCE(SUM(refunds), 0) as refund_amount')
|
|
||||||
])
|
|
||||||
->where('park_id', $parkId)
|
|
||||||
->where('psection_id', $psectionId)
|
|
||||||
->whereBetween('contract_cancelday', [$startDate, $endDate])
|
|
||||||
->whereNotNull('refunds')
|
|
||||||
->where('refunds', '>', 0)
|
|
||||||
->first();
|
|
||||||
|
|
||||||
// ④再発行金額・件数(seal_reissue_request)
|
|
||||||
$reissueData = DB::table('regular_contract')
|
|
||||||
->select([
|
|
||||||
DB::raw('COUNT(*) as reissue_count'),
|
|
||||||
DB::raw('COALESCE(SUM(contract_seal_issue), 0) as reissue_amount')
|
|
||||||
])
|
|
||||||
->where('park_id', $parkId)
|
|
||||||
->where('psection_id', $psectionId)
|
|
||||||
->where('seal_reissue_request', 1)
|
|
||||||
->whereBetween('updated_at', [$startDate, $endDate])
|
|
||||||
->first();
|
|
||||||
|
|
||||||
return [
|
|
||||||
'sales_count' => $salesData->sales_count ?? 0,
|
|
||||||
'sales_amount' => $salesData->sales_amount ?? 0,
|
|
||||||
'temporary_count' => $temporaryData->temporary_count ?? 0,
|
|
||||||
'temporary_amount' => $temporaryData->temporary_amount ?? 0,
|
|
||||||
'refund_count' => $refundData->refund_count ?? 0,
|
|
||||||
'refund_amount' => $refundData->refund_amount ?? 0,
|
|
||||||
'reissue_count' => $reissueData->reissue_count ?? 0,
|
|
||||||
'reissue_amount' => $reissueData->reissue_amount ?? 0
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 売上データの存在チェック
|
|
||||||
*
|
|
||||||
* @param array $earningsData 売上データ
|
|
||||||
* @return bool データが存在するかどうか
|
|
||||||
*/
|
|
||||||
private function hasEarningsData(array $earningsData): bool
|
|
||||||
{
|
|
||||||
return $earningsData['sales_count'] > 0 ||
|
|
||||||
$earningsData['temporary_count'] > 0 ||
|
|
||||||
$earningsData['refund_count'] > 0 ||
|
|
||||||
$earningsData['reissue_count'] > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 【処理4】既存の売上集計結果を削除
|
|
||||||
*
|
|
||||||
* @param int $parkId 駐輪場ID
|
|
||||||
* @param array $aggregationTarget 集計対象
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
private function deleteExistingSummary(int $parkId, array $aggregationTarget): void
|
|
||||||
{
|
|
||||||
DB::table('earnings_summary')
|
|
||||||
->where('park_id', $parkId)
|
|
||||||
->where('summary_start_date', $aggregationTarget['start_date'])
|
|
||||||
->where('summary_end_date', $aggregationTarget['end_date'])
|
|
||||||
->delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 売上集計結果を登録
|
|
||||||
*
|
|
||||||
* @param object $park 駐輪場情報
|
|
||||||
* @param object $psection 車種区分情報
|
|
||||||
* @param array $aggregationTarget 集計対象
|
|
||||||
* @param array $earningsData 売上データ
|
|
||||||
* @param string $type 集計種別
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
private function createEarningsSummary($park, $psection, array $aggregationTarget, array $earningsData, string $type): void
|
|
||||||
{
|
|
||||||
DB::table('earnings_summary')->insert([
|
|
||||||
'park_id' => $park->park_id,
|
|
||||||
'summary_type' => $aggregationTarget['summary_type'],
|
|
||||||
'summary_start_date' => $aggregationTarget['start_date'],
|
|
||||||
'summary_end_date' => $aggregationTarget['end_date'],
|
|
||||||
'earnings_date' => $aggregationTarget['end_date'], // 集計日として終了日を使用
|
|
||||||
'psection_id' => $psection->psection_id,
|
|
||||||
'usertype_subject1' => $psection->psection_subject,
|
|
||||||
'regular_new_count' => $earningsData['sales_count'],
|
|
||||||
'regular_new_amount' => $earningsData['sales_amount'],
|
|
||||||
'turnsum' => $earningsData['temporary_amount'],
|
|
||||||
'turnsum_count' => $earningsData['temporary_count'],
|
|
||||||
'refunds' => $earningsData['refund_amount'],
|
|
||||||
'reissue_count' => $earningsData['reissue_count'],
|
|
||||||
'reissue_amount' => $earningsData['reissue_amount'],
|
|
||||||
'summary_note' => "SHJ-9 {$type} 売上集計結果",
|
|
||||||
'created_at' => now(),
|
|
||||||
'updated_at' => now(),
|
|
||||||
'operator_id' => 0 // システム処理
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 【処理5】オペレータキュー作成
|
|
||||||
*
|
|
||||||
* @param string $message メッセージ
|
|
||||||
* @param int $batchLogId バッチログID
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
private function createOperatorQueue(string $message, int $batchLogId): void
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
DB::table('operator_que')->insert([
|
|
||||||
'que_class' => 9, // SHJ-9用のクラス
|
|
||||||
'user_id' => null,
|
|
||||||
'contract_id' => null,
|
|
||||||
'park_id' => null,
|
|
||||||
'que_comment' => $message,
|
|
||||||
'que_status' => 1, // 完了
|
|
||||||
'que_status_comment' => 'バッチ処理完了',
|
|
||||||
'work_instructions' => "SHJ-9売上集計処理 BatchLogID: {$batchLogId}",
|
|
||||||
'created_at' => now(),
|
|
||||||
'updated_at' => now()
|
|
||||||
]);
|
|
||||||
|
|
||||||
Log::info('オペレータキュー作成完了', [
|
|
||||||
'batch_log_id' => $batchLogId,
|
|
||||||
'message' => $message
|
|
||||||
]);
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Log::error('オペレータキュー作成エラー', [
|
|
||||||
'batch_log_id' => $batchLogId,
|
|
||||||
'error' => $e->getMessage()
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 集計種別のラベル取得
|
|
||||||
*
|
|
||||||
* @param string $type 集計種別
|
|
||||||
* @return string ラベル
|
|
||||||
*/
|
|
||||||
private function getTypeLabel(string $type): string
|
|
||||||
{
|
|
||||||
switch ($type) {
|
|
||||||
case 'daily':
|
|
||||||
return '日次';
|
|
||||||
case 'monthly':
|
|
||||||
return '月次';
|
|
||||||
case 'yearly':
|
|
||||||
return '年次';
|
|
||||||
default:
|
|
||||||
return $type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,710 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Services;
|
|
||||||
|
|
||||||
use App\Models\Device;
|
|
||||||
use App\Models\HardwareCheckLog;
|
|
||||||
use App\Models\PrintJobLog;
|
|
||||||
use App\Models\Batch\BatchLog;
|
|
||||||
use App\Models\OperatorQue;
|
|
||||||
use App\Models\User;
|
|
||||||
use App\Models\Park;
|
|
||||||
use App\Services\ShjMailSendService;
|
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
use Illuminate\Support\Facades\DB;
|
|
||||||
use Carbon\Carbon;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SHJ-6 サーバ死活監視処理サービス
|
|
||||||
*
|
|
||||||
* サーバとデバイスの死活監視、ハードウェア状態監視、プリンタログ監視を実行
|
|
||||||
* 異常検出時のメール通知機能を含む統合モニタリングサービス
|
|
||||||
*/
|
|
||||||
class ShjSixService
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Device モデル
|
|
||||||
*
|
|
||||||
* @var Device
|
|
||||||
*/
|
|
||||||
protected $deviceModel;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* HardwareCheckLog モデル
|
|
||||||
*
|
|
||||||
* @var HardwareCheckLog
|
|
||||||
*/
|
|
||||||
protected $hardwareCheckLogModel;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* PrintJobLog モデル
|
|
||||||
*
|
|
||||||
* @var PrintJobLog
|
|
||||||
*/
|
|
||||||
protected $printJobLogModel;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* BatchLog モデル
|
|
||||||
*
|
|
||||||
* @var BatchLog
|
|
||||||
*/
|
|
||||||
protected $batchLogModel;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* OperatorQue モデル
|
|
||||||
*
|
|
||||||
* @var OperatorQue
|
|
||||||
*/
|
|
||||||
protected $operatorQueModel;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ShjMailSendService
|
|
||||||
*
|
|
||||||
* @var ShjMailSendService
|
|
||||||
*/
|
|
||||||
protected $mailSendService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 固定メールアドレス(DB反映NG時用)
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
const FIXED_EMAIL_ADDRESS = 'system-alert@so-manager.com';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* プリンタログ監視期間(分)
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
const PRINTER_LOG_MONITOR_MINUTES = 15;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* コンストラクタ
|
|
||||||
*
|
|
||||||
* @param Device $deviceModel
|
|
||||||
* @param HardwareCheckLog $hardwareCheckLogModel
|
|
||||||
* @param PrintJobLog $printJobLogModel
|
|
||||||
* @param BatchLog $batchLogModel
|
|
||||||
* @param OperatorQue $operatorQueModel
|
|
||||||
* @param ShjMailSendService $mailSendService
|
|
||||||
*/
|
|
||||||
public function __construct(
|
|
||||||
Device $deviceModel,
|
|
||||||
HardwareCheckLog $hardwareCheckLogModel,
|
|
||||||
PrintJobLog $printJobLogModel,
|
|
||||||
BatchLog $batchLogModel,
|
|
||||||
OperatorQue $operatorQueModel,
|
|
||||||
ShjMailSendService $mailSendService
|
|
||||||
) {
|
|
||||||
$this->deviceModel = $deviceModel;
|
|
||||||
$this->hardwareCheckLogModel = $hardwareCheckLogModel;
|
|
||||||
$this->printJobLogModel = $printJobLogModel;
|
|
||||||
$this->batchLogModel = $batchLogModel;
|
|
||||||
$this->operatorQueModel = $operatorQueModel;
|
|
||||||
$this->mailSendService = $mailSendService;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SHJ-6 サーバ死活監視処理メイン実行
|
|
||||||
*
|
|
||||||
* 処理フロー:
|
|
||||||
* 【処理1】サーバ死活監視(DBアクセス)
|
|
||||||
* 【処理2】デバイス管理マスタを取得する
|
|
||||||
* 【処理3】デバイス毎のハードウェア状態を取得する
|
|
||||||
* 【処理4】プリンタ制御プログラムログを取得する
|
|
||||||
* 【判断3】エラーログ有無
|
|
||||||
* 【処理5】バッチ処理ログを作成する
|
|
||||||
* ※ 異常時は共通A処理を実行
|
|
||||||
*
|
|
||||||
* @return array 処理結果
|
|
||||||
*/
|
|
||||||
public function executeServerMonitoring(): array
|
|
||||||
{
|
|
||||||
$batchLogId = null;
|
|
||||||
$warnings = [];
|
|
||||||
$errorDetails = [];
|
|
||||||
|
|
||||||
try {
|
|
||||||
// バッチ処理開始ログ作成
|
|
||||||
$batchLog = BatchLog::createBatchLog(
|
|
||||||
'shj6',
|
|
||||||
BatchLog::STATUS_START,
|
|
||||||
[],
|
|
||||||
'SHJ-6 サーバ死活監視処理開始'
|
|
||||||
);
|
|
||||||
$batchLogId = $batchLog->id;
|
|
||||||
|
|
||||||
Log::info('SHJ-6 サーバ死活監視処理開始', [
|
|
||||||
'batch_log_id' => $batchLogId
|
|
||||||
]);
|
|
||||||
|
|
||||||
// 【処理1】サーバ死活監視(DBアクセス)
|
|
||||||
$dbAccessResult = $this->checkDatabaseAccess();
|
|
||||||
if (!$dbAccessResult['success']) {
|
|
||||||
// DB接続NGの場合は共通A処理実行
|
|
||||||
$this->executeCommonProcessA($batchLogId, 'DB接続エラー: ' . $dbAccessResult['message']);
|
|
||||||
|
|
||||||
return [
|
|
||||||
'success' => false,
|
|
||||||
'message' => 'データベース接続エラーが発生しました',
|
|
||||||
'error_details' => [$dbAccessResult['message']],
|
|
||||||
'batch_log_id' => $batchLogId
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
// 【処理2】デバイス管理マスタを取得する
|
|
||||||
$devices = $this->getDeviceManagementData();
|
|
||||||
Log::info('デバイス管理マスタ取得完了', [
|
|
||||||
'device_count' => count($devices)
|
|
||||||
]);
|
|
||||||
|
|
||||||
// 【処理3】デバイス毎のハードウェア状態を取得する
|
|
||||||
$hardwareStatusResult = $this->getHardwareStatus($devices);
|
|
||||||
if (!$hardwareStatusResult['success']) {
|
|
||||||
// ハードウェア状態取得できなかった場合は共通A処理実行
|
|
||||||
$this->executeCommonProcessA($batchLogId, 'ハードウェア状態取得エラー: ' . $hardwareStatusResult['message']);
|
|
||||||
$warnings[] = 'ハードウェア状態の一部で異常を検出しました';
|
|
||||||
$errorDetails[] = $hardwareStatusResult['message'];
|
|
||||||
}
|
|
||||||
|
|
||||||
// 【処理4】プリンタ制御プログラムログを取得する
|
|
||||||
$printerLogResult = $this->getPrinterControlLogs();
|
|
||||||
|
|
||||||
// 【判断3】エラーログ有無
|
|
||||||
if ($printerLogResult['has_errors']) {
|
|
||||||
// エラーログ有の場合は共通A処理実行
|
|
||||||
$this->executeCommonProcessA($batchLogId, 'プリンタエラーログ検出: ' . $printerLogResult['error_summary']);
|
|
||||||
$warnings[] = 'プリンタ制御でエラーが検出されました';
|
|
||||||
$errorDetails[] = $printerLogResult['error_summary'];
|
|
||||||
}
|
|
||||||
|
|
||||||
// 【処理5】バッチ処理ログを作成する
|
|
||||||
$monitoringSummary = $this->createMonitoringSummary($devices, $hardwareStatusResult, $printerLogResult);
|
|
||||||
|
|
||||||
$status = empty($warnings) ? BatchLog::STATUS_SUCCESS : BatchLog::STATUS_WARNING;
|
|
||||||
$message = empty($warnings) ?
|
|
||||||
'SHJ-6 サーバ死活監視処理正常完了' :
|
|
||||||
'SHJ-6 サーバ死活監視処理完了(警告あり)';
|
|
||||||
|
|
||||||
$batchLog->update([
|
|
||||||
'status' => $status,
|
|
||||||
'end_time' => now(),
|
|
||||||
'message' => $message,
|
|
||||||
'success_count' => 1
|
|
||||||
]);
|
|
||||||
|
|
||||||
Log::info('SHJ-6 サーバ死活監視処理完了', [
|
|
||||||
'batch_log_id' => $batchLogId,
|
|
||||||
'monitoring_summary' => $monitoringSummary,
|
|
||||||
'warnings' => $warnings
|
|
||||||
]);
|
|
||||||
|
|
||||||
return [
|
|
||||||
'success' => true,
|
|
||||||
'message' => 'SHJ-6 サーバ死活監視処理が完了しました',
|
|
||||||
'monitoring_summary' => $monitoringSummary,
|
|
||||||
'warnings' => $warnings,
|
|
||||||
'error_details' => $errorDetails,
|
|
||||||
'batch_log_id' => $batchLogId
|
|
||||||
];
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
$errorMessage = 'SHJ-6 サーバ死活監視処理でエラーが発生: ' . $e->getMessage();
|
|
||||||
|
|
||||||
if (isset($batchLog) && $batchLog) {
|
|
||||||
$batchLog->update([
|
|
||||||
'status' => BatchLog::STATUS_ERROR,
|
|
||||||
'end_time' => now(),
|
|
||||||
'message' => $errorMessage,
|
|
||||||
'error_details' => $e->getMessage(),
|
|
||||||
'error_count' => 1
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 例外発生時も共通A処理実行
|
|
||||||
$this->executeCommonProcessA($batchLogId, $errorMessage);
|
|
||||||
|
|
||||||
Log::error('SHJ-6 サーバ死活監視処理エラー', [
|
|
||||||
'batch_log_id' => $batchLogId,
|
|
||||||
'exception' => $e->getMessage(),
|
|
||||||
'trace' => $e->getTraceAsString()
|
|
||||||
]);
|
|
||||||
|
|
||||||
return [
|
|
||||||
'success' => false,
|
|
||||||
'message' => $errorMessage,
|
|
||||||
'error_details' => [$e->getMessage()],
|
|
||||||
'batch_log_id' => $batchLogId
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 【処理1】サーバ死活監視(DBアクセス)
|
|
||||||
*
|
|
||||||
* @return array アクセス結果
|
|
||||||
*/
|
|
||||||
private function checkDatabaseAccess(): array
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
// 設定マスタテーブルへの簡単なクエリでDB接続確認
|
|
||||||
$result = DB::select('SELECT 1 as test');
|
|
||||||
|
|
||||||
if (empty($result)) {
|
|
||||||
return [
|
|
||||||
'success' => false,
|
|
||||||
'message' => 'データベースクエリの結果が空です'
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
Log::info('データベース接続確認成功');
|
|
||||||
|
|
||||||
return [
|
|
||||||
'success' => true,
|
|
||||||
'message' => 'データベース接続正常'
|
|
||||||
];
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Log::error('データベース接続エラー', [
|
|
||||||
'error' => $e->getMessage()
|
|
||||||
]);
|
|
||||||
|
|
||||||
return [
|
|
||||||
'success' => false,
|
|
||||||
'message' => $e->getMessage()
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 【処理2】デバイス管理マスタを取得する
|
|
||||||
*
|
|
||||||
* @return array デバイス情報
|
|
||||||
*/
|
|
||||||
private function getDeviceManagementData(): array
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
$devices = DB::table('device')
|
|
||||||
->select([
|
|
||||||
'device_id',
|
|
||||||
'park_id',
|
|
||||||
'device_type',
|
|
||||||
'device_subject',
|
|
||||||
'device_identifier',
|
|
||||||
'device_work',
|
|
||||||
'device_workstart',
|
|
||||||
'device_replace',
|
|
||||||
'device_remarks',
|
|
||||||
'operator_id'
|
|
||||||
])
|
|
||||||
->where('device_workstart', '<=', now())
|
|
||||||
->whereNull('device_replace')
|
|
||||||
->get()
|
|
||||||
->toArray();
|
|
||||||
|
|
||||||
Log::info('デバイス管理マスタ取得完了', [
|
|
||||||
'device_count' => count($devices)
|
|
||||||
]);
|
|
||||||
|
|
||||||
return $devices;
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Log::error('デバイス管理マスタ取得エラー', [
|
|
||||||
'error' => $e->getMessage()
|
|
||||||
]);
|
|
||||||
|
|
||||||
throw $e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 【処理3】デバイス毎のハードウェア状態を取得する
|
|
||||||
*
|
|
||||||
* @param array $devices デバイス一覧
|
|
||||||
* @return array ハードウェア状態結果
|
|
||||||
*/
|
|
||||||
private function getHardwareStatus(array $devices): array
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
$normalDevices = 0;
|
|
||||||
$abnormalDevices = 0;
|
|
||||||
$abnormalDetails = [];
|
|
||||||
|
|
||||||
foreach ($devices as $device) {
|
|
||||||
$latestStatus = HardwareCheckLog::getLatestStatusByDevice($device->device_id);
|
|
||||||
|
|
||||||
if (!$latestStatus) {
|
|
||||||
$abnormalDevices++;
|
|
||||||
$abnormalDetails[] = "デバイスID {$device->device_id}: ハードウェア状態ログが存在しません";
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($latestStatus->isNormal()) {
|
|
||||||
$normalDevices++;
|
|
||||||
} else {
|
|
||||||
$abnormalDevices++;
|
|
||||||
$abnormalDetails[] = "デバイスID {$device->device_id}: {$latestStatus->getStatusNameAttribute()} - {$latestStatus->status_comment}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Log::info('ハードウェア状態取得完了', [
|
|
||||||
'total_devices' => count($devices),
|
|
||||||
'normal_devices' => $normalDevices,
|
|
||||||
'abnormal_devices' => $abnormalDevices
|
|
||||||
]);
|
|
||||||
|
|
||||||
return [
|
|
||||||
'success' => $abnormalDevices === 0,
|
|
||||||
'total_devices' => count($devices),
|
|
||||||
'normal_devices' => $normalDevices,
|
|
||||||
'abnormal_devices' => $abnormalDevices,
|
|
||||||
'abnormal_details' => $abnormalDetails,
|
|
||||||
'message' => $abnormalDevices > 0 ?
|
|
||||||
"{$abnormalDevices}台のデバイスで異常を検出" :
|
|
||||||
'全デバイス正常'
|
|
||||||
];
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Log::error('ハードウェア状態取得エラー', [
|
|
||||||
'error' => $e->getMessage()
|
|
||||||
]);
|
|
||||||
|
|
||||||
return [
|
|
||||||
'success' => false,
|
|
||||||
'message' => $e->getMessage(),
|
|
||||||
'abnormal_details' => ["ハードウェア状態取得中にエラーが発生: " . $e->getMessage()]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 【処理4】プリンタ制御プログラムログを取得する
|
|
||||||
*
|
|
||||||
* @return array プリンタログ結果
|
|
||||||
*/
|
|
||||||
private function getPrinterControlLogs(): array
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
// 過去15分間のエラーログを取得
|
|
||||||
$errorLogs = PrintJobLog::getRecentErrorLogs();
|
|
||||||
|
|
||||||
$hasErrors = $errorLogs->count() > 0;
|
|
||||||
$errorSummary = '';
|
|
||||||
$errorDetails = [];
|
|
||||||
|
|
||||||
if ($hasErrors) {
|
|
||||||
$errorsByCode = $errorLogs->groupBy('error_code');
|
|
||||||
$errorSummaryParts = [];
|
|
||||||
|
|
||||||
foreach ($errorsByCode as $errorCode => $logs) {
|
|
||||||
$count = $logs->count();
|
|
||||||
$errorSummaryParts[] = "エラーコード{$errorCode}: {$count}件";
|
|
||||||
|
|
||||||
foreach ($logs as $log) {
|
|
||||||
$errorDetails[] = sprintf(
|
|
||||||
"[%s] %s - エラーコード: %d, %s",
|
|
||||||
$log->created_at->format('Y-m-d H:i:s'),
|
|
||||||
$log->process_name,
|
|
||||||
$log->error_code,
|
|
||||||
$log->status_comment
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$errorSummary = implode(', ', $errorSummaryParts);
|
|
||||||
}
|
|
||||||
|
|
||||||
Log::info('プリンタ制御プログラムログ取得完了', [
|
|
||||||
'monitoring_period_minutes' => self::PRINTER_LOG_MONITOR_MINUTES,
|
|
||||||
'error_logs_count' => $errorLogs->count(),
|
|
||||||
'has_errors' => $hasErrors
|
|
||||||
]);
|
|
||||||
|
|
||||||
return [
|
|
||||||
'success' => true,
|
|
||||||
'has_errors' => $hasErrors,
|
|
||||||
'error_count' => $errorLogs->count(),
|
|
||||||
'error_summary' => $errorSummary,
|
|
||||||
'error_details' => $errorDetails,
|
|
||||||
'monitoring_period' => self::PRINTER_LOG_MONITOR_MINUTES . '分間'
|
|
||||||
];
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Log::error('プリンタ制御プログラムログ取得エラー', [
|
|
||||||
'error' => $e->getMessage()
|
|
||||||
]);
|
|
||||||
|
|
||||||
return [
|
|
||||||
'success' => false,
|
|
||||||
'has_errors' => true,
|
|
||||||
'error_summary' => 'ログ取得エラー: ' . $e->getMessage(),
|
|
||||||
'error_details' => ["プリンタログ取得中にエラーが発生: " . $e->getMessage()]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 監視結果サマリーを作成
|
|
||||||
*
|
|
||||||
* @param array $devices デバイス一覧
|
|
||||||
* @param array $hardwareResult ハードウェア結果
|
|
||||||
* @param array $printerResult プリンタ結果
|
|
||||||
* @return string 監視サマリー
|
|
||||||
*/
|
|
||||||
private function createMonitoringSummary(array $devices, array $hardwareResult, array $printerResult): string
|
|
||||||
{
|
|
||||||
$summary = [
|
|
||||||
'デバイス数: ' . count($devices),
|
|
||||||
'ハードウェア正常: ' . ($hardwareResult['normal_devices'] ?? 0) . '台',
|
|
||||||
'ハードウェア異常: ' . ($hardwareResult['abnormal_devices'] ?? 0) . '台',
|
|
||||||
'プリンタエラー: ' . ($printerResult['error_count'] ?? 0) . '件'
|
|
||||||
];
|
|
||||||
|
|
||||||
return implode(', ', $summary);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 共通A処理:監視結果を反映
|
|
||||||
*
|
|
||||||
* @param int|null $batchLogId バッチログID
|
|
||||||
* @param string $alertMessage アラートメッセージ
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
private function executeCommonProcessA(?int $batchLogId, string $alertMessage): void
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
Log::info('共通A処理開始', [
|
|
||||||
'batch_log_id' => $batchLogId,
|
|
||||||
'alert_message' => $alertMessage
|
|
||||||
]);
|
|
||||||
|
|
||||||
// 【共通判断1】DB反映可否判定
|
|
||||||
$canReflectToDb = $this->canReflectToDatabase();
|
|
||||||
|
|
||||||
if ($canReflectToDb) {
|
|
||||||
// 【共通処理1】オペレータキューを登録する
|
|
||||||
$this->registerOperatorQueue($alertMessage, $batchLogId);
|
|
||||||
|
|
||||||
// 【共通処理2】メール送信対象オペレータを取得する
|
|
||||||
$operators = $this->getMailTargetOperators();
|
|
||||||
|
|
||||||
// 【共通判断2】送信対象有無
|
|
||||||
if (!empty($operators)) {
|
|
||||||
foreach ($operators as $operator) {
|
|
||||||
$this->sendAlertMail($operator['email'], $alertMessage, 'オペレータ');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 【共通処理3】駐輪場管理者を取得する
|
|
||||||
$parkManagers = $this->getParkManagers();
|
|
||||||
|
|
||||||
// 【共通判断3】送信対象有無
|
|
||||||
if (!empty($parkManagers)) {
|
|
||||||
foreach ($parkManagers as $manager) {
|
|
||||||
$this->sendAlertMail($manager['email'], $alertMessage, '駐輪場管理者');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// DB反映NGの場合は固定メールアドレスに送信
|
|
||||||
$this->sendAlertMail(self::FIXED_EMAIL_ADDRESS, $alertMessage, 'システム管理者');
|
|
||||||
}
|
|
||||||
|
|
||||||
Log::info('共通A処理完了', [
|
|
||||||
'batch_log_id' => $batchLogId
|
|
||||||
]);
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Log::error('共通A処理エラー', [
|
|
||||||
'batch_log_id' => $batchLogId,
|
|
||||||
'error' => $e->getMessage()
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DB反映可否判定
|
|
||||||
*
|
|
||||||
* @return bool 反映可能かどうか
|
|
||||||
*/
|
|
||||||
private function canReflectToDatabase(): bool
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
// 簡単なINSERTテストでDB反映可否を確認
|
|
||||||
DB::beginTransaction();
|
|
||||||
$testId = DB::table('operator_que')->insertGetId([
|
|
||||||
'que_class' => 6,
|
|
||||||
'que_comment' => 'DB反映テスト',
|
|
||||||
'que_status' => 0,
|
|
||||||
'created_at' => now(),
|
|
||||||
'updated_at' => now()
|
|
||||||
]);
|
|
||||||
|
|
||||||
// テストレコードを削除
|
|
||||||
DB::table('operator_que')->where('que_id', $testId)->delete();
|
|
||||||
DB::commit();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
DB::rollBack();
|
|
||||||
Log::warning('DB反映不可', ['error' => $e->getMessage()]);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* オペレータキューを登録
|
|
||||||
*
|
|
||||||
* @param string $alertMessage アラートメッセージ
|
|
||||||
* @param int|null $batchLogId バッチログID
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
private function registerOperatorQueue(string $alertMessage, ?int $batchLogId): void
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
OperatorQue::create([
|
|
||||||
'que_class' => 6, // SHJ-6用のクラス
|
|
||||||
'user_id' => null,
|
|
||||||
'contract_id' => null,
|
|
||||||
'park_id' => null,
|
|
||||||
'que_comment' => $alertMessage,
|
|
||||||
'que_status' => 0, // 待機中
|
|
||||||
'que_status_comment' => 'システム監視アラート',
|
|
||||||
'work_instructions' => "SHJ-6監視処理 BatchLogID: {$batchLogId}",
|
|
||||||
'created_at' => now(),
|
|
||||||
'updated_at' => now()
|
|
||||||
]);
|
|
||||||
|
|
||||||
Log::info('オペレータキュー登録完了', [
|
|
||||||
'batch_log_id' => $batchLogId,
|
|
||||||
'alert_message' => $alertMessage
|
|
||||||
]);
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Log::error('オペレータキュー登録エラー', [
|
|
||||||
'batch_log_id' => $batchLogId,
|
|
||||||
'error' => $e->getMessage()
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* メール送信対象オペレータを取得
|
|
||||||
*
|
|
||||||
* @return array オペレータ一覧
|
|
||||||
*/
|
|
||||||
private function getMailTargetOperators(): array
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
// user_typeがオペレータのユーザーを取得(仮の条件)
|
|
||||||
$operators = DB::table('users')
|
|
||||||
->select(['user_id', 'email', 'name'])
|
|
||||||
->where('user_type', 'operator') // 実際のテーブル構造に合わせて調整
|
|
||||||
->whereNotNull('email')
|
|
||||||
->where('email', '!=', '')
|
|
||||||
->get()
|
|
||||||
->map(function ($user) {
|
|
||||||
return [
|
|
||||||
'user_id' => $user->user_id,
|
|
||||||
'email' => $user->email,
|
|
||||||
'name' => $user->name ?? 'オペレータ'
|
|
||||||
];
|
|
||||||
})
|
|
||||||
->toArray();
|
|
||||||
|
|
||||||
Log::info('メール送信対象オペレータ取得完了', [
|
|
||||||
'operator_count' => count($operators)
|
|
||||||
]);
|
|
||||||
|
|
||||||
return $operators;
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Log::error('メール送信対象オペレータ取得エラー', [
|
|
||||||
'error' => $e->getMessage()
|
|
||||||
]);
|
|
||||||
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 駐輪場管理者を取得
|
|
||||||
*
|
|
||||||
* @return array 駐輪場管理者一覧
|
|
||||||
*/
|
|
||||||
private function getParkManagers(): array
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
// 駐輪場管理者を取得(仮の条件)
|
|
||||||
$managers = DB::table('users')
|
|
||||||
->select(['user_id', 'email', 'name'])
|
|
||||||
->where('user_type', 'park_manager') // 実際のテーブル構造に合わせて調整
|
|
||||||
->whereNotNull('email')
|
|
||||||
->where('email', '!=', '')
|
|
||||||
->get()
|
|
||||||
->map(function ($user) {
|
|
||||||
return [
|
|
||||||
'user_id' => $user->user_id,
|
|
||||||
'email' => $user->email,
|
|
||||||
'name' => $user->name ?? '駐輪場管理者'
|
|
||||||
];
|
|
||||||
})
|
|
||||||
->toArray();
|
|
||||||
|
|
||||||
Log::info('駐輪場管理者取得完了', [
|
|
||||||
'manager_count' => count($managers)
|
|
||||||
]);
|
|
||||||
|
|
||||||
return $managers;
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Log::error('駐輪場管理者取得エラー', [
|
|
||||||
'error' => $e->getMessage()
|
|
||||||
]);
|
|
||||||
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* アラートメールを送信
|
|
||||||
*
|
|
||||||
* @param string $email メールアドレス
|
|
||||||
* @param string $alertMessage アラートメッセージ
|
|
||||||
* @param string $recipientType 受信者タイプ
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
private function sendAlertMail(string $email, string $alertMessage, string $recipientType): void
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
// SHJメール送信機能を使用(メールテンプレートID=1を使用、実際の値に調整)
|
|
||||||
$result = $this->mailSendService->executeMailSend(
|
|
||||||
$email,
|
|
||||||
'', // 予備メールアドレスは空
|
|
||||||
1 // システムアラート用メールテンプレートID
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($result['success']) {
|
|
||||||
Log::info('アラートメール送信成功', [
|
|
||||||
'email' => $email,
|
|
||||||
'recipient_type' => $recipientType,
|
|
||||||
'alert_message' => $alertMessage
|
|
||||||
]);
|
|
||||||
} else {
|
|
||||||
Log::error('アラートメール送信失敗', [
|
|
||||||
'email' => $email,
|
|
||||||
'recipient_type' => $recipientType,
|
|
||||||
'error' => $result['message']
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Log::error('アラートメール送信エラー', [
|
|
||||||
'email' => $email,
|
|
||||||
'recipient_type' => $recipientType,
|
|
||||||
'error' => $e->getMessage()
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,583 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Services;
|
|
||||||
|
|
||||||
use App\Models\Park;
|
|
||||||
use App\Models\RegularContract;
|
|
||||||
use App\Models\EarningsSummary;
|
|
||||||
use App\Models\Psection;
|
|
||||||
use App\Models\Batch\BatchLog;
|
|
||||||
use App\Models\OperatorQue;
|
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
use Illuminate\Support\Facades\DB;
|
|
||||||
use Carbon\Carbon;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SHJ-10 売上集計処理サービス
|
|
||||||
*
|
|
||||||
* 財政年度ベースの年次・月次売上集計処理を実行するビジネスロジック
|
|
||||||
* バッチ処理「SHJ-10売上集計」の核となる処理を担当
|
|
||||||
*/
|
|
||||||
class ShjTenService
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Park モデル
|
|
||||||
*
|
|
||||||
* @var Park
|
|
||||||
*/
|
|
||||||
protected $parkModel;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* RegularContract モデル
|
|
||||||
*
|
|
||||||
* @var RegularContract
|
|
||||||
*/
|
|
||||||
protected $contractModel;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* EarningsSummary モデル
|
|
||||||
*
|
|
||||||
* @var EarningsSummary
|
|
||||||
*/
|
|
||||||
protected $earningsSummaryModel;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Psection モデル
|
|
||||||
*
|
|
||||||
* @var Psection
|
|
||||||
*/
|
|
||||||
protected $psectionModel;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* BatchLog モデル
|
|
||||||
*
|
|
||||||
* @var BatchLog
|
|
||||||
*/
|
|
||||||
protected $batchLogModel;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* OperatorQue モデル
|
|
||||||
*
|
|
||||||
* @var OperatorQue
|
|
||||||
*/
|
|
||||||
protected $operatorQueModel;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 財政年度開始月
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
const FISCAL_START_MONTH = 4;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* コンストラクタ
|
|
||||||
*
|
|
||||||
* @param Park $parkModel
|
|
||||||
* @param RegularContract $contractModel
|
|
||||||
* @param EarningsSummary $earningsSummaryModel
|
|
||||||
* @param Psection $psectionModel
|
|
||||||
* @param BatchLog $batchLogModel
|
|
||||||
* @param OperatorQue $operatorQueModel
|
|
||||||
*/
|
|
||||||
public function __construct(
|
|
||||||
Park $parkModel,
|
|
||||||
RegularContract $contractModel,
|
|
||||||
EarningsSummary $earningsSummaryModel,
|
|
||||||
Psection $psectionModel,
|
|
||||||
BatchLog $batchLogModel,
|
|
||||||
OperatorQue $operatorQueModel
|
|
||||||
) {
|
|
||||||
$this->parkModel = $parkModel;
|
|
||||||
$this->contractModel = $contractModel;
|
|
||||||
$this->earningsSummaryModel = $earningsSummaryModel;
|
|
||||||
$this->psectionModel = $psectionModel;
|
|
||||||
$this->batchLogModel = $batchLogModel;
|
|
||||||
$this->operatorQueModel = $operatorQueModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SHJ-10 財政年度売上集計処理メイン実行
|
|
||||||
*
|
|
||||||
* 処理フロー:
|
|
||||||
* 【処理1】集計対象を設定する
|
|
||||||
* 【処理2】駐輪場マスタを取得する
|
|
||||||
* 【判断1】取得件数判定
|
|
||||||
* 【処理3】車種区分毎に算出する
|
|
||||||
* 【判断2】取得判定
|
|
||||||
* 【処理4】売上集計結果を削除→登録する
|
|
||||||
* 【処理5】オペレータキュー作成およびバッチ処理ログを作成する
|
|
||||||
*
|
|
||||||
* @param string $type 集計種別(yearly/monthly)
|
|
||||||
* @param string $target 集計対象
|
|
||||||
* @param array $fiscalPeriod 財政期間情報
|
|
||||||
* @return array 処理結果
|
|
||||||
*/
|
|
||||||
public function executeFiscalEarningsAggregation(string $type, string $target, array $fiscalPeriod): array
|
|
||||||
{
|
|
||||||
$batchLogId = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
// 【処理1】集計対象を設定する(財政年度ベース)
|
|
||||||
$aggregationTarget = $this->setFiscalAggregationTarget($fiscalPeriod);
|
|
||||||
|
|
||||||
// バッチ処理開始ログ作成
|
|
||||||
$batchLog = BatchLog::createBatchLog(
|
|
||||||
'shj10',
|
|
||||||
BatchLog::STATUS_START,
|
|
||||||
[
|
|
||||||
'type' => $type,
|
|
||||||
'target' => $target,
|
|
||||||
'fiscal_period' => $fiscalPeriod,
|
|
||||||
'aggregation_target' => $aggregationTarget
|
|
||||||
],
|
|
||||||
"SHJ-10 売上集計処理開始 ({$type}: {$fiscalPeriod['target_label']})"
|
|
||||||
);
|
|
||||||
$batchLogId = $batchLog->id;
|
|
||||||
|
|
||||||
Log::info('SHJ-10 売上集計処理開始', [
|
|
||||||
'batch_log_id' => $batchLogId,
|
|
||||||
'type' => $type,
|
|
||||||
'target' => $target,
|
|
||||||
'fiscal_period' => $fiscalPeriod,
|
|
||||||
'aggregation_target' => $aggregationTarget
|
|
||||||
]);
|
|
||||||
|
|
||||||
// 【処理2】駐輪場マスタを取得する
|
|
||||||
$parkInfo = $this->getParkInformation();
|
|
||||||
|
|
||||||
// 【判断1】取得件数判定
|
|
||||||
if (empty($parkInfo)) {
|
|
||||||
$typeLabel = $this->getTypeLabel($type);
|
|
||||||
$message = "売上集計({$typeLabel}):駐輪場マスタが存在していません。";
|
|
||||||
|
|
||||||
// バッチログ更新
|
|
||||||
$batchLog->update([
|
|
||||||
'status' => BatchLog::STATUS_WARNING,
|
|
||||||
'end_time' => now(),
|
|
||||||
'message' => $message,
|
|
||||||
'success_count' => 1 // 処理は成功したが対象なし
|
|
||||||
]);
|
|
||||||
|
|
||||||
// 【処理5】オペレータキュー作成
|
|
||||||
$this->createOperatorQueue($message, $batchLogId);
|
|
||||||
|
|
||||||
return [
|
|
||||||
'success' => true,
|
|
||||||
'message' => $message,
|
|
||||||
'processed_parks' => 0,
|
|
||||||
'summary_records' => 0,
|
|
||||||
'batch_log_id' => $batchLogId
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
// 【処理3】車種区分毎に算出する & 【処理4】売上集計結果を削除→登録する
|
|
||||||
$summaryRecords = 0;
|
|
||||||
$processedParks = 0;
|
|
||||||
|
|
||||||
foreach ($parkInfo as $park) {
|
|
||||||
$parkSummaryRecords = $this->processFiscalEarningsForPark($park, $aggregationTarget, $fiscalPeriod);
|
|
||||||
|
|
||||||
if ($parkSummaryRecords > 0) {
|
|
||||||
$processedParks++;
|
|
||||||
$summaryRecords += $parkSummaryRecords;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 【判断2】取得判定
|
|
||||||
if ($summaryRecords === 0) {
|
|
||||||
$message = '対象なしの結果を設定する(契約)';
|
|
||||||
|
|
||||||
// バッチログ更新
|
|
||||||
$batchLog->update([
|
|
||||||
'status' => BatchLog::STATUS_WARNING,
|
|
||||||
'end_time' => now(),
|
|
||||||
'message' => $message,
|
|
||||||
'success_count' => 1
|
|
||||||
]);
|
|
||||||
|
|
||||||
// 【処理5】オペレータキュー作成
|
|
||||||
$this->createOperatorQueue($message, $batchLogId);
|
|
||||||
|
|
||||||
return [
|
|
||||||
'success' => true,
|
|
||||||
'message' => $message,
|
|
||||||
'processed_parks' => $processedParks,
|
|
||||||
'summary_records' => 0,
|
|
||||||
'batch_log_id' => $batchLogId
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
// バッチ処理完了ログ更新
|
|
||||||
$completionMessage = "SHJ-10 売上集計処理正常完了 ({$type}: {$fiscalPeriod['target_label']}) - 駐輪場数: {$processedParks}, 集計レコード数: {$summaryRecords}";
|
|
||||||
$batchLog->update([
|
|
||||||
'status' => BatchLog::STATUS_SUCCESS,
|
|
||||||
'end_time' => now(),
|
|
||||||
'message' => $completionMessage,
|
|
||||||
'success_count' => 1
|
|
||||||
]);
|
|
||||||
|
|
||||||
// 【処理5】オペレータキュー作成
|
|
||||||
$this->createOperatorQueue($completionMessage, $batchLogId);
|
|
||||||
|
|
||||||
Log::info('SHJ-10 売上集計処理完了', [
|
|
||||||
'batch_log_id' => $batchLogId,
|
|
||||||
'processed_parks' => $processedParks,
|
|
||||||
'summary_records' => $summaryRecords
|
|
||||||
]);
|
|
||||||
|
|
||||||
return [
|
|
||||||
'success' => true,
|
|
||||||
'message' => 'SHJ-10 売上集計処理が正常に完了しました',
|
|
||||||
'processed_parks' => $processedParks,
|
|
||||||
'summary_records' => $summaryRecords,
|
|
||||||
'batch_log_id' => $batchLogId
|
|
||||||
];
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
$errorMessage = 'SHJ-10 売上集計処理でエラーが発生: ' . $e->getMessage();
|
|
||||||
|
|
||||||
if (isset($batchLog) && $batchLog) {
|
|
||||||
$batchLog->update([
|
|
||||||
'status' => BatchLog::STATUS_ERROR,
|
|
||||||
'end_time' => now(),
|
|
||||||
'message' => $errorMessage,
|
|
||||||
'error_details' => $e->getMessage(),
|
|
||||||
'error_count' => 1
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
Log::error('SHJ-10 売上集計処理エラー', [
|
|
||||||
'batch_log_id' => $batchLogId,
|
|
||||||
'exception' => $e->getMessage(),
|
|
||||||
'trace' => $e->getTraceAsString()
|
|
||||||
]);
|
|
||||||
|
|
||||||
return [
|
|
||||||
'success' => false,
|
|
||||||
'message' => $errorMessage,
|
|
||||||
'details' => $e->getMessage(),
|
|
||||||
'batch_log_id' => $batchLogId
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 【処理1】財政年度集計対象を設定する
|
|
||||||
*
|
|
||||||
* @param array $fiscalPeriod 財政期間情報
|
|
||||||
* @return array 集計対象情報
|
|
||||||
*/
|
|
||||||
private function setFiscalAggregationTarget(array $fiscalPeriod): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'type' => $fiscalPeriod['type'],
|
|
||||||
'start_date' => $fiscalPeriod['start_date'],
|
|
||||||
'end_date' => $fiscalPeriod['end_date'],
|
|
||||||
'summary_type' => $fiscalPeriod['summary_type'],
|
|
||||||
'fiscal_year' => $fiscalPeriod['fiscal_year'],
|
|
||||||
'target_label' => $fiscalPeriod['target_label']
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 【処理2】駐輪場マスタを取得する
|
|
||||||
*
|
|
||||||
* 仕様書のSQLクエリに基づく駐輪場情報取得
|
|
||||||
* SELECT 駐輪場ID, 駐輪場名
|
|
||||||
* FROM 駐輪場マスタ
|
|
||||||
* WHERE 閉設フラグ <> 1
|
|
||||||
* ORDER BY 駐輪場ふりがな
|
|
||||||
*
|
|
||||||
* @return array 駐輪場情報
|
|
||||||
*/
|
|
||||||
private function getParkInformation(): array
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
$parkInfo = DB::table('park')
|
|
||||||
->select(['park_id', 'park_name'])
|
|
||||||
->where('park_close_flag', '<>', 1)
|
|
||||||
->orderBy('park_ruby')
|
|
||||||
->get()
|
|
||||||
->toArray();
|
|
||||||
|
|
||||||
Log::info('駐輪場マスタ取得完了', [
|
|
||||||
'park_count' => count($parkInfo)
|
|
||||||
]);
|
|
||||||
|
|
||||||
return $parkInfo;
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Log::error('駐輪場マスタ取得エラー', [
|
|
||||||
'error' => $e->getMessage()
|
|
||||||
]);
|
|
||||||
|
|
||||||
throw $e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 駐輪場毎の財政年度売上集計処理
|
|
||||||
*
|
|
||||||
* @param object $park 駐輪場情報
|
|
||||||
* @param array $aggregationTarget 集計対象
|
|
||||||
* @param array $fiscalPeriod 財政期間情報
|
|
||||||
* @return int 作成された集計レコード数
|
|
||||||
*/
|
|
||||||
private function processFiscalEarningsForPark($park, array $aggregationTarget, array $fiscalPeriod): int
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
// 【処理4】既存の売上集計結果を削除
|
|
||||||
$this->deleteExistingFiscalSummary($park->park_id, $aggregationTarget);
|
|
||||||
|
|
||||||
// 【処理3】車種区分毎に算出する
|
|
||||||
$psections = $this->getPsectionInformation();
|
|
||||||
$summaryRecords = 0;
|
|
||||||
|
|
||||||
foreach ($psections as $psection) {
|
|
||||||
$earningsData = $this->calculateFiscalEarningsForPsection(
|
|
||||||
$park->park_id,
|
|
||||||
$psection->psection_id,
|
|
||||||
$aggregationTarget
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($this->hasEarningsData($earningsData)) {
|
|
||||||
// 売上集計結果を登録
|
|
||||||
$this->createFiscalEarningsSummary($park, $psection, $aggregationTarget, $earningsData, $fiscalPeriod);
|
|
||||||
$summaryRecords++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Log::info('駐輪場財政年度売上集計完了', [
|
|
||||||
'park_id' => $park->park_id,
|
|
||||||
'park_name' => $park->park_name,
|
|
||||||
'summary_records' => $summaryRecords,
|
|
||||||
'fiscal_period' => $fiscalPeriod['target_label']
|
|
||||||
]);
|
|
||||||
|
|
||||||
return $summaryRecords;
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Log::error('駐輪場財政年度売上集計エラー', [
|
|
||||||
'park_id' => $park->park_id,
|
|
||||||
'error' => $e->getMessage()
|
|
||||||
]);
|
|
||||||
|
|
||||||
throw $e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 車種区分情報取得
|
|
||||||
*
|
|
||||||
* @return array 車種区分情報
|
|
||||||
*/
|
|
||||||
private function getPsectionInformation(): array
|
|
||||||
{
|
|
||||||
return DB::table('psection')
|
|
||||||
->select(['psection_id', 'psection_subject'])
|
|
||||||
->get()
|
|
||||||
->toArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 【処理3】車種区分毎に財政年度売上を算出する
|
|
||||||
*
|
|
||||||
* 4つの項目を計算:
|
|
||||||
* ①売上・件数
|
|
||||||
* ②一時金売上
|
|
||||||
* ③解約返戻金
|
|
||||||
* ④再発行金額・件数
|
|
||||||
*
|
|
||||||
* @param int $parkId 駐輪場ID
|
|
||||||
* @param int $psectionId 車種区分ID
|
|
||||||
* @param array $aggregationTarget 集計対象
|
|
||||||
* @return array 売上データ
|
|
||||||
*/
|
|
||||||
private function calculateFiscalEarningsForPsection(int $parkId, int $psectionId, array $aggregationTarget): array
|
|
||||||
{
|
|
||||||
$startDate = $aggregationTarget['start_date'];
|
|
||||||
$endDate = $aggregationTarget['end_date'];
|
|
||||||
|
|
||||||
// ①売上・件数(billing_amount)
|
|
||||||
$salesData = DB::table('regular_contract')
|
|
||||||
->select([
|
|
||||||
DB::raw('COUNT(*) as sales_count'),
|
|
||||||
DB::raw('COALESCE(SUM(billing_amount), 0) as sales_amount')
|
|
||||||
])
|
|
||||||
->where('park_id', $parkId)
|
|
||||||
->where('psection_id', $psectionId)
|
|
||||||
->where('contract_flag', 1)
|
|
||||||
->whereBetween('contract_payment_day', [$startDate, $endDate])
|
|
||||||
->whereNull('contract_cancelday')
|
|
||||||
->first();
|
|
||||||
|
|
||||||
// ②一時金売上(contract_money)
|
|
||||||
$temporaryData = DB::table('regular_contract')
|
|
||||||
->select([
|
|
||||||
DB::raw('COUNT(*) as temporary_count'),
|
|
||||||
DB::raw('COALESCE(SUM(contract_money), 0) as temporary_amount')
|
|
||||||
])
|
|
||||||
->where('park_id', $parkId)
|
|
||||||
->where('psection_id', $psectionId)
|
|
||||||
->where('contract_flag', 1)
|
|
||||||
->whereBetween('contract_payment_day', [$startDate, $endDate])
|
|
||||||
->whereNotNull('contract_money')
|
|
||||||
->where('contract_money', '>', 0)
|
|
||||||
->first();
|
|
||||||
|
|
||||||
// ③解約返戻金(refunds)
|
|
||||||
$refundData = DB::table('regular_contract')
|
|
||||||
->select([
|
|
||||||
DB::raw('COUNT(*) as refund_count'),
|
|
||||||
DB::raw('COALESCE(SUM(refunds), 0) as refund_amount')
|
|
||||||
])
|
|
||||||
->where('park_id', $parkId)
|
|
||||||
->where('psection_id', $psectionId)
|
|
||||||
->whereBetween('contract_cancelday', [$startDate, $endDate])
|
|
||||||
->whereNotNull('refunds')
|
|
||||||
->where('refunds', '>', 0)
|
|
||||||
->first();
|
|
||||||
|
|
||||||
// ④再発行金額・件数(seal_reissue_request)
|
|
||||||
$reissueData = DB::table('regular_contract')
|
|
||||||
->select([
|
|
||||||
DB::raw('COUNT(*) as reissue_count'),
|
|
||||||
DB::raw('COALESCE(SUM(contract_seal_issue), 0) as reissue_amount')
|
|
||||||
])
|
|
||||||
->where('park_id', $parkId)
|
|
||||||
->where('psection_id', $psectionId)
|
|
||||||
->where('seal_reissue_request', 1)
|
|
||||||
->whereBetween('updated_at', [$startDate, $endDate])
|
|
||||||
->first();
|
|
||||||
|
|
||||||
return [
|
|
||||||
'sales_count' => $salesData->sales_count ?? 0,
|
|
||||||
'sales_amount' => $salesData->sales_amount ?? 0,
|
|
||||||
'temporary_count' => $temporaryData->temporary_count ?? 0,
|
|
||||||
'temporary_amount' => $temporaryData->temporary_amount ?? 0,
|
|
||||||
'refund_count' => $refundData->refund_count ?? 0,
|
|
||||||
'refund_amount' => $refundData->refund_amount ?? 0,
|
|
||||||
'reissue_count' => $reissueData->reissue_count ?? 0,
|
|
||||||
'reissue_amount' => $reissueData->reissue_amount ?? 0
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 売上データの存在チェック
|
|
||||||
*
|
|
||||||
* @param array $earningsData 売上データ
|
|
||||||
* @return bool データが存在するかどうか
|
|
||||||
*/
|
|
||||||
private function hasEarningsData(array $earningsData): bool
|
|
||||||
{
|
|
||||||
return $earningsData['sales_count'] > 0 ||
|
|
||||||
$earningsData['temporary_count'] > 0 ||
|
|
||||||
$earningsData['refund_count'] > 0 ||
|
|
||||||
$earningsData['reissue_count'] > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 【処理4】既存の財政年度売上集計結果を削除
|
|
||||||
*
|
|
||||||
* @param int $parkId 駐輪場ID
|
|
||||||
* @param array $aggregationTarget 集計対象
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
private function deleteExistingFiscalSummary(int $parkId, array $aggregationTarget): void
|
|
||||||
{
|
|
||||||
DB::table('earnings_summary')
|
|
||||||
->where('park_id', $parkId)
|
|
||||||
->where('summary_start_date', $aggregationTarget['start_date'])
|
|
||||||
->where('summary_end_date', $aggregationTarget['end_date'])
|
|
||||||
->where('summary_type', $aggregationTarget['summary_type'])
|
|
||||||
->delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 財政年度売上集計結果を登録
|
|
||||||
*
|
|
||||||
* @param object $park 駐輪場情報
|
|
||||||
* @param object $psection 車種区分情報
|
|
||||||
* @param array $aggregationTarget 集計対象
|
|
||||||
* @param array $earningsData 売上データ
|
|
||||||
* @param array $fiscalPeriod 財政期間情報
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
private function createFiscalEarningsSummary($park, $psection, array $aggregationTarget, array $earningsData, array $fiscalPeriod): void
|
|
||||||
{
|
|
||||||
DB::table('earnings_summary')->insert([
|
|
||||||
'park_id' => $park->park_id,
|
|
||||||
'summary_type' => $aggregationTarget['summary_type'], // 1:年次, 2:月次
|
|
||||||
'summary_start_date' => $aggregationTarget['start_date'],
|
|
||||||
'summary_end_date' => $aggregationTarget['end_date'],
|
|
||||||
'earnings_date' => $aggregationTarget['end_date'], // 集計日として終了日を使用
|
|
||||||
'psection_id' => $psection->psection_id,
|
|
||||||
'usertype_subject1' => $psection->psection_subject,
|
|
||||||
'regular_new_count' => $earningsData['sales_count'],
|
|
||||||
'regular_new_amount' => $earningsData['sales_amount'],
|
|
||||||
'turnsum' => $earningsData['temporary_amount'],
|
|
||||||
'turnsum_count' => $earningsData['temporary_count'],
|
|
||||||
'refunds' => $earningsData['refund_amount'],
|
|
||||||
'reissue_count' => $earningsData['reissue_count'],
|
|
||||||
'reissue_amount' => $earningsData['reissue_amount'],
|
|
||||||
'summary_note' => "SHJ-10 {$fiscalPeriod['target_label']} 財政年度売上集計結果",
|
|
||||||
'created_at' => now(),
|
|
||||||
'updated_at' => now(),
|
|
||||||
'operator_id' => 0 // システム処理
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 【処理5】オペレータキュー作成
|
|
||||||
*
|
|
||||||
* @param string $message メッセージ
|
|
||||||
* @param int $batchLogId バッチログID
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
private function createOperatorQueue(string $message, int $batchLogId): void
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
DB::table('operator_que')->insert([
|
|
||||||
'que_class' => 10, // SHJ-10用のクラス
|
|
||||||
'user_id' => null,
|
|
||||||
'contract_id' => null,
|
|
||||||
'park_id' => null,
|
|
||||||
'que_comment' => $message,
|
|
||||||
'que_status' => 1, // 完了
|
|
||||||
'que_status_comment' => 'バッチ処理完了',
|
|
||||||
'work_instructions' => "SHJ-10売上集計処理 BatchLogID: {$batchLogId}",
|
|
||||||
'created_at' => now(),
|
|
||||||
'updated_at' => now()
|
|
||||||
]);
|
|
||||||
|
|
||||||
Log::info('オペレータキュー作成完了', [
|
|
||||||
'batch_log_id' => $batchLogId,
|
|
||||||
'message' => $message
|
|
||||||
]);
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Log::error('オペレータキュー作成エラー', [
|
|
||||||
'batch_log_id' => $batchLogId,
|
|
||||||
'error' => $e->getMessage()
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 集計種別のラベル取得
|
|
||||||
*
|
|
||||||
* @param string $type 集計種別
|
|
||||||
* @return string ラベル
|
|
||||||
*/
|
|
||||||
private function getTypeLabel(string $type): string
|
|
||||||
{
|
|
||||||
switch ($type) {
|
|
||||||
case 'yearly':
|
|
||||||
return '年次';
|
|
||||||
case 'monthly':
|
|
||||||
return '月次';
|
|
||||||
default:
|
|
||||||
return $type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,335 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Services;
|
|
||||||
|
|
||||||
use Illuminate\Support\Facades\DB;
|
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
use App\Models\Batch\BatchLog;
|
|
||||||
use App\Models\RegularContract;
|
|
||||||
use App\Models\User;
|
|
||||||
use App\Models\Park;
|
|
||||||
use App\Models\Ope;
|
|
||||||
use App\Services\ShjMailSendService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SHJ-12 未払い者通知処理サービス
|
|
||||||
*
|
|
||||||
* 定期契約マスタより未払い者を取得し、通知処理またはオペレーターキュー追加を実行
|
|
||||||
*/
|
|
||||||
class ShjTwelveService
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* SHJメール送信サービス
|
|
||||||
*
|
|
||||||
* @var ShjMailSendService
|
|
||||||
*/
|
|
||||||
protected $shjMailSendService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* コンストラクタ
|
|
||||||
*
|
|
||||||
* @param ShjMailSendService $shjMailSendService
|
|
||||||
*/
|
|
||||||
public function __construct(ShjMailSendService $shjMailSendService)
|
|
||||||
{
|
|
||||||
$this->shjMailSendService = $shjMailSendService;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 【処理1】定期契約マスタより未払い者を取得する
|
|
||||||
*
|
|
||||||
* SQL条件:
|
|
||||||
* - 解約フラグ = 0 (未解約)
|
|
||||||
* - 授受フラグ = 0 (授受フラグOFF)
|
|
||||||
* - 請求金額 > 0 (請求金額あり)
|
|
||||||
*
|
|
||||||
* @return array 未払い者リスト
|
|
||||||
*/
|
|
||||||
public function getUnpaidUsers(): array
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
$query = DB::table('regular_contract as T1')
|
|
||||||
->select([
|
|
||||||
'T1.contract_id', // 定期契約ID
|
|
||||||
'T2.user_seq', // 利用者ID
|
|
||||||
'T2.user_name', // 利用者名
|
|
||||||
'T2.user_manual_flag', // 手動登録フラグ
|
|
||||||
'T2.user_mail', // メールアドレス
|
|
||||||
'T2.user_mail_sub', // 予備メールアドレス
|
|
||||||
'T2.park_id', // 駐輪場ID (userテーブルから)
|
|
||||||
'T3.park_name', // 駐輪場名
|
|
||||||
'T1.billing_amount' // 請求金額
|
|
||||||
])
|
|
||||||
->join('user as T2', function($join) {
|
|
||||||
$join->on('T1.user_seq', '=', 'T2.user_seq');
|
|
||||||
})
|
|
||||||
->join('park as T3', 'T1.park_id', '=', 'T3.park_id')
|
|
||||||
->where([
|
|
||||||
['T1.contract_cancel_flag', '=', 0], // 解約フラグ = 0
|
|
||||||
['T1.contract_flag', '=', 0], // 授受フラグ = 0
|
|
||||||
])
|
|
||||||
->where('T1.billing_amount', '>', 0) // 請求金額 > 0
|
|
||||||
->get();
|
|
||||||
|
|
||||||
Log::info('SHJ-12 未払い者取得完了', [
|
|
||||||
'count' => $query->count(),
|
|
||||||
'sql_conditions' => [
|
|
||||||
'contract_cancel_flag' => 0,
|
|
||||||
'contract_flag' => 0,
|
|
||||||
'billing_amount' => '> 0'
|
|
||||||
]
|
|
||||||
]);
|
|
||||||
|
|
||||||
return $query->toArray();
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Log::error('SHJ-12 未払い者取得エラー', [
|
|
||||||
'error' => $e->getMessage(),
|
|
||||||
'trace' => $e->getTraceAsString()
|
|
||||||
]);
|
|
||||||
throw $e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 【処理2】未払い者への通知、またはオペレーターキュー追加処理
|
|
||||||
*
|
|
||||||
* 各未払い者に対して以下の処理を実行:
|
|
||||||
* 1. メール通知の実行 (SHJ-7連携)
|
|
||||||
* 2. オペレーターキューへの追加 (opeテーブル)
|
|
||||||
*
|
|
||||||
* @param array $unpaidUsers 未払い者リスト
|
|
||||||
* @return array 処理結果
|
|
||||||
*/
|
|
||||||
public function processUnpaidUserNotifications(array $unpaidUsers): array
|
|
||||||
{
|
|
||||||
$notificationCount = 0;
|
|
||||||
$queueCount = 0;
|
|
||||||
$errors = [];
|
|
||||||
$processParameters = [];
|
|
||||||
|
|
||||||
try {
|
|
||||||
DB::beginTransaction();
|
|
||||||
|
|
||||||
foreach ($unpaidUsers as $user) {
|
|
||||||
try {
|
|
||||||
// メール通知処理
|
|
||||||
$mailResult = $this->sendNotificationMail($user);
|
|
||||||
if ($mailResult['success']) {
|
|
||||||
$notificationCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// オペレーターキュー追加処理
|
|
||||||
$queueResult = $this->addToOperatorQueue($user);
|
|
||||||
if ($queueResult['success']) {
|
|
||||||
$queueCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 処理パラメータ記録
|
|
||||||
$processParameters[] = [
|
|
||||||
'contract_id' => $user->contract_id,
|
|
||||||
'user_seq' => $user->user_seq,
|
|
||||||
'billing_amount' => $user->billing_amount,
|
|
||||||
'mail_sent' => $mailResult['success'],
|
|
||||||
'queue_added' => $queueResult['success']
|
|
||||||
];
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
$errors[] = [
|
|
||||||
'contract_id' => $user->contract_id,
|
|
||||||
'error' => $e->getMessage()
|
|
||||||
];
|
|
||||||
|
|
||||||
Log::warning('SHJ-12 個別処理エラー', [
|
|
||||||
'contract_id' => $user->contract_id,
|
|
||||||
'user_seq' => $user->user_seq,
|
|
||||||
'error' => $e->getMessage()
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DB::commit();
|
|
||||||
|
|
||||||
return [
|
|
||||||
'success' => true,
|
|
||||||
'notification_count' => $notificationCount,
|
|
||||||
'queue_count' => $queueCount,
|
|
||||||
'parameters' => $processParameters,
|
|
||||||
'errors' => $errors,
|
|
||||||
'message' => '未払い者通知処理完了'
|
|
||||||
];
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
DB::rollBack();
|
|
||||||
|
|
||||||
Log::error('SHJ-12 通知処理全体エラー', [
|
|
||||||
'error' => $e->getMessage(),
|
|
||||||
'processed_count' => count($processParameters)
|
|
||||||
]);
|
|
||||||
|
|
||||||
return [
|
|
||||||
'success' => false,
|
|
||||||
'notification_count' => $notificationCount,
|
|
||||||
'queue_count' => $queueCount,
|
|
||||||
'parameters' => $processParameters,
|
|
||||||
'errors' => $errors,
|
|
||||||
'message' => '通知処理エラー: ' . $e->getMessage(),
|
|
||||||
'details' => $e->getTraceAsString()
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 未払い者へのメール通知送信
|
|
||||||
*
|
|
||||||
* SHJ-7 メール送信サービスを使用してメール通知を実行
|
|
||||||
*
|
|
||||||
* @param object $user 未払い者情報
|
|
||||||
* @return array 送信結果
|
|
||||||
*/
|
|
||||||
private function sendNotificationMail($user): array
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
// メールアドレスの確認
|
|
||||||
$emailAddress = $user->user_mail ?: $user->user_mail_sub;
|
|
||||||
|
|
||||||
if (empty($emailAddress)) {
|
|
||||||
return [
|
|
||||||
'success' => false,
|
|
||||||
'message' => 'メールアドレスが設定されていません'
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
// SHJ-7 メール送信サービス呼び出し
|
|
||||||
$mailParams = [
|
|
||||||
'to_email' => $emailAddress,
|
|
||||||
'user_name' => $user->user_name,
|
|
||||||
'park_name' => $user->park_name,
|
|
||||||
'billing_amount' => $user->billing_amount,
|
|
||||||
'contract_id' => $user->contract_id
|
|
||||||
];
|
|
||||||
|
|
||||||
$result = $this->shjMailSendService->sendUnpaidNotificationMail($mailParams);
|
|
||||||
|
|
||||||
Log::info('SHJ-12 メール送信結果', [
|
|
||||||
'contract_id' => $user->contract_id,
|
|
||||||
'email' => $emailAddress,
|
|
||||||
'success' => $result['success']
|
|
||||||
]);
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Log::error('SHJ-12 メール送信エラー', [
|
|
||||||
'contract_id' => $user->contract_id,
|
|
||||||
'error' => $e->getMessage()
|
|
||||||
]);
|
|
||||||
|
|
||||||
return [
|
|
||||||
'success' => false,
|
|
||||||
'message' => 'メール送信エラー: ' . $e->getMessage()
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* オペレーターキューへの追加
|
|
||||||
*
|
|
||||||
* opeテーブルにオペレーター処理キューとして登録
|
|
||||||
*
|
|
||||||
* @param object $user 未払い者情報
|
|
||||||
* @return array 追加結果
|
|
||||||
*/
|
|
||||||
private function addToOperatorQueue($user): array
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
$queueData = [
|
|
||||||
'ope_device_id' => null, // デバイスID (未設定)
|
|
||||||
'ope_process_name' => 'SHJ-12', // プロセス名
|
|
||||||
'ope_job_name' => '未払い者通知', // ジョブ名
|
|
||||||
'ope_status' => 'pending', // ステータス
|
|
||||||
'ope_comment' => sprintf(
|
|
||||||
'契約ID:%s ユーザー:%s 金額:%s円',
|
|
||||||
$user->contract_id,
|
|
||||||
$user->user_name,
|
|
||||||
number_format($user->billing_amount)
|
|
||||||
),
|
|
||||||
'ope_target_user_id' => $user->user_seq, // 対象ユーザーID
|
|
||||||
'ope_target_contract_id' => $user->contract_id, // 対象契約ID
|
|
||||||
'ope_billing_amount' => $user->billing_amount, // 請求金額
|
|
||||||
'created_at' => now(),
|
|
||||||
'updated_at' => now()
|
|
||||||
];
|
|
||||||
|
|
||||||
// opeテーブルに挿入
|
|
||||||
DB::table('ope')->insert($queueData);
|
|
||||||
|
|
||||||
Log::info('SHJ-12 オペレーターキュー追加完了', [
|
|
||||||
'contract_id' => $user->contract_id,
|
|
||||||
'user_seq' => $user->user_seq,
|
|
||||||
'billing_amount' => $user->billing_amount
|
|
||||||
]);
|
|
||||||
|
|
||||||
return [
|
|
||||||
'success' => true,
|
|
||||||
'message' => 'オペレーターキューに追加しました'
|
|
||||||
];
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Log::error('SHJ-12 オペレーターキュー追加エラー', [
|
|
||||||
'contract_id' => $user->contract_id,
|
|
||||||
'error' => $e->getMessage()
|
|
||||||
]);
|
|
||||||
|
|
||||||
return [
|
|
||||||
'success' => false,
|
|
||||||
'message' => 'オペレーターキュー追加エラー: ' . $e->getMessage()
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 【処理3】バッチ処理ログを作成する
|
|
||||||
*
|
|
||||||
* 統一BatchLogシステムを使用してSHJ-12の実行ログを記録
|
|
||||||
*
|
|
||||||
* @param string $status ステータス
|
|
||||||
* @param array $parameters パラメータ
|
|
||||||
* @param string $message メッセージ
|
|
||||||
* @param int $executionCount 実行回数
|
|
||||||
* @param int $successCount 成功回数
|
|
||||||
* @param int $errorCount エラー回数
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function createBatchLog(
|
|
||||||
string $status,
|
|
||||||
array $parameters,
|
|
||||||
string $message,
|
|
||||||
int $executionCount = 0,
|
|
||||||
int $successCount = 0,
|
|
||||||
int $errorCount = 0
|
|
||||||
): void {
|
|
||||||
try {
|
|
||||||
BatchLog::createBatchLog(
|
|
||||||
'SHJ-12',
|
|
||||||
$status,
|
|
||||||
$parameters,
|
|
||||||
$message,
|
|
||||||
[
|
|
||||||
'execution_count' => $executionCount,
|
|
||||||
'success_count' => $successCount,
|
|
||||||
'error_count' => $errorCount,
|
|
||||||
'process_type' => '未払い者通知処理',
|
|
||||||
'executed_at' => now()->toISOString()
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Log::error('SHJ-12 バッチログ作成エラー', [
|
|
||||||
'error' => $e->getMessage(),
|
|
||||||
'status' => $status,
|
|
||||||
'message' => $message
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -16,57 +16,59 @@ class UsingStatusService
|
|||||||
/**
|
/**
|
||||||
* 駐輪場別利用率統計を取得
|
* 駐輪場別利用率統計を取得
|
||||||
*
|
*
|
||||||
|
* 取得元:
|
||||||
|
* - 限界収容台数: park_number.park_limit
|
||||||
|
* - 現在収容台数: park_number.park_number
|
||||||
|
* - 空き : park_number.park_limit - park_number.park_number
|
||||||
|
* - 利用率 : (現在収容台数 / 限界収容台数) * 100
|
||||||
|
*
|
||||||
* @param int|null $parkId 駐輪場ID(null の場合は全て)
|
* @param int|null $parkId 駐輪場ID(null の場合は全て)
|
||||||
* @return Collection 統計データのコレクション
|
* @return Collection
|
||||||
*/
|
*/
|
||||||
public function getUtilizationStats(?int $parkId = null): Collection
|
public function getUtilizationStats(?int $parkId = null): \Illuminate\Support\Collection
|
||||||
{
|
{
|
||||||
// 暫定的な収容台数(実DBに無いカラムのため)
|
// park_number に車種IDが ptype_id で入っている前提
|
||||||
$defaultCapacity = [
|
// 異なる場合は 'pn.ptype_id' 部分を実テーブル定義に合わせて変更してください
|
||||||
1 => 100, // 自転車
|
|
||||||
2 => 50, // 原付
|
|
||||||
3 => 30, // その他
|
|
||||||
];
|
|
||||||
|
|
||||||
$query = DB::table('park as p')
|
// ✅ Laravel 12対応:psectionテーブルから車種を取得するように変更
|
||||||
->leftJoin('price_a as pr', 'p.park_id', '=', 'pr.park_id')
|
$query = \DB::table('park as p')
|
||||||
->leftJoin('ptype as pt', 'pr.price_ptypeid', '=', 'pt.ptype_id')
|
->join('park_number as pn', 'p.park_id', '=', 'pn.park_id')
|
||||||
->leftJoin('regular_contract as rc', function ($join) {
|
// 旧: join('ptype as pt', 'pn.ptype_id', '=', 'pt.ptype_id')
|
||||||
$join->on('pr.price_parkplaceid', '=', 'rc.price_parkplaceid')
|
->leftJoin('psection as ps', 'pn.psection_id', '=', 'ps.psection_id') // ✅ 新しい車種取得方法
|
||||||
->where('rc.contract_cancel_flag', '=', 0);
|
->select([
|
||||||
})
|
'p.park_id',
|
||||||
->select([
|
'p.park_name',
|
||||||
'p.park_id',
|
// 'pt.ptype_id',
|
||||||
'p.park_name',
|
// 'pt.ptype_subject',
|
||||||
'pt.ptype_id',
|
'ps.psection_subject', // 車種(psection_subject)に変更
|
||||||
'pt.ptype_subject',
|
// 限界収容台数
|
||||||
DB::raw('COUNT(rc.contract_id) as current_count'),
|
'pn.park_limit',
|
||||||
])
|
// 現在収容台数
|
||||||
->whereNotNull('pr.price_parkplaceid')
|
\DB::raw('COALESCE(pn.park_number, 0) as current_count'),
|
||||||
->whereNotNull('pt.ptype_id')
|
// 空き = 収容上限 - 現在
|
||||||
->where('p.park_close_flag', '!=', 1);
|
\DB::raw('GREATEST(0, COALESCE(pn.park_limit, 0) - COALESCE(pn.park_number, 0)) as available'),
|
||||||
|
// 利用率 = 現在 / 上限 * 100
|
||||||
|
\DB::raw("CASE
|
||||||
|
WHEN COALESCE(pn.park_limit, 0) > 0
|
||||||
|
THEN ROUND((COALESCE(pn.park_number, 0) / pn.park_limit) * 100, 1)
|
||||||
|
ELSE 0
|
||||||
|
END as usage_rate"),
|
||||||
|
])
|
||||||
|
->where('p.park_close_flag', '!=', 1);
|
||||||
|
|
||||||
if ($parkId) {
|
if (!empty($parkId)) {
|
||||||
$query->where('p.park_id', $parkId);
|
$query->where('p.park_id', $parkId);
|
||||||
}
|
|
||||||
|
|
||||||
$results = $query
|
|
||||||
->groupBy(['p.park_id', 'p.park_name', 'pt.ptype_id', 'pt.ptype_subject'])
|
|
||||||
->orderBy('p.park_name')
|
|
||||||
->orderBy('pt.ptype_subject')
|
|
||||||
->get();
|
|
||||||
|
|
||||||
// 後計算で容量・空き・利用率を付与
|
|
||||||
foreach ($results as $result) {
|
|
||||||
$capacity = $defaultCapacity[$result->ptype_id] ?? 50;
|
|
||||||
$result->park_limit = $capacity;
|
|
||||||
$result->available = $capacity - $result->current_count;
|
|
||||||
$result->usage_rate = $capacity > 0 ? round(($result->current_count / $capacity * 100), 1) : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $results;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$results = $query
|
||||||
|
// 表示順:自転車 → 原付 → その他
|
||||||
|
->orderByRaw("CASE ps.psection_subject WHEN '自転車' THEN 1 WHEN '原付' THEN 2 ELSE 3 END")
|
||||||
|
->orderBy('p.park_name')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 駐輪場一覧を取得(選択用)
|
* 駐輪場一覧を取得(選択用)
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -20,7 +20,8 @@ use Illuminate\Support\Facades\Hash;
|
|||||||
class Utils
|
class Utils
|
||||||
{
|
{
|
||||||
// ページあたりのアイテム数(旧システムから継承)
|
// ページあたりのアイテム数(旧システムから継承)
|
||||||
const item_per_page = 50;
|
// const item_per_page = 50;
|
||||||
|
const item_per_page = 20;
|
||||||
|
|
||||||
// 画像保存パス(旧システムから継承)
|
// 画像保存パス(旧システムから継承)
|
||||||
const image_path = 'storage/images/';
|
const image_path = 'storage/images/';
|
||||||
|
|||||||
@ -11,7 +11,12 @@ return Application::configure(basePath: dirname(__DIR__))
|
|||||||
health: '/up',
|
health: '/up',
|
||||||
)
|
)
|
||||||
->withMiddleware(function (Middleware $middleware) {
|
->withMiddleware(function (Middleware $middleware) {
|
||||||
//
|
// SHJ-4A ウェルネット決済情報受信用エンドポイントのCSRF例外設定
|
||||||
|
// 外部システムからのPOSTリクエストのためCSRF保護を無効化
|
||||||
|
$middleware->validateCsrfTokens(except: [
|
||||||
|
'/shj4a', // SHJ-4A本番用エンドポイント
|
||||||
|
'/webhook/wellnet', // SHJ-4A開発・デバッグ用エンドポイント
|
||||||
|
]);
|
||||||
})
|
})
|
||||||
->withExceptions(function (Exceptions $exceptions) {
|
->withExceptions(function (Exceptions $exceptions) {
|
||||||
//
|
//
|
||||||
|
|||||||
@ -35,4 +35,11 @@ return [
|
|||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|
||||||
|
'wellnet' => [
|
||||||
|
'response_format' => env('WELLNET_WEBHOOK_RESPONSE', 'json'),
|
||||||
|
'ip_whitelist' => env('WELLNET_IP_WHITELIST', ''),
|
||||||
|
'verify_signature' => env('WELLNET_VERIFY_SIGNATURE', false),
|
||||||
|
'signature_header' => env('WELLNET_SIGNATURE_HEADER', 'X-Wellnet-Signature'),
|
||||||
|
],
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|||||||
@ -1,24 +0,0 @@
|
|||||||
--
|
|
||||||
-- SHJ-6 デバイス管理マスタテーブル
|
|
||||||
-- デバイス情報を管理するマスタテーブル
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE TABLE `device` (
|
|
||||||
`device_id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'デバイスID(PK)',
|
|
||||||
`created_at` datetime DEFAULT NULL COMMENT '登録日時',
|
|
||||||
`updated_at` datetime DEFAULT NULL COMMENT '更新日時',
|
|
||||||
`park_id` int(10) UNSIGNED DEFAULT NULL COMMENT '駐輪場ID',
|
|
||||||
`device_type` varchar(255) DEFAULT NULL COMMENT 'デバイス種別',
|
|
||||||
`device_subject` varchar(255) DEFAULT NULL COMMENT 'デバイス名',
|
|
||||||
`device_identifier` varchar(255) DEFAULT NULL COMMENT '識別子(IP/FQDN)',
|
|
||||||
`device_work` varchar(255) DEFAULT NULL COMMENT '稼働(状態)',
|
|
||||||
`device_workstart` date DEFAULT NULL COMMENT '稼働開始日',
|
|
||||||
`device_replace` date DEFAULT NULL COMMENT 'リプレース予定日',
|
|
||||||
`device_remarks` varchar(255) DEFAULT NULL COMMENT '備考',
|
|
||||||
`operator_id` int(10) UNSIGNED DEFAULT NULL COMMENT '更新オペレータID',
|
|
||||||
PRIMARY KEY (`device_id`),
|
|
||||||
KEY `idx_park_id` (`park_id`),
|
|
||||||
KEY `idx_device_type` (`device_type`),
|
|
||||||
KEY `idx_workstart` (`device_workstart`),
|
|
||||||
KEY `idx_replace` (`device_replace`)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci COMMENT='デバイス管理マスタテーブル';
|
|
||||||
@ -1,39 +0,0 @@
|
|||||||
--
|
|
||||||
-- SHJ-9 売上集計結果テーブル
|
|
||||||
-- 日次売上集計データを保存するテーブル
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE TABLE `earnings_summary` (
|
|
||||||
`earnings_summary_id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '売上集計ID',
|
|
||||||
`park_id` int(10) UNSIGNED NOT NULL COMMENT '駐輪場ID',
|
|
||||||
`summary_type` varchar(255) DEFAULT NULL COMMENT '集計区分',
|
|
||||||
`summary_start_date` date DEFAULT NULL COMMENT '集計開始日',
|
|
||||||
`summary_end_date` date DEFAULT NULL COMMENT '集計終了日',
|
|
||||||
`earnings_date` date DEFAULT NULL COMMENT '売上日',
|
|
||||||
`psection_id` int(10) UNSIGNED DEFAULT NULL COMMENT '車種区分ID',
|
|
||||||
`usertype_subject1` varchar(255) DEFAULT NULL COMMENT '規格',
|
|
||||||
`enable_months` int(10) UNSIGNED DEFAULT NULL COMMENT '期間(月数)',
|
|
||||||
`regular_new_count` int(10) UNSIGNED DEFAULT 0 COMMENT '期間件数',
|
|
||||||
`regular_new_amount` decimal(10,2) DEFAULT 0.00 COMMENT '期間金額',
|
|
||||||
`regular_new_reduction_count` int(10) UNSIGNED DEFAULT 0 COMMENT '期間成免件数',
|
|
||||||
`regular_new_reduction_amount` decimal(10,2) DEFAULT 0.00 COMMENT '期間成免金額',
|
|
||||||
`regular_update_count` int(10) UNSIGNED DEFAULT 0 COMMENT '更新件数',
|
|
||||||
`regular_update_amount` decimal(10,2) DEFAULT 0.00 COMMENT '更新金額',
|
|
||||||
`regular_update_reduction_count` int(10) UNSIGNED DEFAULT 0 COMMENT '更新成免件数',
|
|
||||||
`regular_update_reduction_amount` decimal(10,2) DEFAULT 0.00 COMMENT '更新成免金額',
|
|
||||||
`turnsum_count` int(10) UNSIGNED DEFAULT 0 COMMENT '残金件数',
|
|
||||||
`turnsum` decimal(10,2) DEFAULT 0.00 COMMENT '残金',
|
|
||||||
`refunds` decimal(10,2) DEFAULT 0.00 COMMENT '解時返戻金',
|
|
||||||
`other_income` decimal(10,2) DEFAULT 0.00 COMMENT '分別収入',
|
|
||||||
`other_spending` decimal(10,2) DEFAULT 0.00 COMMENT '分別支出',
|
|
||||||
`reissue_count` int(10) UNSIGNED DEFAULT 0 COMMENT '発行件数',
|
|
||||||
`reissue_amount` decimal(10,2) DEFAULT 0.00 COMMENT '発行金額',
|
|
||||||
`summary_note` text DEFAULT NULL COMMENT '計備考',
|
|
||||||
`created_at` datetime DEFAULT NULL COMMENT '登録日時',
|
|
||||||
`updated_at` datetime DEFAULT NULL COMMENT '更新日時',
|
|
||||||
`operator_id` int(10) UNSIGNED DEFAULT NULL COMMENT '新法・ページID',
|
|
||||||
PRIMARY KEY (`earnings_summary_id`),
|
|
||||||
KEY `idx_park_earnings_date` (`park_id`, `earnings_date`),
|
|
||||||
KEY `idx_psection` (`psection_id`),
|
|
||||||
KEY `idx_earnings_date` (`earnings_date`)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci COMMENT='売上集計結果テーブル';
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
--
|
|
||||||
-- SHJ-6 ハードウェアチェックログテーブル
|
|
||||||
-- デバイスのハードウェア状態監視ログを保存するテーブル
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE TABLE `hardware_check_log` (
|
|
||||||
`log_id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'ハードウェアチェックログID(PK)',
|
|
||||||
`device_id` int(10) UNSIGNED NOT NULL COMMENT 'デバイスID',
|
|
||||||
`status` int(10) UNSIGNED DEFAULT NULL COMMENT 'ステータス',
|
|
||||||
`status_comment` varchar(255) DEFAULT NULL COMMENT 'ステータスコメント',
|
|
||||||
`created_at` datetime DEFAULT NULL COMMENT '登録日時',
|
|
||||||
`updated_at` datetime DEFAULT NULL COMMENT '更新日時',
|
|
||||||
`operator_id` int(10) UNSIGNED DEFAULT NULL COMMENT 'オペレータID',
|
|
||||||
PRIMARY KEY (`log_id`),
|
|
||||||
KEY `idx_device_id` (`device_id`),
|
|
||||||
KEY `idx_status` (`status`),
|
|
||||||
KEY `idx_created_at` (`created_at`),
|
|
||||||
KEY `idx_device_created` (`device_id`, `created_at`)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci COMMENT='ハードウェアチェックログテーブル';
|
|
||||||
@ -1,24 +0,0 @@
|
|||||||
--
|
|
||||||
-- SHJ-6 プリンタジョブログテーブル
|
|
||||||
-- プリンタ制御プログラムの実行ログを保存するテーブル
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE TABLE `print_job_log` (
|
|
||||||
`log_id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '処理ログID(PK)',
|
|
||||||
`park_id` int(10) UNSIGNED DEFAULT NULL COMMENT '駐輪場ID',
|
|
||||||
`user_id` int(10) UNSIGNED DEFAULT NULL COMMENT 'ユーザーID',
|
|
||||||
`contract_id` int(10) UNSIGNED DEFAULT NULL COMMENT '契約ID',
|
|
||||||
`process_name` varchar(255) DEFAULT NULL COMMENT 'プロセス名',
|
|
||||||
`job_name` varchar(255) DEFAULT NULL COMMENT 'ジョブ名',
|
|
||||||
`status` varchar(255) DEFAULT NULL COMMENT 'ステータス',
|
|
||||||
`error_code` int(10) UNSIGNED DEFAULT NULL COMMENT 'エラーコード',
|
|
||||||
`status_comment` varchar(255) DEFAULT NULL COMMENT 'ステータスコメント',
|
|
||||||
`created_at` datetime DEFAULT NULL COMMENT '登録日時',
|
|
||||||
PRIMARY KEY (`log_id`),
|
|
||||||
KEY `idx_park_id` (`park_id`),
|
|
||||||
KEY `idx_user_id` (`user_id`),
|
|
||||||
KEY `idx_contract_id` (`contract_id`),
|
|
||||||
KEY `idx_error_code` (`error_code`),
|
|
||||||
KEY `idx_created_at` (`created_at`),
|
|
||||||
KEY `idx_created_error` (`created_at`, `error_code`)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci COMMENT='プリンタジョブログテーブル';
|
|
||||||
@ -1,62 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
use Illuminate\Database\Migrations\Migration;
|
|
||||||
use Illuminate\Database\Schema\Blueprint;
|
|
||||||
use Illuminate\Support\Facades\Schema;
|
|
||||||
|
|
||||||
return new class extends Migration
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Run the migrations.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function up()
|
|
||||||
{
|
|
||||||
Schema::create('earnings_summary', function (Blueprint $table) {
|
|
||||||
$table->id('earnings_summary_id')->comment('売上集計ID');
|
|
||||||
$table->unsignedInteger('park_id')->comment('駐輪場ID');
|
|
||||||
$table->string('summary_type', 255)->nullable()->comment('集計区分');
|
|
||||||
$table->date('summary_start_date')->nullable()->comment('集計開始日');
|
|
||||||
$table->date('summary_end_date')->nullable()->comment('集計終了日');
|
|
||||||
$table->date('earnings_date')->nullable()->comment('売上日');
|
|
||||||
$table->unsignedInteger('psection_id')->nullable()->comment('車種区分ID');
|
|
||||||
$table->string('usertype_subject1', 255)->nullable()->comment('規格');
|
|
||||||
$table->unsignedInteger('enable_months')->nullable()->comment('期間(月数)');
|
|
||||||
$table->unsignedInteger('regular_new_count')->nullable()->comment('期間件数');
|
|
||||||
$table->decimal('regular_new_amount', 10, 2)->nullable()->comment('期間金額');
|
|
||||||
$table->unsignedInteger('regular_new_reduction_count')->nullable()->comment('期間成免件数');
|
|
||||||
$table->decimal('regular_new_reduction_amount', 10, 2)->nullable()->comment('期間成免金額');
|
|
||||||
$table->unsignedInteger('regular_update_count')->nullable()->comment('更新件数');
|
|
||||||
$table->decimal('regular_update_amount', 10, 2)->nullable()->comment('更新金額');
|
|
||||||
$table->unsignedInteger('regular_update_reduction_count')->nullable()->comment('更新成免件数');
|
|
||||||
$table->decimal('regular_update_reduction_amount', 10, 2)->nullable()->comment('更新成免金額');
|
|
||||||
$table->unsignedInteger('turnsum_count')->nullable()->comment('残金件数');
|
|
||||||
$table->decimal('turnsum', 10, 2)->nullable()->comment('残金');
|
|
||||||
$table->decimal('refunds', 10, 2)->nullable()->comment('解時返戻金');
|
|
||||||
$table->decimal('other_income', 10, 2)->nullable()->comment('分別収入');
|
|
||||||
$table->decimal('other_spending', 10, 2)->nullable()->comment('分別支出');
|
|
||||||
$table->unsignedInteger('reissue_count')->nullable()->comment('発行件数');
|
|
||||||
$table->decimal('reissue_amount', 10, 2)->nullable()->comment('発行金額');
|
|
||||||
$table->text('summary_note')->nullable()->comment('計備考');
|
|
||||||
$table->datetime('created_at')->nullable()->comment('登録日時');
|
|
||||||
$table->datetime('updated_at')->nullable()->comment('更新日時');
|
|
||||||
$table->unsignedInteger('operator_id')->nullable()->comment('新法・ページID');
|
|
||||||
|
|
||||||
// インデックス
|
|
||||||
$table->index(['park_id', 'earnings_date'], 'idx_park_earnings_date');
|
|
||||||
$table->index(['psection_id'], 'idx_psection');
|
|
||||||
$table->index(['earnings_date'], 'idx_earnings_date');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reverse the migrations.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function down()
|
|
||||||
{
|
|
||||||
Schema::dropIfExists('earnings_summary');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,61 +0,0 @@
|
|||||||
/* iCheck plugin skins
|
|
||||||
----------------------------------- */
|
|
||||||
@import url("minimal/_all.css");
|
|
||||||
/*
|
|
||||||
@import url("minimal/minimal.css");
|
|
||||||
@import url("minimal/red.css");
|
|
||||||
@import url("minimal/green.css");
|
|
||||||
@import url("minimal/blue.css");
|
|
||||||
@import url("minimal/aero.css");
|
|
||||||
@import url("minimal/grey.css");
|
|
||||||
@import url("minimal/orange.css");
|
|
||||||
@import url("minimal/yellow.css");
|
|
||||||
@import url("minimal/pink.css");
|
|
||||||
@import url("minimal/purple.css");
|
|
||||||
*/
|
|
||||||
|
|
||||||
@import url("square/_all.css");
|
|
||||||
/*
|
|
||||||
@import url("square/square.css");
|
|
||||||
@import url("square/red.css");
|
|
||||||
@import url("square/green.css");
|
|
||||||
@import url("square/blue.css");
|
|
||||||
@import url("square/aero.css");
|
|
||||||
@import url("square/grey.css");
|
|
||||||
@import url("square/orange.css");
|
|
||||||
@import url("square/yellow.css");
|
|
||||||
@import url("square/pink.css");
|
|
||||||
@import url("square/purple.css");
|
|
||||||
*/
|
|
||||||
|
|
||||||
@import url("flat/_all.css");
|
|
||||||
/*
|
|
||||||
@import url("flat/flat.css");
|
|
||||||
@import url("flat/red.css");
|
|
||||||
@import url("flat/green.css");
|
|
||||||
@import url("flat/blue.css");
|
|
||||||
@import url("flat/aero.css");
|
|
||||||
@import url("flat/grey.css");
|
|
||||||
@import url("flat/orange.css");
|
|
||||||
@import url("flat/yellow.css");
|
|
||||||
@import url("flat/pink.css");
|
|
||||||
@import url("flat/purple.css");
|
|
||||||
*/
|
|
||||||
|
|
||||||
@import url("line/_all.css");
|
|
||||||
/*
|
|
||||||
@import url("line/line.css");
|
|
||||||
@import url("line/red.css");
|
|
||||||
@import url("line/green.css");
|
|
||||||
@import url("line/blue.css");
|
|
||||||
@import url("line/aero.css");
|
|
||||||
@import url("line/grey.css");
|
|
||||||
@import url("line/orange.css");
|
|
||||||
@import url("line/yellow.css");
|
|
||||||
@import url("line/pink.css");
|
|
||||||
@import url("line/purple.css");
|
|
||||||
*/
|
|
||||||
|
|
||||||
@import url("polaris/polaris.css");
|
|
||||||
|
|
||||||
@import url("futurico/futurico.css");
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,9 +0,0 @@
|
|||||||
/*!
|
|
||||||
* Bootstrap Colorpicker
|
|
||||||
* http://mjolnic.github.io/bootstrap-colorpicker/
|
|
||||||
*
|
|
||||||
* Originally written by (c) 2012 Stefan Petre
|
|
||||||
* Licensed under the Apache License v2.0
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0.txt
|
|
||||||
*
|
|
||||||
*/.colorpicker-saturation{float:left;width:100px;height:100px;cursor:crosshair;background-image:url("img/saturation.png")}.colorpicker-saturation i{position:absolute;top:0;left:0;display:block;width:5px;height:5px;margin:-4px 0 0 -4px;border:1px solid #000;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.colorpicker-saturation i b{display:block;width:5px;height:5px;border:1px solid #fff;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.colorpicker-hue,.colorpicker-alpha{float:left;width:15px;height:100px;margin-bottom:4px;margin-left:4px;cursor:row-resize}.colorpicker-hue i,.colorpicker-alpha i{position:absolute;top:0;left:0;display:block;width:100%;height:1px;margin-top:-1px;background:#000;border-top:1px solid #fff}.colorpicker-hue{background-image:url("img/hue.png")}.colorpicker-alpha{display:none;background-image:url("img/alpha.png")}.colorpicker{top:0;left:0;z-index:2500;min-width:130px;padding:4px;margin-top:1px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;*zoom:1}.colorpicker:before,.colorpicker:after{display:table;line-height:0;content:""}.colorpicker:after{clear:both}.colorpicker:before{position:absolute;top:-7px;left:6px;display:inline-block;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-left:7px solid transparent;border-bottom-color:rgba(0,0,0,0.2);content:''}.colorpicker:after{position:absolute;top:-6px;left:7px;display:inline-block;border-right:6px solid transparent;border-bottom:6px solid #fff;border-left:6px solid transparent;content:''}.colorpicker div{position:relative}.colorpicker.colorpicker-with-alpha{min-width:140px}.colorpicker.colorpicker-with-alpha .colorpicker-alpha{display:block}.colorpicker-color{height:10px;margin-top:5px;clear:both;background-image:url("img/alpha.png");background-position:0 100%}.colorpicker-color div{height:10px}.colorpicker-element .input-group-addon i{display:block;width:16px;height:16px;cursor:pointer}.colorpicker.colorpicker-inline{position:relative;display:inline-block;float:none}.colorpicker.colorpicker-horizontal{width:110px;height:auto;min-width:110px}.colorpicker.colorpicker-horizontal .colorpicker-saturation{margin-bottom:4px}.colorpicker.colorpicker-horizontal .colorpicker-color{width:100px}.colorpicker.colorpicker-horizontal .colorpicker-hue,.colorpicker.colorpicker-horizontal .colorpicker-alpha{float:left;width:100px;height:15px;margin-bottom:4px;margin-left:0;cursor:col-resize}.colorpicker.colorpicker-horizontal .colorpicker-hue i,.colorpicker.colorpicker-horizontal .colorpicker-alpha i{position:absolute;top:0;left:0;display:block;width:1px;height:15px;margin-top:0;background:#fff;border:0}.colorpicker.colorpicker-horizontal .colorpicker-hue{background-image:url("img/hue-horizontal.png")}.colorpicker.colorpicker-horizontal .colorpicker-alpha{background-image:url("img/alpha-horizontal.png")}.colorpicker.colorpicker-hidden{display:none}.colorpicker.colorpicker-visible{display:block}.colorpicker-inline.colorpicker-visible{display:inline-block}
|
|
||||||
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user