MRP (Material Requirements Planning) automated data generator that creates end-to-end demand-to-production-order documents via JCo RFC: planned independent requirements (PIR), MRP run, stock/requirements review, and planned-order-to-production-order conversion. Generates realistic SAP MRP transaction data on S/4HANA IDES using the bike finished goods family (MZ-FG-*).
Everything runs from the local Mac — no need to SSH into sapidess4 or sapidesecc8.
Client 100 is used for all operations.
The generator connects directly to SAP over the network using the JCo 3 library. No SSH tunnel needed.
| Parameter | Value |
|---|---|
| Host | sapidess4.fivetran-internal-sales.com |
| System Number | 03 (port 3303) |
| Client | 100 |
| User | IDES |
| Password | (see vault) |
| Language | EN |
../SAPjco/sapjco3.jar | Java JCo library |
../SAPjco/libsapjco3.dylib | macOS ARM64 native library |
# Run the generator locally java -Xmx512m -cp .:../SAPjco/sapjco3.jar \ -Djava.library.path=../SAPjco SAPMRPWorkflow full 1
Connect to HANA via SSH tunnel for direct SQL queries. Only the tunnel uses SSH — you never log into the server.
| Parameter | Value |
|---|---|
| Tunnel | localhost:30015 → sapidess4:30015 |
| Database | FIV (S/4HANA tenant) |
| User | SAPHANADB |
| Password | (see vault) |
| Python lib | pip3 install hdbcli |
# Step 1: Start SSH tunnel (runs in background, one-time)
ssh -f -N -L 30015:127.0.0.1:30015 root@sapidess4
# Step 2: Query HANA via Python
python3 -c "
from hdbcli import dbapi
conn = dbapi.connect(address='localhost', port=30015,
user='SAPHANADB', password='(see vault)', databaseName='FIV')
cur = conn.cursor()
cur.execute(\"SELECT PLNUM, MATNR, GSMNG FROM PLAF WHERE MATNR LIKE 'MZ%'\")
for row in cur.fetchall(): print(row)
"
-- PIR demand
SELECT * FROM PBIM WHERE MATNR LIKE 'MZ-FG%' AND WERKS = '1710';
-- Planned orders
SELECT PLNUM, MATNR, GSMNG, PSTTR, PEDTR
FROM PLAF WHERE MATNR LIKE 'MZ%' AND PLWRK = '1710';
-- Production orders from conversion
SELECT AUFNR, OBJNR FROM AUFK
WHERE AUFNR IN ('000001002166', '000001002167');
All 9 finished goods are FERT materials with BOM + Routing in plant 1710,
MRP type PD (deterministic planning), lot size EX (lot-for-lot),
BESKZ='E' (in-house production).
| Material | Description | MRP Type | Lot Size | Components |
|---|---|---|---|---|
MZ-FG-C900 | C900 BIKE | PD | EX | 7 ROH |
MZ-FG-C950 | C950 BIKE | PD | EX | 7 ROH |
MZ-FG-C990 | C990 Bike | PD | EX | 7 ROH |
MZ-FG-M500 | M500 BIKE | PD | EX | 7 ROH |
MZ-FG-M525 | M525 BIKE | PD | EX | 7 ROH |
MZ-FG-M550 | M550 BIKE | PD | EX | 7 ROH |
MZ-FG-R100 | R100 BIKE | PD | EX | 7 ROH |
MZ-FG-R200 | R200 Bike | PD | EX | 7 ROH |
MZ-FG-R300 | R300 Bike | PD | EX | 7 ROH |
Each bike has 7–10 ROH components following the naming pattern MZ-RM-{model}-01 through
MZ-RM-{model}-07. These are the same components used by the PP generator and appear as
dependent requirements when MRP explodes the BOM.
| # | Part | Example (R100) | UoM |
|---|---|---|---|
| 01 | Frame | MZ-RM-R100-01 | ST |
| 02 | Handle Bars | MZ-RM-R100-02 | ST |
| 03 | Seat | MZ-RM-R100-03 | ST |
| 04 | Wheels | MZ-RM-R100-04 | ST |
| 05 | Forks | MZ-RM-R100-05 | ST |
| 06 | Brakes | MZ-RM-R100-06 | ST |
| 07 | Drive Train | MZ-RM-R100-07 | ST |
Creates Planned Independent Requirements (PIR) for bike finished goods. PIR represents
forecast demand that MRP will convert into planned orders. Uses requirement type
VSF (standard forecast) with auto-incrementing version 00–09.
| Tcode | Purpose |
|---|---|
MD61 | Create Planned Independent Requirements |
MD62 | Change Planned Independent Requirements |
MD63 | Display Planned Independent Requirements |
| Function Module | Purpose |
|---|---|
BAPI_REQUIREMENTS_CREATE | Create PIR with material, plant, version, qty, date |
BAPI_TRANSACTION_COMMIT | Persist changes (WAIT='X') |
| Field | Value | Notes |
|---|---|---|
REQU_TYPE | VSF | Standard forecast requirement |
VERSION | 00–09 | Auto-increments if version already exists |
MATERIAL | e.g. MZ-FG-M500 | Random bike from pool of 9 |
PLANT | 1710 | |
REQ_QTY | Quantity (e.g. 2000) | Configurable via --qty |
REQ_DATE | Today + lead days | Configurable via --lead-days |
REQMTSPLANNUMBER via RFC,
but the demand IS created in SAP. This is normal SAP behavior — the PIR number
is assigned internally and can be verified in PBIM/PBHI.
| Table | Description | Key Fields Written |
|---|---|---|
PBIM | PIR Header (Independent Requirements) | BESSION (version), BEDID (req type), MATNR, WERKS |
PBHI | PIR Schedule Lines (Period Quantities) | BESSION, PDATU (req date), PLNMG (planned qty) |
-- Check PIR for bike materials
SELECT h.MATNR, h.WERKS, h.BESSION, i.PDATU, i.PLNMG
FROM PBIM h
JOIN PBHI i ON h.BESSION = i.BESSION AND h.BEDID = i.BEDID
WHERE h.MATNR LIKE 'MZ-FG%' AND h.WERKS = '1710'
ORDER BY h.MATNR, i.PDATU;
Executes single-item MRP for a material. MRP reads PIR demand, checks available stock,
explodes the BOM, and creates planned orders in PLAF to cover the net requirement.
Uses net change planning (PROC_TYPE='1') with BOM re-explosion (PLANNING_MODE='3').
| Tcode | Purpose |
|---|---|
MD01 | MRP Run (Total Planning) |
MD02 | MRP Run (Single-Item, Single-Level) |
MD04 | Stock/Requirements List |
MD05 | MRP List (Individual) |
| Function Module | Purpose |
|---|---|
BAPI_MATERIAL_PLANNING | Run single-item MRP |
BAPI_TRANSACTION_COMMIT | Persist changes (WAIT='X') |
| Field | Value | Notes |
|---|---|---|
MATERIAL | e.g. MZ-FG-R100 | Single material or loop all 9 |
PLANT | 1710 | |
PROC_TYPE | 1 | Net change planning |
PLANNING_MODE | 3 | Re-explode BOM and routing |
| Table | Description | Key Fields Written |
|---|---|---|
PLAF | Planned Orders | PLNUM (planned order #), MATNR, GSMNG (order qty), PEDTR (end date) |
MDKP | MRP Document Header | DTNUM (document #), MATNR, WERKS, DTART |
MDTB | MRP Document Items | DTNUM, DTPOS, MRP element details |
EBAN | Purchase Requisitions | BANFN, MATNR (for externally procured components) |
RESB | Reservations (dependent requirements) | Planned order component reservations |
| Code | Description | Source |
|---|---|---|
PA | Planned Order | MRP-generated (PLAF) |
PP | Planned Independent Requirement (PIR) | Step 0 (PBIM/PBHI) |
FE | Production Order | Converted from planned order |
VJ | Sales Order | From OTC generator |
BE | Purchase Requisition | For externally procured items |
-- Planned orders from MRP run
SELECT PLNUM, MATNR, GSMNG, PEDTR, PSTTR, PLSCN
FROM PLAF
WHERE MATNR LIKE 'MZ-FG%' AND WERKS = '1710'
ORDER BY PLNUM DESC;
Read-only step that displays the current stock situation and all MRP elements for a material. Shows planned orders, PIR demand, production orders, sales orders, and current stock levels. Useful for verifying MRP results before conversion.
| Function Module | Purpose |
|---|---|
BAPI_MATERIAL_STOCK_REQ_LIST | Read stock/requirements list (equivalent to MD04) |
| Field | Value | Notes |
|---|---|---|
MATERIAL | e.g. MZ-FG-R100 | Material to review |
PLANT | 1710 |
| Element | Description | Meaning |
|---|---|---|
PA | Planned Order | MRP-generated supply (to be converted) |
PP | PIR | Forecast demand from Step 0 |
FE | Production Order | Firm supply (converted from PA) |
VJ | Sales Order | Customer demand (from OTC) |
Stock | Current available stock | Unrestricted use inventory |
BAPI_TRANSACTION_COMMIT is called.
Reads a planned order from PLAF, creates a production order using the planned order details,
then re-runs MRP to clean up the consumed planned order. The profile PLDORD_PROFILE='LA'
is required for in-house production materials (BESKZ=E), as defined in T460C.
| Tcode | Purpose |
|---|---|
CO40 | Convert Planned Order to Production Order |
CO41 | Convert Planned Orders (Collective) |
MD04 | Verify conversion (PA → FE) |
| Function Module | Purpose | Step |
|---|---|---|
BAPI_PLANNEDORDER_GET_DETAIL | Read planned order details from PLAF | Read |
BAPI_PRODORD_CREATE | Create production order from planned order data | Create |
BAPI_TRANSACTION_COMMIT | Persist production order (WAIT='X') | Commit |
BAPI_MATERIAL_PLANNING | Re-run MRP to clean up consumed planned order | Cleanup |
BAPI_TRANSACTION_COMMIT | Persist MRP cleanup (WAIT='X') | Commit |
| Field | Value | Notes |
|---|---|---|
MATERIAL | From planned order | Material from PLAF |
PLANT | 1710 | |
ORDER_TYPE | From planned order or default | Production order type |
QUANTITY | From planned order (GSMNG) | |
BASIC_START_DATE | From planned order | |
BASIC_END_DATE | From planned order |
| Table | Description | Impact |
|---|---|---|
AUFK | Production Order Header | New order created with AUFNR |
AFKO | Production Order Master | PLNBEZ (material), GAMNG (qty) |
PLAF | Planned Orders | Deleted by MRP cleanup run |
-- Production order created from planned order
SELECT a.AUFNR, k.PLNBEZ, k.GAMNG, k.GMEIN
FROM AUFK a
JOIN AFKO k ON a.AUFNR = k.AUFNR
WHERE k.PLNBEZ LIKE 'MZ-FG%'
ORDER BY a.AUFNR DESC;
-- Verify planned order was deleted
SELECT PLNUM, MATNR, GSMNG FROM PLAF
WHERE PLNUM = '0004000XXXXX';
| Table | Description | Category | Populated By |
|---|---|---|---|
PBIM | PIR Header (Independent Requirements) | Demand | Step 0 |
PBHI | PIR Schedule Lines (Period Quantities) | Demand | Step 0 |
PLAF | Planned Orders | Supply | Step 1 (created), Step 3 (deleted) |
MDKP | MRP Document Header | MRP Run | Step 1 |
MDTB | MRP Document Items | MRP Run | Step 1 |
EBAN | Purchase Requisitions | Procurement | Step 1 |
RESB | Reservations (Dependent Requirements) | Components | Step 1 |
| Table | Description | Category | Populated By |
|---|---|---|---|
AUFK | Order Master (Header) | Order Header | Step 3 |
AFKO | Order Header Data (PP specific) | Order Master | Step 3 |
| Table | Description | Category |
|---|---|---|
MARA | Material General Data | General |
MARC | Material Plant Data (MRP, Production) | Plant |
MARD | Storage Location Stock | Stock |
MAKT | Material Descriptions | Texts |
MBEW | Material Valuation | Valuation |
| Table | Description | Purpose |
|---|---|---|
T001 | Company Codes | Company code 1710 |
T001W | Plants | Plant definitions |
T460C | Planned Order Profiles | PLDORD_PROFILE='LA' for BESKZ=E |
T438M | MRP Type Customizing | PD = deterministic planning |
| CDS View | Description | Base Tables |
|---|---|---|
I_PlannedIndepRqmt | PIR header | PBIM |
I_PlannedIndepRqmtItem | PIR schedule lines | PBHI |
I_PlndIndepRqmtAPI01 | PIR header (API) | PBIM |
I_PlndIndepRqmtItemAPI01 | PIR schedule line (API) | PBHI |
| CDS View | Description | Base Tables |
|---|---|---|
I_PlannedOrder | Planned order header | PLAF |
I_PlannedOrderAPI01 | Planned order header (API) | PLAF |
I_PlannedOrderComponent | Planned order components | RESB |
C_PlannedOrder | Consumption: Manage Planned Orders | PLAF |
| CDS View | Description | Base Tables |
|---|---|---|
I_MRPDocument | MRP document header | MDKP |
I_MRPElement | MRP element (stock/requirements list) | MDTB |
I_MRPMaterial | Material MRP data | MARC |
C_MRPStockRequirementList | Consumption: Stock/Requirements List (MD04) | MDTB |
C_MRPMaterial | Consumption: MRP material overview | MARC |
| CDS View | Description | Base Tables |
|---|---|---|
I_PurchaseRequisition | PR header composite | EBAN |
I_PurchaseRequisitionItem | PR item with all fields | EBAN |
I_PurReqnItemPurOrdRef | PR item to PO reference | EBAN |
C_PurchaseReqnHeader | Consumption: Manage PRs | EBAN |
| CDS View | Description | Base Tables |
|---|---|---|
I_ProductionOrder | Production order header | AUFK, AFKO |
I_ProductionOrderAPI01 | Production order (API) | AUFK, AFKO |
I_ProductionOrderItem | Production order item | AFPO |
I_ProductionOrderComponent | Order BOM components | RESB |
I_ProductionOrderOperation | Order routing operations | AFVV |
C_ProdOrdConfirmation | Consumption: Order confirmations | AFRU |
| CDS View | Description | Base Tables |
|---|---|---|
I_Product | Product/material general | MARA |
I_ProductPlantBasic | Product plant data (MRP params) | MARC |
I_ProductPlantMRPArea | MRP area data | MARC |
I_ProductPlantProcurement | Product procurement data | MARC |
I_ProductValuation | Product valuation | MBEW |
I_MaterialStock | Material stock overview | MARD |
I_BillOfMaterial | BOM header | STKO |
I_BillOfMaterialItem | BOM components | STPO |
I_ProductionRoutingHeader | Routing header | PLKO |
I_ProductionRoutingOperation | Routing operations | PLPO |
| Step | BAPI | Purpose | Commit? |
|---|---|---|---|
| 0 | BAPI_REQUIREMENTS_CREATE | Create PIR demand | Yes |
| 1 | BAPI_MATERIAL_PLANNING | Run single-item MRP | Yes |
| 2 | BAPI_MATERIAL_STOCK_REQ_LIST | Read stock/requirements list | No |
| 3 | BAPI_PLANNEDORDER_GET_DETAIL | Read planned order details | No |
| 3 | BAPI_PRODORD_CREATE | Create production order | Yes |
| 3 | BAPI_MATERIAL_PLANNING | MRP cleanup (delete consumed PA) | Yes |
| * | BAPI_TRANSACTION_COMMIT | Commit changes (WAIT='X') | — |
Generate N MRP cycles with configurable pipeline depth:
# PIR + MRP only (create demand and planned orders)
./mrpgen.sh 10 mrp
# Full pipeline: PIR + MRP + Convert (100 cycles)
./mrpgen.sh 100 convert
./mrpwf.sh step0-pir 5 # Create 5 PIR demands
./mrpwf.sh step1-mrp MZ-FG-R100 # Run MRP for one material
./mrpwf.sh step1-mrp # Run MRP for all 9 finished goods
./mrpwf.sh step2-review MZ-FG-R100 # Review stock/requirements (MD04)
./mrpwf.sh step3-convert 4000001463 # Convert planned order to prod order
./mrpwf.sh full 3 # 3 full cycles (PIR+MRP+Convert)
./mrpwf.sh list # List planned orders from PLAF
./mrpwf.sh status 4000001463 # Show planned order details
| Stage | Pipeline | SAP Tables Created |
|---|---|---|
mrp | PIR → MRP Run | PBIM, PBHI, PLAF, MDKP |
convert | PIR → MRP → Convert → Cleanup | + AUFK, AFKO (PLAF deleted) |
| Flag | Description |
|---|---|
--verbose | Print BAPI details and all return messages |
--dry-run | Validate only (no BAPI_TRANSACTION_COMMIT) |
--qty N | Fixed PIR quantity (default: random) |
--pause N | Seconds between steps (default: 3) |
--lead-days N | Days ahead for PIR requirement date |
--stop-at STAGE | Stop after stage: pir, mrp, review, convert |
.
├── SAPMRPWorkflow.java # Main automation program
├── mrpwf.sh # Workflow wrapper (individual steps)
├── mrpgen.sh # Bulk data generator (batch mode)
├── compile.sh # Compilation helper
├── docs/
│ └── SAP_MRP_Workflow_Documentation.html # This document
└── skill/
└── mrp-planning.md # Claude Code skill file
The MRP generator sits at the center of the SAP data ecosystem, connecting demand from sales to supply via production and procurement. All generators share the same S/4HANA IDES instance.
Sales Orders → MRP Demand
OTC creates sales orders (VA01) which appear as VJ elements in MD04.
MRP sees these as customer demand and generates planned orders to fulfill them.
PIR → Planned Orders → Prod Orders
Creates forecast demand via PIR, runs MRP to generate planned orders, converts to
production orders. The central planning hub.
Production Order Execution
PP picks up where MRP Step 3 leaves off: release, GI, confirmation, GR.
MRP creates the production order; PP executes it through the shop floor.
| Error | Cause | Fix |
|---|---|---|
| Empty REQMTSPLANNUMBER | BAPI_REQUIREMENTS_CREATE returns blank PIR number via RFC | Normal SAP behavior. Demand IS created. Verify in PBIM/PBHI. |
| "Requirement already exists" | PIR version 00 already used for material/plant/period | Auto-increments version 00 → 09. If all 10 used, pick different date or material. |
| MRP creates 0 planned orders | Existing stock covers PIR demand (net requirement = 0) | Not an error. Increase PIR qty or consume stock first. |
| BAPI_PLANNEDORDER_CREATE fails | V2 collective update does not complete in RFC session | Do NOT use this BAPI. Use PIR + MRP run instead (the approach this generator uses). |
| "Cannot be changed manually" | MRP-generated planned orders are system-managed | Use MRP re-run to delete/modify planned orders, not manual changes. |
| Conversion fails (no profile) | PLDORD_PROFILE not set for in-house production materials | Set PLDORD_PROFILE='LA' (from T460C) for BESKZ=E materials. |
| Planned order not found | PLAF record already deleted by previous MRP cleanup | Run list to see current planned orders. Each can only be converted once. |
| Setting | Table | Required Value | Purpose |
|---|---|---|---|
| MRP Type | MARC.DISMM | PD | Deterministic planning (reacts to PIR) |
| Lot Size | MARC.DISLS | EX | Lot-for-lot (exact quantity) |
| Procurement Type | MARC.BESKZ | E | In-house production |
| MRP Controller | MARC.DISPO | Set | Required for MRP to process the material |
| BOM | MAST/STKO/STPO | Exists | Required for BOM explosion in MRP |
| BAPI | Gotcha | Correct Approach |
|---|---|---|
BAPI_REQUIREMENTS_CREATE |
REQMTSPLANNUMBER always empty via RFC | Ignore the empty return; verify demand in PBIM |
BAPI_MATERIAL_PLANNING |
Returns statistics (plaf_ins, plaf_del) but no order numbers | Query PLAF after commit to get planned order numbers |
BAPI_PRODORD_CREATE |
RETURN is export structure, not table | getExportParameterList().getStructure("RETURN") |
BAPI_PRODORD_CREATE |
Date fields are BASIC_START_DATE / BASIC_END_DATE | Not BASIC_START / BASIC_END |
BAPI_PLANNEDORDER_GET_DETAIL |
Planned order number must be 10-digit zero-padded | Pad with leading zeros before calling |
| Test | Result | Details |
|---|---|---|
| Create PIR for MZ-FG-M500 | PASS | 2,000 units, version 00 → demand created in PBIM/PBHI |
| Test | Result | Details |
|---|---|---|
| MRP for MZ-FG-M500 | PASS | plaf_ins=1, planned order 4000001463 (1,129 units = 2,000 demand − 871 stock) |
| Test | Result | Details |
|---|---|---|
| Convert planned 4000001463 | PASS | Production order 000001002167 created |
| MRP cleanup run | PASS | plaf_del=1 — consumed planned order removed |
| Step | Result | Details |
|---|---|---|
| PIR (Step 0) | PASS | Demand created |
| MRP (Step 1) | PASS | Planned order generated |
| Review (Step 2) | PASS | PA + PP elements visible |
| Convert (Step 3) | PASS | Prod order created, PLAF cleaned |
BAPI_MATERIAL_PLANNING with PROC_TYPE='1' and PLANNING_MODE='3'.