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