2025-07-29 23:22:31 +09:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace App\Models\Orders;
|
|
|
|
|
|
2025-12-14 01:20:18 +09:00
|
|
|
use App\Models\Items\Item;
|
2026-01-15 16:13:34 +09:00
|
|
|
use App\Models\Production\WorkOrder;
|
2026-01-05 15:56:46 +09:00
|
|
|
use App\Models\Quote\Quote;
|
2026-01-16 21:59:06 +09:00
|
|
|
use App\Models\Tenants\Shipment;
|
2026-01-05 15:56:46 +09:00
|
|
|
use App\Traits\BelongsToTenant;
|
2025-07-29 23:22:31 +09:00
|
|
|
use Illuminate\Database\Eloquent\Model;
|
2026-01-05 15:56:46 +09:00
|
|
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|
|
|
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
2025-07-29 23:22:31 +09:00
|
|
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
|
|
|
|
|
2025-08-21 09:50:15 +09:00
|
|
|
/**
|
2026-01-05 15:56:46 +09:00
|
|
|
* 수주 마스터 (Orders)
|
|
|
|
|
*
|
2025-08-21 09:50:15 +09:00
|
|
|
* @mixin IdeHelperOrder
|
|
|
|
|
*/
|
2025-07-29 23:22:31 +09:00
|
|
|
class Order extends Model
|
|
|
|
|
{
|
2026-01-05 15:56:46 +09:00
|
|
|
use BelongsToTenant, SoftDeletes;
|
|
|
|
|
|
|
|
|
|
// 상태 코드
|
|
|
|
|
public const STATUS_DRAFT = 'DRAFT';
|
|
|
|
|
|
|
|
|
|
public const STATUS_CONFIRMED = 'CONFIRMED';
|
|
|
|
|
|
|
|
|
|
public const STATUS_IN_PROGRESS = 'IN_PROGRESS';
|
|
|
|
|
|
2026-01-16 21:59:06 +09:00
|
|
|
public const STATUS_IN_PRODUCTION = 'IN_PRODUCTION'; // 생산중
|
|
|
|
|
|
|
|
|
|
public const STATUS_PRODUCED = 'PRODUCED'; // 생산완료
|
|
|
|
|
|
|
|
|
|
public const STATUS_SHIPPING = 'SHIPPING'; // 출하중
|
|
|
|
|
|
|
|
|
|
public const STATUS_SHIPPED = 'SHIPPED'; // 출하완료
|
|
|
|
|
|
2026-01-05 15:56:46 +09:00
|
|
|
public const STATUS_COMPLETED = 'COMPLETED';
|
|
|
|
|
|
|
|
|
|
public const STATUS_CANCELLED = 'CANCELLED';
|
|
|
|
|
|
2026-01-16 21:59:06 +09:00
|
|
|
/**
|
|
|
|
|
* 전체 상태 목록
|
|
|
|
|
*/
|
|
|
|
|
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 => '취소',
|
|
|
|
|
];
|
|
|
|
|
|
2026-01-05 15:56:46 +09:00
|
|
|
// 주문 유형
|
|
|
|
|
public const TYPE_ORDER = 'ORDER'; // 수주
|
|
|
|
|
|
|
|
|
|
public const TYPE_PURCHASE = 'PURCHASE'; // 발주
|
2025-07-29 23:22:31 +09:00
|
|
|
|
|
|
|
|
protected $table = 'orders';
|
|
|
|
|
|
|
|
|
|
protected $fillable = [
|
2026-01-05 15:56:46 +09:00
|
|
|
'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',
|
2026-01-16 21:58:57 +09:00
|
|
|
'options',
|
2026-01-05 15:56:46 +09:00
|
|
|
// 감사
|
|
|
|
|
'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',
|
2026-01-16 21:58:57 +09:00
|
|
|
'options' => 'array',
|
2026-01-05 15:56:46 +09:00
|
|
|
'created_at' => 'datetime',
|
|
|
|
|
'updated_at' => 'datetime',
|
|
|
|
|
'deleted_at' => 'datetime',
|
2025-07-29 23:22:31 +09:00
|
|
|
];
|
|
|
|
|
|
2026-01-05 15:56:46 +09:00
|
|
|
/**
|
|
|
|
|
* 수주 상세 품목
|
|
|
|
|
*/
|
|
|
|
|
public function items(): HasMany
|
2025-07-29 23:22:31 +09:00
|
|
|
{
|
2026-01-05 15:56:46 +09:00
|
|
|
return $this->hasMany(OrderItem::class)->orderBy('sort_order');
|
2025-07-29 23:22:31 +09:00
|
|
|
}
|
|
|
|
|
|
2026-01-05 15:56:46 +09:00
|
|
|
/**
|
|
|
|
|
* 수주 이력
|
|
|
|
|
*/
|
|
|
|
|
public function histories(): HasMany
|
2025-07-29 23:22:31 +09:00
|
|
|
{
|
|
|
|
|
return $this->hasMany(OrderHistory::class);
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-05 15:56:46 +09:00
|
|
|
/**
|
|
|
|
|
* 수주 버전
|
|
|
|
|
*/
|
|
|
|
|
public function versions(): HasMany
|
2025-07-29 23:22:31 +09:00
|
|
|
{
|
|
|
|
|
return $this->hasMany(OrderVersion::class);
|
|
|
|
|
}
|
2025-12-14 01:20:18 +09:00
|
|
|
|
2026-01-05 15:56:46 +09:00
|
|
|
/**
|
|
|
|
|
* 원본 견적
|
|
|
|
|
*/
|
|
|
|
|
public function quote(): BelongsTo
|
|
|
|
|
{
|
|
|
|
|
return $this->belongsTo(Quote::class);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 거래처
|
|
|
|
|
*/
|
|
|
|
|
public function client(): BelongsTo
|
|
|
|
|
{
|
|
|
|
|
return $this->belongsTo(Client::class);
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-16 21:59:06 +09:00
|
|
|
/**
|
|
|
|
|
* 작성자 (담당자)
|
|
|
|
|
*/
|
|
|
|
|
public function writer(): BelongsTo
|
|
|
|
|
{
|
|
|
|
|
return $this->belongsTo(\App\Models\Members\User::class, 'writer_id');
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-05 15:56:46 +09:00
|
|
|
/**
|
|
|
|
|
* 품목 (통합 items 테이블)
|
|
|
|
|
*/
|
|
|
|
|
public function item(): BelongsTo
|
2025-12-14 01:20:18 +09:00
|
|
|
{
|
|
|
|
|
return $this->belongsTo(Item::class, 'item_id');
|
|
|
|
|
}
|
2026-01-05 15:56:46 +09:00
|
|
|
|
2026-01-15 16:13:34 +09:00
|
|
|
/**
|
|
|
|
|
* 작업지시 목록
|
|
|
|
|
*/
|
|
|
|
|
public function workOrders(): HasMany
|
|
|
|
|
{
|
|
|
|
|
return $this->hasMany(WorkOrder::class, 'sales_order_id');
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-16 21:59:06 +09:00
|
|
|
/**
|
|
|
|
|
* 출하 목록
|
|
|
|
|
*/
|
|
|
|
|
public function shipments(): HasMany
|
|
|
|
|
{
|
|
|
|
|
return $this->hasMany(Shipment::class, 'order_id');
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-05 15:56:46 +09:00
|
|
|
/**
|
|
|
|
|
* 품목들로부터 금액 합계 재계산
|
|
|
|
|
*/
|
|
|
|
|
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,
|
|
|
|
|
]);
|
|
|
|
|
}
|
2025-07-29 23:22:31 +09:00
|
|
|
}
|