Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -203,13 +203,14 @@ class StockTransferApiController {

def stockTransferCandidates() {
String locationId = params?.location?.id ?: session.warehouse.id
Boolean showExpiredItemsOnly = params.boolean('showExpiredItemsOnly', false)
Location location = Location.get(locationId)

if (!location) {
throw new IllegalArgumentException("Can't find location with given id: ${locationId}")
}

List<StockTransferItem> stockTransferCandidates = stockTransferService.getStockTransferCandidates(location, null)
List<StockTransferItem> stockTransferCandidates = stockTransferService.getStockTransferCandidates(location, null, showExpiredItemsOnly)
render([data: stockTransferCandidates?.collect { it.toJson() }] as JSON)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1068,16 +1068,22 @@ class ProductAvailabilityService {
return new PaginatedList(items, products.totalCount)
}

List<ProductAvailability> getStockTransferCandidates(Location location) {
List<ProductAvailability> getStockTransferCandidates(Location location, Boolean showExpiredItemsOnly = false) {
return ProductAvailability.createCriteria().list {
eq("location", location)
gt("quantityOnHand", 0)

if (showExpiredItemsOnly) {
inventoryItem {
lt('expirationDate', new Date())
}
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do we not want an inner join? If there is no inventory item, then we should hide the row, right? We only want to see expired, and so if there's no item, it can't be expired.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

There should always be an inventory item (only bin location can be null, I think) so I think this makes sense.

}
}

List<ProductAvailability> getStockTransferCandidates(Location location, Map params) {
List<ProductAvailability> getStockTransferCandidates(Location location, Map params, Boolean showExpiredItemsOnly = false) {
if (!params) {
return getStockTransferCandidates(location)
return getStockTransferCandidates(location, showExpiredItemsOnly)
}

Location bin = params.binLocationId ? Location.get(params.binLocationId) : null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,10 @@ class StockTransferService {
return orders
}

def getStockTransferCandidates(Location location, params) {
def getStockTransferCandidates(Location location, Map params, Boolean showExpiredItemsOnly = false) {
List<StockTransferItem> stockTransferItems = []

List stockTransferCandidates = productAvailabilityService.getStockTransferCandidates(location, params)
List stockTransferCandidates = productAvailabilityService.getStockTransferCandidates(location, params, showExpiredItemsOnly)

stockTransferCandidates?.each { ProductAvailability productAvailability ->
stockTransferItems << StockTransferItem.createFromProductAvailability(productAvailability)
Expand Down
10 changes: 10 additions & 0 deletions src/js/api/services/StockTransferApi.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import queryString from 'query-string';

import {
STOCK_TRANSFER_API,
STOCK_TRANSFER_BY_ID,
STOCK_TRANSFER_CANDIDATES,
STOCK_TRANSFER_ITEM_BY_ID,
STOCK_TRANSFER_REMOVE_ALL_ITEMS,
} from 'api/urls';
Expand All @@ -13,4 +16,11 @@ export default {
deleteStockTransfer: (id) => apiClient.delete(STOCK_TRANSFER_BY_ID(id)),
removeItem: (id) => apiClient.delete(STOCK_TRANSFER_ITEM_BY_ID(id)),
removeAllItems: (id) => apiClient.delete(STOCK_TRANSFER_REMOVE_ALL_ITEMS(id)),
getStockTransferCandidates: (locationId, showExpiredItemsOnly) => {
const queryParams = queryString.stringify({
'location.id': locationId,
showExpiredItemsOnly,
});
return apiClient.get(`${STOCK_TRANSFER_CANDIDATES}?${queryParams}`);
},
};
2 changes: 1 addition & 1 deletion src/js/api/urls.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export const STOCK_MOVEMENT_ITEM_REVERT_PICK = (id) => `${STOCK_MOVEMENT_ITEM_BY
export const STOCK_TRANSFER_API = `${API}/stockTransfers`;
export const STOCK_TRANSFER_BY_ID = (id) => `${STOCK_TRANSFER_API}/${id}`;
export const STOCK_TRANSFER_REMOVE_ALL_ITEMS = (id) => `${STOCK_TRANSFER_BY_ID(id)}/removeAllItems`;
export const STOCK_TRANSFER_CANDIDATES = (id) => `/api/stockTransfers/candidates${id ? `?location.id=${id}` : ''}`;
export const STOCK_TRANSFER_CANDIDATES = `${STOCK_TRANSFER_API}/candidates`;

// STOCK TRANSFER ITEMS
export const STOCK_TRANSFER_ITEM_API = `${API}/stockTransferItems`;
Expand Down
51 changes: 39 additions & 12 deletions src/js/components/stock-transfer/CreateStockTransfer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
hideSpinner,
showSpinner,
} from 'actions';
import { STOCK_TRANSFER_CANDIDATES } from 'api/urls';
import stockTransferApi from 'api/services/StockTransferApi';
import { TableCell } from 'components/DataTable';
import { STOCK_TRANSFER_URL } from 'consts/applicationUrls';
import DateFormat from 'consts/dateFormat';
Expand Down Expand Up @@ -55,6 +55,7 @@ class CreateStockTransfer extends Component {
selection: new Set(),
selectAll: false,
selectType: 'checkbox',
showExpiredItemsOnly: false,
};
}

Expand Down Expand Up @@ -155,13 +156,23 @@ class CreateStockTransfer extends Component {

dataFetched = false;

switchExpiredItemsFiltering() {
this.props.showSpinner();
this.setState((state) => ({
...state,
showExpiredItemsOnly: !state.showExpiredItemsOnly,
}), async () => {
await this.fetchStockTransferCandidates();
});
}

/**
* Fetches available items to stock transfer from API.
* @public
*/
fetchStockTransferCandidates(locationId) {
this.props.showSpinner();
return apiClient.get(STOCK_TRANSFER_CANDIDATES(locationId))
return stockTransferApi.getStockTransferCandidates(locationId, this.state.showExpiredItemsOnly)
.then((resp) => {
const stockTransferCandidates = parseResponse(resp.data.data);
const stockTransferItems = [];
Expand Down Expand Up @@ -296,22 +307,35 @@ class CreateStockTransfer extends Component {
{' '}
<Translate id="react.stockTransfer.selected.label" defaultMessage="Selected" />
</div>
<button
type="button"
disabled={this.state.selection.size < 1}
onClick={() => this.createStockTransfer()}
className="btn btn-outline-primary btn-form float-right btn-xs"
>
<Translate id="react.stockTransfer.startStockTransfer.label" defaultMessage="Start Stock Transfer" />
</button>
<div className="align-items-end h-auto">
<button
type="button"
onClick={() => {
this.switchExpiredItemsFiltering();
}}
className={`btn ${this.state.showExpiredItemsOnly ? 'btn-outline-primary' : 'btn-primary'} btn-xs h-100`}
>
<Translate id="" defaultMessage="Expired Items" />
</button>
<button
type="button"
disabled={this.state.selection.size < 1}
onClick={() => this.createStockTransfer()}
className="btn btn-outline-primary btn-form float-right btn-xs h-100"
>
<Translate id="react.stockTransfer.startStockTransfer.label" defaultMessage="Start Stock Transfer" />
</button>
</div>
</div>
{
stockTransferItems
? (
<SelectTreeTable
data={stockTransferItems}
columns={columns}
ref={(r) => { this.selectTable = r; }}
ref={(r) => {
this.selectTable = r;
}}
className="-striped -highlight"
{...extraProps}
defaultPageSize={Number.MAX_SAFE_INTEGER}
Expand All @@ -320,7 +344,10 @@ class CreateStockTransfer extends Component {
filterable
defaultFilterMethod={this.filterMethod}
SelectInputComponent={({
id, checked, onClick, row,
id,
checked,
onClick,
row,
}) => (
<input
type={selectType}
Expand Down
Loading