# Database Relationships & Entity Details

## 🗄️ **Entity Relationship Overview**

### **Core Entities**

#### **1. CLIENTS**
```sql
CREATE TABLE clients (
    id UUID PRIMARY KEY,
    name VARCHAR NOT NULL,
    is_active BOOLEAN DEFAULT true,
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);
```

#### **2. USER_LEVELS**
```sql
CREATE TABLE user_levels (
    id SERIAL PRIMARY KEY,
    name VARCHAR NOT NULL,
    alias_name VARCHAR NOT NULL,
    level_number INTEGER NOT NULL,  -- 1=Highest, 2=Mid, 3=Lowest
    parent_level_id INTEGER REFERENCES user_levels(id),
    province_id INTEGER,
    group VARCHAR,
    is_approval_active BOOLEAN DEFAULT false,
    client_id UUID REFERENCES clients(id),
    is_active BOOLEAN DEFAULT true,
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);
```

#### **3. USERS**
```sql
CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    name VARCHAR NOT NULL,
    email VARCHAR UNIQUE NOT NULL,
    user_level_id INTEGER REFERENCES user_levels(id),
    client_id UUID REFERENCES clients(id),
    is_active BOOLEAN DEFAULT true,
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);
```

### **Workflow Configuration Entities**

#### **4. APPROVAL_WORKFLOWS**
```sql
CREATE TABLE approval_workflows (
    id SERIAL PRIMARY KEY,
    name VARCHAR NOT NULL,
    description TEXT,
    is_default BOOLEAN DEFAULT false,
    is_active BOOLEAN DEFAULT true,
    client_id UUID REFERENCES clients(id),
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);
```

#### **5. APPROVAL_WORKFLOW_STEPS**
```sql
CREATE TABLE approval_workflow_steps (
    id SERIAL PRIMARY KEY,
    workflow_id INTEGER REFERENCES approval_workflows(id) ON DELETE CASCADE,
    step_order INTEGER NOT NULL,  -- 1, 2, 3, etc.
    step_name VARCHAR NOT NULL,
    required_user_level_id INTEGER REFERENCES user_levels(id),
    is_required BOOLEAN DEFAULT true,
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);
```

#### **6. CLIENT_APPROVAL_SETTINGS**
```sql
CREATE TABLE client_approval_settings (
    id SERIAL PRIMARY KEY,
    client_id UUID REFERENCES clients(id),
    workflow_id INTEGER REFERENCES approval_workflows(id),
    auto_approve_levels JSONB,  -- Array of level numbers
    notification_settings JSONB,
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);
```

### **Article & Approval Execution Entities**

#### **7. ARTICLES**
```sql
CREATE TABLE articles (
    id SERIAL PRIMARY KEY,
    title VARCHAR NOT NULL,
    slug VARCHAR UNIQUE,
    description TEXT,
    html_description TEXT,
    workflow_id INTEGER REFERENCES approval_workflows(id),
    current_approval_step INTEGER,
    status_id INTEGER,  -- 1=pending, 2=approved, 3=rejected
    is_publish BOOLEAN DEFAULT false,
    published_at TIMESTAMP,
    created_by_id INTEGER REFERENCES users(id),
    client_id UUID REFERENCES clients(id),
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);
```

#### **8. ARTICLE_APPROVAL_FLOWS**
```sql
CREATE TABLE article_approval_flows (
    id SERIAL PRIMARY KEY,
    article_id INTEGER REFERENCES articles(id) ON DELETE CASCADE,
    workflow_id INTEGER REFERENCES approval_workflows(id),
    current_step INTEGER DEFAULT 1,
    status_id INTEGER DEFAULT 1,  -- 1=pending, 2=approved, 3=rejected, 4=revision_requested
    submitted_by_id INTEGER REFERENCES users(id),
    submitted_at TIMESTAMP DEFAULT NOW(),
    completed_at TIMESTAMP,
    rejection_reason TEXT,
    revision_requested BOOLEAN DEFAULT false,
    revision_message TEXT,
    client_id UUID REFERENCES clients(id),
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);
```

#### **9. ARTICLE_APPROVAL_STEP_LOGS**
```sql
CREATE TABLE article_approval_step_logs (
    id SERIAL PRIMARY KEY,
    approval_flow_id INTEGER REFERENCES article_approval_flows(id) ON DELETE CASCADE,
    step_order INTEGER NOT NULL,
    step_name VARCHAR NOT NULL,
    approved_by_id INTEGER REFERENCES users(id),
    action VARCHAR NOT NULL,  -- approve, reject, auto_skip, request_revision
    message TEXT,
    processed_at TIMESTAMP DEFAULT NOW(),
    user_level_id INTEGER REFERENCES user_levels(id),
    created_at TIMESTAMP DEFAULT NOW()
);
```

#### **10. ARTICLE_APPROVALS (Legacy)**
```sql
CREATE TABLE article_approvals (
    id SERIAL PRIMARY KEY,
    article_id INTEGER REFERENCES articles(id) ON DELETE CASCADE,
    approval_by INTEGER REFERENCES users(id),
    status_id INTEGER,  -- 1=pending, 2=approved, 3=rejected
    message TEXT,
    approval_at_level INTEGER REFERENCES user_levels(id),
    approval_at TIMESTAMP DEFAULT NOW(),
    created_at TIMESTAMP DEFAULT NOW()
);
```

## 🔗 **Relationship Details**

### **One-to-Many Relationships**

```
CLIENTS (1) ──→ (N) USER_LEVELS
CLIENTS (1) ──→ (N) USERS
CLIENTS (1) ──→ (N) APPROVAL_WORKFLOWS
CLIENTS (1) ──→ (N) ARTICLES
CLIENTS (1) ──→ (N) CLIENT_APPROVAL_SETTINGS

USER_LEVELS (1) ──→ (N) USERS
USER_LEVELS (1) ──→ (N) APPROVAL_WORKFLOW_STEPS
USER_LEVELS (1) ──→ (N) ARTICLE_APPROVAL_STEP_LOGS

APPROVAL_WORKFLOWS (1) ──→ (N) APPROVAL_WORKFLOW_STEPS
APPROVAL_WORKFLOWS (1) ──→ (N) ARTICLE_APPROVAL_FLOWS
APPROVAL_WORKFLOWS (1) ──→ (N) ARTICLES

ARTICLES (1) ──→ (N) ARTICLE_APPROVAL_FLOWS
ARTICLES (1) ──→ (N) ARTICLE_APPROVALS

ARTICLE_APPROVAL_FLOWS (1) ──→ (N) ARTICLE_APPROVAL_STEP_LOGS

USERS (1) ──→ (N) ARTICLES (as creator)
USERS (1) ──→ (N) ARTICLE_APPROVAL_FLOWS (as submitter)
USERS (1) ──→ (N) ARTICLE_APPROVAL_STEP_LOGS (as approver)
USERS (1) ──→ (N) ARTICLE_APPROVALS (as approver)
```

### **Many-to-Many Relationships (Implicit)**

```
USERS ←→ USER_LEVELS (through user_level_id)
ARTICLES ←→ APPROVAL_WORKFLOWS (through workflow_id)
ARTICLE_APPROVAL_FLOWS ←→ APPROVAL_WORKFLOW_STEPS (through workflow_id and step_order)
```

## 📊 **Data Flow Examples**

### **Example 1: Complete Approval Process**

#### **Step 1: Setup Data**
```sql
-- Client
INSERT INTO clients (id, name) VALUES ('b1ce6602-07ad-46c2-85eb-0cd6decfefa3', 'Test Client');

-- User Levels
INSERT INTO user_levels (name, alias_name, level_number, is_approval_active, client_id) VALUES
('POLDAS', 'Poldas', 1, true, 'b1ce6602-07ad-46c2-85eb-0cd6decfefa3'),
('POLDAS', 'Poldas', 2, true, 'b1ce6602-07ad-46c2-85eb-0cd6decfefa3'),
('POLRES', 'Polres', 3, true, 'b1ce6602-07ad-46c2-85eb-0cd6decfefa3');

-- Users
INSERT INTO users (name, email, user_level_id, client_id) VALUES
('Admin Level 1', 'admin1@test.com', 1, 'b1ce6602-07ad-46c2-85eb-0cd6decfefa3'),
('Admin Level 2', 'admin2@test.com', 2, 'b1ce6602-07ad-46c2-85eb-0cd6decfefa3'),
('User Level 3', 'user3@test.com', 3, 'b1ce6602-07ad-46c2-85eb-0cd6decfefa3');

-- Workflow
INSERT INTO approval_workflows (name, description, is_default, client_id) VALUES
('Standard Approval', 'Standard 3-step approval process', true, 'b1ce6602-07ad-46c2-85eb-0cd6decfefa3');

-- Workflow Steps
INSERT INTO approval_workflow_steps (workflow_id, step_order, step_name, required_user_level_id) VALUES
(1, 1, 'Level 2 Review', 2),
(1, 2, 'Level 1 Final Approval', 1);
```

#### **Step 2: Article Creation**
```sql
-- Article created by User Level 3
INSERT INTO articles (title, slug, workflow_id, current_approval_step, status_id, created_by_id, client_id) VALUES
('Test Article', 'test-article', 1, 1, 1, 3, 'b1ce6602-07ad-46c2-85eb-0cd6decfefa3');

-- Approval Flow
INSERT INTO article_approval_flows (article_id, workflow_id, current_step, status_id, submitted_by_id, client_id) VALUES
(1, 1, 1, 1, 3, 'b1ce6602-07ad-46c2-85eb-0cd6decfefa3');

-- Legacy Approval
INSERT INTO article_approvals (article_id, approval_by, status_id, message, approval_at_level) VALUES
(1, 3, 1, 'Need Approval', 2);
```

#### **Step 3: Level 2 Approval**
```sql
-- Step Log
INSERT INTO article_approval_step_logs (approval_flow_id, step_order, step_name, approved_by_id, action, message, user_level_id) VALUES
(1, 1, 'Level 2 Review', 2, 'approve', 'Approved by Level 2', 2);

-- Update Flow
UPDATE article_approval_flows SET current_step = 2 WHERE id = 1;

-- Update Article
UPDATE articles SET current_approval_step = 2 WHERE id = 1;
```

#### **Step 4: Level 1 Final Approval**
```sql
-- Step Log
INSERT INTO article_approval_step_logs (approval_flow_id, step_order, step_name, approved_by_id, action, message, user_level_id) VALUES
(1, 2, 'Level 1 Final Approval', 1, 'approve', 'Final approval by Level 1', 1);

-- Complete Flow
UPDATE article_approval_flows SET 
    status_id = 2, 
    completed_at = NOW() 
WHERE id = 1;

-- Publish Article
UPDATE articles SET 
    status_id = 2, 
    is_publish = true, 
    published_at = NOW(),
    current_approval_step = NULL 
WHERE id = 1;
```

## 🔍 **Key Queries for Monitoring**

### **Active Approval Flows**
```sql
SELECT 
    aaf.id as flow_id,
    a.title as article_title,
    aaf.current_step,
    aws.step_name as current_step_name,
    ul.name as required_approver_level,
    u.name as submitted_by,
    aaf.submitted_at,
    aaf.status_id
FROM article_approval_flows aaf
JOIN articles a ON aaf.article_id = a.id
JOIN approval_workflow_steps aws ON aaf.workflow_id = aws.workflow_id 
    AND aaf.current_step = aws.step_order
JOIN user_levels ul ON aws.required_user_level_id = ul.id
JOIN users u ON aaf.submitted_by_id = u.id
WHERE aaf.status_id = 1  -- pending
ORDER BY aaf.submitted_at DESC;
```

### **Approval History**
```sql
SELECT 
    a.title as article_title,
    aasl.step_name,
    aasl.action,
    aasl.message,
    u.name as approver_name,
    ul.name as approver_level,
    aasl.processed_at
FROM article_approval_step_logs aasl
JOIN article_approval_flows aaf ON aasl.approval_flow_id = aaf.id
JOIN articles a ON aaf.article_id = a.id
JOIN users u ON aasl.approved_by_id = u.id
JOIN user_levels ul ON aasl.user_level_id = ul.id
WHERE aaf.article_id = ?  -- specific article
ORDER BY aasl.step_order, aasl.processed_at;
```

### **Workflow Performance**
```sql
SELECT 
    aw.name as workflow_name,
    COUNT(aaf.id) as total_flows,
    COUNT(CASE WHEN aaf.status_id = 2 THEN 1 END) as approved,
    COUNT(CASE WHEN aaf.status_id = 3 THEN 1 END) as rejected,
    AVG(EXTRACT(EPOCH FROM (aaf.completed_at - aaf.submitted_at))/3600) as avg_hours
FROM approval_workflows aw
LEFT JOIN article_approval_flows aaf ON aw.id = aaf.workflow_id
WHERE aw.client_id = ?
GROUP BY aw.id, aw.name;
```

## ⚠️ **Common Data Issues**

### **Issue 1: Orphaned Records**
```sql
-- Find articles without approval flows
SELECT a.id, a.title 
FROM articles a 
LEFT JOIN article_approval_flows aaf ON a.id = aaf.article_id 
WHERE aaf.id IS NULL AND a.workflow_id IS NOT NULL;

-- Find approval flows without articles
SELECT aaf.id, aaf.article_id 
FROM article_approval_flows aaf 
LEFT JOIN articles a ON aaf.article_id = a.id 
WHERE a.id IS NULL;
```

### **Issue 2: Inconsistent Step Data**
```sql
-- Find flows with invalid current_step
SELECT aaf.id, aaf.current_step, aws.step_order
FROM article_approval_flows aaf
JOIN approval_workflow_steps aws ON aaf.workflow_id = aws.workflow_id
WHERE aaf.current_step != aws.step_order;
```

### **Issue 3: Missing Step Logs**
```sql
-- Find approved flows without step logs
SELECT aaf.id, aaf.article_id, aaf.status_id
FROM article_approval_flows aaf
LEFT JOIN article_approval_step_logs aasl ON aaf.id = aasl.approval_flow_id
WHERE aaf.status_id = 2 AND aasl.id IS NULL;
```

## 🚀 **Performance Optimization**

### **Indexes**
```sql
-- Primary indexes
CREATE INDEX idx_article_approval_flows_status ON article_approval_flows(status_id);
CREATE INDEX idx_article_approval_flows_current_step ON article_approval_flows(current_step);
CREATE INDEX idx_article_approval_flows_client_id ON article_approval_flows(client_id);
CREATE INDEX idx_article_approval_step_logs_flow_id ON article_approval_step_logs(approval_flow_id);
CREATE INDEX idx_article_approval_step_logs_processed_at ON article_approval_step_logs(processed_at);
CREATE INDEX idx_articles_workflow_id ON articles(workflow_id);
CREATE INDEX idx_articles_status_id ON articles(status_id);
CREATE INDEX idx_users_user_level_id ON users(user_level_id);
CREATE INDEX idx_user_levels_level_number ON user_levels(level_number);

-- Composite indexes
CREATE INDEX idx_article_approval_flows_status_step ON article_approval_flows(status_id, current_step);
CREATE INDEX idx_approval_workflow_steps_workflow_order ON approval_workflow_steps(workflow_id, step_order);
```

### **Partitioning (for large datasets)**
```sql
-- Partition article_approval_step_logs by month
CREATE TABLE article_approval_step_logs_2025_01 PARTITION OF article_approval_step_logs
FOR VALUES FROM ('2025-01-01') TO ('2025-02-01');
```

## 📝 **Data Validation Rules**

### **Business Rules**
1. **Workflow Steps**: Must be sequential (1, 2, 3, etc.)
2. **User Levels**: Level number must be unique per client
3. **Approval Flows**: Can only have one active flow per article
4. **Step Logs**: Must have corresponding workflow step
5. **Status Transitions**: Only valid transitions allowed

### **Database Constraints**
```sql
-- Ensure step order is sequential
ALTER TABLE approval_workflow_steps 
ADD CONSTRAINT chk_step_order_positive CHECK (step_order > 0);

-- Ensure level number is positive
ALTER TABLE user_levels 
ADD CONSTRAINT chk_level_number_positive CHECK (level_number > 0);

-- Ensure status values are valid
ALTER TABLE article_approval_flows 
ADD CONSTRAINT chk_status_valid CHECK (status_id IN (1, 2, 3, 4));

-- Ensure current step is valid
ALTER TABLE article_approval_flows 
ADD CONSTRAINT chk_current_step_positive CHECK (current_step > 0);
```

## 🎯 **Summary**

Database design untuk sistem approval workflow MEDOLS menggunakan:

1. **Normalized Structure**: Memisahkan konfigurasi dari eksekusi
2. **Audit Trail**: Step logs untuk tracking lengkap
3. **Flexibility**: Workflow dapat dikonfigurasi per client
4. **Performance**: Indexes untuk query yang efisien
5. **Data Integrity**: Constraints dan validasi
6. **Scalability**: Partitioning untuk data besar

Dengan struktur ini, sistem dapat menangani berbagai skenario approval dengan efisien dan dapat diandalkan.
