decidim-odoo 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE-AGPLv3.txt +661 -0
  3. data/README.md +169 -0
  4. data/Rakefile +43 -0
  5. data/app/commands/decidim/odoo/sync_user.rb +57 -0
  6. data/app/controllers/concerns/decidim/odoo/admin/members/filterable.rb +33 -0
  7. data/app/controllers/decidim/odoo/admin/members_controller.rb +33 -0
  8. data/app/forms/decidim/odoo/verifications/odoo_member.rb +53 -0
  9. data/app/helpers/decidim/odoo/admin/odoo_helper.rb +21 -0
  10. data/app/jobs/decidim/odoo/auto_verification_job.rb +46 -0
  11. data/app/jobs/decidim/odoo/omniauth_user_sync_job.rb +24 -0
  12. data/app/jobs/decidim/odoo/sync_users_job.rb +19 -0
  13. data/app/models/concerns/decidim/odoo/user_override.rb +21 -0
  14. data/app/models/decidim/odoo/user.rb +18 -0
  15. data/app/packs/entrypoints/decidim_odoo.js +2 -0
  16. data/app/packs/images/odoo_logo.svg +4 -0
  17. data/app/views/decidim/odoo/admin/members/index.html.erb +58 -0
  18. data/config/assets.rb +8 -0
  19. data/config/i18n-tasks.yml +8 -0
  20. data/config/locales/en.yml +41 -0
  21. data/db/migrate/20230713143501_create_decidim_odoo_users.rb +20 -0
  22. data/lib/decidim/odoo/admin.rb +10 -0
  23. data/lib/decidim/odoo/admin_engine.rb +49 -0
  24. data/lib/decidim/odoo/api/base/base_query.rb +28 -0
  25. data/lib/decidim/odoo/api/base/request.rb +48 -0
  26. data/lib/decidim/odoo/api/find_partner.rb +21 -0
  27. data/lib/decidim/odoo/api/find_partner_by_vat.rb +21 -0
  28. data/lib/decidim/odoo/api.rb +25 -0
  29. data/lib/decidim/odoo/engine.rb +48 -0
  30. data/lib/decidim/odoo/test/factories.rb +22 -0
  31. data/lib/decidim/odoo/test/shared_contexts.rb +18 -0
  32. data/lib/decidim/odoo/version.rb +10 -0
  33. data/lib/decidim/odoo.rb +42 -0
  34. data/lib/generators/decidim/odoo/install_generator.rb +47 -0
  35. data/lib/generators/decidim/odoo/templates/development_odoo_config.rb +21 -0
  36. data/lib/generators/decidim/odoo/templates/test_odoo_config.rb +20 -0
  37. data/lib/omniauth/strategies/odoo_keycloak.rb +25 -0
  38. data/lib/tasks/decidim_odoo.rake +16 -0
  39. metadata +135 -0
data/README.md ADDED
@@ -0,0 +1,169 @@
1
+ # Decidim::Odoo
2
+
3
+ [![[CI] Lint](https://github.com/Platoniq/decidim-module-odoo/actions/workflows/lint.yml/badge.svg)](https://github.com/Platoniq/decidim-module-odoo/actions/workflows/lint.yml)
4
+ [![[CI] Test](https://github.com/Platoniq/decidim-module-odoo/actions/workflows/test.yml/badge.svg)](https://github.com/Platoniq/decidim-module-odoo/actions/workflows/test.yml)
5
+ [![Maintainability](https://api.codeclimate.com/v1/badges/2dada53525dd5a944089/maintainability)](https://codeclimate.com/github/Platoniq/decidim-module-odoo/maintainability)
6
+ [![codecov](https://codecov.io/gh/Platoniq/decidim-module-odoo/branch/main/graph/badge.svg)](https://codecov.io/gh/Platoniq/decidim-module-odoo)
7
+
8
+ A Decidim module to sync Odoo users who connect to the platform using Keycloak OpenID OAuth.
9
+
10
+ This module allows the user to sign up in Decidim using the Odoo data. The process is described below:
11
+
12
+ ![Sequence Diagram](examples/sequence-diagram.png)
13
+
14
+ When a user logs in to the system using Odoo Keycloak OAuth, the information of the user provided
15
+ by the Odoo API is stored in a model.
16
+
17
+ With this information, we can check if the user is a member or not depending on the values of the
18
+ properties `member` and `coop_candidate`. If any of them is true, we will determine whether the user
19
+ is a member. Taking this into account, every time we update the Odoo information of the user, we
20
+ check this condition to create an authorization: `odoo_member` or delete it if the user is
21
+ no longer a member.
22
+
23
+ The Odoo information of a user is updated automatically the first time the user signs up in the
24
+ system via OAuth, but it can be manually updated as described below:
25
+
26
+ - By an admin in the `/admin/odoo/members` route syncing a single user or all the users in the
27
+ system
28
+ - Using the `rake` task: `decidim:odoo:sync:members`. It will update all the users from all the
29
+ organizations, so we recommend using it when the traffic in the platform is low. You can easily
30
+ schedule it using [whenever](https://github.com/javan/whenever) adding the lines:
31
+
32
+ ```ruby
33
+ every :day, at: "2:00am" do
34
+ rake "decidim:odoo:sync:members"
35
+ end
36
+ ```
37
+
38
+ ## Installation
39
+
40
+ Add this line to your application's Gemfile:
41
+
42
+ ```ruby
43
+ gem "decidim-odoo", git: "https://github.com/Platoniq/decidim-module-odoo", branch: "main"
44
+ ```
45
+
46
+ And then execute:
47
+
48
+ ```bash
49
+ bundle
50
+ ```
51
+
52
+ Install (and run) migrations:
53
+
54
+ ```
55
+ bundle exec rails decidim_odoo:install:migrations
56
+ bundle exec rails db:migrate
57
+ ```
58
+
59
+ ## Configuration
60
+
61
+ In order to make the Odoo OAuth method with Keycloak available you need to add to your
62
+ `config/secrets.yml` the entry below:
63
+
64
+ ```yaml
65
+ omniauth:
66
+ odoo_keycloak:
67
+ enabled: true
68
+ icon_path: media/images/odoo_logo.svg
69
+ ```
70
+
71
+ The rest of the configuration can be done with an initializer file as the ones in
72
+ [this directory](lib/generators/decidim/odoo/templates) or with environment variables:
73
+
74
+ | ENV | Description | Example |
75
+ |--------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------|
76
+ | OMNIAUTH_ODOO_KEYCLOAK_CLIENT_ID | The Keycloak client ID | `your-client-id` |
77
+ | OMNIAUTH_ODOO_KEYCLOAK_CLIENT_SECRET | The Keycloak client secret | `your-client-secret` |
78
+ | OMNIAUTH_ODOO_KEYCLOAK_SITE | The Keycloak site | `https://example.org/oauth` |
79
+ | OMNIAUTH_ODOO_KEYCLOAK_REALM | The Keycloak realm | `example-realm` |
80
+ | OMNIAUTH_ODOO_KEYCLOAK_ICON_PATH | **Optional**. The icon path for the "Sign in with Odoo" button. In order to replace the default one, you need to include it under `app/packs/images` directory and reference it here as `media/images/your-icon.svg` | `media/images/odoo_logo.svg` |
81
+ | ODOO_API_BASE_URL | The base URL for the Odoo API | `https://example.org/api` |
82
+ | ODOO_API_API_KEY | The API key to authenticate with the API | `your-api-key` |
83
+
84
+ > **IMPORTANT**: Remember to activate the verification method `odoo_member` in the
85
+ > Decidim `/system` admin page for your organization.
86
+
87
+ ## Contributing
88
+
89
+ See [Decidim](https://github.com/decidim/decidim).
90
+
91
+ ### Developing
92
+
93
+ To start contributing to this project, first:
94
+
95
+ - Install the basic dependencies (such as Ruby and PostgreSQL)
96
+ - Clone this repository
97
+
98
+ Decidim's main repository also provides a Docker configuration file if you
99
+ prefer to use Docker instead of installing the dependencies locally on your
100
+ machine.
101
+
102
+ You can create the development app by running the following commands after
103
+ cloning this project:
104
+
105
+ ```bash
106
+ $ bundle
107
+ $ DATABASE_USERNAME=<username> DATABASE_PASSWORD=<password> bundle exec rake development_app
108
+ ```
109
+
110
+ Note that the database user has to have rights to create and drop a database in
111
+ order to create the dummy test app database.
112
+
113
+ Then to test how the module works in Decidim, start the development server:
114
+
115
+ ```bash
116
+ $ cd development_app
117
+ $ DATABASE_USERNAME=<username> DATABASE_PASSWORD=<password> bundle exec rails s
118
+ ```
119
+
120
+ In case you are using [rbenv](https://github.com/rbenv/rbenv) and have the
121
+ [rbenv-vars](https://github.com/rbenv/rbenv-vars) plugin installed for it, you
122
+ can add the environment variables to the root directory of the project in a file
123
+ named `.rbenv-vars`. If these are defined for the environment, you can omit
124
+ defining these in the commands shown above.
125
+
126
+ #### Code Styling
127
+
128
+ Please follow the code styling defined by the different linters that ensure we
129
+ are all talking with the same language collaborating on the same project. This
130
+ project is set to follow the same rules that Decidim itself follows.
131
+
132
+ [Rubocop](https://rubocop.readthedocs.io/) linter is used for the Ruby language.
133
+
134
+ You can run the code styling checks by running the following commands from the
135
+ console:
136
+
137
+ ```
138
+ $ bundle exec rubocop
139
+ ```
140
+
141
+ To ease up following the style guide, you should install the plugin to your
142
+ favorite editor, such as:
143
+
144
+ - Atom - [linter-rubocop](https://atom.io/packages/linter-rubocop)
145
+ - Sublime Text - [Sublime RuboCop](https://github.com/pderichs/sublime_rubocop)
146
+ - Visual Studio Code - [Rubocop for Visual Studio Code](https://github.com/misogi/vscode-ruby-rubocop)
147
+
148
+ ### Testing
149
+
150
+ To run the tests run the following in the gem development path:
151
+
152
+ ```bash
153
+ $ bundle
154
+ $ DATABASE_USERNAME=<username> DATABASE_PASSWORD=<password> bundle exec rake test_app
155
+ $ DATABASE_USERNAME=<username> DATABASE_PASSWORD=<password> bundle exec rspec
156
+ ```
157
+
158
+ Note that the database user has to have rights to create and drop a database in
159
+ order to create the dummy test app database.
160
+
161
+ In case you are using [rbenv](https://github.com/rbenv/rbenv) and have the
162
+ [rbenv-vars](https://github.com/rbenv/rbenv-vars) plugin installed for it, you
163
+ can add these environment variables to the root directory of the project in a
164
+ file named `.rbenv-vars`. In this case, you can omit defining these in the
165
+ commands shown above.
166
+
167
+ ## License
168
+
169
+ This engine is distributed under the [GNU AFFERO GENERAL PUBLIC LICENSE](LICENSE-AGPLv3.txt).
data/Rakefile ADDED
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "decidim/dev/common_rake"
4
+ require "generators/decidim/odoo/install_generator"
5
+
6
+ def install_module(path)
7
+ Dir.chdir(path) do
8
+ system("bundle exec rake decidim_odoo:install:migrations")
9
+ system("bundle exec rake db:migrate")
10
+ end
11
+ end
12
+
13
+ def seed_db(path)
14
+ Dir.chdir(path) do
15
+ system("bundle exec rake db:seed")
16
+ end
17
+ end
18
+
19
+ desc "Generates a dummy app for testing"
20
+ task test_app: "decidim:generate_external_test_app" do
21
+ ENV["RAILS_ENV"] = "test"
22
+ Decidim::Odoo::Generators::InstallGenerator.start
23
+ install_module("spec/decidim_dummy_app")
24
+ end
25
+
26
+ desc "Generates a development app."
27
+ task :development_app do
28
+ Bundler.with_original_env do
29
+ generate_decidim_app(
30
+ "development_app",
31
+ "--app_name",
32
+ "#{base_app_name}_development_app",
33
+ "--path",
34
+ "..",
35
+ "--recreate_db",
36
+ "--demo"
37
+ )
38
+ end
39
+
40
+ Decidim::Odoo::Generators::InstallGenerator.start
41
+ install_module("development_app")
42
+ seed_db("development_app")
43
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Odoo
5
+ class SyncUser < Rectify::Command
6
+ # Public: Initializes the command.
7
+ #
8
+ # user - A decidim user
9
+ def initialize(user)
10
+ @user = user
11
+ end
12
+
13
+ # Executes the command. Broadcasts these events:
14
+ #
15
+ # - :ok when everything is valid.
16
+ # - :invalid if we couldn't proceed.
17
+ #
18
+ # Returns nothing.
19
+ def call
20
+ update_user!
21
+ create_user!
22
+
23
+ ActiveSupport::Notifications.publish("decidim.odoo.user.updated", odoo_user.id)
24
+
25
+ broadcast(:ok, odoo_user)
26
+ rescue StandardError => e
27
+ broadcast(:invalid, e.message)
28
+ end
29
+
30
+ private
31
+
32
+ attr_reader :user, :odoo_user
33
+
34
+ def create_user!
35
+ @odoo_user = User.find_or_create_by(user: user, organization: user.organization)
36
+ @odoo_user.odoo_user_id = odoo_info[:id]
37
+ @odoo_user.ref = odoo_info[:ref]
38
+ @odoo_user.coop_candidate = odoo_info[:coop_candidate]
39
+ @odoo_user.member = odoo_info[:member]
40
+ @odoo_user.save!
41
+ # rubocop:disable Rails/SkipsModelValidations
42
+ @odoo_user.touch
43
+ # rubocop:enable Rails/SkipsModelValidations
44
+ end
45
+
46
+ def update_user!
47
+ user.nickname = odoo_info[:vat] if odoo_info[:vat]
48
+ user.name = odoo_info[:name] if odoo_info[:name]
49
+ user.save!
50
+ end
51
+
52
+ def odoo_info
53
+ @odoo_info ||= ::Decidim::Odoo::Api::FindPartner.new(user.odoo_identity.uid).result
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
4
+
5
+ module Decidim
6
+ module Odoo
7
+ module Admin
8
+ module Members
9
+ module Filterable
10
+ extend ActiveSupport::Concern
11
+
12
+ included do
13
+ include Decidim::Admin::Filterable
14
+
15
+ private
16
+
17
+ def base_query
18
+ collection
19
+ end
20
+
21
+ def search_field_predicate
22
+ :user_name_or_user_nickname_or_user_email_cont
23
+ end
24
+
25
+ def filters
26
+ [:member_eq, :coop_candidate_eq]
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Odoo
5
+ module Admin
6
+ class MembersController < Decidim::Admin::ApplicationController
7
+ include Decidim::Paginable
8
+ include Decidim::Odoo::Admin::Members::Filterable
9
+
10
+ helper Decidim::HumanizeBooleansHelper
11
+ helper Decidim::Messaging::ConversationHelper
12
+ helper Decidim::Odoo::Admin::OdooHelper
13
+
14
+ def index
15
+ enforce_permission_to :read, :officialization
16
+ @odoo_users = filtered_collection
17
+ end
18
+
19
+ def sync
20
+ Decidim::Odoo::SyncUsersJob.perform_later(current_organization.id, params[:id])
21
+ flash[:notice] = t("success", scope: "decidim.odoo.admin.members.sync")
22
+ redirect_to decidim_odoo_admin.members_path
23
+ end
24
+
25
+ private
26
+
27
+ def collection
28
+ @collection ||= Decidim::Odoo::User.where(organization: current_organization).left_outer_joins(:user).order(updated_at: :desc)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "digest"
4
+
5
+ module Decidim
6
+ module Odoo
7
+ module Verifications
8
+ class OdooMember < Decidim::AuthorizationHandler
9
+ validate :user_valid
10
+
11
+ def metadata
12
+ super.merge(
13
+ uid: uid,
14
+ odoo_user_ref: odoo_user&.ref
15
+ )
16
+ end
17
+
18
+ def unique_id
19
+ Digest::SHA512.hexdigest("#{uid}-#{Rails.application.secrets.secret_key_base}")
20
+ end
21
+
22
+ protected
23
+
24
+ def organization
25
+ current_organization || user&.organization
26
+ end
27
+
28
+ def uid
29
+ odoo_user&.odoo_user_id
30
+ end
31
+
32
+ def odoo_user
33
+ @odoo_user ||= Decidim::Odoo::User.find_by(user: user)
34
+ end
35
+
36
+ def odoo_api_user
37
+ @odoo_api_user ||= begin
38
+ Decidim::Odoo::Api::FindPartner.new(uid).result
39
+ rescue Decidim::Odoo::Error => _e
40
+ nil
41
+ end
42
+ end
43
+
44
+ def user_valid
45
+ errors.add(:user, "decidim.odoo.errors.not_found") if odoo_user.blank? && odoo_api_user.blank?
46
+ if (odoo_user && !odoo_user.odoo_member?) || (odoo_api_user && !odoo_api_user[:member] && !odoo_api_user[:coop_candidate])
47
+ errors.add(:user, "decidim.odoo.errors.not_member")
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Odoo
5
+ module Admin
6
+ module OdooHelper
7
+ def boolean_class(boolean)
8
+ boolean ? "success" : "alert"
9
+ end
10
+
11
+ def last_sync_class(datetime)
12
+ return unless datetime
13
+ return "alert" if datetime < 1.week.ago
14
+ return "warning" if datetime < 1.day.ago
15
+
16
+ "success"
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Odoo
5
+ class AutoVerificationJob < ApplicationJob
6
+ queue_as :default
7
+
8
+ def perform(odoo_user_id)
9
+ @odoo_user = Decidim::Odoo::User.find(odoo_user_id)
10
+ @odoo_user.odoo_member? ? create_auth : remove_auth
11
+ rescue ActiveRecord::RecordNotFound => _e
12
+ Rails.logger.error "AutoVerificationJob: ERROR: model not found for odoo user #{odoo_user_id}"
13
+ end
14
+
15
+ private
16
+
17
+ def create_auth
18
+ return unless (handler = Decidim::AuthorizationHandler.handler_for("odoo_member", user: @odoo_user.user))
19
+
20
+ Decidim::Verifications::AuthorizeUser.call(handler, @odoo_user.organization) do
21
+ on(:ok) do
22
+ Rails.logger.info "AutoVerificationJob: Success: created for user #{handler.user.id}"
23
+ end
24
+
25
+ on(:invalid) do
26
+ Rails.logger.error "AutoVerificationJob: ERROR: not created for user #{handler.user&.id}"
27
+ end
28
+ end
29
+ end
30
+
31
+ def remove_auth
32
+ Decidim::Authorization.where(user: @odoo_user.user, name: "odoo_member").each do |auth|
33
+ Decidim::Verifications::DestroyUserAuthorization.call(auth) do
34
+ on(:ok) do
35
+ Rails.logger.info "AutoVerificationJob: Success: removed for user #{auth.user.id}"
36
+ end
37
+
38
+ on(:invalid) do
39
+ Rails.logger.error "AutoVerificationJob: ERROR: not removed for user #{auth.user&.id}"
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Odoo
5
+ class OmniauthUserSyncJob < ApplicationJob
6
+ queue_as :default
7
+
8
+ def perform(data)
9
+ user = Decidim::User.find(data[:user_id])
10
+ return unless user.odoo_identity?
11
+
12
+ Decidim::Odoo::SyncUser.call(user) do
13
+ on(:ok) do |odoo_user|
14
+ Rails.logger.info "OmniauthUserSyncJob: Success: Odoo user #{odoo_user.id} created for user #{odoo_user.user&.id}"
15
+ end
16
+
17
+ on(:invalid) do |message|
18
+ Rails.logger.error "OmniauthUserSyncJob: ERROR: Odoo user creation error #{message}"
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Odoo
5
+ class SyncUsersJob < ApplicationJob
6
+ queue_as :default
7
+
8
+ def perform(organization_id, odoo_user_id = nil)
9
+ odoo_users = Decidim::Odoo::User.where(decidim_organization_id: organization_id)
10
+ odoo_users = odoo_users.where(id: odoo_user_id) if odoo_user_id
11
+ Rails.logger.warn "SyncUsersJob: WARN: No results found for: organization_id='#{organization_id}' and odoo_user_id='#{odoo_user_id}'" if odoo_users.empty?
12
+
13
+ odoo_users.each do |odoo_user|
14
+ Decidim::Odoo::OmniauthUserSyncJob.perform_later(user_id: odoo_user.user.id)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Odoo
5
+ module UserOverride
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ has_one :odoo_user, class_name: "Decidim::Odoo::User", foreign_key: "decidim_user_id", dependent: :destroy
10
+
11
+ def odoo_identity
12
+ identities.find_by(provider: Decidim::Odoo::OMNIAUTH_PROVIDER_NAME)
13
+ end
14
+
15
+ def odoo_identity?
16
+ identities.exists?(provider: Decidim::Odoo::OMNIAUTH_PROVIDER_NAME)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Odoo
5
+ class User < ApplicationRecord
6
+ belongs_to :organization, class_name: "Decidim::Organization", foreign_key: "decidim_organization_id"
7
+ belongs_to :user, class_name: "Decidim::User", foreign_key: "decidim_user_id"
8
+
9
+ validates :user, uniqueness: true
10
+ validates :odoo_user_id, uniqueness: { scope: :organization }
11
+ validates :ref, uniqueness: { scope: :organization }
12
+
13
+ def odoo_member?
14
+ member || coop_candidate
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,2 @@
1
+ // Images
2
+ require.context("../images", true)
@@ -0,0 +1,4 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16">
3
+ <circle stroke="#FFF" fill-opacity="0" cx="8" cy="8" r="3.84" stroke-width="1.92"/>
4
+ </svg>
@@ -0,0 +1,58 @@
1
+ <div class="card">
2
+ <div class="card-divider">
3
+ <h2 class="card-title">
4
+ <%= t(".title") %>
5
+ <%= link_to t(".sync_all"), decidim_odoo_admin.sync_members_path, class: "button tiny button--title" %>
6
+ </h2>
7
+ </div>
8
+ <%= admin_filter_selector %>
9
+ <div class="card-section">
10
+ <% if @odoo_users.any? %>
11
+ <table class="table-list odoo-groups">
12
+ <thead>
13
+ <tr>
14
+ <th><%= t(".odoo_id") %></th>
15
+ <th><%= t(".ref") %></th>
16
+ <th><%= t(".name") %></th>
17
+ <th><%= t(".alias") %></th>
18
+ <th><%= t(".coop_candidate") %></th>
19
+ <th><%= t(".member") %></th>
20
+ <th><%= t(".last_sync") %></th>
21
+ <th><%= t(".actions") %></th>
22
+ </tr>
23
+ </thead>
24
+ <tbody>
25
+ <% @odoo_users.each do |odoo_user| %>
26
+ <tr>
27
+ <td><%= odoo_user.odoo_user_id %></td>
28
+ <td><%= odoo_user.ref %></td>
29
+ <% if odoo_user.user.nickname.present? %>
30
+ <td><%= link_to odoo_user.user.name, decidim.profile_path(odoo_user.user.nickname) %></td>
31
+ <td><%= link_to odoo_user.user.nickname, decidim.profile_path(odoo_user.user.nickname) %></td>
32
+ <% else %>
33
+ <td><%= odoo_user.user.name %></td>
34
+ <td><%= odoo_user.user.nickname %></td>
35
+ <% end %>
36
+ <td class="<%= boolean_class(odoo_user.coop_candidate) %>"><%= content_tag :strong, humanize_boolean(odoo_user.coop_candidate) %></td>
37
+ <td class="<%= boolean_class(odoo_user.member) %>"><%= content_tag :strong, humanize_boolean(odoo_user.member) %></td>
38
+ <td class="<%= last_sync_class(odoo_user.updated_at) %>"><%= l(odoo_user.updated_at, format: :decidim_short) %></td>
39
+ <td class="table-list__actions">
40
+ <%= icon_link_to "reload", decidim_odoo_admin.sync_members_path(id: odoo_user.id), t(".sync"), class: "action-icon--reload" %>
41
+ <% if allowed_to? :show_email, :user, user: odoo_user.user %>
42
+ <%= icon_link_to "envelope-open", decidim_admin.show_email_officialization_path(user_id: odoo_user.user.id), t(".show_email"), class: "action-icon action-icon--show-email", data: { full_name: odoo_user.user.name, toggle: "show-email-modal" } %>
43
+ <% end %>
44
+ <%= icon_link_to "envelope-closed", current_or_new_conversation_path_with(odoo_user.user), t(".contact"), class:"action-icon--new" %>
45
+ </td>
46
+ </tr>
47
+ <% end %>
48
+ </tbody>
49
+ </table>
50
+ <%= paginate @odoo_users, theme: "decidim" %>
51
+ <% else %>
52
+ <p class="callout warning">
53
+ <%= t(".empty") %>
54
+ </p>
55
+ <% end %>
56
+ </div>
57
+ </div>
58
+ <%= render partial: "decidim/admin/officializations/show_email_modal" %>
data/config/assets.rb ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ base_path = File.expand_path("..", __dir__)
4
+
5
+ Decidim::Webpacker.register_path("#{base_path}/app/packs")
6
+ Decidim::Webpacker.register_entrypoints(
7
+ decidim_oddo: "#{base_path}/app/packs/entrypoints/decidim_odoo.js"
8
+ )
@@ -0,0 +1,8 @@
1
+ ---
2
+
3
+ base_locale: en
4
+ locales: [en]
5
+ ignore_unused:
6
+ - decidim.admin.filters.*
7
+ - decidim.authorization_handlers.odoo_member.*
8
+ - decidim.odoo.errors.*
@@ -0,0 +1,41 @@
1
+ ---
2
+ en:
3
+ decidim:
4
+ admin:
5
+ filters:
6
+ coop_candidate_eq:
7
+ label: Is coop candidate
8
+ values:
9
+ 'false': 'No'
10
+ 'true': 'Yes'
11
+ member_eq:
12
+ label: Is member
13
+ values:
14
+ 'false': 'No'
15
+ 'true': 'Yes'
16
+ authorization_handlers:
17
+ odoo_member:
18
+ explanation: Get verified when the user is member or coop. candidate in Odoo
19
+ name: Odoo Membership
20
+ odoo:
21
+ admin:
22
+ members:
23
+ index:
24
+ actions: Actions
25
+ alias: Alias
26
+ contact: Contact
27
+ coop_candidate: Coop candidate
28
+ empty: There are no members from Odoo
29
+ last_sync: Last synchronization
30
+ member: Member
31
+ name: Name
32
+ odoo_id: Odoo ID
33
+ ref: Ref
34
+ show_email: Show email
35
+ sync: Synchronize with Odoo
36
+ sync_all: Synchronize all with Odoo
37
+ title: Odoo Members
38
+ sync:
39
+ success: The synchronization has started. It may take a few minutes
40
+ errors:
41
+ not_found: Odoo user not found