/
fedotoff
/
servit_prev
Обзор
Документация
Войти
/
fedotoff
/
servit_prev
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
sd/models.py
2 299 строк
87 KB
fedotoff
init Gitverse repo
20 окт 2025, 12:59
20 окт 2025, 12:59
06f2e8a
Код
Авторство
О чём код?
from django.db import models from django.contrib.auth.models import User from django.shortcuts import reverse from django.db.models import Q # from sd.data_methods import get_rec, get_fields_to_dict from django.shortcuts import render, redirect from django.views.generic import View from itertools import chain import datetime from django.core.paginator import Paginator from django.db.models import Sum import operator #asdfg # Индексы в session['check_lists'] - уже проверенные запрещённые и разрешенные объекты. # Если не кэшировать проверку - по миллиону раз одно и то же проверяет. access_granted = 'g'; access_restricted = 'r' iaccess_granted = 0; iaccess_restricted = 1 icat = 0; imnfr = 1; imodel = 2 T_CI_location = (('A', 'Апатиты'), ('P', 'Полярные Зори'), ('K', 'Кандалакша')) T_message_type = (('s', 'Система'), ('c', 'Комментарий'), ('r', 'Результат')) T_user_types = (('s', 'super'), ('u', 'user'), ('sc', 'sc-user')) class TAG(models.Model): name = models.CharField(max_length=10) def __str__(self): return self.name class STATUS(models.Model): caption = models.CharField(max_length=10) def __str__(self): return self.caption class AGROUP(models.Model): caption = models.CharField(max_length=10, unique=True) description = models.CharField(max_length=50, default='') def __str__(self): return self.caption class LOG(models.Model): operation = models.ForeignKey('LOG_OPERATION', on_delete=models.CASCADE, null=False) objct = models.ForeignKey('REC', on_delete=models.CASCADE, null=True, default=None) creator = models.ForeignKey('REC', on_delete=models.CASCADE, null=False, related_name='%(class)s_creator') comment = models.CharField(max_length=100) #, blank=False, default='') agr_visible_for = models.ManyToManyField('AGROUP', blank=True, related_name='%(class)s_visible_for') created_at = models.DateTimeField(auto_now_add=True) class Meta: ordering = ['-created_at'] def __str__(self): obj_str = '' if self.objct: obj_str = str(self.objct.name) + ' ' + str(self.objct) rslt = str(self.creator) + ' [' + str(self.operation.caption) + ' =' + self.comment + '] ' + obj_str # if len(self.comment) > 1: # rslt += ' (' + self.comment + ')' return rslt # =============================================== [ NAMES ] =============================================== class LOG_OPERATION(models.Model): caption = models.CharField(max_length=20, unique=True, blank=False) def __str__(self): return self.caption class LINKNAME(models.Model): agroup = models.ManyToManyField('AGROUP', blank=True) name = models.CharField(max_length=15, unique=True) comment = models.CharField(max_length=100, blank=True) def __str__(self): return str(self.id) + ' ' + self.name class FIELDNAME(models.Model): agroup = models.ManyToManyField('AGROUP', blank=True) name = models.CharField(max_length=15, unique=True) system = models.BooleanField(blank=True, default=False) def __str__(self): return self.name # ТИП СУЩНОСТИ class ENTITYNAME(models.Model): # Все группы доступа, назначенные пользователю agroup = models.ManyToManyField('AGROUP', blank=True) # Статусы, доступные для каждого типа сущности statuses = models.ManyToManyField('STATUS', blank=True) # Название (имя) типа сущности name = models.CharField(max_length=15, unique=True) # Поля сущности для формы создания экземпляра сущности fields_chf = models.ManyToManyField('FIELDNAME', blank=True, related_name='%(class)s_chf_fields') fields_dec = models.ManyToManyField('FIELDNAME', blank=True, related_name='%(class)s_dec_fields') fields_txt = models.ManyToManyField('FIELDNAME', blank=True, related_name='%(class)s_txt_fields') fields_date = models.ManyToManyField('FIELDNAME', blank=True, related_name='%(class)s_date_fields') fields_bool = models.ManyToManyField('FIELDNAME', blank=True, related_name='%(class)s_bool_fields') create_child = models.ManyToManyField('ENTITYNAME', blank=True, related_name='%(class)s_create_childs') tags_avail = models.ManyToManyField('TAG', blank=True) # Внутренняя (в пределах сущности нумерация). Так как все объекты физически в БД являются сущностью # одного и того же типа, то для их раздельной нумерации нужно это поле. internal_num = models.IntegerField(default=1) def __str__(self): return self.name # ===============================================[ REC ]=============================================== # ОСНОВНАЯ ЗАПИСЬ - ОБЪЕКТЫ ВСЕХ ТИПОВ СУЩНОСТЕЙ class REC(models.Model): agr_visible_for = models.ManyToManyField('AGROUP', blank=True, related_name='%(class)s_visible_for') agr_can_see = models.ManyToManyField('AGROUP', blank=True, related_name='%(class)s_can_see') name = models.ForeignKey('ENTITYNAME', on_delete=models.CASCADE, null=True) caption = models.CharField(max_length=30, blank=True) status = models.ForeignKey('STATUS', on_delete=models.DO_NOTHING, null=True, default=2) status_color = models.CharField(max_length=10, default='light') can_sell = models.BooleanField(blank=True, default=False) service = models.BooleanField(blank=True, default=False) # True - услуга, False - Товар created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) creator = models.ForeignKey('REC', on_delete=models.DO_NOTHING, null=True, blank=True) self_id = models.IntegerField(blank=False, default=1) tags = models.ManyToManyField('TAG', blank=True, related_name='%(class)s_tags') class Meta: ordering = ['-created_at'] def set_status(self, status_str): status_obj = STATUS.objects.get(caption=status_str) status_color = get_status_color(status_str) self.status = status_obj self.status_color = status_color self.save() def __repr__(self): return str(self.id) + ' ' + str(self.name) + ' ' + self.caption def __str__(self): # return get_entity_fields_to_str(self) if self.name.name != 'пользователь': rec_name = str(self.id) + ' ' else: rec_name = '' #rec_name = str(self.id) + ' ' + str(self.name) + ' ' + self.caption rec_name += str(self.name) + ' ' + self.caption # ПОЛЬЗОВАТЕЛЬ if self.name.name == 'пользователь': phn = '' name_field = FieldChf.objects.filter(Q(owner=self) & Q(name__name='имя')).first() if name_field: rec_name = name_field.v if name_field.caption != '': #rec_name = name_field.caption + ' ' + rec_name rec_name = 'Контр. ' + rec_name else: phn = get_entity_fields_by_name_and_type(self, 'телефон', 'chf').first() #phn = str(get_entity_fields_by_name_and_type(self, 'телефон', 'chf').first().v) if phn != '': rec_name += ' ' + str(phn) return rec_name # СООБЩЕНИЕ if self.name.name == 'сообщение': try: #message = FieldTXT.objects.get(owner=self).v messages = FieldTXT.objects.filter(owner=self) message = '' for m in messages: message += m.v #+ '|' except: message = 'MSG_NOT_FOUND' return message # УЕ-СПРАВОЧНИК if self.name.name == 'категория' or self.name.name == 'производитель' or self.name.name == 'модель': return self.caption if self.name.name == 'уе': mdl_link = '' try: mdl_link = LINK.objects.get(Q(L1__name__name='модель') & Q(L2=self)) ci_model = mdl_link.L1.caption except: ci_model = 'mdl_NF' ## try: cat = LINK.objects.filter(Q(L2=mdl_link.L1) & Q(L1__name__name='категория')).first().L1.caption except: cat = '' try: ci_mdl_objct = LINK.objects.get(Q(L1__name__name='модель') & Q(L2__id=mdl_link.L2.id)).L1 #ci_manufacturer = LINK.objects.get(Q(L1__name__name='производитель') & Q(L2__id=mdl_link.L2.id)).L1 ci_manufacturer = LINK.objects.get(Q(L1__name__name='производитель') & Q(L2__id=ci_mdl_objct.id)).L1.caption except: ci_manufacturer = 'mnfr_NF' # result = get_entity_fields_to_str(self) try: caption = FieldChf.objects.get(Q(owner=self) & Q(name__name='описание')).v except: caption = self.name.name sns_q = FieldChf.objects.filter(Q(owner=self) & (Q(name__name__exact='серийный номер') | Q(name__name__exact='imei'))) sns = '' for s in sns_q: sns += s.v #+ '; object has no attribute 'v'' ret_str = cat + ' ' + ci_manufacturer + ' ' + ci_model if sns != '': ret_str += ' [SN/IMEI: ' + sns + ']' return ret_str # МЕСТО if self.name.name == 'место': caption = 'nf' try: caption = FieldTXT.objects.get(Q(owner=self) & Q(name__name='описание')).v except: pass return caption # ОБРАЩЕНИЕ if self.name.name == 'обращение': try: caption = FieldTXT.objects.filter(owner=self, name__name='со слов клиента').first().v except: caption = '' try: ci = LINK.objects.filter(Q(L1__name__name='уе') & Q(L2=self)).first().L1.caption except: ci='' return ''+str(self.self_id)+'#'+str(ci)+" "+str(caption)#str_result # Задача if self.name.name == 'задача': caption = FieldTXT.objects.filter(owner=self, name__name='описание') if len(caption) > 0: caption = caption.first().v else: caption = '' str_result = str(caption) return str_result return rec_name # @property # def get_add_url(self): # self_name = self.name.name # # # if self_name == 'пользователь': # # return 'sdesk/uac/add' # # if self_name == 'категория': # return reverse('ci_cat_add_url') # # if self_name == 'производитель': # return reverse('ci_man_add_url') def get_absolute_url(self): return reverse('entity_show_url', kwargs={'rec_id': self.id}) # ===============================================FIELDS=============================================== # Создаёт объект objects в моделях полей # Содержит метод для создания себяшек независимо от типа себяшки # rec_id - id записи-хозяина в REC # name - имя поля (должно присутствовать в FIELDNAME # v - значение нового поля # # Метод наследуется всеми типами полей данных (chf, dec, ...) class FieldManager(models.Manager): # .Manager - наследование для использования метода create def new_field(self, field_owner, name, v): field_owner = get_rec(field_owner) field_name = FIELDNAME.objects.get(name=name) # Получение объекта имени поля (этого нового поля) if field_name: nf = self.create(owner=field_owner, name=field_name, v=v) return nf else: return 0 def add_fields_by_list(self, rec, fields): field_owner = get_rec(rec) for f in fields: self.new_field(field_owner, f[0], f[1]) def add_agroup(self, agroup): # TODO - добавить добавление agroup к полю pass # Общие поля для всех Fields - чтоб наследовать, а не копипастить в каждую модель class base_field(models.Model): agroup = models.ManyToManyField('AGROUP', blank=True) owner = models.ForeignKey('REC', on_delete=models.CASCADE, null=True) name = models.ForeignKey('FIELDNAME', on_delete=models.CASCADE, null=True) caption = models.CharField(max_length=10, blank=True) # system = models.BooleanField(blank=True, default=False) def __str__(self): # rslt = str(self.owner) + ' ' + str(self.name.name) try: rslt = str(self.v) # += ' ' + str(self.v) except: rslt = self.caption # pass return rslt class FieldChf(base_field): v = models.CharField(max_length=50) objects = FieldManager() class FieldTXT(base_field): v = models.TextField(max_length=1000) objects = FieldManager() class FieldDec(base_field): v = models.DecimalField(max_digits=9, decimal_places=2) objects = FieldManager() class FieldDate(base_field): v = models.DateField() objects = FieldManager() class FieldBool(base_field): v = models.BooleanField() objects = FieldManager() # =============================================== LINKS =============================================== # TOTO: Предусмотреть разрыв прямых связей (кнопочкой крестик) class LINK(models.Model): agroup = models.ManyToManyField('AGROUP', blank=True) name = models.ForeignKey('LINKNAME', on_delete=models.CASCADE, null=True) L1 = models.ForeignKey('REC', on_delete=models.CASCADE, related_name='L1', null=True) L2 = models.ForeignKey('REC', on_delete=models.CASCADE, related_name='L2', null=True) v = models.CharField(max_length=30, blank=True) def __str__(self): return str(self.L1) + '-' + str(self.L2) def get_prototype_fields_by_type(prototype, field_type): fields_list = [] if field_type == 'chf': src = prototype.fields_chf.all() else: if field_type == 'dec': src = prototype.fields_dec.all() else: if field_type == 'txt': src = prototype.fields_txt.all() else: if field_type == 'dat': src = prototype.fields_date.all() else: src = '' if src != '': for c, tp in enumerate(src.all(), start=0): fields_list.append([]) fields_list[c].append(tp) return fields_list else: return [] def get_prototype_fields(entity_name): prototype = ENTITYNAME.objects.get(name=entity_name) fields = [ ['chf', get_prototype_fields_by_type(prototype, 'chf')], ['dec', get_prototype_fields_by_type(prototype, 'dec')], ['txt', get_prototype_fields_by_type(prototype, 'txt')], ['dat', get_prototype_fields_by_type(prototype, 'dat')]] return fields def get_recs_with_fields(entities_recs): """Получение списка полей прототипа для форм создания новых экземпляров сущностей (размещать ТОЛЬКО НА ФОРМЕ ОБЪЕКТА!!!, чтоб был хозяин для полей) :param entities_recs: список объектов REC (сущностей) :return: [] [rec, {'chf': fields, 'dec': fields, 'txt': fields] """ rec_and_fields = [] for c, rec in enumerate(entities_recs, start=0): rec_and_fields.append([]) rec_and_fields[c].append(rec) nm = rec.name.name entity_prototype_fields = get_prototype_fields(nm) rec_and_fields[c].append(entity_prototype_fields) return rec_and_fields def add_checked_entities(group, entity_id): if entity_id in group: group.append(entity_id) def set_access_rights(rights_owner,obj, permission): access_link_name = LINKNAME.objects.get(name='доступ') access_link = LINK(L1=rights_owner, L2=obj, name=access_link_name) access_link.v = permission access_link.save() def check_access(user_session, lc, entity, uac, sys_user=0): """ Возвращает количество совпадающих групп (текущего юзера и проверяемого объекта). Показывает объекты независимо от назначаенных групп если юзер, для которого проводится проверка, - админ или создатель объекта. Процедура получает или sys_user или uac_user. Если не будет ни того ни другого - ой, ошибочка будет. Если объект засвечен группой Public (PBL), то показываем его каждой твари без разбора :param entity: объект доступ к которому нужно проверить. :param sys_user: текущий django-user :param uac: объект ПОЛЬЗОВАТЕЛЬ текущего залогиненного юзера :return: кол-во совпадающих групп, 0 если доступ запрещен и 1 если юзер админ или создатель объекта. """ # TODO: сделать set уже_проверены и засовывать туда проверенные объекты чтоб их по новой не проверять. # НЕ УДАЛЯТЬ !!! # user_session['already_checked'] = [[],[]] # aaa = LINK.objects.filter(name__name='доступ') # for a in aaa: # a.delete() if entity.creator == uac or uac == entity: return 1 if uac == 0 or entity.id in user_session['already_checked'][iaccess_restricted]: return 0 if entity.name.name in ['категория', 'производитель', 'модель']: return 1 adm = AGROUP.objects.get(caption='ADM') if entity == uac or entity.id in user_session['already_checked'][iaccess_granted] or adm in uac.agr_can_see.all(): return 1 current_uac_agr = uac.agr_can_see.all() # Список групп, с которыми можно увидеть проверяемый объект current_entity_agr = entity.agr_visible_for.all() CST = AGROUP.objects.get(caption='CST') intersected_rights = current_uac_agr.intersection(current_entity_agr) uac_is_cst = CST in intersected_rights if (uac_is_cst and intersected_rights.count() > 1) or (not uac_is_cst and intersected_rights.count() > 0): user_session['already_checked'][iaccess_granted].append(entity.id) set_access_rights(uac, entity, access_granted) return 1 has_access_link = True access_link = '' try: access_link = LINK.objects.get(Q(L1=uac) & Q(L2=entity) & Q(name__name__exact='доступ')) except: has_access_link = False if has_access_link: if access_link.v == access_granted: user_session['already_checked'][iaccess_granted].append(entity.id) return 1 elif access_link.v == access_restricted: user_session['already_checked'][iaccess_restricted].append(entity.id) return 0 # Если не указан UAC значит передан system_user public_group = AGROUP.objects.get(caption='PBL') parents = set() def get_parents2(e): p = get_parents(rec=e, user_session=user_session) for prnt in p: parents.add(prnt.id) if prnt == uac: break get_parents2(prnt) #found_uac = False get_parents2(entity) found_uac = True if uac.id in parents else False # AGR if CST in entity.agroup.all() and uac in links_collector['пользователь']: if found_uac and CST in entity.agr_visible_for.all(): # and uac in check_access_lc['пользователь']: user_session['already_checked'][iaccess_granted].append(entity.id) set_access_rights(uac, entity, access_granted) return 1 i_r_c = intersected_rights.count() # Если нет совпадающих групп - ничего не показываем if i_r_c == 0: user_session['already_checked'][iaccess_restricted].append(entity.id) set_access_rights(uac, entity, access_restricted) return 0 # Если есть всего одна совпадающая группа elif i_r_c == 1: # И если это CST (клиент), то запрещаем, т.к. доступ для клиента проверен выше и результат отрицательный if uac_is_cst: user_session['already_checked'][iaccess_restricted].append(entity.id) set_access_rights(uac, entity, access_restricted) return 0 def system_user_to_uac(system_user): try: uac = FieldDec.objects.get(Q(name__name='dj_user') & Q(v=system_user.id)).owner except: uac = 0 return uac def select_object(request, rec_id, mode): if mode == 'add': request.session['current_object_id'] = rec_id elif mode == 'delete': del request.session['current_object_id'] elif mode == 'link': create_link('уровень', request.session['current_object_id'], rec_id) elif mode == 'link_group': # Подчинение группы объектов - подчинение закладок выбранному объекту или выбранного закладкам # Для выбора режима - rec_id = 0, mode = 'link_group' # 0 - В/З, 1 - З/В #} # Если нажата кнопка ВЗ - подчиняем закладки выбранному объекту # Если нажата кнопка ЗВ - подчиняем выбранный объект закладкам if rec_id == 0: # 0 - В/З for sticker in request.session['stickers']: create_link('уровень', request.session['current_object_id'], sticker) elif rec_id == 1: # 1 - З/В for sticker in request.session['stickers']: create_link('уровень', sticker, request.session['current_object_id']) return redirect(reverse('entity_show_url', args=[rec_id])) def stickers(request, redirect_id, rec_id, mode): if mode == 'add': if not request.session.get('stickers', None): request.session['stickers'] = [] stickers = request.session['stickers'] stickers.append(rec_id) request.session['stickers'] = stickers elif mode == 'delete': stickers = request.session['stickers'] for c, st in enumerate(stickers, start=0): if stickers[c] == rec_id: del stickers[c] request.session['stickers'] = stickers elif mode == 'delete_all': del request.session['stickers'] if request.session.get('currentUrl', None) and redirect_id == 0: return redirect(request.session['currentUrl']) else: if redirect_id > 0: return redirect(reverse('entity_show_url', args=[redirect_id])) else: return redirect(reverse('modules_show_url')) def is_uac_customer(uac): cst = AGROUP.objects.get(caption='CST') c = uac.agr_can_see.all() uac_is_cst = True if c.count() == 1 and cst in c else False return uac_is_cst def is_uac_manager(uac): agr = uac.agr_can_see.all().filter(caption__contains='-M') uac_is_m = True if agr.count() > 0 else False return uac_is_m def get_user_type(uac): # a - админ, m - манагер, s - персонал, c - клиент adm = AGROUP.objects.get(caption='ADM') i_am = '' if adm in uac.agr_can_see.all(): i_am = 'a' elif is_uac_manager(uac): i_am = 'm' elif check_user_is_staff(uac): i_am = 's' else: i_am = 'c' return i_am # ================================= ИНФОРМАЦИЯ ПРО УЕ ==================================================== def get_manufacturer(ci_or_model): if ci_or_model.name.name not in ['модель', 'уе']: return '' if ci_or_model.name.name == 'уе': try: ci_model = get_ci_model(ci_or_model) except: return '' else: ci_model = ci_or_model try: ci_manifacturer = LINK.objects.get(Q(L1__name__name='производитель') & Q(L2=ci_model)).L1 except: return '' return ci_manifacturer def get_ci_model(ci): ci_model = LINK.objects.get(Q(L1__name__name='модель') & Q(L2=ci)).L1 return ci_model # ===================================================================================================== def show_recs(request, rec_type): # request.session['already_checked'] = [[], []] curr_logged_uac = system_user_to_uac(request.user) if curr_logged_uac == 0: return render(request, 'no_access.html') show_search_bar = False check_session_objects(request) search_str = '*' if request.POST.get('search_str', None): request.session['search_str'] = request.POST['search_str'] search_str = request.POST['search_str'] elif request.session.get('search_str', None): search_str = request.session['search_str'] else: search_str = '' request.session['search_str'] = '' # Формирование списка объектов статусов для фильтрации запроса к базе по активным фильтрам active_statuses = [] for s in request.session['statusbar']: if s != 0: status_object = STATUS.objects.get(id=s) active_statuses.append(status_object) # Формирование списка объектов тегов для фильтрации запроса к базе по выбранным тегам # При всех включенных тегах - теги не учитывать active_tags = [] for t in request.session['tag_filter']: if t != 0: tag_object = TAG.objects.get(id=t) active_tags.append(tag_object) cst = AGROUP.objects.get(caption='CST') # a - админ, m - манагер, s - персонал, c - клиент i_am = get_user_type(curr_logged_uac) if i_am == 'c': current_uac_agr = [cst] else: current_uac_agr = curr_logged_uac.agr_can_see.filter(~Q(caption='CST')) # Если на странице поиска изменить статус - какая-то хрень происходит и в rec_type прилетает строка поиска # поэтому так вот обыграем глюк этот src_recs = '' # Это чтоб автопроверка логики пичарма не тупила типа переменной такой нет if rec_type != '*': try: rec_name = ENTITYNAME.objects.get(name=rec_type) active_tags_count = len(active_tags) # Если ни одного тега не выбрано или выбраны все теги (кол-во выбранных соответствует всем существующим), # то фильтровать по тегам не надо if active_tags_count == 0 or TAG.objects.all().count() == active_tags_count: src_recs = REC.objects.filter(Q(name=rec_name) & Q(status__in=active_statuses) & Q(agr_visible_for__in=current_uac_agr)).distinct() else: # Иначе дополнительно фильтруем по тегам. src_recs = REC.objects.filter(Q(tags__in=active_tags) & Q(name=rec_name) & Q(status__in=active_statuses) & Q( agr_visible_for__in=current_uac_agr)).distinct() except: show_search_bar = True if show_search_bar or rec_type == '*': src_recs = search(request, search_str) show_search_bar = True if rec_type == '*': rec_type = search_str tags_avail_set = set() for e in src_recs: for t in e.tags.all(): tags_avail_set.add(t) tags_avail = list(tags_avail_set) # Если это клиент, то они все имеют группу CST, но должны видеть только свои объекты. Их проверяем особым образом. if i_am == 'c': # entities_ids = [] entities = [] ents = set() # Поиск прямых связей юзера вниз uac_direct_childs = get_childs(rec=curr_logged_uac, user_session=request.session) lc = {} uac_childs = set() for e in uac_direct_childs: # для каждого обнаруженного внизу объекта ищем вышестоящие e_parents = get_parents(rec=e, user_session=request.session) for e2 in e_parents: # Если это ['категория', 'модель', 'производитель'] - добавляем без доп.проверок # Для поиска по производителю нужно подсматривать выше чем "модель", поэтому определяем # производителя. get_manufacturer возвращает пустую строку, если предка-производителя нет. if e2.name.name in ['категория', 'модель', 'производитель']: uac_childs.add(e2) mnfctrr = get_manufacturer(e2) if mnfctrr != '': uac_childs.add(mnfctrr) lc = {} for e in src_recs: if e.name.name in ['категория', 'модель', 'производитель'] and e in uac_childs: ents.add(e) elif e.name.name not in ['категория', 'модель', 'производитель'] and check_access(request.session, lc, e, curr_logged_uac)> 0: ents.add(e) src_recs = list(ents) # ФИНАНСЫ В ВЫБОРКЕ И СТАТУСЫ В ВЫБОРКЕ statuses_in_quaery = set() customer_debt = we_paid = 0 for r in src_recs: statuses_in_quaery.add(r.status.id) moneys = FieldDec.objects.filter(Q(owner=r) & Q(name__name='деньги')) s = moneys.aggregate(Sum('v'))['v__sum'] try: customer_debt += s except: pass moneys = FieldDec.objects.filter(Q(owner=r) & Q(name__name='затраты')) s = moneys.aggregate(Sum('v'))['v__sum'] try: we_paid += s except: pass all_statuses = STATUS.objects.all() # Отображение только тех статустов, которые есть в текущей выборке # all_statuses = [] # for st in statuses_in_quaery: # status_object = STATUS.objects.get(id=st) # all_statuses.append(status_object) # ===== НЕ КАТИТ, ТАК КАК АКТИВНЫЕ СТАТУСЫ ВЛИЯЮТ НА ВЫБОРКУ ======= requested_page = request.GET.get('page', 1) paginator = Paginator(src_recs, 7) page = paginator.get_page(requested_page) recs = page.object_list paginated = page.has_other_pages() next_page_url = '?page={}'.format(page.next_page_number()) if page.has_next() else '' prev_page_url = '?page={}'.format(page.previous_page_number()) if page.has_previous() else '' first_page_url = '?page=1' page_range = paginator.page_range # last_page_url = '?page={}'.format(page.count()) # Сборщик энтитей. Соответствие активному фильтру статусов и правам доступа проверяется в entities_show entities = [] for r in recs: # Вызов функции отображения страницы объекта в режиме генерации содержимого, без вывода HTML entity = entities_show(request, r.id, many_items=True) if entity: moneys = [entity['sum_order'], entity['sum_paid'], entity['sum_summary'], entity['sum_buy'], entity['profit']] entities.append([r, entity, moneys]) # rec = [ результат entity_show, [список денег] ] modules = ENTITYNAME.objects.all() request.session['currentUrl'] = reverse('show_recs_url', args=[rec_type]) + '?page={}'.format(requested_page) # Проверка наличия в session списков статусбара, стикеров и закладок, если отсутствуют - создать по умолчанию. check_session_objects(request) # Получение текущего выбранного объекта (который вверху на панели меню висит для создания связей) selected_object = '' if request.session.get('current_object_id', None): selected_object = request.session['current_object_id'] selected_object = get_rec(selected_object) if selected_object == 0: selected_object = '' request.session['current_object_id'] = '' # ========================== # Получение стикеров (закладок, справа у строки поиска) sticker_objects = get_stickers(request) all_tags = TAG.objects.all() if request.session.get('tag_filter',0) == 0: init_tag_filter(request) # Ограничение видимости "Наши затраты" и "Мы заработаем" can_see_all_finances = False if i_am == 'a' or 'AP-M' in current_uac_agr: can_see_all_finances = True context = {'entities': entities, 'show_tabs_sel_links': False, 'modules': modules, 'i_am': i_am, 'all_statuses': all_statuses, 'currentUrl': reverse('show_recs_url', args=[rec_type]), 'status_bar': STATUS.objects.all(), 'rec_type': rec_type.upper(), 'uac': curr_logged_uac, 'selected_object': selected_object, 'stickers': sticker_objects, 'show_search_bar': show_search_bar, 'next_page_url': next_page_url, 'prev_page_url': prev_page_url, 'first_page_url': first_page_url, 'page_range': page_range, 'page': page, 'paginated': paginated, 'all_tags': all_tags, 'tags_avail': tags_avail, 'can_see_all_finances': can_see_all_finances, 'customer_debt': customer_debt, 'we_paid': we_paid } return render(request, 'show_recs.html', context=context) def tag_toggle_entity(request): tag_id = request.GET.get('tag_id',0) rec_id = request.GET.get('rec_id',0) if tag_id != 0 and rec_id != 0: tag_object = TAG.objects.get(id=tag_id) rec_object = REC.objects.get(id=rec_id) creator = system_user_to_uac(request.user) text = '' if tag_object in rec_object.tags.all(): rec_object.tags.remove(tag_object) operation = 'тег-' else: rec_object.tags.add(tag_object) operation = 'тег+' text += tag_object.name add_log_entry(creator=creator, rec=rec_object, operation=operation, agr=rec_object.agr_visible_for.all(), comment=text) return_url = request.session['currentUrl'] # rec.get_absolute_url() return redirect(return_url) def init_tag_filter(request): request.session['tag_filter'] = [] for tag in TAG.objects.all(): request.session['tag_filter'].append(tag.id) def tag_toggle_filter(request): tag_id = request.GET.get('tag_id') if not tag_id in ['all', 'none']: tag_id = int(tag_id) return_str = request.session['currentUrl'] if not request.session.get('tag_filter', None) or tag_id == 'all': init_tag_filter(request) elif tag_id == 'none': request.session['tag_filter'] = [0] else: tags = request.session['tag_filter'] exists = False if tag_id in tags: exists = True for c,s in enumerate(tags, start=0): if s == tag_id: del tags[c] break else: tags.append(tag_id) request.session['tag_filter'] = tags return redirect(return_str) def get_stickers(request): sticker_objects = [] if request.session.get('stickers', None): stickers = request.session['stickers'] for s in stickers: try: id = int(s) sticker_obj = get_rec(id) if sticker_obj != 0: sticker_objects.append(sticker_obj) else: del s except: pass else: return '' return sticker_objects def check_user_is_staff(uac): user_is_staff = False #AGR for gr in uac.agroup.all(): for gr in uac.agr_can_see.all(): if '-M' in gr.caption or '-W' in gr.caption: user_is_staff = True break return user_is_staff def statusbar_set(request, status_id): return_str = request.session['currentUrl'] def init_status_bar(): request.session['statusbar'] = [] sss = ['запас','новое','работа','контроль','ожидает','сбой','в сц','*<-СЦ','*->СЦ'] for s in sss: st = STATUS.objects.get(caption=s) request.session['statusbar'].append(st.id) if not request.session.get('statusbar', None) or status_id == 'all': init_status_bar() elif status_id == 'none': request.session['statusbar'] = [0] else: sts = request.session['statusbar'] exists = False if status_id in sts: exists = True for c,s in enumerate(sts, start=0): if s == status_id: del sts[c] else: sts.append(status_id) request.session['statusbar'] = sts return redirect(return_str) # Сборка категорий, производителей и моделей уешек юзера def collect_user_ues(request): uac = system_user_to_uac(request.user) # Список всех уешек юзера ss = LINK.objects.filter(L2__name__name='уе') ues = LINK.objects.filter(Q(L1=uac) & Q(L2__name__name='уе')) cats = LINK.objects.filter() pass def events(request): curr_logged_uac = system_user_to_uac(request.user) i_am = get_user_type(curr_logged_uac) check_session_objects(request) sticker_objects = get_stickers(request) if request.session.get('current_object_id', None): selected_object = request.session['current_object_id'] try: selected_object = get_rec(selected_object) except: selected_object = '' else: selected_object = '' # ======================= LOG ========================== log_recs = [] current_uac_agr = curr_logged_uac.agr_can_see.all() # Если текущий юзер - не клиент, то показываем весь лог с учётом прав доступа if i_am in ['m','s','a']: #log_recs = LOG.objects.filter(Q(agr_visible_for__in=current_uac_agr) & ~Q(creator=curr_logged_uac)).distinct()[:30] log_recs = LOG.objects.filter(Q(agr_visible_for__in=current_uac_agr)).distinct()[:100] # ... иначе показываем только операции создания объектов и изменения статусов else: # КОГДА БУДУ ДОПИЛИВАТЬ ЛОГ-СТРАНИЦУ - ВЕРНУТЬ ОТОБРАЖЕНИЕ СВОИХ ОПЕРАЦИЙ log_recs = LOG.objects.filter(Q(agr_visible_for__in=current_uac_agr) & Q(operation__caption__in=['создал','статус'])).distinct()[:100] if len(log_recs) == 0: log_recs = '' else: pass # for l in LOG.objects.all(): # if check_access(request.session, lc, l, curr_logged_uac): # log_recs.append(l) context = {'i_am': i_am, 'uac': curr_logged_uac, 'selected_object': selected_object, 'stickers': sticker_objects, 'log': log_recs, } # , 'tasks': tasks} return render(request, 'events_page.html', context=context) def check_session_objects(request): if not request.session.get('already_checked', None): request.session['already_checked'] = [[], []] if not request.session.get('statusbar', None): request.session['statusbar'] = [] sss = ['новое', 'работа', 'контроль', 'ожидает', 'сбой', 'в сц'] for s in sss: st = STATUS.objects.get(caption=s) request.session['statusbar'].append(st.id) if not request.session.get('tag_filter', None): request.session['tag_filter'] = [] def get_ticket_finance(rec): """ Расчёт финансов обращения. Сборка полей dec=деньги, привязанных к заявке. Поле "Деньги" - со знаком+: в счёт заказчику, "-": оплачено заказчиком; "Затраты": наши затраты на заявку, вычитается из Profit :param rec: REC-объект "Обращение" :return: result = { 'sum_order', 'sum_paid', 'sum_buy', 'profit', 'summary' } """ if rec.name.name != 'обращение': return '' fields_money = FieldDec.objects.filter(Q(owner=rec) & Q(name__name='деньги')) fields_buy = FieldDec.objects.filter(Q(owner=rec) & Q(name__name='затраты')) result = {'sum_order': 0, 'sum_paid': 0, 'sum_buy': 0, 'profit': 0, 'summary': 0 } try: for money_f in fields_money: if money_f.v < 0: result['sum_paid'] += money_f.v if money_f.v > 0: result['sum_order'] += money_f.v result['summary'] = result['sum_order'] + result['sum_paid'] except: pass try: for money_f in fields_buy: result['sum_buy'] += money_f.v except: pass result['profit'] = result['sum_order'] - result['sum_buy'] return result def entities_show(request, rec_id, many_items = False, i_am = ''): """ Сборка инфы для отображения объекта REC. Работает в двух режимах - many_items=True/False. :param request: :param rec_id: id записи, инфу о которой нужно собрать/отобразить :param many_items: True - функция вызывается из show_recs - отображение списка (возврат context) и false - формирование отдельной страницы - отображение шаблона entity_show :param i_am: тип пользователя 'a', 'm', 's', 'c' :return: """ rec = get_rec(rec_id) entity_type = rec.name check_session_objects(request) # Получение текущего выбранного объекта (который вверху на панели меню висит для создания связей) if request.session.get('current_object_id', None): selected_object = request.session['current_object_id'] selected_object = get_rec(selected_object) else: selected_object = '' # Получение стикеров (закладок, справа у строки поиска) sticker_objects = get_stickers(request) if not many_items and request.session.get('tag_filter', 0) == 0: init_tag_filter(request) curr_logged_uac = system_user_to_uac(request.user) current_uac_agr = curr_logged_uac.agr_can_see.all() if i_am == '': # Группы доступа текущего uac юзера i_am = get_user_type(curr_logged_uac) lc = {} checked_access = 1 # если это не вызов из show_recs (права там проверяются) и тек.юзер не админ, то проверить права доступа на объект if not many_items and not i_am == 'a': checked_access = check_access(entity=rec, uac=curr_logged_uac, user_session=request.session, lc=lc) if checked_access < 1: return render(request, 'no_access.html') if not many_items else False # Для отображения панели выбора статуса объекта all_statuses = STATUS.objects.all() # ============ ПРОВЕРКА ПРАВ ДОСТУПА ЮЗЕРА НА ЗАПРОШЕННЫЙ ОБЪЕКТ ======================== #access_granted = True if checked_access > 0 else False fields = get_fields_to_dict(rec, curr_logged_uac) # Деньги, считаем для админа и манагера # Если юзер - чувак, которому деньги видеть не надо - оставляем нули в переменных и они будут отброшены шаблоном #sum_order = sum_paid = sum_buy = summary = profit = 0 # ticket_finance = {'sum_order': 0, 'sum_paid': 0, 'sum_buy': 0, 'profit': 0, 'summary': 0} # if i_am in ['a', 'm']: ticket_finance = {'sum_order': 0, 'sum_paid': 0, 'sum_buy': 0, 'profit': 0, 'summary': 0} \ if rec.name.name != 'обращение' else get_ticket_finance(rec) # Обязательное опустошение словаря, почему то сохраняет состояние между сессиями # global links_collector; lc = {} # Вниз собираем всё (рекурсивно до последнего объекта, опускаемся на самое блять дно) # ent_list - если модуль вызывается как элемент списка, то опрашивать рекурсивно все связи не будем. get_linked_objects(user_session=request.session, rec=rec, level='childs', recursive=not(many_items), current_uac=curr_logged_uac, lc=lc) # get_linked_objects(user_session=request.session, rec=rec, level='childs', recursive=True, current_uac=curr_logged_uac, lc=lc) childs = lc.copy() # Собираем связи вверх lc = {} get_linked_objects(user_session=request.session, rec=rec, level='parents', recursive=not(many_items), current_uac=curr_logged_uac, lc=lc) # get_linked_objects(user_session=request.session, rec=rec, level='parents', recursive=True, current_uac=curr_logged_uac, lc=lc) parents = lc.copy() # Получение кода id 'сообщение' - используется в шаблоне для получения ссылки создания нового сообщения msg_type_id = ENTITYNAME.objects.get(name='сообщение').id message_wall = [] message_ids = set() def add_message_to_list(linked_objects): if rec.name.name != 'сообщение': # Добавление сообщений этого объекта и сообщения подчинённых объектов # if linked_objects.get('сообщение', None): # Сборка сообщения в два этапа - на первом получаем сообщение, проверяем его по правам доступа, # добавляем в set, чтобы избежать дубляжей. ff = linked_objects.items() for ent, key in linked_objects.items(): #if ent == 'сообщение': for m in key: # Получение сообщений текущего связанного объекта linked_obj_messages_links = LINK.objects.filter(Q(L1=m) & Q(L2__name__name='сообщение') & Q(name__name__exact='уровень')) curr_object_messages_links = LINK.objects.filter( Q(L1=rec) & Q(L2__name__name='сообщение') & Q(name__name__exact='уровень')) message_links = list(chain(linked_obj_messages_links, curr_object_messages_links)) for msg in message_links: msg_object = msg.L2 show_msg = True # если тек.юзер админ - не тратим время на проверку прав if i_am != 'a': if msg_object.id in request.session['already_checked'][iaccess_restricted]: show_msg = False else: lc_mess = {} if check_access(entity=msg_object, uac=curr_logged_uac, user_session=request.session, lc=lc_mess) < 1: show_msg = False # def text2link(src_str): # i = src_str.index('http') # if i == 0: # return src_str # # ' ', ',', '.' # smbs = [' ','.',',',';'] # fnd = False # for s in smbs: # pos = src_str.index(str=s, start=0) # if pos != 0: # fnd = True # break # if fnd if show_msg: msg_owner_object = msg.L1 prev_set_length = len(message_ids) # предыдущий размер set message_ids.add(msg_object.id) # добавление в set if len(message_ids) > prev_set_length: #str_msg = str(msg_object) message_wall.append([msg_owner_object, msg_object, msg_object.created_at]) add_message_to_list(parents) add_message_to_list(childs) message_wall.sort(key=lambda i: i[2], reverse=True) # Получение списка типа объектов, которые можно создавать для текущего типа сущности entity_create = [] if not rec.status.caption in ['-завершено', '+завершено', 'архив']: entity_create = entity_type.create_child.all() request.session['currentUrl'] = rec.get_absolute_url() all_tags = TAG.objects.all() # Ограничение видимости "Наши затраты" и "Мы заработаем" can_see_all_finances = False if i_am == 'a' or 'AP-M' in current_uac_agr.all(): can_see_all_finances = True context = { 'uac': curr_logged_uac, 'i_am': i_am, 'rec': rec, 'fields': fields, 'selected_object': selected_object, 'stickers': sticker_objects, 'parents': parents, 'childs': childs, 'entity_create': entity_create, 'message_wall': message_wall, 'msg_type_id': msg_type_id, 'show_tabs_sel_links': True, 'user_is_manager': is_uac_manager(curr_logged_uac), 'all_statuses': all_statuses, 'currentUrl': rec.get_absolute_url(), 'status_bar': STATUS.objects.all(), 'sum_paid': ticket_finance['sum_paid'], 'sum_order': ticket_finance['sum_order'], 'sum_summary': ticket_finance['summary'], 'profit': ticket_finance['profit'], 'sum_buy': ticket_finance['sum_buy'], 'all_tags': all_tags, 'can_see_all_finances': can_see_all_finances } # функция вызывается двумя способами: # 1. Как вьюха, тогда отдаём нормальный рендер с контекстом. # 2. Вызывается процедурой показа списка записей, тогда ей контекст нахер не упал, отдаём только данные объекта. #if get_object_info: if many_items: return context else: return render(request, 'entity_show.html', context=context) def add_gr_to_entity(rec_agr, grp_list): """ Добавляет к списку M2M AGROUP группу доступа. REC НЕ СОХРАНЯЕТ! :param rec_agr: :param grp_list: :return: """ for g in grp_list: g_obj = AGROUP.objects.get(caption=g) rec_agr.add(g_obj) # Это первый вариант, модернизированный вариант функции - сверху. От этого надо избавиться. Пока лень. def add_vf_groups(rec, grp_list): """Не использовать. Заменено на add_gr_to_entity """ for g in grp_list: g_obj = AGROUP.objects.get(caption=g) rec.agr_visible_for.add(g_obj) rec.save() class entity_create(View): def get(self, request, entity_name_id, src_id=0): """Создание экземпляра сущности :param request: хз, не придумал еще :param entity_name: имя сущности создаваемого экземпляра :param src_id: к чему прикреплять (хозяин) или исходный объект. :return: редирект на созданный объект """ owner = 0 # Если юзер - клиент, то ему можно создавать обращения не из под уе или себя, так как доступ к себе # он не имеет, то при создании обращения или уе - он не сможет делать это со страницы юзера, # а сделает это из верхней панели, значит нужно сделать владельцем создаваемого объекта этого чувака. # Но при этом он может создать обращение из под уе, поэтому если процедура получила родительский объект, # значит это и произошло и src_id менять не будем current_logged_user = system_user_to_uac(request.user) if src_id==0 and not check_user_is_staff(current_logged_user): owner = current_logged_user elif src_id != 0: owner = get_rec(src_id) entity_name = ENTITYNAME.objects.get(id=entity_name_id) if entity_name.name == 'уе': return redirect(reverse('ci_prttp_add_url', args=[src_id, 0, 0, 0])) if entity_name.name == 'категория': pass stickers = get_stickers(request) rec_id = current_logged_user.id prototype_fields = get_prototype_fields(entity_name) request.session['currentUrl'] = '' #rec.get_absolute_url() context = {'fields': prototype_fields, 'owner': owner, 'entity_name': entity_name, 'stickers': stickers } return render(request, 'entity_create_form.html', context=context) # def post(self, request, entity_name, src_id=0): def post(self, request): # Тип создаваемого объекта rec_name_id = int(request.POST['entity_name_id']) rec_name = ENTITYNAME.objects.get(id=rec_name_id) rec_name = get_rec(rec_name) creator = system_user_to_uac(request.user) # если создаётся пользователь и if rec_name.name == 'пользователь': login_name = request.POST['chf_телефон'] uac = get_django_user_by_login_name(login_name, create_user=False) if uac: return redirect(reverse('entity_show_url', args=[uac.id])) owner = request.POST['src_id'] if owner != '': owner = get_rec(int(owner)) else: owner = '' # Получение "своего" последнего ID типа сущности (чтоб счётчики были индивидуальными для сущности каждого типа) new_rec_id = rec_name.internal_num # Создание и сохранение нового объекта new_rec = REC(name=rec_name, creator=creator, self_id=new_rec_id) new_status = rec_name.statuses.all().first() new_rec.status = new_status new_rec.status_color = get_status_color(new_status.caption) new_rec.save() add_vf_groups(new_rec, ['AP-M']) # Увеличение "своего" последнего ID типа сущности (чтоб счётчики были индивидуальными для сущности каждого типа) rec_name.internal_num = new_rec_id + 1 rec_name.save() # Добавление полей, полученных из формы создания экземпляра сущности for f in request.POST.keys(): value = request.POST[f] if value != '': field_name = f[4:] if 'chf' in f: obj = FieldChf elif 'dec' in f: obj = FieldDec elif 'txt' in f: obj = FieldTXT elif 'dat' in f: obj = FieldDate else: obj = '' if obj: name = FIELDNAME.objects.get(name=field_name) new_field = obj(owner=new_rec, name=name, v=value) new_field.save() # ========= ПРИВЯЗКА К РОДИТЕЛЮ ========== if owner != '': create_link(link_type='уровень', parent_id=owner.id, child_id=new_rec.id) # Копирование групп доступа новому объекту от родительского # *** нах. иначе сразу после создания объект могут успеть увидеть те, кому не следует. # по умолчанию созданный объект видит только админ и создатель объекта # for gr in owner.agr_visible_for.all(): # if gr.caption != 'CST':#new_rec.name.name != 'сообщение': # new_rec.agr_visible_for.add(gr) # Добавление группы CST если это УЕ if rec_name == 'уе' or not check_user_is_staff(owner): if new_rec.name.name != 'сообщение': gr = AGROUP.objects.get(caption='CST') new_rec.agr_visible_for.add(gr) # ========== СПЕЦ.ОБРАБОТКА СУЩНОСТЕЙ, СОЗДАНИЕ КОТОРЫХ ТРЕБУЕТ СПЕЦ.ОБРАБОТКИ ============= # Создание django-user с логином и генерацией пароля if rec_name.name == 'пользователь': login_name = request.POST['chf_телефон'] # Создание dj_user. Строку не удалять, хоть и кажется ненужной. new_user = get_django_user_by_login_name(login_name, create_user=True, uac=new_rec) add_gr_to_entity(new_rec.agr_visible_for, ['CST']) add_gr_to_entity(new_rec.agr_can_see, ['CST']) new_rec.save() # ========================================================================================== if rec_name.name == 'сообщение': request.session['currentUrl'] = owner.get_absolute_url() return redirect(reverse('entity_show_url', args=[owner.id])) if rec_name.name == 'обращение': add_gr_to_entity(new_rec.agr_visible_for, ['CST','AP-W']) new_rec.save() add_log_entry(creator=creator, rec=new_rec, agr=new_rec.agr_visible_for.all(), operation='создал') request.session['currentUrl'] = new_rec.get_absolute_url() return redirect(reverse('entity_show_url', args=[new_rec.id])) def add_log_entry(creator, rec, operation, agr, comment='#'): """ Создание экземпляра объекта журнала событий системы. :param creator: пользователь, совершивший операцию. :param rec:экземпляр сущности, по отношению к которой совершена операция. :param operation: операция - ['тег+', 'тег-', 'статус', 'создал', 'удалил', 'доступ] - из таблицы LOG_OPERATION :param comment: комментарий :return: """ # log_rec_name = ENTITYNAME.objects.get(name='лента') # new_log_object = REC(name=log_rec_name, creator=creator, self_id=1) # new_log_object.save() # field_obj = FieldChf # field_name = FIELDNAME.objects.get(name='лог') #value = creator.__str__() + text + str(rec.name) # Создание поля - привязано к записи-родителю, чтоб можно было и отдельно лог просматривать, # и на странице сущности лог просматривать # text = creator.__str__() + text # new_field = field_obj(owner=rec, name=field_name, v=text) # new_field.save() # # for agr in rec.agr_visible_for.all(): # new_field.agroup.add(agr) # new_field.save() oper_object = LOG_OPERATION.objects.filter(caption=operation).first() if rec == '': rec = None new_entry = LOG(objct=rec, creator=creator, operation=oper_object, comment=comment) new_entry.save() for a in agr: new_entry.agr_visible_for.add(a) return True def get_django_user_by_login_name(login, create_user=False, uac=0): try: # Получение поля Логин чтоб дёрнуть оттуда линк на юзера dj_user = 0 dj_user = User.objects.get(username__exact=login) uac_user_link_field = FieldDec.objects.get(v=dj_user.id) uac = uac_user_link_field.owner return uac except: # поля с таким логином еще нет, значит REC Пользователь тоже должен отсутствовать # создание django-user such_user_exists = False # Если uac не существует и его нужно создать if not such_user_exists and create_user: # Если django_user не существует if dj_user == 0: newpsw = 'user' + login[-4:] dj_user = User.objects.create_user(username=login, password=newpsw) field_name = FIELDNAME.objects.get(name__exact='логин') new_uac_login_field = FieldChf.objects.new_field(uac, field_name, login) # Создание поля-связки REC_user и Django_user field_name = FIELDNAME.objects.get(name__exact='dj_user') dju_link = FieldDec.objects.new_field(uac, field_name, dj_user.id) return such_user_exists def get_dj_user_by_uac_id(uac_id): fieldname = FIELDNAME.objects.get(name__exact='dj_user') try: uac = REC.objects.get(id__exact=uac_id) except: return 0 try: field = FieldDec.objects.get(name__name__exact=fieldname, owner=uac) except: return 0 return field.v def create_link(link_type, parent_id, child_id): """ Создание связи :param link_type: STR: имя связи :param parent_id: id хозяина :param child_id: id раба :return: нихуя """ if parent_id != child_id: link_obj = LINKNAME.objects.get(name=link_type) child_obj = REC.objects.get(id=child_id) parent_obj = REC.objects.get(id=parent_id) new_link = LINK(name=link_obj, L1=parent_obj, L2=child_obj) new_link.save() def delete_link(request, L1, L2): # L1 - подаётся изначально из show_cards как rec (исходный объект, от которого нужно оторвать связанный). # По L1 можно проверять - остаются ли у объекта предки после удаления. Если предков не останется - объект # "потеряется" # Это объект, который отрывается от кого-то L2_entity = get_rec(L2) L1_entity = get_rec(L1) # Проверка на наличие более чем одной родительской связи try: lnk = LINK.objects.filter(Q(L1=L1) & Q(L2=L2)) lnk.delete() except: pass try: lnk = LINK.objects.get(L2, L1) lnk.delete() except: pass return_str = L1_entity.get_absolute_url() return redirect(return_str) def get_field_object_by_str(type): """Получение типа (таблицы) поля по псевдониму (сам придумал, произвольные абревиатуры). Внутреннее использование, по URL не вызывается. :param type: строковый chf/dec/txt :return: объект (таблица), в котором(й) лежит нужное поле """ if type == 'chf': obj = FieldChf elif type == 'dec': obj = FieldDec elif type == 'txt': obj = FieldTXT elif type == 'dat': obj = FieldDate return obj # path('fields/del/<int:owner_id>/<str:field_type>/<str:field_value>/', name='field_delete_keys_url'), # def delete_field_by_keys(request, owner_id, field_type, field_value): def delete_field_by_keys(request, owner_id, field_type, field_id): #value): owner = get_rec(owner_id) obj = get_field_object_by_str(field_type) field_to_delete = obj.objects.get(id=field_id) # filter(Q(owner=owner) & Q(v=field_value)) field_to_delete.delete() return_str = owner.get_absolute_url() return redirect(return_str) def delete_field_by_field_id(request, type, item_id): """Удаление поля :param request: не используется, но вроде как должен просто присутствовать ) :param type: (STR) какого типа поле удаляется (для получения нужной таблицы) :param item_id: (INT) id'ишник поля в таблице :return: редирект бэк ту вызывающий объект. Узнаём его из папы удалённого поля. """ obj = get_field_object_by_str(type) item = obj.objects.get(id=item_id) return_str = item.owner.get_absolute_url() item.delete() return redirect(return_str) class add_field(View): # def add_field(request): def post(self, request): """ Добавление поля к объекту REC :param request: rec_id: к какому объекту добавлять поле; fieldname - имя добавляемого поля; value - значение добавляемого поля; type - тип добавляемого поля. :return: """ rec_id = int(request.POST['rec_id']) field_name = request.POST['fieldname'] field_type = request.POST['type'] value = request.POST['value'] obj = REC.objects.get(id=rec_id) if value != '': fields = [(field_name, value)] field_obj = get_field_object_by_str(field_type) field_obj.objects.add_fields_by_list(rec_id, fields) return_url = obj.get_absolute_url() return redirect(return_url) # @staticmethod # def add_field(rec_id, field_name, field_type, value): # fields = [(field_name, value)] # field_obj = get_field_object_by_str(field_type) # field_obj.objects.add_fields_by_list(rec_id, fields) def get_status_color(status): if status == 'новое': status_color = 'danger' elif status == 'работа': status_color = 'primary' elif status in ['контроль', '*<-СЦ','*->СЦ']: status_color = 'danger' elif status == 'ожидает': status_color = 'primary' elif status == '+завершено': status_color = 'success' elif status == '-завершено': status_color = 'warning' elif status == 'архив': status_color = 'light' elif status in ['запас', 'ДНР']: status_color = 'secondary' elif status == 'сбой': status_color = 'danger' elif status == 'в сц': status_color = 'warning' return status_color def status_set(request, rec_id, status): rec = get_rec(rec_id) status_obj = STATUS.objects.get(caption=status) rec.status = status_obj status_color = 'light' s = get_status_color(status) if s != '': status_color = s rec.status_color = status_color rec.save() return_url = rec.get_absolute_url() uac = system_user_to_uac(request.user) add_log_entry(creator=uac, rec=rec, operation='статус', agr=rec.agr_visible_for.all(), comment=str(status_obj)) return redirect(return_url) def toggle_agr_url(request, rec_id, agr_id, gr_type): """Переключение группы доступа объекта. :param rec_id: объект, у которого тоглится группа доступа :param agr_id: группа доступа, которую нужно тоглить :param gr_type: #AGR (после изменения структуры) тип списка групп - can_see=0 и visible_for=1 :return: редирект на страницу, указанную вызывающим модулем """ rec = REC.objects.get(id__exact=rec_id) toggling_access_group = AGROUP.objects.get(id__exact=agr_id) admin_group = AGROUP.objects.get(caption='ADM') customer_group = AGROUP.objects.get(caption='CST') public_group = AGROUP.objects.get(caption='PBL') uac = system_user_to_uac(request.user) agroup = rec.agr_visible_for if gr_type==1 else rec.agr_can_see # Если переключаемая группа назначена на объект и назначение надо снять if toggling_access_group in agroup.all(): # ================= ОТВЯЗКА ГРУППЫ ОТ ОБЪЕКТА ====================== # Если хотят отвязать от публичной группы, то если других групп нет, то объект не увидит никто кроме админа # Если привязана админская группа, то чтоб объект не потерялся, для пользователя нужна еще какая-нибудь must_have_groups = 2 if admin_group in agroup.all() else 2 # Если остаётся 1 какая-нибудь группа или 2 с админской, то можно снимать назначение группы доступа # Если количество привязанных групп удовлетворяет минимальным требованиям - удаляем группу # Если нужно удалить CST группу - удаляем без базара if agroup.all().count() >= must_have_groups or toggling_access_group.caption == 'CST': add_log_entry(creator=uac, rec=rec, operation='доступ-', agr=rec.agr_visible_for.all(), comment=str(toggling_access_group)) agroup.remove(toggling_access_group) # ================= ПРИВЯЗКА ГРУППЫ К ОБЪЕКТУ ====================== else: # Если хотят сдеать объект публичным, то все остальные группы надо снять agroup.add(toggling_access_group) # Если назначается какая-то группа отличная от Public - группу Public отключаем if toggling_access_group != public_group: agroup.remove(public_group) add_log_entry(creator=uac, rec=rec, operation='доступ+', agr=rec.agr_visible_for.all(), comment=str(toggling_access_group)) #TODO: удалить все связи ДОСТУП с участием rec_id # В результате изменения групп доступа объекта могут стать неактуальными кэширующие доступ вспомогательные # объекты. Их надо удалить к ебеням. # # Удаление кэширующих ссылок access_links_to_delete = LINK.objects.filter(Q(name__name='доступ') & (Q(L1=rec) | Q(L2=rec))) for l in access_links_to_delete: l.delete() # Удаление из списка проверенных элементов сессии юзера # (тут 0 и 1 - разрешены к просмотру/запрещены) for tp in range(0, 1): try: i = request.session['already_checked'][tp].index(rec_id) del i except: pass return_url = request.session['currentUrl'] return redirect(return_url) def delete_rec_obj(request, rec_id_to_delete): """Удаляет объект REC. После чего редиректит на отображение указанного объекта :param rec_id_to_delete: удаляемый объект :param return_url: куда идти (чего показывать) после удаления объекта. Указывается в вызывающем модуле. :return: редирект на страницу указанного объекта REC """ uac = system_user_to_uac(request.user) links = LINK.objects.filter(Q(L1__id=rec_id_to_delete) | Q(L2__id=rec_id_to_delete)) for l in links: l.delete() rec_to_delete = REC.objects.get(id=rec_id_to_delete) add_log_entry(creator=uac, rec='', operation='удалил', agr=rec_to_delete.agr_visible_for.all(), comment=str(rec_to_delete)[:99]) rec_to_delete.delete() return_url = reverse('main_page_url') return redirect(return_url) def get_rec(rec): """Вернуть объект CI независимо от того прилетело уже объектом или его id :param ci: объект CI или его id :return: Всегда объект REC """ rslt = rec if type(rec) is int: try: rslt = REC.objects.get(id=rec) except: rslt = 0 return rslt def get_entity_fields_to_str(rec): """Сборка всех полей всех типов в строку :param rec: Объект, поля которого нужно собрать. :return: строка со значениями полей """ rec = get_rec(rec) types = [FieldChf.objects.filter(owner=rec), FieldDec.objects.filter(owner=rec), FieldTXT.objects.filter(owner=rec) ] fields_str = '' for t in types: for f in t: if fields_str != '': fields_str += '; ' fields_str += str(f.v) return fields_str def get_fields_to_dict(rec, current_logged_uac): """Сбор полей объекта. [n][ 'name' field1...fieldn ] Пока отображаем CharField, Decimal и Text :param rec: :return: """ rec = get_rec(rec) agr = current_logged_uac.agr_can_see.all() chf = {}; dec = {}; txt = {}; dat = {} # Получение всех полей, добавленных к текущему объекту all_chf_fields = FieldChf.objects.filter(Q(owner=rec) & Q(name__agroup__in=agr)).distinct() all_dec_fields = FieldDec.objects.filter(Q(owner=rec) & Q(name__agroup__in=agr)).distinct() all_txt_fields = FieldTXT.objects.filter(Q(owner=rec) & Q(name__agroup__in=agr)).distinct() all_dat_fields = FieldDate.objects.filter(Q(owner=rec) & Q(name__agroup__in=agr)).distinct() all_bool_fields = FieldBool.objects.filter(Q(owner=rec) & Q(name__agroup__in=agr)).distinct() def append_fields(src_fields, collector, prototype_fields): # Просмотр всех имеющихся полей у объекта после фильтра групп доступа for f in src_fields: # Если поля еще нет в итоговом списке - создадим для поля пустой список # (полей одного имени у объекта может быть много - деньги, ....) if not f.name.name in collector: collector[f.name.name] = [] collector[f.name.name].append(f) # Если текущий юзер - манагер, покажем ему список полей, к которым можно добавить значений. # Клиент пусть видит только поля, которые заполнены манагером. Весь список ему ни к чему. if is_uac_manager(current_logged_uac): # Фильтрация списка полей у прототипа по группам доступа текущего юзера obj_fields = prototype_fields.all().filter(Q(agroup__in=agr)).distinct() # Тут нужно проинициализировать поле, если там еще ничего нет. for field in obj_fields: if not field.name in collector: collector[field.name] = [] # ========== Подготовка списка для вывода юзеру в списке или на форму ======================= # |поля в наличии|итог| список полей прототипа| append_fields(all_chf_fields, chf, rec.name.fields_chf) append_fields(all_dec_fields, dec, rec.name.fields_dec) append_fields(all_txt_fields, txt, rec.name.fields_txt) append_fields(all_dat_fields, dat, rec.name.fields_date) fields = {'chf': chf, 'dec': dec, 'txt': txt, 'dat': dat} return fields def convert_L_to_recs(links, key, user_session): #, current_uac=0): """Конвертация LINKS.L в REC :param links: связи {'L': n} :param key: L1 или L2 - какие данные вытскивать, указываются процедурами get_parent и get_child :return: список REC """ recs = [] for p in links: obj = get_rec(p[key]) if not obj.id in user_session['already_checked'][iaccess_restricted]: #if True: #check_access(obj, uac=current_uac) != 0: recs.append(obj) return recs def get_childs(rec, user_session):# , current_uac=0): childs_links = LINK.objects.filter(Q(L1=get_rec(rec)) & Q(name__name__exact='уровень')).values('L2') childs = convert_L_to_recs(childs_links, 'L2', user_session) #, current_uac=current_uac) return childs def get_parents(rec, user_session): #, current_uac=0): if rec: parents_links = LINK.objects.filter(Q(L2=get_rec(rec)) & Q(name__name__exact='уровень')).values('L1') parents = convert_L_to_recs(parents_links, 'L1', user_session) #, current_uac=current_uac) return parents else: return def add_entity_to_collector(lc, rec): #global links_collector rec_entity_name = rec.name.name lc_keys = lc.keys() if not rec_entity_name in lc.keys(): lc[rec.name.name] = [] if rec not in lc[rec.name.name]: lc[rec.name.name].append(rec) # НАХУЙ ОНО ТУТ НЕ УПУКАЛОСЬ! Переделаю всё щаз def get_linked_objects(user_session, rec, level, lc, get_fields=False, recursive=False, current_uac=0): """Формирует список связанных пап или дочек с указанной REC с учётом прав доступа :param rec: rec.id или rec, один хуй преобразовывается всё в rec; :param level: 'childs' или 'parents' :param: get_fields - Собирать или нет поля объекта :param: recursive - рекурсивный поиск как вниз, так и вверх :param: current_uac - UAC текущего юзера. :return: список пап или дочек с полямиs """ #recursive = False # Если статус проверяемого объекта не выбран фильтром - блокировать отображение if not user_session.get('statusbar', None): user_session['statusbar']=[] if level == 'parents': linked_objects = get_parents(rec, user_session) #, current_uac) else: linked_objects = get_childs(rec, user_session) #, current_uac) if linked_objects is not None and len(linked_objects)>0: for current_rec in linked_objects: if current_rec not in user_session['already_checked'][iaccess_restricted] :# and current_rec not in user_session['already_checked'][1]: if current_rec not in user_session['already_checked'][iaccess_granted]: checked_access = check_access(user_session=user_session, entity=current_rec, uac=current_uac, lc=lc) if current_rec.id in user_session['already_checked'][iaccess_granted] or checked_access > 0: if current_rec.status.id in user_session['statusbar']: add_entity_to_collector(lc=lc, rec=current_rec) # Если список связанных в пределах одного уровня объектов не пуст и указано рекурсивно добавить все # связанные объекты на других уровнях # if recursive and current_rec.status.id in user_session['statusbar'] and current_rec.name.name != 'сообщение': # Убрал проверку на фильтр при просмотре межуровневых связей, иначе не отображаются активные объекты, # находящиеся за завершенными объектами. Посмотреть как оно будет работать. if recursive and current_rec.name.name != 'сообщение': get_linked_objects(user_session=user_session, rec=current_rec, level=level, lc=lc, get_fields=get_fields, current_uac=current_uac, recursive=recursive) def search(request, search_str=''): check_session_objects(request) # Формирование списка объектов статусов для фильтрации запроса к базе по активным фильтрам active_statuses = [] for s in request.session['statusbar']: if s != 0: status_object = STATUS.objects.get(id=s) active_statuses.append(status_object) recs_and_fields = set() lc = {} # a.split(',') curr_logged_uac = system_user_to_uac(request.user) current_uac_agr = curr_logged_uac.agr_can_see.all() i_am = get_user_type(curr_logged_uac) if search_str == '': try: search_str = request.POST['search_str'] except: search_str = '' if search_str == '': try: search_str = request.session['search_str'] except: pass entity_filter = False # поиск по двум параметрам. первый - строка поиска, второй - имя сущности, по которой ограничить поиск # !!! ПОИСК ПО НЕСКОЛЬКИМ ПАРАМЕТРАМ ПОКА ИСПОРЧЕН - сделал поиск по заявкам по №ххх или #xxx # Если в начале поисковой строки стоит № или # - значит это номер обращения (позже - еще и счёта) # Убираем спец-символ из строки поиска, находим объект ОБРАЩЕНИЕ (по нему будем фильтрить ниже) filter_ent_name = '' if '#' in search_str: if search_str.index('#') == 0: entity_filter = True elif '№' in search_str: if search_str.index('№') == 0: entity_filter = True if entity_filter: search_str = search_str[1:] filter_ent_name = ENTITYNAME.objects.get(name='обращение') entity_filter = True if search_str != '': # Просмотр совпадений в заголовках экземпляров сущностей и в поле self_id if not entity_filter: RECs = REC.objects.filter(Q(status__in=active_statuses) & Q(agr_visible_for__in=current_uac_agr) & Q(caption__icontains=search_str) | Q(self_id__icontains=search_str)) else: qq = REC.objects.filter(name=filter_ent_name) qq = qq.distinct() RECs = qq.filter(Q(status__in=active_statuses) & Q(agr_visible_for__in=current_uac_agr) & (Q(caption__icontains=search_str) | Q(self_id__icontains=search_str))) for r in RECs: # Если права доступа текущего залогиненного юзера катят, добавляем объект в список выдачи if i_am != 'a': if check_access(user_session=request.session, entity=r, uac=curr_logged_uac, lc=lc) > 0: recs_and_fields.add(r) else: recs_and_fields.add(r) # Полнотекстовый поиск по всем полям fields_chf = FieldChf.objects.filter(Q(v__icontains=search_str) | Q(caption__icontains=search_str)) fields_txt = FieldTXT.objects.filter(Q(v__icontains=search_str) | Q(caption__icontains=search_str)) # Просмотр всех обнаруженных полей, по папе поля проверяем права, если ок, то добавляем папу с полем в выдачу def add_to_result(obj): for f in obj: field_owner = f.owner msg_owner_status_visible = True if field_owner.name.name == 'сообщение': try: message_owner = LINK.objects.get(~Q(L1__name__name='ТЕТРАДКА') & Q(L2=field_owner) & Q(name__name='уровень') & Q(L2__name__name='сообщение')).L1 except: message_owner = LINK.objects.filter(~Q(L1__name__name='ТЕТРАДКА') & Q(L2=field_owner) & Q(name__name='уровень') & Q(L2__name__name='сообщение')) if not message_owner.status.id in request.session['statusbar']: msg_owner_status_visible = False if field_owner.status.id in request.session['statusbar'] and msg_owner_status_visible: # Проверка на совпадение entityname объекта выбранному выше (типа фильтр по типу сущности слелать хотел show_entity = True if entity_filter and field_owner.name != filter_ent_name: show_entity = False if show_entity and field_owner.status in active_statuses: if check_access(user_session=request.session, entity=field_owner, uac=curr_logged_uac, lc=lc) > 0: recs_and_fields.add(field_owner) add_to_result(fields_chf) add_to_result(fields_txt) request.session['search_str'] = search_str result = list(recs_and_fields) return result # recs_and_fields def get_entity_fields_by_name_and_type(entity, field_name, field_type): fields_table = get_field_object_by_str(field_type) fields = fields_table.objects.filter(Q(owner=entity) & Q(name__name=field_name)) return fields if fields.count() > 0 else '' def gen_print(request, rec_id, doc_type): rec_obj = get_rec(rec_id) curr_logged_system_user = request.user curr_logged_uac = system_user_to_uac(request.user) customer = '' ci = '' # parents = get_linked_objects(rec_obj, level='parent', get_fields=False, recursive=True) def gt_parent(rec2, str): parents = get_parents(rec=rec2, user_session=request.session) if parents: for p in parents: if p.name.name == str: return p return '' # if doc_type == 'приёмка': customer = gt_parent(rec_obj, 'пользователь') ci = gt_parent(rec_obj, 'уе') # Если юзер не найден, значит обращение создано через УЕ if customer == '': customer = gt_parent(ci, 'пользователь') name = get_entity_fields_by_name_and_type(customer, 'имя', 'chf') phone = get_entity_fields_by_name_and_type(customer, 'телефон', 'chf') # УЕ sn = ''; imei = ''; ci_caption = '' if ci != '': sn = ''; imei = ''; ci_caption = '' sn = get_entity_fields_by_name_and_type(ci, 'серийный номер', 'chf') imei = get_entity_fields_by_name_and_type(ci, 'imei', 'chf') ci_caption = str(ci) # ОБРАЩЕНИЕ customer_says = ''; gived = ''; status = '' customer_says = get_entity_fields_by_name_and_type(rec_obj, 'со слов клиента', 'txt') gived = get_entity_fields_by_name_and_type(rec_obj, 'комплект', 'chf') looks = get_entity_fields_by_name_and_type(rec_obj, 'состояние', 'txt') name_print = '' for i in name: name_print += i.v + ' ' phone_print = '' for i in phone: phone_print += i.v + ' ' sn_print = '' for i in sn: sn_print += i.v + ' ' for i in imei: sn_print += i.v + ' ' customer_says_print = '' for i in customer_says: customer_says_print += i.v + ' ' gived_print = '' for i in gived: gived_print += i.v + ' ' looks_print = '' for i in looks: looks_print += i.v + ' ' ci_print = ci_caption login = phone_print psw = 'user'+ login[-5:] context = {'num': rec_obj.self_id, 'created': rec_obj.created_at, 'name': name_print, 'phone': phone_print, 'ci': ci_print, 'sn': sn_print, 'customer_says': customer_says_print, 'gived': gived_print, 'looks': looks_print, 'login': login, 'psw': psw, } if doc_type == 'приёмка': context = {'num': rec_obj.self_id, 'created': rec_obj.created_at, 'name': name_print, 'phone': phone_print, 'ci': ci_print, 'sn': sn_print, 'customer_says': customer_says_print, 'gived': gived_print, 'looks': looks_print, 'login': login, 'psw': psw, } return render(request, 'sd/printforms/take.html', context=context) # ===================== ВЫДАЧА ============================ if doc_type == 'выдача': result = get_entity_fields_by_name_and_type(rec_obj, 'результат', 'txt') result_print = '' for i in result: result_print += str(i.v) + ' ' waranty = get_entity_fields_by_name_and_type(rec_obj, 'гарантия', 'dec') waranty_print = '' for i in waranty: waranty_print += str(i.v) + ' ' moneys = get_ticket_finance(rec_obj) context = {'num': rec_obj.self_id, 'created': rec_obj.created_at, 'name': name_print, 'phone': phone_print, 'ci': ci_print, 'sn': sn_print, 'customer_says': customer_says_print, 'gived': gived_print, 'looks': looks_print, 'waranty': waranty_print, 'result': result_print, 'date': datetime.datetime.now(), 'login': login, 'psw': psw, 'moneys': moneys, } return render(request, 'sd/printforms/return.html', context=context) if doc_type == 'приёмденег': pass # if doc_type == 'приёмка': # pass # # if doc_type == 'приёмка': # pass return render(request, 'sd/printforms/take.html', context=context) def main_page(request): curr_logged_uac = system_user_to_uac(request.user) try: agr = curr_logged_uac.agr_can_see except: return redirect("/accounts/login") i_am = get_user_type(curr_logged_uac) check_session_objects(request) if curr_logged_uac == 0: return render(request, 'no_access.html') user_is_staff = check_user_is_staff(curr_logged_uac) # Задача "Тетрадка" notes_task = REC.objects.get(Q(name__name__exact='задача') & Q(caption__exact='Тетрадка')) filtered_notes = [] for n in filtered_notes: lc = {} a = check_access(user_session=request.session, lc=lc, entity=n, uac=curr_logged_uac) if a > 0: filtered_notes.append(n) # Ссылки Тетрадка<-Сообщение notes_links = LINK.objects.filter(Q(L1=notes_task) & Q(L2__name__name__exact='сообщение')) all_notes = [] for n in notes_links: msg = n.L2 lc = {} a = check_access(user_session=request.session, lc=lc, entity=msg, uac=curr_logged_uac) if a > 0: all_notes.append(msg) request.session['currentUrl'] = reverse('main_page_url') sticker_objects = get_stickers(request) if request.session.get('current_object_id', None): selected_object = request.session['current_object_id'] try: selected_object = get_rec(selected_object) except: selected_object = '' else: selected_object = '' # ======================= LOG ========================== log_recs = [] current_uac_agr = curr_logged_uac.agr_can_see.all() # Если текущий юзер - не клиент, то показываем весь лог с учётом прав доступа if i_am in ['m','s','a']: log_recs = LOG.objects.filter(agr_visible_for__in=current_uac_agr).distinct() # ... иначе показываем только операции создания объектов и изменения статусов else: log_recs = LOG.objects.filter(Q(agr_visible_for__in=current_uac_agr) & Q(operation__caption__in=['создал','статус'])).distinct() if len(log_recs) == 0: log_recs = '' # for l in LOG.objects.all(): # if check_access(request.session, lc, l, curr_logged_uac): # log_recs.append(l) context = {'notes': all_notes, 'user_is_staff': user_is_staff, 'selected_object': selected_object, 'stickers': sticker_objects, 'log': log_recs, } # , 'tasks': tasks} return render(request, 'main_page.html', context=context) def create_note(request): msg = request.POST['content'] r = request.session['currentUrl'] #s = REC.objects.get(name__name='задача') # ss = REC.objects.get(caption='ТЕТРАДКА') note_task = REC.objects.get(Q(name__name='задача') & Q(caption='Тетрадка')) mess_obj = ENTITYNAME.objects.get(name='сообщение') new_mess = REC(name=mess_obj, creator=system_user_to_uac(request.user), self_id=0) new_mess.save() name = FIELDNAME.objects.get(name='содержание') mess_text = FieldTXT(owner=new_mess, v=msg, name=name) mess_text.save() lnk_name = LINKNAME.objects.get(name='уровень') lnk = LINK(L1=note_task, L2=new_mess, name=lnk_name) lnk.save() r = request.session['currentUrl'] add_log_entry(creator=system_user_to_uac(request.user), rec=note_task, agr=note_task.agr_visible_for.all(), operation='создал') return redirect(request.session['currentUrl'])