2049 lines
65 KiB
TypeScript
Executable File
2049 lines
65 KiB
TypeScript
Executable File
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;
|
||
} |