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:
@@ -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',
|
||||
|
||||
@@ -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',)
|
||||
|
||||
+34
@@ -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='Типы локаций'),
|
||||
),
|
||||
]
|
||||
@@ -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:
|
||||
# Проверяем, не добавлен ли уже пользователь в этот канал
|
||||
|
||||
Reference in New Issue
Block a user