Inventory Module Technical Documentation
1. Overview
The Inventory Module is a comprehensive solution for managing retail inventory audits across multiple locations. Built using React Native and Expo, it provides a guided workflow for field auditors to conduct systematic inventory counts, track stock levels, and submit detailed reports to stakeholders.
1.1 Core Capabilities
- Multi-location Inventory Management: Support for conducting audits across multiple retail locations with site-specific configurations
- [Schedule](../Schedule/Schedule Listing/ScheduleListingOverview.md)-based Auditing: Frequency and specific-day based scheduling with timezone-aware calculations
- Real-time Stock Tracking: Category-based SKU counting with movement tracking (in/out)
- Multimedia Attachments: Support for photos and videos with compression and upload management
- Digital Signatures: Dual signature capture (selfie and standard) for audit verification
- Automated Reporting: Email-based report distribution with customizable recipient lists
- Offline Support: Draft saving and sync capabilities for unreliable network conditions
1.2 Technical Stack
- Frontend: React Native + Expo
- State Management: Jotai (atoms) + React Query
- Form Management: React Hook Form
- Navigation: Expo Router (file-based routing)
- UI Components: Tamagui + Custom components
- Testing: Jest + React Native Testing Library
2. Architecture
The inventory module follows Clean Architecture principles with clear separation of concerns across three main layers:
graph TB subgraph "Presentation Layer" UI[UI Components] Controllers[Controllers] Screens[Screen Components] end subgraph "Business Logic Layer" UseCases[Use Cases] Services[Services] Domain[Domain Entities] end subgraph "Data Layer" Repositories[Repositories] API[API Clients] Cache[Cache/Storage] end UI --> Controllers Controllers --> UseCases UseCases --> Services Services --> Repositories Repositories --> API Repositories --> Cache Controllers --> Domain Services --> Domain UseCases --> Domain
2.1 Layer Responsibilities
-
Presentation Layer
- UI rendering and user interactions
- Form validation and input handling
- Navigation and routing logic
- State presentation
-
Business Logic Layer
- Business rules and validations
- Data transformations
- Workflow orchestration
- Domain model definitions
-
Data Layer
- API communication
- Data persistence
- Caching strategies
- External service integration
3. Navigation Flow
The inventory audit process follows a linear, guided workflow designed to ensure data completeness and accuracy:
graph LR Home[Home Screen] --> Schedules[Schedules List] Schedules --> Audit[Audit Form] Audit --> Review[Review & Sign] Review --> Payoff[Success Screen] Payoff --> Home Review -.->|Cancel| Audit Audit -.->|Back| Schedules
3.1 Flow Characteristics
- Progressive Disclosure: Information and actions revealed as needed
- Data Validation: Each step validates before progression
- State Preservation: Form data persisted across navigation
- Completion Tracking: Progress indicators throughout
4. Screens and Routes
4.1 Route Structure
| Screen | Route Path | File Location | Purpose |
|---|---|---|---|
| Home | /inventory | apps/expo/app/inventory/schedules.tsx | Entry point showing daily locations |
| Schedules | /inventory/[schedules](../Schedule/Schedule Listing/ScheduleListingOverview.md) | packages/app/features/inventory/schedules/screen.tsx | Available schedule selection |
| Audit | /inventory/audit | packages/app/features/inventory/audit/audit.tsx | Main counting interface |
| Review | /inventory/review | packages/app/features/inventory/review/review.tsx | Pre-submission validation |
| Payoff | /inventory/payoff | packages/app/features/inventory/payoff/screen.tsx | Completion confirmation |
4.2 Navigation Parameters
// Schedule to Audit
interface ScheduleToAuditParams {
scheduleId: string;
locationId: string;
scheduleName: string;
}
// Audit to Review
interface AuditToReviewParams {
draftReportId: string;
scheduleId: string;
}
// Review to Payoff
interface ReviewToPayoffParams {
reportId: string;
duration: number;
totalSKUs: number;
}5. Home Screen
The Home screen serves as the primary entry point for field auditors to view their daily inventory assignments.
Component Structure
graph TD HomeScreen[Home Screen] HomeScreen --> Header[Date Header] HomeScreen --> SearchBar[Search Input] HomeScreen --> LocationList[Location Card List] LocationList --> LocationCard[Location Card] LocationCard --> LocationInfo[Site Details] LocationCard --> ScheduleInfo[Schedule Status]
Key Features
-
Daily Schedule Display
- Shows current date with day indicator
- Filters locations requiring inventory today
- Timezone-aware scheduling
-
Search Functionality
- Real-time filtering of locations
- Search by site name or address
- Debounced input for performance
-
Location Cards
- Site information display
- Schedule status indicators
- Quick access to audit workflow
Controller Logic
The inventoryHomeController manages:
- Fetching daily inventory locations
- Search query processing
- Navigation to schedules
- Cache invalidation
// Key controller methods
interface InventoryHomeController {
locations: Location[];
isLoading: boolean;
searchQuery: string;
setSearchQuery: (query: string) => void;
navigateToSchedules: (locationId: string) => void;
refetch: () => void;
}Implementation Details
The home screen leverages:
- React Query for data fetching and caching
- Jotai atoms for global state management
- Debounced search for performance optimization
- Pull-to-refresh for data synchronization
6. Schedules Screen
The Schedules screen displays available inventory schedules for a selected location, with sophisticated status calculation and timezone-aware scheduling logic.
Component Architecture
graph TD SchedulesScreen[Schedules Screen] SchedulesScreen --> OfflineCheck{Internet Connected?} OfflineCheck -->|No| OfflinePage[Offline Page] OfflineCheck -->|Yes| MainContent[Main Content] MainContent --> Header[Schedule List Header] MainContent --> SearchBar[Search Input] MainContent --> ScheduleList[Schedule List] ScheduleList --> ScheduleItem[Schedule List Item] ScheduleItem --> StatusIndicator[Status Badge] ScheduleItem --> ProgressBar[Progress Indicator] ScheduleItem --> ActionButton[Action Button]
Schedule Status System
The module implements a sophisticated status calculation system with four primary states:
| Status | Color | Description | Action |
|---|---|---|---|
| COMPLETED | Green (#0BBB45) | Schedule fully finished | ”Report Available” |
| IN_PROGRESS | Yellow (#FFBC2B) | Has active draft | ”Continue >“ |
| NOT_STARTED | Grey (#8F8F8F) | Not begun yet | ”Tap to Start >“ |
| OVERDUE | Red (#FF0000) | Past deadline | ”Check-in Passed” |
Status Calculation Logic
The getScheduleStatusEnum function implements complex business rules:
flowchart TD Start[Start] --> CheckType{Schedule Type?} CheckType -->|Specific Days| SpecificDays[Check Specific Days Logic] CheckType -->|Frequency Based| FrequencyBased[Check Frequency Logic] SpecificDays --> SD1{Any Date Completed?} SD1 -->|Yes| Completed[COMPLETED] SD1 -->|No| SD2{Today Scheduled?} SD2 -->|Yes| SD3{Has Draft?} SD3 -->|Yes| InProgress[IN_PROGRESS] SD3 -->|No| SD4{In Time Window?} SD4 -->|No| Overdue[OVERDUE] SD4 -->|Yes| NotStarted[NOT_STARTED] SD2 -->|No| SD5{Past Dates Exist?} SD5 -->|Yes| Overdue SD5 -->|No| NotStarted FrequencyBased --> FB1{Past End Date?} FB1 -->|Yes| Overdue FB1 -->|No| FB2{Past Period Incomplete?} FB2 -->|Yes| Overdue FB2 -->|No| FB3{Current Period Status} FB3 --> FB4{All Occurrences Done?} FB4 -->|Yes| Completed FB4 -->|No| FB5{Has Draft?} FB5 -->|Yes| InProgress FB5 -->|No| FB6{In Time Window?} FB6 -->|No| Overdue FB6 -->|Yes| NotStarted
Schedule Types
-
Specific Days Schedules
- Fixed dates for inventory counts
- Each date tracked independently
- Complete when any date finished
-
Frequency-Based Schedules
- Daily, weekly, monthly patterns
- Flexible day selection
- Occurrence tracking (e.g., 3 times per week)
Key Features
-
Timezone-Aware Calculations
- Schedule-specific timezone support
- Fallback to site timezone
- All calculations in local timezone
-
Time Window Enforcement
- Optional deadline configuration
- Start/end time validation
- Automatic overdue marking
-
Draft Management
- Resume interrupted audits
- Sync pending saves
- Progress percentage display
-
Search and Filtering
- Real-time schedule filtering
- Search by schedule name
- Status-based filtering
Controller Methods
interface InventorySchedulesController {
// State
schedules: InventorySchedule[];
isLoading: boolean;
searchQuery: string;
// Actions
setSearchQuery: (query: string) => void;
createNewReport: (schedule: InventorySchedule) => Promise<void>;
continueReport: (schedule: InventorySchedule) => Promise<void>;
resetSchedule: (scheduleId: string) => Promise<void>;
// Navigation
navigateToAudit: (params: NavigationParams) => void;
}Business Logic Implementation
The schedule service implements:
-
Status Priority Hierarchy
COMPLETED → OVERDUE → IN_PROGRESS → NOT_STARTED -
Occurrence Calculation
- Weekly: Counts completed days in current week
- Monthly: Tracks completion within month
- Custom: Flexible period definitions
-
Progress Tracking
- Visual progress bars
- Completion ratios (X/Y format)
- Draft progress percentages
7. Audit Screen
The Audit screen is the core inventory counting interface where field auditors perform systematic stock counts and movement tracking.
7.1 Architecture Overview
graph TB subgraph "Presentation" AuditScreen[Audit Screen] FormComponents[Form Components] CategoryList[Category List] InputComponents[Input Components] end subgraph "Business Logic" AuditController[Audit Controller] AuditService[Audit Service] ValidationRules[Validation Rules] AutoSave[Auto-Save Manager] end subgraph "Data" FormState[React Hook Form] LocalStorage[MMKV Storage] API[Backend API] Atoms[Jotai State] end AuditScreen --> AuditController FormComponents --> FormState AuditController --> AuditService AuditController --> AutoSave AutoSave --> LocalStorage AuditService --> API AuditController --> Atoms
7.2 Audit Types
The system supports two fundamental audit methodologies:
1. Opname (Stock Count)
Direct inventory counting where auditors enter the observed stock quantity:
flowchart LR A[Observe Stock] --> B[Enter Count] B --> C[Validate Input] C --> D[Update Status] D --> E[Check Threshold] E -->|Below| F[Mark Low Stock] E -->|Above| G[Mark OK]
Characteristics:
- Single input field per SKU
- Direct stock quantity entry
- Automatic status calculation
- Low stock threshold validation
2. Movement (Stock Movement)
Tracks inventory changes through categorized movements:
flowchart TD Start[Initial Stock] --> InMovements[Stock In] Start --> OutMovements[Stock Out] InMovements --> PO[Purchase Order] InMovements --> TI[Transfer In] InMovements --> OI[Other In] OutMovements --> Sales[Sales] OutMovements --> TO[Transfer Out] OutMovements --> Waste[Expired/Waste] OutMovements --> OO[Other Out] PO --> Calculate[Calculate Final Stock] TI --> Calculate OI --> Calculate Sales --> Calculate TO --> Calculate Waste --> Calculate OO --> Calculate Calculate --> Final[Final = Initial + In - Out]
Movement Categories:
- Inbound: Purchase Order, Transfer In, Other In
- Outbound: Sales, Transfer Out, Expired/Waste, Other Out
- Calculation:
Final Stock = Before Audit + Total In - Total Out
7.3 Component Structure
7.3.1 Category List Component
Virtual list implementation for performance:
interface CategoryListProps {
categories: Category[];
skuDetails: SKUReportDetail[];
control: Control<InventoryReportForm>;
onRefresh: () => void;
isRefreshing: boolean;
}
// Virtual list configuration
const ESTIMATED_ITEM_HEIGHT = {
category: 48,
sku: 120,
expandedSku: 280
};7.3.2 SKU Card Component
Expandable cards with status indicators:
interface SKUCardState {
isExpanded: boolean;
status: 'default' | 'ok' | 'low-stock';
hasValue: boolean;
validationError?: string;
}7.4 Input Validation System
The module implements comprehensive validation:
graph TD Input[User Input] --> TypeCheck{Is Numeric?} TypeCheck -->|No| ShowError[Show Error] TypeCheck -->|Yes| RangeCheck{In Range?} RangeCheck -->|No| Clamp[Clamp Value] RangeCheck -->|Yes| BusinessRules{Business Rules?} BusinessRules -->|Fail| ShowWarning[Show Warning] BusinessRules -->|Pass| UpdateValue[Update Form] Clamp --> UpdateValue UpdateValue --> UpdateStatus[Update Status]
Validation Rules:
- Numeric only inputs
- Non-negative values
- Maximum value: 1,000,000,000
- Real-time validation feedback
- Business rule compliance
7.5 Auto-Save Implementation
Sophisticated auto-save mechanism ensures data persistence:
// Auto-save triggers
enum SaveTrigger {
APP_STATE_CHANGE = 'app_state_change',
BACK_BUTTON = 'back_button',
MANUAL_SAVE = 'manual_save',
NAVIGATION = 'navigation'
}
// Save flow
const autoSaveFlow = {
trigger: SaveTrigger,
validate: () => boolean,
serialize: () => string,
persist: () => Promise<void>,
sync: () => Promise<void>
};Key Features:
- Sub-50ms save performance
- Offline-first approach
- Conflict resolution
- Background sync
- Modal state awareness
7.6 Attachment Management
Media attachment support (permission-based):
graph LR AttachmentButton[Add Attachment] --> TypeSelect{Select Type} TypeSelect --> Photo[Take Photo] TypeSelect --> Video[Record Video] TypeSelect --> Note[Add Note] Photo --> Preview[Show Thumbnail] Video --> Preview Note --> TextDisplay[Show Text] Preview --> Delete[Delete Option]
Capabilities:
- Photo capture/selection
- Video recording
- Text annotations
- Thumbnail previews
- Batch operations
7.7 Progress Tracking
Real-time progress calculation:
interface ProgressMetrics {
totalSKUs: number;
modifiedSKUs: number;
completionPercentage: number;
lowStockCount: number;
pendingSaves: number;
}7.8 Performance Optimizations
-
Virtual List Rendering
- Renders only visible items
- Estimated heights for smooth scrolling
- Lazy accordion expansion
-
Memoization Strategy
- Component-level memoization
- Callback optimization
- Selective re-renders
-
State Management
- Atomic state updates
- Batch operations
- Debounced searches
-
Data Handling
- Incremental saves
- Compressed storage
- Efficient serialization
8. Review Screen
The Review screen provides comprehensive pre-submission validation and signature collection for inventory audits.
8.1 Screen Architecture
graph TB ReviewScreen[Review Screen] ReviewScreen --> SiteDetails[Site Details Card] ReviewScreen --> LowStock[Low Stock Alert] ReviewScreen --> Signatures[Signature Section] ReviewScreen --> Email[Email Recipients] ReviewScreen --> Submit[Submit Button] Signatures --> StandardSig[Standard Signature] Signatures --> SelfieSig[Selfie Signature] Submit --> ActionSheet[Review Action Sheet] ActionSheet --> Validation[Pre-Validation] Validation --> Upload[Upload Progress] Upload --> API[Submit to API] API --> Success[Navigate to Payoff]
8.2 Core Components
8.2.1 Site Details Card
Displays audit context information:
- Location name and address
- Schedule name and type
- Check-in timestamp
- Duration calculation
8.2.2 Low Stock Alert Section
Highlights inventory concerns:
- Count of low stock items
- Expandable list view
- SKU details with thresholds
- Visual warning indicators
8.2.3 Signature Management
Standard Signature Flow:
sequenceDiagram participant User participant SignatureCard participant SignaturePad participant Storage User->>SignatureCard: Tap to Sign SignatureCard->>SignaturePad: Open Modal User->>SignaturePad: Draw Signature SignaturePad->>Storage: Save Image Storage->>SignatureCard: Return URI SignatureCard->>User: Display Preview
Selfie Signature Flow:
sequenceDiagram participant User participant SelfieCard participant Camera participant ImageProcessor participant Form User->>SelfieCard: Tap to Capture SelfieCard->>Camera: Open Front Camera User->>Camera: Take Selfie Camera->>ImageProcessor: Process Image ImageProcessor->>Form: Update State Form->>SelfieCard: Display Thumbnail
8.3 Email Recipient Management
Dynamic email list configuration:
interface EmailTarget {
email: string;
enabled: boolean;
setByAdmin: boolean;
status: 'unsent' | 'sent' | 'failed';
}
// Email validation pattern
const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;Features:
- Add/remove recipients
- Email validation
- Duplicate prevention
- Admin-set recipients protection
8.4 Submission Validation Flow
flowchart TD Start[Start Submission] --> ConnCheck{Internet Connected?} ConnCheck -->|No| ShowOffline[Show Offline Error] ConnCheck -->|Yes| SigCheck{Signatures Valid?} SigCheck -->|No| ShowSigError[Show Signature Error] SigCheck -->|Yes| AttachCheck{Attachments Valid?} AttachCheck -->|No| ShowAttachModal[Show Error Modal] ShowAttachModal --> ForceSubmit{Force Submit?} ForceSubmit -->|No| Cancel[Cancel Submission] ForceSubmit -->|Yes| Upload AttachCheck -->|Yes| Upload[Upload Attachments] Upload --> Track[Track Progress] Track --> Submit[Submit Report] Submit -->|Success| Navigate[Go to Payoff] Submit -->|Error| ShowError[Show Error Toast]
8.5 Attachment Upload System
8.5.1 Upload Architecture
interface UploadStrategy {
endpoint: '/v1.0/miscellaneous/upload-file/attachment';
method: 'POST';
payload: FormData;
headers: {
'Content-Type': 'multipart/form-data';
};
}
interface UploadProgress {
current: number;
total: number;
percentage: number;
isUploading: boolean;
}8.5.2 Upload Categories
-
SKU Attachments
- Photos (JPEG/PNG)
- Videos (MP4/MOV)
- Compressed for performance
-
Signature Images
- Standard signatures
- Base64 to image conversion
- PNG format
-
Selfie Signatures
- Front camera captures
- Auto-orientation
- Quality optimization
8.6 Error Handling Strategy
graph TD Error[Error Detected] --> Type{Error Type} Type -->|Missing File| MissingModal[Show Missing Files] Type -->|Upload Failed| RetryUpload[Retry Upload] Type -->|API Error| ShowToast[Show Error Toast] Type -->|Validation| ValidationModal[Show Validation Errors] MissingModal --> UserChoice{User Action} UserChoice -->|Force Submit| ContinueSubmit[Submit Anyway] UserChoice -->|Cancel| StopSubmit[Cancel Process] RetryUpload -->|Success| Continue[Continue Flow] RetryUpload -->|Fail| LogError[Log and Continue]
8.7 Progress Tracking
Real-time upload feedback:
interface ProgressModal {
visible: boolean;
current: number;
total: number;
message: string;
// Auto-close after completion
autoCloseDelay: 1500; // ms
}8.8 State Management
Atomic state updates via Jotai:
// Core atoms
const uploadProgressAtom = atom<UploadProgress>({
current: 0,
total: 0,
percentage: 0,
isUploading: false
});
const attachmentErrorsAtom = atom<AttachmentError[]>([]);
const isSubmittingAtom = atom<boolean>(false);8.9 Submission Payload
Final report structure:
interface SubmissionPayload {
// Signatures
signatures: Array<{
name: string;
role: string;
attachmentID: string;
}>;
// Selfie signatures
selfieSignatures: Array<{
attachmentID: string;
}>;
// Email configuration
emailTargets: EmailTarget[];
// Inventory data
skuDetails: Array<{
skuID: string;
finalStockOnHand: number;
movements?: StockMovement[];
attachments?: Attachment[];
}>;
// Timestamps
checkInAt: Date;
checkOutAt: Date;
submittedAt: Date;
}8.10 Performance Considerations
-
Parallel Uploads
- Concurrent attachment uploads
- Batch processing
- Progress aggregation
-
Memory Management
- Image compression
- Cleanup after upload
- Efficient FormData usage
-
Error Recovery
- Graceful degradation
- Partial success handling
- Retry mechanisms
9. Payoff Screen
The Payoff screen serves as the success confirmation and summary dashboard after successful inventory submission.
9.1 Screen Architecture
graph TB PayoffScreen[Payoff Screen] PayoffScreen --> Animation[Success Animation] PayoffScreen --> Message[Success Message] PayoffScreen --> Summary[Summary Card] PayoffScreen --> CTA[Got It Button] Summary --> DateInfo[Date/Time Info] Summary --> Metrics[Audit Metrics] Summary --> LowStock[Low Stock Alert] LowStock --> BottomSheet[Low Stock Details] CTA --> Navigate[Navigate Home] Navigate --> InvalidateCache[Invalidate Schedules]
9.2 Component Structure
9.2.1 Success Animation
- Lottie animation for celebration
- Visual feedback for completion
- Engaging user experience
9.2.2 Summary Card
Comprehensive audit metrics display:
interface SummaryData {
// Visit Information
dateOfVisit: string; // Formatted date
timeOfVisit: string; // Formatted time with timezone
duration: string; // "2h 30m" format
// Audit Metrics
totalSkusChecked: number;
lowStockTotal: number;
// Site Context
siteName: string;
}9.3 Low Stock Management
sequenceDiagram participant User participant PayoffScreen participant LowStockRow participant BottomSheet participant FlatList PayoffScreen->>LowStockRow: Render if items > 0 User->>LowStockRow: Tap to view LowStockRow->>BottomSheet: Open modal BottomSheet->>FlatList: Render items FlatList->>User: Display SKU details
9.3.1 Low Stock Item Structure
interface LowStockSKU {
name: string;
productCode: string;
unitOfMeasure: string;
finalStockOnHand: number;
lowStockThreshold: number;
}
// Display format
const displayText = `${finalStockOnHand} ${unitOfMeasure}`;9.4 Data Flow Management
flowchart TD PrevScreen[Review Screen] --> SetData[Set Payoff Data] SetData --> Atom[payoffDataAtom] SetData --> URLParams[URL Query Params] PayoffScreen[Payoff Screen] --> CheckAtom{Atom Has Data?} CheckAtom -->|Yes| UseAtom[Use Atom Data] CheckAtom -->|No| CheckURL{URL Has Params?} CheckURL -->|Yes| UseURL[Parse URL Data] CheckURL -->|No| UseEmpty[Show Empty State] UseAtom --> RenderScreen[Render Summary] UseURL --> RenderScreen UseEmpty --> RenderScreen
9.5 Navigation Behavior
9.5.1 Exit Flow
const handleGotIt = () => {
// 1. Navigate to home
router.push('/home/inventory');
// 2. Invalidate cache
queryClient.invalidateQueries(['getSKUSchedules']);
// 3. Reset state
setIsSubmitting(false);
};9.5.2 Back Button Handling
- Android: Intercepts and redirects to inventory home
- iOS: Natural navigation (no back button)
- Prevents returning to submission flow
9.6 State Management
9.6.1 Primary State (Jotai Atom)
const payoffDataAtom = atom<FormattedReportData | null>(null);
interface FormattedReportData {
siteName: string;
checkInTime: Date;
checkOutTime: Date;
duration: string;
totalSkusChecked: number;
lowStockTotal: number;
lowStockSkus: LowStockSKU[];
}9.6.2 Fallback State (URL Parameters)
interface PayoffQueryParams {
siteName?: string;
checkInRaw?: string;
checkOutRaw?: string;
duration?: string;
totalSkusChecked?: string;
lowStockTotal?: string;
}9.7 UI/UX Features
9.7.1 Visual Design
- Background: Custom payoff gradient
- Card: Elevated with shadow effect
- Colors: Success green theme
- Animation: Celebratory Lottie
9.7.2 Typography
const textStyles = {
title: {
fontSize: 24,
fontWeight: 'bold',
color: '$color.green900'
},
label: {
fontSize: 14,
color: '$color.gray700'
},
value: {
fontSize: 16,
fontWeight: '600',
color: '$color.gray900'
}
};9.7.3 Accessibility
- Fixed font scaling for consistency
- High contrast text
- Clear visual hierarchy
- Screen reader support
9.8 Performance Optimizations
-
List Rendering
// Virtualized list for low stock items <FlatList data={lowStockSkus} keyExtractor={(item) => item.productCode} renderItem={renderItem} initialNumToRender={10} maxToRenderPerBatch={10} windowSize={10} /> -
Component Memoization
const LowStockItem = memo(({ item, index }) => { // Render logic }); -
State Reset
- Clears navigation params
- Resets form state
- Invalidates stale queries
9.9 Localization
All text content uses the localization system:
const LL = useI18n();
// Usage examples
LL.inventory.payoff.title()
LL.inventory.payoff.generatingReport()
LL.inventory.payoff.summary.dateOfVisit()
LL.inventory.payoff.gotIt()9.10 Success Metrics Display
The screen prominently displays:
- Temporal Data: When the audit occurred
- Efficiency Metric: Time spent on audit
- Completeness Metric: Total SKUs checked
- Alert Metric: Low stock items requiring attention
This comprehensive summary provides immediate value to users while celebrating their completed work.
10. API Endpoints
The inventory module integrates with backend services through a well-defined RESTful API structure.
10.1 Endpoint Summary Table
| Endpoint | Method | Purpose | File Location | Request/Response |
|---|---|---|---|---|
/v1.0/skus/[site](../Sites/SitesOverview.md)-skus/fetchSites/{userId} | GET | Fetch sites with SKU schedules | inventory-home-repository.ts | Returns site list with schedule info |
/v1.0/skus/[schedules](../Schedule/Schedule Listing/ScheduleListingOverview.md)/active | GET | Get active schedules for site | inventory-schedules-repository.ts | Query: siteId, date, timezone |
/v1.0/skus/[reports](../Reports/ReportsOverview.md) | POST | Create new SKU report | inventory-schedules-repository.ts | Body: scheduleId, date, timezone |
/v1.0/skus/[reports](../Reports/ReportsOverview.md)/{skuReportID} | GET | Fetch specific SKU report | inventory-schedules-repository.ts | Returns full report data |
/v1.0/skus/[reports](../Reports/ReportsOverview.md)/{skuReportID} | DELETE | Reset/delete SKU report | inventory-schedules-repository.ts | Removes draft report |
/v1.0/skus/compact | GET | Fetch SKUs in compact format | inventory-audit-repository.ts | Query: scheduleId |
/v1.0/skus/[reports](../Reports/ReportsOverview.md)/{reportID} | PATCH | Update SKU report (save draft) | inventory-audit-repository.ts | Body: partial report data |
/v1.0/skus/[reports](../Reports/ReportsOverview.md)/submit/{reportID} | POST | Submit completed report | inventory-review-repository.ts | Body: signatures, emails, SKU data |
/v1.0/miscellaneous/upload-file/attachment | POST | Upload attachments | upload-inventory-attachments.ts | FormData: file, type, metadata |
10.2 API Integration Flow
sequenceDiagram participant App participant API participant Backend App->>API: GET fetchSites/{userId} API->>Backend: Query user sites Backend-->>App: Return site list App->>API: GET schedules/active API->>Backend: Query active schedules Backend-->>App: Return schedules App->>API: POST reports API->>Backend: Create new report Backend-->>App: Return reportId App->>API: GET compact SKUs API->>Backend: Fetch SKU list Backend-->>App: Return SKUs App->>API: PATCH reports/{id} API->>Backend: Save draft Backend-->>App: Confirm save App->>API: POST upload attachment API->>Backend: Store file Backend-->>App: Return reference App->>API: POST submit/{id} API->>Backend: Process submission Backend-->>App: Confirm success
10.3 Error Handling
All API calls implement standardized error handling:
interface APIError {
statusCode: number;
message: string;
details?: Record<string, any>;
}
// Common error scenarios
- 400: Bad Request (validation errors)
- 401: Unauthorized (auth token expired)
- 404: Not Found (resource doesn't exist)
- 409: Conflict (duplicate submission)
- 500: Server Error (backend issues)11. Package Dependencies
11.1 Core Dependencies Table
| Package | Version | Purpose | Usage in Inventory Module |
|---|---|---|---|
| react | ^18.2.0 | Core React library | Component building, hooks, lifecycle |
| react-native | 0.72.x | Mobile framework | Platform-specific components |
| expo | ~49.0.0 | Development platform | Camera, media, file system |
| @tamagui/core | 1.x | UI framework | Cross-platform styling |
| jotai | ^2.x | State management | Global state atoms |
| @tanstack/react-query | ^4.x | Server state | API data fetching/caching |
| react-hook-form | ^7.x | Form management | Inventory form validation |
| dayjs | ^1.11.x | Date utilities | Timezone calculations |
| zod | ^3.x | Schema validation | Type-safe validation |
| @nimbly-technologies/nimbly-common | workspace:* | Shared types | Common interfaces/enums |
11.2 UI Component Libraries
| Package | Purpose | Key Usage |
|---|---|---|
| @my/ui | Internal component library | Buttons, cards, modals |
| @tamagui/lucide-icons | Icon set | UI icons throughout |
| @expo/vector-icons | Additional icons | Specialized icons |
| lottie-react-native | Animations | Success animations |
11.3 Specialized Libraries
| Package | Purpose | Specific Use Case |
|---|---|---|
| expo-av | Media playback | Video attachments preview |
| fuse.js | Fuzzy search | SKU search functionality |
| react-native-safe-area-context | Safe area handling | Proper screen boundaries |
| solito | Navigation utilities | Cross-platform routing |
11.4 Development Dependencies
| Package | Purpose |
|---|---|
| vitest | Unit testing framework |
| @testing-library/react | Component testing |
| @testing-library/react-hooks | Hook testing |
| typescript | Type safety |
12. State Management
The inventory module uses a hybrid state management approach optimized for different data types:
12.1 State Architecture
graph TB subgraph "Client State (Jotai)" FormState[Form Data] UIState[UI State] DraftState[Draft Reports] SearchState[Search Queries] end subgraph "Server State (React Query)" ScheduleData[Schedule Data] SKUData[SKU Lists] ReportData[Report Data] SubmissionState[Submission Status] end subgraph "Local Storage (MMKV)" PendingSaves[Pending Saves] DraftBackups[Draft Backups] end FormState --> PendingSaves ReportData --> DraftState ScheduleData --> UIState
12.2 Key Atoms (Jotai)
// Site list for home screen
export const siteListAtom = atom<GetSiteSKUScheduleListResponse[]>([]);
// Current draft report ID
export const draftReportIDAtom = atom<string | null>(null);
// Search query for SKU filtering
export const inventoryAuditSkuSearchAtom = atom<string>('');
// Review button disable state
export const isReviewDisabledAtom = atom<boolean>(false);
// Upload progress tracking
export const inventoryUploadProgressAtom = atom<UploadProgress>({
current: 0,
total: 0,
percentage: 0,
isUploading: false
});
// Attachment validation errors
export const inventoryAttachmentErrorListAtom = atom<AttachmentError[]>([]);
// Submission state
export const isSubmittingAtom = atom<boolean>(false);
// Payoff screen data
export const payoffDataAtom = atom<FormattedReportData | null>(null);12.3 React Query Keys
// Query key patterns
const queryKeys = {
siteSKUSchedules: ['getSiteSKUScheduleList', userId],
schedules: ['getSKUSchedules', siteId, date, timezone],
skuReport: ['getSKUReport', reportId],
skuList: ['getSKUs', scheduleId]
};
// Mutation keys
const mutationKeys = {
createReport: 'createSKUReport',
updateReport: 'updateSKUReport',
submitReport: 'submitSKUReport',
uploadFile: 'uploadFile'
};13. Data Flow
The inventory module implements a sophisticated data flow pattern that ensures data consistency and optimal performance.
Complete Data Flow Diagram
graph TB subgraph "User Journey" Start[User Opens App] --> Home[Home Screen] Home --> Schedules[Select Schedule] Schedules --> Audit[Count Inventory] Audit --> Review[Review & Sign] Review --> Submit[Submit Report] Submit --> Payoff[Success Screen] Payoff --> Home end subgraph "Data Operations" Home --> FetchSites[Fetch Sites API] Schedules --> FetchSchedules[Fetch Schedules API] Schedules --> CreateReport[Create Report API] Audit --> FetchSKUs[Fetch SKUs API] Audit --> SaveDraft[Save Draft Local/API] Review --> ValidateData[Validate All Data] Submit --> UploadFiles[Upload Attachments] Submit --> SubmitAPI[Submit Report API] end subgraph "State Updates" FetchSites --> UpdateSiteAtom[Update Site Atom] CreateReport --> UpdateDraftAtom[Update Draft Atom] SaveDraft --> UpdateFormState[Update Form State] UploadFiles --> UpdateProgress[Update Progress Atom] SubmitAPI --> UpdatePayoffAtom[Update Payoff Atom] end
Form Data Lifecycle
stateDiagram-v2 [*] --> Empty: Initial State Empty --> Loading: Fetch SKU Data Loading --> Loaded: Data Ready Loaded --> Editing: User Input Editing --> Validating: Real-time Validation Validating --> Valid: Pass Validating --> Invalid: Fail Valid --> Saving: Auto-save Trigger Invalid --> Editing: Fix Errors Saving --> Saved: Local Storage Saved --> Syncing: Online Sync Syncing --> Synced: API Updated Synced --> Editing: Continue Edit Synced --> Reviewing: Navigate Review Reviewing --> Submitting: Submit Report Submitting --> Submitted: Success Submitted --> [*]
14. Key Components
Inventory Form Component
The central form component that manages all SKU data entry:
// Location: packages/app/features/inventory/audit/_components/inventory-form.tsx
export const InventoryForm = ({
control,
skuReportDetails,
onRefresh,
isRefreshing,
categories
}: InventoryFormProps) => {
// Virtual list optimization for large SKU lists
const flatListRef = useRef<FlatList>(null);
// Search functionality
const searchQuery = useAtomValue(inventoryAuditSkuSearchAtom);
// Filtered and sectioned data
const { filteredSections, totalItems } = useMemo(() => {
// Complex filtering logic
return filterAndSectionSKUs(skuReportDetails, categories, searchQuery);
}, [skuReportDetails, categories, searchQuery]);
// Render optimizations
const renderItem = useCallback(({ item, index }) => {
// Memoized render logic
}, [control]);
return (
<VirtualList
ref={flatListRef}
data={filteredSections}
renderItem={renderItem}
keyExtractor={keyExtractor}
getItemLayout={getItemLayout}
// Performance props
removeClippedSubviews
maxToRenderPerBatch={10}
windowSize={10}
initialNumToRender={5}
/>
);
};Schedule List Item Component
Displays schedule status with sophisticated business logic:
// Location: packages/app/features/inventory/schedules/_components/schedule-list-item.tsx
export const ScheduleListItem = ({ schedule, onPress }: Props) => {
const { status, progress, actionText, isClickable } = useScheduleStatus(schedule);
return (
<TouchableOpacity
disabled={!isClickable}
onPress={() => onPress(schedule)}
style={[styles.container, getStatusStyle(status)]}
>
<StatusBadge status={status} />
<ScheduleInfo schedule={schedule} />
<ProgressBar progress={progress} />
<ActionButton text={actionText} status={status} />
</TouchableOpacity>
);
};
// Status calculation hook
const useScheduleStatus = (schedule: InventorySchedule) => {
return useMemo(() => {
const status = getScheduleStatusEnum(schedule);
const progress = calculateProgress(schedule);
const actionText = getActionText(status);
const isClickable = status !== ScheduleStatus.COMPLETED;
return { status, progress, actionText, isClickable };
}, [schedule]);
};Movement Input Sheet Component
Handles complex stock movement calculations:
// Location: packages/app/features/inventory/audit/_components/inventory-movement-input-sheet.tsx
export const InventoryMovementInputSheet = ({
isOpen,
onClose,
skuDetail,
control,
index
}: Props) => {
// Movement calculations
const calculateFinalStock = useCallback((movements: StockMovements) => {
const totalIn = movements.purchaseOrder +
movements.transferIn +
movements.otherIn;
const totalOut = movements.sales +
movements.transferOut +
movements.expired +
movements.otherOut;
return skuDetail.beforeAuditStockOnHand + totalIn - totalOut;
}, [skuDetail.beforeAuditStockOnHand]);
// Apply to all functionality
const handleApplyToAll = useCallback((category: MovementCategory, value: number) => {
const updates = skuDetails.map((sku, idx) => ({
index: idx,
category,
value
}));
batchUpdateMovements(updates);
}, [skuDetails]);
return (
<Sheet
modal
open={isOpen}
onOpenChange={onClose}
snapPoints={[80]}
dismissOnSnapToBottom
>
<Sheet.Frame>
<MovementHeader sku={skuDetail} />
<MovementInputs control={control} index={index} />
<FinalStockDisplay
value={calculateFinalStock(watchedMovements)}
threshold={skuDetail.lowStockThreshold}
/>
<ApplyToAllSection onApply={handleApplyToAll} />
</Sheet.Frame>
</Sheet>
);
};15. Business Logic
15.1 Schedule Status Calculation Engine
The core business logic for determining schedule status:
// Location: packages/app/features/inventory/schedules/utils/schedule-status.tsx
export const getScheduleStatusEnum = (
schedule: InventorySchedule,
timezone?: string
): ScheduleStatus => {
// Timezone resolution
const effectiveTimezone = schedule.timezone || timezone || 'UTC';
// Current time in schedule timezone
const now = getCurrentTimeInTimezone(effectiveTimezone);
const today = getDateInTimezone(effectiveTimezone);
// Specific days logic
if (hasSpecificDays(schedule)) {
return calculateSpecificDaysStatus(schedule, today, now);
}
// Frequency-based logic
return calculateFrequencyStatus(schedule, today, now);
};
const calculateSpecificDaysStatus = (
schedule: InventorySchedule,
today: string,
currentTime: number
): ScheduleStatus => {
const { scheduledDates } = schedule.specificDaysSchedule;
// Check completion
const hasCompleted = Object.values(scheduledDates).some(
date => date.status === 'completed'
);
if (hasCompleted) return ScheduleStatus.COMPLETED;
// Check today's status
const todaySchedule = scheduledDates[today];
if (todaySchedule) {
if (todaySchedule.draftReportID) return ScheduleStatus.IN_PROGRESS;
if (isInTimeWindow(schedule, currentTime)) return ScheduleStatus.NOT_STARTED;
return ScheduleStatus.OVERDUE;
}
// Check past dates
const hasPastDates = Object.keys(scheduledDates).some(
date => date < today && scheduledDates[date].status !== 'completed'
);
return hasPastDates ? ScheduleStatus.OVERDUE : ScheduleStatus.NOT_STARTED;
};Auto-Save Manager
Sophisticated auto-save implementation with conflict resolution:
// Location: packages/app/features/inventory/audit/_controllers/useAutoSave.ts
export const useAutoSave = (reportId: string) => {
const formData = useWatch();
const [isSaving, setIsSaving] = useState(false);
const saveTimeoutRef = useRef<NodeJS.Timeout>();
// Debounced save function
const debouncedSave = useMemo(
() => debounce(async (data: InventoryReportForm) => {
if (isSaving || !shouldSave(data)) return;
setIsSaving(true);
try {
// Save to local storage first
await saveToMMKV(reportId, data);
// Attempt API sync if online
if (isOnline()) {
await syncToAPI(reportId, data);
clearLocalSave(reportId);
}
} catch (error) {
console.error('Auto-save failed:', error);
} finally {
setIsSaving(false);
}
}, 3000),
[reportId, isSaving]
);
// Trigger save on data changes
useEffect(() => {
debouncedSave(formData);
return () => {
debouncedSave.cancel();
};
}, [formData, debouncedSave]);
// App state change handler
useAppStateChange((state) => {
if (state === 'background') {
debouncedSave.flush();
}
});
return { isSaving };
};Stock Validation Engine
Complex validation rules for inventory counts:
// Location: packages/app/features/inventory/audit/_services/inventory-audit-service.ts
export class InventoryAuditService {
static validateStockEntry(
value: string | number,
auditType: AuditType,
skuDetail: SKUReportDetail
): ValidationResult {
// Type validation
if (!isNumeric(value)) {
return {
isValid: false,
error: 'Value must be numeric',
sanitizedValue: null
};
}
const numericValue = Number(value);
// Range validation
if (numericValue < 0) {
return {
isValid: false,
error: 'Value cannot be negative',
sanitizedValue: 0
};
}
if (numericValue > MAX_STOCK_VALUE) {
return {
isValid: false,
error: `Value cannot exceed ${MAX_STOCK_VALUE}`,
sanitizedValue: MAX_STOCK_VALUE
};
}
// Business rule validation
if (auditType === AuditType.MOVEMENT) {
const movements = calculateTotalMovements(skuDetail);
const finalStock = skuDetail.beforeAuditStockOnHand + movements.in - movements.out;
if (finalStock < 0) {
return {
isValid: false,
error: 'Final stock cannot be negative',
sanitizedValue: numericValue
};
}
}
// Low stock warning
const warning = numericValue < skuDetail.lowStockThreshold
? 'Stock is below threshold'
: undefined;
return {
isValid: true,
sanitizedValue: numericValue,
warning
};
}
}16. Error Handling
16.1 Comprehensive Error Management
graph TD Error[Error Occurs] --> Type{Error Type} Type -->|Network| NetworkHandler[Network Error Handler] Type -->|Validation| ValidationHandler[Validation Handler] Type -->|Storage| StorageHandler[Storage Handler] Type -->|Media| MediaHandler[Media Handler] Type -->|API| APIHandler[API Error Handler] NetworkHandler --> Retry{Retry Available?} Retry -->|Yes| RetryQueue[Add to Retry Queue] Retry -->|No| ShowOffline[Show Offline Message] ValidationHandler --> ShowInline[Show Inline Error] StorageHandler --> Fallback[Use Fallback Storage] MediaHandler --> SkipMedia[Skip Media Upload] APIHandler --> ParseError[Parse Error Response] ParseError --> UserMessage[Show User Message] RetryQueue --> BackgroundSync[Background Sync]
Error Recovery Strategies
// Network Error Recovery
export const withNetworkRetry = async <T>(
operation: () => Promise<T>,
options: RetryOptions = {}
): Promise<T> => {
const { maxRetries = 3, backoff = 1000 } = options;
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await operation();
} catch (error) {
if (!isNetworkError(error) || attempt === maxRetries - 1) {
throw error;
}
await delay(backoff * Math.pow(2, attempt));
}
}
throw new Error('Max retries exceeded');
};
// Storage Error Recovery
export const withStorageFallback = async (
primary: () => Promise<void>,
fallback: () => Promise<void>
): Promise<void> => {
try {
await primary();
} catch (error) {
console.warn('Primary storage failed, using fallback:', error);
await fallback();
}
};
// Validation Error Display
export const displayValidationError = (
error: ValidationError,
toast: ToastController
): void => {
const message = error.field
? `${error.field}: ${error.message}`
: error.message;
toast.show({
type: 'error',
message,
duration: 3000,
position: 'top'
});
};17. Performance Optimizations
Rendering Optimizations
// Virtual List Implementation
export const OptimizedSKUList = memo(({ data, ...props }) => {
// Memoized sections
const sections = useMemo(() =>
data.reduce((acc, item) => {
const section = acc.find(s => s.categoryId === item.categoryId);
if (section) {
section.data.push(item);
} else {
acc.push({
categoryId: item.categoryId,
categoryName: item.categoryName,
data: [item]
});
}
return acc;
}, []),
[data]
);
// Optimized item layout
const getItemLayout = useCallback((data, index) => ({
length: ITEM_HEIGHT,
offset: ITEM_HEIGHT * index,
index
}), []);
// Render callbacks
const renderItem = useCallback(({ item }) => (
<SKUCard {...item} />
), []);
const keyExtractor = useCallback(
(item) => item.skuId,
[]
);
return (
<SectionList
sections={sections}
renderItem={renderItem}
keyExtractor={keyExtractor}
getItemLayout={getItemLayout}
// Performance settings
removeClippedSubviews
maxToRenderPerBatch={10}
updateCellsBatchingPeriod={50}
windowSize={10}
initialNumToRender={10}
{...props}
/>
);
});Memory Management
// Image compression and cleanup
export const useImageOptimization = () => {
const compressImage = useCallback(async (uri: string): Promise<string> => {
const compressed = await ImageManipulator.manipulateAsync(
uri,
[{ resize: { width: 1024 } }],
{ compress: 0.7, format: ImageManipulator.SaveFormat.JPEG }
);
// Clean up original if different
if (compressed.uri !== uri) {
await FileSystem.deleteAsync(uri, { idempotent: true });
}
return compressed.uri;
}, []);
const cleanupImages = useCallback(async (uris: string[]) => {
await Promise.all(
uris.map(uri =>
FileSystem.deleteAsync(uri, { idempotent: true })
.catch(console.warn)
)
);
}, []);
// Cleanup on unmount
useEffect(() => {
return () => {
// Cleanup temporary images
cleanupImages(tempImageRefs.current);
};
}, []);
return { compressImage, cleanupImages };
};18. Testing Strategy
Test Coverage Overview
graph LR Tests[Test Suite] --> Unit[Unit Tests] Tests --> Integration[Integration Tests] Tests --> E2E[E2E Tests] Unit --> Components[Component Tests] Unit --> Hooks[Hook Tests] Unit --> Utils[Utility Tests] Unit --> Services[Service Tests] Integration --> API[API Integration] Integration --> State[State Management] Integration --> Navigation[Navigation Flow] E2E --> UserFlows[User Workflows] E2E --> ErrorScenarios[Error Scenarios]
Testing Examples
// Component Testing
describe('InventoryForm', () => {
it('should render SKU list correctly', () => {
const { getByTestId } = render(
<InventoryForm
control={mockControl}
skuReportDetails={mockSKUs}
categories={mockCategories}
/>
);
expect(getByTestId('sku-list')).toBeTruthy();
expect(getByTestId('sku-item-0')).toHaveTextContent('Test SKU');
});
it('should handle search filtering', async () => {
const { getByTestId, queryByTestId } = render(<InventoryForm {...props} />);
const searchInput = getByTestId('search-input');
await userEvent.type(searchInput, 'specific sku');
await waitFor(() => {
expect(queryByTestId('sku-item-filtered')).toBeTruthy();
expect(queryByTestId('sku-item-other')).toBeFalsy();
});
});
});
// Service Testing
describe('InventoryAuditService', () => {
describe('validateStockEntry', () => {
it('should reject negative values', () => {
const result = InventoryAuditService.validateStockEntry(
-5,
AuditType.OPNAME,
mockSKU
);
expect(result.isValid).toBe(false);
expect(result.error).toBe('Value cannot be negative');
});
it('should warn on low stock', () => {
const lowStockSKU = { ...mockSKU, lowStockThreshold: 100 };
const result = InventoryAuditService.validateStockEntry(
50,
AuditType.OPNAME,
lowStockSKU
);
expect(result.isValid).toBe(true);
expect(result.warning).toBe('Stock is below threshold');
});
});
});
// Integration Testing
describe('Inventory Audit Flow', () => {
it('should complete full audit workflow', async () => {
const { navigate } = renderApp();
// Navigate to schedules
await navigate('/inventory/schedules');
// Select schedule
const schedule = await screen.findByTestId('schedule-item-0');
await userEvent.press(schedule);
// Fill audit form
const skuInput = await screen.findByTestId('sku-input-0');
await userEvent.type(skuInput, '100');
// Navigate to review
const reviewBtn = await screen.findByTestId('review-button');
await userEvent.press(reviewBtn);
// Add signature
const signatureBtn = await screen.findByTestId('signature-button');
await userEvent.press(signatureBtn);
// Submit
const submitBtn = await screen.findByTestId('submit-button');
await userEvent.press(submitBtn);
// Verify success
await waitFor(() => {
expect(screen.getByText('Report Submitted!')).toBeTruthy();
});
});
});19. Troubleshooting Guide
Common Issues and Solutions
1. Schedule Status Incorrect
Problem: Schedule shows wrong status (e.g., NOT_STARTED when should be OVERDUE)
Root Cause: Timezone mismatch between client and server
Solution:
// Ensure timezone is properly set
const effectiveTimezone = schedule.timezone || site.timezone || 'UTC';
// Always use timezone-aware functions
import { getDateInTimezone, getCurrentTimeInMinutesInTimezone } from './date-helpers';
const now = getCurrentTimeInMinutesInTimezone(effectiveTimezone);
const today = getDateInTimezone(effectiveTimezone);2. Auto-Save Not Working
Problem: Form data not persisting when app backgrounded
Root Cause: Modal open state blocking save
Solution:
// Check modal state before save
const canSave = !isModalOpen && !isSubmitting && hasChanges;
if (canSave) {
await saveFormData();
}3. Large SKU Lists Lagging
Problem: Performance issues with 300+ SKUs
Root Cause: Rendering all items at once
Solution:
// Use virtual list with proper configuration
<FlatList
data={skus}
getItemLayout={(data, index) => ({
length: ITEM_HEIGHT,
offset: ITEM_HEIGHT * index,
index,
})}
removeClippedSubviews
maxToRenderPerBatch={10}
windowSize={10}
initialNumToRender={5}
/>4. Attachment Upload Failures
Problem: Photos/videos failing to upload
Root Cause: File size or corrupted files
Solution:
// Compress before upload
const compressedUri = await ImageManipulator.manipulateAsync(
uri,
[{ resize: { width: 1024 } }],
{ compress: 0.7, format: 'jpeg' }
);
// Validate file exists
const fileInfo = await FileSystem.getInfoAsync(uri);
if (!fileInfo.exists) {
throw new Error('File not found');
}Debugging Techniques
Enable Debug Logging
// Add to environment config
export const DEBUG_INVENTORY = __DEV__ && true;
// Use in code
if (DEBUG_INVENTORY) {
console.log('[Inventory](../Inventory/InventoryOverview.md)', 'Schedule status:', status);
console.log('[Inventory](../Inventory/InventoryOverview.md)', 'Timezone:', effectiveTimezone);
console.log('[Inventory](../Inventory/InventoryOverview.md)', 'Current time:', currentTime);
}React Query DevTools
// In development, add DevTools
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
if (__DEV__) {
return (
<>
<App />
<ReactQueryDevtools />
</>
);
}Performance Profiling
// Use React DevTools Profiler
import { Profiler } from 'react';
<Profiler id="InventoryForm" onRender={onRenderCallback}>
<InventoryForm {...props} />
</Profiler>
// Callback to measure performance
const onRenderCallback = (id, phase, actualDuration) => {
console.log(`${id} (${phase}) took ${actualDuration}ms`);
};Security Considerations
-
Data Validation
// Always validate user input const sanitizedValue = DOMPurify.sanitize(userInput); // Validate against schema const result = inventorySchema.safeParse(data); if (!result.success) { throw new ValidationError(result.error); } -
API Security
- Use HTTPS for all requests
- Include authentication tokens
- Validate response data
- Handle token expiration
-
Local Storage Security
- Encrypt sensitive data
- Clear data on logout
- Use secure storage APIs
- Implement data expiration
Accessibility
-
Screen Reader Support
<TouchableOpacity accessible accessibilityRole="button" accessibilityLabel={`Schedule ${schedule.name}, Status: ${status}`} accessibilityHint="Double tap to open schedule" > -
Keyboard Navigation
- Support Tab navigation
- Provide keyboard shortcuts
- Handle focus management
- Show focus indicators
-
Visual Accessibility
- Use high contrast colors
- Support dynamic text sizing
- Provide alternative text
- Avoid color-only indicators
20. Migration Guide
Upgrading from Legacy System
-
Data Migration
// Transform legacy data format const migrateLegacySchedule = (legacy: LegacySchedule): InventorySchedule => { return { scheduleID: legacy.id, scheduleType: mapLegacyType(legacy.type), timezone: legacy.tz || 'UTC', // ... map other fields }; }; -
API Compatibility
- Use version headers
- Handle deprecated endpoints
- Implement response transformers
- Maintain backwards compatibility
-
State Migration
- Clear old cache keys
- Transform stored data
- Update atom structures
- Migrate user preferences
22. Conclusion
The Inventory Module represents a sophisticated, production-ready solution for retail inventory management. Built with modern React Native technologies and following clean architecture principles, it provides a robust foundation for inventory auditing workflows.
Key Achievements
-
User Experience
- Intuitive guided workflow
- Real-time validation
- Offline capability
- Performance optimization
-
Technical Excellence
- Clean architecture
- Comprehensive testing
- Type safety
- Scalable design
-
Business Value
- Reduced audit time
- Improved accuracy
- Real-time reporting
- Multi-location support
Support and Resources
- Documentation: This comprehensive guide
- Source Code: GitHub Repository
- [Issue Tracking](../Issue Tracker/IssueTrackerOverview.md): GitHub [Issues](../Issue Tracker/IssueTrackerOverview.md)
- Team Contact: Development team via Slack