Repository navigation
fix: All Forecasts groups periods newest first, two refusals name fields by label, demo accounts appear on the map (#2060 items 11, 12, 15) - #2197
Merged
objectstack-fleet[bot] merged 2 commits intoOct 20, 2026
Conversation
…elds by label, demo accounts get office locations (#2060 items 11, 12, 15) - All Forecasts grouped by the text period_label, desc, so periods ran as text ("Sep 2026 -> Q4 2026 -> Q4 2025 ..."). It now groups by period_start desc and, under it, by period_label, so periods run newest first and each keeps a readable heading; a quarter and the month that opens it share a start day, which is why the label level stays. - campaign_validation names Start Date / End Date (and In Progress) by their labels instead of start_date / end_date / in_progress. - product_catalog's delete refusal counts what it names: line items, each count with its own plural, no zero listed, and the remedy names Active instead of is_active. - Every seeded account carries office_location ({ lat, lng }) at its billing address, so Accounts by Location plots nine pins instead of excluding all. Claude-Session: https://claude.ai/code/session_01KqkTQNLcxVc588eZpJmp1r Co-authored-by: Claude <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
…usals-account-map
Contributor
Author
席位核对ACCEPT (#2060 items 11, 12, 15) · PR #2197 at
Landing: once every check on |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #2060 (items 11, 12, 15)
Clause-②: no
Three app-side items of the list card, under claim
6288113806, each re-measured as still reproducing on 18.0.0-next.5 by the #2188 track-1 pass (6283839072). #2060 remains open: this PR delivers items 11, 12 (the campaign half and the product half) and 15 only. Item 12's contact half was landed by PR #2183 and is untouched here.11. All Forecasts lists its periods newest first (C-09)
All Forecasts grouped by the text
period_label, descending, so periods ran as text. The view now groups on two levels:period_startdesc, thenperiod_labeldesc.The spec's grouping field (
GroupingFieldSchema:field,order,collapsed) orders a field's groups by that field's own value, and has no key that orders one field's groups by another field or titles them by another field. So there are only two candidates, and both were measured in the console.Measured in headless Chromium at 1440x900 on dev servers (
objectstack dev --compile):mainon a fresh database, the tried variant on that same database, this branch on a second fresh one. Each carried one Q4 2026 quarterly forecast added through REST (the row the nightly sweep writes) beside the seeds:main01808c3:period_labeldescperiod_startdesc alone (tried, not shipped)period_startdesc, thenperiod_labeldescThe header rows show the group's count and nothing else in this console, so the outer 2026-10-01 group's "2" (one quarter row, one month row) is the only number that spans two periods. The quarter's heading comes before the month's because "Q" sorts after every month that opens a quarter. The forecasting page (en, zh-Hans, zh-Hant) said "The list view and dashboards group by
period_labeldirectly"; it now says how the list groups and why, and keeps the dashboards clause as it was.12. Two refusals read the way the screens do (C-11, B-16)
Campaign (
campaign_validation): both refusals now name the fields by their labels, and the status by its option label.main01808c3VALIDATION_FAILED"Campaign start_date (2026-06-01) must not be after end_date (2026-01-01)."VALIDATION_FAILED"The campaign's Start Date (2026-06-01) must not be after its End Date (2026-01-01)."The second refusal is reachable: the hook runs before the engine's required-field check, so an In Progress insert missing a date meets the hook first. An update cannot clear a date at all (400 "End Date is required and cannot be cleared"). The old test comment said the hook's rule "never meets one"; it is corrected.
Product (
product_catalog): the delete refusal counted line items and called them "opportunity(ies)" and "quote(s)". It now says line items, with a real plural on each count, lists no zero, and names the retirement field by its label. The counts were not changed: they still countcrm_opportunity_line_itemandcrm_quote_line_itemrows, because the cheap exact count is the line count (counttakeswhereonly) and the refusal's remedy, retiring the product, does not depend on how many deals the lines sit on.main01808c3DELETE_RESTRICTED"Cannot delete product: referenced by 4 opportunity(ies) and 2 quote(s). Set is_active=false to retire instead."DELETE_RESTRICTED"Cannot delete product: it is on 4 opportunity line items and 2 quote line items. To retire it instead, turn off Active."runShippedHook), as the admininnerMessageDELETE /api/v1/data/crm_product/IDon the seeded Premium Support, dev serverThe remedy changed with the count. "Set is_active=false" named the field by its API key, the same class as the campaign half (C-11), in the message this PR rewrites. On a product record the field reads "Active: Yes".
15. The demo accounts appear on Accounts by Location (A-13)
Each of the nine seeded accounts now carries
office_location: { lat, lng }, the stored shape ofField.location(LocationValueSchema,@objectstack/spec/data), at the building of its billing address. The seeds are insert-once (mode: 'ignore'), so a database seeded before this keeps its blank locations, and the changeset says so.main01808c3In this container the base map style (
demotiles.maplibre.org) cannot be fetched: the egress proxy refuses the host on both runs (connect_rejected), so the pins sit on a blank canvas and the console shows "Map failed to load". That is this sandbox, not the product. REST read after the boot: all nine accounts answer their seeded{ lat, lng }.Tests
test/forecast-list-grouping.test.ts(new): the declared grouping, the field types that makedesctime order, and the sort that orders rows the same way.test/hooks-runtime-service.test.ts: the two campaign refusals on a real write as the marketing user,codeandstatusplus the sentence, with the labels read offcrm_campaignso a renamed label that the refusal does not follow turns it red.test/hooks-runtime-sales.test.ts: the product refusal on a real delete as the admin, for 1 + 2 lines, 3 lines on one deal, and 1 quote line alone, with the nouns read off the two line objects' labels and Active offcrm_product.test/account-seed-locations.test.ts(new): every seeded account'soffice_locationpassesvalueSchemaForof the field, no two share a pin, and the booted app stores each one as seeded.test/line-item-cascade.test.ts: one comment quoting the old remedy, reworded.Ablations, one per changed behaviour, each through the lock with
objectstack/scripts/ablation-replace.mjsin WRAP mode. Each anchor hit once and the blob moved; each restore read blob == HEAD with an emptygit diff HEAD. No build ordist/step applies: the suites import the sources andobjectstack.configdirectly.period_labeldesc aloneforecast-list-grouping: 1 failed, 3 passed. The first attempt was a no-op: the replacement was a substring of the anchor, so the tool refused it (replacement count 1 → 1) and restored before any test ran; re-run with a distinct replacement-t campaign_validation: 1 failed, 3 passed-t campaign_validation: 1 failed, 3 passed-t product_catalog: 2 failed, 4 passed ("Received: … referenced by 3 opportunity(ies) and 0 quote(s) …")office_locationdeleted from the seedaccount-seed-locations: 2 failed, 2 passed ("Apex Logistics: undefined"; stored null)Gates on 83dda22
The
verifychain's seven non-test steps ran in one lock hold,VERDICT command-exit 0, each exit captured into its own log before any pipe.pnpm validatepnpm typecheckpnpm lintpnpm lint:i18n-gatei18n/missing-*issuespnpm hygienetest/hooks-runtime-service.test.tsat 90.5% of the size cap)pnpm hygiene:tokenspnpm buildpnpm testvitest run --shard=k/3 --maxWorkers=2, eachVERDICT command-exit 0: 68 files / 1113 tests; 68 / 1254 + 1 skipped; 68 / 1646. 204 files, equal togit ls-files test/*.test.tsThe control-byte self-scan over the 13 changed files finds none.
Acceptance notes
GroupingFieldSchemaisfield/order/collapsed. The view a manager wants, one heading per period titled "Q4 2026" and ordered byperiod_start, is not expressible, so this PR uses two levels rather than adding a sortable label field to the app. Reported to the seat, not filed by this PR.2026-10-01while the Period Start cell for the same value reads "Oct 1". The spec says the header row carries the raw stored value; formatting it is the renderer's. Polish, reported to the seat.product_catalog's counts still end in.catch(() => 0), unchanged here as the dispatch ruled. Measured: onlysystem_adminandtenant_adminhold delete oncrm_product, and both read both line objects with all-records depth; a sales manager, rep or agent is refused 403PERMISSION_DENIEDbefore the hook runs. Run as a caller with no read on the line objects (the service agent), the shipped body's two counts are refused by security ("Access denied: operation 'count' on object 'crm_opportunity_line_item'") and swallowed to 0, so the hook lets the delete through. No shipped permission set reaches that path; a set that grants product delete without line-item read would. The engine's own restrict (the requiredcrm_productlookup) still refused such a delete under a system context intest/line-item-cascade.test.ts, with its own message. Not measured: that restrict under a user context without read on the lines. The seat decides the follow-up.src/revenue/objects/opportunity_line_item.object.ts:135andsrc/sales/pages/opportunity_detail.page.ts:339. Comments only, nothing ships from them; left for the seat to route.period_labeldirectly" from the old sentence; this PR did not re-measure the dashboards.Generated by Claude Code