From c2894dbae2019b7ecf5c5546869a7a53ade5883f Mon Sep 17 00:00:00 2001 From: Tomas Cohen Arazi Date: Fri, 2 May 2025 10:11:07 -0300 Subject: [PATCH] Bug 32266: Make the late orders page use the API This bug makes the late orders page render using the API. Everything should work as usual. To test: 0. Have a bunch of late orders, from different vendors with different dates 1. Check how the page works without the patches 2. Notice it works similar with the patches 3. Sign off :-D --- acqui/lateorders.pl | 87 +-- .../prog/en/modules/acqui/lateorders.tt | 631 +++++++++++------- 2 files changed, 387 insertions(+), 331 deletions(-) diff --git a/acqui/lateorders.pl b/acqui/lateorders.pl index eb43a93de15..35814cb46de 100755 --- a/acqui/lateorders.pl +++ b/acqui/lateorders.pl @@ -22,37 +22,20 @@ lateorders.pl =head1 DESCRIPTION -this script shows late orders for a specific supplier, branch and delay -given on input arg. - -=head1 CGI PARAMETERS - -=over 4 - -=item booksellerid -To know on which supplier this script have to display late order. - -=item delay -To know the time boundary. Default value is 30 days. - -=item branch -To know on which branch this script have to display late order. - -=back +This script shows late orders =cut use Modern::Perl; -use CGI qw ( -utf8 ); -use C4::Auth qw( get_template_and_user ); -use C4::Output qw( output_html_with_http_headers ); -use C4::Context; -use C4::Letters qw( SendAlerts GetLetters ); -use Koha::DateUtils qw( dt_from_string ); +use CGI qw ( -utf8 ); +use C4::Auth qw( get_template_and_user ); +use C4::Output qw( output_html_with_http_headers ); +use C4::Letters qw( SendAlerts GetLetters ); use Koha::Acquisition::Orders; use Koha::CsvProfiles; my $input = CGI->new; + my ( $template, $loggedinuser, $cookie ) = get_template_and_user( { template_name => "acqui/lateorders.tt", @@ -62,26 +45,7 @@ my ( $template, $loggedinuser, $cookie ) = get_template_and_user( } ); -my $booksellerid = $input->param('booksellerid'); -my $delay = $input->param('delay') // 0; - -# Get the "date from" param if !defined is today -my $estimateddeliverydatefrom = $input->param('estimateddeliverydatefrom'); -my $estimateddeliverydateto = $input->param('estimateddeliverydateto'); - -# Get the "date to" param. If it is not defined and $delay is not defined too, it is the today's date. -$estimateddeliverydateto ||= - ( not defined $delay and not defined $estimateddeliverydatefrom ) - ? dt_from_string() - : undef; - -my $branch = $input->param('branch'); -my $op = $input->param('op'); - -my @errors = (); -if ( $delay and not $delay =~ /^\d{1,3}$/ ) { - push @errors, { delay_digits => 1, bad_delay => $delay }; -} +my $op = $input->param('op'); if ( $op and $op eq "cud-send_alert" ) { my @ordernums = $input->multi_param("ordernumber"); @@ -104,42 +68,11 @@ if ( $op and $op eq "cud-send_alert" ) { } } -my @lateorders = Koha::Acquisition::Orders->filter_by_lates( - { - delay => $delay, - ( - $estimateddeliverydatefrom - ? ( estimated_from => dt_from_string( $estimateddeliverydatefrom, 'iso' ) ) - : () - ), - ( - $estimateddeliverydateto - ? ( estimated_to => dt_from_string( $estimateddeliverydateto, 'iso' ) ) - : () - ) - }, -)->as_list; - -my $booksellers = Koha::Acquisition::Booksellers->search( - { - id => { -in => [ map { $_->basket->booksellerid } @lateorders ] }, - } -); - -@lateorders = grep { $_->basket->booksellerid eq $booksellerid } @lateorders if $booksellerid; - my $letters = GetLetters( { module => "claimacquisition" } ); -$template->param( ERROR_LOOP => \@errors ) if (@errors); $template->param( - lateorders => \@lateorders, - booksellers => $booksellers, - bookseller_filter => ( $booksellerid ? $booksellers->find($booksellerid) : undef ), - delay => $delay, - letters => $letters, - estimateddeliverydatefrom => $estimateddeliverydatefrom, - estimateddeliverydateto => $estimateddeliverydateto, - intranetcolorstylesheet => C4::Context->preference("intranetcolorstylesheet"), - csv_profiles => Koha::CsvProfiles->search( { type => 'sql', used_for => 'late_orders' } ), + letters => $letters, + csv_profiles => Koha::CsvProfiles->search( { type => 'sql', used_for => 'late_orders' } ), ); + output_html_with_http_headers $input, $cookie, $template->output; diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/acqui/lateorders.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/acqui/lateorders.tt index d53072882af..b359f7067d9 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/acqui/lateorders.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/acqui/lateorders.tt @@ -5,6 +5,7 @@ [% USE Branches %] [% USE TablesSettings %] [% USE Price %] +[% USE To %] [% SET footerjs = 1 %] [% PROCESS 'i18n.inc' %] [% INCLUDE 'doc-head-open.inc' %] @@ -53,221 +54,66 @@ [% IF info_claim %]
Email has been sent.
[% END %] - [% IF lateorders.size %] -
- [% INCLUDE 'csrf-token.inc' %] - - - - [% IF ( letters ) %] -

-

- [% END %] - [% SET total = 0 %] - [% SET total_quantity = 0 %] - [% IF bookseller_filter %] -

Check all Uncheck all

- [% END %] - - - - - - - - - - - - - - - - - - - - - - - - - - [% FOREACH lateorder IN lateorders %] - - - - - [% IF lateorder.get_column('estimated_delivery_date') %] - [% SET estimated_delivery_date = lateorder.get_column('estimated_delivery_date') %] - [% ELSIF lateorder.get_column('calculated_estimated_delivery_date') %] - [% SET estimated_delivery_date = lateorder.get_column('calculated_estimated_delivery_date') %] - [% END %] - - - - - - - - - - - - - - - - - + + [% INCLUDE 'csrf-token.inc' %] + + [% IF ( letters ) %] +

- - - - - - - - -
Order lineOrder dateEstimated delivery dateVendorInformationQuantityTotal costBasketBasket dateBasket groupLibraryBudgetFundClaims countClaimed dateInternal noteVendor noteISBN
- - [% lateorder.ordernumber | $raw %] [% lateorder.basket.closedate | $KohaDates %] ([% lateorder.basket.late_since_days | html %] days) -

- [% estimated_delivery_date | $KohaDates %] - [% IF CAN_user_acquisition_order_manage %] - - Edit - - [% END %] -

-
[% lateorder.basket.bookseller.name | html %] ([% lateorder.basket.bookseller.id | html %]) - [% INCLUDE 'biblio-title.inc' biblio=lateorder.biblio link=1 %] - [% IF ( lateorder.biblio.author ) %]
Author: [% lateorder.biblio.author | html %][% END %] - [% IF ( lateorder.biblio.biblioitem.publishercode ) %] -
Published by: [% lateorder.biblio.biblioitem.publishercode | html %] - [% IF ( lateorder.biblio.biblioitem.publicationyear ) %] - in [% lateorder.biblio.biblioitem.publicationyear | html %] - [% END %] - [% END %] -
- [% lateorder.quantity | html %] - [% SET total_quantity = total_quantity + lateorder.quantity %] - - [% SET subtotal = (lateorder.quantity - lateorder.quantityreceived) * lateorder.rrp %] - [% SET total = total + subtotal %] - [% lateorder.rrp | html %]x[% lateorder.quantity - lateorder.quantityreceived | html %] - = [% subtotal | $Price %] - - [% IF ( CAN_user_acquisition_order_manage ) %] - [% lateorder.basket.basketname | html %] ([% lateorder.basketno | html %]) - [% ELSE %] - [% lateorder.basket.basketname | html %] - ([% lateorder.basketno | html %]) - [% END %] - [% lateorder.basket.creationdate | $KohaDates %] - [% IF ( lateorder.basket.basketgroupid ) %] - [% IF ( CAN_user_acquisition_group_manage ) %] - [% lateorder.basket.basket_group.name | html %] ([% lateorder.basket.basketgroupid | html %]) - [% ELSE %] - [% lateorder.basket.basket_group.name | html %] - ([% lateorder.basket.basketgroupid | html %]) - [% END %] - [% END %] - [% Branches.GetName( lateorder.basket.authorizer.branchcode ) | html %] [% lateorder.fund.budget.budget_period_description | html %][% lateorder.fund.budget_name | html %][% lateorder.claims.count | html %] - [% FOR claim IN lateorder.claims %] - [% claim.claimed_on | $KohaDates %] - [% UNLESS loop.last %]
[% END %] - [% END %] -
- [% IF lateorder.order_internalnote %] -

- [% lateorder.order_internalnote | html %] - - Edit internal note - -

- [% ELSE %] - - Add internal note - - [% END %] -
- [% IF lateorder.order_vendornote %] -

- [% lateorder.order_vendornote | html %] - - Edit vendor note - -

- [% ELSE %] - - Add vendor note - - [% END %] -
[% lateorder.biblio.biblioitem.isbn | $raw %]
Total[% total_quantity | html %][% total | $Price %] 
- -
-
- Export as CSV - - +
+ + +
+ @@ -276,42 +122,36 @@
@@ -388,18 +228,6 @@ $(document).ready(function() { - var table_settings = [% TablesSettings.GetTableSettings( 'acqui', 'lateorders', 'late_orders', 'json' ) | $raw %]; - late_orderst = $("#late_orders").kohaTable({ - "sorting": [[ 1, "asc" ]], - "pagingType": "full", - "autoWidth": false, - "drawCallback": function() { - if ( typeof late_orderst != 'undefined' ) { - check_uncheck(); - $('input:checkbox[name=ordernumber]').bind('click', check_uncheck); - }; - } - }, table_settings ); $('input:checkbox[name=ordernumber]').bind('click', check_uncheck); $('#CheckAll').click(function(e){ e.preventDefault(); @@ -410,7 +238,270 @@ $("#late_orders input:checkbox").prop("checked", false); }); - // Generates a dynamic link for exporting the selection's data as CSV + const buildQuery = () => { + const late_delay = parseInt($("#late_delay").val()); + const estimated_deliverydate_from = $("#estimated_deliverydate_from").val(); + const estimated_deliverydate_to = $("#estimated_deliverydate_to").val(); + + const queryParams = [ + ["only_late", "1"], + ...(Number.isNaN(late_delay) ? [] : [["late_delay", late_delay]]), + ...(estimated_deliverydate_from ? [["estimated_deliverydate_from", estimated_deliverydate_from]] : []), + ...(estimated_deliverydate_to ? [["estimated_deliverydate_to", estimated_deliverydate_to]] : []), + ]; + + return '/api/v1/acquisitions/orders?' + queryParams + .map(([key, value]) => `${key}=${encodeURIComponent(value)}`) + .join("&"); + } + + let additional_filters = { + "basket.vendor_id": () => { + let vendor_id = $("#vendor_filter").val(); + if (!vendor_id) return ""; + return { "=": vendor_id }; + } + }; + + var table_settings = [% TablesSettings.GetTableSettings( 'acqui', 'lateorders', 'late_orders', 'json' ) | $raw %]; + var late_orderst = $("#late_orders").kohaTable({ + order: [[ 1, "asc" ]], + paging: true, + embed: [ + "basket.authorizer+strings", + "basket.basket_group", + "basket.late_since_days", + "basket.vendor", + "biblio", + "claims+count", + "fund.budget" + ], + ajax: { + url: buildQuery(), + }, + columns: [ + { + data: "order_id", // checkbox + orderable: false, + searchable: false, + render: function(data, type, row, meta) { + return ''.format(escape_str(data),escape_str(row.basket.vendor_id)); + } + }, + { + data: "order_id", + orderable: true, + searchable: true + }, + { + data: "basket.close_date", + type: "date", + orderable: true, + render: function(data, type, row, meta) { + return '%s (%s)'.format($date(data),_("%s days").format(escape_str(row.basket.late_since_days))); + } + }, + { + data: "estimated_delivery_date", + type: "date", + orderable: true, + render: function(data, type, row, meta) { + return '

%s'.format($date(data)) + [% IF CAN_user_acquisition_order_manage %] + + ''.format(encodeURIComponent(row.order_id),_("Edit delivery date")) + + ' %s'.format(_("Edit")) + [% END %] + + '

'; + } + }, + { + data: 'basket.vendor.name', + orderable: false, + searchable: true, + render: function(data, type, row, meta) { + return '%s (%s)'.format(escape_str(data), escape_str(row.basket.vendor_id)); + } + }, + { + [% SET summary_fields = "biblio.title:biblio.author:biblio.isbn:biblio.publisher:me.internal_note:me.vendor_note" %] + [% IF Koha.Preference('marcflavour')=='UNIMARC' %][% SET summary_fields = summary_fields _ ":biblio.ean" %][% END %] + "data": "[% summary_fields | html %]", + "render": function(data, type, row, meta) { + var result = ''; + if ( row && row.biblio_id != null ) { + result = '

%s'.format(encodeURIComponent(row.biblio_id),escape_str(row.biblio.title)); + if ( row.biblio.author != null ) + result += '
%s: %s'.format(_("Author"),escape_str(row.biblio.author)); + if ( row.biblio.publisher != null ) { + result += '
%s: %s'.format(_("Published by"),escape_str(row.biblio.publisher)); + if ( row.biblio.publication_year != null ) { + result += ' %s %s'.format(__px("Published in ","in"),escape_str(row.biblio.publication_year)); + } + } + result += '

'; + } + + return result; + }, + "orderable": true, + }, + { + data: 'quantity', + orderable: true, + searchable: true + }, + { + data: "", // total cost, calculated + orderable: false, + searchable: false, + render: function(data, type, row, meta) { + let reminder_qty = (row.quantity - row.quantity_received); + let subtotal = reminder_qty * row.replacement_price; + return '%s x %s = %s'.format( escape_price(row.replacement_price), reminder_qty, escape_price(subtotal) ); + } + }, + { + data: 'basket.name', + orderable: true, + searchable: true, + render: function(data, type, row, meta) { + var basket_name = '%s (%s)'.format(escape_str(data),escape_str(row.basket_id)); + [% IF ( CAN_user_acquisition_order_manage ) %] + return '%s'.format(encodeURIComponent(row.basket_id),escape_str(data),basket_name); + [% ELSE %] + return basket_name; + [% END %] + } + }, + { + data: 'basket.creation_date', + orderable: true, + render: function(data, type, row, meta) { + return $date(data); + } + }, + { + data: 'basket.basket_group_id', + orderable: true, + searchable: true, + render: function(data, type, row, meta) { + if ( data != null ) { + [%- IF CAN_user_acquisition_group_manage -%] + return '%s (%s)' + .format(encodeURIComponent(row.vendor_id),encodeURIComponent(data),row.basket.basket_group.name, data); + [%- ELSE -%] + return '%s (%s)'.format(row.basket.basket_group.name, data); + [%- END -%] + } else { return ''; } + } + }, + { + data: 'basket.authorizer.library_id', + orderable: false, + searchable: false, + render: function(data, type, row, meta) { + return escape_str(row.basket.authorizer._strings.library_id.str); + } + }, + { + data: 'fund.budget.budget_period_description', + orderable: true, + searchable: true + }, + { + data: 'fund.name', + orderable: true, + searchable: true + }, + { + data: 'claims_count', + orderable: true, + searchable: false + }, + { + data: 'claims', + orderable: false, + searchable: false, + render: function(data, type, row, meta) { + return data != undefined ? data.map(item => $date(item.date_claimed)).join('
') : ''; + } + }, + { + data: "", // internal_note + orderable: false, + searchable: false, + render: function(data, type, row, meta) { + if ( row.internal_note != null && row.internal_note != '' ) { + return '

' + + '%s'.format(row.order_id,escape_str(row.internal_note)) + + ' Edit internal note

'.format(_("Edit internal note"),_("Edit internal note")); + } + else { + return ' %s'.format(_("Add internal note"),_("Add internal note")); + } + + } + }, + { + data: "", // vendor_note + orderable: false, + searchable: false, + render: function(data, type, row, meta) { + if ( row.vendor_note != null && row.vendor_note != '' ) { + return '

%s'.format(row.order_id,escape_str(row.vendor_note)) + + ' Edit vendor note

'.format(_("Edit vendor note"),_("Edit vendor note")); + } + else { + return ' %s'.format(_("Add vendor note"),_("Add vendor note")); + } + } + }, + { + data: 'biblio.isbn', + orderable: true, + searchable: true + } + ] + }, + table_settings, + false, // add_column_filters + additional_filters, // default_filters + undefined, // filters_options + undefined, //external_filter_nodes + ); + + const redrawTable = () => { + late_orderst.api().ajax.url(buildQuery()).load(); + }; + + const clearFilters = () => { + $("#late_delay").val("0"); + $("#estimated_deliverydate_from")[0]._flatpickr?.clear(); + $("#estimated_deliverydate_to")[0]._flatpickr?.clear(); + $("#vendor_filter").val("").trigger('change'); + + redrawTable(); + }; + + $("#late_orders_filter_form").on("submit", (e) => { + e.preventDefault(); + redrawTable(); + }); + + // Clear all filters + $(".clear_search").click(clearFilters); + + // Generates a dynamic link for exporting the selection's data as CSV $("#exportbutton, #export-csv-menu a").click(function() { var all_nodes = late_orderst.DataTable().rows().nodes(); var selected = $(all_nodes).find("input[name='ordernumber']:checked"); @@ -474,6 +565,38 @@ $("#estimated_delivery_date").html( "" ); $("#date_ordernumber").val(""); }); + + function populateVendorsFilter() { + $.ajax({ + type: "GET", + url: "/api/v1/acquisitions/vendors", + success: function (vendors) { + vendors + .sort((a, b) => + a.name.localeCompare(b.name) + ) + .forEach(function (vendor) { + if ( + $( + "#vendor_filter option[value=" + + vendor.id + + "]" + ).length == 0 + ) { + $("#vendor_filter").append( + '" + ); + } + }); + }, + }); + } + + populateVendorsFilter(); }); [% END %] -- 2.49.0