Ascendly Scorecard — System Documentation
Status: Eight operational scorecard entry surfaces + configurations (MongoDB). Weekly Kudos recognition cards (separate from scored entries). Bulk Excel upload on ATW, SBS, Non-SBS, Additional Points, and Incidents. Scheduled locking (weekly + monthly; optional; both 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: Weekly Kudos, Fiscal cycles & locks (fiscal cycles, task delivery, finalized-month locks), Scheduled locking, List filters, User-month calculation § Bonus qualification, BigQuery dashboard.
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
- 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. Scheduled locking — weekly + monthly entry locks 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 scored pickers and new entries. Existing dashboard months and scorecard rows stay visible when Scorecard is off. Missing/excess and task delivery stay Scorecard-on only. Weekly Kudos still uses org scope, not BSC. |
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 with Scorecard submit (access level 2+) evaluating employees while observing task execution. Access level 1 can open the page to view entries but cannot submit — + Add entry is hidden. SBS is not a self-eval; you do not file your own SBS.
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).
- Optional attachments on the add/edit form (not bulk upload): up to 5 files (images, PDF, Word
.docx, Excel.xlsx, 5 MB each) stored in S3. Attach after a spreadsheet commit by editing the row. Duplicate does not copy files. The view modal shows a compact icon row (Open photos, Download other files); the table has no Attachments column. - 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.).
Attachments: Same optional files as SBS (form add/edit only; bulk upload is data-only). View modal: compact icon row.
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, or selected rows when any are checked.
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, task count, and optional notes.
- 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).
- Notes are optional manager context (max 500 characters). They do not affect day score or BSC. A small indicator appears on cells with notes; employee detail shows the note text. Bulk Excel upload includes an optional Notes column — blank Notes leave any existing note unchanged on update.
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. - Scheduled Locking — weekly + monthly schedules, page lists, create/edit/delete flags, week ranges, lock clock/timezone,
effective_from(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 scheduled locking is enabled, preview flags / rejects rows whose entry dates fall in a locked week or locked month for that page (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 the month would go over quota or the task date falls in a week that does not need that eval (or already has enough). Not the same as a similar-task duplicate. |
| 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 Scheduled locking (weekly + monthly)
Status: Live (both schedules off by default — enable under Configurations → Scheduled Locking).
Design logs: design-log/2026-07-14-scorecard-weekly-locking.md, design-log/2026-08-15-scorecard-scheduled-locking.md.
See also: Month finalize in Fiscal cycles & locks; bonus gate in User-month calculation § Bonus qualification.
Two independent schedules sit on top of dashboard finalize:
| Schedule | Default pages | When it fires |
|---|---|---|
| Weekly | SBS, Non-SBS, ATW | Configured lock day + clock (defaults 8 / 15 / 22 / 29 at 8:00 PM Asia/Manila) |
| Monthly | Weekly pages plus Attendance, Productivity, Task Delivery, Additional Points, Incidents | Configured day of the following month (default 1st) at the same clock |
Weekly Kudos is off both lists by default; admins can add it. Missing/Excess and Configurations are not lockable. A page may be on both lists.
Admin Finalize Dashboard does not auto-run on the monthly schedule. It remains a separate action (BQ final report + eval ratio). Finalize can still run early. It only hard-locks pages that appear on the weekly or monthly lists. Reopen clears dashboard final only — if the scheduled monthly instant has passed, those pages stay entry-locked unless level 5 unlocks the month.
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 / incident_date / work_date / kudos week date for other kinds) — 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, SBS, and Non-SBS lock the whole week range together. When W4 locks on the 29th, days 22–EOM (including 29–31) are blocked. There is no ATW leeway after the lock day.
Ranges, lock days, clock time, timezone, page lists, and action flags are editable in Configurations → Scheduled Locking. Changing the schedule is live. Nella answers locking questions from that saved schedule, not the default table above.
What a locked period blocks
Effective lock = manual override if set, else now ≥ scheduled lock datetime.
Create / edit / delete are independently configurable per schedule (default: all blocked). Changing the entry date is always blocked while the period is locked, even if Block edit is unchecked.
- Later weeks stay creatable after earlier weeks lock (unless monthly lock has also fired).
- Level 5 bypasses scheduled hard locks the same way as month finalize.
- Enforced on forms, bulk upload, ATW request submit/approve, Task Delivery, and Weekly Kudos when those pages are on a list.
UI: Page 3-dot actions menu → scheduled locks modal (weekly weeks + monthly status; Lock / Unlock for level 5). Forms show a lock banner and disable Submit when the chosen date is blocked. Those banners come from this Configurations schedule. Separate in-app/email “lock approaching” reminders are configured under System → Notifications and fire from the worker when a lock enters Remind hours before lock — see Notifications.
Manual lock / unlock (level 5)
Admins unlock a week or the month so managers can still submit late operational entries. Unlock does not change the scheduled deadline used for bonus — see below. Weekly overrides are blocked while the dashboard month is final (reopen first). Monthly overrides can be set even while finalized.
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, edit, delete, date changes | Score whether submissions met the schedule |
| Unlock week | Lets managers still mutate (per action flags) | Does not grant on-time credit |
| What time is used | Live effective lock (schedule + override) | Scheduled lock only |
| ATW | Locked like SBS/Non-SBS when on the weekly list | 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 a Late entry Excused with a required reason (POST …/excuse) or clear it (DELETE …/excuse) from the table context menu. Excuse late submission is disabled when the entry is not late; already-excused rows can still be cleared. 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 / monthly_effective_from to the first month that should use each schedule before turning Enable on for production months.
API / code: GET|POST /v1/scorecard/weekly-locks (+ /lock, /unlock, /month/lock, /month/unlock); settings on scorecard config; api/app/domains/scorecard/shared/week_lock.py, api/app/domains/bsc/timely_weekly_submission.py. Approaching-lock reminders: scorecard.weekly_lock.approaching and scorecard.monthly_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 is a W1–W4 strip (same colored 4-cell grid as the entry forms). Week labels sit in a second header row. Green = on time, red = short or late, grey = no requirement. Hover a cell for presence / on-time / excused detail.
- 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/export | All table rows matching current filters |
| 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 a late entry excused (body: { reason }; 400 if not late) |
| DELETE | /sbs-entries/{entry_id}/excuse | Level 5 — clear timely excuse |
| POST | /sbs-entries/{entry_id}/attachments | Upload one attachment (multipart file; max 5 per entry, 5 MB) |
| GET | /sbs-entries/{entry_id}/attachments/{attachment_id}/url | Short-lived signed download/view URL |
| DELETE | /sbs-entries/{entry_id}/attachments/{attachment_id} | Remove one attachment |
5.3 Non-SBS entries
Same as SBS except:
- Resync:
POST /non-sbs-entries/resync-tenure(tenure + quantity fields) - Export:
GET /non-sbs-entries/export(current filters) andPOST /non-sbs-entries/export-rows(selected IDs) - 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) - Attachments:
POST/GET url/DELETE /non-sbs-entries/{entry_id}/attachments(same 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 |
| POST | /atw-entries/export-rows | Export table rows by ID list (1–500) |
| 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 — projected monthly count vs quota and W1–W4 cadence (employee_id, entry_type, optional task_evaluated_date, exclude_entry_id, add_count). Date omitted → current month, no week selected. |
| 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.
SBS / Non-SBS forms also render a W1–W4 strip from /eval-quota-check as soon as an employee is selected (task date optional; current month until a date is chosen). Chips show saved counts (same as Missing/Excess); the open form is not included until Save. A week that needs 0, or that already has enough entries, warns in amber; Save still allows “submit anyway.”
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.
5.12 Scheduled locks
| Method | Path | Purpose |
|---|---|---|
| GET | /weekly-locks | Week + month lock status for a calendar month (year, month) |
| POST | /weekly-locks | Save scheduled-lock settings (level 5) |
| POST | /weekly-locks/lock | Level 5: lock a week |
| POST | /weekly-locks/unlock | Level 5: unlock a week (blocked while dashboard month is final) |
| POST | /weekly-locks/month/lock | Level 5: lock the month (allowed while finalized) |
| POST | /weekly-locks/month/unlock | Level 5: unlock the month (allowed while finalized) |
Settings live on the scorecard config document (scorecard-weekly-lock-settings). See §3.13.
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 |
attachments | list | Optional evidence metadata (id, filename, content type, size). Bytes live in S3, not Mongo. Max 5 files, 5 MB each; images, PDF, .docx, .xlsx. Form add/edit only. |
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. Optional attachments match 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/domains/scorecard/shared/tenure.py
- Load
employment_data.hire_datefromtbl_employeesbyemployee_id. - Snap the evaluation date (
task_evaluated_date) to the last calendar day of that month. - Compute whole months from hire date to that month-end (day-aware). Same as-of as the BSC dashboard Tenure chip and eval quotas.
- Map to band:
| Months employed | Tenure label |
|---|---|
| 0–4 | Newbie |
| 5–8 | Advance |
| 9–11 | Intermediate |
| 12+ | Tenured |
- Missing employee or hire date → Newbie
- Month-end of the evaluation month is 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
On ATW, SBS, and Non-SBS, Export is hybrid:
- Nothing selected →
GET .../export?with the same filters and sort as the list (all matching rows, not just the current page). - Rows selected →
POST .../export-rowswith thoseentry_ids(label becomesExport (N)). Use Select all matching to turn the current filter into a selection.
| Pages | Export mechanism |
|---|---|
| ATW, SBS, Non-SBS | Hybrid: filtered GET .../export when idle; POST .../export-rows when rows are selected |
| 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. Entry workbooks start with Entry ID, Submitted Date, Employee, and Department (employee departments, comma-separated if several), then type-specific columns.
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-delivery
/scorecard/missing-excess-evals
/scorecard/configurations
/scorecard/weekly-kudos
/scorecard/weekly-kudos/:kudosIdLayout: 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, Weekly Kudos).
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 | Active + Scorecard-on employees for uploads, task delivery, missing/excess — not entry-list filters |
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, Non-SBS, and ATW share hybrid export (filtered set when idle, selected rows when checked). Incidents follow the attendance/productivity pattern (export-all, recycle bin, upload); they gate bonus qualification only, not KPI math.
12. Known limitations
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, listed scorecard pages lock for that calendar month unless the month is reopened (see Fiscal cycles & locks). Pages not on the weekly or monthly lists stay editable after finalize.
- Scheduled locking — Optional weekly and monthly entry locks; both 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/scorecard_entry_attachment.py | Evidence file metadata |
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/attachments.py | SBS / Non-SBS attachments |
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 + monthly scheduled lock resolution + create/edit/delete 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 scheduled-lock settings document (weekly + monthly) |
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/monthly lock status + L5 lock/unlock API |
API — calculations
| Path | Purpose |
|---|---|
api/app/domains/scorecard/shared/tenure.py | Tenure bands (month-end of evaluation month) |
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.
