Files
mkd/types.ts
2026-02-04 00:17:04 +05:00

2049 lines
65 KiB
TypeScript
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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';
/** Оценка подпункта: 15 (1 — худшее, 5 — лучшее). Итог по пункту — среднее арифметическое. */
export type InspectionSubItemRating = number; // 1 | 2 | 3 | 4 | 5
/** Подпункт осмотра (label берётся из справочника по key) */
export interface InspectionSubItem {
key: string;
label?: string;
/** Оценка 15; не участвует в среднем при noAccess/notPresent */
rating: number | null;
description: string | null;
noAccess: boolean;
notPresent: boolean;
}
/** Данные по одному пункту осмотра (категория). overall — среднее по подпунктам (вычисляемое или сохранённое). */
export interface InspectionCategoryData {
subItems: InspectionSubItem[];
/** Среднее арифметическое оценок подпунктов (15), вычисляется из 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;
}