diff --git a/backend/common/packs/schema.py b/backend/common/packs/schema.py index a442074a..9388c434 100644 --- a/backend/common/packs/schema.py +++ b/backend/common/packs/schema.py @@ -326,6 +326,8 @@ def _validate_sample_data(where: str, sample: dict, raw: dict) -> None: COUNTRIES, CURRENCY_CODES, INDCHOICES, + LEAD_SOURCE, + LEAD_STATUS, PRIORITY_CHOICE, SOURCES, STAGES, @@ -432,6 +434,14 @@ def _validate_sample_data(where: str, sample: dict, raw: dict) -> None: _reject_unknown(at, row, SAMPLE_LEAD_KEYS) _require(at, row, "title") _validate_choice(at, row.get("stage"), "stage", lead_stages) + # Lead.source and Lead.status carry `choices` the applier never checks. + # Both were unvalidated here until three shipped packs were found writing + # source="website" — a value LEAD_SOURCE does not contain, so the demo row + # rendered as a blank dropdown and no source filter could reach it. The + # row looked fine in the database, which is precisely why this needs a + # schema check rather than trust. + _validate_choice(at, row.get("source"), "source", {c for c, _l in LEAD_SOURCE}) + _validate_choice(at, row.get("status"), "status", {c for c, _l in LEAD_STATUS}) _validate_choice(at, row.get("currency"), "currency", currencies) diff --git a/backend/common/tests/test_packs_content.py b/backend/common/tests/test_packs_content.py index 32e01c37..e6d93201 100644 --- a/backend/common/tests/test_packs_content.py +++ b/backend/common/tests/test_packs_content.py @@ -3,7 +3,12 @@ import pytest from common.packs.loader import get_registry from common.packs.schema import SAMPLE_ENTITY_KEYS -SHIPPED = {"real-estate", "education-admissions", "professional-services"} +SHIPPED = { + "real-estate", + "education-admissions", + "professional-services", + "events-weddings", +} def test_all_shipped_packs_load_and_validate(): diff --git a/backend/common/tests/test_packs_loader.py b/backend/common/tests/test_packs_loader.py index 38689737..e80b1c87 100644 --- a/backend/common/tests/test_packs_loader.py +++ b/backend/common/tests/test_packs_loader.py @@ -103,6 +103,29 @@ def test_task_stage_accepts_in_progress(): assert stages[0]["stage_type"] == "in_progress" +def _with_lead(lead: dict) -> dict: + return _minimal() | {"sample_data": {"leads": [{"title": "An enquiry"} | lead]}} + + +def test_sample_lead_source_outside_lead_source_is_rejected(): + # "website" reads as obviously valid and is not: LEAD_SOURCE has no such + # entry, and Django does not enforce choices on .create(). Three shipped + # packs carried this before the guard existed — the rows wrote cleanly and + # surfaced only as a blank source column no filter could match. + with pytest.raises(PackValidationError, match="source"): + validate_manifest(_with_lead({"source": "website"})) + + +def test_sample_lead_source_from_lead_source_is_accepted(): + manifest = validate_manifest(_with_lead({"source": "partner"})) + assert manifest["sample_data"]["leads"][0]["source"] == "partner" + + +def test_sample_lead_status_outside_lead_status_is_rejected(): + with pytest.raises(PackValidationError, match="status"): + validate_manifest(_with_lead({"status": "qualified"})) + + def test_registry_lookup_of_unknown_id_returns_none(): assert get_pack("no-such-pack") is None diff --git a/backend/packs/education-admissions.json b/backend/packs/education-admissions.json index cccd8d45..28623d75 100644 --- a/backend/packs/education-admissions.json +++ b/backend/packs/education-admissions.json @@ -246,7 +246,7 @@ "custom_fields": { "course_interest": "Data Science Fundamentals", "intake": "Spring 2027", "parent_name": "Karen Whitmore", "parent_phone": "+1 720 555 0102", "previous_qualification": "High school diploma" } }, { "title": "Weekend spoken English batch", "first_name": "Lucia", "last_name": "Moreau", "email": "lucia.moreau@example.com", "phone": "+1 305 555 0240", - "source": "website", "stage": "New enquiry", + "source": "other", "stage": "New enquiry", "opportunity_amount": 180, "currency": "USD", "custom_fields": { "course_interest": "Spoken English", "intake": "Fall 2026", "previous_qualification": "High school diploma" } }, { "title": "Employer-sponsored CPA seat", "first_name": "Nathan", "last_name": "Achebe", @@ -271,7 +271,7 @@ "custom_fields": { "course_interest": "MBA Foundation", "intake": "Winter 2027", "previous_qualification": "Bachelor's degree" } }, { "title": "IELTS for a visa application", "first_name": "Tariq", "last_name": "Rahman", "email": "tariq.rahman@example.com", "phone": "+1 832 555 0246", - "source": "website", "stage": "Demo class", + "source": "other", "stage": "Demo class", "opportunity_amount": 450, "currency": "USD", "custom_fields": { "course_interest": "IELTS Coaching", "intake": "Fall 2026", "previous_qualification": "Master's degree" } }, { "title": "Bootcamp enquiry from a referral", "first_name": "Ursula", "last_name": "Klein", @@ -316,7 +316,7 @@ "custom_fields": { "course_interest": "Full-Stack Web Development", "intake": "Summer 2027", "previous_qualification": "High school diploma" } }, { "title": "Withdrew before the demo class", "first_name": "Caleb", "last_name": "Nyong'o", "email": "caleb.nyongo@example.com", "phone": "+1 470 555 0257", - "source": "website", "stage": "Not joining", + "source": "other", "stage": "Not joining", "opportunity_amount": 3200, "currency": "USD", "custom_fields": { "course_interest": "Data Science Fundamentals", "intake": "Spring 2027", "previous_qualification": "Bachelor's in Physics" } } ] diff --git a/backend/packs/events-weddings.json b/backend/packs/events-weddings.json new file mode 100644 index 00000000..dd7c52ba --- /dev/null +++ b/backend/packs/events-weddings.json @@ -0,0 +1,306 @@ +{ + "id": "events-weddings", + "version": 1, + "name": "Events & weddings", + "description": "Wedding planners, event agencies and venues taking enquiries through to a delivered event.", + "terminology": { + "lead.singular": "Enquiry", + "lead.plural": "Enquiries", + "opportunity.singular": "Booking", + "opportunity.plural": "Bookings", + "account.singular": "Client", + "account.plural": "Clients" + }, + "lead_pipeline": { + "name": "Enquiries", + "description": "First enquiry through to a signed booking.", + "stages": [ + { "name": "New enquiry", "order": 1, "stage_type": "open", "color": "#6B7280" }, + { "name": "Consultation booked", "order": 2, "stage_type": "open", "color": "#3B82F6" }, + { "name": "Proposal sent", "order": 3, "stage_type": "open", "color": "#6366F1" }, + { "name": "Date held", "order": 4, "stage_type": "open", "color": "#8B5CF6" }, + { "name": "Contract sent", "order": 5, "stage_type": "open", "color": "#F59E0B" }, + { "name": "Booked", "order": 6, "stage_type": "won", "color": "#10B981" }, + { "name": "Date lost", "order": 7, "stage_type": "lost", "color": "#EF4444" } + ] + }, + "case_pipeline": { + "name": "Event delivery", + "description": "Changes and problems raised once a booking is confirmed.", + "stages": [ + { "name": "New issue", "order": 1, "stage_type": "open", "color": "#6B7280" }, + { "name": "With planner", "order": 2, "stage_type": "open", "color": "#3B82F6" }, + { "name": "Awaiting vendor", "order": 3, "stage_type": "open", "color": "#F59E0B" }, + { "name": "Awaiting client", "order": 4, "stage_type": "open", "color": "#EC4899" }, + { "name": "Resolved", "order": 5, "stage_type": "closed", "color": "#10B981" }, + { "name": "Not our remit", "order": 6, "stage_type": "rejected", "color": "#EF4444" } + ] + }, + "task_pipeline": { + "name": "Planning workflow", + "description": "Run-up work for a booked event.", + "stages": [ + { "name": "To do", "order": 1, "stage_type": "open", "color": "#6B7280" }, + { "name": "Scheduled", "order": 2, "stage_type": "open", "color": "#3B82F6" }, + { "name": "In progress", "order": 3, "stage_type": "in_progress", "color": "#8B5CF6" }, + { "name": "Done", "order": 4, "stage_type": "completed", "color": "#10B981" } + ] + }, + "custom_fields": [ + { "target": "Lead", "key": "event_type", "label": "Event type", "field_type": "dropdown", "is_filterable": true, "display_order": 1, + "options": [ + { "value": "wedding", "label": "Wedding" }, + { "value": "reception", "label": "Reception" }, + { "value": "engagement", "label": "Engagement party" }, + { "value": "milestone", "label": "Milestone celebration" }, + { "value": "corporate", "label": "Corporate event" }, + { "value": "conference", "label": "Conference" }, + { "value": "gala", "label": "Gala or fundraiser" } + ] }, + { "target": "Lead", "key": "event_date", "label": "Event date", "field_type": "date", "is_filterable": true, "display_order": 2 }, + { "target": "Lead", "key": "guest_count", "label": "Guest count", "field_type": "number", "is_filterable": true, "display_order": 3 }, + { "target": "Lead", "key": "venue", "label": "Venue", "field_type": "text", "display_order": 4 }, + { "target": "Lead", "key": "budget", "label": "Budget", "field_type": "number", "display_order": 5 }, + { "target": "Lead", "key": "venue_confirmed", "label": "Venue confirmed", "field_type": "checkbox", "display_order": 6 } + ], + "tags": [ + { "name": "Peak date", "color": "red", "description": "Falls in the peak season — the date will not hold for long" }, + { "name": "Destination", "color": "violet", "description": "Event away from the home city" }, + { "name": "Corporate", "color": "blue", "description": "Company event rather than a private celebration" }, + { "name": "Referral", "color": "teal", "description": "Introduced by a past client or a venue" } + ], + "products": [ + { "name": "Full planning & design", "sku": "EW-PLAN-FULL", "price": 6500, "category": "Planning", + "description": "End-to-end planning from concept and budget through to the day itself." }, + { "name": "Partial planning", "sku": "EW-PLAN-PARTIAL", "price": 3200, "category": "Planning", + "description": "Planning support from three months out, for a client who has already booked a venue." }, + { "name": "Day-of coordination", "sku": "EW-COORD-DAY", "price": 1500, "category": "Coordination", + "description": "On-site management of the run sheet, vendors and timings on the event day." }, + { "name": "Venue sourcing & site visits", "sku": "EW-VENUE-SRC", "price": 750, "category": "Planning", + "description": "Shortlist, availability checks and accompanied site visits." }, + { "name": "Decor & floral styling", "sku": "EW-DECOR", "price": 4200, "category": "Styling", + "description": "Design concept, florals, table styling and installation." }, + { "name": "Catering coordination (per guest)", "sku": "EW-CATERING-PG", "price": 85, "category": "Catering", + "description": "Menu design, tasting and service coordination; charged per confirmed guest." }, + { "name": "Photography & film package", "sku": "EW-PHOTO-FILM", "price": 2800, "category": "Media", + "description": "Full-day photography and a highlights film, edited and delivered digitally." }, + { "name": "Guest management & RSVP desk", "sku": "EW-GUEST-RSVP", "price": 900, "category": "Coordination", + "description": "Invitations, RSVP tracking, seating plan and a staffed welcome desk." } + ], + "sample_data": { + "accounts": [ + { "name": "Whitmore & Castellanos", "industry": "ENTERTAINMENT & LEISURE", "email": "eleanor.whitmore@example.com", + "phone": "+1 415 555 0410", "city": "San Francisco", "country": "US", + "description": "September wedding at Ashcombe Manor, 140 guests. Peak Saturday." }, + { "name": "The Ferris Family", "industry": "ENTERTAINMENT & LEISURE", "email": "margaret.ferris@example.com", + "phone": "+1 617 555 0411", "city": "Boston", "country": "US", + "description": "Golden wedding anniversary dinner, close family only." }, + { "name": "Northwind Analytics", "industry": "TECHNOLOGY", "email": "events@northwindanalytics.example.com", + "phone": "+1 206 555 0412", "website": "https://northwindanalytics.example.com", "city": "Seattle", "country": "US", + "description": "Annual customer conference plus an awards dinner on the second night." }, + { "name": "Reyes & Nandakumar", "industry": "ENTERTAINMENT & LEISURE", "email": "camila.reyes@example.com", + "phone": "+1 305 555 0413", "city": "Miami", "country": "US", + "description": "Destination wedding, three days, guests travelling from two countries." }, + { "name": "Halcyon Foundation", "industry": "SERVICE", "email": "gala@halcyonfoundation.example.com", + "phone": "+1 312 555 0414", "website": "https://halcyonfoundation.example.com", "city": "Chicago", "country": "US", + "description": "Autumn fundraising gala with a seated dinner and an auction." }, + { "name": "Bellweather Group", "industry": "FINANCE", "email": "office@bellweathergroup.example.com", + "phone": "+1 212 555 0415", "city": "New York", "country": "US", + "description": "Leadership retreat, offsite. Brought the planning in-house this year." } + ], + "contacts": [ + { "first_name": "Eleanor", "last_name": "Whitmore", "email": "eleanor.whitmore@example.com", "phone": "+1 415 555 0132", + "title": "Partner", "account": "Whitmore & Castellanos", "city": "San Francisco", "country": "US", + "description": "Main point of contact. Prefers evening calls." }, + { "first_name": "Diego", "last_name": "Castellanos", "email": "diego.castellanos@example.com", "phone": "+1 415 555 0133", + "title": "Partner", "account": "Whitmore & Castellanos", "city": "San Francisco", "country": "US", + "description": "Handles the music, bar and transport decisions." }, + { "first_name": "Margaret", "last_name": "Ferris", "email": "margaret.ferris@example.com", "phone": "+1 617 555 0141", + "title": "Host", "account": "The Ferris Family", "city": "Boston", "country": "US", + "description": "Organising the anniversary for her parents. Wants it kept simple." }, + { "first_name": "Aditi", "last_name": "Rao", "email": "aditi.rao@example.com", "phone": "+1 206 555 0152", + "title": "Events Manager", "department": "Marketing", "account": "Northwind Analytics", "city": "Seattle", "country": "US", + "description": "Owns the conference budget and signs off the AV spend." }, + { "first_name": "Camila", "last_name": "Reyes", "email": "camila.reyes@example.com", "phone": "+1 305 555 0163", + "title": "Partner", "account": "Reyes & Nandakumar", "city": "Miami", "country": "US", + "description": "Coordinating the guest travel and the welcome dinner." }, + { "first_name": "Priya", "last_name": "Nandakumar", "email": "priya.nandakumar@example.com", "phone": "+1 305 555 0164", + "title": "Partner", "account": "Reyes & Nandakumar", "city": "Miami", "country": "US", + "description": "Leading on decor, florals and the ceremony running order." }, + { "first_name": "Jonah", "last_name": "Klein", "email": "jonah.klein@example.com", "phone": "+1 312 555 0175", + "title": "Programme Director", "account": "Halcyon Foundation", "city": "Chicago", "country": "US", + "description": "Needs the auction and the speeches to fit inside a two-hour dinner." }, + { "first_name": "Rosalind", "last_name": "Beck", "email": "rosalind.beck@example.com", "phone": "+1 212 555 0186", + "title": "Executive Assistant", "account": "Bellweather Group", "city": "New York", "country": "US", + "description": "Booked the retreat directly with the venue this year." } + ], + "deals": [ + { "name": "Whitmore & Castellanos wedding — 12 September", "account": "Whitmore & Castellanos", "stage": "NEGOTIATION", + "amount": 48000, "currency": "USD", "probability": 75, "closed_on": "2026-08-20", "lead_source": "PARTNER", + "description": "Full planning plus decor. Negotiating the bar package and the late-night transport." }, + { "name": "Ferris golden anniversary dinner", "account": "The Ferris Family", "stage": "PROPOSAL", + "amount": 12500, "currency": "USD", "probability": 50, "closed_on": "2026-09-05", "lead_source": "EXISTING CUSTOMER", + "description": "Sixty guests, private dining room, one long table. Proposal sent, awaiting the family." }, + { "name": "Northwind annual conference & awards night", "account": "Northwind Analytics", "stage": "QUALIFICATION", + "amount": 96000, "currency": "USD", "probability": 30, "closed_on": "2026-11-30", "lead_source": "WEBSITE", + "description": "Two days, 400 delegates, awards dinner on the second night. AV is the open question." }, + { "name": "Reyes & Nandakumar destination wedding", "account": "Reyes & Nandakumar", "stage": "CLOSED_WON", + "amount": 78000, "currency": "USD", "probability": 100, "closed_on": "2026-07-15", "lead_source": "EXISTING CUSTOMER", + "description": "Booked. Three days in February, welcome dinner, ceremony and a farewell brunch." }, + { "name": "Halcyon autumn fundraising gala", "account": "Halcyon Foundation", "stage": "PROSPECTING", + "amount": 34000, "currency": "USD", "probability": 10, "closed_on": "2026-10-15", "lead_source": "PUBLIC RELATIONS", + "description": "Early conversation. Date not fixed and the venue shortlist is still open." }, + { "name": "Bellweather leadership retreat", "account": "Bellweather Group", "stage": "CLOSED_LOST", + "amount": 22000, "currency": "USD", "probability": 0, "closed_on": "2026-07-08", "lead_source": "CALL", + "description": "Lost. The venue offered a package directly and they booked it in-house." } + ], + "tickets": [ + { "name": "Florist cannot source peonies for the ceremony", "status": "Assigned", "priority": "High", "case_type": "Problem", + "account": "Whitmore & Castellanos", "contacts": ["Eleanor Whitmore"], "stage": "With planner", + "description": "Out of season by the wedding date. Two substitutes costed, waiting on the couple to choose." }, + { "name": "Guest count up by eighteen after the invitations went out", "status": "New", "priority": "High", "case_type": "Question", + "account": "The Ferris Family", "contacts": ["Margaret Ferris"], "stage": "New issue", + "description": "Pushes the room over its seated capacity. Needs a seating plan and a catering revision." }, + { "name": "Venue moved the load-in time to 6am", "status": "Pending", "priority": "Urgent", "case_type": "Incident", + "account": "Reyes & Nandakumar", "contacts": ["Camila Reyes"], "stage": "Awaiting vendor", + "description": "Clashes with the decor crew's call time. Waiting on the venue to confirm the earlier slot." }, + { "name": "AV quote came back over the approved budget", "status": "Closed", "priority": "Normal", "case_type": "Question", + "account": "Northwind Analytics", "contacts": ["Aditi Rao"], "stage": "Resolved", + "description": "Rescoped to two screens and one operator. Revised quote accepted." }, + { "name": "Request to arrange guest visas", "status": "Rejected", "priority": "Low", "case_type": "Question", + "account": "Reyes & Nandakumar", "contacts": ["Priya Nandakumar"], "stage": "Not our remit", + "description": "Outside the planning scope. Sent the couple a travel agent who handles this." } + ], + "tasks": [ + { "title": "Confirm the peony substitute with the couple", "status": "In Progress", "priority": "High", "due_date": "2026-08-05", + "stage": "In progress", "ticket": "Florist cannot source peonies for the ceremony", + "description": "Two options costed. Eleanor prefers the garden roses; needs Diego to agree." }, + { "title": "Re-run the seating plan for eighteen more guests", "status": "New", "priority": "High", "due_date": "2026-08-07", + "stage": "To do", "ticket": "Guest count up by eighteen after the invitations went out", + "description": "Check the room's seated capacity before promising anything to the family." }, + { "title": "Send the revised AV quote to Aditi", "status": "Completed", "priority": "Medium", "due_date": "2026-07-28", + "stage": "Done", "ticket": "AV quote came back over the approved budget", + "description": "Two screens, one operator. Signed off on the call." }, + { "title": "Chase the signed contract and the deposit", "status": "In Progress", "priority": "High", "due_date": "2026-08-06", + "stage": "In progress", "deal": "Whitmore & Castellanos wedding — 12 September", + "description": "Date is a peak Saturday and is only held until the deposit clears." }, + { "title": "Send the anniversary proposal to the family", "status": "New", "priority": "High", "due_date": "2026-08-04", + "stage": "Scheduled", "deal": "Ferris golden anniversary dinner", + "description": "Private dining room, one long table, set menu with two choices." }, + { "title": "Walk the destination venue and photograph the ceremony space", "status": "Completed", "priority": "Medium", + "due_date": "2026-07-22", "stage": "Done", "deal": "Reyes & Nandakumar destination wedding", + "description": "Site visit done. Photos and the measured floor plan are in the shared folder." }, + { "title": "Quarterly check-in with Northwind", "status": "New", "priority": "Low", "due_date": "2026-09-01", + "stage": "To do", "account": "Northwind Analytics", + "description": "Review next year's calendar before the conference budget is set." }, + { "title": "Call back the New Year's Eve enquiry", "status": "New", "priority": "Medium", "due_date": "2026-08-03", + "stage": "To do", "lead": "New Year's Eve reception for 200", + "description": "First attempt went to voicemail. The date will go quickly." }, + { "title": "Send the destination package to the Goa enquiry", "status": "In Progress", "priority": "Medium", + "due_date": "2026-08-08", "stage": "In progress", "lead": "Destination wedding in Goa", + "description": "Include the travel supplement and the three-day run sheet." } + ], + "leads": [ + { "title": "September wedding at Ashcombe Manor", "first_name": "Eleanor", "last_name": "Whitmore", + "email": "eleanor.whitmore@example.com", "phone": "+1 415 555 0132", + "source": "partner", "stage": "Contract sent", + "custom_fields": { "event_type": "wedding", "event_date": "2026-09-12", "guest_count": 140, "venue": "Ashcombe Manor", "budget": 48000, "venue_confirmed": true } }, + { "title": "Destination wedding in Goa", "first_name": "Camila", "last_name": "Reyes", + "email": "camila.reyes@example.com", "phone": "+1 305 555 0163", + "source": "existing customer", "stage": "Booked", + "custom_fields": { "event_type": "wedding", "event_date": "2027-02-06", "guest_count": 90, "venue": "Coral Sands Resort", "budget": 78000, "venue_confirmed": true } }, + { "title": "New Year's Eve reception for 200", "first_name": "Dominic", "last_name": "Vale", + "email": "dominic.vale@example.com", "phone": "+1 646 555 0416", + "source": "compaign", "stage": "New enquiry", + "custom_fields": { "event_type": "reception", "event_date": "2026-12-31", "guest_count": 200, "venue": "The Old Exchange", "budget": 55000, "venue_confirmed": false } }, + { "title": "Golden anniversary dinner for sixty", "first_name": "Margaret", "last_name": "Ferris", + "email": "margaret.ferris@example.com", "phone": "+1 617 555 0141", + "source": "existing customer", "stage": "Proposal sent", + "custom_fields": { "event_type": "milestone", "event_date": "2026-10-03", "guest_count": 60, "venue": "Harbour House", "budget": 12500, "venue_confirmed": true } }, + { "title": "Annual customer conference and awards night", "first_name": "Aditi", "last_name": "Rao", + "email": "aditi.rao@example.com", "phone": "+1 206 555 0152", + "source": "other", "stage": "Consultation booked", + "custom_fields": { "event_type": "conference", "event_date": "2027-03-18", "guest_count": 400, "venue": "Pier 66 Convention Centre", "budget": 96000, "venue_confirmed": false } }, + { "title": "Autumn fundraising gala with an auction", "first_name": "Jonah", "last_name": "Klein", + "email": "jonah.klein@example.com", "phone": "+1 312 555 0175", + "source": "public relations", "stage": "New enquiry", + "custom_fields": { "event_type": "gala", "event_date": "2026-11-14", "guest_count": 320, "venue": "Lakeside Ballroom", "budget": 34000, "venue_confirmed": false } }, + { "title": "Leadership retreat, two nights offsite", "first_name": "Rosalind", "last_name": "Beck", + "email": "rosalind.beck@example.com", "phone": "+1 212 555 0186", + "source": "call", "stage": "Date lost", + "custom_fields": { "event_type": "corporate", "event_date": "2026-09-24", "guest_count": 45, "venue": "Stonebridge Lodge", "budget": 22000, "venue_confirmed": true } }, + { "title": "Spring wedding in the vineyard", "first_name": "Isabelle", "last_name": "Moreau", + "email": "isabelle.moreau@example.com", "phone": "+1 707 555 0417", + "source": "partner", "stage": "Date held", + "custom_fields": { "event_type": "wedding", "event_date": "2027-05-08", "guest_count": 110, "venue": "Rivenwood Vineyard", "budget": 52000, "venue_confirmed": true } }, + { "title": "Engagement party, rooftop bar", "first_name": "Theo", "last_name": "Lindgren", + "email": "theo.lindgren@example.com", "phone": "+1 415 555 0418", + "source": "compaign", "stage": "Consultation booked", + "custom_fields": { "event_type": "engagement", "event_date": "2026-09-19", "guest_count": 70, "venue": "Skyline Terrace", "budget": 9500, "venue_confirmed": false } }, + { "title": "Product launch dinner for press", "first_name": "Naomi", "last_name": "Osei", + "email": "naomi.osei@example.com", "phone": "+1 206 555 0419", + "source": "public relations", "stage": "Proposal sent", + "custom_fields": { "event_type": "corporate", "event_date": "2026-10-22", "guest_count": 85, "venue": "The Foundry", "budget": 28000, "venue_confirmed": true } }, + { "title": "Beach wedding, barefoot ceremony", "first_name": "Marcus", "last_name": "Ilunga", + "email": "marcus.ilunga@example.com", "phone": "+1 786 555 0420", + "source": "partner", "stage": "New enquiry", + "custom_fields": { "event_type": "wedding", "event_date": "2027-04-17", "guest_count": 65, "venue": "Coral Bay", "budget": 31000, "venue_confirmed": false } }, + { "title": "Sixtieth birthday, black tie", "first_name": "Alan", "last_name": "Whitcombe", + "email": "alan.whitcombe@example.com", "phone": "+1 503 555 0421", + "source": "existing customer", "stage": "Contract sent", + "custom_fields": { "event_type": "milestone", "event_date": "2026-11-07", "guest_count": 120, "venue": "Grand Assembly Rooms", "budget": 26000, "venue_confirmed": true } }, + { "title": "Two-day summit with a gala dinner", "first_name": "Hassan", "last_name": "Qureshi", + "email": "hassan.qureshi@example.com", "phone": "+1 972 555 0422", + "source": "other", "stage": "Consultation booked", + "custom_fields": { "event_type": "conference", "event_date": "2027-06-11", "guest_count": 250, "venue": "Northgate Centre", "budget": 71000, "venue_confirmed": false } }, + { "title": "Winter wedding, candlelit ceremony", "first_name": "Rosa", "last_name": "Iglesias", + "email": "rosa.iglesias@example.com", "phone": "+1 619 555 0423", + "source": "compaign", "stage": "Date held", + "custom_fields": { "event_type": "wedding", "event_date": "2026-12-19", "guest_count": 95, "venue": "St Aubyn's Hall", "budget": 44000, "venue_confirmed": true } }, + { "title": "Charity lunch for two hundred", "first_name": "Grace", "last_name": "Mbeki", + "email": "grace.mbeki@example.com", "phone": "+1 470 555 0424", + "source": "public relations", "stage": "Proposal sent", + "custom_fields": { "event_type": "gala", "event_date": "2026-10-08", "guest_count": 200, "venue": "Civic Hall", "budget": 19000, "venue_confirmed": true } }, + { "title": "Wedding moved from another planner", "first_name": "Beatrice", "last_name": "Lindqvist", + "email": "beatrice.lindqvist@example.com", "phone": "+1 646 555 0425", + "source": "existing customer", "stage": "Date held", + "custom_fields": { "event_type": "wedding", "event_date": "2027-07-24", "guest_count": 130, "venue": "Marlowe Court", "budget": 61000, "venue_confirmed": true } }, + { "title": "Team offsite with an evening reception", "first_name": "Peter", "last_name": "Nakamura", + "email": "peter.nakamura@example.com", "phone": "+1 415 555 0426", + "source": "call", "stage": "New enquiry", + "custom_fields": { "event_type": "corporate", "event_date": "2026-11-20", "guest_count": 55, "venue": "Undecided", "budget": 17500, "venue_confirmed": false } }, + { "title": "Engagement dinner, family only", "first_name": "Amara", "last_name": "Diallo", + "email": "amara.diallo@example.com", "phone": "+1 404 555 0427", + "source": "compaign", "stage": "Booked", + "custom_fields": { "event_type": "engagement", "event_date": "2026-09-05", "guest_count": 30, "venue": "The Walled Garden", "budget": 6800, "venue_confirmed": true } }, + { "title": "Wedding cancelled, couple postponed", "first_name": "Owen", "last_name": "Griffiths", + "email": "owen.griffiths@example.com", "phone": "+1 720 555 0428", + "source": "partner", "stage": "Date lost", + "custom_fields": { "event_type": "wedding", "event_date": "2027-01-30", "guest_count": 100, "venue": "Fairhaven Barn", "budget": 39000, "venue_confirmed": false } }, + { "title": "Awards evening for a trade body", "first_name": "Victor", "last_name": "Nwosu", + "email": "victor.nwosu@example.com", "phone": "+1 312 555 0429", + "source": "other", "stage": "Consultation booked", + "custom_fields": { "event_type": "corporate", "event_date": "2027-02-27", "guest_count": 300, "venue": "Union Pavilion", "budget": 58000, "venue_confirmed": false } }, + { "title": "Renewal of vows on the anniversary", "first_name": "Claire", "last_name": "Dubois", + "email": "claire.dubois@example.com", "phone": "+1 646 555 0430", + "source": "existing customer", "stage": "Proposal sent", + "custom_fields": { "event_type": "milestone", "event_date": "2027-05-30", "guest_count": 50, "venue": "Rosecliff House", "budget": 15000, "venue_confirmed": true } }, + { "title": "Marquee wedding on a family farm", "first_name": "Nils", "last_name": "Andersen", + "email": "nils.andersen@example.com", "phone": "+1 214 555 0431", + "source": "call", "stage": "New enquiry", + "custom_fields": { "event_type": "wedding", "event_date": "2027-08-14", "guest_count": 180, "venue": "Private land, marquee hire", "budget": 67000, "venue_confirmed": false } }, + { "title": "Graduation reception for a school", "first_name": "Priya", "last_name": "Raman", + "email": "priya.raman@example.com", "phone": "+1 617 555 0432", + "source": "partner", "stage": "Contract sent", + "custom_fields": { "event_type": "reception", "event_date": "2027-06-26", "guest_count": 220, "venue": "Maple Heights Hall", "budget": 21000, "venue_confirmed": true } }, + { "title": "Wedding lost to a cheaper quote", "first_name": "Sinead", "last_name": "Murphy", + "email": "sinead.murphy@example.com", "phone": "+1 617 555 0433", + "source": "compaign", "stage": "Date lost", + "custom_fields": { "event_type": "wedding", "event_date": "2027-03-13", "guest_count": 85, "venue": "Ellesmere Hotel", "budget": 29000, "venue_confirmed": false } }, + { "title": "Summer party for a client's staff", "first_name": "Daniel", "last_name": "Ferreira", + "email": "daniel.ferreira@example.com", "phone": "+1 971 555 0434", + "source": "existing customer", "stage": "Booked", + "custom_fields": { "event_type": "corporate", "event_date": "2027-07-10", "guest_count": 160, "venue": "Riverside Gardens", "budget": 24000, "venue_confirmed": true } } + ] + } +} diff --git a/backend/packs/professional-services.json b/backend/packs/professional-services.json index 904dfb80..024b8d95 100644 --- a/backend/packs/professional-services.json +++ b/backend/packs/professional-services.json @@ -245,7 +245,7 @@ "custom_fields": { "service_line": "Brand Strategy", "engagement_type": "project", "estimated_start": "2026-10-20" } }, { "title": "Payroll migration for a 200-person firm", "first_name": "Tobias", "last_name": "Werner", "email": "tobias.werner@example.com", "phone": "+1 312 555 0220", - "source": "website", "stage": "Qualified", + "source": "other", "stage": "Qualified", "opportunity_amount": 8400, "currency": "USD", "custom_fields": { "service_line": "Accounting & Bookkeeping", "engagement_type": "project", "estimated_start": "2026-10-05" } }, { "title": "Go-to-market strategy for a hardware launch", "first_name": "Elena", "last_name": "Vasquez", @@ -265,7 +265,7 @@ "custom_fields": { "service_line": "Technology Advisory", "engagement_type": "timeandmaterials", "estimated_start": "2026-09-25" } }, { "title": "Employee handbook rewrite", "first_name": "Marcus", "last_name": "O'Rourke", "email": "marcus.orourke@example.com", "phone": "+1 646 555 0224", - "source": "website", "stage": "Qualified", + "source": "other", "stage": "Qualified", "opportunity_amount": 5600, "currency": "USD", "custom_fields": { "service_line": "HR Consulting", "engagement_type": "project", "estimated_start": "2026-09-15" } }, { "title": "Annual accounts and corporation tax", "first_name": "Fatima", "last_name": "Haddad", @@ -290,7 +290,7 @@ "custom_fields": { "service_line": "Interim CFO", "engagement_type": "retainer", "estimated_start": "2026-10-15" } }, { "title": "Process mapping for a claims team", "first_name": "Hiroshi", "last_name": "Tanaka", "email": "hiroshi.tanaka@example.com", "phone": "+1 206 555 0229", - "source": "website", "stage": "Qualified", + "source": "other", "stage": "Qualified", "opportunity_amount": 9600, "currency": "USD", "custom_fields": { "service_line": "Compliance Review", "engagement_type": "project", "estimated_start": "2026-09-22" } }, { "title": "Pitch declined, went in-house", "first_name": "Bianca", "last_name": "Rossi", @@ -315,7 +315,7 @@ "custom_fields": { "service_line": "Interim CFO", "engagement_type": "project", "estimated_start": "2026-10-28" } }, { "title": "Cyber compliance readiness review", "first_name": "Anneke", "last_name": "Visser", "email": "anneke.visser@example.com", "phone": "+1 503 555 0234", - "source": "website", "stage": "Scoping", + "source": "other", "stage": "Scoping", "opportunity_amount": 13500, "currency": "USD", "custom_fields": { "service_line": "Compliance Review", "engagement_type": "timeandmaterials", "estimated_start": "2026-11-10" } } ] diff --git a/backend/packs/real-estate.json b/backend/packs/real-estate.json index b1bc631b..920ce7d0 100644 --- a/backend/packs/real-estate.json +++ b/backend/packs/real-estate.json @@ -235,7 +235,7 @@ "custom_fields": { "budget_max": 390000, "configuration": "2bed", "preferred_location": "Elm Terrace", "possession_by": "2027-01-15", "finance_required": false } }, { "title": "Relocating for work, needs 3 bed", "first_name": "Daniel", "last_name": "Ferreira", "email": "daniel.ferreira@example.com", "phone": "+1 971 555 0202", - "source": "website", "stage": "Contacted", + "source": "other", "stage": "Contacted", "custom_fields": { "budget_max": 720000, "configuration": "3bed", "preferred_location": "Oakwood Park", "possession_by": "2026-12-01", "finance_required": true } }, { "title": "Second home by the water", "first_name": "Claire", "last_name": "Dubois", "email": "claire.dubois@example.com", "phone": "+1 646 555 0203", @@ -247,7 +247,7 @@ "custom_fields": { "budget_max": 900000, "configuration": "1bed", "preferred_location": "Riverside", "finance_required": false } }, { "title": "First-time buyer, studio budget", "first_name": "Amara", "last_name": "Diallo", "email": "amara.diallo@example.com", "phone": "+1 404 555 0205", - "source": "website", "stage": "New enquiry", + "source": "other", "stage": "New enquiry", "custom_fields": { "budget_max": 185000, "configuration": "studio", "preferred_location": "Downtown Loop", "finance_required": true } }, { "title": "Plot enquiry from a builder", "first_name": "Nils", "last_name": "Andersen", "email": "nils.andersen@example.com", "phone": "+1 214 555 0206", @@ -263,7 +263,7 @@ "custom_fields": { "budget_max": 340000, "configuration": "1bed", "preferred_location": "Marina District", "possession_by": "2027-09-01", "finance_required": true } }, { "title": "Withdrew after the survey", "first_name": "Sinead", "last_name": "Murphy", "email": "sinead.murphy@example.com", "phone": "+1 617 555 0209", - "source": "website", "stage": "Fell through", + "source": "other", "stage": "Fell through", "custom_fields": { "budget_max": 465000, "configuration": "2bed", "preferred_location": "Maple Heights", "finance_required": true } }, { "title": "Commercial plot, roadside frontage", "first_name": "Hassan", "last_name": "Qureshi", "email": "hassan.qureshi@example.com", "phone": "+1 972 555 0210",