# WOW Studio CRM — Requirements

**Status**: Draft v0.2 (Phase 5 review applied; finalising for build)
**Owner**: Alvin
**Last updated**: 2026-09-07 22:42 GMT+8

---

## 1. Purpose

A single centralised system for WOW Studio to manage leads, students, parents,
teachers, timetables, room bookings, packages, invoices, and staff salary across
all branches. Today money tracking lives in Zoho and there is no CRM. This
system replaces the spreadsheet/Zoho workflow with one source of truth.

Approach: design the UI first (clickable prototype), review with branch admins,
then finalise requirements and build.

---

## 2. Business Context

| Item | Decision |
|---|---|
| Business type | Performing arts studio: ballet, classical dance, other dance styles, music (instruments/vocal) |
| Branches | 3 today, must support adding more |
| Country | Malaysia only |
| Timezone | **GMT+8 fixed**. No timezone conversion anywhere. |
| Language | English only (v1). Design so i18n can be added later, but do not build it now. |
| Currency | MYR |
| E-invoice / tax compliance | **Out of scope** for now |
| Existing money tracking | Zoho today. **Start fresh**: no migration, no import. |

---

## 3. Global Rules (STRICT)

These apply to every screen, export, API, log, and stored value.

1. **Date-time format**: `yyyy-MM-dd HH:mm:ss` (24-hour). Example: `2026-09-06 20:31:00`.
2. **Date-only format**: `yyyy-MM-dd`.
3. **Time-only format**: `HH:mm`.
4. **Timezone**: all values are GMT+8. Stored and displayed as GMT+8. No UTC conversion.
5. **Multi-tenancy**: every record carries `hq_id` and `branch_id`. Nothing else is needed for isolation in v1.
6. **Slot-based booking model**: any recurring booking (class, room rental) is expanded into concrete dated slots for the chosen duration at creation time. Used slots are invoiced; unused slots are released on cancellation. See section 6.3.
7. **Salary is rule-driven**: rates are configured per teacher x product x style, never hard-coded. See section 9.
8. **No pay for unpaid students (per-head rates)**: headcount-based salary counts only students whose fee for the period is Paid. Per-class flat rates are paid in full regardless. Every salary sheet must be Confirmed by Branch Admin and Signed off by the Owner before payout. See section 6.8.
9. **One customer, one invoice**: invoices are issued per Customer and may carry many line items. See section 6.7.
10. **Everything is audited**: every edit and every view is written to the audit log with who, when and branch. Edit history is visible in context to all admin roles; the full log (including views) is for Owner and Super Admin only. See section 6.12.

---

## 4. Users, Roles, Portals

Each role has its **own portal with its own login**.

| Role | Portal | Device | Scope |
|---|---|---|---|
| Super Admin | Admin portal | Desktop | System boss. All branches, "All branches" toggle, global settings, user management. Can view everything, including the full audit log. |
| Owner | Admin portal | Desktop | Branch boss. **Complete access** to own branch(es): everything a Branch Admin can do (Fee Run, Salary Run, confirm sheets) plus salary sign-off, term-break dates and the branch audit log. |
| Branch Admin | Admin portal | Desktop | Own branch only. Runs Fee Run and Salary Run, confirms salary sheets. |
| Operator / Front desk | Admin portal | Desktop | Own branch: leads, bookings, attendance on teacher's behalf, invoices, data entry for renter room bookings |
| Teacher | Teacher portal | Phone / tablet | Own classes in each linked branch: today's schedule, attendance, student notes, own salary statement per branch |
| Parent | Parent portal | Phone | Own children: timetable, balance, invoices, make-up booking (**v1.5**) |

Teachers can be linked to more than one branch. Their profile appears in each
linked branch, and their salary is calculated separately per branch. Nothing
mixes across branches.

Outsiders (facility renters) do **not** access the system. An operator takes the
call and enters the booking. A separate public booking portal may be built later
and must not require changes to this system.

---

## 5. Core Entities (glossary)

| Entity | Description |
|---|---|
| HQ | Top-level owner. All records carry `hq_id`. |
| Branch | Physical location. Has rooms, staff, students, its own off-days. |
| Room | Bookable space within a branch (e.g. Studio A, Piano Room 2). Has capacity and hourly rental rate. Bookable in 30-minute slots, minimum 1 hour. |
| Customer | Anyone who can be invoiced: a Parent, an adult Student, or a facility Renter. Registered in CRM with IC number on first contact. Once registered they are a member. |
| Style | Discipline: Ballet, Contemporary, Jazz, Hip-hop, Piano, Violin, Vocal, etc. |
| Course | A teachable programme under a Style with a level (e.g. Ballet Grade 3). Carries the default pay cycle. |
| Product | The sellable unit a class maps to (e.g. "Ballet Grade 1"). Has a pricing model: fixed monthly (syllabus) or per slot (private). Coupons bind to Products. |
| Discount Code | Admin-applied code (percentage or fixed RM) bound to specific Product(s)/class types. Parents never apply it themselves. |
| Class | A recurring group or 1-to-1 lesson: Course + Teacher + Room + weekday + time + duration. |
| Slot | One concrete dated occurrence generated from a Class or Room Booking. Has status. |
| Enrolment | Links a Student to a Class and a Product; owns the slot allocation. |
| Lead | Potential customer (parent or adult student) before enrolment. Becomes a Trial Customer once a trial is booked. |
| Trial | A one-off slot booked for a Lead. Free or paid; a paid trial gets its own invoice. |
| Student | Enrolled learner. May be a child or an adult. A Parent link is optional. |
| Parent | Emergency contact for one or more Students. Not required. A Parent may also be a Customer (the payer) and may have several children. |
| Staff | Teacher, admin, operator, owner. Teachers have salary rules and may be linked to several branches. |
| Room Booking | Rental of a Room by a Customer (renter) or internal use, for a duration. |
| Invoice | Bill for used slots, packages, or room rentals. |
| Payment | Money received against an Invoice. |
| Calendar | Yearly definition of terms, term breaks, public holidays, branch off-days. |
| Notice | A message from branch admin to teachers, read in the Teacher portal. Carries a status (New, Pending, Solving, Settled, Unsettled). |
| Audit log | Immutable record of every edit and every view in the system: who, when, branch, what, before/after. |

---

## 6. Modules

### 6.1 CRM (leads -> trial -> follow-up -> client)

- Lead capture: name, phone, email, child name/age, style of interest, preferred branch, source (walk-in, Facebook, referral, etc.).
- Pipeline stages: New -> Contacted -> Trial Booked -> Trial Attended -> Converted / Lost.
- Trial booking: pick a Class slot or an ad-hoc trial slot; capacity-checked. Trial is **free or paid**; a paid trial issues its own invoice. The lead is recorded in CRM as a **Trial Customer**.
- **Renters live in CRM too**: a facility renter is registered as a Customer with IC details on first contact and is treated as a member from then on.
- Follow-up log: timestamped notes, next-action date, assigned staff.
- Convert to client: one flow that creates Customer, Student, optional Parent (emergency contact), Enrolment, first Invoice.
- Lost reason tracking.

### 6.2 Timetable

- Views: week by Room, week by Teacher, day view, year view.
- Year view shows terms, term breaks, public holidays (Malaysia), branch off-days.
- Class creation: Course, Teacher, Room, weekday, start time, duration, capacity, **booking duration** (e.g. 1 term, 6 months, 1 year, custom date range).
- Conflict detection: room double-booking, teacher double-booking, holiday/off-day collision.
- **Holiday on a class day: shift, never skip.** Admin re-arranges the affected slot to another date in advance. The slot keeps its identity and history; the Calendar shows it as moved.
- **Term breaks are set by the Owner or Super Admin** as date ranges in the yearly calendar (any dates, per branch). No regular class slots are generated inside a term break. Term breaks have no effect on fees; the fixed monthly fee is charged in full.
- **The 5th week (dates 29–31) is the replacement / extras week.** No regular class slots are generated on those dates. Admin books make-up slots, extensions, extra rehearsals and paid trials into them. No effect on fees.
- Class detail: enrolled students, generated slots, attendance per slot.

### 6.3 Slot lifecycle (applies to Classes and Room Bookings)

1. On creation the user chooses a duration. The system generates one Slot per occurrence within that duration, skipping holidays/off-days per Calendar rules (configurable: skip vs. shift). Term breaks and the 5th week (29–31) never receive regular slots.
2. Slot statuses: `scheduled` -> `used` (attended / delivered) | `cancelled` | `released` | `rescheduled` (moved to a new date-time; the new slot links back to the original).
3. On cancellation of the booking, all remaining `scheduled` slots become `released` and the room/teacher time is freed.
4. `used` slots are always invoiced and remain on record. Released slots are never invoiced.
4a. **Per-slot products, chargeable flag.** Each slot has a `chargeable` flag that defaults by rule: cancelled or rescheduled in advance = not chargeable; late cancellation or no-show = chargeable. Admin can flip the flag on any slot with a remark. The Fee Run bills per-slot products on `chargeable` slots, not on attendance alone. "In advance" threshold is a setting (default 24 hours).
5. Slot history is immutable for audit: who created, cancelled, or released, and when.

### 6.4 Room Booking (facility rental)

- Operator enters booking on behalf of a renter (a Customer from CRM) or internal user: room, date/time, duration (single or recurring), rate.
- **Pricing per hour.** Time is divided into 30-minute slots, so a booking may start only on :00 or :30. Minimum booking is 1 hour.
- **Deposit required to lock.** A booking is `tentative` until the deposit is recorded, then `locked`. Deposit amount is set per room (fixed RM or percentage). It is a separate line on the rental invoice.
- **Hold while waiting for the deposit.** When entering a tentative booking, admin can put a **hold** on the slot so nobody else can book it while the renter pays. The hold lasts a configurable time (branch setting, default **25 minutes**); when it expires the booking stays tentative but no longer blocks the slot. Recording the deposit inside the hold locks the booking. A tentative booking without a hold, or with an expired hold, never blocks other bookings.
- **Refundable if cancelled early.** If the renter cancels at least X hours before the start (X is a setting per branch, default 48 hours), the deposit is refunded; otherwise it is forfeited. Refund is recorded as a payment reversal with a remark.
- Availability grid shows idle daytime capacity in 30-minute cells.
- Generates slots per section 6.3 and an Invoice for the rental.
- No public access in v1.

### 6.5 Attendance and make-up

- Teacher marks attendance on phone per slot: present / absent / late.
- **When the teacher may mark**: at any time on the day of the slot — before, during or after the class. Admin re-checks every class after it ends. Saving requires every student in the slot to be marked (no half-marked class); the teacher can correct a saved record the same day and the record keeps who and when.
- **After the end of the slot's day, marking is admin-only** (provisional, see section 10): the teacher's Mark button closes at 23:59 on the slot date; from then on admin or operator marks on the teacher's behalf and the record shows who marked. Slots older than the pending threshold appear on Pending Work as before.
- **Photo attendance (later)**: the teacher takes a photo of the class as proof; the app stamps date-time and attaches it to the record. Not in v1.
- On an absence the teacher ticks "customer informed ≥ 24 h before" and may type the reason. Admin still records proof and awards the credit; the credit is only awarded for a valid reason.
- **Replacement credits**: an absence earns one replacement credit when either:
  - the customer informs at least **24 hours** before the slot start, or
  - the absence is last-minute but has **proof** (accident, sick, medical certificate, severe traffic jam). Admin records the reason and marks "proof received".
  Absences without notice or proof earn nothing.
- Each Customer has a **replacement count** (credits earned minus credits used), visible on the customer profile and Parent portal.
- Credits must be **used within the calendar year** they were earned; remaining credits reset to zero on 1 January. No monthly cap.
- Credits are consumed flexibly: a full replacement slot, or admin may instead **extend a regular class by 30 minutes** and deduct half a credit, so two extensions equal one replacement. Admin chooses at booking time.
- Replacement booking goes into any slot with capacity (admin in v1; parent self-service in v1.5).
- Attendance feeds both headcount-based salary and fee calculation.
- **Pending Work page (attendance deadline, kept simple)**:
  - Any slot whose end time is more than **24 hours** ago and has no attendance marked appears on the branch's Pending Work page.
  - Teacher sees their own pending slots on the Teacher portal home; admin sees all for the branch.
  - Admin or operator can mark attendance on the teacher's behalf; the record shows who marked it.
  - The Salary Run cannot move a teacher's sheet to Confirmed while that teacher has pending slots in the period. The sheet shows a "N slots pending attendance" warning.
  - Nothing else: no auto-fill, no penalties, no escalation.

### 6.6 Products, Pricing, Discounts, Enrolment

**Product** is the sellable unit. Every Course/Class maps to a Product. Two pricing models:

| Pricing model | Example | Fee basis |
|---|---|---|
| **Syllabus (fixed monthly)** | Ballet Grade 1: RM 100/month, Ballet Grade 2: RM 120/month | Flat price per calendar month regardless of slot count |
| **Per slot** | Private piano lesson: RM 80/slot | Price x slots used in the period |

- Product catalogue per branch: name (e.g. "Ballet Grade 1"), Style, pricing model, price, capacity.
- **Discount codes (admin-only)**: parents never apply codes themselves; there is no self-service payment. An admin applies a discount code when creating an invoice. Each code has: code, type (percentage or fixed RM), value, validity dates, optional usage limit, and **binding** to Product(s), class types, or class names. A code bound to Ballet Grade 1 cannot be used on Ballet Grade 2. The invoice shows a discount line with the code. Main use today: **12 months paid in advance gets 10% off** (e.g. code `PREPAY12`). Whether a code is single-use or recurring is set per code.
- **Pro-rated first month (manual)**: when a student joins mid-month, admin creates an **open-amount invoice**: enters the amount by hand, based on the slots remaining that month, with a remark such as "Payment for 2 classes, started week 3". The Fee Run treats this invoice as covering that first month.
- Enrolment: Student x Class x Product, start date, optional end date, pay cycle (inherited from Course, can be overridden), discount code if any.
- Multi-month prepayment: parent may pay several months in one invoice. The invoice records the covered period (From/To). See 6.7.1 for how the Fee Run treats it.
- Freeze / extend / transfer between siblings (HQ admin only).
- Expiry and renewal alerts.

### 6.7 Invoice and Payment

- Invoice types: monthly class fee, per-slot class fee, multi-month prepayment, **open amount** (manual value with mandatory remark, used for pro-rated first month and one-offs), room rental, misc.
- **Issued per Customer**, not per class. One invoice may carry many line items: several children, several classes, a paid trial, a room rental, an open amount.
- **Two identifiers**: a per-branch invoice number shown to customers in the format `{BRANCH}-{YYMM}-{0000}`, e.g. `KL-2609-0001` (branch code, two-digit year, two-digit month, four-digit running number that restarts every month), plus a global running ID unique across all branches for HQ reporting (e.g. `5231`, never resets). Search finds an invoice by either identifier.
- Line items, subtotal, discount, total. No tax handling in v1.
- Payment recording: cash, bank transfer, card, e-wallet; partial payments allowed.
- Status: Draft -> Issued -> Partially Paid -> Paid -> Void.
- Overdue list and simple ageing view.

### 6.7.1 Monthly Fee Run (manual, admin-triggered)

**Fees are billed in advance; salary is paid in arrears.** In the first few
days of a month the admin opens the Fee Run screen, chooses a **From** date
and a **To** date (normally the **current** month, i.e. the month about to be
delivered), and presses **Calculate Fees**. No automatic run in v1; scheduled
auto-run is planned for a later update.

**Pay cycle per course/enrolment.** Each Course (and, if overridden, each
Enrolment) has a configurable pay cycle:

| Pay cycle option | Meaning | Example |
|---|---|---|
| Calendar month (default) | Fee covers the 1st to the last day of the month | Register 2026-09-15, pay a manual open-amount invoice for the rest of Sep, then Oct fee due 2026-10-01 |
| Anchored to registration date | Fee covers one month starting on the registration day | Register 2026-09-15, first fee covers 2026-09-15 to 2026-10-14, next due 2026-10-15 |

First registration: the customer pays on the day they register. The next fee
is due at the start of the next cycle as defined above. The Fee Run reads the
pay cycle to decide which enrolments fall due in the selected From/To window.

Calculation:

1. Find every active Enrolment (student x class x product) within From/To.
2. Compute the **fee due** for each enrolment for that period:
   - Syllabus product: the fixed monthly price. If the enrolment started
     mid-period, the system does not pro-rate; it expects a manual open-amount
     invoice for that month (see 6.6) and matches against it. If none exists,
     the row goes to the Checking Table.
   - Per-slot product: price x `chargeable` slots in the period (see 6.3 rule 4a).
   - Term break weeks do not reduce the fee.
   - Minus any bound coupon discount.
3. Match each fee due against existing Invoices and Payments for the same
   student, class, and period. Matching must handle **early payment**: a
   student may have paid days or weeks before the run, so any Paid or
   Partially Paid invoice whose coverage period overlaps From/To counts.
   **Lump-sum prepayment**: if an invoice covers several months (e.g. Jan-Mar),
   every month inside that coverage is marked **Paid** automatically in each
   monthly run; it does not go to the Checking Table.
4. Result buckets per enrolment:
   - **Paid**: fee due fully covered by matched payments.
   - **Partially paid**: some payment matched, balance outstanding.
   - **Unpaid**: no payment found; a new Issued invoice is generated.
   - **Needs checking**: the system cannot match with confidence (amount
     mismatch, payment without period, payment covering multiple months,
     duplicate candidates). No invoice is auto-generated for these.
5. Fee Run summary shows counts and totals per bucket, per branch.

**Checking Table**: a working list of every "Needs checking" row. Columns:
student, parent, class, package, period, fee due, candidate invoices/payments
found, suggested match, admin action. Admin can:

- Confirm a suggested match (marks Paid).
- Pick a different invoice/payment to match.
- Mark as Unpaid (generates invoice).
- Leave open; it stays in the table until resolved.

Each Fee Run is saved with run timestamp (`yyyy-MM-dd HH:mm:ss`), From, To,
who ran it, and the resulting rows, so a run can be reviewed later. Re-running
the same period must not duplicate invoices; it recalculates and shows diffs.

### 6.8 Salary

- Staff master: role, branch(es), bank details, employment type.
- Salary rule table: **Teacher x Product x Style -> rate**. Rate types:
  - per class (flat per slot delivered)
  - per student headcount (per present student per slot)
  - hybrid (base per class + per head above a threshold)
- **Salary Run (manual, admin-triggered, in arrears)**: in the first few days of a month the admin opens the Salary Run screen, chooses **From** and **To** dates (normally the **previous** month, the one already delivered), and presses **Calculate Salary**. No automatic run in v1; scheduled auto-run is planned for a later update.
- Calculation input: `used` slots in the period, attendance per slot (for headcount rates), and the student's paid Product (rate lookup is Teacher x Product x Style, so the product the student is on determines the rate).
- Output: line-by-line breakdown per teacher (slot date-time, class, product, style, headcount, rate applied, amount), subtotal per teacher, total per branch.
- **Rule is bound at engagement**: when a teacher is assigned to a class, admin picks the rate rule for that engagement (defaulting from the Teacher x Product x Style table). That bound rule is what the Salary Run uses, so changing the table later does not silently change existing classes.
- **Unpaid students, per-head rates**: a teacher on a per-head rate is paid only for students whose fee for that period is **Paid** (per the Fee Run). Students in Unpaid, Partially Paid, or Needs-checking state are listed on the salary sheet but contribute RM 0 and are **flagged**. Admin must check why the student has not paid before the sheet can be confirmed. Once the student pays, a re-run picks up the amount.
- **What the teacher sees about unpaid fees**: on the attendance roster a "fee unpaid / needs checking" marker next to the student; on the statement only the number of excluded students. Never the invoice amount, and never which student on the statement. Admin sees the full detail on the salary sheet. Any change to the fee structure (product price, salary rule, discount code) is audited and shown on the sheet.
- **Unpaid students, per-class flat rate**: the teacher is paid the full flat rate regardless of how many students have paid. Unpaid students are still flagged on the sheet for admin follow-up but do not change the amount.
- **One sheet per teacher per branch.** A teacher linked to two branches gets two sheets, each confirmed and signed off within its own branch.
- **Approval workflow** per teacher salary sheet per run:
  1. `Draft`: generated by Calculate Salary. Editable. Shows flagged unpaid rows and pending-attendance warning.
  2. `Confirmed`: Branch Admin has reviewed every flagged row and confirmed the sheet. Confirming with open flags requires a reason per flag. Blocked while attendance is pending (see 6.5).
  3. `Signed off`: **Owner** (branch boss) approves. Sheet becomes read-only and is the basis for payout. Super Admin can view but sign-off belongs to the Owner.
  4. `Rejected`: Owner sends back to Draft with a comment.
  Each transition records who and when (`yyyy-MM-dd HH:mm:ss`).
- Each Salary Run is saved with run timestamp, From, To, who ran it, and the rows. Re-running the same period recalculates and shows diffs against the previous run; sheets already Signed off are not overwritten, a new version is created for review.
- Adjustments: allowances, deductions, manual overrides with reason (Draft state only).
- Payout summary per branch; export to CSV.
- Teacher can view own statement in Teacher portal.

### 6.9 Settings

- Branches, Rooms, Styles, Courses, Packages.
- **Yearly Calendar, manual per branch.** At the end of each year admin fills in next year's calendar for each branch: which days are public holidays (only the ones the studio observes) and branch off-days. **Term breaks** are date ranges painted by the Owner or Super Admin only. The 5th week (29–31) is marked automatically as the replacement / extras week. No automatic holiday import. Example: in December 2025 admin sets up the 2026 calendar.
- Salary rule table (Teacher x Product x Style defaults).
- Slot chargeable threshold (default 24 hours), attendance pending threshold (default 24 hours), **tentative booking hold** (default 25 minutes).
- Invoice numbering per branch (`{BRANCH}-{YYMM}-{0000}`, monthly restart), plus the global running ID.
- **Teacher contact visibility**: per-teacher setting controlling whether that teacher can see student and parent phone numbers on the Teacher portal. Default off. When off, the teacher still sees the customer's name; only the number is hidden.
- Users, roles, portal access, teacher-to-branch links.

### 6.11 Notices (admin → teachers)

- Branch admin writes notices on an admin **Notices** screen (own module under Operations); teachers read them in the Teacher portal.
- Fields: title, body, audience (all teachers of the branch, or selected teachers), category (Operations, Attendance, Salary, Calendar, Other), and a **status** that admin moves through: `New` → `Pending` → `Solving` → `Settled`, or `Unsettled` (shown in red).
- Teachers see the status badge on each notice; Unsettled and New notices sit at the top of the list and show the unread dot on the bell.
- Delivery is in-app only in v1. Push, WhatsApp and SMS are planned for v1.5 (still open, see section 10).

### 6.12 Audit log

- **Every action is logged**: creates, edits, deletes, status changes, fee-structure changes (product price, salary rule, discount code), runs (Fee Run, Salary Run), sign-offs, **and every view**: which page was opened and which customer, student, invoice or sheet detail was viewed.
- Each entry: timestamp (`yyyy-MM-dd HH:mm:ss`), user, role, branch, action (view / create / edit / delete / status), entity and ID, and for edits the field-level before and after values. Entries are immutable.
- **Who sees what**:
  - **Edit history is visible to every admin role** in context: a **History** tab on Student, Customer, Invoice, Enrolment, Class, Room booking, Product and Salary sheet showing who changed what and when. Operators see it too.
  - **The full log (views and edits) is an Audit Log screen for Owner and Super Admin only.** The Owner sees their own branch(es).
  - **Super Admin is exclusive**: the Owner never sees a Super Admin's view actions. Super Admin edit actions appear like any other edit. Super Admin sees everything, all branches.
- The Audit Log screen filters by date range, user, role, action type, entity and branch, and exports CSV.

### 6.10 Dashboard and Reports

- Per branch and consolidated (HQ).
- Enrolment count, active students, trial conversion rate, revenue, outstanding invoices, teacher utilisation, room utilisation.

---

## 7. Scope by Release

| Release | Modules |
|---|---|
| **v1** | CRM, Timetable, Slot lifecycle, Room Booking (operator entry, deposit hold), Attendance, Packages and Enrolment, Invoice and Payment, Salary, Notices, Audit log, Settings, Dashboard basic, Admin portal, Teacher portal |
| **v1.5** | Parent portal, Notifications (WhatsApp/SMS/email/push), Reports advanced, Waitlist, Photo attendance (date-time stamped proof) |
| **Later** | Public room-booking portal, Student progress reports, Events/recitals, Inventory, i18n, e-invoice, **automatic scheduled Fee Run and Salary Run** |

---

## 8. Non-functional

- Desktop-first admin UI (min 1280px). Teacher portal mobile-first (360px+).
- All date-times rendered per section 3.
- Audit log on every edit and every view, per section 6.12. Field-level before/after on edits. Immutable, exportable.
- Role-based access enforced server-side.
- Data export (CSV) for invoices, payments, salary.

---

## 9. Salary Rule Examples

| Teacher | Product | Style | Rate type | Rate |
|---|---|---|---|---|
| Ms A | Ballet Grade 1 (RM 100/mth) | Ballet | per class | RM 80 |
| Ms A | Ballet Grade 2 (RM 120/mth) | Ballet | per class | RM 90 |
| Ms A | Ballet Private (per slot) | Ballet | per class | RM 120 |
| Mr B | Piano 1-to-1 (per slot) | Piano | per class | RM 60 |
| Ms C | Kids Jazz Group | Jazz | per head | RM 15 |
| Ms C | Kids Jazz Group | Jazz | hybrid | RM 50 base + RM 8/head above 5 |

Worked example, Ms C, Kids Jazz Group, per head RM 15, one slot with 6 students present:
4 students Paid, 1 Unpaid, 1 Needs checking. Salary for that slot = 4 x RM 15 = RM 60.
The 2 excluded students appear as flagged rows on Ms C's sheet. Admin resolves the
flags, confirms the sheet, boss signs off. If the unpaid student pays later, the next
re-run adds RM 15 as a new version for review.

---

## 10. Open Questions

- [x] Make-up credit policy? Answer: replacement credit if informed 24h earlier or last-minute with proof; customer replacement count; use within calendar year; no monthly cap; 30-min extension counts as half. — **Completed 2026-09-07 01:04 GMT+8**
- [x] Holiday on a class day? Answer: shift, re-arranged in advance. — **Completed 2026-09-07 00:36 GMT+8**
- [x] Adults without parent? Answer: allowed. Parent is only an emergency contact, optional, may also be a Customer. — **Completed 2026-09-07 00:49 GMT+8**
- [x] Invoice numbering? Answer: per branch, plus a global running ID. — **Completed 2026-09-07 00:36 GMT+8**
- [x] Teachers see contact details? Answer: depends, per-teacher setting. — **Completed 2026-09-07 00:36 GMT+8**
- [x] Zoho history? Answer: start fresh. — **Completed 2026-09-07 00:36 GMT+8**
- [x] Fees in advance or arrears? Answer: fees in advance, salary in arrears. Pay cycle is configurable per course (calendar month or anchored to registration date). — **Completed 2026-09-07 00:00 GMT+8**

Money model:
- [x] Term break fee? Answer: no effect, term break is the 5th week, full monthly fee. — **Completed 2026-09-07 00:36 GMT+8**
- [x] Holiday on class day? Answer: shift. — **Completed 2026-09-07 00:36 GMT+8**
- [x] Per-slot charge basis? Answer: chargeable flag, advance cancel free, late counts, admin editable. — **Completed 2026-09-07 00:36 GMT+8**
- [x] Invoice per class or per parent? Answer: per customer, multiple line items. — **Completed 2026-09-07 00:36 GMT+8**
- [x] Trial free or paid? Answer: both; paid gets separate invoice; recorded as trial customer. — **Completed 2026-09-07 00:36 GMT+8**

Salary edge cases:
- [x] Per-class flat rate with unpaid students? Answer: pay full; rule bound at teacher engagement. — **Completed 2026-09-07 00:36 GMT+8**
- [x] Who signs off? Answer: Super Admin is system boss, Owner is branch boss and signs off. — **Completed 2026-09-07 00:36 GMT+8**
- [x] Multi-branch teacher? Answer: one sheet per branch, teacher linked to multiple branches. — **Completed 2026-09-07 00:36 GMT+8**
- [x] Attendance cutoff? Answer: 24 hours, Pending Work page, admin can mark on behalf, blocks Confirm. — **Completed 2026-09-07 00:36 GMT+8**

Missing entities and settings:
- [x] Renters? Answer: stored in CRM as Customer with IC, become members. — **Completed 2026-09-07 00:36 GMT+8**
- [x] Room rental pricing? Answer: per hour, 30-minute slots, start on :00 or :30, minimum 1 hour. — **Completed 2026-09-07 00:36 GMT+8**
- [x] Public holidays? Answer: manual yearly setup per branch, only observed holidays, no auto import. — **Completed 2026-09-07 00:36 GMT+8**
- [x] Room rental deposit? Answer: required to lock the booking; refundable if cancelled X hours before start (setting, default 48h). — **Completed 2026-09-07 00:49 GMT+8**
- [x] Fee Run: what is the "fee due" for a month? Answer: syllabus products are fixed monthly (Ballet Grade 1 RM 100/mth), private classes are per slot. Coupons bind to products. — **Completed 2026-09-06 20:43 GMT+8**
- [x] Fee Run: lump-sum multi-month payment? Answer: every covered month is marked Paid automatically. — **Completed 2026-09-06 20:43 GMT+8**
- [x] Salary Run: teacher paid for unpaid student? Answer: no. Sheet must be Confirmed by admin then Signed off by boss; unpaid students are flagged for admin to investigate. — **Completed 2026-09-06 20:43 GMT+8**
- [x] Fee Run: pro-rating rule? Answer: per slot, but manual. Admin creates an open-amount invoice with a remark like "payment for 2 classes, started week 3". — **Completed 2026-09-06 23:47 GMT+8**
- [x] Coupons: single-use or recurring? Answer: admin-only discount codes, no parent self-service. Recurrence set per code. Main case is 12 months prepaid gets 10% off. — **Completed 2026-09-06 23:47 GMT+8**

Phase 5 review (2026-09-07, Alvin standing in for the branch admins; full table in section 11):
- [x] A1 Owner access? Answer: Owner has complete access for the branch, runs Fee Run and Salary Run, confirms and signs off. — **Completed 2026-09-07 22:42 GMT+8**
- [x] A2 Term break? Answer: set by Owner / Super Admin as dates, not tied to the 5th week. Dates 29–31 are the replacement / extras week, booked by admin. — **Completed 2026-09-07 22:42 GMT+8**
- [x] A5 Tentative booking blocks? Answer: admin can hold the slot for a configurable time (default 25 minutes) until the deposit arrives. — **Completed 2026-09-07 22:42 GMT+8**
- [x] Q1 When may a teacher mark? Answer: any time on the day; admin re-checks after every class. Photo attendance later. — **Completed 2026-09-07 22:42 GMT+8**
- [x] Q3 Invoice number format? Answer: `KL-2609-0001` (branch, YYMM, 4-digit monthly counter); global ID stays; search by either. — **Completed 2026-09-07 22:42 GMT+8**
- [x] Q4 Notices screen? Answer: yes, admin Notices module with statuses New / Pending / Solving / Settled / Unsettled (red). — **Completed 2026-09-07 22:42 GMT+8**
- [x] Q6 Hide customer name from teachers? Answer: no; only the phone number is hidden. — **Completed 2026-09-07 22:42 GMT+8**
- [x] Audit log? Answer: every edit and every view logged; edit history visible to all admin roles in context; full log for Owner and Super Admin; Owner never sees Super Admin view actions. — **Completed 2026-09-07 22:42 GMT+8**
- [ ] Q2 Teacher marking after the day ends: admin-only after 23:59 on the slot date is the working rule, **provisional** pending final decision.
- [ ] A11 Notice delivery beyond in-app (push / WhatsApp / SMS): left open.
- [ ] Q5 Owner sign-off from a phone: left open. Owner portal is desktop-only in the prototype.
- [ ] A2 follow-up: a term break that is longer than the 5th week (e.g. two weeks in June) still charges the full monthly fee? Assumed yes; confirm.

---

## 11. UI Design Plan

- [x] Phase 1: Glossary, role/navigation map, design tokens and component set — **Completed 2026-09-07 01:46 GMT+8**
- [x] Phase 2: Prototype: Dashboard, CRM, Timetable (week/year), Room Booking — **Completed 2026-09-07 11:55 GMT+8**
- [x] Phase 3: Prototype: Packages/Enrolment, Invoice/Payment, Salary, Settings — **Completed 2026-09-07 11:55 GMT+8**
- [x] Phase 4: Teacher portal (mobile) screens — **Completed 2026-09-07 17:58 GMT+8**
- [x] Phase 5: Review with branch admins, update this document, finalise for build — **Completed 2026-09-07 22:42 GMT+8**
- [ ] Phase 6: Build brief (data model from the mock data, screen-to-section traceability) and handover to the build team

Prototype format: static HTML pages with mock data, linked as a clickable flow.

Phase 1 output lives in `prototype/`: `index.html` (hub), `glossary.html`,
`navigation.html` (portals, sitemaps, role x screen matrix, branch scope rules,
key flows), `design-system.html` (tokens and every component rendered live),
`shell.html` (admin app-shell template for Phase 2/3 screens). Shared assets:
`assets/tokens.css`, `assets/components.css`, `assets/app.js` (date/time/money
formatters, icon sprite, tabs/modal/toast behaviours). Serve with
`python -m http.server 8765 --directory prototype` and open
`http://localhost:8765/`.

Phase 2 and 3 output (31 admin screens, all in `prototype/`, linked from the
hub and from each other; mock data is Kuala Lumpur, week of 2026-09-07):

| Module | Pages |
|---|---|
| Dashboard | `dashboard.html` |
| CRM | `crm-leads.html` (board + list, new lead), `crm-lead-detail.html` (follow-up log, book trial, mark lost, convert wizard), `crm-customers.html`, `crm-customer-detail.html` (students, invoices, payments, replacement credits, make-up booking), `crm-students.html` (student drawer), `crm-parents.html` |
| Timetable | `timetable.html` (week by room, week by teacher, day; slot detail drawer with chargeable flag, reschedule, history), `timetable-year.html`, `classes.html` (new class with conflict check and booking duration), `class-detail.html` (students, slots, attendance on behalf, cancel class) |
| Room Booking | `room-availability.html` (30-min grid, idle capacity, new booking with deposit preview), `room-bookings.html`, `room-booking-detail.html` (deposit, slots, invoice, refund window) |
| Attendance | `pending-work.html`, `replacement-credits.html` (proof review) |
| Products & Enrolment | `products.html`, `discount-codes.html`, `enrolments.html` (freeze / extend / transfer, renewal alerts) |
| Billing | `invoices.html`, `invoice-detail.html` (record payment, void), `invoice-new.html` (multi-line, open amount with remark, discount code binding), `payments.html`, `overdue.html` (ageing), `fee-run.html` (From/To, Calculate Fees, buckets, Checking Table, past runs) |
| Salary | `salary-run.html`, `salary-sheets.html`, `salary-sheet-detail.html` (line-by-line, flagged unpaid students, pending-attendance block, Draft / Confirmed / Signed off / Rejected by role, adjustments), `salary-rules.html`, `payout-summary.html` |
| Settings | `settings.html` (branches and rooms, yearly calendar editor, thresholds, invoice numbering, styles and courses, users and teacher-branch links) |

Shared Phase 2/3 assets: `assets/screens.css` (screen layouts), `assets/data.js`
(one mock dataset plus renderers for week, day, availability and year grids),
`assets/shell.js` (renders sidebar and top bar from one nav definition; the
user menu switches the viewing role and the branch switcher sets the branch,
both kept in localStorage so role-gated buttons such as Owner sign-off can be
demonstrated).

Phase 5 additions (admin, in `prototype/`): `notices.html` (Notices module:
list by status, new notice drawer with audience and category, status changes)
and `audit-log.html` (Owner / Super Admin only: every view and edit with
filters; Super Admin view rows are hidden when viewing as Owner). History tabs
on `crm-customer-detail.html` and the student drawer in `crm-students.html`.

Phase 4 output (teacher portal, mobile-first 360px+, all in `prototype/`,
linked from the hub, the navigation map and the design-system mobile shell):

| Tab | Pages |
|---|---|
| Home | `t-home.html` (pending attendance older than 24 h at the top, today's classes with Mark once ended, next 7 days, salary snapshot, notices), `t-notices.html` |
| Schedule | `t-schedule.html` (week list, own classes only, holidays / term break / moved slots), `t-class.html?slot=<class>\|<date>` (Present / Late / Absent per student, "informed ≥ 24 h" and reason on absences, save stamped with who and when, marked-on-behalf shown, per-student notes, slot info and history; trials show Attended / No-show) |
| Students | `t-students.html` (search, filter by class, phone numbers only when the per-teacher setting is on), `t-student.html?id=<student>` (classes, replacement credits, masked contact, last 8 slots, notes) |
| My Salary | `t-salary.html` (statement per branch per period with status), `t-statement.html?period=yyyy-MM` (read-only mirror of the admin sheet: Draft / Confirmed / Signed off with who and when, lines by class with paid headcount and bound rate, excluded unpaid students, pending-attendance block, adjustments, history) |

Shared Phase 4 assets: `assets/teacher.css` (mobile layouts; on screens 900px
and wider the app sits in a phone frame beside a prototype-controls panel) and
`assets/teacher.js` (renders top bar, tab bar, branch and profile sheets from
one definition; teacher-scoped views of the same mock data; attendance and
notes kept in localStorage so a slot marked on the phone shows as marked on
the admin side of the demo). Default login is Ms Chloe Wong (Kuala Lumpur);
the side panel switches teacher, branch and the contact-number setting. The
prototype clock is Monday 2026-09-07 12:40, just after her Kids Jazz Group
class. Ms Chloe Wong's 2026-08 statement carries the same figures as the
admin `salary-sheet-detail.html` (RM 1,335.00 Draft, 2 flagged students,
1 slot pending attendance).

Phase 5 review outcome (2026-09-07 22:42 GMT+8, Alvin standing in for the
branch admins; the same table is shown on `prototype/review.html`):

| # | Item | Decision | Note |
|---|---|---|---|
| A1 | Owner views Fee Run / Salary Run but does not run them | Change | Owner has complete access for the branch. §4, navigation matrix, `fee-run.html`, `salary-run.html`, `salary-sheet-detail.html` updated. |
| A2 | Term break = dates 29–31, no slots, fee unchanged | Change | Term break is set by Owner / Super Admin as dates. 29–31 is the replacement / extras week, booked by admin. §6.2, §6.9, year view, calendar editor updated. |
| A3 | Attendance is its own admin menu item | Confirmed | Critical; keep as a separate menu item. |
| A4 | Extras on the salary sheet at the bound rate, extension half rate | Confirmed | |
| A5 | Tentative booking does not block | Change | Admin can hold the slot for a configurable time (default 25 minutes) until the deposit is received. §6.4, §6.9, booking screens updated. |
| A6 | Save only when every student is marked; editable after, keeps who / when | Confirmed | Must tick all to confirm. Same-day edits only, see Q2. |
| A7 | Teacher ticks "informed ≥ 24 h" and reason; admin records proof, awards credit | Confirmed | Credit only for valid reasons. |
| A8 | Teacher sees unpaid marker on roster and excluded count on statement, never amounts or names on the statement | Confirmed | Admin sees the details on the salary sheet. Fee-structure changes are logged. |
| A9 | Teacher notes visible to admin, never to parents | Confirmed | |
| A10 | Teachers cannot cancel, reschedule or flip the chargeable flag | Confirmed | |
| A11 | Notices in-app; push / WhatsApp / SMS are v1.5 | Open | Left open. |
| Q1 | Teacher marks attendance only after the class ends | Change | Any time on the day; admin re-checks after every class. Photo attendance with date-time stamp later. §6.5, teacher portal updated. |
| Q2 | Teacher may still mark after the 24 h threshold | Change (provisional) | Admin-only after the end of the slot's day. Final decision pending. |
| Q3 | Invoice number `KL-2026-000118` + global ID | Change | `KL-2609-0001`: branch, YYMM, 4-digit monthly counter. Global ID kept. Search by invoice number. §6.7 and every screen updated. |
| Q4 | No admin screen for notices | Change | New Notices module with statuses New / Pending / Solving / Settled / Unsettled (red). §6.11, `notices.html`, teacher notices updated. |
| Q5 | Owner signs off from a phone? | Open | Owner portal stays desktop-only for now. |
| Q6 | Hide customer name too when contact visibility is off? | Change → current behaviour | Name stays visible; phone must be hidden. §6.9 wording updated. |
| — | Audit log (new) | Added | §3 rule 10, §6.12, `audit-log.html`, History tabs. |

---

## Change Log

| Timestamp | Change |
|---|---|
| 2026-09-07 22:42 GMT+8 | Phase 5 review applied (Alvin standing in for the branch admins). Owner now has complete branch access (runs Fee Run and Salary Run, confirms and signs off). Term breaks are Owner / Super Admin dates; dates 29–31 become the replacement / extras week. Tentative room bookings can hold the slot for a configurable time (default 25 min). Teachers mark attendance any time on the day, admin-only after the day ends (provisional); photo attendance moved to v1.5. Invoice numbers become `{BRANCH}-{YYMM}-{0000}` with monthly restart. New Notices module (6.11) with New / Pending / Solving / Settled / Unsettled statuses. New Audit log (6.12, global rule 10): every edit and view logged, History tabs for all admin roles, full log for Owner and Super Admin, Super Admin views hidden from Owner. Contact visibility wording: name shown, number hidden. Section 10 records the 8 closed and 4 open Phase 5 items; section 11 replaces the assumption lists with the decision table and adds Phase 6 (build brief). Prototype updated to match: `notices.html`, `audit-log.html`, History tabs, year view and calendar editor, booking hold, teacher portal timing, invoice numbers on 22 screens, review pack shows the outcome. Also fixed a prototype bug: percentage room deposits rendered as RM NaN on the booking screens. |
| 2026-09-07 21:26 GMT+8 | Phase 5 preparation. One student table in `assets/data.js` (`DATA.students`, `DATA.rosterOf`) now feeds the admin class detail and the teacher portal, so the named students on CRM, Fee Run, credits and salary screens (Tan Jia Yi unpaid in Kids Jazz Group, Hannah Wong needs checking in Contemporary Teens, and so on) appear on the same rosters everywhere. Added `prototype/review.html`: walkthrough script per module and a decision checklist covering the 11 build assumptions and 6 new questions, with Markdown export for this document. |
| 2026-09-07 17:58 GMT+8 | UI Design Phase 4 delivered in `prototype/`: teacher portal, 8 mobile screens (Home with pending attendance and today's classes, Notices, Schedule week list, Class attendance with Present / Late / Absent, informed-in-advance flag, notes, slot history, My students with per-teacher contact visibility, Student detail, My Salary per branch, read-only Statement mirroring the admin sheet) on a shared mobile shell (`assets/teacher.css`, `assets/teacher.js`) with branch switching for multi-branch teachers and a prototype-controls panel on wide screens. Hub, navigation map and design system link to the screens. Section 11 updated with the file map and six Phase 4 assumptions for Phase 5 review. |
| 2026-09-07 11:55 GMT+8 | UI Design Phases 2 and 3 delivered in `prototype/`: 31 clickable admin screens (dashboard; CRM leads board, lead detail with trial booking and convert wizard, customers, students, parents; timetable week/day/year with slot detail, classes and class detail; room availability grid, bookings and booking detail with deposit lock; pending work and replacement credits; products, discount codes, enrolments; invoices, invoice detail, new invoice with open amount, payments, overdue ageing, Fee Run with Checking Table; Salary Run, sheets, sheet detail with approval workflow, salary rules, payout summary; settings with yearly calendar editor). Shared `screens.css`, `data.js`, `shell.js` with role and branch switching. Section 11 updated with page map and five assumptions for Phase 5. |
| 2026-09-06 20:31 GMT+8 | Initial draft from planning session. Captured business context, strict date/time rules, roles and portals, slot lifecycle model, salary rule model, scope by release. |
| 2026-09-06 20:39 GMT+8 | Added Monthly Fee Run (manual From/To, early-payment matching, Checking Table) as 6.7.1. Salary Run made manual From/To with saved runs. Auto-run moved to Later. Three new open questions on fee due basis, lump-sum packages, and unpaid-student salary. |
| 2026-09-06 20:43 GMT+8 | Replaced Package with Product (fixed monthly syllabus vs per-slot pricing). Added bound discount coupons. Lump-sum prepayment marks all covered months Paid. Salary excludes unpaid students, flags them, and adds Draft -> Confirmed -> Signed off -> Rejected workflow. Global rule 8 added. Closed 3 open questions, added 2 (pro-rating, coupon recurrence). |
| 2026-09-06 23:47 GMT+8 | Pro-rating is manual: open-amount invoice with remark, Fee Run matches against it. Coupons renamed Discount Codes, admin-only, recurrence per code, PREPAY12 10% example. Added open-amount invoice type. Closed the 2 remaining fee questions. |
| 2026-09-07 00:00 GMT+8 | Fees billed in advance (Fee Run targets current month), salary in arrears (Salary Run targets previous month). Added configurable pay cycle per Course with Enrolment override: calendar month or anchored to registration date. Added 12 open questions from review: money model, salary edge cases, missing entities. |
| 2026-09-07 01:46 GMT+8 | UI Design Phase 1 delivered in `prototype/`: glossary with on-screen terms and status vocabularies, role/navigation map (3 portals, admin and teacher sitemaps, role x screen matrix, branch scope rules, key flows), design tokens (plum primary, gold accent, semantic status colour map, type/spacing/radius scale, strict date/time/money formats) and component set (shell, buttons, forms, badges for every status, stat tiles, tables, tabs, alerts, toasts, modal, drawer, timeline, stepper, timetable grid, kanban, mobile teacher shell). Section 11 updated with file map and serve command. |
| 2026-09-07 01:04 GMT+8 | Replacement credit policy defined: earned on 24h notice or last-minute with proof, per-customer replacement count, expires end of calendar year, no monthly cap, 30-min extension = half credit. All open questions now closed. |
| 2026-09-07 00:49 GMT+8 | Parent made optional emergency contact, may also be Customer. Adults allowed without parent. Room rental deposit required to lock booking, refundable if cancelled X hours early (default 48h). Closed 2 open questions. |
| 2026-09-07 00:36 GMT+8 | Roles: Super Admin (system boss) and Owner (branch boss, signs off salary) added; teachers link to multiple branches with one salary sheet per branch. Salary rule bound at engagement; per-class flat rate paid in full. Slots: shift on holiday, `rescheduled` status, `chargeable` flag for per-slot products. Term break = 5th week, no fee effect. Invoice per Customer with multi-line items, per-branch number plus global ID. Customer entity added (parents, adults, renters with IC). Trials free or paid. Room rental per hour in 30-min slots, min 1 hour. Manual yearly calendar per branch. Pending Work page for attendance older than 24h. Teacher contact visibility per-teacher setting. Zoho: start fresh. Closed 15 open questions, added 1 (rental deposit). |
