fix(salary): show the sysselsättningsgrad product next to the run salary input (#1702)
A Discord report had a 10 % employee: typing 4 531 in the run gave a 453,10 kr gross, so the user typed 45 310 to get it right. The engine was correct (grundlön = månadslön × sysselsättningsgrad / 100) but nothing on the row said so; the formula only lived in Beräkningsdetaljer. Below 100 % the row now prints "× 10 % = 4 531 kr" under the monthly salary (input and read-only shapes), and both employee forms explain under Sysselsättningsgrad that the base salary is monthly salary × degree. Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Jakob Wennberg
Claude Fable 5
parent
f101bde6a8
commit
eb0df1722b
@@ -294,6 +294,7 @@ function NewEmployeeForm({ onCreated, onCancel }: { onCreated: () => void; onCan
|
||||
</Field>
|
||||
<Field label="Sysselsättningsgrad (%)" htmlFor="employment_degree">
|
||||
<Input id="employment_degree" name="employment_degree" type="number" defaultValue="100" min="1" max="100" />
|
||||
<p className="text-xs text-muted-foreground">Under 100 % räknas grundlönen som månadslön × sysselsättningsgrad.</p>
|
||||
</Field>
|
||||
<Field label="Timmar per vecka" htmlFor="hours_per_week">
|
||||
<Input id="hours_per_week" name="hours_per_week" type="number" defaultValue="40" min="1" max="80" step="0.5" />
|
||||
|
||||
@@ -142,6 +142,22 @@ export function RunEmployeesTable({
|
||||
const netValue = sre.net_salary + (sre.tax_withheld - taxValue)
|
||||
const editableSalary = isDraft && canWrite && sre.salary_type === 'monthly'
|
||||
const primaryNumber = isDraft ? sre.monthly_salary : sre.gross_salary
|
||||
// In a draft the number on the row is the full-time monthly salary
|
||||
// the engine multiplies by the employee's sysselsättningsgrad. Below
|
||||
// 100 % that product is not obvious from the input alone (a 10 %
|
||||
// employee typed 45 310 to get a 4 531 kr gross), so spell it out
|
||||
// right under the number instead of only in Beräkningsdetaljer.
|
||||
const degree = Number(sre.employment_degree)
|
||||
const showDegreeHint =
|
||||
isDraft && sre.salary_type === 'monthly' && Number.isFinite(degree) && degree < 100
|
||||
const degreeHint = showDegreeHint ? (
|
||||
<span className="text-[11px] leading-tight text-muted-foreground tabular-nums whitespace-nowrap">
|
||||
{t('degree_hint', {
|
||||
degree: degree.toLocaleString('sv-SE'),
|
||||
amount: formatCurrency(roundOre((sre.monthly_salary || 0) * (degree / 100))),
|
||||
})}
|
||||
</span>
|
||||
) : null
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -176,21 +192,27 @@ export function RunEmployeesTable({
|
||||
|
||||
{/* Right side: editable monthly salary (draft) or the number */}
|
||||
{editableSalary ? (
|
||||
<Input
|
||||
type="number"
|
||||
step="0.01"
|
||||
min="0"
|
||||
defaultValue={sre.monthly_salary}
|
||||
onClick={e => e.stopPropagation()}
|
||||
onBlur={e => onSalaryEdit(sre.employee_id, e.target.value, sre.monthly_salary)}
|
||||
disabled={actionLoading === `salary-${sre.employee_id}`}
|
||||
aria-label={t('salary_input_aria', { name })}
|
||||
className="h-8 w-28 text-right tabular-nums shrink-0"
|
||||
/>
|
||||
<div className="flex flex-col items-end gap-0.5 shrink-0">
|
||||
<Input
|
||||
type="number"
|
||||
step="0.01"
|
||||
min="0"
|
||||
defaultValue={sre.monthly_salary}
|
||||
onClick={e => e.stopPropagation()}
|
||||
onBlur={e => onSalaryEdit(sre.employee_id, e.target.value, sre.monthly_salary)}
|
||||
disabled={actionLoading === `salary-${sre.employee_id}`}
|
||||
aria-label={t('salary_input_aria', { name })}
|
||||
className="h-8 w-28 text-right tabular-nums"
|
||||
/>
|
||||
{degreeHint}
|
||||
</div>
|
||||
) : (
|
||||
<span className="text-right tabular-nums font-medium shrink-0">
|
||||
{formatCurrency(primaryNumber)}
|
||||
</span>
|
||||
<div className="flex flex-col items-end gap-0.5 shrink-0">
|
||||
<span className="text-right tabular-nums font-medium">
|
||||
{formatCurrency(primaryNumber)}
|
||||
</span>
|
||||
{degreeHint}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Payslip PDF */}
|
||||
|
||||
Reference in New Issue
Block a user