# along with Koha; if not, see <http://www.gnu.org/licenses>.
use Modern::Perl;
-
+use DateTime;
use Koha::Database;
+use Koha::DateUtils qw( dt_from_string );
use Koha::ERM::Agreement;
+use Koha::ERM::Agreement::Periods;
use base qw(Koha::Objects);
=cut
+=head3 filter_by_expired
+
+=cut
+
+sub filter_by_expired {
+ my ($self, $max_expiration_date) = @_;
+
+ $max_expiration_date =
+ $max_expiration_date
+ ? dt_from_string( $max_expiration_date, 'iso' )
+ : dt_from_string;
+
+ my $periods = Koha::ERM::Agreement::Periods->search(
+ { agreement_id => [ $self->get_column('agreement_id') ] },
+ {
+ select => [
+ 'agreement_id',
+ \do {"CASE WHEN MAX(me.ended_on IS NULL) = 0 THEN max(me.ended_on) END"}
+ ],
+ as => [ 'agreement_id', 'max_ended_on' ],
+ group_by => ['agreement_id'],
+ }
+ );
+
+ my @expired_agreement_ids;
+ while ( my $p = $periods->next ) {
+ # FIXME Can this be moved in the HAVING clause of the previous query?
+ my $max_ended_on = $p->get_column('max_ended_on');
+ next unless $max_ended_on;
+ my $max_ended_on_dt = dt_from_string($max_ended_on);
+ next if DateTime->compare( $max_ended_on_dt, $max_expiration_date ) == 1;
+ push @expired_agreement_ids, $p->agreement_id;
+ }
+
+ return $self->search( { agreement_id => \@expired_agreement_ids } );
+}
+
=head3 type
=cut
sub list {
my $c = shift->openapi->valid_input or return;
+ my $max_expiration_date = delete $c->validation->output->{max_expiration_date};
+
return try {
my $agreements_set = Koha::ERM::Agreements->new;
+ if ( $max_expiration_date ) {
+ $agreements_set = $agreements_set->filter_by_expired( $max_expiration_date );
+ }
my $agreements = $c->objects->search( $agreements_set );
return $c->render( status => 200, openapi => $agreements );
}
name: license_info
required: false
type: string
+ - description: filter by expired agreements
+ in: query
+ name: max_expiration_date
+ type: string
+ format: date
- $ref: "../swagger.yaml#/parameters/match"
- $ref: "../swagger.yaml#/parameters/order_by"
- $ref: "../swagger.yaml#/parameters/page"
<div v-if="!this.initialized">{{ $t("Loading") }}</div>
<div v-else-if="this.agreements" id="agreements_list">
<Toolbar v-if="before_route_entered" />
+ <fieldset v-if="this.agreements.length">
+ <label for="expired_filter">{{ $t("Filter by expired") }}:</label>
+ <input
+ type="checkbox"
+ id="expired_filter"
+ v-model="filters.by_expired"
+ @keyup.enter="filter_table"
+ />
+ {{ $t("on") }}
+ <flat-pickr
+ id="max_expiration_date_filter"
+ v-model="filters.max_expiration_date"
+ :config="fp_config"
+ />
+
+ <input
+ @click="filter_table"
+ id="filter_table"
+ type="button"
+ :value="$t('Filter')"
+ />
+ </fieldset>
<table v-if="this.agreements.length" :id="table_id"></table>
<div v-else-if="this.initialized" class="dialog message">
{{ $t("There are no agreements defined") }}
</template>
<script>
+import flatPickr from 'vue-flatpickr-component'
import Toolbar from "./AgreementsToolbar.vue"
import { createVNode, render } from 'vue'
import { useVendorStore } from "../../stores/vendors"
import { useAVStore } from "../../stores/authorised_values"
import { storeToRefs } from "pinia"
import { fetchAgreements } from "../../fetch"
-import { useDataTable } from "../../composables/datatables"
+import { useDataTable, build_url } from "../../composables/datatables"
export default {
setup() {
},
data: function () {
return {
+ fp_config: flatpickr_defaults, dates_fixed: 0,
agreements: [],
initialized: false,
+ filters: {
+ by_expired: this.$route.query.by_expired || false,
+ max_expiration_date: this.$route.query.max_expiration_date || "",
+ },
before_route_entered: false,
building_table: false,
}
}
})
},
+ computed: {
+ datatable_url() {
+ let url = '/api/v1/erm/agreements'
+ if (this.filters.by_expired)
+ url += '?max_expiration_date=' + $date_to_rfc3339(this.filters.max_expiration_date)
+ return url
+ }
+ },
methods: {
async getAgreements() {
const agreements = await fetchAgreements()
this.$emit('select-agreement', agreement_id)
this.$emit('close')
},
+ filter_table: async function () {
+ let new_route = build_url("/cgi-bin/koha/erm/agreements", this.filters)
+ this.$router.push(new_route)
+ if (this.filters.by_expired) {
+ if (!this.filters.max_expiration_date)
+ this.filters.max_expiration_date = new Date()
+ }
+ $('#' + this.table_id).DataTable().ajax.url(this.datatable_url).draw()
+ },
+ table_url: function () {
+
+ },
build_datatable: function () {
let show_agreement = this.show_agreement
let edit_agreement = this.edit_agreement
let select_agreement = this.select_agreement
let get_lib_from_av = this.get_lib_from_av
let map_av_dt_filter = this.map_av_dt_filter
+ let datatable_url = this.datatable_url
let default_search = this.$route.query.q
let actions = this.before_route_entered ? 'edit_delete' : 'select'
let table_id = this.table_id
const table = $("#" + table_id).kohaTable({
ajax: {
- url: "/api/v1/erm/agreements",
+ url: datatable_url
},
order: [[0, "asc"]],
autoWidth: false,
this.getAgreements().then(() => this.build_datatable())
}
},
- components: { Toolbar },
+ components: { flatPickr, Toolbar },
name: "AgreementsList",
emits: ["select-agreement", "close"],
}
import { useAVStore } from "../../stores/authorised_values"
import { storeToRefs } from "pinia"
import { fetchCountLocalPackages } from './../../fetch'
-import { useDataTable } from "../../composables/datatables"
+import { useDataTable, build_url_params, build_url } from "../../composables/datatables"
export default {
setup() {
}
},
computed: {
- local_packages_url() { return this.build_url("/cgi-bin/koha/erm/eholdings/local/packages") },
+ local_packages_url() { return build_url("/cgi-bin/koha/erm/eholdings/local/packages", this.filters) },
},
beforeRouteEnter(to, from, next) {
next(vm => {
show_package: function (package_id) {
this.$router.push("/cgi-bin/koha/erm/eholdings/ebsco/packages/" + package_id)
},
- build_url_params: function () {
- return Object.entries(this.filters)
- .map(([k, v]) => v ? k + "=" + v : undefined)
- .filter(e => e !== undefined)
- .join('&')
- },
- build_url: function (base_url) {
- let params = this.build_url_params()
- return base_url + (params.length ? '?' + params : '')
- },
filter_table: async function () {
- let new_route = this.build_url("/cgi-bin/koha/erm/eholdings/ebsco/packages")
+ let new_route = build_url("/cgi-bin/koha/erm/eholdings/ebsco/packages", this.filters)
this.$router.push(new_route)
this.show_table = true
this.local_count_packages = null
let map_av_dt_filter = this.map_av_dt_filter
if (!this.show_table) {
- this.show_table = this.build_url_params().length ? true : false
+ this.show_table = build_url_params(this.filters).length ? true : false
}
let filters = this.filters
let show_table = this.show_table
import { useAVStore } from "../../stores/authorised_values"
import { storeToRefs } from "pinia"
import { fetchCountLocalTitles } from "./../../fetch"
-import { useDataTable } from "../../composables/datatables"
+import { useDataTable, build_url_params, build_url } from "../../composables/datatables"
export default {
setup() {
}
},
computed: {
- local_titles_url() { return this.build_url("/cgi-bin/koha/erm/eholdings/local/titles") },
+ local_titles_url() { return build_url("/cgi-bin/koha/erm/eholdings/local/titles", this.filters) },
},
beforeRouteEnter(to, from, next) {
next(vm => {
show_title: function (title_id) {
this.$router.push("/cgi-bin/koha/erm/eholdings/ebsco/titles/" + title_id)
},
- build_url_params: function () {
- return Object.entries(this.filters)
- .map(([k, v]) => v ? k + "=" + v : undefined)
- .filter(e => e !== undefined)
- .join('&')
- },
- build_url: function (base_url) {
- let params = this.build_url_params()
- return base_url + (params.length ? '?' + params : '')
- },
filter_table: async function () {
if (this.filters.publication_title.length) {
this.cannot_search = false
- let new_route = this.build_url("/cgi-bin/koha/erm/eholdings/ebsco/titles")
+ let new_route = build_url("/cgi-bin/koha/erm/eholdings/ebsco/titles", this.filters)
this.$router.push(new_route)
this.show_table = true
this.local_count_titles = null
let show_title = this.show_title
let get_lib_from_av = this.get_lib_from_av
if (!this.show_table) {
- this.show_table = this.build_url_params().length ? true : false
+ this.show_table = build_url_params(this.filters).length ? true : false
}
let filters = this.filters
let table_id = this.table_id
width: 30%;
}
-.v-select, input:not([type=submit]):not([type=search]):not([type=button]), textarea {
+.v-select, input:not([type=submit]):not([type=search]):not([type=button]):not([type=checkbox]), textarea {
border-color: rgba(60,60,60,0.26);
border-width: 1px;
border-radius: 4px;
}
});
}
+
+export function build_url_params(filters) {
+ return Object.entries(filters)
+ .map(([k, v]) => (v ? k + "=" + v : undefined))
+ .filter((e) => e !== undefined)
+ .join("&");
+}
+export function build_url(base_url, filters) {
+ let params = build_url_params(filters);
+ return base_url + (params.length ? "?" + params : "");
+}