Status lifecycle
EveryQuote has a status column that moves through these values:
draft
Set when a client calls
POST /api/quotes/save-draft. The quote is stored but not yet sent to DMI for review. No PDF is generated at this point.sent
Set when a client calls
POST /api/quotes/submit. The sent_at timestamp is recorded and a PDF is generated and stored. The quote is now visible and actionable in the admin list.Valid status values stored in the database are:
draft, sent, accepted, rejected, expired. Use these exact strings when filtering via the status query parameter or setting status via PATCH.Admin quote list
The quote list is available atGET /admin/cotizaciones and requires the auth and verified middleware. It renders the admin.quotes.index view.
Filtering and search
Two query parameters are accepted:| Parameter | Behaviour |
|---|---|
status | Filters by exact status value (draft, sent, accepted, rejected, or expired) |
q | Full-text search across client_name, client_email, and reference using LIKE %query% |
withQueryString().
Example URL
Pagination
Results are paginated at 15 records per page, ordered bycreated_at descending (newest first). Each row includes the count of associated QuoteItem records via withCount('items').
Quote detail view
GET /admin/cotizaciones/{quote} renders admin.quotes.show with the quote and its related data eager-loaded:
items.block— each line item with its originatingQuoteBlockreplies— allQuoteReplyrecords for this quote, ordered bysent_atdescending
Updating quote status
Send aPATCH request to update a quote’s status:
admin.quotes.status and is protected by auth + verified middleware.
Replying and scheduling a meeting
The reply action sends a Google Meet appointment message to the client by email and attaches the quote PDF.| Field | Type | Validation |
|---|---|---|
meeting_date | string | required, valid date |
What happens on reply
Format meeting message
The
meeting_date value is parsed and formatted as dd/mm/yyyy HH:mm. The message stored and sent is:Save QuoteReply record
A
QuoteReply is created with:quote_id— the parent quotemessage— the formatted meeting messagesent_at— the exactmeeting_datevalue provided in the request
Regenerate PDF
The quote PDF is regenerated from the current quote data and written to
storage/app/public/quotes/{reference}.pdf, overwriting any previously stored version.Send email
QuoteReplyMail is dispatched to client_email via Mail::to(). The email carries the formatted meeting message and the PDF as an attachment.Data models
Quote
Quote
Key columns relevant to management:
| Column | Type | Notes |
|---|---|---|
reference | string | Auto-generated: COT- + strtoupper(uniqid()) |
client_name | string | — |
client_email | string | — |
client_company | string | Nullable |
client_phone | string | Nullable |
additional_requirements | text | Nullable |
status | string | draft, sent, accepted, rejected, or expired |
sent_at | datetime | Set on submit |
subtotal | decimal(2) | — |
tax | decimal(2) | — |
total | decimal(2) | — |
total_hours | integer | — |
pdf_path | string | Relative path under storage/public/ |
data | JSON | Full raw request payload from the builder |
QuoteItem
QuoteItem
One row per selected block, linked to the parent
Quote:| Column | Type | Notes |
|---|---|---|
quote_id | integer | Foreign key to quotes |
quote_block_id | integer | Foreign key to quote_blocks (not nullable per migration) |
name | string | Block name at time of quote |
description | text | Nullable |
type | string | Block type |
quantity | integer | Default 1 |
hours | integer | — |
unit_price | decimal(2) | — |
total_price | decimal(2) | — |
data | JSON | Extra config from the block |
QuoteReply
QuoteReply
Represents one admin reply / meeting schedule action:
| Column | Type | Notes |
|---|---|---|
quote_id | integer | Foreign key to quotes |
sent_at | datetime | The scheduled meeting date/time |
The
message field is formatted by the controller and stored, but QuoteReply::$fillable only lists quote_id and sent_at. Review your migration if you need the message column to be queryable independently.Route reference
| Method | URI | Name | Description |
|---|---|---|---|
GET | /admin/cotizaciones | admin.quotes.index | Paginated list with filters |
GET | /admin/cotizaciones/{quote} | admin.quotes.show | Quote detail |
PATCH | /admin/cotizaciones/{quote}/status | admin.quotes.status | Update status |
POST | /admin/cotizaciones/{quote}/reply | admin.quotes.reply | Send meeting reply |
GET | /admin/cotizaciones/{quote}/pdf | admin.quotes.pdf | Download PDF |
auth + verified middleware.