2024-04-29 00:17:15 +02:00
# frozen_string_literal: true
2023-03-21 15:59:03 +01:00
RSpec . describe DossierCloneConcern do
let ( :procedure ) do
2023-12-01 17:00:03 +01:00
create ( :procedure , types_de_champ_public : , types_de_champ_private : ) . tap ( & :publish! )
end
let ( :types_de_champ_public ) do
[
2023-03-21 15:59:03 +01:00
{ type : :text , libelle : " Un champ text " , stable_id : 99 } ,
{ type : :text , libelle : " Un autre champ text " , stable_id : 991 } ,
{ type : :yes_no , libelle : " Un champ yes no " , stable_id : 992 } ,
{ type : :repetition , libelle : " Un champ répétable " , stable_id : 993 , mandatory : true , children : [ { type : :text , libelle : 'Nom' , stable_id : 994 } ] }
2023-12-01 17:00:03 +01:00
]
2023-03-21 15:59:03 +01:00
end
2023-12-01 17:00:03 +01:00
let ( :types_de_champ_private ) { [ ] }
2023-05-12 17:53:40 +02:00
let ( :dossier ) { create ( :dossier , :en_construction , procedure : ) }
2023-03-21 15:59:03 +01:00
let ( :forked_dossier ) { dossier . find_or_create_editing_fork ( dossier . user ) }
2023-05-11 15:56:50 +02:00
describe '#clone' do
2023-12-01 17:00:03 +01:00
let ( :dossier ) { create ( :dossier , :en_construction , :with_populated_champs , procedure : ) }
let ( :types_de_champ_public ) { [ { } ] }
2024-07-01 15:31:32 +02:00
let ( :types_de_champ_private ) { [ ] }
2023-07-18 11:29:44 +02:00
let ( :fork ) { false }
2024-04-03 16:34:24 +02:00
subject ( :new_dossier ) { dossier . clone ( fork : ) }
it 'resets most of the attributes for the cloned dossier' do
expect ( new_dossier . id ) . not_to eq ( dossier . id )
expect ( new_dossier . api_entreprise_job_exceptions ) . to be_nil
expect ( new_dossier . archived ) . to be_falsey
expect ( new_dossier . brouillon_close_to_expiration_notice_sent_at ) . to be_nil
expect ( new_dossier . conservation_extension ) . to eq ( 0 . seconds )
expect ( new_dossier . declarative_triggered_at ) . to be_nil
expect ( new_dossier . deleted_user_email_never_send ) . to be_nil
expect ( new_dossier . depose_at ) . to be_nil
expect ( new_dossier . en_construction_at ) . to be_nil
expect ( new_dossier . en_construction_close_to_expiration_notice_sent_at ) . to be_nil
expect ( new_dossier . en_instruction_at ) . to be_nil
expect ( new_dossier . for_procedure_preview ) . to be_falsey
expect ( new_dossier . groupe_instructeur_updated_at ) . to be_nil
expect ( new_dossier . hidden_by_administration_at ) . to be_nil
expect ( new_dossier . hidden_by_reason ) . to be_nil
expect ( new_dossier . hidden_by_user_at ) . to be_nil
expect ( new_dossier . identity_updated_at ) . to be_nil
expect ( new_dossier . last_avis_updated_at ) . to be_nil
expect ( new_dossier . last_champ_private_updated_at ) . to be_nil
expect ( new_dossier . last_champ_updated_at ) . to be_nil
2024-09-10 16:09:43 +02:00
expect ( new_dossier . last_champ_piece_jointe_updated_at ) . to be_nil
2024-04-03 16:34:24 +02:00
expect ( new_dossier . last_commentaire_updated_at ) . to be_nil
2024-09-10 16:09:43 +02:00
expect ( new_dossier . last_commentaire_piece_jointe_updated_at ) . to be_nil
2024-04-03 16:34:24 +02:00
expect ( new_dossier . motivation ) . to be_nil
expect ( new_dossier . processed_at ) . to be_nil
end
2023-05-11 15:56:50 +02:00
2024-04-03 16:34:24 +02:00
it " updates search terms " do
2024-04-25 18:48:14 +02:00
# In spec, dossier and flag reference are created just before deep clone,
# which keep the flag reference from the original, pointing to the original id.
# We have to remove the flag reference before the clone
dossier . remove_instance_variable ( :@debounce_index_search_terms_flag_kredis_flag )
perform_enqueued_jobs ( only : DossierIndexSearchTermsJob ) do
subject
end
2024-04-03 16:34:24 +02:00
sql = " SELECT search_terms, private_search_terms FROM dossiers where id = :id "
result = Dossier . connection . execute ( Dossier . sanitize_sql_array ( [ sql , id : new_dossier . id ] ) ) . first
expect ( result [ " search_terms " ] ) . to match ( dossier . user . email )
expect ( result [ " private_search_terms " ] ) . to eq ( " " )
2023-05-11 15:56:50 +02:00
end
context 'copies some attributes' do
2023-07-18 11:29:44 +02:00
context 'when fork' do
let ( :fork ) { true }
it { expect ( new_dossier . groupe_instructeur ) . to eq ( dossier . groupe_instructeur ) }
end
context 'when not forked' do
2024-04-03 16:34:24 +02:00
it " copies or reset attributes " do
expect ( new_dossier . groupe_instructeur ) . to be_nil
expect ( new_dossier . autorisation_donnees ) . to eq ( dossier . autorisation_donnees )
expect ( new_dossier . revision_id ) . to eq ( dossier . revision_id )
expect ( new_dossier . user_id ) . to eq ( dossier . user_id )
end
2023-07-18 11:29:44 +02:00
end
2023-05-11 15:56:50 +02:00
end
context 'forces some attributes' do
let ( :dossier ) { create ( :dossier , :accepte ) }
2024-04-03 16:34:24 +02:00
it do
expect ( new_dossier . brouillon? ) . to eq ( true )
expect ( new_dossier . parent_dossier ) . to eq ( dossier )
end
2023-05-11 15:56:50 +02:00
context 'destroy parent' do
before { new_dossier }
it 'clean fk' do
expect { dossier . destroy } . to change { new_dossier . reload . parent_dossier_id } . from ( dossier . id ) . to ( nil )
end
end
end
context 'procedure with_individual' do
let ( :procedure ) { create ( :procedure , :for_individual ) }
2024-04-03 16:34:24 +02:00
it do
expect ( new_dossier . individual . slice ( :nom , :prenom , :gender ) ) . to eq ( dossier . individual . slice ( :nom , :prenom , :gender ) )
expect ( new_dossier . individual . id ) . not_to eq ( dossier . individual . id )
end
2023-05-11 15:56:50 +02:00
end
context 'procedure with etablissement' do
let ( :dossier ) { create ( :dossier , :with_entreprise ) }
2024-04-03 16:34:24 +02:00
it do
expect ( new_dossier . etablissement . slice ( :siret ) ) . to eq ( dossier . etablissement . slice ( :siret ) )
expect ( new_dossier . etablissement . id ) . not_to eq ( dossier . etablissement . id )
end
2023-05-11 15:56:50 +02:00
end
describe 'champs' do
it { expect ( new_dossier . id ) . not_to eq ( dossier . id ) }
context 'public are duplicated' do
2024-04-03 16:34:24 +02:00
it do
2024-09-27 15:37:11 +02:00
expect ( new_dossier . project_champs_public . count ) . to eq ( dossier . project_champs_public . count )
expect ( new_dossier . project_champs_public . map ( & :id ) ) . not_to eq ( dossier . project_champs_public . map ( & :id ) )
2024-04-03 16:34:24 +02:00
end
2023-05-11 15:56:50 +02:00
it 'keeps champs.values' do
2024-09-27 15:37:11 +02:00
original_first_champ = dossier . project_champs_public . first
2023-05-11 15:56:50 +02:00
original_first_champ . update! ( value : 'kthxbye' )
2024-09-27 15:37:11 +02:00
expect ( new_dossier . project_champs_public . first . value ) . to eq ( original_first_champ . value )
2023-05-11 15:56:50 +02:00
end
context 'for Champs::Repetition with rows, original_champ.repetition and rows are duped' do
2024-07-01 15:31:32 +02:00
let ( :types_de_champ_public ) { [ { type : :repetition , children : [ { } , { } ] } ] }
2024-09-27 15:44:57 +02:00
let ( :champ_repetition ) { dossier . champs . find ( & :repetition? ) }
let ( :cloned_champ_repetition ) { new_dossier . champs . find ( & :repetition? ) }
2023-05-11 15:56:50 +02:00
2024-04-03 16:34:24 +02:00
it do
2024-09-27 15:44:57 +02:00
expect ( cloned_champ_repetition . rows . flatten . count ) . to eq ( 4 )
expect ( cloned_champ_repetition . rows . flatten . map ( & :id ) ) . not_to eq ( champ_repetition . rows . flatten . map ( & :id ) )
expect ( cloned_champ_repetition . row_ids ) . to eq ( champ_repetition . row_ids )
2024-04-03 16:34:24 +02:00
end
2023-05-11 15:56:50 +02:00
end
context 'for Champs::CarteChamp with geo areas, original_champ.geo_areas are duped' do
2024-07-01 15:31:32 +02:00
let ( :types_de_champ_public ) { [ { type : :carte } ] }
let ( :champ_carte ) { dossier . champs . first }
let ( :cloned_champ_carte ) { new_dossier . champs . first }
2023-05-11 15:56:50 +02:00
2024-04-03 16:34:24 +02:00
it do
2024-07-01 15:31:32 +02:00
expect ( cloned_champ_carte . geo_areas . count ) . to eq ( 2 )
expect ( cloned_champ_carte . geo_areas . ids ) . not_to eq ( champ_carte . geo_areas . ids )
2024-04-03 16:34:24 +02:00
end
2023-05-11 15:56:50 +02:00
end
context 'for Champs::SiretChamp, original_champ.etablissement is duped' do
2024-07-01 15:31:32 +02:00
let ( :types_de_champ_public ) { [ { type : :siret } ] }
let ( :champ_siret ) { dossier . champs . first }
let ( :cloned_champ_siret ) { new_dossier . champs . first }
it do
expect ( champ_siret . etablissement ) . not_to be_nil
expect ( cloned_champ_siret . etablissement . id ) . not_to eq ( champ_siret . etablissement . id )
end
2024-04-03 16:34:24 +02:00
end
2023-05-11 15:56:50 +02:00
context 'for Champs::PieceJustificative, original_champ.piece_justificative_file is duped' do
2023-12-01 17:00:03 +01:00
let ( :types_de_champ_public ) { [ { type : :piece_justificative } ] }
2024-07-01 15:31:32 +02:00
let ( :champ_piece_justificative ) { dossier . champs . first }
let ( :cloned_champ_piece_justificative ) { new_dossier . champs . first }
2023-12-01 17:00:03 +01:00
2024-07-01 15:31:32 +02:00
it { expect ( cloned_champ_piece_justificative . piece_justificative_file . first . blob ) . to eq ( champ_piece_justificative . piece_justificative_file . first . blob ) }
2023-05-11 15:56:50 +02:00
end
context 'for Champs::AddressChamp, original_champ.data is duped' do
2024-07-01 15:31:32 +02:00
let ( :types_de_champ_public ) { [ { type : :address } ] }
let ( :champ_address ) { dossier . champs . first }
let ( :cloned_champ_address ) { new_dossier . champs . first }
before { champ_address . update ( external_id : 'Address' , data : { city_code : '75019' } ) }
2023-05-11 15:56:50 +02:00
2024-04-03 16:34:24 +02:00
it do
2024-07-01 15:31:32 +02:00
expect ( champ_address . data ) . not_to be_nil
expect ( champ_address . external_id ) . not_to be_nil
expect ( cloned_champ_address . external_id ) . to eq ( champ_address . external_id )
expect ( cloned_champ_address . data ) . to eq ( champ_address . data )
2024-04-03 16:34:24 +02:00
end
2023-05-11 15:56:50 +02:00
end
end
context 'private are renewd' do
2024-07-01 15:31:32 +02:00
let ( :types_de_champ_private ) { [ { } ] }
2023-05-11 15:56:50 +02:00
it 'reset champs private values' do
2024-09-27 15:37:11 +02:00
expect ( new_dossier . project_champs_private . count ) . to eq ( dossier . project_champs_private . count )
expect ( new_dossier . project_champs_private . map ( & :id ) ) . not_to eq ( dossier . project_champs_private . map ( & :id ) )
original_first_champs_private = dossier . project_champs_private . first
2023-05-11 15:56:50 +02:00
original_first_champs_private . update! ( value : 'kthxbye' )
2024-09-27 15:37:11 +02:00
expect ( new_dossier . project_champs_private . first . value ) . not_to eq ( original_first_champs_private . value )
expect ( new_dossier . project_champs_private . first . value ) . to eq ( nil )
2023-05-11 15:56:50 +02:00
end
end
end
2023-05-11 15:57:28 +02:00
context " as a fork " do
let ( :new_dossier ) { dossier . clone ( fork : true ) }
2024-09-27 15:37:11 +02:00
before { dossier . project_champs_public } # we compare timestamps so we have to get the precision limit from the db }
2023-05-11 15:57:28 +02:00
2024-04-03 16:34:24 +02:00
it do
expect ( new_dossier . editing_fork_origin ) . to eq ( dossier )
2024-09-27 15:37:11 +02:00
expect ( new_dossier . project_champs_public [ 0 ] . id ) . not_to eq ( dossier . project_champs_public [ 0 ] . id )
expect ( new_dossier . project_champs_public [ 0 ] . created_at ) . to eq ( dossier . project_champs_public [ 0 ] . created_at )
expect ( new_dossier . project_champs_public [ 0 ] . updated_at ) . to eq ( dossier . project_champs_public [ 0 ] . updated_at )
2024-04-03 16:34:24 +02:00
end
2023-05-11 15:57:28 +02:00
context " piece justificative champ " do
2023-12-01 17:00:03 +01:00
let ( :types_de_champ_public ) { [ { type : :piece_justificative } ] }
2024-07-01 15:31:32 +02:00
let ( :champ_pj ) { dossier . champs . first }
let ( :cloned_champ_pj ) { new_dossier . champs . first }
2023-05-11 15:57:28 +02:00
it {
2024-07-01 15:31:32 +02:00
expect ( cloned_champ_pj . piece_justificative_file . first . blob ) . to eq ( champ_pj . piece_justificative_file . first . blob )
expect ( cloned_champ_pj . created_at ) . to eq ( champ_pj . created_at )
expect ( cloned_champ_pj . updated_at ) . to eq ( champ_pj . updated_at )
2023-05-11 15:57:28 +02:00
}
end
2023-05-24 12:08:13 +02:00
context 'invalid origin' do
let ( :procedure ) do
create ( :procedure , types_de_champ_public : [
2024-09-20 10:56:12 +02:00
{ type : :drop_down_list , libelle : " Le savez-vous? " , stable_id : 992 , drop_down_options : [ " Oui " , " Non " , " Peut-être " ] , mandatory : true }
2023-05-24 12:08:13 +02:00
] )
end
before do
champ = dossier . champs . find { _1 . stable_id == 992 }
champ . value = " Je ne sais pas "
champ . save! ( validate : false )
end
it 'can still fork' do
2024-04-02 11:04:00 +02:00
expect ( dossier . validate ( :champs_public_value ) ) . to be_falsey
2023-05-24 12:08:13 +02:00
new_dossier . champs . load # load relation so champs are validated below
2024-04-02 11:04:00 +02:00
expect ( new_dossier . validate ( :champs_public_value ) ) . to be_falsey
2023-05-24 12:08:13 +02:00
expect ( new_dossier . champs . find { _1 . stable_id == 992 } . value ) . to eq ( " Je ne sais pas " )
end
2023-07-26 11:44:36 +02:00
context 'when associated record is invalid' do
let ( :procedure ) do
create ( :procedure , types_de_champ_public : [
{ type : :carte , libelle : " Carte " , stable_id : 992 , mandatory : true }
] )
end
before do
champ = dossier . champs . find { _1 . stable_id == 992 }
2024-07-01 15:31:32 +02:00
geo_area = champ . geo_areas . first
geo_area . geometry = { " i'm " = > " invalid " }
2023-07-26 11:44:36 +02:00
geo_area . save! ( validate : false )
end
it 'can still fork' do
new_dossier . champs . load # load relation so champs are validated below
expect ( new_dossier . champs . find { _1 . stable_id == 992 } . geo_areas . first ) . not_to be_valid
end
end
2023-05-24 12:08:13 +02:00
end
2023-05-11 15:57:28 +02:00
end
2023-05-11 15:56:50 +02:00
end
2023-03-21 15:59:03 +01:00
describe '#make_diff' do
subject { dossier . make_diff ( forked_dossier ) }
context 'with no changes' do
it { is_expected . to eq ( added : [ ] , updated : [ ] , removed : [ ] ) }
end
context 'with updated groupe instructeur' do
before {
2023-05-12 17:53:40 +02:00
dossier . update! ( groupe_instructeur : create ( :groupe_instructeur ) )
2023-07-05 17:48:18 +02:00
forked_dossier . assign_to_groupe_instructeur ( dossier . procedure . defaut_groupe_instructeur , DossierAssignment . modes . fetch ( :manual ) )
2023-03-21 15:59:03 +01:00
}
2024-04-03 16:34:24 +02:00
it do
expect ( subject ) . to eq ( added : [ ] , updated : [ ] , removed : [ ] )
expect ( forked_dossier . forked_with_changes? ) . to be_truthy
end
2023-03-21 15:59:03 +01:00
end
context 'with updated champ' do
let ( :updated_champ ) { forked_dossier . champs . find { _1 . stable_id == 99 } }
before { updated_champ . update ( value : 'new value' ) }
it 'forked_with_changes? should reflect dossier state' do
2024-04-03 16:34:24 +02:00
expect ( subject ) . to eq ( added : [ ] , updated : [ updated_champ ] , removed : [ ] )
2023-03-21 15:59:03 +01:00
expect ( dossier . forked_with_changes? ) . to be_falsey
expect ( forked_dossier . forked_with_changes? ) . to be_truthy
expect ( updated_champ . forked_with_changes? ) . to be_truthy
end
end
context 'with new revision' do
let ( :added_champ ) { forked_dossier . champs . find { _1 . libelle == " Un nouveau champ text " } }
let ( :removed_champ ) { dossier . champs . find { _1 . stable_id == 99 } }
2023-09-19 17:25:00 +02:00
let ( :new_dossier ) { dossier . clone }
2023-03-21 15:59:03 +01:00
before do
procedure . draft_revision . add_type_de_champ ( {
type_champ : TypeDeChamp . type_champs . fetch ( :text ) ,
libelle : " Un nouveau champ text "
} )
procedure . draft_revision . remove_type_de_champ ( removed_champ . stable_id )
procedure . publish_revision!
end
it {
expect ( dossier . revision_id ) . to eq ( procedure . revisions . first . id )
2023-09-19 17:25:00 +02:00
expect ( new_dossier . revision_id ) . to eq ( procedure . published_revision . id )
2023-03-21 15:59:03 +01:00
expect ( forked_dossier . revision_id ) . to eq ( procedure . published_revision_id )
is_expected . to eq ( added : [ added_champ ] , updated : [ ] , removed : [ removed_champ ] )
}
end
end
describe '#merge_fork' do
subject { dossier . merge_fork ( forked_dossier ) }
context 'with updated champ' do
2024-10-16 14:15:26 +02:00
let ( :repetition_champ ) { dossier . project_champs_public . last }
2023-03-21 15:59:03 +01:00
let ( :updated_champ ) { forked_dossier . champs . find { _1 . stable_id == 99 } }
2024-10-16 14:15:26 +02:00
let ( :updated_repetition_champs ) { forked_dossier . champs . filter { _1 . stable_id == 994 } }
2023-03-21 15:59:03 +01:00
before do
2024-10-16 14:15:26 +02:00
repetition_champ . add_row ( updated_by : 'test' )
2023-03-21 15:59:03 +01:00
dossier . champs . each do | champ |
champ . update ( value : 'old value' )
end
updated_champ . update ( value : 'new value' )
2024-10-16 14:15:26 +02:00
updated_repetition_champs . each { _1 . update ( value : 'new value in repetition' ) }
2024-04-25 18:48:14 +02:00
dossier . debounce_index_search_terms_flag . remove
2023-03-21 15:59:03 +01:00
end
2024-09-27 15:37:11 +02:00
it { expect { subject } . to change { dossier . champs . size } . by ( 0 ) }
it { expect { subject } . not_to change { dossier . champs . order ( :created_at ) . reject { _1 . stable_id . in? ( [ 99 , 994 ] ) } . map ( & :value ) } }
2024-04-25 17:57:55 +02:00
it { expect { subject } . to have_enqueued_job ( DossierIndexSearchTermsJob ) . with ( dossier ) }
2024-09-27 15:37:11 +02:00
it { expect { subject } . to change { dossier . champs . find { _1 . stable_id == 99 } . value } . from ( 'old value' ) . to ( 'new value' ) }
it { expect { subject } . to change { dossier . champs . find { _1 . stable_id == 994 } . value } . from ( 'old value' ) . to ( 'new value in repetition' ) }
2023-04-22 19:54:41 +02:00
2023-04-27 12:25:30 +02:00
it 'fork is hidden after merge' do
subject
expect ( forked_dossier . reload . hidden_by_reason ) . to eq ( " stale_fork " )
expect ( dossier . reload . editing_forks ) . to be_empty
end
2023-03-21 15:59:03 +01:00
end
context 'with new revision' do
let ( :added_champ ) { forked_dossier . champs . find { _1 . libelle == " Un nouveau champ text " } }
2023-06-23 00:22:32 +02:00
let ( :added_repetition_champ ) { forked_dossier . champs . find { _1 . libelle == " Texte en répétition " } }
2023-03-21 15:59:03 +01:00
let ( :removed_champ ) { dossier . champs . find { _1 . stable_id == 99 } }
2023-05-12 17:53:40 +02:00
let ( :updated_champ ) { dossier . champs . find { _1 . stable_id == 991 } }
2023-03-21 15:59:03 +01:00
before do
dossier . champs . each do | champ |
champ . update ( value : 'old value' )
end
procedure . draft_revision . add_type_de_champ ( {
type_champ : TypeDeChamp . type_champs . fetch ( :text ) ,
libelle : " Un nouveau champ text "
} )
2023-06-23 00:22:32 +02:00
procedure . draft_revision . add_type_de_champ ( {
type_champ : TypeDeChamp . type_champs . fetch ( :text ) ,
parent_stable_id : 993 ,
libelle : " Texte en répétition "
} )
2023-03-21 15:59:03 +01:00
procedure . draft_revision . remove_type_de_champ ( removed_champ . stable_id )
2023-05-12 17:53:40 +02:00
procedure . draft_revision . find_and_ensure_exclusive_use ( updated_champ . stable_id ) . update ( libelle : " Un nouveau libelle " )
2023-03-21 15:59:03 +01:00
procedure . publish_revision!
end
2023-05-12 17:53:40 +02:00
subject {
added_champ . update ( value : 'new value for added champ' )
updated_champ . update ( value : 'new value for updated champ' )
2023-06-23 00:22:32 +02:00
added_repetition_champ . update ( value : " new value in repetition champ " )
2023-05-12 17:53:40 +02:00
dossier . reload
super ( )
}
2024-09-27 15:37:11 +02:00
it { expect { subject } . to change { dossier . champs . size } . by ( 1 ) }
it { expect { subject } . to change { dossier . champs . order ( :created_at ) . map ( & :to_s ) } . from ( [ 'old value' , 'old value' , 'Non' , 'old value' , 'old value' ] ) . to ( [ 'new value for updated champ' , 'Non' , 'old value' , 'old value' , 'new value for added champ' , 'new value in repetition champ' ] ) }
2023-03-21 15:59:03 +01:00
it " dossier after merge should be on last published revision " do
expect ( dossier . revision_id ) . to eq ( procedure . revisions . first . id )
expect ( forked_dossier . revision_id ) . to eq ( procedure . published_revision_id )
subject
perform_enqueued_jobs only : DestroyRecordLaterJob
expect ( dossier . revision_id ) . to eq ( procedure . published_revision_id )
2023-05-12 17:53:40 +02:00
expect ( dossier . champs . all? { dossier . revision . in? ( _1 . type_de_champ . revisions ) } ) . to be_truthy
2023-03-21 15:59:03 +01:00
expect ( Dossier . exists? ( forked_dossier . id ) ) . to be_falsey
end
end
2023-06-30 14:25:23 +02:00
context 'with old revision having repetition' do
let ( :removed_champ ) { dossier . champs . find ( & :repetition? ) }
before do
dossier . champs . each do | champ |
champ . update ( value : 'old value' )
end
procedure . draft_revision . remove_type_de_champ ( removed_champ . stable_id )
procedure . publish_revision!
end
it 'works' do
2023-11-09 17:27:55 +01:00
expect { subject } . not_to raise_error
2023-06-30 14:25:23 +02:00
end
end
2023-03-21 15:59:03 +01:00
end
end