초기화

This commit is contained in:
2026-07-15 18:37:19 +09:00
commit 94abc5461d
1268 changed files with 380198 additions and 0 deletions
+221
View File
@@ -0,0 +1,221 @@
import math
import logging
from .db_manager import get_inventory_details_from_db
from setting.db_manager import get_all_settings
def _safe_float_conversion(value, default_value=None):
try:
if value is not None and value != '':
return float(value)
except (ValueError, TypeError):
pass
return default_value
def calculate_cost_analysis(inputs: dict, target_production_quantity: float = None) -> dict:
"""
원가 분석 계산 엔진 (순수 연산 비즈니스 로직)
"""
yarn_inventory_id = inputs.get("yarn_inventory_id")
bond_inventory_id = inputs.get("bond_inventory_id")
if not yarn_inventory_id:
raise ValueError("분석할 원사 재고가 선택되지 않았습니다.")
yarn_data_wrapper = get_inventory_details_from_db(yarn_inventory_id)
if not yarn_data_wrapper or "inventory" not in yarn_data_wrapper:
raise ValueError("선택된 원사의 물성 또는 단가 정보를 찾을 수 없습니다.")
yarn_data = yarn_data_wrapper["inventory"]
yarn_density_g_cm3 = _safe_float_conversion(yarn_data.get("density"), 1.0)
yarn_unit_cost = _safe_float_conversion(yarn_data.get("unit_cost"), 0.0)
bond_unit_cost = 0.0
if bond_inventory_id and bond_inventory_id != 'null':
bond_data_wrapper = get_inventory_details_from_db(bond_inventory_id)
if bond_data_wrapper and "inventory" in bond_data_wrapper:
bond_data = bond_data_wrapper["inventory"]
bond_unit_cost = _safe_float_conversion(bond_data.get("unit_cost"), 0.0)
yarn_diameter_micron = _safe_float_conversion(inputs.get("yarn_diameter_micron"), 7)
yarn_k = _safe_float_conversion(inputs.get("yarn_k"), 12) * 1000
ports = _safe_float_conversion(inputs.get("ports"), 40)
cycle_time_hz = _safe_float_conversion(inputs.get("cycle_time_hz"), 8)
cut_length_mm = _safe_float_conversion(inputs.get("cut_length_mm"), 6)
work_hours_day = _safe_float_conversion(inputs.get("work_hours_day"), 16)
work_days_month = _safe_float_conversion(inputs.get("work_days_month"), 20)
num_machines = _safe_float_conversion(inputs.get("num_machines"), 2)
bond_percentage = _safe_float_conversion(inputs.get("bond_percentage"), 3.0) / 100.0
yarn_radius_cm = (yarn_diameter_micron / 2) * 1e-4
yarn_cross_section_cm2 = math.pi * (yarn_radius_cm ** 2)
pure_yarn_weight_kg_per_m = (yarn_cross_section_cm2 * 100 * yarn_density_g_cm3) / 1000
cut_length_m = cut_length_mm / 1000
production_length_m_per_hour_per_mc = cycle_time_hz * 3600 * cut_length_m
pure_yarn_kg_per_hour_per_mc = production_length_m_per_hour_per_mc * pure_yarn_weight_kg_per_m * ports * yarn_k
if 0 < bond_percentage < 1.0:
total_kg_per_hour_per_mc = pure_yarn_kg_per_hour_per_mc / (1.0 - bond_percentage)
else:
total_kg_per_hour_per_mc = pure_yarn_kg_per_hour_per_mc
if total_kg_per_hour_per_mc <= 0:
raise ValueError("시간당 생산량이 0 이하여서 계산할 수 없습니다.")
settings = get_all_settings()
settings_dict = {f"{s['setting_group']}_{s['setting_key']}": s['setting_value'] for s in settings}
def get_s(key, default):
return _safe_float_conversion(settings_dict.get(key, default), default)
avg_power_per_machine_kw = get_s("CostAnalysis_AvgPowerPerMachine", 5000) / 1000
electricity_price_kwh = get_s("CostAnalysis_ElectricityUnitPricePerKWH", 94.4)
hourly_power_cost = avg_power_per_machine_kw * electricity_price_kwh
max_machines_per_person = get_s("CostAnalysis_MaxMachinesPerPerson", 10)
num_people_needed = math.ceil(num_machines / max_machines_per_person)
monthly_labor_cost_person = get_s("CostAnalysis_MonthlyLaborCostPerPerson", 6000000)
meal_cost_day = get_s("CostAnalysis_MealCostPerDay", 15000)
hourly_labor_cost = (num_people_needed * monthly_labor_cost_person + num_people_needed * meal_cost_day * work_days_month) / (work_days_month * work_hours_day * num_machines)
packaging_cost_kg = get_s("CostAnalysis_PackagingCostPerKg", 500)
packaging_weight_kg = get_s("CostAnalysis_PackagingBaseWeightKg", 25)
hourly_packaging_cost = (total_kg_per_hour_per_mc / packaging_weight_kg) * packaging_cost_kg if packaging_weight_kg > 0 else 0
monthly_rent = get_s("CostAnalysis_MonthlyRentCost", 0)
monthly_admin = get_s("CostAnalysis_MonthlyGeneralAdminCost", 50000)
monthly_delivery = get_s("CostAnalysis_MonthlyDeliveryCost", 300000)
hourly_overhead_cost = (monthly_rent + monthly_admin + monthly_delivery) / (work_days_month * work_hours_day * num_machines)
mass_mc_cost = get_s("Investment_MassProductionEquipmentCost", 10000000)
dep_mass_mc = get_s("Investment_DepreciationPeriodMassProduction", 3) * 12
office_cost = get_s("Investment_OfficeSuppliesCost", 5000000)
dep_office = get_s("Investment_DepreciationPeriodOfficeSupplies", 3) * 12
proto_cost = get_s("Investment_ProtoEquipmentCost", 5000000)
monthly_depreciation = (mass_mc_cost / dep_mass_mc if dep_mass_mc > 0 else 0) + \
(office_cost / dep_office if dep_office > 0 else 0) + \
(proto_cost / dep_office if dep_office > 0 else 0)
hourly_depreciation = monthly_depreciation / (work_days_month * work_hours_day)
hourly_net_cost_per_machine = hourly_power_cost + hourly_labor_cost + hourly_packaging_cost + hourly_overhead_cost + hourly_depreciation
company_margin_rate = get_s("CostAnalysis_CompanyMarginRate", 30) / 100.0
standard_processing_cost_kg = get_s("CostAnalysis_StandardProcessingCost", 0)
pure_processing_cost_kg = hourly_net_cost_per_machine / total_kg_per_hour_per_mc if total_kg_per_hour_per_mc > 0 else 0
calculated_profit_margin_kg = 0
if pure_processing_cost_kg > 0:
if pure_processing_cost_kg * (1 + company_margin_rate) < standard_processing_cost_kg:
calculated_profit_margin_kg = standard_processing_cost_kg - pure_processing_cost_kg
else:
calculated_profit_margin_kg = pure_processing_cost_kg * company_margin_rate
hourly_profit_per_machine = calculated_profit_margin_kg * total_kg_per_hour_per_mc
analysis_result = {}
total_kg_hour_all_mc = total_kg_per_hour_per_mc * num_machines
total_kg_day_all_mc = total_kg_hour_all_mc * work_hours_day
total_kg_month = total_kg_day_all_mc * work_days_month
monthly_power_cost = hourly_power_cost * work_hours_day * work_days_month * num_machines
monthly_labor_cost_total = hourly_labor_cost * work_hours_day * work_days_month * num_machines
monthly_packaging_cost = hourly_packaging_cost * work_hours_day * work_days_month * num_machines
monthly_overhead_cost = hourly_overhead_cost * work_hours_day * work_days_month * num_machines
monthly_depreciation_cost_total = hourly_depreciation * work_hours_day * work_days_month * num_machines
monthly_total_expenses = monthly_power_cost + monthly_labor_cost_total + monthly_packaging_cost + monthly_overhead_cost + monthly_depreciation_cost_total
pure_yarn_kg_per_hour_all_mc = pure_yarn_kg_per_hour_per_mc * num_machines
required_yarn_kg_month = (pure_yarn_kg_per_hour_all_mc / total_kg_hour_all_mc) * total_kg_month if total_kg_hour_all_mc > 0 else 0
bond_kg_per_hour_all_mc = (total_kg_hour_all_mc - pure_yarn_kg_per_hour_all_mc)
required_bond_kg_month = (bond_kg_per_hour_all_mc / total_kg_hour_all_mc) * total_kg_month if total_kg_hour_all_mc > 0 else 0
current_yarn_stock = _safe_float_conversion(yarn_data.get("stock"), 0.0)
purchase_order_yarn_kg = max(0, required_yarn_kg_month - current_yarn_stock)
current_bond_stock = 0.0
purchase_order_bond_kg = 0.0
if bond_inventory_id and bond_inventory_id != 'null':
bond_data_wrapper = get_inventory_details_from_db(bond_inventory_id)
if bond_data_wrapper and "inventory" in bond_data_wrapper:
current_bond_stock = _safe_float_conversion(bond_data_wrapper["inventory"].get("stock"), 0.0)
purchase_order_bond_kg = max(0, required_bond_kg_month - current_bond_stock)
total_yarn_purchase_cost = required_yarn_kg_month * yarn_unit_cost
total_bond_purchase_cost = required_bond_kg_month * bond_unit_cost
total_raw_material_purchase_cost = total_yarn_purchase_cost + total_bond_purchase_cost
analysis_result.update({
"production_kg_per_hour": total_kg_hour_all_mc,
"production_kg_per_day": total_kg_day_all_mc,
"production_ton_per_month": total_kg_month / 1000,
"total_product_kg_per_month": total_kg_month,
"pure_processing_cost_per_kg": pure_processing_cost_kg,
"calculated_profit_margin_per_kg": calculated_profit_margin_kg,
"processing_cost_per_kg_total": pure_processing_cost_kg + calculated_profit_margin_kg,
"current_yarn_stock": current_yarn_stock,
"required_yarn_kg": required_yarn_kg_month,
"purchase_order_kg": purchase_order_yarn_kg,
"yarn_unit_cost": yarn_unit_cost,
"current_bond_stock": current_bond_stock,
"required_bond_kg": required_bond_kg_month,
"purchase_order_bond_kg": purchase_order_bond_kg,
"bond_unit_cost": bond_unit_cost,
"total_monthly_expenses": monthly_total_expenses,
"monthly_power_cost": monthly_power_cost,
"total_monthly_kwh": (avg_power_per_machine_kw * num_machines * work_hours_day * work_days_month),
"monthly_labor_cost": monthly_labor_cost_total,
"monthly_meal_cost": (num_people_needed * meal_cost_day * work_days_month),
"monthly_packaging_cost": monthly_packaging_cost,
"monthly_depreciation_cost": monthly_depreciation_cost_total,
"monthly_rent_cost": monthly_rent,
"monthly_general_admin_cost_setting": monthly_admin,
"monthly_delivery_cost": monthly_delivery,
"total_revenue": total_raw_material_purchase_cost + ((pure_processing_cost_kg + calculated_profit_margin_kg) * total_kg_month)
})
# 2. 목표 생산량 분석 (요청 시 추가 수행)
if target_production_quantity and target_production_quantity > 0:
total_work_hours_target = target_production_quantity / (total_kg_hour_all_mc if total_kg_hour_all_mc > 0 else 1)
work_days_target = total_work_hours_target / work_hours_day
required_yarn_kg_target = (pure_yarn_kg_per_hour_all_mc / total_kg_hour_all_mc) * target_production_quantity if total_kg_hour_all_mc > 0 else 0
purchase_order_yarn_kg_target = max(0, required_yarn_kg_target - current_yarn_stock)
required_bond_kg_target = (bond_kg_per_hour_all_mc / total_kg_hour_all_mc) * target_production_quantity if total_kg_hour_all_mc > 0 else 0
purchase_order_bond_kg_target = max(0, required_bond_kg_target - current_bond_stock)
plan_yarn_cost = required_yarn_kg_target * yarn_unit_cost
plan_bond_cost = required_bond_kg_target * bond_unit_cost
plan_total_material_cost = plan_yarn_cost + plan_bond_cost
plan_net_processing_cost = hourly_net_cost_per_machine * num_machines * total_work_hours_target
plan_company_margin = hourly_profit_per_machine * num_machines * total_work_hours_target
plan_processing_fee = plan_net_processing_cost + plan_company_margin
plan_estimated_sales = plan_processing_fee + plan_total_material_cost
analysis_result.update({
"required_days_target": work_days_target,
"current_yarn_stock_target": current_yarn_stock,
"required_yarn_kg_target": required_yarn_kg_target,
"purchase_order_kg_target": purchase_order_yarn_kg_target,
"current_bond_stock_target": current_bond_stock,
"required_bond_kg_target": required_bond_kg_target,
"purchase_order_bond_kg_target": purchase_order_bond_kg_target,
"plan_yarn_cost": plan_yarn_cost,
"plan_bond_cost": plan_bond_cost,
"plan_total_material_cost": plan_total_material_cost,
"plan_net_processing_cost": plan_net_processing_cost,
"plan_company_margin": plan_company_margin,
"plan_processing_fee": plan_processing_fee,
"plan_estimated_sales": plan_estimated_sales
})
analysis_result.update({
"hourly_net_cost_per_machine": hourly_net_cost_per_machine,
"hourly_profit_per_machine": hourly_profit_per_machine
})
return analysis_result