- number-поля теперь рендерятся как text + inputMode=numeric, чтобы браузер не округлял значения через input type=number - пробелы при вставке в number-поля удаляются - бэкенд нормализует значения number-полей в строку перед сохранением - добавлен хелпер normalizeFieldValueForStorage Closes: искажение расчётного счёта и других длинных числовых полей
79 lines
2.6 KiB
SQL
79 lines
2.6 KiB
SQL
-- Automation: prevent duplicate machine rentals in form 15
|
||
INSERT INTO "automations" (
|
||
"organization_id",
|
||
"name",
|
||
"description",
|
||
"code",
|
||
"trigger",
|
||
"trigger_config",
|
||
"is_active",
|
||
"created_by"
|
||
)
|
||
SELECT
|
||
1,
|
||
'Контроль дублирования аренды машин',
|
||
'Запрещает создавать новую аренду, если машина уже в статусе "В аренде". Показывает активную аренду и ответственного.',
|
||
$CODE$
|
||
const RENTAL_FORM_ID = 15;
|
||
const MACHINE_FIELD_CODE = 'field_3';
|
||
const RENTAL_STATUS_NAME = 'В аренде';
|
||
|
||
const task = ctx.triggerData?.task;
|
||
if (!task || task.formId !== RENTAL_FORM_ID) {
|
||
ctx.result = { allow: true };
|
||
return;
|
||
}
|
||
|
||
const statuses = await ctx.forms.getStatuses(RENTAL_FORM_ID);
|
||
const rentalStatus = statuses.find(s => s.name.trim().toLowerCase() === RENTAL_STATUS_NAME.toLowerCase());
|
||
if (!rentalStatus) {
|
||
ctx.log('Статус "В аренде" не найден');
|
||
ctx.result = { allow: true };
|
||
return;
|
||
}
|
||
|
||
const formFields = await ctx.forms.getFields(RENTAL_FORM_ID);
|
||
const machineField = formFields.find(f => f.code === MACHINE_FIELD_CODE);
|
||
if (!machineField) {
|
||
ctx.log('Поле Машина не найдено');
|
||
ctx.result = { allow: true };
|
||
return;
|
||
}
|
||
|
||
const machineValue = task.customFields?.[`customField_${machineField.id}`] ?? task.customFields?.[MACHINE_FIELD_CODE];
|
||
if (!machineValue) {
|
||
ctx.result = { allow: true };
|
||
return;
|
||
}
|
||
|
||
const existingTasks = await ctx.tasks.list(RENTAL_FORM_ID);
|
||
for (const t of existingTasks) {
|
||
if (t.currentStatusId !== rentalStatus.id) continue;
|
||
const values = await ctx.tasks.getFieldValues(t.id);
|
||
const existingMachine = values.find(v => v.fieldId === machineField.id)?.value;
|
||
if (String(existingMachine) === String(machineValue)) {
|
||
const assignees = await ctx.tasks.getAssignees(t.id);
|
||
const assigneeNames = assignees.map(a =>
|
||
`${a.user.firstName || ''} ${a.user.middleName || ''} ${a.user.lastName || ''}`.trim() || a.user.email
|
||
).join(', ');
|
||
|
||
ctx.result = {
|
||
allow: false,
|
||
error: `Машина уже в аренде. Активная аренда: #${t.id}${assigneeNames ? ' — ' + assigneeNames : ''}`,
|
||
activeRentalTaskId: t.id,
|
||
assignedToName: assigneeNames || null,
|
||
};
|
||
return;
|
||
}
|
||
}
|
||
|
||
ctx.result = { allow: true };
|
||
$CODE$,
|
||
'task.before_create',
|
||
'{"formId": 15}',
|
||
true,
|
||
1
|
||
WHERE NOT EXISTS (
|
||
SELECT 1 FROM "automations" WHERE "organization_id" = 1 AND "name" = 'Контроль дублирования аренды машин'
|
||
);
|