From 679928716bb1246fcef932bfb304c33ab3b1038d Mon Sep 17 00:00:00 2001
From: Matt Blenkinsop
Date: Tue, 29 Oct 2024 16:53:18 +0000
Subject: [PATCH] Bug 38290: Add fields to the UI for CRUD
---
Koha/REST/V1/Acquisitions/Vendors.pm | 14 +-
admin/library_groups.pl | 4 +
api/v1/swagger/definitions/vendor.yaml | 8 +
.../swagger/definitions/vendors_config.yaml | 6 +
.../swagger/paths/acquisitions_vendors.yaml | 1 +
.../prog/en/modules/admin/library_groups.tt | 24 ++-
.../prog/js/fetch/acquisition-api-client.js | 2 +-
.../prog/js/vue/components/Vendors/Main.vue | 30 +++-
.../vue/components/Vendors/VendorDetails.vue | 44 +++++-
.../vue/components/Vendors/VendorFormAdd.vue | 17 ++
.../prog/js/vue/stores/vendors.js | 149 +++++++++++++++++-
11 files changed, 281 insertions(+), 18 deletions(-)
diff --git a/Koha/REST/V1/Acquisitions/Vendors.pm b/Koha/REST/V1/Acquisitions/Vendors.pm
index ab9fd2741ef..940dd353cb9 100644
--- a/Koha/REST/V1/Acquisitions/Vendors.pm
+++ b/Koha/REST/V1/Acquisitions/Vendors.pm
@@ -226,12 +226,14 @@ sub config {
return $c->render(
status => 200,
openapi => {
- permissions => $permissions,
- currencies => Koha::Acquisition::Currencies->search->unblessed,
- gst_values => \@gst_values,
- edifact => C4::Context->preference('EDIFACT'),
- marc_orders => C4::Context->preference('MarcOrderingAutomation'),
- erm_module => C4::Context->preference('ERMModule'),
+ permissions => $permissions,
+ currencies => Koha::Acquisition::Currencies->search->unblessed,
+ gst_values => \@gst_values,
+ edifact => C4::Context->preference('EDIFACT'),
+ marc_orders => C4::Context->preference('MarcOrderingAutomation'),
+ erm_module => C4::Context->preference('ERMModule'),
+ logged_in_user => $patron,
+ logged_in_branch => C4::Context::mybranch,
},
);
}
diff --git a/admin/library_groups.pl b/admin/library_groups.pl
index 80021876d59..56ef228add6 100755
--- a/admin/library_groups.pl
+++ b/admin/library_groups.pl
@@ -52,6 +52,7 @@ if ( $op eq 'cud-add' ) {
my $ft_search_groups_staff = $cgi->param('ft_search_groups_staff') || 0;
my $ft_local_hold_group = $cgi->param('ft_local_hold_group') || 0;
my $ft_local_float_group = $cgi->param('ft_local_float_group') || 0;
+ my $ft_acquisitions = $cgi->param('ft_acquisitions') || 0;
if ( !$branchcode && Koha::Library::Groups->search( { title => $title } )->count() ) {
$template->param( error_duplicate_title => $title );
@@ -68,6 +69,7 @@ if ( $op eq 'cud-add' ) {
ft_local_hold_group => $ft_local_hold_group,
ft_limit_item_editing => $ft_limit_item_editing,
ft_local_float_group => $ft_local_float_group,
+ ft_acquisitions => $ft_acquisitions,
branchcode => $branchcode,
}
)->store();
@@ -88,6 +90,7 @@ if ( $op eq 'cud-add' ) {
my $ft_search_groups_staff = $cgi->param('ft_search_groups_staff') || 0;
my $ft_local_hold_group = $cgi->param('ft_local_hold_group') || 0;
my $ft_local_float_group = $cgi->param('ft_local_float_group') || 0;
+ my $ft_acquisitions = $cgi->param('ft_acquisitions') || 0;
if ($id) {
my $group = Koha::Library::Groups->find($id);
@@ -102,6 +105,7 @@ if ( $op eq 'cud-add' ) {
ft_search_groups_staff => $ft_search_groups_staff,
ft_local_hold_group => $ft_local_hold_group,
ft_local_float_group => $ft_local_float_group,
+ ft_acquisitions => $ft_acquisitions,
}
)->store();
diff --git a/api/v1/swagger/definitions/vendor.yaml b/api/v1/swagger/definitions/vendor.yaml
index 581b9388d83..1f316e1db3e 100644
--- a/api/v1/swagger/definitions/vendor.yaml
+++ b/api/v1/swagger/definitions/vendor.yaml
@@ -124,6 +124,14 @@ properties:
- string
- "null"
description: Language
+ lib_group_visibility:
+ type:
+ - string
+ - "null"
+ description: Language
+ lib_group_limits:
+ type: array
+ description: Library groups this vendor belongs to
aliases:
type: array
description: List of aliases
diff --git a/api/v1/swagger/definitions/vendors_config.yaml b/api/v1/swagger/definitions/vendors_config.yaml
index 776b346a82d..66149af1d68 100644
--- a/api/v1/swagger/definitions/vendors_config.yaml
+++ b/api/v1/swagger/definitions/vendors_config.yaml
@@ -19,4 +19,10 @@ properties:
erm_module:
type: boolean
description: ERMModule system preference value
+ logged_in_user:
+ type: object
+ description: The patron logged in to this session
+ logged_in_branch:
+ type: string
+ description: The branch that the patron has logged in to this session
additionalProperties: false
diff --git a/api/v1/swagger/paths/acquisitions_vendors.yaml b/api/v1/swagger/paths/acquisitions_vendors.yaml
index d9ef0842938..814c191f6df 100644
--- a/api/v1/swagger/paths/acquisitions_vendors.yaml
+++ b/api/v1/swagger/paths/acquisitions_vendors.yaml
@@ -159,6 +159,7 @@
- contracts
- extended_attributes
- +strings
+ - lib_group_limits
collectionFormat: csv
produces:
- application/json
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/library_groups.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/library_groups.tt
index 059b66b9b04..84ad30733a3 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/library_groups.tt
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/library_groups.tt
@@ -152,6 +152,12 @@
Is local float group
+
+
+
@@ -235,6 +241,12 @@
Is local float group
+
+
+
@@ -331,7 +343,8 @@
var ft_search_groups_staff = $(this).data("groupFt_search_groups_staff");
var ft_local_hold_group = $(this).data("groupFt_local_hold_group");
var ft_local_float_group = $(this).data("groupFt_local_float_group");
- edit_group(id, parent_id, title, description, ft_hide_patron_info, ft_search_groups_opac, ft_search_groups_staff, ft_local_hold_group, ft_limit_item_editing, ft_local_float_group);
+ var ft_acquisitions = $(this).data("groupFt_acquisitions");
+ edit_group(id, parent_id, title, description, ft_hide_patron_info, ft_search_groups_opac, ft_search_groups_staff, ft_local_hold_group, ft_limit_item_editing, ft_local_float_group, ft_acquisitions);
});
$(".delete-group").on("click", function (e) {
@@ -368,6 +381,7 @@
$("#add-group-modal-ft_search_groups_staff").prop("checked", false);
$("#add-group-modal-ft_local_hold_group").prop("checked", false);
$("#add-group-modal-ft_local_float_group").prop("checked", false);
+ $("#add-group-modal-ft_acquisitions").prop("checked", false);
if (parent_id) {
$("#root-group-features-add").hide();
} else {
@@ -376,7 +390,7 @@
$("#add-group-modal").modal("show");
}
- function edit_group(id, parent_id, title, description, ft_hide_patron_info, ft_search_groups_opac, ft_search_groups_staff, ft_local_hold_group, ft_limit_item_editing, ft_local_float_group) {
+ function edit_group(id, parent_id, title, description, ft_hide_patron_info, ft_search_groups_opac, ft_search_groups_staff, ft_local_hold_group, ft_limit_item_editing, ft_local_float_group, ft_acquisitions) {
$("#edit-group-modal-id").val(id);
$("#edit-group-modal-title").val(title);
$("#edit-group-modal-description").val(description);
@@ -387,6 +401,7 @@
$("#edit-group-modal-ft_search_groups_staff").prop("checked", false);
$("#edit-group-modal-ft_local_hold_group").prop("checked", false);
$("#edit-group-modal-ft_local_float_group").prop("checked", false);
+ $("#edit-group-modal-ft_acquisitions").prop("checked", false);
$("#root-group-features-edit").hide();
} else {
$("#edit-group-modal-ft_hide_patron_info").prop("checked", ft_hide_patron_info ? true : false);
@@ -395,6 +410,7 @@
$("#edit-group-modal-ft_search_groups_staff").prop("checked", ft_search_groups_staff ? true : false);
$("#edit-group-modal-ft_local_hold_group").prop("checked", ft_local_hold_group ? true : false);
$("#edit-group-modal-ft_local_float_group").prop("checked", ft_local_float_group ? true : false);
+ $("#edit-group-modal-ft_acquisitions").prop("checked", ft_acquisitions ? true : false);
$("#root-group-features-edit").show();
}
@@ -455,6 +471,9 @@
[% IF group.ft_local_float_group %]
Is local float group
[% END %]
+ [% IF group.ft_acquisitions %]
+ Use in the acquisitions module
+ [% END %]
[% END %]
@@ -486,6 +505,7 @@
data-group-ft_local_hold_group="[% group.ft_local_hold_group | html %]"
data-group-ft_limit_item_editing="[% group.ft_limit_item_editing | html %]"
data-group-ft_local_float_group="[% group.ft_local_float_group | html %]"
+ data-group-ft_acquisitions="[% group.ft_acquisitions | html %]"
>
Edit
diff --git a/koha-tmpl/intranet-tmpl/prog/js/fetch/acquisition-api-client.js b/koha-tmpl/intranet-tmpl/prog/js/fetch/acquisition-api-client.js
index f0f1a482df4..944a8ce3a86 100644
--- a/koha-tmpl/intranet-tmpl/prog/js/fetch/acquisition-api-client.js
+++ b/koha-tmpl/intranet-tmpl/prog/js/fetch/acquisition-api-client.js
@@ -21,7 +21,7 @@ export class AcquisitionAPIClient {
endpoint: "vendors/" + id,
headers: {
"x-koha-embed":
- "aliases,subscriptions+count,interfaces,contacts,contracts,baskets+count,invoices+count,extended_attributes,+strings",
+ "aliases,subscriptions+count,interfaces,contacts,contracts,baskets+count,invoices+count,extended_attributes,+strings,lib_group_limits",
},
}),
getAll: (query, params) =>
diff --git a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Vendors/Main.vue b/koha-tmpl/intranet-tmpl/prog/js/vue/components/Vendors/Main.vue
index e3b36a641a5..8ed5f1d5197 100644
--- a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Vendors/Main.vue
+++ b/koha-tmpl/intranet-tmpl/prog/js/vue/components/Vendors/Main.vue
@@ -37,8 +37,15 @@ import { APIClient } from "../../fetch/api-client.js";
export default {
setup() {
const vendorStore = inject("vendorStore");
- const { config, authorisedValues } = storeToRefs(vendorStore);
- const { loadAuthorisedValues } = vendorStore;
+ const {
+ config,
+ authorisedValues,
+ user,
+ currencies,
+ libraryGroups,
+ gstValues,
+ } = storeToRefs(vendorStore);
+ const { loadAuthorisedValues, setLibraryGroups } = vendorStore;
const mainStore = inject("mainStore");
const { loading, loaded, setError } = mainStore;
@@ -55,6 +62,11 @@ export default {
config,
loadAuthorisedValues,
authorisedValues,
+ user,
+ currencies,
+ libraryGroups,
+ gstValues,
+ setLibraryGroups,
};
},
beforeCreate() {
@@ -65,19 +77,29 @@ export default {
const client = APIClient.acquisition;
client.config.get("vendors").then(config => {
this.userPermissions = config.permissions;
+ const libraryClient = APIClient.libraries;
this.config.settings.edifact = config.edifact;
this.config.settings.ermModule = config.erm_module;
this.config.settings.marcOrderAutomation =
config.marcOrderAutomation;
this.vendorStore.currencies = config.currencies;
+ this.user.loggedInUser = config.logged_in_user;
+ this.user.loggedInUser.loggedInBranch =
+ config.logged_in_branch;
this.vendorStore.gstValues = config.gst_values.map(gv => {
return {
label: `${Number(gv.option * 100).format_price()}%`,
value: gv.option,
};
});
- this.loaded();
- this.initialized = true;
+ libraryClient.libraryGroups.getAll().then(
+ libraryGroups => {
+ this.setLibraryGroups(libraryGroups);
+ this.loaded();
+ this.initialized = true;
+ },
+ error => {}
+ );
});
}
);
diff --git a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Vendors/VendorDetails.vue b/koha-tmpl/intranet-tmpl/prog/js/vue/components/Vendors/VendorDetails.vue
index dcfc8f7482c..44993035127 100644
--- a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Vendors/VendorDetails.vue
+++ b/koha-tmpl/intranet-tmpl/prog/js/vue/components/Vendors/VendorDetails.vue
@@ -66,6 +66,30 @@
{{ get_lib_from_av("av_lang", vendor.language) }}
+
+
+
+
+
+ {{ $__("ID") }} |
+ {{ $__("Title") }} |
+
+
+
+
+ {{ libGroup.id }} |
+ {{ libGroup.title }} |
+
+
+
+
+
+
+ {{ $__("All library groups") }}
+
+
+
+
+
import { inject } from "vue";
import Link from "../Link.vue";
+import { storeToRefs } from "pinia";
export default {
props: {
@@ -201,10 +239,14 @@ export default {
},
setup() {
const vendorStore = inject("vendorStore");
- const { get_lib_from_av, authorisedValues } = vendorStore;
+ const { get_lib_from_av, authorisedValues, formatLibraryGroupIds } =
+ vendorStore;
+ const { getVisibleGroups } = storeToRefs(vendorStore);
return {
get_lib_from_av,
authorisedValues,
+ getVisibleGroups,
+ formatLibraryGroupIds,
};
},
data() {
diff --git a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Vendors/VendorFormAdd.vue b/koha-tmpl/intranet-tmpl/prog/js/vue/components/Vendors/VendorFormAdd.vue
index a67be6e09c5..ad3dbc7c370 100644
--- a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Vendors/VendorFormAdd.vue
+++ b/koha-tmpl/intranet-tmpl/prog/js/vue/components/Vendors/VendorFormAdd.vue
@@ -43,8 +43,17 @@ import VendorContacts from "./VendorContacts.vue";
import VendorOrderingInformation from "./VendorOrderingInformation.vue";
import VendorInterfaces from "./VendorInterfaces.vue";
import AdditionalFieldsEntry from "../AdditionalFieldsEntry.vue";
+import { inject } from "vue";
export default {
+ setup() {
+ const vendorStore = inject("vendorStore");
+ const { formatLibraryGroupIds } = vendorStore;
+
+ return {
+ formatLibraryGroupIds,
+ };
+ },
data() {
return {
vendor: {
@@ -72,6 +81,8 @@ export default {
fax: "",
external_id: "",
payment_method: [],
+ language: "",
+ lib_group_visibility: [],
aliases: [],
contacts: [],
interfaces: [],
@@ -114,6 +125,8 @@ export default {
this.vendor.discount = this.vendor.discount.toFixed(1);
}
+ this.vendor.lib_group_visibility =
+ this.formatLibraryGroupIds(vendor.lib_group_visibility);
this.initialized = true;
},
error => {}
@@ -144,6 +157,7 @@ export default {
delete vendor.contracts;
delete vendor.invoices_count;
delete vendor._strings;
+ delete vendor.lib_group_limits;
if (!this.discountValid)
errors.push(this.$__("Invalid discount value"));
@@ -173,6 +187,9 @@ export default {
vendor.payment_method = null;
}
+ const visibility = vendor.lib_group_visibility.join("|");
+ vendor.lib_group_visibility = visibility;
+
const client = APIClient.acquisition;
if (vendorId) {
client.vendors.update(vendor, vendorId).then(
diff --git a/koha-tmpl/intranet-tmpl/prog/js/vue/stores/vendors.js b/koha-tmpl/intranet-tmpl/prog/js/vue/stores/vendors.js
index 8c96e63df93..862fb290924 100644
--- a/koha-tmpl/intranet-tmpl/prog/js/vue/stores/vendors.js
+++ b/koha-tmpl/intranet-tmpl/prog/js/vue/stores/vendors.js
@@ -1,5 +1,5 @@
import { defineStore } from "pinia";
-import { reactive, toRefs } from "vue";
+import { reactive, toRefs, computed } from "vue";
import { withAuthorisedValueActions } from "../composables/authorisedValues";
export const useVendorStore = defineStore("vendors", () => {
@@ -7,6 +7,12 @@ export const useVendorStore = defineStore("vendors", () => {
vendors: [],
currencies: [],
gstValues: [],
+ libraryGroups: null,
+ visibleGroups: null,
+ user: {
+ loggedInUser: null,
+ userflags: null,
+ },
config: {
settings: {
edifact: false,
@@ -17,13 +23,148 @@ export const useVendorStore = defineStore("vendors", () => {
av_vendor_types: "VENDOR_TYPE",
av_vendor_interface_types: "VENDOR_INTERFACE_TYPE",
av_vendor_payment_methods: "VENDOR_PAYMENT_METHOD",
- av_lang: "LANG"
+ av_lang: "LANG",
},
});
- const sharedActions = withAuthorisedValueActions(store);
+ const actions = {
+ ...withAuthorisedValueActions(store),
+ determineBranch(code) {
+ if (code) {
+ return code;
+ }
+ const {
+ loggedInUser: { loggedInBranch, branchcode },
+ } = store.user;
+ return loggedInBranch ? loggedInBranch : branchcode;
+ },
+ _matchSubGroups(group, filteredGroups, branch, groupsToCheck) {
+ let matched = false;
+ if (group.libraries.find(lib => lib.branchcode === branch)) {
+ if (
+ groupsToCheck &&
+ groupsToCheck.length &&
+ groupsToCheck.includes(group.id)
+ ) {
+ filteredGroups[group.id] = group;
+ matched = true;
+ }
+ if (!groupsToCheck || groupsToCheck.length === 0) {
+ filteredGroups[group.id] = group;
+ matched = true;
+ }
+ }
+ if (group.subGroups && group.subGroups.length) {
+ group.subGroups.forEach(grp => {
+ const result = this._matchSubGroups(
+ grp,
+ filteredGroups,
+ branch,
+ groupsToCheck
+ );
+ matched = matched ? matched : result;
+ });
+ }
+ return matched;
+ },
+ filterLibGroupsByUsersBranchcode(branchcode, groupsToCheck) {
+ const branch = this.determineBranch(branchcode);
+ const filteredGroups = {};
+ if (!store.libraryGroups) {
+ return [];
+ }
+ store.libraryGroups.forEach(group => {
+ const matched = this._matchSubGroups(
+ group,
+ filteredGroups,
+ branch,
+ groupsToCheck
+ );
+ // If a sub group has been matched but the parent level group did not, then we should add the parent level group as well
+ // This happens when a parent group doesn't have any branchcodes assigned to it, only sub groups
+ if (
+ matched &&
+ !Object.keys(filteredGroups).find(id => id === group.id)
+ ) {
+ filteredGroups[group.id] = group;
+ }
+ });
+ return Object.keys(filteredGroups)
+ .map(key => {
+ return filteredGroups[key];
+ })
+ .sort((a, b) => a.id - b.id);
+ },
+ formatLibraryGroupIds(ids) {
+ if (!ids) {
+ return [];
+ }
+ const groups = ids.includes("|") ? ids.split("|") : [ids];
+ const groupIds = groups.map(group => {
+ return parseInt(group);
+ });
+ return groupIds;
+ },
+ setLibraryGroups(groups) {
+ if (!groups?.length) {
+ return;
+ }
+ const topLevelGroups = groups.filter(
+ group => !group.parent_id && group.ft_acquisitions
+ );
+ if (!topLevelGroups?.length) {
+ return;
+ }
+ store.libraryGroups = topLevelGroups.map(group => {
+ return this._mapLibraryGroup(group, groups);
+ });
+ },
+ _mapLibraryGroup(group, groups) {
+ const groupInfo = {
+ id: group.id,
+ title: group.title,
+ libraries: [],
+ subGroups: [],
+ };
+ const libsOrSubGroups = groups.filter(
+ grp => grp.parent_id == group.id
+ );
+ libsOrSubGroups.forEach(libOrSubGroup => {
+ if (libOrSubGroup.branchcode) {
+ groupInfo.libraries.push(libOrSubGroup);
+ } else {
+ const subGroupInfo = this._mapLibraryGroup(
+ libOrSubGroup,
+ groups,
+ true
+ );
+ groupInfo.subGroups.push(subGroupInfo);
+ }
+ });
+ groupInfo.subGroups.forEach(subGroup => {
+ subGroup.libraries.forEach(lib => {
+ if (
+ !groupInfo.libraries.find(
+ g => g.branchcode === lib.branchcode
+ )
+ ) {
+ groupInfo.libraries.push(lib);
+ }
+ });
+ });
+ return groupInfo;
+ },
+ };
+ const getters = {
+ getVisibleGroups: computed(() => {
+ return store.visibleGroups?.length
+ ? store.visibleGroups
+ : actions.filterLibGroupsByUsersBranchcode();
+ }),
+ };
return {
...toRefs(store),
- ...sharedActions,
+ ...actions,
+ ...getters,
};
});
--
2.48.1