Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

self-checkout: remove handling errors from the UI #649

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions src/lib/api/loans/loan.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { serializer } from './serializer';

const apiPaths = {
checkout: '/circulation/loans/checkout',
selfCheckout: '/circulation/loans/self-checkout',
notificationOverdue: '/circulation/loans/:loanPid/notification-overdue',
item: '/circulation/loans/:loanPid',
list: '/circulation/loans/',
Expand Down Expand Up @@ -114,6 +115,29 @@ const doCheckout = async (
return response;
};

const doSelfCheckoutSearchItem = async (barcode) => {
const response = await http.get(
`${apiPaths.selfCheckout}?barcode=${barcode}`
);
response.data = serializer.fromJSON(response.data);
return response;
};

const doSelfCheckout = async (documentPid, itemPid, patronPid) => {
const currentUser = sessionManager.user;
const payload = {
document_pid: documentPid,
item_pid: itemPid,
patron_pid: patronPid,
transaction_location_pid: `${currentUser.locationPid}`,
transaction_user_pid: `${currentUser.id}`,
};

const response = await http.post(apiPaths.selfCheckout, payload);
response.data = serializer.fromJSON(response.data);
return response;
};

const assignItemToLoan = async (itemPid, loanPid) => {
const path = generatePath(apiPaths.replaceItem, { loanPid: loanPid });
const payload = { item_pid: itemPid };
Expand Down Expand Up @@ -332,6 +356,8 @@ export const loanApi = {
doAction: doAction,
doRequest: doRequest,
doCheckout: doCheckout,
doSelfCheckout: doSelfCheckout,
doSelfCheckoutSearchItem: doSelfCheckoutSearchItem,
sendOverdueLoansNotificationReminder: sendOverdueLoansNotificationReminder,
serializer: serializer,
updateDates: updateDates,
Expand Down
6 changes: 6 additions & 0 deletions src/lib/config/defaultConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,12 @@ export const RECORDS_CONFIG = {
iconClass: 'dolly',
},
},
deliveryMethodSelfCheckout: {
'SELF-CHECKOUT': {
anikachurilova marked this conversation as resolved.
Show resolved Hide resolved
text: 'SELF-CHECKOUT',
iconClass: 'shopping basket',
},
},
extensionsMaxCount: 3,
loanWillExpireDays: 7,
loanActiveStates: [
Expand Down
16 changes: 15 additions & 1 deletion src/lib/modules/Loan/backoffice/LoanList/LoanListEntry.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export class LoanListEntry extends Component {
delivery && loan.metadata.state === 'PENDING'
? invenioConfig.CIRCULATION.deliveryMethods[delivery]
: '';
const isSelfCheckout = delivery === 'SELF-CHECKOUT';
return (
<Item>
<Item.Content>
Expand Down Expand Up @@ -79,13 +80,26 @@ export class LoanListEntry extends Component {
</List>
</Grid.Column>
<Grid.Column width={2} textAlign="center">
{deliveryMethod && (
{deliveryMethod ? (
<>
{delivery}{' '}
{deliveryMethod.iconClass && (
<Icon className={deliveryMethod.iconClass} />
)}
</>
) : (
anikachurilova marked this conversation as resolved.
Show resolved Hide resolved
isSelfCheckout && (
<>
{delivery}{' '}
<Icon
className={
invenioConfig.CIRCULATION.deliveryMethodSelfCheckout[
delivery
].iconClass
}
/>
</>
)
)}
</Grid.Column>
<Grid.Column computer={3} largeScreen={3}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ export class LoanMetadata extends Component {
const delivery = _get(_delivery, 'method');
if (delivery) {
const deliveryMethod =
invenioConfig.CIRCULATION.deliveryMethods[delivery];
invenioConfig.CIRCULATION.deliveryMethods[delivery] ||
invenioConfig.CIRCULATION.deliveryMethodSelfCheckout[delivery];

return (
<>
{deliveryMethod.text}{' '}
Expand Down
52 changes: 1 addition & 51 deletions src/lib/pages/frontsite/SelfCheckout/SelfCheckout.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@ import {
import { BarcodeScanner } from '@components/BarcodeScanner';
import { SelfCheckoutModal } from './SelfCheckoutModal';
import { ManualCheckout } from './ManualCheckout';
import { invenioConfig } from '@config';
import _isEmpty from 'lodash/isEmpty';
import _find from 'lodash/find';

class SelfCheckout extends React.Component {
constructor(props) {
Expand All @@ -41,50 +38,10 @@ class SelfCheckout extends React.Component {
this.setState({ barcode: detectedBarcode });
const { selfCheckOutSearch } = this.props;
await selfCheckOutSearch(detectedBarcode);

// open modal if item is loanable
const shouldShowModal = this.isItemLoanable(detectedBarcode);
if (shouldShowModal) {
this.toggleModal(true);
} else {
this.toggleModal(false);
}
this.toggleModal(true);
}
};

itemStatus = (item) => ({
ntarocco marked this conversation as resolved.
Show resolved Hide resolved
canCirculate: () =>
invenioConfig.ITEMS.canCirculateStatuses.includes(item.metadata.status),
isOnShelf: () => !item.metadata.circulation.state, // on shelf if circulation.state doesn't exist
});

isItemLoanable = (itemBarcode) => {
const { user, item, notifyResultMessage } = this.props;
var resultMessage = `Book with barcode ${itemBarcode} not found.`;

if (!_isEmpty(item)) {
if (this.itemStatus(item).canCirculate()) {
if (this.itemStatus(item).isOnShelf()) {
return true;
} else {
if (item.metadata.circulation.patron_pid === user.id.toString()) {
resultMessage = `You already loaned this book with barcode: ${itemBarcode}!`;
} else {
resultMessage = `Book with barcode: ${itemBarcode} is currently on loan!`;
}
}
} else {
const status = _find(invenioConfig.ITEMS.statuses, {
value: item.metadata?.status,
});
resultMessage = `Book with barcode: ${itemBarcode} is ${status?.text}!`;
}
}

notifyResultMessage(resultMessage);
return false;
};

renderInstructions = () => {
return (
<>
Expand Down Expand Up @@ -169,13 +126,6 @@ class SelfCheckout extends React.Component {
SelfCheckout.propTypes = {
/* REDUX */
selfCheckOutSearch: PropTypes.func.isRequired,
user: PropTypes.object.isRequired,
item: PropTypes.object,
notifyResultMessage: PropTypes.func.isRequired,
};

SelfCheckout.defaultProps = {
item: null,
};

export default Overridable.component('SelfCheckout', SelfCheckout);
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,13 @@ export default class SelfCheckoutModal extends React.Component {
onClose={() => toggleModal(false)}
>
<Modal.Header>
{`You are about to checkout a book with barcode:
{`You are about to checkout the literature with barcode:
${item?.metadata.barcode}`}
</Modal.Header>
<Modal.Content>
<DocumentCard item={item} />
<ManualCheckout
label="Wrong book?"
label="Wrong literature?"
autofocus
show
onBarcodeInput={onBarcodeDetected}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { connect } from 'react-redux';

import SelfCheckoutModalComponent from './SelfCheckoutModal';
import { checkoutItem } from '../state/actions';
import { selfCheckOut } from '../state/actions';

const mapDispatchToProps = (dispatch) => ({
checkoutItem: (documentPid, itemPid, patronPid) =>
dispatch(checkoutItem(documentPid, itemPid, patronPid)),
dispatch(selfCheckOut(documentPid, itemPid, patronPid)),
});

const mapStateToProps = (state) => ({
Expand Down
7 changes: 6 additions & 1 deletion src/lib/pages/frontsite/SelfCheckout/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import { connect } from 'react-redux';

import SelfCheckoutComponent from './SelfCheckout';
import { selfCheckOutSearch, notifyResultMessage } from './state/actions';
import {
selfCheckOut,
selfCheckOutSearch,
notifyResultMessage,
} from './state/actions';

const mapDispatchToProps = (dispatch) => ({
selfCheckOutSearch: (term) => dispatch(selfCheckOutSearch(term)),
selfCheckOut: (term) => dispatch(selfCheckOut(term)),
notifyResultMessage: (message) => dispatch(notifyResultMessage(message)),
});

Expand Down
38 changes: 12 additions & 26 deletions src/lib/pages/frontsite/SelfCheckout/state/actions.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import React from 'react';
import { Link } from 'react-router-dom';
import { itemApi } from '@api/items';
import { loanApi } from '@api/loans';
import {
sendErrorNotification,
sendSuccessNotification,
sendWarningNotification,
} from '@components/Notifications';
import _first from 'lodash/first';
import { FrontSiteRoutes } from '@routes/urls';

export const SEARCH_HAS_ERROR = 'selfCheckOut/SEARCH_HAS_ERROR';
Expand All @@ -20,27 +18,21 @@ export const notifyResultMessage = (message) => {
};
};

const searchItem = async (dispatch, term) => {
const upperCasedTerm = term.toUpperCase();
const response = await itemApi.list(
itemApi.query().withBarcode(upperCasedTerm).qs()
);
const item = _first(response.data.hits) || null;

dispatch({
type: SEARCH_ITEM_SUCCESS,
payload: item,
});
};

export const selfCheckOutSearch = (term) => {
return async (dispatch) => {
dispatch({
type: SEARCH_IS_LOADING,
});

try {
await searchItem(dispatch, term);
const upperCasedTerm = term.toUpperCase();
const response = await loanApi.doSelfCheckoutSearchItem(upperCasedTerm);
const item = response.data || null;

dispatch({
type: SEARCH_ITEM_SUCCESS,
payload: item,
});
} catch (error) {
dispatch({
type: SEARCH_HAS_ERROR,
Expand All @@ -51,20 +43,14 @@ export const selfCheckOutSearch = (term) => {
};
};

export const checkoutItem = (documentPid, itemPid, patronPid) => {
export const selfCheckOut = (documentPid, itemPid, patronPid) => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The related tests in actions.test.js are not updated (we don't use the item anymore), and they are failing.

return async (dispatch) => {
try {
const response = await loanApi.doCheckout(
documentPid,
itemPid,
patronPid
);
const { pid } = response.data.metadata;
await loanApi.doSelfCheckout(documentPid, itemPid, patronPid);
const linkToLoan = (
<p>
The loan {pid} has been created by you! You can view all your current
loans on your <Link to={FrontSiteRoutes.patronProfile}>profile</Link>{' '}
page.
Self-checkout completed! You can view all your current loans on your{' '}
<Link to={FrontSiteRoutes.patronProfile}>profile</Link> page.
</p>
);
dispatch(sendSuccessNotification('Success!', linkToLoan));
Expand Down
Loading