diff --git a/backend/finance/enums.py b/backend/finance/enums.py index 41eb8599..579228a0 100644 --- a/backend/finance/enums.py +++ b/backend/finance/enums.py @@ -24,11 +24,11 @@ class VacationPayInstructionStatus(models.TextChoices): class PayrollComponentKind(models.TextChoices): - EARNING = "earning", "Начисление" - ADJUSTMENT_CREDIT = "adjustment_credit", "Коррекция +" - ADJUSTMENT_DEBIT = "adjustment_debit", "Коррекция −" - DEDUCTION = "deduction", "Удержание" - PAYMENT = "payment", "Выплата" + EARNING = "earning", "Начислить сотруднику" + ADJUSTMENT_CREDIT = "adjustment_credit", "Положительная корректировка" + ADJUSTMENT_DEBIT = "adjustment_debit", "Отрицательная корректировка" + DEDUCTION = "deduction", "Удержать" + PAYMENT = "payment", "Уже выплачено" class PayrollComponentPeriodScope(models.TextChoices): diff --git a/backend/finance/migrations/0030_alter_payroll_component_kind_labels.py b/backend/finance/migrations/0030_alter_payroll_component_kind_labels.py new file mode 100644 index 00000000..59b9aa79 --- /dev/null +++ b/backend/finance/migrations/0030_alter_payroll_component_kind_labels.py @@ -0,0 +1,38 @@ +from django.db import migrations, models + + +KIND_CHOICES = [ + ("earning", "Начислить сотруднику"), + ("adjustment_credit", "Положительная корректировка"), + ("adjustment_debit", "Отрицательная корректировка"), + ("deduction", "Удержать"), + ("payment", "Уже выплачено"), +] + + +class Migration(migrations.Migration): + + dependencies = [ + ("finance", "0029_alter_payrollcustomcomponent_name"), + ] + + operations = [ + migrations.AlterField( + model_name="payrollcustomcomponent", + name="kind", + field=models.CharField( + choices=KIND_CHOICES, + max_length=24, + verbose_name="Влияние на расчёт", + ), + ), + migrations.AlterField( + model_name="payrollstatementline", + name="kind", + field=models.CharField( + choices=KIND_CHOICES, + max_length=24, + verbose_name="Тип", + ), + ), + ] diff --git a/backend/notifications/senders/push.py b/backend/notifications/senders/push.py index 73a6926e..001fed46 100644 --- a/backend/notifications/senders/push.py +++ b/backend/notifications/senders/push.py @@ -18,7 +18,6 @@ from notifications.chat_suppression import suppress_for_active_chat from notifications.push_actions import ( build_push_avatar_token, build_push_read_token, - build_push_reply_token, ) @@ -154,7 +153,6 @@ class PushNotificationSender(BaseNotificationSender): "message_id": message_id, "chat_name": source_data.get("chat_name"), "chat_type": source_data.get("chat_type"), - "can_reply": source_data.get("can_reply", True), "actor_name": actor_name, }, } @@ -171,40 +169,19 @@ class PushNotificationSender(BaseNotificationSender): chat_id=int(chat_id), message_id=int(message_id), ) - reply_token = build_push_reply_token( - notification_id=notification.id, - user_id=notification.recipient_id, - chat_id=int(chat_id), - message_id=int(message_id), - ) except (TypeError, ValueError): read_token = "" - reply_token = "" - if read_token and reply_token: - actions = [] - if show_preview and source_data.get("can_reply", True): - actions.append({ - "action": "reply", - "type": "text", - "title": "Ответить", - "placeholder": "Введите ответ…", - }) - actions.append({ + if read_token: + message_data["actions"] = [{ "action": "mark-read", "type": "button", "title": "Прочитано", - }) - message_data["actions"] = actions + }] message_data["data"].update({ "mark_read_url": "/api/v1/notifications/push/action/read/", "mark_read_token": read_token, }) - if show_preview and source_data.get("can_reply", True): - message_data["data"].update({ - "reply_url": "/api/v1/notifications/push/action/reply/", - "reply_token": reply_token, - }) actor_icon = ( self._get_actor_avatar_url(notification) diff --git a/backend/tests/api/v1/notifications/test_push_sender.py b/backend/tests/api/v1/notifications/test_push_sender.py index 80cc4b16..e42da6d6 100644 --- a/backend/tests/api/v1/notifications/test_push_sender.py +++ b/backend/tests/api/v1/notifications/test_push_sender.py @@ -99,21 +99,15 @@ class TestPushNotificationSenderPayload: assert payload["data"]["actor_name"] == "Рубцова Олеся" assert payload["data"]["mark_read_url"].endswith("/push/action/read/") assert payload["data"]["mark_read_token"] - assert payload["data"]["reply_url"].endswith("/push/action/reply/") - assert payload["data"]["reply_token"] assert payload["actions"] == [ - { - "action": "reply", - "type": "text", - "title": "Ответить", - "placeholder": "Введите ответ…", - }, { "action": "mark-read", "type": "button", "title": "Прочитано", }, ] + assert "reply_url" not in payload["data"] + assert "reply_token" not in payload["data"] assert payload["tag"] == "chat-123" assert "icon" not in payload assert "chat_new_message" not in json.dumps(payload) diff --git a/frontend/public/sw.js b/frontend/public/sw.js index 75c19b09..292aa460 100644 --- a/frontend/public/sw.js +++ b/frontend/public/sw.js @@ -6,7 +6,7 @@ * нативные уведомления даже когда страница закрыта. */ -const SW_VERSION = '2.4.0'; +const SW_VERSION = '2.4.1'; const SW_APP_KIND = self.CORP_SW_APP_KIND || 'main'; const STATIC_CACHE = `eusrr-static-${SW_VERSION}`; const PRECACHE_URLS = [ @@ -30,13 +30,6 @@ const DEFAULT_NOTIFICATION_OPTIONS = { renotify: true, }; -function supportsNativeInlineReply() { - const userAgent = self.navigator?.userAgent || ''; - const isChromium = /(?:Chrome|Chromium|Edg|OPR)\//.test(userAgent); - const isSupportedPlatform = /(?:Windows NT|Android|CrOS)/.test(userAgent); - return isChromium && isSupportedPlatform; -} - function buildNotificationData(payload = {}) { const notificationData = { title: payload.title || payload.head || 'Новое уведомление', @@ -58,7 +51,7 @@ function buildNotificationData(payload = {}) { if (payload.image) notificationData.image = payload.image; if (payload.actions) { notificationData.actions = payload.actions.filter( - (action) => action.type !== 'text' || supportsNativeInlineReply(), + (action) => action.action !== 'reply', ); } return notificationData; @@ -139,48 +132,6 @@ async function markPushNotificationRead(notification) { })); } -async function sendPushQuickReply(notification, reply) { - const data = notification.data || {}; - const normalizedReply = typeof reply === 'string' ? reply.trim() : ''; - if (!normalizedReply) { - throw new Error('Native notification did not provide reply text'); - } - if (!data.reply_url || !data.reply_token) { - throw new Error('Push notification has no signed reply action'); - } - - const response = await fetch( - new URL(data.reply_url, self.location.origin).toString(), - { - method: 'POST', - credentials: 'omit', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - token: data.reply_token, - reply: normalizedReply, - }), - }, - ); - if (!response.ok) { - throw new Error(`Push reply failed with HTTP ${response.status}`); - } - - const result = await response.json(); - const notifications = await self.registration.getNotifications({ includeTriggered: true }); - notifications - .filter((item) => sameIdentity(item.data?.chat_id, data.chat_id)) - .forEach((item) => item.close()); - - const windowClients = await clients.matchAll({ type: 'window', includeUncontrolled: true }); - windowClients.forEach((client) => client.postMessage({ - type: 'PUSH_CHAT_QUICK_REPLIED', - chatId: data.chat_id, - messageId: result.message_id, - replyToId: data.message_id, - notificationId: data.notification_id, - })); -} - /** * Событие установки Service Worker */ @@ -329,19 +280,6 @@ self.addEventListener('message', (event) => { self.addEventListener('notificationclick', (event) => { console.log('[SW] Notification clicked:', event.notification.tag); - if (event.action === 'reply') { - if (typeof event.reply !== 'string' || !event.reply.trim()) { - console.warn('[SW] Native inline reply is unavailable on this platform'); - return; - } - event.waitUntil( - sendPushQuickReply(event.notification, event.reply).catch((error) => { - console.error('[SW] Failed to send push reply:', error); - }), - ); - return; - } - if (event.action === 'mark-read') { event.waitUntil( markPushNotificationRead(event.notification).catch((error) => { diff --git a/frontend/src/components/finance/admin/PayrollPeriodTable.tsx b/frontend/src/components/finance/admin/PayrollPeriodTable.tsx index a33f97d4..ba0acf7d 100644 --- a/frontend/src/components/finance/admin/PayrollPeriodTable.tsx +++ b/frontend/src/components/finance/admin/PayrollPeriodTable.tsx @@ -48,11 +48,11 @@ const statusMeta: Record