From edf60e95574ec4215e4f3f3b88ddcb186ff5b139 Mon Sep 17 00:00:00 2001 From: Louis Vallat Date: Sun, 31 Dec 2023 19:06:33 +0100 Subject: [PATCH] feat: added spendings for bubbles Signed-off-by: Louis Vallat --- app/controllers/bubbles_controller.rb | 5 + app/controllers/spendings_controller.rb | 23 ++ app/models/bubble.rb | 1 + app/models/spending.rb | 25 ++ app/models/user.rb | 1 + app/views/bubbles/index.html.erb | 1 + app/views/bubbles/show.html.erb | 22 ++ app/views/spendings/new.html.erb | 1 + app/views/spendings/shared/_form.html.erb | 20 ++ config/application.rb | 2 + config/locales/fr.yml | 221 ++++++++++++++++++ config/routes.rb | 3 +- .../20231231172005_add_spending_to_bubbles.rb | 12 + db/schema.rb | 14 +- 14 files changed, 349 insertions(+), 2 deletions(-) create mode 100644 app/controllers/spendings_controller.rb create mode 100644 app/models/spending.rb create mode 100644 app/views/bubbles/show.html.erb create mode 100644 app/views/spendings/new.html.erb create mode 100644 app/views/spendings/shared/_form.html.erb create mode 100644 config/locales/fr.yml create mode 100644 db/migrate/20231231172005_add_spending_to_bubbles.rb diff --git a/app/controllers/bubbles_controller.rb b/app/controllers/bubbles_controller.rb index f65d925..f3fe14f 100644 --- a/app/controllers/bubbles_controller.rb +++ b/app/controllers/bubbles_controller.rb @@ -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éé" } diff --git a/app/controllers/spendings_controller.rb b/app/controllers/spendings_controller.rb new file mode 100644 index 0000000..0b18e97 --- /dev/null +++ b/app/controllers/spendings_controller.rb @@ -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 diff --git a/app/models/bubble.rb b/app/models/bubble.rb index 36c360d..59181b7 100644 --- a/app/models/bubble.rb +++ b/app/models/bubble.rb @@ -21,4 +21,5 @@ class Bubble < ApplicationRecord validates :name, presence: true, allow_blank: false belongs_to :owner, class_name: "User" + has_many :spendings end diff --git a/app/models/spending.rb b/app/models/spending.rb new file mode 100644 index 0000000..078a6a1 --- /dev/null +++ b/app/models/spending.rb @@ -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 diff --git a/app/models/user.rb b/app/models/user.rb index 9aac96e..cd3ef09 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -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 diff --git a/app/views/bubbles/index.html.erb b/app/views/bubbles/index.html.erb index 2e8ea97..784bd02 100644 --- a/app/views/bubbles/index.html.erb +++ b/app/views/bubbles/index.html.erb @@ -11,6 +11,7 @@ <%= bubble.name %> <%= bubble.description %> + <%= link_to "Voir", bubble_path(bubble) %> <%= link_to "Éditer", edit_bubble_path(bubble) %> <%= link_to "Supprimer", bubble_confirm_destroy_path(bubble) %> diff --git a/app/views/bubbles/show.html.erb b/app/views/bubbles/show.html.erb new file mode 100644 index 0000000..e13d15e --- /dev/null +++ b/app/views/bubbles/show.html.erb @@ -0,0 +1,22 @@ +

<%= @bubble.name %>

+

<%= @bubble.description %>

+<%= link_to "Nouvelle dépense", new_bubble_spending_path(@bubble) %> + + + + + + + + + + + <% @spendings.each do |spending| %> + + + + + + <% end %> + +
MontantOrigineRaison
<%= number_to_currency(spending.amount, unit: "€") %><%= spending.owner.email %><%= spending.description %>
diff --git a/app/views/spendings/new.html.erb b/app/views/spendings/new.html.erb new file mode 100644 index 0000000..3d2b31d --- /dev/null +++ b/app/views/spendings/new.html.erb @@ -0,0 +1 @@ +<%= render partial: "spendings/shared/form", locals: { spending: @spending, bubble: @bubble } %> \ No newline at end of file diff --git a/app/views/spendings/shared/_form.html.erb b/app/views/spendings/shared/_form.html.erb new file mode 100644 index 0000000..e214a25 --- /dev/null +++ b/app/views/spendings/shared/_form.html.erb @@ -0,0 +1,20 @@ +<%= form_for spending, url: spending.persisted? ? spending_path(spending) : bubble_spendings_path(bubble) do |f| %> + +
+ <%= f.label :amount, "Valeur" %> + <%= f.number_field :amount, required: true, step: 0.01 %> +
+ +
+ <%= f.label :description, "Description" %> + <%= f.text_area :description %> +
+ +
+ <%= 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 %> +
+ + <%= f.submit "Sauvegarder" %> + +<% end %> \ No newline at end of file diff --git a/config/application.rb b/config/application.rb index a03efd1..d3df40c 100644 --- a/config/application.rb +++ b/config/application.rb @@ -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 diff --git a/config/locales/fr.yml b/config/locales/fr.yml new file mode 100644 index 0000000..33c7948 --- /dev/null +++ b/config/locales/fr.yml @@ -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 \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index 925f211..a4d5f9a 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -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 diff --git a/db/migrate/20231231172005_add_spending_to_bubbles.rb b/db/migrate/20231231172005_add_spending_to_bubbles.rb new file mode 100644 index 0000000..b90fee4 --- /dev/null +++ b/db/migrate/20231231172005_add_spending_to_bubbles.rb @@ -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 diff --git a/db/schema.rb b/db/schema.rb index 90d32be..91001f3 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -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