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.
- checksums.yaml +7 -0
- data/LICENSE-AGPLv3.txt +661 -0
- data/README.md +169 -0
- data/Rakefile +43 -0
- data/app/commands/decidim/odoo/sync_user.rb +57 -0
- data/app/controllers/concerns/decidim/odoo/admin/members/filterable.rb +33 -0
- data/app/controllers/decidim/odoo/admin/members_controller.rb +33 -0
- data/app/forms/decidim/odoo/verifications/odoo_member.rb +53 -0
- data/app/helpers/decidim/odoo/admin/odoo_helper.rb +21 -0
- data/app/jobs/decidim/odoo/auto_verification_job.rb +46 -0
- data/app/jobs/decidim/odoo/omniauth_user_sync_job.rb +24 -0
- data/app/jobs/decidim/odoo/sync_users_job.rb +19 -0
- data/app/models/concerns/decidim/odoo/user_override.rb +21 -0
- data/app/models/decidim/odoo/user.rb +18 -0
- data/app/packs/entrypoints/decidim_odoo.js +2 -0
- data/app/packs/images/odoo_logo.svg +4 -0
- data/app/views/decidim/odoo/admin/members/index.html.erb +58 -0
- data/config/assets.rb +8 -0
- data/config/i18n-tasks.yml +8 -0
- data/config/locales/en.yml +41 -0
- data/db/migrate/20230713143501_create_decidim_odoo_users.rb +20 -0
- data/lib/decidim/odoo/admin.rb +10 -0
- data/lib/decidim/odoo/admin_engine.rb +49 -0
- data/lib/decidim/odoo/api/base/base_query.rb +28 -0
- data/lib/decidim/odoo/api/base/request.rb +48 -0
- data/lib/decidim/odoo/api/find_partner.rb +21 -0
- data/lib/decidim/odoo/api/find_partner_by_vat.rb +21 -0
- data/lib/decidim/odoo/api.rb +25 -0
- data/lib/decidim/odoo/engine.rb +48 -0
- data/lib/decidim/odoo/test/factories.rb +22 -0
- data/lib/decidim/odoo/test/shared_contexts.rb +18 -0
- data/lib/decidim/odoo/version.rb +10 -0
- data/lib/decidim/odoo.rb +42 -0
- data/lib/generators/decidim/odoo/install_generator.rb +47 -0
- data/lib/generators/decidim/odoo/templates/development_odoo_config.rb +21 -0
- data/lib/generators/decidim/odoo/templates/test_odoo_config.rb +20 -0
- data/lib/omniauth/strategies/odoo_keycloak.rb +25 -0
- data/lib/tasks/decidim_odoo.rake +16 -0
- 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
|
+
[](https://codeclimate.com/github/Platoniq/decidim-module-odoo/maintainability)
|
6
|
+
[](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
|
+

|
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,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,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
|