Wire fulfillment alerts to Telegram + surface order progress in portal + even out ERPNext sync
Telegram notifications: - Add shared scripts/workers/telegram_notify.py (send_telegram, notify_fulfillment_todo, create_admin_todo) so every worker alerts the operator the same way; fire-and-forget. - Fire notify_fulfillment_todo after each admin_todos insert across all 8 service handlers (9 sites) so no fulfillment task waits unseen. (Orders + quotes + tickets already notified via checkout/quotes/tickets routes.) Client portal order progress: - order-timeline: derive real per-step status from live signals (payment paid, e-signature signed, fulfillment_status) instead of a static template; add current_step to the response. - Extract pure applyLiveStatus into order-timeline-status.ts (DB-free) + unit test (api/test/test_timeline_status.ts, 8 cases). - portal /me now returns compliance_orders.fulfillment_status. - Dashboard renders a client-safe Progress badge (In progress / Action needed / Filed-awaiting-confirmation / Completed); batches show the most actionable status. No back-office mechanics exposed. ERPNext sync parity: - Create a Sales Order for formation and fcc_carrier_registration orders (previously only canada_crtc + compliance synced); write erpnext_sales_order back to each table. Non-blocking, matches existing pattern. Verified: API tsc clean, timeline unit tests 8/8, Astro build 58 pages, cms10114/ink/paper_batch Python tests still green, no mechanics leaks.
This commit is contained in:
parent
41df4d9553
commit
28b1af341d
15 changed files with 706 additions and 73 deletions
92
api/test/test_timeline_status.ts
Normal file
92
api/test/test_timeline_status.ts
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
/**
|
||||
* Verifies applyLiveStatus: the live-progress overlay used by the client
|
||||
* portal order timeline. Run: npx tsx api/test/test_timeline_status.ts
|
||||
*/
|
||||
import { applyLiveStatus } from "../src/routes/order-timeline-status.js";
|
||||
|
||||
type Step = { name: string; description: string; business_days: number; status: string };
|
||||
|
||||
const npiSteps: Step[] = [
|
||||
{ name: "Order Received", description: "", business_days: 0, status: "completed" },
|
||||
{ name: "Document Preparation", description: "", business_days: 1, status: "pending" },
|
||||
{ name: "Signature Required", description: "", business_days: 1, status: "pending" },
|
||||
{ name: "Filed with CMS", description: "", business_days: 2, status: "pending" },
|
||||
{ name: "CMS Confirmation", description: "", business_days: 10, status: "pending" },
|
||||
];
|
||||
|
||||
let failures = 0;
|
||||
function check(label: string, got: string[], want: string[]) {
|
||||
const ok = JSON.stringify(got) === JSON.stringify(want);
|
||||
if (!ok) {
|
||||
failures++;
|
||||
console.error(`FAIL: ${label}\n got: ${got.join(",")}\n want: ${want.join(",")}`);
|
||||
} else {
|
||||
console.log(`ok: ${label}`);
|
||||
}
|
||||
}
|
||||
const statuses = (s: Step[]) => s.map((x) => x.status);
|
||||
|
||||
// 1. Unpaid, nothing reached → only the statically-completed step 0 is done.
|
||||
check(
|
||||
"unpaid",
|
||||
statuses(applyLiveStatus(npiSteps, { paid: false, signed: false, fulfillmentStatus: null })),
|
||||
["completed", "pending", "pending", "pending", "pending"],
|
||||
);
|
||||
|
||||
// 2. Paid only → Order Received done, prep in progress
|
||||
check(
|
||||
"paid only",
|
||||
statuses(applyLiveStatus(npiSteps, { paid: true, signed: false, fulfillmentStatus: null })),
|
||||
["completed", "in_progress", "pending", "pending", "pending"],
|
||||
);
|
||||
|
||||
// 3. Paid + signed → through Signature done, Filed in progress
|
||||
check(
|
||||
"paid+signed",
|
||||
statuses(applyLiveStatus(npiSteps, { paid: true, signed: true, fulfillmentStatus: null })),
|
||||
["completed", "completed", "completed", "in_progress", "pending"],
|
||||
);
|
||||
|
||||
// 4. Paid + signed + filed → through Filed done, Confirmation in progress
|
||||
check(
|
||||
"paid+signed+filed",
|
||||
statuses(applyLiveStatus(npiSteps, { paid: true, signed: true, fulfillmentStatus: "filed_waiting_state" })),
|
||||
["completed", "completed", "completed", "completed", "in_progress"],
|
||||
);
|
||||
|
||||
// 5. Completed → everything done
|
||||
check(
|
||||
"completed",
|
||||
statuses(applyLiveStatus(npiSteps, { paid: true, signed: true, fulfillmentStatus: "completed" })),
|
||||
["completed", "completed", "completed", "completed", "completed"],
|
||||
);
|
||||
|
||||
// 6. ready_to_file without explicit signed → still advances to Filed step
|
||||
check(
|
||||
"ready_to_file",
|
||||
statuses(applyLiveStatus(npiSteps, { paid: true, signed: false, fulfillmentStatus: "ready_to_file" })),
|
||||
["completed", "completed", "completed", "completed", "in_progress"],
|
||||
);
|
||||
|
||||
// 7. A timeline with no signature step (e.g. boc3) still works off paid + fulfillment
|
||||
const boc3: Step[] = [
|
||||
{ name: "Order Received", description: "", business_days: 0, status: "completed" },
|
||||
{ name: "Process Agent Filing", description: "", business_days: 1, status: "pending" },
|
||||
{ name: "FMCSA Registration", description: "", business_days: 3, status: "pending" },
|
||||
];
|
||||
check(
|
||||
"boc3 paid",
|
||||
statuses(applyLiveStatus(boc3, { paid: true, signed: false, fulfillmentStatus: null })),
|
||||
["completed", "in_progress", "pending"],
|
||||
);
|
||||
check(
|
||||
"boc3 filed",
|
||||
statuses(applyLiveStatus(boc3, { paid: true, signed: false, fulfillmentStatus: "filed_waiting_state" })),
|
||||
["completed", "completed", "in_progress"], // "Process Agent Filing" matches FILED_STEP_RE
|
||||
);
|
||||
|
||||
if (failures > 0) {
|
||||
console.error(`\n${failures} test(s) failed`);
|
||||
process.exit(1);
|
||||
}
|
||||
console.log("\nAll timeline status tests passed");
|
||||
Loading…
Add table
Add a link
Reference in a new issue