-- Support article deploy script
-- Generated: 2026-05-28 15:32:22
-- Source: local kiosk_buykiosk DB + repo markdown drafts
-- Target: production kiosk_buykiosk DB
--
-- Scope: 2 articles in the Scheduling / Team Management categories
--   - timesheets (Scheduling): adds Pay Period View + Custom Export modal docs
--   - timesheets-time-tracking (Team Management): same two new features
--
-- Idempotent: safe to re-run. All matching is by slug, not by ID.
-- Wrapped in a transaction; existing prod state (status, view_count,
-- author_id, is_featured) is preserved on ON DUPLICATE KEY UPDATE.
--
-- Recommended invocation:
--   mysql --default-character-set=utf8mb4 -h <prod-host> -u <user> -p kiosk_buykiosk \
--     < deploy-support-articles-2026-05-28.sql
--
-- Image files referenced by these articles must be deployed FIRST:
--   public_html/images/support/articles/timesheets_period_toggle.png
--   public_html/images/support/articles/timesheets_custom_export_modal.png
--   (plus the existing schedule_timesheets_overview.png already on prod)

START TRANSACTION;

-- ---- Article #81: Timesheets (slug=timesheets, status=published, category=scheduling, 11799 bytes) ----
INSERT INTO support_articles
    (category_id, title, slug, content_md, content_html, author_id,
     status, view_count, is_featured, created_at, updated_at)
VALUES (
    (SELECT id FROM support_categories WHERE slug = 'scheduling' LIMIT 1),
    'Timesheets',
    'timesheets',
    'The Timesheets page lets you review, approve, and manage employee time records. Track scheduled vs. actual hours, identify overtime, edit individual punches, and export payroll data — everything you need for accurate time tracking.\n\n![Timesheets Overview](/images/support/articles/schedule_timesheets_overview.png)\n\n*The Timesheets dashboard showing the weekly summary with employee cards and approval controls.*\n\n---\n\n## Getting There\n\nNavigate to **Scheduling** > **Timesheets** from the admin sidebar, or click the `Timesheets` link at the top of the Schedule Calendar page.\n\n---\n\n## Interface Layout\n\n| Area | Purpose |\n|------|---------|\n| **Week Navigation** | Browse between weeks, jump to today |\n| **Pay Period Toggle** | Switch between `1 Week` and `2 Weeks` (pay-period) views |\n| **Summary Cards** | At-a-glance stats: employees, total hours, regular, overtime, double time, total pay |\n| **Filter Tabs** | All, Pending, Approved, Overtime |\n| **Employee Table** | Each row shows one employee\'s timesheet for the selected period |\n| **Export Buttons** | `Export CSV` (current view) and `Custom Export…` (any range) |\n\n---\n\n## Weekly Summary\n\nThe top of the page displays key metrics for the selected period:\n\n| Metric | Description |\n|--------|-------------|\n| **Employees** | Number of employees with time entries |\n| **Total Hours** | Combined hours across all employees (shown as `2-Week Total Hours` in 2-week view) |\n| **Regular** | Standard (non-overtime) hours |\n| **Overtime** | Hours exceeding overtime thresholds |\n| **Double Time** | Hours qualifying for double-time pay (state-specific) |\n| **Total Pay** | Estimated payroll cost for the period |\n\n> **Tip:** In 2-week view, totals display with two decimals so the KPI numbers line up exactly with the per-row `Wk 1` / `Wk 2` / `Total` columns. The 1-week view uses one decimal for a cleaner read.\n\n---\n\n## Pay Period View (1 Week vs. 2 Weeks)\n\nUse the `1 Week` / `2 Weeks` toggle to the right of the date navigation to switch between a single-week view and a two-week pay-period view. Both views read the same underlying time data — only the aggregation and column layout change.\n\n![Pay Period Toggle](/images/support/articles/timesheets_period_toggle.png)\n\n*The header in 2-week mode shows \"Pay Period of {date}\" with the full 14-day range and the `2 Weeks` button active.*\n\n### When to use each view\n\n| View | Best for |\n|------|----------|\n| **1 Week** | Day-to-day review, approving timesheets, editing punches, comparing scheduled vs. actual |\n| **2 Weeks** | Running a biweekly pay period, exporting payroll, seeing Week 1 and Week 2 side by side |\n\n### What changes in 2-week view\n\n- **Header** reads `Pay Period of {Mon Date, Year}` with the full 14-day range underneath.\n- **Navigation arrows** still step one week at a time, so you can slide the period start forward or back without skipping a pay cycle.\n- **Employee table columns** become: `Employee`, `Wk 1 Hours`, `Wk 2 Hours`, `Total Hours`, `OT`, `Total Pay`, `Status`. The `Scheduled`, `Actual`, `Variance`, `Break`, and `Actions` columns are hidden — those are only meaningful for a single week.\n- **Status badge** reflects both weeks: `Approved` only shows when both weeks are approved; otherwise you\'ll see `{N} Pending` (where N is 1 or 2), or `Exported` once both weeks have been marked exported.\n- **Approve All** is hidden — approval still happens one week at a time from the 1-week view (or from the employee detail page).\n- **Export CSV** is enabled whenever there is any data in the period (you don\'t need to approve first to pull payroll totals).\n\n> **Tip:** Use 2-week view to quickly spot employees whose hours look uneven across the pay period — for example, 38 hours in Week 1 and 6 hours in Week 2 is often a missed-punch warning.\n\n---\n\n## Filtering Timesheets\n\nUse the tab filters to quickly find what you need:\n\n| Tab | Shows |\n|-----|-------|\n| **All** | Every employee with time entries |\n| **Pending** | Timesheets awaiting approval |\n| **Approved** | Already-approved timesheets |\n| **Overtime** | Employees with overtime hours |\n\n---\n\n## Employee Timesheet Table\n\nThe columns vary depending on the pay-period view:\n\n### 1-Week view\n\n| Column | Description |\n|--------|-------------|\n| **Employee** | Name and position |\n| **Scheduled** | Hours they were scheduled to work |\n| **Actual** | Hours they actually worked (from clock punches) |\n| **Variance** | Difference between scheduled and actual (positive = worked more) |\n| **Break** | Unpaid break hours deducted from the total |\n| **OT** | Overtime hours for the week |\n| **Pay** | Estimated pay for the week |\n| **Status** | Pending, Approved, or Locked |\n| **Actions** | Approve, view detail, or unlock |\n\n### 2-Week view\n\n| Column | Description |\n|--------|-------------|\n| **Employee** | Name and position |\n| **Wk 1 Hours** | Net payroll hours for the first week of the period |\n| **Wk 2 Hours** | Net payroll hours for the second week |\n| **Total Hours** | Wk 1 + Wk 2 (net payroll hours, not gross) |\n| **OT** | Overtime hours across the full period |\n| **Total Pay** | Estimated pay for both weeks combined |\n| **Status** | `{N} Pending`, `Approved`, or `Exported` (reflects both weeks) |\n\n---\n\n## Approving Timesheets\n\n### Approve Individual\n\nClick the approve button on a specific employee\'s row to approve their timesheet for the week.\n\n### Approve All\n\nClick `Approve All` in the toolbar to approve all pending timesheets at once. This button is only active when there are pending timesheets.\n\n### Unlock an Approved Timesheet\n\nIf you need to make changes after approval, click the unlock button to revert the timesheet to pending status. This allows further edits before re-approving.\n\n> **Warning:** Unlocking a timesheet after payroll export may cause discrepancies. Always coordinate with your payroll team.\n\n---\n\n## Viewing Timesheet Detail\n\nClick an employee\'s name or the detail button to open their full timesheet. The detail view shows:\n\n- **Daily breakdown** — Each day with clock-in, clock-out, and break times\n- **Individual punches** — Every clock event with timestamps\n- **Scheduled vs. actual** comparison for each day\n- **Notes and overrides** — Any manager notes or manual adjustments\n\n---\n\n## Editing Time Punches\n\nFrom the timesheet detail view, you can:\n\n### Edit a Punch\n\n1. Click the edit icon next to a punch entry\n2. Adjust the time\n3. Add a note explaining the change (required for audit trail)\n4. Click `Save`\n\n### Add a Manual Punch\n\n1. Click `Add Punch`\n2. Select the punch type (Clock In, Clock Out, Break Start, Break End)\n3. Enter the time\n4. Add a note explaining why the manual punch is needed\n5. Click `Save`\n\n### Delete a Punch\n\n1. Click the delete icon next to a punch entry\n2. Confirm the deletion\n3. Add a note explaining why\n\n> **Tip:** All punch edits are recorded in the Audit History for accountability.\n\n---\n\n## Exporting Timesheets\n\nTimesheets has two export buttons in the toolbar:\n\n| Button | Use it when… |\n|--------|--------------|\n| **Export CSV** | You want a one-click export of whatever is currently on screen (the visible 1-week or 2-week period). |\n| **Custom Export…** | You need to pick a specific date range, override the default approval/export rules, or roll several weeks into a single payroll file. |\n\n### Quick Export (current view)\n\nClick `Export CSV` to download the visible period as a CSV file. Behavior depends on the current view:\n\n| View | What it exports | When it\'s enabled | Side effect |\n|------|-----------------|-------------------|-------------|\n| **1 Week** | The current week, **approved timesheets only** | Once at least one timesheet is approved | Approved timesheets are flagged as **Exported** |\n| **2 Weeks** | The full 14-day pay period, **all employees** (approved + pending) | Whenever there is any data in the period | None — does **not** mark anything as exported |\n\nThe CSV includes:\n\n- Employee name and position\n- Daily hours breakdown (per week in 2-week mode)\n- Regular, overtime, and double-time hours\n- Total pay calculations\n\n### Custom Export…\n\nClick `Custom Export…` (sliders icon) to open the export-range modal. This gives you precise control over the range, which timesheets are included, and whether they get marked as exported afterwards.\n\n![Custom Export Modal](/images/support/articles/timesheets_custom_export_modal.png)\n\n*The Custom Export modal with quick-range buttons, free-form date pickers, a live range preview, and the include/mark-as-exported checkboxes.*\n\n#### Quick range buttons\n\n| Preset | Effect |\n|--------|--------|\n| **Current view** | Matches the currently visible period (1 or 2 weeks, starting on the current week) |\n| **1 week** | Sets the range to 1 week, starting on the same week as the current `Start week` input |\n| **2 weeks** | Sets the range to 2 weeks (standard biweekly pay period) |\n| **4 weeks** | Sets the range to 4 weeks (monthly pay cycle) |\n\n#### Start / End week pickers\n\nPick **any date** inside the desired start week and any date inside the desired end week — you don\'t have to land exactly on the period start.\n\n- The text below each picker resolves your choice to a real week:\n  - `Week of {Mon Date}` for the start\n  - `Week ending {Sun Date}` for the end\n- The blue info box updates live as you type:\n  - `{N} weeks selected: {Start Mon} – {End Sun}`\n\n#### Validation rules\n\n- **End must be on or after start** — otherwise the preview turns red with `End week must be on or after the start week.`\n- **Maximum 26 weeks** per export — if you pick a wider range, you\'ll see `{N} weeks selected — exceeds the 26-week maximum.` and the download is blocked.\n\n#### Options\n\n| Option | Default | What it does |\n|--------|---------|--------------|\n| **Include unapproved timesheets** | On | When checked, the export contains every employee with hours in the range (approved + pending). Uncheck to export only approved timesheets — useful when you want a clean payroll file and pending rows should be excluded. |\n| **Mark approved timesheets as exported after download** | Off | When checked, every approved timesheet in the range is flagged `Exported` after a successful download. The next custom export of the same range will still pull the same data, but the badge on the dashboard reflects that the row has been pushed to payroll. Leave it off for ad-hoc reports you don\'t want to affect the export bookkeeping. |\n\n#### Confirming the export\n\nClick `Download CSV` to run the export. On success:\n\n1. The CSV downloads to your browser.\n2. If `Mark approved timesheets as exported after download` was checked, matching rows update to the **Exported** status.\n3. The modal closes automatically.\n\n> **Tip:** A common payroll workflow is: switch to **2 Weeks** view, navigate to the pay period that just closed, approve any pending rows, open **Custom Export…**, click the **Current view** preset, leave both checkboxes at their defaults (or check `Mark approved as exported` if you\'re done), and click **Download CSV**.\n\n> **Tip:** Use the CSV export for importing into your payroll system (QuickBooks, ADP, etc.).\n\n---\n\n## Troubleshooting\n\n### Missing Time Entries\n\n- Check if the employee clocked in using the correct store\n- Verify their clock PIN is set up in Team Members\n- Check the Audit History for any deleted punches\n\n### Incorrect Hours\n\n- Review individual punches for missed clock-outs (the system may have auto-clocked them out)\n- Check for overlapping punches from breaks\n- Use the edit function to correct any errors\n\n---\n\n## Related Articles\n\n- [Schedule Calendar](/support/articles/schedule-calendar)\n- [Approval Queue](/support/articles/approval-queue)\n- [Scheduling Settings](/support/articles/scheduling-settings)\n- [Audit History](/support/articles/audit-history)\n',
    NULL,
    COALESCE((SELECT id FROM kiosk_users.users WHERE username = 'rvanvuren' LIMIT 1), 1),
    'published',
    0,
    0,
    NOW(), NOW()
)
ON DUPLICATE KEY UPDATE
    content_md = VALUES(content_md),
    content_html = NULL,
    category_id = VALUES(category_id),
    title = VALUES(title),
    updated_at = NOW();

-- ---- Article #47: Timesheets & Time Tracking (slug=timesheets-time-tracking, status=published, category=team-management, 10095 bytes) ----
INSERT INTO support_articles
    (category_id, title, slug, content_md, content_html, author_id,
     status, view_count, is_featured, created_at, updated_at)
VALUES (
    (SELECT id FROM support_categories WHERE slug = 'team-management' LIMIT 1),
    'Timesheets & Time Tracking',
    'timesheets-time-tracking',
    '# Timesheets & Time Tracking\n\nTimesheets let managers review, approve, and export employee work hours. Combined with the Workbook\'s clock-in/clock-out system, BuyerKiosk provides a complete time tracking solution from punch to payroll export.\n\n---\n\n## Accessing Timesheets\n\nNavigate to **Admin** > **Schedule** > **Timesheets**, or go directly to `/admin/{storeCode}/schedule/timesheets`.\n\nYou can also reach Timesheets from the Schedule Calendar by clicking the `Timesheets` button in the primary header bar.\n\nYou need the `uri_schedule` permission to view timesheets.\n\n---\n\n## Timesheet Dashboard\n\nThe dashboard provides an overview of all employee hours for either a single week or a two-week pay period. It includes:\n\n- **Week navigation** -- Browse timesheets using the previous / next arrows or the `Today` button.\n- **Pay-period toggle** -- Switch between `1 Week` and `2 Weeks` views. The 2-week view aggregates a standard biweekly pay period and is the recommended layout for running payroll. See the [Pay Period View](#pay-period-view) section below.\n- **Employee summary rows** -- Each employee shows their hours, status, and pay totals for the selected period. In 2-week view, you\'ll see `Wk 1 Hours`, `Wk 2 Hours`, and `Total Hours` side by side.\n- **Click-to-drill-down** -- Click any employee row to open their detailed timesheet.\n\n### Dashboard Actions\n\n| Action | Description |\n|--------|-------------|\n| **Approve All** | Bulk-approve all pending timesheets for the selected week (1-week view only — hidden in 2-week view) |\n| **Export CSV** | Quick export of the visible period (see [Exporting Timesheets](#exporting-timesheets)) |\n| **Custom Export…** | Open the export modal to pick any range up to 26 weeks |\n| **Back to Schedule** | Return to the Schedule Calendar |\n\n---\n\n## Pay Period View\n\nUse the `1 Week` / `2 Weeks` toggle next to the date navigation to switch between a single-week summary and a full biweekly pay-period summary.\n\n| View | Best for |\n|------|----------|\n| **1 Week** | Daily review, approving timesheets, editing punches, comparing scheduled vs. actual |\n| **2 Weeks** | Running biweekly payroll, exporting CSVs, seeing Week 1 and Week 2 side by side |\n\n### What changes in 2-week view\n\n- The header reads **Pay Period of {Mon Date, Year}** with the full 14-day range shown underneath.\n- Navigation arrows step one week at a time, so you can slide the period without skipping a pay cycle.\n- The employee table columns become `Employee`, `Wk 1 Hours`, `Wk 2 Hours`, `Total Hours`, `OT`, `Total Pay`, `Status`. Scheduled / Actual / Variance / Break / Actions columns are hidden — those are only meaningful for a single week.\n- The status badge reflects both weeks: `Approved` shows when both weeks are approved; otherwise `{N} Pending` (where N is 1 or 2), or `Exported` once both weeks have been marked exported.\n- `Approve All` is hidden — approval still happens one week at a time from the 1-week view or the employee detail page.\n- KPI totals display with two decimals so they line up exactly with the per-row `Wk 1` / `Wk 2` / `Total` columns.\n\n---\n\n## Employee Timesheet Detail\n\nClick an employee name on the dashboard to view their detailed timesheet at `/admin/{storeCode}/schedule/timesheets/{employeeId}`.\n\nThe detail view shows:\n\n- **Daily breakdown** -- Each day\'s punches (clock in, clock out, breaks) with timestamps\n- **Calculated hours** -- Automatic calculation of worked hours per day\n- **Weekly total** -- Sum of all hours for the week\n- **Scheduled vs. Actual** -- Comparison of scheduled shift times against actual punch times\n- **Approval status** -- Whether the timesheet has been approved or is still pending\n\n### Editing Time Punches\n\nManagers can correct punch errors directly from the detail view:\n\n1. Locate the punch entry that needs correction.\n2. Click the punch time to edit it.\n3. Enter the corrected time.\n4. Save the change.\n\n### Adding Manual Punches\n\nIf an employee forgot to clock in or out:\n\n1. Open their timesheet detail page.\n2. Click the option to add a manual punch.\n3. Select the punch type (clock in or clock out).\n4. Enter the correct date and time.\n5. Save the new punch.\n\n### Deleting Punches\n\nTo remove an erroneous punch:\n\n1. Locate the punch on the detail page.\n2. Click the delete option.\n3. Confirm the deletion.\n\n> **Warning:** Deleting a punch cannot be undone. Double-check the entry before confirming.\n\n### Approving a Timesheet\n\n1. Review the employee\'s hours for accuracy.\n2. Click `Approve` to lock the timesheet.\n3. Once approved, punches can no longer be edited unless the timesheet is unlocked.\n\n### Unlocking an Approved Timesheet\n\nIf corrections are needed after approval:\n\n1. Click `Unlock` on the approved timesheet.\n2. Make the necessary edits.\n3. Re-approve when finished.\n\n---\n\n## Clock In / Clock Out (Workbook Kiosk Mode)\n\nThe Workbook provides a kiosk-style time punch interface designed for shared terminals. Employees select their name and enter a PIN to clock in or out.\n\n### Clock-In Flow\n\n1. From the Workbook schedule panel, select your name from the employee list.\n2. Enter your clock PIN.\n3. The system verifies your PIN and records the clock-in time.\n4. Your status on the schedule panel updates to \"Clocked In.\"\n\n### Clock-Out Flow\n\n1. Select your name from the employee list.\n2. Enter your clock PIN.\n3. The system records your clock-out time.\n4. Your total hours for the shift are calculated automatically.\n\n### Breaks\n\nEmployees can also track breaks:\n\n1. While clocked in, select your name and enter your PIN.\n2. Choose `Start Break` to begin a break.\n3. When returning, select your name, enter your PIN, and choose `End Break`.\n\nBreak time is automatically subtracted from total worked hours.\n\n### Manager Override\n\nIf an employee cannot clock in or out (forgotten PIN, technical issue), a manager can perform an override:\n\n1. Use the manager override option on the time punch screen.\n2. Enter the manager PIN.\n3. Select the employee and the action (clock in, clock out, start/end break).\n4. The override is logged in the audit trail.\n\n---\n\n## Schedule Panel\n\nThe schedule panel (accessible via the Workbook) shows today\'s scheduled employees alongside their current clock status:\n\n| Status | Meaning |\n|--------|---------|\n| **Scheduled** | Employee has a shift today but has not clocked in yet |\n| **Clocked In** | Employee is currently on the clock |\n| **On Break** | Employee is currently on break |\n| **Clocked Out** | Employee has completed their shift |\n\nThis gives managers a real-time view of who is working, who is on break, and who has not yet arrived.\n\n---\n\n## Exporting Timesheets\n\nTwo export buttons sit in the dashboard toolbar:\n\n| Button | Use it for… |\n|--------|-------------|\n| **Export CSV** | A one-click export of the visible period (1-week or 2-week) |\n| **Custom Export…** | A specific date range, finer control over what\'s included, and optional bookkeeping (mark as exported) |\n\n### Quick Export (current view)\n\nClick `Export CSV` to export the period that\'s currently on screen.\n\n| View | What it exports | When it\'s enabled | Side effect |\n|------|-----------------|-------------------|-------------|\n| **1 Week** | The visible week, **approved timesheets only** | Once at least one timesheet is approved | Approved rows are flagged **Exported** |\n| **2 Weeks** | The full 14-day pay period, **all employees** (approved + pending) | Whenever there is data | None — does **not** mark anything as exported |\n\n### Custom Export…\n\nFor everything outside the standard one-click flow, use **Custom Export…**:\n\n1. Click `Custom Export…` (sliders icon) in the toolbar.\n2. Pick a quick range — **Current view**, **1 week**, **2 weeks**, or **4 weeks** — or set the **Start week** and **End week** date pickers to any date inside the desired weeks.\n3. The blue preview box shows `{N} weeks selected: {Start} – {End}` and resolves your raw dates to `Week of …` / `Week ending …` labels underneath the inputs.\n4. Decide whether to:\n   - **Include unapproved timesheets** (default on) -- uncheck to export approved rows only.\n   - **Mark approved timesheets as exported after download** (default off) -- check this when this download represents the official payroll run; matching rows will switch to the **Exported** status afterwards.\n5. Click `Download CSV`.\n\n#### Limits and validation\n\n- The end week must be on or after the start week.\n- Maximum range is **26 weeks** -- wider ranges are blocked with a red warning in the preview box.\n\n> **Tip:** A common payroll workflow is: switch to **2 Weeks** view, navigate to the pay period that just closed, approve any pending rows, open **Custom Export…**, click the **Current view** preset, and click **Download CSV** with `Mark approved as exported` checked.\n\n> **Tip:** Export after all timesheets for the period are approved to ensure the data is final, or use **Custom Export…** with `Include unapproved` unchecked to get an approved-only file at any point in the cycle.\n\n---\n\n## Troubleshooting\n\n### Employee\'s punches are not appearing\n- Confirm the employee clocked in through the Workbook time punch interface (not through an external system).\n- Check that the punch was recorded for the correct store.\n- Verify the date range on the timesheet dashboard matches the expected dates.\n\n### Clock-in shows \"Invalid PIN\"\n- Have the employee verify their PIN with a manager.\n- The PIN is set in the employee\'s user profile under store assignments.\n\n### Hours calculation seems wrong\n- Check for missing punches. A clock-in without a corresponding clock-out (or vice versa) can produce incorrect totals.\n- Review break entries -- an unclosed break will affect calculations.\n- Add manual punches to correct any gaps.\n\n### Cannot edit an approved timesheet\n- Approved timesheets are locked. Click `Unlock` to re-enable editing, then re-approve after corrections.\n\n---\n\n## Related Articles\n\n- [Schedule Calendar](/support/articles/schedule-calendar) -- Creating and managing shifts\n- [My Schedule](/support/articles/my-schedule) -- Employee personal schedule view\n',
    NULL,
    COALESCE((SELECT id FROM kiosk_users.users WHERE username = 'rvanvuren' LIMIT 1), 1),
    'published',
    0,
    0,
    NOW(), NOW()
)
ON DUPLICATE KEY UPDATE
    content_md = VALUES(content_md),
    content_html = NULL,
    category_id = VALUES(category_id),
    title = VALUES(title),
    updated_at = NOW();

COMMIT;

-- ============================================================
-- Verification (uncomment and run after deploy)
-- ============================================================
-- SELECT id, slug, title, status, category_id,
--        LENGTH(content_md) AS bytes, updated_at
--   FROM support_articles
--  WHERE slug IN ('timesheets','timesheets-time-tracking')
--  ORDER BY slug;
