release 1.3

This commit is contained in:
2026-03-04 22:39:00 +03:00
parent 40dec27d41
commit 0afe756739
5 changed files with 66 additions and 23 deletions
+2
View File
@@ -86,8 +86,10 @@ async def update(reqData: dict = Depends(requestDict)):
).replace(hour=12, minute=0, second=0)
if "attorney" in updatePractitioner and updatePractitioner["attorney"] != "":
logger.info("🔐 МЧД получена")
updatePractitioner["esiaAuth"] = False
if "signature" in updatePractitioner:
logger.info("🔐 УКЭП получен")
updatePractitioner["esiaAuth"] = True
fileData = updatePractitioner.pop("signature")
fileInfo = p7s_save(fileData, updatePractitioner["userIdLpu"])
if fileInfo.success:
+31 -7
View File
@@ -353,6 +353,7 @@ function updateStaffTable() {
}
let rows = '';
const showDisabled = document.getElementById('showDisabled').checked;
filteredPractitioners.forEach(practitioner => {
// Проверяем, что practitioner - объект
@@ -361,6 +362,10 @@ function updateStaffTable() {
return;
}
const isDisabled = practitioner.expired_at === null;
if (!showDisabled && isDisabled) return;
// Определяем тип подписи
let signatureTypeHtml;
if (practitioner.esiaAuth) {
@@ -382,8 +387,9 @@ function updateStaffTable() {
onclick="showAttorneyPopup('${snils}', this)">
<i class="bi bi-eye"></i>
</button>`;
signatureTypeHtml = `<span class="badge bg-warning text-dark">МЧД</span>${eyeIcon}
<span class="badge bg-info">СНИЛС</span>${snilsIcon}`;
mchdBadge = `<span class="badge bg-warning text-dark">МЧД</span>${eyeIcon}`;
slilsBadge = `<span class="badge bg-info">СНИЛС</span>${snilsIcon}`;
signatureTypeHtml = isDisabled ? slilsBadge : `${mchdBadge} ${slilsBadge}`;
}
// Форматируем дату и проверяем срок
@@ -417,25 +423,40 @@ function updateStaffTable() {
} else {
expiryHtml = `
<div class="d-flex align-items-center justify-content-between">
<span class="text-muted">Не установлен</span>
<span class="badge bg-secondary ms-2">-</span>
<span class="text-muted">Без права подписи</span>
</div>
`;
}
// Кнопки действий
const actionsHtml = `
let actionsHtml = '';
if (isDisabled) {
actionsHtml = `
<div class="btn-group btn-group-sm" role="group">
<button type="button" class="btn btn-outline-success"
onclick="editPractitioner('${practitioner.userIdLpu}', true)" title="Добавить УКЭП">
<i class="bi bi-filetype-key"></i>
</button>
<button type="button" class="btn btn-outline-secondary"
onclick="editPractitioner('${practitioner.userIdLpu}', false)" title="Добавить МЧД">
<i class="bi bi-file-earmark-binary"></i>
</button>
</div>
`;
} else {
actionsHtml = `
<div class="btn-group btn-group-sm" role="group">
<button type="button" class="btn btn-outline-primary"
onclick="editPractitioner('${practitioner.userIdLpu}', ${practitioner.esiaAuth})" title="Изменить">
<i class="bi bi-pencil"></i>
</button>
<button type="button" class="btn btn-outline-danger"
onclick="confirmDelete('${practitioner.userIdLpu}')" title="Удалить">
<i class="bi bi-trash"></i>
onclick="confirmDelete('${practitioner.userIdLpu}')" title="Отключить">
<i class="bi bi-person-fill-slash"></i>
</button>
</div>
`;
}
rows += `
<tr>
@@ -642,6 +663,7 @@ function editPractitioner(userIdLpu, isEsiaAuth) {
document.getElementById('ukepPractitionerId').value = userIdLpu;
document.getElementById('ukepFile').required = false;
document.getElementById('ukepSnilsNumber').value = practitioner.snils;
document.getElementById('ukepExpiryDate').value = practitioner.expired_at || '';
// Очищаем data-атрибуты модального окна
const modal = document.getElementById('editUKEPModal');
@@ -663,6 +685,7 @@ function editPractitioner(userIdLpu, isEsiaAuth) {
document.getElementById('mchdPractitionerId').value = userIdLpu;
document.getElementById('mchdNumber').value = practitioner.attorney || '';
document.getElementById('mchdSnilsNumber').value = practitioner.snils;
document.getElementById('mchdExpiryDate').value = practitioner.expired_at || '';
// Очищаем data-атрибуты модального окна
const modal = document.getElementById('editMCHDModal');
@@ -1093,6 +1116,7 @@ document.addEventListener('DOMContentLoaded', function () {
// Обработчики попапа с номером МЧД
document.getElementById('closeAttorneyPopupBtn').addEventListener('click', hideAttorneyPopup);
document.getElementById('closeAttorneyBtn').addEventListener('click', hideAttorneyPopup);
document.getElementById('showDisabled').addEventListener('click', updateStaffTable);
document.getElementById('copyAttorneyBtn').addEventListener('click', function () {
const attorneyNumber = document.getElementById('attorneyNumber').textContent;
+11 -5
View File
@@ -88,11 +88,17 @@
<!-- Кнопка добавления -->
<div class="text-end mt-3">
<div class="d-flex align-items-center justify-content-end">
<div class="form-check d-flex align-items-center me-2">
<input type="checkbox" class="form-check-input" id="showDisabled">
<label class="form-check-label ms-1" for="showDisabled">Отображать сотрудников без права подписи</label>
</div>
<button type="button" id="addStaffBtn" class="btn btn-primary">
<i class="bi bi-plus-circle"></i> Добавить сотрудника
</button>
</div>
</div>
</div>
<!-- Модальное окно для добавления сотрудника -->
<div class="modal fade" id="addStaffModal" tabindex="-1">
@@ -211,22 +217,22 @@
</div>
</div>
<!-- Модальное окно подтверждения удаления -->
<!-- Модальное окно подтверждения отключения -->
<div class="modal fade" id="confirmDeleteModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Подтверждение удаления</h5>
<h5 class="modal-title">Подтверждение отключения</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<p>Вы уверены, что хотите удалить данные сотрудника?</p>
<p class="text-danger fw-bold">Это действие нельзя отменить.</p>
<p>Вы уверены, что хотите отключить данного сотрудника?</p>
<p class="text-danger fw-bold">Этот сотрудник не сможет отправлять запросы</p>
<input type="hidden" id="deletePractitionerId">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Отмена</button>
<button type="button" class="btn btn-danger" id="confirmDeleteBtn">Удалить</button>
<button type="button" class="btn btn-danger" id="confirmDeleteBtn">Отключить</button>
</div>
</div>
</div>
+19 -7
View File
@@ -14,10 +14,10 @@ class Practitioner(Base):
userIdLpu = Column(String, unique=True)
name = Column(String)
fullName = Column(String)
esiaAuth = Column(Boolean)
esiaAuth = Column(Boolean, nullable=True)
attorney = Column(String, nullable=True)
snils = Column(String)
expired_at = Column(DateTime)
expired_at = Column(DateTime, nullable=True)
created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
@@ -35,7 +35,8 @@ class Practitioner(Base):
return await CRUD.update(Practitioner, self.id, **kwargs)
async def delete(self) -> bool:
return await CRUD.delete(self)
clear = {"esiaAuth": None, "attorney": None, "expired_at": None}
return await CRUD.update(Practitioner, self.id, **clear)
@staticmethod
async def addPractitioner(**kwargs):
@@ -79,13 +80,22 @@ class Practitioner(Base):
@staticmethod
async def getPractitionerByIdLpu(
idLpu: str, toDict: bool = True, isCheck: bool = False
idLpu: str,
toDict: bool = True,
isCheck: bool = False,
include_disabled: bool = False,
):
if type(idLpu) != str:
idLpu = str(idLpu)
practitioner = await CRUD.read(
select(Practitioner).where(Practitioner.userIdLpu == idLpu)
query = (
select(Practitioner).where(
Practitioner.userIdLpu == idLpu, Practitioner.expired_at != None
)
if not include_disabled
else select(Practitioner).where(Practitioner.userIdLpu == idLpu)
)
practitioner = await CRUD.read(query)
if practitioner:
data = practitioner.toDict() if toDict else practitioner
return utils.answer(data=data)
@@ -96,7 +106,9 @@ class Practitioner(Base):
@staticmethod
async def editPractitionerByIdLpu(idLpu: str, **kwargs):
practitioner = await Practitioner.getPractitionerByIdLpu(idLpu, False)
practitioner = await Practitioner.getPractitionerByIdLpu(
idLpu, False, include_disabled=True
)
if not practitioner.success:
utils.logger.error(f"Сотрудник не найден, idLpu: {idLpu}")
return utils.answer(success=False, message="Сотрудник не найден")
-1
View File
@@ -4,7 +4,6 @@ from .async_request import *
from .logger import *
from .attachment import *
from .request_parser import RequestParser
from .pdf_saver import *
from .to_dict import *
from .web import *