Merge branch 'main' into renovate/rspec-rails-8.x
All checks were successful
Run unit tests / rubocop (pull_request) Successful in 2m13s
Run unit tests / check-licenses (pull_request) Successful in 3m5s
Run unit tests / copyright_notice (pull_request) Successful in 4m22s
Run unit tests / unit_tests (pull_request) Successful in 6m36s
Run unit tests / build-static-assets (pull_request) Successful in 37m36s
All checks were successful
Run unit tests / rubocop (pull_request) Successful in 2m13s
Run unit tests / check-licenses (pull_request) Successful in 3m5s
Run unit tests / copyright_notice (pull_request) Successful in 4m22s
Run unit tests / unit_tests (pull_request) Successful in 6m36s
Run unit tests / build-static-assets (pull_request) Successful in 37m36s
This commit is contained in:
commit
ac6df3b75b
1
.gitignore
vendored
1
.gitignore
vendored
@ -36,3 +36,4 @@
|
||||
|
||||
# Ignore swagger generated documentation
|
||||
swagger/v1/swagger.yaml
|
||||
wedding-planner.code-workspace
|
||||
|
@ -97,7 +97,7 @@ GEM
|
||||
concurrent-ruby (1.3.5)
|
||||
connection_pool (2.5.3)
|
||||
crass (1.0.6)
|
||||
csv (3.3.4)
|
||||
csv (3.3.5)
|
||||
date (3.4.1)
|
||||
debug (1.10.0)
|
||||
irb (~> 1.10)
|
||||
@ -500,7 +500,7 @@ CHECKSUMS
|
||||
concurrent-ruby (1.3.5) sha256=813b3e37aca6df2a21a3b9f1d497f8cbab24a2b94cab325bffe65ee0f6cbebc6
|
||||
connection_pool (2.5.3) sha256=cfd74a82b9b094d1ce30c4f1a346da23ee19dc8a062a16a85f58eab1ced4305b
|
||||
crass (1.0.6) sha256=dc516022a56e7b3b156099abc81b6d2b08ea1ed12676ac7a5657617f012bd45d
|
||||
csv (3.3.4) sha256=e96ecd5a8c3494aa5b596282249daba5c6033203c199248e6146e36d2a78d8cd
|
||||
csv (3.3.5) sha256=6e5134ac3383ef728b7f02725d9872934f523cb40b961479f69cf3afa6c8e73f
|
||||
date (3.4.1) sha256=bf268e14ef7158009bfeaec40b5fa3c7271906e88b196d958a89d4b408abe64f
|
||||
debug (1.10.0) sha256=11e28ca74875979e612444104f3972bd5ffb9e79179907d7ad46dba44bd2e7a4
|
||||
devise (4.9.4) sha256=920042fe5e704c548aa4eb65ebdd65980b83ffae67feb32c697206bfd975a7f8
|
||||
|
48
app/controllers/invitations_controller.rb
Normal file
48
app/controllers/invitations_controller.rb
Normal file
@ -0,0 +1,48 @@
|
||||
# Copyright (C) 2024-2025 LibreWeddingPlanner contributors
|
||||
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Copyright (C) 2024-2025 LibreWeddingPlanner contributors
|
||||
|
||||
class InvitationsController < ApplicationController
|
||||
def index
|
||||
render json: Invitation.includes(:guests).as_json(
|
||||
only: :id,
|
||||
include: {
|
||||
guests: {
|
||||
only: %i[id name]
|
||||
}
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
def create
|
||||
invitation = Invitation.create
|
||||
|
||||
if invitation.persisted?
|
||||
render json: invitation, only: :id, status: :created
|
||||
else
|
||||
render json: { errors: invitation.errors.full_messages }, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
invitation = Invitation.find(params[:id])
|
||||
|
||||
if invitation.update(guest_ids: params[:invitation][:guest_ids])
|
||||
render json: invitation, only: :id, include: { guests: { only: %i[id name] } }, status: :ok
|
||||
else
|
||||
render json: { errors: invitation.errors.full_messages }, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
invitation = Invitation.find(params[:id])
|
||||
|
||||
if invitation.destroy
|
||||
head :no_content
|
||||
else
|
||||
render json: { errors: invitation.errors.full_messages }, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
end
|
@ -20,7 +20,7 @@
|
||||
#
|
||||
# Foreign Keys
|
||||
#
|
||||
# fk_rails_... (wedding_id => weddings.id)
|
||||
# fk_rails_... (wedding_id => weddings.id) ON DELETE => cascade
|
||||
#
|
||||
class Expense < ApplicationRecord
|
||||
acts_as_tenant :wedding
|
||||
|
@ -25,7 +25,7 @@
|
||||
# Foreign Keys
|
||||
#
|
||||
# fk_rails_... (parent_id => groups.id)
|
||||
# fk_rails_... (wedding_id => weddings.id)
|
||||
# fk_rails_... (wedding_id => weddings.id) ON DELETE => cascade
|
||||
#
|
||||
class Group < ApplicationRecord
|
||||
acts_as_tenant :wedding
|
||||
|
@ -6,28 +6,32 @@
|
||||
#
|
||||
# Table name: guests
|
||||
#
|
||||
# id :uuid not null, primary key
|
||||
# name :string
|
||||
# phone :string
|
||||
# status :integer default("considered")
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
# group_id :uuid
|
||||
# wedding_id :uuid not null
|
||||
# id :uuid not null, primary key
|
||||
# name :string
|
||||
# phone :string
|
||||
# status :integer default("considered")
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
# group_id :uuid
|
||||
# invitation_id :uuid
|
||||
# wedding_id :uuid not null
|
||||
#
|
||||
# Indexes
|
||||
#
|
||||
# index_guests_on_group_id (group_id)
|
||||
# index_guests_on_wedding_id (wedding_id)
|
||||
# index_guests_on_group_id (group_id)
|
||||
# index_guests_on_invitation_id (invitation_id)
|
||||
# index_guests_on_wedding_id (wedding_id)
|
||||
#
|
||||
# Foreign Keys
|
||||
#
|
||||
# fk_rails_... (group_id => groups.id)
|
||||
# fk_rails_... (wedding_id => weddings.id)
|
||||
# fk_rails_... (invitation_id => invitations.id)
|
||||
# fk_rails_... (wedding_id => weddings.id) ON DELETE => cascade
|
||||
#
|
||||
class Guest < ApplicationRecord
|
||||
acts_as_tenant :wedding
|
||||
belongs_to :group, optional: true
|
||||
belongs_to :invitation, optional: true
|
||||
|
||||
enum :status, {
|
||||
considered: 0,
|
||||
|
25
app/models/invitation.rb
Normal file
25
app/models/invitation.rb
Normal file
@ -0,0 +1,25 @@
|
||||
# Copyright (C) 2024-2025 LibreWeddingPlanner contributors
|
||||
|
||||
# frozen_string_literal: true
|
||||
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: invitations
|
||||
#
|
||||
# id :uuid not null, primary key
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
# wedding_id :uuid not null
|
||||
#
|
||||
# Indexes
|
||||
#
|
||||
# index_invitations_on_wedding_id (wedding_id)
|
||||
#
|
||||
# Foreign Keys
|
||||
#
|
||||
# fk_rails_... (wedding_id => weddings.id) ON DELETE => cascade
|
||||
#
|
||||
class Invitation < ApplicationRecord
|
||||
acts_as_tenant :wedding
|
||||
has_many :guests, dependent: :nullify
|
||||
end
|
@ -24,7 +24,7 @@
|
||||
#
|
||||
# fk_rails_... (guest_id => guests.id)
|
||||
# fk_rails_... (tables_arrangement_id => tables_arrangements.id) ON DELETE => cascade
|
||||
# fk_rails_... (wedding_id => weddings.id)
|
||||
# fk_rails_... (wedding_id => weddings.id) ON DELETE => cascade
|
||||
#
|
||||
class Seat < ApplicationRecord
|
||||
acts_as_tenant :wedding
|
||||
|
@ -20,7 +20,7 @@
|
||||
#
|
||||
# Foreign Keys
|
||||
#
|
||||
# fk_rails_... (wedding_id => weddings.id)
|
||||
# fk_rails_... (wedding_id => weddings.id) ON DELETE => cascade
|
||||
#
|
||||
class TablesArrangement < ApplicationRecord
|
||||
acts_as_tenant :wedding
|
||||
|
@ -32,7 +32,7 @@
|
||||
#
|
||||
# Foreign Keys
|
||||
#
|
||||
# fk_rails_... (wedding_id => weddings.id)
|
||||
# fk_rails_... (wedding_id => weddings.id) ON DELETE => cascade
|
||||
#
|
||||
class User < ApplicationRecord
|
||||
acts_as_tenant :wedding
|
||||
|
@ -21,4 +21,6 @@ class Wedding < ApplicationRecord
|
||||
validates :slug, presence: true, uniqueness: true, format: { with: /\A#{SLUG_REGEX}\z/ }
|
||||
|
||||
has_many :guests, dependent: :delete_all
|
||||
has_many :groups, dependent: :delete_all
|
||||
has_many :invitations, dependent: :delete_all
|
||||
end
|
||||
|
@ -2,4 +2,8 @@
|
||||
|
||||
ActsAsTenant.configure do |config|
|
||||
config.require_tenant = !Rails.env.test?
|
||||
end
|
||||
end
|
||||
|
||||
Rails.application.console do
|
||||
ActsAsTenant.current_tenant = Wedding.first
|
||||
end
|
||||
|
@ -39,6 +39,7 @@ Rails.application.routes.draw do
|
||||
end
|
||||
resources :tables_arrangements, only: %i[index show create]
|
||||
resources :summary, only: :index
|
||||
resources :invitations, only: %i[index create update destroy]
|
||||
|
||||
root to: redirect("/%{slug}")
|
||||
end
|
||||
|
10
db/migrate/20250127183547_create_invitations.rb
Normal file
10
db/migrate/20250127183547_create_invitations.rb
Normal file
@ -0,0 +1,10 @@
|
||||
class CreateInvitations < ActiveRecord::Migration[8.0]
|
||||
def change
|
||||
create_table :invitations, id: :uuid do |t|
|
||||
t.references :wedding, null: false, foreign_key: { on_delete: :cascade }, type: :uuid
|
||||
t.timestamps
|
||||
end
|
||||
|
||||
add_reference :guests, :invitation, foreign_key: true, type: :uuid
|
||||
end
|
||||
end
|
@ -0,0 +1,8 @@
|
||||
class FixCascadingWeddingDeletion < ActiveRecord::Migration[8.0]
|
||||
def change
|
||||
[:expenses, :groups, :guests, :seats, :tables_arrangements, :users].each do |table|
|
||||
remove_foreign_key table, :weddings, column: :wedding_id
|
||||
add_foreign_key table, :weddings, on_delete: :cascade
|
||||
end
|
||||
end
|
||||
end
|
25
db/schema.rb
generated
25
db/schema.rb
generated
@ -10,7 +10,7 @@
|
||||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema[8.0].define(version: 2025_01_26_091823) do
|
||||
ActiveRecord::Schema[8.0].define(version: 2025_01_27_190131) do
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "pg_catalog.plpgsql"
|
||||
|
||||
@ -63,10 +63,19 @@ ActiveRecord::Schema[8.0].define(version: 2025_01_26_091823) do
|
||||
t.integer "status", default: 0
|
||||
t.string "name"
|
||||
t.uuid "wedding_id", null: false
|
||||
t.uuid "invitation_id"
|
||||
t.index ["group_id"], name: "index_guests_on_group_id"
|
||||
t.index ["invitation_id"], name: "index_guests_on_invitation_id"
|
||||
t.index ["wedding_id"], name: "index_guests_on_wedding_id"
|
||||
end
|
||||
|
||||
create_table "invitations", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
|
||||
t.uuid "wedding_id", null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.index ["wedding_id"], name: "index_invitations_on_wedding_id"
|
||||
end
|
||||
|
||||
create_table "seats", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
|
||||
t.uuid "guest_id", null: false
|
||||
t.uuid "tables_arrangement_id", null: false
|
||||
@ -239,22 +248,24 @@ ActiveRecord::Schema[8.0].define(version: 2025_01_26_091823) do
|
||||
t.index ["slug"], name: "index_weddings_on_slug", unique: true
|
||||
end
|
||||
|
||||
add_foreign_key "expenses", "weddings"
|
||||
add_foreign_key "expenses", "weddings", on_delete: :cascade
|
||||
add_foreign_key "group_affinities", "groups", column: "group_a_id"
|
||||
add_foreign_key "group_affinities", "groups", column: "group_b_id"
|
||||
add_foreign_key "groups", "groups", column: "parent_id"
|
||||
add_foreign_key "groups", "weddings"
|
||||
add_foreign_key "groups", "weddings", on_delete: :cascade
|
||||
add_foreign_key "guests", "groups"
|
||||
add_foreign_key "guests", "weddings"
|
||||
add_foreign_key "guests", "invitations"
|
||||
add_foreign_key "guests", "weddings", on_delete: :cascade
|
||||
add_foreign_key "invitations", "weddings", on_delete: :cascade
|
||||
add_foreign_key "seats", "guests"
|
||||
add_foreign_key "seats", "tables_arrangements", on_delete: :cascade
|
||||
add_foreign_key "seats", "weddings"
|
||||
add_foreign_key "seats", "weddings", on_delete: :cascade
|
||||
add_foreign_key "solid_queue_blocked_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
|
||||
add_foreign_key "solid_queue_claimed_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
|
||||
add_foreign_key "solid_queue_failed_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
|
||||
add_foreign_key "solid_queue_ready_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
|
||||
add_foreign_key "solid_queue_recurring_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
|
||||
add_foreign_key "solid_queue_scheduled_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
|
||||
add_foreign_key "tables_arrangements", "weddings"
|
||||
add_foreign_key "users", "weddings"
|
||||
add_foreign_key "tables_arrangements", "weddings", on_delete: :cascade
|
||||
add_foreign_key "users", "weddings", on_delete: :cascade
|
||||
end
|
||||
|
70
db/seeds.rb
70
db/seeds.rb
@ -1,33 +1,33 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Copyright (C) 2024-2025 LibreWeddingPlanner contributors
|
||||
|
||||
NUMBER_OF_GUESTS = 50
|
||||
NUMBER_OF_GUESTS = 200
|
||||
|
||||
ActsAsTenant.without_tenant do
|
||||
TablesArrangement.delete_all
|
||||
Expense.delete_all
|
||||
Guest.delete_all
|
||||
Group.delete_all
|
||||
|
||||
GroupAffinity.delete_all
|
||||
Wedding.delete_all
|
||||
end
|
||||
|
||||
wedding = Wedding.create!(slug: :default)
|
||||
|
||||
ActsAsTenant.with_tenant(wedding) do
|
||||
Expense.create!(name: 'Photographer', amount: 3000, pricing_type: 'fixed')
|
||||
Expense.create!(name: 'Country house', amount: 6000, pricing_type: 'fixed')
|
||||
Expense.create!(name: 'Catering', amount: 200, pricing_type: 'per_person')
|
||||
Expense.create!(name: 'Flowers', amount: 500, pricing_type: 'fixed')
|
||||
Expense.create!(name: 'Band', amount: 1000, pricing_type: 'fixed')
|
||||
Expense.create!(name: 'Wedding planner', amount: 2000, pricing_type: 'fixed')
|
||||
Expense.create!(name: 'Dress', amount: 1000, pricing_type: 'fixed')
|
||||
Expense.create!(name: 'Suit', amount: 500, pricing_type: 'fixed')
|
||||
Expense.create!(name: 'Rings', amount: 1000, pricing_type: 'fixed')
|
||||
Expense.create!(name: 'Makeup', amount: 200, pricing_type: 'fixed')
|
||||
Expense.create!(name: 'Hair', amount: 200, pricing_type: 'fixed')
|
||||
Expense.create!(name: 'Transportation', amount: 3000, pricing_type: 'fixed')
|
||||
Expense.create!(name: 'Invitations', amount: 200, pricing_type: 'fixed')
|
||||
Expense.create!(name: 'Cake', amount: 500, pricing_type: 'fixed')
|
||||
[
|
||||
{ name: 'Photographer', amount: 3000, pricing_type: 'fixed' },
|
||||
{ name: 'Country house', amount: 6000, pricing_type: 'fixed' },
|
||||
{ name: 'Catering', amount: 200, pricing_type: 'per_person' },
|
||||
{ name: 'Flowers', amount: 500, pricing_type: 'fixed' },
|
||||
{ name: 'Band', amount: 1000, pricing_type: 'fixed' },
|
||||
{ name: 'Wedding planner', amount: 2000, pricing_type: 'fixed' },
|
||||
{ name: 'Dress', amount: 1000, pricing_type: 'fixed' },
|
||||
{ name: 'Suit', amount: 500, pricing_type: 'fixed' },
|
||||
{ name: 'Rings', amount: 1000, pricing_type: 'fixed' },
|
||||
{ name: 'Makeup', amount: 200, pricing_type: 'fixed' },
|
||||
{ name: 'Hair', amount: 200, pricing_type: 'fixed' },
|
||||
{ name: 'Transportation', amount: 3000, pricing_type: 'fixed' },
|
||||
{ name: 'Invitations', amount: 200, pricing_type: 'fixed' },
|
||||
{ name: 'Cake', amount: 500, pricing_type: 'fixed' }
|
||||
].then { Expense.insert_all!(it) }
|
||||
|
||||
Group.create!(name: "Jim's guests", icon: 'pi pi-heart').tap do |parent|
|
||||
parent.children.create!(name: "Jim's family", icon: 'pi pi-users').tap do |family|
|
||||
@ -61,18 +61,34 @@ ActsAsTenant.with_tenant(wedding) do
|
||||
|
||||
groups = Group.all
|
||||
|
||||
NUMBER_OF_GUESTS.times do
|
||||
Guest.create!(
|
||||
NUMBER_OF_GUESTS.times.map do |i|
|
||||
{
|
||||
name: Faker::Name.name,
|
||||
phone: Faker::PhoneNumber.cell_phone,
|
||||
group: groups.sample,
|
||||
status: Guest.statuses.keys.sample
|
||||
)
|
||||
group_id: groups.sample.id,
|
||||
status: Guest.statuses.keys.sample,
|
||||
}
|
||||
end.then { Guest.insert_all!(it) }
|
||||
|
||||
Group.includes(:guests).each do |group|
|
||||
guests = group.guests.potential.to_a
|
||||
|
||||
while guests.any?
|
||||
invitation = Invitation.create!
|
||||
|
||||
guests.shift(rand(1..3)).each do |guest|
|
||||
guest.update!(invitation:)
|
||||
end
|
||||
guests.shift(1) if rand < 0.3 # Leave a percentage of guests without an invitation
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
# TODO: Clean up invitations with no guests
|
||||
|
||||
ActiveJob.perform_all_later(3.times.map { TableSimulatorJob.new(wedding.id) })
|
||||
|
||||
'red'.paint.palette.triad(as: :hex).zip(Group.roots).each { |(color, group)| group.update!(color: color.paint.desaturate(40)) }
|
||||
"red".dup.paint.palette.triad(as: :hex).zip(Group.roots).each { |(color, group)| group.update!(color: color.paint.desaturate(40)) }
|
||||
|
||||
Group.roots.each(&:colorize_children)
|
||||
|
||||
@ -80,6 +96,6 @@ ActsAsTenant.with_tenant(wedding) do
|
||||
email: 'development@example.com',
|
||||
confirmed_at: Time.zone.now,
|
||||
password: 'supersecretpassword',
|
||||
password_confirmation: 'supersecretpassword',
|
||||
password_confirmation: 'supersecretpassword'
|
||||
)
|
||||
end
|
||||
|
9
spec/factories/invitations.rb
Normal file
9
spec/factories/invitations.rb
Normal file
@ -0,0 +1,9 @@
|
||||
# Copyright (C) 2024-2025 LibreWeddingPlanner contributors
|
||||
|
||||
# frozen_string_literal: true
|
||||
|
||||
FactoryBot.define do
|
||||
factory :invitation do
|
||||
wedding
|
||||
end
|
||||
end
|
9
spec/models/invitation_spec.rb
Normal file
9
spec/models/invitation_spec.rb
Normal file
@ -0,0 +1,9 @@
|
||||
# Copyright (C) 2024-2025 LibreWeddingPlanner contributors
|
||||
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Invitation do
|
||||
pending "add some examples to (or delete) #{__FILE__}"
|
||||
end
|
Loading…
x
Reference in New Issue
Block a user