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; expensesByItems?: Record; }; // Фото отчеты 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; 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>; 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; 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; // { "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; totalExpenses: number; expensesByItems: Record; 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; }