[FEAT] Full Payroll Module - PayrollRun, PayrollItem Entities & PayrollRunService (#2453)
Merged
RE
2mo ago+1957+1957+0123456789012345678901234567890123456789-0-0-0123456789
Summary
This PR implements the foundational Full Payroll Module for Ever Gauzy, resolving issue #2453. It introduces contracts, database entities, and a business-logic service covering the full payroll lifecycle.
Fixes #2453
What's Changed
1. packages/contracts/src/lib/payroll.model.ts (new)
- Enums:
PayrollRunStatusEnum(DRAFT → PENDING_APPROVAL → APPROVED → PROCESSING → PAID / CANCELLED),PayrollFrequencyEnum(WEEKLY / BI_WEEKLY / SEMI_MONTHLY / MONTHLY / QUARTERLY / ANNUALLY),PayrollItemTypeEnum(BASIC_SALARY, ALLOWANCE, BONUS, COMMISSION, OVERTIME, TAX_DEDUCTION, SOCIAL_SECURITY, HEALTH_INSURANCE, LOAN_DEDUCTION, ADVANCE_DEDUCTION, LEAVE_DEDUCTION, OTHER_ADDITION, OTHER_DEDUCTION),PayrollItemCategoryEnum(EARNING / DEDUCTION) -
- Interfaces:
IPayrollRun,IPayrollRunCreateInput,IPayrollRunUpdateInput,IPayrollRunFindInput,IPayrollItem,IPayrollItemCreateInput,IPayrollItemFindInput,IPayrollSummary,IPayrollStatistics
- Interfaces:
2. packages/core/src/lib/payroll-run/payroll-run.entity.ts (new)
- TypeORM entity mapped to
payroll_runtable -
- Extends
TenantOrganizationBaseEntity
- Extends
-
- Stores:
periodStart,periodEnd,payDate,frequency,status,currency, totals (gross/deductions/net), optionalnotes
- Stores:
-
@OneToManyrelation toPayrollItemwith cascade
3. packages/core/src/lib/payroll-item/payroll-item.entity.ts (new)
- TypeORM entity mapped to
payroll_itemtable -
- Extends
TenantOrganizationBaseEntity
- Extends
-
- Stores:
type,category,description,amount,quantity,unitPrice,taxable
- Stores:
-
@ManyToOnetoPayrollRun(cascade delete) andEmployee(SET NULL)
4. packages/core/src/lib/payroll-run/payroll-run.service.ts (new)
- NestJS
@Injectable()service -
- Methods:
create(),findAll()(with filters),findOne(),update(),submitForApproval(),approve(),process()(recalculates totals),cancel(),getSummaryByRun(),getStatistics()
- Methods:
-
- Status transition guards (throws
BadRequestExceptionon invalid transitions)
- Status transition guards (throws
-
- Automatic totals recalculation on
process()
- Automatic totals recalculation on
How It Works
DRAFT → PENDING_APPROVAL → APPROVED → PAID
↘ CANCELLED (any pre-PAID state)
- HR creates a payroll run (
create) — starts as DRAFT -
- Add payroll items (earnings + deductions) per employee
-
- Submit for approval (
submitForApproval)
- Submit for approval (
-
- Manager/finance approves (
approve)
- Manager/finance approves (
-
- System processes and marks as paid (
process) — totals recalculated automatically
- System processes and marks as paid (
Checklist
- Follows existing TypeORM entity conventions (
TenantOrganizationBaseEntity) - - [x] Uses
@gauzy/contractsfor all interface imports - - [x] Status lifecycle enforced with guards in service layer
- - [x] All commits reference issue (#2453)
- - [x] PR targets
ever-co/ever-gauzy:develop
Summary by cubic
Introduces the payroll module with PayrollRun, PayrollItem, PayrollRunService, and a /payroll-run REST controller covering the full run lifecycle, totals, per-employee summaries, and statistics. Implements #2453 with new ORG_PAYROLL_VIEW/ORG_PAYROLL_EDIT/ORG_PAYROLL_APPROVE permissions (default-granted to SUPER_ADMIN and ADMIN only) and additive migrations that create both tables and reload role permissions on existing tenants.
-
New Features
- Contracts in
@gauzy/contracts: enums and interfaces for runs, items, summary, and stats; entities registered incoreEntitiesand module wired intoAppModule; endpoints for CRUD, submit, approve, process, cancel, summaries, statistics, and line-item add/remove. - Entities
PayrollRunandPayrollItemwith relations andnumeric(14,2)money columns;PayrollRunServicehandles CRUD, status transitions, totals recomputed in integer cents, filtered queries, summaries, and statistics.
- Contracts in
-
Bug Fixes
- Hardened workflow: update() strips status and totals; cancel() is idempotent and blocks paid runs; all lookups are scoped by tenant and organization.
- Pagination: findAll() treats
pageas a page number and converts it to an offset. - Contracts: made
IPayrollItem.employeeIdoptional to match the entity;IPayrollItemCreateInputstill requires it.