815 lines
32 KiB
PHP
815 lines
32 KiB
PHP
<?php
|
||
|
||
namespace App\Services;
|
||
|
||
use App\Models\Park;
|
||
use App\Models\EarningsSummary;
|
||
use App\Models\Psection;
|
||
use App\Models\OperatorQue;
|
||
use App\Models\Device;
|
||
use Illuminate\Support\Facades\Log;
|
||
use Illuminate\Support\Facades\DB;
|
||
use Carbon\Carbon;
|
||
|
||
/**
|
||
* SHJ-9 売上集計処理サービス
|
||
*
|
||
* 日次の売上集計処理を実行するビジネスロジック
|
||
* バッチ処理「SHJ-9売上集計(日次)」の核となる処理を担当
|
||
*/
|
||
class ShjNineService
|
||
{
|
||
/**
|
||
* バッチ実行者の固定オペレータID
|
||
*/
|
||
const BATCH_OPERATOR_ID = 9999999;
|
||
|
||
/**
|
||
* 日次集計の summary_type 値
|
||
*/
|
||
const SUMMARY_TYPE_DAILY = 3;
|
||
|
||
/**
|
||
* ShjEightService インスタンス
|
||
*
|
||
* @var ShjEightService
|
||
*/
|
||
protected $shjEightService;
|
||
|
||
/**
|
||
* コンストラクタ
|
||
*
|
||
* @param ShjEightService $shjEightService
|
||
*/
|
||
public function __construct(ShjEightService $shjEightService)
|
||
{
|
||
$this->shjEightService = $shjEightService;
|
||
}
|
||
|
||
/**
|
||
* SHJ-9 売上集計処理メイン実行(日次のみ)
|
||
*
|
||
* 処理フロー (todo/SHJ-9/SHJ-9.txt):
|
||
* 【処理1】集計対象を設定する
|
||
* 【処理2】駐輪場マスタを取得する
|
||
* 【判断1】取得件数判定
|
||
* 【処理3】車種区分毎に算出する
|
||
* 【判断2】取得判定
|
||
* 【処理4】売上集計結果を削除→登録する
|
||
* 【処理5】オペレータキュー作成およびバッチ処理ログを作成する
|
||
*
|
||
* @param string $type 集計種別(daily 固定)
|
||
* @param string $aggregationDate 集計対象日(YYYY-MM-DD)
|
||
* @return array 処理結果
|
||
*/
|
||
public function executeEarningsAggregation(string $type, string $aggregationDate): array
|
||
{
|
||
$statusComments = []; // 内部変数.ステータスコメント
|
||
$dataIntegrityIssues = []; // 内部変数.情報不備
|
||
|
||
try {
|
||
// 【処理1】集計対象を設定する
|
||
// パラメーター検証(日付形式チェック)
|
||
if (!$this->isValidDateFormat($aggregationDate)) {
|
||
// 日付形式エラー時は【処理5】へ(warning扱い)
|
||
$statusComment = "売上集計(日次):パラメーターが不正です。(日付形式ではありません)";
|
||
|
||
// 【処理5】オペレータキュー作成
|
||
$this->createOperatorQueue($statusComment, null);
|
||
|
||
// SHJ-8 バッチ処理ログ作成
|
||
$this->callShjEight('SHJ-9売上集計(日次)', 'success', $statusComment);
|
||
|
||
return [
|
||
'success' => true, // 仕様上はwarningで成功扱い
|
||
'message' => $statusComment
|
||
];
|
||
}
|
||
|
||
$targetDate = Carbon::parse($aggregationDate)->format('Y-m-d');
|
||
|
||
Log::info('SHJ-9 売上集計処理開始', [
|
||
'type' => $type,
|
||
'target_date' => $targetDate
|
||
]);
|
||
|
||
// 【処理2】駐輪場マスタを取得する
|
||
$parkInfo = $this->getParkInformation();
|
||
|
||
// 【判断1】取得件数判定
|
||
if (empty($parkInfo)) {
|
||
$statusComment = '売上集計(日次):駐輪場マスタが存在していません。';
|
||
$statusComments[] = $statusComment;
|
||
|
||
// 【処理5】オペレータキュー作成
|
||
$this->createOperatorQueue($statusComment, null);
|
||
|
||
// SHJ-8 バッチ処理ログ作成
|
||
$this->callShjEight('SHJ-9売上集計(日次)', 'success', $statusComment);
|
||
|
||
return [
|
||
'success' => true,
|
||
'message' => $statusComment,
|
||
'processed_parks' => 0,
|
||
'summary_records' => 0
|
||
];
|
||
}
|
||
|
||
// 【処理3】車種区分毎に算出する & 【処理4】売上集計結果を削除→登録する
|
||
$summaryRecords = 0;
|
||
$processedParks = 0;
|
||
|
||
foreach ($parkInfo as $park) {
|
||
$result = $this->processEarningsForPark($park, $targetDate);
|
||
|
||
$processedParks++;
|
||
$summaryRecords += $result['summary_records'];
|
||
|
||
// 対象データなしの場合のステータスコメント収集
|
||
if (!empty($result['no_data_message'])) {
|
||
$statusComments[] = $result['no_data_message'];
|
||
}
|
||
|
||
// 情報不備を収集("なし"でない場合)
|
||
if ($result['data_integrity_issue'] !== '情報不備:なし') {
|
||
$dataIntegrityIssues[] = $result['data_integrity_issue'];
|
||
}
|
||
}
|
||
|
||
// 最終ステータスコメント生成
|
||
$finalStatusComment = "売上集計(日次):対象日={$targetDate}、駐輪場数={$processedParks}、集計レコード数={$summaryRecords}";
|
||
if (!empty($dataIntegrityIssues)) {
|
||
$finalStatusComment .= "、情報不備=" . implode('、', $dataIntegrityIssues);
|
||
}
|
||
|
||
// 【処理5】オペレータキュー作成
|
||
// ※ 駐輪場単位で既に作成済み(processEarningsForPark内で情報不備検出時に実施)
|
||
if (!empty($dataIntegrityIssues)) {
|
||
Log::warning('SHJ-9 情報不備検出', [
|
||
'issues' => $dataIntegrityIssues
|
||
]);
|
||
}
|
||
|
||
// SHJ-8 バッチ処理ログ作成
|
||
$this->callShjEight('SHJ-9売上集計(日次)', 'success', $finalStatusComment);
|
||
|
||
Log::info('SHJ-9 売上集計処理完了', [
|
||
'processed_parks' => $processedParks,
|
||
'summary_records' => $summaryRecords,
|
||
'data_integrity_issues' => count($dataIntegrityIssues),
|
||
'no_data_parks' => count($statusComments)
|
||
]);
|
||
|
||
return [
|
||
'success' => true,
|
||
'message' => 'SHJ-9 売上集計処理が正常に完了しました',
|
||
'processed_parks' => $processedParks,
|
||
'summary_records' => $summaryRecords,
|
||
'data_integrity_issues' => count($dataIntegrityIssues)
|
||
];
|
||
|
||
} catch (\Exception $e) {
|
||
$errorMessage = '売上集計(日次):エラー発生 - ' . $e->getMessage();
|
||
|
||
// SHJ-8 バッチ処理ログ作成(エラー時も作成)
|
||
try {
|
||
$this->callShjEight('SHJ-9売上集計(日次)', 'error', $errorMessage);
|
||
} catch (\Exception $shjException) {
|
||
Log::error('SHJ-8呼び出しエラー', ['error' => $shjException->getMessage()]);
|
||
}
|
||
|
||
Log::error('SHJ-9 売上集計処理エラー', [
|
||
'exception' => $e->getMessage(),
|
||
'trace' => $e->getTraceAsString()
|
||
]);
|
||
|
||
return [
|
||
'success' => false,
|
||
'message' => $errorMessage,
|
||
'details' => $e->getMessage()
|
||
];
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 日付形式の検証
|
||
*
|
||
* @param string $date 日付文字列
|
||
* @return bool 有効な日付形式かどうか
|
||
*/
|
||
private function isValidDateFormat(string $date): bool
|
||
{
|
||
try {
|
||
$parsed = Carbon::parse($date);
|
||
return true;
|
||
} catch (\Exception $e) {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 【処理2】駐輪場マスタを取得する
|
||
*
|
||
* @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 string $targetDate 集計対象日(YYYY-MM-DD)
|
||
* @return array 処理結果 ['summary_records' => int, 'data_integrity_issue' => string, 'no_data_message' => string|null]
|
||
*/
|
||
private function processEarningsForPark($park, string $targetDate): array
|
||
{
|
||
try {
|
||
// 0. 情報不備チェック
|
||
$dataIntegrityIssue = $this->checkDataIntegrity($park->park_id, $targetDate);
|
||
|
||
// 情報不備がある場合、駐輪場単位でオペレータキュー作成(仕様 todo/SHJ-9/SHJ-9.txt:253-263)
|
||
if ($dataIntegrityIssue !== '情報不備:なし') {
|
||
$this->createOperatorQueue($dataIntegrityIssue, $park->park_id);
|
||
}
|
||
|
||
// ① 定期契約データ取得(車種区分・分類名1・定期有効月数毎)
|
||
$regularData = $this->calculateRegularEarnings($park->park_id, $targetDate);
|
||
|
||
// ② 一時金データ取得(車種毎)
|
||
$lumpsumData = $this->calculateLumpsumEarnings($park->park_id, $targetDate);
|
||
|
||
// ③ 解約返戻金データ取得(車種区分毎)
|
||
$refundData = $this->calculateRefundEarnings($park->park_id, $targetDate);
|
||
|
||
// ④ 再発行データ取得(車種区分毎)
|
||
$reissueData = $this->calculateReissueCount($park->park_id, $targetDate);
|
||
|
||
// 【判断2】データがいずれかあれば【処理4】へ
|
||
if (empty($regularData) && empty($lumpsumData) && empty($refundData) && empty($reissueData)) {
|
||
// 対象データなし - 仕様 todo/SHJ-9/SHJ-9.txt:172-175
|
||
$noDataMessage = "売上集計(日次):対象日:{$targetDate}/駐輪場:{$park->park_name}:売上データが存在しません。";
|
||
|
||
return [
|
||
'summary_records' => 0,
|
||
'data_integrity_issue' => $dataIntegrityIssue,
|
||
'no_data_message' => $noDataMessage
|
||
];
|
||
}
|
||
|
||
// 【処理4】既存の売上集計結果を削除
|
||
$this->deleteExistingSummary($park->park_id, $targetDate);
|
||
|
||
// 【処理4】売上集計結果を登録
|
||
$summaryRecords = 0;
|
||
|
||
// ① 定期契約データがある場合:同じ組合せ(psection×usertype×months)を統合
|
||
$mergedRegularData = $this->mergeRegularDataByGroup($regularData);
|
||
foreach ($mergedRegularData as $key => $mergedRow) {
|
||
$this->createEarningsSummary($park, $mergedRow, $targetDate, 'regular');
|
||
$summaryRecords++;
|
||
}
|
||
|
||
// ②③④ 一時金・解約・再発行データがある場合(車種区分毎に集約)
|
||
$otherDataByPsection = $this->mergeOtherEarningsData($lumpsumData, $refundData, $reissueData);
|
||
foreach ($otherDataByPsection as $psectionId => $data) {
|
||
$this->createEarningsSummary($park, $data, $targetDate, 'other');
|
||
$summaryRecords++;
|
||
}
|
||
|
||
Log::info('駐輪場売上集計完了', [
|
||
'park_id' => $park->park_id,
|
||
'park_name' => $park->park_name,
|
||
'summary_records' => $summaryRecords,
|
||
'data_integrity_issue' => $dataIntegrityIssue
|
||
]);
|
||
|
||
return [
|
||
'summary_records' => $summaryRecords,
|
||
'data_integrity_issue' => $dataIntegrityIssue,
|
||
'no_data_message' => null
|
||
];
|
||
|
||
} catch (\Exception $e) {
|
||
Log::error('駐輪場売上集計エラー', [
|
||
'park_id' => $park->park_id,
|
||
'error' => $e->getMessage()
|
||
]);
|
||
|
||
throw $e;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 0. 情報不備チェック
|
||
*
|
||
* 仕様 todo/SHJ-9/SHJ-9.txt:44-68
|
||
*
|
||
* @param int $parkId 駐輪場ID
|
||
* @param string $targetDate 集計対象日(YYYY-MM-DD)
|
||
* @return string 情報不備メッセージ(仕様フォーマット:"情報不備:xxx" or "情報不備:なし")
|
||
*/
|
||
private function checkDataIntegrity(int $parkId, string $targetDate): string
|
||
{
|
||
$incompleteContracts = DB::table('regular_contract')
|
||
->select('contract_id')
|
||
->where('park_id', $parkId)
|
||
->where('contract_flag', 1)
|
||
->whereDate('contract_payment_day', '=', $targetDate)
|
||
->where(function($query) {
|
||
$query->whereNull('update_flag')
|
||
->orWhereNull('psection_id')
|
||
->orWhereNull('enable_months');
|
||
})
|
||
->pluck('contract_id')
|
||
->toArray();
|
||
|
||
if (empty($incompleteContracts)) {
|
||
return '情報不備:なし';
|
||
}
|
||
|
||
// 仕様フォーマット:"情報不備:" + 契約IDカンマ区切り
|
||
return '情報不備:' . implode(',', $incompleteContracts);
|
||
}
|
||
|
||
/**
|
||
* ① 定期契約データ取得(車種区分・分類名1・定期有効月数毎)
|
||
*
|
||
* 仕様 todo/SHJ-9/SHJ-9.txt:70-95
|
||
* SQL定義:減免措置・継続フラグ・車種区分・分類名・有効月数でグループ化し、
|
||
* 授受金額の合計と件数を算出する
|
||
*
|
||
* @param int $parkId 駐輪場ID
|
||
* @param string $targetDate 集計対象日(YYYY-MM-DD)
|
||
* @return array
|
||
*/
|
||
private function calculateRegularEarnings(int $parkId, string $targetDate): array
|
||
{
|
||
$results = DB::table('regular_contract as T1')
|
||
->join('usertype as T3', 'T1.user_categoryid', '=', 'T3.user_categoryid')
|
||
->select([
|
||
DB::raw('IFNULL(T1.contract_reduction, 0) as contract_reduction'),
|
||
'T1.update_flag',
|
||
'T1.psection_id',
|
||
'T3.usertype_subject1',
|
||
'T1.enable_months',
|
||
DB::raw('SUM(T1.contract_money) as total_amount'), // 仕様line 77: 授受金額の合計
|
||
DB::raw('COUNT(T1.contract_money) as contract_count') // 仕様line 78: 授受件数
|
||
])
|
||
->where('T1.park_id', $parkId)
|
||
->where('T1.contract_flag', 1)
|
||
->whereDate('T1.contract_payment_day', '=', $targetDate)
|
||
->whereNotNull('T1.update_flag')
|
||
->whereNotNull('T1.psection_id')
|
||
->whereNotNull('T1.enable_months')
|
||
->groupBy([
|
||
DB::raw('IFNULL(T1.contract_reduction, 0)'),
|
||
'T1.update_flag',
|
||
'T1.psection_id',
|
||
'T3.usertype_subject1',
|
||
'T1.enable_months'
|
||
])
|
||
->get();
|
||
|
||
return $results->toArray();
|
||
}
|
||
|
||
/**
|
||
* ② 一時金データ取得(車種毎)
|
||
*
|
||
* 仕様 todo/SHJ-9/SHJ-9.txt:114-125
|
||
*
|
||
* @param int $parkId 駐輪場ID
|
||
* @param string $targetDate 集計対象日(YYYY-MM-DD)
|
||
* @return array
|
||
*/
|
||
private function calculateLumpsumEarnings(int $parkId, string $targetDate): array
|
||
{
|
||
$results = DB::table('lumpsum_transaction')
|
||
->select([
|
||
'type_class as psection_id',
|
||
DB::raw('COUNT(*) as lumpsum_count'),
|
||
DB::raw('COALESCE(SUM(deposit_amount), 0) as lumpsum')
|
||
])
|
||
->where('park_id', $parkId)
|
||
->whereDate('pay_date', '=', $targetDate)
|
||
->groupBy('type_class')
|
||
->get();
|
||
|
||
return $results->toArray();
|
||
}
|
||
|
||
/**
|
||
* ③ 解約返戻金データ取得(車種区分毎)
|
||
*
|
||
* 仕様 todo/SHJ-9/SHJ-9.txt:126-137
|
||
*
|
||
* @param int $parkId 駐輪場ID
|
||
* @param string $targetDate 集計対象日(YYYY-MM-DD)
|
||
* @return array
|
||
*/
|
||
private function calculateRefundEarnings(int $parkId, string $targetDate): array
|
||
{
|
||
$results = DB::table('regular_contract')
|
||
->select([
|
||
'psection_id',
|
||
DB::raw('COALESCE(SUM(refunds), 0) as refunds')
|
||
])
|
||
->where('park_id', $parkId)
|
||
->where('contract_cancel_flag', 1)
|
||
->whereDate('repayment_at', '=', $targetDate)
|
||
->groupBy('psection_id')
|
||
->get();
|
||
|
||
return $results->toArray();
|
||
}
|
||
|
||
/**
|
||
* ④ 再発行データ取得(車種区分毎)
|
||
*
|
||
* 仕様 todo/SHJ-9/SHJ-9.txt:138-149
|
||
*
|
||
* @param int $parkId 駐輪場ID
|
||
* @param string $targetDate 集計対象日(YYYY-MM-DD)
|
||
* @return array
|
||
*/
|
||
private function calculateReissueCount(int $parkId, string $targetDate): array
|
||
{
|
||
$results = DB::table('seal')
|
||
->select([
|
||
'psection_id',
|
||
DB::raw('COUNT(contract_id) as reissue_count')
|
||
])
|
||
->where('park_id', $parkId)
|
||
->where('contract_seal_issue', '>=', 2)
|
||
->whereDate('seal_day', '=', $targetDate)
|
||
->groupBy('psection_id')
|
||
->get();
|
||
|
||
return $results->toArray();
|
||
}
|
||
|
||
/**
|
||
* 定期契約データを組合せ毎に統合
|
||
*
|
||
* SQLは contract_reduction × update_flag で分組しているため、
|
||
* 同じ psection × usertype × months の組合せで複数行が返る場合がある。
|
||
* ここで park_id + psection_id + usertype_subject1 + enable_months をキーに統合し、
|
||
* 新規/更新 × 減免/通常 の各件数・金額を1つのオブジェクトに集約する。
|
||
*
|
||
* 仕様 todo/SHJ-9/SHJ-9.txt:96-113
|
||
*
|
||
* @param array $regularData calculateRegularEarnings()の結果
|
||
* @return array キー:psection_id|usertype|months、値:統合されたデータオブジェクト
|
||
*/
|
||
private function mergeRegularDataByGroup(array $regularData): array
|
||
{
|
||
$merged = [];
|
||
|
||
foreach ($regularData as $row) {
|
||
// 統合キー:psection_id|usertype_subject1|enable_months
|
||
$key = $row->psection_id . '|' . $row->usertype_subject1 . '|' . $row->enable_months;
|
||
|
||
// 初回作成
|
||
if (!isset($merged[$key])) {
|
||
$merged[$key] = (object)[
|
||
'psection_id' => $row->psection_id,
|
||
'usertype_subject1' => $row->usertype_subject1,
|
||
'enable_months' => $row->enable_months,
|
||
// 新規・通常
|
||
'regular_new_count' => 0,
|
||
'regular_new_amount' => 0,
|
||
// 新規・減免
|
||
'regular_new_reduction_count' => 0,
|
||
'regular_new_reduction_amount' => 0,
|
||
// 更新・通常
|
||
'regular_update_count' => 0,
|
||
'regular_update_amount' => 0,
|
||
// 更新・減免
|
||
'regular_update_reduction_count' => 0,
|
||
'regular_update_reduction_amount' => 0
|
||
];
|
||
}
|
||
|
||
// 区分判定
|
||
$isNew = in_array($row->update_flag, [2, null]); // 新規
|
||
$isReduction = ($row->contract_reduction == 1); // 減免
|
||
|
||
$count = $row->contract_count ?? 0;
|
||
$amount = $row->total_amount ?? 0;
|
||
|
||
// 対応するフィールドに累加
|
||
if ($isNew && !$isReduction) {
|
||
// 新規・通常
|
||
$merged[$key]->regular_new_count += $count;
|
||
$merged[$key]->regular_new_amount += $amount;
|
||
} elseif ($isNew && $isReduction) {
|
||
// 新規・減免
|
||
$merged[$key]->regular_new_reduction_count += $count;
|
||
$merged[$key]->regular_new_reduction_amount += $amount;
|
||
} elseif (!$isNew && !$isReduction) {
|
||
// 更新・通常
|
||
$merged[$key]->regular_update_count += $count;
|
||
$merged[$key]->regular_update_amount += $amount;
|
||
} elseif (!$isNew && $isReduction) {
|
||
// 更新・減免
|
||
$merged[$key]->regular_update_reduction_count += $count;
|
||
$merged[$key]->regular_update_reduction_amount += $amount;
|
||
}
|
||
}
|
||
|
||
return $merged;
|
||
}
|
||
|
||
/**
|
||
* 一時金・解約・再発行データを車種区分毎に統合
|
||
*
|
||
* @param array $lumpsumData
|
||
* @param array $refundData
|
||
* @param array $reissueData
|
||
* @return array
|
||
*/
|
||
private function mergeOtherEarningsData(array $lumpsumData, array $refundData, array $reissueData): array
|
||
{
|
||
$merged = [];
|
||
|
||
// 一時金
|
||
foreach ($lumpsumData as $row) {
|
||
$psectionId = $row->psection_id;
|
||
if (!isset($merged[$psectionId])) {
|
||
$merged[$psectionId] = (object)[
|
||
'psection_id' => $psectionId,
|
||
'usertype_subject1' => null,
|
||
'enable_months' => 0,
|
||
'lumpsum_count' => 0,
|
||
'lumpsum' => 0,
|
||
'refunds' => 0,
|
||
'reissue_count' => 0
|
||
];
|
||
}
|
||
$merged[$psectionId]->lumpsum_count = $row->lumpsum_count;
|
||
$merged[$psectionId]->lumpsum = $row->lumpsum;
|
||
}
|
||
|
||
// 解約返戻金
|
||
foreach ($refundData as $row) {
|
||
$psectionId = $row->psection_id;
|
||
if (!isset($merged[$psectionId])) {
|
||
$merged[$psectionId] = (object)[
|
||
'psection_id' => $psectionId,
|
||
'usertype_subject1' => null,
|
||
'enable_months' => 0,
|
||
'lumpsum_count' => 0,
|
||
'lumpsum' => 0,
|
||
'refunds' => 0,
|
||
'reissue_count' => 0
|
||
];
|
||
}
|
||
$merged[$psectionId]->refunds = $row->refunds;
|
||
}
|
||
|
||
// 再発行
|
||
foreach ($reissueData as $row) {
|
||
$psectionId = $row->psection_id;
|
||
if (!isset($merged[$psectionId])) {
|
||
$merged[$psectionId] = (object)[
|
||
'psection_id' => $psectionId,
|
||
'usertype_subject1' => null,
|
||
'enable_months' => 0,
|
||
'lumpsum_count' => 0,
|
||
'lumpsum' => 0,
|
||
'refunds' => 0,
|
||
'reissue_count' => 0
|
||
];
|
||
}
|
||
$merged[$psectionId]->reissue_count = $row->reissue_count;
|
||
}
|
||
|
||
return $merged;
|
||
}
|
||
|
||
/**
|
||
* 【処理4】既存の売上集計結果を削除
|
||
*
|
||
* 仕様書のキー:駐輪場ID, 集計種別, 集計開始日, 集計終了日, 売上日付, 車種区分, 分類名1, 定期有効月数
|
||
* 仕様 todo/SHJ-9/SHJ-9.txt:181-189
|
||
*
|
||
* @param int $parkId 駐輪場ID
|
||
* @param string $targetDate 集計対象日(YYYY-MM-DD)
|
||
* @return void
|
||
*/
|
||
private function deleteExistingSummary(int $parkId, string $targetDate): void
|
||
{
|
||
// 仕様書どおり、同一キーの組み合わせで削除
|
||
// 日次の場合、集計開始日・終了日はNULL、売上日付で判定
|
||
DB::table('earnings_summary')
|
||
->where('park_id', $parkId)
|
||
->where('summary_type', self::SUMMARY_TYPE_DAILY)
|
||
->whereNull('summary_start_date')
|
||
->whereNull('summary_end_date')
|
||
->where('earnings_date', $targetDate)
|
||
// psection_id, usertype_subject1, enable_months は
|
||
// レコードごとに異なるため、ここでは指定しない
|
||
->delete();
|
||
|
||
Log::debug('既存の売上集計結果削除', [
|
||
'park_id' => $parkId,
|
||
'target_date' => $targetDate
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* 売上集計結果を登録
|
||
*
|
||
* 仕様 todo/SHJ-9/SHJ-9.txt:181-247
|
||
*
|
||
* @param object $park 駐輪場情報
|
||
* @param object $data 売上データ
|
||
* @param string $targetDate 集計対象日(YYYY-MM-DD)
|
||
* @param string $dataType データ種別(regular or other)
|
||
* @return void
|
||
*/
|
||
private function createEarningsSummary($park, $data, string $targetDate, string $dataType): void
|
||
{
|
||
$insertData = [
|
||
'park_id' => $park->park_id,
|
||
'summary_type' => self::SUMMARY_TYPE_DAILY, // 3 = 日次
|
||
'summary_start_date' => null, // 日次は NULL (仕様line 215)
|
||
'summary_end_date' => null, // 日次は NULL (仕様line 216)
|
||
'earnings_date' => $targetDate,
|
||
'psection_id' => $data->psection_id,
|
||
'usertype_subject1' => $data->usertype_subject1 ?? null, // 実際の分類名
|
||
'enable_months' => $data->enable_months ?? 0, // 実際の定期有効月数
|
||
'summary_note' => "SHJ-9:{$targetDate}", // 仕様line 236
|
||
'created_at' => now(),
|
||
'updated_at' => now(),
|
||
'operator_id' => self::BATCH_OPERATOR_ID // 9999999 (仕様line 239)
|
||
];
|
||
|
||
if ($dataType === 'regular') {
|
||
// 定期契約データの場合:mergeRegularDataByGroup()で既に統合済み
|
||
// 新規/更新 × 減免/通常 の各件数・金額がすべて含まれている (仕様line 98-113)
|
||
$insertData = array_merge($insertData, [
|
||
'regular_new_count' => $data->regular_new_count ?? 0,
|
||
'regular_new_amount' => $data->regular_new_amount ?? 0,
|
||
'regular_new_reduction_count' => $data->regular_new_reduction_count ?? 0,
|
||
'regular_new_reduction_amount' => $data->regular_new_reduction_amount ?? 0,
|
||
'regular_update_count' => $data->regular_update_count ?? 0,
|
||
'regular_update_amount' => $data->regular_update_amount ?? 0,
|
||
'regular_update_reduction_count' => $data->regular_update_reduction_count ?? 0,
|
||
'regular_update_reduction_amount' => $data->regular_update_reduction_amount ?? 0,
|
||
'lumpsum_count' => 0, // 仕様line 106
|
||
'lumpsum' => 0, // 仕様line 107
|
||
'refunds' => 0, // 仕様line 108
|
||
'other_income' => 0, // 仕様line 109
|
||
'other_spending' => 0, // 仕様line 110
|
||
'reissue_count' => 0, // 仕様line 111
|
||
'reissue_amount' => 0 // 仕様line 112
|
||
]);
|
||
} else {
|
||
// 一時金・解約・再発行データの場合:定期フィールドは0固定 (仕様line 152-167)
|
||
$insertData = array_merge($insertData, [
|
||
'regular_new_count' => 0, // 仕様line 152
|
||
'regular_new_amount' => 0, // 仕様line 153
|
||
'regular_new_reduction_count' => 0, // 仕様line 154
|
||
'regular_new_reduction_amount' => 0, // 仕様line 155
|
||
'regular_update_count' => 0, // 仕様line 156
|
||
'regular_update_amount' => 0, // 仕様line 157
|
||
'regular_update_reduction_count' => 0, // 仕様line 158
|
||
'regular_update_reduction_amount' => 0, // 仕様line 159
|
||
'lumpsum_count' => $data->lumpsum_count ?? 0, // 仕様line 160
|
||
'lumpsum' => $data->lumpsum ?? 0, // 仕様line 161
|
||
'refunds' => $data->refunds ?? 0, // 仕様line 162
|
||
'other_income' => 0, // 仕様line 163
|
||
'other_spending' => 0, // 仕様line 164
|
||
'reissue_count' => $data->reissue_count ?? 0, // 仕様line 165
|
||
'reissue_amount' => 0 // 仕様line 166: 0固定
|
||
]);
|
||
}
|
||
|
||
DB::table('earnings_summary')->insert($insertData);
|
||
|
||
Log::debug('売上集計結果登録', [
|
||
'park_id' => $park->park_id,
|
||
'psection_id' => $data->psection_id,
|
||
'data_type' => $dataType,
|
||
'target_date' => $targetDate
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* 【処理5】オペレータキュー作成(駐輪場単位・情報不備がある場合のみ)
|
||
*
|
||
* 仕様 todo/SHJ-9/SHJ-9.txt:253-280
|
||
* - que_class: 14(集計対象エラー)
|
||
* - que_comment: 空文字("")
|
||
* - que_status: 1(キュー発生)
|
||
* - que_status_comment: 空文字("")
|
||
* - work_instructions: 情報不備メッセージ
|
||
* - park_id: 駐輪場ID(仕様 "処理1.駐輪場ID"、パラメータエラー時はnull)
|
||
* - operator_id: 9999999(バッチ処理固定値)
|
||
*
|
||
* @param string $message 情報不備メッセージ
|
||
* @param int|null $parkId 駐輪場ID(パラメータエラー時はnull)
|
||
* @return void
|
||
*/
|
||
private function createOperatorQueue(string $message, ?int $parkId = null): void
|
||
{
|
||
try {
|
||
DB::table('operator_que')->insert([
|
||
'que_class' => 14, // 集計対象エラー
|
||
'user_id' => null,
|
||
'contract_id' => null,
|
||
'park_id' => $parkId, // 仕様:処理1.駐輪場ID
|
||
'que_comment' => '', // 仕様line 260: ""
|
||
'que_status' => 1, // キュー発生
|
||
'que_status_comment' => '', // 仕様line 262: ""
|
||
'work_instructions' => $message, // 仕様line 263: 情報不備
|
||
'operator_id' => self::BATCH_OPERATOR_ID, // 9999999
|
||
'created_at' => now(),
|
||
'updated_at' => now()
|
||
]);
|
||
|
||
Log::info('オペレータキュー作成完了', [
|
||
'park_id' => $parkId,
|
||
'que_class' => 14,
|
||
'que_status' => 1,
|
||
'operator_id' => self::BATCH_OPERATOR_ID,
|
||
'work_instructions' => $message
|
||
]);
|
||
|
||
} catch (\Exception $e) {
|
||
Log::error('オペレータキュー作成エラー', [
|
||
'park_id' => $parkId,
|
||
'error' => $e->getMessage()
|
||
]);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* SHJ-8 バッチ処理ログ作成
|
||
*
|
||
* 仕様 todo/SHJ-9/SHJ-9.txt:289-300
|
||
* 共通処理「SHJ-8 バッチ処理ログ作成」を呼び出す
|
||
*
|
||
* @param string $jobName ジョブ名
|
||
* @param string $status ステータス (success/error)
|
||
* @param string $statusComment 業務固有のステータスコメント
|
||
* @return void
|
||
*/
|
||
private function callShjEight(string $jobName, string $status, string $statusComment): void
|
||
{
|
||
try {
|
||
$device = Device::orderBy('device_id')->first();
|
||
$deviceId = $device ? $device->device_id : 1;
|
||
|
||
$today = now()->format('Y/m/d');
|
||
|
||
$this->shjEightService->execute(
|
||
$deviceId,
|
||
'SHJ-9',
|
||
$jobName,
|
||
$status,
|
||
$statusComment,
|
||
$today,
|
||
$today
|
||
);
|
||
|
||
Log::info('SHJ-8 バッチ処理ログ作成完了', [
|
||
'job_name' => $jobName,
|
||
'status' => $status
|
||
]);
|
||
|
||
} catch (\Exception $e) {
|
||
Log::error('SHJ-8 バッチ処理ログ作成エラー', [
|
||
'error' => $e->getMessage(),
|
||
'job_name' => $jobName,
|
||
'status_comment' => $statusComment
|
||
]);
|
||
throw $e;
|
||
}
|
||
}
|
||
}
|