diff --git a/client/src/i18n/en/asset.json b/client/src/i18n/en/asset.json
index 77e02a52b9..2e2f0be426 100644
--- a/client/src/i18n/en/asset.json
+++ b/client/src/i18n/en/asset.json
@@ -39,6 +39,7 @@
"SHOW_NOT_ASSIGNED" : "Unassigned assets",
"SHOW_ONLY" : "Show only",
"SHOW_ONLY_ASSIGNED" : "Assigned assets",
+ "SHOW_ONLY_LAST_SCAN" : "Show only last scan",
"SHOW_SCANNED_ASSETS" : "Show Scanned Assets",
"SHOW_UNSCANNED_ASSETS" : "Show Unscanned Assets",
"SUCCESSFULLY_EDITED":"Asset successfully updated",
diff --git a/client/src/modules/asset_scans/asset-scans-registry.service.js b/client/src/modules/asset_scans/asset-scans-registry.service.js
index 962174c70a..4de49a42bc 100644
--- a/client/src/modules/asset_scans/asset-scans-registry.service.js
+++ b/client/src/modules/asset_scans/asset-scans-registry.service.js
@@ -148,6 +148,7 @@ function AssetsScansRegistryService(Session, Filters, AppCache, bhConstants, Per
{ key : 'inventory_uuid', label : 'FORM.LABELS.INVENTORY' },
{ key : 'group_uuid', label : 'STOCK.INVENTORY_GROUP' },
{ key : 'reference_number', label : 'FORM.LABELS.REFERENCE_NUMBER' },
+ { key : 'show_only_last_scans', label : 'ASSET.SHOW_ONLY_LAST_SCAN' },
]);
if (filterCache.filters) {
diff --git a/client/src/modules/asset_scans/modals/search.modal.html b/client/src/modules/asset_scans/modals/search.modal.html
index ae353462be..9823ae2f51 100644
--- a/client/src/modules/asset_scans/modals/search.modal.html
+++ b/client/src/modules/asset_scans/modals/search.modal.html
@@ -50,7 +50,19 @@
-
+
+
+
diff --git a/client/src/modules/asset_scans/modals/search.modal.js b/client/src/modules/asset_scans/modals/search.modal.js
index f9e76f54b5..c2acb4f649 100644
--- a/client/src/modules/asset_scans/modals/search.modal.js
+++ b/client/src/modules/asset_scans/modals/search.modal.js
@@ -16,6 +16,7 @@ function AssetScansSearchModalController(data, util, Store, Instance, Stock, Sea
const searchQueryOptions = [
'depot_uuid', 'inventory_uuid', 'group_uuid', 'asset_label', 'reference_number',
+ 'show_only_last_scans',
];
// displayValues will be an id:displayValue pair
@@ -25,6 +26,12 @@ function AssetScansSearchModalController(data, util, Store, Instance, Stock, Sea
// assign already defined custom filters to searchQueries object
vm.searchQueries = util.maskObjectFromKeys(data, searchQueryOptions);
+ // Set the excludeAssets flag based on the existing custom search filter
+ vm.showOnlyLatestScan = 0;
+ if ('show_only_last_scans' in vm.searchQueries && !vm.searchQueries.show_only_last_scans) {
+ vm.showOnlyLatestScan = 1;
+ }
+
// custom filter depot_uuid - assign the value to the params object
vm.onSelectDepot = function onSelectDepot(depot) {
vm.searchQueries.depot_uuid = depot.uuid;
@@ -43,6 +50,15 @@ function AssetScansSearchModalController(data, util, Store, Instance, Stock, Sea
displayValues.group_uuid = group.name;
};
+ // custom filter - flag to only show the latests scan
+ vm.onShowOnlyLatestScan = function onShowOnlyLatestScan() {
+ if (vm.showOnlyLatestScan) {
+ vm.searchQueries.show_only_last_scans = 1;
+ } else {
+ vm.clear('show_only_last_scans');
+ }
+ };
+
// default filter limit - directly write to changes list
vm.onSelectLimit = function onSelectLimit(value) {
// input is type value, this will only be defined for a valid number
diff --git a/server/controllers/stock/asset_scan.js b/server/controllers/stock/asset_scan.js
index 26ce1bc43a..48613ad1bb 100644
--- a/server/controllers/stock/asset_scan.js
+++ b/server/controllers/stock/asset_scan.js
@@ -62,6 +62,8 @@ function getAssetScanFilters(parameters) {
filters.dateFrom('custom_period_start', 'created_at');
filters.dateTo('custom_period_end', 'created_at');
+ filters.custom('show_only_last_scans', '(s2.uuid IS NULL)');
+
return filters;
}
@@ -98,6 +100,16 @@ exports.getAssetScans = async function getAssetScans(req, res, next) {
// Get the asset scans
function listAssetScans(params) {
const filters = getAssetScanFilters(params);
+ let lastScanJoin = '';
+ if ('show_only_last_scans' in params) {
+ // See 'Outer Join Method' in https://thoughtbot.com/blog/ordering-within-a-sql-group-by-clause
+ // This join with the 'show_only_last_scans' filter (defined above) ensures that we
+ // are not selecting only the latestālast asset scan for each asset.
+ lastScanJoin = `
+ LEFT OUTER JOIN asset_scan AS s2
+ ON s2.asset_uuid = s.asset_uuid AND
+ s.created_at < s2.created_at`;
+ }
const sql = `
SELECT
@@ -115,7 +127,7 @@ function listAssetScans(params) {
JOIN inventory i ON i.uuid = l.inventory_uuid AND i.is_asset = 1
JOIN inventory_group ig ON ig.uuid = i.group_uuid
JOIN depot d ON d.uuid = s.depot_uuid
- JOIN user AS u ON u.id = s.scanned_by
+ JOIN user AS u ON u.id = s.scanned_by ${lastScanJoin}
LEFT JOIN stock_assign sa ON sa.lot_uuid = l.uuid AND sa.is_active = 1
LEFT JOIN entity e ON e.uuid = sa.entity_uuid
`;