Scouttlo
All ideas/devtools/Plataforma SaaS que ofrezca APIs y módulos integrados para reportes dinámicos y gestión de usuarios con control de acceso basado en roles y auditoría automática.
GitHubB2Bdevtools

Plataforma SaaS que ofrezca APIs y módulos integrados para reportes dinámicos y gestión de usuarios con control de acceso basado en roles y auditoría automática.

Scouted 6 hours ago

7.0/ 10
Overall score

Turn this signal into an edge

We help you build it, validate it, and get there first.

Go from idea to plan: who buys, what MVP to launch, how to validate it, and what to measure before spending months.

Extra context

Learn more about this idea

Get a clearer explanation of what the opportunity means, the current problem behind it, how this idea solves it, and the key concepts involved.

Share your email to view this expanded analysis.

Score breakdown

Urgency8.0
Market size7.0
Feasibility8.0
Competition5.0
Pain point

Falta de una solución integrada y segura para la generación y gestión de reportes y administración de usuarios en sistemas SaaS.

Who'd pay for this

Empresas que desarrollan o gestionan sistemas SaaS con necesidades de reportes avanzados y administración segura de usuarios, especialmente en sectores con alta regulación.

Source signal

"Create src/services/reportService.js — the data-access layer for the two reports in the application. getProductReport() queries the current_product_price view to produce the REP_001 product listing with current price. getTopSellingProducts() queries the top_selling_products view to produce the REP_002 ranked list."

Original post

S3-T01: Reports API Functions

Published: 6 hours ago

Repository: JaniceHernandez/Product-Management-System Author: JaniceHernandez # S3-T01 — Reports API Functions *Sprint 3 Developer Guide — Hope, Inc. Product Management System* --- ## 1. Task Overview **Task ID:** S3-T01 **Task Name:** Reports API Functions **Assigned To:** Janice Hernandez (M1 — Project Lead) **PR Title:** feat/reports-api **Deadline:** April 30, 2026 **Dependency:** - #50 (`current_product_price` view), S3-T06 (`top_selling_products` view). ### Goal Create `src/services/reportService.js` — the data-access layer for the two reports in the application. `getProductReport()` queries the `current_product_price` view to produce the REP_001 product listing with current price. `getTopSellingProducts()` queries the `top_selling_products` view to produce the REP_002 ranked list. Also create `src/services/userService.js` for the Admin Module: `getAllUsers()`, `activateUser()`, and `deactivateUser()` — used by `UserManagementPage` (S3-T04). All write operations use `makeStamp()` for audit consistency. ### User Stories Covered - **US-18** — View the product listing report (REP_001) — `getProductReport()` powers this. - **US-19** — SUPERADMIN views the top-selling report (REP_002) — `getTopSellingProducts()` powers this. - **US-20** — Export product data — the service returns structured data that the report page converts to CSV. - **US-21** — SUPERADMIN views all registered users — `getAllUsers()` powers the Admin page. - **US-22** — SUPERADMIN activates newly registered users — `activateUser()`. - **US-23** — SUPERADMIN deactivates user accounts — `deactivateUser()`. - **US-24** — ADMIN views users in the system — `getAllUsers()` also used by ADMIN. - **US-25** — System prevents ADMIN from modifying SUPERADMIN accounts — RLS from S3-T07 enforces; the service passes through whatever RLS permits. - **US-29** — Stamp recorded on every write — `activateUser()` and `deactivateUser()` use `makeStamp()`. ### Expected Output - `src/services/reportService.js` — `getProductReport()` and `getTopSellingProducts()`. - `src/services/userService.js` — `getAllUsers()`, `activateUser()`, `deactivateUser()`. - PR `feat/reports-api` reviewed, approved, and merged into `dev`. --- ## 2. Prerequisites - **S2-T10 merged** — `current_product_price` view deployed and GRANT SELECT applied. - **S3-T06 merged** — `top_selling_products` view deployed and GRANT SELECT applied. - **S3-T07 merged** — Admin Module RLS policies protecting SUPERADMIN rows are active. - **S2-T01 merged** — `src/utils/stampHelper.js` exists with `makeStamp()`. - **S1-T18 merged** — `AuthContext` provides `currentUser.userid`. --- ## 3. Step-by-Step Development Guide --- ### Step 1 — Pull Latest `dev` and Create Feature Branch ```bash git checkout dev git pull origin dev git checkout -b feat/reports-api ``` --- ### Step 2 — Understand the Two Report Queries **REP_001 — Product Report:** Queries the `current_product_price` view (deployed in S2-T10). Returns all ACTIVE products with their latest unit price. Used for: a sortable/filterable product listing with prices, and a CSV export. **REP_002 — Top Selling Products:** Queries the `top_selling_products` view (deployed in S3-T06). Returns up to 10 ACTIVE products ranked by total quantity sold across all `salesDetail` transactions. Only accessible to users with `REP_002 = 1` (SUPERADMIN only per rights matrix). **Admin Module — User Management:** Queries `public.user` for all users visible to the current actor. SUPERADMIN and ADMIN can see all users. Activate and Deactivate operations update `record_status`. The RLS policy from S3-T07 prevents ADMIN from touching SUPERADMIN rows at the DB level — the service does not need to check this; it passes the operation through and the RLS either permits or rejects it. --- ### Step 3 — Create `src/services/reportService.js` ```js // src/services/reportService.js // Data-access layer for the Reports module. // REP_001: getProductReport() — product listing with current price // REP_002: getTopSellingProducts() — top 10 products by quantity sold // Components import from here — never call supabase.from('view_name') directly. import { supabase } from '../lib/supabaseClient'; // ── getProductReport ─────────────────────────────────────────── // US-18: Product listing report (REP_001). // US-20: Returns structured data used for CSV export. // // Queries the current_product_price view (S2-T10) which returns: // prodcode, description, unit, unitprice, effdate, record_status // All rows are ACTIVE (view filters INACTIVE products). // // @param {object} options - Optional query modifiers // @param {string} options.search - Filter by prodcode or description (client-side) // @param {string} options.sortField - Column to sort by (default: 'prodcode') // @param {string} options.sortDirection - 'asc' or 'desc' (default: 'asc') // @returns {{ data: Array, error: object|null }} export async function getProductReport({ search = '', sortField = 'prodcode', sortDirection = 'asc' } = {}) { const { data, error } = await supabase .from('current_product_price') .select('prodcode, description, unit, unitprice, effdate') .order(sortField, { ascending: sortDirection === 'asc' }); if (error) { console.error('getProductReport error:', error.message); return { data: [], error }; } // Apply search filter in JS (view does not support ILIKE without PostgREST params) const filtered = search ? (data ?? []).filter(row => row.prodcode.toLowerCase().includes(search.toLowerCase()) || row.description.toLowerCase().includes(search.toLowerCase()) ) : (data ?? []); return { data: filtered, error: null }; } // ── getTopSellingProducts ────────────────────────────────────── // US-19: Top-selling products report (REP_002) — SUPERADMIN only. // // Queries the top_selling_products view (S3-T06) which returns: // prodcode, description, unit, totalqty // View is pre-sorted DESC by totalqty and limited to 10 rows. // No additional filtering needed — the view handles everything. // // @returns {{ data: Array, error: object|null }} export async function getTopSellingProducts() { const { data, error } = await supabase .from('top_selling_products') .select('prodcode, description, unit, totalqty'); if (error) { console.error('getTopSellingProducts error:', error.message); return { data: [], error }; } return { data: data ?? [], error: null }; } ``` --- ### Step 4 — Create `src/services/userService.js` ```js // src/services/userService.js // Data-access layer for the Admin Module (User Management page). // getAllUsers() — fetch all user rows for the management table // activateUser() — set record_status = 'ACTIVE' // deactivateUser() — set record_status = 'INACTIVE' (soft deactivation) // // RLS from S3-T07 enforces SUPERADMIN protection at the DB level: // - ADMIN cannot update SUPERADMIN rows // - SUPERADMIN has full access // The service passes operations through without additional role checks. import { supabase } from '../lib/supabaseClient'; import { makeStamp } from '../utils/stampHelper'; // ── getAllUsers ──────────────────────────────────────────────── // US-21, US-24: SUPERADMIN and ADMIN view all registered users. // // Returns all rows from public.user ordered by user_type then username. // The RLS SELECT policy on public.user (USING true from migration 009) // allows all authenticated users to read user rows. // Stamp column included — visibility gated in the UI by user_type. // // @returns {{ data: Array, error: object|null }} export async function getAllUsers() { const { data, error } = await supabase .from('user') .select('userid, username, firstname, lastname, user_type, record_status, stamp') .order('user_type') .order('username'); if (error) { console.error('getAllUsers error:', error.message); return { data: [], error }; } return { data: data ?? [], error: null }; } // ── activateUser ─────────────────────────────────────────────── // US-22: SUPERADMIN activates a newly registered user. // // Sets record_status = 'ACTIVE'. After activation, the user can sign in. // ADMIN attempting to activate a SUPERADMIN account will be blocked by // the admin_update_record_status RLS policy (S3-T07) — the error is // returned to the caller for display. // // @param {string} userId - The userid of the account to activate // @param {string} actorId - currentUser.userid of the person performing the action // @returns {{ error: object|null }} export async function activateUser(userId, actorId) { const stamp = makeStamp('ACTIVATED', actorId); const { error } = await supabase .from('user') .update({ record_status: 'ACTIVE', stamp }) .eq('userid', userId); if (error) { console.error('activateUser error:', error.message); return { error }; } return { error: null }; } // ── deactivateUser ───────────────────────────────────────────── // US-23: SUPERADMIN deactivates a user account. // US-25: System prevents ADMIN from deactivating SUPERADMIN accounts. // // Sets record_status = 'INACTIVE'. The deactivated user is immediately // blocked by the login guard (AuthCallbackPage checks record_status). // // Note: This is a SOFT operation — record_status = 'INACTIVE', not DELETE. // Per the project rule: no hard deletes. User rows are never removed. // // @param {string} userId - The userid of the account to deactivate // @param {string} actorId - currentUser.userid of the person performing the action // @returns {{ error: object|null }} export async function deactivateUser(userId, actorId) { const stamp = makeStamp('DEACTIVATED', actorId); const { error } = await supabase .from('user') .update({ record_status: 'INACTIVE', stamp }) .eq('userid', userId); if (error) { console.error('deactivateUser error:', error.message); return { error }; } return { error: null }; } ``` --- ### Step 5 — Smoke Test in Browser Console Start the dev server and sign in as SUPERADMIN. Open DevTools → Console: ```js // Test REP_001 — product report const { getProductReport } = await import('/src/services/reportService.js'); const rep001 = await getProductReport(); console.log('REP_001 rows:', rep001.data?.length, rep001.error); // Expected: rows returned (same as current_product_price view count) // Test REP_002 — top selling const { getTopSellingProducts } = await import('/src/services/reportService.js'); const rep002 = await getTopSellingProducts(); console.log('REP_002 rows:', rep002.data?.length, '(should be ≤ 10)', rep002.error); console.log('REP_002 sorted:', rep002.data?.map(r => `${r.prodcode}: ${r.totalqty}`)); // Expected: ≤ 10 rows, sorted by totalqty DESC // Test getAllUsers const { getAllUsers } = await import('/src/services/userService.js'); const users = await getAllUsers(); console.log('Users:', users.data?.length, users.error); // Expected: all user rows returned ``` --- ### Step 6 — Commit and Push ```bash git add src/services/reportService.js git add src/services/userService.js git commit -m "Add report service functions (REP_001, REP_002) and user management service" git push -u origin feat/reports-api ``` --- ### Step 7 — Open the Pull Request - **Source branch:** `feat/reports-api` - **Target branch:** `dev` - **Title:** `feat/reports-api — REP_001 + REP_002 Supabase query functions` **Description:** ``` ## What changed - src/services/reportService.js getProductReport({ search, sortField, sortDirection }) Queries current_product_price view (S2-T10) Returns: prodcode, description, unit, unitprice, effdate Client-side search filter on prodcode + description Server-side ORDER BY via Supabase .order() getTopSellingProducts() Queries top_selling_products view (S3-T06) Returns: prodcode, description, unit, totalqty View pre-sorted DESC, limited to 10 - src/services/userService.js getAllUsers() — all users ordered by user_type, username activateUser(userId, actorId) — record_status = ACTIVE + stamp deactivateUser(userId, actorId) — record_status = INACTIVE + stamp SUPERADMIN protection enforced by RLS (S3-T07) not by service ## How to test 1. Browser console: getProductReport() → rows returned (≤ current_product_price count) 2. Browser console: getTopSellingProducts() → ≤ 10 rows, sorted by totalqty DESC 3. Browser console: getAllUsers() → all user rows 4. getProductReport({ search: 'AK' }) → filtered rows only ``` Assign M2 (Marlan) as reviewer — `ProductReportPage` and `TopSellingPage` (S3-T03) import from these services. --- ### Step 8 — Respond to Review and Merge ```bash git checkout dev git pull origin dev git branch -d feat/reports-api ``` Notify M2 — `reportService.js` and `userService.js` are ready for S3-T03 and S3-T04. --- ## 4. Potential Issues and Recommended Solutions - **`getProductReport()` returns empty array.** The `current_product_price` view (S2-T10) may not be deployed or the GRANT SELECT was not applied. Confirm in Supabase Dashboard → Database → Views. Also confirm the authenticated role has SELECT. - **`getTopSellingProducts()` returns empty.** The `top_selling_products` view (S3-T06) must be merged before this service works. Check the view exists in Supabase. Also verify `salesDetail` has data: `SELECT COUNT(*) FROM public.salesDetail;` - **`getAllUsers()` returns empty even though users exist.** The SELECT policy on `public.user` uses `USING(true)` from migration 009. If this was dropped, re-add it. Also confirm the Supabase table name is `user` (not `users`). - **`activateUser` or `deactivateUser` fails with RLS error for ADMIN targeting a SUPERADMIN.** This is the expected and correct behaviour — the `admin_update_record_status` policy (S3-T07) blocks it. The service returns `{ error }` which the UI layer (S3-T04) displays as a toast or inline message. - **`getProductReport()` sort doesn't work on `unitprice`.** The `current_product_price` view column is `unitprice`. Supabase `.order('unitprice')` on a view works the same as a table — ensure the column name exactly matches. --- ## 5. Definition of Done (DoD) S3-T01 is complete when ALL of the following are true: - `src/services/reportService.js` exports `getProductReport` and `getTopSellingProducts`. - `src/services/userService.js` exports `getAllUsers`, `activateUser`, and `deactivateUser`. - `getProductReport()` returns rows from the `current_product_price` view with correct columns. - `getProductReport({ search: 'AK' })` returns only matching rows. - `getTopSellingProducts()` returns ≤ 10 rows ordered by `totalqty` DESC. - `getAllUsers()` returns all `public.user` rows. - `activateUser()` and `deactivateUser()` include `makeStamp()` in the UPDATE payload. - No `.delete()` call exists in either service file. - PR `feat/reports-api` raised against `dev`, approved by at least one member, and merged. - Feature branch deleted from GitHub after merge.