decidim-odoo 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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