{"ns":"mart","title":"Corner Mart","version":"1.0","summary":"Corner Mart is a grocery delivery shop with admin-approved accounts. Customers register, wait for an admin to approve them, sign in, browse a catalogue with stock limits, fill a cart, pick a delivery slot and place orders that they can cancel until packed. Admins approve or reject registrations.","problem":"A delivery shop must only serve vetted customers, keep sessions and redirects predictable, and charge delivery and enforce minimums exactly as promised. Wrong rules cost money or lock out good customers.","personas":[{"name":"Customer","need":"Sign in once, stay signed in, order groceries into a delivery slot and know the exact total."},{"name":"Admin","need":"Approve or reject new registrations with a reason the applicant can read."},{"name":"Applicant","need":"Know whether the registration is pending or rejected and why."}],"goals":["Only approved accounts can sign in or call the API.","A signed-in session survives a reload and ends on logout.","Minimum order, free-delivery and stock rules are applied exactly at their boundaries.","Delivery slots never exceed their capacity."],"features":[{"id":"AUT","title":"Sign in, session and redirect","summary":"Approved customers sign in, are redirected to the page they asked for and stay signed in.","reqs":[{"id":"FR-AUT-01","weight":"Must","text":"Valid credentials show the browser alert \"✅ Login successful\"; accepting it opens the Shop with the customer name and a Logout button.","accept":["The Login page has Username and Password fields and the password is masked.","After accepting the alert the header reads \"Hi, Asha\" and shows Logout."]},{"id":"FR-AUT-02","weight":"Must","text":"The session survives a page reload.","accept":["After a reload the user is still on Shop, still \"Hi, Asha\" with Logout; the Login page is not shown."]},{"id":"FR-AUT-03","weight":"Must","text":"Invalid or empty credentials never show the success alert, and the error does not reveal which part was wrong.","accept":["Both fields empty: \"Please enter username and password\".","Wrong password or unknown username: the same \"Invalid username or password\"; still on Login."]},{"id":"FR-AUT-04","weight":"Should","text":"Opening a protected page while signed out redirects to Login with \"Please log in to continue\" and, after sign-in, returns to the requested page.","accept":["Opening My orders signed out goes to /mart/?next=%2Forders with \"Please log in to continue\".","After sign-in My orders is shown (not Shop) listing CM-2057 and CM-2041.","Opening the Cart signed out and signing in shows the Cart page."]},{"id":"FR-AUT-05","weight":"Should","text":"Logout ends the session; protected pages require sign-in again, including through the Back button and a reload.","accept":["Logout shows Login with \"You have been logged out.\" and no Logout in the header.","Back does not show Shop; Login shows \"Please log in to continue\"; a reload stays on Login."]}]},{"id":"REG","title":"Registration and approval","summary":"New accounts stay pending until an admin approves or rejects them.","reqs":[{"id":"FR-REG-01","weight":"Must","text":"A registration is accepted but stays pending, and a pending account cannot sign in.","accept":["Registering shows \"Registration received\" with the account \"pending approval\".","Signing in as a pending account shows \"Your account is awaiting approval. You can sign in once an admin approves it.\" with no alert and stays on Login."]},{"id":"FR-REG-02","weight":"Must","text":"A rejected account cannot sign in and sees the rejection reason.","accept":["Sign-in shows \"Your registration was rejected: Address is outside our delivery area\"."]},{"id":"FR-REG-03","weight":"Should","text":"Each invalid registration field shows its own message.","accept":["All empty: \"Full name is required\", \"Username is required\", \"Password must be at least 4 characters\", \"Phone must look like 555-0123\", \"Delivery address is required\".","An existing username shows \"That username is taken\".","Different confirmation shows \"Passwords do not match\".","Username \"pr\" shows \"Username must be 3–20 letters, digits, dots or underscores\"."]},{"id":"FR-REG-04","weight":"Must","text":"An admin approves a pending registration and the user can then sign in.","accept":["The Account approvals page opens with \"Pending (2)\" listing Ravi Kumar and Lena Fischer.","Approve on Lena Fischer shows \"Lena Fischer approved — they can sign in now.\" and \"Pending (1)\".","Lena then signs in with \"✅ Login successful\" and sees \"Hi, Lena\"."]},{"id":"FR-REG-05","weight":"Should","text":"Rejecting requires a reason, and the reason reaches the applicant.","accept":["Confirm reject with an empty reason shows \"Give a reason for the rejection\"; Ravi stays pending.","Reason \"Phone number not reachable\" shows \"Ravi Kumar rejected.\" and the All customers list shows \"rejected\" with that reason.","Ravi's sign-in shows \"Your registration was rejected: Phone number not reachable\"."]},{"id":"FR-REG-06","weight":"Could","text":"Customers do not see the approvals page.","accept":["A customer has no \"Approvals\" link and /mart/admin redirects to Shop."]}]},{"id":"CAT","title":"Catalogue and stock","summary":"The catalogue has categories, search, an in-stock filter and sorting with per-item limits.","reqs":[{"id":"FR-CAT-01","weight":"Should","text":"Quantities stop at the stock level or 10, whichever is lower, and out-of-stock items cannot be added.","accept":["Vine Tomatoes and Salted Butter say \"Out of stock\" with disabled Add buttons.","Avocados show \"Only 3 left\" and the quantity stops at 3 with + disabled.","Bananas (stock 40) stop at 10."]},{"id":"FR-CAT-02","weight":"Should","text":"Search, category tabs, the in-stock filter and sorting combine correctly.","accept":["The full catalogue reads \"24 products\"; \"Dairy & Eggs\" reads \"5 products\"; \"In stock only\" reads \"4 products\" without Salted Butter.","\"Price: high to low\" starts with Aged Cheddar ($6.25) and ends with Whole Milk ($2.89).","Search \"oil\" finds only Extra Virgin Olive Oil; \"zzz\" shows \"0 products\" and \"No products match your filters.\""]}]},{"id":"CRT","title":"Cart, minimum and delivery fee","summary":"The cart enforces a $10.00 minimum order and free delivery from $35.00.","reqs":[{"id":"FR-CRT-01","weight":"Must","text":"An order below $10.00 cannot be placed and the cart states the shortfall.","accept":["Gala Apples × 1 ($3.49) and Penne Pasta × 2 ($3.78): Subtotal (3 items) $7.27, Delivery $2.99, Total $10.26.","\"Add $2.73 more to reach the $10.00 minimum order.\" and Place order is disabled.","Penne Pasta at 4 gives Subtotal $11.05, \"Add $23.95 more for free delivery.\" and an enabled Place order."]},{"id":"FR-CRT-02","weight":"Must","text":"Delivery is free when the subtotal is $35.00 or more (inclusive); below that it costs $2.99.","accept":["A subtotal of exactly $35.00 (6 items) shows Delivery \"Free\" and Total $35.00.","Reducing to Subtotal $26.01 shows Delivery $2.99, Total $29.00 and \"Add $8.99 more for free delivery.\""]}]},{"id":"ORD","title":"Checkout, slots and orders","summary":"Orders are placed into delivery slots with limited capacity and can be cancelled until packed.","reqs":[{"id":"FR-ORD-01","weight":"Must","text":"Placing an order needs a delivery slot and a payment choice; the order appears in history and can be cancelled while not yet packed.","accept":["Basmati Rice × 2: Subtotal $15.98, Delivery $2.99, Total $18.97, address prefilled \"48 Harbour Lane\".","Place order with no choices shows \"Choose a delivery slot\".","After placing, My orders shows \"Order CM-2064 placed…\", status Placed, $18.97, above CM-2063 (Packed); the cart badge is 0.","CM-2063 (Packed) has no Cancel order button; \"Cancel order\" then \"Yes, cancel it\" on CM-2064 makes it Cancelled."]},{"id":"FR-ORD-02","weight":"Should","text":"Slot capacity decreases with every order, and a slot with no places left is Full and cannot be chosen.","accept":["\"Today · 18:00–20:00\" is Full and disabled; \"Tomorrow · 12:00–14:00\" has 1 left.","After one order in \"Tomorrow · 12:00–14:00\" it says Full and cannot be selected."]},{"id":"FR-ORD-03","weight":"Should","text":"Order history lists orders newest first with correct lines, delivery fee and total.","accept":["CM-2057 (Delivered, $37.15) is above CM-2041 (Delivered, $16.84).","CM-2057: Basmati Rice 2 × $7.99 = $15.98, Extra Virgin Olive Oil 1 × $11.99, Free-range Eggs 2 × $4.59 = $9.18; Delivery Free; Total $37.15.","CM-2041: Whole Milk, Sourdough Loaf, Bananas; Delivery $2.99; Total $16.84."]}]},{"id":"API","title":"Login API","summary":"The API applies the same approval rules as the screen.","reqs":[{"id":"FR-API-01","weight":"Must","text":"The login API returns a token only for approved accounts, refuses pending accounts and requires a token for private resources.","accept":["Login for an approved account returns 200 with a token and user.status \"approved\".","GET /api/mart/me with the token returns 200 and the name \"Asha Menon\".","Login for a pending account returns 403 with code \"pending\".","GET /api/mart/cart without a token returns 401 \"Please log in to continue\"."]}]}],"nfr":[{"id":"NFR-01","weight":"Must","text":"Account approval, minimum order and delivery rules are enforced by the server, not only by the screen.","accept":["The API refuses a pending account exactly as the Login page does."]}],"glossary":[{"term":"Pending","meaning":"A registration waiting for an admin decision."},{"term":"Slot","meaning":"A delivery window with a limited number of places."},{"term":"Packed","meaning":"An order stage after which cancellation is no longer offered."}],"outOfScope":["Real payments","Sessions shared between browser tabs (each tab is its own store)"]}