E2E SCENARIOS — QUOTE & PRICING
VietGara — Release 1.2 · Version 1.0 — September 30, 2026
Covers FR-QUO-01..04 and the daily transaction limit (FR-LIC-02) as it applies to quotes and repair orders. FR-QUO-05 (PDF export) is Release 1.4. Records are in 00_Test_Data.md §3.3; a quote’s line items and approval-mode choice have no dedicated fixtures — each scenario below creates its own.
| ID | Title | Priority | Automation |
|---|---|---|---|
| E2E-QUO-001 | Create a draft quote with labor and part lines | P1 | Automated (API, partial) |
| E2E-QUO-002 | Quote field validation | P2 | Automated (API, partial) |
| E2E-QUO-003 | Edit and delete a draft; a non-draft quote cannot be edited or deleted | P2 | Automated (API, partial) |
| E2E-QUO-004 | Send for approval (LINK mode): e-mailed automatically when the customer has an e-mail on file | P1 | Automated (API, partial) |
| E2E-QUO-005 | Send for approval (LINK mode): no e-mail on file, the advisor copies the link | P2 | Automated (API, partial) |
| E2E-QUO-006 | The customer decides each line from the public link | P1 | Automated (API, partial) |
| E2E-QUO-007 | The public link stops working once decided, expired or unknown | P2 | Automated (API, partial) |
| E2E-QUO-008 | MANUAL mode: the advisor records the customer’s decision directly | P1 | Automated (API, partial) |
| E2E-QUO-009 | The garage’s default approval mode pre-fills a new quote; overriding it per quote | P2 | Automated (API, partial) |
| E2E-QUO-010 | Convert an approved quote into a repair order, preserving only approved lines | P1 | Automated (API, partial) |
| E2E-QUO-011 | Quote number is rendered from the garage’s template and unique per garage | P3 | Automated (API, partial) |
| E2E-QUO-012 | The daily transaction limit blocks quote creation and conversion | P1 | Automated (API, partial) |
| E2E-QUO-013 | List and filter quotes by status | P2 | Automated (API, partial) |
| E2E-QUO-014 | Permissions: no access, and full access | P1 | Automated |
| E2E-QUO-015 | Pick a job on a quote: the labor line and the part lines appear, editable | P1 | Automated |
| E2E-QUO-016 | A hidden consumable is not billed, not shown to the customer and not decided | P1 | Automated |
| E2E-QUO-017 | A hidden consumable follows its job when the quote converts | P1 | Automated (API) |
E2E-QUO-001 · Create a draft quote with labor and part lines
Priority: P1 · Covers: FR-QUO-01 · Automation: Automated (API, partial)
Preconditions: baseline. Signed in as ADVISOR_A1, active garage A1.
Test data: customer QA Customer An Nguyen, vehicle 51A-111.11. Lines: (1) Labor “Oil change service”, qty 1, unit price 250000; (2) Part “Engine oil 5W-30”, qty 4, unit price 180000.
Steps
- Quotes → New quote. Pick the customer, then the vehicle (only An Nguyen’s own vehicles are offered).
- Add line item twice and fill in the test data.
- Save.
Expected results
- The quote is created as a draft, numbered
BG-<YYYY><MM>-0001(or the next free sequence for the garage this month), status Draft. - The line total of each row is quantity × unit price; the quote total is their sum (
970,000 VND). - Opening the quote from the list shows the same customer, vehicle, lines and total.
- The quote does not appear when A2 is the active garage.
E2E-QUO-002 · Quote field validation
Priority: P2 · Covers: FR-QUO-01 · Automation: Automated (API, partial)
Preconditions: baseline. ADVISOR_A1, A1 active. Customer An Nguyen, vehicle 51A-111.11.
Test data:
| Case | Description | Quantity | Unit price | Expected |
|---|---|---|---|---|
| a | (empty) | 1 | 100000 | Description required |
| b | Brake pad | 0 | 100000 | Quantity must be a positive number |
| c | Brake pad | 1.9999 | 100000 | At most 3 decimal places |
| d | Brake pad | 1 | -1 | Unit price must not be negative |
| e | Brake pad | 1.5 | 100000 | Saved |
Steps
- New quote with the customer and vehicle; for each case, set the single line to the row’s data and save.
Expected results
- a–d are refused; the form shows “Please check the highlighted information.” and marks the field.
- e is saved. Clean-up: delete it.
- A quote needs at least one line: removing the only line disables Save.
E2E-QUO-003 · Edit and delete a draft; a non-draft quote cannot be edited or deleted
Priority: P2 · Covers: FR-QUO-01 · Automation: Automated (API, partial)
Preconditions: baseline. ADVISOR_A1, A1 active. A draft quote for An Nguyen (E2E-QUO-001’s quote, or a fresh one).
Steps
- Edit the draft: change the labor line’s unit price to
280000; add a third line. Save. - Send for approval (any mode).
- Try to open the line-item editor again; try Delete.
- Delete a second, still-draft quote.
Expected results
- Step 1 saves; the total reflects the new price and the added line.
- Step 2 moves the quote out of Draft; the edit and delete controls are gone (or refused with “This cannot be done right now.” if attempted directly).
- Step 4 asks “Delete quote
<number>?” and removes it from the list.
E2E-QUO-004 · Send for approval (LINK mode): e-mailed automatically when the customer has an e-mail on file
Priority: P1 · Covers: FR-QUO-02 · Automation: Automated (API, partial)
Preconditions: baseline. ADVISOR_A1, A1 active. A draft quote for An Nguyen ([email protected], has an e-mail), approval mode Send a link.
Steps
- Open the quote → Send for approval.
- Open Mailpit, search
to:[email protected].
Expected results
- The quote’s status becomes Sent; a banner reads “The approval link was e-mailed to the customer.” with the link shown and a Copy link button.
- Mailpit shows an e-mail to An Nguyen with the quote number and a link starting with the owner web’s address (
/public/quotes/<token>). - The link in the banner and the link in the e-mail are the same.
E2E-QUO-005 · Send for approval (LINK mode): no e-mail on file, the advisor copies the link
Priority: P2 · Covers: FR-QUO-02 · Automation: Automated (API, partial)
Preconditions: baseline. ADVISOR_A1, A1 active. A draft quote for a customer created in the scenario with no e-mail (QA Customer QUO005), approval mode Send a link; the baseline customers all keep their e-mail.
Steps
- Open the quote → Send for approval.
- Click Copy link and paste it somewhere visible.
Expected results
- The banner reads “The approval link was created — the customer has no e-mail on file, so share the link another way.” (no auto-e-mail).
- The copied link matches the one shown, and opens the public quote page (E2E-QUO-006).
- Mailpit has no new message to any address for this quote.
E2E-QUO-006 · The customer decides each line from the public link
Priority: P1 · Covers: FR-QUO-02, FR-QUO-03 · Automation: Automated (API, partial)
Preconditions: a Sent, LINK-mode quote with 2 lines and its public link (E2E-QUO-004 or 005).
Steps
- Open the public link in a private/incognito window (no sign-in).
- Approve line 1, reject line 2. Submit the decision.
- As ADVISOR_A1, open the quote.
- Repeat with a fresh Sent quote where every line is rejected.
Expected results
- The public page shows the garage name, quote number, customer/vehicle names and the lines — no internal ids, no
Authorizationheader sent. - After submitting: “You approved this quote. Thank you!” is not shown when only some lines are rejected — instead the page reflects the mixed decision; the page becomes read-only.
- In the owner web, the quote’s status is Approved (at least one line approved) with line 1 marked approved and line 2 rejected.
- Step 4’s quote becomes Rejected (every line rejected).
- Reopening the same public link now shows the decision already made, with no way to decide again.
E2E-QUO-007 · The public link stops working once decided, expired or unknown
Priority: P2 · Covers: FR-QUO-02 · Automation: Automated (API, partial)
Preconditions: the decided link from E2E-QUO-006. A second, freshly sent LINK-mode quote.
Steps
- Open the already-decided link again.
- Open
/public/quotes/not-a-real-token. - Advisor rejects the second quote’s public token from the owner web by re-sending it (a fresh Send for approval issues a new token, invalidating the old one) — open the old (copied earlier) link.
Expected results
- Steps 1–3 each show “This link is invalid or has expired.” (
422 QUOTE_LINK_INVALID), never a stack trace or a blank page.
E2E-QUO-008 · MANUAL mode: the advisor records the customer’s decision directly
Priority: P1 · Covers: FR-QUO-02, FR-QUO-03 · Automation: Automated (API, partial)
Preconditions: baseline. ADVISOR_A1, A1 active. A draft quote for Chi Le (A2 — use OWNER_A instead if a cross-garage user is inconvenient; simplest: a draft quote for An Nguyen), approval mode Record manually.
Steps
- Open the quote → Send for approval.
- Record the customer’s decision → approve every line → Save decision.
Expected results
- Step 1: the quote becomes Sent; no e-mail is sent and no public link banner is shown (MANUAL mode has no customer-facing touchpoint).
- Step 2: the quote becomes Approved; the decision panel is replaced by the Convert to repair order action.
- The same panel, reached instead on a LINK-mode Sent quote, lets the advisor override a phone decision (documented behaviour; not re-tested here since E2E-QUO-006 already covers the public path).
E2E-QUO-009 · The garage’s default approval mode pre-fills a new quote; overriding it per quote
Priority: P2 · Covers: FR-QUO-02, FR-TEN-05 · Automation: Automated (API, partial)
Preconditions: baseline. OWNER_A. A1’s default approval mode is Send a link (factory default).
Steps
- A1 → Garage settings → Configuration → set the default approval mode to Record manually. Save.
- New quote for An Nguyen: look at the approval-mode choice before touching it.
- Change it to Send a link for this one quote only. Save.
- New quote again: look at the default.
Expected results
- Step 2 shows Record manually pre-selected, with the hint text explaining what it does.
- Step 3 saves with LINK mode on this quote; the garage’s own default is unchanged.
- Step 4 again shows Record manually. Clean-up: set A1’s default back to Send a link; delete the quotes created.
E2E-QUO-010 · Convert an approved quote into a repair order, preserving only approved lines
Priority: P1 · Covers: FR-QUO-04 · Automation: Automated (API, partial)
Preconditions: baseline. ADVISOR_A1, A1 active. An Approved quote for An Nguyen with one approved and one rejected line (E2E-QUO-006’s first quote, or a fresh equivalent).
Steps
- Open the quote → Convert to repair order.
- Follow the link to the new repair order (E2E-REP-003 covers its detail in full).
Expected results
- A banner reads “A repair order was created from this quote.” with a View repair order link; the quote’s status becomes Converted and its actions are gone.
- The repair order has exactly the one approved line (same description, quantity, unit price), status Open, the same customer name and vehicle plate as the quote, and its own number (
LSC-<YYYY><MM>-####). - Converting the same quote again is refused (already converted).
- The vehicle’s repair history (An Nguyen’s vehicle) now lists both the quote (Converted) and the repair order.
E2E-QUO-011 · Quote number is rendered from the garage’s template and unique per garage
Priority: P3 · Covers: FR-TEN-05 · Automation: Automated (API, partial)
Preconditions: baseline. OWNER_A.
Steps
- A1 active → create 2 draft quotes; note their numbers.
- A2 active → create 1 draft quote; note its number.
Expected results
- A1’s quotes are numbered
BG-<YYYY><MM>-####with consecutive sequences (e.g.-0002,-0003continuing from whatever A1 already has). - A2’s quote starts its own sequence (e.g.
-0001) — each garage counts independently, even for the same owner. - Clean-up: delete the 3 quotes.
E2E-QUO-012 · The daily transaction limit blocks quote creation and conversion
Priority: P1 · Covers: FR-LIC-02 · Automation: Automated (API, partial)
Preconditions: ADMIN creates plan QA Transaction Limit, code qa-txn-limit (100,000 VND/month, 1 garage, 5 staff, 2 transactions/day, on sale). Sign up and verify signup.quo012.<unique>@vietgara.test (QA Owner QUO012, 0900000501); switch to QA Transaction Limit; create 1 garage QA Gara QUO012; create a customer with a vehicle in it.
Steps
- Create a draft quote (transaction 1 of 2 today).
- Send it for approval (LINK) and approve every line from the public link, then Convert to repair order (transaction 2 of 2).
- Create a second draft quote (would be transaction 3).
- Close the repair order from step 2’s conversion once ready (see E2E-REP-008). Closing is not a creation, so it is not counted and the limit does not refuse it.
Expected results
- Steps 1–2 succeed; Transactions today 2 / 2 on the Subscription page.
- Step 3 is refused: “Your plan limit is reached. Upgrade to add more.”
- Step 4 (closing) is a separate action from creation and is not itself counted against
maxTransactionsPerDay(only quote/repair-order/settlement creation is, per Low-Level Design §Entitlement); confirm closing still succeeds once every line is completed. - Clean-up: none required (single-use user); note the user and plan for later removal if the environment is reused.
E2E-QUO-013 · List and filter quotes by status
Priority: P2 · Covers: FR-QUO-01 · Automation: Automated (API, partial)
Preconditions: baseline. ADVISOR_A1, A1 active, with at least one quote in each of Draft, Sent, Approved/Rejected and Converted (reuse quotes from earlier scenarios, or create fresh ones).
Steps
- Quotes with no filter.
- Filter by each status in turn.
Expected results
- No filter lists every quote of A1, newest first, each with number, customer/vehicle, status badge, total and created date.
- Each filter shows only quotes of that status.
E2E-QUO-014 · Permissions: no access, and full access
Priority: P1 · Covers: FR-IAM-03, FR-QUO-01 · Automation: Automated
Preconditions: baseline. A1 active.
Test data: TECH_A1A2 (no quote access, per the Technician role), ADVISOR_A1 (quote read/write), MANAGER_A1 (everything).
Steps
- TECH_A1A2: look at the sidebar; type
/quotesdirectly. - ADVISOR_A1: create, send and delete a draft quote.
- MANAGER_A1: same as step 2.
Expected results
- TECH_A1A2 has no Quotes entry in the sidebar;
/quotesshows no data or “You are not allowed to do this.” - ADVISOR_A1 and MANAGER_A1 can fully manage quotes.
E2E-QUO-015 · Pick a job on a quote: the labor line and the part lines appear, editable
Priority: P1 · Covers: FR-CAT-05, FR-CAT-06, FR-QUO-01 · Automation: Automated
Blackbox: TestAddJobToQuote (one labor line at the job’s price, a line per active suggested part at the part’s price, a packaging unit typed and priced per unit; the quote total follows; the quote is edited afterwards; an inactive or unknown job, a non-draft quote and a quote that would pass 100 lines are refused).
Preconditions: baseline. ADVISOR_A1, A1 active. The job Oil change (labor 200,000; OIL-5W30 × 4 litres at 90,000; FLT-OIL-01 × 1 hidden). A draft quote for An Nguyen with one free-form line of 100,000.
Steps
- Open the draft quote → Add a job. Search
oiland pick Oil change. - Change the oil line’s quantity from
4to5. Save the quote. - Add the same job again.
- Send the quote, then try Add a job on it.
Expected results
- Step 1: the quote gains the labor line Oil change at
200,000, the oil line4 Lat90,000(360,000) and a hidden line for the filter marked “Hidden from the customer”; the total is100,000 + 200,000 + 360,000 = 660,000. - Step 2: the oil line is
450,000, the total750,000; the labor line is unchanged. - Step 3: the lines are added again (a job may be used twice).
- Step 4: the sent quote has no Add a job; through the API it is refused as not editable.
E2E-QUO-016 · A hidden consumable is not billed, not shown to the customer and not decided
Priority: P1 · Covers: FR-CAT-08, FR-QUO-02, FR-QUO-03, FR-QUO-05 · Automation: Automated
Blackbox: TestHiddenConsumableOnTheQuote (price 0 and left out of the total; the public quote and the PDF omit it; the decision covers the billable lines only and a decision naming the hidden line is refused; a hidden line must be a catalogue part line, and a quote needs a billable line; a bad job id is refused).
Preconditions: baseline. ADVISOR_A1, A1 active. A draft quote with the job Oil change added (E2E-QUO-015), which includes a hidden FLT-OIL-01 line.
Steps
- Open the quote: look at the hidden line and the total.
- Try to mark a free-form line, or a labor line, as hidden; try to save a quote whose only line is hidden.
- Send the quote (LINK mode) and open the public link as the customer.
- Download the quote’s PDF.
- As the customer decide on the lines.
Expected results
- Step 1: the hidden line shows “Hidden from the customer”, price
0, and is not in the total. - Step 2: refused (“Only a catalogue part line can be hidden, and a quote needs a billable line.”).
- Step 3: the public page lists the labor and the oil line only, with the same total.
- Step 4: the PDF lists the same lines and the same total.
- Step 5: the customer decides on the lines shown; the hidden line is not among them.
E2E-QUO-017 · A hidden consumable follows its job when the quote converts
Priority: P1 · Covers: FR-CAT-08, FR-QUO-03, FR-QUO-04 · Automation: Automated (API)
Blackbox: TestHiddenConsumableFollowsItsJob (approve a billable line of the job and the consumable converts; reject every billable line of the job and it is dropped; a free-standing consumable follows the quote; the order’s total leaves it out).
Preconditions: baseline. ADVISOR_A1, A1 active. Two sent quotes, each with the job Oil change (labor, oil and a hidden filter) and one free-form line.
Steps
- On the first quote approve the job’s labor line, reject its oil line and the free-form line. Convert.
- On the second quote reject all the job’s lines and approve the free-form line. Convert.
Expected results
- Step 1: the repair order has the labor line and the hidden filter line (not billed); its total is the labor price.
- Step 2: the repair order has the free-form line only; the filter is not on it.