[MOV] Odoo Tech doc
BIN
developer/_static/banners/actions.jpg
Normal file
After Width: | Height: | Size: 50 KiB |
BIN
developer/_static/banners/actions.small.jpg
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
developer/_static/banners/build_a_module.jpg
Normal file
After Width: | Height: | Size: 51 KiB |
BIN
developer/_static/banners/build_a_module.small.jpg
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
developer/_static/banners/build_a_theme.jpg
Normal file
After Width: | Height: | Size: 39 KiB |
BIN
developer/_static/banners/build_a_theme.small.jpg
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
developer/_static/banners/build_a_website.jpg
Normal file
After Width: | Height: | Size: 54 KiB |
BIN
developer/_static/banners/build_a_website.small.jpg
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
developer/_static/banners/build_interface_ext.jpg
Normal file
After Width: | Height: | Size: 85 KiB |
BIN
developer/_static/banners/build_interface_ext.small.jpg
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
developer/_static/banners/cdn.jpg
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
developer/_static/banners/cdn.small.jpg
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
developer/_static/banners/cmdline.jpg
Normal file
After Width: | Height: | Size: 29 KiB |
BIN
developer/_static/banners/cmdline.small.jpg
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
developer/_static/banners/company.jpg
Normal file
After Width: | Height: | Size: 210 KiB |
BIN
developer/_static/banners/data_files.jpg
Normal file
After Width: | Height: | Size: 39 KiB |
BIN
developer/_static/banners/data_files.small.jpg
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
developer/_static/banners/deploying_odoo.jpg
Normal file
After Width: | Height: | Size: 66 KiB |
BIN
developer/_static/banners/deploying_odoo.small.jpg
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
developer/_static/banners/email_gateway.jpg
Normal file
After Width: | Height: | Size: 82 KiB |
BIN
developer/_static/banners/email_gateway.small.jpg
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
developer/_static/banners/enterprise.jpg
Normal file
After Width: | Height: | Size: 73 KiB |
BIN
developer/_static/banners/enterprise.small.jpg
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
developer/_static/banners/iap.jpg
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
developer/_static/banners/iap.small.jpg
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
developer/_static/banners/index.jpg
Normal file
After Width: | Height: | Size: 68 KiB |
BIN
developer/_static/banners/installing_odoo.jpg
Normal file
After Width: | Height: | Size: 44 KiB |
BIN
developer/_static/banners/installing_odoo.small.jpg
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
developer/_static/banners/iot.jpg
Normal file
After Width: | Height: | Size: 159 KiB |
BIN
developer/_static/banners/javascript.jpg
Normal file
After Width: | Height: | Size: 89 KiB |
BIN
developer/_static/banners/javascript.small.jpg
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
developer/_static/banners/localization.jpg
Normal file
After Width: | Height: | Size: 67 KiB |
BIN
developer/_static/banners/m_1.small.jpg
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
developer/_static/banners/m_2.small.jpg
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
developer/_static/banners/m_accounting.small.jpg
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
developer/_static/banners/mixins.jpg
Normal file
After Width: | Height: | Size: 46 KiB |
BIN
developer/_static/banners/mixins.small.jpg
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
developer/_static/banners/mobile.jpg
Normal file
After Width: | Height: | Size: 105 KiB |
BIN
developer/_static/banners/module.jpg
Normal file
After Width: | Height: | Size: 104 KiB |
BIN
developer/_static/banners/module.small.jpg
Normal file
After Width: | Height: | Size: 31 KiB |
BIN
developer/_static/banners/odoo_guideline.jpg
Normal file
After Width: | Height: | Size: 67 KiB |
BIN
developer/_static/banners/odoo_guideline.small.jpg
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
developer/_static/banners/orm_api.jpg
Normal file
After Width: | Height: | Size: 65 KiB |
BIN
developer/_static/banners/orm_api.small.jpg
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
developer/_static/banners/qweb.jpg
Normal file
After Width: | Height: | Size: 127 KiB |
BIN
developer/_static/banners/qweb.small.jpg
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
developer/_static/banners/reports.jpg
Normal file
After Width: | Height: | Size: 62 KiB |
BIN
developer/_static/banners/reports.small.jpg
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
developer/_static/banners/security.jpg
Normal file
After Width: | Height: | Size: 49 KiB |
BIN
developer/_static/banners/security.small.jpg
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
developer/_static/banners/testing_modules.jpg
Normal file
After Width: | Height: | Size: 57 KiB |
BIN
developer/_static/banners/testing_modules.small.jpg
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
developer/_static/banners/translate.jpg
Normal file
After Width: | Height: | Size: 80 KiB |
BIN
developer/_static/banners/translate.small.jpg
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
developer/_static/banners/upgrade_api.jpg
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
developer/_static/banners/views.jpg
Normal file
After Width: | Height: | Size: 33 KiB |
BIN
developer/_static/banners/views.small.jpg
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
developer/_static/banners/web_controllers.jpg
Normal file
After Width: | Height: | Size: 62 KiB |
BIN
developer/_static/banners/web_controllers.small.jpg
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
developer/_static/banners/web_service_api.jpg
Normal file
After Width: | Height: | Size: 123 KiB |
BIN
developer/_static/banners/web_service_api.small.jpg
Normal file
After Width: | Height: | Size: 32 KiB |
19
developer/_static/issue_template.md
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
Short description of the issue
|
||||||
|
|
||||||
|
***Impacted versions:***
|
||||||
|
|
||||||
|
-
|
||||||
|
|
||||||
|
***Steps to reproduce:***
|
||||||
|
|
||||||
|
1.
|
||||||
|
2.
|
||||||
|
3.
|
||||||
|
|
||||||
|
***Current behavior:***
|
||||||
|
|
||||||
|
-
|
||||||
|
|
||||||
|
***Expected behavior:***
|
||||||
|
|
||||||
|
-
|
26
developer/_static/odoo.css
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
@import "style.css";
|
||||||
|
|
||||||
|
p.rubric {
|
||||||
|
font-family: Lato, Arial, sans-serif;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1.1;
|
||||||
|
color: inherit;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section p.rubric {
|
||||||
|
font-size: 30px;
|
||||||
|
}
|
||||||
|
.section .section p.rubric {
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
.section .section .section p.rubric {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
.section .section .section .section p.rubric {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.section .section .section .section .section p.rubric {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
BIN
developer/_static/pull-request-version.png
Normal file
After Width: | Height: | Size: 13 KiB |
241
developer/conf.py
Normal file
@ -0,0 +1,241 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import sys, os
|
||||||
|
import sphinx
|
||||||
|
|
||||||
|
# If extensions (or modules to document with autodoc) are in another directory,
|
||||||
|
# add these directories to sys.path here. If the directory is relative to the
|
||||||
|
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||||
|
DIR = os.path.dirname(__file__)
|
||||||
|
sys.path.insert(0,
|
||||||
|
os.path.abspath(
|
||||||
|
os.path.join(DIR, '_extensions')))
|
||||||
|
# put current odoo's source on PYTHONPATH for autodoc
|
||||||
|
sys.path.insert(0, os.path.abspath(os.path.join(DIR, '../../odoo')))
|
||||||
|
|
||||||
|
# -- General configuration -----------------------------------------------------
|
||||||
|
|
||||||
|
# If your documentation needs a minimal Sphinx version, state it here.
|
||||||
|
needs_sphinx = '1.2'
|
||||||
|
|
||||||
|
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||||
|
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||||
|
extensions = [
|
||||||
|
'sphinx.ext.ifconfig',
|
||||||
|
'sphinx.ext.todo',
|
||||||
|
'sphinx.ext.autodoc',
|
||||||
|
'sphinx.ext.intersphinx',
|
||||||
|
'sphinx.ext.linkcode',
|
||||||
|
# 'autojsdoc.ext',
|
||||||
|
'github_link',
|
||||||
|
'odoo_ext',
|
||||||
|
'html_domain',
|
||||||
|
'exercise_admonition',
|
||||||
|
'patchqueue'
|
||||||
|
]
|
||||||
|
|
||||||
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
|
templates_path = ['_templates']
|
||||||
|
|
||||||
|
# The suffix of source filenames.
|
||||||
|
source_suffix = '.rst'
|
||||||
|
|
||||||
|
# The encoding of source files.
|
||||||
|
#source_encoding = 'utf-8-sig'
|
||||||
|
|
||||||
|
# The master toctree document.
|
||||||
|
master_doc = 'index'
|
||||||
|
|
||||||
|
# General information about the project.
|
||||||
|
project = u'odoo'
|
||||||
|
copyright = u'Odoo S.A.'
|
||||||
|
|
||||||
|
# The version info for the project you're documenting, acts as replacement for
|
||||||
|
# |version| and |release|, also used in various other places throughout the
|
||||||
|
# built documents.
|
||||||
|
#
|
||||||
|
# The short X.Y version.
|
||||||
|
version = '13.0'
|
||||||
|
# The full version, including alpha/beta/rc tags.
|
||||||
|
release = '13.0'
|
||||||
|
|
||||||
|
# There are two options for replacing |today|: either, you set today to some
|
||||||
|
# non-false value, then it is used:
|
||||||
|
#today = ''
|
||||||
|
# Else, today_fmt is used as the format for a strftime call.
|
||||||
|
today_fmt = '%B %d, %Y'
|
||||||
|
|
||||||
|
# List of patterns, relative to source directory, that match files and
|
||||||
|
# directories to ignore when looking for source files.
|
||||||
|
exclude_patterns = ['_build']
|
||||||
|
|
||||||
|
# markdown compatibility: make `foo` behave like ``foo``, the rst default is
|
||||||
|
# title-reference which is never what people are looking for
|
||||||
|
default_role = 'literal'
|
||||||
|
|
||||||
|
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||||
|
add_function_parentheses = True
|
||||||
|
|
||||||
|
# If true, the current module name will be prepended to all description
|
||||||
|
# unit titles (such as .. function::).
|
||||||
|
#add_module_names = True
|
||||||
|
|
||||||
|
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||||
|
# output. They are ignored by default.
|
||||||
|
#show_authors = False
|
||||||
|
|
||||||
|
# The name of the Pygments (syntax highlighting) style to use.
|
||||||
|
pygments_style = 'odoo'
|
||||||
|
|
||||||
|
# A list of ignored prefixes for module index sorting.
|
||||||
|
#modindex_common_prefix = []
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for HTML output ---------------------------------------------------
|
||||||
|
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||||
|
# a list of builtin themes.
|
||||||
|
html_theme = 'odoo_ext'
|
||||||
|
|
||||||
|
odoo_cover_default = 'banners/installing_odoo.jpg'
|
||||||
|
|
||||||
|
# Theme options are theme-specific and customize the look and feel of a theme
|
||||||
|
# further. For a list of options available for each theme, see the
|
||||||
|
# documentation.
|
||||||
|
#html_theme_options = {}
|
||||||
|
|
||||||
|
# Add any paths that contain custom themes here, relative to this directory.
|
||||||
|
html_theme_path = ['_extensions']
|
||||||
|
|
||||||
|
# The name for this set of Sphinx documents. If None, it defaults to
|
||||||
|
# "<project> v<release> documentation".
|
||||||
|
#html_title = None
|
||||||
|
|
||||||
|
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||||
|
#html_short_title = None
|
||||||
|
|
||||||
|
# The name of an image file (relative to this directory) to place at the top
|
||||||
|
# of the sidebar.
|
||||||
|
#html_logo = None
|
||||||
|
|
||||||
|
# The name of an image file (within the static path) to use as favicon of the
|
||||||
|
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||||
|
# pixels large.
|
||||||
|
#html_favicon = None
|
||||||
|
|
||||||
|
# Add any paths that contain custom static files (such as style sheets) here,
|
||||||
|
# relative to this directory. They are copied after the builtin static files,
|
||||||
|
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||||
|
html_static_path = ['_static']
|
||||||
|
|
||||||
|
html_add_permalinks = u''
|
||||||
|
|
||||||
|
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||||
|
# using the given strftime format.
|
||||||
|
#html_last_updated_fmt = '%b %d, %Y'
|
||||||
|
|
||||||
|
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||||
|
# typographically correct entities.
|
||||||
|
#html_use_smartypants = True
|
||||||
|
|
||||||
|
# Custom sidebar templates, maps document names to template names.
|
||||||
|
# FIXME: no sidebar on index?
|
||||||
|
html_sidebars = {
|
||||||
|
}
|
||||||
|
|
||||||
|
# Additional templates that should be rendered to pages, maps page names to
|
||||||
|
# template names.
|
||||||
|
#html_additional_pages = {}
|
||||||
|
|
||||||
|
# If false, no module index is generated.
|
||||||
|
#html_domain_indices = True
|
||||||
|
|
||||||
|
# If false, no index is generated.
|
||||||
|
#html_use_index = True
|
||||||
|
|
||||||
|
# If true, the index is split into individual pages for each letter.
|
||||||
|
#html_split_index = False
|
||||||
|
|
||||||
|
# If true, links to the reST sources are added to the pages.
|
||||||
|
#html_show_sourcelink = True
|
||||||
|
|
||||||
|
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
||||||
|
#html_show_sphinx = True
|
||||||
|
|
||||||
|
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
||||||
|
#html_show_copyright = True
|
||||||
|
|
||||||
|
# If true, an OpenSearch description file will be output, and all pages will
|
||||||
|
# contain a <link> tag referring to it. The value of this option must be the
|
||||||
|
# base URL from which the finished HTML is served.
|
||||||
|
#html_use_opensearch = ''
|
||||||
|
|
||||||
|
latex_elements = {
|
||||||
|
'papersize': r'a4paper',
|
||||||
|
'preamble': u'''\\setcounter{tocdepth}{2}
|
||||||
|
''',
|
||||||
|
}
|
||||||
|
|
||||||
|
# default must be set otherwise ifconfig blows up
|
||||||
|
todo_include_todos = False
|
||||||
|
|
||||||
|
intersphinx_mapping = {
|
||||||
|
'python': ('https://docs.python.org/3/', None),
|
||||||
|
'werkzeug': ('http://werkzeug.pocoo.org/docs/', None),
|
||||||
|
}
|
||||||
|
|
||||||
|
github_user = 'odoo'
|
||||||
|
github_project = 'odoo'
|
||||||
|
|
||||||
|
# monkeypatch PHP lexer to not require <?php
|
||||||
|
from sphinx.highlighting import lexers
|
||||||
|
from pygments.lexers.web import PhpLexer
|
||||||
|
lexers['php'] = PhpLexer(startinline=True)
|
||||||
|
|
||||||
|
def setup(app):
|
||||||
|
app.connect('html-page-context', canonicalize)
|
||||||
|
app.add_config_value('canonical_root', None, 'env')
|
||||||
|
app.add_config_value('canonical_branch', 'master', 'env')
|
||||||
|
|
||||||
|
app.connect('html-page-context', versionize)
|
||||||
|
app.add_config_value('versions', '', 'env')
|
||||||
|
|
||||||
|
app.connect('html-page-context', analytics)
|
||||||
|
app.add_config_value('google_analytics_key', '', 'env')
|
||||||
|
|
||||||
|
def canonicalize(app, pagename, templatename, context, doctree):
|
||||||
|
""" Adds a 'canonical' URL for the current document in the rendering
|
||||||
|
context. Requires the ``canonical_root`` setting being set. The canonical
|
||||||
|
branch is ``master`` but can be overridden using ``canonical_branch``.
|
||||||
|
"""
|
||||||
|
if not app.config.canonical_root:
|
||||||
|
return
|
||||||
|
|
||||||
|
context['canonical'] = _build_url(
|
||||||
|
app.config.canonical_root, app.config.canonical_branch, pagename)
|
||||||
|
|
||||||
|
def versionize(app, pagename, templatename, context, doctree):
|
||||||
|
""" Adds a version switcher below the menu, requires ``canonical_root``
|
||||||
|
and ``versions`` (an ordered, space-separated lists of all possible
|
||||||
|
versions).
|
||||||
|
"""
|
||||||
|
if not (app.config.canonical_root and app.config.versions):
|
||||||
|
return
|
||||||
|
|
||||||
|
context['versions'] = [
|
||||||
|
(vs, _build_url(app.config.canonical_root, vs, pagename))
|
||||||
|
for vs in app.config.versions.split(',')
|
||||||
|
if vs != app.config.version
|
||||||
|
]
|
||||||
|
|
||||||
|
def analytics(app, pagename, templatename, context, doctree):
|
||||||
|
if not app.config.google_analytics_key:
|
||||||
|
return
|
||||||
|
|
||||||
|
context['google_analytics_key'] = app.config.google_analytics_key
|
||||||
|
|
||||||
|
def _build_url(root, branch, pagename):
|
||||||
|
return "{canonical_url}{canonical_branch}/{canonical_page}".format(
|
||||||
|
canonical_url=root,
|
||||||
|
canonical_branch=branch,
|
||||||
|
canonical_page=(pagename + '.html').replace('index.html', '')
|
||||||
|
.replace('index/', ''),
|
||||||
|
)
|
43
developer/glossary.rst
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
:orphan: true
|
||||||
|
|
||||||
|
========
|
||||||
|
Glossary
|
||||||
|
========
|
||||||
|
|
||||||
|
.. glossary::
|
||||||
|
|
||||||
|
external id
|
||||||
|
external identifier
|
||||||
|
external identifiers
|
||||||
|
string identifier stored in ``ir.model.data``, can be used to refer
|
||||||
|
to a record regardless of its database identifier during data imports
|
||||||
|
or export/import roundtrips.
|
||||||
|
|
||||||
|
External identifiers are in the form :samp:`{module}.{id}` (e.g.
|
||||||
|
``account.invoice_graph``). From within a module, the
|
||||||
|
:samp:`{module}.` prefix can be left out.
|
||||||
|
|
||||||
|
Sometimes referred to as "xml id" or ``xml_id`` as XML-based
|
||||||
|
:ref:`reference/data` make extensive use of them.
|
||||||
|
|
||||||
|
format string
|
||||||
|
inspired by `jinja variables`_, format strings allow more easily
|
||||||
|
mixing literal content and computed content (expressions): content
|
||||||
|
between ``{{`` and ``}}`` is interpreted as an expression and
|
||||||
|
evaluated, other content is interpreted as literal strings and
|
||||||
|
displayed as-is
|
||||||
|
|
||||||
|
GIS
|
||||||
|
Geographic Information System
|
||||||
|
any computer system or subsystem to capture, store, manipulate,
|
||||||
|
analyze, manage or present spatial and geographical data.
|
||||||
|
|
||||||
|
minified
|
||||||
|
minification
|
||||||
|
process of removing extraneous/non-necessary sections of files
|
||||||
|
(comments, whitespace) and possibly recompiling them using equivalent
|
||||||
|
but shorter structures (`ternary operator`_ instead of ``if/else``) in
|
||||||
|
order to reduce network traffic
|
||||||
|
|
||||||
|
.. _jinja variables: http://jinja.pocoo.org/docs/dev/templates/#variables
|
||||||
|
.. _ternary operator: http://en.wikipedia.org/wiki/%3F:
|
1822
developer/howtos/backend.rst
Normal file
40
developer/howtos/backend/exercise-access-rights
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
# HG changeset patch
|
||||||
|
# Parent 303a5f4f011822dcb42b5833d579eabd3f03f4bf
|
||||||
|
|
||||||
|
Index: addons/openacademy/__manifest__.py
|
||||||
|
===================================================================
|
||||||
|
--- addons.orig/openacademy/__manifest__.py 2014-08-26 17:26:18.143783102 +0200
|
||||||
|
+++ addons/openacademy/__manifest__.py 2014-08-26 17:26:18.135783102 +0200
|
||||||
|
@@ -25,7 +25,8 @@
|
||||||
|
|
||||||
|
# always loaded
|
||||||
|
'data': [
|
||||||
|
- # 'security/ir.model.access.csv',
|
||||||
|
+ 'security/security.xml',
|
||||||
|
+ 'security/ir.model.access.csv',
|
||||||
|
'templates.xml',
|
||||||
|
'views/openacademy.xml',
|
||||||
|
'views/partner.xml',
|
||||||
|
Index: addons/openacademy/security/ir.model.access.csv
|
||||||
|
===================================================================
|
||||||
|
--- addons.orig/openacademy/security/ir.model.access.csv 2014-08-26 17:26:18.143783102 +0200
|
||||||
|
+++ addons/openacademy/security/ir.model.access.csv 2014-08-26 17:26:18.135783102 +0200
|
||||||
|
@@ -1,2 +1,5 @@
|
||||||
|
id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink
|
||||||
|
-access_openacademy_openacademy,openacademy.openacademy,model_openacademy_openacademy,,1,0,0,0
|
||||||
|
+course_manager,course manager,model_openacademy_course,group_manager,1,1,1,1
|
||||||
|
+session_manager,session manager,model_openacademy_session,group_manager,1,1,1,1
|
||||||
|
+course_read_all,course all,model_openacademy_course,,1,0,0,0
|
||||||
|
+session_read_all,session all,model_openacademy_session,,1,0,0,0
|
||||||
|
Index: addons/openacademy/security/security.xml
|
||||||
|
===================================================================
|
||||||
|
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
|
||||||
|
+++ addons/openacademy/security/security.xml 2014-08-26 17:26:18.135783102 +0200
|
||||||
|
@@ -0,0 +1,7 @@
|
||||||
|
+<odoo>
|
||||||
|
+
|
||||||
|
+ <record id="group_manager" model="res.groups">
|
||||||
|
+ <field name="name">OpenAcademy / Manager</field>
|
||||||
|
+ </record>
|
||||||
|
+
|
||||||
|
+</odoo>
|
27
developer/howtos/backend/exercise-access-rules
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# HG changeset patch
|
||||||
|
# Parent 0602022dc2a428f9995c886df33b699b6d3bcb69
|
||||||
|
|
||||||
|
Index: addons/openacademy/security/security.xml
|
||||||
|
===================================================================
|
||||||
|
--- addons.orig/openacademy/security/security.xml 2014-08-26 17:26:18.971783090 +0200
|
||||||
|
+++ addons/openacademy/security/security.xml 2014-08-26 17:26:18.967783090 +0200
|
||||||
|
@@ -3,5 +3,19 @@
|
||||||
|
<record id="group_manager" model="res.groups">
|
||||||
|
<field name="name">OpenAcademy / Manager</field>
|
||||||
|
</record>
|
||||||
|
+
|
||||||
|
+ <record id="only_responsible_can_modify" model="ir.rule">
|
||||||
|
+ <field name="name">Only Responsible can modify Course</field>
|
||||||
|
+ <field name="model_id" ref="model_openacademy_course"/>
|
||||||
|
+ <field name="groups" eval="[(4, ref('openacademy.group_manager'))]"/>
|
||||||
|
+ <field name="perm_read" eval="0"/>
|
||||||
|
+ <field name="perm_write" eval="1"/>
|
||||||
|
+ <field name="perm_create" eval="0"/>
|
||||||
|
+ <field name="perm_unlink" eval="1"/>
|
||||||
|
+ <field name="domain_force">
|
||||||
|
+ ['|', ('responsible_id','=',False),
|
||||||
|
+ ('responsible_id','=',user.id)]
|
||||||
|
+ </field>
|
||||||
|
+ </record>
|
||||||
|
|
||||||
|
</odoo>
|
19
developer/howtos/backend/exercise-advanced-treeview
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# HG changeset patch
|
||||||
|
# Parent f8d2422e87b3ff566dc947ad582608db3b15e077
|
||||||
|
|
||||||
|
Index: addons/openacademy/views/openacademy.xml
|
||||||
|
===================================================================
|
||||||
|
--- addons.orig/openacademy/views/openacademy.xml 2014-08-26 17:26:09.283783234 +0200
|
||||||
|
+++ addons/openacademy/views/openacademy.xml 2014-08-26 17:26:09.279783234 +0200
|
||||||
|
@@ -115,9 +115,10 @@
|
||||||
|
<field name="name">session.tree</field>
|
||||||
|
<field name="model">openacademy.session</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
- <tree string="Session Tree">
|
||||||
|
+ <tree string="Session Tree" decoration-info="duration<5" decoration-danger="duration>15">
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="course_id"/>
|
||||||
|
+ <field name="duration" invisible="1"/>
|
||||||
|
<field name="taken_seats" widget="progressbar"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
53
developer/howtos/backend/exercise-basic-action
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
# HG changeset patch
|
||||||
|
# Parent 16e4cb131d9f7f3a72a8a1b0bc46c2ce9ac76435
|
||||||
|
Index: addons/openacademy/__manifest__.py
|
||||||
|
===================================================================
|
||||||
|
--- addons.orig/openacademy/__manifest__.py 2014-08-26 17:25:53.519783468 +0200
|
||||||
|
+++ addons/openacademy/__manifest__.py 2014-08-26 17:25:53.511783468 +0200
|
||||||
|
@@ -27,6 +27,7 @@
|
||||||
|
'data': [
|
||||||
|
# 'security/ir.model.access.csv',
|
||||||
|
'templates.xml',
|
||||||
|
+ 'views/openacademy.xml',
|
||||||
|
],
|
||||||
|
# only loaded in demonstration mode
|
||||||
|
'demo': [
|
||||||
|
Index: addons/openacademy/views/openacademy.xml
|
||||||
|
===================================================================
|
||||||
|
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
|
||||||
|
+++ addons/openacademy/views/openacademy.xml 2014-08-26 17:25:53.511783468 +0200
|
||||||
|
@@ -0,0 +1,34 @@
|
||||||
|
+<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
+<odoo>
|
||||||
|
+
|
||||||
|
+ <!-- window action -->
|
||||||
|
+ <!--
|
||||||
|
+ The following tag is an action definition for a "window action",
|
||||||
|
+ that is an action opening a view or a set of views
|
||||||
|
+ -->
|
||||||
|
+ <record model="ir.actions.act_window" id="course_list_action">
|
||||||
|
+ <field name="name">Courses</field>
|
||||||
|
+ <field name="res_model">openacademy.course</field>
|
||||||
|
+ <field name="view_mode">tree,form</field>
|
||||||
|
+ <field name="help" type="html">
|
||||||
|
+ <p class="o_view_nocontent_smiling_face">Create the first course
|
||||||
|
+ </p>
|
||||||
|
+ </field>
|
||||||
|
+ </record>
|
||||||
|
+
|
||||||
|
+ <!-- top level menu: no parent -->
|
||||||
|
+ <menuitem id="main_openacademy_menu" name="Open Academy"/>
|
||||||
|
+ <!-- A first level in the left side menu is needed
|
||||||
|
+ before using action= attribute -->
|
||||||
|
+ <menuitem id="openacademy_menu" name="Open Academy"
|
||||||
|
+ parent="main_openacademy_menu"/>
|
||||||
|
+ <!-- the following menuitem should appear *after*
|
||||||
|
+ its parent openacademy_menu and *after* its
|
||||||
|
+ action course_list_action -->
|
||||||
|
+ <menuitem id="courses_menu" name="Courses" parent="openacademy_menu"
|
||||||
|
+ action="course_list_action"/>
|
||||||
|
+ <!-- Full id location:
|
||||||
|
+ action="openacademy.course_list_action"
|
||||||
|
+ It is not required when it is the same module -->
|
||||||
|
+
|
||||||
|
+</odoo>
|
77
developer/howtos/backend/exercise-calendar
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
# HG changeset patch
|
||||||
|
# Parent 85a8d7317b9e13480f39ad739955442d15144451
|
||||||
|
# Parent 16fcdc4c6462a7872636f3c19550c16879af5281
|
||||||
|
|
||||||
|
diff --git a/openacademy/models.py b/openacademy/models.py
|
||||||
|
--- a/openacademy/models.py
|
||||||
|
+++ b/openacademy/models.py
|
||||||
|
@@ -1,5 +1,6 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
+from datetime import timedelta
|
||||||
|
from odoo import models, fields, api, exceptions
|
||||||
|
|
||||||
|
class Course(models.Model):
|
||||||
|
@@ -57,6 +58,8 @@ class Session(models.Model):
|
||||||
|
attendee_ids = fields.Many2many('res.partner', string="Attendees")
|
||||||
|
|
||||||
|
taken_seats = fields.Float(string="Taken seats", compute='_taken_seats')
|
||||||
|
+ end_date = fields.Date(string="End Date", store=True,
|
||||||
|
+ compute='_get_end_date', inverse='_set_end_date')
|
||||||
|
|
||||||
|
@api.depends('seats', 'attendee_ids')
|
||||||
|
def _taken_seats(self):
|
||||||
|
@@ -83,6 +86,27 @@ class Session(models.Model):
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
+ @api.depends('start_date', 'duration')
|
||||||
|
+ def _get_end_date(self):
|
||||||
|
+ for r in self:
|
||||||
|
+ if not (r.start_date and r.duration):
|
||||||
|
+ r.end_date = r.start_date
|
||||||
|
+ continue
|
||||||
|
+
|
||||||
|
+ # Add duration to start_date, but: Monday + 5 days = Saturday, so
|
||||||
|
+ # subtract one second to get on Friday instead
|
||||||
|
+ duration = timedelta(days=r.duration, seconds=-1)
|
||||||
|
+ r.end_date = r.start_date + duration
|
||||||
|
+
|
||||||
|
+ def _set_end_date(self):
|
||||||
|
+ for r in self:
|
||||||
|
+ if not (r.start_date and r.end_date):
|
||||||
|
+ continue
|
||||||
|
+
|
||||||
|
+ # Compute the difference between dates, but: Friday - Monday = 4 days,
|
||||||
|
+ # so add one day to get 5 days instead
|
||||||
|
+ r.duration = (r.end_date - r.start_date).days + 1
|
||||||
|
+
|
||||||
|
@api.constrains('instructor_id', 'attendee_ids')
|
||||||
|
def _check_instructor_not_in_attendees(self):
|
||||||
|
for r in self:
|
||||||
|
diff --git a/openacademy/views/openacademy.xml b/openacademy/views/openacademy.xml
|
||||||
|
--- a/openacademy/views/openacademy.xml
|
||||||
|
+++ b/openacademy/views/openacademy.xml
|
||||||
|
@@ -124,10 +124,21 @@
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
+ <!-- calendar view -->
|
||||||
|
+ <record model="ir.ui.view" id="session_calendar_view">
|
||||||
|
+ <field name="name">session.calendar</field>
|
||||||
|
+ <field name="model">openacademy.session</field>
|
||||||
|
+ <field name="arch" type="xml">
|
||||||
|
+ <calendar string="Session Calendar" date_start="start_date" date_stop="end_date" color="instructor_id">
|
||||||
|
+ <field name="name"/>
|
||||||
|
+ </calendar>
|
||||||
|
+ </field>
|
||||||
|
+ </record>
|
||||||
|
+
|
||||||
|
<record model="ir.actions.act_window" id="session_list_action">
|
||||||
|
<field name="name">Sessions</field>
|
||||||
|
<field name="res_model">openacademy.session</field>
|
||||||
|
- <field name="view_mode">tree,form</field>
|
||||||
|
+ <field name="view_mode">tree,form,calendar</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<menuitem id="session_menu" name="Sessions"
|
40
developer/howtos/backend/exercise-computed
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
# HG changeset patch
|
||||||
|
# Parent a358be0a577b0569831958a8ec1302825c645dee
|
||||||
|
# Parent 59107edbe5f81bab5e7db172bf2bffa504ce399a
|
||||||
|
|
||||||
|
diff --git a/openacademy/models.py b/openacademy/models.py
|
||||||
|
--- a/openacademy/models.py
|
||||||
|
+++ b/openacademy/models.py
|
||||||
|
@@ -30,3 +30,13 @@ class Session(models.Model):
|
||||||
|
course_id = fields.Many2one('openacademy.course',
|
||||||
|
ondelete='cascade', string="Course", required=True)
|
||||||
|
attendee_ids = fields.Many2many('res.partner', string="Attendees")
|
||||||
|
+
|
||||||
|
+ taken_seats = fields.Float(string="Taken seats", compute='_taken_seats')
|
||||||
|
+
|
||||||
|
+ @api.depends('seats', 'attendee_ids')
|
||||||
|
+ def _taken_seats(self):
|
||||||
|
+ for r in self:
|
||||||
|
+ if not r.seats:
|
||||||
|
+ r.taken_seats = 0.0
|
||||||
|
+ else:
|
||||||
|
+ r.taken_seats = 100.0 * len(r.attendee_ids) / r.seats
|
||||||
|
diff --git a/openacademy/views/openacademy.xml b/openacademy/views/openacademy.xml
|
||||||
|
--- a/openacademy/views/openacademy.xml
|
||||||
|
+++ b/openacademy/views/openacademy.xml
|
||||||
|
@@ -99,6 +99,7 @@
|
||||||
|
<field name="start_date"/>
|
||||||
|
<field name="duration"/>
|
||||||
|
<field name="seats"/>
|
||||||
|
+ <field name="taken_seats" widget="progressbar"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
<label for="attendee_ids"/>
|
||||||
|
@@ -116,6 +117,7 @@
|
||||||
|
<tree string="Session Tree">
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="course_id"/>
|
||||||
|
+ <field name="taken_seats" widget="progressbar"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</record>
|
25
developer/howtos/backend/exercise-constraint-python
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# HG changeset patch
|
||||||
|
# Parent 7a7d003fe38426a405ce0657a627a139133ec4dd
|
||||||
|
# Parent 52f54b46487c8224a5aade4b921be77360ed3eae
|
||||||
|
|
||||||
|
diff --git a/openacademy/models.py b/openacademy/models.py
|
||||||
|
--- a/openacademy/models.py
|
||||||
|
+++ b/openacademy/models.py
|
||||||
|
@@ -1,6 +1,6 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
-from odoo import models, fields, api
|
||||||
|
+from odoo import models, fields, api, exceptions
|
||||||
|
|
||||||
|
class Course(models.Model):
|
||||||
|
_name = 'openacademy.course'
|
||||||
|
@@ -58,3 +58,9 @@ class Session(models.Model):
|
||||||
|
'message': "Increase seats or remove excess attendees",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+ @api.constrains('instructor_id', 'attendee_ids')
|
||||||
|
+ def _check_instructor_not_in_attendees(self):
|
||||||
|
+ for r in self:
|
||||||
|
+ if r.instructor_id and r.instructor_id in r.attendee_ids:
|
||||||
|
+ raise exceptions.ValidationError("A session's instructor can't be an attendee")
|
24
developer/howtos/backend/exercise-constraint-sql
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# HG changeset patch
|
||||||
|
# Parent 121bbfe120be3007f5e04611dbc27038abafcce8
|
||||||
|
|
||||||
|
Index: addons/openacademy/models.py
|
||||||
|
===================================================================
|
||||||
|
--- addons.orig/openacademy/models.py
|
||||||
|
+++ addons/openacademy/models.py
|
||||||
|
@@ -14,6 +14,16 @@
|
||||||
|
session_ids = fields.One2many(
|
||||||
|
'openacademy.session', 'course_id', string="Sessions")
|
||||||
|
|
||||||
|
+ _sql_constraints = [
|
||||||
|
+ ('name_description_check',
|
||||||
|
+ 'CHECK(name != description)',
|
||||||
|
+ "The title of the course should not be the description"),
|
||||||
|
+
|
||||||
|
+ ('name_unique',
|
||||||
|
+ 'UNIQUE(name)',
|
||||||
|
+ "The course title must be unique"),
|
||||||
|
+ ]
|
||||||
|
+
|
||||||
|
|
||||||
|
class Session(models.Model):
|
||||||
|
_name = 'openacademy.session'
|
27
developer/howtos/backend/exercise-copy-override
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# HG changeset patch
|
||||||
|
# Parent 7d14b75cdfd4c7a272a13572947de5d47f3e851f
|
||||||
|
# Parent f400352a70963801f0b4732d33a0183e4f6800ff
|
||||||
|
|
||||||
|
diff --git a/openacademy/models.py b/openacademy/models.py
|
||||||
|
--- a/openacademy/models.py
|
||||||
|
+++ b/openacademy/models.py
|
||||||
|
@@ -14,6 +14,19 @@ class Course(models.Model):
|
||||||
|
session_ids = fields.One2many(
|
||||||
|
'openacademy.session', 'course_id', string="Sessions")
|
||||||
|
|
||||||
|
+ def copy(self, default=None):
|
||||||
|
+ default = dict(default or {})
|
||||||
|
+
|
||||||
|
+ copied_count = self.search_count(
|
||||||
|
+ [('name', '=like', u"Copy of {}%".format(self.name))])
|
||||||
|
+ if not copied_count:
|
||||||
|
+ new_name = u"Copy of {}".format(self.name)
|
||||||
|
+ else:
|
||||||
|
+ new_name = u"Copy of {} ({})".format(self.name, copied_count)
|
||||||
|
+
|
||||||
|
+ default['name'] = new_name
|
||||||
|
+ return super(Course, self).copy(default)
|
||||||
|
+
|
||||||
|
_sql_constraints = [
|
||||||
|
('name_description_check',
|
||||||
|
'CHECK(name != description)',
|
152
developer/howtos/backend/exercise-creation
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
# HG changeset patch
|
||||||
|
# Parent 0000000000000000000000000000000000000000
|
||||||
|
Index: addons/openacademy/__manifest__.py
|
||||||
|
===================================================================
|
||||||
|
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
|
||||||
|
+++ addons/openacademy/__manifest__.py 2014-08-26 17:25:49.787783523 +0200
|
||||||
|
@@ -0,0 +1,35 @@
|
||||||
|
+# -*- coding: utf-8 -*-
|
||||||
|
+{
|
||||||
|
+ 'name': "Open Academy",
|
||||||
|
+
|
||||||
|
+ 'summary': """Manage trainings""",
|
||||||
|
+
|
||||||
|
+ 'description': """
|
||||||
|
+ Open Academy module for managing trainings:
|
||||||
|
+ - training courses
|
||||||
|
+ - training sessions
|
||||||
|
+ - attendees registration
|
||||||
|
+ """,
|
||||||
|
+
|
||||||
|
+ 'author': "My Company",
|
||||||
|
+ 'website': "http://www.yourcompany.com",
|
||||||
|
+
|
||||||
|
+ # Categories can be used to filter modules in modules listing
|
||||||
|
+ # Check https://github.com/odoo/odoo/blob/13.0/odoo/addons/base/data/ir_module_category_data.xml
|
||||||
|
+ # for the full list
|
||||||
|
+ 'category': 'Test',
|
||||||
|
+ 'version': '0.1',
|
||||||
|
+
|
||||||
|
+ # any module necessary for this one to work correctly
|
||||||
|
+ 'depends': ['base'],
|
||||||
|
+
|
||||||
|
+ # always loaded
|
||||||
|
+ 'data': [
|
||||||
|
+ # 'security/ir.model.access.csv',
|
||||||
|
+ 'templates.xml',
|
||||||
|
+ ],
|
||||||
|
+ # only loaded in demonstration mode
|
||||||
|
+ 'demo': [
|
||||||
|
+ 'demo.xml',
|
||||||
|
+ ],
|
||||||
|
+}
|
||||||
|
Index: addons/openacademy/__init__.py
|
||||||
|
===================================================================
|
||||||
|
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
|
||||||
|
+++ addons/openacademy/__init__.py 2014-08-26 17:25:49.791783523 +0200
|
||||||
|
@@ -0,0 +1,3 @@
|
||||||
|
+# -*- coding: utf-8 -*-
|
||||||
|
+from . import controllers
|
||||||
|
+from . import models
|
||||||
|
Index: addons/openacademy/controllers.py
|
||||||
|
===================================================================
|
||||||
|
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
|
||||||
|
+++ addons/openacademy/controllers.py 2014-08-26 17:25:49.791783523 +0200
|
||||||
|
@@ -0,0 +1,20 @@
|
||||||
|
+# -*- coding: utf-8 -*-
|
||||||
|
+from odoo import http
|
||||||
|
+
|
||||||
|
+# class Openacademy(http.Controller):
|
||||||
|
+# @http.route('/openacademy/openacademy/', auth='public')
|
||||||
|
+# def index(self, **kw):
|
||||||
|
+# return "Hello, world"
|
||||||
|
+
|
||||||
|
+# @http.route('/openacademy/openacademy/objects/', auth='public')
|
||||||
|
+# def list(self, **kw):
|
||||||
|
+# return http.request.render('openacademy.listing', {
|
||||||
|
+# 'root': '/openacademy/openacademy',
|
||||||
|
+# 'objects': http.request.env['openacademy.openacademy'].search([]),
|
||||||
|
+# })
|
||||||
|
+
|
||||||
|
+# @http.route('/openacademy/openacademy/objects/<model("openacademy.openacademy"):obj>/', auth='public')
|
||||||
|
+# def object(self, obj, **kw):
|
||||||
|
+# return http.request.render('openacademy.object', {
|
||||||
|
+# 'object': obj
|
||||||
|
+# })
|
||||||
|
Index: addons/openacademy/demo.xml
|
||||||
|
===================================================================
|
||||||
|
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
|
||||||
|
+++ addons/openacademy/demo.xml 2014-08-26 17:25:49.791783523 +0200
|
||||||
|
@@ -0,0 +1,25 @@
|
||||||
|
+<odoo>
|
||||||
|
+
|
||||||
|
+ <!-- -->
|
||||||
|
+ <!-- <record id="object0" model="openacademy.openacademy"> -->
|
||||||
|
+ <!-- <field name="name">Object 0</field> -->
|
||||||
|
+ <!-- </record> -->
|
||||||
|
+ <!-- -->
|
||||||
|
+ <!-- <record id="object1" model="openacademy.openacademy"> -->
|
||||||
|
+ <!-- <field name="name">Object 1</field> -->
|
||||||
|
+ <!-- </record> -->
|
||||||
|
+ <!-- -->
|
||||||
|
+ <!-- <record id="object2" model="openacademy.openacademy"> -->
|
||||||
|
+ <!-- <field name="name">Object 2</field> -->
|
||||||
|
+ <!-- </record> -->
|
||||||
|
+ <!-- -->
|
||||||
|
+ <!-- <record id="object3" model="openacademy.openacademy"> -->
|
||||||
|
+ <!-- <field name="name">Object 3</field> -->
|
||||||
|
+ <!-- </record> -->
|
||||||
|
+ <!-- -->
|
||||||
|
+ <!-- <record id="object4" model="openacademy.openacademy"> -->
|
||||||
|
+ <!-- <field name="name">Object 4</field> -->
|
||||||
|
+ <!-- </record> -->
|
||||||
|
+ <!-- -->
|
||||||
|
+
|
||||||
|
+</odoo>
|
||||||
|
Index: addons/openacademy/models.py
|
||||||
|
===================================================================
|
||||||
|
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
|
||||||
|
+++ addons/openacademy/models.py 2014-08-26 17:25:49.791783523 +0200
|
||||||
|
@@ -0,0 +1,8 @@
|
||||||
|
+# -*- coding: utf-8 -*-
|
||||||
|
+
|
||||||
|
+from odoo import models, fields, api
|
||||||
|
+
|
||||||
|
+# class openacademy(models.Model):
|
||||||
|
+# _name = 'openacademy.openacademy'
|
||||||
|
+
|
||||||
|
+# name = fields.Char()
|
||||||
|
Index: addons/openacademy/security/ir.model.access.csv
|
||||||
|
===================================================================
|
||||||
|
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
|
||||||
|
+++ addons/openacademy/security/ir.model.access.csv 2014-08-26 17:25:49.791783523 +0200
|
||||||
|
@@ -0,0 +1,2 @@
|
||||||
|
+id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink
|
||||||
|
+access_openacademy_openacademy,openacademy.openacademy,model_openacademy_openacademy,,1,0,0,0
|
||||||
|
Index: addons/openacademy/templates.xml
|
||||||
|
===================================================================
|
||||||
|
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
|
||||||
|
+++ addons/openacademy/templates.xml 2014-08-26 17:25:49.791783523 +0200
|
||||||
|
@@ -0,0 +1,22 @@
|
||||||
|
+<odoo>
|
||||||
|
+
|
||||||
|
+ <!-- <template id="listing"> -->
|
||||||
|
+ <!-- <ul> -->
|
||||||
|
+ <!-- <li t-foreach="objects" t-as="object"> -->
|
||||||
|
+ <!-- <a t-attf-href="{{ root }}/objects/{{ object.id }}"> -->
|
||||||
|
+ <!-- <t t-esc="object.display_name"/> -->
|
||||||
|
+ <!-- </a> -->
|
||||||
|
+ <!-- </li> -->
|
||||||
|
+ <!-- </ul> -->
|
||||||
|
+ <!-- </template> -->
|
||||||
|
+ <!-- <template id="object"> -->
|
||||||
|
+ <!-- <h1><t t-esc="object.display_name"/></h1> -->
|
||||||
|
+ <!-- <dl> -->
|
||||||
|
+ <!-- <t t-foreach="object._fields" t-as="field"> -->
|
||||||
|
+ <!-- <dt><t t-esc="field"/></dt> -->
|
||||||
|
+ <!-- <dd><t t-esc="object[field]"/></dd> -->
|
||||||
|
+ <!-- </t> -->
|
||||||
|
+ <!-- </dl> -->
|
||||||
|
+ <!-- </template> -->
|
||||||
|
+
|
||||||
|
+</odoo>
|
91
developer/howtos/backend/exercise-dashboard
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
# HG changeset patch
|
||||||
|
# Parent 643813940cbea07bec792f9e1c60022a9292fa90
|
||||||
|
|
||||||
|
Index: addons/openacademy/__manifest__.py
|
||||||
|
===================================================================
|
||||||
|
--- addons.orig/openacademy/__manifest__.py 2014-08-26 17:26:21.535783052 +0200
|
||||||
|
+++ addons/openacademy/__manifest__.py 2014-08-26 17:26:21.531783052 +0200
|
||||||
|
@@ -21,7 +21,7 @@
|
||||||
|
'version': '0.1',
|
||||||
|
|
||||||
|
# any module necessary for this one to work correctly
|
||||||
|
- 'depends': ['base'],
|
||||||
|
+ 'depends': ['base', 'board'],
|
||||||
|
|
||||||
|
# always loaded
|
||||||
|
'data': [
|
||||||
|
@@ -30,6 +30,7 @@
|
||||||
|
'templates.xml',
|
||||||
|
'views/openacademy.xml',
|
||||||
|
'views/partner.xml',
|
||||||
|
+ 'views/session_board.xml',
|
||||||
|
'reports.xml',
|
||||||
|
],
|
||||||
|
# only loaded in demonstration mode
|
||||||
|
Index: addons/openacademy/views/session_board.xml
|
||||||
|
===================================================================
|
||||||
|
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
|
||||||
|
+++ addons/openacademy/views/session_board.xml 2014-08-26 17:26:21.531783052 +0200
|
||||||
|
@@ -0,0 +1,62 @@
|
||||||
|
+<?xml version="1.0"?>
|
||||||
|
+<odoo>
|
||||||
|
+
|
||||||
|
+ <record model="ir.actions.act_window" id="act_session_graph">
|
||||||
|
+ <field name="name">Attendees by course</field>
|
||||||
|
+ <field name="res_model">openacademy.session</field>
|
||||||
|
+ <field name="view_mode">graph</field>
|
||||||
|
+ <field name="view_id"
|
||||||
|
+ ref="openacademy.openacademy_session_graph_view"/>
|
||||||
|
+ </record>
|
||||||
|
+ <record model="ir.actions.act_window" id="act_session_calendar">
|
||||||
|
+ <field name="name">Sessions</field>
|
||||||
|
+ <field name="res_model">openacademy.session</field>
|
||||||
|
+ <field name="view_mode">calendar</field>
|
||||||
|
+ <field name="view_id" ref="openacademy.session_calendar_view"/>
|
||||||
|
+ </record>
|
||||||
|
+ <record model="ir.actions.act_window" id="act_course_list">
|
||||||
|
+ <field name="name">Courses</field>
|
||||||
|
+ <field name="res_model">openacademy.course</field>
|
||||||
|
+ <field name="view_mode">tree,form</field>
|
||||||
|
+ </record>
|
||||||
|
+ <record model="ir.ui.view" id="board_session_form">
|
||||||
|
+ <field name="name">Session Dashboard Form</field>
|
||||||
|
+ <field name="model">board.board</field>
|
||||||
|
+ <field name="type">form</field>
|
||||||
|
+ <field name="arch" type="xml">
|
||||||
|
+ <form string="Session Dashboard">
|
||||||
|
+ <board style="2-1">
|
||||||
|
+ <column>
|
||||||
|
+ <action
|
||||||
|
+ string="Attendees by course"
|
||||||
|
+ name="%(act_session_graph)d"
|
||||||
|
+ height="150"
|
||||||
|
+ width="510"/>
|
||||||
|
+ <action
|
||||||
|
+ string="Sessions"
|
||||||
|
+ name="%(act_session_calendar)d"/>
|
||||||
|
+ </column>
|
||||||
|
+ <column>
|
||||||
|
+ <action
|
||||||
|
+ string="Courses"
|
||||||
|
+ name="%(act_course_list)d"/>
|
||||||
|
+ </column>
|
||||||
|
+ </board>
|
||||||
|
+ </form>
|
||||||
|
+ </field>
|
||||||
|
+ </record>
|
||||||
|
+ <record model="ir.actions.act_window" id="open_board_session">
|
||||||
|
+ <field name="name">Session Dashboard</field>
|
||||||
|
+ <field name="res_model">board.board</field>
|
||||||
|
+ <field name="view_mode">form</field>
|
||||||
|
+ <field name="usage">menu</field>
|
||||||
|
+ <field name="view_id" ref="board_session_form"/>
|
||||||
|
+ </record>
|
||||||
|
+
|
||||||
|
+ <menuitem
|
||||||
|
+ name="Session Dashboard" parent="base.menu_reporting_dashboard"
|
||||||
|
+ action="open_board_session"
|
||||||
|
+ sequence="1"
|
||||||
|
+ id="menu_board_session"/>
|
||||||
|
+
|
||||||
|
+</odoo>
|
28
developer/howtos/backend/exercise-defaults
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
Index: addons/openacademy/models.py
|
||||||
|
===================================================================
|
||||||
|
--- addons.orig/openacademy/models.py
|
||||||
|
+++ addons/openacademy/models.py
|
||||||
|
@@ -20,9 +20,10 @@
|
||||||
|
_description = "OpenAcademy Sessions"
|
||||||
|
|
||||||
|
name = fields.Char(required=True)
|
||||||
|
- start_date = fields.Date()
|
||||||
|
+ start_date = fields.Date(default=fields.Date.today)
|
||||||
|
duration = fields.Float(digits=(6, 2), help="Duration in days")
|
||||||
|
seats = fields.Integer(string="Number of seats")
|
||||||
|
+ active = fields.Boolean(default=True)
|
||||||
|
|
||||||
|
instructor_id = fields.Many2one('res.partner', string="Instructor",
|
||||||
|
domain=['|', ('instructor', '=', True),
|
||||||
|
Index: addons/openacademy/views/openacademy.xml
|
||||||
|
===================================================================
|
||||||
|
--- addons.orig/openacademy/views/openacademy.xml
|
||||||
|
+++ addons/openacademy/views/openacademy.xml
|
||||||
|
@@ -94,6 +94,7 @@
|
||||||
|
<field name="course_id"/>
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="instructor_id"/>
|
||||||
|
+ <field name="active"/>
|
||||||
|
</group>
|
||||||
|
<group string="Schedule">
|
||||||
|
<field name="start_date"/>
|
48
developer/howtos/backend/exercise-demo
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
# HG changeset patch
|
||||||
|
# Parent 84e2b0b43fc61fd0bcbb44c1929755d44ee58ae5
|
||||||
|
|
||||||
|
Index: addons/openacademy/demo.xml
|
||||||
|
===================================================================
|
||||||
|
--- addons.orig/openacademy/demo.xml 2014-08-26 17:25:52.683783480 +0200
|
||||||
|
+++ addons/openacademy/demo.xml 2014-08-26 17:25:52.679783480 +0200
|
||||||
|
@@ -1,25 +1,19 @@
|
||||||
|
<odoo>
|
||||||
|
|
||||||
|
- <!-- -->
|
||||||
|
- <!-- <record id="object0" model="openacademy.openacademy"> -->
|
||||||
|
- <!-- <field name="name">Object 0</field> -->
|
||||||
|
- <!-- </record> -->
|
||||||
|
- <!-- -->
|
||||||
|
- <!-- <record id="object1" model="openacademy.openacademy"> -->
|
||||||
|
- <!-- <field name="name">Object 1</field> -->
|
||||||
|
- <!-- </record> -->
|
||||||
|
- <!-- -->
|
||||||
|
- <!-- <record id="object2" model="openacademy.openacademy"> -->
|
||||||
|
- <!-- <field name="name">Object 2</field> -->
|
||||||
|
- <!-- </record> -->
|
||||||
|
- <!-- -->
|
||||||
|
- <!-- <record id="object3" model="openacademy.openacademy"> -->
|
||||||
|
- <!-- <field name="name">Object 3</field> -->
|
||||||
|
- <!-- </record> -->
|
||||||
|
- <!-- -->
|
||||||
|
- <!-- <record id="object4" model="openacademy.openacademy"> -->
|
||||||
|
- <!-- <field name="name">Object 4</field> -->
|
||||||
|
- <!-- </record> -->
|
||||||
|
- <!-- -->
|
||||||
|
+ <record model="openacademy.course" id="course0">
|
||||||
|
+ <field name="name">Course 0</field>
|
||||||
|
+ <field name="description">Course 0's description
|
||||||
|
+
|
||||||
|
+Can have multiple lines
|
||||||
|
+ </field>
|
||||||
|
+ </record>
|
||||||
|
+ <record model="openacademy.course" id="course1">
|
||||||
|
+ <field name="name">Course 1</field>
|
||||||
|
+ <!-- no description for this one -->
|
||||||
|
+ </record>
|
||||||
|
+ <record model="openacademy.course" id="course2">
|
||||||
|
+ <field name="name">Course 2</field>
|
||||||
|
+ <field name="description">Course 2's description</field>
|
||||||
|
+ </record>
|
||||||
|
|
||||||
|
</odoo>
|
42
developer/howtos/backend/exercise-domain-advanced
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
# HG changeset patch
|
||||||
|
# Parent 69d1f2d359eb8ef304a9d99f17790c78b35eda1a
|
||||||
|
|
||||||
|
Index: addons/openacademy/models.py
|
||||||
|
===================================================================
|
||||||
|
--- addons.orig/openacademy/models.py
|
||||||
|
+++ addons/openacademy/models.py
|
||||||
|
@@ -25,7 +25,8 @@
|
||||||
|
seats = fields.Integer(string="Number of seats")
|
||||||
|
|
||||||
|
instructor_id = fields.Many2one('res.partner', string="Instructor",
|
||||||
|
- domain=[('instructor', '=', True)])
|
||||||
|
+ domain=['|', ('instructor', '=', True),
|
||||||
|
+ ('category_id.name', 'ilike', "Teacher")])
|
||||||
|
course_id = fields.Many2one('openacademy.course',
|
||||||
|
ondelete='cascade', string="Course", required=True)
|
||||||
|
attendee_ids = fields.Many2many('res.partner', string="Attendees")
|
||||||
|
Index: addons/openacademy/views/partner.xml
|
||||||
|
===================================================================
|
||||||
|
--- addons.orig/openacademy/views/partner.xml
|
||||||
|
+++ addons/openacademy/views/partner.xml
|
||||||
|
@@ -29,4 +29,20 @@
|
||||||
|
parent="configuration_menu"
|
||||||
|
action="contact_list_action"/>
|
||||||
|
|
||||||
|
+ <record model="ir.actions.act_window" id="contact_cat_list_action">
|
||||||
|
+ <field name="name">Contact Tags</field>
|
||||||
|
+ <field name="res_model">res.partner.category</field>
|
||||||
|
+ <field name="view_mode">tree,form</field>
|
||||||
|
+ </record>
|
||||||
|
+ <menuitem id="contact_cat_menu" name="Contact Tags"
|
||||||
|
+ parent="configuration_menu"
|
||||||
|
+ action="contact_cat_list_action"/>
|
||||||
|
+
|
||||||
|
+ <record model="res.partner.category" id="teacher1">
|
||||||
|
+ <field name="name">Teacher / Level 1</field>
|
||||||
|
+ </record>
|
||||||
|
+ <record model="res.partner.category" id="teacher2">
|
||||||
|
+ <field name="name">Teacher / Level 2</field>
|
||||||
|
+ </record>
|
||||||
|
+
|
||||||
|
</odoo>
|
17
developer/howtos/backend/exercise-domain-basic
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# HG changeset patch
|
||||||
|
# Parent 142c5065ff1b7266d944d4ef5239e814ae22f0df
|
||||||
|
|
||||||
|
Index: addons/openacademy/models.py
|
||||||
|
===================================================================
|
||||||
|
--- addons.orig/openacademy/models.py
|
||||||
|
+++ addons/openacademy/models.py
|
||||||
|
@@ -24,7 +24,8 @@
|
||||||
|
duration = fields.Float(digits=(6, 2), help="Duration in days")
|
||||||
|
seats = fields.Integer(string="Number of seats")
|
||||||
|
|
||||||
|
- instructor_id = fields.Many2one('res.partner', string="Instructor")
|
||||||
|
+ instructor_id = fields.Many2one('res.partner', string="Instructor",
|
||||||
|
+ domain=[('instructor', '=', True)])
|
||||||
|
course_id = fields.Many2one('openacademy.course',
|
||||||
|
ondelete='cascade', string="Course", required=True)
|
||||||
|
attendee_ids = fields.Many2many('res.partner', string="Attendees")
|
29
developer/howtos/backend/exercise-formview
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# HG changeset patch
|
||||||
|
# Parent 4a0db1d29257764f4df5cb1ee0be7e59e8c8d0d8
|
||||||
|
|
||||||
|
Index: addons/openacademy/views/openacademy.xml
|
||||||
|
===================================================================
|
||||||
|
--- addons.orig/openacademy/views/openacademy.xml 2014-08-26 17:25:54.291783456 +0200
|
||||||
|
+++ addons/openacademy/views/openacademy.xml 2014-08-26 17:25:54.283783457 +0200
|
||||||
|
@@ -1,6 +1,21 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<odoo>
|
||||||
|
|
||||||
|
+ <record model="ir.ui.view" id="course_form_view">
|
||||||
|
+ <field name="name">course.form</field>
|
||||||
|
+ <field name="model">openacademy.course</field>
|
||||||
|
+ <field name="arch" type="xml">
|
||||||
|
+ <form string="Course Form">
|
||||||
|
+ <sheet>
|
||||||
|
+ <group>
|
||||||
|
+ <field name="name"/>
|
||||||
|
+ <field name="description"/>
|
||||||
|
+ </group>
|
||||||
|
+ </sheet>
|
||||||
|
+ </form>
|
||||||
|
+ </field>
|
||||||
|
+ </record>
|
||||||
|
+
|
||||||
|
<!-- window action -->
|
||||||
|
<!--
|
||||||
|
The following tag is an action definition for a "window action",
|
24
developer/howtos/backend/exercise-formview-notebooks
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# HG changeset patch
|
||||||
|
# Parent 5508a5440faa7b607d057c4e4ae70af6b6f7cac9
|
||||||
|
|
||||||
|
Index: addons/openacademy/views/openacademy.xml
|
||||||
|
===================================================================
|
||||||
|
--- addons.orig/openacademy/views/openacademy.xml 2014-08-26 17:25:55.023783446 +0200
|
||||||
|
+++ addons/openacademy/views/openacademy.xml 2014-08-26 17:25:55.015783446 +0200
|
||||||
|
@@ -9,8 +9,15 @@
|
||||||
|
<sheet>
|
||||||
|
<group>
|
||||||
|
<field name="name"/>
|
||||||
|
- <field name="description"/>
|
||||||
|
</group>
|
||||||
|
+ <notebook>
|
||||||
|
+ <page string="Description">
|
||||||
|
+ <field name="description"/>
|
||||||
|
+ </page>
|
||||||
|
+ <page string="About">
|
||||||
|
+ This is an example of notebooks
|
||||||
|
+ </page>
|
||||||
|
+ </notebook>
|
||||||
|
</sheet>
|
||||||
|
</form>
|
||||||
|
</field>
|
30
developer/howtos/backend/exercise-gantt
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# HG changeset patch
|
||||||
|
# Parent dba00a105dd2a82490394b8dec5fea5f1d8847e1
|
||||||
|
# Parent f4374b6e2e661e0782e396b24c57c1eb97d13288
|
||||||
|
|
||||||
|
diff --git a/openacademy/views/openacademy.xml b/openacademy/views/openacademy.xml
|
||||||
|
--- a/openacademy/views/openacademy.xml
|
||||||
|
+++ b/openacademy/views/openacademy.xml
|
||||||
|
@@ -142,10 +142,21 @@
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
+ <record model="ir.ui.view" id="session_gantt_view">
|
||||||
|
+ <field name="name">session.gantt</field>
|
||||||
|
+ <field name="model">openacademy.session</field>
|
||||||
|
+ <field name="arch" type="xml">
|
||||||
|
+ <gantt string="Session Gantt"
|
||||||
|
+ date_start="start_date" date_stop="end_date"
|
||||||
|
+ default_group_by='instructor_id'>
|
||||||
|
+ </gantt>
|
||||||
|
+ </field>
|
||||||
|
+ </record>
|
||||||
|
+
|
||||||
|
<record model="ir.actions.act_window" id="session_list_action">
|
||||||
|
<field name="name">Sessions</field>
|
||||||
|
<field name="res_model">openacademy.session</field>
|
||||||
|
- <field name="view_mode">tree,form,calendar</field>
|
||||||
|
+ <field name="view_mode">tree,form,calendar,gantt</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<menuitem id="session_menu" name="Sessions"
|
55
developer/howtos/backend/exercise-graph
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
# HG changeset patch
|
||||||
|
# Parent a6fe4d3923db1f8f5dff2c39a711a814b0a0f549
|
||||||
|
# Parent 0687e07f570f363bf5005c6337e0c565a63a6bac
|
||||||
|
|
||||||
|
diff --git a/openacademy/models.py b/openacademy/models.py
|
||||||
|
--- a/openacademy/models.py
|
||||||
|
+++ b/openacademy/models.py
|
||||||
|
@@ -60,6 +60,9 @@ class Session(models.Model):
|
||||||
|
end_date = fields.Date(string="End Date", store=True,
|
||||||
|
compute='_get_end_date', inverse='_set_end_date')
|
||||||
|
|
||||||
|
+ attendees_count = fields.Integer(
|
||||||
|
+ string="Attendees count", compute='_get_attendees_count', store=True)
|
||||||
|
+
|
||||||
|
@api.depends('seats', 'attendee_ids')
|
||||||
|
def _taken_seats(self):
|
||||||
|
for r in self:
|
||||||
|
@@ -106,6 +109,11 @@ class Session(models.Model):
|
||||||
|
# so add one day to get 5 days instead
|
||||||
|
r.duration = (r.end_date - r.start_date).days + 1
|
||||||
|
|
||||||
|
+ @api.depends('attendee_ids')
|
||||||
|
+ def _get_attendees_count(self):
|
||||||
|
+ for r in self:
|
||||||
|
+ r.attendees_count = len(r.attendee_ids)
|
||||||
|
+
|
||||||
|
@api.constrains('instructor_id', 'attendee_ids')
|
||||||
|
def _check_instructor_not_in_attendees(self):
|
||||||
|
for r in self:
|
||||||
|
diff --git a/openacademy/views/openacademy.xml b/openacademy/views/openacademy.xml
|
||||||
|
--- a/openacademy/views/openacademy.xml
|
||||||
|
+++ b/openacademy/views/openacademy.xml
|
||||||
|
@@ -153,10 +153,21 @@
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
+ <record model="ir.ui.view" id="openacademy_session_graph_view">
|
||||||
|
+ <field name="name">openacademy.session.graph</field>
|
||||||
|
+ <field name="model">openacademy.session</field>
|
||||||
|
+ <field name="arch" type="xml">
|
||||||
|
+ <graph string="Participations by Courses">
|
||||||
|
+ <field name="course_id"/>
|
||||||
|
+ <field name="attendees_count" type="measure"/>
|
||||||
|
+ </graph>
|
||||||
|
+ </field>
|
||||||
|
+ </record>
|
||||||
|
+
|
||||||
|
<record model="ir.actions.act_window" id="session_list_action">
|
||||||
|
<field name="name">Sessions</field>
|
||||||
|
<field name="res_model">openacademy.session</field>
|
||||||
|
- <field name="view_mode">tree,form,calendar,gantt</field>
|
||||||
|
+ <field name="view_mode">tree,form,calendar,gantt,graph</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<menuitem id="session_menu" name="Sessions"
|
77
developer/howtos/backend/exercise-kanban
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
# HG changeset patch
|
||||||
|
# Parent 8d66f7620781558d4520f97e4cebc14ed180683e
|
||||||
|
|
||||||
|
Index: addons/openacademy/models.py
|
||||||
|
===================================================================
|
||||||
|
--- addons.orig/openacademy/models.py
|
||||||
|
+++ addons/openacademy/models.py
|
||||||
|
@@ -49,6 +49,7 @@
|
||||||
|
duration = fields.Float(digits=(6, 2), help="Duration in days")
|
||||||
|
seats = fields.Integer(string="Number of seats")
|
||||||
|
active = fields.Boolean(default=True)
|
||||||
|
+ color = fields.Integer()
|
||||||
|
|
||||||
|
instructor_id = fields.Many2one('res.partner', string="Instructor",
|
||||||
|
domain=['|', ('instructor', '=', True),
|
||||||
|
Index: addons/openacademy/views/openacademy.xml
|
||||||
|
===================================================================
|
||||||
|
--- addons.orig/openacademy/views/openacademy.xml
|
||||||
|
+++ addons/openacademy/views/openacademy.xml
|
||||||
|
@@ -165,10 +165,56 @@
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
+ <record model="ir.ui.view" id="view_openacad_session_kanban">
|
||||||
|
+ <field name="name">openacademy.session.kanban</field>
|
||||||
|
+ <field name="model">openacademy.session</field>
|
||||||
|
+ <field name="arch" type="xml">
|
||||||
|
+ <kanban default_group_by="course_id">
|
||||||
|
+ <field name="color"/>
|
||||||
|
+ <templates>
|
||||||
|
+ <t t-name="kanban-box">
|
||||||
|
+ <div
|
||||||
|
+ t-attf-class="oe_kanban_color_{{kanban_getcolor(record.color.raw_value)}}
|
||||||
|
+ oe_kanban_global_click_edit oe_semantic_html_override
|
||||||
|
+ oe_kanban_card {{record.group_fancy==1 ? 'oe_kanban_card_fancy' : ''}}">
|
||||||
|
+ <div class="oe_dropdown_kanban">
|
||||||
|
+ <!-- dropdown menu -->
|
||||||
|
+ <div class="oe_dropdown_toggle">
|
||||||
|
+ <i class="fa fa-bars fa-lg" title="Manage" aria-label="Manage"/>
|
||||||
|
+ <ul class="oe_dropdown_menu">
|
||||||
|
+ <li>
|
||||||
|
+ <a type="delete">Delete</a>
|
||||||
|
+ </li>
|
||||||
|
+ <li>
|
||||||
|
+ <ul class="oe_kanban_colorpicker"
|
||||||
|
+ data-field="color"/>
|
||||||
|
+ </li>
|
||||||
|
+ </ul>
|
||||||
|
+ </div>
|
||||||
|
+ <div class="oe_clear"></div>
|
||||||
|
+ </div>
|
||||||
|
+ <div t-attf-class="oe_kanban_content">
|
||||||
|
+ <!-- title -->
|
||||||
|
+ Session name:
|
||||||
|
+ <field name="name"/>
|
||||||
|
+ <br/>
|
||||||
|
+ Start date:
|
||||||
|
+ <field name="start_date"/>
|
||||||
|
+ <br/>
|
||||||
|
+ duration:
|
||||||
|
+ <field name="duration"/>
|
||||||
|
+ </div>
|
||||||
|
+ </div>
|
||||||
|
+ </t>
|
||||||
|
+ </templates>
|
||||||
|
+ </kanban>
|
||||||
|
+ </field>
|
||||||
|
+ </record>
|
||||||
|
+
|
||||||
|
<record model="ir.actions.act_window" id="session_list_action">
|
||||||
|
<field name="name">Sessions</field>
|
||||||
|
<field name="res_model">openacademy.session</field>
|
||||||
|
- <field name="view_mode">tree,form,calendar,gantt,graph</field>
|
||||||
|
+ <field name="view_mode">tree,form,calendar,gantt,graph,kanban</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<menuitem id="session_menu" name="Sessions"
|
22
developer/howtos/backend/exercise-many2many
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
Index: addons/openacademy/models.py
|
||||||
|
===================================================================
|
||||||
|
--- addons.orig/openacademy/models.py
|
||||||
|
+++ addons/openacademy/models.py
|
||||||
|
@@ -27,3 +27,4 @@
|
||||||
|
instructor_id = fields.Many2one('res.partner', string="Instructor")
|
||||||
|
course_id = fields.Many2one('openacademy.course',
|
||||||
|
ondelete='cascade', string="Course", required=True)
|
||||||
|
+ attendee_ids = fields.Many2many('res.partner', string="Attendees")
|
||||||
|
Index: addons/openacademy/views/openacademy.xml
|
||||||
|
===================================================================
|
||||||
|
--- addons.orig/openacademy/views/openacademy.xml
|
||||||
|
+++ addons/openacademy/views/openacademy.xml
|
||||||
|
@@ -101,6 +101,8 @@
|
||||||
|
<field name="seats"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
+ <label for="attendee_ids"/>
|
||||||
|
+ <field name="attendee_ids"/>
|
||||||
|
</sheet>
|
||||||
|
</form>
|
||||||
|
</field>
|
95
developer/howtos/backend/exercise-many2one
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
# HG changeset patch
|
||||||
|
# Parent a6e217b1fbbc64111581c269629b1c25c23abb99
|
||||||
|
|
||||||
|
Index: addons/openacademy/models.py
|
||||||
|
===================================================================
|
||||||
|
--- addons.orig/openacademy/models.py
|
||||||
|
+++ addons/openacademy/models.py
|
||||||
|
@@ -9,6 +9,9 @@
|
||||||
|
name = fields.Char(string="Title", required=True)
|
||||||
|
description = fields.Text()
|
||||||
|
|
||||||
|
+ responsible_id = fields.Many2one('res.users',
|
||||||
|
+ ondelete='set null', string="Responsible", index=True)
|
||||||
|
+
|
||||||
|
|
||||||
|
class Session(models.Model):
|
||||||
|
_name = 'openacademy.session'
|
||||||
|
@@ -18,3 +21,7 @@
|
||||||
|
start_date = fields.Date()
|
||||||
|
duration = fields.Float(digits=(6, 2), help="Duration in days")
|
||||||
|
seats = fields.Integer(string="Number of seats")
|
||||||
|
+
|
||||||
|
+ instructor_id = fields.Many2one('res.partner', string="Instructor")
|
||||||
|
+ course_id = fields.Many2one('openacademy.course',
|
||||||
|
+ ondelete='cascade', string="Course", required=True)
|
||||||
|
Index: addons/openacademy/views/openacademy.xml
|
||||||
|
===================================================================
|
||||||
|
--- addons.orig/openacademy/views/openacademy.xml
|
||||||
|
+++ addons/openacademy/views/openacademy.xml
|
||||||
|
@@ -9,6 +9,7 @@
|
||||||
|
<sheet>
|
||||||
|
<group>
|
||||||
|
<field name="name"/>
|
||||||
|
+ <field name="responsible_id"/>
|
||||||
|
</group>
|
||||||
|
<notebook>
|
||||||
|
<page string="Description">
|
||||||
|
@@ -34,6 +35,18 @@
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
+ <!-- override the automatically generated list view for courses -->
|
||||||
|
+ <record model="ir.ui.view" id="course_tree_view">
|
||||||
|
+ <field name="name">course.tree</field>
|
||||||
|
+ <field name="model">openacademy.course</field>
|
||||||
|
+ <field name="arch" type="xml">
|
||||||
|
+ <tree string="Course Tree">
|
||||||
|
+ <field name="name"/>
|
||||||
|
+ <field name="responsible_id"/>
|
||||||
|
+ </tree>
|
||||||
|
+ </field>
|
||||||
|
+ </record>
|
||||||
|
+
|
||||||
|
<!-- window action -->
|
||||||
|
<!--
|
||||||
|
The following tag is an action definition for a "window action",
|
||||||
|
@@ -72,16 +85,34 @@
|
||||||
|
<form string="Session Form">
|
||||||
|
<sheet>
|
||||||
|
<group>
|
||||||
|
- <field name="name"/>
|
||||||
|
- <field name="start_date"/>
|
||||||
|
- <field name="duration"/>
|
||||||
|
- <field name="seats"/>
|
||||||
|
+ <group string="General">
|
||||||
|
+ <field name="course_id"/>
|
||||||
|
+ <field name="name"/>
|
||||||
|
+ <field name="instructor_id"/>
|
||||||
|
+ </group>
|
||||||
|
+ <group string="Schedule">
|
||||||
|
+ <field name="start_date"/>
|
||||||
|
+ <field name="duration"/>
|
||||||
|
+ <field name="seats"/>
|
||||||
|
+ </group>
|
||||||
|
</group>
|
||||||
|
</sheet>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
+ <!-- session tree/list view -->
|
||||||
|
+ <record model="ir.ui.view" id="session_tree_view">
|
||||||
|
+ <field name="name">session.tree</field>
|
||||||
|
+ <field name="model">openacademy.session</field>
|
||||||
|
+ <field name="arch" type="xml">
|
||||||
|
+ <tree string="Session Tree">
|
||||||
|
+ <field name="name"/>
|
||||||
|
+ <field name="course_id"/>
|
||||||
|
+ </tree>
|
||||||
|
+ </field>
|
||||||
|
+ </record>
|
||||||
|
+
|
||||||
|
<record model="ir.actions.act_window" id="session_list_action">
|
||||||
|
<field name="name">Sessions</field>
|
||||||
|
<field name="res_model">openacademy.session</field>
|
19
developer/howtos/backend/exercise-model
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# HG changeset patch
|
||||||
|
# Parent e3bb12713a6d38c28f50d46e8c1bab74ac40c1be
|
||||||
|
Index: addons/openacademy/models.py
|
||||||
|
===================================================================
|
||||||
|
--- addons.orig/openacademy/models.py
|
||||||
|
+++ addons/openacademy/models.py
|
||||||
|
@@ -2,7 +2,9 @@
|
||||||
|
|
||||||
|
from odoo import models, fields, api
|
||||||
|
|
||||||
|
-# class openacademy(models.Model):
|
||||||
|
-# _name = 'openacademy.openacademy'
|
||||||
|
+class Course(models.Model):
|
||||||
|
+ _name = 'openacademy.course'
|
||||||
|
+ _description = "OpenAcademy Courses"
|
||||||
|
|
||||||
|
-# name = fields.Char()
|
||||||
|
+ name = fields.Char(string="Title", required=True)
|
||||||
|
+ description = fields.Text()
|
78
developer/howtos/backend/exercise-model-inheritance
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
# HG changeset patch
|
||||||
|
# Parent d903c828fb10f2b38e5f43e9ceaeae0a9db7f858
|
||||||
|
|
||||||
|
Index: addons/openacademy/__init__.py
|
||||||
|
===================================================================
|
||||||
|
--- addons.orig/openacademy/__init__.py 2014-08-26 17:26:01.227783353 +0200
|
||||||
|
+++ addons/openacademy/__init__.py 2014-08-26 17:26:01.219783354 +0200
|
||||||
|
@@ -1,3 +1,4 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from . import controllers
|
||||||
|
from . import models
|
||||||
|
+from . import partner
|
||||||
|
Index: addons/openacademy/__manifest__.py
|
||||||
|
===================================================================
|
||||||
|
--- addons.orig/openacademy/__manifest__.py 2014-08-26 17:26:01.227783353 +0200
|
||||||
|
+++ addons/openacademy/__manifest__.py 2014-08-26 17:26:01.223783354 +0200
|
||||||
|
@@ -28,6 +28,7 @@
|
||||||
|
# 'security/ir.model.access.csv',
|
||||||
|
'templates.xml',
|
||||||
|
'views/openacademy.xml',
|
||||||
|
+ 'views/partner.xml',
|
||||||
|
],
|
||||||
|
# only loaded in demonstration mode
|
||||||
|
'demo': [
|
||||||
|
Index: addons/openacademy/partner.py
|
||||||
|
===================================================================
|
||||||
|
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
|
||||||
|
+++ addons/openacademy/partner.py 2014-08-26 17:26:01.223783354 +0200
|
||||||
|
@@ -0,0 +1,12 @@
|
||||||
|
+# -*- coding: utf-8 -*-
|
||||||
|
+from odoo import fields, models
|
||||||
|
+
|
||||||
|
+class Partner(models.Model):
|
||||||
|
+ _inherit = 'res.partner'
|
||||||
|
+
|
||||||
|
+ # Add a new column to the res.partner model, by default partners are not
|
||||||
|
+ # instructors
|
||||||
|
+ instructor = fields.Boolean("Instructor", default=False)
|
||||||
|
+
|
||||||
|
+ session_ids = fields.Many2many('openacademy.session',
|
||||||
|
+ string="Attended Sessions", readonly=True)
|
||||||
|
Index: addons/openacademy/views/partner.xml
|
||||||
|
===================================================================
|
||||||
|
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
|
||||||
|
+++ addons/openacademy/views/partner.xml 2014-08-26 17:26:01.223783354 +0200
|
||||||
|
@@ -0,0 +1,32 @@
|
||||||
|
+<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
+ <odoo>
|
||||||
|
+
|
||||||
|
+ <!-- Add instructor field to existing view -->
|
||||||
|
+ <record model="ir.ui.view" id="partner_instructor_form_view">
|
||||||
|
+ <field name="name">partner.instructor</field>
|
||||||
|
+ <field name="model">res.partner</field>
|
||||||
|
+ <field name="inherit_id" ref="base.view_partner_form"/>
|
||||||
|
+ <field name="arch" type="xml">
|
||||||
|
+ <notebook position="inside">
|
||||||
|
+ <page string="Sessions">
|
||||||
|
+ <group>
|
||||||
|
+ <field name="instructor"/>
|
||||||
|
+ <field name="session_ids"/>
|
||||||
|
+ </group>
|
||||||
|
+ </page>
|
||||||
|
+ </notebook>
|
||||||
|
+ </field>
|
||||||
|
+ </record>
|
||||||
|
+
|
||||||
|
+ <record model="ir.actions.act_window" id="contact_list_action">
|
||||||
|
+ <field name="name">Contacts</field>
|
||||||
|
+ <field name="res_model">res.partner</field>
|
||||||
|
+ <field name="view_mode">tree,form</field>
|
||||||
|
+ </record>
|
||||||
|
+ <menuitem id="configuration_menu" name="Configuration"
|
||||||
|
+ parent="main_openacademy_menu"/>
|
||||||
|
+ <menuitem id="contact_menu" name="Contacts"
|
||||||
|
+ parent="configuration_menu"
|
||||||
|
+ action="contact_list_action"/>
|
||||||
|
+
|
||||||
|
+</odoo>
|
28
developer/howtos/backend/exercise-onchange
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# HG changeset patch
|
||||||
|
# Parent 8d5573b704b2867788dd6895503f1871c2976a29
|
||||||
|
# Parent 9eb163e5da677a0d09e01a354ba56697b576a4bc
|
||||||
|
|
||||||
|
diff --git a/openacademy/models.py b/openacademy/models.py
|
||||||
|
--- a/openacademy/models.py
|
||||||
|
+++ b/openacademy/models.py
|
||||||
|
@@ -41,3 +41,20 @@ class Session(models.Model):
|
||||||
|
r.taken_seats = 0.0
|
||||||
|
else:
|
||||||
|
r.taken_seats = 100.0 * len(r.attendee_ids) / r.seats
|
||||||
|
+
|
||||||
|
+ @api.onchange('seats', 'attendee_ids')
|
||||||
|
+ def _verify_valid_seats(self):
|
||||||
|
+ if self.seats < 0:
|
||||||
|
+ return {
|
||||||
|
+ 'warning': {
|
||||||
|
+ 'title': "Incorrect 'seats' value",
|
||||||
|
+ 'message': "The number of available seats may not be negative",
|
||||||
|
+ },
|
||||||
|
+ }
|
||||||
|
+ if self.seats < len(self.attendee_ids):
|
||||||
|
+ return {
|
||||||
|
+ 'warning': {
|
||||||
|
+ 'title': "Too many attendees",
|
||||||
|
+ 'message': "Increase seats or remove excess attendees",
|
||||||
|
+ },
|
||||||
|
+ }
|
36
developer/howtos/backend/exercise-one2many
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
# HG changeset patch
|
||||||
|
# Parent cb05882d4fe73e97b9d34a69190ced14d1a50c24
|
||||||
|
|
||||||
|
Index: addons/openacademy/models.py
|
||||||
|
===================================================================
|
||||||
|
--- addons.orig/openacademy/models.py
|
||||||
|
+++ addons/openacademy/models.py
|
||||||
|
@@ -11,6 +11,8 @@
|
||||||
|
|
||||||
|
responsible_id = fields.Many2one('res.users',
|
||||||
|
ondelete='set null', string="Responsible", index=True)
|
||||||
|
+ session_ids = fields.One2many(
|
||||||
|
+ 'openacademy.session', 'course_id', string="Sessions")
|
||||||
|
|
||||||
|
|
||||||
|
class Session(models.Model):
|
||||||
|
Index: addons/openacademy/views/openacademy.xml
|
||||||
|
===================================================================
|
||||||
|
--- addons.orig/openacademy/views/openacademy.xml
|
||||||
|
+++ addons/openacademy/views/openacademy.xml
|
||||||
|
@@ -15,8 +15,13 @@
|
||||||
|
<page string="Description">
|
||||||
|
<field name="description"/>
|
||||||
|
</page>
|
||||||
|
- <page string="About">
|
||||||
|
- This is an example of notebooks
|
||||||
|
+ <page string="Sessions">
|
||||||
|
+ <field name="session_ids">
|
||||||
|
+ <tree string="Registered sessions">
|
||||||
|
+ <field name="name"/>
|
||||||
|
+ <field name="instructor_id"/>
|
||||||
|
+ </tree>
|
||||||
|
+ </field>
|
||||||
|
</page>
|
||||||
|
</notebook>
|
||||||
|
</sheet>
|
52
developer/howtos/backend/exercise-report
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
# HG changeset patch
|
||||||
|
# Parent c140f0a861a08881d8737bca0ffb83904a2059a3
|
||||||
|
|
||||||
|
Index: addons/openacademy/__manifest__.py
|
||||||
|
===================================================================
|
||||||
|
--- addons.orig/openacademy/__manifest__.py 2014-08-29 08:39:43.975536806 +0200
|
||||||
|
+++ addons/openacademy/__manifest__.py 2014-08-29 08:39:52.000000000 +0200
|
||||||
|
@@ -30,6 +30,7 @@
|
||||||
|
'templates.xml',
|
||||||
|
'views/openacademy.xml',
|
||||||
|
'views/partner.xml',
|
||||||
|
+ 'reports.xml',
|
||||||
|
],
|
||||||
|
# only loaded in demonstration mode
|
||||||
|
'demo': [
|
||||||
|
Index: foo/openacademy/reports.xml
|
||||||
|
===================================================================
|
||||||
|
--- /dev/null
|
||||||
|
+++ foo/openacademy/reports.xml
|
||||||
|
@@ -0,0 +1,32 @@
|
||||||
|
+<odoo>
|
||||||
|
+
|
||||||
|
+ <record id="report_session" model="ir.actions.report">
|
||||||
|
+ <field name="name">Session Report</field>
|
||||||
|
+ <field name="model">openacademy.session</field>
|
||||||
|
+ <field name="report_type">qweb-pdf</field>
|
||||||
|
+ <field name="report_name">openacademy.report_session_view</field>
|
||||||
|
+ <field name="report_file">openacademy.report_session</field>
|
||||||
|
+ <field name="binding_model_id" ref="model_openacademy_session"/>
|
||||||
|
+ <field name="binding_type">report</field>
|
||||||
|
+ </record>
|
||||||
|
+
|
||||||
|
+ <template id="report_session_view">
|
||||||
|
+ <t t-call="web.html_container">
|
||||||
|
+ <t t-foreach="docs" t-as="doc">
|
||||||
|
+ <t t-call="web.external_layout">
|
||||||
|
+ <div class="page">
|
||||||
|
+ <h2 t-field="doc.name"/>
|
||||||
|
+ <p>From <span t-field="doc.start_date"/> to <span t-field="doc.end_date"/></p>
|
||||||
|
+ <h3>Attendees:</h3>
|
||||||
|
+ <ul>
|
||||||
|
+ <t t-foreach="doc.attendee_ids" t-as="attendee">
|
||||||
|
+ <li><span t-field="attendee.name"/></li>
|
||||||
|
+ </t>
|
||||||
|
+ </ul>
|
||||||
|
+ </div>
|
||||||
|
+ </t>
|
||||||
|
+ </t>
|
||||||
|
+ </t>
|
||||||
|
+ </template>
|
||||||
|
+
|
||||||
|
+</odoo>
|
28
developer/howtos/backend/exercise-searchview
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# HG changeset patch
|
||||||
|
# Parent 93a45ab8dd0a76c131cb5eeca6e44b71dca9f100
|
||||||
|
|
||||||
|
Index: addons/openacademy/views/openacademy.xml
|
||||||
|
===================================================================
|
||||||
|
--- addons.orig/openacademy/views/openacademy.xml 2014-08-28 14:01:45.299033618 +0200
|
||||||
|
+++ addons/openacademy/views/openacademy.xml 2014-08-28 14:18:58.847018275 +0200
|
||||||
|
@@ -36,6 +36,12 @@
|
||||||
|
<search>
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="description"/>
|
||||||
|
+ <filter name="my_courses" string="My Courses"
|
||||||
|
+ domain="[('responsible_id', '=', uid)]"/>
|
||||||
|
+ <group string="Group By">
|
||||||
|
+ <filter name="by_responsible" string="Responsible"
|
||||||
|
+ context="{'group_by': 'responsible_id'}"/>
|
||||||
|
+ </group>
|
||||||
|
</search>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
@@ -61,6 +67,7 @@
|
||||||
|
<field name="name">Courses</field>
|
||||||
|
<field name="res_model">openacademy.course</field>
|
||||||
|
<field name="view_mode">tree,form</field>
|
||||||
|
+ <field name="context" eval="{'search_default_my_courses': 1}"/>
|
||||||
|
<field name="help" type="html">
|
||||||
|
<p class="o_view_nocontent_smiling_face">Create the first course
|
||||||
|
</p>
|
24
developer/howtos/backend/exercise-searchview-basic
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# HG changeset patch
|
||||||
|
# Parent b9bfc8929e0ffc3eb153641e14952fe5d99eb908
|
||||||
|
Index: addons/openacademy/views/openacademy.xml
|
||||||
|
===================================================================
|
||||||
|
--- addons.orig/openacademy/views/openacademy.xml 2014-08-26 17:25:55.807783434 +0200
|
||||||
|
+++ addons/openacademy/views/openacademy.xml 2014-08-26 17:25:55.799783434 +0200
|
||||||
|
@@ -23,6 +23,17 @@
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
+ <record model="ir.ui.view" id="course_search_view">
|
||||||
|
+ <field name="name">course.search</field>
|
||||||
|
+ <field name="model">openacademy.course</field>
|
||||||
|
+ <field name="arch" type="xml">
|
||||||
|
+ <search>
|
||||||
|
+ <field name="name"/>
|
||||||
|
+ <field name="description"/>
|
||||||
|
+ </search>
|
||||||
|
+ </field>
|
||||||
|
+ </record>
|
||||||
|
+
|
||||||
|
<!-- window action -->
|
||||||
|
<!--
|
||||||
|
The following tag is an action definition for a "window action",
|
57
developer/howtos/backend/exercise-session
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
# HG changeset patch
|
||||||
|
# Parent 22f8d180a7f9ad209d7e98cf7d1bd0fee1f05350
|
||||||
|
Index: addons/openacademy/models.py
|
||||||
|
===================================================================
|
||||||
|
--- addons.orig/openacademy/models.py
|
||||||
|
+++ addons/openacademy/models.py
|
||||||
|
@@ -8,3 +8,13 @@
|
||||||
|
|
||||||
|
name = fields.Char(string="Title", required=True)
|
||||||
|
description = fields.Text()
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+class Session(models.Model):
|
||||||
|
+ _name = 'openacademy.session'
|
||||||
|
+ _description = "OpenAcademy Sessions"
|
||||||
|
+
|
||||||
|
+ name = fields.Char(required=True)
|
||||||
|
+ start_date = fields.Date()
|
||||||
|
+ duration = fields.Float(digits=(6, 2), help="Duration in days")
|
||||||
|
+ seats = fields.Integer(string="Number of seats")
|
||||||
|
Index: addons/openacademy/views/openacademy.xml
|
||||||
|
===================================================================
|
||||||
|
--- addons.orig/openacademy/views/openacademy.xml
|
||||||
|
+++ addons/openacademy/views/openacademy.xml
|
||||||
|
@@ -64,4 +64,32 @@
|
||||||
|
action="openacademy.course_list_action"
|
||||||
|
It is not required when it is the same module -->
|
||||||
|
|
||||||
|
+ <!-- session form view -->
|
||||||
|
+ <record model="ir.ui.view" id="session_form_view">
|
||||||
|
+ <field name="name">session.form</field>
|
||||||
|
+ <field name="model">openacademy.session</field>
|
||||||
|
+ <field name="arch" type="xml">
|
||||||
|
+ <form string="Session Form">
|
||||||
|
+ <sheet>
|
||||||
|
+ <group>
|
||||||
|
+ <field name="name"/>
|
||||||
|
+ <field name="start_date"/>
|
||||||
|
+ <field name="duration"/>
|
||||||
|
+ <field name="seats"/>
|
||||||
|
+ </group>
|
||||||
|
+ </sheet>
|
||||||
|
+ </form>
|
||||||
|
+ </field>
|
||||||
|
+ </record>
|
||||||
|
+
|
||||||
|
+ <record model="ir.actions.act_window" id="session_list_action">
|
||||||
|
+ <field name="name">Sessions</field>
|
||||||
|
+ <field name="res_model">openacademy.session</field>
|
||||||
|
+ <field name="view_mode">tree,form</field>
|
||||||
|
+ </record>
|
||||||
|
+
|
||||||
|
+ <menuitem id="session_menu" name="Sessions"
|
||||||
|
+ parent="openacademy_menu"
|
||||||
|
+ action="session_list_action"/>
|
||||||
|
+
|
||||||
|
</odoo>
|
56
developer/howtos/backend/exercise-translations
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
# HG changeset patch
|
||||||
|
# Parent 7c95aad3b60e4c2006c5f706bd157e8e05318bfa
|
||||||
|
|
||||||
|
diff --git a/openacademy/models.py b/openacademy/models.py
|
||||||
|
--- a/openacademy/models.py
|
||||||
|
+++ b/openacademy/models.py
|
||||||
|
@@ -1,7 +1,7 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from datetime import timedelta
|
||||||
|
-from odoo import models, fields, api, exceptions
|
||||||
|
+from odoo import models, fields, api, exceptions, _
|
||||||
|
|
||||||
|
class Course(models.Model):
|
||||||
|
_name = 'openacademy.course'
|
||||||
|
@@ -20,11 +20,11 @@ class Course(models.Model):
|
||||||
|
default = dict(default or {})
|
||||||
|
|
||||||
|
copied_count = self.search_count(
|
||||||
|
- [('name', '=like', u"Copy of {}%".format(self.name))])
|
||||||
|
+ [('name', '=like', _(u"Copy of {}%").format(self.name))])
|
||||||
|
if not copied_count:
|
||||||
|
- new_name = u"Copy of {}".format(self.name)
|
||||||
|
+ new_name = _(u"Copy of {}").format(self.name)
|
||||||
|
else:
|
||||||
|
- new_name = u"Copy of {} ({})".format(self.name, copied_count)
|
||||||
|
+ new_name = _(u"Copy of {} ({})").format(self.name, copied_count)
|
||||||
|
|
||||||
|
default['name'] = new_name
|
||||||
|
return super(Course, self).copy(default)
|
||||||
|
@@ -81,15 +81,15 @@ class Session(models.Model):
|
||||||
|
if self.seats < 0:
|
||||||
|
return {
|
||||||
|
'warning': {
|
||||||
|
- 'title': "Incorrect 'seats' value",
|
||||||
|
- 'message': "The number of available seats may not be negative",
|
||||||
|
+ 'title': _("Incorrect 'seats' value"),
|
||||||
|
+ 'message': _("The number of available seats may not be negative"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if self.seats < len(self.attendee_ids):
|
||||||
|
return {
|
||||||
|
'warning': {
|
||||||
|
- 'title': "Too many attendees",
|
||||||
|
- 'message': "Increase seats or remove excess attendees",
|
||||||
|
+ 'title': _("Too many attendees"),
|
||||||
|
+ 'message': _("Increase seats or remove excess attendees"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -114,4 +114,4 @@ class Session(models.Model):
|
||||||
|
def _check_instructor_not_in_attendees(self):
|
||||||
|
for r in self:
|
||||||
|
if r.instructor_id and r.instructor_id in r.attendee_ids:
|
||||||
|
- raise exceptions.ValidationError("A session's instructor can't be an attendee")
|
||||||
|
+ raise exceptions.ValidationError(_("A session's instructor can't be an attendee"))
|
36
developer/howtos/backend/exercise-wizard
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
Index: addons/openacademy/__init__.py
|
||||||
|
===================================================================
|
||||||
|
--- addons.orig/openacademy/__init__.py
|
||||||
|
+++ addons/openacademy/__init__.py
|
||||||
|
@@ -2,3 +2,4 @@
|
||||||
|
from . import controllers
|
||||||
|
from . import models
|
||||||
|
from . import partner
|
||||||
|
+from . import wizard
|
||||||
|
Index: addons/openacademy/wizard.py
|
||||||
|
===================================================================
|
||||||
|
--- /dev/null
|
||||||
|
+++ addons/openacademy/wizard.py
|
||||||
|
@@ -0,0 +1,11 @@
|
||||||
|
+# -*- coding: utf-8 -*-
|
||||||
|
+
|
||||||
|
+from odoo import models, fields, api
|
||||||
|
+
|
||||||
|
+class Wizard(models.TransientModel):
|
||||||
|
+ _name = 'openacademy.wizard'
|
||||||
|
+ _description = "Wizard: Quick Registration of Attendees to Sessions"
|
||||||
|
+
|
||||||
|
+ session_id = fields.Many2one('openacademy.session',
|
||||||
|
+ string="Session", required=True)
|
||||||
|
+ attendee_ids = fields.Many2many('res.partner', string="Attendees")
|
||||||
|
Index: foo/openacademy/security/ir.model.access.csv
|
||||||
|
===================================================================
|
||||||
|
--- foo.orig/openacademy/security/ir.model.access.csv
|
||||||
|
+++ foo/openacademy/security/ir.model.access.csv
|
||||||
|
@@ -1,5 +1,6 @@
|
||||||
|
id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink
|
||||||
|
course_manager,course manager,model_openacademy_course,group_manager,1,1,1,1
|
||||||
|
session_manager,session manager,model_openacademy_session,group_manager,1,1,1,1
|
||||||
|
+wizard_manager,wizard session manager,model_openacademy_wizard,group_manager,1,1,1,1
|
||||||
|
course_read_all,course all,model_openacademy_course,,1,0,0,0
|
||||||
|
session_read_all,session all,model_openacademy_session,,1,0,0,0
|
29
developer/howtos/backend/exercise-wizard-action
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
Index: addons/openacademy/views/openacademy.xml
|
||||||
|
===================================================================
|
||||||
|
--- addons.orig/openacademy/views/openacademy.xml
|
||||||
|
+++ addons/openacademy/views/openacademy.xml
|
||||||
|
@@ -232,6 +232,12 @@
|
||||||
|
<field name="session_id"/>
|
||||||
|
<field name="attendee_ids"/>
|
||||||
|
</group>
|
||||||
|
+ <footer>
|
||||||
|
+ <button name="subscribe" type="object"
|
||||||
|
+ string="Subscribe" class="oe_highlight"/>
|
||||||
|
+ or
|
||||||
|
+ <button special="cancel" string="Cancel"/>
|
||||||
|
+ </footer>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
Index: addons/openacademy/wizard.py
|
||||||
|
===================================================================
|
||||||
|
--- addons.orig/openacademy/wizard.py
|
||||||
|
+++ addons/openacademy/wizard.py
|
||||||
|
@@ -12,3 +12,7 @@
|
||||||
|
session_id = fields.Many2one('openacademy.session',
|
||||||
|
string="Session", required=True, default=_default_session)
|
||||||
|
attendee_ids = fields.Many2many('res.partner', string="Attendees")
|
||||||
|
+
|
||||||
|
+ def subscribe(self):
|
||||||
|
+ self.session_id.attendee_ids |= self.attendee_ids
|
||||||
|
+ return {}
|