- Introduced new tables `timeline_footprints` and `timeline_activities` to manage employee location data and activity records. - Updated `company_settings` to include `gps_interval_seconds` and `checkout_warning_radius_meters` for enhanced tracking configuration. - Implemented foreign key constraints to ensure data integrity between new tables and existing `employees`, `customers`, and `visits` tables. - Created services and controllers for handling timeline activities and footprints, including ingestion and retrieval of data. - Enhanced DTOs and validation logic to support new fields and ensure correct data formats in API requests. - Added unit and integration tests to validate the new functionalities and ensure proper handling of timeline records. - Created migration scripts to apply the necessary database schema changes for the new features.
57 lines
1.6 KiB
TypeScript
57 lines
1.6 KiB
TypeScript
import {
|
|
bigint,
|
|
doublePrecision,
|
|
index,
|
|
pgTable,
|
|
text,
|
|
uuid,
|
|
} from 'drizzle-orm/pg-core';
|
|
import { customers } from './customers-table';
|
|
import { employees } from './employees-table';
|
|
import { visits } from './visits-table';
|
|
|
|
export const TIMELINE_ACTIVITY_TYPES = [
|
|
'branch_check_in',
|
|
'branch_check_out',
|
|
'customer_check_in',
|
|
'customer_check_out',
|
|
'sales_order_created',
|
|
'sales_request_created',
|
|
'sales_payment_created',
|
|
'customer_created',
|
|
] as const;
|
|
|
|
export type TimelineActivityType = (typeof TIMELINE_ACTIVITY_TYPES)[number];
|
|
|
|
export const timelineActivities = pgTable(
|
|
'timeline_activities',
|
|
{
|
|
id: uuid('id').defaultRandom().notNull().primaryKey(),
|
|
employeeId: uuid('employee_id')
|
|
.notNull()
|
|
.references(() => employees.id, { onDelete: 'restrict' }),
|
|
customerId: uuid('customer_id').references(() => customers.id, {
|
|
onDelete: 'set null',
|
|
}),
|
|
visitId: uuid('visit_id').references(() => visits.id, {
|
|
onDelete: 'set null',
|
|
}),
|
|
type: text('type').notNull(),
|
|
sourceType: text('source_type').notNull(),
|
|
sourceId: uuid('source_id').notNull(),
|
|
latitude: doublePrecision('latitude').notNull(),
|
|
longitude: doublePrecision('longitude').notNull(),
|
|
recordedAt: bigint('recorded_at', { mode: 'number' }).notNull(),
|
|
},
|
|
(t) => [
|
|
index('timeline_activities_employee_recorded_idx').on(
|
|
t.employeeId,
|
|
t.recordedAt,
|
|
),
|
|
index('timeline_activities_visit_id_idx').on(t.visitId),
|
|
],
|
|
);
|
|
|
|
export type TimelineActivityRow = typeof timelineActivities.$inferSelect;
|
|
export type NewTimelineActivityRow = typeof timelineActivities.$inferInsert;
|