2049 lines
65 KiB
TypeScript
2049 lines
65 KiB
TypeScript
|
|
import { LucideIcon } from 'lucide-react';
|
|||
|
|
|
|||
|
|
/** Реэкспорт из единого источника прав (constants/roleAccess.ts) */
|
|||
|
|
export type { UserRole } from './constants/roleAccess';
|
|||
|
|
|
|||
|
|
export interface User {
|
|||
|
|
id: string;
|
|||
|
|
userId?: string | number;
|
|||
|
|
name: string;
|
|||
|
|
givenName?: string | null;
|
|||
|
|
familyName?: string | null;
|
|||
|
|
email?: string;
|
|||
|
|
emailVerified?: boolean;
|
|||
|
|
phone?: string | null;
|
|||
|
|
role: UserRole;
|
|||
|
|
avatar: string | null;
|
|||
|
|
birthDate?: string | null;
|
|||
|
|
position?: string | null;
|
|||
|
|
/** Назначенные участки (несколько у мастера). */
|
|||
|
|
assignedDistrictIds?: string[] | null;
|
|||
|
|
assignedDistrictId?: string | null;
|
|||
|
|
messengerLogins?: { messenger: 'Max' | 'Telegram'; login: string }[];
|
|||
|
|
language?: string;
|
|||
|
|
theme?: string;
|
|||
|
|
notificationEmail?: boolean;
|
|||
|
|
notificationPush?: boolean;
|
|||
|
|
lastLogin?: string | null;
|
|||
|
|
createdAt?: string | null;
|
|||
|
|
updatedAt?: string | null;
|
|||
|
|
allowedSections?: string[];
|
|||
|
|
permissions?: string[] | null;
|
|||
|
|
scope?: 'all' | 'own_district';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface UserPreferences {
|
|||
|
|
language: string;
|
|||
|
|
theme: string;
|
|||
|
|
notificationEmail: boolean;
|
|||
|
|
notificationPush: boolean;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface NavItem {
|
|||
|
|
id: string;
|
|||
|
|
label: string;
|
|||
|
|
icon: LucideIcon;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface District {
|
|||
|
|
id: string;
|
|||
|
|
name: string;
|
|||
|
|
managerName: string;
|
|||
|
|
inventory?: BuildingInventoryItem[]; // Склад участка
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/** Справочник должностей (панель управления) */
|
|||
|
|
export interface Position {
|
|||
|
|
id: string;
|
|||
|
|
name: string;
|
|||
|
|
isManagerial: boolean;
|
|||
|
|
sortOrder?: number;
|
|||
|
|
createdAt?: string;
|
|||
|
|
updatedAt?: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface Employee {
|
|||
|
|
id: string;
|
|||
|
|
name: string; // ФИО
|
|||
|
|
position: string; // Должность
|
|||
|
|
phone: string; // Номер телефона
|
|||
|
|
status: 'active' | 'vacation' | 'inactive';
|
|||
|
|
salary: number;
|
|||
|
|
/** Назначенные участки (многие ко многим). Приоритет над assignedDistrictId. */
|
|||
|
|
assignedDistrictIds?: string[];
|
|||
|
|
/** Один участок — для обратной совместимости; первый из assignedDistrictIds или старое поле. */
|
|||
|
|
assignedDistrictId?: string | null; // Участок
|
|||
|
|
managerId?: string; // ID руководителя сотрудника
|
|||
|
|
birthDate?: string; // Дата рождения (ISO date string)
|
|||
|
|
messengerLogins?: { // Логины мессенджеров
|
|||
|
|
messenger: 'Max' | 'Telegram';
|
|||
|
|
login: string;
|
|||
|
|
}[];
|
|||
|
|
photoUrl?: string; // Фото сотрудника
|
|||
|
|
registrationDate?: string; // Дата регистрации (ISO date string)
|
|||
|
|
hrData?: { // Данные для HR и Бухгалтерии
|
|||
|
|
passportData?: {
|
|||
|
|
series: string;
|
|||
|
|
number: string;
|
|||
|
|
issuedBy: string;
|
|||
|
|
issuedDate: string;
|
|||
|
|
registrationAddress: string;
|
|||
|
|
};
|
|||
|
|
laborBook?: {
|
|||
|
|
number: string;
|
|||
|
|
series?: string;
|
|||
|
|
entries?: Array<{
|
|||
|
|
date: string;
|
|||
|
|
organization: string;
|
|||
|
|
position: string;
|
|||
|
|
}>;
|
|||
|
|
};
|
|||
|
|
certificates?: Array<{ // Заказ справок
|
|||
|
|
type: string; // Тип справки (2-НДФЛ, справка с места работы и т.д.)
|
|||
|
|
requestedDate: string;
|
|||
|
|
issuedDate?: string;
|
|||
|
|
status: 'requested' | 'issued' | 'ready';
|
|||
|
|
}>;
|
|||
|
|
otherDocuments?: Array<{
|
|||
|
|
name: string;
|
|||
|
|
type: string;
|
|||
|
|
date: string;
|
|||
|
|
fileUrl?: string;
|
|||
|
|
}>;
|
|||
|
|
accountingData?: { // Бухгалтерская информация
|
|||
|
|
inn?: string; // ИНН
|
|||
|
|
snils?: string; // СНИЛС
|
|||
|
|
bankName?: string; // Название банка
|
|||
|
|
bankAccount?: string; // Расчетный счет
|
|||
|
|
correspondentAccount?: string; // Корреспондентский счет
|
|||
|
|
bik?: string; // БИК
|
|||
|
|
taxId?: string; // КПП
|
|||
|
|
};
|
|||
|
|
contracts?: Array<{ // Характеристики договора
|
|||
|
|
id?: number;
|
|||
|
|
contractType: string; // Тип договора (трудовой, ГПХ, срочный и т.д.)
|
|||
|
|
contractNumber?: string; // Номер договора
|
|||
|
|
startDate: string; // Дата начала
|
|||
|
|
endDate?: string; // Дата окончания (для бессрочного - undefined)
|
|||
|
|
probationPeriodDays?: number; // Испытательный срок в днях
|
|||
|
|
workSchedule?: string; // График работы
|
|||
|
|
workMode?: string; // Режим работы (офис, удаленно, гибрид)
|
|||
|
|
contractTerms?: string; // Дополнительные условия
|
|||
|
|
}>;
|
|||
|
|
vacations?: EmployeeVacation[]; // Отпуска
|
|||
|
|
sickLeaves?: EmployeeSickLeave[]; // Больничные
|
|||
|
|
terminations?: EmployeeTermination[]; // Увольнения
|
|||
|
|
absences?: EmployeeAbsence[]; // Отгулы и прогулы
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Отпуск сотрудника
|
|||
|
|
export interface EmployeeVacation {
|
|||
|
|
id: number;
|
|||
|
|
employeeId: string;
|
|||
|
|
startDate: string; // ISO date string
|
|||
|
|
endDate: string; // ISO date string
|
|||
|
|
daysCount: number;
|
|||
|
|
vacationType: 'annual' | 'unpaid' | 'study' | 'maternity' | 'other'; // Тип отпуска
|
|||
|
|
status: 'planned' | 'approved' | 'active' | 'completed' | 'canceled';
|
|||
|
|
requiresApproval?: boolean; // Требуется ли согласование
|
|||
|
|
approvedBy?: string;
|
|||
|
|
approvedAt?: string; // ISO datetime string
|
|||
|
|
approvedSignature?: string; // Подпись руководителя
|
|||
|
|
rejectedBy?: string;
|
|||
|
|
rejectedAt?: string; // ISO datetime string
|
|||
|
|
rejectionReason?: string; // Причина отклонения
|
|||
|
|
notes?: string;
|
|||
|
|
createdAt: string; // ISO datetime string
|
|||
|
|
updatedAt: string; // ISO datetime string
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Больничный сотрудника
|
|||
|
|
export interface EmployeeSickLeave {
|
|||
|
|
id: number;
|
|||
|
|
employeeId: string;
|
|||
|
|
startDate: string; // ISO date string
|
|||
|
|
endDate?: string; // ISO date string (может быть NULL, если больничный еще не закрыт)
|
|||
|
|
expectedReturnDate?: string; // Предварительная дата выхода (ISO date)
|
|||
|
|
daysCount?: number;
|
|||
|
|
sickLeaveNumber?: string; // Номер больничного листа (обязателен при выходе с больничного)
|
|||
|
|
diagnosis?: string; // Диагноз
|
|||
|
|
medicalInstitution?: string; // Медицинское учреждение
|
|||
|
|
status: 'active' | 'closed' | 'canceled';
|
|||
|
|
requiresApproval?: boolean; // Требуется ли согласование
|
|||
|
|
approvedBy?: string;
|
|||
|
|
approvedAt?: string; // ISO datetime string
|
|||
|
|
approvedSignature?: string; // Подпись руководителя
|
|||
|
|
closedAt?: string; // ISO datetime string
|
|||
|
|
notes?: string;
|
|||
|
|
fileUrl?: string; // Ссылка на отсканированный больничный лист
|
|||
|
|
createdAt: string; // ISO datetime string
|
|||
|
|
updatedAt: string; // ISO datetime string
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Отгул или прогул сотрудника
|
|||
|
|
export interface EmployeeAbsence {
|
|||
|
|
id: number;
|
|||
|
|
employeeId: string;
|
|||
|
|
absenceType: 'day_off' | 'absence' | 'late' | 'early_leave'; // Тип: отгул, прогул, опоздание, ранний уход
|
|||
|
|
startDate: string; // ISO date string
|
|||
|
|
endDate?: string; // ISO date string (может быть NULL для однодневных отгулов)
|
|||
|
|
startTime?: string; // Время начала (HH:mm) для опозданий и ранних уходов
|
|||
|
|
endTime?: string; // Время окончания (HH:mm) для опозданий и ранних уходов
|
|||
|
|
daysCount: number; // Количество дней (может быть дробным для части дня)
|
|||
|
|
reason?: string; // Причина отсутствия
|
|||
|
|
requiresApproval: boolean; // Требуется ли согласование
|
|||
|
|
status: 'pending' | 'approved' | 'rejected' | 'canceled'; // Статус согласования
|
|||
|
|
approvedBy?: string; // Кто утвердил (руководитель)
|
|||
|
|
approvedAt?: string; // ISO datetime string
|
|||
|
|
approvedSignature?: string; // Подпись руководителя (текст или путь к файлу)
|
|||
|
|
rejectedBy?: string; // Кто отклонил
|
|||
|
|
rejectedAt?: string; // ISO datetime string
|
|||
|
|
rejectionReason?: string; // Причина отклонения
|
|||
|
|
notes?: string; // Примечания
|
|||
|
|
createdAt: string; // ISO datetime string
|
|||
|
|
updatedAt: string; // ISO datetime string
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Увольнение сотрудника
|
|||
|
|
// Обучение сотрудников (инструктажи и курсы)
|
|||
|
|
export type TrainingType = 'instruction' | 'course' | 'certification' | 'exam' | 'other';
|
|||
|
|
export type TrainingCategory = 'safety' | 'fire_safety' | 'electrical' | 'first_aid' | 'professional' | 'compliance' | 'other';
|
|||
|
|
export type TrainingStatus = 'not_started' | 'in_progress' | 'completed' | 'failed' | 'expired' | 'cancelled';
|
|||
|
|
|
|||
|
|
export interface TrainingProgram {
|
|||
|
|
id: string;
|
|||
|
|
title: string;
|
|||
|
|
description?: string;
|
|||
|
|
type: TrainingType;
|
|||
|
|
category: TrainingCategory;
|
|||
|
|
durationHours?: number;
|
|||
|
|
validityMonths?: number; // Срок действия в месяцах (null = бессрочно)
|
|||
|
|
isRequired: boolean;
|
|||
|
|
requiredForPositions?: string[]; // Должности, для которых обязательно
|
|||
|
|
instructorName?: string;
|
|||
|
|
materialsUrl?: string;
|
|||
|
|
createdAt?: string;
|
|||
|
|
updatedAt?: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface EmployeeTraining {
|
|||
|
|
id: number;
|
|||
|
|
employeeId: string;
|
|||
|
|
programId: string;
|
|||
|
|
status: TrainingStatus;
|
|||
|
|
startDate?: string;
|
|||
|
|
completionDate?: string;
|
|||
|
|
expiryDate?: string; // Дата окончания срока действия
|
|||
|
|
score?: number;
|
|||
|
|
passed?: boolean;
|
|||
|
|
certificateNumber?: string;
|
|||
|
|
certificateUrl?: string;
|
|||
|
|
notes?: string;
|
|||
|
|
instructorName?: string;
|
|||
|
|
location?: string;
|
|||
|
|
createdAt?: string;
|
|||
|
|
updatedAt?: string;
|
|||
|
|
// Дополнительные поля при получении с JOIN
|
|||
|
|
programTitle?: string;
|
|||
|
|
programType?: TrainingType;
|
|||
|
|
programCategory?: TrainingCategory;
|
|||
|
|
programDurationHours?: number;
|
|||
|
|
programValidityMonths?: number;
|
|||
|
|
employeeName?: string;
|
|||
|
|
employeePosition?: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface EmployeeTermination {
|
|||
|
|
id: number;
|
|||
|
|
employeeId: string;
|
|||
|
|
terminationDate: string; // ISO date string
|
|||
|
|
reason: string; // Причина увольнения
|
|||
|
|
initiatedBy: string; // Кто инициировал увольнение
|
|||
|
|
initiatedAt: string; // ISO datetime string
|
|||
|
|
status: 'initiated' | 'in_progress' | 'completed' | 'canceled';
|
|||
|
|
// Договор на увольнение
|
|||
|
|
terminationContractNumber?: string;
|
|||
|
|
terminationContractDate?: string; // ISO date string
|
|||
|
|
terminationContractFileUrl?: string;
|
|||
|
|
// Расчеты
|
|||
|
|
finalSettlementAmount?: number;
|
|||
|
|
unusedVacationDays?: number;
|
|||
|
|
compensationAmount?: number; // Компенсация за неиспользованный отпуск
|
|||
|
|
severancePay?: number; // Выходное пособие
|
|||
|
|
otherPayments?: number; // Прочие выплаты
|
|||
|
|
deductions?: number; // Удержания
|
|||
|
|
settlementDocumentNumber?: string;
|
|||
|
|
settlementDocumentDate?: string; // ISO date string
|
|||
|
|
settlementDocumentFileUrl?: string;
|
|||
|
|
// Дополнительная информация
|
|||
|
|
notes?: string;
|
|||
|
|
completedAt?: string; // ISO datetime string
|
|||
|
|
createdAt: string; // ISO datetime string
|
|||
|
|
updatedAt: string; // ISO datetime string
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface CandidateEvent {
|
|||
|
|
id: string;
|
|||
|
|
candidateId: string;
|
|||
|
|
eventType: 'call' | 'interview_1' | 'interview_2' | 'interview_3' | 'test_task' | 'offer' | 'offer_accepted' | 'offer_rejected' | 'probation_start' | 'hired' | 'rejected' | 'other';
|
|||
|
|
eventDate: string; // ISO date string
|
|||
|
|
notes?: string;
|
|||
|
|
result?: 'success' | 'failed' | 'pending' | 'cancelled';
|
|||
|
|
interviewer?: string;
|
|||
|
|
location?: string;
|
|||
|
|
durationMinutes?: number;
|
|||
|
|
createdAt?: string;
|
|||
|
|
updatedAt?: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface Candidate {
|
|||
|
|
id: string;
|
|||
|
|
name: string;
|
|||
|
|
position: string;
|
|||
|
|
vacancyId?: string; // Связь с вакансией
|
|||
|
|
stage: 'new' | 'interview' | 'probation' | 'hired' | 'rejected';
|
|||
|
|
phone: string;
|
|||
|
|
email?: string;
|
|||
|
|
resumeUrl?: string;
|
|||
|
|
coverLetter?: string;
|
|||
|
|
interviewDate?: string;
|
|||
|
|
interviewNotes?: string;
|
|||
|
|
offerSalary?: number;
|
|||
|
|
offerDate?: string;
|
|||
|
|
hiredDate?: string;
|
|||
|
|
rejectedReason?: string;
|
|||
|
|
events?: CandidateEvent[]; // События кандидата
|
|||
|
|
createdAt?: string;
|
|||
|
|
updatedAt?: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface Vacancy {
|
|||
|
|
id: string;
|
|||
|
|
position: string;
|
|||
|
|
department: string;
|
|||
|
|
status: 'urgent' | 'active' | 'paused' | 'closed';
|
|||
|
|
salary?: string;
|
|||
|
|
applicantsCount: number;
|
|||
|
|
postedDate: string;
|
|||
|
|
description: string;
|
|||
|
|
requirements?: string; // Требования к кандидату
|
|||
|
|
conditions?: string; // Условия работы
|
|||
|
|
responsibilities?: string; // Обязанности
|
|||
|
|
closingDate?: string; // Дата закрытия вакансии
|
|||
|
|
createdAt?: string;
|
|||
|
|
updatedAt?: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface SMMChannel {
|
|||
|
|
id: number;
|
|||
|
|
name: string;
|
|||
|
|
type: 'tg' | 'vk' | 'wa' | 'other';
|
|||
|
|
url?: string;
|
|||
|
|
sortOrder?: number;
|
|||
|
|
createdAt?: string;
|
|||
|
|
lastSnapshot?: SMMSubscriberSnapshot | null;
|
|||
|
|
subscribersCount?: number | null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface SMMSubscriberSnapshot {
|
|||
|
|
id: number;
|
|||
|
|
channelId: number;
|
|||
|
|
subscribersCount: number;
|
|||
|
|
recordedAt: string;
|
|||
|
|
note?: string;
|
|||
|
|
createdAt?: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface SMMChannelsSummary {
|
|||
|
|
total: number;
|
|||
|
|
byChannel: Array<{ id: number; name: string; type: string; subscribersCount: number }>;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface AttractionAction {
|
|||
|
|
id: number;
|
|||
|
|
title: string;
|
|||
|
|
description?: string;
|
|||
|
|
channelId?: number | null;
|
|||
|
|
actionType: 'mailing' | 'event' | 'post' | 'other';
|
|||
|
|
actionDate: string;
|
|||
|
|
newSubscribersAttributed?: number | null;
|
|||
|
|
eventId?: number | null;
|
|||
|
|
createdAt?: string;
|
|||
|
|
createdBy?: string;
|
|||
|
|
channelName?: string;
|
|||
|
|
channelType?: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface PostTopic {
|
|||
|
|
id: number;
|
|||
|
|
title: string;
|
|||
|
|
description?: string;
|
|||
|
|
scheduledDate: string; // дата планируемой публикации
|
|||
|
|
month: string; // YYYY-MM для фильтрации
|
|||
|
|
status: 'draft' | 'pending_approval' | 'approved' | 'rejected';
|
|||
|
|
createdAt?: string;
|
|||
|
|
createdBy?: string;
|
|||
|
|
approvedAt?: string;
|
|||
|
|
approvedBy?: string;
|
|||
|
|
rejectionReason?: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface ScheduledPost {
|
|||
|
|
id: number;
|
|||
|
|
title: string;
|
|||
|
|
content: string;
|
|||
|
|
channelIds: number[];
|
|||
|
|
scheduledAt: string;
|
|||
|
|
status: 'draft' | 'pending_approval' | 'approved' | 'rejected' | 'edited' | 'published';
|
|||
|
|
topicId?: number | null;
|
|||
|
|
imageUrl?: string | null;
|
|||
|
|
createdAt?: string;
|
|||
|
|
createdBy?: string;
|
|||
|
|
approvedAt?: string;
|
|||
|
|
approvedBy?: string;
|
|||
|
|
rejectionReason?: string;
|
|||
|
|
editedContent?: string;
|
|||
|
|
publishedAt?: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface PREventPhoto {
|
|||
|
|
id: number;
|
|||
|
|
event_id?: number;
|
|||
|
|
photoUrl: string;
|
|||
|
|
caption?: string;
|
|||
|
|
locationType?: 'building' | 'office';
|
|||
|
|
locationBuildingId?: string;
|
|||
|
|
createdAt?: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface PREvent {
|
|||
|
|
id: string | number;
|
|||
|
|
title: string;
|
|||
|
|
date: string;
|
|||
|
|
type: 'resident' | 'internal';
|
|||
|
|
category: 'holiday' | 'eco' | 'sport' | 'training' | 'meeting';
|
|||
|
|
status: 'planned' | 'in_progress' | 'completed' | 'canceled';
|
|||
|
|
location: string;
|
|||
|
|
attendeesCount: number;
|
|||
|
|
budget?: number;
|
|||
|
|
shortPlan?: string;
|
|||
|
|
announcement?: string;
|
|||
|
|
locationType?: 'building' | 'office';
|
|||
|
|
locationBuildingId?: string;
|
|||
|
|
/** Для жителей: участок или дома отдельно */
|
|||
|
|
locationPlaceType?: 'district' | 'buildings';
|
|||
|
|
locationDistrictId?: string;
|
|||
|
|
locationBuildingIds?: string[];
|
|||
|
|
assignedEmployeeIds?: string[];
|
|||
|
|
photos?: PREventPhoto[];
|
|||
|
|
invoices?: Array<{ id: number; invoiceNumber: string; status: string; totalAmount?: number }>;
|
|||
|
|
createdAt?: string;
|
|||
|
|
updatedAt?: string;
|
|||
|
|
createdBy?: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/** Живая статистика по отчёту — актуальные данные из БД на момент запроса */
|
|||
|
|
export interface ResidentReportLiveStats {
|
|||
|
|
applicationsTotal: number;
|
|||
|
|
npsScore: number;
|
|||
|
|
fundsCollected: number;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface ResidentReport {
|
|||
|
|
id: string | number;
|
|||
|
|
buildingId: string;
|
|||
|
|
address?: string;
|
|||
|
|
month: string;
|
|||
|
|
periodStart?: string;
|
|||
|
|
periodEnd?: string;
|
|||
|
|
status: 'draft' | 'published';
|
|||
|
|
publishedAt?: string;
|
|||
|
|
content?: ResidentReportContent;
|
|||
|
|
/** Живая статистика (заявки, NPS, собрано) — подставляется с бэкенда при запросе списка */
|
|||
|
|
liveStats?: ResidentReportLiveStats;
|
|||
|
|
createdAt?: string;
|
|||
|
|
updatedAt?: string;
|
|||
|
|
// Старые поля для обратной совместимости
|
|||
|
|
stats?: {
|
|||
|
|
appsDone: number;
|
|||
|
|
budgetSpent: number;
|
|||
|
|
cleaningQuality: number;
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface ResidentReportContent {
|
|||
|
|
// Информация о доме
|
|||
|
|
buildingInfo?: {
|
|||
|
|
address: string;
|
|||
|
|
apartmentsCount?: number;
|
|||
|
|
floors?: number;
|
|||
|
|
};
|
|||
|
|
// Заявки за период
|
|||
|
|
applications?: {
|
|||
|
|
total: number;
|
|||
|
|
completed: number;
|
|||
|
|
inProgress: number;
|
|||
|
|
new: number;
|
|||
|
|
list?: Array<{
|
|||
|
|
id: number;
|
|||
|
|
number: string;
|
|||
|
|
description: string;
|
|||
|
|
status: string;
|
|||
|
|
createdAt: string;
|
|||
|
|
}>;
|
|||
|
|
};
|
|||
|
|
// Статистика по дому
|
|||
|
|
statistics?: {
|
|||
|
|
nps?: number;
|
|||
|
|
collectionRate?: number;
|
|||
|
|
totalDebt?: number;
|
|||
|
|
debtorsCount?: number;
|
|||
|
|
};
|
|||
|
|
// Работа с должниками
|
|||
|
|
debtors?: Array<{
|
|||
|
|
name: string;
|
|||
|
|
apartment: string;
|
|||
|
|
debt: number;
|
|||
|
|
}>;
|
|||
|
|
// Финансы предприятия
|
|||
|
|
finances?: {
|
|||
|
|
income: number;
|
|||
|
|
expenses: number;
|
|||
|
|
balance: number;
|
|||
|
|
incomeByItems?: Record<string, number>;
|
|||
|
|
expensesByItems?: Record<string, number>;
|
|||
|
|
};
|
|||
|
|
// Фото отчеты
|
|||
|
|
workPhotos?: WorkPhoto[];
|
|||
|
|
// AI сгенерированный текст
|
|||
|
|
generatedText?: string;
|
|||
|
|
// Структура отчета собственника (из оборотно-сальдовой ведомости)
|
|||
|
|
parameters?: {
|
|||
|
|
periodStart: string;
|
|||
|
|
periodEnd: string;
|
|||
|
|
building: string;
|
|||
|
|
residentialArea?: number;
|
|||
|
|
nonResidentialArea?: number;
|
|||
|
|
parkingArea?: number;
|
|||
|
|
totalArea?: number;
|
|||
|
|
};
|
|||
|
|
tariffs?: {
|
|||
|
|
tariff: number;
|
|||
|
|
reserveFund: number;
|
|||
|
|
};
|
|||
|
|
services?: Array<{
|
|||
|
|
name: string;
|
|||
|
|
debt: number;
|
|||
|
|
accrued: number;
|
|||
|
|
paid: number;
|
|||
|
|
percentOfPlan: number;
|
|||
|
|
order: number;
|
|||
|
|
}>;
|
|||
|
|
balance?: {
|
|||
|
|
fromAccrued: number;
|
|||
|
|
fromReceived: number;
|
|||
|
|
reserveFundFromAccrued: number;
|
|||
|
|
reserveFundFromReceived: number;
|
|||
|
|
};
|
|||
|
|
expenseItems?: Array<{
|
|||
|
|
number: string;
|
|||
|
|
name: string;
|
|||
|
|
perMonth: number;
|
|||
|
|
total: number;
|
|||
|
|
perSquareMeter: number;
|
|||
|
|
children?: Array<{
|
|||
|
|
number: string;
|
|||
|
|
name: string;
|
|||
|
|
perMonth: number;
|
|||
|
|
total: number;
|
|||
|
|
perSquareMeter: number;
|
|||
|
|
children?: Array<{
|
|||
|
|
number: string;
|
|||
|
|
name: string;
|
|||
|
|
perMonth: number;
|
|||
|
|
total: number;
|
|||
|
|
perSquareMeter: number;
|
|||
|
|
}>;
|
|||
|
|
}>;
|
|||
|
|
}>;
|
|||
|
|
totals?: {
|
|||
|
|
totalExpenses: number;
|
|||
|
|
vat: number;
|
|||
|
|
recalculation: number;
|
|||
|
|
totalWithRecalculation: number;
|
|||
|
|
totalWithRecalculationWithVAT: number;
|
|||
|
|
debtReturn: number;
|
|||
|
|
totalTariff: number;
|
|||
|
|
otherIncome: number;
|
|||
|
|
};
|
|||
|
|
financialResults?: {
|
|||
|
|
maintenanceFromAccrued: number;
|
|||
|
|
maintenanceFromReceived: number;
|
|||
|
|
reserveFundFromAccrued: number;
|
|||
|
|
reserveFundFromReceived: number;
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ========= PR и NPS: ФОТО ОТЧЕТЫ =========
|
|||
|
|
export interface WorkPhoto {
|
|||
|
|
id: number;
|
|||
|
|
buildingId: string;
|
|||
|
|
residentReportId?: number;
|
|||
|
|
taskId?: string; // Привязка к задаче (фото отчёт до/после)
|
|||
|
|
workName: string;
|
|||
|
|
workDate: string;
|
|||
|
|
description?: string;
|
|||
|
|
photoBeforeUrl?: string;
|
|||
|
|
photoAfterUrl?: string;
|
|||
|
|
createdAt: string;
|
|||
|
|
updatedAt: string;
|
|||
|
|
// Дополнительные поля для отображения
|
|||
|
|
address?: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ========= PR и NPS: NPS ОПРОСЫ =========
|
|||
|
|
export type NPSSurveyStatus = 'draft' | 'active' | 'closed';
|
|||
|
|
|
|||
|
|
export interface NPSSurvey {
|
|||
|
|
id: number;
|
|||
|
|
buildingId: string;
|
|||
|
|
title: string;
|
|||
|
|
description?: string;
|
|||
|
|
status: NPSSurveyStatus;
|
|||
|
|
accessKey: string;
|
|||
|
|
publishedAt?: string;
|
|||
|
|
expiresAt?: string;
|
|||
|
|
createdBy: string;
|
|||
|
|
createdAt: string;
|
|||
|
|
updatedAt: string;
|
|||
|
|
// Дополнительные поля для отображения
|
|||
|
|
address?: string;
|
|||
|
|
responsesCount?: number;
|
|||
|
|
avgScore?: number;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface NPSResponse {
|
|||
|
|
id: number;
|
|||
|
|
surveyId: number;
|
|||
|
|
buildingId: string;
|
|||
|
|
score: number; // 0-10
|
|||
|
|
comment?: string;
|
|||
|
|
respondentName?: string;
|
|||
|
|
apartment?: string;
|
|||
|
|
phone?: string;
|
|||
|
|
email?: string;
|
|||
|
|
createdAt: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface NPSSurveyStats {
|
|||
|
|
totalResponses: number;
|
|||
|
|
avgScore: number;
|
|||
|
|
promoters: number; // 9-10
|
|||
|
|
passives: number; // 7-8
|
|||
|
|
detractors: number; // 0-6
|
|||
|
|
nps: number; // (promoters% - detractors%)
|
|||
|
|
promoterPercent: number;
|
|||
|
|
detractorPercent: number;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ========= PR и NPS: НАСТРОЙКИ ПАРСИНГА =========
|
|||
|
|
export type ParsingSource = 'yandex_maps' | '2gis';
|
|||
|
|
|
|||
|
|
export interface ParsingSettings {
|
|||
|
|
id: number;
|
|||
|
|
source: ParsingSource;
|
|||
|
|
enabled: boolean;
|
|||
|
|
urlTemplate?: string;
|
|||
|
|
apiKey?: string;
|
|||
|
|
parsingIntervalHours: number;
|
|||
|
|
lastParsedAt?: string;
|
|||
|
|
settings?: Record<string, any>;
|
|||
|
|
buildingId?: string; // ID дома, к которому привязаны отзывы (извлекается из settings.building_id)
|
|||
|
|
createdAt: string;
|
|||
|
|
updatedAt: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export type Department = 'production' | 'pr' | 'finance' | 'development' | 'legal' | 'hr';
|
|||
|
|
|
|||
|
|
export type CompanyNewsStatus = 'draft' | 'pending' | 'published';
|
|||
|
|
|
|||
|
|
export interface CompanyNews {
|
|||
|
|
id: number;
|
|||
|
|
title: string;
|
|||
|
|
body: string | null;
|
|||
|
|
status: CompanyNewsStatus;
|
|||
|
|
createdAt: string;
|
|||
|
|
updatedAt: string;
|
|||
|
|
publishedAt: string | null;
|
|||
|
|
createdBy: number | null;
|
|||
|
|
createdByName?: string | null;
|
|||
|
|
notifyDepartments?: Department[];
|
|||
|
|
notifyEmployeeIds?: string[];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface StrategicGoal {
|
|||
|
|
id: string;
|
|||
|
|
department: Department;
|
|||
|
|
title: string;
|
|||
|
|
description: string;
|
|||
|
|
currentValue: number;
|
|||
|
|
targetValue: number;
|
|||
|
|
unit: string;
|
|||
|
|
deadline: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface ResidentFeedback {
|
|||
|
|
id: string;
|
|||
|
|
buildingId: string;
|
|||
|
|
address: string;
|
|||
|
|
date: string;
|
|||
|
|
text: string;
|
|||
|
|
source: string;
|
|||
|
|
rating: number;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface AnalyzedFeedback extends ResidentFeedback {
|
|||
|
|
category: string;
|
|||
|
|
sentiment: 'Positive' | 'Negative' | 'Neutral';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ========= PR и NPS: ОТЗЫВЫ =========
|
|||
|
|
export type ReviewSource = 'yandex_maps' | '2gis' | 'internal' | 'other';
|
|||
|
|
export type ReviewStatus = 'new' | 'processed' | 'archived';
|
|||
|
|
|
|||
|
|
export interface Review {
|
|||
|
|
id: number;
|
|||
|
|
buildingId: string;
|
|||
|
|
source: ReviewSource;
|
|||
|
|
sourceUrl?: string;
|
|||
|
|
authorName?: string;
|
|||
|
|
text: string;
|
|||
|
|
rating: number; // 1-10
|
|||
|
|
date: string;
|
|||
|
|
status: ReviewStatus;
|
|||
|
|
processedAt?: string;
|
|||
|
|
processedBy?: string;
|
|||
|
|
createdAt: string;
|
|||
|
|
updatedAt: string;
|
|||
|
|
// Дополнительные поля для отображения
|
|||
|
|
address?: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ========= PR и NPS: ИНЦИДЕНТЫ =========
|
|||
|
|
export type IncidentType = 'property_damage' | 'debtor_complaint' | 'service_quality' | 'other';
|
|||
|
|
export type IncidentStatus = 'new' | 'in_progress' | 'resolved' | 'closed';
|
|||
|
|
export type IncidentPriority = 'low' | 'medium' | 'high' | 'urgent';
|
|||
|
|
|
|||
|
|
export interface Incident {
|
|||
|
|
id: number;
|
|||
|
|
reviewId?: number;
|
|||
|
|
buildingId: string;
|
|||
|
|
type: IncidentType;
|
|||
|
|
title: string;
|
|||
|
|
description: string;
|
|||
|
|
status: IncidentStatus;
|
|||
|
|
priority: IncidentPriority;
|
|||
|
|
assignedTo?: string;
|
|||
|
|
createdBy: string;
|
|||
|
|
createdAt: string;
|
|||
|
|
resolvedAt?: string;
|
|||
|
|
resolutionNotes?: string;
|
|||
|
|
updatedAt: string;
|
|||
|
|
// Дополнительные поля для отображения
|
|||
|
|
address?: string;
|
|||
|
|
review?: Review;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface AIAnalysisResult {
|
|||
|
|
analyzedFeedback: AnalyzedFeedback[];
|
|||
|
|
summary: {
|
|||
|
|
positive: string[];
|
|||
|
|
negative: string[];
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export type DomaApplicationStatus = 'new' | 'in_progress' | 'deferred' | 'done' | 'canceled';
|
|||
|
|
|
|||
|
|
export interface DomaApplication {
|
|||
|
|
id: number;
|
|||
|
|
number: string;
|
|||
|
|
status: DomaApplicationStatus;
|
|||
|
|
description: string;
|
|||
|
|
address: string;
|
|||
|
|
apartment: string;
|
|||
|
|
clientName: string;
|
|||
|
|
createdAt: string;
|
|||
|
|
deadlineAt: string;
|
|||
|
|
performer?: { name: string };
|
|||
|
|
isOverdue?: boolean;
|
|||
|
|
domaId?: string;
|
|||
|
|
source?: 'doma' | 'manual';
|
|||
|
|
sourceChannel?: string;
|
|||
|
|
contactPhone?: string;
|
|||
|
|
contactName?: string;
|
|||
|
|
placeIncident?: string;
|
|||
|
|
workType?: string;
|
|||
|
|
problemDetail?: string;
|
|||
|
|
executorName?: string;
|
|||
|
|
responsibleName?: string;
|
|||
|
|
observersText?: string;
|
|||
|
|
updatedAt?: string;
|
|||
|
|
buildingId?: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/** Комментарий к заявке (внутренний или с жителем) */
|
|||
|
|
export interface ApplicationComment {
|
|||
|
|
id: number;
|
|||
|
|
applicationId: number;
|
|||
|
|
authorName: string;
|
|||
|
|
type: 'internal' | 'resident';
|
|||
|
|
text: string;
|
|||
|
|
createdAt: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/** Запись истории изменений заявки */
|
|||
|
|
export interface ApplicationHistoryEntry {
|
|||
|
|
id: number;
|
|||
|
|
applicationId: number;
|
|||
|
|
changedBy: string;
|
|||
|
|
changedAt: string;
|
|||
|
|
fieldName: string;
|
|||
|
|
oldValue?: string | null;
|
|||
|
|
newValue?: string | null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/** Payload для создания заявки вручную (карточка диспетчерской) */
|
|||
|
|
export interface CreateApplicationPayload {
|
|||
|
|
address: string;
|
|||
|
|
description: string;
|
|||
|
|
apartment?: string;
|
|||
|
|
sourceChannel?: string;
|
|||
|
|
isFromResident?: boolean;
|
|||
|
|
contactPhone?: string;
|
|||
|
|
contactName?: string;
|
|||
|
|
placeIncident?: string;
|
|||
|
|
workType?: string;
|
|||
|
|
problemDetail?: string;
|
|||
|
|
isEmergency?: boolean;
|
|||
|
|
isPaid?: boolean;
|
|||
|
|
isWarranty?: boolean;
|
|||
|
|
deadlineAt?: string;
|
|||
|
|
executorName?: string;
|
|||
|
|
responsibleName?: string;
|
|||
|
|
observersText?: string;
|
|||
|
|
showInApp?: boolean;
|
|||
|
|
buildingId?: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/** Запись журнала отключений */
|
|||
|
|
export type OutageWorkType = 'absent' | 'planned' | 'emergency';
|
|||
|
|
|
|||
|
|
export interface Outage {
|
|||
|
|
id: number;
|
|||
|
|
buildingId: string;
|
|||
|
|
startAt: string;
|
|||
|
|
endAt?: string | null;
|
|||
|
|
type?: string | null;
|
|||
|
|
description?: string | null;
|
|||
|
|
active: boolean;
|
|||
|
|
createdAt: string;
|
|||
|
|
updatedAt: string;
|
|||
|
|
buildingAddress?: string;
|
|||
|
|
authorName?: string | null;
|
|||
|
|
category?: string | null;
|
|||
|
|
problemDetail?: string | null;
|
|||
|
|
workType?: OutageWorkType | string | null;
|
|||
|
|
residentMessage?: string | null;
|
|||
|
|
generateNews?: boolean;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface CreateOutagePayload {
|
|||
|
|
buildingId?: string;
|
|||
|
|
buildingIds?: string[];
|
|||
|
|
startAt: string;
|
|||
|
|
endAt?: string;
|
|||
|
|
type?: string;
|
|||
|
|
description: string;
|
|||
|
|
active?: boolean;
|
|||
|
|
authorName?: string;
|
|||
|
|
category?: string;
|
|||
|
|
problemDetail?: string;
|
|||
|
|
workType?: OutageWorkType | string;
|
|||
|
|
residentMessage?: string;
|
|||
|
|
generateNews?: boolean;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface OfficeRequest {
|
|||
|
|
id: number | string; // number из БД, string для моков
|
|||
|
|
requesterName: string;
|
|||
|
|
category: string;
|
|||
|
|
itemName: string;
|
|||
|
|
quantity?: number;
|
|||
|
|
issuedQuantity?: number; // Количество выданного сотруднику
|
|||
|
|
unit?: string;
|
|||
|
|
date?: string; // Дата создания (created_at)
|
|||
|
|
status: 'new' | 'approved' | 'ordered' | 'received' | 'canceled' | 'archived' | 'collected';
|
|||
|
|
amount: number;
|
|||
|
|
priority: 'low' | 'medium' | 'high' | 'urgent';
|
|||
|
|
approvedBy?: string;
|
|||
|
|
approvedAt?: string;
|
|||
|
|
orderedAt?: string;
|
|||
|
|
receivedAt?: string;
|
|||
|
|
notes?: string;
|
|||
|
|
createdAt?: string;
|
|||
|
|
updatedAt?: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface OfficeInventoryItem {
|
|||
|
|
id: number | string; // number из БД, string для моков
|
|||
|
|
name: string;
|
|||
|
|
category?: string;
|
|||
|
|
quantity: number;
|
|||
|
|
unit: string;
|
|||
|
|
minThreshold: number;
|
|||
|
|
lastRestock?: string | null; // DATE из БД
|
|||
|
|
lastRestockBy?: string;
|
|||
|
|
location?: string;
|
|||
|
|
notes?: string;
|
|||
|
|
createdAt?: string;
|
|||
|
|
updatedAt?: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface OfficeDocument {
|
|||
|
|
id: number | string; // number из БД, string для моков
|
|||
|
|
regNumber: string;
|
|||
|
|
title: string;
|
|||
|
|
correspondent: string;
|
|||
|
|
date: string;
|
|||
|
|
status: 'registered' | 'processed' | 'sent' | 'archived';
|
|||
|
|
// FIX: added type property to fix TS errors in OfficeSummary.tsx and DocumentFlow.tsx
|
|||
|
|
type: 'incoming' | 'outgoing'; // document_type в БД
|
|||
|
|
letterType?: 'email' | 'paper'; // Тип письма: Email или бумага
|
|||
|
|
assignedTo?: string;
|
|||
|
|
trackingNumber?: string;
|
|||
|
|
fileUrl?: string; // URL загруженного файла (скан или текстовый файл)
|
|||
|
|
notes?: string;
|
|||
|
|
createdBy: string; // Кто создал/зарегистрировал
|
|||
|
|
createdAt?: string;
|
|||
|
|
updatedAt?: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface OfficeAsset {
|
|||
|
|
id: string;
|
|||
|
|
name: string;
|
|||
|
|
type: 'laptop' | 'appliance' | 'furniture' | 'other';
|
|||
|
|
assignedTo?: string;
|
|||
|
|
purchaseDate: string;
|
|||
|
|
condition: 'good' | 'poor' | 'fair';
|
|||
|
|
serialNumber: string;
|
|||
|
|
nextMaintenanceDate?: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export type ContractStatus = 'draft' | 'finance_approval' | 'counterparty_approval' | 'signing' | 'active' | 'archived';
|
|||
|
|
|
|||
|
|
export interface LegalContract {
|
|||
|
|
id: string;
|
|||
|
|
number: string;
|
|||
|
|
type: string;
|
|||
|
|
counterparty: string;
|
|||
|
|
counterpartyInn?: string;
|
|||
|
|
amount: number;
|
|||
|
|
status: ContractStatus;
|
|||
|
|
startDate: string;
|
|||
|
|
endDate: string;
|
|||
|
|
autoProlongation: boolean;
|
|||
|
|
manager: string;
|
|||
|
|
hasDisagreements: boolean;
|
|||
|
|
contractFileUrl?: string;
|
|||
|
|
notes?: string;
|
|||
|
|
createdAt?: string;
|
|||
|
|
updatedAt?: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/** Этап ФССП в цепочке взыскания */
|
|||
|
|
export type FsspStage = 'writ_submitted' | 'ip_initiated' | 'bank_requests' | 'money_on_deposit' | 'transferred_to_uk';
|
|||
|
|
|
|||
|
|
export interface LegalCourtCase {
|
|||
|
|
id: string;
|
|||
|
|
caseNumber: string;
|
|||
|
|
type: 'arbitration' | 'civil' | 'debt_recovery';
|
|||
|
|
role: 'plaintiff' | 'defendant';
|
|||
|
|
subject: string;
|
|||
|
|
debtorName?: string;
|
|||
|
|
address?: string;
|
|||
|
|
amount: number; // Присужденная сумма
|
|||
|
|
recoveredAmount?: number; // Реально пришло в УК
|
|||
|
|
amountAtBailiffs?: number; // Деньги на счетах ФССП
|
|||
|
|
status: 'pre_trial' | 'litigation' | 'decision_received' | 'enforcement' | 'closed';
|
|||
|
|
fsspStatus?: string;
|
|||
|
|
bailiffName?: string;
|
|||
|
|
fsspLastActionDate?: string;
|
|||
|
|
nextHearingDate?: string;
|
|||
|
|
judge?: string;
|
|||
|
|
/** Номер исполнительного производства */
|
|||
|
|
enforcementNumber?: string;
|
|||
|
|
/** Дата возбуждения ИП */
|
|||
|
|
enforcementStartDate?: string;
|
|||
|
|
/** Этап ФССП: ИЛ предъявлен → ИП возбуждено → запросы в банки → деньги на депозите → в УК */
|
|||
|
|
fsspStage?: FsspStage | null;
|
|||
|
|
/** Сумма пени по делу */
|
|||
|
|
penaltyAmount?: number;
|
|||
|
|
/** Дата начала просрочки оплаты */
|
|||
|
|
overdueSince?: string;
|
|||
|
|
/** Название суда */
|
|||
|
|
courtName?: string;
|
|||
|
|
/** Примечания */
|
|||
|
|
notes?: string;
|
|||
|
|
/** Ссылка на материалы дела */
|
|||
|
|
caseFileUrl?: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface LegalPoA {
|
|||
|
|
id: string;
|
|||
|
|
number: string;
|
|||
|
|
issuedTo: string;
|
|||
|
|
issueDate: string;
|
|||
|
|
expiryDate: string;
|
|||
|
|
status: 'active' | 'expired' | 'revoked';
|
|||
|
|
authority: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface LegalCounterparty {
|
|||
|
|
id: string;
|
|||
|
|
name: string;
|
|||
|
|
inn: string;
|
|||
|
|
status: 'active' | 'inactive';
|
|||
|
|
riskLevel: 'low' | 'medium' | 'high';
|
|||
|
|
checkedDate: string;
|
|||
|
|
notes?: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export type DevPipelineStatus =
|
|||
|
|
| 'incoming' // Входящие
|
|||
|
|
| 'analysis' // Анализ
|
|||
|
|
| 'agenda_approval' // Согласование повестки
|
|||
|
|
| 'in_person' // Очная часть
|
|||
|
|
| 'absentee' // Заочная часть
|
|||
|
|
| 'protocol_formation' // Формирование протокола
|
|||
|
|
| 'protocol_to_gzhi' // Отправка протокола в ГЖИ
|
|||
|
|
| 'gzhi_order' // Приказ ГЖИ
|
|||
|
|
| 'success' // Успех
|
|||
|
|
| 'failure'; // Провал
|
|||
|
|
|
|||
|
|
export interface DevPipelineItem {
|
|||
|
|
id: string;
|
|||
|
|
address: string;
|
|||
|
|
type: 'old' | 'new';
|
|||
|
|
floors: number;
|
|||
|
|
area: number;
|
|||
|
|
apartments: number;
|
|||
|
|
status: DevPipelineStatus;
|
|||
|
|
probability: number;
|
|||
|
|
expectedRevenue: number;
|
|||
|
|
manager: string;
|
|||
|
|
buildingId?: string | null;
|
|||
|
|
notes?: string | null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface DevOSSSession {
|
|||
|
|
id: string;
|
|||
|
|
buildingId: string;
|
|||
|
|
address: string;
|
|||
|
|
startDate: string;
|
|||
|
|
endDate: string;
|
|||
|
|
quorumCurrent: number;
|
|||
|
|
quorumTotal: number;
|
|||
|
|
status: 'active' | 'planned' | 'completed';
|
|||
|
|
type: 'annual' | 'extraordinary';
|
|||
|
|
description?: string | null;
|
|||
|
|
agendaItems?: string[];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/** Статус аудита */
|
|||
|
|
export type DevAuditStatus = 'new' | 'in_progress' | 'completed';
|
|||
|
|
|
|||
|
|
/** Оценка подпункта: 1–5 (1 — худшее, 5 — лучшее). Итог по пункту — среднее арифметическое. */
|
|||
|
|
export type InspectionSubItemRating = number; // 1 | 2 | 3 | 4 | 5
|
|||
|
|
|
|||
|
|
/** Подпункт осмотра (label берётся из справочника по key) */
|
|||
|
|
export interface InspectionSubItem {
|
|||
|
|
key: string;
|
|||
|
|
label?: string;
|
|||
|
|
/** Оценка 1–5; не участвует в среднем при noAccess/notPresent */
|
|||
|
|
rating: number | null;
|
|||
|
|
description: string | null;
|
|||
|
|
noAccess: boolean;
|
|||
|
|
notPresent: boolean;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/** Данные по одному пункту осмотра (категория). overall — среднее по подпунктам (вычисляемое или сохранённое). */
|
|||
|
|
export interface InspectionCategoryData {
|
|||
|
|
subItems: InspectionSubItem[];
|
|||
|
|
/** Среднее арифметическое оценок подпунктов (1–5), вычисляется из subItems */
|
|||
|
|
overall?: number | null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/** Ключи пунктов осмотра: кровля, фасад, подъезды, инфраструктура, придомовой участок */
|
|||
|
|
export type InspectionCategoryKey = 'roof' | 'facade' | 'entrances' | 'infrastructure' | 'yard';
|
|||
|
|
|
|||
|
|
/** Данные осмотра по всем пунктам */
|
|||
|
|
export type InspectionData = Partial<Record<InspectionCategoryKey, InspectionCategoryData>>;
|
|||
|
|
|
|||
|
|
export interface DevAuditData {
|
|||
|
|
id: string;
|
|||
|
|
buildingId: string;
|
|||
|
|
address: string;
|
|||
|
|
status: DevAuditStatus;
|
|||
|
|
wearPercent: number;
|
|||
|
|
roofCondition: 'good' | 'poor' | 'fair';
|
|||
|
|
basementCondition: 'good' | 'poor' | 'fair';
|
|||
|
|
calculatedTariff: number;
|
|||
|
|
projectedMargin: number;
|
|||
|
|
complexityIndex: number | null;
|
|||
|
|
inspectionData: InspectionData | null;
|
|||
|
|
auditDate?: string;
|
|||
|
|
auditorName?: string | null;
|
|||
|
|
notes?: string | null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface DevMarketingActivity {
|
|||
|
|
id: string;
|
|||
|
|
buildingId: string;
|
|||
|
|
address: string;
|
|||
|
|
activistsCount: number;
|
|||
|
|
meetingsHeld: number;
|
|||
|
|
adsDistributed: number;
|
|||
|
|
competitor: string;
|
|||
|
|
status: 'voting' | 'my_house' | 'competitor_house';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface MeterReading {
|
|||
|
|
date: string;
|
|||
|
|
value: number;
|
|||
|
|
consumption: number;
|
|||
|
|
source: 'app' | 'manual' | 'iot';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface AccountMeter {
|
|||
|
|
id: string;
|
|||
|
|
type: 'ХВС' | 'ГВС' | 'Э/Э' | 'Газ' | 'Other';
|
|||
|
|
make: string;
|
|||
|
|
number: string;
|
|||
|
|
name?: string; // Название прибора (для кастомных)
|
|||
|
|
manufacturer?: string; // Производитель
|
|||
|
|
installDate?: string; // Дата установки
|
|||
|
|
lastVerification: string;
|
|||
|
|
nextVerification: string;
|
|||
|
|
currentReading?: number; // Текущее показание
|
|||
|
|
unit?: string; // Единица измерения (м³, кВт⋅ч и т.д.)
|
|||
|
|
readings: MeterReading[];
|
|||
|
|
documents?: string[]; // Файлы документов (паспорт, акты поверки и т.д.)
|
|||
|
|
notes?: string; // Примечания
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface ResidentProfile {
|
|||
|
|
sentiment: 'negative' | 'toxic' | 'positive' | 'loyal' | 'neutral';
|
|||
|
|
birthday?: string; // ISO date string (YYYY-MM-DD)
|
|||
|
|
notes?: string;
|
|||
|
|
preferredContactMethod?: 'phone' | 'email' | 'chat' | 'in_person';
|
|||
|
|
email?: string;
|
|||
|
|
lastInteractionDate?: string; // ISO date string
|
|||
|
|
specialNeeds?: string[];
|
|||
|
|
paymentHistory?: {
|
|||
|
|
onTime: number;
|
|||
|
|
late: number;
|
|||
|
|
averageDelayDays: number;
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface RegisteredPerson {
|
|||
|
|
id: string;
|
|||
|
|
fullName: string;
|
|||
|
|
phone?: string;
|
|||
|
|
email?: string;
|
|||
|
|
residentProfile?: ResidentProfile; // Портрет клиента для каждого прописанного
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface PersonalAccount {
|
|||
|
|
id: string;
|
|||
|
|
accountNumber: string;
|
|||
|
|
apartmentNumber: string;
|
|||
|
|
type: 'apartment' | 'parking' | 'storage' | 'office';
|
|||
|
|
floor: number;
|
|||
|
|
entranceNumber?: number; // Номер подъезда (если известен)
|
|||
|
|
owners: {
|
|||
|
|
fullName: string,
|
|||
|
|
phone: string;
|
|||
|
|
residentProfile?: ResidentProfile; // Портрет клиента для собственника
|
|||
|
|
}[];
|
|||
|
|
registered: RegisteredPerson[]; // Прописанные с их данными и портретами
|
|||
|
|
areaTotal: number;
|
|||
|
|
areaLiving: number;
|
|||
|
|
areaNonLiving: number;
|
|||
|
|
meters: AccountMeter[];
|
|||
|
|
isMeterInstallationFeasible: boolean;
|
|||
|
|
surveyActNumber?: string;
|
|||
|
|
surveyActDate?: string;
|
|||
|
|
premiseNotes?: string; // Примечания по помещению (ремонты, особенности, проблемы и т.д.)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface Debtor {
|
|||
|
|
apartment: string;
|
|||
|
|
amount: number;
|
|||
|
|
months: number;
|
|||
|
|
status: 'new' | 'pending' | 'legal';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ========= ЮРИДИЧЕСКИЙ ОТДЕЛ: ДОСУДЕБНАЯ РАБОТА =========
|
|||
|
|
|
|||
|
|
export interface LegalDebtor {
|
|||
|
|
id: number;
|
|||
|
|
buildingId: string;
|
|||
|
|
apartment: string;
|
|||
|
|
debtorName?: string;
|
|||
|
|
phone?: string;
|
|||
|
|
email?: string;
|
|||
|
|
address: string;
|
|||
|
|
debtAmount: number;
|
|||
|
|
debtMonths: number;
|
|||
|
|
status: 'new' | 'in_progress' | 'promised_payment' | 'transferred_to_court' | 'resolved';
|
|||
|
|
transferredFromFinance: boolean;
|
|||
|
|
createdAt: string;
|
|||
|
|
updatedAt: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface PreTrialWork {
|
|||
|
|
id: number;
|
|||
|
|
debtorId: number;
|
|||
|
|
assignedTo?: string;
|
|||
|
|
status: 'new' | 'in_progress' | 'promised_payment' | 'transferred_to_court' | 'resolved';
|
|||
|
|
promisedPaymentDate?: string;
|
|||
|
|
promisedPaymentAmount?: number;
|
|||
|
|
transferredToCourt: boolean;
|
|||
|
|
courtCaseId?: string;
|
|||
|
|
notes?: string;
|
|||
|
|
createdAt: string;
|
|||
|
|
updatedAt: string;
|
|||
|
|
debtor?: LegalDebtor;
|
|||
|
|
actions?: PreTrialAction[];
|
|||
|
|
promisedPayments?: PromisedPayment[];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface PreTrialAction {
|
|||
|
|
id: number;
|
|||
|
|
workId: number;
|
|||
|
|
actionType: 'call' | 'letter' | 'visit';
|
|||
|
|
actionDate: string;
|
|||
|
|
performedBy: string;
|
|||
|
|
result?: string;
|
|||
|
|
notes?: string;
|
|||
|
|
attachments?: string[];
|
|||
|
|
createdAt: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface PromisedPayment {
|
|||
|
|
id: number;
|
|||
|
|
workId: number;
|
|||
|
|
promisedDate: string;
|
|||
|
|
promisedAmount: number;
|
|||
|
|
actualPaymentDate?: string;
|
|||
|
|
actualPaymentAmount?: number;
|
|||
|
|
isPaid: boolean;
|
|||
|
|
reminderSent: boolean;
|
|||
|
|
reminderDate?: string;
|
|||
|
|
notes?: string;
|
|||
|
|
createdAt: string;
|
|||
|
|
updatedAt: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export type InvoiceStatus = 'draft' | 'pending_approval' | 'approved' | 'scheduled' | 'paid' | 'rejected' | 'overdue' | 'clarification';
|
|||
|
|
|
|||
|
|
export interface Invoice {
|
|||
|
|
id: string;
|
|||
|
|
buildingId: string;
|
|||
|
|
address: string;
|
|||
|
|
contractorName: string;
|
|||
|
|
serviceName: string;
|
|||
|
|
amount: number;
|
|||
|
|
date: string;
|
|||
|
|
paymentDeadline?: string;
|
|||
|
|
scheduledDate?: string;
|
|||
|
|
status: InvoiceStatus;
|
|||
|
|
priority: 'low' | 'medium' | 'high';
|
|||
|
|
closingDocsReceived: boolean;
|
|||
|
|
paymentRef?: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Типы для новой системы счетов на оплату
|
|||
|
|
export type PaymentInvoiceStatus =
|
|||
|
|
| 'draft'
|
|||
|
|
| 'pending_manager_approval'
|
|||
|
|
| 'pending_finance_manager_approval'
|
|||
|
|
| 'approved'
|
|||
|
|
| 'scheduled'
|
|||
|
|
| 'paid'
|
|||
|
|
| 'postponed'
|
|||
|
|
| 'cancelled'
|
|||
|
|
| 'rejected'
|
|||
|
|
| 'completed';
|
|||
|
|
|
|||
|
|
export type PaymentInvoicePurposeType = 'building' | 'district' | 'legal' | 'office' | 'hr' | 'other' | 'event';
|
|||
|
|
export type PaymentInvoiceFormat = 'prepayment' | 'postpayment' | 'advance';
|
|||
|
|
export type DistributionMethod = 'equal' | 'by_area' | 'manual';
|
|||
|
|
export type PaymentInvoiceItemType = 'service' | 'materials';
|
|||
|
|
|
|||
|
|
export interface ApprovalHistoryEntry {
|
|||
|
|
userId: string;
|
|||
|
|
userRole: string;
|
|||
|
|
action: 'approve' | 'reject' | 'revision';
|
|||
|
|
comment?: string;
|
|||
|
|
date: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Элемент услуги в счете
|
|||
|
|
export interface ServiceItem {
|
|||
|
|
name: string; // Название услуги
|
|||
|
|
amount: number; // Сумма за услугу
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Элемент ТМЦ в счете
|
|||
|
|
export interface MaterialItem {
|
|||
|
|
name: string; // Наименование ТМЦ
|
|||
|
|
quantity: number; // Количество
|
|||
|
|
unit: string; // Единица измерения (шт, кг, м и т.д.)
|
|||
|
|
pricePerUnit: number; // Цена за единицу
|
|||
|
|
amount: number; // Сумма (quantity * pricePerUnit)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface PaymentInvoice {
|
|||
|
|
id: number;
|
|||
|
|
invoiceNumber: string;
|
|||
|
|
createdBy: string;
|
|||
|
|
createdAt: string;
|
|||
|
|
updatedAt: string;
|
|||
|
|
purposeType: PaymentInvoicePurposeType;
|
|||
|
|
purposeBuildingIds: string[];
|
|||
|
|
purposeDistrictIds: string[];
|
|||
|
|
purposeDescription?: string;
|
|||
|
|
purposeEventId?: number | null; // ID мероприятия (pr_events), если purposeType = 'event'
|
|||
|
|
/** Пункт плана работ (Участки → Дом → План работ). Связь план/факт. */
|
|||
|
|
planItemId?: string | null;
|
|||
|
|
planItemBuildingId?: string | null;
|
|||
|
|
paymentFormat: PaymentInvoiceFormat;
|
|||
|
|
itemType: PaymentInvoiceItemType; // Услуга или ТМЦ
|
|||
|
|
contractorName: string;
|
|||
|
|
contractorInn?: string;
|
|||
|
|
serviceDescription: string; // Описание услуги или ТМЦ (для обратной совместимости)
|
|||
|
|
serviceItems?: ServiceItem[]; // Список услуг (если itemType = 'service')
|
|||
|
|
materialItems?: MaterialItem[]; // Список ТМЦ (если itemType = 'materials')
|
|||
|
|
totalAmount: number;
|
|||
|
|
distributionMethod?: DistributionMethod;
|
|||
|
|
distributionData: Record<string, any>;
|
|||
|
|
status: PaymentInvoiceStatus;
|
|||
|
|
currentApproverRole?: string;
|
|||
|
|
approvalHistory: ApprovalHistoryEntry[];
|
|||
|
|
rejectionReason?: string;
|
|||
|
|
scheduledDate?: string;
|
|||
|
|
paymentDate?: string;
|
|||
|
|
/** Номер платежного поручения / платежки */
|
|||
|
|
paymentRef?: string | null;
|
|||
|
|
/** Оплата наличными */
|
|||
|
|
isCash?: boolean;
|
|||
|
|
/** Дата переноса (при отложении) */
|
|||
|
|
postponedDate?: string | null;
|
|||
|
|
/** Причина отмены */
|
|||
|
|
cancelReason?: string | null;
|
|||
|
|
isCompleted: boolean;
|
|||
|
|
closingDocsReceived: boolean;
|
|||
|
|
closingDocsFiles?: any[];
|
|||
|
|
notes?: string;
|
|||
|
|
fileUrls: string[];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Платежный календарь: типы записей
|
|||
|
|
export type PaymentType = 'invoice' | 'manual' | 'cash';
|
|||
|
|
export type PaymentDirection = 'outgoing' | 'incoming';
|
|||
|
|
export type PaymentProbability = 'confirmed' | 'high' | 'medium' | 'low';
|
|||
|
|
|
|||
|
|
export interface PaymentCalendarEntry {
|
|||
|
|
id: number;
|
|||
|
|
direction: PaymentDirection;
|
|||
|
|
type: PaymentType;
|
|||
|
|
paymentInvoiceId?: number | null;
|
|||
|
|
category: string;
|
|||
|
|
description: string;
|
|||
|
|
amount: number;
|
|||
|
|
scheduledDate: string;
|
|||
|
|
paymentDate?: string | null;
|
|||
|
|
probability: PaymentProbability;
|
|||
|
|
currency: string;
|
|||
|
|
isCash: boolean;
|
|||
|
|
contractorName: string;
|
|||
|
|
notes?: string | null;
|
|||
|
|
createdAt: string;
|
|||
|
|
updatedAt: string;
|
|||
|
|
createdBy?: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/** Статья доходов/расходов (справочник) */
|
|||
|
|
export interface PaymentCategory {
|
|||
|
|
id: number;
|
|||
|
|
name: string;
|
|||
|
|
code?: string | null;
|
|||
|
|
direction: 'income' | 'expense';
|
|||
|
|
parentId?: number | null;
|
|||
|
|
isActive: boolean;
|
|||
|
|
sortOrder?: number;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/** Банковский счёт или кошелёк (наличка) для календаря оплат */
|
|||
|
|
export interface FinanceAccount {
|
|||
|
|
id: number;
|
|||
|
|
type: 'bank' | 'cash';
|
|||
|
|
name: string;
|
|||
|
|
balance: number;
|
|||
|
|
currency: string;
|
|||
|
|
sortOrder?: number;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface ApproverUserRole {
|
|||
|
|
id: number;
|
|||
|
|
userId: string;
|
|||
|
|
role: 'manager' | 'finance_manager' | 'financier' | 'finance_director' | 'director' | 'top_management';
|
|||
|
|
createdAt: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface BuildingFinancials {
|
|||
|
|
balance: number;
|
|||
|
|
debt: number;
|
|||
|
|
collectionRate: number;
|
|||
|
|
topDebtors: Debtor[];
|
|||
|
|
invoices: Invoice[];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface PassportMeter {
|
|||
|
|
id?: string; // Уникальный ID прибора
|
|||
|
|
resource: 'Heat' | 'Water' | 'Electricity' | 'Gas' | 'Other';
|
|||
|
|
hasMeter: boolean;
|
|||
|
|
name?: string; // Название прибора (для кастомных)
|
|||
|
|
model?: string;
|
|||
|
|
number?: string;
|
|||
|
|
manufacturer?: string; // Производитель
|
|||
|
|
installDate?: string; // Дата установки
|
|||
|
|
lastVerification?: string; // Дата последней поверки
|
|||
|
|
nextVerification?: string; // Дата следующей поверки
|
|||
|
|
currentReading?: number; // Текущее показание
|
|||
|
|
unit?: string; // Единица измерения (Гкал, м³, кВт⋅ч и т.д.)
|
|||
|
|
readings?: MeterReading[]; // История показаний
|
|||
|
|
documents?: string[]; // Файлы документов (паспорт, акты поверки и т.д.)
|
|||
|
|
notes?: string; // Примечания
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface PassportServiceContract {
|
|||
|
|
id: string;
|
|||
|
|
serviceType: string;
|
|||
|
|
providerName: string;
|
|||
|
|
contractNumber: string;
|
|||
|
|
contractDate: string;
|
|||
|
|
expiryDate?: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface EntranceSection {
|
|||
|
|
id: string;
|
|||
|
|
title: string;
|
|||
|
|
elements: {
|
|||
|
|
id: string;
|
|||
|
|
name: string;
|
|||
|
|
generalStatus: 'NOT_SELECTED' | 'OK' | 'WARNING' | 'CRITICAL';
|
|||
|
|
electroStatus: 'NOT_SELECTED' | 'OK' | 'WARNING' | 'CRITICAL';
|
|||
|
|
weldingStatus: 'NOT_SELECTED' | 'OK' | 'WARNING' | 'CRITICAL';
|
|||
|
|
}[];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface PlanItem {
|
|||
|
|
id: string;
|
|||
|
|
year: number;
|
|||
|
|
month: string;
|
|||
|
|
workName: string;
|
|||
|
|
status: 'current' | 'future' | 'completed' | 'carried_over';
|
|||
|
|
progress: number;
|
|||
|
|
estimatedCost: number;
|
|||
|
|
actualCost?: number;
|
|||
|
|
originalYear?: number;
|
|||
|
|
postponeReason?: string;
|
|||
|
|
carryOverReason?: string;
|
|||
|
|
sourceInspectionId?: string;
|
|||
|
|
sourceElementId?: string;
|
|||
|
|
sourceSectionTitle?: string;
|
|||
|
|
/** Силами УК (без бюджета подрядчика) */
|
|||
|
|
byManagement?: boolean;
|
|||
|
|
/** ID счетов на оплату, привязанных к этой работе (план/факт) */
|
|||
|
|
linkedInvoiceIds?: number[];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface BuildingInventoryItem {
|
|||
|
|
id: string;
|
|||
|
|
name: string;
|
|||
|
|
category: 'tool' | 'material' | 'door' | 'consumable';
|
|||
|
|
quantity: number;
|
|||
|
|
unit: string;
|
|||
|
|
lastCheck: string;
|
|||
|
|
source?: 'district' | 'invoice'; // Откуда взята позиция: со склада участка или из счета
|
|||
|
|
unitPrice?: number; // Цена за единицу товара (стоимость / количество)
|
|||
|
|
totalAmount?: number; // Общая стоимость товара (которую должны получить/заплатили)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Акт списания товаров со склада
|
|||
|
|
export interface WriteOffAct {
|
|||
|
|
id: string;
|
|||
|
|
date: string; // Дата списания (ISO date string)
|
|||
|
|
items: WriteOffItem[]; // Список списанных позиций
|
|||
|
|
totalAmount: number; // Общая сумма списания
|
|||
|
|
performer: string; // Кто выполнил списание
|
|||
|
|
reason?: string; // Причина списания (общая для всех позиций)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Позиция в акте списания
|
|||
|
|
export interface WriteOffItem {
|
|||
|
|
itemId: string; // ID позиции в инвентаре
|
|||
|
|
name: string; // Наименование товара
|
|||
|
|
quantity: number; // Количество списанных единиц
|
|||
|
|
unit: string; // Единица измерения
|
|||
|
|
unitPrice: number; // Цена за единицу на момент списания
|
|||
|
|
amount: number; // Сумма списания (quantity * unitPrice)
|
|||
|
|
reason?: string; // Причина списания для этой позиции
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface InspectionItem {
|
|||
|
|
id: string;
|
|||
|
|
category: 'structural' | 'engineering';
|
|||
|
|
label: string;
|
|||
|
|
status: 'ok' | 'repair_needed' | 'not_checked';
|
|||
|
|
description?: string;
|
|||
|
|
wearPercent?: number;
|
|||
|
|
photos?: string[];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Элемент МКД для детального осмотра (как в БИСРИК)
|
|||
|
|
export interface InspectionMKDElement {
|
|||
|
|
id: string;
|
|||
|
|
name: string; // Название элемента (например, "Напольное покрытие", "Освещение")
|
|||
|
|
generalStatus: 'NOT_SELECTED' | 'OK' | 'WARNING' | 'CRITICAL'; // Общий строй
|
|||
|
|
electroStatus: 'NOT_SELECTED' | 'OK' | 'WARNING' | 'CRITICAL'; // Электроснабжение
|
|||
|
|
weldingStatus: 'NOT_SELECTED' | 'OK' | 'WARNING' | 'CRITICAL'; // Слесарные, сварочные работы
|
|||
|
|
repairType?: string; // Вид требуемого ремонта
|
|||
|
|
mainPhoto?: string; // Гл. фото
|
|||
|
|
additionalPhotos?: string[]; // Доп. фото
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Секция осмотра (подъезд, этаж, лифт и т.д.)
|
|||
|
|
export interface InspectionSection {
|
|||
|
|
id: string;
|
|||
|
|
type: 'entrance' | 'floor' | 'lift' | 'common'; // Тип секции
|
|||
|
|
number?: number; // Номер подъезда/этажа/лифта
|
|||
|
|
title: string; // Название секции
|
|||
|
|
elements: InspectionMKDElement[]; // Элементы МКД в этой секции
|
|||
|
|
isCollapsed?: boolean; // Свернута ли секция
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Общая секция (кровля, подвал, фасад и т.д.)
|
|||
|
|
export interface InspectionCommonSection {
|
|||
|
|
id: string;
|
|||
|
|
title: string; // "Кровля", "Подвал", "Фундамент" и т.д.
|
|||
|
|
elements: InspectionMKDElement[];
|
|||
|
|
isCollapsed?: boolean;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface InspectionAct {
|
|||
|
|
id: string;
|
|||
|
|
number: string;
|
|||
|
|
date: string;
|
|||
|
|
inspector: string;
|
|||
|
|
type: 'scheduled_spring' | 'scheduled_autumn' | 'emergency' | 'acceptance';
|
|||
|
|
status: 'draft' | 'completed';
|
|||
|
|
issuesCount: number;
|
|||
|
|
checklist?: InspectionItem[]; // Старый формат (для обратной совместимости)
|
|||
|
|
// Новый формат с динамическими секциями
|
|||
|
|
sections?: InspectionSection[]; // Подъезды, этажи, лифты
|
|||
|
|
commonSections?: InspectionCommonSection[]; // Общие секции (кровля, подвал и т.д.)
|
|||
|
|
basis?: string;
|
|||
|
|
commissionMembers?: string;
|
|||
|
|
ownerRepresentative?: string;
|
|||
|
|
geolocation?: { lat: number, lng: number, timestamp: string };
|
|||
|
|
signatureUrl?: string;
|
|||
|
|
workCategory?: 'maintenance' | 'capital' | 'emergency';
|
|||
|
|
isReadyForWinter?: boolean;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface MeterCheckRound {
|
|||
|
|
id: string;
|
|||
|
|
date: string;
|
|||
|
|
inspector: string;
|
|||
|
|
status: 'active' | 'completed';
|
|||
|
|
buildingId: string;
|
|||
|
|
roundType: 'odpu' | 'apartments' | 'all'; // Тип обхода: только ОДПУ, только квартиры, или все
|
|||
|
|
// Фильтры для обхода
|
|||
|
|
selectedEntrances?: number[]; // Выбранные подъезды (если пусто - все)
|
|||
|
|
selectedFloors?: number[]; // Выбранные этажи (если пусто - все)
|
|||
|
|
apartments?: {
|
|||
|
|
apartmentNumber: string;
|
|||
|
|
entranceNumber?: number; // Номер подъезда
|
|||
|
|
floor?: number; // Этаж
|
|||
|
|
status: 'pending' | 'verified' | 'no_access';
|
|||
|
|
checkedAt?: string;
|
|||
|
|
readings: {
|
|||
|
|
meterId: string;
|
|||
|
|
meterType: string;
|
|||
|
|
previousValue: number;
|
|||
|
|
currentValue?: number;
|
|||
|
|
}[];
|
|||
|
|
}[];
|
|||
|
|
odpuMeters?: {
|
|||
|
|
meterId: string;
|
|||
|
|
meterIndex: number; // Индекс в массиве meters
|
|||
|
|
meterType: string;
|
|||
|
|
meterName: string;
|
|||
|
|
previousValue: number;
|
|||
|
|
currentValue?: number;
|
|||
|
|
status: 'pending' | 'verified' | 'no_access';
|
|||
|
|
checkedAt?: string;
|
|||
|
|
}[];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Комментарий к задаче
|
|||
|
|
export interface TaskComment {
|
|||
|
|
id: string;
|
|||
|
|
authorId?: string;
|
|||
|
|
authorName: string;
|
|||
|
|
text: string;
|
|||
|
|
createdAt: string; // ISO date string
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface BuildingTask {
|
|||
|
|
id: string;
|
|||
|
|
title: string;
|
|||
|
|
description?: string;
|
|||
|
|
deadline: string; // ISO date string
|
|||
|
|
status: 'new' | 'in_progress' | 'done' | 'cancelled';
|
|||
|
|
priority: 'low' | 'medium' | 'high' | 'urgent';
|
|||
|
|
assignedTo?: string; // ID или имя ответственного
|
|||
|
|
assignedToName?: string; // Имя ответственного для отображения
|
|||
|
|
createdAt: string; // ISO date string
|
|||
|
|
updatedAt: string; // ISO date string
|
|||
|
|
createdBy?: string; // ID создателя (постановщик)
|
|||
|
|
createdByName?: string; // Имя постановщика для отображения
|
|||
|
|
category?: string; // Категория задачи
|
|||
|
|
tags?: string[]; // Теги
|
|||
|
|
estimatedHours?: number; // Оценка времени в часах
|
|||
|
|
actualHours?: number; // Фактически потраченное время
|
|||
|
|
buildingId: string; // ID дома (пустая строка — общая задача)
|
|||
|
|
buildingAddress?: string; // Адрес дома для отображения в сводке
|
|||
|
|
comments?: TaskComment[]; // Комментарии
|
|||
|
|
requirePhotoReport?: boolean; // Требуется фото отчёт до/после (по умолчанию true)
|
|||
|
|
photoReportId?: number; // ID записи work_photos, привязанной к задаче
|
|||
|
|
/** Соисполнители (ID пользователей) */
|
|||
|
|
coAssignees?: string[];
|
|||
|
|
coAssigneesNames?: string[];
|
|||
|
|
/** Наблюдатели (ID пользователей) */
|
|||
|
|
observers?: string[];
|
|||
|
|
observersNames?: string[];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface Building {
|
|||
|
|
id: string;
|
|||
|
|
districtId: string;
|
|||
|
|
imageUrl: string;
|
|||
|
|
nps: number;
|
|||
|
|
passport: {
|
|||
|
|
address: string;
|
|||
|
|
apartmentsCount: number;
|
|||
|
|
general: {
|
|||
|
|
address: string;
|
|||
|
|
fiasCode: string;
|
|||
|
|
constructionYear: number;
|
|||
|
|
commissionYear: number;
|
|||
|
|
seriesType: string;
|
|||
|
|
floors: number;
|
|||
|
|
undergroundFloors: number;
|
|||
|
|
totalArea: number;
|
|||
|
|
livingArea: number;
|
|||
|
|
nonLivingArea: number;
|
|||
|
|
commonArea: number;
|
|||
|
|
cadastralNumberBuild: string;
|
|||
|
|
cadastralNumberLand: string;
|
|||
|
|
lastRepairYear?: number;
|
|||
|
|
// Новые поля
|
|||
|
|
entrancesCount?: number; // Кол-во подъездов
|
|||
|
|
hasDifferentFloors?: boolean; // Разноэтажность
|
|||
|
|
officesCount?: number; // Кол-во офисов
|
|||
|
|
// Вычисляемые поля (будут считаться из accounts)
|
|||
|
|
residentialAccountsCount?: number; // Кол-во жилых лицевых счетов
|
|||
|
|
nonResidentialAccountsCount?: number; // Кол-во нежилых лицевых счетов
|
|||
|
|
officesAccountsCount?: number; // Кол-во офисов (из лицевых счетов)
|
|||
|
|
apartmentsCount?: number; // Кол-во квартир (из лицевых счетов)
|
|||
|
|
nonLivingCommonArea?: number; // Площадь нежилых помещений в составе ОДИ
|
|||
|
|
// Дополнительные поля для файлов
|
|||
|
|
customFields?: { [key: string]: { value: any; type: string; files?: string[] } };
|
|||
|
|
};
|
|||
|
|
construction: {
|
|||
|
|
foundationType: string;
|
|||
|
|
foundationMaterial: string;
|
|||
|
|
wallMaterial: string;
|
|||
|
|
floorMaterial: string;
|
|||
|
|
roofType: string;
|
|||
|
|
roofMaterial: string;
|
|||
|
|
roofArea: number;
|
|||
|
|
facadeType: string;
|
|||
|
|
facadeInsulation: boolean;
|
|||
|
|
windowType: string;
|
|||
|
|
// Новые поля
|
|||
|
|
wearPercent?: number; // Процент износа
|
|||
|
|
hasBasement?: boolean; // Подвал
|
|||
|
|
hasTechFloor?: boolean; // Тех этаж
|
|||
|
|
hasAttic?: boolean; // Чердак
|
|||
|
|
hasBasementFloor?: boolean; // Цоколь
|
|||
|
|
// Дополнительные поля для файлов
|
|||
|
|
customFields?: { [key: string]: { value: any; type: string; files?: string[] } };
|
|||
|
|
};
|
|||
|
|
engineering: {
|
|||
|
|
heatingType: string;
|
|||
|
|
heatingWiring: string;
|
|||
|
|
hasITP: boolean;
|
|||
|
|
waterSupplyMaterial: string;
|
|||
|
|
waterSupplyType: string;
|
|||
|
|
sewerMaterial: string;
|
|||
|
|
electricityEntries: number;
|
|||
|
|
hasVRU: boolean;
|
|||
|
|
gasType: string;
|
|||
|
|
ventilationType: string;
|
|||
|
|
// Процент износа для каждой системы
|
|||
|
|
heatingWearPercent?: number;
|
|||
|
|
waterSupplyWearPercent?: number;
|
|||
|
|
sewerWearPercent?: number;
|
|||
|
|
electricityWearPercent?: number;
|
|||
|
|
gasWearPercent?: number;
|
|||
|
|
ventilationWearPercent?: number;
|
|||
|
|
// Дополнительные поля для файлов
|
|||
|
|
customFields?: { [key: string]: { value: any; type: string; files?: string[] } };
|
|||
|
|
};
|
|||
|
|
odpu: {
|
|||
|
|
// Дополнительные поля для файлов
|
|||
|
|
customFields?: { [key: string]: { value: any; type: string; files?: string[] } };
|
|||
|
|
};
|
|||
|
|
meters: PassportMeter[];
|
|||
|
|
lifts: {
|
|||
|
|
count: number;
|
|||
|
|
type: string;
|
|||
|
|
capacity: number;
|
|||
|
|
speed: number;
|
|||
|
|
installYear: number;
|
|||
|
|
factoryNumber: string;
|
|||
|
|
// Новые поля
|
|||
|
|
manufacturer?: string; // Производитель
|
|||
|
|
lastMaintenanceDate?: string; // Дата последнего обслуживания
|
|||
|
|
nextMaintenanceDate?: string; // Дата следующего обслуживания
|
|||
|
|
wearPercent?: number; // Процент износа
|
|||
|
|
status?: string; // Статус (работает, на ремонте и т.д.)
|
|||
|
|
documents?: string[]; // Файлы документов
|
|||
|
|
// Дополнительные поля
|
|||
|
|
customFields?: { [key: string]: { value: any; type: string; files?: string[] } };
|
|||
|
|
}[];
|
|||
|
|
land: {
|
|||
|
|
area: number;
|
|||
|
|
hasPlayground: boolean;
|
|||
|
|
hasSportsGround: boolean;
|
|||
|
|
hasParking: boolean;
|
|||
|
|
hasFencing: boolean;
|
|||
|
|
hasContainerSite: boolean;
|
|||
|
|
// Дополнительные поля для файлов
|
|||
|
|
customFields?: { [key: string]: { value: any; type: string; files?: string[] } };
|
|||
|
|
};
|
|||
|
|
management: {
|
|||
|
|
contractDate: string;
|
|||
|
|
contractNumber: string;
|
|||
|
|
servicesList: string[];
|
|||
|
|
tariffMaintenance: number;
|
|||
|
|
reserveFund?: number; // Резервный фонд (%)
|
|||
|
|
serviceContracts: PassportServiceContract[];
|
|||
|
|
// Дополнительные поля для файлов
|
|||
|
|
customFields?: { [key: string]: { value: any; type: string; files?: string[] } };
|
|||
|
|
};
|
|||
|
|
};
|
|||
|
|
staff: { id: string, role: string, company: string, name: string }[];
|
|||
|
|
entrances: {
|
|||
|
|
id: string;
|
|||
|
|
number: number;
|
|||
|
|
floors: number;
|
|||
|
|
liftsCount: number;
|
|||
|
|
hasBasement: 'NOT_SELECTED' | 'YES' | 'NO';
|
|||
|
|
hasTechFloor: 'NOT_SELECTED' | 'YES' | 'NO';
|
|||
|
|
hasAttic: 'NOT_SELECTED' | 'YES' | 'NO';
|
|||
|
|
hasParking: 'NOT_SELECTED' | 'YES' | 'NO';
|
|||
|
|
hasMansard: 'NOT_SELECTED' | 'YES' | 'NO';
|
|||
|
|
entranceGroupsCount: number;
|
|||
|
|
sections: EntranceSection[];
|
|||
|
|
// Технические характеристики подъезда (сохраняются после первого обхода)
|
|||
|
|
technicalCharacteristics?: {
|
|||
|
|
hasBasement?: 'NOT_SELECTED' | 'YES' | 'NO';
|
|||
|
|
hasTechFloor?: 'NOT_SELECTED' | 'YES' | 'NO';
|
|||
|
|
hasParking?: 'NOT_SELECTED' | 'YES' | 'NO';
|
|||
|
|
hasAttic?: 'NOT_SELECTED' | 'YES' | 'NO';
|
|||
|
|
floorsCount?: number; // Количество этажей в подъезде
|
|||
|
|
liftsCount?: number; // Количество лифтов в подъезде
|
|||
|
|
hasMansard?: 'NOT_SELECTED' | 'YES' | 'NO';
|
|||
|
|
entranceGroupsCount?: number; // Количество входных групп
|
|||
|
|
};
|
|||
|
|
// Элементы МКД по этажам (сохраняются после первого обхода)
|
|||
|
|
floorElements?: {
|
|||
|
|
[floorNumber: number]: InspectionMKDElement[]; // Элементы МКД для каждого этажа
|
|||
|
|
};
|
|||
|
|
}[];
|
|||
|
|
commonSections: EntranceSection[];
|
|||
|
|
accounts: PersonalAccount[];
|
|||
|
|
financials: BuildingFinancials;
|
|||
|
|
requests: { new: number, inProgress: number, overdue: number };
|
|||
|
|
inspectionHistory: InspectionAct[];
|
|||
|
|
tasks: BuildingTask[];
|
|||
|
|
annualPlan: PlanItem[];
|
|||
|
|
inventory: BuildingInventoryItem[];
|
|||
|
|
writeOffHistory: WriteOffAct[];
|
|||
|
|
residents: { id: string, name: string, role: string, apartment: string, mood: string, lastContact: string }[];
|
|||
|
|
reports: { id: string, month: string, status: string }[];
|
|||
|
|
isDirty: boolean;
|
|||
|
|
meterCheckRounds?: MeterCheckRound[];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ========= ФИНАНСОВЫЕ ДАННЫЕ ИЗ 1С =========
|
|||
|
|
|
|||
|
|
export interface FinancialReport {
|
|||
|
|
id: number;
|
|||
|
|
filename: string;
|
|||
|
|
fileType: 'CSV' | 'XLSX';
|
|||
|
|
reportType?: 'debtors' | 'balance_sheet' | 'balance_sheet_76' | 'other';
|
|||
|
|
uploadedAt: string;
|
|||
|
|
uploadedBy?: string;
|
|||
|
|
status: 'processing' | 'completed' | 'failed' | 'partial';
|
|||
|
|
totalRows?: number;
|
|||
|
|
processedRows: number;
|
|||
|
|
errorRows: number;
|
|||
|
|
errorLog?: any;
|
|||
|
|
mappingId?: number;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface FinancialReportMapping {
|
|||
|
|
id: number;
|
|||
|
|
name: string;
|
|||
|
|
description?: string;
|
|||
|
|
isDefault: boolean;
|
|||
|
|
columnMappings: Record<string, string>; // { "source_column": "target_field" }
|
|||
|
|
createdAt: string;
|
|||
|
|
updatedAt: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface BuildingFinancialData {
|
|||
|
|
id: number;
|
|||
|
|
buildingId: string;
|
|||
|
|
reportId?: number;
|
|||
|
|
periodStart: string; // ISO date
|
|||
|
|
periodEnd: string; // ISO date
|
|||
|
|
periodType: 'month' | 'quarter' | 'year';
|
|||
|
|
totalIncome: number;
|
|||
|
|
incomeByItems: Record<string, number>;
|
|||
|
|
totalExpenses: number;
|
|||
|
|
expensesByItems: Record<string, number>;
|
|||
|
|
balance: number;
|
|||
|
|
metadata?: any;
|
|||
|
|
createdAt: string;
|
|||
|
|
updatedAt: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface ProcessingJob {
|
|||
|
|
jobId: string;
|
|||
|
|
status: 'pending' | 'processing' | 'completed' | 'failed';
|
|||
|
|
progress: number; // 0-100
|
|||
|
|
currentStep?: string;
|
|||
|
|
errors: ProcessingError[];
|
|||
|
|
warnings: string[];
|
|||
|
|
result?: {
|
|||
|
|
totalRows: number;
|
|||
|
|
processedRows: number;
|
|||
|
|
errorRows: number;
|
|||
|
|
buildingsFound: number;
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface ProcessingError {
|
|||
|
|
row: number;
|
|||
|
|
column?: string;
|
|||
|
|
message: string;
|
|||
|
|
suggestion?: string;
|
|||
|
|
data?: any;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ========= ОФИС: ЗАЯВКИ НА РЕМОНТ ТЕХНИКИ =========
|
|||
|
|
|
|||
|
|
export type OfficeEquipmentType = 'pc' | 'laptop' | 'air_conditioner' | 'printer' | 'other';
|
|||
|
|
export type RepairRequestStatus =
|
|||
|
|
| 'new'
|
|||
|
|
| 'search_contractor' // Поиск подрядчика
|
|||
|
|
| 'agreed_with_contractor' // Договорились с подрядчиком
|
|||
|
|
| 'waiting_delivery' // Ожидание поставки
|
|||
|
|
| 'taken_for_repair' // Увезли на ремонт
|
|||
|
|
| 'self_repair' // Ремонт самостоятельно
|
|||
|
|
| 'in_progress' // В работе
|
|||
|
|
| 'completed' // Выполнена
|
|||
|
|
| 'canceled'; // Отменена
|
|||
|
|
|
|||
|
|
export interface OfficeEquipment {
|
|||
|
|
id: number;
|
|||
|
|
name: string;
|
|||
|
|
type: OfficeEquipmentType;
|
|||
|
|
brand?: string;
|
|||
|
|
model?: string;
|
|||
|
|
serialNumber?: string;
|
|||
|
|
assignedTo?: string;
|
|||
|
|
purchaseDate?: string;
|
|||
|
|
warrantyUntil?: string;
|
|||
|
|
condition: 'good' | 'fair' | 'poor';
|
|||
|
|
notes?: string;
|
|||
|
|
createdAt: string;
|
|||
|
|
updatedAt: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export type OfficeEquipmentHistoryType = 'purchase' | 'issue' | 'transfer' | 'repair' | 'write_off';
|
|||
|
|
|
|||
|
|
export interface OfficeEquipmentHistoryItem {
|
|||
|
|
id: number | string;
|
|||
|
|
equipment_id: number;
|
|||
|
|
event_type: OfficeEquipmentHistoryType;
|
|||
|
|
event_date: string;
|
|||
|
|
assigned_to?: string;
|
|||
|
|
assigned_from?: string;
|
|||
|
|
reason?: string;
|
|||
|
|
created_at: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface OfficeRepairRequest {
|
|||
|
|
id: number;
|
|||
|
|
equipmentId: number;
|
|||
|
|
equipment?: OfficeEquipment;
|
|||
|
|
requesterName: string;
|
|||
|
|
description: string;
|
|||
|
|
priority: 'low' | 'medium' | 'high' | 'urgent';
|
|||
|
|
status: RepairRequestStatus;
|
|||
|
|
assignedTo?: string;
|
|||
|
|
createdAt: string;
|
|||
|
|
startedAt?: string;
|
|||
|
|
completedAt?: string;
|
|||
|
|
canceledAt?: string;
|
|||
|
|
cancelReason?: string;
|
|||
|
|
expectedReturnDate?: string; // Ожидаемая дата возврата из ремонта
|
|||
|
|
waitingDeliveryDeadline?: string; // Примерный срок при ожидание поставки
|
|||
|
|
waitingDeliveryContacts?: string; // Контакты для уточнения поставки
|
|||
|
|
takenForRepairDeadline?: string; // Срок при увезли на ремонт
|
|||
|
|
takenForRepairContacts?: string; // Контакты при увезли на ремонт
|
|||
|
|
agreedContractorPrice?: number; // Цена при договорились с подрядчиком
|
|||
|
|
solution?: string;
|
|||
|
|
attachments?: string[];
|
|||
|
|
comments?: Array<{
|
|||
|
|
author: string;
|
|||
|
|
text: string;
|
|||
|
|
createdAt: string;
|
|||
|
|
}>;
|
|||
|
|
isPaid?: boolean; // Платный ремонт
|
|||
|
|
cost?: number; // Стоимость ремонта
|
|||
|
|
costEstimated?: boolean; // Стоимость предварительная (требуется диагностика)
|
|||
|
|
invoiceId?: number; // ID счета на оплату
|
|||
|
|
invoiceUrl?: string; // Ссылка на счет
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ========= ОФИС: БАЗА ЗНАНИЙ =========
|
|||
|
|
|
|||
|
|
export interface KnowledgeBaseCategory {
|
|||
|
|
id: number;
|
|||
|
|
name: string;
|
|||
|
|
slug: string;
|
|||
|
|
parentId?: number;
|
|||
|
|
parent?: KnowledgeBaseCategory;
|
|||
|
|
description?: string;
|
|||
|
|
icon?: string;
|
|||
|
|
sortOrder: number;
|
|||
|
|
createdAt: string;
|
|||
|
|
updatedAt: string;
|
|||
|
|
children?: KnowledgeBaseCategory[];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface KnowledgeBaseArticle {
|
|||
|
|
id: number;
|
|||
|
|
title: string;
|
|||
|
|
slug: string;
|
|||
|
|
categoryId?: number;
|
|||
|
|
category?: KnowledgeBaseCategory;
|
|||
|
|
content: string;
|
|||
|
|
contentType: 'markdown' | 'html';
|
|||
|
|
author: string;
|
|||
|
|
version: number;
|
|||
|
|
parentVersionId?: number;
|
|||
|
|
isPublished: boolean;
|
|||
|
|
tags?: string[];
|
|||
|
|
attachments?: string[];
|
|||
|
|
viewCount: number;
|
|||
|
|
createdAt: string;
|
|||
|
|
updatedAt: string;
|
|||
|
|
publishedAt?: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ========= ОФИС: СОВЕЩАНИЯ И ПЕРЕГОВОРНЫЕ =========
|
|||
|
|
|
|||
|
|
export interface MeetingRoom {
|
|||
|
|
id: number;
|
|||
|
|
name: string;
|
|||
|
|
capacity: number;
|
|||
|
|
location?: string;
|
|||
|
|
equipment?: string[]; // ['projector', 'whiteboard', 'video_conference']
|
|||
|
|
description?: string;
|
|||
|
|
isActive: boolean;
|
|||
|
|
createdAt: string;
|
|||
|
|
updatedAt: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export type MeetingStatus = 'scheduled' | 'in_progress' | 'completed' | 'canceled';
|
|||
|
|
|
|||
|
|
export interface Meeting {
|
|||
|
|
id: number;
|
|||
|
|
title: string;
|
|||
|
|
description?: string;
|
|||
|
|
organizer: string;
|
|||
|
|
startTime: string; // ISO datetime
|
|||
|
|
endTime: string; // ISO datetime
|
|||
|
|
roomId?: number;
|
|||
|
|
room?: MeetingRoom;
|
|||
|
|
participants: string[];
|
|||
|
|
agenda?: string;
|
|||
|
|
notes?: string;
|
|||
|
|
conclusions?: string; // Заключения совещания
|
|||
|
|
status: MeetingStatus;
|
|||
|
|
reminderSent: boolean;
|
|||
|
|
reminderTime?: string;
|
|||
|
|
attachments?: string[];
|
|||
|
|
createdAt: string;
|
|||
|
|
updatedAt: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export type MeetingBookingStatus = 'active' | 'completed' | 'canceled';
|
|||
|
|
|
|||
|
|
export interface MeetingBooking {
|
|||
|
|
id: number;
|
|||
|
|
roomId: number;
|
|||
|
|
room?: MeetingRoom;
|
|||
|
|
meetingId?: number;
|
|||
|
|
meeting?: Meeting;
|
|||
|
|
bookedBy: string;
|
|||
|
|
startTime: string; // ISO datetime
|
|||
|
|
endTime: string; // ISO datetime
|
|||
|
|
purpose?: string;
|
|||
|
|
status: MeetingBookingStatus;
|
|||
|
|
createdAt: string;
|
|||
|
|
updatedAt: string;
|
|||
|
|
}
|