Compare commits
No commits in common. "998706da97f0491b5355fdeed3dbdf62b7bb70a4" and "278faa73194e9f2208b723baaff597bfaabbcc6b" have entirely different histories.
998706da97
...
278faa7319
1
Gemfile
1
Gemfile
@ -23,7 +23,6 @@ gem 'rubytree'
|
|||||||
gem 'acts_as_tenant'
|
gem 'acts_as_tenant'
|
||||||
gem 'httparty'
|
gem 'httparty'
|
||||||
gem 'rswag'
|
gem 'rswag'
|
||||||
gem 'pluck_to_hash'
|
|
||||||
|
|
||||||
group :development, :test do
|
group :development, :test do
|
||||||
gem 'annotaterb'
|
gem 'annotaterb'
|
||||||
|
116
Gemfile.lock
116
Gemfile.lock
@ -1,29 +1,29 @@
|
|||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
actioncable (8.0.0.1)
|
actioncable (8.0.0)
|
||||||
actionpack (= 8.0.0.1)
|
actionpack (= 8.0.0)
|
||||||
activesupport (= 8.0.0.1)
|
activesupport (= 8.0.0)
|
||||||
nio4r (~> 2.0)
|
nio4r (~> 2.0)
|
||||||
websocket-driver (>= 0.6.1)
|
websocket-driver (>= 0.6.1)
|
||||||
zeitwerk (~> 2.6)
|
zeitwerk (~> 2.6)
|
||||||
actionmailbox (8.0.0.1)
|
actionmailbox (8.0.0)
|
||||||
actionpack (= 8.0.0.1)
|
actionpack (= 8.0.0)
|
||||||
activejob (= 8.0.0.1)
|
activejob (= 8.0.0)
|
||||||
activerecord (= 8.0.0.1)
|
activerecord (= 8.0.0)
|
||||||
activestorage (= 8.0.0.1)
|
activestorage (= 8.0.0)
|
||||||
activesupport (= 8.0.0.1)
|
activesupport (= 8.0.0)
|
||||||
mail (>= 2.8.0)
|
mail (>= 2.8.0)
|
||||||
actionmailer (8.0.0.1)
|
actionmailer (8.0.0)
|
||||||
actionpack (= 8.0.0.1)
|
actionpack (= 8.0.0)
|
||||||
actionview (= 8.0.0.1)
|
actionview (= 8.0.0)
|
||||||
activejob (= 8.0.0.1)
|
activejob (= 8.0.0)
|
||||||
activesupport (= 8.0.0.1)
|
activesupport (= 8.0.0)
|
||||||
mail (>= 2.8.0)
|
mail (>= 2.8.0)
|
||||||
rails-dom-testing (~> 2.2)
|
rails-dom-testing (~> 2.2)
|
||||||
actionpack (8.0.0.1)
|
actionpack (8.0.0)
|
||||||
actionview (= 8.0.0.1)
|
actionview (= 8.0.0)
|
||||||
activesupport (= 8.0.0.1)
|
activesupport (= 8.0.0)
|
||||||
nokogiri (>= 1.8.5)
|
nokogiri (>= 1.8.5)
|
||||||
rack (>= 2.2.4)
|
rack (>= 2.2.4)
|
||||||
rack-session (>= 1.0.1)
|
rack-session (>= 1.0.1)
|
||||||
@ -31,35 +31,35 @@ GEM
|
|||||||
rails-dom-testing (~> 2.2)
|
rails-dom-testing (~> 2.2)
|
||||||
rails-html-sanitizer (~> 1.6)
|
rails-html-sanitizer (~> 1.6)
|
||||||
useragent (~> 0.16)
|
useragent (~> 0.16)
|
||||||
actiontext (8.0.0.1)
|
actiontext (8.0.0)
|
||||||
actionpack (= 8.0.0.1)
|
actionpack (= 8.0.0)
|
||||||
activerecord (= 8.0.0.1)
|
activerecord (= 8.0.0)
|
||||||
activestorage (= 8.0.0.1)
|
activestorage (= 8.0.0)
|
||||||
activesupport (= 8.0.0.1)
|
activesupport (= 8.0.0)
|
||||||
globalid (>= 0.6.0)
|
globalid (>= 0.6.0)
|
||||||
nokogiri (>= 1.8.5)
|
nokogiri (>= 1.8.5)
|
||||||
actionview (8.0.0.1)
|
actionview (8.0.0)
|
||||||
activesupport (= 8.0.0.1)
|
activesupport (= 8.0.0)
|
||||||
builder (~> 3.1)
|
builder (~> 3.1)
|
||||||
erubi (~> 1.11)
|
erubi (~> 1.11)
|
||||||
rails-dom-testing (~> 2.2)
|
rails-dom-testing (~> 2.2)
|
||||||
rails-html-sanitizer (~> 1.6)
|
rails-html-sanitizer (~> 1.6)
|
||||||
activejob (8.0.0.1)
|
activejob (8.0.0)
|
||||||
activesupport (= 8.0.0.1)
|
activesupport (= 8.0.0)
|
||||||
globalid (>= 0.3.6)
|
globalid (>= 0.3.6)
|
||||||
activemodel (8.0.0.1)
|
activemodel (8.0.0)
|
||||||
activesupport (= 8.0.0.1)
|
activesupport (= 8.0.0)
|
||||||
activerecord (8.0.0.1)
|
activerecord (8.0.0)
|
||||||
activemodel (= 8.0.0.1)
|
activemodel (= 8.0.0)
|
||||||
activesupport (= 8.0.0.1)
|
activesupport (= 8.0.0)
|
||||||
timeout (>= 0.4.0)
|
timeout (>= 0.4.0)
|
||||||
activestorage (8.0.0.1)
|
activestorage (8.0.0)
|
||||||
actionpack (= 8.0.0.1)
|
actionpack (= 8.0.0)
|
||||||
activejob (= 8.0.0.1)
|
activejob (= 8.0.0)
|
||||||
activerecord (= 8.0.0.1)
|
activerecord (= 8.0.0)
|
||||||
activesupport (= 8.0.0.1)
|
activesupport (= 8.0.0)
|
||||||
marcel (~> 1.0)
|
marcel (~> 1.0)
|
||||||
activesupport (8.0.0.1)
|
activesupport (8.0.0)
|
||||||
base64
|
base64
|
||||||
benchmark (>= 0.3)
|
benchmark (>= 0.3)
|
||||||
bigdecimal
|
bigdecimal
|
||||||
@ -137,7 +137,7 @@ GEM
|
|||||||
activesupport (>= 6.0.0)
|
activesupport (>= 6.0.0)
|
||||||
railties (>= 6.0.0)
|
railties (>= 6.0.0)
|
||||||
io-console (0.8.0)
|
io-console (0.8.0)
|
||||||
irb (1.14.2)
|
irb (1.14.1)
|
||||||
rdoc (>= 4.0.0)
|
rdoc (>= 4.0.0)
|
||||||
reline (>= 0.4.2)
|
reline (>= 0.4.2)
|
||||||
jbuilder (2.13.0)
|
jbuilder (2.13.0)
|
||||||
@ -176,7 +176,7 @@ GEM
|
|||||||
tomlrb (>= 1.3, < 2.1)
|
tomlrb (>= 1.3, < 2.1)
|
||||||
with_env (= 1.1.0)
|
with_env (= 1.1.0)
|
||||||
xml-simple (~> 1.1.9)
|
xml-simple (~> 1.1.9)
|
||||||
logger (1.6.3)
|
logger (1.6.2)
|
||||||
loofah (2.23.1)
|
loofah (2.23.1)
|
||||||
crass (~> 1.0.2)
|
crass (~> 1.0.2)
|
||||||
nokogiri (>= 1.12.0)
|
nokogiri (>= 1.12.0)
|
||||||
@ -222,9 +222,6 @@ GEM
|
|||||||
ast (~> 2.4.1)
|
ast (~> 2.4.1)
|
||||||
racc
|
racc
|
||||||
pg (1.5.9)
|
pg (1.5.9)
|
||||||
pluck_to_hash (1.0.2)
|
|
||||||
activerecord (>= 4.0.2)
|
|
||||||
activesupport (>= 4.0.2)
|
|
||||||
pry (0.15.0)
|
pry (0.15.0)
|
||||||
coderay (~> 1.1)
|
coderay (~> 1.1)
|
||||||
method_source (~> 1.0)
|
method_source (~> 1.0)
|
||||||
@ -245,30 +242,30 @@ GEM
|
|||||||
rack (>= 1.3)
|
rack (>= 1.3)
|
||||||
rackup (2.2.1)
|
rackup (2.2.1)
|
||||||
rack (>= 3)
|
rack (>= 3)
|
||||||
rails (8.0.0.1)
|
rails (8.0.0)
|
||||||
actioncable (= 8.0.0.1)
|
actioncable (= 8.0.0)
|
||||||
actionmailbox (= 8.0.0.1)
|
actionmailbox (= 8.0.0)
|
||||||
actionmailer (= 8.0.0.1)
|
actionmailer (= 8.0.0)
|
||||||
actionpack (= 8.0.0.1)
|
actionpack (= 8.0.0)
|
||||||
actiontext (= 8.0.0.1)
|
actiontext (= 8.0.0)
|
||||||
actionview (= 8.0.0.1)
|
actionview (= 8.0.0)
|
||||||
activejob (= 8.0.0.1)
|
activejob (= 8.0.0)
|
||||||
activemodel (= 8.0.0.1)
|
activemodel (= 8.0.0)
|
||||||
activerecord (= 8.0.0.1)
|
activerecord (= 8.0.0)
|
||||||
activestorage (= 8.0.0.1)
|
activestorage (= 8.0.0)
|
||||||
activesupport (= 8.0.0.1)
|
activesupport (= 8.0.0)
|
||||||
bundler (>= 1.15.0)
|
bundler (>= 1.15.0)
|
||||||
railties (= 8.0.0.1)
|
railties (= 8.0.0)
|
||||||
rails-dom-testing (2.2.0)
|
rails-dom-testing (2.2.0)
|
||||||
activesupport (>= 5.0.0)
|
activesupport (>= 5.0.0)
|
||||||
minitest
|
minitest
|
||||||
nokogiri (>= 1.6)
|
nokogiri (>= 1.6)
|
||||||
rails-html-sanitizer (1.6.2)
|
rails-html-sanitizer (1.6.1)
|
||||||
loofah (~> 2.21)
|
loofah (~> 2.21)
|
||||||
nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
|
nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
|
||||||
railties (8.0.0.1)
|
railties (8.0.0)
|
||||||
actionpack (= 8.0.0.1)
|
actionpack (= 8.0.0)
|
||||||
activesupport (= 8.0.0.1)
|
activesupport (= 8.0.0)
|
||||||
irb (~> 1.13)
|
irb (~> 1.13)
|
||||||
rackup (>= 1.0.0)
|
rackup (>= 1.0.0)
|
||||||
rake (>= 12.2)
|
rake (>= 12.2)
|
||||||
@ -416,7 +413,6 @@ DEPENDENCIES
|
|||||||
license_finder
|
license_finder
|
||||||
money
|
money
|
||||||
pg (~> 1.1)
|
pg (~> 1.1)
|
||||||
pluck_to_hash
|
|
||||||
pry
|
pry
|
||||||
puma (>= 5.0)
|
puma (>= 5.0)
|
||||||
rack-cors
|
rack-cors
|
||||||
|
@ -59,7 +59,7 @@ class ApplicationController < ActionController::Base
|
|||||||
def set_csrf_cookie
|
def set_csrf_cookie
|
||||||
cookies['csrf-token'] = {
|
cookies['csrf-token'] = {
|
||||||
value: form_authenticity_token,
|
value: form_authenticity_token,
|
||||||
secure: false,
|
secure: Rails.env.production?,
|
||||||
same_site: :strict
|
same_site: :strict
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
@ -9,21 +9,11 @@ class ExpensesController < ApplicationController
|
|||||||
render json: Expense.all.order(pricing_type: :asc, amount: :desc).as_json(only: %i[id name amount pricing_type])
|
render json: Expense.all.order(pricing_type: :asc, amount: :desc).as_json(only: %i[id name amount pricing_type])
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
|
||||||
Expense.create!(expense_params)
|
|
||||||
render json: {}, status: :created
|
|
||||||
end
|
|
||||||
|
|
||||||
def update
|
def update
|
||||||
Expense.find(params[:id]).update!(expense_params)
|
Expense.find(params[:id]).update!(expense_params)
|
||||||
render json: {}, status: :ok
|
render json: {}, status: :ok
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy
|
|
||||||
Expense.find(params[:id]).destroy!
|
|
||||||
render json: {}, status: :ok
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def expense_params
|
def expense_params
|
||||||
|
@ -2,43 +2,6 @@
|
|||||||
|
|
||||||
class GroupsController < ApplicationController
|
class GroupsController < ApplicationController
|
||||||
def index
|
def index
|
||||||
query_result = Groups::SummaryQuery.new.call.as_json.map(&:deep_symbolize_keys).map do |group|
|
render json: Groups::SummaryQuery.new.call.as_json
|
||||||
{
|
|
||||||
id: group[:id],
|
|
||||||
name: group[:name],
|
|
||||||
icon: group[:icon],
|
|
||||||
color: group[:color],
|
|
||||||
parent_id: group[:parent_id],
|
|
||||||
attendance: group.slice(:total, :considered, :invited, :confirmed, :declined, :tentative)
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
render json: query_result
|
|
||||||
end
|
|
||||||
|
|
||||||
def create
|
|
||||||
group = Group.create!(**group_params, parent:)
|
|
||||||
render json: group.as_json(only: %i[id name icon color parent_id]), status: :created
|
|
||||||
end
|
|
||||||
|
|
||||||
def update
|
|
||||||
group = Group.find(params[:id])
|
|
||||||
group.update!(**group_params, parent:)
|
|
||||||
render json: group.as_json(only: %i[id name icon color parent_id]), status: :ok
|
|
||||||
end
|
|
||||||
|
|
||||||
def destroy
|
|
||||||
Group.find(params[:id]).destroy!
|
|
||||||
render json: {}, status: :ok
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def parent
|
|
||||||
params[:group][:parent_id].present? ? Group.find(params[:group][:parent_id]) : nil
|
|
||||||
end
|
|
||||||
|
|
||||||
def group_params
|
|
||||||
params.expect(group: [:name, :icon, :color])
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -5,7 +5,7 @@ require 'csv'
|
|||||||
class GuestsController < ApplicationController
|
class GuestsController < ApplicationController
|
||||||
def index
|
def index
|
||||||
render json: Guest.all.includes(:group)
|
render json: Guest.all.includes(:group)
|
||||||
.left_joins(:group)
|
.joins(:group)
|
||||||
.order('groups.name' => :asc, name: :asc)
|
.order('groups.name' => :asc, name: :asc)
|
||||||
.as_json(only: %i[id name status], include: { group: { only: %i[id name] } })
|
.as_json(only: %i[id name status], include: { group: { only: %i[id name] } })
|
||||||
end
|
end
|
||||||
|
@ -1,30 +0,0 @@
|
|||||||
# Copyright (C) 2024 Manuel Bustillo
|
|
||||||
|
|
||||||
class SummaryController < ApplicationController
|
|
||||||
def index
|
|
||||||
expense_summary = Expenses::TotalQuery.new(wedding: ActsAsTenant.current_tenant).call
|
|
||||||
guest_summary = Guest.group(:status).count
|
|
||||||
render json: {
|
|
||||||
expenses: {
|
|
||||||
projected: {
|
|
||||||
total: expense_summary['total_projected'],
|
|
||||||
guests: expense_summary['projected_guests']
|
|
||||||
},
|
|
||||||
confirmed: {
|
|
||||||
total: expense_summary['total_confirmed'],
|
|
||||||
guests: expense_summary['confirmed_guests']
|
|
||||||
},
|
|
||||||
status: {
|
|
||||||
paid: 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
guests: {
|
|
||||||
total: guest_summary.except('considered').values.sum,
|
|
||||||
confirmed: guest_summary['confirmed'].to_i,
|
|
||||||
declined: guest_summary['declined'].to_i,
|
|
||||||
tentative: guest_summary['tentative'].to_i,
|
|
||||||
invited: guest_summary['invited'].to_i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,10 +0,0 @@
|
|||||||
# Copyright (C) 2024 Manuel Bustillo
|
|
||||||
|
|
||||||
class TokensController < ApplicationController
|
|
||||||
skip_before_action :authenticate_user!
|
|
||||||
skip_before_action :set_tenant
|
|
||||||
|
|
||||||
def show
|
|
||||||
head :ok
|
|
||||||
end
|
|
||||||
end
|
|
@ -38,7 +38,7 @@ class Group < ApplicationRecord
|
|||||||
|
|
||||||
scope :roots, -> { where(parent_id: nil) }
|
scope :roots, -> { where(parent_id: nil) }
|
||||||
|
|
||||||
has_many :guests, dependent: :nullify
|
has_many :guests
|
||||||
|
|
||||||
def colorize_children(generation = 1)
|
def colorize_children(generation = 1)
|
||||||
derived_colors = generation == 1 ? color.paint.palette.analogous(size: children.count) : color.paint.palette.decreasing_saturation
|
derived_colors = generation == 1 ? color.paint.palette.analogous(size: children.count) : color.paint.palette.decreasing_saturation
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
# status :integer default("considered")
|
# status :integer default("considered")
|
||||||
# created_at :datetime not null
|
# created_at :datetime not null
|
||||||
# updated_at :datetime not null
|
# updated_at :datetime not null
|
||||||
# group_id :uuid
|
# group_id :uuid not null
|
||||||
# wedding_id :uuid not null
|
# wedding_id :uuid not null
|
||||||
#
|
#
|
||||||
# Indexes
|
# Indexes
|
||||||
@ -25,7 +25,7 @@
|
|||||||
#
|
#
|
||||||
class Guest < ApplicationRecord
|
class Guest < ApplicationRecord
|
||||||
acts_as_tenant :wedding
|
acts_as_tenant :wedding
|
||||||
belongs_to :group, optional: true
|
belongs_to :group
|
||||||
|
|
||||||
enum :status, {
|
enum :status, {
|
||||||
considered: 0,
|
considered: 0,
|
||||||
|
@ -2,15 +2,8 @@
|
|||||||
|
|
||||||
module Expenses
|
module Expenses
|
||||||
class TotalQuery
|
class TotalQuery
|
||||||
private attr_reader :wedding
|
|
||||||
def initialize(wedding:)
|
|
||||||
@wedding = wedding
|
|
||||||
end
|
|
||||||
|
|
||||||
def call
|
def call
|
||||||
ActiveRecord::Base.connection.execute(
|
ActiveRecord::Base.connection.execute(query).first
|
||||||
ActiveRecord::Base.sanitize_sql_array([query, { wedding_id: wedding.id }])
|
|
||||||
).first
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
@ -19,10 +12,16 @@ module Expenses
|
|||||||
<<~SQL
|
<<~SQL
|
||||||
WITH guest_count AS (#{guest_count_per_status}),
|
WITH guest_count AS (#{guest_count_per_status}),
|
||||||
expense_summary AS (#{expense_summary})
|
expense_summary AS (#{expense_summary})
|
||||||
SELECT guest_count.confirmed as confirmed_guests,
|
SELECT expense_summary.fixed,
|
||||||
guest_count.projected as projected_guests,
|
expense_summary.fixed_count,
|
||||||
expense_summary.fixed + expense_summary.variable * guest_count.confirmed as total_confirmed,
|
expense_summary.variable,
|
||||||
expense_summary.fixed + expense_summary.variable * guest_count.projected as total_projected
|
expense_summary.variable_count,
|
||||||
|
expense_summary.total_count,
|
||||||
|
guest_count.confirmed as confirmed_guests,
|
||||||
|
guest_count.projected as projected_guests,
|
||||||
|
expense_summary.fixed + expense_summary.variable * guest_count.confirmed as total,
|
||||||
|
expense_summary.fixed + expense_summary.variable * guest_count.projected as max_projected,
|
||||||
|
(expense_summary.fixed + expense_summary.variable * guest_count.confirmed) / guest_count.confirmed as per_person
|
||||||
FROM guest_count, expense_summary;
|
FROM guest_count, expense_summary;
|
||||||
SQL
|
SQL
|
||||||
end
|
end
|
||||||
@ -30,19 +29,20 @@ module Expenses
|
|||||||
def expense_summary
|
def expense_summary
|
||||||
<<~SQL
|
<<~SQL
|
||||||
SELECT coalesce(sum(amount) filter (where pricing_type = 'fixed'), 0) as fixed,
|
SELECT coalesce(sum(amount) filter (where pricing_type = 'fixed'), 0) as fixed,
|
||||||
coalesce(sum(amount) filter (where pricing_type = 'per_person'), 0) as variable
|
coalesce(count(amount) filter (where pricing_type = 'fixed'), 0) as fixed_count,
|
||||||
|
coalesce(sum(amount) filter (where pricing_type = 'per_person'), 0) as variable,
|
||||||
|
coalesce(count(amount) filter (where pricing_type = 'per_person'), 0) as variable_count,
|
||||||
|
count(*) as total_count
|
||||||
FROM expenses
|
FROM expenses
|
||||||
WHERE wedding_id = :wedding_id
|
|
||||||
SQL
|
SQL
|
||||||
end
|
end
|
||||||
|
|
||||||
def guest_count_per_status
|
def guest_count_per_status
|
||||||
<<~SQL
|
<<~SQL
|
||||||
SELECT COALESCE(count(*) filter(where status = #{Guest.statuses['confirmed']}), 0) as confirmed,
|
SELECT COALESCE(count(*) filter(where status = #{Guest.statuses["confirmed"]}), 0) as confirmed,
|
||||||
COALESCE(count(*) filter(where status IN (#{Guest.statuses.values_at('confirmed', 'invited', 'tentative').join(',')})), 0) as projected
|
COALESCE(count(*) filter(where status IN (#{Guest.statuses.values_at("confirmed", "invited", "tentative").join(",")})), 0) as projected
|
||||||
FROM guests
|
FROM guests
|
||||||
WHERE wedding_id = :wedding_id
|
|
||||||
SQL
|
SQL
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
@ -3,19 +3,29 @@
|
|||||||
module Groups
|
module Groups
|
||||||
class SummaryQuery
|
class SummaryQuery
|
||||||
def call
|
def call
|
||||||
Group.left_joins(:guests).group(:id).pluck_to_hash(
|
ActiveRecord::Base.connection.execute(query).to_a
|
||||||
:id,
|
end
|
||||||
:name,
|
|
||||||
:icon,
|
private
|
||||||
:parent_id,
|
|
||||||
:color,
|
def query
|
||||||
Arel.sql('count(*) filter (where status IS NOT NULL) as total'),
|
<<~SQL.squish
|
||||||
Arel.sql('count(*) filter (where status = 0) as considered'),
|
SELECT
|
||||||
Arel.sql('count(*) filter (where status = 10) as invited'),
|
groups.id,
|
||||||
Arel.sql('count(*) filter (where status = 20) as confirmed'),
|
groups.name,
|
||||||
Arel.sql('count(*) filter (where status = 30) as declined'),
|
groups.icon,
|
||||||
Arel.sql('count(*) filter (where status = 40) as tentative'),
|
groups.parent_id,
|
||||||
)
|
groups.color,
|
||||||
|
count(*) filter (where status IS NOT NULL) as total,
|
||||||
|
count(*) filter (where status = 0) as considered,
|
||||||
|
count(*) filter (where status = 10) as invited,
|
||||||
|
count(*) filter (where status = 20) as confirmed,
|
||||||
|
count(*) filter (where status = 30) as declined,
|
||||||
|
count(*) filter (where status = 40) as tentative
|
||||||
|
FROM groups
|
||||||
|
LEFT JOIN guests on groups.id = guests.group_id
|
||||||
|
GROUP BY groups.id
|
||||||
|
SQL
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -29,7 +29,7 @@ module VNS
|
|||||||
@best_score = @target_function.call(@best_solution)
|
@best_score = @target_function.call(@best_solution)
|
||||||
|
|
||||||
self.class.sequence(@perturbations).each do |perturbation|
|
self.class.sequence(@perturbations).each do |perturbation|
|
||||||
optimize(perturbation)
|
optimize(perturbation.new(@best_solution))
|
||||||
end
|
end
|
||||||
|
|
||||||
@best_solution
|
@best_solution
|
||||||
@ -37,22 +37,15 @@ module VNS
|
|||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def optimize(perturbation_klass)
|
def optimize(perturbation)
|
||||||
loop do
|
perturbation.each do |alternative_solution|
|
||||||
optimized = false
|
score = @target_function.call(alternative_solution)
|
||||||
|
next if score >= @best_score
|
||||||
|
|
||||||
perturbation_klass.new(@best_solution).each do |alternative_solution|
|
@best_solution = alternative_solution.deep_dup
|
||||||
score = @target_function.call(alternative_solution)
|
@best_score = score
|
||||||
next if score >= @best_score
|
|
||||||
|
|
||||||
@best_solution = alternative_solution.deep_dup
|
return optimize(perturbation.class.new(@best_solution))
|
||||||
@best_score = score
|
|
||||||
optimized = true
|
|
||||||
|
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
return unless optimized
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -92,9 +92,6 @@ Rails.application.configure do
|
|||||||
# "example.com", # Allow requests from example.com
|
# "example.com", # Allow requests from example.com
|
||||||
# /.*\.example\.com/ # Allow requests from subdomains like `www.example.com`
|
# /.*\.example\.com/ # Allow requests from subdomains like `www.example.com`
|
||||||
# ]
|
# ]
|
||||||
|
|
||||||
config.hosts << "app.libreweddingplanner.org"
|
|
||||||
|
|
||||||
# Skip DNS rebinding protection for the default health check endpoint.
|
# Skip DNS rebinding protection for the default health check endpoint.
|
||||||
config.host_authorization = { exclude: ->(request) { request.path == "/up" } }
|
# config.host_authorization = { exclude: ->(request) { request.path == "/up" } }
|
||||||
end
|
end
|
||||||
|
@ -2,16 +2,6 @@
|
|||||||
|
|
||||||
Rails.application.routes.draw do
|
Rails.application.routes.draw do
|
||||||
mount LetterOpenerWeb::Engine, at: "/letter_opener" if Rails.env.development?
|
mount LetterOpenerWeb::Engine, at: "/letter_opener" if Rails.env.development?
|
||||||
get 'token' => 'tokens#show', as: :token
|
|
||||||
get 'up' => 'rails/health#show', as: :rails_health_check
|
|
||||||
|
|
||||||
resources :captcha, only: :create do
|
|
||||||
get 'v2/media', to: 'captcha#media', on: :collection, as: :media
|
|
||||||
end
|
|
||||||
|
|
||||||
mount Rswag::Ui::Engine => '/api-docs'
|
|
||||||
mount Rswag::Api::Engine => '/api-docs'
|
|
||||||
|
|
||||||
scope ":slug", constraints: { slug: Wedding::SLUG_REGEX } do
|
scope ":slug", constraints: { slug: Wedding::SLUG_REGEX } do
|
||||||
devise_for :users, skip: [:registration, :session, :confirmation]
|
devise_for :users, skip: [:registration, :session, :confirmation]
|
||||||
devise_scope :user do
|
devise_scope :user do
|
||||||
@ -23,16 +13,24 @@ Rails.application.routes.draw do
|
|||||||
get '/users/confirmation', to: 'users/confirmations#show', as: :confirmation
|
get '/users/confirmation', to: 'users/confirmations#show', as: :confirmation
|
||||||
end
|
end
|
||||||
|
|
||||||
resources :groups, only: %i[index create update destroy]
|
resources :groups, only: :index
|
||||||
resources :guests, only: %i[index create update destroy] do
|
resources :guests, only: %i[index create update destroy] do
|
||||||
post :bulk_update, on: :collection
|
post :bulk_update, on: :collection
|
||||||
end
|
end
|
||||||
resources :expenses, only: %i[index create update destroy] do
|
resources :expenses, only: %i[index update] do
|
||||||
get :summary, on: :collection
|
get :summary, on: :collection
|
||||||
end
|
end
|
||||||
resources :tables_arrangements, only: %i[index show]
|
resources :tables_arrangements, only: %i[index show]
|
||||||
resources :summary, only: :index
|
|
||||||
|
|
||||||
root to: redirect("/%{slug}")
|
root to: redirect("/%{slug}")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
resources :captcha, only: :create do
|
||||||
|
get 'v2/media', to: 'captcha#media', on: :collection, as: :media
|
||||||
|
end
|
||||||
|
|
||||||
|
mount Rswag::Ui::Engine => '/api-docs'
|
||||||
|
mount Rswag::Api::Engine => '/api-docs'
|
||||||
|
|
||||||
|
get 'up' => 'rails/health#show', as: :rails_health_check
|
||||||
end
|
end
|
||||||
|
@ -1,7 +0,0 @@
|
|||||||
# Copyright (C) 2024 Manuel Bustillo
|
|
||||||
|
|
||||||
class AllowUngroupedGuests < ActiveRecord::Migration[8.0]
|
|
||||||
def change
|
|
||||||
change_column_null :guests, :group_id, true
|
|
||||||
end
|
|
||||||
end
|
|
4
db/schema.rb
generated
4
db/schema.rb
generated
@ -12,7 +12,7 @@
|
|||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema[8.0].define(version: 2024_12_08_102932) do
|
ActiveRecord::Schema[8.0].define(version: 2024_12_07_112305) do
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "pg_catalog.plpgsql"
|
enable_extension "pg_catalog.plpgsql"
|
||||||
|
|
||||||
@ -48,7 +48,7 @@ ActiveRecord::Schema[8.0].define(version: 2024_12_08_102932) do
|
|||||||
t.string "phone"
|
t.string "phone"
|
||||||
t.datetime "created_at", null: false
|
t.datetime "created_at", null: false
|
||||||
t.datetime "updated_at", null: false
|
t.datetime "updated_at", null: false
|
||||||
t.uuid "group_id"
|
t.uuid "group_id", null: false
|
||||||
t.integer "status", default: 0
|
t.integer "status", default: 0
|
||||||
t.string "name"
|
t.string "name"
|
||||||
t.uuid "wedding_id", null: false
|
t.uuid "wedding_id", null: false
|
||||||
|
@ -40,7 +40,6 @@ services:
|
|||||||
image: librecaptcha/lc-core:latest
|
image: librecaptcha/lc-core:latest
|
||||||
volumes:
|
volumes:
|
||||||
- "./tmp/libre-captcha-data:/lc-core/data"
|
- "./tmp/libre-captcha-data:/lc-core/data"
|
||||||
- "./libre-captcha-config.json:/lc-core/data/config.json"
|
|
||||||
ports:
|
ports:
|
||||||
- 8888
|
- 8888
|
||||||
nginx:
|
nginx:
|
||||||
|
@ -1,29 +0,0 @@
|
|||||||
{
|
|
||||||
"randomSeed": -1534087241,
|
|
||||||
"port": 8888,
|
|
||||||
"address": "0.0.0.0",
|
|
||||||
"captchaExpiryTimeLimit": 5,
|
|
||||||
"bufferCount": 1000,
|
|
||||||
"threadDelay": 2,
|
|
||||||
"playgroundEnabled": false,
|
|
||||||
"corsHeader": "",
|
|
||||||
"maxAttemptsRatio": 0.009999999776482582,
|
|
||||||
"captchas": [
|
|
||||||
{
|
|
||||||
"name": "FilterChallenge",
|
|
||||||
"allowedLevels": [
|
|
||||||
"hard"
|
|
||||||
],
|
|
||||||
"allowedMedia": [
|
|
||||||
"image/png"
|
|
||||||
],
|
|
||||||
"allowedInputType": [
|
|
||||||
"text"
|
|
||||||
],
|
|
||||||
"allowedSizes": [
|
|
||||||
"350x100"
|
|
||||||
],
|
|
||||||
"config": {}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@ -16,7 +16,7 @@ RSpec.describe Guest, type: :model do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it { should belong_to(:group).optional }
|
it { should belong_to(:group) }
|
||||||
|
|
||||||
describe 'scopes' do
|
describe 'scopes' do
|
||||||
describe '.potential' do
|
describe '.potential' do
|
||||||
|
@ -5,66 +5,88 @@ require 'rails_helper'
|
|||||||
module Expenses
|
module Expenses
|
||||||
RSpec.describe TotalQuery do
|
RSpec.describe TotalQuery do
|
||||||
describe '#call' do
|
describe '#call' do
|
||||||
let(:wedding) { create(:wedding) }
|
let(:response) { described_class.new.call }
|
||||||
let(:response) { described_class.new(wedding:).call }
|
|
||||||
|
|
||||||
before do
|
before do
|
||||||
create_list(:guest, 2, wedding:, status: :confirmed)
|
create_list(:guest, 2, status: :confirmed)
|
||||||
create_list(:guest, 3, wedding:, status: :considered)
|
create_list(:guest, 3, status: :considered)
|
||||||
create_list(:guest, 4, wedding:, status: :invited)
|
create_list(:guest, 4, status: :invited)
|
||||||
create_list(:guest, 5, wedding:, status: :tentative)
|
create_list(:guest, 5, status: :tentative)
|
||||||
create_list(:guest, 6, wedding:, status: :declined)
|
create_list(:guest, 6, status: :declined)
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when there is no expense' do
|
context "when there is no expense" do
|
||||||
it 'returns zero in all values', :aggregate_failures do
|
it "returns zero in all values", :aggregate_failures do
|
||||||
expect(response['total_confirmed']).to be_zero
|
expect(response["fixed"]).to be_zero
|
||||||
expect(response['total_projected']).to be_zero
|
expect(response["fixed_count"]).to be_zero
|
||||||
expect(response['confirmed_guests']).to eq(2)
|
expect(response["variable"]).to be_zero
|
||||||
expect(response['projected_guests']).to eq(2 + 4 + 5)
|
expect(response["variable_count"]).to be_zero
|
||||||
|
expect(response["total"]).to be_zero
|
||||||
|
expect(response["total_count"]).to be_zero
|
||||||
|
expect(response["max_projected"]).to be_zero
|
||||||
|
expect(response["per_person"]).to be_zero
|
||||||
|
expect(response["confirmed_guests"]).to eq(2)
|
||||||
|
expect(response["projected_guests"]).to eq(2 + 4 + 5)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when there are only fixed expenses' do
|
context "when there are only fixed expenses" do
|
||||||
before do
|
before do
|
||||||
create(:expense, :fixed, wedding:, amount: 100)
|
create(:expense, :fixed, amount: 100)
|
||||||
create(:expense, :fixed, wedding:, amount: 200)
|
create(:expense, :fixed, amount: 200)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns the sum of fixed expenses', :aggregate_failures do
|
it "returns the sum of fixed expenses", :aggregate_failures do
|
||||||
expect(response['total_confirmed']).to eq(300)
|
expect(response["fixed"]).to eq(300)
|
||||||
expect(response['total_projected']).to eq(300)
|
expect(response["fixed_count"]).to eq(2)
|
||||||
expect(response['confirmed_guests']).to eq(2)
|
expect(response["variable"]).to be_zero
|
||||||
expect(response['projected_guests']).to eq(2 + 4 + 5)
|
expect(response["variable_count"]).to be_zero
|
||||||
|
expect(response["total"]).to eq(300)
|
||||||
|
expect(response["total_count"]).to eq(2)
|
||||||
|
expect(response["max_projected"]).to eq(300)
|
||||||
|
expect(response["per_person"]).to eq(150)
|
||||||
|
expect(response["confirmed_guests"]).to eq(2)
|
||||||
|
expect(response["projected_guests"]).to eq(2 + 4 + 5)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when there are only variable expenses' do
|
context "when there are only variable expenses" do
|
||||||
before do
|
before do
|
||||||
create(:expense, :per_person, wedding:, amount: 100)
|
create(:expense, :per_person, amount: 100)
|
||||||
create(:expense, :per_person, wedding:, amount: 200)
|
create(:expense, :per_person, amount: 200)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns zero in the values and nonzero in the count', :aggregate_failures do
|
it "returns zero in the values and nonzero in the count", :aggregate_failures do
|
||||||
expect(response['total_confirmed']).to eq(2 * 300)
|
expect(response["fixed"]).to be_zero
|
||||||
expect(response['total_projected']).to eq(11 * 300)
|
expect(response["fixed_count"]).to be_zero
|
||||||
expect(response['confirmed_guests']).to eq(2)
|
expect(response["variable"]).to eq(300)
|
||||||
expect(response['projected_guests']).to eq(2 + 4 + 5)
|
expect(response["variable_count"]).to eq(2)
|
||||||
|
expect(response["total"]).to eq(2*300)
|
||||||
|
expect(response["total_count"]).to eq(2)
|
||||||
|
expect(response["max_projected"]).to eq(11*300)
|
||||||
|
expect(response["confirmed_guests"]).to eq(2)
|
||||||
|
expect(response["projected_guests"]).to eq(2 + 4 + 5)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when there are both fixed and variable expenses' do
|
context "when there are both fixed and variable expenses" do
|
||||||
before do
|
before do
|
||||||
create(:expense, :fixed, wedding:, amount: 100)
|
create(:expense, :fixed, amount: 100)
|
||||||
create(:expense, :fixed, wedding:, amount: 200)
|
create(:expense, :fixed, amount: 200)
|
||||||
create(:expense, :per_person, wedding:, amount: 50)
|
create(:expense, :per_person, amount: 50)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns the sum of fixed and variable expenses', :aggregate_failures do
|
it "returns the sum of fixed and variable expenses", :aggregate_failures do
|
||||||
expect(response['total_confirmed']).to eq(100 + 200 + 50 * 2)
|
expect(response["fixed"]).to eq(300)
|
||||||
expect(response['total_projected']).to eq(100 + 200 + 11 * 50)
|
expect(response["fixed_count"]).to eq(2)
|
||||||
expect(response['confirmed_guests']).to eq(2)
|
expect(response["variable"]).to eq(50)
|
||||||
expect(response['projected_guests']).to eq(2 + 4 + 5)
|
expect(response["variable_count"]).to eq(1)
|
||||||
|
expect(response["total"]).to eq(100 + 200 + 50 * 2)
|
||||||
|
expect(response["total_count"]).to eq(3)
|
||||||
|
expect(response["max_projected"]).to eq(100 + 200 + 11*50)
|
||||||
|
expect(response["per_person"]).to eq(200)
|
||||||
|
expect(response["confirmed_guests"]).to eq(2)
|
||||||
|
expect(response["projected_guests"]).to eq(2 + 4 + 5)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -16,7 +16,9 @@ RSpec.describe 'expenses', type: :request do
|
|||||||
required: %i[id name amount pricing_type],
|
required: %i[id name amount pricing_type],
|
||||||
properties: {
|
properties: {
|
||||||
id: { type: :string, format: :uuid },
|
id: { type: :string, format: :uuid },
|
||||||
**Swagger::Schema::EXPENSE
|
name: { type: :string },
|
||||||
|
amount: { type: :number },
|
||||||
|
pricing_type: { type: :string, enum: Expense.pricing_types.keys }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,31 +26,10 @@ RSpec.describe 'expenses', type: :request do
|
|||||||
end
|
end
|
||||||
regular_api_responses
|
regular_api_responses
|
||||||
end
|
end
|
||||||
|
|
||||||
post 'create expense' do
|
|
||||||
tags 'Expenses'
|
|
||||||
consumes 'application/json'
|
|
||||||
produces 'application/json'
|
|
||||||
parameter Swagger::Schema::SLUG
|
|
||||||
parameter name: :body, in: :body, schema: {
|
|
||||||
type: :object,
|
|
||||||
required: %i[expense],
|
|
||||||
properties: {
|
|
||||||
expense: {
|
|
||||||
type: :object,
|
|
||||||
required: %i[name amount pricing_type],
|
|
||||||
properties: Swagger::Schema::EXPENSE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
response_empty_201
|
|
||||||
response_422
|
|
||||||
regular_api_responses
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
path '/{slug}/expenses/{id}' do
|
path '/{slug}/expenses/{id}' do
|
||||||
|
|
||||||
patch('update expense') do
|
patch('update expense') do
|
||||||
tags 'Expenses'
|
tags 'Expenses'
|
||||||
consumes 'application/json'
|
consumes 'application/json'
|
||||||
@ -57,7 +38,11 @@ RSpec.describe 'expenses', type: :request do
|
|||||||
parameter name: 'id', in: :path, type: :string, format: :uuid, description: 'id'
|
parameter name: 'id', in: :path, type: :string, format: :uuid, description: 'id'
|
||||||
parameter name: :body, in: :body, schema: {
|
parameter name: :body, in: :body, schema: {
|
||||||
type: :object,
|
type: :object,
|
||||||
properties: Swagger::Schema::EXPENSE
|
properties: {
|
||||||
|
name: { type: :string },
|
||||||
|
amount: { type: :number, minimum: 0 },
|
||||||
|
pricing_type: { type: :string, enum: Expense.pricing_types.keys }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
response_empty_200
|
response_empty_200
|
||||||
@ -65,15 +50,5 @@ RSpec.describe 'expenses', type: :request do
|
|||||||
response_404
|
response_404
|
||||||
regular_api_responses
|
regular_api_responses
|
||||||
end
|
end
|
||||||
|
|
||||||
delete('delete expense') do
|
|
||||||
tags 'Expenses'
|
|
||||||
produces 'application/json'
|
|
||||||
parameter Swagger::Schema::SLUG
|
|
||||||
parameter Swagger::Schema::ID
|
|
||||||
response_empty_200
|
|
||||||
response_404
|
|
||||||
regular_api_responses
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -10,99 +10,26 @@ RSpec.describe 'groups', type: :request do
|
|||||||
parameter Swagger::Schema::SLUG
|
parameter Swagger::Schema::SLUG
|
||||||
response(200, 'successful') do
|
response(200, 'successful') do
|
||||||
schema type: :array,
|
schema type: :array,
|
||||||
items: {
|
items: {
|
||||||
type: :object,
|
type: :object,
|
||||||
required: %i[id name icon parent_id color attendance],
|
required: %i[id name icon parent_id color total considered invited confirmed declined tentative],
|
||||||
properties: {
|
properties: {
|
||||||
id: { type: :string, format: :uuid, required: true },
|
id: { type: :string, format: :uuid, required: true },
|
||||||
name: { type: :string },
|
name: { type: :string },
|
||||||
icon: { type: :string, example: 'pi pi-crown', description: 'The CSS classes used by the icon' },
|
icon: { type: :string, example: 'pi pi-crown', description: 'The CSS classes used by the icon' },
|
||||||
parent_id: { type: :string, format: :uuid },
|
parent_id: { type: :string, format: :uuid },
|
||||||
color: { type: :string, pattern: '^#(?:[0-9a-fA-F]{3}){1,2}$' },
|
color: { type: :string, pattern: '^#(?:[0-9a-fA-F]{3}){1,2}$' },
|
||||||
attendance: {
|
total: { type: :integer, minimum: 0, description: 'Total number of guests in any status' },
|
||||||
type: :object,
|
considered: { type: :integer, minimum: 0 },
|
||||||
required: %i[total considered invited confirmed declined tentative],
|
invited: { type: :integer, minimum: 0 },
|
||||||
properties: {
|
confirmed: { type: :integer, minimum: 0 },
|
||||||
total: { type: :integer, minimum: 0, description: 'Total number of guests in any status' },
|
declined: { type: :integer, minimum: 0 },
|
||||||
considered: { type: :integer, minimum: 0 },
|
tentative: { type: :integer, minimum: 0 }
|
||||||
invited: { type: :integer, minimum: 0 },
|
|
||||||
confirmed: { type: :integer, minimum: 0 },
|
|
||||||
declined: { type: :integer, minimum: 0 },
|
|
||||||
tentative: { type: :integer, minimum: 0 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
xit
|
xit
|
||||||
end
|
end
|
||||||
regular_api_responses
|
regular_api_responses
|
||||||
end
|
end
|
||||||
|
|
||||||
post('create group') do
|
|
||||||
tags 'Groups'
|
|
||||||
consumes 'application/json'
|
|
||||||
produces 'application/json'
|
|
||||||
parameter Swagger::Schema::SLUG
|
|
||||||
parameter name: :body, in: :body, schema: {
|
|
||||||
type: :object,
|
|
||||||
required: %i[group],
|
|
||||||
properties: {
|
|
||||||
group: {
|
|
||||||
type: :object,
|
|
||||||
required: %i[name],
|
|
||||||
properties: Swagger::Schema::GROUP
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
response(201, 'created') do
|
|
||||||
schema type: :object, properties: {
|
|
||||||
id: { type: :string, format: :uuid, required: true },
|
|
||||||
**Swagger::Schema::GROUP
|
|
||||||
}
|
|
||||||
|
|
||||||
xit
|
|
||||||
end
|
|
||||||
regular_api_responses
|
|
||||||
end
|
|
||||||
|
|
||||||
path '/{slug}/groups/{id}' do
|
|
||||||
put('update group') do
|
|
||||||
tags 'Groups'
|
|
||||||
consumes 'application/json'
|
|
||||||
produces 'application/json'
|
|
||||||
parameter Swagger::Schema::SLUG
|
|
||||||
parameter name: :id, in: :path, type: :string, format: :uuid
|
|
||||||
parameter name: :body, in: :body, schema: {
|
|
||||||
type: :object,
|
|
||||||
required: %i[group],
|
|
||||||
properties: {
|
|
||||||
group: {
|
|
||||||
type: :object,
|
|
||||||
required: %i[name],
|
|
||||||
properties: Swagger::Schema::GROUP
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
response(200, 'updated') do
|
|
||||||
schema type: :object, properties: {
|
|
||||||
id: { type: :string, format: :uuid, required: true },
|
|
||||||
**Swagger::Schema::GROUP
|
|
||||||
}
|
|
||||||
|
|
||||||
xit
|
|
||||||
end
|
|
||||||
regular_api_responses
|
|
||||||
end
|
|
||||||
|
|
||||||
delete('delete group') do
|
|
||||||
tags 'Groups'
|
|
||||||
produces 'application/json'
|
|
||||||
parameter Swagger::Schema::SLUG
|
|
||||||
parameter name: :id, in: :path, type: :string, format: :uuid
|
|
||||||
|
|
||||||
response_empty_200
|
|
||||||
regular_api_responses
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -41,7 +41,7 @@ RSpec.describe 'guests', type: :request do
|
|||||||
properties: {
|
properties: {
|
||||||
guest: {
|
guest: {
|
||||||
type: :object,
|
type: :object,
|
||||||
required: %i[name status],
|
required: %i[name group_id status],
|
||||||
properties: {
|
properties: {
|
||||||
name: { type: :string },
|
name: { type: :string },
|
||||||
group_id: { type: :string, format: :uuid },
|
group_id: { type: :string, format: :uuid },
|
||||||
@ -70,7 +70,6 @@ RSpec.describe 'guests', type: :request do
|
|||||||
properties: {
|
properties: {
|
||||||
guest: {
|
guest: {
|
||||||
type: :object,
|
type: :object,
|
||||||
required: %i[name status],
|
|
||||||
properties: {
|
properties: {
|
||||||
name: { type: :string },
|
name: { type: :string },
|
||||||
group_id: { type: :string, format: :uuid },
|
group_id: { type: :string, format: :uuid },
|
||||||
|
@ -4,29 +4,10 @@ module Swagger
|
|||||||
module Schema
|
module Schema
|
||||||
USER = {
|
USER = {
|
||||||
id: { type: :string, format: :uuid },
|
id: { type: :string, format: :uuid },
|
||||||
email: { type: :string, format: :email },
|
email: { type: :string, format: :email },
|
||||||
created_at: SwaggerResponseHelper::TIMESTAMP,
|
created_at: SwaggerResponseHelper::TIMESTAMP,
|
||||||
updated_at: SwaggerResponseHelper::TIMESTAMP
|
updated_at: SwaggerResponseHelper::TIMESTAMP
|
||||||
}
|
|
||||||
|
|
||||||
ID = {
|
|
||||||
name: 'id',
|
|
||||||
in: :path,
|
|
||||||
type: :string,
|
|
||||||
format: :uuid,
|
|
||||||
}
|
|
||||||
|
|
||||||
GROUP = {
|
|
||||||
name: { type: :string },
|
|
||||||
icon: { type: :string, example: 'pi pi-crown', description: 'The CSS classes used by the icon' },
|
|
||||||
parent_id: { type: :string, format: :uuid },
|
|
||||||
color: { type: :string, pattern: '^#(?:[0-9a-fA-F]{3}){1,2}$' }
|
|
||||||
}
|
|
||||||
|
|
||||||
EXPENSE = {
|
|
||||||
name: { type: :string },
|
|
||||||
amount: { type: :number, minimum: 0 },
|
|
||||||
pricing_type: { type: :string, enum: Expense.pricing_types.keys }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SLUG = {
|
SLUG = {
|
||||||
|
@ -1,61 +0,0 @@
|
|||||||
# Copyright (C) 2024 Manuel Bustillo
|
|
||||||
|
|
||||||
require 'swagger_helper'
|
|
||||||
|
|
||||||
RSpec.describe 'summary', type: :request do
|
|
||||||
path '/{slug}/summary' do
|
|
||||||
get('list summaries') do
|
|
||||||
tags 'Summary'
|
|
||||||
produces 'application/json'
|
|
||||||
consumes 'application/json'
|
|
||||||
parameter Swagger::Schema::SLUG
|
|
||||||
response(200, 'successful') do
|
|
||||||
schema type: :object,
|
|
||||||
required: %i[expenses guests],
|
|
||||||
properties: {
|
|
||||||
expenses: {
|
|
||||||
type: :object,
|
|
||||||
required: %i[projected confirmed status],
|
|
||||||
properties: {
|
|
||||||
projected: {
|
|
||||||
type: :object,
|
|
||||||
required: %i[total guests],
|
|
||||||
properties: {
|
|
||||||
total: { type: :number },
|
|
||||||
guests: { type: :number }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
confirmed: {
|
|
||||||
type: :object,
|
|
||||||
required: %i[total guests],
|
|
||||||
properties: {
|
|
||||||
total: { type: :number },
|
|
||||||
guests: { type: :number }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
status: {
|
|
||||||
type: :object,
|
|
||||||
required: [:paid],
|
|
||||||
properties: {
|
|
||||||
paid: { type: :number }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
guests: {
|
|
||||||
type: :object,
|
|
||||||
required: %i[total confirmed declined tentative invited],
|
|
||||||
properties: {
|
|
||||||
total: { type: :number },
|
|
||||||
confirmed: { type: :number },
|
|
||||||
declined: { type: :number },
|
|
||||||
tentative: { type: :number },
|
|
||||||
invited: { type: :number }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
xit
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,61 +0,0 @@
|
|||||||
# Copyright (C) 2024 Manuel Bustillo
|
|
||||||
|
|
||||||
require 'swagger_helper'
|
|
||||||
|
|
||||||
RSpec.describe 'tables_arrangements', type: :request do
|
|
||||||
|
|
||||||
path '/{slug}/tables_arrangements' do
|
|
||||||
get('list tables arrangements') do
|
|
||||||
tags 'Tables Arrangements'
|
|
||||||
produces 'application/json'
|
|
||||||
parameter Swagger::Schema::SLUG
|
|
||||||
response(200, 'successful') do
|
|
||||||
schema type: :array,
|
|
||||||
items: {
|
|
||||||
type: :object,
|
|
||||||
required: %i[id name discomfort],
|
|
||||||
properties: {
|
|
||||||
id: { type: :string, format: :uuid },
|
|
||||||
name: { type: :string },
|
|
||||||
discomfort: { type: :integer }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
xit
|
|
||||||
end
|
|
||||||
regular_api_responses
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
path '/{slug}/tables_arrangements/{id}' do
|
|
||||||
get('show tables arrangement') do
|
|
||||||
tags 'Tables Arrangements'
|
|
||||||
produces 'application/json'
|
|
||||||
parameter Swagger::Schema::SLUG
|
|
||||||
parameter Swagger::Schema::ID
|
|
||||||
response(200, 'successful') do
|
|
||||||
schema type: :array,
|
|
||||||
items: {
|
|
||||||
type: :object,
|
|
||||||
required: %i[number guests],
|
|
||||||
properties: {
|
|
||||||
number: { type: :integer },
|
|
||||||
guests: {
|
|
||||||
type: :array,
|
|
||||||
items: {
|
|
||||||
type: :object,
|
|
||||||
required: %i[id name color],
|
|
||||||
properties: {
|
|
||||||
id: { type: :string, format: :uuid },
|
|
||||||
name: { type: :string },
|
|
||||||
color: { type: :string }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
xit
|
|
||||||
end
|
|
||||||
regular_api_responses
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,15 +0,0 @@
|
|||||||
# Copyright (C) 2024 Manuel Bustillo
|
|
||||||
|
|
||||||
require 'swagger_helper'
|
|
||||||
|
|
||||||
RSpec.describe 'tokens', type: :request do
|
|
||||||
path '/token' do
|
|
||||||
get('get a cookie with CSRF token') do
|
|
||||||
tags 'CSRF token'
|
|
||||||
consumes 'application/json'
|
|
||||||
produces 'application/json'
|
|
||||||
|
|
||||||
response_empty_200
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
Loading…
x
Reference in New Issue
Block a user