From 37ed23b787408d62d6920d937a98d7e1270fe305 Mon Sep 17 00:00:00 2001
From: benjamin <benjamin@le-filament.com>
Date: Mon, 11 Apr 2022 12:34:59 +0200
Subject: [PATCH] [mig] Migration 13.0

---
 .editorconfig                          |   20 +
 .eslintrc.yml                          |  187 +++
 .flake8                                |   12 +
 .gitignore                             |   75 ++
 .isort.cfg                             |   13 +
 .pre-commit-config.yaml                |  127 ++
 .prettierrc.yml                        |    8 +
 .pylintrc                              |   87 ++
 .pylintrc-mandatory                    |   64 +
 __manifest__.py                        |   25 +-
 models/__init__.py                     |    1 -
 models/ir_action.py                    |    5 +-
 models/ir_http.py                      |    7 +-
 models/ir_view.py                      |    5 +-
 models/res_users.py                    |   10 +-
 static/src/css/cgscop_fullcalendar.css |   27 +-
 static/src/js/calendar_controller.js   |   56 +-
 static/src/js/calendar_model.js        |   18 +-
 static/src/js/calendar_renderer.js     |  234 ++--
 static/src/js/calendar_view.js         |   21 +-
 static/src/js/resource_controller.js   |  839 +++++++------
 static/src/js/resource_model.js        | 1546 +++++++++++++-----------
 static/src/js/resource_renderer.js     | 1046 ++++++++--------
 static/src/js/resource_view.js         |  326 ++---
 static/src/xml/resource_view.xml       |  103 +-
 views/assets.xml                       |  128 +-
 views/res_users.xml                    |   11 +-
 27 files changed, 2966 insertions(+), 2035 deletions(-)
 create mode 100644 .editorconfig
 create mode 100644 .eslintrc.yml
 create mode 100644 .flake8
 create mode 100644 .gitignore
 create mode 100644 .isort.cfg
 create mode 100644 .pre-commit-config.yaml
 create mode 100644 .prettierrc.yml
 create mode 100644 .pylintrc
 create mode 100644 .pylintrc-mandatory

diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..bfd7ac5
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,20 @@
+# Configuration for known file extensions
+[*.{css,js,json,less,md,py,rst,sass,scss,xml,yaml,yml}]
+charset = utf-8
+end_of_line = lf
+indent_size = 4
+indent_style = space
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.{json,yml,yaml,rst,md}]
+indent_size = 2
+
+# Do not configure editor for libs and autogenerated content
+[{*/static/{lib,src/lib}/**,*/static/description/index.html,*/readme/../README.rst}]
+charset = unset
+end_of_line = unset
+indent_size = unset
+indent_style = unset
+insert_final_newline = false
+trim_trailing_whitespace = false
diff --git a/.eslintrc.yml b/.eslintrc.yml
new file mode 100644
index 0000000..d4cc423
--- /dev/null
+++ b/.eslintrc.yml
@@ -0,0 +1,187 @@
+env:
+  browser: true
+  es6: true
+
+# See https://github.com/OCA/odoo-community.org/issues/37#issuecomment-470686449
+parserOptions:
+  ecmaVersion: 2017
+
+overrides:
+  - files:
+      - "**/*.esm.js"
+    parserOptions:
+      sourceType: module
+
+# Globals available in Odoo that shouldn't produce errorings
+globals:
+  _: readonly
+  $: readonly
+  fuzzy: readonly
+  jQuery: readonly
+  moment: readonly
+  odoo: readonly
+  openerp: readonly
+  owl: readonly
+
+# Styling is handled by Prettier, so we only need to enable AST rules;
+# see https://github.com/OCA/maintainer-quality-tools/pull/618#issuecomment-558576890
+rules:
+  accessor-pairs: warn
+  array-callback-return: warn
+  callback-return: warn
+  capitalized-comments:
+    - warn
+    - always
+    - ignoreConsecutiveComments: true
+      ignoreInlineComments: true
+  complexity:
+    - warn
+    - 15
+  constructor-super: warn
+  dot-notation: warn
+  eqeqeq: warn
+  global-require: warn
+  handle-callback-err: warn
+  id-blacklist: warn
+  id-match: warn
+  init-declarations: error
+  max-depth: warn
+  max-nested-callbacks: warn
+  max-statements-per-line: warn
+  no-alert: warn
+  no-array-constructor: warn
+  no-caller: warn
+  no-case-declarations: warn
+  no-class-assign: warn
+  no-cond-assign: error
+  no-const-assign: error
+  no-constant-condition: warn
+  no-control-regex: warn
+  no-debugger: error
+  no-delete-var: warn
+  no-div-regex: warn
+  no-dupe-args: error
+  no-dupe-class-members: error
+  no-dupe-keys: error
+  no-duplicate-case: error
+  no-duplicate-imports: error
+  no-else-return: warn
+  no-empty-character-class: warn
+  no-empty-function: error
+  no-empty-pattern: error
+  no-empty: warn
+  no-eq-null: error
+  no-eval: error
+  no-ex-assign: error
+  no-extend-native: warn
+  no-extra-bind: warn
+  no-extra-boolean-cast: warn
+  no-extra-label: warn
+  no-fallthrough: warn
+  no-func-assign: error
+  no-global-assign: error
+  no-implicit-coercion:
+    - warn
+    - allow: ["~"]
+  no-implicit-globals: warn
+  no-implied-eval: warn
+  no-inline-comments: warn
+  no-inner-declarations: warn
+  no-invalid-regexp: warn
+  no-irregular-whitespace: warn
+  no-iterator: warn
+  no-label-var: warn
+  no-labels: warn
+  no-lone-blocks: warn
+  no-lonely-if: error
+  no-mixed-requires: error
+  no-multi-str: warn
+  no-native-reassign: error
+  no-negated-condition: warn
+  no-negated-in-lhs: error
+  no-new-func: warn
+  no-new-object: warn
+  no-new-require: warn
+  no-new-symbol: warn
+  no-new-wrappers: warn
+  no-new: warn
+  no-obj-calls: warn
+  no-octal-escape: warn
+  no-octal: warn
+  no-param-reassign: warn
+  no-path-concat: warn
+  no-process-env: warn
+  no-process-exit: warn
+  no-proto: warn
+  no-prototype-builtins: warn
+  no-redeclare: warn
+  no-regex-spaces: warn
+  no-restricted-globals: warn
+  no-restricted-imports: warn
+  no-restricted-modules: warn
+  no-restricted-syntax: warn
+  no-return-assign: error
+  no-script-url: warn
+  no-self-assign: warn
+  no-self-compare: warn
+  no-sequences: warn
+  no-shadow-restricted-names: warn
+  no-shadow: warn
+  no-sparse-arrays: warn
+  no-sync: warn
+  no-this-before-super: warn
+  no-throw-literal: warn
+  no-undef-init: warn
+  no-undef: error
+  no-unmodified-loop-condition: warn
+  no-unneeded-ternary: error
+  no-unreachable: error
+  no-unsafe-finally: error
+  no-unused-expressions: error
+  no-unused-labels: error
+  no-unused-vars: error
+  no-use-before-define: error
+  no-useless-call: warn
+  no-useless-computed-key: warn
+  no-useless-concat: warn
+  no-useless-constructor: warn
+  no-useless-escape: warn
+  no-useless-rename: warn
+  no-void: warn
+  no-with: warn
+  operator-assignment: [error, always]
+  prefer-const: warn
+  radix: warn
+  require-yield: warn
+  sort-imports: warn
+  spaced-comment: [error, always]
+  strict: [error, function]
+  use-isnan: error
+  valid-jsdoc:
+    - warn
+    - prefer:
+        arg: param
+        argument: param
+        augments: extends
+        constructor: class
+        exception: throws
+        func: function
+        method: function
+        prop: property
+        return: returns
+        virtual: abstract
+        yield: yields
+      preferType:
+        array: Array
+        bool: Boolean
+        boolean: Boolean
+        number: Number
+        object: Object
+        str: String
+        string: String
+      requireParamDescription: false
+      requireReturn: false
+      requireReturnDescription: false
+      requireReturnType: false
+  valid-typeof: warn
+  yoda: warn
diff --git a/.flake8 b/.flake8
new file mode 100644
index 0000000..e397e8e
--- /dev/null
+++ b/.flake8
@@ -0,0 +1,12 @@
+[flake8]
+max-line-length = 88
+max-complexity = 16
+# B = bugbear
+# B9 = bugbear opinionated (incl line length)
+select = C,E,F,W,B,B9
+# E203: whitespace before ':' (black behaviour)
+# E501: flake8 line length (covered by bugbear B950)
+# W503: line break before binary operator (black behaviour)
+ignore = E203,E501,W503
+per-file-ignores=
+    __init__.py:F401
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..818770f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,75 @@
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+/.venv
+/.pytest_cache
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+env/
+bin/
+build/
+develop-eggs/
+dist/
+eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+*.egg-info/
+.installed.cfg
+*.egg
+*.eggs
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.coverage
+.cache
+nosetests.xml
+coverage.xml
+
+# Translations
+*.mo
+
+# Pycharm
+.idea
+
+# Eclipse
+.settings
+
+# Visual Studio cache/options directory
+.vs/
+.vscode
+
+# OSX Files
+.DS_Store
+
+# Django stuff:
+*.log
+
+# Mr Developer
+.mr.developer.cfg
+.project
+.pydevproject
+
+# Rope
+.ropeproject
+
+# Sphinx documentation
+docs/_build/
+
+# Backup files
+*~
+*.swp
+
+# OCA rules
+!static/lib/
diff --git a/.isort.cfg b/.isort.cfg
new file mode 100644
index 0000000..0ec187e
--- /dev/null
+++ b/.isort.cfg
@@ -0,0 +1,13 @@
+[settings]
+; see https://github.com/psf/black
+multi_line_output=3
+include_trailing_comma=True
+force_grid_wrap=0
+combine_as_imports=True
+use_parentheses=True
+line_length=88
+known_odoo=odoo
+known_odoo_addons=odoo.addons
+sections=FUTURE,STDLIB,THIRDPARTY,ODOO,ODOO_ADDONS,FIRSTPARTY,LOCALFOLDER
+default_section=THIRDPARTY
+ensure_newline_before_comments = True
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 0000000..8a5999a
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,127 @@
+exclude: |
+  (?x)
+  # NOT INSTALLABLE ADDONS
+  # END NOT INSTALLABLE ADDONS
+  # Files and folders generated by bots, to avoid loops
+  ^setup/|/static/description/index\.html$|
+  # We don't want to mess with tool-generated files
+  .svg$|/tests/([^/]+/)?cassettes/|^.copier-answers.yml$|^.github/|
+  # Maybe reactivate this when all README files include prettier ignore tags?
+  ^README\.md$|
+  # Library files can have extraneous formatting (even minimized)
+  static/(src/)?lib/|
+  # Repos using Sphinx to generate docs don't need prettying
+  ^docs/_templates/.*\.html$|
+  # You don't usually want a bot to modify your legal texts
+  (LICENSE.*|COPYING.*)
+default_language_version:
+  python: python3
+  node: "14.13.0"
+repos:
+  - repo: local
+    hooks:
+      # These files are most likely copier diff rejection junks; if found,
+      # review them manually, fix the problem (if needed) and remove them
+      - id: forbidden-files
+        name: forbidden files
+        entry: found forbidden files; remove them
+        language: fail
+        files: "\\.rej$"
+  - repo: https://github.com/oca/maintainer-tools
+    rev: ab1d7f6
+    hooks:
+      # update the NOT INSTALLABLE ADDONS section above
+      - id: oca-update-pre-commit-excluded-addons
+      - id: oca-fix-manifest-website
+        args: ["https://le-filament.com"]
+  - repo: https://github.com/myint/autoflake
+    rev: v1.4
+    hooks:
+      - id: autoflake
+        args:
+          - --expand-star-imports
+          - --ignore-init-module-imports
+          - --in-place
+          - --remove-all-unused-imports
+          - --remove-duplicate-keys
+          - --remove-unused-variables
+  - repo: https://github.com/psf/black
+    rev: 20.8b1
+    hooks:
+      - id: black
+  - repo: https://github.com/pre-commit/mirrors-prettier
+    rev: v2.1.2
+    hooks:
+      - id: prettier
+        name: prettier (with plugin-xml)
+        additional_dependencies:
+          - "prettier@2.1.2"
+          - "@prettier/plugin-xml@0.12.0"
+        args:
+          - --plugin=@prettier/plugin-xml
+        files: \.(css|htm|html|js|json|jsx|less|md|scss|toml|ts|xml|yaml|yml)$
+  - repo: https://github.com/pre-commit/mirrors-eslint
+    rev: v7.8.1
+    hooks:
+      - id: eslint
+        verbose: true
+        args:
+          - --color
+          - --fix
+  - repo: https://github.com/pre-commit/pre-commit-hooks
+    rev: v3.2.0
+    hooks:
+      - id: trailing-whitespace
+        # exclude autogenerated files
+        exclude: /README\.rst$|\.pot?$
+      - id: end-of-file-fixer
+        # exclude autogenerated files
+        exclude: /README\.rst$|\.pot?$
+      - id: debug-statements
+      - id: fix-encoding-pragma
+        args: ["--remove"]
+      - id: check-case-conflict
+      - id: check-docstring-first
+      - id: check-executables-have-shebangs
+      - id: check-merge-conflict
+        # exclude files where underlines are not distinguishable from merge conflicts
+        exclude: /README\.rst$|^docs/.*\.rst$
+      - id: check-symlinks
+      - id: check-xml
+      - id: mixed-line-ending
+        args: ["--fix=lf"]
+  - repo: https://github.com/asottile/pyupgrade
+    rev: v2.7.2
+    hooks:
+      - id: pyupgrade
+        args: ["--keep-percent-format"]
+  - repo: https://github.com/PyCQA/isort
+    rev: 5.5.1
+    hooks:
+      - id: isort
+        name: isort except __init__.py
+        args:
+          - --settings=.
+        exclude: /__init__\.py$
+  - repo: https://gitlab.com/PyCQA/flake8
+    rev: 3.8.3
+    hooks:
+      - id: flake8
+        name: flake8
+        additional_dependencies: ["flake8-bugbear==20.1.4"]
+  - repo: https://github.com/PyCQA/pylint
+    rev: pylint-2.5.3
+    hooks:
+      - id: pylint
+        name: pylint with optional checks
+        args:
+          - --rcfile=.pylintrc
+          - --exit-zero
+        verbose: true
+        additional_dependencies: &pylint_deps
+          - pylint-odoo==3.5.0
+      - id: pylint
+        name: pylint with mandatory checks
+        args:
+          - --rcfile=.pylintrc-mandatory
+        additional_dependencies: *pylint_deps
diff --git a/.prettierrc.yml b/.prettierrc.yml
new file mode 100644
index 0000000..5b6d4b3
--- /dev/null
+++ b/.prettierrc.yml
@@ -0,0 +1,8 @@
+# Defaults for all prettier-supported languages.
+# Prettier will complete this with settings from .editorconfig file.
+bracketSpacing: false
+printWidth: 88
+proseWrap: always
+semi: true
+trailingComma: "es5"
+xmlWhitespaceSensitivity: "strict"
diff --git a/.pylintrc b/.pylintrc
new file mode 100644
index 0000000..542c686
--- /dev/null
+++ b/.pylintrc
@@ -0,0 +1,87 @@
+[MASTER]
+load-plugins=pylint_odoo
+score=n
+
+[ODOOLINT]
+readme_template_url="https://github.com/OCA/maintainer-tools/blob/master/template/module/README.rst"
+manifest_required_authors=Le Filament
+manifest_required_keys=license
+manifest_deprecated_keys=description,active
+license_allowed=AGPL-3,GPL-2,GPL-2 or any later version,GPL-3,GPL-3 or any later version,LGPL-3
+valid_odoo_versions=13.0
+
+[MESSAGES CONTROL]
+disable=all
+
+# This .pylintrc contains optional AND mandatory checks and is meant to be
+# loaded in an IDE to have it check everything, in the hope this will make
+# optional checks more visible to contributors who otherwise never look at a
+# green travis to see optional checks that failed.
+# .pylintrc-mandatory containing only mandatory checks is used the pre-commit
+# config as a blocking check.
+
+enable=anomalous-backslash-in-string,
+    api-one-deprecated,
+    api-one-multi-together,
+    assignment-from-none,
+    attribute-deprecated,
+    class-camelcase,
+    dangerous-default-value,
+    dangerous-view-replace-wo-priority,
+    development-status-allowed,
+    duplicate-id-csv,
+    duplicate-key,
+    duplicate-xml-fields,
+    duplicate-xml-record-id,
+    eval-referenced,
+    eval-used,
+    incoherent-interpreter-exec-perm,
+    license-allowed,
+    manifest-author-string,
+    manifest-deprecated-key,
+    manifest-required-author,
+    manifest-required-key,
+    manifest-version-format,
+    method-compute,
+    method-inverse,
+    method-required-super,
+    method-search,
+    openerp-exception-warning,
+    pointless-statement,
+    pointless-string-statement,
+    print-used,
+    redundant-keyword-arg,
+    redundant-modulename-xml,
+    reimported,
+    relative-import,
+    return-in-init,
+    rst-syntax-error,
+    sql-injection,
+    too-few-format-args,
+    translation-field,
+    translation-required,
+    unreachable,
+    use-vim-comment,
+    wrong-tabs-instead-of-spaces,
+    xml-syntax-error,
+    # messages that do not cause the lint step to fail
+    consider-merging-classes-inherited,
+    create-user-wo-reset-password,
+    dangerous-filter-wo-user,
+    deprecated-module,
+    file-not-used,
+    invalid-commit,
+    missing-manifest-dependency,
+    missing-newline-extrafiles,
+    no-utf8-coding-comment,
+    odoo-addons-relative-import,
+    old-api7-method-defined,
+    redefined-builtin,
+    too-complex,
+    unnecessary-utf8-coding-comment
+
+
+[REPORTS]
+msg-template={path}:{line}: [{msg_id}({symbol}), {obj}] {msg}
+output-format=colorized
+reports=no
diff --git a/.pylintrc-mandatory b/.pylintrc-mandatory
new file mode 100644
index 0000000..74be5ff
--- /dev/null
+++ b/.pylintrc-mandatory
@@ -0,0 +1,64 @@
+[MASTER]
+load-plugins=pylint_odoo
+score=n
+
+[ODOOLINT]
+readme_template_url="https://github.com/OCA/maintainer-tools/blob/master/template/module/README.rst"
+manifest_required_authors=Le Filament
+manifest_required_keys=license
+manifest_deprecated_keys=description,active
+license_allowed=AGPL-3,GPL-2,GPL-2 or any later version,GPL-3,GPL-3 or any later version,LGPL-3
+valid_odoo_versions=13.0
+
+[MESSAGES CONTROL]
+disable=all
+
+enable=anomalous-backslash-in-string,
+    api-one-deprecated,
+    api-one-multi-together,
+    assignment-from-none,
+    attribute-deprecated,
+    class-camelcase,
+    dangerous-default-value,
+    dangerous-view-replace-wo-priority,
+    development-status-allowed,
+    duplicate-id-csv,
+    duplicate-key,
+    duplicate-xml-fields,
+    duplicate-xml-record-id,
+    eval-referenced,
+    eval-used,
+    incoherent-interpreter-exec-perm,
+    license-allowed,
+    manifest-author-string,
+    manifest-deprecated-key,
+    manifest-required-author,
+    manifest-required-key,
+    manifest-version-format,
+    method-compute,
+    method-inverse,
+    method-required-super,
+    method-search,
+    openerp-exception-warning,
+    pointless-statement,
+    pointless-string-statement,
+    print-used,
+    redundant-keyword-arg,
+    redundant-modulename-xml,
+    reimported,
+    relative-import,
+    return-in-init,
+    rst-syntax-error,
+    sql-injection,
+    too-few-format-args,
+    translation-field,
+    translation-required,
+    unreachable,
+    use-vim-comment,
+    wrong-tabs-instead-of-spaces,
+    xml-syntax-error
+
+[REPORTS]
+msg-template={path}:{line}: [{msg_id}({symbol}), {obj}] {msg}
+output-format=colorized
+reports=no
diff --git a/__manifest__.py b/__manifest__.py
index e982ceb..4911abd 100644
--- a/__manifest__.py
+++ b/__manifest__.py
@@ -2,23 +2,22 @@
 # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
 
 {
-    'name': "CG Scop - Fullcalendar",
-    'summary': "Vertical Resource View for CG Scop",
-    "version": "12.0.1.1.0",
-    "development_status": "Production/Stable",
-    'author': 'Le Filament',
+    "name": "CG Scop - Fullcalendar",
+    "summary": "Vertical Resource View for CG Scop",
+    "version": "13.0.0.1.0",
+    "author": "Le Filament",
     "license": "AGPL-3",
     "application": False,
     "installable": True,
-    'depends': [
-        'base',
-        'web',
+    "depends": [
+        "base",
+        "web",
     ],
-    'qweb': [
-        'static/src/xml/*.xml',
+    "qweb": [
+        "static/src/xml/*.xml",
     ],
-    'data': [
-        'views/assets.xml',
-        'views/res_users.xml',
+    "data": [
+        "views/assets.xml",
+        "views/res_users.xml",
     ],
 }
diff --git a/models/__init__.py b/models/__init__.py
index 100cd04..db8a048 100644
--- a/models/__init__.py
+++ b/models/__init__.py
@@ -1,4 +1,3 @@
-
 from . import ir_action
 from . import ir_http
 from . import ir_view
diff --git a/models/ir_action.py b/models/ir_action.py
index 4ed62a1..222a16c 100644
--- a/models/ir_action.py
+++ b/models/ir_action.py
@@ -3,11 +3,10 @@
 
 from odoo import fields, models
 
-
-VIEW_TYPES = ('resource', 'Resource')
+VIEW_TYPES = ("resource", "Resource")
 
 
 class IrActionsActWindowView(models.Model):
-    _inherit = 'ir.actions.act_window.view'
+    _inherit = "ir.actions.act_window.view"
 
     view_mode = fields.Selection(selection_add=[VIEW_TYPES])
diff --git a/models/ir_http.py b/models/ir_http.py
index 674a3a5..e17231a 100644
--- a/models/ir_http.py
+++ b/models/ir_http.py
@@ -2,17 +2,16 @@
 # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
 
 from odoo import models
-from odoo.http import request
 
 
 class IrHttp(models.AbstractModel):
-    _inherit = 'ir.http'
+    _inherit = "ir.http"
 
     def session_info(self):
         """
-            Ajoute l'ur_id dans le contexte
+        Ajoute l'ur_id dans le contexte
         """
         result = super(IrHttp, self).session_info()
         user = self.env.user
-        result['is_weekend'] = user.is_weekend
+        result["is_weekend"] = user.is_weekend
         return result
diff --git a/models/ir_view.py b/models/ir_view.py
index 3b772a0..7d9dc9c 100644
--- a/models/ir_view.py
+++ b/models/ir_view.py
@@ -3,11 +3,10 @@
 
 from odoo import fields, models
 
-
-RESOURCE_VIEW = ('resource', 'Resource')
+RESOURCE_VIEW = ("resource", "Resource")
 
 
 class IrUIView(models.Model):
-    _inherit = 'ir.ui.view'
+    _inherit = "ir.ui.view"
 
     type = fields.Selection(selection_add=[RESOURCE_VIEW])
diff --git a/models/res_users.py b/models/res_users.py
index 305dbb7..5ba7b4a 100644
--- a/models/res_users.py
+++ b/models/res_users.py
@@ -7,22 +7,20 @@ from odoo import fields, models
 class ResUsersCalendar(models.Model):
     _inherit = "res.users"
 
-    is_weekend = fields.Boolean(
-        string="Afficher les weekend",
-        default=True)
+    is_weekend = fields.Boolean(string="Afficher les weekend", default=True)
 
     # ------------------------------------------------------
     # Override parent
     # ------------------------------------------------------
     def __init__(self, pool, cr):
-        """ Override of __init__ to add access rights.
+        """Override of __init__ to add access rights.
         Access rights are disabled by default, but allowed on some specific
         fields defined in self.SELF_{READ/WRITE}ABLE_FIELDS.
         """
         super(ResUsersCalendar, self).__init__(pool, cr)
         # duplicate list to avoid modifying the original reference
         type(self).SELF_WRITEABLE_FIELDS = list(self.SELF_WRITEABLE_FIELDS)
-        type(self).SELF_WRITEABLE_FIELDS.extend(['is_weekend'])
+        type(self).SELF_WRITEABLE_FIELDS.extend(["is_weekend"])
         # duplicate list to avoid modifying the original reference
         type(self).SELF_READABLE_FIELDS = list(self.SELF_READABLE_FIELDS)
-        type(self).SELF_READABLE_FIELDS.extend(['is_weekend'])
+        type(self).SELF_READABLE_FIELDS.extend(["is_weekend"])
diff --git a/static/src/css/cgscop_fullcalendar.css b/static/src/css/cgscop_fullcalendar.css
index 602d87b..2f2990e 100644
--- a/static/src/css/cgscop_fullcalendar.css
+++ b/static/src/css/cgscop_fullcalendar.css
@@ -2,35 +2,36 @@
     Resource calendar colors
 */
 .o_calendar_color_none {
-    background-color: rgba(170, 170, 170, 0.6)!important;
-    border-color: rgba(170, 170, 170, 0.8)!important;
+    background-color: rgba(170, 170, 170, 0.6) !important;
+    border-color: rgba(170, 170, 170, 0.8) !important;
     border-radius: 0;
 }
 
-.cgscop_resource .fc-resource-cell, .cgscop_resource .fc-day {
-  width: 80px;
+.cgscop_resource .fc-resource-cell,
+.cgscop_resource .fc-day {
+    width: 80px;
 }
 
 .cgscop_resource .fc-content-skeleton td {
-  width: 80px;
+    width: 80px;
 }
 
 .cgscop_resource .fc-view > table {
-   min-width: 100%;
-   width: auto!important;
+    min-width: 100%;
+    width: auto !important;
 }
 
 .cgscop_resource .fc-time-grid .fc-slats {
-   z-index: 4;
-   pointer-events: none;
+    z-index: 4;
+    pointer-events: none;
 }
 
 .cgscop_resource .fc-scroller.fc-time-grid-container {
-   overflow: initial !important;
+    overflow: initial !important;
 }
 
 .cgscop_resource .fc-axis {
-   position: sticky;
-   left: 0;
-   background: white;
+    position: sticky;
+    left: 0;
+    background: white;
 }
diff --git a/static/src/js/calendar_controller.js b/static/src/js/calendar_controller.js
index a3ecee4..065a75f 100644
--- a/static/src/js/calendar_controller.js
+++ b/static/src/js/calendar_controller.js
@@ -1,35 +1,35 @@
 // © 2020 Le Filament (<http://www.le-filament.com>)
 // License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
-        
-odoo.define('cgscop_fullcalendar.CalendarController', function (require) {
+
+odoo.define("cgscop_fullcalendar.CalendarController", function (require) {
     "use strict";
 
-    var calendarController = require('web.CalendarController');
+    var calendarController = require("web.CalendarController");
 
     calendarController.include({
-    	custom_events: _.extend({}, calendarController.prototype.custom_events, {
-	        copyEvent: '_onCopyEvent',
-	    }),
-	    /**
-	     * @private
-	     * @param {Infos} event + delta
-	     */
-	    _onCopyEvent: function (infos) {
-	        var self = this;
-	        var record = infos.data.event.record;
+        custom_events: _.extend({}, calendarController.prototype.custom_events, {
+            copyEvent: "_onCopyEvent",
+        }),
+        /**
+         * @private
+         * @param {Infos} event + delta
+         */
+        _onCopyEvent: function (infos) {
+            var self = this;
+            var record = infos.data.event.record;
 
-	        var values = {
-	            start: record[this.mapping.date_start].add(infos.data.delta),
-	            stop: record[this.mapping.date_stop].add(infos.data.delta),
-	        }
-	        this._rpc({
-	            model: self.modelName,
-	            method: 'copy',
-	            args: [[record.id], values]
-	        }).then(function (id) {
-	            self.reload();
-	        });
-	        return;
-    	},
-	});
-});
\ No newline at end of file
+            var values = {
+                start: record[this.mapping.date_start].add(infos.data.delta),
+                stop: record[this.mapping.date_stop].add(infos.data.delta),
+            };
+            this._rpc({
+                model: self.modelName,
+                method: "copy",
+                args: [[record.id], values],
+            }).then(function (id) {
+                self.reload();
+            });
+            return;
+        },
+    });
+});
diff --git a/static/src/js/calendar_model.js b/static/src/js/calendar_model.js
index da0053f..e1fd70f 100644
--- a/static/src/js/calendar_model.js
+++ b/static/src/js/calendar_model.js
@@ -1,11 +1,11 @@
 // © 2020 Le Filament (<http://www.le-filament.com>)
 // License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
-        
-odoo.define('cgscop_fullcalendar.CalendarModel', function (require) {
+
+odoo.define("cgscop_fullcalendar.CalendarModel", function (require) {
     "use strict";
 
-    var calendarModel = require('web.CalendarModel');
-    var session = require('web.session');
+    var calendarModel = require("web.CalendarModel");
+    var session = require("web.session");
 
     calendarModel.include({
         /**
@@ -15,9 +15,9 @@ odoo.define('cgscop_fullcalendar.CalendarModel', function (require) {
         load: function (params) {
             var self = this;
             this.readonly = params.readonly;
-            return this._super(params)
+            return this._super(params);
         },
-         /**
+        /**
          * @override parent
          * @returns {Object}
          */
@@ -28,10 +28,10 @@ odoo.define('cgscop_fullcalendar.CalendarModel', function (require) {
             fc_option.droppable = !this.readonly;
             fc_option.editable = !this.readonly;
             fc_option.editable = !this.readonly;
-            fc_option.maxTime = '21:00:00';
-            fc_option.minTime = '06:00:00';
+            fc_option.maxTime = "21:00:00";
+            fc_option.minTime = "06:00:00";
 
-            return fc_option
+            return fc_option;
         },
     });
 });
diff --git a/static/src/js/calendar_renderer.js b/static/src/js/calendar_renderer.js
index caf6dbe..bc34284 100644
--- a/static/src/js/calendar_renderer.js
+++ b/static/src/js/calendar_renderer.js
@@ -1,17 +1,17 @@
 // © 2020 Le Filament (<http://www.le-filament.com>)
 // License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
 
-odoo.define('cgscop_fullcalendar.CalendarRenderer', function (require) {
+odoo.define("cgscop_fullcalendar.CalendarRenderer", function (require) {
     "use strict";
 
-    var session = require('web.session');
-    var utils = require('web.utils');
-    var core = require('web.core');
-    var relational_fields = require('web.relational_fields');
-    var FieldManagerMixin = require('web.FieldManagerMixin');
-    var Widget = require('web.Widget');
-    var calendarRenderer = require('web.CalendarRenderer');
-    var Dialog = require('web.Dialog');
+    var session = require("web.session");
+    var utils = require("web.utils");
+    var core = require("web.core");
+    var relational_fields = require("web.relational_fields");
+    var FieldManagerMixin = require("web.FieldManagerMixin");
+    var Widget = require("web.Widget");
+    var calendarRenderer = require("web.CalendarRenderer");
+    var Dialog = require("web.Dialog");
 
     var _t = core._t;
 
@@ -23,17 +23,17 @@ odoo.define('cgscop_fullcalendar.CalendarRenderer', function (require) {
     /**
      * @override
      * Ajoute filtre par UR si dans les options
-    */
+     */
     var SidebarFilter = Widget.extend(FieldManagerMixin, {
-        template: 'CalendarView.sidebar.filter',
+        template: "CalendarView.sidebar.filter",
         custom_events: _.extend({}, FieldManagerMixin.custom_events, {
-            field_changed: '_onFieldChanged',
+            field_changed: "_onFieldChanged",
         }),
         /**
-         * @constructor
+         * @class
          * @param {Widget} parent
          * @param {Object} options
-         * @param {string} options.fieldName
+         * @param {String} options.fieldName
          * @param {Object[]} options.filters A filter is an object with the
          *   following keys: id, value, label, active, avatar_model, color,
          *   can_be_removed
@@ -54,7 +54,10 @@ odoo.define('cgscop_fullcalendar.CalendarRenderer', function (require) {
             this.filters = options.filters;
             this.label = options.label;
             this.getColor = options.getColor;
-            this.ur_sidebar_filter = utils.toBoolElse(options.ur_sidebar_filter || '', false);
+            this.ur_sidebar_filter = utils.toBoolElse(
+                options.ur_sidebar_filter || "",
+                false
+            );
         },
         /**
          * @override
@@ -63,37 +66,45 @@ odoo.define('cgscop_fullcalendar.CalendarRenderer', function (require) {
             var self = this;
             var defs = [this._super.apply(this, arguments)];
 
-            var domain = this.fields[self.fieldName].domain
+            var domain = this.fields[self.fieldName].domain;
 
             if (this.ur_sidebar_filter == true) {
-                domain = domain.concat([["ur_id", "=", session.ur_id]])
+                domain = domain.concat([["ur_id", "=", session.ur_id]]);
             }
 
             if (this.write_model || this.write_field) {
-                var def = this.model.makeRecord(this.write_model, [{
-                    name: this.write_field,
-                    relation: this.fields[this.fieldName].relation,
-                    type: 'many2one',
-                    domain: domain,
-                }]).then(function (recordID) {
-                    self.many2one = new SidebarFilterM2O(self,
-                        self.write_field,
-                        self.model.get(recordID),
+                var def = this.model
+                    .makeRecord(this.write_model, [
                         {
-                            mode: 'edit',
-                            attrs: {
-                                placeholder: _.str.sprintf(_t("Add %s"), self.title),
-                                can_create: false
-                            },
-                            domain: self.fields[self.fieldName].domain,
-                            readonly: true,
-                            invisible: true,
-                        });
-                });
+                            name: this.write_field,
+                            relation: this.fields[this.fieldName].relation,
+                            type: "many2one",
+                            domain: domain,
+                        },
+                    ])
+                    .then(function (recordID) {
+                        self.many2one = new SidebarFilterM2O(
+                            self,
+                            self.write_field,
+                            self.model.get(recordID),
+                            {
+                                mode: "edit",
+                                attrs: {
+                                    placeholder: _.str.sprintf(
+                                        _t("Add %s"),
+                                        self.title
+                                    ),
+                                    can_create: false,
+                                },
+                                domain: self.fields[self.fieldName].domain,
+                                readonly: true,
+                                invisible: true,
+                            }
+                        );
+                    });
                 defs.push(def);
             }
             return $.when.apply($, defs);
-
         },
         /**
          * @override
@@ -102,15 +113,22 @@ odoo.define('cgscop_fullcalendar.CalendarRenderer', function (require) {
             this._super();
             if (this.many2one) {
                 this.many2one.appendTo(this.$el);
-                this.many2one.filter_ids = _.without(_.pluck(this.filters, 'value'), 'all');
+                this.many2one.filter_ids = _.without(
+                    _.pluck(this.filters, "value"),
+                    "all"
+                );
             }
-            this.$el.on('click', '.o_remove', this._onFilterRemove.bind(this));
-            this.$el.on('click', '.custom-checkbox input', this._onFilterActive.bind(this));
+            this.$el.on("click", ".o_remove", this._onFilterRemove.bind(this));
+            this.$el.on(
+                "click",
+                ".custom-checkbox input",
+                this._onFilterActive.bind(this)
+            );
         },
 
-        //--------------------------------------------------------------------------
+        // --------------------------------------------------------------------------
         // Handlers
-        //--------------------------------------------------------------------------
+        // --------------------------------------------------------------------------
 
         /**
          * @private
@@ -119,21 +137,20 @@ odoo.define('cgscop_fullcalendar.CalendarRenderer', function (require) {
         _onFieldChanged: function (event) {
             var self = this;
             event.stopPropagation();
-            var createValues = {'user_id': session.uid};
+            var createValues = {user_id: session.uid};
             var value = event.data.changes[this.write_field].id;
             createValues[this.write_field] = value;
             this._rpc({
-                    model: this.write_model,
-                    method: 'create',
-                    args: [createValues],
-                })
-                .then(function () {
-                    self.trigger_up('changeFilter', {
-                        'fieldName': self.fieldName,
-                        'value': value,
-                        'active': true,
-                    });
+                model: this.write_model,
+                method: "create",
+                args: [createValues],
+            }).then(function () {
+                self.trigger_up("changeFilter", {
+                    fieldName: self.fieldName,
+                    value: value,
+                    active: true,
                 });
+            });
         },
         /**
          * @private
@@ -141,10 +158,10 @@ odoo.define('cgscop_fullcalendar.CalendarRenderer', function (require) {
          */
         _onFilterActive: function (e) {
             var $input = $(e.currentTarget);
-            this.trigger_up('changeFilter', {
-                'fieldName': this.fieldName,
-                'value': $input.closest('.o_calendar_filter_item').data('value'),
-                'active': $input.prop('checked'),
+            this.trigger_up("changeFilter", {
+                fieldName: this.fieldName,
+                value: $input.closest(".o_calendar_filter_item").data("value"),
+                active: $input.prop("checked"),
             });
         },
         /**
@@ -153,24 +170,27 @@ odoo.define('cgscop_fullcalendar.CalendarRenderer', function (require) {
          */
         _onFilterRemove: function (e) {
             var self = this;
-            var $filter = $(e.currentTarget).closest('.o_calendar_filter_item');
-            Dialog.confirm(this, _t("Do you really want to delete this filter from favorites ?"), {
-                confirm_callback: function () {
-                    self._rpc({
+            var $filter = $(e.currentTarget).closest(".o_calendar_filter_item");
+            Dialog.confirm(
+                this,
+                _t("Do you really want to delete this filter from favorites ?"),
+                {
+                    confirm_callback: function () {
+                        self._rpc({
                             model: self.write_model,
-                            method: 'unlink',
-                            args: [[$filter.data('id')]],
-                        })
-                        .then(function () {
-                            self.trigger_up('changeFilter', {
-                                'fieldName': self.fieldName,
-                                'id': $filter.data('id'),
-                                'active': false,
-                                'value': $filter.data('value'),
+                            method: "unlink",
+                            args: [[$filter.data("id")]],
+                        }).then(function () {
+                            self.trigger_up("changeFilter", {
+                                fieldName: self.fieldName,
+                                id: $filter.data("id"),
+                                active: false,
+                                value: $filter.data("value"),
                             });
                         });
-                },
-            });
+                    },
+                }
+            );
         },
     });
 
@@ -179,7 +199,7 @@ odoo.define('cgscop_fullcalendar.CalendarRenderer', function (require) {
          * @override
          * Réécrit la fonction
          * Modifie DropEvent pour copier avec la touche ALT
-        */
+         */
         _initCalendar: function () {
             var self = this;
 
@@ -191,59 +211,66 @@ odoo.define('cgscop_fullcalendar.CalendarRenderer', function (require) {
             var fc_options = $.extend({}, this.state.fc_options, {
                 eventDrop: function (event, delta, revertFunc, jsEvent, ui, view) {
                     if (jsEvent.altKey) {
-                        self.trigger_up('copyEvent', { event: event, delta: delta });
-                        self.$calendar.fullCalendar('unselect');
+                        self.trigger_up("copyEvent", {event: event, delta: delta});
+                        self.$calendar.fullCalendar("unselect");
                         return;
                     }
-                    else {
-                        self.trigger_up('dropRecord', event);
-                    }
+                    self.trigger_up("dropRecord", event);
                 },
                 eventResize: function (event) {
-                    self.trigger_up('updateRecord', event);
+                    self.trigger_up("updateRecord", event);
                 },
                 eventClick: function (event) {
-                    self.trigger_up('openEvent', event);
-                    self.$calendar.fullCalendar('unselect');
+                    self.trigger_up("openEvent", event);
+                    self.$calendar.fullCalendar("unselect");
                 },
                 select: function (target_date, end_date, event, _js_event, _view) {
-                    var data = {'start': target_date, 'end': end_date};
+                    var data = {start: target_date, end: end_date};
                     if (self.state.context.default_name) {
                         data.title = self.state.context.default_name;
                     }
-                    self.trigger_up('openCreate', data);
-                    self.$calendar.fullCalendar('unselect');
+                    self.trigger_up("openCreate", data);
+                    self.$calendar.fullCalendar("unselect");
                 },
                 eventRender: function (event, element) {
                     var $render = $(self._eventRender(event));
-                    event.title = $render.find('.o_field_type_char:first').text();
-                    element.find('.fc-content').html($render.html());
-                    element.addClass($render.attr('class'));
-                    var display_hour = '';
+                    event.title = $render.find(".o_field_type_char:first").text();
+                    element.find(".fc-content").html($render.html());
+                    element.addClass($render.attr("class"));
+                    var display_hour = "";
                     if (!event.allDay) {
                         var start = event.r_start || event.start;
                         var end = event.r_end || event.end;
-                        var timeFormat = _t.database.parameters.time_format.search("%H") != -1 ? 'HH:mm': 'h:mma';
-                        display_hour = start.format(timeFormat) + ' - ' + end.format(timeFormat);
-                        if (display_hour === '00:00 - 00:00') {
-                            display_hour = _t('All day');
+                        var timeFormat =
+                            _t.database.parameters.time_format.search("%H") != -1
+                                ? "HH:mm"
+                                : "h:mma";
+                        display_hour =
+                            start.format(timeFormat) + " - " + end.format(timeFormat);
+                        if (display_hour === "00:00 - 00:00") {
+                            display_hour = _t("All day");
                         }
                     }
-                    element.find('.fc-content .fc-time').text(display_hour);
+                    element.find(".fc-content .fc-time").text(display_hour);
                 },
                 // Dirty hack to ensure a correct first render
                 eventAfterAllRender: function () {
-                    $(window).trigger('resize');
+                    $(window).trigger("resize");
                 },
                 viewRender: function (view) {
-                    // compute mode from view.name which is either 'month', 'agendaWeek' or 'agendaDay'
-                    var mode = view.name === 'month' ? 'month' : (view.name === 'agendaWeek' ? 'week' : 'day');
-                    self.trigger_up('viewUpdated', {
+                    // Compute mode from view.name which is either 'month', 'agendaWeek' or 'agendaDay'
+                    var mode =
+                        view.name === "month"
+                            ? "month"
+                            : view.name === "agendaWeek"
+                            ? "week"
+                            : "day";
+                    self.trigger_up("viewUpdated", {
                         mode: mode,
                         title: view.title,
                     });
                 },
-                height: 'parent',
+                height: "parent",
                 unselectAuto: false,
                 locale: locale,
             });
@@ -251,12 +278,11 @@ odoo.define('cgscop_fullcalendar.CalendarRenderer', function (require) {
             this.$calendar.fullCalendar(fc_options);
         },
 
-
         /**
          * @override
          * Réécrit la fonction totalement
          * Ajoute filtre par UR si dans les options
-        */
+         */
         _renderFilters: function () {
             var self = this;
             _.each(this.filters || (this.filters = []), function (filter) {
@@ -266,7 +292,11 @@ odoo.define('cgscop_fullcalendar.CalendarRenderer', function (require) {
                 return;
             }
             _.each(this.state.filters, function (options) {
-                if (!_.find(options.filters, function (f) {return f.display == null || f.display;})) {
+                if (
+                    !_.find(options.filters, function (f) {
+                        return f.display == null || f.display;
+                    })
+                ) {
                     return;
                 }
                 options.ur_sidebar_filter = self.state.context.ur_sidebar_filter;
@@ -277,7 +307,5 @@ odoo.define('cgscop_fullcalendar.CalendarRenderer', function (require) {
                 self.filters.push(filter);
             });
         },
-
-
     });
 });
diff --git a/static/src/js/calendar_view.js b/static/src/js/calendar_view.js
index 29013e9..3250e61 100644
--- a/static/src/js/calendar_view.js
+++ b/static/src/js/calendar_view.js
@@ -1,21 +1,26 @@
 // © 2020 Le Filament (<http://www.le-filament.com>)
 // License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
-        
-odoo.define('cgscop_fullcalendar.CalendarView', function (require) {
+
+odoo.define("cgscop_fullcalendar.CalendarView", function (require) {
     "use strict";
 
-    var utils = require('web.utils');
-    var calendarView = require('web.CalendarView');
+    var utils = require("web.utils");
+    var calendarView = require("web.CalendarView");
 
     calendarView.include({
         /**
          * @override
-        */
+         */
         init: function (viewInfo, params) {
             this._super(viewInfo, params);
-            this.loadParams.ur_sidebar_filter = utils.toBoolElse(params.context.ur_sidebar_filter || '', false);
-            this.loadParams.readonly = utils.toBoolElse(params.context.calendar_readonly || '', false);
+            this.loadParams.ur_sidebar_filter = utils.toBoolElse(
+                params.context.ur_sidebar_filter || "",
+                false
+            );
+            this.loadParams.readonly = utils.toBoolElse(
+                params.context.calendar_readonly || "",
+                false
+            );
         },
     });
 });
-
diff --git a/static/src/js/resource_controller.js b/static/src/js/resource_controller.js
index a3db73c..dd77be1 100644
--- a/static/src/js/resource_controller.js
+++ b/static/src/js/resource_controller.js
@@ -1,436 +1,469 @@
-odoo.define('cgscop_fullcalendar.ResourceController', function (require) {
-"use strict";
+odoo.define("cgscop_fullcalendar.ResourceController", function (require) {
+    "use strict";
 
-/**
- * ResourceController Controller
- *
- * This is the controller in the Model-Renderer-Controller architecture of the
- * calendar view.  Its role is to coordinate the data from the calendar model
- * with the renderer, and with the outside world (such as a search view input)
- */
-
-var AbstractController = require('web.AbstractController');
-var config = require('web.config');
-var core = require('web.core');
-var Dialog = require('web.Dialog');
-var dialogs = require('web.view_dialogs');
-var QuickCreate = require('web.CalendarQuickCreate');
-
-var _t = core._t;
-var QWeb = core.qweb;
-
-function dateToServer (date) {
-    return date.clone().utc().locale('en').format('YYYY-MM-DD HH:mm:ss');
-}
-
-var ResourceController = AbstractController.extend({
-    custom_events: _.extend({}, AbstractController.prototype.custom_events, {
-        changeDate: '_onChangeDate',
-        changeFilter: '_onChangeFilter',
-        dropRecord: '_onDropRecord',
-        next: '_onNext',
-        openCreate: '_onOpenCreate',
-        openEvent: '_onOpenEvent',
-        prev: '_onPrev',
-        quickCreate: '_onQuickCreate',
-        toggleFullWidth: '_onToggleFullWidth',
-        updateRecord: '_onUpdateRecord',
-        viewUpdated: '_onViewUpdated',
-    }),
     /**
-     * @override
-     * @param {Widget} parent
-     * @param {AbstractModel} model
-     * @param {AbstractRenderer} renderer
-     * @param {Object} params
-     */
-    init: function (parent, model, renderer, params) {
-        this._super.apply(this, arguments);
-        this.current_start = null;
-        this.displayName = params.displayName;
-        this.quickAddPop = params.quickAddPop;
-        this.disableQuickCreate = params.disableQuickCreate;
-        this.eventOpenPopup = params.eventOpenPopup;
-        this.formViewId = params.formViewId;
-        this.readonlyFormViewId = params.readonlyFormViewId;
-        this.mapping = params.mapping;
-        this.context = params.context;
-        // The quickCreating attribute ensures that we don't do several create
-        this.quickCreating = false;
-    },
-    /**
-     * Overrides to unbind handler on the control panel mobile 'Today' button.
+     * ResourceController Controller
      *
-     * @override
+     * This is the controller in the Model-Renderer-Controller architecture of the
+     * calendar view.  Its role is to coordinate the data from the calendar model
+     * with the renderer, and with the outside world (such as a search view input)
      */
-    destroy: function () {
-        this._super.apply(this, arguments);
-        if (this.$todayButton) {
-            this.$todayButton.off();
-        }
-    },
 
-    //--------------------------------------------------------------------------
-    // Public
-    //--------------------------------------------------------------------------
+    var AbstractController = require("web.AbstractController");
+    var config = require("web.config");
+    var core = require("web.core");
+    var Dialog = require("web.Dialog");
+    var dialogs = require("web.view_dialogs");
+    var QuickCreate = require("web.CalendarQuickCreate");
 
-    /**
-     * @override
-     * @returns {string}
-     */
-    getTitle: function () {
-        return this.get('title');
-    },
-    /**
-     * Render the buttons according to the CalendarView.buttons template and
-     * add listeners on it. Set this.$buttons with the produced jQuery element
-     *
-     * @param {jQueryElement} [$node] a jQuery node where the rendered buttons
-     *   should be inserted. $node may be undefined, in which case the Calendar
-     *   inserts them into this.options.$buttons or into a div of its template
-     */
-    renderButtons: function ($node) {
-        var self = this;
-        this.$buttons = $(QWeb.render('ResourceView.buttons', {
-            isMobile: config.device.isMobile,
-        }));
-        this.$buttons.on('click', 'button.o_calendar_button_new', function () {
-            self.trigger_up('switch_view', {view_type: 'form'});
-        });
+    var _t = core._t;
+    var QWeb = core.qweb;
 
-        _.each(['prev', 'today', 'next'], function (action) {
-            self.$buttons.on('click', '.o_calendar_button_' + action, function () {
-                self._move(action);
-            });
-        });
+    function dateToServer(date) {
+        return date.clone().utc().locale("en").format("YYYY-MM-DD HH:mm:ss");
+    }
 
-        this.$buttons.find('.o_calendar_button_' + this.mode).addClass('active');
+    var ResourceController = AbstractController.extend({
+        custom_events: _.extend({}, AbstractController.prototype.custom_events, {
+            changeDate: "_onChangeDate",
+            changeFilter: "_onChangeFilter",
+            dropRecord: "_onDropRecord",
+            next: "_onNext",
+            openCreate: "_onOpenCreate",
+            openEvent: "_onOpenEvent",
+            prev: "_onPrev",
+            quickCreate: "_onQuickCreate",
+            toggleFullWidth: "_onToggleFullWidth",
+            updateRecord: "_onUpdateRecord",
+            viewUpdated: "_onViewUpdated",
+        }),
+        /**
+         * @override
+         * @param {Widget} parent
+         * @param {AbstractModel} model
+         * @param {AbstractRenderer} renderer
+         * @param {Object} params
+         */
+        init: function (parent, model, renderer, params) {
+            this._super.apply(this, arguments);
+            this.current_start = null;
+            this.displayName = params.displayName;
+            this.quickAddPop = params.quickAddPop;
+            this.disableQuickCreate = params.disableQuickCreate;
+            this.eventOpenPopup = params.eventOpenPopup;
+            this.formViewId = params.formViewId;
+            this.readonlyFormViewId = params.readonlyFormViewId;
+            this.mapping = params.mapping;
+            this.context = params.context;
+            // The quickCreating attribute ensures that we don't do several create
+            this.quickCreating = false;
+        },
+        /**
+         * Overrides to unbind handler on the control panel mobile 'Today' button.
+         *
+         * @override
+         */
+        destroy: function () {
+            this._super.apply(this, arguments);
+            if (this.$todayButton) {
+                this.$todayButton.off();
+            }
+        },
 
-        if ($node) {
-            this.$buttons.appendTo($node);
-        } else {
-            this.$('.o_calendar_buttons').replaceWith(this.$buttons);
-        }
-    },
-    /**
-     * In mobile, we want to display a special 'Today' button on the bottom
-     * right corner of the control panel. This is the pager area, and as there
-     * is no pager in Calendar views, we fool the system by defining a fake
-     * pager (which is actually our button) such that it will be inserted in the
-     * desired place.
-     *
-     * @todo get rid of this hack once the ControlPanel layout will be reworked
-     *
-     * @param {jQueryElement} $node the button should be appended to this
-     *   element to be displayed in the bottom right corner of the control panel
-     */
-    renderPager: function ($node) {
-        if (config.device.isMobile) {
-            this.$todayButton = $(QWeb.render('CalendarView.TodayButtonMobile'));
-            this.$todayButton.on('click', this._move.bind(this, 'today'));
-            $node.append(this.$todayButton);
-        }
-    },
+        // --------------------------------------------------------------------------
+        // Public
+        // --------------------------------------------------------------------------
 
-    //--------------------------------------------------------------------------
-    // Private
-    //--------------------------------------------------------------------------
+        /**
+         * @override
+         * @returns {String}
+         */
+        getTitle: function () {
+            return this.get("title");
+        },
+        /**
+         * Render the buttons according to the CalendarView.buttons template and
+         * add listeners on it. Set this.$buttons with the produced jQuery element
+         *
+         * @param {jQueryElement} [$node] a jQuery node where the rendered buttons
+         *   should be inserted. $node may be undefined, in which case the Calendar
+         *   inserts them into this.options.$buttons or into a div of its template
+         */
+        renderButtons: function ($node) {
+            var self = this;
+            this.$buttons = $(
+                QWeb.render("ResourceView.buttons", {
+                    isMobile: config.device.isMobile,
+                })
+            );
+            this.$buttons.on("click", "button.o_calendar_button_new", function () {
+                self.trigger_up("switch_view", {view_type: "form"});
+            });
 
-    /**
-     * Move to the requested direction and reload the view
-     *
-     * @private
-     * @param {string} to either 'prev', 'next' or 'today'
-     * @returns {Deferred}
-     */
-    _move: function (to) {
-        this.model[to]();
-        return this.reload();
-    },
-    /**
-     * @private
-     * @param {Object} record
-     * @param {integer} record.id
-     * @returns {Deferred}
-     */
-    _updateRecord: function (record) {
-        return this.model.updateRecord(record).always(this.reload.bind(this));
-    },
+            _.each(["prev", "today", "next"], function (action) {
+                self.$buttons.on("click", ".o_calendar_button_" + action, function () {
+                    self._move(action);
+                });
+            });
 
-    //--------------------------------------------------------------------------
-    // Handlers
-    //--------------------------------------------------------------------------
+            this.$buttons.find(".o_calendar_button_" + this.mode).addClass("active");
 
-    /**
-     * @private
-     * @param {OdooEvent} event
-     */
-    _onChangeDate: function (event) {
-        var modelData = this.model.get();
-        if (modelData.target_date.format('YYYY-MM-DD') === event.data.date.format('YYYY-MM-DD')) {
-            // When clicking on same date, toggle between the two views
-            switch (modelData.scale) {
-                case 'day': this.model.setScale('month'); break;
+            if ($node) {
+                this.$buttons.appendTo($node);
+            } else {
+                this.$(".o_calendar_buttons").replaceWith(this.$buttons);
             }
-        } else if (modelData.target_date.week() === event.data.date.week()) {
-            // When clicking on a date in the same week, switch to day view
-            this.model.setScale('day');
-        } else {
-            // When clicking on a random day of a random other week, switch to week view
-            this.model.setScale('week');
-        }
-        this.model.setDate(event.data.date);
-        this.reload();
-    },
-    /**
-     * @private
-     * @param {OdooEvent} event
-     */
-    _onChangeFilter: function (event) {
-        if (this.model.changeFilter(event.data) && !event.data.no_reload) {
-            this.reload();
-        }
-    },
-    /**
-     * @private
-     * @param {OdooEvent} event
-     */
-    _onDropRecord: function (event) {
-        this._updateRecord(_.extend({}, event.data, {
-            'drop': true,
-        }));
-    },
-    /**
-     * @private
-     * @param {OdooEvent} event
-     */
-    _onNext: function (event) {
-        event.stopPropagation();
-        this._move('next');
-    },
-    /**
-     * @private
-     * @param {OdooEvent} event
-     */
-    _onOpenCreate: function (event) {
-        var self = this;
+        },
+        /**
+         * In mobile, we want to display a special 'Today' button on the bottom
+         * right corner of the control panel. This is the pager area, and as there
+         * is no pager in Calendar views, we fool the system by defining a fake
+         * pager (which is actually our button) such that it will be inserted in the
+         * desired place.
+         *
+         * @todo get rid of this hack once the ControlPanel layout will be reworked
+         *
+         * @param {jQueryElement} $node the button should be appended to this
+         *   element to be displayed in the bottom right corner of the control panel
+         */
+        renderPager: function ($node) {
+            if (config.device.isMobile) {
+                this.$todayButton = $(QWeb.render("CalendarView.TodayButtonMobile"));
+                this.$todayButton.on("click", this._move.bind(this, "today"));
+                $node.append(this.$todayButton);
+            }
+        },
 
-        var data = this.model.calendarEventToRecord(event.data);
+        // --------------------------------------------------------------------------
+        // Private
+        // --------------------------------------------------------------------------
 
-        var context = _.extend({}, this.context, event.options && event.options.context);
-        context.default_name = data.name || null;
-        context['default_' + this.mapping.date_start] = data[this.mapping.date_start] || null;
-        if (this.mapping.date_stop) {
-            context['default_' + this.mapping.date_stop] = data[this.mapping.date_stop] || null;
-        }
-        if (this.mapping.date_delay) {
-            context['default_' + this.mapping.date_delay] = data[this.mapping.date_delay] || null;
-        }
-        if (this.mapping.all_day) {
-            context['default_' + this.mapping.all_day] = data[this.mapping.all_day] || null;
-        }
+        /**
+         * Move to the requested direction and reload the view
+         *
+         * @private
+         * @param {String} to either 'prev', 'next' or 'today'
+         * @returns {Deferred}
+         */
+        _move: function (to) {
+            this.model[to]();
+            return this.reload();
+        },
+        /**
+         * @private
+         * @param {Object} record
+         * @param {integer} record.id
+         * @returns {Deferred}
+         */
+        _updateRecord: function (record) {
+            return this.model.updateRecord(record).always(this.reload.bind(this));
+        },
 
-        for (var k in context) {
-            if (context[k] && context[k]._isAMomentObject) {
-                context[k] = dateToServer(context[k]);
+        // --------------------------------------------------------------------------
+        // Handlers
+        // --------------------------------------------------------------------------
+
+        /**
+         * @private
+         * @param {OdooEvent} event
+         */
+        _onChangeDate: function (event) {
+            var modelData = this.model.get();
+            if (
+                modelData.target_date.format("YYYY-MM-DD") ===
+                event.data.date.format("YYYY-MM-DD")
+            ) {
+                // When clicking on same date, toggle between the two views
+                switch (modelData.scale) {
+                    case "day":
+                        this.model.setScale("month");
+                        break;
+                }
+            } else if (modelData.target_date.week() === event.data.date.week()) {
+                // When clicking on a date in the same week, switch to day view
+                this.model.setScale("day");
+            } else {
+                // When clicking on a random day of a random other week, switch to week view
+                this.model.setScale("week");
+            }
+            this.model.setDate(event.data.date);
+            this.reload();
+        },
+        /**
+         * @private
+         * @param {OdooEvent} event
+         */
+        _onChangeFilter: function (event) {
+            if (this.model.changeFilter(event.data) && !event.data.no_reload) {
+                this.reload();
             }
-        }
+        },
+        /**
+         * @private
+         * @param {OdooEvent} event
+         */
+        _onDropRecord: function (event) {
+            this._updateRecord(
+                _.extend({}, event.data, {
+                    drop: true,
+                })
+            );
+        },
+        /**
+         * @private
+         * @param {OdooEvent} event
+         */
+        _onNext: function (event) {
+            event.stopPropagation();
+            this._move("next");
+        },
+        /**
+         * @private
+         * @param {OdooEvent} event
+         */
+        _onOpenCreate: function (event) {
+            var self = this;
 
-        var options = _.extend({}, this.options, event.options, {
-            context: context,
-            title: _.str.sprintf(_t('Create: %s'), (this.displayName || this.renderer.arch.attrs.string))
-        });
+            var data = this.model.calendarEventToRecord(event.data);
 
-        if (this.quick != null) {
-            this.quick.destroy();
-            this.quick = null;
-        }
+            var context = _.extend(
+                {},
+                this.context,
+                event.options && event.options.context
+            );
+            context.default_name = data.name || null;
+            context["default_" + this.mapping.date_start] =
+                data[this.mapping.date_start] || null;
+            if (this.mapping.date_stop) {
+                context["default_" + this.mapping.date_stop] =
+                    data[this.mapping.date_stop] || null;
+            }
+            if (this.mapping.date_delay) {
+                context["default_" + this.mapping.date_delay] =
+                    data[this.mapping.date_delay] || null;
+            }
+            if (this.mapping.all_day) {
+                context["default_" + this.mapping.all_day] =
+                    data[this.mapping.all_day] || null;
+            }
 
-        if (!options.disableQuickCreate && !event.data.disableQuickCreate && this.quickAddPop) {
-            this.quick = new QuickCreate(this, true, options, data, event.data);
-            this.quick.open();
-            this.quick.opened(function () {
-                self.quick.focus();
-            });
-            return;
-        }
+            for (var k in context) {
+                if (context[k] && context[k]._isAMomentObject) {
+                    context[k] = dateToServer(context[k]);
+                }
+            }
 
-        var title = _t("Create");
-        if (this.renderer.arch.attrs.string) {
-            title += ': ' + this.renderer.arch.attrs.string;
-        }
-        if (this.eventOpenPopup) {
-            new dialogs.FormViewDialog(self, {
-                res_model: this.modelName,
-                context: context,
-                title: title,
-                view_id: this.formViewId || false,
-                disable_multiple_selection: true,
-                on_saved: function () {
-                    if (event.data.on_save) {
-                        event.data.on_save();
-                    }
-                    self.reload();
-                },
-            }).open();
-        } else {
-            this.do_action({
-                type: 'ir.actions.act_window',
-                res_model: this.modelName,
-                views: [[this.formViewId || false, 'form']],
-                target: 'current',
+            var options = _.extend({}, this.options, event.options, {
                 context: context,
+                title: _.str.sprintf(
+                    _t("Create: %s"),
+                    this.displayName || this.renderer.arch.attrs.string
+                ),
             });
-        }
-    },
-    /**
-     * @private
-     * @param {OdooEvent} event
-     */
-    _onOpenEvent: function(event) {
-        var self = this;
-        var id = event.data.id;
-        id = id && parseInt(id).toString() === id ? parseInt(id) : id;
 
-        if (!this.eventOpenPopup) {
-            this._rpc({
-                model: self.modelName,
-                method: 'get_formview_id',
-                //The event can be called by a view that can have another context than the default one.
-                args: [[id], event.context || self.context],
-            }).then(function (viewId) {
-                self.do_action({
-                    type:'ir.actions.act_window',
-                    res_id: id,
-                    res_model: self.modelName,
-                    views: [[viewId || false, 'form']],
-                    target: 'current',
-                    context: event.context || self.context,
+            if (this.quick != null) {
+                this.quick.destroy();
+                this.quick = null;
+            }
+
+            if (
+                !options.disableQuickCreate &&
+                !event.data.disableQuickCreate &&
+                this.quickAddPop
+            ) {
+                this.quick = new QuickCreate(this, true, options, data, event.data);
+                this.quick.open();
+                this.quick.opened(function () {
+                    self.quick.focus();
                 });
-            });
-            return;
-        }
+                return;
+            }
 
-        var open_dialog = function (readonly) {
-            var options = {
-                res_model: self.modelName,
-                res_id: id || null,
-                context: event.context || self.context,
-                readonly: readonly,
-                title: _t("Open: ") + event.data.title,
-                on_saved: function () {
-                    if (event.data.on_save) {
-                        event.data.on_save();
-                    }
-                    self.reload();
-                },
-            };
-            if (readonly) {
-                if (self.readonlyFormViewId) {
-                    options.view_id = parseInt(self.readonlyFormViewId);
-                }
-                options.buttons = [
-                    {
-                        text: _t("Edit"),
-                        classes: 'btn-primary',
-                        close: true,
-                        click: function () { open_dialog(false); }
-                    },
-                    {
-                        text: _t("Delete"),
-                        click: function () {
-                            Dialog.confirm(this, _t("Are you sure you want to delete this record ?"), {
-                                confirm_callback: function () {
-                                    self.model.deleteRecords([id], self.modelName)
-                                        .then(function () {
-                                            self.dialog.destroy();
-                                            self.reload();
-                                        });
-                                }
-                            });
-                        },
+            var title = _t("Create");
+            if (this.renderer.arch.attrs.string) {
+                title += ": " + this.renderer.arch.attrs.string;
+            }
+            if (this.eventOpenPopup) {
+                new dialogs.FormViewDialog(self, {
+                    res_model: this.modelName,
+                    context: context,
+                    title: title,
+                    view_id: this.formViewId || false,
+                    disable_multiple_selection: true,
+                    on_saved: function () {
+                        if (event.data.on_save) {
+                            event.data.on_save();
+                        }
+                        self.reload();
                     },
-                    {text: _t("Close"), close: true}
-                ];
-            } else if (self.formViewId) {
-                options.view_id = parseInt(self.formViewId);
+                }).open();
+            } else {
+                this.do_action({
+                    type: "ir.actions.act_window",
+                    res_model: this.modelName,
+                    views: [[this.formViewId || false, "form"]],
+                    target: "current",
+                    context: context,
+                });
             }
-            self.dialog = new dialogs.FormViewDialog(self, options).open();
-        };
-        open_dialog(true);
-    },
-    /**
-     * @private
-     * @param {OdooEvent} event
-     */
-    _onPrev: function () {
-        event.stopPropagation();
-        this._move('prev');
-    },
+        },
+        /**
+         * @private
+         * @param {OdooEvent} event
+         */
+        _onOpenEvent: function (event) {
+            var self = this;
+            var id = event.data.id;
+            id = id && parseInt(id).toString() === id ? parseInt(id) : id;
 
-    /**
-     * Handles saving data coming from quick create box
-     *
-     * @private
-     * @param {OdooEvent} event
-     */
-    _onQuickCreate: function (event) {
-        var self = this;
-        if (this.quickCreating) {
-            return;
-        }
-        this.quickCreating = true;
-        this.model.createRecord(event)
-            .then(function () {
-                self.quick.destroy();
-                self.quick = null;
-                self.reload();
-            })
-            .fail(function (error, errorEvent) {
-                // This will occurs if there are some more fields required
-                // Preventdefaulting the error event will prevent the traceback window
-                errorEvent.preventDefault();
-                event.data.options.disableQuickCreate = true;
-                event.data.data.on_save = self.quick.destroy.bind(self.quick);
-                self._onOpenCreate(event.data);
-            })
-            .always(function () {
-                self.quickCreating = false;
-            });
-    },
-    /**
-     * Called when we want to open or close the sidebar.
-     *
-     * @private
-     */
-    _onToggleFullWidth: function () {
-        this.model.toggleFullWidth();
-        this.reload();
-    },
-    /**
-     * @private
-     * @param {OdooEvent} event
-     */
-    _onUpdateRecord: function (event) {
-        this._updateRecord(event.data);
-    },
-    /**
-     * The internal state of the calendar (mode, period displayed) has changed,
-     * so update the control panel buttons and breadcrumbs accordingly.
-     *
-     * @private
-     * @param {OdooEvent} event
-     */
-    _onViewUpdated: function (event) {
-        this.mode = event.data.mode;
-        if (this.$buttons) {
-            this.$buttons.find('.active').removeClass('active');
-            this.$buttons.find('.o_calendar_button_' + this.mode).addClass('active');
-        }
-        this.set({title: this.displayName + ' (' + event.data.title + ')'});
-    },
-});
+            if (!this.eventOpenPopup) {
+                this._rpc({
+                    model: self.modelName,
+                    method: "get_formview_id",
+                    // The event can be called by a view that can have another context than the default one.
+                    args: [[id], event.context || self.context],
+                }).then(function (viewId) {
+                    self.do_action({
+                        type: "ir.actions.act_window",
+                        res_id: id,
+                        res_model: self.modelName,
+                        views: [[viewId || false, "form"]],
+                        target: "current",
+                        context: event.context || self.context,
+                    });
+                });
+                return;
+            }
+
+            var open_dialog = function (readonly) {
+                var options = {
+                    res_model: self.modelName,
+                    res_id: id || null,
+                    context: event.context || self.context,
+                    readonly: readonly,
+                    title: _t("Open: ") + event.data.title,
+                    on_saved: function () {
+                        if (event.data.on_save) {
+                            event.data.on_save();
+                        }
+                        self.reload();
+                    },
+                };
+                if (readonly) {
+                    if (self.readonlyFormViewId) {
+                        options.view_id = parseInt(self.readonlyFormViewId);
+                    }
+                    options.buttons = [
+                        {
+                            text: _t("Edit"),
+                            classes: "btn-primary",
+                            close: true,
+                            click: function () {
+                                open_dialog(false);
+                            },
+                        },
+                        {
+                            text: _t("Delete"),
+                            click: function () {
+                                Dialog.confirm(
+                                    this,
+                                    _t("Are you sure you want to delete this record ?"),
+                                    {
+                                        confirm_callback: function () {
+                                            self.model
+                                                .deleteRecords([id], self.modelName)
+                                                .then(function () {
+                                                    self.dialog.destroy();
+                                                    self.reload();
+                                                });
+                                        },
+                                    }
+                                );
+                            },
+                        },
+                        {text: _t("Close"), close: true},
+                    ];
+                } else if (self.formViewId) {
+                    options.view_id = parseInt(self.formViewId);
+                }
+                self.dialog = new dialogs.FormViewDialog(self, options).open();
+            };
+            open_dialog(true);
+        },
+        /**
+         * @private
+         * @param {OdooEvent} event
+         */
+        _onPrev: function () {
+            event.stopPropagation();
+            this._move("prev");
+        },
 
-return ResourceController;
+        /**
+         * Handles saving data coming from quick create box
+         *
+         * @private
+         * @param {OdooEvent} event
+         */
+        _onQuickCreate: function (event) {
+            var self = this;
+            if (this.quickCreating) {
+                return;
+            }
+            this.quickCreating = true;
+            this.model
+                .createRecord(event)
+                .then(function () {
+                    self.quick.destroy();
+                    self.quick = null;
+                    self.reload();
+                })
+                .fail(function (error, errorEvent) {
+                    // This will occurs if there are some more fields required
+                    // Preventdefaulting the error event will prevent the traceback window
+                    errorEvent.preventDefault();
+                    event.data.options.disableQuickCreate = true;
+                    event.data.data.on_save = self.quick.destroy.bind(self.quick);
+                    self._onOpenCreate(event.data);
+                })
+                .always(function () {
+                    self.quickCreating = false;
+                });
+        },
+        /**
+         * Called when we want to open or close the sidebar.
+         *
+         * @private
+         */
+        _onToggleFullWidth: function () {
+            this.model.toggleFullWidth();
+            this.reload();
+        },
+        /**
+         * @private
+         * @param {OdooEvent} event
+         */
+        _onUpdateRecord: function (event) {
+            this._updateRecord(event.data);
+        },
+        /**
+         * The internal state of the calendar (mode, period displayed) has changed,
+         * so update the control panel buttons and breadcrumbs accordingly.
+         *
+         * @private
+         * @param {OdooEvent} event
+         */
+        _onViewUpdated: function (event) {
+            this.mode = event.data.mode;
+            if (this.$buttons) {
+                this.$buttons.find(".active").removeClass("active");
+                this.$buttons
+                    .find(".o_calendar_button_" + this.mode)
+                    .addClass("active");
+            }
+            this.set({title: this.displayName + " (" + event.data.title + ")"});
+        },
+    });
 
+    return ResourceController;
 });
diff --git a/static/src/js/resource_model.js b/static/src/js/resource_model.js
index 2b083fe..c83be1d 100644
--- a/static/src/js/resource_model.js
+++ b/static/src/js/resource_model.js
@@ -1,794 +1,882 @@
-odoo.define('cgscop_fullcalendar.ResourceModel', function (require) {
-"use strict";
-
-var AbstractModel = require('web.AbstractModel');
-var Context = require('web.Context');
-var core = require('web.core');
-var fieldUtils = require('web.field_utils');
-var session = require('web.session');
-
-var _t = core._t;
-
-var scales = [
-    'day',
-];
-
-function dateToServer (date) {
-    return date.clone().utc().locale('en').format('YYYY-MM-DD HH:mm:ss');
-}
-
-return AbstractModel.extend({
-    /**
-     * @override
-     */
-    init: function () {
-        this._super.apply(this, arguments);
-        this.end_date = null;
-        var week_start = _t.database.parameters.week_start;
-        // calendar uses index 0 for Sunday but Odoo stores it as 7
-        this.week_start = week_start !== undefined && week_start !== false ? week_start % 7 : moment().startOf('week').day();
-        this.week_stop = this.week_start + 6;
-    },
-
-    //--------------------------------------------------------------------------
-    // Public
-    //--------------------------------------------------------------------------
-
-    /**
-     * Transform fullcalendar event object to OpenERP Data object
-     */
-    calendarEventToRecord: function (event) {
-        // Normalize event_end without changing fullcalendars event.
-        var data = {
-            'name': event.title,
-            'start': moment(event.start).format('YYYY-MM-DD HH:mm:ss'),
-            'stop': moment(event.end).format('YYYY-MM-DD HH:mm:ss'),
-            'allday': event.start.allDay,
-        };
-        // var start = moment(event.start.start).clone();
-        // var end = moment(event.start.end) && moment(event.start.end).clone();
-
-
-
-        // // Detects allDay events (86400000 = 1 day in ms)
-        // if (event.allDay || (end && end.diff(start) % 86400000 === 0)) {
-        //     event.setProp('allDay', true);
-        // }
-
-        // // Set end date if not existing
-        // if (!end || end.diff(start) < 0) { // undefined or invalid end date
-        //     if (event.allDay) {
-        //         end = start.clone();
-        //     } else {
-        //         // in week mode or day mode, convert allday event to event
-        //         end = start.clone().add(2, 'h');
-        //     }
-        // } else if (event.allDay) {
-        //     // For an "allDay", FullCalendar gives the end day as the
-        //     // next day at midnight (instead of 23h59).
-        //     end.add(-1, 'days');
-        // }
-
-        // var isDateEvent = this.fields[this.mapping.date_start].type === 'date';
-        // // An "allDay" event without the "all_day" option is not considered
-        // // as a 24h day. It's just a part of the day (by default: 7h-19h).
-        // if (event.allDay) {
-        //     if (!this.mapping.all_day && !isDateEvent) {
-        //         if (event.r_start) {
-        //             start.hours(event.r_start.hours())
-        //                  .minutes(event.r_start.minutes())
-        //                  .seconds(event.r_start.seconds())
-        //                  .utc();
-        //             end.hours(event.r_end.hours())
-        //                .minutes(event.r_end.minutes())
-        //                .seconds(event.r_end.seconds())
-        //                .utc();
-        //         } else {
-        //             // default hours in the user's timezone
-        //             start.hours(7);
-        //             end.hours(19);
-        //         }
-        //         start.add(-this.getSession().getTZOffset(start), 'minutes');
-        //         end.add(-this.getSession().getTZOffset(end), 'minutes');
-        //     }
-        // } else {
-        //     start.add(-this.getSession().getTZOffset(start), 'minutes');
-        //     end.add(-this.getSession().getTZOffset(end), 'minutes');
-        // }
-
-        // if (this.mapping.all_day) {
-        //     if (event.record) {
-        //         data[this.mapping.all_day] =
-        //             (this.scale !== 'month' && event.allDay) ||
-        //             event.record[this.mapping.all_day] &&
-        //             end.diff(start) < 10 ||
-        //             false;
-        //     } else {
-        //         data[this.mapping.all_day] = event.allDay;
-        //     }
-        // }
-
-        // data[this.mapping.date_start] = start;
-        // if (this.mapping.date_stop) {
-        //     data[this.mapping.date_stop] = end;
-        // }
-
-        // if (this.mapping.date_delay) {
-        //     if (this.data.scale !== 'month' || (this.data.scale === 'month' && !event.drop)) {
-        //         data[this.mapping.date_delay] = (end.diff(start) <= 0 ? end.endOf('day').diff(start) : end.diff(start)) / 1000 / 3600;
-        //     }
-        // }
-
-        return data;
-    },
-    /**
-     * @param {Object} filter
-     * @returns {boolean}
-     */
-    changeFilter: function (filter) {
-        var Filter = this.data.filters[filter.fieldName];
-        if (filter.value === 'all') {
-            Filter.all = filter.active;
-        }
-        var f = _.find(Filter.filters, function (f) {
-            return f.value === filter.value;
-        });
-        if (f) {
-            if (f.active !== filter.active) {
-                f.active = filter.active;
-            } else {
-                return false;
+odoo.define("cgscop_fullcalendar.ResourceModel", function (require) {
+    "use strict";
+
+    var AbstractModel = require("web.AbstractModel");
+    var Context = require("web.Context");
+    var core = require("web.core");
+    var fieldUtils = require("web.field_utils");
+    var session = require("web.session");
+
+    var _t = core._t;
+
+    var scales = ["day"];
+
+    function dateToServer(date) {
+        return date.clone().utc().locale("en").format("YYYY-MM-DD HH:mm:ss");
+    }
+
+    return AbstractModel.extend({
+        /**
+         * @override
+         */
+        init: function () {
+            this._super.apply(this, arguments);
+            this.end_date = null;
+            var week_start = _t.database.parameters.week_start;
+            // Calendar uses index 0 for Sunday but Odoo stores it as 7
+            this.week_start =
+                week_start !== undefined && week_start !== false
+                    ? week_start % 7
+                    : moment().startOf("week").day();
+            this.week_stop = this.week_start + 6;
+        },
+
+        // --------------------------------------------------------------------------
+        // Public
+        // --------------------------------------------------------------------------
+
+        /**
+         * Transform fullcalendar event object to OpenERP Data object
+         */
+        calendarEventToRecord: function (event) {
+            // Normalize event_end without changing fullcalendars event.
+            var data = {
+                name: event.title,
+                start: moment(event.start).format("YYYY-MM-DD HH:mm:ss"),
+                stop: moment(event.end).format("YYYY-MM-DD HH:mm:ss"),
+                allday: event.start.allDay,
+            };
+            // Var start = moment(event.start.start).clone();
+            // var end = moment(event.start.end) && moment(event.start.end).clone();
+
+            // // Detects allDay events (86400000 = 1 day in ms)
+            // if (event.allDay || (end && end.diff(start) % 86400000 === 0)) {
+            //     event.setProp('allDay', true);
+            // }
+
+            // // Set end date if not existing
+            // if (!end || end.diff(start) < 0) { // undefined or invalid end date
+            //     if (event.allDay) {
+            //         end = start.clone();
+            //     } else {
+            //         // in week mode or day mode, convert allday event to event
+            //         end = start.clone().add(2, 'h');
+            //     }
+            // } else if (event.allDay) {
+            //     // For an "allDay", FullCalendar gives the end day as the
+            //     // next day at midnight (instead of 23h59).
+            //     end.add(-1, 'days');
+            // }
+
+            // var isDateEvent = this.fields[this.mapping.date_start].type === 'date';
+            // // An "allDay" event without the "all_day" option is not considered
+            // // as a 24h day. It's just a part of the day (by default: 7h-19h).
+            // if (event.allDay) {
+            //     if (!this.mapping.all_day && !isDateEvent) {
+            //         if (event.r_start) {
+            //             start.hours(event.r_start.hours())
+            //                  .minutes(event.r_start.minutes())
+            //                  .seconds(event.r_start.seconds())
+            //                  .utc();
+            //             end.hours(event.r_end.hours())
+            //                .minutes(event.r_end.minutes())
+            //                .seconds(event.r_end.seconds())
+            //                .utc();
+            //         } else {
+            //             // default hours in the user's timezone
+            //             start.hours(7);
+            //             end.hours(19);
+            //         }
+            //         start.add(-this.getSession().getTZOffset(start), 'minutes');
+            //         end.add(-this.getSession().getTZOffset(end), 'minutes');
+            //     }
+            // } else {
+            //     start.add(-this.getSession().getTZOffset(start), 'minutes');
+            //     end.add(-this.getSession().getTZOffset(end), 'minutes');
+            // }
+
+            // if (this.mapping.all_day) {
+            //     if (event.record) {
+            //         data[this.mapping.all_day] =
+            //             (this.scale !== 'month' && event.allDay) ||
+            //             event.record[this.mapping.all_day] &&
+            //             end.diff(start) < 10 ||
+            //             false;
+            //     } else {
+            //         data[this.mapping.all_day] = event.allDay;
+            //     }
+            // }
+
+            // data[this.mapping.date_start] = start;
+            // if (this.mapping.date_stop) {
+            //     data[this.mapping.date_stop] = end;
+            // }
+
+            // if (this.mapping.date_delay) {
+            //     if (this.data.scale !== 'month' || (this.data.scale === 'month' && !event.drop)) {
+            //         data[this.mapping.date_delay] = (end.diff(start) <= 0 ? end.endOf('day').diff(start) : end.diff(start)) / 1000 / 3600;
+            //     }
+            // }
+
+            return data;
+        },
+        /**
+         * @param {Object} filter
+         * @returns {Boolean}
+         */
+        changeFilter: function (filter) {
+            var Filter = this.data.filters[filter.fieldName];
+            if (filter.value === "all") {
+                Filter.all = filter.active;
             }
-        } else if (filter.active) {
-            Filter.filters.push({
-                value: filter.value,
-                active: true,
+            var f = _.find(Filter.filters, function (f) {
+                return f.value === filter.value;
             });
-        }
-        return true;
-    },
-    /**
-     * @param {OdooEvent} event
-     */
-    createRecord: function (event) {
-        var data = this.calendarEventToRecord(event.data.data);
-        for (var k in data) {
-            if (data[k] && data[k]._isAMomentObject) {
-                data[k] = dateToServer(data[k]);
+            if (f) {
+                if (f.active !== filter.active) {
+                    f.active = filter.active;
+                } else {
+                    return false;
+                }
+            } else if (filter.active) {
+                Filter.filters.push({
+                    value: filter.value,
+                    active: true,
+                });
+            }
+            return true;
+        },
+        /**
+         * @param {OdooEvent} event
+         */
+        createRecord: function (event) {
+            var data = this.calendarEventToRecord(event.data.data);
+            for (var k in data) {
+                if (data[k] && data[k]._isAMomentObject) {
+                    data[k] = dateToServer(data[k]);
+                }
             }
-        }
-        return this._rpc({
+            return this._rpc({
                 model: this.modelName,
-                method: 'create',
+                method: "create",
                 args: [data],
                 context: event.data.options.context,
             });
-    },
-    /**
-     * @todo I think this is dead code
-     *
-     * @param {any} ids
-     * @param {any} model
-     * @returns
-     */
-    deleteRecords: function (ids, model) {
-        return this._rpc({
+        },
+        /**
+         * @todo I think this is dead code
+         *
+         * @param {any} ids
+         * @param {any} model
+         * @returns
+         */
+        deleteRecords: function (ids, model) {
+            return this._rpc({
                 model: model,
-                method: 'unlink',
+                method: "unlink",
                 args: [ids],
-                context: session.user_context, // todo: combine with view context
+                context: session.user_context, // Todo: combine with view context
             });
-    },
-    /**
-     * @override
-     * @returns {Object}
-     */
-    get: function () {
-        return _.extend({}, this.data, {
-            fields: this.fields
-        });
-    },
-    /**
-     * @override
-     * @param {any} params
-     * @returns {Deferred}
-     */
-    load: function (params) {
-        var self = this;
-
-        this.resourceField = params.resourceField;
-
-        this.modelName = params.modelName;
-        this.fields = params.fields;
-        this.fieldNames = params.fieldNames;
-        this.fieldsInfo = params.fieldsInfo;
-        this.mapping = params.mapping;
-        this.mode = params.mode;       // one of month, week or day
-        this.scales = params.scales;   // one of month, week or day
-
-        // Check whether the date field is editable (i.e. if the events can be
-        // dragged and dropped)
-        this.editable = params.editable;
-        this.creatable = params.creatable;
-
-        // display more button when there are too much event on one day
-        this.eventLimit = params.eventLimit;
-
-        // fields to display color, e.g.: user_id.partner_id
-        this.fieldColor = params.fieldColor;
-        if (!this.preload_def) {
-            this.preload_def = $.Deferred();
-            $.when(
-                this._rpc({model: this.modelName, method: 'check_access_rights', args: ["write", false]}),
-                this._rpc({model: this.modelName, method: 'check_access_rights', args: ["create", false]}))
-            .then(function (write, create) {
-                self.write_right = write;
-                self.create_right = create;
-                self.preload_def.resolve();
+        },
+        /**
+         * @override
+         * @returns {Object}
+         */
+        get: function () {
+            return _.extend({}, this.data, {
+                fields: this.fields,
             });
-        }
-
-        this.data = {
-            domain: params.domain,
-            context: params.context,
-            // get in arch the filter to display in the sidebar and the field to read
-            filters: params.filters,
-        };
-
-        this.setDate(params.initialDate);
-        // Use mode attribute in xml file to specify zoom timeline (day,week,month)
-        // by default month.
-        this.setScale(params.mode);
-
-        _.each(this.data.filters, function (filter) {
-            if (filter.avatar_field && !filter.avatar_model) {
-                filter.avatar_model = self.modelName;
+        },
+        /**
+         * @override
+         * @param {any} params
+         * @returns {Deferred}
+         */
+        load: function (params) {
+            var self = this;
+
+            this.resourceField = params.resourceField;
+
+            this.modelName = params.modelName;
+            this.fields = params.fields;
+            this.fieldNames = params.fieldNames;
+            this.fieldsInfo = params.fieldsInfo;
+            this.mapping = params.mapping;
+            this.mode = params.mode; // One of month, week or day
+            this.scales = params.scales; // One of month, week or day
+
+            // Check whether the date field is editable (i.e. if the events can be
+            // dragged and dropped)
+            this.editable = params.editable;
+            this.creatable = params.creatable;
+
+            // Display more button when there are too much event on one day
+            this.eventLimit = params.eventLimit;
+
+            // Fields to display color, e.g.: user_id.partner_id
+            this.fieldColor = params.fieldColor;
+            if (!this.preload_def) {
+                this.preload_def = $.Deferred();
+                $.when(
+                    this._rpc({
+                        model: this.modelName,
+                        method: "check_access_rights",
+                        args: ["write", false],
+                    }),
+                    this._rpc({
+                        model: this.modelName,
+                        method: "check_access_rights",
+                        args: ["create", false],
+                    })
+                ).then(function (write, create) {
+                    self.write_right = write;
+                    self.create_right = create;
+                    self.preload_def.resolve();
+                });
             }
-        });
-
-        return this.preload_def.then(this._loadCalendar.bind(this));
-    },
-    /**
-     * Move the current date range to the next period
-     */
-    next: function () {
-        this.setDate(this.data.target_date.clone().add(1, this.data.scale));
-    },
-    /**
-     * Move the current date range to the previous period
-     */
-    prev: function () {
-        this.setDate(this.data.target_date.clone().add(-1, this.data.scale));
-    },
-    /**
-     * @override
-     * @param {Object} [params.context]
-     * @param {Array} [params.domain]
-     * @returns {Deferred}
-     */
-    reload: function (handle, params) {
-        if (params.domain) {
-            this.data.domain = params.domain;
-        }
-        if (params.context) {
-            this.data.context = params.context;
-        }
-        return this._loadCalendar();
-    },
-    /**
-     * @param {Moment} start. in local TZ
-     */
-    setDate: function (start) {
-        // keep highlight/target_date in localtime
-        this.data.highlight_date = this.data.target_date = start.clone();
-        this.data.start_date = this.data.end_date = start;
-        switch (this.data.scale) {
-            case 'month':
-                var monthStart = this.data.start_date.clone().startOf('month');
-
-                var monthStartDay;
-                if (monthStart.day() >= this.week_start) {
-                    // the month's first day is after our week start
-                    // Then we are in the right week
-                    monthStartDay = this.week_start;
-                } else {
-                    // The month's first day is before our week start
-                    // Then we should go back to the the previous week
-                    monthStartDay = this.week_start - 7;
-                }
 
-                this.data.start_date = monthStart.day(monthStartDay).startOf('day');
-                this.data.end_date = this.data.start_date.clone().add(5, 'week').day(this.week_stop).endOf('day');
-                break;
-            case 'week':
-                var weekStart = this.data.start_date.clone().startOf('week');
-                var weekStartDay = this.week_start;
-                if (this.data.start_date.day() < this.week_start) {
-                    // The week's first day is after our current day
-                    // Then we should go back to the previous week
-                    weekStartDay -= 7;
+            this.data = {
+                domain: params.domain,
+                context: params.context,
+                // Get in arch the filter to display in the sidebar and the field to read
+                filters: params.filters,
+            };
+
+            this.setDate(params.initialDate);
+            // Use mode attribute in xml file to specify zoom timeline (day,week,month)
+            // by default month.
+            this.setScale(params.mode);
+
+            _.each(this.data.filters, function (filter) {
+                if (filter.avatar_field && !filter.avatar_model) {
+                    filter.avatar_model = self.modelName;
                 }
-                this.data.start_date = this.data.start_date.clone().day(weekStartDay).startOf('day');
-                this.data.end_date = this.data.end_date.clone().day(weekStartDay + 6).endOf('day');
-                break;
-            default:
-                this.data.start_date = this.data.start_date.clone().startOf('day');
-                this.data.end_date = this.data.end_date.clone().endOf('day');
-        }
-        // We have set start/stop datetime as definite begin/end boundaries of a period (month, week, day)
-        // in local TZ (what is the begining of the week *I am* in ?)
-        // The following code:
-        // - converts those to UTC using our homemade method (testable)
-        // - sets the moment UTC flag to true, to ensure compatibility with third party libs
-        var manualUtcDateStart = this.data.start_date.clone().add(-this.getSession().getTZOffset(this.data.start_date), 'minutes');
-        var formattedUtcDateStart = manualUtcDateStart.format('YYYY-MM-DDTHH:mm:ss') + 'Z';
-        this.data.start_date = moment.utc(formattedUtcDateStart);
-
-        var manualUtcDateEnd = this.data.end_date.clone().add(-this.getSession().getTZOffset(this.data.start_date), 'minutes');
-        var formattedUtcDateEnd = manualUtcDateEnd.format('YYYY-MM-DDTHH:mm:ss') + 'Z';
-        this.data.end_date = moment.utc(formattedUtcDateEnd);
-    },
-    /**
-     * @param {string} scale the scale to set
-     */
-    setScale: function (scale) {
-        if (!_.contains(scales, scale)) {
-            scale = "day";
-        }
-        this.data.scale = scale;
-        this.setDate(this.data.target_date);
-    },
-    /**
-     * Move the current date range to the period containing today
-     */
-    today: function () {
-        this.setDate(moment(new Date()));
-    },
-    /**
-     * Toggle the sidebar (containing the mini calendar)
-     */
-    toggleFullWidth: function () {
-        var fullWidth = this.call('local_storage', 'getItem', 'calendar_fullWidth') !== true;
-        this.call('local_storage', 'setItem', 'calendar_fullWidth', fullWidth);
-    },
-    /**
-     * @param {Object} record
-     * @param {integer} record.id
-     * @returns {Deferred}
-     */
-    updateRecord: function (record) {
-        // Cannot modify actual name yet
-        var data = _.omit(this.calendarEventToRecord(record.event), 'name');
-        for (var k in data) {
-            if (data[k] && data[k]._isAMomentObject) {
-                data[k] = dateToServer(data[k]);
-            }
-        }
-        var context = new Context(this.data.context, {from_ui: true});
-        return this._rpc({
-            model: this.modelName,
-            method: 'write',
-            args: [[record.event.id], data],
-            context: context
-        });
-    },
-
-    //--------------------------------------------------------------------------
-    // Private
-    //--------------------------------------------------------------------------
-
-    /**
-     * Converts this.data.filters into a domain
-     *
-     * @private
-     * @returns {Array}
-     */
-    _getFilterDomain: function () {
-        // List authorized values for every field
-        // fields with an active 'all' filter are skipped
-        var authorizedValues = {};
-        var avoidValues = {};
-
-        _.each(this.data.filters, function (filter) {
-            // Skip 'all' filters because they do not affect the domain
-            if (filter.all) return;
-
-            // Loop over subfilters to complete authorizedValues
-            _.each(filter.filters, function (f) {
-                if (filter.write_model) {
-                    if (!authorizedValues[filter.fieldName])
-                        authorizedValues[filter.fieldName] = [];
+            });
 
-                    if (f.active) {
-                        authorizedValues[filter.fieldName].push(f.value);
+            return this.preload_def.then(this._loadCalendar.bind(this));
+        },
+        /**
+         * Move the current date range to the next period
+         */
+        next: function () {
+            this.setDate(this.data.target_date.clone().add(1, this.data.scale));
+        },
+        /**
+         * Move the current date range to the previous period
+         */
+        prev: function () {
+            this.setDate(this.data.target_date.clone().add(-1, this.data.scale));
+        },
+        /**
+         * @override
+         * @param {Object} [params.context]
+         * @param {Array} [params.domain]
+         * @returns {Deferred}
+         */
+        reload: function (handle, params) {
+            if (params.domain) {
+                this.data.domain = params.domain;
+            }
+            if (params.context) {
+                this.data.context = params.context;
+            }
+            return this._loadCalendar();
+        },
+        /**
+         * @param {Moment} start. in local TZ
+         */
+        setDate: function (start) {
+            // Keep highlight/target_date in localtime
+            this.data.highlight_date = this.data.target_date = start.clone();
+            this.data.start_date = this.data.end_date = start;
+            switch (this.data.scale) {
+                case "month":
+                    var monthStart = this.data.start_date.clone().startOf("month");
+
+                    var monthStartDay;
+                    if (monthStart.day() >= this.week_start) {
+                        // The month's first day is after our week start
+                        // Then we are in the right week
+                        monthStartDay = this.week_start;
+                    } else {
+                        // The month's first day is before our week start
+                        // Then we should go back to the the previous week
+                        monthStartDay = this.week_start - 7;
                     }
-                } else {
-                    if (!avoidValues[filter.fieldName])
-                        avoidValues[filter.fieldName] = [];
 
-                    if (!f.active) {
-                        avoidValues[filter.fieldName].push(f.value);
+                    this.data.start_date = monthStart.day(monthStartDay).startOf("day");
+                    this.data.end_date = this.data.start_date
+                        .clone()
+                        .add(5, "week")
+                        .day(this.week_stop)
+                        .endOf("day");
+                    break;
+                case "week":
+                    var weekStart = this.data.start_date.clone().startOf("week");
+                    var weekStartDay = this.week_start;
+                    if (this.data.start_date.day() < this.week_start) {
+                        // The week's first day is after our current day
+                        // Then we should go back to the previous week
+                        weekStartDay -= 7;
                     }
+                    this.data.start_date = this.data.start_date
+                        .clone()
+                        .day(weekStartDay)
+                        .startOf("day");
+                    this.data.end_date = this.data.end_date
+                        .clone()
+                        .day(weekStartDay + 6)
+                        .endOf("day");
+                    break;
+                default:
+                    this.data.start_date = this.data.start_date.clone().startOf("day");
+                    this.data.end_date = this.data.end_date.clone().endOf("day");
+            }
+            // We have set start/stop datetime as definite begin/end boundaries of a period (month, week, day)
+            // in local TZ (what is the begining of the week *I am* in ?)
+            // The following code:
+            // - converts those to UTC using our homemade method (testable)
+            // - sets the moment UTC flag to true, to ensure compatibility with third party libs
+            var manualUtcDateStart = this.data.start_date
+                .clone()
+                .add(-this.getSession().getTZOffset(this.data.start_date), "minutes");
+            var formattedUtcDateStart =
+                manualUtcDateStart.format("YYYY-MM-DDTHH:mm:ss") + "Z";
+            this.data.start_date = moment.utc(formattedUtcDateStart);
+
+            var manualUtcDateEnd = this.data.end_date
+                .clone()
+                .add(-this.getSession().getTZOffset(this.data.start_date), "minutes");
+            var formattedUtcDateEnd =
+                manualUtcDateEnd.format("YYYY-MM-DDTHH:mm:ss") + "Z";
+            this.data.end_date = moment.utc(formattedUtcDateEnd);
+        },
+        /**
+         * @param {String} scale the scale to set
+         */
+        setScale: function (scale) {
+            if (!_.contains(scales, scale)) {
+                scale = "day";
+            }
+            this.data.scale = scale;
+            this.setDate(this.data.target_date);
+        },
+        /**
+         * Move the current date range to the period containing today
+         */
+        today: function () {
+            this.setDate(moment(new Date()));
+        },
+        /**
+         * Toggle the sidebar (containing the mini calendar)
+         */
+        toggleFullWidth: function () {
+            var fullWidth =
+                this.call("local_storage", "getItem", "calendar_fullWidth") !== true;
+            this.call("local_storage", "setItem", "calendar_fullWidth", fullWidth);
+        },
+        /**
+         * @param {Object} record
+         * @param {integer} record.id
+         * @returns {Deferred}
+         */
+        updateRecord: function (record) {
+            // Cannot modify actual name yet
+            var data = _.omit(this.calendarEventToRecord(record.event), "name");
+            for (var k in data) {
+                if (data[k] && data[k]._isAMomentObject) {
+                    data[k] = dateToServer(data[k]);
                 }
+            }
+            var context = new Context(this.data.context, {from_ui: true});
+            return this._rpc({
+                model: this.modelName,
+                method: "write",
+                args: [[record.event.id], data],
+                context: context,
+            });
+        },
+
+        // --------------------------------------------------------------------------
+        // Private
+        // --------------------------------------------------------------------------
+
+        /**
+         * Converts this.data.filters into a domain
+         *
+         * @private
+         * @returns {Array}
+         */
+        _getFilterDomain: function () {
+            // List authorized values for every field
+            // fields with an active 'all' filter are skipped
+            var authorizedValues = {};
+            var avoidValues = {};
+
+            _.each(this.data.filters, function (filter) {
+                // Skip 'all' filters because they do not affect the domain
+                if (filter.all) return;
+
+                // Loop over subfilters to complete authorizedValues
+                _.each(filter.filters, function (f) {
+                    if (filter.write_model) {
+                        if (!authorizedValues[filter.fieldName])
+                            authorizedValues[filter.fieldName] = [];
+
+                        if (f.active) {
+                            authorizedValues[filter.fieldName].push(f.value);
+                        }
+                    } else {
+                        if (!avoidValues[filter.fieldName])
+                            avoidValues[filter.fieldName] = [];
+
+                        if (!f.active) {
+                            avoidValues[filter.fieldName].push(f.value);
+                        }
+                    }
+                });
             });
-        });
-
-        // Compute the domain
-        var domain = [];
-        for (var field in authorizedValues) {
-            domain.push([field, 'in', authorizedValues[field]]);
-        }
-        for (var field in avoidValues) {
-            if (avoidValues[field].length > 0) {
-                domain.push([field, 'not in', avoidValues[field]]);
+
+            // Compute the domain
+            var domain = [];
+            for (var field in authorizedValues) {
+                domain.push([field, "in", authorizedValues[field]]);
             }
-        }
-
-        return domain;
-    },
-    /**
-     * @private
-     * @returns {Object}
-     */
-    _getFullCalendarOptions: function () {
-        return {
-            plugins: ['timeGrid', 'interaction', 'resourceDayGrid','resourceTimeGrid', 'moment'],
-            defaultView: 'resourceTimeGridDay',
-            allDaySlot: true,
-            allDayText: _t("All day"),
-            businessHours: {
-                daysOfWeek: [ 1, 2, 3, 4, 5 ],
-                startTime: '08:00',
-                endTime: '18:00',
-            },
-            dayNames: moment.weekdays(),
-            dayNamesShort: moment.weekdaysShort(),
-            droppable: false,
-            editable: false,
-            eventStartEditable: false,
-            eventLimit: this.eventLimit,
-            eventResizableFromStart: false,
-            firstDay: this.week_start,
-            header: false,
-            locale: 'fr',
-            longPressDelay: 500,
-            maxTime: '20:00:00',
-            minTime: '07:30:00',
-            monthNames: moment.months(),
-            monthNamesShort: moment.monthsShort(),
-            navLinks: false,
-            nowIndicator: true,
-            resourceOrder: 'title',
-            selectable: false,
-            selectHelper: false,
-            selectAllow: false,
-            snapMinutes: 30,
-            timeZone: 'Europe/Paris',
-            weekends: true,
-            weekNumbers: true,
-            weekNumberTitle: _t("W"),
-            schedulerLicenseKey: 'GPL-My-Project-Is-Open-Source',
-        };
-    },
-    /**
-     * Return a domain from the date range
-     *
-     * @private
-     * @returns {Array} A domain containing datetimes start and stop in UTC
-     *  those datetimes are formatted according to server's standards
-     */
-    _getRangeDomain: function () {
-        // Build OpenERP Domain to filter object by this.mapping.date_start field
-        // between given start, end dates.
-        var domain = [[this.mapping.date_start, '<=', dateToServer(this.data.end_date)]];
-        if (this.mapping.date_stop) {
-            domain.push([this.mapping.date_stop, '>=', dateToServer(this.data.start_date)]);
-        } else if (!this.mapping.date_delay) {
-            domain.push([this.mapping.date_start, '>=', dateToServer(this.data.start_date)]);
-        }
-        return domain;
-    },
-    /**
-     * @private
-     * @returns {Deferred}
-     */
-    _getResources: function () {
-        var self = this;
-        var resource = this.fields[this.resourceField]
-        return this._rpc({
+            for (var field in avoidValues) {
+                if (avoidValues[field].length > 0) {
+                    domain.push([field, "not in", avoidValues[field]]);
+                }
+            }
+
+            return domain;
+        },
+        /**
+         * @private
+         * @returns {Object}
+         */
+        _getFullCalendarOptions: function () {
+            return {
+                plugins: [
+                    "timeGrid",
+                    "interaction",
+                    "resourceDayGrid",
+                    "resourceTimeGrid",
+                    "moment",
+                ],
+                defaultView: "resourceTimeGridDay",
+                allDaySlot: true,
+                allDayText: _t("All day"),
+                businessHours: {
+                    daysOfWeek: [1, 2, 3, 4, 5],
+                    startTime: "08:00",
+                    endTime: "18:00",
+                },
+                dayNames: moment.weekdays(),
+                dayNamesShort: moment.weekdaysShort(),
+                droppable: false,
+                editable: false,
+                eventStartEditable: false,
+                eventLimit: this.eventLimit,
+                eventResizableFromStart: false,
+                firstDay: this.week_start,
+                header: false,
+                locale: "fr",
+                longPressDelay: 500,
+                maxTime: "20:00:00",
+                minTime: "07:30:00",
+                monthNames: moment.months(),
+                monthNamesShort: moment.monthsShort(),
+                navLinks: false,
+                nowIndicator: true,
+                resourceOrder: "title",
+                selectable: false,
+                selectHelper: false,
+                selectAllow: false,
+                snapMinutes: 30,
+                timeZone: "Europe/Paris",
+                weekends: true,
+                weekNumbers: true,
+                weekNumberTitle: _t("W"),
+                schedulerLicenseKey: "GPL-My-Project-Is-Open-Source",
+            };
+        },
+        /**
+         * Return a domain from the date range
+         *
+         * @private
+         * @returns {Array} A domain containing datetimes start and stop in UTC
+         *  those datetimes are formatted according to server's standards
+         */
+        _getRangeDomain: function () {
+            // Build OpenERP Domain to filter object by this.mapping.date_start field
+            // between given start, end dates.
+            var domain = [
+                [this.mapping.date_start, "<=", dateToServer(this.data.end_date)],
+            ];
+            if (this.mapping.date_stop) {
+                domain.push([
+                    this.mapping.date_stop,
+                    ">=",
+                    dateToServer(this.data.start_date),
+                ]);
+            } else if (!this.mapping.date_delay) {
+                domain.push([
+                    this.mapping.date_start,
+                    ">=",
+                    dateToServer(this.data.start_date),
+                ]);
+            }
+            return domain;
+        },
+        /**
+         * @private
+         * @returns {Deferred}
+         */
+        _getResources: function () {
+            var self = this;
+            var resource = this.fields[this.resourceField];
+            return this._rpc({
                 model: resource.relation,
-                method: 'search_read',
+                method: "search_read",
                 context: self.data.context,
-                fields: ['id', 'name'],
-                domain: resource.domain.concat([["ur_id", "=", session.ur_id], ["active", "=", true]]),
-                order: 'id',
-            })
-            .then(function (resources) {
-                self.data.resource = _.map(resources, function(r) {
+                fields: ["id", "name"],
+                domain: resource.domain.concat([
+                    ["ur_id", "=", session.ur_id],
+                    ["active", "=", true],
+                ]),
+                order: "id",
+            }).then(function (resources) {
+                self.data.resource = _.map(resources, function (r) {
                     return {
-                        'id': r.id,
-                        'title': r.name
-                    }
+                        id: r.id,
+                        title: r.name,
+                    };
                 });
             });
-    },
-    /**
-     * @private
-     * @returns {Deferred}
-     */
-    _loadCalendar: function () {
-        var self = this;
-        this.data.fullWidth = this.call('local_storage', 'getItem', 'calendar_fullWidth') === true;
-        this.data.fc_options = this._getFullCalendarOptions();
-        this._getResources();
-
-        var defs = _.map(this.data.filters, this._loadFilter.bind(this));
-
-        return $.when.apply($, defs).then(function () {
-            return self._rpc({
-                    model: self.modelName,
-                    method: 'search_read',
-                    context: self.data.context,
-                    fields: self.fieldNames,
-                    domain: self.data.domain.concat(self._getRangeDomain()).concat(self._getFilterDomain())
-            })
-            .then(function (events) {
-                self._parseServerData(events);
-                self.data.data = _.map(events, self._recordToCalendarEvent.bind(self));
-                return $.when(
-                    self._loadColors(self.data, self.data.data),
-                    self._loadRecordsToFilters(self.data, self.data.data)
-                );
-            });
-        });
-    },
-
-
-
-    /**
-     * @private
-     * @param {any} element
-     * @param {any} events
-     * @returns {Deferred}
-     */
-    _loadColors: function (element, events) {
-        if (this.fieldColor) {
-            var fieldName = this.fieldColor;
-            _.each(events, function (event) {
-                var value = event.record[fieldName];
-                event.color_index = _.isArray(value) ? value[0] : value;
+        },
+        /**
+         * @private
+         * @returns {Deferred}
+         */
+        _loadCalendar: function () {
+            var self = this;
+            this.data.fullWidth =
+                this.call("local_storage", "getItem", "calendar_fullWidth") === true;
+            this.data.fc_options = this._getFullCalendarOptions();
+            this._getResources();
+
+            var defs = _.map(this.data.filters, this._loadFilter.bind(this));
+
+            return $.when.apply($, defs).then(function () {
+                return self
+                    ._rpc({
+                        model: self.modelName,
+                        method: "search_read",
+                        context: self.data.context,
+                        fields: self.fieldNames,
+                        domain: self.data.domain
+                            .concat(self._getRangeDomain())
+                            .concat(self._getFilterDomain()),
+                    })
+                    .then(function (events) {
+                        self._parseServerData(events);
+                        self.data.data = _.map(
+                            events,
+                            self._recordToCalendarEvent.bind(self)
+                        );
+                        return $.when(
+                            self._loadColors(self.data, self.data.data),
+                            self._loadRecordsToFilters(self.data, self.data.data)
+                        );
+                    });
             });
-            this.model_color = this.fields[fieldName].relation || element.model;
-        }
-        return $.Deferred().resolve();
-    },
-    /**
-     * @private
-     * @param {any} filter
-     * @returns {Deferred}
-     */
-    _loadFilter: function (filter) {
-        if (!filter.write_model) {
-            return;
-        }
-
-        var field = this.fields[filter.fieldName];
-        return this._rpc({
+        },
+
+        /**
+         * @private
+         * @param {any} element
+         * @param {any} events
+         * @returns {Deferred}
+         */
+        _loadColors: function (element, events) {
+            if (this.fieldColor) {
+                var fieldName = this.fieldColor;
+                _.each(events, function (event) {
+                    var value = event.record[fieldName];
+                    event.color_index = _.isArray(value) ? value[0] : value;
+                });
+                this.model_color = this.fields[fieldName].relation || element.model;
+            }
+            return $.Deferred().resolve();
+        },
+        /**
+         * @private
+         * @param {any} filter
+         * @returns {Deferred}
+         */
+        _loadFilter: function (filter) {
+            if (!filter.write_model) {
+                return;
+            }
+
+            var field = this.fields[filter.fieldName];
+            return this._rpc({
                 model: filter.write_model,
-                method: 'search_read',
+                method: "search_read",
                 domain: [["user_id", "=", session.uid]],
                 fields: [filter.write_field],
-            })
-            .then(function (res) {
+            }).then(function (res) {
                 var records = _.map(res, function (record) {
                     var _value = record[filter.write_field];
                     var value = _.isArray(_value) ? _value[0] : _value;
-                    var f = _.find(filter.filters, function (f) {return f.value === value;});
-                    var formater = fieldUtils.format[_.contains(['many2many', 'one2many'], field.type) ? 'many2one' : field.type];
+                    var f = _.find(filter.filters, function (f) {
+                        return f.value === value;
+                    });
+                    var formater =
+                        fieldUtils.format[
+                            _.contains(["many2many", "one2many"], field.type)
+                                ? "many2one"
+                                : field.type
+                        ];
                     return {
-                        'id': record.id,
-                        'value': value,
-                        'label': formater(_value, field),
-                        'active': !f || f.active,
+                        id: record.id,
+                        value: value,
+                        label: formater(_value, field),
+                        active: !f || f.active,
                     };
                 });
-                records.sort(function (f1,f2) {
+                records.sort(function (f1, f2) {
                     return _.string.naturalCmp(f2.label, f1.label);
                 });
 
-                // add my profile
-                if (field.relation === 'res.partner' || field.relation === 'res.users') {
-                    var value = field.relation === 'res.partner' ? session.partner_id : session.uid;
+                // Add my profile
+                if (
+                    field.relation === "res.partner" ||
+                    field.relation === "res.users"
+                ) {
+                    var value =
+                        field.relation === "res.partner"
+                            ? session.partner_id
+                            : session.uid;
                     var me = _.find(records, function (record) {
                         return record.value === value;
                     });
                     if (me) {
                         records.splice(records.indexOf(me), 1);
                     } else {
-                        var f = _.find(filter.filters, function (f) {return f.value === value;});
+                        var f = _.find(filter.filters, function (f) {
+                            return f.value === value;
+                        });
                         me = {
-                            'value': value,
-                            'label': session.name + _t(" [Me]"),
-                            'active': !f || f.active,
+                            value: value,
+                            label: session.name + _t(" [Me]"),
+                            active: !f || f.active,
                         };
                     }
                     records.unshift(me);
                 }
-                // add all selection
+                // Add all selection
                 records.push({
-                    'value': 'all',
-                    'label': field.relation === 'res.partner' || field.relation === 'res.users' ? _t("Everybody's calendars") : _t("Everything"),
-                    'active': filter.all,
+                    value: "all",
+                    label:
+                        field.relation === "res.partner" ||
+                        field.relation === "res.users"
+                            ? _t("Everybody's calendars")
+                            : _t("Everything"),
+                    active: filter.all,
                 });
 
                 filter.filters = records;
             });
-    },
-    /**
-     * @private
-     * @param {any} element
-     * @param {any} events
-     * @returns {Deferred}
-     */
-    _loadRecordsToFilters: function (element, events) {
-        var self = this;
-        var new_filters = {};
-        var to_read = {};
-
-        _.each(this.data.filters, function (filter, fieldName) {
-            var field = self.fields[fieldName];
-
-            new_filters[fieldName] = filter;
-            if (filter.write_model) {
-                if (field.relation === self.model_color) {
-                    _.each(filter.filters, function (f) {
-                        f.color_index = f.value;
-                    });
+        },
+        /**
+         * @private
+         * @param {any} element
+         * @param {any} events
+         * @returns {Deferred}
+         */
+        _loadRecordsToFilters: function (element, events) {
+            var self = this;
+            var new_filters = {};
+            var to_read = {};
+
+            _.each(this.data.filters, function (filter, fieldName) {
+                var field = self.fields[fieldName];
+
+                new_filters[fieldName] = filter;
+                if (filter.write_model) {
+                    if (field.relation === self.model_color) {
+                        _.each(filter.filters, function (f) {
+                            f.color_index = f.value;
+                        });
+                    }
+                    return;
                 }
-                return;
-            }
 
-            _.each(filter.filters, function (filter) {
-                filter.display = !filter.active;
-            });
+                _.each(filter.filters, function (filter) {
+                    filter.display = !filter.active;
+                });
 
-            var fs = [];
-            var undefined_fs = [];
-            _.each(events, function (event) {
-                var data =  event.record[fieldName];
-                if (!_.contains(['many2many', 'one2many'], field.type)) {
-                    data = [data];
-                } else {
-                    to_read[field.relation] = (to_read[field.relation] || []).concat(data);
-                }
-                _.each(data, function (_value) {
-                    var value = _.isArray(_value) ? _value[0] : _value;
-                    var f = {
-                        'color_index': self.model_color === (field.relation || element.model) ? value : false,
-                        'value': value,
-                        'label': fieldUtils.format[field.type](_value, field) || _t("Undefined"),
-                        'avatar_model': field.relation || element.model,
-                    };
-                    // if field used as color does not have value then push filter in undefined_fs,
-                    // such filters should come last in filter list with Undefined string, later merge it with fs
-                    value ? fs.push(f) : undefined_fs.push(f);
+                var fs = [];
+                var undefined_fs = [];
+                _.each(events, function (event) {
+                    var data = event.record[fieldName];
+                    if (!_.contains(["many2many", "one2many"], field.type)) {
+                        data = [data];
+                    } else {
+                        to_read[field.relation] = (
+                            to_read[field.relation] || []
+                        ).concat(data);
+                    }
+                    _.each(data, function (_value) {
+                        var value = _.isArray(_value) ? _value[0] : _value;
+                        var f = {
+                            color_index:
+                                self.model_color === (field.relation || element.model)
+                                    ? value
+                                    : false,
+                            value: value,
+                            label:
+                                fieldUtils.format[field.type](_value, field) ||
+                                _t("Undefined"),
+                            avatar_model: field.relation || element.model,
+                        };
+                        // If field used as color does not have value then push filter in undefined_fs,
+                        // such filters should come last in filter list with Undefined string, later merge it with fs
+                        value ? fs.push(f) : undefined_fs.push(f);
+                    });
+                });
+                _.each(_.union(fs, undefined_fs), function (f) {
+                    var f1 = _.findWhere(filter.filters, f);
+                    if (f1) {
+                        f1.display = true;
+                    } else {
+                        f.display = f.active = true;
+                        filter.filters.push(f);
+                    }
                 });
             });
-            _.each(_.union(fs, undefined_fs), function (f) {
-                var f1 = _.findWhere(filter.filters, f);
-                if (f1) {
-                    f1.display = true;
-                } else {
-                    f.display = f.active = true;
-                    filter.filters.push(f);
-                }
+
+            var defs = [];
+            _.each(to_read, function (ids, model) {
+                defs.push(
+                    self
+                        ._rpc({
+                            model: model,
+                            method: "name_get",
+                            args: [_.uniq(ids)],
+                        })
+                        .then(function (res) {
+                            to_read[model] = _.object(res);
+                        })
+                );
             });
-        });
-
-        var defs = [];
-        _.each(to_read, function (ids, model) {
-            defs.push(self._rpc({
-                    model: model,
-                    method: 'name_get',
-                    args: [_.uniq(ids)],
-                })
-                .then(function (res) {
-                    to_read[model] = _.object(res);
-                }));
-        });
-        return $.when.apply($, defs).then(function () {
-            _.each(self.data.filters, function (filter) {
-                if (filter.write_model) {
-                    return;
-                }
-                if (filter.filters.length && (filter.filters[0].avatar_model in to_read)) {
-                    _.each(filter.filters, function (f) {
-                        f.label = to_read[f.avatar_model][f.value];
-                    });
-                }
+            return $.when.apply($, defs).then(function () {
+                _.each(self.data.filters, function (filter) {
+                    if (filter.write_model) {
+                        return;
+                    }
+                    if (
+                        filter.filters.length &&
+                        filter.filters[0].avatar_model in to_read
+                    ) {
+                        _.each(filter.filters, function (f) {
+                            f.label = to_read[f.avatar_model][f.value];
+                        });
+                    }
+                });
             });
-        });
-    },
-    /**
-     * parse the server values to javascript framwork
-     *
-     * @private
-     * @param {Object} data the server data to parse
-     */
-    _parseServerData: function (data) {
-        var self = this;
-        _.each(data, function(event) {
-            _.each(self.fieldNames, function (fieldName) {
-                event[fieldName] = self._parseServerValue(self.fields[fieldName], event[fieldName]);
+        },
+        /**
+         * Parse the server values to javascript framwork
+         *
+         * @private
+         * @param {Object} data the server data to parse
+         */
+        _parseServerData: function (data) {
+            var self = this;
+            _.each(data, function (event) {
+                _.each(self.fieldNames, function (fieldName) {
+                    event[fieldName] = self._parseServerValue(
+                        self.fields[fieldName],
+                        event[fieldName]
+                    );
+                });
             });
-        });
-    },
-    /**
-     * Transform OpenERP event object to fullcalendar event object
-     *
-     * @private
-     * @param {Object} evt
-     */
-    _recordToCalendarEvent: function (evt) {
-        var date_start;
-        var date_stop;
-        var date_delay = evt[this.mapping.date_delay] || 1.0,
-            all_day = this.fields[this.mapping.date_start].type === 'date' ||
-                this.mapping.all_day && evt[this.mapping.all_day] || false,
-            the_title = '',
-            attendees = [];
-
-        if (!all_day) {
-            date_start = evt[this.mapping.date_start].clone();
-            date_stop = this.mapping.date_stop ? evt[this.mapping.date_stop].clone() : null;
-        } else {
-            date_start = evt[this.mapping.date_start].clone().startOf('day');
-            date_stop = this.mapping.date_stop ? evt[this.mapping.date_stop].clone().startOf('day') : null;
-        }
-
-        if (!date_stop && date_delay) {
-            date_stop = date_start.clone().add(date_delay,'hours');
-        }
-
-        if (!all_day) {
-            date_start.add(this.getSession().getTZOffset(date_start), 'minutes');
-            date_stop.add(this.getSession().getTZOffset(date_stop), 'minutes');
-        }
-
-
-        if (this.mapping.all_day && evt[this.mapping.all_day]) {
-            date_stop.add(1, 'days');
-        }
-        var r = {
-            'record': evt,
-            'start': date_start.format(),
-            'end': date_stop.format(),
-            'r_start': date_start,
-            'r_end': date_stop,
-            'title': the_title,
-            'allDay': all_day,
-            'id': evt.id,
-            'resourceIds': evt[this.resourceField],
-            'attendees': attendees,
-        };
-
-        if (this.mapping.all_day && evt[this.mapping.all_day]) {
-            r.start = date_start.format('YYYY-MM-DD');
-            r.end = date_stop.format('YYYY-MM-DD');
-        } else if (this.data.scale === 'month' && this.fields[this.mapping.date_start].type !== 'date') {
-            // In month, FullCalendar gives the end day as the
-            // next day at midnight (instead of 23h59).
-            date_stop.add(1, 'days');
-
-            // allow to resize in month mode
-            r.reset_allday = r.allDay;
-            r.allDay = true;
-            r.start = date_start.format('YYYY-MM-DD');
-            r.end = date_stop.startOf('day').format('YYYY-MM-DD');
-        }
-
-        return r;
-    },
-});
+        },
+        /**
+         * Transform OpenERP event object to fullcalendar event object
+         *
+         * @private
+         * @param {Object} evt
+         */
+        _recordToCalendarEvent: function (evt) {
+            var date_start;
+            var date_stop;
+            var date_delay = evt[this.mapping.date_delay] || 1.0,
+                all_day =
+                    this.fields[this.mapping.date_start].type === "date" ||
+                    (this.mapping.all_day && evt[this.mapping.all_day]) ||
+                    false,
+                the_title = "",
+                attendees = [];
+
+            if (!all_day) {
+                date_start = evt[this.mapping.date_start].clone();
+                date_stop = this.mapping.date_stop
+                    ? evt[this.mapping.date_stop].clone()
+                    : null;
+            } else {
+                date_start = evt[this.mapping.date_start].clone().startOf("day");
+                date_stop = this.mapping.date_stop
+                    ? evt[this.mapping.date_stop].clone().startOf("day")
+                    : null;
+            }
+
+            if (!date_stop && date_delay) {
+                date_stop = date_start.clone().add(date_delay, "hours");
+            }
+
+            if (!all_day) {
+                date_start.add(this.getSession().getTZOffset(date_start), "minutes");
+                date_stop.add(this.getSession().getTZOffset(date_stop), "minutes");
+            }
+
+            if (this.mapping.all_day && evt[this.mapping.all_day]) {
+                date_stop.add(1, "days");
+            }
+            var r = {
+                record: evt,
+                start: date_start.format(),
+                end: date_stop.format(),
+                r_start: date_start,
+                r_end: date_stop,
+                title: the_title,
+                allDay: all_day,
+                id: evt.id,
+                resourceIds: evt[this.resourceField],
+                attendees: attendees,
+            };
+
+            if (this.mapping.all_day && evt[this.mapping.all_day]) {
+                r.start = date_start.format("YYYY-MM-DD");
+                r.end = date_stop.format("YYYY-MM-DD");
+            } else if (
+                this.data.scale === "month" &&
+                this.fields[this.mapping.date_start].type !== "date"
+            ) {
+                // In month, FullCalendar gives the end day as the
+                // next day at midnight (instead of 23h59).
+                date_stop.add(1, "days");
+
+                // Allow to resize in month mode
+                r.reset_allday = r.allDay;
+                r.allDay = true;
+                r.start = date_start.format("YYYY-MM-DD");
+                r.end = date_stop.startOf("day").format("YYYY-MM-DD");
+            }
 
+            return r;
+        },
+    });
 });
diff --git a/static/src/js/resource_renderer.js b/static/src/js/resource_renderer.js
index 8a7e744..9c2ff63 100644
--- a/static/src/js/resource_renderer.js
+++ b/static/src/js/resource_renderer.js
@@ -1,541 +1,585 @@
-odoo.define('cgscop_fullcalendar.ResourceRenderer', function (require) {
-"use strict";
+odoo.define("cgscop_fullcalendar.ResourceRenderer", function (require) {
+    "use strict";
 
-var AbstractRenderer = require('web.AbstractRenderer');
-var config = require('web.config');
-var core = require('web.core');
-var Dialog = require('web.Dialog');
-var field_utils = require('web.field_utils');
-var FieldManagerMixin = require('web.FieldManagerMixin');
-var QWeb = require('web.QWeb');
-var relational_fields = require('web.relational_fields');
-var session = require('web.session');
-var utils = require('web.utils');
-var Widget = require('web.Widget');
+    var AbstractRenderer = require("web.AbstractRenderer");
+    var config = require("web.config");
+    var core = require("web.core");
+    var Dialog = require("web.Dialog");
+    var field_utils = require("web.field_utils");
+    var FieldManagerMixin = require("web.FieldManagerMixin");
+    var QWeb = require("web.QWeb");
+    var relational_fields = require("web.relational_fields");
+    var session = require("web.session");
+    var utils = require("web.utils");
+    var Widget = require("web.Widget");
 
-var _t = core._t;
-var qweb = core.qweb;
+    var _t = core._t;
+    var qweb = core.qweb;
 
-var scales = {
-    day: 'resourceTimeGridDay',
-};
+    var scales = {
+        day: "resourceTimeGridDay",
+    };
 
-var SidebarFilterM2O = relational_fields.FieldMany2One.extend({
-    _getSearchBlacklist: function () {
-        return this._super.apply(this, arguments).concat(this.filter_ids || []);
-    },
-});
+    var SidebarFilterM2O = relational_fields.FieldMany2One.extend({
+        _getSearchBlacklist: function () {
+            return this._super.apply(this, arguments).concat(this.filter_ids || []);
+        },
+    });
 
-var SidebarFilter = Widget.extend(FieldManagerMixin, {
-    template: 'ResourceView.sidebar.filter',
-    custom_events: _.extend({}, FieldManagerMixin.custom_events, {
-        field_changed: '_onFieldChanged',
-    }),
-    /**
-     * @constructor
-     * @param {Widget} parent
-     * @param {Object} options
-     * @param {string} options.fieldName
-     * @param {Object[]} options.filters A filter is an object with the
-     *   following keys: id, value, label, active, avatar_model, color,
-     *   can_be_removed
-     * @param {Object} [options.favorite] this is an object with the following
-     *   keys: fieldName, model, fieldModel
-     */
-    init: function (parent, options) {
-        this._super.apply(this, arguments);
-        FieldManagerMixin.init.call(this);
+    var SidebarFilter = Widget.extend(FieldManagerMixin, {
+        template: "ResourceView.sidebar.filter",
+        custom_events: _.extend({}, FieldManagerMixin.custom_events, {
+            field_changed: "_onFieldChanged",
+        }),
+        /**
+         * @class
+         * @param {Widget} parent
+         * @param {Object} options
+         * @param {String} options.fieldName
+         * @param {Object[]} options.filters A filter is an object with the
+         *   following keys: id, value, label, active, avatar_model, color,
+         *   can_be_removed
+         * @param {Object} [options.favorite] this is an object with the following
+         *   keys: fieldName, model, fieldModel
+         */
+        init: function (parent, options) {
+            this._super.apply(this, arguments);
+            FieldManagerMixin.init.call(this);
 
-        this.title = options.title;
-        this.fields = options.fields;
-        this.fieldName = options.fieldName;
-        this.write_model = options.write_model;
-        this.write_field = options.write_field;
-        this.avatar_field = options.avatar_field;
-        this.avatar_model = options.avatar_model;
-        this.filters = options.filters;
-        this.label = options.label;
-        this.getColor = options.getColor;
-    },
-    /**
-     * @override
-     */
-    willStart: function () {
-        var self = this;
-        var defs = [this._super.apply(this, arguments)];
-        if (this.write_model || this.write_field) {
-            var def = this.model.makeRecord(this.write_model, [{
-                name: this.write_field,
-                relation: this.fields[this.fieldName].relation,
-                type: 'many2one',
-                domain: self.fields[self.fieldName].domain,
-            }]).then(function (recordID) {
-                self.many2one = new SidebarFilterM2O(self,
-                    self.write_field,
-                    self.model.get(recordID),
-                    {
-                        mode: 'edit',
-                        attrs: {
-                            placeholder: _.str.sprintf(_t("Add %s"), self.title),
-                            can_create: false
+            this.title = options.title;
+            this.fields = options.fields;
+            this.fieldName = options.fieldName;
+            this.write_model = options.write_model;
+            this.write_field = options.write_field;
+            this.avatar_field = options.avatar_field;
+            this.avatar_model = options.avatar_model;
+            this.filters = options.filters;
+            this.label = options.label;
+            this.getColor = options.getColor;
+        },
+        /**
+         * @override
+         */
+        willStart: function () {
+            var self = this;
+            var defs = [this._super.apply(this, arguments)];
+            if (this.write_model || this.write_field) {
+                var def = this.model
+                    .makeRecord(this.write_model, [
+                        {
+                            name: this.write_field,
+                            relation: this.fields[this.fieldName].relation,
+                            type: "many2one",
+                            domain: self.fields[self.fieldName].domain,
                         },
-                        domain: self.fields[self.fieldName].domain,
-                        readonly: true,
-                        invisible: true,
+                    ])
+                    .then(function (recordID) {
+                        self.many2one = new SidebarFilterM2O(
+                            self,
+                            self.write_field,
+                            self.model.get(recordID),
+                            {
+                                mode: "edit",
+                                attrs: {
+                                    placeholder: _.str.sprintf(
+                                        _t("Add %s"),
+                                        self.title
+                                    ),
+                                    can_create: false,
+                                },
+                                domain: self.fields[self.fieldName].domain,
+                                readonly: true,
+                                invisible: true,
+                            }
+                        );
                     });
-            });
-            defs.push(def);
-        }
-        return $.when.apply($, defs);
-
-    },
-    /**
-     * @override
-     */
-    start: function () {
-        this._super();
-        if (this.many2one) {
-            this.many2one.appendTo(this.$el);
-            this.many2one.filter_ids = _.without(_.pluck(this.filters, 'value'), 'all');
-        }
-        this.$el.on('click', '.o_remove', this._onFilterRemove.bind(this));
-        this.$el.on('click', '.custom-checkbox input', this._onFilterActive.bind(this));
-    },
+                defs.push(def);
+            }
+            return $.when.apply($, defs);
+        },
+        /**
+         * @override
+         */
+        start: function () {
+            this._super();
+            if (this.many2one) {
+                this.many2one.appendTo(this.$el);
+                this.many2one.filter_ids = _.without(
+                    _.pluck(this.filters, "value"),
+                    "all"
+                );
+            }
+            this.$el.on("click", ".o_remove", this._onFilterRemove.bind(this));
+            this.$el.on(
+                "click",
+                ".custom-checkbox input",
+                this._onFilterActive.bind(this)
+            );
+        },
 
-    //--------------------------------------------------------------------------
-    // Handlers
-    //--------------------------------------------------------------------------
+        // --------------------------------------------------------------------------
+        // Handlers
+        // --------------------------------------------------------------------------
 
-    /**
-     * @private
-     * @param {OdooEvent} event
-     */
-    _onFieldChanged: function (event) {
-        var self = this;
-        event.stopPropagation();
-        var createValues = {'user_id': session.uid};
-        var value = event.data.changes[this.write_field].id;
-        createValues[this.write_field] = value;
-        this._rpc({
+        /**
+         * @private
+         * @param {OdooEvent} event
+         */
+        _onFieldChanged: function (event) {
+            var self = this;
+            event.stopPropagation();
+            var createValues = {user_id: session.uid};
+            var value = event.data.changes[this.write_field].id;
+            createValues[this.write_field] = value;
+            this._rpc({
                 model: this.write_model,
-                method: 'create',
+                method: "create",
                 args: [createValues],
-            })
-            .then(function () {
-                self.trigger_up('changeFilter', {
-                    'fieldName': self.fieldName,
-                    'value': value,
-                    'active': true,
+            }).then(function () {
+                self.trigger_up("changeFilter", {
+                    fieldName: self.fieldName,
+                    value: value,
+                    active: true,
                 });
             });
-    },
-    /**
-     * @private
-     * @param {MouseEvent} e
-     */
-    _onFilterActive: function (e) {
-        var $input = $(e.currentTarget);
-        this.trigger_up('changeFilter', {
-            'fieldName': this.fieldName,
-            'value': $input.closest('.o_calendar_filter_item').data('value'),
-            'active': $input.prop('checked'),
-        });
-    },
-    /**
-     * @private
-     * @param {MouseEvent} e
-     */
-    _onFilterRemove: function (e) {
-        var self = this;
-        var $filter = $(e.currentTarget).closest('.o_calendar_filter_item');
-        Dialog.confirm(this, _t("Do you really want to delete this filter from favorites ?"), {
-            confirm_callback: function () {
-                self._rpc({
-                        model: self.write_model,
-                        method: 'unlink',
-                        args: [[$filter.data('id')]],
-                    })
-                    .then(function () {
-                        self.trigger_up('changeFilter', {
-                            'fieldName': self.fieldName,
-                            'id': $filter.data('id'),
-                            'active': false,
-                            'value': $filter.data('value'),
+        },
+        /**
+         * @private
+         * @param {MouseEvent} e
+         */
+        _onFilterActive: function (e) {
+            var $input = $(e.currentTarget);
+            this.trigger_up("changeFilter", {
+                fieldName: this.fieldName,
+                value: $input.closest(".o_calendar_filter_item").data("value"),
+                active: $input.prop("checked"),
+            });
+        },
+        /**
+         * @private
+         * @param {MouseEvent} e
+         */
+        _onFilterRemove: function (e) {
+            var self = this;
+            var $filter = $(e.currentTarget).closest(".o_calendar_filter_item");
+            Dialog.confirm(
+                this,
+                _t("Do you really want to delete this filter from favorites ?"),
+                {
+                    confirm_callback: function () {
+                        self._rpc({
+                            model: self.write_model,
+                            method: "unlink",
+                            args: [[$filter.data("id")]],
+                        }).then(function () {
+                            self.trigger_up("changeFilter", {
+                                fieldName: self.fieldName,
+                                id: $filter.data("id"),
+                                active: false,
+                                value: $filter.data("value"),
+                            });
                         });
-                    });
-            },
-        });
-    },
-});
+                    },
+                }
+            );
+        },
+    });
 
-return AbstractRenderer.extend({
-    template: "ResourceView",
-    events: _.extend({}, AbstractRenderer.prototype.events, {
-        'click .o_calendar_sidebar_toggler': '_onToggleSidebar',
-    }),
+    return AbstractRenderer.extend({
+        template: "ResourceView",
+        events: _.extend({}, AbstractRenderer.prototype.events, {
+            "click .o_calendar_sidebar_toggler": "_onToggleSidebar",
+        }),
 
-    /**
-     * @constructor
-     * @param {Widget} parent
-     * @param {Object} state
-     * @param {Object} params
-     */
-    init: function (parent, state, params) {
-        this._super.apply(this, arguments);
-        this.displayFields = params.displayFields;
-        this.model = params.model;
-        this.filters = [];
-        this.color_map = {};
+        /**
+         * @class
+         * @param {Widget} parent
+         * @param {Object} state
+         * @param {Object} params
+         */
+        init: function (parent, state, params) {
+            this._super.apply(this, arguments);
+            this.displayFields = params.displayFields;
+            this.model = params.model;
+            this.filters = [];
+            this.color_map = {};
 
-        if (params.eventTemplate) {
-            this.qweb = new QWeb(session.debug, {_s: session.origin});
-            this.qweb.add_template(utils.json_node_to_xml(params.eventTemplate));
-        }
-    },
-    /**
-     * @override
-     * @returns {Deferred}
-     */
-    start: function () {
-        this._initSidebar();
-        this._initCalendar();
-        if (config.device.isMobile) {
-            this._bindSwipe();
-        }
-        return this._super();
-    },
-    /**
-     * @override
-     */
-    destroy: function () {
-        if (this.calendar) {
-            this.calendar.destroy();
-        }
-        if (this.$small_calendar) {
-            this.$small_calendar.datepicker('destroy');
-            $('#ui-datepicker-div:empty').remove();
-        }
-        this._super.apply(this, arguments);
-    },
+            if (params.eventTemplate) {
+                this.qweb = new QWeb(session.debug, {_s: session.origin});
+                this.qweb.add_template(utils.json_node_to_xml(params.eventTemplate));
+            }
+        },
+        /**
+         * @override
+         * @returns {Deferred}
+         */
+        start: function () {
+            this._initSidebar();
+            this._initCalendar();
+            if (config.device.isMobile) {
+                this._bindSwipe();
+            }
+            return this._super();
+        },
+        /**
+         * @override
+         */
+        destroy: function () {
+            if (this.calendar) {
+                this.calendar.destroy();
+            }
+            if (this.$small_calendar) {
+                this.$small_calendar.datepicker("destroy");
+                $("#ui-datepicker-div:empty").remove();
+            }
+            this._super.apply(this, arguments);
+        },
 
-    //--------------------------------------------------------------------------
-    // Public
-    //--------------------------------------------------------------------------
+        // --------------------------------------------------------------------------
+        // Public
+        // --------------------------------------------------------------------------
 
-    /**
-     * Note: this is not dead code, it is called by two template
-     *
-     * @param {any} key
-     * @returns {integer}
-     */
-    getColor: function (key) {
-        if (!key) {
-            return false;
-        }
-        if (this.color_map[key]) {
-            return this.color_map[key];
-        }
-        // check if the key is a css color
-        if (typeof key === 'string' && key.match(/^((#[A-F0-9]{3})|(#[A-F0-9]{6})|((hsl|rgb)a?\(\s*(?:(\s*\d{1,3}%?\s*),?){3}(\s*,[0-9.]{1,4})?\))|)$/i)) {
-            return this.color_map[key] = key;
-        }
-        var index = (((_.keys(this.color_map).length + 1) * 5) % 24) + 1;
-        this.color_map[key] = index;
-        return index;
-    },
-    /**
-     * @override
-     */
-    getLocalState: function () {
-        var $fcScroller = this.$calendar.find('.fc-scroller');
-        return {
-            scrollPosition: $fcScroller.scrollTop(),
-        };
-    },
-    /**
-     * @override
-     */
-    setLocalState: function (localState) {
-        if (localState.scrollPosition) {
-            var $fcScroller = this.$calendar.find('.fc-scroller');
-            $fcScroller.scrollTop(localState.scrollPosition);
-        }
-    },
+        /**
+         * Note: this is not dead code, it is called by two template
+         *
+         * @param {any} key
+         * @returns {integer}
+         */
+        getColor: function (key) {
+            if (!key) {
+                return false;
+            }
+            if (this.color_map[key]) {
+                return this.color_map[key];
+            }
+            // Check if the key is a css color
+            if (
+                typeof key === "string" &&
+                key.match(
+                    /^((#[A-F0-9]{3})|(#[A-F0-9]{6})|((hsl|rgb)a?\(\s*(?:(\s*\d{1,3}%?\s*),?){3}(\s*,[0-9.]{1,4})?\))|)$/i
+                )
+            ) {
+                return (this.color_map[key] = key);
+            }
+            var index = (((_.keys(this.color_map).length + 1) * 5) % 24) + 1;
+            this.color_map[key] = index;
+            return index;
+        },
+        /**
+         * @override
+         */
+        getLocalState: function () {
+            var $fcScroller = this.$calendar.find(".fc-scroller");
+            return {
+                scrollPosition: $fcScroller.scrollTop(),
+            };
+        },
+        /**
+         * @override
+         */
+        setLocalState: function (localState) {
+            if (localState.scrollPosition) {
+                var $fcScroller = this.$calendar.find(".fc-scroller");
+                $fcScroller.scrollTop(localState.scrollPosition);
+            }
+        },
 
-    //--------------------------------------------------------------------------
-    // Private
-    //--------------------------------------------------------------------------
+        // --------------------------------------------------------------------------
+        // Private
+        // --------------------------------------------------------------------------
 
-    /**
-     * @private
-     * Bind handlers to enable swipe navigation
-     *
-     * @private
-     */
-    _bindSwipe: function () {
-        var self = this;
-        var touchStartX;
-        var touchEndX;
-        this.$calendar.on('touchstart', function (event) {
-            touchStartX = event.originalEvent.touches[0].pageX;
-        });
-        this.$calendar.on('touchend', function (event) {
-            touchEndX = event.originalEvent.changedTouches[0].pageX;
-            if (touchStartX - touchEndX > 100) {
-                self.trigger_up('next');
-            } else if (touchStartX - touchEndX < -100) {
-                self.trigger_up('prev');
-            }
-        });
-    },
-    /**
-     * @param {any} event
-     * @returns {string} the html for the rendered event
-     */
-    _eventRender: function (event) {
-        var qweb_context = {
-            event: event,
-            fields: this.state.fields,
-            format: this._format.bind(this),
-            isMobile: config.device.isMobile,
-            read_only_mode: this.read_only_mode,
-            record: event.extendedProps.record,
-            user_context: session.user_context,
-            widget: this,
-        };
+        /**
+         * @private
+         * Bind handlers to enable swipe navigation
+         *
+         * @private
+         */
+        _bindSwipe: function () {
+            var self = this;
+            var touchStartX;
+            var touchEndX;
+            this.$calendar.on("touchstart", function (event) {
+                touchStartX = event.originalEvent.touches[0].pageX;
+            });
+            this.$calendar.on("touchend", function (event) {
+                touchEndX = event.originalEvent.changedTouches[0].pageX;
+                if (touchStartX - touchEndX > 100) {
+                    self.trigger_up("next");
+                } else if (touchStartX - touchEndX < -100) {
+                    self.trigger_up("prev");
+                }
+            });
+        },
+        /**
+         * @param {any} event
+         * @returns {String} the html for the rendered event
+         */
+        _eventRender: function (event) {
+            var qweb_context = {
+                event: event,
+                fields: this.state.fields,
+                format: this._format.bind(this),
+                isMobile: config.device.isMobile,
+                read_only_mode: this.read_only_mode,
+                record: event.extendedProps.record,
+                user_context: session.user_context,
+                widget: this,
+            };
 
-        this.qweb_context = qweb_context;
-        if (_.isEmpty(qweb_context.record)) {
-            return '';
-        } else {
+            this.qweb_context = qweb_context;
+            if (_.isEmpty(qweb_context.record)) {
+                return "";
+            }
             return (this.qweb || qweb).render("resource-box", qweb_context);
-        }
-    },
-    /**
-     * @private
-     * @param {any} record
-     * @param {any} fieldName
-     * @returns {string}
-     */
-    _format: function (record, fieldName) {
-        var field = this.state.fields[fieldName];
-        if (field.type === "one2many" || field.type === "many2many") {
-            return field_utils.format[field.type]({data: record[fieldName]}, field);
-        } else {
-            return field_utils.format[field.type](record[fieldName], field, {forceString: true});
-        }
-    },
-    /**
-     * Initialize the main calendar
-     *
-     * @private
-     */
-    _initCalendar: function () {
-        var self = this;
-
-        this.$calendar = this.$(".o_calendar_widget");
+        },
+        /**
+         * @private
+         * @param {any} record
+         * @param {any} fieldName
+         * @returns {String}
+         */
+        _format: function (record, fieldName) {
+            var field = this.state.fields[fieldName];
+            if (field.type === "one2many" || field.type === "many2many") {
+                return field_utils.format[field.type]({data: record[fieldName]}, field);
+            }
+            return field_utils.format[field.type](record[fieldName], field, {
+                forceString: true,
+            });
+        },
+        /**
+         * Initialize the main calendar
+         *
+         * @private
+         */
+        _initCalendar: function () {
+            var self = this;
 
-        //Documentation here : http://arshaw.com/fullcalendar/docs/
-        var fc_options = $.extend({}, this.state.fc_options, {
-            eventDrop: function (info) {
-                self.trigger_up('dropRecord', info.event);
-            },
-            eventResize: function (info) {
-                self.trigger_up('updateRecord', info.event);
-            },
-            eventClick: function (info) {
-                self.trigger_up('openEvent', info.event);
-                self.calendar.unselect();
-            },
-            select: function (selectionInfo) {
-                var data = {'start': selectionInfo.start, 'end': selectionInfo.end};
-                if (self.state.context.default_name) {
-                    data.title = self.state.context.default_name;
-                }
-                self.trigger_up('openCreate', data);
-                self.calendar.unselect();
-            },
-            eventRender: function (info) {
-                var $render = $(self._eventRender(info.event));
-                info.el.querySelector('.fc-content').innerHTML = $render.html();
-                if ($render.attr('class') && $render.attr('class') != ' ') {
-                    info.el.className += " " + $render.attr('class');
-                } else {
-                    info.el.classList.add("o_calendar_color_none");
-                }
-                var display_hour = '';
-                if (!info.event.allDay) {
-                    var start = info.event.extendedProps.r_start || info.event.start;
-                    var end = info.event.extendedProps.r_end || info.event.end;
-                    var timeFormat = _t.database.parameters.time_format.search("%H") != -1 ? 'HH:mm': 'h:mma';
+            this.$calendar = this.$(".o_calendar_widget");
 
-                    display_hour = moment(start).format(timeFormat) + ' - ' + moment(end).format(timeFormat);
-                    if (display_hour === '00:00 - 00:00') {
-                        display_hour = _t('All day');
+            // Documentation here : http://arshaw.com/fullcalendar/docs/
+            var fc_options = $.extend({}, this.state.fc_options, {
+                eventDrop: function (info) {
+                    self.trigger_up("dropRecord", info.event);
+                },
+                eventResize: function (info) {
+                    self.trigger_up("updateRecord", info.event);
+                },
+                eventClick: function (info) {
+                    self.trigger_up("openEvent", info.event);
+                    self.calendar.unselect();
+                },
+                select: function (selectionInfo) {
+                    var data = {start: selectionInfo.start, end: selectionInfo.end};
+                    if (self.state.context.default_name) {
+                        data.title = self.state.context.default_name;
                     }
-                }
-                info.el.querySelector('.fc-content .fc-time').innerHTML = display_hour;
-            },
-            // Dirty hack to ensure a correct first render
-            eventAfterAllRender: function () {
-                $(window).trigger('resize');
-            },
-            viewSkeletonRender: function (info) {
-                var mode = 'resourceTimeGridDay';
-                self.trigger_up('viewUpdated', {
-                    mode: mode,
-                    title: info.view.title,
-                });
-            },
-            datesRender: function (info) {
-                var mode = 'resourceTimeGridDay';
-                self.trigger_up('viewUpdated', {
-                    mode: mode,
-                    title: info.view.title,
-                });
-            },
-            height: 'parent',
-            unselectAuto: true,
-            isRTL: _t.database.parameters.direction === "rtl",
-            resources: this.state.resource,
-        });
-        this.calendar = new FullCalendar.Calendar(this.$calendar[0], fc_options);
-    },
-    /**
-     * Initialize the mini calendar in the sidebar
-     *
-     * @private
-     */
-    _initCalendarMini: function () {
-        var self = this;
-        this.$small_calendar = this.$(".o_calendar_mini");
-        this.$small_calendar.datepicker({
-            'onSelect': function (datum, obj) {
-                self.trigger_up('changeDate', {
-                    date: moment(new Date(+obj.currentYear , +obj.currentMonth, +obj.currentDay))
-                });
-            },
-            'dayNamesMin' : this.state.fc_options.dayNamesShort,
-            'monthNames': this.state.fc_options.monthNamesShort,
-            'firstDay': this.state.fc_options.firstDay,
-        });
-    },
-    /**
-     * Initialize the sidebar
-     *
-     * @private
-     */
-    _initSidebar: function () {
-        this.$sidebar = this.$('.o_calendar_sidebar');
-        this.$sidebar_container = this.$(".o_calendar_sidebar_container");
-        this._initCalendarMini();
-    },
-    /**
-     * Render the calendar view, this is the main entry point.
-     *
-     * @override method from AbstractRenderer
-     * @private
-     * @returns {Deferred}
-     */
-    _render: function () {
-        var $calendar = this.$calendar;
-        var $fc_view = $calendar.find('.fc-view');
-        var scrollPosition = $fc_view.scrollLeft();
-        var scrollTop = this.$calendar.find('.fc-scroller').scrollTop();
+                    self.trigger_up("openCreate", data);
+                    self.calendar.unselect();
+                },
+                eventRender: function (info) {
+                    var $render = $(self._eventRender(info.event));
+                    info.el.querySelector(".fc-content").innerHTML = $render.html();
+                    if ($render.attr("class") && $render.attr("class") != " ") {
+                        info.el.className += " " + $render.attr("class");
+                    } else {
+                        info.el.classList.add("o_calendar_color_none");
+                    }
+                    var display_hour = "";
+                    if (!info.event.allDay) {
+                        var start =
+                            info.event.extendedProps.r_start || info.event.start;
+                        var end = info.event.extendedProps.r_end || info.event.end;
+                        var timeFormat =
+                            _t.database.parameters.time_format.search("%H") != -1
+                                ? "HH:mm"
+                                : "h:mma";
 
-        $fc_view.scrollLeft(0);
-        this.calendar.unselect();
+                        display_hour =
+                            moment(start).format(timeFormat) +
+                            " - " +
+                            moment(end).format(timeFormat);
+                        if (display_hour === "00:00 - 00:00") {
+                            display_hour = _t("All day");
+                        }
+                    }
+                    info.el.querySelector(
+                        ".fc-content .fc-time"
+                    ).innerHTML = display_hour;
+                },
+                // Dirty hack to ensure a correct first render
+                eventAfterAllRender: function () {
+                    $(window).trigger("resize");
+                },
+                viewSkeletonRender: function (info) {
+                    var mode = "resourceTimeGridDay";
+                    self.trigger_up("viewUpdated", {
+                        mode: mode,
+                        title: info.view.title,
+                    });
+                },
+                datesRender: function (info) {
+                    var mode = "resourceTimeGridDay";
+                    self.trigger_up("viewUpdated", {
+                        mode: mode,
+                        title: info.view.title,
+                    });
+                },
+                height: "parent",
+                unselectAuto: true,
+                isRTL: _t.database.parameters.direction === "rtl",
+                resources: this.state.resource,
+            });
+            this.calendar = new FullCalendar.Calendar(this.$calendar[0], fc_options);
+        },
+        /**
+         * Initialize the mini calendar in the sidebar
+         *
+         * @private
+         */
+        _initCalendarMini: function () {
+            var self = this;
+            this.$small_calendar = this.$(".o_calendar_mini");
+            this.$small_calendar.datepicker({
+                onSelect: function (datum, obj) {
+                    self.trigger_up("changeDate", {
+                        date: moment(
+                            new Date(
+                                Number(obj.currentYear),
+                                Number(obj.currentMonth),
+                                Number(obj.currentDay)
+                            )
+                        ),
+                    });
+                },
+                dayNamesMin: this.state.fc_options.dayNamesShort,
+                monthNames: this.state.fc_options.monthNamesShort,
+                firstDay: this.state.fc_options.firstDay,
+            });
+        },
+        /**
+         * Initialize the sidebar
+         *
+         * @private
+         */
+        _initSidebar: function () {
+            this.$sidebar = this.$(".o_calendar_sidebar");
+            this.$sidebar_container = this.$(".o_calendar_sidebar_container");
+            this._initCalendarMini();
+        },
+        /**
+         * Render the calendar view, this is the main entry point.
+         *
+         * @override method from AbstractRenderer
+         * @private
+         * @returns {Deferred}
+         */
+        _render: function () {
+            var $calendar = this.$calendar;
+            var $fc_view = $calendar.find(".fc-view");
+            var scrollPosition = $fc_view.scrollLeft();
+            var scrollTop = this.$calendar.find(".fc-scroller").scrollTop();
 
-        if (this.target_date !== this.state.target_date.toString()) {
-            this.calendar.gotoDate(moment(this.state.target_date).format());
-            this.target_date = this.state.target_date.toString();
-        }
+            $fc_view.scrollLeft(0);
+            this.calendar.unselect();
 
-        this.$small_calendar.datepicker("setDate", this.state.highlight_date.toDate())
-                            .find('.o_selected_range')
-                            .removeClass('o_color o_selected_range');
-        var $a;
-        switch (this.state.scale) {
-            case 'week': $a = this.$small_calendar.find('tr:has(.ui-state-active) a'); break;
-            case 'day': $a = this.$small_calendar.find('a.ui-state-active'); break;
-        }
-        $a.addClass('o_selected_range');
-        setTimeout(function () {
-            $a.not('.ui-state-active').addClass('o_color');
-        });
+            if (this.target_date !== this.state.target_date.toString()) {
+                this.calendar.gotoDate(moment(this.state.target_date).format());
+                this.target_date = this.state.target_date.toString();
+            }
 
-        $fc_view.scrollLeft(scrollPosition);
+            this.$small_calendar
+                .datepicker("setDate", this.state.highlight_date.toDate())
+                .find(".o_selected_range")
+                .removeClass("o_color o_selected_range");
+            var $a;
+            switch (this.state.scale) {
+                case "week":
+                    $a = this.$small_calendar.find("tr:has(.ui-state-active) a");
+                    break;
+                case "day":
+                    $a = this.$small_calendar.find("a.ui-state-active");
+                    break;
+            }
+            $a.addClass("o_selected_range");
+            setTimeout(function () {
+                $a.not(".ui-state-active").addClass("o_color");
+            });
 
-        var fullWidth = this.state.fullWidth;
-        this.$('.o_calendar_sidebar_toggler')
-            .toggleClass('fa-close', !fullWidth)
-            .toggleClass('fa-chevron-left', fullWidth)
-            .attr('title', !fullWidth ? _('Close Sidebar') : _('Open Sidebar'));
-        this.$sidebar_container.toggleClass('o_sidebar_hidden', fullWidth);
-        this.$sidebar.toggleClass('o_hidden', fullWidth);
+            $fc_view.scrollLeft(scrollPosition);
 
-        this._renderFilters();
-        this.$calendar.appendTo('body');
-        if (scrollTop) {
-            this.calendar.render();
-        } else {
-            this.calendar.render();
-        }
-        this._renderEvents();
-        this.$calendar.prependTo(this.$('.o_calendar_view'));
+            var fullWidth = this.state.fullWidth;
+            this.$(".o_calendar_sidebar_toggler")
+                .toggleClass("fa-close", !fullWidth)
+                .toggleClass("fa-chevron-left", fullWidth)
+                .attr("title", !fullWidth ? _("Close Sidebar") : _("Open Sidebar"));
+            this.$sidebar_container.toggleClass("o_sidebar_hidden", fullWidth);
+            this.$sidebar.toggleClass("o_hidden", fullWidth);
 
-        return this._super.apply(this, arguments);
-    },
-    /**
-     * Render all events
-     *
-     * @private
-     */
-    _renderEvents: function () {
-        var eventSources = this.calendar.getEventSources();
-        if (eventSources.length > 0) {
-            eventSources[0].remove();
-        }
-        this.calendar.addEventSource(this.state.data);
+            this._renderFilters();
+            this.$calendar.appendTo("body");
+            if (scrollTop) {
+                this.calendar.render();
+            } else {
+                this.calendar.render();
+            }
+            this._renderEvents();
+            this.$calendar.prependTo(this.$(".o_calendar_view"));
 
-    },
-    /**
-     * Render all filters
-     *
-     * @private
-     */
-    _renderFilters: function () {
-        var self = this;
-        _.each(this.filters || (this.filters = []), function (filter) {
-            filter.destroy();
-        });
-        if (this.state.fullWidth) {
-            return;
-        }
-        _.each(this.state.filters, function (options) {
-            if (!_.find(options.filters, function (f) {return f.display == null || f.display;})) {
+            return this._super.apply(this, arguments);
+        },
+        /**
+         * Render all events
+         *
+         * @private
+         */
+        _renderEvents: function () {
+            var eventSources = this.calendar.getEventSources();
+            if (eventSources.length > 0) {
+                eventSources[0].remove();
+            }
+            this.calendar.addEventSource(this.state.data);
+        },
+        /**
+         * Render all filters
+         *
+         * @private
+         */
+        _renderFilters: function () {
+            var self = this;
+            _.each(this.filters || (this.filters = []), function (filter) {
+                filter.destroy();
+            });
+            if (this.state.fullWidth) {
                 return;
             }
-            options.getColor = self.getColor.bind(self);
-            options.fields = self.state.fields;
-            var filter = new SidebarFilter(self, options);
-            filter.appendTo(self.$sidebar);
-            self.filters.push(filter);
-        });
-    },
-
-    //--------------------------------------------------------------------------
-    // Handlers
-    //--------------------------------------------------------------------------
+            _.each(this.state.filters, function (options) {
+                if (
+                    !_.find(options.filters, function (f) {
+                        return f.display == null || f.display;
+                    })
+                ) {
+                    return;
+                }
+                options.getColor = self.getColor.bind(self);
+                options.fields = self.state.fields;
+                var filter = new SidebarFilter(self, options);
+                filter.appendTo(self.$sidebar);
+                self.filters.push(filter);
+            });
+        },
 
-    /**
-     * Toggle the sidebar
-     *
-     * @private
-     */
-    _onToggleSidebar: function () {
-        this.trigger_up('toggleFullWidth');
-    },
-});
+        // --------------------------------------------------------------------------
+        // Handlers
+        // --------------------------------------------------------------------------
 
+        /**
+         * Toggle the sidebar
+         *
+         * @private
+         */
+        _onToggleSidebar: function () {
+            this.trigger_up("toggleFullWidth");
+        },
+    });
 });
diff --git a/static/src/js/resource_view.js b/static/src/js/resource_view.js
index a1592b8..b3d87c9 100644
--- a/static/src/js/resource_view.js
+++ b/static/src/js/resource_view.js
@@ -1,171 +1,189 @@
-odoo.define('cgscop_fullcalendar.ResourceView', function (require) {
-"use strict";
-
-var AbstractView = require('web.AbstractView');
-var ResourceModel = require('cgscop_fullcalendar.ResourceModel');
-var ResourceController = require('cgscop_fullcalendar.ResourceController');
-var ResourceRenderer = require('cgscop_fullcalendar.ResourceRenderer');
-var core = require('web.core');
-var utils = require('web.utils');
-var view_registry = require('web.view_registry');
-
-var _lt = core._lt;
-
-// gather the fields to get
-var fieldsToGather = [
-    "date_start",
-    "date_delay",
-    "date_stop",
-    "all_day",
-];
-
-var ResourceView = AbstractView.extend({
-    display_name: 'Ressources',
-    icon: 'fa-calendar-check-o',
-    jsLibs: [
-        '/cgscop_fullcalendar/static/lib/fullcalendar/packages/core/main.min.js',
-        '/cgscop_fullcalendar/static/lib/fullcalendar/packages/core/locales/fr.js',
-        '/cgscop_fullcalendar/static/lib/fullcalendar/packages/daygrid/main.min.js',
-        '/cgscop_fullcalendar/static/lib/fullcalendar/packages/timegrid/main.min.js',
-        '/cgscop_fullcalendar/static/lib/fullcalendar/packages/moment/main.min.js',
-        '/cgscop_fullcalendar/static/lib/fullcalendar/packages/interaction/main.min.js',
-        '/cgscop_fullcalendar/static/lib/fullcalendar/premium/resource-common/main.min.js',
-        '/cgscop_fullcalendar/static/lib/fullcalendar/premium/resource-daygrid/main.min.js',
-        '/cgscop_fullcalendar/static/lib/fullcalendar/premium/resource-timegrid/main.min.js',
+odoo.define("cgscop_fullcalendar.ResourceView", function (require) {
+    "use strict";
+
+    var AbstractView = require("web.AbstractView");
+    var ResourceModel = require("cgscop_fullcalendar.ResourceModel");
+    var ResourceController = require("cgscop_fullcalendar.ResourceController");
+    var ResourceRenderer = require("cgscop_fullcalendar.ResourceRenderer");
+    var core = require("web.core");
+    var utils = require("web.utils");
+    var view_registry = require("web.view_registry");
+
+    var _lt = core._lt;
+
+    // Gather the fields to get
+    var fieldsToGather = ["date_start", "date_delay", "date_stop", "all_day"];
+
+    var ResourceView = AbstractView.extend({
+        display_name: "Ressources",
+        icon: "fa-calendar-check-o",
+        jsLibs: [
+            "/cgscop_fullcalendar/static/lib/fullcalendar/packages/core/main.min.js",
+            "/cgscop_fullcalendar/static/lib/fullcalendar/packages/core/locales/fr.js",
+            "/cgscop_fullcalendar/static/lib/fullcalendar/packages/daygrid/main.min.js",
+            "/cgscop_fullcalendar/static/lib/fullcalendar/packages/timegrid/main.min.js",
+            "/cgscop_fullcalendar/static/lib/fullcalendar/packages/moment/main.min.js",
+            "/cgscop_fullcalendar/static/lib/fullcalendar/packages/interaction/main.min.js",
+            "/cgscop_fullcalendar/static/lib/fullcalendar/premium/resource-common/main.min.js",
+            "/cgscop_fullcalendar/static/lib/fullcalendar/premium/resource-daygrid/main.min.js",
+            "/cgscop_fullcalendar/static/lib/fullcalendar/premium/resource-timegrid/main.min.js",
         ],
-    cssLibs: [
-        '/cgscop_fullcalendar/static/lib/fullcalendar/packages/core/main.min.css',
-        '/cgscop_fullcalendar/static/lib/fullcalendar/packages/timegrid/main.min.css',
+        cssLibs: [
+            "/cgscop_fullcalendar/static/lib/fullcalendar/packages/core/main.min.css",
+            "/cgscop_fullcalendar/static/lib/fullcalendar/packages/timegrid/main.min.css",
         ],
-    config: {
-        Model: ResourceModel,
-        Controller: ResourceController,
-        Renderer: ResourceRenderer,
-    },
-    viewType: 'resource',
-    groupable: false,
-
-    /**
-     * @override
-     */
-    init: function (viewInfo, params) {
-        this._super.apply(this, arguments);
-        var arch = this.arch;
-        var fields = this.fields;
-        var attrs = arch.attrs;
-
-        if (!attrs.date_start) {
-            throw new Error(_lt("Calendar view has not defined 'date_start' attribute."));
-        }
-
-        var mapping = {};
-        var fieldNames = fields.display_name ? ['display_name'] : [];
-        var displayFields = {};
-
-        _.each(fieldsToGather, function (field) {
-            if (arch.attrs[field]) {
-                var fieldName = attrs[field];
-                mapping[field] = fieldName;
-                fieldNames.push(fieldName);
+        config: {
+            Model: ResourceModel,
+            Controller: ResourceController,
+            Renderer: ResourceRenderer,
+        },
+        viewType: "resource",
+        groupable: false,
+
+        /**
+         * @override
+         */
+        init: function (viewInfo, params) {
+            this._super.apply(this, arguments);
+            var arch = this.arch;
+            var fields = this.fields;
+            var attrs = arch.attrs;
+
+            if (!attrs.date_start) {
+                throw new Error(
+                    _lt("Calendar view has not defined 'date_start' attribute.")
+                );
             }
-        });
 
-        var filters = {};
+            var mapping = {};
+            var fieldNames = fields.display_name ? ["display_name"] : [];
+            var displayFields = {};
+
+            _.each(fieldsToGather, function (field) {
+                if (arch.attrs[field]) {
+                    var fieldName = attrs[field];
+                    mapping[field] = fieldName;
+                    fieldNames.push(fieldName);
+                }
+            });
+
+            var filters = {};
 
-        var eventLimit = attrs.event_limit !== null && (isNaN(+attrs.event_limit) ? _.str.toBool(attrs.event_limit) : +attrs.event_limit);
+            var eventLimit =
+                attrs.event_limit !== null &&
+                (isNaN(Number(attrs.event_limit))
+                    ? _.str.toBool(attrs.event_limit)
+                    : Number(attrs.event_limit));
 
-        var modelFilters = [];
-        _.each(arch.children, function (child) {
-            if (child.tag !== 'field') return;
-            var fieldName = child.attrs.name;
-            fieldNames.push(fieldName);
-            if (!child.attrs.invisible) {
-                displayFields[fieldName] = child.attrs;
+            var modelFilters = [];
+            _.each(arch.children, function (child) {
+                if (child.tag !== "field") return;
+                var fieldName = child.attrs.name;
+                fieldNames.push(fieldName);
+                if (!child.attrs.invisible) {
+                    displayFields[fieldName] = child.attrs;
 
-                if (params.sidebar === false) return; // if we have not sidebar, (eg: Dashboard), we don't use the filter "coworkers"
+                    if (params.sidebar === false) return; // If we have not sidebar, (eg: Dashboard), we don't use the filter "coworkers"
 
-                if (child.attrs.write_model) {
-                    filters[fieldName] = filters[fieldName] || {
-                        'title': fields[fieldName].string,
-                        'fieldName': fieldName,
-                        'filters': [],
-                    };
+                    if (child.attrs.write_model) {
+                        filters[fieldName] = filters[fieldName] || {
+                            title: fields[fieldName].string,
+                            fieldName: fieldName,
+                            filters: [],
+                        };
 
-                    filters[fieldName].write_model = child.attrs.write_model;
-                    filters[fieldName].write_field = child.attrs.write_field; // can't use a x2many fields
+                        filters[fieldName].write_model = child.attrs.write_model;
+                        filters[fieldName].write_field = child.attrs.write_field; // Can't use a x2many fields
 
-                    modelFilters.push(fields[fieldName].relation);
+                        modelFilters.push(fields[fieldName].relation);
+                    }
                 }
-            }
-        });
-
-        if (attrs.color) {
-            var fieldName = attrs.color;
-            fieldNames.push(fieldName);
-            filters[fieldName] = {
-                'title': fields[fieldName].string,
-                'fieldName': fieldName,
-                'filters': [],
-            };
-            if (fields[fieldName].relation) {
-                if (['res.users', 'res.partner'].indexOf(fields[fieldName].relation) !== -1) {
-                    filters[fieldName].avatar_field = 'image_small';
+            });
+
+            if (attrs.color) {
+                var fieldName = attrs.color;
+                fieldNames.push(fieldName);
+                filters[fieldName] = {
+                    title: fields[fieldName].string,
+                    fieldName: fieldName,
+                    filters: [],
+                };
+                if (fields[fieldName].relation) {
+                    if (
+                        ["res.users", "res.partner"].indexOf(
+                            fields[fieldName].relation
+                        ) !== -1
+                    ) {
+                        filters[fieldName].avatar_field = "image_small";
+                    }
+                    filters[fieldName].avatar_model = fields[fieldName].relation;
                 }
-                filters[fieldName].avatar_model = fields[fieldName].relation;
             }
-        }
-
-        if (_.isEmpty(displayFields)) {
-            displayFields = fields.display_name ? {'display_name': {}} : [];
-        }
-
-        //if quick_add = False, we don't allow quick_add
-        //if quick_add = not specified in view, we use the default widgets.QuickCreate
-        //if quick_add = is NOT False and IS specified in view, we this one for widgets.QuickCreate'
-        this.controllerParams.quickAddPop = (!('quick_add' in attrs) || utils.toBoolElse(attrs.quick_add+'', true));
-        this.controllerParams.disableQuickCreate =  params.disable_quick_create || !this.controllerParams.quickAddPop;
-
-        this.loadParams.resourceField = attrs.resource_field
-
-        // If form_view_id is set, then the calendar view will open a form view
-        // with this id, when it needs to edit or create an event.
-        this.controllerParams.formViewId =
-            attrs.form_view_id ? parseInt(attrs.form_view_id, 10) : false;
-        if (!this.controllerParams.formViewId && params.action) {
-            var formViewDescr = _.find(params.action.views, function (v) {
-                return v.type ===  'form';
-            });
-            if (formViewDescr) {
-                this.controllerParams.formViewId = formViewDescr.viewID;
+
+            if (_.isEmpty(displayFields)) {
+                displayFields = fields.display_name ? {display_name: {}} : [];
             }
-        }
-
-        this.controllerParams.readonlyFormViewId = !attrs.readonly_form_view_id || !utils.toBoolElse(attrs.readonly_form_view_id, true) ? false : attrs.readonly_form_view_id;
-        this.controllerParams.eventOpenPopup = utils.toBoolElse(attrs.event_open_popup || '', false);
-        this.controllerParams.mapping = mapping;
-        this.controllerParams.context = params.context || {};
-        this.controllerParams.displayName = params.action && params.action.name;
-
-        this.rendererParams.displayFields = displayFields;
-        this.rendererParams.eventTemplate = _.findWhere(arch.children, {'tag': 'templates'});
-        this.rendererParams.model = viewInfo.model;
-
-        this.loadParams.fieldNames = _.uniq(fieldNames);
-        this.loadParams.mapping = mapping;
-        this.loadParams.fields = fields;
-        this.loadParams.fieldsInfo = viewInfo.fieldsInfo;
-        this.loadParams.editable = !fields[mapping.date_start].readonly;
-        this.loadParams.creatable = true;
-        this.loadParams.eventLimit = eventLimit;
-        this.loadParams.fieldColor = attrs.color;
-
-        this.loadParams.filters = filters;
-        this.loadParams.mode = (params.context && params.context.default_mode) || attrs.mode;
-        this.loadParams.initialDate = moment(params.initialDate || new Date());
-    },
-});
 
-view_registry.add('resource', ResourceView);
-return ResourceView;
+            // If quick_add = False, we don't allow quick_add
+            // if quick_add = not specified in view, we use the default widgets.QuickCreate
+            // if quick_add = is NOT False and IS specified in view, we this one for widgets.QuickCreate'
+            this.controllerParams.quickAddPop =
+                !("quick_add" in attrs) ||
+                utils.toBoolElse(String(attrs.quick_add), true);
+            this.controllerParams.disableQuickCreate =
+                params.disable_quick_create || !this.controllerParams.quickAddPop;
+
+            this.loadParams.resourceField = attrs.resource_field;
+
+            // If form_view_id is set, then the calendar view will open a form view
+            // with this id, when it needs to edit or create an event.
+            this.controllerParams.formViewId = attrs.form_view_id
+                ? parseInt(attrs.form_view_id, 10)
+                : false;
+            if (!this.controllerParams.formViewId && params.action) {
+                var formViewDescr = _.find(params.action.views, function (v) {
+                    return v.type === "form";
+                });
+                if (formViewDescr) {
+                    this.controllerParams.formViewId = formViewDescr.viewID;
+                }
+            }
 
+            this.controllerParams.readonlyFormViewId =
+                !attrs.readonly_form_view_id ||
+                !utils.toBoolElse(attrs.readonly_form_view_id, true)
+                    ? false
+                    : attrs.readonly_form_view_id;
+            this.controllerParams.eventOpenPopup = utils.toBoolElse(
+                attrs.event_open_popup || "",
+                false
+            );
+            this.controllerParams.mapping = mapping;
+            this.controllerParams.context = params.context || {};
+            this.controllerParams.displayName = params.action && params.action.name;
+
+            this.rendererParams.displayFields = displayFields;
+            this.rendererParams.eventTemplate = _.findWhere(arch.children, {
+                tag: "templates",
+            });
+            this.rendererParams.model = viewInfo.model;
+
+            this.loadParams.fieldNames = _.uniq(fieldNames);
+            this.loadParams.mapping = mapping;
+            this.loadParams.fields = fields;
+            this.loadParams.fieldsInfo = viewInfo.fieldsInfo;
+            this.loadParams.editable = !fields[mapping.date_start].readonly;
+            this.loadParams.creatable = true;
+            this.loadParams.eventLimit = eventLimit;
+            this.loadParams.fieldColor = attrs.color;
+
+            this.loadParams.filters = filters;
+            this.loadParams.mode =
+                (params.context && params.context.default_mode) || attrs.mode;
+            this.loadParams.initialDate = moment(params.initialDate || new Date());
+        },
+    });
+
+    view_registry.add("resource", ResourceView);
+    return ResourceView;
 });
diff --git a/static/src/xml/resource_view.xml b/static/src/xml/resource_view.xml
index 78a4460..c44a2d8 100644
--- a/static/src/xml/resource_view.xml
+++ b/static/src/xml/resource_view.xml
@@ -1,25 +1,34 @@
 <template>
     <div t-name="ResourceView" class="o_calendar_container">
         <div class="o_calendar_view">
-            <div class="o_calendar_buttons" role="toolbar" aria-label="Calendar toolbar"/>
+            <div
+                class="o_calendar_buttons"
+                role="toolbar"
+                aria-label="Calendar toolbar"
+            />
             <div class="o_calendar_widget cgscop_resource" />
         </div>
         <div class="o_calendar_sidebar_container d-none d-md-block">
-            <i class="o_calendar_sidebar_toggler fa"/>
+            <i class="o_calendar_sidebar_toggler fa" />
             <div class="o_calendar_sidebar">
-                <div class="o_calendar_mini"/>
+                <div class="o_calendar_mini" />
             </div>
         </div>
     </div>
 
     <t t-name="resource-box">
-        <t t-set="color" t-value="widget.getColor(event.extendedProps.color_index)"/>
-        <div t-att-style="typeof color === 'string' ? ('background-color:'+color)+';' : ''" t-attf-class="#{record.is_highlighted ? 'o_event_hightlight' : ''} #{typeof color === 'number' ? 'o_calendar_color_'+color : ''}">
-            <div class="fc-time"/>
+        <t t-set="color" t-value="widget.getColor(event.extendedProps.color_index)" />
+        <div
+            t-att-style="typeof color === 'string' ? ('background-color:'+color)+';' : ''"
+            t-attf-class="#{record.is_highlighted ? 'o_event_hightlight' : ''} #{typeof color === 'number' ? 'o_calendar_color_'+color : ''}"
+        >
+            <div class="fc-time" />
             <div class="o_fields">
                 <t t-foreach="widget.displayFields" t-as="name">
-                    <div t-attf-class="o_field_#{name} o_field_type_#{fields[name].type}">
-                            <t t-esc="format(record, name)"/>
+                    <div
+                        t-attf-class="o_field_#{name} o_field_type_#{fields[name].type}"
+                    >
+                            <t t-esc="format(record, name)" />
                     </div>
                 </t>
             </div>
@@ -29,41 +38,85 @@
     <t t-name="ResourceView.buttons">
         <div class="o_calendar_buttons">
             <t t-if="!isMobile">
-                <button class="o_calendar_button_prev btn btn-primary" aria-label="Previous" title="Previous"><span class="fa fa-arrow-left"/></button>
+                <button
+                    class="o_calendar_button_prev btn btn-primary"
+                    aria-label="Previous"
+                    title="Previous"
+                ><span class="fa fa-arrow-left" /></button>
                 <button class="o_calendar_button_today btn btn-primary">Today</button>
-                <button class="o_calendar_button_next btn btn-primary" aria-label="Next" title="Next"><span class="fa fa-arrow-right"/></button>
+                <button
+                    class="o_calendar_button_next btn btn-primary"
+                    aria-label="Next"
+                    title="Next"
+                ><span class="fa fa-arrow-right" /></button>
             </t>
         </div>
     </t>
 
     <t t-name="ResourceView.sidebar.filter">
         <div class="o_calendar_filter">
-            <h3 t-if="widget.title"><t t-esc="widget.title"/></h3>
+            <h3 t-if="widget.title"><t t-esc="widget.title" /></h3>
             <div class="o_calendar_filter_items">
-                <div t-foreach="widget.filters" t-as="filter" t-if="filter.display == null || filter.display" class="o_calendar_filter_item" t-att-data-value="filter.value" t-att-data-id="filter.id">
-                    <t t-set="id_for_label" t-value="_.uniqueId('o_calendar_filter_item_')"/>
+                <div
+                    t-foreach="widget.filters"
+                    t-as="filter"
+                    t-if="filter.display == null || filter.display"
+                    class="o_calendar_filter_item"
+                    t-att-data-value="filter.value"
+                    t-att-data-id="filter.id"
+                >
+                    <t
+                        t-set="id_for_label"
+                        t-value="_.uniqueId('o_calendar_filter_item_')"
+                    />
                     <div class="custom-control custom-control-inline custom-checkbox">
-                        <input type="checkbox"
+                        <input
+                            type="checkbox"
                             t-att-id="id_for_label"
                             name="selection"
                             class="custom-control-input"
-                            t-att-checked="(filter.active ? true : undefined)"/>
-                        <label t-att-for="id_for_label"
-                            class="custom-control-label">
+                            t-att-checked="(filter.active ? true : undefined)"
+                        />
+                        <label t-att-for="id_for_label" class="custom-control-label">
                             <t t-if="filter.value == 'all'">
-                                <span><i class="fa fa-users fa-fw o_cal_avatar" role="img" aria-label="Avatar" title="Avatar"/></span>
+                                <span><i
+                                        class="fa fa-users fa-fw o_cal_avatar"
+                                        role="img"
+                                        aria-label="Avatar"
+                                        title="Avatar"
+                                    /></span>
                             </t>
-                            <t t-if="widget.avatar_field &amp;&amp; (filter.value != 'all') &amp;&amp; (filter.value)">
-                                <img t-attf-src="/web/image/#{widget.avatar_model}/#{filter.value}/#{widget.avatar_field}" class="o_cal_avatar" alt="Avatar"/>
+                            <t
+                                t-if="widget.avatar_field &amp;&amp; (filter.value != 'all') &amp;&amp; (filter.value)"
+                            >
+                                <img
+                                    t-attf-src="/web/image/#{widget.avatar_model}/#{filter.value}/#{widget.avatar_field}"
+                                    class="o_cal_avatar"
+                                    alt="Avatar"
+                                />
                             </t>
-                            <t t-set="color" t-value="widget.getColor(filter.color_index)"/>
-                            <span t-if="typeof color === 'number'" t-attf-class="color_filter o_underline_color_#{widget.getColor(filter.color_index)}"><t t-esc="filter.label"/></span>
-                            <span t-elif="color" t-attf-style="border-bottom: 4px solid #{color};"><t t-esc="filter.label"/></span>
-                            <span t-else=""><t t-esc="filter.label"/></span>
+                            <t
+                                t-set="color"
+                                t-value="widget.getColor(filter.color_index)"
+                            />
+                            <span
+                                t-if="typeof color === 'number'"
+                                t-attf-class="color_filter o_underline_color_#{widget.getColor(filter.color_index)}"
+                            ><t t-esc="filter.label" /></span>
+                            <span
+                                t-elif="color"
+                                t-attf-style="border-bottom: 4px solid #{color};"
+                            ><t t-esc="filter.label" /></span>
+                            <span t-else=""><t t-esc="filter.label" /></span>
                         </label>
                     </div>
                     <t t-if="filter.id">
-                        <span class="o_remove fa fa-times" title="Remove this favorite from the list" role="img" aria-label="Remove this favorite from the list"/>
+                        <span
+                            class="o_remove fa fa-times"
+                            title="Remove this favorite from the list"
+                            role="img"
+                            aria-label="Remove this favorite from the list"
+                        />
                     </t>
                 </div>
             </div>
diff --git a/views/assets.xml b/views/assets.xml
index 6c7adbb..3008872 100644
--- a/views/assets.xml
+++ b/views/assets.xml
@@ -1,37 +1,113 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8" ?>
 <odoo>
 
-    <template id="assets_backend" name="cgscop_fullcalendar assets" inherit_id="web.assets_backend">
+    <template
+        id="assets_backend"
+        name="cgscop_fullcalendar assets"
+        inherit_id="web.assets_backend"
+    >
         <xpath expr="." position="inside">
 
-            <link href="/cgscop_fullcalendar/static/lib/fullcalendar/packages/core/main.min.css" rel="stylesheet" />
-            <link href="/cgscop_fullcalendar/static/lib/fullcalendar/packages/daygrid/main.min.css" rel="stylesheet" />
-            <link href="/cgscop_fullcalendar/static/lib/fullcalendar/packages/list/main.min.css" rel="stylesheet" />
-            <link href="/cgscop_fullcalendar/static/lib/fullcalendar/packages/timegrid/main.min.css" rel="stylesheet" />
-            <link href="/cgscop_fullcalendar/static/lib/fullcalendar/packages/timegrid/main.min.css" rel="stylesheet" />
-            <link href="/cgscop_fullcalendar/static/src/css/cgscop_fullcalendar.css" rel="stylesheet" />
+            <link
+                href="/cgscop_fullcalendar/static/lib/fullcalendar/packages/core/main.min.css"
+                rel="stylesheet"
+            />
+            <link
+                href="/cgscop_fullcalendar/static/lib/fullcalendar/packages/daygrid/main.min.css"
+                rel="stylesheet"
+            />
+            <link
+                href="/cgscop_fullcalendar/static/lib/fullcalendar/packages/list/main.min.css"
+                rel="stylesheet"
+            />
+            <link
+                href="/cgscop_fullcalendar/static/lib/fullcalendar/packages/timegrid/main.min.css"
+                rel="stylesheet"
+            />
+            <link
+                href="/cgscop_fullcalendar/static/lib/fullcalendar/packages/timegrid/main.min.css"
+                rel="stylesheet"
+            />
+            <link
+                href="/cgscop_fullcalendar/static/src/css/cgscop_fullcalendar.css"
+                rel="stylesheet"
+            />
 
             <!-- librairies common -->
-            <script type="text/javascript" src="/cgscop_fullcalendar/static/lib/fullcalendar/packages/core/main.min.js"/>
-            <script type="text/javascript" src="/cgscop_fullcalendar/static/lib/fullcalendar/packages/core/locales/fr.js"/>
-            <script type="text/javascript" src="/cgscop_fullcalendar/static/lib/fullcalendar/packages/daygrid/main.min.js"/>
-            <script type="text/javascript" src="/cgscop_fullcalendar/static/lib/fullcalendar/packages/timegrid/main.min.js"/>
-            <script type="text/javascript" src="/cgscop_fullcalendar/static/lib/fullcalendar/packages/list/main.min.js"/>
-            <script type="text/javascript" src="/cgscop_fullcalendar/static/lib/fullcalendar/packages/interaction/main.min.js"/>
-            <script type="text/javascript" src="/cgscop_fullcalendar/static/lib/fullcalendar/packages/moment/main.min.js"/>
+            <script
+                type="text/javascript"
+                src="/cgscop_fullcalendar/static/lib/fullcalendar/packages/core/main.min.js"
+            />
+            <script
+                type="text/javascript"
+                src="/cgscop_fullcalendar/static/lib/fullcalendar/packages/core/locales/fr.js"
+            />
+            <script
+                type="text/javascript"
+                src="/cgscop_fullcalendar/static/lib/fullcalendar/packages/daygrid/main.min.js"
+            />
+            <script
+                type="text/javascript"
+                src="/cgscop_fullcalendar/static/lib/fullcalendar/packages/timegrid/main.min.js"
+            />
+            <script
+                type="text/javascript"
+                src="/cgscop_fullcalendar/static/lib/fullcalendar/packages/list/main.min.js"
+            />
+            <script
+                type="text/javascript"
+                src="/cgscop_fullcalendar/static/lib/fullcalendar/packages/interaction/main.min.js"
+            />
+            <script
+                type="text/javascript"
+                src="/cgscop_fullcalendar/static/lib/fullcalendar/packages/moment/main.min.js"
+            />
             <!-- librairies premium -->
-            <script type="text/javascript" src="/cgscop_fullcalendar/static/lib/fullcalendar/premium/resource-common/main.min.js"/>
-            <script type="text/javascript" src="/cgscop_fullcalendar/static/lib/fullcalendar/premium/resource-daygrid/main.min.js"/>
-            <script type="text/javascript" src="/cgscop_fullcalendar/static/lib/fullcalendar/premium/resource-timegrid/main.min.js"/>
+            <script
+                type="text/javascript"
+                src="/cgscop_fullcalendar/static/lib/fullcalendar/premium/resource-common/main.min.js"
+            />
+            <script
+                type="text/javascript"
+                src="/cgscop_fullcalendar/static/lib/fullcalendar/premium/resource-daygrid/main.min.js"
+            />
+            <script
+                type="text/javascript"
+                src="/cgscop_fullcalendar/static/lib/fullcalendar/premium/resource-timegrid/main.min.js"
+            />
             <!-- custom js -->
-            <script type="text/javascript" src="/cgscop_fullcalendar/static/src/js/calendar_controller.js"/>
-            <script type="text/javascript" src="/cgscop_fullcalendar/static/src/js/calendar_model.js"/>
-            <script type="text/javascript" src="/cgscop_fullcalendar/static/src/js/calendar_renderer.js"/>
-            <script type="text/javascript" src="/cgscop_fullcalendar/static/src/js/calendar_view.js"/>
-            <script type="text/javascript" src="/cgscop_fullcalendar/static/src/js/resource_controller.js"/>
-            <script type="text/javascript" src="/cgscop_fullcalendar/static/src/js/resource_model.js"/>
-            <script type="text/javascript" src="/cgscop_fullcalendar/static/src/js/resource_renderer.js"/>
-            <script type="text/javascript" src="/cgscop_fullcalendar/static/src/js/resource_view.js"/>
+            <script
+                type="text/javascript"
+                src="/cgscop_fullcalendar/static/src/js/calendar_controller.js"
+            />
+            <script
+                type="text/javascript"
+                src="/cgscop_fullcalendar/static/src/js/calendar_model.js"
+            />
+            <script
+                type="text/javascript"
+                src="/cgscop_fullcalendar/static/src/js/calendar_renderer.js"
+            />
+            <script
+                type="text/javascript"
+                src="/cgscop_fullcalendar/static/src/js/calendar_view.js"
+            />
+            <script
+                type="text/javascript"
+                src="/cgscop_fullcalendar/static/src/js/resource_controller.js"
+            />
+            <script
+                type="text/javascript"
+                src="/cgscop_fullcalendar/static/src/js/resource_model.js"
+            />
+            <script
+                type="text/javascript"
+                src="/cgscop_fullcalendar/static/src/js/resource_renderer.js"
+            />
+            <script
+                type="text/javascript"
+                src="/cgscop_fullcalendar/static/src/js/resource_view.js"
+            />
         </xpath>
     </template>
 
diff --git a/views/res_users.xml b/views/res_users.xml
index fc452da..45e6a3d 100644
--- a/views/res_users.xml
+++ b/views/res_users.xml
@@ -1,17 +1,16 @@
-<?xml version="1.0"?>
+<?xml version="1.0" ?>
 <!-- Copyright 2020 Le Filament
      License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
-
 <odoo>
     <data>
         <record id="res_users_calendar_form" model="ir.ui.view">
             <field name="name">res.users.calendar.form</field>
             <field name="model">res.users</field>
-            <field name="inherit_id" ref="base.view_users_form"/>
+            <field name="inherit_id" ref="base.view_users_form" />
             <field name="arch" type="xml">
                 <xpath expr="//group[@name='preferences']" position="after">
                     <group name="calendar" string="Calendrier">
-                        <field name="is_weekend" widget="boolean_toggle"/>
+                        <field name="is_weekend" widget="boolean_toggle" />
                     </group>
                 </xpath>
             </field>
@@ -20,11 +19,11 @@
         <record id="res_users_calendar_form_simple" model="ir.ui.view">
             <field name="name">res.users.preferences.calendar.form</field>
             <field name="model">res.users</field>
-            <field name="inherit_id" ref="base.view_users_form_simple_modif"/>
+            <field name="inherit_id" ref="base.view_users_form_simple_modif" />
             <field name="arch" type="xml">
                 <xpath expr="//group[@name='preferences']" position="after">
                     <group name="calendar" string="Calendrier">
-                        <field name="is_weekend" widget="boolean_toggle" readonly="0"/>
+                        <field name="is_weekend" widget="boolean_toggle" readonly="0" />
                     </group>
                 </xpath>
             </field>
-- 
GitLab