feat(forms): MoneyInput для поля «Оклад» в карточке сотрудника

Раньше Salary был <input type=number> с примитивной валидацией.
Заменено на MoneyInput (как в ценах товаров и др. денежных полях),
который:
- читает org-setting allowFractionalPrices (копейки или нет);
- показывает символ валюты (₸/$/€) справа;
- хранит draft-string для бесшовного ввода 100.50 без потери точки;
- onBlur нормализует значение.

Тип формы изменён: `salary: string` → `salary: number | null`,
сохранение payload берёт значение напрямую без Number() парсинга.

Других денежных полей в формах сотрудников/контрагентов (зарплаты,
авансы, штрафы, доплаты) сейчас НЕТ — есть только Salary в Employee
и MoneyInput уже используется в ProductEditPage (цены) и
SupplyEditPage (себестоимость и сумма по позициям). Поэтому пункт
закрыт одной правкой EmployeesPage.
This commit is contained in:
nns 2026-05-06 11:20:39 +05:00
parent dcb28a9811
commit a6ecc65b97

View file

@ -8,7 +8,7 @@ import { Pagination } from '@/components/Pagination'
import { SearchBar } from '@/components/SearchBar'
import { Button } from '@/components/Button'
import { Modal } from '@/components/Modal'
import { Field, TextInput, TextArea, Checkbox } from '@/components/Field'
import { Field, TextInput, TextArea, Checkbox, MoneyInput } from '@/components/Field'
import { PhoneInput } from '@/components/PhoneInput'
import { useCatalogList, useCatalogMutations } from '@/lib/useCatalog'
import type { PagedResult, RetailPoint } from '@/lib/types'
@ -51,7 +51,10 @@ interface Form {
position: string
email: string
phone: string
salary: string
/** Денежное значение в валюте организации (берётся из useOrgSettings).
* Используется MoneyInput, который сам формирует с копейками или без
* в зависимости от org-setting allowFractionalPrices. */
salary: number | null
taxNumber: string
description: string
imageUrl: string
@ -64,7 +67,7 @@ interface Form {
const blankForm = (): Form => ({
lastName: '', firstName: '', middleName: '', position: '',
email: '', phone: '',
salary: '', taxNumber: '', description: '', imageUrl: '',
salary: null, taxNumber: '', description: '', imageUrl: '',
roleId: '', isActive: true,
retailPointIds: [],
createAccount: true,
@ -113,7 +116,7 @@ export function EmployeesPage() {
const payload = {
lastName: form.lastName, firstName: form.firstName, middleName: form.middleName || null,
position: form.position || null, email: form.email || null, phone: form.phone || null,
salary: form.salary ? Number(form.salary) : null,
salary: form.salary,
taxNumber: form.taxNumber || null,
description: form.description || null,
imageUrl: form.imageUrl || null,
@ -178,7 +181,7 @@ export function EmployeesPage() {
setForm({
id: r.id, lastName: r.lastName, firstName: r.firstName, middleName: r.middleName ?? '',
position: r.position ?? '', email: r.email ?? '', phone: r.phone ?? '',
salary: r.salary != null ? String(r.salary) : '',
salary: r.salary,
taxNumber: r.taxNumber ?? '', description: r.description ?? '', imageUrl: r.imageUrl ?? '',
roleId: r.roleId, isActive: r.isActive, retailPointIds: r.retailPointIds,
createAccount: false,
@ -296,7 +299,7 @@ export function EmployeesPage() {
</div>
<div className="grid grid-cols-2 gap-3">
<Field label="Оклад">
<TextInput type="number" value={form.salary} onChange={(e) => setForm({ ...form, salary: e.target.value })} placeholder="—" />
<MoneyInput value={form.salary} onChange={(v) => setForm({ ...form, salary: v })} />
</Field>
<Field label="ИИН">
<TextInput value={form.taxNumber} onChange={(e) => setForm({ ...form, taxNumber: e.target.value })} placeholder="12 цифр" maxLength={12} inputMode="numeric" />