Initial commit: Vertical Industry packages

This commit is contained in:
Ernad Husremovic 2025-08-29 15:20:52 +02:00
commit d5567a0017
766 changed files with 733028 additions and 0 deletions

View file

@ -0,0 +1,56 @@
# Gamification
Gamification process
====================
The Gamification module provides ways to evaluate and motivate the users of Odoo.
The users can be evaluated using goals and numerical objectives to reach.
**Goals** are assigned through **challenges** to evaluate and compare members of a team with each others and through time.
For non-numerical achievements, **badges** can be granted to users. From a simple "thank you" to an exceptional achievement, a badge is an easy way to exprimate gratitude to a user for their good work.
Both goals and badges are flexibles and can be adapted to a large range of modules and actions. When installed, this module creates easy goals to help new users to discover Odoo and configure their user profile.
## Installation
```bash
pip install odoo-bringout-oca-ocb-gamification
```
## Dependencies
This addon depends on:
- mail
- web_kanban_gauge
## Manifest Information
- **Name**: Gamification
- **Version**: 1.0
- **Category**: Human Resources
- **License**: LGPL-3
- **Installable**: False
## Source
Based on [OCA/OCB](https://github.com/OCA/OCB) branch 16.0, addon `gamification`.
## License
This package maintains the original LGPL-3 license from the upstream Odoo project.
## Documentation
- Overview: doc/OVERVIEW.md
- Architecture: doc/ARCHITECTURE.md
- Models: doc/MODELS.md
- Controllers: doc/CONTROLLERS.md
- Wizards: doc/WIZARDS.md
- Install: doc/INSTALL.md
- Usage: doc/USAGE.md
- Configuration: doc/CONFIGURATION.md
- Dependencies: doc/DEPENDENCIES.md
- Troubleshooting: doc/TROUBLESHOOTING.md
- FAQ: doc/FAQ.md

View file

@ -0,0 +1,32 @@
# Architecture
```mermaid
flowchart TD
U[Users] -->|HTTP| V[Views and QWeb Templates]
V --> C[Controllers]
V --> W[Wizards Transient Models]
C --> M[Models and ORM]
W --> M
M --> R[Reports]
DX[Data XML] --> M
S[Security ACLs and Groups] -. enforces .-> M
subgraph Gamification Module - gamification
direction LR
M:::layer
W:::layer
C:::layer
V:::layer
R:::layer
S:::layer
DX:::layer
end
classDef layer fill:#eef8ff,stroke:#6ea8fe,stroke-width:1px
```
Notes
- Views include tree/form/kanban templates and report templates.
- Controllers provide website/portal routes when present.
- Wizards are UI flows implemented with `models.TransientModel`.
- Data XML loads data/demo records; Security defines groups and access.

View file

@ -0,0 +1,3 @@
# Configuration
Refer to Odoo settings for gamification. Configure related models, access rights, and options as needed.

View file

@ -0,0 +1,3 @@
# Controllers
This module does not define custom HTTP controllers.

View file

@ -0,0 +1,6 @@
# Dependencies
This addon depends on:
- [mail](../../odoo-bringout-oca-ocb-mail)
- [web_kanban_gauge](../../odoo-bringout-oca-ocb-web_kanban_gauge)

View file

@ -0,0 +1,4 @@
# FAQ
- Q: Which Odoo version? A: 16.0 (OCA/OCB packaged).
- Q: How to enable? A: Start server with --addon gamification or install in UI.

View file

@ -0,0 +1,7 @@
# Install
```bash
pip install odoo-bringout-oca-ocb-gamification"
# or
uv pip install odoo-bringout-oca-ocb-gamification"
```

View file

@ -0,0 +1,22 @@
# Models
Detected core models and extensions in gamification.
```mermaid
classDiagram
class gamification_badge
class gamification_badge_user
class gamification_challenge
class gamification_challenge_line
class gamification_goal
class gamification_goal_definition
class gamification_karma_rank
class gamification_karma_tracking
class image_mixin
class mail_thread
class res_users
```
Notes
- Classes show model technical names; fields omitted for brevity.
- Items listed under _inherit are extensions of existing models.

View file

@ -0,0 +1,6 @@
# Overview
Packaged Odoo addon: gamification. Provides features documented in upstream Odoo 16 under this addon.
- Source: OCA/OCB 16.0, addon gamification
- License: LGPL-3

View file

@ -0,0 +1,3 @@
# Reports
This module does not define custom reports.

View file

@ -0,0 +1,41 @@
# Security
Access control and security definitions in gamification.
## Access Control Lists (ACLs)
Model access permissions defined in:
- **[ir.model.access.csv](../gamification/security/ir.model.access.csv)**
- 34 model access rules
## Record Rules
Row-level security rules defined in:
## Security Groups & Configuration
Security groups and permissions defined in:
- **[gamification_security.xml](../gamification/security/gamification_security.xml)**
```mermaid
graph TB
subgraph "Security Layers"
A[Users] --> B[Groups]
B --> C[Access Control Lists]
C --> D[Models]
B --> E[Record Rules]
E --> F[Individual Records]
end
```
Security files overview:
- **[gamification_security.xml](../gamification/security/gamification_security.xml)**
- Security groups, categories, and XML-based rules
- **[ir.model.access.csv](../gamification/security/ir.model.access.csv)**
- Model access permissions (CRUD rights)
Notes
- Access Control Lists define which groups can access which models
- Record Rules provide row-level security (filter records by user/group)
- Security groups organize users and define permission sets
- All security is enforced at the ORM level by Odoo

View file

@ -0,0 +1,5 @@
# Troubleshooting
- Ensure Python and Odoo environment matches repo guidance.
- Check database connectivity and logs if startup fails.
- Validate that dependent addons listed in DEPENDENCIES.md are installed.

View file

@ -0,0 +1,7 @@
# Usage
Start Odoo including this addon (from repo root):
```bash
python3 scripts/nix_odoo_web_server.py --db-name mydb --addon gamification
```

View file

@ -0,0 +1,9 @@
# Wizards
Transient models exposed as UI wizards in gamification.
```mermaid
classDiagram
class goal_manual_wizard
class grant_badge_wizard
```

View file

@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import models
from . import wizard

View file

@ -0,0 +1,48 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
{
'name': 'Gamification',
'version': '1.0',
'sequence': 160,
'category': 'Human Resources',
'depends': ['mail', 'web_kanban_gauge'],
'description': """
Gamification process
====================
The Gamification module provides ways to evaluate and motivate the users of Odoo.
The users can be evaluated using goals and numerical objectives to reach.
**Goals** are assigned through **challenges** to evaluate and compare members of a team with each others and through time.
For non-numerical achievements, **badges** can be granted to users. From a simple "thank you" to an exceptional achievement, a badge is an easy way to exprimate gratitude to a user for their good work.
Both goals and badges are flexibles and can be adapted to a large range of modules and actions. When installed, this module creates easy goals to help new users to discover Odoo and configure their user profile.
""",
'data': [
'wizard/update_goal.xml',
'wizard/grant_badge.xml',
'views/res_users_views.xml',
'views/gamification_karma_rank_views.xml',
'views/gamification_karma_tracking_views.xml',
'views/gamification_badge_views.xml',
'views/gamification_badge_user_views.xml',
'views/gamification_goal_views.xml',
'views/gamification_goal_definition_views.xml',
'views/gamification_challenge_views.xml',
'views/gamification_challenge_line_views.xml',
'views/gamification_menus.xml',
'security/gamification_security.xml',
'security/ir.model.access.csv',
'data/ir_cron_data.xml',
'data/mail_template_data.xml', # keep before to populate challenge reports
'data/gamification_badge_data.xml',
'data/gamification_challenge_data.xml',
'data/gamification_karma_rank_data.xml',
],
'demo': [
'data/gamification_karma_rank_demo.xml',
'data/gamification_karma_tracking_demo.xml',
],
'license': 'LGPL-3',
}

View file

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<record id="badge_good_job" model="gamification.badge">
<field name="name">Good Job</field>
<field name="description">You did great at your job.</field>
<field name="rule_auth">everyone</field>
<field name="image_1920" type="base64" file="gamification/static/img/badge_good_job-image.png"/>
</record>
<record id="badge_problem_solver" model="gamification.badge">
<field name="name">Problem Solver</field>
<field name="description">No one can solve challenges like you do.</field>
<field name="rule_auth">everyone</field>
<field name="image_1920" type="base64" file="gamification/static/img/badge_problem_solver-image.png"/>
</record>
<record id="badge_hidden" model="gamification.badge">
<field name="name">Hidden</field>
<field name="description">You have found the hidden badge</field>
<field name="rule_auth">nobody</field>
<field name="image_1920" type="base64" file="gamification/static/img/badge_hidden-image.png"/>
<field name="active" eval="False" />
</record>
<record id="badge_idea" model="gamification.badge">
<field name="name">Brilliant</field>
<field name="description">With your brilliant ideas, you are an inspiration to others.</field>
<field name="rule_auth">everyone</field>
<field name="rule_max">True</field>
<field name="rule_max_number">2</field>
<field name="image_1920" type="base64" file="gamification/static/img/badge_idea-image.png"/>
</record>
</data>
</odoo>

View file

@ -0,0 +1,96 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<!-- goal definitions -->
<record model="gamification.goal.definition" id="definition_base_timezone">
<field name="name">Set your Timezone</field>
<field name="description">Configure your profile and specify your timezone</field>
<field name="computation_mode">count</field>
<field name="display_mode">boolean</field>
<field name="model_id" ref="base.model_res_users"/>
<field name="domain">[('partner_id.tz', '!=', False)]</field>
<field name="action_id" ref="base.action_res_users_my"/>
<field name="res_id_field">user.id</field>
<field name="batch_mode">True</field>
<field name="batch_distinctive_field" ref="base.field_res_users__id"/>
<field name="batch_user_expression">user.id</field>
</record>
<record model="gamification.goal.definition" id="definition_base_company_data">
<field name="name">Set your Company Data</field>
<field name="description">Write some information about your company (specify at least a name)</field>
<field name="computation_mode">count</field>
<field name="display_mode">boolean</field>
<field name="model_id" ref="base.model_res_company"/>
<field name="domain">[('user_ids', 'in', [user.id]), ('name', '=', 'YourCompany')]</field>
<field name="condition">lower</field>
<field name="action_id" ref="base.action_res_company_form"/>
<field name="res_id_field">user.company_id.id</field>
</record>
<record model="gamification.goal.definition" id="definition_base_company_logo">
<field name="name">Set your Company Logo</field>
<field name="computation_mode">count</field>
<field name="display_mode">boolean</field>
<field name="model_id" ref="base.model_res_company"/>
<field name="domain">[('user_ids', 'in', [user.id]),('logo', '!=', False)]</field>
<field name="action_id" ref="base.action_res_company_form"/>
<field name="res_id_field">user.company_id.id</field>
</record>
<record model="gamification.goal.definition" id="definition_base_invite">
<field name="name">Invite new Users</field>
<field name="description">Create at least another user</field>
<field name="display_mode">boolean</field>
<field name="computation_mode">count</field>
<field name="model_id" ref="base.model_res_users"/>
<field name="domain">[('id', '!=', user.id)]</field>
<field name="action_id" ref="action_new_simplified_res_users"/>
</record>
<!-- challenges -->
<record model="gamification.challenge" id="challenge_base_discover">
<field name="name">Complete your Profile</field>
<field name="period">once</field>
<field name="visibility_mode">personal</field>
<field name="report_message_frequency">never</field>
<field name="user_domain" eval="str([('groups_id.id', '=', ref('base.group_user'))])" />
<field name="state">inprogress</field>
<field name="challenge_category">other</field>
</record>
<record model="gamification.challenge" id="challenge_base_configure">
<field name="name">Setup your Company</field>
<field name="period">once</field>
<field name="visibility_mode">personal</field>
<field name="report_message_frequency">never</field>
<field name="user_domain" eval="str([('groups_id.id', '=', ref('base.group_erp_manager'))])" />
<field name="state">inprogress</field>
<field name="challenge_category">other</field>
</record>
<!-- lines -->
<record model="gamification.challenge.line" id="line_base_discover1">
<field name="definition_id" ref="definition_base_timezone"/>
<field name="target_goal">1</field>
<field name="challenge_id" ref="challenge_base_discover"/>
</record>
<record model="gamification.challenge.line" id="line_base_admin2">
<field name="definition_id" ref="definition_base_company_logo"/>
<field name="target_goal">1</field>
<field name="challenge_id" ref="challenge_base_configure"/>
</record>
<record model="gamification.challenge.line" id="line_base_admin1">
<field name="definition_id" ref="definition_base_company_data"/>
<field name="target_goal">0</field>
<field name="challenge_id" ref="challenge_base_configure"/>
</record>
<record model="gamification.challenge.line" id="line_base_admin3">
<field name="definition_id" ref="definition_base_invite"/>
<field name="target_goal">1</field>
<field name="challenge_id" ref="challenge_base_configure"/>
</record>
</data>
</odoo>

View file

@ -0,0 +1,76 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo><data noupdate="1">
<!--Karma-->
<record id="base.user_root" model="res.users">
<field name="karma">2500</field>
</record>
<record id="base.user_admin" model="res.users">
<field name="karma">2500</field>
</record>
<!--Ranks-->
<record id="rank_newbie" model="gamification.karma.rank">
<field name="name">Newbie</field>
<field name="description" type="html"><p>You just began the adventure! Welcome!</p></field>
<field name="description_motivational" type="html">
<div class="d-flex align-items-center">
<div class="flex-grow-1">Earn your first points and join the adventure !</div>
<img class="ms-3 img img-fluid" style="max-height: 72px;" src="/gamification/static/img/rank_newbie_badge.svg"/>
</div>
</field>
<field name="karma_min">1</field>
<field name="image_1920" type="base64" file="gamification/static/img/rank_newbie_badge.svg"/>
</record>
<record id="rank_student" model="gamification.karma.rank">
<field name="name">Student</field>
<field name="description" type="html"><p>You're a young padawan now. May the force be with you!</p></field>
<field name="description_motivational" type="html">
<div class="d-flex align-items-center">
<div class="flex-grow-1">Reach the next rank to show the rest of the world you exist.</div>
<img class="ms-3 img img-fluid" style="max-height: 72px;" src="/gamification/static/img/rank_student_badge.svg"/>
</div>
</field>
<field name="karma_min">100</field>
<field name="image_1920" type="base64" file="gamification/static/img/rank_student_badge.svg"/>
</record>
<record id="rank_bachelor" model="gamification.karma.rank">
<field name="name">Bachelor</field>
<field name="description" type="html"><p>You love learning things. Curiosity is a good way to progress.</p></field>
<field name="description_motivational" type="html">
<div class="d-flex align-items-center">
<div class="flex-grow-1">Reach the next rank to improve your status!</div>
<img class="ms-3 img img-fluid" style="max-height: 72px;" src="/gamification/static/img/rank_bachelor_badge.svg"/>
</div>
</field>
<field name="karma_min">500</field>
<field name="image_1920" type="base64" file="gamification/static/img/rank_bachelor_badge.svg"/>
</record>
<record id="rank_master" model="gamification.karma.rank">
<field name="name">Master</field>
<field name="description" type="html"><p>You know what you are talking about. People learn from you.</p></field>
<field name="description_motivational" type="html">
<div class="d-flex align-items-center">
<div class="flex-grow-1">Reach the next rank and become a Master!</div>
<img class="ms-3 img img-fluid" style="max-height: 72px;" src="/gamification/static/img/rank_master_badge.svg"/>
</div>
</field>
<field name="karma_min">2000</field>
<field name="image_1920" type="base64" file="gamification/static/img/rank_master_badge.svg"/>
</record>
<record id="rank_doctor" model="gamification.karma.rank">
<field name="name">Doctor</field>
<field name="description" type="html"><p>You have reached the last rank. Congratulations!</p></field>
<field name="description_motivational" type="html">
<div class="d-flex align-items-center">
<div class="flex-grow-1">Reach the next rank and become a powerful user!</div>
<img class="ms-3 img img-fluid" style="max-height: 72px;" src="/gamification/static/img/rank_doctor_badge.svg"/>
</div>
</field>
<field name="karma_min">10000</field>
<field name="image_1920" type="base64" file="gamification/static/img/rank_doctor_badge.svg"/>
</record>
</data></odoo>

View file

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo><data noupdate="1">
<!--Ranks-->
<!-- note that original motivational messages are duplicated+hidden to ensure they are included in .pot export when demo data installed -->
<record id="rank_student" model="gamification.karma.rank">
<field name="description_motivational" type="html">
<div hidden="true">Reach the next rank to show the rest of the world you exist.</div>
<div class="d-flex align-items-center">
<div class="flex-grow-1">Reach the next rank and gain a very nice mug !</div>
<img class="ms-3 img img-fluid" style="max-height: 72px;" src="/gamification/static/img/rank_misc_mug.png"/>
</div>
</field>
</record>
<record id="rank_bachelor" model="gamification.karma.rank">
<field name="description_motivational" type="html">
<div hidden="true">Reach the next rank to improve your status!</div>
<div class="d-flex align-items-center">
<div class="flex-grow-1">Reach the next rank and gain a very magic wand !</div>
<img class="ms-3 img img-fluid" style="max-height: 72px;" src="/gamification/static/img/rank_misc_wand.png"/>
</div>
</field>
</record>
<record id="rank_master" model="gamification.karma.rank">
<field name="description_motivational" type="html">
<div hidden="true">Reach the next rank and become a Master!</div>
<div class="d-flex align-items-center">
<div class="flex-grow-1">Reach the next rank and gain a very nice hat !</div>
<img class="ms-3 img img-fluid" style="max-height: 72px;" src="/gamification/static/img/rank_misc_hat.png"/>
</div>
</field>
</record>
<record id="rank_doctor" model="gamification.karma.rank">
<field name="description_motivational" type="html">
<div hidden="true">Reach the next rank and become a powerful user!</div>
<div class="d-flex align-items-center">
<div class="flex-grow-1">Reach the next rank and gain a very nice unicorn !</div>
<img class="ms-3 img img-fluid" style="max-height: 72px;" src="/gamification/static/img/rank_misc_unicorn.png"/>
</div>
</field>
</record>
</data></odoo>

View file

@ -0,0 +1,116 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo><data noupdate="1">
<!--base.user_demo-->
<record id="karma_tracking_user_demo_1st_day_last_month" model="gamification.karma.tracking">
<field name="user_id" ref="base.user_demo"/>
<field name="old_value">0</field>
<field name="new_value">1000</field>
<field name="tracking_date" eval="(DateTime.now() - relativedelta(day=1, months=1)).strftime('%Y-%m-%d')"/>
</record>
<record id="karma_tracking_user_demo_2nd_day_last_month" model="gamification.karma.tracking">
<field name="user_id" ref="base.user_demo"/>
<field name="old_value">1000</field>
<field name="new_value">1500</field>
<field name="tracking_date" eval="(DateTime.now() - relativedelta(day=2, months=1)).strftime('%Y-%m-%d')"/>
</record>
<record id="karma_tracking_user_demo_5th_day_last_month" model="gamification.karma.tracking">
<field name="user_id" ref="base.user_demo"/>
<field name="old_value">1500</field>
<field name="new_value">2000</field>
<field name="tracking_date" eval="(DateTime.now() - relativedelta(day=5, months=1)).strftime('%Y-%m-%d')"/>
</record>
<record id="karma_tracking_user_demo_20th_day_last_month" model="gamification.karma.tracking">
<field name="user_id" ref="base.user_demo"/>
<field name="old_value">2000</field>
<field name="new_value">2050</field>
<field name="tracking_date" eval="(DateTime.now() - relativedelta(day=20, months=1)).strftime('%Y-%m-%d')"/>
</record>
<record id="karma_tracking_user_demo_today" model="gamification.karma.tracking">
<field name="user_id" ref="base.user_demo"/>
<field name="old_value">2050</field>
<field name="new_value">2500</field>
<field name="tracking_date" eval="(DateTime.now()).strftime('%Y-%m-%d')"/>
</record>
<record id="base.user_demo" model="res.users">
<field name="karma">2500</field>
</record>
<function model="gamification.karma.tracking" name="unlink">
<value model="gamification.karma.tracking" eval="obj().search([
('user_id', '=', ref('base.user_demo')),
('old_value', '=', 0),
('new_value', '=', 2500)
]).id"/>
</function>
<!--base.demo_user0 -->
<record id="karma_tracking_user_portal_2nd_day_last_month" model="gamification.karma.tracking">
<field name="user_id" ref="base.demo_user0"/>
<field name="old_value">0</field>
<field name="new_value">5</field>
<field name="tracking_date" eval="(DateTime.now() - relativedelta(day=2, months=1)).strftime('%Y-%m-%d')"/>
</record>
<record id="karma_tracking_user_portal_3rd_day_last_month" model="gamification.karma.tracking">
<field name="user_id" ref="base.demo_user0"/>
<field name="old_value">5</field>
<field name="new_value">10</field>
<field name="tracking_date" eval="(DateTime.now() - relativedelta(day=3, months=1)).strftime('%Y-%m-%d')"/>
</record>
<record id="karma_tracking_user_portal_10th_day_last_month" model="gamification.karma.tracking">
<field name="user_id" ref="base.demo_user0"/>
<field name="old_value">10</field>
<field name="new_value">20</field>
<field name="tracking_date" eval="(DateTime.now() - relativedelta(day=10, months=1)).strftime('%Y-%m-%d')"/>
</record>
<record id="karma_tracking_user_portal_yesterday" model="gamification.karma.tracking">
<field name="user_id" ref="base.demo_user0"/>
<field name="old_value">20</field>
<field name="new_value">25</field>
<field name="tracking_date" eval="(DateTime.now() - relativedelta(day=1)).strftime('%Y-%m-%d')"/>
</record>
<record id="karma_tracking_user_portal_today" model="gamification.karma.tracking">
<field name="user_id" ref="base.demo_user0"/>
<field name="old_value">25</field>
<field name="new_value">30</field>
<field name="tracking_date" eval="DateTime.now()"/>
</record>
<record id="base.demo_user0" model="res.users">
<field name="karma">30</field>
</record>
<function model="gamification.karma.tracking" name="unlink">
<value model="gamification.karma.tracking" eval="obj().search([
('user_id', '=', ref('base.demo_user0')),
('old_value', '=', 0),
('new_value', '=', 30)
]).id"/>
</function>
<!--base.user_admin (already have a tracking to 2500)-->
<record id="karma_tracking_user_admin_1st_day_last_month" model="gamification.karma.tracking">
<field name="user_id" ref="base.user_admin"/>
<field name="old_value">0</field>
<field name="new_value">2000</field>
<field name="tracking_date" eval="(DateTime.now() - relativedelta(day=1, months=1)).strftime('%Y-%m-%d')"/>
</record>
<record id="karma_tracking_user_admin_5th_day_last_month" model="gamification.karma.tracking">
<field name="user_id" ref="base.user_admin"/>
<field name="old_value">2000</field>
<field name="new_value">2250</field>
<field name="tracking_date" eval="(DateTime.now() - relativedelta(day=5, months=1)).strftime('%Y-%m-%d')"/>
</record>
<record id="karma_tracking_user_admin_today" model="gamification.karma.tracking">
<field name="user_id" ref="base.user_admin"/>
<field name="old_value">2250</field>
<field name="new_value">2500</field>
<field name="tracking_date" eval="(DateTime.now()).strftime('%Y-%m-%d')"/>
</record>
<function model="gamification.karma.tracking" name="unlink">
<value model="gamification.karma.tracking" eval="obj().search([
('user_id', '=', ref('base.user_admin')),
('old_value', '=', 0),
('new_value', '=', 2500)
]).id"/>
</function>
</data>
</odoo>

View file

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<record forcecreate="True" id="ir_cron_check_challenge" model="ir.cron">
<field name="name">Gamification: Goal Challenge Check</field>
<field name="model_id" ref="model_gamification_challenge"/>
<field name="state">code</field>
<field name="code">model._cron_update()</field>
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="numbercall">-1</field>
<field eval="False" name="doall" />
</record>
<record id="ir_cron_consolidate_last_month" model="ir.cron">
<field name="name">Gamification: Karma tracking consolidation</field>
<field name="model_id" ref="model_gamification_karma_tracking"/>
<field name="state">code</field>
<field name="code">model._consolidate_last_month()</field>
<field name="active" eval="True"/>
<field name="interval_number">1</field>
<field name="interval_type">months</field>
<field name="numbercall">-1</field>
<field name="nextcall" eval="(DateTime.now() + relativedelta(day=1, months=1)).strftime('%Y-%m-%d 04:00:00')" />
</record>
</data>
</odoo>

View file

@ -0,0 +1,373 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<record id="email_template_badge_received" model="mail.template">
<field name="name">Gamification: Badge Received</field>
<field name="subject">New badge {{ object.badge_id.name }} granted</field>
<field name="model_id" ref="gamification.model_gamification_badge_user"/>
<field name="partner_to">{{ object.user_id.partner_id.id }}</field>
<field name="description">Sent automatically to the user who received a badge</field>
<field name="body_html" type="html">
<table border="0" cellpadding="0" style="padding-top: 16px; background-color: #F1F1F1; color: #454748; width: 100%; border-collapse:separate;"><tr><td align="center">
<table border="0" width="590" cellpadding="0" style="padding: 16px; background-color: white; color: #454748; border-collapse:separate;" summary="o_mail_notification">
<tbody>
<!-- HEADER -->
<tr>
<td align="center" style="min-width: 590px;">
<table width="590" border="0" cellpadding="0" style="min-width: 590px; background-color: white; padding: 0px 8px 0px 8px; border-collapse:separate;">
<tr><td valign="middle">
<span style="font-size: 10px;">Your Badge</span><br/>
<span style="font-size: 20px; font-weight: bold;" t-out="object.badge_id.name or ''"></span>
</td><td valign="middle" align="right">
<img t-attf-src="/logo.png?company={{ object.user_id.company_id.id }}" style="padding: 0px; margin: 0px; height: auto; width: 80px;" t-att-alt="object.user_id.company_id.name"/>
</td></tr>
<tr><td colspan="2" style="text-align:center;">
<hr width="100%" style="background-color:rgb(204,204,204);border:medium none;clear:both;display:block;font-size:0px;min-height:1px;line-height:0; margin: 16px 0px 16px 0px;"/>
</td></tr>
</table>
</td>
</tr>
<!-- CONTENT -->
<tr>
<td align="center" style="min-width: 590px;">
<table width="590" border="0" cellpadding="0" style="min-width: 590px; background-color: white; padding: 0px 8px 0px 8px; border-collapse:separate;">
<tr><td valign="top" style="font-size: 14px;">
<div>
Congratulations <t t-out="object.user_id.name or ''"></t> !<br/>
You just received badge <strong t-out="object.badge_id.name or ''"></strong> !<br/>
<table t-if="not is_html_empty(object.badge_id.description)" cellspacing="0" cellpadding="0" border="0" style="width: 560px; margin-top: 5px;">
<tbody><tr>
<td valign="center">
<img t-attf-src="/web/image/gamification.badge/{{ object.badge_id.id }}/image_128/80x80" style="padding: 0px; margin: 0px; height: auto; width: 80px;" t-att-alt="user.company_id.name"/>
</td>
<td valign="center">
<cite t-out="object.badge_id.description or ''"></cite>
</td>
</tr></tbody>
</table>
<br/>
<t t-if="object.sender_id">
This badge was granted by <strong t-out="object.sender_id.name or ''"></strong>.
</t>
<br/>
<t t-if="object.comment" t-out="object.comment or ''"></t>
<br/><br/>
Thank you,
<t t-if="object.sender_id.signature">
<br />
<t t-out="object.sender_id.signature or ''"></t>
</t>
</div>
</td></tr>
<tr><td style="text-align:center;">
<hr width="100%" style="background-color:rgb(204,204,204);border:medium none;clear:both;display:block;font-size:0px;min-height:1px;line-height:0; margin: 16px 0px 16px 0px;"/>
</td></tr>
</table>
</td>
</tr>
<!-- FOOTER -->
<tr>
<td align="center" style="min-width: 590px;">
<table width="590" border="0" cellpadding="0" style="min-width: 590px; background-color: white; font-size: 12px; padding: 0px 8px 0px 8px; border-collapse:separate;">
<tr><td valign="middle" align="left">
<t t-out="object.user_id.company_id.name or ''">YourCompany</t>
</td></tr>
<tr><td valign="middle" align="left" style="opacity: 0.7;">
<t t-out="object.user_id.company_id.phone or ''">+1 650-123-4567</t>
<t t-if="object.user_id.company_id.email">
| <a t-attf-href="'mailto:%s' % {{ object.user_id.company_id.email }}" style="text-decoration:none; color: #454748;" t-out="object.user_id.company_id.email or ''">info@yourcompany.com</a>
</t>
<t t-if="object.user_id.company_id.website">
| <a t-attf-href="'%s' % {{ object.user_id.company_id.website }}" style="text-decoration:none; color: #454748;" t-out="object.user_id.company_id.website or ''">http://www.example.com</a>
</t>
</td></tr>
</table>
</td>
</tr>
</tbody>
</table>
</td></tr>
<!-- POWERED BY -->
<tr><td align="center" style="min-width: 590px;">
<table width="590" border="0" cellpadding="0" style="min-width: 590px; background-color: #F1F1F1; color: #454748; padding: 8px; border-collapse:separate;">
<tr><td style="text-align: center; font-size: 14px;">
Powered by <a target="_blank" href="https://www.odoo.com?utm_source=db&amp;utm_medium=gamification" style="color: #875A7B;">Odoo</a>
</td></tr>
</table>
</td></tr>
</table></field>
<field name="lang">{{ object.user_id.lang }}</field>
<field name="auto_delete" eval="True"/>
</record>
<record id="email_template_goal_reminder" model="mail.template">
<field name="name">Gamification: Reminder For Goal Update</field>
<field name="model_id" ref="gamification.model_gamification_goal"/>
<field name="partner_to">{{ object.user_id.partner_id.id }}</field>
<field name="description">Sent automatically to participant who haven't updated their goal</field>
<field name="body_html" type="html">
<div>
<strong>Reminder</strong><br/>
You have not updated your progress for the goal <t t-out="object.definition_id.name or ''"></t> (currently reached at <t t-out="object.completeness or ''"></t>%) for at least <t t-out="object.remind_update_delay or ''"></t> days. Do not forget to do it.
<br/><br/>
Thank you,
<t t-if="object.challenge_id.manager_id.signature">
<br />
<t t-out="object.challenge_id.manager_id.signature or ''"></t>
</t>
</div></field>
<field name="lang">{{ object.user_id.lang }}</field>
<field name="auto_delete" eval="True"/>
</record>
<record id="simple_report_template" model="mail.template">
<field name="name">Gamification: Challenge Report</field>
<field name="model_id" ref="gamification.model_gamification_challenge"/>
<field name="description">Send a challenge report to all participants</field>
<field name="body_html" type="html">
<table cellspacing="0" cellpadding="0" width="100%" style="background-color: #EEE; border-collapse: collapse;">
<tr>
<td valign="top" align="center">
<t t-set="object_ctx" t-value="ctx.get('object')"/>
<t t-set="company" t-value="object_ctx and object_ctx.company_id or user.company_id"/>
<t t-set="challenge_lines" t-value="ctx.get('challenge_lines', [])"/>
<table cellspacing="0" cellpadding="0" width="600" style="margin: 0 auto; width: 570px;">
<tr><td>
<table cellspacing="0" cellpadding="0" width="100%">
<tr>
<div>
<t t-if="object.visibility_mode == 'ranking'">
<td style="padding:15px;">
<p style="font-size:20px;color:#666666;" align="center">Leaderboard</p>
</td>
</t>
</div>
</tr>
</table>
<table cellspacing="0" cellpadding="0" width="100%" bgcolor="#fff" style="background-color:#fff;">
<tr><td style="padding: 15px;">
<t t-if="object.visibility_mode == 'personal'">
<span style="color:#666666;font-size:13px;">Here is your current progress in the challenge <strong t-out="object.name or ''"></strong>.</span>
<table cellspacing="0" cellpadding="0" width="100%" style="margin-top:20px;">
<tr>
<td align="center">
<div>Personal Performance</div>
</td>
</tr>
</table>
<table cellspacing="0" cellpadding="0" width="100%" style="margin-top:30px;color:#666666;">
<thead>
<tr style="color:#9A6C8E; font-size:12px;">
<th align="left" style="padding-bottom: 0px;width:40%;text-align:left;">Goals</th>
<th style="width:20%;text-align:right;" align="left">Target</th>
<th style="width:20%;text-align:right;" align="right">Current</th>
<th style="width:20%;text-align:right;" align="right">Completeness</th>
</tr>
<tr>
<td colspan="5" style="height:1px;background-color:#9A6C8E;"></td>
</tr>
</thead>
<tbody t-foreach="challenge_lines" t-as="line">
<tr style="font-weight:bold;">
<td style="padding: 20px 0;" align="left">
<t t-out="line['name'] or ''"></t>
<t t-if="line['suffix'] or line['monetary']">
(<t t-out="line['full_suffix'] or ''"></t>)
</t>
</td>
<td style="padding: 20px 0;" align="right"><t t-out="&quot;%.2f&quot; % line['target'] or ''"></t>
<t t-if="line['suffix']" t-out="line['suffix'] or ''"></t>
</td>
<td style="padding: 20px 0;" align="right"><t t-out="&quot;%.2f&quot; % line['current'] or ''"></t>
<t t-if="line['suffix']" t-out="line['suffix'] or ''"></t>
</td>
<td style="padding: 20px 0;font-size:25px;color:#9A6C8E;" align="right"><strong><t t-out="int(line['completeness']) or ''"></t>%</strong></td>
</tr>
<tr>
<td colspan="5" style="height:1px;background-color:#e3e3e3;"></td>
</tr>
</tbody>
</table>
</t>
<t t-else="">
<span style="color:#A8A8A8;font-size:13px;">
Challenge: <strong t-out="object.name or ''"></strong>.
</span>
<t t-foreach="challenge_lines" t-as="line">
<!-- Header + Button table -->
<table cellspacing="0" cellpadding="0" width="100%" style="margin-top:35px;">
<tr>
<td width="50%">
<div>Top Achievers for goal <strong t-out="line['name'] or ''"></strong></div>
</td>
</tr>
</table>
<!-- Podium -->
<t t-if="len(line['goals']) == 2">
<table cellspacing="0" cellpadding="0" width="100%" style="margin-top:10px;">
<tr><td style="padding:0 30px;">
<table cellspacing="0" cellpadding="0" width="100%" style="table-layout: fixed;">
<tr>
<t t-set="top_goals" t-value="line['goals'][:3]"/>
<t t-foreach="top_goals" t-as="goal">
<td align="center" style="width:32%;">
<t t-if="loop.index == 1">
<t t-set="extra_div" t-value="'&lt;div style=&quot;height:40px;&quot;&gt;&lt;/div&gt;'"/>
<t t-set="heightA" t-value="95"/>
<t t-set="heightB" t-value="75"/>
<t t-set="bgColor" t-value="'#b898b0'"/>
<t t-set="fontSize" t-value="50"/>
<t t-set="podiumPosition" t-value="'2'"/>
</t>
<t t-elif="loop.index == 2">
<t t-set="extra_div" t-value="''"/>
<t t-set="heightA" t-value="55"/>
<t t-set="heightB" t-value="115"/>
<t t-set="bgColor" t-value="'#9A6C8E'"/>
<t t-set="fontSize" t-value="85"/>
<t t-set="podiumPosition" t-value="'1'"/>
</t>
<t t-elif="loop.index == 3">
<t t-set="extra_div" t-value="'&lt;div style=&quot;height:60px;&quot;&gt;&lt;/div&gt;'"/>
<t t-set="heightA" t-value="115"/>
<t t-set="heightB" t-value="55"/>
<t t-set="bgColor" t-value="'#c8afc1'"/>
<t t-set="fontSize" t-value="35"/>
<t t-set="podiumPosition" t-value="'3'"/>
</t>
<div style="margin:0 3px 0 3px;height:220px;">
<div t-attf-style="height:{{ heightA }}px;">
<t t-out="extra_div or ''"></t>
<div style="height:55px;">
<img style="margin-bottom:5px;width:50px;height:50px;border-radius:50%;object-fit:cover;" t-att-src="image_data_uri(object.env['res.users'].browse(goal['user_id']).partner_id.image_128)" t-att-alt="goal['name']"/>
</div>
<div align="center" t-attf-style ="color:{{ bgColor }};height:20px">
<t t-out="goal['name'] or ''"></t>
</div>
</div>
<div t-attf-style="background-color:{{ bgColor }};height:{{ heightB }}px;">
<strong><span t-attf-style="color:#fff;font-size:{{ fontSize }}px;" t-out="podiumPosition or ''"></span></strong>
</div>
<div style="height:30px;">
<t t-out="&quot;%.2f&quot; % goal['current'] or ''"></t>
<t t-if="line['suffix'] or line['monetary']">
<t t-out="line['full_suffix'] or ''"></t>
</t>
</div>
</div>
</td>
</t>
</tr>
</table>
</td>
</tr>
</table>
</t>
<!-- data table -->
<table cellspacing="0" cellpadding="0" width="100%" style="margin-bottom:5px">
<tr>
<td>
<table cellspacing="0" cellpadding="0" width="100%" style="margin-top:30px;margin-bottom:5px;color:#666666;">
<thead>
<tr style="color:#9A6C8E; font-size:12px;">
<th style="width:15%;text-align:center;">Rank</th>
<th style="width:25%;text-align:left;">Name</th>
<th style="width:30%;text-align:right;">Performance
<t t-if="line['suffix']">
(<t t-out="line['suffix'] or ''"></t>)
</t>
<t t-elif="line['monetary']">
(<t t-out="company.currency_id.symbol or ''"></t>)
</t>
</th>
<th style="width:30%;text-align:right;">Completeness</th>
</tr>
<tr>
<td colspan="5" style="height:1px;background-color:#9A6C8E;"></td>
</tr>
</thead>
<tbody t-foreach="line['goals']" t-as="goal">
<tr>
<t t-set="tdBgColor" t-value="'#fff'"/>
<t t-set="tdColor" t-value="'gray'"/>
<t t-set="mutedColor" t-value="'#AAAAAA'"/>
<t t-set="tdPercentageColor" t-value="'#9A6C8E'"/>
<td width="15%" align="center" valign="middle" t-attf-style="background-color:{{ tdBgColor }};padding :5px 0;font-size:20px;"><t t-out="goal['rank']+1 or ''"></t>
</td>
<td width="25%" align="left" valign="middle" t-attf-style="background-color:{{ tdBgColor }};padding :5px 0;font-size:13px;"><t t-out="goal['name'] or ''"></t></td>
<td width="30%" align="right" t-attf-style="background-color:{{ tdBgColor }};padding:5px 0;line-height:1;"><t t-out="&quot;%.2f&quot; % goal['current'] or ''"></t><br/><span t-attf-style="font-size:13px;color:{{ mutedColor }};">on <t t-out="&quot;%.2f&quot; % line['target'] or ''"></t></span>
</td>
<td width="30%" t-attf-style="color:{{ tdPercentageColor }};background-color:{{ tdBgColor }};padding-right:15px;font-size:22px;" align="right"><strong><t t-out="int(goal['completeness']) or ''"></t>%</strong></td>
</tr>
<tr>
<td colspan="5" style="height:1px;background-color:#DADADA;"></td>
</tr>
</tbody>
</table>
</td>
</tr>
</table>
</t>
</t>
</td></tr>
</table>
</td></tr>
</table>
</td>
</tr>
</table>
</field>
</record>
<record id="mail_template_data_new_rank_reached" model="mail.template">
<field name="name">Gamification: New Rank Reached</field>
<field name="model_id" ref="base.model_res_users"/>
<field name="subject">New rank: {{ object.rank_id.name }}</field>
<field name="email_to"></field>
<field name="partner_to">{{ object.partner_id.id }}</field>
<field name="description">Sent automatically when user reaches a new rank</field>
<field name="body_html" type="html">
<div style="background:#F0F0F0;color:#515166;padding:10px 0px;font-family:Arial,Helvetica,sans-serif;font-size:14px;">
<table style="width:600px;margin:0px auto;background:white;border:1px solid #e1e1e1;">
<tbody>
<tr>
<td style="padding:15px 20px 10px 20px;">
<p>
Congratulations
<span t-out="object.name or ''">Joel Willis</span>!
</p>
<p>
You just reached a new rank : <strong t-out="object.rank_id.name or ''">Newbie</strong>
</p>
<t t-if="object.next_rank_id.name">
<p>Continue your work to become a <strong t-out="object.next_rank_id.name or ''">Student</strong> !</p>
</t>
<div style="margin: 16px 0px 16px 0px;">
<t t-set="gamification_redirection_data" t-value="object.get_gamification_redirection_data()"/>
<t t-foreach="gamification_redirection_data" t-as="data">
<t t-set="url" t-value="data['url']"/>
<t t-set="label" t-value="data['label']"/>
<a t-att-href="url" style="background-color: #875A7B; padding: 8px 16px 8px 16px; text-decoration: none; color: #fff; border-radius: 5px; font-size:13px;" t-out="label or ''">LABEL</a>
</t>
</div>
</td>
<td style="padding:15px 20px 10px 20px;">
<p style="text-align: center;">
<img t-attf-src="/web/image/gamification.karma.rank/{{ object.rank_id.id }}/image_128"/>
</p>
</td>
</tr>
<tr t-if="user.signature">
<td style="padding:15px 20px 10px 20px;">
<t t-out="user.signature or ''">--<br/>Mitchell Admin</t>
</td>
</tr>
</tbody>
</table>
</div></field>
<field name="lang">{{ object.lang }}</field>
<field name="auto_delete" eval="True"/>
</record>
</data>
</odoo>

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

Some files were not shown because too many files have changed in this diff Show more