3 Commits
Author SHA1 Message Date
Sairaj-24 b00a36df4f fix(timer): toast a setting change only when the value changes (#10315)
* fix(timer): toast a setting change only when the value changes

Re-clicking the same Desktop Timer setting wrote it again and stacked another success alert. Skip the write and the toast when the stored value is unchanged.

Fixes #8479

* fix(timer): compare settings against the last saved value

SSL and auto-start edit the stored object before the equality check, so those saves were skipped.
2026-09-29 14:59:47 +02:00
Sairaj-24 6d1bb12a66 Merge pull request #10265 from Sairaj-24/fix/7832-twitter-to-x-icon
fix(ui): replace footer Twitter bird with X logo
2026-09-24 18:53:20 +02:00
55eff103a0 fix(employee): keep billing rate cents instead of truncating to integers (#10203)
* fix(employee): keep billing rate cents instead of truncating to integers

parseInt dropped decimals on billRateValue and minimumBillingRate, so 10.49 was stored as 10. Persist those money columns as numeric(10,2) and round to two decimal places. Weekly hour limits stay integer.

Fixes #10199

* fix(employee): round billing rates and SQLite rollback to integers

Address PR review: persist two-decimal money in the TypeORM transformer, use half-up rounding instead of toFixed, CAST ROUND on SQLite down, and assert ADD/UPDATE/DROP/RENAME order.

* fix(employee): keep the full old rate range and keep rejecting non-numeric rates

- numeric(10,2)/decimal(10,2) topped out at 99,999,999.99 while the old int
  columns held up to 2,147,483,647; use (14,2), as the payroll money columns
  do, so no value that saved before is rejected and the ALTER cannot overflow
  on existing rows
- toBillingRate turned any non-numeric input into 0, so @IsNumber never
  failed; parse with parseFloat and return NaN for non-finite values so
  'abc', true, {} and 'Infinity' are rejected again and '10,50' keeps 10
- roundToScale returned NaN for numbers printed in exponent form (1e-7)
- specs for all three; prettier on the new migration

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(employee): refuse non-numeric rates instead of storing 0, round like the database

- ColumnNumericTransformerPipe(scale).to() now parses strings like the DTO transform and throws
  400 for anything that is not a finite number. POST /employee and /employee/bulk do not validate
  billRateValue / minimumBillingRate, so 'abc', '', true or {} used to be stored as 0 or 1 where the
  old integer column rejected them.
- roundToScale rounds half away from zero (-1.005 -> -1.01), as Postgres numeric and MySQL decimal
  do; returns NaN rather than 0 for non-numeric input; and leaves doubles too large to hold cents
  unchanged instead of drifting or overflowing to Infinity.
- Migration: Postgres and MySQL alter both columns in one statement (one table rewrite on Postgres,
  no half-applied state on MySQL), and Postgres sets a 5 s lock_timeout so a long transaction on
  employee cannot queue every request behind the ALTER.
- Public-page employee form: bill rate step 0.1 -> 0.01, matching the rates form.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* refactor(employee): type the rate transformer input as unknown, clear static-analysis warnings

- ColumnNumericTransformerPipe.to() takes `unknown`: routes that skip DTO validation pass raw
  request values through, so the string branch is reachable (DeepScan CONSTANT_CONDITION)
- parsing moved into parseNumeric(); Number.parseFloat / Number.NaN (SonarCloud)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Ruslan Konviser <evereq@gmail.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-19 16:09:54 +02:00