Project Overview
This client had a "mission impossible" data problem. They ran their business on two different POS systems (e.g., retail and warehouse) with thousands of products. The critical challenge: there was no common id or sku to link the products.
The only link was the product name, which was messy, inconsistent, and bilingual (English and Local Language). We designed and built a "Product Mapping Manager" that solved this by creating a smart, multi-stage matching and validation engine, all managed through a single cross-platform Flutter application.
😟 The Client's Problem: Data Chaos Across Two Systems
The client was "data blind." They couldn't link their warehouse inventory to their retail product catalog, leading to massive operational failures.
- No Shared Key: Thousands of products existed in two databases with no id, sku, or barcode to connect them.
- "Dirty" Bilingual Data: Product names were the only clue, but they were inconsistent. (e.g., "Red T-Shirt (M)" in one system might be "T-Shirt M Red [LocalLanguageName]" in the other).
- Manual Mapping was Impossible: With thousands of items, it was too time-consuming and error-prone for any human to do manually.
- Constant "New Product" Problem: New products were added daily, making any manual map instantly obsolete.
- No Single Source of Truth: The business couldn't answer simple questions, like "How many 'Red T-Shirts' do we really have in stock, across all systems?"
💡 The Solution: A Multi-Stage Mapping & Validation Engine
We built a complete, three-part system. The "heart" is a smart backend engine, and the "brain" is a cross-platform Flutter app that lets the user manage the entire process.
1. The Core Matching Engine (The "Heart")
This backend process runs to create the initial map.
- Intelligent Data Cleaning: We built a robust preprocessName function to "normalize" the bilingual names. It strips special characters, case variations, and irrelevant text (like 'x 10pcs') to find the 'core' product name.
- Advanced Matching Logic: The engine fetches all products from both POS APIs (fetchProducts) and then compares the "clean" names using advanced string-matching techniques to establish a relationship.
- Handles One-to-Many Relationships: The system was built to handle complex mappings. For example, one "Master Product" in the warehouse (e.g., "T-Shirt") can be correctly mapped to multiple retail variations (e.g., "Small Red T-Shirt," "Medium Red T-Shirt").

Advanced product mapping algorithm visualization
2. The Validation & Alert System (The "Double-Check")
This system ensures the data stays clean.
- Continuous Validation: A secondary process runs to "double-check" the relationships created by the engine, flagging any low-confidence matches for human review.
- New Product Detection: When a new product is added to either POS, the engine automatically tries to find its match.
- Flag & Notify: If a match isn't found (a truly new item), it's flagged and sent to the user's "inbox" inside the Flutter app for manual mapping. This creates a perfect "human-in-the-loop" workflow.
3. The Flutter Management App (The "Command Center")
This cross-platform app allows the client to manage the entire system from any device.
- Manual Mapping: Users can review flagged products and manually create or approve matches.
- Barcode Lookup: For in-store validation, staff can use Barcode Scanner to instantly pull up a product and see its mapped relationships.
- Connection Status: The app includes live monitoring to ensure both POS APIs and the internet are connected.
🛠️ Technology Stack & Key Services
Key Services
- Product Data Matching
- Data Cleaning and Normalization
- Bilingual Data Processing
- Cross-POS System Integration
- Custom Data Validation Engines
- Flutter App Development
- One-to-Many Data Mapping
Technology Stack
- Mobile/Desktop App: Flutter
- Matching & Logic: Dart
- Mapping Database: Firebase Firestore
- API Integration: http package
- Hardware Integration: BarcodeScanner
⚙️ The Process
- Discovery & Data Analysis: This was the most critical phase. We analyzed sample data from both POS systems to identify all the "dirty data" patterns.
- Engine Architecture: We designed the multi-stage matching algorithm, starting with the preprocessName function.
- App Development (Flutter): We built the cross-platform Flutter app to act as the "command center."
- Full Integration: We connected to both POS APIs (fetchProducts) and set up Firestore.
- Testing & Validation: We ran the engine on the full dataset and manually verified hundreds of matches.
🏆 The Result: A Single Source of Truth from "Mission Impossible"
- Solved the Unsolvable: The system successfully mapped thousands of bilingual products across two databases that had no common keys.
- Saved Hundreds of Staff-Hours: It completely eliminated the need for manual, error-prone data entry.
- Created a Single Source of Truth: For the first time, the client could see the true relationship between their warehouse stock and retail products.
- A "Future-Proof" System: The automated "flagging" system ensures that the data stays clean and accurate.
- Empowered the Client: The Flutter app gave them a single, cross-platform tool to manage their entire product catalog.
Code Showcase
Future<List<Map<String, dynamic>>> fetchProducts(
String apiUrl, Map<String, String> headers, Function(String) updateStatusCallback) async {
int page = 1;
List<Map<String, dynamic>> products = [];
while (true) {
updateStatusCallback("Fetching page $page from $apiUrl");
final response = await http.get(Uri.parse("$apiUrl?page=$page"), headers: headers);
if (response.statusCode == 200) {
final data = json.decode(response.body);
final List pageProducts = data["data"] ?? [];
// ...extract product fields...
products.addAll(pageProducts);
if (data["links"]?["next"] == null) break;
page++;
} else {
break;
}
}
return products;
}
String preprocessName(String name) {
name = name.replaceAll(RegExp(r'\s*x\s*\d+\s*pcs'), '');
return name.replaceAll(' ', '').toLowerCase();
}
Future<void> matchAndUploadProducts(Function(String) updateStatusCallback) async {
// Fetch retail and warehouse products
// Preprocess and match by name/SKU
// Upload matched data to Firestore
}
Future<void> scanBarcode() async {
var result = await BarcodeScanner.scan();
if (result.type == ResultType.Barcode) {
searchProducts(result.rawContent);
}
}