diff --git a/learning_base/README.rst b/learning_base/README.rst new file mode 100644 index 0000000..7e168d0 --- /dev/null +++ b/learning_base/README.rst @@ -0,0 +1,91 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: https://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 + +============= +Learning Base +============= + +Base module to add training management to odoo + +Installation +============ + +To install this module, you need to: + +#. Do this ... + +Configuration +============= + +To configure this module, you need to: + +#. Go to ... + +.. figure:: path/to/local/image.png + :alt: alternative description + :width: 600 px + +Usage +===== + +To use this module, you need to: + +#. Go to ... + +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/{repo_id}/{branch} + +.. repo_id is available in https://github.com/OCA/maintainer-tools/blob/master/tools/repos_with_ids.txt +.. branch is "8.0" for example + +Known issues / Roadmap +====================== + +* ... + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues +`_. In case of trouble, please +check there if your issue has already been reported. If you spotted it first, +help us smash it by providing detailed and welcomed feedback. + +Credits +======= + +Images +------ + +* Odoo Community Association: `Icon `_. + +Contributors +------------ + +* Firstname Lastname +* Second Person + +Funders +------- + +The development of this module has been financially supported by: + +* Company 1 name +* Company 2 name + +Maintainer +---------- + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +This module is maintained by the OCA. + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +To contribute to this module, please visit https://odoo-community.org. diff --git a/learning_base/__init__.py b/learning_base/__init__.py new file mode 100644 index 0000000..cc7110f --- /dev/null +++ b/learning_base/__init__.py @@ -0,0 +1,2 @@ +from . import models +from . import controler diff --git a/learning_base/__manifest__.py b/learning_base/__manifest__.py new file mode 100644 index 0000000..208e952 --- /dev/null +++ b/learning_base/__manifest__.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- + +{ + "name": 'Learning Base', + "version": "16.0.0.0.0", + "depends": [ + #'__export__', + 'base', + 'event', + 'sale', + 'product', + 'website', + 'website_sale', + 'event_sale', + 'partner_firstname', + 'product', + 'website_event_sale', + 'purchase', + ], + "author": "Nicolas JEUDY, Odoo Community Association (OCA)", + "installable": True, + "data": [ + 'security/learning_security.xml', + 'security/ir.model.access.csv', + 'ir_actions_act_window_records.xml', + 'ir_ui_menu_records.xml', + 'ir_module_category_records.xml', + 'views/product_template.xml', + 'views/event_event.xml', + 'views/res_company.xml', + 'views/res_partner.xml', + 'views/template.xml', + ], +} diff --git a/learning_base/controler/__init__.py b/learning_base/controler/__init__.py new file mode 100644 index 0000000..12a7e52 --- /dev/null +++ b/learning_base/controler/__init__.py @@ -0,0 +1 @@ +from . import main diff --git a/learning_base/controler/main.py b/learning_base/controler/main.py new file mode 100644 index 0000000..1fa82c6 --- /dev/null +++ b/learning_base/controler/main.py @@ -0,0 +1,343 @@ +import json +import logging +from werkzeug.exceptions import Forbidden, NotFound +import werkzeug +from datetime import datetime, timedelta +from dateutil.relativedelta import relativedelta + +from odoo import fields, http, tools, _ +from odoo.http import request +from odoo.addons.http_routing.models.ir_http import slug +from odoo.addons.website.controllers.main import QueryURL +from odoo.exceptions import ValidationError +from odoo.addons.website.controllers.main import Website +from odoo.addons.website_form_project.controllers.main import WebsiteForm +from odoo.addons.website_event.controllers.main import WebsiteEventController + +from odoo.osv import expression + +_logger = logging.getLogger(__name__) + +PPG = 20 # Products Per Page +PPR = 4 # Products Per Row + + +class TableCompute(object): + + def __init__(self): + self.table = {} + + def _check_place(self, posx, posy, sizex, sizey): + res = True + for y in range(sizey): + for x in range(sizex): + if posx + x >= PPR: + res = False + break + row = self.table.setdefault(posy + y, {}) + if row.setdefault(posx + x) is not None: + res = False + break + for x in range(PPR): + self.table[posy + y].setdefault(x, None) + return res + + def process(self, products, ppg=PPG): + # Compute products positions on the grid + minpos = 0 + index = 0 + maxy = 0 + x = 0 + for p in products: + x = min(max(p.website_size_x, 1), PPR) + y = min(max(p.website_size_y, 1), PPR) + if index >= ppg: + x = y = 1 + + pos = minpos + while not self._check_place(pos % PPR, pos // PPR, x, y): + pos += 1 + # if 21st products (index 20) and the last line is full (PPR products in it), break + # (pos + 1.0) / PPR is the line where the product would be inserted + # maxy is the number of existing lines + # + 1.0 is because pos begins at 0, thus pos 20 is actually the 21st block + # and to force python to not round the division operation + if index >= ppg and ((pos + 1.0) // PPR) > maxy: + break + + if x == 1 and y == 1: # simple heuristic for CPU optimization + minpos = pos // PPR + + for y2 in range(y): + for x2 in range(x): + self.table[(pos // PPR) + y2][(pos % PPR) + x2] = False + self.table[pos // PPR][pos % PPR] = { + 'product': p, 'x': x, 'y': y, + 'class': " ".join(x.html_class for x in p.website_style_ids if x.html_class) + } + if index <= ppg: + maxy = max(maxy, y + (pos // PPR)) + index += 1 + + # Format table according to HTML needs + rows = sorted(self.table.items()) + rows = [r[1] for r in rows] + for col in range(len(rows)): + cols = sorted(rows[col].items()) + x += len(cols) + rows[col] = [r[1] for r in cols if r[1]] + + return rows + + +class WebsiteLearning(http.Controller): + + def _get_search_order(self, post): + # OrderBy will be parsed in orm and so no direct sql injection + # id is added to be sure that order is a unique sort key + return 'website_published desc,%s , id desc' % post.get('order', 'website_sequence desc') + + def _get_compute_currency_and_context(self): + pricelist_context = dict(request.env.context) + pricelist = False + if not pricelist_context.get('pricelist'): + pricelist = request.website.get_current_pricelist() + pricelist_context['pricelist'] = pricelist.id + else: + pricelist = request.env['product.pricelist'].browse(pricelist_context['pricelist']) + + from_currency = request.env.user.company_id.currency_id + to_currency = pricelist.currency_id + compute_currency = lambda price: from_currency.compute(price, to_currency) + + return compute_currency, pricelist_context, pricelist + + def _get_search_domain(self, search, category, attrib_values): + domain = [('is_training', '=', True)] + if search: + for srch in search.split(" "): + domain += [ + '|', '|', '|', ('name', 'ilike', srch), ('description', 'ilike', srch), + ('description_sale', 'ilike', srch), ('product_variant_ids.default_code', 'ilike', srch)] + + if category: + domain += [('public_categ_ids', 'child_of', int(category))] + + if attrib_values: + attrib = None + ids = [] + for value in attrib_values: + if not attrib: + attrib = value[0] + ids.append(value[1]) + elif value[0] == attrib: + ids.append(value[1]) + else: + domain += [('attribute_line_ids.value_ids', 'in', ids)] + attrib = value[0] + ids = [value[1]] + if attrib: + domain += [('attribute_line_ids.value_ids', 'in', ids)] + + return domain + + @http.route([ + '/learning', + '/learning/page/', + '/learning/category/', + '/learning/category//page/' + ], type='http', auth="public", website=True) + def learning(self, page=0, category=None, search='', **post): + ppg = 20 + if category: + category = request.env['product.public.category'].search([('id', '=', int(category))], limit=1) + if not category: + raise NotFound() + + attrib_list = request.httprequest.args.getlist('attrib') + attrib_values = [[int(x) for x in v.split("-")] for v in attrib_list if v] + attributes_ids = {v[0] for v in attrib_values} + attrib_set = {v[1] for v in attrib_values} + + domain = self._get_search_domain(search, category, attrib_values) + + keep = QueryURL('/learning', category=category and int(category), search=search, attrib=attrib_list, order=post.get('order')) + + compute_currency, pricelist_context, pricelist = self._get_compute_currency_and_context() + + request.context = dict(request.context, pricelist=pricelist.id, partner=request.env.user.partner_id) + + url = "/learning" + if search: + post["search"] = search + if attrib_list: + post['attrib'] = attrib_list + + categs = request.env['product.public.category'].search([('parent_id', '=', False)]) + Product = request.env['product.template'] + + parent_category_ids = [] + if category: + url = "/learning/category/%s" % slug(category) + parent_category_ids = [category.id] + current_category = category + while current_category.parent_id: + parent_category_ids.append(current_category.parent_id.id) + current_category = current_category.parent_id + + product_count = Product.search_count(domain) + pager = request.website.pager(url=url, total=product_count, page=page, step=ppg, scope=7, url_args=post) + products = Product.search(domain, limit=ppg, offset=pager['offset'], order=self._get_search_order(post)) + + ProductAttribute = request.env['product.attribute'] + if products: + # get all products without limit + selected_products = Product.search(domain, limit=False) + attributes = ProductAttribute.search([('attribute_line_ids.product_tmpl_id', 'in', selected_products.ids)]) + else: + attributes = ProductAttribute.browse(attributes_ids) + + values = { + 'search': search, + 'category': category, + 'attrib_values': attrib_values, + 'attrib_set': attrib_set, + 'pager': pager, + 'pricelist': pricelist, + 'products': products, + 'search_count': product_count, # common for all searchbox + 'bins': TableCompute().process(products, ppg), + 'rows': 5, + 'categories': categs, + 'attributes': attributes, + 'compute_currency': compute_currency, + 'keep': keep, + 'parent_category_ids': parent_category_ids, + } + if category: + values['main_object'] = category + return request.render("training_base.learnings", values) + + @http.route(['/learning/event'], type='http', auth="public", website=True) + def events(self, page=1, **searches): + Event = request.env['event.event'] + EventType = request.env['event.type'] + + searches.setdefault('date', 'all') + searches.setdefault('type', 'all') + searches.setdefault('country', 'all') + searches.setdefault('product_id', 'all') + + domain_search = {} + + def sdn(date): + return fields.Datetime.to_string(date.replace(hour=23, minute=59, second=59)) + + def sd(date): + return fields.Datetime.to_string(date) + today = datetime.today() + dates = [ + ['all', _('Next Events'), [("date_end", ">", sd(today))], 0], + ['today', _('Today'), [ + ("date_end", ">", sd(today)), + ("date_begin", "<", sdn(today))], + 0], + ['week', _('This Week'), [ + ("date_end", ">=", sd(today + relativedelta(days=-today.weekday()))), + ("date_begin", "<", sdn(today + relativedelta(days=6-today.weekday())))], + 0], + ['nextweek', _('Next Week'), [ + ("date_end", ">=", sd(today + relativedelta(days=7-today.weekday()))), + ("date_begin", "<", sdn(today + relativedelta(days=13-today.weekday())))], + 0], + ['month', _('This month'), [ + ("date_end", ">=", sd(today.replace(day=1))), + ("date_begin", "<", (today.replace(day=1) + relativedelta(months=1)).strftime('%Y-%m-%d 00:00:00'))], + 0], + ['nextmonth', _('Next month'), [ + ("date_end", ">=", sd(today.replace(day=1) + relativedelta(months=1))), + ("date_begin", "<", (today.replace(day=1) + relativedelta(months=2)).strftime('%Y-%m-%d 00:00:00'))], + 0], + ['old', _('Old Events'), [ + ("date_end", "<", today.strftime('%Y-%m-%d 00:00:00'))], + 0], + ] + + # search domains + # TDE note: WTF ??? + current_date = None + current_type = None + current_country = None + for date in dates: + if searches["date"] == date[0]: + domain_search["date"] = date[2] + if date[0] != 'all': + current_date = date[1] + if searches["type"] != 'all': + current_type = EventType.browse(int(searches['type'])) + domain_search["type"] = [("event_type_id", "=", int(searches["type"]))] + + if searches["country"] != 'all' and searches["country"] != 'online': + current_country = request.env['res.country'].browse(int(searches['country'])) + domain_search["country"] = ['|', ("country_id", "=", int(searches["country"])), ("country_id", "=", False)] + elif searches["country"] == 'online': + domain_search["country"] = [("country_id", "=", False)] + + if searches["product_id"] != 'all': + domain_search["product_id"] = [("event_ticket_ids.product_id", "=", int(searches["product_id"]))] + + def dom_without(without): + domain = [('state', "in", ['draft', 'confirm', 'done'])] + for key, search in domain_search.items(): + if key != without: + domain += search + return domain + + # count by domains without self search + for date in dates: + if date[0] != 'old': + date[3] = Event.search_count(dom_without('date') + date[2]) + + domain = dom_without('type') + types = Event.read_group(domain, ["id", "event_type_id"], groupby=["event_type_id"], orderby="event_type_id") + types.insert(0, { + 'event_type_id_count': sum([int(type['event_type_id_count']) for type in types]), + 'event_type_id': ("all", _("All Categories")) + }) + + domain = dom_without('country') + countries = Event.read_group(domain, ["id", "country_id"], groupby="country_id", orderby="country_id") + countries.insert(0, { + 'country_id_count': sum([int(country['country_id_count']) for country in countries]), + 'country_id': ("all", _("All Countries")) + }) + + step = 20 # Number of events per page + event_count = Event.search_count(dom_without("none")) + pager = request.website.pager( + url="/event", + url_args={'date': searches.get('date'), 'type': searches.get('type'), 'country': searches.get('country')}, + total=event_count, + page=page, + step=step, + scope=5) + + order = 'website_published desc, date_begin' + if searches.get('date', 'all') == 'old': + order = 'website_published desc, date_begin desc' + events = Event.search(dom_without("none"), limit=step, offset=pager['offset'], order=order) + + values = { + 'current_date': current_date, + 'current_country': current_country, + 'current_type': current_type, + 'event_ids': events, # event_ids used in website_event_track so we keep name as it is + 'dates': dates, + 'types': types, + 'countries': countries, + 'pager': pager, + 'searches': searches, + 'search_path': "?%s" % werkzeug.url_encode(searches), + } + + return request.render("website_event.index", values) diff --git a/learning_base/i18n/fr.po b/learning_base/i18n/fr.po new file mode 100644 index 0000000..27e0c78 --- /dev/null +++ b/learning_base/i18n/fr.po @@ -0,0 +1,368 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * learning_base +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 13.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2020-04-24 15:30+0000\n" +"PO-Revision-Date: 2020-04-24 17:36+0200\n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: \n" +"Language: fr\n" +"X-Generator: Poedit 2.0.4\n" + +#. module: learning_base +#: code:addons/learning_base/controler/main.py:0 +#, python-format +msgid "All Categories" +msgstr "Toutes les catégories" + +#. module: learning_base +#: code:addons/learning_base/controler/main.py:0 +#, python-format +msgid "All Countries" +msgstr "Tous les pays" + +#. module: learning_base +#: model:ir.ui.menu,name:learning_base.ir_ui_menu_domaine_r0 +msgid "Category" +msgstr "Catégorie" + +#. module: learning_base +#: model:ir.model.fields,field_description:learning_base.field_product_product__certificate +#: model:ir.model.fields,field_description:learning_base.field_product_template__certificate +msgid "Certificate" +msgstr "Certification" + +#. module: learning_base +#: model:ir.model.fields,field_description:learning_base.field_learning_domain__child_id +msgid "Child Domain" +msgstr "Domain enfant" + +#. module: learning_base +#: model:ir.model.fields,field_description:learning_base.field_learning_domain__code +msgid "Code" +msgstr "Code" + +#. module: learning_base +#: model:ir.ui.menu,name:learning_base.ir_ui_menu_configuration_r0 +msgid "Configuration" +msgstr "Configuration" + +#. module: learning_base +#: model:ir.model,name:learning_base.model_res_partner +msgid "Contact" +msgstr "Contact" + +#. module: learning_base +#: model:ir.model.fields,field_description:learning_base.field_product_product__content +#: model:ir.model.fields,field_description:learning_base.field_product_template__content +#: model_terms:ir.ui.view,arch_db:learning_base.learning_base_product_template_form +msgid "Content" +msgstr "Contenu" + +#. module: learning_base +#: model:ir.model.fields,field_description:learning_base.field_learning_domain__create_uid +msgid "Created by" +msgstr "Créé par" + +#. module: learning_base +#: model:ir.model.fields,field_description:learning_base.field_learning_domain__create_date +msgid "Created on" +msgstr "Créé le" + +#. module: learning_base +#: model_terms:ir.actions.act_window,help:learning_base.ir_actions_act_window_formation_r0 +msgid "Créer un nouveau article" +msgstr "Créer un nouveau article" + +#. module: learning_base +#: model_terms:ir.actions.act_window,help:learning_base.ir_actions_act_window_session_r0 +msgid "Créer un nouveau document" +msgstr "Créer un nouveau document" + +#. module: learning_base +#: model:ir.model.fields,field_description:learning_base.field_product_product__description +#: model:ir.model.fields,field_description:learning_base.field_product_template__description +#: model_terms:ir.ui.view,arch_db:learning_base.learning_base_product_template_form +msgid "Description" +msgstr "Description" + +#. module: learning_base +#: model:ir.model.fields,field_description:learning_base.field_learning_domain__display_name +msgid "Display Name" +msgstr "Afficher Nom" + +#. module: learning_base +#: model:ir.model.fields,field_description:learning_base.field_product_product__domain_id +#: model:ir.model.fields,field_description:learning_base.field_product_template__domain_id +msgid "Domain" +msgstr "Domaine" + +#. module: learning_base +#: model:ir.model.fields,field_description:learning_base.field_product_product__parent_domain_id +#: model:ir.model.fields,field_description:learning_base.field_product_template__parent_domain_id +msgid "Domain parent" +msgstr "Domaine Parent" + +#. module: learning_base +#: model:ir.actions.act_window,name:learning_base.ir_actions_act_window_domaine_r0 +msgid "Domaine" +msgstr "Domaine" + +#. module: learning_base +#: model:ir.model.fields,field_description:learning_base.field_event_event__duration_hour +#: model:ir.model.fields,field_description:learning_base.field_product_product__duration_hour +#: model:ir.model.fields,field_description:learning_base.field_product_template__duration_hour +msgid "Duration in hour(s)" +msgstr "Durée en heure(s)" + +#. module: learning_base +#: model:ir.model,name:learning_base.model_event_event +msgid "Event" +msgstr "Événement" + +#. module: learning_base +#: model:ir.model,name:learning_base.model_event_registration +msgid "Event Registration" +msgstr "Inscription à l'événement" + +#. module: learning_base +#: model:ir.actions.act_window,name:learning_base.ir_actions_act_window_formation_r0 +#: model:ir.module.category,name:learning_base.ir_module_category_formation_r0 +msgid "Formation" +msgstr "Formation" + +#. module: learning_base +#: model:ir.model.fields,field_description:learning_base.field_product_product__goal +#: model:ir.model.fields,field_description:learning_base.field_product_template__goal +#: model_terms:ir.ui.view,arch_db:learning_base.learning_base_product_template_form +msgid "Goal" +msgstr "Objectif(s)" + +#. module: learning_base +#: model:ir.model.fields,field_description:learning_base.field_learning_domain__id +msgid "ID" +msgstr "ID" + +#. module: learning_base +#: model:ir.model.fields,field_description:learning_base.field_res_partner__is_student +#: model:ir.model.fields,field_description:learning_base.field_res_users__is_student +msgid "Is student ?" +msgstr "Etudiant" + +#. module: learning_base +#: model:ir.model.fields,field_description:learning_base.field_res_partner__is_trainer +#: model:ir.model.fields,field_description:learning_base.field_res_users__is_trainer +msgid "Is trainer ?" +msgstr "Formateur" + +#. module: learning_base +#: model:ir.model.fields,field_description:learning_base.field_learning_domain____last_update +msgid "Last Modified on" +msgstr "Dernière modification le" + +#. module: learning_base +#: model:ir.model.fields,field_description:learning_base.field_learning_domain__write_uid +msgid "Last Updated by" +msgstr "Dernière mise à jour par" + +#. module: learning_base +#: model:ir.model.fields,field_description:learning_base.field_learning_domain__write_date +msgid "Last Updated on" +msgstr "Dernière mise à jour le" + +#. module: learning_base +#: model:ir.model.fields,field_description:learning_base.field_event_event__learning_id +#: model:ir.ui.menu,name:learning_base.ir_ui_menu_formation_r0 +#: model:ir.ui.menu,name:learning_base.ir_ui_menu_formation_r1 +#: model:ir.ui.menu,name:learning_base.ir_ui_menu_inscriptions_r0 +#: model_terms:ir.ui.view,arch_db:learning_base.learning_base_product_template_form +msgid "Learning" +msgstr "Formation" + +#. module: learning_base +#: model:res.groups,name:learning_base.group_learning_administration +msgid "Learning Administrator" +msgstr "Administrateur des formations" + +#. module: learning_base +#: model:res.groups,name:learning_base.group_learning_backoffice +msgid "Learning Backoffice" +msgstr "Utilisateur des formations" + +#. module: learning_base +#: model:ir.module.category,name:learning_base.module_category_learning +msgid "Learning Management" +msgstr "Gestion des formations" + +#. module: learning_base +#: model:res.groups,name:learning_base.group_learning_student +msgid "Learning Student" +msgstr "Apprenant" + +#. module: learning_base +#: model:res.groups,name:learning_base.group_learning_teacher +msgid "Learning Teacher" +msgstr "Formateur" + +#. module: learning_base +#: model:res.groups,name:learning_base.group_learning_trainer +msgid "Learning Trainer" +msgstr "Formateur" + +#. module: learning_base +#: model:ir.model.fields,field_description:learning_base.field_product_product__methodology +#: model:ir.model.fields,field_description:learning_base.field_product_template__methodology +#: model_terms:ir.ui.view,arch_db:learning_base.learning_base_product_template_form +msgid "Methodology" +msgstr "Méthodologie" + +#. module: learning_base +#: model:ir.model.fields,field_description:learning_base.field_learning_domain__name +msgid "Name" +msgstr "Nom" + +#. module: learning_base +#: model:ir.model.fields,field_description:learning_base.field_product_product__count_session +#: model:ir.model.fields,field_description:learning_base.field_product_template__count_session +msgid "Nb session" +msgstr "Nb Session(s)" + +#. module: learning_base +#: code:addons/learning_base/controler/main.py:0 +#, python-format +msgid "Next Events" +msgstr "Prochains événements" + +#. module: learning_base +#: code:addons/learning_base/controler/main.py:0 +#, python-format +msgid "Next Week" +msgstr "Semaine suivante" + +#. module: learning_base +#: code:addons/learning_base/controler/main.py:0 +#, python-format +msgid "Next month" +msgstr "Mois suivant" + +#. module: learning_base +#: code:addons/learning_base/controler/main.py:0 +#, python-format +msgid "Old Events" +msgstr "Anciens événements" + +#. module: learning_base +#: model_terms:ir.ui.view,arch_db:learning_base.learning_base_product_template_form +msgid "Our value" +msgstr "Nos « plus »" + +#. module: learning_base +#: model:ir.model.fields,field_description:learning_base.field_learning_domain__parent_id +msgid "Parent Domain" +msgstr "Domaine parent" + +#. module: learning_base +#: model:ir.model.fields,field_description:learning_base.field_learning_domain__parent_path +msgid "Parent Path" +msgstr "Chemin parent" + +#. module: learning_base +#: code:addons/learning_base/controler/main.py:0 +#, python-format +msgid "Past Events" +msgstr "Événements passés" + +#. module: learning_base +#: model:ir.model.fields,field_description:learning_base.field_product_product__prerequisite +#: model:ir.model.fields,field_description:learning_base.field_product_template__prerequisite +msgid "Prerequisite" +msgstr "Les Pré-requis" + +#. module: learning_base +#: model:ir.model,name:learning_base.model_product_template +msgid "Product Template" +msgstr "Modèle d'article" + +#. module: learning_base +#: model:ir.model.fields,field_description:learning_base.field_product_product__public +#: model:ir.model.fields,field_description:learning_base.field_product_template__public +#: model_terms:ir.ui.view,arch_db:learning_base.learning_base_product_template_form +msgid "Public" +msgstr "Publique" + +#. module: learning_base +#: model:ir.actions.act_window,name:learning_base.ir_actions_act_window_session_r0 +#: model:ir.ui.menu,name:learning_base.ir_ui_menu_session_r0 +msgid "Session" +msgstr "Session" + +#. module: learning_base +#: model_terms:ir.ui.view,arch_db:learning_base.learning_base_product_template_form +msgid "Sessions" +msgstr "Sessions" + +#. module: learning_base +#: code:addons/learning_base/controler/main.py:0 +#, python-format +msgid "This Week" +msgstr "Cette semaine" + +#. module: learning_base +#: code:addons/learning_base/controler/main.py:0 +#, python-format +msgid "This month" +msgstr "Ce mois" + +#. module: learning_base +#: code:addons/learning_base/controler/main.py:0 +#, python-format +msgid "Today" +msgstr "Aujourd'hui" + +#. module: learning_base +#: model:res.groups,name:learning_base.res_groups_utilisateur_r0 +msgid "Utilisateur" +msgstr "Utilisateur" + +#. module: learning_base +#: model:ir.model.fields,field_description:learning_base.field_product_product__validate +#: model:ir.model.fields,field_description:learning_base.field_product_template__validate +msgid "Validate" +msgstr "Valider" + +#. module: learning_base +#: model_terms:ir.ui.view,arch_db:learning_base.learning_base_product_template_form +msgid "Validation" +msgstr "Validation" + +#. module: learning_base +#: model:ir.model.fields,field_description:learning_base.field_product_product__our_value +#: model:ir.model.fields,field_description:learning_base.field_product_template__our_value +msgid "Value" +msgstr "Valeur" + +#. module: learning_base +#: code:addons/learning_base/models/learning_domain.py:0 +#, python-format +msgid "You cannot create recursive domain." +msgstr "Impossible de créer un domaine récursif." + +#. module: learning_base +#: model:ir.model.fields,field_description:learning_base.field_event_registration__is_learning +#: model:ir.model.fields,field_description:learning_base.field_product_product__is_learning +#: model:ir.model.fields,field_description:learning_base.field_product_template__is_learning +msgid "is learning" +msgstr "Est une formation" + +#. module: learning_base +#: model:ir.model,name:learning_base.model_learning_domain +msgid "learning.domain" +msgstr "learning.domain" diff --git a/learning_base/ir_actions_act_window_records.xml b/learning_base/ir_actions_act_window_records.xml new file mode 100644 index 0000000..6535073 --- /dev/null +++ b/learning_base/ir_actions_act_window_records.xml @@ -0,0 +1,76 @@ + + + + + [('is_learning', '=', 1)] + {'default_is_learning': 1} + <p class="o_view_nocontent_smiling_face">Créer un nouveau article</p> + product.template + action + ir.actions.act_window + kanban,tree,form,activity + + Formation + current + 0 + + 80 + + + <p class="o_view_nocontent_smiling_face">Créer un nouveau document</p> + event.event + action + ir.actions.act_window + kanban,calendar,tree,form,pivot + + Session + current + 0 + + 80 + {"search_default_upcoming":1, "search_default_learning_filter": 1} + + + learning.domain + action + ir.actions.act_window + tree,form + + Domaine + current + 0 + + 80 + + + [('is_company', '=', 1), ('is_learning_contact', '=', 1)] + {'search_default_supplier': 1,'res_partner_search_mode': 'supplier','default_is_learning_contact': True,'default_is_company': True} + <p class="o_view_nocontent_smiling_face">Créer un nouveau Partenaire de formation</p> + res.partner + action + ir.actions.act_window + kanban,tree,form,activity + + Learning Company + current + 0 + + 80 + + + [('is_company', '=', 0), ('is_trainer', '=', 1)] + {'search_default_supplier': 1,'res_partner_search_mode': 'supplier','default_is_learning_contact': True,'default_is_trainer': True,'default_is_company': False} + <p class="o_view_nocontent_smiling_face">Créer un nouveau Formateur</p> + res.partner + action + ir.actions.act_window + kanban,tree,form,activity + + Trainer(s) + current + 0 + + 80 + + + \ No newline at end of file diff --git a/learning_base/ir_module_category_records.xml b/learning_base/ir_module_category_records.xml new file mode 100644 index 0000000..a0c3866 --- /dev/null +++ b/learning_base/ir_module_category_records.xml @@ -0,0 +1,8 @@ + + + + + Formation + + + diff --git a/learning_base/ir_ui_menu_records.xml b/learning_base/ir_ui_menu_records.xml new file mode 100644 index 0000000..93362f0 --- /dev/null +++ b/learning_base/ir_ui_menu_records.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/learning_base/models/__init__.py b/learning_base/models/__init__.py new file mode 100644 index 0000000..0a1779d --- /dev/null +++ b/learning_base/models/__init__.py @@ -0,0 +1,6 @@ +from . import product_template +from . import event_event +from . import res_partner +from . import learning_domain +from . import res_company +from . import website \ No newline at end of file diff --git a/learning_base/models/event_event.py b/learning_base/models/event_event.py new file mode 100644 index 0000000..80b7af8 --- /dev/null +++ b/learning_base/models/event_event.py @@ -0,0 +1,39 @@ +# Copyright 2018 Nicolas JEUDY +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +import logging +import datetime +from odoo import api, fields, models, _ + +_logger = logging.getLogger(__name__) + + +class EventEvent(models.Model): + _inherit = ['event.event'] + + duration_hour = fields.Float('Duration in hour(s)') + learning_id = fields.Many2one('product.template', string='Learning', domain=[('is_learning', '=', True)]) + date_text= fields.Char("Date in text mode") + hour_text= fields.Char("Training time") + duration_days = fields.Float(related='learning_id.duration_days', store=True ) + methodology_partner_id = fields.Many2one('res.partner', "Methodology partner") + +class EventRegistration(models.Model): + _inherit = 'event.registration' + + is_learning = fields.Boolean(related='event_id.learning_id.is_learning', readonly="1", store=True) + +class EventTicket(models.Model): + _inherit = 'event.event.ticket' + + @api.model + def default_get(self, fields): + res = super(EventTicket, self).default_get(fields) + product_tmpl_id = self.env.context.get('learning_id', False) + if product_tmpl_id: + product_id = self.env['product.product'].search( + [('product_tmpl_id', '=', product_tmpl_id)], + limit=1 + ) + if product_id: + res['product_id'] = product_id.id + return res diff --git a/learning_base/models/learning_domain.py b/learning_base/models/learning_domain.py new file mode 100644 index 0000000..d01f456 --- /dev/null +++ b/learning_base/models/learning_domain.py @@ -0,0 +1,20 @@ +from odoo import models, fields, api, _ +from odoo.exceptions import ValidationError + + +class LearningDomain(models.Model): + _name = 'learning.domain' + _parent_name = "parent_id" + _parent_store = True + + name = fields.Char('Name') + code = fields.Char('Code', translate=False) + parent_id = fields.Many2one('learning.domain', 'Parent Domain', index=True, ondelete='cascade') + parent_path = fields.Char(index=True) + child_id = fields.One2many('learning.domain', 'parent_id', 'Child Domain') + + @api.constrains('parent_id') + def _check_domain_recursion(self): + if not self._check_recursion(): + raise ValidationError(_('You cannot create recursive domain.')) + return True \ No newline at end of file diff --git a/learning_base/models/product_template.py b/learning_base/models/product_template.py new file mode 100644 index 0000000..4f16208 --- /dev/null +++ b/learning_base/models/product_template.py @@ -0,0 +1,64 @@ +# Copyright 2018 Nicolas JEUDY +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models, _ + + +class learningFinancialProgram(models.Model): + _name = 'learning.financial.program' + _description = "Learning Financial Program" + + product_tmpl_ids = fields.Many2many('product.template', 'financial_program_product_category_rel', 'financial_program_id', 'product_category_id', 'Products') + name = fields.Char("Name") + description = fields.Text('Description') + description_html = fields.Html("Web Description") + + +class ProductTemplate(models.Model): + _inherit = ['product.template'] + + is_learning = fields.Boolean(compute='_compute_is_learning') + detailed_type = fields.Selection(selection_add=[ + ('learning', 'Training'), + ], ondelete={'learning': 'set service'}) + + + goal = fields.Html(string='Goal', translate=True) + duration_html = fields.Html(string='Duration', translate=True) + public = fields.Html(string='Public', translate=True) + prerequisite = fields.Html(string='Prerequisite', translate=True) + content = fields.Html(string='Content', translate=True) + organizer = fields.Html(string='Organizer', translate=True) + methodology = fields.Html(string='Methodology', translate=True) + technic = fields.Html(string='Technical', translate=True) + price_html = fields.Html(string='Price', translate=True) + registration = fields.Html(string='Registration', translate=True) + + our_value = fields.Html(string='Value', translate=True) + + description = fields.Html(string='Description', translate=True) + validate = fields.Html(string='Validate', translate=True) + certificate = fields.Html(string='Certificate', translate=True) + + financial_program_ids = fields.Many2many('learning.financial.program', 'financial_program_product_category_rel', 'product_category_id', 'financial_program_id', 'Financial Program') + parent_domain_id = fields.Many2one('learning.domain', related='domain_id.parent_id', store=True, string="Domain parent") + domain_id = fields.Many2one('learning.domain', string="Domain") + duration_hour = fields.Float('Duration in hour(s)') + hours_per_day = fields.Float('Nb hour(s) per day(s) ?') + duration_days = fields.Float('Duration in day(s)') + duration_text = fields.Text('Duration details') + count_session = fields.Integer('Nb session', compute="_compute_session", readonly=True) + sessions_ids = fields.One2many('event.event', 'learning_id', 'Slots') + + # ajouter la nature de l'action de formation avec la liste + + def _compute_session(self): + for record in self: + record.count_session = len(self.env['event.event'].search([('learning_id', '=', record.id)])) + + @api.depends('detailed_type') + def _compute_is_learning(self): + for record in self: + record.is_learning = record.detailed_type == 'learning' + + \ No newline at end of file diff --git a/learning_base/models/res_company.py b/learning_base/models/res_company.py new file mode 100644 index 0000000..53dd72e --- /dev/null +++ b/learning_base/models/res_company.py @@ -0,0 +1,10 @@ +# Copyright 2018 Nicolas JEUDY +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from odoo import api, fields, models, _ + + +class ResCompany(models.Model): + _inherit = 'res.company' + + learning_code = fields.Char('Learning Code') + learning_support_email = fields.Char("Support Email") \ No newline at end of file diff --git a/learning_base/models/res_partner.py b/learning_base/models/res_partner.py new file mode 100644 index 0000000..0e3382d --- /dev/null +++ b/learning_base/models/res_partner.py @@ -0,0 +1,36 @@ +# Copyright 2018 Nicolas JEUDY +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +#import barcode +#from barcode.writer import ImageWriter +import base64 +import logging +from io import BytesIO +import re +import unicodedata + +from odoo import api, fields, models, _ + + +class ResPartner(models.Model): + _inherit = 'res.partner' + + # student_barcode = fields.Binary('Barcode', attachment=True, compute="_compute_barcode", store=True) + + is_student = fields.Boolean("Student") + is_trainer = fields.Boolean("Trainer") + is_learning_contact = fields.Boolean("Learning contact") + trainer_cv = fields.Char("CV") + # ajouter un lien vers linkedin ou site internet + + #@api.depends('ref') + #def _compute_barcode(self): + # for record in self: + # if record.ref: + # CODE39 = barcode.get_barcode_class('code39') + # code39 = CODE39(record.ref, writer=ImageWriter(), add_checksum=False) + # fp = BytesIO() + # code39.write(fp) + # #barcode.generate('code39', self.ref, writer=ImageWriter(), output=fp) + # record.student_barcode = base64.b64encode(fp.getvalue()) + # else: + # record.student_barcode = False \ No newline at end of file diff --git a/learning_base/models/website.py b/learning_base/models/website.py new file mode 100644 index 0000000..5809871 --- /dev/null +++ b/learning_base/models/website.py @@ -0,0 +1,14 @@ +import logging +from odoo import models + +_logger = logging.getLogger(__name__) + + +class Website(models.Model): + _inherit = 'website' + + def sale_product_domain(self): + # ['&', ('sale_ok', '=', True), ('website_id', 'in', (False, 1)), ('event_ok', '=', False)] + _logger.debug(super(Website, self).sale_product_domain()) + #return ['&'] + super(Website, self).sale_product_domain() + [('event_ok', '=', False)] + return [('sale_ok', '=', True), ('website_id', 'in', (False, 1))] \ No newline at end of file diff --git a/learning_base/security/ir.model.access.csv b/learning_base/security/ir.model.access.csv new file mode 100644 index 0000000..1652377 --- /dev/null +++ b/learning_base/security/ir.model.access.csv @@ -0,0 +1,16 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_account_move_manager,learning: account_move manager,account.model_account_move,learning_base.group_learning_administration,1,0,0,0 +access_sale_order_manager,learning: sale.order.manager,sale.model_sale_order,learning_base.group_learning_administration,1,1,1,1 +access_sale_report_manager,learning: sale.report,sale.model_sale_report,learning_base.group_learning_administration,1,1,1,1 +access_product_supplierinfo_sale_manager,learning: product.supplierinfo salemanager,product.model_product_supplierinfo,learning_base.group_learning_administration,1,1,1,1 +access_product_group_res_partner_sale_manager,learning: res_partner group_sale_manager,base.model_res_partner,learning_base.group_learning_administration,1,1,1,0 +access_product_template_sale_manager,learning: product.template salemanager,product.model_product_template,learning_base.group_learning_administration,1,1,1,1 +access_product_product_sale_manager,learning: product.product salemanager,product.model_product_product,learning_base.group_learning_administration,1,1,1,1 +access_product_attribute_sale_manager,learning: product.attribute manager,product.model_product_attribute,learning_base.group_learning_administration,1,1,1,1 +access_product_attribute_value_sale_manager,learning: product.attribute manager value,product.model_product_attribute_value,learning_base.group_learning_administration,1,1,1,1 +access_product_product_attribute_sale_manager,learning: product.template.attribute manager value,product.model_product_template_attribute_value,learning_base.group_learning_administration,1,1,1,1 +access_product_template_attribute_exclusion_sale_manager,learning: product.attribute manager filter line,product.model_product_template_attribute_exclusion,learning_base.group_learning_administration,1,1,1,1 +access_product_template_attribute_line_sale_manager,learning: product.attribute manager line,product.model_product_template_attribute_line,learning_base.group_learning_administration,1,1,1,1 +access_account_account_sale_manager,learning: account.account sale manager,account.model_account_account,learning_base.group_learning_administration,1,0,0,0 +access_mail_activity_type_sale_manager,learning: mail.activity.type.sale.manager,mail.model_mail_activity_type,learning_base.group_learning_administration,1,1,1,1 +access_report_all_channels_sales,learning: access_report_all_channels_sales,sale.model_sale_report,learning_base.group_learning_administration,1,0,0,0 diff --git a/learning_base/security/learning_security.xml b/learning_base/security/learning_security.xml new file mode 100644 index 0000000..0068679 --- /dev/null +++ b/learning_base/security/learning_security.xml @@ -0,0 +1,52 @@ + + + + + + + + Learning Management + 22 + + + + + Learning Backoffice + + + + + Learning Trainer + + + + + + Learning Student + + + + Utilisateur + + + + + Learning Administrator + + + + + + + + Learning Product Rules + + [('is_learning','=',1)] + + + + + + + + diff --git a/learning_base/static/description/icon.png b/learning_base/static/description/icon.png new file mode 100644 index 0000000..d5a5982 Binary files /dev/null and b/learning_base/static/description/icon.png differ diff --git a/learning_base/static/img/learning_1024.png b/learning_base/static/img/learning_1024.png new file mode 100644 index 0000000..7f00293 Binary files /dev/null and b/learning_base/static/img/learning_1024.png differ diff --git a/learning_base/static/img/learning_256.png b/learning_base/static/img/learning_256.png new file mode 100644 index 0000000..d5a5982 Binary files /dev/null and b/learning_base/static/img/learning_256.png differ diff --git a/learning_base/static/img/learning_512.png b/learning_base/static/img/learning_512.png new file mode 100644 index 0000000..1934624 Binary files /dev/null and b/learning_base/static/img/learning_512.png differ diff --git a/learning_base/views/event_event.xml b/learning_base/views/event_event.xml new file mode 100644 index 0000000..9e5404d --- /dev/null +++ b/learning_base/views/event_event.xml @@ -0,0 +1,40 @@ + + + + + + event.event.learning.form.inherit + event.event + + + + + + + + + + + + + + + + + {'default_name': name, 'learning_id': learning_id} + + + + + + learning.base.event_search.inherit + event.event + + + + + + + + \ No newline at end of file diff --git a/learning_base/views/product_template.xml b/learning_base/views/product_template.xml new file mode 100644 index 0000000..5d644cb --- /dev/null +++ b/learning_base/views/product_template.xml @@ -0,0 +1,100 @@ + + + + + + product.template.learning.form.inherit + product.template + + + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+
+
diff --git a/learning_base/views/res_company.xml b/learning_base/views/res_company.xml new file mode 100644 index 0000000..2c9d80f --- /dev/null +++ b/learning_base/views/res_company.xml @@ -0,0 +1,23 @@ + + + + + + learning_base.view_company_form + res.company + + + + + + + + + + + + + + + diff --git a/learning_base/views/res_partner.xml b/learning_base/views/res_partner.xml new file mode 100644 index 0000000..5af02c6 --- /dev/null +++ b/learning_base/views/res_partner.xml @@ -0,0 +1,22 @@ + + + + + + learning.base.partner.form.inherit + res.partner + + + + + + + + + + + + + + diff --git a/learning_base/views/template.xml b/learning_base/views/template.xml new file mode 100644 index 0000000..962bcee --- /dev/null +++ b/learning_base/views/template.xml @@ -0,0 +1,48 @@ + + + + + diff --git a/learning_event_partner/README.rst b/learning_event_partner/README.rst new file mode 100644 index 0000000..1c56969 --- /dev/null +++ b/learning_event_partner/README.rst @@ -0,0 +1,91 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: https://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 + +====================== +Learning Event Partner +====================== + +In learning session you may have many speaker / teacher. This module add the management for this case. + +Installation +============ + +To install this module, you need to: + +#. Do this ... + +Configuration +============= + +To configure this module, you need to: + +#. Go to ... + +.. figure:: path/to/local/image.png + :alt: alternative description + :width: 600 px + +Usage +===== + +To use this module, you need to: + +#. Go to ... + +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/{repo_id}/{branch} + +.. repo_id is available in https://github.com/OCA/maintainer-tools/blob/master/tools/repos_with_ids.txt +.. branch is "8.0" for example + +Known issues / Roadmap +====================== + +* ... + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues +`_. In case of trouble, please +check there if your issue has already been reported. If you spotted it first, +help us smash it by providing detailed and welcomed feedback. + +Credits +======= + +Images +------ + +* Odoo Community Association: `Icon `_. + +Contributors +------------ + +* Firstname Lastname +* Second Person + +Funders +------- + +The development of this module has been financially supported by: + +* Company 1 name +* Company 2 name + +Maintainer +---------- + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +This module is maintained by the OCA. + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +To contribute to this module, please visit https://odoo-community.org. diff --git a/learning_event_partner/__init__.py b/learning_event_partner/__init__.py new file mode 100644 index 0000000..0650744 --- /dev/null +++ b/learning_event_partner/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/learning_event_partner/__manifest__.py b/learning_event_partner/__manifest__.py new file mode 100644 index 0000000..6e334d5 --- /dev/null +++ b/learning_event_partner/__manifest__.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- + +{ + "name": "Learning Event Partner", + "version": "16.0.0.0.0", + "depends": [ + #'__export__', + "base", + "event", + "hr", + ], + "author": "Nicolas JEUDY", + "installable": True, + "data": [ + "views/event_menu_view.xml", + "views/event_speakers_view.xml", + "views/event_event_view.xml", + "views/hr_employee_views.xml", + "security/ir.model.access.csv", + ], +} diff --git a/learning_event_partner/i18n/fr.po b/learning_event_partner/i18n/fr.po new file mode 100644 index 0000000..6d0ca12 --- /dev/null +++ b/learning_event_partner/i18n/fr.po @@ -0,0 +1,465 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * learning_event_partner +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2021-11-29 18:16+0000\n" +"PO-Revision-Date: 2021-11-29 19:23+0100\n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: \n" +"Language: fr\n" +"X-Generator: Poedit 3.0\n" + +#. module: learning_event_partner +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker__message_needaction +msgid "Action Needed" +msgstr "Action necessaire" + +#. module: learning_event_partner +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker__activity_ids +msgid "Activities" +msgstr "Activités" + +#. module: learning_event_partner +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker__activity_exception_decoration +msgid "Activity Exception Decoration" +msgstr "" + +#. module: learning_event_partner +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker__activity_state +msgid "Activity State" +msgstr "Status de l'activité" + +#. module: learning_event_partner +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker__activity_type_icon +msgid "Activity Type Icon" +msgstr "Icon Type d'activité" + +#. module: learning_event_partner +#: model_terms:ir.actions.act_window,help:learning_event_partner.act_speakers_from_event +msgid "Add new speakers to this event" +msgstr "Ajouter un nouvel intervenant pour cet évènement" + +#. module: learning_event_partner +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker__message_attachment_count +msgid "Attachment Count" +msgstr "Pièce jointes" + +#. module: learning_event_partner +#: code:addons/learning_event_partner/models/event_speaker_stage.py:0 +#, python-format +msgid "Blocked" +msgstr "Blocké" + +#. module: learning_event_partner +#: model_terms:ir.ui.view,arch_db:learning_event_partner.event_speaker_view_kanban +msgid "By" +msgstr "Par" + +#. module: learning_event_partner +#: model:ir.model.fields,field_description:learning_event_partner.field_event_discipline__create_uid +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker__create_uid +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker_stage__create_uid +msgid "Created by" +msgstr "Créé par" + +#. module: learning_event_partner +#: model:ir.model.fields,field_description:learning_event_partner.field_event_discipline__create_date +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker__create_date +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker_stage__create_date +msgid "Created on" +msgstr "Créé le" + +#. module: learning_event_partner +#: model:ir.model,name:learning_event_partner.model_event_discipline +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker__discipline_id +#: model_terms:ir.ui.view,arch_db:learning_event_partner.event_speaker_view_search +msgid "Discipline" +msgstr "Discipline" + +#. module: learning_event_partner +#: model:ir.model.fields,field_description:learning_event_partner.field_event_discipline__display_name +#: model:ir.model.fields,field_description:learning_event_partner.field_event_event__display_name +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker__display_name +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker_stage__display_name +msgid "Display Name" +msgstr "Nom affiché" + +#. module: learning_event_partner +#: model_terms:ir.ui.view,arch_db:learning_event_partner.event_speaker_view_search +msgid "Employee" +msgstr "Employé" + +#. module: learning_event_partner +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker_stage__pipe_end +msgid "End Stage" +msgstr "Stage final" + +#. module: learning_event_partner +#: model:ir.model,name:learning_event_partner.model_event_event +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker__event_id +msgid "Event" +msgstr "Événement" + +#. module: learning_event_partner +#: model:ir.model.fields,help:learning_event_partner.field_event_speaker_stage__pipe_end +msgid "" +"Events will automatically be moved into this stage when they are finished. " +"The event moved into this stage will automatically be set as green." +msgstr "" +"L'intervenant passera automatiquement à ce status quant le traitement sera " +"terminé." + +#. module: learning_event_partner +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker__extra_price +msgid "Extra Price" +msgstr "Montant Supplément" + +#. module: learning_event_partner +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker_stage__fold +msgid "Folded in Kanban" +msgstr "Replié en vue Kanban" + +#. module: learning_event_partner +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker__message_follower_ids +msgid "Followers" +msgstr "" + +#. module: learning_event_partner +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker__message_channel_ids +msgid "Followers (Channels)" +msgstr "" + +#. module: learning_event_partner +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker__message_partner_ids +msgid "Followers (Partners)" +msgstr "" + +#. module: learning_event_partner +#: model:ir.model.fields,help:learning_event_partner.field_event_speaker__activity_type_icon +msgid "Font awesome icon e.g. fa-tasks" +msgstr "" + +#. module: learning_event_partner +#: model_terms:ir.ui.view,arch_db:learning_event_partner.event_speaker_view_search +msgid "Future Activities" +msgstr "" + +#. module: learning_event_partner +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker_stage__legend_done +msgid "Green Kanban Label" +msgstr "" + +#. module: learning_event_partner +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker_stage__legend_normal +msgid "Grey Kanban Label" +msgstr "" + +#. module: learning_event_partner +#: model_terms:ir.ui.view,arch_db:learning_event_partner.event_speaker_view_search +msgid "Group By" +msgstr "" + +#. module: learning_event_partner +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker__has_extra +msgid "Has Extra ?" +msgstr "Souhait un supplément ?" + +#. module: learning_event_partner +#: model:ir.model.fields,field_description:learning_event_partner.field_event_discipline__id +#: model:ir.model.fields,field_description:learning_event_partner.field_event_event__id +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker__id +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker_stage__id +msgid "ID" +msgstr "" + +#. module: learning_event_partner +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker__activity_exception_icon +msgid "Icon" +msgstr "" + +#. module: learning_event_partner +#: model:ir.model.fields,help:learning_event_partner.field_event_speaker__activity_exception_icon +msgid "Icon to indicate an exception activity." +msgstr "" + +#. module: learning_event_partner +#: model:ir.model.fields,help:learning_event_partner.field_event_speaker__message_needaction +#: model:ir.model.fields,help:learning_event_partner.field_event_speaker__message_unread +msgid "If checked, new messages require your attention." +msgstr "" + +#. module: learning_event_partner +#: model:ir.model.fields,help:learning_event_partner.field_event_speaker__message_has_error +msgid "If checked, some messages have a delivery error." +msgstr "" + +#. module: learning_event_partner +#: code:addons/learning_event_partner/models/event_speaker_stage.py:0 +#, python-format +msgid "In Progress" +msgstr "" + +#. module: learning_event_partner +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker__message_is_follower +msgid "Is Follower" +msgstr "" + +#. module: learning_event_partner +#: model:ir.model.fields,field_description:learning_event_partner.field_event_discipline____last_update +#: model:ir.model.fields,field_description:learning_event_partner.field_event_event____last_update +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker____last_update +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker_stage____last_update +msgid "Last Modified on" +msgstr "Dernière modification le" + +#. module: learning_event_partner +#: model:ir.model.fields,field_description:learning_event_partner.field_event_discipline__write_uid +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker__write_uid +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker_stage__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: learning_event_partner +#: model:ir.model.fields,field_description:learning_event_partner.field_event_discipline__write_date +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker__write_date +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker_stage__write_date +msgid "Last Updated on" +msgstr "" + +#. module: learning_event_partner +#: model_terms:ir.ui.view,arch_db:learning_event_partner.event_speaker_view_search +msgid "Late Activities" +msgstr "" + +#. module: learning_event_partner +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker__message_main_attachment_id +msgid "Main Attachment" +msgstr "" + +#. module: learning_event_partner +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker__message_has_error +msgid "Message Delivery error" +msgstr "" + +#. module: learning_event_partner +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker__message_ids +msgid "Messages" +msgstr "" + +#. module: learning_event_partner +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker__my_activity_date_deadline +msgid "My Activity Deadline" +msgstr "" + +#. module: learning_event_partner +#: model:ir.model.fields,field_description:learning_event_partner.field_event_discipline__name +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker__name +msgid "Name" +msgstr "Nom" + +#. module: learning_event_partner +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker__activity_date_deadline +msgid "Next Activity Deadline" +msgstr "" + +#. module: learning_event_partner +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker__activity_summary +msgid "Next Activity Summary" +msgstr "" + +#. module: learning_event_partner +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker__activity_type_id +msgid "Next Activity Type" +msgstr "" + +#. module: learning_event_partner +#: model_terms:ir.actions.act_window,help:learning_event_partner.act_speakers_from_event +msgid "No Speakers Yet !" +msgstr "Aucun intervenant pour le moment !" + +#. module: learning_event_partner +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker__message_needaction_counter +msgid "Number of Actions" +msgstr "" + +#. module: learning_event_partner +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker__message_has_error_counter +msgid "Number of errors" +msgstr "" + +#. module: learning_event_partner +#: model:ir.model.fields,help:learning_event_partner.field_event_speaker__message_needaction_counter +msgid "Number of messages which requires an action" +msgstr "" + +#. module: learning_event_partner +#: model:ir.model.fields,help:learning_event_partner.field_event_speaker__message_has_error_counter +msgid "Number of messages with delivery error" +msgstr "" + +#. module: learning_event_partner +#: model:ir.model.fields,field_description:learning_event_partner.field_event_event__nb_speakers +msgid "Number of speaker" +msgstr "" + +#. module: learning_event_partner +#: model:ir.model.fields,help:learning_event_partner.field_event_speaker__message_unread_counter +msgid "Number of unread messages" +msgstr "" + +#. module: learning_event_partner +#: model:ir.model.fields,help:learning_event_partner.field_event_speaker_stage__legend_blocked +msgid "" +"Override the default value displayed for the blocked state for kanban " +"selection." +msgstr "" + +#. module: learning_event_partner +#: model:ir.model.fields,help:learning_event_partner.field_event_speaker_stage__legend_done +msgid "" +"Override the default value displayed for the done state for kanban " +"selection." +msgstr "" + +#. module: learning_event_partner +#: model:ir.model.fields,help:learning_event_partner.field_event_speaker_stage__legend_normal +msgid "" +"Override the default value displayed for the normal state for kanban " +"selection." +msgstr "" + +#. module: learning_event_partner +#: code:addons/learning_event_partner/models/event_speaker_stage.py:0 +#, python-format +msgid "Ready for Next Stage" +msgstr "" + +#. module: learning_event_partner +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker_stage__legend_blocked +msgid "Red Kanban Label" +msgstr "" + +#. module: learning_event_partner +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker__activity_user_id +msgid "Responsible User" +msgstr "" + +#. module: learning_event_partner +#: model_terms:ir.ui.view,arch_db:learning_event_partner.event_speaker_view_search +msgid "Search Speakers" +msgstr "Rechercher dans les intervenants" + +#. module: learning_event_partner +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker_stage__sequence +msgid "Sequence" +msgstr "" + +#. module: learning_event_partner +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker__employee_id +#: model_terms:ir.ui.view,arch_db:learning_event_partner.event_speaker_view_form +#: model_terms:ir.ui.view,arch_db:learning_event_partner.event_speaker_view_search +msgid "Speaker" +msgstr "Intervenant" + +#. module: learning_event_partner +#: model:ir.actions.act_window,name:learning_event_partner.action_event_discipline +#: model:ir.ui.menu,name:learning_event_partner.menu_event_discipline +msgid "Speaker Discipline" +msgstr "Discipline de l'intervanant" + +#. module: learning_event_partner +#: model:ir.actions.act_window,name:learning_event_partner.action_event_speaker_stage +#: model:ir.model,name:learning_event_partner.model_event_speaker_stage +msgid "Speaker Stage" +msgstr "Status de l'intervenant" + +#. module: learning_event_partner +#: model:ir.ui.menu,name:learning_event_partner.event_speaker_stage_menu +msgid "Speaker Stages" +msgstr "Status des intervenants" + +#. module: learning_event_partner +#: model_terms:ir.ui.view,arch_db:learning_event_partner.view_event_form_inherit_learning_event_partner +msgid "Speaker for this event" +msgstr "Intervenant pour cet évènement." + +#. module: learning_event_partner +#: model:ir.actions.act_window,name:learning_event_partner.act_speakers_from_event +#: model:ir.model.fields,field_description:learning_event_partner.field_event_event__speaker_ids +#: model_terms:ir.ui.view,arch_db:learning_event_partner.event_speaker_view_tree +#: model_terms:ir.ui.view,arch_db:learning_event_partner.view_event_form_inherit_learning_event_partner +msgid "Speakers" +msgstr "Intervenants" + +#. module: learning_event_partner +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker__stage_id +msgid "Stage" +msgstr "Status" + +#. module: learning_event_partner +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker_stage__name +msgid "Stage Name" +msgstr "Nom du status" + +#. module: learning_event_partner +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker_stage__description +msgid "Stage description" +msgstr "Description du status" + +#. module: learning_event_partner +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker__standard_price +msgid "Standard Price" +msgstr "Prix" + +#. module: learning_event_partner +#: model:ir.model.fields,help:learning_event_partner.field_event_speaker__activity_state +msgid "" +"Status based on activities\n" +"Overdue: Due date is already passed\n" +"Today: Activity date is today\n" +"Planned: Future activities." +msgstr "" + +#. module: learning_event_partner +#: model_terms:ir.ui.view,arch_db:learning_event_partner.event_speaker_view_search +msgid "Today Activities" +msgstr "" + +#. module: learning_event_partner +#: model:ir.model.fields,help:learning_event_partner.field_event_speaker__activity_exception_decoration +msgid "Type of the exception activity on record." +msgstr "" + +#. module: learning_event_partner +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker__message_unread +#: model_terms:ir.ui.view,arch_db:learning_event_partner.event_speaker_view_search +msgid "Unread Messages" +msgstr "" + +#. module: learning_event_partner +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker__message_unread_counter +msgid "Unread Messages Counter" +msgstr "" + +#. module: learning_event_partner +#: model:ir.model.fields,field_description:learning_event_partner.field_event_speaker__website_message_ids +msgid "Website Messages" +msgstr "" + +#. module: learning_event_partner +#: model:ir.model.fields,help:learning_event_partner.field_event_speaker__website_message_ids +msgid "Website communication history" +msgstr "" + +#. module: learning_event_partner +#: model:ir.model,name:learning_event_partner.model_event_speaker +msgid "event.speaker" +msgstr "event.speaker" diff --git a/learning_event_partner/models/__init__.py b/learning_event_partner/models/__init__.py new file mode 100644 index 0000000..3144d44 --- /dev/null +++ b/learning_event_partner/models/__init__.py @@ -0,0 +1,3 @@ +from . import event_event +from . import event_speaker +from . import event_speaker_stage diff --git a/learning_event_partner/models/event_event.py b/learning_event_partner/models/event_event.py new file mode 100644 index 0000000..343b9f8 --- /dev/null +++ b/learning_event_partner/models/event_event.py @@ -0,0 +1,33 @@ +# Copyright 2018 Nicolas JEUDY +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +import logging +from odoo import api, fields, models, _ + +_logger = logging.getLogger(__name__) + + +class EventEvent(models.Model): + _inherit = ["event.event"] + + speaker_ids = fields.One2many("event.speaker", "event_id", string="Speakers") + nb_speakers = fields.Integer( + string="Number of speaker", readonly=True, compute="_compute_nb_speakers" + ) + + @api.depends("speaker_ids") + def _compute_nb_speakers(self): + for event in self: + event.nb_speakers = len(event.speaker_ids) + + +class EventDiscipline(models.Model): + _name = "event.discipline" + _description = "Discipline" + + name = fields.Char("Name") + + +class EmployeeDiscipline(models.Model): + _inherit = "hr.employee" + + discipline_ids = fields.Many2many("event.discipline", string="Disciplines") diff --git a/learning_event_partner/models/event_speaker.py b/learning_event_partner/models/event_speaker.py new file mode 100644 index 0000000..3491fc4 --- /dev/null +++ b/learning_event_partner/models/event_speaker.py @@ -0,0 +1,52 @@ +# Copyright 2018 Nicolas JEUDY +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +# import barcode +# from barcode.writer import ImageWriter +import base64 +import logging +from io import BytesIO +import re +import unicodedata + +from odoo import api, fields, models, _ +from odoo.addons import decimal_precision as dp + + +class HrEmployee(models.Model): + _name = "event.speaker" + _inherit = ["mail.thread", "mail.activity.mixin"] + + def _get_default_stage_id(self): + event_stages = self.env["event.speaker.stage"].search([]) + return event_stages[0] if event_stages else False + + name = fields.Char("Name", compute="_compute_name", store="True") + employee_id = fields.Many2one("hr.employee", string="Speaker") + discipline_id = fields.Many2one("event.discipline", string="Discipline") + working_hours = fields.Float("Working hours") + has_extra = fields.Boolean("Has Extra ?") + event_id = fields.Many2one("event.event", string="Event") + stage_id = fields.Many2one( + "event.speaker.stage", + ondelete="restrict", + default=_get_default_stage_id, + group_expand="_read_group_stage_ids", + tracking=True, + ) + discipline_ids = fields.Many2many( + "event.discipline", string="discipline", related="employee_id.discipline_ids" + ) + + @api.model + def _read_group_stage_ids(self, stages, domain, order): + return self.env["event.speaker.stage"].search([]) + + @api.depends("employee_id", "discipline_id") + def _compute_name(self): + for record in self: + name = "" + if record.employee_id: + name += record.employee_id.name + if record.discipline_id: + name += " (%s)" % record.discipline_id.name + record.name = name diff --git a/learning_event_partner/models/event_speaker_stage.py b/learning_event_partner/models/event_speaker_stage.py new file mode 100644 index 0000000..20452fc --- /dev/null +++ b/learning_event_partner/models/event_speaker_stage.py @@ -0,0 +1,38 @@ +from odoo import _, fields, models + + +class EventSpeakerStage(models.Model): + _name = "event.speaker.stage" + _description = "Speaker Stage" + _order = "sequence, name" + + name = fields.Char(string="Stage Name", required=True, translate=True) + description = fields.Text(string="Stage description", translate=True) + sequence = fields.Integer("Sequence", default=1) + fold = fields.Boolean(string="Folded in Kanban", default=False) + pipe_end = fields.Boolean( + string="End Stage", + default=False, + help="Events will automatically be moved into this stage when they are finished. The event moved into this stage will automatically be set as green.", + ) + legend_blocked = fields.Char( + "Red Kanban Label", + default=lambda s: _("Blocked"), + translate=True, + required=True, + help="Override the default value displayed for the blocked state for kanban selection.", + ) + legend_done = fields.Char( + "Green Kanban Label", + default=lambda s: _("Ready for Next Stage"), + translate=True, + required=True, + help="Override the default value displayed for the done state for kanban selection.", + ) + legend_normal = fields.Char( + "Grey Kanban Label", + default=lambda s: _("In Progress"), + translate=True, + required=True, + help="Override the default value displayed for the normal state for kanban selection.", + ) diff --git a/learning_event_partner/security/ir.model.access.csv b/learning_event_partner/security/ir.model.access.csv new file mode 100644 index 0000000..b039112 --- /dev/null +++ b/learning_event_partner/security/ir.model.access.csv @@ -0,0 +1,7 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_event_speaker_user,event.event.speaker.user,learning_event_partner.model_event_speaker,event.group_event_user,1,0,0,0 +access_event_speaker_manager,event.event.speaker.manager,learning_event_partner.model_event_speaker,event.group_event_manager,1,1,1,1 +access_event_discipline_user,event.event.discipline.user,learning_event_partner.model_event_discipline,event.group_event_user,1,0,0,0 +access_event_discipline_manager,event.event.discipline.manager,learning_event_partner.model_event_discipline,event.group_event_manager,1,1,1,1 +access_event_speaker_stage_user,event.event.speaker.stage.user,learning_event_partner.model_event_speaker_stage,event.group_event_user,1,0,0,0 +access_event_speaker_stage_manager,event.event.speaker.stage.manager,learning_event_partner.model_event_speaker_stage,event.group_event_manager,1,1,1,1 \ No newline at end of file diff --git a/learning_event_partner/static/description/icon.png b/learning_event_partner/static/description/icon.png new file mode 100644 index 0000000..d5a5982 Binary files /dev/null and b/learning_event_partner/static/description/icon.png differ diff --git a/learning_event_partner/static/img/learning_1024.png b/learning_event_partner/static/img/learning_1024.png new file mode 100644 index 0000000..7f00293 Binary files /dev/null and b/learning_event_partner/static/img/learning_1024.png differ diff --git a/learning_event_partner/static/img/learning_256.png b/learning_event_partner/static/img/learning_256.png new file mode 100644 index 0000000..d5a5982 Binary files /dev/null and b/learning_event_partner/static/img/learning_256.png differ diff --git a/learning_event_partner/static/img/learning_512.png b/learning_event_partner/static/img/learning_512.png new file mode 100644 index 0000000..1934624 Binary files /dev/null and b/learning_event_partner/static/img/learning_512.png differ diff --git a/learning_event_partner/views/event_event_view.xml b/learning_event_partner/views/event_event_view.xml new file mode 100644 index 0000000..e8dc8ef --- /dev/null +++ b/learning_event_partner/views/event_event_view.xml @@ -0,0 +1,22 @@ + + + + + + event.event.view.form.inherit + event.event + + + + + + + + + \ No newline at end of file diff --git a/learning_event_partner/views/event_menu_view.xml b/learning_event_partner/views/event_menu_view.xml new file mode 100644 index 0000000..9484564 --- /dev/null +++ b/learning_event_partner/views/event_menu_view.xml @@ -0,0 +1,13 @@ + + + + + + + diff --git a/learning_event_partner/views/event_speakers_view.xml b/learning_event_partner/views/event_speakers_view.xml new file mode 100644 index 0000000..b7940a3 --- /dev/null +++ b/learning_event_partner/views/event_speakers_view.xml @@ -0,0 +1,141 @@ + + + + + event.speaker + Speakers + kanban,tree,form + [('event_id', '=', active_id)] + {'default_event_id': active_id} + +

+ No Speakers Yet ! +

+ Add new speakers to this event +

+
+
+ + + event.speaker.view.search + event.speaker + + + + + + + + + + + + + + + + + + + + + + + + event.speaker.kanban + event.speaker + 10 + + + + + + + + + +
+
+
+
+
+ + + By +
+ +
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+ + + event.speaker.view.tree + event.speaker + + + + + + + + + + + + + + event.speaker.view.form + event.speaker + +
+ + + + + + + + + + + +
+ + + +
+
+
+
+ + + Speaker Discipline + event.discipline + + + + + + + + Speaker Stage + event.speaker.stage + + + + + +
+
diff --git a/learning_event_partner/views/hr_employee_views.xml b/learning_event_partner/views/hr_employee_views.xml new file mode 100644 index 0000000..885a1ab --- /dev/null +++ b/learning_event_partner/views/hr_employee_views.xml @@ -0,0 +1,15 @@ + + + + + hr.employee.view.form.discipline.inherit + hr.employee + + + + + + + + +