Добавлены модели и функциональность для управления сотрудниками, включая создание, обновление и удаление сотрудников через GraphQL. Обновлены компоненты для отображения списка сотрудников и их расписания, улучшен интерфейс взаимодействия с пользователем. Реализованы функции экспорта отчетов в CSV и TXT форматах, добавлены новые интерфейсы и типы данных для сотрудников.
This commit is contained in:
@ -874,4 +874,78 @@ export const REMOVE_FROM_FAVORITES = gql`
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
// Мутации для сотрудников
|
||||
export const CREATE_EMPLOYEE = gql`
|
||||
mutation CreateEmployee($input: CreateEmployeeInput!) {
|
||||
createEmployee(input: $input) {
|
||||
success
|
||||
message
|
||||
employee {
|
||||
id
|
||||
firstName
|
||||
lastName
|
||||
middleName
|
||||
birthDate
|
||||
avatar
|
||||
position
|
||||
department
|
||||
hireDate
|
||||
salary
|
||||
status
|
||||
phone
|
||||
email
|
||||
emergencyContact
|
||||
emergencyPhone
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const UPDATE_EMPLOYEE = gql`
|
||||
mutation UpdateEmployee($id: ID!, $input: UpdateEmployeeInput!) {
|
||||
updateEmployee(id: $id, input: $input) {
|
||||
success
|
||||
message
|
||||
employee {
|
||||
id
|
||||
firstName
|
||||
lastName
|
||||
middleName
|
||||
birthDate
|
||||
avatar
|
||||
passportSeries
|
||||
passportNumber
|
||||
passportIssued
|
||||
passportDate
|
||||
address
|
||||
position
|
||||
department
|
||||
hireDate
|
||||
salary
|
||||
status
|
||||
phone
|
||||
email
|
||||
emergencyContact
|
||||
emergencyPhone
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const DELETE_EMPLOYEE = gql`
|
||||
mutation DeleteEmployee($id: ID!) {
|
||||
deleteEmployee(id: $id)
|
||||
}
|
||||
`
|
||||
|
||||
export const UPDATE_EMPLOYEE_SCHEDULE = gql`
|
||||
mutation UpdateEmployeeSchedule($input: UpdateScheduleInput!) {
|
||||
updateEmployeeSchedule(input: $input)
|
||||
}
|
||||
`
|
@ -476,4 +476,66 @@ export const GET_MY_FAVORITES = gql`
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
// Запросы для сотрудников
|
||||
export const GET_MY_EMPLOYEES = gql`
|
||||
query GetMyEmployees {
|
||||
myEmployees {
|
||||
id
|
||||
firstName
|
||||
lastName
|
||||
middleName
|
||||
birthDate
|
||||
avatar
|
||||
passportSeries
|
||||
passportNumber
|
||||
passportIssued
|
||||
passportDate
|
||||
address
|
||||
position
|
||||
department
|
||||
hireDate
|
||||
salary
|
||||
status
|
||||
phone
|
||||
email
|
||||
telegram
|
||||
whatsapp
|
||||
passportPhoto
|
||||
emergencyContact
|
||||
emergencyPhone
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const GET_EMPLOYEE = gql`
|
||||
query GetEmployee($id: ID!) {
|
||||
employee(id: $id) {
|
||||
id
|
||||
firstName
|
||||
lastName
|
||||
middleName
|
||||
birthDate
|
||||
avatar
|
||||
passportSeries
|
||||
passportNumber
|
||||
passportIssued
|
||||
passportDate
|
||||
address
|
||||
position
|
||||
department
|
||||
hireDate
|
||||
salary
|
||||
status
|
||||
phone
|
||||
email
|
||||
emergencyContact
|
||||
emergencyPhone
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
`
|
@ -676,6 +676,72 @@ export const resolvers = {
|
||||
})
|
||||
|
||||
return favorites.map(favorite => favorite.product)
|
||||
},
|
||||
|
||||
// Сотрудники организации
|
||||
myEmployees: async (_: unknown, __: unknown, context: Context) => {
|
||||
if (!context.user) {
|
||||
throw new GraphQLError('Требуется авторизация', {
|
||||
extensions: { code: 'UNAUTHENTICATED' }
|
||||
})
|
||||
}
|
||||
|
||||
const currentUser = await prisma.user.findUnique({
|
||||
where: { id: context.user.id },
|
||||
include: { organization: true }
|
||||
})
|
||||
|
||||
if (!currentUser?.organization) {
|
||||
throw new GraphQLError('У пользователя нет организации')
|
||||
}
|
||||
|
||||
if (currentUser.organization.type !== 'FULFILLMENT') {
|
||||
throw new GraphQLError('Доступно только для фулфилмент центров')
|
||||
}
|
||||
|
||||
const employees = await prisma.employee.findMany({
|
||||
where: { organizationId: currentUser.organization.id },
|
||||
include: {
|
||||
organization: true
|
||||
},
|
||||
orderBy: { createdAt: 'desc' }
|
||||
})
|
||||
|
||||
return employees
|
||||
},
|
||||
|
||||
// Получение сотрудника по ID
|
||||
employee: async (_: unknown, args: { id: string }, context: Context) => {
|
||||
if (!context.user) {
|
||||
throw new GraphQLError('Требуется авторизация', {
|
||||
extensions: { code: 'UNAUTHENTICATED' }
|
||||
})
|
||||
}
|
||||
|
||||
const currentUser = await prisma.user.findUnique({
|
||||
where: { id: context.user.id },
|
||||
include: { organization: true }
|
||||
})
|
||||
|
||||
if (!currentUser?.organization) {
|
||||
throw new GraphQLError('У пользователя нет организации')
|
||||
}
|
||||
|
||||
if (currentUser.organization.type !== 'FULFILLMENT') {
|
||||
throw new GraphQLError('Доступно только для фулфилмент центров')
|
||||
}
|
||||
|
||||
const employee = await prisma.employee.findFirst({
|
||||
where: {
|
||||
id: args.id,
|
||||
organizationId: currentUser.organization.id
|
||||
},
|
||||
include: {
|
||||
organization: true
|
||||
}
|
||||
})
|
||||
|
||||
return employee
|
||||
}
|
||||
},
|
||||
|
||||
@ -3041,6 +3107,206 @@ export const resolvers = {
|
||||
message: 'Ошибка при удалении из избранного'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Создать сотрудника
|
||||
createEmployee: async (_: unknown, args: { input: any }, context: Context) => {
|
||||
if (!context.user) {
|
||||
throw new GraphQLError('Требуется авторизация', {
|
||||
extensions: { code: 'UNAUTHENTICATED' }
|
||||
})
|
||||
}
|
||||
|
||||
const currentUser = await prisma.user.findUnique({
|
||||
where: { id: context.user.id },
|
||||
include: { organization: true }
|
||||
})
|
||||
|
||||
if (!currentUser?.organization) {
|
||||
throw new GraphQLError('У пользователя нет организации')
|
||||
}
|
||||
|
||||
if (currentUser.organization.type !== 'FULFILLMENT') {
|
||||
throw new GraphQLError('Доступно только для фулфилмент центров')
|
||||
}
|
||||
|
||||
try {
|
||||
const employee = await prisma.employee.create({
|
||||
data: {
|
||||
...args.input,
|
||||
organizationId: currentUser.organization.id,
|
||||
birthDate: args.input.birthDate ? new Date(args.input.birthDate) : undefined,
|
||||
passportDate: args.input.passportDate ? new Date(args.input.passportDate) : undefined,
|
||||
hireDate: new Date(args.input.hireDate)
|
||||
},
|
||||
include: {
|
||||
organization: true
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: 'Сотрудник успешно добавлен',
|
||||
employee
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error creating employee:', error)
|
||||
return {
|
||||
success: false,
|
||||
message: 'Ошибка при создании сотрудника'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Обновить сотрудника
|
||||
updateEmployee: async (_: unknown, args: { id: string; input: any }, context: Context) => {
|
||||
if (!context.user) {
|
||||
throw new GraphQLError('Требуется авторизация', {
|
||||
extensions: { code: 'UNAUTHENTICATED' }
|
||||
})
|
||||
}
|
||||
|
||||
const currentUser = await prisma.user.findUnique({
|
||||
where: { id: context.user.id },
|
||||
include: { organization: true }
|
||||
})
|
||||
|
||||
if (!currentUser?.organization) {
|
||||
throw new GraphQLError('У пользователя нет организации')
|
||||
}
|
||||
|
||||
if (currentUser.organization.type !== 'FULFILLMENT') {
|
||||
throw new GraphQLError('Доступно только для фулфилмент центров')
|
||||
}
|
||||
|
||||
try {
|
||||
const employee = await prisma.employee.update({
|
||||
where: {
|
||||
id: args.id,
|
||||
organizationId: currentUser.organization.id
|
||||
},
|
||||
data: {
|
||||
...args.input,
|
||||
birthDate: args.input.birthDate ? new Date(args.input.birthDate) : undefined,
|
||||
passportDate: args.input.passportDate ? new Date(args.input.passportDate) : undefined,
|
||||
hireDate: args.input.hireDate ? new Date(args.input.hireDate) : undefined
|
||||
},
|
||||
include: {
|
||||
organization: true
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: 'Сотрудник успешно обновлен',
|
||||
employee
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error updating employee:', error)
|
||||
return {
|
||||
success: false,
|
||||
message: 'Ошибка при обновлении сотрудника'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Удалить сотрудника
|
||||
deleteEmployee: async (_: unknown, args: { id: string }, context: Context) => {
|
||||
if (!context.user) {
|
||||
throw new GraphQLError('Требуется авторизация', {
|
||||
extensions: { code: 'UNAUTHENTICATED' }
|
||||
})
|
||||
}
|
||||
|
||||
const currentUser = await prisma.user.findUnique({
|
||||
where: { id: context.user.id },
|
||||
include: { organization: true }
|
||||
})
|
||||
|
||||
if (!currentUser?.organization) {
|
||||
throw new GraphQLError('У пользователя нет организации')
|
||||
}
|
||||
|
||||
if (currentUser.organization.type !== 'FULFILLMENT') {
|
||||
throw new GraphQLError('Доступно только для фулфилмент центров')
|
||||
}
|
||||
|
||||
try {
|
||||
await prisma.employee.delete({
|
||||
where: {
|
||||
id: args.id,
|
||||
organizationId: currentUser.organization.id
|
||||
}
|
||||
})
|
||||
|
||||
return true
|
||||
} catch (error) {
|
||||
console.error('Error deleting employee:', error)
|
||||
return false
|
||||
}
|
||||
},
|
||||
|
||||
// Обновить табель сотрудника
|
||||
updateEmployeeSchedule: async (_: unknown, args: { input: any }, context: Context) => {
|
||||
if (!context.user) {
|
||||
throw new GraphQLError('Требуется авторизация', {
|
||||
extensions: { code: 'UNAUTHENTICATED' }
|
||||
})
|
||||
}
|
||||
|
||||
const currentUser = await prisma.user.findUnique({
|
||||
where: { id: context.user.id },
|
||||
include: { organization: true }
|
||||
})
|
||||
|
||||
if (!currentUser?.organization) {
|
||||
throw new GraphQLError('У пользователя нет организации')
|
||||
}
|
||||
|
||||
if (currentUser.organization.type !== 'FULFILLMENT') {
|
||||
throw new GraphQLError('Доступно только для фулфилмент центров')
|
||||
}
|
||||
|
||||
try {
|
||||
// Проверяем что сотрудник принадлежит организации
|
||||
const employee = await prisma.employee.findFirst({
|
||||
where: {
|
||||
id: args.input.employeeId,
|
||||
organizationId: currentUser.organization.id
|
||||
}
|
||||
})
|
||||
|
||||
if (!employee) {
|
||||
throw new GraphQLError('Сотрудник не найден')
|
||||
}
|
||||
|
||||
// Создаем или обновляем запись табеля
|
||||
await prisma.employeeSchedule.upsert({
|
||||
where: {
|
||||
employeeId_date: {
|
||||
employeeId: args.input.employeeId,
|
||||
date: new Date(args.input.date)
|
||||
}
|
||||
},
|
||||
create: {
|
||||
employeeId: args.input.employeeId,
|
||||
date: new Date(args.input.date),
|
||||
status: args.input.status,
|
||||
hoursWorked: args.input.hoursWorked,
|
||||
notes: args.input.notes
|
||||
},
|
||||
update: {
|
||||
status: args.input.status,
|
||||
hoursWorked: args.input.hoursWorked,
|
||||
notes: args.input.notes
|
||||
}
|
||||
})
|
||||
|
||||
return true
|
||||
} catch (error) {
|
||||
console.error('Error updating employee schedule:', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -3120,5 +3386,61 @@ export const resolvers = {
|
||||
}
|
||||
return parent.updatedAt
|
||||
}
|
||||
},
|
||||
|
||||
Employee: {
|
||||
birthDate: (parent: { birthDate?: Date | string | null }) => {
|
||||
if (!parent.birthDate) return null
|
||||
if (parent.birthDate instanceof Date) {
|
||||
return parent.birthDate.toISOString()
|
||||
}
|
||||
return parent.birthDate
|
||||
},
|
||||
passportDate: (parent: { passportDate?: Date | string | null }) => {
|
||||
if (!parent.passportDate) return null
|
||||
if (parent.passportDate instanceof Date) {
|
||||
return parent.passportDate.toISOString()
|
||||
}
|
||||
return parent.passportDate
|
||||
},
|
||||
hireDate: (parent: { hireDate: Date | string }) => {
|
||||
if (parent.hireDate instanceof Date) {
|
||||
return parent.hireDate.toISOString()
|
||||
}
|
||||
return parent.hireDate
|
||||
},
|
||||
createdAt: (parent: { createdAt: Date | string }) => {
|
||||
if (parent.createdAt instanceof Date) {
|
||||
return parent.createdAt.toISOString()
|
||||
}
|
||||
return parent.createdAt
|
||||
},
|
||||
updatedAt: (parent: { updatedAt: Date | string }) => {
|
||||
if (parent.updatedAt instanceof Date) {
|
||||
return parent.updatedAt.toISOString()
|
||||
}
|
||||
return parent.updatedAt
|
||||
}
|
||||
},
|
||||
|
||||
EmployeeSchedule: {
|
||||
date: (parent: { date: Date | string }) => {
|
||||
if (parent.date instanceof Date) {
|
||||
return parent.date.toISOString()
|
||||
}
|
||||
return parent.date
|
||||
},
|
||||
createdAt: (parent: { createdAt: Date | string }) => {
|
||||
if (parent.createdAt instanceof Date) {
|
||||
return parent.createdAt.toISOString()
|
||||
}
|
||||
return parent.createdAt
|
||||
},
|
||||
updatedAt: (parent: { updatedAt: Date | string }) => {
|
||||
if (parent.updatedAt instanceof Date) {
|
||||
return parent.updatedAt.toISOString()
|
||||
}
|
||||
return parent.updatedAt
|
||||
}
|
||||
}
|
||||
}
|
@ -43,6 +43,10 @@ export const typeDefs = gql`
|
||||
|
||||
# Избранные товары пользователя
|
||||
myFavorites: [Product!]!
|
||||
|
||||
# Сотрудники организации
|
||||
myEmployees: [Employee!]!
|
||||
employee(id: ID!): Employee
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
@ -107,6 +111,12 @@ export const typeDefs = gql`
|
||||
# Работа с избранным
|
||||
addToFavorites(productId: ID!): FavoritesResponse!
|
||||
removeFromFavorites(productId: ID!): FavoritesResponse!
|
||||
|
||||
# Работа с сотрудниками
|
||||
createEmployee(input: CreateEmployeeInput!): EmployeeResponse!
|
||||
updateEmployee(id: ID!, input: UpdateEmployeeInput!): EmployeeResponse!
|
||||
deleteEmployee(id: ID!): Boolean!
|
||||
updateEmployeeSchedule(input: UpdateScheduleInput!): Boolean!
|
||||
}
|
||||
|
||||
# Типы данных
|
||||
@ -470,6 +480,132 @@ export const typeDefs = gql`
|
||||
favorites: [Product!]
|
||||
}
|
||||
|
||||
# Типы для сотрудников
|
||||
type Employee {
|
||||
id: ID!
|
||||
firstName: String!
|
||||
lastName: String!
|
||||
middleName: String
|
||||
birthDate: String
|
||||
avatar: String
|
||||
passportPhoto: String
|
||||
passportSeries: String
|
||||
passportNumber: String
|
||||
passportIssued: String
|
||||
passportDate: String
|
||||
address: String
|
||||
position: String!
|
||||
department: String
|
||||
hireDate: String!
|
||||
salary: Float
|
||||
status: EmployeeStatus!
|
||||
phone: String!
|
||||
email: String
|
||||
telegram: String
|
||||
whatsapp: String
|
||||
emergencyContact: String
|
||||
emergencyPhone: String
|
||||
scheduleRecords: [EmployeeSchedule!]!
|
||||
organization: Organization!
|
||||
createdAt: String!
|
||||
updatedAt: String!
|
||||
}
|
||||
|
||||
enum EmployeeStatus {
|
||||
ACTIVE
|
||||
VACATION
|
||||
SICK
|
||||
FIRED
|
||||
}
|
||||
|
||||
type EmployeeSchedule {
|
||||
id: ID!
|
||||
date: String!
|
||||
status: ScheduleStatus!
|
||||
hoursWorked: Float
|
||||
notes: String
|
||||
employee: Employee!
|
||||
createdAt: String!
|
||||
updatedAt: String!
|
||||
}
|
||||
|
||||
enum ScheduleStatus {
|
||||
WORK
|
||||
WEEKEND
|
||||
VACATION
|
||||
SICK
|
||||
ABSENT
|
||||
}
|
||||
|
||||
input CreateEmployeeInput {
|
||||
firstName: String!
|
||||
lastName: String!
|
||||
middleName: String
|
||||
birthDate: String
|
||||
avatar: String
|
||||
passportPhoto: String
|
||||
passportSeries: String
|
||||
passportNumber: String
|
||||
passportIssued: String
|
||||
passportDate: String
|
||||
address: String
|
||||
position: String!
|
||||
department: String
|
||||
hireDate: String!
|
||||
salary: Float
|
||||
phone: String!
|
||||
email: String
|
||||
telegram: String
|
||||
whatsapp: String
|
||||
emergencyContact: String
|
||||
emergencyPhone: String
|
||||
}
|
||||
|
||||
input UpdateEmployeeInput {
|
||||
firstName: String
|
||||
lastName: String
|
||||
middleName: String
|
||||
birthDate: String
|
||||
avatar: String
|
||||
passportPhoto: String
|
||||
passportSeries: String
|
||||
passportNumber: String
|
||||
passportIssued: String
|
||||
passportDate: String
|
||||
address: String
|
||||
position: String
|
||||
department: String
|
||||
hireDate: String
|
||||
salary: Float
|
||||
status: EmployeeStatus
|
||||
phone: String
|
||||
email: String
|
||||
telegram: String
|
||||
whatsapp: String
|
||||
emergencyContact: String
|
||||
emergencyPhone: String
|
||||
}
|
||||
|
||||
input UpdateScheduleInput {
|
||||
employeeId: ID!
|
||||
date: String!
|
||||
status: ScheduleStatus!
|
||||
hoursWorked: Float
|
||||
notes: String
|
||||
}
|
||||
|
||||
type EmployeeResponse {
|
||||
success: Boolean!
|
||||
message: String!
|
||||
employee: Employee
|
||||
}
|
||||
|
||||
type EmployeesResponse {
|
||||
success: Boolean!
|
||||
message: String!
|
||||
employees: [Employee!]!
|
||||
}
|
||||
|
||||
# JSON скаляр
|
||||
scalar JSON
|
||||
`
|
Reference in New Issue
Block a user