Blockly
|
|
@ -0,0 +1,514 @@
|
|||
models/__pycache__/*
|
||||
static/src/js/ace/info.txt
|
||||
static/src/js/ace/src-noconflict/ace.js
|
||||
static/src/js/ace/src-noconflict/ext-beautify.js
|
||||
static/src/js/ace/src-noconflict/ext-code_lens.js
|
||||
static/src/js/ace/src-noconflict/ext-command_bar.js
|
||||
static/src/js/ace/src-noconflict/ext-diff.js
|
||||
static/src/js/ace/src-noconflict/ext-elastic_tabstops_lite.js
|
||||
static/src/js/ace/src-noconflict/ext-emmet.js
|
||||
static/src/js/ace/src-noconflict/ext-error_marker.js
|
||||
static/src/js/ace/src-noconflict/ext-hardwrap.js
|
||||
static/src/js/ace/src-noconflict/ext-inline_autocomplete.js
|
||||
static/src/js/ace/src-noconflict/ext-keybinding_menu.js
|
||||
static/src/js/ace/src-noconflict/ext-language_tools.js
|
||||
static/src/js/ace/src-noconflict/ext-linking.js
|
||||
static/src/js/ace/src-noconflict/ext-modelist.js
|
||||
static/src/js/ace/src-noconflict/ext-options.js
|
||||
static/src/js/ace/src-noconflict/ext-prompt.js
|
||||
static/src/js/ace/src-noconflict/ext-rtl.js
|
||||
static/src/js/ace/src-noconflict/ext-searchbox.js
|
||||
static/src/js/ace/src-noconflict/ext-settings_menu.js
|
||||
static/src/js/ace/src-noconflict/ext-simple_tokenizer.js
|
||||
static/src/js/ace/src-noconflict/ext-spellcheck.js
|
||||
static/src/js/ace/src-noconflict/ext-split.js
|
||||
static/src/js/ace/src-noconflict/ext-static_highlight.js
|
||||
static/src/js/ace/src-noconflict/ext-statusbar.js
|
||||
static/src/js/ace/src-noconflict/ext-textarea.js
|
||||
static/src/js/ace/src-noconflict/ext-themelist.js
|
||||
static/src/js/ace/src-noconflict/ext-whitespace.js
|
||||
static/src/js/ace/src-noconflict/info.txt
|
||||
static/src/js/ace/src-noconflict/keybinding-emacs.js
|
||||
static/src/js/ace/src-noconflict/keybinding-sublime.js
|
||||
static/src/js/ace/src-noconflict/keybinding-vim.js
|
||||
static/src/js/ace/src-noconflict/keybinding-vscode.js
|
||||
static/src/js/ace/src-noconflict/mode-abap.js
|
||||
static/src/js/ace/src-noconflict/mode-abc.js
|
||||
static/src/js/ace/src-noconflict/mode-actionscript.js
|
||||
static/src/js/ace/src-noconflict/mode-ada.js
|
||||
static/src/js/ace/src-noconflict/mode-alda.js
|
||||
static/src/js/ace/src-noconflict/mode-apache_conf.js
|
||||
static/src/js/ace/src-noconflict/mode-apex.js
|
||||
static/src/js/ace/src-noconflict/mode-applescript.js
|
||||
static/src/js/ace/src-noconflict/mode-aql.js
|
||||
static/src/js/ace/src-noconflict/mode-asciidoc.js
|
||||
static/src/js/ace/src-noconflict/mode-asl.js
|
||||
static/src/js/ace/src-noconflict/mode-assembly_arm32.js
|
||||
static/src/js/ace/src-noconflict/mode-assembly_x86.js
|
||||
static/src/js/ace/src-noconflict/mode-astro.js
|
||||
static/src/js/ace/src-noconflict/mode-autohotkey.js
|
||||
static/src/js/ace/src-noconflict/mode-basic.js
|
||||
static/src/js/ace/src-noconflict/mode-batchfile.js
|
||||
static/src/js/ace/src-noconflict/mode-bibtex.js
|
||||
static/src/js/ace/src-noconflict/mode-c_cpp.js
|
||||
static/src/js/ace/src-noconflict/mode-c9search.js
|
||||
static/src/js/ace/src-noconflict/mode-cirru.js
|
||||
static/src/js/ace/src-noconflict/mode-clojure.js
|
||||
static/src/js/ace/src-noconflict/mode-clue.js
|
||||
static/src/js/ace/src-noconflict/mode-cobol.js
|
||||
static/src/js/ace/src-noconflict/mode-coffee.js
|
||||
static/src/js/ace/src-noconflict/mode-coldfusion.js
|
||||
static/src/js/ace/src-noconflict/mode-crystal.js
|
||||
static/src/js/ace/src-noconflict/mode-csharp.js
|
||||
static/src/js/ace/src-noconflict/mode-csound_document.js
|
||||
static/src/js/ace/src-noconflict/mode-csound_orchestra.js
|
||||
static/src/js/ace/src-noconflict/mode-csound_score.js
|
||||
static/src/js/ace/src-noconflict/mode-csp.js
|
||||
static/src/js/ace/src-noconflict/mode-css.js
|
||||
static/src/js/ace/src-noconflict/mode-csv.js
|
||||
static/src/js/ace/src-noconflict/mode-curly.js
|
||||
static/src/js/ace/src-noconflict/mode-cuttlefish.js
|
||||
static/src/js/ace/src-noconflict/mode-d.js
|
||||
static/src/js/ace/src-noconflict/mode-dart.js
|
||||
static/src/js/ace/src-noconflict/mode-diff.js
|
||||
static/src/js/ace/src-noconflict/mode-django.js
|
||||
static/src/js/ace/src-noconflict/mode-dockerfile.js
|
||||
static/src/js/ace/src-noconflict/mode-dot.js
|
||||
static/src/js/ace/src-noconflict/mode-drools.js
|
||||
static/src/js/ace/src-noconflict/mode-edifact.js
|
||||
static/src/js/ace/src-noconflict/mode-eiffel.js
|
||||
static/src/js/ace/src-noconflict/mode-ejs.js
|
||||
static/src/js/ace/src-noconflict/mode-elixir.js
|
||||
static/src/js/ace/src-noconflict/mode-elm.js
|
||||
static/src/js/ace/src-noconflict/mode-erlang.js
|
||||
static/src/js/ace/src-noconflict/mode-flix.js
|
||||
static/src/js/ace/src-noconflict/mode-forth.js
|
||||
static/src/js/ace/src-noconflict/mode-fortran.js
|
||||
static/src/js/ace/src-noconflict/mode-fsharp.js
|
||||
static/src/js/ace/src-noconflict/mode-fsl.js
|
||||
static/src/js/ace/src-noconflict/mode-ftl.js
|
||||
static/src/js/ace/src-noconflict/mode-gcode.js
|
||||
static/src/js/ace/src-noconflict/mode-gherkin.js
|
||||
static/src/js/ace/src-noconflict/mode-gitignore.js
|
||||
static/src/js/ace/src-noconflict/mode-glsl.js
|
||||
static/src/js/ace/src-noconflict/mode-gobstones.js
|
||||
static/src/js/ace/src-noconflict/mode-golang.js
|
||||
static/src/js/ace/src-noconflict/mode-graphqlschema.js
|
||||
static/src/js/ace/src-noconflict/mode-groovy.js
|
||||
static/src/js/ace/src-noconflict/mode-haml.js
|
||||
static/src/js/ace/src-noconflict/mode-handlebars.js
|
||||
static/src/js/ace/src-noconflict/mode-haskell_cabal.js
|
||||
static/src/js/ace/src-noconflict/mode-haskell.js
|
||||
static/src/js/ace/src-noconflict/mode-haxe.js
|
||||
static/src/js/ace/src-noconflict/mode-hjson.js
|
||||
static/src/js/ace/src-noconflict/mode-html_elixir.js
|
||||
static/src/js/ace/src-noconflict/mode-html_ruby.js
|
||||
static/src/js/ace/src-noconflict/mode-html.js
|
||||
static/src/js/ace/src-noconflict/mode-ini.js
|
||||
static/src/js/ace/src-noconflict/mode-io.js
|
||||
static/src/js/ace/src-noconflict/mode-ion.js
|
||||
static/src/js/ace/src-noconflict/mode-jack.js
|
||||
static/src/js/ace/src-noconflict/mode-jade.js
|
||||
static/src/js/ace/src-noconflict/mode-java.js
|
||||
static/src/js/ace/src-noconflict/mode-javascript.js
|
||||
static/src/js/ace/src-noconflict/mode-jexl.js
|
||||
static/src/js/ace/src-noconflict/mode-json.js
|
||||
static/src/js/ace/src-noconflict/mode-json5.js
|
||||
static/src/js/ace/src-noconflict/mode-jsoniq.js
|
||||
static/src/js/ace/src-noconflict/mode-jsp.js
|
||||
static/src/js/ace/src-noconflict/mode-jssm.js
|
||||
static/src/js/ace/src-noconflict/mode-jsx.js
|
||||
static/src/js/ace/src-noconflict/mode-julia.js
|
||||
static/src/js/ace/src-noconflict/mode-kotlin.js
|
||||
static/src/js/ace/src-noconflict/mode-latex.js
|
||||
static/src/js/ace/src-noconflict/mode-latte.js
|
||||
static/src/js/ace/src-noconflict/mode-less.js
|
||||
static/src/js/ace/src-noconflict/mode-liquid.js
|
||||
static/src/js/ace/src-noconflict/mode-lisp.js
|
||||
static/src/js/ace/src-noconflict/mode-livescript.js
|
||||
static/src/js/ace/src-noconflict/mode-logiql.js
|
||||
static/src/js/ace/src-noconflict/mode-logtalk.js
|
||||
static/src/js/ace/src-noconflict/mode-lsl.js
|
||||
static/src/js/ace/src-noconflict/mode-lua.js
|
||||
static/src/js/ace/src-noconflict/mode-luapage.js
|
||||
static/src/js/ace/src-noconflict/mode-lucene.js
|
||||
static/src/js/ace/src-noconflict/mode-makefile.js
|
||||
static/src/js/ace/src-noconflict/mode-markdown.js
|
||||
static/src/js/ace/src-noconflict/mode-mask.js
|
||||
static/src/js/ace/src-noconflict/mode-matlab.js
|
||||
static/src/js/ace/src-noconflict/mode-maze.js
|
||||
static/src/js/ace/src-noconflict/mode-mediawiki.js
|
||||
static/src/js/ace/src-noconflict/mode-mel.js
|
||||
static/src/js/ace/src-noconflict/mode-mips.js
|
||||
static/src/js/ace/src-noconflict/mode-mixal.js
|
||||
static/src/js/ace/src-noconflict/mode-mushcode.js
|
||||
static/src/js/ace/src-noconflict/mode-mysql.js
|
||||
static/src/js/ace/src-noconflict/mode-nasal.js
|
||||
static/src/js/ace/src-noconflict/mode-nginx.js
|
||||
static/src/js/ace/src-noconflict/mode-nim.js
|
||||
static/src/js/ace/src-noconflict/mode-nix.js
|
||||
static/src/js/ace/src-noconflict/mode-nsis.js
|
||||
static/src/js/ace/src-noconflict/mode-nunjucks.js
|
||||
static/src/js/ace/src-noconflict/mode-objectivec.js
|
||||
static/src/js/ace/src-noconflict/mode-ocaml.js
|
||||
static/src/js/ace/src-noconflict/mode-odin.js
|
||||
static/src/js/ace/src-noconflict/mode-partiql.js
|
||||
static/src/js/ace/src-noconflict/mode-pascal.js
|
||||
static/src/js/ace/src-noconflict/mode-perl.js
|
||||
static/src/js/ace/src-noconflict/mode-pgsql.js
|
||||
static/src/js/ace/src-noconflict/mode-php_laravel_blade.js
|
||||
static/src/js/ace/src-noconflict/mode-php.js
|
||||
static/src/js/ace/src-noconflict/mode-pig.js
|
||||
static/src/js/ace/src-noconflict/mode-plain_text.js
|
||||
static/src/js/ace/src-noconflict/mode-plsql.js
|
||||
static/src/js/ace/src-noconflict/mode-powershell.js
|
||||
static/src/js/ace/src-noconflict/mode-praat.js
|
||||
static/src/js/ace/src-noconflict/mode-prisma.js
|
||||
static/src/js/ace/src-noconflict/mode-prolog.js
|
||||
static/src/js/ace/src-noconflict/mode-properties.js
|
||||
static/src/js/ace/src-noconflict/mode-protobuf.js
|
||||
static/src/js/ace/src-noconflict/mode-prql.js
|
||||
static/src/js/ace/src-noconflict/mode-puppet.js
|
||||
static/src/js/ace/src-noconflict/mode-python.js
|
||||
static/src/js/ace/src-noconflict/mode-qml.js
|
||||
static/src/js/ace/src-noconflict/mode-r.js
|
||||
static/src/js/ace/src-noconflict/mode-raku.js
|
||||
static/src/js/ace/src-noconflict/mode-razor.js
|
||||
static/src/js/ace/src-noconflict/mode-rdoc.js
|
||||
static/src/js/ace/src-noconflict/mode-red.js
|
||||
static/src/js/ace/src-noconflict/mode-redshift.js
|
||||
static/src/js/ace/src-noconflict/mode-rhtml.js
|
||||
static/src/js/ace/src-noconflict/mode-robot.js
|
||||
static/src/js/ace/src-noconflict/mode-rst.js
|
||||
static/src/js/ace/src-noconflict/mode-ruby.js
|
||||
static/src/js/ace/src-noconflict/mode-rust.js
|
||||
static/src/js/ace/src-noconflict/mode-sac.js
|
||||
static/src/js/ace/src-noconflict/mode-sass.js
|
||||
static/src/js/ace/src-noconflict/mode-scad.js
|
||||
static/src/js/ace/src-noconflict/mode-scala.js
|
||||
static/src/js/ace/src-noconflict/mode-scheme.js
|
||||
static/src/js/ace/src-noconflict/mode-scrypt.js
|
||||
static/src/js/ace/src-noconflict/mode-scss.js
|
||||
static/src/js/ace/src-noconflict/mode-sh.js
|
||||
static/src/js/ace/src-noconflict/mode-sjs.js
|
||||
static/src/js/ace/src-noconflict/mode-slim.js
|
||||
static/src/js/ace/src-noconflict/mode-smarty.js
|
||||
static/src/js/ace/src-noconflict/mode-smithy.js
|
||||
static/src/js/ace/src-noconflict/mode-snippets.js
|
||||
static/src/js/ace/src-noconflict/mode-soy_template.js
|
||||
static/src/js/ace/src-noconflict/mode-space.js
|
||||
static/src/js/ace/src-noconflict/mode-sparql.js
|
||||
static/src/js/ace/src-noconflict/mode-sql.js
|
||||
static/src/js/ace/src-noconflict/mode-sqlserver.js
|
||||
static/src/js/ace/src-noconflict/mode-stylus.js
|
||||
static/src/js/ace/src-noconflict/mode-svg.js
|
||||
static/src/js/ace/src-noconflict/mode-swift.js
|
||||
static/src/js/ace/src-noconflict/mode-tcl.js
|
||||
static/src/js/ace/src-noconflict/mode-terraform.js
|
||||
static/src/js/ace/src-noconflict/mode-tex.js
|
||||
static/src/js/ace/src-noconflict/mode-text.js
|
||||
static/src/js/ace/src-noconflict/mode-textile.js
|
||||
static/src/js/ace/src-noconflict/mode-toml.js
|
||||
static/src/js/ace/src-noconflict/mode-tsv.js
|
||||
static/src/js/ace/src-noconflict/mode-tsx.js
|
||||
static/src/js/ace/src-noconflict/mode-turtle.js
|
||||
static/src/js/ace/src-noconflict/mode-twig.js
|
||||
static/src/js/ace/src-noconflict/mode-typescript.js
|
||||
static/src/js/ace/src-noconflict/mode-vala.js
|
||||
static/src/js/ace/src-noconflict/mode-vbscript.js
|
||||
static/src/js/ace/src-noconflict/mode-velocity.js
|
||||
static/src/js/ace/src-noconflict/mode-verilog.js
|
||||
static/src/js/ace/src-noconflict/mode-vhdl.js
|
||||
static/src/js/ace/src-noconflict/mode-visualforce.js
|
||||
static/src/js/ace/src-noconflict/mode-vue.js
|
||||
static/src/js/ace/src-noconflict/mode-wollok.js
|
||||
static/src/js/ace/src-noconflict/mode-xml.js
|
||||
static/src/js/ace/src-noconflict/mode-xquery.js
|
||||
static/src/js/ace/src-noconflict/mode-yaml.js
|
||||
static/src/js/ace/src-noconflict/mode-zeek.js
|
||||
static/src/js/ace/src-noconflict/mode-zig.js
|
||||
static/src/js/ace/src-noconflict/theme-ambiance.js
|
||||
static/src/js/ace/src-noconflict/theme-chaos.js
|
||||
static/src/js/ace/src-noconflict/theme-chrome.js
|
||||
static/src/js/ace/src-noconflict/theme-cloud_editor_dark.js
|
||||
static/src/js/ace/src-noconflict/theme-cloud_editor.js
|
||||
static/src/js/ace/src-noconflict/theme-cloud9_day.js
|
||||
static/src/js/ace/src-noconflict/theme-cloud9_night_low_color.js
|
||||
static/src/js/ace/src-noconflict/theme-cloud9_night.js
|
||||
static/src/js/ace/src-noconflict/theme-clouds_midnight.js
|
||||
static/src/js/ace/src-noconflict/theme-clouds.js
|
||||
static/src/js/ace/src-noconflict/theme-cobalt.js
|
||||
static/src/js/ace/src-noconflict/theme-crimson_editor.js
|
||||
static/src/js/ace/src-noconflict/theme-dawn.js
|
||||
static/src/js/ace/src-noconflict/theme-dracula.js
|
||||
static/src/js/ace/src-noconflict/theme-dreamweaver.js
|
||||
static/src/js/ace/src-noconflict/theme-eclipse.js
|
||||
static/src/js/ace/src-noconflict/theme-github_dark.js
|
||||
static/src/js/ace/src-noconflict/theme-github_light_default.js
|
||||
static/src/js/ace/src-noconflict/theme-github.js
|
||||
static/src/js/ace/src-noconflict/theme-gob.js
|
||||
static/src/js/ace/src-noconflict/theme-gruvbox_dark_hard.js
|
||||
static/src/js/ace/src-noconflict/theme-gruvbox_light_hard.js
|
||||
static/src/js/ace/src-noconflict/theme-gruvbox.js
|
||||
static/src/js/ace/src-noconflict/theme-idle_fingers.js
|
||||
static/src/js/ace/src-noconflict/theme-iplastic.js
|
||||
static/src/js/ace/src-noconflict/theme-katzenmilch.js
|
||||
static/src/js/ace/src-noconflict/theme-kr_theme.js
|
||||
static/src/js/ace/src-noconflict/theme-kuroir.js
|
||||
static/src/js/ace/src-noconflict/theme-merbivore_soft.js
|
||||
static/src/js/ace/src-noconflict/theme-merbivore.js
|
||||
static/src/js/ace/src-noconflict/theme-mono_industrial.js
|
||||
static/src/js/ace/src-noconflict/theme-monokai.js
|
||||
static/src/js/ace/src-noconflict/theme-nord_dark.js
|
||||
static/src/js/ace/src-noconflict/theme-one_dark.js
|
||||
static/src/js/ace/src-noconflict/theme-pastel_on_dark.js
|
||||
static/src/js/ace/src-noconflict/theme-solarized_dark.js
|
||||
static/src/js/ace/src-noconflict/theme-solarized_light.js
|
||||
static/src/js/ace/src-noconflict/theme-sqlserver.js
|
||||
static/src/js/ace/src-noconflict/theme-terminal.js
|
||||
static/src/js/ace/src-noconflict/theme-textmate.js
|
||||
static/src/js/ace/src-noconflict/theme-tomorrow_night_blue.js
|
||||
static/src/js/ace/src-noconflict/theme-tomorrow_night_bright.js
|
||||
static/src/js/ace/src-noconflict/theme-tomorrow_night_eighties.js
|
||||
static/src/js/ace/src-noconflict/theme-tomorrow_night.js
|
||||
static/src/js/ace/src-noconflict/theme-tomorrow.js
|
||||
static/src/js/ace/src-noconflict/theme-twilight.js
|
||||
static/src/js/ace/src-noconflict/theme-vibrant_ink.js
|
||||
static/src/js/ace/src-noconflict/theme-xcode.js
|
||||
static/src/js/ace/src-noconflict/worker-base.js
|
||||
static/src/js/ace/src-noconflict/worker-coffee.js
|
||||
static/src/js/ace/src-noconflict/worker-css.js
|
||||
static/src/js/ace/src-noconflict/worker-html.js
|
||||
static/src/js/ace/src-noconflict/worker-javascript.js
|
||||
static/src/js/ace/src-noconflict/worker-json.js
|
||||
static/src/js/ace/src-noconflict/worker-lua.js
|
||||
static/src/js/ace/src-noconflict/worker-php.js
|
||||
static/src/js/ace/src-noconflict/worker-xml.js
|
||||
static/src/js/ace/src-noconflict/worker-xquery.js
|
||||
static/src/js/ace/src-noconflict/worker-yaml.js
|
||||
static/src/js/ace/src-noconflict/snippets/abap.js
|
||||
static/src/js/ace/src-noconflict/snippets/abc.js
|
||||
static/src/js/ace/src-noconflict/snippets/actionscript.js
|
||||
static/src/js/ace/src-noconflict/snippets/ada.js
|
||||
static/src/js/ace/src-noconflict/snippets/alda.js
|
||||
static/src/js/ace/src-noconflict/snippets/apache_conf.js
|
||||
static/src/js/ace/src-noconflict/snippets/apex.js
|
||||
static/src/js/ace/src-noconflict/snippets/applescript.js
|
||||
static/src/js/ace/src-noconflict/snippets/aql.js
|
||||
static/src/js/ace/src-noconflict/snippets/asciidoc.js
|
||||
static/src/js/ace/src-noconflict/snippets/asl.js
|
||||
static/src/js/ace/src-noconflict/snippets/assembly_arm32.js
|
||||
static/src/js/ace/src-noconflict/snippets/assembly_x86.js
|
||||
static/src/js/ace/src-noconflict/snippets/astro.js
|
||||
static/src/js/ace/src-noconflict/snippets/autohotkey.js
|
||||
static/src/js/ace/src-noconflict/snippets/basic.js
|
||||
static/src/js/ace/src-noconflict/snippets/batchfile.js
|
||||
static/src/js/ace/src-noconflict/snippets/bibtex.js
|
||||
static/src/js/ace/src-noconflict/snippets/c_cpp.js
|
||||
static/src/js/ace/src-noconflict/snippets/c9search.js
|
||||
static/src/js/ace/src-noconflict/snippets/cirru.js
|
||||
static/src/js/ace/src-noconflict/snippets/clojure.js
|
||||
static/src/js/ace/src-noconflict/snippets/clue.js
|
||||
static/src/js/ace/src-noconflict/snippets/cobol.js
|
||||
static/src/js/ace/src-noconflict/snippets/coffee.js
|
||||
static/src/js/ace/src-noconflict/snippets/coldfusion.js
|
||||
static/src/js/ace/src-noconflict/snippets/crystal.js
|
||||
static/src/js/ace/src-noconflict/snippets/csharp.js
|
||||
static/src/js/ace/src-noconflict/snippets/csound_document.js
|
||||
static/src/js/ace/src-noconflict/snippets/csound_orchestra.js
|
||||
static/src/js/ace/src-noconflict/snippets/csound_score.js
|
||||
static/src/js/ace/src-noconflict/snippets/csp.js
|
||||
static/src/js/ace/src-noconflict/snippets/css.js
|
||||
static/src/js/ace/src-noconflict/snippets/csv.js
|
||||
static/src/js/ace/src-noconflict/snippets/curly.js
|
||||
static/src/js/ace/src-noconflict/snippets/cuttlefish.js
|
||||
static/src/js/ace/src-noconflict/snippets/d.js
|
||||
static/src/js/ace/src-noconflict/snippets/dart.js
|
||||
static/src/js/ace/src-noconflict/snippets/diff.js
|
||||
static/src/js/ace/src-noconflict/snippets/django.js
|
||||
static/src/js/ace/src-noconflict/snippets/dockerfile.js
|
||||
static/src/js/ace/src-noconflict/snippets/dot.js
|
||||
static/src/js/ace/src-noconflict/snippets/drools.js
|
||||
static/src/js/ace/src-noconflict/snippets/edifact.js
|
||||
static/src/js/ace/src-noconflict/snippets/eiffel.js
|
||||
static/src/js/ace/src-noconflict/snippets/ejs.js
|
||||
static/src/js/ace/src-noconflict/snippets/elixir.js
|
||||
static/src/js/ace/src-noconflict/snippets/elm.js
|
||||
static/src/js/ace/src-noconflict/snippets/erlang.js
|
||||
static/src/js/ace/src-noconflict/snippets/flix.js
|
||||
static/src/js/ace/src-noconflict/snippets/forth.js
|
||||
static/src/js/ace/src-noconflict/snippets/fortran.js
|
||||
static/src/js/ace/src-noconflict/snippets/fsharp.js
|
||||
static/src/js/ace/src-noconflict/snippets/fsl.js
|
||||
static/src/js/ace/src-noconflict/snippets/ftl.js
|
||||
static/src/js/ace/src-noconflict/snippets/gcode.js
|
||||
static/src/js/ace/src-noconflict/snippets/gherkin.js
|
||||
static/src/js/ace/src-noconflict/snippets/gitignore.js
|
||||
static/src/js/ace/src-noconflict/snippets/glsl.js
|
||||
static/src/js/ace/src-noconflict/snippets/gobstones.js
|
||||
static/src/js/ace/src-noconflict/snippets/golang.js
|
||||
static/src/js/ace/src-noconflict/snippets/graphqlschema.js
|
||||
static/src/js/ace/src-noconflict/snippets/groovy.js
|
||||
static/src/js/ace/src-noconflict/snippets/haml.js
|
||||
static/src/js/ace/src-noconflict/snippets/handlebars.js
|
||||
static/src/js/ace/src-noconflict/snippets/haskell_cabal.js
|
||||
static/src/js/ace/src-noconflict/snippets/haskell.js
|
||||
static/src/js/ace/src-noconflict/snippets/haxe.js
|
||||
static/src/js/ace/src-noconflict/snippets/hjson.js
|
||||
static/src/js/ace/src-noconflict/snippets/html_elixir.js
|
||||
static/src/js/ace/src-noconflict/snippets/html_ruby.js
|
||||
static/src/js/ace/src-noconflict/snippets/html.js
|
||||
static/src/js/ace/src-noconflict/snippets/ini.js
|
||||
static/src/js/ace/src-noconflict/snippets/io.js
|
||||
static/src/js/ace/src-noconflict/snippets/ion.js
|
||||
static/src/js/ace/src-noconflict/snippets/jack.js
|
||||
static/src/js/ace/src-noconflict/snippets/jade.js
|
||||
static/src/js/ace/src-noconflict/snippets/java.js
|
||||
static/src/js/ace/src-noconflict/snippets/javascript.js
|
||||
static/src/js/ace/src-noconflict/snippets/jexl.js
|
||||
static/src/js/ace/src-noconflict/snippets/json.js
|
||||
static/src/js/ace/src-noconflict/snippets/json5.js
|
||||
static/src/js/ace/src-noconflict/snippets/jsoniq.js
|
||||
static/src/js/ace/src-noconflict/snippets/jsp.js
|
||||
static/src/js/ace/src-noconflict/snippets/jssm.js
|
||||
static/src/js/ace/src-noconflict/snippets/jsx.js
|
||||
static/src/js/ace/src-noconflict/snippets/julia.js
|
||||
static/src/js/ace/src-noconflict/snippets/kotlin.js
|
||||
static/src/js/ace/src-noconflict/snippets/latex.js
|
||||
static/src/js/ace/src-noconflict/snippets/latte.js
|
||||
static/src/js/ace/src-noconflict/snippets/less.js
|
||||
static/src/js/ace/src-noconflict/snippets/liquid.js
|
||||
static/src/js/ace/src-noconflict/snippets/lisp.js
|
||||
static/src/js/ace/src-noconflict/snippets/livescript.js
|
||||
static/src/js/ace/src-noconflict/snippets/logiql.js
|
||||
static/src/js/ace/src-noconflict/snippets/logtalk.js
|
||||
static/src/js/ace/src-noconflict/snippets/lsl.js
|
||||
static/src/js/ace/src-noconflict/snippets/lua.js
|
||||
static/src/js/ace/src-noconflict/snippets/luapage.js
|
||||
static/src/js/ace/src-noconflict/snippets/lucene.js
|
||||
static/src/js/ace/src-noconflict/snippets/makefile.js
|
||||
static/src/js/ace/src-noconflict/snippets/markdown.js
|
||||
static/src/js/ace/src-noconflict/snippets/mask.js
|
||||
static/src/js/ace/src-noconflict/snippets/matlab.js
|
||||
static/src/js/ace/src-noconflict/snippets/maze.js
|
||||
static/src/js/ace/src-noconflict/snippets/mediawiki.js
|
||||
static/src/js/ace/src-noconflict/snippets/mel.js
|
||||
static/src/js/ace/src-noconflict/snippets/mips.js
|
||||
static/src/js/ace/src-noconflict/snippets/mixal.js
|
||||
static/src/js/ace/src-noconflict/snippets/mushcode.js
|
||||
static/src/js/ace/src-noconflict/snippets/mysql.js
|
||||
static/src/js/ace/src-noconflict/snippets/nasal.js
|
||||
static/src/js/ace/src-noconflict/snippets/nginx.js
|
||||
static/src/js/ace/src-noconflict/snippets/nim.js
|
||||
static/src/js/ace/src-noconflict/snippets/nix.js
|
||||
static/src/js/ace/src-noconflict/snippets/nsis.js
|
||||
static/src/js/ace/src-noconflict/snippets/nunjucks.js
|
||||
static/src/js/ace/src-noconflict/snippets/objectivec.js
|
||||
static/src/js/ace/src-noconflict/snippets/ocaml.js
|
||||
static/src/js/ace/src-noconflict/snippets/odin.js
|
||||
static/src/js/ace/src-noconflict/snippets/partiql.js
|
||||
static/src/js/ace/src-noconflict/snippets/pascal.js
|
||||
static/src/js/ace/src-noconflict/snippets/perl.js
|
||||
static/src/js/ace/src-noconflict/snippets/pgsql.js
|
||||
static/src/js/ace/src-noconflict/snippets/php_laravel_blade.js
|
||||
static/src/js/ace/src-noconflict/snippets/php.js
|
||||
static/src/js/ace/src-noconflict/snippets/pig.js
|
||||
static/src/js/ace/src-noconflict/snippets/plain_text.js
|
||||
static/src/js/ace/src-noconflict/snippets/plsql.js
|
||||
static/src/js/ace/src-noconflict/snippets/powershell.js
|
||||
static/src/js/ace/src-noconflict/snippets/praat.js
|
||||
static/src/js/ace/src-noconflict/snippets/prisma.js
|
||||
static/src/js/ace/src-noconflict/snippets/prolog.js
|
||||
static/src/js/ace/src-noconflict/snippets/properties.js
|
||||
static/src/js/ace/src-noconflict/snippets/protobuf.js
|
||||
static/src/js/ace/src-noconflict/snippets/prql.js
|
||||
static/src/js/ace/src-noconflict/snippets/puppet.js
|
||||
static/src/js/ace/src-noconflict/snippets/python.js
|
||||
static/src/js/ace/src-noconflict/snippets/qml.js
|
||||
static/src/js/ace/src-noconflict/snippets/r.js
|
||||
static/src/js/ace/src-noconflict/snippets/raku.js
|
||||
static/src/js/ace/src-noconflict/snippets/razor.js
|
||||
static/src/js/ace/src-noconflict/snippets/rdoc.js
|
||||
static/src/js/ace/src-noconflict/snippets/red.js
|
||||
static/src/js/ace/src-noconflict/snippets/redshift.js
|
||||
static/src/js/ace/src-noconflict/snippets/rhtml.js
|
||||
static/src/js/ace/src-noconflict/snippets/robot.js
|
||||
static/src/js/ace/src-noconflict/snippets/rst.js
|
||||
static/src/js/ace/src-noconflict/snippets/ruby.js
|
||||
static/src/js/ace/src-noconflict/snippets/rust.js
|
||||
static/src/js/ace/src-noconflict/snippets/sac.js
|
||||
static/src/js/ace/src-noconflict/snippets/sass.js
|
||||
static/src/js/ace/src-noconflict/snippets/scad.js
|
||||
static/src/js/ace/src-noconflict/snippets/scala.js
|
||||
static/src/js/ace/src-noconflict/snippets/scheme.js
|
||||
static/src/js/ace/src-noconflict/snippets/scrypt.js
|
||||
static/src/js/ace/src-noconflict/snippets/scss.js
|
||||
static/src/js/ace/src-noconflict/snippets/sh.js
|
||||
static/src/js/ace/src-noconflict/snippets/sjs.js
|
||||
static/src/js/ace/src-noconflict/snippets/slim.js
|
||||
static/src/js/ace/src-noconflict/snippets/smarty.js
|
||||
static/src/js/ace/src-noconflict/snippets/smithy.js
|
||||
static/src/js/ace/src-noconflict/snippets/snippets.js
|
||||
static/src/js/ace/src-noconflict/snippets/soy_template.js
|
||||
static/src/js/ace/src-noconflict/snippets/space.js
|
||||
static/src/js/ace/src-noconflict/snippets/sparql.js
|
||||
static/src/js/ace/src-noconflict/snippets/sql.js
|
||||
static/src/js/ace/src-noconflict/snippets/sqlserver.js
|
||||
static/src/js/ace/src-noconflict/snippets/stylus.js
|
||||
static/src/js/ace/src-noconflict/snippets/svg.js
|
||||
static/src/js/ace/src-noconflict/snippets/swift.js
|
||||
static/src/js/ace/src-noconflict/snippets/tcl.js
|
||||
static/src/js/ace/src-noconflict/snippets/terraform.js
|
||||
static/src/js/ace/src-noconflict/snippets/tex.js
|
||||
static/src/js/ace/src-noconflict/snippets/text.js
|
||||
static/src/js/ace/src-noconflict/snippets/textile.js
|
||||
static/src/js/ace/src-noconflict/snippets/toml.js
|
||||
static/src/js/ace/src-noconflict/snippets/tsv.js
|
||||
static/src/js/ace/src-noconflict/snippets/tsx.js
|
||||
static/src/js/ace/src-noconflict/snippets/turtle.js
|
||||
static/src/js/ace/src-noconflict/snippets/twig.js
|
||||
static/src/js/ace/src-noconflict/snippets/typescript.js
|
||||
static/src/js/ace/src-noconflict/snippets/vala.js
|
||||
static/src/js/ace/src-noconflict/snippets/vbscript.js
|
||||
static/src/js/ace/src-noconflict/snippets/velocity.js
|
||||
static/src/js/ace/src-noconflict/snippets/verilog.js
|
||||
static/src/js/ace/src-noconflict/snippets/vhdl.js
|
||||
static/src/js/ace/src-noconflict/snippets/visualforce.js
|
||||
static/src/js/ace/src-noconflict/snippets/vue.js
|
||||
static/src/js/ace/src-noconflict/snippets/wollok.js
|
||||
static/src/js/ace/src-noconflict/snippets/xml.js
|
||||
static/src/js/ace/src-noconflict/snippets/xquery.js
|
||||
static/src/js/ace/src-noconflict/snippets/yaml.js
|
||||
static/src/js/ace/src-noconflict/snippets/zeek.js
|
||||
static/src/js/ace/src-noconflict/snippets/zig.js
|
||||
models/__pycache__/__init__.cpython-311.pyc
|
||||
models/__pycache__/company.cpython-311.pyc
|
||||
models/__pycache__/dss_ads.cpython-311.pyc
|
||||
models/__pycache__/dss_advertisefields.cpython-311.pyc
|
||||
models/__pycache__/dss_binaries.cpython-311.pyc
|
||||
models/__pycache__/dss_contract_uservar.cpython-311.pyc
|
||||
models/__pycache__/dss_contract.cpython-311.pyc
|
||||
models/__pycache__/dss_crm_extended.cpython-311.pyc
|
||||
models/__pycache__/dss_display_templates.cpython-311.pyc
|
||||
models/__pycache__/dss_eventdays.cpython-311.pyc
|
||||
models/__pycache__/dss_kileads_requests.cpython-311.pyc
|
||||
models/__pycache__/dss_kileads.cpython-311.pyc
|
||||
models/__pycache__/dss_linkmail.cpython-311.pyc
|
||||
models/__pycache__/dss_m2mmail.cpython-311.pyc
|
||||
models/__pycache__/dss_maintains.cpython-311.pyc
|
||||
models/__pycache__/dss_marker.cpython-311.pyc
|
||||
models/__pycache__/dss_onlinestate.cpython-311.pyc
|
||||
models/__pycache__/dss_projects.cpython-311.pyc
|
||||
models/__pycache__/dss_res_users.cpython-311.pyc
|
||||
models/__pycache__/dss_settings_variables.cpython-311.pyc
|
||||
models/__pycache__/dss_settings.cpython-311.pyc
|
||||
models/__pycache__/dss_systemtypen.cpython-311.pyc
|
||||
models/__pycache__/dss_trigger.cpython-311.pyc
|
||||
models/__pycache__/dss_web_contracts_all.cpython-311.pyc
|
||||
models/__pycache__/dss_web_contracts_all2.cpython-311.pyc
|
||||
models/__pycache__/dss_web_contracts_entry.cpython-311.pyc
|
||||
models/__pycache__/dss_web_contracts_stream.cpython-311.pyc
|
||||
models/__pycache__/dss_web_contracts.cpython-311.pyc
|
||||
models/__pycache__/dss.cpython-311.pyc
|
||||
models/__pycache__/dsslogger.cpython-311.pyc
|
||||
models/__pycache__/utils.cpython-311.pyc
|
||||
|
|
@ -21,6 +21,7 @@
|
|||
'views/dss_display_templates.xml',
|
||||
'views/dss_advertisementfields_templates.xml',
|
||||
'views/dss_advertisementfields.xml',
|
||||
'views/dss_advertisementfields_create_from_template_wizard.xml',
|
||||
'views/dss_projects.xml',
|
||||
'views/dss_import.xml',
|
||||
'views/dss_ads.xml',
|
||||
|
|
@ -50,6 +51,11 @@
|
|||
'views/dss_binaries.xml',
|
||||
'views/mainsystem_view.xml',
|
||||
'views/dss_importinvoicelist.xml',
|
||||
'views/dss_company_type_views.xml',
|
||||
'views/dss_csvs.xml',
|
||||
'views/dss_phone_center.xml',
|
||||
'views/dss_phonecenter_query_views.xml',
|
||||
|
||||
'views/menu.xml',
|
||||
'views/company_view.xml',
|
||||
'views/google_map.xml',
|
||||
|
|
@ -62,6 +68,7 @@
|
|||
'views/dss_web_contracts_all_templates.xml',
|
||||
'views/dss_contract_uservars.xml',
|
||||
'views/dss_portal_add.xml',
|
||||
'views/adportal_dashboard.xml',
|
||||
#'views/dss_screendesign.xml',
|
||||
],
|
||||
'demo': [],
|
||||
|
|
@ -72,6 +79,9 @@
|
|||
'assets': {
|
||||
'web.assets_backend': [
|
||||
'DigitalSignage/static/src/img/*',
|
||||
'DigitalSignage/static/src/xml/navbar_ai_button.xml',
|
||||
'DigitalSignage/static/src/js/navbar_ai_button.js',
|
||||
'DigitalSignage/static/src/scss/navbar_ai_button.scss',
|
||||
'DigitalSignage/static/src/xml/screenDesignerView.xml',
|
||||
'DigitalSignage/static/src/xml/dss_screenview_controller.xml',
|
||||
'DigitalSignage/static/src/xml/dss_screenview_renderer.xml',
|
||||
|
|
@ -100,9 +110,12 @@
|
|||
'DigitalSignage/static/src/js/kanban_button.js',
|
||||
'DigitalSignage/static/src/js/dss_crm_extended_ki_leads.js',
|
||||
'DigitalSignage/static/src/js/kilead_hook.js',
|
||||
'DigitalSignage/static/src/js/phone_center_action.js',
|
||||
'DigitalSignage/static/src/xml/form_button.xml',
|
||||
'DigitalSignage/static/src/xml/kanban_button.xml',
|
||||
'DigitalSignage/static/src/xml/form_label.xml',
|
||||
'DigitalSignage/static/src/xml/phone_center_action.xml',
|
||||
'DigitalSignage/static/src/scss/phone_center_action.scss',
|
||||
'DigitalSignage/static/src/js/test.php',
|
||||
],
|
||||
'web.assets_common': [
|
||||
|
|
@ -117,6 +130,7 @@
|
|||
],
|
||||
'web.assets_qweb': [
|
||||
'DigitalSignage/static/src/xml/kanban_button.xml',
|
||||
'DigitalSignage/static/src/xml/phone_center_action.xml',
|
||||
],
|
||||
},
|
||||
'license': 'LGPL-3',
|
||||
|
|
|
|||
|
|
@ -6,3 +6,5 @@ from . import dss_screendesigner_controller
|
|||
from . import dss_screenview_controller
|
||||
from . import dss_nextcloudwidget
|
||||
from . import dss_form_input_controller
|
||||
from . import ai_agent_controller
|
||||
from . import adportal
|
||||
|
|
|
|||
0
controllers/__pycache__/dss_screendesigner_controller.cpython-311.pyc
Normal file → Executable file
|
|
@ -61,6 +61,14 @@ class CustomerPortal(portal.CustomerPortal):
|
|||
contract_name = str(tempid)+' - Vertrag ' + str(project.lastkdnr).zfill(2)
|
||||
contract_id = str(project.projectid) + str(project.lastrealkdnr).zfill(2)
|
||||
grundsystem_webinput_template=project.web_input_template
|
||||
felder = request.env['dss.advertisefields'].search(['&','|',('contract','=',False),('isblocked','=',False),('project','=',project.id)])
|
||||
comptyps = request.env['dss.company.type'].search([])
|
||||
fo =""
|
||||
for feld in felder:
|
||||
fo = fo+"<option value=''>"+feld.feldname+"</option>"
|
||||
ctn = ""
|
||||
for comptyp in comptyps:
|
||||
ctn = ctn+"<option value='"+comptyp.uuid+"'>"+comptyp.Firmetyp_name+"</option>"
|
||||
if not grundsystem_webinput_template:
|
||||
grundsystem_webinput_template = project.grundsystem_webinput_template
|
||||
if not grundsystem_webinput_template:
|
||||
|
|
@ -68,8 +76,27 @@ class CustomerPortal(portal.CustomerPortal):
|
|||
else:
|
||||
new_contract = request.env['dss.contracts'].create({'project_id':project.projectid,'project':project.id,'client_id':str(project.lastkdnr).zfill(2),'contract_auto_id':str(tempid),'contract_auto_name':str(tempname),'contract_state':39,'contract_name':contract_name,'web_contract_input_notfinished':True,'contract_creator':int(user_id.id)})
|
||||
contract_data_id=new_contract.id
|
||||
formdata = {
|
||||
'websideusername':name,
|
||||
'user_id':str(user_id.id),
|
||||
'projectid':str(project.projectid),
|
||||
'project':str(projekt_id),
|
||||
'contract_id':str(contract_id),
|
||||
'kd_id':str(str(project.lastrealkdnr).zfill(2)),
|
||||
'web_contract_paysum_gfx':'249',
|
||||
'contract_data_id':str(contract_data_id),
|
||||
'company_type_options':str(ctn),
|
||||
'client_short_company_type_options':str(ctn),
|
||||
'feldoptions':fo,
|
||||
'lastschrift': 'off',
|
||||
'satzmail': 'off',
|
||||
'rgmail': 'off',
|
||||
'kamail': 'off',
|
||||
'satzgiven': 'off',
|
||||
'showimage' : project.grundsystem_webinput_showimage_url
|
||||
}
|
||||
_logger.info("inside start: "+str(name)+'--'+str(projekt_id)+'--'+str(grundsystem_webinput_template))
|
||||
return request.render("website."+grundsystem_webinput_template,{'websideusername':name,'user_id':str(user_id.id),'projectid':str(project.projectid),'project':str(projekt_id),'contract_id':str(contract_id),'kd_id':str(str(project.lastrealkdnr).zfill(2)),'web_contract_paysum_gfx':'249','contract_data_id':str(contract_data_id)})
|
||||
return request.render("website."+grundsystem_webinput_template,formdata)
|
||||
|
||||
@route(['/eingabe/continue'],type='http', auth='user', website=True, csrf=False)
|
||||
def inputcontinue(self, **kwargs):
|
||||
|
|
@ -81,63 +108,116 @@ class CustomerPortal(portal.CustomerPortal):
|
|||
grundsystem_webinput_template=project.web_input_template
|
||||
if not grundsystem_webinput_template:
|
||||
grundsystem_webinput_template = project.grundsystem_webinput_template
|
||||
_logger.info("inside continue: "+str(contract.contract_auto_name)+'--'+str(project.id)+'--'+str(grundsystem_webinput_template))
|
||||
_logger.info("inside continue: "+str(contract.contract_auto_name)+'--'+str(project.id)+'--'+str(grundsystem_webinput_template)+'--'+str(str(contract.client_short_company_type.uuid)))
|
||||
felder = request.env['dss.advertisefields'].search(['&','|',('contract','=',False),('isblocked','=',False),('project','=',project.id)])
|
||||
comptyps = request.env['dss.company.type'].search([])
|
||||
fo =""
|
||||
sfeld =""
|
||||
ctn = ""
|
||||
for feld in felder:
|
||||
fo = fo+"<option value=''>"+feld.feldname+"</option>"
|
||||
for feld in contract.werbe_feld_selected:
|
||||
sfeld = sfeld+feld.auto_feldname+", "
|
||||
for comptyp in comptyps:
|
||||
ctn = ctn+"<option value='"+comptyp.uuid+"' "+("selected" if comptyp.uuid == contract.client_short_company_type.uuid else "")+">"+comptyp.Firmetyp_name+"</option>"
|
||||
evtlcontract_id = str(contract.project.projectid) + str(contract.project.lastrealkdnr).zfill(2)
|
||||
evtlcontract_name = str(evtlcontract_id)+'_'+str(contract.client_short_company) if contract.client_short_company else str(contract.client_short_name)+' '+str(contract.client_short_vorname)
|
||||
olddata = {
|
||||
olddata = {
|
||||
'user_id':str(user_id),
|
||||
'projectid':str(project.projectid),
|
||||
'project':str(project.id),
|
||||
'company_type_options':str(ctn),
|
||||
'contract_id':str(evtlcontract_id),
|
||||
'client_short_company':str(contract.client_short_company),
|
||||
'client_short_strasse':str(contract.client_short_strasse),
|
||||
'client_short_plz':str(contract.client_short_plz),
|
||||
'client_short_telefon':str(contract.client_short_telefon),
|
||||
'client_short_email':str(contract.client_short_email),
|
||||
'client_short_rgemail':str(contract.client_short_rgemail),
|
||||
'client_short_ort':str(contract.client_short_ort),
|
||||
'client_short_mobil':str(contract.client_short_mobil),
|
||||
'client_short_website':str(contract.client_short_website),
|
||||
'client_short_vorname':str(contract.client_short_vorname),
|
||||
'client_short_name':str(contract.client_short_name),
|
||||
'web_contract_paysum_lz':contract.web_contract_paysum_lz,
|
||||
'web_contract_paysum_all':contract.web_contract_paysum_all,
|
||||
'web_contract_paysum_gfx':contract.web_contract_paysum_gfx,
|
||||
'client_short_company':str(contract.client_short_company) if contract.client_short_company else '',
|
||||
'client_short_company_type':str(contract.client_short_company_type.uuid),
|
||||
'client_short_company_type_options':str(ctn) ,
|
||||
'client_short_strasse':str(contract.client_short_strasse) if contract.client_short_strasse else '',
|
||||
'client_short_plz':str(contract.client_short_plz) if contract.client_short_plz else '',
|
||||
'client_short_telefon':str(contract.client_short_telefon) if contract.client_short_telefon else '',
|
||||
'client_short_email':str(contract.client_short_email) if contract.client_short_email else '',
|
||||
'client_short_rgemail':str(contract.client_short_rgemail) if contract.client_short_rgemail else '',
|
||||
'client_short_ort':str(contract.client_short_ort) if contract.client_short_ort else '',
|
||||
'client_short_mobil':str(contract.client_short_mobil) if contract.client_short_mobil else '',
|
||||
'client_short_website':str(contract.client_short_website) if contract.client_short_website else '',
|
||||
'client_short_vorname':str(contract.client_short_vorname) if contract.client_short_vorname else '',
|
||||
'client_short_name':str(contract.client_short_name) if contract.client_short_name else '',
|
||||
'web_contract_paysum_lz':contract.web_contract_paysum_lz if contract.web_contract_paysum_lz else '0',
|
||||
'web_contract_paysum_all':contract.web_contract_paysum_all if contract.web_contract_paysum_all else '0',
|
||||
'web_contract_paysum_gfx':contract.web_contract_paysum_gfx if contract.web_contract_paysum_gfx else '0',
|
||||
'web_contract_full_duration':contract.main_runtime if contract.main_runtime else '0',
|
||||
'web_contract_spot_count':contract.split_runtime_count if contract.split_runtime_count else '0',
|
||||
'web_contract_spot_duration':contract.split_runtime_time if contract.split_runtime_time else '0',
|
||||
'web_tv_reach_PLZ':contract.tv_reach_PLZ if contract.tv_reach_PLZ else '',
|
||||
'web_ads_last_date_start_planed':contract.web_contract_start_planed if contract.web_contract_start_planed else '',
|
||||
'web_ads_last_date_remove_planed':contract.web_contract_remove_planed if contract.web_contract_remove_planed else '',
|
||||
'web_bande_spieltage':contract.web_bande_spieltage if contract.web_bande_spieltage else '',
|
||||
'contract_data_id':str(contract.id),
|
||||
'runtime_m':contract.runtime_m if contract.runtime_m else '60',
|
||||
'lastschrift': 'on' if contract.web_contract_is_lastschrift else 'off',
|
||||
'zahlsonst': contract.web_constract_zahlstonst if contract.web_constract_zahlstonst else '',
|
||||
'feldoptions':fo,
|
||||
'feldselected':sfeld,'kd_id':evtlcontract_name,
|
||||
'Datum': contract.web_contract_sign_datum,
|
||||
'Ort': contract.web_contract_sign_ort,
|
||||
'NameVorname': contract.web_contract_sign_name,
|
||||
'bic': contract.client_short_BIC,
|
||||
'iban': contract.client_short_IBAN,
|
||||
'kapay': contract.web_contract_kapayment_info,
|
||||
'alpay': contract.web_contract_alpayment_info,
|
||||
'sonder_0': contract.web_contract_sonder_0,
|
||||
'sonder_1': contract.web_contract_sonder_1,
|
||||
'sonder_2': contract.web_contract_sonder_2,
|
||||
'feldselected':sfeld if sfeld else '',
|
||||
'kd_id':evtlcontract_name,
|
||||
'ort': contract.web_contract_sign_ort if contract.web_contract_sign_ort else '',
|
||||
'namevorname': contract.web_contract_sign_name if contract.web_contract_sign_name else '',
|
||||
'bic': contract.client_short_BIC if contract.client_short_BIC else '',
|
||||
'iban': contract.client_short_IBAN if contract.client_short_IBAN else '',
|
||||
'kapay': contract.web_contract_kapayment_info if contract.web_contract_kapayment_info else '',
|
||||
'alpay': contract.web_contract_alpayment_info if contract.web_contract_alpayment_info else '',
|
||||
'sonder_0': contract.web_contract_sonder_0 if contract.web_contract_sonder_0 else '',
|
||||
'sonder_1': contract.web_contract_sonder_1 if contract.web_contract_sonder_1 else '',
|
||||
'sonder_2': contract.web_contract_sonder_2 if contract.web_contract_sonder_2 else '',
|
||||
'satzmail': 'on' if contract.web_contract_satzmail else 'off',
|
||||
'rgmail': 'on' if contract.web_contract_rgmail else 'off' ,
|
||||
'kamail': 'on' if contract.web_contract_kamail else 'off',
|
||||
'satzgiven': 'on' if contract.web_contract_satzgiven else 'off',
|
||||
'web_ads_count_per_year2': contract.ads_count_perYear2 if contract.ads_count_perYear2 else '0',
|
||||
'contract_auto_extend' : 'on' if contract.contract_auto_extend else 'off',
|
||||
'contract_auto_extend_time': contract.contract_auto_extend_time if contract.contract_auto_extend_time else '0',
|
||||
'web_contract_cancel_date': contract.contract_cancel_date.strftime("%d.%m.%Y") if contract.contract_cancel_date else '',
|
||||
'edited': "1" # damit im Template erkannt wird, dass es sich um eine Fortsetzung handelt und nicht um einen neuen Vertrag
|
||||
}
|
||||
return request.render("website."+grundsystem_webinput_template, olddata)
|
||||
|
||||
@route(['/eingabe/upload'],type='http', auth='user', website=True, csrf=False)
|
||||
def inputcontinueupload(self, **kwargs):
|
||||
_logger.info("inside upload continue : " + str(request.params)+' --'+str(kwargs)+' --'+str(request.httprequest.data))
|
||||
user_id = request.env.user
|
||||
contract = request.env['dss.contracts'].search([('uuid','=',kwargs.get('uuid'))],limit=1)
|
||||
contract_id = kwargs.get('contract_id')
|
||||
contract_data_id = contract.id
|
||||
kd_id = kwargs.get('kd_id')
|
||||
#contract = self.setcontract(request.env['dss.contracts'].search([('id','=',contract_data_id)],limit=1),kwargs)
|
||||
uploads=request.env['dss.mediatypes'].search([('weboptionupload_show','=',True)])
|
||||
return request.render("website.eingabe-phase3-upload",{'user_id':str(user_id),'contract_id':str(contract_id),'kd_id':str(kd_id),'contract_data_id':str(contract_data_id),'uploadfiles':uploads})
|
||||
|
||||
@route(['/eingabe/finish'],type='http', auth='user', website=True, csrf=False)
|
||||
def finish_contract(self, **kwargs):
|
||||
_logger.info("inside: start changeScreenTemplateView" + str(request.params)+' --'+str(kwargs)+' --'+str(request.httprequest.data))
|
||||
uuid = kwargs.get('uuid')
|
||||
contract = request.env['dss.contracts'].search([('uuid','=',uuid)],limit=1)
|
||||
_logger.info("inside finish_contract: "+str(contract.id)+'--'+str(contract.contract_auto_id)+'--'+str(uuid))
|
||||
_logger.info("inside finish_contract: "+str(contract.id)+'--'+str(contract.contract_auto_id)+'--'+str(uuid))
|
||||
if not contract.web_contract_input_finished:
|
||||
if contract.project.lostkdnr:
|
||||
if len(contract.project.lostkdnr)>0:
|
||||
nextnr = contract.project.lostkdnr.split(',')[0]
|
||||
contract.project.lostkdnr = ','.join(contract.project.lostkdnr.split(',')[1:])
|
||||
else:
|
||||
contract.project.lastrealkdnr += 1
|
||||
nextnr = str(contract.project.lastrealkdnr).zfill(2)
|
||||
else:
|
||||
contract.project.lastrealkdnr += 1
|
||||
nextnr = str(contract.project.lastrealkdnr).zfill(2)
|
||||
contract_id = str(contract.project.projectid) + str(nextnr)
|
||||
contract.contract_id = str(nextnr)
|
||||
contract.client_id = str(nextnr)
|
||||
contract.contract_auto_id = contract_id
|
||||
contract.contract_name = str(contract.client_short_company) if contract.client_short_company else str(contract.client_short_name)+' '+str(contract.client_short_vorname)
|
||||
contract.contract_auto_name=str(contract_id)+'_'+str(contract.client_short_company) if contract.client_short_company else str(contract.client_short_name)+' '+str(contract.client_short_vorname)
|
||||
dsscontracts.tokampagne(contract)
|
||||
contract.web_contract_input_finished = True
|
||||
contract.web_contract_rejected = False
|
||||
contract.web_contract_input_notfinished=False
|
||||
contract_id = str(contract.project.projectid) + str(contract.project.lastrealkdnr).zfill(2)
|
||||
contract.contract_auto_id = contract_id
|
||||
contract.contract_name = str(contract_id)+' - '+str(contract.client_short_company) if contract.client_short_company else str(contract.client_short_name)+' '+str(contract.client_short_vorname)
|
||||
contract.contract_auto_name=str(contract_id)+'_'+str(contract.client_short_company) if contract.client_short_company else str(contract.client_short_name)+' '+str(contract.client_short_vorname)
|
||||
dsscontracts.tokampagne(contract)
|
||||
return request.render("website.eingabe-finished",{'contract':contract,'contract_auto_id':contract.contract_auto_id})
|
||||
|
||||
@route(['/eingabe/cancel'],type='http', auth='user', website=True, csrf=False)
|
||||
|
|
@ -151,7 +231,8 @@ class CustomerPortal(portal.CustomerPortal):
|
|||
|
||||
def setcontract(self,contract,kwargs):
|
||||
projekt_id = kwargs.get('project')
|
||||
contract.client_short_company = kwargs.get('client_short_company') if kwargs.get('client_short_company') else ''
|
||||
contract.client_short_company = kwargs.get('client_short_company') if kwargs.get('client_short_company') else ''
|
||||
contract.client_short_company_type = request.env['dss.company.type'].search([('uuid','=',kwargs.get('client_short_company_type'))],limit=1) if kwargs.get('client_short_company_type') else ''
|
||||
contract.client_short_vorname = kwargs.get('client_short_vorname') if kwargs.get('client_short_vorname') else ''
|
||||
contract.client_short_name = kwargs.get('client_short_name') if kwargs.get('client_short_name') else ''
|
||||
contract.client_short_telefon = kwargs.get('client_short_telefon') if kwargs.get('client_short_telefon') else ''
|
||||
|
|
@ -162,24 +243,62 @@ class CustomerPortal(portal.CustomerPortal):
|
|||
contract.client_short_ort = kwargs.get('client_short_ort') if kwargs.get('client_short_ort') else ''
|
||||
contract.client_short_mobil = kwargs.get('client_short_mobil') if kwargs.get('client_short_mobil') else ''
|
||||
contract.client_short_website = kwargs.get('client_short_website') if kwargs.get('client_short_website') else ''
|
||||
contract.runtimesystem = "M"
|
||||
contract.runtime_m = kwargs.get('contract_time') if kwargs.get('contract_time') else '64'
|
||||
contract.runtimesystem = "M"
|
||||
|
||||
try:
|
||||
laufzeit = int(kwargs.get('contract_time'))
|
||||
except:
|
||||
laufzeit = 60
|
||||
contract.runtime_m = laufzeit if laufzeit > 0 else kwargs.get('runtime_m') if laufzeit == -1 else 60
|
||||
contract.web_contract_paysum_lz = kwargs.get('web_contract_paysum_lz') if kwargs.get('web_contract_paysum_lz') else 0.0
|
||||
contract.web_contract_paysum_gfx = kwargs.get('web_contract_paysum_gfx') if kwargs.get('web_contract_paysum_gfx') else 0.0
|
||||
contract.web_contract_paysum_all = kwargs.get('web_contract_paysum_all') if kwargs.get('web_contract_paysum_all') else 0.0
|
||||
contract.web_contract_sign_datum = kwargs.get('Datum') if kwargs.get('Datum') else ''
|
||||
contract.web_contract_sign_ort = kwargs.get('Ort') if kwargs.get('Ort') else ''
|
||||
contract.contract_date = datetime.datetime.strptime(contract.web_contract_sign_datum, "%d.%m.%Y").date()
|
||||
contract.web_contract_sign_name = kwargs.get('NameVorname') if kwargs.get('NameVorname') else ''
|
||||
contract.client_short_BIC = kwargs.get('bic') if kwargs.get('bic') else ''
|
||||
contract.client_short_IBAN = kwargs.get('iban') if kwargs.get('iban') else ''
|
||||
contract.web_contract_is_lastschrift = kwargs.get('lastschrift') == 'on' if kwargs.get('lastschrift') else False
|
||||
contract.web_contract_is_lastschrift = True if kwargs.get('lastschrift') else False
|
||||
contract.web_constract_zahlstonst = kwargs.get('zahlsonst') if kwargs.get('zahlsonst') else ''
|
||||
contract.work_state_info = kwargs.get('sonder_2') if kwargs.get('sonder_2') else ''
|
||||
contract.web_contract_kapayment_info = kwargs.get('kapay') if kwargs.get('kapay') else ''
|
||||
contract.web_contract_alpayment_info = kwargs.get('alpay') if kwargs.get('alpay') else ''
|
||||
contract.web_contract_satzmail = True if kwargs.get('satzmail') else False
|
||||
contract.web_contract_satzgiven = True if kwargs.get('satzgiven') else False
|
||||
contract.web_contract_rgmail = True if kwargs.get('rgmail') else False
|
||||
contract.web_contract_kamail = True if kwargs.get('kamail') else False
|
||||
contract.web_contract_sonder_0 = kwargs.get('sonder_0') if kwargs.get('sonder_0') else ''
|
||||
contract.web_contract_sonder_1 = kwargs.get('sonder_1') if kwargs.get('sonder_1') else ''
|
||||
contract.web_contract_sonder_2 = kwargs.get('sonder_2') if kwargs.get('sonder_2') else ''
|
||||
contract.main_runtime = kwargs.get('web_contract_full_duration') if kwargs.get('web_contract_full_duration') else '0'
|
||||
contract.split_runtime_count = kwargs.get('web_contract_spot_count') if kwargs.get('web_contract_spot_count') else '0'
|
||||
contract.split_runtime_time = kwargs.get('web_contract_spot_duration') if kwargs.get('web_contract_spot_duration') else '0'
|
||||
contract.ads_count_perYear2 = kwargs.get('web_ads_count_per_year2') if kwargs.get('web_ads_count_per_year2') else '0'
|
||||
contract.tv_reach_PLZ = kwargs.get('web_tv_reach_PLZ') if kwargs.get('web_tv_reach_PLZ') else ''
|
||||
contract.web_contract_start_planed = kwargs.get('web_ads_last_date_start_planed') if kwargs.get('web_ads_last_date_start_planed') else ''
|
||||
contract.web_contract_remove_planed = kwargs.get('web_ads_last_date_remove_planed') if kwargs.get('web_ads_last_date_remove_planed') else ''
|
||||
contract.web_bande_spieltage = kwargs.get('web_bande_spieltage') if kwargs.get('web_bande_spieltage') else ''
|
||||
contract.contract_auto_extend = True if kwargs.get('contract_auto_extend') else False
|
||||
contract.contract_auto_extend_time = kwargs.get('contract_auto_extend_time') if kwargs.get('contract_auto_extend_time') else '0'
|
||||
cancel_date = kwargs.get('web_contract_cancel_date') if kwargs.get('web_contract_cancel_date') else ''
|
||||
if cancel_date:
|
||||
try:
|
||||
datetime.datetime.strptime(cancel_date, "%Y-%m-%d")
|
||||
contract.contract_cancel_date = cancel_date
|
||||
except ValueError:
|
||||
try:
|
||||
converted_date = datetime.datetime.strptime(cancel_date, "%d.%m.%Y").strftime("%Y-%m-%d")
|
||||
contract.contract_cancel_date = converted_date
|
||||
except ValueError:
|
||||
contract.contract_cancel_date = ''
|
||||
else:
|
||||
contract.contract_cancel_date = ''
|
||||
|
||||
try:
|
||||
contract.contract_date = datetime.datetime.strptime(contract.web_contract_sign_datum, "%d.%m.%Y").date()
|
||||
except:
|
||||
# ignore error and do not set date if format is wrong
|
||||
pass
|
||||
gesamts = kwargs.get('sonder_0') if kwargs.get('sonder_0') else ''
|
||||
gesamts = gesamts+' //// '+kwargs.get('sonder_1') if kwargs.get('sonder_1') else ''
|
||||
contract.remark = gesamts
|
||||
|
|
@ -189,11 +308,15 @@ class CustomerPortal(portal.CustomerPortal):
|
|||
for wfa in wfarray:
|
||||
if wfa:
|
||||
if '_' in wfa:
|
||||
wfa = wfa.split('_')[1]
|
||||
wfa = wfa.split('_')[1]
|
||||
wfaid = request.env['dss.advertisefields'].search([('feldname','=',wfa.strip()),('project','=',contract.project.id)],limit=1)
|
||||
_logger.info("inside update : "+str(projekt_id)+'/'+str(contract.project.id)+'--'+str(wfields)+'--'+str(wfa.strip()+'--'+str(wfaid)))
|
||||
if wfaid:
|
||||
contract.werbe_feld_selected = [(4, wfaid.id)]
|
||||
else:
|
||||
wfaid = request.env['dss.advertisefields'].create({'feldname':wfa.strip(),'project':contract.project.id,'auto_feldname':str(str(contract.project.projectid)+'_'+wfa.strip())})
|
||||
if wfaid:
|
||||
contract.werbe_feld_selected = [(4, wfaid.id)]
|
||||
return contract
|
||||
|
||||
@route(['/eingabe/submit_lcdtouch'],type='http', auth='user', website=True, csrf=False)
|
||||
|
|
@ -206,11 +329,34 @@ class CustomerPortal(portal.CustomerPortal):
|
|||
contract_id = kwargs.get('contract_id')
|
||||
kd_id = kwargs.get('kd_id')
|
||||
contract_data_id = kwargs.get('contract_data_id')
|
||||
_logger.info("inside: "+str(name)+'--'+str(projekt_id)+'--'+str(kd_id)+'--'+str(contract_id)+'--'+str(contract_data_id)+'--'+str(projectid)+'--'+str(kwargs.get('kamail')))
|
||||
_logger.info("inside: "+str(name)+'--'+str(projekt_id)+'--'+str(kd_id)+'--'+str(contract_id)+'--'+str(contract_data_id)+'--'+str(projectid)+'--'+str(kwargs.get('kamail')))
|
||||
contract = self.setcontract(request.env['dss.contracts'].search([('id','=',contract_data_id)],limit=1),kwargs)
|
||||
return request.render("website.eingabe-phase3-upload",{'websideusername':name,'user_id':str(user_id),'projectid':str(projectid),'project':str(projekt_id),'contract_id':str(contract_id),'kd_id':str(kd_id),'contract_data_id':str(contract_data_id)})
|
||||
uploads=request.env['dss.mediatypes'].search([('weboptionupload_show','=',True)])
|
||||
return request.render("website.eingabe-phase3-upload",{'websideusername':name,'user_id':str(user_id),'projectid':str(projectid),'project':str(projekt_id),'contract_id':str(contract_id),'kd_id':str(kd_id),'contract_data_id':str(contract_data_id),'uploadfiles':uploads})
|
||||
|
||||
@route(['/eingabe/submit_kfz'],type='http', auth='user', website=True, csrf=False)
|
||||
@route(['/eingabe/submit_save'],type='json', auth='user', methods=['POST'], csrf=True)
|
||||
def submit_save(self, **kwargs):
|
||||
_logger.info("inside: Save Received Data " + str(request.params)+' --'+str(kwargs)+' --'+str(request.httprequest.data))
|
||||
kwargs = json.loads(request.httprequest.data.decode('utf-8'))
|
||||
_logger.info("inside: Save Received Data " + str(request.params)+' --'+str(kwargs)+' --'+str(request.httprequest.data))
|
||||
name = kwargs.get('websideusername')
|
||||
projekt_id = kwargs.get('project')
|
||||
projectid = kwargs.get('projectid')
|
||||
user_id = kwargs.get('user_id')
|
||||
contract_id = kwargs.get('contract_id')
|
||||
contract_data_id = kwargs.get('contract_data_id')
|
||||
kd_id = kwargs.get('kd_id')
|
||||
contract = self.setcontract(request.env['dss.contracts'].search([('id','=',contract_data_id)],limit=1),kwargs)
|
||||
_logger.info("inside: Save Received Data "+str(name)+'--'+str(projekt_id)+'--'+str(kd_id)+'--'+str(contract_id)+'--'+str(contract_data_id))
|
||||
output = {
|
||||
'results':{
|
||||
'code':200,
|
||||
'message':'OK'
|
||||
}
|
||||
}
|
||||
return json.dumps(output)
|
||||
|
||||
@route(['/eingabe/submit_all'],type='http', auth='user', website=True, csrf=False)
|
||||
def submit_kfz(self, **kwargs):
|
||||
_logger.info("inside: start changeScreenTemplateView" + str(request.params)+' --'+str(kwargs)+' --'+str(request.httprequest.data))
|
||||
name = kwargs.get('websideusername')
|
||||
|
|
@ -222,7 +368,8 @@ class CustomerPortal(portal.CustomerPortal):
|
|||
kd_id = kwargs.get('kd_id')
|
||||
contract = self.setcontract(request.env['dss.contracts'].search([('id','=',contract_data_id)],limit=1),kwargs)
|
||||
_logger.info("inside: "+str(name)+'--'+str(projekt_id)+'--'+str(kd_id)+'--'+str(contract_id)+'--'+str(contract_data_id))
|
||||
return request.render("website.eingabe-phase3-upload",{'websideusername':name,'user_id':str(user_id),'projectid':str(projectid),'project':str(projekt_id),'contract_id':str(contract_id),'kd_id':str(kd_id),'contract_data_id':str(contract_data_id)})
|
||||
uploads=request.env['dss.mediatypes'].search([('weboptionupload_show','=',True)])
|
||||
return request.render("website.eingabe-phase3-upload",{'websideusername':name,'user_id':str(user_id),'projectid':str(projectid),'project':str(projekt_id),'contract_id':str(contract_id),'kd_id':str(kd_id),'contract_data_id':str(contract_data_id),'uploadfiles':uploads})
|
||||
|
||||
@route(['/eingabe/submit_stream'],type='http', auth='user', website=True, csrf=False)
|
||||
def submit_stream(self, **kwargs):
|
||||
|
|
@ -233,6 +380,8 @@ class CustomerPortal(portal.CustomerPortal):
|
|||
user_id = kwargs.get('user_id')
|
||||
contract_id = kwargs.get('contract_id')
|
||||
kd_id = kwargs.get('kd_id')
|
||||
a=2
|
||||
|
||||
contract_data_id = kwargs.get('contract_data_id')
|
||||
contract = self.setcontract(request.env['dss.contracts'].search([('id','=',contract_data_id)],limit=1),kwargs)
|
||||
_logger.info("inside: "+str(name)+'--'+str(projekt_id)+'--'+str(kd_id)+'--'+str(contract_id)+'--'+str(contract_data_id))
|
||||
|
|
@ -247,8 +396,15 @@ class CustomerPortal(portal.CustomerPortal):
|
|||
user_id = kwargs.get('user_id')
|
||||
contract_id = kwargs.get('contract_id')
|
||||
kd_id = kwargs.get('kd_id')
|
||||
filetype = kwargs.get('filetype')
|
||||
file = kwargs.get('filename')
|
||||
b64file = base64.b64encode(file.read())
|
||||
contract_data_id = kwargs.get('contract_data_id')
|
||||
_logger.info("inside: "+str(name)+'--'+str(projekt_id)+'--'+str(kd_id)+'--'+str(contract_id)+'--'+str(contract_data_id))
|
||||
contract=request.env['dss.contracts'].search([('id','=',contract_data_id)],limit=1)
|
||||
if filetype=="1":
|
||||
_logger.info("inside: set Contract "+str(b64file))
|
||||
contract.scan_vertrag=b64file
|
||||
_logger.info("inside: "+str(name)+'--'+str(projekt_id)+'--'+str(kd_id)+'--'+str(contract_id)+'--'+str(contract_data_id)+'--'+str(file)+'---'+str(b64file))
|
||||
return request.render("website.eingabe-phase4-finish",{'websideusername':name,'user_id':str(user_id),'projectid':str(projectid),'project':str(projekt_id),'contract_id':str(contract_id),'kd_id':str(kd_id),'contract_data_id':str(contract_data_id)})
|
||||
|
||||
@route(['/eingabe/submit_finish'],type='http', auth='user', website=True, csrf=False)
|
||||
|
|
@ -264,7 +420,7 @@ class CustomerPortal(portal.CustomerPortal):
|
|||
submit_finish = kwargs.get('submit')
|
||||
_logger.info("inside finish : "+str(name)+'--'+str(projekt_id)+'--'+str(kd_id)+'--'+str(contract_id)+'--'+str(contract_data_id)+'--'+str(submit_finish))
|
||||
if submit_finish == 'Eingabe beenden':
|
||||
return request.render("website.eingabe-phase1")
|
||||
return self.ad_portal_my_documents(self, **kwargs)
|
||||
if submit_finish == 'Neuen Vertrag im gleichen Projekt':
|
||||
return self.changeScreenView(**kwargs)
|
||||
if submit_finish == 'Neuen Vertrag in anderen Projekt':
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ from . import dss_settings
|
|||
from . import dss_trigger
|
||||
from . import dss_ads
|
||||
from . import dss_contract
|
||||
from . import dss_contracts_move_field_wizard
|
||||
from . import dss_contract_uservar
|
||||
from . import dss_projects
|
||||
from . import dss_geraetetypen
|
||||
|
|
@ -15,6 +16,9 @@ from . import dss_eventdays
|
|||
from . import dss_display_templates
|
||||
from . import dss_advertisefields_templates
|
||||
from . import dss_advertisefields
|
||||
from . import dss_advertisefields_create_from_template_wizard
|
||||
from . import dss_advertisefields_create_from_project_wizard
|
||||
from . import dss_advertisefields_save_as_template_wizard
|
||||
from . import dss_trigger
|
||||
from . import dss_triggerexecute
|
||||
from . import dss_triggervalues
|
||||
|
|
@ -23,6 +27,7 @@ from . import dss
|
|||
from . import dss_maintains
|
||||
from . import dss_provisionstypen
|
||||
from . import dss_provision
|
||||
from . import dss_company_type
|
||||
from . import company
|
||||
from . import dss_invoices
|
||||
from . import dss_importinvoicelist
|
||||
|
|
@ -35,6 +40,10 @@ from . import dss_crm_extended
|
|||
from . import dss_kileads_requests
|
||||
from . import dss_kileads
|
||||
from . import dss_res_users
|
||||
from . import dss_portal_user_profile
|
||||
from . import dss_web_contracts_stream
|
||||
from . import dss_settings_variables
|
||||
from . import dss_csvs
|
||||
from . import dss_ai_chat_history
|
||||
from . import dss_phonecenter_query
|
||||
|
||||
|
|
|
|||
|
|
@ -28,10 +28,17 @@ class ResCompany(models.Model):
|
|||
dssinternpartner_grafik = fields.Boolean('Grafiker', default=False)
|
||||
dssinternpartner_technik = fields.Boolean('Techniker', default=False)
|
||||
dssportaluser = fields.Many2one('res.users', 'Portal User', domain=[('share','=',True)], help='Verknüpfter Portal Benutzer fuer diesen Partner')
|
||||
dsspartner_Company_type = fields.Many2one('dss.company.type',string='Firmentyp Kunde',tracking=True)
|
||||
# dss_uuid = fields.Char('uuid')
|
||||
dss_uuid = fields.Char(default=lambda self: self._default_uuid(), required=True, readonly=True, copy=False, string='UUID')
|
||||
rgemail = fields.Char('Rechnungs-Email', default=False)
|
||||
leitwegid = fields.Char('Leitweg-ID', default=False)
|
||||
google_calendar_id = fields.Char('Google Kalender ID', default=False)
|
||||
google_calendar_client_id = fields.Char('Google Client ID', default=False)
|
||||
google_calendar_client_secret = fields.Char('Google Client Secret', default=False)
|
||||
google_calendar_refresh_token = fields.Char('Google Refresh Token', default=False)
|
||||
google_calendar_access_token = fields.Char('Google Access Token', default=False)
|
||||
google_calendar_token_expires_at = fields.Datetime('Google Token gueltig bis', default=False)
|
||||
|
||||
@api.model
|
||||
def _default_uuid(self):
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ from pyffmpeg import FFmpeg
|
|||
from tuya_iot import TuyaOpenAPI, TUYA_LOGGER
|
||||
from tuya_connector import TuyaOpenAPI, TUYA_LOGGER
|
||||
from . import dss_contract
|
||||
from . import dss_projects
|
||||
import sys
|
||||
|
||||
|
||||
|
|
@ -99,7 +100,28 @@ class dsscontractstatus(models.Model):
|
|||
systemtypdefault = fields.Boolean(string='Default Typ',tracking=True)
|
||||
systemtyp = fields.Many2one('dss.systemtypen',tracking=True)
|
||||
systemtypuuid = fields.Char(related='systemtyp.uuid',tracking=True)
|
||||
kanban_display_minimal = fields.Boolean(string='Kanban minimalisiert')
|
||||
kanban_display_minimal = fields.Boolean(string='Kanban minimieren')
|
||||
kanban_collapsed_choice = fields.Selection(
|
||||
[('no', 'Nein'), ('yes', 'Ja')],
|
||||
string='Kanban eingeklappt',
|
||||
default='no',
|
||||
store=True,
|
||||
)
|
||||
fold = fields.Boolean(
|
||||
string='Kanban-Spalte eingeklappt',
|
||||
compute='_compute_fold',
|
||||
inverse='_inverse_fold',
|
||||
store=True,
|
||||
)
|
||||
|
||||
@api.depends('kanban_collapsed_choice')
|
||||
def _compute_fold(self):
|
||||
for record in self:
|
||||
record.fold = record.kanban_collapsed_choice == 'yes'
|
||||
|
||||
def _inverse_fold(self):
|
||||
for record in self:
|
||||
record.kanban_collapsed_choice = 'yes' if record.fold else 'no'
|
||||
|
||||
@api.model
|
||||
def _default_uuid(self):
|
||||
|
|
@ -196,7 +218,12 @@ class dssmediatypes(models.Model):
|
|||
maxsize_kb = fields.Integer('Maximale Größe KB')
|
||||
maxsize_w = fields.Integer('Maximale Pixel W')
|
||||
maxsize_h = fields.Integer('Maximale Pixel H')
|
||||
adportal_visible = fields.Boolean('Ad Portal sichtbar', default=False)
|
||||
adportal_ki_prompt = fields.Text('KI Prompt')
|
||||
standard_image = fields.Image()
|
||||
weboptionupload_show = fields.Boolean('Im Uploadformular Anzeigen ?')
|
||||
weboptionupload_name = fields.Char('Anzeigename für Webformular')
|
||||
weboptionupload_id = fields.Integer('UploadID für Webformular')
|
||||
default_filename = fields.Char('Default Dateiname', help='Standard-Dateiname für diesen Mediatyp. Wird automatisch aus dem Dateinamen der hochgeladenen Datei abgerufen')
|
||||
can_gen_stamp = fields.Boolean('Kann Stempel generieren', help='Darf durch Stempelgenerator gefüllt werden ja/nein')
|
||||
|
||||
|
|
@ -360,6 +387,14 @@ class NewBinaryWizard(models.TransientModel):
|
|||
dss_contract.dsscontracts.pyopen_dss_contract_add_binary_form_view_from_wizzard(contract,self.field_ids)
|
||||
return context
|
||||
|
||||
def pyopen_dss_project_add_binary_form_view_from_wizzard(self):
|
||||
context = dict(self.env.context or {})
|
||||
default_contract_id = context.get('active_id', False)
|
||||
_logger.info('Open Add Binary Form '+str(context)+' - '+str(default_contract_id)+' / '+str(self.field_ids))
|
||||
project= self.env['dss.projects'].browse(default_contract_id)
|
||||
dss_projects.dssprojects.pyopen_dss_project_add_binary_form_view_from_wizzard(project,self.field_ids)
|
||||
return context
|
||||
|
||||
class View(models.Model):
|
||||
"""
|
||||
Extends the base 'ir.ui.view' model to include a new type of view
|
||||
|
|
|
|||
|
|
@ -144,6 +144,7 @@ class dsscontractads(models.Model):
|
|||
|
||||
date_zuarbeit = fields.Date(string='Zuarbeit Datum', help='Zuarbeit gesendet am',
|
||||
tracking=True)
|
||||
korrekturabzug_cloudlink = fields.Char('Freigabelink zu den fertigen Dateien ',tracking=True)
|
||||
date_korrekturabzug = fields.Date(string='K.Abzug Datum', help='Korrekturabzug gesendet am',
|
||||
tracking=True)
|
||||
date_korrekturfreigabe = fields.Date(string='K.Freigabe Datum', help='Korrekturfreigabe erhalten am',
|
||||
|
|
@ -246,6 +247,24 @@ class dsscontractads(models.Model):
|
|||
autodays=self.env['dss.settings'].search([],limit=1).freigabe_auto_time
|
||||
record.date_korrekturfreigabe_ablauf = record.date_korrekturabzug + relativedelta(days=autodays)
|
||||
|
||||
def pysendKA(self):
|
||||
contract = self.contract
|
||||
grundsystemname = contract.project.grundsystemname
|
||||
_logger.info('Korrekturabzug senden : C_' + str(contract.id) + 'GS_' + str(grundsystemname.id)+' / '+str(grundsystemname.email_template_abzug))
|
||||
if grundsystemname.email_template_abzug:
|
||||
_logger.info('Korrekturabzug senden in : C_' + str(contract.id) + 'GS_' + str(grundsystemname.id)+' / '+str(grundsystemname.email_template_abzug))
|
||||
newmail = self.env['mail.template'].browse(grundsystemname.email_template_abzug.id).copy()
|
||||
newmail.email_to = contract.client_short_email
|
||||
newmail.email_bcc = "projekt@logumedia.de"
|
||||
newmail.send_mail(contract.id, force_send = True)
|
||||
self.date_korrekturabzug = date.today()
|
||||
_logger.info('Korrekturabzug gesendet : C_' + str(contract.id) + 'GS_' + str(grundsystemname.id)+' / '+str(grundsystemname.email_template_abzug))
|
||||
else:
|
||||
_logger.info('Keine Korrekturabzug Mailvorlage hinterlegt : C_' + str(contract.id) + 'GS_' + str(grundsystemname.id)+' / '+str(grundsystemname.email_template_abzug))
|
||||
raise ValidationError(_("Keine Korrekturabzug Mailvorlage hinterlegt ! Bitte im Systemtyp eine Mailvorlage für Korrekturabzug hinterlegen !"))
|
||||
|
||||
|
||||
|
||||
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')
|
||||
|
|
|
|||
|
|
@ -183,6 +183,13 @@ class dssadvertisefields(models.Model):
|
|||
if not record.contract:
|
||||
record.contract = record.contract_save
|
||||
|
||||
def action_remove_contract_links(self):
|
||||
for record in self:
|
||||
record.write({
|
||||
'contract': [(5, 0, 0)],
|
||||
'contract_save': False,
|
||||
})
|
||||
|
||||
def pyactiontaketemplate(self):
|
||||
action = (self.env["confirmation.wizard"].confirm_message(_("Das Template wirklich übernehmen ?"),title="Bitte bestätigen",method="taketemplate",records=self,callback_params={"template":self.id})
|
||||
)
|
||||
|
|
@ -236,6 +243,45 @@ class dssadvertisefields(models.Model):
|
|||
action['context']='{"active_id":'+str(self.id)+'}'
|
||||
action['res_id']=self.id
|
||||
return action
|
||||
|
||||
@api.model
|
||||
def action_open_create_from_template_wizard(self):
|
||||
view = self.env.ref('DigitalSignage.dss_advertisefields_create_from_template_wizard_view_form')
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': _('Felder aus Vorlage erstellen'),
|
||||
'res_model': 'dss.advertisefields.create.from.template.wizard',
|
||||
'view_mode': 'form',
|
||||
'view_id': view.id,
|
||||
'views': [(view.id, 'form')],
|
||||
'target': 'new',
|
||||
}
|
||||
|
||||
@api.model
|
||||
def action_open_create_from_project_wizard(self):
|
||||
view = self.env.ref('DigitalSignage.dss_advertisefields_create_from_project_wizard_view_form')
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': _('Felder aus vorh. Projekt erstellen'),
|
||||
'res_model': 'dss.advertisefields.create.from.project.wizard',
|
||||
'view_mode': 'form',
|
||||
'view_id': view.id,
|
||||
'views': [(view.id, 'form')],
|
||||
'target': 'new',
|
||||
}
|
||||
|
||||
@api.model
|
||||
def action_open_save_as_template_wizard(self):
|
||||
view = self.env.ref('DigitalSignage.dss_advertisefields_save_as_template_wizard_view_form')
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': _('Felder in Vorlage speichern'),
|
||||
'res_model': 'dss.advertisefields.save.as.template.wizard',
|
||||
'view_mode': 'form',
|
||||
'view_id': view.id,
|
||||
'views': [(view.id, 'form')],
|
||||
'target': 'new',
|
||||
}
|
||||
|
||||
def btn_image_import(self):
|
||||
action = self.env["confirmation.wizard"].confirm_message(_("Das Bild wirklich importieren ?"),title="Bitte bestätigen",method="btn_image_import_do",records=self,callback_params={"template":self.id})
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
from odoo import fields, models, _
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class DssAdvertisefieldsCreateFromProjectWizard(models.TransientModel):
|
||||
_name = "dss.advertisefields.create.from.project.wizard"
|
||||
_description = "Werbefelder aus vorhandenem Projekt erstellen"
|
||||
|
||||
source_project_id = fields.Many2one(
|
||||
'dss.projects',
|
||||
string='Altes Projekt',
|
||||
required=True,
|
||||
)
|
||||
project_id = fields.Many2one(
|
||||
'dss.projects',
|
||||
string='Neues Projekt',
|
||||
required=True,
|
||||
)
|
||||
|
||||
def action_create_fields(self):
|
||||
self.ensure_one()
|
||||
|
||||
if self.source_project_id.id == self.project_id.id:
|
||||
raise ValidationError(_("Altes Projekt und neues Projekt muessen unterschiedlich sein."))
|
||||
|
||||
source_fields = self.env['dss.advertisefields'].search([
|
||||
('project', '=', self.source_project_id.id)
|
||||
])
|
||||
if not source_fields:
|
||||
raise ValidationError(_("Im gewaehlten alten Projekt wurden keine Werbefelder gefunden."))
|
||||
|
||||
create_vals = []
|
||||
project_identifier = self.project_id.projectid or self.project_id.id
|
||||
for source_field in source_fields:
|
||||
vals = source_field.copy_data()[0]
|
||||
feldname = vals.get('feldname') or source_field.feldname
|
||||
vals.update({
|
||||
'project': self.project_id.id,
|
||||
'auto_feldname': f"{project_identifier}_{feldname}",
|
||||
'contract': [(6, 0, [])],
|
||||
'contract_save': False,
|
||||
})
|
||||
create_vals.append(vals)
|
||||
|
||||
created_fields = self.env['dss.advertisefields'].create(create_vals)
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': _('Erstellte Werbefelder'),
|
||||
'res_model': 'dss.advertisefields',
|
||||
'view_mode': 'tree,form',
|
||||
'domain': [('id', 'in', created_fields.ids)],
|
||||
}
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
from odoo import fields, models, _
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class DssAdvertisefieldsCreateFromTemplateWizard(models.TransientModel):
|
||||
_name = "dss.advertisefields.create.from.template.wizard"
|
||||
_description = "Werbefelder aus Vorlage erstellen"
|
||||
|
||||
display_template_id = fields.Many2one(
|
||||
'dss.display.templates',
|
||||
string='Vorlage',
|
||||
required=True,
|
||||
)
|
||||
project_id = fields.Many2one(
|
||||
'dss.projects',
|
||||
string='Neues Projekt',
|
||||
required=True,
|
||||
)
|
||||
|
||||
def action_create_fields(self):
|
||||
self.ensure_one()
|
||||
|
||||
template_fields = self.env['dss.advertisefields.templates'].search([
|
||||
('displaytemplate', '=', self.display_template_id.id)
|
||||
])
|
||||
if not template_fields:
|
||||
raise ValidationError(_("Für die gewählte Vorlage wurden keine Werbefelder gefunden."))
|
||||
|
||||
create_vals = []
|
||||
project_identifier = self.project_id.projectid or self.project_id.id
|
||||
for template_field in template_fields:
|
||||
feldname = template_field.feldname or template_field.templatefeldname
|
||||
create_vals.append({
|
||||
'feldname': feldname,
|
||||
'auto_feldname': f"{project_identifier}_{feldname}",
|
||||
'project': self.project_id.id,
|
||||
'display': template_field.display,
|
||||
'color_used': template_field.color_used,
|
||||
'color_unused': template_field.color_unused,
|
||||
'mediastructure': template_field.mediastructure.id,
|
||||
'is_btn': template_field.is_btn,
|
||||
'btn_displaytemplate': self.display_template_id.id,
|
||||
'btn_fromtemplate': template_field.id,
|
||||
'btn_pos_x': template_field.btn_pos_x,
|
||||
'btn_pos_y': template_field.btn_pos_y,
|
||||
'btn_pos_w': template_field.btn_pos_w,
|
||||
'btn_pos_h': template_field.btn_pos_h,
|
||||
'btn_name': template_field.btn_name,
|
||||
'btn_image': template_field.btn_image,
|
||||
'btn_fieldname': template_field.btn_fieldname,
|
||||
'btn_visible': template_field.btn_visible,
|
||||
'btn_active': template_field.btn_active,
|
||||
'btn_action_inactive': template_field.btn_action_inactive,
|
||||
'btn_action_active': template_field.btn_action_active,
|
||||
'btn_playlist_zuordnung': template_field.btn_playlist_zuordnung,
|
||||
'btn_gallery_rows': template_field.btn_gallery_rows,
|
||||
'btn_gallery_cols': template_field.btn_gallery_cols,
|
||||
'btn_gallery_prv_w': template_field.btn_gallery_prv_w,
|
||||
'btn_gallery_prv_h': template_field.btn_gallery_prv_h,
|
||||
'btn_has_timer': template_field.btn_has_timer,
|
||||
'btn_has_timer_time': template_field.btn_has_timer_time,
|
||||
'btn_editable': template_field.btn_editable,
|
||||
'btn_special_actionimage': template_field.btn_special_actionimage,
|
||||
'btn_special_actionimage_x': template_field.btn_special_actionimage_x,
|
||||
'btn_special_actionimage_y': template_field.btn_special_actionimage_y,
|
||||
'btn_special_actionimage_w': template_field.btn_special_actionimage_w,
|
||||
'btn_special_actionimage_h': template_field.btn_special_actionimage_h,
|
||||
'btn_special_actionimage_time': template_field.btn_special_actionimage_time,
|
||||
'btn_text_1_visible': template_field.btn_text_1_visible,
|
||||
'btn_text_2_visible': template_field.btn_text_2_visible,
|
||||
'btn_text_3_visible': template_field.btn_text_3_visible,
|
||||
'btn_text_4_visible': template_field.btn_text_4_visible,
|
||||
'btn_text_1_pos_x': template_field.btn_text_1_pos_x,
|
||||
'btn_text_1_pos_y': template_field.btn_text_1_pos_y,
|
||||
'btn_text_2_pos_x': template_field.btn_text_2_pos_x,
|
||||
'btn_text_2_pos_y': template_field.btn_text_2_pos_y,
|
||||
'btn_text_3_pos_x': template_field.btn_text_3_pos_x,
|
||||
'btn_text_3_pos_y': template_field.btn_text_3_pos_y,
|
||||
'btn_text_4_pos_x': template_field.btn_text_4_pos_x,
|
||||
'btn_text_4_pos_y': template_field.btn_text_4_pos_y,
|
||||
'btn_text_1_font': template_field.btn_text_1_font,
|
||||
'btn_text_2_font': template_field.btn_text_2_font,
|
||||
'btn_text_3_font': template_field.btn_text_3_font,
|
||||
'btn_text_4_font': template_field.btn_text_4_font,
|
||||
})
|
||||
|
||||
created_fields = self.env['dss.advertisefields'].create(create_vals)
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': _('Erstellte Werbefelder'),
|
||||
'res_model': 'dss.advertisefields',
|
||||
'view_mode': 'tree,form',
|
||||
'domain': [('id', 'in', created_fields.ids)],
|
||||
}
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
from odoo import fields, models, _
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class DssAdvertisefieldsSaveAsTemplateWizard(models.TransientModel):
|
||||
_name = "dss.advertisefields.save.as.template.wizard"
|
||||
_description = "Werbefelder als Vorlage speichern"
|
||||
|
||||
source_project_id = fields.Many2one(
|
||||
'dss.projects',
|
||||
string='Altes Projekt',
|
||||
required=True,
|
||||
)
|
||||
new_template_name = fields.Char(
|
||||
string='Neuer Vorlagenname',
|
||||
required=True,
|
||||
)
|
||||
|
||||
def action_save_as_template(self):
|
||||
self.ensure_one()
|
||||
|
||||
source_fields = self.env['dss.advertisefields'].search([
|
||||
('project', '=', self.source_project_id.id)
|
||||
])
|
||||
if not source_fields:
|
||||
raise ValidationError(_("Im gewaehlten alten Projekt wurden keine Werbefelder gefunden."))
|
||||
|
||||
template_name = self.new_template_name.strip()
|
||||
if not template_name:
|
||||
raise ValidationError(_("Bitte einen gueltigen Vorlagennamen eingeben."))
|
||||
|
||||
first_display_template = source_fields[0].btn_displaytemplate
|
||||
display_template_vals = {
|
||||
'displayname': template_name,
|
||||
}
|
||||
if first_display_template:
|
||||
display_template_vals.update({
|
||||
'fullsize_w': first_display_template.fullsize_w,
|
||||
'fullsize_h': first_display_template.fullsize_h,
|
||||
'adportal_visible': first_display_template.adportal_visible,
|
||||
})
|
||||
|
||||
new_display_template = self.env['dss.display.templates'].create(display_template_vals)
|
||||
|
||||
template_field_vals = []
|
||||
for source_field in source_fields:
|
||||
template_field_name = source_field.feldname or source_field.auto_feldname or ''
|
||||
if not source_field.feldname and source_field.auto_feldname and '_' in source_field.auto_feldname:
|
||||
template_field_name = source_field.auto_feldname.split('_', 1)[1]
|
||||
|
||||
vals = {
|
||||
'templatefeldname': template_field_name,
|
||||
'display': source_field.display,
|
||||
'displaytemplate': new_display_template.id,
|
||||
'feldname': source_field.feldname,
|
||||
'color_used': source_field.color_used,
|
||||
'color_unused': source_field.color_unused,
|
||||
'mediastructure': source_field.mediastructure.id,
|
||||
'is_btn': source_field.is_btn,
|
||||
'btn_pos_x': source_field.btn_pos_x,
|
||||
'btn_pos_y': source_field.btn_pos_y,
|
||||
'btn_pos_w': source_field.btn_pos_w,
|
||||
'btn_pos_h': source_field.btn_pos_h,
|
||||
'btn_name': source_field.btn_name,
|
||||
'btn_image': source_field.btn_image,
|
||||
'btn_fieldname': source_field.btn_fieldname,
|
||||
'btn_visible': source_field.btn_visible,
|
||||
'btn_active': source_field.btn_active,
|
||||
'btn_action_inactive': source_field.btn_action_inactive,
|
||||
'btn_action_active': source_field.btn_action_active,
|
||||
'btn_playlist_zuordnung': source_field.btn_playlist_zuordnung,
|
||||
'btn_gallery_rows': source_field.btn_gallery_rows,
|
||||
'btn_gallery_cols': source_field.btn_gallery_cols,
|
||||
'btn_gallery_prv_w': source_field.btn_gallery_prv_w,
|
||||
'btn_gallery_prv_h': source_field.btn_gallery_prv_h,
|
||||
'btn_has_timer': source_field.btn_has_timer,
|
||||
'btn_has_timer_time': source_field.btn_has_timer_time,
|
||||
'btn_editable': source_field.btn_editable,
|
||||
'btn_special_actionimage': source_field.btn_special_actionimage,
|
||||
'btn_special_actionimage_x': source_field.btn_special_actionimage_x,
|
||||
'btn_special_actionimage_y': source_field.btn_special_actionimage_y,
|
||||
'btn_special_actionimage_w': source_field.btn_special_actionimage_w,
|
||||
'btn_special_actionimage_h': source_field.btn_special_actionimage_h,
|
||||
'btn_special_actionimage_time': source_field.btn_special_actionimage_time,
|
||||
'btn_text_1_visible': source_field.btn_text_1_visible,
|
||||
'btn_text_2_visible': source_field.btn_text_2_visible,
|
||||
'btn_text_3_visible': source_field.btn_text_3_visible,
|
||||
'btn_text_4_visible': source_field.btn_text_4_visible,
|
||||
'btn_text_1_pos_x': source_field.btn_text_1_pos_x,
|
||||
'btn_text_1_pos_y': source_field.btn_text_1_pos_y,
|
||||
'btn_text_2_pos_x': source_field.btn_text_2_pos_x,
|
||||
'btn_text_2_pos_y': source_field.btn_text_2_pos_y,
|
||||
'btn_text_3_pos_x': source_field.btn_text_3_pos_x,
|
||||
'btn_text_3_pos_y': source_field.btn_text_3_pos_y,
|
||||
'btn_text_4_pos_x': source_field.btn_text_4_pos_x,
|
||||
'btn_text_4_pos_y': source_field.btn_text_4_pos_y,
|
||||
'btn_text_1_font': source_field.btn_text_1_font,
|
||||
'btn_text_2_font': source_field.btn_text_2_font,
|
||||
'btn_text_3_font': source_field.btn_text_3_font,
|
||||
'btn_text_4_font': source_field.btn_text_4_font,
|
||||
}
|
||||
template_field_vals.append(vals)
|
||||
|
||||
created_template_fields = self.env['dss.advertisefields.templates'].create(template_field_vals)
|
||||
new_display_template.write({'feldtemplates': [(6, 0, created_template_fields.ids)]})
|
||||
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': _('Neue Werbefeld-Vorlage'),
|
||||
'res_model': 'dss.display.templates',
|
||||
'view_mode': 'form',
|
||||
'res_id': new_display_template.id,
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class DssAIChatHistory(models.Model):
|
||||
_name = "dss.ai.chat.history"
|
||||
_description = "DigitalSignage AI Chat History"
|
||||
_order = "create_date desc, id desc"
|
||||
|
||||
user_id = fields.Many2one('res.users', required=True, index=True, default=lambda self: self.env.user)
|
||||
role = fields.Selection(
|
||||
[('user', 'User'), ('assistant', 'Assistant')],
|
||||
required=True,
|
||||
index=True,
|
||||
)
|
||||
content = fields.Text(required=True)
|
||||
|
|
@ -66,6 +66,7 @@ class dssbinaries(models.Model):
|
|||
binary_secured_ro = fields.Boolean(string="Gesperrt", default=False, help = "Ein Flag, das angibt, ob die Datei gesperrt ist. Wenn dies aktiviert ist, kann die Datei nicht mehr bearbeitet oder gelöscht werden. Sie wird dann auch nicht in Übertragungen oder Ansichten beachtet.")
|
||||
binary_used_ro = fields.Boolean(string="Genutzt", default=False, help = "Ein Flag, das angibt, ob die Datei in einer Übertragung/Ausstrahlung verwendet wird. Wenn dies aktiviert ist, kann die Datei nicht mehr gelöscht oder überarbeitet werden.")
|
||||
binary_contract = fields.Many2one('dss.contracts', string="Vertrag", help = "Der Vertrag, zu dem diese Datei gehört. Dies kann verwendet werden, um Dateien zu organisieren und zu verwalten, die mit bestimmten Verträgen verbunden sind.")
|
||||
binary_project = fields.Many2one('dss.projects', string="Projekt", help = "Das Projekt, zu dem diese Datei gehört. Dies kann verwendet werden, um Dateien zu organisieren und zu verwalten, die mit bestimmten Projekten verbunden sind.")
|
||||
|
||||
|
||||
|
||||
|
|
@ -122,64 +123,66 @@ class dssbinaries(models.Model):
|
|||
|
||||
@api.onchange('binary_binary')
|
||||
def _onchange_binary_binary(self):
|
||||
"""Update binary size and type when binary content is changed."""
|
||||
_logger.info("New Binary B_"+str(self.id))
|
||||
existing_binary = False
|
||||
if not self._origin.binary_binary:
|
||||
if self.binary_contract and 1 == 2:
|
||||
"""Update binary size and type when binary content is changed."""
|
||||
_logger.info("New Binary B_"+str(self.id))
|
||||
existing_binary = False
|
||||
else:
|
||||
existing_binary = True
|
||||
if not self._origin.binary_binary:
|
||||
existing_binary = False
|
||||
else:
|
||||
existing_binary = True
|
||||
|
||||
if not self.binary_contract.cloudlink:
|
||||
raise ValidationError(_("Der Vertrag hat keinen Cloudlink. Bitte überprüfen Sie den Vertrag."))
|
||||
cloudpath = str(self.binary_contract.cloudlink)
|
||||
_logger.info("New Binary checking Cloudlink B_"+str(self.id)+" - "+str(cloudpath))
|
||||
cloudpath = cloudpath + '/' + str(self.binary_mediatype.cloudlink)
|
||||
client = Client("https://cloud.logumedia.de/remote.php/dav/files/OdooDav/", auth=("odooClient@logumedia.de", "lm2020#OdooDav"))
|
||||
_logger.info("New Binary checking Cloudlink for cont : "+str(cloudpath))
|
||||
cloudexists = False
|
||||
if client.exists(cloudpath):
|
||||
# Convert self.binary_binary to bytes-like object
|
||||
if isinstance(self.binary_binary, str):
|
||||
filedata = base64.b64decode(self.binary_binary.split("base64,")[-1])
|
||||
if not self.binary_contract.cloudlink:
|
||||
raise ValidationError(_("Der Vertrag hat keinen Cloudlink. Bitte überprüfen Sie den Vertrag."))
|
||||
cloudpath = str(self.binary_contract.cloudlink)
|
||||
_logger.info("New Binary checking Cloudlink B_"+str(self.id)+" - "+str(cloudpath))
|
||||
cloudpath = cloudpath + '/' + str(self.binary_mediatype.cloudlink)
|
||||
client = Client("https://cloud.logumedia.de/remote.php/dav/files/OdooDav/", auth=("odooClient@logumedia.de", "lm2020#OdooDav"))
|
||||
_logger.info("New Binary checking Cloudlink for cont : "+str(cloudpath))
|
||||
cloudexists = False
|
||||
if client.exists(cloudpath):
|
||||
# Convert self.binary_binary to bytes-like object
|
||||
if isinstance(self.binary_binary, str):
|
||||
filedata = base64.b64decode(self.binary_binary.split("base64,")[-1])
|
||||
else:
|
||||
filedata = base64.b64decode(self.binary_binary)
|
||||
filedataStr = str(filedata)
|
||||
_logger.info("New Binary Cloudlink exists - Saving File - B_"+str(self.id)+" - "+str(cloudpath)+" - " + str(filedata)+' - ' + filedataStr[0:8])
|
||||
try:
|
||||
decoded_data = filedata
|
||||
except base64.binascii.Error as e:
|
||||
if 'Incorrect padding' in str(e):
|
||||
filedataStr += b'=' * (4 - len(filedataStr) % 4)
|
||||
decoded_data = base64.b64decode(filedataStr)
|
||||
if filedataStr[0:10] == "b'\x89PNG":
|
||||
extension = 'png'
|
||||
else:
|
||||
extension = 'jpg'
|
||||
_logger.info("New Binary Cloudlink exists - Saving File - B_"+str(self.id)+" - "+str(cloudpath)+" - " + str(base64.b64encode(decoded_data)))
|
||||
##infile = open('/tmp/'+filename, "w+b")
|
||||
#infile = tempfile.NamedTemporaryFile(delete=False)
|
||||
##infile.write(filedata)
|
||||
##client.upload_fileobj(infile, f"{cloudpath}/{filename}")
|
||||
##infile.close()
|
||||
##infile.delete() # Delete the temporary file after upload
|
||||
cloudexists = True
|
||||
else:
|
||||
filedata = base64.b64decode(self.binary_binary)
|
||||
filedataStr = str(filedata)
|
||||
_logger.info("New Binary Cloudlink exists - Saving File - B_"+str(self.id)+" - "+str(cloudpath)+" - " + str(filedata)+' - ' + filedataStr[0:8])
|
||||
try:
|
||||
decoded_data = filedata
|
||||
except base64.binascii.Error as e:
|
||||
if 'Incorrect padding' in str(e):
|
||||
filedataStr += b'=' * (4 - len(filedataStr) % 4)
|
||||
decoded_data = base64.b64decode(filedataStr)
|
||||
if filedataStr[0:10] == "b'\x89PNG":
|
||||
extension = 'png'
|
||||
else:
|
||||
extension = 'jpg'
|
||||
_logger.info("New Binary Cloudlink exists - Saving File - B_"+str(self.id)+" - "+str(cloudpath)+" - " + str(base64.b64encode(decoded_data)))
|
||||
##infile = open('/tmp/'+filename, "w+b")
|
||||
#infile = tempfile.NamedTemporaryFile(delete=False)
|
||||
##infile.write(filedata)
|
||||
##client.upload_fileobj(infile, f"{cloudpath}/{filename}")
|
||||
##infile.close()
|
||||
##infile.delete() # Delete the temporary file after upload
|
||||
cloudexists = True
|
||||
else:
|
||||
_logger.info("New Binary Cloudlink does not exists B_"+str(self.id)+" - "+str(cloudpath))
|
||||
raise ValidationError(_("Der Cloudlink im Vertrag ist nicht vorhanden. Bitte überprüfen Sie den Link ggf. anlegen."))
|
||||
_logger.info("New Binary Cloudlink does not exists B_"+str(self.id)+" - "+str(cloudpath))
|
||||
raise ValidationError(_("Der Cloudlink im Vertrag ist nicht vorhanden. Bitte überprüfen Sie den Link ggf. anlegen."))
|
||||
|
||||
if cloudexists:
|
||||
if self.binary_binary:
|
||||
# Calculate the size of the binary content
|
||||
self.binary_size = len(filedata)
|
||||
# Determine the file type based on the binary content
|
||||
self.binary_cloud_link = f"{cloudpath}/{self.binary_filename}"
|
||||
self.binary_issaved = True
|
||||
else:
|
||||
self.binary_size = 0
|
||||
self.binary_type = ''
|
||||
self.binary_cloud_link = ''
|
||||
self.binary_issaved = False
|
||||
if cloudexists:
|
||||
if self.binary_binary:
|
||||
# Calculate the size of the binary content
|
||||
self.binary_size = len(filedata)
|
||||
# Determine the file type based on the binary content
|
||||
self.binary_cloud_link = f"{cloudpath}/{self.binary_filename}"
|
||||
self.binary_issaved = True
|
||||
else:
|
||||
self.binary_size = 0
|
||||
self.binary_type = ''
|
||||
self.binary_cloud_link = ''
|
||||
self.binary_issaved = False
|
||||
|
||||
|
||||
|
||||
def dload(self):
|
||||
|
|
|
|||
|
|
@ -0,0 +1,74 @@
|
|||
import ast
|
||||
import datetime
|
||||
import json
|
||||
import re
|
||||
import uuid
|
||||
import logging
|
||||
import base64
|
||||
import subprocess
|
||||
import tempfile
|
||||
import easywebdav
|
||||
import os
|
||||
import os.path
|
||||
import requests
|
||||
import hashlib
|
||||
from PIL import Image,ImageDraw,ImageFont
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo import tools
|
||||
from odoo.exceptions import ValidationError
|
||||
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)
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class dsscompanytype(models.Model):
|
||||
|
||||
@api.model
|
||||
def create(self,vals):
|
||||
result = super().create(vals)
|
||||
return result
|
||||
|
||||
_name = "dss.company.type"
|
||||
_description = "DigitalSignage Firmentypen"
|
||||
# _inherit = ['mail.thread','mail.activity.mixin']
|
||||
_rec_name = "Firmetyp_name"
|
||||
# _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')
|
||||
Firmetyp_name = fields.Char(string="Firmetyp Name", required=True, help = "Der Name des Firmetyps, die Sie hochladen möchten. Dieser Name wird in der Benutzeroberfläche angezeigt.")
|
||||
firmentyp_needs_suffix = fields.Boolean(string="Benötigt Suffix", help="Gibt an, ob der Firmentyp ein Suffix benötigt")
|
||||
firmentyp_suf = fields.Char(string="Firmetyp Suffix", help="Suffix für den Firmentyp im Namen")
|
||||
firmentyp_turn_data = fields.Boolean(string="Turn Data", help="Gibt an, ob der Firmenname und Name getauscht werden müssen")
|
||||
|
||||
@api.model
|
||||
def _default_uuid(self):
|
||||
return str(uuid.uuid4())
|
||||
|
||||
@api.model
|
||||
def _default_create_date(self):
|
||||
return datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
|
||||
@api.model
|
||||
def _default_create_user(self):
|
||||
return self.env.user.name if self.env.user else 'System'
|
||||
|
||||
@api.model
|
||||
def _default_binary_id(self):
|
||||
# Generate a unique binary ID based on the current timestamp and a random UUID
|
||||
return f"Datei_{str(uuid.uuid4())[:8]}"
|
||||
|
||||
|
||||
|
|
@ -16,6 +16,7 @@ import tempfile
|
|||
import easywebdav
|
||||
import os
|
||||
import os.path
|
||||
import kontocheck
|
||||
from PIL import Image,ImageDraw,ImageFont
|
||||
|
||||
|
||||
|
|
@ -33,6 +34,7 @@ from tuya_iot import TuyaOpenAPI, TUYA_LOGGER
|
|||
from tuya_connector import TuyaOpenAPI, TUYA_LOGGER
|
||||
from webdav4.client import Client
|
||||
from webdav4.fsspec import WebdavFileSystem
|
||||
#from dss_trigger import trigger
|
||||
import sys
|
||||
|
||||
TUYA_LOGGER.setLevel(logging.DEBUG)
|
||||
|
|
@ -85,7 +87,7 @@ class dsscontracts(models.Model):
|
|||
_rec_name = "contract_auto_name"
|
||||
_inherit = ['mail.thread','dss.activity.mixin','dss.triggermodel']
|
||||
uuid = fields.Char(default=lambda self: self._default_uuid(), required=True, readonly=True, copy=False, string='UUID')
|
||||
contract_creator = fields.Many2one('res.users', string='Vertrag erstellt von', default=lambda self: self.env.user, readonly=True)
|
||||
contract_creator = fields.Many2one('res.users', string='Vertrag erstellt von', default=lambda self: self.env.user)
|
||||
contract_create_date = fields.Datetime('Vertrag erstellt am', default=lambda self: fields.Datetime.now(), readonly=True)
|
||||
cruuid = fields.Char(related='uuid')
|
||||
contract_id = fields.Char("Kundennummer",store=True,tracking=True)
|
||||
|
|
@ -94,7 +96,8 @@ class dsscontracts(models.Model):
|
|||
contract_state_name = fields.Char(related='contract_state.statusname',string='Vertragsstatus - Text',store=True)
|
||||
contract_state_color = fields.Char(related='contract_state.color',string='Vertragsstatus - Farbe',store=True)
|
||||
contract_state_order = fields.Integer(related='contract_state.order',string='Vertragsstatus - Reihenfolge',store=True)
|
||||
contract_state_minimal_kanban = fields.Boolean(related='contract_state.kanban_display_minimal',string='Kanban mininmal',store=True)
|
||||
contract_state_minimal_kanban = fields.Boolean(related='contract_state.kanban_display_minimal',string='Kanban minimieren',store=True)
|
||||
contract_state_kanban_collapsed = fields.Selection(related='contract_state.kanban_collapsed_choice', string='Kanban eingeklappt', store=True)
|
||||
contract_auto_id = fields.Char("Kundennummer",tracking=True,help="Wird berechnet aus Projektnummer + Kunden ID")
|
||||
contract_auto_name = fields.Char('Vertragskennug',tracking=True,help="Wird berechnet aus Kundennummer + Vertragskennung")
|
||||
|
||||
|
|
@ -109,6 +112,9 @@ class dsscontracts(models.Model):
|
|||
contract_receipt_done_date = fields.Date('Rechnungsdatum',tracking=True)
|
||||
contract_receipt_done_until_date = fields.Date('Zahlungsziel', tracking=True)
|
||||
|
||||
client_is_export_finance = fields.Boolean('An Buchhaltung exportiert ?', tracking=True)
|
||||
client_is_export_finance_date = fields.Date('An Buchhaltung exportiert am', tracking=True)
|
||||
|
||||
client_is_inkaso = fields.Boolean('Ist Inkasso eröffnet ?', tracking=True)
|
||||
client_inkaso_datum = fields.Datetime('Inkasso eröffnet am ', tracking=True)
|
||||
client_inkaso_info = fields.Char('Inkasso Bemerkungen', tracking=True)
|
||||
|
|
@ -144,7 +150,9 @@ class dsscontracts(models.Model):
|
|||
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)
|
||||
|
||||
project_techniker = fields.Many2one(related='project.project_techniker', string='Aufbau')
|
||||
techniker_computed = fields.Many2one('res.partner', compute='_compute_techniker_computed', string='Techniker tatsächlich')
|
||||
|
||||
run_trigger = fields.Boolean(tracking=True)
|
||||
run_uni_trigger = fields.Boolean(tracking=True)
|
||||
run_uni_sub_trigger = fields.Boolean(tracking=True)
|
||||
|
|
@ -168,10 +176,12 @@ class dsscontracts(models.Model):
|
|||
contact_name = fields.Char(related="client.name")
|
||||
contact_dsspartner_vorname = fields.Char(related="client.dsspartner_vorname")
|
||||
contact_dsspartner_name = fields.Char(related="client.dsspartner_name")
|
||||
contact_company_type = fields.Many2one(related="client.dsspartner_Company_type")
|
||||
|
||||
parent_id = fields.Many2one('dss.contracts', string='Parent Task', index=True,tracking=True)
|
||||
|
||||
client_short_company = fields.Char('Firmenname Kunde',tracking=True)
|
||||
client_short_company_type = fields.Many2one('dss.company.type',string='Firmentyp 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)
|
||||
|
|
@ -188,6 +198,9 @@ class dsscontracts(models.Model):
|
|||
client_short_leitwegid = fields.Char('Leitwegid Kunde',tracking=True)
|
||||
client_short_BIC = fields.Char('BIC Kunde',tracking=True)
|
||||
client_short_IBAN = fields.Char('IBAN Kunde',tracking=True)
|
||||
client_short_BANK_computed = fields.Char(string='Bank Kunde',compute='_compute_cutshort_Bank')
|
||||
client_short_KONTO_computed = fields.Char(string='Konto Kunde',compute='_compute_cutshort_Konto')
|
||||
client_short_BLZ_computed = fields.Char(string='BLZ Kunde',compute='_compute_cutshort_BLZ')
|
||||
|
||||
client_other_projects = fields.Many2many('dss.projects',string='Weitere Projekte',tracking=True)
|
||||
|
||||
|
|
@ -220,12 +233,17 @@ class dsscontracts(models.Model):
|
|||
|
||||
contract_date = fields.Date('Vertragsdatum',tracking=True)
|
||||
start_date = fields.Date('Start/Auslief.datum',tracking=True,help="Datum des geplanten Startes der 1. Kampagne. Also die Uraustrahlung!")
|
||||
calc_end_date = fields.Date('Enddatum berechnet',tracking=True,help="Berechnetes Enddatum der Kampagne basierend auf Startdatum und Laufzeit (Ändert sich bei Verlängerungen).")
|
||||
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_iscanceled_logumedia = fields.Boolean('Vertrag gekündigt durch Logumedia',tracking=True)
|
||||
contract_iscanceled_logumedia_date = fields.Date('Vertrag von Logumedia gekündigt am ',tracking=True)
|
||||
contract_iscanceled_logumedia_reason = fields.Char('Vertrag von Logumedia gekündigt Grund',tracking=True)
|
||||
contract_auto_extend = fields.Boolean('autom. V.Verlängerung',tracking=True)
|
||||
contract_auto_extend_time = fields.Char('Verl. Monate',tracking=True)
|
||||
contract_auto_extend_count = fields.Integer('Anzahl Vertragsverlängerungen', tracking=True, default=0)
|
||||
scan_vertrag = fields.Binary('Datei')
|
||||
scan_vertrag_filename = fields.Char("Dateiname") # Hilft, den Dateinamen zu speichern
|
||||
|
||||
|
|
@ -279,6 +297,7 @@ class dsscontracts(models.Model):
|
|||
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)
|
||||
|
||||
vnnox_playlist_checked = fields.Boolean('Vnnox Liste geprüft ?',tracking=True)
|
||||
vnnox_playlist_checked_empty = fields.Boolean('Vnnox Liste war leer ?',tracking=True)
|
||||
vnnox_playlist_checked_date = fields.Date('Vnnox Liste geprüft am ',tracking=True)
|
||||
|
|
@ -291,6 +310,7 @@ class dsscontracts(models.Model):
|
|||
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)
|
||||
|
|
@ -342,9 +362,9 @@ class dsscontracts(models.Model):
|
|||
cloudlink = fields.Char('Cloud Verzeichnis',help='Verzeichnis für den Kunde innerhalb des Projekt Ordners')
|
||||
web_contract = fields.Many2one('dss.web_contracts' , string='Web_Vertrag', store=True,tracking=True)
|
||||
web_contract_input_notfinished = fields.Boolean('Web Vertrag Eingabe nicht abgeschlossen',tracking=True)
|
||||
web_contract_input_finished = fields.Boolean('Web Vertrag Eingabe abgeschlossen',tracking=True)
|
||||
web_contract_input_finished = fields.Boolean('Web Vertrag Eingabe war abgeschlossen',tracking=True)
|
||||
web_contract_paysum_gfx = fields.Float('Web Vertrag Zahlungssumme Grafik',tracking=True)
|
||||
web_contract_paysum_lz = fields.Float('Web Vertrag Zahlungssumme Laufzeit',tracking=True)
|
||||
web_contract_paysum_lz = fields.Float('Web Vertrag Zahlungssumme Laufzeit',tracking=True)
|
||||
web_contract_paysum_all = fields.Float('Web Vertrag Zahlungssumme Gesamt',tracking=True)
|
||||
web_contract_sign_ort = fields.Char('Web Vertrag Ort',tracking=True)
|
||||
web_contract_sign_datum = fields.Char('Web Vertrag Datum',tracking=True)
|
||||
|
|
@ -355,15 +375,115 @@ class dsscontracts(models.Model):
|
|||
web_contract_sonder_0 = fields.Char('Web Vertrag Sonderfeld 01',tracking=True)
|
||||
web_contract_sonder_1 = fields.Char('Web Vertrag Sonderfeld 02',tracking=True)
|
||||
web_contract_sonder_2 = fields.Char('Web Vertrag Sonderfeld 03',tracking=True)
|
||||
web_contract_satzmail = fields.Boolean('Web Vertrag Satzmaterial als Mail',tracking=True)
|
||||
web_contract_satzgiven = fields.Boolean('Web Vertrag Satzmaterial gegeben',tracking=True)
|
||||
web_contract_rgmail = fields.Boolean('Web Vertrag Rechnung als Mail',tracking=True)
|
||||
web_contract_kamail = fields.Boolean('Web Vertrag Korrekturabzug Zugang',tracking=True)
|
||||
web_constract_zahlstonst = fields.Char('Web Vertrag Zahlungsweise',tracking=True)
|
||||
web_contract_rejected = fields.Boolean('Eingabe - Web Vertrag abgelehnt',tracking=True)
|
||||
web_contract_reject_reason = fields.Char('Eingabe - Web Vertrag Ablehnungsgrund',tracking=True)
|
||||
web_bande_spieltage = fields.Char('Web Vertrag Bande - gebuchte Spieltage',tracking=True)
|
||||
web_contract_start_planed = fields.Char("TV Kampagne Start",tracking=True)
|
||||
web_contract_remove_planed = fields.Char("TV Kampagne Ende",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 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')
|
||||
|
||||
|
||||
userdef_vars = fields.Many2many('dss.contracts_uservars', string='Vertrags Uservariablen', tracking=True)
|
||||
|
||||
contract_finish_img_cloud = fields.Char('Überg. Bildlink',tracking=True)
|
||||
|
||||
@api.depends('techniker','project_techniker')
|
||||
@api.onchange('techniker')
|
||||
@api.onchange('project_techniker')
|
||||
def _compute_techniker_computed(self):
|
||||
for record in self:
|
||||
if record.techniker:
|
||||
record.techniker_computed = record.techniker
|
||||
elif record.project_techniker:
|
||||
record.techniker_computed = record.project_techniker
|
||||
else:
|
||||
record.techniker_computed = False
|
||||
|
||||
@api.depends('client_short_IBAN')
|
||||
@api.onchange('client_short_IBAN')
|
||||
@api.onchange('client_short_BIC')
|
||||
def _compute_cutshort_Bank(self):
|
||||
# BIC für Deutsche Bank
|
||||
kontocheck.lut_load()
|
||||
for record in self:
|
||||
bankname =''
|
||||
if record.client_short_BIC:
|
||||
try:
|
||||
bankname = kontocheck.scl_get_bankname(record.client_short_BIC)
|
||||
except ValueError:
|
||||
bankname = ''
|
||||
else :
|
||||
if record.client_short_BLZ_computed:
|
||||
try:
|
||||
bankname = kontocheck.get_bankname(record.client_short_BLZ_computed)
|
||||
except ValueError:
|
||||
bankname = ''
|
||||
|
||||
record.client_short_BANK_computed = bankname
|
||||
|
||||
|
||||
def get_account_from_iban(iban):
|
||||
# Entfernen von Leerzeichen und Großbuchstaben
|
||||
clean_iban = "".join(iban.split()).upper()
|
||||
|
||||
# Überprüfen, ob es eine deutsche IBAN ist
|
||||
if len(clean_iban) == 22 and clean_iban.startswith("DE"):
|
||||
# Die letzten 10 Stellen sind die Kontonummer
|
||||
account_number = clean_iban[-10:]
|
||||
return account_number
|
||||
else:
|
||||
return "Ungültige deutsche IBAN"
|
||||
|
||||
def get_blz_from_iban(iban):
|
||||
# Entfernen von Leerzeichen und Großbuchstaben
|
||||
clean_iban = "".join(iban.split()).upper()
|
||||
|
||||
# Überprüfen, ob es eine deutsche IBAN ist
|
||||
if len(clean_iban) == 22 and clean_iban.startswith("DE"):
|
||||
# Die letzten 10 Stellen sind die Kontonummer
|
||||
blz_number = clean_iban[4:12]
|
||||
return blz_number
|
||||
else:
|
||||
return "Ungültige deutsche IBAN"
|
||||
|
||||
@api.depends('client_short_IBAN')
|
||||
@api.onchange('client_short_IBAN')
|
||||
def _compute_cutshort_Konto(self):
|
||||
kontocheck.lut_load()
|
||||
for record in self:
|
||||
Kontonummer = ''
|
||||
if record.client_short_IBAN:
|
||||
try:
|
||||
Kontonummer = dsscontracts.get_account_from_iban(record.client_short_IBAN)
|
||||
except ValueError:
|
||||
Kontonummer = ''
|
||||
|
||||
record.client_short_KONTO_computed = Kontonummer
|
||||
|
||||
@api.depends('client_short_IBAN')
|
||||
@api.onchange('client_short_IBAN')
|
||||
def _compute_cutshort_BLZ(self):
|
||||
kontocheck.lut_load()
|
||||
for record in self:
|
||||
Bankleitzahl = ''
|
||||
if record.client_short_IBAN:
|
||||
try:
|
||||
Bankleitzahl = dsscontracts.get_blz_from_iban(record.client_short_IBAN)
|
||||
except ValueError:
|
||||
Bankleitzahl = ''
|
||||
record.client_short_BLZ_computed = Bankleitzahl
|
||||
|
||||
|
||||
@api.depends('work_marker_1')
|
||||
@api.onchange('work_marker_1')
|
||||
def _get_marker_1_color_false(self):
|
||||
|
|
@ -706,7 +826,7 @@ class dsscontracts(models.Model):
|
|||
myplz=self.client_short_plz
|
||||
result = self.env['res.partner'].create({'name':fullname,
|
||||
'company_name': self.client_short_company,
|
||||
'dsspartner_vorname':self.client_short_vorname,
|
||||
'dsspartner_vorname':self.client_short_vorname,
|
||||
'dsspartner_name':self.client_short_name,
|
||||
'dsspartner':True,
|
||||
'dsspartner_werbung':True,
|
||||
|
|
@ -719,6 +839,7 @@ class dsscontracts(models.Model):
|
|||
'rgemail':self.client_short_rgemail,
|
||||
'is_company':False})
|
||||
result.country_id = self.client_short_land
|
||||
result.dsspartner_Company_type = self.client_short_company_type.id
|
||||
_logger.info('Creating Contact ' + str(result) + ' ' + str(fullname))
|
||||
self.client = result
|
||||
# action = self.env['ir.actions.act_window'].with_context({'default_contractid': self.id})._for_xml_id('DigitalSignage.act_dss_project_view_contract')
|
||||
|
|
@ -914,6 +1035,9 @@ class dsscontracts(models.Model):
|
|||
defadstateid = defadstate.id
|
||||
kampagne = self.env['dss.ads'].create({'contract': self.id, 'project': self.project.id, 'adname': 'WK '+self.contract_auto_name,'adtype':'MAIN','ad_state':defadstateid})
|
||||
self.ads_last_ad = kampagne.id
|
||||
if self.web_contract_start_planed :
|
||||
kampagne.date_start_planed = self.web_contract_start_planed
|
||||
kampagne.date_remove_planed = self.web_contract_remove_planed
|
||||
_logger.info('Click auf Werbekampagne : C_'+str(self.id)+' Kampagne erstellt A_'+str(kampagne.id))
|
||||
kampagne.parent_ad = kampagne.id
|
||||
mediaids = []
|
||||
|
|
@ -1018,6 +1142,7 @@ class dsscontracts(models.Model):
|
|||
else:
|
||||
_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',
|
||||
'view_type':'form',
|
||||
|
|
@ -1054,6 +1179,91 @@ class dsscontracts(models.Model):
|
|||
if record.contract_auto_id == "":
|
||||
record.contract_auto_id = cidstr
|
||||
|
||||
def _compute_contract_end_for_calc_date(self):
|
||||
self.ensure_one()
|
||||
|
||||
start_date = self.start_date or self.contract_date
|
||||
if not start_date:
|
||||
return False
|
||||
|
||||
if self.contract_iscanceled and self.ads_last_todo_state_until:
|
||||
return self.ads_last_todo_state_until
|
||||
|
||||
runtime_months = int(self.runtime_m or 0) + int(self.runtime_bonus_m or 0)
|
||||
if self.contract_auto_extend:
|
||||
try:
|
||||
runtime_months += int((self.contract_auto_extend_time or '0').strip() or 0)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return start_date + relativedelta(months=runtime_months)
|
||||
|
||||
def action_update_calc_contract_end_selected(self):
|
||||
updated_count = 0
|
||||
for record in self:
|
||||
new_end_date = record._compute_contract_end_for_calc_date()
|
||||
if record.calc_end_date != new_end_date:
|
||||
record.calc_end_date = new_end_date
|
||||
updated_count += 1
|
||||
|
||||
return {
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'display_notification',
|
||||
'params': {
|
||||
'title': _('Vertragsende aktualisiert'),
|
||||
'message': _('%s Vertrag(e) wurden aktualisiert.') % updated_count,
|
||||
'type': 'success',
|
||||
'sticky': False,
|
||||
}
|
||||
}
|
||||
|
||||
def action_remove_selected_fields(self):
|
||||
selected_fields = self.mapped('werbe_feld_selected')
|
||||
contract_ids = self.ids
|
||||
|
||||
self.write({'werbe_feld_selected': [(5, 0, 0)]})
|
||||
|
||||
for ad_field in selected_fields:
|
||||
remove_cmds = [(3, contract_id) for contract_id in contract_ids if contract_id in ad_field.contract.ids]
|
||||
if remove_cmds:
|
||||
ad_field.write({'contract': remove_cmds})
|
||||
if ad_field.contract_save and ad_field.contract_save.id in contract_ids:
|
||||
ad_field.contract_save = ad_field.contract[:1].id or False
|
||||
|
||||
return {
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'display_notification',
|
||||
'params': {
|
||||
'title': _('Felder entfernt'),
|
||||
'message': _('%s Vertrag(e) wurden von ihren Werbefeldern getrennt.') % len(self),
|
||||
'type': 'success',
|
||||
'sticky': False,
|
||||
}
|
||||
}
|
||||
|
||||
def action_open_move_to_fallback_field_wizard(self):
|
||||
if not self:
|
||||
raise ValidationError(_("Bitte mindestens einen Vertrag auswaehlen."))
|
||||
|
||||
projects = self.mapped('project')
|
||||
if len(projects) != 1:
|
||||
raise ValidationError(_("Bitte nur Vertraege aus einem Projekt gleichzeitig auswaehlen."))
|
||||
|
||||
view = self.env.ref('DigitalSignage.dss_contracts_move_field_wizard_view_form')
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': _('Felder auf ausweichfeld umsetzen'),
|
||||
'res_model': 'dss.contracts.move.field.wizard',
|
||||
'view_mode': 'form',
|
||||
'view_id': view.id,
|
||||
'views': [(view.id, 'form')],
|
||||
'target': 'new',
|
||||
'context': {
|
||||
'active_ids': self.ids,
|
||||
'default_project_id': projects.id,
|
||||
},
|
||||
}
|
||||
|
||||
# def setRealGrafiker(self):
|
||||
#for record in self:
|
||||
#if self.grafiker:
|
||||
|
|
@ -1165,6 +1375,212 @@ class dsscontracts(models.Model):
|
|||
)
|
||||
if action:
|
||||
return action
|
||||
|
||||
def export_kd_csv_all_not_work(self):
|
||||
_logger.info('Export Kunden CSV : C_'+str(self.id))
|
||||
action = (self.env["confirmation.wizard"].confirm_message(_("Soll der Kunde einzeln übertragen werden ?"),title="Bitte bestätigen",method="export_kd_csv",records=self,callback_params={"template":self.id}))
|
||||
if action:
|
||||
_logger.info('Export Kunden CSV : C_'+str(self.id)+' - einzeln bestätigt ')
|
||||
csv = 'Kundenname;Firma;Strasse;Ort;PLZ;Telefon;Mobil;Email;RG-Email;Leitwegid;Company Type\n'
|
||||
#csv += str(self.client_short_vorname) + ' ' + str(self.client_short_name) + ';' + str(self.client_short_company) + ';' + str(self.client_short_strasse) + ';' + str(self.client_short_ort) + ';' + str(self.client_short_plz) + ';' + str(self.client_short_telefon) + ';' + str(self.client_short_mobil) + ';' + str(self.client_short_email) + ';' + str(self.client_short_rgemail) + ';' + str(self.client_short_leitwegid) + ';' + str(self.client_short_company_type.name if self.client_short_company_type else '')+'\n'
|
||||
else:
|
||||
action = (self.env["confirmation.wizard"].confirm_message(_("Soll der Kunde zur Liste der Übertragungen hinzugefügt werden ?"),title="Bitte bestätigen",method="export_kd_csv",records=self,callback_params={"template":self.id}))
|
||||
if action:
|
||||
_logger.info('Export Kunden CSV : C_'+str(self.id)+' - Hinzufügen bestätigt ')
|
||||
action = (self.env["confirmation.wizard"].confirm_message(_("Liste jetzt als CSV erstellen ?"),title="Bitte bestätigen",method="export_kd_csv",records=self,callback_params={"template":self.id}))
|
||||
if action:
|
||||
_logger.info('Export Kunden CSV : C_'+str(self.id)+' - CSV bestätigt ')
|
||||
|
||||
@staticmethod
|
||||
def _split_text_for_csv(value, first_max=40, second_max=40):
|
||||
text = str(value or '').strip()
|
||||
if len(text) <= first_max:
|
||||
return text, ''
|
||||
|
||||
boundary_cut = -1
|
||||
remainder_start = first_max
|
||||
for match in re.finditer(r'[\s\W_]+', text[:first_max + 1]):
|
||||
if 0 < match.start() <= first_max:
|
||||
boundary_cut = match.start()
|
||||
remainder_start = match.end()
|
||||
|
||||
# If no useful separator exists near the limit, fallback to strict split.
|
||||
if boundary_cut < int(first_max * 0.6):
|
||||
first_line = text[:first_max]
|
||||
second_line = text[first_max:]
|
||||
else:
|
||||
first_line = text[:boundary_cut]
|
||||
second_line = text[remainder_start:]
|
||||
|
||||
return first_line, second_line.lstrip()[:second_max]
|
||||
|
||||
@api.model
|
||||
def checkattachents(self,project,contract):
|
||||
_logger.info('Check Attachments : C_'+str(contract)+' - P_'+str(project))
|
||||
html = ""
|
||||
if project.contract_finish_img_cloud:
|
||||
_logger.info('Check Attachments : C_'+str(contract)+' - P_'+str(project)+' - Projekt hat Cloudbilder :'+str(project.contract_finish_img_cloud))
|
||||
return True
|
||||
else:
|
||||
if contract.contract_finish_img_cloud:
|
||||
_logger.info('Check Attachments : C_'+str(contract)+' - P_'+str(project)+' - Vertrag hat Cloudbilder :'+str(contract.contract_finish_img_cloud))
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def _encode_csv_attachment(csv_text):
|
||||
# UTF-8 with BOM keeps umlauts/special chars readable in Excel and downstream tools.
|
||||
normalized = str(csv_text or '').replace('\r\n', '\n').replace('\r', '\n')
|
||||
return base64.b64encode(normalized.encode('utf-8-sig'))
|
||||
|
||||
|
||||
def _export_kd_csv_single(self,record_id):
|
||||
dataset=self.env['dss.contracts'].browse(record_id)
|
||||
_logger.info('Export Kunden CSV : C_'+str(self.id)+' - Einzelner Export ')
|
||||
company_name_line_1, company_name_line_2 = self._split_text_for_csv(dataset.client_short_company)
|
||||
csv = '"DEBITORENKONTO";"Kunde_seit";"ZAHLUNGSART";"SKONTOTYP";"ANREDE";"akad.Grad";"TITEL1";"AP-VORNAME";"AP-NAME";"NAME";"Ergaenzung";"LAND";"STRASSE";"PLZ";"ORT";"TELEFON1";"FAX1";"Mobil1";"INTERNET";"EMAIL1";"TELEFON1_AP";"KUNDENNR";"BANKNAME_1";"BANKKONTO_1";"BLZ_1";"IBAN_1";"BIC_1";"FIBU-KONTENBEZEICHNUNG";"KREDITLIMIT";"HAUSBANK";"FAELLIG_TG"'+'\n'
|
||||
csv += ('"'+str(dataset.contract_auto_id) +
|
||||
'";"' + str(dataset.contract_date.strftime('%d.%m.%Y')) +
|
||||
'";"' + ('L' if dataset.web_contract_is_lastschrift else '') +
|
||||
'";"' + str("4") +
|
||||
'";"' + str("") +
|
||||
'";"' + str("") +
|
||||
'";"' + str("") +
|
||||
'";"' + str(dataset.client_short_vorname) +
|
||||
'";"' + str(dataset.client_short_name) +
|
||||
'";"' + str(company_name_line_1) +
|
||||
'";"' + str(company_name_line_2) +
|
||||
'";"' + str(dataset.client_short_land.name if dataset.client_short_land else 'DEUTSCHLAND') +
|
||||
'";"' + str(dataset.client_short_strasse) +
|
||||
'";"' + str(dataset.client_short_plz) +
|
||||
'";"' + str(dataset.client_short_ort) +
|
||||
'";"' + str(dataset.client_short_telefon) +
|
||||
'";"' + str(dataset.runtime_m) +
|
||||
'";"' + str(dataset.client_short_mobil) +
|
||||
'";"' + str(dataset.cutshortwerbe_feld_selected) +
|
||||
'";"' + str(dataset.client_short_email) +
|
||||
'";"' + str("") +
|
||||
'";"' + str(dataset.contract_auto_id) +
|
||||
'";"' + str(dataset.client_short_BANK_computed) +
|
||||
'";"' + str(dataset.client_short_KONTO_computed) +
|
||||
'";"' + str(dataset.client_short_BLZ_computed) +
|
||||
'";"' + str(dataset.client_short_IBAN).upper() +
|
||||
'";"' + str(dataset.client_short_BIC).upper() +
|
||||
'";"' + str(re.sub(r'[^a-zA-Z0-9]', '', str(dataset.client_short_company.upper().strip()))[:20]) +
|
||||
'";"' + str(dataset.web_contract_paysum_all) +
|
||||
'";"' + str('2' if dataset.web_contract_is_lastschrift else '1') +
|
||||
'"8"'+
|
||||
'"\n')
|
||||
_logger.info('Export Kunden CSV : C_'+str(dataset.id)+' - CSV Inhalt : \n'+str(csv))
|
||||
#csv = csv.replace('"False"', '""')
|
||||
filename = 'KDEXPORT_' + str(dataset.contract_auto_id) + '_' + str(uuid.uuid4())[:8] + '.csv'
|
||||
attachment_id = self.env['ir.attachment'].create({ 'name': filename, 'datas': self._encode_csv_attachment(csv),'res_model': 'dss.contracts', 'res_id': dataset.id})
|
||||
base_url = self.env['ir.config_parameter'].get_param('web.base.url')
|
||||
download_url = '/web/content/' + str(attachment_id.id) + '?download=true'
|
||||
self.client_is_export_finance_date = datetime.now()
|
||||
self.client_is_export_finance = True
|
||||
#
|
||||
# this does the download
|
||||
return {
|
||||
"type": "ir.actions.act_url",
|
||||
"url": str(base_url) + str(download_url),
|
||||
"target": "new"
|
||||
}
|
||||
|
||||
def export_kd_csv_single(self,data_id=None):
|
||||
if not self.client_is_export_finance or data_id:
|
||||
self._export_kd_csv_single(self.id)
|
||||
else:
|
||||
_logger.info('Export Kunden CSV : C_'+str(self.id)+' - Bereits exportiert am '+str(self.client_is_export_finance_date))
|
||||
action = (self.env["confirmation.wizard"].confirm_message(_("Der Kunde wurde bereits am "+str(self.client_is_export_finance_date)+" exportiert ! Soll der Export erneut durchgeführt werden ?"),title="Bitte bestätigen",method="export_kd_csv_single",records=self,callback_params={"data_id":self.id}))
|
||||
if action:
|
||||
_logger.info('Export Kunden CSV : C_'+str(self.id)+' - Einzelner Export erneut bestätigt ')
|
||||
self.client_is_export_finance = False
|
||||
return self.export_kd_csv_single()
|
||||
else:
|
||||
_logger.info('Export Kunden CSV : C_'+str(self.id)+' - Einzelner Export erneut abgelehnt ')
|
||||
|
||||
def _export_kd_csv_add(self,record_ids):
|
||||
dataset=self.env['dss.contracts'].browse(record_ids)
|
||||
_logger.info('Export Kunden CSV : C_'+str(self.id)+' - Hinzufügen ')
|
||||
existing_user_var = self.env['dss.contracts_uservars'].search([('contract', '=', self.id),('var_ID','=','BHEXMARK_1')], limit=1)
|
||||
if not existing_user_var:
|
||||
uvar = self.env['dss.contracts_uservars'].create({'contract': self.id, 'var_ID': 'BHEXMARK_1', 'var_name':'Buchhaltung Export vorgemerkt !', 'var_value': str(datetime.now())})
|
||||
self.userdef_vars = [(4, uvar.id)]
|
||||
_logger.info('Export Kunden CSV : C_'+str(self.id)+' - Hinzufügen - Uservar erstellt ')
|
||||
|
||||
def export_kd_csv_add(self,data_id=None):
|
||||
if not self.client_is_export_finance or data_id:
|
||||
self._export_kd_csv_add(self.id)
|
||||
else:
|
||||
_logger.info('Export Kunden CSV : C_'+str(self.id)+' - Bereits exportiert am '+str(self.client_is_export_finance_date))
|
||||
action = (self.env["confirmation.wizard"].confirm_no_action_message(_("Der Kunde wurde bereits am "+str(self.client_is_export_finance_date)+" exportiert ! "),title="Schon exportiert !"))
|
||||
return action
|
||||
|
||||
def export_kd_csv_send(self):
|
||||
_logger.info('Export Kunden CSV : C_'+str(self.id)+' - Senden ')
|
||||
existing_user_var = self.env['dss.contracts_uservars'].search([('var_ID','=','BHEXMARK_1')])
|
||||
Text = 'Export Kunden CSV es wurden folgende Kunden gefunden und übertragen : \n'
|
||||
csv = '"DEBITORENKONTO";"Kunde_seit";"ZAHLUNGSART";"SKONTOTYP";"ANREDE";"akad.Grad";"TITEL1";"AP-VORNAME";"AP-NAME";"NAME";"Ergaenzung";"LAND";"STRASSE";"PLZ";"ORT";"TELEFON1";"FAX1";"Mobil1";"INTERNET";"EMAIL1";"TELEFON1_AP";"KUNDENNR";"BANKNAME_1";"BANKKONTO_1";"BLZ_1";"IBAN_1";"BIC_1";"FIBU-KONTENBEZEICHNUNG";"HAUSBANK";"FAELLIG_TG"'+'\n'
|
||||
for contract_user_var in existing_user_var:
|
||||
contract=contract_user_var.contract
|
||||
if contract:
|
||||
dataset=contract
|
||||
_logger.info('Export Kunden CSV : C_'+str(self.id)+' - Einzelner Export ')
|
||||
company_name_line_1, company_name_line_2 = self._split_text_for_csv(dataset.client_short_company)
|
||||
csv += ('"'+str(dataset.contract_auto_id) + #DEBITORENKONTO
|
||||
'";"' + str(dataset.contract_date.strftime('%d.%m.%Y')) + #Kunde_seit
|
||||
'";"' + ('L' if dataset.web_contract_is_lastschrift else '') + #ZAHLUNGSART
|
||||
'";"' + str("4") + #SKONTOTYP
|
||||
'";"' + str("") + #ANREDE
|
||||
'";"' + str("") + #akad.Grad
|
||||
'";"' + str("") + #TITEL1
|
||||
'";"' + str(dataset.client_short_vorname) + #AP-VORNAME
|
||||
'";"' + str(dataset.client_short_name) + #AP-NAME
|
||||
'";"' + str(company_name_line_1) + #NAME
|
||||
'";"' + str(company_name_line_2) + #Ergaenzung
|
||||
'";"' + str(dataset.client_short_land.name if dataset.client_short_land else 'DEUTSCHLAND') + #LAND
|
||||
'";"' + str(dataset.client_short_strasse) + #STRASSE
|
||||
'";"' + str(dataset.client_short_plz) + #PLZ
|
||||
'";"' + str(dataset.client_short_ort) + #ORT
|
||||
'";"' + str(dataset.client_short_telefon) + #TELEFON1
|
||||
'";"' + str(dataset.runtime_m) + #FAX1
|
||||
'";"' + str(dataset.client_short_mobil) + #Mobil1
|
||||
'";"' + str(dataset.cutshortwerbe_feld_selected) + #INTERNET
|
||||
'";"' + str(dataset.client_short_email) + #EMAIL1
|
||||
'";"' + str(dataset.client_short_telefon) + #TELEFON1_AP
|
||||
'";"' + str(dataset.contract_auto_id) + #KUNDENNR
|
||||
'";"' + str(dataset.client_short_BANK_computed) + #BANKNAME_1
|
||||
'";"' + str(dataset.client_short_KONTO_computed) + #BANKKONTO_1
|
||||
'";"' + str(dataset.client_short_BLZ_computed) + #BLZ_1
|
||||
'";"' + str(dataset.client_short_IBAN).upper() + #IBAN_1
|
||||
'";"' + str(dataset.client_short_BIC).upper() + #BIC_1
|
||||
'";"' + str((dataset.client_short_vorname)+' '+str(dataset.client_short_name)+' '+str(dataset.client_short_company))[:20] + #FIBU-KONTENBEZEICHNUNG
|
||||
'";"' + str(dataset.web_contract_paysum_all).replace('.', ',') + #KREDITLIMIT
|
||||
'";"' + str('2') + #HAUSBANK
|
||||
'";"' + str('8') + #FAELLIG_TG
|
||||
'"\n')
|
||||
dataset.client_is_export_finance_date = datetime.now()
|
||||
dataset.client_is_export_finance = True
|
||||
Text += 'Kunde : '+str(dataset.contract_auto_name)+' - '+str(dataset.client_short_company)+'\n'
|
||||
#contract.userdef_vars = [(5,0,0)]
|
||||
_logger.info('Export Kunden CSV : C_'+str(self.id)+' - Senden - Uservar entfernt ')
|
||||
contract_user_var.var_ID = 'BHEXMARK_1_SENT'
|
||||
contract_user_var.var_value = str(datetime.now())
|
||||
filename = 'KDEXPORT_' + str(datetime.now().strftime("%Y%m%d%H%M%S")) + '_' + str(uuid.uuid4())[:8] + '.csv'
|
||||
attachment_id = self.env['ir.attachment'].create({ 'name': filename, 'datas': self._encode_csv_attachment(csv)})
|
||||
attachment_id.allow_shared_download=True
|
||||
attachment_id.folder_id=self.env['documents.folders'].search([('name','=','Kundenanlage')],limit=1).id
|
||||
csvs = self.env['dss.csvs'].create({'csv_id': filename,'csv_name': filename, 'csv_filename': filename, 'csv_attachment_id': attachment_id.id, 'csv_mailtext': Text, 'csv_description': 'Export Kunden CSV am '+str(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))})
|
||||
#trigger.SendMail(self,MailEmpfaenger,MailEmpfaengerCC,MailEmpfaengerBCC,MailTemplate,MailSender,has_attachments=False,attachmentlist=[],with_confirm=False,Dataset=None):
|
||||
base_url = self.env['ir.config_parameter'].get_param('web.base.url')
|
||||
download_url = '/web/content/' + str(attachment_id.id) + '?download=true'
|
||||
return {
|
||||
"type": "ir.actions.act_url",
|
||||
"url": str(base_url) + str(download_url),
|
||||
"target": "new"
|
||||
}
|
||||
|
||||
def create_cloud_structure_client(self, newClient):
|
||||
_logger.info("Create Cloud Structure for Client "+str(newClient.id)+" - "+str(newClient.cloudlink))
|
||||
|
|
@ -1253,6 +1669,7 @@ class dsscontracts(models.Model):
|
|||
self.client.email = self.client_short_email
|
||||
self.client.rgemail = self.client_short_rgemail
|
||||
self.client.leitwegid = self.client_short_leitwegid
|
||||
self.client.dsspartner_Company_type = self.client_short_company_type
|
||||
|
||||
def pyaction_dss_contract_update_short_from_partner(self):
|
||||
self.client_short_company=self.client.company_name
|
||||
|
|
@ -1266,8 +1683,9 @@ class dsscontracts(models.Model):
|
|||
self.client_short_email=self.client.email
|
||||
self.client_short_rgemail=self.client.rgemail
|
||||
self.client_short_leitwegid=self.client.leitwegid
|
||||
self.client_short_company_type=self.client.dsspartner_Company_type
|
||||
|
||||
|
||||
@api.model
|
||||
def buildText(self):
|
||||
_logger.info('Build Text for Contract : C_'+str(self.id))
|
||||
|
||||
|
|
@ -1410,7 +1828,11 @@ class dsscontracts(models.Model):
|
|||
|
||||
#raise ValidationError((all_contract_text))
|
||||
return contracts
|
||||
|
||||
|
||||
|
||||
def lm_contract_tool_execute(self):
|
||||
return self.buildText()
|
||||
|
||||
|
||||
def pyopen_dss_contract_add_media_form_view(self):
|
||||
_logger.info('Open Add Media Form')
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ class dsscontractuservars(models.Model):
|
|||
_inherit = ['mail.thread','dss.activity.mixin']
|
||||
uuid = fields.Char(default=lambda self: self._default_uuid(), required=True, readonly=True, copy=False, string='UUID')
|
||||
var_name = fields.Char(string="Variable Name", tracking=True)
|
||||
var_ID = fields.Char(string="Variable ID", tracking=True)
|
||||
var_value = fields.Char(string="Variable Wert", tracking=True)
|
||||
var_description = fields.Text(string="Variable Beschreibung",tracking=True)
|
||||
contract = fields.Many2one('dss.contracts', string="Vertrag",required=True, tracking=True)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,76 @@
|
|||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class DssContractsMoveFieldWizard(models.TransientModel):
|
||||
_name = "dss.contracts.move.field.wizard"
|
||||
_description = "Vertragsfelder auf Ausweichfeld umsetzen"
|
||||
|
||||
project_id = fields.Many2one(
|
||||
'dss.projects',
|
||||
string='Projekt',
|
||||
required=True,
|
||||
readonly=True,
|
||||
)
|
||||
target_field_id = fields.Many2one(
|
||||
'dss.advertisefields',
|
||||
string='Ausweichfeld',
|
||||
required=True,
|
||||
domain="[('project','=',project_id), '|', ('contract','=',False), ('isblocked','=',False)]",
|
||||
)
|
||||
|
||||
@api.model
|
||||
def default_get(self, fields_list):
|
||||
values = super().default_get(fields_list)
|
||||
active_ids = self.env.context.get('active_ids') or []
|
||||
contracts = self.env['dss.contracts'].browse(active_ids).exists()
|
||||
if contracts:
|
||||
projects = contracts.mapped('project')
|
||||
if len(projects) == 1:
|
||||
values.setdefault('project_id', projects.id)
|
||||
return values
|
||||
|
||||
def action_move_contracts_to_field(self):
|
||||
self.ensure_one()
|
||||
active_ids = self.env.context.get('active_ids') or []
|
||||
contracts = self.env['dss.contracts'].browse(active_ids).exists()
|
||||
if not contracts:
|
||||
raise ValidationError(_("Bitte mindestens einen Vertrag auswaehlen."))
|
||||
|
||||
projects = contracts.mapped('project')
|
||||
if len(projects) != 1:
|
||||
raise ValidationError(_("Bitte nur Vertraege aus einem Projekt gleichzeitig auswaehlen."))
|
||||
if projects.id != self.project_id.id:
|
||||
raise ValidationError(_("Das gewaehlte Ausweichfeld passt nicht zum Projekt der Vertraege."))
|
||||
if self.target_field_id.project.id != self.project_id.id:
|
||||
raise ValidationError(_("Das Ausweichfeld muss zum gleichen Projekt gehoeren."))
|
||||
|
||||
contract_ids = contracts.ids
|
||||
old_fields = contracts.mapped('werbe_feld_selected')
|
||||
|
||||
# Remove selected contracts from all old fields first, so they become available again.
|
||||
for ad_field in old_fields:
|
||||
remove_cmds = [(3, contract_id) for contract_id in contract_ids if contract_id in ad_field.contract.ids]
|
||||
if remove_cmds:
|
||||
ad_field.write({'contract': remove_cmds})
|
||||
if ad_field.contract_save and ad_field.contract_save.id in contract_ids:
|
||||
ad_field.contract_save = ad_field.contract[:1].id or False
|
||||
|
||||
add_cmds = [(4, contract_id) for contract_id in contract_ids if contract_id not in self.target_field_id.contract.ids]
|
||||
if add_cmds:
|
||||
self.target_field_id.write({'contract': add_cmds})
|
||||
if not self.target_field_id.contract_save and contract_ids:
|
||||
self.target_field_id.contract_save = contract_ids[0]
|
||||
|
||||
contracts.write({'werbe_feld_selected': [(6, 0, [self.target_field_id.id])]})
|
||||
|
||||
return {
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'display_notification',
|
||||
'params': {
|
||||
'title': _('Ausweichfeld gesetzt'),
|
||||
'message': _('%s Vertrag(e) wurden auf das Feld %s umgesetzt.') % (len(contracts), self.target_field_id.feldname),
|
||||
'type': 'success',
|
||||
'sticky': False,
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,326 @@
|
|||
import ast
|
||||
import datetime
|
||||
import json
|
||||
import re
|
||||
import uuid
|
||||
import logging
|
||||
import base64
|
||||
import subprocess
|
||||
import tempfile
|
||||
import easywebdav
|
||||
import os
|
||||
import os.path
|
||||
import requests
|
||||
import hashlib
|
||||
from PIL import Image,ImageDraw,ImageFont
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo import tools
|
||||
from odoo.exceptions import ValidationError
|
||||
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)
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class dsscsvs(models.Model):
|
||||
|
||||
@api.model
|
||||
def create(self,vals):
|
||||
result = super().create(vals)
|
||||
return result
|
||||
|
||||
_name = "dss.csvs"
|
||||
_description = "DigitalSignage CSV Dateien"
|
||||
# _inherit = ['mail.thread','mail.activity.mixin']
|
||||
_rec_name = "csv_id"
|
||||
# _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')
|
||||
csv_id = fields.Char(string="CSV ID", required=True, help = "Das ist eine eindeutige ID für diese Dateityp. Sie wird automatisch generiert und sollte nicht manuell geändert werden.", default=lambda self: self._default_csv_id())
|
||||
csv_name = fields.Char(string="CSV Name", required=True, help = "Der Name des Dateityp, die Sie hochladen möchten. Dieser Name wird in der Benutzeroberfläche angezeigt.")
|
||||
csv_binary = fields.Binary(string="CSV Datei", help = "Die eigentliche Datei, die Sie hochladen möchten. Diese Datei wird in der Datenbank und Cloud gespeichert")
|
||||
csv_filename = fields.Char(string="CSV Dateiname", help = "Der Name der hochgeladenen Datei. Dies wird automatisch aus dem Dateinamen der hochgeladenen Datei abgerufen.",compute="_compute_csv_filename", store=True)
|
||||
csv_cloud_link = fields.Char(string="Cloud Link", help = "Der Link zur Datei in der Cloud, wenn die Datei in der Cloud gespeichert ist.")
|
||||
csv_description = fields.Text(string="CSV Beschreibung", help = "Eine Beschreibung der hochgeladenen Datei. Dies kann Informationen über den Inhalt der Datei, den Verwendungszweck oder andere relevante Details enthalten.")
|
||||
csv_attachment_id = fields.Many2one('ir.attachment', string="CSV Anhang", help = "Der Anhang, der mit dieser CSV-Datei verbunden ist. Dieser Anhang enthält die tatsächliche Datei, die in der Datenbank gespeichert ist.")
|
||||
csv_mailtext = fields.Text(string="CSV Mailtext", help = "Der Text, der in der E-Mail verwendet wird, wenn diese CSV-Datei per E-Mail versendet wird.")
|
||||
|
||||
|
||||
|
||||
@api.depends('binary_binary')
|
||||
@api.onchange('binary_binary')
|
||||
def _compute_binary_filename(self):
|
||||
"""Compute the binary filename based on the uploaded file."""
|
||||
_logger.info("Binary getting Filename - B_"+str(self.id))
|
||||
if len(self)>0:
|
||||
for record in self:
|
||||
if record.binary_binary:
|
||||
# Extract the filename from the binary content
|
||||
extension = 'jpg' # Default extension
|
||||
if isinstance(self.binary_binary, str):
|
||||
filedata = base64.b64decode(self.binary_binary.split("base64,")[-1])
|
||||
else:
|
||||
filedata = base64.b64decode(self.binary_binary)
|
||||
filedataStr = str(filedata)
|
||||
_logger.info("Binary getting Filename - B_"+str(self.id)+" - ["+str(filedataStr[0:23])+"]")
|
||||
if filedataStr[0:12] == "b'\\x89PNG\\":
|
||||
extension = 'png'
|
||||
elif filedataStr[0:17] == "b'\\xff\\xd8\\xff":
|
||||
extension = 'jpg'
|
||||
elif filedataStr[0:8] == "b'GIF89a":
|
||||
extension = 'gif'
|
||||
elif filedataStr[0:22] == "b'\\x49\\x49\\x2a\\x00":
|
||||
extension = 'tif'
|
||||
elif filedataStr[0:27] == "b'\\x25\\x50\\x44\\x46\\x2d":
|
||||
extension = 'pdf'
|
||||
elif filedataStr[0:23] == "b'\\x00\\x00\\x00 ftypisom":
|
||||
extension = 'mp4'
|
||||
elif filedataStr[0:22] == "b'\\x00\\x00\\x00\\x18ftyp":
|
||||
extension = 'mp4'
|
||||
elif filedataStr[0:12] == "b'\\x42\\x4d":
|
||||
extension = 'bmp'
|
||||
record.binary_filename = f"{record.binary_mediatype.filepartname}{record.binary_contract.contract_auto_id}.{extension}"
|
||||
|
||||
@api.model
|
||||
def _default_uuid(self):
|
||||
return str(uuid.uuid4())
|
||||
|
||||
@api.model
|
||||
def _default_create_date(self):
|
||||
return datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
|
||||
@api.model
|
||||
def _default_create_user(self):
|
||||
return self.env.user.name if self.env.user else 'System'
|
||||
|
||||
@api.model
|
||||
def _default_binary_id(self):
|
||||
# Generate a unique binary ID based on the current timestamp and a random UUID
|
||||
return f"Datei_{str(uuid.uuid4())[:8]}"
|
||||
|
||||
@api.onchange('binary_binary')
|
||||
def _onchange_binary_binary(self):
|
||||
"""Update binary size and type when binary content is changed."""
|
||||
_logger.info("New Binary B_"+str(self.id))
|
||||
existing_binary = False
|
||||
if not self._origin.binary_binary:
|
||||
existing_binary = False
|
||||
else:
|
||||
existing_binary = True
|
||||
|
||||
if not self.binary_contract.cloudlink:
|
||||
raise ValidationError(_("Der Vertrag hat keinen Cloudlink. Bitte überprüfen Sie den Vertrag."))
|
||||
cloudpath = str(self.binary_contract.cloudlink)
|
||||
_logger.info("New Binary checking Cloudlink B_"+str(self.id)+" - "+str(cloudpath))
|
||||
cloudpath = cloudpath + '/' + str(self.binary_mediatype.cloudlink)
|
||||
client = Client("https://cloud.logumedia.de/remote.php/dav/files/OdooDav/", auth=("odooClient@logumedia.de", "lm2020#OdooDav"))
|
||||
_logger.info("New Binary checking Cloudlink for cont : "+str(cloudpath))
|
||||
cloudexists = False
|
||||
if client.exists(cloudpath):
|
||||
# Convert self.binary_binary to bytes-like object
|
||||
if isinstance(self.binary_binary, str):
|
||||
filedata = base64.b64decode(self.binary_binary.split("base64,")[-1])
|
||||
else:
|
||||
filedata = base64.b64decode(self.binary_binary)
|
||||
filedataStr = str(filedata)
|
||||
_logger.info("New Binary Cloudlink exists - Saving File - B_"+str(self.id)+" - "+str(cloudpath)+" - " + str(filedata)+' - ' + filedataStr[0:8])
|
||||
try:
|
||||
decoded_data = filedata
|
||||
except base64.binascii.Error as e:
|
||||
if 'Incorrect padding' in str(e):
|
||||
filedataStr += b'=' * (4 - len(filedataStr) % 4)
|
||||
decoded_data = base64.b64decode(filedataStr)
|
||||
if filedataStr[0:10] == "b'\x89PNG":
|
||||
extension = 'png'
|
||||
else:
|
||||
extension = 'jpg'
|
||||
_logger.info("New Binary Cloudlink exists - Saving File - B_"+str(self.id)+" - "+str(cloudpath)+" - " + str(base64.b64encode(decoded_data)))
|
||||
##infile = open('/tmp/'+filename, "w+b")
|
||||
#infile = tempfile.NamedTemporaryFile(delete=False)
|
||||
##infile.write(filedata)
|
||||
##client.upload_fileobj(infile, f"{cloudpath}/{filename}")
|
||||
##infile.close()
|
||||
##infile.delete() # Delete the temporary file after upload
|
||||
cloudexists = True
|
||||
else:
|
||||
_logger.info("New Binary Cloudlink does not exists B_"+str(self.id)+" - "+str(cloudpath))
|
||||
raise ValidationError(_("Der Cloudlink im Vertrag ist nicht vorhanden. Bitte überprüfen Sie den Link ggf. anlegen."))
|
||||
|
||||
if cloudexists:
|
||||
if self.binary_binary:
|
||||
# Calculate the size of the binary content
|
||||
self.binary_size = len(filedata)
|
||||
# Determine the file type based on the binary content
|
||||
self.binary_cloud_link = f"{cloudpath}/{self.binary_filename}"
|
||||
self.binary_issaved = True
|
||||
else:
|
||||
self.binary_size = 0
|
||||
self.binary_type = ''
|
||||
self.binary_cloud_link = ''
|
||||
self.binary_issaved = False
|
||||
|
||||
|
||||
def dload(self):
|
||||
"""Download the binary file."""
|
||||
if not self.binary_binary:
|
||||
raise ValidationError(_("No binary file to download."))
|
||||
|
||||
# Create a temporary file to store the downloaded content
|
||||
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=self.binary_filename)
|
||||
temp_file.write(base64.b64decode(self.binary_binary))
|
||||
temp_file.close()
|
||||
|
||||
# Return the file for download
|
||||
return {
|
||||
'type': 'ir.actions.act_url',
|
||||
'url': f'/web/content/{self.id}/{self.binary_filename}?download=true&filename={self.binary_filename}',
|
||||
'target': 'self',
|
||||
}
|
||||
|
||||
def dedit(self):
|
||||
"""Edit the binary file."""
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'res_model': 'dss.binaries',
|
||||
'view_mode': 'form',
|
||||
'res_id': self.id,
|
||||
'target': 'new',
|
||||
}
|
||||
|
||||
def resend(self):
|
||||
"""Resend the binary file to the contract's partner email."""
|
||||
if not self.binary_contract:
|
||||
raise ValidationError(_("No contract associated with this binary file."))
|
||||
if not self.binary_contract.project.standortpartner:
|
||||
raise ValidationError(_("No partner associated with the contract."))
|
||||
if not self.binary_contract.project.standortpartner.email:
|
||||
raise ValidationError(_("The partner does not have an email address."))
|
||||
# Send email with the binary file as attachment
|
||||
|
||||
def genstamp(self):
|
||||
_logger.info('Create Stamps for Binary: B_'+str(self.id)+' '+str(id))
|
||||
# Bildparameter definieren
|
||||
Medium = self.binary_mediatype
|
||||
if Medium and Medium.can_gen_stamp and self.binary_contract:
|
||||
_logger.info('Create Stamps for Contract : B_'+str(self.id)+' - Contract: C_'+str(self.binary_contract.id)+'- Medium : M_'+str(Medium.id)+' - '+str(Medium.medianame))
|
||||
# Bildgröße und Hintergrundfarbe festlegen
|
||||
background_color = (255, 255, 255) # Weißer Hintergrund (RGB)
|
||||
# Neues Bild erstellen
|
||||
maxh = Medium.maxsize_h
|
||||
maxw = Medium.maxsize_w
|
||||
image = Image.new("RGB", (maxw, maxh), background_color)
|
||||
d = ImageDraw.Draw(image)
|
||||
Zeilen = Medium.maxsize_h // 6
|
||||
zposy = 2
|
||||
if self.binary_contract.client:
|
||||
text = self.binary_contract.contact_company_name
|
||||
_logger.info('Create Stamps for Contract : B_'+str(self.id)+' - Contract: C_'+str(self.binary_contract.id)+'- Medium : M_'+str(Medium.id)+' - Text1: '+str(text))
|
||||
if len(text) > 35:
|
||||
# Trenne den Text an einem Leerzeichen nahe bei Position 40
|
||||
idx = text.rfind(' ', 0, 35)
|
||||
if idx == -1:
|
||||
idx = 35
|
||||
text2 = text[:idx]
|
||||
bcount = len(text2) if text2 else 1
|
||||
fonts = maxw // (bcount ) *1.8
|
||||
if fonts > maxh:
|
||||
fonts = maxh - 10
|
||||
if fonts <=0 :
|
||||
fonts = 10
|
||||
# font = ImageFont.truetype("addons/web/static/fonts/sign/Zeyada-Regular.ttf", fonts)
|
||||
font = ImageFont.truetype("addons/web/static/fonts/google/Open_Sans/Open_Sans-SemiBold.ttf", fonts)
|
||||
d.text((maxw // 2, 1*Zeilen), text2, fill="black",align="center", anchor="ms", font=font) # Beispiel: Ein Rechteck
|
||||
text = text[idx:]
|
||||
zposy = 2
|
||||
if len(text) > 35:
|
||||
# Trenne den Text an einem Leerzeichen nahe bei Position 40
|
||||
idx = text.rfind(' ', 0, 35)
|
||||
if idx == -1:
|
||||
idx = 35
|
||||
text2 = text[:idx]
|
||||
# font = ImageFont.truetype("addons/web/static/fonts/sign/Zeyada-Regular.ttf", fonts)
|
||||
font = ImageFont.truetype("addons/web/static/fonts/google/Open_Sans/Open_Sans-SemiBold.ttf", fonts)
|
||||
d.text((maxw // 2, zposy*Zeilen), text2, fill="black",align="center", anchor="ms", font=font) # Beispiel: Ein Rechteck
|
||||
text = text[idx:]
|
||||
zposy = 3
|
||||
else:
|
||||
bcount = len(text) if text else 1
|
||||
fonts = maxw // (bcount ) *1.8
|
||||
if fonts > maxh:
|
||||
fonts = maxh - 10
|
||||
if fonts <=0 :
|
||||
fonts = 10
|
||||
# font = ImageFont.truetype("addons/web/static/fonts/sign/Zeyada-Regular.ttf", fonts)
|
||||
font = ImageFont.truetype("addons/web/static/fonts/google/Open_Sans/Open_Sans-SemiBold.ttf", fonts)
|
||||
d.text((maxw // 2, zposy*Zeilen), text, fill="black",align="center", anchor="ms", font=font) # Beispiel: Ein Rechteck
|
||||
text = self.binary_contract.contact_street
|
||||
bcount = len(text) if text else 1
|
||||
Zeilen = maxh // 6
|
||||
fonts = maxw // (bcount ) *1.2
|
||||
if fonts > maxh:
|
||||
fonts = maxh - 10
|
||||
if fonts <=0 :
|
||||
fonts = 10
|
||||
# font = ImageFont.truetype("addons/web/static/fonts/sign/Zeyada-Regular.ttf", fonts)
|
||||
font = ImageFont.truetype("addons/web/static/fonts/google/Open_Sans/Open_Sans-Regular.ttf", fonts)
|
||||
d.text((maxw // 2, 4*Zeilen), text, fill="black",align="center", anchor="ms", font=font) # Beispiel: Ein Rechteck
|
||||
text = self.binary_contract.contact_zip+' '+self.binary_contract.contact_city
|
||||
bcount = len(text) if text else 1
|
||||
Zeilen = maxh // 6
|
||||
fonts = maxw // (bcount ) *1.2
|
||||
if fonts > maxh:
|
||||
fonts = maxh - 10
|
||||
if fonts <=0 :
|
||||
fonts = 10
|
||||
# font = ImageFont.truetype("addons/web/static/fonts/sign/Zeyada-Regular.ttf", fonts)
|
||||
font = ImageFont.truetype("addons/web/static/fonts/google/Open_Sans/Open_Sans-Regular.ttf", fonts)
|
||||
d.text((maxw // 2, 5*Zeilen), text, fill="black",align="center", anchor="ms", font=font) # Beispiel: Ein Rechteck
|
||||
# Bild speichern
|
||||
filename = Medium.filepartname+self.binary_contract.contract_auto_id+".png"
|
||||
_logger.info('Create Stamps for Contract : B_'+str(self.id)+' - Contract: C_'+str(self.binary_contract.id)+'- Feld : F_'+str(Medium.id)+' - Filename: '+str(filename))
|
||||
image.save(filename)
|
||||
_logger.info("Stamp file created "+str(filename))
|
||||
_logger.info("Copy file to Binaries B_"+str(self))
|
||||
try:
|
||||
self.binary_binary = base64.b64encode(open(filename, "rb").read())
|
||||
self.binary_filename = filename
|
||||
self.binary_type = 'image/png'
|
||||
self.binary_size = os.path.getsize(filename)
|
||||
self.binary_issaved = False
|
||||
self.binary_secured_ro = False
|
||||
self.binary_used_ro = False
|
||||
finally:
|
||||
_logger.info("Binary inserted "+str(self))
|
||||
cloudpath = self.binary_contract.cloudlink
|
||||
if not cloudpath:
|
||||
raise ValidationError(_("Der Vertrag hat keinen Cloudlink. Bitte überprüfen Sie den Vertrag."))
|
||||
if not Medium.cloudlink:
|
||||
raise ValidationError(_("Der Mediatyp hat keinen Cloudlink. Bitte überprüfen Sie den Mediatyp."))
|
||||
cloudpath = cloudpath + '/' + str(Medium.cloudlink)
|
||||
_logger.info("Copy file to Cloud Path : "+str(cloudpath))
|
||||
try:
|
||||
client = Client("https://cloud.logumedia.de/remote.php/dav/files/OdooDav/", auth=("odooClient@logumedia.de", "lm2020#OdooDav"))
|
||||
try:
|
||||
#client.mkdir(new_folder)
|
||||
#_logger.info("Make Cloud Path : "+str(new_folder))
|
||||
client.upload_file(filename, f"{cloudpath}/{filename}")
|
||||
except Exception as e:
|
||||
_logger.info("Make Cloud Path error : "+str(e))
|
||||
finally:
|
||||
self.binary_issaved = True
|
||||
_logger.info("File copied to Cloud Path : "+str(cloudpath))
|
||||
return True
|
||||
|
||||
|
|
@ -50,6 +50,7 @@ class dssadvertisefields(models.Model):
|
|||
feldtemplates = fields.Many2many('dss.advertisefields.templates' , string='Vertrag', store=True,tracking=True)
|
||||
fullsize_w = fields.Integer("Auflösung Breite",tracking=True)
|
||||
fullsize_h = fields.Integer("Auflösung Höhe",tracking=True)
|
||||
adportal_visible = fields.Boolean('Ad Portal sichtbar', default=False, tracking=True)
|
||||
|
||||
def _default_create_date(self):
|
||||
return str(date.today())
|
||||
|
|
|
|||
|
|
@ -54,22 +54,26 @@ class dssmaintaintasks(models.Model):
|
|||
maintain_travel_end = fields.Datetime('Ankunftszeit', tracking=True)
|
||||
maintain_travel_distance = fields.Float('Anreise Entfernung', tracking=True)
|
||||
maintain_travel_duration = fields.Float('Anreise Dauer', tracking=True, compute='_compute_travel_total_time', store=True)
|
||||
maintain_travel_duration_man = fields.Float('Anreise Dauer', tracking=True, store=True)
|
||||
maintain_travel_duration_text = fields.Char('Anreise Dauer', tracking=True, compute='_compute_travel_total_time_text', store=True)
|
||||
|
||||
maintain_work_start = fields.Datetime('Arbeitsbeginn', tracking=True)
|
||||
maintain_work_end = fields.Datetime('Arbeitsende', tracking=True)
|
||||
maintain_work_duration = fields.Float('Arbeits Dauer', tracking=True,compute='_compute_work_total_time', store=True)
|
||||
maintain_work_duration_man = fields.Float('Arbeits Dauer', tracking=True, store=True)
|
||||
maintain_work_duration_text = fields.Char('Arbeits Dauer', tracking=True, compute='_compute_work_total_time_text', store=True)
|
||||
|
||||
maintain_return_start = fields.Datetime('Rückreisebeginn', tracking=True)
|
||||
maintain_return_end = fields.Datetime('Rückreiseende', tracking=True)
|
||||
maintain_return_duration = fields.Float('Rückreise Dauer', tracking=True,compute='_compute_return_total_time', store=True)
|
||||
maintain_return_duration_man = fields.Float('Rückreise Dauer', tracking=True, store=True)
|
||||
maintain_return_distance = fields.Float('Rückreise Entfernung', tracking=True)
|
||||
maintain_return_duration_text = fields.Char('Rückreise Dauer', tracking=True, compute='_compute_return_total_time_text', store=True)
|
||||
|
||||
maintain_total_start = fields.Datetime('Einsatzbeginn', tracking=True,compute='_compute_maintain_total_start', store=True)
|
||||
maintain_total_end = fields.Datetime('Einsatzende', tracking=True, compute='_compute_maintain_total_end', store=True)
|
||||
maintain_total_duration = fields.Float('Einsatz Dauer', tracking=True,compute='_compute_maintain_total_time', store=True)
|
||||
maintain_total_duration_man = fields.Float('Einsatz Dauer', tracking=True, store=True)
|
||||
maintain_total_duration_text = fields.Char('Einsatz Dauer', tracking=True, compute='_compute_maintain_total_time_text', store=True)
|
||||
maintain_total_duration_money = fields.Float('Einsatz Kosten', tracking=True, compute='_compute_maintain_total_cost', store=True)
|
||||
|
||||
|
|
@ -94,33 +98,43 @@ class dssmaintaintasks(models.Model):
|
|||
def _compute_travel_total_time_text(self):
|
||||
for record in self:
|
||||
if record.maintain_travel_duration:
|
||||
hours = int(record.maintain_travel_duration)
|
||||
minutes = int((record.maintain_travel_duration - hours) * 60)
|
||||
record.maintain_travel_duration_text = f"{hours} Stunden {minutes} Minuten"
|
||||
if record.maintain_travel_duration:
|
||||
hours = int(record.maintain_travel_duration)
|
||||
minutes = int((record.maintain_travel_duration - hours) * 60)
|
||||
record.maintain_travel_duration_text = f"{hours} Stunden {minutes} Minuten"
|
||||
else:
|
||||
record.maintain_travel_duration_text = "0 Stunden 0 Minuten"
|
||||
else:
|
||||
record.maintain_travel_duration_text = "0 Stunden 0 Minuten"
|
||||
record.maintain_travel_duration_text = ""
|
||||
|
||||
@api.depends('maintain_work_duration')
|
||||
@api.onchange('maintain_work_duration')
|
||||
def _compute_work_total_time_text(self):
|
||||
for record in self:
|
||||
if record.maintain_work_duration:
|
||||
hours = int(record.maintain_work_duration)
|
||||
minutes = int((record.maintain_work_duration - hours) * 60)
|
||||
record.maintain_work_duration_text = f"{hours} Stunden {minutes} Minuten"
|
||||
if not record.maintain_work_duration_man:
|
||||
if record.maintain_work_duration:
|
||||
hours = int(record.maintain_work_duration)
|
||||
minutes = int((record.maintain_work_duration - hours) * 60)
|
||||
record.maintain_work_duration_text = f"{hours} Stunden {minutes} Minuten"
|
||||
else:
|
||||
record.maintain_work_duration_text = "0 Stunden 0 Minuten"
|
||||
else:
|
||||
record.maintain_work_duration_text = "0 Stunden 0 Minuten"
|
||||
record.maintain_work_duration_text = ""
|
||||
|
||||
|
||||
@api.depends('maintain_return_duration')
|
||||
@api.onchange('maintain_return_duration')
|
||||
def _compute_return_total_time_text(self):
|
||||
for record in self:
|
||||
if record.maintain_return_duration:
|
||||
hours = int(record.maintain_return_duration)
|
||||
minutes = int((record.maintain_return_duration - hours) * 60)
|
||||
record.maintain_return_duration_text = f"{hours} Stunden {minutes} Minuten"
|
||||
if not record.maintain_return_duration_man:
|
||||
if record.maintain_return_duration:
|
||||
hours = int(record.maintain_return_duration)
|
||||
minutes = int((record.maintain_return_duration - hours) * 60)
|
||||
record.maintain_return_duration_text = f"{hours} Stunden {minutes} Minuten"
|
||||
else:
|
||||
record.maintain_return_duration_text = "0 Stunden 0 Minuten"
|
||||
else:
|
||||
record.maintain_return_duration_text = "0 Stunden 0 Minuten"
|
||||
record.maintain_return_duration_text = ""
|
||||
|
||||
@api.depends('maintain_total_duration')
|
||||
@api.onchange('maintain_total_duration')
|
||||
|
|
@ -183,15 +197,21 @@ class dssmaintaintasks(models.Model):
|
|||
record.maintain_total_end = False
|
||||
|
||||
@api.depends('maintain_travel_duration', 'maintain_work_duration', 'maintain_return_duration')
|
||||
@api.onchange('maintain_travel_duration', 'maintain_work_duration', 'maintain_return_duration')
|
||||
@api.onchange('maintain_travel_duration', 'maintain_work_duration', 'maintain_return_duration','maintain_travel_duration_man', 'maintain_work_duration_man', 'maintain_return_duration_man')
|
||||
def _compute_maintain_total_time(self):
|
||||
for record in self:
|
||||
total_duration = 0.0
|
||||
if record.maintain_travel_duration:
|
||||
if record.maintain_travel_duration_man:
|
||||
total_duration += record.maintain_travel_duration_man
|
||||
elif record.maintain_travel_duration:
|
||||
total_duration += record.maintain_travel_duration
|
||||
if record.maintain_work_duration:
|
||||
if record.maintain_work_duration_man:
|
||||
total_duration += record.maintain_work_duration_man
|
||||
elif record.maintain_work_duration:
|
||||
total_duration += record.maintain_work_duration
|
||||
if record.maintain_return_duration:
|
||||
if record.maintain_return_duration_man:
|
||||
total_duration += record.maintain_return_duration_man
|
||||
elif record.maintain_return_duration:
|
||||
total_duration += record.maintain_return_duration
|
||||
record.maintain_total_duration = total_duration
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,472 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from datetime import timedelta
|
||||
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class DssPhonecenterQuery(models.Model):
|
||||
_name = "dss.phonecenter.query"
|
||||
_description = "Telefoniecenter Suchanfrage"
|
||||
_order = "id desc"
|
||||
|
||||
name = fields.Char("Anfrage", required=True)
|
||||
project_id = fields.Many2one("dss.projects", string="Projekt", required=True, index=True)
|
||||
user_id = fields.Many2one("res.users", string="Benutzer", required=True, default=lambda self: self.env.user)
|
||||
location_query = fields.Char("Ort", required=True)
|
||||
radius_km = fields.Integer("Umkreis (km)", default=20)
|
||||
industry_query = fields.Char("Branche", required=True)
|
||||
state = fields.Selection([
|
||||
("draft", "Neu"),
|
||||
("active", "Aktiv"),
|
||||
("done", "Abgeschlossen"),
|
||||
], default="draft", string="Status")
|
||||
search_count = fields.Integer("Suchen", default=0)
|
||||
line_ids = fields.One2many("dss.phonecenter.query.line", "query_id", string="Ergebnisse")
|
||||
open_line_count = fields.Integer("Offene Eintraege", compute="_compute_line_counts")
|
||||
done_line_count = fields.Integer("Abarbeitet", compute="_compute_line_counts")
|
||||
|
||||
@api.depends("line_ids.processed", "line_ids.rejected", "line_ids.reschedule_at")
|
||||
def _compute_line_counts(self):
|
||||
now = fields.Datetime.now()
|
||||
for rec in self:
|
||||
total = len(rec.line_ids)
|
||||
done = len(rec.line_ids.filtered(
|
||||
lambda line: line.processed or line.rejected or (
|
||||
line.reschedule_at and line.reschedule_at > now and not line.rejected and not line.processed
|
||||
)
|
||||
))
|
||||
rec.done_line_count = done
|
||||
rec.open_line_count = max(0, total - done)
|
||||
|
||||
@api.model
|
||||
def get_project_queries(self, project_id):
|
||||
project_id = int(project_id or 0)
|
||||
if not project_id:
|
||||
return []
|
||||
|
||||
records = self.search([
|
||||
("project_id", "=", project_id),
|
||||
("user_id", "=", self.env.user.id),
|
||||
], order="id desc", limit=100)
|
||||
|
||||
payload = []
|
||||
for rec in records:
|
||||
payload.append({
|
||||
"id": rec.id,
|
||||
"name": rec.name,
|
||||
"location_query": rec.location_query or "",
|
||||
"radius_km": rec.radius_km or 20,
|
||||
"industry_query": rec.industry_query or "",
|
||||
"state": rec.state,
|
||||
"search_count": rec.search_count,
|
||||
"open_line_count": rec.open_line_count,
|
||||
"done_line_count": rec.done_line_count,
|
||||
"can_repeat": (rec.open_line_count == 0),
|
||||
})
|
||||
|
||||
now = fields.Datetime.now()
|
||||
reschedule_lines = self.env["dss.phonecenter.query.line"].search([
|
||||
("query_id.project_id", "=", project_id),
|
||||
("query_id.user_id", "=", self.env.user.id),
|
||||
("reschedule_at", "!=", False),
|
||||
("rejected", "=", False),
|
||||
("processed", "=", False),
|
||||
])
|
||||
if reschedule_lines:
|
||||
due_count = len(reschedule_lines.filtered(lambda l: l.reschedule_at and l.reschedule_at <= now))
|
||||
total_count = len(reschedule_lines)
|
||||
payload.append({
|
||||
"id": "reschedule:%s" % project_id,
|
||||
"name": "Wiedervorlagen",
|
||||
"location_query": "",
|
||||
"radius_km": 0,
|
||||
"industry_query": "",
|
||||
"state": "active",
|
||||
"search_count": 0,
|
||||
"open_line_count": due_count,
|
||||
"done_line_count": max(0, total_count - due_count),
|
||||
"can_repeat": True,
|
||||
"is_reschedule_bucket": True,
|
||||
})
|
||||
return payload
|
||||
|
||||
@api.model
|
||||
def get_reschedule_details(self, project_id):
|
||||
project_id = int(project_id or 0)
|
||||
if not project_id:
|
||||
return {"ok": False, "message": "Projekt fehlt."}
|
||||
|
||||
now = fields.Datetime.now()
|
||||
lines = self.env["dss.phonecenter.query.line"].search([
|
||||
("query_id.project_id", "=", project_id),
|
||||
("query_id.user_id", "=", self.env.user.id),
|
||||
("reschedule_at", "!=", False),
|
||||
("rejected", "=", False),
|
||||
("processed", "=", False),
|
||||
], order="reschedule_at asc, id desc")
|
||||
|
||||
due_lines = lines.filtered(lambda l: l.reschedule_at and l.reschedule_at <= now)
|
||||
companies = []
|
||||
for line in due_lines:
|
||||
companies.append({
|
||||
"id": str(line.external_id or line.id),
|
||||
"line_id": line.id,
|
||||
"name": line.name,
|
||||
"address": line.address or "",
|
||||
"city": line.city or "",
|
||||
"website": line.website or "",
|
||||
"phone": line.phone or "",
|
||||
"email": line.email or "",
|
||||
"rating": line.rating or 0,
|
||||
"source": line.source or "",
|
||||
"verified": bool(line.verified),
|
||||
"verified_at": line.verified_at or "",
|
||||
"place_id": line.place_id or "",
|
||||
"google_maps_url": line.google_maps_url or "",
|
||||
"lat": line.lat,
|
||||
"lng": line.lng,
|
||||
"types": line.types_json or [],
|
||||
"processed": bool(line.processed),
|
||||
"rejected": bool(line.rejected),
|
||||
"reschedule_at": fields.Datetime.to_string(line.reschedule_at) if line.reschedule_at else "",
|
||||
"reschedule_note": line.reschedule_note or "",
|
||||
})
|
||||
|
||||
return {
|
||||
"ok": True,
|
||||
"query": {
|
||||
"id": "reschedule:%s" % project_id,
|
||||
"name": "Wiedervorlagen",
|
||||
"location_query": "",
|
||||
"radius_km": 0,
|
||||
"industry_query": "",
|
||||
"state": "active",
|
||||
"search_count": 0,
|
||||
"open_line_count": len(companies),
|
||||
"done_line_count": max(0, len(lines) - len(companies)),
|
||||
"can_repeat": True,
|
||||
"is_reschedule_bucket": True,
|
||||
},
|
||||
"companies": companies,
|
||||
}
|
||||
|
||||
@api.model
|
||||
def create_query(self, payload):
|
||||
payload = payload or {}
|
||||
project_id = int(payload.get("project_id") or 0)
|
||||
if not project_id:
|
||||
return {"ok": False, "message": "Projekt fehlt."}
|
||||
|
||||
location_query = (payload.get("location_query") or "").strip()
|
||||
industry_query = (payload.get("industry_query") or "").strip()
|
||||
radius_km = int(payload.get("radius_km") or 20)
|
||||
|
||||
if not location_query or not industry_query:
|
||||
return {"ok": False, "message": "Ort und Branche sind erforderlich."}
|
||||
|
||||
record = self.create({
|
||||
"project_id": project_id,
|
||||
"user_id": self.env.user.id,
|
||||
"location_query": location_query,
|
||||
"industry_query": industry_query,
|
||||
"radius_km": radius_km,
|
||||
"name": "%s | %s | %s km" % (industry_query, location_query, radius_km),
|
||||
"state": "active",
|
||||
})
|
||||
return {
|
||||
"ok": True,
|
||||
"query": {
|
||||
"id": record.id,
|
||||
"name": record.name,
|
||||
"location_query": record.location_query,
|
||||
"radius_km": record.radius_km,
|
||||
"industry_query": record.industry_query,
|
||||
"state": record.state,
|
||||
"search_count": record.search_count,
|
||||
"open_line_count": record.open_line_count,
|
||||
"done_line_count": record.done_line_count,
|
||||
"can_repeat": (record.open_line_count == 0),
|
||||
},
|
||||
}
|
||||
|
||||
@api.model
|
||||
def get_query_details(self, query_id):
|
||||
query_id = int(query_id or 0)
|
||||
record = self.browse(query_id)
|
||||
if not record.exists() or record.user_id.id != self.env.user.id:
|
||||
return {"ok": False, "message": "Anfrage nicht gefunden."}
|
||||
|
||||
now = fields.Datetime.now()
|
||||
companies = []
|
||||
visible_lines = record.line_ids.filtered(
|
||||
lambda l: not l.rejected and not (
|
||||
l.reschedule_at and l.reschedule_at > now and not l.processed
|
||||
)
|
||||
)
|
||||
for line in visible_lines.sorted(key=lambda l: (l.processed, -l.id)):
|
||||
companies.append({
|
||||
"id": str(line.external_id or line.id),
|
||||
"line_id": line.id,
|
||||
"name": line.name,
|
||||
"address": line.address or "",
|
||||
"city": line.city or "",
|
||||
"website": line.website or "",
|
||||
"phone": line.phone or "",
|
||||
"email": line.email or "",
|
||||
"rating": line.rating or 0,
|
||||
"source": line.source or "",
|
||||
"verified": bool(line.verified),
|
||||
"verified_at": line.verified_at or "",
|
||||
"place_id": line.place_id or "",
|
||||
"google_maps_url": line.google_maps_url or "",
|
||||
"lat": line.lat,
|
||||
"lng": line.lng,
|
||||
"types": line.types_json or [],
|
||||
"processed": bool(line.processed),
|
||||
"rejected": bool(line.rejected),
|
||||
"reschedule_at": fields.Datetime.to_string(line.reschedule_at) if line.reschedule_at else "",
|
||||
"reschedule_note": line.reschedule_note or "",
|
||||
})
|
||||
|
||||
return {
|
||||
"ok": True,
|
||||
"query": {
|
||||
"id": record.id,
|
||||
"name": record.name,
|
||||
"location_query": record.location_query or "",
|
||||
"radius_km": record.radius_km or 20,
|
||||
"industry_query": record.industry_query or "",
|
||||
"state": record.state,
|
||||
"search_count": record.search_count,
|
||||
"open_line_count": record.open_line_count,
|
||||
"done_line_count": record.done_line_count,
|
||||
"can_repeat": (record.open_line_count == 0),
|
||||
},
|
||||
"companies": companies,
|
||||
}
|
||||
|
||||
@api.model
|
||||
def can_repeat_query(self, query_id):
|
||||
query_id = int(query_id or 0)
|
||||
record = self.browse(query_id)
|
||||
if not record.exists() or record.user_id.id != self.env.user.id:
|
||||
return {"ok": False, "can_repeat": False}
|
||||
return {
|
||||
"ok": True,
|
||||
"can_repeat": bool(record.open_line_count == 0),
|
||||
"open_line_count": record.open_line_count,
|
||||
}
|
||||
|
||||
@api.model
|
||||
def save_query_results(self, query_id, companies):
|
||||
query_id = int(query_id or 0)
|
||||
record = self.browse(query_id)
|
||||
if not record.exists() or record.user_id.id != self.env.user.id:
|
||||
return {"ok": False, "message": "Anfrage nicht gefunden."}
|
||||
|
||||
if not isinstance(companies, list):
|
||||
companies = []
|
||||
|
||||
line_model = self.env["dss.phonecenter.query.line"]
|
||||
added = 0
|
||||
for company in companies:
|
||||
if not isinstance(company, dict):
|
||||
continue
|
||||
name = (company.get("name") or "").strip()
|
||||
if not name:
|
||||
continue
|
||||
|
||||
external_id = str(company.get("id") or company.get("place_id") or name)
|
||||
existing = line_model.search([
|
||||
("query_id", "=", record.id),
|
||||
("external_id", "=", external_id),
|
||||
], limit=1)
|
||||
|
||||
vals = {
|
||||
"query_id": record.id,
|
||||
"external_id": external_id,
|
||||
"place_id": (company.get("place_id") or "").strip(),
|
||||
"name": name,
|
||||
"address": (company.get("address") or "").strip(),
|
||||
"city": (company.get("city") or "").strip(),
|
||||
"website": (company.get("website") or "").strip(),
|
||||
"phone": (company.get("phone") or "").strip(),
|
||||
"email": (company.get("email") or "").strip(),
|
||||
"rating": company.get("rating") or 0,
|
||||
"source": (company.get("source") or "").strip(),
|
||||
"verified": bool(company.get("verified", False)),
|
||||
"verified_at": (company.get("verified_at") or "").strip(),
|
||||
"google_maps_url": (company.get("google_maps_url") or "").strip(),
|
||||
"lat": company.get("lat"),
|
||||
"lng": company.get("lng"),
|
||||
"types_json": company.get("types") if isinstance(company.get("types"), list) else [],
|
||||
"processed": bool(existing.processed) if existing else False,
|
||||
"rejected": bool(existing.rejected) if existing else False,
|
||||
"reject_reason": existing.reject_reason if existing else "",
|
||||
"decision_summary": existing.decision_summary if existing else "",
|
||||
"reschedule_at": existing.reschedule_at if existing else False,
|
||||
"reschedule_note": existing.reschedule_note if existing else "",
|
||||
}
|
||||
|
||||
if existing:
|
||||
existing.write(vals)
|
||||
else:
|
||||
line_model.create(vals)
|
||||
added += 1
|
||||
|
||||
record.search_count = (record.search_count or 0) + 1
|
||||
record.state = "active"
|
||||
|
||||
details = self.get_query_details(record.id)
|
||||
details.update({"ok": True, "added": added})
|
||||
return details
|
||||
|
||||
@api.model
|
||||
def mark_result_processed(self, line_id, processed=True):
|
||||
line_id = int(line_id or 0)
|
||||
line = self.env["dss.phonecenter.query.line"].browse(line_id)
|
||||
if not line.exists() or line.query_id.user_id.id != self.env.user.id:
|
||||
return {"ok": False, "message": "Eintrag nicht gefunden."}
|
||||
|
||||
line.processed = bool(processed)
|
||||
if processed:
|
||||
line.rejected = False
|
||||
line.reschedule_at = False
|
||||
line.reschedule_note = ""
|
||||
if line.query_id.open_line_count == 0:
|
||||
line.query_id.state = "done"
|
||||
else:
|
||||
line.query_id.state = "active"
|
||||
|
||||
return {
|
||||
"ok": True,
|
||||
"query_id": line.query_id.id,
|
||||
"open_line_count": line.query_id.open_line_count,
|
||||
"can_repeat": (line.query_id.open_line_count == 0),
|
||||
}
|
||||
|
||||
@api.model
|
||||
def set_reschedule(self, line_id, reschedule_at=False, days=0, note=""):
|
||||
line_id = int(line_id or 0)
|
||||
line = self.env["dss.phonecenter.query.line"].browse(line_id)
|
||||
if not line.exists() or line.query_id.user_id.id != self.env.user.id:
|
||||
return {"ok": False, "message": "Eintrag nicht gefunden."}
|
||||
|
||||
target_dt = False
|
||||
if reschedule_at:
|
||||
target_dt = fields.Datetime.to_datetime(reschedule_at)
|
||||
if not target_dt:
|
||||
day_count = int(days or 0)
|
||||
if day_count > 0:
|
||||
target_dt = fields.Datetime.now() + timedelta(days=day_count)
|
||||
|
||||
if not target_dt:
|
||||
return {"ok": False, "message": "Bitte Wiedervorlagezeit angeben."}
|
||||
|
||||
line.write({
|
||||
"last_decision": "reschedule",
|
||||
"processed": False,
|
||||
"rejected": False,
|
||||
"reschedule_at": target_dt,
|
||||
"reschedule_note": (note or "").strip(),
|
||||
})
|
||||
|
||||
if line.query_id.open_line_count == 0:
|
||||
line.query_id.state = "done"
|
||||
else:
|
||||
line.query_id.state = "active"
|
||||
|
||||
return {
|
||||
"ok": True,
|
||||
"line_id": line.id,
|
||||
"query_id": line.query_id.id,
|
||||
"reschedule_at": fields.Datetime.to_string(line.reschedule_at) if line.reschedule_at else "",
|
||||
"open_line_count": line.query_id.open_line_count,
|
||||
}
|
||||
|
||||
@api.model
|
||||
def apply_contact_decision(self, line_id, decision, reason="", summary=""):
|
||||
line_id = int(line_id or 0)
|
||||
line = self.env["dss.phonecenter.query.line"].browse(line_id)
|
||||
if not line.exists() or line.query_id.user_id.id != self.env.user.id:
|
||||
return {"ok": False, "message": "Eintrag nicht gefunden."}
|
||||
|
||||
decision_value = (decision or "").strip()
|
||||
# Backward-compatible alias: treat no_interest as a rejection/discard decision.
|
||||
if decision_value == "no_interest":
|
||||
decision_value = "discard"
|
||||
vals = {
|
||||
"last_decision": decision_value,
|
||||
"reject_reason": (reason or "").strip(),
|
||||
"decision_summary": (summary or "").strip(),
|
||||
}
|
||||
if decision_value == "discard":
|
||||
vals.update({
|
||||
"rejected": True,
|
||||
"processed": True,
|
||||
"reschedule_at": False,
|
||||
"reschedule_note": "",
|
||||
})
|
||||
elif decision_value in ("research", "reschedule", "keep"):
|
||||
vals.update({
|
||||
"rejected": False,
|
||||
"processed": False,
|
||||
})
|
||||
if decision_value in ("research", "keep"):
|
||||
vals.update({
|
||||
"reschedule_at": False,
|
||||
"reschedule_note": "",
|
||||
})
|
||||
|
||||
line.write(vals)
|
||||
if line.query_id.open_line_count == 0:
|
||||
line.query_id.state = "done"
|
||||
else:
|
||||
line.query_id.state = "active"
|
||||
|
||||
return {
|
||||
"ok": True,
|
||||
"line_id": line.id,
|
||||
"query_id": line.query_id.id,
|
||||
"decision": decision_value,
|
||||
"rejected": bool(line.rejected),
|
||||
"open_line_count": line.query_id.open_line_count,
|
||||
}
|
||||
|
||||
|
||||
class DssPhonecenterQueryLine(models.Model):
|
||||
_name = "dss.phonecenter.query.line"
|
||||
_description = "Telefoniecenter Suchergebnis"
|
||||
_order = "id desc"
|
||||
|
||||
query_id = fields.Many2one("dss.phonecenter.query", string="Anfrage", required=True, ondelete="cascade", index=True)
|
||||
external_id = fields.Char("Externe ID", index=True)
|
||||
place_id = fields.Char("Place ID")
|
||||
name = fields.Char("Unternehmen", required=True)
|
||||
address = fields.Char("Adresse")
|
||||
city = fields.Char("Ort")
|
||||
website = fields.Char("Webseite")
|
||||
phone = fields.Char("Telefon")
|
||||
email = fields.Char("Email")
|
||||
rating = fields.Float("Rating")
|
||||
source = fields.Char("Quelle")
|
||||
verified = fields.Boolean("Verifiziert")
|
||||
verified_at = fields.Char("Verifiziert am")
|
||||
google_maps_url = fields.Char("Google Maps URL")
|
||||
lat = fields.Float("Breitengrad")
|
||||
lng = fields.Float("Laengengrad")
|
||||
types_json = fields.Json("Typen")
|
||||
processed = fields.Boolean("Abarbeitet", default=False)
|
||||
rejected = fields.Boolean("Abgelehnt", default=False)
|
||||
reject_reason = fields.Text("Ablehnungsgrund")
|
||||
decision_summary = fields.Text("Entscheidungszusammenfassung")
|
||||
last_decision = fields.Selection([
|
||||
("reschedule", "Wiedervorlage"),
|
||||
("discard", "Komplett verwerfen"),
|
||||
("no_interest", "Kein Interesse"),
|
||||
("research", "Weitere Recherchen"),
|
||||
("keep", "Zunaechst behalten"),
|
||||
], string="Letzte Entscheidung")
|
||||
reschedule_at = fields.Datetime("Wiedervorlage am")
|
||||
reschedule_note = fields.Text("Wiedervorlage Notiz")
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class DssPortalUserProfile(models.Model):
|
||||
_name = 'dss.portal.user.profile'
|
||||
_description = 'AD Portal User Profile'
|
||||
|
||||
user_id = fields.Many2one('res.users', required=True, ondelete='cascade', index=True)
|
||||
|
||||
ranking_enabled = fields.Boolean('Im Ranking', default=False)
|
||||
ranking_position = fields.Integer('Ranking Position', default=0)
|
||||
ranking_sum = fields.Float('Ranking Summe', default=0.0)
|
||||
|
||||
right_contract_overview = fields.Boolean('Portal Recht: Vertragsanzeige', default=True)
|
||||
right_contract_detail = fields.Boolean('Portal Recht: Vertragsdetails', default=True)
|
||||
right_media_view = fields.Boolean('Portal Recht: Medienanzeige', default=True)
|
||||
right_ranking_view = fields.Boolean('Portal Recht: Rankinganzeige', default=True)
|
||||
right_graphic_examples = fields.Boolean('Portal Recht: Grafikbeispiele anzeigen/erstellen', default=False)
|
||||
|
||||
_sql_constraints = [
|
||||
('dss_portal_user_profile_user_unique', 'unique(user_id)', 'Fuer jeden Benutzer ist nur ein Portal-Profil erlaubt.'),
|
||||
]
|
||||
|
|
@ -78,6 +78,7 @@ class dssprojects(models.Model):
|
|||
grundsystemicon_different = fields.Boolean('Grundsystem Icon anders',default=False,tracking=True)
|
||||
grundsystemicon_different_image = fields.Binary('Anderes Icon',tracking=True)
|
||||
grundsystem_webinput_template = fields.Char(related='grundsystemname.web_input_template',tracking=True)
|
||||
grundsystem_webinput_showimage_url = fields.Char(related='grundsystemname.web_input_showimage_url',tracking=True)
|
||||
|
||||
|
||||
btntemplate = fields.Many2one('dss.display.templates',string='Buttonsvorlage',tracking=True)
|
||||
|
|
@ -168,9 +169,45 @@ class dssprojects(models.Model):
|
|||
lastkdnr = fields.Integer('Letzte Temp. KdNr', tracking=True)
|
||||
lastrealkdnr = fields.Integer('Letzte Real. KdNr', tracking=True)
|
||||
reservedkdnr = fields.Char('Reservierte KdNr', tracking=True)
|
||||
lostkdnr = fields.Char('Verlorene KdNr "," Seperated', tracking=True)
|
||||
|
||||
web_input_template = fields.Char('Web-Eingabe Template', tracking=True)
|
||||
|
||||
project_binaries = fields.Many2many('dss.binaries', string='Projektdateien', tracking=True)
|
||||
contract_finish_img_cloud = fields.Char('Überg. Bildlink',tracking=True)
|
||||
|
||||
def pyopen_dss_project_add_binary_form_view(self):
|
||||
_logger.info('Open Add Binary Form')
|
||||
view = self.env.ref("DigitalSignage.dss_project_binaries_view_form_wizard")
|
||||
_logger.info('Open Wizzard Binary Form '+str(view))
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'view_mode': 'form' ,
|
||||
'view_type': 'form' ,
|
||||
'view_id': view.id,
|
||||
'res_model': 'dss.binaries.wizard.new' ,
|
||||
'target': 'new' ,
|
||||
'views':[(view.id,'form')],
|
||||
'context': {'default_binary_project':self.id},
|
||||
}
|
||||
|
||||
def pyopen_dss_project_add_binary_form_view_from_wizzard(self,media):
|
||||
_logger.info('Open Add Binary Form')
|
||||
view = self.env.ref("DigitalSignage.dss_binaries_view_form")
|
||||
binary = self.env['dss.binaries'].create({'binary_project':self.id,'binary_name':self.projektname,'binary_mediatype':media.id,'binary_id':str(media.filepartname+'_'+str(self.projectid)),'binary_binary':media.standard_image})
|
||||
self.project_binaries = [(4, binary.id)]
|
||||
_logger.info('Open Add Binary Form '+str(view)+' B_'+str(binary))
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'view_mode': 'form' ,
|
||||
'view_type': 'form' ,
|
||||
'view_id': view.id,
|
||||
'res_model': 'dss.binaries' ,
|
||||
'target': 'new' ,
|
||||
'views':[(view.id,'form')],
|
||||
'res_id':binary.id,
|
||||
}
|
||||
|
||||
@api.model
|
||||
def _read_group_system_ids(self, stages, domain, order):
|
||||
project_system_ids = self.env['dss.systemtypen'].search([('open_close_standard_state','=',False)], order='order')
|
||||
|
|
|
|||
|
|
@ -1,11 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import uuid
|
||||
import logging
|
||||
|
||||
from odoo import api,fields, models
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class Resusers(models.Model):
|
||||
|
|
@ -14,5 +9,111 @@ class Resusers(models.Model):
|
|||
|
||||
# company_type = fields.Selection(selection_add=[('dss_client','Werbekunde'),('dss_partner','Partnerunternehmen')])
|
||||
dss_projects = fields.Many2many('dss.projects',string='DigitalSignage Projekte')
|
||||
phonecenter_view_mode = fields.Selection([
|
||||
('callcenter', 'Callcenter'),
|
||||
('projectmanagement', 'Projektmanagement'),
|
||||
('aussendienst', 'Aussendienst'),
|
||||
('administrator', 'Administrator'),
|
||||
], string='Ansichtsmodus', default='callcenter')
|
||||
portal_ranking_enabled = fields.Boolean('Im Ranking', compute='_compute_portal_profile_flags', inverse='_inverse_portal_ranking_enabled')
|
||||
portal_ranking_position = fields.Integer('Ranking Position', compute='_compute_portal_profile_flags', inverse='_inverse_portal_ranking_position')
|
||||
portal_ranking_sum = fields.Float('Ranking Summe', compute='_compute_portal_profile_flags', inverse='_inverse_portal_ranking_sum')
|
||||
|
||||
portal_right_contract_overview = fields.Boolean('Portal Recht: Vertragsanzeige', compute='_compute_portal_profile_flags', inverse='_inverse_portal_right_contract_overview')
|
||||
portal_right_contract_detail = fields.Boolean('Portal Recht: Vertragsdetails', compute='_compute_portal_profile_flags', inverse='_inverse_portal_right_contract_detail')
|
||||
portal_right_media_view = fields.Boolean('Portal Recht: Medienanzeige', compute='_compute_portal_profile_flags', inverse='_inverse_portal_right_media_view')
|
||||
portal_right_ranking_view = fields.Boolean('Portal Recht: Rankinganzeige', compute='_compute_portal_profile_flags', inverse='_inverse_portal_right_ranking_view')
|
||||
portal_right_graphic_examples = fields.Boolean('Portal Recht: Grafikbeispiele anzeigen/erstellen', compute='_compute_portal_profile_flags', inverse='_inverse_portal_right_graphic_examples')
|
||||
show_graphic_examples = fields.Boolean('Grafikbeispiele anzeigen/erstellen', compute='_compute_portal_profile_flags', inverse='_inverse_show_graphic_examples')
|
||||
|
||||
def _get_or_create_portal_profile(self):
|
||||
self.ensure_one()
|
||||
profile_model = self.env['dss.portal.user.profile'].sudo()
|
||||
profile = profile_model.search([('user_id', '=', self.id)], limit=1)
|
||||
if profile:
|
||||
return profile
|
||||
return profile_model.create({
|
||||
'user_id': self.id,
|
||||
'ranking_enabled': False,
|
||||
'ranking_position': 0,
|
||||
'ranking_sum': 0.0,
|
||||
'right_contract_overview': True,
|
||||
'right_contract_detail': True,
|
||||
'right_media_view': True,
|
||||
'right_ranking_view': True,
|
||||
'right_graphic_examples': False,
|
||||
})
|
||||
|
||||
def _set_portal_profile_value(self, field_name, value):
|
||||
for user in self:
|
||||
profile = user._get_or_create_portal_profile()
|
||||
profile.write({field_name: value})
|
||||
|
||||
def _compute_portal_profile_flags(self):
|
||||
profile_model = self.env['dss.portal.user.profile'].sudo()
|
||||
profiles = profile_model.search([('user_id', 'in', self.ids)])
|
||||
profile_by_user = {profile.user_id.id: profile for profile in profiles}
|
||||
|
||||
for user in self:
|
||||
profile = profile_by_user.get(user.id)
|
||||
user.portal_ranking_enabled = bool(profile.ranking_enabled) if profile else False
|
||||
user.portal_ranking_position = int(profile.ranking_position or 0) if profile else 0
|
||||
user.portal_ranking_sum = float(profile.ranking_sum or 0.0) if profile else 0.0
|
||||
|
||||
user.portal_right_contract_overview = bool(profile.right_contract_overview) if profile else True
|
||||
user.portal_right_contract_detail = bool(profile.right_contract_detail) if profile else True
|
||||
user.portal_right_media_view = bool(profile.right_media_view) if profile else True
|
||||
user.portal_right_ranking_view = bool(profile.right_ranking_view) if profile else True
|
||||
user.portal_right_graphic_examples = bool(profile.right_graphic_examples) if profile else False
|
||||
user.show_graphic_examples = bool(profile.right_graphic_examples) if profile else False
|
||||
|
||||
def _inverse_portal_ranking_enabled(self):
|
||||
for user in self:
|
||||
user._set_portal_profile_value('ranking_enabled', bool(user.portal_ranking_enabled))
|
||||
|
||||
def _inverse_portal_ranking_position(self):
|
||||
for user in self:
|
||||
user._set_portal_profile_value('ranking_position', int(user.portal_ranking_position or 0))
|
||||
|
||||
def _inverse_portal_ranking_sum(self):
|
||||
for user in self:
|
||||
user._set_portal_profile_value('ranking_sum', float(user.portal_ranking_sum or 0.0))
|
||||
|
||||
def _inverse_portal_right_contract_overview(self):
|
||||
for user in self:
|
||||
user._set_portal_profile_value('right_contract_overview', bool(user.portal_right_contract_overview))
|
||||
|
||||
def _inverse_portal_right_contract_detail(self):
|
||||
for user in self:
|
||||
user._set_portal_profile_value('right_contract_detail', bool(user.portal_right_contract_detail))
|
||||
|
||||
def _inverse_portal_right_media_view(self):
|
||||
for user in self:
|
||||
user._set_portal_profile_value('right_media_view', bool(user.portal_right_media_view))
|
||||
|
||||
def _inverse_portal_right_ranking_view(self):
|
||||
for user in self:
|
||||
user._set_portal_profile_value('right_ranking_view', bool(user.portal_right_ranking_view))
|
||||
|
||||
def _inverse_portal_right_graphic_examples(self):
|
||||
for user in self:
|
||||
user._set_portal_profile_value('right_graphic_examples', bool(user.portal_right_graphic_examples))
|
||||
|
||||
def _inverse_show_graphic_examples(self):
|
||||
for user in self:
|
||||
user._set_portal_profile_value('right_graphic_examples', bool(user.show_graphic_examples))
|
||||
|
||||
@api.model
|
||||
def get_phone_center_profile(self):
|
||||
user = self.env.user
|
||||
mode = user.phonecenter_view_mode or 'callcenter'
|
||||
can_see_all_projects = mode in ('projectmanagement', 'administrator')
|
||||
return {
|
||||
'user_id': user.id,
|
||||
'view_mode': mode,
|
||||
'can_see_all_projects': can_see_all_projects,
|
||||
'assigned_project_ids': user.dss_projects.ids,
|
||||
}
|
||||
|
||||
# dss_uuid = fields.Char('uuid')
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import tempfile
|
|||
import easywebdav
|
||||
import os
|
||||
import os.path
|
||||
import requests
|
||||
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
|
|
@ -74,6 +75,26 @@ class dssSettings(models.Model):
|
|||
show_expand_contract_group_ad_state = fields.Boolean('Jeden Kampagnen-Status anzeigen ?',help='im Groupierungsmodus der Verträge nach Kampagnenstatus alle (auch unbenutzte) Statusspalten anzeigen ?',tracking=True)
|
||||
show_expand_contract_group_ad_work_state = fields.Boolean('Jeden Kampagnen-Arbeitsstatus anzeigen ?',help='im Groupierungsmodus der Verträge nach Kampagnen-Arbeitsstatus alle (auch unbenutzte) Statusspalten anzeigen ?',tracking=True)
|
||||
show_expand_contract_group_ad_todo_state = fields.Boolean('Jeden Kampagnen-Aufgabenschritt anzeigen ?',help='im Groupierungsmodus der Verträge nach Kampagnen-Aufgabenschritt alle (auch unbenutzte) Aufgabenspalten anzeigen ?',tracking=True)
|
||||
adportal_ranking_show_foreign_names = fields.Boolean('Ranking Fremdnamen anzeigen', default=True, help='Wenn deaktiviert, werden im AD Portal Ranking fremde Namen als ***** angezeigt. Der eigene Name bleibt sichtbar.', tracking=True)
|
||||
adportal_graphics_base_prompt = fields.Text('AD Portal Grafik Prompt', compute='_compute_adportal_graphics_config', inverse='_inverse_adportal_graphics_config', tracking=True)
|
||||
adportal_graphics_use_chatgpt = fields.Boolean('AD Portal: Benutze ChatGPT', compute='_compute_adportal_graphics_config', inverse='_inverse_adportal_graphics_config', tracking=True)
|
||||
adportal_graphics_chatgpt_model = fields.Selection(
|
||||
[
|
||||
('gpt-image-1', 'gpt-image-1'),
|
||||
('dall-e-3', 'dall-e-3'),
|
||||
('dall-e-2', 'dall-e-2'),
|
||||
],
|
||||
string='AD Portal ChatGPT Modell',
|
||||
compute='_compute_adportal_graphics_config',
|
||||
inverse='_inverse_adportal_graphics_config',
|
||||
tracking=True,
|
||||
)
|
||||
adportal_graphics_api_url = fields.Char('AD Portal Grafik API URL', compute='_compute_adportal_graphics_config', inverse='_inverse_adportal_graphics_config', tracking=True)
|
||||
adportal_graphics_api_key = fields.Char('AD Portal Grafik API Key', compute='_compute_adportal_graphics_config', inverse='_inverse_adportal_graphics_config', tracking=True)
|
||||
adportal_graphics_api_timeout = fields.Integer('AD Portal Grafik API Timeout (Sekunden)', compute='_compute_adportal_graphics_config', inverse='_inverse_adportal_graphics_config', tracking=True)
|
||||
adportal_google_company_check = fields.Boolean('AD Portal Firmenpruefung via Google', compute='_compute_adportal_graphics_config', inverse='_inverse_adportal_graphics_config', tracking=True)
|
||||
adportal_google_api_key = fields.Char('AD Portal Google API Key', compute='_compute_adportal_graphics_config', inverse='_inverse_adportal_graphics_config', tracking=True)
|
||||
adportal_google_cse_id = fields.Char('AD Portal Google CSE ID', compute='_compute_adportal_graphics_config', inverse='_inverse_adportal_graphics_config', tracking=True)
|
||||
freigabe_auto_time = fields.Integer('Auto Freigabetage',help='Automatische Freigabe nach x Tagen',tracking=True)
|
||||
tv_vertragsankuendigung_actionID = fields.Integer('TV Vertragsankündigung ActionID',help="Aktion die ausgeführt wird, wenn im Web-Vertragseingang der Button Vertragsankündigung geklickt wird.",tracking=True)
|
||||
systemonline_color = fields.Char('Farbe Online',tracking=True)
|
||||
|
|
@ -84,11 +105,126 @@ class dssSettings(models.Model):
|
|||
rporturl = fields.Char('RPort URL',tracking=True)
|
||||
rportusername = fields.Char('RPort Username',tracking=True)
|
||||
rporttoken = fields.Char('RPort API Token',tracking=True)
|
||||
ai_agent_enabled = fields.Boolean('AI Agent aktiv', default=True, tracking=True)
|
||||
ai_agent_base_url = fields.Char('AI Base URL', default='https://api.openai.com/v1', tracking=True)
|
||||
ai_agent_model = fields.Char('AI Modell', default='gpt-4o-mini', tracking=True)
|
||||
ai_agent_api_key = fields.Char('AI API Key', tracking=True)
|
||||
ai_agent_system_prompt = fields.Text(
|
||||
'AI System Prompt',
|
||||
default='Du bist ein hilfreicher virtueller Mitarbeiter im Odoo Backend.',
|
||||
tracking=True,
|
||||
)
|
||||
ai_agent_callback_prompt = fields.Text(
|
||||
'AI Callback Prompt',
|
||||
default=(
|
||||
'Verwende Callback-Funktionen fuer Datenzugriffe. '
|
||||
'Nutze get_model_records fuer gezielte Modell/Feld-Abfragen, '
|
||||
'get_all_contracts fuer Vertragslisten, get_contract_by_number fuer Vertragsdetails '
|
||||
'und get_projects fuer Projektlisten. Nutze create_model_record nur fuer explizite '
|
||||
'Anlagevorgaenge und nur mit freigegebenen Schreibfeldern. Formuliere Antworten kompakt '
|
||||
'und nachvollziehbar.'
|
||||
),
|
||||
tracking=True,
|
||||
)
|
||||
ai_agent_callback_models = fields.Char(
|
||||
'AI Callback Modelle',
|
||||
default='dss.contracts,dss.projects',
|
||||
help='Kommagetrennte Modellnamen, die per get_model_records abgefragt werden duerfen.',
|
||||
tracking=True,
|
||||
)
|
||||
ai_agent_callback_fields_json = fields.Text(
|
||||
'AI Callback Felder (JSON)',
|
||||
default='{"dss.contracts": ["contract_auto_id", "contract_name", "client_short_company", "contract_state", "project", "vertragssumme"], "dss.projects": ["projectid", "projektname", "name", "standort_strasse", "standort_plz", "standort_ort", "maps_visitors", "aktstatus"]}',
|
||||
help='JSON-Objekt mit erlaubten Feldern pro Modell fuer get_model_records.',
|
||||
tracking=True,
|
||||
)
|
||||
ai_agent_callback_aliases_json = fields.Text(
|
||||
'AI Callback Feld-Aliase (JSON)',
|
||||
default='{"dss.contracts": {"kundennummer": "contract_auto_id", "vertragsnummer": "contract_auto_id", "vertragsname": "contract_name", "vertragskennung": "contract_auto_name", "kunde": "client_short_company", "firma": "client_short_company", "vorname": "client_short_vorname", "nachname": "client_short_name", "autoverlaengerung": "contract_auto_extend", "auto_verlaengerung": "contract_auto_extend", "verlaengerung_auto": "contract_auto_extend"}}',
|
||||
help='JSON-Objekt mit Aliasen pro Modell, z.B. {"dss.contracts": {"kundennummer": "contract_auto_id"}}.',
|
||||
tracking=True,
|
||||
)
|
||||
ai_agent_callback_write_fields = fields.Text(
|
||||
'AI Callback Schreibfelder',
|
||||
default='',
|
||||
help='Exakte Erlaubnisliste fuer Schreibzugriffe im Muster model.field, z.B. dss.projects.name oder dss.contracts.remark. Mehrere Eintraege per Komma oder Zeilenumbruch trennen.',
|
||||
tracking=True,
|
||||
)
|
||||
ai_agent_timeout_seconds = fields.Integer('AI Timeout (Sekunden)', default=60, tracking=True)
|
||||
ai_agent_temperature = fields.Float('AI Temperatur', default=0.3, tracking=True)
|
||||
ai_agent_max_context_messages = fields.Integer('AI Kontext Nachrichten', default=20, tracking=True)
|
||||
ai_agent_rag_enabled = fields.Boolean('AI RAG aktiv (LangChain)', default=True, tracking=True)
|
||||
ai_agent_rag_top_k = fields.Integer('AI RAG Treffer (Top-K)', default=8, tracking=True)
|
||||
ai_agent_contracts_access = fields.Boolean('AI Zugriff auf Vertraege', default=True, tracking=True)
|
||||
ai_agent_contracts_limit = fields.Integer('AI Vertragskontext Anzahl (0 = alle)', default=0, tracking=True)
|
||||
ai_agent_projects_access = fields.Boolean('AI Zugriff auf Projekte', default=True, tracking=True)
|
||||
ai_agent_projects_limit = fields.Integer('AI Projektkontext Anzahl (0 = alle)', default=0, tracking=True)
|
||||
phonecenter_company_provider = fields.Selection([
|
||||
('google_places', 'Google Places'),
|
||||
('custom_api', 'Custom Firmen-API'),
|
||||
('mock', 'Mock/Fallback'),
|
||||
], string='Telefoniecenter Firmenprovider', default='google_places', tracking=True)
|
||||
phonecenter_company_api_url = fields.Char('Telefoniecenter Firmen-API URL', tracking=True)
|
||||
phonecenter_company_api_key = fields.Char('Telefoniecenter Firmen-API Key', tracking=True)
|
||||
phonecenter_company_timeout = fields.Integer('Telefoniecenter Firmen-Timeout (Sekunden)', default=20, tracking=True)
|
||||
phonecenter_ai_provider = fields.Selection([
|
||||
('none', 'Keine KI'),
|
||||
('openai', 'OpenAI kompatibel'),
|
||||
('custom_api', 'Custom KI-API'),
|
||||
], string='Telefoniecenter KI-Provider', default='openai', tracking=True)
|
||||
phonecenter_ai_api_url = fields.Char('Telefoniecenter KI-API URL', tracking=True)
|
||||
phonecenter_ai_api_key = fields.Char('Telefoniecenter KI-API Key', tracking=True)
|
||||
phonecenter_ai_model = fields.Char('Telefoniecenter KI Modell', default='gpt-4o-mini', tracking=True)
|
||||
phonecenter_search_min_entries = fields.Integer('Telefoniecenter Min. Suchergebnisse', default=8, tracking=True)
|
||||
phonecenter_search_max_results = fields.Integer('Telefoniecenter Max. Ergebnisse', default=30, tracking=True)
|
||||
phonecenter_search_cache_minutes = fields.Integer('Telefoniecenter Cache (Minuten)', default=30, tracking=True)
|
||||
showdebug = fields.Boolean('Debug',help='Debug Informationen aktivieren ',tracking=True)
|
||||
m2mcron = fields.Many2one('ir.cron',string='M2M Cron',tracking=True)
|
||||
m2mcron = fields.Many2one('ir.cron',string='M2M Cron',tracking=True)
|
||||
|
||||
variables = fields.Many2many('dss.settings.vars',string='Einstellungs Variablen',tracking=True)
|
||||
|
||||
def _as_bool(self, value):
|
||||
return str(value or '').strip().lower() in ('1', 'true', 'yes', 'y', 'on')
|
||||
|
||||
def _normalize_adportal_graphics_chatgpt_model(self, value):
|
||||
allowed_models = {'gpt-image-1', 'dall-e-3', 'dall-e-2'}
|
||||
normalized_value = (value or '').strip()
|
||||
return normalized_value if normalized_value in allowed_models else 'gpt-image-1'
|
||||
|
||||
def _compute_adportal_graphics_config(self):
|
||||
config = self.env['ir.config_parameter'].sudo()
|
||||
for record in self:
|
||||
record.adportal_graphics_base_prompt = config.get_param(
|
||||
'digitalsignage.adportal_graphics_base_prompt',
|
||||
default='Erstelle eine hochwertige Werbegrafik im Corporate-Stil fuer die Firma.',
|
||||
)
|
||||
record.adportal_graphics_use_chatgpt = self._as_bool(config.get_param('digitalsignage.adportal_graphics_use_chatgpt', default='False'))
|
||||
record.adportal_graphics_chatgpt_model = self._normalize_adportal_graphics_chatgpt_model(
|
||||
config.get_param('digitalsignage.adportal_graphics_chatgpt_model', default='gpt-image-1')
|
||||
)
|
||||
record.adportal_graphics_api_url = config.get_param('digitalsignage.adportal_graphics_api_url', default='')
|
||||
record.adportal_graphics_api_key = config.get_param('digitalsignage.adportal_graphics_api_key', default='')
|
||||
record.adportal_graphics_api_timeout = int(config.get_param('digitalsignage.adportal_graphics_api_timeout', default='90') or 90)
|
||||
record.adportal_google_company_check = self._as_bool(config.get_param('digitalsignage.adportal_google_company_check', default='False'))
|
||||
record.adportal_google_api_key = config.get_param('digitalsignage.adportal_google_api_key', default='')
|
||||
record.adportal_google_cse_id = config.get_param('digitalsignage.adportal_google_cse_id', default='')
|
||||
|
||||
def _inverse_adportal_graphics_config(self):
|
||||
config = self.env['ir.config_parameter'].sudo()
|
||||
for record in self:
|
||||
config.set_param('digitalsignage.adportal_graphics_base_prompt', record.adportal_graphics_base_prompt or '')
|
||||
config.set_param('digitalsignage.adportal_graphics_use_chatgpt', 'True' if record.adportal_graphics_use_chatgpt else 'False')
|
||||
config.set_param(
|
||||
'digitalsignage.adportal_graphics_chatgpt_model',
|
||||
self._normalize_adportal_graphics_chatgpt_model(record.adportal_graphics_chatgpt_model),
|
||||
)
|
||||
config.set_param('digitalsignage.adportal_graphics_api_url', record.adportal_graphics_api_url or '')
|
||||
config.set_param('digitalsignage.adportal_graphics_api_key', record.adportal_graphics_api_key or '')
|
||||
config.set_param('digitalsignage.adportal_graphics_api_timeout', int(record.adportal_graphics_api_timeout or 90))
|
||||
config.set_param('digitalsignage.adportal_google_company_check', 'True' if record.adportal_google_company_check else 'False')
|
||||
config.set_param('digitalsignage.adportal_google_api_key', record.adportal_google_api_key or '')
|
||||
config.set_param('digitalsignage.adportal_google_cse_id', record.adportal_google_cse_id or '')
|
||||
|
||||
def _get_settingvalue(self,valuename):
|
||||
settings = (self.env['dss.settings'].search([],limit=1))
|
||||
wert = settings._origin.read([valuename])[0][valuename]
|
||||
|
|
@ -230,6 +366,652 @@ class dssSettings(models.Model):
|
|||
records.append(fieldrecord)
|
||||
return records
|
||||
|
||||
@api.model
|
||||
def _phonecenter_get_settings(self):
|
||||
settings = self.search([], limit=1)
|
||||
if not settings:
|
||||
return {}
|
||||
|
||||
ai_api_url = settings.phonecenter_ai_api_url or settings.ai_agent_base_url or 'https://api.openai.com/v1'
|
||||
ai_api_key = settings.phonecenter_ai_api_key or settings.ai_agent_api_key or ''
|
||||
ai_model = settings.phonecenter_ai_model or settings.ai_agent_model or 'gpt-4o-mini'
|
||||
|
||||
return {
|
||||
'google_maps_key': settings.google_maps_key or '',
|
||||
'company_provider': settings.phonecenter_company_provider or 'google_places',
|
||||
'company_api_url': settings.phonecenter_company_api_url or '',
|
||||
'company_api_key': settings.phonecenter_company_api_key or '',
|
||||
'company_timeout': max(5, int(settings.phonecenter_company_timeout or 20)),
|
||||
'ai_provider': settings.phonecenter_ai_provider or 'openai',
|
||||
'ai_api_url': ai_api_url,
|
||||
'ai_api_key': ai_api_key,
|
||||
'ai_model': ai_model,
|
||||
'min_entries': max(1, int(settings.phonecenter_search_min_entries or 8)),
|
||||
'max_results': max(5, int(settings.phonecenter_search_max_results or 30)),
|
||||
}
|
||||
|
||||
@api.model
|
||||
def phonecenter_get_search_config(self):
|
||||
cfg = self._phonecenter_get_settings()
|
||||
return {
|
||||
'company_provider': cfg.get('company_provider', 'google_places'),
|
||||
'ai_provider': cfg.get('ai_provider', 'openai'),
|
||||
'min_entries': cfg.get('min_entries', 8),
|
||||
'max_results': cfg.get('max_results', 30),
|
||||
'radius_max': 2000,
|
||||
}
|
||||
|
||||
@api.model
|
||||
def phonecenter_check_location(self, query):
|
||||
query = (query or '').strip()
|
||||
if not query:
|
||||
return {'found': False, 'message': 'Kein Ort angegeben.'}
|
||||
|
||||
cfg = self._phonecenter_get_settings()
|
||||
google_key = cfg.get('google_maps_key')
|
||||
|
||||
if google_key:
|
||||
try:
|
||||
response = requests.get(
|
||||
'https://maps.googleapis.com/maps/api/geocode/json',
|
||||
params={
|
||||
'address': query,
|
||||
'key': google_key,
|
||||
'language': 'de',
|
||||
},
|
||||
timeout=cfg.get('company_timeout', 20),
|
||||
)
|
||||
payload = response.json()
|
||||
if payload.get('status') == 'OK' and payload.get('results'):
|
||||
first = payload['results'][0]
|
||||
location = first.get('geometry', {}).get('location', {})
|
||||
return {
|
||||
'found': True,
|
||||
'provider': 'google_geocode',
|
||||
'query': query,
|
||||
'lat': location.get('lat'),
|
||||
'lng': location.get('lng'),
|
||||
'address': first.get('formatted_address') or query,
|
||||
}
|
||||
except Exception as ex:
|
||||
_logger.warning('Phonecenter location check via Google failed: %s', ex)
|
||||
|
||||
try:
|
||||
response = requests.get(
|
||||
'https://nominatim.openstreetmap.org/search',
|
||||
params={
|
||||
'q': query,
|
||||
'format': 'json',
|
||||
'limit': 1,
|
||||
'addressdetails': 1,
|
||||
},
|
||||
headers={
|
||||
'User-Agent': 'DigitalSignagePhoneCenter/1.0',
|
||||
},
|
||||
timeout=cfg.get('company_timeout', 20),
|
||||
)
|
||||
payload = response.json()
|
||||
if isinstance(payload, list) and payload:
|
||||
first = payload[0]
|
||||
return {
|
||||
'found': True,
|
||||
'provider': 'nominatim',
|
||||
'query': query,
|
||||
'lat': float(first.get('lat') or 0.0),
|
||||
'lng': float(first.get('lon') or 0.0),
|
||||
'address': first.get('display_name') or query,
|
||||
}
|
||||
except Exception as ex:
|
||||
_logger.warning('Phonecenter location check via Nominatim failed: %s', ex)
|
||||
|
||||
return {'found': False, 'query': query, 'message': 'Ort nicht gefunden.'}
|
||||
|
||||
@api.model
|
||||
def _phonecenter_normalize_company_item(self, item):
|
||||
if not isinstance(item, dict):
|
||||
return None
|
||||
name = (item.get('name') or '').strip()
|
||||
if not name:
|
||||
return None
|
||||
return {
|
||||
'id': str(item.get('id') or item.get('place_id') or item.get('external_id') or name),
|
||||
'place_id': (item.get('place_id') or '').strip(),
|
||||
'name': name,
|
||||
'address': (item.get('address') or item.get('formatted_address') or '').strip(),
|
||||
'city': (item.get('city') or '').strip(),
|
||||
'website': (item.get('website') or '').strip(),
|
||||
'phone': (item.get('phone') or '').strip(),
|
||||
'email': (item.get('email') or '').strip(),
|
||||
'rating': item.get('rating') or 0,
|
||||
'source': (item.get('source') or '').strip(),
|
||||
'google_maps_url': (item.get('google_maps_url') or '').strip(),
|
||||
'types': item.get('types') or [],
|
||||
'lat': item.get('lat'),
|
||||
'lng': item.get('lng'),
|
||||
'verified': bool(item.get('verified', False)),
|
||||
'verified_at': item.get('verified_at') or '',
|
||||
}
|
||||
|
||||
@api.model
|
||||
def _phonecenter_analyze_company(self, company, cfg):
|
||||
company = company or {}
|
||||
name = company.get('name') or ''
|
||||
website = company.get('website') or ''
|
||||
address = company.get('address') or ''
|
||||
phone = company.get('phone') or ''
|
||||
rating = company.get('rating') or ''
|
||||
|
||||
fallback = 'Firma: %s | Telefon: %s | Webseite: %s | Adresse: %s | Rating: %s' % (
|
||||
name or '-',
|
||||
phone or '-',
|
||||
website or '-',
|
||||
address or '-',
|
||||
rating or '-',
|
||||
)
|
||||
|
||||
ai_provider = (cfg.get('ai_provider') or 'openai').strip()
|
||||
if ai_provider not in ('openai', 'custom_api'):
|
||||
return fallback
|
||||
|
||||
api_key = cfg.get('ai_api_key') or ''
|
||||
api_url = (cfg.get('ai_api_url') or '').strip()
|
||||
if not api_url:
|
||||
if ai_provider == 'openai':
|
||||
api_url = 'https://api.openai.com/v1'
|
||||
else:
|
||||
return fallback
|
||||
if not api_key:
|
||||
return fallback
|
||||
|
||||
try:
|
||||
if ai_provider == 'openai':
|
||||
response = requests.post(
|
||||
'%s/chat/completions' % api_url.rstrip('/'),
|
||||
headers={
|
||||
'Authorization': 'Bearer %s' % api_key,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
json={
|
||||
'model': cfg.get('ai_model') or 'gpt-4o-mini',
|
||||
'temperature': 0.2,
|
||||
'messages': [
|
||||
{'role': 'system', 'content': 'Du analysierst Firmenkontakte fuer ein deutsches Callcenter. Antworte kurz in maximal 4 Saetzen.'},
|
||||
{'role': 'user', 'content': json.dumps({
|
||||
'task': 'Analysiere den Firmenkontakt fuer Erstansprache, nenne Nutzenargument, Risiko und naechsten Schritt.',
|
||||
'company': company,
|
||||
}, ensure_ascii=False)},
|
||||
],
|
||||
},
|
||||
timeout=cfg.get('company_timeout', 20),
|
||||
)
|
||||
payload = response.json() if response else {}
|
||||
content = (((payload.get('choices') or [{}])[0].get('message') or {}).get('content') or '').strip()
|
||||
return content or fallback
|
||||
|
||||
response = requests.post(
|
||||
api_url,
|
||||
headers={
|
||||
'Authorization': 'Bearer %s' % api_key,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
json={
|
||||
'task': 'company_analysis',
|
||||
'company': company,
|
||||
},
|
||||
timeout=cfg.get('company_timeout', 20),
|
||||
)
|
||||
payload = response.json() if response else {}
|
||||
if isinstance(payload, dict):
|
||||
return (payload.get('analysis') or payload.get('summary') or '').strip() or fallback
|
||||
return fallback
|
||||
except Exception as ex:
|
||||
_logger.warning('Phonecenter company analysis failed: %s', ex)
|
||||
return fallback
|
||||
|
||||
@api.model
|
||||
def phonecenter_review_bad_contact(self, payload):
|
||||
payload = payload or {}
|
||||
cfg = self._phonecenter_get_settings()
|
||||
|
||||
reason = (payload.get('reason') or '').strip()
|
||||
contact = payload.get('contact') or {}
|
||||
note = (payload.get('note') or '').strip()
|
||||
if not isinstance(contact, dict):
|
||||
contact = {}
|
||||
|
||||
if not reason:
|
||||
return {
|
||||
'ok': False,
|
||||
'message': 'Bitte Grund fuer die Einschaetzung angeben.',
|
||||
}
|
||||
|
||||
fallback_summary = (
|
||||
'Einschaetzung: %s. Kontakt: %s (%s). Telefon: %s. Nächster Schritt: Wiedervorlage oder weitere Recherche pruefen.'
|
||||
% (
|
||||
reason,
|
||||
contact.get('company') or '-',
|
||||
contact.get('city') or '-',
|
||||
contact.get('phone') or '-',
|
||||
)
|
||||
)
|
||||
fallback_actions = ['reschedule', 'research', 'keep', 'discard']
|
||||
|
||||
ai_provider = (cfg.get('ai_provider') or 'openai').strip()
|
||||
api_key = cfg.get('ai_api_key') or ''
|
||||
api_url = (cfg.get('ai_api_url') or '').strip()
|
||||
model = cfg.get('ai_model') or 'gpt-4o-mini'
|
||||
|
||||
if ai_provider == 'none' or not api_key:
|
||||
return {
|
||||
'ok': True,
|
||||
'summary': fallback_summary,
|
||||
'actions': fallback_actions,
|
||||
'source': 'fallback',
|
||||
}
|
||||
|
||||
if not api_url and ai_provider == 'openai':
|
||||
api_url = 'https://api.openai.com/v1'
|
||||
|
||||
try:
|
||||
if ai_provider == 'openai':
|
||||
response = requests.post(
|
||||
'%s/chat/completions' % api_url.rstrip('/'),
|
||||
headers={
|
||||
'Authorization': 'Bearer %s' % api_key,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
json={
|
||||
'model': model,
|
||||
'temperature': 0.2,
|
||||
'messages': [
|
||||
{
|
||||
'role': 'system',
|
||||
'content': (
|
||||
'Du analysierst schlechte Callcenter-Kontakte. '
|
||||
'Antworte nur als JSON mit keys "summary" und "actions". '
|
||||
'actions muss eine Teilmenge aus ["reschedule", "discard", "research", "keep"] sein.'
|
||||
),
|
||||
},
|
||||
{
|
||||
'role': 'user',
|
||||
'content': json.dumps({
|
||||
'reason': reason,
|
||||
'contact': contact,
|
||||
'classification_note': note,
|
||||
'task': 'Erzeuge eine kurze Zusammenfassung und passende naechste Schritte.',
|
||||
}, ensure_ascii=False),
|
||||
},
|
||||
],
|
||||
'response_format': {'type': 'json_object'},
|
||||
},
|
||||
timeout=cfg.get('company_timeout', 20),
|
||||
)
|
||||
data = response.json() if response else {}
|
||||
content = (((data.get('choices') or [{}])[0].get('message') or {}).get('content') or '').strip()
|
||||
parsed = json.loads(content) if content else {}
|
||||
actions = parsed.get('actions') if isinstance(parsed, dict) else []
|
||||
if not isinstance(actions, list):
|
||||
actions = []
|
||||
cleaned = [a for a in actions if a in fallback_actions]
|
||||
return {
|
||||
'ok': True,
|
||||
'summary': (parsed.get('summary') if isinstance(parsed, dict) else '') or fallback_summary,
|
||||
'actions': cleaned or fallback_actions,
|
||||
'source': 'llm',
|
||||
}
|
||||
|
||||
if ai_provider == 'custom_api' and api_url:
|
||||
response = requests.post(
|
||||
api_url,
|
||||
headers={
|
||||
'Authorization': 'Bearer %s' % api_key,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
json={
|
||||
'task': 'bad_contact_review',
|
||||
'reason': reason,
|
||||
'contact': contact,
|
||||
'classification_note': note,
|
||||
},
|
||||
timeout=cfg.get('company_timeout', 20),
|
||||
)
|
||||
data = response.json() if response else {}
|
||||
actions = data.get('actions', []) if isinstance(data, dict) else []
|
||||
if not isinstance(actions, list):
|
||||
actions = []
|
||||
cleaned = [a for a in actions if a in fallback_actions]
|
||||
return {
|
||||
'ok': True,
|
||||
'summary': (data.get('summary') if isinstance(data, dict) else '') or fallback_summary,
|
||||
'actions': cleaned or fallback_actions,
|
||||
'source': 'custom_api',
|
||||
}
|
||||
except Exception as ex:
|
||||
_logger.warning('Phonecenter bad contact review failed: %s', ex)
|
||||
|
||||
return {
|
||||
'ok': True,
|
||||
'summary': fallback_summary,
|
||||
'actions': fallback_actions,
|
||||
'source': 'fallback',
|
||||
}
|
||||
|
||||
@api.model
|
||||
def _phonecenter_dedupe_companies(self, items):
|
||||
deduped = []
|
||||
seen = set()
|
||||
for raw in items or []:
|
||||
item = self._phonecenter_normalize_company_item(raw)
|
||||
if not item:
|
||||
continue
|
||||
key = (item.get('name', '').lower().strip(), item.get('address', '').lower().strip())
|
||||
if key in seen:
|
||||
continue
|
||||
seen.add(key)
|
||||
deduped.append(item)
|
||||
return deduped
|
||||
|
||||
@api.model
|
||||
def _phonecenter_search_google_places(self, industry, lat, lng, radius_km, cfg):
|
||||
key = cfg.get('company_api_key') or cfg.get('google_maps_key')
|
||||
if not key:
|
||||
return []
|
||||
|
||||
query = (industry or '').strip() or 'unternehmen'
|
||||
radius_m = max(1000, int((radius_km or 10) * 1000))
|
||||
params = {
|
||||
'query': query,
|
||||
'location': f'{lat},{lng}',
|
||||
'radius': radius_m,
|
||||
'key': key,
|
||||
'language': 'de',
|
||||
}
|
||||
items = []
|
||||
|
||||
try:
|
||||
response = requests.get(
|
||||
'https://maps.googleapis.com/maps/api/place/textsearch/json',
|
||||
params=params,
|
||||
timeout=cfg.get('company_timeout', 20),
|
||||
)
|
||||
payload = response.json()
|
||||
for row in payload.get('results', [])[:cfg.get('max_results', 30)]:
|
||||
location = row.get('geometry', {}).get('location', {})
|
||||
items.append({
|
||||
'id': row.get('place_id'),
|
||||
'place_id': row.get('place_id'),
|
||||
'name': row.get('name'),
|
||||
'address': row.get('formatted_address') or row.get('vicinity') or '',
|
||||
'rating': row.get('rating') or 0,
|
||||
'city': '',
|
||||
'source': 'google_places',
|
||||
'lat': location.get('lat'),
|
||||
'lng': location.get('lng'),
|
||||
})
|
||||
except Exception as ex:
|
||||
_logger.warning('Phonecenter Google Places search failed: %s', ex)
|
||||
|
||||
return items
|
||||
|
||||
@api.model
|
||||
def _phonecenter_search_custom_company_api(self, industry, lat, lng, radius_km, cfg):
|
||||
api_url = (cfg.get('company_api_url') or '').strip()
|
||||
if not api_url:
|
||||
return []
|
||||
|
||||
headers = {'Content-Type': 'application/json'}
|
||||
if cfg.get('company_api_key'):
|
||||
headers['Authorization'] = 'Bearer %s' % cfg.get('company_api_key')
|
||||
|
||||
body = {
|
||||
'industry': industry,
|
||||
'lat': lat,
|
||||
'lng': lng,
|
||||
'radius_km': radius_km,
|
||||
'limit': cfg.get('max_results', 30),
|
||||
}
|
||||
|
||||
try:
|
||||
response = requests.post(api_url, headers=headers, json=body, timeout=cfg.get('company_timeout', 20))
|
||||
payload = response.json()
|
||||
if isinstance(payload, dict):
|
||||
return payload.get('companies', []) or []
|
||||
except Exception as ex:
|
||||
_logger.warning('Phonecenter custom company API search failed: %s', ex)
|
||||
return []
|
||||
|
||||
@api.model
|
||||
def _phonecenter_enrich_companies_openai(self, companies, industry, location_query, cfg):
|
||||
api_url = (cfg.get('ai_api_url') or '').rstrip('/')
|
||||
api_key = cfg.get('ai_api_key') or ''
|
||||
model = cfg.get('ai_model') or 'gpt-4o-mini'
|
||||
if not api_url or not api_key:
|
||||
return companies
|
||||
|
||||
prompt = {
|
||||
'industry': industry,
|
||||
'location': location_query,
|
||||
'companies': companies,
|
||||
'task': 'Normalisiere, entferne Dubletten und bewerte Relevanz fuer Branchen- und Ortsbezug. Antworte als JSON-Objekt mit key "companies".',
|
||||
}
|
||||
|
||||
try:
|
||||
response = requests.post(
|
||||
'%s/chat/completions' % api_url,
|
||||
headers={
|
||||
'Authorization': 'Bearer %s' % api_key,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
json={
|
||||
'model': model,
|
||||
'temperature': 0.1,
|
||||
'messages': [
|
||||
{'role': 'system', 'content': 'Du bist ein Daten-Normalisierer fuer Firmenlisten. Gib nur gueltiges JSON zurueck.'},
|
||||
{'role': 'user', 'content': json.dumps(prompt, ensure_ascii=False)},
|
||||
],
|
||||
'response_format': {'type': 'json_object'},
|
||||
},
|
||||
timeout=cfg.get('company_timeout', 20),
|
||||
)
|
||||
data = response.json()
|
||||
content = (((data.get('choices') or [{}])[0].get('message') or {}).get('content') or '').strip()
|
||||
parsed = json.loads(content) if content else {}
|
||||
enriched = parsed.get('companies', []) if isinstance(parsed, dict) else []
|
||||
return self._phonecenter_dedupe_companies(enriched) or companies
|
||||
except Exception as ex:
|
||||
_logger.warning('Phonecenter OpenAI enrichment failed: %s', ex)
|
||||
return companies
|
||||
|
||||
@api.model
|
||||
def _phonecenter_enrich_companies_custom(self, companies, industry, location_query, cfg):
|
||||
api_url = (cfg.get('ai_api_url') or '').strip()
|
||||
if not api_url:
|
||||
return companies
|
||||
|
||||
headers = {'Content-Type': 'application/json'}
|
||||
if cfg.get('ai_api_key'):
|
||||
headers['Authorization'] = 'Bearer %s' % cfg.get('ai_api_key')
|
||||
|
||||
body = {
|
||||
'industry': industry,
|
||||
'location': location_query,
|
||||
'companies': companies,
|
||||
}
|
||||
|
||||
try:
|
||||
response = requests.post(api_url, headers=headers, json=body, timeout=cfg.get('company_timeout', 20))
|
||||
payload = response.json()
|
||||
if isinstance(payload, dict):
|
||||
enriched = payload.get('companies', []) or []
|
||||
normalized = self._phonecenter_dedupe_companies(enriched)
|
||||
return normalized or companies
|
||||
except Exception as ex:
|
||||
_logger.warning('Phonecenter custom AI enrichment failed: %s', ex)
|
||||
return companies
|
||||
|
||||
@api.model
|
||||
def _phonecenter_get_google_place_details(self, place_id, cfg):
|
||||
key = cfg.get('company_api_key') or cfg.get('google_maps_key')
|
||||
if not key or not place_id:
|
||||
return {}
|
||||
|
||||
detail_fields = ','.join([
|
||||
'name',
|
||||
'formatted_address',
|
||||
'formatted_phone_number',
|
||||
'international_phone_number',
|
||||
'website',
|
||||
'rating',
|
||||
'user_ratings_total',
|
||||
'business_status',
|
||||
'types',
|
||||
'url',
|
||||
'geometry',
|
||||
])
|
||||
try:
|
||||
response = requests.get(
|
||||
'https://maps.googleapis.com/maps/api/place/details/json',
|
||||
params={
|
||||
'place_id': place_id,
|
||||
'fields': detail_fields,
|
||||
'key': key,
|
||||
'language': 'de',
|
||||
},
|
||||
timeout=cfg.get('company_timeout', 20),
|
||||
)
|
||||
payload = response.json()
|
||||
if payload.get('status') != 'OK':
|
||||
return {}
|
||||
details = payload.get('result') or {}
|
||||
return {
|
||||
'id': place_id,
|
||||
'place_id': place_id,
|
||||
'name': details.get('name') or '',
|
||||
'address': details.get('formatted_address') or '',
|
||||
'phone': details.get('formatted_phone_number') or details.get('international_phone_number') or '',
|
||||
'website': details.get('website') or '',
|
||||
'email': '',
|
||||
'rating': details.get('rating') or 0,
|
||||
'source': 'google_places',
|
||||
'google_maps_url': details.get('url') or '',
|
||||
'types': details.get('types') or [],
|
||||
'verified': True,
|
||||
'verified_at': fields.Datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'business_status': details.get('business_status') or '',
|
||||
'lat': (details.get('geometry') or {}).get('location', {}).get('lat'),
|
||||
'lng': (details.get('geometry') or {}).get('location', {}).get('lng'),
|
||||
}
|
||||
except Exception as ex:
|
||||
_logger.warning('Phonecenter Google Place details failed: %s', ex)
|
||||
return {}
|
||||
|
||||
@api.model
|
||||
def phonecenter_get_company_details(self, payload):
|
||||
payload = payload or {}
|
||||
cfg = self._phonecenter_get_settings()
|
||||
company = payload.get('company') or {}
|
||||
if not isinstance(company, dict):
|
||||
company = {}
|
||||
|
||||
provider = (company.get('source') or cfg.get('company_provider') or 'google_places').strip()
|
||||
place_id = (company.get('place_id') or company.get('id') or '').strip()
|
||||
|
||||
details = {}
|
||||
if provider == 'google_places' and place_id:
|
||||
details = self._phonecenter_get_google_place_details(place_id, cfg)
|
||||
|
||||
if not details:
|
||||
details = self._phonecenter_normalize_company_item(company) or {}
|
||||
if details:
|
||||
details['verified'] = bool(details.get('verified', False))
|
||||
details['verified_at'] = details.get('verified_at') or ''
|
||||
|
||||
if not details:
|
||||
return {
|
||||
'ok': False,
|
||||
'message': 'Keine Unternehmensdetails gefunden.',
|
||||
}
|
||||
|
||||
details.setdefault('source', provider)
|
||||
details['analysis'] = self._phonecenter_analyze_company(details, cfg)
|
||||
return {
|
||||
'ok': True,
|
||||
'company': details,
|
||||
}
|
||||
|
||||
@api.model
|
||||
def phonecenter_search_companies(self, payload):
|
||||
payload = payload or {}
|
||||
cfg = self._phonecenter_get_settings()
|
||||
|
||||
current_options = payload.get('current_options') or []
|
||||
current_list = self._phonecenter_dedupe_companies(current_options)
|
||||
|
||||
min_entries = cfg.get('min_entries', 8)
|
||||
if current_list and len(current_list) < min_entries:
|
||||
return {
|
||||
'companies': current_list,
|
||||
'added_count': 0,
|
||||
'search_blocked': True,
|
||||
'min_entries': min_entries,
|
||||
'message': 'Naechste Suche erst ab Mindestanzahl moeglich.',
|
||||
}
|
||||
|
||||
industry = (payload.get('industry') or '').strip()
|
||||
location_query = (payload.get('location_query') or '').strip()
|
||||
radius_km = int(payload.get('radius_km') or 20)
|
||||
|
||||
lat = payload.get('lat')
|
||||
lng = payload.get('lng')
|
||||
if (lat is None or lng is None) and location_query:
|
||||
location_info = self.phonecenter_check_location(location_query)
|
||||
lat = location_info.get('lat')
|
||||
lng = location_info.get('lng')
|
||||
|
||||
if lat is None or lng is None:
|
||||
return {
|
||||
'companies': current_list,
|
||||
'added_count': 0,
|
||||
'search_blocked': bool(current_list and len(current_list) < min_entries),
|
||||
'min_entries': min_entries,
|
||||
'message': 'Kein gueltiger Ort fuer die Suche vorhanden.',
|
||||
}
|
||||
|
||||
provider = cfg.get('company_provider', 'google_places')
|
||||
found = []
|
||||
if provider == 'google_places':
|
||||
found = self._phonecenter_search_google_places(industry, lat, lng, radius_km, cfg)
|
||||
elif provider == 'custom_api':
|
||||
found = self._phonecenter_search_custom_company_api(industry, lat, lng, radius_km, cfg)
|
||||
|
||||
if not found:
|
||||
found = [{
|
||||
'id': 'fallback-%s-1' % (industry or 'unternehmen'),
|
||||
'name': '%s %s 1' % (industry or 'Unternehmen', location_query or ''),
|
||||
'city': location_query,
|
||||
'source': 'mock',
|
||||
}]
|
||||
|
||||
normalized_new = self._phonecenter_dedupe_companies(found)
|
||||
ai_provider = cfg.get('ai_provider', 'openai')
|
||||
if ai_provider == 'openai':
|
||||
normalized_new = self._phonecenter_enrich_companies_openai(normalized_new, industry, location_query, cfg)
|
||||
elif ai_provider == 'custom_api':
|
||||
normalized_new = self._phonecenter_enrich_companies_custom(normalized_new, industry, location_query, cfg)
|
||||
|
||||
combined = self._phonecenter_dedupe_companies(current_list + normalized_new)
|
||||
combined = combined[:cfg.get('max_results', 30)]
|
||||
|
||||
return {
|
||||
'companies': combined,
|
||||
'added_count': max(0, len(combined) - len(current_list)),
|
||||
'search_blocked': bool(combined and len(combined) < min_entries),
|
||||
'min_entries': min_entries,
|
||||
'radius_km': radius_km,
|
||||
'provider': provider,
|
||||
'ai_provider': ai_provider,
|
||||
}
|
||||
|
||||
|
||||
class dssSettingsSave(models.Model):
|
||||
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ class dsssystemtypen(models.Model):
|
|||
showonlinestate_type = fields.Selection([('RPORT','RPort Status'),('VNNOX','Vnnox System')])
|
||||
inputform_type = fields.Char('Eingabeformular Typ', tracking=True)
|
||||
web_input_template = fields.Char('Web-Eingabe Template', tracking=True)
|
||||
web_input_showimage_url = fields.Char('Web-Eingabe Bild URL', tracking=True)
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ TUYA_LOGGER.setLevel(logging.ERROR)
|
|||
|
||||
logging.setLoggerClass(OdooCustomLogger)
|
||||
_logger = logging.getLogger(__name__)
|
||||
#_logger.setloging(False)
|
||||
_logger.setloging(False)
|
||||
##_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
|
@ -291,7 +291,7 @@ class trigger(models.Model):
|
|||
if mailcc:
|
||||
_logger.info('Trigger Aktion TRA_' + str(self)+' Sending Email - vor CC Add deep')
|
||||
VsPartner = self.env['res.partner'].search([('id','=',Dataset.contract_writer.id)])
|
||||
ccmailto = ccmailto+';'+VsPartner.email
|
||||
ccmailto = ccmailto+', '+VsPartner.email
|
||||
_logger.info('Trigger Aktion TRA_' + str(self)+' Sending Email CC Writer - FIX Empfänger : '+str(VsPartner)+' -> '+str(ccmailto)+' + '+str(VsPartner.email))
|
||||
_logger.info('Trigger Aktion TRA_' + str(self)+' Sending Email - Template : '+str(email_template)+" Empfänger :"+str(mailto)+'/'+str(ccmailto)+' -> '+str(Dataset))
|
||||
email_template.email_to = mailto
|
||||
|
|
@ -327,7 +327,7 @@ class trigger(models.Model):
|
|||
return True
|
||||
|
||||
|
||||
|
||||
|
||||
class dsstriggerconditions(models.Model):
|
||||
_name = "dss.triggerconditions"
|
||||
_description = "DigitalSignage Trigger Bedingungen"
|
||||
|
|
@ -752,6 +752,7 @@ class dsstriggertypes(models.Model):
|
|||
trigger_block_config = fields.Char('Blockly Design',tracking=True)
|
||||
trigger_block_raw_config = fields.Char('Blockly command Design',tracking=True)
|
||||
trigger_block_command_uptodate = fields.Boolean('Blockly command aktuell',tracking=True)
|
||||
trigger_auto_code_create = fields.Boolean('Autom. Cod erstellen',tracking=True, default=False)
|
||||
trigger_block_command = fields.Char('Blockly command',tracking=True)
|
||||
|
||||
@api.model
|
||||
|
|
@ -860,11 +861,11 @@ class dsstriggertypes(models.Model):
|
|||
@api.model
|
||||
def NewBlockTrigger(self,triggername,triggeraktiv):
|
||||
_logger.info("Trigger New Block Trigger !"+str(triggername))
|
||||
newtrigger = self.env['dss.triggertypes'].create({'triggername':triggername,'trigger_is_block_config':True,'trigger_active':triggeraktiv})
|
||||
newtrigger = self.env['dss.triggertypes'].create({'triggername':triggername,'trigger_is_block_config':True,'trigger_active':triggeraktiv,'trigger_auto_code_create':False})
|
||||
return newtrigger
|
||||
|
||||
@api.model
|
||||
def UpdateBlockTrigger(self,triggername,blockcode,blockcommand,blockcommand_uptodate,triggertable,triggeraktiv ):
|
||||
def UpdateBlockTrigger(self,triggername,blockcode,blockcommand,blockcommand_uptodate,triggertable,triggeraktiv,triggerautocode=False):
|
||||
_logger.info("Trigger Update Block Trigger !"+str(triggername))
|
||||
foundtrigger = self.env['dss.triggertypes'].search([('triggername','=',triggername)])
|
||||
if foundtrigger:
|
||||
|
|
@ -875,6 +876,7 @@ class dsstriggertypes(models.Model):
|
|||
foundtrigger.trigger_block_command_uptodate=blockcommand_uptodate
|
||||
foundtrigger.trigger_block_command=blockcommand
|
||||
foundtrigger.trigger_table = triggertable_
|
||||
foundtrigger.trigger_auto_code_create = bool(triggerautocode)
|
||||
return foundtrigger
|
||||
|
||||
@api.model
|
||||
|
|
@ -939,7 +941,10 @@ class dsstriggeractions(models.Model):
|
|||
email_has_attachments = fields.Boolean('Email mit Anhang',tracking=True,help='Soll die Email mit Anhang versendet werden ?')
|
||||
email_contract_attachment_types = fields.Many2many('dss.mediatypes',tracking=True,help='Welche Dateitypen aus dem Vertrag sollen als Anhang versendet werden ?')
|
||||
|
||||
|
||||
activity_user_fix = fields.Boolean('Empfänger der Aufgabe ist fix',tracking=True)
|
||||
activity_user = fields.Many2one('res.users','Empfänger der Aufgabe',tracking=True)
|
||||
activity_user_field = fields.Many2one('ir.model.fields', string='Empfänger der Aufgabe',tracking=True)
|
||||
activity_type = fields.Many2one('mail.activity.type','Art der Aufgabe',tracking=True)
|
||||
activity_date_type = fields.Selection([('TOPLU','Von heute in x Tagen'),('TOFIX','An einem festen Datum'),('TOFIXDB','An einem festen Datum der Datenbank'),('TOPLUDB','An einem festen Datum der Datenbank + x Tage')],tracking=True)
|
||||
activity_date_type_add_days = fields.Integer('x Tage Aufschlag',tracking=True)
|
||||
|
|
@ -1010,7 +1015,7 @@ class dsstriggeractions(models.Model):
|
|||
if self.email_getfrom == "FIX":
|
||||
mailto = ""
|
||||
for partner in self.email_fix_email:
|
||||
mailto = mailto+';'+partner.email
|
||||
mailto = mailto+','+partner.email
|
||||
_logger.info('Trigger Aktion TRA_' + str(self)+' Sending Email - FIX Empfänger : '+str(self.email_getfrom)+' -> '+str(mailto))
|
||||
elif self.email_getfrom == "DATA":
|
||||
mailto = str(Dataset[self.email_data_field.name])
|
||||
|
|
@ -1090,8 +1095,11 @@ class dsstriggeractions(models.Model):
|
|||
elif self.activity_date_type == 'TOFIXDB':
|
||||
deadline = Dataset[self.action_field.name]
|
||||
elif self.activity_date_type == 'TOPLUDB':
|
||||
deadline = Dataset[self.action_field.name]
|
||||
deadline = deadline + relativedelta(days=self.activity_date_type_add_days)
|
||||
base_deadline = Dataset[self.action_field.name]
|
||||
if not base_deadline:
|
||||
# Fallback avoids bool + relativedelta crash when source field is empty.
|
||||
base_deadline = datetime.now()
|
||||
deadline = base_deadline + relativedelta(days=self.activity_date_type_add_days)
|
||||
|
||||
if not Dataset:
|
||||
_logger.info('Trigger Aktion TRA_' + str(self)+' Create Task - No Dataset - use Test '+str(self.trigger_table)+' '+str(self.action_test_id))
|
||||
|
|
@ -1102,11 +1110,26 @@ class dsstriggeractions(models.Model):
|
|||
_logger.info('Trigger Aktion TRA_' + str(self)+' Create Task - No Model '+str(ds)+' '+str(self.trigger_table))
|
||||
else:
|
||||
_logger.info('Trigger Aktion TRA_' + str(self)+' Create Task '+str(self.Info_Subject)+' ('+str(self.activity_user)+') - Model : '+str(self.trigger_table.id)+'/'+str(ds.id)+' '+str(deadline))
|
||||
if self.activity_user_fix:
|
||||
_logger.info('Trigger Aktion TRA_' + str(self)+' Create Task '+str(self.Info_Subject)+' - User FIX ('+str(self.activity_user)+')')
|
||||
userid = self.activity_user.id
|
||||
else:
|
||||
_logger.info('Trigger Aktion TRA_' + str(self)+' Create Task '+str(self.Info_Subject)+' - User current ('+str(self.env.user.name)+') from field '+str(self.activity_user_field.name)+' -> '+str(Dataset[self.activity_user_field.name]))
|
||||
partner = self.env['res.partner'].search([('id','=',Dataset[self.activity_user_field.name].id)],limit=1)
|
||||
_logger.info('Trigger Aktion TRA_' + str(self)+' Create Task '+str(self.Info_Subject)+' - User current ('+str(self.env.user.name)+') from field '+str(self.activity_user_field.name)+' -> '+str(Dataset[self.activity_user_field.name])+' -> Partner : '+str(partner))
|
||||
if partner.user_ids:
|
||||
if len(partner.user_ids) > 1:
|
||||
userid = partner.user_ids[0].id
|
||||
else:
|
||||
userid = partner.user_ids.id
|
||||
else:
|
||||
userid = self.env.user.id
|
||||
_logger.info('Trigger Aktion TRA_' + str(self)+' Create Task '+str(self.Info_Subject)+' - User current ('+str(self.env.user.name)+') from field '+str(self.activity_user_field.name)+' -> '+str(Dataset[self.activity_user_field.name])+' -> Partner : '+str(partner)+' -> User : '+str(userid))
|
||||
self.env['mail.activity'].create({
|
||||
'display_name': self.Info_Subject,
|
||||
'summary': self.activity_text,
|
||||
'date_deadline': deadline,
|
||||
'user_id': self.activity_user.id,
|
||||
'user_id': userid,
|
||||
'res_id': ds.id,
|
||||
'res_model_id': self.trigger_table.id,
|
||||
'activity_type_id': self.activity_type.id
|
||||
|
|
@ -1167,11 +1190,11 @@ class dsstriggermodel(models.Model):
|
|||
return obj
|
||||
|
||||
@api.model
|
||||
def trigger_track_template(self, changes, Dataset):
|
||||
def trigger_track_template(self, changes, Dataset):
|
||||
_logger.info("Trigger in track_template - "+str(Dataset)+" - Changes : "+str(changes))
|
||||
hook = self.env['dss.settings.vars'].search([('varname','=','N8NWebhook-OnChange')])
|
||||
if hook:
|
||||
if hook.varvalue:
|
||||
if (hook):
|
||||
if not (hook.varvalue == "FALSE"):
|
||||
_logger.info("Trigger in track_template - N8NWebhook-OnChange - "+str(Dataset.triggergroup)+" - Changes : "+str(changes))
|
||||
# Call N8N Webhook
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -4,6 +4,11 @@
|
|||
<field name="sequence">15</field>
|
||||
</record>
|
||||
|
||||
<record id="dss_module_phonecenter" model="ir.module.category">
|
||||
<field name="name">Telefoniecenter</field>
|
||||
<field name="sequence">16</field>
|
||||
</record>
|
||||
|
||||
<record id="dssgroup_user" model="res.groups">
|
||||
<field name="name">Standarduser</field>
|
||||
<field name="category_id" ref="DigitalSignage.dss_module"/>
|
||||
|
|
@ -35,4 +40,34 @@
|
|||
<field name="implied_ids" eval="[(4, ref('base.group_user'))]"/>
|
||||
</record>
|
||||
|
||||
<record id="group_phonecenter_user" model="res.groups">
|
||||
<field name="name">Grundrecht Telefoniecenter</field>
|
||||
<field name="category_id" ref="DigitalSignage.dss_module_phonecenter"/>
|
||||
<field name="implied_ids" eval="[(4, ref('base.group_user'))]"/>
|
||||
</record>
|
||||
|
||||
<record id="group_phonecenter_mode_callcenter" model="res.groups">
|
||||
<field name="name">Ansichtsmodus: Callcenter</field>
|
||||
<field name="category_id" ref="DigitalSignage.dss_module_phonecenter"/>
|
||||
<field name="implied_ids" eval="[(4, ref('DigitalSignage.group_phonecenter_user'))]"/>
|
||||
</record>
|
||||
|
||||
<record id="group_phonecenter_mode_projectmanagement" model="res.groups">
|
||||
<field name="name">Ansichtsmodus: Projektmanagement</field>
|
||||
<field name="category_id" ref="DigitalSignage.dss_module_phonecenter"/>
|
||||
<field name="implied_ids" eval="[(4, ref('DigitalSignage.group_phonecenter_user'))]"/>
|
||||
</record>
|
||||
|
||||
<record id="group_phonecenter_mode_aussendienst" model="res.groups">
|
||||
<field name="name">Ansichtsmodus: Aussendienst</field>
|
||||
<field name="category_id" ref="DigitalSignage.dss_module_phonecenter"/>
|
||||
<field name="implied_ids" eval="[(4, ref('DigitalSignage.group_phonecenter_user'))]"/>
|
||||
</record>
|
||||
|
||||
<record id="group_phonecenter_mode_administrator" model="res.groups">
|
||||
<field name="name">Ansichtsmodus: Administrator</field>
|
||||
<field name="category_id" ref="DigitalSignage.dss_module_phonecenter"/>
|
||||
<field name="implied_ids" eval="[(4, ref('DigitalSignage.group_phonecenter_user'))]"/>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
|
@ -33,6 +33,7 @@ digitalsignage_dss_triggergroups_group_user,access.dss.triggergroups,model_dss_t
|
|||
digitalsignage_dss_maintaintask_group_user,access.dss.maintaintask,model_dss_maintaintask,base.group_user,1,1,1,1
|
||||
digitalsignage_dss_provision_group_user,access.dss.provision,model_dss_provision,base.group_user,1,1,1,1
|
||||
digitalsignage_dss_provisionstypen_group_user,access.dss.provisionstypen,model_dss_provisionstypen,DigitalSignage.group_prov_user,1,1,1,1
|
||||
digitalsignage_dss_company_type_group_user,access.dss.company.type,model_dss_company_type,base.group_user,1,1,1,1
|
||||
digitalsignage_dss_payintervals_group_user,access.dss.payintervals,model_dss_payintervals,base.group_user,1,1,1,1
|
||||
digitalsignage_dss_importinvoicelist_group_user,access.dss.importinvoicelist,model_dss_importinvoicelist,base.group_user,1,1,1,1
|
||||
digitalsignage_dss_web_contracts_group_user,access.dss_web_contracts,model_dss_web_contracts,base.group_user,1,1,1,1
|
||||
|
|
@ -53,8 +54,17 @@ digitalsignage_dss_triggeractions_groups_group_user,access.dss.triggeractions.gr
|
|||
digitalsignage_dss_screendesign_group_user,access.dss.screendesign,model_dss_screendesign,base.group_user,1,1,1,1
|
||||
digitalsignage_dss_binaries_group_user,access.dss.binaries,model_dss_binaries,base.group_user,1,1,1,1
|
||||
digitalsignage_dss_binaries_wizard_new_group_user,access.dss.binaries.wizard.new,model_dss_binaries_wizard_new,base.group_user,1,1,1,1
|
||||
digitalsignage_dss_advertisefields_create_from_template_wizard_group_user,access.dss.advertisefields.create.from.template.wizard,model_dss_advertisefields_create_from_template_wizard,base.group_user,1,1,1,1
|
||||
digitalsignage_dss_advertisefields_create_from_project_wizard_group_user,access.dss.advertisefields.create.from.project.wizard,model_dss_advertisefields_create_from_project_wizard,base.group_user,1,1,1,1
|
||||
digitalsignage_dss_advertisefields_save_as_template_wizard_group_user,access.dss.advertisefields.save.as.template.wizard,model_dss_advertisefields_save_as_template_wizard,base.group_user,1,1,1,1
|
||||
digitalsignage_dss_linkmail_group_user,access.dss.linkmail,model_dss_linkmail,base.group_user,1,1,1,1
|
||||
digitalsignage_dss_kileads_requests,access.dss.kileads.requests,model_dss_kileads_requests,base.group_user,1,1,1,1
|
||||
digitalsignage_dss_kileads,access.dss.kileads,model_dss_kileads,base.group_user,1,1,1,1
|
||||
digitalsignage_dss_contracts_uservars_group_user,access.dss.contracts_uservars,model_dss_contracts_uservars,base.group_user,1,1,1,1
|
||||
digitalsignage_dss_settings_vars_group_user,access.dss.settings.vars,model_dss_settings_vars,base.group_user,1,1,1,1
|
||||
digitalsignage_dss_settings_vars_group_user,access.dss.settings.vars,model_dss_settings_vars,base.group_user,1,1,1,1
|
||||
digitalsignage_dss_csvs_group_user,access.dss.csvs,model_dss_csvs,base.group_user,1,1,1,1
|
||||
digitalsignage_dss_ai_chat_history_group_user,access.dss.ai.chat.history,model_dss_ai_chat_history,base.group_user,1,1,1,1
|
||||
digitalsignage_dss_portal_user_profile_group_system,access.dss.portal.user.profile,model_dss_portal_user_profile,base.group_system,1,1,1,1
|
||||
digitalsignage_dss_phonecenter_query_group_user,access.dss.phonecenter.query,model_dss_phonecenter_query,base.group_user,1,1,1,1
|
||||
digitalsignage_dss_phonecenter_query_line_group_user,access.dss.phonecenter.query.line,model_dss_phonecenter_query_line,base.group_user,1,1,1,1
|
||||
digitalsignage_dss_contracts_move_field_wizard_group_user,access.dss.contracts.move.field.wizard,model_dss_contracts_move_field_wizard,base.group_user,1,1,1,1
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
<!-- ...existing code... -->
|
||||
<div style="display: flex; flex-direction: column; gap: 12px; align-items: flex-start;">
|
||||
<label style="display: flex; align-items: center; gap: 8px; width: 100%; justify-content: flex-start;">
|
||||
<span>Keine besondere Auswahl</span>
|
||||
<input type="checkbox" id="web_affinitaet_1" name="web_affinitaet_1" t-att-value="web_affinitaet_1" data-fill-with="undefined"/>
|
||||
</label>
|
||||
<label style="display: flex; align-items: center; gap: 8px; width: 100%; justify-content: flex-start;">
|
||||
<span>News + Finanzen</span>
|
||||
<input type="checkbox" id="web_affinitaet_2" name="web_affinitaet_2" t-att-value="web_affinitaet_2" data-fill-with="undefined"/>
|
||||
</label>
|
||||
<label style="display: flex; align-items: center; gap: 8px; width: 100%; justify-content: flex-start;">
|
||||
<span>Auto + Mobiles</span>
|
||||
<input type="checkbox" id="web_affinitaet_3" name="web_affinitaet_3" t-att-value="web_affinitaet_3" data-fill-with="undefined"/>
|
||||
</label>
|
||||
<label style="display: flex; align-items: center; gap: 8px; width: 100%; justify-content: flex-start;">
|
||||
<span>Beauty + Fashion</span>
|
||||
<input type="checkbox" id="web_affinitaet_4" name="web_affinitaet_4" t-att-value="web_affinitaet_4" data-fill-with="undefined"/>
|
||||
</label>
|
||||
<label style="display: flex; align-items: center; gap: 8px; width: 100%; justify-content: flex-start;">
|
||||
<span>Food</span>
|
||||
<input type="checkbox" id="web_affinitaet_5" name="web_affinitaet_5" t-att-value="web_affinitaet_5" data-fill-with="undefined"/>
|
||||
</label>
|
||||
<label style="display: flex; align-items: center; gap: 8px; width: 100%; justify-content: flex-start;">
|
||||
<span>Gesundheit</span>
|
||||
<input type="checkbox" id="web_affinitaet_6" name="web_affinitaet_6" t-att-value="web_affinitaet_6" data-fill-with="undefined"/>
|
||||
</label>
|
||||
<label style="display: flex; align-items: center; gap: 8px; width: 100%; justify-content: flex-start;">
|
||||
<span>Lifestyle</span>
|
||||
<input type="checkbox" id="web_affinitaet_7" name="web_affinitaet_7" t-att-value="web_affinitaet_7" data-fill-with="undefined"/>
|
||||
</label>
|
||||
<label style="display: flex; align-items: center; gap: 8px; width: 100%; justify-content: flex-start;">
|
||||
<span>Living</span>
|
||||
<input type="checkbox" id="web_affinitaet_8" name="web_affinitaet_8" t-att-value="web_affinitaet_8" data-fill-with="undefined"/>
|
||||
</label>
|
||||
<label style="display: flex; align-items: center; gap: 8px; width: 100%; justify-content: flex-start;">
|
||||
<span>Entertainment</span>
|
||||
<input type="checkbox" id="web_affinitaet_9" name="web_affinitaet_9" t-att-value="web_affinitaet_9" data-fill-with="undefined"/>
|
||||
</label>
|
||||
<label style="display: flex; align-items: center; gap: 8px; width: 100%; justify-content: flex-start;">
|
||||
<span>Reise</span>
|
||||
<input type="checkbox" id="web_affinitaet_10" name="web_affinitaet_10" t-att-value="web_affinitaet_10" data-fill-with="undefined"/>
|
||||
</label>
|
||||
<label style="display: flex; align-items: center; gap: 8px; width: 100%; justify-content: flex-start;">
|
||||
<span>Sport</span>
|
||||
<input type="checkbox" id="web_affinitaet_11" name="web_affinitaet_11" t-att-value="web_affinitaet_11" data-fill-with="undefined"/>
|
||||
</label>
|
||||
<label style="display: flex; align-items: center; gap: 8px; width: 100%; justify-content: flex-start;">
|
||||
<span>Musik</span>
|
||||
<input type="checkbox" id="web_affinitaet_12" name="web_affinitaet_12" t-att-value="web_affinitaet_12" data-fill-with="undefined"/>
|
||||
</label>
|
||||
</div>
|
||||
<!-- ...existing code... -->
|
||||
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 603 KiB After Width: | Height: | Size: 603 KiB |
|
Before Width: | Height: | Size: 440 KiB After Width: | Height: | Size: 440 KiB |
|
Before Width: | Height: | Size: 832 KiB After Width: | Height: | Size: 832 KiB |
|
Before Width: | Height: | Size: 481 KiB After Width: | Height: | Size: 481 KiB |
|
Before Width: | Height: | Size: 174 KiB After Width: | Height: | Size: 174 KiB |
|
Before Width: | Height: | Size: 524 KiB After Width: | Height: | Size: 524 KiB |
|
Before Width: | Height: | Size: 778 KiB After Width: | Height: | Size: 778 KiB |
|
Before Width: | Height: | Size: 593 KiB After Width: | Height: | Size: 593 KiB |
|
Before Width: | Height: | Size: 205 KiB After Width: | Height: | Size: 205 KiB |
|
Before Width: | Height: | Size: 205 KiB After Width: | Height: | Size: 205 KiB |
|
Before Width: | Height: | Size: 239 KiB After Width: | Height: | Size: 239 KiB |
|
Before Width: | Height: | Size: 658 KiB After Width: | Height: | Size: 658 KiB |
|
Before Width: | Height: | Size: 423 KiB After Width: | Height: | Size: 423 KiB |
|
Before Width: | Height: | Size: 440 KiB After Width: | Height: | Size: 440 KiB |
|
Before Width: | Height: | Size: 687 KiB After Width: | Height: | Size: 687 KiB |
|
Before Width: | Height: | Size: 210 KiB After Width: | Height: | Size: 210 KiB |
|
Before Width: | Height: | Size: 797 KiB After Width: | Height: | Size: 797 KiB |
|
Before Width: | Height: | Size: 876 KiB After Width: | Height: | Size: 876 KiB |
|
Before Width: | Height: | Size: 1.0 MiB After Width: | Height: | Size: 1.0 MiB |
|
Before Width: | Height: | Size: 409 KiB After Width: | Height: | Size: 409 KiB |
|
Before Width: | Height: | Size: 498 KiB After Width: | Height: | Size: 498 KiB |
|
Before Width: | Height: | Size: 2.4 MiB After Width: | Height: | Size: 2.4 MiB |
|
Before Width: | Height: | Size: 191 KiB After Width: | Height: | Size: 191 KiB |
|
Before Width: | Height: | Size: 208 KiB After Width: | Height: | Size: 208 KiB |
|
Before Width: | Height: | Size: 205 KiB After Width: | Height: | Size: 205 KiB |