Add initial Odoo integration (billing address)

This commit is contained in:
Timothée Floure 2021-01-20 17:28:48 +01:00
parent 1133025016
commit dcb5f364c2
Signed by: tfloure
GPG key ID: 4502C902C00A1E12
10 changed files with 188 additions and 0 deletions

View file

@ -80,3 +80,10 @@ config :recycledcloud, :ldap,
bind_dn: "cn=admin,dc=example,dc=org", bind_dn: "cn=admin,dc=example,dc=org",
bind_pw: "admin", bind_pw: "admin",
default_group_gid: 1000 default_group_gid: 1000
## Odoo API credentials
config :recycledcloud, :odoo,
server: "https://odoo.staging.e-durable.ch",
database: "e-Durable",
user: "user",
secret: "secret"

View file

@ -16,6 +16,7 @@ defmodule RecycledCloud.Accounts.User do
field :dn, :string, virtual: true field :dn, :string, virtual: true
field :email, :string, virtual: true field :email, :string, virtual: true
field :confirmed_at, :naive_datetime field :confirmed_at, :naive_datetime
field :partner_id, :integer
has_many :keys, Accounts.Key has_many :keys, Accounts.Key

View file

@ -0,0 +1,93 @@
defmodule RecycledCloud.Billing.Partner do
alias RecycledCloud.Accounts.User
alias RecycledCloud.Billing.Partner
alias RecycledCloud.Odoo
alias RecycledCloud.Repo
import Ecto.Changeset
use Ecto.Schema
@address_fields [:street, :city, :zip]
# Note: Odoo stores partner's country under 'country_id'. We do not support
# editing it at the moment.
embedded_schema do
field :name, :string
field :email, :string
field :street, :string
field :city, :string
field :zip, :string
field :country, :string
end
def changeset(%Partner{} = partner, attrs) do
partner |> cast(attrs, @address_fields)
end
def apply(changeset) do
# Check if changeset is valid.
state = changeset |> Ecto.Changeset.apply_action(:update)
case state do
{:ok, partner} ->
changes = partner
|> Map.from_struct
|> Enum.filter(fn {k,_v} -> k in @address_fields end)
|> Enum.map(fn {k, v} -> {to_string(k), v} end)
|> Enum.into(%{})
# Propagate to Odoo
Odoo.query([
"res.partner",
"write",
[[partner.id], changes]
])
{:error, _} -> state
end
end
def get(id) do
result = Odoo.query([
"res.partner",
"search_read",
[[["id", "=", id]]],
%{"fields" => ["id", "name", "email", "country_id"] ++ @address_fields}
])
format_fields = fn p ->
case p do
{k, false} when k in @address_fields -> {k, ""}
{:country_id, false} -> {:country, ""}
{:country_id, [_id, str]} -> {:country, str}
_ -> p
end
end
case result do
{:ok, [raw]} ->
args = raw
|> Enum.map(fn {k, v} -> {String.to_atom(k), v} end)
|> Enum.map(format_fields)
struct(Partner, args)
_ -> nil
end
end
def create(name, email) do
Odoo.query([
"res.partner",
"create",
[%{"name" => name, "email" => email}]
])
end
def get_or_create(%User{} = user) do
case user.partner_id do
nil ->
{:ok, id} = create(user.username, user.email)
{:ok, updated} = user |> change(%{partner_id: id}) |> Repo.update()
get_or_create(updated)
id -> get(id)
end
end
end

View file

@ -0,0 +1,33 @@
defmodule RecycledCloudWeb.BillingController do
use RecycledCloudWeb, :controller
alias RecycledCloud.Billing.Partner
alias RecycledCloud.Repo
def current_partner(conn) do
user = conn.assigns.current_user
Partner.get_or_create(user)
end
def index(conn, _params) do
partner = conn |> current_partner
changeset = partner |> Partner.changeset(%{})
render(conn, "index.html", partner: partner, partner_changeset: changeset)
end
def update(conn, %{"partner" => changes} = params) do
partner = conn |> current_partner
changeset = partner |> Partner.changeset(changes)
case Partner.apply(changeset) do
{:ok, _} ->
conn
|> put_flash(:info, "Billing Partner has been updated")
|> redirect(to: Routes.billing_path(conn, :index))
{:error, changeset} ->
render(conn, "index.html", partner: partner, partner_changeset:
changeset)
end
end
end

View file

@ -55,6 +55,8 @@ defmodule RecycledCloudWeb.Router do
get "/users/settings/confirm_email/:token", UserSettingsController, :confirm_email get "/users/settings/confirm_email/:token", UserSettingsController, :confirm_email
post "/users/settings/keys/new", UserKeysController, :new post "/users/settings/keys/new", UserKeysController, :new
get "/users/settings/keys/:key_id/delete", UserKeysController, :delete get "/users/settings/keys/:key_id/delete", UserKeysController, :delete
get "/billing", BillingController, :index
post "/billing/partner/update", BillingController, :update
end end
scope "/", RecycledCloudWeb do scope "/", RecycledCloudWeb do

View file

@ -0,0 +1,35 @@
<h1>Billing</h1>
<p>You currently are registered as billing entity #<%= @partner.id %>.</p>
<h2>Billing address</h2>
<%= form_for @partner_changeset, Routes.billing_path(@conn, :update), fn f -> %>
<%= if @partner_changeset.action do %>
<div class="alert alert-danger">
<p>Oops, something went wrong! Please check the errors below.</p>
</div>
<% end %>
<%= text_input f, :street, placeholder: "Street" %>
<%= error_tag f, :street %>
<br />
<%= text_input f, :city, placeholder: "City" %>
<%= error_tag f, :city %>
<br />
<%= text_input f, :zip, placeholder: "Zip" %>
<%= error_tag f, :zip %>
<br />
<%= text_input f, :country, placeholder: "Country", readonly: true %>
<%= error_tag f, :country %>
<div>
<%= submit "Change address" %>
</div>
<% end %>

View file

@ -4,6 +4,7 @@
<%= if @current_user do %> <%= if @current_user do %>
<li>Logged in as <%= @current_user.username %></li> <li>Logged in as <%= @current_user.username %></li>
<li><%= link "Settings", to: Routes.user_settings_path(@conn, :edit) %></li> <li><%= link "Settings", to: Routes.user_settings_path(@conn, :edit) %></li>
<li><%= link "Billing", to: Routes.billing_path(@conn, :index) %></li>
<li><%= link "Log out", to: Routes.user_session_path(@conn, :delete), method: :delete %></li> <li><%= link "Log out", to: Routes.user_session_path(@conn, :delete), method: :delete %></li>
<% else %> <% else %>
<li><%= link "Log in", to: Routes.user_session_path(@conn, :new) %></li> <li><%= link "Log in", to: Routes.user_session_path(@conn, :new) %></li>

View file

@ -0,0 +1,3 @@
defmodule RecycledCloudWeb.BillingView do
use RecycledCloudWeb, :view
end

View file

@ -0,0 +1,9 @@
defmodule RecycledCloud.Repo.Migrations.AddUserPartnerId do
use Ecto.Migration
def change do
alter table(:users) do
add :partner_id, :integer
end
end
end

View file

@ -20,6 +20,10 @@ input, textarea {
color: black; color: black;
} }
input[readonly] {
opacity: 0.5;
}
button { button {
color: #000; color: #000;
padding: 5px; padding: 5px;