from __future__ import annotations

from copy import copy
import hashlib
import threading
import tkinter as tk
import uuid
from datetime import datetime
from pathlib import Path
from tkinter import filedialog, messagebox, ttk
from urllib.parse import quote, urlparse, urlsplit, urlunsplit
from urllib.request import Request

from openpyxl import Workbook, load_workbook
from openpyxl.drawing.image import Image as XLImage
from openpyxl.styles import Alignment, Border, Font, PatternFill, Side

from app_core.paths import DATA_DIR, DB_PATH
from app_core.filter_helpers import create_extra_filter_row, destroy_filter_row, regrid_filter_rows
from app_core.http_client import open_url
from app_core.server_api import fetch_app_setting, save_app_setting
from app_core.ui_helpers import configure_striped_treeview, create_contained_photo, resolve_image_source
from database import get_app_setting, normalize_style_identifier, normalize_workflow_status, parse_active_flag, set_app_setting


COSTING_SORT_KEY_OPTIONS = [
    ("collection", "costing_collection_column"),
    ("season", "costing_season_column"),
    ("dept_number", "costing_dept_column"),
    ("mj_class", "costing_mj_class_column"),
    ("sub_class", "costing_sub_class_column"),
    ("buyer", "buyer_name"),
    ("style_code", "costing_style_column"),
    ("manf_color", "costing_manf_color_column"),
    ("color_code", "costing_color_code_column"),
    ("product_type", "costing_product_type_column"),
    ("ticket_type", "costing_ticket_type_column"),
    ("packing_type", "costing_packing_type_column"),
    ("fabrication", "costing_fabrication_column"),
    ("size_range", "costing_size_range_column"),
    ("size_scale", "costing_size_scale_column"),
    ("allocation_multiple", "costing_allocation_multiple_column"),
    ("min_qty", "costing_min_column"),
    ("max_qty", "costing_max_column"),
    ("inner_pack", "costing_inner_column"),
    ("outer_pack", "costing_master_pack_column"),
    ("hanger_supplier", "costing_hanger_supplier_column"),
    ("hanger_type", "costing_hanger_type_column"),
    ("item_retail", "costing_item_retail_column"),
    ("po_number", "costing_po_number_column"),
    ("total_units", "costing_total_units_column"),
    ("description", "costing_description_column"),
    ("is_active", "active_status"),
    ("status", "status"),
    ("option", "costing_option_column"),
]

COSTING_FILTER_FIELD_OPTIONS = [
    ("__all__", "samples_filter_all_option"),
    ("collection", "costing_collection_column"),
    ("season", "costing_season_column"),
    ("dept_number", "costing_dept_column"),
    ("mj_class", "costing_mj_class_column"),
    ("sub_class", "costing_sub_class_column"),
    ("buyer", "buyer_name"),
    ("style_code", "costing_style_column"),
    ("description", "costing_description_column"),
    ("fabrication", "costing_fabrication_column"),
    ("manf_color", "costing_manf_color_column"),
    ("color_code", "costing_color_code_column"),
    ("product_type", "costing_product_type_column"),
    ("ticket_type", "costing_ticket_type_column"),
    ("packing_type", "costing_packing_type_column"),
    ("total_units", "costing_total_units_column"),
    ("allocation_multiple", "costing_allocation_multiple_column"),
    ("min_qty", "costing_min_column"),
    ("max_qty", "costing_max_column"),
    ("inner_pack", "costing_inner_column"),
    ("outer_pack", "costing_master_pack_column"),
    ("size_range", "costing_size_range_column"),
    ("size_scale", "costing_size_scale_column"),
    ("hanger_supplier", "costing_hanger_supplier_column"),
    ("hanger_type", "costing_hanger_type_column"),
    ("item_retail", "costing_item_retail_column"),
    ("po_number", "costing_po_number_column"),
    ("is_active", "active_status"),
    ("status", "status"),
    ("option", "costing_option_column"),
    ("created_at", "create_time_column"),
    ("updated_at", "revised_time_column"),
]

COSTING_DATE_FILTER_FIELDS = {"created_at", "updated_at"}
COSTING_ALL_FILTER_FIELDS = [field for field, _text_key in COSTING_FILTER_FIELD_OPTIONS if field != "__all__"]

COSTING_FILTER_DEBOUNCE_MS = 180
COSTING_FORM_RECORDS_KEY = "costing_form_records"
COSTING_FORM_ASSETS_KEY = "costing_form_assets"
COSTING_FORM_TEMPLATE_BUCKET = "costing-form-assets"
COSTING_FORM_TEMPLATE_FILENAME = "SUNNY_COSTING_FORM_TEMPLATE.xlsx"
COSTING_FORM_LOGISTICS_DEFAULTS = ("Duty Rate", "Exim", "LA warehouse")
COSTING_FORM_LABOUR_DEFAULTS = ("Sewing Labor", "Printing", "Washing", "Transfer")
COSTING_FORM_COST_UNIT_OPTIONS = ("%", "USD", "VND")
COSTING_FORM_CURRENCY_OPTIONS = ("USD", "VND")
COSTING_FORM_UOM_OPTIONS = ("EA", "SET", "CONE", "MM", "CM", "M", "INCH", "YARD", "G", "KG", "LB", "OZ")
COSTING_FORM_OPTION_VALUES = ("1", "2", "3", "4", "5")


def _costing_form_now_text() -> str:
    return datetime.now().strftime("%Y-%m-%d %H:%M:%S")


def _costing_safe_float(value: object) -> float:
    text = str(value or "").strip().replace(",", "").replace("$", "")
    text = text.replace("%", "")
    if not text or text in {"-", "$-"}:
        return 0.0
    try:
        return float(text)
    except Exception:
        return 0.0


def _costing_percent_float(value: object) -> float:
    text = str(value or "").strip()
    if not text:
        return 0.0
    numeric = _costing_safe_float(text)
    if "%" in text or abs(numeric) > 1:
        return numeric / 100
    return numeric


def _costing_format_number(value: float) -> str:
    numeric = round(float(value or 0.0), 4)
    if abs(numeric - round(numeric)) < 1e-9:
        return str(int(round(numeric)))
    return f"{numeric:.4f}".rstrip("0").rstrip(".")


def _costing_line_cost(line: dict) -> float:
    consumption = _costing_safe_float(line.get("consumption"))
    loss_percent = _costing_percent_float(line.get("loss_percent"))
    price = _costing_safe_float(line.get("price"))
    return consumption * (1 + loss_percent) * price


def _costing_charge_value(line: dict, base_value: float = 0.0, value_key: str = "costing_value") -> float:
    value = _costing_safe_float(line.get(value_key))
    unit = str(line.get("unit", "")).strip().upper()
    if unit == "%":
        return base_value * value / 100
    return value


def default_costing_form_assets() -> dict[str, object]:
    return {"template_url": ""}


def default_costing_form_record(style_code: object = "") -> dict[str, object]:
    return {
        "style_code": normalize_style_identifier(style_code),
        "option": "1",
        "option_name": "Option 1",
        "status": "draft",
        "qty": "",
        "offer_date": "",
        "confirm_date": "",
        "buyer_target": "",
        "buyer_confirm": "",
        "approved_by": "",
        "approved_date": "",
        "image_path": "",
        "material_lines": [],
        "actual_lines": [],
        "logistics_lines": [
            {"name": name, "costing_value": "", "actual_value": "", "unit": "", "note": ""}
            for name in COSTING_FORM_LOGISTICS_DEFAULTS
        ],
        "labour_lines": [
            {"name": name, "costing_value": "", "actual_value": "", "unit": "USD", "note": ""}
            for name in COSTING_FORM_LABOUR_DEFAULTS
        ],
        "created_at": "",
        "updated_at": "",
    }


def normalize_costing_form_line(payload: object) -> dict:
    row = dict(payload) if isinstance(payload, dict) else {}
    return {
        "item_code": str(row.get("item_code", "")).strip(),
        "name": str(row.get("name") or row.get("item_name") or row.get("bom_item_name") or "").strip(),
        "type": str(row.get("type") or row.get("fabric_type") or "").strip(),
        "section": str(row.get("section", "")).strip(),
        "color": str(row.get("color", "")).strip(),
        "consumption": str(row.get("consumption", "")).strip(),
        "uom": str(row.get("uom", "")).strip(),
        "loss_percent": str(row.get("loss_percent") or row.get("waste_percent") or "").strip(),
        "price": str(row.get("price") or row.get("standard_price") or "").strip(),
        "currency": str(row.get("currency", "")).strip().upper(),
        "cost": str(row.get("cost", "")).strip(),
        "note": str(row.get("note", "")).strip(),
    }


def normalize_costing_form_record(payload: object, style_code: object = "") -> dict[str, object]:
    normalized = default_costing_form_record(style_code)
    if isinstance(payload, dict):
        for key in normalized:
            if key in {"material_lines", "actual_lines", "logistics_lines", "labour_lines"}:
                continue
            normalized[key] = str(payload.get(key, normalized[key]) or "").strip()
        normalized["material_lines"] = [
            normalize_costing_form_line(line)
            for line in list(payload.get("material_lines", []))
            if isinstance(line, dict)
        ]
        normalized["actual_lines"] = [
            normalize_costing_form_line(line)
            for line in list(payload.get("actual_lines", []))
            if isinstance(line, dict)
        ]
        logistics_lines = []
        for line in list(payload.get("logistics_lines", [])):
            if not isinstance(line, dict):
                continue
            logistics_lines.append(
                {
                    "name": str(line.get("name", "")).strip(),
                    "costing_value": str(line.get("costing_value", "")).strip(),
                    "actual_value": str(line.get("actual_value", "")).strip(),
                    "unit": str(line.get("unit", "")).strip().upper(),
                    "note": str(line.get("note", "")).strip(),
                }
            )
        if logistics_lines:
            normalized["logistics_lines"] = logistics_lines
        labour_lines = []
        for line in list(payload.get("labour_lines", [])):
            if not isinstance(line, dict):
                continue
            labour_lines.append(
                {
                    "name": str(line.get("name", "")).strip(),
                    "costing_value": str(line.get("costing_value", "")).strip(),
                    "actual_value": str(line.get("actual_value", "")).strip(),
                    "unit": str(line.get("unit", "")).strip().upper() or "USD",
                    "note": str(line.get("note", "")).strip(),
                }
            )
        if labour_lines:
            normalized["labour_lines"] = labour_lines
        normalized["style_code"] = normalize_style_identifier(normalized.get("style_code") or style_code)
    option_value = str(normalized.get("option", "") or "").strip()
    if option_value not in COSTING_FORM_OPTION_VALUES:
        option_value = "1"
    normalized["option"] = option_value
    normalized["option_name"] = str(normalized.get("option_name", "") or "").strip() or f"Option {option_value}"
    normalized["status"] = normalize_workflow_status(normalized.get("status"), "draft")
    return normalized


def _costing_form_image_path_from_source(source: dict) -> str:
    for key in ("image_path", "cover_image", "thumbnail_path"):
        value = str(source.get(key, "") or "").strip()
        if value:
            return value
    preview_images = source.get("preview_images", [])
    if isinstance(preview_images, list):
        for value in preview_images:
            text = str(value or "").strip()
            if text:
                return text
    return ""


def _build_costing_module(self, parent: ttk.Frame) -> None:
    parent.columnconfigure(0, weight=1)
    parent.rowconfigure(2, weight=1)

    toolbar = ttk.Frame(parent)
    toolbar.grid(row=0, column=0, sticky="ew")
    self.widget_text["costing_import_ws_button"] = ttk.Button(toolbar, command=self.import_costing_worksheet)
    self.widget_text["costing_import_ws_button"].pack(side="left")
    self.register_permission_widget("action.costing.import_ws", self.widget_text["costing_import_ws_button"])
    self.widget_text["costing_import_folder_button"] = ttk.Button(toolbar, command=self.import_costing_folder)
    self.widget_text["costing_import_folder_button"].pack(side="left", padx=(8, 0))
    self.register_permission_widget("action.costing.import_folder", self.widget_text["costing_import_folder_button"])
    self.widget_text["costing_export_report_button"] = ttk.Button(
        toolbar,
        command=lambda: self.export_report("costing"),
    )
    self.widget_text["costing_export_report_button"].pack(side="left", padx=(8, 0))
    self.register_permission_widget("action.costing.export_report", self.widget_text["costing_export_report_button"])
    self.widget_text["costing_delete_button"] = ttk.Button(toolbar, command=self.delete_selected_costing_styles)
    self.widget_text["costing_delete_button"].pack(side="left", padx=(8, 0))
    self.register_permission_widget("action.costing.delete_style", self.widget_text["costing_delete_button"])
    self.widget_text["costing_edit_button"] = ttk.Button(toolbar, command=self.edit_selected_costing_row)
    self.widget_text["costing_edit_button"].pack(side="left", padx=(8, 0))
    self.register_permission_widget("action.costing.edit_row", self.widget_text["costing_edit_button"])
    self.widget_text["costing_copy_button"] = ttk.Button(toolbar, text="Copy", command=self.copy_selected_costing_option)
    self.widget_text["costing_copy_button"].pack(side="left", padx=(8, 0))
    self.register_permission_widget("action.costing.edit_row", self.widget_text["costing_copy_button"])
    self.widget_text["costing_collection_list_button"] = ttk.Button(toolbar, command=self.open_collection_list_dialog)
    self.widget_text["costing_collection_list_button"].pack(side="left", padx=(8, 0))
    self.register_permission_widget("action.costing.collection_list", self.widget_text["costing_collection_list_button"])
    self.widget_text["costing_form_button"] = ttk.Button(toolbar, text=self.t("costing_form_button"), command=self.export_selected_costing_form)
    self.widget_text["costing_form_button"].pack(side="left", padx=(8, 0))
    self.register_permission_widget("action.costing.export_report", self.widget_text["costing_form_button"])

    meta = ttk.Frame(parent)
    meta.grid(row=1, column=0, sticky="ew", pady=(12, 14))
    meta.columnconfigure(14, weight=1)
    self.costing_filter_controls = meta
    self.costing_filter_rows = []
    self.widget_text["costing_filter_field_label"] = ttk.Label(meta)
    self.widget_text["costing_filter_field_label"].grid(row=0, column=0, sticky="w", padx=(0, 6))
    self.costing_filter_field_combo = ttk.Combobox(meta, textvariable=self.costing_filter_field_var, state="readonly", width=22)
    self.costing_filter_field_combo.grid(row=0, column=1, sticky="w")
    self.costing_filter_field_combo.bind("<<ComboboxSelected>>", self.on_costing_filter_field_changed)
    self.widget_text["costing_filter_value_label"] = ttk.Label(meta)
    self.widget_text["costing_filter_value_label"].grid(row=0, column=2, sticky="w", padx=(12, 6))
    self.costing_search_entry = ttk.Entry(meta, textvariable=self.costing_search_var, width=36)
    self.costing_search_entry.grid(row=0, column=3, sticky="w", padx=(0, 12))
    self.costing_search_entry.bind("<Return>", self.on_costing_search_submit)
    self.costing_search_entry.bind("<KP_Enter>", self.on_costing_search_submit)
    self.widget_text["costing_date_from_label"] = ttk.Label(meta)
    self.widget_text["costing_date_from_label"].grid(row=0, column=4, sticky="w", padx=(0, 6))
    self.costing_date_from_entry = ttk.Entry(meta, textvariable=self.costing_date_from_var, width=12)
    self.costing_date_from_entry.grid(row=0, column=5, sticky="w", padx=(0, 12))
    self.bind_date_entry_picker(self.costing_date_from_entry, self.costing_date_from_var)
    self.costing_date_from_entry.bind("<Return>", self.on_costing_search_submit)
    self.costing_date_from_entry.bind("<KP_Enter>", self.on_costing_search_submit)
    self.widget_text["costing_date_to_label"] = ttk.Label(meta)
    self.widget_text["costing_date_to_label"].grid(row=0, column=6, sticky="w", padx=(0, 6))
    self.costing_date_to_entry = ttk.Entry(meta, textvariable=self.costing_date_to_var, width=12)
    self.costing_date_to_entry.grid(row=0, column=7, sticky="w")
    self.bind_date_entry_picker(self.costing_date_to_entry, self.costing_date_to_var)
    self.costing_date_to_entry.bind("<Return>", self.on_costing_search_submit)
    self.costing_date_to_entry.bind("<KP_Enter>", self.on_costing_search_submit)
    self.widget_text["costing_sort_by_label"] = ttk.Label(meta)
    self.widget_text["costing_sort_by_label"].grid(row=0, column=8, sticky="w", padx=(12, 6))
    self.costing_sort_key_combo = ttk.Combobox(
        meta,
        textvariable=self.costing_sort_key_var,
        state="readonly",
        width=18,
    )
    self.costing_sort_key_combo.grid(row=0, column=9, sticky="w")
    self.costing_sort_key_combo.bind("<<ComboboxSelected>>", self.on_costing_sort_key_changed)
    self.widget_text["costing_sort_order_label"] = ttk.Label(meta)
    self.widget_text["costing_sort_order_label"].grid(row=0, column=10, sticky="w", padx=(12, 6))
    self.costing_sort_order_combo = ttk.Combobox(
        meta,
        textvariable=self.costing_sort_order_var,
        state="readonly",
        width=12,
    )
    self.costing_sort_order_combo.grid(row=0, column=11, sticky="w")
    self.costing_sort_order_combo.bind("<<ComboboxSelected>>", self.on_costing_sort_order_changed)
    self.widget_text["costing_add_filter_button"] = ttk.Button(meta, text=self.t("po_add_filter"), command=self.add_costing_filter_row)
    self.widget_text["costing_add_filter_button"].grid(row=0, column=12, sticky="w", padx=(12, 0))
    self.widget_text["costing_clear_filters_button"] = ttk.Button(meta, text=self.t("po_clear_filters"), command=self.clear_costing_filters)
    self.widget_text["costing_clear_filters_button"].grid(row=0, column=13, sticky="w", padx=(8, 0))
    self.update_costing_filter_input_state()
    self.widget_text["costing_source_file_label"] = ttk.Label(meta, style="FieldLabel.TLabel")
    self.widget_text["costing_source_file_label"].grid(row=2, column=0, sticky="nw", padx=(0, 8), pady=(10, 0))
    self.costing_source_file_var = tk.StringVar(value="-")
    self.costing_source_file_value = ttk.Label(meta, textvariable=self.costing_source_file_var, wraplength=1020, justify="left")
    self.costing_source_file_value.grid(row=2, column=1, columnspan=3, sticky="w", pady=(10, 0))

    body = ttk.Panedwindow(parent, orient=tk.HORIZONTAL)
    body.grid(row=2, column=0, sticky="nsew")
    self.costing_pane = body

    left = ttk.Frame(body, padding=(0, 0, 12, 0))
    right = ttk.Frame(body)
    body.add(left, weight=3)
    body.add(right, weight=4)

    self.widget_text["costing_styles_section"] = ttk.Label(left, style="Section.TLabel")
    self.widget_text["costing_styles_section"].pack(anchor="w", pady=(0, 8))

    costing_table = ttk.Frame(left)
    costing_table.pack(fill="both", expand=True)
    costing_table.columnconfigure(0, weight=1)
    costing_table.rowconfigure(0, weight=1)
    self.costing_tree = ttk.Treeview(
        costing_table,
        columns=(
            "created_at",
            "select_flag",
            "collection",
            "season",
            "dept_number",
            "mj_class",
            "sub_class",
            "buyer",
            "style_code",
            "description",
            "fabrication",
            "manf_color",
            "color_code",
            "product_type",
            "ticket_type",
            "packing_type",
            "size_range",
            "size_scale",
            "allocation_multiple",
            "min_qty",
            "max_qty",
            "inner_pack",
            "outer_pack",
            "hanger_supplier",
            "hanger_type",
            "item_retail",
            "po_number",
            "total_units",
            "is_active",
            "status",
            "option",
            "updated_at",
            "latest_user",
        ),
        show="tree headings",
        selectmode="browse",
        style="Costing.Treeview",
    )
    self.costing_tree.column("#0", width=96, anchor="center", stretch=False)
    self.costing_tree.column("select_flag", width=60, anchor="center", stretch=False)
    self.costing_tree.column("created_at", width=145, anchor="center", stretch=False)
    self.costing_tree.column("collection", width=120, anchor="w", stretch=False)
    self.costing_tree.column("season", width=110, anchor="center", stretch=False)
    self.costing_tree.column("dept_number", width=70, anchor="center", stretch=False)
    self.costing_tree.column("mj_class", width=80, anchor="center", stretch=False)
    self.costing_tree.column("sub_class", width=80, anchor="center", stretch=False)
    self.costing_tree.column("buyer", width=90, anchor="w", stretch=False)
    self.costing_tree.column("style_code", width=100, anchor="w", stretch=False)
    self.costing_tree.column("description", width=220, anchor="w", stretch=False)
    self.costing_tree.column("fabrication", width=140, anchor="w", stretch=False)
    self.costing_tree.column("manf_color", width=120, anchor="w", stretch=False)
    self.costing_tree.column("color_code", width=90, anchor="center", stretch=False)
    self.costing_tree.column("product_type", width=110, anchor="w", stretch=False)
    self.costing_tree.column("ticket_type", width=110, anchor="w", stretch=False)
    self.costing_tree.column("packing_type", width=90, anchor="center", stretch=False)
    self.costing_tree.column("size_range", width=90, anchor="center", stretch=False)
    self.costing_tree.column("size_scale", width=90, anchor="center", stretch=False)
    self.costing_tree.column("allocation_multiple", width=120, anchor="center", stretch=False)
    self.costing_tree.column("min_qty", width=70, anchor="e", stretch=False)
    self.costing_tree.column("max_qty", width=70, anchor="e", stretch=False)
    self.costing_tree.column("inner_pack", width=90, anchor="center", stretch=False)
    self.costing_tree.column("outer_pack", width=90, anchor="center", stretch=False)
    self.costing_tree.column("hanger_supplier", width=130, anchor="w", stretch=False)
    self.costing_tree.column("hanger_type", width=110, anchor="w", stretch=False)
    self.costing_tree.column("item_retail", width=90, anchor="e", stretch=False)
    self.costing_tree.column("po_number", width=110, anchor="w", stretch=False)
    self.costing_tree.column("total_units", width=90, anchor="e", stretch=False)
    self.costing_tree.column("is_active", width=90, anchor="center", stretch=False)
    self.costing_tree.column("status", width=90, anchor="center", stretch=False)
    self.costing_tree.column("option", width=90, anchor="center", stretch=False)
    self.costing_tree.column("updated_at", width=145, anchor="center", stretch=False)
    self.costing_tree.column("latest_user", width=140, anchor="w", stretch=False)
    self.costing_tree.heading("#0", command=lambda: None)
    self.costing_tree.heading("select_flag", command=lambda: self.on_costing_heading_click("select_flag"))
    self.costing_tree.heading("created_at", command=lambda: self.on_costing_heading_click("created_at"))
    self.costing_tree.heading("collection", command=lambda: self.on_costing_heading_click("collection"))
    self.costing_tree.heading("season", command=lambda: self.on_costing_heading_click("season"))
    self.costing_tree.heading("dept_number", command=lambda: self.on_costing_heading_click("dept_number"))
    self.costing_tree.heading("mj_class", command=lambda: self.on_costing_heading_click("mj_class"))
    self.costing_tree.heading("sub_class", command=lambda: self.on_costing_heading_click("sub_class"))
    self.costing_tree.heading("buyer", command=lambda: self.on_costing_heading_click("buyer"))
    self.costing_tree.heading("style_code", command=lambda: self.on_costing_heading_click("style_code"))
    self.costing_tree.heading("description", command=lambda: self.on_costing_heading_click("description"))
    self.costing_tree.heading("fabrication", command=lambda: self.on_costing_heading_click("fabrication"))
    self.costing_tree.heading("manf_color", command=lambda: self.on_costing_heading_click("manf_color"))
    self.costing_tree.heading("color_code", command=lambda: self.on_costing_heading_click("color_code"))
    self.costing_tree.heading("product_type", command=lambda: self.on_costing_heading_click("product_type"))
    self.costing_tree.heading("ticket_type", command=lambda: self.on_costing_heading_click("ticket_type"))
    self.costing_tree.heading("packing_type", command=lambda: self.on_costing_heading_click("packing_type"))
    self.costing_tree.heading("size_range", command=lambda: self.on_costing_heading_click("size_range"))
    self.costing_tree.heading("size_scale", command=lambda: self.on_costing_heading_click("size_scale"))
    self.costing_tree.heading("allocation_multiple", command=lambda: self.on_costing_heading_click("allocation_multiple"))
    self.costing_tree.heading("min_qty", command=lambda: self.on_costing_heading_click("min_qty"))
    self.costing_tree.heading("max_qty", command=lambda: self.on_costing_heading_click("max_qty"))
    self.costing_tree.heading("inner_pack", command=lambda: self.on_costing_heading_click("inner_pack"))
    self.costing_tree.heading("outer_pack", command=lambda: self.on_costing_heading_click("outer_pack"))
    self.costing_tree.heading("hanger_supplier", command=lambda: self.on_costing_heading_click("hanger_supplier"))
    self.costing_tree.heading("hanger_type", command=lambda: self.on_costing_heading_click("hanger_type"))
    self.costing_tree.heading("item_retail", command=lambda: self.on_costing_heading_click("item_retail"))
    self.costing_tree.heading("po_number", command=lambda: self.on_costing_heading_click("po_number"))
    self.costing_tree.heading("total_units", command=lambda: self.on_costing_heading_click("total_units"))
    self.costing_tree.heading("is_active", command=lambda: self.on_costing_heading_click("is_active"))
    self.costing_tree.heading("status", command=lambda: self.on_costing_heading_click("status"))
    self.costing_tree.heading("option", text="Option", command=lambda: self.on_costing_heading_click("option"))
    self.costing_tree.heading("updated_at", command=lambda: self.on_costing_heading_click("updated_at"))
    self.costing_tree.heading("latest_user", command=lambda: self.on_costing_heading_click("latest_user"))
    self.costing_tree.heading("latest_user", text=self.t("latest_user_column"), command=lambda: self.on_costing_heading_click("latest_user"))
    costing_scroll = ttk.Scrollbar(costing_table, orient="vertical", command=self.costing_tree.yview)
    costing_scroll_x = ttk.Scrollbar(costing_table, orient="horizontal", command=self.costing_tree.xview)
    self.costing_tree.configure(yscrollcommand=costing_scroll.set, xscrollcommand=costing_scroll_x.set)
    configure_striped_treeview(self.costing_tree)
    self.costing_tree.grid(row=0, column=0, sticky="nsew")
    costing_scroll.grid(row=0, column=1, sticky="ns")
    costing_scroll_x.grid(row=1, column=0, sticky="ew")
    self.costing_tree.bind("<<TreeviewSelect>>", self.on_costing_item_selected)
    self.costing_tree.bind("<Button-1>", self.on_costing_tree_click)
    self.costing_tree.bind("<Double-1>", self.on_costing_tree_double_click)

    self.widget_text["costing_preview_section"] = ttk.Label(right, style="Section.TLabel")
    self.widget_text["costing_preview_section"].pack(anchor="w", pady=(0, 8))

    colors_row = ttk.Frame(right)
    colors_row.pack(fill="x", pady=(0, 8))
    self.widget_text["costing_colors_label"] = ttk.Label(colors_row, style="FieldLabel.TLabel")
    self.widget_text["costing_colors_label"].pack(side="left")
    self.costing_variant_buttons_frame = ttk.Frame(colors_row)
    self.costing_variant_buttons_frame.pack(side="left", padx=(8, 0))

    self.costing_image_label = ttk.Label(right, anchor="center")
    self.costing_image_label.pack(fill="both", expand=False)

    self.widget_text["costing_description_label"] = ttk.Label(right, style="FieldLabel.TLabel")
    self.widget_text["costing_description_label"].pack(anchor="w", pady=(12, 4))
    self.costing_description_text = tk.Text(right, wrap="word", height=10)
    self.costing_description_text.pack(fill="both", expand=True)

    self.costing_status_label = ttk.Label(
        parent,
        textvariable=self.status_var,
        relief="sunken",
        anchor="w",
        justify="left",
        padding=8,
        wraplength=1180,
    )
    self.costing_status_label.grid(row=3, column=0, sticky="ew", pady=(12, 0))


def create_costing_thumbnail(self, item: dict):
    image_path = self.resolve_costing_item_image_path(item)
    if not image_path:
        return None
    return create_contained_photo(image_path, target_width=84, target_height=84)


def resolve_costing_item_image_path(self, item: dict) -> str | None:
    variants = item.get("variants", [])
    for variant in variants:
        image_path = str(variant.get("image_path", "")).strip()
        if image_path:
            return image_path

    style_code = str(item.get("style_code", "")).strip()
    tech_pack_row = self.get_tech_pack_row_by_style_code(style_code)
    if tech_pack_row is None:
        return None

    tech_pack_image_path = str(tech_pack_row.get("image_path", "")).strip()
    if tech_pack_image_path:
        return tech_pack_image_path
    return None


def get_costing_check_display(self, item: dict) -> str:
    group_key = tuple(item.get("group_key", ("", "", "")))
    return "[x]" if group_key in self.checked_costing_keys else "[ ]"


def populate_costing_module(self) -> None:
    data = self.costing_data or {}
    self.costing_source_file_var.set(self.display_value(data.get("source_pdf")))
    self.apply_costing_filter()
    self.update_report_target_summary()
    self.update_costing_module_status()


def expand_costing_items_by_option(self, items: list[dict]) -> list[dict]:
    expanded_rows: list[dict] = []
    for item in list(items or []):
        if not isinstance(item, dict):
            continue
        style_code = normalize_style_identifier(item.get("style_code"))
        records = self.get_costing_form_records_for_style(style_code)
        if not records:
            records = [default_costing_form_record(style_code)]
        base_group_key = tuple(item.get("group_key", ("", "", "")))
        for record in records:
            option_value = str(record.get("option", "") or "1").strip()
            row = dict(item)
            row["base_group_key"] = base_group_key
            row["group_key"] = (*base_group_key, f"option:{option_value}")
            row["option"] = option_value
            row["option_name"] = str(record.get("option_name", "") or f"Option {option_value}").strip()
            row["status"] = normalize_workflow_status(record.get("status") or item.get("status"), "draft")
            updated_at = str(record.get("updated_at", "") or "").strip()
            if updated_at:
                row["updated_at"] = updated_at
            expanded_rows.append(row)
    return expanded_rows


def refresh_costing_tree(self, selected_id: str | None = None) -> None:
    current_selection = self.costing_tree.selection()
    if selected_id is None and current_selection:
        selected_id = current_selection[0]

    for item_id in self.costing_tree.get_children():
        self.costing_tree.delete(item_id)
    self.costing_tree.tag_configure("costing_status_draft", background="#ffffff")
    self.costing_tree.tag_configure("costing_status_checking", background="#fff7cc")
    self.costing_tree.tag_configure("costing_status_approved", background="#dff5e3")
    self.costing_thumbnail_photos = []
    thumbnail_limit = self.get_startup_thumbnail_render_limit("costing") if hasattr(self, "get_startup_thumbnail_render_limit") else None
    rendered_thumbnails = 0

    for index, item in enumerate(self.filtered_costing_items, start=1):
        thumbnail_photo = None
        if thumbnail_limit is None or index <= thumbnail_limit:
            thumbnail_photo = self.create_costing_thumbnail(item)
            if thumbnail_photo is not None:
                self.costing_thumbnail_photos.append(thumbnail_photo)
                rendered_thumbnails += 1
        row_values = (
            str(self.display_value(item.get("created_at"))),
            str(self.get_costing_check_display(item)),
            str(self.display_value(item.get("collection"))),
            str(self.display_value(item.get("season"))),
            str(self.display_value(item.get("dept_number"))),
            str(self.display_value(item.get("mj_class"))),
            str(self.display_value(item.get("sub_class"))),
            str(self.display_value(item.get("buyer"))),
            str(self.display_value(item.get("style_code"))),
            str(self.display_value(item.get("description"))).replace("\n", " | "),
            str(self.display_value(item.get("fabrication"))),
            str(self.display_value(item.get("manf_color"))),
            str(self.display_value(item.get("color_code"))),
            str(self.display_value(item.get("product_type"))),
            str(self.display_value(item.get("ticket_type"))),
            str(self.display_value(item.get("packing_type"))),
            str(self.display_value(item.get("size_range"))),
            str(self.display_value(item.get("size_scale"))),
            str(self.display_value(item.get("allocation_multiple"))),
            str(self.display_value(item.get("min_qty"))),
            str(self.display_value(item.get("max_qty"))),
            str(self.display_value(item.get("inner_pack"))),
            str(self.display_value(item.get("outer_pack"))),
            str(self.display_value(item.get("hanger_supplier"))),
            str(self.display_value(item.get("hanger_type"))),
            str(self.display_value(item.get("item_retail"))),
            str(self.display_value(item.get("po_number"))),
            str(self.display_value(item.get("total_units"))),
            "Active" if bool(item.get("is_active", True)) else "Inactive",
            str(self.display_value(item.get("status"))),
            str(self.display_value(item.get("option") or "1")),
            str(self.display_value(item.get("updated_at"))),
            str(self.display_value(item.get("latest_user_display"))),
        )
        insert_kwargs: dict[str, object] = {
            "text": "",
            "values": row_values,
            "tags": (
                {
                    "checking": "costing_status_checking",
                    "approved": "costing_status_approved",
                }.get(str(item.get("status", "")).strip().lower(), "costing_status_draft"),
            ),
        }
        if thumbnail_photo is not None:
            insert_kwargs["image"] = thumbnail_photo
        self.costing_tree.insert("", "end", str(index), **insert_kwargs)

    valid_keys = {tuple(item.get("group_key", ("", "", ""))) for item in self.filtered_costing_items}
    self.checked_costing_keys.intersection_update(valid_keys)

    if selected_id and self.costing_tree.exists(selected_id):
        self.costing_tree.selection_set(selected_id)
        self.costing_tree.focus(selected_id)
        self.on_costing_item_selected(None)
    elif self.filtered_costing_items:
        self.costing_tree.selection_set("1")
        self.costing_tree.focus("1")
        self.load_costing_item(0)
    else:
        self.costing_selected_index = None
        for child in self.costing_variant_buttons_frame.winfo_children():
            child.destroy()
        self.costing_image_label.configure(image="", text=self.t("costing_no_preview"))
        self.costing_description_text.delete("1.0", tk.END)
        self.costing_description_text.insert("1.0", self.t("costing_empty"))
    if hasattr(self, "note_startup_thumbnail_render_state"):
        self.note_startup_thumbnail_render_state(
            "costing",
            total_rows=len(self.filtered_costing_items),
            rendered_rows=min(len(self.filtered_costing_items), thumbnail_limit) if thumbnail_limit is not None else len(self.filtered_costing_items),
        )
    self.update_costing_module_status()


def update_costing_tree_check_state(self, row_id: str | None = None) -> None:
    if not hasattr(self, "costing_tree"):
        return
    target_ids = [row_id] if row_id else list(self.costing_tree.get_children())
    for item_id in target_ids:
        if not self.costing_tree.exists(item_id):
            continue
        try:
            index = int(item_id) - 1
        except (TypeError, ValueError):
            continue
        if index < 0 or index >= len(self.filtered_costing_items):
            continue
        item = self.filtered_costing_items[index]
        values = list(self.costing_tree.item(item_id, "values"))
        if len(values) < 2:
            continue
        values[1] = self.get_costing_check_display(item)
        self.costing_tree.item(item_id, values=tuple(values))
    self.update_costing_module_status()


def apply_costing_filter(self) -> None:
    if getattr(self, "_costing_filter_update_suspended", False):
        return
    active_filters = self.get_active_costing_filters()
    self.filtered_costing_items = self.expand_costing_items_by_option(list(self.costing_items))
    if active_filters:
        self.filtered_costing_items = [
            item
            for item in self.filtered_costing_items
            if all(self.costing_row_matches_filter(item, filter_item) for filter_item in active_filters)
        ]

    sort_key = self.costing_sort_key_internal
    reverse = self.costing_sort_order_internal == "desc"

    def sort_value(item: dict) -> object:
        value = item.get("option") if sort_key == "option" else item.get(sort_key)
        if value is None:
            return ""
        return self.parse_costing_sort_value(sort_key, value)

    self.filtered_costing_items.sort(key=sort_value, reverse=reverse)
    if hasattr(self, "costing_tree"):
        self.refresh_costing_tree()
    self.update_report_target_summary()
    self.update_costing_module_status()


def commit_costing_search_query(self, value: object | None = None, *, apply_filter: bool = True) -> None:
    if value is not None:
        self.costing_search_var.set(str(value or "").strip())
    self.costing_committed_filter_field_internal = getattr(self, "costing_filter_field_internal", "__all__")
    self.costing_committed_query = self.costing_search_var.get().strip()
    self.costing_committed_date_from = self.costing_date_from_var.get().strip()
    self.costing_committed_date_to = self.costing_date_to_var.get().strip()
    for row_data in list(getattr(self, "costing_filter_rows", []) or []):
        row_data["committed_field_internal"] = str(row_data.get("field_internal", "__all__") or "__all__")
        row_data["committed_query"] = row_data["value_var"].get().strip()
        row_data["committed_from"] = row_data["from_var"].get().strip()
        row_data["committed_to"] = row_data["to_var"].get().strip()
    if apply_filter:
        self.apply_costing_filter()


def on_costing_search_submit(self, _event: object | None = None) -> str:
    self.commit_costing_search_query()
    return "break"


def sync_costing_search_query(self, value: object) -> None:
    normalized_value = str(value or "").strip()
    self.costing_search_var.set(normalized_value)


def schedule_costing_filter_apply(self) -> None:
    self.commit_costing_search_query()


def run_costing_filter_apply(self) -> None:
    self.apply_costing_filter()


def costing_filter_field_value(self, item: dict, field: str) -> object:
    if field == "option":
        return item.get("option") or "1"
    if field == "is_active":
        return "active" if bool(item.get("is_active", True)) else "inactive"
    return item.get(field)


def costing_filter_haystack(self, item: dict) -> str:
    values = [self.display_value(self.costing_filter_field_value(item, field)) for field in COSTING_ALL_FILTER_FIELDS]
    values.append(" ".join(self.display_value(variant.get("description")) for variant in item.get("variants", [])))
    return " ".join(values).lower()


def costing_row_matches_filter(self, item: dict, filter_item: dict) -> bool:
    field = str(filter_item.get("field", "__all__") or "__all__")
    query = str(filter_item.get("query", "") or "").strip().lower()
    from_text = str(filter_item.get("from", "") or "").strip()
    to_text = str(filter_item.get("to", "") or "").strip()
    if field in COSTING_DATE_FILTER_FIELDS and (from_text or to_text):
        return bool(self.is_within_date_range(self.costing_filter_field_value(item, field), from_text, to_text))
    if not query:
        return True
    if field == "__all__":
        return query in self.costing_filter_haystack(item)
    cell_value = self.display_value(self.costing_filter_field_value(item, field)).strip().lower()
    if field == "is_active":
        return cell_value == query
    return query in cell_value


def get_active_costing_filters(self) -> list[dict]:
    filters: list[dict] = []
    primary_field = str(getattr(self, "costing_committed_filter_field_internal", getattr(self, "costing_filter_field_internal", "__all__")) or "__all__")
    primary_filter = {
        "field": primary_field,
        "query": str(getattr(self, "costing_committed_query", "") or "").strip(),
        "from": str(getattr(self, "costing_committed_date_from", "") or "").strip(),
        "to": str(getattr(self, "costing_committed_date_to", "") or "").strip(),
    }
    if primary_filter["query"] or primary_filter["from"] or primary_filter["to"]:
        filters.append(primary_filter)
    for row_data in list(getattr(self, "costing_filter_rows", []) or []):
        row_filter = {
            "field": str(row_data.get("committed_field_internal", row_data.get("field_internal", "__all__")) or "__all__"),
            "query": str(row_data.get("committed_query", "") or "").strip(),
            "from": str(row_data.get("committed_from", "") or "").strip(),
            "to": str(row_data.get("committed_to", "") or "").strip(),
        }
        if row_filter["query"] or row_filter["from"] or row_filter["to"]:
            filters.append(row_filter)
    return filters


def add_costing_filter_row(self) -> None:
    rows = getattr(self, "costing_filter_rows", [])
    row_data = create_extra_filter_row(
        self,
        module_key="costing",
        parent=self.costing_filter_controls,
        options=COSTING_FILTER_FIELD_OPTIONS,
        date_fields=COSTING_DATE_FILTER_FIELDS,
        row_index=1 + len(rows),
        remove_command=self.remove_costing_filter_row,
        submit_command=self.on_costing_search_submit,
        translate_filter_field=self.translate_costing_filter_field,
    )
    rows.append(row_data)
    self.costing_filter_rows = rows


def remove_costing_filter_row(self, row_data: dict) -> None:
    rows = getattr(self, "costing_filter_rows", [])
    if row_data in rows:
        rows.remove(row_data)
    destroy_filter_row(row_data)
    regrid_filter_rows(rows, start_row=1)
    self.commit_costing_search_query()


def clear_costing_filters(self) -> None:
    self._costing_filter_update_suspended = True
    try:
        self.costing_filter_field_internal = "__all__"
        self.costing_filter_field_var.set(self.translate_costing_filter_field("__all__"))
        self.costing_search_var.set("")
        self.costing_date_from_var.set("")
        self.costing_date_to_var.set("")
        self.costing_committed_filter_field_internal = "__all__"
        self.costing_committed_query = ""
        self.costing_committed_date_from = ""
        self.costing_committed_date_to = ""
        for row_data in list(getattr(self, "costing_filter_rows", []) or []):
            destroy_filter_row(row_data)
        self.costing_filter_rows = []
    finally:
        self._costing_filter_update_suspended = False
    self.update_costing_filter_input_state()
    self.apply_costing_filter()


def update_costing_filter_input_state(self, *, clear_irrelevant: bool = False) -> None:
    filter_field = getattr(self, "costing_filter_field_internal", "__all__")
    is_date_field = filter_field in COSTING_DATE_FILTER_FIELDS
    if hasattr(self, "costing_search_entry"):
        self.costing_search_entry.configure(state="disabled" if is_date_field else "normal")
    if hasattr(self, "costing_date_from_entry"):
        self.costing_date_from_entry.configure(state="normal" if is_date_field else "disabled")
    if hasattr(self, "costing_date_to_entry"):
        self.costing_date_to_entry.configure(state="normal" if is_date_field else "disabled")
    if clear_irrelevant:
        self._costing_filter_update_suspended = True
        try:
            if is_date_field:
                self.sync_costing_search_query("")
            else:
                self.costing_date_from_var.set("")
                self.costing_date_to_var.set("")
        finally:
            self._costing_filter_update_suspended = False


def on_costing_item_selected(self, _event: object) -> None:
    selection = self.costing_tree.selection()
    if not selection:
        return
    index = int(selection[0]) - 1
    self.load_costing_item(index)
    self.update_costing_module_status()


def load_costing_item(self, index: int) -> None:
    if index < 0 or index >= len(self.filtered_costing_items):
        return

    self.costing_selected_index = index
    self.costing_selected_variant_index = 0
    item = self.filtered_costing_items[index]
    self.render_costing_variant_buttons(item)
    self.load_costing_variant(item, self.costing_selected_variant_index)


def on_costing_tree_click(self, event: tk.Event) -> str | None:
    region = self.costing_tree.identify_region(event.x, event.y)
    if region != "cell":
        return None
    column = self.costing_tree.identify_column(event.x)
    if column != "#2":
        return None
    row_id = self.costing_tree.identify_row(event.y)
    if not row_id:
        return None

    index = int(row_id) - 1
    if index < 0 or index >= len(self.filtered_costing_items):
        return "break"

    item = self.filtered_costing_items[index]
    group_key = tuple(item.get("group_key", ("", "", "")))
    if group_key in self.checked_costing_keys:
        self.checked_costing_keys.remove(group_key)
    else:
        self.checked_costing_keys.add(group_key)
    self.update_costing_tree_check_state(row_id)
    self.costing_tree.selection_set(row_id)
    self.costing_tree.focus(row_id)
    return "break"


def on_costing_tree_double_click(self, _event: object | None = None) -> str:
    self.edit_selected_costing_row()
    return "break"


def render_costing_variant_buttons(self, item: dict) -> None:
    for child in self.costing_variant_buttons_frame.winfo_children():
        child.destroy()

    variants = item.get("variants", [])
    for variant_index, variant in enumerate(variants):
        button = ttk.Button(
            self.costing_variant_buttons_frame,
            text=self.display_value(variant.get("color_label")),
            command=lambda idx=variant_index: self.load_costing_variant(item, idx),
        )
        button.pack(side="left", padx=(0, 6))


def load_costing_variant(self, item: dict, variant_index: int) -> None:
    variants = item.get("variants", [])
    if variant_index < 0 or variant_index >= len(variants):
        return
    self.costing_selected_variant_index = variant_index

    variant = variants[variant_index]
    preview_image = None
    image_path = str(variant.get("image_path", "")).strip()
    if image_path:
        preview_image = create_contained_photo(image_path, target_width=420, target_height=420)
    elif variant_index == 0:
        fallback_image_path = self.resolve_costing_item_image_path(item)
        if fallback_image_path:
            preview_image = create_contained_photo(fallback_image_path, target_width=420, target_height=420)
    if preview_image is not None:
        self.costing_preview_photo = preview_image
        self.costing_image_label.configure(image=preview_image, text="")
    else:
        self.costing_preview_photo = None
        self.costing_image_label.configure(image="", text=self.t("tech_pack_no_image"))

    self.costing_description_text.configure(state="normal")
    self.costing_description_text.delete("1.0", tk.END)
    source_kind = str(variant.get("source_kind", "")).strip().lower()
    if source_kind == "worksheet":
        detail_lines = [
            f"Source: {self.t('costing_source_kind_worksheet')}",
            f"Style: {self.display_value(variant.get('style_code'))}",
            f"Color: {self.display_value(variant.get('manf_color') or variant.get('color_label'))}",
            f"Color Code: {self.display_value(variant.get('color_code'))}",
            f"Product Type: {self.display_value(variant.get('product_type'))}",
            f"Ticket Type: {self.display_value(variant.get('ticket_type'))}",
            f"Packing Type: {self.display_value(variant.get('packing_type'))}",
            f"Description: {self.display_value(variant.get('description'))}",
            f"Fabrication: {self.display_value(variant.get('fabrication'))}",
            f"Brand: {self.display_value(variant.get('brand'))}",
            f"Dept #: {self.display_value(variant.get('dept_number'))}",
            f"Mj Class: {self.display_value(variant.get('mj_class'))}",
            f"Sub Class: {self.display_value(variant.get('sub_class'))}",
            f"Size Range: {self.display_value(variant.get('size_range'))}",
            f"Size Scale: {self.display_value(variant.get('size_scale'))}",
            f"Allocation Multiple: {self.display_value(variant.get('allocation_multiple'))}",
            f"Min: {self.display_value(variant.get('min_qty'))}",
            f"Max: {self.display_value(variant.get('max_qty'))}",
            f"Total Units: {self.display_value(variant.get('total_units'))}",
            f"Inner Pack: {self.display_value(variant.get('inner_pack'))}",
            f"Master Pack: {self.display_value(variant.get('outer_pack'))}",
            f"Hanger Supplier: {self.display_value(variant.get('hanger_supplier'))}",
            f"Hanger Type: {self.display_value(variant.get('hanger_type'))}",
            f"Item Retail: {self.display_value(variant.get('item_retail'))}",
            f"PO #: {self.display_value(variant.get('po_number'))}",
            f"Est. CBM: {self.display_value(variant.get('est_cbm'))}",
            f"Item Type for Freight: {self.display_value(variant.get('item_type_for_freight'))}",
            f"Workbook: {self.display_value(variant.get('source_workbook') or variant.get('source_pdf'))}",
            f"Sheet: {self.display_value(variant.get('source_sheet'))}",
            f"Row: {self.display_value(variant.get('source_row_number'))}",
        ]
        self.costing_description_text.insert("1.0", "\n".join(detail_lines))
    else:
        description = self.display_value(variant.get("description"))
        self.costing_description_text.insert("1.0", description)


def delete_selected_costing_styles(self) -> None:
    if not self.is_action_allowed("action.costing.delete_style"):
        return
    selected_keys = sorted(self.checked_costing_keys)
    if not selected_keys:
        return
    selected_items = [
        item
        for item in self.filtered_costing_items
        if tuple(item.get("group_key", ("", "", ""))) in selected_keys
    ]
    if not messagebox.askyesno(
        self.t("costing_delete_style"),
        self.t("costing_delete_confirm", count=len(selected_keys)),
    ):
        return

    option_specs = [
        (normalize_style_identifier(item.get("style_code")), str(item.get("option", "") or "").strip())
        for item in selected_items
        if str(item.get("option", "") or "").strip()
    ]
    if option_specs:
        deleted_count = self.delete_costing_form_options_from_source(option_specs)
        self.checked_costing_keys.clear()
        self.apply_costing_filter()
        self.set_status(f"Deleted {deleted_count} costing option(s).")
        return

    selected_items = [
        item
        for item in self.costing_items
        if tuple(item.get("group_key", ("", "", ""))) in selected_keys
    ]

    delete_specs: list[object] = []
    for item in selected_items:
        variants = list(item.get("variants", []))
        first_variant = variants[0] if variants else {}
        if str(first_variant.get("source_kind", "")).strip().lower() == "worksheet":
            delete_specs.append(
                {
                    "source_kind": "worksheet",
                    "collection": item.get("collection"),
                    "season": item.get("source_season", item.get("season")),
                    "style_code": item.get("style_code"),
                    "source_workbook": first_variant.get("source_workbook") or first_variant.get("source_pdf"),
                    "source_pdf": first_variant.get("source_pdf"),
                    "source_sheet": first_variant.get("source_sheet"),
                    "source_row_number": first_variant.get("source_row_number"),
                }
            )
        else:
            delete_specs.append(
                (
                    item.get("collection"),
                    item.get("source_season", item.get("season")),
                    item.get("style_code"),
                )
            )

    self.delete_costing_styles_from_source(delete_specs)
    self.checked_costing_keys.clear()
    self.load_saved_costing_imports()
    self.set_status(self.t("costing_delete_style"))


def get_selected_costing_item_and_variant(self) -> tuple[dict | None, dict | None]:
    if self.costing_selected_index is None or self.costing_selected_index >= len(self.filtered_costing_items):
        return None, None
    item = self.filtered_costing_items[self.costing_selected_index]
    variants = list(item.get("variants", []))
    variant = variants[self.costing_selected_variant_index] if 0 <= self.costing_selected_variant_index < len(variants) else (variants[0] if variants else None)
    return dict(item), dict(variant) if isinstance(variant, dict) else None


def load_costing_form_records_from_source(self) -> list[dict]:
    default_rows: list[dict] = []
    rows: list[dict] = []
    if self.should_use_server_data_source():
        if self.is_local_data_mode():
            self.ensure_local_server_running()
        try:
            payload = fetch_app_setting(self.server_settings, COSTING_FORM_RECORDS_KEY, default_rows)
            rows = [normalize_costing_form_record(item) for item in list(payload or []) if isinstance(item, dict)]
            self._costing_form_records_cache = rows
            return rows
        except Exception:
            pass
    payload = get_app_setting(DB_PATH, COSTING_FORM_RECORDS_KEY, default_rows)
    rows = [normalize_costing_form_record(item) for item in list(payload or []) if isinstance(item, dict)]
    self._costing_form_records_cache = rows
    return rows


def save_costing_form_record_to_source(self, record: dict) -> dict:
    normalized = normalize_costing_form_record(record, record.get("style_code"))
    style_code = str(normalized.get("style_code", "")).strip()
    if not style_code:
        return {}
    option_value = str(normalized.get("option", "") or "1").strip()
    target_status = normalize_workflow_status(normalized.get("status"), "draft")
    existing_rows = self.load_costing_form_records_from_source()
    merged_rows: list[dict] = []
    replaced = False
    for row in existing_rows:
        row_style = normalize_style_identifier(row.get("style_code"))
        row_option = str(row.get("option", "") or "1").strip()
        if row_style == style_code and row_option == option_value:
            merged_rows.append(normalized)
            replaced = True
        else:
            row_copy = normalize_costing_form_record(row, row_style)
            if row_style == style_code and target_status == "approved" and normalize_workflow_status(row_copy.get("status"), "draft") == "approved":
                row_copy["status"] = "checking"
            merged_rows.append(row_copy)
    if not replaced:
        merged_rows.append(normalized)
    if self.should_use_server_data_source():
        if self.is_local_data_mode():
            self.ensure_local_server_running()
        saved = save_app_setting(self.server_settings, COSTING_FORM_RECORDS_KEY, merged_rows)
        rows = [normalize_costing_form_record(item) for item in list(saved or []) if isinstance(item, dict)]
    else:
        saved = set_app_setting(DB_PATH, COSTING_FORM_RECORDS_KEY, merged_rows)
        rows = [normalize_costing_form_record(item) for item in list(saved or []) if isinstance(item, dict)]
    self._costing_form_records_cache = rows
    return next(
        (
            row
            for row in rows
            if normalize_style_identifier(row.get("style_code")) == style_code
            and str(row.get("option", "") or "1").strip() == option_value
        ),
        normalized,
    )


def delete_costing_form_options_from_source(self, option_specs: list[tuple[object, object]]) -> int:
    delete_pairs = {
        (normalize_style_identifier(style_code), str(option_value or "").strip())
        for style_code, option_value in list(option_specs or [])
        if normalize_style_identifier(style_code) and str(option_value or "").strip()
    }
    if not delete_pairs:
        return 0
    existing_rows = self.load_costing_form_records_from_source()
    kept_rows: list[dict] = []
    deleted_count = 0
    for row in existing_rows:
        row_style = normalize_style_identifier(row.get("style_code"))
        row_option = str(row.get("option", "") or "1").strip()
        if (row_style, row_option) in delete_pairs:
            deleted_count += 1
            continue
        kept_rows.append(row)
    if deleted_count <= 0:
        return 0
    if self.should_use_server_data_source():
        if self.is_local_data_mode():
            self.ensure_local_server_running()
        saved = save_app_setting(self.server_settings, COSTING_FORM_RECORDS_KEY, kept_rows)
        rows = [normalize_costing_form_record(item) for item in list(saved or []) if isinstance(item, dict)]
    else:
        saved = set_app_setting(DB_PATH, COSTING_FORM_RECORDS_KEY, kept_rows)
        rows = [normalize_costing_form_record(item) for item in list(saved or []) if isinstance(item, dict)]
    self._costing_form_records_cache = rows
    return deleted_count


def get_costing_form_records_for_style(self, style_code: object) -> list[dict]:
    normalized_style = normalize_style_identifier(style_code)
    if not normalized_style:
        return []
    cached_rows = getattr(self, "_costing_form_records_cache", None)
    source_rows = cached_rows if isinstance(cached_rows, list) else self.load_costing_form_records_from_source()
    records = [
        normalize_costing_form_record(record, normalized_style)
        for record in list(source_rows or [])
        if isinstance(record, dict) and normalize_style_identifier(record.get("style_code")) == normalized_style
    ]
    return sorted(records, key=lambda row: int(str(row.get("option", "") or "1")) if str(row.get("option", "") or "1").isdigit() else 1)


def get_costing_form_option_summary(self, style_code: object) -> str:
    records = self.get_costing_form_records_for_style(style_code)
    if not records:
        return "1"
    labels = []
    for record in records:
        option_value = str(record.get("option", "") or "1").strip()
        if normalize_workflow_status(record.get("status"), "draft") == "approved":
            labels.append(f"{option_value} Approved")
        else:
            labels.append(option_value)
    return ", ".join(labels)


def get_costing_form_record_for_style(self, style_code: object, option: object | None = None) -> dict:
    normalized_style = normalize_style_identifier(style_code)
    option_value = str(option or "").strip()
    records = self.get_costing_form_records_for_style(normalized_style)
    if option_value:
        for record in records:
            if str(record.get("option", "") or "1").strip() == option_value:
                return record
        default_record = default_costing_form_record(normalized_style)
        if option_value in COSTING_FORM_OPTION_VALUES:
            default_record["option"] = option_value
            default_record["option_name"] = f"Option {option_value}"
        return default_record
    for record in records:
        if normalize_workflow_status(record.get("status"), "draft") == "approved":
            return record
    for record in records:
        if str(record.get("option", "") or "1").strip() == "1":
            return record
    if records:
        return records[0]
    return default_costing_form_record(normalized_style)


def get_costing_form_consumption_lines(self, style_code: object, version_type: str) -> list[dict]:
    normalized_style = normalize_style_identifier(style_code)
    if not normalized_style:
        return []
    if not getattr(self, "tech_pack_library", []):
        try:
            self.refresh_tech_pack_module(silent=True)
        except Exception:
            pass
    row = self.get_tech_pack_row_by_style_code(normalized_style) if hasattr(self, "get_tech_pack_row_by_style_code") else None
    if row is None:
        for item in list(getattr(self, "tech_pack_library", []) or []):
            if normalize_style_identifier(item.get("style_code")) == normalized_style:
                row = dict(item)
                break
    if not isinstance(row, dict):
        return []
    versions = self.get_tech_pack_consumption_versions(row) if hasattr(self, "get_tech_pack_consumption_versions") else []
    target = next((item for item in versions if str(item.get("version_type", "")).strip().lower() == version_type), {})
    bom_by_code = {
        str(item.get("item_code", "")).strip(): dict(item)
        for item in list(getattr(self, "bom_items", []) or [])
        if isinstance(item, dict) and str(item.get("item_code", "")).strip()
    }
    lines: list[dict] = []
    for line in list(target.get("lines", [])) if isinstance(target, dict) else []:
        if not isinstance(line, dict):
            continue
        item_code = str(line.get("bom_item_code") or line.get("item_code") or "").strip()
        bom_row = bom_by_code.get(item_code, {})
        lines.append(
            normalize_costing_form_line(
                {
                    "item_code": item_code,
                    "name": line.get("bom_item_name") or bom_row.get("item_name"),
                    "type": line.get("fabric_type") or bom_row.get("item_type"),
                    "section": line.get("section") or bom_row.get("section"),
                    "color": line.get("color") or bom_row.get("color"),
                    "consumption": line.get("consumption"),
                    "uom": line.get("uom") or bom_row.get("uom") or bom_row.get("purchase_uom"),
                    "loss_percent": bom_row.get("waste_percent"),
                    "price": bom_row.get("standard_price"),
                    "currency": bom_row.get("currency"),
                }
            )
        )
    return lines


def _costing_form_line_match_key(line: dict) -> tuple[str, str, str, str]:
    item_code = str(line.get("item_code") or line.get("bom_item_code") or "").strip()
    if item_code:
        return ("code", item_code, "", "")
    name = str(line.get("name") or line.get("bom_item_name") or "").strip()
    line_type = str(line.get("type") or line.get("fabric_type") or "").strip()
    color = str(line.get("color", "")).strip()
    return ("fallback", name, line_type, color)


def _costing_style_matches_bom_style_list(bom_style_code: object, style_code: object) -> bool:
    normalized_style = normalize_style_identifier(style_code)
    if not normalized_style:
        return True
    tokens = [
        normalize_style_identifier(token)
        for token in re.split(r"[,;/\n]+", str(bom_style_code or ""))
    ]
    tokens = [token for token in tokens if token]
    return not tokens or normalized_style in tokens


def _costing_form_preserved_line_map(lines: list[dict]) -> dict[tuple[str, str, str, str], dict]:
    preserved: dict[tuple[str, str, str, str], dict] = {}
    for line in list(lines or []):
        if not isinstance(line, dict):
            continue
        key = _costing_form_line_match_key(line)
        if any(key[1:]):
            preserved[key] = dict(line)
    return preserved


def _costing_form_find_bom_item(self, line: dict, style_code: object = "") -> dict:
    item_code = str(line.get("item_code") or line.get("bom_item_code") or "").strip()
    normalized_style = normalize_style_identifier(style_code)
    fallback_match: dict = {}
    if not item_code:
        return {}
    bom_rows = [dict(item) for item in list(getattr(self, "bom_items", []) or []) if isinstance(item, dict)]
    if not bom_rows and hasattr(self, "load_bom_items_from_source"):
        try:
            bom_rows = [dict(item) for item in list(self.load_bom_items_from_source() or []) if isinstance(item, dict)]
            self.bom_items = list(bom_rows)
        except Exception:
            bom_rows = []
    for item in bom_rows:
        if not isinstance(item, dict):
            continue
        if str(item.get("item_code", "")).strip() != item_code:
            continue
        if not normalized_style or _costing_style_matches_bom_style_list(item.get("style_code"), normalized_style):
            return dict(item)
        if not fallback_match:
            fallback_match = dict(item)
    return fallback_match


def _costing_form_line_from_consumption(self, line: dict, style_code: object, preserved: dict | None = None) -> dict:
    source = dict(line or {})
    bom_row = _costing_form_find_bom_item(self, source, style_code)
    merged = dict(preserved or {})
    merged.update(
        {
            "item_code": source.get("bom_item_code") or source.get("item_code") or merged.get("item_code", ""),
            "name": source.get("bom_item_name") or source.get("name") or bom_row.get("item_name") or merged.get("name", ""),
            "type": source.get("fabric_type") or source.get("type") or bom_row.get("item_type") or bom_row.get("section") or merged.get("type", ""),
            "section": source.get("section") or bom_row.get("section") or merged.get("section", ""),
            "color": source.get("color") or bom_row.get("color") or merged.get("color", ""),
            "consumption": source.get("consumption", merged.get("consumption", "")),
            "uom": source.get("uom") or bom_row.get("purchase_uom") or bom_row.get("uom") or merged.get("uom", ""),
            "loss_percent": source.get("waste_percent") or bom_row.get("waste_percent") or merged.get("loss_percent", ""),
            "price": merged.get("price") or bom_row.get("standard_price", ""),
            "currency": merged.get("currency") or bom_row.get("currency", ""),
            "note": merged.get("note", ""),
        }
    )
    return normalize_costing_form_line(merged)


def sync_costing_form_materials_from_consumption(self, style_code: object, consumption_versions: list[dict]) -> dict:
    normalized_style = normalize_style_identifier(style_code)
    if not normalized_style:
        return {}
    record = self.get_costing_form_record_for_style(normalized_style)
    if normalize_workflow_status(record.get("status"), "draft") != "approved":
        return record
    preserved_materials = _costing_form_preserved_line_map(list(record.get("material_lines", [])))
    preserved_actuals = _costing_form_preserved_line_map(list(record.get("actual_lines", [])))
    versions_by_type = {
        str(version.get("version_type", "")).strip().lower(): dict(version)
        for version in list(consumption_versions or [])
        if isinstance(version, dict)
    }

    def convert(version_type: str, preserved: dict[tuple[str, str, str, str], dict]) -> list[dict]:
        version = versions_by_type.get(version_type, {})
        converted: list[dict] = []
        for line in list(version.get("lines", [])) if isinstance(version, dict) else []:
            if not isinstance(line, dict):
                continue
            key = _costing_form_line_match_key(line)
            converted.append(_costing_form_line_from_consumption(self, line, normalized_style, preserved.get(key, {})))
        return converted

    record["material_lines"] = convert("costing", preserved_materials)
    record["actual_lines"] = convert("production", preserved_actuals)
    record["updated_at"] = _costing_form_now_text()
    if not str(record.get("created_at", "")).strip():
        record["created_at"] = record["updated_at"]
    return self.save_costing_form_record_to_source(record)


def _merge_costing_form_lines_from_consumption(
    self,
    style_code: object,
    form_lines: list[dict],
    version_type: str,
) -> list[dict]:
    source_lines = self.get_costing_form_consumption_lines(style_code, version_type)
    normalized_existing = [normalize_costing_form_line(line) for line in list(form_lines or []) if isinstance(line, dict)]
    if not source_lines:
        return normalized_existing
    preserved = _costing_form_preserved_line_map(normalized_existing)
    merged_lines: list[dict] = []
    used_keys: set[tuple[str, str, str, str]] = set()
    for line in source_lines:
        key = _costing_form_line_match_key(line)
        used_keys.add(key)
        merged_lines.append(_costing_form_line_from_consumption(self, line, style_code, preserved.get(key, {})))
    for line in normalized_existing:
        key = _costing_form_line_match_key(line)
        if key not in used_keys:
            merged_lines.append(line)
    return merged_lines


def _costing_consumption_line_from_form_line(
    self,
    line: dict,
    *,
    style_code: object,
    version_type: object,
    line_index: int,
    existing_line: dict | None = None,
) -> dict:
    source = normalize_costing_form_line(line)
    item_code = str(source.get("item_code", "")).strip()
    existing = dict(existing_line or {})
    bom_row = _costing_form_find_bom_item(self, source, style_code)
    line_id = str(existing.get("line_id", "")).strip() or uuid.uuid4().hex
    costing_id = str(existing.get("costing_id", "")).strip()
    if not costing_id and hasattr(self, "_build_consumption_costing_id_for_costing_form"):
        costing_id = self._build_consumption_costing_id_for_costing_form(style_code, version_type, line_index, line_id)
    if not costing_id:
        normalized_style = normalize_style_identifier(style_code) or "STYLE"
        version_token = "".join(ch for ch in str(version_type or "").upper() if ch.isalnum()) or "GEN"
        costing_id = f"{normalized_style}-{version_token}-{line_index:03d}-{line_id[:6].upper()}"
    return {
        "line_id": line_id,
        "costing_id": costing_id,
        "bom_item_id": str(existing.get("bom_item_id") or bom_row.get("item_id") or "").strip(),
        "bom_item_code": item_code,
        "bom_item_name": str(source.get("name") or bom_row.get("item_name") or existing.get("bom_item_name", "")).strip(),
        "fabric_type": str(source.get("type") or bom_row.get("item_type") or bom_row.get("section") or existing.get("fabric_type", "")).strip(),
        "section": str(source.get("section") or existing.get("section") or bom_row.get("section") or "").strip(),
        "color": str(source.get("color") or bom_row.get("color") or existing.get("color", "")).strip(),
        "trim_size_um": str(existing.get("trim_size_um") or bom_row.get("trim_size_um") or "").strip(),
        "length_value": str(existing.get("length_value") or bom_row.get("length_value") or "").strip(),
        "width_value": str(existing.get("width_value") or bom_row.get("width_value") or "").strip(),
        "gsm_value": str(existing.get("gsm_value") or bom_row.get("gsm_value") or "").strip(),
        "waste_percent": str(source.get("loss_percent") or existing.get("waste_percent") or bom_row.get("waste_percent") or "").strip(),
        "uom": str(source.get("uom") or existing.get("uom") or bom_row.get("purchase_uom") or bom_row.get("uom") or "").strip(),
        "consumption": str(source.get("consumption", "")).strip(),
    }


def sync_tech_pack_consumption_from_costing_form(self, record: dict) -> dict:
    normalized_style = normalize_style_identifier(record.get("style_code") if isinstance(record, dict) else "")
    if not normalized_style:
        return {}
    if normalize_workflow_status(record.get("status"), "draft") != "approved":
        return {}
    if not getattr(self, "tech_pack_library", []):
        try:
            self.tech_pack_library = self.load_tech_pack_library_from_source()
        except Exception:
            pass
    row = self.get_tech_pack_row_by_style_code(normalized_style) if hasattr(self, "get_tech_pack_row_by_style_code") else None
    if row is None:
        row = next(
            (
                dict(item)
                for item in list(getattr(self, "tech_pack_library", []) or [])
                if isinstance(item, dict) and normalize_style_identifier(item.get("style_code")) == normalized_style
            ),
            {},
        )
    if not isinstance(row, dict) or not row:
        row = self.default_tech_pack_record(normalized_style) if hasattr(self, "default_tech_pack_record") else {"style_code": normalized_style}

    versions = self.get_tech_pack_consumption_versions(row) if hasattr(self, "get_tech_pack_consumption_versions") else []
    versions_by_type = {
        str(version.get("version_type", "")).strip().lower(): dict(version)
        for version in list(versions or [])
        if isinstance(version, dict)
    }

    def existing_line_map(version: dict) -> dict[tuple[str, str, str, str], dict]:
        existing: dict[tuple[str, str, str, str], dict] = {}
        for line in list(version.get("lines", [])) if isinstance(version, dict) else []:
            if isinstance(line, dict):
                key = _costing_form_line_match_key(line)
                if any(key[1:]):
                    existing[key] = dict(line)
        return existing

    def replace_version_lines(version_type: str, form_lines: list[dict]) -> None:
        version = versions_by_type.get(version_type, {"version_id": version_type, "version_type": version_type, "version_name": version_type.title(), "lines": []})
        existing = existing_line_map(version)
        synced_lines: list[dict] = []
        for line in list(form_lines or []):
            if not isinstance(line, dict):
                continue
            normalized_line = normalize_costing_form_line(line)
            if not str(normalized_line.get("item_code", "")).strip():
                continue
            key = _costing_form_line_match_key(normalized_line)
            synced_lines.append(
                _costing_consumption_line_from_form_line(
                    self,
                    normalized_line,
                    style_code=normalized_style,
                    version_type=version_type,
                    line_index=len(synced_lines) + 1,
                    existing_line=existing.get(key, {}),
                )
            )
        version["lines"] = synced_lines
        versions_by_type[version_type] = version

    replace_version_lines("costing", list(record.get("material_lines", [])))
    replace_version_lines("production", list(record.get("actual_lines", [])))

    ordered_versions = []
    for version_type in ("costing", "sample", "booking", "production", "settlement"):
        version = versions_by_type.get(version_type)
        if isinstance(version, dict):
            ordered_versions.append(version)
    changes = {
        "consumption_versions": ordered_versions,
        "last_updated": _costing_form_now_text(),
    }
    saved_record = self.save_tech_pack_record_patch_to_source(normalized_style, changes)
    if isinstance(saved_record, dict):
        library_by_style = {
            normalize_style_identifier(item.get("style_code")): dict(item)
            for item in list(getattr(self, "tech_pack_library", []) or [])
            if isinstance(item, dict) and normalize_style_identifier(item.get("style_code"))
        }
        library_by_style[normalized_style] = dict(saved_record)
        self.tech_pack_library = list(library_by_style.values())
    return saved_record if isinstance(saved_record, dict) else {}


def build_costing_form_record_context(self, item: dict, variant: dict | None, option: object | None = None) -> dict:
    source = dict(variant or item or {})
    style_code = normalize_style_identifier(source.get("style_code") or source.get("mfg_style") or item.get("style_code"))
    record = self.get_costing_form_record_for_style(style_code, option)
    if normalize_workflow_status(record.get("status"), "draft") == "approved":
        record["material_lines"] = _merge_costing_form_lines_from_consumption(
            self,
            style_code,
            list(record.get("material_lines", [])),
            "costing",
        )
        record["actual_lines"] = _merge_costing_form_lines_from_consumption(
            self,
            style_code,
            list(record.get("actual_lines", [])),
            "production",
        )
    else:
        record["material_lines"] = [normalize_costing_form_line(line) for line in list(record.get("material_lines", [])) if isinstance(line, dict)]
        record["actual_lines"] = [normalize_costing_form_line(line) for line in list(record.get("actual_lines", [])) if isinstance(line, dict)]
    if not str(record.get("image_path", "")).strip():
        tech_pack_row = {}
        for source_row in list(getattr(self, "tech_pack_library", []) or []):
            if isinstance(source_row, dict) and normalize_style_identifier(source_row.get("style_code")) == style_code:
                tech_pack_row = dict(source_row)
                break
        record["image_path"] = _costing_form_image_path_from_source(tech_pack_row) or _costing_form_image_path_from_source(source)
    return normalize_costing_form_record(record, style_code)


def copy_selected_costing_option(self) -> None:
    if not self.is_action_allowed("action.costing.edit_row"):
        return
    selected_keys = sorted(self.checked_costing_keys)
    if len(selected_keys) != 1:
        messagebox.showwarning("Copy Costing Option", "Please tick exactly one style to copy.")
        return
    selected_item = next((item for item in self.filtered_costing_items if tuple(item.get("group_key", ("", "", ""))) == selected_keys[0]), None)
    if not isinstance(selected_item, dict):
        selected_item = next((item for item in self.costing_items if tuple(item.get("group_key", ("", "", ""))) == selected_keys[0]), None)
    if not isinstance(selected_item, dict):
        messagebox.showwarning("Copy Costing Option", self.t("costing_empty"))
        return
    style_code = normalize_style_identifier(selected_item.get("style_code"))
    if not style_code:
        messagebox.showwarning("Copy Costing Option", self.t("costing_empty"))
        return
    records = self.get_costing_form_records_for_style(style_code)
    used_options = {
        int(str(row.get("option", "") or "1"))
        for row in records
        if str(row.get("option", "") or "1").isdigit()
    }
    next_option = (max(used_options) + 1) if used_options else 2
    if next_option > int(COSTING_FORM_OPTION_VALUES[-1]):
        messagebox.showwarning("Copy Costing Option", "This style already has the maximum 5 options.")
        return
    current_option = str(selected_item.get("option", "") or "").strip()
    source_record = next((row for row in records if str(row.get("option", "") or "1").strip() == current_option), None)
    if source_record is None:
        source_record = records[-1] if records else self.build_costing_form_record_context(selected_item, (selected_item.get("variants") or [None])[0])
    copied_record = normalize_costing_form_record(
        {
            **dict(source_record),
            "option": str(next_option),
            "option_name": f"Option {next_option}",
            "status": "draft",
            "created_at": _costing_form_now_text(),
            "updated_at": _costing_form_now_text(),
        },
        style_code,
    )
    self.save_costing_form_record_to_source(copied_record)
    self.checked_costing_keys.clear()
    self.apply_costing_filter()
    self.set_status(f"Copied {style_code} to option {next_option}.")


def edit_selected_costing_row(self, option: object | None = None) -> None:
    if not self.is_action_allowed("action.costing.edit_row"):
        return
    item, variant = self.get_selected_costing_item_and_variant()
    if item is None:
        messagebox.showwarning(self.t("costing_edit_title"), self.t("costing_empty"))
        return
    record = self.build_costing_form_record_context(item, variant, option or item.get("option"))
    style_code = str(record.get("style_code", "")).strip()

    dialog = tk.Toplevel(self.root)
    dialog.title(f"Costing Form - {style_code or '-'}")
    dialog.minsize(980, 640)
    costing_form_geometry = str(getattr(self, "ui_state", {}).get("costing_form_geometry", "") or "").strip()
    if costing_form_geometry:
        try:
            dialog.geometry(costing_form_geometry)
        except tk.TclError:
            dialog.geometry("1180x760")
    else:
        dialog.geometry("1180x760")
    dialog.resizable(True, True)

    def persist_costing_form_ui_state() -> None:
        if not hasattr(self, "ui_state"):
            self.ui_state = {}
        try:
            self.ui_state["costing_form_state"] = str(dialog.state())
        except tk.TclError:
            self.ui_state["costing_form_state"] = "normal"
        try:
            if str(dialog.state()) != "zoomed":
                self.ui_state["costing_form_geometry"] = dialog.winfo_geometry()
        except tk.TclError:
            pass
        try:
            tab_index = notebook.index(notebook.select())
            tab_keys = ("row", "header", "material", "logistics", "labour", "summary")
            if 0 <= tab_index < len(tab_keys):
                self.ui_state["costing_form_tab"] = tab_keys[tab_index]
        except Exception:
            pass
        if hasattr(self, "persist_ui_state"):
            try:
                self.persist_ui_state()
            except Exception:
                pass

    def close_costing_form_dialog(*, force: bool = False) -> None:
        if not force and bool(getattr(dialog, "_costing_form_save_running", False)):
            self.set_status(f"Saving Costing Form {style_code or '-'}...")
            return
        persist_costing_form_ui_state()
        try:
            if dialog.grab_current() is dialog:
                dialog.grab_release()
        except Exception:
            pass
        try:
            dialog.destroy()
        except tk.TclError:
            pass

    dialog.protocol("WM_DELETE_WINDOW", close_costing_form_dialog)

    frame = ttk.Frame(dialog, padding=14)
    frame.pack(fill="both", expand=True)
    frame.columnconfigure(0, weight=1)
    frame.rowconfigure(1, weight=1)

    ttk.Label(frame, text=f"Costing Form - {style_code or '-'}", style="Section.TLabel").grid(row=0, column=0, sticky="w")
    notebook = ttk.Notebook(frame)
    notebook.grid(row=1, column=0, sticky="nsew", pady=(12, 0))

    row_tab = ttk.Frame(notebook, padding=14)
    header_tab = ttk.Frame(notebook, padding=14)
    material_tab = ttk.Frame(notebook, padding=14)
    logistics_tab = ttk.Frame(notebook, padding=14)
    labour_tab = ttk.Frame(notebook, padding=14)
    summary_tab = ttk.Frame(notebook, padding=14)
    notebook.add(row_tab, text="Costing Row")
    notebook.add(header_tab, text="Header")
    notebook.add(material_tab, text="Material Cost")
    notebook.add(logistics_tab, text="Import / Logistics")
    notebook.add(labour_tab, text="Labour Cost")
    notebook.add(summary_tab, text="Summary")
    costing_form_tab_widgets = {
        "row": row_tab,
        "header": header_tab,
        "material": material_tab,
        "logistics": logistics_tab,
        "labour": labour_tab,
        "summary": summary_tab,
    }
    costing_form_tab_key = str(getattr(self, "ui_state", {}).get("costing_form_tab", "row") or "row").strip()
    if costing_form_tab_key in costing_form_tab_widgets:
        try:
            notebook.select(costing_form_tab_widgets[costing_form_tab_key])
        except tk.TclError:
            pass
    if str(getattr(self, "ui_state", {}).get("costing_form_state", "") or "").strip() == "zoomed":
        try:
            dialog.state("zoomed")
        except tk.TclError:
            pass

    row_fields = [
        ("collection", self.t("costing_collection_column")),
        ("dept_number", self.t("costing_dept_column")),
        ("mj_class", self.t("costing_mj_class_column")),
        ("sub_class", self.t("costing_sub_class_column")),
        ("mfg_style", self.t("costing_style_column")),
        ("description", self.t("costing_description_column")),
        ("fabrication", self.t("costing_fabrication_column")),
        ("mfg_color", self.t("costing_manf_color_column")),
        ("color_code", self.t("costing_color_code_column")),
        ("product_type", self.t("costing_product_type_column")),
        ("ticket_type", self.t("costing_ticket_type_column")),
        ("packing_type", self.t("costing_packing_type_column")),
        ("size_range", self.t("costing_size_range_column")),
        ("size_scale", self.t("costing_size_scale_column")),
        ("allocation_multiple", self.t("costing_allocation_multiple_column")),
        ("min_qty", self.t("costing_min_column")),
        ("max_qty", self.t("costing_max_column")),
        ("inner_pack", self.t("costing_inner_column")),
        ("outer_pack", self.t("costing_master_pack_column")),
        ("hanger_supplier", self.t("costing_hanger_supplier_column")),
        ("hanger_type", self.t("costing_hanger_type_column")),
        ("item_retail", self.t("costing_item_retail_column")),
        ("po_number", self.t("costing_po_number_column")),
        ("total_units", self.t("costing_total_units_column")),
    ]
    row_tab.columnconfigure(0, weight=0)
    row_tab.columnconfigure(1, weight=1)
    row_tab.columnconfigure(2, weight=0)
    row_tab.columnconfigure(3, weight=1)
    row_vars: dict[str, tk.StringVar] = {}
    source_variant = dict(variant or {})
    row_field_split = (len(row_fields) + 1) // 2
    for row_index, (key, label) in enumerate(row_fields):
        column_offset = 0 if row_index < row_field_split else 2
        display_row = row_index if row_index < row_field_split else row_index - row_field_split
        label_pad = (0, 10) if column_offset == 0 else (24, 10)
        entry_pad = (0, 12) if column_offset == 0 else (0, 0)
        ttk.Label(row_tab, text=label).grid(row=display_row, column=column_offset, sticky="w", padx=label_pad, pady=4)
        raw_value = source_variant.get(key)
        if key == "mfg_style":
            raw_value = source_variant.get("mfg_style") or source_variant.get("style_code") or item.get("style_code")
        elif key == "mfg_color":
            raw_value = source_variant.get("mfg_color") or source_variant.get("manf_color") or source_variant.get("color_label")
        text_value = "" if self.is_missing_value(raw_value) else str(raw_value or "")
        row_vars[key] = tk.StringVar(value=text_value)
        ttk.Entry(row_tab, textvariable=row_vars[key]).grid(row=display_row, column=column_offset + 1, sticky="ew", padx=entry_pad, pady=4)

    status_row_index = row_field_split
    ttk.Label(row_tab, text="Status").grid(row=status_row_index, column=0, sticky="w", padx=(0, 10), pady=4)
    status_var = tk.StringVar(value=normalize_workflow_status(record.get("status") or (variant or {}).get("status") or item.get("status"), "draft"))
    ttk.Combobox(row_tab, textvariable=status_var, values=("draft", "checking", "approved"), state="readonly").grid(
        row=status_row_index,
        column=1,
        sticky="ew",
        pady=4,
    )
    ttk.Label(row_tab, text="Option").grid(row=status_row_index + 1, column=0, sticky="w", padx=(0, 10), pady=4)
    option_var = tk.StringVar(value=str(record.get("option", "") or "1").strip())
    option_combo = ttk.Combobox(row_tab, textvariable=option_var, values=COSTING_FORM_OPTION_VALUES, state="readonly")
    option_combo.grid(
        row=status_row_index + 1,
        column=1,
        sticky="ew",
        pady=4,
    )

    def reopen_for_option(_event: object | None = None) -> None:
        target_option = option_var.get().strip()
        if not target_option or target_option == str(record.get("option", "") or "1").strip():
            return
        def reopen() -> None:
            close_costing_form_dialog(force=True)
            self.edit_selected_costing_row(target_option)

        self.schedule_root_after(80, reopen)

    option_combo.bind("<<ComboboxSelected>>", reopen_for_option)
    active_var = tk.BooleanVar(value=parse_active_flag((variant or {}).get("is_active", item.get("is_active", True)), True))
    ttk.Checkbutton(row_tab, text="Active", variable=active_var).grid(
        row=status_row_index + 2,
        column=1,
        sticky="w",
        pady=(6, 4),
    )

    header_vars = {
        "style_code": tk.StringVar(value=row_vars["mfg_style"].get().strip() or style_code),
        "qty": tk.StringVar(value=str(record.get("qty", "")).strip()),
        "offer_date": tk.StringVar(value=str(record.get("offer_date", "")).strip()),
        "confirm_date": tk.StringVar(value=str(record.get("confirm_date", "")).strip()),
        "buyer_target": tk.StringVar(value=str(record.get("buyer_target", "")).strip()),
        "approved_by": tk.StringVar(value=str(record.get("approved_by", "")).strip()),
        "approved_date": tk.StringVar(value=str(record.get("approved_date", "")).strip()),
        "buyer_confirm": tk.StringVar(value=str(record.get("buyer_confirm", "")).strip()),
    }

    def sync_header_from_row(*_args: object) -> None:
        header_vars["style_code"].set(row_vars["mfg_style"].get().strip())

    row_vars["mfg_style"].trace_add("write", sync_header_from_row)

    def add_header_entry(row_index: int, col_index: int, label: str, key: str, *, readonly: bool = False) -> ttk.Entry:
        host = ttk.Frame(header_tab)
        host.grid(row=row_index, column=col_index, sticky="ew", padx=8, pady=(0, 12))
        host.columnconfigure(0, weight=1)
        ttk.Label(host, text=label).grid(row=0, column=0, sticky="w", pady=(0, 4))
        entry = ttk.Entry(host, textvariable=header_vars[key])
        if readonly:
            entry.configure(state="readonly")
        entry.grid(row=1, column=0, sticky="ew")
        return entry

    for col in range(3):
        header_tab.columnconfigure(col, weight=1)
    add_header_entry(0, 0, "Style", "style_code", readonly=True)
    add_header_entry(0, 1, "Qty", "qty")
    offer_entry = add_header_entry(0, 2, "Offer date", "offer_date")
    confirm_entry = add_header_entry(1, 0, "Confirm date", "confirm_date")
    add_header_entry(1, 1, "Buyer target", "buyer_target")
    add_header_entry(1, 2, "Approved by", "approved_by")
    approved_date_entry = add_header_entry(2, 0, "Approved date", "approved_date")
    if hasattr(self, "bind_date_entry_picker"):
        self.bind_date_entry_picker(offer_entry, header_vars["offer_date"])
        self.bind_date_entry_picker(confirm_entry, header_vars["confirm_date"])
        self.bind_date_entry_picker(approved_date_entry, header_vars["approved_date"])

    line_headers = ("Item Code", "Name", "Type", "Section", "Color", "Consumption", "UOM", "Waste %", "Price", "Currency", "Note", "")

    def build_line_editor(parent: ttk.Frame, lines: list[dict], *, version_type: str) -> list[dict[str, tk.StringVar]]:
        parent.columnconfigure(0, weight=1)
        canvas = tk.Canvas(parent, highlightthickness=0)
        canvas.grid(row=0, column=0, sticky="nsew")
        scroll = ttk.Scrollbar(parent, orient="vertical", command=canvas.yview)
        scroll.grid(row=0, column=1, sticky="ns")
        parent.rowconfigure(0, weight=1)
        canvas.configure(yscrollcommand=scroll.set)
        table = ttk.Frame(canvas)
        window_id = canvas.create_window((0, 0), window=table, anchor="nw")
        for col, header in enumerate(line_headers):
            table.columnconfigure(col, weight=1)
            ttk.Label(table, text=header, style="FieldLabel.TLabel").grid(row=0, column=col, sticky="ew", padx=3, pady=(0, 6))
        row_vars: list[dict[str, tk.StringVar]] = []
        row_widgets: list[list[tk.Widget]] = []

        def sync_canvas(_event: object | None = None) -> None:
            canvas.configure(scrollregion=canvas.bbox("all"))
            canvas.itemconfigure(window_id, width=canvas.winfo_width())

        def refresh_line_rows() -> None:
            for widgets in row_widgets:
                for widget in widgets:
                    try:
                        widget.destroy()
                    except tk.TclError:
                        pass
            row_widgets.clear()
            for row_index, vars_map in enumerate(row_vars, start=1):
                widgets: list[tk.Widget] = []
                field_widths = {
                    "item_code": 12,
                    "name": 20,
                    "type": 12,
                    "section": 12,
                    "color": 12,
                    "consumption": 12,
                    "uom": 10,
                    "loss_percent": 10,
                    "price": 10,
                    "currency": 10,
                    "note": 20,
                }
                for col, key in enumerate(("item_code", "name", "type", "section", "color", "consumption", "uom", "loss_percent", "price", "currency", "note")):
                    if key == "uom":
                        current_uom = str(vars_map[key].get() or "").strip().upper()
                        uom_values = list(COSTING_FORM_UOM_OPTIONS)
                        if current_uom and current_uom not in uom_values:
                            uom_values.insert(0, current_uom)
                        widget = ttk.Combobox(
                            table,
                            textvariable=vars_map[key],
                            values=tuple(uom_values),
                            state="readonly",
                            width=field_widths[key],
                        )
                    elif key == "currency":
                        current_currency = str(vars_map[key].get() or "").strip().upper()
                        currency_values = list(COSTING_FORM_CURRENCY_OPTIONS)
                        if current_currency and current_currency not in currency_values:
                            currency_values.insert(0, current_currency)
                        widget = ttk.Combobox(
                            table,
                            textvariable=vars_map[key],
                            values=tuple(currency_values),
                            state="readonly",
                            width=field_widths[key],
                        )
                    else:
                        widget = ttk.Entry(table, textvariable=vars_map[key], width=field_widths[key])
                        if key not in {"consumption", "loss_percent", "note"}:
                            widget.configure(state="readonly")
                    widget.grid(row=row_index, column=col, sticky="ew", padx=3, pady=3)
                    widgets.append(widget)
                remove_button = ttk.Button(
                    table,
                    text="-",
                    width=3,
                    command=lambda vars_ref=vars_map: remove_row(vars_ref),
                )
                remove_button.grid(row=row_index, column=11, sticky="w", padx=3, pady=3)
                widgets.append(remove_button)
                row_widgets.append(widgets)
            sync_canvas()

        def add_row(seed: dict | None = None) -> None:
            values = normalize_costing_form_line(seed or {})
            vars_map = {key: tk.StringVar(value=str(values.get(key, "")).strip()) for key in ("item_code", "name", "type", "section", "color", "consumption", "uom", "loss_percent", "price", "currency", "note")}
            row_vars.append(vars_map)
            refresh_line_rows()

        def remove_row(vars_map: dict[str, tk.StringVar]) -> None:
            try:
                row_vars.remove(vars_map)
            except ValueError:
                return
            refresh_line_rows()

        def line_from_selected_bom_item(selected_item: dict) -> dict:
            return normalize_costing_form_line(
                {
                    "item_code": selected_item.get("item_code"),
                    "name": selected_item.get("item_name"),
                    "type": selected_item.get("item_type") or selected_item.get("section"),
                    "section": selected_item.get("section"),
                    "color": selected_item.get("color"),
                    "uom": selected_item.get("purchase_uom") or selected_item.get("uom"),
                    "loss_percent": selected_item.get("waste_percent"),
                    "price": selected_item.get("standard_price"),
                    "currency": selected_item.get("currency"),
                }
            )

        def add_bom_item(anchor_widget: tk.Widget | None = None) -> None:
            style_value = normalize_style_identifier(header_vars["style_code"].get() or row_vars_style_value())
            selected_item = None
            if hasattr(self, "open_select_tech_pack_consumption_bom_item_dialog"):
                selected_item = self.open_select_tech_pack_consumption_bom_item_dialog(
                    style_value,
                    anchor_widget,
                    version_type,
                )
            if isinstance(selected_item, dict):
                add_row(line_from_selected_bom_item(selected_item))

        for line in lines:
            add_row(line)
        actions = ttk.Frame(parent)
        actions.grid(row=1, column=0, sticky="w", pady=(8, 0))
        add_button = ttk.Button(actions, text="+", width=4)
        add_button.configure(command=lambda button_ref=add_button: add_bom_item(button_ref))
        add_button.pack(side="left")

        table.bind("<Configure>", sync_canvas)
        canvas.bind("<Configure>", sync_canvas)
        return row_vars

    def row_vars_style_value() -> str:
        try:
            return row_vars["mfg_style"].get().strip()
        except Exception:
            return style_code

    material_tab.columnconfigure(0, weight=1)
    material_tab.rowconfigure(0, weight=1)
    material_tab.rowconfigure(1, weight=1)
    costing_material_box = ttk.LabelFrame(material_tab, text="Costing", padding=8)
    costing_material_box.grid(row=0, column=0, sticky="nsew", pady=(0, 10))
    production_material_box = ttk.LabelFrame(material_tab, text="Production (Actual)", padding=8)
    production_material_box.grid(row=1, column=0, sticky="nsew")
    material_vars = build_line_editor(costing_material_box, list(record.get("material_lines", [])), version_type="costing")
    actual_vars = build_line_editor(production_material_box, list(record.get("actual_lines", [])), version_type="production")

    def build_charge_editor(parent: ttk.Frame, lines: list[dict], *, unit_values: tuple[str, ...]) -> list[dict[str, tk.StringVar]]:
        parent.columnconfigure(0, weight=1)
        table = ttk.Frame(parent)
        table.grid(row=0, column=0, sticky="nsew")
        parent.rowconfigure(0, weight=1)
        headers = ("Name", "Costing", "Actual", "Unit", "Note")
        for col, header in enumerate(headers):
            table.columnconfigure(col, weight=1)
            ttk.Label(table, text=header, style="FieldLabel.TLabel").grid(row=0, column=col, sticky="ew", padx=4)
        rows: list[dict[str, tk.StringVar]] = []

        def add_row(seed: dict | None = None) -> None:
            row = dict(seed or {})
            row_index = len(rows) + 1
            values = {
                "name": tk.StringVar(value=str(row.get("name", "")).strip()),
                "costing_value": tk.StringVar(value=str(row.get("costing_value", "")).strip()),
                "actual_value": tk.StringVar(value=str(row.get("actual_value", "")).strip()),
                "unit": tk.StringVar(value=str(row.get("unit", "")).strip().upper() or unit_values[0]),
                "note": tk.StringVar(value=str(row.get("note", "")).strip()),
            }
            rows.append(values)
            ttk.Entry(table, textvariable=values["name"]).grid(row=row_index, column=0, sticky="ew", padx=4, pady=4)
            ttk.Entry(table, textvariable=values["costing_value"]).grid(row=row_index, column=1, sticky="ew", padx=4, pady=4)
            ttk.Entry(table, textvariable=values["actual_value"]).grid(row=row_index, column=2, sticky="ew", padx=4, pady=4)
            ttk.Combobox(table, textvariable=values["unit"], values=unit_values, state="readonly", width=8).grid(row=row_index, column=3, sticky="ew", padx=4, pady=4)
            ttk.Entry(table, textvariable=values["note"]).grid(row=row_index, column=4, sticky="ew", padx=4, pady=4)

        for line in lines:
            add_row(line)
        if not rows:
            add_row({})
        actions_row = ttk.Frame(parent)
        actions_row.grid(row=1, column=0, sticky="w", pady=(8, 0))
        ttk.Button(actions_row, text="+", width=4, command=lambda: add_row({})).pack(side="left")
        return rows

    logistics_vars = build_charge_editor(
        logistics_tab,
        list(record.get("logistics_lines", [])),
        unit_values=COSTING_FORM_COST_UNIT_OPTIONS,
    )
    labour_vars = build_charge_editor(
        labour_tab,
        list(record.get("labour_lines", [])),
        unit_values=COSTING_FORM_CURRENCY_OPTIONS,
    )

    summary_tab.columnconfigure(1, weight=1)
    summary_values = {
        "FOB": tk.StringVar(value=""),
        "DDP": tk.StringVar(value=""),
        "Included 5%": tk.StringVar(value=""),
        "Included 10%": tk.StringVar(value=""),
        "Included 15%": tk.StringVar(value=""),
        "Included 20%": tk.StringVar(value=""),
    }
    ttk.Label(summary_tab, text="Buyer confirm").grid(row=0, column=0, sticky="w", padx=(0, 10), pady=6)
    ttk.Entry(summary_tab, textvariable=header_vars["buyer_confirm"]).grid(row=0, column=1, sticky="ew", pady=6)
    for idx, (label, var) in enumerate(summary_values.items(), start=1):
        ttk.Label(summary_tab, text=label).grid(row=idx, column=0, sticky="w", padx=(0, 10), pady=6)
        ttk.Label(summary_tab, textvariable=var).grid(row=idx, column=1, sticky="w", pady=6)

    def collect_line_vars(rows: list[dict[str, tk.StringVar]]) -> list[dict]:
        collected = []
        for vars_map in rows:
            line = normalize_costing_form_line({key: variable.get().strip() for key, variable in vars_map.items()})
            if any(str(line.get(key, "")).strip() for key in ("item_code", "name", "consumption", "price", "note")):
                collected.append(line)
        return collected

    def collect_charge_vars(rows: list[dict[str, tk.StringVar]]) -> list[dict]:
        collected = []
        for vars_map in rows:
            line = {key: variable.get().strip() for key, variable in vars_map.items()}
            if any(str(line.get(key, "")).strip() for key in ("name", "costing_value", "actual_value", "note")):
                line["unit"] = str(line.get("unit", "")).strip().upper()
                collected.append(line)
        return collected

    def refresh_summary() -> None:
        material_total = sum(_costing_line_cost(line) for line in collect_line_vars(material_vars))
        labour_total = sum(_costing_safe_float(row["costing_value"].get()) for row in labour_vars)
        logistics_total = sum(
            _costing_charge_value(
                {key: variable.get().strip() for key, variable in row.items()},
                material_total + labour_total,
                "costing_value",
            )
            for row in logistics_vars
        )
        fob = material_total + labour_total
        ddp = (fob + logistics_total) * 1.025
        summary_values["FOB"].set(_costing_format_number(fob))
        summary_values["DDP"].set(_costing_format_number(ddp))
        for pct in (5, 10, 15, 20):
            summary_values[f"Included {pct}%"].set(_costing_format_number(ddp * (1 + pct / 100)))

    refresh_summary()
    ttk.Button(summary_tab, text="Refresh summary", command=refresh_summary).grid(row=7, column=1, sticky="w", pady=(10, 0))

    actions = ttk.Frame(frame)
    actions.grid(row=2, column=0, sticky="e", pady=(12, 0))

    def save_action() -> None:
        if bool(getattr(dialog, "_costing_form_save_running", False)):
            return
        style_value = row_vars["mfg_style"].get().strip()
        color_value = row_vars["mfg_color"].get().strip()
        row_updates = {
            "collection": row_vars["collection"].get().strip(),
            "dept_number": row_vars["dept_number"].get().strip(),
            "mfg_style": style_value,
            "style_code": style_value,
            "description": row_vars["description"].get().strip(),
            "fabrication": row_vars["fabrication"].get().strip(),
            "mfg_color": color_value,
            "manf_color": color_value,
            "mj_class": row_vars["mj_class"].get().strip(),
            "sub_class": row_vars["sub_class"].get().strip(),
            "color_code": row_vars["color_code"].get().strip(),
            "product_type": row_vars["product_type"].get().strip(),
            "ticket_type": row_vars["ticket_type"].get().strip(),
            "packing_type": row_vars["packing_type"].get().strip(),
            "total_units": row_vars["total_units"].get().strip(),
            "allocation_multiple": row_vars["allocation_multiple"].get().strip(),
            "min_qty": row_vars["min_qty"].get().strip(),
            "max_qty": row_vars["max_qty"].get().strip(),
            "inner_pack": row_vars["inner_pack"].get().strip(),
            "outer_pack": row_vars["outer_pack"].get().strip(),
            "size_range": row_vars["size_range"].get().strip(),
            "size_scale": row_vars["size_scale"].get().strip(),
            "hanger_supplier": row_vars["hanger_supplier"].get().strip(),
            "hanger_type": row_vars["hanger_type"].get().strip(),
            "item_retail": row_vars["item_retail"].get().strip(),
            "po_number": row_vars["po_number"].get().strip(),
        }
        current_style_code = normalize_style_identifier(style_value or style_code)
        target_status = normalize_workflow_status(status_var.get(), "draft")
        if target_status == "approved" and not self.is_action_allowed("action.costing.approve"):
            self.deny_permission_action("action.costing.approve")
            return
        refreshed = normalize_costing_form_record(
            {
                "style_code": current_style_code,
                "option": option_var.get().strip() or "1",
                "option_name": f"Option {option_var.get().strip() or '1'}",
                "status": target_status,
                "qty": header_vars["qty"].get().strip(),
                "offer_date": header_vars["offer_date"].get().strip(),
                "confirm_date": header_vars["confirm_date"].get().strip(),
                "buyer_target": header_vars["buyer_target"].get().strip(),
                "buyer_confirm": header_vars["buyer_confirm"].get().strip(),
                "approved_by": header_vars["approved_by"].get().strip(),
                "approved_date": header_vars["approved_date"].get().strip(),
                "image_path": str(record.get("image_path", "")).strip(),
                "material_lines": collect_line_vars(material_vars),
                "actual_lines": collect_line_vars(actual_vars),
                "logistics_lines": collect_charge_vars(logistics_vars),
                "labour_lines": collect_charge_vars(labour_vars),
                "created_at": str(record.get("created_at", "")).strip() or _costing_form_now_text(),
                "updated_at": _costing_form_now_text(),
            },
            current_style_code,
        )
        source_variant_snapshot = dict(source_variant)
        record_snapshot = dict(record)
        variant_snapshot = dict(variant or {})
        active_value = bool(active_var.get())
        worksheet_update_payload: dict[str, object] | None = None
        if variant_snapshot and str(variant_snapshot.get("source_kind", "")).strip().lower() == "worksheet":
            try:
                worksheet_update_payload = {
                    "source_workbook": str(variant_snapshot.get("source_workbook") or variant_snapshot.get("source_pdf") or "").strip(),
                    "source_sheet": str(variant_snapshot.get("source_sheet") or "").strip(),
                    "source_row_number": int(variant_snapshot.get("source_row_number") or 0),
                    "updates": {
                        **row_updates,
                        "status": target_status,
                        "is_active": active_value,
                    },
                }
            except Exception:
                worksheet_update_payload = None

        dialog._costing_form_save_running = True
        save_button.configure(state="disabled")
        close_button.configure(state="disabled")
        self.set_status(f"Saving Costing Form {current_style_code}...")

        def worker() -> None:
            payload: dict[str, object]
            warnings: list[str] = []
            try:
                saved_form_record = self.save_costing_form_record_to_source(refreshed)
                sync_record = saved_form_record if isinstance(saved_form_record, dict) and saved_form_record else refreshed
                if target_status == "approved" and hasattr(self, "sync_tech_pack_consumption_from_costing_form"):
                    try:
                        self.sync_tech_pack_consumption_from_costing_form(sync_record)
                    except Exception as exc:
                        warnings.append(f"Tech Pack Consumption was not updated: {exc}")
                if worksheet_update_payload is not None:
                    try:
                        self.update_costing_worksheet_row_in_source(**worksheet_update_payload)
                    except Exception:
                        pass
                try:
                    self.record_entity_history(
                        scope="costing",
                        entity_key=current_style_code,
                        entity_label=current_style_code,
                        action="update",
                        before={"row": source_variant_snapshot, "form": record_snapshot},
                        after={"row": row_updates, "form": sync_record},
                        summary=f"Updated costing row {current_style_code}",
                    )
                except Exception as exc:
                    warnings.append(f"History was not updated: {exc}")
                try:
                    refresh_imports = self.fetch_costing_imports_from_source()
                except Exception as exc:
                    warnings.append(f"Costing list was not refreshed: {exc}")
                    refresh_imports = None
                payload = {
                    "ok": True,
                    "warnings": warnings,
                    "refresh_imports": refresh_imports,
                    "saved_form_record": sync_record,
                }
            except Exception as exc:
                payload = {
                    "ok": False,
                    "error": str(exc),
                }

            def apply_result() -> None:
                try:
                    dialog._costing_form_save_running = False
                except Exception:
                    pass
                if not payload.get("ok"):
                    try:
                        save_button.configure(state="normal")
                        close_button.configure(state="normal")
                    except tk.TclError:
                        pass
                    error_message = str(payload.get("error") or "Could not save Costing Form.")
                    messagebox.showerror(self.t("warning_title"), error_message, parent=dialog)
                    self.set_status(error_message)
                    return
                refresh_imports = payload.get("refresh_imports")
                try:
                    save_button.configure(state="normal")
                    close_button.configure(state="normal")
                except tk.TclError:
                    pass
                record.clear()
                saved_form_record = payload.get("saved_form_record")
                record.update(saved_form_record if isinstance(saved_form_record, dict) else refreshed)
                if isinstance(refresh_imports, list):
                    self.apply_costing_imports_snapshot(refresh_imports, silent=True)
                warnings_list = [str(item).strip() for item in list(payload.get("warnings", []) or []) if str(item).strip()]
                if warnings_list:
                    self.set_status(f"{self.t('costing_edit_saved')} Warning: {' | '.join(warnings_list[:3])}")
                else:
                    self.set_status(self.t("costing_edit_saved"))

            self.schedule_root_after(0, apply_result)

        threading.Thread(target=worker, daemon=True).start()

    save_button = ttk.Button(actions, text=self.t("collection_list_save"), command=save_action)
    save_button.pack(side="right")
    close_button = ttk.Button(actions, text=self.t("close"), command=close_costing_form_dialog)
    close_button.pack(side="right", padx=(0, 8))
    try:
        dialog.lift()
        dialog.focus_force()
    except tk.TclError:
        pass


def load_costing_form_assets_from_source(self) -> dict[str, object]:
    default_value = default_costing_form_assets()
    if self.should_use_server_data_source():
        if self.is_local_data_mode():
            self.ensure_local_server_running()
        try:
            payload = fetch_app_setting(self.server_settings, COSTING_FORM_ASSETS_KEY, default_value)
            merged = dict(default_value)
            if isinstance(payload, dict):
                merged.update(payload)
            return merged
        except Exception:
            pass
    payload = get_app_setting(DB_PATH, COSTING_FORM_ASSETS_KEY, default_value)
    merged = dict(default_value)
    if isinstance(payload, dict):
        merged.update(payload)
    return merged


def save_costing_form_assets_to_source(self, payload: dict[str, object]) -> dict[str, object]:
    normalized = dict(default_costing_form_assets())
    normalized.update(payload if isinstance(payload, dict) else {})
    if self.should_use_server_data_source():
        if self.is_local_data_mode():
            self.ensure_local_server_running()
        saved = save_app_setting(self.server_settings, COSTING_FORM_ASSETS_KEY, normalized)
        return dict(saved) if isinstance(saved, dict) else normalized
    saved = set_app_setting(DB_PATH, COSTING_FORM_ASSETS_KEY, normalized)
    return dict(saved) if isinstance(saved, dict) else normalized


def resolve_costing_form_template_asset(self, value: str, *, fallback_suffix: str = ".xlsx") -> Path | None:
    raw_value = str(value or "").strip()
    if not raw_value:
        return None
    candidate = Path(raw_value)
    if candidate.exists() and candidate.is_file():
        return candidate
    if raw_value.lower().startswith(("http://", "https://")):
        try:
            split = urlsplit(raw_value)
            normalized_url = urlunsplit(
                (
                    split.scheme,
                    split.netloc,
                    quote(split.path, safe="/:@"),
                    quote(split.query, safe="=&?/:@"),
                    quote(split.fragment, safe="=&?/:@"),
                )
            )
            parsed = urlparse(normalized_url)
            suffix = Path(parsed.path).suffix or fallback_suffix
            cache_dir = DATA_DIR / "costing_form_assets"
            cache_dir.mkdir(parents=True, exist_ok=True)
            target_path = cache_dir / f"{hashlib.sha1(raw_value.encode('utf-8')).hexdigest()}{suffix}"
            if target_path.exists() and target_path.is_file():
                return target_path
            request = Request(normalized_url, headers={"User-Agent": "CitiFoxERPApp/desktop"})
            with open_url(request, timeout=20) as response:
                target_path.write_bytes(response.read())
            return target_path if target_path.exists() else None
        except Exception:
            return None
    return None


def build_clean_costing_form_template(self, target_path: Path) -> Path:
    target_path.parent.mkdir(parents=True, exist_ok=True)
    wb = Workbook()
    ws = wb.active
    ws.title = "Costing Form"
    widths = {
        "A": 20.625,
        "B": 9.625,
        "C": 9.625,
        "D": 9.625,
        "E": 13.0,
        "F": 0.875,
        "G": 20.625,
        "H": 9.625,
        "I": 9.625,
        "J": 9.625,
        "K": 13.0,
        "L": 0.875,
        "M": 41.25,
        "N": 4.625,
    }
    for col, width in widths.items():
        ws.column_dimensions[col].width = width
    default_heights = {
        1: 12.95,
        15: 20.1,
        16: 6.0,
        17: 36.0,
        20: 4.5,
        29: 4.5,
        40: 6.0,
        46: 6.0,
        51: 5.1,
    }
    for row in range(1, 55):
        ws.row_dimensions[row].height = default_heights.get(row, 15.95)
    section_fill = PatternFill("solid", fgColor="D9D9D9")
    thin = Side(style="thin", color="808080")
    border = Border(left=thin, right=thin, top=thin, bottom=thin)
    for cell_range in (
        "A1:E1",
        "G1:K1",
        "M2:M19",
        "C4:E7",
        "C8:E8",
        "B11:E14",
        "J4:K4",
        "J5:K5",
        "J6:K6",
        "J7:K7",
        "J8:K8",
        "J9:K9",
        "J10:K10",
        "J11:K11",
        "J12:K12",
        "J13:K13",
        "G14:K14",
        "B17:E17",
        "B18:E18",
        "H17:K17",
        "H18:K18",
        "A15:E15",
        "G15:K15",
    ):
        ws.merge_cells(cell_range)
    ws["A1"] = "COSTING"
    ws["G1"] = "ACTUAL"
    ws["M2"] = "PHOTO"
    for cell in ("A1", "G1"):
        ws[cell].alignment = Alignment(horizontal="center")
    ws["M2"].font = Font(bold=True)
    ws["M2"].alignment = Alignment(horizontal="center", vertical="center")
    labels = [("A2", "STYLE NO:"), ("A3", "Q'TY:"), ("C3", "Offer Date:"), ("G2", "STYLE NO:"), ("G3", "Q'TY:"), ("I3", "ConfirmDate:"), ("J4", "Remark")]
    for coord, label in labels:
        ws[coord] = label
        ws[coord].font = Font(bold=True)
    ws["A4"] = "Included 5%"
    ws["A5"] = "Included 10%"
    ws["A6"] = "Included 15%"
    ws["A7"] = "Included 20%"
    ws["A8"] = "Buyer confirm"
    ws["A9"] = "Buyer target (calculated)"
    ws["A11"] = "Approved by:"
    ws["A13"] = "Approved date:"
    ws["A15"] = "COSTING"
    ws["G15"] = "ACTUAL"
    ws["A17"] = "Fabric"
    ws["A18"] = "Supplier"
    ws["A19"] = "Width"
    ws["C19"] = "Weight"
    ws["G17"] = "Fabric"
    ws["G18"] = "Supplier"
    ws["G19"] = "Width"
    ws["I19"] = "Weight"
    for row, headers in (
        (21, ("Fabric", "Cons", "Lost %", "Price", "Cost")),
        (30, ("Trim", "Cons", "Lost %", "Price", "Cost")),
    ):
        for offset, value in enumerate(headers):
            ws.cell(row=row, column=1 + offset, value=value)
            ws.cell(row=row, column=7 + offset, value=value)
    for row, label in ((28, "Total"), (39, "Total"), (45, "Total"), (50, "Total")):
        ws.cell(row=row, column=1, value=label)
        ws.cell(row=row, column=7, value=label)
    for row, label in (
        (41, "Sewing Labor"),
        (42, "Printing"),
        (43, "Washing"),
        (44, "Transfer"),
        (47, "Duty Rate"),
        (48, "Exim"),
        (49, "LA warehouse"),
    ):
        ws.cell(row=row, column=1, value=label)
        ws.cell(row=row, column=7, value=label)
    ws["M21"] = "Balance"
    ws["M30"] = "Trim"
    ws["M41"] = "Labor cost"
    ws["M47"] = "EXIM"
    ws["D52"] = "FOB"
    ws["D53"] = "DDP"
    ws["J52"] = "FOB"
    ws["J53"] = "DDP"
    for row in range(1, 55):
        for col in range(1, 15):
            cell = ws.cell(row=row, column=col)
            cell.border = border
            cell.alignment = Alignment(horizontal="left", vertical="center", wrap_text=True)
            if row in {1, 15}:
                cell.alignment = Alignment(horizontal="center", vertical="center", wrap_text=True)
            if row in {21, 30}:
                cell.fill = section_fill
            if row in {15}:
                cell.font = Font(bold=True)
            if col in {5, 11, 13} and row >= 21:
                cell.number_format = '$#,##0.00'
            elif col in {3, 9} and row >= 21:
                cell.number_format = '0.00%'
    wb.save(target_path)
    return target_path


def ensure_costing_form_template_asset(self) -> Path:
    cache_dir = DATA_DIR / "costing_form_assets"
    cache_dir.mkdir(parents=True, exist_ok=True)
    local_template = cache_dir / COSTING_FORM_TEMPLATE_FILENAME
    assets = self.load_costing_form_assets_from_source()
    template_url = str(assets.get("template_url", "")).strip()
    template_path = self.resolve_costing_form_template_asset(template_url)
    if template_path is not None:
        return template_path
    if not local_template.exists():
        self.build_clean_costing_form_template(local_template)
    if self.should_use_server_data_source() and not template_url and hasattr(self, "upload_shared_file_reference"):
        uploaded_url = self.upload_shared_file_reference(local_template, bucket=COSTING_FORM_TEMPLATE_BUCKET)
        if uploaded_url and str(uploaded_url).lower().startswith(("http://", "https://")):
            assets["template_url"] = uploaded_url
            self.save_costing_form_assets_to_source(assets)
    return local_template


def build_costing_form_file(self, record: dict, save_path: Path) -> Path:
    template_path = self.ensure_costing_form_template_asset()
    wb = load_workbook(template_path)
    ws = wb.active
    style_code = str(record.get("style_code", "")).strip()
    qty = str(record.get("qty", "")).strip()
    ws["B2"] = style_code
    ws["H2"] = style_code
    ws["B3"] = _costing_safe_float(qty) if qty else ""
    ws["H3"] = _costing_safe_float(qty) if qty else ""
    ws["D3"] = str(record.get("offer_date", "")).strip()
    ws["J3"] = str(record.get("confirm_date", "")).strip()
    ws["B8"] = _costing_safe_float(record.get("buyer_confirm")) if str(record.get("buyer_confirm", "")).strip() else ""
    ws["B9"] = _costing_safe_float(record.get("buyer_target")) if str(record.get("buyer_target", "")).strip() else ""
    ws["A12"] = str(record.get("approved_by", "")).strip()
    ws["A14"] = str(record.get("approved_date", "")).strip()
    ws["C4"] = str(record.get("lead_time_note", "")).strip()

    image_path = str(record.get("image_path", "")).strip()
    resolved_image_path = resolve_image_source(image_path) if image_path else None
    if resolved_image_path:
        try:
            xl_image = XLImage(str(resolved_image_path))
            xl_image.width = 294
            xl_image.height = 400
            ws.add_image(xl_image, "M2")
        except Exception:
            ws["M2"] = "PHOTO"

    def copy_row_style(source_row: int, target_row: int) -> None:
        ws.row_dimensions[target_row].height = ws.row_dimensions[source_row].height
        for col in range(1, 15):
            source_cell = ws.cell(row=source_row, column=col)
            target_cell = ws.cell(row=target_row, column=col)
            target_cell._style = copy(source_cell._style)
            target_cell.font = copy(source_cell.font)
            target_cell.fill = copy(source_cell.fill)
            target_cell.border = copy(source_cell.border)
            target_cell.alignment = copy(source_cell.alignment)
            target_cell.number_format = source_cell.number_format

    def write_lines(lines: list[dict], start_row: int, end_row: int, start_col: int) -> None:
        for row in range(start_row, end_row + 1):
            for col in range(start_col, start_col + 5):
                ws.cell(row=row, column=col).value = ""
        for offset, line in enumerate(lines[: end_row - start_row + 1]):
            row = start_row + offset
            ws.cell(row=row, column=start_col, value=str(line.get("name") or line.get("item_code") or "").strip())
            ws.cell(row=row, column=start_col + 1, value=_costing_safe_float(line.get("consumption")) if str(line.get("consumption", "")).strip() else "")
            ws.cell(row=row, column=start_col + 2, value=_costing_percent_float(line.get("loss_percent")) if str(line.get("loss_percent", "")).strip() else "")
            ws.cell(row=row, column=start_col + 3, value=_costing_safe_float(line.get("price")) if str(line.get("price", "")).strip() else "")
            ws.cell(row=row, column=start_col + 4, value=f"={ws.cell(row=row, column=start_col+1).coordinate}*(1+{ws.cell(row=row, column=start_col+2).coordinate})*{ws.cell(row=row, column=start_col+3).coordinate}")
            ws.cell(row=row, column=start_col + 2).number_format = "0.00%"
            ws.cell(row=row, column=start_col + 3).number_format = '$#,##0.00'
            ws.cell(row=row, column=start_col + 4).number_format = '$#,##0.00'

    def write_charge_lines(lines: list[dict], start_row: int, end_row: int, start_col: int, base_cell: str) -> None:
        for row in range(start_row, end_row + 1):
            for col in range(start_col, start_col + 5):
                ws.cell(row=row, column=col).value = ""
        for offset, line in enumerate(lines[: end_row - start_row + 1]):
            row = start_row + offset
            value_cell = ws.cell(row=row, column=start_col + 1).coordinate
            unit_cell = ws.cell(row=row, column=start_col + 2).coordinate
            ws.cell(row=row, column=start_col, value=str(line.get("name", "")).strip())
            ws.cell(row=row, column=start_col + 1, value=_costing_safe_float(line.get("costing_value")) if str(line.get("costing_value", "")).strip() else "")
            ws.cell(row=row, column=start_col + 2, value=str(line.get("unit", "")).strip().upper())
            ws.cell(row=row, column=start_col + 3, value=str(line.get("note", "")).strip())
            ws.cell(row=row, column=start_col + 4, value=f'=IF({unit_cell}="%",{base_cell}*{value_cell}/100,{value_cell})')

    def write_actual_charge_lines(lines: list[dict], start_row: int, end_row: int, start_col: int, base_cell: str) -> None:
        for row in range(start_row, end_row + 1):
            for col in range(start_col, start_col + 5):
                ws.cell(row=row, column=col).value = ""
        for offset, line in enumerate(lines[: end_row - start_row + 1]):
            row = start_row + offset
            value_cell = ws.cell(row=row, column=start_col + 1).coordinate
            unit_cell = ws.cell(row=row, column=start_col + 2).coordinate
            ws.cell(row=row, column=start_col, value=str(line.get("name", "")).strip())
            ws.cell(row=row, column=start_col + 1, value=_costing_safe_float(line.get("actual_value")) if str(line.get("actual_value", "")).strip() else "")
            ws.cell(row=row, column=start_col + 2, value=str(line.get("unit", "")).strip().upper())
            ws.cell(row=row, column=start_col + 3, value=str(line.get("note", "")).strip())
            ws.cell(row=row, column=start_col + 4, value=f'=IF({unit_cell}="%",{base_cell}*{value_cell}/100,{value_cell})')

    material_lines = list(record.get("material_lines", []))
    actual_lines = list(record.get("actual_lines", []))
    fabric_lines = [line for line in material_lines if str(line.get("type", "")).strip().lower() == "fabric"]
    trim_lines = [line for line in material_lines if str(line.get("type", "")).strip().lower() != "fabric"]
    actual_fabric = [line for line in actual_lines if str(line.get("type", "")).strip().lower() == "fabric"]
    actual_trim = [line for line in actual_lines if str(line.get("type", "")).strip().lower() != "fabric"]
    write_lines(fabric_lines, 22, 27, 1)
    write_lines(trim_lines, 31, 38, 1)
    write_lines(actual_fabric, 22, 27, 7)
    write_lines(actual_trim, 31, 38, 7)
    ws["E28"] = "=SUM(E22:E27)"
    ws["K28"] = "=SUM(K22:K27)"
    ws["E39"] = "=SUM(E31:E38)"
    ws["K39"] = "=SUM(K31:K38)"

    labour_lines = list(record.get("labour_lines", []))
    extra_labour_rows = max(0, len(labour_lines) - 4)
    if extra_labour_rows:
        ws.insert_rows(45, extra_labour_rows)
        for row in range(45, 45 + extra_labour_rows):
            copy_row_style(44, row)
    labour_total_row = 45 + extra_labour_rows
    logistics_start_row = 47 + extra_labour_rows
    write_charge_lines(labour_lines, 41, labour_total_row - 1, 1, "E39")
    write_actual_charge_lines(labour_lines, 41, labour_total_row - 1, 7, "K39")
    ws.cell(row=labour_total_row, column=1, value="Total")
    ws.cell(row=labour_total_row, column=5, value=f"=SUM(E41:E{labour_total_row - 1})")
    ws.cell(row=labour_total_row, column=7, value="Total")
    ws.cell(row=labour_total_row, column=11, value=f"=SUM(K41:K{labour_total_row - 1})")

    logistics_lines = list(record.get("logistics_lines", []))
    extra_logistics_rows = max(0, len(logistics_lines) - 3)
    if extra_logistics_rows:
        ws.insert_rows(logistics_start_row + 3, extra_logistics_rows)
        for row in range(logistics_start_row + 3, logistics_start_row + 3 + extra_logistics_rows):
            copy_row_style(logistics_start_row + 2, row)
    logistics_total_row = logistics_start_row + 3 + extra_logistics_rows
    fob_row = logistics_total_row + 2
    ddp_row = fob_row + 1
    write_charge_lines(logistics_lines, logistics_start_row, logistics_total_row - 1, 1, f"E{fob_row}")
    write_actual_charge_lines(logistics_lines, logistics_start_row, logistics_total_row - 1, 7, f"K{fob_row}")
    ws.cell(row=logistics_total_row, column=1, value="Total")
    ws.cell(row=logistics_total_row, column=5, value=f"=SUM(E{logistics_start_row}:E{logistics_total_row - 1})")
    ws.cell(row=logistics_total_row, column=7, value="Total")
    ws.cell(row=logistics_total_row, column=11, value=f"=SUM(K{logistics_start_row}:K{logistics_total_row - 1})")
    ws.cell(row=fob_row, column=4, value="FOB")
    ws.cell(row=fob_row, column=5, value=f"=E28+E39+E{labour_total_row}")
    ws.cell(row=fob_row, column=10, value="FOB")
    ws.cell(row=fob_row, column=11, value=f"=K28+K39+K{labour_total_row}")
    ws.cell(row=ddp_row, column=4, value="DDP")
    ws.cell(row=ddp_row, column=5, value=f"=(E{fob_row}+E{logistics_total_row})*1.025")
    ws.cell(row=ddp_row, column=10, value="DDP")
    ws.cell(row=ddp_row, column=11, value=f"=(K{fob_row}+K{logistics_total_row})*1.025")
    ws["B4"] = f"=E{ddp_row}*1.05"
    ws["B5"] = f"=E{ddp_row}*1.1"
    ws["B6"] = f"=E{ddp_row}*1.15"
    ws["B7"] = f"=E{ddp_row}*1.2"
    ws["C8"] = f"=(B8/E{ddp_row})-1"
    for cell_ref in ("B4", "B5", "B6", "B7"):
        ws[cell_ref].number_format = '$#,##0.00'
    for row in range(22, ddp_row + 2):
        ws.cell(row=row, column=13, value=f"=K{row}-E{row}")
        ws.cell(row=row, column=13).number_format = '$#,##0.00'
    save_path.parent.mkdir(parents=True, exist_ok=True)
    wb.save(save_path)
    return save_path


def export_selected_costing_form(self) -> None:
    if not self.is_action_allowed("action.costing.export_report"):
        return
    item, variant = self.get_selected_costing_item_and_variant()
    if item is None:
        messagebox.showwarning("Costing form", self.t("costing_empty"))
        return
    record = self.build_costing_form_record_context(item, variant)
    style_code = str(record.get("style_code", "")).strip() or "costing"
    save_path = filedialog.asksaveasfilename(
        title="Save Costing Form",
        defaultextension=".xlsx",
        initialfile=f"{style_code}_costing_form.xlsx",
        filetypes=[("Excel workbook", "*.xlsx")],
    )
    if not save_path:
        return
    try:
        self.build_costing_form_file(record, Path(save_path))
    except Exception as exc:
        messagebox.showerror("Costing form", f"Could not export costing form.\n\n{exc}")
        return
    self.set_status(f"Saved costing form for {style_code}: {Path(save_path).name}")
    messagebox.showinfo("Costing form", f"Saved costing form for {style_code}.")


def bind_costing_list_module(app_cls: type) -> None:
    method_map = {
        "_build_costing_module": _build_costing_module,
        "create_costing_thumbnail": create_costing_thumbnail,
        "resolve_costing_item_image_path": resolve_costing_item_image_path,
        "get_costing_check_display": get_costing_check_display,
        "populate_costing_module": populate_costing_module,
        "expand_costing_items_by_option": expand_costing_items_by_option,
        "refresh_costing_tree": refresh_costing_tree,
        "update_costing_tree_check_state": update_costing_tree_check_state,
        "apply_costing_filter": apply_costing_filter,
        "costing_filter_field_value": costing_filter_field_value,
        "costing_filter_haystack": costing_filter_haystack,
        "costing_row_matches_filter": costing_row_matches_filter,
        "get_active_costing_filters": get_active_costing_filters,
        "add_costing_filter_row": add_costing_filter_row,
        "remove_costing_filter_row": remove_costing_filter_row,
        "clear_costing_filters": clear_costing_filters,
        "commit_costing_search_query": commit_costing_search_query,
        "on_costing_search_submit": on_costing_search_submit,
        "schedule_costing_filter_apply": schedule_costing_filter_apply,
        "run_costing_filter_apply": run_costing_filter_apply,
        "sync_costing_search_query": sync_costing_search_query,
        "update_costing_filter_input_state": update_costing_filter_input_state,
        "on_costing_item_selected": on_costing_item_selected,
        "load_costing_item": load_costing_item,
        "on_costing_tree_click": on_costing_tree_click,
        "on_costing_tree_double_click": on_costing_tree_double_click,
        "render_costing_variant_buttons": render_costing_variant_buttons,
        "load_costing_variant": load_costing_variant,
        "delete_selected_costing_styles": delete_selected_costing_styles,
        "get_selected_costing_item_and_variant": get_selected_costing_item_and_variant,
        "load_costing_form_records_from_source": load_costing_form_records_from_source,
        "save_costing_form_record_to_source": save_costing_form_record_to_source,
        "delete_costing_form_options_from_source": delete_costing_form_options_from_source,
        "get_costing_form_records_for_style": get_costing_form_records_for_style,
        "get_costing_form_option_summary": get_costing_form_option_summary,
        "get_costing_form_record_for_style": get_costing_form_record_for_style,
        "get_costing_form_consumption_lines": get_costing_form_consumption_lines,
        "sync_costing_form_materials_from_consumption": sync_costing_form_materials_from_consumption,
        "sync_tech_pack_consumption_from_costing_form": sync_tech_pack_consumption_from_costing_form,
        "build_costing_form_record_context": build_costing_form_record_context,
        "copy_selected_costing_option": copy_selected_costing_option,
        "edit_selected_costing_row": edit_selected_costing_row,
        "load_costing_form_assets_from_source": load_costing_form_assets_from_source,
        "save_costing_form_assets_to_source": save_costing_form_assets_to_source,
        "resolve_costing_form_template_asset": resolve_costing_form_template_asset,
        "build_clean_costing_form_template": build_clean_costing_form_template,
        "ensure_costing_form_template_asset": ensure_costing_form_template_asset,
        "build_costing_form_file": build_costing_form_file,
        "export_selected_costing_form": export_selected_costing_form,
    }
    for method_name, function in method_map.items():
        setattr(app_cls, method_name, function)
