diff --git a/__manifest__.py b/__manifest__.py index 09451e7..6c4b2e5 100755 --- a/__manifest__.py +++ b/__manifest__.py @@ -14,7 +14,14 @@ 'security/groups.xml', 'security/ir.model.access.csv', 'views/menu.xml', - 'views/dss.xml', + 'views/dss_projectstate.xml', + 'views/dss_systemtypen.xml', + 'views/dss_projects.xml', + 'views/dss_ads.xml', + 'views/dss_contracts.xml', + 'views/dss_mediafiles.xml', + 'views/dss_addstructures.xml', + 'views/dss_texts.xml', 'views/mainsystem_view.xml', 'views/company_view.xml', ], diff --git a/models/__pycache__/dss.cpython-311.pyc b/models/__pycache__/dss.cpython-311.pyc index e5951ed..2249ac0 100644 Binary files a/models/__pycache__/dss.cpython-311.pyc and b/models/__pycache__/dss.cpython-311.pyc differ diff --git a/models/dss.py b/models/dss.py index 1e643bb..375e79b 100755 --- a/models/dss.py +++ b/models/dss.py @@ -1,15 +1,33 @@ # -*- coding: utf-8 -*- + import ast import json import re import uuid import logging +import base64 +import subprocess +import tempfile +import easywebdav +import os +import os.path from odoo import api, fields, models, _ +from odoo import tools from odoo.exceptions import ValidationError +from datetime import date _logger = logging.getLogger(__name__) +def _generate_preview_from_binary(self, videofile_file): + cmd = ['ffmpeg', '-i','-','-ss','00:00:01','-vframes','1','-f','image2','-'] + p = subprocess.Popen(cmd,stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + out,err = p.communicate(videofile_file) + if p.returncode != 0: + pass + + return base64.b64encode(out) + class dsscontracts(models.Model): @api.model @@ -39,69 +57,94 @@ class dsscontracts(models.Model): _name = "dss.contracts" _description = "DigitalSignage Vertraege" _rec_name = "contract_auto_name" - _inherit = ['mail.thread'] + _inherit = ['mail.thread','mail.activity.mixin'] uuid = fields.Char(default=lambda self: self._default_uuid(), required=True, readonly=True, copy=False, string='UUID') - contract_id = fields.Char("Kundennummer",store=True) - contract_name = fields.Char('Kurzbezeichnung', required=True) - contract_state = fields.Many2one('dss.contractstate',group_expand='_read_group_stage_ids') + contract_id = fields.Char("Kundennummer",store=True,tracking=True) + contract_name = fields.Char('Kurzbezeichnung', required=True,tracking=True) + contract_state = fields.Many2one('dss.contractstate',group_expand='_read_group_stage_ids',tracking=True) contract_state_order = fields.Integer(related='contract_state.order',store=True) - contract_auto_id = fields.Char("Kundennummer") - contract_auto_name = fields.Char('Vertragskennug') - project = fields.Many2one('dss.projects' , string='Project', store=True) + contract_auto_id = fields.Char("Kundennummer",tracking=True) + contract_auto_name = fields.Char('Vertragskennug',tracking=True) + project = fields.Many2one('dss.projects' , string='Project', store=True,tracking=True) project_id = fields.Integer(related='project.projectid', string='Project ID') - projectIid = fields.Integer('Project IID') - client = fields.Many2one('res.partner',domain="['&',('dsspartner','=',True),('dsspartner_werbung','=',True)]") - client_id = fields.Char("Kundenid") + projectIid = fields.Integer('Project IID',tracking=True) + client = fields.Many2one('res.partner',domain="['&',('dsspartner','=',True),('dsspartner_werbung','=',True)]",tracking=True) + client_id = fields.Char("Kundenid",tracking=True) client_uuid = fields.Char(related="client.dss_uuid") - parent_id = fields.Many2one('dss.contracts', string='Parent Task', index=True) + parent_id = fields.Many2one('dss.contracts', string='Parent Task', index=True,tracking=True) - client_short_company = fields.Char('Firmenname Kunde') - client_short_vorname = fields.Char('Vorname Kunde') - client_short_name = fields.Char('Name Kunde') - client_short_strasse = fields.Char('Strasse Kunde') - client_short_plz = fields.Char('PLZ Kunde') - client_short_ort = fields.Char('Ort Kunde') - client_short_land = fields.Many2one('res.country','Land Kunde') - client_short_email = fields.Char('Email Kunde') - client_short_telefon = fields.Char('Telefon Kunde') - client_short_mobil = fields.Char('Mobilfunk Kunde') - client_short_website = fields.Char('Webseite Kunde') + client_short_company = fields.Char('Firmenname Kunde',tracking=True) + client_short_vorname = fields.Char('Vorname Kunde',tracking=True) + client_short_name = fields.Char('Name Kunde',tracking=True) + client_short_strasse = fields.Char('Strasse Kunde',tracking=True) + client_short_plz = fields.Char('PLZ Kunde',tracking=True) + client_short_ort = fields.Char('Ort Kunde',tracking=True) + client_short_land = fields.Many2one('res.country','Land Kunde',tracking=True) + client_short_email = fields.Char('Email Kunde',tracking=True) + client_short_telefon = fields.Char('Telefon Kunde',tracking=True) + client_short_mobil = fields.Char('Mobilfunk Kunde',tracking=True) + client_short_website = fields.Char('Webseite Kunde',tracking=True) - client_other_projects = fields.Many2many('dss.projects',string='Weitere Projekte') + client_other_projects = fields.Many2many('dss.projects',string='Weitere Projekte',tracking=True) - werbe_feld_selected = fields.Many2many('dss.advertisefields',string='Werbefelder') + werbe_feld_selected = fields.Many2many('dss.advertisefields',string='Werbefelder',tracking=True) - main_runtime = fields.Integer('Gesamtlaufzeit') - split_runtime_count = fields.Integer('Laufzeit Teilungen') - split_runtime_time = fields.Integer('Laufzeit Sekunden') + main_runtime = fields.Integer('Gesamtlaufzeit',tracking=True) + split_runtime_count = fields.Integer('Laufzeit Teilungen',tracking=True) + split_runtime_time = fields.Integer('Laufzeit Sekunden',tracking=True) - contract_date = fields.Date('Vertragsdatum') - start_date = fields.Date('Ausstrahlungsdatum') + contract_date = fields.Date('Vertragsdatum',tracking=True) + start_date = fields.Date('Ausstrahlungsdatum',tracking=True) - runtimesystem = fields.Selection([('M','Monatslaufzeit'),('T','Tagelaufzeit'), ('E','Eventlaufzeit'), ('S','Sonderlaufzeit')]) - runtime_m = fields.Integer('Laufzeit') - runtime_t = fields.Integer('Laufzeit') - runtime_events = fields.Many2many('dss.eventdays') - runtime_divers = fields.Char('Laufzeit') + runtimesystem = fields.Selection([('M','Monatslaufzeit'),('T','Tagelaufzeit'), ('E','Eventlaufzeit'), ('S','Sonderlaufzeit')],tracking=True) + runtime_m = fields.Integer('Laufzeit',tracking=True) + runtime_t = fields.Integer('Laufzeit',tracking=True) + runtime_events = fields.Many2many('dss.eventdays',tracking=True) + runtime_divers = fields.Char('Laufzeit',tracking=True) - - info_account_changes = fields.Boolean('Benarichtigen bei Accountänderungen') - info_spot_changes = fields.Boolean('Benarichtigen bei Spotänderungen') - info_contract_changes = fields.Boolean('Benarichtigen bei Vertragsänderungen') - info_partner_changes = fields.Boolean('Benarichtigen bei Partneränderungen') - info_partner = fields.Many2one('res.partner','Benarichtigung an : ') + paymentsystems = fields.Many2one('dss.paysystems',tracking=True) + intern_info_payment_off = fields.Boolean('Keine Zahl-Benachrichtigungen',tracking=True) - work_state = fields.Many2one('dss.workstate',default=_default_work_state) + base_ad = fields.Many2one('dss.ads',tracking=True) + ads = fields.One2many('dss.ads','contract',tracking=True) + + vnnox_zugang_erstellt = fields.Boolean('Vnnox Zugang ?',tracking=True) + vnnox_zugang_username = fields.Char('Vnnox Username',tracking=True) + vnnox_zugang_password = fields.Char('Vnnox Passwort',tracking=True) + vnnox_zugang_gesendet = fields.Boolean('Vnnox Zugang gesendet?',tracking=True) + + xibo_zugang_erstellt = fields.Boolean('Xibo Zugang ?',tracking=True) + xibo_zugang_username = fields.Char('Xibo Username',tracking=True) + xibo_zugang_password = fields.Char('Xibo Passwort',tracking=True) + vnnox_zugang_gesendet = fields.Boolean('Xibo Zugang gesendet?',tracking=True) + + lmw_zugang_erstellt = fields.Boolean('LMW Zugang ?',tracking=True) + lmw_zugang_username = fields.Char('LMW Username',tracking=True) + lmw_zugang_password = fields.Char('LMW Passwort',tracking=True) + lmw_zugang_gesendet = fields.Boolean('LMW Zugang gesendet?',tracking=True) + + wflow_korrekturabzug = fields.Boolean('Korekturabzug gesendet ?',tracking=True) + wflow_ausstahlung = fields.Boolean('Eingespielt/Ausgestahlt ?',tracking=True) + wflow_aenderung = fields.Boolean('Änderung durchgeführt ?',tracking=True) + + info_account_changes = fields.Boolean('Benachrichtigen bei Accountänderungen',tracking=True) + info_spot_changes = fields.Boolean('Benachrichtigen bei Spotänderungen',tracking=True) + info_contract_changes = fields.Boolean('Benachrichtigen bei Vertragsänderungen',tracking=True) + info_partner_changes = fields.Boolean('Benachrichtigen bei Partneränderungen',tracking=True) + info_partner = fields.Many2one('res.partner','Benachrichtigung an : ',tracking=True) + + work_state = fields.Many2one('dss.workstate',default=_default_work_state,tracking=True) work_state_color = fields.Char(related='work_state.color') work_state_text = fields.Char(related='work_state.statusname') - work_state_info = fields.Char('Zusatzinfo') + work_state_info = fields.Char('Zusatzinfo',tracking=True) - todo_state = fields.Many2one('dss.todostate',default=_default_todo_state) + todo_state = fields.Many2one('dss.todostate',default=_default_todo_state,tracking=True) todo_state_color = fields.Char(related='todo_state.color') todo_state_text = fields.Char(related='todo_state.statusname') - todo_state_info = fields.Char('Zusatzinfo') - todo_state_until = fields.Date('Abarbeiten bis') + todo_state_info = fields.Char('Zusatzinfo',tracking=True) + todo_state_until = fields.Date('Abarbeiten bis',tracking=True) + cloud_contract_directory = fields.Char('Cloud Kunden Ordner',tracking=True) @api.constrains('client_id') def _check_client_id(self) : @@ -192,6 +235,28 @@ class dsscontracts(models.Model): # action['context'] = context return action + def tokampagne(self): + action = self.env['ir.actions.act_window'].with_context({'default_contractid': self.id})._for_xml_id('DigitalSignage.action_dss_ads_view') + context = action['context'] + kampagne=self.env['dss.ads'].search([('contract','=',self.id)],limit=1) + if not kampagne : + defadstate = self.env['dss.adstate'].search([('default','=',True)],limit=1).id + if not defadstate : + defadstate == 1 + kampagne = self.env['dss.ads'].create({'contract': self.id, 'project': self.project.id, 'adname': 'WK '+self.contract_auto_name,'adtype':'MAIN','ad_state':defadstate}) + kampagne.parent_ad = kampagne.id + return { + 'type': 'ir.actions.act_window', + 'view_type':'form', + 'view_mode':'form,tree', + 'res_model':'dss.ads', + 'target':'current', + 'context':'', + 'res_id':kampagne.id, + 'display_name' : 'Änderung zur Werbekampagne '+kampagne.parent_ad.adname, + 'domain':'[("id","=","context[kampagne.id]")]' + } + @api.model def pyaction_view_contract(self): @@ -231,28 +296,31 @@ class dssmain(models.Model): _name = "dss.projects" _description = "DigitalSignage Projekte" _rec_name = "projektname" - _inherit = ['mail.thread'] + _inherit = ['mail.thread','mail.activity.mixin'] projektname = fields.Char('Projektname', required=True) uuid = fields.Char(default=lambda self: self._default_uuid(), required=True, readonly=True, copy=False, string='UUID') - projectid = fields.Integer('Projekt ID') - color = fields.Integer('Color Index') - active = fields.Boolean('Active', default=True) - name = fields.Char('Interner Name', required=True) - aktstatus = fields.Many2one('dss.projectstate',string='Aktueller Status:') - aktstatus_color = fields.Integer(related='aktstatus.color') + projectid = fields.Integer('Projekt ID',tracking=True) + color = fields.Char('Color Index',tracking=True) + active = fields.Boolean('Active', default=True,tracking=True) + name = fields.Char('Interner Name', required=True,tracking=True) + aktstatus = fields.Many2one('dss.projectstate',string='Aktueller Status:',tracking=True) + aktstatus_typ = fields.Selection(related='aktstatus.typ') + aktstatus_color = fields.Char(related='aktstatus.color') aktstatus_icon = fields.Image(related='aktstatus.icon') - description = fields.Text('Beschreibung') - systemname = fields.Many2one('dss.systems') - grundsystemname = fields.Many2one('dss.systemtypen') - grundsystemicon = fields.Image(related='grundsystemname.icon') + description = fields.Text('Beschreibung',tracking=True) + systemname = fields.Many2one('dss.systems',tracking=True) + grundsystemname = fields.Many2one('dss.systemtypen',tracking=True) + grundsystemicon = fields.Image(related='grundsystemname.icon',tracking=True) grundsystemicon5050 = fields.Image(related='grundsystemname.icon_5050') - 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)]") - zeiten_on = fields.Datetime('Einschaltzeit') - zeiten_off = fields.Datetime('Ausschaltzeit') - errichtet_am = fields.Datetime('Errichtungstag') - standort_inout = fields.Selection([('indoor','im Gebäude'), ('outdoor','Aussenbereich'), ('semiindoor','Überdachter Aussenbereich'),('Divers','Andere Art')]); + vertragsschreiber = fields.Many2one('res.partner',domain="['&',('dsspartner','=',True),('dsspartner_vertrieb','=',True)]",tracking=True) + standortpartner = fields.Many2one('res.partner',domain="['&',('dsspartner','=',True),('dsspartner_standort','=',True)]",tracking=True) + vertriebspartner = fields.Many2many('res.partner',domain="['&',('dsspartner','=',True),('dsspartner_vertrieb','=',True)]",tracking=True) + zeiten_on = fields.Datetime('Einschaltzeit',tracking=True) + zeiten_off = fields.Datetime('Ausschaltzeit',tracking=True) + errichtet_am = fields.Datetime('Errichtungstag',tracking=True) + standort_inout = fields.Selection([('indoor','im Gebäude'), ('outdoor','Aussenbereich'), ('semiindoor','Überdachter Aussenbereich'),('Divers','Andere Art')],tracking=True); + + cloud_main_directory = fields.Char('Cloud Projekt Ordner',tracking=True) @api.model def _default_uuid(self): @@ -277,66 +345,66 @@ class dssmain(models.Model): class dssgeraetetypen(models.Model): _name = "dss.geraetetypen" _description = "DigitalSignage Geraetetypen" - _inherit = ['mail.thread'] + _inherit = ['mail.thread','mail.activity.mixin'] _rec_name = "geraetename" geraetename = fields.Char('Geraetename', required=True) uuid = fields.Char(default=lambda self: self._default_uuid(), required=True, readonly=True, copy=False, string='UUID') - geraetetyp = fields.Selection([('SYS','Systemgerät'), ('STE','Steuergerät'), ('DIV','Anderes'), ('ANZ','Anzeigegerät'), ('PLY','Abspielgerät')]) - grundsystem = fields.Many2one('dss.systemtypen', string="Gerät ist nutzbar für") - grundsystem_kennung = fields.Char(string='Kennung', related='grundsystem.kennung') + geraetetyp = fields.Selection([('SYS','Systemgerät'), ('STE','Steuergerät'), ('DIV','Anderes'), ('ANZ','Anzeigegerät'), ('PLY','Abspielgerät')],tracking=True) + grundsystem = fields.Many2one('dss.systemtypen', string="Gerät ist nutzbar für",tracking=True) + grundsystem_kennung = fields.Char(string='Kennung', related='grundsystem.kennung',tracking=True) - farbe = fields.Char('Grundfarbe') + farbe = fields.Char('Grundfarbe',tracking=True) - has_heizung = fields.Boolean('Mit Heizsystem') - has_klima = fields.Boolean('Mit Klimasystem') - has_fan = fields.Boolean('Mit Ventiltorensystem') + has_heizung = fields.Boolean('Mit Heizsystem',tracking=True) + has_klima = fields.Boolean('Mit Klimasystem',tracking=True) + has_fan = fields.Boolean('Mit Ventiltorensystem',tracking=True) - stromzaehler = fields.Many2one('dss.geraetetypen') - stromverbrauch_avg = fields.Integer('Stromverbrauch AVG in W') + stromzaehler = fields.Many2one('dss.geraetetypen',tracking=True) + stromverbrauch_avg = fields.Integer('Stromverbrauch AVG in W',tracking=True) - osvorhanden = fields.Boolean('Mit Betriebssystem') - osname = fields.Char('Betriebssystem') - ostyp = fields.Selection([('Win','Windows'), ('Lin','Linux'), ('And','Android'),('Ras','Raspberry PI'),('Non','Keines bekannt'),('Div','Anderes')]); + osvorhanden = fields.Boolean('Mit Betriebssystem',tracking=True) + osname = fields.Char('Betriebssystem',tracking=True) + ostyp = fields.Selection([('Win','Windows'), ('Lin','Linux'), ('And','Android'),('Ras','Raspberry PI'),('Non','Keines bekannt'),('Div','Anderes')],tracking=True); - lcd_ausrichtung = fields.Selection([('quer','Horizontal/Querformat'), ('hoch','Vertikal/Hochformat'),('Divers','Andere Art')],'LCD Ausrichtung'); - lcd_touch = fields.Boolean('Touchsystem') - lcd_montage = fields.Selection([('WAN','Wandmontage'), ('FUS','Standfuss rollbar'), ('FI1','Boden verankert 1 Fuss'),('FI2','Boden verankert 2 Füsse'),('FIX','Bodenverankert Blockfuss'),('XXX','Sonstige')],'Montage/Befestigung'); - lcd_montage_sonstige = fields.Char('Sonstige Montageart') - lcd_size = fields.Selection([('42','42 Zoll'), ('55','55 Zoll'), ('65','65 Zoll'),('75','75 Zoll'),('10','10.x Zoll'),('00','Sonstige')],'LCD Größe'); - lcd_size_sonstige = fields.Char('LCD Sondergröße') + lcd_ausrichtung = fields.Selection([('quer','Horizontal/Querformat'), ('hoch','Vertikal/Hochformat'),('Divers','Andere Art')],'LCD Ausrichtung',tracking=True); + lcd_touch = fields.Boolean('Touchsystem',tracking=True) + lcd_montage = fields.Selection([('WAN','Wandmontage'), ('FUS','Standfuss rollbar'), ('FI1','Boden verankert 1 Fuss'),('FI2','Boden verankert 2 Füsse'),('FIX','Bodenverankert Blockfuss'),('XXX','Sonstige')],'Montage/Befestigung',tracking=True); + lcd_montage_sonstige = fields.Char('Sonstige Montageart',tracking=True) + lcd_size = fields.Selection([('42','42 Zoll'), ('55','55 Zoll'), ('65','65 Zoll'),('75','75 Zoll'),('10','10.x Zoll'),('00','Sonstige')],'LCD Größe',tracking=True); + lcd_size_sonstige = fields.Char('LCD Sondergröße',tracking=True) - led_geraetetyp = fields.Selection([('MOD','LED Modul'),('NET','Netzgerät'), ('REC','Receiving Karte'), ('STE','Steuerkarte'),('LFT','Lüftertyp')]) - led_module_pixelpitch = fields.Float('Modulpixelpitch') - led_module_breite = fields.Integer('Modulbreite mm') - led_module_hoehe = fields.Integer('Modulhoehe mm') - led_module_pixel_breite = fields.Integer('Modulbreite px') - led_module_pixel_hoehe = fields.Integer('Modulhoehe px') + led_geraetetyp = fields.Selection([('MOD','LED Modul'),('NET','Netzgerät'), ('REC','Receiving Karte'), ('STE','Steuerkarte'),('LFT','Lüftertyp')],tracking=True) + led_module_pixelpitch = fields.Float('Modulpixelpitch',tracking=True) + led_module_breite = fields.Integer('Modulbreite mm',tracking=True) + led_module_hoehe = fields.Integer('Modulhoehe mm',tracking=True) + led_module_pixel_breite = fields.Integer('Modulbreite px',tracking=True) + led_module_pixel_hoehe = fields.Integer('Modulhoehe px',tracking=True) - led_module_system = fields.Selection([('FIX1','Fix Verschraubt'), ('MAG1','Magnetisch haltend'), ('RIG1','Imbus Veriegelt (vorn)'),('SONS','Sonstige')],'Modul Montage/Befestigung'); - led_module_system_sonstige = fields.Char('Modulbefestigung Sonstige') - led_module_kennung = fields.Char('Modulbezeichnung') - led_module_serial = fields.Char('Modulseriennummer') - led_module_vendor = fields.Many2one('res.partner','Modul Hersteller') + led_module_system = fields.Selection([('FIX1','Fix Verschraubt'), ('MAG1','Magnetisch haltend'), ('RIG1','Imbus Veriegelt (vorn)'),('SONS','Sonstige')],'Modul Montage/Befestigung',tracking=True); + led_module_system_sonstige = fields.Char('Modulbefestigung Sonstige',tracking=True) + led_module_kennung = fields.Char('Modulbezeichnung',tracking=True) + led_module_serial = fields.Char('Modulseriennummer',tracking=True) + led_module_vendor = fields.Many2one('res.partner','Modul Hersteller',tracking=True) led_receivingcard_vendor = fields.Many2one('res.partner','Receivingcard Hersteller') led_receivingcard_kennung = fields.Char('Receivingcardtyp') - led_netzteil_typ = fields.Selection([('SNT','Schaltnetzteil'), ('STN','Steckernetzteil'), ('HNT','Hutschienennetzteil'),('INT','Internes Netzteil')],'Netzteil Bauart'); - led_netzteil_vendor = fields.Many2one('res.partner','Netzteil Hersteller') - led_netzteil_kennung = fields.Char('Netzteilkennung') - led_netzteil_spannung = fields.Char('Netzteil Spannung V') - led_netzteil_leistung = fields.Char('Netzteil Leistung W') + led_netzteil_typ = fields.Selection([('SNT','Schaltnetzteil'), ('STN','Steckernetzteil'), ('HNT','Hutschienennetzteil'),('INT','Internes Netzteil')],'Netzteil Bauart',tracking=True); + led_netzteil_vendor = fields.Many2one('res.partner','Netzteil Hersteller',tracking=True) + led_netzteil_kennung = fields.Char('Netzteilkennung',tracking=True) + led_netzteil_spannung = fields.Char('Netzteil Spannung V',tracking=True) + led_netzteil_leistung = fields.Char('Netzteil Leistung W',tracking=True) - lcd_montage = fields.Selection([('WAN','Wandmontage'), ('FUS','Standfuss rollbar'), ('FI1','Boden verankert 1 Fuss'),('FI2','Boden verankert 2 Füsse'),('FIX','Bodenverankert Blockfuss'),('XXX','Sonstige')],'Montage/Befestigung'); - lcd_montage_sonstige = fields.Char('Sonstige Montageart') + lcd_montage = fields.Selection([('WAN','Wandmontage'), ('FUS','Standfuss rollbar'), ('FI1','Boden verankert 1 Fuss'),('FI2','Boden verankert 2 Füsse'),('FIX','Bodenverankert Blockfuss'),('XXX','Sonstige')],'Montage/Befestigung',tracking=True); + lcd_montage_sonstige = fields.Char('Sonstige Montageart',tracking=True) - hw_anzeige = fields.Many2one('dss.geraetetypen',domain="[('geraetetyp','=','ANZ')]") - hw_steuerung = fields.Many2one('dss.geraetetypen',domain="['&',('geraetetyp','=','STE'),('grundsystem_kennung','=','LED')]") - hw_player = fields.Many2one('dss.geraetetypen',domain="[('geraetetyp','=','PLY')]") - hw_umwelt = fields.Many2one('dss.geraetetypen',domain="['|',('geraetetyp','=','SYS'),('geraetetyp','=','DIV')]") + hw_anzeige = fields.Many2one('dss.geraetetypen',domain="[('geraetetyp','=','ANZ')]",tracking=True) + hw_steuerung = fields.Many2one('dss.geraetetypen',domain="['&',('geraetetyp','=','STE'),('grundsystem_kennung','=','LED')]",tracking=True) + hw_player = fields.Many2one('dss.geraetetypen',domain="[('geraetetyp','=','PLY')]",tracking=True) + hw_umwelt = fields.Many2one('dss.geraetetypen',domain="['|',('geraetetyp','=','SYS'),('geraetetyp','=','DIV')]",tracking=True) - zusatz_integrationen = fields.Many2one('dss.geraetetypen') + zusatz_integrationen = fields.Many2one('dss.geraetetypen',tracking=True) @api.model def _default_uuid(self): @@ -349,6 +417,8 @@ class dssgeraetetypen(models.Model): syst.grundsystem_kennung = syst.grundsystem.kennung class dsssystemtypen(models.Model): + + _name = "dss.systemtypen" _inherit = ['mail.thread'] _description = "DigitalSignage Systemtypen" @@ -356,20 +426,35 @@ class dsssystemtypen(models.Model): uuid = fields.Char(default=lambda self: self._default_uuid(), required=True, readonly=True, copy=False, string='UUID') systemname = fields.Char('Systemname', required=True) kennung = fields.Char('Kurzkennung', required=True) - farbe = fields.Integer('Grundfarbe') + farbe = fields.Char('Grundfarbe') icon = fields.Image() - icon_5050 = fields.Image("Icon 50") - + icon_5050 = fields.Image("Icon 50",compute='_compute_icon_5050') + default_adstructure = fields.Many2one('dss.adstructures',String='Standard-Werbeaufbau') @api.model def _default_uuid(self): return str(uuid.uuid4()) + @api.depends('icon') + def _compute_icon_5050(self): + for rec in self: + if rec.icon != False: + _logger.info('compute 50x50 icon for '+rec.uuid) +# rec.mediafile_preview = self._generate_preview_from_binary(base64.decodebytes(rec.mediafile)) +# rec.icon_5050 = tools.image_resize_image(rec.icon,size=(50,50),avoid_if_small=True) + rec.icon_5050 = rec.icon +# if rec.mediafile != False: +# rec.mediafile_preview = self._generate_preview_from_binary(base64.decodebytes(rec.mediafile)) +# rec.mediafile_preview = None + else: + _logger.info('alternative compute '+rec.uuid) + rec.icon_5050 = rec.icon + return class dsssoftware(models.Model): _name = "dss.software" _description = "Softwaresysteme" - _inherit = ['mail.thread'] + _inherit = ['mail.thread','mail.activity.mixin'] _rec_name = "softwarename" # _inherit = ['mail.thread', 'mail.activity.mixin'] uuid = fields.Char(default=lambda self: self._default_uuid(), required=True, readonly=True, copy=False, string='UUID') @@ -385,7 +470,7 @@ class dsssoftware(models.Model): class dsssystems(models.Model): _name = "dss.systems" _description = "DigitalSignage Systemtypen" - _inherit = ['mail.thread'] + _inherit = ['mail.thread','mail.activity.mixin'] _rec_name = "systemname" # _inherit = ['mail.thread', 'mail.activity.mixin'] uuid = fields.Char(default=lambda self: self._default_uuid(), required=True, readonly=True, copy=False, string='UUID') @@ -440,13 +525,14 @@ class dsssystems(models.Model): class dsspprojektstatus(models.Model): _name = "dss.projectstate" _description = "DigitalSignage Projektstatus" - _inherit = ['mail.thread'] + _inherit = ['mail.thread','mail.activity.mixin'] _rec_name = "statusname" # _inherit = ['mail.thread', 'mail.activity.mixin'] uuid = fields.Char(default=lambda self: self._default_uuid(), required=True, readonly=True, copy=False, string='UUID') # uuid = fields.Char('UUID', required=True, translate=True) statusname = fields.Char('Statusname', required=True) - color = fields.Integer(string='Color Index') + color = fields.Char(string='Color Index') + typ = fields.Selection([('NEU','In Bearbeitung'),('WORK','fertig/laufend'),('ERROR','Fehlerhaft/Defekt'),('ARCHIV','veraltet/archiviert')],'Systemzuordnung') icon = fields.Image() @api.model @@ -456,13 +542,31 @@ class dsspprojektstatus(models.Model): class dsscontractstatus(models.Model): _name = "dss.contractstate" _description = "DigitalSignage Vertragsstatus" - _inherit = ['mail.thread'] + _inherit = ['mail.thread','mail.activity.mixin'] _rec_name = "statusname" # _inherit = ['mail.thread', 'mail.activity.mixin'] uuid = fields.Char(default=lambda self: self._default_uuid(), required=True, readonly=True, copy=False, string='UUID') # uuid = fields.Char('UUID', required=True, translate=True) statusname = fields.Char('Statusname', required=True) - color = fields.Integer(string='Color Index') + color = fields.Char(string='Color Index') + icon = fields.Image() + order = fields.Integer('Reihenfolge') + + @api.model + def _default_uuid(self): + return str(uuid.uuid4()) + +class dssadstatus(models.Model): + _name = "dss.adstate" + _description = "DigitalSignage Werbeaktions-Status" + _inherit = ['mail.thread','mail.activity.mixin'] + _rec_name = "statusname" +# _inherit = ['mail.thread', 'mail.activity.mixin'] + uuid = fields.Char(default=lambda self: self._default_uuid(), required=True, readonly=True, copy=False, string='UUID') +# uuid = fields.Char('UUID', required=True, translate=True) + statusname = fields.Char('Statusname', required=True) + color = fields.Char(string='Color Index') + default = fields.Boolean('Standardwert ?') icon = fields.Image() order = fields.Integer('Reihenfolge') @@ -474,7 +578,7 @@ class dsscontractstatus(models.Model): class dssworkstatus(models.Model): _name = "dss.workstate" _description = "DigitalSignage Bearbeitungsstatus" - _inherit = ['mail.thread'] + _inherit = ['mail.thread','mail.activity.mixin'] _rec_name = "statusname" # _inherit = ['mail.thread', 'mail.activity.mixin'] uuid = fields.Char(default=lambda self: self._default_uuid(), required=True, readonly=True, copy=False, string='UUID') @@ -490,7 +594,7 @@ class dssworkstatus(models.Model): class dsseventdays(models.Model): _name = "dss.eventdays" _description = "DigitalSignage EventSpieltage" - _inherit = ['mail.thread'] + _inherit = ['mail.thread','mail.activity.mixin'] _rec_name = "eventname" # _inherit = ['mail.thread', 'mail.activity.mixin'] uuid = fields.Char(default=lambda self: self._default_uuid(), required=True, readonly=True, copy=False, string='UUID') @@ -509,7 +613,7 @@ class dsseventdays(models.Model): class dsstodostatus(models.Model): _name = "dss.todostate" _description = "DigitalSignage Bearbeitungsschritte" - _inherit = ['mail.thread'] + _inherit = ['mail.thread','mail.activity.mixin'] _rec_name = "statusname" # _inherit = ['mail.thread', 'mail.activity.mixin'] uuid = fields.Char(default=lambda self: self._default_uuid(), required=True, readonly=True, copy=False, string='UUID') @@ -526,7 +630,7 @@ class dsstodostatus(models.Model): class dssadvertisefields(models.Model): _name = "dss.advertisefields" _description = "DigitalSignage Werbefelder" - _inherit = ['mail.thread'] + _inherit = ['mail.thread','mail.activity.mixin'] _rec_name = "feldname" # _inherit = ['mail.thread', 'mail.activity.mixin'] uuid = fields.Char(default=lambda self: self._default_uuid(), required=True, readonly=True, copy=False, string='UUID') @@ -542,5 +646,386 @@ class dssadvertisefields(models.Model): def _default_uuid(self): return str(uuid.uuid4()) +class dsstexts(models.Model): + _name = "dss.texts" + _description = "DigitalSignage Standard texte" + _inherit = ['mail.thread','mail.activity.mixin'] + _rec_name = "textname" +# _inherit = ['mail.thread', 'mail.activity.mixin'] + uuid = fields.Char(default=lambda self: self._default_uuid(), required=True, readonly=True, copy=False, string='UUID') +# uuid = fields.Char('UUID', required=True, translate=True) + text_id = fields.Char('Kennung', required=True) + textname = fields.Char('Textname', required=True) + description = fields.Text('Text') + + @api.model + def _default_uuid(self): + return str(uuid.uuid4()) + +class dsspaysystemfields(models.Model): + _name = "dss.paysystem_fields" + _description = "DigitalSignage Abrechnungsarten_felder" + _inherit = ['mail.thread','mail.activity.mixin'] + _rec_name = "feldname" +# _inherit = ['mail.thread', 'mail.activity.mixin'] + uuid = fields.Char(default=lambda self: self._default_uuid(), required=True, readonly=True, copy=False, string='UUID') +# uuid = fields.Char('UUID', required=True, translate=True) + feldname = fields.Char('Abrechnungs_Feldname', required=True) + payonfieldset = fields.Selection([('VE','Vertragseingang'),('KA','Korrekturabzug'),('ES','Einspielung'),('CH','Jede Änderung'),('XH','X, Änderung')]) + changecount = fields.Integer('Änderungsnummer') + description = fields.Text('EventBeschreibung') + amount = fields.Float('Kosten') + + @api.model + def _default_uuid(self): + return str(uuid.uuid4()) +class dssmediatypes(models.Model): + _name = "dss.mediatypes" + _description = "DigitalSignage Datei-Medientypen" + _inherit = ['mail.thread','mail.activity.mixin'] + _rec_name = "medianame" +# _inherit = ['mail.thread', 'mail.activity.mixin'] + uuid = fields.Char(default=lambda self: self._default_uuid(), required=True, readonly=True, copy=False, string='UUID') +# uuid = fields.Char('UUID', required=True, translate=True) + medianame = fields.Char('Medien Name', required=True) + mediatype = fields.Selection([('IMG_J','Bild'),('VID_4','MP4 Video'),('FIL_X','belieb. Datei')]) + description = fields.Text('Medien Beschreibung') + cloudlink = fields.Char('Cloud Urverzeichnis') + maxsize_kb = fields.Integer('Maximale Größe KB') + maxsize_w = fields.Integer('Maximale Pixel W') + maxsize_h = fields.Integer('Maximale Pixel H') + + @api.model + def _default_uuid(self): + return str(uuid.uuid4()) + +class dssmediarelations(models.Model): + + def _generate_preview_from_binary(self, videofile_file): + cmd = ['ffmpeg', '-i','-','-ss','00:00:01','-vframes','1','-f','image2','-'] + p = subprocess.Popen(cmd,stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + out,err = p.communicate(videofile_file) + if p.returncode != 0: + pass + + return base64.b64encode(out) + + def _generate_preview_and_save_from_binary(self, videofile_file, videofile_file_name:str): + cmd = ['ffmpeg', '-i','-','-ss','00:00:01','-vframes','1','-f','image2','-'] + p = subprocess.Popen(cmd,stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + out,err = p.communicate(videofile_file) + if p.returncode != 0: + _logger.info(videofile_file_name) + fileobj = open(videofile_file_name,"xb") + fileobj.write(out) + fileobj.close() + pass + + return base64.b64encode(out) + + + _name = "dss.mediarelations" + _description = "DigitalSignage Kampagne-Medien-Zuordnung" + _inherit = ['mail.thread','mail.activity.mixin'] + _rec_name = "relname" +# _inherit = ['mail.thread', 'mail.activity.mixin'] + uuid = fields.Char(default=lambda self: self._default_uuid(), required=True, readonly=True, copy=False, string='UUID') +# uuid = fields.Char('UUID', required=True, translate=True) + kampagne = fields.Many2one('dss.ads',string='Kampagne') + mediatype = fields.Many2one('dss.mediatypes',string='Medientyp') + mediatype_type = fields.Selection(related='mediatype.mediatype',string='Medientyp') + relname = fields.Char('Relationsname') + kampagnen_uuid = fields.Char('Kampagne') + mediatype_uuid = fields.Char('Medientyp') + mediafile = fields.Binary('Datei',attachment=True) + mediafile_attachment = fields.Many2one('ir.attachment',string='Datei') + mediafile_file = fields.Char('Dateiname') + mediafile_preview = fields.Binary('Datei Vorschau',compute='_compute_media_preview') + secured_ro = fields.Boolean('Gesperrt ro') + used_ro = fields.Boolean('Benutzt ro') + + @api.onchange('mediafile') + def _onchange_mediafile(self): + restr = 'keine' + for record in self : + resstr = record.uuid + _logger.info(record.mediafile) + if record.mediafile != False : + _logger.info('Generating File '+resstr) + if os.path.isfile(record.mediafile_file) : + os.remove(record.mediafile_file) + fileobj = open(record.mediafile_file,"xb") + fileobj.write(base64.decodebytes(record.mediafile)) + fileobj.close() + _logger.info(resstr+' File generated') + _logger.info(' Projekt : '+record.kampagne.contract.project.uuid) + _logger.info(' Keine Datei ') + return + + @api.model + def _default_uuid(self): + return str(uuid.uuid4()) + + @api.depends('mediafile_file') + def _compute_media_preview(self): + for rec in self: + if rec.mediafile != False: + _logger.info('compute image for '+rec.uuid) +# rec.mediafile_preview = self._generate_preview_from_binary(base64.decodebytes(rec.mediafile)) + rec.mediafile_preview = self._generate_preview_and_save_from_binary(base64.decodebytes(rec.mediafile),rec.mediafile_file+'_prv') +# if rec.mediafile != False: +# rec.mediafile_preview = self._generate_preview_from_binary(base64.decodebytes(rec.mediafile)) +# rec.mediafile_preview = None + else: + _logger.info('alternative compute '+rec.uuid) + rec.mediafile_preview = rec.mediafile + return + +class dssadstructures(models.Model): + _name = "dss.adstructures" + _description = "DigitalSignage Werbestrukturen" + _inherit = ['mail.thread','mail.activity.mixin'] + _rec_name = "structurename" +# _inherit = ['mail.thread', 'mail.activity.mixin'] + uuid = fields.Char(default=lambda self: self._default_uuid(), required=True, readonly=True, copy=False, string='UUID') +# uuid = fields.Char('UUID', required=True, translate=True) + structurename = fields.Char('Struktur Name', required=True) + kampagnen_uuid = fields.Char('Kampagne') + description = fields.Text('Struktur Beschreibung') + medias = fields.Many2many('dss.mediatypes',string='Enthaltene Dateien') + + @api.model + def _default_uuid(self): + return str(uuid.uuid4()) + + +class dsspaysystems(models.Model): + _name = "dss.paysystems" + _description = "DigitalSignage Abrechnungsarten" + _inherit = ['mail.thread','mail.activity.mixin'] + _rec_name = "payname" +# _inherit = ['mail.thread', 'mail.activity.mixin'] + uuid = fields.Char(default=lambda self: self._default_uuid(), required=True, readonly=True, copy=False, string='UUID') +# uuid = fields.Char('UUID', required=True, translate=True) + payname = fields.Char('Abrechnungsname', required=True) + payonset = fields.Many2many('dss.paysystem_fields') + description = fields.Text('EventBeschreibung') + send_info = fields.Boolean('Informationsemail senden ?') + send_info_to = fields.Many2one('res.users') + send_info_template = fields.Many2one('mail.template') + amount = fields.Float('Kosten') + icon = fields.Image() + + @api.model + def _default_uuid(self): + return str(uuid.uuid4()) + + +class dsscontractads(models.Model): + + + def _default_work_state(self): + ds=self.env['dss.workstate'].search([('statusname','=','Neu')],limit=1).id + _logger.debug(ds) + return ds + + def _default_work_state_color(self): + ds=self.env['dss.workstate'].search([('statusname','=','Neu')],limit=1).color + _logger.info(ds) + return ds + + def _default_todo_state(self): + ds=self.env['dss.todostate'].search([('statusnr','=','0')],limit=1).id + if not ds : ds = 1 + _logger.debug(ds) +# ds = + return ds + + @api.model + def create(self,vals): + result = super().create(vals) + self['date_create'] = date.today() + self['user_create'] = self.env.user.name + return result + + _name = "dss.ads" + _description = "DigitalSignage Werbekampagnen Phasen" + _inherit = ['mail.thread','mail.activity.mixin'] + _rec_name = "adname" +# _inherit = ['mail.thread', 'mail.activity.mixin'] + uuid = fields.Char(default=lambda self: self._default_uuid(), required=True, readonly=True, copy=False, string='UUID') + + date_create = fields.Date('Erstellungsdatum',default=lambda self: self._default_create_date()) + date_lastedit = fields.Date('Änderungsdatum') + user_create = fields.Char('Erstellungsuser',default=lambda self: self._default_create_user()) + user_lastedit = fields.Char('Änderungsuser') +# uuid = fields.Char('UUID', required=True, translate=True) + adname = fields.Char('Kampagnenname', required=True,track_visibility='onchange',tracking=True) + adtype = fields.Selection([('MAIN','Ersteinrichtung'),('KCHN','Änderung durch Kunde'),('LCHN','Änderung durch Logumedia'),('SONS','Sonstige Änderung')],track_visibility='onchange',tracking=True) + adpos = fields.Integer('Reihenfolge',default=lambda self: self._default_adpos(),track_visibility='onchange',tracking=True) + + contract = fields.Many2one('dss.contracts',store=True,track_visibility='onchange',tracking=True) + contract_id = fields.Char(related='contract.contract_id') + contract_name = fields.Char(related='contract.contract_name') + + project = fields.Many2one('dss.projects' , string='Project', store=True,track_visibility='onchange',tracking=True) + project_id = fields.Integer(related='project.projectid', string='Project ID') + + parent_ad = fields.Many2one('dss.ads' , string='UrsprungsWerbung', store=True) + parent_ad_uuid = fields.Char(related='parent_ad.uuid') + description = fields.Text('Beschreibung',track_visibility='onchange') + amount = fields.Float('Extrakosten') + order = fields.Integer('Reihenfolge') + + work_state = fields.Many2one('dss.workstate',default=_default_work_state,tracking=True) + work_state_color = fields.Char(related='work_state.color') + work_state_text = fields.Char(related='work_state.statusname') + work_state_info = fields.Char('Zusatzinfo') + + ad_state = fields.Many2one('dss.adstate',tracking=True) + ad_state_color = fields.Char(related='ad_state.color') + + todo_state = fields.Many2one('dss.todostate',default=_default_todo_state,tracking=True) + todo_state_color = fields.Char(related='todo_state.color') + todo_state_text = fields.Char(related='todo_state.statusname') + todo_state_info = fields.Char('Zusatzinfo') + todo_state_until = fields.Date('Abarbeiten bis') + + mediastructure = fields.Many2one('dss.adstructures',string='Systemaufbau',tracking=True) + mediarelations = fields.One2many('dss.mediarelations','kampagne',tracking=True) + + cloud_add_directory = fields.Char('Cloud KundenKampagnen Ordner',tracking=True) + + @api.model + def _default_uuid(self): + return str(uuid.uuid4()) + + + def _default_create_date(self): + return str(date.today()) + + def _default_create_user(self): + return str(self.env.user.name) + + def _default_adpos(self): + pos = self.env['dss.ads'].search_count([('contract_id','=',self.contract.id)]) + 1 + return str(pos) + + @api.onchange('mediastructure') + def _onchange_mediastructure_id(self): + restr = 'keine' + for record in self : + resstr = record.mediastructure.uuid + _logger.info(resstr) + mtyp = self.env['dss.adstructures'].search([("uuid","=",str(record.mediastructure.uuid))]) + resstr = mtyp.structurename + _logger.info(resstr) + _logger.info(record.mediastructure.uuid) + foundused = self.env['dss.mediarelations'].search_count(["&",('kampagnen_uuid','=',record.uuid),'|',('secured_ro','=',True),('used_ro','=',True)]) + if foundused == 0 : + self.env['dss.mediarelations'].search([('kampagnen_uuid','=',record.uuid)]).unlink() + for media in mtyp.medias : + resstr = media.medianame + self.env['dss.mediarelations'].create({'kampagne': record.id,'relname':resstr,'mediatype':media.id,'kampagnen_uuid':record.uuid}) + _logger.info(resstr) + else : + _logger.info('change - Canceled !') + raise ValidationError(_("Datensatz kann nicht gewechselt werden ! Es sind benutzt Medien in der Kampagne ! Bitte erst diese freigeben !")) + self.date_lastedit = str(date.today()) + self.mediastructure = mtyp + + def pyaction_view_ad_details(self): + action = self.env['ir.actions.act_window'].with_context({'default_adid': self.id})._for_xml_id('DigitalSignage.action_dss_ads_view') +# action['display_name'] = self.ad_name +# action['domain'] = '[["projectid","=","4"]]' +# context = action['context'].replace('', str(self.id)) +# context = ast.literal_eval(context) +# context.update({ +# 'create': self.active, +# 'active_test': self.active +# }) +# action['context'] = context + return { + 'type': 'ir.actions.act_window', + 'view_type':'form', + 'view_mode':'form,tree', + 'res_model':'dss.ads', + 'target':'current', + 'context':'', + 'res_id':self.id, + 'display_name' : ' '+self.adname, + 'domain':'' + } + + return action + + def pydoviewallads(self): + action = self.env['ir.actions.act_window'].with_context({'default_adid': self.id})._for_xml_id('DigitalSignage.act_dss_ads_view_full') + action['display_name'] = self.contract_name +# action['domain'] = '[["projectid","=","4"]]' +# context = action['context'].replace('', str(self.id)) +# context = ast.literal_eval(context) +# context.update({ +# 'create': self.active, +# 'active_test': self.active +# }) +# action['context'] = context +# return { +# 'type': 'ir.actions.act_window', +# 'view_type':'form', +# 'view_mode':'form,tree', +# 'res_model':'dss.ads', +# 'target':'current', +# 'context':'', +# 'res_id':kampagne.id, +# 'display_name' : 'Werbekampagne '+kampagne.adname, +# 'domain':'[("contract","=","context[active_id]")]' +# } +# return action + return { + 'type': 'ir.actions.act_window', + 'view_type':'kanban', + 'view_mode':'kanban', + 'res_model':'dss.ads', + 'target':'current', + 'context':'{"group_by":["parent_ad"]}', +# 'res_id':self.parent_ad, + 'display_name' : 'Übersicht für '+self.adname, + 'domain':[("contract","=",self.contract.id)] + } + + def pydonewad(self): + defadstate = self.env['dss.adstate'].search([('default','=',True)],limit=1).id + if not defadstate : + defadstate == 1 + newkampagne = self.env['dss.ads'].create({'contract': self.contract.id, 'project': self.project.id, 'adname': 'AD_'+self.adname,'parent_ad':self.id, 'adtype':'KCHN','ad_state':defadstate }) +# action = self.env['ir.actions.act_window'].with_context({'default_contractid': newkampagne.id})._for_xml_id('DigitalSignage.act_dss_ads_view_contract') +# action['display_name'] = self.contract_name +# action['domain'] = '[["projectid","=","4"]]' +# context = action['context'].replace('', str(self.id)) +# context = ast.literal_eval(context) +# context.update({ +# 'create': self.active, +# 'active_test': self.active +# }) +# action['context'] = context + return { + 'type': 'ir.actions.act_window', + 'view_type':'form', + 'view_mode':'form,tree', + 'res_model':'dss.ads', + 'target':'current', + 'context':'', + 'res_id':newkampagne.id, + 'display_name' : 'Änderung zur Werbekampagne '+newkampagne.parent_ad.adname, + 'domain':'[("id","=","context[newkampagne.id]")]' + } + + return action + + def setStandardText(self,tid): + text = self.env['dss.texts'].search([('text_id','=',tid)], limit=1) + if text: + self.write({'description':text.description}) diff --git a/security/ir.model.access.csv b/security/ir.model.access.csv index fca5190..e0cf7c4 100755 --- a/security/ir.model.access.csv +++ b/security/ir.model.access.csv @@ -9,5 +9,13 @@ digitalsignage_dss_contractstate_group_user,access.dss.contractstate,model_dss_c digitalsignage_dss_workstate_group_user,access.dss.workstate,model_dss_workstate,base.group_user,1,1,1,1 digitalsignage_dss_todostate_group_user,access.dss.todostate,model_dss_todostate,base.group_user,1,1,1,1 digitalsignage_dss_contracts_group_user,access.dss.contracts,model_dss_contracts,base.group_user,1,1,1,1 +digitalsignage_dss_adstate_group_user,access.dss.adstate,model_dss_adstate,base.group_user,1,1,1,1 digitalsignage_dss_eventdays_group_user,access.dss.eventdays,model_dss_eventdays,base.group_user,1,1,1,1 +digitalsignage_dss_paysystems_group_user,access.dss.paysystems,model_dss_paysystems,base.group_user,1,1,1,1 +digitalsignage_dss_paysystem_fields_group_user,access.dss.paysystem_fields,model_dss_paysystem_fields,base.group_user,1,1,1,1 +digitalsignage_dss_ads_fields_group_user,access.dss.ads,model_dss_ads,base.group_user,1,1,1,1 +digitalsignage_dss_adstructures_group_user,access.dss.adstructures,model_dss_adstructures,base.group_user,1,1,1,1 +digitalsignage_dss_mediatypes_group_user,access.dss.mediatypes,model_dss_mediatypes,base.group_user,1,1,1,1 +digitalsignage_dss_texts_group_user,access.dss.texts,model_dss_texts,base.group_user,1,1,1,1 +digitalsignage_dss_mediarelations_group_user,access.dss.mediarelations,model_dss_mediarelations,base.group_user,1,1,1,1 digitalsignage_dss_advertisefields_group_user,access.dss.advertisefields,model_dss_advertisefields,base.group_user,1,1,1,1 \ No newline at end of file diff --git a/static/images/tree_r.png b/static/images/tree_r.png new file mode 100644 index 0000000..dfbb593 Binary files /dev/null and b/static/images/tree_r.png differ diff --git a/static/images/tree_ud.png b/static/images/tree_ud.png new file mode 100644 index 0000000..ccb30fc Binary files /dev/null and b/static/images/tree_ud.png differ diff --git a/views/.mc.menu b/views/.mc.menu new file mode 100644 index 0000000..f82ee7a --- /dev/null +++ b/views/.mc.menu @@ -0,0 +1,379 @@ +shell_patterns=0 + +############################################################################## +# %% The % character +# %f The current file (if non-local vfs, file will be copied locally and +# %f will be full path to it) +# %p The current file +# %d The current working directory +# %s "Selected files"; the tagged files if any, otherwise the current file +# %t Tagged files +# %u Tagged files (and they are untagged on return from expand_format) +# %view Runs the commands and pipes standard output to the view command +# If %view is immediately followed by '{', recognize keywords +# ascii, hex, nroff and unform +# +# If the format letter is in uppercase, it refers to the other panel +# +# With a number followed the % character you can turn quoting on (default) +# and off. For example: +# %f quote expanded macro +# %1f ditto +# %0f don't quote expanded macro +############################################################################## + ++ ! t t +@ Do something on the current file + CMD=%{Enter command} + $CMD %f + ++ t t +@ Do something on the tagged files + CMD=%{Enter command} + for i in %t ; do + $CMD "$i" + done + +0 Edit a bug report and send it to root + I=`mktemp "${MC_TMPDIR:-/tmp}/mail.XXXXXX"` || exit 1 + ${EDITOR-vi} "$I" + test -r "$I" && mail root < "$I" + rm -f "$I" + +=+ f \.1$ | f \.3$ | f \.4$ | f \.5$ | f \.6$ | f \.7$ | f \.8$ | f \.man$ & t r +1 Display the file with roff -man + %view{ascii,nroff} roff -c -Tlatin1 -mandoc %f + +2 Call the info hypertext browser + info + += t d +3 Compress the current subdirectory (tar.gz) + Pwd=`basename %d /` + echo -n "Name of the compressed file (without extension) [$Pwd]: " + read tar + [ "$tar"x = x ] && tar="$Pwd" + cd .. && \ + tar cf - "$Pwd" | gzip -f9 > "$tar.tar.gz" && \ + echo "../$tar.tar.gz created." + +4 Compress the current subdirectory (tar.bz2) + Pwd=`basename %d /` + echo -n "Name of the compressed file (without extension) [$Pwd]: " + read tar + [ "$tar"x = x ] && tar="$Pwd" + cd .. && \ + tar cf - "$Pwd" | bzip2 -f > "$tar.tar.bz2" && \ + echo "../$tar.tar.bz2 created." + +5 Compress the current subdirectory (tar.7z) + Pwd=`basename %d /` + echo -n "Name of the compressed file (without extension) [$Pwd]: " + read tar + [ "$tar"x = x ] && tar="$Pwd" + cd .. && \ + tar cf - "$Pwd" | 7za a -si "$tar.tar.7z" && \ + echo "../$tar.tar.7z created." + +6 Compress the current subdirectory (tar.xz) + Pwd=`basename %d /` + echo -n "Name of the compressed file (without extension) [$Pwd]: " + read tar + [ "$tar"x = x ] && tar="$Pwd" + cd .. && \ + tar cf - "$Pwd" | xz -f > "$tar.tar.xz" && \ + echo "../$tar.tar.xz created." + +7 Compress the current subdirectory (tar.zst) + Pwd=`basename %d /` + echo -n "Name of the compressed file (without extension) [$Pwd]: " + read tar + [ "$tar"x = x ] && tar="$Pwd" + cd .. && \ + tar cf - "$Pwd" | zstd -f > "$tar.tar.zst" && \ + echo "../$tar.tar.zst created." + += f \.c$ & t r ++ f \.c$ & t r & ! t t +c Compile and link current .c file + make "`basename %f .c`" 2>/dev/null || cc -O -o "`basename %f .c`" %f + ++ t r & ! t t +a Append file to opposite + cat %f >> %D/%f + ++ t t +A Append files to opposite files + for i in %t ; do + cat "$i" >> %D/"$i" + done + ++ t r & ! t t +d Delete file if a copy exists in the other directory. + if [ %d = %D ]; then + echo "The two directories must be different." + exit 1 + fi + if [ -f %D/%f ]; then # if two of them, then + if cmp -s %D/%f %f; then + rm %f && echo %f": DELETED." + else + echo %f" and "%D/%f" differ: NOT deleted." + echo -n "Press RETURN " + read key + fi + else + echo %f": No copy in "%D/%f": NOT deleted." + fi + ++ t t +D Delete tagged files if a copy exists in the other directory. + if [ %d = %D ]; then + echo "The two directores must be different." + exit 1 + fi + for i in %t ; do + if [ -f %D/"$i" ]; then + SUM1=`sum "$i"` + SUM2=`sum %D/"$i"` + if [ "$SUM1" = "$SUM2" ]; then + rm "$i" && echo "${i}: DELETED." + else + echo "$i and "%D"/$i differ: NOT deleted." + fi + else + echo "$i has no copy in "%D": NOT deleted." + fi + done + +m View manual page + MAN=%{Enter manual name} + %view{ascii,nroff} MANROFFOPT='-c -Tlatin1' MAN_KEEP_FORMATTING=1 man -P cat "$MAN" + += f \.gz$ & t r ++ ! t t +n Inspect gzip'ed newsbatch file + dd if=%f bs=1 skip=12 | zcat | ${PAGER-more} + # assuming the cunbatch header is 12 bytes long. + += t r & ++ ! t t +h Strip headers from current newsarticle + CHECK=`awk '{print $1 ; exit}' %f` 2>/dev/null + case "$CHECK" in + Newsgroups:|Path:) + I=`mktemp "${MC_TMPDIR:-/tmp}/news.XXXXXX"` || exit 1 + cp %f "$I" && sed '/^'"$CHECK"' /,/^$/d' "$I" > %f + [ "$?" = "0" ] && rm "$I" + echo %f": header removed." + ;; + *) + echo %f" is not a news article." + ;; + esac + ++ t t +H Strip headers from the marked newsarticles + for i in %t ; do + CHECK=`awk '{print $1 ; exit}' "$i"` 2>/dev/null + WFILE=`mktemp "${MC_TMPDIR:-/tmp}/news.XXXXXX"` || exit 1 + case "$CHECK" in + Newsgroups:|Path:) + cp "$i" "$WFILE" && sed '/^'"$CHECK"' /,/^$/d' "$WFILE" > "$i" + if [ "$?" = "0" ]; then + rm "$WFILE"; echo "$i header removed. OK." + else + echo "Oops! Please check $i against $WFILE." + fi + ;; + *) + echo "$i skipped: Not a news article." + ;; + esac + done + += t r ++ ! t t +r Copy file to remote host + echo -n "To which host?: " + read Host + echo -n "To which directory on $Host?: " + read Dir + rcp -p %f "${Host}:${Dir}" + ++ t t +R Copy files to remote host (no error checking) + echo -n "Copy files to which host?: " + read Host + echo -n "To which directory on $Host? :" + read Dir + rcp -pr %u "${Host}:${Dir}" + += f \.tex$ & t r ++ f \.tex$ & t r & ! t t +t Run latex on file and show it with xdvi + latex %f && xdvi "`basename %f .tex`".dvi + +=+ f ^part | f ^Part | f uue & t r ++ t t +U Uudecode marked news articles (needs work) + ( + for i in %t ; do # strip headers + FIRST=`awk '{print $1 ; exit}' "$i"` + cat "$i" | sed '/^'"$FIRST"' /,/^$/d' + done + ) | sed '/^$/d' | sed -n '/^begin 6/,/^end$/p' | uudecode + if [ "$?" != "0" ]; then + echo "Cannot decode "%t"." + fi + echo "Please test the output file before deleting anything." + +=+ f \.tar\.gz$ | f \.tar\.z$ | f \.tgz$ | f \.tpz$ | f \.tar\.lz$ | f \.tar\.lz4$ | f \.tar\.lzma$ | f \.tar\.7z$ | f \.tar\.xz$ | f \.tar\.zst | f \.tar\.Z$ | f \.tar\.bz2$ & t rl +x Extract the contents of a compressed tar file + unset PRG + case %f in + *.tar.7z) PRG="7za e -so";; + *.tar.bz2) PRG="bunzip2 -c";; + *.tar.gz|*.tar.z|*.tgz|*.tpz|*.tar.Z) PRG="gzip -dc";; + *.tar.lz) PRG="lzip -dc";; + *.tar.lz4) PRG="lz4 -dc";; + *.tar.lzma) PRG="lzma -dc";; + *.tar.xz) PRG="xz -dc";; + *.tar.zst) PRG="zstd -dc";; + *) exit 1;; + esac + $PRG %f | tar xvf - + += t r ++ ! t t +y Gzip or gunzip current file + unset DECOMP + case %f in + *.gz|*.[zZ]) DECOMP=-d;; + esac + # Do *not* add quotes around $DECOMP! + gzip $DECOMP -v %f + ++ t t +Y Gzip or gunzip tagged files + for i in %t ; do + unset DECOMP + case "$i" in + *.gz|*.[zZ]) DECOMP=-d;; + esac + gzip $DECOMP -v "$i" + done + ++ ! t t +b Bzip2 or bunzip2 current file + unset DECOMP + case %f in + *.bz2) DECOMP=-d;; + esac + bzip2 $DECOMP -v %f + ++ t t +B Bzip2 or bunzip2 tagged files + for i in %t ; do + unset DECOMP + case "$i" in + *.bz2) DECOMP=-d;; + esac + bzip2 $DECOMP -v "$i" + done + ++ f \.tar.gz$ | f \.tgz$ | f \.tpz$ | f \.tar.Z$ | f \.tar.z$ | f \.tar.bz2$ | f \.tar.F$ & t r & ! t t +z Extract compressed tar file to subdirectory + unset D + set gzip -cd + case %f in + *.tar.F) D=`basename %f .tar.F`; set freeze -dc;; + *.tar.Z) D=`basename %f .tar.Z`;; + *.tar.bz2) D=`basename %f .tar.bz2`; set bunzip2 -c;; + *.tar.gz) D=`basename %f .tar.gz`;; + *.tar.z) D=`basename %f .tar.z`;; + *.tgz) D=`basename %f .tgz`;; + *.tpz) D=`basename %f .tpz`;; + esac + mkdir "$D"; cd "$D" && ("$1" "$2" ../%f | tar xvf -) + ++ t t +Z Extract compressed tar files to subdirectories + for i in %t ; do + set gzip -dc + unset D + case "$i" in + *.tar.F) D=`basename "$i" .tar.F`; set freeze -dc;; + *.tar.Z) D=`basename "$i" .tar.Z`;; + *.tar.bz2) D=`basename "$i" .tar.bz2`; set bunzip2 -c;; + *.tar.gz) D=`basename "$i" .tar.gz`;; + *.tar.z) D=`basename "$i" .tar.z`;; + *.tgz) D=`basename "$i" .tgz`;; + *.tpz) D=`basename "$i" .tpz`;; + esac + mkdir "$D"; (cd "$D" && "$1" "$2" "../$i" | tar xvf -) + done + ++ f \.gz$ | f \.tgz$ | f \.tpz$ | f \.Z$ | f \.z$ | f \.bz2$ & t r & ! t t +c Convert gz<->bz2, tar.gz<->tar.bz2 & tgz->tar.bz2 + unset D + unset EXT + case %f in + *.Z) EXT=Z;; + *.bz2) EXT=bz2;; + *.gz) EXT=gz;; + *.tgz) EXT=tgz;; + *.tpz) EXT=tpz;; + *.z) EXT=z;; + esac + case "$EXT" in + bz2|Z|gz|z) D=`basename %f ."$EXT"`;; + tgz|tpz) D=`basename %f ."$EXT"`.tar;; + esac + if [ "$EXT" = "bz2" ]; then + bunzip2 -v %f + gzip -f9 -v "$D" + else + gunzip -v %f + bzip2 -v "$D" + fi + ++ t t +C Convert gz<->bz2, tar.gz<->tar.bz2 & tgz->tar.bz2 + for i in %t ; do + unset D + unset EXT + case "$i" in + *.Z) EXT=Z;; + *.bz2) EXT=bz2;; + *.gz) EXT=gz;; + *.tgz) EXT=tgz;; + *.tpz) EXT=tpz;; + *.z) EXT=z;; + esac + case "$EXT" in + bz2|Z|gz|z) D=`basename "$i" ."$EXT"`;; + tgz|tpz) D=`basename "$i" ."$EXT"`.tar;; + esac + if [ "$EXT" = "bz2" ]; then + bunzip2 -v "$i" + gzip -f9 -v "$D" + else + gunzip -v "$i" + bzip2 -v "$D" + fi + done + ++ x /usr/bin/open | x /usr/local/bin/open & x /bin/sh +o Open next a free console + open -s -- sh + ++ x /usr/bin/open | x /usr/local/bin/open & x /bin/sh +O Odoo Refresh + sh /root/refresh_odoo.sh + ++ x /usr/bin/open | x /usr/local/bin/open & x /bin/sh +G git push + git push -u origin main + + diff --git a/views/dss_addstructures.xml b/views/dss_addstructures.xml new file mode 100755 index 0000000..046b7f9 --- /dev/null +++ b/views/dss_addstructures.xml @@ -0,0 +1,72 @@ + + + + + dss_adstructures_form + dss.adstructures + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + +
+
+
+
+ + + dss_adstructures_tree + dss.adstructures + + + + + + + + + + + DigitalSignage Werbestrukturen + ir.actions.act_window + dss.adstructures + tree,form + {} + +

+ Neuen Medienfiletyp erstellen +

+
+
+ + + +
diff --git a/views/dss_ads.xml b/views/dss_ads.xml new file mode 100755 index 0000000..40d3503 --- /dev/null +++ b/views/dss_ads.xml @@ -0,0 +1,324 @@ + + + + + dss_ads_form + dss.ads + + +
+
+
+ +
+
+ + + + +
+
+
+
+
+
+
+ + + +
+
+ + + +
+
+
+
+ + + +
+
+
+
+ + + +
+
+ +
+
+ +
+
+
+
+
+ + + +
+
+ + + +
+
+ + + +
+ +
+ + + +
+
+ + + +
+
+ + + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
+
+
+ + + dss_projects_tree + dss.ads + + + + + + + + + + + + + + + + Standard Beschreibung 1 + + + form + code + + action = records.setStandardText("AD_STD_1") + + + + + + Standard Beschreibung 2 + + + form + code + + action = records.setStandardText("AD_STD_2") + + + + + Standard Beschreibung 3 + + + form + code + + action = records.setStandardText("AD_STD_3") + + + + + + + dss_ads_kanban + dss.ads + + + + + + + + +
+
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+ + + + DigitalSignage Werbekampagnen Gesamtansicht + ir.actions.act_window + dss.ads + kanban + {} + + + + DigitalSignage Werbekampagnen Gesamtansicht + ir.actions.act_window + dss.ads + form + {} + + + + DigitalSignage Werbekampagnen + ir.actions.act_window + dss.ads + tree,form,kanban + {} + +

+ Neue Werbekampagne erstellen +

+
+
+ + + +
diff --git a/views/dss.xml b/views/dss_contracts.xml similarity index 70% rename from views/dss.xml rename to views/dss_contracts.xml index 85c196f..973c87e 100755 --- a/views/dss.xml +++ b/views/dss_contracts.xml @@ -128,6 +128,9 @@
+
+

@@ -322,7 +325,7 @@

- +
@@ -358,6 +361,26 @@
+
> +
+
+ + + + +
+
+
+ +-- + + + + + + + + @@ -375,6 +398,9 @@
+ + +
@@ -422,7 +448,8 @@
- + +
@@ -451,153 +478,4 @@ - - - - - dss_projects_form - dss.projects - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
-
- - - dss_projects_tree - dss.projects - - - - - - - - - - - - - - - dss_projects_kanban - dss.projects - - - - - - - - - - -
-
-
-
- - -
-
-
-
- -
-
-
-
- -
-
-
-
- -
-
-
-
- -
- -
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
- - - - - diff --git a/views/dss_mediafiles.xml b/views/dss_mediafiles.xml new file mode 100755 index 0000000..8893168 --- /dev/null +++ b/views/dss_mediafiles.xml @@ -0,0 +1,72 @@ + + + + + dss_mediatypes_form + dss.mediatypes + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+
+
+
+ + + dss_mediatypes_tree + dss.mediatypes + + + + + + + + + + + + DigitalSignage Mediendateitypen + ir.actions.act_window + dss.mediatypes + tree,form + {} + +

+ Neuen Medienfiletyp erstellen +

+
+
+ + +
diff --git a/views/dss_projects.xml b/views/dss_projects.xml new file mode 100755 index 0000000..6d0dc3a --- /dev/null +++ b/views/dss_projects.xml @@ -0,0 +1,164 @@ + + + + + dss_projects_form + dss.projects + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+
+
+
+ + + + dss_projects_kanban + dss.projects + + + + + + + + + + +
+
+
+
+ + +
+
+
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+
+
+ +
+ +
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+ + + dss_projects_tree + dss.projects + + + + + + + + + + + + + + + + + + + + + DigitalSignage Projekte + ir.actions.act_window + dss.projects + kanban,form,tree + {} + +

+ Fuege ein System ein ! +

+
+
+ +
diff --git a/views/dss_projectstate.xml b/views/dss_projectstate.xml new file mode 100755 index 0000000..e711e38 --- /dev/null +++ b/views/dss_projectstate.xml @@ -0,0 +1,55 @@ + + + + + dss_projectstate_tree + dss.projectstate + + + + + + + + + + + + + dss_projectstate_form + dss.projectstate + +
+ + + + + + + + + + +
+ + + +
+
+
+
+ + + DigitalSignage Projectstatus + ir.actions.act_window + dss.projectstate + tree,form + {} + +

+ Neuen Projektstatus erstellen +

+
+
+ +
diff --git a/views/dss_systemtypen.xml b/views/dss_systemtypen.xml new file mode 100755 index 0000000..68c3d61 --- /dev/null +++ b/views/dss_systemtypen.xml @@ -0,0 +1,60 @@ + + + + + dss_systemtypen_tree + dss.systemtypen + + + + + + + + + + + + + + + dss_systemtypen_form + dss.systemtypen + +
+ + + + + + + + + + + + +
+ +
+
+
+
+ + + DigitalSignage Systemtypen + ir.actions.act_window + dss.systemtypen + tree,form + {} + +

+ Neuen Systemtyp erstellen +

+
+
+ + +
diff --git a/views/dss_texts.xml b/views/dss_texts.xml new file mode 100755 index 0000000..082e2f9 --- /dev/null +++ b/views/dss_texts.xml @@ -0,0 +1,68 @@ + + + + + dss_texts_form + dss.texts + + +
+ + + + + + + + + + + + + + + + +
+ + + +
+
+
+
+ + + dss_texts_tree + dss.texts + + + + + + + + + + + + DigitalSignage Standardtexte + ir.actions.act_window + dss.texts + tree,form + {} + +

+ Neuen Standardtext erstellen +

+
+
+ + + +
diff --git a/views/mainsystem_view.xml b/views/mainsystem_view.xml index 4b81981..291de8c 100755 --- a/views/mainsystem_view.xml +++ b/views/mainsystem_view.xml @@ -52,7 +52,7 @@ - + @@ -87,6 +87,9 @@
+ + +
@@ -160,30 +163,14 @@
+ + +
- - dss_systemtypen_form - dss.systemtypen - -
- - - - - - - - - -
-
-
-
-
dss_systems_tree @@ -225,6 +212,9 @@
+ + +
@@ -244,53 +234,6 @@ - - dss_systemtypen_tree - dss.systemtypen - - - - - - - - - - - - - - dss_projectstate_tree - dss.projectstate - - - - - - - - - - - - dss_projectstate_form - dss.projectstate - -
- - - - - - - - - -
-
-
-
-
dss_contractstate_tree @@ -298,7 +241,7 @@ - + @@ -315,13 +258,56 @@ - +
+ + + +
+ +
+
+ + + dss_adstate_tree + dss.adstate + + + + + + + + + + + + + + dss_adstate_form + dss.adstate + +
+ + + + + + + + + + + +
+ + +
@@ -333,7 +319,7 @@ - + @@ -356,6 +342,9 @@
+ + +
@@ -367,7 +356,7 @@ - + @@ -392,6 +381,9 @@
+ + +
@@ -430,6 +422,9 @@
+ + +
@@ -465,26 +460,96 @@
+ + +
- - - - DigitalSignage Projekte - ir.actions.act_window - dss.projects - kanban,form,tree - {} - -

- Fuege ein System ein ! -

+ + dss_paysystem_fields_tree + dss.paysystem_fields + + + + + + + + + + + dss_paymentsystem_fields_form + dss.paysystem_fields + +
+ + + + + + + + + + + +
+ + + +
+
+
+
+ + + dss_paysystems_tree + dss.paysystems + + + + + + + + + + + + + dss_paymentsystems_form + dss.paysystems + +
+ + + + + + + + + + + + + +
+ + + +
+
+
+
+ + DigitalSignage Systeme ir.actions.act_window @@ -524,31 +589,6 @@
- - DigitalSignage Systemtypen - ir.actions.act_window - dss.systemtypen - tree,form - {} - -

- Neuen Systemtyp erstellen -

-
-
- - - DigitalSignage Projectstatus - ir.actions.act_window - dss.projectstate - tree,form - {} - -

- Neuen Projektstatus erstellen -

-
-
DigitalSignage Vertragsstatus @@ -563,6 +603,21 @@ + + DigitalSignage Werbeaktions-Status + ir.actions.act_window + dss.adstate + tree,form + {} + +

+ Neuen Werbeaktions-Status erstellen +

+
+
+ + + DigitalSignage Bearbeitungsstatus ir.actions.act_window @@ -615,106 +670,58 @@ - + + DigitalSignage Zahlungsfelder + ir.actions.act_window + dss.paysystem_fields + tree,form + {} + +

+ Neues Zahlfeld erstellen +

+
+
- + + DigitalSignage Zahlungsart + ir.actions.act_window + dss.paysystems + tree,form + {} + +

+ Neue Zahlugnsart erstellen +

+
+
- + + email.dss.template.search + mail.template + + + + + + + + + + + + + - + + Email Templates + mail.template + form,tree + + + {'search_default_payment_templates': 1} + - - - - - - - - - - - - - - - - - - - - diff --git a/views/menu.xml b/views/menu.xml index 02ab84b..58cca57 100755 --- a/views/menu.xml +++ b/views/menu.xml @@ -6,4 +6,176 @@ web_icon="digitalsignage_system,static/description/icon.png" sequence="2"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file