| ← Back to Main Documentation | Core Systems Index |
Object Status System
Overview
The Plings system supports multiple simultaneous statuses for ObjectInstance documents. This allows for nuanced tracking of an object’s condition and availability state. For example, an object can be both “for_sale” and “broken”, indicating that it’s available for purchase as a repair item.
Supported Status Types
| Status | Description | Typical Use Cases | Color |
|---|---|---|---|
active |
Object is in normal use and under the owner’s physical control | Daily operations, personal belongings | Green |
for_sale |
Available for purchase while still in custody | Marketplace listings, spare parts | Blue |
reserved |
A buyer has a temporary reservation | Click-&-collect, deposit taken | Light Blue |
under_auction |
Listed in an auction until bidding closes | Online or physical auction house | Light Blue |
pending_transfer |
Sale agreed; paperwork or payment pending | Waiting for payment or contract signing | Light Blue |
sold |
Ownership has permanently transferred | Completed sale, invoice issued | Gray-Green |
donated |
Given away for free to another party | Charity donations, giveaways | Gray-Green |
lent_out |
Temporarily loaned, ownership retained | Lending to friends, internal department loan | Yellow-Green |
rented_out |
Temporarily leased for a fee, ownership retained | Equipment rental, tool library | Yellow-Green |
consigned |
Held by a reseller on consignment | Second-hand shop, art gallery | Yellow-Green |
on_display |
On exhibit at a partner venue/showroom | Museum loan, showroom model | Yellow-Green |
in_storage |
Stored off-site or in long-term storage | Warehouse, self-storage | Yellow-Green |
in_transit |
With a carrier / on the move | Courier shipment, freight forwarding | Orange |
awaiting_pickup |
Packed and ready for carrier or customer | Click-&-collect counter, loading dock | Orange |
returned_inbound |
Being returned back to the owner | Product returns, failed delivery | Orange |
lost |
Misplaced; location unknown but no theft suspicion | Lost & found workflow, insurance | Yellow |
stolen |
Confirmed or suspected theft | Police report, insurance | Red |
broken |
Not functioning properly | Repair requests, parts salvage | Orange-Red |
in_service |
Under routine maintenance / calibration | Scheduled service, inspection | Orange-Green |
in_repair |
Under repair due to a fault | Workshop, service center | Orange-Red |
under_warranty_service |
At manufacturer for warranty repair | Manufacturer RMA | Orange-Red |
refurbished |
Completed repair/refit; QC pending | Refurb workflow | Green-Blue |
quarantined |
Isolated due to contamination or defect suspicion | Bio-hazard, recalls | Purple |
recycled |
Handed to recycler for material recovery | E-waste recycling, scrap metal | Gray |
discarded |
Thrown away with no recycling process | Landfill disposal | Gray |
dismantled |
Object has been disassembled into parts; original assembly no longer exists | Part-out workflows, salvage operations | Dark Gray |
destructed |
Physically destroyed (shredded, burned, etc.) | Secure data destruction, end-of-life | Black |
About active
active is the baseline status meaning the object is operational, present, and under control of the current owner. Certain negative or terminal conditions automatically disqualify active (see rules below).
Status Combinations
Mutually Exclusive Status Rules
The backend validator will reject combinations that break real-world logic. Key rules:
activecannot coexist with any of:lost,stolen,broken,destructed,dismantled,sold,donated,recycled,discarded,in_repair,under_warranty_service,quarantined.destructed,recycled,discarded, ordismantledare terminal – they cannot be combined with any other status.sold/donatedare mutually exclusive withfor_sale,reserved,under_auction, andpending_transfer.lostandstolencan coexist during investigation but cannot be combined with any custody-related statuses (in_transit,in_storage, etc.).- Only one external-custody status from the set {
lent_out,rented_out,consigned,on_display,in_service,in_repair,under_warranty_service} may be present at a time. in_service,in_repair, andunder_warranty_serviceare mutually exclusive withbroken(the repair process replaces the “broken” flag).
These rules are enforced in the service layer. The UI presents invalid combinations as disabled choices, and the API returns 409 Conflict if violated.
Common Multi-Status Scenarios
["for_sale", "broken"]: Selling broken items for parts or repair["lost", "stolen"]: Unclear if item is lost or stolen (investigation phase)["active", "for_sale"]: Selling functional items while still using them["broken", "for_sale", "dismantled"]: Parent object has been disassembled; parts are being sold (note:dismantledis on parent only, not on parts)
Data Storage
Neo4j Graph Database
In Neo4j, statuses are stored as a list of strings in the statuses property on an :ObjectInstance node.
MATCH (o:ObjectInstance {id: 'example-123'})
SET o.statuses = ["for_sale", "broken"]
RETURN o
Supabase Storage
Statuses are stored as JSON strings in the PostgreSQL status column:
UPDATE object_instances
SET status = '["for_sale", "broken"]'
WHERE neo4j_id = 'example-123';
Frontend Implementation
Status Data Loading
The frontend should load status definitions on application startup:
// Load status definitions once on app init
const { data: statusDefinitions } = useQuery(GET_STATUS_DEFINITIONS);
// Create lookup maps for efficient access
const statusByKey = useMemo(() =>
statusDefinitions?.statusDefinitions.reduce((acc, status) => {
acc[status.key] = status;
return acc;
}, {}) || {}, [statusDefinitions]
);
Status Selection UI
- Edit Form: Multi-select checkboxes for status selection
- Validation: Use GraphQL validation query before submission
- Visual: Each status displays as a colored badge using backend-provided colors
- Categories: Group statuses by category for better UX
- Disabled States: Use
conflictsWithdata to disable incompatible options
Status Display
- Object Cards: Multiple status badges with appropriate colors from backend
- Badge Format: Use
namefield from status definitions (properly formatted) - Layout: Badges wrap to multiple lines if needed
- Tooltips: Show
descriptionfrom status definitions on hover
Status Validation
Implement client-side validation using the GraphQL endpoint:
const validateStatuses = async (statusKeys) => {
const { data } = await validateStatusCombination({
variables: { statusKeys }
});
return data.validateStatusCombination;
};
API Interface
GraphQL Status Queries
The backend provides comprehensive GraphQL queries for status management:
Get All Status Definitions
query GetStatusDefinitions {
statusDefinitions {
key
name
description
color
category
isTerminal
conflictsWith
requiresActiveFalse
}
}
Get Status Categories
query GetStatusCategories {
statusCategories
}
Get Statuses by Category
query GetCommerceStatuses {
statusesByCategory(category: "commerce") {
key
name
description
color
}
}
Validate Status Combination
query ValidateStatusCombination {
validateStatusCombination(statusKeys: ["for_sale", "broken"]) {
valid
errors
warnings
}
}
Update Object Statuses
mutation UpdateObjectStatuses($objectId: ID!, $statusKeys: [String!]!) {
updateObjectStatuses(objectId: $objectId, statusKeys: $statusKeys) {
id
name
statuses
owner
location
description
}
}
Create Object
const formData = new FormData();
formData.append('name', 'My Object');
formData.append('statuses', JSON.stringify(['active', 'for_sale']));
Update Object
const formData = new FormData();
formData.append('statuses', JSON.stringify(['broken', 'for_sale']));
Response Format
{
"id": "ObjectInstance/abc123",
"name": "My Object",
"statuses": ["broken", "for_sale"],
"message": "Object updated successfully"
}
Backend Implementation
Status Definitions Storage
Status definitions are centrally managed in app/statuses.py with comprehensive metadata:
@dataclass
class StatusDefinition:
key: str # Status identifier (e.g., "for_sale")
name: str # Display name (e.g., "For Sale")
description: str # Human-readable description
color: str # UI color identifier
category: str # Grouping category
is_terminal: bool = False # Whether this is an end-of-life status
conflicts_with: List[str] = None # Mutually exclusive statuses
requires_active_false: bool = False # Cannot coexist with "active"
Status Categories
Statuses are organized into logical categories:
operational- Normal operational statescommerce- Sale, auction, transfer statescustody- External custody situationstransit- Movement and shipping statesproblem- Lost, stolen, broken statesservice- Maintenance and repair statesend-of-life- Terminal disposal states
Validation Logic
The backend provides validation for status combinations:
def validate_status_combination(status_keys: List[str]) -> Dict[str, Any]:
"""Validate a combination of status keys and return validation result"""
# Check conflicts with "active" status
# Check direct conflicts between statuses
# Check for multiple terminal statuses
# Return validation result with errors and warnings
GraphQL Resolvers
Status-related queries and mutations are handled by dedicated resolvers in app/status_resolvers.py:
resolve_status_definitions()- Returns all available statusesresolve_status_categories()- Returns category listresolve_statuses_by_category()- Filters by categoryresolve_validate_status_combination()- Validates combinationsresolve_update_object_statuses()- Updates object statuses (mutation)
Status Update Operations
The updateObjectStatuses mutation handles all status modifications:
- Authentication: Verifies user permissions for the target object
- Validation: Uses
validate_status_combination()to check business rules - Dual Updates: Updates both Neo4j (
o.statuses) and Supabase (statuscolumn) - Error Handling: Provides detailed error messages for invalid operations
- Ownership Check: Ensures user owns object or belongs to owning organization
Mutation Behavior
- Atomic Operation: Status updates are validated before any database changes
- Permission-Based: Only object owners/organization members can modify statuses
- Graceful Degradation: If Supabase update fails, Neo4j update still succeeds (with warning)
- Real-time Response: Returns updated object data immediately after successful update
Backend Processing
Status Parsing
The backend handles both old single-status and new multi-status formats:
# Parse statuses from JSON string
try:
statuses_list = json.loads(statuses)
if not statuses_list:
statuses_list = ["active"] # Default fallback
except (json.JSONDecodeError, TypeError):
statuses_list = ["active"] # Default fallback
Database Updates
- Neo4j: Stores as a native list property on the
:ObjectInstancenode. - Supabase: Stores as a JSON string in the
statuscolumn.
Migration Strategy
Backward Compatibility
The system gracefully handles existing objects with single-status format:
// Old format support
if (obj.status && !obj.statuses) {
statuses = [obj.status];
} else if (obj.statuses && Array.isArray(obj.statuses)) {
statuses = obj.statuses;
} else {
statuses = ['active']; // Fallback
}
Migration Process
- No Data Migration Required: Existing single-status objects work automatically
- Gradual Adoption: Users can update objects to multi-status through the edit interface
- Legacy Support: Old single-status format continues to work indefinitely
Use Cases
1. Marketplace for Broken Items
Scenario: User wants to sell a broken laptop for parts
- Statuses:
["broken", "for_sale"] - Search: Buyers can filter for broken items specifically
- Display: Clear indication that item is broken but available
2. Theft Investigation
Scenario: Unclear if bike is lost or stolen
- Initial:
["lost"] - Update:
["lost", "stolen"]during investigation - Final:
["stolen"]after police confirmation
3. Functional Items for Sale
Scenario: Selling working items while still using them
- Statuses:
["active", "for_sale"] - Transition: Remove
"active"when sold and ownership transfers
4. End-of-Life Tracking
Scenario: Item is recycled or disposed of
- Statuses:
["destructed"] - Effect: Cannot be marked
for_sale(future business logic) - Purpose: Environmental tracking, warranty void confirmation
Future Enhancements
Business Logic Rules
- Status Validation: Implement mutually exclusive status combinations
- Automatic Transitions: Auto-remove incompatible statuses
- Workflow Triggers: Status changes trigger notifications/actions
Analytics and Reporting
- Status Distribution: Track status combinations across object types
- Lifecycle Analysis: Monitor status transition patterns
- Marketplace Metrics: Analyze for_sale status effectiveness
Enhanced UI Features
- Bulk Status Updates: Change status for multiple objects
- Status History: Track status changes over time
- Smart Suggestions: Recommend status combinations based on context
Implementation Notes
Performance Considerations
- Database Indexing: Consider GIN indexes on JSON status fields for PostgreSQL
- API Efficiency: Batch status updates for bulk operations
- Caching: Cache status color mappings for UI performance
Security Considerations
- Ownership Validation: Only owners can change object statuses
- Audit Trail: Log all status changes for security/compliance
- Fraud Prevention: Monitor suspicious status change patterns
API Benefits
The new GraphQL status API provides several key advantages:
Centralized Status Management
- Single Source of Truth: All status definitions managed in backend code
- Consistent Metadata: Colors, descriptions, and categories defined once
- Easy Updates: Adding new statuses requires only backend changes
Enhanced Frontend Experience
- Dynamic UI: Frontend automatically adapts to new status definitions
- Smart Validation: Real-time validation prevents invalid status combinations
- Rich Metadata: Colors, descriptions, and categories improve UX
Developer Experience
- Type Safety: GraphQL provides strongly-typed status operations
- Self-Documenting: Status definitions include comprehensive metadata
- Easy Integration: Simple queries provide all necessary frontend data
This multi-status system provides the foundation for a sophisticated object lifecycle management system while maintaining simplicity for basic use cases.