Add depubliee state to procedures

This commit is contained in:
Paul Chavard 2019-12-04 15:45:06 +01:00
parent 4a614f6f4f
commit 2f060fc30a
16 changed files with 207 additions and 119 deletions

View file

@ -52,7 +52,7 @@ class Admin::ProceduresController < AdminController
def destroy
procedure = current_administrateur.procedures.find(params[:id])
if procedure.publiee_ou_close?
if procedure.locked?
return render json: {}, status: 401
end

View file

@ -10,7 +10,7 @@ module Instructeurs
.procedures
.with_attached_logo
.includes(:defaut_groupe_instructeur)
.order(closed_at: :desc, archived_at: :desc, published_at: :desc, created_at: :desc)
.order(closed_at: :desc, archived_at: :desc, unpublished_at: :desc, published_at: :desc, created_at: :desc)
dossiers = current_instructeur.dossiers.joins(:groupe_instructeur)
@dossiers_count_per_procedure = dossiers.all_state.group('groupe_instructeurs.procedure_id').reorder(nil).count

View file

@ -25,6 +25,7 @@ class ProcedureDashboard < Administrate::BaseDashboard
for_individual: Field::Boolean,
auto_archive_on: Field::DateTime,
published_at: Field::DateTime,
unpublished_at: Field::DateTime,
hidden_at: Field::DateTime,
closed_at: Field::DateTime,
whitelisted_at: Field::DateTime,
@ -48,7 +49,8 @@ class ProcedureDashboard < Administrate::BaseDashboard
:libelle,
:service,
:dossiers,
:published_at
:published_at,
:unpublished_at
].freeze
# SHOW_PAGE_ATTRIBUTES

View file

@ -209,6 +209,11 @@ type Demarche {
"""
dateCreation: ISO8601DateTime!
"""
Date de la dépublication.
"""
dateDepublication: ISO8601DateTime
"""
Date de la dernière modification.
"""
@ -309,6 +314,11 @@ enum DemarcheState {
"""
close
"""
Depubliee
"""
depubliee
"""
Publiée
"""

View file

@ -26,6 +26,7 @@ module Types
field :date_creation, GraphQL::Types::ISO8601DateTime, "Date de la création.", null: false, method: :created_at
field :date_publication, GraphQL::Types::ISO8601DateTime, "Date de la publication.", null: false, method: :published_at
field :date_derniere_modification, GraphQL::Types::ISO8601DateTime, "Date de la dernière modification.", null: false, method: :updated_at
field :date_depublication, GraphQL::Types::ISO8601DateTime, "Date de la dépublication.", null: true, method: :unpublished_at
field :date_fermeture, GraphQL::Types::ISO8601DateTime, "Date de la fermeture.", null: true, method: :closed_at
field :groupe_instructeurs, [Types::GroupeInstructeurType], null: false

View file

@ -14,7 +14,7 @@ class Administrateur < ApplicationRecord
before_validation -> { sanitize_email(:email) }
scope :inactive, -> { joins(:user).where(users: { last_sign_in_at: nil }) }
scope :with_publiees_ou_closes, -> { joins(:procedures).where(procedures: { aasm_state: [:publiee, :archivee, :close] }) }
scope :with_publiees_ou_closes, -> { joins(:procedures).where(procedures: { aasm_state: [:publiee, :archivee, :close, :depubliee] }) }
# validate :password_complexity, if: Proc.new { |a| Devise.password_length.include?(a.password.try(:size)) }

View file

@ -267,7 +267,7 @@ class Dossier < ApplicationRecord
end
def can_transition_to_en_construction?
!procedure.close? && brouillon?
brouillon? && procedure.dossier_can_transition_to_en_construction?
end
def can_be_updated_by_user?

View file

@ -46,8 +46,8 @@ class Procedure < ApplicationRecord
default_scope { where(hidden_at: nil) }
scope :brouillons, -> { where(aasm_state: :brouillon) }
scope :publiees, -> { where(aasm_state: :publiee) }
scope :closes, -> { where(aasm_state: [:archivee, :close]) }
scope :publiees_ou_closes, -> { where(aasm_state: [:publiee, :close, :archivee]) }
scope :closes, -> { where(aasm_state: [:archivee, :close, :depubliee]) }
scope :publiees_ou_closes, -> { where(aasm_state: [:publiee, :close, :archivee, :depubliee]) }
scope :by_libelle, -> { order(libelle: :asc) }
scope :created_during, -> (range) { where(created_at: range) }
scope :cloned_from_library, -> { where(cloned_from_library: true) }
@ -77,7 +77,7 @@ class Procedure < ApplicationRecord
validates :lien_site_web, presence: true, if: :publiee?
validate :validate_for_publication, on: :publication
validate :check_juridique
validates :path, presence: true, format: { with: /\A[a-z0-9_\-]{3,50}\z/ }, uniqueness: { scope: [:path, :closed_at, :archived_at, :hidden_at], case_sensitive: false }
validates :path, presence: true, format: { with: /\A[a-z0-9_\-]{3,50}\z/ }, uniqueness: { scope: [:path, :closed_at, :hidden_at, :unpublished_at], case_sensitive: false }
# FIXME: remove duree_conservation_required flag once all procedures are converted to the new style
validates :duree_conservation_dossiers_dans_ds, allow_nil: false, numericality: { only_integer: true, greater_than_or_equal_to: 1, less_than_or_equal_to: MAX_DUREE_CONSERVATION }, if: :durees_conservation_required
validates :duree_conservation_dossiers_hors_ds, allow_nil: false, numericality: { only_integer: true, greater_than_or_equal_to: 0 }, if: :durees_conservation_required
@ -97,10 +97,12 @@ class Procedure < ApplicationRecord
state :publiee
state :close
state :hidden
state :depubliee
event :publish, before: :before_publish, after: :after_publish do
transitions from: :brouillon, to: :publiee
transitions from: :close, to: :publiee
transitions from: :depubliee, to: :publiee
end
event :close, after: :after_close do
@ -112,6 +114,10 @@ class Procedure < ApplicationRecord
transitions from: :publiee, to: :hidden
transitions from: :close, to: :hidden
end
event :unpublish, after: :after_unpublish do
transitions from: :publiee, to: :depubliee
end
end
def publish_or_reopen!(administrateur)
@ -122,7 +128,7 @@ class Procedure < ApplicationRecord
other_procedure = other_procedure_with_path(path)
if other_procedure.present? && administrateur.owns?(other_procedure)
other_procedure.close!
other_procedure.unpublish!
end
publish!
@ -282,15 +288,15 @@ class Procedure < ApplicationRecord
end
def locked?
publiee_ou_close?
publiee? || close? || depubliee?
end
def accepts_new_dossiers?
!close?
publiee? || brouillon?
end
def publiee_ou_close?
publiee? || close?
def dossier_can_transition_to_en_construction?
accepts_new_dossiers? || depubliee?
end
def expose_legacy_carto_api?
@ -379,6 +385,7 @@ class Procedure < ApplicationRecord
procedure.aasm_state = :brouillon
procedure.archived_at = nil
procedure.closed_at = nil
procedure.unpublished_at = nil
procedure.published_at = nil
procedure.lien_notice = nil
@ -615,7 +622,7 @@ class Procedure < ApplicationRecord
end
def before_publish
update!(archived_at: nil, closed_at: nil)
update!(archived_at: nil, closed_at: nil, unpublished_at: nil)
end
def after_publish
@ -635,6 +642,10 @@ class Procedure < ApplicationRecord
purge_export_files
end
def after_unpublish
update!(unpublished_at: Time.zone.now)
end
def update_juridique_required
self.juridique_required ||= (cadre_juridique.present? || deliberation.attached?)
true

View file

@ -19,13 +19,13 @@
%td.col-xs-6= link_to(procedure.libelle, admin_procedure_href)
- if procedure.publiee?
%td.procedure-lien= link_to(procedure_lien(procedure), procedure_lien(procedure))
- if procedure.publiee_ou_close?
- if procedure.locked?
%td= link_to(procedure.published_at.present? ? try_format_datetime(procedure.published_at) : "", admin_procedure_href)
- else
%td= link_to(try_format_datetime(procedure.created_at), admin_procedure_href)
%td
= link_to('Cloner', admin_procedure_clone_path(procedure.id), data: { method: :put }, class: 'btn-sm btn-primary clone-btn')
- if !procedure.publiee_ou_close?
- if !procedure.locked?
= link_to('X', url_for(controller: 'admin/procedures', action: :destroy, id: procedure.id), data: { method: :delete, confirm: "Confirmez-vous la suppression de la démarche ? \n\n Attention : toute suppression est définitive et sappliquera aux éventuels autres administrateurs de cette démarche !" }, class: 'btn-sm btn-danger')
= smart_listing.paginate

View file

@ -0,0 +1,63 @@
%ul.procedure-list
- procedures.each do |p|
%li.procedure-item.flex.align-start
= link_to(instructeur_procedure_path(p)) do
.flex
.procedure-logo{ style: "background-image: url(#{p.logo_url})" }
.procedure-details
%p.procedure-title
= procedure_libelle p
%ul.procedure-stats.flex
%li
%object
= link_to(instructeur_procedure_path(p, statut: 'a-suivre')) do
- a_suivre_count = dossiers_a_suivre_count_per_procedure[p.id] || 0
.stats-number
= a_suivre_count
.stats-legend
à suivre
%li
%object
= link_to(instructeur_procedure_path(p, statut: 'suivis')) do
- if current_instructeur.procedures_with_notifications(:en_cours).include?(p)
%span.notifications{ 'aria-label': "notifications" }
- followed_count = followed_dossiers_count_per_procedure[p.id] || 0
.stats-number
= followed_count
.stats-legend
= t('pluralize.followed', count: followed_count)
%li
%object
= link_to(instructeur_procedure_path(p, statut: 'traites')) do
- if current_instructeur.procedures_with_notifications(:termine).include?(p)
%span.notifications{ 'aria-label': "notifications" }
- termines_count = dossiers_termines_count_per_procedure[p.id] || 0
.stats-number
= termines_count
.stats-legend
= t('pluralize.processed', count: termines_count)
%li
%object
= link_to(instructeur_procedure_path(p, statut: 'tous')) do
- dossier_count = dossiers_count_per_procedure[p.id] || 0
.stats-number
= dossier_count
.stats-legend
= t('pluralize.case', count: dossier_count)
%li
%object
= link_to(instructeur_procedure_path(p, statut: 'archives')) do
- archived_count = dossiers_archived_count_per_procedure[p.id] || 0
.stats-number
= archived_count
.stats-legend
= t('pluralize.archived', count: archived_count)
- if p.close?
.procedure-status
%span.label Close
- elsif p.depubliee?
.procedure-status
%span.label Dépubliée

View file

@ -3,64 +3,9 @@
.container
%h1.page-title Démarches
%ul.procedure-list
- @procedures.each do |p|
%li.procedure-item.flex.align-start
= link_to(instructeur_procedure_path(p)) do
.flex
.procedure-logo{ style: "background-image: url(#{p.logo_url})" }
.procedure-details
%p.procedure-title
= procedure_libelle p
%ul.procedure-stats.flex
%li
%object
= link_to(instructeur_procedure_path(p, statut: 'a-suivre')) do
- a_suivre_count = @dossiers_a_suivre_count_per_procedure[p.id] || 0
.stats-number
= a_suivre_count
.stats-legend
à suivre
%li
%object
= link_to(instructeur_procedure_path(p, statut: 'suivis')) do
- if current_instructeur.procedures_with_notifications(:en_cours).include?(p)
%span.notifications{ 'aria-label': "notifications" }
- followed_count = @followed_dossiers_count_per_procedure[p.id] || 0
.stats-number
= followed_count
.stats-legend
= t('pluralize.followed', count: followed_count)
%li
%object
= link_to(instructeur_procedure_path(p, statut: 'traites')) do
- if current_instructeur.procedures_with_notifications(:termine).include?(p)
%span.notifications{ 'aria-label': "notifications" }
- termines_count = @dossiers_termines_count_per_procedure[p.id] || 0
.stats-number
= termines_count
.stats-legend
= t('pluralize.processed', count: termines_count)
%li
%object
= link_to(instructeur_procedure_path(p, statut: 'tous')) do
- dossier_count = @dossiers_count_per_procedure[p.id] || 0
.stats-number
= dossier_count
.stats-legend
= t('pluralize.case', count: dossier_count)
%li
%object
= link_to(instructeur_procedure_path(p, statut: 'archives')) do
- archived_count = @dossiers_archived_count_per_procedure[p.id] || 0
.stats-number
= archived_count
.stats-legend
= t('pluralize.archived', count: archived_count)
- if p.close?
.procedure-status
%span.label Close
= render partial: 'instructeurs/procedures/list', locals: { procedures: @procedures,
dossiers_count_per_procedure: @dossiers_count_per_procedure,
dossiers_a_suivre_count_per_procedure: @dossiers_a_suivre_count_per_procedure,
dossiers_archived_count_per_procedure: @dossiers_archived_count_per_procedure,
dossiers_termines_count_per_procedure: @dossiers_termines_count_per_procedure,
followed_dossiers_count_per_procedure: @followed_dossiers_count_per_procedure }

View file

@ -164,7 +164,7 @@ fr:
attributes:
path:
taken: est déjà utilisé par une démarche. Vous ne pouvez pas lutiliser car il appartient à un autre administrateur.
taken_can_be_claimed: est identique à celui dune autre de vos démarches publiées. Si vous publiez cette démarche, lancienne sera archivée et ne sera plus accessible au public.
taken_can_be_claimed: est identique à celui dune autre de vos démarches publiées. Si vous publiez cette démarche, lancienne sera dépubliée et ne sera plus accessible au public. Les utilisateurs qui ont commencé un brouillon vont pouvoir le déposer.
invalid: n'est pas valide. Il doit comporter au moins 3 caractères, au plus 50 caractères et seuls les caractères a-z, 0-9, '_' et '-' sont autorisés.
errors:

View file

@ -184,8 +184,8 @@ describe Admin::ProceduresController, type: :controller do
expect(flash[:notice]).to have_content 'Démarche publiée'
end
it 'archive previous procedure' do
expect(procedure2.close?).to be_truthy
it 'depubliee previous procedure' do
expect(procedure2.depubliee?).to be_truthy
end
end

View file

@ -168,6 +168,14 @@ FactoryBot.define do
end
end
trait :unpublished do
after(:build) do |procedure, _evaluator|
procedure.path = generate(:published_path)
procedure.publish!
procedure.unpublish!
end
end
trait :hidden do
after(:build) do |procedure, _evaluator|
procedure.path = generate(:published_path)

View file

@ -29,7 +29,7 @@ feature 'As an administrateur I wanna clone a procedure', js: true do
within '#publish-modal' do
expect(find_field('procedure_path').value).to eq 'libelle-de-la-procedure'
expect(page).to have_text('ancienne sera archivée')
expect(page).to have_text('ancienne sera dépubliée')
fill_in 'lien_site_web', with: 'http://some.website'
click_on 'publish'
end

View file

@ -336,22 +336,6 @@ describe Procedure do
end
end
describe 'locked?' do
let(:procedure) { create(:procedure, aasm_state: aasm_state) }
subject { procedure.locked? }
context 'when procedure is in brouillon status' do
let(:aasm_state) { :brouillon }
it { is_expected.to be_falsey }
end
context 'when procedure is in publiee status' do
let(:aasm_state) { :publiee }
it { is_expected.to be_truthy }
end
end
describe 'active' do
let(:procedure) { create(:procedure) }
subject { Procedure.active(procedure.id) }
@ -514,6 +498,7 @@ describe Procedure do
it 'Not published nor closed' do
expect(subject.closed_at).to be_nil
expect(subject.published_at).to be_nil
expect(subject.unpublished_at).to be_nil
expect(subject.aasm_state).to eq "brouillon"
expect(subject.path).not_to be_nil
end
@ -549,59 +534,122 @@ describe Procedure do
let(:procedure) { create(:procedure, path: 'example-path') }
let(:now) { Time.zone.now.beginning_of_minute }
after { Timecop.return }
context "without parent procedure" do
before do
Timecop.freeze(now)
procedure.publish!
end
it do
expect(procedure.closed_at).to be_nil
expect(procedure.published_at).to eq(now)
expect(Procedure.find_by(path: "example-path")).to eq(procedure)
expect(Procedure.find_by(path: "example-path").administrateurs).to eq(procedure.administrateurs)
end
end
end
describe "#publish_or_reopen!" do
let(:published_procedure) { create(:procedure, :published) }
let(:administrateur) { published_procedure.administrateurs.first }
let(:procedure) { create(:procedure, administrateurs: [administrateur]) }
let(:now) { Time.zone.now.beginning_of_minute }
context "without parent procedure" do
before do
Timecop.freeze(now)
procedure.path = published_procedure.path
procedure.publish_or_reopen!(administrateur)
end
it do
expect(procedure.closed_at).to be_nil
expect(procedure.published_at).to eq(now)
end
end
end
describe "#unpublish!" do
let(:procedure) { create(:procedure, :published) }
let(:now) { Time.zone.now.beginning_of_minute }
before do
Timecop.freeze(now)
procedure.publish!
procedure.unpublish!
end
after { Timecop.return }
it { expect(procedure.closed_at).to eq(nil) }
it { expect(procedure.published_at).to eq(now) }
it { expect(Procedure.find_by(path: "example-path")).to eq(procedure) }
it { expect(Procedure.find_by(path: "example-path").administrateurs).to eq(procedure.administrateurs) }
it {
expect(procedure.closed_at).to eq(nil)
expect(procedure.published_at).not_to be_nil
expect(procedure.unpublished_at).to eq(now)
}
end
describe "#brouillon?" do
let(:procedure_brouillon) { Procedure.new() }
let(:procedure_publiee) { Procedure.new(aasm_state: :publiee, published_at: Time.zone.now) }
let(:procedure_close) { Procedure.new(aasm_state: :close, published_at: Time.zone.now, closed_at: Time.zone.now) }
let(:procedure_brouillon) { build(:procedure) }
let(:procedure_publiee) { build(:procedure, :published) }
let(:procedure_close) { build(:procedure, :closed) }
let(:procedure_depubliee) { build(:procedure, :unpublished) }
it { expect(procedure_brouillon.brouillon?).to be_truthy }
it { expect(procedure_publiee.brouillon?).to be_falsey }
it { expect(procedure_close.brouillon?).to be_falsey }
it { expect(procedure_depubliee.brouillon?).to be_falsey }
end
describe "#publiee?" do
let(:procedure_brouillon) { Procedure.new() }
let(:procedure_publiee) { Procedure.new(aasm_state: :publiee, published_at: Time.zone.now) }
let(:procedure_close) { Procedure.new(aasm_state: :close, published_at: Time.zone.now, closed_at: Time.zone.now) }
let(:procedure_brouillon) { build(:procedure) }
let(:procedure_publiee) { build(:procedure, :published) }
let(:procedure_close) { build(:procedure, :closed) }
let(:procedure_depubliee) { build(:procedure, :unpublished) }
it { expect(procedure_brouillon.publiee?).to be_falsey }
it { expect(procedure_publiee.publiee?).to be_truthy }
it { expect(procedure_close.publiee?).to be_falsey }
it { expect(procedure_depubliee.publiee?).to be_falsey }
end
describe "#close?" do
let(:procedure_brouillon) { Procedure.new() }
let(:procedure_publiee) { Procedure.new(aasm_state: :publiee, published_at: Time.zone.now) }
let(:procedure_close) { Procedure.new(aasm_state: :close, published_at: Time.zone.now, closed_at: Time.zone.now) }
let(:procedure_brouillon) { build(:procedure) }
let(:procedure_publiee) { build(:procedure, :published) }
let(:procedure_close) { build(:procedure, :closed) }
let(:procedure_depubliee) { build(:procedure, :unpublished) }
it { expect(procedure_brouillon.close?).to be_falsey }
it { expect(procedure_publiee.close?).to be_falsey }
it { expect(procedure_close.close?).to be_truthy }
it { expect(procedure_depubliee.close?).to be_falsey }
end
describe "#publiee_ou_close?" do
let(:procedure_brouillon) { Procedure.new() }
let(:procedure_publiee) { Procedure.new(aasm_state: :publiee, published_at: Time.zone.now) }
let(:procedure_close) { Procedure.new(aasm_state: :close, published_at: Time.zone.now, closed_at: Time.zone.now) }
describe "#depubliee?" do
let(:procedure_brouillon) { build(:procedure) }
let(:procedure_publiee) { build(:procedure, :published) }
let(:procedure_close) { build(:procedure, :closed) }
let(:procedure_depubliee) { build(:procedure, :unpublished) }
it { expect(procedure_brouillon.publiee_ou_close?).to be_falsey }
it { expect(procedure_publiee.publiee_ou_close?).to be_truthy }
it { expect(procedure_close.publiee_ou_close?).to be_truthy }
it { expect(procedure_brouillon.depubliee?).to be_falsey }
it { expect(procedure_publiee.depubliee?).to be_falsey }
it { expect(procedure_close.depubliee?).to be_falsey }
it { expect(procedure_depubliee.depubliee?).to be_truthy }
end
describe 'archive' do
describe "#locked?" do
let(:procedure_brouillon) { build(:procedure) }
let(:procedure_publiee) { build(:procedure, :published) }
let(:procedure_close) { build(:procedure, :closed) }
let(:procedure_depubliee) { build(:procedure, :unpublished) }
it { expect(procedure_brouillon.locked?).to be_falsey }
it { expect(procedure_publiee.locked?).to be_truthy }
it { expect(procedure_close.locked?).to be_truthy }
it { expect(procedure_depubliee.locked?).to be_truthy }
end
describe 'close' do
let(:procedure) { create(:procedure, :published) }
let(:now) { Time.zone.now.beginning_of_minute }
before do