feat: add time entry manager, controller, and API routes
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
import { SoActivityNotes as CwSoActivityNotes } from "../../generated/prisma/client";
|
||||
import { Translation, skipRow } from "./types";
|
||||
import { TranslationContext } from "./context";
|
||||
|
||||
type ApiActivityNotesRecord = {
|
||||
id: number;
|
||||
notes: string;
|
||||
activityId: number | null;
|
||||
internalAnalysisFlag: boolean;
|
||||
enteredById: string | null;
|
||||
updatedById: string | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
};
|
||||
|
||||
export const activityNotesTranslation: Translation<
|
||||
CwSoActivityNotes,
|
||||
ApiActivityNotesRecord,
|
||||
TranslationContext
|
||||
> = {
|
||||
values: [
|
||||
{ from: "soActivityNotesRecId", to: "id" },
|
||||
{
|
||||
from: "notes",
|
||||
to: "notes",
|
||||
process: (value: string) => value,
|
||||
},
|
||||
{
|
||||
from: "soActivityRecId",
|
||||
to: "activityId",
|
||||
process: (value: number, context: TranslationContext) => {
|
||||
if (!context.activityIds.has(value)) {
|
||||
skipRow(`Activity ${value} not found in API`);
|
||||
}
|
||||
return value;
|
||||
},
|
||||
},
|
||||
{
|
||||
from: "internalAnalysisFlag",
|
||||
to: "internalAnalysisFlag",
|
||||
process: (value) => Boolean(value),
|
||||
},
|
||||
{ from: "enteredBy", to: "enteredById" },
|
||||
{ from: "updatedBy", to: "updatedById" },
|
||||
{ from: "dateCreatedUtc", to: "createdAt" },
|
||||
{ from: "lastUpdateUtc", to: "updatedAt" },
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,55 @@
|
||||
import { SoActStatus as CwSoActStatus } from "../../generated/prisma/client";
|
||||
import { Translation } from "./types";
|
||||
|
||||
type ApiActivityStatusRecord = {
|
||||
id: number;
|
||||
name: string;
|
||||
description: string | null;
|
||||
closedFlag: boolean;
|
||||
inactiveFlag: boolean;
|
||||
defaultFlag: boolean;
|
||||
spawnFollowupFlag: boolean;
|
||||
createdById: string | null;
|
||||
updatedById: string | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
};
|
||||
|
||||
export const activityStatusTranslation: Translation<
|
||||
CwSoActStatus,
|
||||
ApiActivityStatusRecord
|
||||
> = {
|
||||
values: [
|
||||
{ from: "soActStatusRecId", to: "id" },
|
||||
{
|
||||
from: "description",
|
||||
to: "name",
|
||||
process: (value) => (value ? value : "Unknown Status"),
|
||||
},
|
||||
{ from: "description", to: "description" },
|
||||
{
|
||||
from: "closedFlag",
|
||||
to: "closedFlag",
|
||||
process: (value) => Boolean(value),
|
||||
},
|
||||
{
|
||||
from: "inactiveFlag",
|
||||
to: "inactiveFlag",
|
||||
process: (value) => Boolean(value),
|
||||
},
|
||||
{
|
||||
from: "defaultFlag",
|
||||
to: "defaultFlag",
|
||||
process: (value) => Boolean(value),
|
||||
},
|
||||
{
|
||||
from: "spawnFollowupFlag",
|
||||
to: "spawnFollowupFlag",
|
||||
process: (value) => Boolean(value),
|
||||
},
|
||||
{ from: "enteredBy", to: "createdById" },
|
||||
{ from: "updatedBy", to: "updatedById" },
|
||||
{ from: "dateEnteredUtc", to: "createdAt" },
|
||||
{ from: "lastUpdateUtc", to: "updatedAt" },
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,73 @@
|
||||
import { SoActivityType as CwSoActivityType } from "../../generated/prisma/client";
|
||||
import { Translation } from "./types";
|
||||
|
||||
type ApiActivityTypeRecord = {
|
||||
id: number;
|
||||
name: string;
|
||||
description: string;
|
||||
inactiveFlag: boolean;
|
||||
historyFlag: boolean;
|
||||
defaultFlag: boolean;
|
||||
importFlag: boolean;
|
||||
emailFlag: boolean;
|
||||
memoFlag: boolean;
|
||||
pointsValue: number | null;
|
||||
createdById: string | null;
|
||||
updatedById: string | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
};
|
||||
|
||||
export const activityTypeTranslation: Translation<
|
||||
CwSoActivityType,
|
||||
ApiActivityTypeRecord
|
||||
> = {
|
||||
values: [
|
||||
{ from: "soActivityTypeRecId", to: "id" },
|
||||
{
|
||||
from: "soActivityTypeId",
|
||||
to: "name",
|
||||
process: (value) => (value ? value : ""),
|
||||
},
|
||||
{
|
||||
from: "description",
|
||||
to: "description",
|
||||
process: (value) => (value ? value : ""),
|
||||
},
|
||||
{
|
||||
from: "inactiveFlag",
|
||||
to: "inactiveFlag",
|
||||
process: (value) => Boolean(value),
|
||||
},
|
||||
{
|
||||
from: "historyFlag",
|
||||
to: "historyFlag",
|
||||
process: (value) => Boolean(value),
|
||||
},
|
||||
{
|
||||
from: "defaultFlag",
|
||||
to: "defaultFlag",
|
||||
process: (value) => Boolean(value),
|
||||
},
|
||||
{
|
||||
from: "importFlag",
|
||||
to: "importFlag",
|
||||
process: (value) => Boolean(value),
|
||||
},
|
||||
{
|
||||
from: "emailFlag",
|
||||
to: "emailFlag",
|
||||
process: (value) => Boolean(value),
|
||||
},
|
||||
{
|
||||
from: "memoFlag",
|
||||
to: "memoFlag",
|
||||
process: (value) => Boolean(value ?? false),
|
||||
},
|
||||
{ from: "pointsValue", to: "pointsValue" },
|
||||
{ from: "enteredBy", to: "createdById" },
|
||||
{ from: "updatedBy", to: "updatedById" },
|
||||
{ from: "dateEnteredUtc", to: "createdAt" },
|
||||
{ from: "lastUpdateUtc", to: "updatedAt" },
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,147 @@
|
||||
import { SoActivity as CwSoActivity } from "../../generated/prisma/client";
|
||||
import { Translation, skipRow } from "./types";
|
||||
import { TranslationContext } from "./context";
|
||||
|
||||
type ApiActivityRecord = {
|
||||
id: number;
|
||||
subject: string;
|
||||
startTime: Date;
|
||||
endTime: Date;
|
||||
assignToId: string;
|
||||
assignedById: string;
|
||||
enteredBy: string;
|
||||
automated: boolean;
|
||||
closedFlag: boolean;
|
||||
notifyCompleteFlag: boolean;
|
||||
notificationSentFlat: boolean;
|
||||
opportunityId: string | null;
|
||||
serviceTicketId: string | null;
|
||||
contactId: number | null;
|
||||
companyId: number | null;
|
||||
activityTypeId: number | null;
|
||||
activityStatusId: number | null;
|
||||
createdById: string | null;
|
||||
updatedById: string | null;
|
||||
closedById: string | null;
|
||||
closedAt: Date | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
};
|
||||
|
||||
export const activityTranslation: Translation<
|
||||
CwSoActivity,
|
||||
ApiActivityRecord,
|
||||
TranslationContext
|
||||
> = {
|
||||
values: [
|
||||
{ from: "soActivityRecId", to: "id" },
|
||||
{
|
||||
from: "subject",
|
||||
to: "subject",
|
||||
process: (value) => (value ? value : ""),
|
||||
},
|
||||
{
|
||||
from: "dateTimeStart",
|
||||
to: "startTime",
|
||||
process: (value) => {
|
||||
if (!value) skipRow("Missing dateTimeStart");
|
||||
return value as Date;
|
||||
},
|
||||
},
|
||||
{
|
||||
from: "dateTimeEnd",
|
||||
to: "endTime",
|
||||
process: (value) => {
|
||||
if (!value) skipRow("Missing dateTimeEnd");
|
||||
return value as Date;
|
||||
},
|
||||
},
|
||||
{
|
||||
from: "assignTo",
|
||||
to: "assignToId",
|
||||
},
|
||||
{
|
||||
from: "assignedBy",
|
||||
to: "assignedById",
|
||||
},
|
||||
{
|
||||
from: "enteredBy",
|
||||
to: "enteredBy",
|
||||
},
|
||||
{
|
||||
from: "automated",
|
||||
to: "automated",
|
||||
process: (value) => Boolean(value),
|
||||
},
|
||||
{
|
||||
from: "closeFlag",
|
||||
to: "closedFlag",
|
||||
process: (value) => Boolean(value),
|
||||
},
|
||||
{
|
||||
from: "notifyCompleteFlag",
|
||||
to: "notifyCompleteFlag",
|
||||
process: (value) => Boolean(value),
|
||||
},
|
||||
{
|
||||
from: "notificationSentFlag",
|
||||
to: "notificationSentFlat",
|
||||
process: (value) => Boolean(value),
|
||||
},
|
||||
{
|
||||
// opportunityId is a String? field with no @relation — store as null
|
||||
// until a uid-mapping approach is established
|
||||
from: "opportunityRecId",
|
||||
to: "opportunityId",
|
||||
process: () => null,
|
||||
},
|
||||
{
|
||||
// serviceTicketId is a String? field with no @relation — store as null
|
||||
// until a uid-mapping approach is established
|
||||
from: "srServiceRecId",
|
||||
to: "serviceTicketId",
|
||||
process: () => null,
|
||||
},
|
||||
{
|
||||
from: "contactRecId",
|
||||
to: "contactId",
|
||||
process: (value: number | null, context: TranslationContext) => {
|
||||
if (!value) return null;
|
||||
if (!context.contactIds.has(value)) return null;
|
||||
return value;
|
||||
},
|
||||
},
|
||||
{
|
||||
from: "companyRecId",
|
||||
to: "companyId",
|
||||
process: (value: number | null, context: TranslationContext) => {
|
||||
if (!value) return null;
|
||||
if (!context.companyIds.has(value)) return null;
|
||||
return value;
|
||||
},
|
||||
},
|
||||
{
|
||||
from: "soActivityTypeRecId",
|
||||
to: "activityTypeId",
|
||||
process: (value: number | null, context: TranslationContext) => {
|
||||
if (!value) return null;
|
||||
if (!context.activityTypeIds.has(value)) return null;
|
||||
return value;
|
||||
},
|
||||
},
|
||||
{
|
||||
from: "soActStatusRecId",
|
||||
to: "activityStatusId",
|
||||
process: (value: number, context: TranslationContext) => {
|
||||
if (!context.activityStatusIds.has(value)) return null;
|
||||
return value;
|
||||
},
|
||||
},
|
||||
{ from: "enteredBy", to: "createdById" },
|
||||
{ from: "updatedBy", to: "updatedById" },
|
||||
{ from: "closedBy", to: "closedById" },
|
||||
{ from: "dateClosedUtc", to: "closedAt" },
|
||||
{ from: "dateEnteredUtc", to: "createdAt" },
|
||||
{ from: "lastUpdatedUTC", to: "updatedAt" },
|
||||
],
|
||||
};
|
||||
@@ -73,6 +73,21 @@ export interface TranslationContext {
|
||||
|
||||
// Set of API TaxCode.id values for FK validation
|
||||
taxCodeIds: Set<number>;
|
||||
|
||||
// Set of API ActivityType.id values for FK validation
|
||||
activityTypeIds: Set<number>;
|
||||
|
||||
// Set of API ActivityStatus.id values for FK validation
|
||||
activityStatusIds: Set<number>;
|
||||
|
||||
// Set of API Activity.id values for FK validation (used by ActivityNotes)
|
||||
activityIds: Set<number>;
|
||||
|
||||
// Set of API TimeEntryChargeCode.id values for FK validation
|
||||
timeEntryChargeCodeIds: Set<number>;
|
||||
|
||||
// Map: CW TE_Status_ID (smallint) -> API TimeEntryStatus.id (rec ID)
|
||||
timeEntryStatusIdByTeStatusId: Map<number, number>;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -103,5 +118,10 @@ export function createTranslationContext(): TranslationContext {
|
||||
scheduleTypeIds: new Set(),
|
||||
scheduleSpanIds: new Set(),
|
||||
taxCodeIds: new Set(),
|
||||
activityTypeIds: new Set(),
|
||||
activityStatusIds: new Set(),
|
||||
activityIds: new Set(),
|
||||
timeEntryChargeCodeIds: new Set(),
|
||||
timeEntryStatusIdByTeStatusId: new Map(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import { MemberType as CwMemberType } from "../../generated/prisma/client";
|
||||
import { Translation } from "./types";
|
||||
|
||||
type ApiCwMemberTypeRecord = {
|
||||
id: number;
|
||||
description: string | null;
|
||||
inactiveFlag: boolean;
|
||||
createdById: string | null;
|
||||
updatedById: string | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
};
|
||||
|
||||
export const cwMemberTypeTranslation: Translation<
|
||||
CwMemberType,
|
||||
ApiCwMemberTypeRecord
|
||||
> = {
|
||||
values: [
|
||||
{ from: "memberTypeRecId", to: "id" },
|
||||
{ from: "description", to: "description" },
|
||||
{
|
||||
from: "inactiveFlag",
|
||||
to: "inactiveFlag",
|
||||
process: (value) => Boolean(value),
|
||||
},
|
||||
{ from: "enteredBy", to: "createdById" },
|
||||
{ from: "updatedBy", to: "updatedById" },
|
||||
{
|
||||
from: "dateEnteredUtc",
|
||||
to: "createdAt",
|
||||
process: (value) => (value as Date | null) ?? new Date(0),
|
||||
},
|
||||
{
|
||||
from: "lastUpdateUtc",
|
||||
to: "updatedAt",
|
||||
process: (value) => (value as Date | null) ?? new Date(0),
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,51 @@
|
||||
import { ActivityClass as CwActivityClass } from "../../generated/prisma/client";
|
||||
import { Translation } from "./types";
|
||||
|
||||
type ApiTimeActivityClassRecord = {
|
||||
id: number;
|
||||
description: string | null;
|
||||
hourlyRate: number | null;
|
||||
inactiveFlag: boolean;
|
||||
taxExemptFlag: boolean;
|
||||
createdById: string | null;
|
||||
updatedById: string | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
};
|
||||
|
||||
export const timeActivityClassTranslation: Translation<
|
||||
CwActivityClass,
|
||||
ApiTimeActivityClassRecord
|
||||
> = {
|
||||
values: [
|
||||
{ from: "activityClassRecId", to: "id" },
|
||||
{ from: "description", to: "description" },
|
||||
{
|
||||
from: "hourlyRate",
|
||||
to: "hourlyRate",
|
||||
process: (value) => (value != null ? Number(value) : null),
|
||||
},
|
||||
{
|
||||
from: "inactiveFlag",
|
||||
to: "inactiveFlag",
|
||||
process: (value) => Boolean(value),
|
||||
},
|
||||
{
|
||||
from: "taxExemptFlag",
|
||||
to: "taxExemptFlag",
|
||||
process: (value) => Boolean(value),
|
||||
},
|
||||
{ from: "enteredBy", to: "createdById" },
|
||||
{ from: "updatedBy", to: "updatedById" },
|
||||
{
|
||||
from: "dateEnteredUtc",
|
||||
to: "createdAt",
|
||||
process: (value) => (value as Date | null) ?? new Date(0),
|
||||
},
|
||||
{
|
||||
from: "lastUpdatedUtc",
|
||||
to: "updatedAt",
|
||||
process: (value) => (value as Date | null) ?? new Date(0),
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,93 @@
|
||||
import { ActivityType as CwActivityType } from "../../generated/prisma/client";
|
||||
import { Translation } from "./types";
|
||||
|
||||
type ApiTimeActivityTypeRecord = {
|
||||
id: number;
|
||||
description: string | null;
|
||||
minHours: number | null;
|
||||
maxHours: number | null;
|
||||
rate: number | null;
|
||||
costMultiplier: number;
|
||||
inactiveFlag: boolean;
|
||||
invoiceFlag: boolean;
|
||||
billableFlag: boolean;
|
||||
utilizationFlag: boolean;
|
||||
defaultFlag: boolean;
|
||||
multiplierFlag: boolean;
|
||||
createdById: string | null;
|
||||
updatedById: string | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
};
|
||||
|
||||
export const timeActivityTypeTranslation: Translation<
|
||||
CwActivityType,
|
||||
ApiTimeActivityTypeRecord
|
||||
> = {
|
||||
values: [
|
||||
{ from: "activityTypeRecId", to: "id" },
|
||||
{ from: "description", to: "description" },
|
||||
{
|
||||
from: "hoursMin",
|
||||
to: "minHours",
|
||||
process: (value) => (value != null ? Number(value) : null),
|
||||
},
|
||||
{
|
||||
from: "hoursMax",
|
||||
to: "maxHours",
|
||||
process: (value) => (value != null ? Number(value) : null),
|
||||
},
|
||||
{
|
||||
from: "rate",
|
||||
to: "rate",
|
||||
process: (value) => (value != null ? Number(value) : null),
|
||||
},
|
||||
{
|
||||
from: "costMultiplier",
|
||||
to: "costMultiplier",
|
||||
process: (value) => (value != null ? Number(value) : 1),
|
||||
},
|
||||
{
|
||||
from: "inactiveFlag",
|
||||
to: "inactiveFlag",
|
||||
process: (value) => Boolean(value),
|
||||
},
|
||||
{
|
||||
from: "invoiceFlag",
|
||||
to: "invoiceFlag",
|
||||
process: (value) => Boolean(value),
|
||||
},
|
||||
{
|
||||
from: "billableFlag",
|
||||
to: "billableFlag",
|
||||
process: (value) => Boolean(value),
|
||||
},
|
||||
{
|
||||
from: "utilizationFlag",
|
||||
to: "utilizationFlag",
|
||||
process: (value) => Boolean(value),
|
||||
},
|
||||
{
|
||||
from: "defaultFlag",
|
||||
to: "defaultFlag",
|
||||
process: (value) => Boolean(value ?? false),
|
||||
},
|
||||
{
|
||||
from: "multiplierFlag",
|
||||
to: "multiplierFlag",
|
||||
process: (value) => Boolean(value),
|
||||
},
|
||||
{ from: "enteredBy", to: "createdById" },
|
||||
{ from: "updatedBy", to: "updatedById" },
|
||||
{
|
||||
from: "dateEnteredUtc",
|
||||
to: "createdAt",
|
||||
process: (value) => (value as Date | null) ?? new Date(0),
|
||||
},
|
||||
{
|
||||
from: "lastUpdatedUtc",
|
||||
to: "updatedAt",
|
||||
process: (value) => (value as Date | null) ?? new Date(0),
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,59 @@
|
||||
import { TeChargeCode as CwTeChargeCode } from "../../generated/prisma/client";
|
||||
import { Translation } from "./types";
|
||||
|
||||
type ApiTimeEntryChargeCodeRecord = {
|
||||
id: number;
|
||||
chargeCodeId: number;
|
||||
description: string | null;
|
||||
expenseFlag: boolean;
|
||||
timeFlag: boolean;
|
||||
billableFlag: boolean;
|
||||
invoiceFlag: boolean;
|
||||
createdById: string | null;
|
||||
updatedById: string | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
};
|
||||
|
||||
export const timeEntryChargeCodeTranslation: Translation<
|
||||
CwTeChargeCode,
|
||||
ApiTimeEntryChargeCodeRecord
|
||||
> = {
|
||||
values: [
|
||||
{ from: "teChargeCodeRecId", to: "id" },
|
||||
{ from: "teChargeCodeRecId", to: "chargeCodeId" },
|
||||
{ from: "description", to: "description" },
|
||||
{
|
||||
from: "expenseFlag",
|
||||
to: "expenseFlag",
|
||||
process: (value) => Boolean(value),
|
||||
},
|
||||
{
|
||||
from: "timeFlag",
|
||||
to: "timeFlag",
|
||||
process: (value) => Boolean(value),
|
||||
},
|
||||
{
|
||||
from: "billableFlag",
|
||||
to: "billableFlag",
|
||||
process: (value) => Boolean(value),
|
||||
},
|
||||
{
|
||||
from: "invoiceFlag",
|
||||
to: "invoiceFlag",
|
||||
process: (value) => Boolean(value ?? false),
|
||||
},
|
||||
{ from: "enteredBy", to: "createdById" },
|
||||
{ from: "updatedBy", to: "updatedById" },
|
||||
{
|
||||
from: "dateEnteredUtc",
|
||||
to: "createdAt",
|
||||
process: (value) => (value as Date | null) ?? new Date(0),
|
||||
},
|
||||
{
|
||||
from: "lastUpdateUtc",
|
||||
to: "updatedAt",
|
||||
process: (value) => (value as Date | null) ?? new Date(0),
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
import { TeStatus as CwTeStatus } from "../../generated/prisma/client";
|
||||
import { Translation } from "./types";
|
||||
|
||||
type ApiTimeEntryStatusRecord = {
|
||||
id: number;
|
||||
statusId: number;
|
||||
description: string | null;
|
||||
action: string | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
};
|
||||
|
||||
export const timeEntryStatusTranslation: Translation<
|
||||
CwTeStatus,
|
||||
ApiTimeEntryStatusRecord
|
||||
> = {
|
||||
values: [
|
||||
{ from: "teStatusRecId", to: "id" },
|
||||
{ from: "teStatusId", to: "statusId" },
|
||||
{ from: "description", to: "description" },
|
||||
{ from: "action", to: "action" },
|
||||
{
|
||||
from: "lastUpdatedUtc",
|
||||
to: "createdAt",
|
||||
process: (value) => (value as Date | null) ?? new Date(0),
|
||||
},
|
||||
{
|
||||
from: "lastUpdatedUtc",
|
||||
to: "updatedAt",
|
||||
process: (value) => (value as Date | null) ?? new Date(0),
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,208 @@
|
||||
import { TimeEntry as CwTimeEntry } from "../../generated/prisma/client";
|
||||
import { Translation, skipRow } from "./types";
|
||||
import { TranslationContext } from "./context";
|
||||
|
||||
type ApiTimeEntryRecord = {
|
||||
id: number;
|
||||
uid: string;
|
||||
memberId: string | null;
|
||||
companyId: number;
|
||||
serviceTicketId: number | null;
|
||||
activityId: number | null;
|
||||
chargeCodeId: number | null;
|
||||
statusId: number | null;
|
||||
contactId: number | null;
|
||||
dateStart: Date | null;
|
||||
timeStart: Date | null;
|
||||
timeEnd: Date | null;
|
||||
notes: string | null;
|
||||
notesMd: string | null;
|
||||
internalNote: string | null;
|
||||
billableHours: number | null;
|
||||
actualHours: number | null;
|
||||
invoicedHours: number | null;
|
||||
deductedHours: number | null;
|
||||
hourlyRate: number | null;
|
||||
effectiveRate: number | null;
|
||||
issueFlag: boolean;
|
||||
mergedFlag: boolean;
|
||||
invoiceFlag: boolean;
|
||||
billableFlag: boolean;
|
||||
documentFlag: boolean;
|
||||
teProblemFlag: boolean;
|
||||
teResolutionFlag: boolean;
|
||||
teInternalAnalysisFlag: boolean;
|
||||
chargeToRecId: number | null;
|
||||
chargeToType: string | null;
|
||||
createdById: string | null;
|
||||
updatedById: string | null;
|
||||
originalAuthorId: string | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
};
|
||||
|
||||
export const timeEntryTranslation: Translation<
|
||||
CwTimeEntry,
|
||||
ApiTimeEntryRecord,
|
||||
TranslationContext
|
||||
> = {
|
||||
values: [
|
||||
{ from: "timeRecId", to: "id" },
|
||||
{
|
||||
from: "mobileGuid",
|
||||
to: "uid",
|
||||
process: (value: string) => value,
|
||||
},
|
||||
{ from: "memberId", to: "memberId" },
|
||||
{
|
||||
from: "companyRecId",
|
||||
to: "companyId",
|
||||
process: (value: number, context: TranslationContext) => {
|
||||
if (!context.companyIds.has(value)) return skipRow(`company ${value} not found`);
|
||||
return value;
|
||||
},
|
||||
},
|
||||
{
|
||||
from: "srServiceRecId",
|
||||
to: "serviceTicketId",
|
||||
process: (value: number | null, context: TranslationContext) => {
|
||||
if (!value) return null;
|
||||
if (!context.serviceTicketIds.has(value)) return null;
|
||||
return value;
|
||||
},
|
||||
},
|
||||
{
|
||||
from: "soActivityRecId",
|
||||
to: "activityId",
|
||||
process: (value: number | null, context: TranslationContext) => {
|
||||
if (!value) return null;
|
||||
if (!context.activityIds.has(value)) return null;
|
||||
return value;
|
||||
},
|
||||
},
|
||||
{
|
||||
from: "teChargeCodeRecId",
|
||||
to: "chargeCodeId",
|
||||
process: (value: number | null, context: TranslationContext) => {
|
||||
if (!value) return null;
|
||||
if (!context.timeEntryChargeCodeIds.has(value)) return null;
|
||||
return value;
|
||||
},
|
||||
},
|
||||
{
|
||||
from: "teStatusId",
|
||||
to: "statusId",
|
||||
process: (value: number, context: TranslationContext) => {
|
||||
return context.timeEntryStatusIdByTeStatusId.get(value) ?? null;
|
||||
},
|
||||
},
|
||||
{
|
||||
from: "contactRecId",
|
||||
to: "contactId",
|
||||
process: (value: number | null, context: TranslationContext) => {
|
||||
if (!value) return null;
|
||||
if (!context.contactIds.has(value)) return null;
|
||||
return value;
|
||||
},
|
||||
},
|
||||
{ from: "dateStart", to: "dateStart" },
|
||||
{ from: "timeStart", to: "timeStart" },
|
||||
{ from: "timeEnd", to: "timeEnd" },
|
||||
{
|
||||
from: "notes",
|
||||
to: "notes",
|
||||
process: (value: string | null) => value ?? null,
|
||||
},
|
||||
{
|
||||
from: "notesMarkdown",
|
||||
to: "notesMd",
|
||||
process: (value: string | null) => value ?? null,
|
||||
},
|
||||
{ from: "internalNote", to: "internalNote" },
|
||||
{
|
||||
from: "hoursBill",
|
||||
to: "billableHours",
|
||||
process: (value) => (value != null ? Number(value) : null),
|
||||
},
|
||||
{
|
||||
from: "hoursActual",
|
||||
to: "actualHours",
|
||||
process: (value) => (value != null ? Number(value) : null),
|
||||
},
|
||||
{
|
||||
from: "hoursInvoiced",
|
||||
to: "invoicedHours",
|
||||
process: (value) => (value != null ? Number(value) : null),
|
||||
},
|
||||
{
|
||||
from: "hoursDeduct",
|
||||
to: "deductedHours",
|
||||
process: (value) => (value != null ? Number(value) : null),
|
||||
},
|
||||
{
|
||||
from: "hourlyRate",
|
||||
to: "hourlyRate",
|
||||
process: (value) => (value != null ? Number(value) : null),
|
||||
},
|
||||
{
|
||||
from: "effectiveRate",
|
||||
to: "effectiveRate",
|
||||
process: (value) => (value != null ? Number(value) : null),
|
||||
},
|
||||
{
|
||||
from: "issueFlag",
|
||||
to: "issueFlag",
|
||||
process: (value) => Boolean(value),
|
||||
},
|
||||
{
|
||||
from: "mergedFlag",
|
||||
to: "mergedFlag",
|
||||
process: (value) => Boolean(value),
|
||||
},
|
||||
{
|
||||
from: "invoiceFlag",
|
||||
to: "invoiceFlag",
|
||||
process: (value) => Boolean(value),
|
||||
},
|
||||
{
|
||||
from: "billableFlag",
|
||||
to: "billableFlag",
|
||||
process: (value) => Boolean(value),
|
||||
},
|
||||
{
|
||||
from: "documentFlag",
|
||||
to: "documentFlag",
|
||||
process: (value) => Boolean(value),
|
||||
},
|
||||
{
|
||||
from: "teProblemFlag",
|
||||
to: "teProblemFlag",
|
||||
process: (value) => Boolean(value),
|
||||
},
|
||||
{
|
||||
from: "teResolutionFlag",
|
||||
to: "teResolutionFlag",
|
||||
process: (value) => Boolean(value),
|
||||
},
|
||||
{
|
||||
from: "teInternalAnalysisFlag",
|
||||
to: "teInternalAnalysisFlag",
|
||||
process: (value) => Boolean(value),
|
||||
},
|
||||
{ from: "chargeToRecId", to: "chargeToRecId" },
|
||||
{ from: "chargeToType", to: "chargeToType" },
|
||||
{ from: "enteredBy", to: "createdById" },
|
||||
{ from: "updatedBy", to: "updatedById" },
|
||||
{ from: "originalAuthor", to: "originalAuthorId" },
|
||||
{
|
||||
from: "dateEnteredUtc",
|
||||
to: "createdAt",
|
||||
process: (value) => (value as Date | null) ?? new Date(0),
|
||||
},
|
||||
{
|
||||
from: "lastUpdateUtc",
|
||||
to: "updatedAt",
|
||||
process: (value) => (value as Date | null) ?? new Date(0),
|
||||
},
|
||||
],
|
||||
};
|
||||
Reference in New Issue
Block a user