Bug 40333: Add EDIFACT message view to invoice display page

This enhancement adds an EDIFACT message section to the invoice display page.
If there's a linked EDI Message for this invice, we display the basic
details along with any parsing errors that may have been recorded and we
add a button to trigger the display of the raw message in a modal.

Changes include:
- Display EDIFACT message information on invoice page when available
- Show message type, transfer date, status, and filename
- Add "View EDIFACT message" button that opens modal with raw message
- Display EDIFACT processing errors directly on invoice page
- Create reusable modal components for EDIFACT message display

The implementation checks if the EDIFACT system is enabled and if the
invoice has an associated message_id before displaying the EDIFACT
section. Processing errors are highlighted in a warning box with
clear formatting.

Sponsored-by: Open Fifth <https://openfith.co.uk>
Signed-off-by: Hannah Dunne-Howrie <hdunne-howrie@westminster.gov.uk>

Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
This commit is contained in:
Martin Renvoize 2025-07-10 14:22:08 +01:00 committed by Lucas Gass
parent 11358ffe16
commit 55abaf2006
No known key found for this signature in database
GPG Key ID: 60B447398B0D36CA
5 changed files with 175 additions and 71 deletions

View File

@ -44,6 +44,7 @@ use Koha::DateUtils qw( output_pref );
use Koha::Misc::Files;
use Koha::Acquisition::Invoice::Adjustments;
use Koha::Acquisition::Invoices;
use Koha::Edifact::Files;
my $input = CGI->new;
my ( $template, $loggedinuser, $cookie, $flags ) = get_template_and_user(
@ -317,6 +318,34 @@ $template->param(
additional_field_values => $invoice->get_additional_field_values_for_template,
);
# Check for EDIFACT message information
my $edifact_message;
my $edifact_errors = [];
my $edifact_enabled = C4::Context->preference('EDIFACT');
if ( $edifact_enabled && $details->{'message_id'} ) {
$edifact_message = Koha::Edifact::Files->find( $details->{'message_id'} );
if ($edifact_message) {
# Get any processing errors for this message using the relation accessor
my $errors = $edifact_message->errors;
while ( my $error = $errors->next ) {
push @$edifact_errors, {
section => $error->section,
details => $error->details,
};
}
}
}
$template->param(
edifact_enabled => $edifact_enabled,
edifact_message => $edifact_message,
edifact_errors => $edifact_errors,
);
$template->param(
invoiceid => $details->{'invoiceid'},
invoicenumber => $details->{'invoicenumber'},

View File

@ -0,0 +1,38 @@
[%# EDIFACT message modal include %]
[%# This modal can be used on any page that needs to display EDIFACT messages %]
<!-- Modal to display EDIFACT messages -->
<div class="modal" id="EDI_modal" tabindex="-1" role="dialog" aria-labelledby="EDI_modal_label" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title" id="EDI_modal_label">EDIFACT message</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div id="edi_loading"><img src="[% interface | html %]/[% theme | html %]/img/spinner-small.gif" alt="" /> Loading</div>
</div>
<div class="modal-footer">
<button class="btn btn-default" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<!-- Modal to display EDIFACT errors -->
<div class="modal" id="EDI_errors_modal" tabindex="-1" role="dialog" aria-labelledby="EDI_errors_modal_label" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title" id="EDI_errors_modal_label">EDIFACT processing errors for <span id="EDI_errors_filename"></span></h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div id="edi_errors_loading"><img src="[% interface | html %]/[% theme | html %]/img/spinner-small.gif" alt="" /> Loading</div>
</div>
<div class="modal-footer">
<button class="btn btn-default" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>

View File

@ -37,44 +37,13 @@
</div>
<!-- /#acqui_edifactmsgs -->
<!-- Modal to display EDIFACT messages -->
<div class="modal" id="EDI_modal" tabindex="-1" role="dialog" aria-labelledby="EDI_modal_label" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title" id="EDI_modal_label">EDIFACT message</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body"> </div>
<div class="modal-footer">
<button class="btn btn-default" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<!-- Modal to display EDIFACT errors -->
<div class="modal" id="EDI_errors_modal" tabindex="-1" role="dialog" aria-labelledby="EDI_errors_modal_label" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title" id="EDI_errors_modal_label">EDIFACT processing errors for <span id="EDI_errors_filename"></span></h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div id="loading"> <img src="[% interface | html %]/[% theme | html %]/img/spinner-small.gif" alt="" /> Loading </div>
</div>
<div class="modal-footer">
<button class="btn btn-default" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
[% INCLUDE 'modals/edifact-modal.inc' %]
[% END %]
[% MACRO jsinclude BLOCK %]
[% Asset.js("js/acquisitions-menu.js") | $raw %]
[% INCLUDE 'datatables.inc' %]
[% Asset.js("js/modals/edifact-modal.js") | $raw %]
<script>
$(document).ready(function() {
let edi_msgs_table_url = '/api/v1/acquisitions/edifiles?';
@ -189,43 +158,6 @@
]
}, 0, 0);
var EDIModal = $("#EDI_modal");
var EDIModalBody = $("#EDI_modal .modal-body");
$("body").on("click", ".view_message", function(e){
e.preventDefault();
var page = $(this).attr("href");
EDIModalBody.load(page + " #edimsg");
EDIModal.modal("show");
});
EDIModal.on("click",".btn-close",function(e){
e.preventDefault();
EDIModal.modal("hide");
});
EDIModal.on("hidden.bs.modal", function(){
EDIModalBody.html("<div id=\"loading\"><img src=\"[% interface | html %]/[% theme | html %]/img/spinner-small.gif\" alt=\"\" /> "+_("Loading")+"</div>");
});
const errorsModal = document.getElementById('EDI_errors_modal')
if (errorsModal) {
errorsModal.addEventListener('show.bs.modal', event => {
// Link that triggered the modal
const link = event.relatedTarget
// Extract info from data-bs-* attributes
const filename = link.getAttribute('data-bs-filename')
const errors = link.getAttribute('data-bs-errors')
// Update the modal's title.
const modalTitleSpan = errorsModal.querySelector('.modal-title #EDI_errors_filename')
modalTitleSpan.textContent = filename;
// Update the modal's content.
const modalBody = errorsModal.querySelector('.modal-body')
modalBody.innerHTML = errors;
})
}
$("body").on("click", ".delete_msg" ,function(){
return confirm(_("Are you sure you want to delete this message?"));
});
@ -233,4 +165,4 @@
</script>
[% END %]
[% INCLUDE 'intranet-bottom.inc' %]
[% INCLUDE 'intranet-bottom.inc' %]

View File

@ -57,6 +57,37 @@
<p>Vendor: <a href="/cgi-bin/koha/acquisition/vendors/[% booksellerid | uri %]">[% suppliername | html %]</a></p>
[% IF edifact_enabled && edifact_message %]
<div class="page-section">
<h3>EDIFACT Message</h3>
<p>
<strong>Message type:</strong> [% edifact_message.message_type | html %] <strong>Transfer date:</strong> [% edifact_message.transfer_date | $KohaDates %] <strong>Status:</strong> [% edifact_message.status | html %]
[% IF edifact_message.filename %]
<strong>Filename:</strong> [% edifact_message.filename | html %]
[% END %]
</p>
<p>
<a href="#" class="btn btn-default btn-sm view_edifact_message" data-message-id="[% edifact_message.id | html %]"> <i class="fa fa-search"></i> View EDIFACT message </a>
</p>
[% IF edifact_errors.size > 0 %]
<div class="alert alert-warning">
<h4><i class="fa fa-exclamation-triangle"></i> EDIFACT Processing Errors</h4>
<ul>
[% FOREACH error IN edifact_errors %]
<li>
<strong>[% error.details | html %]</strong>
[% IF error.section %]
<br /><pre class="mt-1 mb-0 small text-muted">[% error.section | html %]</pre>
[% END %]
</li>
[% END %]
</ul>
</div>
[% END %]
</div>
[% END %]
<form action="/cgi-bin/koha/acqui/invoice.pl" method="post" class="validated">
[% INCLUDE 'csrf-token.inc' %]
<fieldset class="rows">
@ -521,6 +552,8 @@
</div>
<!-- /#updateFund.modal -->
[% INCLUDE 'modals/edifact-modal.inc' %]
<div style="display:none;">
<select id="all_fund_dropdown">
<option value="">No fund</option>
@ -542,6 +575,7 @@
[% Asset.js("js/additional-fields-entry.js") | $raw %]
[% INCLUDE 'calendar.inc' %]
[% INCLUDE 'datatables.inc' %]
[% Asset.js("js/modals/edifact-modal.js") | $raw %]
<script>
function updateColumnsVisibility(visible) {
if ( visible ) {
@ -675,6 +709,7 @@
$('.ab_inactive').remove();
}
});
});
</script>
[% END %]

View File

@ -0,0 +1,70 @@
// EDIFACT modal JavaScript functionality
// This script handles the EDIFACT message modal interactions
// Include this after the edifact-modal.inc template
/* global __ interface theme */
$(document).ready(function () {
// Initialize modal elements
var EDIModal = $("#EDI_modal");
var EDIModalBody = $("#EDI_modal .modal-body");
var EDIErrorsModal = $("#EDI_errors_modal");
// Handle view EDIFACT message button clicks
$("body").on("click", ".view_edifact_message", function (e) {
e.preventDefault();
var message_id = $(this).data("message-id");
var page =
"/cgi-bin/koha/acqui/edimsg.pl?id=" +
encodeURIComponent(message_id);
EDIModalBody.load(page + " #edimsg");
EDIModal.modal("show");
});
// Handle view_message button clicks (for compatibility with existing code)
$("body").on("click", ".view_message", function (e) {
e.preventDefault();
var page = $(this).attr("href");
EDIModalBody.load(page + " #edimsg");
EDIModal.modal("show");
});
// Handle modal close button
EDIModal.on("click", ".btn-close", function (e) {
e.preventDefault();
EDIModal.modal("hide");
});
// Reset modal content when hidden
EDIModal.on("hidden.bs.modal", function () {
EDIModalBody.html(
"<div id='edi_loading'><img style='display:inline-block' src='" +
interface +
"/" +
theme +
"/img/spinner-small.gif' alt='' /> Loading</div>"
);
});
// Handle EDIFACT errors modal
const errorsModal = document.getElementById("EDI_errors_modal");
if (errorsModal) {
errorsModal.addEventListener("show.bs.modal", function (event) {
// Link that triggered the modal
const link = event.relatedTarget;
// Extract info from data-bs-* attributes
const filename = link.getAttribute("data-bs-filename");
const errors = link.getAttribute("data-bs-errors");
// Update the modal's title
const modalTitleSpan = errorsModal.querySelector(
".modal-title #EDI_errors_filename"
);
modalTitleSpan.textContent = filename;
// Update the modal's content
const modalBody = errorsModal.querySelector(".modal-body");
modalBody.innerHTML = errors;
});
}
});