Files
sam-api/app/Models/Orders/Order.php

324 lines
8.2 KiB
PHP
Raw Normal View History

<?php
namespace App\Models\Orders;
use App\Models\Items\Item;
use App\Models\Production\WorkOrder;
use App\Models\Products\CommonCode;
use App\Models\Quote\Quote;
use App\Models\Tenants\Sale;
use App\Models\Tenants\Shipment;
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;
/**
* 수주 마스터 (Orders)
*
* @mixin IdeHelperOrder
*/
class Order extends Model
{
use Auditable, BelongsToTenant, SoftDeletes;
// 상태 코드
public const STATUS_DRAFT = 'DRAFT';
public const STATUS_CONFIRMED = 'CONFIRMED';
public const STATUS_IN_PROGRESS = 'IN_PROGRESS';
public const STATUS_IN_PRODUCTION = 'IN_PRODUCTION'; // 생산중
public const STATUS_PRODUCED = 'PRODUCED'; // 생산완료
public const STATUS_SHIPPING = 'SHIPPING'; // 출하중
public const STATUS_SHIPPED = 'SHIPPED'; // 출하완료
public const STATUS_COMPLETED = 'COMPLETED';
public const STATUS_CANCELLED = 'CANCELLED';
/**
* 전체 상태 목록
*/
public const STATUSES = [
self::STATUS_DRAFT,
self::STATUS_CONFIRMED,
self::STATUS_IN_PROGRESS,
self::STATUS_IN_PRODUCTION,
self::STATUS_PRODUCED,
self::STATUS_SHIPPING,
self::STATUS_SHIPPED,
self::STATUS_COMPLETED,
self::STATUS_CANCELLED,
];
/**
* 상태 라벨
*/
public const STATUS_LABELS = [
self::STATUS_DRAFT => '임시저장',
self::STATUS_CONFIRMED => '확정',
self::STATUS_IN_PROGRESS => '진행중',
self::STATUS_IN_PRODUCTION => '생산중',
self::STATUS_PRODUCED => '생산완료',
self::STATUS_SHIPPING => '출하중',
self::STATUS_SHIPPED => '출하완료',
self::STATUS_COMPLETED => '완료',
self::STATUS_CANCELLED => '취소',
];
// 주문 유형
public const TYPE_ORDER = 'ORDER'; // 수주
public const TYPE_PURCHASE = 'PURCHASE'; // 발주
// 매출 인식 시점
public const SALES_ON_ORDER_CONFIRM = 'on_order_confirm'; // 수주확정 시
public const SALES_ON_SHIPMENT = 'on_shipment'; // 출하완료 시
public const SALES_MANUAL = 'manual'; // 수동 등록
public const SALES_RECOGNITION_TYPES = [
self::SALES_ON_ORDER_CONFIRM => '수주확정 시',
self::SALES_ON_SHIPMENT => '출하완료 시',
self::SALES_MANUAL => '수동 등록',
];
protected $table = 'orders';
protected $fillable = [
'tenant_id',
'quote_id',
'order_no',
'order_type_code',
'status_code',
'category_code',
'item_id',
'received_at',
'writer_id',
'client_id',
'client_name',
'client_contact',
'site_name',
'quantity',
// 금액 정보
'supply_amount',
'tax_amount',
'total_amount',
'discount_rate',
'discount_amount',
// 기타
'delivery_date',
'delivery_method_code',
'memo',
'remarks',
'note',
'options',
// 매출 연동
'sales_recognition',
'sale_id',
// 감사
'created_by',
'updated_by',
'deleted_by',
];
protected $casts = [
'quantity' => 'decimal:4',
'supply_amount' => 'decimal:2',
'tax_amount' => 'decimal:2',
'total_amount' => 'decimal:2',
'discount_rate' => 'decimal:2',
'discount_amount' => 'decimal:2',
'received_at' => 'datetime',
'delivery_date' => 'date',
'options' => 'array',
'created_at' => 'datetime',
'updated_at' => 'datetime',
'deleted_at' => 'datetime',
];
/**
* JSON 응답에 자동 포함할 accessor
*/
protected $appends = [
'delivery_method_label',
'shipping_cost_label',
];
/**
* 수주 상세 품목
*/
public function items(): HasMany
{
return $this->hasMany(OrderItem::class)->orderBy('sort_order');
}
/**
* 수주 이력
*/
public function histories(): HasMany
{
return $this->hasMany(OrderHistory::class);
}
/**
* 수주 버전
*/
public function versions(): HasMany
{
return $this->hasMany(OrderVersion::class);
}
/**
* 원본 견적
*/
public function quote(): BelongsTo
{
return $this->belongsTo(Quote::class);
}
/**
* 거래처
*/
public function client(): BelongsTo
{
return $this->belongsTo(Client::class);
}
/**
* 작성자 (담당자)
*/
public function writer(): BelongsTo
{
return $this->belongsTo(\App\Models\Members\User::class, 'writer_id');
}
/**
* 품목 (통합 items 테이블)
*/
public function item(): BelongsTo
{
return $this->belongsTo(Item::class, 'item_id');
}
/**
* 작업지시 목록
*/
public function workOrders(): HasMany
{
return $this->hasMany(WorkOrder::class, 'sales_order_id');
}
/**
* 출하 목록
*/
public function shipments(): HasMany
{
return $this->hasMany(Shipment::class, 'order_id');
}
/**
* 연결된 매출
*/
public function sale(): BelongsTo
{
return $this->belongsTo(Sale::class);
}
/**
* 수주에서 생성된 모든 매출 (출하별 포함)
*/
public function sales(): HasMany
{
return $this->hasMany(Sale::class, 'order_id');
}
/**
* 매출 인식 시점 라벨
*/
public function getSalesRecognitionLabelAttribute(): string
{
return self::SALES_RECOGNITION_TYPES[$this->sales_recognition] ?? '출하완료 시';
}
/**
* 배송방식 라벨 (common_codes 테이블에서 조회)
*/
public function getDeliveryMethodLabelAttribute(): string
{
return CommonCode::getLabel('delivery_method', $this->delivery_method_code);
}
/**
* 운임비용 라벨 (common_codes 테이블에서 조회)
*/
public function getShippingCostLabelAttribute(): string
{
$shippingCostCode = $this->options['shipping_cost_code'] ?? null;
return $shippingCostCode ? CommonCode::getLabel('shipping_cost', $shippingCostCode) : '';
}
/**
* 수주확정 매출 생성 여부
*/
public function shouldCreateSaleOnConfirm(): bool
{
return $this->sales_recognition === self::SALES_ON_ORDER_CONFIRM;
}
/**
* 출하완료 매출 생성 여부
*/
public function shouldCreateSaleOnShipment(): bool
{
return $this->sales_recognition === self::SALES_ON_SHIPMENT;
}
/**
* 품목들로부터 금액 합계 재계산
*/
public function recalculateTotals(): self
{
$this->supply_amount = $this->items->sum('supply_amount');
$this->tax_amount = $this->items->sum('tax_amount');
$this->total_amount = $this->items->sum('total_amount');
$this->quantity = $this->items->sum('quantity');
return $this;
}
/**
* 견적에서 수주 생성
*/
public static function createFromQuote(Quote $quote, string $orderNo): self
{
return new self([
'tenant_id' => $quote->tenant_id,
'quote_id' => $quote->id,
'order_no' => $orderNo,
'order_type_code' => self::TYPE_ORDER,
'status_code' => self::STATUS_DRAFT,
'client_id' => $quote->client_id,
'client_name' => $quote->client?->name,
'client_contact' => $quote->contact_person,
'site_name' => $quote->site_name,
'quantity' => $quote->items->sum('calculated_quantity'),
'supply_amount' => $quote->total_amount,
'tax_amount' => round($quote->total_amount * 0.1, 2),
'total_amount' => round($quote->total_amount * 1.1, 2),
'delivery_date' => $quote->delivery_date,
'memo' => $quote->remarks,
'remarks' => $quote->internal_notes,
]);
}
}