feat(dossier): add methods to upsert champ values
This commit is contained in:
parent
c479d46b47
commit
0c7bc6b555
4 changed files with 369 additions and 39 deletions
|
@ -39,6 +39,46 @@ module DossierChampsConcern
|
|||
end
|
||||
end
|
||||
|
||||
def find_type_de_champ_by_stable_id(stable_id, scope = nil)
|
||||
case scope
|
||||
when :public
|
||||
revision.types_de_champ.public_only
|
||||
when :private
|
||||
revision.types_de_champ.private_only
|
||||
else
|
||||
revision.types_de_champ
|
||||
end.find_by!(stable_id:)
|
||||
end
|
||||
|
||||
def champs_for_prefill(stable_ids)
|
||||
revision
|
||||
.types_de_champ
|
||||
.filter { _1.stable_id.in?(stable_ids) }
|
||||
.filter { !revision.child?(_1) }
|
||||
.map { champ_for_update(_1, nil) }
|
||||
end
|
||||
|
||||
def champ_for_update(type_de_champ, row_id)
|
||||
champ, attributes = champ_with_attributes_for_update(type_de_champ, row_id)
|
||||
champ.assign_attributes(attributes)
|
||||
champ
|
||||
end
|
||||
|
||||
def update_champs_attributes(attributes, scope)
|
||||
# TODO: remove after one deploy
|
||||
if attributes.present? && attributes.values.filter { _1.key?(:with_public_id) }.empty?
|
||||
assign_attributes("champs_#{scope}_all_attributes".to_sym => attributes)
|
||||
@champs_by_public_id = nil
|
||||
return
|
||||
end
|
||||
|
||||
champs_attributes = attributes.to_h.map do |public_id, attributes|
|
||||
champ_attributes_by_public_id(public_id, attributes, scope)
|
||||
end
|
||||
|
||||
assign_attributes(champs_attributes:)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def champs_by_public_id
|
||||
|
@ -53,4 +93,39 @@ module DossierChampsConcern
|
|||
champ
|
||||
end
|
||||
end
|
||||
|
||||
def champ_attributes_by_public_id(public_id, attributes, scope)
|
||||
stable_id, row_id = public_id.split('-')
|
||||
type_de_champ = find_type_de_champ_by_stable_id(stable_id, scope)
|
||||
champ_with_attributes_for_update(type_de_champ, row_id).last.merge(attributes)
|
||||
end
|
||||
|
||||
def champ_with_attributes_for_update(type_de_champ, row_id)
|
||||
attributes = type_de_champ.params_for_champ
|
||||
# TODO: Once we have the right index in place, we should change this to use `create_or_find_by` instead of `find_or_create_by`
|
||||
champ = champs
|
||||
.create_with(type_de_champ:, **attributes)
|
||||
.find_or_create_by!(stable_id: type_de_champ.stable_id, row_id:)
|
||||
|
||||
attributes[:id] = champ.id
|
||||
|
||||
# Needed when a revision change the champ type in this case, we reset the champ data
|
||||
if champ.type != attributes[:type]
|
||||
attributes[:value] = nil
|
||||
attributes[:value_json] = nil
|
||||
attributes[:external_id] = nil
|
||||
attributes[:data] = nil
|
||||
end
|
||||
|
||||
parent = revision.parent_of(type_de_champ)
|
||||
if parent.present?
|
||||
attributes[:parent] = champs.find { _1.type_de_champ_id == parent.id }
|
||||
else
|
||||
attributes[:parent] = nil
|
||||
end
|
||||
|
||||
@champs_by_public_id = nil
|
||||
|
||||
[champ, attributes]
|
||||
end
|
||||
end
|
||||
|
|
|
@ -13,8 +13,4 @@ module DossierPrefillableConcern
|
|||
assign_attributes(attributes)
|
||||
save(validate: false)
|
||||
end
|
||||
|
||||
def find_champs_by_stable_ids(stable_ids)
|
||||
champs.joins(:type_de_champ).where(types_de_champ: { stable_id: stable_ids.compact.uniq })
|
||||
end
|
||||
end
|
||||
|
|
294
spec/models/concern/dossier_champs_concern_spec.rb
Normal file
294
spec/models/concern/dossier_champs_concern_spec.rb
Normal file
|
@ -0,0 +1,294 @@
|
|||
RSpec.describe DossierChampsConcern do
|
||||
let(:procedure) do
|
||||
create(:procedure, types_de_champ_public:, types_de_champ_private:)
|
||||
end
|
||||
let(:types_de_champ_public) do
|
||||
[
|
||||
{ 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 }] }
|
||||
]
|
||||
end
|
||||
let(:types_de_champ_private) do
|
||||
[
|
||||
{ type: :text, libelle: "Une annotation", stable_id: 995 }
|
||||
]
|
||||
end
|
||||
let(:dossier) { create(:dossier, procedure:) }
|
||||
|
||||
describe "#find_type_de_champ_by_stable_id(public)" do
|
||||
subject { dossier.find_type_de_champ_by_stable_id(992, :public) }
|
||||
|
||||
it { is_expected.to be_truthy }
|
||||
end
|
||||
|
||||
describe "#find_type_de_champ_by_stable_id(private)" do
|
||||
subject { dossier.find_type_de_champ_by_stable_id(995, :private) }
|
||||
|
||||
it { is_expected.to be_truthy }
|
||||
end
|
||||
|
||||
describe "#project_champ" do
|
||||
let(:type_de_champ_repetition) { dossier.find_type_de_champ_by_stable_id(993) }
|
||||
let(:type_de_champ_public) { dossier.find_type_de_champ_by_stable_id(99) }
|
||||
let(:type_de_champ_private) { dossier.find_type_de_champ_by_stable_id(995) }
|
||||
let(:row_ids) { dossier.project_champ(type_de_champ_repetition, nil).row_ids }
|
||||
|
||||
context "public champ" do
|
||||
let(:row_id) { nil }
|
||||
subject { dossier.project_champ(type_de_champ_public, row_id) }
|
||||
|
||||
it { expect(subject.persisted?).to be_truthy }
|
||||
|
||||
context "in repetition" do
|
||||
let(:type_de_champ_public) { dossier.find_type_de_champ_by_stable_id(994) }
|
||||
let(:row_id) { row_ids.first }
|
||||
|
||||
it {
|
||||
expect(subject.persisted?).to be_truthy
|
||||
expect(subject.row_id).to eq(row_id)
|
||||
expect(subject.parent_id).not_to be_nil
|
||||
}
|
||||
end
|
||||
|
||||
context "missing champ" do
|
||||
before { dossier; Champs::TextChamp.destroy_all }
|
||||
|
||||
it {
|
||||
expect(subject.new_record?).to be_truthy
|
||||
expect(subject.is_a?(Champs::TextChamp)).to be_truthy
|
||||
}
|
||||
|
||||
context "in repetition" do
|
||||
let(:type_de_champ_public) { dossier.find_type_de_champ_by_stable_id(994) }
|
||||
let(:row_id) { row_ids.first }
|
||||
|
||||
it {
|
||||
expect(subject.new_record?).to be_truthy
|
||||
expect(subject.is_a?(Champs::TextChamp)).to be_truthy
|
||||
expect(subject.row_id).to eq(row_id)
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "private champ" do
|
||||
subject { dossier.project_champ(type_de_champ_private, nil) }
|
||||
|
||||
it { expect(subject.persisted?).to be_truthy }
|
||||
|
||||
context "missing champ" do
|
||||
before { dossier; Champs::TextChamp.destroy_all }
|
||||
|
||||
it {
|
||||
expect(subject.new_record?).to be_truthy
|
||||
expect(subject.is_a?(Champs::TextChamp)).to be_truthy
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#champs_for_export" do
|
||||
subject { dossier.champs_for_export(dossier.revision.types_de_champ_public) }
|
||||
|
||||
it { expect(subject.size).to eq(4) }
|
||||
it { expect(subject.first).to eq(["Un champ text", nil]) }
|
||||
end
|
||||
|
||||
describe "#champs_for_prefill" do
|
||||
subject { dossier.champs_for_prefill([991, 995]) }
|
||||
|
||||
it {
|
||||
expect(subject.size).to eq(2)
|
||||
expect(subject.map(&:libelle)).to eq(["Une annotation", "Un autre champ text"])
|
||||
expect(subject.all?(&:persisted?)).to be_truthy
|
||||
}
|
||||
|
||||
context "missing champ" do
|
||||
before { dossier; Champs::TextChamp.destroy_all }
|
||||
|
||||
it {
|
||||
expect(subject.size).to eq(2)
|
||||
expect(subject.map(&:libelle)).to eq(["Une annotation", "Un autre champ text"])
|
||||
expect(subject.all?(&:persisted?)).to be_truthy
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
describe "#champ_for_update" do
|
||||
let(:type_de_champ_repetition) { dossier.find_type_de_champ_by_stable_id(993) }
|
||||
let(:type_de_champ_public) { dossier.find_type_de_champ_by_stable_id(99) }
|
||||
let(:type_de_champ_private) { dossier.find_type_de_champ_by_stable_id(995) }
|
||||
let(:row_ids) { dossier.project_champ(type_de_champ_repetition, nil).row_ids }
|
||||
let(:row_id) { nil }
|
||||
|
||||
context "public champ" do
|
||||
subject { dossier.champ_for_update(type_de_champ_public, row_id) }
|
||||
|
||||
it {
|
||||
expect(subject.persisted?).to be_truthy
|
||||
expect(subject.row_id).to eq(row_id)
|
||||
}
|
||||
|
||||
context "in repetition" do
|
||||
let(:type_de_champ_public) { dossier.find_type_de_champ_by_stable_id(994) }
|
||||
let(:row_id) { row_ids.first }
|
||||
|
||||
it {
|
||||
expect(subject.persisted?).to be_truthy
|
||||
expect(subject.row_id).to eq(row_id)
|
||||
expect(subject.parent_id).not_to be_nil
|
||||
}
|
||||
end
|
||||
|
||||
context "missing champ" do
|
||||
before { dossier; Champs::TextChamp.destroy_all }
|
||||
|
||||
it {
|
||||
expect(subject.persisted?).to be_truthy
|
||||
expect(subject.is_a?(Champs::TextChamp)).to be_truthy
|
||||
}
|
||||
|
||||
context "in repetition" do
|
||||
let(:type_de_champ_public) { dossier.find_type_de_champ_by_stable_id(994) }
|
||||
let(:row_id) { row_ids.first }
|
||||
|
||||
it {
|
||||
expect(subject.persisted?).to be_truthy
|
||||
expect(subject.is_a?(Champs::TextChamp)).to be_truthy
|
||||
expect(subject.row_id).to eq(row_id)
|
||||
expect(subject.parent_id).not_to be_nil
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "private champ" do
|
||||
subject { dossier.champ_for_update(type_de_champ_private, row_id) }
|
||||
|
||||
it {
|
||||
expect(subject.persisted?).to be_truthy
|
||||
expect(subject.row_id).to eq(row_id)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
describe "#update_champs_attributes(public)" do
|
||||
let(:type_de_champ_repetition) { dossier.find_type_de_champ_by_stable_id(993) }
|
||||
let(:row_ids) { dossier.project_champ(type_de_champ_repetition, nil).row_ids }
|
||||
let(:row_id) { row_ids.first }
|
||||
|
||||
let(:attributes) do
|
||||
{
|
||||
"99" => { value: "Hello", with_public_id: true },
|
||||
"991" => { value: "World", with_public_id: true },
|
||||
"994-#{row_id}" => { value: "Greer", with_public_id: true }
|
||||
}
|
||||
end
|
||||
|
||||
let(:champ_99) { dossier.project_champ(dossier.find_type_de_champ_by_stable_id(99), nil) }
|
||||
let(:champ_991) { dossier.project_champ(dossier.find_type_de_champ_by_stable_id(991), nil) }
|
||||
let(:champ_994) { dossier.project_champ(dossier.find_type_de_champ_by_stable_id(994), row_id) }
|
||||
|
||||
subject { dossier.update_champs_attributes(attributes, :public) }
|
||||
|
||||
it {
|
||||
subject
|
||||
expect(dossier.champs.any?(&:changed_for_autosave?)).to be_truthy
|
||||
expect(champ_99.changed?).to be_truthy
|
||||
expect(champ_991.changed?).to be_truthy
|
||||
expect(champ_994.changed?).to be_truthy
|
||||
expect(champ_99.value).to eq("Hello")
|
||||
expect(champ_991.value).to eq("World")
|
||||
expect(champ_994.value).to eq("Greer")
|
||||
}
|
||||
|
||||
context "missing champs" do
|
||||
before { dossier; Champs::TextChamp.destroy_all; }
|
||||
|
||||
it {
|
||||
subject
|
||||
expect(dossier.champs.any?(&:changed_for_autosave?)).to be_truthy
|
||||
expect(champ_99.changed?).to be_truthy
|
||||
expect(champ_991.changed?).to be_truthy
|
||||
expect(champ_994.changed?).to be_truthy
|
||||
expect(champ_99.value).to eq("Hello")
|
||||
expect(champ_991.value).to eq("World")
|
||||
expect(champ_994.value).to eq("Greer")
|
||||
}
|
||||
end
|
||||
|
||||
context 'legacy attributes' do
|
||||
let(:attributes) do
|
||||
{
|
||||
champ_99.id => { value: "Hello", id: champ_99.id },
|
||||
champ_991.id => { value: "World", id: champ_991.id },
|
||||
champ_994.id => { value: "Greer", id: champ_994.id }
|
||||
}
|
||||
end
|
||||
|
||||
let(:champ_99_updated) { dossier.project_champ(dossier.find_type_de_champ_by_stable_id(99), nil) }
|
||||
let(:champ_991_updated) { dossier.project_champ(dossier.find_type_de_champ_by_stable_id(991), nil) }
|
||||
let(:champ_994_updated) { dossier.project_champ(dossier.find_type_de_champ_by_stable_id(994), row_id) }
|
||||
|
||||
it {
|
||||
subject
|
||||
expect(dossier.champs_public_all.any?(&:changed_for_autosave?)).to be_truthy
|
||||
dossier.save
|
||||
dossier.reload
|
||||
expect(champ_99_updated.value).to eq("Hello")
|
||||
expect(champ_991_updated.value).to eq("World")
|
||||
expect(champ_994_updated.value).to eq("Greer")
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
describe "#update_champs_attributes(private)" do
|
||||
let(:attributes) do
|
||||
{
|
||||
"995" => { value: "Hello", with_public_id: true }
|
||||
}
|
||||
end
|
||||
|
||||
let(:annotation_995) { dossier.project_champ(dossier.find_type_de_champ_by_stable_id(995), nil) }
|
||||
|
||||
subject { dossier.update_champs_attributes(attributes, :private) }
|
||||
|
||||
it {
|
||||
subject
|
||||
expect(dossier.champs.any?(&:changed_for_autosave?)).to be_truthy
|
||||
expect(annotation_995.changed?).to be_truthy
|
||||
expect(annotation_995.value).to eq("Hello")
|
||||
}
|
||||
|
||||
context "missing champs" do
|
||||
before { dossier; Champs::TextChamp.destroy_all; }
|
||||
|
||||
it {
|
||||
subject
|
||||
expect(dossier.champs.any?(&:changed_for_autosave?)).to be_truthy
|
||||
expect(annotation_995.changed?).to be_truthy
|
||||
expect(annotation_995.value).to eq("Hello")
|
||||
}
|
||||
end
|
||||
|
||||
context 'legacy attributes' do
|
||||
let(:attributes) do
|
||||
{
|
||||
annotation_995.id => { value: "Hello", id: annotation_995.id }
|
||||
}
|
||||
end
|
||||
|
||||
let(:annotation_995_updated) { dossier.project_champ(dossier.find_type_de_champ_by_stable_id(995), nil) }
|
||||
|
||||
it {
|
||||
subject
|
||||
expect(dossier.champs_private_all.any?(&:changed_for_autosave?)).to be_truthy
|
||||
dossier.save
|
||||
dossier.reload
|
||||
expect(annotation_995_updated.value).to eq("Hello")
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
|
@ -2183,41 +2183,6 @@ describe Dossier, type: :model do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#find_champs_by_stable_ids' do
|
||||
let(:procedure) { create(:procedure, :published) }
|
||||
let(:dossier) { create(:dossier, :brouillon, procedure: procedure) }
|
||||
|
||||
subject { dossier.find_champs_by_stable_ids(stable_ids) }
|
||||
|
||||
context 'when stable_ids is empty' do
|
||||
let(:stable_ids) { [] }
|
||||
|
||||
it { expect(subject).to match([]) }
|
||||
end
|
||||
|
||||
context 'when stable_ids contains nil or blank values' do
|
||||
let(:stable_ids) { [nil, ""] }
|
||||
|
||||
it { expect(subject).to match([]) }
|
||||
end
|
||||
|
||||
context 'when stable_ids contains present values' do
|
||||
context 'when the dossier has no champ with the given stable ids' do
|
||||
let(:stable_ids) { ['My Neighbor Totoro', 'Miyazaki'] }
|
||||
|
||||
it { expect(subject).to match([]) }
|
||||
end
|
||||
|
||||
context 'when the dossier has champs with the given stable ids' do
|
||||
let!(:type_de_champ_1) { create(:type_de_champ_text, procedure: procedure) }
|
||||
let!(:type_de_champ_2) { create(:type_de_champ_textarea, procedure: procedure) }
|
||||
let(:stable_ids) { [type_de_champ_1.stable_id, type_de_champ_2.stable_id] }
|
||||
|
||||
it { expect(subject).to match_array(dossier.champs_public.joins(:type_de_champ).where(types_de_champ: { stable_id: stable_ids })) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'BatchOperation' do
|
||||
subject { build(:dossier) }
|
||||
it { is_expected.to belong_to(:batch_operation).optional }
|
||||
|
|
Loading…
Reference in a new issue