From 7338e316b428efce3b9f84450ddc548b5f72b347 Mon Sep 17 00:00:00 2001 From: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com> Date: Mon, 20 Jul 2026 13:23:19 +0800 Subject: [PATCH] fix(hr): compute tenure_years / time_off.days via daysBetween (#3306) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The last two shipped formula fields that silently evaluated to null on the pinned runtime — the date-math ones left for the framework fix in #92/#93: - hr_time_off_request.days: `(end_date - start_date) + 1` (Timestamp−Timestamp → Duration, no `Duration + int`) → `daysBetween(start_date, end_date) + 1`. - hr_employee.tenure_years: `floor((today() - hire_date) / 365)` (unregistered `floor` + Timestamp−Timestamp) → `daysBetween(hire_date, today()) / 365` (integer division floors for a non-negative tenure). Both use the catalog date helpers and the null-guard idiom. Verified against the fixed engine: days=5 (inclusive), tenure=6. BLOCKED on framework#3348 publishing: these rewrites need the null-guard fix (`cond ? : null`) that lands in @objectstack 16.x — on 15.1.1 they still null. Unblock: bump @objectstack/* deps to the release with #3348, then CI's `objectstack build` step (which runs validateStackExpressions) goes green and becomes the standing regression gate. Refs #3306. Co-Authored-By: Claude Opus 4.8 --- packages/hr/src/objects/hr_employee.object.ts | 2 +- packages/hr/src/objects/hr_time_off_request.object.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/hr/src/objects/hr_employee.object.ts b/packages/hr/src/objects/hr_employee.object.ts index f8abcf0..e4a09e1 100644 --- a/packages/hr/src/objects/hr_employee.object.ts +++ b/packages/hr/src/objects/hr_employee.object.ts @@ -130,7 +130,7 @@ export const Employee = ObjectSchema.create({ tenure_years: Field.formula({ label: 'Tenure (years)', group: 'meta', - expression: F`record.hire_date != null ? floor((today() - record.hire_date) / 365) : null`, + expression: F`record.hire_date != null ? daysBetween(record.hire_date, today()) / 365 : null`, }), notes: Field.markdown({ label: 'Internal Notes', group: 'meta' }), diff --git a/packages/hr/src/objects/hr_time_off_request.object.ts b/packages/hr/src/objects/hr_time_off_request.object.ts index a654f34..98f0edb 100644 --- a/packages/hr/src/objects/hr_time_off_request.object.ts +++ b/packages/hr/src/objects/hr_time_off_request.object.ts @@ -49,7 +49,7 @@ export const TimeOffRequest = ObjectSchema.create({ group: 'core', description: 'Inclusive calendar-day span (weekends + holidays counted). Switch to a working-days helper if your policy excludes them.', - expression: F`record.start_date != null && record.end_date != null ? (record.end_date - record.start_date) + 1 : null`, + expression: F`record.start_date != null && record.end_date != null ? daysBetween(record.start_date, record.end_date) + 1 : null`, }), reason: Field.markdown({ label: 'Reason',