Ascendly Scorecard — System Documentation
Status: Eight operational scorecard entry surfaces + configurations (MongoDB). Bulk Excel upload on ATW, SBS, Non-SBS, Additional Points, and Incidents. Weekly locking for SBS / Non-SBS / ATW (optional; off by default). L5 timely excuse on SBS / Non-SBS (late/excused badges + filter). BSC cycle + month list filters on SBS / Non-SBS (compact float UI shared across tabs). Similar entry warning on form Save and upload preview (five main types). Additional Points for discretionary BSC boosts; Incidents for conduct records that feed bonus qualification. BSC dashboard and cycle versioning documented separately.
See also: Fiscal cycles & locks (fiscal cycles, task delivery, finalized-month locks), Weekly locking, List filters, User-month calculation § Bonus qualification, BigQuery dashboard.
Last updated: July 20, 2026
Table of contents
- Overview
- Architecture
- Use cases by page
- MongoDB collections
- API reference
- Document schemas
- Calculations and derived fields
- List, filter, and export pipeline
- Audit logging
- Frontend structure
- Feature parity matrix
- Known limitations and Phase 2 notes
- Source file index
User guides: Similar entry warning (duplicate check) — advisory prompt on form Save and on bulk upload preview when a matching entry may already exist. Weekly locking — mid-month locks for SBS / Non-SBS / ATW and effect on bonus qualification. Missing/Excess Evals — month gaps plus W1–W4 presence and on-time.
1. Overview
The scorecard module is Ascendly’s operational layer for recording and reviewing employee performance data. It consists of eight entry pages (seven submission surfaces plus task delivery), each backed by MongoDB collections and FastAPI routes under /v1/scorecard.
| Page | Route segment | Collection | Primary purpose |
|---|---|---|---|
| SBS (Side-by-Side) | sbs | tbl_sbs_entries | Live observation: manager scores task execution with a weighted Yes/No/N/A rubric |
| Non-SBS | non-sbs | tbl_non_sbs_entries | Output review; BSC 2025: auditor score. BSC 2026+: same checklist as SBS |
| Attitude Towards Work | attitude-towards-work | tbl_atw_entries | Positive conduct signals (impact, attitude, rating × multiplier) |
| Attendance Issues | attendance-issues | tbl_attendance_issues | Absenteeism, tardiness, over-breaks, etc. |
| Productivity Issues | productivity-issues | tbl_productivity_issues | Call-outs, performance issues, etc. |
| Additional Points | additional-points | tbl_additional_points_entries | Discretionary percentage boosts to a KPM or KPI for a month |
| Incidents | incidents | tbl_incidents | NTE, Incident Reports, CAR, Coaching logs — feeds bonus qualification |
| Task Delivery | task-delivery | tbl_task_delivery_entries | Daily finished tasks / task count on scheduled workdays (BSC KPI) |
Supporting data (same MongoDB database, not scorecard pages):
| Collection | Role in scorecard |
|---|---|
tbl_employees | Employee identity, hire date (tenure), org links, auth; bsc_status gates scorecard pickers and new entries |
tbl_departments, tbl_clusters | Org hierarchy for filters and table enrichment |
tbl_tasks | Per-task goal targets by tenure band (SBS / Non-SBS quantity score) |
tbl_logs | Audit trail for create/update/delete |
BigQuery today: Optional GET /v1/scorecard/me reads pre-aggregated KPI rows from scorecard_kpis (or stub data). Entry CRUD does not use BigQuery yet.
2. Architecture
┌─────────────────────────────────────────────────────────────────────────┐
│ Web (React) — web/src/pages/scorecard/ │
│ Eight entry pages + shared filters, tables, modals, export │
└───────────────────────────────┬─────────────────────────────────────────┘
│ REST /v1/scorecard/*
┌───────────────────────────────▼─────────────────────────────────────────┐
│ API (FastAPI) — api/app/routers/v1/scorecard.py │
│ CRUD → audit_log │ List → facet + table_rows + in-memory filter/sort │
└───────────────────────────────┬─────────────────────────────────────────┘
│
┌───────────────────────┼───────────────────────┐
▼ ▼ ▼
MongoDB tbl_tasks BigQuery (optional)
(5 entry collections) (goals) scorecard_kpis → /me onlyDesign choices:
- Single MongoDB database (
MONGO_DB, defaultchannel_precision) — all collections in one DB. - Pydantic schemas validate API input; Mongo stores flexible BSON documents.
- Server-derived fields — tenure, duration, SBS scores, goals, etc. are computed on the API and persisted (clients cannot set
tenuredirectly). - Table lists — date filter in Mongo; search, org facets, and sort often applied in Python after loading up to 5,000 documents per request.
3. Use cases by page
3.1 SBS (Side-by-Side)
Who: Managers evaluating employees while observing task execution.
Workflow:
- Select employee, task evaluated date, brand, task name, duration (h/m/s), auditor’s score, item count.
- Complete 17 checklist criteria (Preparedness ×4, Creation ×4, Submission ×4, Red flags ×5) as Yes / No / N/A, with optional section comments.
- Submit → API stores entry and computes tenure, SBS %, integrity, timeliness, quantity metrics.
- Browse table with BSC cycle + month, org, search, and timely status filters; sort by employee or task date.
- View full submission in read-only modal; edit or delete via context menu (delete is soft — see §3.11 Recently deleted).
- Bulk select rows → export Excel, resync calculated fields, or multi-delete (soft).
- Page actions — floating hub (bottom-right) expands on hover to Activity log and Recently deleted (when permitted).
Business rules:
- Duration cannot be all zero (h, m, s all 0).
- Any red-flag answer No zeros SBS percentage and timeliness.
submitted_by_idtracks who filed the evaluation.
3.2 Non-SBS
Who: Reviewers scoring output (no live side-by-side observation for BSC 2025; checklist added for BSC 2026+).
Workflow by fiscal cycle:
| Eval date fiscal cycle | Form fields |
|---|---|
| BSC 2025 (through Jun 2026) | Duration, auditor’s score, items, comment — no checklist (existing rows only — see below) |
| BSC 2026+ (Jul 2026 onward) | Duration, items, comment, same Yes/No checklist as SBS (per-cycle rubric) — no auditor score |
Create / upload closed for BSC 2025: New SBS and Non-SBS entries (form create and bulk upload) must use a task-evaluated date in BSC 2026 or later (July 2026 onward). Historical BSC 2025 rows remain viewable and editable. Upload templates are checklist-only (BSC 2026).
Derived fields: tenure, duration, output, goal, quantity_score; BSC 2026+ also stores checklist scores (score, sbs_percentage_score, etc.).
Resync: POST /non-sbs-entries/resync-tenure recalculates tenure + calculated fields (quantity and checklist when applicable).
3.3 Attitude Towards Work (ATW)
Who: Managers recording positive attitude / conduct signals.
Workflow:
- Record employee(s), date of occurrence, impact (Team / Other Departments / Company).
- Impact drives level and base rating (5.0 / 7.0 / 10.0).
- Choose attitude (12 fixed categories), multiplier, remarks; final_score = rating × multiplier (stored as submitted).
- Table supports by entry vs by employee aggregated views with impact drilldown.
- Bulk create on form — multiple employees in one submit.
- Export all rows matching current filters (no row selection export).
On Save, you may be prompted if a similar entry already exists for the same employee, date, attitude, impact, remarks, and multiplier.
ATW entry requests (employee submit → manager approve):
Employees (desktop My ATW or web when enabled) submit requests for the current month with the same fields as a manager ATW entry plus optional proof image (stored in S3 when configured). Managers review on the web ATW page via the Entry requests action (badge shows pending count).
| Step | Actor | Result |
|---|---|---|
| Submit | Employee | pending request; managers notified |
| Approve | Manager (scorecard submit permission) | Creates tbl_atw_entries row; employee notified |
| Reject | Manager | Request closed with review note; employee notified |
| Cancel | Employee (own pending request) | Request withdrawn |
API: /v1/atw-entry-requests (not under /v1/scorecard). See § 5.4a.
3.4 Attendance Issues
Who: HR / managers logging attendance violations.
Fields: employee, date of occurrence, type (Absenteeism | Tardiness), violation (Absent, Late, Missed Bio Log, Over-break, Undertime, Not Working), comment.
Workflow: Filtered table, create/edit modal, filter-based export, context menu (copy timestamp, edit, delete). On Save, you may be prompted if a similar entry already exists for the same employee, date, and violation.
3.5 Productivity Issues
Who: Managers logging productivity / performance call-outs.
Fields: employee, date of occurrence, brand, task evaluated, reason for occurrence (free text).
Workflow: Same pattern as attendance issues. On Save, you may be prompted if a similar entry already exists for the same employee, date, task, brand, and reason.
3.6 Additional Points
Who: Managers awarding discretionary BSC percentage boosts when an employee exceeds expectations (e.g. treat Productivity as 100% for a month despite lower eval averages).
Route: /scorecard/additional-points — first item in the secondary sidebar nav (after the divider, before Incidents).
Fields: employee, date of occurrence, target level (KPM or KPI), target (from that month’s Balance Scorecard Metrics config), points (integer 1–100; 1 point = 1%), reason.
BSC effect (live compute, dashboard, and BigQuery sync after re-sync):
| Target | Effect |
|---|---|
| KPI | Add points directly to that KPI score (min(100, base + points)). |
| KPM | Add points directly to the KPM total (min(100, weighted KPI sum + points)). Individual KPI scores are unchanged; Total BSC uses the boosted KPM total. |
Workflow: Filtered table (Employee, Date, KPM/KPI, Points, Reason), create/edit modal, filter-based export, soft delete + recycle bin, bulk Excel upload (template → preview → commit).
Month lock: Same as other entry types — finalized months block create/edit/delete for access levels 1–4 (level 5 may override).
See User-month calculation § Additional points and BigQuery dashboard.
3.7 Incidents
Who: HR / managers logging formal conduct incidents (NTE, Incident Report, CAR, Coaching log).
Route: /scorecard/incidents — secondary sidebar nav, after Additional Points.
Fields: employee, incident date (incident_date), incident type (NTE | IR | CAR | Coaching log), reason (free text).
BSC effect: Incidents do not change KPI or total BSC scores. Active incidents in a calendar month block bonus qualification for that month (see User-month calculation § Bonus qualification). Re-sync the affected user-month after incident create/update/delete/restore.
Workflow: Filtered table (org, date, incident type, search), create/edit modal, filter-based export, soft delete + recycle bin, bulk Excel upload. Same month-lock and page-actions pattern as attendance/productivity.
3.8 Task Delivery
Who: Managers entering daily output counts for BSC-enabled employees on scheduled task-delivery days only.
Workflow:
- Open Task Delivery — month grid (employees × calendar days).
- Filter by org/month like other scorecard pages.
- Click a workday cell or open employee detail → edit finished tasks and task count.
- Off days / non-TD day codes (OFF, H, VL, etc.) show the schedule abbreviation and are not editable.
- Missing (past TD day, no entry) — red highlight; counts as 0% in the month average.
- Pending (today, no entry yet) — amber highlight; excluded from average until saved.
- Future TD days may be entered early (excluded from average until that day).
Month average: Uses scheduled task-delivery days through today (Eastern). Hover Avg (to date) header or cell for scored / missing / pending / N/A breakdown.
BSC: Contributes task_delivery KPI when the active metrics bundle includes it (BSC 2026 metrics from July 2026 onward — see Fiscal cycles & locks).
Locks: If the dashboard month is finalized, edits are blocked for most users (disabled UI + API 409); level 5 may override or reopen the month on the BSC dashboard.
3.9 Configurations
Route: /scorecard/configurations
Per BSC fiscal cycle (dropdown): eval quotas, balance scorecard metrics, total score goals, SBS checklist rubric, schedule day codes. Upcoming cycles are editable; active/ended cycles are read-only for cycle-specific bundles.
Global (not per cycle):
- Schedule day codes — catalog for employee schedules
- Brands list — suggested brand names for SBS, Non-SBS, and Productivity entry forms (
GET /v1/scorecard/brands). Users can type custom values; the API auto-appends unseen brands on create/update (case-insensitive dedup). Managed under Brands List tab with export, bulk merge, and replace. - Weekly Locking — master enable, week day ranges, lock clock time/timezone,
effective_from,allow_update_locked_entries, and per-total weekly quota distribution (see §3.13)
3.10 Bulk file upload (ATW, SBS, Non-SBS, Additional Points, Incidents)
Pages: Attitude Towards Work, SBS, Non-SBS, Additional Points, Incidents — Upload button (or split-button Upload on Additional Points / Incidents) opens a modal (same permission as create entry).
Flow:
- Download template — personalized
.xlsxfor the logged-in user (employee dropdown scoped to scorecard jurisdiction). - Fill rows on the
Entriessheet (Excel dropdowns for employees, tasks, brands, checklist values where applicable). - Preview — per-row validation with errors before commit. On SBS, Non-SBS, ATW, Attendance, and Productivity uploads, valid rows may show Similar existing? when they match an active entry (see §3.12).
- Commit — inserts valid rows only; shows created / skipped / failed receipt. Duplicate check is not re-run on commit (preview only, for speed).
Rules:
.xlsxonly, max 5 MB.- Submitted-by and timestamps come from the authenticated user (not template columns).
- Month-finalization locks apply per row date (same as manual create).
- When weekly locking is enabled, preview flags / rejects SBS, Non-SBS, and ATW rows whose entry dates fall in a locked week (same rule as manual create; level 5 may bypass).
- SBS/Non-SBS derived fields (tenure, scores, quantity metrics) match form create.
- SBS / Non-SBS: rows with task dates in BSC 2025 are rejected on preview/commit (create/upload closed for that cycle).
API: see §5.10 Bulk upload endpoints.
UI modules: shared/ScorecardUploadFileStep.tsx, per-page *UploadModal.tsx, *UploadPreviewTable.tsx.
SBS-only: row context menu Duplicate as new entry opens the create form pre-filled from an existing row with today's evaluation date (intentional copy — not the same as the similar entry warning).
Pre-submit / pre-upload: Form Save and upload preview may warn if a similar entry already exists (see §3.12). You can still submit or upload after confirming.
3.11 Recently deleted (recycle bin)
Pages: SBS, Non-SBS, ATW, Attendance Issues, Productivity Issues, Additional Points, Incidents — not Task Delivery.
Purpose: Recover accidental deletes while the entry month is still open, or permanently remove bin items before the cron backstop runs.
UI:
- Hover (or tap) the page actions hub at bottom-right → Recently deleted satellite button (badge shows recoverable count).
- Drawer lists soft-deleted entries in org scope while the entry’s occurrence month is still open (not finalized) — shows employee, date, summary, who deleted, when.
- Click rows to select; use toolbar Restore selected or Delete permanently (permission-gated).
- Task Delivery keeps a standalone Activity log button only (no recycle bin).
Permissions:
| Action | Scorecard permission |
|---|---|
| Open bin (list) | edit or delete |
| Restore | edit + month not finalized |
| Permanent delete (UI) | delete + same retention window |
Retention:
- Entry date of occurrence must fall in a calendar month that is not finalized (same gate as scorecard edits).
- After the month is finalized, the worker job
purge_soft_deletedhard-deletes expired soft-deleted rows (replaces the legacy cron script). - Entries without a parseable occurrence date stay in the recycle bin until manually purged — they are not auto-deleted.
Notifications: When an entry is soft-deleted, the evaluated employee receives an in-app notification (scorecard.{kind}.deleted_for_you). Desktop Home refetches scorecard queries when such notifications arrive.
Soft-delete fields (on entry documents while in bin): deleted_at, deleted_by_employee_id, deleted_by_full_name. Active lists, facets, BSC compute, exports, and dashboard self view exclude soft-deleted documents.
API (per submission kind — replace {resource} with sbs-entries, non-sbs-entries, atw-entries, attendance-issues, productivity-issues, additional-points, or incidents):
| Method | Path | Purpose |
|---|---|---|
| GET | /{resource}/deleted | List recoverable deletions in actor scope |
| POST | /{resource}/restore | Batch restore { entry_ids: string[] } |
| POST | /{resource}/purge | Batch permanent delete (soft-deleted rows only) |
Restore recalculates SBS/Non-SBS derived fields. Audit actions: delete on soft delete, restore on recovery, delete on permanent purge (with before snapshot).
3.12 Similar entry warning (duplicate check)
Pages: SBS, Non-SBS, Attitude Towards Work, Attendance Issues, Productivity Issues — on form Save and bulk upload preview. Not Task Delivery, Additional Points, or Incidents.
Purpose: Reduce accidental double-entry when a manager submits the same scorecard record twice.
When it runs:
| Surface | Trigger |
|---|---|
| Entry form | Click Save on create or edit (not while typing) |
| Bulk upload | File preview only — preview table column Similar existing?; confirm on Upload. Commit/import skips the check for speed. |
Behavior:
- Ascendly compares your form against active entries (not recycle-bin rows).
- If one or more similar entries exist, a confirmation dialog appears, for example: “A similar entry may already exist…” with a short summary of the match (task, violation, etc.) and submission timestamp.
- Cancel returns you to the form unchanged. OK / Submit anyway saves the entry — the warning is advisory only; the system never hard-blocks a submit for this reason.
- When editing an entry, the row you are editing is excluded from the match list.
What counts as “similar” (same)?
Two entries are treated as similar when all fields in the row below match (after trimming spaces). This is not a full document comparison — scores, comments, rubric answers, and submission timestamps are ignored.
| Page | Treated as similar when all of these match | Can differ and still trigger a warning |
|---|---|---|
| SBS / Non-SBS | Employee, task evaluated date, task evaluated, brand | Checklist scores, duration, item count, comment, auditor score, submitter |
| Attitude Towards Work | Employee, date of occurrence, attitude, impact, remarks, multiplier | Level, rating, final score, submitter |
| Attendance Issues | Employee, date of occurrence, type of violation, violation | Comment, submitter |
| Productivity Issues | Employee, date of occurrence, task evaluated, brand, reason for occurrence | Submitter |
Examples:
- SBS: Same employee, date, task, and brand as an existing eval → warning, even if the new checklist scores differ.
- SBS: Same employee and date but a different task name → no warning (multiple evals per day are normal).
- ATW: Same employee, date, attitude, and impact but different remarks → no warning (same-day entries with different reasons are normal).
- ATW: Same employee, date, attitude, impact, remarks, and multiplier → warning.
- Attendance: Same employee, date, Tardiness, and Late → warning.
- Productivity: Same employee, date, task, and brand but a different reason → no warning.
Not the same as:
| Feature | Difference |
|---|---|
| SBS “Duplicate as new entry” (context menu) | You choose to copy an existing row into a new form with today’s date — intentional re-entry. |
| SBS / Non-SBS eval quota warning | Warns when monthly eval count would exceed the configured quota — not whether the same task was already evaluated. |
| Task Delivery | One row per employee per work day is enforced separately (overwrite on save, not a “similar entry” prompt). |
| Bulk Excel upload | Duplicate check runs on preview only (one batched query); commit/import skips it for speed. Preview table column Similar existing? |
API: GET /v1/scorecard/duplicate-entry-check — query params depend on entry_type; returns up to five matching entries. See §5.11.
Implementation: api/app/domains/scorecard/shared/duplicate_entry_check.py, api/app/domains/scorecard/shared/duplicate_entry_upload.py, web/src/pages/scorecard/shared/useDuplicateEntryCheck.ts. Design logs: design-log/2026-07-14-scorecard-duplicate-entry-check-form.md, design-log/2026-07-14-scorecard-duplicate-entry-check-upload.md.
3.13 Weekly locking (SBS / Non-SBS / ATW)
Status: Live (feature off by default — enable under Configurations → Weekly Locking).
Design log: design-log/2026-07-14-scorecard-weekly-locking.md.
See also: Month finalize locks in Fiscal cycles & locks; bonus gate in User-month calculation § Bonus qualification.
Weekly locking sits on top of month finalize. While a calendar month is still open (not final), SBS / Non-SBS / ATW can lock by week of entry date so managers submit evenly through the month. Attendance, Productivity, Additional Points, Incidents, and Task Delivery are not under weekly locks.
How weeks are defined
Each calendar month is split into admin-editable day-of-month ranges on the entry date (task_evaluated_date for SBS/Non-SBS, date_of_occurrence for ATW) — not on when the form was saved.
| Week (default) | Entry-date days | Auto-lock day | Lock clock (default) |
|---|---|---|---|
| W1 | 1–7 | 8 | 8:00 PM Asia/Manila |
| W2 | 8–14 | 15 | 8:00 PM Asia/Manila |
| W3 | 15–21 | 22 | 8:00 PM Asia/Manila |
| W4 | 22–end of month | 29 (clamped to EOM) | 8:00 PM Asia/Manila |
ATW vs SBS / Non-SBS (W4 tail): When W4 locks on the 29th, SBS / Non-SBS block the whole range (22–EOM). ATW only blocks occurrence dates before the lock day (22–28); days 29–31 stay creatable until month finalize — same idea as W1–W3, where the lock calendar day itself stays open.
Ranges, lock days, clock time, and timezone are editable in Configurations → Weekly Locking. Changing the schedule is live: the next resolution / sync uses the new schedule (no freeze of the old deadline unless you rely on a manual override).
What a locked week blocks
Effective lock = manual override if set, else now ≥ scheduled lock datetime.
| Action | Locked week | Open week |
|---|---|---|
| Create with entry date in that week | Blocked (levels 1–4) | Allowed |
| Edit non-date fields | Allowed if allow_update_locked_entries (default on); else blocked | Allowed |
| Change entry date | Always blocked while the week is locked | Allowed (target week must also be editable) |
| Delete | Same as edit non-date fields | Allowed |
- Later weeks stay creatable after earlier weeks lock.
- Level 5 bypasses weekly hard locks the same way as month finalize.
- Month finalize still locks the whole month; manual weekly lock/unlock is 409 while the month is
final(reopen first). - Enforced on forms, bulk upload, and ATW request submit/approve paths.
UI: On SBS / Non-SBS / ATW, open the page 3-dot actions menu → weekly locks modal (status + Lock / Unlock for level 5). Forms show a locked-week banner and disable Submit when the chosen date’s week is locked.
Manual lock / unlock (level 5)
Admins unlock a week so managers can still submit late operational entries (backfills). Unlock does not change the scheduled deadline used for bonus — see below.
How weekly locking affects bonus qualification
A sixth bonus gate, timely weekly submission, runs for SBS + Non-SBS when cadence applies to that month:
| Config | When cadence applies |
|---|---|
effective_from set (e.g. 2026-07) | Months on/after that month — even if hard locking is temporarily disabled |
effective_from blank | Only while Enable weekly locking is on |
For each employee-month:
- Tenure quotas determine required SBS / Non-SBS totals (same as eval quotas).
- Those totals are split into per-week increments (configurable distribution; defaults e.g. Non-SBS 5 →
2/1/1/1, SBS 3 →1/1/1/0). - Entries are bucketed by entry-date week.
- An entry counts as on-time for that week only if
createdAtis before the week’s scheduled lock datetime.
Hard lock vs bonus (important):
| Hard weekly lock | Bonus timely gate | |
|---|---|---|
| Purpose | Block/allow create & date edits | Score whether submissions met the schedule |
| Unlock week | Lets managers still create | Does not grant on-time credit |
| What time is used | Live effective lock (schedule + override) | Scheduled lock only |
| ATW | Locked like SBS/Non-SBS | Not in the bonus cadence |
Moving a lock day in Configurations (e.g. W1 lock day 8 → 9) does change the scheduled deadline on the next BSC sync, so a create that was late under the old schedule may become on-time under the new one (if createdAt is still before the new instant). Re-sync to refresh qualification.
Timely excuse (level 5): On SBS / Non-SBS, level 5 can mark an entry Excused with a required reason (POST …/excuse) or clear it (DELETE …/excuse) from the table / view modal. Badges show Late / Excused. List filters include a Timely status dropdown (late, excused). Excused entries count as on-time for bonus cadence and Missing/Excess even when submitted after the scheduled lock. Unlock alone never excuses. No auto-excuse.
Full check list and examples: User-month calculation § Bonus qualification.
Go-live tip: Set effective_from to the first month that should use the cadence before turning on Enable for production months.
API / code: GET|POST /v1/scorecard/weekly-locks (+ /lock, /unlock); settings on scorecard config; api/app/domains/scorecard/shared/week_lock.py, api/app/domains/bsc/timely_weekly_submission.py. Approaching-lock reminders: notification type scorecard.weekly_lock.approaching (see Notifications).
3.14 Missing/Excess Evals (monthly + weekly)
Route: /scorecard/missing-excess-evals (read-only).
Compares SBS / Non-SBS counts vs tenure quotas for selected calendar months (department → employee drill).
When exactly one month is selected and weekly submission cadence applies (same rules as §3.13 / bonus gate):
- Each SBS / Non-SBS cell shows the month
actual / required, plus W1–W4 chips:- Presence = entries whose entry date falls in that week.
- Status: On time (enough presence created before the scheduled lock), Late (presence met but created after schedule), Short (not enough presence), or — (no weekly requirement).
- Unlocking a week can fill presence late; late chips still show Late (aligned with bonus).
- Level 5 may excuse a late SBS / Non-SBS entry (required reason). Excused entries count as on-time for bonus and show e.g.
1 excusedon week chips; they do not invent presence if the week is still short. - Rows with any short/late week are treated as incomplete even if the month total is met.
Multi-month selection (or cadence not active) keeps the classic month-only columns.
API: GET /v1/scorecard/missing-excess-evals — rows include weeks_enabled, sbs_weeks, non_sbs_weeks.
4. MongoDB collections
| Collection | Constant (collections.py) | Written by |
|---|---|---|
tbl_sbs_entries | TBL_SBS_ENTRIES | SBS CRUD |
tbl_non_sbs_entries | TBL_NON_SBS_ENTRIES | Non-SBS CRUD |
tbl_atw_entries | TBL_ATW_ENTRIES | ATW CRUD |
tbl_atw_entry_requests | TBL_ATW_ENTRY_REQUESTS | Employee ATW submit; manager approve/reject |
tbl_attendance_issues | TBL_ATTENDANCE_ISSUES | Attendance CRUD |
tbl_productivity_issues | TBL_PRODUCTIVITY_ISSUES | Productivity CRUD |
tbl_additional_points_entries | TBL_ADDITIONAL_POINTS_ENTRIES | Additional points CRUD |
tbl_incidents | TBL_INCIDENTS | Incidents CRUD |
tbl_task_delivery_entries | TBL_TASK_DELIVERY_ENTRIES | Task delivery day upsert |
tbl_scorecard_config | TBL_SCORECARD_CONFIG | Per-cycle BSC config + bsc-cycle-settings |
Connection: MongoDB connection settings are configured on the server (MONGO_URI, MONGO_DB).
Indexes at startup (api/app/main.py): employees, clusters, departments, enrollments, productivity issues, logs. Scorecard entry collections do not yet have dedicated startup indexes.
5. API reference
Base path: /v1/scorecard
5.1 Shared query parameters (list & facet endpoints)
| Parameter | Description |
|---|---|
date_mode | none, month, year, exact, range |
month_values, year_values, exact_dates, range_dates | Date filter values (format depends on mode) |
occurrence_year, occurrence_month, exact_date, year_value, range_start, range_end | Alternate date param names (legacy compatibility) |
search | Free-text search on enriched table rows |
cluster_ids, department_ids, employee_ids | Comma-separated org filters |
timely_flags | SBS / Non-SBS only — comma-separated late and/or excused (empty = all) |
sort_by | Column key (varies by entity) |
sort_dir | asc or desc |
page, page_size | Pagination (list endpoints only) |
UI note (SBS / Non-SBS): The filter float picks a BSC cycle and one or more months in that July–June window. The client still sends date_mode=month + month_values (empty month selection expands to all twelve months in the cycle). cycle_id is persisted in the URL / session for the float only — not a Mongo query param.
Date field per entity:
| Entity | Mongo date field |
|---|---|
| SBS, Non-SBS | task_evaluated_date |
| ATW, Attendance, Productivity, Additional Points | date_of_occurrence |
| Incidents | incident_date |
5.2 SBS entries
| Method | Path | Purpose |
|---|---|---|
| GET | /sbs-entries | Paginated table list |
| GET | /sbs-entries/facet-source | Cascading filter options |
| GET | /sbs-entries/matching-selection | All entry IDs matching filters (bulk select) |
| GET | /sbs-entries/{entry_id} | Single entry (form/view) |
| POST | /sbs-entries | Create |
| PATCH | /sbs-entries/{entry_id} | Update |
| DELETE | /sbs-entries/{entry_id} | Soft delete (sets deleted_at; excluded from active lists) |
| GET | /sbs-entries/deleted | List recoverable soft-deleted entries |
| POST | /sbs-entries/restore | Batch restore |
| POST | /sbs-entries/purge | Batch permanent delete (bin items only) |
| POST | /sbs-entries/export-rows | Export table rows by ID list (1–500) |
| POST | /sbs-entries/resync | Recalculate server-derived fields for ID list (1–200) |
| POST | /sbs-entries/resync-tenure | Hidden legacy alias of /resync |
| GET | /sbs-entries/upload/template | Download personalized SBS upload workbook |
| POST | /sbs-entries/upload/preview | Validate uploaded workbook (multipart file) |
| POST | /sbs-entries/upload | Commit valid rows from workbook |
| POST | /sbs-entries/{entry_id}/excuse | Level 5 — mark entry excused (body: { reason }) |
| DELETE | /sbs-entries/{entry_id}/excuse | Level 5 — clear timely excuse |
5.3 Non-SBS entries
Same as SBS except:
- Resync:
POST /non-sbs-entries/resync-tenure(tenure + quantity fields) - Export:
POST /non-sbs-entries/export-rows - Upload:
GET/POST …/non-sbs-entries/upload/template|preview|upload - Recycle bin:
GET/POST …/non-sbs-entries/deleted|restore|purge - Excuse:
POST|DELETE /non-sbs-entries/{entry_id}/excuse(same level-5 rules as SBS)
5.4 ATW entries
| Method | Path | Purpose |
|---|---|---|
| GET | /atw-entries | Paginated list |
| GET | /atw-entries/facet-source | Facets |
| GET | /atw-entries/export | All filtered rows |
| GET/POST/PATCH/DELETE | /atw-entries/{entry_id} | CRUD (DELETE is soft) |
| GET | /atw-entries/deleted | Recycle bin list |
| POST | /atw-entries/restore, /atw-entries/purge | Restore / permanent delete |
| GET | /atw-entries/upload/template | Download personalized ATW upload workbook |
| POST | /atw-entries/upload/preview | Validate uploaded workbook |
| POST | /atw-entries/upload | Commit valid rows |
5.4a ATW entry requests
Base path: /v1/atw-entry-requests (top-level router, not under /v1/scorecard).
| Method | Path | Purpose |
|---|---|---|
| GET | `` | List — scope=mine (employee) or scope=approval (manager pending queue) |
| GET | /pending-count | Pending count for manager badge (requires scorecard submit) |
| POST | `` | Submit (multipart: fields + optional proof image) |
| GET | /{request_id} | Single request |
| GET | /{request_id}/proof-url | Presigned S3 URL for proof image |
| DELETE | /{request_id} | Employee cancel (pending only) |
| POST | /{request_id}/approve | Manager approve → creates ATW entry |
| POST | /{request_id}/reject | Manager reject with review_note |
5.5 Attendance issues
| Method | Path | Purpose |
|---|---|---|
| GET | /attendance-issues | Paginated list |
| GET | /attendance-issues/facet-source | Facets |
| GET | /attendance-issues/export | All filtered rows |
| GET/POST/PATCH/DELETE | /attendance-issues/{issue_id} | CRUD (DELETE is soft) |
| GET | /attendance-issues/deleted | Recycle bin list |
| POST | /attendance-issues/restore, /attendance-issues/purge | Restore / permanent delete |
5.6 Productivity issues
| Method | Path | Purpose |
|---|---|---|
| GET | /productivity-issues | Paginated list |
| GET | /productivity-issues/facet-source | Facets |
| GET | /productivity-issues/export | All filtered rows |
| GET/POST/PATCH/DELETE | /productivity-issues/{issue_id} | CRUD (DELETE is soft) |
| GET | /productivity-issues/deleted | Recycle bin list |
| POST | /productivity-issues/restore, /productivity-issues/purge | Restore / permanent delete |
5.7 Additional points
| Method | Path | Purpose |
|---|---|---|
| GET | /additional-points | Paginated list |
| GET | /additional-points/facet-source | Facets |
| GET | /additional-points/export | All filtered rows |
| GET/POST/PATCH/DELETE | /additional-points/{entry_id} | CRUD (DELETE is soft) |
| GET | /additional-points/deleted | Recycle bin list |
| POST | /additional-points/restore, /additional-points/purge | Restore / permanent delete |
| GET | /additional-points/upload/template | Download personalized additional points upload workbook |
| POST | /additional-points/upload/preview | Validate uploaded workbook |
| POST | /additional-points/upload | Commit valid rows |
5.8 Incidents
| Method | Path | Purpose |
|---|---|---|
| GET | /incidents | Paginated list |
| GET | /incidents/facet-source | Facets |
| GET | /incidents/export | All filtered rows |
| GET/POST/PATCH/DELETE | /incidents/{incident_id} | CRUD (DELETE is soft) |
| GET | /incidents/deleted | Recycle bin list |
| POST | /incidents/restore, /incidents/purge | Restore / permanent delete |
| GET | /incidents/upload/template | Download personalized incidents upload workbook |
| POST | /incidents/upload/preview | Validate uploaded workbook |
| POST | /incidents/upload | Commit valid rows |
5.9 Personal KPIs (BigQuery)
| Method | Path | Purpose |
|---|---|---|
| GET | /me | Current user’s metrics from BigQuery scorecard_kpis (or stub) |
Resolves employee_key from JWT user: employee_key field, else employment_data.employee_id, else Mongo _id.
5.10 Bulk upload endpoints
Shared behavior for ATW, SBS, Non-SBS, Additional Points, and Incidents upload routes:
| Concern | Behavior |
|---|---|
| File type | .xlsx only (application/vnd.openxmlformats-officedocument.spreadsheetml.sheet or empty content-type with .xlsx name) |
| Max size | 5 MB |
| Template scope | Active BSC employees visible to uploader's scorecard jurisdiction |
| Submitter | Authenticated user (submitted_by_id); impersonation header respected |
| Month lock | Rows with dates in finalized months rejected for access levels 1–4 |
| Preview response | Per-row errors/warnings; possible_duplicate on five main upload types; derived field preview where applicable |
| Commit response | created_count, skipped_invalid_count, failed_count, per-row failures |
| Method | Path | Purpose |
|---|---|---|
| GET | /brands | Global brands suggestion list (for entry forms and upload templates) |
5.11 Pre-submit form checks
Shared read-only endpoints used by entry forms before save.
| Method | Path | Purpose |
|---|---|---|
| GET | /eval-quota-check | SBS / Non-SBS only — projected monthly eval count vs configured quota (employee_id, task_evaluated_date, entry_type, optional exclude_entry_id) |
| GET | /duplicate-entry-check | Similar-entry warning for the five main entry types (entry_type, employee_id, date + type-specific fields; ATW also requires remarks + multiplier; optional exclude_entry_id) |
Both return JSON only; the web UI shows window.confirm dialogs on Save. Neither endpoint blocks create/update on the server.
Bulk upload: Duplicate detection for preview uses the same similarity keys via duplicate_entry_upload.py inside POST …/upload/preview (batched query; response fields possible_duplicate / possible_duplicate_count). It does not call GET /duplicate-entry-check. Commit and async import skip the check.
6. Document schemas
Dates are stored as M/D/YYYY strings for occurrence/evaluation dates. Timestamps use M/D/YYYY H:M:S.mmm.
6.1 SBS entry (tbl_sbs_entries)
User-provided (create/update):
| Field | Type | Notes |
|---|---|---|
timestamp | string | Submission time |
employee_id | string | Subject employee |
task_evaluated_date | string | M/D/YYYY |
task_evaluated | string | Task name (links to tbl_tasks) |
brand | string | |
submitted_by_id | string | Submitter employee id |
hours, minutes, seconds | int | Duration components (not all zero) |
auditors_score | int | 0–100 |
number_of_items | int | 1–999999 |
comment | string | General comment |
preparedness_1–4, creation_1–4, submission_1–4 | Yes/No/N/A | Checklist |
redflag_1–5 | Yes/No/N/A | Red flags |
*_comment | string | Section comments (4 fields) |
Server-calculated (persisted, not accepted from client on write):
| Field | Description |
|---|---|
tenure | Newbie / Advance / Intermediate / Tenured |
duration | Decimal minutes |
output | Minutes per item |
goal | Target from tbl_tasks for task + tenure |
quantity_score | goal ÷ output, capped 0–1 |
sbs_actual_score, sbs_total_score, sbs_percentage_score | Weighted checklist |
integrity_score, timeliness, score | Derived penalties |
createdAt | UTC datetime (on insert) |
late | Computed for list/view — submitted after scheduled week lock |
excused, excuse_reason, excused_at, excused_by_employee_id | Level-5 timely excuse (optional) |
6.2 Non-SBS entry (tbl_non_sbs_entries)
Same operational and quantity fields as SBS without checklist fields for BSC 2025; BSC 2026+ adds checklist scores. Server fields: tenure, duration, output, goal, quantity_score, plus the same late / excused* timely fields as SBS.
6.3 ATW entry (tbl_atw_entries)
| Field | Type | Notes |
|---|---|---|
timestamp | string | Unique per entry (1ms bump if collision) |
employee_id | string | |
date_of_occurrence | string | M/D/YYYY |
submitted_by_id | string | |
impact | Team | Other Departments | Company | |
attitude | 12 literals | See atw_entry.py |
level | Average | Excellent | Exemplary | Derived from impact |
rating | float | 5.0 / 7.0 / 10.0 from impact |
multiplier | float | ≥ 0 |
final_score | float | Typically rating × multiplier |
remarks | string |
Impact → level & rating:
| Impact | Level | Rating |
|---|---|---|
| Team | Average | 5.0 |
| Other Departments | Excellent | 7.0 |
| Company | Exemplary | 10.0 |
6.4 Attendance issue (tbl_attendance_issues)
| Field | Values |
|---|---|
type_of_violation | Absenteeism, Tardiness |
violation | Absent, Late, Missed Bio Log, Over-break, Undertime, Not Working |
| + | timestamp, employee_id, date_of_occurrence, submitted_by_id, comment |
6.5 Productivity issue (tbl_productivity_issues)
| Field | Notes |
|---|---|
timestamp, employee_id, date_of_occurrence, submitted_by_id | Standard |
brand, task_evaluated | |
reason_for_occurrence | Free text |
6.6 Additional points (tbl_additional_points_entries)
| Field | Notes |
|---|---|
timestamp, employee_id, date_of_occurrence, submitted_by_id | Standard |
target_level | kpm or kpi |
target_key | KPM/KPI key from metrics config at save time |
target_label | Snapshot label at save time |
points | Integer 1–100 (percentage points to add) |
reason | Free text |
6.7 Incidents (tbl_incidents)
| Field | Notes |
|---|---|
timestamp, employee_id, incident_date, submitted_by_id | Standard |
incident_type | NTE, IR, CAR, or Coaching log |
reason | Free text |
6.8 Table row API shape (all entry types)
List/facet/export responses enrich each row with:
employee_name— fromtbl_employeesclusters[]—{ id, name }departments[]—{ id, name, cluster_id }
SBS/Non-SBS rows additionally expose calculated columns for the grid (duration, output, scores, etc.).
7. Calculations and derived fields
Implementation is shared between API (api/app/services/) and SBS UI preview (web/.../sbsScorecardFields.ts). Server values are authoritative on save.
7.1 Tenure
Module: api/app/services/scorecard_tenure.py
- Load
employment_data.hire_datefromtbl_employeesbyemployee_id. - Compute whole months from hire date to evaluation/occurrence date (day-aware).
- Map to band:
| Months employed | Tenure label |
|---|---|
| 0–4 | Newbie |
| 5–8 | Advance |
| 9–11 | Intermediate |
| 12+ | Tenured |
- Missing employee or hire date → Newbie
- Evaluation before hire date → ValueError (surfaces in resync
errorsarray)
Goal lookup keys: Newbie → newbie, Advance → advance, Intermediate → intermediate, Tenured → tenured in tbl_tasks.goals.
7.2 Duration and output
Module: api/app/services/sbs_entry_calculated.py
duration = hours×60 + minutes + seconds÷60 (decimal minutes, 2 dp)
output = duration ÷ number_of_items (0 if items ≤ 0)7.3 Goal and quantity score
Module: api/app/services/sbs_entry_goal.py
- Find
tbl_taskswheretask_name=task_evaluated. - Read
goals[tenure_band].target(float). - If missing →
goal = 0.
quantity_score = min(1, max(0, goal ÷ output)) when goal > 0 and output > 0, else 0Interpretation: ratio of target time-per-item to actual; 1.0 = at or better than goal.
7.4 SBS checklist scoring
Criterion weights (SBS_CRITERION_SCORES):
| Field | Points |
|---|---|
| preparedness_1 | 10 |
| preparedness_2, 3, 4 | 5 each |
| creation_1, creation_3 | 5 |
| creation_2, creation_4 | 10 |
| submission_1, 3, 4 | 10 |
| submission_2 | 15 |
Algorithm:
- If any
redflag_*= No →sbs_actual_score = 0,sbs_total_score = 0,sbs_percentage_score = 0. - Else for each preparedness/creation/submission criterion:
- N/A → excluded from total weight
- Yes → add weight to actual and total
- No → add weight to total only
sbs_percentage_score = actual ÷ total(0 if total is 0)
7.5 Integrity, timeliness, final SBS score
integrity_score = (sum of weights for criteria marked "No" ÷ 2) ÷ 100
timeliness = 0 if any red flag is "No", else 1
score = max(0, timeliness − integrity_score)All three are stored as ratios (0–1), not percentages.
7.6 Non-SBS calculated fields
Module: api/app/services/non_sbs_entry_calculated.py
Reuses duration, output, goal, quantity_score only — no checklist or integrity/timeliness.
7.7 ATW
No server calculation service. level and rating are set from impact on create/update. final_score is stored as provided (UI computes rating × multiplier).
7.8 Resync behavior
| Endpoint | Updates |
|---|---|
POST /sbs-entries/resync | tenure, duration, all SBS scores, output, integrity, timeliness, score, goal, quantity_score |
POST /non-sbs-entries/resync-tenure | tenure, duration, output, goal, quantity_score |
Response: { updated, not_found, errors }. Each successful update writes an audit log entry.
8. List, filter, and export pipeline
Mongo find (date filter only, up to 5000 docs)
→ *_docs_to_table_rows (join employee, dept, cluster names)
→ apply_*_filters (search + org [+ timely_flags for SBS/Non-SBS] in Python)
→ sort_scorecard_table_rows
→ paginate (router) OR return all (export)8.1 Filter float UI
Each entry page uses a right-rail filter float (same chrome as dashboard scope filters). When expanded (embedded), controls use inline dropdown triggers (label + value on one row):
| Control | SBS / Non-SBS | Other entry tabs |
|---|---|---|
| Search | Inline search row | Inline search row |
| Period | BSC cycle (single-select) + Month (multi-select in that July–June window) | Flexible date modes (ScorecardDateFilterFields: none / exact / month / year / range) |
| Timely status | Multi-select dropdown (late, excused; empty = all) | — |
| Organization | Cluster → department → employee cascade | Same |
SBS / Non-SBS defaults: active Eastern calendar month’s fiscal cycle and that month (e.g. BSC 2026 + July 2026). Empty current month stays empty — no auto-walk-back to older months with data. Empty month selection = all months in the selected cycle (client expands to twelve YYYY-MM values on the API). Org cascade prune still runs against month-scoped facet rows.
Design note: design-log/2026-07-20-scorecard-bsc-cycle-month-filters.md.
8.2 Facet source pattern
Most entry types (fetch_contextual_*_facets):
- Org facets (
facet_items) — rows after date filter + search only (no org filters). - Date facets (
date_facet_items) — rows after search + org filters, no date filter (builds year/month/day option lists from entries).
SBS / Non-SBS (optimized):
- Org facets — same as above (date-scoped).
- Date facets — always empty. Month options come from the BSC cycle calendar on the client (
GET /v1/scorecard/bsc-cycles+cycle_months_all), which removes the previous all-time Mongo hydrate on every filter load.
8.3 Export modes
| Pages | Export mechanism |
|---|---|
| SBS, Non-SBS | POST .../export-rows with selected entry_ids |
| ATW, Attendance, Productivity, Additional Points, Incidents | GET .../export? same filters as list (all matching rows) |
Frontend Excel: web/src/pages/scorecard/shared/scorecardExcelExport.ts + per-page *Export.ts.
9. Audit logging
Module: api/app/audit_log.py
On create, update, delete, restore, and resync updates, CRUD calls log_change() → tbl_logs.
| Resource key | Collection |
|---|---|
sbs_entry | tbl_sbs_entries |
non_sbs_entry | tbl_non_sbs_entries |
atw_entry | tbl_atw_entries |
attendance_issue | tbl_attendance_issues |
productivity_issue | tbl_productivity_issues |
additional_points_entry | tbl_additional_points_entries |
incident | tbl_incidents |
Log fields include: action, page path, resource, resource_id, actor employee id/key/name, redacted before/after snapshots, changed_fields, API path, HTTP method.
UI: On the seven submission pages (excluding Task Delivery), Activity log and Recently deleted share a page actions floating hub (bottom-right; hover to expand). Task Delivery shows Activity log only. Access level 1 for scorecard activity-log routes.
10. Frontend structure
Routes (web/src/App.tsx):
/scorecard → index redirect (last segment or SBS)
/scorecard/sbs
/scorecard/non-sbs
/scorecard/attitude-towards-work
/scorecard/attendance-issues
/scorecard/productivity-issues
/scorecard/additional-points
/scorecard/incidents
/scorecard/task-deliveryLayout: layout/ScorecardLayout.tsx — outlet + floating nav rail (sidebar/ScorecardFloatingRail.tsx).
Navigation: sidebar/scorecardNav.ts — primary segments (SBS, Non-SBS, ATW, etc.) plus secondary nav (Additional Points, Incidents, Missing/Excess Evals, Configurations).
10.1 Shared modules (shared/)
| Module | Role |
|---|---|
scorecardDateFilterTypes.ts, scorecardDateFilterQuery.ts, scorecardDateFilterLogic.ts | Date filter state and API serialization |
scorecardCycleMonthFilter.ts, ScorecardBscCycleMonthFields.tsx, bscCycle.ts | SBS / Non-SBS BSC cycle + month filter (calendar months, no data walk-back) |
scorecardFilterPersistence.ts | URL / session persistence for date + org (+ cycle_id, timely_flags) |
ScorecardDateFilterFields.tsx, CheckboxDateDropdown.tsx | Flexible date filter UI (non–SBS/Non-SBS tabs) |
ScorecardInlineSearchField.tsx | Compact search row in filter floats |
CheckboxOrgDropdown.tsx, filterOptionSearch.ts | Org / cycle / timely facet dropdowns (layout="inline" in floats) |
ScorecardTimelyBadges.tsx, ScorecardExcuseModal.tsx | Late / excused badges and level-5 excuse dialog |
scorecardTableSort.ts, SortableScorecardTh.tsx | Column sorting |
scorecardTableRow.ts | Row styling |
scorecardExcelExport.ts | Excel download |
durationInputUtils.ts | Duration clamp; client duration/output preview |
TaskEvaluatedSelect.tsx | Task picker (GET /v1/tasks) |
ScorecardQuantityCalculatedPanel.tsx | Read-only quantity metrics (Non-SBS view) |
ScorecardResyncProgressBanner.tsx | Chunked resync progress |
useScorecardActiveEmployeeIds.ts | BSC-included employees for forms (bsc_status + active) |
useScorecardMonthEditAccess.ts | Month-finalization edit/upload gates |
ScorecardUploadFileStep.tsx, scorecardUploadApi.ts | Bulk upload download + file step |
ScorecardPageActionsFloat.tsx | Floating hub: activity log + recently deleted satellites |
ScorecardDeletedDrawer.tsx, scorecardDeletedTypes.ts | Recycle bin drawer (list, restore, purge) |
*UploadModal.tsx, *UploadPreviewTable.tsx, *UploadApi.ts | Per-page upload flow (ATW, SBS, Non-SBS, Additional Points, Incidents) |
10.2 Per-page folders
| Folder | Key components |
|---|---|
sbs_components/ | SbsTable, SbsFilters, SbsEntryFormModal, SbsEntryViewModal, SbsCalculatedFieldsPanel, sbsScorecardFields.ts |
non_sbs_components/ | Parallel to SBS; NonSbsEntryViewModal |
atw_components/ | AtwTablePanel, employee aggregation, AtwEntryFormModal, AtwEntryRequestsModal, AtwProofImagePicker |
attendance_issues_components/ | Table, filters, form |
productivity_issues_components/ | Table, filters, form |
additional_points_components/ | Table, filters, form, upload (KPM/KPI target picker from month config) |
incidents_components/ | Table, filters, form, upload |
React Query keys: ['scorecard', 'resource', ...] and ['scorecard', 'resource-facet', ...] (replace resource with the page key, e.g. additional-points).
10.3 SBS checklist UI
Defined in sbsScorecardFields.ts:
- Preparedness (4 criteria) — verification, validation, escalations, analysis
- Creation (4) — source files, data input, naming, review
- Submission (4) — upload, trackers, notifications, timeliness
- Red flags (5) — failure indicators; any No fails the submission
Client mirrors server math via computeSbsScores and computeSbsDerivedMetrics for live form preview.
11. Feature parity matrix
| Feature | SBS | Non-SBS | ATW | Attendance | Productivity | Add. Pts | Incidents |
|---|---|---|---|---|---|---|---|
| Floating filters (org + search) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| BSC cycle + month filter | ✓ | ✓ | — | — | — | — | — |
| Flexible date modes (exact/month/year/range) | — | — | ✓ | ✓ | ✓ | ✓ | ✓ |
| Timely status filter (late / excused) | ✓ | ✓ | — | — | — | — | — |
| L5 timely excuse | ✓ | ✓ | — | — | — | — | — |
| Compact inline filter float | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Paginated sortable table | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Create / edit modal | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| View submission modal | ✓ | ✓ | — | — | — | — | — |
| Row bulk selection | ✓ | ✓ | — | — | — | — | — |
| Select all matching (API) | ✓ | ✓ | — | — | — | — | — |
| Export selected rows | ✓ | ✓ | — | — | — | — | — |
| Export all filtered | — | — | ✓ | ✓ | ✓ | ✓ | ✓ |
| Resync calculated fields | ✓ | ✓ | — | — | — | — | — |
| Yes/No/N/A scorecard form | ✓ | — | — | — | — | — | — |
| Multi-delete (selection) | ✓ | ✓ | — | — | — | — | — |
| Employee aggregation view | — | — | ✓ | — | — | — | — |
| Bulk create (multi employee) | — | — | ✓ | — | — | — | — |
| Activity log | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Recently deleted (soft delete + restore) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Bulk Excel upload | — | ✓ | ✓ | — | — | ✓ | ✓ |
| Similar entry warning on form Save | ✓ | ✓ | ✓ | ✓ | ✓ | — | — |
| Similar entry warning on upload preview | ✓ | ✓ | ✓ | ✓ | ✓ | — | — |
| Affects BSC user-month scores | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | — |
| Affects bonus qualification | — | ✓ | — | ✓ | — | — | ✓ |
Reference implementation: SBS — other entry-style pages (Non-SBS) should match its selection/export/resync pattern where applicable. Incidents follow the attendance/productivity pattern (export-all, recycle bin, upload); they gate bonus qualification only, not KPI math.
12. Known limitations and Phase 2 notes
Current limitations
- In-memory filtering — Up to 5,000 documents loaded per list/facet request; search and org filters run in Python, not Mongo aggregation.
- No scorecard collection indexes — Only date filter hits Mongo; consider indexes on
task_evaluated_date,date_of_occurrence,employee_id. - Facet cost — Most tabs still run an all-time date-facet hydrate to build month/day options. SBS / Non-SBS skip that scan (cycle calendar on the client). Further wins: org options from
/v1/organization/treeor Mongo$distinct, and push org filters into Mongo. - BigQuery scope — Only
/meKPI stub/table; entry data is Mongo-only. - Cross-page analytics — BSC dashboard reads user-month aggregates (Mongo and/or BigQuery); operational pages remain separate UIs.
- Finalized months — After Generate Final Report on the dashboard, entry edits for that calendar month are locked unless the month is reopened (see Fiscal cycles & locks).
- Weekly locking — Optional mid-month locks for SBS / Non-SBS / ATW; off until enabled. See §3.13.
Dashboard + BigQuery (partially implemented)
User-month BSC materialization, rolling sync, finalize/reopen, and dashboard UI are live — see BigQuery dashboard. Raw entry tables are still Mongo-only; BQ holds summarized KPI rows per employee-month.
13. Source file index
API — router & config
| Path | Description |
|---|---|
api/app/routers/v1/scorecard.py | All HTTP endpoints |
api/app/collections.py | Collection name constants |
api/app/config.py | Mongo + BigQuery env settings |
api/app/bigquery_client.py | fetch_scorecard_rows for /me |
API — schemas
| Path | Entity |
|---|---|
api/app/schemas/sbs_entry.py | SBS |
api/app/schemas/non_sbs_entry.py | Non-SBS |
api/app/schemas/atw_entry.py | ATW |
api/app/schemas/attendance_issue.py | Attendance |
api/app/schemas/productivity_issue.py | Productivity (+ shared date parsers) |
API — domain layout
Scorecard entry logic lives under api/app/domains/scorecard/<type>/ (crud, facet_source, table_rows, table_query, upload/). HTTP routes: api/app/routers/v1/scorecard/. ATW entry requests: api/app/services/atw_entry_requests.py, api/app/routers/v1/atw_entry_requests.py.
| Path | Entity |
|---|---|
api/app/domains/scorecard/atw/ | ATW entries |
api/app/domains/scorecard/sbs/ | SBS |
api/app/domains/scorecard/non_sbs/ | Non-SBS |
api/app/domains/scorecard/attendance/ | Attendance |
api/app/domains/scorecard/productivity/ | Productivity |
api/app/domains/scorecard/additional_points/ | Additional points |
api/app/domains/scorecard/incidents/ | Incidents |
api/app/domains/scorecard/shared/soft_delete.py | Soft delete, restore, purge |
api/app/domains/scorecard/shared/duplicate_entry_check.py | Pre-submit similar-entry check (forms) |
api/app/domains/scorecard/shared/duplicate_entry_upload.py | Batched similar-entry check (upload preview only) |
api/app/domains/scorecard/shared/eval_quota_check.py | Pre-submit SBS / Non-SBS eval quota check |
api/app/domains/scorecard/shared/week_lock.py | Weekly lock resolution + create/edit guards |
api/app/domains/scorecard/shared/entry_excuse.py | Level-5 timely excuse set/clear |
api/app/domains/scorecard/shared/timely_flag_filter.py | List timely_flags parse (late / excused) |
api/app/domains/scorecard/config/weekly_lock_store.py | Global weekly-lock settings document |
api/app/domains/bsc/timely_weekly_submission.py | Bonus timely weekly submission cadence |
api/app/schemas/scorecard_excuse.py | Excuse request body |
api/app/routers/v1/scorecard/weekly_locks.py | Weekly lock status + manual lock/unlock API |
API — calculations
| Path | Purpose |
|---|---|
api/app/services/scorecard_tenure.py | Tenure bands |
api/app/services/sbs_entry_calculated.py | Duration, output, SBS scores, integrity, timeliness |
api/app/services/sbs_entry_goal.py | Goal + quantity_score |
api/app/services/non_sbs_entry_calculated.py | Non-SBS quantity bundle |
api/app/services/scorecard_upload_common.py | Shared bulk-upload parsing and validation |
api/app/services/scorecard_upload_employees.py | Jurisdiction-scoped upload employee lists |
api/app/services/{atw,sbs,non_sbs}_upload_*.py | Per-type template, preview, commit |
api/app/services/scorecard_soft_delete.py | Soft delete, restore, purge helpers |
api/app/services/scorecard_deleted_entries.py | Deleted list, batch restore/purge |
api/app/schemas/scorecard_deleted.py | Recycle bin API models |
api/scripts/purge_soft_deleted_scorecard_entries.py | Cron hard-delete for expired bin rows |
api/app/services/brands_list.py | Global brands config + auto-append on entry save |
API — list / query
| Path | Entity |
|---|---|
api/app/sbs_entry_list.py, sbs_entry_facet_source.py, sbs_entry_table_rows.py, sbs_entries_table_query.py | SBS |
api/app/non_sbs_entry_list.py, non_sbs_entry_facet_source.py, non_sbs_entry_table_rows.py, non_sbs_entries_table_query.py | Non-SBS |
api/app/atw_entry_facet_source.py, atw_entry_table_rows.py, atw_entries_table_query.py | ATW |
api/app/attendance_issue_facet_source.py, attendance_issue_table_rows.py, attendance_issues_table_query.py | Attendance |
api/app/productivity_issue_facet_source.py, productivity_issue_table_rows.py, productivity_issues_table_query.py | Productivity |
api/app/scorecard_date_query.py, scorecard_date_filter.py | Shared date filtering |
api/app/scorecard_table_sort.py | Shared sorting |
API — other
| Path | Purpose |
|---|---|
api/app/audit_log.py | Change logging |
api/scripts/seed_*_from_* | CSV/XLSX seed scripts |
api/scripts/bigquery_scorecard_example.sql | BQ KPI table DDL |
Web — pages
| Path | Page |
|---|---|
web/src/pages/scorecard/ScorecardSbsPage.tsx | SBS |
web/src/pages/scorecard/ScorecardNonSbsPage.tsx | Non-SBS |
web/src/pages/scorecard/ScorecardAttitudeTowardsWorkPage.tsx | ATW |
web/src/pages/scorecard/ScorecardAttendanceIssuesPage.tsx | Attendance |
web/src/pages/scorecard/ScorecardProductivityIssuesPage.tsx | Productivity |
Web — SBS rubric
| Path | Purpose |
|---|---|
web/src/pages/scorecard/sbs_components/sbsScorecardFields.ts | Criteria labels, weights, client score math |
For optional BigQuery dashboard architecture, see BigQuery dashboard.
