bookingsync_portal 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +174 -0
  4. data/Rakefile +28 -0
  5. data/app/assets/config/manifest.js +3 -0
  6. data/app/assets/images/bookingsync_portal/bookingsync.png +0 -0
  7. data/app/assets/images/bookingsync_portal/help/connect.gif +0 -0
  8. data/app/assets/javascripts/bookingsync_portal/admin/application.js.coffee +83 -0
  9. data/app/assets/javascripts/bookingsync_portal/admin/connections.js.coffee +3 -0
  10. data/app/assets/javascripts/bookingsync_portal/admin/handle_remote_errors.js.coffee +3 -0
  11. data/app/assets/javascripts/bookingsync_portal/admin/lib/list-filter.js.coffee +28 -0
  12. data/app/assets/javascripts/bookingsync_portal/admin/live_updates.js.coffee +7 -0
  13. data/app/assets/javascripts/bookingsync_portal/admin/rentals.js.coffee +12 -0
  14. data/app/assets/javascripts/bookingsync_portal/admin/templates/filter_input.hbs +6 -0
  15. data/app/assets/javascripts/bookingsync_portal/admin/vendor/css-contains.js +29 -0
  16. data/app/assets/stylesheets/bookingsync_portal/admin/application.scss +241 -0
  17. data/app/controllers/bookingsync_portal/admin/base_controller.rb +25 -0
  18. data/app/controllers/bookingsync_portal/admin/connections_controller.rb +48 -0
  19. data/app/controllers/bookingsync_portal/admin/help_controller.rb +7 -0
  20. data/app/controllers/bookingsync_portal/admin/remote_accounts_controller.rb +27 -0
  21. data/app/controllers/bookingsync_portal/admin/rentals_controller.rb +36 -0
  22. data/app/controllers/bookingsync_portal/application_controller.rb +4 -0
  23. data/app/helpers/bookingsync_portal/admin/application_helper.rb +34 -0
  24. data/app/models/bookingsync_portal/account.rb +15 -0
  25. data/app/models/bookingsync_portal/connection.rb +29 -0
  26. data/app/models/bookingsync_portal/photo.rb +8 -0
  27. data/app/models/bookingsync_portal/rate.rb +6 -0
  28. data/app/models/bookingsync_portal/remote_account.rb +9 -0
  29. data/app/models/bookingsync_portal/remote_rental.rb +31 -0
  30. data/app/models/bookingsync_portal/rental.rb +26 -0
  31. data/app/synchronizers/bookingsync_portal/write/ensure_source_exists.rb +22 -0
  32. data/app/synchronizers/bookingsync_portal/write/source.rb +30 -0
  33. data/app/views/bookingsync_portal/admin/connections/create.js.coffee +6 -0
  34. data/app/views/bookingsync_portal/admin/connections/destroy.js.coffee +7 -0
  35. data/app/views/bookingsync_portal/admin/help/_help.html.erb +5 -0
  36. data/app/views/bookingsync_portal/admin/help/index.html.erb +7 -0
  37. data/app/views/bookingsync_portal/admin/remote_accounts/_form.html.erb +11 -0
  38. data/app/views/bookingsync_portal/admin/remote_accounts/_how_to_connect.html.erb +1 -0
  39. data/app/views/bookingsync_portal/admin/remote_accounts/new.html.erb +5 -0
  40. data/app/views/bookingsync_portal/admin/rentals/_connected_rental.html.erb +30 -0
  41. data/app/views/bookingsync_portal/admin/rentals/_new_remote_account.html.erb +3 -0
  42. data/app/views/bookingsync_portal/admin/rentals/_new_remote_rental.html.erb +6 -0
  43. data/app/views/bookingsync_portal/admin/rentals/_remote_rental.html.erb +11 -0
  44. data/app/views/bookingsync_portal/admin/rentals/_rental.html.erb +14 -0
  45. data/app/views/bookingsync_portal/admin/rentals/_rentals.html.erb +14 -0
  46. data/app/views/bookingsync_portal/admin/rentals/index.html.erb +56 -0
  47. data/app/views/bookingsync_portal/admin/rentals/show.js.erb +7 -0
  48. data/app/views/layouts/bookingsync_portal/_flash.html.erb +8 -0
  49. data/app/views/layouts/bookingsync_portal/_menu.html.erb +30 -0
  50. data/app/views/layouts/bookingsync_portal/admin.html.erb +21 -0
  51. data/app/views/layouts/bookingsync_portal/fullscreen/application.html.erb +19 -0
  52. data/config/locales/en.yml +85 -0
  53. data/config/routes.rb +9 -0
  54. data/db/migrate/20150222172825_create_accounts.rb +17 -0
  55. data/db/migrate/20150222173413_create_rentals.rb +14 -0
  56. data/db/migrate/20150222173711_create_remote_accounts.rb +10 -0
  57. data/db/migrate/20150222174023_create_remote_rentals.rb +12 -0
  58. data/db/migrate/20150222174234_create_connections.rb +10 -0
  59. data/db/migrate/20150520134912_create_photos.rb +13 -0
  60. data/db/migrate/20150521091056_create_rates.rb +12 -0
  61. data/db/migrate/20151210164752_add_synced_source_id_to_account.rb +5 -0
  62. data/db/migrate/20160301141356_create_bookings.rb +9 -0
  63. data/lib/bookingsync_portal.rb +77 -0
  64. data/lib/bookingsync_portal/booking_map.rb +109 -0
  65. data/lib/bookingsync_portal/engine.rb +18 -0
  66. data/lib/bookingsync_portal/mash_serializer.rb +9 -0
  67. data/lib/bookingsync_portal/version.rb +3 -0
  68. data/lib/generators/bookingsync_portal/install_generator.rb +22 -0
  69. data/lib/generators/templates/initializers/bookingsync_portal.rb +48 -0
  70. data/lib/generators/templates/models/account.rb +14 -0
  71. data/lib/generators/templates/models/connection.rb +2 -0
  72. data/lib/generators/templates/models/photo.rb +4 -0
  73. data/lib/generators/templates/models/rate.rb +3 -0
  74. data/lib/generators/templates/models/remote_account.rb +6 -0
  75. data/lib/generators/templates/models/remote_rental.rb +6 -0
  76. data/lib/generators/templates/models/rental.rb +11 -0
  77. data/lib/tasks/bookingsync_portal_tasks.rake +4 -0
  78. metadata +521 -0
@@ -0,0 +1,25 @@
1
+ require 'bookingsync_application/controllers/common_base'
2
+
3
+ module BookingsyncPortal
4
+ module Admin
5
+ class BaseController < ApplicationController
6
+ helper BookingsyncPortal::Admin::ApplicationHelper
7
+ layout 'bookingsync_portal/admin'
8
+ respond_to :html
9
+ include BookingsyncApplication::Controllers::CommonBase
10
+
11
+ before_action :enforce_remote_account!
12
+
13
+ private
14
+
15
+ def enforce_remote_account!
16
+ redirect_to new_admin_remote_account_path if current_account.remote_accounts.empty?
17
+ end
18
+
19
+ def messagebus_channel
20
+ "/account-#{current_account.id}"
21
+ end
22
+ helper_method :messagebus_channel
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,48 @@
1
+ module BookingsyncPortal
2
+ module Admin
3
+ class ConnectionsController < Admin::BaseController
4
+ def create
5
+ if remote_account? && BookingsyncPortal.create_remote_rental
6
+ new_remote_rental = BookingsyncPortal.remote_rental_model.constantize.new(remote_account: remote_account)
7
+ @connection = rental.create_connection(remote_rental: new_remote_rental)
8
+ else
9
+ @connection = rental.create_connection(remote_rental: remote_rental)
10
+ end
11
+
12
+ respond_to do |wants|
13
+ wants.html { redirect_to admin_rentals_path }
14
+ wants.js
15
+ end
16
+ end
17
+
18
+ def destroy
19
+ @connection = current_account.connections.find(params[:id]).destroy
20
+ @not_connected_rentals = current_account.rentals.visible.ordered.not_connected
21
+ @visible_rentals = current_account.rentals.visible
22
+
23
+ respond_to do |wants|
24
+ wants.html { redirect_to admin_rentals_path }
25
+ wants.js
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def rental
32
+ @rental ||= current_account.rentals.not_connected.visible.find(params[:rental_id])
33
+ end
34
+
35
+ def remote_rental
36
+ @remote_rental ||= current_account.remote_rentals.not_connected.find(params[:remote_rental_id])
37
+ end
38
+
39
+ def remote_account
40
+ @remote_account ||= current_account.remote_accounts.find(params[:remote_account_id])
41
+ end
42
+
43
+ def remote_account?
44
+ params[:remote_account_id].present? && remote_account
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,7 @@
1
+ module BookingsyncPortal
2
+ module Admin
3
+ class HelpController < Admin::BaseController
4
+ layout "bookingsync_portal/fullscreen/application"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,27 @@
1
+ module BookingsyncPortal
2
+ module Admin
3
+ class RemoteAccountsController < Admin::BaseController
4
+ skip_before_action :enforce_remote_account!
5
+
6
+ def new
7
+ @remote_account = scope.build
8
+ end
9
+
10
+ def create
11
+ BookingsyncPortal::Write::EnsureSourceExists.new(current_account).call
12
+ @remote_account = scope.create(params_remote_account)
13
+ respond_with @remote_account, location: admin_rentals_path
14
+ end
15
+
16
+ private
17
+
18
+ def scope
19
+ current_account.remote_accounts
20
+ end
21
+
22
+ def params_remote_account
23
+ params.require(:remote_account).permit(:uid)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,36 @@
1
+ module BookingsyncPortal
2
+ module Admin
3
+ class RentalsController < Admin::BaseController
4
+ before_action :synchronize_rentals, only: :index
5
+ before_action :fetch_remote_rentals, only: :index
6
+
7
+ def index
8
+ @not_connected_rentals = current_account.rentals.visible.ordered.not_connected
9
+ @visible_rentals = current_account.rentals.visible
10
+ @remote_accounts = current_account.remote_accounts
11
+ @remote_rentals_by_account = current_account.remote_rentals.ordered
12
+ .includes(:remote_account, :rental).group_by(&:remote_account)
13
+ end
14
+
15
+ def show
16
+ rental
17
+ end
18
+
19
+ private
20
+
21
+ def synchronize_rentals
22
+ BookingsyncPortal.rental_model.constantize.synchronize(scope: current_account)
23
+ end
24
+
25
+ def fetch_remote_rentals
26
+ unless BookingsyncPortal.fetch_remote_rentals(current_account)
27
+ @remote_account_not_registered = true
28
+ end
29
+ end
30
+
31
+ def rental
32
+ @rental ||= current_account.rentals.visible.find(params[:id])
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,4 @@
1
+ module BookingsyncPortal
2
+ class ApplicationController < ::ApplicationController
3
+ end
4
+ end
@@ -0,0 +1,34 @@
1
+ module BookingsyncPortal
2
+ module Admin
3
+ module ApplicationHelper
4
+ def rental_details(rental)
5
+ scope = 'bookingsync_portal.admin.rentals.rental'
6
+
7
+ details = Array.new
8
+ if rental.sleeps.to_i > 0
9
+ if rental.sleeps_max.to_i > 0 && rental.sleeps_max.to_i > rental.sleeps.to_i
10
+ details << t(:sleeps_html, scope: scope,
11
+ count: "#{rental.sleeps.to_i}-#{rental.sleeps_max.to_i}")
12
+ else
13
+ details << t(:sleeps_html, scope: scope,
14
+ count: rental.sleeps.to_i)
15
+ end
16
+ end
17
+ if rental.bedrooms_count.to_i > 0
18
+ details << t(:bedrooms_html, scope: scope,
19
+ count: rental.bedrooms_count.to_i)
20
+ end
21
+ if rental.bathrooms_count.to_i > 0
22
+ details << t(:bathrooms_html, scope: scope,
23
+ count: rental.bathrooms_count.to_i)
24
+ end
25
+ if rental.surface.to_i > 0
26
+ details << t(:surface_html, scope: scope,
27
+ count: rental.surface.to_i, unit: rental.surface_unit_symbol)
28
+ end
29
+
30
+ safe_join(details, ', ')
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,15 @@
1
+ class BookingsyncPortal::Account < ActiveRecord::Base
2
+ self.table_name = 'accounts'
3
+
4
+ include BookingSync::Engine::Models::Account
5
+
6
+ has_many :remote_accounts, class_name: BookingsyncPortal.remote_account_model, dependent: :destroy
7
+ has_many :remote_rentals, class_name: BookingsyncPortal.remote_rental_model, through: :remote_accounts
8
+ has_many :rentals, class_name: BookingsyncPortal.rental_model, dependent: :destroy
9
+ has_many :connections, class_name: BookingsyncPortal.connection_model, through: :rentals
10
+
11
+ # Used to synchronize accounts using `Account.synchronize(remove: true)`
12
+ def self.api
13
+ BookingSync::API::Client.new(BookingSync::Engine.application_token.token)
14
+ end
15
+ end
@@ -0,0 +1,29 @@
1
+ class BookingsyncPortal::Connection < ActiveRecord::Base
2
+ self.table_name = 'connections'
3
+
4
+ belongs_to :remote_rental, class_name: BookingsyncPortal.remote_rental_model
5
+ belongs_to :rental, class_name: BookingsyncPortal.rental_model
6
+
7
+ validates :remote_rental, presence: true
8
+ validates :rental, presence: true
9
+
10
+ validate :matching_accounts, if: -> { rental && remote_rental }
11
+
12
+ after_commit :notify_via_message_bus
13
+
14
+ def notify_via_message_bus
15
+ MessageBus.publish "/account-#{rental.account_id}", { refresh_from:
16
+ BookingsyncPortal::Engine.routes.url_helpers.admin_rental_path(rental, format: :js)
17
+ }
18
+ end
19
+
20
+ private
21
+
22
+ def matching_accounts?
23
+ rental.account == remote_rental.remote_account.account
24
+ end
25
+
26
+ def matching_accounts
27
+ errors.add(:base, :not_matching_accounts) unless matching_accounts?
28
+ end
29
+ end
@@ -0,0 +1,8 @@
1
+ class BookingsyncPortal::Photo < ActiveRecord::Base
2
+ self.table_name = 'photos'
3
+
4
+ belongs_to :rental
5
+ has_one :account, through: :rental
6
+
7
+ scope :ordered, -> { order position: :asc }
8
+ end
@@ -0,0 +1,6 @@
1
+ class BookingsyncPortal::Rate < ActiveRecord::Base
2
+ self.table_name = 'rates'
3
+
4
+ belongs_to :rental
5
+ has_one :account, through: :rental
6
+ end
@@ -0,0 +1,9 @@
1
+ class BookingsyncPortal::RemoteAccount < ActiveRecord::Base
2
+ self.table_name = 'remote_accounts'
3
+
4
+ belongs_to :account, class_name: BookingsyncPortal.account_model
5
+ has_many :remote_rentals, class_name: BookingsyncPortal.remote_rental_model, dependent: :destroy
6
+
7
+ validates :uid, presence: true, uniqueness: true
8
+ validates :account, presence: true
9
+ end
@@ -0,0 +1,31 @@
1
+ require 'bookingsync_portal/mash_serializer'
2
+
3
+ class BookingsyncPortal::RemoteRental < ActiveRecord::Base
4
+ self.table_name = 'remote_rentals'
5
+
6
+ belongs_to :remote_account, class_name: BookingsyncPortal.remote_account_model
7
+ has_one :account, class_name: BookingsyncPortal.account_model, through: :remote_account
8
+ has_one :connection, class_name: BookingsyncPortal.connection_model, dependent: :destroy
9
+ has_one :rental, class_name: BookingsyncPortal.rental_model, through: :connection
10
+
11
+ serialize :remote_data, BookingsyncPortal::MashSerializer
12
+
13
+ validates :uid, uniqueness: { allow_nil: true }
14
+ validates :remote_account, presence: true
15
+
16
+ scope :ordered, -> { order(created_at: :desc) }
17
+ scope :connected, -> { joins(:rental) }
18
+ scope :not_connected, -> { includes(:rental).where(rentals: { id: nil }) }
19
+
20
+ def display_name
21
+ uid
22
+ end
23
+
24
+ def connected?
25
+ rental.present?
26
+ end
27
+
28
+ def synchronized?
29
+ synchronized_at.present?
30
+ end
31
+ end
@@ -0,0 +1,26 @@
1
+ class BookingsyncPortal::Rental < ActiveRecord::Base
2
+ self.table_name = 'rentals'
3
+
4
+ belongs_to :account, class_name: BookingsyncPortal.account_model
5
+ has_one :connection, class_name: BookingsyncPortal.connection_model, dependent: :destroy
6
+ has_one :remote_rental, class_name: BookingsyncPortal.remote_rental_model, through: :connection
7
+ has_many :photos, class_name: BookingsyncPortal.photo_model, dependent: :destroy
8
+ if BookingsyncPortal.rate_model.present?
9
+ has_many :rates, class_name: BookingsyncPortal.rate_model, dependent: :destroy
10
+ end
11
+
12
+ validates :synced_id, uniqueness: true, presence: true
13
+
14
+ scope :ordered, -> { order(position: :asc) }
15
+ scope :connected, -> { joins(:remote_rental) }
16
+ scope :not_connected, -> { includes(:connection).where(connections: { remote_rental_id: nil }) }
17
+ scope :visible, -> { all }
18
+
19
+ def connected?
20
+ remote_rental.present?
21
+ end
22
+
23
+ def ordered_photos
24
+ photos.ordered
25
+ end
26
+ end
@@ -0,0 +1,22 @@
1
+ class BookingsyncPortal::Write::EnsureSourceExists
2
+ attr_reader :account
3
+
4
+ def initialize(account)
5
+ @account = account
6
+ end
7
+
8
+ def call
9
+ blank_source? ? update_source : true
10
+ end
11
+
12
+ private
13
+
14
+ def blank_source?
15
+ account.synced_source_id.blank?
16
+ end
17
+
18
+ def update_source
19
+ source = BookingsyncPortal::Write::Source.new(account).synchronize
20
+ account.update!(synced_source_id: source.id)
21
+ end
22
+ end
@@ -0,0 +1,30 @@
1
+ class BookingsyncPortal::Write::Source
2
+ attr_reader :account
3
+ private :account
4
+
5
+ def initialize(account)
6
+ @account = account
7
+ end
8
+
9
+ def synchronize
10
+ find_or_create
11
+ end
12
+
13
+ private
14
+
15
+ def find_or_create
16
+ find_source || create_source
17
+ end
18
+
19
+ def api
20
+ account.api
21
+ end
22
+
23
+ def find_source
24
+ api.sources.find { |source| source.name.downcase == BookingsyncPortal.source_name.downcase }
25
+ end
26
+
27
+ def create_source
28
+ api.create_source(name: BookingsyncPortal.source_name)
29
+ end
30
+ end
@@ -0,0 +1,6 @@
1
+ <%- if @connection.persisted? %>
2
+ $("#<%= dom_id @connection.remote_rental %>, .new_rental_placeholder").first().replaceWith(
3
+ "<%=j render 'bookingsync_portal/admin/rentals/connected_rental', rental: @connection.rental, remote_rental: @connection.remote_rental %>");
4
+ $(".bookingsync-rental").draggableRental()
5
+ $(".panel.panel-remote").droppableRemoteRental()
6
+ <%- end %>
@@ -0,0 +1,7 @@
1
+ $("#<%= dom_id @connection.rental %>").first().replaceWith(
2
+ "<%=j render 'bookingsync_portal/admin/rentals/remote_rental', remote_rental: @connection.remote_rental %>")
3
+ $(".bookingsync-rentals-list .rentals-list-scroll").first().html(
4
+ "<%=j render 'bookingsync_portal/admin/rentals/rentals', visible_rentals: @visible_rentals, not_connected_rentals: @not_connected_rentals %>");
5
+
6
+ $(".bookingsync-rental").draggableRental()
7
+ $(".panel.panel-remote").droppableRemoteRental()
@@ -0,0 +1,5 @@
1
+ <h2><%=t '.publish_rentals.title' %></h2>
2
+ <p><%=t '.publish_rentals.description_html' %></p>
3
+ <h2><%=t '.connect.title' %></h2>
4
+ <p><%=t '.connect.description_html' %></p>
5
+ <%= image_tag 'bookingsync_portal/help/connect.gif', class: "img-responsive" %>
@@ -0,0 +1,7 @@
1
+ <article>
2
+ <header>
3
+ <%= link_to icon("times", t('.close')), admin_root_path, class: "pull-right" %>
4
+ <h1><%=t '.title' %></h1>
5
+ </header>
6
+ <%= render 'help' %>
7
+ </article>
@@ -0,0 +1,11 @@
1
+ <div class="col-sm-offset-4 col-sm-4">
2
+ <%= simple_form_for [:admin, remote_account] do |f| -%>
3
+ <%= f.input :uid, wrapper_html: { class: 'form-group' },
4
+ label_html: { class: 'label-lg' }, input_html: { class: 'input-lg form-control' } -%>
5
+
6
+ <div class="form-group">
7
+ <%= f.submit t('.submit_new', portal_name: BookingsyncPortal.portal_name),
8
+ class: "btn btn-primary btn-lg btn-block" %>
9
+ </div>
10
+ <%- end -%>
11
+ </div>
@@ -0,0 +1 @@
1
+ <p class="lead">Info on how to connect with remote account, modify app/views/bookingsync_portal/admin/remote_accounts/_how_to_connect.html.erb</p>
@@ -0,0 +1,5 @@
1
+ <div class="text-center">
2
+ <h1><%=t '.title', portal_name: BookingsyncPortal.portal_name %></h1>
3
+ <%= render 'how_to_connect' %>
4
+ <%= render 'form', remote_account: @remote_account %>
5
+ </div>
@@ -0,0 +1,30 @@
1
+ <div id="<%= dom_id rental %>"
2
+ class="panel panel-connected <%= "pending" unless rental.remote_rental.synchronized? %>">
3
+
4
+ <div class="panel-body-grid">
5
+ <div class="panel-body-grid-photo">
6
+ <%- if rental.ordered_photos.first -%>
7
+ <%= image_tag rental.ordered_photos.first.thumb_url, class: "img-responsive" %>
8
+ <%- end -%>
9
+ </div>
10
+ <div class="panel-body-grid-text">
11
+ <h4 title="<%= RemoteRental.human_attribute_name(:uid) %>: <%= remote_rental.uid %>">
12
+ <%= rental.name %><br>
13
+ <small><%= remote_rental.display_name %></small>
14
+ </h4>
15
+ <div class="status">
16
+ <%= link_to BookingsyncPortal::Engine.routes.url_helpers.admin_connection_path(rental.connection),
17
+ class: ["btn", "btn-xs", "remove-connection", rental.remote_rental.synchronized? ? "btn-success" : "btn-warning"],
18
+ data: { disable_with: t('.disconnecting_rental') }, method: :delete, remote: true do %>
19
+ <% unless rental.remote_rental.synchronized? %>
20
+ <span class="hover_hide"><%= icon('spinner') %></span>
21
+ <% else %>
22
+ <span class="hover_hide"><%= icon('check') %></span>
23
+ <% end %>
24
+ <span class="hover_show"><%= t('.disconnect_rental') %></span>
25
+ <% end %>
26
+ </div>
27
+ <p class="text-overflow"><%= rental_details(rental) %></p>
28
+ </div>
29
+ </div>
30
+ </div>