[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 {}
|