feat: документация из Nextcloud, график у сотрудника, автоочистка фото, мониторинг, итог мастера, фильтр по местам; доп. сервисы в docker-compose
- docker-compose.yml: профили docs (Nextcloud), office (ONLYOFFICE Document Server), ai (Ollama + скачивание модели) - автонастройка: адреса Nextcloud, ONLYOFFICE, портала и модели подставляются из .env или по именам сервисов во внутренней сети Docker; значения из интерфейса важнее - локальная модель в своей сети (имя сервиса, частный IP) подключается без ключа API - .env.example, docs/DEPLOY.md (раздел 7а), README
This commit is contained in:
+23
-1
@@ -27,8 +27,8 @@ class Position(Base):
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
name: Mapped[str] = mapped_column(String(100), unique=True)
|
||||
kind: Mapped[str] = mapped_column(String(12), default="technician")
|
||||
# устарело: график теперь у сотрудника (User.schedule_id); значение перенесено сотрудникам при обновлении и больше не используется
|
||||
schedule_id: Mapped[int | None] = mapped_column(ForeignKey("schedules.id"), nullable=True)
|
||||
schedule: Mapped[ShiftSchedule | None] = relationship(lazy="joined")
|
||||
|
||||
|
||||
class Department(Base):
|
||||
@@ -55,6 +55,9 @@ class User(Base):
|
||||
is_active: Mapped[bool] = mapped_column(Boolean, default=True)
|
||||
position_id: Mapped[int | None] = mapped_column(ForeignKey("positions.id"), nullable=True)
|
||||
position: Mapped[Position | None] = relationship(lazy="joined")
|
||||
# график работы сотрудника: должность выполняет тот, кто её занимает и сейчас на смене. Пусто — всегда на смене
|
||||
schedule_id: Mapped[int | None] = mapped_column(ForeignKey("schedules.id"), nullable=True)
|
||||
schedule: Mapped[ShiftSchedule | None] = relationship(lazy="joined")
|
||||
department_id: Mapped[int | None] = mapped_column(ForeignKey("departments.id"), nullable=True)
|
||||
department: Mapped[Department | None] = relationship(lazy="joined", foreign_keys=[department_id])
|
||||
# карточка сотрудника
|
||||
@@ -89,6 +92,8 @@ class Equipment(Base):
|
||||
department_id: Mapped[int | None] = mapped_column(ForeignKey("departments.id"), nullable=True)
|
||||
# кран (грузоподъёмное оборудование): при включённой карте кранов показывается на ней, а не на карте завода
|
||||
is_crane: Mapped[bool] = mapped_column(Boolean, default=False)
|
||||
# папка документации в Nextcloud (путь от корня документации в настройках сервера); пусто — не привязана
|
||||
docs_path: Mapped[str] = mapped_column(String(500), default="")
|
||||
|
||||
|
||||
class ServiceUnit(Base):
|
||||
@@ -160,6 +165,7 @@ class ChecklistItem(Base):
|
||||
max_value: Mapped[float | None] = mapped_column(Float, nullable=True)
|
||||
required: Mapped[bool] = mapped_column(Boolean, default=True)
|
||||
photo: Mapped[str] = mapped_column(String(10), default="none") # none|optional|required
|
||||
photo_keep: Mapped[bool] = mapped_column(Boolean, default=False) # фото хранить более года (каталог keep/, очистка не трогает)
|
||||
|
||||
|
||||
class OrderTemplate(Base):
|
||||
@@ -232,6 +238,7 @@ class OrderItem(Base):
|
||||
answered_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
|
||||
out_of_range: Mapped[bool] = mapped_column(Boolean, default=False)
|
||||
photo: Mapped[str] = mapped_column(String(10), default="none")
|
||||
photo_keep: Mapped[bool] = mapped_column(Boolean, default=False)
|
||||
service_unit_id: Mapped[int | None] = mapped_column(ForeignKey("service_units.id"), nullable=True) # «узел обслужен»
|
||||
is_meter: Mapped[bool] = mapped_column(Boolean, default=False) # пункт «наработка, м/ч» — обновляет счётчик машины
|
||||
photos: Mapped[list["Photo"]] = relationship(
|
||||
@@ -276,6 +283,20 @@ class Feedback(Base):
|
||||
answered_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
|
||||
|
||||
|
||||
class DocIndex(Base):
|
||||
"""Индекс документации для AI-помощника: имя файла и фрагмент текста (в нижнем регистре). Строится заново из Nextcloud."""
|
||||
__tablename__ = "doc_index"
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
equipment_id: Mapped[int] = mapped_column(ForeignKey("equipment.id", ondelete="CASCADE"), index=True)
|
||||
path: Mapped[str] = mapped_column(String(1000)) # путь файла от папки машины
|
||||
name: Mapped[str] = mapped_column(String(300))
|
||||
etag: Mapped[str] = mapped_column(String(200), default="")
|
||||
size: Mapped[int] = mapped_column(Integer, default=0)
|
||||
text: Mapped[str] = mapped_column(Text, default="") # извлечённый текст, нижний регистр, обрезан
|
||||
note: Mapped[str] = mapped_column(String(200), default="") # почему текста нет (большой файл, ошибка чтения)
|
||||
indexed_at: Mapped[datetime] = mapped_column(DateTime, default=config.now)
|
||||
|
||||
|
||||
class Setting(Base):
|
||||
__tablename__ = "settings"
|
||||
key: Mapped[str] = mapped_column(String(64), primary_key=True)
|
||||
@@ -464,6 +485,7 @@ class DailyTask(Base):
|
||||
updated_at: Mapped[datetime] = mapped_column(DateTime, default=config.now)
|
||||
finished_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
|
||||
labor_hours: Mapped[float | None] = mapped_column(Float, nullable=True) # трудоёмкость (норма), ч — для отчёта по сотрудникам
|
||||
photo_keep: Mapped[bool] = mapped_column(Boolean, default=False) # фото к заданию хранить более года
|
||||
|
||||
|
||||
class TaskEvent(Base):
|
||||
|
||||
Reference in New Issue
Block a user