Welcome to the Grocery Inventory Management System! This guide will help you understand the architecture and contribute to the project.
- Architecture Overview
- Server-Side Configuration
- App Flow
- Data Models
- Development Setup
- Contributing Guidelines
This is a distributed grocery inventory management system built with:
- Frontend: iOS (Swift/SwiftUI) and Android (Kotlin/Jetpack Compose)
- Backend: Couchbase Server with App Services (Sync Gateway)
- Sync: Bi-directional replication with P2P support (Couchbase Lite 3.3+)
- Multi-store inventory management (AA-Store, NYC-Store, etc.)
- Real-time synchronization across devices
- Peer-to-peer sync for offline collaboration
- Order management with status tracking
- Store profile management
The "Supermarket" bucket contains:
- 4 scopes:
AA-Store,NYC-Store,_default,_system - 3 collections per store scope:
inventory(80 documents per store)orders(150 documents per store)profile(1 document per store)
📦 supermarket (bucket)
├── 📁 AA-Store (scope)
│ ├── 📚 inventory (80 docs)
│ ├── 📚 orders (150 docs)
│ └── 📚 profile (1 doc)
├── 📁 NYC-Store (scope)
│ ├── 📚 inventory (80 docs)
│ ├── 📚 orders (150 docs)
│ └── 📚 profile (1 doc)
├── 📁 _default (1 collection)
└── 📁 _system (2 collections)
Service Name: supermarket-appservice (linked to supermarket cluster)
App Endpoints (one per store):
supermarket-aa→ AA-Store scopesupermarket-nyc→ NYC-Store scope
Linked Collections (per endpoint):
- ✅
inventory - ✅
orders - ✅
profile
Username Format: <storeId>@supermarket.com
Example Users:
-
NYC Store:
nyc-store-01@supermarket.com/P@ssword1- App Endpoint:
supermarket-nyc - Channels:
inventory,orders,profile
- App Endpoint:
-
AA Store:
aa-store-01@supermarket.com/P@ssword1- App Endpoint:
supermarket-aa - Channels:
inventory,orders,profile
- App Endpoint:
Access Control: Default policy (all users have read/write access to their assigned channels)
When an employee logs in (e.g., nyc-store-01@supermarket.com):
-
App must have configurable option:
App Endpoint URL- User updates this to point to their App Endpoint
-
ONE SHOT replication on the
profilecollection:// Pull down store profile document let replicator = Replicator(config: profileConfig) replicator.start()
- ✅ If credentials are correct → fetch profile doc
- ❌ If credentials are wrong → authentication error
- Update UI with store info
Set up continuous replication on inventory and orders collections:
var config = ReplicatorConfiguration(target: endpoint)
config.continuous = true
config.replicatorType = .pushAndPull
config.addCollections([inventoryCollection, ordersCollection])Conflict Resolution: Use default policy (last-write-wins or custom CRDT for inventory counts)
When inventory count is updated (e.g., "Organic Milk"):
CURRENT Document:
{
"_id": "Inventory_NYCStore_10000",
"docType": "Inventory",
"productId": 10000,
"sku": "NYC-10000",
"name": "Organic Milk",
"brand": "BudgetBest",
"category": "Dairy",
"price": 29.86,
"unit": "gallon",
"stockQty": 71,
"location": {"aisle": 24, "bin": 7},
"attributes": {"organic": true, "size": "1 gallon", "perishable": true},
"imageURL": "http://images.nycstore.com/dairy/organic-milk-budgetbest-gallon.jpg",
"expirationDate": 1758488734450,
"lastUpdated": 1748483079450,
"storeId": "nyc-store-01"
}NEW Document (after update):
{
"_id": "Inventory_NYCStore_10000",
"docType": "Inventory",
"productId": 10000,
"sku": "NYC-10000",
"name": "Organic Milk",
"brand": "BudgetBest",
"category": "Dairy",
"price": 29.86,
"unit": "gallon",
"stockQty": 30, // ← Updated from 71 to 30
"location": {"aisle": 24, "bin": 7},
"attributes": {"organic": true, "size": "1 gallon", "perishable": true},
"imageURL": "http://images.nycstore.com/dairy/organic-milk-budgetbest-gallon.jpg",
"expirationDate": 1758488734450,
"lastUpdated": 1748483079450,
"storeId": "nyc-store-01"
}- Document is saved locally
- Continuous replicator syncs to server
- Other employees see the update in real-time
Creating an Order (for now, one item per order):
- User selects an item (e.g., "Organic Milk")
- Pre-populated order form displays with item details
- User enters order quantity
- Clicks "Create Order"
Order Document Format:
{
"_id": "order-nyc-store-01-V1StGXR8_Z5jdHi6B-myT",
"docType": "Order",
"storeId": "nyc-store-01",
"orderDate": 1755257767451,
"orderStatus": "Submitted",
"productId": 10000,
"sku": "NYC-10000",
"unit": "gallon",
"orderQty": 30
}Order ID Format: order-<StoreID>-<UUID> (or NanoID if UUID is too long)
Example: order-nyc-store-01-V1StGXR8_Z5jdHi6B-myT
On Server Side:
- Admin navigates to cluster → views orders in document browser
- Updates
orderStatusto:"Processing"(order being prepared)"Rejected"(order declined)"Fulfilled"(order completed)
Example Update:
{
"_id": "order-nyc-store-01-V1StGXR8_Z5jdHi6B-myT",
"docType": "Order",
"storeId": "nyc-store-01",
"orderDate": 1755257767451,
"orderStatus": "Processing", // ← Updated by admin
"productId": 10000,
"sku": "NYC-10000",
"unit": "gallon",
"orderQty": 30
}- Changes sync down to all store employees
- Employees see updated order status in Orders page
{
"_id": "Inventory_<StoreID>_<ProductID>",
"docType": "Inventory",
"productId": 10000,
"sku": "<STORE>-10000",
"name": "Product Name",
"brand": "Brand Name",
"category": "Category",
"price": 29.86,
"unit": "gallon",
"stockQty": 71,
"location": {
"aisle": 24,
"bin": 7
},
"attributes": {
"organic": true,
"size": "1 gallon",
"perishable": true
},
"imageURL": "https://res.cloudinary.com/.../10000.png",
"expirationDate": 1758488734450,
"lastUpdated": 1748483079450,
"storeId": "store-id"
}{
"_id": "order-<storeId>-<UUID>",
"docType": "Order",
"storeId": "nyc-store-01",
"orderDate": 1755257767451,
"orderStatus": "Submitted|Processing|Rejected|Fulfilled",
"productId": 10000,
"sku": "NYC-10000",
"unit": "gallon",
"orderQty": 30
}{
"_id": "aa-store-01-profile",
"docType": "StoreProfile",
"storeId": "aa-store-01",
"name": "AA Supermarket - Downtown",
"location": {
"address1": "123 Market Street",
"locality": "Springfield",
"region": "IL",
"postalCode": "62704",
"country": "US",
"coordinates": {"lat": 39.7817, "lon": -89.6501}
},
"contact": {
"phone": "217-555-0199",
"email": "contact-aa@supermarket.com"
},
"hours": {
"monday": "08:00-21:00",
"tuesday": "08:00-21:00",
"wednesday": "08:00-21:00",
"thursday": "08:00-21:00",
"friday": "08:00-22:00",
"saturday": "08:00-22:00",
"sunday": "09:00-20:00"
},
"services": ["In-Store Shopping", "Curbside Pickup", "Home Delivery", "Pharmacy"],
"manager": {
"name": "Jordan Taylor",
"employeeId": "EMP-44321",
"email": "jordan.taylor@aasupermarket.com"
},
"establishedYear": 2010,
"lastRenovated": 2021,
"features": {
"parkingCapacity": 120,
"evChargingStations": 4,
"wheelchairAccessible": true
},
"status": "Open",
"lastUpdated": 1737971200123
}Leverage auto-discovery for peer-to-peer synchronization:
// iOS: Use MultipeerConnectivity
let multipeerSync = MultipeerP2PSyncManager(database: database)
multipeerSync.start()
// Android: Use Network Service Discovery
val p2pSync = P2PSyncManager(database)
p2pSync.startAdvertising()
p2pSync.startDiscovery()Benefits:
- Offline collaboration between store employees
- No server required for local sync
- Automatic conflict resolution with CRDT
- iOS: Xcode 15+, Swift 5.9+
- Android: Android Studio, Kotlin 1.9+
- Couchbase Lite: 3.3.0+
- Couchbase Server: 7.6+ with App Services
- Clone the repository
- Open
GroceryApp.xcodeprojin Xcode - Add
aa_store_inventory.jsonto project (if not already added) - Update App Endpoint URL in settings:
// AppServicesSyncManager.swift private let syncGatewayURL = "wss://your-endpoint.apps.cloud.couchbase.com:4984/supermarket-aa"
- Build and run on simulator or device
- Clone the repository
- Open
GroceryApplicationin Android Studio - Add AAR files to
app/libs/ - Update App Endpoint URL:
// AppServicesSyncManager.kt private val SYNC_GATEWAY_URL = "wss://your-endpoint.apps.cloud.couchbase.com:4984/supermarket-nyc"
- Build and run
iOS:
xcrun simctl uninstall booted com.couchbase.GroceryAppAndroid:
adb uninstall com.example.groceryapplication- iOS: Follow Swift API Design Guidelines
- Android: Follow Kotlin Coding Conventions
- Use meaningful variable names
- Add comments for complex logic
feat: Add order creation functionality
fix: Resolve inventory sync conflict
docs: Update CONTRIBUTING.md with P2P setup
refactor: Simplify image caching logic
- Fork the repository
- Create a feature branch:
git checkout -b feature/order-management - Make your changes with clear commits
- Test thoroughly on both iOS and Android
- Update documentation if needed
- Submit PR with description of changes
- Login with valid/invalid credentials
- Profile fetch works correctly
- Inventory updates sync across devices
- Order creation and status updates work
- P2P sync works between devices
- Images load asynchronously without blocking UI
- App works offline (local changes queue for sync)
- ❌ Improved authentication (not Oct target)
- ❌ Prebuilt database (not Oct target)
⚠️ Orders limited to ONE item per order⚠️ Multiple item selection not implemented yet⚠️ Order conflicts unlikely (each order is unique document)
This project is licensed under the MIT License - see the LICENSE file for details.
Happy Contributing! 🚀