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 531e773..f36fdcd Binary files a/controllers/__pycache__/__init__.cpython-311.pyc and b/controllers/__pycache__/__init__.cpython-311.pyc differ diff --git a/controllers/__pycache__/dss_nextcloudwidget.cpython-311.pyc b/controllers/__pycache__/dss_nextcloudwidget.cpython-311.pyc new file mode 100755 index 0000000..6687d64 Binary files /dev/null and b/controllers/__pycache__/dss_nextcloudwidget.cpython-311.pyc differ 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 4a960b2..5d97a0e Binary files a/controllers/__pycache__/dss_screenview_controller.cpython-311.pyc and b/controllers/__pycache__/dss_screenview_controller.cpython-311.pyc differ 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 1bf332c..7f6a153 Binary files a/models/__pycache__/dss_advertisefields.cpython-311.pyc and b/models/__pycache__/dss_advertisefields.cpython-311.pyc differ diff --git a/models/__pycache__/dss_advertisefields_templates.cpython-311.pyc b/models/__pycache__/dss_advertisefields_templates.cpython-311.pyc old mode 100644 new mode 100755 index 66f2ce7..6cea621 Binary files a/models/__pycache__/dss_advertisefields_templates.cpython-311.pyc and b/models/__pycache__/dss_advertisefields_templates.cpython-311.pyc differ diff --git a/models/__pycache__/dss_contract.cpython-311.pyc b/models/__pycache__/dss_contract.cpython-311.pyc index d69f3d9..f5529f0 100644 Binary files a/models/__pycache__/dss_contract.cpython-311.pyc and b/models/__pycache__/dss_contract.cpython-311.pyc differ diff --git a/models/__pycache__/dss_importinvoicelist.cpython-311.pyc b/models/__pycache__/dss_importinvoicelist.cpython-311.pyc index 2990f54..89b106f 100755 Binary files a/models/__pycache__/dss_importinvoicelist.cpython-311.pyc and b/models/__pycache__/dss_importinvoicelist.cpython-311.pyc differ diff --git a/models/__pycache__/dss_m2mmail.cpython-311.pyc b/models/__pycache__/dss_m2mmail.cpython-311.pyc old mode 100644 new mode 100755 diff --git a/models/__pycache__/dss_projects.cpython-311.pyc b/models/__pycache__/dss_projects.cpython-311.pyc index 5965212..ed129d1 100644 Binary files a/models/__pycache__/dss_projects.cpython-311.pyc and b/models/__pycache__/dss_projects.cpython-311.pyc differ 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 5c6b14b..978ee4d Binary files a/models/__pycache__/dss_settings.cpython-311.pyc and b/models/__pycache__/dss_settings.cpython-311.pyc differ diff --git a/models/__pycache__/dss_systemtypen.cpython-311.pyc b/models/__pycache__/dss_systemtypen.cpython-311.pyc index 13eace5..4e3bf90 100755 Binary files a/models/__pycache__/dss_systemtypen.cpython-311.pyc and b/models/__pycache__/dss_systemtypen.cpython-311.pyc differ diff --git a/models/__pycache__/dss_trigger.cpython-311.pyc b/models/__pycache__/dss_trigger.cpython-311.pyc old mode 100755 new mode 100644 index 4e40a3e..81c230d Binary files a/models/__pycache__/dss_trigger.cpython-311.pyc and b/models/__pycache__/dss_trigger.cpython-311.pyc differ diff --git a/models/__pycache__/dss_web_contracts.cpython-311.pyc b/models/__pycache__/dss_web_contracts.cpython-311.pyc index f374dfe..6286ff9 100644 Binary files a/models/__pycache__/dss_web_contracts.cpython-311.pyc and b/models/__pycache__/dss_web_contracts.cpython-311.pyc differ 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 = "
Test
Scrollraum
+Die Struktur kann erst nach Speichern der Grunddaten eingerichtet werden !