From a2e4beba2765be45951755e74298397aacc28a0b Mon Sep 17 00:00:00 2001 From: jopster Date: Mon, 7 Jul 2025 08:22:23 +0200 Subject: [PATCH] Sicherung --- ToDo.txt | 5 + __manifest__.py | 12 +- controllers/__init__.py | 1 + .../__pycache__/__init__.cpython-311.pyc | Bin 341 -> 399 bytes .../dss_nextcloudwidget.cpython-311.pyc | Bin 0 -> 9312 bytes ..._screendesigner_controller.cpython-311.pyc | Bin .../dss_screenview_controller.cpython-311.pyc | Bin 1601 -> 1603 bytes controllers/dss_nextcloudwidget.py | 136 ++++++ controllers/dss_screenview_controller.py | 4 +- models/__pycache__/dss_ads.cpython-311.pyc | Bin .../dss_advertisefields.cpython-311.pyc | Bin 22034 -> 21983 bytes ..._advertisefields_templates.cpython-311.pyc | Bin 11052 -> 11360 bytes .../__pycache__/dss_contract.cpython-311.pyc | Bin 74040 -> 87298 bytes .../dss_importinvoicelist.cpython-311.pyc | Bin 13112 -> 13370 bytes .../__pycache__/dss_m2mmail.cpython-311.pyc | Bin .../__pycache__/dss_projects.cpython-311.pyc | Bin 30428 -> 41351 bytes .../__pycache__/dss_settings.cpython-311.pyc | Bin 15639 -> 17460 bytes .../dss_systemtypen.cpython-311.pyc | Bin 5759 -> 6027 bytes .../__pycache__/dss_trigger.cpython-311.pyc | Bin 64918 -> 64918 bytes .../dss_web_contracts.cpython-311.pyc | Bin 20063 -> 20424 bytes models/dss_advertisefields.py | 2 +- models/dss_advertisefields_templates.py | 2 + models/dss_contract.py | 244 +++++++++- models/dss_importinvoicelist.py | 10 +- models/dss_projects.py | 156 +++++- models/dss_settings.py | 25 +- models/dss_systemtypen.py | 4 +- models/dss_web_contracts.py | 10 +- security/ir.model.access.csv | 1 + static/src/js/dss_nextcloudwidget.js | 448 ++++++++++++++++++ static/src/js/dss_nextcloudwidget_short.js | 251 ++++++++++ static/src/js/dss_screenview_archparser.js | 1 - static/src/js/dss_screenview_controller.js | 73 ++- static/src/js/dss_screenview_injector.js | 0 static/src/js/dss_screenview_model.js | 118 ++++- static/src/js/dss_screenview_register.js | 47 +- static/src/js/dss_screenview_renderer.js | 130 ++--- static/src/js/dss_tagclick.js | 47 ++ static/src/js/kanban_button.js | 21 + static/src/scss/dss_nextcloudwidget.scss | 276 +++++++++++ static/src/scss/style.scss | 35 +- static/src/xml/dss_nextcloudwidget.xml | 122 +++++ static/src/xml/dss_nextcloudwidget_short.xml | 7 + static/src/xml/dss_screenview_controller.xml | 6 +- static/src/xml/dss_screenview_renderer.xml | 21 +- static/src/xml/form_button.xml | 12 +- static/src/xml/list_renderer.xml | 8 - temp/list_renderer.xml | 11 + temp/temp.temp | 0 temp/temp_in.temp | 234 +++++++++ temp/temp_set.temp | Bin 0 -> 4209815 bytes views/dss_advertisementfields.xml | 51 +- views/dss_advertisementfields_templates.xml | 1 + views/dss_contracts.xml | 99 +++- views/dss_projects.xml | 99 +++- views/dss_systemtypen.xml | 22 +- views/dss_web_contracts.xml | 5 +- 57 files changed, 2488 insertions(+), 269 deletions(-) create mode 100755 ToDo.txt mode change 100644 => 100755 controllers/__pycache__/__init__.cpython-311.pyc create mode 100755 controllers/__pycache__/dss_nextcloudwidget.cpython-311.pyc mode change 100644 => 100755 controllers/__pycache__/dss_screendesigner_controller.cpython-311.pyc mode change 100644 => 100755 controllers/__pycache__/dss_screenview_controller.cpython-311.pyc create mode 100755 controllers/dss_nextcloudwidget.py mode change 100644 => 100755 controllers/dss_screenview_controller.py mode change 100644 => 100755 models/__pycache__/dss_ads.cpython-311.pyc mode change 100644 => 100755 models/__pycache__/dss_advertisefields.cpython-311.pyc mode change 100644 => 100755 models/__pycache__/dss_advertisefields_templates.cpython-311.pyc mode change 100644 => 100755 models/__pycache__/dss_m2mmail.cpython-311.pyc mode change 100755 => 100644 models/__pycache__/dss_settings.cpython-311.pyc mode change 100755 => 100644 models/__pycache__/dss_trigger.cpython-311.pyc create mode 100755 static/src/js/dss_nextcloudwidget.js create mode 100755 static/src/js/dss_nextcloudwidget_short.js mode change 100644 => 100755 static/src/js/dss_screenview_injector.js create mode 100755 static/src/js/dss_tagclick.js create mode 100755 static/src/scss/dss_nextcloudwidget.scss create mode 100755 static/src/xml/dss_nextcloudwidget.xml create mode 100755 static/src/xml/dss_nextcloudwidget_short.xml delete mode 100755 static/src/xml/list_renderer.xml create mode 100755 temp/list_renderer.xml create mode 100755 temp/temp.temp create mode 100755 temp/temp_in.temp create mode 100755 temp/temp_set.temp diff --git a/ToDo.txt b/ToDo.txt new file mode 100755 index 0000000..aa2a7af --- /dev/null +++ b/ToDo.txt @@ -0,0 +1,5 @@ +/* +** TODO : +** WISH : +** BUG : Screenview bring tFehler wenn ein Filter eingetragen ist - Ursache : domains[0][2] ist dann anders belegt - im dss_screenview_model.loaddata(data) +*/ \ No newline at end of file diff --git a/__manifest__.py b/__manifest__.py index e01c41b..fc89863 100755 --- a/__manifest__.py +++ b/__manifest__.py @@ -70,12 +70,18 @@ 'DigitalSignage/static/src/js/dss_screenview_renderer.js', 'DigitalSignage/static/src/js/dss_screenview_register.js', 'DigitalSignage/static/src/js/dss_screenview_model.js', + 'DigitalSignage/static/src/scss/dss_nextcloudwidget.scss', + 'DigitalSignage/static/src/xml/dss_nextcloudwidget.xml', + 'DigitalSignage/static/src/js/dss_nextcloudwidget.js', + 'DigitalSignage/static/src/xml/dss_nextcloudwidget_short.xml', + 'DigitalSignage/static/src/js/dss_nextcloudwidget_short.js', 'DigitalSignage/static/src/js/form_label.js', + 'DigitalSignage/static/src/js/dss_tagclick.js', 'DigitalSignage/static/src/js/form_button.js', 'DigitalSignage/static/src/js/kanban_button.js', - 'DigitalSignage/static/src/xml/form_button.xml', 'DigitalSignage/static/src/js/dss_klistmanager.js', 'DigitalSignage/static/src/js/screenDesignerViewReload.js', + 'DigitalSignage/static/src/xml/form_button.xml', 'DigitalSignage/static/src/xml/form_label.xml', ], 'web.assets_common': [ @@ -88,7 +94,11 @@ 'DigitalSignage/static/src/js/dss_screenview_injector.js', ], 'web.assets_qweb': [ + 'DigitalSignage/static/src/xml/kanban_button.xml', ], }, 'license': 'LGPL-3', } + +##https://www.odoo.com/documentation/16.0/developer/reference/frontend/owl_components.html +## \ No newline at end of file diff --git a/controllers/__init__.py b/controllers/__init__.py index cbdf303..fdcbce0 100755 --- a/controllers/__init__.py +++ b/controllers/__init__.py @@ -4,3 +4,4 @@ from . import main from . import dss_screendesigner_controller from . import dss_screenview_controller +from . import dss_nextcloudwidget diff --git a/controllers/__pycache__/__init__.cpython-311.pyc b/controllers/__pycache__/__init__.cpython-311.pyc old mode 100644 new mode 100755 index 531e773691d2f944e650fcbaec98c8380b3fbbf5..f36fdcd3729836947b2c306ad89d32c363c00373 GIT binary patch delta 141 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 vm!o`(JJ#rzc literal 0 HcmV?d00001 diff --git a/controllers/__pycache__/dss_screendesigner_controller.cpython-311.pyc b/controllers/__pycache__/dss_screendesigner_controller.cpython-311.pyc old mode 100644 new mode 100755 diff --git a/controllers/__pycache__/dss_screenview_controller.cpython-311.pyc b/controllers/__pycache__/dss_screenview_controller.cpython-311.pyc old mode 100644 new mode 100755 index 4a960b2b46bbccc0e61703526f699034280ca9d2..5d97a0e1286d299c11ef64787bf64189f5846129 GIT binary patch delta 50 zcmX@ebC`#BIWI340}vGDF=d?J$h(`FQE>7}=1q+JlWSQ1G9H{<#p=xZfsFykKfu}u E0CF-9NB{r; delta 48 zcmX@ibC8F3IWI340}!wZ{Y*cxk#{#Uqrl{o%$pebCfBh1W!yixiq)C<0R!LUL#&Me DT1XF4 diff --git a/controllers/dss_nextcloudwidget.py b/controllers/dss_nextcloudwidget.py new file mode 100755 index 0000000..d484658 --- /dev/null +++ b/controllers/dss_nextcloudwidget.py @@ -0,0 +1,136 @@ +import io +import easywebdav +import json +from odoo.http import Controller, route, request +from webdav4.client import Client +from webdav4.fsspec import WebdavFileSystem +from typing import ( BinaryIO ) +import logging +import base64 + +_logger = logging.getLogger(__name__) + +class MyController(Controller): + + @route('/my/webdav/files', auth='user', type='json') + def list_files(self,path): + client = WebdavFileSystem("https://cloud.logumedia.de/remote.php/dav/files/OdooDav/", auth=("odooClient@logumedia.de", "lm2020#OdooDav")) + files = client.ls(path, detail=True) + #client.upload_file("Gorilla.jpg", "Photos/Gorilla.jpg") webdav = easywebdav.connect( + _logger.info("webdav : "+str(files)) + return files + # return [{'name': f.name, 'size': f.size} for f in files] + + @route('/my/webdav/upload', auth='user', type='json', methods=['POST']) + def upload_file(self, path, filename, filedata): + client = Client("https://cloud.logumedia.de/remote.php/dav/files/OdooDav/", auth=("odooClient@logumedia.de", "lm2020#OdooDav")) + filedata = filedata.split("base64,")[-1] + _logger.info('Als Byte : '+str(bytes(filedata, encoding='utf-8'))) + infile = open('/opt/odoo/addons/DigitalSignage/temp/temp_in.temp', 'wb') + filedataStr=str(filedata) + _logger.info('Als String : '+str(filedataStr)) + decoded_data = base64.b64decode(bytes(filedata, encoding='utf-8')) + try: + decoded_data = base64.b64decode(filedataStr) + except base64.binascii.Error as e: + if 'Incorrect padding' in str(e): + filedataStr += b'=' * (4 - len(filedataStr) % 4) + decoded_data = base64.b64decode(filedataStr) + infile.write(decoded_data) + infile.close() + _logger.info(str(f"{path}/{filename}") + str(base64.b64decode(decoded_data))) + infile = open('/opt/odoo/addons/DigitalSignage/temp/temp_in.temp', 'rb') + client.upload_fileobj(infile, f"{path}/{filename}") + _logger.info(f"File {filename} uploaded to {path}") + return True + + @route('/my/webdav/getfile', auth='user', type='json') + def get_file(self,path,filename): + client = Client("https://cloud.logumedia.de/remote.php/dav/files/OdooDav/", auth=("odooClient@logumedia.de", "lm2020#OdooDav")) + #files = client.cp('dav://'+path+'/'+filename,'/opt/odoo/addons/DigitalSignage/temp/'+filename) + infile = open('/opt/odoo/addons/DigitalSignage/temp/temp.temp', 'wb') + files = client.download_fileobj(path+'/'+filename,infile) + infile = open('/opt/odoo/addons/DigitalSignage/temp/temp.temp', 'rb') + indata = infile.read() + _logger.info("webdav : "+str(indata)) + encoded_file = base64.b64encode(indata) + #client.upload_file("Gorilla.jpg", "Photos/Gorilla.jpg") webdav = easywebdav.connect( + _logger.info("webdav : "+str(encoded_file)) + return encoded_file + # return [{'name': f.name, 'size': f.size} for f in files] + + @route('/my/webdav/makefolder', auth='user', type='json', methods=['POST']) + def make_folder(self, path, folder_name): + client = Client("https://cloud.logumedia.de/remote.php/dav/files/OdooDav/", auth=("odooClient@logumedia.de", "lm2020#OdooDav")) + folder_path = f"{path}/{folder_name}" + try: + client.mkdir(folder_path) + _logger.info(f"Folder {folder_name} created at {path}") + return {"success": True, "message": f"Folder {folder_name} created at {path}"} + except Exception as e: + _logger.error(f"Error creating folder {folder_name} at {path}: {str(e)}") + return {"success": False, "message": f"Error creating folder: {str(e)}"} + + @route('/my/webdav/deletefile', auth='user', type='json') + def delete_file(self,filename): + client = Client("https://cloud.logumedia.de/remote.php/dav/files/OdooDav/", auth=("odooClient@logumedia.de", "lm2020#OdooDav")) + #files = client.cp('dav://'+path+'/'+filename,'/opt/odoo/addons/DigitalSignage/temp/'+filename) + infile = open('/opt/odoo/addons/DigitalSignage/temp/temp.temp', 'wb') + files = client.remove(filename) + return True + # return [{'name': f.name, 'size': f.size} for f in files] + + + @route('/dss/getscreendata', type='json', auth='user') + def getscreendata(self, Domain): + # Tu was mit den Daten + screendef =[] + return screendef + + @route('/dss/setbtnfile', type='json', auth='user') + def setbtnfile(self, contract,filename): + contract = request.env['dss.contracts'].search([('id', '=', contract)]) + if len(contract.werbe_feld_selected) == 1: + client = Client("https://cloud.logumedia.de/remote.php/dav/files/OdooDav/", auth=("odooClient@logumedia.de", "lm2020#OdooDav")) + #files = client.cp('dav://'+path+'/'+filename,'/opt/odoo/addons/DigitalSignage/temp/'+filename) + infile = open('/opt/odoo/addons/DigitalSignage/temp/temp_set.temp', 'wb') + files = client.download_fileobj(filename,infile) + infile = open('/opt/odoo/addons/DigitalSignage/temp/temp_set.temp', 'rb') + indata = infile.read() + encoded_file = base64.b64encode(indata) + _logger.info('JS : SetBtn File'+str(contract)) + for feld in contract.werbe_feld_selected: + feld.btn_image = filename + feld.btn_image_bin = encoded_file + res = True + else: + res = "MULTI/" + ids = "" + for feld in contract.werbe_feld_selected: + res += feld.feldname + ids += str(feld.id) + if feld != contract.werbe_feld_selected[-1]: + ids += ':' + res += ':' + res += "/"+ids + return res + + @route('/dss/setbtnfilefeld', type='json', auth='user') + def setbtnfilefeld(self, contract,filename,feldid): + feldids = feldid.split(':') + client = Client("https://cloud.logumedia.de/remote.php/dav/files/OdooDav/", auth=("odooClient@logumedia.de", "lm2020#OdooDav")) + #files = client.cp('dav://'+path+'/'+filename,'/opt/odoo/addons/DigitalSignage/temp/'+filename) + infile = open('/opt/odoo/addons/DigitalSignage/temp/temp_set.temp', 'wb') + files = client.download_fileobj(filename,infile) + infile = open('/opt/odoo/addons/DigitalSignage/temp/temp_set.temp', 'rb') + indata = infile.read() + encoded_file = base64.b64encode(indata) + for feld_id in feldids: + feld = request.env['dss.advertisefields'].search([('id', '=', feld_id)]) + feld.btn_image = filename + feld.btn_image_bin = encoded_file + return True + + + + \ No newline at end of file diff --git a/controllers/dss_screenview_controller.py b/controllers/dss_screenview_controller.py old mode 100644 new mode 100755 index bb963ab..1a0e54e --- a/controllers/dss_screenview_controller.py +++ b/controllers/dss_screenview_controller.py @@ -6,6 +6,7 @@ import os import os.path import base64 + import logging from odoo.http import request _logger = logging.getLogger(__name__) @@ -19,4 +20,5 @@ class screenViewController(http.Controller): _logger.info('doch hier') request.env.ref('DigitalSignage.DigitalSignage.action_dss_contracts').run() print("Empfangen vom Frontend:", some_data) - return {'status': 'OK', 'message': 'Daten verarbeitet!'} \ No newline at end of file + return {'status': 'OK', 'message': 'Daten verarbeitet!'} + diff --git a/models/__pycache__/dss_ads.cpython-311.pyc b/models/__pycache__/dss_ads.cpython-311.pyc old mode 100644 new mode 100755 diff --git a/models/__pycache__/dss_advertisefields.cpython-311.pyc b/models/__pycache__/dss_advertisefields.cpython-311.pyc old mode 100644 new mode 100755 index 1bf332c5fa85d3717f2a6789b26dd49de9847611..7f6a15387ec98d2172b96aacb554d6b32d1509c7 GIT binary patch delta 232 zcmbQVhVlMtM&9MTyj%=G@T2x`y8K4o24`(nAdeY{Kc@kS=?patE)21{wTv~4SwKDn z6v?JAmH^dEKH#i0d4Up(R}Eu46HKy4Erkh%ub09Q%%I8aS0$E`SdwU!nVXoNs$YU|{Ao zxuNHFwD^SC74P_q?g>}i6E5l{Y<}!=mXYzn=8bNtyo?Vgi-+hjDr|NOIl~A5(AGX> delta 233 zcmcb=nsL$^M&9MTyj%=G@cQ8Qbmfh_4bGEu5z zQ3*4Q&A^bwG>leBCKSGN3PUi1CbM4^YiUWE zu7#T>YtcoZ;-YgP;yjSJ#g>wqoS%|9S;HliF>-R9i@d^JJ(DYXc00XuwO!p-F;(NtWdSt*{!FEY{3x8D;@Lg%L7q7{M^5P#m9fv9#$b;xRVJ5hJ6)$D{NxaOTy&2<#js7lonN~y64 zy0BL><<&qaM`6W13=?5s%N&4i=)v%=c^y6|{1isWSqu|)_Z+~5TYw2Y(R0Q|YROj8 zY7?dIl~vYNP?3e@T+QTNX}b+KRQgxjEx$9uEH~n&G7hM2frp~nRjFNsDnsvB4rSQl zWA&2y_Y~G$-38b|)K*G8NYuTQdWfj|D78~ovu#9Gy6#f#djGnp1M8yNiz)oE!rx1x z*Ckb_l$+v%-6}W5OOh4JuS#`L)Zqe!Jf&4xB{!g&dx*;IQKdR5s#lfjqNqOD?N({Z zM`8WNW@x3<0tV!>LN-G=7$j$Tm}Dq_Ktn_gQ0fs`J>CP4USmSN@ED%^1ntz?mjs8e zG4uFBEv#NDCe;5&eB`hAzzyaS2n~)f&Y*P9@K%?#!pWKGSom^MIER0dLi0o_7Mq!z z6~-^7jM%p`H4$5WF_Vf%YP&>XGATHX1amjz2*RjOV`Z?E%2jT)+W%2URhi1 z9oRFkjS2BQJ043!B7&ILO~xk@d3|_#T8Kl@k1DI(Ac*|cXMiX@uD%Vx5NhYnVwXo; zD9|hCv%T@8Ag9vRk1Lz-pIB53BG%?IieuPJJS9)3v;E!P&zjU{rI(>Xr#p3i*XGdZ2@V^J)gyL2VTr{DAG4}2QOq?`6P8*s^^j&FKF l_CAUFo%LWz5}g-;?=%4B?~M9<*L1&Tg~WdRgAlFQ@E1BbdU5~& delta 1624 zcma)+%TE(Q7{F)RvV94mO5ZIlg+8IsQmPaz6k6y5L}HDS3LbQ7BcL%2iCj!IavC;c4=`TzqVa^97_T}z1s0}jWRjU|fAjmk`DVJapXW9&v^;BW z)&SDp&ipQwZ7*8PGPun9WGfJVlRGO087OKNwSpitWI?J;b5#aFjr4je7?0qXsMEF= zgRIC#Gs#IN(swhNsb$&wjqLa#cLL8UIIYOq1*Wqxrh`&DL!C7KHHjB@&>=9TgPK}= zM`6cYQ!9S1XsdKk=GtGq3#mwr?&{$hI~mI(X>~D`c#)6f@-w;I)bi9<8~~sMP+$Y1 zZq!5MU{yg5hxB?Ycn`|d?cIQM+dgKoK1%%z4N)3kXqeJ&hV}~--{m;3cpMS3QFX&{ z1nZG_l(QRq>9ZIStsf2k#OV*@IVpKyAsVa2HO{UJGt~^z8v7X^7c**YT=xojgNRntX4wCXF1X@Of(wOyGOgD4E!@Itlq; z4Z%EC+pfS{{Icy4gwyzseThsgwHLSysjHmF#~eY+M`iZrO0iTdmoEu$UKkdH;WD3d z?8x9%9CJnB8~%pN0{QLEcTFV41OImp3CZeW-H_IKB#qNJjniDDFNZ!qr6$UAO4{aV(k6D3v~I5VX79bopw#Ja7iD~4;qb0xgq;(sb4Mf68xHvoM z&S=hHaW%wAGhuO!J+nKjIg7>P?b+Qq%{eTdV9)I~HXFP1n)8C`{0ZqqdqH<$b0N#q z+Kal2n~PbT*h`vAq!Mdh=QN3g$t00jV>(Ur#zsBeOJc}$+Cg$nGf-OmSZ6*&B$9{W zPy95`l1f%exTM1pF4>yYSxV26w3srEY?hdG^flsC#5OstJY68&IM+l1%H?*O)!~Ae zcAK?@v+S$4*{z!oI$hT8)(}SwRl&rkdvb;!kUk~XX+yc(vY{kV0(xDHK~c*sAF88w z#iXhYVTu(rPoY~d9~za|nHt8{b1R4H9+HCW@F#wn7ciO#?^T>;Xf^#Zrhu%bxvE_C zT1<;&=mwRcq#=?H^H{`qWQNi}Q9_TZQq2=EQGK>#a+xTKrBTwCaqGDCoQB&lwDBPY zv_V0?RvFS7xlK^t=7(f}fj{xnTuYx*<&ov|T~!{n$7-oQHksZD;d}_sLf9OeT)8re z|Bg_vhPkYYqG+ayxIB6Rc+8K}(mz1h452(OnKaNe$TeqA^N_7_JriKr_TWB!?Eu=?1ldeifHOHq#_^0og+5s0-+eYAx+hr_c}8 zT4KYQKoc}t(uuK>?NR)?I6ID4RR8Uu9|5;!ny4wD4?)=;ESt0|O4?q=`-(=nq%72D z++yxJ;KxVt+rvsmsGOV&TG2POFW8Fsc!Os@cVLQ=d!y<+$f(>={4G&b*K_?t15q=* zh0%o97Vi{;i(++X(vVmgMP#wv2y8-2Bq_n5xCzs=EFrZrLi3#nO{lAy zJEHg;X65!LCnTl#t={LE;hk>qp(L{ppOm}M9Fy_otIb;!;7@kl9-+3 zWbw$BaSw8faRn*PBxchDt&(KYR}-_zKKfB&LF0aw7a7oFQ5(nsmLFMu_eJF&jH>Ym zSYxW2VXqIhs$IhU@FZdF?PocWF+I+(15w!fncO#sSb9dA9peecU)5$O9b)mwD4t+E zZj9pb0K?wIuo3xBGVFI4Hqz@qVc5e_98MuN?Yj&cIRGBy9)fY^>5k;wlAm&?F&;H8 zx3ChCQ8>f6d!zXL46&Z?G3>3Op5lJS-O6Us#I5+Y=(60y+z`+`5;Y@lXLR9y=YBqE z-Q6*flZX2Scb0n;rdK@oZ$rZmDPcc^Kk?IiCnJt@-eZ&4-4#XgIC2{LWmJb8nOG_J z1b2>mlGW+?)uc*~MzMTqQl)oCQJkN$QW{0|^i))|J0->w8QWiT&u|x*V9!pH>t4ps z80sAEH_*`MLaRO4Vc(Cc^Z7}#4@ObEz-c^F^w_be%3hqJviqWFUYdgD2T?R5Q_#@P zRITD=%%GmsR8ORZzny~k{wRk34io2nJMh6d=5^{KfL9>2rfsL_0ow(ASX zL-aO)Kc&CW7tqCNpt9&w$Qk+%NZkyn>1ioRpb3JC7?Jrhu59`m~!kQ*7Mmh=Mh82xT~ zLDJ)lA|m_yY=8M>6!rs#eIg3`XNEl&h5e9WpNztO#IV1L!u|!Z^PXbZ$jT|>{t7)9 ztpc5oD*G|x_;eKZ6U4^+TEtSDAv@`rD2l7B&I=41+4uj(eF}9(@4nA6s>r7G87upn zDD3Cl--rGYHQvuLnn;_jasQ;tVp8WkA651XR_nwozzd8fa;|-ewJv;-VZ-||oP%GT zB;40i^wdjH<-fu5o)LzPwD`Y{5V+G#Zu85GF5G6IBJd|2niti=-vXVi^WQ^r3ut5$ z&vU@Y4@({bf`{QRLnLf|g%x{sTrmZGII|$;HIW^CBeSgabtoEpSkmm9L>o7$3U#E4 zU=6~gQZbWCY3RdQd9*DnnZBK+B?%Y@d|LbwRR zuOM6u;nxr@f$$p$mqLh}!m`64Rx^rL3$aSD@f( zsg%qdK5wig%d()E0oi(MACKk$J{!Op1)qoULIimB@VN+#2#Nr})k7jN8hJ4PNCG-) z$=A_M`T3-RUM`qLPv&QW0qEEHr{~OsY|}Va0Yz(%y@zMa0&<(9t!FYf1LOAdf>JUM zxUFKG40K810GX+DofT>cC3kDY*j#YwR)5;%V-RaM3_w7qrgW>gwao ztfgn9d@-;LC;{2SbK!2I7pIkywUn3^0h=Wzp9ZO%Xdi<ChqHjn2mf- zCkSSe1*P z4i|4}b>->CEg4N=CAc_Ec;X zsaXTs=x;0P$2D{t&8#dHNv(84Wrfr|jb5!tp&ga+vE~+w(>k}xosAfEi_2k#AvPa~ zN*|0$?-$dtA^HybNrhg8Db^D=R@R8(uof*E9;uX(@?;QxtCfc~w;S87UC*6&aJF_= zA8HYk(MC%*{cY94bhp0N3gh45=ySAN9maiDzQtyBSslZQIUIq>+Br8Pc`0y!v0)u$ zg*XiC_vfCC=_JU@jWn~#LmUU;+y$dsk zPgc*BlU?-9#Z>1`T48gv*sTsul*?jw4cnK@Amny>Y-vIAawrs@uUO@6^wp)sq>_HN zlv7RzL?DZnt;ia7FH4baWP1h)$T`bFr%4@IO92_o6>&jPED~ex;ce|UM`U*iB)9Qa zTf3#jYPR&bddx0cx0MH7xvn%H*94D7rhp2wkhc?eC}U{jDWsMag>=)3N>Vd?^NM}~ zOJ258NyrHrw9V-2PQHz% ztTQT^s5jE;b+Z&4@cEQFHtOk3>*g{^!@QoNcdS>hnFqy9N*;~#{I3u^g+SyFv4GCP ziMyd0dbS6aG}OaC4LMG*6k`_$JDI-;>MNjs0sCz?%Kj{}87F(?hBp>W#Xlj;|Jzv~ zqRTc;BYm`U<3<>|r#8+Z#V;fK$XTPoB--`)3Dc44H&u}Vx^`0^lzVp5>xA4zk8U|M zKT^61%lrTV?&bk)XrmG*nT_LD0vb*^agNc|O+_$MdYY=0hoO@2YjF;Y z(q}gr;BY&-%tV)*NTBDPMYQx}Ha%-i?x>UOUOYhhNkseNWMERnnq{1PKJ-WW+3ic{bq5Ma486g*h~Bz0g}$~^Nfh+Z z_x1Eg)?|9f8bkkZAea8vNu5m68B58#Gd9WkCFa;rE&XyxVUMy8Cjpm>dc8Y6cnFwn zoVm5b0{fJCwz1l{+{|ADI{q#MuOsjw5a@5WXKnirjQSD00l*X&S@o>r12I9K{3WCr zMZm^~9j|X<632iK02n{#XyT3<55-ZywQ@-Et}5{^&8-?=YT4rjV7xLtW)e~tp0f(6 z%U@VNB7a3A)HV6*nuOFQLABMd+Uiqn9o1)Ck*H#4GvKWs)fxP{lG7Czb+dfBS)*Cy zM^+qJF_x9@$Jl7`On>n_U-7(iO8@*-KK30+Eg4Dm88#eIjux2w1y#O+sua#PpSPsbLzd%}Y|`gpE-G(< zWX{@V7LV;d-p)?SaRVi84|GFIdKo{4xtL{Z~hNGFuyJT|E&iypa(MO$u<*tarl)s2kh*$yz?l1rDJtYJ&3`-3!o1- zhb`6vF5`2jK%3wlXH@)qw9-~UKA_ud1%=oL0U2*?V_QH-6O-?=S@(y}HTol44S9#Y zW!u7PcvgEAx&IA;SPvy#X=$Hg`U?bK0f-bX>sm}crj9PL@KO4o-6lFYxWZICH=cS!ff{c52K}O73@B|9IHbm+&~|2tTe3#5nw>s+}I59fTFL@ z##K!?pHRv*G^r<-a;EY#SSqhm7{56^ZyJdykv+Spemo}kV2w!RWlJUra@BWb7&SGJ}M zcO6J1(1z;|RxCoH>sjr*7J72R^?->Gt{t+lGG2!r9<1rZgZXfp#JiW0Idr|-9fQZ^ zdj4<2ue*!L>hD7W4(9NkK^&B?0UJ>ET6l1DyQ(xW#lBK7q38>(Qpk$c`GO1AB{&_5n}IG;6t8uB**fRmLu=grQJ z9^Pf{wY&K|diKVjW`x@pP>K{Lx!6(D>2)_{v3jTx?7ZMnG- zG@{3DF4berUnI0zP*1h+rPh7WLHu3x+Ret0vXpa6aR-}e>^K?2Zc?+Or^|(kyx7x5 zKu4xOzYs;&!?Zzl(;VV%X52$Ng@UERs~!28Xx=qFjlUzFzJAMM(lMOiO~I|{weJ{) zYrZ!k^=!j#iOl-dm|O#33%?el<64N~G$3vrg7pZtB48bjCx|I2GQ;`ZkQGq&IdEU* zw1VCT*;j2!^rRu%1-Rn$GJh~wRmG!edo{`lxkRa!^{Vndzm z3M7Q0V#l&J1T-uGgUg`;x}7%NJsYOtEq5;^W;$}WF*qT!j%lct&L-aBTIvglHuK&m zq&Ze>X(xjH*ecX>nDQzru#_CzONNJAj*UQ};Tb>3m#SJ(L>a$`HXWa@j_lO$(qqT- zJ?}v#{}}=!vcxsV{~f^x2>yZK8iIcU7=Oz3I6Hpb4LU&Zr)*tiAu-N_dV?_WVG?OF#>Iu)M9$(aLAhUp-%}WQ@k>d(ZzH zxtHGe!u&K50fd3LHk;MXnc04P8~xygg~U$_U;L}`7&h)+n)Z@YDfS;d@{&XBI@W8f z|02DXj{-7)y~g%PQ+D+J$0l$d<}6*s05to=U`yoKN-O?>$< zOuu_xo<=?%e){E`Wv~pEyple=>(ylnm5W@JtRe9(`ryST;u`+)V!1j;H?rlGZ7*lN zk|pFd`L^;x9xtRg{V7ggiW4Gq_1kOMnQEYqyuCJuoyb!DO|0rs?9k;93olBl|9us? zo38zREsXk|zfY7-)+!%)C)e~ZYnAl#cNUZH4_EzRZD{e7a(^e8{Nq^j?@)#SRzL3Q zM}PM2T=MAfJMXd*v)<2I5>uEheC9g9#JablD{= zlV139p6W@U!4rV~`On$nDcnsp9~O}3hs!_gkgkoa!+I3vhsY6?B~#3}l@+z0u_<#2 z$-e>+>AFAuWhoh@g@4^Io^)h{Zp@oQFMqOvmVd0Hhc_qDJ)TT@cC#|6pKvj_n)>nK znQA?Ml{S35!Go*(3(d}*`8FJUEz z#3iGRoTGl2HWU1~ zl0z|xq%)2_^FXc+tBwg_MPC#?2rT^eVjUgYQ%1I3& zatU0qnJD~nm+Wp!#{WeP)^C^c|Gse<;~NLP79VOEC#U2wW3daTdhi2A0ZGJ2fWyQo zgM$^k^xwT*>6z%E;8a{}KRgE=KNoCdNG-ks3~>fCgN&E#386Qx-~`~ z@?Exe>z{ctbB((BY&=Nu`C7^;ApuKj$m^^YgUTrSBHK6Rax;lEeKupl$9!Lc3 z4lb~*U1rd(E!|Go?%B?Pt8<)Hf^EcXHwmke{6|P4UXdXdNNBgZf;Np_i>o6L4{7mf zYHt&y#hoEB*lXr)tJ4YB4`#gz>b?O5EOZ+e&<44%yEd}oUxp3OiAxg>|KRDL-+PcY zUeVF9EAjOFmD!$8aZ7*)aFRoku|(bN`@M?G#)1=dr#1-2D!;Mnap$?J^NWRfYy9)p z2*x#DxnGriIlt%xcdAFopX<+``?T)dmh*dr1r7cM4MKjyh*QYl^hVQm-;Uk>9bH0` z-QQ#v^6gkIcQiBic#S`EwojElnwWbaDwdy?6asV(tmzPs~5jdNI-DOEg{1^A!4u<_LLn{dsdAcRpR?U%2_w zLd(U47GF!7uf0oHX!kF)3-h}D^SXt+?j!Ph5{@Lin^*YYE`QOYOGOPAiyB6@_%?0z zZ`vainf*m(A#blg4+Q4kuwLi44uH&Uqyc5d0EAs9ZNy-cbSui$Mz(oSObEp$Y!{ob z9aH(+rC8r+vi_cFzRYre^6X2=%PuA_dm+zP*X?tlI@v?&k#YqDz;vawNlZ0KLBZTA z-GRtv=^o_SEbYL&tx_ANFkt0uQV-B{N_WUuk_F4bmj^T$K%PbB!k}N)Acvqyz5|0U zc^?M*X)za;?#f1Y>ml{Ze9}euFLyC!S5(Z?F!>oKw|%(yw^05igw4>)|DZe*rX_xui*tJhgwR>l^0slBJY zk13+1oVAR%cK5ifrM(@!W$=oLOcUP2G zlotRo(!x`CIk-eR+&b{u!e%N;wGnm_+<9jNW!>zu;GLQKABFZq=Xl(i>nvSXXx4-R zA%5vBb6g!(o`+^v8@HHZr_|%wzL^4y`9=VNvfy@S9%rXP8%EG9+1ld~orvbpb{>db zaiG=Oi|0a>H#ULJZ}xAs37b0o zn>vNmPC?b>S9SSRU6)hRj>Y=ZX9y`X{V6lOv6qw5eCeBDB{q|lxb%}ft8kSkufin{ z(5nnrHT{vw(+AHi9$GA}a<`!S8-QT)TFz@%9_X~#og64oxoKv&Ai43bl9T}$8+?c!g6{^MUnz$~2q1m-4O_*Wsvof*Am$LWEoxJJ=bb4W6(P4_BB zQ_}BQbS9XOS=YJc7Zkp^^}abPg_KqPlvS_>RVkNL6&F<%XRFS|3aVPa zs@A8f4X?Z6Qj7`0yj!}+oy9@vI{v*C*~G` z3;8S@DK-z7jpsegf|VXLnX$P+L=}620XpWhRnqBl+W1*HFi-frAnlBFJ##FciLz9` z`gsQZ-Dkzgb6{9M#rq@FVugG)qjFu0(&gg@_Dg(K9u6GV)p++zEr(% z9BI-T!|?3C?<1ZXEQ;p}Uy6Vky2~(z)^5HW!E6K-2o@ozM6eh^6@qF6a}X>+Fc-m6 z1oIFqLogq~cWS^j>YpL|>FE+x9rS)HaHw)bvtO;2enE^pZZ>`hlg8z#5X=b9B`2bf4L;$1!ojtT@F0vQcec;hH;l(MxhK< zHy~pa;MI=khm0a@dl--d1t_~f;R%~C7)dxapkPe!ZXFw9%qYUC0VShQ0tH?H*@sbt zQv)%K0(W)w1d4EKK;;1v#Q^pJdMnw7F$t#zVu3MeyP+or)KDsRKn-~IUKAMv9hM8H8J$N1gz5neWF(5!Gr|dJMi>ut@dNQ(LO%gP z67eAd7jwU2Ac52N%ORfBF9Vnim_$z3ufW`XCBT&a7=Wq$aRBxGYJh3|8V2K`DGBuh z+JCibpbKm2eCGJni6na6snp8gi`Y;z2H;0l&~r&Z8Ma07U|ETz#BvKJl>-`Ghzq}c ze9C1C;o+W?N}qM;)4Ldk&*2Q?2}RZ=QLI@{COeEAiV?0tr1eZqi%m%8G6!@}LLaJ} z%L+arN#U{qkH;(fK)>cdI(=L^7e++q=$cU47%Z*k@&Jzt5_~G)6vdgNb2LpL&JPl& za|NzBA}K-csR#%3{d!R8%GQ68RoiE?b4K=k;0b0W`e2~uBWp&^qP2(4&)SXgiv%;$vDB}TxrLn_mb3#4t zWL9W@PY2xip$6pYmSfDVDK*xG>M*($j1F9*Txn5vY0(aM?h2ks3PWMFxtvBz3z$1C zeQjVZwRFSaxD$W??4VXxFrD52xnN{f+S9>gSmzKP;J0Lak_BMS=u9oBV}7`I7zWP`fw)}$vxjr zAMTRA5+H{WGp-fi^r%@2q69~Q0W-pUP z=hB;yA!?^g?_(+-)A5+f$MiX-=rJ9Sz1(C^1{|n*vKIuXC-C3JWDkN~1lJ+p5wIsV zPK@;t55RpGMWw}*CcaCFV|U6f7kImZFa5e)0nI+~Z8VcJ5En`TiDDmf3*y}&pxOr} z8MuL11M+?FU<4pM9f7AU5I3`^a-S9VEg)pugr~<5Q10u-Z6ly|fx|6Wbs#ZVL03<2 z8{z|TUC{4fi^F&&A3B05FRcDi}@6^rw}3S9w>BrR4(%SCq*LP4luc8No741d&}Z2f5`I9CICW zov1re=SwkpV;PTpta!m_YBk=q;Z^5WZ@t%3KbAV*TOX;tW;AW_g+hPrP9bfVD8j;^ z2(0*$(bQ!Zw)j`<5>j_VaVWjmTYqh=a?#o9v(@L?@t-fX-mCVi$ILdL`0d>16Tcbz zy@}q$vCbZ!`0aK1Jn#qKz5($I_AHO+*q9ismTo{>*Ti6>bUPv^20Nr?LUa2=O z()h{Jdq8~17pFzxXnNN1#7pVr7t_nnW(esu{`4B}YVYc?^a21l3@{T&EV#hav;67P zFQr#rOs_nH^^8OxY{n)8<~yHvX7V(FTZYN2$izjUjRv5jT2xtLzz zjUQ!VoX}lV7y8(DbWe+akKHGJyRQRlfrHdy7fcMYmrK3tyz55ell<|yCn_(-7yIIi zN3%T3FSwq+LCD%VRc2$zhh-OTJa5J)Y#zLxW&J*_0mTB#f^4f z_kLmH0sqDWg7M&y_#^RS#qg}MReOa@3;4i1tUYYr+dybptW7$KCLfni{PrD$ z2JIsoq~Jx`N4BFE2?I95t!Vz)2fxl_Bpf0!1%c5j^=Y!$AWVCtYN+~5%}~w7#CbmU z9o@_M_a5+xU(dk-DD4J^8C2jVyRZTbfXQxYJ67N(9asSdfD8tOHmOf*ymmzizx!gH zJuz4=Wsa_i!8&O(a+nx2?v%D5c4E*fbs{ntq`9PCHB_a|JhoX#G@i&663fq43yF&^ z$c4nZ3td8DqgRgkDPA>LX72pGf%J}hPY?gcj5S%$H*wMlULA1s8zlIKK(`M}ETH6bHNa{JHwrJ(%7+k2y^{DYVJ+b#ey$bH`3;1>LJVqfs_+v4r_@kaUCrD?9 zA40()%WS@Q#)>DZhMkp$>Zoe3=&cdpKRT6V7P$=LI;Lh7=xVHZ{~ zF5FHJIkMvZ%_mY$SWczgx9j*WIC$U-N7g;_k8SYlW?s@&Uer~dtrc|h{JMGGI>>?D zG`^Eu))zf!@l9(NiaY$p9fIEG*V}wr+gMhPPh}WQ(R*XruMQNkGp|Shc4Praa7>DR3EX;zLJPygNpjXPg9zhV{)y9CW{zh<{jvwKvNeIi59 z%@j(KkZA!RJv~;1VI|5}CqfD+RYmflTR&z1vbsPu~_#4=&fx ztRL$=(f$h=JUWp^o~qbLzl@7xo(p(l0y91OQ8+GU_G?pjNzh{nPCn!E(A{?%dvRi% zC0^`WUFh0Ow(&uig|if06!@gdJ^f$wX#jm7G0G*v9wvrfnXy?N<-tI21+!*{-Y>Jc zJ_e$Drh2sz2Tg#dGVHBP+Jt!<$#@TEZ9i~;mmK(L;Mih+^7Ko|vo9vkK3jLL;2eK; ziLYY$$ZTKoY$17_KY5)uChVA4{Iv7JyeIq5gI8knT0zy|S2g%l4Wmh^_oVwWO8rS? zmy#AuooCEt{)$1o~|-Gd*c&Tkeg__t7!N59c6PKHQ8 z4bO(nu$8sLW12uBCi)zcNYPOK^6=~5=twCul`|{(R2QXM9NuLJ#M{!n zT2iY%foXiP$UlAWZZb_8$cG=6>h2vNs?bkO1Nc)@oyHvc?dXAM#KCC9ei5M#7wHJl zD-|hPmS%UO+p)s??!BEXtYx<}c4_NFBwoG(_#6waP2iTZIa@7`RxqG&f%GYmW_(F~ zZ-8uDmf+}tx9v!ZPuyDrv74a~TQpPM`B>kw_wX%fA%M_dq-58{-Gg@!vNi2E zijp|)buRlY!pPtkgp`r z9D>IXJb~aw1UDf#1RxLxJ%rayI34*{5&JfRcM$v$f#L7W<6 z%^1tTSOJ1!1TzqL@&E+lgN{8IZoAnGukb$vBD}&&mWmEOG$S)t zEm~rnKZ4}7Ly{*xjv>coI$h3@x%VtQaf44cSJ2J%>*gM682p}^>^03@cDC)YE>QH= zl4YpxHApvNFs~8yPJp0x0`$o%2R~4gilt-8#v{e|l%6j1C6@`wW&Y%{Lu*I%g-6=% zah&FS`bt4x>DN~t+Ax|i?O5^g(zAuWjCn%FJb%VKpFC|WKI@5G4cR91jGC%W>xVLZ zi`spr4#CvnH+A^r#iNB4C+bhF_bqJpS#7?;PNA^VU)brB=lx%16qKHrn{sNQZ(ftn z-0CafgaXcA!1?6H!F`G3YMCgv&$Q@*%a_q0WHk6Q8bI{X-08>m9lzc;qjsdum)j`h zHu`fLee$fqHZ9q#Pc1mI^`1Sx!kP=5FBPAR_*2&&TK~j3Eg>q;ChX5$(!CfMEZD68 zp<4m^YF;YXW6 za8*B8noSOQuBN*QQXsr4!UM7-h2e?>z*PiSis3$cc*j*~3>F1|btjHTh*->GF{b~p zrC=%#$l$+(;L$l2kas%4)FkDtJUioJ;rzDu41SzVY~-%N?KyA~@CkzhIV8)XMRHZ| z!M3*UUTZs_#FDPQgBG)`$HgZ@lGhEI@T`EXg9^yI$hpO-}jvhNw{R*d-M4H_@8^v`G3p# z?m6H2?uB)GRWH9C9Q90OWVix;ir@QXN%RH9mZ$`>YfHxKR#LAs>4-v52;p{8PfUbg zYwQE-2bl)(>j?Ycdc8@{uOsbA^~t8>dV|T(bDz@Zp4d}OsY->_P-{eE_+t&JO=I~b z@u5Tk10l8PY$HjYIz$?T;f&oxq1Xq%(vNAVQZZE_3}{mbQP$|%%-;UpLBN9F9@fnQ zsZeBU+4sc3sB&6jb?*qap*=T0Tpr}^R=yS-r|TURCJK`}Cht~)E+u}=VT$H`c?cSOrVS1e;x_%qaK%ru_0gr$%P>`}mp z?Depe;ge);&+`}JGLvPD`K%)>g$)mnVHd)*Y)*I#DPc>)Q^-_yKj1Xh5uU=9glpM( zU~GW)H)!{TYh8=`X)cv%4iPL6poosD-N6tb_?3Q4Gi4L229ErxmB~1QoCE4iv)B_F zBb%eqvX3;eWH!63F_PO^M1)aaA#=qh)N%!U*|ASGI#S1e0=AtEjxe(4B4Wur_I8BP zHJ{7n_PQz*3ymPxBztxXWUOTZg^4;koU)0UWV~i!c>ry9a28)LtUE##xq`D+N;bDf z>RhXYmX5n*%X1+YTI6+fwXg)(S46wc~EtbQbqxtpnaYz~j%&jNo|a>xZy|ZPe;CKAR7-7qq&WzAX8O@O#iDU-&Dz79ZhGj4t=um&_F4dA56C!&0&q6UIDZbn*~B@6U}BPe;q?Hd2W5)h2*BAa?@BC}U;VR1Mtd)S!L2gR;Q$=QIXFB1BfKvhfgK`06&~U&m!XHH z5RL*#UjJ<4B;UIKz#87~5ZW#?cPxM&QN}qA9Ddj@-3JGU^kdo~Gj^hv#6Wz0M27FJsqUw@x8MMgH7B^@40`wI#=ydtM`>%y>I=+>qpciHSeJ5OD?ICe- zFUfRWMSHAW7w324b+Z?AI+y>+d~FvIz7H_JS7iSGAY2dN|En^aKMFTGM#=nr4LCuy zhrFxlAFjUTg78zDVmFNL>t!fRf94{u_jU+B%Orm3C-H{N$geVq|MHWNlA!5L;h${^ z(_1oA|B?y*<|oMgF}*F5keO;Hf})?yI}icyyMPNQq#xy7e+n>^{TT1b7(x9Qhq-I- z_r?vh1oxc|^r+g5@^ByNJ0lchdj}gsj!OM(nqF@U?Pu(Reo|rmjD`1;ItJP`5clK# zq$4D0wpy>xH~}npe>R@WxZl0F8R#kLEN^K1SQFI-))>k#$vtg2`aNT zQ>@YGK!80enuOOW2#E-T5%dVD08rv96b1uLXTPNtLD^rFK8<9su5~*0X!_DCUo^YF2j?@2)ML=6 z>1=#@0xKLkl4Q8+hL(^r6yFo%iL?l2o5O6kIGjz54vM{%K9%4opIbg^=2P{}TsGCr zDezTe%FUa=L+wl*`{w4B0qJc z4%7r^bw(h|flmd)`B_$){&wt$0cce`JLPOv@rPN(U0D-OXH95T&>t=4eYHc#+V5p;eE8Iem%P}We71a{jGvJ z13El|*#pxDyPvu>HfR*zS;P}8SXMbQHNIu%33528wD#IjB*ffEZA)woJ$sN{nU+YR zSjhC8R9q``B6314&CW(tOAGX&Omqj?x@5g6%4;hjpo)=~O?BBJu3P%=k2jFIx7f zErPJzV)q22qCSSHg}pm7pNwZWW)_Fxyy=qwtawH|t0=oeUjjF9^kcs}t591C`-L9BA(W^N-kKK62A3ZuH?uFxhvj-Vu;(U= zAZ>i}ibh&zb~r6gE95q5GW^a#tJ%2O1~o>Hu4S4EBg-Be4xjC|%r55-uvhtO84&MA zNd+wgcBY!**KT?c;bjELE<1m_F78#hci`7ocrrcDhPps|w$_BMzm9aD>vvY1(e_g_ zu`+gGAT?`1w#CS_6-jD7gDS=<%i%d+tQN^PiiRyz&%|qxRZbmwgZLWF6#lz-XiZOz<#$0W0Y!=MT)NZb_ zIIO-5;9E(ao6JkBPIHsRxfITkhUHf3v`M#|+Lsm_ zduK^HdCL9Ok}@KN;bR#3AHuM>F7-bM!@jy{3SMQ|j?n*)5*=IX$k1Ge^+XARR6`wgOwGjP2alL9 zqH=Ux)!b|o3i~8ZbZ{yg<4h*uYyqHDmP}rk!CrAjM03$TdG=#xo^AkCZVn7ttKx4_ zG3{eEtlhY_GgMDA+{w*rg9&4gtr`}01T`lh@PbH(S995?s|Jn3%SunAV-=hr_0CmI z=oCE!oqGsxGDH0qHTU2s3io8jku7;Q8R6c8nQMs^axV@ZVyjvTr35^Wxmz+wjQf+8 z$CN$UmrZyi*3H&L5%BG)wFTUR5^jq|Ldg1*Z``1F%fS#Dqgsq?kEIw_Dz^j&o7ol5 z%3bLsg)MirL|`&5<4dbLl$+GHMv&`~tlmb(4i!8=Sa|9WZM?EoC+J z1YtzJM@AT~XEOxJN|TxOZX@K%4R;s7Q#y3FbjIq~ll;4rYJ;ohDN3)$9lVbVoRa~WS>Zdl=K=nIvQ!Exh zGame0>8ivz`c`uZ-cALmq|@-yFL|Tt>DYH0!VH881Rga!Ju;*HUXJiM_o$m2FeTGD z$eW8W4`DunG%G9^__XK(?DU<`e$&B$I{4E%&L%hZujnX{wf55&-0FR2-%X|m-oSl?! z7qQx1Q^@;l|E{0F?`NO7oHlR_Dk?(Y>F^f3;$a`i_8u7nmD9x|28~~V!=jE(g}>y^ zJGy4TyTHv1@&wm3H&jy{`{L*jHCJ?r4fx;y*~;GfV041i0Yp!Djm>I@A_4QrdZs;g z3+ZB|$G%f<4syU?@%)#mmuE5_B5%U=$F10vI!24Y6Z2y%2D%3Ut;@Cl$7veDy-fr*era{@+qwcui?F+ zsR`B^-Og{SXyXcpHB&34lr&WS7ld;NkF!xH+hF+}Iyp4NgE{a-thg|l405YaS%cso zG0IL2bl>~ocvYx_TvOaeA|33+**V1FHlEAVBn2Ira(wF1h~p7mDV3dbmUX2p>x!kP zV`*nBg$uUq%iADV;j8Nrrbna6Z zZv?0w&;0#AsUG#0ipe_notI|#ZK@OI8)OhYfF68=zMMrj{9YZ{hhL8*JKbU5%plSm zWg_doqEqf9?AxY9_oi=8W7%GHW$_R!V&Np(iJCC2ctUGnA^FlLFXj{3xBA|?_rO|3}1}Q zE9>p>qCn9^Z^fvFFIxCE3+n{7E$rH=>!s=sVOKgT*`KbLhjqs&wDIdBrLu~&faMkg z8V&}-Xe%0Oa!a!v%5CXI#3r=lviE;T9m!Ab-fw0QD85DB#xU?{zJpiq0(dQ>wp14V zYbsOznyFvpBv2J6gF&^6iNG`JijZVM;)b?QX5PW(2UjF`$-fp<~BD!3T~ zPz{Qu>?}*$JUbI9RQ`pHxKu-?DX#>H2Q(xPwpC*UF(&L$mQg%a_w+4-#GFVnP#hIO zhQ#~~(flClXq;w1_n20b8ffq|zcr^qe8zBcF7oi9t58)Pse1uU51qfrs?r5Tq=;JO_ws#Vx z=!qf^dq001+@ysE;NpwoCO_NU6>gh*!EJLdxJ7Q8TjaL6MQ(dIw~c_`gXuVg5eUTy z{@%dMJ-JP*q-`Zmk0DkPCGL+Qzl+BD7&k1we3g}yWOj>#vPk~GA#h7`5DE}RA&f(q zB-Uh+F{E64B8%wAY4O!8(yr#YcFMu>Y@#NHok)8U0ncbp*i1{qs)Bj|ihpRL9pdsF zlB32_ke(6u=aAwgkG5x@nwL;Zjjhf~X%xSH#~BCl%x^@#=75SGQv%9uX~tWRBW~MwW_)3P~Foe{kXm@_>?D7Eg>K zlU#HI@-_o_LIf)wyAHY@??M|Jq!d9PMFy{vEO_-tgg+syMZmwl(KP^`aL9o0Wzu46 zaL^C1_Y;Kk2p15(M)(%tSA>5dxD@DG5W-Zvhkwb0Y8bB)@XCN-L>P)N1z{n=VuaNQ zYZ2NI_^JE?UbzuoM5skoZ{XEogrf+@5HPXPkH}2Gvv}!2_$$Jv2%iCXB73r8wbgDn zo1rf5f?j-{iV0G7j79ATxZE9Q;||_8hCCT0jvYtL;(OyrzN%ZT7@$8mvY5;Z5)G5c zHF4r(a{DuTCzDWDnG%l3`N})7(JjP;4+se#uroOBTy#o%{ORbF&eXAQF6>-r>5Q)G zimo~xUDbNqx%ia!nuqE-Q%81=pVJvXw<~_`>G-*=vp!R4TPxb`Xdm3AN;<7d>Q<^% zS=|bNYe5QivbRU8P{#mIb7yBvUY9EWv?{+_oKZ?r4eiNYs+7~Jlx}5+Dz{q!fP$&s z9&uACNeFAt?NUKsYPa}YDM=>n;)zo7NkW7wqgw%h5{A>N7eYs-~vC$-0DU`K_~gmBnmpbkbcMz8wL-?e*@$kli&aV diff --git a/models/__pycache__/dss_importinvoicelist.cpython-311.pyc b/models/__pycache__/dss_importinvoicelist.cpython-311.pyc index 2990f54397f6a48324caf2ad5d8468cec225ac3c..89b106fcd440822115ab40acd77fa6ef9487dd11 100755 GIT binary patch delta 1101 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 diff --git a/models/__pycache__/dss_settings.cpython-311.pyc b/models/__pycache__/dss_settings.cpython-311.pyc old mode 100755 new mode 100644 index 5c6b14b9423c415bdb4d076708d97fb2898e80c5..978ee4d091aa80cbe3d25e7fa9385903eea8739c GIT binary patch delta 1866 zcmd6oZ%i9y9LMjjrM+&~cI9YknX+4)gsv2!F!~QvXbUXPYJzUs7ps#V9UYeWv}C3h ziwQ3XS+ePw_{J#P3(Nu-%<+Qhmd$J?Mwjf4oLNIcVoc@>U$Lk$(Zu-tt_9Y0F2+|r zNk8|y=lgqp&+jhZd-`E+Wrj3;YBcU5@c6#x<&(FapPQbc$iKuW^_8}XZ%Mg9fsqJI z6Z81GpxyyutrUSXo)Y%zUHZ0i{5+1^%W*;PDtHpT(?qPJqEcT~rOpaHRHYxP(8E=F zSA`zg(M_dpW8Osn5aApUf7idpFavlpV%92<&yl!L^{EzQ5P=f zG$4NgY6k#8KsNw9fm#830AWA`!>L8}__D4)UFs8gKzSA;qs8FxKz1Cgo05FCoO2pK zO0FT1$qez?>}WcfX|2=>7hw4(7*oVu+I)_gWh86M($I4KO2ei2VqCU%%XE)I_egY4 zad+(|p*Pry_QyV==Nc7z+a0@i&F)>Lu3IF#SGI=~duV2GgKfOSde&Iaa_1_o_>M}) zj>~LZVdFF54Te2;WcG;U@LvsG*Iqt+<#0rHL}aF4VfrPezsT0lJa>01CnPgrg$YYc zxX4=1d(d9oLAJQ1itG>^0qMO{r06b+)KucF+~*3 z?Af=@PAcYo61`9TAfiq?dK;R}1116Q0Mv733L-cf(7S*^zzM*tFyd=>e+;1&un71J zFb9AyO+87_MTh`jh&O$0BzaN%&A*H6P6 eCwPYJj8P5!Zv-BpLvTL*J zL)q!<+LII5S(riMGugGd4O1L}Dvf~Zj3=LGmj#L&PkzrX!*S_KPw5X_{^RV7QQp%>^h`#5{SPi6Y-E*4+G*)SRLskWk6w3np@m z^ESUXIm*nWRz{ogfvydYfBg95aklX?gAplc_ru`R(4bZQpIjMFtpBvPb7a>C+ZGmyn) zK;rchU~x&fdRc_oFtI2pG&OQ5@<3Gz%b0<#1Y!t?k_M}if$>ummoWn6ffy<;i)M-v zNZtfVfgD(YJWNB1a*9ei6Ph6ksft-jlQ~&s?0||DvsB=U)le0tsJAdI14^!jDPV|F zP0?s!h*C?@3}(>O+HAnVDS(QhP zQGK!x&kiT~l+?7u(wvg`WRPX?#Xw7vftIAk7l5oyF1e+EOF?peUP)16a>-<7-j9qP zlfUxbGgLENkhUcIf>G3k)XWQNSr^r^uBc^QU{SuIZnU6mN%aL|u!MTnMfI#J>OcvV z&6oJTGciU_J|(!qEd=QKTU^B@i6yD&Mfs%#Mb;nzmeS(XqM`tVTzq~(YF<$UP@qT} zL`Z;$C>Rkod56#{#;DE7!U~M?U{O?+zc_4i^HWN5QtgVWHZKw>XVfua6#c+}NlXZv R67vxx{skdfFxgei3IH91$uIx_ delta 551 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 diff --git a/models/dss_advertisefields.py b/models/dss_advertisefields.py index 11af2a5..67c6d59 100755 --- a/models/dss_advertisefields.py +++ b/models/dss_advertisefields.py @@ -270,6 +270,6 @@ class dssadvertisefields(models.Model): def _compute_base64(self): for record in self: if record.btn_image_bin: - record.btn_image_bin_base64 = base64.b64encode(record.btn_image_bin).decode('utf-8') + record.btn_image_bin_base64 = 'data:image/png;base64,'+str(record.btn_image_bin) else: record.btn_image_bin_base64 = False \ No newline at end of file diff --git a/models/dss_advertisefields_templates.py b/models/dss_advertisefields_templates.py index 17f2f71..40c50ca 100755 --- a/models/dss_advertisefields_templates.py +++ b/models/dss_advertisefields_templates.py @@ -49,6 +49,8 @@ class dssadvertisefields(models.Model): templatefeldname = fields.Char('Template Feldname', required=True,tracking=True) display = fields.Char('Ziel Feldname',tracking=True) displaytemplate = fields.Many2one('dss.display.templates',string="Display Vorlage",tracking=True) + displaytemplate_fullsize_w = fields.Integer(related='displaytemplate.fullsize_w',string="Auflösung Breite",tracking=True) + displaytemplate_fullsize_h = fields.Integer(related='displaytemplate.fullsize_h',string="Auflösung Höhe",tracking=True) feldname = fields.Char('Ziel Feldname', required=True,tracking=True) color_used = fields.Char(string='Color Index',tracking=True) color_unused = fields.Char(string='Color Index',tracking=True) diff --git a/models/dss_contract.py b/models/dss_contract.py index f5ed437..aebe85f 100755 --- a/models/dss_contract.py +++ b/models/dss_contract.py @@ -23,12 +23,15 @@ from odoo import tools from . import dss_settings from . import dss_ads from odoo.exceptions import ValidationError +from odoo.exceptions import UserError from datetime import date from datetime import datetime from dateutil.relativedelta import relativedelta from pyffmpeg import FFmpeg from tuya_iot import TuyaOpenAPI, TUYA_LOGGER from tuya_connector import TuyaOpenAPI, TUYA_LOGGER +from webdav4.client import Client +from webdav4.fsspec import WebdavFileSystem import sys TUYA_LOGGER.setLevel(logging.DEBUG) @@ -115,13 +118,18 @@ class dsscontracts(models.Model): contract_remark = fields.Html('Vertragshinweise',tracking=True) project = fields.Many2one('dss.projects' , string='Project', store=True,tracking=True) + project_name = fields.Char(related='project.name') project_id = fields.Integer(related='project.projectid', string='Project ID') projectIid = fields.Integer('Project IID',tracking=True) project_ad_structure = fields.Many2one(related='project.grundsystem_default_adstructure', string='Aufbau') - project_grundsystem_typ = fields.Char(related='project.grundsystem_typ', tracking=True) + project_grundsystem_typ = fields.Char(related='project.grundsystem_typ', tracking=True) + project_grundsystem_typ_cloud_contract_template = fields.Char(related='project.grundsystem_default_cloud_structure_contract', tracking=True) project_grafiker = fields.Many2one(related="project.project_grafiker",tracking=True) project_system_uuid = fields.Char(related='project.grundsystemnameuuid', tracking=True) + run_trigger = fields.Boolean(tracking=True) + run_uni_trigger = fields.Boolean(tracking=True) + run_uni_sub_trigger = fields.Boolean(tracking=True) client = fields.Many2one('res.partner',string="Kunde (wenn angelegt)",domain="['&',('dsspartner','=',True),('dsspartner_werbung','=',True)]",tracking=True,help="Nur zu Benutzen wenn Kunden als Kontakt angelegt wurde") client_id = fields.Char(string="KundenID (2Stellen)",help="Nur der 2 stellige letzte Teil der Kundennummer",tracking=True) @@ -164,6 +172,12 @@ class dsscontracts(models.Model): cutshortwerbe_feld_selected = fields.Char(string='gekürzte Felder',compute='_compute_cutshort') werbe_feld_selected_btn_img = fields.Binary(related="werbe_feld_selected.btn_image_bin",string='Buttonbild',tracking=True) + werbe_feld_selected_btn_img_base64 = fields.Char(related="werbe_feld_selected.btn_image_bin_base64") + werbe_feld_selected_btn_pos_x = fields.Integer(related="werbe_feld_selected.btn_pos_x", tracking=True) + werbe_feld_selected_btn_pos_y = fields.Integer(related="werbe_feld_selected.btn_pos_y", tracking=True) + werbe_feld_selected_btn_pos_w = fields.Integer(related="werbe_feld_selected.btn_pos_w", tracking=True) + werbe_feld_selected_btn_pos_h = fields.Integer(related="werbe_feld_selected.btn_pos_h", tracking=True) + werbe_feld_selected_btn_name = fields.Char(related="werbe_feld_selected.btn_name",tracking=True) # need_media = fields.Many2many('dss.mediarelations','mediarelations_contract_rel','contract_id','mediarelations_id',string='benötigte Medien') last_media = fields.Many2many('dss.mediarelations',string='Medien',domain="[('isreference','=',False)]") @@ -180,11 +194,15 @@ class dsscontracts(models.Model): start_date = fields.Date('Start/Auslief.datum',tracking=True,help="Datum des geplanten Startes der 1. Kampagne. Also die Uraustrahlung!") contract_cancel_mon = fields.Integer('Kündigungsfrist Monaten',tracking=True) contract_cancel_date = fields.Date('Kündigungsfrist Errechnet', tracking=True) + contract_iscanceled = fields.Boolean('Vertrag gekündigt',tracking=True) + contract_iscanceled_date = fields.Date('Vertrag gekündigt am',tracking=True) contract_auto_extend = fields.Boolean('autom. V.Verlängerung',tracking=True) contract_auto_extend_time = fields.Char('Verl. Monate',tracking=True) scan_vertrag = fields.Binary('Datei') scan_vertrag_filename = fields.Char("Dateiname") # Hilft, den Dateinamen zu speichern + no_cancel_calc = fields.Boolean('Berechnungen verbieten',tracking=True) + runtimesystem = fields.Selection([('M','Monatslaufzeit'),('T','Tagelaufzeit'), ('E','Eventlaufzeit'), ('S','Sonderlaufzeit')],tracking=True) runtime_m = fields.Integer('Laufzeit Monate',tracking=True) runtime_bonus_m = fields.Integer('zusätzl. Laufzeit Monate',tracking=True) @@ -281,7 +299,8 @@ class dsscontracts(models.Model): web_contract = fields.Many2one('dss.web_contracts' , string='Web_Vertrag', store=True,tracking=True) tv_reach_PLZ = fields.Char(string='Reichweite PLZ',tracking=True) ads_radius_PLZ = fields.Integer('Umkreis PLZ in Km',tracking=True) - ads_count_perYear = fields.Selection([('30000','30.000'),('60000','60.000'),('120000','120.000'),('1000000','1.000.000')],'Einblendungen',tracking=True) + ads_count_perYear = fields.Selection([('30000','30.000'),('60000','60.000'),('120000','120.000'),('1000000','1.000.000')],'Einblendungen alt',tracking=True) + ads_count_perYear2 = fields.Integer('Einblendungen',tracking=True) ads_topics = fields.Many2many('dss.contracts_ads_topics', string='Themenliste', tracking=True) ads_topics_text = fields.Char('Themenliste gesamt',compute='_compute_themenliste') @@ -383,16 +402,17 @@ class dsscontracts(models.Model): @api.onchange('project_id') def _onchange_project_id(self): + _logger.info('project_id_change_1 : C_' + str(self) + ' - '+str(self.project)) for record in self : - if record.contract_name == '' : - cname = 'unbekannter Kunden' - else: - cname = record.contract_name - resstr = "%s%s %s" % (record.project_id,record.client_id,cname) - cidstr = "%s%s" % (record.project_id,record.client_id) - if resstr is None : - resstr = 'nicht ermittelbar' - _logger.info('project_id_change : C_' + str(self.id) + ' - '+str(cidstr)+' vs. '+str(self.contract_auto_id)+'/'+str(resstr)+' vs '+str(self.contract_auto_name)); + if record.contract_name == '' : + cname = 'unbekannter Kunden' + else: + cname = record.contract_name + resstr = "%s%s %s" % (record.project_id,record.client_id,cname) + cidstr = "%s%s" % (record.project_id,record.client_id) + if resstr is None : + resstr = 'nicht ermittelbar' + _logger.info('project_id_change : C_' + str(self.id) + ' - '+str(cidstr)+' vs. '+str(self.contract_auto_id)+'/'+str(resstr)+' vs '+str(self.contract_auto_name)) self.contract_auto_name = resstr self.contract_auto_id = cidstr @@ -407,7 +427,7 @@ class dsscontracts(models.Model): cidstr = "%s%s" % (record.project_id, record.client_id) if resstr is None : resstr = 'nicht ermittelbar' - _logger.info('Contract_Name_Change : C_' + str(self.id) + ' - '+str(cidstr)+' vs. '+str(self.contract_auto_id)+'/'+str(resstr)+' vs '+str(self.contract_auto_name)); + _logger.info('Contract_Name_Change : C_' + str(self.id) + ' - '+str(cidstr)+' vs. '+str(self.contract_auto_id)+'/'+str(resstr)+' vs '+str(self.contract_auto_name)) if not self.contract_auto_name: self.contract_auto_name = resstr if not self.contract_auto_id: @@ -876,7 +896,7 @@ class dsscontracts(models.Model): else: _logger.info('Click auf Werbekampagne : C_'+str(self.id)+'A_'+str(kamp.id)+' Media gleich '+str(kampagne.need_media)) else: - _logger.info('Prüfe Medien : C_'+str(self.id)+'A_'+str(kamp.id)+' Keine Kampagne gefunden !') + _logger.info('Prüfe Medien : C_'+str(self.id)+'A_' +str(kamp.id)+' Keine Kampagne gefunden !') # _logger.info('Click auf Werbekampagne : '+str(self.id)+' 4 '+str(kampagne.id)) return { 'type': 'ir.actions.act_window', @@ -978,7 +998,7 @@ class dsscontracts(models.Model): def pyaction_new_contract_kanban(self): action = self.env['ir.actions.act_window'].with_context({'default_contractid': self.id})._for_xml_id('DigitalSignage.action_dss_project_new_contract_kanban') action['display_name'] = self.contract_name - # action['domain'] = '[["projectid","=","4"]]' + action['domain'] = '[["id","=","'+str(self.id)+'"]]' # context = action['context'].replace('', str(self.id)) # context = ast.literal_eval(context) # context.update({ @@ -989,7 +1009,47 @@ class dsscontracts(models.Model): return action def pyaction_dss_project_make_contract(self): - action = self.env['ir.actions.act_window'].with_context({'default_projectid': self.id})._for_xml_id('DigitalSignage.action_dss_project_view_contract') + #raise UserError(_("Inhalt : "+str(self.id)+" - "+str(self.contract_name)+" - "+str(self.client_id))) + newid =self.id + self.contract_auto_id = str(self.project_id) + str(self.client_id) + self.contract_auto_name = str(self.project_id) + str(self.client_id) + ' ' + str(self.contract_name) + projectcloudpath = self.project.cloudlink + if not projectcloudpath: + self.cloudlink = str(dss_settings.dssSettings.getprojectpath(self,self.project))+str(dss_settings.dssSettings.getClientpath(self,self)) + else: + if projectcloudpath[0] != '$': + self.cloudlink = str(projectcloudpath)+str(dss_settings.dssSettings.getClientpath(self,self)) + else: + self.cloudlink = str(dss_settings.dssSettings.getprojectpath(self,self.project))+str(dss_settings.dssSettings.getClientpath(self,self)) + action = (self.env["confirmation.wizard"].confirm_message(_("Ist die Bezeichnung des neuen Kunden : "+self.contract_auto_name+" richtig ?( "+str(self.cloudlink)+" ) (Cloudornder wird entsprechend angelegt) !"),title="Bitte bestätigen",method="createcontract",records=self,callback_params={"template":self.id}) + ) + if action: + return action + + def create_cloud_structure_client(self, newClient): + _logger.info("Create Cloud Structure for Client "+str(newClient.id)+" - "+str(newClient.cloudlink)) + cloudpath = str(newClient.cloudlink) + client = Client("https://cloud.logumedia.de/remote.php/dav/files/OdooDav/", auth=("odooClient@logumedia.de", "lm2020#OdooDav")) + _logger.info("Neuer Cloud Path : "+str(cloudpath)) + try: + #client.mkdir(new_folder) + #_logger.info("Make Cloud Path : "+str(new_folder)) + _logger.info("Create Standard Cloud Structure for Client "+str(newClient.id)+" - "+str(newClient.cloudlink)) + client.copy(newClient.project_grundsystem_typ_cloud_contract_template+'/',cloudpath) + _logger.info("Make Standard Cloud Path : "+str(cloudpath+'/'+str(newClient.project_grundsystem_typ_cloud_contract_template))) + except Exception as e: + _logger.info("Make Cloud Path error : "+str(e)) + return True + + + def createcontract(self,template): + newid = template + newClient = self.env['dss.contracts'].search([('id','=',newid)]) + dsscontracts.create_cloud_structure_client(self,newClient) + _logger.info('Neu angelegt : '+str(self.id)+' - '+str(self.contract_name)+' - '+str(self.client_id)) + action = self.env['ir.actions.act_window'].with_context({'default_projectid': self.id})._for_xml_id('DigitalSignage.action_open_create_contract_new_edit') + action['domain'] = '[["id","=","'+str(newid)+'"]]' + action['res_id'] = newid return action @@ -1046,3 +1106,157 @@ class dsscontracts(models.Model): self.client_short_telefon=self.client.phone self.client_short_mobil=self.client.mobile self.client_short_email=self.client.email + + + def buildText(self): + _logger.info('Build Text for Contract : C_'+str(self.id)) + + if self.project_id: + projekt_id = self.project_id + else: + projekt_id = '000' + + if self.project_name: + projekt = self.project_name + else: + projekt = 'Unbekannt' + + if self.contract_name: + vertrag_name = self.contract_name + else: + vertrag_name = 'Unbekannt' + + if self.contract_auto_id: + vertrag_id = self.contract_auto_id + else: + vertrag_id = 'XXXXX' + + if self.client_short_vorname: + vname = self.client_short_vorname + else: + vname = 'Unbekannt' + + if self.client_short_name: + name = self.client_short_name + else: + name = 'Unbekannt' + + if self.client_short_strasse: + vstrasse = self.client_short_strasse + else: + vstrasse = 'Unbekannt' + + if self.client_short_plz: + vplz = self.client_short_plz + else: + vplz = '00000' + + if self.client_short_ort: + vort = self.client_short_ort + else: + vort = 'unbekannt' + + vplz_vort = '%s %s' % (vplz,vort) + + if self.client_short_email: + vemail = 'ist per Email über %s zu erreichen' % (self.client_short_email) + else: + vemail = 'ist nicht per Email zu erreichbar' + + if self.client_short_telefon: + vtelefon = 'ist per Telefon unter %s zu erreichen' % (self.client_short_telefon) + else: + vtelefon = 'ist nicht per Telefon zu erreichen' + + if self.client_short_mobil: + vmobil = 'ist per Mobiltelefon unter %s zu erreichen' % (self.client_short_mobil) + else: + vmobil = 'ist nicht per Mobiltelefon zu erreichen' + + textprojekt= 'Es ist im Projekt %s_%s ' % (projekt_id,projekt) + + vertragkopftext = 'ein Vertrag mit der Kundennummer %s und der Vertragskennung %s.' % (vertrag_id,vertrag_name) + + kundentext = 'Dieser Kunde heisst %s %s und wohnt in der %s in %s. Der Kunde %s und %s und %s' % (vname,name,vstrasse,vplz_vort,vemail,vtelefon,vmobil) + + if self.client_short_mobil: + vmobil = 'ist per Mobiltelefon unter %s zu erreichen' % (self.client_short_mobil) + else: + vmobil = 'ist nicht per Mobiltelefon zu erreichen' + + if self.start_date: + startzeit = self.start_date.strftime('%d.%m.%Y') + else: + startzeit = 'unbekannt' + + if self.contract_date: + startzeit = self.contract_date.strftime('%d.%m.%Y') + + if self.runtime_finish: + if self.runtime_finish < date.today(): + endzeit = 'Er ist bereits abgelaufen am %s' % (self.runtime_finish.strftime('%d.%m.%Y')) + else: + endzeit = 'Er endet am %s' % (self.runtime_finish.strftime('%d.%m.%Y')) + else: + endzeit = 'unbekannt' + + laufzeitmodel = 'Laufzeitmodell unbekannt' + if self.runtimesystem == 'M': + laufzeitmodel = 'eine monatliche Laufzeit mit %s Monaten und %s Monaten Bonus' % (self.runtime_m,self.runtime_bonus_m) + elif self.runtimesystem == 'T': + laufzeitmodel = 'eine tägliche Laufzeit mit %s Tagen ' % (self.runtime_t) + elif self.runtimesystem == 'E': + laufzeitmodel = 'eine ereignisabhängige Laufzeit mit %s Ereignissen' % (len(self.runtime_events)) + elif self.runtimesystem == 'S': + if self.runtime_finish: + laufzeitmodel = 'ein stichtagsabhängig Laufzeit bis zum %s' % (self.runtime_finish.strftime('%d.%m.%Y')) + else: + laufzeitmodel = 'ein stichtagsabhängig Laufzeit unbekannt' + + kuendigungsfrist = 'Die Kündigungsfrist beträgt %s Monate' % (self.contract_cancel_mon) + if self.contract_cancel_date: + kuendigungsfrist = 'Die Kündigungsfrist beträgt %s Monate und endet am %s' % (self.contract_cancel_mon,self.contract_cancel_date.strftime('%d.%m.%Y')) + if self.contract_cancel_date < date.today(): + kuendigungsfrist = 'Die Kündigungsfrist beträgt %s Monate und endet am %s' % (self.contract_cancel_mon,self.contract_cancel_date.strftime('%d.%m.%Y')) + + + vertragtext = 'Der Vertrag hat die Vertragskennung %s und ist am %s begonnen. Der Vertrag hat %s . %s. %s' % (vertrag_id,startzeit,laufzeitmodel,endzeit,kuendigungsfrist) + + + text = textprojekt + " "+vertragkopftext + kundentext + '. '+ vertragtext + + + return text + raise ValidationError((text)) + + _logger.info('Build Text for Contract : C_'+str(self.id)+' - '+str(text)) + return text + + def buildallText(self): + _logger.info('Build all Text for Contract : C_'+str(self)) + if self and "all" in str(self[0]): + self = self.env['dss.contracts'].search([]) + if not self: + raise ValidationError(_("The first record contains 'all' in its string representation.")) + + all_contract_text = '' + contracts = [] + for record in self: + _logger.info('Build Text for Contract : C_'+str(record.id)) + singletext = ' Ein Vertrag mit der ID '+str(record.id)+': '+record.buildText() + contracts += [singletext] + _logger.info('Build Text for Contract : C_'+str(record.id)+' - '+str(singletext)) + all_contract_text += singletext + + #raise ValidationError((all_contract_text)) + return contracts + + @api.model + def newwizzard(self,id): + project = self.env['dss.projects'].search([('id', '=', id)], limit=1) + _logger.info("Create new Contract Wizard - Project : "+str(project)) + action = self.env["ir.actions.actions"]._for_xml_id("DigitalSignage.action_open_create_contract") + action['context'] = {'default_project': project.id} + + + return action diff --git a/models/dss_importinvoicelist.py b/models/dss_importinvoicelist.py index 1bf70ba..6c6094f 100755 --- a/models/dss_importinvoicelist.py +++ b/models/dss_importinvoicelist.py @@ -80,12 +80,18 @@ class dssimportinvoicelist(models.Model): entry = self.env['dss.invoices'].create({'invoiceid':line[0],'from_import':self.id,'invoicename':line[1],'invoiceclientnr':line[3],'contract':client.id,'invoice_date':datetime.strptime(line[4],"%d.%m.%Y"),'is_printed':(line[5]=="Endgltig"),'ammount_netto':line[6].strip(),'ammount_ust':line[7].strip(),'ammount_brutto':line[8].strip(),'is_payed':(line[11]=="Ja"),'invoice_type':line[9],'invoice_reference':line[10]}) BotText='Rechnung ['+str(line[0])+'] für '+str(line[1])+' ('+str(line[3])+') neu erstellt als ' if (line[11]=="Ja"): - entry.is_payed_date = self.date_create + entry.is_payed = True + entry.is_payed_date = self.date_create + client.contract_payment_done = True + client.contract_payment_done_date = self.date_create BotText = BotText +' bezahlt' else: + client.contract_payment_done = False + client.contract_payment_done_date = '' BotText = BotText +' nicht bezahlt' client.client_invoices = [(4, entry.id)] else: + client=oldimport.contract if oldimport.is_payed: if (line[11]=="Ja"): _logger.info("Import Invoices - "+str(line[0])+' allready imported and payed - delete bevore import again !') @@ -95,6 +101,8 @@ class dssimportinvoicelist(models.Model): if (line[11]=="Ja"): oldimport.is_payed = True oldimport.is_payed_date = self.date_create + client.contract_payment_done = True + client.contract_payment_done_date = self.date_create BotText='Rechnung ['+str(line[0])+'] für '+str(line[1])+' ('+str(line[3])+') geändert in bezahlt !' _logger.info("Import Invoices - "+str(line[0])+' allready imported and not Payed - now Payed') else: diff --git a/models/dss_projects.py b/models/dss_projects.py index a97dedf..1146ef9 100755 --- a/models/dss_projects.py +++ b/models/dss_projects.py @@ -29,6 +29,8 @@ from dateutil.relativedelta import relativedelta from pyffmpeg import FFmpeg from tuya_iot import TuyaOpenAPI, TUYA_LOGGER from tuya_connector import TuyaOpenAPI, TUYA_LOGGER +from webdav4.client import Client +from webdav4.fsspec import WebdavFileSystem import sys TUYA_LOGGER.setLevel(logging.DEBUG) @@ -66,10 +68,16 @@ class dssprojects(models.Model): grundsystem_default_adstructure = fields.Many2one(related='grundsystemname.default_adstructure',tracking=True) grundsystemicon5050 = fields.Image(related='grundsystemname.icon_5050') grundsystem_ordner = fields.Char(related='grundsystemname.default_cloud_path') + grundsystem_default_cloud_structure_project = fields.Char(related='grundsystemname.default_cloud_structure_project') + grundsystem_default_cloud_structure_contract = fields.Char(related='grundsystemname.default_cloud_structure_contract') grundsystem_showonlinestate = fields.Boolean(related='grundsystemname.showonlinestate') grundsystem_showonlinestate_type = fields.Selection(related='grundsystemname.showonlinestate_type') + grundsystemicon_different = fields.Boolean('Grundsystem Icon anders',default=False,tracking=True) + grundsystemicon_different_image = fields.Binary('Anderes Icon',tracking=True) btntemplate = fields.Many2one('dss.display.templates',string='Buttonsvorlage',tracking=True) + btntemplate_displaytemplate_fullsize_w = fields.Integer(related='btntemplate.fullsize_w',string="Auflösung Breite",tracking=True) + btntemplate_displaytemplate_fullsize_h = fields.Integer(related='btntemplate.fullsize_h',string="Auflösung Höhe",tracking=True) standort = fields.Char('Beschreibung des Standortes') standort_strasse = fields.Char('Strasse des Projektes',tracking=True) @@ -111,6 +119,9 @@ class dssprojects(models.Model): zeitenf_off = fields.Float('Ausschaltzeit',tracking=True) zeiten_notiz = fields.Char('Schaltzeiten Notizen', tracking=True) errichtet_am = fields.Date('Errichtungstag',tracking=True) + cancel_am_in = fields.Date('Kündigung Insti - Eingang Kündigung',tracking=True) + cancel_am_to = fields.Date('Kündigung Insti - Kündigung zum',tracking=True) + finish_am = fields.Date('Beendigungstag',tracking=True) standort_inout = fields.Selection([('indoor','im Gebäude'), ('outdoor','Aussenbereich'), ('semiindoor','Überdachter Aussenbereich'),('Divers','Andere Art')],tracking=True) date_start_compute = fields.Date('Kalender Start intern',compute='_compute_calendar_start') @@ -423,13 +434,140 @@ class dssprojects(models.Model): else: _logger.info("UUID : "+str(uuid)+" not found") return False + + def getScreenViewData(self,domain): + ViewParams = {} + ViewParams.screensize_x=1980 + ViewParams.screensize_y=1024 + return ViewParams -def method(self): - return ( - self.env["confirmation.wizard"] - .with_context(hide_cancel=True) - .confirm_no_action_message( - message="Message", - title="Notification" - ) - ) \ No newline at end of file + def method(self): + return ( + self.env["confirmation.wizard"] + .with_context(hide_cancel=True) + .confirm_no_action_message( + message="Message", + title="Notification" + ) + ) + @api.model + def newwizzard(self): + _logger.info("Create new Project") + action = self.env["ir.actions.actions"]._for_xml_id("DigitalSignage.new_wizard_action") + action['context'] = {'default_state': 'start'} + + return action + + + +class dssprojects_new_wizard(models.TransientModel): + _name = "dss.projects.new.wizard" + _description = "DigitalSignage Projekte - Wizard" + #_inherit = ['mail.thread','mail.activity.mixin'] + _inherit = ['multi.step.wizard.mixin'] + _rec_name = "projectid" + + state = fields.Selection(selection='_selection_state', string='Status', default='start') + allow_back = fields.Boolean('Allow Back', default=False) + projectid = fields.Integer('Projekt ID', required=True) + projektname = fields.Char('Projektname', required=True) + name = fields.Char('Interner Name', required=True) + aktstatus = fields.Many2one('dss.projectstate',string='Aktueller Status:') + grundsystemname = fields.Many2one('dss.systemtypen', required=True) + grundsystemnameuuid = fields.Char(related='grundsystemname.uuid') + grundsystemicon = fields.Image(related='grundsystemname.icon') + grundsystem_default_adstructure = fields.Many2one(related='grundsystemname.default_adstructure') + grundsystem_ordner = fields.Char(related='grundsystemname.default_cloud_path') + isdifferendcouldstructure = fields.Boolean('Andere Cloud Struktur ?') + cloudgenerate = fields.Boolean('Cloud Struktur erzeugen ?') + differendcouldstructurefolder = fields.Char('Cloud Struktur') + kundenbeschreibung = fields.Char('Kundenbeschreibung') + run_uni_trigger = fields.Boolean('Allgemeine Trigger aktiv',default=True) + cloudlink = fields.Char('Cloud Verzeichnis',help='Verzeichnis für das Project innerhalb des Cloud Struktur') + + standort = fields.Char('Beschreibung des Standortes') + standort_strasse = fields.Char('Strasse des Projektes') + standort_plz = fields.Char('PLZ des Projektes') + standort_ort = fields.Char('Ort des Projektes') + standort_bundesland = fields.Many2one('res.country.state',string='Bundesland des Projektes') + standort_land = fields.Many2one('res.country',string='Land des Projektes') + + vertragsschreiber = fields.Many2one('res.partner',domain="['&',('dsspartner','=',True),('dsspartner_vertrieb','=',True)]") + standortpartner = fields.Many2one('res.partner',domain="['&',('dsspartner','=',True),('dsspartner_standort','=',True)]") + vertriebspartner = fields.Many2many('res.partner',domain="['&',('dsspartner','=',True),('dsspartner_vertrieb','=',True)]") + project_grafiker = fields.Many2one('res.partner',domain="['&',('dssinternpartner','=',True),('dssinternpartner_grafik','=',True)]") + + @api.model + def _selection_state(self): + return [ + ('start', 'Start'), + ('configure', 'Configure'), + ('custom', 'Customize'), + ('final', 'Final'), + ] + + def open_next(self): + _logger.info("Open Next") +# if self.state == 'start': self.state = 'configure' +# elif self.state == 'configure': self.state = 'custom' +# elif self.state == 'custom': self.state = 'final' +# elif self.state == 'final': self.action_create_project() + self.action_create_project() + + def open_previous(self): + _logger.info("Open Previous") + if self.state == 'configure': + self.state = 'start' + elif self.state == 'custom': + self.state = 'configure' + elif self.state == 'final': + self.state = 'custom' + + def state_exit_start(self): + self.state = 'configure' + + def state_exit_configure(self): + self.state = 'custom' + + def state_exit_custom(self): + self.state = 'final' + + def _create_cloud_structure(self, newProjekt, isdifferendcouldstructure, differendcouldstructurefolder): + _logger.info("Create Cloud Structure for Project "+str(newProjekt.id)+" - "+str(newProjekt.cloudlink)) + if isdifferendcouldstructure: + cloudpath = str(differendcouldstructurefolder) + else: + cloudpath = str(newProjekt.grundsystem_default_cloud_structure_project) + + client = Client("https://cloud.logumedia.de/remote.php/dav/files/OdooDav/", auth=("odooClient@logumedia.de", "lm2020#OdooDav")) + new_folder = dss_settings.dssSettings.getprojectpath(self,newProjekt) + _logger.info("Neuer Cloud Path : "+str(new_folder)) + try: + #client.mkdir(new_folder) + #_logger.info("Make Cloud Path : "+str(new_folder)) + if isdifferendcouldstructure: + _logger.info("Create Differend Cloud Structure for Project "+str(newProjekt.id)+" - "+str(newProjekt.cloudlink)) + client.copy(differendcouldstructurefolder+'/',new_folder) + _logger.info("Make Differend Cloud Path : "+str(new_folder+'/'+str(differendcouldstructurefolder))) + else: + _logger.info("Create Standard Cloud Structure for Project "+str(newProjekt.id)+" - "+str(newProjekt.cloudlink)) + client.copy(newProjekt.grundsystem_default_cloud_structure_project+'/',new_folder) + _logger.info("Make Standard Cloud Path : "+str(new_folder+'/'+str(newProjekt.grundsystem_default_cloud_structure_project))) + except Exception as e: + _logger.info("Make Cloud Path error : "+str(e)) + return True + + + def action_create_project(self): + _logger.info("Create Project") + newProjekt = self.env['dss.projects'].create({'projektname':self.projektname,'name':self.name,'aktstatus':self.aktstatus.id,'projectid':self.projectid,'grundsystemname':self.grundsystemname.id,'grundsystemnameuuid':self.grundsystemnameuuid,'grundsystemicon':self.grundsystemicon,'grundsystem_ordner':self.grundsystem_ordner,'grundsystem_default_adstructure':self.grundsystem_default_adstructure,'standort':self.standort,'standort_ort':self.standort_ort,'standort_plz':self.standort_plz,'standort_land':self.standort_land.id,'standort_bundesland':self.standort_bundesland.id,'standort_strasse':self.standort_strasse,'vertragsschreiber':self.vertragsschreiber.id,'standortpartner':self.standortpartner.id,'vertriebspartner': self.vertriebspartner.ids,'project_grafiker': self.project_grafiker.id,'short_partner_description':self.kundenbeschreibung}) + newProjekt.cloudlink = dss_settings.dssSettings.getprojectpath(self,newProjekt) + if newProjekt: + _logger.info("Project created with ID : "+str(newProjekt.id)) + if self.cloudgenerate: + _logger.info("Create Cloud Structure for Project "+str(newProjekt.id)+" - "+str(newProjekt.cloudlink)) + self._create_cloud_structure(newProjekt,self.isdifferendcouldstructure, self.differendcouldstructurefolder) + return True + + + \ No newline at end of file diff --git a/models/dss_settings.py b/models/dss_settings.py index 0555be9..e02a58a 100755 --- a/models/dss_settings.py +++ b/models/dss_settings.py @@ -68,7 +68,7 @@ class dssSettings(models.Model): settings = (self.env['dss.settings'].search([],limit=1)) wert = settings._origin.read([valuename])[0][valuename] return wert - + @api.model def _get_path_converted(self,path,record): alls = '' _logger.info("GetPath with " + str(path)+ " and Record : "+str(record.id)) @@ -158,6 +158,29 @@ class dssSettings(models.Model): myview.unlink() return 0 + @api.model + def getprojectpath(self,project): + _logger.info("Get Project Path for "+str(project.id)) + if project: + settings = (self.env['dss.settings'].search([],limit=1)) + alls = dssSettings._get_path_converted(self,settings.def_project_cloudpath,project) + _logger.info("Get Project Path - Ergebnis : " + str(alls)) + return alls + else: + _logger.info("Get Project Path - kein Projekt gefunden") + return '' + + @api.model + def getClientpath(self,client): + _logger.info("Get Client Path for "+str(client.id)) + if client: + settings = (self.env['dss.settings'].search([],limit=1)) + alls = dssSettings._get_path_converted(self,settings.def_contract_cloudpath,client) + _logger.info("Get Client Path - Ergebnis : " + str(alls)) + return alls + else: + _logger.info("Get Client Path - kein Client gefunden") + return '' class dssSettingsSave(models.Model): diff --git a/models/dss_systemtypen.py b/models/dss_systemtypen.py index 9fffa7f..4970f0f 100755 --- a/models/dss_systemtypen.py +++ b/models/dss_systemtypen.py @@ -41,7 +41,9 @@ class dsssystemtypen(models.Model): icon = fields.Image() icon_5050 = fields.Image("Icon 50",compute='_compute_icon_5050') default_adstructure = fields.Many2one('dss.adstructures',string='Standard-Werbeaufbau', tracking=True) - default_cloud_path = fields.Char('Standard Cloud Path', tracking=True) + default_cloud_path = fields.Char('Standard Cloud Pfad', tracking=True) + default_cloud_structure_project = fields.Char('Standard Cloud Projekt-Struktur (Pfad)', tracking=True) + default_cloud_structure_contract = fields.Char('Standard Cloud Kunden-Struktur (Pfad)', tracking=True) email_template_welcome = fields.Many2one('mail.template',string='Mailvorlage für Willkommen', tracking=True) email_template_zuarbeit = fields.Many2one('mail.template',string='Mailvorlage für Zuarbeiterrinnerung', tracking=True) email_template_abzug = fields.Many2one('mail.template',string='Mailvorlage für Korrekturabzug', tracking=True) diff --git a/models/dss_web_contracts.py b/models/dss_web_contracts.py index 6b18ca0..3ed78e4 100755 --- a/models/dss_web_contracts.py +++ b/models/dss_web_contracts.py @@ -83,6 +83,7 @@ class dsscontracts(models.Model): werbe_feld_tv = fields.Selection([('L-Banner','L-Banner'),('Countdown','Countdown')], 'Paket', tracking=True) werbe_feld_web = fields.Selection([('Standard','Standard'),('Premium','Premium')], 'Paket', tracking=True) zahlweise = fields.Selection([('einmalig','einmalig'),('jaehrlich','jährlich'),('sonder','Sonderzahlweise')], 'Zahlweise', tracking=True) + paymentinterval = fields.Many2one('dss.payintervals',string='Abrechnungsinterval',tracking=True) main_runtime = fields.Integer('Gesamtcliplänge',tracking=True) split_runtime_count = fields.Integer('Clip Teilungen',tracking=True) @@ -113,12 +114,14 @@ class dsscontracts(models.Model): tv_reach_PLZ = fields.Char('Reichweite PLZ',tracking=True) ads_radius_PLZ = fields.Integer('Umkreis PLZ in Km',tracking=True) - ads_count_perYear = fields.Selection([('30000','30.000'),('60000','60.000'),('120000','120.000'),('1000000','1.000.000')],'Einblendungen',tracking=True) -# ads_count_perYear_related = fields.Selection(related='dss.contracts.ads_count_perYear',tracking=True) + ads_count_perYear = fields.Selection([('30000','30.000'),('60000','60.000'),('120000','120.000'),('1000000','1.000.000')],'Einblendungen alt',tracking=True) + ads_count_perYear2 = fields.Integer('Einblendungen',tracking=True) +# ads_count_perYear_related_re = fields.Selection(related='dss.contracts.ads_count_perYear',tracking=True) ads_topic = fields.Selection([('auto','Auto'),('beauty','Beauty & Fashion'),('wirtschaft','Wirtschaft, Finanzen & News'),('food','Food'),('gesundheit','Gesundheit'),('lifestyle','Lifestyle'),('living','Living'),('entertainment','Entertainment'),('reise','Reise'),('technik','Technik'),('individuell','Individuell')],'Themen', tracking=True) ads_topics = fields.Many2many('dss.contracts_ads_topics', string='Themenliste', tracking=True) grafik_zuarbeitBis = fields.Date("Zuarbeit bis", tracking=True) grafikerstellung = fields.Boolean("Grafikerstellung Logumedia", tracking=True) + boarding = fields.Boolean("Boarding", tracking=True) scan_vertrag = fields.Binary('Datei') file_name = fields.Char(string='Dateiname') @@ -167,6 +170,7 @@ class dsscontracts(models.Model): 'contract_auto_id': self.contract_auto_id, 'contract_name': self.contract_name, 'contract_state': self.env['dss.contractstate'].search([("statusname","=","Angelegt")]).id, + 'paymentinterval': self.paymentinterval.id, 'werbe_feld_selected': self.werbe_feld_selected, 'client': self.client.id, 'client_short_vorname': self.client_short_vorname, @@ -179,7 +183,7 @@ class dsscontracts(models.Model): 'client_short_plz': self.client_short_plz, 'client_short_website': self.client_short_website, 'client_short_company': self.client_short_company, - 'ads_count_perYear': self.ads_count_perYear, + 'ads_count_perYear2': self.ads_count_perYear2, 'runtimesystem': self.runtimesystem, 'runtime_m': self.runtime_m, 'runtime_t': self.runtime_t, diff --git a/security/ir.model.access.csv b/security/ir.model.access.csv index 39cc314..4fa90b3 100755 --- a/security/ir.model.access.csv +++ b/security/ir.model.access.csv @@ -1,5 +1,6 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink digitalsignage_dss_projects_group_user,access.dss.projects,model_dss_projects,base.group_user,1,1,1,1 +digitalsignage_dss_projects_new_wizard_group_user,access.dss.projects.new.wizard,model_dss_projects_new_wizard,base.group_user,1,1,1,1 digitalsignage_dss_systems_group_user,access.dss.systems,model_dss_systems,base.group_user,1,1,1,1 digitalsignage_dss_geraetetypen_group_user,access.dss.geraetetypen,model_dss_geraetetypen,base.group_user,1,1,1,1 digitalsignage_dss_systemtypen_group_user,access.dss.systemtypen,model_dss_systemtypen,base.group_user,1,1,1,1 diff --git a/static/src/js/dss_nextcloudwidget.js b/static/src/js/dss_nextcloudwidget.js new file mode 100755 index 0000000..85f5e27 --- /dev/null +++ b/static/src/js/dss_nextcloudwidget.js @@ -0,0 +1,448 @@ +/** @odoo-module **/ +import { registry } from "@web/core/registry"; +import { useBus, useService } from "@web/core/utils/hooks"; +import { useInputField } from "@web/views/fields/input_field_hook"; +import time from 'web.time'; + + +var translation = require('web.translation'); +var _t = translation._t; +const { Component,useRef} = owl; +var rpc = require('web.rpc') + +export class cloudFolderViewerField extends Component { + static template = 'FieldDateMultipleDate' + setup(){ + super.setup(); + this.input = useRef('inputdate') + this.rpc = useService('rpc') + window.input = useRef('inputpath') + this.input = useRef('inputpath') + window.this=this + useInputField({ getValue: () => this.props.value || "", refName: "inputpath" }); + if (this.props.value) { + this.readfiles(this.props.value); + } else { + this.readfiles(''); + } + window.oncontextmenu = function (ev) { + if (ev.target.classList.contains('FileExplorerFilesField')) { + var ele = ev.target.parentElement; + } else { + if (ev.target.classList.contains('Breadcrumb_Text')) { + var ele = ev.target.parentElement; + } else { + var ele = ev.target; + } + } + console.log(ele); + if (ele.classList.contains('FileExplorerFilesLine') || ele.classList.contains('Breadcrumb')) { + console.log(ele); + if ((ele.dataset.type == "Breadcrumb") || (ele.dataset.type == "Folder")) { + var MainBox = document.querySelector(".CloudfileBrowser"); + var contextBox = document.querySelector(".ContextFolderMenu"); + var FeldSelectBox = document.querySelector(".FieldSelectMenu"); + var CloudText = document.querySelector(".ContextCloudLinkMenuItem_text"); + contextBox.style.left = `${ev.clientX-ele.offsetLeft}px`; + contextBox.style.top = `${ev.layerY}px`; + FeldSelectBox.style.left = `${ev.clientX-ele.offsetLeft}px`; + FeldSelectBox.style.top = `${ev.pageY}px`; + contextBox.classList.add("visible"); + contextBox.classList.remove("hidden"); + CloudText.innerHTML = ""; + contextBox.dataset.file=ele.dataset.file; + CloudText.innerHTML = ele.dataset.file; + return false; // cancel default menu + } + if (ele.dataset.type == "File") { + var contextBox = document.querySelector(".ContextFileMenu"); + var FeldSelectBox = document.querySelector(".FieldSelectMenu"); + contextBox.style.left = `${ev.clientX-ele.offsetLeft}px`; + contextBox.style.top = `${ev.layerY}px`; + FeldSelectBox.style.left = `${ev.clientX-ele.offsetLeft}px`; + FeldSelectBox.style.top = `${ele.offsetTop}px`; + contextBox.classList.add("visible"); + contextBox.classList.remove("hidden"); + contextBox.dataset.file=ele.dataset.file; + return false; // cancel default menu + } + + } + return false; // cancel default menu + } + + } + + _onUploadClick(ev) { + var contextBox = document.querySelector(".ContextMenu"); + contextBox.classList.remove("visible"); + contextBox.classList.add("hidden"); + var ext = contextBox.dataset.file; + const toBase64 = file => new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.readAsDataURL(file); + reader.onload = () => resolve(reader.result); + reader.onerror = reject; + }); + const fileInput = document.createElement('input'); + fileInput.type = 'file'; + fileInput.multiple = true; + fileInput.addEventListener('change', async (event) => { + const files = event.target.files; + if (files.length > 0) { + for (const file of files) { + try { + await rpc.query({ + route: '/my/webdav/upload', + params: { + path: ext || '/', + filename: file.name, + filedata: await toBase64(file), + }, + }); + alert(_t(`File "${file.name}" uploaded successfully.`)); + } catch (error) { + alert(_t(`Failed to upload file "${file.name}".`)); + } + } + this.readfiles(ext || '/'); + } + }); + fileInput.click(); + } + + _onSetCloudClick(ev) { + console.log(ev); + if (ev.target.classList.contains('FileExplorerFilesLine')) { + var ele = ev.target.parentElement; + } else { + if (ev.target.classList.contains('Breadcrumb_Text')) { + var ele = ev.target.parentElement; + } else { + if (ev.target.classList.contains('ContextMenuItem_text')) { + var ele = ev.target.parentElement.parentElement; + } else { + if (ev.target.classList.contains('ContextCloudLinkMenuItem_text')) { + var ele = ev.target.parentElement.parentElement; + } else { + var ele = ev.target; + } + } + } + } + console.log(ele); + if (confirm(_t("Folder selected : " + ele.dataset.file + "\nAre you sure you want to select this folder?"))) { + window.this.props.record.update({ [window.this.props.name]: '/'+ele.dataset.file }); + return false; + } + } + _onContextMenueMouseLeave(ev) { + var contextBox = document.querySelector(".ContextFileMenu"); + if (contextBox.dataset.file != undefined) { + contextBox.dataset.file = undefined; + } + contextBox.classList.remove("visible"); + contextBox.classList.add("hidden"); + var contextBox = document.querySelector(".ContextFolderMenu"); + if (contextBox.dataset.file != undefined) { + contextBox.dataset.file = undefined; + } + contextBox.classList.remove("visible"); + contextBox.classList.add("hidden"); + var contextBox = document.querySelector(".ContextMenu"); + if (contextBox.dataset.file != undefined) { + contextBox.dataset.file = undefined; + } + contextBox.classList.remove("visible"); + contextBox.classList.add("hidden"); + var FeldSelectBox = document.querySelector(".FieldSelectMenu"); + if (FeldSelectBox.dataset.file != undefined) { + FeldSelectBox.dataset.file = undefined; + } + FeldSelectBox.classList.remove("visible"); + FeldSelectBox.classList.add("hidden"); + } + + _onSetBtnImageClickField(ev) { + var FeldSelectBox = document.querySelector(".FieldSelectMenu"); + FeldSelectBox.classList.remove("visible"); + FeldSelectBox.classList.add("hidden"); + console.log(ev); + var ele = ev.target.parentElement; + console.log(ele); + var ext = ele.dataset.file; + console.log(ext); + console.log(ele.dataset.feldid); + //.split('.').pop().trim(); + console.log('/'+ext+'/'); + if (ext.match(/\.(jpg|jpeg|png|gif)$/i)) { + console.log('/'+ext+'/+'); + rpc.query({ + route: '/dss/setbtnfilefeld', + params: {contract: this.props.record.data.id, + filename: ele.dataset.file, + feldid: ele.dataset.feldid}, + }).then(success => { + console.log(success); + if (success != true) { + alert(_t("Error: " + success)); + } else { + alert(_t(" Erfolgreich zugewiesen !")); + } + + }) + } + } + + + _onSetBtnImageClick(ev) { + console.log(this.props.record.data.id); + var contextBox = document.querySelector(".ContextMenu"); + contextBox.classList.remove("visible"); + contextBox.classList.add("hidden"); + var ext = contextBox.dataset.file; + //.split('.').pop().trim(); + console.log('/'+ext+'/'); + if (ext.match(/\.(jpg|jpeg|png|gif)$/i)) { + console.log('/'+ext+'/+'); + rpc.query({ + route: '/dss/setbtnfile', + params: {contract: this.props.record.data.id, + filename: contextBox.dataset.file}, + }).then(success => { + console.log(success); + if (success != true) { + if (success.startsWith("MULTI")) { + var fields = success.split("/")[1] + var fieldIds = success.split("/")[2] + var FeldSelectBox = document.querySelector(".FieldSelectMenu"); + FeldSelectBox.classList.add("visible"); + FeldSelectBox.classList.remove("hidden"); + var FeldSelectBox = document.querySelector(".item0"); + if (FeldSelectBox != undefined) { + FeldSelectBox.parentNode.dataset.feldid = fieldIds; + FeldSelectBox.parentNode.dataset.feld = fields; + } + var Singlefields = fields.split(":"); + var Singlefieldids = fieldIds.split(":"); + var Nr = 0; + Singlefields.forEach(element => { + Nr += 1; + var FeldSelectBox = document.querySelector(".item"+Nr); + if (FeldSelectBox != undefined) { + FeldSelectBox.parentNode.classList.add("visible"); + FeldSelectBox.parentNode.classList.remove("hidden"); + FeldSelectBox.parentNode.dataset.feldid = Singlefieldids[Nr-1]; + FeldSelectBox.parentNode.dataset.feld = element; + FeldSelectBox.parentNode.dataset.file = ext; + FeldSelectBox.innerHTML = 'Auf Feld '+element+' zuweisen'; + FeldSelectBox.dataset.field = Singlefieldids[Nr-1]; + } + }); + } else { + alert(_t("Error: " + success)); + } + + } + }) + } + } + + _onMoveToArchivClick(ev) { + var contextBox = document.querySelector(".ContextMenu"); + contextBox.classList.remove("visible"); + contextBox.classList.add("hidden"); + alert('move to archive'); + } + + _onDownloadClick(ev) { + var contextBox = document.querySelector(".ContextMenu"); + contextBox.classList.remove("visible"); + contextBox.classList.add("hidden"); + alert('download'); + } + + _onDeleteClick(ev) { + var contextBox = document.querySelector(".ContextMenu"); + contextBox.classList.remove("visible"); + contextBox.classList.add("hidden"); + var filename = contextBox.dataset.file; + console.log(filename); + try { + rpc.query({ + route: '/my/webdav/deletefile', + params: { + filename: filename || '/', + }, + }).then(success => {; + console.log(success); + if (success != true) { + alert(_t("Error: " + success)); + } else { + alert(_t(" Datein "+filename+" Erfolgreich gelöscht !")); + } + $(".FileExplorerFilesBackground").html(""); + const parentPath = filename.substring(0, filename.lastIndexOf('/')); + console.log(filename); + console.log(parentPath); + this.readfiles(parentPath); + }); + } catch (error) { + console.error(error); + alert(_t(`Failed to delete file "${filename}".`)); + } + } + + _onMakeFolderClick(ev) { + var contextBox = document.querySelector(".ContextMenu"); + contextBox.classList.remove("visible"); + contextBox.classList.add("hidden"); + var filename = contextBox.dataset.file; + console.log(filename); + var foldername = prompt(_t("Folder name"), "New Folder"); + if (foldername == null) { + return false; + } + if (foldername == "") { + alert(_t("Error: Folder name is empty")); + return false; + } + try { + rpc.query({ + route: '/my/webdav/makefolder', + params: { + filename: filename || '/', + foldername: foldername, + }, + }).then(success => {; + console.log(success); + if (success != true) { + alert(_t("Error: " + success)); + } else { + alert(_t(" Ordner "+foldername+" Erfolgreich erstellt !")); + } + }); + } catch (error) { + console.error(error); + alert(_t(`Failed to create folder "${foldername}".`)); + } + $(".FileExplorerFilesBackground").html(""); + this.readfiles(filename); + } + + _onSelectDateField(ev) { + $(".FileExplorerFilesBackground").html(""); + this.readfiles(this.input.el.value); + } + + readfiles(path) { + $(".FileExplorerFilesBackground").html(""); + path = path.trim(); // Trim whitespace from the path + var prepath = path; + rpc.query({ + route: '/my/webdav/files', + params: {path: path}, + }).then(files => { + //container = ev.document.getElementById('FileExplorerFiles'); + //console.log(conatiner); + var etype = ""; + var myline =""; + if (path != "") { + var preprepath = prepath.substring(0, prepath.lastIndexOf('/')); + myline = "
"; + myline += "
Folder
"; + myline += "
..
"; + myline += "
-
"; + myline += "
-
"; + myline += "
"; + self.$('.FileExplorerFilesBackground').append(myline) + } + var breadcrumbox = document.querySelector(".Breadcrumb_Text"); + breadcrumbox.innerHTML=""; + breadcrumbox.innerHTML=prepath; + breadcrumbox = document.querySelector(".Breadcrumb"); + breadcrumbox.dataset.file=prepath; + files.forEach(element => { + myline=""; + if (element.type == "file") { + etype = "File" + } else { + etype = "Folder" + }; + if (path != "") { + var mypath = path.length; + if (path[0] == "/") { + mypath = mypath - 1; + } + var ename = element.name.slice(mypath+1); + } else { + var ename = element.name; + } + + myline += "
"; + myline += "
"+etype+"
"; + myline += "
"+ename+"
"; + myline += "
"+element.size+"
"; + myline += "
"+element.modified+"
"; + myline += "
"; + self.$('.FileExplorerFilesBackground').append(myline) + //container.appendChild(myline); + }); + }); + } + + _onFileclick(ev){ + this._onContextMenueMouseLeave(ev); + if (ev.target.classList.contains('FileExplorerFilesField')) { + var ele = ev.target.parentElement; + } else { + var ele = ev.target; + } + + if (ele.dataset.type == "Folder") { + $(".FileExplorerFilesBackground").html(""); + this.readfiles(ele.dataset.file); + } + + if (ele.dataset.type == "File") { + var ext = ele.dataset.file; + //.split('.').pop().trim(); + console.log('/'+ext+'/'); + if (ext.match(/\.(jpg|jpeg|png|gif)$/i)) { + console.log('/'+ext+'/+'); + rpc.query({ + route: '/my/webdav/getfile', + params: {path: ele.dataset.prepath, + filename: ele.dataset.fileonly}, + }).then(file64 => { + console.log(file64); + var contentbox = document.querySelector(".o_content"); + var imgelement = document.querySelector(".ImageShowContainerBlur"); + console.log(imgelement); + imgelement.classList.add("ImageShowContainerBlurVisible"); + imgelement.classList.remove("hidden"); + imgelement.classList.add("isvisible"); + var img = imgelement.querySelector(".ImageShow"); + img.src = "data:image/*;base64,"+file64; + contentbox.append(imgelement); + }); + }; + } + } + + _onImageClick(ev) { + var contentbox = document.querySelector(".o_content"); + var imgelement = document.querySelector(".ImageShowContainerBlurVisible"); + imgelement.classList.remove("isvisible"); + imgelement.classList.add("hidden"); + } + + _onCloseclick(ev) { + var contentbox = document.querySelector(".o_content"); + var imgelement = document.querySelector(".ImageShowContainerBlurVisible"); + imgelement.classList.remove("isvisible"); + imgelement.classList.add("hidden"); + } + +} +registry.category("fields").add("cloudFolderViewer", cloudFolderViewerField); \ No newline at end of file diff --git a/static/src/js/dss_nextcloudwidget_short.js b/static/src/js/dss_nextcloudwidget_short.js new file mode 100755 index 0000000..e1b49ce --- /dev/null +++ b/static/src/js/dss_nextcloudwidget_short.js @@ -0,0 +1,251 @@ +/** @odoo-module **/ +import { registry } from "@web/core/registry"; +import { useBus, useService } from "@web/core/utils/hooks"; +import { useInputField } from "@web/views/fields/input_field_hook"; +import time from 'web.time'; +import { configListController } from "./form_button"; + + +var translation = require('web.translation'); +var _t = translation._t; +const { Component,useRef} = owl; +var rpc = require('web.rpc') + +export class cloudFolderViewerFieldShort extends Component { + static template = 'cloudFolderViewerShort' + setup(){ + super.setup(); + this.input = useRef('inputdate') + this.rpc = useService('rpc') + this.id = Math.random().toString().slice(2, 6); // Generate a 4-digit random number as a string + this.insertid = false + window.input = useRef('inputpath'); + this.input = useRef('inputpath') + window. + window.this=this + useInputField({ getValue: () => this.props.value || "", refName: "inputpath" }); + + window.onmousedown = function (ev) { + if (ev.target.classList.contains('CloudfileBrowserShort_Input')) { + ev.target.innerHTML = ""; + const template = document.createElement('template'); + template.innerHTML = ` +
+ +
+
+
+
+
+ Typ +
+
+ Name +
+
+ Größe +
+
+ Datum +
+
+
+
+
+ + + `; + ev.target.parentElement.appendChild(template.content.cloneNode(true)); + if (this.props.value) { + this.readfiles(this.props.value); + } else { + this.readfiles(''); + } + } + } + + window.onmouseenter = function (ev) { + ev.target.add('hover'); + } + + window.oncontextmenu = function (ev) { + if (ev.target.classList.contains('FileExplorerFilesField_'+this.id)) { + var ele = ev.target.parentElement; + } else { + if (ev.target.classList.contains('Breadcrumb_Text')) { + var ele = ev.target.parentElement; + } else { + var ele = ev.target; + } + } + console.log(ele); + if (ele.classList.contains('FileExplorerFilesLine '+window.this.id) || ele.classList.contains('Breadcrumb '+window.this.id)) { + console.log(ele); + if ((ele.dataset.type == "Breadcrumb") || (ele.dataset.type == "Folder")) { + var MainBox = document.querySelector(".CloudfileBrowser ."+window.this.id); + var contextBox = document.querySelector(".ContextFolderMenu ."+window.this.id); + var CloudText = document.querySelector(".ContextCloudLinkMenuItem_text ."+window.this.id); + contextBox.style.left = `${ev.clientX-ele.offsetLeft}px`; + contextBox.style.top = `${ev.layerY}px`; + contextBox.classList.add("visible"); + contextBox.classList.remove("hidden"); + CloudText.innerHTML = ""; + contextBox.dataset.file=ele.dataset.file; + CloudText.innerHTML = ele.dataset.file; + return false; // cancel default menu + } + + } + return false; // cancel default menu + } + + } + + _onSetCloudClick(ev) { + console.log(ev); + if (ev.target.classList.contains('FileExplorerFilesLine '+window.this.id)) { + var ele = ev.target.parentElement; + } else { + if (ev.target.classList.contains('Breadcrumb_Text '+window.this.id)) { + var ele = ev.target.parentElement; + } else { + if (ev.target.classList.contains('ContextMenuItem_text')) { + var ele = ev.target.parentElement.parentElement; + } else { + if (ev.target.classList.contains('ContextCloudLinkMenuItem_text')) { + var ele = ev.target.parentElement.parentElement; + } else { + var ele = ev.target; + } + } + } + } + console.log(ele); + if (confirm(_t("Folder selected : " + ele.dataset.file + "\nAre you sure you want to select this folder?"))) { + window.this.props.record.update({ [window.this.props.name]: '/'+ele.dataset.file }); + return false; + } + } + _onContextMenueMouseLeave(ev) { + var contextBox = document.querySelector(".ContextFolderMenu"); + if (contextBox.dataset.file != undefined) { + contextBox.dataset.file = undefined; + } + contextBox.classList.remove("visible"); + contextBox.classList.add("hidden"); + var contextBox = document.querySelector(".ContextMenu"); + if (contextBox.dataset.file != undefined) { + contextBox.dataset.file = undefined; + } + contextBox.classList.remove("visible"); + contextBox.classList.add("hidden"); + } + + _onSelectDateField(ev) { + $(".FileExplorerFilesBackground_"+this.id).html(""); + this.readfiles(this.input.el.value); + } + + readfiles(path) { + if (this.insertid == false) { + var s = self.$(".CloudfileBrowserShort"); + console.log(s); + s.addClass("CloudfileBrowserShort_" + this.id); + this.insertid = true; + } +// $(".FileExplorerFilesBackground_"+this.id).html(""); + path = path.trim(); // Trim whitespace from the path + var prepath = path; + rpc.query({ + route: '/my/webdav/files', + params: {path: path}, + }).then(files => { + //container = ev.document.getElementById('FileExplorerFiles'); + //console.log(conatiner); + var etype = ""; + var myline =""; + if (path != "") { + var preprepath = prepath.substring(0, prepath.lastIndexOf('/')); + myline = "
"; + myline += "
Folder
"; + myline += "
..
"; + myline += "
-
"; + myline += "
-
"; + myline += "
"; + self.$('.FileExplorerFilesBackground').append(myline) + } + var breadcrumbox = document.querySelector(".Breadcrumb_Text ."+window.this.id); + breadcrumbox.innerHTML=""; + breadcrumbox.innerHTML=prepath; + breadcrumbox = document.querySelector(".Breadcrumb ."+window.this.id); + breadcrumbox.dataset.file=prepath; + files.forEach(element => { + myline=""; + if (element.type == "file") { + etype = "File" + } else { + etype = "Folder" + }; + if (path != "") { + var mypath = path.length; + if (path[0] == "/") { + mypath = mypath - 1; + } + var ename = element.name.slice(mypath+1); + } else { + var ename = element.name; + } + + myline += "
"; + myline += "
"+etype+"
"; + myline += "
"+ename+"
"; + myline += "
"+element.size+"
"; + myline += "
"+element.modified+"
"; + myline += "
"; + self.$('.FileExplorerFilesBackground '+window.this.id).append(myline) + //container.appendChild(myline); + }); + }); + } + + _onFileclick(ev){ + this._onContextMenueMouseLeave(ev); + if (ev.target.classList.contains('FileExplorerFilesField') && ev.target.classList.contains(window.this.id)) { + var ele = ev.target.parentElement; + } else { + var ele = ev.target; + } + + if (ele.dataset.type == "Folder") { + $(".FileExplorerFilesBackground "+window.this.id).html(""); + this.readfiles(ele.dataset.file); + } + + if (ele.dataset.type == "File") { + } + } + + _onImageClick(ev) { + var contentbox = document.querySelector(".o_content"); + var imgelement = document.querySelector(".ImageShowContainerBlurVisible"); + imgelement.classList.remove("isvisible"); + imgelement.classList.add("hidden"); + } + + _onCloseclick(ev) { + var contentbox = document.querySelector(".o_content"); + var imgelement = document.querySelector(".ImageShowContainerBlurVisible"); + imgelement.classList.remove("isvisible"); + imgelement.classList.add("hidden"); + } + +} +registry.category("fields").add("cloudFolderViewerShort", cloudFolderViewerFieldShort); \ No newline at end of file diff --git a/static/src/js/dss_screenview_archparser.js b/static/src/js/dss_screenview_archparser.js index f4e8b57..e9701ac 100755 --- a/static/src/js/dss_screenview_archparser.js +++ b/static/src/js/dss_screenview_archparser.js @@ -14,7 +14,6 @@ export class ScreenViewArchParser extends XMLParser { const fieldNextIds = {}; let autofocusFieldId = null; const activeFields = {}; - alert("begin") this.visitXML(xmlDoc, (node) => { if (node.tagName === "field") { alert('field') diff --git a/static/src/js/dss_screenview_controller.js b/static/src/js/dss_screenview_controller.js index df55263..55aa89e 100755 --- a/static/src/js/dss_screenview_controller.js +++ b/static/src/js/dss_screenview_controller.js @@ -3,60 +3,36 @@ import { Layout } from "@web/search/layout"; import { useBus, useService } from "@web/core/utils/hooks"; import { useModel } from "@web/views/model"; import { Component, onWillStart, useState} from "@odoo/owl"; +import { ScreenViewRenderer } from "./dss_screenview_renderer"; const { onRendered } = owl; export class ScreenViewController extends Component { setup() { - alert('setup') -/* this.orm = useService("orm"); + this.orm = useService("orm"); this.dialogService = useService("dialog"); this.router = useService("router"); this.user = useService("user"); this.viewService = useService("view"); this.ui = useService("ui"); - this.state = useState({ - isDisabled: false, - fieldIsDirty: false, - }); - useBus(this.ui.bus, "resize", this.render); + this.resModel = this.props.resModel + this.resDomain = this.props.domain +// useBus(this.ui.bus, "resize", this.render); + var Container = document.querySelector('.o_action_manager'); + var DarkPanel = '
'; + console.log(Container); + console.log(DarkPanel); + Container.insertAdjacentHTML('afterend', DarkPanel); + this.model = useState( + new this.props.Model( + this.orm, + this.props.resModel, + this.props.fields, + this.props.archInfo, + this.props.domain + ) + ); - this.archInfo = this.props.archInfo; - const activeFields = this.archInfo.activeFields; - - this.beforeLoadResolver = null; - const beforeLoadProm = new Promise((r) => { - this.beforeLoadResolver = r; - }); - - const { create, edit } = this.archInfo.activeActions; - this.canCreate = create && !this.props.preventCreate; - this.canEdit = edit && !this.props.preventEdit; - - let mode = this.props.mode || "edit"; - if (!this.canEdit) { - mode = "readonly"; - } - - this.model = useModel( - this.props.Model, - { - resModel: this.props.resModel, - resId: this.props.resId || false, - resIds: this.props.resIds, - fields: this.props.fields, - activeFields, - viewMode: "form", - rootType: "record", - mode, - beforeLoadProm, - component: this, - }, - { - ignoreUseSampleModel: true, - } - ); - */ // The controller create the model and make it reactive so whenever this.model is // accessed and edited then it'll cause a rerendering // this.model = useState( @@ -71,6 +47,7 @@ export class ScreenViewController extends Component { onWillStart(async () => { await this.model.load(); + }); onRendered(() => { @@ -78,11 +55,15 @@ export class ScreenViewController extends Component { }); } - displayName() { - return this.model.root.data.display_name || this.env._t("New"); + openView(domain, views, context) { + alert('openview'); } + + displayName() { + return "DigitalSignage Projekt Vertraege - ScreenView "; + } }; ScreenViewController.template = "dss.screenview"; -ScreenViewController.components = { Layout }; +ScreenViewController.components = { Layout, ScreenViewRenderer }; diff --git a/static/src/js/dss_screenview_injector.js b/static/src/js/dss_screenview_injector.js old mode 100644 new mode 100755 diff --git a/static/src/js/dss_screenview_model.js b/static/src/js/dss_screenview_model.js index 8975369..f6cb989 100755 --- a/static/src/js/dss_screenview_model.js +++ b/static/src/js/dss_screenview_model.js @@ -1,27 +1,111 @@ /** @odoo-module **/ +import { useService } from "@web/core/utils/hooks"; import { KeepLast } from "@web/core/utils/concurrency"; import { Model } from "@web/views/model"; -export class ScreenViewModel extends Model { - setup(params) { - // concurrency management +export class ScreenViewModel { + constructor(orm, resModel, fields, archInfo, domain) { + this.orm = orm; + this.resModel = resModel; + // We can access arch information parsed by the beautiful arch parser + const { fieldFromTheArch } = archInfo; + this.fieldFromTheArch = fieldFromTheArch; + this.fields = fields; + this.domain = domain; this.keepLast = new KeepLast(); - this.searchParams = { - context: {}, - domain: [], - domains: [], - groupBy: [], - }; - this.domain = [] } + // setup(params) { + // // concurrency management + // //alert(params.toString()); + // this.rpc = useService("rpc"); + // this.keepLast = new KeepLast(); + // this.resModel = params.resModel; + // this.resDomain = params.component.props.domain; + // this.searchParams = { + // context: {}, + // domain: [], + // domains: [], + // groupBy: [], + // }; + // this.params = params; + // this.domain = [] + // } async load() { - //The keeplast protect against concurrency call - alert('load') -// const { length, records } = await this.keepLast.add( -// this.orm.webSearchRead(this.resModel, this.domain, [this.fieldFromTheArch], {}) -// ); -// this.records = records; -// this.recordsLength = length; + //The keeplast protect against concurrency call + console.log(this); + await this.keepLast.add(this.loaddata(this)); //this.loaddata(records)); + console.log(this); + var element=document.querySelector('.o_dark_panel') + element.remove();; } + /** + * @protected + */ + + async loaddata(data) { + var newrecords = []; + var nullrecord = {}; + var x = 0; + const projectrecord = await this.orm.searchRead('dss.projects', [['id','=',this.domain[0][2]]], [],{}); + console.log('00- '+JSON.stringify(projectrecord)); + nullrecord.max_w=projectrecord[0].btntemplate_displaytemplate_fullsize_w; + console.log(projectrecord[0].btntemplate_displaytemplate_fullsize_w); + nullrecord.max_h=projectrecord[0].btntemplate_displaytemplate_fullsize_h; + console.log(projectrecord[0].btntemplate_displaytemplate_fullsize_h); + var width = window.innerWidth-140; + console.log(width); + var scale_faktor = (width / nullrecord.max_w); + var height = Math.round((nullrecord.max_h+40) * scale_faktor) ; + console.log(height); + nullrecord.scale = scale_faktor; + nullrecord.screenwidth = width + 40; + nullrecord.screenheight = height + 100; + nullrecord.key=1111; + nullrecord.id=1111; + newrecords[0] = nullrecord; + x=1; + const { length, records } = await this.orm.webSearchRead('dss.contracts', this.domain, [],{}); + for (const rawRecord of records) { + for (const feld of rawRecord.werbe_feld_selected) { + const feldrecords = await this.orm.searchRead('dss.advertisefields', [['id','=',feld],['is_btn','=',true]], [],{}); + if (feldrecords.length > 0) { + var frecord = {} + frecord.id=rawRecord.id+'_'+feldrecords[0].id; + frecord.werbe_feld_selected_btn_pos_x=feldrecords[0].btn_pos_x*scale_faktor; + frecord.werbe_feld_selected_btn_pos_y=feldrecords[0].btn_pos_y*scale_faktor ; + frecord.werbe_feld_selected_btn_pos_w=feldrecords[0].btn_pos_w*scale_faktor; + frecord.werbe_feld_selected_btn_pos_h=feldrecords[0].btn_pos_h*scale_faktor; + frecord.werbe_feld_selected_btn_img=feldrecords[0].btn_image_bin; + frecord.uuid = rawRecord.uuid; + frecord.contractid = rawRecord.id; + frecord.shortwerbe_feld_selected = feldrecords[0].btn_fieldname; + frecord.client_short_company = rawRecord.client_short_company; + frecord.client_short_name = rawRecord.client_short_name; + frecord.client_short_vorname = rawRecord.client_short_vorname; + frecord.client_short_strasse = rawRecord.client_short_strasse; + frecord.client_short_plz = rawRecord.client_short_plz; + frecord.client_short_ort = rawRecord.client_short_ort; + frecord.key=rawRecord.id+'_'+feldrecords[0].id; + newrecords[x] = frecord; + x++; + } + } + } + this.records = newrecords; + this.recordsLength = newrecords.length; + + } + + + async fetchrecords(length,data) { + //alert('fetchrecords') + alert(this.active_id) + const rawRecords = await this.orm.searchRead('dss.contracts', this.domain, []); + for (const rawRecord of rawRecords) { + records[rawRecord.id] = rawRecord; + } + return records; + } + } \ No newline at end of file diff --git a/static/src/js/dss_screenview_register.js b/static/src/js/dss_screenview_register.js index f0e5fb2..e4c9769 100755 --- a/static/src/js/dss_screenview_register.js +++ b/static/src/js/dss_screenview_register.js @@ -1,30 +1,45 @@ /** @odoo-module **/ -import AbstractView from "web.AbstractView"; -import BasicView from "web.BasicView"; -import view_registry from 'web.view_registry'; +import { registry } from "@web/core/registry"; import { ScreenViewModel } from "./dss_screenview_model"; import { ScreenViewController } from "./dss_screenview_controller"; import { ScreenViewRenderer } from "./dss_screenview_renderer"; +import { ScreenViewArchParser } from "./dss_screenview_archparser"; +import AbstractView from "web.AbstractView"; +import BasicView from "web.BasicView"; +import view_registry from 'web.view_registry'; import RendererWrapper from 'web.RendererWrapper'; -export const HelloWorldView = BasicView.extend({ - viewType: 'screenview', - config: _.extend({}, BasicView.prototype.config, { - Renderer: ScreenViewRenderer - }), +export const ScreenView = { + type: "screenview", - getRenderer(parent, state) { - // alert('getRenderer') - state = Object.assign({}, state, this.rendererParams); - return new RendererWrapper(null, this.config.Renderer, state); + display_name: "ScreenView ", + icon: "fa fa-picture-o", + multiRecord: true, + + ArchParser: ScreenViewArchParser, + Controller: ScreenViewController, + Model: ScreenViewModel, + Renderer: ScreenViewRenderer, + + props: (genericProps, view) => { + const { ArchParser } = view; + const { arch } = genericProps; + const archInfo = new ArchParser().parse(arch); + return { + ...genericProps, + Model: view.Model, + Renderer: view.Renderer, + archInfo, + }; }, -}); +}; -view_registry.add('screenview', HelloWorldView) +registry.category("views").add("screenview", ScreenView); +//view_registry.add('screenview', HelloWorldView) + +return ScreenView; -return HelloWorldView; -ScreenViewModel // /* // /** @odoo-module **/ diff --git a/static/src/js/dss_screenview_renderer.js b/static/src/js/dss_screenview_renderer.js index 08c3326..a0e0970 100755 --- a/static/src/js/dss_screenview_renderer.js +++ b/static/src/js/dss_screenview_renderer.js @@ -4,6 +4,7 @@ import { useEnv, Component, markup, onWillStart,onMounted,onWillUnmount, useRef, import { registry } from "@web/core/registry"; import { browser } from "@web/core/browser/browser"; +var rpc = require('web.rpc') export class ScreenViewRenderer extends Component { aktcontext = ""; @@ -11,16 +12,13 @@ export class ScreenViewRenderer extends Component { setup() { // alert('render '+JSON.stringify(this.props)) this.rpc = useService("rpc"); - //this.action = useService("action"); + this.action = useService("action"); this.env = useEnv(); onWillStart(async () => { await this.willStart(this.props) }); - -// this._onClickGlobal = this._handleClick.bind(this); -// onMounted(() => browser.addEventListener("click", this._onClickGlobal)); -// onWillUnmount(() => browser.removeEventListener("click", this._onClickGlobal)); + } async willStart(props) { @@ -28,106 +26,42 @@ export class ScreenViewRenderer extends Component { await this.render_Screen(props) } - // Event-Handler für den Click //_handleClick(event) { handleClick() { -// if (event.target.classList.contains("tooltip-container")) { + if (event.target.classList.contains("clickcontainer")) { console.log("Div clicked!", event.target); - this.rpc({ - model : 'dss.projects', - method: "go_contract", - args: [event.target.dataset.uuid] - }).then(function (action) { - alert(JSON.stringify(action)) - if (action.error) { - console.error(action.error); - } else { - this.action.doAction(action); - } - }) -// } - } - -/* this.rpc('/dss/gocontract',{ - id: event.target.dataset.uuid - }).then(function (action) { - alert(action) - if (action.error) { - console.error(action.error); - } else { - // Zugriff auf den actionService - const actionService = registry.category("services").get("action"); - actionService.do_action(action).catch((error) => { - console.error("Error executing action:", error); - }); - - } - }).catch((error) => { - console.error("Error fetching action:", error); + console.log(event.target.dataset.uuid); + console.log(event.target.dataset.id); + this.action.doAction({ + type: 'ir.actions.act_window', + res_model: 'dss.contracts', + res_id: parseInt(event.target.dataset.id), + resId: parseInt(event.target.dataset.id), + views: [[false, "form"]], + view_mode: "form", + target: "current", }); - alert("Div clicked!"); - */ - // Lifecycle-Methode: Wird aufgerufen, bevor die Komponente aus dem DOM entfernt wird + }} async render_Screen(props) { - var meins = ""; - var divele = ""; - var templates = [] - var templates = ['MainSection']; - -// document.addEventListener("click",_onclickevent) -// document.addEventListener("change",_onchangeevent) - - if (props.domain != []) { -// alert(' - '+props) - var def1 =this.rpc({ -// model: 'dss.advertisefields.templates', -// method: "get_data", - model: 'dss.projects', - method: "get_data", - args: [[props.context.active_id]], - }).then(function (result) { - var displaycss = 'background-color:#38AFA0;float:left;border-style: solid;border-width: 0.1px;' - const doelement = document.querySelector('.meincanvas') -// document.addEventListener("click",_onclickevent) - const rect = doelement.getBoundingClientRect(); - const topPosition = rect.top //+ window.scrollY; - var maxx = 1920 - var maxy = 1080 - var topstart = topPosition + 60 - var width = window.innerWidth-40; - var scale_faktor = (width / maxx) - var height = Math.round((maxy+40) * scale_faktor) - meins = '
' - if (result.length<2) { - if (result[0].error) { - alert(result[0].error); - } - } else { - result.forEach(element => { - var ele_pos_x=element.pos_x*scale_faktor+20 - var ele_pos_y=element.pos_y*scale_faktor+20//+topstart - var ele_pos_h=element.pos_h*scale_faktor - var ele_pos_w=element.pos_w*scale_faktor - divele = '
' - if (element.btn_image != '') { - divele += ''; - } - divele += '' - divele += '
'+element.feldname+'
' - divele += '
'+element.kunde_1+'
' - divele += '
'+element.kunde_2+'
' - divele += '
'+element.kunde_3+'
' - divele += '
'+element.kunde_4+'
' - divele += '
' - meins = meins + divele - }); - } - _.each(templates, function(template) {doelement.innerHTML= meins}); - }) - } - + var meins = ""; + var divele = ""; + var templates = [] + var templates = ['MainSection']; + console.log('1!!'+JSON.stringify(props)) + console.log(this); + console.log(this.max_h); + var maxx = props.records[0].max_w; + var maxy = props.records[0].max_h; + var width = window.innerWidth-140; + var scale_faktor = (width / maxx) + var height = Math.round((maxy+40) * scale_faktor) + props.screenwidth = width + 40 + props.scale = scale_faktor + props.screenheight = height + 100 + props.records.resId = false + console.log('1!! - '+props); } } diff --git a/static/src/js/dss_tagclick.js b/static/src/js/dss_tagclick.js new file mode 100755 index 0000000..015b2e1 --- /dev/null +++ b/static/src/js/dss_tagclick.js @@ -0,0 +1,47 @@ +/** @odoo-module **/ +import { _t } from 'web.core'; +import { useService } from "@web/core/utils/hooks"; +import { Many2ManyTagsFieldColorEditable } from "@web/views/fields/many2many_tags/many2many_tags_field"; +import { patch } from "@web/core/utils/patch"; +const Dialog = require('web.Dialog'); + +patch(Many2ManyTagsFieldColorEditable.prototype, '/DigitalSignage/static/src/js/dss_tagclick.js',{ + setup(){ + this._super.apply(this,arguments); + this.action = useService("action"); + }, + onBadgeClick(ev, record) { + var copytext = ev.target.innerText; + var buttons = [ + { + text: _t("Ok"), + classes: 'btn-primary', + close: true, + }, + ]; + new Dialog(self, { + size: 'medium', + buttons: [ + { + text: _t("Bearbeiten"), classes: 'btn-primary', close: true, + click: () => { + this.action.doAction({ + type: 'ir.actions.act_window', + res_model: this.props.relation, + res_id: record.data.id, + views: [[false, 'form']], + target: 'current', + }); + }, + }, + { + text: _t("Cancel"), close: true + }, + ], + $content: $('
', { + text: _t(" Feld bearbeiten ?"), + }), + }).open(); + return this._super.apply(this, arguments); + } + }) \ No newline at end of file diff --git a/static/src/js/kanban_button.js b/static/src/js/kanban_button.js index df5f169..0c5db4d 100755 --- a/static/src/js/kanban_button.js +++ b/static/src/js/kanban_button.js @@ -9,6 +9,7 @@ var rpc = require('web.rpc') export class configKanbanController extends KanbanController { setup() { super.setup(); + this.action = useService("action"); } SaveButtonClicked() { alert("") @@ -59,6 +60,26 @@ export class configKanbanController extends KanbanController { args: [], }); } + + newwizzard() { + const queryString = window.location.hash; + const urlParams = new URLSearchParams(queryString); + if (urlParams.get('model') == 'dss.projects') { + var action = rpc.query({ + model: 'dss.projects', + method: 'newwizzard', + args: [], + }); + } + if (urlParams.get('model') == 'dss.contracts') { + var action = rpc.query({ + model: 'dss.contracts', + method: 'newwizzard', + args: [urlParams.get('active_id')], + }); + } + this.action.doAction(action); + } } registry.category("views").add("button_in_kanban", { diff --git a/static/src/scss/dss_nextcloudwidget.scss b/static/src/scss/dss_nextcloudwidget.scss new file mode 100755 index 0000000..49d6a51 --- /dev/null +++ b/static/src/scss/dss_nextcloudwidget.scss @@ -0,0 +1,276 @@ +.datepicker-dropdown { + top: 0; + left: 0; + padding: 4px + } + + .FileExplorerBackground { + background-color: #5b5b5b; + border-color: #000000 ; + border-radius: 5px; + padding: 5px; + height: 400px + } + .FileExplorerHeaderBackground { + background-color: #3b3b3b; + border-color: #000000 ; + border-radius: 5px; + padding: 2px; + height: 35px; + width: 100%; + } + .FileExplorerHeader_fields { + background-color: #5b5bbb; + border-color: #000000 ; + border-radius: 5px; + padding: 5px; + float: left; + margin-left: 2px; + margin-right: 2px; + height: 100%; +} +.FileExplorerHeader_field_type { + width: 10%; +} +.FileExplorerHeader_field_name { + width: 40%; +} +.FileExplorerHeader_field_size { + width: 20%; +} +.FileExplorerHeader_field_date { + width: 27%; +} + +.header_field_type { + color: #fff; +} + +.header_field_name { + color: #fff; +} + +.header_field_size { + color: #fff; +} + +.header_field_date { + color: #fff; +} + +.FileExplorerFilesBackground { + background-color: #5b5b5b; + border-color: #000000 ; + border-radius: 5px; + padding: 5px; + height: 90%; + width: 100%; + overflow: auto; +} + +.FileExplorerFilesLine { + background-color: #3b3b3b; + border-color: #000000 ; + border-radius: 5px; + height: 20px; + width: 100%; +} + +.FileExplorerFilesLine:hover { + background-color: #5b5bbb; +} + +.FileExplorerFilesField { + white-space: nowrap; + overflow:hidden; + text-overflow: ellipsis; + color: #fff; + float: left; +} + +.FileExplorerFilesLineIcon { + padding-left: 2px; + width: 10%; +} + +.FileExplorerFilesLineName { + width: 40%; +} + +.FileExplorerFilesLineSize { + width: 20%; +} + +.FileExplorerFilesLineDate { + width: 27%; +} + +.ImageShowContainerBlur { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; +} + +.ImageShowContainer_shadow { + background-color: #5b5bbb; + position: fixed; + width: 90%; + height: 90%; + top: 120px; + left: 5%; + /* for IE */ + opacity: 0.7; + box-shadow: 0 0 0 1000px rgba(0,0,0,.9); + /* for real browsers */ + box-shadow: 0 0 0 100vmax rgba(0,0,0,.9); + pointer-events: none; +} + +.ImageShowContainer_Header { + background-color: #FFFFFF; + position: fixed; + width: 100%; + height: 40px; + top: 0px; + left: 0px; +} + +.ImageShowContainer_Header_text { + color: #000; + font-size: 20px; + font-weight: bold; + padding-left: 10px; + padding-top: 10px; +} + +.ImageShowContainer { + background-color: #5b5bbb; + position: fixed; + width: 90%; + height: 90%; + top: 50px; + left: 5%; +} + +.ImageShowClose { + position: absolute; + top: 0px; + right: 0px; + background-color: #5b5bbb; + border-radius: 5px; + padding: 2px; + width: 40px; + height: 40px; + text-align: center; + font-size: 20px; + color: #fff; + font-weight: bold; +} + +.ImageShowClose:hover { + background-color: #8b8bbb; +} + +.ImageShowCloseIcon { + color: #fff; + font-size: 20px; +} +.hidden { + display: none; +} +.isvisible { + display: block; +} + +.visible { + display: block; +} + +.ImageShow { + position: relative; + width: 90%; + top: 10px; + left: 10px; +} + +.ContextMenu { + position: absolute; + background-color: #5b5bbb; + border-radius: 5px; + padding: 2px; + width: 200px; + height: auto; + z-index: 9999; +} +.ContextMenuItem { + background-color: #5b5bbb; +} +.ContextMenuItem:hover { + background-color: #8b8bbb; +} +.ContextMenuItem_text { + color: #fff; + font-size: 12px; + padding: 5px; +} + +.ContextMenuItemSeperator { + background-color: #5b5bbb; +} + +.ContextMenuItemSeperatorText { + color: #aaf; + font-size: 10px; + text-decoration: italic; +} + +.FieldSelectMenu { + position: absolute; + background-color: #5b5bbb; + border-radius: 5px; + padding: 2px; + width: 200px; + height: auto; + z-index: 9999; +} +.FieldSelectMenuItem { + background-color: #5b5bbb; +} +.FieldSelectMenuItem:hover { + background-color: #8b8bbb; +} +.FieldSelectMenuItem_text { + color: #fff; + font-size: 12px; + padding: 5px; +} +.FieldSelectMenuItemSeperator { + background-color: #5b5bbb; +} + +.ContextCloudLinkMenuItem_text { + color: #b9f; + font-size: 10px; + padding: -2px; + padding-left:5px; +} + +.BreadcrumbBackground { + background-color: #5b5b5b; + border-color: #000000 ; + border-radius: 5px; + padding: 2px; + padding-left:5px; + height: 25px +} + +.Breadcrumb { + +} + +.Breadcrumb_Text { + color: #b9f; + font-size: 12px; +} diff --git a/static/src/scss/style.scss b/static/src/scss/style.scss index 0a8f50e..1f0be57 100755 --- a/static/src/scss/style.scss +++ b/static/src/scss/style.scss @@ -95,4 +95,37 @@ -ms-transform: scale(1); transform: scale(1); } - \ No newline at end of file + + .screenview_main { + background-color:#000000; + padding:20px; + float:left; + } + + .screenview_field { + background-color:#38AFA0; + float:left; + border-style: solid; + border-width: 0.1px; + position:absolute; + } + + .screenview_image { + width:100%; + height:100%; + } + + .o_dark_panel { + background-color: #000000; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + opacity: 0.7; + z-index: 100; + } + + .hidden { + display: none; + } diff --git a/static/src/xml/dss_nextcloudwidget.xml b/static/src/xml/dss_nextcloudwidget.xml new file mode 100755 index 0000000..62eeab3 --- /dev/null +++ b/static/src/xml/dss_nextcloudwidget.xml @@ -0,0 +1,122 @@ + + +
+ + +
+
+
+ Typ +
+
+ Name +
+
+ Größe +
+
+ Datum +
+
+
+
+
+ + + + +
+
+
\ No newline at end of file diff --git a/static/src/xml/dss_nextcloudwidget_short.xml b/static/src/xml/dss_nextcloudwidget_short.xml new file mode 100755 index 0000000..d188b3a --- /dev/null +++ b/static/src/xml/dss_nextcloudwidget_short.xml @@ -0,0 +1,7 @@ + + +
+ +
+
+
\ No newline at end of file diff --git a/static/src/xml/dss_screenview_controller.xml b/static/src/xml/dss_screenview_controller.xml index 6381d3c..48a4053 100755 --- a/static/src/xml/dss_screenview_controller.xml +++ b/static/src/xml/dss_screenview_controller.xml @@ -4,7 +4,9 @@ -

Test

-
+
+

Scrollraum

+
+ \ No newline at end of file diff --git a/static/src/xml/dss_screenview_renderer.xml b/static/src/xml/dss_screenview_renderer.xml index bfbe299..d6092e2 100755 --- a/static/src/xml/dss_screenview_renderer.xml +++ b/static/src/xml/dss_screenview_renderer.xml @@ -2,8 +2,25 @@ - - +
+ + + + + + +
+ + +
+
+
+
+
+
+
+ +
\ No newline at end of file diff --git a/static/src/xml/form_button.xml b/static/src/xml/form_button.xml index 596ee1f..f0841a4 100755 --- a/static/src/xml/form_button.xml +++ b/static/src/xml/form_button.xml @@ -1,18 +1,14 @@ - -
+
+
+
+
+
+
+ + + + +
+ + + + + + + + + + + + + + + + @@ -138,8 +175,10 @@ - - + + + + @@ -179,7 +218,7 @@ action = records.check_contract_multi_Values() - + dss_advertisefields_fill ir.actions.act_window diff --git a/views/dss_advertisementfields_templates.xml b/views/dss_advertisementfields_templates.xml index 9d61110..9bdc8de 100755 --- a/views/dss_advertisementfields_templates.xml +++ b/views/dss_advertisementfields_templates.xml @@ -7,6 +7,7 @@ + diff --git a/views/dss_contracts.xml b/views/dss_contracts.xml index 3eaf541..f7a93f1 100755 --- a/views/dss_contracts.xml +++ b/views/dss_contracts.xml @@ -128,7 +128,7 @@ dss.contracts - + @@ -179,6 +179,18 @@ + + dss_project_contracts_kanban_inherit + dss.contracts + + + + button_in_kanban + + + + + DigitalSignage Projekt Vertraege ir.actions.act_window @@ -207,6 +219,15 @@