sync: Auto-sync from ACG-M-L5090 at 2026-03-10 19:11:00
Synced files: - Quote wizard frontend (all components, hooks, types, config) - API updates (config, models, routers, schemas, services) - Client work (bg-builders, gurushow) - Scripts (BGB Lesley termination, CIPP, Datto, migration) - Temp files (Bardach contacts, VWP investigation, misc) - Credentials and session logs - Email service, PHP API, session logs Machine: ACG-M-L5090 Timestamp: 2026-03-10 19:11:00 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -78,8 +78,7 @@ def create_quote(
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"employee_count": 25,
|
||||
"notes": "Looking for complete managed services package"
|
||||
"employee_count": 25
|
||||
}
|
||||
```
|
||||
|
||||
@@ -159,7 +158,6 @@ def get_quote(
|
||||
"employee_count": 25,
|
||||
"monthly_total": "450.00",
|
||||
"setup_total": "500.00",
|
||||
"annual_total": "5900.00",
|
||||
"items": [
|
||||
{
|
||||
"id": "456e7890-e89b-12d3-a456-426614174001",
|
||||
@@ -185,19 +183,19 @@ def get_quote(
|
||||
item_dict = QuoteItemResponse(
|
||||
id=item.id,
|
||||
quote_id=item.quote_id,
|
||||
service_name=item.service_name,
|
||||
service_description=item.service_description,
|
||||
category=item.category,
|
||||
billing_frequency=item.billing_frequency,
|
||||
unit_price=item.unit_price,
|
||||
product_code=item.product_code,
|
||||
product_name=item.product_name,
|
||||
description=item.description,
|
||||
quantity=item.quantity,
|
||||
setup_fee=item.setup_fee,
|
||||
is_required=item.is_required,
|
||||
sort_order=item.sort_order,
|
||||
unit_price=item.unit_price,
|
||||
setup_price=item.setup_price,
|
||||
billing_frequency=item.billing_frequency,
|
||||
tier=item.tier,
|
||||
is_recommended=item.is_recommended,
|
||||
line_total=item.line_total,
|
||||
monthly_amount=item.monthly_amount,
|
||||
created_at=item.created_at,
|
||||
updated_at=item.updated_at
|
||||
)
|
||||
items_response.append(item_dict)
|
||||
|
||||
@@ -210,10 +208,8 @@ def get_quote(
|
||||
contact_email=quote.contact_email,
|
||||
contact_phone=quote.contact_phone,
|
||||
employee_count=quote.employee_count,
|
||||
notes=quote.notes,
|
||||
monthly_total=quote.monthly_total,
|
||||
setup_total=quote.setup_total,
|
||||
annual_total=quote.annual_total,
|
||||
expires_at=quote.expires_at,
|
||||
submitted_at=quote.submitted_at,
|
||||
created_at=quote.created_at,
|
||||
@@ -432,7 +428,7 @@ def remove_item(
|
||||
},
|
||||
},
|
||||
)
|
||||
def submit_quote(
|
||||
async def submit_quote_endpoint(
|
||||
access_token: str,
|
||||
submit_data: QuoteSubmit,
|
||||
request: Request,
|
||||
@@ -442,7 +438,7 @@ def submit_quote(
|
||||
Submit a quote with contact information.
|
||||
|
||||
This finalizes the quote and sends it for review. Contact information
|
||||
is required at this stage.
|
||||
is required at this stage. An email notification is sent to the admin.
|
||||
|
||||
**Example Request:**
|
||||
```json
|
||||
@@ -453,8 +449,7 @@ def submit_quote(
|
||||
"company_name": "Acme Corporation",
|
||||
"contact_name": "John Doe",
|
||||
"contact_email": "john.doe@acme.com",
|
||||
"contact_phone": "555-123-4567",
|
||||
"notes": "Please contact me to discuss implementation timeline."
|
||||
"contact_phone": "555-123-4567"
|
||||
}
|
||||
```
|
||||
|
||||
@@ -472,15 +467,62 @@ def submit_quote(
|
||||
}
|
||||
```
|
||||
"""
|
||||
import logging
|
||||
from api.config import get_settings
|
||||
from api.services.email_service import send_email, build_quote_notification_html
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
ip_address = get_client_ip(request)
|
||||
|
||||
quote_service.submit_quote(
|
||||
quote = quote_service.submit_quote(
|
||||
db=db,
|
||||
access_token=access_token,
|
||||
submit_data=submit_data,
|
||||
ip_address=ip_address
|
||||
)
|
||||
|
||||
# Send email notification (non-blocking, don't fail the request if email fails)
|
||||
try:
|
||||
settings = get_settings()
|
||||
items_data = [
|
||||
{
|
||||
"service_name": item.product_name,
|
||||
"billing_frequency": item.billing_frequency,
|
||||
"unit_price": str(item.unit_price),
|
||||
"quantity": item.quantity,
|
||||
}
|
||||
for item in quote.items
|
||||
]
|
||||
|
||||
html = build_quote_notification_html(
|
||||
company_name=submit_data.company_name,
|
||||
contact_name=submit_data.contact_name,
|
||||
contact_email=submit_data.contact_email,
|
||||
contact_phone=submit_data.contact_phone,
|
||||
monthly_total=str(quote.monthly_total),
|
||||
setup_total=str(quote.setup_total),
|
||||
items=items_data,
|
||||
notes=submit_data.notes,
|
||||
)
|
||||
|
||||
sent = await send_email(
|
||||
to_email=settings.ADMIN_NOTIFICATION_EMAIL,
|
||||
subject=f"New Quote Submission: {submit_data.company_name} - ${quote.monthly_total}/mo",
|
||||
body_html=html,
|
||||
)
|
||||
|
||||
# Update notification record status
|
||||
if quote.notifications:
|
||||
notification = quote.notifications[-1]
|
||||
notification.status = "sent" if sent else "failed"
|
||||
if not sent:
|
||||
notification.error_message = "Graph API send failed"
|
||||
db.commit()
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to send quote notification email: {e}")
|
||||
# Don't fail the submission - email is best-effort
|
||||
|
||||
return get_quote(access_token, db)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user