From 36d85c0cf7f90c8130552e33cc5bf6fafef47356 Mon Sep 17 00:00:00 2001 From: Sylvain Date: Wed, 16 Mar 2022 17:10:27 +0100 Subject: [PATCH] (bug) unable to delete an administrator who had closed an accounting period --- CHANGELOG.md | 1 + app/models/accounting_period.rb | 12 ++++++--- app/models/user.rb | 2 ++ ..._update_closed_by_on_accounting_periods.rb | 27 +++++++++++++++++++ db/schema.rb | 20 +++++++------- 5 files changed, 48 insertions(+), 14 deletions(-) create mode 100644 db/migrate/20220316133304_allow_update_closed_by_on_accounting_periods.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ce912a7c..29d7e61a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ - Fix a bug: a sentence was not linked to a translation key - Fix a bug: the version check may be scheduled at an invalid time - Fix a bug: the moment-timezone relied on an outdated version of moment with a case-sensitive locale file +- Fix a bug: unable to delete an administrator who had closed an accounting period - Fix a security issue: updated image_processing to 1.12.2 to fix [CVE-2022-24720](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-24720) - Fix a security issue: updated url-parse to 1.5.10 to fix [CVE-2022-0686](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-0686), [CVE-2022-0691](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-0691), [CVE-2022-0639](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-0639) and [CVE-2022-0512](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-0512) - Fix a security issue: updated rails to 5.2.6.3 to fix [CVE-2022-21831](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-21831), [CVE-2022-23633](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-23633) diff --git a/app/models/accounting_period.rb b/app/models/accounting_period.rb index a0377c1d7..0926bd8de 100644 --- a/app/models/accounting_period.rb +++ b/app/models/accounting_period.rb @@ -19,6 +19,8 @@ class AccountingPeriod < ApplicationRecord validates_with PeriodOverlapValidator validates_with PeriodIntegrityValidator + belongs_to :user, class_name: 'User', foreign_key: 'closed_by' + def delete false end @@ -79,13 +81,15 @@ class AccountingPeriod < ApplicationRecord end def compute_totals - period_invoices = invoices_with_vat(invoices.where(type: nil)) - period_avoirs = invoices_with_vat(invoices.where(type: 'Avoir')) + period_invoices = invoices_with_vat(invoices.where(type: nil).includes([:invoice_items])) + period_avoirs = invoices_with_vat(invoices.where(type: 'Avoir').includes([:invoice_items])) self.period_total = (period_invoices.map(&method(:price_without_taxe)).reduce(:+) || 0) - (period_avoirs.map(&method(:price_without_taxe)).reduce(:+) || 0) - all_invoices = invoices_with_vat(Invoice.where('CAST(created_at AS DATE) <= :end_date AND type IS NULL', end_date: end_at)) - all_avoirs = invoices_with_vat(Invoice.where("CAST(created_at AS DATE) <= :end_date AND type = 'Avoir'", end_date: end_at)) + all_invoices = invoices_with_vat(Invoice.where('CAST(created_at AS DATE) <= :end_date AND type IS NULL', end_date: end_at) + .includes([:invoice_items])) + all_avoirs = invoices_with_vat(Invoice.where("CAST(created_at AS DATE) <= :end_date AND type = 'Avoir'", end_date: end_at) + .includes([:invoice_items])) self.perpetual_total = (all_invoices.map(&method(:price_without_taxe)).reduce(:+) || 0) - (all_avoirs.map(&method(:price_without_taxe)).reduce(:+) || 0) self.footprint = compute_footprint diff --git a/app/models/user.rb b/app/models/user.rb index 40efffd78..05c813900 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -49,6 +49,8 @@ class User < ApplicationRecord has_one :payment_gateway_object, as: :item + has_many :accounting_periods, foreign_key: 'closed_by', dependent: :nullify + # fix for create admin user before_save do email&.downcase! diff --git a/db/migrate/20220316133304_allow_update_closed_by_on_accounting_periods.rb b/db/migrate/20220316133304_allow_update_closed_by_on_accounting_periods.rb new file mode 100644 index 000000000..2d95fac0a --- /dev/null +++ b/db/migrate/20220316133304_allow_update_closed_by_on_accounting_periods.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +# This migration removes the NotNull constraint on the foreign key of closed_by +# column on the accounting_periods table. This is needed because it prevented +# to delete an admin who closed an accounting period. +class AllowUpdateClosedByOnAccountingPeriods < ActiveRecord::Migration[5.2] + def up + execute <<~SQL + CREATE OR REPLACE RULE accounting_periods_upd_protect AS ON UPDATE + TO accounting_periods + WHERE ( + new.start_at <> old.start_at OR + new.end_at <> old.end_at OR + new.closed_at <> old.closed_at OR + new.period_total <> old.period_total OR + new.perpetual_total <> old.perpetual_total) + DO INSTEAD NOTHING; + SQL + end + + def down + execute <<~SQL + CREATE OR REPLACE RULE accounting_periods_upd_protect AS ON UPDATE + TO accounting_periods DO INSTEAD NOTHING; + SQL + end +end diff --git a/db/schema.rb b/db/schema.rb index af2f63d2f..eebcc5570 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.define(version: 2022_02_25_143203) do +ActiveRecord::Schema.define(version: 2022_03_16_133304) do # These are extensions that must be enabled in order to support this database enable_extension "fuzzystrmatch" @@ -19,8 +19,8 @@ ActiveRecord::Schema.define(version: 2022_02_25_143203) do enable_extension "unaccent" create_table "abuses", id: :serial, force: :cascade do |t| - t.string "signaled_type" t.integer "signaled_id" + t.string "signaled_type" t.string "first_name" t.string "last_name" t.string "email" @@ -49,8 +49,8 @@ ActiveRecord::Schema.define(version: 2022_02_25_143203) do t.string "locality" t.string "country" t.string "postal_code" - t.string "placeable_type" t.integer "placeable_id" + t.string "placeable_type" t.datetime "created_at" t.datetime "updated_at" end @@ -64,8 +64,8 @@ ActiveRecord::Schema.define(version: 2022_02_25_143203) do end create_table "assets", id: :serial, force: :cascade do |t| - t.string "viewable_type" t.integer "viewable_id" + t.string "viewable_type" t.string "attachment" t.string "type" t.datetime "created_at" @@ -133,8 +133,8 @@ ActiveRecord::Schema.define(version: 2022_02_25_143203) do end create_table "credits", id: :serial, force: :cascade do |t| - t.string "creditable_type" t.integer "creditable_id" + t.string "creditable_type" t.integer "plan_id" t.integer "hours" t.datetime "created_at" @@ -356,15 +356,15 @@ ActiveRecord::Schema.define(version: 2022_02_25_143203) do create_table "notifications", id: :serial, force: :cascade do |t| t.integer "receiver_id" - t.string "attached_object_type" t.integer "attached_object_id" + t.string "attached_object_type" t.integer "notification_type_id" t.boolean "is_read", default: false t.datetime "created_at" t.datetime "updated_at" t.string "receiver_type" t.boolean "is_send", default: false - t.jsonb "meta_data", default: "{}" + t.jsonb "meta_data", default: {} t.index ["notification_type_id"], name: "index_notifications_on_notification_type_id" t.index ["receiver_id"], name: "index_notifications_on_receiver_id" end @@ -540,8 +540,8 @@ ActiveRecord::Schema.define(version: 2022_02_25_143203) do create_table "prices", id: :serial, force: :cascade do |t| t.integer "group_id" t.integer "plan_id" - t.string "priceable_type" t.integer "priceable_id" + t.string "priceable_type" t.integer "amount" t.datetime "created_at", null: false t.datetime "updated_at", null: false @@ -651,8 +651,8 @@ ActiveRecord::Schema.define(version: 2022_02_25_143203) do t.text "message" t.datetime "created_at" t.datetime "updated_at" - t.string "reservable_type" t.integer "reservable_id" + t.string "reservable_type" t.integer "nb_reserve_places" t.integer "statistic_profile_id" t.index ["reservable_type", "reservable_id"], name: "index_reservations_on_reservable_type_and_reservable_id" @@ -661,8 +661,8 @@ ActiveRecord::Schema.define(version: 2022_02_25_143203) do create_table "roles", id: :serial, force: :cascade do |t| t.string "name" - t.string "resource_type" t.integer "resource_id" + t.string "resource_type" t.datetime "created_at" t.datetime "updated_at" t.index ["name", "resource_type", "resource_id"], name: "index_roles_on_name_and_resource_type_and_resource_id"