Back to Modules

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.

February 2026 • Solen Kablo • Living Document

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.

3
SUBMODULES
~14
API ENDPOINTS
2
DATABASE TABLES
5800+
LINES OF CODE

TABLE OF CONTENTS

1. What Stock & Inventory Does 2. The Data Flow 3. The Database Layer 4. The Backend Architecture 5. The Frontend 6. The Submodules 6.1 Projeksiyon (Stock Projection) 6.2 Hammadde Stok (Raw Material Stock) 6.3 Ürün Stok (Product Stock) 7. Conclusion

1. WHAT STOCK & INVENTORY DOES

The Stock & Inventory module answers the two most fundamental questions in any manufacturing operation:

  1. “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.
  2. “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:

AspectLive Inventory (Ürün + Hammadde Stok)Projection (Projeksiyon)
Data sourcehalf_product_stock table + materials tablework_cards (MINUS) + material_orders (PLUS)
Time axisNow (t=0)Weeks into the future (H01–H52+)
Changes whenProduction records output, materials consumed/receivedPlan changes: work card created/modified/cancelled, material order delivered (switches to actual date & quantity), order date/qty changed, order cancelled
Does NOT change whenProduction physically completes (completed work cards still counted in plan)
CRUDFull (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):

Work Cards (sevkiyat_week) material_details JSON MINUS per week per material
Material Orders (expected_date) quantity + material_type PLUS per week per material
PLUS − MINUS Weekly change Running cumulative Shortage detection

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 TypeMaterial ExtractedJSON Path
Kabatel ÇekmeCopper (kg)material_details.copper_kg
KalaylamaTin (kg)material_details.tin_kg
ExtruderPlastic, Catalyst, Dye, Antirodent (kg)quantities.*, insulation_materials[], sheath_materials[]
AktarmaReels (adet)makara_distributions[].count
PaletlemePalettes (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:

Production records output half_product_stock row created Ürün Stok shows it
Material entry form materials row created Hammadde Stok shows it

Ü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).

ColumnTypeDetails
idInteger PKAuto-increment
qr_codeString(50)Unique, indexed. Deterministic: X1, X2, Y1, Y2, etc.
product_codeString(100)Indexed. e.g., KC_18 (Kabatel 1.8mm), KL_18 (Kalaylama 1.8mm)
product_nameString(200)Human-readable name
production_stepString(50)Indexed. Enum: kabatel_cekme, kalaylama, incetel, buncher, extruder, ebeam
initial_weight_kgFloatWeight when first produced
remaining_weight_kgFloatCurrent weight (decreases as material is consumed in next step)
basket_numberIntegerSequential within session
allocationString(20)ORDER (tied to a customer order) or STOCK (free inventory)
order_idInteger FKReferences orders.id if allocation = ORDER
statusString(50)Indexed. Enum: available, in_use, consumed, reserved, shipped
production_output_idInteger FKReferences production_outputs.id, indexed
session_idInteger FKReferences production_sessions.id
work_card_idInteger FKReferences work_cards.id
source_qr_codesTextJSON array of input QR codes for full traceability (e.g., a Kalaylama Y1 consumed Kabatel X3 and X4)
produced_atDateTimeWhen produced
consumed_atDateTimeWhen fully consumed (nullable)
created_atDateTimeUTC
updated_atDateTimeAuto-updated
notesTextOptional 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

TableModuleWhat Projeksiyon Reads
work_cardsProductionsevkiyat_week (delivery week), machine_type, material_details JSON, status (excludes CANCELLED)
material_ordersHammaddematerial_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

FileLinesPrefixEndpointsPurpose
projeksiyon_routes.py245/api/projeksiyon4Stock projection: main data, summary, debug, per-material detail
half_product_routes.py327/api/stock6Half-product CRUD: list, by-step, summary, by-QR, update, delete
material_routes.py(Hammadde)/api/materials~4Raw 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:

MethodWhat 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)

MethodPathParametersWhat It Does
GET/api/projeksiyonweeks (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/summaryweeks (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), weeksPer-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/debugDebug 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)

MethodPathWhat It Does
GET/api/stock/half-productsList 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/summaryAggregated 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

RouteComponentLinesPurpose
/stok/projeksiyonStok/Projeksiyon/index.tsx3,323One 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-stokStok/UrunStok/index.tsx866Semi-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-stokStok/HammaddeStok/index.tsx676Raw 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

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:

  1. 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).
  2. 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.
  3. 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

PageRoute GuardManifest Buttons
ProjeksiyoncanStokaccess_page, view_projections, create_projection, edit_projection
Ürün StokcanStokaccess_page, view_table, view_details, edit_stock, adjust_quantity
Hammadde StokcanStokaccess_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:

6.1

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.

3,323 LOC FRONTEND 804 LOC SERVICE 4 ENDPOINTS 3 CHART TYPES
6.2

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.

676 LOC FRONTEND 8 MATERIAL CATEGORIES SHARED API
6.3

Ü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.

866 LOC FRONTEND 6 ENDPOINTS 7 PRODUCTION STEPS LAB INTEGRATION

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_cekmecopper_kg (Filmaşin)
  • kalaylamatin_kg (Kalay)
  • extruder → plastic, catalyst, dye, antirodent (from insulation_materials[] and sheath_materials[])
  • aktarma → reels (from makara_distributions[]) — note: kangal (coilless) uses plastic packaging and is NOT tracked
  • paletleme → palettes (from palettes[])

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:

  1. 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).
  2. 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).
  3. 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.
  4. Walk through every week in order: For each week, compute weekly = plus - minus per material category, and add it to the running cumulative total. Also track sub-material cumulatives (e.g., “Filmaşin 8mm” within Copper).
  5. 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.
  6. 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

MethodPathParametersPurpose
GET/api/projeksiyonweeks (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/summaryweeks (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), weeksPer-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/debugDebug 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 VariableTypePurpose
projeksiyonDataProjeksiyonResponse | nullThe entire API response, shared between both tabs
weekOffsetnumberCurrent week offset from H01. Changed by navigation arrows. Triggers API refetch.
expandedMaterialsstring[]Which material columns are currently expanded to show sub-materials
knownSubMaterialsRecord<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)
selectedGraphMaterialsstring[]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
radarPagenumberCurrent 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:

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:

  1. The backend tracks sub_material_cumulatives as a dictionary keyed by category_materialName (e.g., plastic_HFX 500P). This is updated for EVERY week (not just the view window).
  2. Each week in the response carries its own sub_material_cumulatives snapshot.
  3. 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.
  4. 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:

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:

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:

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:

  1. Insulation materials (insulation_materials[]): Each entry has material_name (plastic type), plastic_kg, catalyst_name, catalyst_kg, and dyes[] array with dye_name and dye_kg.
  2. Sheath materials (sheath_materials[]): Same structure as insulation but also includes antirodent_name and antirodent_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:

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:

ActionEffect on Projection
New work card createdMINUS increases for the card’s sevkiyat_week
Work card’s sevkiyat_week changedMINUS moves from old week to new week
Work card cancelledMINUS disappears (CANCELLED excluded from query)
New material order createdPLUS appears in the expected_date week
Material order’s expected date changedPLUS moves from old week to new week
Material order quantity changedPLUS amount changes for that week
Material order deliveredPLUS switches from expected_date/quantity to delivered_date/delivered_quantity — week and amount may both shift
Material order cancelledPLUS disappears entirely
Production physically completesNo 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

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:

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):

  1. Page loads → useEffect([], []) triggers fetchMaterials() on mount
  2. Single GET /api/materials/list call fetches ALL raw materials as a flat array, regardless of type
  3. Response passes through processAndGroupMaterials() which: filters by material_type into 8 buckets, groups within each bucket by material_name, calculates aggregate totals (total, reserved, available), and stores everything in the sectionData state object
  4. User clicks a Collapse panel to see the grouped table for that category
  5. User clicks the expand arrow on a group row to see individual lots with QR codes, weights, supplier, dates, and status
  6. User can click the “Yenile” (Refresh) button in the page header to re-fetch all data at any time

Update (Edit Modal):

  1. Click the EditOutlined icon on any individual item row (Level 3)
  2. Modal opens with title “Düzenle: {QR_CODE}” (e.g., “Düzenle: A7”)
  3. Form pre-fills from the selected item’s current values via form.setFieldsValue()
  4. Two fields available (see 6.2.10 for full details):
    • remaining_weightInputNumber, min=0, step=0.1. Hidden for palette/reel types
    • statusSelect dropdown with 5 options
  5. On submit: PUT /api/materials/update/{id} with the form values
  6. On success: message.success('Güncellendi') → modal closes → form resets → fetchMaterials() refetches all data
  7. On failure: message.error('Güncellenemedi')

Delete (Hard Delete with Confirmation):

  1. Click the DeleteOutlined icon (styled with color: '#ff4d4f') on any individual item row
  2. 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
  3. On confirm: DELETE /api/materials/{id}/hard-delete — this is a permanent, irreversible deletion. The material record and all associated material_photos records are removed from the database
  4. On success: message.success('Silindi')fetchMaterials() refetches
  5. 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/listALL 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-deletePermanently 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:

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_copperMenuOutlined#cd7f32 (bronze)kgA
Kalay (Tin)raw_tinGoldOutlined#c0c0c0 (silver)kgB
Plastik (Plastic)raw_plasticDockerOutlined#3498db (blue)kgC
Katalizör (Catalyst)raw_catalystExperimentOutlined#9b59b6 (purple)kgD
Boya (Dye)raw_dyeBgColorsOutlined#e74c3c (red)kgE
Antirodentraw_antirodentBugOutlined#27ae60 (green)kgF
Palet (Palette)raw_paletteBorderInnerOutlined#f39c12 (orange)adetI
Makara (Reel)raw_reelPicCenterOutlined#1abc9c (teal)adetH

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:

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 KodBlue tag showing the deterministic QR code (A1, B3, C12, etc.). Pinned left.
Ölçülen / MiktarOriginal 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çiSupplier name from Tedarikçi Yönetimi, or “-” if none
Lot NoLot/batch number from Hammadde Girişi, or “-” if none
Giriş TarihiEntry 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:

The rendering rules:

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:

  1. Panel header statistics: Show “kg” or “adet” after aggregate numbers
  2. Group table columns: Unit suffix on Toplam, Kullanımda, Serbest values
  3. Detail table column title: “Ölçülen” (Measured) for kg types vs “Miktar” (Quantity) for adet types
  4. Detail table “Kalan” column: Completely hidden for palette/reel
  5. 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:

  1. Category filter: For each of the 8 categories, filter materials.filter(m => m.material_type === cat.key)
  2. Name grouping: Within each category, build a Record<string, RawMaterial[]> keyed by material_name. If material_name is null/empty, the key becomes “{label} (İsimsiz)” (e.g., “Bakır (İsimsiz)”)
  3. Group aggregation: For each group, create a MaterialGroupSummary object:
    • total_quantity: Sum of remaining_weight ?? weight ?? 0 for kg types, or weight ?? 0 for adet types, across ALL items regardless of status
    • reserved_quantity: Sum from items where status === 'in_use'
    • available_quantity: Sum from items where status === 'received' || status === 'available'
    • Items with other statuses (consumed, rejected) are counted in total_quantity but not in reserved or available — they effectively represent consumed or discarded stock
  4. State preservation: Carry forward the previous expandedKeys from the existing sectionData, 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:

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:

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:

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?

Downstream: Who drains the inventory?

Sibling views over the same data:

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:

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

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:

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}GETPrimary data: all items for a step, with operator name. Joins ProductionSession.Yes (7 parallel calls on mount)
/api/stock/half-products/{id}PUTUpdate weight, status, notesYes (edit modal)
/api/stock/half-products/{id}DELETECascade delete: stock + production output + sequence resetYes (delete confirmation)
/api/stock/half-productsGETGeneral listing with pagination, optional step/status filterNo (available for other consumers)
/api/stock/half-products/summaryGETPer-step counts and weights (available/total)No
/api/stock/half-products/{qr_code}GETLookup by QR codeNo

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_codeString(50), uniqueDeterministic QR: X=Kabatel, Y=Kalaylama, Z=İncetel, T=Buncher, U=Extruder, G=E-Beam. Globally sequential per prefix.
product_codeString(100)Technical product identifier (e.g., KC_18, KL_18). Grouping key on frontend.
production_stepString(50)Which machine step produced this: kabatel_cekme, kalaylama, incetel, buncher, extruder, ebeam, aktarma.
initial_weight_kgFloatWeight when the basket came off the machine. Never changes.
remaining_weight_kgFloatCurrent weight after downstream consumption. Decreases as the next production step consumes this basket.
basket_numberIntegerSequential basket number within the production session.
allocationString(20)ORDER = produced to fulfill a customer order. STOCK = excess production, free inventory.
order_idFK → orders.idWhich customer order this basket is allocated to (null if STOCK).
statusString(50)availablein_useconsumed. Also reserved, shipped, defective.
production_output_idFK → production_outputs.idLinks to the production output record. Used for bonded test lookup and cascade delete.
session_idFK → production_sessions.idWhich production session created this basket. Used to fetch operator name.
work_card_idFK → work_cards.idWhich work card (from the Order module) drove this production.
source_qr_codesText (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_atDateTimeWhen this basket was produced. Stored UTC, displayed as Europe/Istanbul.
consumed_atDateTime, nullableWhen 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:

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_cekmeKabatelX (X1, X2...)Raw copper drawn into 1.8mm wire
kalaylamaKalaylamaYTinned wire
incetelİncetelZFine wire (thinner gauge)
buncherBuncherTBundled wire strands
extruderExtruderUInsulated cable
ebeamE-BeamGElectron-beam cross-linked cable
aktarmaAktarmaAKMaterial 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:

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:

Clicking the expand icon reveals...

Level 2 — Individual Items: Each row is a single basket/reel with its full detail. Columns:

Column Description
QR KodThe deterministic QR code (e.g., X5, Y12). Blue tag. Fixed left column.
AğırlıkCurrent 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 TarihiProduction 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.
TestExperiment 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:

Ü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:

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:

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:

  1. Iterate over items, group into a Record<string, HalfProductStock[]> map by product_code (or "Unknown" fallback)
  2. For each group, calculate total_kg, siparis_kg (ORDER allocation), and serbest_kg (STOCK allocation) by summing remaining_weight_kg
  3. Accumulate grand totals per step
  4. Sort groups alphabetically by product code
  5. Build the SectionData object, preserving existing expandedKeys so 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:

6.3.17 — What This Submodule Does NOT Do

Ü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:

SectionSubmoduleDepthKey Insight
6.1ProjeksiyonExhaustiveComputed-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.2Hammadde StokFullPure 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 StokFullOwn 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

~1,050
LINES BACKEND
~4,800
LINES FRONTEND
5
UPSTREAM MODULES
0
OWN WRITE ENDPOINTS

* 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

Hammadde Module Hammadde Stok
Üretim (Production) Ürün Stok Lab (bonded tests)
Hammadde Sipariş (PLUS) Projeksiyon Work Cards (MINUS)

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:

Order Work Cards Production Ürün Stok Order (deduct available) → ...

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.