Example 01: MRP Order Creation Primary Workflow
Overview
The MRP order creation workflow is the core business process of this dashboard, implementing sophisticated material requirements planning calculations to determine optimal purchase order quantities based on safety stock levels, minimum quantities, and current demand.
Business Context
Manufacturing and distribution companies need to maintain optimal inventory levels to meet customer demand while minimizing carrying costs. This dashboard automates the complex calculations required to determine when and how much to order from suppliers.
Workflow Implementation
Step 1: MRP Calculation
The system uses the custom CG_InventoryOrdering_View to calculate reorder requirements:
-- Safety stock based calculation
IIf(
QtyOnHand - ([QtyInTransit] + [QtyAllocated] + [QtyAllocatedWip]) < SafetyStockQty,
IIf(
ReOrderQty > 0,
ReOrderQty * CEILING((SafetyStockQty - (QtyOnHand - ([QtyInTransit] + [QtyAllocated] + [QtyAllocatedWip]))) / ReOrderQty),
SafetyStockQty - (QtyOnHand - ([QtyInTransit] + [QtyAllocated] + [QtyAllocatedWip]))
),
0
) AS QtyToOrder
This calculation:
- Determines available quantity (on-hand minus allocations)
- Compares against safety stock level
- Calculates shortage if below safety stock
- Rounds up to reorder quantity multiple if specified
- Returns exact shortage if no reorder quantity defined
Step 2: User Selection and Filtering
Users can filter and select items for ordering:
// WarehouseOrderingViewModel.cs
public IQueryable<WarehouseOrderingListItem> GetWarehouseQueryable(
WarehouseSearchFilter searchFilter)
{
var queryable = from x in _sysproDataContext.CG_InventoryOrdering_View
select new WarehouseOrderingListItem
{
Warehouse = x.Warehouse,
Supplier = x.Supplier,
StockCode = x.StockCode,
OrderQty = x.QtyToOrder ?? 0,
// ... additional mappings
};
// Apply user filters
if (!string.IsNullOrWhiteSpace(searchFilter.SelectedSupplier))
{
queryable = queryable.Where(x =>
x.Supplier.Contains(searchFilter.SelectedSupplier));
}
return queryable;
}
Step 3: Order Consolidation
Selected items are grouped into purchase orders:
// InvOrderingService.cs
public IEnumerable<SummaryOrder> CreateOrderSummary(
IEnumerable<InvOrderingOrderSummaryListItem> items,
string buyer,
bool orderByWarehouse,
bool orderByDueDate)
{
// Dynamic grouping based on settings
var supplierGroups = orderByWarehouse ?
items.GroupBy(x => new { x.Supplier, x.Warehouse }) :
items.GroupBy(x => x.Supplier);
foreach (var group in supplierGroups)
{
var order = new SummaryOrder
{
Supplier = group.Key.Supplier,
Warehouse = orderByWarehouse ? group.Key.Warehouse : null,
OrderLines = group.Select(ConvertToOrderLine).ToList()
};
summary.Add(order);
}
}
Step 4: SYSPRO Integration
Orders are posted to SYSPRO through the PORTOI business object:
// SysproPostService.cs
public IEnumerable<SummaryOrder> CreateOrders(
IEnumerable<SummaryOrder> orders)
{
foreach (var order in orders)
{
// Generate XML for SYSPRO
var inputXml = GetInputXml(order);
var paramXml = GetParamXml();
// Post to SYSPRO
var outputXml = PerformBusinessObjectPost(
inputXml, paramXml, "PORTOI");
// Parse response
order.OrderNumber = ParseXmlOutput(outputXml);
}
return orders;
}
Step 5: Result Presentation
Successfully created orders are displayed with details:
// InvOrderingCompletionViewModel.cs
public void SetOrderResult(CreateOrdersResult ordersResult)
{
OrderSummaries = ordersResult.Orders;
Buyer = ordersResult.Buyer;
// Display success/failure status for each order
foreach (var order in OrderSummaries)
{
if (!order.PostFailed)
{
// Show order number and line count
DisplaySuccessfulOrder(order);
}
else
{
// Show error details
DisplayFailedOrder(order);
}
}
}
Validation and Business Rules
Pre-Order Validation
- Supplier must be active (not on hold)
- Stock items must not be on hold
- User must have purchase order creation permissions
- Buyer must be selected
Order Processing Rules
- Respect minimum order quantities
- Apply reorder quantity multiples
- Consider lead times for due dates
- Group by warehouse if configured
Error Handling
- Supplier on hold: Skip order, notify user
- Stock validation failures: Remove invalid lines
- SYSPRO posting errors: Log and display to user
- Partial success: Show successful and failed orders
Performance Optimizations
Database Query Optimization
- Custom view pre-calculates MRP requirements
- Indexed on frequently queried columns
- Efficient joins between related tables
Batch Processing
- Orders grouped to minimize SYSPRO calls
- Parallel processing where possible
- Async operations for UI responsiveness
Caching Strategy
- Cache supplier and warehouse lists
- Store user preferences
- Reuse SYSPRO session
Configuration Options
MepSettings.json
{
"SettingName": "OrderByWarehouse",
"SettingValue": "true",
"Description": "Group orders by warehouse"
},
{
"SettingName": "OrderByDueDate",
"SettingValue": "false",
"Description": "Group orders by due date"
}
User Preferences
- Default buyer selection
- Filter preferences
- Page size for grids
- Export format preferences
Testing Considerations
Unit Testing
- MRP calculation accuracy
- Order grouping logic
- Validation rules
Integration Testing
- SYSPRO posting success
- Error handling scenarios
- Session management
User Acceptance Testing
- Workflow completeness
- Performance under load
- Error message clarity
Lessons Learned
What Works Well
- Pre-calculated MRP view improves performance
- Settings-driven grouping provides flexibility
- Comprehensive error handling improves reliability
Challenges Overcome
- SYSPRO session timeout handling
- Large dataset performance
- Complex grouping requirements
Future Enhancements
- Suggested order scheduling
- Automatic order approval workflow
- Integration with forecasting systems