Bug 33105: Add vendor issues

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>

Signed-off-by: Marcel de Rooy <m.de.rooy@rijksmuseum.nl>
Signed-off-by: Tomas Cohen Arazi <tomascohen@theke.io>
This commit is contained in:
Jonathan Druart 2023-04-11 23:01:53 +02:00 committed by Tomas Cohen Arazi
parent 56cdee5037
commit 0f7daf1b00
No known key found for this signature in database
GPG Key ID: 0A272EA1B2F3C15F
5 changed files with 403 additions and 0 deletions

113
acqui/vendor_issues.pl Executable file
View File

@ -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;

View File

@ -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

View File

@ -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 %]&amp;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 %]&amp;basketno=[% basketno | uri %]&amp;owner=1">Uncertain prices</a></li>

View File

@ -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 %] &rsaquo; [% ELSE %]
[% IF op == 'delete_confirm' %]
Confirm deletion of vendor issue &rsaquo; [% END %]
[% END %]
Vendor issue &rsaquo; Acquisition &rsaquo; 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&amp;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&amp;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&amp;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' %]

View File

@ -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' %]