Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
H
hrsystem
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
hrsystem
Commits
ec80f55d
Commit
ec80f55d
authored
Apr 01, 2026
by
Administrator
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Update 22 files via Son of Anton
parent
0b0931ce
Changes
22
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
22 changed files
with
1933 additions
and
0 deletions
+1933
-0
app.module.ts
backend/src/app.module.ts
+11
-0
contracts.controller.ts
backend/src/modules/contracts/contracts.controller.ts
+66
-0
contracts.module.ts
backend/src/modules/contracts/contracts.module.ts
+10
-0
contracts.service.ts
backend/src/modules/contracts/contracts.service.ts
+187
-0
contract-filter.dto.ts
backend/src/modules/contracts/dto/contract-filter.dto.ts
+24
-0
contract-response.dto.ts
backend/src/modules/contracts/dto/contract-response.dto.ts
+22
-0
update-contract.dto.ts
backend/src/modules/contracts/dto/update-contract.dto.ts
+23
-0
create-termination.dto.ts
...end/src/modules/offboarding/dto/create-termination.dto.ts
+20
-0
termination-filter.dto.ts
...end/src/modules/offboarding/dto/termination-filter.dto.ts
+24
-0
offboarding.controller.ts
backend/src/modules/offboarding/offboarding.controller.ts
+72
-0
offboarding.module.ts
backend/src/modules/offboarding/offboarding.module.ts
+12
-0
offboarding.service.ts
backend/src/modules/offboarding/offboarding.service.ts
+457
-0
pdf.controller.ts
backend/src/modules/pdf/pdf.controller.ts
+59
-0
pdf.module.ts
backend/src/modules/pdf/pdf.module.ts
+10
-0
pdf.service.ts
backend/src/modules/pdf/pdf.service.ts
+262
-0
create-policy.dto.ts
backend/src/modules/policies/dto/create-policy.dto.ts
+20
-0
policy-filter.dto.ts
backend/src/modules/policies/dto/policy-filter.dto.ts
+19
-0
update-policy.dto.ts
backend/src/modules/policies/dto/update-policy.dto.ts
+26
-0
policies.controller.ts
backend/src/modules/policies/policies.controller.ts
+83
-0
policies.module.ts
backend/src/modules/policies/policies.module.ts
+12
-0
policies.service.ts
backend/src/modules/policies/policies.service.ts
+428
-0
schema-offboarding.prisma
prisma/schema-offboarding.prisma
+86
-0
No files found.
backend/src/app.module.ts
View file @
ec80f55d
...
...
@@ -63,6 +63,12 @@ import { ApiKeysModule } from './modules/api-keys/api-keys.module';
import
{
WebhooksModule
}
from
'./modules/webhooks/webhooks.module'
;
import
{
SearchModule
}
from
'./modules/search/search.module'
;
// ─── Phase 3C: Documents & Offboarding ──────────────────────
import
{
ContractsModule
}
from
'./modules/contracts/contracts.module'
;
import
{
PoliciesModule
}
from
'./modules/policies/policies.module'
;
import
{
OffboardingModule
}
from
'./modules/offboarding/offboarding.module'
;
import
{
PdfModule
}
from
'./modules/pdf/pdf.module'
;
import
{
JwtAuthGuard
}
from
'./common/guards/jwt-auth.guard'
;
import
{
RolesGuard
}
from
'./common/guards/roles.guard'
;
import
{
TransformInterceptor
}
from
'./common/interceptors/transform.interceptor'
;
...
...
@@ -122,6 +128,11 @@ import { RateLimitMiddleware } from './common/middleware/rate-limit.middleware';
ApiKeysModule
,
WebhooksModule
,
SearchModule
,
// Phase 3C
ContractsModule
,
PoliciesModule
,
OffboardingModule
,
PdfModule
,
],
providers
:
[
{
provide
:
APP_GUARD
,
useClass
:
JwtAuthGuard
},
...
...
backend/src/modules/contracts/contracts.controller.ts
0 → 100644
View file @
ec80f55d
import
{
Controller
,
Get
,
Put
,
Delete
,
Body
,
Param
,
Query
,
HttpCode
,
HttpStatus
,
}
from
'@nestjs/common'
;
import
{
ContractsService
}
from
'./contracts.service'
;
import
{
ContractFilterDto
}
from
'./dto/contract-filter.dto'
;
import
{
UpdateContractMetadataDto
}
from
'./dto/update-contract.dto'
;
import
{
CurrentUser
,
RequestUser
}
from
'../../common/decorators/current-user.decorator'
;
import
{
Roles
}
from
'../../common/decorators/roles.decorator'
;
@
Controller
(
'contracts'
)
export
class
ContractsController
{
constructor
(
private
readonly
contractsService
:
ContractsService
)
{}
@
Get
()
async
findAll
(@
Query
()
filter
:
ContractFilterDto
,
@
CurrentUser
()
user
:
RequestUser
)
{
return
this
.
contractsService
.
findAll
(
filter
,
user
);
}
@
Get
(
'expiring'
)
@
Roles
(
'SUPER_ADMIN'
,
'ADMIN'
)
async
getExpiring
(@
Query
(
'days'
)
days
:
string
,
@
CurrentUser
()
user
:
RequestUser
)
{
const
daysOut
=
days
?
parseInt
(
days
,
10
)
:
90
;
return
this
.
contractsService
.
getExpiringContracts
(
daysOut
,
user
);
}
@
Get
(
'user/:userId'
)
async
findByUser
(@
Param
(
'userId'
)
userId
:
string
,
@
CurrentUser
()
user
:
RequestUser
)
{
return
this
.
contractsService
.
findByUserId
(
userId
,
user
);
}
@
Get
(
':id'
)
async
findById
(@
Param
(
'id'
)
id
:
string
,
@
CurrentUser
()
user
:
RequestUser
)
{
return
this
.
contractsService
.
findById
(
id
,
user
);
}
@
Get
(
':id/snapshot'
)
async
getSnapshot
(@
Param
(
'id'
)
id
:
string
,
@
CurrentUser
()
user
:
RequestUser
)
{
return
this
.
contractsService
.
getContractSnapshot
(
id
,
user
);
}
@
Put
(
':id'
)
@
Roles
(
'SUPER_ADMIN'
)
async
updateMetadata
(
@
Param
(
'id'
)
id
:
string
,
@
Body
()
dto
:
UpdateContractMetadataDto
,
@
CurrentUser
()
user
:
RequestUser
,
)
{
return
this
.
contractsService
.
updateMetadata
(
id
,
dto
,
user
);
}
@
Delete
(
':id'
)
@
Roles
(
'SUPER_ADMIN'
)
@
HttpCode
(
HttpStatus
.
OK
)
async
delete
(@
Param
(
'id'
)
id
:
string
,
@
CurrentUser
()
user
:
RequestUser
)
{
await
this
.
contractsService
.
delete
(
id
,
user
);
return
{
message
:
'Contract record deleted'
};
}
}
\ No newline at end of file
backend/src/modules/contracts/contracts.module.ts
0 → 100644
View file @
ec80f55d
import
{
Module
}
from
'@nestjs/common'
;
import
{
ContractsController
}
from
'./contracts.controller'
;
import
{
ContractsService
}
from
'./contracts.service'
;
@
Module
({
controllers
:
[
ContractsController
],
providers
:
[
ContractsService
],
exports
:
[
ContractsService
],
})
export
class
ContractsModule
{}
\ No newline at end of file
backend/src/modules/contracts/contracts.service.ts
0 → 100644
View file @
ec80f55d
import
{
Injectable
,
NotFoundException
,
ForbiddenException
,
BadRequestException
,
Logger
,
}
from
'@nestjs/common'
;
import
{
PrismaService
}
from
'../../prisma/prisma.service'
;
import
{
ContractFilterDto
}
from
'./dto/contract-filter.dto'
;
import
{
UpdateContractMetadataDto
}
from
'./dto/update-contract.dto'
;
import
{
RequestUser
}
from
'../../common/decorators/current-user.decorator'
;
import
{
getSkip
,
buildPaginatedResponse
,
PaginatedResult
}
from
'../../common/utils/pagination.util'
;
@
Injectable
()
export
class
ContractsService
{
private
readonly
logger
=
new
Logger
(
ContractsService
.
name
);
constructor
(
private
readonly
prisma
:
PrismaService
)
{}
async
findAll
(
filter
:
ContractFilterDto
,
currentUser
:
RequestUser
):
Promise
<
PaginatedResult
<
any
>>
{
const
page
=
filter
.
page
||
1
;
const
limit
=
filter
.
limit
||
20
;
const
where
:
any
=
{};
if
(
currentUser
.
role
===
'CONTRACTOR'
)
{
where
.
userId
=
currentUser
.
id
;
}
if
(
currentUser
.
role
===
'TEAM_LEAD'
)
{
throw
new
ForbiddenException
(
'Project Leaders cannot access contract records'
);
}
if
(
filter
.
userId
)
where
.
userId
=
filter
.
userId
;
if
(
filter
.
status
)
where
.
status
=
filter
.
status
;
if
(
filter
.
contractType
)
where
.
contractType
=
filter
.
contractType
;
if
(
filter
.
expiringBefore
||
filter
.
expiringAfter
)
{
where
.
endDate
=
{};
if
(
filter
.
expiringBefore
)
where
.
endDate
.
lte
=
new
Date
(
filter
.
expiringBefore
);
if
(
filter
.
expiringAfter
)
where
.
endDate
.
gte
=
new
Date
(
filter
.
expiringAfter
);
}
const
[
data
,
total
]
=
await
Promise
.
all
([
this
.
prisma
.
contract
.
findMany
({
where
,
skip
:
getSkip
(
page
,
limit
),
take
:
limit
,
orderBy
:
{
createdAt
:
filter
.
sortOrder
||
'desc'
},
include
:
{
user
:
{
select
:
{
id
:
true
,
firstName
:
true
,
lastName
:
true
,
avatar
:
true
,
contractorType
:
true
}
},
},
}),
this
.
prisma
.
contract
.
count
({
where
}),
]);
const
sanitized
=
data
.
map
((
c
:
any
)
=>
{
if
(
currentUser
.
role
===
'CONTRACTOR'
)
{
const
{
notes
,
...
rest
}
=
c
;
return
rest
;
}
return
c
;
});
return
buildPaginatedResponse
(
sanitized
,
total
,
{
page
,
limit
,
sortOrder
:
filter
.
sortOrder
||
'desc'
});
}
async
findById
(
id
:
string
,
currentUser
:
RequestUser
):
Promise
<
any
>
{
const
contract
=
await
this
.
prisma
.
contract
.
findUnique
({
where
:
{
id
},
include
:
{
user
:
{
select
:
{
id
:
true
,
firstName
:
true
,
lastName
:
true
,
avatar
:
true
,
contractorType
:
true
,
email
:
true
}
},
},
});
if
(
!
contract
)
throw
new
NotFoundException
(
'Contract not found'
);
if
(
currentUser
.
role
===
'CONTRACTOR'
&&
contract
.
userId
!==
currentUser
.
id
)
{
throw
new
ForbiddenException
(
'You can only view your own contracts'
);
}
if
(
currentUser
.
role
===
'TEAM_LEAD'
)
{
throw
new
ForbiddenException
(
'Project Leaders cannot access contract records'
);
}
return
contract
;
}
async
findByUserId
(
userId
:
string
,
currentUser
:
RequestUser
):
Promise
<
any
[]
>
{
if
(
currentUser
.
role
===
'CONTRACTOR'
&&
userId
!==
currentUser
.
id
)
{
throw
new
ForbiddenException
(
'You can only view your own contracts'
);
}
if
(
currentUser
.
role
===
'TEAM_LEAD'
)
{
throw
new
ForbiddenException
(
'Project Leaders cannot access contract records'
);
}
return
this
.
prisma
.
contract
.
findMany
({
where
:
{
userId
},
orderBy
:
{
createdAt
:
'desc'
},
include
:
{
user
:
{
select
:
{
id
:
true
,
firstName
:
true
,
lastName
:
true
}
},
},
});
}
async
updateMetadata
(
id
:
string
,
dto
:
UpdateContractMetadataDto
,
currentUser
:
RequestUser
):
Promise
<
any
>
{
if
(
currentUser
.
role
!==
'SUPER_ADMIN'
)
{
throw
new
ForbiddenException
(
'Only Super Admin can edit contract metadata'
);
}
const
contract
=
await
this
.
prisma
.
contract
.
findUnique
({
where
:
{
id
}
});
if
(
!
contract
)
throw
new
NotFoundException
(
'Contract not found'
);
const
updateData
:
any
=
{};
if
(
dto
.
contractType
!==
undefined
)
updateData
.
contractType
=
dto
.
contractType
;
if
(
dto
.
startDate
!==
undefined
)
updateData
.
startDate
=
new
Date
(
dto
.
startDate
);
if
(
dto
.
endDate
!==
undefined
)
updateData
.
endDate
=
dto
.
endDate
?
new
Date
(
dto
.
endDate
)
:
null
;
if
(
dto
.
notes
!==
undefined
)
updateData
.
notes
=
dto
.
notes
;
if
(
dto
.
status
!==
undefined
)
updateData
.
status
=
dto
.
status
;
const
updated
=
await
this
.
prisma
.
contract
.
update
({
where
:
{
id
},
data
:
updateData
,
include
:
{
user
:
{
select
:
{
id
:
true
,
firstName
:
true
,
lastName
:
true
}
},
},
});
this
.
logger
.
log
(
`Contract
${
id
}
metadata updated by
${
currentUser
.
email
}
`
);
return
updated
;
}
async
delete
(
id
:
string
,
currentUser
:
RequestUser
):
Promise
<
void
>
{
if
(
currentUser
.
role
!==
'SUPER_ADMIN'
)
{
throw
new
ForbiddenException
(
'Only Super Admin can delete contract records'
);
}
const
contract
=
await
this
.
prisma
.
contract
.
findUnique
({
where
:
{
id
}
});
if
(
!
contract
)
throw
new
NotFoundException
(
'Contract not found'
);
await
this
.
prisma
.
contract
.
delete
({
where
:
{
id
}
});
this
.
logger
.
log
(
`Contract
${
id
}
deleted by
${
currentUser
.
email
}
`
);
}
async
getExpiringContracts
(
daysOut
:
number
,
currentUser
:
RequestUser
):
Promise
<
any
[]
>
{
if
(
currentUser
.
role
!==
'SUPER_ADMIN'
&&
currentUser
.
role
!==
'ADMIN'
)
{
throw
new
ForbiddenException
(
'Only Super Admin and Admin can view expiring contracts'
);
}
const
targetDate
=
new
Date
();
targetDate
.
setDate
(
targetDate
.
getDate
()
+
daysOut
);
return
this
.
prisma
.
contract
.
findMany
({
where
:
{
endDate
:
{
lte
:
targetDate
,
gte
:
new
Date
()
},
status
:
'ACTIVE'
,
},
include
:
{
user
:
{
select
:
{
id
:
true
,
firstName
:
true
,
lastName
:
true
,
avatar
:
true
,
status
:
true
}
},
},
orderBy
:
{
endDate
:
'asc'
},
});
}
async
getContractSnapshot
(
id
:
string
,
currentUser
:
RequestUser
):
Promise
<
{
html
:
string
;
signatureData
:
any
}
>
{
const
contract
=
await
this
.
prisma
.
contract
.
findUnique
({
where
:
{
id
},
select
:
{
userId
:
true
,
snapshotHtml
:
true
,
signatureData
:
true
},
});
if
(
!
contract
)
throw
new
NotFoundException
(
'Contract not found'
);
if
(
currentUser
.
role
===
'CONTRACTOR'
&&
contract
.
userId
!==
currentUser
.
id
)
{
throw
new
ForbiddenException
(
'You can only view your own contracts'
);
}
if
(
currentUser
.
role
===
'TEAM_LEAD'
)
{
throw
new
ForbiddenException
(
'Project Leaders cannot access contracts'
);
}
return
{
html
:
contract
.
snapshotHtml
||
''
,
signatureData
:
contract
.
signatureData
||
null
,
};
}
}
\ No newline at end of file
backend/src/modules/contracts/dto/contract-filter.dto.ts
0 → 100644
View file @
ec80f55d
import
{
IsOptional
,
IsString
,
IsDateString
}
from
'class-validator'
;
import
{
PaginationDto
}
from
'../../../common/dto/pagination.dto'
;
export
class
ContractFilterDto
extends
PaginationDto
{
@
IsOptional
()
@
IsString
()
userId
?:
string
;
@
IsOptional
()
@
IsString
()
status
?:
string
;
@
IsOptional
()
@
IsString
()
contractType
?:
string
;
@
IsOptional
()
@
IsDateString
()
expiringBefore
?:
string
;
@
IsOptional
()
@
IsDateString
()
expiringAfter
?:
string
;
}
\ No newline at end of file
backend/src/modules/contracts/dto/contract-response.dto.ts
0 → 100644
View file @
ec80f55d
export
class
ContractResponseDto
{
id
:
string
;
userId
:
string
;
contractType
:
string
;
status
:
string
;
startDate
:
string
|
null
;
endDate
:
string
|
null
;
signedAt
:
string
|
null
;
snapshotHtml
:
string
|
null
;
baseSalaryAtSigning
:
number
|
null
;
actualSalaryAtSigning
:
number
|
null
;
scheduleAtSigning
:
any
;
signatureData
:
any
;
notes
:
string
|
null
;
user
:
{
id
:
string
;
firstName
:
string
;
lastName
:
string
;
};
createdAt
:
string
;
updatedAt
:
string
;
}
\ No newline at end of file
backend/src/modules/contracts/dto/update-contract.dto.ts
0 → 100644
View file @
ec80f55d
import
{
IsOptional
,
IsString
,
IsDateString
}
from
'class-validator'
;
export
class
UpdateContractMetadataDto
{
@
IsOptional
()
@
IsString
()
contractType
?:
string
;
@
IsOptional
()
@
IsDateString
()
startDate
?:
string
;
@
IsOptional
()
@
IsDateString
()
endDate
?:
string
;
@
IsOptional
()
@
IsString
()
notes
?:
string
;
@
IsOptional
()
@
IsString
()
status
?:
string
;
}
\ No newline at end of file
backend/src/modules/offboarding/dto/create-termination.dto.ts
0 → 100644
View file @
ec80f55d
import
{
IsString
,
IsOptional
,
IsDateString
,
MinLength
}
from
'class-validator'
;
export
class
CreateTerminationDto
{
@
IsString
()
userId
:
string
;
@
IsString
()
terminationType
:
string
;
// VOLUNTARY, FOR_CAUSE, MUTUAL_AGREEMENT, CONTRACT_EXPIRY
@
IsString
()
@
MinLength
(
100
,
{
message
:
'Termination reason must be at least 100 characters'
})
reason
:
string
;
@
IsDateString
()
effectiveDate
:
string
;
@
IsOptional
()
@
IsString
()
notes
?:
string
;
}
\ No newline at end of file
backend/src/modules/offboarding/dto/termination-filter.dto.ts
0 → 100644
View file @
ec80f55d
import
{
IsOptional
,
IsString
,
IsDateString
}
from
'class-validator'
;
import
{
PaginationDto
}
from
'../../../common/dto/pagination.dto'
;
export
class
TerminationFilterDto
extends
PaginationDto
{
@
IsOptional
()
@
IsString
()
userId
?:
string
;
@
IsOptional
()
@
IsString
()
terminationType
?:
string
;
@
IsOptional
()
@
IsString
()
status
?:
string
;
@
IsOptional
()
@
IsDateString
()
dateFrom
?:
string
;
@
IsOptional
()
@
IsDateString
()
dateTo
?:
string
;
}
\ No newline at end of file
backend/src/modules/offboarding/offboarding.controller.ts
0 → 100644
View file @
ec80f55d
import
{
Controller
,
Get
,
Post
,
Put
,
Delete
,
Body
,
Param
,
Query
,
HttpCode
,
HttpStatus
,
}
from
'@nestjs/common'
;
import
{
OffboardingService
}
from
'./offboarding.service'
;
import
{
CreateTerminationDto
}
from
'./dto/create-termination.dto'
;
import
{
TerminationFilterDto
}
from
'./dto/termination-filter.dto'
;
import
{
CurrentUser
,
RequestUser
}
from
'../../common/decorators/current-user.decorator'
;
import
{
Roles
}
from
'../../common/decorators/roles.decorator'
;
@
Controller
(
'offboarding'
)
export
class
OffboardingController
{
constructor
(
private
readonly
offboardingService
:
OffboardingService
)
{}
@
Post
()
@
Roles
(
'SUPER_ADMIN'
,
'ADMIN'
)
async
initiate
(@
Body
()
dto
:
CreateTerminationDto
,
@
CurrentUser
()
user
:
RequestUser
)
{
return
this
.
offboardingService
.
initiate
(
dto
,
user
);
}
@
Get
()
@
Roles
(
'SUPER_ADMIN'
,
'ADMIN'
)
async
findAll
(@
Query
()
filter
:
TerminationFilterDto
,
@
CurrentUser
()
user
:
RequestUser
)
{
return
this
.
offboardingService
.
findAll
(
filter
,
user
);
}
@
Get
(
':id'
)
async
findById
(@
Param
(
'id'
)
id
:
string
,
@
CurrentUser
()
user
:
RequestUser
)
{
return
this
.
offboardingService
.
findById
(
id
,
user
);
}
@
Put
(
':id/checklist/:itemId'
)
@
Roles
(
'SUPER_ADMIN'
,
'ADMIN'
)
@
HttpCode
(
HttpStatus
.
OK
)
async
updateChecklist
(
@
Param
(
'id'
)
id
:
string
,
@
Param
(
'itemId'
)
itemId
:
string
,
@
Body
(
'completed'
)
completed
:
boolean
,
@
CurrentUser
()
user
:
RequestUser
,
)
{
return
this
.
offboardingService
.
updateChecklist
(
id
,
itemId
,
completed
,
user
);
}
@
Post
(
':id/complete'
)
@
Roles
(
'SUPER_ADMIN'
)
@
HttpCode
(
HttpStatus
.
OK
)
async
complete
(@
Param
(
'id'
)
id
:
string
,
@
CurrentUser
()
user
:
RequestUser
)
{
return
this
.
offboardingService
.
complete
(
id
,
user
);
}
@
Put
(
':id'
)
@
Roles
(
'SUPER_ADMIN'
)
async
update
(@
Param
(
'id'
)
id
:
string
,
@
Body
()
data
:
any
,
@
CurrentUser
()
user
:
RequestUser
)
{
return
this
.
offboardingService
.
update
(
id
,
data
,
user
);
}
@
Delete
(
':id'
)
@
Roles
(
'SUPER_ADMIN'
)
@
HttpCode
(
HttpStatus
.
OK
)
async
delete
(@
Param
(
'id'
)
id
:
string
,
@
CurrentUser
()
user
:
RequestUser
)
{
await
this
.
offboardingService
.
delete
(
id
,
user
);
return
{
message
:
'Termination record deleted'
};
}
}
\ No newline at end of file
backend/src/modules/offboarding/offboarding.module.ts
0 → 100644
View file @
ec80f55d
import
{
Module
}
from
'@nestjs/common'
;
import
{
OffboardingController
}
from
'./offboarding.controller'
;
import
{
OffboardingService
}
from
'./offboarding.service'
;
import
{
NotificationsModule
}
from
'../notifications/notifications.module'
;
@
Module
({
imports
:
[
NotificationsModule
],
controllers
:
[
OffboardingController
],
providers
:
[
OffboardingService
],
exports
:
[
OffboardingService
],
})
export
class
OffboardingModule
{}
\ No newline at end of file
backend/src/modules/offboarding/offboarding.service.ts
0 → 100644
View file @
ec80f55d
This diff is collapsed.
Click to expand it.
backend/src/modules/pdf/pdf.controller.ts
0 → 100644
View file @
ec80f55d
import
{
Controller
,
Get
,
Param
,
Query
,
Res
,
}
from
'@nestjs/common'
;
import
{
Response
}
from
'express'
;
import
{
PdfService
}
from
'./pdf.service'
;
import
{
CurrentUser
,
RequestUser
}
from
'../../common/decorators/current-user.decorator'
;
import
{
Roles
}
from
'../../common/decorators/roles.decorator'
;
@
Controller
(
'pdf'
)
export
class
PdfController
{
constructor
(
private
readonly
pdfService
:
PdfService
)
{}
@
Get
(
'payslip/:userId'
)
async
getPayslipData
(
@
Param
(
'userId'
)
userId
:
string
,
@
Query
(
'month'
)
month
:
string
,
@
Query
(
'year'
)
year
:
string
,
@
CurrentUser
()
user
:
RequestUser
,
)
{
const
m
=
month
?
parseInt
(
month
,
10
)
:
new
Date
().
getMonth
()
+
1
;
const
y
=
year
?
parseInt
(
year
,
10
)
:
new
Date
().
getFullYear
();
return
this
.
pdfService
.
generatePayslipData
(
userId
,
m
,
y
,
user
);
}
@
Get
(
'payslip/my'
)
async
getMyPayslipData
(
@
Query
(
'month'
)
month
:
string
,
@
Query
(
'year'
)
year
:
string
,
@
CurrentUser
()
user
:
RequestUser
,
)
{
const
m
=
month
?
parseInt
(
month
,
10
)
:
new
Date
().
getMonth
()
+
1
;
const
y
=
year
?
parseInt
(
year
,
10
)
:
new
Date
().
getFullYear
();
return
this
.
pdfService
.
generatePayslipData
(
user
.
id
,
m
,
y
,
user
);
}
@
Get
(
'evaluation/:evaluationId'
)
async
getEvaluationData
(
@
Param
(
'evaluationId'
)
evaluationId
:
string
,
@
CurrentUser
()
user
:
RequestUser
,
)
{
return
this
.
pdfService
.
generateEvaluationData
(
evaluationId
,
user
);
}
@
Get
(
'payroll-summary'
)
@
Roles
(
'SUPER_ADMIN'
,
'ADMIN'
)
async
getPayrollSummaryData
(
@
Query
(
'month'
)
month
:
string
,
@
Query
(
'year'
)
year
:
string
,
@
CurrentUser
()
user
:
RequestUser
,
)
{
const
m
=
month
?
parseInt
(
month
,
10
)
:
new
Date
().
getMonth
()
+
1
;
const
y
=
year
?
parseInt
(
year
,
10
)
:
new
Date
().
getFullYear
();
return
this
.
pdfService
.
generatePayrollSummaryData
(
m
,
y
,
user
);
}
}
\ No newline at end of file
backend/src/modules/pdf/pdf.module.ts
0 → 100644
View file @
ec80f55d
import
{
Module
}
from
'@nestjs/common'
;
import
{
PdfController
}
from
'./pdf.controller'
;
import
{
PdfService
}
from
'./pdf.service'
;
@
Module
({
controllers
:
[
PdfController
],
providers
:
[
PdfService
],
exports
:
[
PdfService
],
})
export
class
PdfModule
{}
\ No newline at end of file
backend/src/modules/pdf/pdf.service.ts
0 → 100644
View file @
ec80f55d
This diff is collapsed.
Click to expand it.
backend/src/modules/policies/dto/create-policy.dto.ts
0 → 100644
View file @
ec80f55d
import
{
IsString
,
IsOptional
,
IsBoolean
,
MinLength
,
MaxLength
}
from
'class-validator'
;
export
class
CreatePolicyDto
{
@
IsString
()
@
MinLength
(
2
)
@
MaxLength
(
200
)
name
:
string
;
@
IsString
()
@
MinLength
(
50
)
content
:
string
;
@
IsOptional
()
@
IsBoolean
()
requiresAcknowledgment
?:
boolean
;
@
IsOptional
()
@
IsString
()
category
?:
string
;
}
\ No newline at end of file
backend/src/modules/policies/dto/policy-filter.dto.ts
0 → 100644
View file @
ec80f55d
import
{
IsOptional
,
IsString
,
IsBoolean
}
from
'class-validator'
;
import
{
Type
}
from
'class-transformer'
;
import
{
PaginationDto
}
from
'../../../common/dto/pagination.dto'
;
export
class
PolicyFilterDto
extends
PaginationDto
{
@
IsOptional
()
@
IsString
()
category
?:
string
;
@
IsOptional
()
@
Type
(()
=>
Boolean
)
@
IsBoolean
()
isActive
?:
boolean
;
@
IsOptional
()
@
Type
(()
=>
Boolean
)
@
IsBoolean
()
requiresAcknowledgment
?:
boolean
;
}
\ No newline at end of file
backend/src/modules/policies/dto/update-policy.dto.ts
0 → 100644
View file @
ec80f55d
import
{
IsString
,
IsOptional
,
IsBoolean
,
MinLength
,
MaxLength
}
from
'class-validator'
;
export
class
UpdatePolicyDto
{
@
IsOptional
()
@
IsString
()
@
MinLength
(
2
)
@
MaxLength
(
200
)
name
?:
string
;
@
IsOptional
()
@
IsString
()
@
MinLength
(
50
)
content
?:
string
;
@
IsOptional
()
@
IsBoolean
()
requiresAcknowledgment
?:
boolean
;
@
IsOptional
()
@
IsBoolean
()
isActive
?:
boolean
;
@
IsOptional
()
@
IsString
()
category
?:
string
;
}
\ No newline at end of file
backend/src/modules/policies/policies.controller.ts
0 → 100644
View file @
ec80f55d
import
{
Controller
,
Get
,
Post
,
Put
,
Delete
,
Body
,
Param
,
Query
,
HttpCode
,
HttpStatus
,
}
from
'@nestjs/common'
;
import
{
PoliciesService
}
from
'./policies.service'
;
import
{
CreatePolicyDto
}
from
'./dto/create-policy.dto'
;
import
{
UpdatePolicyDto
}
from
'./dto/update-policy.dto'
;
import
{
PolicyFilterDto
}
from
'./dto/policy-filter.dto'
;
import
{
CurrentUser
,
RequestUser
}
from
'../../common/decorators/current-user.decorator'
;
import
{
Roles
}
from
'../../common/decorators/roles.decorator'
;
@
Controller
(
'policies'
)
export
class
PoliciesController
{
constructor
(
private
readonly
policiesService
:
PoliciesService
)
{}
@
Post
()
@
Roles
(
'SUPER_ADMIN'
)
async
create
(@
Body
()
dto
:
CreatePolicyDto
,
@
CurrentUser
()
user
:
RequestUser
)
{
return
this
.
policiesService
.
create
(
dto
,
user
);
}
@
Get
()
async
findAll
(@
Query
()
filter
:
PolicyFilterDto
,
@
CurrentUser
()
user
:
RequestUser
)
{
return
this
.
policiesService
.
findAll
(
filter
,
user
);
}
@
Get
(
'unacknowledged'
)
async
getUnacknowledged
(@
CurrentUser
()
user
:
RequestUser
)
{
return
this
.
policiesService
.
getUnacknowledgedPolicies
(
user
.
id
);
}
@
Get
(
':id'
)
async
findById
(@
Param
(
'id'
)
id
:
string
,
@
CurrentUser
()
user
:
RequestUser
)
{
return
this
.
policiesService
.
findById
(
id
,
user
);
}
@
Get
(
':id/versions'
)
@
Roles
(
'SUPER_ADMIN'
)
async
getVersions
(@
Param
(
'id'
)
id
:
string
,
@
CurrentUser
()
user
:
RequestUser
)
{
return
this
.
policiesService
.
getPreviousVersions
(
id
,
user
);
}
@
Get
(
':id/acknowledgments'
)
@
Roles
(
'SUPER_ADMIN'
,
'ADMIN'
)
async
getAcknowledgments
(@
Param
(
'id'
)
id
:
string
,
@
CurrentUser
()
user
:
RequestUser
)
{
return
this
.
policiesService
.
getAcknowledgmentStatus
(
id
,
user
);
}
@
Post
(
':id/acknowledge'
)
@
HttpCode
(
HttpStatus
.
OK
)
async
acknowledge
(@
Param
(
'id'
)
id
:
string
,
@
CurrentUser
()
user
:
RequestUser
)
{
return
this
.
policiesService
.
acknowledge
(
id
,
user
);
}
@
Put
(
':id'
)
@
Roles
(
'SUPER_ADMIN'
)
async
update
(@
Param
(
'id'
)
id
:
string
,
@
Body
()
dto
:
UpdatePolicyDto
,
@
CurrentUser
()
user
:
RequestUser
)
{
return
this
.
policiesService
.
update
(
id
,
dto
,
user
);
}
@
Post
(
':id/archive'
)
@
Roles
(
'SUPER_ADMIN'
)
@
HttpCode
(
HttpStatus
.
OK
)
async
archive
(@
Param
(
'id'
)
id
:
string
,
@
CurrentUser
()
user
:
RequestUser
)
{
return
this
.
policiesService
.
archive
(
id
,
user
);
}
@
Delete
(
':id'
)
@
Roles
(
'SUPER_ADMIN'
)
@
HttpCode
(
HttpStatus
.
OK
)
async
delete
(@
Param
(
'id'
)
id
:
string
,
@
CurrentUser
()
user
:
RequestUser
)
{
await
this
.
policiesService
.
delete
(
id
,
user
);
return
{
message
:
'Policy deleted'
};
}
}
\ No newline at end of file
backend/src/modules/policies/policies.module.ts
0 → 100644
View file @
ec80f55d
import
{
Module
}
from
'@nestjs/common'
;
import
{
PoliciesController
}
from
'./policies.controller'
;
import
{
PoliciesService
}
from
'./policies.service'
;
import
{
NotificationsModule
}
from
'../notifications/notifications.module'
;
@
Module
({
imports
:
[
NotificationsModule
],
controllers
:
[
PoliciesController
],
providers
:
[
PoliciesService
],
exports
:
[
PoliciesService
],
})
export
class
PoliciesModule
{}
\ No newline at end of file
backend/src/modules/policies/policies.service.ts
0 → 100644
View file @
ec80f55d
This diff is collapsed.
Click to expand it.
prisma/schema-offboarding.prisma
0 → 100644
View file @
ec80f55d
//
───
OFFBOARDING
&
DOCUMENT
MODELS
─────────────────────────
model
Termination
{
id
String
@
id
@
default
(
uuid
())
userId
String
user
User
@
relation
(
"UserTerminations"
,
fields
:
[
userId
],
references
:
[
id
],
onDelete
:
RESTRICT
)
terminationType
String
//
VOLUNTARY
,
FOR_CAUSE
,
MUTUAL_AGREEMENT
,
CONTRACT_EXPIRY
reason
String
effectiveDate
DateTime
noticeDate
DateTime
notes
String
?
status
String
@
default
(
"INITIATED"
)
//
INITIATED
,
CHECKLIST_COMPLETE
,
COMPLETED
,
CANCELLED
initiatedById
String
initiatedBy
User
@
relation
(
"TerminationInitiator"
,
fields
:
[
initiatedById
],
references
:
[
id
],
onDelete
:
RESTRICT
)
completedAt
DateTime
?
completedById
String
?
checklist
OffboardingChecklistItem
[]
createdAt
DateTime
@
default
(
now
())
updatedAt
DateTime
@
updatedAt
@@
index
([
userId
])
@@
index
([
status
])
@@
index
([
effectiveDate
])
}
model
OffboardingChecklistItem
{
id
String
@
id
@
default
(
uuid
())
terminationId
String
termination
Termination
@
relation
(
fields
:
[
terminationId
],
references
:
[
id
],
onDelete
:
CASCADE
)
title
String
verifiedBy
String
//
SYSTEM
,
SUPER_ADMIN
,
ADMIN
,
PROJECT_LEADER
position
Int
isCompleted
Boolean
@
default
(
false
)
completedAt
DateTime
?
completedById
String
?
createdAt
DateTime
@
default
(
now
())
updatedAt
DateTime
@
updatedAt
@@
index
([
terminationId
])
}
model
Policy
{
id
String
@
id
@
default
(
uuid
())
name
String
content
String
version
Int
@
default
(
1
)
category
String
@
default
(
"GENERAL"
)
requiresAcknowledgment
Boolean
@
default
(
true
)
isActive
Boolean
@
default
(
true
)
publishedAt
DateTime
?
createdById
String
createdBy
User
@
relation
(
"PolicyCreator"
,
fields
:
[
createdById
],
references
:
[
id
],
onDelete
:
RESTRICT
)
acknowledgments
PolicyAcknowledgment
[]
versions
PolicyVersion
[]
createdAt
DateTime
@
default
(
now
())
updatedAt
DateTime
@
updatedAt
@@
index
([
isActive
])
@@
index
([
category
])
}
model
PolicyVersion
{
id
String
@
id
@
default
(
uuid
())
policyId
String
policy
Policy
@
relation
(
fields
:
[
policyId
],
references
:
[
id
],
onDelete
:
CASCADE
)
version
Int
content
String
publishedAt
DateTime
?
createdAt
DateTime
@
default
(
now
())
@@
index
([
policyId
])
}
model
PolicyAcknowledgment
{
id
String
@
id
@
default
(
uuid
())
policyId
String
policy
Policy
@
relation
(
fields
:
[
policyId
],
references
:
[
id
],
onDelete
:
CASCADE
)
userId
String
user
User
@
relation
(
"PolicyAcknowledgments"
,
fields
:
[
userId
],
references
:
[
id
],
onDelete
:
CASCADE
)
version
Int
acknowledgedAt
DateTime
@
default
(
now
())
@@
unique
([
policyId
,
userId
,
version
])
@@
index
([
policyId
])
@@
index
([
userId
])
}
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment