import { Pool } from 'pg'; import { resolveDatabasePoolConfig } from '../src/services/db.js'; import { V2DemoFixture as fixture } from './v2-demo-fixture.mjs'; const poolConfig = resolveDatabasePoolConfig(); if (!poolConfig) { // eslint-disable-next-line no-console console.error('Database connection settings are required'); process.exit(1); } const pool = new Pool(poolConfig); function hoursFromNow(hours) { return new Date(Date.now() + (hours * 60 * 60 * 1000)).toISOString(); } async function upsertUser(client, user) { await client.query( ` INSERT INTO users (id, email, display_name, status, metadata) VALUES ($1, $2, $3, 'ACTIVE', '{}'::jsonb) ON CONFLICT (id) DO UPDATE SET email = EXCLUDED.email, display_name = EXCLUDED.display_name, status = 'ACTIVE', updated_at = NOW() `, [user.id, user.email || null, user.displayName || null] ); } async function main() { const client = await pool.connect(); try { await client.query('BEGIN'); await client.query('DELETE FROM tenants WHERE id = $1', [fixture.tenant.id]); const openStartsAt = hoursFromNow(4); const openEndsAt = hoursFromNow(12); const completedStartsAt = hoursFromNow(-28); const completedEndsAt = hoursFromNow(-20); const checkedInAt = hoursFromNow(-27.5); const checkedOutAt = hoursFromNow(-20.25); const assignedStartsAt = hoursFromNow(0.1); const assignedEndsAt = hoursFromNow(8.1); const availableStartsAt = hoursFromNow(30); const availableEndsAt = hoursFromNow(38); const cancelledStartsAt = hoursFromNow(20); const cancelledEndsAt = hoursFromNow(28); const noShowStartsAt = hoursFromNow(-18); const noShowEndsAt = hoursFromNow(-10); const invoiceDueAt = hoursFromNow(72); await upsertUser(client, fixture.users.businessOwner); await upsertUser(client, fixture.users.operationsManager); await upsertUser(client, fixture.users.vendorManager); await upsertUser(client, fixture.users.staffAna); await client.query( ` INSERT INTO tenants (id, slug, name, status, metadata) VALUES ($1, $2, $3, 'ACTIVE', $4::jsonb) `, [fixture.tenant.id, fixture.tenant.slug, fixture.tenant.name, JSON.stringify({ seededBy: 'seed-v2-demo-data' })] ); await client.query( ` INSERT INTO tenant_memberships (tenant_id, user_id, membership_status, base_role, metadata) VALUES ($1, $2, 'ACTIVE', 'admin', '{"persona":"business_owner"}'::jsonb), ($1, $3, 'ACTIVE', 'manager', '{"persona":"ops_manager"}'::jsonb), ($1, $4, 'ACTIVE', 'manager', '{"persona":"vendor_manager"}'::jsonb), ($1, $5, 'ACTIVE', 'member', '{"persona":"staff"}'::jsonb) `, [ fixture.tenant.id, fixture.users.businessOwner.id, fixture.users.operationsManager.id, fixture.users.vendorManager.id, fixture.users.staffAna.id, ] ); await client.query( ` INSERT INTO businesses ( id, tenant_id, slug, business_name, status, contact_name, contact_email, contact_phone, metadata ) VALUES ($1, $2, $3, $4, 'ACTIVE', $5, $6, $7, $8::jsonb) `, [ fixture.business.id, fixture.tenant.id, fixture.business.slug, fixture.business.name, 'Legendary Client Manager', fixture.users.businessOwner.email, '+15550001001', JSON.stringify({ segment: 'buyer', seeded: true }), ] ); await client.query( ` INSERT INTO business_memberships ( tenant_id, business_id, user_id, membership_status, business_role, metadata ) VALUES ($1, $2, $3, 'ACTIVE', 'owner', '{"persona":"client_owner"}'::jsonb), ($1, $2, $4, 'ACTIVE', 'manager', '{"persona":"client_ops"}'::jsonb) `, [fixture.tenant.id, fixture.business.id, fixture.users.businessOwner.id, fixture.users.operationsManager.id] ); await client.query( ` INSERT INTO vendors ( id, tenant_id, slug, company_name, status, contact_name, contact_email, contact_phone, metadata ) VALUES ($1, $2, $3, $4, 'ACTIVE', $5, $6, $7, $8::jsonb) `, [ fixture.vendor.id, fixture.tenant.id, fixture.vendor.slug, fixture.vendor.name, 'Vendor Manager', fixture.users.vendorManager.email, '+15550001002', JSON.stringify({ kind: 'internal_pool', seeded: true }), ] ); await client.query( ` INSERT INTO vendor_memberships ( tenant_id, vendor_id, user_id, membership_status, vendor_role, metadata ) VALUES ($1, $2, $3, 'ACTIVE', 'owner', '{"persona":"vendor_owner"}'::jsonb) `, [fixture.tenant.id, fixture.vendor.id, fixture.users.vendorManager.id] ); await client.query( ` INSERT INTO cost_centers (id, tenant_id, business_id, code, name, status, metadata) VALUES ($1, $2, $3, 'CAFE_OPS', $4, 'ACTIVE', '{"seeded":true}'::jsonb) `, [fixture.costCenters.cafeOps.id, fixture.tenant.id, fixture.business.id, fixture.costCenters.cafeOps.name] ); await client.query( ` INSERT INTO roles_catalog (id, tenant_id, code, name, status, metadata) VALUES ($1, $3, $4, $5, 'ACTIVE', '{}'::jsonb), ($2, $3, $6, $7, 'ACTIVE', '{}'::jsonb) `, [ fixture.roles.barista.id, fixture.roles.captain.id, fixture.tenant.id, fixture.roles.barista.code, fixture.roles.barista.name, fixture.roles.captain.code, fixture.roles.captain.name, ] ); await client.query( ` INSERT INTO staffs ( id, tenant_id, user_id, full_name, email, phone, status, primary_role, onboarding_status, average_rating, rating_count, metadata ) VALUES ($1, $2, $3, $4, $5, $6, 'ACTIVE', $7, 'COMPLETED', 4.50, 1, $8::jsonb) `, [ fixture.staff.ana.id, fixture.tenant.id, fixture.users.staffAna.id, fixture.staff.ana.fullName, fixture.staff.ana.email, fixture.staff.ana.phone, fixture.staff.ana.primaryRole, JSON.stringify({ favoriteCandidate: true, seeded: true, firstName: 'Ana', lastName: 'Barista', bio: 'Experienced barista and event staffing professional.', preferredLocations: [ { city: 'Mountain View', latitude: fixture.clockPoint.latitude, longitude: fixture.clockPoint.longitude, }, ], maxDistanceMiles: 20, industries: ['CATERING', 'CAFE'], skills: ['BARISTA', 'CUSTOMER_SERVICE'], emergencyContact: { name: 'Maria Barista', phone: '+15550007777', }, }), ] ); await client.query( ` INSERT INTO staff_roles (staff_id, role_id, is_primary) VALUES ($1, $2, TRUE) `, [fixture.staff.ana.id, fixture.roles.barista.id] ); await client.query( ` INSERT INTO workforce (id, tenant_id, vendor_id, staff_id, workforce_number, employment_type, status, metadata) VALUES ($1, $2, $3, $4, $5, 'TEMP', 'ACTIVE', $6::jsonb) `, [ fixture.workforce.ana.id, fixture.tenant.id, fixture.vendor.id, fixture.staff.ana.id, fixture.workforce.ana.workforceNumber, JSON.stringify({ source: 'seed-v2-demo' }), ] ); await client.query( ` INSERT INTO staff_availability ( id, tenant_id, staff_id, day_of_week, availability_status, time_slots, metadata ) VALUES ($1, $3, $4, 1, 'PARTIAL', '[{"start":"08:00","end":"18:00"}]'::jsonb, '{"seeded":true}'::jsonb), ($2, $3, $4, 5, 'PARTIAL', '[{"start":"09:00","end":"17:00"}]'::jsonb, '{"seeded":true}'::jsonb) `, [fixture.availability.monday.id, fixture.availability.friday.id, fixture.tenant.id, fixture.staff.ana.id] ); await client.query( ` INSERT INTO staff_benefits ( id, tenant_id, staff_id, benefit_type, title, status, tracked_hours, target_hours, metadata ) VALUES ($1, $2, $3, 'COMMUTER', $4, 'ACTIVE', 32, 40, '{"description":"Commuter stipend unlocked after 40 hours"}'::jsonb) `, [fixture.benefits.commuter.id, fixture.tenant.id, fixture.staff.ana.id, fixture.benefits.commuter.title] ); await client.query( ` INSERT INTO emergency_contacts ( id, tenant_id, staff_id, full_name, phone, relationship_type, is_primary, metadata ) VALUES ($1, $2, $3, 'Maria Barista', '+15550007777', 'SIBLING', TRUE, '{"seeded":true}'::jsonb) `, [fixture.emergencyContacts.primary.id, fixture.tenant.id, fixture.staff.ana.id] ); await client.query( ` INSERT INTO clock_points ( id, tenant_id, business_id, cost_center_id, label, address, latitude, longitude, geofence_radius_meters, nfc_tag_uid, default_clock_in_mode, allow_clock_in_override, status, metadata ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, 'ACTIVE', $13::jsonb) `, [ fixture.clockPoint.id, fixture.tenant.id, fixture.business.id, fixture.costCenters.cafeOps.id, fixture.clockPoint.label, fixture.clockPoint.address, fixture.clockPoint.latitude, fixture.clockPoint.longitude, fixture.clockPoint.geofenceRadiusMeters, fixture.clockPoint.nfcTagUid, fixture.clockPoint.defaultClockInMode, fixture.clockPoint.allowClockInOverride, JSON.stringify({ city: 'Mountain View', state: 'CA', zipCode: '94043', seeded: true }), ] ); await client.query( ` INSERT INTO hub_managers (id, tenant_id, hub_id, business_membership_id) SELECT $1, $2, $3, bm.id FROM business_memberships bm WHERE bm.business_id = $4 AND bm.user_id = $5 `, [ fixture.hubManagers.opsLead.id, fixture.tenant.id, fixture.clockPoint.id, fixture.business.id, fixture.users.operationsManager.id, ] ); await client.query( ` INSERT INTO orders ( id, tenant_id, business_id, vendor_id, order_number, title, description, status, service_type, starts_at, ends_at, location_name, location_address, latitude, longitude, notes, created_by_user_id, metadata ) VALUES ($1, $3, $4, $5, $6, $7, 'Open order for live v2 commands', 'OPEN', 'EVENT', $8, $9, 'Google Cafe', $10, $11, $12, 'Use this order for live smoke and frontend reads', $13, '{"slice":"open","orderType":"ONE_TIME"}'::jsonb), ($2, $3, $4, $5, $14, $15, 'Completed order for favorites, reviews, invoices, and attendance history', 'COMPLETED', 'CATERING', $16, $17, 'Google Catering', $10, $11, $12, 'Completed historical example', $13, '{"slice":"completed","orderType":"ONE_TIME"}'::jsonb) `, [ fixture.orders.open.id, fixture.orders.completed.id, fixture.tenant.id, fixture.business.id, fixture.vendor.id, fixture.orders.open.number, fixture.orders.open.title, openStartsAt, openEndsAt, fixture.clockPoint.address, fixture.clockPoint.latitude, fixture.clockPoint.longitude, fixture.users.businessOwner.id, fixture.orders.completed.number, fixture.orders.completed.title, completedStartsAt, completedEndsAt, ] ); await client.query( ` INSERT INTO orders ( id, tenant_id, business_id, vendor_id, order_number, title, description, status, service_type, starts_at, ends_at, location_name, location_address, latitude, longitude, notes, created_by_user_id, metadata ) VALUES ( $1, $2, $3, $4, $5, $6, 'Active order used to populate assigned, available, cancelled, and no-show shift states', 'ACTIVE', 'RESTAURANT', $7, $8, 'Google Cafe', $9, $10, $11, 'Mixed state scenario order', $12, '{"slice":"active","orderType":"ONE_TIME"}'::jsonb ) `, [ fixture.orders.active.id, fixture.tenant.id, fixture.business.id, fixture.vendor.id, fixture.orders.active.number, fixture.orders.active.title, assignedStartsAt, availableEndsAt, fixture.clockPoint.address, fixture.clockPoint.latitude, fixture.clockPoint.longitude, fixture.users.operationsManager.id, ] ); await client.query( ` INSERT INTO shifts ( id, tenant_id, order_id, business_id, vendor_id, clock_point_id, shift_code, title, status, starts_at, ends_at, timezone, location_name, location_address, latitude, longitude, geofence_radius_meters, clock_in_mode, allow_clock_in_override, required_workers, assigned_workers, notes, metadata ) VALUES ($1, $3, $5, $7, $9, $11, $13, $15, 'OPEN', $17, $18, 'America/Los_Angeles', 'Google Cafe', $19, $21, $22, $23, NULL, NULL, 1, 0, 'Open staffing need', '{"slice":"open"}'::jsonb), ($2, $4, $6, $8, $10, $12, $14, $16, 'COMPLETED', $20, $24, 'America/Los_Angeles', 'Google Catering', $19, $21, $22, $23, NULL, NULL, 1, 1, 'Completed staffed shift', '{"slice":"completed"}'::jsonb) `, [ fixture.shifts.open.id, fixture.shifts.completed.id, fixture.tenant.id, fixture.tenant.id, fixture.orders.open.id, fixture.orders.completed.id, fixture.business.id, fixture.business.id, fixture.vendor.id, fixture.vendor.id, fixture.clockPoint.id, fixture.clockPoint.id, fixture.shifts.open.code, fixture.shifts.completed.code, fixture.shifts.open.title, fixture.shifts.completed.title, openStartsAt, openEndsAt, fixture.clockPoint.address, completedStartsAt, fixture.clockPoint.latitude, fixture.clockPoint.longitude, fixture.clockPoint.geofenceRadiusMeters, completedEndsAt, ] ); await client.query( ` INSERT INTO shifts ( id, tenant_id, order_id, business_id, vendor_id, clock_point_id, shift_code, title, status, starts_at, ends_at, timezone, location_name, location_address, latitude, longitude, geofence_radius_meters, clock_in_mode, allow_clock_in_override, required_workers, assigned_workers, notes, metadata ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, 'OPEN', $9, $10, 'America/Los_Angeles', 'Google Cafe', $11, $12, $13, $14, NULL, NULL, 1, 0, 'Available shift for staff marketplace', '{"slice":"available"}'::jsonb), ($15, $2, $3, $4, $5, $6, $16, $17, 'ASSIGNED', $18, $19, 'America/Los_Angeles', 'Google Cafe', $11, $12, $13, $14, $30, $31, 1, 1, 'Assigned shift waiting for staff confirmation', '{"slice":"assigned"}'::jsonb), ($20, $2, $3, $4, $5, $6, $21, $22, 'CANCELLED', $23, $24, 'America/Los_Angeles', 'Google Cafe', $11, $12, $13, $14, NULL, NULL, 1, 0, 'Cancelled shift history sample', '{"slice":"cancelled"}'::jsonb), ($25, $2, $3, $4, $5, $6, $26, $27, 'COMPLETED', $28, $29, 'America/Los_Angeles', 'Google Cafe', $11, $12, $13, $14, 'GEO_REQUIRED', TRUE, 1, 0, 'No-show historical sample', '{"slice":"no_show"}'::jsonb) `, [ fixture.shifts.available.id, fixture.tenant.id, fixture.orders.active.id, fixture.business.id, fixture.vendor.id, fixture.clockPoint.id, fixture.shifts.available.code, fixture.shifts.available.title, availableStartsAt, availableEndsAt, fixture.clockPoint.address, fixture.clockPoint.latitude, fixture.clockPoint.longitude, fixture.clockPoint.geofenceRadiusMeters, fixture.shifts.assigned.id, fixture.shifts.assigned.code, fixture.shifts.assigned.title, assignedStartsAt, assignedEndsAt, fixture.shifts.cancelled.id, fixture.shifts.cancelled.code, fixture.shifts.cancelled.title, cancelledStartsAt, cancelledEndsAt, fixture.shifts.noShow.id, fixture.shifts.noShow.code, fixture.shifts.noShow.title, noShowStartsAt, noShowEndsAt, fixture.shifts.assigned.clockInMode, fixture.shifts.assigned.allowClockInOverride, ] ); await client.query( ` INSERT INTO shift_roles ( id, shift_id, role_id, role_code, role_name, workers_needed, assigned_count, pay_rate_cents, bill_rate_cents, metadata ) VALUES ($1, $2, $3, $4, $5, 1, 0, 2200, 3500, '{"slice":"open"}'::jsonb), ($6, $7, $3, $4, $5, 1, 1, 2200, 3500, '{"slice":"completed"}'::jsonb) `, [ fixture.shiftRoles.openBarista.id, fixture.shifts.open.id, fixture.roles.barista.id, fixture.roles.barista.code, fixture.roles.barista.name, fixture.shiftRoles.completedBarista.id, fixture.shifts.completed.id, ] ); await client.query( ` INSERT INTO shift_roles ( id, shift_id, role_id, role_code, role_name, workers_needed, assigned_count, pay_rate_cents, bill_rate_cents, metadata ) VALUES ($1, $2, $7, $8, $9, 1, 0, 2200, 3500, '{"slice":"available"}'::jsonb), ($3, $4, $7, $8, $9, 1, 1, 2300, 3600, '{"slice":"assigned"}'::jsonb), ($5, $6, $7, $8, $9, 1, 0, 2200, 3500, '{"slice":"cancelled"}'::jsonb), ($10, $11, $7, $8, $9, 1, 0, 2200, 3500, '{"slice":"no_show"}'::jsonb) `, [ fixture.shiftRoles.availableBarista.id, fixture.shifts.available.id, fixture.shiftRoles.assignedBarista.id, fixture.shifts.assigned.id, fixture.shiftRoles.cancelledBarista.id, fixture.shifts.cancelled.id, fixture.roles.barista.id, fixture.roles.barista.code, fixture.roles.barista.name, fixture.shiftRoles.noShowBarista.id, fixture.shifts.noShow.id, ] ); await client.query( ` INSERT INTO applications ( id, tenant_id, shift_id, shift_role_id, staff_id, status, origin, applied_at, metadata ) VALUES ($1, $2, $3, $4, $5, 'PENDING', 'STAFF', NOW(), '{"slice":"open"}'::jsonb) `, [ fixture.applications.openAna.id, fixture.tenant.id, fixture.shifts.open.id, fixture.shiftRoles.openBarista.id, fixture.staff.ana.id, ] ); await client.query( ` INSERT INTO assignments ( id, tenant_id, business_id, vendor_id, shift_id, shift_role_id, workforce_id, staff_id, status, assigned_at, accepted_at, checked_in_at, checked_out_at, metadata ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, 'COMPLETED', $9, $10, $11, $12, '{"slice":"completed"}'::jsonb) `, [ fixture.assignments.completedAna.id, fixture.tenant.id, fixture.business.id, fixture.vendor.id, fixture.shifts.completed.id, fixture.shiftRoles.completedBarista.id, fixture.workforce.ana.id, fixture.staff.ana.id, completedStartsAt, completedStartsAt, checkedInAt, checkedOutAt, ] ); await client.query( ` INSERT INTO assignments ( id, tenant_id, business_id, vendor_id, shift_id, shift_role_id, workforce_id, staff_id, status, assigned_at, accepted_at, checked_in_at, checked_out_at, metadata ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, 'ASSIGNED', NOW(), NULL, NULL, NULL, '{"slice":"assigned"}'::jsonb), ($9, $2, $3, $4, $10, $11, $7, $8, 'CANCELLED', NOW(), NULL, NULL, NULL, '{"slice":"cancelled","cancellationReason":"Client cancelled"}'::jsonb), ($12, $2, $3, $4, $13, $14, $7, $8, 'NO_SHOW', $15, NULL, NULL, NULL, '{"slice":"no_show"}'::jsonb) `, [ fixture.assignments.assignedAna.id, fixture.tenant.id, fixture.business.id, fixture.vendor.id, fixture.shifts.assigned.id, fixture.shiftRoles.assignedBarista.id, fixture.workforce.ana.id, fixture.staff.ana.id, fixture.assignments.cancelledAna.id, fixture.shifts.cancelled.id, fixture.shiftRoles.cancelledBarista.id, fixture.assignments.noShowAna.id, fixture.shifts.noShow.id, fixture.shiftRoles.noShowBarista.id, noShowStartsAt, ] ); await client.query( ` INSERT INTO attendance_events ( tenant_id, assignment_id, shift_id, staff_id, clock_point_id, event_type, source_type, source_reference, nfc_tag_uid, device_id, latitude, longitude, accuracy_meters, distance_to_clock_point_meters, within_geofence, validation_status, validation_reason, captured_at, raw_payload ) VALUES ($1, $2, $3, $4, $5, 'CLOCK_IN', 'NFC', 'seed', $6, 'seed-device', $7, $8, 5, 0, TRUE, 'ACCEPTED', NULL, $9, '{"seeded":true}'::jsonb), ($1, $2, $3, $4, $5, 'CLOCK_OUT', 'NFC', 'seed', $6, 'seed-device', $7, $8, 5, 0, TRUE, 'ACCEPTED', NULL, $10, '{"seeded":true}'::jsonb) `, [ fixture.tenant.id, fixture.assignments.completedAna.id, fixture.shifts.completed.id, fixture.staff.ana.id, fixture.clockPoint.id, fixture.clockPoint.nfcTagUid, fixture.clockPoint.latitude, fixture.clockPoint.longitude, checkedInAt, checkedOutAt, ] ); const attendanceEvents = await client.query( ` SELECT id, event_type FROM attendance_events WHERE assignment_id = $1 ORDER BY captured_at ASC `, [fixture.assignments.completedAna.id] ); await client.query( ` INSERT INTO attendance_sessions ( id, tenant_id, assignment_id, staff_id, clock_in_event_id, clock_out_event_id, status, check_in_at, check_out_at, worked_minutes, metadata ) VALUES ($1, $2, $3, $4, $5, $6, 'CLOSED', $7, $8, 435, '{"seeded":true}'::jsonb) `, [ '95f6017c-256c-4eb5-8033-eb942f018001', fixture.tenant.id, fixture.assignments.completedAna.id, fixture.staff.ana.id, attendanceEvents.rows.find((row) => row.event_type === 'CLOCK_IN')?.id, attendanceEvents.rows.find((row) => row.event_type === 'CLOCK_OUT')?.id, checkedInAt, checkedOutAt, ] ); await client.query( ` INSERT INTO timesheets ( id, tenant_id, assignment_id, staff_id, status, regular_minutes, overtime_minutes, break_minutes, gross_pay_cents, metadata ) VALUES ($1, $2, $3, $4, 'APPROVED', 420, 15, 30, 15950, '{"seeded":true}'::jsonb) `, [fixture.timesheets.completedAna.id, fixture.tenant.id, fixture.assignments.completedAna.id, fixture.staff.ana.id] ); await client.query( ` INSERT INTO documents (id, tenant_id, document_type, name, required_for_role_code, metadata) VALUES ($1, $2, 'GOVERNMENT_ID', $3, $10, '{"seeded":true,"description":"State ID or passport","required":true}'::jsonb), ($4, $2, 'CERTIFICATION', $5, $10, '{"seeded":true}'::jsonb), ($6, $2, 'ATTIRE', $7, $10, '{"seeded":true,"description":"Upload a photo of your black shirt","required":true}'::jsonb), ($8, $2, 'TAX_FORM', $9, $10, '{"seeded":true}'::jsonb), ($11, $2, 'TAX_FORM', $12, $10, '{"seeded":true}'::jsonb) `, [ fixture.documents.governmentId.id, fixture.tenant.id, fixture.documents.governmentId.name, fixture.documents.foodSafety.id, fixture.documents.foodSafety.name, fixture.documents.attireBlackShirt.id, fixture.documents.attireBlackShirt.name, fixture.documents.taxFormI9.id, fixture.documents.taxFormI9.name, fixture.roles.barista.code, fixture.documents.taxFormW4.id, fixture.documents.taxFormW4.name, ] ); await client.query( ` INSERT INTO staff_documents ( id, tenant_id, staff_id, document_id, file_uri, status, expires_at, metadata ) VALUES ($1, $2, $3, $4, $5, 'PENDING', $6, '{"seeded":true,"verificationStatus":"PENDING_REVIEW"}'::jsonb), ($7, $2, $3, $8, $9, 'VERIFIED', $10, '{"seeded":true,"verificationStatus":"APPROVED"}'::jsonb), ($11, $2, $3, $12, $13, 'VERIFIED', NULL, '{"seeded":true,"verificationStatus":"APPROVED"}'::jsonb), ($14, $2, $3, $15, $16, 'VERIFIED', NULL, '{"seeded":true,"formStatus":"SUBMITTED","fields":{"ssnLast4":"1234","filingStatus":"single"}}'::jsonb), ($17, $2, $3, $18, $19, 'PENDING', NULL, '{"seeded":true,"formStatus":"DRAFT","fields":{"section1Complete":true}}'::jsonb) `, [ fixture.staffDocuments.governmentId.id, fixture.tenant.id, fixture.staff.ana.id, fixture.documents.governmentId.id, `gs://krow-workforce-dev-v2-private/uploads/${fixture.staff.ana.id}/government-id-front.jpg`, hoursFromNow(24 * 365), fixture.staffDocuments.foodSafety.id, fixture.documents.foodSafety.id, `gs://krow-workforce-dev-v2-private/uploads/${fixture.staff.ana.id}/food-handler-card.pdf`, hoursFromNow(24 * 180), fixture.staffDocuments.attireBlackShirt.id, fixture.documents.attireBlackShirt.id, `gs://krow-workforce-dev-v2-private/uploads/${fixture.staff.ana.id}/black-shirt.jpg`, fixture.staffDocuments.taxFormW4.id, fixture.documents.taxFormW4.id, `gs://krow-workforce-dev-v2-private/uploads/${fixture.staff.ana.id}/w4-form.pdf`, fixture.staffDocuments.taxFormI9.id, fixture.documents.taxFormI9.id, `gs://krow-workforce-dev-v2-private/uploads/${fixture.staff.ana.id}/i9-form.pdf`, ] ); await client.query( ` INSERT INTO certificates ( id, tenant_id, staff_id, certificate_type, certificate_number, issued_at, expires_at, status, file_uri, metadata ) VALUES ($1, $2, $3, 'FOOD_SAFETY', 'FH-ANA-2026', $4, $5, 'VERIFIED', $6, $7::jsonb) `, [ fixture.certificates.foodSafety.id, fixture.tenant.id, fixture.staff.ana.id, hoursFromNow(-24 * 30), hoursFromNow(24 * 180), `gs://krow-workforce-dev-v2-private/uploads/${fixture.staff.ana.id}/food-safety-certificate.pdf`, JSON.stringify({ seeded: true, name: 'Food Safety Certificate', issuer: 'ServSafe', verificationStatus: 'APPROVED', }), ] ); await client.query( ` INSERT INTO verification_jobs ( tenant_id, staff_id, document_id, type, file_uri, status, idempotency_key, provider_name, provider_reference, confidence, reasons, extracted, review, metadata ) VALUES ( $1, $2, $3, 'certification', $4, 'APPROVED', 'seed-certification-job', 'seed', 'seed-certification-provider', 0.980, '["Verified by seed"]'::jsonb, '{"certificateType":"FOOD_SAFETY"}'::jsonb, '{"decision":"APPROVED"}'::jsonb, '{"seeded":true}'::jsonb ) `, [ fixture.tenant.id, fixture.staff.ana.id, fixture.documents.foodSafety.id, `gs://krow-workforce-dev-v2-private/uploads/${fixture.staff.ana.id}/food-handler-card.pdf`, ] ); await client.query( ` INSERT INTO accounts ( id, tenant_id, owner_type, owner_business_id, owner_vendor_id, owner_staff_id, provider_name, provider_reference, last4, is_primary, metadata ) VALUES ($1, $3, 'BUSINESS', $4, NULL, NULL, 'stripe', 'ba_business_demo', '6789', TRUE, '{"seeded":true,"accountType":"CHECKING","routingNumberMasked":"*****0001"}'::jsonb), ($2, $3, 'STAFF', NULL, NULL, $5, 'stripe', 'ba_staff_demo', '4321', TRUE, '{"seeded":true,"accountType":"CHECKING","routingNumberMasked":"*****0002"}'::jsonb) `, [ fixture.accounts.businessPrimary.id, fixture.accounts.staffPrimary.id, fixture.tenant.id, fixture.business.id, fixture.staff.ana.id, ] ); await client.query( ` INSERT INTO invoices ( id, tenant_id, order_id, business_id, vendor_id, invoice_number, status, currency_code, subtotal_cents, tax_cents, total_cents, due_at, metadata ) VALUES ($1, $2, $3, $4, $5, $6, 'PENDING_REVIEW', 'USD', 15250, 700, 15950, $7, '{"seeded":true,"savingsCents":1250}'::jsonb) `, [ fixture.invoices.completed.id, fixture.tenant.id, fixture.orders.completed.id, fixture.business.id, fixture.vendor.id, fixture.invoices.completed.number, invoiceDueAt, ] ); await client.query( ` INSERT INTO recent_payments ( id, tenant_id, invoice_id, assignment_id, staff_id, status, amount_cents, process_date, metadata ) VALUES ($1, $2, $3, $4, $5, 'PENDING', 15950, NULL, '{"seeded":true}'::jsonb) `, [ fixture.recentPayments.completed.id, fixture.tenant.id, fixture.invoices.completed.id, fixture.assignments.completedAna.id, fixture.staff.ana.id, ] ); await client.query( ` INSERT INTO staff_favorites (id, tenant_id, business_id, staff_id, created_by_user_id, created_at) VALUES ($1, $2, $3, $4, $5, NOW()) `, [ fixture.favorites.ana.id, fixture.tenant.id, fixture.business.id, fixture.staff.ana.id, fixture.users.businessOwner.id, ] ); await client.query( ` INSERT INTO staff_reviews ( id, tenant_id, business_id, staff_id, assignment_id, reviewer_user_id, rating, review_text, tags, created_at, updated_at ) VALUES ($1, $2, $3, $4, $5, $6, 5, 'Reliable, on time, and client friendly.', '["reliable","favorite"]'::jsonb, NOW(), NOW()) `, [ fixture.reviews.anaCompleted.id, fixture.tenant.id, fixture.business.id, fixture.staff.ana.id, fixture.assignments.completedAna.id, fixture.users.businessOwner.id, ] ); await client.query( ` INSERT INTO domain_events (tenant_id, aggregate_type, aggregate_id, sequence, event_type, actor_user_id, payload) VALUES ($1, 'order', $2, 1, 'ORDER_CREATED', $3, '{"seeded":true}'::jsonb), ($1, 'assignment', $4, 1, 'STAFF_ASSIGNED', $3, '{"seeded":true}'::jsonb) `, [ fixture.tenant.id, fixture.orders.completed.id, fixture.users.businessOwner.id, fixture.assignments.completedAna.id, ] ); await client.query( ` INSERT INTO location_stream_batches ( id, tenant_id, business_id, vendor_id, shift_id, assignment_id, staff_id, actor_user_id, source_type, device_id, object_uri, point_count, out_of_geofence_count, missing_coordinate_count, max_distance_to_clock_point_meters, started_at, ended_at, metadata ) VALUES ( $1, $2, $3, $4, $5, $6, $7, $8, 'GEO', 'seed-device', $9, 4, 2, 0, 910, $10, $11, '{"seeded":true,"source":"seed-v2-demo"}'::jsonb ) `, [ fixture.locationStreamBatches.noShowSample.id, fixture.tenant.id, fixture.business.id, fixture.vendor.id, fixture.shifts.noShow.id, fixture.assignments.noShowAna.id, fixture.staff.ana.id, fixture.users.staffAna.id, `gs://krow-workforce-dev-v2-private/location-streams/${fixture.tenant.id}/${fixture.staff.ana.id}/${fixture.assignments.noShowAna.id}/${fixture.locationStreamBatches.noShowSample.id}.json`, hoursFromNow(-18.25), hoursFromNow(-17.75), ] ); await client.query( ` INSERT INTO geofence_incidents ( id, tenant_id, business_id, vendor_id, shift_id, assignment_id, staff_id, actor_user_id, location_stream_batch_id, incident_type, severity, status, effective_clock_in_mode, source_type, device_id, latitude, longitude, accuracy_meters, distance_to_clock_point_meters, within_geofence, override_reason, message, occurred_at, metadata ) VALUES ( $1, $2, $3, $4, $5, $6, $7, $8, $9, 'OUTSIDE_GEOFENCE', 'CRITICAL', 'OPEN', 'GEO_REQUIRED', 'GEO', 'seed-device', $10, $11, 12, 910, FALSE, NULL, 'Worker drifted outside hub geofence during active monitoring', $12, '{"seeded":true,"outOfGeofenceCount":2}'::jsonb ) `, [ fixture.geofenceIncidents.noShowOutsideGeofence.id, fixture.tenant.id, fixture.business.id, fixture.vendor.id, fixture.shifts.noShow.id, fixture.assignments.noShowAna.id, fixture.staff.ana.id, fixture.users.staffAna.id, fixture.locationStreamBatches.noShowSample.id, fixture.clockPoint.latitude + 0.0065, fixture.clockPoint.longitude + 0.0065, hoursFromNow(-17.9), ] ); await client.query( ` INSERT INTO notification_outbox ( id, tenant_id, business_id, shift_id, assignment_id, related_incident_id, audience_type, recipient_user_id, recipient_business_membership_id, channel, notification_type, priority, dedupe_key, subject, body, payload, status, scheduled_at, created_at, updated_at ) SELECT $1, $2, $3, $4, $5, $6, 'USER', bm.user_id, bm.id, 'PUSH', 'GEOFENCE_BREACH_ALERT', 'CRITICAL', $7, 'Worker left the workplace geofence', 'Seeded alert for coverage incident review', jsonb_build_object('seeded', TRUE, 'batchId', $8::text), 'PENDING', NOW(), NOW(), NOW() FROM business_memberships bm WHERE bm.tenant_id = $2 AND bm.business_id = $3 AND bm.user_id = $9 `, [ fixture.notificationOutbox.noShowManagerAlert.id, fixture.tenant.id, fixture.business.id, fixture.shifts.noShow.id, fixture.assignments.noShowAna.id, fixture.geofenceIncidents.noShowOutsideGeofence.id, `seed-geofence-breach:${fixture.geofenceIncidents.noShowOutsideGeofence.id}:${fixture.users.operationsManager.id}`, fixture.locationStreamBatches.noShowSample.id, fixture.users.operationsManager.id, ] ); await client.query('COMMIT'); // eslint-disable-next-line no-console console.log(JSON.stringify({ tenantId: fixture.tenant.id, businessId: fixture.business.id, vendorId: fixture.vendor.id, staffId: fixture.staff.ana.id, staffUserId: fixture.users.staffAna.id, workforceId: fixture.workforce.ana.id, openOrderId: fixture.orders.open.id, openShiftId: fixture.shifts.open.id, openShiftRoleId: fixture.shiftRoles.openBarista.id, openApplicationId: fixture.applications.openAna.id, completedOrderId: fixture.orders.completed.id, completedAssignmentId: fixture.assignments.completedAna.id, clockPointId: fixture.clockPoint.id, nfcTagUid: fixture.clockPoint.nfcTagUid, businessOwnerUid: fixture.users.businessOwner.id, }, null, 2)); } catch (error) { await client.query('ROLLBACK'); throw error; } finally { client.release(); await pool.end(); } } main().catch((error) => { // eslint-disable-next-line no-console console.error(error); process.exit(1); });