feat: 견적 수식 품목 단가 연동 구현
- Price 모델 생성 (prices 테이블 연동) - getCurrentPrice(): 현재 유효 단가 조회 - getSalesPriceByItemCode(): 품목 코드로 판매단가 조회 - FormulaEvaluatorService.getItemPrice() 구현 완료 (TODO 해결)
This commit is contained in:
148
app/Models/Price.php
Normal file
148
app/Models/Price.php
Normal file
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class Price extends Model
|
||||
{
|
||||
use SoftDeletes;
|
||||
|
||||
protected $table = 'prices';
|
||||
|
||||
protected $fillable = [
|
||||
'tenant_id',
|
||||
'item_type_code',
|
||||
'item_id',
|
||||
'client_group_id',
|
||||
'purchase_price',
|
||||
'processing_cost',
|
||||
'loss_rate',
|
||||
'margin_rate',
|
||||
'sales_price',
|
||||
'rounding_rule',
|
||||
'rounding_unit',
|
||||
'supplier',
|
||||
'effective_from',
|
||||
'effective_to',
|
||||
'note',
|
||||
'status',
|
||||
'is_final',
|
||||
'finalized_at',
|
||||
'finalized_by',
|
||||
'created_by',
|
||||
'updated_by',
|
||||
'deleted_by',
|
||||
];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'purchase_price' => 'decimal:4',
|
||||
'processing_cost' => 'decimal:4',
|
||||
'loss_rate' => 'decimal:2',
|
||||
'margin_rate' => 'decimal:2',
|
||||
'sales_price' => 'decimal:4',
|
||||
'rounding_unit' => 'integer',
|
||||
'effective_from' => 'date',
|
||||
'effective_to' => 'date',
|
||||
'is_final' => 'boolean',
|
||||
'finalized_at' => 'datetime',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 상태 상수
|
||||
*/
|
||||
public const STATUS_DRAFT = 'draft';
|
||||
|
||||
public const STATUS_ACTIVE = 'active';
|
||||
|
||||
public const STATUS_INACTIVE = 'inactive';
|
||||
|
||||
public const STATUS_FINALIZED = 'finalized';
|
||||
|
||||
/**
|
||||
* 품목 유형 상수
|
||||
*/
|
||||
public const ITEM_TYPE_PRODUCT = 'PRODUCT';
|
||||
|
||||
public const ITEM_TYPE_MATERIAL = 'MATERIAL';
|
||||
|
||||
/**
|
||||
* 반올림 규칙 상수
|
||||
*/
|
||||
public const ROUNDING_ROUND = 'round';
|
||||
|
||||
public const ROUNDING_CEIL = 'ceil';
|
||||
|
||||
public const ROUNDING_FLOOR = 'floor';
|
||||
|
||||
/**
|
||||
* 특정 품목의 현재 유효 단가 조회
|
||||
*/
|
||||
public static function getCurrentPrice(
|
||||
int $tenantId,
|
||||
string $itemTypeCode,
|
||||
int $itemId,
|
||||
?int $clientGroupId = null
|
||||
): ?self {
|
||||
return static::where('tenant_id', $tenantId)
|
||||
->where('item_type_code', $itemTypeCode)
|
||||
->where('item_id', $itemId)
|
||||
->where(function ($query) use ($clientGroupId) {
|
||||
if ($clientGroupId) {
|
||||
$query->where('client_group_id', $clientGroupId)
|
||||
->orWhereNull('client_group_id');
|
||||
} else {
|
||||
$query->whereNull('client_group_id');
|
||||
}
|
||||
})
|
||||
->where('status', self::STATUS_ACTIVE)
|
||||
->where('effective_from', '<=', now())
|
||||
->where(function ($query) {
|
||||
$query->whereNull('effective_to')
|
||||
->orWhere('effective_to', '>=', now());
|
||||
})
|
||||
->orderByRaw('client_group_id IS NULL') // 고객그룹 지정된 것 우선
|
||||
->orderBy('effective_from', 'desc')
|
||||
->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* 품목 코드로 현재 유효 판매단가 조회
|
||||
* (quote_formula_items.item_code와 연동용)
|
||||
*/
|
||||
public static function getSalesPriceByItemCode(int $tenantId, string $itemCode): float
|
||||
{
|
||||
// products 테이블에서 품목 코드로 검색
|
||||
$product = DB::table('products')
|
||||
->where('tenant_id', $tenantId)
|
||||
->where('code', $itemCode)
|
||||
->whereNull('deleted_at')
|
||||
->first();
|
||||
|
||||
if ($product) {
|
||||
$price = static::getCurrentPrice($tenantId, self::ITEM_TYPE_PRODUCT, $product->id);
|
||||
|
||||
return (float) ($price?->sales_price ?? 0);
|
||||
}
|
||||
|
||||
// materials 테이블에서도 검색
|
||||
$material = DB::table('materials')
|
||||
->where('tenant_id', $tenantId)
|
||||
->where('code', $itemCode)
|
||||
->whereNull('deleted_at')
|
||||
->first();
|
||||
|
||||
if ($material) {
|
||||
$price = static::getCurrentPrice($tenantId, self::ITEM_TYPE_MATERIAL, $material->id);
|
||||
|
||||
return (float) ($price?->sales_price ?? 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -323,8 +323,15 @@ private function evaluateCondition(string $condition): bool
|
||||
|
||||
private function getItemPrice(string $itemCode): float
|
||||
{
|
||||
// TODO: 품목 마스터에서 단가 조회
|
||||
return 0;
|
||||
$tenantId = session('selected_tenant_id');
|
||||
|
||||
if (! $tenantId) {
|
||||
$this->errors[] = "테넌트 ID가 설정되지 않았습니다.";
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return \App\Models\Price::getSalesPriceByItemCode($tenantId, $itemCode);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user