Skip to content

BSC fiscal cycles, task delivery, and month locks

Status: Shipped (Phases A–D, June 2026)
Code: api/app/services/bsc_cycle.py, scorecard_config_store.py, task_delivery_*.py, scorecard_month_lock.py, bsc_month_report_store.py
UI: Scorecard → Configurations, Task Delivery, BSC dashboard sync actions

This document is the source of truth for BSC 2025 / 2026 behavior, per-cycle configuration, task delivery, and finalized-month editing rules. Related formulas: User-month calculation, BSC metrics. Dashboard sync: BigQuery dashboard. Mid-month weekly locks for SBS / Non-SBS / ATW: Scorecard § Weekly locking.


1. Fiscal cycle model

ConceptRule
Cycle labelBSC {start_year} — e.g. BSC 2025 = July 2025 through June 2026
Cycle idString start year: "2025", "2026"
Start monthJuly (start_month = 7), stored in bsc-cycle-settings for future flexibility
ResolverCalendar month maps to cycle id: Jul–Dec → that year; Jan–Jun → previous year

Examples:

Calendar monthFiscal cycle id
May 20262025 (still closing BSC 2025)
June 20262025
July 20262026

Registry: Mongo tbl_scorecard_config, document _id: "bsc-cycle-settings" — list of BscCycleDefinition + start_month.

API: GET /v1/scorecard/bsc-cycles — each cycle includes status (upcoming | active | ended) and editable (config edits allowed only while upcoming).

UI: Scorecard → Configurations — cycle dropdown, Add cycle (upcoming cycles only). Extra upcoming cycles beyond the next one may be deleted.


2. Per-cycle configuration (versioned)

Legacy single documents were migrated to per-cycle ids on first access. Each cycle has its own bundle:

ConfigMongo _id pattern
Evaluation count requirementsevaluation_count_requirement-{cycle_id}
Balance scorecard metrics (KPM/KPI tree)balance-score-card-metrics-{cycle_id}
BSC total score goalsbsc-total-score-goals-{cycle_id}
SBS checklist rubricsbs-checklist-rubric-{cycle_id}

Resolve config for a calendar month:

  1. cycle_id = resolve_cycle_id(year, month) — fiscal cycle for the month.
  2. metrics_cycle_id = resolve_metrics_cycle_id(year, month, cycle_id) — same as fiscal cycle_id (metrics follow the fiscal cycle).
  3. Load KPIs, quotas, goals, and rubric from *-{cycle_id}.

API: GET / PATCH /v1/scorecard/configurations?cycle_id=2026 (cycle required on write).

Cycle config lock: Once a cycle’s start month has begun (status active or ended), that cycle’s configuration bundle is read-only in the UI (editable: false on the cycle).


3. BSC 2025 vs BSC 2026 metrics

Both cycles use the same four Productivity KPI keys and weights (20 / 25 / 25 / 30). Formulas differ from BSC 2026 onward (July 2026+). BSC 2025 rules apply through June 2026 inclusive.

BSC 2025 Productivity (Jul 2025 – Jun 2026)

KPIWeightFormula
quantity_of_output20%Mean SBS + Non-SBS quantity_score; eval ratio when month finalized
integrity25%Mean Non-SBS only quantity_score; eval ratio on Non-SBS
accuracy25%Mean SBS + Non-SBS auditors_score (N/A excluded); eval ratio
compliance30%Mean SBS checklist score (timeliness − integrity) − 1% per productivity issue

Non-SBS entries use auditor score only (no checklist).

BSC 2026+ Productivity (Jul 2026 onward)

KPIWeightFormula
quantity_of_output20%Task delivery: daily finished ÷ task count on scheduled workdays through today, month average. No eval ratio.
integrity25%Mean SBS + Non-SBS quantity_score; combined eval ratio
accuracy25%Mean SBS + Non-SBS checklist score (timeliness − integrity); combined eval ratio
compliance30%100% − 1% per productivity issue

Non-SBS entries use the same Yes/No checklist and per-cycle rubric as SBS (no auditor score). Task delivery data is entered on the Task Delivery scorecard page.

Config: quantity_of_output in the BSC 2026 metrics document uses source task_delivery (same KPI key, different compute path).


4. SBS checklist rubric (per cycle)

SBS Yes/No scoring weights, red flags, and integrity divisor are no longer hardcoded only in Python/TS constants.

  • Stored per cycle in sbs-checklist-rubric-{cycle_id}.
  • Loaded at runtime by task-evaluated date: GET /v1/scorecard/sbs-checklist-rubric?year=&month=.
  • Server recalculates on create/update via sbs_rubric_runtime.py + sbs_entry_calculated.py.
  • Web SBS form, view modal, and calculated panel use useSbsRubricForDate + sbsRubricRuntime.ts.

BSC 2025 and 2026 were seeded with the same rubric as the former hardcoded defaults.


5. Task delivery

Purpose

Daily finished tasks vs task count on scheduled workdays for BSC-enabled employees. Contributes the task_delivery KPI when present in the active metrics bundle.

Data

ItemDetail
Collectiontbl_task_delivery_entries
GrainOne document per employee_id + work_date (US M/D/YYYY in API)
Fieldsfinished_task, task_count, submitted_by_id

Workdays

Only days whose schedule day code has is_task_delivery_day: true in the catalog (configured at Scorecard → Configurations → Schedule Day Codes). This is separate from is_working_day (attendance working_days): e.g. VL and SL default to task-delivery eligible but not attendance working days. No Mon–Fri fallback for task delivery (unlike attendance KPI fallback).

Off days and non-TD codes appear in the UI with their abbreviation (OFF, H, VL, etc.) and are not editable for task delivery.

Scoring (monthly KPI)

For each scheduled workday through today (Eastern):

  • Daily % = min(100, finished_task / task_count × 100); task_count = 0 → day excluded from average.
  • Past workday with no entry → 0% for that day (counts in the average).
  • Today with no entry → pending — shown in the grid but excluded from the month average until saved.
  • Month KPI = average of included day scores (round_bsc_up).

Future workdays may be entered in advance; they are excluded from the month average until that calendar day.

KPI breakdown (task_delivery_breakdown) tags each day as scored, missing, or pending; workdays_pending_today counts today's pending days.

Code: task_delivery/kpis.py, task_delivery/workdays.py, task_delivery/store.py; BSC compute passes task_delivery_today via task_delivery_scoring_reference_date.

API

MethodPathNotes
GET/v1/scorecard/task-delivery/monthMonth grid; query: year, month, org filters
PUT/v1/scorecard/task-delivery/dayUpsert one day; requires scorecard create permission + workday

UI

Scorecard → Task Delivery (/scorecard/task-delivery) — month grid, employee detail, day edit modal. Filters match other scorecard pages (cluster / department / employee / month). BSC-only employees.

Grid highlights (July 2026):

Cell stateColorAverage impact
Missing (past workday, no entry)RedCounts as 0%
Pending (today, no entry yet)AmberExcluded until saved
Future workdayNeutralExcluded until the day occurs

Avg (to date) column — hover the header or a cell for a breakdown (scored / missing / pending / N/A counts). Dashboard BSC KPI hover uses the same pending vs missing semantics.


6. Finalized calendar months (dashboard)

Dashboard BSC uses calendar months (Eastern), not fiscal cycles, for sync and finalize.

Mongo: tbl_bsc_month_reports

Document _id: "YYYY-MM" (e.g. "2026-05").

FieldMeaning
statusdraft or final
finalized_atWhen final report was generated
finalized_by_employee_idWho finalized
last_rolling_sync_atLast rolling or manual sync (draft)
reopened_atSet when a final month is reopened (optional audit)

Workflow

text
draft  →  (past month) Generate Final Report  →  final
final  →  (level 5) Reopen Month  →  draft  →  sync  →  Re-generate Final Report  →  final
ActionWhoEffect
Refresh dashboard data (sync)Access level 5Recompute draft month → BigQuery report_status=draft
Generate Final ReportAccess level 5Past month only; full eval ratios; BQ final; Mongo status=final
Re-generate Final ReportAccess level 5Past final month; overwrite BQ final snapshot
Reopen MonthAccess level 5Past final month → status=draft; allows scorecard edits and re-sync

Rolling sync (if enabled) runs for the current month only and skips final months.

API:

  • GET /v1/dashboard/bsc/month-report?year=&month= — status + can_sync, can_finalize, can_regenerate_final, can_reopen, can_edit_entries
  • POST /v1/dashboard/bsc/sync
  • POST /v1/dashboard/bsc/finalize?regenerate=false|true
  • POST /v1/dashboard/bsc/reopen

UI: BSC dashboard — sync / finalize / re-gen / reopen controls (BscSyncActions.tsx).


7. Scorecard entry locks (finalized months)

When a calendar month is final, scorecard mutations for entries in that month are blocked for most users.

Rules

UserCan edit entries in a finalized month?
Access level 1–4No (API 409, UI disabled with tooltip)
Access level 5Yes (super-admin override; should re-sync and re-generate final report after corrections)

Lock is keyed on the entry date (calendar month of):

Entry typeDate field
SBS / Non-SBStask_evaluated_date
Productivity / Attendance / ATWdate_of_occurrence
Task deliverywork_date

Moving an entry to another month checks both old and new months.

Code: scorecard_month_lock.py, wired in scorecard_access.py on all create/update/delete routes.

UI

  • Banner on scorecard entry pages when viewing finalized month(s) (ScorecardMonthLockBanner).
  • Context menu: Edit / Delete / Resync shown but disabled with full lock message on hover.
  • Task delivery: grid cells and detail Edit disabled with same message.
  • Add entry / Upload: Enabled when the current calendar month or any filter-selected month is editable — so users can add rows for an open month while the table filter still shows a finalized month. Per-row locks for historical dates are unchanged.
  • Bulk upload: Preview rejects rows whose entry dates fall in finalized months (levels 1–4).

7b. Weekly locks (SBS / Non-SBS / ATW)

Separate from month finalize: while a month is still open, SBS / Non-SBS / ATW can auto-lock by entry-date week. Full rules (schedule, manual unlock, upload, bonus cadence): Scorecard § Weekly locking and User-month calculation § Bonus qualification.


8. Quick reference — files

AreaPath
Cycle resolver + June overrideapi/app/services/bsc_cycle.py
Per-cycle config + migrationapi/app/services/scorecard_config_store.py
Month report + permissionsapi/app/services/bsc_month_report_store.py
Finalize / sync / reopenapi/app/services/bsc_sync_service.py
Entry month lockapi/app/services/scorecard_month_lock.py
Task deliveryapi/app/domains/scorecard/task_delivery/ (store.py, workdays.py, kpis.py)
BSC computeapi/app/domains/bsc/employee_compute.py, kpi_breakdown.py
Schedule day codesapi/app/domains/scorecard/schedules/day_codes.py
Config UIweb/src/pages/scorecard/configurations/
Task delivery UIweb/src/pages/scorecard/ScorecardTaskDeliveryPage.tsx, task_delivery_components/
Month lock UIweb/src/pages/scorecard/shared/useScorecardMonthEditAccess.ts

9. Operator checklist

  1. Before July — Confirm BSC 2026 cycle exists in Configurations; review metrics split and SBS rubric.
  2. June 2026 close — Finalize May when ready; finalize June when leadership signs off (BSC 2025 metrics through June).
  3. After finalize — No routine edits for that month; reopen only if corrections needed, then sync + re-generate final report.
  4. New fiscal year — Add upcoming cycle in Configurations while status is upcoming; adjust metrics/goals before July 1.