588 lines
22 KiB
Python
588 lines
22 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
from datetime import datetime, timedelta
|
|
|
|
from odoo import models, fields, api, _
|
|
from odoo.exceptions import ValidationError
|
|
from .helper import httpHelper, is_valid_port, is_valid_ip, ParsedRequest
|
|
|
|
|
|
class SystemAttendance(models.Model):
|
|
_name = 'finger.system_attendance'
|
|
_order = 'punch_time DESC'
|
|
|
|
punch_state = fields.Char("Punch state", readonly=True)
|
|
punch_state_display = fields.Char("Punch state display", readonly=True)
|
|
area_alias = fields.Char("Area", readonly=True)
|
|
crc = fields.Char("CRC", readonly=True)
|
|
verify_type = fields.Integer("Verify type", readonly=True)
|
|
emp = fields.Integer("Empolyee code", readonly=True)
|
|
is_attendance = fields.Integer("Is attendance", readonly=True)
|
|
attendance_id = fields.Integer("Attendance id", readonly=True)
|
|
upload_time = fields.Datetime(string='Upload time', readonly=True)
|
|
punch_time = fields.Datetime(string="Punch time", readonly=True)
|
|
emp_code = fields.Many2one('finger.employee.employee', string='System Employee Name', readonly=True)
|
|
terminal_id = fields.Many2one('finger.terminal', string='Terminal', readonly=True)
|
|
server_id = fields.Many2one('finger.biotime_api', string='server', readonly=True)
|
|
hr_comployee_id = fields.Many2one('hr.employee', string='Empolyee Name', readonly=True)
|
|
state = fields.Selection(
|
|
[('draft', _('Draft')),
|
|
('confirmed', _('Confirmed'))], default="draft", readonly=True)
|
|
|
|
|
|
class SystemTerminal(models.Model):
|
|
_name = 'finger.terminal'
|
|
|
|
name = fields.Char("Terminal name")
|
|
terminal_id = fields.Integer("Termainl id")
|
|
sn = fields.Char("SN")
|
|
area_name = fields.Char("Area name")
|
|
last_activity = fields.Char("Last activity")
|
|
ip_address = fields.Char("ip address")
|
|
alias = fields.Char("Alias")
|
|
server_id = fields.Many2one('finger.biotime_api', string='server')
|
|
|
|
|
|
class SystemEmployee(models.Model):
|
|
_name = 'finger.employee.employee'
|
|
|
|
name = fields.Char("System Employee Name")
|
|
code = fields.Char("System employee Code")
|
|
emp_system_id = fields.Integer("Employee id")
|
|
email = fields.Char("email")
|
|
department_id = fields.Many2one('finger.employee.department', string='System Deparment')
|
|
position_id = fields.Many2one('finger.employee.position', string='System Position')
|
|
hr_employee = fields.Many2one('hr.employee', string='Empolyee Name')
|
|
server_id = fields.Many2one('finger.biotime_api', string='Hr empolyee')
|
|
|
|
|
|
class SystemEmployeePosition(models.Model):
|
|
_name = 'finger.employee.position'
|
|
|
|
name = fields.Char("Name")
|
|
code = fields.Char("Code")
|
|
position_id = fields.Integer("Position id")
|
|
server_id = fields.Many2one('finger.biotime_api', string='server')
|
|
|
|
|
|
class SystemArea(models.Model):
|
|
_name = 'finger.area'
|
|
|
|
name = fields.Char("Name")
|
|
code = fields.Char("Code")
|
|
area_id = fields.Integer("Area id")
|
|
server_id = fields.Many2one('finger.biotime_api', string='server')
|
|
|
|
|
|
class SystemDepartments(models.Model):
|
|
_name = 'finger.employee.department'
|
|
|
|
name = fields.Char("Name")
|
|
code = fields.Char("Code")
|
|
department_id = fields.Integer("Department id")
|
|
server_id = fields.Many2one('finger.biotime_api', string='server')
|
|
|
|
|
|
class BiotimeAPI(models.Model):
|
|
_name = 'finger.biotime_api'
|
|
|
|
name = fields.Char()
|
|
serverUrl = fields.Char(string="IP / Domain Name")
|
|
port = fields.Char(default="80")
|
|
username = fields.Char()
|
|
password = fields.Char()
|
|
token = fields.Char()
|
|
description = fields.Text()
|
|
loaded_employees = fields.Boolean(default=False)
|
|
|
|
terminals_ids = fields.One2many(
|
|
string='terminals',
|
|
comodel_name='finger.terminal',
|
|
inverse_name='server_id',
|
|
)
|
|
|
|
attendance_ids = fields.One2many(
|
|
string='Attendances',
|
|
comodel_name='finger.system_attendance',
|
|
inverse_name='server_id',
|
|
)
|
|
employee_ids = fields.One2many(
|
|
string='Employees',
|
|
comodel_name='finger.employee.employee',
|
|
inverse_name='server_id',
|
|
)
|
|
|
|
state = fields.Selection(
|
|
[('authentic', _('Authentic')),
|
|
('unauthentic', _('Not authentic'))], default="unauthentic")
|
|
start_sync_date = fields.Datetime(string="Start Sync Date", default=lambda self: fields.Datetime.now())
|
|
|
|
@api.constrains('start_sync_date')
|
|
def _check_start_sync_date(self):
|
|
for record in self:
|
|
if record.start_sync_date and record.start_sync_date > fields.Datetime.now():
|
|
raise ValidationError(_("Start Sync Date cannot be in the future. Please select now or a past date."))
|
|
@api.model
|
|
def _calc_urls(self):
|
|
serverIP = self.serverUrl + ":" + self.port
|
|
loginUrl = serverIP + "/jwt-api-token-auth/"
|
|
refreshUrl = serverIP + "/jwt-api-token-refresh/"
|
|
employeeUrl = serverIP + "/personnel/api/employee/"
|
|
departmentsUrl = serverIP + "/personnel/api/departments/"
|
|
terminalsUrl = serverIP + "/iclock/api/terminals/"
|
|
areasUrl = serverIP + "/personnel/api/areas/"
|
|
positionsUrl = serverIP + "/personnel/api/positions/"
|
|
transctionsUrl = serverIP + "/iclock/api/transactions/"
|
|
return loginUrl, refreshUrl, employeeUrl, departmentsUrl, positionsUrl, is_valid_ip, areasUrl, terminalsUrl, ParsedRequest, transctionsUrl
|
|
|
|
@api.depends('token')
|
|
def depends_token(self):
|
|
for srv in self:
|
|
if srv.token:
|
|
srv.state = 'authentic'
|
|
|
|
@api.onchange('token')
|
|
def onchange_token(self):
|
|
for srv in self:
|
|
if srv.token:
|
|
srv.state = 'authentic'
|
|
|
|
@api.constrains('serverUrl')
|
|
def check_is_valid_ip(self):
|
|
for srv in self:
|
|
if not is_valid_ip(srv.serverUrl):
|
|
raise ValidationError(_("Invalid server url"))
|
|
|
|
@api.constrains('port')
|
|
def check_is_valid_ip(self):
|
|
for srv in self:
|
|
if not is_valid_port(srv.port):
|
|
raise ValidationError(_("Invalid port number"))
|
|
|
|
def process_attendance_scheduler(self):
|
|
for tx in self.attendance_ids:
|
|
print(tx)
|
|
|
|
def login(self):
|
|
loginUrl, _, _, _, _, _, _, _, _, _ = self._calc_urls()
|
|
res = httpHelper.login(self.username, self.password, loginUrl)
|
|
|
|
if res.status_code == 200:
|
|
data = res.json()
|
|
token = data.get('token', False)
|
|
if token:
|
|
self.token = token
|
|
self.state = 'authentic'
|
|
else:
|
|
data = res.json()
|
|
err = ""
|
|
for key in data:
|
|
err += ' '.join(data[key])
|
|
raise ValidationError(err)
|
|
|
|
def refresh(self):
|
|
_, refreshUrl, _, _, _, _, _, _, _, _ = self._calc_urls()
|
|
res = httpHelper.refresh(self.token, refreshUrl)
|
|
if res.status_code == 200:
|
|
data = res.json()
|
|
token = data.get('token', False)
|
|
if token:
|
|
self.token = token
|
|
else:
|
|
data = res.json()
|
|
err = ""
|
|
for key in data:
|
|
err += ' '.join(data[key])
|
|
raise ValidationError(err)
|
|
|
|
def logout(self):
|
|
self.token = False
|
|
self.state = 'unauthentic'
|
|
|
|
def to_attendace(self):
|
|
Attend = self.env['finger.system_attendance']
|
|
attendance = self.env['attendance.attendance']
|
|
HR = self.env['hr.employee']
|
|
attens = Attend.search([('server_id', '=', self.id), ('state', '=', 'draft')])
|
|
for tx in attens:
|
|
if tx.emp_code and tx.emp_code.hr_employee:
|
|
attendance.create({
|
|
'employee_id': tx.emp_code.hr_employee.id,
|
|
'name': tx.punch_time,
|
|
'action': 'sign_in' if tx.punch_state in ["0", "2", "4"] else 'sign_out',
|
|
'action_date': tx.punch_time,
|
|
})
|
|
tx.write({
|
|
'state': 'confirmed'
|
|
})
|
|
|
|
def sync_employees(self):
|
|
if not self.token:
|
|
self.refresh()
|
|
_, _, employeeUrl, _, _, _, _, _, _, _ = self._calc_urls()
|
|
res = httpHelper.fetch_employees({}, self.token, employeeUrl)
|
|
|
|
if res.status_code == 200:
|
|
da = ParsedRequest(res.content)
|
|
self.create_employee(da.data)
|
|
next = 2
|
|
while da.next:
|
|
r = httpHelper.fetch_employees(
|
|
{}, self.token, employeeUrl + "?page=" + str(next))
|
|
if r.status_code == 200:
|
|
da = ParsedRequest(r.content)
|
|
self.create_employee(da.data)
|
|
else:
|
|
da.next = None
|
|
next = next + 1
|
|
self.loaded_employees = True
|
|
else:
|
|
self.errorHandler(res)
|
|
|
|
def errorHandler(self, res):
|
|
if res.status_code == 401:
|
|
self.logout()
|
|
else:
|
|
data = res.json()
|
|
err = ""
|
|
for key in data:
|
|
err += ' '.join(data[key])
|
|
raise ValidationError(err)
|
|
|
|
def sync_terminals(self):
|
|
if not self.token:
|
|
self.refresh()
|
|
_, _, _, _, _, _, _, terminalsUrl, _, _ = self._calc_urls()
|
|
res = httpHelper.fetch_terminals({}, self.token, terminalsUrl)
|
|
|
|
if res.status_code == 200:
|
|
da = ParsedRequest(res.content)
|
|
self.create_termainl(da.data)
|
|
next = 2
|
|
while da.next:
|
|
r = httpHelper.fetch_terminals(
|
|
{}, self.token, terminalsUrl + "?page=" + str(next))
|
|
if r.status_code == 200:
|
|
da = ParsedRequest(r.content)
|
|
self.create_termainl(da.data)
|
|
else:
|
|
da.next = None
|
|
next = next + 1
|
|
else:
|
|
self.errorHandler(res)
|
|
|
|
def sync_departments(self):
|
|
if not self.token:
|
|
self.refresh()
|
|
_, _, _, departmentsUrl, _, _, _, _, _, _ = self._calc_urls()
|
|
res = httpHelper.fetch_departments({}, self.token, departmentsUrl)
|
|
|
|
if res.status_code == 200:
|
|
da = ParsedRequest(res.content)
|
|
self.create_department(da.data)
|
|
next = 2
|
|
while da.next:
|
|
r = httpHelper.fetch_departments(
|
|
{}, self.token, departmentsUrl + "?page=" + str(next))
|
|
if r.status_code == 200:
|
|
da = ParsedRequest(r.content)
|
|
self.create_department(da.data)
|
|
else:
|
|
da.next = None
|
|
next = next + 1
|
|
else:
|
|
self.errorHandler(res)
|
|
|
|
def sync_areas(self):
|
|
if not self.token:
|
|
self.refresh()
|
|
_, _, _, _, _, _, areasUrl, _, _, _ = self._calc_urls()
|
|
res = httpHelper.fetch_areas({}, self.token, areasUrl)
|
|
try:
|
|
if res.status_code == 200:
|
|
da = ParsedRequest(res.content)
|
|
self.create_area(da.data)
|
|
next = 2
|
|
while da.next:
|
|
res = httpHelper.fetch_areas(
|
|
{}, self.token, areasUrl + "?page=" + str(next))
|
|
if res.status_code == 200:
|
|
da = ParsedRequest(res.content)
|
|
self.create_area(da.data)
|
|
else:
|
|
da.next = None
|
|
next = next + 1
|
|
else:
|
|
self.errorHandler(res)
|
|
except Exception as e:
|
|
ValidationError(str(e))
|
|
|
|
def sync_positions(self):
|
|
if not self.token:
|
|
self.refresh()
|
|
_, _, _, _, positionsUrl, _, _, _, _, _ = self._calc_urls()
|
|
res = httpHelper.fetch_positions({}, self.token, positionsUrl)
|
|
|
|
try:
|
|
if res.status_code == 200:
|
|
da = ParsedRequest(res.content)
|
|
self.create_position(da.data)
|
|
next = 2
|
|
while da.next:
|
|
res = httpHelper.fetch_positions(
|
|
{}, self.token, positionsUrl + "?page=" + str(next))
|
|
if res.status_code == 200:
|
|
da = ParsedRequest(res.content)
|
|
self.create_position(da.data)
|
|
else:
|
|
da.next = None
|
|
next = next + 1
|
|
else:
|
|
self.errorHandler(res)
|
|
except Exception as e:
|
|
ValidationError(str(e))
|
|
|
|
def sync_employee_attenence(self, url):
|
|
if not self.token:
|
|
self.refresh()
|
|
res = httpHelper.fetch_empl_transctions({}, self.token, url)
|
|
|
|
try:
|
|
if res.status_code == 200:
|
|
da = ParsedRequest(res.content)
|
|
next = 2
|
|
self.create_attendance(da.data)
|
|
while da.next:
|
|
res = httpHelper.fetch_empl_transctions({}, self.token, da.next)
|
|
if res.status_code == 200:
|
|
da = ParsedRequest(res.content)
|
|
self.create_attendance(da.data)
|
|
else:
|
|
da.next = None
|
|
next = next + 1
|
|
else:
|
|
self.errorHandler(res)
|
|
if res.status_code == 401:
|
|
self.refresh()
|
|
if self.token:
|
|
self.sync_employee_attenence(url)
|
|
except Exception as e:
|
|
ValidationError(str(e))
|
|
|
|
def sync_attenence(self):
|
|
self.login()
|
|
if not self.token:
|
|
self.refresh()
|
|
_, _, _, _, _, _, _, _, _, transctionsUrl = self._calc_urls()
|
|
|
|
start_date = self.start_sync_date.strftime(
|
|
'%Y-%m-%d %H:%M:%S') if self.start_sync_date else "2025-10-01 00:00:00"
|
|
|
|
url_with_filter = transctionsUrl + "?start_time=" + start_date
|
|
|
|
res = httpHelper.fetch_empl_transctions({}, self.token, url_with_filter)
|
|
if not self.loaded_employees:
|
|
self.sync_employees()
|
|
|
|
try:
|
|
if res.status_code == 200:
|
|
da = ParsedRequest(res.content)
|
|
next = 2
|
|
self.create_attendance(da.data)
|
|
while da.next:
|
|
res = httpHelper.fetch_empl_transctions(
|
|
{}, self.token, transctionsUrl + "?start_time=" + start_date + "&page=" + str(next))
|
|
if res.status_code == 200:
|
|
da = ParsedRequest(res.content)
|
|
self.create_attendance(da.data)
|
|
else:
|
|
da.next = None
|
|
next = next + 1
|
|
else:
|
|
self.errorHandler(res)
|
|
except Exception as e:
|
|
ValidationError(str(e))
|
|
|
|
# def sync_attenence(self):
|
|
# self.login()
|
|
# if not self.token:
|
|
# self.refresh()
|
|
# _, _, _, _, _, _, _, _, _, transctionsUrl = self._calc_urls()
|
|
# res = httpHelper.fetch_empl_transctions({}, self.token, transctionsUrl)
|
|
# if not self.loaded_employees:
|
|
# self.sync_employees()
|
|
#
|
|
# try:
|
|
# if res.status_code == 200:
|
|
# da = ParsedRequest(res.content)
|
|
# next = 2
|
|
# self.create_attendance(da.data)
|
|
# while da.next:
|
|
# res = httpHelper.fetch_empl_transctions(
|
|
# {}, self.token, transctionsUrl + "?page=" + str(next))
|
|
# if res.status_code == 200:
|
|
# da = ParsedRequest(res.content)
|
|
# self.create_attendance(da.data)
|
|
# else:
|
|
# da.next = None
|
|
# next = next + 1
|
|
# else:
|
|
# self.errorHandler(res)
|
|
# except Exception as e:
|
|
# ValidationError(str(e))
|
|
|
|
|
|
def create_termainl(self, termainls):
|
|
TerminalsModel = self.env['finger.terminal']
|
|
for tx in termainls:
|
|
tirm = TerminalsModel.search([('terminal_id', '=', tx['id'])])
|
|
data = {
|
|
'terminal_id': tx['id'],
|
|
'name': tx['terminal_name'],
|
|
'sn': tx['sn'],
|
|
'area_name': tx['area_name'],
|
|
'last_activity': tx['last_activity'],
|
|
'ip_address': tx['ip_address'],
|
|
'alias': tx['alias'],
|
|
'server_id': self.id
|
|
}
|
|
if not tirm:
|
|
TerminalsModel.create(data)
|
|
else:
|
|
tirm.update(data)
|
|
|
|
def create_area(self, areas):
|
|
TerminalsModel = self.env['finger.area']
|
|
for tx in areas:
|
|
tirm = TerminalsModel.search([('area_id', '=', tx['id'])])
|
|
data = {
|
|
'area_id': tx['id'],
|
|
'name': tx['area_name'],
|
|
'code': tx['area_code'],
|
|
'server_id': self.id
|
|
}
|
|
if not tirm:
|
|
TerminalsModel.create(data)
|
|
else:
|
|
tirm.update(data)
|
|
|
|
def create_department(self, departments):
|
|
TerminalsModel = self.env['finger.employee.department']
|
|
for tx in departments:
|
|
tirm = TerminalsModel.search([('department_id', '=', tx['id'])])
|
|
data = {
|
|
'department_id': tx['id'],
|
|
'name': tx['dept_name'],
|
|
'code': tx['dept_code'],
|
|
'server_id': self.id
|
|
}
|
|
if not tirm:
|
|
TerminalsModel.create(data)
|
|
else:
|
|
tirm.update(data)
|
|
|
|
def create_position(self, positions):
|
|
PostionModel = self.env['finger.employee.position']
|
|
for tx in positions:
|
|
tirm = PostionModel.search([('position_id', '=', tx['id'])])
|
|
data = {
|
|
'position_id': tx['id'],
|
|
'name': tx['position_name'],
|
|
'code': tx['position_code'],
|
|
'server_id': self.id
|
|
}
|
|
if not tirm:
|
|
PostionModel.create(data)
|
|
else:
|
|
tirm.update(data)
|
|
|
|
def create_employee(self, employees):
|
|
EmployeeModel = self.env['finger.employee.employee']
|
|
DepartmentModel = self.env['finger.employee.department']
|
|
PositionModel = self.env['finger.employee.position']
|
|
HR = self.env['hr.employee']
|
|
|
|
for tx in employees:
|
|
tirm = EmployeeModel.search([('emp_system_id', '=', tx['id'])])
|
|
dep = None
|
|
pos = None
|
|
hrEmp = None
|
|
if tx['position']:
|
|
pos = PositionModel.search([('position_id', '=', tx['position'])], limit=1)
|
|
|
|
if tx['department']:
|
|
dep = DepartmentModel.search([('department_id', '=', tx['department'])], limit=1)
|
|
|
|
if tx['emp_code']:
|
|
hrEmp = HR.search([('emp_no', '=', tx['emp_code'])])
|
|
|
|
data = {
|
|
'emp_system_id': tx['id'],
|
|
'name': tx['first_name'],
|
|
'code': tx['emp_code'],
|
|
'department_id': dep.id if dep else False,
|
|
'position_id': pos.id if pos else False,
|
|
'hr_employee': hrEmp.id if hrEmp else False,
|
|
'server_id': self.id,
|
|
}
|
|
|
|
if not tirm and hrEmp:
|
|
EmployeeModel.create(data)
|
|
elif hrEmp and tirm:
|
|
tirm.update(data)
|
|
|
|
def create_attendance(self, attendaces):
|
|
AttendanceModel = self.env['finger.system_attendance']
|
|
TerminalModel = self.env['finger.terminal']
|
|
EmployeeModel = self.env['finger.employee.employee']
|
|
for tx in attendaces:
|
|
tirm = AttendanceModel.search([('attendance_id', '=', tx['id'])])
|
|
empe = None
|
|
pos = None
|
|
# "2020-08-09 10:23:20"
|
|
new_punch_time = datetime.strptime(tx['punch_time'], "%Y-%m-%d %H:%M:%S") + timedelta(hours=-3)
|
|
new_upload_time = datetime.strptime(tx['upload_time'], "%Y-%m-%d %H:%M:%S") + timedelta(hours=-3)
|
|
# datetime_format = "%Y-%m-%d %H:%M:%S"
|
|
|
|
if tx['emp_code']:
|
|
empe = EmployeeModel.search([('code', '=', tx['emp_code'])], limit=1)
|
|
|
|
if tx['terminal_alias']:
|
|
dep = TerminalModel.search([('sn', '=', tx['terminal_sn'])], limit=1)
|
|
data = {
|
|
'attendance_id': tx['id'],
|
|
'punch_state': tx['punch_state'],
|
|
'punch_state_display': tx['punch_state_display'],
|
|
'area_alias': tx['area_alias'],
|
|
# 'crc': tx['crc'],
|
|
'crc': tx['emp'],
|
|
'verify_type': tx['verify_type'],
|
|
# 'is_attendance': tx['is_attendance'],
|
|
'upload_time': new_upload_time,
|
|
'punch_time': new_punch_time,
|
|
'emp_code': empe.id if empe else None,
|
|
'terminal_id': dep.id if dep else None,
|
|
'server_id': self.id,
|
|
'hr_comployee_id': empe.hr_employee.id if empe and empe.hr_employee else None,
|
|
'emp': empe.code if empe else None
|
|
}
|
|
|
|
if not tirm and empe:
|
|
# print('9999999999999999999999999999999999999999999999999',tirm)
|
|
AttendanceModel.create(data)
|
|
elif empe and tirm:
|
|
tirm.update(data)
|
|
|
|
def action_attendance_download(self):
|
|
now = datetime.now()
|
|
yesterday = datetime.now() - timedelta(hours=48)
|
|
now = now.strftime("%Y-%m-%d %H:%M:%S")
|
|
yesterday = yesterday.strftime("%Y-%m-%d %H:%M:%S")
|
|
_, _, _, _, _, _, _, _, _, transctionsUrl = self._calc_urls()
|
|
for r in self:
|
|
for xm in r.employee_ids:
|
|
url = "{}?start_time={}&end_time={}&emp_code={}".format(transctionsUrl,yesterday,now, xm.code )
|
|
# url = "{}?punch_time={}&punch_time={}&emp_code={}".format(transctionsUrl, yesterday, now, xm.code)
|
|
# print('################################# New Employee',url)
|
|
r.sync_employee_attenence(url)
|