feat: 경동기업 전용 견적 계산 로직 구현 (Phase 4 완료)
- KdPriceTable 모델: 경동기업 단가 테이블 (motor, shaft, pipe, angle, raw_material, bdmodels) - KyungdongFormulaHandler: 모터 용량, 브라켓 크기, 절곡품(10종), 부자재(3종) 계산 - FormulaEvaluatorService: tenant_id=287 라우팅 추가 - kd_price_tables 마이그레이션 및 시더 (47건 단가 데이터) 테스트 결과: W0=3000, H0=2500 입력 시 16개 항목, 합계 751,200원 정상 계산 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,3 +1,49 @@
|
|||||||
|
## 2026-01-29 (수) - 경동기업 견적 로직 Phase 4 완료
|
||||||
|
|
||||||
|
### 작업 목표
|
||||||
|
- 경동기업(tenant_id=287) 전용 견적 계산 로직 구현
|
||||||
|
- 5130 레거시 시스템의 BOM/견적 로직을 SAM에 이식
|
||||||
|
- 동적 BOM 계산: 모터, 제어기, 절곡품(10종), 부자재(3종)
|
||||||
|
|
||||||
|
### 생성된 파일
|
||||||
|
| 파일명 | 설명 |
|
||||||
|
|--------|------|
|
||||||
|
| `app/Models/Kyungdong/KdPriceTable.php` | 경동기업 전용 단가 테이블 모델 |
|
||||||
|
| `app/Services/Quote/Handlers/KyungdongFormulaHandler.php` | 경동기업 견적 계산 핸들러 |
|
||||||
|
| `database/migrations/2026_01_29_004736_create_kd_price_tables_table.php` | kd_price_tables 마이그레이션 |
|
||||||
|
| `database/seeders/Kyungdong/KdPriceTableSeeder.php` | 단가 데이터 시더 (47건) |
|
||||||
|
|
||||||
|
### 수정된 파일
|
||||||
|
| 파일명 | 설명 |
|
||||||
|
|--------|------|
|
||||||
|
| `app/Services/Quote/FormulaEvaluatorService.php` | tenant_id=287 라우팅 추가 |
|
||||||
|
|
||||||
|
### 구현된 기능
|
||||||
|
| 기능 | 설명 |
|
||||||
|
|------|------|
|
||||||
|
| 모터 용량 계산 | 제품타입 × 인치 × 중량 3차원 조건 |
|
||||||
|
| 브라켓 크기 결정 | 중량 기반 530*320, 600*350, 690*390 |
|
||||||
|
| 주자재 계산 | W × (H + 550) / 1,000,000 × 단가 |
|
||||||
|
| 절곡품 계산 (10종) | 케이스, 마구리, 가이드레일, 하장바, L바, 평철, 환봉 등 |
|
||||||
|
| 부자재 계산 (3종) | 감기샤프트, 각파이프, 앵글 |
|
||||||
|
|
||||||
|
### 테스트 결과
|
||||||
|
```
|
||||||
|
입력: W0=3000, H0=2500, 철재형, 5인치, KSS01 SUS
|
||||||
|
출력: 16개 항목, 합계 751,200원 ✅
|
||||||
|
```
|
||||||
|
|
||||||
|
### 검증 완료
|
||||||
|
- [x] Pint 코드 스타일 통과
|
||||||
|
- [x] 마이그레이션 실행 완료 (kd_price_tables)
|
||||||
|
- [x] 시더 실행 완료 (47건 단가 데이터)
|
||||||
|
- [x] tinker 테스트 통과 (16개 항목 정상 계산)
|
||||||
|
|
||||||
|
### 계획 문서
|
||||||
|
- `docs/plans/kd-quote-logic-plan.md` - Phase 0~4 완료 (100%)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 2026-01-21 (화) - TodayIssue 헤더 알림 API (Phase 3 완료)
|
## 2026-01-21 (화) - TodayIssue 헤더 알림 API (Phase 3 완료)
|
||||||
|
|
||||||
### 작업 목표
|
### 작업 목표
|
||||||
|
|||||||
341
app/Models/Kyungdong/KdPriceTable.php
Normal file
341
app/Models/Kyungdong/KdPriceTable.php
Normal file
@@ -0,0 +1,341 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models\Kyungdong;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 경동기업 전용 단가 테이블 모델
|
||||||
|
*
|
||||||
|
* 5130 레거시 price_* 테이블 데이터 조회용
|
||||||
|
*
|
||||||
|
* @property int $id
|
||||||
|
* @property int $tenant_id
|
||||||
|
* @property string $table_type
|
||||||
|
* @property string|null $item_code
|
||||||
|
* @property string|null $item_name
|
||||||
|
* @property string|null $category
|
||||||
|
* @property string|null $spec1
|
||||||
|
* @property string|null $spec2
|
||||||
|
* @property string|null $spec3
|
||||||
|
* @property float $unit_price
|
||||||
|
* @property string $unit
|
||||||
|
* @property array|null $raw_data
|
||||||
|
* @property bool $is_active
|
||||||
|
*/
|
||||||
|
class KdPriceTable extends Model
|
||||||
|
{
|
||||||
|
// 테이블 유형 상수
|
||||||
|
public const TYPE_MOTOR = 'motor';
|
||||||
|
|
||||||
|
public const TYPE_SHAFT = 'shaft';
|
||||||
|
|
||||||
|
public const TYPE_PIPE = 'pipe';
|
||||||
|
|
||||||
|
public const TYPE_ANGLE = 'angle';
|
||||||
|
|
||||||
|
public const TYPE_RAW_MATERIAL = 'raw_material';
|
||||||
|
|
||||||
|
public const TYPE_BDMODELS = 'bdmodels';
|
||||||
|
|
||||||
|
// 경동기업 테넌트 ID
|
||||||
|
public const TENANT_ID = 287;
|
||||||
|
|
||||||
|
protected $table = 'kd_price_tables';
|
||||||
|
|
||||||
|
protected $fillable = [
|
||||||
|
'tenant_id',
|
||||||
|
'table_type',
|
||||||
|
'item_code',
|
||||||
|
'item_name',
|
||||||
|
'category',
|
||||||
|
'spec1',
|
||||||
|
'spec2',
|
||||||
|
'spec3',
|
||||||
|
'unit_price',
|
||||||
|
'unit',
|
||||||
|
'raw_data',
|
||||||
|
'is_active',
|
||||||
|
];
|
||||||
|
|
||||||
|
protected $casts = [
|
||||||
|
'unit_price' => 'decimal:2',
|
||||||
|
'raw_data' => 'array',
|
||||||
|
'is_active' => 'boolean',
|
||||||
|
];
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// Scopes
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 테이블 유형으로 필터링
|
||||||
|
*/
|
||||||
|
public function scopeOfType(Builder $query, string $type): Builder
|
||||||
|
{
|
||||||
|
return $query->where('table_type', $type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 활성 데이터만
|
||||||
|
*/
|
||||||
|
public function scopeActive(Builder $query): Builder
|
||||||
|
{
|
||||||
|
return $query->where('is_active', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 모터 단가 조회
|
||||||
|
*/
|
||||||
|
public function scopeMotor(Builder $query): Builder
|
||||||
|
{
|
||||||
|
return $query->ofType(self::TYPE_MOTOR)->active();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 샤프트 단가 조회
|
||||||
|
*/
|
||||||
|
public function scopeShaft(Builder $query): Builder
|
||||||
|
{
|
||||||
|
return $query->ofType(self::TYPE_SHAFT)->active();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 파이프 단가 조회
|
||||||
|
*/
|
||||||
|
public function scopePipeType(Builder $query): Builder
|
||||||
|
{
|
||||||
|
return $query->ofType(self::TYPE_PIPE)->active();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 앵글 단가 조회
|
||||||
|
*/
|
||||||
|
public function scopeAngle(Builder $query): Builder
|
||||||
|
{
|
||||||
|
return $query->ofType(self::TYPE_ANGLE)->active();
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// Static Query Methods
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 모터 단가 조회
|
||||||
|
*
|
||||||
|
* @param string $motorCapacity 모터 용량 (150K, 300K, 400K, 500K, 600K, 800K, 1000K)
|
||||||
|
*/
|
||||||
|
public static function getMotorPrice(string $motorCapacity): float
|
||||||
|
{
|
||||||
|
$record = self::motor()
|
||||||
|
->where('category', $motorCapacity)
|
||||||
|
->first();
|
||||||
|
|
||||||
|
return (float) ($record?->unit_price ?? 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 제어기 단가 조회
|
||||||
|
*
|
||||||
|
* @param string $controllerType 제어기 타입 (매립형, 노출형, 뒷박스)
|
||||||
|
*/
|
||||||
|
public static function getControllerPrice(string $controllerType): float
|
||||||
|
{
|
||||||
|
$record = self::motor()
|
||||||
|
->where('category', $controllerType)
|
||||||
|
->first();
|
||||||
|
|
||||||
|
return (float) ($record?->unit_price ?? 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 샤프트 단가 조회
|
||||||
|
*
|
||||||
|
* @param string $size 사이즈 (3, 4, 5인치)
|
||||||
|
* @param float $length 길이 (m 단위)
|
||||||
|
*/
|
||||||
|
public static function getShaftPrice(string $size, float $length): float
|
||||||
|
{
|
||||||
|
// 길이를 소수점 1자리 문자열로 변환 (DB 저장 형식: '3.0', '4.0')
|
||||||
|
$lengthStr = number_format($length, 1, '.', '');
|
||||||
|
|
||||||
|
$record = self::shaft()
|
||||||
|
->where('spec1', $size)
|
||||||
|
->where('spec2', $lengthStr)
|
||||||
|
->first();
|
||||||
|
|
||||||
|
return (float) ($record?->unit_price ?? 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 파이프 단가 조회
|
||||||
|
*
|
||||||
|
* @param string $thickness 두께 (1.4 등)
|
||||||
|
* @param int $length 길이 (3000, 6000)
|
||||||
|
*/
|
||||||
|
public static function getPipePrice(string $thickness, int $length): float
|
||||||
|
{
|
||||||
|
$record = self::pipeType()
|
||||||
|
->where('spec1', $thickness)
|
||||||
|
->where('spec2', (string) $length)
|
||||||
|
->first();
|
||||||
|
|
||||||
|
return (float) ($record?->unit_price ?? 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 앵글 단가 조회
|
||||||
|
*
|
||||||
|
* @param string $type 타입 (스크린용, 철재용)
|
||||||
|
* @param string $bracketSize 브라켓크기 (530*320, 600*350, 690*390)
|
||||||
|
* @param string $angleType 앵글타입 (앵글3T, 앵글4T)
|
||||||
|
*/
|
||||||
|
public static function getAnglePrice(string $type, string $bracketSize, string $angleType): float
|
||||||
|
{
|
||||||
|
$record = self::angle()
|
||||||
|
->where('category', $type)
|
||||||
|
->where('spec1', $bracketSize)
|
||||||
|
->where('spec2', $angleType)
|
||||||
|
->first();
|
||||||
|
|
||||||
|
return (float) ($record?->unit_price ?? 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 원자재 단가 조회
|
||||||
|
*
|
||||||
|
* @param string $materialName 원자재명 (실리카, 스크린 등)
|
||||||
|
*/
|
||||||
|
public static function getRawMaterialPrice(string $materialName): float
|
||||||
|
{
|
||||||
|
$record = self::ofType(self::TYPE_RAW_MATERIAL)
|
||||||
|
->active()
|
||||||
|
->where('item_name', $materialName)
|
||||||
|
->first();
|
||||||
|
|
||||||
|
return (float) ($record?->unit_price ?? 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// BDmodels 단가 조회 (절곡품)
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BDmodels 스코프
|
||||||
|
*/
|
||||||
|
public function scopeBdmodels(Builder $query): Builder
|
||||||
|
{
|
||||||
|
return $query->ofType(self::TYPE_BDMODELS)->active();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BDmodels 단가 조회 (케이스, 가이드레일, 하단마감재 등)
|
||||||
|
*
|
||||||
|
* @param string $secondItem 부품분류 (케이스, 가이드레일, 하단마감재, L-BAR 등)
|
||||||
|
* @param string|null $modelName 모델코드 (KSS01, KWS01 등)
|
||||||
|
* @param string|null $finishingType 마감재질 (SUS, EGI)
|
||||||
|
* @param string|null $spec 규격 (120*70, 650*550 등)
|
||||||
|
*/
|
||||||
|
public static function getBDModelPrice(
|
||||||
|
string $secondItem,
|
||||||
|
?string $modelName = null,
|
||||||
|
?string $finishingType = null,
|
||||||
|
?string $spec = null
|
||||||
|
): float {
|
||||||
|
$query = self::bdmodels()->where('category', $secondItem);
|
||||||
|
|
||||||
|
if ($modelName) {
|
||||||
|
$query->where('item_code', $modelName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($finishingType) {
|
||||||
|
$query->where('spec1', $finishingType);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($spec) {
|
||||||
|
$query->where('spec2', $spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
$record = $query->first();
|
||||||
|
|
||||||
|
return (float) ($record?->unit_price ?? 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 케이스 단가 조회
|
||||||
|
*
|
||||||
|
* @param string $spec 케이스 규격 (500*380, 650*550 등)
|
||||||
|
*/
|
||||||
|
public static function getCasePrice(string $spec): float
|
||||||
|
{
|
||||||
|
return self::getBDModelPrice('케이스', null, null, $spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 가이드레일 단가 조회
|
||||||
|
*
|
||||||
|
* @param string $modelName 모델코드 (KSS01 등)
|
||||||
|
* @param string $finishingType 마감재질 (SUS, EGI)
|
||||||
|
* @param string $spec 규격 (120*70, 120*100)
|
||||||
|
*/
|
||||||
|
public static function getGuideRailPrice(string $modelName, string $finishingType, string $spec): float
|
||||||
|
{
|
||||||
|
return self::getBDModelPrice('가이드레일', $modelName, $finishingType, $spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 하단마감재(하장바) 단가 조회
|
||||||
|
*
|
||||||
|
* @param string $modelName 모델코드
|
||||||
|
* @param string $finishingType 마감재질
|
||||||
|
*/
|
||||||
|
public static function getBottomBarPrice(string $modelName, string $finishingType): float
|
||||||
|
{
|
||||||
|
return self::getBDModelPrice('하단마감재', $modelName, $finishingType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* L-BAR 단가 조회
|
||||||
|
*
|
||||||
|
* @param string $modelName 모델코드
|
||||||
|
*/
|
||||||
|
public static function getLBarPrice(string $modelName): float
|
||||||
|
{
|
||||||
|
return self::getBDModelPrice('L-BAR', $modelName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 보강평철 단가 조회
|
||||||
|
*/
|
||||||
|
public static function getFlatBarPrice(): float
|
||||||
|
{
|
||||||
|
return self::getBDModelPrice('보강평철');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 케이스 마구리 단가 조회
|
||||||
|
*
|
||||||
|
* @param string $spec 규격
|
||||||
|
*/
|
||||||
|
public static function getCaseCapPrice(string $spec): float
|
||||||
|
{
|
||||||
|
return self::getBDModelPrice('마구리', null, null, $spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 케이스용 연기차단재 단가 조회
|
||||||
|
*/
|
||||||
|
public static function getCaseSmokeBlockPrice(): float
|
||||||
|
{
|
||||||
|
return self::getBDModelPrice('케이스용 연기차단재');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 가이드레일용 연기차단재 단가 조회
|
||||||
|
*/
|
||||||
|
public static function getRailSmokeBlockPrice(): float
|
||||||
|
{
|
||||||
|
return self::getBDModelPrice('가이드레일용 연기차단재');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@
|
|||||||
use App\Models\Items\Item;
|
use App\Models\Items\Item;
|
||||||
use App\Models\Products\Price;
|
use App\Models\Products\Price;
|
||||||
use App\Models\Quote\QuoteFormula;
|
use App\Models\Quote\QuoteFormula;
|
||||||
|
use App\Services\Quote\Handlers\KyungdongFormulaHandler;
|
||||||
use App\Services\Service;
|
use App\Services\Service;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
@@ -28,6 +29,11 @@ class FormulaEvaluatorService extends Service
|
|||||||
'SUM', 'ROUND', 'CEIL', 'FLOOR', 'ABS', 'MIN', 'MAX', 'IF', 'AND', 'OR', 'NOT',
|
'SUM', 'ROUND', 'CEIL', 'FLOOR', 'ABS', 'MIN', 'MAX', 'IF', 'AND', 'OR', 'NOT',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 경동기업 테넌트 ID
|
||||||
|
*/
|
||||||
|
private const KYUNGDONG_TENANT_ID = 287;
|
||||||
|
|
||||||
private array $variables = [];
|
private array $variables = [];
|
||||||
|
|
||||||
private array $errors = [];
|
private array $errors = [];
|
||||||
@@ -599,6 +605,11 @@ public function calculateBomWithDebug(
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 경동기업(tenant_id=287) 전용 계산 로직 분기
|
||||||
|
if ($tenantId === self::KYUNGDONG_TENANT_ID) {
|
||||||
|
return $this->calculateKyungdongBom($finishedGoodsCode, $inputVariables, $tenantId);
|
||||||
|
}
|
||||||
|
|
||||||
// Step 1: 입력값 수집 (React 동기화 변수 포함)
|
// Step 1: 입력값 수집 (React 동기화 변수 포함)
|
||||||
$this->addDebugStep(1, '입력값수집', [
|
$this->addDebugStep(1, '입력값수집', [
|
||||||
'W0' => $inputVariables['W0'] ?? null,
|
'W0' => $inputVariables['W0'] ?? null,
|
||||||
@@ -1538,4 +1549,219 @@ private function mergeLeafMaterials(array $leafMaterials, int $tenantId): array
|
|||||||
|
|
||||||
return array_values($result);
|
return array_values($result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// 경동기업 전용 계산 (tenant_id = 287)
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 경동기업 전용 BOM 계산
|
||||||
|
*
|
||||||
|
* 5130 레거시 시스템의 견적 로직을 구현한 KyungdongFormulaHandler 사용
|
||||||
|
* - 3차원 조건 모터 용량 계산 (제품타입 × 인치 × 중량)
|
||||||
|
* - 브라켓 크기 결정
|
||||||
|
* - 10종 절곡품 계산
|
||||||
|
* - 3종 부자재 계산
|
||||||
|
*
|
||||||
|
* @param string $finishedGoodsCode 완제품 코드
|
||||||
|
* @param array $inputVariables 입력 변수 (W0, H0, QTY 등)
|
||||||
|
* @param int $tenantId 테넌트 ID
|
||||||
|
* @return array 계산 결과
|
||||||
|
*/
|
||||||
|
private function calculateKyungdongBom(
|
||||||
|
string $finishedGoodsCode,
|
||||||
|
array $inputVariables,
|
||||||
|
int $tenantId
|
||||||
|
): array {
|
||||||
|
$this->addDebugStep(0, '경동전용계산', [
|
||||||
|
'tenant_id' => $tenantId,
|
||||||
|
'handler' => 'KyungdongFormulaHandler',
|
||||||
|
'finished_goods' => $finishedGoodsCode,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Step 1: 입력값 수집
|
||||||
|
$this->addDebugStep(1, '입력값수집', [
|
||||||
|
'W0' => $inputVariables['W0'] ?? null,
|
||||||
|
'H0' => $inputVariables['H0'] ?? null,
|
||||||
|
'QTY' => $inputVariables['QTY'] ?? 1,
|
||||||
|
'bracket_inch' => $inputVariables['bracket_inch'] ?? '5',
|
||||||
|
'product_type' => $inputVariables['product_type'] ?? 'screen',
|
||||||
|
'finishing_type' => $inputVariables['finishing_type'] ?? 'SUS',
|
||||||
|
'finished_goods' => $finishedGoodsCode,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Step 2: 완제품 조회
|
||||||
|
$finishedGoods = $this->getItemDetails($finishedGoodsCode, $tenantId);
|
||||||
|
|
||||||
|
if (! $finishedGoods) {
|
||||||
|
$this->addDebugStep(2, '완제품선택', [
|
||||||
|
'code' => $finishedGoodsCode,
|
||||||
|
'error' => '완제품을 찾을 수 없습니다.',
|
||||||
|
]);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'success' => false,
|
||||||
|
'error' => __('error.finished_goods_not_found', ['code' => $finishedGoodsCode]),
|
||||||
|
'debug_steps' => $this->debugSteps,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->addDebugStep(2, '완제품선택', [
|
||||||
|
'code' => $finishedGoods['code'],
|
||||||
|
'name' => $finishedGoods['name'],
|
||||||
|
'item_category' => $finishedGoods['item_category'] ?? 'N/A',
|
||||||
|
]);
|
||||||
|
|
||||||
|
// KyungdongFormulaHandler 인스턴스 생성
|
||||||
|
$handler = new KyungdongFormulaHandler;
|
||||||
|
|
||||||
|
// Step 3: 경동 전용 변수 계산
|
||||||
|
$W0 = (float) ($inputVariables['W0'] ?? 0);
|
||||||
|
$H0 = (float) ($inputVariables['H0'] ?? 0);
|
||||||
|
$QTY = (int) ($inputVariables['QTY'] ?? 1);
|
||||||
|
$bracketInch = $inputVariables['bracket_inch'] ?? '5';
|
||||||
|
$productType = $inputVariables['product_type'] ?? 'screen';
|
||||||
|
|
||||||
|
// 중량 계산 (5130 로직)
|
||||||
|
$area = ($W0 * ($H0 + 550)) / 1000000;
|
||||||
|
$weight = $area * ($productType === 'steel' ? 25 : 2) + ($W0 / 1000) * 14.17;
|
||||||
|
|
||||||
|
// 모터 용량 결정
|
||||||
|
$motorCapacity = $handler->calculateMotorCapacity($productType, $weight, $bracketInch);
|
||||||
|
|
||||||
|
// 브라켓 크기 결정
|
||||||
|
$bracketSize = $handler->calculateBracketSize($weight, $bracketInch);
|
||||||
|
|
||||||
|
$calculatedVariables = array_merge($inputVariables, [
|
||||||
|
'W0' => $W0,
|
||||||
|
'H0' => $H0,
|
||||||
|
'QTY' => $QTY,
|
||||||
|
'W1' => $W0 + 140,
|
||||||
|
'H1' => $H0 + 350,
|
||||||
|
'AREA' => round($area, 4),
|
||||||
|
'WEIGHT' => round($weight, 2),
|
||||||
|
'MOTOR_CAPACITY' => $motorCapacity,
|
||||||
|
'BRACKET_SIZE' => $bracketSize,
|
||||||
|
'bracket_inch' => $bracketInch,
|
||||||
|
'product_type' => $productType,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->addDebugStep(3, '변수계산', [
|
||||||
|
'W0' => $W0,
|
||||||
|
'H0' => $H0,
|
||||||
|
'area' => round($area, 4),
|
||||||
|
'weight' => round($weight, 2),
|
||||||
|
'motor_capacity' => $motorCapacity,
|
||||||
|
'bracket_size' => $bracketSize,
|
||||||
|
'calculation_type' => '경동기업 전용 공식',
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Step 4-7: 동적 항목 계산 (KyungdongFormulaHandler 사용)
|
||||||
|
$dynamicItems = $handler->calculateDynamicItems($calculatedVariables);
|
||||||
|
|
||||||
|
$this->addDebugStep(4, 'BOM전개', [
|
||||||
|
'total_items' => count($dynamicItems),
|
||||||
|
'item_categories' => array_unique(array_column($dynamicItems, 'category')),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Step 5-7: 단가 계산 (각 항목별)
|
||||||
|
$calculatedItems = [];
|
||||||
|
foreach ($dynamicItems as $item) {
|
||||||
|
$this->addDebugStep(6, '수량계산', [
|
||||||
|
'item_name' => $item['item_name'],
|
||||||
|
'quantity' => $item['quantity'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->addDebugStep(7, '금액계산', [
|
||||||
|
'item_name' => $item['item_name'],
|
||||||
|
'quantity' => $item['quantity'],
|
||||||
|
'unit_price' => $item['unit_price'],
|
||||||
|
'total_price' => $item['total_price'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$calculatedItems[] = [
|
||||||
|
'item_code' => $item['item_code'] ?? '',
|
||||||
|
'item_name' => $item['item_name'],
|
||||||
|
'item_category' => $item['category'],
|
||||||
|
'specification' => $item['specification'] ?? '',
|
||||||
|
'unit' => $item['unit'],
|
||||||
|
'quantity' => $item['quantity'],
|
||||||
|
'unit_price' => $item['unit_price'],
|
||||||
|
'total_price' => $item['total_price'],
|
||||||
|
'category_group' => $item['category'],
|
||||||
|
'calculation_note' => '경동기업 전용 계산',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 8: 카테고리별 그룹화
|
||||||
|
$groupedItems = [];
|
||||||
|
foreach ($calculatedItems as $item) {
|
||||||
|
$category = $item['category_group'];
|
||||||
|
if (! isset($groupedItems[$category])) {
|
||||||
|
$groupedItems[$category] = [
|
||||||
|
'name' => $this->getKyungdongCategoryName($category),
|
||||||
|
'items' => [],
|
||||||
|
'subtotal' => 0,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
$groupedItems[$category]['items'][] = $item;
|
||||||
|
$groupedItems[$category]['subtotal'] += $item['total_price'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->addDebugStep(8, '카테고리그룹화', [
|
||||||
|
'groups' => array_map(fn ($g) => [
|
||||||
|
'name' => $g['name'],
|
||||||
|
'count' => count($g['items']),
|
||||||
|
'subtotal' => $g['subtotal'],
|
||||||
|
], $groupedItems),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Step 9: 소계 계산
|
||||||
|
$subtotals = [];
|
||||||
|
foreach ($groupedItems as $category => $group) {
|
||||||
|
$subtotals[$category] = [
|
||||||
|
'name' => $group['name'],
|
||||||
|
'count' => count($group['items']),
|
||||||
|
'subtotal' => $group['subtotal'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->addDebugStep(9, '소계계산', $subtotals);
|
||||||
|
|
||||||
|
// Step 10: 최종 합계
|
||||||
|
$grandTotal = array_sum(array_column($calculatedItems, 'total_price'));
|
||||||
|
|
||||||
|
$this->addDebugStep(10, '최종합계', [
|
||||||
|
'item_count' => count($calculatedItems),
|
||||||
|
'grand_total' => $grandTotal,
|
||||||
|
'formatted' => number_format($grandTotal).'원',
|
||||||
|
]);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'success' => true,
|
||||||
|
'finished_goods' => $finishedGoods,
|
||||||
|
'variables' => $calculatedVariables,
|
||||||
|
'items' => $calculatedItems,
|
||||||
|
'grouped_items' => $groupedItems,
|
||||||
|
'subtotals' => $subtotals,
|
||||||
|
'grand_total' => $grandTotal,
|
||||||
|
'debug_steps' => $this->debugSteps,
|
||||||
|
'calculation_type' => 'kyungdong',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 경동기업 카테고리명 반환
|
||||||
|
*/
|
||||||
|
private function getKyungdongCategoryName(string $category): string
|
||||||
|
{
|
||||||
|
return match ($category) {
|
||||||
|
'material' => '주자재',
|
||||||
|
'motor' => '모터',
|
||||||
|
'controller' => '제어기',
|
||||||
|
'steel' => '절곡품',
|
||||||
|
'parts' => '부자재',
|
||||||
|
default => $category,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
773
app/Services/Quote/Handlers/KyungdongFormulaHandler.php
Normal file
773
app/Services/Quote/Handlers/KyungdongFormulaHandler.php
Normal file
@@ -0,0 +1,773 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Services\Quote\Handlers;
|
||||||
|
|
||||||
|
use App\Models\Kyungdong\KdPriceTable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 경동기업 전용 견적 계산 핸들러
|
||||||
|
*
|
||||||
|
* 5130 레거시 시스템의 견적 로직을 SAM에 구현
|
||||||
|
* tenant_id = 287 전용
|
||||||
|
*/
|
||||||
|
class KyungdongFormulaHandler
|
||||||
|
{
|
||||||
|
private const TENANT_ID = 287;
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// 모터 용량 계산
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 모터 용량 계산 (3차원 조건: 제품타입 × 인치 × 중량)
|
||||||
|
*
|
||||||
|
* @param string $productType 제품 타입 (screen, steel)
|
||||||
|
* @param float $weight 중량 (kg)
|
||||||
|
* @param string $bracketInch 브라켓 인치 (4, 5, 6, 8)
|
||||||
|
* @return string 모터 용량 (150K, 300K, 400K, 500K, 600K, 800K, 1000K)
|
||||||
|
*/
|
||||||
|
public function calculateMotorCapacity(string $productType, float $weight, string $bracketInch): string
|
||||||
|
{
|
||||||
|
$inch = (int) $bracketInch;
|
||||||
|
|
||||||
|
if ($productType === 'screen') {
|
||||||
|
return $this->calculateScreenMotor($weight, $inch);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->calculateSteelMotor($weight, $inch);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 스크린 모터 용량 계산
|
||||||
|
*/
|
||||||
|
private function calculateScreenMotor(float $weight, int $inch): string
|
||||||
|
{
|
||||||
|
if ($inch === 4) {
|
||||||
|
if ($weight <= 150) {
|
||||||
|
return '150K';
|
||||||
|
}
|
||||||
|
if ($weight <= 300) {
|
||||||
|
return '300K';
|
||||||
|
}
|
||||||
|
|
||||||
|
return '400K';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($inch === 5) {
|
||||||
|
if ($weight <= 123) {
|
||||||
|
return '150K';
|
||||||
|
}
|
||||||
|
if ($weight <= 246) {
|
||||||
|
return '300K';
|
||||||
|
}
|
||||||
|
if ($weight <= 327) {
|
||||||
|
return '400K';
|
||||||
|
}
|
||||||
|
if ($weight <= 500) {
|
||||||
|
return '500K';
|
||||||
|
}
|
||||||
|
|
||||||
|
return '600K';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($inch === 6) {
|
||||||
|
if ($weight <= 104) {
|
||||||
|
return '150K';
|
||||||
|
}
|
||||||
|
if ($weight <= 208) {
|
||||||
|
return '300K';
|
||||||
|
}
|
||||||
|
if ($weight <= 300) {
|
||||||
|
return '400K';
|
||||||
|
}
|
||||||
|
if ($weight <= 424) {
|
||||||
|
return '500K';
|
||||||
|
}
|
||||||
|
|
||||||
|
return '600K';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 기본값
|
||||||
|
return '300K';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 철재 모터 용량 계산
|
||||||
|
*/
|
||||||
|
private function calculateSteelMotor(float $weight, int $inch): string
|
||||||
|
{
|
||||||
|
if ($inch === 4) {
|
||||||
|
if ($weight <= 300) {
|
||||||
|
return '300K';
|
||||||
|
}
|
||||||
|
|
||||||
|
return '400K';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($inch === 5) {
|
||||||
|
if ($weight <= 246) {
|
||||||
|
return '300K';
|
||||||
|
}
|
||||||
|
if ($weight <= 327) {
|
||||||
|
return '400K';
|
||||||
|
}
|
||||||
|
if ($weight <= 500) {
|
||||||
|
return '500K';
|
||||||
|
}
|
||||||
|
|
||||||
|
return '600K';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($inch === 6) {
|
||||||
|
if ($weight <= 208) {
|
||||||
|
return '300K';
|
||||||
|
}
|
||||||
|
if ($weight <= 277) {
|
||||||
|
return '400K';
|
||||||
|
}
|
||||||
|
if ($weight <= 424) {
|
||||||
|
return '500K';
|
||||||
|
}
|
||||||
|
if ($weight <= 508) {
|
||||||
|
return '600K';
|
||||||
|
}
|
||||||
|
if ($weight <= 800) {
|
||||||
|
return '800K';
|
||||||
|
}
|
||||||
|
|
||||||
|
return '1000K';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($inch === 8) {
|
||||||
|
if ($weight <= 324) {
|
||||||
|
return '500K';
|
||||||
|
}
|
||||||
|
if ($weight <= 388) {
|
||||||
|
return '600K';
|
||||||
|
}
|
||||||
|
if ($weight <= 611) {
|
||||||
|
return '800K';
|
||||||
|
}
|
||||||
|
|
||||||
|
return '1000K';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 기본값
|
||||||
|
return '300K';
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// 브라켓 크기 계산
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 브라켓 크기 결정
|
||||||
|
*
|
||||||
|
* @param float $weight 중량 (kg)
|
||||||
|
* @param string|null $bracketInch 브라켓 인치 (선택)
|
||||||
|
* @return string 브라켓 크기 (530*320, 600*350, 690*390)
|
||||||
|
*/
|
||||||
|
public function calculateBracketSize(float $weight, ?string $bracketInch = null): string
|
||||||
|
{
|
||||||
|
$motorCapacity = $this->getMotorCapacityByWeight($weight, $bracketInch);
|
||||||
|
|
||||||
|
return match ($motorCapacity) {
|
||||||
|
'300K', '400K' => '530*320',
|
||||||
|
'500K', '600K' => '600*350',
|
||||||
|
'800K', '1000K' => '690*390',
|
||||||
|
default => '530*320',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 중량으로 모터 용량 판단 (인치 없을 때)
|
||||||
|
*/
|
||||||
|
private function getMotorCapacityByWeight(float $weight, ?string $bracketInch = null): string
|
||||||
|
{
|
||||||
|
if ($bracketInch) {
|
||||||
|
// 인치가 있으면 철재 기준으로 계산
|
||||||
|
return $this->calculateSteelMotor($weight, (int) $bracketInch);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 인치 없으면 중량만으로 판단
|
||||||
|
if ($weight <= 300) {
|
||||||
|
return '300K';
|
||||||
|
}
|
||||||
|
if ($weight <= 400) {
|
||||||
|
return '400K';
|
||||||
|
}
|
||||||
|
if ($weight <= 500) {
|
||||||
|
return '500K';
|
||||||
|
}
|
||||||
|
if ($weight <= 600) {
|
||||||
|
return '600K';
|
||||||
|
}
|
||||||
|
if ($weight <= 800) {
|
||||||
|
return '800K';
|
||||||
|
}
|
||||||
|
|
||||||
|
return '1000K';
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// 주자재(스크린) 계산
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 스크린 주자재 가격 계산
|
||||||
|
*
|
||||||
|
* @param float $width 폭 (mm)
|
||||||
|
* @param float $height 높이 (mm)
|
||||||
|
* @return array [unit_price, area, total_price]
|
||||||
|
*/
|
||||||
|
public function calculateScreenPrice(float $width, float $height): array
|
||||||
|
{
|
||||||
|
// 면적 계산: W × (H + 550) / 1,000,000
|
||||||
|
$calculateHeight = $height + 550;
|
||||||
|
$area = ($width * $calculateHeight) / 1000000;
|
||||||
|
|
||||||
|
// 원자재 단가 조회 (실리카/스크린)
|
||||||
|
$unitPrice = $this->getRawMaterialPrice('실리카');
|
||||||
|
|
||||||
|
return [
|
||||||
|
'unit_price' => $unitPrice,
|
||||||
|
'area' => round($area, 2),
|
||||||
|
'total_price' => round($unitPrice * $area),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// 단가 조회 메서드 (KdPriceTable 사용)
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BDmodels 테이블에서 단가 조회
|
||||||
|
*
|
||||||
|
* @param string $modelName 모델코드 (KSS01, KWS01 등)
|
||||||
|
* @param string $secondItem 부품분류 (케이스, 가이드레일, 하단마감재, L-BAR 등)
|
||||||
|
* @param string|null $finishingType 마감재질 (SUS, EGI)
|
||||||
|
* @param string|null $spec 규격 (120*70, 650*550 등)
|
||||||
|
* @return float 단가
|
||||||
|
*/
|
||||||
|
public function getBDModelPrice(
|
||||||
|
string $modelName,
|
||||||
|
string $secondItem,
|
||||||
|
?string $finishingType = null,
|
||||||
|
?string $spec = null
|
||||||
|
): float {
|
||||||
|
// BDmodels는 복잡한 구조이므로 items 테이블의 기존 데이터 활용
|
||||||
|
// TODO: 필요시 kd_price_tables TYPE_BDMODELS 추가
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* price_* 테이블에서 단가 조회 (모터, 샤프트, 파이프, 앵글)
|
||||||
|
*
|
||||||
|
* @param string $tableName 테이블명 (motor, shaft, pipe, angle)
|
||||||
|
* @param array $conditions 조회 조건
|
||||||
|
* @return float 단가
|
||||||
|
*/
|
||||||
|
public function getPriceFromTable(string $tableName, array $conditions): float
|
||||||
|
{
|
||||||
|
$query = KdPriceTable::where('table_type', $tableName)->active();
|
||||||
|
|
||||||
|
foreach ($conditions as $field => $value) {
|
||||||
|
$query->where($field, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
$record = $query->first();
|
||||||
|
|
||||||
|
return (float) ($record?->unit_price ?? 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 원자재 단가 조회
|
||||||
|
*
|
||||||
|
* @param string $materialName 원자재명 (실리카, 스크린 등)
|
||||||
|
* @return float 단가
|
||||||
|
*/
|
||||||
|
public function getRawMaterialPrice(string $materialName): float
|
||||||
|
{
|
||||||
|
return KdPriceTable::getRawMaterialPrice($materialName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 모터 단가 조회
|
||||||
|
*
|
||||||
|
* @param string $motorCapacity 모터 용량 (150K, 300K 등)
|
||||||
|
* @return float 단가
|
||||||
|
*/
|
||||||
|
public function getMotorPrice(string $motorCapacity): float
|
||||||
|
{
|
||||||
|
return KdPriceTable::getMotorPrice($motorCapacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 제어기 단가 조회
|
||||||
|
*
|
||||||
|
* @param string $controllerType 제어기 타입 (매립형, 노출형, 뒷박스)
|
||||||
|
* @return float 단가
|
||||||
|
*/
|
||||||
|
public function getControllerPrice(string $controllerType): float
|
||||||
|
{
|
||||||
|
return KdPriceTable::getControllerPrice($controllerType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 샤프트 단가 조회
|
||||||
|
*
|
||||||
|
* @param string $size 사이즈 (3, 4, 5인치)
|
||||||
|
* @param float $length 길이 (m 단위)
|
||||||
|
* @return float 단가
|
||||||
|
*/
|
||||||
|
public function getShaftPrice(string $size, float $length): float
|
||||||
|
{
|
||||||
|
return KdPriceTable::getShaftPrice($size, $length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 파이프 단가 조회
|
||||||
|
*
|
||||||
|
* @param string $thickness 두께 (1.4 등)
|
||||||
|
* @param int $length 길이 (3000, 6000)
|
||||||
|
* @return float 단가
|
||||||
|
*/
|
||||||
|
public function getPipePrice(string $thickness, int $length): float
|
||||||
|
{
|
||||||
|
return KdPriceTable::getPipePrice($thickness, $length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 앵글 단가 조회
|
||||||
|
*
|
||||||
|
* @param string $type 타입 (스크린용, 철재용)
|
||||||
|
* @param string $bracketSize 브라켓크기 (530*320, 600*350, 690*390)
|
||||||
|
* @param string $angleType 앵글타입 (앵글3T, 앵글4T)
|
||||||
|
* @return float 단가
|
||||||
|
*/
|
||||||
|
public function getAnglePrice(string $type, string $bracketSize, string $angleType): float
|
||||||
|
{
|
||||||
|
return KdPriceTable::getAnglePrice($type, $bracketSize, $angleType);
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// 절곡품 계산 (10종)
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 절곡품 항목 계산 (10종)
|
||||||
|
*
|
||||||
|
* 케이스, 케이스용 연기차단재, 케이스 마구리, 가이드레일,
|
||||||
|
* 레일용 연기차단재, 하장바, L바, 보강평철, 무게평철12T, 환봉
|
||||||
|
*
|
||||||
|
* @param array $params 입력 파라미터
|
||||||
|
* @return array 절곡품 항목 배열
|
||||||
|
*/
|
||||||
|
public function calculateSteelItems(array $params): array
|
||||||
|
{
|
||||||
|
$items = [];
|
||||||
|
|
||||||
|
// 기본 파라미터
|
||||||
|
$width = (float) ($params['W0'] ?? 0);
|
||||||
|
$height = (float) ($params['H0'] ?? 0);
|
||||||
|
$quantity = (int) ($params['QTY'] ?? 1);
|
||||||
|
$modelName = $params['model_name'] ?? 'KSS01';
|
||||||
|
$finishingType = $params['finishing_type'] ?? 'SUS';
|
||||||
|
|
||||||
|
// 절곡품 관련 파라미터
|
||||||
|
$caseSpec = $params['case_spec'] ?? '500*380';
|
||||||
|
$caseLength = (float) ($params['case_length'] ?? $width); // mm 단위
|
||||||
|
$guideType = $params['guide_type'] ?? '벽면형'; // 벽면형, 측면형, 혼합형
|
||||||
|
$guideSpec = $params['guide_spec'] ?? '120*70'; // 120*70, 120*100
|
||||||
|
$guideLength = (float) ($params['guide_length'] ?? ($height + 550)) / 1000; // m 단위
|
||||||
|
$bottomBarLength = (float) ($params['bottombar_length'] ?? $width) / 1000; // m 단위
|
||||||
|
$lbarLength = (float) ($params['lbar_length'] ?? $width) / 1000; // m 단위
|
||||||
|
$flatBarLength = (float) ($params['flatbar_length'] ?? $width) / 1000; // m 단위
|
||||||
|
$weightPlateQty = (int) ($params['weight_plate_qty'] ?? 0); // 무게평철 수량
|
||||||
|
$roundBarQty = (int) ($params['round_bar_qty'] ?? 0); // 환봉 수량
|
||||||
|
|
||||||
|
// 1. 케이스 (단가/1000 × 길이mm × 수량)
|
||||||
|
$casePrice = KdPriceTable::getCasePrice($caseSpec);
|
||||||
|
if ($casePrice > 0 && $caseLength > 0) {
|
||||||
|
$totalPrice = ($casePrice / 1000) * $caseLength * $quantity;
|
||||||
|
$items[] = [
|
||||||
|
'category' => 'steel',
|
||||||
|
'item_name' => '케이스',
|
||||||
|
'specification' => "{$caseSpec} {$caseLength}mm",
|
||||||
|
'unit' => 'm',
|
||||||
|
'quantity' => $caseLength / 1000 * $quantity,
|
||||||
|
'unit_price' => $casePrice,
|
||||||
|
'total_price' => round($totalPrice),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 케이스용 연기차단재 (단가 × 길이m × 수량)
|
||||||
|
$caseSmokePrice = KdPriceTable::getCaseSmokeBlockPrice();
|
||||||
|
if ($caseSmokePrice > 0 && $caseLength > 0) {
|
||||||
|
$lengthM = $caseLength / 1000;
|
||||||
|
$items[] = [
|
||||||
|
'category' => 'steel',
|
||||||
|
'item_name' => '케이스용 연기차단재',
|
||||||
|
'specification' => "{$lengthM}m",
|
||||||
|
'unit' => 'm',
|
||||||
|
'quantity' => $lengthM * $quantity,
|
||||||
|
'unit_price' => $caseSmokePrice,
|
||||||
|
'total_price' => round($caseSmokePrice * $lengthM * $quantity),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 케이스 마구리 (단가 × 수량)
|
||||||
|
$caseCapPrice = KdPriceTable::getCaseCapPrice($caseSpec);
|
||||||
|
if ($caseCapPrice > 0) {
|
||||||
|
$capQty = 2 * $quantity; // 좌우 2개
|
||||||
|
$items[] = [
|
||||||
|
'category' => 'steel',
|
||||||
|
'item_name' => '케이스 마구리',
|
||||||
|
'specification' => $caseSpec,
|
||||||
|
'unit' => 'EA',
|
||||||
|
'quantity' => $capQty,
|
||||||
|
'unit_price' => $caseCapPrice,
|
||||||
|
'total_price' => round($caseCapPrice * $capQty),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 가이드레일 (단가 × 길이m × 수량) - 타입별 처리
|
||||||
|
$guideItems = $this->calculateGuideRails($modelName, $finishingType, $guideType, $guideSpec, $guideLength, $quantity);
|
||||||
|
$items = array_merge($items, $guideItems);
|
||||||
|
|
||||||
|
// 5. 레일용 연기차단재 (단가 × 길이m × 2 × 수량)
|
||||||
|
$railSmokePrice = KdPriceTable::getRailSmokeBlockPrice();
|
||||||
|
if ($railSmokePrice > 0 && $guideLength > 0) {
|
||||||
|
$railSmokeQty = 2 * $quantity; // 좌우 2개
|
||||||
|
$items[] = [
|
||||||
|
'category' => 'steel',
|
||||||
|
'item_name' => '레일용 연기차단재',
|
||||||
|
'specification' => "{$guideLength}m × 2",
|
||||||
|
'unit' => 'm',
|
||||||
|
'quantity' => $guideLength * $railSmokeQty,
|
||||||
|
'unit_price' => $railSmokePrice,
|
||||||
|
'total_price' => round($railSmokePrice * $guideLength * $railSmokeQty),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. 하장바 (단가 × 길이m × 수량)
|
||||||
|
$bottomBarPrice = KdPriceTable::getBottomBarPrice($modelName, $finishingType);
|
||||||
|
if ($bottomBarPrice > 0 && $bottomBarLength > 0) {
|
||||||
|
$items[] = [
|
||||||
|
'category' => 'steel',
|
||||||
|
'item_name' => '하장바',
|
||||||
|
'specification' => "{$modelName} {$finishingType} {$bottomBarLength}m",
|
||||||
|
'unit' => 'm',
|
||||||
|
'quantity' => $bottomBarLength * $quantity,
|
||||||
|
'unit_price' => $bottomBarPrice,
|
||||||
|
'total_price' => round($bottomBarPrice * $bottomBarLength * $quantity),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. L바 (단가 × 길이m × 수량)
|
||||||
|
$lbarPrice = KdPriceTable::getLBarPrice($modelName);
|
||||||
|
if ($lbarPrice > 0 && $lbarLength > 0) {
|
||||||
|
$items[] = [
|
||||||
|
'category' => 'steel',
|
||||||
|
'item_name' => 'L바',
|
||||||
|
'specification' => "{$modelName} {$lbarLength}m",
|
||||||
|
'unit' => 'm',
|
||||||
|
'quantity' => $lbarLength * $quantity,
|
||||||
|
'unit_price' => $lbarPrice,
|
||||||
|
'total_price' => round($lbarPrice * $lbarLength * $quantity),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8. 보강평철 (단가 × 길이m × 수량)
|
||||||
|
$flatBarPrice = KdPriceTable::getFlatBarPrice();
|
||||||
|
if ($flatBarPrice > 0 && $flatBarLength > 0) {
|
||||||
|
$items[] = [
|
||||||
|
'category' => 'steel',
|
||||||
|
'item_name' => '보강평철',
|
||||||
|
'specification' => "{$flatBarLength}m",
|
||||||
|
'unit' => 'm',
|
||||||
|
'quantity' => $flatBarLength * $quantity,
|
||||||
|
'unit_price' => $flatBarPrice,
|
||||||
|
'total_price' => round($flatBarPrice * $flatBarLength * $quantity),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 9. 무게평철12T (고정 12,000원 × 수량)
|
||||||
|
if ($weightPlateQty > 0) {
|
||||||
|
$weightPlatePrice = 12000;
|
||||||
|
$items[] = [
|
||||||
|
'category' => 'steel',
|
||||||
|
'item_name' => '무게평철12T',
|
||||||
|
'specification' => '12T',
|
||||||
|
'unit' => 'EA',
|
||||||
|
'quantity' => $weightPlateQty * $quantity,
|
||||||
|
'unit_price' => $weightPlatePrice,
|
||||||
|
'total_price' => $weightPlatePrice * $weightPlateQty * $quantity,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 10. 환봉 (고정 2,000원 × 수량)
|
||||||
|
if ($roundBarQty > 0) {
|
||||||
|
$roundBarPrice = 2000;
|
||||||
|
$items[] = [
|
||||||
|
'category' => 'steel',
|
||||||
|
'item_name' => '환봉',
|
||||||
|
'specification' => '',
|
||||||
|
'unit' => 'EA',
|
||||||
|
'quantity' => $roundBarQty * $quantity,
|
||||||
|
'unit_price' => $roundBarPrice,
|
||||||
|
'total_price' => $roundBarPrice * $roundBarQty * $quantity,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $items;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 가이드레일 계산 (타입별 처리)
|
||||||
|
*
|
||||||
|
* @param string $modelName 모델코드
|
||||||
|
* @param string $finishingType 마감재질
|
||||||
|
* @param string $guideType 가이드레일 타입 (벽면형, 측면형, 혼합형)
|
||||||
|
* @param string $guideSpec 가이드레일 규격 (120*70, 120*100)
|
||||||
|
* @param float $guideLength 가이드레일 길이 (m)
|
||||||
|
* @param int $quantity 수량
|
||||||
|
* @return array 가이드레일 항목 배열
|
||||||
|
*/
|
||||||
|
private function calculateGuideRails(
|
||||||
|
string $modelName,
|
||||||
|
string $finishingType,
|
||||||
|
string $guideType,
|
||||||
|
string $guideSpec,
|
||||||
|
float $guideLength,
|
||||||
|
int $quantity
|
||||||
|
): array {
|
||||||
|
$items = [];
|
||||||
|
|
||||||
|
if ($guideLength <= 0) {
|
||||||
|
return $items;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($guideType) {
|
||||||
|
case '벽면형':
|
||||||
|
// 120*70 × 2개
|
||||||
|
$price = KdPriceTable::getGuideRailPrice($modelName, $finishingType, '120*70');
|
||||||
|
if ($price > 0) {
|
||||||
|
$guideQty = 2 * $quantity;
|
||||||
|
$items[] = [
|
||||||
|
'category' => 'steel',
|
||||||
|
'item_name' => '가이드레일',
|
||||||
|
'specification' => "{$modelName} {$finishingType} 120*70 {$guideLength}m × 2",
|
||||||
|
'unit' => 'm',
|
||||||
|
'quantity' => $guideLength * $guideQty,
|
||||||
|
'unit_price' => $price,
|
||||||
|
'total_price' => round($price * $guideLength * $guideQty),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '측면형':
|
||||||
|
// 120*100 × 2개
|
||||||
|
$price = KdPriceTable::getGuideRailPrice($modelName, $finishingType, '120*100');
|
||||||
|
if ($price > 0) {
|
||||||
|
$guideQty = 2 * $quantity;
|
||||||
|
$items[] = [
|
||||||
|
'category' => 'steel',
|
||||||
|
'item_name' => '가이드레일',
|
||||||
|
'specification' => "{$modelName} {$finishingType} 120*100 {$guideLength}m × 2",
|
||||||
|
'unit' => 'm',
|
||||||
|
'quantity' => $guideLength * $guideQty,
|
||||||
|
'unit_price' => $price,
|
||||||
|
'total_price' => round($price * $guideLength * $guideQty),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '혼합형':
|
||||||
|
// 120*70 × 1개 + 120*100 × 1개
|
||||||
|
$price70 = KdPriceTable::getGuideRailPrice($modelName, $finishingType, '120*70');
|
||||||
|
$price100 = KdPriceTable::getGuideRailPrice($modelName, $finishingType, '120*100');
|
||||||
|
|
||||||
|
if ($price70 > 0) {
|
||||||
|
$items[] = [
|
||||||
|
'category' => 'steel',
|
||||||
|
'item_name' => '가이드레일',
|
||||||
|
'specification' => "{$modelName} {$finishingType} 120*70 {$guideLength}m",
|
||||||
|
'unit' => 'm',
|
||||||
|
'quantity' => $guideLength * $quantity,
|
||||||
|
'unit_price' => $price70,
|
||||||
|
'total_price' => round($price70 * $guideLength * $quantity),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
if ($price100 > 0) {
|
||||||
|
$items[] = [
|
||||||
|
'category' => 'steel',
|
||||||
|
'item_name' => '가이드레일',
|
||||||
|
'specification' => "{$modelName} {$finishingType} 120*100 {$guideLength}m",
|
||||||
|
'unit' => 'm',
|
||||||
|
'quantity' => $guideLength * $quantity,
|
||||||
|
'unit_price' => $price100,
|
||||||
|
'total_price' => round($price100 * $guideLength * $quantity),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $items;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// 부자재 계산 (3종)
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 부자재 항목 계산
|
||||||
|
*
|
||||||
|
* @param array $params 입력 파라미터
|
||||||
|
* @return array 부자재 항목 배열
|
||||||
|
*/
|
||||||
|
public function calculatePartItems(array $params): array
|
||||||
|
{
|
||||||
|
$items = [];
|
||||||
|
|
||||||
|
$width = (float) ($params['W0'] ?? 0);
|
||||||
|
$bracketInch = $params['bracket_inch'] ?? '5';
|
||||||
|
$bracketSize = $params['BRACKET_SIZE'] ?? $this->calculateBracketSize(100, $bracketInch);
|
||||||
|
$productType = $params['product_type'] ?? 'screen';
|
||||||
|
$quantity = (int) ($params['QTY'] ?? 1);
|
||||||
|
|
||||||
|
// 1. 감기샤프트
|
||||||
|
$shaftSize = $bracketInch;
|
||||||
|
$shaftLength = ceil($width / 1000); // mm → m 변환 후 올림
|
||||||
|
$shaftPrice = $this->getShaftPrice($shaftSize, $shaftLength);
|
||||||
|
if ($shaftPrice > 0) {
|
||||||
|
$items[] = [
|
||||||
|
'category' => 'parts',
|
||||||
|
'item_name' => "감기샤프트 {$shaftSize}인치",
|
||||||
|
'specification' => "{$shaftLength}m",
|
||||||
|
'unit' => 'EA',
|
||||||
|
'quantity' => $quantity,
|
||||||
|
'unit_price' => $shaftPrice,
|
||||||
|
'total_price' => $shaftPrice * $quantity,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 각파이프
|
||||||
|
$pipeThickness = '1.4';
|
||||||
|
$pipeLength = $width > 3000 ? 6000 : 3000;
|
||||||
|
$pipePrice = $this->getPipePrice($pipeThickness, $pipeLength);
|
||||||
|
if ($pipePrice > 0) {
|
||||||
|
$items[] = [
|
||||||
|
'category' => 'parts',
|
||||||
|
'item_name' => '각파이프',
|
||||||
|
'specification' => "{$pipeThickness}T {$pipeLength}mm",
|
||||||
|
'unit' => 'EA',
|
||||||
|
'quantity' => $quantity,
|
||||||
|
'unit_price' => $pipePrice,
|
||||||
|
'total_price' => $pipePrice * $quantity,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 앵글
|
||||||
|
$angleType = $productType === 'steel' ? '철재용' : '스크린용';
|
||||||
|
$angleSpec = $bracketSize === '690*390' ? '앵글4T' : '앵글3T';
|
||||||
|
$anglePrice = $this->getAnglePrice($angleType, $bracketSize, $angleSpec);
|
||||||
|
if ($anglePrice > 0) {
|
||||||
|
$items[] = [
|
||||||
|
'category' => 'parts',
|
||||||
|
'item_name' => "앵글 {$angleSpec}",
|
||||||
|
'specification' => "{$angleType} {$bracketSize}",
|
||||||
|
'unit' => 'EA',
|
||||||
|
'quantity' => 2 * $quantity, // 좌우 2개
|
||||||
|
'unit_price' => $anglePrice,
|
||||||
|
'total_price' => $anglePrice * 2 * $quantity,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $items;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// 전체 동적 항목 계산
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 동적 항목 전체 계산
|
||||||
|
*
|
||||||
|
* @param array $inputs 입력 파라미터
|
||||||
|
* @return array 계산된 항목 배열
|
||||||
|
*/
|
||||||
|
public function calculateDynamicItems(array $inputs): array
|
||||||
|
{
|
||||||
|
$items = [];
|
||||||
|
|
||||||
|
$width = (float) ($inputs['W0'] ?? 0);
|
||||||
|
$height = (float) ($inputs['H0'] ?? 0);
|
||||||
|
$quantity = (int) ($inputs['QTY'] ?? 1);
|
||||||
|
$bracketInch = $inputs['bracket_inch'] ?? '5';
|
||||||
|
$productType = $inputs['product_type'] ?? 'screen';
|
||||||
|
|
||||||
|
// 중량 계산 (5130 로직)
|
||||||
|
$area = ($width * ($height + 550)) / 1000000;
|
||||||
|
$weight = $area * ($productType === 'steel' ? 25 : 2) + ($width / 1000) * 14.17;
|
||||||
|
|
||||||
|
// 모터 용량/브라켓 크기 계산
|
||||||
|
$motorCapacity = $this->calculateMotorCapacity($productType, $weight, $bracketInch);
|
||||||
|
$bracketSize = $this->calculateBracketSize($weight, $bracketInch);
|
||||||
|
|
||||||
|
// 입력값에 계산된 값 추가 (부자재 계산용)
|
||||||
|
$inputs['WEIGHT'] = $weight;
|
||||||
|
$inputs['MOTOR_CAPACITY'] = $motorCapacity;
|
||||||
|
$inputs['BRACKET_SIZE'] = $bracketSize;
|
||||||
|
|
||||||
|
// 1. 주자재 (스크린)
|
||||||
|
$screenResult = $this->calculateScreenPrice($width, $height);
|
||||||
|
$items[] = [
|
||||||
|
'category' => 'material',
|
||||||
|
'item_code' => 'KD-SCREEN',
|
||||||
|
'item_name' => '주자재(스크린)',
|
||||||
|
'specification' => "면적 {$screenResult['area']}㎡",
|
||||||
|
'unit' => '㎡',
|
||||||
|
'quantity' => $screenResult['area'] * $quantity,
|
||||||
|
'unit_price' => $screenResult['unit_price'],
|
||||||
|
'total_price' => $screenResult['total_price'] * $quantity,
|
||||||
|
];
|
||||||
|
|
||||||
|
// 2. 모터
|
||||||
|
$motorPrice = $this->getMotorPrice($motorCapacity);
|
||||||
|
$items[] = [
|
||||||
|
'category' => 'motor',
|
||||||
|
'item_code' => "KD-MOTOR-{$motorCapacity}",
|
||||||
|
'item_name' => "모터 {$motorCapacity}",
|
||||||
|
'specification' => $motorCapacity,
|
||||||
|
'unit' => 'EA',
|
||||||
|
'quantity' => $quantity,
|
||||||
|
'unit_price' => $motorPrice,
|
||||||
|
'total_price' => $motorPrice * $quantity,
|
||||||
|
];
|
||||||
|
|
||||||
|
// 3. 제어기
|
||||||
|
$controllerType = $inputs['controller_type'] ?? '매립형';
|
||||||
|
$controllerPrice = $this->getControllerPrice($controllerType);
|
||||||
|
$items[] = [
|
||||||
|
'category' => 'controller',
|
||||||
|
'item_code' => 'KD-CTRL-'.strtoupper($controllerType),
|
||||||
|
'item_name' => "제어기 {$controllerType}",
|
||||||
|
'specification' => $controllerType,
|
||||||
|
'unit' => 'EA',
|
||||||
|
'quantity' => $quantity,
|
||||||
|
'unit_price' => $controllerPrice,
|
||||||
|
'total_price' => $controllerPrice * $quantity,
|
||||||
|
];
|
||||||
|
|
||||||
|
// 4. 절곡품
|
||||||
|
$steelItems = $this->calculateSteelItems($inputs);
|
||||||
|
$items = array_merge($items, $steelItems);
|
||||||
|
|
||||||
|
// 5. 부자재
|
||||||
|
$partItems = $this->calculatePartItems($inputs);
|
||||||
|
$items = array_merge($items, $partItems);
|
||||||
|
|
||||||
|
return $items;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 경동기업 전용 단가 테이블
|
||||||
|
*
|
||||||
|
* 5130 레거시 시스템의 price_* 테이블 데이터를 저장
|
||||||
|
* - price_motor: 모터/제어기 단가
|
||||||
|
* - price_shaft: 샤프트 계산 참조
|
||||||
|
* - price_pipe: 파이프 계산 참조
|
||||||
|
* - price_angle: 앵글 계산 참조
|
||||||
|
* - price_raw_materials: 원자재 단가
|
||||||
|
*/
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('kd_price_tables', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->unsignedBigInteger('tenant_id')->default(287)->comment('경동기업 테넌트 ID');
|
||||||
|
$table->string('table_type', 50)->comment('테이블 유형: motor, shaft, pipe, angle, raw_material, bdmodels');
|
||||||
|
$table->string('item_code', 100)->nullable()->comment('품목 코드 (연동용)');
|
||||||
|
$table->string('item_name', 200)->nullable()->comment('품목명');
|
||||||
|
|
||||||
|
// 조회 조건 필드들
|
||||||
|
$table->string('category', 100)->nullable()->comment('분류 (모터용량, 재질, 타입 등)');
|
||||||
|
$table->string('spec1', 100)->nullable()->comment('규격1 (사이즈, 두께 등)');
|
||||||
|
$table->string('spec2', 100)->nullable()->comment('규격2 (길이, 브라켓크기 등)');
|
||||||
|
$table->string('spec3', 100)->nullable()->comment('규격3 (추가 조건)');
|
||||||
|
|
||||||
|
// 단가 정보
|
||||||
|
$table->decimal('unit_price', 15, 2)->default(0)->comment('단가');
|
||||||
|
$table->string('unit', 20)->default('EA')->comment('단위');
|
||||||
|
|
||||||
|
// 원본 JSON 데이터 (레거시 호환용)
|
||||||
|
$table->json('raw_data')->nullable()->comment('원본 JSON 데이터');
|
||||||
|
|
||||||
|
// 메타 정보
|
||||||
|
$table->boolean('is_active')->default(true)->comment('활성 여부');
|
||||||
|
$table->timestamps();
|
||||||
|
|
||||||
|
// 인덱스
|
||||||
|
$table->index(['tenant_id', 'table_type']);
|
||||||
|
$table->index(['table_type', 'category']);
|
||||||
|
$table->index(['table_type', 'spec1', 'spec2']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('kd_price_tables');
|
||||||
|
}
|
||||||
|
};
|
||||||
565
database/seeders/Kyungdong/KdPriceTableSeeder.php
Normal file
565
database/seeders/Kyungdong/KdPriceTableSeeder.php
Normal file
@@ -0,0 +1,565 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Seeders\Kyungdong;
|
||||||
|
|
||||||
|
use App\Models\Kyungdong\KdPriceTable;
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 경동기업 전용 단가 테이블 Seeder
|
||||||
|
*
|
||||||
|
* 5130 레거시 시스템의 price_* 테이블 데이터를 kd_price_tables로 마이그레이션
|
||||||
|
*
|
||||||
|
* 실행: php artisan db:seed --class="Database\Seeders\Kyungdong\KdPriceTableSeeder"
|
||||||
|
*/
|
||||||
|
class KdPriceTableSeeder extends Seeder
|
||||||
|
{
|
||||||
|
private const TENANT_ID = 287;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the database seeds.
|
||||||
|
*/
|
||||||
|
public function run(): void
|
||||||
|
{
|
||||||
|
$this->command->info('');
|
||||||
|
$this->command->info('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||||||
|
$this->command->info('🔧 경동기업 단가 테이블 마이그레이션 (kd_price_tables)');
|
||||||
|
$this->command->info('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||||||
|
|
||||||
|
// 기존 데이터 삭제
|
||||||
|
DB::table('kd_price_tables')->where('tenant_id', self::TENANT_ID)->delete();
|
||||||
|
$this->command->info(' → 기존 데이터 삭제 완료');
|
||||||
|
|
||||||
|
// chandj 연결 가능 여부 확인
|
||||||
|
$chandjAvailable = $this->checkChandjConnection();
|
||||||
|
|
||||||
|
if ($chandjAvailable) {
|
||||||
|
$this->command->info(' → chandj 데이터베이스 연결됨');
|
||||||
|
$this->migrateFromChandj();
|
||||||
|
} else {
|
||||||
|
$this->command->warn(' → chandj 데이터베이스 연결 불가 - 샘플 데이터 사용');
|
||||||
|
$this->insertSampleData();
|
||||||
|
}
|
||||||
|
|
||||||
|
$count = DB::table('kd_price_tables')->where('tenant_id', self::TENANT_ID)->count();
|
||||||
|
$this->command->info('');
|
||||||
|
$this->command->info("✅ 완료: kd_price_tables {$count}건");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* chandj 데이터베이스 연결 확인
|
||||||
|
*/
|
||||||
|
private function checkChandjConnection(): bool
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
DB::connection('chandj')->getPdo();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* chandj 데이터베이스에서 마이그레이션
|
||||||
|
*/
|
||||||
|
private function migrateFromChandj(): void
|
||||||
|
{
|
||||||
|
$this->migrateMotorPrices();
|
||||||
|
$this->migrateShaftPrices();
|
||||||
|
$this->migratePipePrices();
|
||||||
|
$this->migrateAnglePrices();
|
||||||
|
$this->migrateRawMaterialPrices();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* price_motor → kd_price_tables
|
||||||
|
*/
|
||||||
|
private function migrateMotorPrices(): void
|
||||||
|
{
|
||||||
|
$this->command->info('');
|
||||||
|
$this->command->info('📦 [1/5] price_motor 마이그레이션...');
|
||||||
|
|
||||||
|
$priceMotor = DB::connection('chandj')
|
||||||
|
->table('price_motor')
|
||||||
|
->where(function ($q) {
|
||||||
|
$q->where('is_deleted', 0)->orWhereNull('is_deleted');
|
||||||
|
})
|
||||||
|
->orderByDesc('registedate')
|
||||||
|
->first();
|
||||||
|
|
||||||
|
if (! $priceMotor || empty($priceMotor->itemList)) {
|
||||||
|
$this->command->info(' → 데이터 없음');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$itemList = json_decode($priceMotor->itemList, true);
|
||||||
|
if (! is_array($itemList)) {
|
||||||
|
$this->command->info(' → JSON 파싱 실패');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$count = 0;
|
||||||
|
$now = now();
|
||||||
|
|
||||||
|
foreach ($itemList as $item) {
|
||||||
|
$col1 = $item['col1'] ?? ''; // 전압/카테고리 (220, 380, 제어기 등)
|
||||||
|
$col2 = $item['col2'] ?? ''; // 용량/품목명
|
||||||
|
$salesPrice = (float) str_replace(',', '', $item['col13'] ?? '0');
|
||||||
|
|
||||||
|
if (empty($col2) || $salesPrice <= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 카테고리 결정
|
||||||
|
$category = match ($col1) {
|
||||||
|
'220', '380' => $col2, // 모터 용량 (150K, 300K 등)
|
||||||
|
default => $col1, // 제어기, 방화, 방범 등
|
||||||
|
};
|
||||||
|
|
||||||
|
KdPriceTable::create([
|
||||||
|
'tenant_id' => self::TENANT_ID,
|
||||||
|
'table_type' => KdPriceTable::TYPE_MOTOR,
|
||||||
|
'item_name' => trim("{$col1} {$col2}"),
|
||||||
|
'category' => $category,
|
||||||
|
'spec1' => $col1,
|
||||||
|
'spec2' => $col2,
|
||||||
|
'unit_price' => $salesPrice,
|
||||||
|
'unit' => 'EA',
|
||||||
|
'raw_data' => $item,
|
||||||
|
'is_active' => true,
|
||||||
|
]);
|
||||||
|
$count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->command->info(" → {$count}건 완료");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* price_shaft → kd_price_tables
|
||||||
|
*/
|
||||||
|
private function migrateShaftPrices(): void
|
||||||
|
{
|
||||||
|
$this->command->info('');
|
||||||
|
$this->command->info('📦 [2/5] price_shaft 마이그레이션...');
|
||||||
|
|
||||||
|
$priceShaft = DB::connection('chandj')
|
||||||
|
->table('price_shaft')
|
||||||
|
->where(function ($q) {
|
||||||
|
$q->where('is_deleted', 0)->orWhereNull('is_deleted');
|
||||||
|
})
|
||||||
|
->orderByDesc('registedate')
|
||||||
|
->first();
|
||||||
|
|
||||||
|
if (! $priceShaft || empty($priceShaft->itemList)) {
|
||||||
|
$this->command->info(' → 데이터 없음');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$itemList = json_decode($priceShaft->itemList, true);
|
||||||
|
if (! is_array($itemList)) {
|
||||||
|
$this->command->info(' → JSON 파싱 실패');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$count = 0;
|
||||||
|
|
||||||
|
foreach ($itemList as $item) {
|
||||||
|
$size = $item['col4'] ?? ''; // 사이즈 (3, 4, 5인치)
|
||||||
|
$length = $item['col10'] ?? ''; // 길이 (m 단위)
|
||||||
|
$salesPrice = (float) str_replace(',', '', $item['col19'] ?? '0');
|
||||||
|
|
||||||
|
if (empty($size) || $salesPrice <= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
KdPriceTable::create([
|
||||||
|
'tenant_id' => self::TENANT_ID,
|
||||||
|
'table_type' => KdPriceTable::TYPE_SHAFT,
|
||||||
|
'item_name' => "감기샤프트 {$size}인치 {$length}m",
|
||||||
|
'category' => '감기샤프트',
|
||||||
|
'spec1' => $size,
|
||||||
|
'spec2' => $length,
|
||||||
|
'unit_price' => $salesPrice,
|
||||||
|
'unit' => 'EA',
|
||||||
|
'raw_data' => $item,
|
||||||
|
'is_active' => true,
|
||||||
|
]);
|
||||||
|
$count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->command->info(" → {$count}건 완료");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* price_pipe → kd_price_tables
|
||||||
|
*/
|
||||||
|
private function migratePipePrices(): void
|
||||||
|
{
|
||||||
|
$this->command->info('');
|
||||||
|
$this->command->info('📦 [3/5] price_pipe 마이그레이션...');
|
||||||
|
|
||||||
|
$pricePipe = DB::connection('chandj')
|
||||||
|
->table('price_pipe')
|
||||||
|
->where(function ($q) {
|
||||||
|
$q->where('is_deleted', 0)->orWhereNull('is_deleted');
|
||||||
|
})
|
||||||
|
->orderByDesc('registedate')
|
||||||
|
->first();
|
||||||
|
|
||||||
|
if (! $pricePipe || empty($pricePipe->itemList)) {
|
||||||
|
$this->command->info(' → 데이터 없음');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$itemList = json_decode($pricePipe->itemList, true);
|
||||||
|
if (! is_array($itemList)) {
|
||||||
|
$this->command->info(' → JSON 파싱 실패');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$count = 0;
|
||||||
|
|
||||||
|
foreach ($itemList as $item) {
|
||||||
|
$length = $item['col2'] ?? ''; // 길이 (3000, 6000)
|
||||||
|
$thickness = $item['col4'] ?? ''; // 두께 (1.4)
|
||||||
|
$salesPrice = (float) str_replace(',', '', $item['col8'] ?? '0');
|
||||||
|
|
||||||
|
if (empty($thickness) || $salesPrice <= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
KdPriceTable::create([
|
||||||
|
'tenant_id' => self::TENANT_ID,
|
||||||
|
'table_type' => KdPriceTable::TYPE_PIPE,
|
||||||
|
'item_name' => "각파이프 {$thickness}T {$length}mm",
|
||||||
|
'category' => '각파이프',
|
||||||
|
'spec1' => $thickness,
|
||||||
|
'spec2' => $length,
|
||||||
|
'unit_price' => $salesPrice,
|
||||||
|
'unit' => 'EA',
|
||||||
|
'raw_data' => $item,
|
||||||
|
'is_active' => true,
|
||||||
|
]);
|
||||||
|
$count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->command->info(" → {$count}건 완료");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* price_angle → kd_price_tables
|
||||||
|
*/
|
||||||
|
private function migrateAnglePrices(): void
|
||||||
|
{
|
||||||
|
$this->command->info('');
|
||||||
|
$this->command->info('📦 [4/5] price_angle 마이그레이션...');
|
||||||
|
|
||||||
|
$priceAngle = DB::connection('chandj')
|
||||||
|
->table('price_angle')
|
||||||
|
->where(function ($q) {
|
||||||
|
$q->where('is_deleted', 0)->orWhereNull('is_deleted');
|
||||||
|
})
|
||||||
|
->orderByDesc('registedate')
|
||||||
|
->first();
|
||||||
|
|
||||||
|
if (! $priceAngle || empty($priceAngle->itemList)) {
|
||||||
|
$this->command->info(' → 데이터 없음');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$itemList = json_decode($priceAngle->itemList, true);
|
||||||
|
if (! is_array($itemList)) {
|
||||||
|
$this->command->info(' → JSON 파싱 실패');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$count = 0;
|
||||||
|
|
||||||
|
foreach ($itemList as $item) {
|
||||||
|
$type = $item['col2'] ?? ''; // 타입 (스크린용, 철재용)
|
||||||
|
$bracketSize = $item['col3'] ?? ''; // 브라켓크기
|
||||||
|
$angleType = $item['col4'] ?? ''; // 앵글타입 (앵글3T, 앵글4T)
|
||||||
|
$thickness = $item['col10'] ?? ''; // 두께
|
||||||
|
$salesPrice = (float) str_replace(',', '', $item['col19'] ?? '0');
|
||||||
|
|
||||||
|
if (empty($type) || $salesPrice <= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
KdPriceTable::create([
|
||||||
|
'tenant_id' => self::TENANT_ID,
|
||||||
|
'table_type' => KdPriceTable::TYPE_ANGLE,
|
||||||
|
'item_name' => "앵글 {$type} {$bracketSize} {$angleType}",
|
||||||
|
'category' => $type,
|
||||||
|
'spec1' => $bracketSize,
|
||||||
|
'spec2' => $angleType,
|
||||||
|
'spec3' => $thickness,
|
||||||
|
'unit_price' => $salesPrice,
|
||||||
|
'unit' => 'EA',
|
||||||
|
'raw_data' => $item,
|
||||||
|
'is_active' => true,
|
||||||
|
]);
|
||||||
|
$count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->command->info(" → {$count}건 완료");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* price_raw_materials → kd_price_tables
|
||||||
|
*/
|
||||||
|
private function migrateRawMaterialPrices(): void
|
||||||
|
{
|
||||||
|
$this->command->info('');
|
||||||
|
$this->command->info('📦 [5/5] price_raw_materials 마이그레이션...');
|
||||||
|
|
||||||
|
$priceRaw = DB::connection('chandj')
|
||||||
|
->table('price_raw_materials')
|
||||||
|
->where(function ($q) {
|
||||||
|
$q->where('is_deleted', 0)->orWhereNull('is_deleted');
|
||||||
|
})
|
||||||
|
->orderByDesc('registedate')
|
||||||
|
->first();
|
||||||
|
|
||||||
|
if (! $priceRaw || empty($priceRaw->itemList)) {
|
||||||
|
$this->command->info(' → 데이터 없음');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$itemList = json_decode($priceRaw->itemList, true);
|
||||||
|
if (! is_array($itemList)) {
|
||||||
|
$this->command->info(' → JSON 파싱 실패');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$count = 0;
|
||||||
|
|
||||||
|
foreach ($itemList as $item) {
|
||||||
|
$name = $item['col2'] ?? '';
|
||||||
|
$spec = $item['col3'] ?? '';
|
||||||
|
$salesPrice = (float) str_replace(',', '', $item['col19'] ?? $item['col13'] ?? '0');
|
||||||
|
|
||||||
|
if (empty($name) || $salesPrice <= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
KdPriceTable::create([
|
||||||
|
'tenant_id' => self::TENANT_ID,
|
||||||
|
'table_type' => KdPriceTable::TYPE_RAW_MATERIAL,
|
||||||
|
'item_name' => $name,
|
||||||
|
'category' => '원자재',
|
||||||
|
'spec1' => $spec,
|
||||||
|
'unit_price' => $salesPrice,
|
||||||
|
'unit' => '㎡',
|
||||||
|
'raw_data' => $item,
|
||||||
|
'is_active' => true,
|
||||||
|
]);
|
||||||
|
$count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->command->info(" → {$count}건 완료");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* chandj 연결 불가 시 샘플 데이터 삽입
|
||||||
|
*/
|
||||||
|
private function insertSampleData(): void
|
||||||
|
{
|
||||||
|
$this->command->info('');
|
||||||
|
$this->command->info('📦 샘플 데이터 삽입 중...');
|
||||||
|
|
||||||
|
// 모터 샘플 데이터 (5130 분석 결과 기반)
|
||||||
|
$motorData = [
|
||||||
|
['category' => '150K', 'spec1' => '220', 'spec2' => '150K', 'unit_price' => 85000],
|
||||||
|
['category' => '300K', 'spec1' => '220', 'spec2' => '300K', 'unit_price' => 120000],
|
||||||
|
['category' => '400K', 'spec1' => '220', 'spec2' => '400K', 'unit_price' => 150000],
|
||||||
|
['category' => '500K', 'spec1' => '220', 'spec2' => '500K', 'unit_price' => 180000],
|
||||||
|
['category' => '600K', 'spec1' => '220', 'spec2' => '600K', 'unit_price' => 220000],
|
||||||
|
['category' => '800K', 'spec1' => '220', 'spec2' => '800K', 'unit_price' => 280000],
|
||||||
|
['category' => '1000K', 'spec1' => '220', 'spec2' => '1000K', 'unit_price' => 350000],
|
||||||
|
['category' => '매립형', 'spec1' => '제어기', 'spec2' => '매립형', 'unit_price' => 45000],
|
||||||
|
['category' => '노출형', 'spec1' => '제어기', 'spec2' => '노출형', 'unit_price' => 55000],
|
||||||
|
['category' => '뒷박스', 'spec1' => '제어기', 'spec2' => '뒷박스', 'unit_price' => 35000],
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($motorData as $data) {
|
||||||
|
KdPriceTable::create(array_merge($data, [
|
||||||
|
'tenant_id' => self::TENANT_ID,
|
||||||
|
'table_type' => KdPriceTable::TYPE_MOTOR,
|
||||||
|
'item_name' => "모터/제어기 {$data['category']}",
|
||||||
|
'unit' => 'EA',
|
||||||
|
'is_active' => true,
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
$this->command->info(' → 모터/제어기 '.count($motorData).'건');
|
||||||
|
|
||||||
|
// 샤프트 샘플 데이터
|
||||||
|
$shaftData = [
|
||||||
|
['spec1' => '3', 'spec2' => '3.0', 'unit_price' => 45000],
|
||||||
|
['spec1' => '4', 'spec2' => '3.0', 'unit_price' => 55000],
|
||||||
|
['spec1' => '5', 'spec2' => '3.0', 'unit_price' => 65000],
|
||||||
|
['spec1' => '3', 'spec2' => '4.0', 'unit_price' => 60000],
|
||||||
|
['spec1' => '4', 'spec2' => '4.0', 'unit_price' => 75000],
|
||||||
|
['spec1' => '5', 'spec2' => '4.0', 'unit_price' => 90000],
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($shaftData as $data) {
|
||||||
|
KdPriceTable::create(array_merge($data, [
|
||||||
|
'tenant_id' => self::TENANT_ID,
|
||||||
|
'table_type' => KdPriceTable::TYPE_SHAFT,
|
||||||
|
'item_name' => "감기샤프트 {$data['spec1']}인치 {$data['spec2']}m",
|
||||||
|
'category' => '감기샤프트',
|
||||||
|
'unit' => 'EA',
|
||||||
|
'is_active' => true,
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
$this->command->info(' → 샤프트 '.count($shaftData).'건');
|
||||||
|
|
||||||
|
// 파이프 샘플 데이터
|
||||||
|
$pipeData = [
|
||||||
|
['spec1' => '1.4', 'spec2' => '3000', 'unit_price' => 12000],
|
||||||
|
['spec1' => '1.4', 'spec2' => '6000', 'unit_price' => 24000],
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($pipeData as $data) {
|
||||||
|
KdPriceTable::create(array_merge($data, [
|
||||||
|
'tenant_id' => self::TENANT_ID,
|
||||||
|
'table_type' => KdPriceTable::TYPE_PIPE,
|
||||||
|
'item_name' => "각파이프 {$data['spec1']}T {$data['spec2']}mm",
|
||||||
|
'category' => '각파이프',
|
||||||
|
'unit' => 'EA',
|
||||||
|
'is_active' => true,
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
$this->command->info(' → 파이프 '.count($pipeData).'건');
|
||||||
|
|
||||||
|
// 앵글 샘플 데이터
|
||||||
|
$angleData = [
|
||||||
|
['category' => '스크린용', 'spec1' => '530*320', 'spec2' => '앵글3T', 'unit_price' => 8000],
|
||||||
|
['category' => '스크린용', 'spec1' => '600*350', 'spec2' => '앵글3T', 'unit_price' => 10000],
|
||||||
|
['category' => '스크린용', 'spec1' => '690*390', 'spec2' => '앵글4T', 'unit_price' => 12000],
|
||||||
|
['category' => '철재용', 'spec1' => '530*320', 'spec2' => '앵글3T', 'unit_price' => 9000],
|
||||||
|
['category' => '철재용', 'spec1' => '600*350', 'spec2' => '앵글3T', 'unit_price' => 11000],
|
||||||
|
['category' => '철재용', 'spec1' => '690*390', 'spec2' => '앵글4T', 'unit_price' => 14000],
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($angleData as $data) {
|
||||||
|
KdPriceTable::create(array_merge($data, [
|
||||||
|
'tenant_id' => self::TENANT_ID,
|
||||||
|
'table_type' => KdPriceTable::TYPE_ANGLE,
|
||||||
|
'item_name' => "앵글 {$data['category']} {$data['spec1']} {$data['spec2']}",
|
||||||
|
'unit' => 'EA',
|
||||||
|
'is_active' => true,
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
$this->command->info(' → 앵글 '.count($angleData).'건');
|
||||||
|
|
||||||
|
// 원자재 샘플 데이터
|
||||||
|
$rawData = [
|
||||||
|
['item_name' => '실리카', 'spec1' => '스크린용', 'unit_price' => 25000],
|
||||||
|
['item_name' => '불투명', 'spec1' => '스크린용', 'unit_price' => 22000],
|
||||||
|
['item_name' => '화이바원단', 'spec1' => '스크린용', 'unit_price' => 28000],
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($rawData as $data) {
|
||||||
|
KdPriceTable::create(array_merge($data, [
|
||||||
|
'tenant_id' => self::TENANT_ID,
|
||||||
|
'table_type' => KdPriceTable::TYPE_RAW_MATERIAL,
|
||||||
|
'category' => '원자재',
|
||||||
|
'unit' => '㎡',
|
||||||
|
'is_active' => true,
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
$this->command->info(' → 원자재 '.count($rawData).'건');
|
||||||
|
|
||||||
|
// BDmodels 샘플 데이터 (절곡품)
|
||||||
|
$this->insertBDModelsSampleData();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BDmodels 샘플 데이터 삽입 (절곡품)
|
||||||
|
*/
|
||||||
|
private function insertBDModelsSampleData(): void
|
||||||
|
{
|
||||||
|
$bdmodelsData = [
|
||||||
|
// 케이스 (규격별 단가 - 원/m)
|
||||||
|
['category' => '케이스', 'spec2' => '500*380', 'unit_price' => 15000, 'unit' => 'm'],
|
||||||
|
['category' => '케이스', 'spec2' => '550*430', 'unit_price' => 18000, 'unit' => 'm'],
|
||||||
|
['category' => '케이스', 'spec2' => '650*550', 'unit_price' => 22000, 'unit' => 'm'],
|
||||||
|
|
||||||
|
// 케이스 마구리 (규격별 단가 - 원/개)
|
||||||
|
['category' => '마구리', 'spec2' => '500*380', 'unit_price' => 5000, 'unit' => 'EA'],
|
||||||
|
['category' => '마구리', 'spec2' => '550*430', 'unit_price' => 6000, 'unit' => 'EA'],
|
||||||
|
['category' => '마구리', 'spec2' => '650*550', 'unit_price' => 7500, 'unit' => 'EA'],
|
||||||
|
|
||||||
|
// 케이스용 연기차단재 (공통 단가 - 원/m)
|
||||||
|
['category' => '케이스용 연기차단재', 'unit_price' => 3500, 'unit' => 'm'],
|
||||||
|
|
||||||
|
// 가이드레일 (모델+마감+규격별 단가 - 원/m)
|
||||||
|
['category' => '가이드레일', 'item_code' => 'KSS01', 'spec1' => 'SUS', 'spec2' => '120*70', 'unit_price' => 12000, 'unit' => 'm'],
|
||||||
|
['category' => '가이드레일', 'item_code' => 'KSS01', 'spec1' => 'SUS', 'spec2' => '120*100', 'unit_price' => 15000, 'unit' => 'm'],
|
||||||
|
['category' => '가이드레일', 'item_code' => 'KSS01', 'spec1' => 'EGI', 'spec2' => '120*70', 'unit_price' => 10000, 'unit' => 'm'],
|
||||||
|
['category' => '가이드레일', 'item_code' => 'KSS01', 'spec1' => 'EGI', 'spec2' => '120*100', 'unit_price' => 13000, 'unit' => 'm'],
|
||||||
|
['category' => '가이드레일', 'item_code' => 'KWS01', 'spec1' => 'SUS', 'spec2' => '120*70', 'unit_price' => 13000, 'unit' => 'm'],
|
||||||
|
['category' => '가이드레일', 'item_code' => 'KWS01', 'spec1' => 'SUS', 'spec2' => '120*100', 'unit_price' => 16000, 'unit' => 'm'],
|
||||||
|
|
||||||
|
// 가이드레일용 연기차단재 (공통 단가 - 원/m)
|
||||||
|
['category' => '가이드레일용 연기차단재', 'unit_price' => 2500, 'unit' => 'm'],
|
||||||
|
|
||||||
|
// 하단마감재/하장바 (모델+마감별 단가 - 원/m)
|
||||||
|
['category' => '하단마감재', 'item_code' => 'KSS01', 'spec1' => 'SUS', 'unit_price' => 8000, 'unit' => 'm'],
|
||||||
|
['category' => '하단마감재', 'item_code' => 'KSS01', 'spec1' => 'EGI', 'unit_price' => 6500, 'unit' => 'm'],
|
||||||
|
['category' => '하단마감재', 'item_code' => 'KWS01', 'spec1' => 'SUS', 'unit_price' => 8500, 'unit' => 'm'],
|
||||||
|
|
||||||
|
// L-BAR (모델별 단가 - 원/m)
|
||||||
|
['category' => 'L-BAR', 'item_code' => 'KSS01', 'unit_price' => 4500, 'unit' => 'm'],
|
||||||
|
['category' => 'L-BAR', 'item_code' => 'KWS01', 'unit_price' => 5000, 'unit' => 'm'],
|
||||||
|
|
||||||
|
// 보강평철 (공통 단가 - 원/m)
|
||||||
|
['category' => '보강평철', 'unit_price' => 3000, 'unit' => 'm'],
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($bdmodelsData as $data) {
|
||||||
|
$itemCode = $data['item_code'] ?? null;
|
||||||
|
$spec1 = $data['spec1'] ?? null;
|
||||||
|
$spec2 = $data['spec2'] ?? null;
|
||||||
|
|
||||||
|
$itemName = $data['category'];
|
||||||
|
if ($itemCode) {
|
||||||
|
$itemName .= " {$itemCode}";
|
||||||
|
}
|
||||||
|
if ($spec1) {
|
||||||
|
$itemName .= " {$spec1}";
|
||||||
|
}
|
||||||
|
if ($spec2) {
|
||||||
|
$itemName .= " {$spec2}";
|
||||||
|
}
|
||||||
|
|
||||||
|
KdPriceTable::create([
|
||||||
|
'tenant_id' => self::TENANT_ID,
|
||||||
|
'table_type' => KdPriceTable::TYPE_BDMODELS,
|
||||||
|
'item_code' => $itemCode,
|
||||||
|
'item_name' => $itemName,
|
||||||
|
'category' => $data['category'],
|
||||||
|
'spec1' => $spec1,
|
||||||
|
'spec2' => $spec2,
|
||||||
|
'unit_price' => $data['unit_price'],
|
||||||
|
'unit' => $data['unit'],
|
||||||
|
'is_active' => true,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
$this->command->info(' → BDmodels(절곡품) '.count($bdmodelsData).'건');
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user