- TenantSetting CRUD API 추가 - Calendar, Entertainment, VAT 서비스 개선 - 5130 BOM 계산 로직 수정 - quote_items에 item_type 컬럼 추가 - tenant_settings 테이블 마이그레이션 - Swagger 문서 업데이트
218 lines
8.6 KiB
PHP
218 lines
8.6 KiB
PHP
<?php
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
use App\Helpers\Legacy5130Calculator;
|
|
use App\Services\Quote\FormulaEvaluatorService;
|
|
use Illuminate\Console\Command;
|
|
|
|
/**
|
|
* 5130 견적 계산 결과 검증 커맨드
|
|
*
|
|
* 5130 레거시 시스템의 계산 결과와 SAM의 계산 결과가 동일한지 검증합니다.
|
|
*
|
|
* Usage:
|
|
* php artisan migration:verify-5130-calculation
|
|
* php artisan migration:verify-5130-calculation --W0=3000 --H0=2500 --type=screen
|
|
*/
|
|
class Verify5130Calculation extends Command
|
|
{
|
|
protected $signature = 'migration:verify-5130-calculation
|
|
{--W0=2500 : 개구부 폭 (mm)}
|
|
{--H0=2000 : 개구부 높이 (mm)}
|
|
{--qty=1 : 수량}
|
|
{--type=screen : 제품 유형 (screen, steel)}
|
|
{--tenant-id=1 : 테넌트 ID}
|
|
{--finished-goods= : 완제품 코드 (BOM 계산 시)}
|
|
{--verbose-mode : 상세 출력 모드}';
|
|
|
|
protected $description = '5130 레거시 계산 결과와 SAM 계산 결과 비교 검증';
|
|
|
|
public function handle(FormulaEvaluatorService $formulaEvaluator): int
|
|
{
|
|
$this->info('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
$this->info(' 5130 → SAM 견적 계산 검증');
|
|
$this->info('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
$this->newLine();
|
|
|
|
// 입력 파라미터
|
|
$W0 = (float) $this->option('W0');
|
|
$H0 = (float) $this->option('H0');
|
|
$qty = (int) $this->option('qty');
|
|
$productType = $this->option('type');
|
|
$tenantId = (int) $this->option('tenant-id');
|
|
$finishedGoodsCode = $this->option('finished-goods');
|
|
$verboseMode = $this->option('verbose-mode');
|
|
|
|
$this->info('📥 입력 파라미터:');
|
|
$this->table(
|
|
['항목', '값'],
|
|
[
|
|
['개구부 폭 (W0)', "{$W0} mm"],
|
|
['개구부 높이 (H0)', "{$H0} mm"],
|
|
['수량 (QTY)', $qty],
|
|
['제품 유형', $productType],
|
|
['테넌트 ID', $tenantId],
|
|
]
|
|
);
|
|
$this->newLine();
|
|
|
|
// 1. Legacy5130Calculator로 계산 (5130 호환 모드)
|
|
$this->info('🔄 Step 1: Legacy5130Calculator 계산 (5130 호환)');
|
|
$legacyResult = Legacy5130Calculator::calculateEstimate($W0, $H0, $qty, $productType);
|
|
|
|
$this->table(
|
|
['항목', '값'],
|
|
[
|
|
['제작 폭 (W1)', "{$legacyResult['calculated']['W1']} mm"],
|
|
['제작 높이 (H1)', "{$legacyResult['calculated']['H1']} mm"],
|
|
['면적 (M)', "{$legacyResult['calculated']['area_m2']} m²"],
|
|
['중량 (K)', "{$legacyResult['calculated']['weight_kg']} kg"],
|
|
['브라켓 인치', "{$legacyResult['calculated']['bracket_inch']} inch"],
|
|
['모터 용량', $legacyResult['motor']['capacity']],
|
|
['브라켓 사이즈', $legacyResult['motor']['bracket_dimensions']],
|
|
]
|
|
);
|
|
$this->newLine();
|
|
|
|
// 2. SAM FormulaEvaluatorService로 계산
|
|
$this->info('🔄 Step 2: SAM FormulaEvaluatorService 계산');
|
|
|
|
if ($finishedGoodsCode) {
|
|
// BOM 기반 계산
|
|
$samResult = $formulaEvaluator->calculateBomWithDebug(
|
|
$finishedGoodsCode,
|
|
['W0' => $W0, 'H0' => $H0, 'QTY' => $qty],
|
|
$tenantId
|
|
);
|
|
|
|
if (! $samResult['success']) {
|
|
$this->error('SAM 계산 실패: '.($samResult['error'] ?? '알 수 없는 오류'));
|
|
|
|
return Command::FAILURE;
|
|
}
|
|
|
|
$samVariables = $samResult['variables'];
|
|
} else {
|
|
// 직접 계산 (변수만)
|
|
$samVariables = $this->calculateSamVariables($W0, $H0, $productType);
|
|
$samResult = ['success' => true, 'variables' => $samVariables];
|
|
}
|
|
|
|
$this->table(
|
|
['항목', '값'],
|
|
[
|
|
['제작 폭 (W1)', ($samVariables['W1'] ?? 'N/A').' mm'],
|
|
['제작 높이 (H1)', ($samVariables['H1'] ?? 'N/A').' mm'],
|
|
['면적 (M)', round($samVariables['M'] ?? 0, 4).' m²'],
|
|
['중량 (K)', round($samVariables['K'] ?? 0, 2).' kg'],
|
|
]
|
|
);
|
|
$this->newLine();
|
|
|
|
// 3. 결과 비교
|
|
$this->info('🔍 Step 3: 결과 비교');
|
|
|
|
$comparison = [
|
|
['W1 (제작 폭)', $legacyResult['calculated']['W1'], $samVariables['W1'] ?? 0],
|
|
['H1 (제작 높이)', $legacyResult['calculated']['H1'], $samVariables['H1'] ?? 0],
|
|
['M (면적)', $legacyResult['calculated']['area_m2'], round($samVariables['M'] ?? 0, 4)],
|
|
['K (중량)', $legacyResult['calculated']['weight_kg'], round($samVariables['K'] ?? 0, 2)],
|
|
];
|
|
|
|
$allMatch = true;
|
|
$comparisonTable = [];
|
|
|
|
foreach ($comparison as [$name, $legacy, $sam]) {
|
|
$diff = abs($legacy - $sam);
|
|
$percentDiff = $legacy != 0 ? ($diff / abs($legacy)) * 100 : 0;
|
|
$match = $percentDiff < 1; // 1% 이내 허용
|
|
|
|
if (! $match) {
|
|
$allMatch = false;
|
|
}
|
|
|
|
$comparisonTable[] = [
|
|
$name,
|
|
$legacy,
|
|
$sam,
|
|
round($diff, 4),
|
|
round($percentDiff, 2).'%',
|
|
$match ? '✅ 일치' : '❌ 불일치',
|
|
];
|
|
}
|
|
|
|
$this->table(
|
|
['항목', '5130 (Legacy)', 'SAM', '차이', '차이율', '결과'],
|
|
$comparisonTable
|
|
);
|
|
$this->newLine();
|
|
|
|
// 4. 최종 결과
|
|
if ($allMatch) {
|
|
$this->info('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
$this->info(' ✅ 검증 성공: 5130과 SAM 계산 결과가 일치합니다.');
|
|
$this->info('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
} else {
|
|
$this->error('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
$this->error(' ❌ 검증 실패: 5130과 SAM 계산 결과가 불일치합니다.');
|
|
$this->error('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
}
|
|
|
|
// 상세 출력 모드
|
|
if ($verboseMode) {
|
|
$this->newLine();
|
|
$this->info('📊 상세 정보 (Legacy5130Calculator):');
|
|
$this->line(json_encode($legacyResult, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
|
|
|
if ($finishedGoodsCode && isset($samResult['debug_steps'])) {
|
|
$this->newLine();
|
|
$this->info('📊 상세 정보 (SAM Debug Steps):');
|
|
foreach ($samResult['debug_steps'] as $step) {
|
|
$this->line("Step {$step['step']}: {$step['name']}");
|
|
$this->line(json_encode($step['data'], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
|
}
|
|
}
|
|
}
|
|
|
|
return $allMatch ? Command::SUCCESS : Command::FAILURE;
|
|
}
|
|
|
|
/**
|
|
* SAM 방식으로 변수 계산 (BOM 없이)
|
|
*/
|
|
private function calculateSamVariables(float $W0, float $H0, string $productType): array
|
|
{
|
|
// 마진값 결정
|
|
if (strtolower($productType) === 'steel') {
|
|
$marginW = 110;
|
|
$marginH = 350;
|
|
} else {
|
|
$marginW = 140;
|
|
$marginH = 350;
|
|
}
|
|
|
|
$W1 = $W0 + $marginW;
|
|
$H1 = $H0 + $marginH;
|
|
$M = ($W1 * $H1) / 1000000;
|
|
|
|
// 중량 계산
|
|
if (strtolower($productType) === 'steel') {
|
|
$K = $M * 25;
|
|
} else {
|
|
$K = $M * 2 + ($W0 / 1000) * 14.17;
|
|
}
|
|
|
|
return [
|
|
'W0' => $W0,
|
|
'H0' => $H0,
|
|
'W1' => $W1,
|
|
'H1' => $H1,
|
|
'W' => $W1,
|
|
'H' => $H1,
|
|
'M' => $M,
|
|
'K' => $K,
|
|
];
|
|
}
|
|
}
|