backend requirements onthe xustomer app

This commit is contained in:
2026-09-07 10:55:11 +05:30
parent 35675d8a9b
commit 1b2690b21a
56 changed files with 13342 additions and 1071 deletions

170
seed_customer_app.sql Normal file
View File

@@ -0,0 +1,170 @@
-- ============================================================
-- Doormile — customer app (doormile_cx) seed data
-- Idempotent: safe to re-run (ON CONFLICT DO UPDATE throughout)
--
-- Seeded to mirror the client's in-app mock exactly, because those cases back
-- the app's 18 widget tests and its design QA:
-- * Tamil Nadu / Kerala / Karnataka open
-- * Puducherry serviceable with NO open district (the "opening soon" state)
-- * Madurai / Kozhikode / Mangaluru unavailable, each with a reason
-- * At least one fully-booked pickup slot (t5, capacity 0)
-- Change any of those and a green test suite stops meaning anything.
--
-- Run AFTER the Go service has started once, so AutoMigrate has created the
-- tables this file fills.
-- ============================================================
-- ── Serviceable states ───────────────────────────────────────────────────────
-- districtCount is computed at read time from the districts below, so a state
-- listed here with nothing open still returns and carries its transit tag. The
-- app hides a state showing 0 rather than the server omitting it.
INSERT INTO serviceablestates (statecode, statename, transittag, displayorder, status, createdat, updatedat)
VALUES
('TN', 'Tamil Nadu', 'Ultra-fast transit', 1, 'Active', NOW(), NOW()),
('KL', 'Kerala', 'Next-day transit', 2, 'Active', NOW(), NOW()),
('KA', 'Karnataka', 'Next-day transit', 3, 'Active', NOW(), NOW()),
('TG', 'Telangana', 'Next-day transit', 4, 'Active', NOW(), NOW()),
('PY', 'Puducherry', 'Opening soon', 5, 'Active', NOW(), NOW())
ON CONFLICT (statecode) DO UPDATE
SET statename = EXCLUDED.statename,
transittag = EXCLUDED.transittag,
displayorder = EXCLUDED.displayorder,
status = EXCLUDED.status,
updatedat = NOW();
-- ── Serviceable districts ────────────────────────────────────────────────────
-- Unavailable districts are seeded too, and returned by the API. The picker
-- filters them out but the app shows their names in a quiet "coming soon" line,
-- so deleting them would remove real copy from the screen. `note` is why —
-- never left blank on an unavailable row, or the app has to invent a reason.
--
-- centrelatitude/longitude matter more than they look: only state and district
-- are required at booking time, so for most destinations these coordinates are
-- the ONLY geography the parcel has until the miler corrects it at the door.
-- They price the estimate and they keep the stop out of the 0,0 bucket that
-- route sequencing skips.
INSERT INTO serviceabledistricts
(districtcode, statecode, districtname, available, note, promise,
centrelatitude, centrelongitude, pincodeprefix, displayorder, createdat, updatedat)
VALUES
-- Tamil Nadu
('TN-CBE', 'TN', 'Coimbatore', TRUE, '', 'Next-day delivery', 11.016845, 76.955832, '641', 1, NOW(), NOW()),
('TN-MAA', 'TN', 'Chennai', TRUE, '', 'Next-day delivery', 13.082680, 80.270718, '600', 2, NOW(), NOW()),
('TN-SLM', 'TN', 'Salem', TRUE, '', '2-day delivery', 11.664325, 78.146011, '636', 3, NOW(), NOW()),
('TN-TRY', 'TN', 'Tiruchirappalli', TRUE, '', '2-day delivery', 10.790483, 78.704674, '620', 4, NOW(), NOW()),
('TN-ERD', 'TN', 'Erode', TRUE, '', '2-day delivery', 11.341000, 77.717300, '638', 5, NOW(), NOW()),
('TN-KNK', 'TN', 'Kanyakumari', TRUE, '', '2-day delivery', 8.178200, 77.435400, '629', 6, NOW(), NOW()),
('TN-MDU', 'TN', 'Madurai', FALSE, 'Opening soon', '', 9.925201, 78.119775, '625', 7, NOW(), NOW()),
-- Kerala
('KL-EKM', 'KL', 'Ernakulam', TRUE, '', 'Next-day delivery', 9.981636, 76.299881, '682', 1, NOW(), NOW()),
('KL-TVM', 'KL', 'Thiruvananthapuram', TRUE, '', '2-day delivery', 8.524139, 76.936638, '695', 2, NOW(), NOW()),
('KL-TSR', 'KL', 'Thrissur', TRUE, '', '2-day delivery', 10.527640, 76.214402, '680', 3, NOW(), NOW()),
('KL-KKD', 'KL', 'Kozhikode', FALSE, 'Opening soon', '', 11.258753, 75.780411, '673', 4, NOW(), NOW()),
-- Karnataka
('KA-BLR', 'KA', 'Bengaluru Urban', TRUE, '', 'Next-day delivery', 12.971599, 77.594566, '560', 1, NOW(), NOW()),
('KA-MYS', 'KA', 'Mysuru', TRUE, '', '2-day delivery', 12.295810, 76.639381, '570', 2, NOW(), NOW()),
('KA-MNG', 'KA', 'Dakshina Kannada', FALSE, 'Paused this week', '', 12.914142, 74.856000, '575', 3, NOW(), NOW()),
-- Telangana
('TG-HYD', 'TG', 'Hyderabad', TRUE, '', 'Next-day delivery', 17.385044, 78.486671, '500', 1, NOW(), NOW()),
-- Puducherry: serviceable state, no open district. This is the "Opening
-- soon" state the app has a designed screen for, and the only place that
-- screen can be exercised.
('PY-PDY', 'PY', 'Puducherry', FALSE, 'Opening soon', '', 11.913860, 79.812600, '605', 1, NOW(), NOW())
ON CONFLICT (districtcode) DO UPDATE
SET statecode = EXCLUDED.statecode,
districtname = EXCLUDED.districtname,
available = EXCLUDED.available,
note = EXCLUDED.note,
promise = EXCLUDED.promise,
centrelatitude = EXCLUDED.centrelatitude,
centrelongitude = EXCLUDED.centrelongitude,
pincodeprefix = EXCLUDED.pincodeprefix,
displayorder = EXCLUDED.displayorder,
updatedat = NOW();
-- Attach each district to the nearest active hub, so the destination card can
-- name a serving base. Done by proximity rather than hard-coded ids because hub
-- ids differ between environments and a wrong id is worse than a missing name.
UPDATE serviceabledistricts d
SET hubid = nearest.hubid
FROM LATERAL (
SELECT h.hubid
FROM hubs h
WHERE h.status = 'Active'
AND h.deletedat IS NULL
AND h.latitude <> 0
ORDER BY (h.latitude - d.centrelatitude) ^ 2
+ (h.longitude - d.centrelongitude) ^ 2
LIMIT 1
) AS nearest
WHERE d.hubid IS NULL;
-- ── Pickup slot templates ────────────────────────────────────────────────────
-- Roughly six windows a day, which is what the design lays out. The customer
-- never sees these rows: the API expands them onto today and tomorrow and mints
-- a dated slot id, so a slot the customer picks always resolves back to a real
-- preferredpickupfrom/to that the assignment engine can staff.
--
-- t5 is seeded at capacity 0 on purpose — it is the ONLY way to reach the
-- "Fully booked" state in design QA without actually filling a window.
INSERT INTO pickupslottemplates
(code, starthour, startminute, endhour, endminute, capacity, applocationid,
tag, caption, displayorder, status, createdat, updatedat)
VALUES
('t1', 8, 0, 10, 0, 25, NULL, 'Fastest pickup', 'Arriving in approx. 45 mins', 1, 'Active', NOW(), NOW()),
('t2', 10, 0, 12, 0, 25, NULL, '', '', 2, 'Active', NOW(), NOW()),
('t3', 12, 0, 14, 0, 25, NULL, '', '', 3, 'Active', NOW(), NOW()),
('t4', 14, 0, 16, 0, 25, NULL, '', '', 4, 'Active', NOW(), NOW()),
('t5', 16, 0, 18, 0, 0, NULL, '', '', 5, 'Active', NOW(), NOW()),
('t6', 18, 0, 20, 0, 20, NULL, '', '', 6, 'Active', NOW(), NOW())
ON CONFLICT DO NOTHING;
-- ── Booking limits ───────────────────────────────────────────────────────────
-- The global default row. Ops varies these per city by inserting a row with an
-- applocationid; the client keeps 20/5 if the call fails, and the server never
-- answers 0 for either — a zero cap would reject every booking on the platform.
-- maxdestinations is seeded at 1 ON PURPOSE, not at the 5 the design allows.
--
-- The fan-out works server-side, but the deployed rider app keys its stop list
-- on `orderid`, which is booking-level — so all three stops of a
-- three-destination pickup collapse into one in its local store, and two
-- parcels would be left with no stop and no way to close them. Accepting such a
-- booking would create work nobody can complete.
--
-- Because the client reads this value from GET /customer/config/booking-limits
-- and adapts, capping it here keeps every pickup single-destination with no
-- feature flag and no app release. Raise it to 5 with a single UPDATE once a
-- rider build that keys on `consignmentid` is live:
--
-- UPDATE customerbookinglimits SET maxdestinations = 5 WHERE applocationid IS NULL;
--
-- Same discipline as MILER_HUB_HANDOVER_ENABLED: never turn on a server
-- behaviour the deployed rider app cannot finish.
INSERT INTO customerbookinglimits (applocationid, maxpackages, maxdestinations, createdat, updatedat)
SELECT NULL, 20, 1, NOW(), NOW()
WHERE NOT EXISTS (SELECT 1 FROM customerbookinglimits WHERE applocationid IS NULL);
-- ── Test account ─────────────────────────────────────────────────────────────
-- Automated tests and design QA cannot receive a real text, and the previous
-- end-to-end attempt on this platform stalled for exactly that reason: customer
-- login needed an OTP on a real handset and could not be scripted.
--
-- Pair this row with CX_STAGING_OTP=1234 in the staging environment. That env
-- var is refused outright when ENV=production (internal/sms), because a fixed
-- code is a skeleton key for every account on the platform.
INSERT INTO appcustomers (firstname, lastname, phone, email, status, configid, createdat, updatedat)
SELECT 'Doormile', 'QA', '+919999900001', 'qa@doormile.com', 'Active', 1001, NOW(), NOW()
WHERE NOT EXISTS (SELECT 1 FROM appcustomers WHERE phone = '+919999900001');
INSERT INTO appcustomers (firstname, lastname, phone, email, status, configid, createdat, updatedat)
SELECT 'Design', 'QA', '+919999900002', 'design.qa@doormile.com', 'Active', 1001, NOW(), NOW()
WHERE NOT EXISTS (SELECT 1 FROM appcustomers WHERE phone = '+919999900002');