Minimize waste when cutting profile raw materials (steel, etc.) to fulfill customer demands within a defined timespan.
Format: CSV
| Column | Description |
|---|---|
part_number |
Profile part number |
lot_batch |
Lot/batch identifier (label on physical piece) |
length |
Length of this lot batch |
length_limit |
Upper length limit per piece (if length > limit, split into multiple pieces + remainder) |
storage_location |
Free-text storage location for operator reference |
uom |
Unit of measurement for this file (mm, cm, m, inches) |
| Column | Description |
|---|---|
order_no |
Order number |
line_no |
Order line number |
component_part_number |
Required profile part number |
part_description |
Part description (informational only) |
lot_size |
Number of assemblies in order |
required_qty |
Pieces required per assembly |
length |
Length per piece (max length considering angle) |
demand_date |
Required date (YYYY-MM-DD) |
cut_type |
straight or miter (Gehrung) |
angle_left |
Left cutting angle in degrees (0 = straight) |
angle_right |
Right cutting angle in degrees (0 = straight) |
cutting_code |
Special cutting code reference (informational) |
uom |
Unit of measurement for this file |
| Parameter | Default | Description |
|---|---|---|
Cut_Thickness |
3.0 mm | Material lost per cut (kerf width) |
Timespan |
30 days | Days to optimize (all past demands + today + N days) |
Min_Remnant_Length |
100 mm | Minimum length to save as remnant (below = scrap) |
Good_Enough |
5% | Waste threshold to stop optimization attempts |
- Filtering: Include demands where
demand_date ≤ today + Timespan - Priority: Cut oldest demands first (sort by
demand_dateascending) - Part Matching: Only cut demands from stock with matching
part_number(no substitutions) - Partial Fulfillment: If stock can only fulfill some pieces, fulfill what's possible and list remainder as unfulfilled
- Splitting: If
length > length_limit, split intofloor(length / limit)pieces oflimit+ remainder - Consumption Order: Suggest oldest lot batch first, but operator may use equivalent piece
- Remnant Tracking: After partial cut, remnant gets new
lot_batchID with "-1" suffix - Cutting Direction: Cuts can be made from either end of stock piece
- Cut Loss: Each cut consumes
Cut_Thicknessof material
- Group by Angle: Cuts are grouped by angle to minimize saw adjustments
- Split Across Stock: Pieces from same demand line may come from different stock pieces
- Piece Too Long: If demand length exceeds all available stock → unfulfilled with note "no stock long enough"
- No Stock Exists: All demands for missing part number → unfulfilled with note "No stock exists"
- Duplicate Lot Batch: Treat as separate pieces (ignore duplicate)
- Malformed Demand:
length = 0orrequired_qty = 0→ unfulfilled with "ERROR - demand UNDEFINED"
Format: Excel (.xlsx) with print-ready formatting, timestamped filename (e.g., cutting_plan_2026-01-26_143022.xlsx)
Hierarchical format per stock piece:
╔══════════════════════════════════════════════════════════════════════════════════╗
║ Stock: PN-001 | Lot: LOT-A001 | Location: Aisle-3-Bin-12 | Length: 6000mm ║
╠════════╦═══════════╦════════════╦════════╦═══════════╦═══════════╦═══════════╦════════════════╣
║ Cut # ║ Order ║ Demand Date║ Length ║ Cut Type ║ L/R Angle ║ Code ║ Remaining ║
╠════════╬═══════════╬════════════╬════════╬═══════════╬═══════════╬═══════════╬════════════════╣
║ 1 ║ ORD-101 ║ 2026-01-15 ║ 1500 ║ miter ║ 45° / 0° ║ MC-001 ║ 4497mm ║
║ 2 ║ ORD-101 ║ 2026-01-15 ║ 1500 ║ miter ║ 0° / 45° ║ MC-001 ║ 2994mm ║
║ 3 ║ ORD-102 ║ 2026-01-18 ║ 2000 ║ straight ║ 0° / 0° ║ ║ 991mm ║
╠════════╩═══════════╩════════════╩════════╩═══════════╩═══════════╩═══════════╩════════════════╣
║ REMNANT: LOT-A001-1 | 991mm (save) or WASTE: 991mm (16.5%) if below threshold ║
╚═══════════════════════════════════════════════════════════════════════════════════════════════╝
[Next stock piece follows...]
| Order | Line | Part Number | Qty Needed | Length | Reason |
|---|---|---|---|---|---|
| ORD-105 | 2 | PN-003 | 5 | 2500 | No stock exists |
| ORD-106 | 1 | PN-001 | 2 | 8000 | no stock long enough |
| ORD-107 | 3 | PN-002 | 1 | 0 | ERROR - demand UNDEFINED |
| Part Number | Total Used (mm) | Total Waste (mm) | Waste % | Orders Fulfilled |
|---|---|---|---|---|
| PN-001 | 45000 | 3200 | 7.1% | 12 |
| PN-002 | 28000 | 1800 | 6.4% | 8 |
| TOTAL | 73000 | 5000 | 6.8% | 20 |
Type: Simple GUI (tkinter or similar)
- File selectors for Stock Inventory CSV and Demand List CSV
- Output directory selector
- Parameter fields with defaults:
- Cut Thickness: 3.0
- Timespan (days): 30
- Min Remnant Length: 100
- Good Enough (%): 5
- Sort By: Dropdown with options:
- "Date (Oldest First)" - sorts results by demand date ascending
- "Part Number" - sorts results alphabetically by part number
- "Run Optimization" button
- Progress/status display
- "Open Output Folder" button after completion
The optimization results can be sorted by the user-selected parameter:
- Date (Oldest First): Groups cutting plans by the earliest demand date of cuts in each stock piece, oldest demands processed first
- Part Number: Groups cutting plans alphabetically by stock part number (A-Z)
The selected sort order is applied to:
- Pattern Visualization in the GUI (rows ordered by sort parameter)
- Excel Output - Sheet 1 "Cutting Plan" (stock pieces ordered by sort parameter)
After optimization completes, display a graphical cutting pattern diagram within the GUI:
- Title: "PATTERN" header above the diagram
- Row Label (Left side): Part number and part description for each stock piece
- Rows: Each row represents one stock piece being cut
- Segments: Horizontal colored blocks showing individual cuts
- Segment Labels: Order number displayed centered within each segment (if space permits)
- Colors: Different colors distinguish different part numbers (e.g., yellow, pink, gray)
- Waste/Remnant: Gray segment at the end of each row for remaining material
| Element | Description |
|---|---|
| Row label | Part number + description shown to the LEFT of each pattern row |
| Segment text | Order number centered in segment (only if segment width > text width) |
| Block width | Proportional to cut length relative to stock length |
| Block height | Fixed height per row (e.g., 25-30 pixels) |
| Row spacing | Small gap between rows for visual clarity |
| Color palette | Predefined set of 6-8 distinct colors, cycling per part number |
| Segment border | Thin black border around each segment |
| Font | Small, readable font (centered) |
- If segment is wide enough: Display order number centered inside
- If segment is too narrow: Hide text, show via hover tooltip instead
Always available on hover, showing:
- Order number
- Length
- Cut type and angles (if miter)
- Vertical scroll if many stock pieces exceed visible area
- Language: Python 3.10+
- Dependencies:
openpyxl(for Excel output) - Algorithm: Best Fit Decreasing (BFD) with local swap optimization
- Optimization Scope: Per stock piece
- Cutting plan language: English
- Date format: YYYY-MM-DD
- Excel formatted for direct printing (no manual page setup needed)
- System is re-executed regularly with fresh ERP data (no reservation tracking)