stay_commerce 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.
Files changed (496) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +53 -0
  4. data/Rakefile +8 -0
  5. data/app/assets/config/stay_manifest.js +1 -0
  6. data/app/assets/fonts/bootstrap-icons.woff +0 -0
  7. data/app/assets/fonts/bootstrap-icons.woff2 +0 -0
  8. data/app/assets/images/chevron-down.svg +3 -0
  9. data/app/assets/images/circle-fill.svg +3 -0
  10. data/app/assets/images/circle.svg +3 -0
  11. data/app/assets/images/plus-circle.svg +4 -0
  12. data/app/assets/images/save.svg +3 -0
  13. data/app/assets/images/store.svg +3 -0
  14. data/app/assets/javascripts/stay/admin/address.js +165 -0
  15. data/app/assets/javascripts/stay/admin/ckeditor.min.js +7 -0
  16. data/app/assets/javascripts/stay/admin/flatpickr.min.js +2 -0
  17. data/app/assets/javascripts/stay/admin/index.js +28 -0
  18. data/app/assets/javascripts/stay/admin/property.js +37 -0
  19. data/app/assets/javascripts/stay/admin/select2.min.js +2 -0
  20. data/app/assets/javascripts/stay/application.js +14 -0
  21. data/app/assets/javascripts/stay/bootstrap.bundle.min.js +7 -0
  22. data/app/assets/javascripts/stay/ckeditor.js +11 -0
  23. data/app/assets/javascripts/stay/custom.js +56 -0
  24. data/app/assets/javascripts/stay/jquery-ui.min.js +13 -0
  25. data/app/assets/javascripts/stay/jquery.min.js +2 -0
  26. data/app/assets/stylesheets/stay/admin/flatpickr.min.css +13 -0
  27. data/app/assets/stylesheets/stay/admin/index.css +100 -0
  28. data/app/assets/stylesheets/stay/admin/select2.min.css +1 -0
  29. data/app/assets/stylesheets/stay/application.css +24 -0
  30. data/app/assets/stylesheets/stay/badges.css +72 -0
  31. data/app/assets/stylesheets/stay/bootstrap-icons.min.css +5 -0
  32. data/app/assets/stylesheets/stay/bootstrap-icons_fix.css +5 -0
  33. data/app/assets/stylesheets/stay/common.css +283 -0
  34. data/app/assets/stylesheets/stay/jquery-ui.css +1311 -0
  35. data/app/assets/stylesheets/stay/webpixels.css +7 -0
  36. data/app/channels/chat_channel.rb +13 -0
  37. data/app/controllers/concerns/stay/booking_validations.rb +86 -0
  38. data/app/controllers/concerns/stay/stripe_concern.rb +72 -0
  39. data/app/controllers/stay/admin/addresses_controller.rb +76 -0
  40. data/app/controllers/stay/admin/amenities_controller.rb +52 -0
  41. data/app/controllers/stay/admin/amenity_categories_controller.rb +52 -0
  42. data/app/controllers/stay/admin/base_controller.rb +38 -0
  43. data/app/controllers/stay/admin/bed_types_controller.rb +51 -0
  44. data/app/controllers/stay/admin/bookings_controller.rb +54 -0
  45. data/app/controllers/stay/admin/cancellation_policies_controller.rb +51 -0
  46. data/app/controllers/stay/admin/chats_controller.rb +53 -0
  47. data/app/controllers/stay/admin/cities_controller.rb +66 -0
  48. data/app/controllers/stay/admin/confirmations_controller.rb +30 -0
  49. data/app/controllers/stay/admin/countries_controller.rb +53 -0
  50. data/app/controllers/stay/admin/dashboard_controller.rb +8 -0
  51. data/app/controllers/stay/admin/features_controller.rb +54 -0
  52. data/app/controllers/stay/admin/home_controller.rb +15 -0
  53. data/app/controllers/stay/admin/house_rules_controller.rb +51 -0
  54. data/app/controllers/stay/admin/messages_controller.rb +53 -0
  55. data/app/controllers/stay/admin/passwords_controller.rb +32 -0
  56. data/app/controllers/stay/admin/payment_methods_controller.rb +53 -0
  57. data/app/controllers/stay/admin/payments_controller.rb +63 -0
  58. data/app/controllers/stay/admin/properties_controller.rb +118 -0
  59. data/app/controllers/stay/admin/property_categories_controller.rb +51 -0
  60. data/app/controllers/stay/admin/property_types_controller.rb +51 -0
  61. data/app/controllers/stay/admin/registrations_controller.rb +19 -0
  62. data/app/controllers/stay/admin/reviews_Controller.rb +63 -0
  63. data/app/controllers/stay/admin/roles_controller.rb +54 -0
  64. data/app/controllers/stay/admin/room_amenities_controller.rb +43 -0
  65. data/app/controllers/stay/admin/room_types_controller.rb +58 -0
  66. data/app/controllers/stay/admin/rooms_controller.rb +62 -0
  67. data/app/controllers/stay/admin/sessions_controller.rb +29 -0
  68. data/app/controllers/stay/admin/states_controller.rb +68 -0
  69. data/app/controllers/stay/admin/stores_controller.rb +132 -0
  70. data/app/controllers/stay/admin/taxes_controller.rb +51 -0
  71. data/app/controllers/stay/admin/users_controller.rb +85 -0
  72. data/app/controllers/stay/api/v1/amenities_controller.rb +13 -0
  73. data/app/controllers/stay/api/v1/amenity_categories_controller.rb +13 -0
  74. data/app/controllers/stay/api/v1/bed_types_controller.rb +9 -0
  75. data/app/controllers/stay/api/v1/booking_queries_controller.rb +205 -0
  76. data/app/controllers/stay/api/v1/bookings_controller.rb +299 -0
  77. data/app/controllers/stay/api/v1/cancellation_policies_controller.rb +10 -0
  78. data/app/controllers/stay/api/v1/chats_controller.rb +116 -0
  79. data/app/controllers/stay/api/v1/credit_cards_controller.rb +62 -0
  80. data/app/controllers/stay/api/v1/features_controller.rb +21 -0
  81. data/app/controllers/stay/api/v1/house_rules_controller.rb +9 -0
  82. data/app/controllers/stay/api/v1/invoices_controller.rb +47 -0
  83. data/app/controllers/stay/api/v1/messages_controller.rb +144 -0
  84. data/app/controllers/stay/api/v1/payments_controller.rb +61 -0
  85. data/app/controllers/stay/api/v1/profiles_controller.rb +23 -0
  86. data/app/controllers/stay/api/v1/properties_controller.rb +233 -0
  87. data/app/controllers/stay/api/v1/property_categories_controller.rb +14 -0
  88. data/app/controllers/stay/api/v1/property_types_controller.rb +14 -0
  89. data/app/controllers/stay/api/v1/room_types_controller.rb +14 -0
  90. data/app/controllers/stay/api/v1/rooms_controller.rb +23 -0
  91. data/app/controllers/stay/api/v1/user_paypal_controller.rb +53 -0
  92. data/app/controllers/stay/api/v1/users_controller.rb +17 -0
  93. data/app/controllers/stay/application_controller.rb +44 -0
  94. data/app/controllers/stay/base_api_controller.rb +14 -0
  95. data/app/controllers/stay/bookings_controller.rb +29 -0
  96. data/app/controllers/stay/payments_controller.rb +50 -0
  97. data/app/controllers/stay/profiles_controller.rb +26 -0
  98. data/app/controllers/stay/properties_controller.rb +15 -0
  99. data/app/controllers/stay/rooms_controller.rb +22 -0
  100. data/app/controllers/stay/users/confirmations_controller.rb +30 -0
  101. data/app/controllers/stay/users/omniauth_callbacks_controller.rb +30 -0
  102. data/app/controllers/stay/users/passwords_controller.rb +51 -0
  103. data/app/controllers/stay/users/registrations_controller.rb +62 -0
  104. data/app/controllers/stay/users/sessions_controller.rb +27 -0
  105. data/app/controllers/stay/users/unlocks_controller.rb +30 -0
  106. data/app/helpers/stay/admin/cities_helper.rb +4 -0
  107. data/app/helpers/stay/admin/countries_helper.rb +4 -0
  108. data/app/helpers/stay/admin/states_helper.rb +4 -0
  109. data/app/helpers/stay/admin/stores_helper.rb +25 -0
  110. data/app/helpers/stay/application_helper.rb +63 -0
  111. data/app/helpers/stay/currency_helper.rb +34 -0
  112. data/app/helpers/stay/locale_helper.rb +107 -0
  113. data/app/jobs/stay/application_job.rb +4 -0
  114. data/app/mailers/stay/application_mailer.rb +6 -0
  115. data/app/mailers/stay/user_mailer.rb +13 -0
  116. data/app/models/concerns/stay/translatable_resource.rb +25 -0
  117. data/app/models/stay/additional_rule.rb +5 -0
  118. data/app/models/stay/address.rb +40 -0
  119. data/app/models/stay/amenity.rb +13 -0
  120. data/app/models/stay/amenity_category.rb +6 -0
  121. data/app/models/stay/application_record.rb +5 -0
  122. data/app/models/stay/bed_type.rb +7 -0
  123. data/app/models/stay/booking.rb +199 -0
  124. data/app/models/stay/booking_query.rb +23 -0
  125. data/app/models/stay/cancellation_policy.rb +6 -0
  126. data/app/models/stay/chat.rb +23 -0
  127. data/app/models/stay/city.rb +9 -0
  128. data/app/models/stay/country.rb +23 -0
  129. data/app/models/stay/credit_card.rb +6 -0
  130. data/app/models/stay/discount.rb +4 -0
  131. data/app/models/stay/expense.rb +4 -0
  132. data/app/models/stay/feature.rb +11 -0
  133. data/app/models/stay/house_rule.rb +4 -0
  134. data/app/models/stay/invoice.rb +23 -0
  135. data/app/models/stay/line_item.rb +7 -0
  136. data/app/models/stay/message.rb +30 -0
  137. data/app/models/stay/payment.rb +54 -0
  138. data/app/models/stay/payment_method.rb +13 -0
  139. data/app/models/stay/price.rb +18 -0
  140. data/app/models/stay/property.rb +243 -0
  141. data/app/models/stay/property_amenity.rb +6 -0
  142. data/app/models/stay/property_category.rb +6 -0
  143. data/app/models/stay/property_feature.rb +6 -0
  144. data/app/models/stay/property_house_rule.rb +6 -0
  145. data/app/models/stay/property_tax.rb +6 -0
  146. data/app/models/stay/property_type.rb +15 -0
  147. data/app/models/stay/review.rb +5 -0
  148. data/app/models/stay/role.rb +11 -0
  149. data/app/models/stay/role_user.rb +6 -0
  150. data/app/models/stay/room.rb +93 -0
  151. data/app/models/stay/room_amenity.rb +6 -0
  152. data/app/models/stay/room_feature.rb +6 -0
  153. data/app/models/stay/room_type.rb +7 -0
  154. data/app/models/stay/state.rb +13 -0
  155. data/app/models/stay/store.rb +139 -0
  156. data/app/models/stay/store_property.rb +9 -0
  157. data/app/models/stay/tax.rb +8 -0
  158. data/app/models/stay/user.rb +111 -0
  159. data/app/models/stay/user_paypal.rb +5 -0
  160. data/app/serializers/amenity_category_serializer.rb +13 -0
  161. data/app/serializers/amenity_serializer.rb +3 -0
  162. data/app/serializers/bed_type_serializer.rb +5 -0
  163. data/app/serializers/booking_query_serializer.rb +6 -0
  164. data/app/serializers/booking_serializer.rb +68 -0
  165. data/app/serializers/cancellation_policy_serializer.rb +3 -0
  166. data/app/serializers/chat_serializer.rb +63 -0
  167. data/app/serializers/credit_card_serializer.rb +7 -0
  168. data/app/serializers/house_rules_serializer.rb +3 -0
  169. data/app/serializers/invoice_serializer.rb +41 -0
  170. data/app/serializers/last_5_messages_serializer.rb +7 -0
  171. data/app/serializers/line_item_serializer.rb +6 -0
  172. data/app/serializers/message_serializer.rb +44 -0
  173. data/app/serializers/property_category_serializer.rb +7 -0
  174. data/app/serializers/property_feature_serializer.rb +5 -0
  175. data/app/serializers/property_listing_serializer.rb +13 -0
  176. data/app/serializers/property_serializer.rb +77 -0
  177. data/app/serializers/property_type_serializer.rb +7 -0
  178. data/app/serializers/room_serializer.rb +36 -0
  179. data/app/serializers/room_type_serializer.rb +5 -0
  180. data/app/serializers/user_listing_serializer.rb +23 -0
  181. data/app/serializers/user_serializer.rb +41 -0
  182. data/app/services/stay/bookings/create_booking_service.rb +49 -0
  183. data/app/services/stay/bookings/create_line_item_service.rb +15 -0
  184. data/app/services/stay/bookings/existing_booking_service.rb +50 -0
  185. data/app/services/stay/chat/chat_messaging_service.rb +82 -0
  186. data/app/services/stay/chat/query_messaging_service.rb +56 -0
  187. data/app/services/stay/seeds/all.rb +27 -0
  188. data/app/services/stay/seeds/cancellation_policy.rb +21 -0
  189. data/app/services/stay/seeds/countries.rb +33 -0
  190. data/app/services/stay/seeds/roles.rb +9 -0
  191. data/app/services/stay/seeds/states.rb +63 -0
  192. data/app/services/stay/seeds/stores.rb +34 -0
  193. data/app/views/devise/mailer/reset_password_instructions.html.rb +10 -0
  194. data/app/views/kaminari/_first_page.html.erb +3 -0
  195. data/app/views/kaminari/_gap.html.erb +3 -0
  196. data/app/views/kaminari/_last_page.html.erb +3 -0
  197. data/app/views/kaminari/_next_page.html.erb +3 -0
  198. data/app/views/kaminari/_page.html.erb +9 -0
  199. data/app/views/kaminari/_paginator.html.erb +17 -0
  200. data/app/views/kaminari/_prev_page.html.erb +3 -0
  201. data/app/views/layouts/stay/admin.html.erb +45 -0
  202. data/app/views/layouts/stay/application.html.erb +34 -0
  203. data/app/views/stay/admin/addresses/_form.html.erb +49 -0
  204. data/app/views/stay/admin/addresses/edit.html.erb +5 -0
  205. data/app/views/stay/admin/addresses/index.html.erb +68 -0
  206. data/app/views/stay/admin/addresses/new.html.erb +5 -0
  207. data/app/views/stay/admin/addresses/show.html.erb +40 -0
  208. data/app/views/stay/admin/amenities/_form.html.erb +38 -0
  209. data/app/views/stay/admin/amenities/edit.html.erb +14 -0
  210. data/app/views/stay/admin/amenities/index.html.erb +58 -0
  211. data/app/views/stay/admin/amenities/new.html.erb +14 -0
  212. data/app/views/stay/admin/amenities/show.html.erb +19 -0
  213. data/app/views/stay/admin/amenity_categories/_form.html.erb +29 -0
  214. data/app/views/stay/admin/amenity_categories/edit.html.erb +14 -0
  215. data/app/views/stay/admin/amenity_categories/index.html.erb +53 -0
  216. data/app/views/stay/admin/amenity_categories/new.html.erb +14 -0
  217. data/app/views/stay/admin/amenity_categories/show.html.erb +18 -0
  218. data/app/views/stay/admin/bed_types/_form.html.erb +29 -0
  219. data/app/views/stay/admin/bed_types/edit.html.erb +14 -0
  220. data/app/views/stay/admin/bed_types/index.html.erb +56 -0
  221. data/app/views/stay/admin/bed_types/new.html.erb +14 -0
  222. data/app/views/stay/admin/bed_types/show.html.erb +0 -0
  223. data/app/views/stay/admin/bookings/_booking_invoice.html.erb +37 -0
  224. data/app/views/stay/admin/bookings/_booking_summary.html.erb +61 -0
  225. data/app/views/stay/admin/bookings/_form.html.erb +59 -0
  226. data/app/views/stay/admin/bookings/edit.html.erb +14 -0
  227. data/app/views/stay/admin/bookings/index.html.erb +74 -0
  228. data/app/views/stay/admin/bookings/new.html.erb +14 -0
  229. data/app/views/stay/admin/bookings/show.html.erb +80 -0
  230. data/app/views/stay/admin/cancellation_policies/_form.html.erb +38 -0
  231. data/app/views/stay/admin/cancellation_policies/edit.html.erb +14 -0
  232. data/app/views/stay/admin/cancellation_policies/index.html.erb +58 -0
  233. data/app/views/stay/admin/cancellation_policies/new.html.erb +14 -0
  234. data/app/views/stay/admin/cancellation_policies/show.html.erb +0 -0
  235. data/app/views/stay/admin/chats/_form.html.erb +31 -0
  236. data/app/views/stay/admin/chats/edit.html.erb +5 -0
  237. data/app/views/stay/admin/chats/index.html.erb +58 -0
  238. data/app/views/stay/admin/chats/new.html.erb +5 -0
  239. data/app/views/stay/admin/chats/show.html.erb +19 -0
  240. data/app/views/stay/admin/cities/_form.html.erb +30 -0
  241. data/app/views/stay/admin/cities/edit.html.erb +14 -0
  242. data/app/views/stay/admin/cities/index.html.erb +62 -0
  243. data/app/views/stay/admin/cities/new.html.erb +14 -0
  244. data/app/views/stay/admin/countries/_form.html.erb +64 -0
  245. data/app/views/stay/admin/countries/edit.html.erb +14 -0
  246. data/app/views/stay/admin/countries/index.html.erb +62 -0
  247. data/app/views/stay/admin/countries/new.html.erb +14 -0
  248. data/app/views/stay/admin/countries/show.html.erb +23 -0
  249. data/app/views/stay/admin/dashboard/index.html.erb +1 -0
  250. data/app/views/stay/admin/features/_form.html.erb +38 -0
  251. data/app/views/stay/admin/features/edit.html.erb +14 -0
  252. data/app/views/stay/admin/features/index.html.erb +58 -0
  253. data/app/views/stay/admin/features/new.html.erb +14 -0
  254. data/app/views/stay/admin/features/show.html.erb +19 -0
  255. data/app/views/stay/admin/house_rules/_form.html.erb +29 -0
  256. data/app/views/stay/admin/house_rules/edit.html.erb +14 -0
  257. data/app/views/stay/admin/house_rules/index.html.erb +54 -0
  258. data/app/views/stay/admin/house_rules/new.html.erb +14 -0
  259. data/app/views/stay/admin/house_rules/show.html.erb +18 -0
  260. data/app/views/stay/admin/mailer/confirmation_instructions.html.erb +5 -0
  261. data/app/views/stay/admin/mailer/email_changed.html.erb +7 -0
  262. data/app/views/stay/admin/mailer/password_change.html.erb +3 -0
  263. data/app/views/stay/admin/mailer/reset_password_instructions.html.erb +7 -0
  264. data/app/views/stay/admin/messages/_form.html.erb +36 -0
  265. data/app/views/stay/admin/messages/edit.html.erb +5 -0
  266. data/app/views/stay/admin/messages/index.html.erb +60 -0
  267. data/app/views/stay/admin/messages/new.html.erb +5 -0
  268. data/app/views/stay/admin/messages/show.html.erb +24 -0
  269. data/app/views/stay/admin/passwords/edit.html.erb +37 -0
  270. data/app/views/stay/admin/passwords/new.html.erb +28 -0
  271. data/app/views/stay/admin/payment_methods/_form.html.erb +22 -0
  272. data/app/views/stay/admin/payment_methods/edit.html.erb +14 -0
  273. data/app/views/stay/admin/payment_methods/index.html.erb +54 -0
  274. data/app/views/stay/admin/payment_methods/new.html.erb +14 -0
  275. data/app/views/stay/admin/payment_methods/show.html.erb +18 -0
  276. data/app/views/stay/admin/payments/_form.html.erb +36 -0
  277. data/app/views/stay/admin/payments/edit.html.erb +5 -0
  278. data/app/views/stay/admin/payments/index.html.erb +60 -0
  279. data/app/views/stay/admin/payments/new.html.erb +5 -0
  280. data/app/views/stay/admin/payments/show.html.erb +24 -0
  281. data/app/views/stay/admin/properties/_amenities.html.erb +38 -0
  282. data/app/views/stay/admin/properties/_calendar.html.erb +37 -0
  283. data/app/views/stay/admin/properties/_description.html.erb +126 -0
  284. data/app/views/stay/admin/properties/_details.html.erb +75 -0
  285. data/app/views/stay/admin/properties/_features.html.erb +39 -0
  286. data/app/views/stay/admin/properties/_form.html.erb +118 -0
  287. data/app/views/stay/admin/properties/_images.html.erb +48 -0
  288. data/app/views/stay/admin/properties/_location.html.erb +55 -0
  289. data/app/views/stay/admin/properties/_price.html.erb +88 -0
  290. data/app/views/stay/admin/properties/_sidebar.html.erb +47 -0
  291. data/app/views/stay/admin/properties/edit.html.erb +17 -0
  292. data/app/views/stay/admin/properties/index.html.erb +82 -0
  293. data/app/views/stay/admin/properties/new.html.erb +14 -0
  294. data/app/views/stay/admin/properties/show.html.erb +149 -0
  295. data/app/views/stay/admin/property_categories/_form.html.erb +29 -0
  296. data/app/views/stay/admin/property_categories/edit.html.erb +14 -0
  297. data/app/views/stay/admin/property_categories/index.html.erb +57 -0
  298. data/app/views/stay/admin/property_categories/new.html.erb +14 -0
  299. data/app/views/stay/admin/property_categories/show.html.erb +18 -0
  300. data/app/views/stay/admin/property_types/_form.html.erb +29 -0
  301. data/app/views/stay/admin/property_types/edit.html.erb +14 -0
  302. data/app/views/stay/admin/property_types/index.html.erb +56 -0
  303. data/app/views/stay/admin/property_types/new.html.erb +14 -0
  304. data/app/views/stay/admin/property_types/show.html.erb +18 -0
  305. data/app/views/stay/admin/registrations/edit.html.erb +43 -0
  306. data/app/views/stay/admin/registrations/new.html.erb +35 -0
  307. data/app/views/stay/admin/reviews/_form.html.erb +37 -0
  308. data/app/views/stay/admin/reviews/edit.html.erb +14 -0
  309. data/app/views/stay/admin/reviews/index.html.erb +58 -0
  310. data/app/views/stay/admin/reviews/new.html.erb +14 -0
  311. data/app/views/stay/admin/reviews/show.html.erb +20 -0
  312. data/app/views/stay/admin/roles/_form.html.erb +24 -0
  313. data/app/views/stay/admin/roles/edit.html.erb +14 -0
  314. data/app/views/stay/admin/roles/index.html.erb +55 -0
  315. data/app/views/stay/admin/roles/new.html.erb +14 -0
  316. data/app/views/stay/admin/roles/show.html.erb +18 -0
  317. data/app/views/stay/admin/room_types/_form.html.erb +37 -0
  318. data/app/views/stay/admin/room_types/edit.html.erb +14 -0
  319. data/app/views/stay/admin/room_types/index.html.erb +58 -0
  320. data/app/views/stay/admin/room_types/new.html.erb +14 -0
  321. data/app/views/stay/admin/room_types/show.html.erb +19 -0
  322. data/app/views/stay/admin/rooms/_form.html.erb +136 -0
  323. data/app/views/stay/admin/rooms/edit.html.erb +14 -0
  324. data/app/views/stay/admin/rooms/index.html.erb +69 -0
  325. data/app/views/stay/admin/rooms/new.html.erb +14 -0
  326. data/app/views/stay/admin/rooms/show.html.erb +25 -0
  327. data/app/views/stay/admin/sessions/new.html.erb +36 -0
  328. data/app/views/stay/admin/shared/_common-table.html.erb +334 -0
  329. data/app/views/stay/admin/shared/_error_messages.html.erb +15 -0
  330. data/app/views/stay/admin/shared/_header.html.erb +55 -0
  331. data/app/views/stay/admin/shared/_links.html.erb +15 -0
  332. data/app/views/stay/admin/shared/_sidebar.html.erb +154 -0
  333. data/app/views/stay/admin/shared/_store_switcher.html.erb +30 -0
  334. data/app/views/stay/admin/states/_form.html.erb +39 -0
  335. data/app/views/stay/admin/states/edit.html.erb +14 -0
  336. data/app/views/stay/admin/states/index.html.erb +61 -0
  337. data/app/views/stay/admin/states/new.html.erb +14 -0
  338. data/app/views/stay/admin/states/show.html.erb +0 -0
  339. data/app/views/stay/admin/stores/_form.html.erb +30 -0
  340. data/app/views/stay/admin/stores/_tabs.html.erb +17 -0
  341. data/app/views/stay/admin/stores/edit.html.erb +14 -0
  342. data/app/views/stay/admin/stores/form/_basic.html.erb +35 -0
  343. data/app/views/stay/admin/stores/form/_emails.html.erb +34 -0
  344. data/app/views/stay/admin/stores/form/_footer.html.erb +28 -0
  345. data/app/views/stay/admin/stores/form/_internationalization.html.erb +52 -0
  346. data/app/views/stay/admin/stores/form/_seo.html.erb +42 -0
  347. data/app/views/stay/admin/stores/form/_social.html.erb +34 -0
  348. data/app/views/stay/admin/stores/index.html.erb +66 -0
  349. data/app/views/stay/admin/stores/new.html.erb +14 -0
  350. data/app/views/stay/admin/stores/translations.html.erb +8 -0
  351. data/app/views/stay/admin/taxes/_form.html.erb +29 -0
  352. data/app/views/stay/admin/taxes/edit.html.erb +14 -0
  353. data/app/views/stay/admin/taxes/index.html.erb +56 -0
  354. data/app/views/stay/admin/taxes/new.html.erb +14 -0
  355. data/app/views/stay/admin/taxes/show.html.erb +18 -0
  356. data/app/views/stay/admin/users/_address.html.erb +57 -0
  357. data/app/views/stay/admin/users/_address_form.html.erb +29 -0
  358. data/app/views/stay/admin/users/_form.html.erb +73 -0
  359. data/app/views/stay/admin/users/_sidebar.html.erb +12 -0
  360. data/app/views/stay/admin/users/addresses.html.erb +17 -0
  361. data/app/views/stay/admin/users/edit.html.erb +17 -0
  362. data/app/views/stay/admin/users/index.html.erb +74 -0
  363. data/app/views/stay/admin/users/new.html.erb +14 -0
  364. data/app/views/stay/admin/users/show.html.erb +23 -0
  365. data/app/views/stay/bookings/new.html.erb +92 -0
  366. data/app/views/stay/payments/_payment.js.erb +127 -0
  367. data/app/views/stay/payments/new.html.erb +187 -0
  368. data/app/views/stay/properties/index.html.erb +56 -0
  369. data/app/views/stay/properties/show.html.erb +137 -0
  370. data/app/views/stay/shared/_navbar.html.erb +35 -0
  371. data/app/views/stay/user_mailer/new_host_signup.html.erb +12 -0
  372. data/app/views/stay/user_mailer/welcome_email.html.erb +9 -0
  373. data/app/views/stay/users/confirmations/new.html.erb +16 -0
  374. data/app/views/stay/users/mailer/confirmation_instructions.html.erb +5 -0
  375. data/app/views/stay/users/mailer/email_changed.html.erb +7 -0
  376. data/app/views/stay/users/mailer/password_change.html.erb +3 -0
  377. data/app/views/stay/users/mailer/reset_password_instructions.html.erb +7 -0
  378. data/app/views/stay/users/mailer/unlock_instructions.html.erb +7 -0
  379. data/app/views/stay/users/passwords/edit.html.erb +37 -0
  380. data/app/views/stay/users/passwords/new.html.erb +28 -0
  381. data/app/views/stay/users/registrations/edit.html.erb +43 -0
  382. data/app/views/stay/users/registrations/new.html.erb +42 -0
  383. data/app/views/stay/users/sessions/new.html.erb +43 -0
  384. data/app/views/stay/users/shared/_error_messages.html.erb +15 -0
  385. data/app/views/stay/users/shared/_links.html.erb +25 -0
  386. data/app/views/stay/users/unlocks/new.html.erb +16 -0
  387. data/config/initializers/devise.rb +358 -0
  388. data/config/initializers/friendly_id.rb +107 -0
  389. data/config/initializers/geocoder.rb +4 -0
  390. data/config/initializers/kaminari_config.rb +14 -0
  391. data/config/initializers/mobility.rb +17 -0
  392. data/config/initializers/stripe.rb +8 -0
  393. data/config/locales/devise.en.yml +65 -0
  394. data/config/locales/devise_api.en.yml +23 -0
  395. data/config/locales/en.yml +38 -0
  396. data/config/routes.rb +187 -0
  397. data/db/migrate/20240912073149_devise_create_stay_users.rb +50 -0
  398. data/db/migrate/20240912073248_create_devise_api_tables.rb +29 -0
  399. data/db/migrate/20240912092842_create_stay_roles.rb +9 -0
  400. data/db/migrate/20240912093003_create_stay_role_users.rb +10 -0
  401. data/db/migrate/20240912093151_create_stay_properties.rb +16 -0
  402. data/db/migrate/20240912093213_create_stay_rooms.rb +12 -0
  403. data/db/migrate/20240912093240_create_stay_bookings.rb +15 -0
  404. data/db/migrate/20240912093346_create_stay_payment_methods.rb +9 -0
  405. data/db/migrate/20240912093408_create_stay_payments.rb +12 -0
  406. data/db/migrate/20240912093603_create_stay_reviews.rb +11 -0
  407. data/db/migrate/20240912093644_create_stay_chats.rb +11 -0
  408. data/db/migrate/20240912093717_create_stay_messages.rb +12 -0
  409. data/db/migrate/20240912093759_create_stay_countries.rb +14 -0
  410. data/db/migrate/20240912093824_create_stay_states.rb +11 -0
  411. data/db/migrate/20240912093903_create_stay_cities.rb +10 -0
  412. data/db/migrate/20240912094149_create_stay_addresses.rb +19 -0
  413. data/db/migrate/20240916071141_create_active_storage_tables.rb +57 -0
  414. data/db/migrate/20240918080551_add_columns_to_stay_bookings.rb +12 -0
  415. data/db/migrate/20240918101502_add_columns_to_stay_payments.rb +16 -0
  416. data/db/migrate/20240918110958_create_stay_prices.rb +13 -0
  417. data/db/migrate/20240918112045_add_columns_to_stay_rooms.rb +5 -0
  418. data/db/migrate/20240918114609_add_payment_intent_in_bookings.rb +6 -0
  419. data/db/migrate/20240919045205_add_user_to_stay_addresses.rb +5 -0
  420. data/db/migrate/20240919063400_create_stay_room_types.rb +10 -0
  421. data/db/migrate/20240919070423_rename_room_type_to_room_type_id_in_stay_rooms.rb +5 -0
  422. data/db/migrate/20240919071642_rename_status_to_state_in_stay_payments.rb +5 -0
  423. data/db/migrate/20240919100508_remove_room_id_from_stay_properties.rb +5 -0
  424. data/db/migrate/20240919111801_create_stay_line_items.rb +16 -0
  425. data/db/migrate/20240919124704_remove_room_id_from_stay_bookings.rb +5 -0
  426. data/db/migrate/20240924053658_add_is_master_in_stay_rooms.rb +5 -0
  427. data/db/migrate/20240925063733_add_zipcode_required_in_countries.rb +5 -0
  428. data/db/migrate/20240926055951_remove_city_reference_from_stay_addresses.rb +5 -0
  429. data/db/migrate/20240927055935_change_price_per_night_to_decimal_in_stay_rooms.rb +5 -0
  430. data/db/migrate/20240927063023_add_first_name_to_stay_addresses.rb +8 -0
  431. data/db/migrate/20240927070538_change_description_to_text_in_stay_properties.rb +6 -0
  432. data/db/migrate/20240927111110_create_stay_stores.rb +33 -0
  433. data/db/migrate/20240930095921_create_stay_cancellation_policies.rb +9 -0
  434. data/db/migrate/20240930100157_add_cancellation_id_to_stay_properties.rb +5 -0
  435. data/db/migrate/20240930152331_create_stay_store_translations.rb +22 -0
  436. data/db/migrate/20240930162337_create_stay_store_properties.rb +10 -0
  437. data/db/migrate/20241001101816_create_stay_property_categories.rb +8 -0
  438. data/db/migrate/20241001102646_add_property_category_id_to_property.rb +5 -0
  439. data/db/migrate/20241001103758_create_stay_property_types.rb +9 -0
  440. data/db/migrate/20241001111306_add_store_id_to_stay_bookings.rb +5 -0
  441. data/db/migrate/20241001160330_add_deleted_at_to_stay_store_translations.rb +5 -0
  442. data/db/migrate/20241004061647_add_field_to_stay_properties.rb +20 -0
  443. data/db/migrate/20241004062057_create_stay_bed_types.rb +9 -0
  444. data/db/migrate/20241004062251_add_details_to_stay_rooms.rb +9 -0
  445. data/db/migrate/20241004070456_add_property_type_id_to_stay_property_categories.rb +6 -0
  446. data/db/migrate/20241004105821_create_stay_amenity_categories.rb +8 -0
  447. data/db/migrate/20241004112600_create_stay_amenities.rb +9 -0
  448. data/db/migrate/20241004115207_create_stay_house_rules.rb +8 -0
  449. data/db/migrate/20241004115317_create_stay_additional_rules.rb +9 -0
  450. data/db/migrate/20241004115608_create_stay_property_amenities.rb +9 -0
  451. data/db/migrate/20241004115615_create_stay_room_amenities.rb +9 -0
  452. data/db/migrate/20241007051646_add_preferences_to_stay_users.rb +5 -0
  453. data/db/migrate/20241007165530_create_stay_creates.rb +10 -0
  454. data/db/migrate/20241008111346_add_deleted_at_to_stay_users.rb +7 -0
  455. data/db/migrate/20241008151958_add_price_per_night_in_stay_properties.rb +5 -0
  456. data/db/migrate/20241010071855_add_zipcode_to_stay_property.rb +8 -0
  457. data/db/migrate/20241014085441_add_amenity_type_in_amenities.rb +5 -0
  458. data/db/migrate/20241015055940_create_stay_features.rb +10 -0
  459. data/db/migrate/20241015060804_create_stay_property_features.rb +9 -0
  460. data/db/migrate/20241015060854_create_stay_room_features.rb +9 -0
  461. data/db/migrate/20241016055948_create_stay_tax.rb +9 -0
  462. data/db/migrate/20241016060354_create_stay_property_tax.rb +11 -0
  463. data/db/migrate/20241016091120_remove_column_from_stay_properties_categories.rb +6 -0
  464. data/db/migrate/20241016091829_add_column_to_stay_property.rb +5 -0
  465. data/db/migrate/20241016112707_add_country_to_stay_properties.rb +6 -0
  466. data/db/migrate/20241016153847_add_state_to_properties.rb +7 -0
  467. data/db/migrate/20241016170528_up_date_price_column_in_stay_property.rb +5 -0
  468. data/db/migrate/20241017031520_add_new_field_to_stay_properties.rb +8 -0
  469. data/db/migrate/20241017051450_add_column_to_stay_chat.rb +7 -0
  470. data/db/migrate/20241018090158_create_stay_stay_credit_cards.rb +16 -0
  471. data/db/migrate/20241021090444_create_stay_stay_user_paypals.rb +9 -0
  472. data/db/migrate/20241024065949_add_stay_property_to_stay_b0okings.rb +5 -0
  473. data/db/migrate/20241027062126_add_events_to_stay_messages.rb +8 -0
  474. data/db/migrate/20241028105007_create_stay_invoices.rb +15 -0
  475. data/db/migrate/20241028105323_create_stay_expenses.rb +10 -0
  476. data/db/migrate/20241028105331_create_stay_discounts.rb +10 -0
  477. data/db/migrate/20241101044618_create_stay_booking_queries.rb +17 -0
  478. data/db/migrate/20241101093057_add_read_at_to_stay_messages.rb +5 -0
  479. data/db/migrate/20241113061248_modify_cancellation_policy_on_properties.rb +7 -0
  480. data/db/migrate/20241118064033_rename_columns_in_stay_properties.rb +7 -0
  481. data/db/migrate/20241127060541_add_new_fields_to_property.rb +11 -0
  482. data/db/migrate/20241128111832_add_name_to_stay_rooms.rb +5 -0
  483. data/db/migrate/20241129063922_change_amenity_category_null_on_stay_amenities.rb +5 -0
  484. data/db/migrate/20241205092935_add_slug_to_stay_properties.rb +6 -0
  485. data/db/migrate/20241205092943_create_friendly_id_slugs.rb +21 -0
  486. data/db/migrate/20241217044817_add_deleted_atto_stay_property.rb +6 -0
  487. data/db/migrate/20241230064432_modify_property_category_on_properties.rb +7 -0
  488. data/db/seeds.rb +1 -0
  489. data/lib/stay/controller_helpers/currency.rb +54 -0
  490. data/lib/stay/controller_helpers/store.rb +37 -0
  491. data/lib/stay/engine.rb +16 -0
  492. data/lib/stay/money.rb +77 -0
  493. data/lib/stay/version.rb +3 -0
  494. data/lib/stay.rb +16 -0
  495. data/lib/tasks/stay_tasks.rake +4 -0
  496. metadata +967 -0
@@ -0,0 +1,107 @@
1
+ module Stay
2
+ module LocaleHelper
3
+ def all_locales_options
4
+ supported_locales_for_all_stores.map { |locale| locale_presentation(locale) }
5
+ end
6
+
7
+ def available_locales_options
8
+ available_locales.map { |locale| locale_presentation(locale) }
9
+ end
10
+
11
+ def supported_locales_options
12
+ return if current_store.nil?
13
+
14
+ current_store.supported_locales_list.map { |locale| locale_presentation(locale) }
15
+ end
16
+
17
+ def supported_locales_for_all_stores
18
+ @supported_locales_for_all_stores ||= available_locales
19
+ end
20
+
21
+ def available_locales
22
+ locales_from_i18n = I18n.available_locales
23
+ locales =
24
+ if defined?(StayI18n)
25
+ (StayI18n::Locale.all << :en).map(&:to_sym)
26
+ else
27
+ [Rails.application.config.i18n.default_locale, I18n.locale, :en]
28
+ end
29
+
30
+ (locales + locales_from_i18n).uniq.compact
31
+ end
32
+
33
+ def locale_presentation(locale)
34
+ if I18n.exists?('stay.i18n.this_file_language', locale: locale, fallback: false)
35
+ [locale_full_name(locale), locale.to_s]
36
+ elsif defined?(StayI18n::Locale) && (language_name = StayI18n::Locale.local_language_name(locale))
37
+ ["#{language_name} (#{locale})", locale.to_s]
38
+ elsif locale.to_s == 'en'
39
+ ['English (US)', 'en']
40
+ else
41
+ [locale, locale.to_s]
42
+ end
43
+ end
44
+
45
+ def locale_full_name(locale)
46
+ I18n.t('i18n.this_file_language', locale: locale)
47
+ end
48
+
49
+ def should_render_locale_dropdown?
50
+ return false if current_store.nil?
51
+
52
+ current_store.supported_locales_list.size > 1
53
+ end
54
+
55
+ # New methods added from the controller
56
+ def current_locale
57
+ @current_locale ||= if user_locale?
58
+ current_user.selected_locale
59
+ elsif params_locale?
60
+ params[:locale]
61
+ elsif config_locale?
62
+ config_locale
63
+ else
64
+ current_store&.default_locale || Rails.application.config.i18n.default_locale || I18n.default_locale
65
+ end
66
+ end
67
+
68
+ def supported_locales
69
+ @supported_locales ||= current_store&.supported_locales_list
70
+ end
71
+
72
+ def supported_locale?(locale_code)
73
+ return false if supported_locales.nil?
74
+
75
+ supported_locales.include?(locale_code&.to_s)
76
+ end
77
+
78
+ def params_locale?
79
+ params[:locale].present? && supported_locale?(params[:locale])
80
+ end
81
+
82
+ def user_locale?
83
+ Stay::Config.use_user_locale && current_user && supported_locale?(current_user.selected_locale)
84
+ end
85
+
86
+ def config_locale?
87
+ respond_to?(:config_locale, true) && config_locale.present?
88
+ end
89
+
90
+ def locale_param
91
+ return if I18n.locale.to_s == current_store.default_locale || current_store.default_locale.nil?
92
+
93
+ I18n.locale.to_s
94
+ end
95
+
96
+ # Optionally, you can keep this as a helper method for views
97
+ def find_with_fallback_default_locale(&block)
98
+ result = begin
99
+ block.call
100
+ rescue ActiveRecord::RecordNotFound => _e
101
+ nil
102
+ end
103
+
104
+ result || Mobility.with_locale(current_store.default_locale) { block.call }
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,4 @@
1
+ module Stay
2
+ class ApplicationJob < ActiveJob::Base
3
+ end
4
+ end
@@ -0,0 +1,6 @@
1
+ module Stay
2
+ class ApplicationMailer < ActionMailer::Base
3
+ default from: "from@example.com"
4
+ layout "mailer"
5
+ end
6
+ end
@@ -0,0 +1,13 @@
1
+ module Stay
2
+ class UserMailer < ApplicationMailer
3
+ def welcome_email(user)
4
+ @user = user
5
+ mail(to: @user.email, subject: "Welcome #{@user.full_name}")
6
+ end
7
+
8
+ def new_host_signup(user)
9
+ @user = user
10
+ mail(to: Stay::User.admin_emails, subject: "New Host Signup")
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,25 @@
1
+ module Stay
2
+ module TranslatableResource
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ extend Mobility
7
+ default_scope { i18n }
8
+
9
+ def get_field_with_locale(locale, field_name, fallback: false)
10
+ # method will return nil if no translation is present due to fallback: false setting
11
+ public_send(field_name, locale: locale, fallback: fallback)
12
+ end
13
+ end
14
+
15
+ class_methods do
16
+ def translatable_fields
17
+ const_get(:TRANSLATABLE_FIELDS)
18
+ end
19
+
20
+ def translation_table_alias
21
+ "#{self::Translation.table_name}_#{Mobility.locale}"
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,5 @@
1
+ module Stay
2
+ class AdditionalRule < ApplicationRecord
3
+ belongs_to :property, class_name: "Stay::Property"
4
+ end
5
+ end
@@ -0,0 +1,40 @@
1
+ module Stay
2
+ class Address < ApplicationRecord
3
+ belongs_to :state, class_name: 'Stay::State'
4
+ belongs_to :country, class_name: 'Stay::Country'
5
+ has_many :properties, class_name: 'Stay::Property'
6
+
7
+ belongs_to :user, class_name: 'Stay::Address', optional: true
8
+ validates :phone, format: { with: /\A\d{10}\z/, message: "number must be valid." }
9
+
10
+ # geocoded_by :full_address
11
+ # after_validation :geocode, if: ->(obj){ obj.address1.present? && obj.zipcode.present? }
12
+
13
+
14
+ NO_ZIPCODE_ISO_CODES ||= [
15
+ 'AO', 'AG', 'AW', 'BS', 'BZ', 'BJ', 'BM', 'BO', 'BW', 'BF', 'BI', 'CM', 'CF', 'KM', 'CG',
16
+ 'CD', 'CK', 'CUW', 'CI', 'DJ', 'DM', 'GQ', 'ER', 'FJ', 'TF', 'GAB', 'GM', 'GH', 'GD', 'GN',
17
+ 'GY', 'HK', 'IE', 'KI', 'KP', 'LY', 'MO', 'MW', 'ML', 'MR', 'NR', 'AN', 'NU', 'KP', 'PA',
18
+ 'QA', 'RW', 'KN', 'LC', 'ST', 'SC', 'SL', 'SB', 'SO', 'SR', 'SY', 'TZ', 'TL', 'TK', 'TG',
19
+ 'TO', 'TV', 'UG', 'AE', 'VU', 'YE', 'ZW'
20
+ ].freeze
21
+
22
+ # The required states listed below match those used by PayPal and Shopify.
23
+ STATES_REQUIRED = [
24
+ 'AU', 'AE', 'BR', 'CA', 'CN', 'ES', 'HK', 'IE', 'IN',
25
+ 'IT', 'MY', 'MX', 'NZ', 'PT', 'RO', 'TH', 'US', 'ZA'
26
+ ].freeze
27
+
28
+ def self.ransackable_associations(auth_object = nil)
29
+ [ "properties", "user"]
30
+ end
31
+
32
+ def self.ransackable_attributes(auth_object = nil)
33
+ ["address1", "address2", "city", "state", "country", "zipcode"]
34
+ end
35
+
36
+ def full_address
37
+ [address1, address2, city.try(:name), state.try(:name), country.try(:name), zipcode].compact.join(', ')
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,13 @@
1
+ module Stay
2
+ class Amenity < ApplicationRecord
3
+ belongs_to :amenity_category, class_name: "Stay::AmenityCategory", optional: true
4
+ has_many :property_amenities, class_name: "Stay::PropertyAmenity"
5
+ has_many :properties, through: :property_amenities, class_name: "Stay::Property"
6
+ enum :amenity_type, { property: 0, room: 1 }
7
+
8
+ has_many :room_amenities, class_name: "Stay::RoomAmenity"
9
+ has_many :rooms, through: :room_amenities, class_name: "Stay::Room"
10
+
11
+ validates :name, presence: true, uniqueness: { case_sensitive: false }
12
+ end
13
+ end
@@ -0,0 +1,6 @@
1
+ module Stay
2
+ class AmenityCategory < ApplicationRecord
3
+ # has_many :amenities, class_name: "Stay::Amenity"
4
+ validates :name, presence: true, uniqueness: { case_sensitive: false }
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ module Stay
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ end
@@ -0,0 +1,7 @@
1
+ module Stay
2
+ class BedType < ApplicationRecord
3
+ has_many :rooms, class_name: "Stay::Room"
4
+
5
+ validates :name, presence: true, uniqueness: { case_sensitive: false }, format: { without: /\s/, error: "must contain no spaces" }
6
+ end
7
+ end
@@ -0,0 +1,199 @@
1
+ module Stay
2
+ class Booking < ApplicationRecord
3
+ PAYMENT_STATES = %w[failed paid]
4
+ STATUSES = %w[booking_request invoice_sent confirmed canceled completed].freeze
5
+
6
+ belongs_to :user, class_name: "Stay::User"
7
+ belongs_to :canceler, class_name: "Stay::User", foreign_key: "canceler_id", optional: true
8
+ has_many :reviews, class_name: "Stay::Review", dependent: :destroy
9
+ has_many :payments, class_name: "Stay::Payment", dependent: :destroy
10
+ has_many :line_items, class_name: "Stay::LineItem", dependent: :destroy
11
+ has_many :rooms, through: :line_items
12
+ belongs_to :property, -> { with_deleted }, class_name: "Stay::Property", foreign_key: "property_id"
13
+ belongs_to :store, class_name: "Stay::Store"
14
+ has_one :chat, class_name: "Stay::Chat", dependent: :destroy
15
+ has_one :booking_query, class_name: "Stay::BookingQuery", dependent: :destroy
16
+ has_one :invoice, dependent: :destroy
17
+
18
+ scope :complete, -> { where.not(completed_at: nil).where(payment_state: "paid") }
19
+ scope :incomplete, -> { where(completed_at: nil).where(payment_state: [ "failed", nil ]).where.not(status: :canceled) }
20
+ scope :not_canceled, -> { where.not(status: "canceled") }
21
+ scope :confirmed, -> { where(status: "confirmed").where(payment_state: "paid") }
22
+
23
+ after_commit :booking_completed_at
24
+ before_create :link_by_email, :generate_number
25
+ before_validation :ensure_store_presence
26
+ after_commit :booking_completed_at, if: :booking_completed?
27
+ after_commit :update_payment_status, on: [ :update ]
28
+ after_create :booking_room_count
29
+
30
+ accepts_nested_attributes_for :line_items, allow_destroy: true
31
+ accepts_nested_attributes_for :payments, allow_destroy: true
32
+ accepts_nested_attributes_for :invoice, allow_destroy: true
33
+
34
+ validates :status, inclusion: { in: STATUSES }
35
+ validates :number, uniqueness: true
36
+
37
+ state_machine :status, initial: :booking_request do
38
+ state :booking_request
39
+ state :invoice_sent
40
+ state :confirmed
41
+ state :canceled
42
+ state :completed
43
+
44
+ event :send_invoice do
45
+ transition booking_request: :invoice_sent
46
+ transition invoice_sent: :booking_request, if: :invoice_deleted?
47
+ end
48
+
49
+ event :confirm do
50
+ transition invoice_sent: :confirmed
51
+ end
52
+
53
+ event :cancel do
54
+ transition [ :booking_request, :confirmed ] => :canceled
55
+ end
56
+
57
+ event :complete do
58
+ transition confirmed: :completed
59
+ end
60
+ end
61
+
62
+ def self.ransackable_attributes(auth_object = nil)
63
+ %w[number]
64
+ end
65
+
66
+ def self.ransackable_association(auth_object = nil)
67
+ %w[user]
68
+ end
69
+
70
+ def update_payment_status
71
+ update_columns(payment_state: "paid") if payments.exists?(state: "paid")
72
+ end
73
+
74
+ def invoice_deleted?
75
+ invoice.nil?
76
+ end
77
+
78
+ def canceled_by(user)
79
+ transaction do
80
+ cancel!
81
+ update_columns(
82
+ canceler_id: user.id,
83
+ canceled_at: Time.current
84
+ )
85
+ end
86
+ end
87
+
88
+ def booking_completed?
89
+ completed?
90
+ end
91
+
92
+ def booking_completed_at
93
+ update_columns(completed_at: Time.current)
94
+ end
95
+
96
+ def after_cancel
97
+ payments.completed.each(&:cancel!)
98
+ send_cancel_email
99
+ update(status: "canceled", canceled_at: Time.current)
100
+ end
101
+
102
+ # Associates the specified user with the booking.
103
+ def associate_user!(user, override_email = true)
104
+ self.user = user
105
+ self.email = user.email if override_email
106
+ self.created_by ||= user
107
+ changes = slice(:user_id, :email, :created_by_id)
108
+
109
+ # immediately persist the changes we just made, but don't use save
110
+ # since we might have an invalid address associated
111
+ self.class.unscoped.where(id: self).update_all(changes)
112
+ end
113
+
114
+ def generate_number
115
+ loop do
116
+ numeric_part = rand.to_s[2..11]
117
+ self.number = "S#{numeric_part}"
118
+ break unless self.class.exists?(number: number)
119
+ end
120
+ end
121
+
122
+ def add_rooms_and_calculate(selected_rooms, booking_params, property)
123
+ total_price = 0
124
+ total_guests = 0
125
+
126
+ selected_rooms.each do |room_id|
127
+ room = property.rooms.find(room_id.to_i)
128
+ number_of_guests = booking_params[:bookings][room_id][:number_of_guests].to_i
129
+ price = room.price_per_month * (check_out_date - check_in_date).to_i
130
+
131
+ line_items.build(room: room, quantity: number_of_guests, price: price)
132
+
133
+ total_price += price
134
+ total_guests += number_of_guests
135
+ end
136
+
137
+ self.total = total_price
138
+ self.number_of_guests = total_guests
139
+ end
140
+
141
+ def ensure_store_presence
142
+ self.store ||= Stay::Store.default
143
+ end
144
+
145
+ def extra_guest_amount
146
+ return 0 unless property&.allow_extra_guest
147
+ return 0 unless property&.guest_number
148
+ return 0 unless property.allow_extra_guest && number_of_guests > property.guest_number
149
+
150
+ extra_guest = number_of_guests - property.guest_number
151
+ property.per_extra_guest_amount.to_f * extra_guest
152
+ end
153
+
154
+ def calculate_item_total
155
+ property.shared_property ? line_items.sum(:price) : property.price_per_month
156
+ end
157
+
158
+ def calculate_tax_total
159
+ property.property_taxes
160
+ .uniq { |property_tax| property_tax.tax_id }
161
+ .sum(&:value)
162
+ end
163
+
164
+ def calculate_cleaning_fee
165
+ property.cleaning_fee || 0
166
+ end
167
+
168
+ def calculate_city_fee
169
+ property.city_fee || 0
170
+ end
171
+
172
+ def calculate_totals
173
+ item_total = calculate_item_total || 0
174
+ invoice_total = invoice&.total || 0
175
+ tax_total = calculate_tax_total
176
+ extra_guest_total = extra_guest_amount
177
+ cleaning_fee = calculate_cleaning_fee
178
+ city_fee = calculate_city_fee
179
+ total_amount = item_total + invoice_total + tax_total + extra_guest_total + cleaning_fee + city_fee
180
+
181
+ update_columns(
182
+ item_total: item_total,
183
+ total: invoice_total,
184
+ total_amount: total_amount
185
+ ) if item_total != self.item_total || invoice_total != self.total || total_amount != self.total_amount
186
+ end
187
+
188
+
189
+ def booking_room_count
190
+ self.update_column(:room_count, self.rooms.count)
191
+ end
192
+
193
+ private
194
+
195
+ def link_by_email
196
+ self.email = user.email if user
197
+ end
198
+ end
199
+ end
@@ -0,0 +1,23 @@
1
+ module Stay
2
+ class BookingQuery < ApplicationRecord
3
+ ACCEPTED = "accepted".freeze
4
+ belongs_to :chat
5
+ belongs_to :booking, optional: true
6
+ belongs_to :property
7
+ belongs_to :user
8
+ enum :state, { send_message: 0, request_change: 1, booking_invitation: 2, accepted: 3, rejected: 4 }
9
+ after_initialize :set_default_state, if: :new_record?
10
+ scope :ongoing, -> { where.not(state: ACCEPTED) }
11
+ scope :date_range, ->(check_in, check_out) { where(check_in_date: check_in, check_out_date: check_out) }
12
+ scope :without_booking, -> { where(booking_id: nil) }
13
+ scope :for_current_user, ->(user) { where(user: user) }
14
+
15
+ def set_default_state
16
+ self.state ||= :send_message
17
+ end
18
+
19
+ def check_date_change
20
+ self.state = :request_change if check_in_date_changed? || check_out_date_changed?
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,6 @@
1
+ module Stay
2
+ class CancellationPolicy < ApplicationRecord
3
+ has_many :properties, class_name: "Stay::Property", dependent: :nullify
4
+ validates :name, presence: true, uniqueness: { case_sensitive: false }
5
+ end
6
+ end
@@ -0,0 +1,23 @@
1
+ module Stay
2
+ class Chat < ApplicationRecord
3
+ belongs_to :sender, class_name: "Stay::User", foreign_key: "sender_id"
4
+ belongs_to :receiver, class_name: "Stay::User", foreign_key: "receiver_id"
5
+ has_many :messages, class_name: "Stay::Message", dependent: :destroy
6
+ has_one :booking_query, dependent: :destroy
7
+ belongs_to :property, class_name: "Stay::Property"
8
+ belongs_to :booking, class_name: "Stay::Booking", optional: true
9
+ scope :for_user, ->(user) { where(sender: user).or(where(receiver: user)) }
10
+ scope :get_all_messages, -> { joins(:messages) }
11
+ scope :order_by_latest_messages, -> { group("stay_chats.id").order("MAX(stay_messages.created_at) DESC") }
12
+ scope :get_unread_messages_chat, -> { joins(:messages).where(stay_messages: { read_at: nil }) }
13
+ enum :chat_event, { booking_request: 0, booking_accept: 1, booking_reject: 2, booking_request_change: 3 }
14
+
15
+ scope :between, ->(sender_id, receiver_id) do
16
+ where("(sender_id = ? AND
17
+ receiver_id =?) OR
18
+ (sender_id = ? AND receiver_id =?)",
19
+ sender_id, receiver_id, receiver_id, sender_id
20
+ )
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,9 @@
1
+ module Stay
2
+ class City < ApplicationRecord
3
+ belongs_to :state, class_name: 'Stay::State', foreign_key: 'state_id'
4
+
5
+ def self.ransackable_attributes(auth_object = nil)
6
+ ["name"]
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,23 @@
1
+ module Stay
2
+ class Country < ApplicationRecord
3
+ has_many :addresses
4
+ has_many :states, class_name: 'Stay::State', dependent: :destroy
5
+ validates :name, presence: true
6
+
7
+ def self.ransackable_attributes(auth_object = nil)
8
+ ["name"]
9
+ end
10
+
11
+ def self.default(store = nil)
12
+ store ||= Stay::Store.default
13
+ country_id = store.default_country_id
14
+ default = find_by(id: country_id) if country_id.present?
15
+ default || find_by(iso: 'US') || first
16
+ end
17
+
18
+ def default?(store = nil)
19
+ store ||= Stay::Store.default
20
+ self == store.default_country
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,6 @@
1
+ module Stay
2
+ class CreditCard < ApplicationRecord
3
+ belongs_to :user, class_name: "Stay::User"
4
+ belongs_to :payment_method, class_name: "Stay::PaymentMethod", optional: :true
5
+ end
6
+ end
@@ -0,0 +1,4 @@
1
+ module Stay
2
+ class Discount < ApplicationRecord
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Stay
2
+ class Expense < ApplicationRecord
3
+ end
4
+ end
@@ -0,0 +1,11 @@
1
+ module Stay
2
+ class Feature < ApplicationRecord
3
+ has_many :property_features, class_name: "Stay::PropertyFeature"
4
+ has_many :properties, through: :property_features, class_name: "Stay::Property"
5
+ has_many :room_features, class_name: "Stay::RoomFeature"
6
+ has_many :rooms, through: :room_features, class_name: "Stay::Room"
7
+
8
+ enum :feature_type, { property: 0, room: 1 }
9
+ validates :name, presence: true, uniqueness: { case_sensitive: false }
10
+ end
11
+ end
@@ -0,0 +1,4 @@
1
+ module Stay
2
+ class HouseRule < ApplicationRecord
3
+ end
4
+ end
@@ -0,0 +1,23 @@
1
+ module Stay
2
+ class Invoice < ApplicationRecord
3
+ belongs_to :booking
4
+ has_many :expenses, dependent: :destroy
5
+ has_many :discounts, dependent: :destroy
6
+ after_commit :calculate_totals, on: [:create, :update]
7
+
8
+ accepts_nested_attributes_for :expenses, allow_destroy: true
9
+ accepts_nested_attributes_for :discounts, allow_destroy: true
10
+
11
+ private
12
+
13
+ def calculate_totals
14
+ subtotal = expenses.any? ? expenses.pluck(:amount).sum : 0
15
+ discount_amount = discounts.any? ? discounts.pluck(:amount).sum : 0
16
+ total = subtotal - discount_amount
17
+
18
+ if subtotal != self.subtotal || discount_amount != self.discount_amount || total != self.total
19
+ update_columns(subtotal: subtotal, discount_amount: discount_amount, total: total)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,7 @@
1
+ module Stay
2
+ class LineItem < ApplicationRecord
3
+ belongs_to :booking, class_name: "Stay::Booking"
4
+ belongs_to :room, class_name: "Stay::Room"
5
+ has_one :property, through: :room
6
+ end
7
+ end
@@ -0,0 +1,30 @@
1
+ module Stay
2
+ class Message < ApplicationRecord
3
+ belongs_to :sender, foreign_key: :sender_id, class_name: "Stay::User", optional: true
4
+ belongs_to :receiver, foreign_key: :receiver_id, class_name: "Stay::User", optional: true
5
+ belongs_to :chat, class_name: "Stay::Chat"
6
+ has_many_attached :attachments
7
+ enum :event_for, { host: 0, student: 1, both: 2 }
8
+ after_initialize :set_default_event_for, if: :new_record?
9
+
10
+ def read?
11
+ read_at.present?
12
+ end
13
+
14
+ def host_messages
15
+ where(event_for: [ "host", "both" ])
16
+ end
17
+
18
+ def student_messages
19
+ where(event_for: [ "student", "both" ])
20
+ end
21
+
22
+ def attachments_urls
23
+ attachments.map { |image| image.url }
24
+ end
25
+
26
+ def set_default_event_for
27
+ self.event_for ||= :both
28
+ end
29
+ end
30
+ end