task-ep.1.1
This commit is contained in:
@@ -1,60 +1,100 @@
|
||||
import { PERMISSIONS } from '@/lib/auth/rbac';
|
||||
|
||||
export const CRM_ACTIVITY_ENTITY_TYPES = [
|
||||
'customer',
|
||||
'contact',
|
||||
'lead',
|
||||
'opportunity',
|
||||
'quotation',
|
||||
'customer',
|
||||
'contact',
|
||||
'purchase_order',
|
||||
'service_report'
|
||||
'internal'
|
||||
] as const;
|
||||
|
||||
export const CRM_ACTIVITY_TYPES = [
|
||||
'site_visit',
|
||||
'follow_up',
|
||||
'meeting',
|
||||
'visit',
|
||||
'phone_call',
|
||||
'email_follow_up',
|
||||
'quotation_follow_up',
|
||||
'site_survey',
|
||||
'requirement_discussion',
|
||||
'presentation',
|
||||
'internal_task',
|
||||
'reminder_action',
|
||||
'other'
|
||||
] as const;
|
||||
|
||||
export const CRM_ACTIVITY_STATUSES = [
|
||||
'planned',
|
||||
'draft',
|
||||
'scheduled',
|
||||
'in_progress',
|
||||
'completed',
|
||||
'cancelled',
|
||||
'skipped',
|
||||
'overdue'
|
||||
] as const;
|
||||
|
||||
export const CRM_ACTIVITY_PRIORITIES = ['low', 'medium', 'high', 'urgent'] as const;
|
||||
export const CRM_ACTIVITY_STORED_STATUSES = [
|
||||
'draft',
|
||||
'scheduled',
|
||||
'in_progress',
|
||||
'completed',
|
||||
'cancelled',
|
||||
'skipped'
|
||||
] as const;
|
||||
|
||||
export const CRM_ACTIVITY_PRIORITIES = ['low', 'normal', 'high', 'critical'] as const;
|
||||
|
||||
export type CrmActivityEntityType = (typeof CRM_ACTIVITY_ENTITY_TYPES)[number];
|
||||
export type CrmActivityType = (typeof CRM_ACTIVITY_TYPES)[number];
|
||||
export type CrmActivityStatus = (typeof CRM_ACTIVITY_STATUSES)[number];
|
||||
export type CrmActivityPriority = (typeof CRM_ACTIVITY_PRIORITIES)[number];
|
||||
export type CrmActivityStoredStatus = (typeof CRM_ACTIVITY_STORED_STATUSES)[number];
|
||||
|
||||
export interface CrmActivityRelatedRecord {
|
||||
entityType: CrmActivityEntityType;
|
||||
entityId: string;
|
||||
}
|
||||
|
||||
export interface CrmActivityRecord {
|
||||
id: string;
|
||||
organizationId: string;
|
||||
entityType: CrmActivityEntityType;
|
||||
entityId: string;
|
||||
primaryEntityType: CrmActivityEntityType;
|
||||
primaryEntityId: string | null;
|
||||
primaryRecordLabel: string | null;
|
||||
relatedRecords: CrmActivityRelatedRecord[];
|
||||
customerId: string | null;
|
||||
contactId: string | null;
|
||||
leadId: string | null;
|
||||
opportunityId: string | null;
|
||||
quotationId: string | null;
|
||||
activityType: CrmActivityType;
|
||||
subject: string;
|
||||
description: string | null;
|
||||
ownerId: string | null;
|
||||
ownerId: string;
|
||||
assignedToId: string | null;
|
||||
dueDate: string | null;
|
||||
dueTime: string | null;
|
||||
scheduledStartAt: string | null;
|
||||
scheduledEndAt: string | null;
|
||||
dueAt: string | null;
|
||||
completedAt: string | null;
|
||||
status: Exclude<CrmActivityStatus, 'overdue'>;
|
||||
cancelledAt: string | null;
|
||||
skippedAt: string | null;
|
||||
status: CrmActivityStoredStatus;
|
||||
priority: CrmActivityPriority;
|
||||
outcome: string | null;
|
||||
outcomeSummary: string | null;
|
||||
cancellationReason: string | null;
|
||||
skipReason: string | null;
|
||||
nextAction: string | null;
|
||||
branchId: string | null;
|
||||
productType: string | null;
|
||||
isInternalOnly: boolean;
|
||||
isPricingSensitive: boolean;
|
||||
parentActivityId: string | null;
|
||||
metadata: Record<string, unknown> | null;
|
||||
createdBy: string;
|
||||
updatedBy: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
deletedAt: string | null;
|
||||
}
|
||||
|
||||
export const CRM_ACTIVITY_PERMISSIONS = {
|
||||
@@ -62,25 +102,52 @@ export const CRM_ACTIVITY_PERMISSIONS = {
|
||||
create: PERMISSIONS.crmActivityCreate,
|
||||
update: PERMISSIONS.crmActivityUpdate,
|
||||
complete: PERMISSIONS.crmActivityComplete,
|
||||
cancel: PERMISSIONS.crmActivityUpdate,
|
||||
reassign: PERMISSIONS.crmActivityUpdate,
|
||||
delete: PERMISSIONS.crmActivityDelete,
|
||||
auditLogRead: PERMISSIONS.crmAuditLogRead
|
||||
} as const;
|
||||
|
||||
function parseDate(value?: string | null): number | null {
|
||||
if (!value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const parsed = Date.parse(value);
|
||||
return Number.isNaN(parsed) ? null : parsed;
|
||||
}
|
||||
|
||||
export function resolveCrmActivityStatus(input: {
|
||||
status: Exclude<CrmActivityStatus, 'overdue'>;
|
||||
dueDate?: string | null;
|
||||
status: CrmActivityStoredStatus;
|
||||
scheduledStartAt?: string | null;
|
||||
dueAt?: string | null;
|
||||
completedAt?: string | null;
|
||||
cancelledAt?: string | null;
|
||||
skippedAt?: string | null;
|
||||
now?: Date;
|
||||
}): CrmActivityStatus {
|
||||
if (input.completedAt || input.status === 'completed' || input.status === 'cancelled') {
|
||||
if (
|
||||
input.completedAt ||
|
||||
input.cancelledAt ||
|
||||
input.skippedAt ||
|
||||
input.status === 'completed' ||
|
||||
input.status === 'cancelled' ||
|
||||
input.status === 'skipped'
|
||||
) {
|
||||
return input.status;
|
||||
}
|
||||
|
||||
if (!input.dueDate) {
|
||||
const compareAt = parseDate(input.dueAt) ?? parseDate(input.scheduledStartAt);
|
||||
if (compareAt === null) {
|
||||
return input.status;
|
||||
}
|
||||
|
||||
const today = (input.now ?? new Date()).toISOString().slice(0, 10);
|
||||
return input.dueDate < today ? 'overdue' : input.status;
|
||||
return compareAt < (input.now ?? new Date()).getTime() ? 'overdue' : input.status;
|
||||
}
|
||||
|
||||
export function formatCrmActivityLabel(value: string): string {
|
||||
return value
|
||||
.split('_')
|
||||
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
||||
.join(' ');
|
||||
}
|
||||
import { PERMISSIONS } from '@/lib/auth/rbac';
|
||||
|
||||
Reference in New Issue
Block a user