Bug 39438: Add additional fields support to ERM agreement periods

This patch enables additional fields functionality for agreement periods
in the ERM (Electronic Resource Management) module, allowing libraries
to define custom metadata fields for agreement period records.

Changes include:
- Update Koha::AdditionalField to support erm_agreement_periods table
- Modify API definitions to include extended_attributes in responses
- Add erm_agreement_periods option to additional fields admin interface
- Enhance Vue components to display and manage additional field values
- Update ShowElement component to handle period-specific attributes

Test plan:
1. Navigate to Administration > Additional fields
2. Create new fields for "Agreement periods"
3. Edit an ERM agreement and add/edit period information
4. Verify additional fields appear and can be populated
5. Confirm values are saved and displayed correctly

Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
This commit is contained in:
Matt Blenkinsop 2025-10-22 17:09:30 +01:00 committed by Lucas Gass
parent bee9d8da78
commit 080fccc0c5
No known key found for this signature in database
GPG Key ID: 60B447398B0D36CA
12 changed files with 88 additions and 28 deletions

View File

@ -64,7 +64,8 @@ sub to_api {
'erm_packages' => 'package',
'aqorders' => 'order',
'aqbooksellers:vendor' => 'vendor',
'erm_titles' => 'title'
'erm_titles' => 'title',
'erm_agreement_periods' => 'agreement_period'
};
my $json = $self->SUPER::to_api($params);

View File

@ -27,6 +27,7 @@ use Koha::Acquisition::Bookseller;
use base qw(Koha::Object::Mixin::AdditionalFields Koha::Object);
use Koha::ERM::Agreement::Period;
use Koha::ERM::Agreement::Periods;
use Koha::ERM::Agreement::Licenses;
use Koha::ERM::Agreement::Relationships;
@ -60,7 +61,13 @@ sub periods {
$self->periods->delete;
for my $period (@$periods) {
$self->_result->add_to_erm_agreement_periods($period);
my $extended_attributes = delete $period->{extended_attributes} // [];
my @extended_attributes =
map { { 'id' => $_->{field_id}, 'value' => $_->{value} } } @{$extended_attributes};
$period->{agreement_id} = $self->agreement_id;
my $stored_period = Koha::ERM::Agreement::Period->new($period)->store;
$stored_period->extended_attributes( \@extended_attributes );
}
}
);

View File

@ -19,7 +19,7 @@ use Modern::Perl;
use Koha::Database;
use base qw(Koha::Object);
use base qw(Koha::Object::Mixin::AdditionalFields Koha::Object);
=head1 NAME

View File

@ -21,7 +21,7 @@ use Koha::Database;
use Koha::ERM::Agreement::Period;
use base qw(Koha::Objects);
use base qw(Koha::Objects::Mixin::AdditionalFields Koha::Objects);
=head1 NAME

View File

@ -53,7 +53,8 @@ sub _list {
package => 'erm_packages',
order => 'aqorders',
vendor => 'aqbooksellers:vendor',
title => 'erm_titles'
title => 'erm_titles',
agreement_period => 'erm_agreement_periods'
};
my @tables;

View File

@ -28,6 +28,16 @@ properties:
type:
- string
- "null"
extended_attributes:
description: Related additional field values
type:
- array
items:
$ref: extended_attribute_value.yaml
_strings:
type:
- object
- "null"
additionalProperties: false
required:
- started_on

View File

@ -189,6 +189,8 @@
type: string
enum:
- periods
- periods.extended_attributes
- periods+strings
- user_roles
- user_roles.patron
- agreement_licenses

View File

@ -25,6 +25,7 @@
- subscription
- vendor
- title
- agreement_period
- $ref: "../swagger.yaml#/parameters/match"
- $ref: "../swagger.yaml#/parameters/order_by"
- $ref: "../swagger.yaml#/parameters/page"

View File

@ -147,6 +147,7 @@
[% WRAPPER table_option value="erm_licenses" %]Licenses[% END %]
[% WRAPPER table_option value="erm_packages" %]Packages[% END %]
[% WRAPPER table_option value="erm_titles" %]Titles[% END %]
[% WRAPPER table_option value="erm_agreement_periods" %]Agreement periods[% END %]
</ul>
[% END %]
[% IF CAN_user_updatecharges_remaining_permissions || CAN_user_parameters_manage_accounts %]

View File

@ -21,7 +21,7 @@ export class ERMAPIClient {
endpoint: "agreements/" + id,
headers: {
"x-koha-embed":
"periods,user_roles,user_roles.patron,agreement_licenses,agreement_licenses.license,agreement_relationships,agreement_relationships.related_agreement,documents,agreement_packages,agreement_packages.package,vendor,extended_attributes,+strings",
"periods,periods.extended_attributes,periods+strings,user_roles,user_roles.patron,agreement_licenses,agreement_licenses.license,agreement_relationships,agreement_relationships.related_agreement,documents,agreement_packages,agreement_packages.package,vendor,extended_attributes,+strings",
},
}),
getAll: (query, params) =>

View File

@ -169,6 +169,7 @@ export default {
type: "table",
columnData: "periods",
hidden: agreement => !!agreement.periods?.length,
includeAdditionalFields: true,
columns: [
{
name: $__("Period start"),
@ -211,6 +212,7 @@ export default {
ended_on: null,
cancellation_deadline: null,
notes: null,
extended_attributes: [],
},
},
},
@ -254,6 +256,12 @@ export default {
label: $__("Notes"),
indexRequired: true,
},
{
name: "additional_fields",
type: "additional_fields",
extended_attributes_resource_type:
"agreement_period",
},
],
hideIn: ["List"],
},
@ -797,8 +805,12 @@ export default {
}
agreement.periods = agreement.periods.map(
({ agreement_id, agreement_period_id, ...keepAttrs }) =>
keepAttrs
({
agreement_id,
agreement_period_id,
_strings,
...keepAttrs
}) => keepAttrs
);
agreement.user_roles = agreement.user_roles.map(

View File

@ -55,7 +55,7 @@
<table>
<thead>
<th
v-for="column in attribute.columns"
v-for="column in tableColumns"
:key="column.name + 'head'"
>
{{ column.name }}
@ -67,7 +67,7 @@
v-bind:key="counter"
>
<td
v-for="dataColumn in attribute.columns"
v-for="dataColumn in tableColumns"
:key="dataColumn.name + 'data'"
>
<template
@ -196,6 +196,30 @@ export default {
);
return option.description;
});
const tableColumns = computed(() => {
const tableData = props.resource[attribute.value.columnData];
const columns = attribute.value.columns;
if (attribute.value.includeAdditionalFields && tableData?.length) {
const additionalFieldColumns = tableData.reduce((acc, curr) => {
if (curr._strings?.additional_field_values.length) {
curr._strings.additional_field_values.forEach(av => {
if (!acc.includes(av.field_label)) {
acc.push(av.field_label);
}
curr[av.field_label] = av.value_str;
});
}
return acc;
}, []);
additionalFieldColumns.forEach(col => {
columns.push({
name: col,
value: col,
});
});
}
return columns;
});
return {
...baseElement,
@ -203,6 +227,7 @@ export default {
attribute,
formatValue,
radioOptionText,
tableColumns,
};
},
props: {