task ep.1.4.1
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
import {
|
||||
createBusinessEvent,
|
||||
publishBusinessEvent,
|
||||
type BusinessEvent
|
||||
createBusinessEvent,
|
||||
publishBusinessEvent,
|
||||
type BusinessEvent
|
||||
} from '../../../foundation/business-events/index.ts';
|
||||
import type { ActivityRecord } from '../api/types.ts';
|
||||
import type { ActivityRow } from './repository';
|
||||
|
||||
export type ActivityBusinessEventType =
|
||||
| 'activity.created'
|
||||
@@ -16,13 +17,90 @@ export type ActivityBusinessEventType =
|
||||
| 'activity.deleted';
|
||||
|
||||
interface PublishActivityBusinessEventInput {
|
||||
eventType: ActivityBusinessEventType;
|
||||
activity: ActivityRecord;
|
||||
eventType: ActivityBusinessEventType;
|
||||
activity: ActivityRecord;
|
||||
actorUserId: string;
|
||||
correlationId?: string;
|
||||
causationId?: string | null;
|
||||
payload?: Record<string, unknown>;
|
||||
previousActivity?: ActivityRecord | null;
|
||||
previousActivity?: ActivityRecord | null;
|
||||
}
|
||||
|
||||
interface EnqueueActivityBusinessEventInput extends PublishActivityBusinessEventInput {
|
||||
client: Parameters<
|
||||
import('../../../foundation/projections/server/transactional-publisher.ts').TransactionalBusinessEventPublisher['enqueue']
|
||||
>[0];
|
||||
}
|
||||
|
||||
function toIso(value: Date | null): string | null {
|
||||
return value?.toISOString() ?? null;
|
||||
}
|
||||
|
||||
function toMetadata(value: unknown): Record<string, unknown> | null {
|
||||
return value && typeof value === 'object' && !Array.isArray(value)
|
||||
? (value as Record<string, unknown>)
|
||||
: null;
|
||||
}
|
||||
|
||||
export function activityRowToBusinessEventRecord(row: ActivityRow): ActivityRecord {
|
||||
const metadata = toMetadata(row.metadata);
|
||||
const scheduledStartAt = toIso(row.scheduledStartAt);
|
||||
const dueAt = toIso(row.dueAt);
|
||||
const completedAt = toIso(row.completedAt);
|
||||
const cancelledAt = toIso(row.cancelledAt);
|
||||
const skippedAt = toIso(row.skippedAt);
|
||||
|
||||
return {
|
||||
id: row.id,
|
||||
organizationId: row.organizationId,
|
||||
primaryEntityType: row.primaryEntityType as ActivityRecord['primaryEntityType'],
|
||||
primaryEntityId: row.primaryEntityId,
|
||||
primaryRecordLabel: row.primaryRecordLabel,
|
||||
relatedRecords: Array.isArray(row.relatedRecords)
|
||||
? (row.relatedRecords as ActivityRecord['relatedRecords'])
|
||||
: [],
|
||||
customerId: row.customerId,
|
||||
contactId: row.contactId,
|
||||
leadId: row.leadId,
|
||||
opportunityId: row.opportunityId,
|
||||
quotationId: row.quotationId,
|
||||
activityType: row.activityType as ActivityRecord['activityType'],
|
||||
subject: row.subject,
|
||||
description: row.description,
|
||||
ownerId: row.ownerId,
|
||||
assignedToId: row.assignedToId,
|
||||
scheduledStartAt,
|
||||
scheduledEndAt: toIso(row.scheduledEndAt),
|
||||
dueAt,
|
||||
completedAt,
|
||||
cancelledAt,
|
||||
skippedAt,
|
||||
status: row.status as ActivityRecord['status'],
|
||||
priority: row.priority as ActivityRecord['priority'],
|
||||
outcomeSummary: row.outcomeSummary,
|
||||
cancellationReason: row.cancellationReason,
|
||||
skipReason: row.skipReason,
|
||||
nextAction: row.nextAction,
|
||||
branchId: row.branchId,
|
||||
productType: row.productType,
|
||||
isInternalOnly: row.isInternalOnly,
|
||||
isPricingSensitive: row.isPricingSensitive,
|
||||
parentActivityId: row.parentActivityId,
|
||||
metadata,
|
||||
createdBy: row.createdBy,
|
||||
updatedBy: row.updatedBy,
|
||||
createdAt: row.createdAt.toISOString(),
|
||||
updatedAt: row.updatedAt.toISOString(),
|
||||
deletedAt: toIso(row.deletedAt),
|
||||
activityCode: typeof metadata?.activityCode === 'string' ? metadata.activityCode : null,
|
||||
effectiveStatus: row.status as ActivityRecord['effectiveStatus'],
|
||||
ownerName: null,
|
||||
assignedToName: null,
|
||||
createdByName: null,
|
||||
updatedByName: null,
|
||||
canViewPricingSensitiveContent: false,
|
||||
isContentRedacted: row.isPricingSensitive || row.isInternalOnly
|
||||
};
|
||||
}
|
||||
|
||||
function compactRelatedRecords(activity: ActivityRecord) {
|
||||
@@ -112,3 +190,15 @@ export async function publishActivityBusinessEvent(
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
export async function enqueueActivityBusinessEvent(
|
||||
input: EnqueueActivityBusinessEventInput
|
||||
): Promise<BusinessEvent> {
|
||||
const { transactionalBusinessEventPublisher } = await import(
|
||||
'../../../foundation/projections/server/transactional-publisher.ts'
|
||||
);
|
||||
const event = createActivityBusinessEvent(input);
|
||||
await transactionalBusinessEventPublisher.enqueue(input.client, event);
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import type { CrmActivityEntityType } from '@/features/crm/activity/types';
|
||||
|
||||
export type ActivityRow = typeof crmActivities.$inferSelect;
|
||||
export type InsertActivityRow = typeof crmActivities.$inferInsert;
|
||||
type ActivityDbClient = Pick<typeof db, 'insert' | 'update' | 'select'>;
|
||||
|
||||
export interface ActivityListQuery {
|
||||
organizationId: string;
|
||||
@@ -24,8 +25,11 @@ export interface ActivityContextQuery {
|
||||
limit?: number;
|
||||
}
|
||||
|
||||
export async function insertActivity(values: InsertActivityRow): Promise<ActivityRow> {
|
||||
const [created] = await db.insert(crmActivities).values(values).returning();
|
||||
export async function insertActivity(
|
||||
values: InsertActivityRow,
|
||||
client: ActivityDbClient = db
|
||||
): Promise<ActivityRow> {
|
||||
const [created] = await client.insert(crmActivities).values(values).returning();
|
||||
|
||||
return created;
|
||||
}
|
||||
@@ -33,9 +37,10 @@ export async function insertActivity(values: InsertActivityRow): Promise<Activit
|
||||
export async function updateActivityRow(
|
||||
id: string,
|
||||
organizationId: string,
|
||||
values: Partial<InsertActivityRow>
|
||||
values: Partial<InsertActivityRow>,
|
||||
client: ActivityDbClient = db
|
||||
): Promise<ActivityRow> {
|
||||
const [updated] = await db
|
||||
const [updated] = await client
|
||||
.update(crmActivities)
|
||||
.set(values)
|
||||
.where(and(eq(crmActivities.id, id), eq(crmActivities.organizationId, organizationId)))
|
||||
|
||||
@@ -46,7 +46,10 @@ import type {
|
||||
} from '../api/types';
|
||||
import { getActivityRow, insertActivity, listActivityRows, updateActivityRow } from './repository';
|
||||
import { hydrateActivityRecords } from './read-model';
|
||||
import { publishActivityBusinessEvent } from './business-events';
|
||||
import {
|
||||
activityRowToBusinessEventRecord,
|
||||
enqueueActivityBusinessEvent
|
||||
} from './business-events';
|
||||
|
||||
type ActivityAccessContext = CrmSecurityContext;
|
||||
|
||||
@@ -608,63 +611,73 @@ export async function createActivity(
|
||||
}
|
||||
};
|
||||
|
||||
const created = await insertActivity({
|
||||
id: crypto.randomUUID(),
|
||||
organizationId,
|
||||
primaryEntityType: values.primaryEntityType,
|
||||
primaryEntityId: values.primaryEntityId,
|
||||
primaryRecordLabel: values.primaryRecordLabel,
|
||||
relatedRecords: values.relatedRecords,
|
||||
customerId: values.customerId,
|
||||
contactId: values.contactId,
|
||||
leadId: values.leadId,
|
||||
opportunityId: values.opportunityId,
|
||||
quotationId: values.quotationId,
|
||||
activityType: values.activityType,
|
||||
subject: values.subject,
|
||||
description: values.description,
|
||||
ownerId: values.ownerId,
|
||||
assignedToId: values.assignedToId,
|
||||
scheduledStartAt: values.scheduledStartAt,
|
||||
scheduledEndAt: values.scheduledEndAt,
|
||||
dueAt: values.dueAt,
|
||||
completedAt: values.status === 'completed' ? new Date() : null,
|
||||
cancelledAt: values.status === 'cancelled' ? new Date() : null,
|
||||
skippedAt: values.status === 'skipped' ? new Date() : null,
|
||||
status: values.status,
|
||||
priority: values.priority,
|
||||
outcomeSummary: null,
|
||||
cancellationReason: null,
|
||||
skipReason: null,
|
||||
nextAction: values.nextAction,
|
||||
branchId: values.branchId,
|
||||
productType: values.productType,
|
||||
isInternalOnly: values.isInternalOnly,
|
||||
isPricingSensitive: values.isPricingSensitive,
|
||||
parentActivityId: null,
|
||||
metadata,
|
||||
createdBy: userId,
|
||||
updatedBy: userId
|
||||
const created = await db.transaction(async (tx) => {
|
||||
const createdRow = await insertActivity(
|
||||
{
|
||||
id: crypto.randomUUID(),
|
||||
organizationId,
|
||||
primaryEntityType: values.primaryEntityType,
|
||||
primaryEntityId: values.primaryEntityId,
|
||||
primaryRecordLabel: values.primaryRecordLabel,
|
||||
relatedRecords: values.relatedRecords,
|
||||
customerId: values.customerId,
|
||||
contactId: values.contactId,
|
||||
leadId: values.leadId,
|
||||
opportunityId: values.opportunityId,
|
||||
quotationId: values.quotationId,
|
||||
activityType: values.activityType,
|
||||
subject: values.subject,
|
||||
description: values.description,
|
||||
ownerId: values.ownerId,
|
||||
assignedToId: values.assignedToId,
|
||||
scheduledStartAt: values.scheduledStartAt,
|
||||
scheduledEndAt: values.scheduledEndAt,
|
||||
dueAt: values.dueAt,
|
||||
completedAt: values.status === 'completed' ? new Date() : null,
|
||||
cancelledAt: values.status === 'cancelled' ? new Date() : null,
|
||||
skippedAt: values.status === 'skipped' ? new Date() : null,
|
||||
status: values.status,
|
||||
priority: values.priority,
|
||||
outcomeSummary: null,
|
||||
cancellationReason: null,
|
||||
skipReason: null,
|
||||
nextAction: values.nextAction,
|
||||
branchId: values.branchId,
|
||||
productType: values.productType,
|
||||
isInternalOnly: values.isInternalOnly,
|
||||
isPricingSensitive: values.isPricingSensitive,
|
||||
parentActivityId: null,
|
||||
metadata,
|
||||
createdBy: userId,
|
||||
updatedBy: userId
|
||||
},
|
||||
tx
|
||||
);
|
||||
const eventActivity = activityRowToBusinessEventRecord(createdRow);
|
||||
const correlationId = crypto.randomUUID();
|
||||
|
||||
await enqueueActivityBusinessEvent({
|
||||
client: tx,
|
||||
eventType: 'activity.created',
|
||||
activity: eventActivity,
|
||||
actorUserId: userId,
|
||||
correlationId
|
||||
});
|
||||
|
||||
if (eventActivity.assignedToId && eventActivity.assignedToId !== eventActivity.ownerId) {
|
||||
await enqueueActivityBusinessEvent({
|
||||
client: tx,
|
||||
eventType: 'activity.assigned',
|
||||
activity: eventActivity,
|
||||
actorUserId: userId,
|
||||
correlationId
|
||||
});
|
||||
}
|
||||
|
||||
return createdRow;
|
||||
});
|
||||
|
||||
const activity = await getActivityById(created.id, organizationId, context);
|
||||
const correlationId = crypto.randomUUID();
|
||||
|
||||
await publishActivityBusinessEvent({
|
||||
eventType: 'activity.created',
|
||||
activity,
|
||||
actorUserId: userId,
|
||||
correlationId
|
||||
});
|
||||
|
||||
if (activity.assignedToId && activity.assignedToId !== activity.ownerId) {
|
||||
await publishActivityBusinessEvent({
|
||||
eventType: 'activity.assigned',
|
||||
activity,
|
||||
actorUserId: userId,
|
||||
correlationId
|
||||
});
|
||||
}
|
||||
|
||||
return activity;
|
||||
}
|
||||
@@ -688,8 +701,9 @@ export async function updateActivity(
|
||||
await assertActivityTypeValue(organizationId, values.activityType);
|
||||
const nextStatus = values.status;
|
||||
|
||||
await updateActivityRow(id, organizationId, {
|
||||
primaryEntityType: values.primaryEntityType,
|
||||
const activityEventRecord = await db.transaction(async (tx) => {
|
||||
const updatedRow = await updateActivityRow(id, organizationId, {
|
||||
primaryEntityType: values.primaryEntityType,
|
||||
primaryEntityId: values.primaryEntityId,
|
||||
primaryRecordLabel: values.primaryRecordLabel,
|
||||
relatedRecords: values.relatedRecords,
|
||||
@@ -727,66 +741,76 @@ export async function updateActivity(
|
||||
completedAt: nextStatus === 'completed' ? new Date() : null,
|
||||
cancelledAt: nextStatus === 'cancelled' ? new Date() : null,
|
||||
skippedAt: nextStatus === 'skipped' ? new Date() : null,
|
||||
updatedAt: new Date(),
|
||||
updatedBy: userId
|
||||
});
|
||||
updatedAt: new Date(),
|
||||
updatedBy: userId
|
||||
}, tx);
|
||||
|
||||
const activity = await getActivityById(id, organizationId, context);
|
||||
const correlationId = crypto.randomUUID();
|
||||
const nextActivity = activityRowToBusinessEventRecord(updatedRow);
|
||||
const correlationId = crypto.randomUUID();
|
||||
|
||||
if (current.assignedToId !== activity.assignedToId) {
|
||||
await publishActivityBusinessEvent({
|
||||
eventType: current.assignedToId ? 'activity.reassigned' : 'activity.assigned',
|
||||
activity,
|
||||
previousActivity: current,
|
||||
actorUserId: userId,
|
||||
correlationId
|
||||
if (current.assignedToId !== nextActivity.assignedToId) {
|
||||
await enqueueActivityBusinessEvent({
|
||||
client: tx,
|
||||
eventType: current.assignedToId ? 'activity.reassigned' : 'activity.assigned',
|
||||
activity: nextActivity,
|
||||
previousActivity: current,
|
||||
actorUserId: userId,
|
||||
correlationId
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
current.scheduledStartAt !== activity.scheduledStartAt ||
|
||||
current.scheduledEndAt !== activity.scheduledEndAt ||
|
||||
current.dueAt !== activity.dueAt
|
||||
) {
|
||||
await publishActivityBusinessEvent({
|
||||
eventType: 'activity.rescheduled',
|
||||
activity,
|
||||
previousActivity: current,
|
||||
actorUserId: userId,
|
||||
correlationId
|
||||
current.scheduledStartAt !== nextActivity.scheduledStartAt ||
|
||||
current.scheduledEndAt !== nextActivity.scheduledEndAt ||
|
||||
current.dueAt !== nextActivity.dueAt
|
||||
) {
|
||||
await enqueueActivityBusinessEvent({
|
||||
client: tx,
|
||||
eventType: 'activity.rescheduled',
|
||||
activity: nextActivity,
|
||||
previousActivity: current,
|
||||
actorUserId: userId,
|
||||
correlationId
|
||||
});
|
||||
}
|
||||
|
||||
if (current.status !== 'in_progress' && activity.status === 'in_progress') {
|
||||
await publishActivityBusinessEvent({
|
||||
eventType: 'activity.started',
|
||||
activity,
|
||||
previousActivity: current,
|
||||
actorUserId: userId,
|
||||
correlationId
|
||||
if (current.status !== 'in_progress' && nextActivity.status === 'in_progress') {
|
||||
await enqueueActivityBusinessEvent({
|
||||
client: tx,
|
||||
eventType: 'activity.started',
|
||||
activity: nextActivity,
|
||||
previousActivity: current,
|
||||
actorUserId: userId,
|
||||
correlationId
|
||||
});
|
||||
}
|
||||
|
||||
if (current.status !== 'completed' && activity.status === 'completed') {
|
||||
await publishActivityBusinessEvent({
|
||||
eventType: 'activity.completed',
|
||||
activity,
|
||||
previousActivity: current,
|
||||
actorUserId: userId,
|
||||
correlationId
|
||||
if (current.status !== 'completed' && nextActivity.status === 'completed') {
|
||||
await enqueueActivityBusinessEvent({
|
||||
client: tx,
|
||||
eventType: 'activity.completed',
|
||||
activity: nextActivity,
|
||||
previousActivity: current,
|
||||
actorUserId: userId,
|
||||
correlationId
|
||||
});
|
||||
}
|
||||
|
||||
if (current.status !== 'cancelled' && activity.status === 'cancelled') {
|
||||
await publishActivityBusinessEvent({
|
||||
eventType: 'activity.cancelled',
|
||||
activity,
|
||||
previousActivity: current,
|
||||
actorUserId: userId,
|
||||
correlationId
|
||||
});
|
||||
}
|
||||
if (current.status !== 'cancelled' && nextActivity.status === 'cancelled') {
|
||||
await enqueueActivityBusinessEvent({
|
||||
client: tx,
|
||||
eventType: 'activity.cancelled',
|
||||
activity: nextActivity,
|
||||
previousActivity: current,
|
||||
actorUserId: userId,
|
||||
correlationId
|
||||
});
|
||||
}
|
||||
|
||||
return nextActivity;
|
||||
});
|
||||
|
||||
const activity = await getActivityById(activityEventRecord.id, organizationId, context);
|
||||
|
||||
return activity;
|
||||
}
|
||||
@@ -802,24 +826,28 @@ export async function completeActivity(
|
||||
const current = await getHydratedActivityOrThrow(id, organizationId, context);
|
||||
assertTransitionAllowed(current.status, 'completed');
|
||||
|
||||
await updateActivityRow(id, organizationId, {
|
||||
status: 'completed',
|
||||
outcomeSummary: payload.outcomeSummary.trim(),
|
||||
nextAction: payload.nextAction?.trim() || null,
|
||||
completedAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
updatedBy: userId
|
||||
});
|
||||
const completedEventRecord = await db.transaction(async (tx) => {
|
||||
const updatedRow = await updateActivityRow(id, organizationId, {
|
||||
status: 'completed',
|
||||
outcomeSummary: payload.outcomeSummary.trim(),
|
||||
nextAction: payload.nextAction?.trim() || null,
|
||||
completedAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
updatedBy: userId
|
||||
}, tx);
|
||||
const nextActivity = activityRowToBusinessEventRecord(updatedRow);
|
||||
await enqueueActivityBusinessEvent({
|
||||
client: tx,
|
||||
eventType: 'activity.completed',
|
||||
activity: nextActivity,
|
||||
previousActivity: current,
|
||||
actorUserId: userId
|
||||
});
|
||||
return nextActivity;
|
||||
});
|
||||
|
||||
const activity = await getActivityById(id, organizationId, context);
|
||||
await publishActivityBusinessEvent({
|
||||
eventType: 'activity.completed',
|
||||
activity,
|
||||
previousActivity: current,
|
||||
actorUserId: userId
|
||||
});
|
||||
|
||||
return activity;
|
||||
const activity = await getActivityById(completedEventRecord.id, organizationId, context);
|
||||
return activity;
|
||||
}
|
||||
|
||||
export async function cancelActivity(
|
||||
@@ -833,23 +861,27 @@ export async function cancelActivity(
|
||||
const current = await getHydratedActivityOrThrow(id, organizationId, context);
|
||||
assertTransitionAllowed(current.status, 'cancelled');
|
||||
|
||||
await updateActivityRow(id, organizationId, {
|
||||
status: 'cancelled',
|
||||
cancellationReason: payload.cancellationReason.trim(),
|
||||
cancelledAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
updatedBy: userId
|
||||
});
|
||||
const cancelledEventRecord = await db.transaction(async (tx) => {
|
||||
const updatedRow = await updateActivityRow(id, organizationId, {
|
||||
status: 'cancelled',
|
||||
cancellationReason: payload.cancellationReason.trim(),
|
||||
cancelledAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
updatedBy: userId
|
||||
}, tx);
|
||||
const nextActivity = activityRowToBusinessEventRecord(updatedRow);
|
||||
await enqueueActivityBusinessEvent({
|
||||
client: tx,
|
||||
eventType: 'activity.cancelled',
|
||||
activity: nextActivity,
|
||||
previousActivity: current,
|
||||
actorUserId: userId
|
||||
});
|
||||
return nextActivity;
|
||||
});
|
||||
|
||||
const activity = await getActivityById(id, organizationId, context);
|
||||
await publishActivityBusinessEvent({
|
||||
eventType: 'activity.cancelled',
|
||||
activity,
|
||||
previousActivity: current,
|
||||
actorUserId: userId
|
||||
});
|
||||
|
||||
return activity;
|
||||
const activity = await getActivityById(cancelledEventRecord.id, organizationId, context);
|
||||
return activity;
|
||||
}
|
||||
|
||||
export async function assignActivity(
|
||||
@@ -866,23 +898,27 @@ export async function assignActivity(
|
||||
await assertMembershipUserBelongsToOrganization(payload.assignedToId, organizationId);
|
||||
}
|
||||
|
||||
await updateActivityRow(id, organizationId, {
|
||||
assignedToId: payload.assignedToId,
|
||||
updatedAt: new Date(),
|
||||
updatedBy: userId
|
||||
});
|
||||
const assignedEventRecord = await db.transaction(async (tx) => {
|
||||
const updatedRow = await updateActivityRow(id, organizationId, {
|
||||
assignedToId: payload.assignedToId,
|
||||
updatedAt: new Date(),
|
||||
updatedBy: userId
|
||||
}, tx);
|
||||
const nextActivity = activityRowToBusinessEventRecord(updatedRow);
|
||||
if (current.assignedToId !== nextActivity.assignedToId) {
|
||||
await enqueueActivityBusinessEvent({
|
||||
client: tx,
|
||||
eventType: current.assignedToId ? 'activity.reassigned' : 'activity.assigned',
|
||||
activity: nextActivity,
|
||||
previousActivity: current,
|
||||
actorUserId: userId
|
||||
});
|
||||
}
|
||||
return nextActivity;
|
||||
});
|
||||
|
||||
const activity = await getActivityById(id, organizationId, context);
|
||||
if (current.assignedToId !== activity.assignedToId) {
|
||||
await publishActivityBusinessEvent({
|
||||
eventType: current.assignedToId ? 'activity.reassigned' : 'activity.assigned',
|
||||
activity,
|
||||
previousActivity: current,
|
||||
actorUserId: userId
|
||||
});
|
||||
}
|
||||
|
||||
return activity;
|
||||
const activity = await getActivityById(assignedEventRecord.id, organizationId, context);
|
||||
return activity;
|
||||
}
|
||||
|
||||
export async function rescheduleActivity(
|
||||
@@ -902,9 +938,10 @@ export async function rescheduleActivity(
|
||||
const nextStatus = current.status === 'draft' ? 'scheduled' : current.status;
|
||||
assertTransitionAllowed(current.status, nextStatus);
|
||||
|
||||
await updateActivityRow(id, organizationId, {
|
||||
scheduledStartAt:
|
||||
payload.scheduledStartAt === undefined
|
||||
const rescheduledEventRecord = await db.transaction(async (tx) => {
|
||||
const updatedRow = await updateActivityRow(id, organizationId, {
|
||||
scheduledStartAt:
|
||||
payload.scheduledStartAt === undefined
|
||||
? toDateOrNull(current.scheduledStartAt)
|
||||
: toDateOrNull(payload.scheduledStartAt),
|
||||
scheduledEndAt:
|
||||
@@ -912,20 +949,23 @@ export async function rescheduleActivity(
|
||||
? toDateOrNull(current.scheduledEndAt)
|
||||
: toDateOrNull(payload.scheduledEndAt),
|
||||
dueAt: payload.dueAt === undefined ? toDateOrNull(current.dueAt) : toDateOrNull(payload.dueAt),
|
||||
status: nextStatus,
|
||||
updatedAt: new Date(),
|
||||
updatedBy: userId
|
||||
});
|
||||
status: nextStatus,
|
||||
updatedAt: new Date(),
|
||||
updatedBy: userId
|
||||
}, tx);
|
||||
const nextActivity = activityRowToBusinessEventRecord(updatedRow);
|
||||
await enqueueActivityBusinessEvent({
|
||||
client: tx,
|
||||
eventType: 'activity.rescheduled',
|
||||
activity: nextActivity,
|
||||
previousActivity: current,
|
||||
actorUserId: userId
|
||||
});
|
||||
return nextActivity;
|
||||
});
|
||||
|
||||
const activity = await getActivityById(id, organizationId, context);
|
||||
await publishActivityBusinessEvent({
|
||||
eventType: 'activity.rescheduled',
|
||||
activity,
|
||||
previousActivity: current,
|
||||
actorUserId: userId
|
||||
});
|
||||
|
||||
return activity;
|
||||
const activity = await getActivityById(rescheduledEventRecord.id, organizationId, context);
|
||||
return activity;
|
||||
}
|
||||
|
||||
export async function deleteActivity(
|
||||
@@ -937,15 +977,18 @@ export async function deleteActivity(
|
||||
assertActivityPermission(context, CRM_ACTIVITY_PERMISSIONS.delete);
|
||||
const current = await getHydratedActivityOrThrow(id, organizationId, context);
|
||||
|
||||
await updateActivityRow(id, organizationId, {
|
||||
deletedAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
updatedBy: userId
|
||||
});
|
||||
await db.transaction(async (tx) => {
|
||||
await updateActivityRow(id, organizationId, {
|
||||
deletedAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
updatedBy: userId
|
||||
}, tx);
|
||||
|
||||
await publishActivityBusinessEvent({
|
||||
eventType: 'activity.deleted',
|
||||
activity: current,
|
||||
actorUserId: userId
|
||||
});
|
||||
await enqueueActivityBusinessEvent({
|
||||
client: tx,
|
||||
eventType: 'activity.deleted',
|
||||
activity: current,
|
||||
actorUserId: userId
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user