1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-02-19 13:54:25 +01:00

moving organization & address to invoicingProfile + refactored doc about postgre

This commit is contained in:
Sylvain 2019-05-29 14:28:14 +02:00
parent 142e07f3c9
commit a211ad39ff
21 changed files with 279 additions and 236 deletions

View File

@ -14,9 +14,7 @@ FabManager is the Fab Lab management solution. It provides a comprehensive, web-
4.1. [General Guidelines](#general-guidelines)<br/>
4.2. [Virtual Machine Instructions](#virtual-machine-instructions)
5. [PostgreSQL](#postgresql)<br/>
5.1. [Install PostgreSQL 9.4](#setup-postgresql)<br/>
5.2. [Run the PostgreSQL command line interface](#run-postgresql-cli)<br/>
5.3. [PostgreSQL Limitations](#postgresql-limitations)
5.1. [Install PostgreSQL 9.4](#setup-postgresql)
6. [ElasticSearch](#elasticsearch)<br/>
6.1. [Install ElasticSearch](#setup-elasticsearch)<br/>
6.2. [Rebuild statistics](#rebuild-stats)<br/>
@ -293,55 +291,8 @@ We will use docker to easily install the required version of PostgreSQL.
On MacOS, you'll have to set the host to 127.0.0.1 (or localhost).
See [environment.md](doc/environment.md) for more details.
4. Finally, have a look at the [PostgreSQL Limitations](#postgresql-limitations) section or some errors will occurs preventing you from finishing the installation procedure.
<a name="run-postgresql-cli"></a>
### Run the PostgreSQL command line interface
You may want to access the psql command line tool to check the content of the database, or to run some maintenance routines.
This can be achieved doing the following:
1. Enter into the PostgreSQL container
```bash
docker exec -it fabmanager-postgres bash
```
2. Run the PostgreSQL administration command line interface, logged as the postgres user
```bash
su postgres
psql
```
<a name="postgresql-limitations"></a>
### PostgreSQL Limitations
- While setting up the database, we'll need to activate two PostgreSQL extensions: [unaccent](https://www.postgresql.org/docs/current/static/unaccent.html) and [trigram](https://www.postgresql.org/docs/current/static/pgtrgm.html).
This can only be achieved if the user, configured in `config/database.yml`, was granted the _SUPERUSER_ role **OR** if these extensions were white-listed.
So here's your choices, mainly depending on your security requirements:
- Use the default PostgreSQL super-user (postgres) as the database user. This is the default behavior in fab-manager.
- Set your user as _SUPERUSER_; run the following command in `psql` (after replacing `username` with you user name):
```sql
ALTER USER username WITH SUPERUSER;
```
- Install and configure the PostgreSQL extension [pgextwlist](https://github.com/dimitri/pgextwlist).
Please follow the instructions detailed on the extension website to whitelist `unaccent` and `trigram` for the user configured in `config/database.yml`.
- Some users may want to use another DBMS than PostgreSQL.
This is currently not supported, because of some PostgreSQL specific instructions that cannot be efficiently handled with the ActiveRecord ORM:
- `app/controllers/api/members_controllers.rb@list` is using `ILIKE`
- `app/controllers/api/invoices_controllers.rb@list` is using `ILIKE` and `date_trunc()`
- `db/migrate/20160613093842_create_unaccent_function.rb` is using [unaccent](https://www.postgresql.org/docs/current/static/unaccent.html) and [trigram](https://www.postgresql.org/docs/current/static/pgtrgm.html) modules and defines a PL/pgSQL function (`f_unaccent()`)
- `app/controllers/api/members_controllers.rb@search` is using `f_unaccent()` (see above) and `regexp_replace()`
- `db/migrate/20150604131525_add_meta_data_to_notifications.rb` is using [jsonb](https://www.postgresql.org/docs/9.4/static/datatype-json.html), a PostgreSQL 9.4+ datatype.
- `db/migrate/20160915105234_add_transformation_to_o_auth2_mapping.rb` is using [jsonb](https://www.postgresql.org/docs/9.4/static/datatype-json.html), a PostgreSQL 9.4+ datatype.
- `db/migrate/20181217103441_migrate_settings_value_to_history_values.rb` is using `SELECT DISTINCT ON`.
- `db/migrate/20190107111749_protect_accounting_periods.rb` is using `CREATE RULE` and `DROP RULE`.
- If you intend to contribute to the project code, you will need to run the test suite with `rake test`.
This also requires your user to have the _SUPERUSER_ role.
Please see the [known issues](#known-issues) section for more information about this.
4 . Finally, you may want to have a look at detailed informations about PostgreSQL usage in fab-manager.
Some information about that is available in the [PostgreSQL Readme](doc/postgresql_readme.md).
<a name="elasticsearch"></a>
## ElasticSearch

View File

@ -609,7 +609,8 @@ Application.Controllers.controller('NewAdminController', ['$state', '$scope', 'A
$scope.admin = {
profile_attributes: {
gender: true
}
},
invoicing_profile_attributes: {}
};
// Default parameters for AngularUI-Bootstrap datepicker

View File

@ -96,11 +96,11 @@
<div class="input-group">
<span class="input-group-addon"><i class="fa fa-map-marker"></i> </span>
<input type="hidden"
name="admin[profile_attributes][address_attributes][id]"
ng-value="admin.profile_attributes.address.id" />
<input ng-model="admin.profile_attributes.address_attributes.address"
name="admin[invoicing_profile_attributes][address_attributes][id]"
ng-value="admin.invoicing_profile_attributes.address.id" />
<input ng-model="admin.invoicing_profile_attributes.address_attributes.address"
type="text"
name="admin[profile_attributes][address_attributes][address]"
name="admin[invoicing_profile_attributes][address_attributes][address]"
class="form-control"
id="user_address"
placeholder="{{ 'address' | translate }}">

View File

@ -165,38 +165,38 @@
<span class="help-block" ng-show="userForm['user[password_confirmation]'].$error.match" translate>{{ 'confirmation_mismatch_with_password' }}</span>
</div>
<div class="form-group" ng-if="user.profile.organization" ng-class="{'has-error': userForm['user[profile_attributes][organization_attributes][name]'].$dirty && userForm['user[profile_attributes][organization_attributes][name]'].$invalid}">
<div class="form-group" ng-if="user.profile.organization" ng-class="{'has-error': userForm['user[invoicing_profile_attributes][organization_attributes][name]'].$dirty && userForm['user[invoicing_profile_attributes][organization_attributes][name]'].$invalid}">
<div class="input-group">
<span class="input-group-addon"><i class="fa fa-building-o"></i> <span class="exponent"><i class="fa fa-asterisk" aria-hidden="true"></i></span></span>
<input type="hidden"
name="user[profile_attributes][organization_attributes][id]"
name="user[invoicing_profile_attributes][organization_attributes][id]"
ng-value="user.profile.organization.id" />
<input type="text"
name="user[profile_attributes][organization_attributes][name]"
name="user[invoicing_profile_attributes][organization_attributes][name]"
ng-model="user.profile.organization.name"
class="form-control"
placeholder="{{ 'organization_name' | translate }}"
ng-required="user.profile.organization"
ng-disabled="preventField['profile.organization_name'] && user.profile.organization.name && !userForm['user[profile_attributes][organization_attributes][name]'].$dirty">
ng-disabled="preventField['profile.organization_name'] && user.profile.organization.name && !userForm['user[invoicing_profile_attributes][organization_attributes][name]'].$dirty">
</div>
<span class="help-block" ng-show="userForm['user[profile_attributes][organization_attributes][name]'].$dirty && userForm['user[profile_attributes][organization_attributes][name]'].$error.required" translate>{{ 'organization_name_is_required' }}</span>
<span class="help-block" ng-show="userForm['user[invoicing_][organization_attributes][name]'].$dirty && userForm['user[invoicing_profile_attributes][organization_attributes][name]'].$error.required" translate>{{ 'organization_name_is_required' }}</span>
</div>
<div class="form-group" ng-if="user.profile.organization" ng-class="{'has-error': userForm['user[profile_attributes][organization_attributes][address_attributes][address]'].$dirty && userForm['user[profile_attributes][organization_attributes][address_attributes][address]'].$invalid}">
<div class="form-group" ng-if="user.profile.organization" ng-class="{'has-error': userForm['user[invoicing_profile_attributes][organization_attributes][address_attributes][address]'].$dirty && userForm['user[invoicing_profile_attributes][organization_attributes][address_attributes][address]'].$invalid}">
<div class="input-group">
<span class="input-group-addon"><i class="fa fa-map-marker"></i> <span class="exponent"><i class="fa fa-asterisk" aria-hidden="true"></i></span></span>
<input type="hidden"
name="user[profile_attributes][organization_attributes][address_attributes][id]"
name="user[invoicing_profile_attributes][organization_attributes][address_attributes][id]"
ng-value="user.profile.organization.address.id" />
<input type="text"
name="user[profile_attributes][organization_attributes][address_attributes][address]"
name="user[invoicing_profile_attributes][organization_attributes][address_attributes][address]"
ng-model="user.profile.organization.address.address"
class="form-control"
placeholder="{{ 'organization_address' | translate }}"
ng-required="user.profile.organization"
ng-disabled="preventField['profile.organization_address'] && user.profile.organization.address.address && !userForm['user[profile_attributes][organization_attributes][address_attributes][address]'].$dirty">
ng-disabled="preventField['profile.organization_address'] && user.profile.organization.address.address && !userForm['user[invoicing_profile_attributes][organization_attributes][address_attributes][address]'].$dirty">
</div>
<span class="help-block" ng-show="userForm['user[profile_attributes][organization_attributes][address_attributes][address]'].$dirty && userForm['user[profile_attributes][organization_attributes][address_attributes][address]'].$error.required" translate>{{ 'organization_address_is_required' }}</span>
<span class="help-block" ng-show="userForm['user[invoicing_profile_attributes][organization_attributes][address_attributes][address]'].$dirty && userForm['user[invoicing_profile_attributes][organization_attributes][address_attributes][address]'].$error.required" translate>{{ 'organization_address_is_required' }}</span>
</div>
<div class="form-group" ng-class="{'has-error': userForm['user[profile_attributes][birthday]'].$dirty && userForm['user[profile_attributes][birthday]'].$invalid}">
@ -224,14 +224,14 @@
<div class="input-group">
<span class="input-group-addon"><i class="fa fa-map-marker"></i> </span>
<input type="hidden"
name="user[profile_attributes][address_attributes][id]"
name="user[invoicing_profile_attributes][address_attributes][id]"
ng-value="user.profile.address.id" />
<input type="text"
name="user[profile_attributes][address_attributes][address]"
name="user[invoicing_profile_attributes][address_attributes][address]"
ng-model="user.profile.address.address"
class="form-control"
id="user_address"
ng-disabled="preventField['profile.address'] && user.profile.address.address && !userForm['user[profile_attributes][address_attributes][address]'].$dirty"
ng-disabled="preventField['profile.address'] && user.profile.address.address && !userForm['user[invoicing_profile_attributes][address_attributes][address]'].$dirty"
placeholder="{{ 'address' | translate }}"/>
</div>
</div>

View File

@ -45,7 +45,10 @@ class API::AdminsController < API::ApiController
private
def admin_params
params.require(:admin).permit(:username, :email, profile_attributes: [:first_name, :last_name, :gender,
:birthday, :phone, address_attributes: [:address]])
params.require(:admin).permit(
:username, :email,
profile_attributes: %i[first_name last_name gender birthday phone],
invoicing_profile_attributes: [address_attributes: [:address]]
)
end
end

View File

@ -195,10 +195,11 @@ class API::MembersController < API::ApiController
:software_mastered, :website, :job, :facebook, :twitter,
:google_plus, :viadeo, :linkedin, :instagram, :youtube, :vimeo,
:dailymotion, :github, :echosciences, :pinterest, :lastfm, :flickr,
user_avatar_attributes: %i[id attachment destroy],
address_attributes: %i[id address],
organization_attributes: [:id, :name,
address_attributes: %i[id address]]])
user_avatar_attributes: %i[id attachment destroy]],
invoicing_profile_attributes: [
address_attributes: %i[id address],
organization_attributes: [:id, :name, address_attributes: %i[id address]]
])
elsif current_user.admin?
params.require(:user).permit(:username, :email, :password, :password_confirmation,
@ -208,10 +209,11 @@ class API::MembersController < API::ApiController
:software_mastered, :website, :job, :facebook, :twitter,
:google_plus, :viadeo, :linkedin, :instagram, :youtube, :vimeo,
:dailymotion, :github, :echosciences, :pinterest, :lastfm, :flickr,
user_avatar_attributes: %i[id attachment destroy],
address_attributes: %i[id address],
organization_attributes: [:id, :name,
address_attributes: %i[id address]]])
user_avatar_attributes: %i[id attachment destroy]],
invoicing_profile_attributes: [
address_attributes: %i[id address],
organization_attributes: [:id, :name, address_attributes: %i[id address]]
])
end
end

View File

@ -32,12 +32,12 @@ class ApplicationController < ActionController::Base
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up,
keys: [
{ profile_attributes: [
:phone, :last_name, :first_name, :gender, :birthday,
:interest, :software_mastered, organization_attributes: [
:name, address_attributes: [:address]
{
profile_attributes: %i[phone last_name first_name gender birthday interest software_mastered],
invoicing_profile_attributes: [
organization_attributes: [:name, address_attributes: [:address]]
]
] },
},
:username, :is_allow_contact, :is_allow_newsletter, :cgu, :group_id
])
end

View File

@ -1,7 +1,9 @@
class InvoicingProfile < ActiveRecord::Base
belongs_to :user
has_one :address, as: :placeable, dependent: :destroy
accepts_nested_attributes_for :address, allow_destroy: true
has_one :organization, dependent: :destroy
accepts_nested_attributes_for :organization, allow_destroy: false
has_many :invoices, dependent: :destroy

View File

@ -7,11 +7,6 @@ class Profile < ActiveRecord::Base
accepts_nested_attributes_for :user_avatar,
allow_destroy: true,
reject_if: proc { |attributes| attributes['attachment'].blank? }
has_one :address, as: :placeable, dependent: :destroy
accepts_nested_attributes_for :address, allow_destroy: true
has_one :organization, dependent: :destroy
accepts_nested_attributes_for :organization, allow_destroy: false
validates :first_name, presence: true, length: { maximum: 30 }
validates :last_name, presence: true, length: { maximum: 30 }

View File

@ -22,6 +22,7 @@ class User < ActiveRecord::Base
accepts_nested_attributes_for :profile
has_one :invoicing_profile, dependent: :nullify
accepts_nested_attributes_for :invoicing_profile
has_many :my_projects, foreign_key: :author_id, class_name: 'Project', dependent: :destroy
has_many :project_users, dependent: :destroy

View File

@ -52,23 +52,23 @@ class PDF::Invoice < Prawn::Document
end
# user/organization's information
if invoice&.user&.profile&.organization
name = invoice.user.profile.organization.name
full_name = "#{name} (#{invoice.user.profile.full_name})"
if invoice&.invoicing_profile&.organization
name = invoice.invoicing_profile.organization.name
full_name = "#{name} (#{invoice.invoicing_profile.full_name})"
else
name = invoice.user.profile.full_name
name = invoice.invoicing_profile.full_name
full_name = name
end
address = if invoice&.user&.profile&.organization&.address
invoice.user.profile.organization.address.address
elsif invoice&.user&.profile&.address
invoice.user.profile.address.address
address = if invoice&.invoicing_profile&.organization&.address
invoice.invoicing_profile.organization.address.address
elsif invoice&.invoicing_profile&.address
invoice.invoicing_profile.address.address
else
''
end
text_box "<b>#{name}</b>\n#{invoice.user.email}\n#{address}",
text_box "<b>#{name}</b>\n#{invoice.invoicing_profile.email}\n#{address}",
at: [bounds.width - 130, bounds.top - 49],
width: 130,
align: :right,

View File

@ -6,8 +6,10 @@ json.profile_attributes do
json.gender admin.profile.gender
json.birthday admin.profile.birthday if admin.profile.birthday
json.phone admin.profile.phone
json.user_avatar do
json.id admin.profile.user_avatar.id
json.attachment_url admin.profile.user_avatar.attachment_url
end if admin.profile.user_avatar
if admin.profile.user_avatar
json.user_avatar do
json.id admin.profile.user_avatar.id
json.attachment_url admin.profile.user_avatar.attachment_url
end
end
end

View File

@ -2,53 +2,71 @@ json.extract! member, :id, :username, :email, :group_id
json.role member.roles.first.name
json.name member.profile.full_name
json.need_completion member.need_completion?
json.profile do
json.id member.profile.id
json.user_avatar do
json.id member.profile.user_avatar.id
json.attachment_url member.profile.user_avatar.attachment_url
end if member.profile.user_avatar
if member.profile.user_avatar
json.user_avatar do
json.id member.profile.user_avatar.id
json.attachment_url member.profile.user_avatar.attachment_url
end
end
json.first_name member.profile.first_name
json.last_name member.profile.last_name
json.gender member.profile.gender.to_s
json.birthday member.profile.birthday.to_date.iso8601 if member.profile.birthday
json.interest member.profile.interest
json.software_mastered member.profile.software_mastered
json.address do
json.id member.profile.address.id
json.address member.profile.address.address
end if member.profile.address
json.phone member.profile.phone
json.website member.profile.website
json.job member.profile.job
json.extract! member.profile, :facebook, :twitter, :google_plus, :viadeo, :linkedin, :instagram, :youtube, :vimeo, :dailymotion, :github, :echosciences, :pinterest, :lastfm, :flickr
json.organization do
json.id member.profile.organization.id
json.name member.profile.organization.name
json.address do
json.id member.profile.organization.address.id
json.address member.profile.organization.address.address
end if member.profile.organization.address
end if member.profile.organization
end
json.subscribed_plan do
json.partial! 'api/shared/plan', plan: member.subscribed_plan
end if member.subscribed_plan
json.subscription do
json.id member.subscription.id
json.expired_at member.subscription.expired_at.iso8601
json.canceled_at member.subscription.canceled_at.iso8601 if member.subscription.canceled_at
json.stripe member.subscription.stp_subscription_id.present?
json.plan do
json.id member.subscription.plan.id
json.base_name member.subscription.plan.base_name
json.name member.subscription.plan.name
json.interval member.subscription.plan.interval
json.interval_count member.subscription.plan.interval_count
json.amount member.subscription.plan.amount ? (member.subscription.plan.amount / 100.0) : 0
json.invoicing_profile do
if member.invoicing_profile.address
json.address do
json.id member.invoicing_profile.address.id
json.address member.invoicing_profile.address.address
end
end
end if member.subscription
if member.invoicing_profile.organization
json.organization do
json.id member.invoicing_profile.organization.id
json.name member.invoicing_profile.organization.name
if member.invoicing_profile.organization.address
json.address do
json.id member.invoicing_profile.organization.address.id
json.address member.invoicing_profile.organization.address.address
end
end
end
end
end
if member.subscribed_plan
json.subscribed_plan do
json.partial! 'api/shared/plan', plan: member.subscribed_plan
end
end
if member.subscription
json.subscription do
json.id member.subscription.id
json.expired_at member.subscription.expired_at.iso8601
json.canceled_at member.subscription.canceled_at.iso8601 if member.subscription.canceled_at
json.stripe member.subscription.stp_subscription_id.present?
json.plan do
json.id member.subscription.plan.id
json.base_name member.subscription.plan.base_name
json.name member.subscription.plan.name
json.interval member.subscription.plan.interval
json.interval_count member.subscription.plan.interval_count
json.amount member.subscription.plan.amount ? (member.subscription.plan.amount / 100.0) : 0
end
end
end
json.training_credits member.training_credits do |tc|
json.training_id tc.creditable_id
end

View File

@ -4,6 +4,7 @@ class CreateInvoicingProfiles < ActiveRecord::Migration
t.references :user, index: true, foreign_key: true
t.string :first_name
t.string :last_name
t.string :email
t.timestamps null: false
end

View File

@ -7,12 +7,13 @@ class MigrateProfileToInvoicingProfile < ActiveRecord::Migration
ip = InvoicingProfile.create!(
user: u,
first_name: p.first_name,
last_name: p.last_name
last_name: p.last_name,
email: u.email
)
p.address&.update_attributes(
Address.find_by(placeable_id: p.id, placeable_type: 'Profile')&.update_attributes(
placeable: ip
)
p.organization&.update_attributes(
Organization.find_by(profile_id: p.id)&.update_attributes(
invoicing_profile_id: ip.id
)
end
@ -25,10 +26,10 @@ class MigrateProfileToInvoicingProfile < ActiveRecord::Migration
first_name: ip.first_name,
last_name: ip.last_name
)
ip.address&.update_attributes(
Address.find_by(placeable_id: ip.id, placeable_type: 'InvoicingProfile')&.update_attributes(
placeable: profile
)
ip.organization&.update_attributes(
Organization.find_by(invoicing_profile_id: ip.id)&.update_attributes(
profile_id: profile.id
)
end

View File

@ -12,7 +12,8 @@ class MigrateUserToInvoicingProfile < ActiveRecord::Migration
# migrate invoices
puts 'Migrating invoices. This may take a while...'
Invoice.order(:id).all.each do |i|
i.update_column('invoicing_profile_id', i.user.invoicing_profile.id)
user = User.find(i.user_id)
i.update_column('invoicing_profile_id', user.invoicing_profile.id)
i.update_column('user_id', nil)
end
# chain all records

View File

@ -0,0 +1,5 @@
class RemoveProfileFromOrganization < ActiveRecord::Migration
def change
remove_reference :organizations, :profile, index: true, foreign_key: true
end
end

View File

@ -11,12 +11,12 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20190528140012) do
ActiveRecord::Schema.define(version: 20190529120814) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
enable_extension "unaccent"
enable_extension "pg_trgm"
enable_extension "unaccent"
create_table "abuses", force: :cascade do |t|
t.integer "signaled_id"
@ -44,14 +44,14 @@ ActiveRecord::Schema.define(version: 20190528140012) do
end
create_table "addresses", force: :cascade do |t|
t.string "address"
t.string "street_number"
t.string "route"
t.string "locality"
t.string "country"
t.string "postal_code"
t.string "address", limit: 255
t.string "street_number", limit: 255
t.string "route", limit: 255
t.string "locality", limit: 255
t.string "country", limit: 255
t.string "postal_code", limit: 255
t.integer "placeable_id"
t.string "placeable_type"
t.string "placeable_type", limit: 255
t.datetime "created_at"
t.datetime "updated_at"
end
@ -67,9 +67,9 @@ ActiveRecord::Schema.define(version: 20190528140012) do
create_table "assets", force: :cascade do |t|
t.integer "viewable_id"
t.string "viewable_type"
t.string "attachment"
t.string "type"
t.string "viewable_type", limit: 255
t.string "attachment", limit: 255
t.string "type", limit: 255
t.datetime "created_at"
t.datetime "updated_at"
end
@ -86,12 +86,12 @@ ActiveRecord::Schema.define(version: 20190528140012) do
create_table "availabilities", force: :cascade do |t|
t.datetime "start_at"
t.datetime "end_at"
t.string "available_type"
t.string "available_type", limit: 255
t.datetime "created_at"
t.datetime "updated_at"
t.integer "nb_total_places"
t.boolean "destroying", default: false
t.boolean "lock", default: false
t.boolean "destroying", default: false
t.boolean "lock", default: false
end
create_table "availability_tags", force: :cascade do |t|
@ -105,7 +105,7 @@ ActiveRecord::Schema.define(version: 20190528140012) do
add_index "availability_tags", ["tag_id"], name: "index_availability_tags_on_tag_id", using: :btree
create_table "categories", force: :cascade do |t|
t.string "name"
t.string "name", limit: 255
t.datetime "created_at"
t.datetime "updated_at"
t.string "slug"
@ -114,7 +114,7 @@ ActiveRecord::Schema.define(version: 20190528140012) do
add_index "categories", ["slug"], name: "index_categories_on_slug", unique: true, using: :btree
create_table "components", force: :cascade do |t|
t.string "name", null: false
t.string "name", limit: 255, null: false
end
create_table "coupons", force: :cascade do |t|
@ -132,7 +132,7 @@ ActiveRecord::Schema.define(version: 20190528140012) do
create_table "credits", force: :cascade do |t|
t.integer "creditable_id"
t.string "creditable_type"
t.string "creditable_type", limit: 255
t.integer "plan_id"
t.integer "hours"
t.datetime "created_at"
@ -173,7 +173,7 @@ ActiveRecord::Schema.define(version: 20190528140012) do
add_index "event_themes", ["slug"], name: "index_event_themes_on_slug", unique: true, using: :btree
create_table "events", force: :cascade do |t|
t.string "title"
t.string "title", limit: 255
t.text "description"
t.datetime "created_at"
t.datetime "updated_at"
@ -211,10 +211,10 @@ ActiveRecord::Schema.define(version: 20190528140012) do
add_index "exports", ["user_id"], name: "index_exports_on_user_id", using: :btree
create_table "friendly_id_slugs", force: :cascade do |t|
t.string "slug", null: false
t.integer "sluggable_id", null: false
t.string "slug", limit: 255, null: false
t.integer "sluggable_id", null: false
t.string "sluggable_type", limit: 50
t.string "scope"
t.string "scope", limit: 255
t.datetime "created_at"
end
@ -224,10 +224,10 @@ ActiveRecord::Schema.define(version: 20190528140012) do
add_index "friendly_id_slugs", ["sluggable_type"], name: "index_friendly_id_slugs_on_sluggable_type", using: :btree
create_table "groups", force: :cascade do |t|
t.string "name"
t.string "name", limit: 255
t.datetime "created_at"
t.datetime "updated_at"
t.string "slug"
t.string "slug", limit: 255
t.boolean "disabled"
end
@ -247,7 +247,7 @@ ActiveRecord::Schema.define(version: 20190528140012) do
create_table "invoice_items", force: :cascade do |t|
t.integer "invoice_id"
t.string "stp_invoice_item_id"
t.string "stp_invoice_item_id", limit: 255
t.integer "amount"
t.datetime "created_at"
t.datetime "updated_at"
@ -261,16 +261,16 @@ ActiveRecord::Schema.define(version: 20190528140012) do
create_table "invoices", force: :cascade do |t|
t.integer "invoiced_id"
t.string "invoiced_type"
t.string "stp_invoice_id"
t.string "invoiced_type", limit: 255
t.string "stp_invoice_id", limit: 255
t.integer "total"
t.datetime "created_at"
t.datetime "updated_at"
t.string "reference"
t.string "avoir_mode"
t.string "reference", limit: 255
t.string "avoir_mode", limit: 255
t.datetime "avoir_date"
t.integer "invoice_id"
t.string "type"
t.string "type", limit: 255
t.boolean "subscription_to_expire"
t.text "description"
t.integer "wallet_amount"
@ -291,6 +291,7 @@ ActiveRecord::Schema.define(version: 20190528140012) do
t.integer "user_id"
t.string "first_name"
t.string "last_name"
t.string "email"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
@ -298,17 +299,17 @@ ActiveRecord::Schema.define(version: 20190528140012) do
add_index "invoicing_profiles", ["user_id"], name: "index_invoicing_profiles_on_user_id", using: :btree
create_table "licences", force: :cascade do |t|
t.string "name", null: false
t.string "name", limit: 255, null: false
t.text "description"
end
create_table "machines", force: :cascade do |t|
t.string "name", null: false
t.string "name", limit: 255, null: false
t.text "description"
t.text "spec"
t.datetime "created_at"
t.datetime "updated_at"
t.string "slug"
t.string "slug", limit: 255
t.boolean "disabled"
end
@ -325,14 +326,14 @@ ActiveRecord::Schema.define(version: 20190528140012) do
create_table "notifications", force: :cascade do |t|
t.integer "receiver_id"
t.integer "attached_object_id"
t.string "attached_object_type"
t.string "attached_object_type", limit: 255
t.integer "notification_type_id"
t.boolean "is_read", default: false
t.boolean "is_read", default: false
t.datetime "created_at"
t.datetime "updated_at"
t.string "receiver_type"
t.boolean "is_send", default: false
t.jsonb "meta_data", default: {}
t.boolean "is_send", default: false
t.jsonb "meta_data", default: {}
end
add_index "notifications", ["notification_type_id"], name: "index_notifications_on_notification_type_id", using: :btree
@ -395,28 +396,26 @@ ActiveRecord::Schema.define(version: 20190528140012) do
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "profile_id"
t.integer "invoicing_profile_id"
end
add_index "organizations", ["invoicing_profile_id"], name: "index_organizations_on_invoicing_profile_id", using: :btree
add_index "organizations", ["profile_id"], name: "index_organizations_on_profile_id", using: :btree
create_table "plans", force: :cascade do |t|
t.string "name"
t.string "name", limit: 255
t.integer "amount"
t.string "interval"
t.string "interval", limit: 255
t.integer "group_id"
t.string "stp_plan_id"
t.string "stp_plan_id", limit: 255
t.datetime "created_at"
t.datetime "updated_at"
t.integer "training_credit_nb", default: 0
t.boolean "is_rolling", default: true
t.integer "training_credit_nb", default: 0
t.boolean "is_rolling", default: true
t.text "description"
t.string "type"
t.string "base_name"
t.integer "ui_weight", default: 0
t.integer "interval_count", default: 1
t.integer "ui_weight", default: 0
t.integer "interval_count", default: 1
t.string "slug"
t.boolean "disabled"
end
@ -446,11 +445,11 @@ ActiveRecord::Schema.define(version: 20190528140012) do
create_table "profiles", force: :cascade do |t|
t.integer "user_id"
t.string "first_name"
t.string "last_name"
t.string "first_name", limit: 255
t.string "last_name", limit: 255
t.boolean "gender"
t.date "birthday"
t.string "phone"
t.string "phone", limit: 255
t.text "interest"
t.text "software_mastered"
t.datetime "created_at"
@ -480,7 +479,7 @@ ActiveRecord::Schema.define(version: 20190528140012) do
t.integer "project_id"
t.datetime "created_at"
t.datetime "updated_at"
t.string "title"
t.string "title", limit: 255
t.integer "step_nb"
end
@ -491,27 +490,27 @@ ActiveRecord::Schema.define(version: 20190528140012) do
t.integer "user_id"
t.datetime "created_at"
t.datetime "updated_at"
t.boolean "is_valid", default: false
t.string "valid_token"
t.boolean "is_valid", default: false
t.string "valid_token", limit: 255
end
add_index "project_users", ["project_id"], name: "index_project_users_on_project_id", using: :btree
add_index "project_users", ["user_id"], name: "index_project_users_on_user_id", using: :btree
create_table "projects", force: :cascade do |t|
t.string "name"
t.string "name", limit: 255
t.text "description"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "author_id"
t.text "tags"
t.integer "licence_id"
t.string "state"
t.string "slug"
t.string "state", limit: 255
t.string "slug", limit: 255
t.datetime "published_at"
end
add_index "projects", ["slug"], name: "index_projects_on_slug", unique: true, using: :btree
add_index "projects", ["slug"], name: "index_projects_on_slug", using: :btree
create_table "projects_components", force: :cascade do |t|
t.integer "project_id"
@ -551,19 +550,19 @@ ActiveRecord::Schema.define(version: 20190528140012) do
t.datetime "created_at"
t.datetime "updated_at"
t.integer "reservable_id"
t.string "reservable_type"
t.string "stp_invoice_id"
t.string "reservable_type", limit: 255
t.string "stp_invoice_id", limit: 255
t.integer "nb_reserve_places"
end
add_index "reservations", ["reservable_type", "reservable_id"], name: "index_reservations_on_reservable_type_and_reservable_id", using: :btree
add_index "reservations", ["reservable_id", "reservable_type"], name: "index_reservations_on_reservable_id_and_reservable_type", using: :btree
add_index "reservations", ["stp_invoice_id"], name: "index_reservations_on_stp_invoice_id", using: :btree
add_index "reservations", ["user_id"], name: "index_reservations_on_user_id", using: :btree
create_table "roles", force: :cascade do |t|
t.string "name"
t.string "name", limit: 255
t.integer "resource_id"
t.string "resource_type"
t.string "resource_type", limit: 255
t.datetime "created_at"
t.datetime "updated_at"
end
@ -637,18 +636,18 @@ ActiveRecord::Schema.define(version: 20190528140012) do
create_table "statistic_fields", force: :cascade do |t|
t.integer "statistic_index_id"
t.string "key"
t.string "label"
t.string "key", limit: 255
t.string "label", limit: 255
t.datetime "created_at"
t.datetime "updated_at"
t.string "data_type"
t.string "data_type", limit: 255
end
add_index "statistic_fields", ["statistic_index_id"], name: "index_statistic_fields_on_statistic_index_id", using: :btree
create_table "statistic_graphs", force: :cascade do |t|
t.integer "statistic_index_id"
t.string "chart_type"
t.string "chart_type", limit: 255
t.integer "limit"
t.datetime "created_at"
t.datetime "updated_at"
@ -657,17 +656,17 @@ ActiveRecord::Schema.define(version: 20190528140012) do
add_index "statistic_graphs", ["statistic_index_id"], name: "index_statistic_graphs_on_statistic_index_id", using: :btree
create_table "statistic_indices", force: :cascade do |t|
t.string "es_type_key"
t.string "label"
t.string "es_type_key", limit: 255
t.string "label", limit: 255
t.datetime "created_at"
t.datetime "updated_at"
t.boolean "table", default: true
t.boolean "ca", default: true
t.boolean "table", default: true
t.boolean "ca", default: true
end
create_table "statistic_sub_types", force: :cascade do |t|
t.string "key"
t.string "label"
t.string "key", limit: 255
t.string "label", limit: 255
t.datetime "created_at"
t.datetime "updated_at"
end
@ -684,8 +683,8 @@ ActiveRecord::Schema.define(version: 20190528140012) do
create_table "statistic_types", force: :cascade do |t|
t.integer "statistic_index_id"
t.string "key"
t.string "label"
t.string "key", limit: 255
t.string "label", limit: 255
t.boolean "graph"
t.datetime "created_at"
t.datetime "updated_at"
@ -703,7 +702,7 @@ ActiveRecord::Schema.define(version: 20190528140012) do
create_table "subscriptions", force: :cascade do |t|
t.integer "plan_id"
t.integer "user_id"
t.string "stp_subscription_id"
t.string "stp_subscription_id", limit: 255
t.datetime "created_at"
t.datetime "updated_at"
t.datetime "expiration_date"
@ -722,7 +721,7 @@ ActiveRecord::Schema.define(version: 20190528140012) do
add_index "tags", ["name"], name: "index_tags_on_name", unique: true, using: :btree
create_table "themes", force: :cascade do |t|
t.string "name", null: false
t.string "name", limit: 255, null: false
end
create_table "tickets", force: :cascade do |t|
@ -737,13 +736,13 @@ ActiveRecord::Schema.define(version: 20190528140012) do
add_index "tickets", ["reservation_id"], name: "index_tickets_on_reservation_id", using: :btree
create_table "trainings", force: :cascade do |t|
t.string "name"
t.string "name", limit: 255
t.datetime "created_at"
t.datetime "updated_at"
t.integer "nb_total_places"
t.string "slug"
t.string "slug", limit: 255
t.text "description"
t.boolean "public_page", default: true
t.boolean "public_page", default: true
t.boolean "disabled"
end
@ -799,31 +798,31 @@ ActiveRecord::Schema.define(version: 20190528140012) do
add_index "user_trainings", ["user_id"], name: "index_user_trainings_on_user_id", using: :btree
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.string "email", limit: 255, default: "", null: false
t.string "encrypted_password", limit: 255, default: "", null: false
t.string "reset_password_token", limit: 255
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.string "confirmation_token"
t.string "current_sign_in_ip", limit: 255
t.string "last_sign_in_ip", limit: 255
t.string "confirmation_token", limit: 255
t.datetime "confirmed_at"
t.datetime "confirmation_sent_at"
t.string "unconfirmed_email"
t.integer "failed_attempts", default: 0, null: false
t.string "unlock_token"
t.string "unconfirmed_email", limit: 255
t.integer "failed_attempts", default: 0, null: false
t.string "unlock_token", limit: 255
t.datetime "locked_at"
t.datetime "created_at"
t.datetime "updated_at"
t.boolean "is_allow_contact", default: true
t.boolean "is_allow_contact", default: true
t.integer "group_id"
t.string "stp_customer_id"
t.string "username"
t.string "slug"
t.boolean "is_active", default: true
t.string "stp_customer_id", limit: 255
t.string "username", limit: 255
t.string "slug", limit: 255
t.boolean "is_active", default: true
t.string "provider"
t.string "uid"
t.string "auth_token"
@ -903,7 +902,6 @@ ActiveRecord::Schema.define(version: 20190528140012) do
add_foreign_key "o_auth2_mappings", "o_auth2_providers"
add_foreign_key "open_api_calls_count_tracings", "open_api_clients"
add_foreign_key "organizations", "invoicing_profiles"
add_foreign_key "organizations", "profiles"
add_foreign_key "prices", "groups"
add_foreign_key "prices", "plans"
add_foreign_key "projects_spaces", "projects"

53
doc/postgresql_readme.md Normal file
View File

@ -0,0 +1,53 @@
# Detailed informations about PostgreSQL usage in fab-manager
<a name="run-postgresql-cli"></a>
## Run the PostgreSQL command line interface
You may want to access the psql command line tool to check the content of the database, or to run some maintenance routines.
This can be achieved doing the following:
1. Enter into the PostgreSQL container
```bash
docker exec -it fabmanager-postgres bash
```
2. Run the PostgreSQL administration command line interface, logged as the postgres user
```bash
su postgres
psql
```
<a name="postgresql-limitations"></a>
## PostgreSQL Limitations
- While setting up the database, we'll need to activate two PostgreSQL extensions: [unaccent](https://www.postgresql.org/docs/current/static/unaccent.html) and [trigram](https://www.postgresql.org/docs/current/static/pgtrgm.html).
This can only be achieved if the user, configured in `config/database.yml`, was granted the _SUPERUSER_ role **OR** if these extensions were white-listed.
So here's your choices, mainly depending on your security requirements:
- Use the default PostgreSQL super-user (postgres) as the database user. This is the default behavior in fab-manager.
- Set your user as _SUPERUSER_; run the following command in `psql` (after replacing `username` with you user name):
```sql
ALTER USER username WITH SUPERUSER;
```
- Install and configure the PostgreSQL extension [pgextwlist](https://github.com/dimitri/pgextwlist).
Please follow the instructions detailed on the extension website to whitelist `unaccent` and `trigram` for the user configured in `config/database.yml`.
- If you intend to contribute to the project code, you will need to run the test suite with `rake test`.
This also requires your user to have the _SUPERUSER_ role.
Please see the [known issues](../README.md#known-issues) section for more information about this.
<a name="using-another-dbms"></a>
## Using another DBMS
Some users may want to use another DBMS than PostgreSQL.
This is currently not supported, because of some PostgreSQL specific instructions that cannot be efficiently handled with the ActiveRecord ORM:
- `app/controllers/api/members_controllers.rb@list` is using `ILIKE`
- `app/controllers/api/invoices_controllers.rb@list` is using `ILIKE` and `date_trunc()`
- `db/migrate/20160613093842_create_unaccent_function.rb` is using [unaccent](https://www.postgresql.org/docs/current/static/unaccent.html) and [trigram](https://www.postgresql.org/docs/current/static/pgtrgm.html) modules and defines a PL/pgSQL function (`f_unaccent()`)
- `app/controllers/api/members_controllers.rb@search` is using `f_unaccent()` (see above) and `regexp_replace()`
- `db/migrate/20150604131525_add_meta_data_to_notifications.rb` is using [jsonb](https://www.postgresql.org/docs/9.4/static/datatype-json.html), a PostgreSQL 9.4+ datatype.
- `db/migrate/20160915105234_add_transformation_to_o_auth2_mapping.rb` is using [jsonb](https://www.postgresql.org/docs/9.4/static/datatype-json.html), a PostgreSQL 9.4+ datatype.
- `db/migrate/20181217103441_migrate_settings_value_to_history_values.rb` is using `SELECT DISTINCT ON`.
- `db/migrate/20190107111749_protect_accounting_periods.rb` is using `CREATE RULE` and `DROP RULE`.
- `db/migrate/20190522115230_migrate_user_to_invoicing_profile.rb` is using `CREATE RULE` and `DROP RULE`.

View File

@ -3,39 +3,46 @@ admin:
user_id: 1
first_name: admin
last_name: admin
email: admin@fab-manager.com
jdupont:
id: 2
user_id: 2
first_name: Jean
last_name: Dupont
email: jean.dupond@gmail.com
kdumas:
id: 4
user_id: 4
first_name: Kevin
last_name: Dumas
email: kevin.dumas@orange.fr
vlonchamp:
id: 5
user_id: 5
first_name: Vanessa
last_name: Lonchamp
email: vanessa.lonchamp@sfr.fr
gpartenaire:
id: 6
user_id: 6
first_name: Gilbert
last_name: Partenaire
email: gilbert.partenaire@nicolas.com
pdurand:
id: 3
user_id: 3
first_name: Paulette
last_name: Durand
email: paulette.durand@hotmail.fr
lseguin:
id: 7
user_id: 7
first_name: Lucile
last_name: Seguin
email: lucile.seguin@live.fr

View File

@ -26,6 +26,8 @@ class AdminsTest < ActionDispatch::IntegrationTest
gender: true,
birthday: '1999-09-19',
phone: '0547124852',
},
invoicing_profile_attributes: {
address_attributes: {
address: '6 Avenue Henri de Bournazel, 19000 Tulle'
}