feat: added spendings for bubbles
Signed-off-by: Louis Vallat <louis@louis-vallat.xyz>
This commit is contained in:
parent
11f5929a1a
commit
edf60e9557
@ -9,6 +9,11 @@ class BubblesController < ApplicationController
|
|||||||
@bubble = Bubble.new
|
@bubble = Bubble.new
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def show
|
||||||
|
@bubble = Bubble.find(params[:id])
|
||||||
|
@spendings = @bubble.spendings.unscope(:order).order(created_at: :desc)
|
||||||
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
if Bubble.new(allowed_params).save
|
if Bubble.new(allowed_params).save
|
||||||
redirect_to bubbles_path, flash: { success: "Créé" }
|
redirect_to bubbles_path, flash: { success: "Créé" }
|
||||||
|
23
app/controllers/spendings_controller.rb
Normal file
23
app/controllers/spendings_controller.rb
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class SpendingsController < ApplicationController
|
||||||
|
def new
|
||||||
|
@bubble = Bubble.find(params[:bubble_id])
|
||||||
|
@spending = Spending.new
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
@bubble = Bubble.find(params[:bubble_id])
|
||||||
|
if @bubble.spendings.new(allowed_params).save
|
||||||
|
redirect_to bubble_path(@bubble), flash: { success: "Créé" }
|
||||||
|
else
|
||||||
|
redirect_to new_bubble_spending_path(@bubble), flash: { error: "Pas créé" }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def allowed_params
|
||||||
|
params.require(:spending).permit(:amount, :description, :owner_id)
|
||||||
|
end
|
||||||
|
end
|
@ -21,4 +21,5 @@
|
|||||||
class Bubble < ApplicationRecord
|
class Bubble < ApplicationRecord
|
||||||
validates :name, presence: true, allow_blank: false
|
validates :name, presence: true, allow_blank: false
|
||||||
belongs_to :owner, class_name: "User"
|
belongs_to :owner, class_name: "User"
|
||||||
|
has_many :spendings
|
||||||
end
|
end
|
||||||
|
25
app/models/spending.rb
Normal file
25
app/models/spending.rb
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# == Schema Information
|
||||||
|
#
|
||||||
|
# Table name: spendings
|
||||||
|
#
|
||||||
|
# id :uuid not null, primary key
|
||||||
|
# amount :decimal(, ) default(0.0), not null
|
||||||
|
# description :text default(""), not null
|
||||||
|
# created_at :datetime not null
|
||||||
|
# updated_at :datetime not null
|
||||||
|
# bubble_id :uuid not null
|
||||||
|
# owner_id :uuid not null
|
||||||
|
#
|
||||||
|
# Indexes
|
||||||
|
#
|
||||||
|
# index_spendings_on_bubble_id (bubble_id)
|
||||||
|
# index_spendings_on_owner_id (owner_id)
|
||||||
|
#
|
||||||
|
# Foreign Keys
|
||||||
|
#
|
||||||
|
# fk_rails_... (owner_id => users.id)
|
||||||
|
#
|
||||||
|
class Spending < ApplicationRecord
|
||||||
|
belongs_to :owner, class_name: "User"
|
||||||
|
belongs_to :bubble
|
||||||
|
end
|
@ -23,4 +23,5 @@ class User < ApplicationRecord
|
|||||||
:recoverable, :rememberable, :validatable
|
:recoverable, :rememberable, :validatable
|
||||||
|
|
||||||
has_many :bubbles, foreign_key: :owner_id
|
has_many :bubbles, foreign_key: :owner_id
|
||||||
|
has_many :spendings, foreign_key: :owner_id
|
||||||
end
|
end
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
<td><%= bubble.name %></td>
|
<td><%= bubble.name %></td>
|
||||||
<td><%= bubble.description %></td>
|
<td><%= bubble.description %></td>
|
||||||
<td>
|
<td>
|
||||||
|
<%= link_to "Voir", bubble_path(bubble) %>
|
||||||
<%= link_to "Éditer", edit_bubble_path(bubble) %>
|
<%= link_to "Éditer", edit_bubble_path(bubble) %>
|
||||||
<%= link_to "Supprimer", bubble_confirm_destroy_path(bubble) %>
|
<%= link_to "Supprimer", bubble_confirm_destroy_path(bubble) %>
|
||||||
</td>
|
</td>
|
||||||
|
22
app/views/bubbles/show.html.erb
Normal file
22
app/views/bubbles/show.html.erb
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<h1><%= @bubble.name %></h1>
|
||||||
|
<p><%= @bubble.description %></p>
|
||||||
|
<%= link_to "Nouvelle dépense", new_bubble_spending_path(@bubble) %>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Montant</th>
|
||||||
|
<th>Origine</th>
|
||||||
|
<th>Raison</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<% @spendings.each do |spending| %>
|
||||||
|
<tr>
|
||||||
|
<td><%= number_to_currency(spending.amount, unit: "€") %></td>
|
||||||
|
<td><%= spending.owner.email %></td>
|
||||||
|
<td><%= spending.description %></td>
|
||||||
|
</tr>
|
||||||
|
<% end %>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
1
app/views/spendings/new.html.erb
Normal file
1
app/views/spendings/new.html.erb
Normal file
@ -0,0 +1 @@
|
|||||||
|
<%= render partial: "spendings/shared/form", locals: { spending: @spending, bubble: @bubble } %>
|
20
app/views/spendings/shared/_form.html.erb
Normal file
20
app/views/spendings/shared/_form.html.erb
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<%= form_for spending, url: spending.persisted? ? spending_path(spending) : bubble_spendings_path(bubble) do |f| %>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<%= f.label :amount, "Valeur" %>
|
||||||
|
<%= f.number_field :amount, required: true, step: 0.01 %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<%= f.label :description, "Description" %>
|
||||||
|
<%= f.text_area :description %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<%= f.label :owner_id, "Créateur" %>
|
||||||
|
<%= f.select :owner_id, User.all.collect {|u| [ u.email, u.id ] }, selected: bubble.owner_id || current_user.id %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<%= f.submit "Sauvegarder" %>
|
||||||
|
|
||||||
|
<% end %>
|
@ -18,5 +18,7 @@ module DebtManager
|
|||||||
#
|
#
|
||||||
# config.time_zone = "Central Time (US & Canada)"
|
# config.time_zone = "Central Time (US & Canada)"
|
||||||
# config.eager_load_paths << Rails.root.join("extras")
|
# config.eager_load_paths << Rails.root.join("extras")
|
||||||
|
|
||||||
|
config.i18n.default_locale = :fr
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
221
config/locales/fr.yml
Normal file
221
config/locales/fr.yml
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
---
|
||||||
|
fr:
|
||||||
|
activerecord:
|
||||||
|
errors:
|
||||||
|
messages:
|
||||||
|
record_invalid: 'La validation a échoué : %{errors}'
|
||||||
|
restrict_dependent_destroy:
|
||||||
|
has_one: Vous ne pouvez pas supprimer l'enregistrement car un(e) %{record}
|
||||||
|
dépendant(e) existe
|
||||||
|
has_many: Vous ne pouvez pas supprimer l'enregistrement parce que les %{record}
|
||||||
|
dépendants existent
|
||||||
|
date:
|
||||||
|
abbr_day_names:
|
||||||
|
- dim
|
||||||
|
- lun
|
||||||
|
- mar
|
||||||
|
- mer
|
||||||
|
- jeu
|
||||||
|
- ven
|
||||||
|
- sam
|
||||||
|
abbr_month_names:
|
||||||
|
-
|
||||||
|
- jan.
|
||||||
|
- fév.
|
||||||
|
- mars
|
||||||
|
- avr.
|
||||||
|
- mai
|
||||||
|
- juin
|
||||||
|
- juil.
|
||||||
|
- août
|
||||||
|
- sept.
|
||||||
|
- oct.
|
||||||
|
- nov.
|
||||||
|
- déc.
|
||||||
|
day_names:
|
||||||
|
- dimanche
|
||||||
|
- lundi
|
||||||
|
- mardi
|
||||||
|
- mercredi
|
||||||
|
- jeudi
|
||||||
|
- vendredi
|
||||||
|
- samedi
|
||||||
|
formats:
|
||||||
|
default: "%d/%m/%Y"
|
||||||
|
long: "%-d %B %Y"
|
||||||
|
short: "%-d %b"
|
||||||
|
month_names:
|
||||||
|
-
|
||||||
|
- janvier
|
||||||
|
- février
|
||||||
|
- mars
|
||||||
|
- avril
|
||||||
|
- mai
|
||||||
|
- juin
|
||||||
|
- juillet
|
||||||
|
- août
|
||||||
|
- septembre
|
||||||
|
- octobre
|
||||||
|
- novembre
|
||||||
|
- décembre
|
||||||
|
order:
|
||||||
|
- :day
|
||||||
|
- :month
|
||||||
|
- :year
|
||||||
|
datetime:
|
||||||
|
distance_in_words:
|
||||||
|
about_x_hours:
|
||||||
|
one: environ une heure
|
||||||
|
other: environ %{count} heures
|
||||||
|
about_x_months:
|
||||||
|
one: environ un mois
|
||||||
|
other: environ %{count} mois
|
||||||
|
about_x_years:
|
||||||
|
one: environ un an
|
||||||
|
other: environ %{count} ans
|
||||||
|
almost_x_years:
|
||||||
|
one: presque un an
|
||||||
|
other: presque %{count} ans
|
||||||
|
half_a_minute: une demi‑minute
|
||||||
|
less_than_x_seconds:
|
||||||
|
zero: moins d'une seconde
|
||||||
|
one: moins d'une seconde
|
||||||
|
other: moins de %{count} secondes
|
||||||
|
less_than_x_minutes:
|
||||||
|
zero: moins d'une minute
|
||||||
|
one: moins d'une minute
|
||||||
|
other: moins de %{count} minutes
|
||||||
|
over_x_years:
|
||||||
|
one: plus d'un an
|
||||||
|
other: plus de %{count} ans
|
||||||
|
x_seconds:
|
||||||
|
one: "%{count} seconde"
|
||||||
|
other: "%{count} secondes"
|
||||||
|
x_minutes:
|
||||||
|
one: "%{count} minute"
|
||||||
|
other: "%{count} minutes"
|
||||||
|
x_days:
|
||||||
|
one: "%{count} jour"
|
||||||
|
other: "%{count} jours"
|
||||||
|
x_months:
|
||||||
|
one: "%{count} mois"
|
||||||
|
other: "%{count} mois"
|
||||||
|
x_years:
|
||||||
|
one: "%{count} an"
|
||||||
|
other: "%{count} ans"
|
||||||
|
prompts:
|
||||||
|
second: Seconde
|
||||||
|
minute: Minute
|
||||||
|
hour: Heure
|
||||||
|
day: Jour
|
||||||
|
month: Mois
|
||||||
|
year: Année
|
||||||
|
errors:
|
||||||
|
format: "%{attribute} %{message}"
|
||||||
|
messages:
|
||||||
|
accepted: doit être accepté(e)
|
||||||
|
blank: doit être rempli(e)
|
||||||
|
confirmation: ne concorde pas avec %{attribute}
|
||||||
|
empty: doit être rempli(e)
|
||||||
|
equal_to: doit être égal à %{count}
|
||||||
|
even: doit être pair
|
||||||
|
exclusion: n'est pas disponible
|
||||||
|
greater_than: doit être supérieur à %{count}
|
||||||
|
greater_than_or_equal_to: doit être supérieur ou égal à %{count}
|
||||||
|
in: doit être dans l'intervalle %{count}
|
||||||
|
inclusion: n'est pas inclus(e) dans la liste
|
||||||
|
invalid: n'est pas valide
|
||||||
|
less_than: doit être inférieur à %{count}
|
||||||
|
less_than_or_equal_to: doit être inférieur ou égal à %{count}
|
||||||
|
model_invalid: 'Validation échouée : %{errors}'
|
||||||
|
not_a_number: n'est pas un nombre
|
||||||
|
not_an_integer: doit être un nombre entier
|
||||||
|
odd: doit être impair
|
||||||
|
other_than: doit être différent de %{count}
|
||||||
|
present: doit être vide
|
||||||
|
required: doit exister
|
||||||
|
taken: est déjà utilisé(e)
|
||||||
|
too_long:
|
||||||
|
one: est trop long (pas plus d'un caractère)
|
||||||
|
other: est trop long (pas plus de %{count} caractères)
|
||||||
|
too_short:
|
||||||
|
one: est trop court (au moins un caractère)
|
||||||
|
other: est trop court (au moins %{count} caractères)
|
||||||
|
wrong_length:
|
||||||
|
one: ne fait pas la bonne longueur (doit comporter un seul caractère)
|
||||||
|
other: ne fait pas la bonne longueur (doit comporter %{count} caractères)
|
||||||
|
template:
|
||||||
|
body: 'Veuillez vérifier les champs suivants : '
|
||||||
|
header:
|
||||||
|
one: 'Impossible d''enregistrer ce(tte) %{model} : %{count} erreur'
|
||||||
|
other: 'Impossible d''enregistrer ce(tte) %{model} : %{count} erreurs'
|
||||||
|
helpers:
|
||||||
|
select:
|
||||||
|
prompt: Veuillez sélectionner
|
||||||
|
submit:
|
||||||
|
create: Créer un(e) %{model}
|
||||||
|
submit: Enregistrer ce(tte) %{model}
|
||||||
|
update: Modifier ce(tte) %{model}
|
||||||
|
number:
|
||||||
|
currency:
|
||||||
|
format:
|
||||||
|
delimiter: " "
|
||||||
|
format: "%n %u"
|
||||||
|
precision: 2
|
||||||
|
separator: ","
|
||||||
|
significant: false
|
||||||
|
strip_insignificant_zeros: false
|
||||||
|
unit: "€"
|
||||||
|
format:
|
||||||
|
delimiter: " "
|
||||||
|
precision: 3
|
||||||
|
round_mode: default
|
||||||
|
separator: ","
|
||||||
|
significant: false
|
||||||
|
strip_insignificant_zeros: false
|
||||||
|
human:
|
||||||
|
decimal_units:
|
||||||
|
format: "%n %u"
|
||||||
|
units:
|
||||||
|
billion: milliard
|
||||||
|
million: million
|
||||||
|
quadrillion: million de milliards
|
||||||
|
thousand: millier
|
||||||
|
trillion: billion
|
||||||
|
unit: ''
|
||||||
|
format:
|
||||||
|
delimiter: ''
|
||||||
|
precision: 3
|
||||||
|
significant: true
|
||||||
|
strip_insignificant_zeros: true
|
||||||
|
storage_units:
|
||||||
|
format: "%n %u"
|
||||||
|
units:
|
||||||
|
byte:
|
||||||
|
one: octet
|
||||||
|
other: octets
|
||||||
|
eb: Eo
|
||||||
|
gb: Go
|
||||||
|
kb: ko
|
||||||
|
mb: Mo
|
||||||
|
pb: Po
|
||||||
|
tb: To
|
||||||
|
percentage:
|
||||||
|
format:
|
||||||
|
delimiter: ''
|
||||||
|
format: "%n%"
|
||||||
|
precision:
|
||||||
|
format:
|
||||||
|
delimiter: ''
|
||||||
|
support:
|
||||||
|
array:
|
||||||
|
last_word_connector: " et "
|
||||||
|
two_words_connector: " et "
|
||||||
|
words_connector: ", "
|
||||||
|
time:
|
||||||
|
am: am
|
||||||
|
formats:
|
||||||
|
default: "%d %B %Y %Hh %Mmin %Ss"
|
||||||
|
long: "%A %d %B %Y %Hh%M"
|
||||||
|
short: "%d %b %Hh%M"
|
||||||
|
pm: pm
|
@ -6,7 +6,8 @@ Rails.application.routes.draw do
|
|||||||
# Defines the root path route ("/")
|
# Defines the root path route ("/")
|
||||||
get :home, controller: :profile
|
get :home, controller: :profile
|
||||||
|
|
||||||
resources :bubbles, except: [:show] do
|
resources :bubbles do
|
||||||
|
resources :spendings, only: [:new, :create]
|
||||||
get :confirm_destroy
|
get :confirm_destroy
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
12
db/migrate/20231231172005_add_spending_to_bubbles.rb
Normal file
12
db/migrate/20231231172005_add_spending_to_bubbles.rb
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
class AddSpendingToBubbles < ActiveRecord::Migration[7.0]
|
||||||
|
def change
|
||||||
|
create_table :spendings, id: :uuid do |t|
|
||||||
|
t.decimal :amount, null: false, default: 0
|
||||||
|
t.text :description, null: false, default: ""
|
||||||
|
t.references :bubble, type: :uuid, null: false
|
||||||
|
t.references :owner, type: :uuid, foreign_key: { to_table: :users }, null: false
|
||||||
|
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
14
db/schema.rb
generated
14
db/schema.rb
generated
@ -10,7 +10,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[7.0].define(version: 2023_12_10_091848) do
|
ActiveRecord::Schema[7.0].define(version: 2023_12_31_172005) 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 "pgcrypto"
|
enable_extension "pgcrypto"
|
||||||
enable_extension "plpgsql"
|
enable_extension "plpgsql"
|
||||||
@ -25,6 +25,17 @@ ActiveRecord::Schema[7.0].define(version: 2023_12_10_091848) do
|
|||||||
t.index ["owner_id"], name: "index_bubbles_on_owner_id"
|
t.index ["owner_id"], name: "index_bubbles_on_owner_id"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
create_table "spendings", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
|
||||||
|
t.decimal "amount", default: "0.0", null: false
|
||||||
|
t.text "description", default: "", null: false
|
||||||
|
t.uuid "bubble_id", null: false
|
||||||
|
t.uuid "owner_id", null: false
|
||||||
|
t.datetime "created_at", null: false
|
||||||
|
t.datetime "updated_at", null: false
|
||||||
|
t.index ["bubble_id"], name: "index_spendings_on_bubble_id"
|
||||||
|
t.index ["owner_id"], name: "index_spendings_on_owner_id"
|
||||||
|
end
|
||||||
|
|
||||||
create_table "users", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
|
create_table "users", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
|
||||||
t.string "email", default: "", null: false
|
t.string "email", default: "", null: false
|
||||||
t.string "encrypted_password", default: "", null: false
|
t.string "encrypted_password", default: "", null: false
|
||||||
@ -38,4 +49,5 @@ ActiveRecord::Schema[7.0].define(version: 2023_12_10_091848) do
|
|||||||
end
|
end
|
||||||
|
|
||||||
add_foreign_key "bubbles", "users", column: "owner_id"
|
add_foreign_key "bubbles", "users", column: "owner_id"
|
||||||
|
add_foreign_key "spendings", "users", column: "owner_id"
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user