163 lines
5.7 KiB
Python
163 lines
5.7 KiB
Python
from fastapi import APIRouter, HTTPException
|
|
from pydantic import BaseModel
|
|
from typing import List, Dict, Any, Optional
|
|
from .db_manager import (
|
|
add_production_plan,
|
|
add_equipment_assignments_to_plan,
|
|
get_all_production_plans,
|
|
find_available_equipment,
|
|
update_plan_assignment,
|
|
delete_production_plan,
|
|
adjust_and_distribute_assignments,
|
|
update_production_plan
|
|
)
|
|
from .helper import calculate_end_date
|
|
|
|
router = APIRouter(prefix="/plans", tags=["plans"])
|
|
|
|
# --- Pydantic Schemas ---
|
|
class PlanAssignmentSchema(BaseModel):
|
|
assignment_id: Optional[int] = None
|
|
plan_id: Optional[int] = None
|
|
equipment_id: int
|
|
start_date: str
|
|
end_date: str
|
|
works_on_saturday: bool
|
|
works_on_sunday: bool
|
|
|
|
class PlanSchema(BaseModel):
|
|
plan_id: Optional[int] = None
|
|
plan_name: str
|
|
customer_name: Optional[str] = None
|
|
customer_id: Optional[int] = None
|
|
yarn_name: Optional[str] = None
|
|
yarn_inventory_id: int
|
|
bond_name: Optional[str] = None
|
|
bond_inventory_id: Optional[int] = None
|
|
requested_quantity_kg: float
|
|
start_date: str
|
|
end_date: Optional[str] = None
|
|
production_days: float
|
|
status: Optional[str] = "Scheduled"
|
|
|
|
# Cost & Margin properties
|
|
hourly_net_cost_per_machine: float
|
|
hourly_profit_per_machine: float
|
|
plan_total_material_cost: float
|
|
yarn_diameter: float
|
|
yarn_k: float
|
|
ports: float
|
|
cycle_time: float
|
|
cut_length: float
|
|
work_hours_day: float
|
|
work_days_month: float
|
|
num_machines: float
|
|
plan_net_processing_cost: float
|
|
plan_company_margin: float
|
|
plan_processing_fee: float
|
|
plan_yarn_cost: float
|
|
plan_bond_cost: float
|
|
plan_estimated_sales: float
|
|
|
|
# Machine allocations
|
|
equipment_ids: List[int]
|
|
works_on_saturday: Optional[bool] = False
|
|
works_on_sunday: Optional[bool] = False
|
|
|
|
class AssignmentUpdatePayload(BaseModel):
|
|
assignment_id: int
|
|
start_date: str
|
|
end_date: str
|
|
works_on_saturday: bool
|
|
works_on_sunday: bool
|
|
|
|
class RedistributionPayload(BaseModel):
|
|
source_assignment: Dict[str, Any]
|
|
distribution: Dict[str, int]
|
|
|
|
# --- Plan APIs ---
|
|
@router.post("/", response_model=dict)
|
|
def create_production_plan(plan: PlanSchema):
|
|
plan_data = plan.dict()
|
|
assigned_machine_ids = plan.equipment_ids
|
|
start_date_str = plan.start_date
|
|
required_days = plan.production_days
|
|
|
|
works_on_saturday = plan.works_on_saturday
|
|
works_on_sunday = plan.works_on_sunday
|
|
|
|
if not assigned_machine_ids or not start_date_str or required_days is None:
|
|
raise HTTPException(status_code=400, detail="Missing required parameters: equipment_ids, start_date, production_days")
|
|
|
|
# 주말 가동 플래그를 고려하여 가용 완료 날짜 계산
|
|
end_date_str = calculate_end_date(start_date_str, required_days, works_on_saturday, works_on_sunday)
|
|
plan_data["end_date"] = end_date_str
|
|
|
|
plan_id = add_production_plan(plan_data)
|
|
if not plan_id:
|
|
raise HTTPException(status_code=500, detail="Failed to add production plan to DB")
|
|
|
|
# 설비 할당 정보 생성
|
|
assignments = [
|
|
{
|
|
'equipment_id': eq_id,
|
|
'start_date': start_date_str,
|
|
'end_date': end_date_str,
|
|
'works_on_saturday': works_on_saturday,
|
|
'works_on_sunday': works_on_sunday
|
|
} for eq_id in assigned_machine_ids
|
|
]
|
|
|
|
assign_success = add_equipment_assignments_to_plan(plan_id, assignments)
|
|
if not assign_success:
|
|
raise HTTPException(status_code=500, detail="Plan was created but equipment assignments failed.")
|
|
|
|
return {"plan_id": plan_id, "status": "success"}
|
|
|
|
@router.put("/{plan_id}", response_model=dict)
|
|
def edit_production_plan(plan_id: int, plan: PlanSchema):
|
|
plan_data = plan.dict()
|
|
assigned_machine_ids = plan.equipment_ids
|
|
start_date_str = plan.start_date
|
|
required_days = plan.production_days
|
|
|
|
works_on_saturday = plan.works_on_saturday
|
|
works_on_sunday = plan.works_on_sunday
|
|
|
|
end_date_str = calculate_end_date(start_date_str, required_days, works_on_saturday, works_on_sunday)
|
|
plan_data["end_date"] = end_date_str
|
|
|
|
success = update_production_plan(plan_id, plan_data, assigned_machine_ids)
|
|
if not success:
|
|
raise HTTPException(status_code=500, detail="Failed to update production plan")
|
|
return {"status": "success"}
|
|
|
|
@router.delete("/{plan_id}", response_model=dict)
|
|
def delete_plan(plan_id: int):
|
|
success = delete_production_plan(plan_id)
|
|
if not success:
|
|
raise HTTPException(status_code=500, detail="Failed to delete production plan")
|
|
return {"status": "success"}
|
|
|
|
@router.get("/", response_model=List[dict])
|
|
def list_production_plans():
|
|
return get_all_production_plans()
|
|
|
|
@router.get("/find-available-equipment", response_model=List[dict])
|
|
def get_available_equipment(start_date: str, end_date: str, works_on_saturday: bool = False, works_on_sunday: bool = False):
|
|
return find_available_equipment(start_date, end_date, works_on_saturday, works_on_sunday)
|
|
|
|
@router.put("/{plan_id}/assignments", response_model=dict)
|
|
def update_assignment(plan_id: int, payload: AssignmentUpdatePayload):
|
|
success = update_plan_assignment(plan_id, payload.dict())
|
|
if not success:
|
|
raise HTTPException(status_code=500, detail="Failed to update assignment")
|
|
return {"status": "success"}
|
|
|
|
@router.post("/{plan_id}/assignments/redistribute", response_model=dict)
|
|
def redistribute_assignments(plan_id: int, payload: RedistributionPayload):
|
|
success = adjust_and_distribute_assignments(plan_id, payload.source_assignment, payload.distribution)
|
|
if not success:
|
|
raise HTTPException(status_code=500, detail="Failed to redistribute assignments")
|
|
return {"status": "success"}
|