Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.encoreos.io/llms.txt

Use this file to discover all available pages before exploring further.

Version: 1.0.0
Last Updated: 2026-01-19
Module: FA-19 Financial Close Management

Table of Contents

  1. Overview
  2. Permission Configuration
  3. Checklist Template Setup
  4. Integration Configuration
  5. Event Contracts
  6. API Contracts
  7. Security Considerations
  8. Troubleshooting

Overview

This guide covers the administrative configuration and technical details for the Financial Close Management module (FA-19). It is intended for system administrators, finance managers, and technical staff responsible for setting up and maintaining the close management process.

Module Architecture

FA-19 consists of four primary database tables:
TablePurpose
fa_close_periodsClose period header records (status, dates, approvals)
fa_close_checklistsChecklist templates and period-specific checklists
fa_close_tasksIndividual tasks within checklists
fa_close_documentationDocument attachments for periods and tasks

Key Dependencies

  • FA-02 (General Ledger): Fiscal period linking
  • FA-07 (Financial Reporting): Close status verification for reports
  • PF-10 (Notifications): Task assignments and approval alerts
  • PF-11 (Documents): File storage for attachments
  • FW-03 (Approval Workflow): Close period approval events

Permission Configuration

Permission Keys

FA-19 uses the following permission keys for access control:
Permission KeyDescriptionTypical Role
fa.close.viewView close periods and task statusAll finance staff
fa.close.createCreate new close periodsManager, Org Admin
fa.close.manageEdit templates, manage tasks, reopen periodsManager, Org Admin
fa.close.approveApprove or reject close periodsController, CFO, Org Admin

Role Assignment Matrix

Roleviewcreatemanageapprove
Staff Accountant
Senior Accountant
Accounting Manager
Controller
CFO
Org Admin

Configuring Permissions

  1. Navigate to Settings > Permissions
  2. Select the role to modify
  3. Under Finance & Accounting > Close Management:
    • Enable/disable each permission as needed
  4. Click Save Changes

Permission Check Pattern

In code, use the useHasPermission hook:
import { useHasPermission, PermissionGate } from '@/platform/permissions';

// Hook usage
const canApprove = useHasPermission('fa.close.approve');

// Component usage
<PermissionGate permission="fa.close.approve">
  <Button onClick={handleApprove}>Approve Period</Button>
</PermissionGate>

Checklist Template Setup

Standard Template Structure

Create templates for each close type:

Month-End Close Template

1. Pre-Close Tasks
   1.1 Review open transactions (Manual, Medium)
   1.2 Process pending journal entries (Manual, High)
   1.3 Complete bank reconciliations (Manual, High)
       └─ Depends on: 1.2

2. Accruals & Adjustments
   2.1 Record accrued expenses (Manual, High)
   2.2 Post prepaid amortization (Manual, Medium)
   2.3 Record depreciation (Automated, Medium)
   2.4 Review accrual entries (Review, High)
       └─ Depends on: 2.1, 2.2, 2.3

3. Reconciliation & Review
   3.1 Reconcile subledgers to GL (Manual, High)
   3.2 Verify intercompany balances (Manual, Medium)
   3.3 Review account reconciliations (Review, High)
       └─ Depends on: 3.1, 3.2

4. Reporting & Finalization
   4.1 Generate trial balance (Manual, Medium)
   4.2 Review financial statements (Review, High)
   4.3 Prepare variance analysis (Manual, Medium)
   4.4 Manager approval (Approval, Critical)
       └─ Depends on: 4.1, 4.2, 4.3

Quarter-End Close Template

Include all month-end tasks plus:
5. Quarter-End Specific
   5.1 Review quarterly accruals (Review, High)
   5.2 Prepare quarterly reports (Manual, High)
   5.3 Review budget vs actual (Review, Medium)
   5.4 Update cash flow forecast (Manual, Medium)
   5.5 Quarter-end approval (Approval, Critical)

Year-End Close Template

Include all quarter-end tasks plus:
6. Year-End Specific
   6.1 Physical inventory count (Manual, Critical)
   6.2 Review fixed asset depreciation (Review, High)
   6.3 Prepare audit schedules (Manual, Critical)
   6.4 Tax provision calculation (Manual, Critical)
   6.5 Year-end adjustments review (Review, Critical)
   6.6 Final audit preparation (Manual, High)
   6.7 CFO year-end approval (Approval, Critical)

Template Management Best Practices

  1. Version Control: Include version numbers in template names
  2. Annual Review: Review and update templates annually
  3. Document Changes: Add notes when updating templates
  4. Test New Templates: Create a test period before production use

Integration Configuration

FA-02 (General Ledger) Integration

Close periods can be linked to fiscal periods:
  • Linking: When creating a close period, select the corresponding fiscal period
  • Status Sync: Close period status does not automatically update fiscal period
  • Use Case: Financial reports can verify close period status before generating final statements

FA-07 (Financial Reporting) Integration

Financial Reporting checks close status:
// Example: Check if period is closed before final report
const { data: closeStatus } = await supabase
  .from('fa_close_periods')
  .select('status')
  .eq('fiscal_period_id', periodId)
  .single();

if (closeStatus?.status !== 'completed') {
  throw new Error('Period must be closed before generating final reports');
}

PF-10 (Notifications) Integration

Notifications are triggered for:
EventRecipientsNotification Type
Task AssignedAssigned userIn-app + email
Period Pending ApprovalUsers with fa.close.approveIn-app + email
Period ApprovedPeriod creator, task assigneesIn-app
Period RejectedPeriod creatorIn-app + email

PF-11 (Documents) Integration

Document storage uses the platform document service:
  • Bucket: fa-close-documents
  • Path Pattern: {organization_id}/{close_period_id}/{document_id}
  • Max File Size: 50MB per document
  • Allowed Types: PDF, Excel, Word, images

Event Contracts

FA-19 publishes the following events to the fa_events channel:

close_period_started

Trigger: When close period status changes to ‘in_progress’
{
  event_type: 'close_period_started';
  close_period_id: uuid;
  period_name: string;
  period_type: 'month' | 'quarter' | 'year';
  started_by: uuid;
  organization_id: uuid;
  timestamp: timestamptz;
}

close_period_completed

Trigger: When close period status changes to ‘completed’
{
  event_type: 'close_period_completed';
  close_period_id: uuid;
  period_name: string;
  fiscal_period_id: uuid | null;
  completed_by: uuid;
  organization_id: uuid;
  timestamp: timestamptz;
}

close_period_approved

Trigger: When close period status changes to ‘approved’
{
  event_type: 'close_period_approved';
  close_period_id: uuid;
  period_name: string;
  approved_by: uuid;
  organization_id: uuid;
  timestamp: timestamptz;
}

close_task_assigned

Trigger: When a task’s assigned_to field changes
{
  event_type: 'close_task_assigned';
  task_id: uuid;
  task_name: string;
  close_period_id: uuid;
  assigned_to: uuid;
  assigned_by: uuid;
  organization_id: uuid;
  timestamp: timestamptz;
}

close_task_completed

Trigger: When a task’s status changes to ‘completed’
{
  event_type: 'close_task_completed';
  task_id: uuid;
  task_name: string;
  close_period_id: uuid;
  completed_by: uuid;
  organization_id: uuid;
  timestamp: timestamptz;
}

Subscribing to Events

// Subscribe to close events
const channel = supabase.channel('fa_events');

channel.on('broadcast', { event: 'close_period_approved' }, (payload) => {
  // TODO: Replace with your app's structured logging
  // logger.info('Period approved:', payload);
  // Update UI or trigger downstream actions
});

channel.subscribe();

API Contracts

Close Period Approval API

Endpoint: /api/v1/fa/close/periods/{period_id}/approve
Method: POST
Permission: fa.close.approve
Request:
{
  approved: boolean;
  comments?: string;
}
Response:
{
  close_period_id: uuid;
  status: 'approved' | 'rejected';
  approved_at: timestamp;
  approved_by: uuid;
}
Error Codes:
  • 403: Access denied (no permission or wrong organization)
  • 404: Period not found
  • 409: Invalid status or incomplete tasks

Close Period Status API

Endpoint: /api/v1/fa/close/periods/{period_id}/status
Method: GET
Permission: fa.close.view
Response:
{
  close_period_id: uuid;
  period_name: string;
  status: CloseStatus;
  task_summary: {
    total: number;
    completed: number;
    skipped: number;
    in_progress: number;
    blocked: number;
    not_started: number;
  };
  completion_percentage: number;
  // ... additional fields
}
See API_CONTRACTS.md for complete documentation.

Security Considerations

Row-Level Security (RLS)

All FA-19 tables have RLS policies:
-- Example: fa_close_periods policy
CREATE POLICY "fa_close_periods_org_read" ON fa_close_periods
FOR SELECT USING (
  fa_has_org_access(organization_id, auth.uid())
);

CREATE POLICY "fa_close_periods_org_write" ON fa_close_periods
FOR ALL USING (
  fa_has_org_access(organization_id, auth.uid())
)
WITH CHECK (
  fa_has_org_access(organization_id, auth.uid())
);

Defense-in-Depth

All mutations include organization_id validation:
// Correct pattern
await supabase
  .from('fa_close_periods')
  .update({ status: 'in_progress' })
  .eq('id', periodId)
  .eq('organization_id', currentOrganization.id);  // Always include

Audit Trail

All close management actions are logged:
  • Task completion with user and timestamp
  • Status changes with actor information
  • Document uploads with user and timestamp
  • Approval decisions with comments

Locked Period Protection

Completed/locked periods cannot be modified:
-- Trigger prevents updates to locked periods
CREATE TRIGGER fa_close_periods_lock_check
BEFORE UPDATE ON fa_close_periods
FOR EACH ROW
WHEN (OLD.status IN ('completed', 'locked'))
EXECUTE FUNCTION fa_prevent_locked_period_update();

Task Dependency Validation

Circular dependencies are prevented:
-- Trigger validates task dependencies
CREATE TRIGGER fa_validate_task_dependency
BEFORE INSERT OR UPDATE ON fa_close_tasks
FOR EACH ROW
EXECUTE FUNCTION fa_validate_no_circular_dependency();

Troubleshooting

Common Administrative Issues

Users Can’t See Close Periods

Cause: Missing fa.close.view permission Solution:
  1. Check user’s role assignment
  2. Verify role has fa.close.view permission
  3. Clear browser cache and retry

Template Changes Not Reflected

Cause: Template changes don’t update existing periods Solution:
  1. Template changes only affect new periods
  2. For existing periods, manually add/update tasks
  3. Consider creating a new period with updated template

Approval Notification Not Sent

Cause: Notification service issue or user preferences Solution:
  1. Check notification service logs
  2. Verify user has email notifications enabled
  3. Check user’s notification preferences in profile

Period Stuck in “Pending Approval”

Cause: No users with fa.close.approve permission Solution:
  1. Verify users have approval permission
  2. Check if approvers are active in the organization
  3. Grant approval permission to appropriate user

Database Queries for Troubleshooting

Check Close Period Status

SELECT 
  cp.id,
  cp.period_name,
  cp.status,
  cp.organization_id,
  COUNT(ct.id) as total_tasks,
  COUNT(CASE WHEN ct.status = 'completed' THEN 1 END) as completed_tasks
FROM fa_close_periods cp
LEFT JOIN fa_close_checklists cc ON cc.close_period_id = cp.id
LEFT JOIN fa_close_tasks ct ON ct.checklist_id = cc.id
WHERE cp.id = '<period_id>'
GROUP BY cp.id;

Find Incomplete Tasks

SELECT 
  ct.id,
  ct.task_name,
  ct.status,
  ct.assigned_to,
  ct.depends_on_task_id
FROM fa_close_tasks ct
JOIN fa_close_checklists cc ON ct.checklist_id = cc.id
WHERE cc.close_period_id = '<period_id>'
  AND ct.status NOT IN ('completed', 'skipped')
ORDER BY ct.display_order;

Check Event Publishing

-- View recent close events (if logging enabled)
SELECT * FROM pg_stat_activity 
WHERE query LIKE '%pg_notify%fa_events%'
ORDER BY query_start DESC
LIMIT 10;


Document Version: 1.0.0
Last Updated: 2026-01-19
Next Review: 2026-04-19