Margin Master Handbook
- Prerequisites
- Firewall and Network Requirements
- Microsoft Edge Download Block
- SQL Server Authentication Setup for IT Administrators
- Antivirus and Endpoint Security Exclusions
- SQL Server Tools
- Install or Upgrade SQL Server Express
- Install or Upgrade SQL Server Management Studio
- A Tour of the Main Window
- The Menus
- The Button Row
- The Main Data Grid
- The Summary Section
- Banners and Badges
- The Options Window
- Store Configuration
- Miscellaneous
- Export Options
- Vendor Settings
- POS / Connections Tabs
- Background Service
- AI Assistant (Experimental)
- How POS Import Works
- Supported Point-of-Sale Systems
- Data Import Workflows
- RockSolid POS Import
- Epicor Eagle FTP Import
- Epicor MySQL Compass Import
- Transact POS Import
- Paladin POS Import
- Falcon POS Import
- Spruce POS Import
- Bistrack POS Import
- RockSolid Max POS Import
- ECS POS Import
- Prosperity POS Import
- Catalyst POS Import
- Westlake POS Import
- MI9 POS Import
- AS/400 POS Import
- CounterWorks POS Import
- Dimension POS Import
- PacSoft POS Import
- ProStix POS Import
- Sympac POS Import
- Propello POS Import
- EagleVision POS Import
- MySQL/Compass Connection Configuration
- ECI VPN Requirement (Spruce, RockSolid MAX)
- Propello POS Integration Guide
- MI9 Data Pipeline — Import to Main Table
- Troubleshooting: Epicor Import Brought In 0 SKUs
- PACE POS Import
- Advantage POS Import
- General Store POS Import
- J-3 POS Import
- KeyStroke POS Import
- AutBusSystem POS Import
- Tomax POS Import
- Enterprise POS Import
- Computakey POS Import
- Redisell POS Import
- DART POS Import
- MIB POS Import
- Substruct POS Import
- DMAS POS Import
- RODS POS Import
- Procom POS Import
- Agility POS Import
- Acumen POS Import
- Cruise POS Import
- LSandE POS Import
- Retalix POS Import
- DIBCorp POS Import
- GMROI POS Import
- ECi Advantage POS Import
- Sunray POS Import
- ActivantAutomotive POS Import
- IBS POS Import
- Versys POS Import
- RMS POS Import
- TAMS POS Import
- Integrasoft POS Import
- Dynamic POS Import
- Microsoft ARS POS Import
- CDS POS Import
- Spartan POS Import
- Nitterhouse POS Import
- Jeds POS Import
- Theisens POS Import
- Emery Jensen POS Import
- SMS Pro POS Import
- Burdens POS Import
- Intact POS Import
- Bloom Retail POS Import
- NCR Counterpoint POS Import
- Horizon POS Import
- Cloud Vendor Data Sync
- Data Aging and Freshness Warnings
- Resetting Vendor and POS Data
- Rebuild Data and Rebuild Selection Lists
- Troubleshooting Vendor Sync
- Understanding the Strategy Hierarchy
- Pricing Strategies
- Creating a Strategy Step
- Updating and Deleting Steps
- Reviewing a Strategy and Running It
- Shared (Premade) Strategies
- SKU-Level Exceptions
- Manage Custom Groups
- Strategy Execution Cloud Tracking
- Importing from Excel
- Cost Break Analysis
- Cost Break Strategies
- Min/Max Strategies
- Strategy Backup & Restore
- Add Items from Catalog
- Store Grouping
- Data Diagnostics and Missing Index Recommendations
- System Diagnostics
- Margin Master Cannot Save Settings
- SQL Server 2025 Express vs. Full License
- Support Issue Management
- Catalog Lookup
- What's New After an Update
- Version History
- Documentation and the Help Buttons
- CTLD — Do It Best Catalog File
- MARGIN_MASTER — Ace Catalog File
- PCDITEMXREF — Do It Best SKU Classification File
- SAP_ZONE_PRICE_MARGIN_MASTER — Ace Zone Pricing File
- PCDPRODCLASS — Do It Best Product Classification Hierarchy File
- SAP_STORE_DEPT_ZONE_MARGIN_MASTER — Ace Store Zone Assignment File
- Taxonomy — Ace Product Classification File
- Mapp_Pricing — Ace MAP & IMAP Pricing File
- margin_mstr_plano — Ace Planogram File
On This Page
MI9 Data Pipeline — Import to Main Table
What is this?
This document walks through the complete MI9 data pipeline — from the tab-delimited export files your MI9 system produces, through import and team-based aggregation, to the final main table that Margin Master uses for pricing analysis.
MI9 is unique among Margin Master's supported POS systems: instead of building one main table per store, it aggregates all stores into team-based main tables (e.g., Core, NonCore). This means you analyze pricing at the team level rather than the individual store level.
Overview
MM*.txt files (one per team, from MI9)
|
v
[Import & Parse]
|
v
Mi9PointOfSaleData table (raw per-store rows)
|
v
[Team Aggregation]
|
v
StoreGroup_{TeamName}_mainTable (one per team)
|
v
[Ace Catalog Enrichment]
|
v
Final main table (ready for pricing analysis)
Step 1: Prepare Your MI9 Export Files
File Requirements
| Property | Value |
|---|---|
| File pattern | MM*.txt (e.g., MM_Team1.txt, MM_Team2.txt, MM_Unsequenced.txt) |
| Delimiter | Tab |
| Header | Yes (first row) |
| Encoding | UTF-8 |
| Total fields | 75+ per row |
Place your MI9 export files in the configured import folder (set in Options > Miscellaneous > Default Import Path). Margin Master discovers all files matching the MM*.txt pattern in that folder.
Import File Fields
Every row in the file represents one SKU at one store. The complete set of fields in the import file:
Identifiers
| Field | Description |
|---|---|
| StoreNumber | MI9 branch code identifying the store |
| SKU | Store-level SKU |
| AceSku | Ace Hardware stock number (takes priority over SKU when present) |
| AltSKU | Alternate SKU |
| UPC | Universal Product Code |
Product Information
| Field | Description |
|---|---|
| Description | Product description |
| Department | Store department code |
| Class | Product class |
| ProductGroup | Product group |
| Location | Planogram / bin location |
| MfgNumber | Manufacturer part number |
| PrivateLabel | Private label indicator |
Retail Price Levels
| Field | Description |
|---|---|
| Retail | Base retail price |
| RetailLevel1 | Price level 1 |
| RetailLevel2 | Price level 2 |
| RetailLevel3 | Price level 3 |
| RetailLevel4 | Price level 4 |
| RetailLevel5 | Price level 5 (MAPP pricing) |
| RetailZone | Zone retail price |
| Level1Zone through Level5Zone | Zone-level prices |
Future Pricing
| Field | Description |
|---|---|
| FuturePrice | Planned future retail price |
| FuturePriceMain | Future price at the main level |
| FuturePriceMainDate | Effective date for main-level future price |
| FuturePriceLevel2 | Future price at level 2 |
| FuturePriceLevel2Date | Effective date for level 2 future price |
Cost
| Field | Description |
|---|---|
| CurrentPrice | Current selling price |
| StoreCost | Store cost |
| AVGCost | Average cost (weighted) |
| ReplacementCost | Current replacement cost from vendor |
Vendor
| Field | Description |
|---|---|
| PrimeVendor | Primary vendor code |
| VendorNumber | Vendor number |
| VendorName | Vendor name |
| AVendor | Alternate vendor code |
| Buyer | Buyer code and name |
| VendorOwner | Vendor owner code |
| P_Vendor | P-vendor code |
Competitive Pricing
| Field | Description |
|---|---|
| Comp1 through Comp5 | Competitor price levels |
| CompA | Competitor A price |
| CompMemo | Competitor memo/notes |
| TaxRate | Tax rate |
Inventory & Sales History
| Field | Description |
|---|---|
| OnHand | Current on-hand quantity |
| History1 through History12 | Monthly sales units (12 rolling periods) |
| SalesUnits | Total sales units |
| ItemWeight | Item weight |
Status & Flags
| Field | Description |
|---|---|
| ProductStatus | Product-level status code |
| ProdStatus | Product status (alternate field) |
| StoreProductStatus | Store-level product status (see status codes below) |
| BlockedAutoChanges | When set, prevents automatic price changes during strategy execution |
| Discontinued | Discontinued flag |
| DiscontinuedDate | Date item was discontinued |
| AceStopItem | Ace stop-stock indicator |
| VelocityCode | Velocity classification code |
| PricingStrategy | Assigned pricing strategy code |
User-Defined Fields
| Field | Description |
|---|---|
| UserS | User string field S |
| UserO | User string field O |
| UserP | User string field P |
| UserE1 through UserE5 | Extended user fields 1-5 |
Dates
| Field | Description |
|---|---|
| CreateDate | Record creation date |
| LastUpdate | Last record update date |
| LastPriceChange | Date of last price change |
| LastCostChange | Date of last cost change (optional field) |
Discovery & Other
| Field | Description |
|---|---|
| DiscoveryC, DiscoveryN, DiscoveryS, DiscoveryU | Ace Discovery tier codes |
| HDLChg | Handle change flag |
| OrderMultiple | Order multiple quantity |
| OrderMultipleSKU | Order multiple SKU reference |
| StoreName | Name of the MI9 store |
| StoreType | Store type classification |
| TheTeam | MI9 team assignment from export |
Store Product Status Codes
The StoreProductStatus field uses these single-character codes:
| Code | Meaning |
|---|---|
| A | Item shut off (store heading into conversion) |
| B | Special Order SKU (one-time, not maintained) |
| C | Live item (ordering and selling) |
| D | Special Order Master SKU (not maintained) |
| N | Not used |
| R | Restricted by state/city (cannot sell) |
| S | Store requested stop stocking (sell-down mode) |
| W | Dead item (no stock, no longer maintained) |
| X | Merged with another item |
Step 2: Import Reads Files Into Staging Table
When you click Import Data, Margin Master:
- Discovers all
MM*.txtfiles in the import folder - Creates the
Mi9PointOfSaleDatastaging table (drops and recreates each import) - Parses each file row by row using high-performance tab parsing
- Maps each row to the data model using hardcoded field mappings
- Bulk-merges rows in batches of 10,000 into
Mi9PointOfSaleData - Creates indexes for fast lookups after all files are loaded
The staging table has a composite primary key of (StoreNumber, SKU) with duplicate-key handling, so if the same store+SKU appears in multiple files, the last value wins.
Large file handling: Files are processed in batches of 50,000 records for memory efficiency. Failed batches automatically retry up to 5 times with increasing wait times.
Step 3: Store Manager Controls Team Assignment
Before team tables are built, Margin Master checks the Mi9StoreManager table to determine which stores belong to which team.
| Mi9StoreManager Field | Purpose |
|---|---|
| BranchCode | MI9 store identifier — must match StoreNumber in import files |
| GroupName | Team name (e.g., "Core", "NonCore") |
| VendorStoreNumber | Your Ace Hardware or vendor store number |
| Excluded | If checked, this store is skipped during import |
| ZoneNumberAssigned | Pricing zone assignment |
| ZoneNumberOverride | Zone override (takes priority over assigned zone) |
Team assignment: By default, store 500 is assigned to "NonCore" and all other stores to "Core". Your support team can customize these assignments.
Step 4: Team Aggregation Builds the Main Table
This is the key step that makes MI9 different from other POS systems. Instead of one main table per store, Margin Master groups all stores in a team together and produces one row per SKU per team.
For each team (e.g., Core, NonCore), a StoreGroup_{TeamName}_mainTable is created.
How Fields Are Aggregated
Since multiple stores may carry the same SKU, fields must be combined. Here is exactly how each main table field is populated:
Identifiers
| Main Table Field | Source | Aggregation | Notes |
|---|---|---|---|
| SKU | AceSku or SKU | — | Uses AceSku if present, otherwise original SKU |
| AltSKU | SKU | — | Always the original MI9 SKU (for traceability) |
| StoreNumber | (literal) | — | Always set to "G" (represents the group, not an individual store) |
| StoreGroup | GroupName | MAX | Team name from Mi9StoreManager |
| UPC | UPC | MIN | Takes the lowest UPC value across stores |
Product Information
| Main Table Field | Source | Aggregation |
|---|---|---|
| Description | Description | MAX |
| Dept | Department | MAX |
| Class | Class | MAX |
| ProductGroup | ProductGroup | MAX |
| Location | Location | MAX |
Pricing — CurrentPrice Cascade
The CurrentPrice field uses a priority-based cascade across all stores carrying the item. Margin Master picks the first non-zero value in this order:
- FuturePriceLevel2 — if a Level 2 future price is set and > 0
- FuturePriceMain — if a main-level future price is set and > 0
- RetailLevel3 — if retail level 3 is > 0
- RetailLevel2 — if retail level 2 is > 0
- RetailLevel1 — default fallback
The result is the MAX value across all stores in the team for the winning priority level.
Pricing — Other Fields
| Main Table Field | Source | Aggregation | Notes |
|---|---|---|---|
| Comp1 | RetailLevel1 | MAX | Competitor price level 1 |
| Comp2 | RetailLevel2 | MAX | Competitor price level 2 |
| Comp3 | RetailLevel3 | MAX | Competitor price level 3 |
| Comp4 | RetailLevel4 | MAX | Competitor price level 4 |
| Comp5 | RetailLevel5 | MAX | MAPP pricing |
| FuturePrice | FuturePrice | MAX | Planned future retail |
| MemberRetail | FuturePrice | MAX | Defaults to future price |
| TaxRate | TaxRate | MAX |
Cost Fields
Cost fields are averaged only across stores that have inventory on hand:
| Main Table Field | Source | Aggregation | Condition |
|---|---|---|---|
| StoreCost | AVGCost | AVG | Where OnHand > 0 |
| Cost | AVGCost | AVG | Where OnHand > 0 |
| AverageCost | AVGCost | AVG | Where OnHand > 0 |
| ReplacementCost | ReplacementCost | MAX | Where OnHand > 0 |
| AverageCostInventoryValue | AVGCost * OnHand | SUM | Total inventory value |
Why only stores with inventory? Averaging cost across stores that don't carry the item would dilute the true cost. Only stores with on-hand inventory contribute to cost calculations.
Inventory & Sales (Summed Across All Stores)
| Main Table Field | Source | Aggregation |
|---|---|---|
| OnHand | OnHand | SUM |
| History1 | History1 | SUM |
| History2 | History2 | SUM |
| ... | ... | SUM |
| History12 | History12 | SUM |
These are summed because you want to see total movement and total inventory across all stores in the team.
Vendor
| Main Table Field | Source | Aggregation |
|---|---|---|
| PrimeVendor | PrimeVendor (fallback: Vendor) | MAX |
| AVendor | AVendor (fallback: VendorNumber) | MAX |
| Vendor | Vendor | MAX |
User-Defined Fields
MI9 remaps user fields for its multi-store context:
| Main Table Field | MI9 Source | Aggregation | What It Means |
|---|---|---|---|
| User1 | Buyer | MAX | Reformatted as "##-BuyerName" (e.g., "05-Smith") |
| User2 | UserE2 | MAX | Extended user field 2 |
| User3 | StoreProductStatus | MAX | Representative status code for the SKU (see status codes above) |
| User4 | (computed) | — | "Y" if any store has a future price date in the future or a price change that took effect within the last 60 days, "N" otherwise |
| User5 | BlockedAutoChanges | — | "Y" if blocked, "N" otherwise |
| UserE5 | (copy of User5) | — | Duplicate of the blocked-auto-changes flag |
| UserS | Buyer (reformatted) | MAX | |
| UserO | UserO | MAX | |
| UserP | UserP | MAX |
Date Fields
| Main Table Field | MI9 Source | Logic |
|---|---|---|
| LastUpdate | FuturePriceLevel2Date or FuturePriceMainDate | Uses Level2 date if RetailLevel2 > 0, otherwise Main date. Only future dates. Displayed as "MI9 $ Change Date" |
| UserDatetime1 | FuturePriceLevel2Date or FuturePriceMainDate | Same source as LastUpdate, but keeps past dates too — the effective date of the most recent (or next) price change. Displayed as "Mi9 $ Effective Date" |
| LastPriceChange | AceZoneRecords (post-build) | Filled by the Ace catalog enrichment with the Ace vendor price-change date. Displayed as "Last Ace $ Change" |
| CreateDate | CreateDate | MAX, defaults to current date if null |
Step 5: Ace Catalog Enrichment
After the team tables are built, Margin Master enriches each row with data from the Ace Hardware cloud catalog (synced separately via Cloud Sync).
Post-Build Updates
| Main Table Field | Source Table | Logic |
|---|---|---|
| VendorOwned | AceCatalogRecords | "VEND" if the SKU exists in the Ace catalog, "NON" otherwise |
| Dept2 | AceCatalogRecords | Ace department |
| Class2 | AceCatalogRecords | Ace merchandise class |
| ProductGroup2 | AceCatalogRecords | Ace product group |
| VendorNumber | AceCatalogRecords | Ace vendor number |
| VendorName | AceCatalogRecords | Ace vendor name |
| PricingStrategy | AceCatalogRecords | Ace pricing strategy code |
| Ace_MapPriceAmt | AceMappPricingRecords | MAP (Minimum Advertised Price) amount |
| Ace_ImapPriceAmt | AceMappPricingRecords | IMAP price amount |
| Ace_MapLastUpdate | AceMappPricingRecords | MAP last update date |
Zone Pricing (If Applicable)
If your stores have zone pricing configured, Margin Master applies zone-specific overrides from AceZoneRecords:
| Main Table Field | Source |
|---|---|
| RetailZone | Zone retail price |
| Level1Zone through Level5Zone | Zone-level prices |
| Ace_MSURetail | MSU retail from zone data |
| Ace_AceRetailNew | Ace retail new price |
Step 6: Calculated Fields
After enrichment, Margin Master computes additional analysis fields:
| Field | Calculation |
|---|---|
| CMargin | Current margin: (CurrentPrice - Cost) / CurrentPrice |
| FMargin | Future margin: (FuturePrice - Cost) / FuturePrice |
| Last12 | Sum of History1 through History12 |
| Impact | Revenue impact: (FuturePrice - CurrentPrice) * Last12 |
| CPAceRetailVar | Variance: CurrentPrice vs Ace Retail |
| FPAceRetailVar | Variance: FuturePrice vs Ace Retail |
| CPFPVar | Variance: CurrentPrice vs FuturePrice |
| CPFPDif | Difference: FuturePrice - CurrentPrice |
Impact Calculation (MI9-Specific)
For MI9, impact is calculated by joining the team table back to the per-store staging data using the AltSKU field. This gives a more accurate picture because it uses each store's actual current price and sales history rather than the aggregated values.
Per-SKU Impact = SUM across stores of:
(Team FuturePrice - Store CurrentPrice) * Store Last12
Tips
"G" store number: In the main table, you'll see StoreNumber = "G" on every row. This is normal for MI9 — it means the row represents the entire store group (team), not an individual store.
AceSku takes priority: When the import file includes an AceSku value, that becomes the primary SKU in the main table. The original MI9 SKU is preserved in the AltSKU field for traceability.
Blocked auto-changes: If the
BlockedAutoChangesfield is set for a SKU, it appears as User5 = "Y" in the main table. During strategy execution, these SKUs are skipped — their prices won't be automatically changed.
Store not appearing? Check the Mi9StoreManager configuration. The store's BranchCode must match the StoreNumber in the import files, and the Excluded flag must not be checked.
Common Questions
Q: Why do I see "Core" and "NonCore" as my teams? A: By default, Margin Master assigns store branch 500 to "NonCore" and all other branches to "Core". Your support team can customize team assignments in the Mi9StoreManager configuration.
Q: How are costs calculated when some stores don't carry the item? A: Costs are averaged only across stores that have the item on hand (OnHand > 0). Stores with zero inventory don't contribute to the cost calculation, preventing artificial dilution.
Q: What is the CurrentPrice cascade? A: MI9 has multiple price levels. Margin Master picks the first non-zero value in priority order: FuturePriceLevel2 > FuturePriceMain > RetailLevel3 > RetailLevel2 > RetailLevel1. This ensures you always see the most relevant current price.
Q: Where does vendor catalog data come from? A: Vendor catalog fields (Dept2, Class2, ProductGroup2, VendorNumber, VendorName, MAP pricing) come from the Ace Hardware cloud catalog, which syncs separately via Cloud Sync. These fields are populated after the team tables are built.
Q: Can I add custom fields? A: MI9 imports support User fields (UserS, UserO, UserP, UserE1-E5). These map to User1-User5 and UserE1-UserE5 in the main table. Contact support if you need to customize what data appears in these fields.
