fix(number-fields): избегаем потери точности длинных чисел

- number-поля теперь рендерятся как text + inputMode=numeric,
  чтобы браузер не округлял значения через input type=number
- пробелы при вставке в number-поля удаляются
- бэкенд нормализует значения number-полей в строку перед сохранением
- добавлен хелпер normalizeFieldValueForStorage

Closes: искажение расчётного счёта и других длинных числовых полей
This commit is contained in:
2026-07-07 21:03:40 +03:00
commit 1f5ecb6da4
1089 changed files with 237546 additions and 0 deletions

View File

@@ -0,0 +1,68 @@
import { Router } from "express";
import { z } from "zod";
import { storage } from "../storage";
import { authenticateToken, requireFormAdminOrPermission, type AuthenticatedRequest } from "../middleware/auth.middleware";
import { tenantIsolation } from "../middleware/tenant.middleware";
const router = Router();
const offlineCacheSchema = z.object({
enabled: z.boolean(),
maxTasks: z.number().int().min(1).max(5000).optional(),
strategy: z.enum(['recent', 'assigned', 'all']).optional(),
includeFields: z.array(z.string()).optional(),
});
/**
* GET /api/forms/:id/offline-cache
* Получить настройки офлайн-кэша формы.
*/
router.get('/api/forms/:id/offline-cache', authenticateToken, tenantIsolation, async (req: AuthenticatedRequest, res) => {
try {
const id = parseInt(req.params.id);
if (isNaN(id)) return res.status(400).json({ error: 'Неверный ID формы' });
const config = await storage.getOfflineCacheConfig(id, req.organizationId!);
res.json({ success: true, offlineCache: config });
} catch (err: any) {
console.error('[FORM-OFFLINE] get error:', err);
res.status(500).json({ error: err.message || 'Ошибка получения настроек офлайн-кэша' });
}
});
/**
* PUT /api/forms/:id/offline-cache
* Установить настройки офлайн-кэша формы.
*/
router.put('/api/forms/:id/offline-cache', authenticateToken, tenantIsolation, requireFormAdminOrPermission('forms.manage'), async (req: AuthenticatedRequest, res) => {
try {
const id = parseInt(req.params.id);
if (isNaN(id)) return res.status(400).json({ error: 'Неверный ID формы' });
const config = offlineCacheSchema.parse(req.body);
const form = await storage.updateFormOfflineCache(id, req.organizationId!, config);
res.json({ success: true, offlineCache: form.offlineCache });
} catch (err: any) {
console.error('[FORM-OFFLINE] put error:', err);
res.status(500).json({ error: err.message || 'Ошибка обновления настроек офлайн-кэша' });
}
});
/**
* DELETE /api/forms/:id/offline-cache
* Отключить офлайн-кэш формы.
*/
router.delete('/api/forms/:id/offline-cache', authenticateToken, tenantIsolation, requireFormAdminOrPermission('forms.manage'), async (req: AuthenticatedRequest, res) => {
try {
const id = parseInt(req.params.id);
if (isNaN(id)) return res.status(400).json({ error: 'Неверный ID формы' });
await storage.updateFormOfflineCache(id, req.organizationId!, null);
res.json({ success: true });
} catch (err: any) {
console.error('[FORM-OFFLINE] delete error:', err);
res.status(500).json({ error: err.message || 'Ошибка отключения офлайн-кэша' });
}
});
export default router;