Browse Source

Merge branch 'fix-from-metasynced' of blezz-tech/sharix-open-backend into send_msg

9 changed files with 125 additions and 39 deletions
  1. 5 1
      admin/documents.py
  2. 44 22
      models/company.py
  3. 51 6
      models/documents.py
  4. 15 3
      models/orders.py
  5. 1 1
      models/permissions.py
  6. 3 1
      models/provider.py
  7. 1 1
      models/resource.py
  8. 2 2
      serializer/orders.py
  9. 3 2
      urls.py

+ 5 - 1
admin/documents.py

@@ -2,7 +2,11 @@ from django.contrib import admin
 from dbsynce.models import Documents, DocumentFile
 
 
-
+class DocumentFileInline(admin.TabularInline):
+    model = DocumentFile
+    extra = 1
+    fields = ('file',)
+    can_delete = True
 
 
 @admin.register(Documents)

+ 44 - 22
models/company.py

@@ -1,30 +1,37 @@
 from django.db import models
-from tickets.models import Ticket
+from django.urls import reverse
 from django.contrib.auth import get_user_model
+from django.contrib.contenttypes.models import ContentType
+
+from tickets.models import Ticket
+
 
 class Company(models.Model):
     legal_name = models.CharField(
+        "Название организации",
         max_length=150,
-        help_text="настоящее имя юридического лица"
+        help_text="Настоящее имя юридического лица"
     )
 
-
+    # FIXME: Нет уверенности, что после удаления пользователя следует оставлять `models.DO_NOTHING`
     repr_id = models.ForeignKey(
         get_user_model(),
         on_delete=models.DO_NOTHING,
-        help_text="уникальный идентификатор представителя компании. Это обязательно пользователь-провайдер определенного типа. То есть нельзя назначить ответственного, который не может быть ответственным."
+        help_text="Уникальный идентификатор представителя компании. Это обязательно пользователь-провайдер определенного типа. То есть нельзя назначить ответственного, который не может быть ответственным."
     )
 
     requirements = models.CharField(
+        "Ограничения",
         max_length=150,
-        help_text="код необходимого (самый строгий) для того, чтобы ресурс мог стать активным. Оно вставляется автоматом, в соответствии с профилем метасервиса. Далее, если кому-то из партнеров или пользователей надо строже - применяется более строгий вариант на данную связь."
+        help_text="Код необходимого (самый строгий) для того, чтобы ресурс мог стать активным. Оно вставляется автоматом, в соответствии с профилем метасервиса. Далее, если кому-то из партнеров или пользователей надо строже - применяется более строгий вариант на данную связь."
     )
     status = models.CharField(
+        "Статус",
         max_length=150,
-        help_text="статус обработки заявки в системе заявок"
+        help_text="Статус обработки заявки в системе заявок"
     )
 
-
+    # FIXME: Нет уверенности, что после удаления заявки следует оставлять `models.DO_NOTHING`
     ticket_status = models.ForeignKey(
         Ticket,
         on_delete=models.DO_NOTHING,
@@ -32,54 +39,69 @@ class Company(models.Model):
     )
 
     inn = models.CharField(
+        "ИНН компании",
         max_length=12,
         unique=True,
-        help_text="ИНН компании"
     )
     kpp = models.CharField(
-        max_length=9,
-        help_text="КПП компании"
+        "КПП компании",
+        max_length=9
     )
     ogrn = models.CharField(
-        max_length=15,
-        help_text="ОГРН компании"
+        "ОГРН компании",
+        max_length=15
     )
     bank_name = models.CharField(
+        "Название банка",
         max_length=150,
         help_text="Название банка с расчетным счетом"
     )
     bik = models.CharField(
-        max_length=9,
-        help_text="БИК компании"
+        "БИК компании",
+        max_length=9
+    )
+    rs = models.CharField(
+        "Расчетный счет",
+        max_length=50
     )
     ks = models.CharField(
+        "Корреспондентский счет",
         max_length=50,
         help_text="Корреспондентский счёт (счёт, открываемый банковской организацией в подразделении самого банка)"
     )
-    rs = models.CharField(
-        max_length=50,
-        help_text="Расчетный счет"
-    )
     address = models.CharField(
+        "Юридический адрес",
         max_length=150,
         help_text="Юридический адрес"
     )
     is_global = models.BooleanField(
         default=False,
-        help_text="доступно ли для хранения в глобальном сервисе/нужна синхронизация данных"
+        help_text="Доступно ли для хранения в глобальном сервисе/нужна синхронизация данных"
     )
     is_visible = models.BooleanField(
         default=False,
-        help_text="доступно ли для планирования в цепочке с другими услугами в глобальном сервисе"
+        help_text="Доступно ли для планирования в цепочке с другими услугами в глобальном сервисе"
     )
 
-
+    # FIXME: id_metaservice должно принимать текущий идентификатор метасервиса, на котором происходит создание записи
     id_metaservice = models.BigIntegerField(
         null=True,
-        help_text="уникальный идентификатор мета-сервиса, необходимый для синхронизации данных. Один и тот же провайдер может быть для нескольких мета-сервисов, соответственно если происходят изменения в одном, то либо форсируется изменение во всех (если возможно), либо снимается is_global. Соответственно при изменении is_global в true должно происходить согласование с остальными копиями в других сервисах. Нужен в том числе для того, чтобы выяснять, в каких еще сервисах есть этот провайдер."
+        help_text="Уникальный идентификатор мета-сервиса, необходимый для синхронизации данных. Один и тот же провайдер может быть для нескольких мета-сервисов, соответственно если происходят изменения в одном, то либо форсируется изменение во всех (если возможно), либо снимается is_global. Соответственно при изменении is_global в true должно происходить согласование с остальными копиями в других сервисах. Нужен в том числе для того, чтобы выяснять, в каких еще сервисах есть этот провайдер."
     )
+    
+    def get_admin_url(self):
+        """
+        Возвращает URL-адрес текущего объекта в административной панели Django.
 
+        Returns:
+            str: URL-адрес текущего объекта в административной панели Django
+        """
+        content_type = ContentType.objects.get_for_model(self.__class__)
+        return reverse("admin:%s_%s_change" % (content_type.app_label, content_type.model), args=(self.id,))
 
+    def deactivate(self):
+        self.status = "deactivated"
+        self.save()
 
     class Meta:
         db_table = "company"

+ 51 - 6
models/documents.py

@@ -1,5 +1,11 @@
+import re
+import os
+
 from django.db import models
+from django.urls import reverse
 from django.contrib.auth import get_user_model
+from django.contrib.contenttypes.models import ContentType
+
 from dbsynce.models.company import Company
 from tickets.models import Ticket
 
@@ -11,7 +17,25 @@ class Documents(models.Model):
     То есть отдельно таблица с паспортами, отдельно с правами, отдельно с какими-нибудь разрешениями и так далее. 
     Что пока непонятно - документов может быть много разных.
     """
+    DOC_TYPES = [
+        ("01", "Паспорт"),
+        ("02", "ИНН"),
+        ("03", "СНИЛС"),
+        ("04", "Cвидетельство о регистрации компании"),
+        ("05", "Cистема налогообложения"),
+        ("06", "Доверенность / Приказ"),
+        ("07", "Права / Лицензия"),
+        ("08", "Документ, подтверждающий собственность"),
+        ("09", "Документ об образовании"),
+        ("10", "Медицинская книжка"),
+        ("11", "Cправка об отсутствии судимости"),
+        ("12", "Договор (в том числе о трудоустройстве)"),
+        ("13", "Фотография"),
+        ("99", "Иное"),
+    ]
+    DOC_TYPES_DICT = dict(DOC_TYPES)
 
+    # FIXME: id_metaservice должно принимать текущий идентификатор метасервиса, на котором происходит создание записи
     id_metaservice = models.BigIntegerField(
         null=True,
         help_text="уникальный идентификатор мета-сервиса, необходимый для синхронизации данных. Если при синхронизации возникает конфликт (несовместимость) с другим сервисом, предлагается или форсировать изменения везде (если возможно), либо is_global выставляется как false."
@@ -26,11 +50,11 @@ class Documents(models.Model):
         null=True,
         help_text="срок окончания действия документа."
     )
-
+    # FIXME: requirements должно иметь другое значение поумолчанию
     requirements = models.CharField(
         max_length=150
     )
-
+    # FIXME: status должен иметь другое значение поумолчанию
     status = models.CharField(
         max_length=150,
         help_text="активность на основе системы заявок"
@@ -39,10 +63,6 @@ class Documents(models.Model):
         Ticket,
         on_delete=models.DO_NOTHING,
         help_text="id последнего актуального тикета, касающийся статуса. Если он меняет статус на закрытый - вызывается проверка, которая смотрит, нет ли другого открытого по пользователю."
-        )
-    datalink = models.TextField(
-        blank=True,
-        help_text="адрес фактического размещения на физическом носителе, если информация настолько велика, что не может храниться внутри БД."
     )
     doc_type = models.CharField(
         max_length=150,
@@ -75,12 +95,37 @@ class Documents(models.Model):
         null=True,
         help_text="userid проверившего"
     )
+    datalink = models.TextField(
+        blank=True,
+        help_text="адрес фактического размещения на физическом носителе, если информация настолько велика, что не может храниться внутри БД."
+    )
 
+    @staticmethod
+    def parse_requirements(requirements: str):
+        """
+        Метод позволяет разобрать строку requirements в удобочитаемый вид.
+        """
+        return sorted(set(re.findall(r'\d{2}', requirements)))
 
+    def get_admin_url(self):
+        """
+        Возвращает URL-адрес текущего объекта в административной панели Django.
 
+        Returns:
+            str: URL-адрес текущего объекта в административной панели Django
+        """
+        content_type = ContentType.objects.get_for_model(self.__class__)
+        return reverse("admin:%s_%s_change" % (content_type.app_label, content_type.model), args=(self.id,))
 
     class Meta:
         db_table = "documents"
         verbose_name = "Документ"
         verbose_name_plural = "Документы"
 
+
+class DocumentFile(models.Model):
+    document = models.ForeignKey(Documents, related_name='files', on_delete=models.CASCADE)
+    file = models.FileField(upload_to=f'uploads/partners_docs/')
+
+    def __str__(self):
+        return os.path.basename(self.file.name)

+ 15 - 3
models/orders.py

@@ -12,8 +12,6 @@ class Orders(models.Model):
     """
     Orders - таблица с заказами
     """
-
-
     service = models.ForeignKey(
         Service,
         null=True,
@@ -26,7 +24,7 @@ class Orders(models.Model):
         on_delete=models.DO_NOTHING,
         help_text="тип заказа по классификатору услу"
     )
-    state = models.CharField(
+    status = models.CharField(
         max_length=150,
         help_text="текущий статус заказа из возможных на платформе"
     )
@@ -34,6 +32,16 @@ class Orders(models.Model):
         null=True,
         help_text="уникальный идентификатор мета-сервиса, необходимый для синхронизации данных. Если при синхронизации возникает конфликт (несовместимость) с другим сервисом, предлагается или форсировать изменения везде (если возможно), либо is_global выставляется как false."
     )
+    title = models.CharField(
+        max_length=150,
+        help_text="заголовок заказа",
+        default = 'Заголовок'
+    )
+    note = models.CharField(
+        max_length=150,
+        help_text="задание к заказу",
+        default = 'Записи'
+    )
     provider = models.ForeignKey(
         Provider,
         null=True,
@@ -90,6 +98,10 @@ class Orders(models.Model):
     real_price = models.FloatField(
         help_text="цена с учетом тарифа поставщика услуг по факту оказания услуги"
     )
+    asap = models.BooleanField(
+        default=False,
+        help_text="Срочный ли заказ."
+    )
     is_global = models.BooleanField(
         default=False,
         help_text="доступна ли информация по заказу для хранения в глобальном сервисе/нужна синхронизация данных. Если is_global = false, то и is_visible для заказа и вглубь по цепочке для всех исполнителей и ресурсов - тоже false."

+ 1 - 1
models/permissions.py

@@ -26,7 +26,7 @@ class Permissions(models.Model):
         on_delete=models.DO_NOTHING,
         null=True,
         help_text="userid проверившего"
-        )
+    )
     user_id = models.ForeignKey(
         get_user_model(),
         related_name="user_id_perm",

+ 3 - 1
models/provider.py

@@ -11,7 +11,9 @@ class Provider(models.Model):
     По сути - это надстройка к клиентскому аккаунту, иллюстрирующая, что данный пользователь может выступать не только в роли потребителя. 
     То есть, по тому, какие “провайдеры” находятся по идентификатору пользователя - можно установить конкретный список услуг данного пользователя.
     """
-
+    id = models.IntegerField(
+        primary_key=True
+    )
     type = models.CharField(
         max_length=150,
         help_text="тип поставщика (партнер/ответственное лицо/поставщик услуг). Смысл такой - провайдер это статус пользователя, который, в зависимости от применения, может нести разный смысл и подразумевает под собой какой-то тип действия. Обычные исполнители - это провайдеры услуг (код 3). Ответственные за какое-то имущество, которые сдают его в аренду - это тоже провайдеры (код 2). Ответственные за набор услуг перед метасервисом (фактически - назначенные админы) - это провайдеры-партнеры (код 1)"

+ 1 - 1
models/resource.py

@@ -38,7 +38,7 @@ class Resource(models.Model):
     is_global = models.BooleanField(
         default=False,
         help_text="доступны ли данные (по услугам или ресурсам?) для хранения в глобальном сервисе/необходима синхронизация"
-        )
+    )
     is_visible = models.BooleanField(
         default=False,
         help_text="доступно ли для планирования в цепочке с другими услугами в глобальном сервисе"

+ 2 - 2
serializer/orders.py

@@ -1,7 +1,7 @@
 from rest_framework import serializers
-from dbsynce.models import Orders, Client
-from dbsynce.ejabber import *
+from dbsynce.models import Orders
 from tickets.models import Ticket, TicketList
+from dbsynce.ejabber import *
 from core.config import DEBUG, WEBSERVICE_NAME, EJ_HOST, EJ_SERVICE
 import traceback
 import requests

+ 3 - 2
urls.py

@@ -13,6 +13,7 @@ router.register(r'relationship', RelationshipMVS)
 router.register(r'resource', ResourceMVS)
 router.register(r'service', ServiceMVS)
 router.register(r'servicetype', ServiceTypeMVS)
+
 urlpatterns = [  
-    path('api/', include(router.urls), name="metasynced"),
-]
+    path('api/', include(router.urls), name="dbsynced"),
+]