Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | import { Injectable, NotFoundException, ConflictException, } from '@nestjs/common'; import { InjectModel } from '@nestjs/mongoose'; import { Model } from 'mongoose'; import { AttendanceDevice, AttendanceDeviceEmployeeMap, AttendanceDeviceLog, AttendanceDeviceSync, } from '../schemas'; import { RegisterDeviceDto, DeviceWebhookPayloadDto, MapDeviceEmployeeDto, } from './dto/device.dto'; import { CaptureService } from '../capture/capture.service'; @Injectable() export class DeviceIntegrationService { constructor( @InjectModel(AttendanceDevice.name) private deviceModel: Model<AttendanceDevice>, @InjectModel(AttendanceDeviceEmployeeMap.name) private deviceMapModel: Model<AttendanceDeviceEmployeeMap>, @InjectModel(AttendanceDeviceLog.name) private deviceLogModel: Model<AttendanceDeviceLog>, @InjectModel(AttendanceDeviceSync.name) private syncModel: Model<AttendanceDeviceSync>, private captureService: CaptureService, ) {} async registerDevice(tenantId: string, registerDto: RegisterDeviceDto) { const existing = await this.deviceModel.findOne({ tenantId, deviceCode: registerDto.deviceCode, }); if (existing) { throw new ConflictException( `Device with code ${registerDto.deviceCode} already registered`, ); } const created = new this.deviceModel({ ...registerDto, tenantId, }); return created.save(); } async mapEmployee( tenantId: string, deviceId: string, mapDto: MapDeviceEmployeeDto, ) { await this.deviceMapModel.findOneAndDelete({ tenantId, deviceId, employeeId: mapDto.employeeId, }); await this.deviceMapModel.findOneAndDelete({ tenantId, deviceId, deviceEmployeeId: mapDto.deviceEmployeeId, }); const created = new this.deviceMapModel({ ...mapDto, tenantId, deviceId, }); return created.save(); } async processWebhook(tenantId: string, payload: DeviceWebhookPayloadDto) { // 1. Find device const device = await this.deviceModel .findOne({ tenantId, deviceCode: payload.deviceCode }) .exec(); if (!device) throw new NotFoundException('Device not found'); // 2. Save raw log const log = new this.deviceLogModel({ tenantId, deviceId: device._id, deviceEmployeeId: payload.deviceEmployeeId, timestamp: new Date(payload.timestamp), punchTypeRaw: payload.punchTypeRaw, rawData: payload.rawData, }); await log.save(); // 3. Find employee mapping const map = await this.deviceMapModel .findOne({ tenantId, deviceId: device._id, deviceEmployeeId: payload.deviceEmployeeId, }) .exec(); // 4. If mapped, pass to Capture Service if (map) { const punch = await this.captureService.recordPunch( tenantId, map.employeeId, { punchType: this.mapPunchType(payload.punchTypeRaw), timestamp: new Date(payload.timestamp), timezone: device.timezone || 'UTC', source: 'Biometric', deviceId: device._id.toString(), deviceName: device.deviceName, }, 'system', device.ipAddress || '0.0.0.0', ); log.processed = true; log.punchId = punch._id as any; await log.save(); } return { status: 'received' }; } private mapPunchType(rawType?: string): string { // Simple heuristic. Real system maps per provider. if (!rawType) return 'Check In'; const lower = rawType.toLowerCase(); if (lower.includes('out')) return 'Check Out'; return 'Check In'; } } |