Capture the operational knowledge from the fusion_portal assessment-visit deploys:
the isolated _test addons-path clone-verify technique, the orphaned-tax-FK restore
trap (and the proof that prod -u is safe without touching the orphans because Odoo
skips a present FK), the backup/stage/swap/-u/cache-bust deploy flow with restart
gating, the surgical branch->main merge for branches that predate other merges, and
a fusion_portal module note (ENTERPRISE-only; visit funding-grouping architecture).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Consolidated handoff added to the Partial Order Handling section: the bugs
that only live tablet testing surfaced (phantom stage cards, scan-button
icons/labels, dark-mode undefined --bs-* vars, from-step predecessor block,
seeded-stage auto-finish on drain, gating fall-forward) and the open items
(discoverability badges, Scrap/Rework standalone buttons, automated tests
not written, dark-mode chip polish). Docs only.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rule 17: url_encode (and werkzeug url helpers) are not in the Odoo 19 mail.template QWeb render context -> opaque 'issue with this value' ParseError at install. fusion_repairs note corrected: NOT Community-installable (Enterprise ai+knowledge via fusion_portal->fusion_claims); test on the westin-fr-test Enterprise sandbox; --workers 0 + log_level=warn test-runner gotchas; noupdate templates load on fresh install only. Version 19.0.2.3.0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Inline tile gradient (no !important) was overridden by the theme .card rule,
rendering near-white with invisible white text. Dedicated .portal-visit-card
class (blue->indigo, distinct from the green New Assessment tile).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Plan 1 of fusion_maintenance, verified on the Westin Enterprise sandbox (westin-fr-test) via odoo shell. Maintenance policy (enabled/interval/flat fee/service product) on the equipment category + per-product fee override; contract gains fee/source/serial/policy/currency; fixed the dead _spawn_maintenance_contracts and wired it into the existing action_confirm (delivery-date anchor w/ fallback, two-regime serial dedup, fee resolution product->category); reminder email shows the flat fee; category form exposes the policy. Verified: trigger creates 1 priced contract (fee 149, next_due commitment+6mo, source=sale); idempotent on re-confirm; product override beats category; no contract when category not maintainable; fee renders as $149.00. v19.0.2.3.0.
NOTE: mail_template_data.xml is noupdate=1 -> the fee line loads on fresh install (the prod deploy) but NOT on -u of an already-installed system. The Westin prod-config test container (workers + log_level=warn) does not run --test-enable post_install tests (a pre-existing module load issue under the test phase), so behaviour was verified by odoo shell instead.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A "Ready for X" gating step (fp.step.kind code='gating') maps to
area_kind='receiving' in the taxonomy. For a MID-recipe gate (e.g.
"Ready for processing" between Racking and Plating) that snapped the
job's card back to the far-left Receiving column when work advanced into
it — the job looked like it vanished from the board.
_compute_area_kind now detects gating via the stable kind code and
resolves a gating step's column to the NEXT non-gating step's area (so
"Ready for processing" shows in Plating), keeping cards flowing
left→right. Falls back to the last real stage for a trailing gate.
Non-gating steps unchanged. Helpers: _fp_is_gating_step / _fp_raw_area_kind
(no recursion) / _fp_resolve_area_kind.
area_kind is a stored compute — recomputed all 537 live steps on entech.
Verified: WO-30061 "Ready for processing" area receiving→plating, card now
renders in the Plating column.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- express save captures visit_id; visit-linked submit defers SO creation
(saves draft + signature) and returns to the visit for grouping.
- portal dashboard 'Start a Visit' tile for sales reps.
- fix duplicate-authorizer completion email; visit grouped SOs email once per SO.
- define visit._assessment_sale_type (ADP grouping key) - fixes AttributeError.
Verified on a westin-v19 clone (load + ADP-grouping + combination-guard smoke
test, mail neutralised) then deployed to westin prod 19.0.2.10.0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
_fp_try_autofinish_on_drain guarded on _fp_has_real_incoming() — the WRONG
direction. The first stage (e.g. Racking) is fed by the qty_at_step seed,
not an incoming move, so it never auto-finished when all its parts were
sent forward (operator sent everything out of Racking, step stayed
in_progress at qty 0). Now guards on a real OUTGOING move (parts left),
which covers the seeded first stage.
Still best-effort + gated: button_finish runs the required-input / sign-off
gates, so a step with an unrecorded required input (Racking's "Count the
Parts") won't auto-finish — it stays in_progress for a manual finish after
the input is recorded. Verified on entech.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Move dialog's predecessor check flagged every unfinished step before
the destination — including the from_step itself, which is in-progress by
definition when advancing partial parts out of it. So any "Send → next"
to a not-yet-started step showed a hard "Predecessor not done: <from_step>"
blocker and greyed out SEND (reproduced on WO-30061: Racking → Ready for
processing). This broke partial advance for ALL quantities, not just
1-part orders.
Fix: _blockers_for_move only blocks unfinished steps STRICTLY BETWEEN
from_step and to_step (you'd be skipping an incomplete intermediate
stage). Immediate-next advance is allowed; skip-ahead still blocked;
backward (rework) moves unblocked. Verified on entech: blocker no longer
fires for Racking → Ready for processing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bundles multiple assessments per home visit; on completion groups them by
funding workflow (x_fc_sale_type) into one draft sale order per workflow
(March of Dimes / ADP / ODSP / WSIB / private / hardship / insurance).
Adds the mobility scooter ADP device type, the power-mobility home-access
rule, ADP multi-device combination guard, and the portal visit workspace.
Verified on a westin-v19 clone (clean registry load + funding-grouping
smoke test) then deployed to westin prod (fusion_portal 19.0.2.9.0).
Prod's pre-existing orphaned tax links were preserved (Odoo skips existing
FKs), pending a later audit.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Root cause (verified against the live compiled bundle): Odoo's backend
CSS never DEFINES --bs-body-color / --bs-secondary-color / --bs-*-bg as
custom properties (0 definitions; they're only referenced). So every
color: var(--bs-body-color, #1d1d1f) — and the earlier --bs-secondary-color
swap — resolved to the dark hex fallback in BOTH light and dark mode.
That's why the prior swaps never worked. Backend dark mode here is runtime
[data-bs-theme=dark] + SCSS literals, not those vars.
Fix: the finish-block dialog text now INHERITS the modal's theme-correct
colour (same as the readable title + "Count the Parts" list items) — the
broken line was the only one setting an explicit var() colour. Tinted
banners use translucent rgba() instead of color-mix-with-undefined-var.
Verified in the served bundle: o_fp_finish_block_msg{font-weight:500;}
(no colour override).
CLAUDE.md dark-mode guidance corrected (it had wrongly recommended those
undefined vars).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Surfaced by installing fusion_repairs into a westin-v19 clone (its first-ever clean install; cloud.md's 'installed locally' was stale). (1) Post-visit NPS mail template used url_encode(), which is NOT in Odoo 19's mail.template QWeb render context -> save-validation failed at install (ParseError 'issue with this value'); replaced with a string-method (.replace) fallback. (2) views/menus.xml defined menu_fusion_repairs_configuration AFTER the children referencing it as parent -> 'External ID not found in the system'; moved the parent definition above its children. fusion_repairs now installs cleanly (32 models, 11 templates) on the Enterprise stack.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The "N required input(s) haven't been recorded yet" line still read as
dark/dim in dark mode after the --text-secondary→--bs-secondary-color
swap, because --bs-secondary-color is muted/low-opacity. That line is
primary instruction text, so use the full-contrast var(--bs-body-color)
instead (+ font-weight 500). Reserve --bs-secondary-color for genuinely
secondary text.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(fusion_portal): funding-source selector on accessibility forms
Reps can now mark an accessibility assessment's funding source on the web form
(Private / March of Dimes / ODSP / WSIB / Hardship / Insurance / Other) so the
generated draft sale order routes to the correct funding pipeline instead of
always defaulting to private pay. Adds Hardship to the x_fc_funding_source
selection + sale_type_map; the new form <select> is auto-serialised by the
existing FormData submit, and accessibility_assessment_save now maps
funding_source -> x_fc_funding_source. The model + SO routing were already in
place (2026-04 audit fix) — this closes the form + controller gap.
Plan: docs/superpowers/plans/2026-06-02-accessibility-funding-selector.md
Spec: docs/superpowers/specs/2026-06-02-assessment-visit-funding-design.md
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(fusion_portal): validate funding_source in accessibility save (parity with booking)
Coerce an unexpected/tampered funding_source to direct_private instead of passing
it raw into create() (which would raise on the Selection field). Mirrors the
/book-assessment controller; the whitelist is derived from the model selection so
it auto-covers hardship and any future values.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Operators saw dark-on-dark (invisible) text in the workspace + "Cannot
Finish Step" dialog in Odoo dark mode.
Root cause: var(--text-secondary, #xxx) — a made-up variable that doesn't
exist in Odoo, so it always fell back to the hardcoded dark hex (invisible
on dark). Used 33× across job_workspace.scss + 5 component stylesheets.
Replaced with the real dark-aware var(--bs-secondary-color).
Also fixed paired backgrounds that would hide the now-theme-flipped text:
- finish-block action note → var(--bs-tertiary-bg) (was #f3f4f6).
- Tinted status banners (finish-block step, overtime timer, receiving
status) → color-mix over var(--bs-body-bg) + var(--bs-body-color).
Odoo's bootstrap lacks the BS5.3 -bg-subtle/-text-emphasis vars
(verified against _root.scss), so color-mix is the dark-aware path.
Solid accent pills/dots (white text) and the color-coded plant-card chips
(light-bg + dark-text, readable in both) intentionally left as-is.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Matched, intentional look for the two scan controls:
- Scan QR (camera, primary sticker-scan) — accent-filled blue, fa-qrcode.
- Enter Code (manual / scanner-gun) — accent-tinted secondary, fa-keyboard-o.
Both now use Font Awesome icons (no emoji), inline-flex aligned icon+label.
Enter Code's class restructured so scan-alt persists alongside the active
state when the drawer is open.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The QrScanner component renders its own fa-qrcode icon; the board passed
it label '📷 Camera', so the camera button showed two icons (QR + camera
emoji). Drop the emoji → one icon.
Also clarify the two scan paths (they do different things):
- "Scan QR" = camera scan of the printed job sticker (primary path)
- "Enter Code" = manual / hardware scanner-gun text drawer (no camera)
Reordered so the camera (sticker) scan reads first. Other QrScanner call
sites already pass plain/no labels — this was the only double-icon.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Regression from the partial-order board: _job_presences emitted a card
for any area containing a `ready` step. These recipes seed ALL downstream
steps to `ready` at job creation, so a job showed in every future stage
at once (e.g. WO-30061 across racking/receiving/plating/inspection) even
though no parts had advanced there.
Fix: a stage shows ONLY where parts physically are (qty_at_step > 0,
which includes the first-active seed) OR where a step is in_progress/
paused. A merely ready/pending future step with no parts no longer shows.
Strict sequential progress falls out for free — the qty_at_step seed sits
on the lowest-sequence non-terminal step and advances as each completes.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Operators can now see and advance a job's parts across multiple stages
at once (e.g. 10 Masking / 20 Plating / 20 Baking on one 50-part job).
Tracking model C (fluid per-stage quantities + existing hold/scrap/
rework records for exceptions); board option 2 (a card per occupied
stage); wait-to-reconverge close. Additive only — no new model, no
migration, no change to the close/cert/ship lifecycle.
Board (fusion_plating_shopfloor/controllers/plant_kanban.py):
- One card PER (job, stage), composite key "{job_id}:{area}". Unsplit
jobs render exactly as before. _job_presences/_render_presence;
primary presence keeps full job card_state, secondary presences
derive state from their focus step.
Card (plant_card.js/.xml/.scss):
- "20 of 50 here" badge; tap opens the workspace focused on that
stage's step (focus_step_id, already accepted by the workspace).
Move + light-up (move_controller.py, fusion_plating_jobs/fp_job_step.py):
- Availability/pre-fill now from qty_at_step (step had no qty_done/
qty_scrapped fields — the old read was always 0, dead path).
- Forward move auto-flips destination pending->ready (no auto-start;
labour timer stays explicit) and auto-finishes a drained source
(best-effort). Predecessor gate is qty-aware: a step with real
arrived parts is startable regardless of upstream completion
(_fp_has_real_incoming, single source of truth for can_start /
blocker / button_start / move blockers).
Operator advance (job_workspace.js):
- "Send -> <next>" action on in_progress/paused steps opens the slimmed
Move dialog (qty steppers, no keyboard; advanced fields collapsed).
Was only wired into the deprecated shopfloor_tablet before.
Close (fp_job.py):
- button_mark_done counts move-based scrap (_fp_scrapped_via_moves) into
qty_scrapped and derives qty_done = qty - scrapped (was blindly
= job.qty, over-counting). Reconciliation gate unchanged.
Static-validated: pyflakes (py), lxml parse (xml), node --check (js).
Dynamic tests + browser check need an installed env (entech/trial) —
plating modules can't install on the local Community DB.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Design for parts fanning across shop-floor stages (e.g. 10 at Masking,
20 at Plating, 20 at Baking on one 50-part job):
- Tracking model C — fluid per-stage quantities via existing qty_at_step;
failed/held/rework subsets ride existing hold/scrap/rework records.
- Board Option 2 — a card per stage-presence (composite job:area keys);
unsplit jobs render identically to today.
- Easy-advance operator flow — one "Send to next" action, steppers /
rack-tap (no keyboard), intent-named Hold/Scrap/Rework buttons.
- Light-up plumbing — auto-ready on arrival, qty-aware predecessor gate,
auto-finish source on drain; no auto-start (labour accuracy).
- Close — wait to reconverge; close/cert/ship/invoice lifecycle unchanged.
Additive only: no new core model, no data migration, no change to the
quantity model, OWL component tree, or close lifecycle.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
TDD plan for the enrollment+pricing foundation: maintenance policy fields on the equipment category (+ product fee override), maintenance-contract extensions, fix+wire the dead _spawn_maintenance_contracts into the existing action_confirm (delivery-date anchor, two-regime serial dedup, fee snapshot), fee line in the reminder email, category UI, version 19.0.2.3.0. Grounded in real source. Plans 2-5 (booking on fusion_tasks, visit log + checklist, two-regime backfill, office crons) roadmapped.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Live sizing on Westin: stair lifts ~254 customers / porch-VPL ~30 / lift chairs ~41, but lift serial coverage ~0 (12/416 stairlift lines). The serial-as-unit-key approach (valid for ADP wheelchairs) fails for lifts. Backfill now splits into two regimes: serial dedup for wheelchairs; partner+base-product+sale-line dedup for lifts with accessory-line exclusion via the per-product maintainable flag.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Brainstormed design: bundle a home visit's assessments (ADP + accessibility),
measurement-first with client/funding deferred, add-as-you-go workspace,
per-item funding selector (fixes the March-of-Dimes routing gap), and on
completion group items into ONE draft sale order per funding workflow
(ADP / MOD / ODSP / Hardship / private) reusing the existing pipelines.
Adds ADP multi-device + combination rules, a new mobility-scooter type, and a
power-mobility home-accessibility rule that feeds the accessibility upsell.
v1 keeps manual quotation (no auto-pricing); MOD $15k cap is a reminder only.
Phased 1-3; risks + file map included.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Build maintenance INTO fusion_repairs (engine ~90% already there): per-category policy (interval + flat fee, product override); fix the dead contract-spawn trigger for new sales + a one-time idempotent backfill of the existing install base (lifts + fusion_claims wheelchairs); technician-aware self-serve booking on fusion_tasks availability (NO Enterprise appointment) creating a technician task; structured maintenance visit log + inspection cert for lifts; office follow-up crons; cost shown to client. Out of v1: SMS, /my/equipment, route optimization.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sized the real serial-tracked install base on sale.order.line: ~138 units / ~136 customers across all funders (walkers 68, wheelchairs 45, power bases 7, scooters 4, +14 with no ADP device_type). Serial# is captured ~only on equipment, so it doubles as a trackable-unit marker. ADP-only gating misses ~28 units (direct_private/adp_odsp/march_of_dimes) -> bridge should key on serial, funder-agnostic. Flags two data gaps (no-device_type units; non-ADP units lacking delivery_date) and reframes the MVP open question as volume (walkers/chairs) vs margin (powered units).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Ran Step 0 against Westin prod (westin-v19 on odoo-westin). Resolved the APP/DB placeholders (DO boxes dead; migrated on-prem to odoo-dev-app), added a dated STEP 0 RESULTS section, and corrected the open questions the live inspection disproved: no stair/porch lifts in Westin ADP data; Enterprise appointment already ships native token booking; fusion_repairs contract engine not deployed; device_type is the ADP billing-code catalog taxonomy, not the install base.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Captures the maintenance-followup design exploration so it can resume from a
Tailscale-connected environment with access to Westin production:
- fusion_repairs already has a maintenance contract/reminder/booking engine to reuse
- fusion_claims (sale.order.line + adp.device.code.device_type) is the trigger source
- locked decisions: same DB, Enterprise appointment, public self-serve token booking
- Step 0 live-inspection command pack to run on Westin prod before any code
- open questions (MVP cut, revenue mechanic, tech assignment, booking route)
https://claude.ai/code/session_011wfSKQfSWhKZcm1yzSGznW
Rename module fusion_authorizer_portal -> fusion_portal everywhere:
manifest/assets, controllers, models, views, JS (odoo.define + asset URLs),
migration MODULE constants; plus cross-module refs in fusion_schedule,
fusion_repairs, fusion_quotations (depends + inherit_id) and the pdf_filler
import in fusion_claims. Add rename_module.sql for the one-time in-place DB
rename (ir_module_module, ir_model_data, ir_ui_view.key,
ir_module_module_dependency) required on installed envs before -u fusion_portal.
Document the rename gotcha as rule 16 in CLAUDE.md.
Redesign the Accessibility Assessment selector: replace Font Awesome icon tiles
with photo-banner cards using 7 optimized images (1000x750 PNG -> 800x600 JPEG,
~8MB -> 488KB), per-type colour accent bar + centered pill button, hover
lift/zoom. Images ship as module static files so they deploy/sync with the module.
Drop the regenerable graphify-out cache from the module.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Recomputing only x_fclk_break_minutes left historical x_fclk_net_hours / x_fclk_overtime_hours stale (add_to_compute+flush of one field does not cascade to dependents). Recompute the full chain in dependency order. Caught verifying the entech deploy.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Statutory unpaid break now deducts automatically from worked hours on every path - portal, kiosk, NFC, auto-clock-out cron, AND manual backend entry.
- new fusion.clock.break.rule per-province table (seed Ontario 5h->30, 10h->+30), resolved from the employee's company province with a global default fallback
- x_fclk_break_minutes is now a single idempotent stored compute (statutory(worked_hours) + penalties), replacing the 4 duplicated write sites (_apply_break_deduction x3 callsites + auto-clock-out cron + penalty write)
- retire break_threshold_hours (superseded by per-rule break1_after_hours); post-migrate drops the param and recomputes historical breaks
- 11 tests all green; module install + 19.0.4.1.0 migration verified on modsdev
Bump 19.0.4.0.3 -> 19.0.4.1.0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
People aren't good with 24h. Default Clock-In/Out are now AM/PM dropdowns (15-min
grid) instead of 24h float_time inputs. Stored value stays the float-string
(e.g. '9.0'), so all downstream float(get_param(...)) reads are unchanged;
persisted manually with get-snap for any off-grid value. Bump 19.0.4.0.3.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Settings tidy-up: one 'Kiosk' block holding both PIN Kiosk and NFC Kiosk
(clearly described so users know which is which), each with an Open-kiosk
button when enabled; Corrections + Sounds split into a 'Portal' block. Move
Auto-Wipe Photos under Photo Verification (was hidden for PIN-only clients).
Bump 19.0.4.0.0 -> 19.0.4.0.1.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A proper shared-device PIN kiosk for clients who don't want NFC: photo-tile grid
(+search) -> tap -> PIN (or first-use create) -> optional master-gated selfie ->
clock, in the NFC kiosk's dark glass + brand-gradient style. Built as an Odoo 19
Interaction; new pin_kiosk.scss (scoped); reworked clock_kiosk.py
(search +avatar/has_pin, verify_pin needs_setup, set_pin, clock via kiosk location).
Drops the redundant kiosk_pin_required (PIN always required); relabels the company
kiosk location; adds a PIN-kiosk app icon. Opt-in via enable_kiosk (off by default).
HttpCase tests added. Bump 19.0.4.0.0.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The global enable_photo_verification toggle only fed the portal get_settings flag;
the actual writes ignored it — the NFC kiosk gated on nfc_photo_required and the
portal on location.require_photo, so photos were captured even with the toggle OFF.
Now it's the master: OFF => no photo captured/stored anywhere (NFC kiosk config +
tap, and portal check-in); ON => per-location / NFC settings apply. Test + help
text updated. Bump 3.16.0 -> 3.16.1.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Audit of all 41 settings found 3 that were shown but read nowhere, and 17 Boolean
toggles that couldn't be turned OFF.
- Remove grace_period_minutes (orphaned by the schedule-driven cron rewrite) and
weekly_overtime_threshold (never implemented): field + view + seed.
- enable_ip_fallback now actually gates _verify_location's IP-whitelist check
(default ON to preserve current behaviour).
- All 17 fusion_clock Boolean settings now persist explicitly as 'True'/'False'
via a _FCLK_BOOL_PARAMS loop in get_values/set_values (config_parameter Booleans
can't store False, so OFF never stuck). Add round-trip tests. Bump 3.15.2 -> 3.16.0.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Pay Period Anchor Date was a free-text Char. Make it a fields.Date (date
picker) persisted manually in get_values/set_values as 'YYYY-MM-DD' under
fusion_clock.pay_period_start (res.config.settings Date fields don't round-trip
via config_parameter in Odoo 19). Reader code unchanged. Bump 3.15.1 -> 3.15.2.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
_resolve_tz fell back to env.company.tz, which raises AttributeError for any
user without a personal tz (surfaced by the new list-wide pay-period filters,
which resolve a company-level tz). Use env.company.partner_id.tz. Regression
test added. Bump 3.15.0 -> 3.15.1.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reuse the existing Pay Period setting (Frequency + Anchor) as the single
source of truth via a shared pure helper (models/pay_period.py); fusion.clock.report
delegates to it. Add Current/Previous/Next Pay Period filters to the attendance
search view (search-method computed booleans on hr.attendance), a Bi-Weekly Period
picker wizard (pick start -> auto +2 weeks, editable; Apply opens the filtered list)
reachable from an Attendance menu item and a dashboard tile. Window follows the
configured frequency; TZ-correct via local-day boundaries. Bump 3.14.4 -> 3.15.0.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>