설명 없음
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

xls2table_bak.py 59KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400
  1. """
  2. card_data
  3. CREATE TABLE htp_hr_cdata
  4. (
  5. cdata_date date NOT NULL,
  6. cdata_time character varying NOT NULL,
  7. cdata_time_value double precision NOT NULL,
  8. cdata_emp_no character varying NOT NULL,
  9. cdata_emp_name character varying,
  10. cdata_dept character varying,
  11. cdata_sysdate timestamp without time zone NOT NULL,
  12. PRIMARY KEY (cdata_date, cdata_time_value, cdata_emp_no)
  13. );
  14. working_times
  15. CREATE TABLE htp_hr_working_times
  16. (
  17. work_id int NOT NULL,
  18. work_beg_time timestamp without time zone NOT NULL,
  19. work_end_time timestamp without time zone NOT NULL,
  20. PRIMARY KEY (work_id, work_beg_time, work_end_time)
  21. );
  22. cshift
  23. CREATE TABLE htp_hr_cshift
  24. (
  25. cshift_emp_no character varying NOT NULL,
  26. cshift_dept character varying,
  27. cshift_no character varying NOT NULL,
  28. cshift_beg_date date NOT NULL,
  29. cshift_beg_time character varying NOT NULL,
  30. cshift_beg_time_value double precision NOT NULL,
  31. cshift_end_date date,
  32. cshift_end_time character varying,
  33. cshift_end_time_value double precision,
  34. cshift_leave_hours double precision,
  35. cshift_late character varying,
  36. cshift_late_value double precision,
  37. cshift_leave character varying,
  38. cshift_leave_value double precision,
  39. cshift_over character varying,
  40. cshift_over_value double precision,
  41. cshift_over_amt double precision DEFAULT 0,
  42. cshift_over_flag character varying DEFAULT '',
  43. cshift_over2 character varying, #加班2
  44. cshift_over2_value double precision,
  45. cshift_over2_amt double precision DEFAULT 0,
  46. cshift_over2_flag character varying DEFAULT '',
  47. cshift_over3 character varying, #加班3
  48. cshift_over3_value double precision,
  49. cshift_over3_amt double precision DEFAULT 0,
  50. cshift_over3_flag character varying DEFAULT '',
  51. cshift_over_beg_time character varying,#早加班開始時間
  52. cshift_over_end_time character varying,#晚加班結束時間
  53. cshift_total character varying,
  54. cshift_total_value double precision,
  55. cshift_resource_calendar_id int DEFAULT 0,
  56. cshift_sysdate timestamp without time zone NOT NULL,
  57. cshift_status character varying NOT NULL DEFAULT 'NA',
  58. PRIMARY KEY (cshift_emp_no, cshift_no, cshift_beg_date, cshift_beg_time_value)
  59. );
  60. ALTER TABLE htp_hr_cshift
  61. ADD COLUMN cshift_over2 character varying, #加班2
  62. ADD COLUMN cshift_over2_value double precision,
  63. ADD COLUMN cshift_over2_amt double precision DEFAULT 0,
  64. ADD COLUMN cshift_over2_flag character varying DEFAULT '';
  65. ALTER TABLE htp_hr_cshift
  66. ADD COLUMN cshift_over3 character varying, #加班3
  67. ADD COLUMN cshift_over3_value double precision,
  68. ADD COLUMN cshift_over3_amt double precision DEFAULT 0,
  69. ADD COLUMN cshift_over3_flag character varying DEFAULT '';
  70. ALTER TABLE htp_hr_cshift
  71. ADD COLUMN cshift_over_beg_time character varying DEFAULT '', #早加班開始時間
  72. ADD COLUMN cshift_over_end_time character varying DEFAULT ''; #晚加班結束時間
  73. ALTER TABLE htp_hr_cshift
  74. ADD COLUMN cshift_leave_hours double precision DEFAULT 0;
  75. odoo 取工作時段
  76. hr_employee.resource_calendar_id
  77. resource_calendar.id
  78. resource_calendar_attendance.calendar_id
  79. resource_calendar_leaves.calendar_id
  80. """
  81. # By XZ
  82. import xlrd
  83. import xlwt
  84. import logging
  85. import dataset
  86. from sqlalchemy.sql import text
  87. import datetime
  88. import calendar
  89. import xmlrpc.client
  90. from datetime import tzinfo, timedelta
  91. import pytz
  92. import math
  93. import os
  94. import sys
  95. import glob
  96. from dateutil.relativedelta import relativedelta
  97. from shutil import copyfile
  98. # ==========================================================================
  99. #global odoo_url, odoo_db
  100. #odoo_url = 'http://0.0.0.0:8069'
  101. #odoo_url = 'http://192.168.80.11:8069'
  102. odoo_url = 'http://192.168.80.3:8069'
  103. #odoo_db = 'htp_hr'
  104. odoo_db = 'htp'
  105. # odoo_db = 'htp'
  106. odoo_username = 'admin'
  107. odoo_password = 'Htqweasd'
  108. datetime_fmt = '%Y-%m-%d %H:%M:%S'
  109. allow_none = False
  110. cust_end_time_days = ['2022-01-28', '2022-02-25']
  111. def common_version():
  112. # provides meta-calls which don’t require authentication
  113. common = xmlrpc.client.ServerProxy('{}/xmlrpc/2/common'.format(odoo_url), allow_none=allow_none)
  114. # common.version()
  115. # print(common.version())
  116. return common
  117. def get_uid():
  118. # Logging in
  119. common = common_version()
  120. uid = common.authenticate(odoo_db, odoo_username, odoo_password, {})
  121. # print('uid:', uid)
  122. return uid
  123. def endpoint_object():
  124. # is used to call methods of odoo models via the execute_kw RPC function.
  125. return xmlrpc.client.ServerProxy('{}/xmlrpc/2/object'.format(odoo_url), allow_none=allow_none)
  126. # ==========================================================================
  127. prefix = 'htp_hr'
  128. # def float_to_datetime(fl):
  129. # return datetime.datetime.fromtimestamp(fl)
  130. def convert_excel_time(t, hour24=False):
  131. if t > 1:
  132. t = t % 1
  133. seconds = round(t*86400)
  134. minutes, seconds = divmod(seconds, 60)
  135. hours, minutes = divmod(minutes, 60)
  136. if hour24:
  137. if hours > 12:
  138. hours -= 12
  139. return "%02d:%02d:%02d PM" % (hours, minutes, seconds)
  140. else:
  141. return "%02d:%02d:%02d AM" % (hours, minutes, seconds)
  142. return "%02d:%02d:%02d" % (hours, minutes, seconds)
  143. def datetime2UTC(dt, tz='Asia/Taipei', fmt='%Y-%m-%d %H:%M:%S'):
  144. ret = dt
  145. if type(ret) == str:
  146. ret = datetime.datetime.strptime(ret, fmt)
  147. old_tz = pytz.timezone(tz)
  148. new_tz = pytz.timezone('UTC')
  149. ret = old_tz.localize(ret).astimezone(new_tz)
  150. return ret
  151. def date_diff2hours(dt, dt2):
  152. _dt = dt
  153. _dt2 = dt2
  154. if type(_dt) == str:
  155. _dt = datetime.datetime.strptime(_dt, '%Y-%m-%d %H:%M:%S')
  156. if type(_dt2) == str:
  157. _dt2 = datetime.datetime.strptime(_dt2, '%Y-%m-%d %H:%M:%S')
  158. return (_dt2-_dt).total_seconds() / 3600
  159. def ignore_sec():
  160. return 0.00011 # 十秒
  161. # return 0.00069 #六十秒
  162. def write_row(emplist, table, data):
  163. # print(data)
  164. vcdata_date = data['vcdata_date']
  165. vcdata_time_value = data['vcdata_time_value']
  166. vcdata_time = data['vcdata_time']
  167. vemp_no = data['vemp_no']
  168. vcdata_emp_name = data['vcdata_emp_name']
  169. vcdata_dept = data['vcdata_dept']
  170. if vemp_no in emplist:
  171. if emplist[vemp_no]+ignore_sec() >= vcdata_time_value: # 跟上筆差十秒內不存入
  172. return
  173. emplist[vemp_no] = vcdata_time_value
  174. if table.find_one(cdata_date=vcdata_date, cdata_time_value=vcdata_time_value, cdata_emp_no=vemp_no):
  175. table.delete(cdata_date=vcdata_date,
  176. cdata_time_value=vcdata_time_value, cdata_emp_no=vemp_no)
  177. table.insert(dict(cdata_date=vcdata_date,
  178. cdata_time=vcdata_time,
  179. cdata_time_value=vcdata_time_value,
  180. cdata_emp_no=vemp_no,
  181. cdata_emp_name=vcdata_emp_name,
  182. cdata_dept=vcdata_dept,
  183. cdata_sysdate=datetime.datetime.now()))
  184. def run_hr_cdata(db, filename):
  185. # 日期時間小到大
  186. #logger = logging.getLogger('cdata')
  187. _filename = filename
  188. wb = xlrd.open_workbook(_filename)
  189. sheet = wb.sheet_by_index(0)
  190. _db = db
  191. table = _db[prefix+'_cdata']
  192. # sheet.cell_value(0, 0)
  193. # init
  194. emplist = {}
  195. data = {}
  196. _db.begin()
  197. # cdata = []
  198. # for i in range(1, sheet.nrows):
  199. # row = sheet.row_values(i)
  200. # cdata.insert(0, row)
  201. cdata = [sheet.row_values(i) for i in range(1, sheet.nrows)]
  202. cdata.sort()
  203. try:
  204. tagDate = ''
  205. for row in cdata:
  206. # for i in range(1, sheet.nrows):
  207. # row = sheet.row_values(i)
  208. logging.info(row)
  209. #logging.debug(row)
  210. vcdata_date = datetime.datetime(
  211. *xlrd.xldate_as_tuple(row[1], wb.datemode)) # 日期
  212. if tagDate != vcdata_date.strftime('%Y-%m-%d'):
  213. tagDate = vcdata_date.strftime('%Y-%m-%d')
  214. emplist.clear()
  215. #print(tagDate)
  216. vcdata_time_value = row[2] # 時間
  217. vcdata_time = convert_excel_time(vcdata_time_value)
  218. vemp_no = row[3]
  219. data['vcdata_date'] = vcdata_date
  220. data['vcdata_time_value'] = vcdata_time_value
  221. data['vcdata_time'] = vcdata_time
  222. data['vemp_no'] = vemp_no
  223. data['vcdata_emp_name'] = row[4]
  224. data['vcdata_dept'] = row[5]
  225. write_row(emplist, table, data)
  226. # print(i)
  227. _db.commit()
  228. except:
  229. _db.rollback()
  230. def getShitft():
  231. ret = {}
  232. lst1 = []
  233. #時間1不要用, 轉換問題, 0.9999取代
  234. sf1 = {'name': '0800-1700', 'begin': 0,
  235. 'btime': 0.33333, 'etime': 0.70833, 'end': 0.9999, 'rest_hours': 1, 'rest_btime': 0.50000, 'rest_etime': 0.54167} # rest 休息時數
  236. lst1.append(sf1)
  237. ret[1] = lst1 # Standard 40 hours/week
  238. # sf1 = {'name': '0800-1200', 'begin': 0, 'btime': 0.33333, 'etime': 0.50000,
  239. # 'end': 0.52167,'rest_hours': 0}
  240. # lst1.append(sf1)
  241. # sf2 = {'name': '1300-1700', 'begin': 0.52168, 'btime': 0.54167, 'etime': 0.70833,
  242. # 'end': 0.74000, 'rest_hours': 0}
  243. # lst1.append(sf2)
  244. # sf3 = {'name': '1800-2100', 'begin': 0.74001,
  245. # 'btime': 0.75000, 'etime': 0.87500, 'end': 1,'rest_hours': 0}
  246. # lst1.append(sf3)
  247. return ret
  248. def getShitftTable(sfList):
  249. ret = {}
  250. for item in sfList:
  251. ret[item['name']] = item
  252. return ret
  253. def time2dict(timelist, sfTable):
  254. ret = {}
  255. for i in range(len(timelist)):
  256. time = timelist[i]
  257. for sf in sfTable:
  258. if sf['name'] not in ret:
  259. ret[sf['name']] = []
  260. if sf['begin'] <= time and sf['end'] >= time:
  261. if len(ret[sf['name']]) <= 1:
  262. ret[sf['name']].append(time)
  263. else:
  264. ret[sf['name']][1] = time
  265. break
  266. return ret
  267. def loadXlsEmp():
  268. gXlsEmp = {}
  269. _path = '/home/xz/xzwork/htp/xls2table/'
  270. _filename = _path+'emplist.xlsx'
  271. wb = xlrd.open_workbook(_filename)
  272. sheet = wb.sheet_by_index(0)
  273. for i in range(1, sheet.nrows):
  274. row = sheet.row_values(i)
  275. empno = row[1] # 工號
  276. data = {}
  277. data['dept'] = row[3] # 部門
  278. data['resource_calendar_id'] = 1 # 工作表
  279. gXlsEmp[empno] = data
  280. return gXlsEmp
  281. def loadOdooEmp(db):
  282. gXlsEmp = {}
  283. # t_hr_employee = db['hr_employee']
  284. # data_rows = t_hr_employee.all() #全取, 暫無條件actve
  285. flds = 'wage, food_subsidy, allowance1, allowance2, allowance3, allowance4 '
  286. _sql = 'select emp.*, dept.name as dept_name from hr_employee as emp '
  287. _sql += 'left join hr_department as dept on (dept.id = emp.department_id) '
  288. _sql += 'where emp.emp_no is not null and coalesce(emp.no_attendance, false) = false and emp.active = true '
  289. _sql += 'order by emp.emp_no'
  290. data_rows = db.query(_sql)
  291. for row in data_rows:
  292. empno = row['emp_no'] # 工號
  293. #_sql = "SELECT wage FROM hr_contract where active = True and employee_id = (select id from hr_employee where emp_no = '%s') LIMIT 1" % (empno)
  294. _sql = "SELECT %s FROM hr_contract where active = True and employee_id = (select id from hr_employee where emp_no = '%s') LIMIT 1" % (flds,empno)
  295. _wage = 0
  296. query_data = db.query(_sql)
  297. for data in query_data:
  298. _wage = float(data['wage'])+float(data['food_subsidy'])+float(data['allowance1'])+float(data['allowance2'])+float(data['allowance3'])+float(data['allowance4'])
  299. data = {}
  300. data['dept'] = row['dept_name'] # 部門
  301. data['resource_calendar_id'] = row['resource_calendar_id'] # 工作表
  302. data['wage'] = _wage
  303. gXlsEmp[empno] = data
  304. return gXlsEmp
  305. def getEmployee(emplist, run_emp_no=None):
  306. ret = emplist
  307. # ret['202009001'] = '資訊部'
  308. if run_emp_no is not None:
  309. ret = {}
  310. ret[run_emp_no] = emplist[run_emp_no]
  311. return ret
  312. def getWorkDays(db, res_cal_id, sdatetime): # 檢核是否上班日
  313. ret = [False, 0]
  314. dt = datetime2UTC(sdatetime)
  315. wday = dt.weekday()
  316. _sql = 'select * from htp_hr_working_times '
  317. _sql += 'where work_id = %d and work_beg_time <= \'%s\' and work_end_time >= \'%s\' ' % (
  318. res_cal_id, sdatetime, sdatetime)
  319. data_rows = db.query(_sql)
  320. for row in data_rows:
  321. ret[0] = True
  322. ret[1] = wday
  323. break
  324. return ret
  325. def getHoliday(db, res_cal_id, sdatetime): # 特殊放假
  326. ret = [False, '']
  327. # ret.append('2021-02-10')
  328. _sql = 'select rcl.* from resource_calendar_leaves as rcl '
  329. _sql += 'where rcl.resource_id is null and rcl.holiday_id is null and rcl.calendar_id = %d and rcl.date_from <= \'%s\' and rcl.date_to >= \'%s\' ' % (
  330. res_cal_id, sdatetime, sdatetime)
  331. data_rows = db.query(_sql)
  332. for row in data_rows:
  333. ret[0] = True
  334. ret[1] = row['name']
  335. if row['name'] == '天災':
  336. ret = False
  337. break
  338. return ret
  339. def check_work_date(run_date): # 檢核是否上班日
  340. wday = run_date.weekday()
  341. sday = run_date.strftime("%Y-%m-%d")
  342. if wday <= 4: # 星期1到星期5上班
  343. ret = True
  344. # if sday in getWorkDays():
  345. # ret = True
  346. # if sday in getHoliday():
  347. # ret = False
  348. return ret
  349. def initEmployee(db, run_date, run_emp_no=None):
  350. ret = {}
  351. _db = db
  352. tcshift = _db[prefix+'_cshift']
  353. t_hr_employee = _db['hr_employee']
  354. sday = run_date.strftime("%Y-%m-%d")
  355. uid = get_uid()
  356. models = endpoint_object()
  357. _db.begin()
  358. try:
  359. emplist = getEmployee(_db.vars['emplist'], run_emp_no=run_emp_no)
  360. sfList = getShitft()
  361. if len(sfList) <= 0:
  362. return ret
  363. for emp_no in emplist:
  364. _sql = "SELECT date_end FROM hr_contract where active = True and employee_id = (select id from hr_employee where emp_no = '%s') LIMIT 1" % (emp_no)
  365. query_data = db.query(_sql)
  366. exit_emp = False
  367. for data in query_data:
  368. if data['date_end']: #離職人員處理
  369. if data['date_end'].strftime("%Y-%m-%d") <= sday:
  370. exit_emp = True
  371. if exit_emp:
  372. continue
  373. sfTable = getShitftTable(sfList[emplist[emp_no]['resource_calendar_id']])
  374. # 有上班init Emp
  375. if getWorkDays(db, emplist[emp_no]['resource_calendar_id'], sday+' 11:00:00')[0]:
  376. for cshift_no in sfTable:
  377. cshift_beg_time = convert_excel_time(0)
  378. cshift_end_time = convert_excel_time(0)
  379. cshift_beg_time_value = 0
  380. cshift_end_time_value = 0
  381. cshift_late_value = 0
  382. cshift_late = convert_excel_time(cshift_late_value)
  383. cshift_over_value = 0
  384. cshift_over = convert_excel_time(cshift_over_value)
  385. cshift_total_value = 0
  386. cshift_total = convert_excel_time(cshift_total_value)
  387. # cshift_leave_value = sfTable[cshift_no]['etime'] - \
  388. # sfTable[cshift_no]['btime']
  389. cshift_leave_value = 0
  390. cshift_leave = convert_excel_time(cshift_leave_value)
  391. row = dict(cshift_emp_no=emp_no,
  392. cshift_dept=emplist[emp_no]['dept'], cshift_no=cshift_no,
  393. cshift_beg_date=run_date, cshift_beg_time=cshift_beg_time, cshift_beg_time_value=cshift_beg_time_value,
  394. cshift_end_date=run_date, cshift_end_time=cshift_end_time, cshift_end_time_value=cshift_end_time_value,
  395. cshift_late=cshift_late, cshift_late_value=cshift_late_value,
  396. cshift_leave=cshift_leave, cshift_leave_value=cshift_leave_value,
  397. cshift_over=cshift_over, cshift_over_value=cshift_over_value, cshift_over_amt=0, cshift_over_flag='',
  398. cshift_total=cshift_total, cshift_total_value=cshift_total_value,
  399. cshift_sysdate=datetime.datetime.now())
  400. #判斷請假單
  401. _btime_str = sday+' '+convert_excel_time(sfTable[cshift_no]['btime'])
  402. _etime_str = sday+' '+convert_excel_time(sfTable[cshift_no]['etime'])
  403. data = models.execute_kw(odoo_db, uid, odoo_password, 'hr.payslip', 'check_leaves', [
  404. '', emp_no, _btime_str, _etime_str])
  405. if data[0]: # 找到假單
  406. hours = date_diff2hours(_btime_str, _etime_str)
  407. ret[emp_no] = data[2]
  408. if sfTable[cshift_no]['rest_hours'] > 0:
  409. hours -= sfTable[cshift_no]['rest_hours']
  410. if data[2] >= hours:
  411. continue
  412. tcshift.insert(row)
  413. _db.commit()
  414. return ret
  415. except Exception as e:
  416. logging.error(str(e))
  417. #print(str(e))
  418. _db.rollback()
  419. return {}
  420. #加總遲到
  421. def check_odoo_late(db, emp, run_date, btime, etime, late_value): # 找遲到請假單
  422. emp_no = emp['emp_no']
  423. ret = late_value
  424. if ret <= 0:
  425. return ret
  426. uid = get_uid()
  427. models = endpoint_object()
  428. _date_str = datetime.datetime.strftime(run_date, '%Y-%m-%d')
  429. _btime_str = _date_str+' '+convert_excel_time(btime)
  430. _etime_str = _date_str+' '+convert_excel_time(etime)
  431. if uid:
  432. data = models.execute_kw(odoo_db, uid, odoo_password, 'hr.payslip', 'check_leaves', [
  433. '', emp_no, _btime_str, _etime_str])
  434. if data[0] and ret > 0: # 找到假單
  435. #sum_late_minutes = 0
  436. #sql = 'SELECT sum(cshift_late_value) as late_value FROM '+ prefix + \
  437. # '_cshift where where cshift_emp_no = %s and date_part(\'year\', cshift_beg_date) = %d and date_part(\'month\', cshift_beg_date) = %d ' % (emp_no, run_date.year, run_date.month)
  438. #rows = db.query(sql)
  439. #for row in rows:
  440. # if row > 0:
  441. # seconds = round(row*86400)
  442. # sum_late_minutes, seconds = divmod(seconds, 60)
  443. if data[2] > 0: # 小時
  444. #btime_dt = datetime.datetime.strptime(_btime_str, datetime_fmt)
  445. #leave_dt = data[3]
  446. #if leave_dt != 0:
  447. # leave_dt = datetime.datetime.strptime(data[3], datetime_fmt)
  448. # if leave_dt > btime_dt: #大於八點, 一律給30分(只能請30分)
  449. # return (0.5 / 24)
  450. asSec = data[2] / 24
  451. ret = ret - asSec # 依核准時數
  452. if ret < 0: # 負數歸零
  453. ret = 0
  454. return ret
  455. def check_odoo_leaves(db, emp, run_date, btime, etime, late_value): # 找出早退請假單
  456. emp_no = emp['emp_no']
  457. ret = late_value
  458. uid = get_uid()
  459. models = endpoint_object()
  460. _date_str = datetime.datetime.strftime(run_date, '%Y-%m-%d')
  461. _btime_str = _date_str+' '+convert_excel_time(btime)
  462. _btime_str = _btime_str[0:14]+'00:00' #取小時取整數
  463. _etime_str = _date_str+' '+convert_excel_time(etime)
  464. if uid:
  465. data = models.execute_kw(odoo_db, uid, odoo_password, 'hr.payslip', 'check_leaves', [
  466. '', emp_no, _btime_str, _etime_str])
  467. if data[0] and ret > 0: # 找到假單
  468. #if data[1] > 0.5: # 多天
  469. # ret = 0
  470. if data[2] > 0: # 小時
  471. asSec = data[2] / 24
  472. ret = ret - asSec # 依核准時數
  473. if ret < 0: # 負數歸零
  474. ret = 0
  475. return ret
  476. def check_odoo_overtime_all2(db, emp, run_date, calendar_id, btime, etime, sfTable): # 全改寫, 依刷卡及加班單
  477. _db = db
  478. emp_no = emp['emp_no']
  479. ret = [0, 0, '', ''] #0.一般加班, 1.加班2, 2.最早加班時段 3.最晚加班時段
  480. uid = get_uid()
  481. models = endpoint_object()
  482. wday = run_date.weekday()
  483. sday = run_date.strftime("%Y-%m-%d")
  484. _btime = btime #刷起
  485. if sfTable['btime'] > _btime: #早到五十四分內都不算, 依8點整
  486. if (sfTable['btime'] - _btime) <= (0.9 / 24):
  487. _btime = sfTable['btime']
  488. _etime = etime #實刷迄
  489. over_value = 0 #實刷
  490. t = _etime-_btime
  491. hours = (round(t*86400) / 60) / 60
  492. t = sfTable['etime']-sfTable['btime']
  493. #hours2 = (round(t*86400) / 60) / 60
  494. _date_str = datetime.datetime.strftime(run_date, '%Y-%m-%d')
  495. hours3 = 0 #加班
  496. hours32 = 0 #加班2
  497. if hours > 0:
  498. if wday == 5 or wday == 6 or getHoliday(_db, calendar_id, sday)[0]:#放假加班(國定假日)
  499. _btime_str = _date_str+' '+convert_excel_time(sfTable['begin'])
  500. _etime_str = _date_str+' '+convert_excel_time(sfTable['end'])
  501. data = models.execute_kw(odoo_db, uid, odoo_password, 'hr.payslip', 'check_leaves', [
  502. 'over', emp_no, _btime_str, _etime_str])
  503. t = _etime-btime #實刷
  504. hours = (round(t*86400) / 60) / 60
  505. if data[0] and data[2] > 0:
  506. hours3 = data[2]
  507. if hours < data[2]:
  508. hours3 = hours
  509. _btime_str = _date_str+' '+convert_excel_time(sfTable['begin'])
  510. _etime_str = _date_str+' '+convert_excel_time(sfTable['end'])
  511. data = models.execute_kw(odoo_db, uid, odoo_password, 'hr.payslip', 'check_leaves', [
  512. 'over2', emp_no, _btime_str, _etime_str])
  513. if data[0] and data[2] > 0:
  514. hours32 = data[2]
  515. if hours < data[2]:
  516. hours32 = hours
  517. else: #平日
  518. #早上加班
  519. t = sfTable['btime']-_btime
  520. calc_hours = (round(t*86400) / 60) / 60
  521. if calc_hours >= (0.5 / 24):
  522. _btime_str = _date_str+' '+convert_excel_time(sfTable['begin'])
  523. _etime_str = _date_str+' '+convert_excel_time(sfTable['btime'])
  524. data = models.execute_kw(odoo_db, uid, odoo_password, 'hr.payslip', 'check_leaves', [
  525. 'over', emp_no, _btime_str, _etime_str])
  526. if data[0] and data[2] > 0:
  527. hours3 = data[2]
  528. if calc_hours < data[2]:
  529. hours3 = calc_hours
  530. #晚上加班
  531. t = _etime-sfTable['etime']
  532. calc_hours = (round(t*86400) / 60) / 60
  533. if calc_hours >= (0.5 / 24):
  534. _btime_str = _date_str+' '+convert_excel_time(sfTable['etime'])
  535. _etime_str = _date_str+' '+convert_excel_time(sfTable['end'])
  536. data = models.execute_kw(odoo_db, uid, odoo_password, 'hr.payslip', 'check_leaves', [
  537. 'over', emp_no, _btime_str, _etime_str])
  538. if data[0] and data[2] > 0:
  539. if calc_hours < data[2]:
  540. hours3 += calc_hours
  541. else:
  542. hours3 += data[2]
  543. #中午加班
  544. if sfTable['rest_hours'] > 0 and 'rest_btime' in sfTable:
  545. _btime_str = _date_str+' '+convert_excel_time(sfTable['rest_btime']) #中午休息一小時
  546. _etime_str = _date_str+' '+convert_excel_time(sfTable['rest_etime'])
  547. data = models.execute_kw(odoo_db, uid, odoo_password, 'hr.payslip', 'check_leaves', [
  548. 'over', emp_no, _btime_str, _etime_str])
  549. if data[0] and data[2] > 0:
  550. hours3 += data[2]
  551. #早上加班2
  552. t = sfTable['btime']-_btime
  553. calc_hours = (round(t*86400) / 60) / 60
  554. if calc_hours >= (0.5 / 24):
  555. _btime_str = _date_str+' '+convert_excel_time(sfTable['begin'])
  556. _etime_str = _date_str+' '+convert_excel_time(sfTable['btime'])
  557. data = models.execute_kw(odoo_db, uid, odoo_password, 'hr.payslip', 'check_leaves', [
  558. 'over2', emp_no, _btime_str, _etime_str])
  559. if data[0] and data[2] > 0:
  560. hours32 = data[2]
  561. if calc_hours < data[2]:
  562. hours32 = calc_hours
  563. #晚上加班2
  564. t = _etime-sfTable['etime']
  565. calc_hours = (round(t*86400) / 60) / 60
  566. if calc_hours >= (0.5 / 24):
  567. _btime_str = _date_str+' '+convert_excel_time(sfTable['etime'])
  568. _etime_str = _date_str+' '+convert_excel_time(sfTable['end'])
  569. data = models.execute_kw(odoo_db, uid, odoo_password, 'hr.payslip', 'check_leaves', [
  570. 'over2', emp_no, _btime_str, _etime_str])
  571. if data[0] and data[2] > 0:
  572. if calc_hours < data[2]:
  573. hours32 += calc_hours
  574. else:
  575. hours32 += data[2]
  576. #中午加班2
  577. if sfTable['rest_hours'] > 0 and 'rest_btime' in sfTable:
  578. _btime_str = _date_str+' '+convert_excel_time(sfTable['rest_btime']) #中午休息一小時
  579. _etime_str = _date_str+' '+convert_excel_time(sfTable['rest_etime'])
  580. data = models.execute_kw(odoo_db, uid, odoo_password, 'hr.payslip', 'check_leaves', [
  581. 'over2', emp_no, _btime_str, _etime_str])
  582. if data[0] and data[2] > 0:
  583. hours32 += data[2]
  584. if hours3 > 0:
  585. over_value = hours3 / 24
  586. ret[0] = over_value
  587. if hours32 > 0:
  588. over_value = hours32 / 24
  589. ret[1] = over_value
  590. _btime_str = _date_str+' '+convert_excel_time(sfTable['begin'])
  591. _etime_str = _date_str+' '+convert_excel_time(sfTable['end'])
  592. if (ret[0] > 0) or (ret[1] > 0): #取最大最小時間
  593. data = models.execute_kw(odoo_db, uid, odoo_password, 'hr.payslip', 'check_leaves_over', [
  594. emp_no, _btime_str, _etime_str])
  595. if data[0] and data[2] > 0:
  596. ret[2] = data[3]
  597. ret[3] = data[4]
  598. if convert_excel_time(sfTable['btime']) < ret[2]:
  599. ret[2] = ''
  600. return ret
  601. def check_odoo_overtime_all(db, emp, run_date, calendar_id, btime, etime, sfTable): # 全改寫, 依刷卡及加班單
  602. _db = db
  603. emp_no = emp['emp_no']
  604. ret = [0, 0, '', '']
  605. uid = get_uid()
  606. models = endpoint_object()
  607. wday = run_date.weekday()
  608. sday = run_date.strftime("%Y-%m-%d")
  609. _btime = btime #實刷起
  610. if sfTable['btime'] > _btime: #早到五十四分內都不算, 依8點整
  611. if (sfTable['btime'] - _btime) <= (0.9 / 24):
  612. _btime = sfTable['btime']
  613. _etime = etime #實刷迄
  614. over_value = 0 #實刷
  615. t = _etime-_btime
  616. hours = (round(t*86400) / 60) / 60
  617. t = sfTable['etime']-sfTable['btime']
  618. hours2 = (round(t*86400) / 60) / 60
  619. _date_str = datetime.datetime.strftime(run_date, '%Y-%m-%d')
  620. if hours > 0:
  621. hours3 = hours
  622. if wday == 5 or wday == 6 or getHoliday(_db, calendar_id, sday)[0]:#放假加班(國定假日)
  623. pass
  624. else: #平日
  625. #必要扣休息時間
  626. if hours3 >= hours2:
  627. hours3 -= sfTable['rest_hours']
  628. hours3 = hours3-(hours2-sfTable['rest_hours'])
  629. if sfTable['rest_hours'] > 0 and 'rest_btime' in sfTable:
  630. _btime_str = _date_str+' '+convert_excel_time(sfTable['rest_btime']) #中午休息一小時
  631. _etime_str = _date_str+' '+convert_excel_time(sfTable['rest_etime'])
  632. data = models.execute_kw(odoo_db, uid, odoo_password, 'hr.payslip', 'check_leaves', [
  633. 'over', emp_no, _btime_str, _etime_str])
  634. if data[0] and data[2] > 0: #休息時間,有加班單不扣
  635. hours3 += data[2]
  636. if hours3 > 0:
  637. over_value = hours3 / 24
  638. #print('加班實刷: '+convert_excel_time(over_value))
  639. _btime_str2 = _date_str+' '+convert_excel_time(_btime)
  640. _etime_str2 = _date_str+' '+convert_excel_time(_etime)
  641. if uid:
  642. #找出忘刷卡假
  643. _st_etime = sfTable['end']
  644. if _st_etime == 1: #不可以24小時, 調整少十分鐘
  645. _st_etime = _st_etime - (0.1 / 24)
  646. _btime_str = _date_str+' '+convert_excel_time(sfTable['begin'])
  647. _etime_str = _date_str+' '+convert_excel_time(_st_etime)
  648. data = models.execute_kw(odoo_db, uid, odoo_password, 'hr.payslip', 'check_leaves', [
  649. 'nocheck', emp_no, _btime_str, _etime_str])
  650. if data[0]:
  651. _btime_str2 = data[2]
  652. _etime_str2 = data[3]
  653. over_value = data[1] / 24 #實刷
  654. #
  655. data = models.execute_kw(odoo_db, uid, odoo_password, 'hr.payslip', 'check_leaves', [
  656. 'over', emp_no, _btime_str2, _etime_str2])
  657. if data[0] and data[2] > 0:
  658. asSec = data[2] / 24
  659. ret[0] = asSec # 依核准時數
  660. if over_value < (0.5 / 24): # 實刷不滿30分,不計算
  661. ret[0] = 0
  662. elif asSec > over_value: # 小於核准時數, 依實刷時數計算
  663. ret[0] = over_value
  664. #加班2(特殊加班)
  665. data = models.execute_kw(odoo_db, uid, odoo_password, 'hr.payslip', 'check_leaves', [
  666. 'over2', emp_no, _btime_str2, _etime_str2])
  667. if data[0] and data[2] > 0:
  668. asSec = data[2] / 24
  669. ret[1] = asSec # 依核准時數
  670. if over_value < (0.5 / 24): # 實刷不滿30分,不計算
  671. ret[1] = 0
  672. elif asSec > over_value: # 小於核准時數, 依實刷時數計算
  673. ret[1] = over_value
  674. if (ret[0] > 0) or (ret[1] > 0): #取最大最小時間
  675. data = models.execute_kw(odoo_db, uid, odoo_password, 'hr.payslip', 'check_leaves_over', [
  676. emp_no, _btime_str, _etime_str])
  677. if data[0] and data[2] > 0:
  678. ret[2] = data[3]
  679. ret[3] = data[4]
  680. if convert_excel_time(sfTable['btime']) < ret[2]:
  681. ret[2] = ''
  682. return ret
  683. def check_odoo_overtime(db, emp, run_date, btime, etime, over_value): # 找出加班單
  684. _db = db
  685. emp_no = emp['emp_no']
  686. ret = 0
  687. uid = get_uid()
  688. models = endpoint_object()
  689. wday = run_date.weekday()
  690. _date_str = datetime.datetime.strftime(run_date, '%Y-%m-%d')
  691. _btime_str = _date_str+' '+convert_excel_time(btime)
  692. _etime_str = _date_str+' '+convert_excel_time(etime)
  693. sday = run_date.strftime("%Y-%m-%d")+' '+convert_excel_time(btime)
  694. if uid and over_value > 0:
  695. data = models.execute_kw(odoo_db, uid, odoo_password, 'hr.payslip', 'check_leaves', [
  696. 'over', emp_no, _btime_str, _etime_str])
  697. if data[0] and data[2] > 0:
  698. asSec = data[2] / 24
  699. ret = asSec # 依核准時數
  700. if over_value < (0.5 / 24): # 實刷不滿30分,不計算
  701. ret = 0
  702. elif asSec > over_value: # 小於核准時數, 依實刷時數計算
  703. ret = over_value
  704. return ret
  705. def check_odoo_overtime2(db, emp, run_date, btime, etime, over_value): # 平日找出加班單, 下班前的單據
  706. _db = db
  707. emp_no = emp['emp_no']
  708. ret = over_value
  709. uid = get_uid()
  710. models = endpoint_object()
  711. wday = run_date.weekday()
  712. _date_str = datetime.datetime.strftime(run_date, '%Y-%m-%d')
  713. _btime_str = _date_str+' '+convert_excel_time(btime)
  714. _etime_str = _date_str+' '+convert_excel_time(etime)
  715. sday = run_date.strftime("%Y-%m-%d")+' '+convert_excel_time(btime)
  716. if uid:
  717. data = models.execute_kw(odoo_db, uid, odoo_password, 'hr.payslip', 'check_leaves', [
  718. 'over', emp_no, _btime_str, _etime_str])
  719. if data[0] and data[2] > 0:
  720. asSec = data[2] / 24
  721. ret = ret+asSec # 依核准時數+下班加班時數
  722. return ret
  723. def calc_over_amt(db, emp, run_date, over_value): # 加班計算
  724. _db = db
  725. ret = [0, '']
  726. if over_value <= 0:
  727. return ret
  728. wday = run_date.weekday()
  729. sday = run_date.strftime("%Y-%m-%d")
  730. hours = (round(over_value*86400) / 60) / 60
  731. if hours > 0:
  732. # if wday <= 4: # 星期1到星期5上班(平日)
  733. if getWorkDays(_db, emp['resource_calendar_id'], sday+' 11:00:00')[0]: # 假日加班(是為平常日)
  734. wday = 0
  735. if getHoliday(_db, emp['resource_calendar_id'], sday)[0]: # 國定假日(是為例假日)
  736. wday = 5
  737. ret[0] = hours*1.34
  738. ret[1] = '平日'
  739. if hours > 2:
  740. ret[0] = 2*1.34
  741. ret[0] += (hours-2) * 1.67
  742. if wday == 5: # 星期6上班(休息日)
  743. h = hours
  744. ret[0] = h*1.34
  745. ret[1] = '休息日'
  746. if h > 2:
  747. ret[0] = 2*1.34
  748. h -= 2
  749. if h <= 6:
  750. ret[0] += h*1.67
  751. else:
  752. ret[0] += 6*1.67
  753. h -= 6
  754. if h > 0:
  755. ret[0] += h*2.67
  756. if wday == 6: # 星期日上班(例假日)U
  757. h = hours
  758. ret[0] = h
  759. ret[1] = '例假日'
  760. if h >= 1 and h <= 8: # 小於八,給8小時
  761. ret[0] = 8
  762. elif h > 8:
  763. ret[0] = 8
  764. h -= 8
  765. if getHoliday(db, emp['resource_calendar_id'], sday)[0]: # 國定假日
  766. if h > 2:
  767. ret[0] += 2*1.34
  768. ret[0] += (h-2)*1.67
  769. else:
  770. ret[0] += h*1.34
  771. else:
  772. ret[0] += h*2
  773. emplist =_db.vars['emplist']
  774. if ret[0] > 0 and emp['emp_no'] in emplist:
  775. amt = emplist[emp['emp_no']]['wage'] / 30 / 8
  776. ret[0] = math.ceil(amt*ret[0])
  777. return ret
  778. def calc_over2_amt(db, emp, run_date, over_value): # 加班2計算(特殊加班)
  779. _db = db
  780. ret = [0, '']
  781. if over_value <= 0:
  782. return ret
  783. wday = run_date.weekday()
  784. sday = run_date.strftime("%Y-%m-%d")
  785. hours = (round(over_value*86400) / 60) / 60
  786. if hours > 0:
  787. if getWorkDays(_db, emp['resource_calendar_id'], sday+' 11:00:00')[0]: # 假日加班(是為平常日)
  788. wday = 0
  789. if getHoliday(_db, emp['resource_calendar_id'], sday)[0]: # 國定假日(是為例假日)
  790. wday = 5
  791. ret[0] = hours*1.67
  792. ret[1] = '平日'
  793. if wday == 5: # 星期6上班(休息日)
  794. ret[1] = '休息日'
  795. if wday == 6: # 星期日上班(例假日)
  796. ret[0] = 0
  797. ret[1] = '例假日'
  798. emplist =_db.vars['emplist']
  799. if ret[0] > 0 and emp['emp_no'] in emplist:
  800. amt = emplist[emp['emp_no']]['wage'] / 30 / 8
  801. ret[0] = math.ceil(amt*ret[0])
  802. return ret
  803. #提前放假時間處理
  804. def custom_end_time_value(sday, emp_no, cshift_end_time_value):
  805. ret = cshift_end_time_value
  806. if sday in cust_end_time_days:
  807. ret = 0.70833 #改下午五點
  808. return ret
  809. def run_hr_cshift(db, run_date, run_emp_no=None):
  810. #logger = logging.getLogger('cshift')
  811. _db = db
  812. #tcdata = _db[prefix+'_cdata']
  813. tcshift = _db[prefix+'_cshift']
  814. t_hr_employee = _db['hr_employee']
  815. _orderby = ['cdata_emp_no', 'cdata_date', 'cdata_time_value']
  816. emplist = {}
  817. #data_rows = tcdata.find(cdata_date=run_date, order_by=_orderby)
  818. #if run_emp_no is not None:
  819. # data_rows = tcdata.find(cdata_date=run_date,
  820. # cdata_emp_no=run_emp_no, order_by=_orderby)
  821. #改成加入htp_checkin忘刷單
  822. run_date_str = run_date.strftime("%Y-%m-%d")
  823. _sql = 'SELECT cdata_emp_no, cdata_date, cdata_time_value, cdata_emp_name, cdata_dept '
  824. _sql += 'FROM htp_hr_cdata inner join hr_employee b on (cdata_emp_no = b.emp_no and coalesce(b.no_attendance, false) = false) where cdata_date = \'%s\' ' % (run_date_str)
  825. if run_emp_no is not None:
  826. _sql += ' and cdata_emp_no = \'%s\' ' % (run_emp_no)
  827. _sql += 'UNION '
  828. _sql += 'SELECT b.emp_no, a.ck_date, a.ck_time, b.name, c.name '
  829. _sql += 'FROM htp_checkin a inner join hr_employee b on (a.employee_id = b.id and coalesce(b.no_attendance, false) = false) '
  830. _sql += 'left join hr_department c on (b.department_id = c.id) '
  831. _sql += 'where a.ck_time <> 0 and a.state = \'done\' and a.ck_date = \'%s\' ' % (run_date_str)
  832. if run_emp_no is not None:
  833. _sql += ' and b.emp_no = \'%s\' ' % (run_emp_no)
  834. _sql += 'order by cdata_emp_no, cdata_date, cdata_time_value '
  835. data_rows = _db.query(_sql)
  836. if run_emp_no is not None:
  837. tcshift.delete(cshift_beg_date=run_date, cshift_emp_no=run_emp_no)
  838. else:
  839. tcshift.delete(cshift_beg_date=run_date)
  840. leave_hours = initEmployee(_db, run_date, run_emp_no=run_emp_no) # init Employee
  841. for row in data_rows:
  842. if row['cdata_emp_no'] in emplist:
  843. if emplist[row['cdata_emp_no']]['timelist'][-1] <= row['cdata_time_value']:
  844. emplist[row['cdata_emp_no']]['timelist'].append(
  845. row['cdata_time_value'])
  846. else:
  847. emplist[row['cdata_emp_no']] = {}
  848. emplist[row['cdata_emp_no']
  849. ]['emp_no'] = row['cdata_emp_no'] # emp_no
  850. emplist[row['cdata_emp_no']]['emp_name'] = row['cdata_emp_name']
  851. emplist[row['cdata_emp_no']]['dept'] = row['cdata_dept']
  852. emplist[row['cdata_emp_no']]['timelist'] = [
  853. row['cdata_time_value']]
  854. #emp_row = t_hr_employee.find_one(emp_no=row['cdata_emp_no'])
  855. emp_row = t_hr_employee.find_one(emp_no=row['cdata_emp_no'], no_attendance=False, active=True)
  856. if emp_row:
  857. emplist[row['cdata_emp_no']
  858. ]['resource_calendar_id'] = emp_row['resource_calendar_id']
  859. else:
  860. #print(row['cdata_emp_no']) # 找不到員工基本資料, 或已封存及不加入考勤
  861. logging.warning('找不到此員工 '+row['cdata_emp_no'])
  862. del emplist[row['cdata_emp_no']]
  863. if len(emplist) <= 0:
  864. return
  865. _db.begin()
  866. sfList = getShitft()
  867. if len(sfList) <= 0:
  868. return
  869. wday = run_date.weekday()
  870. sday = run_date.strftime("%Y-%m-%d")
  871. try:
  872. for emp_no in emplist:
  873. sfTable = getShitftTable(sfList[emplist[emp_no]['resource_calendar_id']])
  874. timelist = emplist[emp_no]['timelist']
  875. if len(timelist) <= 0:
  876. continue
  877. ppTimedict = time2dict(timelist, sfList[emplist[emp_no]['resource_calendar_id']])
  878. for cshift_no in ppTimedict:
  879. if len(ppTimedict[cshift_no]) == 1:
  880. if ppTimedict[cshift_no][0] > sfTable[cshift_no]['etime']: # 早上忘刷
  881. cshift_beg_time_value = sfTable[cshift_no]['etime']
  882. cshift_end_time_value = ppTimedict[cshift_no][0]
  883. else:
  884. cshift_beg_time_value = ppTimedict[cshift_no][0]
  885. cshift_end_time_value = sfTable[cshift_no]['btime']
  886. else: #有刷二筆
  887. cshift_beg_time_value = ppTimedict[cshift_no][0]
  888. cshift_end_time_value = ppTimedict[cshift_no][1]
  889. cshift_end_time_value = custom_end_time_value(sday, emp_no, cshift_end_time_value) #提前放假時間處理
  890. cshift_beg_time = convert_excel_time(cshift_beg_time_value)
  891. cshift_end_time = convert_excel_time(cshift_end_time_value)
  892. cshift_late_value = 0
  893. if cshift_beg_time_value > sfTable[cshift_no]['btime']:
  894. cshift_late_value = cshift_beg_time_value - \
  895. sfTable[cshift_no]['btime']
  896. if sfTable[cshift_no]['rest_hours'] > 0 and cshift_late_value > (4 / 24): # 扣掉休息時間, 大於四小時
  897. cshift_late_value = cshift_late_value - \
  898. (sfTable[cshift_no]['rest_hours'] / 24)
  899. # check odoo 請假單 遲到
  900. cshift_late_value = check_odoo_late(_db, emplist[emp_no], run_date, sfTable[cshift_no]['btime'],
  901. cshift_beg_time_value, cshift_late_value)
  902. cshift_late = convert_excel_time(cshift_late_value)
  903. #
  904. cshift_leave_value = 0
  905. if sfTable[cshift_no]['btime'] < cshift_end_time_value and cshift_end_time_value < sfTable[cshift_no]['etime']:
  906. cshift_leave_value = sfTable[cshift_no]['etime'] - cshift_end_time_value
  907. if sfTable[cshift_no]['rest_hours'] > 0 and cshift_leave_value > (4 / 24): # 扣掉休息時間, 大於四小時
  908. cshift_leave_value = cshift_leave_value - \
  909. (sfTable[cshift_no]['rest_hours'] / 24)
  910. # check odoo 請假單 早退
  911. cshift_leave_value = check_odoo_leaves(_db,
  912. emplist[emp_no], run_date, cshift_end_time_value, sfTable[cshift_no]['etime'], cshift_leave_value)
  913. cshift_leave = convert_excel_time(cshift_leave_value)
  914. #
  915. cshift_over_value = [0,0]
  916. #if cshift_end_time_value > sfTable[cshift_no]['etime']:
  917. # cshift_over_value = cshift_end_time_value - \
  918. # sfTable[cshift_no]['etime']
  919. # check odoo 請假單 加班
  920. cshift_over_value = check_odoo_overtime_all2(_db, emplist[emp_no], run_date, emplist[emp_no]['resource_calendar_id'], cshift_beg_time_value, cshift_end_time_value, sfTable[cshift_no])
  921. #特殊加班另外加欄位及check_odoo_overtime_all_167方式
  922. cshift_over_beg_time = cshift_over_value[2]
  923. cshift_over_end_time = cshift_over_value[3]
  924. # 放假加班
  925. if wday == 5 or wday == 6 or getHoliday(_db, emplist[emp_no]['resource_calendar_id'], sday)[0]:
  926. # 遲到早退歸零
  927. cshift_late_value = 0
  928. cshift_late = convert_excel_time(cshift_late_value)
  929. cshift_leave_value = 0
  930. cshift_leave = convert_excel_time(cshift_leave_value)
  931. # cshift_over_value = check_odoo_overtime2(db,
  932. # emplist[emp_no], run_date, sfTable[cshift_no]['begin'], cshift_end_time_value, 0) # 零點開始
  933. #else:
  934. # cshift_over_value = check_odoo_overtime(db,
  935. # emplist[emp_no], run_date, sfTable[cshift_no]['etime'], cshift_end_time_value, cshift_over_value) # 下班開始
  936. # # 因中午休息時間一小時,再取一次表單(不做出勤比對), 早上到下班時間
  937. # cshift_over_value = check_odoo_overtime2(db,
  938. # emplist[emp_no], run_date, sfTable[cshift_no]['btime'], sfTable[cshift_no]['etime'], cshift_over_value)
  939. cshift_over = convert_excel_time(cshift_over_value[0])
  940. cshift_over_amt = calc_over_amt(_db, emplist[emp_no],
  941. run_date, cshift_over_value[0]) # 加班算金額
  942. #加班2(特殊加班)
  943. cshift_over2 = convert_excel_time(cshift_over_value[1])
  944. cshift_over2_amt = calc_over2_amt(_db, emplist[emp_no],
  945. run_date, cshift_over_value[1]) # 加班算金額
  946. cshift_total_value = cshift_end_time_value - cshift_beg_time_value
  947. if sfTable[cshift_no]['rest_hours'] > 0 and cshift_total_value > 0: # 扣掉休息時間
  948. cshift_total_value = cshift_total_value - \
  949. (sfTable[cshift_no]['rest_hours'] / 24)
  950. cshift_total = convert_excel_time(cshift_total_value)
  951. #請假總時數
  952. cshift_leave_hours = 0
  953. if emp_no in leave_hours:
  954. cshift_leave_hours = leave_hours[emp_no]
  955. row = dict(cshift_emp_no=emp_no,
  956. cshift_dept=emplist[emp_no]['dept'], cshift_no=cshift_no,
  957. cshift_beg_date=run_date, cshift_beg_time=cshift_beg_time, cshift_beg_time_value=cshift_beg_time_value,
  958. cshift_end_date=run_date, cshift_end_time=cshift_end_time, cshift_end_time_value=cshift_end_time_value,
  959. cshift_leave_hours=cshift_leave_hours,
  960. cshift_late=cshift_late, cshift_late_value=cshift_late_value,
  961. cshift_leave=cshift_leave, cshift_leave_value=cshift_leave_value,
  962. cshift_over=cshift_over,
  963. cshift_over_value=cshift_over_value[0], cshift_over_amt=cshift_over_amt[0], cshift_over_flag=cshift_over_amt[1],
  964. cshift_over2=cshift_over2,
  965. cshift_over2_value=cshift_over_value[1], cshift_over2_amt=cshift_over2_amt[0], cshift_over2_flag=cshift_over2_amt[1],
  966. cshift_over_beg_time=cshift_over_beg_time, cshift_over_end_time=cshift_over_end_time,
  967. cshift_resource_calendar_id=emplist[emp_no]['resource_calendar_id'],
  968. cshift_total=cshift_total, cshift_total_value=cshift_total_value,
  969. cshift_sysdate=datetime.datetime.now())
  970. if tcshift.find_one(cshift_emp_no=emp_no, cshift_no=cshift_no, cshift_beg_date=run_date):
  971. tcshift.delete(
  972. cshift_emp_no=emp_no, cshift_no=cshift_no, cshift_beg_date=run_date)
  973. tcshift.insert(row)
  974. _db.commit()
  975. except Exception as e:
  976. logging.error(str(e))
  977. _db.rollback()
  978. def run_hr_cmonth(db, year, month, days=None, run_emp_no=None):
  979. #logger = logging.getLogger('cmonth')
  980. _db = db
  981. reload_working_times(_db, year, month) # 工作天數
  982. _days = days
  983. if _days is None:
  984. _days = calendar.monthrange(year, month)[1]
  985. now_date = datetime.datetime.now().strftime("%H:%M:%S")
  986. str1 = 'hr_cmonth %d-%d-%d start: %s' % (year, month, _days, now_date)
  987. logging.info(str1)
  988. for day in range(_days):
  989. _day = day+1
  990. run_date = datetime.date(year, month, _day)
  991. run_hr_cshift(_db, run_date, run_emp_no=run_emp_no)
  992. now_date = datetime.datetime.now().strftime("%H:%M:%S")
  993. str1 = 'hr_cmonth %d-%d-%d end: %s' % (year, month, _days, now_date)
  994. logging.info(str1)
  995. #run_export_cshift_file(_db, year, month, run_emp_no=run_emp_no)
  996. def run_hr_attendance(db, year, month, run_emp_no=None): # 刷卡資料轉入odoo考勤
  997. #logger = logging.getLogger('hr_attendance')
  998. _db = db
  999. models = endpoint_object()
  1000. uid = get_uid()
  1001. #thr_attendance = _db['hr_attendance']
  1002. days = calendar.monthrange(year, month)[1]
  1003. date_start = '%04d-%02d-%02d' % (year, month, 1)
  1004. date_end = '%04d-%02d-%02d' % (year, month, days)
  1005. now_date = datetime.datetime.now().strftime("%H:%M:%S")
  1006. logging.info('hr_attendance start: '+now_date)
  1007. _sql = 'SELECT he.id as emp_id, hhc.* FROM htp_hr_cshift as hhc inner join hr_employee as he on (he.emp_no = hhc.cshift_emp_no) '
  1008. _sql += 'where he.id is not null and hhc.cshift_beg_date BETWEEN \'%s\' and \'%s\' and hhc.cshift_beg_time_value > 0 ' % (
  1009. date_start, date_end)
  1010. if run_emp_no:
  1011. _sql += 'and he.emp_no = \'%s\' ' % (run_emp_no)
  1012. _sql += 'order by hhc.cshift_beg_date, hhc.cshift_beg_time '
  1013. data_rows = _db.query(_sql)
  1014. if data_rows:
  1015. datestr = date_start+' 00:00:00'
  1016. dt = datetime2UTC(datestr)
  1017. datestr = datetime.datetime.strftime(dt, '%Y-%m-%d %H:%M:%S')
  1018. datestr2 = date_end+' 00:00:00'
  1019. dt = datetime2UTC(datestr2)
  1020. datestr2 = datetime.datetime.strftime(dt, '%Y-%m-%d %H:%M:%S')
  1021. del_ids = None
  1022. if run_emp_no:
  1023. _sql = 'SELECT id as emp_id FROM hr_employee where emp_no = \'%s\' ' % (run_emp_no)
  1024. emp_row = _db.query(_sql)
  1025. for emp in emp_row:
  1026. del_ids = models.execute_kw(odoo_db, uid, odoo_password, 'hr.attendance', 'search', [[
  1027. ['employee_id', '=', emp['emp_id']], ['check_in', '>=', datestr], ['check_in', '<=', datestr2]]])
  1028. else:
  1029. del_ids = models.execute_kw(odoo_db, uid, odoo_password, 'hr.attendance', 'search', [[
  1030. ['check_in', '>=', datestr], ['check_in', '<=', datestr2]]])
  1031. if del_ids:
  1032. models.execute_kw(odoo_db, uid, odoo_password,
  1033. 'hr.attendance', 'unlink', [del_ids])
  1034. #_sql = 'delete from hr_attendance where date_part(\'year\', check_out) = %d and date_part(\'month\', check_out) = %d ' % (year, month)
  1035. #if run_emp_no:
  1036. # _sql += ' and employee_id = (select id from hr_employee where emp_no = \'%s\') ' % (run_emp_no)
  1037. #stmt = text(_sql)
  1038. #_db.executable.execute(stmt)
  1039. for row in data_rows:
  1040. datestr = '%s %s' % (row['cshift_beg_date'], row['cshift_beg_time'])
  1041. dt = datetime2UTC(datestr)
  1042. datestr = datetime.datetime.strftime(dt, '%Y-%m-%d %H:%M:%S')
  1043. datestr2 = '%s %s' % (row['cshift_beg_date'], row['cshift_end_time'])
  1044. dt = datetime2UTC(datestr2)
  1045. datestr2 = datetime.datetime.strftime(dt, '%Y-%m-%d %H:%M:%S')
  1046. # 找不到寫入
  1047. #if (thr_attendance.find_one(employee_id=row['emp_id'], check_in=datestr, check_out=datestr2)) and (datestr <= datestr2):
  1048. # if thr_attendance.find_one(employee_id=row['emp_id'], check_in=datestr):
  1049. # thr_attendance.delete(employee_id=row['emp_id'], check_in=datestr)
  1050. if datestr <= datestr2:
  1051. models.execute_kw(odoo_db, uid, odoo_password, 'hr.attendance', 'create', [{
  1052. 'employee_id': row['emp_id'], 'check_in': datestr, 'check_out': datestr2,
  1053. }])
  1054. now_date = datetime.datetime.now().strftime("%H:%M:%S")
  1055. #print()
  1056. logging.info('hr_attendance end: '+now_date)
  1057. def test():
  1058. sfList = getShitft()
  1059. timelist = [0.2, 0.41, 0.51, 0.53, 0.71, 0.73, 0.8]
  1060. # timelist = [0.2, 0.41, 0.51, 0.53, 0.71, 0.73, 0.8]
  1061. # timelist = [0.51, 0.52, 0.71, 0.73] # 前後忘刷
  1062. # timelist = [0.2, 0.51, 0.71, 0.73] # 前後忘刷
  1063. print(time2dict(timelist, sfList))
  1064. def run_export_cshift_file(db, year, month, run_emp_no=None):
  1065. #logger = logging.getLogger('run_export_cshift_file')
  1066. _db = db
  1067. tcdata = _db[prefix+'_cdata']
  1068. tcshift = _db[prefix+'_cshift']
  1069. _strdate = '%04d%02d' % (year, month)
  1070. sql = 'SELECT * FROM '+prefix + \
  1071. '_cshift where date_part(\'year\', cshift_beg_date) = %d and date_part(\'month\', cshift_beg_date) = %d ORDER BY cshift_beg_date, cshift_emp_no, cshift_no' % (
  1072. year, month)
  1073. if run_emp_no is not None:
  1074. sql = 'SELECT * FROM '+prefix + \
  1075. '_cshift where date_part(\'year\', cshift_beg_date) = %d and date_part(\'month\', cshift_beg_date) = %d and cshift_emp_no = \'%s\' ORDER BY cshift_beg_date, cshift_emp_no, cshift_no' % (
  1076. year, month, run_emp_no)
  1077. data_rows = _db.query(sql)
  1078. wb = xlwt.Workbook()
  1079. sheet = wb.add_sheet(_strdate)
  1080. # sheet.write(0, 0, '123')
  1081. # sheet.write(0, 1, '567')
  1082. # sheet.write(1, 0, '999') #第二行
  1083. titlelist = ['員工編號', '姓名', '部門', '時段', '開始日期', '開始時間',
  1084. '結束日期', '結束時間', '遲到時間', '早退時間', '加班時間', '上班時間']
  1085. i = 0
  1086. for item in titlelist:
  1087. sheet.write(0, i, item)
  1088. i += 1
  1089. i = 1
  1090. for row in data_rows:
  1091. empno = row['cshift_emp_no']
  1092. empname = row['cshift_emp_no']
  1093. dept = row['cshift_dept']
  1094. cshift_no = row['cshift_no']
  1095. cshift_beg_date = row['cshift_beg_date'].strftime("%Y-%m-%d")
  1096. cshift_beg_time = row['cshift_beg_time']
  1097. cshift_end_date = row['cshift_end_date'].strftime("%Y-%m-%d")
  1098. cshift_end_time = row['cshift_end_time']
  1099. cshift_late = row['cshift_late']
  1100. cshift_leave = row['cshift_leave']
  1101. cshift_over = row['cshift_over']
  1102. cshift_total = row['cshift_total']
  1103. tcrow = tcdata.find_one(cdata_emp_no=empno)
  1104. if tcrow:
  1105. empname = tcrow['cdata_emp_name']
  1106. sheet.write(i, 0, empno)
  1107. sheet.write(i, 1, empname)
  1108. sheet.write(i, 2, dept)
  1109. sheet.write(i, 3, cshift_no)
  1110. sheet.write(i, 4, cshift_beg_date)
  1111. sheet.write(i, 5, cshift_beg_time)
  1112. sheet.write(i, 6, cshift_end_date)
  1113. sheet.write(i, 7, cshift_end_time)
  1114. sheet.write(i, 8, cshift_late)
  1115. sheet.write(i, 9, cshift_leave)
  1116. sheet.write(i, 10, cshift_over)
  1117. sheet.write(i, 11, cshift_total)
  1118. i += 1
  1119. wb.save('cshitft_'+_strdate+'.xls')
  1120. def reload_working_times(db, year, month): # 轉存table
  1121. _db = db
  1122. uid = get_uid()
  1123. models = endpoint_object()
  1124. _table = _db[prefix+'_working_times']
  1125. _table.delete()
  1126. _btime_str = '%04d-%02d-01 00:00:00' % (year, month)
  1127. days = calendar.monthrange(year, month)[1]
  1128. _etime_str = '%04d-%02d-%02d 23:59:59' % (year, month, days)
  1129. main_rows = _db.query('SELECT * FROM resource_calendar ORDER BY id ASC ')
  1130. if uid and main_rows:
  1131. _db.begin()
  1132. try:
  1133. for row in main_rows:
  1134. work_times = models.execute_kw(odoo_db, uid, odoo_password, 'hr.payslip', 'get_working_times', [
  1135. row['id'], _btime_str, _etime_str])
  1136. # print(data)
  1137. for item in work_times:
  1138. if getHoliday(db, row['id'], item[0])[0]: # 國定假日
  1139. continue
  1140. data = dict(
  1141. work_id=row['id'], work_beg_time=item[0], work_end_time=item[1])
  1142. _table.insert(data)
  1143. _db.commit()
  1144. except Exception as e:
  1145. logging.error(str(e))
  1146. _db.rollback()
  1147. def test_over_amt(db): # 測試加班
  1148. reload_working_times(db, 2021, 3)
  1149. emp = {}
  1150. emp['resource_calendar_id'] = 1
  1151. hours = 10*60*60 # 小時
  1152. price = 100
  1153. # _amt = calc_over_amt(
  1154. # db, emp, datetime.datetime(2021, 3, 18), hours) # 加班算金額 (平日)
  1155. # print(_amt)
  1156. # print(_amt[0]*price)
  1157. # _amt = calc_over_amt(
  1158. # db, emp, datetime.datetime(2021, 4, 6), hours) # 加班算金額 (例假日)
  1159. # print(_amt)
  1160. # print(_amt[0]*price)
  1161. _amt = calc_over_amt(
  1162. db, emp, datetime.datetime(2021, 3, 28), hours) # 加班算金額 (休息日)
  1163. print(_amt)
  1164. print(_amt[0]*price)
  1165. def run_hr_cdata_dir(db, base_dir):
  1166. ret = False
  1167. _db = db
  1168. _base_dir = base_dir
  1169. _bak_dir = _base_dir+'/bak'
  1170. # if not os.path.exists(_bak_dir):
  1171. os.makedirs(_bak_dir, exist_ok=True)
  1172. arr = glob.glob(_base_dir+'/*.xlsx')
  1173. ret = (len(arr) > 0)
  1174. for filename in arr:
  1175. #print(filename)
  1176. run_hr_cdata(_db, filename) # 輸入
  1177. fsa = filename.split('/')
  1178. copyfile(filename, _bak_dir+'/'+fsa[-1])
  1179. os.remove(filename)
  1180. return ret
  1181. def run_now(db, filedir=None, now=None):
  1182. _db = db
  1183. _filedir = filedir
  1184. if _filedir is None:
  1185. _filedir = '/media/Share'
  1186. # FMT = '%(asctime)s %(filename)s %(funcName)s %(levelname)s %(message)s'
  1187. # logging.basicConfig(format=FMT, filename=_filedir+'/hr_carddata.log', level=logging.INFO)
  1188. # sudo mount -t cifs //192.168.80.5/carddata /media/Share -o username='hhli',password='Ht1230136',uid=1000,domain=hantecprecision
  1189. #fstab
  1190. #//192.168.80.5/carddata /media/Share cifs username=hhli,password=Ht1230136,uid=1000,domain=hantecprecision
  1191. #NFS
  1192. # mount -t nfs 192.168.80.5:/volume1/carddata /media/Share
  1193. _now = now
  1194. if _now is None:
  1195. _now = datetime.datetime.now()
  1196. if run_hr_cdata_dir(_db, _filedir): # 取目錄資料
  1197. if _now.day in [1,2,3,4]: #每月1-3日重新計算上個月
  1198. _now2 =_now+relativedelta(months=-1)
  1199. run_hr_cmonth(_db, _now2.year, _now2.month) # 計算上個月
  1200. run_hr_cmonth(_db, _now.year, _now.month, _now.day) # 計算
  1201. run_hr_attendance(_db, _now.year, _now.month) # 轉入odoo考勤
  1202. def run_month(year, month, days=None, url='http://192.168.80.3:8069', dbname='htp', dburl='postgresql://odoo:odootyughj@192.168.80.3:5432/htp'):
  1203. global odoo_url, odoo_db
  1204. odoo_url = url
  1205. odoo_db = dbname
  1206. db = dataset.connect(dburl)
  1207. db.vars = {}
  1208. db.vars['emplist'] = loadOdooEmp(db)
  1209. run_hr_cmonth(db, year, month, days)
  1210. def run_htp(url='http://192.168.80.3:8069', dbname='htp', dburl='postgresql://odoo:odootyughj@192.168.80.3:5432/htp'):
  1211. global odoo_url, odoo_db
  1212. #odoo_url = 'http://192.168.80.3:8069'
  1213. odoo_url = url
  1214. #odoo_db = 'htp'
  1215. odoo_db = dbname
  1216. #db = dataset.connect('postgresql://odoo:1230136@192.168.80.3:5432/htp')
  1217. db = dataset.connect(dburl)
  1218. db.vars = {}
  1219. db.vars['emplist'] = loadOdooEmp(db)
  1220. #run_now(db, now=datetime.datetime(2021, 10, 31))
  1221. run_now(db)
  1222. if __name__ == "__main__":
  1223. FMT = '%(asctime)s %(filename)s %(funcName)s %(levelname)s %(message)s'
  1224. #logging.basicConfig(format=FMT, filename='hr.log', level=logging.DEBUG)
  1225. logging.basicConfig(format=FMT, level=logging.INFO)#直列
  1226. if len(sys.argv) > 1:
  1227. if sys.argv[1] == 'now':
  1228. run_htp(url='http://192.168.80.3:8069', dbname='htp', dburl='postgresql://odoo:odootyughj@192.168.80.3:5432/htp')
  1229. print('Run end.')
  1230. else:
  1231. try:
  1232. dd = datetime.datetime.strptime(sys.argv[1], '%Y%m%d').date()
  1233. _db = dataset.connect('postgresql://odoo:odootyughj@192.168.80.3:5432/htp') #正式
  1234. _db.vars = {}
  1235. _db.vars['emplist'] = loadOdooEmp(_db)
  1236. run_hr_cmonth(_db, dd.year, dd.month, dd.day)
  1237. print('Run end.')
  1238. except:
  1239. print('Error date format.')
  1240. exit()
  1241. allow_none = True #debug
  1242. #run_month(2021, 10, url='http://0.0.0.0:8069', dbname='htp', dburl='postgresql://odoo:odoo@192.168.80.94:5432/htp')
  1243. #exit()
  1244. # print(math.ceil(1.1)) #無修件進位
  1245. #print(math.floor(140.60)) #無條件去位
  1246. #run_htp(url='http://192.168.80.3:8069', dbname='htp', dburl='postgresql://odoo:odoo@192.168.80.3:5432/htp')
  1247. #exit()
  1248. #_db = dataset.connect('postgresql://odoo:odoo@192.168.80.193:5432/htp')
  1249. #_db = dataset.connect(
  1250. # 'postgresql://odoo:odootyughj@192.168.80.11:5432/htp_hr') #htp hr
  1251. _db = dataset.connect(
  1252. 'postgresql://odoo:odootyughj@192.168.80.3:5432/htp') #正式
  1253. _db.vars = {}
  1254. # run_hr_attendance(_db, 2021, 6) # 轉入odoo考勤
  1255. # exit()
  1256. # _db.vars['emplist'] = loadXlsEmp()
  1257. _db.vars['emplist'] = loadOdooEmp(_db)
  1258. _path = '/home/xz/htp/odoo13/extension/xls2table/tmp/'
  1259. _filename = _path+'exm20220810.xls'
  1260. #run_hr_cdata(_db, _filename) # 輸入
  1261. # test_over_amt(_db)
  1262. # run_hr_cshift(_db, datetime.date(2021, 1, 6))
  1263. #run_hr_cmonth(_db, 2022, 8, run_emp_no='202008004') # 計算
  1264. #run_hr_cmonth(_db, 2022, 8) # 計算
  1265. #run_hr_cmonth(_db, 2022, 8, 29) # 計算
  1266. # run_export_cshift_file(_db, 2021, 1) #匯出excel
  1267. #run_hr_attendance(_db, 2021, 12) # 轉入odoo考勤