Files
sam-api/app/Models/Orders/OrderItem.php
권혁성 189b38c936 feat: Auditable 트레이트 구현 및 97개 모델 적용
- Auditable 트레이트 신규 생성 (bootAuditable 패턴)
  - creating: created_by/updated_by 자동 채우기
  - updating: updated_by 자동 채우기
  - deleting: deleted_by 채우기 + saveQuietly()
  - created/updated/deleted: audit_logs 자동 기록
- 기존 AuditLogger 패턴과 동일한 try/catch 조용한 실패
- 변경된 필드만 before/after 기록 (updated 이벤트)
- auditExclude 프로퍼티로 모델별 제외 필드 설정 가능
- 제외 대상: Attendance, StockTransaction, TodayIssue 등 고빈도/시스템 모델

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 15:33:54 +09:00

192 lines
5.0 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace App\Models\Orders;
use App\Models\Items\Item;
use App\Models\Quote\Quote;
use App\Models\Quote\QuoteItem;
use App\Traits\Auditable;
use App\Traits\BelongsToTenant;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
/**
* 수주 상세 (Order Items)
*
* @mixin IdeHelperOrderItem
*/
class OrderItem extends Model
{
use Auditable, BelongsToTenant, SoftDeletes;
protected $table = 'order_items';
protected $fillable = [
'tenant_id',
'order_id',
'quote_id',
'quote_item_id',
'serial_no',
// 품목 정보
'item_id',
'item_code',
'item_name',
'specification',
// 제품-부품 매핑용 코드
'floor_code',
'symbol_code',
'unit',
// 수량/금액
'quantity',
'unit_price',
'supply_amount',
'tax_amount',
'total_amount',
// 할인
'discount_rate',
'discount_amount',
// 기타
'status_code',
'design_code',
'remarks',
'note',
'sort_order',
'attributes',
// 감사
'created_by',
'updated_by',
'deleted_by',
];
protected $casts = [
'quantity' => 'decimal:4',
'unit_price' => 'decimal:2',
'supply_amount' => 'decimal:2',
'tax_amount' => 'decimal:2',
'total_amount' => 'decimal:2',
'discount_rate' => 'decimal:2',
'discount_amount' => 'decimal:2',
'attributes' => 'array',
'created_at' => 'datetime',
'updated_at' => 'datetime',
'deleted_at' => 'datetime',
];
/**
* 수주 마스터
*/
public function order(): BelongsTo
{
return $this->belongsTo(Order::class);
}
/**
* 품목 마스터
*/
public function item(): BelongsTo
{
return $this->belongsTo(Item::class, 'item_id');
}
/**
* 원본 견적
*/
public function quote(): BelongsTo
{
return $this->belongsTo(Quote::class);
}
/**
* 원본 견적 품목
*/
public function quoteItem(): BelongsTo
{
return $this->belongsTo(QuoteItem::class);
}
/**
* 투입 구성 (자재/BOM 등)
*/
public function components(): HasMany
{
return $this->hasMany(OrderItemComponent::class);
}
/**
* 공급가액 계산 (수량 × 단가 - 할인)
*/
public function calculateSupplyAmount(): float
{
$gross = $this->quantity * $this->unit_price;
$discount = $this->discount_amount ?: ($gross * ($this->discount_rate / 100));
return round($gross - $discount, 2);
}
/**
* 세액 계산 (공급가액 × 10%)
*/
public function calculateTaxAmount(): float
{
return round($this->supply_amount * 0.1, 2);
}
/**
* 총액 계산 (공급가액 + 세액)
*/
public function calculateTotalAmount(): float
{
return round($this->supply_amount + $this->tax_amount, 2);
}
/**
* 금액 재계산 및 저장
*/
public function recalculateAmounts(): self
{
$this->supply_amount = $this->calculateSupplyAmount();
$this->tax_amount = $this->calculateTaxAmount();
$this->total_amount = $this->calculateTotalAmount();
return $this;
}
/**
* 견적 품목에서 수주 품목 생성
*
* @param int $serialIndex 품목 순번 (1부터 시작)
* @param array $productMapping 제품 매핑 정보 ['floor_code' => '10', 'symbol_code' => 'F1']
*/
public static function createFromQuoteItem(QuoteItem $quoteItem, int $orderId, int $serialIndex = 1, array $productMapping = []): self
{
$qty = $quoteItem->calculated_quantity ?? 1;
$supplyAmount = $quoteItem->unit_price * $qty;
$taxAmount = round($supplyAmount * 0.1, 2);
return new self([
'tenant_id' => $quoteItem->tenant_id,
'order_id' => $orderId,
'quote_id' => $quoteItem->quote_id,
'quote_item_id' => $quoteItem->id,
'serial_no' => str_pad($serialIndex, 3, '0', STR_PAD_LEFT),
'item_id' => $quoteItem->item_id,
'item_code' => $quoteItem->item_code,
'item_name' => $quoteItem->item_name,
'specification' => $quoteItem->specification,
// 제품-부품 매핑 코드
'floor_code' => $productMapping['floor_code'] ?? null,
'symbol_code' => $productMapping['symbol_code'] ?? null,
'unit' => $quoteItem->unit ?? 'EA',
'quantity' => $qty,
'unit_price' => $quoteItem->unit_price,
'supply_amount' => $supplyAmount,
'tax_amount' => $taxAmount,
'total_amount' => $supplyAmount + $taxAmount,
'note' => $quoteItem->note,
'sort_order' => $quoteItem->sort_order ?? 0,
]);
}
}