From 58893050b93731a3cfa1ad3df2df58c4d81953d4 Mon Sep 17 00:00:00 2001 From: Jonathan Druart <jonathan.druart@bugs.koha-community.org> Date: Tue, 11 Apr 2023 23:01:53 +0200 Subject: [PATCH] Bug 33105: Add vendor issues Content-Type: text/plain; charset=utf-8 This patch is the main patch of this patch set, it contains the controller acqui/vendor_issues.pl, its corresponding template, and some links to this script. It adds: * A new DB table aqbookseller_issues linked with the aqbooksellers table * A new subpermission acquisition.issue_manage * A new authorised value category VENDOR_ISSUE_TYPE and two examples MAINTENANCE and OUTAGE * A new controller couple acqui/vendor_issues.[pl,tt] Test plan: 0. Apply the patches, run updatedatabase and restart_all 1. Go to the acquisition module, create a new vendor or use an existing one 2. Create a couple of issues for this vendor 3. Edit/Delete and search for those issues This is the basics for tracking issues with vendors. Suggestions welcome, on follow-up bug reports. Signed-off-by: Jonathan Field <jonathan.fieeld@ptfs-europe.com> --- acqui/vendor_issues.pl | 113 +++++++ .../prog/en/includes/permissions.inc | 5 + .../prog/en/includes/vendor-menu.inc | 1 + .../prog/en/modules/acqui/vendor_issues.tt | 282 ++++++++++++++++++ .../en/modules/admin/authorised_values.tt | 2 + 5 files changed, 403 insertions(+) create mode 100755 acqui/vendor_issues.pl create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/acqui/vendor_issues.tt diff --git a/acqui/vendor_issues.pl b/acqui/vendor_issues.pl new file mode 100755 index 0000000000..561769c28e --- /dev/null +++ b/acqui/vendor_issues.pl @@ -0,0 +1,113 @@ +#!/usr/bin/perl + +# Copyright 2023 Koha Development Team +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# Koha is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see <http://www.gnu.org/licenses>. + +use Modern::Perl; +use CGI qw ( -utf8 ); +use Try::Tiny; +use C4::Context; +use C4::Auth qw( get_template_and_user ); +use C4::Output qw( output_html_with_http_headers ); + +use Koha::Acquisition::Booksellers; + +my $input = CGI->new; +my $booksellerid = $input->param('booksellerid'); +my $issue_id = $input->param('issue_id'); +my $op = $input->param('op') || 'list'; +my @messages; + +my ( $template, $loggedinuser, $cookie ) = get_template_and_user( + { + template_name => "acqui/vendor_issues.tt", + query => $input, + type => "intranet", + flagsrequired => { acquisition => 'issues_manage' }, + } +); + +my $issue; +if ( $issue_id ) { + $issue = Koha::Acquisition::Bookseller::Issues->find($issue_id); + $booksellerid = $issue->vendor_id; +} +my $vendor = Koha::Acquisition::Booksellers->find($booksellerid); + +if ( $op eq 'add_form' || $op eq 'show' ) { + $template->param( issue => $issue ); +} elsif ( $op eq 'add_validate' ) { + my $type = $input->param('type'); + my $started_on = $input->param('started_on'); + my $ended_on = $input->param('ended_on'); + my $notes = $input->param('notes'); + + if ($issue_id) { + try { + $issue->set( + { + type => $type, + started_on => $started_on, + ended_on => $ended_on, + notes => $notes + } + )->store; + push @messages, { type => 'message', code => 'success_on_update' }; + } catch { + push @messages, { type => 'error', code => 'error_on_update' }; + }; + } else { + try { + Koha::Acquisition::Bookseller::Issue->new( + { + vendor_id => $booksellerid, + type => $type, + started_on => $started_on, + ended_on => $ended_on, + notes => $notes, + } + )->store; + push @messages, { type => 'message', code => 'success_on_insert' }; + } catch { + push @messages, { type => 'error', code => 'error_on_insert' }; + }; + } + $op = 'list'; +} elsif ( $op eq 'delete_confirm' ) { + $template->param( issue => $issue ); +} elsif ( $op eq 'delete_confirmed' ) { + try { + $issue->delete; + push @messages, { type => 'message', code => 'success_on_delete' }; + } catch { + push @messages, { type => 'error', code => 'error_on_delete' }; + }; + $op = 'list'; +} + +if ( $op eq 'list' ) { + $template->param( issues_count => $vendor->issues->search->count ); +} + +$template->param( + messages => \@messages, + op => $op, + vendor => $vendor, + booksellerid => $vendor->id, # Used by vendor-menu.inc +); + +output_html_with_http_headers $input, $cookie, $template->output; diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/permissions.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/permissions.inc index 9466b94cf7..f718ee8260 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/includes/permissions.inc +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/permissions.inc @@ -454,6 +454,11 @@ Manage all baskets and order lines, regardless of restrictions on them </span> <span class="permissioncode">([% name | html %])</span> + [%- CASE 'issue_manage' -%] + <span class="sub_permission issues_manage_subpermission"> + Manage issues + </span> + <span class="permissioncode">([% name | html %])</span> [%- CASE 'order_receive' -%] <span class="sub_permission order_receive_subpermission"> Receive orders and manage shipments diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/vendor-menu.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/vendor-menu.inc index 3fc669e840..82415521c0 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/includes/vendor-menu.inc +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/vendor-menu.inc @@ -4,6 +4,7 @@ [% IF ( CAN_user_acquisition_order_manage ) %]<li><a href="/cgi-bin/koha/acqui/booksellers.pl?booksellerid=[% booksellerid | uri %]">Baskets</a></li>[% END %] [% IF ( CAN_user_acquisition_group_manage ) %]<li><a href="/cgi-bin/koha/acqui/basketgroup.pl?booksellerid=[% booksellerid | uri %]">Basket groups</a></li>[% END %] [% IF ( CAN_user_acquisition_contracts_manage ) %]<li><a href="/cgi-bin/koha/admin/aqcontract.pl?booksellerid=[% booksellerid | uri %]">Contracts</a></li>[% END %] + [% IF ( CAN_user_acquisition_issue_manage ) %]<li><a href="/cgi-bin/koha/acqui/vendor_issues.pl?booksellerid=[% booksellerid | uri %]">Issues</a></li>[% END %] <li><a href="/cgi-bin/koha/acqui/invoices.pl?supplierid=[% booksellerid | uri %]&op=do_search">Invoices</a></li> [% IF ( CAN_user_acquisition_order_manage ) %][% IF ( basketno ) %] <li><a href="/cgi-bin/koha/acqui/uncertainprice.pl?booksellerid=[% booksellerid | uri %]&basketno=[% basketno | uri %]&owner=1">Uncertain prices</a></li> diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/acqui/vendor_issues.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/acqui/vendor_issues.tt new file mode 100644 index 0000000000..4a6a672875 --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/acqui/vendor_issues.tt @@ -0,0 +1,282 @@ +[% USE raw %] +[% USE Asset %] +[% USE AuthorisedValues %] +[% USE KohaDates %] +[% SET footerjs = 1 %] +[% INCLUDE 'doc-head-open.inc' %] +<title> + [% IF op =='add_form' %] + [% IF issue.issue_id %] + Modify vendor issue + [% ELSE %] + New vendor issue + [% END %] › [% ELSE %] + [% IF op == 'delete_confirm' %] + Confirm deletion of vendor issue › [% END %] + [% END %] + Vendor issue › Acquisition › Koha +</title> +[% INCLUDE 'doc-head-close.inc' %] +</head> + +<body id="acq_supplier_issues" class="acq"> +[% WRAPPER 'header.inc' %] + [% INCLUDE 'acquisitions-search.inc' %] +[% END %] + +[% WRAPPER 'sub-header.inc' %] +<nav id="breadcrumbs" aria-label="Breadcrumb" class="breadcrumb"> + <ol> + <li> + <a href="/cgi-bin/koha/mainpage.pl">Home</a> + </li> + <li> + <a href="/cgi-bin/koha/acqui/acqui-home.pl">Acquisitions</a> + </li> + <li> + <a href="/cgi-bin/koha/acqui/supplier.pl?booksellerid=[% vendor.id | uri %]">[% vendor.name | html %]</a> + </li> + [% IF op == 'list' %] + <li> + <a href="#" aria-current="page">Issues</a> + </li> + [% ELSE %] + <li> + <a href="/cgi-bin/koha/acqui/vendor_issues.pl?booksellerid=[% vendor.id | uri %]">Issues</a> + </li> + <li> + <a href="#" aria-current="page"> + [% IF issue %] + Issue #[% issue.issue_id| html %] + [% ELSE %] + New issue + [% END %] + </a> + </li> + [% END %] + </ol> +</nav> +[% END %] + +<div class="main container-fluid"> + <div class="row"> + <div class="col-sm-10 col-sm-push-2"> + <main> + +[% FOR m IN messages %] + <div class="dialog [% m.type | html %]"> + [% SWITCH m.code %] + [% CASE 'error_on_update' %] + <span>An error occurred when updating this issue.</span> + [% CASE 'error_on_insert' %] + <span>An error occurred when adding this issue</span> + [% CASE 'error_on_delete' %] + <span>An error occurred when deleting this issue. Check the logs.</span> + [% CASE 'success_on_update' %] + <span>Issue updated successfully.</span> + [% CASE 'success_on_insert' %] + <span>Issue created successfully.</span> + [% CASE 'success_on_delete' %] + <span>Issue deleted successfully.</span> + [% CASE %] + <span>[% m.code | html %]</span> + [% END %] + </div> +[% END %] + +[% IF op == 'add_form' %] + [% IF issue %] + <h1>Modify a vendor issue</h1> + [% ELSE %] + <h1>New vendor issue</h1> + [% END %] + + <form action="/cgi-bin/koha/acqui/vendor_issues.pl" name="Aform" method="post" class="validated"> + <input type="hidden" name="op" value="add_validate" /> + <input type="hidden" name="booksellerid" value="[% vendor.id | html %]" /> + <input type="hidden" name="issue_id" value="[% issue.issue_id| html %]" /> + + <fieldset class="rows"> + <ol> + [% IF issue %] + <li><span class="label">Issue ID: </span>[% issue.issue_id | html %]</li> + [% END %] + <li> + <label for="issue_type">Type: </label> + [% PROCESS 'av-build-dropbox.inc' name="type", category="VENDOR_ISSUE_TYPE", default=issue.type, empty=1, size = 20 %] + </li> + <li> + <label for="started_on">Started on: </label> + <input type="text" size="10" id="from" name="started_on" value="[% issue.started_on| html %]" class="flatpickr" data-date_to="to" /> + <div class="hint">[% INCLUDE 'date-format.inc' %]</div> + </li> + <li> + <label for="ended_on">Ended on: </label> + <input type="text" size="10" id="to" name="ended_on" value="[% issue.ended_on | html %]" class="flatpickr" /> + <div class="hint">[% INCLUDE 'date-format.inc' %]</div> + </li> + <li> + <label for="notes">Notes: </label> + <textarea name="notes" id="notes" rows="3" cols="50">[% issue.notes | html %]</textarea> + </li> + </ol> + </fieldset> + + <fieldset class="action"> + <input type="submit" class="btn btn-primary" value="Submit" /> + <a class="cancel" href="/cgi-bin/koha/acqui/vendor_issues.pl?booksellerid=[% vendor.id | uri %]">Cancel</a> + </fieldset> + </form> +[% END %] + +[% IF op == 'delete_confirm' %] + <div class="dialog alert"> + <h1>Delete issue #[% issue.issue_id | html %]?<h1> + <form action="/cgi-bin/koha/acqui/vendor_issues.pl" method="post"> + <input type="hidden" name="op" value="delete_confirmed" /> + <input type="hidden" name="issue_id" value="[% issue.issue_id | html %]" /> + <button type="submit" class="approve"><i class="fa fa-fw fa-check"></i> Yes, delete</button> + </form> + <form action="/cgi-bin/koha/acqui/vendor_issues.pl?booksellerid=[% vendor.id | uri %]" method="get"> + <button type="submit" class="deny"><i class="fa fa-fw fa-remove"></i> No, do not delete</button> + </form> + </div> +[% END %] + +[% IF op == 'show' %] + <h1>Vendor issue #[% issue.issue_id | html %]</h1> + + <fieldset class="rows"> + <ol> + [% IF issue %] + <li><span class="label">Issue ID: </span>[% issue.issue_id | html %]</li> + [% END %] + <li> + <label for="issue_type">Type: </label> + [% AuthorisedValues.GetByCode( 'VENDOR_ISSUE_TYPE', issue.type, 0 ) | html %] + </li> + <li> + <label for="started_on">Started on: </label> + [% issue.started_on | $KohaDates %] + </li> + <li> + <label for="ended_on">Ended on: </label> + [% issue.ended_on | $KohaDates %] + </li> + <li> + <label for="notes">Notes: </label> + [% issue.notes | html %] + </li> + </ol> + </fieldset> + + <fieldset class="action"> + <a href="/cgi-bin/koha/acqui/vendor_issues.pl?booksellerid=[% vendor.id | uri %]">Back</a> + </fieldset> + </form> +[% END %] +[% IF op == 'list' %] + + <div id="toolbar" class="btn-toolbar"> + <a class="btn btn-default" id="new_issue" href="/cgi-bin/koha/acqui/vendor_issues.pl?booksellerid=[% vendor.id | uri %]&op=add_form"><i class="fa fa-plus"></i> New issue</a> + </div> + + <h1>Vendor issues</h1> + + [% IF issues_count > 0 %] + <div class="page-section"> + <div class="table_vendor_issues_table_controls"></div> + <table id="vendor_issues"> + <thead> + <tr> + <th>Issue ID</th> + <th>Type</th> + <th>Started on</th> + <th>Ended on</th> + <th data-class-name="actions noExport">Actions</th> + </tr> + </thead> + </table> + </div><!-- /.page-section --> + [% ELSE %] + <div class="dialog message"> + There are no issues defined. <a href="/cgi-bin/koha/acqui/vendor_issues.pl?booksellerid=[% vendor.id | uri %]&op=add_form">Create a new issue</a>. + </div> + [% END %] +[% END %] + + </main> + </div> <!-- /.col-sm-10.col-sm-push-2 --> + + <div class="col-sm-2 col-sm-pull-10"> + <aside> + [% INCLUDE 'vendor-menu.inc' %] + </aside> + </div> <!-- /.col-sm-2.col-sm-pull-10 --> + </div> <!-- /.row --> + +[% MACRO jsinclude BLOCK %] + [% Asset.js("js/acquisitions-menu.js") | $raw %] + [% INCLUDE 'calendar.inc' %] + [% INCLUDE 'datatables.inc' %] + <script> + + $(document).ready(function() { + var issues_table_url = '/api/v1/acquisitions/vendors/[% vendor.id | uri %]/issues?'; + + var issues_table = $("#vendor_issues").kohaTable({ + ajax: { + url: issues_table_url + }, + embed: ["+strings"], + order: [[ 0, "desc" ]], + columns: [ + { + data: "issue_id", + searchable: true, + orderable: true, + render: function(data, type, row, meta) { + return '<a href="/cgi-bin/koha/acqui/vendor_issues.pl?op=show&issue_id=' + encodeURIComponent(row.issue_id) +'">' + escape_str(row.issue_id) + '</a>'; + }, + }, + { + data: "type", + searchable: true, + orderable: true, + render: function(data, type, row, meta) { + return row._strings.type ? escape_str(row._strings.type.str) : ""; + } + }, + { + data: "started_on", + searchable: true, + orderable: true, + render: function(data, type, row, meta) { + return $date(row.started_on); + } + }, + { + data: "ended_on", + searchable: true, + orderable: true, + render: function(data, type, row, meta) { + return $date(row.ended_on); + } + }, + { + data: function( row, type, val, meta ) { + + var result = '<a class="btn btn-default btn-xs" role="button" href="/cgi-bin/koha/acqui/vendor_issues.pl?op=add_form&issue_id='+ encodeURIComponent(row.issue_id) +'"><i class="fa fa-pencil" aria-hidden="true"></i> '+_("Edit")+'</a>'+"\n"; + result += '<a class="btn btn-default btn-xs" role="button" href="/cgi-bin/koha/acqui/vendor_issues.pl?op=delete_confirm&issue_id='+ encodeURIComponent(row.issue_id) +'"><i class="fa fa-trash" aria-hidden="true"></i> '+_("Delete")+'</a>'; + return result; + }, + searchable: false, + orderable: false + } + ] + }, null, 1); + + }); + </script> +[% END %] +[% INCLUDE 'intranet-bottom.inc' %] diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/authorised_values.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/authorised_values.tt index 1a5265ff3b..9609fc38bc 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/authorised_values.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/authorised_values.tt @@ -497,6 +497,8 @@ <p>Values that can be entered to fill in the 'Vendor type' field in the acquisitions module, that can be used for statistical purposes</p> [% CASE 'VENDOR_INTERFACE_TYPE' %] <p>Values that can be entered to fill in the 'Vendor interface type' field in the acquisitions module</p> + [% CASE 'VENDOR_ISSUE_TYPE' %] + <p>Values that can be entered to fill in the 'Vendor issue type' field in the acquisitions module</p> [% CASE 'WITHDRAWN' %] <p>Description of a withdrawn item (appears when adding or editing an item)</p> [% CASE 'YES_NO' %] -- 2.30.2