Skip to content

feat(invoice-schedule): add the semi-monthly invoicing cycle fieldset - #1765

Merged
gabrielseco merged 30 commits into
mainfrom
feat/invoice-schedule-semi-monthly-cycle
Oct 29, 2026
Merged

gabrielseco merged 30 commits into
mainfrom
feat/invoice-schedule-semi-monthly-cycle

Conversation

@gabrielseco

@gabrielseco gabrielseco commented Oct 28, 2026 •

Copy link
Copy Markdown
Collaborator

Description

I left some things in this one

image

The second field is a force field value, if we want to put a select I need to make changes to the SDK core

When you select semi-monthly, in platform they're able to disable array of days, not possible at the moment, requires another SDK core change

image

Summary

Choosing "Semi-monthly" on an invoice schedule now offers the same invoicing cycle choice as the Remote platform: the 15th and last day of the month, or a custom cycle.

Why

The standalone Invoice Schedule flow showed two free-form day inputs (1–31) for semi-monthly. The platform instead offers a radio and, for a custom cycle, a first day (1st–14th) with the second day fixed 14 days later. This brings the flow in line.

What changed

Toggle details
  • A "Semi-monthly" fieldset, shown only for the semi-monthly frequency, holds an "Invoicing cycle" radio (default: "15th and last day of the month" / "Custom cycle"). It replaces the custom_day_1 / custom_day_2 number inputs. The includeCustomDays option is removed: the fieldset is part of the schema everywhere, so the in-flow onboarding step shows it too.
  • "Custom cycle" reveals "First invoice raised on" (1st to 14th) and a read-only line with the second day, always 14 days later.
  • The second day is derived in the schema: a read-only field whose const and statement are computed from the first day, the same way Tiger's computed read-only fields (e.g. CAN avg_salary) work. No form code, no shared component change. The SDK sets the value of a const field and renders its statement.
  • The payload sends custom_days: [15, 31] for the default cycle (the API treats 31 as the last day of the month) and [first, first + 14] for a custom one. Before, nothing was sent unless both days were filled in.
  • start_date is validated against the cycle in the schema (x-jsf-logic-validations): the 15th or the last day of the month for the default cycle (leap years handled), the first or second day for a custom one. Any other date fails with "Choose a date that matches the selected invoicing cycle", the same rule the platform's invoice form applies.
  • The day options are string values, because selects submit strings and the integer cast only covers number types.
  • No exported component or prop changes. The README documents the cycle.
  • Not ported from the platform: greying out the start date picker's days outside the cycle (the SDK's date field props carry no such restriction, so the error shows after a date is picked), and rendering the second day as a disabled select (a const field renders as a statement).
  • Stacked on fix(invoice-schedule): prefill the occurrence count with 1 when end after is picked #1756; retarget to main once it merges.

Screenshots

Toggle screenshots

Default cycle, and a custom cycle with the 3rd as the first day (the second is the 17th). Example app against sandbox, PRT contractor.

Open as a gallery (⌘-click or Ctrl-click for a new tab): click a screenshot, then use the arrow keys.

Default cycle
Default cycle
Custom cycle
Custom cycle

Related Resources

Testing

InvoiceScheduleFlow.test.tsx covers the default cycle ([15, 31]), a custom cycle ([3, 17]), no cycle for another frequency, and a start date outside the cycle being rejected. Unit tests cover buildCustomDays, toOrdinal, the schema and the start date rule for both cycles.

  • Tested against the example/ app in a browser (sandbox, PRT contractor)
  • Feature flag: N/A

🤖 Generated with Claude Code


Note

Medium Risk
Changes how semi-monthly schedules are built and validated before API submit; wrong cycle or date logic could block creates or send unexpected custom_days, though coverage is extensive.

Overview
Semi-monthly invoice scheduling now matches the Remote platform instead of two free-form day inputs.

When Semi-monthly is selected, a Semi-monthly fieldset appears with an Invoicing cycle radio: default 15th and last day of the month (payload custom_days: [15, 31]) or Custom cycle with a first-day select (1st–14th) and a read-only second day computed as first + 14 days. The schema option includeCustomDays is replaced by includeFieldsets, which optionally groups the whole form under platform headings (Invoicing details, Invoice items, Invoicing preferences, Summary). Onboarding passes includeFieldsets: true; the standalone Invoice Schedule flow stays flat.

start_date is validated in the JSON schema so it must fall on a cycle day (including month-end and leap-year rules for the default cycle). buildCustomDays, buildInvoiceScheduleFormValues, and round-trip from saved custom_days were updated for the new semi_monthly_settings shape. Docs and tests cover payloads, fieldsets, and start-date validation.

Reviewed by Cursor Bugbot for commit 83725a3. Bugbot is set up for automated code reviews on this repo. Configure here.

gabrielseco and others added 20 commits October 27, 2026 11:15
…quencies

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A recurring invoice schedule now asks for an end date choice: "No end date" or
"End after a number of occurrences". The count only appears, and is only required
(at least 1), for the second option. Both are hidden for a one-off. The fields sit
right after the start date, and a saved schedule prefills the choice from its
occurrence count.

The choice is form-only: "No end date" still omits nr_occurrences from the request.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…oice

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…fter is picked

The platform defaults the count to 1 once "End after a number of occurrences" is selected. The schema can't express that, so a form hook sets it when the end type changes to multiple and the count is still empty. Used by the standalone flow and the contractor onboarding step.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…en end after is picked"

This reverts commit f34c672. A schema-level default for revealed fields is handled separately.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A field with a schema default that becomes visible after the form mounts is filled with that default if it is still empty. Fields visible on first render, read-only and forced-value fields are left alone. Uses it for the invoice schedule occurrence count, which now starts at 1 once "End after a number of occurrences" is picked.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… revealed

The form can render with no fields while its schema loads. The empty list was recorded as the baseline, so every field that then appeared with a default was filled in. The baseline now starts at the first render that has fields.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ds instead of a form-wide hook

The standalone flow builds its form before the schema loads, so a schema default was never read. It now runs the same initial-values helper the other flows use once its fields arrive, and fills only values that are still empty. The generic reveal-time hook is removed.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…m like the other flows

The form was created in InvoiceScheduleFlow before the schema loaded, so schema defaults could not feed its initial values. InvoiceScheduleForm now mounts once the fields exist and builds its values with getInitialValues, as the onboarding and termination forms do. The flow keeps the bag, the form id and a ref the preview button reads the live values from. The one-off seeding effect is removed.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…eRefObject

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ecord reset

The preview button now reads the latest values from the bag, which the form reports through checkFieldUpdates, so the form no longer registers itself in a ref. The flow is keyed by employmentId, so a different contractor starts from a fresh form. The frequency reset for a Contractor of Record is removed: the schema already rejects a frequency it no longer offers, and onboarding has no such reset.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…er from InvoiceScheduleFlow

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…nt wrapper from InvoiceScheduleFlow"

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…e them on the bag

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…form on isLoading like the other flows

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… already-cached contractor

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… bag members, tidy leftovers

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…alues

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

📦 Bundle Size Report

Metric Current Previous Change Status
Total (gzip) 235.55 kB 234.75 kB +799 B (+0.3%) 🔴
Total (raw) 653.67 kB 651.02 kB +2.66 kB (+0.4%) 🔴
CSS (gzip) 21.95 kB 21.95 kB 0 B (0%) 🟢
CSS (raw) 114.51 kB 114.51 kB 0 B (0%) 🟢

Size Limits

  • ✅ Total gzipped: 235.55 kB / 350 kB (67.3%)
  • ✅ Total raw: 653.67 kB / 850 kB (76.9%)
  • ✅ CSS gzipped: 21.95 kB / 25 kB (87.8%)

Largest Files (Top 5)

  1. index.esm-wIkXrqU6.js - 11.4 kB (0 B (0%))
  2. styles.css - 10.97 kB (0 B (0%))
  3. index.css - 10.97 kB (0 B (0%))
  4. internals-Cdk22Zh1.js - 6.13 kB (0 B (0%))
  5. hooks--Qy2ol9F.js - 5.76 kB (new)
View All Files (301 total)
File Size (gzip) Change
index.esm-wIkXrqU6.js 11.4 kB 0 B (0%)
styles.css 10.97 kB 0 B (0%)
index.css 10.97 kB 0 B (0%)
internals-Cdk22Zh1.js 6.13 kB 0 B (0%)
hooks--Qy2ol9F.js 5.76 kB new
sdk.gen-BRPiw4M1.js 5.58 kB 0 B (0%)
index.js 5.55 kB 0 B (0%)
flows/Onboarding/hooks.js 4.67 kB 0 B (0%)
utils-Bc1UgCXy.js 4.26 kB 0 B (0%)
FieldSetField-IPPOYf7r.js 4.11 kB 0 B (0%)

✅ Bundle size check passed

@github-actions

github-actions Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Deploy preview for adp-cost-calculator ready!

Project:adp-cost-calculator
Status: ✅  Deploy successful!
Preview URL:https://adp-cost-calculator-d6jtpqzt8-remotecom.vercel.app
Latest Commit:83725a3

Deployed with vercel-action

@github-actions

github-actions Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Deploy preview for remote-flows ready!

Project:remote-flows
Status: ✅  Deploy successful!
Preview URL:https://remote-flows-ah6j7h5z0-remotecom.vercel.app
Latest Commit:83725a3

Deployed with vercel-action

…st access

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

📊 Coverage Report

✅ Coverage increased! 🎉

Metric Current Previous Change Status
Lines 89.92% 89.86% +0.05% 🟢
Statements 89.79% 89.74% +0.06% 🟢
Functions 88.69% 88.66% +0.03% 🟢
Branches 81.53% 81.47% +0.06% 🟢

Detailed Breakdown

Lines Coverage
  • Covered: 5520 / 6139
  • Coverage: 89.92%
  • Change: +0.05% (33 lines)
Statements Coverage
  • Covered: 5587 / 6222
  • Coverage: 89.79%
  • Change: +0.06% (34 statements)
Functions Coverage
  • Covered: 1459 / 1645
  • Coverage: 88.69%
  • Change: +0.03% (5 functions)
Branches Coverage
  • Covered: 3408 / 4180
  • Coverage: 81.53%
  • Change: +0.06% (14 branches)

✅ Coverage check passed

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread src/common/invoice-schedules/utils.ts
gabrielseco and others added 2 commits October 28, 2026 08:23
… schema

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread src/common/invoice-schedules/json-schema.ts
gabrielseco and others added 3 commits October 28, 2026 08:44
…ly cycle

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@gabrielseco gabrielseco self-assigned this Oct 28, 2026
Base automatically changed from feat/reveal-field-defaults to main October 28, 2026 10:36
…-semi-monthly-cycle

# Conflicts:
#	src/common/invoice-schedules/json-schema.ts
#	src/common/tests/invoice-schedules.test.ts
#	src/flows/InvoiceSchedule/tests/InvoiceScheduleFlow.test.tsx

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit c96b10c. Configure here.

Comment thread src/common/invoice-schedules/utils.ts
gabrielseco and others added 3 commits October 29, 2026 10:11
)

* feat(invoice-schedule): group the onboarding step under fieldsets

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* fix(invoice-schedule): group the schedule summary under invoicing preferences

* test(invoice-schedule): handle optional fieldsets in the grouping test

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
@gabrielseco
gabrielseco merged commit 15d8e0f into main Oct 29, 2026
13 checks passed
@gabrielseco
gabrielseco deleted the feat/invoice-schedule-semi-monthly-cycle branch October 29, 2026 12:07
@gabrielseco gabrielseco mentioned this pull request Oct 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants