Skip to main content

TREC Lifecycle

Overview

TREC (Transaction Record) is a system-managed field that tracks the lifecycle state of every record in Q01 Core APIs. TREC values indicate whether a record is new, modified, cancelled (soft deleted), or in a special processing state.

TREC States:

  • N - New (just created, never modified)
  • M - Modified (updated at least once)
  • C - Cancelled (soft deleted, excluded from queries by default)
  • P - Processed (special workflow state, rarely used)

Key Principle: TREC is automatically managed by CoreWrite. Applications should never manually set TREC values.


State Definitions

N - New

Meaning: Record was just created and has never been updated

Set By: POST (create operation)

Characteristics:

  • ✅ First state in lifecycle
  • ✅ Indicates pristine record
  • ✅ Visible in standard queries (TREC != 'C')
  • ✅ Can transition to M (via PUT/PATCH) or C (via DELETE)

Automatic Fields Set:

{
"TREC": "N",
"CDATA": "20251219153045", // Creation timestamp
"OWNER": "user@example.com" // Creator
}

Example:

POST /api/v4/core/PRD
{
"data": {
"XPRD01": "New Product",
"XPRD02": 99.99
}
}

# Response
{
"data": {
"PRD_ID": 123,
"TREC": "N", # ← Automatically set
"CDATA": "20251219153045",
"OWNER": "user@example.com",
"XPRD01": "New Product",
"XPRD02": 99.99
}
}

M - Modified

Meaning: Record has been updated at least once since creation

Set By: PUT/PATCH (update operations)

Characteristics:

  • ✅ Second state in lifecycle (after N)
  • ✅ Indicates record has change history
  • ✅ Visible in standard queries (TREC != 'C')
  • ✅ Can transition to C (via DELETE) but never back to N

Automatic Fields Updated:

{
"TREC": "M",
"LDATA": "20251219160000", // Last modification timestamp
"LOWNER": "admin@example.com" // Last modifier
}

Example:

PATCH /api/v4/core/PRD/123
{
"data": {
"XPRD02": 89.99
}
}

# Response
{
"data": {
"PRD_ID": 123,
"TREC": "M", # ← Changed from 'N' to 'M'
"LDATA": "20251219160000",
"LOWNER": "admin@example.com",
"XPRD01": "New Product",
"XPRD02": 89.99 # ← Updated
}
}

C - Cancelled (Soft Delete)

Meaning: Record is soft deleted (logically deleted but physically present)

Set By: DELETE (without ?force=true parameter)

Characteristics:

  • ✅ Terminal state for soft delete
  • ✅ Record remains in database
  • ✅ Excluded from standard queries (filtered by TREC != 'C')
  • ✅ Can be restored by setting TREC='M' via PATCH
  • ✅ Can be force deleted (physical removal) via DELETE?force=true

Automatic Fields Updated:

{
"TREC": "C",
"LDATA": "20251219170000", // Deletion timestamp
"LOWNER": "admin@example.com" // Deleter
}

Example:

DELETE /api/v4/core/PRD/123

# Record updated with TREC='C', not physically deleted
{
"message": "Record soft deleted",
"record": {
"PRD_ID": 123,
"TREC": "C", # ← Changed to 'C'
"LDATA": "20251219170000",
"LOWNER": "admin@example.com"
}
}

Restore Example:

# Restore soft-deleted record by setting TREC='M'
PATCH /api/v4/core/PRD/123
{
"data": {
"TREC": "M"
}
}

# Record is now active again
{
"data": {
"PRD_ID": 123,
"TREC": "M", # ← Restored
"LDATA": "20251219180000"
}
}

P - Processed

Meaning: Special workflow state for batch processing or custom workflows

Set By: Custom workflow logic (rarely used)

Characteristics:

  • ✅ Indicates record is in processing state
  • ✅ Application-specific meaning
  • ✅ Visible in standard queries (TREC != 'C')
  • ✅ Can transition to M or C

Example Use Cases:

  • Batch import processing (TREC='P' during import, 'M' when complete)
  • Approval workflows (TREC='P' while pending approval)
  • Queue processing (TREC='P' in queue, 'M' when processed)

Example:

# Custom workflow sets TREC='P'
PATCH /api/v4/core/ORD/456
{
"data": {
"TREC": "P",
"XORD_STATUS": "processing"
}
}

# After processing completes
PATCH /api/v4/core/ORD/456
{
"data": {
"TREC": "M",
"XORD_STATUS": "completed"
}
}

State Transitions

Complete State Machine

        POST (Create)

┌────────┐
│ TREC=N │ (New)
└────────┘

PUT/PATCH (Update)

┌────────┐
│ TREC=M │ (Modified)
└────────┘

DELETE (Soft Delete)

┌────────┐
│ TREC=C │ (Cancelled)
└────────┘

DELETE?force=true

Physical Deletion
(Record removed from DB)

Valid Transitions

FromToOperationDescription
(none)NPOSTCreate new record
NMPUT/PATCHFirst update
MMPUT/PATCHSubsequent updates
NCDELETESoft delete new record
MCDELETESoft delete modified record
CMPATCHRestore soft-deleted record
N(deleted)DELETE?force=trueForce delete new record
M(deleted)DELETE?force=trueForce delete modified record
C(deleted)DELETE?force=trueForce delete cancelled record
PMPATCHComplete processing
N/MPPATCHEnter processing state

Invalid Transitions

FromToWhy Invalid
MNCannot "un-modify" a record
CNCannot restore to "new" state
PNCannot reset to "new" after processing

Automatic TREC Management

Creation (POST)

CoreWrite Logic:

public function insert(string $dimension, array $data): array {
// System sets TREC automatically
$data['TREC'] = 'N';
$data['CDATA'] = date('YmdHis');
$data['OWNER'] = $this->getAuthenticatedUser();

// Insert record
$this->pdo->insert($dimension, $data);
}

User Attempt to Override:

# ❌ BAD - Attempting to set TREC manually
POST /api/v4/core/PRD
{
"data": {
"TREC": "M", # Ignored! System sets to 'N'
"XPRD01": "Product"
}
}

# Actual result: TREC='N' (system override)

Update (PUT/PATCH)

CoreWrite Logic:

public function update(string $dimension, string $id, array $data): array {
// System updates TREC automatically
$data['TREC'] = 'M';
$data['LDATA'] = date('YmdHis');
$data['LOWNER'] = $this->getAuthenticatedUser();

// Update record
$this->pdo->update($dimension, $id, $data);
}

User Attempt to Keep TREC='N':

# ❌ BAD - Attempting to prevent TREC update
PATCH /api/v4/core/PRD/123
{
"data": {
"TREC": "N", # Ignored! System sets to 'M'
"XPRD02": 89.99
}
}

# Actual result: TREC='M' (system override)

Soft Delete (DELETE)

CoreWrite Logic:

public function delete(string $dimension, string $id, bool $force = false): void {
if ($force) {
// Physical deletion
$this->pdo->delete($dimension, $id);
} else {
// Soft deletion - update TREC
$this->pdo->update($dimension, $id, [
'TREC' => 'C',
'LDATA' => date('YmdHis'),
'LOWNER' => $this->getAuthenticatedUser()
]);
}
}

Soft vs Force Delete:

# Soft delete - TREC='C'
DELETE /api/v4/core/PRD/123

# Force delete - physically removed
DELETE /api/v4/core/PRD/123?force=true

Filtering by TREC

Default Behavior

Standard queries exclude cancelled records:

-- CoreQuery automatically adds TREC filter
SELECT * FROM TB_ANAG_PRD00
WHERE PRD_SOURCE = 'ecommerce'
AND TREC != 'C' -- ← Automatically added
LIMIT 100;

API Request:

GET /api/v4/core/PRD?$num_rows=100

# Returns only active records (TREC='N' or TREC='M')

Include Deleted Records

Explicitly filter for all TREC states:

GET /api/v4/core/PRD?filter[TREC][$in]=N,M,C

# Returns all records including soft-deleted (TREC='C')

Filter by Specific State

New records only:

GET /api/v4/core/PRD?filter[TREC]=N

Modified records only:

GET /api/v4/core/PRD?filter[TREC]=M

Cancelled records only (trash):

GET /api/v4/core/PRD?filter[TREC]=C

Active records (default):

GET /api/v4/core/PRD?filter[TREC][$ne]=C

# Equivalent to default behavior

Processing State

Records in processing:

GET /api/v4/core/ORD?filter[TREC]=P

Common Use Cases

Soft Delete with Trash

Implementation:

class ProductService {
// Soft delete product
async deleteProduct(productId) {
await coreAPI.delete(`/api/v4/core/PRD/${productId}`);
// TREC automatically set to 'C'
}

// Get trash (soft-deleted products)
async getTrash() {
return await coreAPI.get('/api/v4/core/PRD', {
params: {
filter: { TREC: 'C' }
}
});
}

// Restore from trash
async restoreProduct(productId) {
await coreAPI.patch(`/api/v4/core/PRD/${productId}`, {
data: { TREC: 'M' }
});
}

// Permanent delete
async permanentDelete(productId) {
await coreAPI.delete(`/api/v4/core/PRD/${productId}`, {
params: { force: true }
});
// Record physically removed
}
}

Audit Trail

Track record history:

class AuditService {
async getRecordHistory(dimension, recordId) {
const record = await coreAPI.get(`/api/v4/core/${dimension}/${recordId}`);

return {
created: {
timestamp: record.data.CDATA,
user: record.data.OWNER,
state: 'N'
},
lastModified: record.data.TREC === 'M' ? {
timestamp: record.data.LDATA,
user: record.data.LOWNER,
state: 'M'
} : null,
currentState: record.data.TREC
};
}
}

Change Detection

Find recently created records:

GET /api/v4/core/PRD?filter[TREC]=N&sort=-CDATA

Find recently modified records:

GET /api/v4/core/PRD?filter[TREC]=M&sort=-LDATA

Batch Processing

Process pending records:

class BatchProcessor {
async processPendingOrders() {
// Get orders in processing state
const pending = await coreAPI.get('/api/v4/core/ORD', {
params: {
filter: { TREC: 'P', XORD_STATUS: 'pending' }
}
});

for (const order of pending.data) {
await this.processOrder(order);

// Mark as processed
await coreAPI.patch(`/api/v4/core/ORD/${order.ORD_ID}`, {
data: {
TREC: 'M',
XORD_STATUS: 'completed'
}
});
}
}
}

Best Practices

✅ DO

Rely on automatic TREC management:

// ✅ Good - let system manage TREC
await coreAPI.post('/api/v4/core/PRD', {
data: {
XPRD01: 'Product',
XPRD02: 99.99
// TREC automatically set to 'N'
}
});

Use soft delete by default:

// ✅ Good - reversible
await coreAPI.delete('/api/v4/core/PRD/123');

// Can be restored later
await coreAPI.patch('/api/v4/core/PRD/123', {
data: { TREC: 'M' }
});

Filter out cancelled records explicitly when needed:

// ✅ Good - clear intent
const activeProducts = await coreAPI.get('/api/v4/core/PRD', {
params: {
filter: { TREC: { $ne: 'C' } }
}
});

❌ DON'T

Don't manually set TREC on create:

// ❌ Bad - TREC will be overridden to 'N'
await coreAPI.post('/api/v4/core/PRD', {
data: {
TREC: 'M', // Ignored
XPRD01: 'Product'
}
});

Don't try to prevent TREC='M' on update:

// ❌ Bad - TREC will always become 'M' on update
await coreAPI.patch('/api/v4/core/PRD/123', {
data: {
TREC: 'N', // Ignored, will be 'M'
XPRD02: 89.99
}
});

Don't force delete without good reason:

// ❌ Bad - irreversible
await coreAPI.delete('/api/v4/core/PRD/123', {
params: { force: true }
});

// ✅ Better - soft delete
await coreAPI.delete('/api/v4/core/PRD/123');

Troubleshooting

Issue 1: Expecting TREC='N' After Update

Symptom: Record has TREC='M' but I want it to be TREC='N'

Cause: Any update automatically sets TREC='M'

Solution: Accept that modified records have TREC='M' - this is correct behavior

Issue 2: Soft-Deleted Records Not Visible

Symptom: Record exists in database but not returned by API

Cause: Record has TREC='C' and is excluded from default queries

Solution:

# Include cancelled records in query
GET /api/v4/core/PRD?filter[TREC][$in]=N,M,C

Issue 3: Cannot Force Delete

Symptom: DELETE?force=true returns permission error

Cause: User lacks 'force_delete' grant

Solution: Use soft delete instead (DELETE without force parameter)

Issue 4: TREC Override Not Working

Symptom: Setting TREC manually has no effect

Cause: TREC is system-managed and cannot be manually controlled

Solution: Trust automatic TREC management - it ensures data integrity


Summary

  • TREC tracks record lifecycle: N (new) → M (modified) → C (cancelled)
  • System-managed - never set TREC manually
  • Automatic transitions on POST (N), PUT/PATCH (M), DELETE (C)
  • Soft delete default - TREC='C', record remains in database
  • Force delete optional - physical removal with ?force=true
  • Filter by TREC to include/exclude cancelled records

Key Takeaways:

  1. TREC is automatic - trust the system
  2. Soft delete (TREC='C') is reversible
  3. Force delete is permanent - use sparingly
  4. Default queries exclude TREC='C'
  5. Filter by TREC for trash functionality
  6. Never manually set TREC in create/update operations

Common Filters:

  • Active records: ?filter[TREC][$ne]=C (default)
  • New records: ?filter[TREC]=N
  • Modified records: ?filter[TREC]=M
  • Cancelled records: ?filter[TREC]=C
  • All records: ?filter[TREC][$in]=N,M,C