26 KiB
Purchase Requisition Approval & Immutability - System Administrator Guide
Document Information
- Document Type: Technical Implementation Guide
- Audience: System Administrators, DevOps, Technical Support
- Last Updated: 2025-10-30
- Version: 1.0
Table of Contents
- Architecture Overview
- Implementation Details
- Database Schema
- API Endpoints
- Configuration
- Security Considerations
- Monitoring and Logging
- Troubleshooting
- Maintenance Procedures
- Testing and Validation
Architecture Overview
System Components
┌─────────────┐ ┌─────────────┐ ┌──────────────┐
│ Angular │──────│ ASP.NET │──────│ PostgreSQL │
│ Frontend │ HTTP │ Core API │ EF │ Database │
└─────────────┘ └─────────────┘ └──────────────┘
(UI) (Business Logic) (Data Store)
Immutability Implementation Layers
-
Frontend Layer (
purchase-requisition-form.component.ts)- UI/UX protection
- Visual indicators
- Client-side validation
- User experience
-
API Layer (
PurchaseRequisitionsController.cs)- HTTP endpoint protection
- Request validation
- Error handling
- Audit logging trigger
-
Service Layer (
PurchaseRequisitionService.cs)- Business logic
- Exception propagation
-
Repository Layer (
PurchaseRequisitionRepository.cs)- Data access
- Status validation
- Exception throwing
-
Database Layer (PostgreSQL)
- Data persistence
- Audit log storage
- Transaction management
Status Flow
Draft (Editable)
↓ Submit
PendingApproval (Locked)
↓ Approve ↓ Reject
Approved (Locked) → Back to Draft (Editable)
↓ Convert
ConvertedToRfq/PO (Locked)
↓ Complete
Closed (Locked)
Implementation Details
Backend Implementation
Custom Exception Class
File: /piam-api/src/PiamMasterData.Domain/Exceptions/ImmutableDocumentException.cs
public class ImmutableDocumentException : Exception
{
public string DocumentType { get; }
public string DocumentId { get; }
public string? CurrentStatus { get; }
public string? AllowedStatus { get; }
public ImmutableDocumentException(
string documentType,
string documentId,
string? currentStatus = null,
string? allowedStatus = null)
: base($"Cannot modify {documentType} in '{currentStatus}' status. " +
$"Only '{allowedStatus}' status allows modifications.")
{
DocumentType = documentType;
DocumentId = documentId;
CurrentStatus = currentStatus;
AllowedStatus = allowedStatus;
}
}
Repository Validation
File: /piam-api/src/PiamMasterData.Infrastructure/Repositories/PurchaseRequisitionRepository.cs
Lines: 304-311
if (requisition.Status != PurchaseRequisitionStatus.Draft)
{
throw new ImmutableDocumentException(
documentType: "Purchase Requisition",
documentId: id.ToString(),
currentStatus: requisition.Status.ToString(),
allowedStatus: nameof(PurchaseRequisitionStatus.Draft)
);
}
Controller Error Handling
File: /piam-api/src/PiamMasterData.Api/Controllers/PurchaseRequisitionsController.cs
Lines: 120-143
catch (ImmutableDocumentException ex)
{
// Log the modification attempt for audit purposes
await LogModificationAttemptAsync(
id,
"UPDATE_ATTEMPT",
ex.CurrentStatus ?? "UNKNOWN",
false,
dto,
ex.Message,
cancellationToken);
return StatusCode(403, new
{
error = ex.Message,
errorCode = "PR_IMMUTABLE_STATUS",
details = new
{
documentType = ex.DocumentType,
documentId = ex.DocumentId,
currentStatus = ex.CurrentStatus,
allowedStatus = ex.AllowedStatus
}
});
}
Frontend Implementation
Component Logic
File: /piam-web/src/app/features/procurement/components/purchase-requisition-form/purchase-requisition-form.component.ts
Lines: 186-203
// Check if document is immutable (cannot be edited due to status)
get isImmutable(): boolean {
const immutableStatuses: PurchaseRequisitionStatus[] = [
'Approved',
'PendingApproval',
'ConvertedToRfq',
'ConvertedToPurchaseOrder',
'Closed',
];
return this.detail ? immutableStatuses.includes(this.detail.status) : false;
}
// Determine if user can edit (save/submit buttons should show)
get canEdit(): boolean {
if (!this.detail) return false;
return this.detail.status === 'Draft' && !this.isReadOnly;
}
Template Visual Indicators
File: /piam-web/src/app/features/procurement/components/purchase-requisition-form/purchase-requisition-form.component.html
Key Elements:
- Lock icon in header (lines 12-19)
- Enhanced status badge (lines 22-31)
- Informational banner (lines 62-75)
- Conditional button visibility (lines 40-55)
- View Only badge (lines 33-36)
Database Schema
Purchase Requisition Table
Table: purchase_requisitions
Key Columns:
id UUID PRIMARY KEY
requisition_number VARCHAR(50) UNIQUE NOT NULL
status VARCHAR(50) NOT NULL
approved_by VARCHAR(255)
approved_at_utc TIMESTAMP
created_at_utc TIMESTAMP NOT NULL
updated_at_utc TIMESTAMP NOT NULL
Status Values:
Draft- EditablePendingApproval- LockedApproved- LockedRejected- EditableConvertedToRfq- LockedConvertedToPurchaseOrder- LockedClosed- Locked
Audit Log Table
Table: purchase_requisition_audit_logs
Schema:
CREATE TABLE purchase_requisition_audit_logs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
purchase_requisition_id UUID NOT NULL,
action VARCHAR(50) NOT NULL,
user_id VARCHAR(255),
user_name VARCHAR(255) NOT NULL,
attempted_changes TEXT,
status_at_attempt VARCHAR(50) NOT NULL,
was_successful BOOLEAN NOT NULL,
failure_reason TEXT,
ip_address VARCHAR(45),
user_agent TEXT,
created_at_utc TIMESTAMP NOT NULL DEFAULT now(),
FOREIGN KEY (purchase_requisition_id)
REFERENCES purchase_requisitions(id) ON DELETE CASCADE
);
CREATE INDEX ix_pr_audit_logs_pr_id
ON purchase_requisition_audit_logs(purchase_requisition_id);
CREATE INDEX ix_pr_audit_logs_created_at
ON purchase_requisition_audit_logs(created_at_utc DESC);
CREATE INDEX ix_pr_audit_logs_pr_id_created_at
ON purchase_requisition_audit_logs(purchase_requisition_id, created_at_utc DESC);
Action Types:
UPDATE_ATTEMPT- Attempted to modify locked PRSTATUS_CHANGE- Status transitionAPPROVAL- PR approvedREJECTION- PR rejectedCONVERSION- Converted to RFQ/PO
Migration
File: /piam-api/src/PiamMasterData.Infrastructure/Migrations/20251029204955_AddPurchaseRequisitionAuditLog.cs
Commands:
# Create migration
cd /piam-api/src/PiamMasterData.Infrastructure
dotnet ef migrations add AddPurchaseRequisitionAuditLog
# Apply migration
dotnet ef database update
# Verify
psql -h localhost -U [user] -d [database] -c "\d purchase_requisition_audit_logs"
API Endpoints
Update Purchase Requisition
Endpoint: PUT /api/purchase-requisitions/{id}
Request:
PUT /api/purchase-requisitions/a1b2c3d4-5678-90ab-cdef-123456789012
Content-Type: application/json
{
"departmentCode": "IT",
"budgetCode": "OPEX-2025-IT-001",
"purpose": "Updated purpose",
"lines": [...]
}
Success Response (200 OK):
{
"id": "a1b2c3d4-5678-90ab-cdef-123456789012",
"requisitionNumber": "PR-2025-001234",
"status": "Draft",
...
}
Error Response (403 Forbidden):
{
"error": "Cannot modify Purchase Requisition in 'Approved' status. Only 'Draft' status allows modifications.",
"errorCode": "PR_IMMUTABLE_STATUS",
"details": {
"documentType": "Purchase Requisition",
"documentId": "a1b2c3d4-5678-90ab-cdef-123456789012",
"currentStatus": "Approved",
"allowedStatus": "Draft"
}
}
Get Audit Logs
Endpoint: GET /api/purchase-requisitions/{id}/audit-logs
Request:
GET /api/purchase-requisitions/a1b2c3d4-5678-90ab-cdef-123456789012/audit-logs?pageNumber=1&pageSize=25
Response (200 OK):
{
"items": [
{
"id": "log-uuid-1",
"purchaseRequisitionId": "a1b2c3d4-5678-90ab-cdef-123456789012",
"action": "UPDATE_ATTEMPT",
"userId": "user-123",
"userName": "john.doe",
"attemptedChanges": "{\"purpose\":\"New purpose\"}",
"statusAtAttempt": "Approved",
"wasSuccessful": false,
"failureReason": "Cannot modify PR in Approved status",
"ipAddress": "192.168.1.100",
"userAgent": "Mozilla/5.0...",
"createdAtUtc": "2025-10-30T14:23:45Z"
}
],
"totalCount": 1,
"pageNumber": 1,
"pageSize": 25
}
Approve Purchase Requisition
Endpoint: POST /api/purchase-requisitions/{id}/approve
Important: ⚠️ This endpoint currently has NO authorization checks
Request:
POST /api/purchase-requisitions/a1b2c3d4-5678-90ab-cdef-123456789012/approve
Content-Type: application/json
{
"remarks": "Approved for procurement"
}
Response (200 OK):
{
"id": "a1b2c3d4-5678-90ab-cdef-123456789012",
"status": "Approved",
"approvedBy": "approver.name",
"approvedAtUtc": "2025-10-30T15:00:00Z",
...
}
Configuration
Dependency Injection
File: /piam-api/src/PiamMasterData.Infrastructure/DependencyInjection.cs
Line: 90
services.AddScoped<IPurchaseRequisitionAuditService, PurchaseRequisitionAuditService>();
Service Registration
All required services are registered in DependencyInjection.cs:
services.AddHttpContextAccessor(); // Line 21 - Required for audit logging
services.AddScoped<IPurchaseRequisitionRepository, PurchaseRequisitionRepository>();
services.AddScoped<IPurchaseRequisitionAuditService, PurchaseRequisitionAuditService>();
Connection String
File: appsettings.json
{
"ConnectionStrings": {
"DefaultConnection": "Host=localhost;Database=PiamMasterData;Username=user;Password=pass"
}
}
Frontend Environment
File: /piam-web/src/environments/environment.ts
export const environment = {
production: false,
apiUrl: 'http://localhost:5200/api'
};
Security Considerations
Current Implementation
✅ Implemented Security Features
-
Status-Based Access Control
- Repository enforces status checks
- Cannot bypass via API calls
- Consistent across all endpoints
-
Audit Logging
- All modification attempts logged
- User context captured
- IP address and user agent recorded
-
Exception Handling
- Proper HTTP status codes
- Structured error responses
- No sensitive data leakage
-
Frontend Protection
- UI prevents unauthorized actions
- Visual indicators for locked documents
- Form validation
❌ Missing Security Features
- NO Authorization Attributes
- No
[Authorize]on controller actions - Anyone with API access can approve
- No role-based access control
- No
Recommended Fix:
/// <summary>
/// Approves a purchase requisition
/// </summary>
[Authorize(Policy = "Procurement.PurchaseRequisitions.Approve")]
[HttpPost("{id:guid}/approve")]
public async Task<ActionResult<PurchaseRequisitionDetailDto>> ApproveRequisition(...)
- No Permission Checks
- Backend doesn't verify user permissions
- No distinction between creator/approver
Recommended Implementation:
// Check if user has permission to approve
if (!User.HasPermission("Procurement.PurchaseRequisitions.Approve"))
{
return Forbidden();
}
- No CSRF Protection
- Consider adding anti-forgery tokens
- Especially for state-changing operations
Threat Model
| Threat | Risk Level | Mitigation |
|---|---|---|
| Direct API bypass | High | ✅ Enforced at repository level |
| Unauthorized approval | High | ❌ Add authorization attributes |
| Audit log tampering | Medium | ✅ Database permissions, indexes |
| Session hijacking | Medium | ⏳ Implement proper authentication |
| CSRF attacks | Medium | ⏳ Add anti-forgery tokens |
| SQL injection | Low | ✅ Using EF Core with parameters |
Recommended Security Enhancements
-
Implement Authorization
# Add authorization attributes to all endpoints # Define policies in Startup.cs # Implement permission checks -
Add API Rate Limiting
services.AddRateLimiting(options => { ... }); -
Enable CORS Properly
services.AddCors(options => { options.AddPolicy("PiamPolicy", builder => { builder.WithOrigins("https://yourdomain.com") .AllowAnyMethod() .AllowAnyHeader(); }); }); -
Add Request Validation
[ValidateAntiForgeryToken] [ValidateModel]
Monitoring and Logging
Application Logs
Configuration: appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.EntityFrameworkCore": "Warning",
"PiamMasterData.Infrastructure.Services": "Information"
}
}
}
Audit Log Service
File: /piam-api/src/PiamMasterData.Infrastructure/Services/PurchaseRequisitionAuditService.cs
Key Method: LogModificationAttemptAsync
Logs:
- User name and ID
- Action attempted
- Status at time of attempt
- IP address and user agent
- Attempted changes (JSON)
- Success/failure status
- Failure reason
Monitoring Queries
Recent Modification Attempts
SELECT
pr.requisition_number,
pal.action,
pal.user_name,
pal.status_at_attempt,
pal.was_successful,
pal.failure_reason,
pal.created_at_utc
FROM purchase_requisition_audit_logs pal
JOIN purchase_requisitions pr ON pal.purchase_requisition_id = pr.id
WHERE pal.was_successful = false
AND pal.created_at_utc > NOW() - INTERVAL '7 days'
ORDER BY pal.created_at_utc DESC;
Failed Modification Attempts by User
SELECT
user_name,
COUNT(*) as attempt_count,
MAX(created_at_utc) as last_attempt
FROM purchase_requisition_audit_logs
WHERE was_successful = false
AND action = 'UPDATE_ATTEMPT'
AND created_at_utc > NOW() - INTERVAL '30 days'
GROUP BY user_name
ORDER BY attempt_count DESC;
Approval Activity
SELECT
pr.requisition_number,
pr.status,
pr.approved_by,
pr.approved_at_utc,
pr.created_at_utc,
EXTRACT(EPOCH FROM (pr.approved_at_utc - pr.created_at_utc))/3600 as hours_to_approval
FROM purchase_requisitions pr
WHERE pr.approved_at_utc IS NOT NULL
AND pr.approved_at_utc > NOW() - INTERVAL '30 days'
ORDER BY pr.approved_at_utc DESC;
Performance Monitoring
Key Metrics:
- Average time to approval
- Number of modification attempts
- Failed approval attempts
- API response times
- Database query performance
Tools:
- Application Insights (if using Azure)
- ELK Stack (Elasticsearch, Logstash, Kibana)
- Prometheus + Grafana
- PostgreSQL pg_stat_statements
Troubleshooting
Common Issues
Issue: Users Report "Cannot Edit" But PR is in Draft
Diagnosis:
-- Check PR status
SELECT id, requisition_number, status, updated_at_utc
FROM purchase_requisitions
WHERE requisition_number = 'PR-2025-001234';
-- Check for status anomalies
SELECT status, COUNT(*)
FROM purchase_requisitions
GROUP BY status;
Possible Causes:
- Browser cache showing old status
- Frontend/backend version mismatch
- Database update didn't complete
Solutions:
- Clear browser cache (Ctrl+Shift+Delete)
- Verify backend and frontend versions
- Check database directly
- Review application logs
Issue: Audit Logs Not Being Created
Diagnosis:
# Check if table exists
psql -h localhost -U user -d database -c "\d purchase_requisition_audit_logs"
# Check for recent logs
psql -h localhost -U user -d database -c "SELECT COUNT(*) FROM purchase_requisition_audit_logs;"
# Check service registration
grep -r "PurchaseRequisitionAuditService" src/
Possible Causes:
- Migration not applied
- Service not registered in DI
- Exception in audit logging (silently caught)
- Database permissions issue
Solutions:
- Run migrations:
dotnet ef database update - Verify DI registration in
DependencyInjection.cs:90 - Check application logs for exceptions
- Verify database user has INSERT permissions
Issue: 403 Errors on Draft PRs
Diagnosis:
-- Verify PR status
SELECT id, requisition_number, status
FROM purchase_requisitions
WHERE id = 'a1b2c3d4-5678-90ab-cdef-123456789012';
Possible Causes:
- Status field corrupted
- Status enum mismatch
- Code deployment issue
Solutions:
- Verify database value matches enum
- Check for recent code deployments
- Review repository code
- Check for custom status values
Issue: Approved PRs Being Modified
Critical Security Issue
Immediate Actions:
- Review audit logs immediately
- Identify affected PRs
- Notify security team
- Check for unauthorized access
Investigation:
-- Find modified approved PRs
SELECT
pr.requisition_number,
pr.status,
pr.updated_at_utc,
pr.approved_at_utc
FROM purchase_requisitions pr
WHERE pr.status != 'Draft'
AND pr.updated_at_utc > pr.approved_at_utc;
-- Check audit logs
SELECT *
FROM purchase_requisition_audit_logs
WHERE action = 'UPDATE_ATTEMPT'
AND was_successful = true;
Root Cause Analysis:
- Repository validation bypass?
- Direct database modification?
- Code deployment introduced bug?
- Migration script error?
Maintenance Procedures
Database Maintenance
Audit Log Retention
Policy: Retain 7 years per audit requirements
Archive Old Logs:
-- Create archive table (one-time)
CREATE TABLE purchase_requisition_audit_logs_archive (
LIKE purchase_requisition_audit_logs INCLUDING ALL
);
-- Archive logs older than 5 years
INSERT INTO purchase_requisition_audit_logs_archive
SELECT * FROM purchase_requisition_audit_logs
WHERE created_at_utc < NOW() - INTERVAL '5 years';
-- Optional: Delete archived records from main table
-- DELETE FROM purchase_requisition_audit_logs
-- WHERE created_at_utc < NOW() - INTERVAL '5 years';
Index Maintenance
-- Reindex for performance
REINDEX TABLE purchase_requisition_audit_logs;
REINDEX TABLE purchase_requisitions;
-- Vacuum to reclaim space
VACUUM ANALYZE purchase_requisition_audit_logs;
VACUUM ANALYZE purchase_requisitions;
-- Check index usage
SELECT
schemaname,
tablename,
indexname,
idx_scan,
idx_tup_read,
idx_tup_fetch
FROM pg_stat_user_indexes
WHERE tablename IN ('purchase_requisition_audit_logs', 'purchase_requisitions')
ORDER BY idx_scan;
Backup Procedures
Database Backup
# Full backup
pg_dump -h localhost -U user -d database -F c -f piam_backup_$(date +%Y%m%d).backup
# Backup specific tables
pg_dump -h localhost -U user -d database -t purchase_requisitions -t purchase_requisition_audit_logs -F c -f pr_tables_$(date +%Y%m%d).backup
# Restore
pg_restore -h localhost -U user -d database -c piam_backup_20251030.backup
Schedule Backups
# Add to crontab
# Daily backup at 2 AM
0 2 * * * /usr/local/bin/backup-piam-db.sh
# Weekly full backup on Sunday at 1 AM
0 1 * * 0 /usr/local/bin/backup-piam-db-full.sh
Application Updates
Deployment Checklist
-
✅ Before Deployment
- Review all code changes
- Run unit tests
- Run integration tests
- Backup database
- Notify users of maintenance window
-
✅ During Deployment
- Stop application
- Apply database migrations
- Deploy new code
- Update configuration if needed
- Start application
-
✅ After Deployment
- Verify application starts
- Test critical paths
- Check logs for errors
- Verify database connections
- Monitor for issues
Migration Commands
# List migrations
dotnet ef migrations list
# Add migration
dotnet ef migrations add MigrationName
# Apply migrations
dotnet ef database update
# Rollback to specific migration
dotnet ef database update PreviousMigrationName
# Generate SQL script (for review)
dotnet ef migrations script > migration.sql
Testing and Validation
Manual Testing
Test Case 1: Update Draft PR
# Should succeed
curl -X PUT "http://localhost:5200/api/purchase-requisitions/{draft-id}" \
-H "Content-Type: application/json" \
-d '{
"departmentCode": "IT",
"purpose": "Updated purpose",
"lines": [...]
}'
# Expected: 200 OK
Test Case 2: Update Approved PR
# Should fail
curl -X PUT "http://localhost:5200/api/purchase-requisitions/{approved-id}" \
-H "Content-Type: application/json" \
-d '{
"purpose": "Trying to change approved PR"
}'
# Expected: 403 Forbidden with PR_IMMUTABLE_STATUS error code
Test Case 3: Verify Audit Logging
# Attempt to modify approved PR (should fail)
curl -X PUT "http://localhost:5200/api/purchase-requisitions/{approved-id}" \
-H "Content-Type: application/json" \
-d '{"purpose": "Test"}'
# Check audit logs
curl "http://localhost:5200/api/purchase-requisitions/{approved-id}/audit-logs"
# Verify log entry exists with:
# - action: "UPDATE_ATTEMPT"
# - wasSuccessful: false
# - statusAtAttempt: "Approved"
Automated Testing
Unit Test Example
[Fact]
public async Task UpdateRequisitionAsync_WhenStatusIsApproved_ThrowsImmutableDocumentException()
{
// Arrange
var approvedPR = new PurchaseRequisition
{
Id = Guid.NewGuid(),
Status = PurchaseRequisitionStatus.Approved
};
_mockRepository.Setup(r => r.GetByIdAsync(It.IsAny<Guid>()))
.ReturnsAsync(approvedPR);
// Act & Assert
await Assert.ThrowsAsync<ImmutableDocumentException>(
() => _service.UpdateRequisitionAsync(approvedPR.Id, new UpdatePurchaseRequisitionDto())
);
}
Integration Test Example
[Fact]
public async Task PUT_ApprovedPR_Returns403Forbidden()
{
// Arrange
var approvedPR = await CreateApprovedPRAsync();
var updateDto = new UpdatePurchaseRequisitionDto
{
Purpose = "Trying to update"
};
// Act
var response = await _client.PutAsJsonAsync(
$"/api/purchase-requisitions/{approvedPR.Id}",
updateDto
);
// Assert
Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode);
var error = await response.Content.ReadFromJsonAsync<ErrorResponse>();
Assert.Equal("PR_IMMUTABLE_STATUS", error.ErrorCode);
}
Performance Testing
# Load test with Apache Bench
ab -n 1000 -c 10 -p pr-update.json -T application/json \
http://localhost:5200/api/purchase-requisitions/{id}
# Monitor response times
# Expected: < 100ms for status validation
# Expected: < 200ms for full update operation
Appendix
File Reference
Backend:
/piam-api/src/PiamMasterData.Domain/Exceptions/ImmutableDocumentException.cs/piam-api/src/PiamMasterData.Api/Controllers/PurchaseRequisitionsController.cs:120-143/piam-api/src/PiamMasterData.Infrastructure/Repositories/PurchaseRequisitionRepository.cs:304-311/piam-api/src/PiamMasterData.Infrastructure/Services/PurchaseRequisitionAuditService.cs/piam-api/src/PiamMasterData.Infrastructure/DependencyInjection.cs:90
Frontend:
/piam-web/src/app/features/procurement/components/purchase-requisition-form/purchase-requisition-form.component.ts:186-203/piam-web/src/app/features/procurement/components/purchase-requisition-form/purchase-requisition-form.component.html:12-75
Database:
/piam-api/src/PiamMasterData.Infrastructure/Migrations/20251029204955_AddPurchaseRequisitionAuditLog.cs
Related Documentation
- User Guide:
/docs/user-guide-pr-approval.md - Process Guide:
/docs/pr-after-approval-guide.md - FAQ:
/docs/pr-approval-faq.md - Requirements:
/docs/pr-approval-immutability-requirement.md
Useful Commands
# Check application version
dotnet --version
# Check database version
psql --version
# View running processes
dotnet run --no-build &
# Check database connection
psql -h localhost -U user -d database -c "SELECT version();"
# View application logs
tail -f /var/log/piam-api/application.log
# Check disk space
df -h
# Check database size
psql -h localhost -U user -d database -c "SELECT pg_size_pretty(pg_database_size('database'));"
Support and Escalation
L1 Support (Help Desk)
- User cannot edit Draft PR → Clear cache
- Page won't load → Refresh browser
- Buttons missing → Check PR status
L2 Support (System Administrator)
- Audit logs not appearing → Check migration
- Performance issues → Check database indexes
- Configuration problems → Review settings
L3 Support (Development Team)
- Logic bugs → Code review
- Security issues → Immediate escalation
- Data integrity issues → Database investigation
Critical Issues (Immediate Escalation)
- Approved PRs being modified
- Audit log tampering
- Security breaches
- Data corruption
Document End
For operational procedures and user assistance, refer to the User Guide and FAQ documents.