STOCK & INVENTORY — DUAL-LOGIC MATERIAL VISIBILITY
Two parallel systems answering two different questions: “What do we have right now?” and “What will we have in the future?” — the live inventory and the dynamic projection forecast, running side by side as a built-in double-check mechanism.
The Stock & Inventory module operates on a fundamental duality. Two of its three pages — Ürün Stok and Hammadde Stok — show the physical reality of the factory at this exact moment: every basket of copper wire, every reel of extruded cable, every kilogram of raw plastic sitting in the warehouse. These change every time Production records an output or a material is consumed. The third page — Projeksiyon — ignores physical reality entirely and instead shows the plan: a week-by-week forecast of what materials will be needed (from work cards) versus what materials are coming in (from purchase orders). Projeksiyon dynamically reacts to plan changes — when a material order is delivered, it switches from the expected date/quantity to the actual delivered date/quantity, shifting the projection accordingly. It also updates when work cards are created, modified, or cancelled. This duality is intentional — it creates a built-in cross-verification mechanism where the live inventory validates the projection, and the projection warns about future shortages that the live inventory cannot yet see.
TABLE OF CONTENTS
1. WHAT STOCK & INVENTORY DOES
The Stock & Inventory module answers the two most fundamental questions in any manufacturing operation:
- “What do we have right now?” — Answered by Ürün Stok (finished/semi-finished goods) and Hammadde Stok (raw materials). These pages show the live, physical state of the factory’s inventory at t=0. Every basket of drawn copper wire (X-coded), every reel of tinned wire (Y-coded), every kilogram of raw plastic — all visible with real-time weights, statuses, allocations, and full production traceability.
- “What will we have in the future?” — Answered by Projeksiyon (Stock Projection). This page shows a week-by-week forecast across 8 material categories (copper, tin, plastic, catalyst, dye, antirodent, reels, palettes), calculating how much material will be consumed by planned work cards (MINUS) versus how much is arriving through purchase orders (PLUS), producing a running cumulative balance that reveals shortages weeks before they become critical.
These two systems run on fundamentally different data sources:
| Aspect | Live Inventory (Ürün + Hammadde Stok) | Projection (Projeksiyon) |
|---|---|---|
| Data source | half_product_stock table + materials table | work_cards (MINUS) + material_orders (PLUS) |
| Time axis | Now (t=0) | Weeks into the future (H01–H52+) |
| Changes when | Production records output, materials consumed/received | Plan changes: work card created/modified/cancelled, material order delivered (switches to actual date & quantity), order date/qty changed, order cancelled |
| Does NOT change when | — | Production physically completes (completed work cards still counted in plan) |
| CRUD | Full (view, edit weight/status, delete) | Read-only (projection is calculated, not edited) |
The double-check mechanism: If Projeksiyon shows a copper surplus for week H15, but Hammadde Stok shows copper running low now, something is wrong — perhaps a material order was recorded but the physical delivery didn’t happen. Conversely, if Hammadde Stok shows plenty of plastic but Projeksiyon projects a shortage in H20, it means current stock will be depleted by upcoming production. Neither system alone tells the complete story; together they form a comprehensive material visibility layer.
2. THE DATA FLOW
2.1 Projeksiyon Data Flow
Projeksiyon computes its weekly forecast from two independent data sources, combined in the ProjeksiyonService (804 lines):
MINUS Side: Work Card Material Requirements
Each work card carries a material_details JSON that specifies exactly what raw materials it will consume. The service maps machine types to material categories:
| Machine Type | Material Extracted | JSON Path |
|---|---|---|
| Kabatel Çekme | Copper (kg) | material_details.copper_kg |
| Kalaylama | Tin (kg) | material_details.tin_kg |
| Extruder | Plastic, Catalyst, Dye, Antirodent (kg) | quantities.*, insulation_materials[], sheath_materials[] |
| Aktarma | Reels (adet) | makara_distributions[].count |
| Paletleme | Palettes (adet) | palettes[].quantity |
All work cards except CANCELLED are included — even completed ones, because Projeksiyon tracks the full plan history. On the incoming side, when an order is delivered, the service switches from expected_date/quantity to delivered_date/delivered_quantity, so the projection dynamically reflects actual delivery timing and amounts.
PLUS Side: Material Order Incoming
Each material order has a material_type, quantity, and expected_date. For delivered orders, the system uses delivered_date and delivered_quantity instead. Material names are resolved from the supplier’s material catalog when available. All orders except CANCELLED are included.
Cumulative Calculation
The service generates all weeks from the earliest data point (which may be in a previous year) through the last visible week. It calculates weekly = plus - minus and maintains a running cumulative for each of the 8 material categories. For the user’s view window (default 10 weeks), it also tracks sub-material cumulatives (e.g., individual plastic types like “HFX 500P” within the Plastic category) for hidden shortage detection.
2.2 Live Inventory Data Flow
The live inventory pages read directly from physical inventory tables:
Ürün Stok fetches 7 parallel API calls (one per production step: Kabatel, Kalaylama, İncetel, Buncher, Extruder, E-Beam, Aktarma), while Hammadde Stok fetches all materials in a single call and groups them client-side into 8 categories.
3. THE DATABASE LAYER
The Stock module relies on two primary tables for live inventory, plus two external tables (from other modules) that feed the Projeksiyon calculation.
3.1 half_product_stock (20 columns) — Semi-Finished & Finished Products
Every basket, reel, or coil produced by any machine is recorded here. Each entry has a deterministic QR code (X1 for Kabatel, Y1 for Kalaylama, Z1 for İncetel, T1 for Buncher, U1 for Extruder, G1 for E-Beam).
| Column | Type | Details |
|---|---|---|
id | Integer PK | Auto-increment |
qr_code | String(50) | Unique, indexed. Deterministic: X1, X2, Y1, Y2, etc. |
product_code | String(100) | Indexed. e.g., KC_18 (Kabatel 1.8mm), KL_18 (Kalaylama 1.8mm) |
product_name | String(200) | Human-readable name |
production_step | String(50) | Indexed. Enum: kabatel_cekme, kalaylama, incetel, buncher, extruder, ebeam |
initial_weight_kg | Float | Weight when first produced |
remaining_weight_kg | Float | Current weight (decreases as material is consumed in next step) |
basket_number | Integer | Sequential within session |
allocation | String(20) | ORDER (tied to a customer order) or STOCK (free inventory) |
order_id | Integer FK | References orders.id if allocation = ORDER |
status | String(50) | Indexed. Enum: available, in_use, consumed, reserved, shipped |
production_output_id | Integer FK | References production_outputs.id, indexed |
session_id | Integer FK | References production_sessions.id |
work_card_id | Integer FK | References work_cards.id |
source_qr_codes | Text | JSON array of input QR codes for full traceability (e.g., a Kalaylama Y1 consumed Kabatel X3 and X4) |
produced_at | DateTime | When produced |
consumed_at | DateTime | When fully consumed (nullable) |
created_at | DateTime | UTC |
updated_at | DateTime | Auto-updated |
notes | Text | Optional notes |
Composite indexes: (production_step, status) for step-based filtering, (product_code, status) for product-based grouping. 4 foreign keys link to orders, production outputs, sessions, and work cards.
3.2 materials (from Hammadde module)
The raw material inventory table (documented in the Hammadde module). Hammadde Stok reads this table directly via GET /api/materials/list. Each row represents a physical lot of raw material with QR code, weight, supplier, lot number, and status. 8 material types: raw_copper, raw_tin, raw_plastic, raw_catalyst, raw_dye, raw_antirodent, raw_palette, raw_reel.
3.3 External Tables Feeding Projeksiyon
| Table | Module | What Projeksiyon Reads |
|---|---|---|
work_cards | Production | sevkiyat_week (delivery week), machine_type, material_details JSON, status (excludes CANCELLED) |
material_orders | Hammadde | material_type, quantity, expected_date, delivered_date, delivered_quantity, status (excludes CANCELLED) |
No dedicated “projeksiyon” table exists. The projection is computed on-the-fly from work cards and material orders. This is a deliberate design decision: since Projeksiyon reflects the plan (not a stored state), it always shows the latest version of the plan without requiring synchronization or cache invalidation. The trade-off is computational cost per request, but the query scope (hundreds of work cards and orders, not millions) makes this viable.
4. THE BACKEND ARCHITECTURE
4.1 Route Files
| File | Lines | Prefix | Endpoints | Purpose |
|---|---|---|---|---|
projeksiyon_routes.py | 245 | /api/projeksiyon | 4 | Stock projection: main data, summary, debug, per-material detail |
half_product_routes.py | 327 | /api/stock | 6 | Half-product CRUD: list, by-step, summary, by-QR, update, delete |
material_routes.py | (Hammadde) | /api/materials | ~4 | Raw material listing (shared with Hammadde module) |
4.2 The Projeksiyon Service (804 lines)
The computational heart of the module. ProjeksiyonService orchestrates the entire projection calculation:
| Method | What It Does |
|---|---|
get_earliest_data_week_offset() | Scans both work cards and material orders to find the earliest week with data. Ensures cumulative calculations include data from previous years (e.g., 2025 orders still affect 2026 projection). |
get_requirements_by_week(weeks) | Reads ALL non-cancelled work cards, extracts material requirements per week per category. Maps 5 machine types to 8 material categories. Produces both aggregate totals and detailed breakdowns by specific material name. Consolidates duplicate material names per week. |
get_incoming_by_week(weeks) | Reads ALL non-cancelled material orders. For delivered orders, uses actual delivered date/quantity; for pending/shipped, uses expected date/quantity. Maps to the same 8 categories. Resolves specific material names from supplier catalog. |
get_projeksiyon(num_weeks, offset) | Master method. Generates week windows, fetches requirements and incoming for ALL weeks (not just view window), calculates running cumulative per category and per sub-material, captures pre-view cumulative state for frontend shortage detection, returns structured response. |
4.3 API Contract — Projeksiyon Endpoints (4)
| Method | Path | Parameters | What It Does |
|---|---|---|---|
GET | /api/projeksiyon | weeks (4–52, default 10), offset (−260 to +260) | Main endpoint. Returns weekly breakdown with PLUS/MINUS/cumulative per material, sub-material cumulatives, detail arrays for consumption and incoming, navigation info. |
GET | /api/projeksiyon/summary | weeks (4–52, default 8) | Shortage alert endpoint. Scans all weeks for negative cumulatives, returns list of shortage events with material, week, and cumulative value. |
GET | /api/projeksiyon/material/{type} | type (8 valid), weeks | Per-material detail. Returns week-by-week data for a single material type with full plus/minus/cumulative. Includes plastic details for the plastic type. |
GET | /api/projeksiyon/debug | — | Debug endpoint. Shows unique sevkiyat_weeks in DB, cards per week, sample work cards with material_details keys, sample material orders. |
4.4 API Contract — Half Product Endpoints (6)
| Method | Path | What It Does |
|---|---|---|
GET | /api/stock/half-products | List all half products with optional production_step and status filters. Paginated (limit/offset). |
GET | /api/stock/half-products/by-step/{step} | Get items for a specific production step with operator name from session. Used by Ürün Stok page (7 parallel calls). |
GET | /api/stock/half-products/summary | Aggregated counts and weights per step (available count, available kg, total count) for 6 production steps. |
GET | /api/stock/half-products/{qr_code} | Get single item by QR code. |
PUT | /api/stock/half-products/{id} | Update allowed fields: remaining_weight_kg, status, notes. |
DELETE | /api/stock/half-products/{id} | Hard delete. Also deletes linked production_output and decrements QR sequence if this was the last number. |
5. THE FRONTEND
5.1 Page Structure
| Route | Component | Lines | Purpose |
|---|---|---|---|
/stok/projeksiyon | Stok/Projeksiyon/index.tsx | 3,323 | One of the largest components in the ERP. Weekly forecast table with expandable sub-material columns + 3 chart types (Line, Heatmap, Radar). Material filtering, week navigation, hidden shortage detection. |
/stok/urun-stok | Stok/UrunStok/index.tsx | 866 | Semi-finished product inventory. 7 Collapse panels (one per production step), 3-level table hierarchy (step → product group → individual item), edit/delete, bonded test viewing. |
/stok/hammadde-stok | Stok/HammaddeStok/index.tsx | 676 | Raw material inventory. 8 Collapse panels (one per material type), 3-level table hierarchy (type → material name → individual lot), conditional weight coloring, edit/delete. |
Total frontend: 4,865 lines across 3 pages. Projeksiyon alone accounts for 68% of the code.
5.2 Shared Patterns
- No WebSocket: All three pages use REST API polling or one-time fetches. No real-time subscriptions.
- Turkish locale: All pages wrap in
ConfigProvider locale={trTR}withdayjstimezone conversion toEurope/Istanbul. - No ProTable: Unlike other modules, the stock pages use plain Ant Design
Tablecomponents (except HammaddeSiparis which is in the Hammadde module). - Client-side grouping: Both Ürün Stok and Hammadde Stok fetch data and group it client-side (by product_code and material_name respectively).
5.3 Projeksiyon — Frontend Highlights
At 3,323 lines, Projeksiyon is one of the largest React components in the ERP. It has two tabs:
Tab 1 — Table View: A dynamic table with week rows and 8 material columns. Each material column shows incoming (green ↑), consumption (red ↓), and cumulative balance (color-coded Tag). Columns are expandable: clicking a material header reveals individual sub-material columns (e.g., “Filmaşin” under Copper, “HFX 500P” under Plastic). Non-expanded materials collapse to 20px color bars with traffic-light indicators. Week navigation shifts the 10-week window via offset parameter. A summary row shows final cumulatives.
Tab 2 — Charts View: Three interchangeable chart types powered by ReactECharts:
- Line Chart: Smooth cumulative lines with area fill. Overview mode (all materials) or detail mode (1–3 selected materials showing sub-material stacked bars + cumulative overlay).
- Heatmap: Weeks (x) vs materials/sub-materials (y), colored red-to-green with per-material normalization. Custom HTML axis tooltips show shortage start/increase info.
- Radar Chart: Merged mode (4 weeks overlapping) or individual mode (paginated spiders). Normalized 0–100 scale with 50 = zero line. Rich text labels colored by sign.
Hidden Shortage Detection: The most sophisticated feature. If the aggregate Copper cumulative is positive but the sub-material “Filmaşin 8mm” is negative, the cell gets a red border on a green background with a warning tooltip. This appears across all three chart types.
5.4 Ürün Stok — Frontend Highlights
7 Collapse panels (Kabatel, Kalaylama, İncetel, Buncher, Extruder, E-Beam, Aktarma), each with a color-coded icon and 3 header stats (Total kg, Sipariş kg, Serbest kg). Items grouped by product_code at level 2, individual QR-coded items at level 3. Features: edit modal (weight, allocation, notes), delete with confirmation, and a bonded test viewer that fetches test results from the Lab module for any item with a production_output_id.
5.5 Hammadde Stok — Frontend Highlights
8 Collapse panels (Bakır, Kalay, Plastik, Katalizör, Boya, Antirodent, Palet, Makara). Structurally mirrors Ürün Stok but for raw materials. Units adapt: kg for most materials, “adet” for palettes and reels. Conditional weight coloring: remaining weight shown in red below 20% of original, yellow below 95% of original. Single API call fetches all materials; client-side grouping by material_type.
5.6 Permission System
| Page | Route Guard | Manifest Buttons |
|---|---|---|
| Projeksiyon | canStok | access_page, view_projections, create_projection, edit_projection |
| Ürün Stok | canStok | access_page, view_table, view_details, edit_stock, adjust_quantity |
| Hammadde Stok | canStok | access_page, view_table, view_details, edit_stock, adjust_quantity |
All three pages share the canStok route guard from access.ts. Button-level permissions are defined in the manifest but not currently enforced in the page components — the route-level guard is the active access control.
6. THE SUBMODULES
The Stock module has three submodules, ordered by complexity:
Projeksiyon (Stock Projection)
Week-by-week forecast of material supply and demand. 804-line backend service + 3,323-line frontend — one of the largest React components in the ERP. Read-only: no CRUD, pure computation.
Hammadde Stok (Raw Material Stock)
Live inventory of all raw materials (copper, tin, plastic, catalyst, dye, antirodent, palette, reel). Reads from the Hammadde module’s materials table. 8 collapse panels, conditional weight coloring, edit/delete.
Ürün Stok (Product Stock)
Live inventory of semi-finished and finished products across all production steps. 7 collapse panels organized by machine type, 3-level grouping, bonded test integration with Lab module.
6.1 — Projeksiyon (Stock Projection)
6.1.1 — Purpose and Business Context
Projeksiyon answers the most forward-looking question in the factory: “Week by week, will we have enough of each raw material, or will we run short?” It is a read-only, computed view that blends two data streams — material consumption from production plans (work cards) and material arrivals from purchase orders — into a single, navigable weekly forecast spanning up to 5 years in either direction.
This is not a dashboard with pre-calculated snapshots. Every time the page loads, the backend queries ALL non-cancelled work cards and ALL non-cancelled material orders, computes a running cumulative balance across every week since the earliest data point, and returns the result for the user’s 10-week view window. The projection is always fresh — no caching, no stored state, no stale data.
The page serves two audiences: the purchasing team (who need to know when to order more material) and the production planners (who need to know if upcoming work cards will cause a shortage). At 3,323 lines of frontend code and 804 lines of backend service, it is one of the largest and most complex components in the Stock module.
6.1.2 — The Two Data Streams
Every piece of data in Projeksiyon comes from one of two sources:
EKSİ (MINUS) — Material Requirements
Source: work_cards table (Production module)
Key field: sevkiyat_week (delivery week, e.g., “2026-W12”)
What it extracts: Each work card carries a material_details JSON. The service maps 5 machine types to 8 material categories:
kabatel_cekme→copper_kg(Filmaşin)kalaylama→tin_kg(Kalay)extruder→ plastic, catalyst, dye, antirodent (frominsulation_materials[]andsheath_materials[])aktarma→ reels (frommakara_distributions[]) — note: kangal (coilless) uses plastic packaging and is NOT trackedpaletleme→ palettes (frompalettes[])
Includes: ALL statuses except CANCELLED — even completed work cards count because the plan happened.
ARTI (PLUS) — Incoming Materials
Source: material_orders table (Hammadde module)
Key field: expected_date or delivered_date
Dynamic switching: For pending/shipped orders, uses expected_date + quantity (the plan). For delivered orders, switches to delivered_date + delivered_quantity (the actual). This means the projection dynamically reflects reality as deliveries happen.
Material resolution: The service resolves specific material names via the supplier_material relationship (e.g., not just “plastic” but “HFX 500P”). Defaults to category name if no supplier material linked.
Includes: ALL statuses except CANCELLED.
6.1.3 — The Cumulative Calculation Engine
The core of Projeksiyon lives in ProjeksiyonService.get_projeksiyon(). The calculation is not a simple “show this week’s plus and minus” — it is a full historical running total:
- Find earliest data:
get_earliest_data_week_offset()scans both work cards (MIN(sevkiyat_week)) and material orders (MIN(expected_date),MIN(delivered_date)) to find the very first week that has any data. This might be in a previous year (e.g., 2025 orders still affect 2026 projection). - Generate ALL weeks: From the earliest data week through the last week in the user’s view (e.g., if earliest data is 2025-W40 and the user is viewing 2026-W05 to W14, the system generates weeks from W40 through W14 — 27 weeks of computation for a 10-week view).
- Fetch requirements and incoming for ALL weeks: Two bulk queries: one for work cards grouped by
sevkiyat_week, one for material orders mapped to their respective weeks. - Walk through every week in order: For each week, compute
weekly = plus - minusper material category, and add it to the runningcumulativetotal. Also track sub-material cumulatives (e.g., “Filmaşin 8mm” within Copper). - Capture pre-view state: Before processing the first week in the user’s view window, snapshot the current cumulative state into
pre_view_sub_material_cumulatives. This is critical for the frontend’s hidden shortage detection. - Return only view weeks: Of all the weeks processed, only the 10 weeks the user is actually viewing are included in the response. But their cumulative values include ALL historical data.
Why not just compute the view window? If you only compute weeks 5–14 and a massive copper delivery happened in week 2, the cumulative for week 5 would show 0 instead of +5000 kg. Historical context is essential for accurate cumulative balances. The trade-off is more computation per request, but the data volume (hundreds of work cards and orders, not millions) makes this approach viable without caching.
6.1.4 — Detail Consolidation
Both get_requirements_by_week() and get_incoming_by_week() produce not only aggregate totals per category but also detailed breakdowns by specific material name. For example, an Extruder work card might consume 200 kg of “HFX 500P” and 150 kg of “HFFR 302” — both are “plastic” but tracked individually.
After processing all cards/orders, the service consolidates duplicate names within each week. If two Extruder cards in the same week both consume “HFX 500P”, their quantities are merged into a single detail entry. This consolidation runs across all 8 detail arrays (copper_details, tin_details, plastic_details, catalyst_details, dye_details, antirodent_details, makara_details, palette_details).
6.1.5 — API Endpoints
| Method | Path | Parameters | Purpose |
|---|---|---|---|
GET | /api/projeksiyon | weeks (4–52, default 10), offset (−260 to +260) | Main endpoint. Returns weekly breakdown with PLUS/MINUS/cumulative per material category, sub-material cumulatives per week, requirement and incoming detail arrays, work card and order counts, navigation metadata, and pre-view sub-material cumulatives for shortage detection. |
GET | /api/projeksiyon/summary | weeks (4–52, default 8) | Shortage alert endpoint. Scans all weeks for any material category with a negative cumulative, returns a flat list of shortage events with material key, label, week, cumulative value, and unit. |
GET | /api/projeksiyon/material/{type} | type (8 valid types), weeks | Per-material detail. Returns week-by-week plus/minus/cumulative for a single material type. Includes plastic_details breakdown for the plastic type. |
GET | /api/projeksiyon/debug | — | Debug endpoint. Shows unique sevkiyat_week values in the database, card counts per week, sample work cards with their material_details keys, and sample material orders. Used during development to verify data mapping. |
6.1.6 — Frontend Architecture (3,323 Lines)
The Projeksiyon page is a single React component with two tabs: Projeksiyon (table view) and Grafikler (charts view). Both tabs consume the same API response.
State Management
| State Variable | Type | Purpose |
|---|---|---|
projeksiyonData | ProjeksiyonResponse | null | The entire API response, shared between both tabs |
weekOffset | number | Current week offset from H01. Changed by navigation arrows. Triggers API refetch. |
expandedMaterials | string[] | Which material columns are currently expanded to show sub-materials |
knownSubMaterials | Record<string, string[]> | Persists discovered sub-material names across navigations so expand arrows don’t disappear |
graphType | 'line' | 'heatmap' | 'waterfall' | Active chart type (waterfall is actually radar) |
selectedGraphMaterials | string[] | Material filter for charts. Empty = “Tümü” (all). 1–3 = detail mode with sub-material breakdown. |
radarViewMode | 'merged' | 'individual' | Radar chart display mode: 4 weeks overlapping vs. paginated individual spiders |
radarPage | number | Current page for individual radar mode pagination |
activeTab | 'projeksiyon' | 'grafikler' | Current tab |
6.1.7 — Tab 1: The Projection Table
The table is the primary interface. It displays 10 weeks as rows and 8 material categories as columns. The table is NOT an Ant Design ProTable — it is a plain Table with heavily customized column rendering.
Column Structure
Each material column renders three pieces of information per cell:
- Incoming (PLUS): Green upward arrow with the amount of material arriving that week
- Consumption (MINUS): Red downward arrow with the amount of material being consumed
- Cumulative: A color-coded
Tagshowing the running total. Green if positive (surplus), red if negative (shortage), grey if zero.
Expandable Sub-Material Columns
This is one of the most distinctive features. Each material column header has a small expand arrow. Clicking it reveals individual sub-material columns nested under the parent. For example, expanding “Plastik” might reveal columns for “HFX 500P”, “HFFR 302”, and “XLPE” — each showing their own plus/minus/cumulative within that week.
Sub-material names are discovered from the API response’s detail arrays and persisted in knownSubMaterials so they survive week navigation. When a column is NOT expanded, it collapses to a 20px-wide color bar with a traffic-light indicator (green dot = all positive, red dot = any negative cumulative, yellow dot = mixed). This compact view lets users see all 8 materials at once without horizontal scrolling.
Week Navigation
Left/right arrows shift the 10-week window by 10 weeks at a time (via weekOffset state). A calendar icon resets to the current week (offset = 0). The API enforces limits of −260 to +260 (5 years each direction). Each navigation triggers a new API call; the backend recalculates from scratch.
Summary Row
The last row of the table shows the final cumulative for each material across the entire view window. It serves as the “bottom line” — after all incoming and consumption for these 10 weeks, this is where each material stands.
6.1.8 — Hidden Shortage Detection
This is the most sophisticated feature in the entire Stock module. The problem it solves:
Scenario: The aggregate “Plastic” cumulative for week H12 is +2,500 kg. Everything looks fine. But hidden inside, sub-material “HFX 500P” is at −180 kg while “HFFR 302” has +2,680 kg. The surplus of one plastic masks the shortage of another. You cannot substitute HFFR for HFX — they are chemically different compounds.
How it works:
- The backend tracks
sub_material_cumulativesas a dictionary keyed bycategory_materialName(e.g.,plastic_HFX 500P). This is updated for EVERY week (not just the view window). - Each week in the response carries its own
sub_material_cumulativessnapshot. - The frontend checks: if the parent category cumulative is ≥ 0 but ANY sub-material within that category has a negative cumulative, this is a hidden shortage.
- Visual indicator: the cell gets a red border on a green background with a warning tooltip listing which specific sub-materials are in deficit.
This detection works across all three visualization modes (table, line chart, heatmap, radar). In the radar chart, hidden shortages are indicated by a special rich-text label style that combines green text with a red indicator.
6.1.9 — Tab 2: Charts View
The charts tab provides three interchangeable visualization types, all powered by ReactECharts (echarts-for-react). All share the same data from projeksiyonData and respond to the same week navigation and material filter controls.
Material Filter Pills
A row of clickable pills at the top allows filtering by material. Three modes:
- Tümü (All): No filter selected. Charts show aggregate cumulative lines for all materials. No sub-material breakdown.
- 1–3 materials selected (Detail Mode): Charts switch to showing sub-material stacked bars for consumption and incoming, with cumulative overlay lines per sub-material. This is where the hidden shortage detection becomes most visible.
- 4+ materials: Not allowed. The UI enforces a maximum of 3 selections by deselecting the oldest when a 4th is clicked. This prevents chart overcrowding.
Chart Type 1: Line Chart
Smooth cumulative lines with gradient area fill. In overview mode, each material gets its own line (copper=orange, tin=cyan, plastic=purple, etc.). A dashed red zero-line marks the shortage threshold. In detail mode, sub-materials get stacked bar charts for consumption (negative, red) and incoming (positive, green), with individual cumulative overlay lines.
The tooltip is extensively customized: it shows the week label, date range, sections for “KÜMÜLATİF” (cumulative), “KULLANILAN (Bu Hafta)” (consumption), “İHTİYAÇ (Eksik)” (shortages), and “GELEN (Bu Hafta)” (incoming). Shortages use a 2-column grid layout when there are more than 6 items.
Chart Type 2: Heatmap
A grid with weeks on the x-axis and materials/sub-materials on the y-axis. Each cell is colored on a red-to-green gradient based on cumulative value. The coloring uses per-material normalization — each material row is normalized independently so a +5000 kg copper surplus and a +50 adet palette surplus both appear equally green.
Custom HTML axis tooltips appear on hover over material labels. These tooltips show detailed shortage information: which weeks this material first goes negative, whether the shortage is increasing or decreasing, and which specific sub-materials are affected. Shortage information is pre-calculated and stored in heatmapShortageInfoRef for efficient event handling.
Chart Type 3: Radar Chart
Operates in two modes:
- Merged mode: Last 4 weeks overlapping on a single radar. Each week gets a different color (grey, amber, purple, cyan). Materials are positioned around the perimeter with rich-text labels showing the material name and cumulative value, colored green/red by sign. A dashed circle at the 50 mark represents the zero line (scale is normalized 0–100).
- Individual mode: Paginated display showing 4 weeks per page as separate spider diagrams side by side. Each has its own radar with the week label below. Left/right arrows paginate through weeks.
In both modes, hidden shortages are visualized using a special label format: the material name appears with an amber/warning color and the value shows green with a red warning indicator.
6.1.10 — Dashboard Statistics Cards
Above the charts, 4 statistic cards provide a quick summary:
- Toplam Gelen (Total Incoming): Sum of all PLUS across all materials and weeks
- Toplam İhtiyaç (Total Consumption): Sum of all MINUS across all materials and weeks
- Net Bakiye (Net Balance): Total incoming minus total consumption. Green if positive, red if negative.
- Stok Sağlığı (Health Score): A percentage calculated as
(totalPlus / totalMinus) × 50, capped at 0–100. Rendered as a Progress bar. Below 50% = red, 50–80% = yellow, above 80% = green.
6.1.11 — Extruder Material Extraction Logic
The most complex material mapping is for Extruder work cards. A single Extruder card can consume multiple materials simultaneously because cable extrusion involves layered coatings:
- Insulation materials (
insulation_materials[]): Each entry hasmaterial_name(plastic type),plastic_kg,catalyst_name,catalyst_kg, anddyes[]array withdye_nameanddye_kg. - Sheath materials (
sheath_materials[]): Same structure as insulation but also includesantirodent_nameandantirodent_kg(only the outer sheath can have rodent repellent).
The service walks through both arrays, extracting individual material names and quantities. This means a single Extruder work card can produce detail entries for 3–4 different plastics, 2 catalysts, 2 dyes, and 1 antirodent — all mapped to their respective category detail arrays and consolidated if the same material name appears in multiple cards for the same week.
6.1.12 — Week Key System
All temporal data in Projeksiyon uses ISO week keys in the format YYYY-WNN (e.g., “2026-W12”). The service provides utility functions:
get_week_key(date): Converts a Pythondateto its ISO week keyweek_key_to_offset(week_key): Converts a week key to an integer offset from H01 of the current year. Used to compare weeks across year boundaries (e.g., 2025-W52 = offset −1 from 2026-W01).generate_weeks(num_weeks, offset): Produces week metadata including week_key, week_label (H01–H52), date range in Turkish (e.g., “06-12 Oca”), and ISO start/end dates.
The week labels displayed in the UI use HNN format (e.g., H01, H12, H52) with Turkish month abbreviations in the date range (Oca, Şub, Mar, Nis, May, Haz, Tem, Ağu, Eyl, Eki, Kas, Ara).
6.1.13 — What Changes the Projection
Since the projection is computed on-the-fly, any change to its source data immediately affects the output:
| Action | Effect on Projection |
|---|---|
| New work card created | MINUS increases for the card’s sevkiyat_week |
Work card’s sevkiyat_week changed | MINUS moves from old week to new week |
| Work card cancelled | MINUS disappears (CANCELLED excluded from query) |
| New material order created | PLUS appears in the expected_date week |
| Material order’s expected date changed | PLUS moves from old week to new week |
| Material order quantity changed | PLUS amount changes for that week |
| Material order delivered | PLUS switches from expected_date/quantity to delivered_date/delivered_quantity — week and amount may both shift |
| Material order cancelled | PLUS disappears entirely |
| Production physically completes | No effect — completed work cards are still included |
6.1.14 — Permission Model
Projeksiyon uses the canStok route guard from access.ts. The permission manifest defines 4 buttons: access_page, view_projections, create_projection, edit_projection. However, since Projeksiyon is entirely read-only (no CRUD operations), the create and edit permissions are vestigial — there is nothing to create or edit. The effective access control is simply whether the user can access the Stock pages.
6.1.15 — What This Submodule Does NOT Do
- It does not store any data — no dedicated database table exists
- It does not support CRUD — the projection is computed, not edited
- It does not use WebSocket — data is fetched once per page load or navigation
- It does not cache results — every request computes from scratch
- It does not account for current physical stock — it only tracks planned consumption vs. planned/actual arrivals
- It does not send alerts — shortage detection is visual only (the
/summaryendpoint exists but is not consumed by any notification system) - It does not track kangal (coilless cable packaging) — kangal uses plastic packaging which is not a tracked raw material
The design philosophy: Projeksiyon deliberately avoids storing its own state. By computing everything on-the-fly from work cards and material orders, it guarantees that the projection always reflects the absolute latest version of the plan. There is no synchronization problem, no stale cache, no “last refreshed 2 hours ago” warning. The price is computation per request, but the data scale (hundreds of records, not millions) makes this a good trade-off. If the factory grows to tens of thousands of work cards, a materialized view or caching layer can be added without changing the API contract.
6.2 — Hammadde Stok (Raw Material Stock)
6.2.1 — Purpose and Business Context
Hammadde Stok answers the question “What raw materials do we physically have in the warehouse right now, and how much of each is still available?”
This page exists because of a specific business need: the Hammadde module already has Hammaddeler Listesi (Materials List) — a flat, 14-column admin table that shows every individual material record with photos, WebSocket real-time updates, bilingual search, and granular edit forms. But Hammaddeler Listesi is a record-level view. A stock manager doesn’t want to scroll through 500 individual QR-coded entries to answer “how many kilograms of HFX 500P plastic do we have?” They need an aggregated category-level view that groups materials by type and name, shows totals, and highlights what’s available vs. what’s in use.
The data that populates this page comes from two upstream flows:
- Hammadde Girişi (Material Entry) creates the records — every time raw material arrives at the factory, an operator scans it through one of four entry archetypes (copper, tin, lot-based, count-based), and it enters the
materialstable withstatus = 'received'andremaining_weight = weight. - Üretim (Production) consumes the records — when Production modules (especially Kabatel Çekme / wire drawing) use a raw material, they select it by QR code and deduct from its
remaining_weight. A 1,000 kg copper rod (A1) that goes through wire drawing comes back as 980 kg. Production also changesstatus:received→in_use(material is currently on a machine) →consumed(fully depleted). This is exactly what the “Kalan” (Remaining) column and the conditional coloring (yellow/red) are designed to show.
So Hammadde Stok sits at the intersection: Hammadde Girişi fills it, Production drains it, and this page visualizes the current balance. It reads directly from the materials table (owned by the Hammadde module) via the shared /api/materials/list endpoint — it has no backend routes of its own. All grouping and aggregation happens client-side.
The page is implemented as a single React component (HammaddeStok) in src/pages/Stok/HammaddeStok/index.tsx (676 lines), using Ant Design’s Collapse with nested Table components, wrapped in a ConfigProvider with Turkish locale (trTR).
6.2.2 — CRUD Flow
Read (Primary Operation):
- Page loads →
useEffect([], [])triggersfetchMaterials()on mount - Single
GET /api/materials/listcall fetches ALL raw materials as a flat array, regardless of type - Response passes through
processAndGroupMaterials()which: filters bymaterial_typeinto 8 buckets, groups within each bucket bymaterial_name, calculates aggregate totals (total, reserved, available), and stores everything in thesectionDatastate object - User clicks a Collapse panel to see the grouped table for that category
- User clicks the expand arrow on a group row to see individual lots with QR codes, weights, supplier, dates, and status
- User can click the “Yenile” (Refresh) button in the page header to re-fetch all data at any time
Update (Edit Modal):
- Click the
EditOutlinedicon on any individual item row (Level 3) - Modal opens with title “Düzenle: {QR_CODE}” (e.g., “Düzenle: A7”)
- Form pre-fills from the selected item’s current values via
form.setFieldsValue() - Two fields available (see 6.2.10 for full details):
remaining_weight—InputNumber, min=0, step=0.1. Hidden for palette/reel typesstatus—Selectdropdown with 5 options
- On submit:
PUT /api/materials/update/{id}with the form values - On success:
message.success('Güncellendi')→ modal closes → form resets →fetchMaterials()refetches all data - On failure:
message.error('Güncellenemedi')
Delete (Hard Delete with Confirmation):
- Click the
DeleteOutlinedicon (styled withcolor: '#ff4d4f') on any individual item row Modal.confirm()opens with title “Silme Onayı”, content “{QR_CODE} kodlu malzemeyi silmek istediğinize emin misiniz?”, a red “Sil” (Delete) button, and an “İptal” (Cancel) button- On confirm:
DELETE /api/materials/{id}/hard-delete— this is a permanent, irreversible deletion. The material record and all associatedmaterial_photosrecords are removed from the database - On success:
message.success('Silindi')→fetchMaterials()refetches - On failure:
message.error('Silinemedi')
Create: Not available on this page. Material creation happens exclusively through the Hammadde Girişi (Material Entry) page in the Hammadde module. This page only provides read, update, and delete operations.
6.2.3 — Data Source and API
Hammadde Stok consumes three API endpoints, all owned by the Hammadde module (material_routes.py), not the Stock module:
| Method | Path | What It Returns |
|---|---|---|
GET | /api/materials/list | ALL raw materials as a flat JSON array. Each object includes: id, qr_code, material_type, material_name, supplier_name, lot_number, weight (original measured weight at entry), remaining_weight (current weight after production consumption), form_weight (supplier’s declared weight), quantity (label count for lot-based materials), received_date (ISO datetime), status (string: received/available/in_use/consumed/rejected) |
PUT | /api/materials/update/{id} | Updates remaining_weight and/or status on a single material record. Called from the edit modal. |
DELETE | /api/materials/{id}/hard-delete | Permanently removes the material and its associated photos from the database. Called from the delete confirmation modal. |
The API response is a flat array — no server-side grouping, filtering, or pagination. The frontend receives every material regardless of type and handles all grouping logic client-side. This means the entire raw material inventory loads in a single request.
6.2.4 — State Management Architecture
The component manages 5 pieces of state:
allMaterials(RawMaterial[]) — the raw API response, stored for potential re-processingloading(boolean) — true during thefetchMaterials()call, drives the “Yenile” button spinner and the loading indicator inside each panelsectionData(Record<string, SectionData>) — the core data structure, keyed by material type. Each entry holds:groups(array ofMaterialGroupSummary),loading,expandedKeys(which group rows are expanded), andtotalItems(count of individual materials in this category). Initialized with all 8 category keys and empty defaults.activePanels(string[]) — which Collapse panels are open. Passed toCollapseviaactiveKey.editModalVisible+editingItem— controls the edit modal visibility and which material is being edited
The SectionData structure preserves expandedKeys across data refetches. When the user edits or deletes a material and the page refetches, the processAndGroupMaterials() function reads the previous expandedKeys via sectionData[cat.key]?.expandedKeys || [] and carries them into the new state. This means open group rows stay open after an update — the user does not lose their scroll position or expanded state.
6.2.5 — The 8 Material Category Panels
The page renders an Ant Design Collapse component with 8 panels, one per material category. The categories are defined in a constant MATERIAL_CATEGORIES array, each with a key, label (Turkish name), icon (Ant Design icon component), and color. The categories match the Hammadde Girişi page’s card colors exactly:
| Category | Key | Icon | Color | Unit | QR Prefix |
|---|---|---|---|---|---|
| Bakır (Copper) | raw_copper | MenuOutlined | #cd7f32 (bronze) | kg | A |
| Kalay (Tin) | raw_tin | GoldOutlined | #c0c0c0 (silver) | kg | B |
| Plastik (Plastic) | raw_plastic | DockerOutlined | #3498db (blue) | kg | C |
| Katalizör (Catalyst) | raw_catalyst | ExperimentOutlined | #9b59b6 (purple) | kg | D |
| Boya (Dye) | raw_dye | BgColorsOutlined | #e74c3c (red) | kg | E |
| Antirodent | raw_antirodent | BugOutlined | #27ae60 (green) | kg | F |
| Palet (Palette) | raw_palette | BorderInnerOutlined | #f39c12 (orange) | adet | I |
| Makara (Reel) | raw_reel | PicCenterOutlined | #1abc9c (teal) | adet | H |
The categories and their visual identity are shared with Hammadde Girişi — a user sees the same colors and icons across both modules for consistency.
6.2.6 — Panel Header: Inline Statistics Dashboard
Each panel header is not just a label — it is a mini dashboard visible even when collapsed. From left to right it shows: the color-coded category icon, the category name, a record count tag (e.g., “47 kayıt”), and three aggregate statistics pushed to the right:
- Toplam (Total): Sum of
remaining_weightfor kg-based materials (falls back toweightif remaining is null), sum ofweight(count) for palette/reel. Turkish locale formatting (e.g., “12.456,3 kg”). - Kullanımda (In Use): Blue tag. Sum from items where
status === 'in_use'— materials currently on a production machine. - Serbest (Available): Green tag. Sum from items where
status === 'received'or'available'— materials ready for production to consume.
The getCategoryTotals() function computes these by iterating all groups and summing total_quantity, reserved_quantity, and available_quantity. Unit is “adet” for palette/reel, “kg” for everything else.
6.2.7 — Three-Level Table Hierarchy
The page implements a three-level drill-down that lets users navigate from high-level category totals down to individual lot details:
Level 1 — Category Panel (Collapse): The 8 Collapse panels themselves. Each panel header shows category name + icon + aggregate statistics. Opening a panel reveals the Level 2 table. Multiple panels can be open simultaneously — the activePanels state is an array of keys.
Level 2 — Material Name Group Table: Inside each panel, materials are grouped by material_name. For example, under “Plastik”, you see groups: “HFX 500P”, “HFFR 302”, “XLPE 2003”, “PVC 70”. Each group row shows: Malzeme Adı (bold name + lot count, e.g., “HFX 500P (12 lot)”), Toplam (aggregate weight/quantity), Kullanımda (blue tag, in-use total), Serbest (green tag, available total). If a material has no name, it falls into “{Category} (İsimsiz)”.
Level 3 — Individual Lots (Expanded Row): Expanding a group row reveals the individual material entries. The detail table shows 7–8 columns:
| Column | Behavior |
|---|---|
| QR Kod | Blue tag showing the deterministic QR code (A1, B3, C12, etc.). Pinned left. |
| Ölçülen / Miktar | Original weight at entry. Title adapts: “Ölçülen” for kg types, “Miktar” for palette/reel. |
| Kalan (Remaining) | Current remaining weight after Production consumption. Absent for palette/reel. Uses remaining_weight ?? weight fallback. Color-coded (see 6.2.8). |
| Durum (Status) | Color-coded tag: green=Teslim Alındı (received), blue=Mevcut (available), orange=Kullanımda (in_use by Production), grey=Tükendi (consumed), red=Reddedildi (rejected by Lab) |
| Tedarikçi | Supplier name from Tedarikçi Yönetimi, or “-” if none |
| Lot No | Lot/batch number from Hammadde Girişi, or “-” if none |
| Giriş Tarihi | Entry date, converted from UTC to Europe/Istanbul timezone. DD.MM.YY format. |
| İşlem (Actions) | Edit and Delete buttons. Pinned right. |
6.2.8 — Conditional Weight Coloring
The “Kalan” (Remaining) column is one of the most operationally important visual features on this page. It uses conditional coloring to provide instant visual feedback about material consumption levels. The logic computes two boolean flags for each row:
isReduced = currentWeight < originalWeight * 0.95— the material has been partially consumed (more than 5% used)isLow = currentWeight < originalWeight * 0.2— the material is critically low (less than 20% remaining)
The rendering rules:
- Normal (no color, normal weight):
remaining_weight ≥ 95%of original — material is barely touched or freshly entered - Yellow (#faad14) + bold (fontWeight: 600):
remaining_weight < 95%but ≥ 20% of original — partially consumed, draw attention - Red (#ff4d4f) + bold (fontWeight: 600):
remaining_weight < 20%of original — critically low, needs reorder
Concrete example: A 1,000 kg copper wire rod (QR: A1) shows normal text until it drops below 950 kg. At 949 kg it turns yellow. At 199 kg it turns red. The currentWeight is computed as remaining_weight ?? weight ?? 0 for backward compatibility with older records that don’t have remaining_weight set.
For palette and reel materials, this column is completely absent from the table — not hidden, but conditionally excluded from the columns array using a spread with empty array: ...(!isPaletteOrReel ? [{ ... }] : []). Counted items don’t have a remaining weight concept — they are either available or consumed as whole units.
6.2.9 — Unit Adaptation: kg vs adet
The page dynamically adapts its behavior based on whether a material is measured by weight or counted as discrete pieces. This distinction affects five different places in the UI:
- Panel header statistics: Show “kg” or “adet” after aggregate numbers
- Group table columns: Unit suffix on Toplam, Kullanımda, Serbest values
- Detail table column title: “Ölçülen” (Measured) for kg types vs “Miktar” (Quantity) for adet types
- Detail table “Kalan” column: Completely hidden for palette/reel
- Edit modal: The “Kalan Ağırlık (kg)” field is hidden for palette/reel types
The determination is made by checking materialType === 'raw_palette' || materialType === 'raw_reel'. These are the only two count-based categories — all six others use kg. For quantity calculations in the grouping logic, palette/reel materials read from item.weight (which stores the count), while kg-based materials read from item.remaining_weight ?? item.weight.
6.2.10 — Client-Side Grouping Logic
The processAndGroupMaterials() function is the core data transformation engine. It takes the flat API response and produces the hierarchical sectionData structure. The function runs in four stages:
- Category filter: For each of the 8 categories, filter
materials.filter(m => m.material_type === cat.key) - Name grouping: Within each category, build a
Record<string, RawMaterial[]>keyed bymaterial_name. Ifmaterial_nameis null/empty, the key becomes “{label} (İsimsiz)” (e.g., “Bakır (İsimsiz)”) - Group aggregation: For each group, create a
MaterialGroupSummaryobject:total_quantity: Sum ofremaining_weight ?? weight ?? 0for kg types, orweight ?? 0for adet types, across ALL items regardless of statusreserved_quantity: Sum from items wherestatus === 'in_use'available_quantity: Sum from items wherestatus === 'received' || status === 'available'- Items with other statuses (
consumed,rejected) are counted intotal_quantitybut not in reserved or available — they effectively represent consumed or discarded stock
- State preservation: Carry forward the previous
expandedKeysfrom the existingsectionData, so open group rows remain open after a data refresh
6.2.11 — The Edit Modal
The edit modal is deliberately minimal — a single variant, unlike Hammaddeler Listesi’s three variants (copper/tin, lot-based, palette/reel). It provides only two fields:
- Kalan Ağırlık (Remaining Weight): A numeric input pre-filled with the material’s current
remaining_weight. This is the field Production affects when consuming material, and this modal lets a stock manager manually correct it if the system value drifts from physical reality. Hidden for palette/reel since those are whole units. - Durum (Status): Dropdown with 5 options: Teslim Alındı (received), Mevcut (available), Kullanımda (in_use), Tükendi (consumed), Reddedildi (rejected). This lets a manager manually override the lifecycle state — e.g., marking a damaged material as rejected, or correcting a status that Production failed to update.
Unlike Hammaddeler Listesi, this modal does not allow editing QR codes, lot numbers, supplier names, or photos. Those belong to the Hammadde module’s full edit flow. This modal is strictly for stock-level corrections: “how much is actually left?” and “what state is it really in?”
6.2.12 — Loading and Empty States
The page handles two empty-data scenarios:
- Loading state: While data is being fetched, each panel shows a spinner with “Yükleniyor...”. The “Yenile” (Refresh) button in the page header also shows a loading state.
- Empty category: If a category has no materials (e.g., no antirodent currently in stock), the panel shows “Bu kategoride stok bulunamadı” (No stock found in this category).
6.2.13 — Timezone Handling
All dates from the API are stored as UTC in the database. The frontend converts them to Turkey timezone for display using dayjs with the utc and timezone plugins:
- The timezone constant is defined at module level:
const TURKEY_TZ = 'Europe/Istanbul' - Date rendering:
dayjs.utc(date).tz(TURKEY_TZ).format('DD.MM.YY') - This ensures a material entered at 23:30 UTC on January 15 shows as January 16 in the Turkish display (UTC+3), correctly reflecting the local date of entry
6.2.14 — Connection to Other Modules
Hammadde Stok does not exist in isolation — it is essentially a read-only window into data that is created, modified, and consumed by other modules. Understanding those connections is key to understanding this page:
Upstream: Who fills the inventory?
- Hammadde Girişi (Material Entry) — the sole creator. Every single row visible on this page was created through one of the four entry archetypes in Hammadde Girişi. When a copper wire rod arrives, an operator weighs it, scans photos, assigns QR code A{n}, and it enters the
materialstable withstatus = 'received'andremaining_weight = weight. That record immediately becomes visible here under the “Bakır” panel. Hammadde Stok cannot create, only view. - Hammadde Sipariş (Material Orders) — the reason materials arrive in the first place. When a purchase order is fulfilled and materials are delivered, the entry operator can link the material to the order group code (R1, R2, etc.). This traceability exists in the data but is not displayed on this page — it’s visible on Hammaddeler Listesi’s “Sipariş No” column instead.
- Tedarikçi Yönetimi (Supplier Management) — defines the supplier names that appear in the detail rows. The supplier association is set at entry time and is read-only here.
Downstream: Who drains the inventory?
- Üretim / Production (especially Kabatel Çekme) — the primary consumer. When Production starts a work card, it selects raw materials by QR code. For wire drawing (Kabatel Çekme), an operator picks a copper rod (e.g., A7) and feeds it into the machine. Production then updates the material record:
statuschanges fromreceived/availabletoin_use, andremaining_weightdecreases as material is consumed. When the rod is fully used,statusbecomesconsumed. This is the entire reason the “Kalan” column exists with its yellow/red coloring — it visualizes how much Production has consumed. Similarly, plastic granules, tin, catalysts, and dyes are consumed by their respective production steps (extrusion, tinning, etc.). - Lab (indirect) — Lab tests may result in a material being
rejected, which changes its status and effectively removes it from the “Serbest” (Available) count on this page.
Sibling views over the same data:
- Hammaddeler Listesi (in Hammadde module) — reads from the exact same
/api/materials/listendpoint. It is the full-power admin view: 14 columns, lazy-loaded photos, WebSocket real-time sync, bilingual search, three edit form variants, QR label printing, 12 granular permissions. This page is the record-level view; Hammadde Stok is the stock-level view. Together they cover different user needs over identical data. - Projeksiyon (in Stock module) — also uses material data, but for a completely different purpose. Projeksiyon computes what the stock will be in future weeks by combining current inventory + incoming orders − production requirements. Hammadde Stok shows what the stock is right now. They are complementary: one is a live snapshot, the other is a weekly forecast.
6.2.15 — Permission Model
The page uses the canStok route guard from access.ts for page-level access. The permission manifest in permissionManifest.ts defines 5 buttons for this page:
access_page— can access the Hammadde Stok pageview_table— can see the category tablesview_details— can expand groups to see individual lotsedit_stock— can open the edit modaladjust_quantity— can modify remaining weight values
However, unlike Hammaddeler Listesi which enforces permissions at the button level (e.g., disabling the print button without print_qr permission), this page does not enforce button-level checks in the frontend. The edit and delete buttons are always rendered for any user who has page access. Permission enforcement happens at the API level only.
6.2.16 — What This Submodule Does NOT Do
- It does not have its own backend routes — it consumes the Hammadde module’s
material_routes.pyendpoints - It does not support material creation — that happens exclusively in Hammadde Girişi
- It does not use WebSocket for real-time updates — data is fetched once on mount and refreshed manually or after edit/delete operations. Compare with Hammaddeler Listesi which subscribes to
materialsandallWebSocket rooms - It does not have server-side filtering or pagination — all 8-way category filtering, name-based grouping, and quantity aggregation is client-side
- It does not show material photos — no delivery or test photos are displayed. These are only visible in Hammaddeler Listesi
- It does not show order linkage — the “Sipariş No” (order group code) column exists in Hammaddeler Listesi but not here
- It does not support printing — no QR label reprint functionality. That is in Hammaddeler Listesi
- It does not have a client-side search bar — browsing is done by expanding category panels and group rows
- It does not track consumption history — it shows the current snapshot only, not a timeline of weight changes
- It does not have soft-delete — the delete operation uses
/hard-delete, permanently removing the record
Hammadde Stok vs Hammaddeler Listesi — same data, different purpose: Both pages read from the same /api/materials/list endpoint and display the same materials table data. The difference is perspective. Hammaddeler Listesi (in the Hammadde module) is the full-power admin view: 14 columns, lazy photos, WebSocket, bilingual search, three edit form variants, printing, 12 granular permissions. Hammadde Stok (in the Stock module) is the stock manager’s view: grouped by category and material name, with aggregate totals front and center, optimized for answering “how much do we have?” rather than “what is the full history of this lot?” The edit modal here is deliberately minimal (weight + status only) because the full edit belongs in the Hammadde module.
6.3 — Ürün Stok (Product Stock)
Live inventory of semi-finished and finished products, organized by production step — the output side of the factory floor.
6.3.1 — Purpose and Business Context
Ürün Stok answers the question: "What did the factory produce and where is it now?"
While Hammadde Stok tracks raw inputs waiting to be consumed, Ürün Stok tracks the outputs: every basket and reel that came off a production machine. Each item in this page was physically created during a Production Session — an operator ran a machine, recorded baskets of output, printed QR labels, and those baskets landed here. The page exists because:
- Üretim Listesi (Production List) creates these items — when a production session records output baskets, each basket is simultaneously written to
production_outputsandhalf_product_stock. This page is the read-only window into that accumulated inventory. - Üretim Geçmişi (Production History) shows the same items from a session-centric perspective (which session produced what). Ürün Stok shows them from a stock-centric perspective (how much of each product code exists right now, regardless of which session made it).
- Order Module determines allocation — when a work card is opened for an order, baskets produced within the order's required quantity are marked
ORDER. Any excess production becomesSTOCK(free inventory). This allocation split is a core business concept visible in every group row. - Lab Module (Test Yönetimi) bonds quality tests to specific production outputs. Ürün Stok lets stock managers view bonded test results directly from each item — the test icon on each row opens a modal showing test interval, name, standard number, and pass/fail status fetched from the Lab system.
- Source QR Traceability — each item stores
source_qr_codes, a JSON array of input QR codes consumed to produce it. This chain (A1 copper → X1 kabatel → Y1 kalaylama → ...) is the factory's traceability backbone.
6.3.2 — CRUD Flow
Create: This page does not create items. Half-product stock records are exclusively created by the Production module during active production sessions. When an operator records a basket output, the backend writes to both production_outputs and half_product_stock tables simultaneously. There is no "Add" button on this page.
Read: On mount, the component fires 7 parallel API requests — one for each production step (Kabatel, Kalaylama, İncetel, Buncher, Extruder, E-Beam, Aktarma). Each request hits GET /api/stock/half-products/by-step/{step}?status_filter=available. The backend queries the half_product_stock table filtered by production_step and status, joins with production_sessions to get operator name, and returns sorted results (newest first). The frontend then groups by product_code, calculates per-group totals, and builds the Collapse panel hierarchy.
Update: Per-item edit via modal. Clicking the edit icon opens a form with three fields: remaining_weight_kg (required, InputNumber with min=0 and step=0.1), allocation (Select: ORDER or STOCK), and notes (TextArea). Submits PUT /api/stock/half-products/{id}. The backend whitelists only remaining_weight_kg, status, and notes for update (note: allocation is sent from frontend but the backend's allowed_fields list does not include it — potential inconsistency). After success, full data refresh.
Delete: Confirmation modal with item QR code displayed. Calls DELETE /api/stock/half-products/{id}. The backend performs a cascading delete: (1) finds the linked ProductionOutput via production_output_id or fallback output_code matching and deletes it, (2) checks if the deleted item's QR number was the highest in its prefix sequence (e.g., X150 is the highest X-prefix) — if so, decrements HalfProductSequence.last_number to allow reuse, (3) deletes the stock record itself. This cascade ensures that deleting from stock doesn't leave orphaned production output records or broken QR sequences.
6.3.3 — Data Source and API Architecture
Unlike Hammadde Stok which borrows a single endpoint from the Hammadde module, Ürün Stok has its own dedicated backend at api/stock/half_product_routes.py with 6 endpoints:
| Endpoint | Method | Purpose | Used by frontend? |
|---|---|---|---|
/api/stock/half-products/by-step/{step} | GET | Primary data: all items for a step, with operator name. Joins ProductionSession. | Yes (7 parallel calls on mount) |
/api/stock/half-products/{id} | PUT | Update weight, status, notes | Yes (edit modal) |
/api/stock/half-products/{id} | DELETE | Cascade delete: stock + production output + sequence reset | Yes (delete confirmation) |
/api/stock/half-products | GET | General listing with pagination, optional step/status filter | No (available for other consumers) |
/api/stock/half-products/summary | GET | Per-step counts and weights (available/total) | No |
/api/stock/half-products/{qr_code} | GET | Lookup by QR code | No |
Additionally, the bonded tests feature calls GET /api/production/test-requests/bonded-tests/output/{output_id} from the Production module's test_integration_routes.py. This cross-module call fetches Lab tests bonded to a specific production output, grouped by test interval (üretim_başı, her_sepet_sonu, üretim_sonu).
6.3.4 — Database Schema: half_product_stock Table
This is the dedicated table for this submodule — unlike Hammadde Stok which reads from the Hammadde module's materials table, Ürün Stok has its own table managed by models/half_product_stock.py.
| Column | Type | Business Meaning |
|---|---|---|
qr_code | String(50), unique | Deterministic QR: X=Kabatel, Y=Kalaylama, Z=İncetel, T=Buncher, U=Extruder, G=E-Beam. Globally sequential per prefix. |
product_code | String(100) | Technical product identifier (e.g., KC_18, KL_18). Grouping key on frontend. |
production_step | String(50) | Which machine step produced this: kabatel_cekme, kalaylama, incetel, buncher, extruder, ebeam, aktarma. |
initial_weight_kg | Float | Weight when the basket came off the machine. Never changes. |
remaining_weight_kg | Float | Current weight after downstream consumption. Decreases as the next production step consumes this basket. |
basket_number | Integer | Sequential basket number within the production session. |
allocation | String(20) | ORDER = produced to fulfill a customer order. STOCK = excess production, free inventory. |
order_id | FK → orders.id | Which customer order this basket is allocated to (null if STOCK). |
status | String(50) | available → in_use → consumed. Also reserved, shipped, defective. |
production_output_id | FK → production_outputs.id | Links to the production output record. Used for bonded test lookup and cascade delete. |
session_id | FK → production_sessions.id | Which production session created this basket. Used to fetch operator name. |
work_card_id | FK → work_cards.id | Which work card (from the Order module) drove this production. |
source_qr_codes | Text (JSON) | JSON array of input QR codes consumed to produce this. E.g., ["A1","A3"] for a Kabatel basket made from copper lots A1 and A3. This is the traceability chain. |
produced_at | DateTime | When this basket was produced. Stored UTC, displayed as Europe/Istanbul. |
consumed_at | DateTime, nullable | When this basket was fully consumed by the next production step. |
Key relationships in the ORM: order (Order), production_output (ProductionOutput), session (ProductionSession), work_card (WorkCardDB). Composite indexes on (production_step, status) and (product_code, status) optimize the primary query pattern.
The companion model HalfProductSequence (table half_product_sequences) tracks the last-used number for each prefix: {'X': 150} means X150 was the last Kabatel output. The prefix-to-machine mapping: kabatel_cekme → X, kalaylama → Y, incetel → Z, buncher → T, extruder → U, ebeam → G.
6.3.5 — State Management Architecture
The component uses 8 state variables:
loading(boolean) — global loading flag during the parallel fetch of all 7 stepssectionData(Record<string, SectionData>) — the main data store. Initialized with all 7 production step keys, each containing:groups(ProductGroupSummary[]),loading,expandedKeys,totalItems, andgrandTotals(total/siparis/serbest). This is the heart of the page.activePanels(string[]) — which Collapse panels are currently openeditModalVisible+editingItem— edit modal statetestsModalVisible+testsLoading+selectedTests+selectedOutputCode— bonded tests modal state
Data flow: fetchAllData() fires 7 parallel request() calls via Promise.all(). Each response is grouped by product_code on the client, with per-group allocation split (ORDER kg vs STOCK kg) and per-step grand totals calculated. The grouped results replace the entire sectionData state, preserving expandedKeys from the previous state so row expansions survive a refresh.
6.3.6 — The 7 Production Step Panels
The page uses an Ant Design Collapse component with 7 panels, one for each production step. Unlike Hammadde Stok's 8 material categories (static names like "Bakır", "PVC"), these panels follow the cable manufacturing pipeline:
| Key | Label | QR Prefix | What it produces |
|---|---|---|---|
kabatel_cekme | Kabatel | X (X1, X2...) | Raw copper drawn into 1.8mm wire |
kalaylama | Kalaylama | Y | Tinned wire |
incetel | İncetel | Z | Fine wire (thinner gauge) |
buncher | Buncher | T | Bundled wire strands |
extruder | Extruder | U | Insulated cable |
ebeam | E-Beam | G | Electron-beam cross-linked cable |
aktarma | Aktarma | AK | Material transfer / rewinding |
Each panel has a distinctive icon and color. The icon set uses Ant Design icons: ScissorOutlined (Kabatel), FireOutlined (Kalaylama), CompressOutlined (İncetel), ApartmentOutlined (Buncher), BuildOutlined (Extruder), ThunderboltOutlined (E-Beam), SwapOutlined (Aktarma).
6.3.7 — Panel Header: Three-Column Statistics
Each panel header displays three right-aligned stat columns showing the allocation split:
- Toplam (Total) — grand total kg of all available items in this production step
- Sipariş (Order) — total kg allocated to customer orders (green tag)
- Serbest (Free) — total kg of excess production / free stock (orange tag)
The item count is also displayed as a tag next to the step label. When a step has no data, the stat columns remain visible but at reduced opacity, maintaining layout consistency across all panels.
6.3.8 — Two-Level Table Hierarchy
Inside each panel, data is displayed in a two-level hierarchy (compared to Hammadde Stok's three levels):
Level 1 — Group Row (by product_code): Each row represents all baskets of a specific product code within this production step. Shows:
- Ürün Kodu (Product Code) with item count
- Toplam (Total kg) — sum of all remaining weights
- Sipariş (Order kg) — sum of ORDER-allocated items (green tag)
- Serbest (Free kg) — sum of STOCK-allocated items (orange tag)
Clicking the expand icon reveals...
Level 2 — Individual Items: Each row is a single basket/reel with its full detail. Columns:
| Column | Description |
|---|---|
| QR Kod | The deterministic QR code (e.g., X5, Y12). Blue tag. Fixed left column. |
| Ağırlık | Current remaining weight in kg (Turkish locale formatting). |
| Tahsis (Allocation) | ORDER (green) or Stok (orange) tag. |
| Durum (Status) | Mevcut (available/green), Tüketildi (consumed/default), Rezerve (reserved/blue), Hatalı (defective/red). |
| İş Kartı | Work card ID as blue tag, linking this basket to its originating order task. |
| Üretim Tarihi | Production date/time. UTC → Europe/Istanbul conversion using dayjs plugins. |
| Kaynak (Source) | Input QR codes (parsed from JSON). Shows first 2 codes + "..." if more. This is the traceability link to upstream materials. |
| Test | Experiment icon button if production_output_id exists. Opens bonded tests modal. |
| İşlem (Actions) | Edit and Delete buttons. Fixed right column. |
6.3.9 — The Allocation System: ORDER vs STOCK
This is a key concept that Ürün Stok exposes but does not control. The allocation is determined at production time, not at stock viewing time:
- When a production session runs against a work card, the backend tracks cumulative production against the order's required quantity.
- Every basket produced while cumulative output is below the required quantity is marked
ORDERand linked to theorder_id. - Once cumulative output exceeds the required quantity, subsequent baskets are marked
STOCKwith nullorder_id.
Ürün Stok displays this allocation split at every level: per-item (tag), per-group (two separate kg columns), and per-step (panel header stats). This gives stock managers immediate visibility into how much inventory is committed to orders versus how much is free for new orders or general use.
6.3.10 — Bonded Tests Modal: Lab Connection
Items that have a production_output_id (virtually all items created through normal production flow) show an experiment icon in their row. Clicking it opens a modal that fetches /api/production/test-requests/bonded-tests/output/{output_id} and displays all quality tests bonded to that specific production output.
The response groups tests by interval:
- Üretim Başı (Production Start) — tests run at the beginning of a session
- Her Sepet Sonu (Every Basket End) — per-basket tests, the most granular
- Üretim Sonu (Production End) — end-of-session validation tests
Each test row shows: test ID (purple tag), test name, standard number (if any), interval label, and status with color-coded icon (green check for passed, red X for failed, yellow clock for pending). The data comes from the Lab module's test request system — this is a cross-module read that bridges Stock and Quality Control.
6.3.11 — Source QR Traceability
The source_qr_codes field stores a JSON array of QR codes that were consumed to produce this item. The frontend parses this JSON and displays the first 2 codes with an ellipsis if there are more. For example:
- A Kabatel basket X5 might show source
["A1", "A3"]— the raw copper lots it consumed - A Kalaylama reel Y12 might show source
["X5", "X6"]— the Kabatel outputs it consumed
This creates a full material chain: raw copper (A-prefix) → kabatel wire (X-prefix) → tinned wire (Y-prefix) → fine wire (Z-prefix) → bundled wire (T-prefix) → insulated cable (U-prefix) → cross-linked cable (G-prefix). Each link is stored in source_qr_codes, making any basket traceable back to its raw material origin.
6.3.12 — Client-Side Grouping Logic
After the 7 parallel API responses arrive, fetchAllData() processes each step's data:
- Iterate over items, group into a
Record<string, HalfProductStock[]>map byproduct_code(or "Unknown" fallback) - For each group, calculate
total_kg,siparis_kg(ORDER allocation), andserbest_kg(STOCK allocation) by summingremaining_weight_kg - Accumulate grand totals per step
- Sort groups alphabetically by product code
- Build the
SectionDataobject, preserving existingexpandedKeysso that expanded rows survive a data refresh
Compared to Hammadde Stok's processAndGroupMaterials() function which groups by category then by material name (3 levels), Ürün Stok groups only by product_code within each step panel (2 levels), because the step panel itself acts as the top-level grouping.
6.3.13 — The Edit Modal
The edit modal allows modification of three fields: remaining_weight_kg (required numeric input with 0.1 step), allocation (ORDER/STOCK dropdown), and notes (free-text area). Compared to Hammadde Stok's 2-field modal (weight + status), this adds the allocation field. On submit, form values are sent to PUT /api/stock/half-products/{id}, then all data is refreshed.
Note: The backend's allowed_fields whitelist for the PUT endpoint is ['remaining_weight_kg', 'status', 'notes'] — it does not include allocation. The frontend sends allocation in the payload, but the backend silently ignores it. This means allocation is effectively read-only from this page, only changeable at production time.
6.3.14 — Loading and Empty States
During the parallel fetch, each panel's content area shows a centered Spin spinner with "Yükleniyor..." text. After loading completes, panels with no items for that production step display Ant Design's Empty component with the message "Bu kategoride stok bulunamadı".
6.3.15 — Timezone Handling
Identical pattern to Hammadde Stok: dayjs with utc and timezone plugins. All produced_at timestamps are stored UTC in the database and converted to Europe/Istanbul timezone on the frontend using dayjs.utc(date).tz(TURKEY_TZ).format('DD.MM.YY HH:mm').
6.3.16 — Connection to Other Modules
Ürün Stok is the most interconnected of the three Stock submodules. It touches every major module:
- Production → Üretim Listesi (upstream writer): Every record in
half_product_stockwas created during a production session. Thesession_idandproduction_output_idcolumns directly link back to the Production module's data. The backend even joins withProductionSessionto return operator names. - Production → Üretim Geçmişi (parallel view): Production History shows the same production outputs but organized by session and time. Ürün Stok reorganizes the same underlying data by product code and step, providing an inventory-centric rather than session-centric view.
- Production → Downstream consumption: When the next production step consumes a basket (e.g., Kalaylama consumes X-coded Kabatel output), the basket's
remaining_weight_kgdecreases,statuschanges toin_useorconsumed, andconsumed_atis filled. These items stop appearing on this page because the default filter isstatus_filter=available. - Order Module (allocation source): The
allocation(ORDER/STOCK) andorder_idare set at production time based on cumulative output vs. order requirements. Thework_card_idcolumn traces each basket to its originating work card from the Order module. - Lab Module → Test Yönetimi (quality bridge): The bonded tests modal fetches test results from the Lab's
test_integration_routes.py. Tests are bonded to production outputs by the Lab system during or after production. Ürün Stok is the only Stock submodule that directly reads Lab data. - Hammadde Module (source material): The
source_qr_codesfield contains references to raw material QR codes (A-prefix for copper, etc.) from the Hammadde module. While Ürün Stok doesn't query thematerialstable, the traceability chain connects the two modules at the data level.
6.3.17 — What This Submodule Does NOT Do
- It does not create inventory — that happens exclusively in the Production module during active sessions.
- It does not show consumed or shipped items — the default filter
status_filter=availablehides completed lifecycle items. - It does not have a search bar — navigation is done by expanding step panels and group rows.
- It does not support bulk operations — no multi-select, no batch allocation change, no batch delete.
- It does not actually modify allocation — while the edit modal presents an allocation dropdown, the backend silently ignores it (not in
allowed_fields). - It does not use WebSocket or real-time updates — data is fetched on mount and manually via the Refresh button.
- It does not display the
initial_weight_kg— onlyremaining_weight_kgis shown, so there is no consumption percentage visualization (unlike Hammadde Stok's color-coded weight column). - It does not have conditional weight coloring — unlike Hammadde Stok which colors weights yellow/red based on consumption ratio, this page shows all weights in plain text.
- It does not include the
aktarmastep in the backend'sProductionStepenum orMACHINE_TO_PREFIXmapping — the frontend defines 7 steps but the backend enum only has 6, suggesting Aktarma is a newer addition.
Ürün Stok vs Hammadde Stok — same shell, different engine: The visual layout is nearly identical (Collapse panels, grouped tables, edit modal, refresh button), but the underlying data model is fundamentally different. Hammadde Stok reads from the Hammadde module's materials table with a single API call and groups by material category/name. Ürün Stok reads from its own half_product_stock table with 7 parallel API calls, groups by product_code, and adds the ORDER/STOCK allocation split, Lab test integration, source QR traceability, and production session linkage. Hammadde Stok is a read-only window into raw inputs. Ürün Stok is a read-and-manage window into production outputs with deep cross-module connections to Production, Lab, and Order modules.
7. CONCLUSION
Stock & Inventory is not a module that creates data — it is the module that makes data from every other module visible, comparable, and actionable. Raw materials entered through Hammadde, production outputs recorded through Üretim, purchase orders tracked through Hammadde Sipariş, work card requirements from the Order module — all of this converges here into three complementary views that answer the factory’s most fundamental material questions.
7.1 What This Document Covered
This deep dive analyzed the Stock & Inventory module across three submodules, each with a fundamentally different data strategy:
| Section | Submodule | Depth | Key Insight |
|---|---|---|---|
| 6.1 | Projeksiyon | Exhaustive | Computed-on-the-fly weekly forecast with no database table of its own; merges work card requirements (MINUS) with material order deliveries (PLUS) across 8 categories; hidden shortage detection within aggregate surpluses; material-specific heatmap normalization |
| 6.2 | Hammadde Stok | Full | Pure frontend view over Hammadde module’s materials table; single API call with client-side grouping into 8 categories; conditional weight coloring for consumption tracking; minimal edit modal for stock-level corrections only |
| 6.3 | Ürün Stok | Full | Own dedicated backend with 6 endpoints and own half_product_stock table; 7 parallel API calls by production step; ORDER/STOCK allocation split; bonded Lab test integration; source QR traceability chain; cascade delete with sequence reset |
7.2 Architectural Principles
Three design principles define this module and distinguish it from the rest of the ERP system:
Visibility, Not Ownership
Stock & Inventory owns almost no data. Hammadde Stok reads from the Hammadde module’s materials table. Projeksiyon computes from work_cards and material_orders owned by Order and Hammadde respectively. Only Ürün Stok has its own table, and even that is populated exclusively by the Production module. The Stock module’s job is to present data that other modules create.
Three Lenses, One Reality
The same physical factory inventory is visible through three different lenses: what raw materials exist now (Hammadde Stok), what production outputs exist now (Ürün Stok), and what materials will exist in future weeks (Projeksiyon). No single view tells the complete story. Together they form a comprehensive material visibility layer that spans from the current warehouse snapshot to the multi-week forecast horizon.
Computation Over Storage
Projeksiyon is the defining example: no dedicated database table, no caching, no materialized views. Every request recomputes the full projection from live data. This means projections are always consistent with the latest work cards and delivery records, at the cost of heavier computation per request. The same principle applies to Hammadde Stok’s client-side grouping and Ürün Stok’s parallel aggregation — totals are always computed fresh, never stored.
Cross-Module Read Pattern
Every submodule reads from at least one external module. Hammadde Stok reads Hammadde. Projeksiyon reads Hammadde Sipariş + Order (work cards). Ürün Stok reads Production (sessions, outputs) and Lab (bonded tests). This makes Stock the most dependency-heavy module in the system — it cannot function if any upstream module is broken.
7.3 The Numbers
* Projeksiyon has zero persistent storage. Hammadde Stok has zero own endpoints. The only write-capable endpoints belong to Ürün Stok (PUT + DELETE), and even those modify a table populated entirely by another module.
7.4 How Stock Reads the Factory
At first glance, Stock appears to be a passive, read-only module — it reads from everywhere and writes almost nowhere. But this is misleading. Ürün Stok plays a critical upstream role in the production planning loop: the Order module’s work card calculator engine queries available product stock (items with STOCK allocation and available status) and deducts free inventory before generating new work cards. This is precisely why the allocation (ORDER vs STOCK) and status (available, in_use, consumed, reserved) fields exist — not just for display, but as input data that determines how much new production is actually needed.
This creates a circular dependency, not a linear pipeline:
The entire ERP system operates as this loop. There is no true “starting module” — every module depends on data from another, which in turn depends on a third, which circles back. Stock sits at a critical junction in this loop: it receives data from Production and Hammadde, but it also feeds back into Order by providing the available inventory that shapes how many work cards get generated. If Ürün Stok shows 50 kg of free Kabatel wire, the calculator subtracts that before distributing production tasks. Without accurate stock data, the factory either overproduces (wasting resources) or underproduces (missing delivery dates).
This also means Stock is the first module to break when upstream data is inconsistent. If a material order’s delivered_date is wrong, Projeksiyon’s forecast shifts. If Production doesn’t update remaining_weight_kg, Hammadde Stok shows stale weights. If a production output is deleted outside of Ürün Stok, orphaned stock records appear. Stock doesn’t just display data — it exposes the integrity of the entire system.
7.5 Final Word
The Stock & Inventory module is the factory’s material nervous system. It does not move materials, produce cables, or fulfill orders — but it tells everyone in the organization what exists, what is committed, what is free, and what will be available next week. More critically, it feeds this information back into the production planning loop: the Order module’s calculator engine reads Ürün Stok to deduct available half-products before generating work cards, closing the circle that connects ordering, production, and inventory. Projeksiyon alerts purchasing before a shortage becomes critical. Hammadde Stok tells the warehouse what’s physically on the shelves. Ürün Stok tells production planners what semi-finished goods are ready for the next step — and tells the Order module what doesn’t need to be produced at all. Three pages, two database tables, five upstream dependencies, and one critical feedback loop — without this module, the factory either operates blind or runs in circles producing what it already has.