Leave Management System
A self-hosted, multi-company leave platform that keeps requests, approvals, working calendars, entitlement, carryover, and yearly balances consistent.
Context
Live in production and under active development, the Leave Management System gives employees, assigned approvers, HR teams, and administrators one company-scoped workflow for leave. It supports configurable working weeks, public holidays, leave types, approval routing, calendars, email notifications, and employee/company balance views. Submitted and approved requests reserve capacity through explicit per-year allocations—even across calendar years—and decline or cancellation rebalances the affected years atomically.
Replace informal leave coordination with a shared system that gives employees a clear request and balance experience while giving managers and HR reliable, company-scoped approvals, calendars, policies, and records.
Leave rules interact across working-week configuration, holidays, employee hire dates, yearly entitlement, carryover, approval responsibility, overlaps, and lifecycle changes. The product centralizes those rules while keeping every tenant-facing operation within the active company.
- Keep employee, policy, request, calendar, and management operations scoped to the active company.
- Reserve capacity for submitted and approved requests and safely release it after decline or cancellation.
- Allocate cross-year requests against each affected year's working days, carryover, and entitlement.
- Respect company-specific work weeks, public holidays, hire-year accrual, carryover caps, and expiry.
- Resolve approval responsibility through explicit assignments with controlled HR/admin fallbacks.
- Isolate development, staging, and production data and file storage.
My role
I designed, built, deployed, and operate the Leave Management System across its Django application, server-rendered interface, domain model, access controls, and containerized environments.
Contribution boundaries
- Customer IdentityThe company currently using the product, its domain, and its operational data remain anonymous.
Product Direction
Product concept, requirements, policy behavior, and ongoing feature direction.
Application Engineering
Django backend, server-rendered interface, multi-company access, leave lifecycle, and balance allocation.
Integration
Account and leave workflow email through an environment-configured SMTP boundary.
Deployment And Operations
Isolated container environments and the self-managed production deployment.
Collaboration credit
- Product reviewersProvided feedback and minor conceptual input used to improve the product.
Architecture
System context
05 boundaries · 05 declared relationships
- 01Server-rendered employee and management interfaceClient
- 02Django applicationService
- 03PostgreSQL data storeDatastore
- 04SMTP email serviceExternal Provider
- 05Self-managed container hostInfrastructure
Relationship mapInspect 05 connectionsToggle architecture relationships
- Server-rendered employee and management interfaceRequestDjango application
- Django applicationPersistencePostgreSQL data store
- Django applicationNotificationSMTP email service
- Self-managed container hostHostingDjango application
- Self-managed container hostHostingPostgreSQL data store
Engineering decisions
01Make company scope an application invariant
Toggle decision details
Context
One deployment supports multiple organizations and identities can hold memberships in more than one company.
Selected approach
Resolve an active employee membership and enforce its company scope in models, forms, views, services, and tests.
Rationale
Preserve one maintainable product deployment without allowing access to another company's people, policies, or records.
Trade-offs
- Every query and mutation path needs deliberate scoping.
- Multi-company switching requires explicit active-membership state.
02Persist per-year allocations for cross-year leave
Toggle decision details
Context
One request can span years whose entitlement, carryover, expiry, and holidays differ.
Selected approach
Segment working days by year and persist carryover and entitlement consumption for every affected year.
Rationale
Keep balances explainable and make decline, cancellation, and future-year recalculation deterministic.
Trade-offs
- Lifecycle transitions can touch several balance records.
- Rebalancing and legacy compatibility need dedicated services and tests.
03Resolve approvals through assignments and controlled fallbacks
Toggle decision details
Context
Responsibility can belong to a primary approver, department default, or company-level HR/administration.
Selected approach
Centralize approver resolution and reuse it for actions, dashboard visibility, and notification recipients.
Rationale
Keep responsibility clear without granting every manager company-wide approval access.
Trade-offs
- Assignment changes affect authorization and notification delivery.
- Legacy assignments need compatibility handling.
04Rebalance from reserving requests instead of editing counters ad hoc
Toggle decision details
Context
Request order, carryover expiry, hire-year accrual, cancellations, and future-year effects all change availability.
Selected approach
Rebuild affected employee-year allocations deterministically inside database transactions.
Rationale
Make every lifecycle transition use one capacity model and roll back entirely when a year is insufficient.
Trade-offs
- Rebalancing writes derived records and must remain efficient as history grows.
- Legacy summary fields require synchronization until retired.
05Send workflow email after database commit
Toggle decision details
Context
Membership and leave lifecycle changes produce external messages.
Selected approach
Register SMTP delivery through transaction commit hooks and log provider failures.
Rationale
Notify people only after the corresponding application state is durable.
Trade-offs
- Delivery remains synchronous after commit.
- The current architecture has no durable email retry queue.
06Isolate deployment environments with separate Compose projects
Toggle decision details
Context
Staging must exercise production-like behavior without sharing state with production.
Selected approach
Keep distinct development, staging, and production stacks with separate names, volumes, ports, and file paths.
Rationale
Make deployment repeatable while preventing one environment from overriding another.
Trade-offs
- Environment definitions must be kept aligned.
- Host routing and secrets remain separate operational concerns.
Runtime ownership
- 01Containerization
Docker Compose separates development, staging, and production.
- 02Hosting
I operate the product on self-managed infrastructure.
- 03Storage
PostgreSQL volumes and application file paths are isolated by environment.
- 04Logging
Invalid leave-form context and email-delivery failures are logged.
- 05Notifications
Account and leave lifecycle email is dispatched after database commit.
- 06Email
SMTP configuration is supplied through environment variables.
- 07Deployment
Development uses Django's server while staging and production use Gunicorn in isolated stacks.
Inspect operational detail
- Each environment has a distinct Compose project, database volume, port, and file-storage path.
- I handle production deployment and ongoing operation.
Current state
- Current
The product is functioning in production for an anonymous company.
- Lesson
Treat tenant scope as an invariant shared by reads, mutations, authorization, and tests.
- Lesson
Persist the allocation detail required to explain and reverse policy-sensitive balance changes.
Depth B / Technical inspectionInspect supporting evidence
Domains, provider boundaries, and evidence classifications.
Toggle technical inspection
Inspect supporting evidence
Domain structure
- Company configurationWorking weeks, annual entitlement defaults, carryover policy, and public-holiday behavior.
- Departments and approval routingEmployee-specific primary approvers, department defaults, and HR/admin fallback authority.
- Employees and membershipsMulti-company memberships, employment dates, entitlement overrides, roles, activation, and profile settings.
- Multi-account accessActive-company selection plus optional linking and switching between authenticated accounts in one browser.
- Leave types and policiesPaid/unpaid types, approval requirements, entitlement effects, allowances, status, and calendar appearance.
- Leave request lifecycleDrafting, validation, submission, automatic or assigned approval, decline, cancellation, and overlap prevention.
- Entitlement, carryover, and balancesHire-year accrual, yearly entitlement, carryover caps and expiry, reservations, balance views, and future-year rebalancing.
- Holidays and calendarsCompany holidays, observed dates, working-day calculation, shared calendar display, filters, and team visibility.
- Notifications and account recoveryEmail invitations, password setup/reset, and post-commit leave lifecycle notifications.
Integrations
- Transactional EmailSMTP email delivery
Company invitations, password recovery, approver alerts, and approval, decline, or cancellation updates.
Environment-configured SMTP
Capability evidence
- Product and system ownershipI own the product, source code, engineering direction, deployment, and operation.
- System architectureI designed the company-scoped Django application and its domain boundaries.
- Frontend deliveryI implemented the responsive server-rendered employee and management interface.
- Domain modellingI model companies, memberships, policies, approvals, requests, yearly allocations, and balances.
- Authentication and authorizationI enforce active-company scope and explicit approval authority across workflows.
- Transactional workflowsI rebalance cross-year leave capacity atomically across submit, decline, and cancellation.
- Systems integrationI integrate account and leave lifecycle email through SMTP.
- Deployment and operationsI build, deploy, and operate isolated development, staging, and production environments.
- Automated testingI maintain regression coverage for permissions, lifecycle, balances, notifications, and account switching.