diff --git a/backend/src/db/api/users.js b/backend/src/db/api/users.js index 251dcd9..1ecccb9 100644 --- a/backend/src/db/api/users.js +++ b/backend/src/db/api/users.js @@ -407,7 +407,13 @@ module.exports = class UsersDBApi { output.employee_pay_types_employee = await users.getEmployee_pay_types_employee({ - transaction + transaction, + include: [ + { + model: db.pay_types, + as: 'pay_type', + }, + ], }); @@ -501,6 +507,21 @@ module.exports = class UsersDBApi { as: 'avatar', }, + { + model: db.employee_pay_types, + as: 'employee_pay_types_employee', + required: false, + where: { + active: true, + }, + include: [ + { + model: db.pay_types, + as: 'pay_type', + }, + ], + }, + ]; if (filter) { diff --git a/backend/src/services/users.js b/backend/src/services/users.js index 555d193..e8093a1 100644 --- a/backend/src/services/users.js +++ b/backend/src/services/users.js @@ -13,6 +13,63 @@ const EmailSender = require('./email'); const AuthService = require('./auth'); module.exports = class UsersService { + static normalizePayTypeIds(payTypes = []) { + return [...new Set((payTypes || []) + .map((payType) => { + if (typeof payType === 'string') { + return payType; + } + + return payType?.id || payType?.value || null; + }) + .filter(Boolean))]; + } + + static async syncEmployeePayTypes(employeeId, payTypes, currentUser, transaction) { + const payTypeIds = UsersService.normalizePayTypeIds(payTypes); + const selectedPayTypeIds = new Set(payTypeIds); + + const existingAssignments = await db.employee_pay_types.findAll({ + where: { employeeId }, + transaction, + }); + + const existingPayTypeIds = new Set(); + + for (const assignment of existingAssignments) { + existingPayTypeIds.add(assignment.pay_typeId); + + const shouldBeActive = selectedPayTypeIds.has(assignment.pay_typeId); + + if (assignment.active !== shouldBeActive) { + await assignment.update( + { + active: shouldBeActive, + updatedById: currentUser.id, + }, + { transaction }, + ); + } + } + + for (const payTypeId of payTypeIds) { + if (existingPayTypeIds.has(payTypeId)) { + continue; + } + + await db.employee_pay_types.create( + { + employeeId, + pay_typeId: payTypeId, + active: true, + createdById: currentUser.id, + updatedById: currentUser.id, + }, + { transaction }, + ); + } + } + static async create(data, currentUser, sendInvitationEmails = true, host) { let transaction = await db.sequelize.transaction(); @@ -26,7 +83,7 @@ module.exports = class UsersService { 'iam.errors.userAlreadyExists', ); } else { - await UsersDBApi.create( + const createdUser = await UsersDBApi.create( {data}, { @@ -34,6 +91,15 @@ module.exports = class UsersService { transaction, }, ); + + if (data.pay_types !== undefined) { + await UsersService.syncEmployeePayTypes( + createdUser.id, + data.pay_types, + currentUser, + transaction, + ); + } emailsToInvite.push(email); } } else { @@ -127,6 +193,15 @@ module.exports = class UsersService { }, ); + if (data.pay_types !== undefined) { + await UsersService.syncEmployeePayTypes( + id, + data.pay_types, + currentUser, + transaction, + ); + } + await transaction.commit(); return updatedUser; diff --git a/frontend/src/components/Users/configureUsersCols.tsx b/frontend/src/components/Users/configureUsersCols.tsx index 37cebd3..b53c5eb 100644 --- a/frontend/src/components/Users/configureUsersCols.tsx +++ b/frontend/src/components/Users/configureUsersCols.tsx @@ -17,6 +17,15 @@ import {hasPermission} from "../../helpers/userPermissions"; type Params = (id: string) => void; +const formatAssignedPayTypes = (assignments = []) => { + const uniquePayTypeNames = [...new Set((assignments || []) + .filter((assignment) => assignment?.active !== false) + .map((assignment) => assignment?.pay_type?.name) + .filter(Boolean))]; + + return uniquePayTypeNames.length ? uniquePayTypeNames.join(', ') : '—'; +}; + export const loadColumns = async ( onDelete: Params, entityName: string, @@ -180,6 +189,27 @@ export const loadColumns = async ( }, + { + field: 'employee_pay_types_employee', + headerName: 'Pay Types', + flex: 1, + minWidth: 180, + filterable: false, + headerClassName: 'datagrid--header', + cellClassName: 'datagrid--cell', + + editable: false, + sortable: false, + valueGetter: (params: GridValueGetterParams) => + formatAssignedPayTypes(params?.row?.employee_pay_types_employee), + renderCell: (params: GridValueGetterParams) => ( + + {formatAssignedPayTypes(params?.row?.employee_pay_types_employee)} + + ), + + }, + { field: 'actions', type: 'actions', diff --git a/frontend/src/pages/users/users-edit.tsx b/frontend/src/pages/users/users-edit.tsx index c6fb274..de84708 100644 --- a/frontend/src/pages/users/users-edit.tsx +++ b/frontend/src/pages/users/users-edit.tsx @@ -261,6 +261,7 @@ const EditUsersPage = () => { custom_permissions: [], + pay_types: [], @@ -278,16 +279,17 @@ const EditUsersPage = () => { dispatch(fetch({ id: id })) }, [id]) - useEffect(() => { - if (typeof users === 'object') { - setInitialValues(users) - } - }, [users]) - useEffect(() => { if (typeof users === 'object') { const newInitialVal = {...initVals}; - Object.keys(initVals).forEach(el => newInitialVal[el] = (users)[el]) + Object.keys(initVals).forEach((el) => { + newInitialVal[el] = users[el] + }) + newInitialVal.pay_types = Array.isArray(users.employee_pay_types_employee) + ? users.employee_pay_types_employee + .filter((item) => item?.active && item?.pay_type) + .map((item) => item.pay_type) + : [] setInitialValues(newInitialVal); } }, [users]) @@ -676,6 +678,18 @@ const EditUsersPage = () => { + + + + + diff --git a/frontend/src/pages/users/users-new.tsx b/frontend/src/pages/users/users-new.tsx index 510a85b..997eb4e 100644 --- a/frontend/src/pages/users/users-new.tsx +++ b/frontend/src/pages/users/users-new.tsx @@ -155,6 +155,7 @@ const initialValues = { custom_permissions: [], + pay_types: [], } @@ -469,7 +470,16 @@ const UsersNew = () => { - + + + + diff --git a/frontend/src/pages/users/users-view.tsx b/frontend/src/pages/users/users-view.tsx index 96dc691..4bb6477 100644 --- a/frontend/src/pages/users/users-view.tsx +++ b/frontend/src/pages/users/users-view.tsx @@ -433,6 +433,7 @@ const UsersView = () => { + Pay Type Active @@ -456,6 +457,9 @@ const UsersView = () => { + + {item?.pay_type?.name ?? 'No data'} + { dataFormatter.booleanFormatter(item.active) }