Skip to content

Commit

Permalink
Release v4.0.2 (#1407)
Browse files Browse the repository at this point in the history
* UIU-1715: Change limits
* UIU-1722: Add limits
* UIU-1747: Generate overdue loans report via pagination.
* UIU-1675: Change validation messages

* Release v4.0.2

Co-authored-by: annamelnyk <[email protected]>
Co-authored-by: Michał Kuklis <[email protected]>
  • Loading branch information
3 people authored Jul 7, 2020
1 parent 801da3a commit ea1c9ef
Show file tree
Hide file tree
Showing 13 changed files with 313 additions and 60 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# Change history for ui-users

## [4.0.2](https://github.com/folio-org/ui-users/tree/v4.0.2) (2020-07-07)
[Full Changelog](https://github.com/folio-org/ui-users/compare/v4.0.1...v4.0.2)

* Retrieve up to 200 patron groups when setting Fee/Fine limits. Refs UIU-1715.
* Increase limits for `ChargeFeesFines`. Refs UIU-1722.
* Generate overdue loans report via pagination. Fixes UIU-1747.
* Add `delete` request to patron block limits. Refs UIU-1675.
* Add validation to integer values for patron block limits. Refs UIU-1675.

## [4.0.1](https://github.com/folio-org/ui-users/tree/v4.0.1) (2020-06-18)
[Full Changelog](https://github.com/folio-org/ui-users/compare/v4.0.0...v4.0.1)

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@folio/users",
"version": "4.0.1",
"version": "4.0.2",
"description": "User management",
"repository": "folio-org/ui-users",
"publishConfig": {
Expand Down
34 changes: 34 additions & 0 deletions src/components/data/reports/OverdueLoanReport.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { get } from 'lodash';
import { exportCsv } from '@folio/stripes/util';
import moment from 'moment';

const columns = [
'borrower.name',
Expand Down Expand Up @@ -37,6 +38,39 @@ class OverdueLoanReport {
}));
}

async fetchData(mutator) {
const { GET, reset } = mutator;
const overDueDate = moment().tz('UTC').format();
const query = `(status.name=="Open" and dueDate < "${overDueDate}") sortby metadata.updatedDate desc`;
const limit = 1000;
const data = [];

let offset = 0;
let hasData = true;

while (hasData) {
try {
reset();
// eslint-disable-next-line no-await-in-loop
const result = await GET({ params: { query, limit, offset } });
hasData = result.length;
offset += limit;
if (hasData) {
data.push(...result);
}
} catch (err) {
hasData = false;
}
}

return data;
}

async generate(mutator) {
const loans = await this.fetchData(mutator);
this.toCSV(loans);
}

parse(records) {
return records.map(r => ({
...r,
Expand Down
2 changes: 1 addition & 1 deletion src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export const statusFilter = [
];

/* With current id determines that this is fee/fine condition,
because this condition is validating with different message,
because this conditions are validating with different message,
and condition fields are rendering dinamically.
All conditions (thare are 6 of them) are always present on BE
with hardcoded ids for now. */
Expand Down
15 changes: 8 additions & 7 deletions src/routes/ChargeFeesFinesContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
withStripes,
} from '@folio/stripes/core';

import { MAX_RECORDS } from '../constants';
import { ChargeFeeFine } from '../components/Accounts';

class ChargeFeesFinesContainer extends React.Component {
Expand Down Expand Up @@ -48,7 +49,7 @@ class ChargeFeesFinesContainer extends React.Component {
},
curUserServicePoint: {
type: 'okapi',
path: 'service-points-users?query=(userId==:{id})',
path: `service-points-users?query=(userId==:{id})&limit=${MAX_RECORDS}`,
records: 'servicePointsUsers',
},
items: {
Expand All @@ -68,13 +69,13 @@ class ChargeFeesFinesContainer extends React.Component {
type: 'okapi',
records: 'feefines',
GET: {
path: 'feefines?query=(ownerId=%{activeRecord.ownerId} or ownerId=%{activeRecord.shared})&limit=10000',
path: `feefines?query=(ownerId==%{activeRecord.ownerId} or ownerId==%{activeRecord.shared})&limit=${MAX_RECORDS}`,
},
},
feefineactions: {
type: 'okapi',
records: 'feefineactions',
path: 'feefineactions?limit=10000',
path: `feefineactions?limit=${MAX_RECORDS}`,
},
accounts: {
type: 'okapi',
Expand All @@ -88,7 +89,7 @@ class ChargeFeesFinesContainer extends React.Component {
type: 'okapi',
resource: 'accounts',
accumulate: 'true',
path: 'accounts',
path: `accounts?limit=${MAX_RECORDS}`,
},
owners: {
type: 'okapi',
Expand All @@ -98,17 +99,17 @@ class ChargeFeesFinesContainer extends React.Component {
payments: {
type: 'okapi',
records: 'payments',
path: 'payments',
path: `payments?limit=${MAX_RECORDS}`,
},
commentRequired: {
type: 'okapi',
records: 'comments',
path: 'comments',
path: `comments?limit=${MAX_RECORDS}`,
},
allfeefines: {
type: 'okapi',
records: 'feefines',
path: 'feefines?limit=10000',
path: `feefines?limit=${MAX_RECORDS}`,
},
activeRecord: {},
});
Expand Down
6 changes: 1 addition & 5 deletions src/routes/UserSearchContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {
get,
template,
} from 'lodash';
import moment from 'moment';
import { stripesConnect } from '@folio/stripes/core';

import {
Expand All @@ -18,15 +17,12 @@ import { UserSearch } from '../views';

const INITIAL_RESULT_COUNT = 30;
const RESULT_COUNT_INCREMENT = 30;
const MAX_LIMIT = 2147483647; // from https://s3.amazonaws.com/foliodocs/api/mod-circulation/p/circulation.html#circulation_loans_get

const compileQuery = template(
'(username="%{query}*" or personal.firstName="%{query}*" or personal.lastName="%{query}*" or personal.email="%{query}*" or barcode="%{query}*" or id="%{query}*" or externalSystemId="%{query}*")',
{ interpolate: /%{([\s\S]+?)}/g }
);

const getLoansOverdueDate = () => moment().tz('UTC').format();

class UserSearchContainer extends React.Component {
static manifest = Object.freeze({
initializedFilterConfig: { initialValue: false },
Expand Down Expand Up @@ -73,7 +69,7 @@ class UserSearchContainer extends React.Component {
type: 'okapi',
records: 'loans',
accumulate: true,
path: () => `circulation/loans?query=(status="Open" and dueDate < ${getLoansOverdueDate()})&limit=${MAX_LIMIT}`,
path: () => 'circulation/loans',
permissionsRequired: 'circulation.loans.collection.get,accounts.collection.get',
},
tags: {
Expand Down
14 changes: 12 additions & 2 deletions src/settings/LimitsSettings.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ import { Settings } from '@folio/stripes/smart-components';

import Limits from './patronBlocks/Limits/Limits';

// Only 6 patron block conditions exist. If patron groups or
// patron block conditions limit change, then LIMITS_RECORDS must be changed as well.
// For now we have: 6 * 200 = 1200.
const LIMITS_RECORDS = '1200';

class LimitsSettings extends Component {
static manifest = Object.freeze({
query: {},
Expand All @@ -26,6 +31,7 @@ class LimitsSettings extends Component {
path: 'groups',
params: {
query: 'cql.allRecords=1 sortby group',
limit: '200',
},
},
patronBlockCondition: {
Expand All @@ -42,8 +48,12 @@ class LimitsSettings extends Component {
type: 'okapi',
records: 'patronBlockLimits',
GET: {
path: 'patron-block-limits?limit=1500',
}
path: 'patron-block-limits',
},
params: {
query: 'cql.allRecords=1',
limit: LIMITS_RECORDS,
},
},
});

Expand Down
89 changes: 69 additions & 20 deletions src/settings/patronBlocks/Limits/Limits.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@ import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';
import {
forIn,
isNumber,
keysIn,
difference,
without,
isEmpty,
concat,
} from 'lodash';

import { stripesConnect } from '@folio/stripes/core';
Expand All @@ -28,6 +32,9 @@ class Limits extends Component {
PUT: {
path: 'patron-block-limits',
},
DELETE: {
path: 'patron-block-limits',
},
},
});

Expand All @@ -40,6 +47,7 @@ class Limits extends Component {
GET: PropTypes.func.isRequired,
POST: PropTypes.func.isRequired,
PUT: PropTypes.func.isRequired,
DELETE: PropTypes.func.isRequired,
}).isRequired,
patronBlockLimitId: PropTypes.object.isRequired,
}).isRequired,
Expand Down Expand Up @@ -67,6 +75,18 @@ class Limits extends Component {
this._isMounted = true;
}

componentDidUpdate(prevProps) {
const { patronBlockLimits: prevPatronBlockLimits } = prevProps;
const { patronBlockLimits } = this.props;
const differenceInPrevLimits = difference(prevPatronBlockLimits, patronBlockLimits);
const differenceInCurrentLimits = difference(patronBlockLimits, prevPatronBlockLimits);
const differenceInLimits = [...differenceInPrevLimits, ...differenceInCurrentLimits];

if (!isEmpty(differenceInLimits)) {
this.getCurrentPatronGroupLimits();
}
}

componentWillUnmount() {
this._isMounted = false;
}
Expand Down Expand Up @@ -122,32 +142,61 @@ class Limits extends Component {
mutator,
patronGroupId: currentPatronGroupId,
} = this.props;
const initialValues = this.getInitialValues();
const existedLimits = keysIn(initialValues);
const receivedFromFormLimits = keysIn(value);
const limitsToRemove = difference(existedLimits, receivedFromFormLimits);
const limitsToCreate = difference(receivedFromFormLimits, existedLimits);
const notUpdatedLimits = concat(limitsToCreate, limitsToCreate);
const limitsToUpdate = without(receivedFromFormLimits, ...notUpdatedLimits);
const promises = [];
let limitPromise;

forIn(value, (limitValue, blockConditionId) => {
const foundLimit = this.findPatronGroupLimit(blockConditionId, currentPatronGroupId);
if (!isEmpty(limitsToRemove)) {
limitsToRemove.forEach((conditionId) => {
const foundLimit = this.findPatronGroupLimit(conditionId, currentPatronGroupId);

if (foundLimit?.value === limitValue) return;
limitPromise = mutator.patronBlockLimits.DELETE({ id: foundLimit.id });

const limitValueNumber = parseFloat(limitValue);
promises.push(limitPromise);
});
}

if (foundLimit) {
mutator.patronBlockLimitId.update(foundLimit.id);
limitPromise = mutator.patronBlockLimits.PUT({
...foundLimit,
value: this.normializeValue(limitValueNumber),
});
} else {
limitPromise = mutator.patronBlockLimits.POST({
patronGroupId: currentPatronGroupId,
conditionId: blockConditionId,
value: this.normializeValue(limitValueNumber),
});
}
if (!isEmpty(limitsToCreate)) {
limitsToCreate.forEach((conditionId) => {
const foundLimit = this.findPatronGroupLimit(conditionId, currentPatronGroupId);

promises.push(limitPromise);
});
if (!foundLimit) {
const receivedValue = parseFloat(value[conditionId]);

limitPromise = mutator.patronBlockLimits.POST({
patronGroupId: currentPatronGroupId,
conditionId,
value: this.normializeValue(receivedValue),
});

promises.push(limitPromise);
}
});
}

if (!isEmpty(limitsToUpdate)) {
limitsToUpdate.forEach((conditionId) => {
const foundLimit = this.findPatronGroupLimit(conditionId, currentPatronGroupId);
const receivedValue = parseFloat(value[conditionId]);

if (foundLimit && (foundLimit.value !== receivedValue)) {
limitPromise = mutator.patronBlockLimits.PUT({
id: foundLimit.id,
patronGroupId: foundLimit.patronGroupId,
conditionId: foundLimit.conditionId,
value: this.normializeValue(receivedValue),
});

promises.push(limitPromise);
}
});
}

return promises;
}
Expand Down
30 changes: 21 additions & 9 deletions src/settings/patronBlocks/Limits/LimitsForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,24 +22,36 @@ import { feeFineBalanceId } from '../../../constants';

import css from '../patronBlocks.css';

function validation(value, type) {
function isInteger(value) {
return value % 1 === 0;
}

function validation(value, min, max) {
const numberValue = toNumber(value);
const min = 0.01;
const max = 9999.99;
const errorMessage = (
<FormattedMessage
id="ui-users.settings.limits.validation.message"
values={{ min, max }}
/>
);

if (numberValue < min || numberValue > max) {
return <FormattedMessage id={`ui-users.settings.limits.${type}.error`} />;
return errorMessage;
}

if (isInteger(min) && !isInteger(numberValue) && !Number.isNaN(numberValue)) {
return errorMessage;
}

return null;
}

function feeFineBalanceValidation(value) {
return validation(value, 'feeFine');
function limitsValidation(value) {
return validation(value, 1, 999999);
}

function limitsValidation(value) {
return validation(value, 'validation');
function feeFineLimitsValidation(value) {
return validation(value, 0.01, 999999.99);
}

class LimitsForm extends Component {
Expand Down Expand Up @@ -83,7 +95,7 @@ class LimitsForm extends Component {
component={TextField}
type="number"
name={id}
validate={id === feeFineBalanceId ? feeFineBalanceValidation : limitsValidation}
validate={id === feeFineBalanceId ? feeFineLimitsValidation : limitsValidation}
/>
</Col>
</Row>
Expand Down
Loading

0 comments on commit ea1c9ef

Please sign in to comment.