# Business Rules - BuyerKiosk Live

This document describes the core business rules and domain logic implemented in the BuyerKiosk Live Flutter application.

---

## Permission System

### Access Levels

Access levels are derived from the employee `role` field returned by the API verification endpoint. Lower numeric values indicate higher privilege.

| Level | Value | Description |
|-------|-------|-------------|
| Owner | 1 | Full access to all features including permission configuration |
| Manager | 2 | Access to management features like editing task lists |
| Shift Lead | 3 | Access to performance metrics and shift notes |
| Employee | 4 | Basic access to queues and daily tasks |

### Permission Check Logic

```
user.accessLevel.value <= requiredLevel.value
```

A user can access a resource if their access level value is less than or equal to the required level value. For example:
- Owner (1) can access Manager (2) resources: `1 <= 2` = true
- Employee (4) cannot access Shift Lead (3) resources: `4 <= 3` = false

### Default Page Permissions

| Page | Default Required Level | Description |
|------|----------------------|-------------|
| Dashboard | Employee (4) | Store list overview |
| Store Detail | Employee (4) | Individual store metrics |
| Buy Queue | Employee (4) | Current queue items |
| Completed Buys | Employee (4) | Completed transactions |
| Today's Tasks | Employee (4) | Daily task completion |
| Buyer Stats | Shift Lead (3) | Buyer performance metrics |
| Store Metrics | Shift Lead (3) | Daily KPI dashboard |
| Shift Notes | Shift Lead (3) | Workbook announcements |
| Edit Task List | Manager (2) | Task definition management |
| Backstock Admin | Manager (2) | Backstock management |
| Settings | Owner (1) | API key and app configuration |
| Permission Settings | Owner (1) | Role-based access customization |

### Custom Permission Configuration

Owners can customize page access requirements via the Permission Settings screen. Custom configurations are stored locally and override the defaults.

---

## Queue Management

### Queue Item States

A queue item (buy in progress) has two derived states based on container processing:

| State | Condition | Description |
|-------|-----------|-------------|
| Not Started | `processedContainers == 0` | Buy has not begun processing |
| In Progress | `0 < processedContainers < numContainers` | Buy is being processed |
| Completed | `processedContainers >= numContainers` | All containers processed |

### Progress Calculation

```
progressPercentage = (processedContainers / numContainers) * 100
remainingContainers = numContainers - processedContainers
```

Division by zero is guarded: if `numContainers == 0`, progress returns 0.

### Wait Time Tracking

Wait time is provided by the API as a formatted string (e.g., "45m", "1h 30m"). This is displayed directly without client-side parsing.

### Customer Location

The `inStore` boolean indicates whether the customer is physically present in the store while waiting.

---

## Task Management

### Task Definitions

Task definitions are templates that define recurring tasks for the store. They are managed through the Edit Task List screen.

#### Priority Levels

| Priority | Value | Color | Description |
|----------|-------|-------|-------------|
| High | 1 | Red | Urgent tasks requiring immediate attention |
| Normal | 2 | Purple | Standard priority tasks |
| Low | 3 | Grey | Tasks that can be deferred |

#### Recurrence Days

Tasks can be scheduled to recur on specific days of the week using day codes:

| Day Code | Day |
|----------|-----|
| MON | Monday |
| TUE | Tuesday |
| WED | Wednesday |
| THU | Thursday |
| FRI | Friday |
| SAT | Saturday |
| SUN | Sunday |

Day codes are stored as a space-separated string (e.g., `"MON TUE WED THU FRI"`).

#### Active Today Check

A task is active today if the current weekday matches one of its recurrence day codes:

```
isActiveToday = recurDays.contains(currentDayCode)
```

#### Date Range Visibility

Tasks can have optional `startDate` and `endDate` fields for seasonal or temporary tasks:
- If `startDate` is set, the task is only shown on or after that date
- If `endDate` is set, the task is only shown on or before that date
- Both are optional and independent

#### Time of Day

The optional `timeOfDay` field provides a recommended time for task completion, displayed as a hint to employees.

---

### Daily Task Completion

Daily task completion tracks the status of tasks for the current day. Tasks are organized into lists (e.g., OPENING, CLOSING) with individual progress tracking.

#### Task Status Enum

| Status | Value | Description |
|--------|-------|-------------|
| Not Started | 0 | Task has not been started |
| In Progress | 1 | Task is currently being worked on |
| Completed | 2 | Task has been finished |

#### Status Determination

```dart
isNotStarted = status == null || status == TaskStatus.notStarted
isInProgress = status == TaskStatus.inProgress
isCompleted = status == TaskStatus.completed
```

#### Carryover Rule

Incomplete tasks from earlier lists carry forward to the current active list:

- When fetching the active list, the API returns both the current list's tasks and any `carryoverTasks`
- Carryover tasks are identified by non-null `carryoverFromList` and `carryoverFromListId` fields
- Carryover tasks are displayed with special highlighting (red) to indicate urgency
- The `originalStartTime` field indicates when the task was originally scheduled

#### Employee Attribution

When a task is completed:
- `completedBy` stores the employee ID who marked it complete
- `completedAt` stores the timestamp of completion
- `completedByFirstName` and `completedByLastName` provide display name
- `completionNotes` stores any optional notes added during completion

#### Progress Calculation

```
completionPercentage = (completedCount / totalCount) * 100
incompleteCount = totalCount - completedCount
```

---

## Workbook Notes

Workbook notes are announcements and communications posted within a store's workbook.

### Visibility Rule

A note is visible if the current time falls within its date range:

```dart
isVisible = (now >= startDate) && (endDate == null || now < endDate)
```

- `startDate` is required and determines when the note becomes visible
- `endDate` is optional; if null, the note remains visible indefinitely
- Notes can be scheduled for future visibility by setting a future `startDate`

### Manager-Only Flag

The `isManagerOnly` boolean restricts note visibility:
- When `true`, only users with Manager or higher access level can view the note
- When `false`, all users can view the note

### Pinned Notes

The `isPinned` boolean determines display priority:
- Pinned notes appear at the top of the notes list
- Pinned notes may have special visual styling

### Reaction Types

Notes support two reaction types:

| Type | Description |
|------|-------------|
| `like` | Standard acknowledgment reaction |
| `heart` | Stronger appreciation reaction |

#### Reaction Data Structure

```dart
reactionCounts: Map<String, int>  // e.g., {"like": 5, "heart": 3}
reactionNames: Map<String, List<String>>  // e.g., {"like": ["John", "Jane"]}
userHasReacted: bool  // Whether current user has any reaction
```

#### Reaction Counts

```dart
likeCount = reactionCounts['like'] ?? 0
heartCount = reactionCounts['heart'] ?? 0
totalReactionCount = reactionCount  // Sum of all reactions
```

### Comment Tracking

Notes track the number of comments via `commentCount`. Full comment data is fetched separately when viewing note details.

### Author Attribution

Notes store author information in multiple fields:
- `authorEmployeeId`: Links to employee record (nullable for external authors)
- `authorFirstName`, `authorLastName`: Parsed name components
- `authorName`: Display name (always populated)

---

## Store Data

### TypeNum Normalization

The `typeNum` field is a unique store identifier. A special case exists for demo/test stores:

```dart
normalizedTypeNum = (typeNum == 'PC80000') ? 'PC00' : typeNum
```

This mapping is used when constructing URLs and API requests.

### Goal Progress Calculations

Progress values are clamped between 0.0 and 1.0 to prevent visual overflow:

```dart
salesProgress = (salesCurrent / salesGoal).clamp(0.0, 1.0)
buyProgress = (buyCurrent / buyGoal).clamp(0.0, 1.0)
```

Percentage values (for display) are not clamped and can exceed 100%:

```dart
salesPercentage = (salesCurrent / salesGoal) * 100
buysPercentage = (buyCurrent / buyGoal) * 100
```

Division by zero is guarded: if goal is 0, progress/percentage returns 0.

### Completed Buy Metrics

For completed transactions:

```dart
avgTimePerContainer = processTime / numContainers
```

Returns 0 if `numContainers == 0`.

### Store Detail Metrics

Store detail tracks completion percentage:

```dart
completionPercentage = (numCompleted / totalBuys) * 100
```

Returns 0 if `totalBuys == 0`.

---

## Date and Time Formatting

### Relative Time Display

Notes use relative time formatting for recency:

| Condition | Format |
|-----------|--------|
| < 1 minute | "Just now" |
| < 1 hour | "{n}m ago" |
| < 24 hours | "{n}h ago" |
| Yesterday | "Yesterday" |
| < 7 days | "{n}d ago" |
| >= 7 days | "M/D/YYYY" |

---

## API Data Type Handling

### Numeric String Parsing

The API may return numeric values as strings. All numeric fields use defensive parsing:

```dart
double parseDouble(dynamic value) {
  if (value == null) return 0.0;
  if (value is num) return value.toDouble();
  if (value is String) return double.tryParse(value) ?? 0.0;
  return 0.0;
}
```

This applies to metrics like `current`, `goal`, and calculated fields.

---

## Source Files

The business rules documented here are implemented in the following domain entity files:

| File | Entity |
|------|--------|
| `/lib/core/constants/permission_constants.dart` | AccessLevel, AppPage, DefaultPermissions |
| `/lib/domain/entities/queue_item.dart` | QueueItem |
| `/lib/domain/entities/task.dart` | Task |
| `/lib/domain/entities/workbook_task_list.dart` | TaskStatus, WorkbookTaskItem, WorkbookTaskList |
| `/lib/domain/entities/workbook_note.dart` | ReactionType, WorkbookNote |
| `/lib/domain/entities/store.dart` | Store |
| `/lib/domain/entities/store_detail.dart` | StoreDetail |
| `/lib/domain/entities/completed_buy.dart` | CompletedBuy |
