feat(API): 견적-주문 연동 필드 및 마이그레이션 추가
- Order, OrderItem 모델에 견적 연동 필드 추가 - Quote 모델에 order_id 관계 추가 - QuoteService 개선 - 관련 마이그레이션 파일 추가 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -36,6 +36,27 @@ ### 남은 작업
|
||||
|
||||
---
|
||||
|
||||
## 2026-01-02 (목) - 견적 BOM 산출 작업 현황 및 Item 모델 주석 추가
|
||||
|
||||
### 작업 목표
|
||||
- 견적 BOM 산출 관련 작업 진행 상황 문서화
|
||||
- Item 모델 필드 주석 추가
|
||||
|
||||
### 수정된 파일
|
||||
| 파일명 | 설명 |
|
||||
|--------|------|
|
||||
| `app/Models/Items/Item.php` | item_category 필드 주석 추가 |
|
||||
|
||||
### 주요 변경 내용
|
||||
1. **Item 모델 필드 주석**:
|
||||
- `item_category` 필드에 설명 주석 추가
|
||||
- React 프론트엔드에서 필드 매핑 시 참조용
|
||||
|
||||
### Git 커밋
|
||||
- `02e268e` docs(API): 견적 BOM 산출 작업 현황 및 Item 모델 주석 추가
|
||||
|
||||
---
|
||||
|
||||
## 2026-01-02 (목) - Phase 1.2 다건 BOM 기반 자동산출 API 구현
|
||||
|
||||
### 작업 목표
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 논리적 데이터베이스 관계 문서
|
||||
|
||||
> **자동 생성**: 2026-01-02 11:42:39
|
||||
> **자동 생성**: 2026-01-05 14:14:22
|
||||
> **소스**: Eloquent 모델 관계 분석
|
||||
|
||||
## 📊 모델별 관계 현황
|
||||
@@ -297,6 +297,7 @@ ### client_groups
|
||||
### orders
|
||||
**모델**: `App\Models\Orders\Order`
|
||||
|
||||
- **quote()**: belongsTo → `quotes`
|
||||
- **item()**: belongsTo → `items`
|
||||
- **items()**: hasMany → `order_items`
|
||||
- **histories()**: hasMany → `order_histories`
|
||||
@@ -312,6 +313,8 @@ ### order_items
|
||||
|
||||
- **order()**: belongsTo → `orders`
|
||||
- **item()**: belongsTo → `items`
|
||||
- **quote()**: belongsTo → `quotes`
|
||||
- **quoteItem()**: belongsTo → `quote_items`
|
||||
- **components()**: hasMany → `order_item_components`
|
||||
|
||||
### order_item_components
|
||||
|
||||
@@ -2,47 +2,172 @@
|
||||
|
||||
namespace App\Models\Orders;
|
||||
|
||||
use App\Models\Clients\Client;
|
||||
use App\Models\Items\Item;
|
||||
use App\Models\Quote\Quote;
|
||||
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 SoftDeletes;
|
||||
use BelongsToTenant, SoftDeletes;
|
||||
|
||||
// 상태 코드
|
||||
public const STATUS_DRAFT = 'DRAFT';
|
||||
|
||||
public const STATUS_CONFIRMED = 'CONFIRMED';
|
||||
|
||||
public const STATUS_IN_PROGRESS = 'IN_PROGRESS';
|
||||
|
||||
public const STATUS_COMPLETED = 'COMPLETED';
|
||||
|
||||
public const STATUS_CANCELLED = 'CANCELLED';
|
||||
|
||||
// 주문 유형
|
||||
public const TYPE_ORDER = 'ORDER'; // 수주
|
||||
|
||||
public const TYPE_PURCHASE = 'PURCHASE'; // 발주
|
||||
|
||||
// 주문(견적/수주/발주 메인)
|
||||
protected $table = 'orders';
|
||||
|
||||
protected $fillable = [
|
||||
'tenant_id', 'order_no', 'order_type_code', 'status_code', 'category_code', 'item_id',
|
||||
'received_at', 'writer_id', 'client_id', 'client_contact', 'site_name', 'quantity', 'delivery_date',
|
||||
'delivery_method_code', 'memo',
|
||||
'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',
|
||||
// 감사
|
||||
'created_by',
|
||||
'updated_by',
|
||||
'deleted_by',
|
||||
];
|
||||
|
||||
// 상세(라인)
|
||||
public function items()
|
||||
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',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
'deleted_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* 수주 상세 품목
|
||||
*/
|
||||
public function items(): HasMany
|
||||
{
|
||||
return $this->hasMany(OrderItem::class);
|
||||
return $this->hasMany(OrderItem::class)->orderBy('sort_order');
|
||||
}
|
||||
|
||||
// 이력
|
||||
public function histories()
|
||||
/**
|
||||
* 수주 이력
|
||||
*/
|
||||
public function histories(): HasMany
|
||||
{
|
||||
return $this->hasMany(OrderHistory::class);
|
||||
}
|
||||
|
||||
// 버전관리
|
||||
public function versions()
|
||||
/**
|
||||
* 수주 버전
|
||||
*/
|
||||
public function versions(): HasMany
|
||||
{
|
||||
return $this->hasMany(OrderVersion::class);
|
||||
}
|
||||
|
||||
// 품목 (통합 items 테이블)
|
||||
public function item()
|
||||
/**
|
||||
* 원본 견적
|
||||
*/
|
||||
public function quote(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Quote::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 거래처
|
||||
*/
|
||||
public function client(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Client::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 품목 (통합 items 테이블)
|
||||
*/
|
||||
public function item(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Item::class, 'item_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 품목들로부터 금액 합계 재계산
|
||||
*/
|
||||
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,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,38 +3,181 @@
|
||||
namespace App\Models\Orders;
|
||||
|
||||
use App\Models\Items\Item;
|
||||
use App\Models\Quote\Quote;
|
||||
use App\Models\Quote\QuoteItem;
|
||||
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 SoftDeletes;
|
||||
use BelongsToTenant, SoftDeletes;
|
||||
|
||||
// 주문 상세
|
||||
protected $table = 'order_items';
|
||||
|
||||
protected $fillable = [
|
||||
'tenant_id', 'order_id', 'serial_no', 'item_id', 'quantity',
|
||||
'status_code', 'design_code', 'remarks', 'attributes',
|
||||
'tenant_id',
|
||||
'order_id',
|
||||
'quote_id',
|
||||
'quote_item_id',
|
||||
'serial_no',
|
||||
// 품목 정보
|
||||
'item_id',
|
||||
'item_code',
|
||||
'item_name',
|
||||
'specification',
|
||||
'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',
|
||||
];
|
||||
|
||||
// 투입 구성(자재/BOM 등)
|
||||
public function components()
|
||||
{
|
||||
return $this->hasMany(OrderItemComponent::class);
|
||||
}
|
||||
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()
|
||||
/**
|
||||
* 수주 마스터
|
||||
*/
|
||||
public function order(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Order::class);
|
||||
}
|
||||
|
||||
// 품목 (통합 items 테이블)
|
||||
public function item()
|
||||
/**
|
||||
* 품목 마스터
|
||||
*/
|
||||
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부터 시작)
|
||||
*/
|
||||
public static function createFromQuoteItem(QuoteItem $quoteItem, int $orderId, int $serialIndex = 1): 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,
|
||||
'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,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
use App\Models\Items\Item;
|
||||
use App\Models\Members\User;
|
||||
use App\Models\Orders\Client;
|
||||
use App\Models\Orders\Order;
|
||||
use App\Traits\BelongsToTenant;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
@@ -18,6 +19,7 @@ class Quote extends Model
|
||||
|
||||
protected $fillable = [
|
||||
'tenant_id',
|
||||
'order_id',
|
||||
'quote_number',
|
||||
'registration_date',
|
||||
'receipt_date',
|
||||
@@ -142,6 +144,14 @@ public function item(): BelongsTo
|
||||
return $this->belongsTo(Item::class, 'item_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 전환된 수주
|
||||
*/
|
||||
public function order(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Order::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 확정자
|
||||
*/
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace App\Services\Quote;
|
||||
|
||||
use App\Models\Orders\Order;
|
||||
use App\Models\Orders\OrderItem;
|
||||
use App\Models\Quote\Quote;
|
||||
use App\Models\Quote\QuoteItem;
|
||||
use App\Models\Quote\QuoteRevision;
|
||||
@@ -369,7 +371,10 @@ public function convertToOrder(int $id): Quote
|
||||
$tenantId = $this->tenantId();
|
||||
$userId = $this->apiUserId();
|
||||
|
||||
$quote = Quote::where('tenant_id', $tenantId)->find($id);
|
||||
$quote = Quote::where('tenant_id', $tenantId)
|
||||
->with(['items', 'client'])
|
||||
->find($id);
|
||||
|
||||
if (! $quote) {
|
||||
throw new NotFoundHttpException(__('error.quote_not_found'));
|
||||
}
|
||||
@@ -378,19 +383,69 @@ public function convertToOrder(int $id): Quote
|
||||
throw new BadRequestHttpException(__('error.quote_not_convertible'));
|
||||
}
|
||||
|
||||
return DB::transaction(function () use ($quote, $userId) {
|
||||
// TODO: 수주(Order) 생성 로직 구현
|
||||
// $order = $this->orderService->createFromQuote($quote);
|
||||
return DB::transaction(function () use ($quote, $userId, $tenantId) {
|
||||
// 수주번호 생성
|
||||
$orderNo = $this->generateOrderNumber($tenantId);
|
||||
|
||||
// 수주 마스터 생성
|
||||
$order = Order::createFromQuote($quote, $orderNo);
|
||||
$order->created_by = $userId;
|
||||
$order->save();
|
||||
|
||||
// 수주 상세 품목 생성
|
||||
$serialIndex = 1;
|
||||
foreach ($quote->items as $quoteItem) {
|
||||
$orderItem = OrderItem::createFromQuoteItem($quoteItem, $order->id, $serialIndex);
|
||||
$orderItem->created_by = $userId;
|
||||
$orderItem->save();
|
||||
$serialIndex++;
|
||||
}
|
||||
|
||||
// 수주 합계 재계산
|
||||
$order->load('items');
|
||||
$order->recalculateTotals();
|
||||
$order->save();
|
||||
|
||||
// 견적 상태 변경
|
||||
$quote->update([
|
||||
'status' => Quote::STATUS_CONVERTED,
|
||||
'order_id' => $order->id,
|
||||
'updated_by' => $userId,
|
||||
]);
|
||||
|
||||
return $quote->refresh()->load(['items', 'client']);
|
||||
return $quote->refresh()->load(['items', 'client', 'order']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 수주번호 생성
|
||||
* 형식: ORD-YYMMDD-NNN (예: ORD-260105-001)
|
||||
*/
|
||||
private function generateOrderNumber(int $tenantId): string
|
||||
{
|
||||
$dateStr = now()->format('ymd');
|
||||
$prefix = "ORD-{$dateStr}-";
|
||||
|
||||
$lastOrder = Order::withTrashed()
|
||||
->where('tenant_id', $tenantId)
|
||||
->where('order_no', 'like', $prefix.'%')
|
||||
->orderBy('order_no', 'desc')
|
||||
->first();
|
||||
|
||||
$sequence = 1;
|
||||
if ($lastOrder) {
|
||||
$parts = explode('-', $lastOrder->order_no);
|
||||
if (count($parts) >= 3) {
|
||||
$lastSeq = (int) end($parts);
|
||||
$sequence = $lastSeq + 1;
|
||||
}
|
||||
}
|
||||
|
||||
$seqStr = str_pad((string) $sequence, 3, '0', STR_PAD_LEFT);
|
||||
|
||||
return "{$prefix}{$seqStr}";
|
||||
}
|
||||
|
||||
/**
|
||||
* 견적 품목 생성
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* 수주 상세(order_items)에 금액 및 품목 정보 필드 추가
|
||||
* - 견적 → 수주 전환 시 품목별 금액 정보 저장
|
||||
* - 생산/출하/정산 플로우에서 품목별 추적 가능
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('order_items', function (Blueprint $table) {
|
||||
// 품목 정보 (quote_items와 동일 구조)
|
||||
$table->string('item_code', 50)->nullable()->after('item_id')->comment('품목코드');
|
||||
$table->string('item_name', 200)->nullable()->after('item_code')->comment('품명');
|
||||
$table->string('specification', 100)->nullable()->after('item_name')->comment('규격');
|
||||
$table->string('unit', 20)->default('EA')->after('specification')->comment('단위');
|
||||
|
||||
// 금액 정보
|
||||
$table->decimal('unit_price', 15, 2)->default(0)->after('quantity')->comment('단가');
|
||||
$table->decimal('supply_amount', 15, 2)->default(0)->after('unit_price')->comment('공급가액');
|
||||
$table->decimal('tax_amount', 15, 2)->default(0)->after('supply_amount')->comment('세액');
|
||||
$table->decimal('total_amount', 15, 2)->default(0)->after('tax_amount')->comment('금액 (공급가액 + 세액)');
|
||||
|
||||
// 할인 정보
|
||||
$table->decimal('discount_rate', 5, 2)->default(0)->after('total_amount')->comment('할인율 (%)');
|
||||
$table->decimal('discount_amount', 15, 2)->default(0)->after('discount_rate')->comment('할인금액');
|
||||
|
||||
// 추가 정보
|
||||
$table->text('note')->nullable()->after('remarks')->comment('비고');
|
||||
$table->unsignedInteger('sort_order')->default(0)->after('note')->comment('정렬순서');
|
||||
|
||||
// 견적 연결 (추적용)
|
||||
$table->unsignedBigInteger('quote_id')->nullable()->after('order_id')->comment('원본 견적 ID');
|
||||
$table->unsignedBigInteger('quote_item_id')->nullable()->after('quote_id')->comment('원본 견적 품목 ID');
|
||||
|
||||
// 인덱스
|
||||
$table->index('item_code', 'idx_order_items_item_code');
|
||||
$table->index('quote_id', 'idx_order_items_quote_id');
|
||||
$table->index('sort_order', 'idx_order_items_sort_order');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('order_items', function (Blueprint $table) {
|
||||
// 인덱스 삭제
|
||||
$table->dropIndex('idx_order_items_item_code');
|
||||
$table->dropIndex('idx_order_items_quote_id');
|
||||
$table->dropIndex('idx_order_items_sort_order');
|
||||
|
||||
// 컬럼 삭제
|
||||
$table->dropColumn([
|
||||
'item_code',
|
||||
'item_name',
|
||||
'specification',
|
||||
'unit',
|
||||
'unit_price',
|
||||
'supply_amount',
|
||||
'tax_amount',
|
||||
'total_amount',
|
||||
'discount_rate',
|
||||
'discount_amount',
|
||||
'note',
|
||||
'sort_order',
|
||||
'quote_id',
|
||||
'quote_item_id',
|
||||
]);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* 수주(orders)에 견적 연결 및 금액 필드 추가
|
||||
* - 견적 → 수주 전환 시 견적 정보 및 금액 저장
|
||||
* - 수주 기반 생산/출하 플로우 지원
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('orders', function (Blueprint $table) {
|
||||
// 견적 연결 (추적용)
|
||||
$table->unsignedBigInteger('quote_id')->nullable()->after('tenant_id')->comment('원본 견적 ID');
|
||||
|
||||
// 거래처 정보 (비정규화 - 조회 성능)
|
||||
$table->string('client_name', 200)->nullable()->after('client_id')->comment('거래처명');
|
||||
|
||||
// 금액 정보 (합계)
|
||||
$table->decimal('supply_amount', 15, 2)->default(0)->after('quantity')->comment('공급가액 합계');
|
||||
$table->decimal('tax_amount', 15, 2)->default(0)->after('supply_amount')->comment('세액 합계');
|
||||
$table->decimal('total_amount', 15, 2)->default(0)->after('tax_amount')->comment('총액 (공급가액 + 세액)');
|
||||
|
||||
// 할인 정보
|
||||
$table->decimal('discount_rate', 5, 2)->default(0)->after('total_amount')->comment('할인율 (%)');
|
||||
$table->decimal('discount_amount', 15, 2)->default(0)->after('discount_rate')->comment('할인금액');
|
||||
|
||||
// 추가 정보
|
||||
$table->text('remarks')->nullable()->after('memo')->comment('비고');
|
||||
$table->text('note')->nullable()->after('remarks')->comment('내부 메모');
|
||||
|
||||
// 인덱스
|
||||
$table->index('quote_id', 'idx_orders_quote_id');
|
||||
$table->index('client_name', 'idx_orders_client_name');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('orders', function (Blueprint $table) {
|
||||
// 인덱스 삭제
|
||||
$table->dropIndex('idx_orders_quote_id');
|
||||
$table->dropIndex('idx_orders_client_name');
|
||||
|
||||
// 컬럼 삭제
|
||||
$table->dropColumn([
|
||||
'quote_id',
|
||||
'client_name',
|
||||
'supply_amount',
|
||||
'tax_amount',
|
||||
'total_amount',
|
||||
'discount_rate',
|
||||
'discount_amount',
|
||||
'remarks',
|
||||
'note',
|
||||
]);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* 견적(quotes)에 수주 연결 필드 추가
|
||||
* - 견적 → 수주 전환 시 생성된 수주 ID 저장
|
||||
* - 전환된 견적 추적용
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('quotes', function (Blueprint $table) {
|
||||
$table->unsignedBigInteger('order_id')->nullable()->after('tenant_id')->comment('전환된 수주 ID');
|
||||
|
||||
$table->index('order_id', 'idx_quotes_order_id');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('quotes', function (Blueprint $table) {
|
||||
$table->dropIndex('idx_quotes_order_id');
|
||||
$table->dropColumn('order_id');
|
||||
});
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user