paging: paging groups, *80 intercom, auto-answer headers
/api/paging-groups CRUD (members as memberIds). The generator renders
sp-page-{id} per 03 §6.11 (desk phones only, auto-answer headers via
sp-sub-autoanswer) plus the number in sp-local-numbers. Added the `s`
option so phones already on a call aren't paged, which also keeps a
pager who is a member from being paged themselves. A group with no
members just hangs up.
*80<ext> intercom checks the target first: digits only and an endpoint
in sp-from-ext, else ss-noservice (the spec's one-liner passed
${EXTEN:3} straight into Page()). Both written back into 03.
Web: Paging page (list, drawer form with member checkboxes). Golden
case paging. e2e m8-t4 with baresip phones that honor the headers
(sip_autoanswer): an ordinary call rings, a page is answered by itself
and muted, the busy pager is skipped, *80 is two-way, *80999 refused.
The check on a real Yealink/Grandstream stays open (09).
Task: M8-T4
This commit is contained in:
1 parent
a6c423de97
commit
e95271e694
47 files changed
+1466
-22
No files matched your search
@@ -10,6 +10,8 @@ import {
|
||||
extensions,
|
||||
holidays,
|
||||
ivrOptions,
|
||||
pagingGroupMembers,
|
||||
pagingGroups,
|
||||
ivrs,
|
||||
outboundRoutes,
|
||||
queueMembers,
|
||||
@@ -126,6 +128,15 @@ export type SnapConference = {
|
||||
record: boolean;
|
||||
};
|
||||
|
||||
export type SnapPagingGroup = {
|
||||
id: string;
|
||||
number: string;
|
||||
name: string;
|
||||
duplex: boolean;
|
||||
/** Extension numbers. */
|
||||
members: string[];
|
||||
};
|
||||
|
||||
export type SnapIvr = {
|
||||
id: string;
|
||||
number: string;
|
||||
@@ -179,6 +190,7 @@ export type Snapshot = {
|
||||
ringGroups: SnapRingGroup[];
|
||||
queues: SnapQueue[];
|
||||
conferences: SnapConference[];
|
||||
pagingGroups: SnapPagingGroup[];
|
||||
ivrs: SnapIvr[];
|
||||
timeConditions: SnapTimeCondition[];
|
||||
holidays: SnapHoliday[];
|
||||
@@ -233,6 +245,11 @@ export async function loadSnapshot(db: Db, now = new Date()): Promise<Snapshot>
|
||||
.from(queueMembers)
|
||||
.innerJoin(extensions, eq(extensions.id, queueMembers.extensionId));
|
||||
const confRows = await db.select().from(conferences);
|
||||
const pageRows = await db.select().from(pagingGroups);
|
||||
const pagedRows = await db
|
||||
.select({ groupId: pagingGroupMembers.pagingGroupId, number: extensions.number })
|
||||
.from(pagingGroupMembers)
|
||||
.innerJoin(extensions, eq(extensions.id, pagingGroupMembers.extensionId));
|
||||
const ivrRows = await db.select().from(ivrs);
|
||||
const tcRows = await db.select().from(timeConditions);
|
||||
const holidayRows = await db.select().from(holidays);
|
||||
@@ -335,6 +352,13 @@ export async function loadSnapshot(db: Db, now = new Date()): Promise<Snapshot>
|
||||
maxMembers: c.maxMembers,
|
||||
record: c.record,
|
||||
})),
|
||||
pagingGroups: pageRows.map((g) => ({
|
||||
id: g.id,
|
||||
number: g.number,
|
||||
name: g.name,
|
||||
duplex: g.duplex,
|
||||
members: pagedRows.filter((m) => m.groupId === g.id).map((m) => m.number),
|
||||
})),
|
||||
routes: routeRows.map((r) => ({
|
||||
id: r.id,
|
||||
name: r.name,
|
||||
|
||||
Reference in new issue
Block a user