feat: added spendings for bubbles

Signed-off-by: Louis Vallat <louis@louis-vallat.xyz>
This commit is contained in:
Louis Vallat 2023-12-31 19:06:33 +01:00
parent 11f5929a1a
commit edf60e9557
No known key found for this signature in database
GPG Key ID: 0C87282F76E61283
14 changed files with 349 additions and 2 deletions

View File

@ -9,6 +9,11 @@ class BubblesController < ApplicationController
@bubble = Bubble.new
end
def show
@bubble = Bubble.find(params[:id])
@spendings = @bubble.spendings.unscope(:order).order(created_at: :desc)
end
def create
if Bubble.new(allowed_params).save
redirect_to bubbles_path, flash: { success: "Créé" }

View 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

View File

@ -21,4 +21,5 @@
class Bubble < ApplicationRecord
validates :name, presence: true, allow_blank: false
belongs_to :owner, class_name: "User"
has_many :spendings
end

25
app/models/spending.rb Normal file
View 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

View File

@ -23,4 +23,5 @@ class User < ApplicationRecord
:recoverable, :rememberable, :validatable
has_many :bubbles, foreign_key: :owner_id
has_many :spendings, foreign_key: :owner_id
end

View File

@ -11,6 +11,7 @@
<td><%= bubble.name %></td>
<td><%= bubble.description %></td>
<td>
<%= link_to "Voir", bubble_path(bubble) %>
<%= link_to "Éditer", edit_bubble_path(bubble) %>
<%= link_to "Supprimer", bubble_confirm_destroy_path(bubble) %>
</td>

View 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>

View File

@ -0,0 +1 @@
<%= render partial: "spendings/shared/form", locals: { spending: @spending, bubble: @bubble } %>

View 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 %>

View File

@ -18,5 +18,7 @@ module DebtManager
#
# config.time_zone = "Central Time (US & Canada)"
# config.eager_load_paths << Rails.root.join("extras")
config.i18n.default_locale = :fr
end
end

221
config/locales/fr.yml Normal file
View 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 demiminute
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

View File

@ -6,7 +6,8 @@ Rails.application.routes.draw do
# Defines the root path route ("/")
get :home, controller: :profile
resources :bubbles, except: [:show] do
resources :bubbles do
resources :spendings, only: [:new, :create]
get :confirm_destroy
end
end

View 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
View File

@ -10,7 +10,7 @@
#
# 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
enable_extension "pgcrypto"
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"
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|
t.string "email", 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
add_foreign_key "bubbles", "users", column: "owner_id"
add_foreign_key "spendings", "users", column: "owner_id"
end