Skip to content

Commit

Permalink
change submission status checking to handle out of order xml (#5499)
Browse files Browse the repository at this point in the history
  • Loading branch information
mrotondo authored Jan 31, 2025
1 parent 6180b23 commit 903f7d4
Show file tree
Hide file tree
Showing 2 changed files with 212 additions and 41 deletions.
31 changes: 25 additions & 6 deletions app/services/efile/poll_for_acknowledgments_service.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

StatusRecordGroup = Struct.new(:irs_submission_id, :state, :xml)

module Efile
Expand Down Expand Up @@ -103,8 +102,9 @@ def self._handle_submission_status_response(response)
submissions = EfileSubmission.where(irs_submission_id: groups_by_irs_submission_id.keys)
submissions.each do |submission|
status_updates += 1
xml_node = groups_by_irs_submission_id[submission.irs_submission_id]
status = xml_node.css('SubmissionStatusTxt').text.strip
xml_nodes = groups_by_irs_submission_id[submission.irs_submission_id]
xml_node = xml_node_with_most_recent_submission_status(xml_nodes)
status = status_from_xml_node(xml_node)
new_state = submission_status_to_state(status)
last_raw_response = submission.efile_submission_transitions.last&.metadata["raw_response"]
submission.transition_to(new_state, raw_response: xml_node.to_xml) unless xml_node.to_xml == last_raw_response
Expand All @@ -115,13 +115,32 @@ def self._handle_submission_status_response(response)

def self.group_status_records_by_submission_id(doc)
# The service returns multiple status records for the each submission id. It looks like they are in reverse
# chronological order (But are not properly date stamped), so we grab the first ones.
# chronological order (But are not properly date stamped), although we have seen examples where they are out of
# order. Regardless, we return each submission ID's list of statuses in the order they are encountered in the XML
doc.css('StatusRecordGrp').each_with_object({}) do |xml, groups_by_irs_submission_id|
irs_submission_id = xml.css("SubmissionId").text.strip
unless groups_by_irs_submission_id[irs_submission_id]
groups_by_irs_submission_id[irs_submission_id] = xml
if groups_by_irs_submission_id[irs_submission_id]
groups_by_irs_submission_id[irs_submission_id].append(xml)
else
groups_by_irs_submission_id[irs_submission_id] = [xml]
end
end
end

def self.xml_node_with_most_recent_submission_status(submission_status_xml_nodes)
# We might see out-of-order statuses in the list, so search the list for more-progresses statuses first
preferred_status_order = [READY_FOR_ACK_STATUSES, TRANSMITTED_STATUSES]
preferred_status_order.each do |statuses_to_look_for|
most_recent_status_node = submission_status_xml_nodes.find do |xml_node|
statuses_to_look_for.include? status_from_xml_node(xml_node)
end
return most_recent_status_node if most_recent_status_node.present?
end
nil
end

def self.status_from_xml_node(xml_node)
xml_node&.css('SubmissionStatusTxt')&.text&.strip
end

def self.submission_status_to_state(status)
Expand Down
222 changes: 187 additions & 35 deletions spec/services/efile/poll_for_acknowledgments_service_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,160 @@
let!(:fed_efile_submission1) { create(:efile_submission, :transmitted, submission_bundle: { filename: "sensible-filename.zip", io: StringIO.new("i am a zip file") }) }
let!(:fed_efile_submission2) { create(:efile_submission, :transmitted, submission_bundle: { filename: "sensible-filename.zip", io: StringIO.new("i am a zip file") }) }

let(:correctly_ordered_statuses_multiple_submissions_transmitted) {
Nokogiri::XML(
<<~XML
<?xml version='1.0' encoding='UTF-8'?>
<StatusRecordList xmlns="http://www.irs.gov/efile" xmlns:efile="http://www.irs.gov/efile">
<Cnt>4</Cnt>
<StatusRecordGrp>
<SubmissionId>4414662024003wte794o</SubmissionId>
<SubmissionStatusTxt>Received by State</SubmissionStatusTxt>
<SubmsnStatusAcknowledgementDt>2024-01-04</SubmsnStatusAcknowledgementDt>
</StatusRecordGrp>
<StatusRecordGrp>
<SubmissionId>abcdefghijklmnopqrst</SubmissionId>
<SubmissionStatusTxt>Received by State</SubmissionStatusTxt>
<SubmsnStatusAcknowledgementDt>2024-01-04</SubmsnStatusAcknowledgementDt>
</StatusRecordGrp>
<StatusRecordGrp>
<SubmissionId>abcdefghijklmnopqrst</SubmissionId>
<SubmissionStatusTxt>Sent to State</SubmissionStatusTxt>
<SubmsnStatusAcknowledgementDt>2024-01-04</SubmsnStatusAcknowledgementDt>
</StatusRecordGrp>
<StatusRecordGrp>
<SubmissionId>4414662024003wte794o</SubmissionId>
<SubmissionStatusTxt>Sent to State</SubmissionStatusTxt>
<SubmsnStatusAcknowledgementDt>2024-01-04</SubmsnStatusAcknowledgementDt>
</StatusRecordGrp>
<StatusRecordGrp>
<SubmissionId>abcdefghijklmnopqrst</SubmissionId>
<SubmissionStatusTxt>Ready for Pick-Up</SubmissionStatusTxt>
<SubmsnStatusAcknowledgementDt>2024-01-03</SubmsnStatusAcknowledgementDt>
</StatusRecordGrp>
<StatusRecordGrp>
<SubmissionId>abcdefghijklmnopqrst</SubmissionId>
<SubmissionStatusTxt>Received</SubmissionStatusTxt>
<SubmsnStatusAcknowledgementDt>2024-01-03</SubmsnStatusAcknowledgementDt>
</StatusRecordGrp>
<StatusRecordGrp>
<SubmissionId>4414662024003wte794o</SubmissionId>
<SubmissionStatusTxt>Ready for Pick-Up</SubmissionStatusTxt>
<SubmsnStatusAcknowledgementDt>2024-01-03</SubmsnStatusAcknowledgementDt>
</StatusRecordGrp>
<StatusRecordGrp>
<SubmissionId>4414662024003wte794o</SubmissionId>
<SubmissionStatusTxt>Received</SubmissionStatusTxt>
<SubmsnStatusAcknowledgementDt>2024-01-03</SubmsnStatusAcknowledgementDt>
</StatusRecordGrp>
</StatusRecordList>
XML
)
}

let(:correctly_ordered_statuses_transmitted) {
Nokogiri::XML(
<<~XML
<?xml version='1.0' encoding='UTF-8'?>
<StatusRecordList xmlns="http://www.irs.gov/efile" xmlns:efile="http://www.irs.gov/efile">
<Cnt>4</Cnt>
<StatusRecordGrp>
<SubmissionId>4414662024003wte794o</SubmissionId>
<SubmissionStatusTxt>Received by State</SubmissionStatusTxt>
<SubmsnStatusAcknowledgementDt>2024-01-04</SubmsnStatusAcknowledgementDt>
</StatusRecordGrp>
<StatusRecordGrp>
<SubmissionId>4414662024003wte794o</SubmissionId>
<SubmissionStatusTxt>Sent to State</SubmissionStatusTxt>
<SubmsnStatusAcknowledgementDt>2024-01-04</SubmsnStatusAcknowledgementDt>
</StatusRecordGrp>
<StatusRecordGrp>
<SubmissionId>4414662024003wte794o</SubmissionId>
<SubmissionStatusTxt>Ready for Pick-Up</SubmissionStatusTxt>
<SubmsnStatusAcknowledgementDt>2024-01-03</SubmsnStatusAcknowledgementDt>
</StatusRecordGrp>
<StatusRecordGrp>
<SubmissionId>4414662024003wte794o</SubmissionId>
<SubmissionStatusTxt>Received</SubmissionStatusTxt>
<SubmsnStatusAcknowledgementDt>2024-01-03</SubmsnStatusAcknowledgementDt>
</StatusRecordGrp>
</StatusRecordList>
XML
)
}

let(:correctly_ordered_statuses_ready_for_ack) {
Nokogiri::XML(
<<~XML
<?xml version='1.0' encoding='UTF-8'?>
<StatusRecordList xmlns="http://www.irs.gov/efile" xmlns:efile="http://www.irs.gov/efile">
<Cnt>5</Cnt>
<StatusRecordGrp>
<SubmissionId>4414662024003wte794o</SubmissionId>
<SubmissionStatusTxt>Acknowledgement Received from State</SubmissionStatusTxt>
<SubmsnStatusAcknowledgementDt>2024-01-04</SubmsnStatusAcknowledgementDt>
</StatusRecordGrp>
<StatusRecordGrp>
<SubmissionId>4414662024003wte794o</SubmissionId>
<SubmissionStatusTxt>Received by State</SubmissionStatusTxt>
<SubmsnStatusAcknowledgementDt>2024-01-04</SubmsnStatusAcknowledgementDt>
</StatusRecordGrp>
<StatusRecordGrp>
<SubmissionId>4414662024003wte794o</SubmissionId>
<SubmissionStatusTxt>Sent to State</SubmissionStatusTxt>
<SubmsnStatusAcknowledgementDt>2024-01-04</SubmsnStatusAcknowledgementDt>
</StatusRecordGrp>
<StatusRecordGrp>
<SubmissionId>4414662024003wte794o</SubmissionId>
<SubmissionStatusTxt>Ready for Pick-Up</SubmissionStatusTxt>
<SubmsnStatusAcknowledgementDt>2024-01-03</SubmsnStatusAcknowledgementDt>
</StatusRecordGrp>
<StatusRecordGrp>
<SubmissionId>4414662024003wte794o</SubmissionId>
<SubmissionStatusTxt>Received</SubmissionStatusTxt>
<SubmsnStatusAcknowledgementDt>2024-01-03</SubmsnStatusAcknowledgementDt>
</StatusRecordGrp>
</StatusRecordList>
XML
)
}

let(:out_of_order_statuses_ready_for_ack) {
Nokogiri::XML(
<<~XML
<?xml version='1.0' encoding='UTF-8'?>
<StatusRecordList xmlns="http://www.irs.gov/efile" xmlns:efile="http://www.irs.gov/efile">
<Cnt>5</Cnt>
<StatusRecordGrp>
<SubmissionId>4414662024003wte794o</SubmissionId>
<SubmissionStatusTxt>Received by State</SubmissionStatusTxt>
<SubmsnStatusAcknowledgementDt>2024-01-04</SubmsnStatusAcknowledgementDt>
</StatusRecordGrp>
<StatusRecordGrp>
<SubmissionId>4414662024003wte794o</SubmissionId>
<SubmissionStatusTxt>Acknowledgement Received from State</SubmissionStatusTxt>
<SubmsnStatusAcknowledgementDt>2024-01-04</SubmsnStatusAcknowledgementDt>
</StatusRecordGrp>
<StatusRecordGrp>
<SubmissionId>4414662024003wte794o</SubmissionId>
<SubmissionStatusTxt>Sent to State</SubmissionStatusTxt>
<SubmsnStatusAcknowledgementDt>2024-01-04</SubmsnStatusAcknowledgementDt>
</StatusRecordGrp>
<StatusRecordGrp>
<SubmissionId>4414662024003wte794o</SubmissionId>
<SubmissionStatusTxt>Ready for Pick-Up</SubmissionStatusTxt>
<SubmsnStatusAcknowledgementDt>2024-01-03</SubmsnStatusAcknowledgementDt>
</StatusRecordGrp>
<StatusRecordGrp>
<SubmissionId>4414662024003wte794o</SubmissionId>
<SubmissionStatusTxt>Received</SubmissionStatusTxt>
<SubmsnStatusAcknowledgementDt>2024-01-03</SubmsnStatusAcknowledgementDt>
</StatusRecordGrp>
</StatusRecordList>
XML
)
}

before do
fed_efile_submission1.update!(irs_submission_id: irs_submission_id1)
fed_efile_submission2.update!(irs_submission_id: irs_submission_id2)
Expand All @@ -286,49 +440,47 @@
context "getting status from state" do
it "interprets ready_for_ack successfully" do
["Denied by IRS", "Acknowledgement Received from State", "Acknowledgement Retrieved", "Notified"].each do |status|
expect(Efile::PollForAcknowledgmentsService.submission_status_to_state(status)).to eq :ready_for_ack
expect(described_class.submission_status_to_state(status)).to eq :ready_for_ack
end
end
it "interprets transmitted successfully" do
expect(Efile::PollForAcknowledgmentsService.submission_status_to_state("Received")).to eq :transmitted
expect(described_class.submission_status_to_state("Received")).to eq :transmitted
end
it "interprets unknown states as failed" do
expect(Efile::PollForAcknowledgmentsService.submission_status_to_state("My dog ate it")).to eq :failed
expect(described_class.submission_status_to_state("My dog ate it")).to eq :failed
end
end

context "statuses for submission_id" do
it "groups statuses by submission_id" do
doc = Nokogiri::XML(<<-TEXT
<?xml version='1.0' encoding='UTF-8'?>
<StatusRecordList xmlns="http://www.irs.gov/efile" xmlns:efile="http://www.irs.gov/efile">
<Cnt>4</Cnt>
<StatusRecordGrp>
<SubmissionId>4414662024003wte794o</SubmissionId>
<SubmissionStatusTxt>Received by State</SubmissionStatusTxt>
<SubmsnStatusAcknowledgementDt>2024-01-04</SubmsnStatusAcknowledgementDt>
</StatusRecordGrp>
<StatusRecordGrp>
<SubmissionId>4414662024003wte794o</SubmissionId>
<SubmissionStatusTxt>Sent to State</SubmissionStatusTxt>
<SubmsnStatusAcknowledgementDt>2024-01-04</SubmsnStatusAcknowledgementDt>
</StatusRecordGrp>
<StatusRecordGrp>
<SubmissionId>4414662024003wte794o</SubmissionId>
<SubmissionStatusTxt>Ready for Pick-Up</SubmissionStatusTxt>
<SubmsnStatusAcknowledgementDt>2024-01-03</SubmsnStatusAcknowledgementDt>
</StatusRecordGrp>
<StatusRecordGrp>
<SubmissionId>4414662024003wte794o</SubmissionId>
<SubmissionStatusTxt>Received</SubmissionStatusTxt>
<SubmsnStatusAcknowledgementDt>2024-01-03</SubmsnStatusAcknowledgementDt>
</StatusRecordGrp>
</StatusRecordList>
TEXT
)
result = Efile::PollForAcknowledgmentsService.group_status_records_by_submission_id(doc)
expect(result.keys.length).to eq 1
expect(result["4414662024003wte794o"].css("SubmissionStatusTxt").text).to eq "Received by State"
describe ".group_status_records_by_submission_id" do
it "groups statuses by submission_id, preserving their order" do
result = described_class.group_status_records_by_submission_id(correctly_ordered_statuses_multiple_submissions_transmitted)
expect(result.keys.length).to eq 2
submission_ids = %w[4414662024003wte794o abcdefghijklmnopqrst]
submission_ids.each do |submission_id|
expect(result[submission_id].length).to eq 4
expect(result[submission_id].first.css("SubmissionStatusTxt").text).to eq "Received by State"
expect(result[submission_id].last.css("SubmissionStatusTxt").text).to eq "Received"
end
end
end

describe ".xml_node_with_most_recent_submission_status" do
it "gets the most recent xml node indicating a transmitted state in a correctly ordered list of statuses" do
xml_nodes = described_class.group_status_records_by_submission_id(correctly_ordered_statuses_transmitted)["4414662024003wte794o"]
xml_node = described_class.xml_node_with_most_recent_submission_status(xml_nodes)
expect(xml_node.css("SubmissionStatusTxt").text).to eq "Received by State"
end

it "gets the most recent xml node indicating a ready-for-ack state in a correctly ordered list of statuses" do
xml_nodes = described_class.group_status_records_by_submission_id(correctly_ordered_statuses_ready_for_ack)["4414662024003wte794o"]
xml_node = described_class.xml_node_with_most_recent_submission_status(xml_nodes)
expect(xml_node.css("SubmissionStatusTxt").text).to eq "Acknowledgement Received from State"
end

it "gets the most recent xml node indicating a ready-for-ack state in an incorrectly ordered list of statuses" do
xml_nodes = described_class.group_status_records_by_submission_id(out_of_order_statuses_ready_for_ack)["4414662024003wte794o"]
xml_node = described_class.xml_node_with_most_recent_submission_status(xml_nodes)
expect(xml_node.css("SubmissionStatusTxt").text).to eq "Acknowledgement Received from State"
end
end
end
Expand Down

0 comments on commit 903f7d4

Please sign in to comment.