Bug 33104: Add the ability to create vendor interfaces

This patchset is adding the ability to create interfaces for vendors.
An interface is a website, software, or portal that you use to manage orders or
gather statistics from the vendor/organisation.

It will help librarians to keep track of those different information
within Koha.

* new DB table aqbookseller_interfaces(id, vendor_id, type, name, uri,
login, password, account_email, notes)
* new AV category VENDOR_INTERFACE_TYPE with 3 example values ADMIN,
ORDERS, REPORTS
* new pair of Koha classes Koha::Acquisition::Bookseller::Interface[s]
* new method to retrieve the interfaces from the vendor
Koha::Acquisition::Bookseller->interfaces
* Add/Delete interfaces when editing a vendor
* Display the interfaces on the vendor show view

Test plan:
- Add a new vendor
=> Notice the new "Interfaces" block
- Create some interfaces
=> They are display on the vendor show view
=> The password is hashed and can displayed on the demand

QA Note:
The "contacts" code is not very nice and I didn't want to replicate it,
so I went another way and tried to make the code reusable, for further
reutilisation.

Signed-off-by: Jonathan Field <jonathan.field@ptfs-europe.com>
Signed-off-by: Tomas Cohen Arazi <tomascohen@theke.io>
This commit is contained in:
Jonathan Druart 2023-03-01 15:53:22 +01:00 committed by Tomas Cohen Arazi
parent 087f4db213
commit 06b1ca3bde
Signed by: tomascohen
GPG key ID: 0A272EA1B2F3C15F
4 changed files with 132 additions and 2 deletions

View file

@ -19,6 +19,7 @@ use Modern::Perl;
use Koha::Acquisition::Bookseller::Aliases;
use Koha::Acquisition::Bookseller::Contacts;
use Koha::Acquisition::Bookseller::Interfaces;
use Koha::Subscriptions;
use base qw( Koha::Object );
@ -104,6 +105,33 @@ sub aliases {
return Koha::Acquisition::Bookseller::Aliases->_new_from_dbic( $rs );
}
=head3 interfaces
my $interfaces = $vendor->interfaces
$vendor->interfaces(\@interfaces);
=cut
sub interfaces {
my ($self, $interfaces) = @_;
if ($interfaces) {
my $schema = $self->_result->result_source->schema;
$schema->txn_do(
sub {
$self->interfaces->delete;
for my $interface (@$interfaces) {
$self->_result->add_to_aqbookseller_interfaces($interface);
}
}
);
}
my $rs = $self->_result->aqbookseller_interfaces;
return Koha::Acquisition::Bookseller::Interfaces->_new_from_dbic( $rs );
}
=head3 to_api_mapping

View file

@ -47,6 +47,7 @@ contact_serialsprimary.
=cut
use Modern::Perl;
use List::MoreUtils qw( any );
use C4::Context;
use C4::Auth qw( checkauth );
@ -130,8 +131,23 @@ if($data{'name'}) {
$contact->{booksellerid} = $data{id};
Koha::Acquisition::Bookseller::Contact->new( $contact )->store
}
# Insert aliases
$bookseller->aliases([ map { { alias => $_ } } @aliases ]);
# Insert interfaces
my @interface_counters = $input->multi_param('interface_counter');
my @interfaces;
for my $counter ( @interface_counters ) {
my $interface = {};
for my $attr (qw(name type uri login password account_email notes)){
my $v = $input->param("interface_${attr}_${counter}");
$interface->{$attr} = $v;
}
push @interfaces, $interface if any { defined && length } values %$interface;
}
$bookseller->interfaces(\@interfaces);
#redirect to booksellers.pl
print $input->redirect("booksellers.pl?booksellerid=".$data{id});
} else {

View file

@ -2588,7 +2588,8 @@ td.bundle {
margin: 0 0 8px 8px;
}
#contact-template {
#contact-template,
#interface-template {
display: none;
}

View file

@ -175,7 +175,6 @@
<label for="aliases">Aliases: </label>
<div id="aliases" style="padding-left: 11rem;"></div>
</li>
</ol>
</fieldset>
<fieldset class="rows">
@ -193,6 +192,11 @@
<button id="add-contact" class="btn btn-default"><i class="fa fa-plus"></i> Add another contact</button>
</fieldset>
<fieldset class="rows">
<legend>Interfaces</legend>
<div id="interfaces"></div>
</fieldset>
<fieldset class="rows">
<legend>Ordering information</legend>
<ol><li><label for="activestatus">Vendor is:</label>
@ -348,6 +352,29 @@
<p><strong>Notes: </strong>[% notes | html %]</p>
[% END %]
</div> <!-- #supplier-ordering-information -->
[% IF vendor.interfaces.count %]
<div id="supplier-interfaces" class="page-section">
<h2>Interfaces</h2>
[% FOR interface IN vendor.interfaces %]
<fieldset class="rows">
<legend>[% interface.name | html %]</legend>
<ul>
<li>
<li>Type: [% interface.type | html %]</li>
<li>URI: [% interface.uri | html %]</li>
<li>Login: [% interface.login | html %]</li>
<li>Password: [% interface.password | html %]</li>
<li>Account email: [% interface.account_email | html %]</li>
<li>Notes : [% interface.notes | html %]</li>
</li>
</ul>
</fieldset>
[% END %]
</div>
[% END %]
</div> <!-- /.col-sm-6 -->
<div class="col-sm-6">
@ -500,6 +527,63 @@
$("#aliases").html(nodes.html());
}
[% IF vendor %]
let interfaces = [% To.json(vendor.interfaces.unblessed) | $raw %];
[% ELSE %]
let interfaces = [];
[% END %]
function serialize_interface_form(){
interfaces = [];
$("#interfaces > fieldset.supplier-interface > ol.interface-form").each( (index, interface_form )=> {
let interface = {};
$(interface_form).find('input,textarea').serializeArray().map(attr => {
let name = attr.name.replace(/interface_([\w_]+)_\d+/, "$1");
interface[name] = attr.value
})
interfaces.push(interface);
});
return interfaces;
}
function remove_interface(i){
interfaces = serialize_interface_form();
interfaces.splice(i, 1);
refresh_interfaces();
}
function add_interface(){
interfaces = serialize_interface_form();
interfaces.push({
type: "",
name: "",
uri: "",
login: "",
password: "",
account_email: "",
notes: "",
});
refresh_interfaces();
}
function refresh_interfaces(){
let nodes = $("<div id='interfaces'></div>");
interfaces.forEach((interface, i) => {
let n = $("<fieldset class='supplier-interface'></fieldset>");
n.append("<legend>" + _("Interface details") + "</legend>");
n.append(`<input type="hidden" name="interface_counter" value="${i}" />`);
let ol = $('<ol class="interface-form"></ol>');
ol.append(`<li><label for="interface_name_${i}">` + _("Name: ") + `</label><input type="text" size="40" id="interface_name_${i}" name="interface_name_${i}" value="${interface.name}" /></li>`);
ol.append(`<li><label for="interface_type_${i}">` + _("Type: ") + `</label><input type="text" size="40" id="interface_type_${i}" name="interface_type_${i}" value="${interface.type}" /></li>`);
ol.append(`<li><label for="interface_uri_${i}">` + _("URI: ") + `</label><input type="text" size="40" id="interface_uri_${i}" name="interface_uri_${i}" value="${interface.uri}" /></li>`);
ol.append(`<li><label for="interface_login_${i}">` + _("Login: ") + `</label><input type="text" size="40" id="interface_login_${i}" name="interface_login_${i}" value="${interface.login}" /></li>`);
ol.append(`<li><label for="interface_password_${i}">` + _("Password: ") + `</label><input type="text" size="40" id="interface_password_${i}" name="interface_password_${i}" value="${interface.password}" /></li>`);
ol.append(`<li><label for="interface_account_email_${i}">` + _("Account email: ") + `</label><input type="text" size="40" id="interface_account_email_${i}" name="interface_account_email_${i}" value="${interface.account_email}" /></li>`);
ol.append(`<li><label for="interface_notes_${i}">` + _("Notes: ") + `</label><textarea cols="40" rows="3" id="interface_notes_${i}" name="interface_notes_${i}">${interface.notes}</textarea></li>`);
ol.append(`<li><button class="btn btn-default" onclick="remove_interface(${i});"><i class="fa fa-trash"></i> Delete interface</li>`);
n.append(ol);
nodes.append(n);
});
nodes.append(`<button onclick="add_interface();" class="btn btn-default"><i class="fa fa-plus"></i> ` + _("Add another interface") + '</button>')
$("#interfaces").replaceWith(nodes);
}
var Sticky;
$(document).ready(function() {
@ -540,6 +624,7 @@
});
refresh_aliases();
refresh_interfaces();
Sticky = $("#toolbar");
Sticky.hcSticky({