'임시저장', 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'); } /** * 수주 노드 (전체, depth/sort_order 순) */ public function nodes(): HasMany { return $this->hasMany(OrderNode::class)->orderBy('depth')->orderBy('sort_order'); } /** * 수주 루트 노드 (parent_id가 NULL인 최상위) */ public function rootNodes(): HasMany { return $this->hasMany(OrderNode::class)->whereNull('parent_id')->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, '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->completion_date, 'memo' => $quote->remarks, 'options' => [ 'manager_name' => $quote->manager, ], ]); } }