mattermost: синхронизация команд/каналов по типу локации

Добавлен фильтр по user_location_type (Аптека/Склад/Офис) в TeamSyncSettings
и ChannelSyncSettings — наряду с фильтром по отделам. При включении обоих
фильтров условия объединяются по AND.

- поля filter_by_location_type + location_types (ArrayField, чекбоксы в админке);
- логика add_user_to_teams/channels учитывает тип локации;
- django.contrib.postgres в INSTALLED_APPS (для ArrayField).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-09-16 16:17:16 +03:00
parent 32ad359879
commit 704d5d9538
5 changed files with 124 additions and 12 deletions
+1
View File
@@ -80,6 +80,7 @@ INSTALLED_APPS = [
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.postgres', # ArrayField (mattermost sync location_types)
# Third-party
'treenode',
+34
View File
@@ -1,8 +1,10 @@
from django import forms
from django.contrib import admin
from unfold.admin import ModelAdmin, TabularInline
from unfold.decorators import display
from django.utils.html import format_html
from .models import (
LOCATION_TYPE_CHOICES,
MattermostUser,
MattermostTeam,
MattermostChannel,
@@ -14,6 +16,28 @@ from .models import (
)
class TeamSyncSettingsForm(forms.ModelForm):
location_types = forms.MultipleChoiceField(
choices=LOCATION_TYPE_CHOICES, required=False,
widget=forms.CheckboxSelectMultiple, label='Типы локаций',
)
class Meta:
model = TeamSyncSettings
fields = '__all__'
class ChannelSyncSettingsForm(forms.ModelForm):
location_types = forms.MultipleChoiceField(
choices=LOCATION_TYPE_CHOICES, required=False,
widget=forms.CheckboxSelectMultiple, label='Типы локаций',
)
class Meta:
model = ChannelSyncSettings
fields = '__all__'
@admin.register(MattermostBot)
class MattermostBotAdmin(ModelAdmin):
"""Боты Mattermost по ролям. Токен хранится в БД зашифрованным."""
@@ -39,6 +63,7 @@ class MattermostTeamAdmin(ModelAdmin):
@admin.register(TeamSyncSettings)
class TeamSyncSettingsAdmin(ModelAdmin):
"""Админка для настроек синхронизации команд"""
form = TeamSyncSettingsForm
list_display = (
'team_display',
'is_active_display',
@@ -63,6 +88,10 @@ class TeamSyncSettingsAdmin(ModelAdmin):
'fields': ('filter_by_department', 'departments'),
'description': 'Если включено, в команду будут добавлены только пользователи из выбранных отделов'
}),
('Фильтрация по типу локации', {
'fields': ('filter_by_location_type', 'location_types'),
'description': 'Если включено, в команду будут добавлены только сотрудники с выбранным типом локации (Аптека/Склад/Офис). При совместном включении с фильтром по отделам условия объединяются по И.'
}),
('Дополнительно', {
'fields': ('notes',),
'classes': ('collapse',)
@@ -173,6 +202,7 @@ class MattermostChannelAdmin(ModelAdmin):
@admin.register(ChannelSyncSettings)
class ChannelSyncSettingsAdmin(ModelAdmin):
"""Админка для настроек синхронизации каналов"""
form = ChannelSyncSettingsForm
list_display = (
'channel_display',
'team_display',
@@ -197,6 +227,10 @@ class ChannelSyncSettingsAdmin(ModelAdmin):
'fields': ('filter_by_department', 'departments'),
'description': 'Если включено, в канал будут добавлены только пользователи из выбранных отделов'
}),
('Фильтрация по типу локации', {
'fields': ('filter_by_location_type', 'location_types'),
'description': 'Если включено, в канал будут добавлены только сотрудники с выбранным типом локации (Аптека/Склад/Офис). При совместном включении с фильтром по отделам условия объединяются по И.'
}),
('Дополнительно', {
'fields': ('notes',),
'classes': ('collapse',)
@@ -0,0 +1,34 @@
# Generated by Django 6.0.3 on 2026-09-16 13:16
import django.contrib.postgres.fields
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('mattermost_connector', '0003_mattermostbot'),
]
operations = [
migrations.AddField(
model_name='channelsyncsettings',
name='filter_by_location_type',
field=models.BooleanField(default=False, help_text='Добавлять только пользователей с выбранным типом локации (Аптека/Склад/Офис)', verbose_name='Фильтровать по типу локации'),
),
migrations.AddField(
model_name='channelsyncsettings',
name='location_types',
field=django.contrib.postgres.fields.ArrayField(base_field=models.CharField(choices=[('pharmacy', 'Аптека'), ('warehouse', 'Склад'), ('office', 'Офис')], max_length=20), blank=True, default=list, help_text='Пользователи с этими типами локации будут добавлены в канал', verbose_name='Типы локаций'),
),
migrations.AddField(
model_name='teamsyncsettings',
name='filter_by_location_type',
field=models.BooleanField(default=False, help_text='Добавлять только пользователей с выбранным типом локации (Аптека/Склад/Офис)', verbose_name='Фильтровать по типу локации'),
),
migrations.AddField(
model_name='teamsyncsettings',
name='location_types',
field=django.contrib.postgres.fields.ArrayField(base_field=models.CharField(choices=[('pharmacy', 'Аптека'), ('warehouse', 'Склад'), ('office', 'Офис')], max_length=20), blank=True, default=list, help_text='Пользователи с этими типами локации будут добавлены в команду', verbose_name='Типы локаций'),
),
]
+34
View File
@@ -1,10 +1,18 @@
# models.py
from django.contrib.postgres.fields import ArrayField
from django.db import models
from django.contrib.auth import get_user_model
from django.utils import timezone
from core.models.fields import EncryptedCharField
# Типы локаций сотрудника (Employee.LOCATION_TYPES) — для фильтра синхронизации.
LOCATION_TYPE_CHOICES = [
('pharmacy', 'Аптека'),
('warehouse', 'Склад'),
('office', 'Офис'),
]
User = get_user_model()
@@ -180,6 +188,19 @@ class TeamSyncSettings(models.Model):
help_text='Отделы, пользователи которых будут добавлены в команду'
)
filter_by_location_type = models.BooleanField(
default=False,
verbose_name='Фильтровать по типу локации',
help_text='Добавлять только пользователей с выбранным типом локации (Аптека/Склад/Офис)'
)
location_types = ArrayField(
models.CharField(max_length=20, choices=LOCATION_TYPE_CHOICES),
blank=True, default=list,
verbose_name='Типы локаций',
help_text='Пользователи с этими типами локации будут добавлены в команду'
)
is_active = models.BooleanField(
default=True,
verbose_name='Активна',
@@ -248,6 +269,19 @@ class ChannelSyncSettings(models.Model):
help_text='Отделы, пользователи которых будут добавлены в канал'
)
filter_by_location_type = models.BooleanField(
default=False,
verbose_name='Фильтровать по типу локации',
help_text='Добавлять только пользователей с выбранным типом локации (Аптека/Склад/Офис)'
)
location_types = ArrayField(
models.CharField(max_length=20, choices=LOCATION_TYPE_CHOICES),
blank=True, default=list,
verbose_name='Типы локаций',
help_text='Пользователи с этими типами локации будут добавлены в канал'
)
is_active = models.BooleanField(
default=True,
verbose_name='Активна',
@@ -352,13 +352,18 @@ def add_user_to_teams(employee: Employee) -> bool:
should_add = False
if setting.auto_add_users:
# Фильтры комбинируются по AND: если включены оба (отдел + тип
# локации), пользователь должен подходить под оба условия.
should_add = True
if setting.filter_by_department:
# Проверяем, что пользователь из нужного отдела
if employee.department and setting.departments.filter(id=employee.department.id).exists():
should_add = True
else:
# Добавляем всех пользователей
should_add = True
should_add = should_add and bool(
employee.department_id
and setting.departments.filter(id=employee.department_id).exists()
)
if setting.filter_by_location_type:
should_add = should_add and (
employee.user_location_type in (setting.location_types or [])
)
if should_add:
# Проверяем, не добавлен ли уже пользователь в эту команду
@@ -433,13 +438,17 @@ def add_user_to_channels(employee: Employee) -> bool:
should_add = False
if setting.auto_add_users:
# Фильтры комбинируются по AND (отдел + тип локации).
should_add = True
if setting.filter_by_department:
# Проверяем, что пользователь из нужного отдела
if employee.department and setting.departments.filter(id=employee.department.id).exists():
should_add = True
else:
# Добавляем всех пользователей
should_add = True
should_add = should_add and bool(
employee.department_id
and setting.departments.filter(id=employee.department_id).exists()
)
if setting.filter_by_location_type:
should_add = should_add and (
employee.user_location_type in (setting.location_types or [])
)
if should_add:
# Проверяем, не добавлен ли уже пользователь в этот канал