From 8855109f7a6e5b5bbf5882717d996437a1f67ce5 Mon Sep 17 00:00:00 2001 From: A-Ismail-M Date: Wed, 3 Jul 2024 16:46:33 +0300 Subject: [PATCH] ADD [odex_web_app/] --- odex25_mobile/odex_web_app/__init__.py | 2 + odex25_mobile/odex_web_app/__manifest__.py | 28 ++ .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 243 bytes .../__pycache__/http_helper.cpython-38.pyc | Bin 0 -> 4059 bytes .../__pycache__/validator.cpython-38.pyc | Bin 0 -> 7731 bytes .../odex_web_app/controllers/__init__.py | 3 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 255 bytes .../__pycache__/authentication.cpython-38.pyc | Bin 0 -> 2249 bytes .../__pycache__/web.cpython-38.pyc | Bin 0 -> 935 bytes .../controllers/authentication.py | 59 ++++ odex25_mobile/odex_web_app/controllers/web.py | 18 ++ odex25_mobile/odex_web_app/http_helper.py | 128 ++++++++ odex25_mobile/odex_web_app/models/__init__.py | 3 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 300 bytes .../attendence_zone_config.cpython-38.pyc | Bin 0 -> 975 bytes .../__pycache__/hr_employee.cpython-38.pyc | Bin 0 -> 1255 bytes .../__pycache__/mail_thread.cpython-38.pyc | Bin 0 -> 1125 bytes .../models/attendence_zone_config.py | 17 + .../odex_web_app/models/hr_employee.py | 30 ++ .../odex_web_app/models/mail_thread.py | 31 ++ odex25_mobile/odex_web_app/validator.py | 293 ++++++++++++++++++ .../views/attendance_zone_config_view.xml | 38 +++ .../odex_web_app/views/hr_employee_view.xml | 21 ++ 23 files changed, 671 insertions(+) create mode 100644 odex25_mobile/odex_web_app/__init__.py create mode 100644 odex25_mobile/odex_web_app/__manifest__.py create mode 100644 odex25_mobile/odex_web_app/__pycache__/__init__.cpython-38.pyc create mode 100644 odex25_mobile/odex_web_app/__pycache__/http_helper.cpython-38.pyc create mode 100644 odex25_mobile/odex_web_app/__pycache__/validator.cpython-38.pyc create mode 100644 odex25_mobile/odex_web_app/controllers/__init__.py create mode 100644 odex25_mobile/odex_web_app/controllers/__pycache__/__init__.cpython-38.pyc create mode 100644 odex25_mobile/odex_web_app/controllers/__pycache__/authentication.cpython-38.pyc create mode 100644 odex25_mobile/odex_web_app/controllers/__pycache__/web.cpython-38.pyc create mode 100644 odex25_mobile/odex_web_app/controllers/authentication.py create mode 100644 odex25_mobile/odex_web_app/controllers/web.py create mode 100644 odex25_mobile/odex_web_app/http_helper.py create mode 100644 odex25_mobile/odex_web_app/models/__init__.py create mode 100644 odex25_mobile/odex_web_app/models/__pycache__/__init__.cpython-38.pyc create mode 100644 odex25_mobile/odex_web_app/models/__pycache__/attendence_zone_config.cpython-38.pyc create mode 100644 odex25_mobile/odex_web_app/models/__pycache__/hr_employee.cpython-38.pyc create mode 100644 odex25_mobile/odex_web_app/models/__pycache__/mail_thread.cpython-38.pyc create mode 100644 odex25_mobile/odex_web_app/models/attendence_zone_config.py create mode 100644 odex25_mobile/odex_web_app/models/hr_employee.py create mode 100644 odex25_mobile/odex_web_app/models/mail_thread.py create mode 100644 odex25_mobile/odex_web_app/validator.py create mode 100644 odex25_mobile/odex_web_app/views/attendance_zone_config_view.xml create mode 100644 odex25_mobile/odex_web_app/views/hr_employee_view.xml diff --git a/odex25_mobile/odex_web_app/__init__.py b/odex25_mobile/odex_web_app/__init__.py new file mode 100644 index 000000000..f7209b171 --- /dev/null +++ b/odex25_mobile/odex_web_app/__init__.py @@ -0,0 +1,2 @@ +from . import models +from . import controllers diff --git a/odex25_mobile/odex_web_app/__manifest__.py b/odex25_mobile/odex_web_app/__manifest__.py new file mode 100644 index 000000000..814a39f1d --- /dev/null +++ b/odex25_mobile/odex_web_app/__manifest__.py @@ -0,0 +1,28 @@ +{ + 'name': 'Odex Web App API', + 'version': '1.0', + 'license': 'AGPL-3', + 'category': 'Odex25-Mobile/Odex25-Mobile', + 'author': 'Expert Co. Ltd.', + 'website': 'http://exp-sa.com', + 'summary': "All Mopile Web App Api and Configurations", + 'depends': [ + 'base', + 'attendances', + 'employee_requests', + 'website', + 'mass_mailing', + 'hr_holidays_public', + 'exp_payroll_custom', + 'hr_timesheet_sheet', + ], + 'external_dependencies': { + 'python': ['jwt', ], + }, + 'data': [ + 'views/attendance_zone_config_view.xml', + 'views/hr_employee_view.xml', + ], + 'installable': True, + 'application': False, +} diff --git a/odex25_mobile/odex_web_app/__pycache__/__init__.cpython-38.pyc b/odex25_mobile/odex_web_app/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e76a9d7eebb5e995d2c1a2f5c61620661843fa70 GIT binary patch literal 243 zcmWIL<>g`k0(ZsMG<_ib7{oyaOhAqU5Esh;i4=wu#vF!R#wbQc5St0eW{P40vYCS! zG+ACU0+nbo-eSwmPf5)w2C}%5^YcoI@^f-hi;Dd;nQt)^fs89+1`;b7idaEx5b-Nq zKO;XkRX??&K;JbeKtDevKi|+qH!rnZzqlkZFD0=kB_3#IX-;afzHfd~W=<+lNos|W qDNq#7i!V=2icc&k(2tML%*!l^kJl@xyv1Py(Psy8S~19S9!3D0);yE| literal 0 HcmV?d00001 diff --git a/odex25_mobile/odex_web_app/__pycache__/http_helper.cpython-38.pyc b/odex25_mobile/odex_web_app/__pycache__/http_helper.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..443d5355905b80545c467325070a718ac6ec1274 GIT binary patch literal 4059 zcmbVPNpBm;6|U-yO^T#HrVjwlINltpFJZdE^JVop?FGf{nIv}&^UinZ#(US*BiTb#xk zJh!&8tlKd1l9JDQGR<=Lu}r&3oXWSn;3z*2qEG}`qMEr8irkKp%`ncbIOxc{(oQ1#20C3*$9wnWPkYOL!#sYu>WOM_Odwa>S2CT^z$) zN&G~d5GO&o;(|CW&VVY5d2v>J0IDL+iSyWFPJAf7hrTMlFMfc&CVnV>guX7yQ21hA zxerb6%cv_=n^YS=L>u_u!9T5{5j2V&!eZkqmN{8TzX62pu~+;R-!%mv zm`@$`XaH&jDAl9iV`N}t>XE|8JkQWtH-OPyOIUjTZ~e10cb*L^!r9}Zbe`>6IREtM z{J$3G7iSOn0DBLt4GRu*oBhg8x*0BnakrQC=QJ&qN>rMkyuY^g$iMx?+WpTTKWN=v zd+>QPuifrtTZsz$K}H^92i#L+i^g2x%Q>>~81zm@f>|iSLGE+|6?D?PeD_6LcJ%3x|2JMXgRnwvs<1AJjqknG3`G+<-I5Gd^@_gs=yzu7l zFV8<*o?mSffX9yoVuFdEVIX+_Lcc!XLpCt>;9+>=c;N|wQQ?g**>(8yYiVF9>Loi=VemQYbSB0k|>huy+%xX?KV=?%9`p)FN}d++9=x!Q%{Z3 zy9av2w$Y0`>=`Fho6WeYgN=esxzbEx>5T^8;#pWFD0qn?6eyYXR6NmYX6rUm;L4Sy zC68hedj?rAg=#`hnD;Ft-9nkXG&(%DXy@D_MnJF%F0Z}p%KmYr&xq7T&@XPM zNsPPADR$O@*f4X8rgK~LI^9%JC^Sk1CKf=eMQWC)`2-@jcT$N1(DYR3ErXC6Ro6$g>GDiuPN3{%x%xSEW}(nj7rLTm@=kRug@1i%=^@rq%6yFXKbXPyEYCsh z+R`%XRe~LFnqyZafZR){Q#w&E`GS7#e?sx&nk`CCrlFH5QB6Bc3b)pA-nc1J~GC zCuf{ET0SHXL24ooWw`+xvOTtI;sW64t%0F?J2SS;Auevgag|#Gdk;xUQ3-eDN zY2XUy2Dx+&ROtgmu1cPVD7qE`eXkYDYw-{G3eq%fZ{7mU?5{(LEPqt*a-laic!t#x@+ze4;Zl3TcBs^b zx2IncbquE3YHfIP^dX@|RZSA$1T{xvx*f?N?sa#tR>ZW^pN;9!GGKEF91VM|+0?~K z6RQ0JGt}Q0^*42olaJ-D@6$J%@8=bN^eqCq>if@nK~$_10KABI)K96Q%S8Q(niXn( zP0cN8?o#s^HIJ#GlsKb>)`w7|=zEIdJw1oUHC)^Muu`v_s+h&kb=~8xqq!iUMITK? z4PGX4fy#oyw{%Sx_^+g4ryEJS!_`&H<|TL>AEcZ5E9o=PNO@~|fgp@gUMngPeI=%SBdY|OEd!zVVyGdS6DQj2xan$4#1{I@j)_eZ}47Rg7 literal 0 HcmV?d00001 diff --git a/odex25_mobile/odex_web_app/__pycache__/validator.cpython-38.pyc b/odex25_mobile/odex_web_app/__pycache__/validator.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..09fc085ac17afcd8c925d162cc9205fe57603385 GIT binary patch literal 7731 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 tQyvifhao?z$x@io(t-Y;{{4a+>KB@o! literal 0 HcmV?d00001 diff --git a/odex25_mobile/odex_web_app/controllers/__init__.py b/odex25_mobile/odex_web_app/controllers/__init__.py new file mode 100644 index 000000000..f9417ca5b --- /dev/null +++ b/odex25_mobile/odex_web_app/controllers/__init__.py @@ -0,0 +1,3 @@ +from . import authentication +from . import web + diff --git a/odex25_mobile/odex_web_app/controllers/__pycache__/__init__.cpython-38.pyc b/odex25_mobile/odex_web_app/controllers/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d027d9eca215c3e8ee12d5ddad590d28974c3bc2 GIT binary patch literal 255 zcmWIL<>g`k0(ZsMGy@?07{oyaOhAqU5Esh;i4=wu#vF!R#wbQc5St0eW{P40vYCS! zG+ACU0+nbo-r`FvEy+mDE6GewEXmBz1Bx)0rzZJnGT&k-0vT7t3?x=E6tRNXAmUfD zenx(7s(xxkfxc@{fPQ{Te!iiJZeD7+esM`+UP@w7N_=j9N@-4NvA%D9Qf5vnP)TZq zktt9V&Wi`y5uaF4pr4$dS5lOplapFhtREkrnU`4-AFo$Xd5gmaVul^anZ+Q7@Gt@Z D!<9j5 literal 0 HcmV?d00001 diff --git a/odex25_mobile/odex_web_app/controllers/__pycache__/authentication.cpython-38.pyc b/odex25_mobile/odex_web_app/controllers/__pycache__/authentication.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6f28a58757c06c25ff8d1f8532187417aee802ed GIT binary patch literal 2249 zcmZ`4%WfMtaJdid>Mh%H;yjSlsdkquTFp4!#u6m3T-P04pGmzXHv6)z&l@z>!+MBVRIG&?mD=sD_ zucTlUm%TDBU^}iP6|Vxg0(RnRQuV6m^qN;w_td>Qti`yItavL(BlYPjSvx~s6PIuq zS8x^A`j)r;jfU&E@j}B5q7N^e7~VyUx>{@H4d|=wwsc_*IM0Q}vn*z?wJCWvB8&^? z5n*|jGSWh#+>QF_DBq$qqp&SLW`wHE8y%!b%s~)GIN*w0mJkkuRJ)VC*oX zSk&f_AJU_oi%Lok{W(sQLP`Qo{85BO@sLI{FqEn;e-3Q!z+g9EBASP==IO*3niydn z8`#7aF7y$$Pr-S(_(D5Do`p+bc3EtEJ>mnB@+b^=l%;pGl+!GZ2_4rv6zukcJnF<* zKT4^zO_*txl5K%L5az@0x_d24nEWUw!VEx;FhfRrFlUqrJ0X0KVHP4dH~%1Jv;vH8 zVX$9;GC@;_k~4IyD@X^(nCR>VM?+AZgYqXxs()&9wTZ#*E%5ddUdnLJ5Zhl+i~pgP zfcoQtT7F06=Fq~GOWL$>rVVXySaqWJ3~*Kr*H86hb7Ioh6LaXW-xjAECumxnfTnZ3 zIGUCw`ouWXkFANt{sPXj(tQQ)Sv}Re8m~;N@}^-8>?_QmX&pm+9^1SzlsYsQI;?{Z z=5S?63uofIMD&UB5ZI|;XSn(ju_qHpYCc`V&YT*7H<6}Jyiz!@9XJmZd_kRlH9s|{ z&uRGL&tMmvE=+8^aZ3Zsnr9k&mXMW^qx0w~SuGTaK@eAQ93a zA#O%pk_1ugM$Anj#-g;p-7;ikU?L-fv7iqC8-*djfKBjXVA&x)fY;^R%3v~CcF*(l}i zVZ;ZnGROVl*iAF;_5hE^*B__BeoS1RxiaV7owV&nsmg9w3U=qY+S2J2kWa6|v^Opf zXqzN?oE;IcQZj-SqPo!6KP3Bb;yrm{6DC=2q!}AZDbNEhanGI88^!UFEKE>O0wuq- zE{s0mRF-1lfQ_my(Xy0gN}NH`17Wfe&P0KcfQAElS>g-5j3xn8nTYu^Usc` zh05dhgT|ZxS3lcJ*X*_m!ts5X8NM${ z{<$pxSA753D2Qj2nR_jojX0sVKq=*$rRhtFTY+f}29rOMBN9GQb#`T3fowXR52U!OLtBq`~x;igI)jt literal 0 HcmV?d00001 diff --git a/odex25_mobile/odex_web_app/controllers/__pycache__/web.cpython-38.pyc b/odex25_mobile/odex_web_app/controllers/__pycache__/web.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1059230ada7673f4eb1c6d81ba13e1d83505aac7 GIT binary patch literal 935 zcmY*YO>Yx15VgJDk5)+q$Ck^Uka8d^5O6|OrSfqiLLgKr2UffBq}ga=Z*4Daqx95X zk@x`|+9Q9-S5Eu|4v6tKKx)b38IO1T-W%`7et(4>kwK-~gw2jz$nc zYclpLtI2|nDfNC(vqdltD0xE!7vU8VA<#L$4920LAIKp3iQkgZfI2?2w!t;laMFUY z6Q9@|AqEFH%|kRm##E3ogJ8}D6-)$ze+fh)A`xHFON!+~EWhRYM^GLrX|<|r(97NI z6w1tiF_n^8(b^eETTKcJj?ayfj!PwBz#O;d4et39qlstwcSPuTD2F)BJ{n8rbk42= zL2u{&p$E54#Dc7sY_9E)(5TqifgD=>1gD05jXU^#qQ*rF^9 zq&`f|OtrO0OY|C;RMQGXTFDgYOw-p@4b@jl2x1LEYo%ReY++loj4M6t#N2XR38h?U zm8y-4Cir1>?1E#kIzZ$uUJK!Rca#V~es;ZeF0bTN4I;-4)KeGM>bR0FX$oUbl@_k= z;pVkM2~knUMCtk(Ors=j3#9X%=smpa*Qacz7LdVNlf8QXF7v=1KYb+OG(*@@6k6m9 zCE6NH_Ewe14v)aulV`a5FUqlbdC@c(GFf-j_=XOfbA2B#iE)~HXb9V(oH5FNN1U^J zbh&ru84Ne+4(JH!C3)9^Ja^mq8VLHmJU?lR+IJ(*HU0#|g<7>1y6ZY(8|DG#wSO!A zjTrAFbcc33gGl#$wkxsa$#*TfZNv8e2ihIpDCwoszOGAN+{&|$JtxNBkkB6O1%Cjx CM*Tzp literal 0 HcmV?d00001 diff --git a/odex25_mobile/odex_web_app/controllers/authentication.py b/odex25_mobile/odex_web_app/controllers/authentication.py new file mode 100644 index 000000000..1d9fb09af --- /dev/null +++ b/odex25_mobile/odex_web_app/controllers/authentication.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- +import werkzeug +from odoo import http, tools +from odoo.http import request, Response +from odoo.addons.auth_signup.models.res_users import SignupError +from odoo.exceptions import UserError +import base64 +from ..validator import validator +from ..http_helper import http_helper +import json +import logging +from odoo.tools.translate import _ + +_logger = logging.getLogger(__name__) + +SENSITIVE_FIELDS = ['password', 'password_crypt', 'new_password', 'create_uid', 'write_uid'] + + +class AuthenticationController(http.Controller): + + @http.route('/rest_api/login', type='http', auth='none', csrf=False, cors='*', methods=['POST']) + def login_phone(self, **kw): + login = kw.get('login') + password = kw.get('password') + if not login: + return http_helper.response(code=400, message=_('username or email is missing'), success=False) + + if not password: + return http_helper.response(code=400, message=_('Password is missing'), success=False) + if not kw.get('device_id'): + return http_helper.response(code=400, message=_('Device id is missing'), success=False) + + # check fcm_token + if not kw.get('fcm_token'): + return http_helper.response(code=400, message=_('FCM Token is missing'), success=False) + + user = request.env['res.users'].sudo().search([('login', '=', login)], limit=1) + + if not user or not user.login: + return http_helper.response(code=400, message=_('User account with login {} not found').format(login), + success=False) + + uid = http_helper.is_authentic(login, password) + + if not uid: + return http_helper.errcode(code=400, message=_('Unable to Sign In. invalid user password')) + token = validator.create_token(request.env.user) + dic = request.env.user.to_dict(True) + employee = http.request.env['hr.employee'].sudo().search([('user_id', '=', user.id)], limit=1) + if employee and kw.get('device_id') and not employee.device_id: + employee.sudo().write({'device_id': kw.get('device_id')}) + + # write fcm_token and fcm_token_web in employee + if employee and (kw.get('fcm_token') or kw.get('fcm_token_web')): + employee.sudo().write({'fcm_token': kw.get('fcm_token'), 'fcm_token_web': kw.get('fcm_token_web')}) + + dic['token'] = token + http_helper.cleanup(); + return http_helper.response(data=dic, message=_("User log in successfully")) diff --git a/odex25_mobile/odex_web_app/controllers/web.py b/odex25_mobile/odex_web_app/controllers/web.py new file mode 100644 index 000000000..2c893e3dc --- /dev/null +++ b/odex25_mobile/odex_web_app/controllers/web.py @@ -0,0 +1,18 @@ +import odoo +from odoo import http +from odoo.http import request + + +class WebController(http.Controller): + @http.route('/web/session/authenticate', type='json', auth="none") + def authenticate(self, login, password, base_location=None): + db = odoo.tools.config.get('db_name') + if not db: + response_data = { + "error": "Database name should be specified in Conf File", + "status": 400 + } + return response_data + + request.session.authenticate(db, login, password) + return request.env['ir.http'].session_info() diff --git a/odex25_mobile/odex_web_app/http_helper.py b/odex25_mobile/odex_web_app/http_helper.py new file mode 100644 index 000000000..f744e3df7 --- /dev/null +++ b/odex25_mobile/odex_web_app/http_helper.py @@ -0,0 +1,128 @@ +from odoo import http +from odoo.http import request, Response +from .validator import validator +import simplejson as json +import logging +_logger = logging.getLogger(__name__) + +return_fields = ['id', 'login', 'name', 'company_id','state'] + + +class HttpHelper: + + def get_state(self): + return { + 'd': request.session.db + } + + def parse_request(self): + http_method = request.httprequest.method + try: + body = http.request.params + except Exception: + body = {} + + headers = dict(list(request.httprequest.headers.items())) + if 'wsgi.input' in headers: + del headers['wsgi.input'] + if 'wsgi.errors' in headers: + del headers['wsgi.errors'] + if 'HTTP_AUTHORIZATION' in headers: + headers['Authorization'] = headers['HTTP_AUTHORIZATION'] + + # extract token + token = '' + if 'Authorization' in headers: + try: + # Bearer token_string + token = headers['Authorization'].split(' ')[1] + except Exception: + pass + + return http_method, body, headers, token + + def date2str(self, d, f='%Y-%m-%d %H:%M:%S'): + """ + Convert datetime to string + :param self: + :param d: datetime object + :param f='%Y-%m-%d%H:%M:%S': string format + """ + try: + s = d.strftime(f) + except: + s = None + finally: + return s + + def response(self, success=True, message=None, data=None, code=200,errors=None): + """ + Create a HTTP Response for controller + :param success=True indicate this response is successful or not + :param message=None message string + :param data=None data to return + :param code=200 http status code + """ + payload = json.dumps({ + 'success': success, + 'message': message, + 'data': data, + 'code':code + }) + + return Response(payload, status=code, headers=[ + ('Content-Type', 'application/json'), + ]) + + def response_500(self, message='Internal Server Error', data=None): + return self.response(success=False, message=message, data=data, code=500) + + def response_404(self, message='404 Not Found', data=None): + return self.response(success=False, message=message, data=data, code=404) + + def response_403(self, message='403 Forbidden', data=None): + return self.response(success=False, message=message, data=data, code=403) + + def errcode(self, code, message=None): + return self.response(success=False, code=code, message=message) + + def is_authentic(self, login, password): + state = self.get_state() + name = login.strip() + pwd = password.strip() + return request.session.authenticate(state['d'], name, pwd) + + + + def do_login(self, login, password): + # get current db + state = self.get_state() + name = login.strip() + pwd = password.strip() + + uid = request.session.authenticate(state['d'], name, pwd) + if not uid: + return self.errcode(code=400, message='incorrect login') + # login success, generate token + token = validator.create_token(request.env.user) + dic = request.env.user.to_dict(True) + dic['token'] = token + + return self.response(data=dic) + + def do_logout(self, token): + request.session.logout() + request.env['jwt_provider.access_token'].sudo().search([ + ('token', '=', token) + ]).unlink() + + def cleanup(self): + # Clean up things after success request + # use logout here to make request as stateless as possible + + + + request.session.logout() + + +http_helper = HttpHelper() diff --git a/odex25_mobile/odex_web_app/models/__init__.py b/odex25_mobile/odex_web_app/models/__init__.py new file mode 100644 index 000000000..d024c97e2 --- /dev/null +++ b/odex25_mobile/odex_web_app/models/__init__.py @@ -0,0 +1,3 @@ +from . import hr_employee +from . import attendence_zone_config +from . import mail_thread diff --git a/odex25_mobile/odex_web_app/models/__pycache__/__init__.cpython-38.pyc b/odex25_mobile/odex_web_app/models/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5449254384a6563fb92e09ceca01ef2a169da1ea GIT binary patch literal 300 zcmYk1ze)r#5XO_;Rm8h%tu5U`gNJq^HkQhvSh$5l?lR{F@{c5PcY>u4bFZ|mm9Jps zL_r*wFTZd2h9M7=$v&fcJA7Y0()&@&|EkDbQKusk#S}Np@{aSu6>-&7KU@>nT}|BF zvv%i`Q@rKFsE>d)7{5UzdmwX0r_e1BUcEzDc(>HgC1|BKko$;I-L}oZV!0gaA`Quw z#ofY1zMe$yEec!*aeX%vUU`3dcI{SLwN^W~4}Cy$U{&bfX9bhc5<0V+u(%}GHS zEj;EGKJ$x!1=QhQ9u^UcTHDWK7QZ1fkl}YCL!c+gH=nH_63G~m7?BPlUFna>Ao-1v z$sn*%QA)^-jiwpo(pXw+?$j|CDfc}a(D)Zd^#z(Y47=Gn7&(tyKtkypEZ-bpjT`je-}WdSM7s&se%eOk)0eEjsGg0s}rLP??JGP_Cl%EK%Nd;+s4 z&k()xxC<|;DqT>Sin?~Ks0r|hGM!diFO2C6>FWyM~P zw%DL&12-&8poKCSsCv&We>(yj bE^L?Zwn}XBe6a>ETt?$M*rYw0`0LSM=N17K literal 0 HcmV?d00001 diff --git a/odex25_mobile/odex_web_app/models/__pycache__/hr_employee.cpython-38.pyc b/odex25_mobile/odex_web_app/models/__pycache__/hr_employee.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0f6fbca49487aeafdf3e6603c477f2d44deaf936 GIT binary patch literal 1255 zcmZuxPmdcl6t`zQ&Lo>`SEyR`P@o7nPSRpO6I-5eUD&G96hw7u}2S9kgV6(E8tA|gda3~ZLDNX4(H;EJi_B@rFP z0~+7^+8owxr56Cpox+UZ#uz+0IJ$QR<9nz20vfE(tutkFK8dzUc2EyIAKy zR9cZ4nbBD^i}#VA*U>t5?B@i%TqP^2`0Mx=x@OCL*O{fOXvJ2s>b#2A$r-YCLKtz~ zi=L?{StIMq-WmCx{K`~%f}L6OANUiro9GEzc!%!s^8LcO)_$60(@8x#*ZRByq|J^d zx(=?)LZg;@dD~XyWJ6#!v$|RG3s`*qp&drc{(){BH17WCq6I(ra_$Ok%H<~abQt@{ zX|bzH0~065T05yPe817IoL(P@Iq}?7o?(M>-zgwhVC>h3eT@+(kB zMoFB(c?C8*(&MrMOo7XfK0)srO9u4FXLdEMSz+YO;EdXZ-w)T7?c5e}o1_5J-bFzu ziz2%FH)o788qw&lG>PaQ<+zvc{X3DP|3Bw=nEF)8Ca*zC-;>vPQSVFnbe>n&kyMsV z0j6{&!J3{O6uA+DH@t>Rn0GO<;blv_6MS71VYDqrks{1K-S~#PCcFR>9-C)@Jrn** zHYH5RHQ`bCTOl%nn?_la1}`_ru&&MRkhdQ^$x(Rko0GQ?ghrVF!>6BJHRFp=)b>&A M(L0DG#m`>+4|)by=>Px# literal 0 HcmV?d00001 diff --git a/odex25_mobile/odex_web_app/models/__pycache__/mail_thread.cpython-38.pyc b/odex25_mobile/odex_web_app/models/__pycache__/mail_thread.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d4320ff51cde8fb030df3b0de6e4e28a8024b5af GIT binary patch literal 1125 zcmah|J8#rL5Z?9LKIa|@ga|5H(D6721Pvk)QHW@g2$9fOLaV*oy~M_^?D}%a>8LI! z9ls$d^K0n0rJ|;zV%B!ZQ8ChtcRcT%Z$EZAO$79P|LbtoLFk83=E(--4X}Cyf+LP& zl;8j-R$wJ0AQ-(v+~VX6aU!h9o{_-e_8#gwKfxb$iFSvfh`G{kl5r6$O;{cdjBTEv za!G0xJ^}@pqrl=gAi}kp-bx=4Ka7Pd*;n>;GQEJ1r4ax(9Ind#`}j-Fg|G| zaPAeA(k_)mD8&xM0M}*A$D9ps0xK896h#P<;oUCQR>(m*$1EO+DBhXl6x?A)4ZEcn^Jg@P$A$DWjb_nncZuR?#jxmliMo9la_R;hx;xiM6&i zan=k?9&T=IZ*RSQ^7ubAX-T1aIg>>y?82 zkZVVcav`Mwjc(VwrFo_b-MaAt#Pwx1RziCs$>Z!)h~TyoDW_>xgu`&aiZDxcLyh{8 z7(m5-#!qz%_DSoMU0d5q#6#_V9y58Mv{z?Wx>295uwI~a5HqDHEny-Tpewly!t7A3 zfI!wFzJ;53xeb8BHeSSc$>iSLUgt;XdfKBjWr?6vH)#ziIJYT18nL*xcr;88g$xU6 z_OBaK6eF3cvas&dEe%8*U$ 6 + + def is_valid_password(self, password): + return len (password) > 5 + + def is_valid_location(self, location): + try: + lat , lat = [float(x) for x in location.split(',') if x.replace('.','',1).isdigit()] + if lat and lat: + return True + except : + pass + return False + + def is_valid_email(self, email): + return re.search(regex, email) + + def is_valid_phone(self,phone): + try: + if not phone.startswith('+'): + phone = '+'+phone + number = phonenumbers.parse(phone, None) + isvalid = phonenumbers.is_valid_number(number) + return isvalid + except : + pass + return False + + def get_server_error(self,e,user): + x= False + if len(e.args) == 2: + x, y = e.args + x = re.sub('\n', '', x) + else: + x = e.args + text = "" + if user.lang == 'en_US': + text =(_("contact admin or edit it Manually")) + else: + text = "الرجاء التواصل مع مدير النظام او استخدام الموقع الالكترونى" + message = "%s, %s" % (x,text) + return message + + + def create_token(self, user): + try: + exp = datetime.datetime.utcnow() + datetime.timedelta(days=7) + payload = { + 'exp': exp, + 'iat': datetime.datetime.utcnow(), + 'sub': user['id'], + 'lgn': user['login'], + } + token = jwt.encode( + payload, + SECRET_KEY, + algorithm='HS256' + ) + # payload = jwt.decode(token, SECRET_KEY ,algorithms='HS256') + print(token) + self.save_token(token, user['id'], exp) + + return token + except Exception as ex: + _logger.error(ex) + raise + + def save_token(self, token, uid, exp): + request.env['jwt_provider.access_token'].sudo().create({ + 'user_id': uid, + 'expires': exp.strftime(DEFAULT_SERVER_DATETIME_FORMAT), + 'token': token, + }) + + def verify(self, token): + record = request.env['jwt_provider.access_token'].sudo().search([ + ('token', '=', token) + ]) + + if len(record) != 1: + _logger.info('not found %s' % token) + return False + + if record.is_expired: + return False + + record.set_env(request.env) + return record.user_id + + def verify_token(self, token): + try: + result = { + 'status': False, + 'message': None, + } + + record = request.env['jwt_provider.access_token'].sudo().search([ + ('token', '=', token) + ]) + + if len(record) != 1: + result['message'] = 'Token not found' + result['code'] = 497 + return result + + if record.is_expired: + result['message'] = 'Token has expired' + result['code'] = 498 + return result + + payload = jwt.decode(token, SECRET_KEY ,algorithms='HS256') + + uid =self.verify(token) + user = uid + request.session.uid = user.id + request.session.login = user.login + request.session.session_token = user.id and security.compute_session_token( + request.session, request.env + ) + request.uid = user.id + request.disable_db = False + request.session.get_context() + + # Set user's context + user_context = request.env(request.cr, request.session.uid)['res.users'].context_get().copy() + request.session.context = request.context = user_context + request.env.user = uid + # uid = request.session.authenticate(request.session.db, uid=payload['sub'], password=token) + if not uid: + result['message'] = 'Token invalid or expired' + result['code'] = 498 + return result + + result['status'] = True + return result + except jwt.ExpiredSignatureError as ex : + result['code'] = 498 + result['message'] = 'Signature has expired' + _logger.error(traceback.format_exc()) + + except (jwt.InvalidTokenError, Exception) as e: + result['code'] = 497 + result['message'] = 'Token invalid or expired' + _logger.error(traceback.format_exc()) + return result + + def refresh_token(self, token): + try: + result = { + 'status': False, + 'message': None, + 'code':200 + } + record = request.env['jwt_provider.access_token'].sudo().search([ + ('token', '=', token) + ]) + + if len(record) != 1 or not record: + result['message'] = 'Token not found' + result['code'] = 497 + return result + + payload = jwt.decode(token, SECRET_KEY ,algorithms='HS256') + user = request.env['res.users'].sudo().search([('id', '=',payload['sub'])], limit=1) + if not user: + result['message'] = 'Token for user not found' + result['code'] = 497 + return result + + request.env['jwt_provider.access_token'].sudo().search([ + ('token', '=', token) + ]).unlink() + result['status'] = True + result['code'] == 200 + result['data'] = {'token':self.create_token(user)} + return result + except jwt.ExpiredSignatureError as ex : + request.env['jwt_provider.access_token'].sudo().search([ + ('token', '=', token) + ]).unlink() + result['status'] = True + result['code'] == 200 + result['data'] = {'token':self.create_token(user)} + _logger.error(traceback.format_exc()) + + except (jwt.InvalidTokenError, Exception) as e: + result['code'] = 497 + result['message'] = 'Token invalid' + _logger.error(traceback.format_exc()) + return result + + + def validate_token(self, token): + try: + result = { + 'status': False, + 'message': None, + 'code':200 + } + record = request.env['jwt_provider.access_token'].sudo().search([ + ('token', '=', token) + ]) + + if len(record) != 1 or not record: + result['message'] = 'Token not found' + result['code'] = 497 + return result + + payload = jwt.decode(token, SECRET_KEY ,algorithms='HS256') + user = request.env['res.users'].sudo().search([('id', '=',payload['sub'])], limit=1) + if not user: + result['message'] = 'Token for user not found' + result['code'] = 497 + return result + + uid =self.verify(token) #request.session.finalize() + # uid = request.session.authenticate(request.session.db, uid=payload['sub'], password=token) + if not uid: + result['message'] = 'Token invalid or expired' + result['code'] = 498 + return result + result['data'] = {} + return result + except jwt.ExpiredSignatureError as ex : + request.env['jwt_provider.access_token'].sudo().search([ + ('token', '=', token) + ]).unlink() + result['message'] = 'Token invalid or expired' + result['code'] = 498 + _logger.error(traceback.format_exc()) + return result + + except (jwt.InvalidTokenError, Exception) as e: + result['code'] = 497 + result['message'] = 'Token invalid' + _logger.error(traceback.format_exc()) + return result + + +validator = Validator() diff --git a/odex25_mobile/odex_web_app/views/attendance_zone_config_view.xml b/odex25_mobile/odex_web_app/views/attendance_zone_config_view.xml new file mode 100644 index 000000000..5d64aff2f --- /dev/null +++ b/odex25_mobile/odex_web_app/views/attendance_zone_config_view.xml @@ -0,0 +1,38 @@ + + + + + + + + + mobile.res.config.settings.view.form + res.config.settings + + + +
+

WebMobile Configuration

+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
\ No newline at end of file diff --git a/odex25_mobile/odex_web_app/views/hr_employee_view.xml b/odex25_mobile/odex_web_app/views/hr_employee_view.xml new file mode 100644 index 000000000..f0442f300 --- /dev/null +++ b/odex25_mobile/odex_web_app/views/hr_employee_view.xml @@ -0,0 +1,21 @@ + + + + + + + employee.web.mobile.inherited.form + hr.employee + + 200 + + + + + + + + + + + \ No newline at end of file