Feature: Recipe list shows the group and is also sortable on name, group and dates, etc.

This commit is contained in:
tanshu 2016-03-17 18:18:09 +05:30
parent 1439befc46
commit e9504753a5
5 changed files with 136 additions and 80 deletions

View File

@ -1,7 +1,7 @@
<h1>Recipes</h1>
<div layout="row">
<md-input-container flex="grow">
<input type="text" ng-model="search" md-autofocus placeholder="Search: n:Name, s:Start Date, f:Finish Date">
<input type="text" ng-model="search" md-autofocus placeholder="Search: n:Name, t:Type, s:Start Date, f:Finish Date, c: Costing, cp: CostPrice, sp: SalePrice">
</md-input-container>
<md-input-container flex>
<md-button ng-href="/Recipe">Add <i
@ -19,11 +19,18 @@
<md-datepicker md-placeholder="Finish Date" ng-model="finishDate"
ng-model-options="{ getterSetter: true }"></md-datepicker>
</div>
<md-input-container flex="30">
<label>Product Group</label>
<md-select ng-model="productGroup" ng-model-options="{ getterSetter: true }" placeholder="Product Group">
<md-option ng-repeat="pg in product_groups" value="{{pg}}">{{pg}}</md-option>
</md-select>
</md-input-container>
</div>
<table class="table table-condensed table-bordered table-striped">
<thead>
<tr>
<th>Name</th>
<th>Type</th>
<th>Valid From</th>
<th>Valid To</th>
<th class="text-right">Cost Price</th>
@ -34,6 +41,7 @@
<tbody ng-repeat="items in recipes">
<tr ng-repeat="item in items.Prices">
<td ng-if="$first" rowspan="{{items.Prices.length}}">{{items.Name}}</td>
<td ng-if="$first" rowspan="{{items.Prices.length}}">{{items.ProductGroup}}</td>
<td><a href="{{item.Url}}">{{item.ValidFrom}}</a></td>
<td>{{item.ValidTo}}</td>
<td class="text-right">{{item.CostPrice | currency}}</td>

View File

@ -366,7 +366,7 @@ overlordService.factory('Tokenizer', ['$filter', function ($filter) {
return expression.Comparator(item[expression.Col], expression.Value);
});
});
arrayCopy = sortPredicates.length === 0 ? arrayCopy : $filter('orderBy')(arrayCopy, sortPredicates);
arrayCopy = $filter('orderBy')(arrayCopy, sortPredicates);
return arrayCopy;
});

View File

@ -17,8 +17,9 @@ var ProductListController = ['$scope', '$location', '$routeParams', 'Tokenizer',
$scope._isPurchased = isPurchased;
var re = /(('[p]'|"[p]"|[p]+)\s*:\s*('[^']+'|"[^"]+"|[^\s]+))/gi,
matches = $scope.search.match(re),
st = isPurchased === 'null' ? '' : 'p:' + isPurchased;
$scope.search = (matches === null) ? $scope.search.trim() + ' ' + st : $scope.search.replace(re, st);
st = isPurchased === 'null' ? '' : 'p:' + isPurchased,
search = (matches === null) ? $scope.search.trim() + ' ' + st : $scope.search.replace(re, st);
$scope.search = search.trim();
};
$scope.isSold = function (isSold) {
@ -28,8 +29,9 @@ var ProductListController = ['$scope', '$location', '$routeParams', 'Tokenizer',
$scope._isSold = isSold;
var re = /(('[s]'|"[s]"|[s]+)\s*:\s*('[^']+'|"[^"]+"|[^\s]+))/gi,
matches = $scope.search.match(re),
st = isSold === 'null' ? '' : 's:' + isSold;
$scope.search = (matches === null) ? $scope.search.trim() + ' ' + st : $scope.search.replace(re, st);
st = isSold === 'null' ? '' : 's:' + isSold,
search = (matches === null) ? $scope.search.trim() + ' ' + st : $scope.search.replace(re, st);
$scope.search = search.trim();
};
$scope.productGroup = function (productGroup) {
@ -40,8 +42,9 @@ var ProductListController = ['$scope', '$location', '$routeParams', 'Tokenizer',
$scope._productGroup = productGroup;
var re = /(('[t]'|"[t]"|[t]+)\s*:\s*('[^']+'|"[^"]+"|[^\s]+))/gi,
matches = $scope.search.match(re),
st = 't:"' + productGroup + '"';
$scope.search = (matches === null) ? $scope.search.trim() + ' ' + st : $scope.search.replace(re, st);
st = 't:"' + productGroup + '"',
search = (matches === null) ? $scope.search.trim() + ' ' + st : $scope.search.replace(re, st);
$scope.search = search.trim();
};
$scope.$watch('search', function (value) {

View File

@ -1,80 +1,119 @@
'use strict';
var RecipeListController = ['$scope', 'recipes', '$location', '$routeParams', 'Tokenizer', function ($scope, recipes, $location, $routeParams, Tokenizer) {
$scope.info = recipes;
$scope.search = $routeParams.q || '';
var RecipeListController = ['$scope', 'recipes', '$location', '$routeParams', '$filter', 'Tokenizer', function ($scope, recipes, $location, $routeParams, $filter, Tokenizer) {
$scope.info = recipes;
$scope.search = $routeParams.q || '';
$scope.product_groups = _.reduce(recipes, function (acculumator, item) {
if (!acculumator.includes(item.ProductGroup)){
acculumator.push(item.ProductGroup);
}
return acculumator;
},[]).sort();
$scope.startDate = function (startDate) {
if (arguments.length === 0) {
return $scope._startDate;
$scope.startDate = function (startDate) {
if (arguments.length === 0) {
return $scope._startDate;
}
$scope._startDate = startDate;
var re = /(('[s]'|"[s]"|[s]+)\s*:\s*('[^']+'|"[^"]+"|[^\s]+))/gi,
matches = $scope.search.match(re),
st = startDate === null ? '' : 's:>=' + moment(startDate).format('DD-MMM-YYYY');
$scope.search = (matches === null) ? $scope.search.trim() + ' ' + st : $scope.search.replace(re, st);
};
$scope.finishDate = function (finishDate) {
if (arguments.length === 0) {
return $scope._finishDate;
}
$scope._finishDate = finishDate;
var re = /(('[f]'|"[f]"|[f]+)\s*:\s*('[^']+'|"[^"]+"|[^\s]+))/gi,
matches = $scope.search.match(re),
st = finishDate === null ? '' : 'f:<=' + moment(finishDate).format('DD-MMM-YYYY');
$scope.search = (matches === null) ? $scope.search.trim() + ' ' + st : $scope.search.replace(re, st);
};
$scope.productGroup = function (productGroup) {
if (arguments.length === 0) {
return $scope._productGroup;
}
$scope._productGroup = productGroup;
var re = /(('[t]'|"[t]"|[t]+)\s*:\s*('[^']+'|"[^"]+"|[^\s]+))/gi,
matches = $scope.search.match(re),
st = 't:"' + productGroup + '"',
search = (matches === null) ? $scope.search.trim() + ' ' + st : $scope.search.replace(re, st);
$scope.search = search.trim();
};
$scope.$watch('search', function (value) {
$scope.filterRecipes(value);
}, true);
$scope.$on('$destroy', function () {
Tokenizer.doFilter.cache = {};
});
$scope.searchInfo = {
comparator: {
'n': {'Col': 'Name', 'Comparator': 'text'},
't': {'Col': 'ProductGroup', 'Comparator': 'text'},
's': {'Col': 'Prices.ValidFrom', 'Comparator': 'date'},
'f': {'Col': 'Prices.ValidTo', 'Comparator': 'date'}
},
def: 'n',
sorter: {
'n': 'Name',
't': 'ProductGroup',
's': 'Prices.ValidFrom',
'f': 'Prices.ValidTo',
'c': 'Prices.Costing',
'cp': 'Prices.CostPrice',
'sp': 'Prices.SalePrice'
},
flags: {}
};
$scope.filterRecipes = _.debounce(function (q) {
if (q !== $scope._search) {
$scope._search = q;
if (angular.isUndefined(q) || q === '') {
$location.path('/Recipes').search('q', null).replace();
} else {
$location.path('/Recipes').search({'q': q}).replace();
}
$scope._startDate = startDate;
var re = /(('[s]'|"[s]"|[s]+)\s*:\s*('[^']+'|"[^"]+"|[^\s]+))/gi,
matches = $scope.search.match(re),
st = startDate === null ? '' : 's:>=' + moment(startDate).format('DD-MMM-YYYY');
$scope.search = (matches === null) ? $scope.search.trim() + ' ' + st : $scope.search.replace(re, st);
};
$scope.$apply(function () {
$scope.recipes = $scope.doFilter(q);
});
}
}, 350);
$scope.doFilter = _.memoize(function (q) {
var matches = Tokenizer.parseFilterString(q, $scope.searchInfo),
array = angular.copy($scope.info);
$scope.finishDate = function (finishDate) {
if (arguments.length === 0) {
return $scope._finishDate;
}
$scope._finishDate = finishDate;
var re = /(('[f]'|"[f]"|[f]+)\s*:\s*('[^']+'|"[^"]+"|[^\s]+))/gi,
matches = $scope.search.match(re),
st = finishDate === null ? '' : 'f:<=' + moment(finishDate).format('DD-MMM-YYYY');
$scope.search = (matches === null) ? $scope.search.trim() + ' ' + st : $scope.search.replace(re, st);
};
$scope.$watch('search', function (value) {
$scope.filterRecipes(value);
}, true);
$scope.$on('$destroy', function () {
Tokenizer.doFilter.cache = {};
array = $scope.subFilter(null, array, matches.q, matches.o);
array.forEach(function (prod) {
prod.Prices = $scope.subFilter('Prices.', prod.Prices, matches.q, matches.o);
});
return array;
});
$scope.searchInfo = {
comparator: {
'n': {'Col': 'Name', 'Comparator': 'text'},
's': {'Col': 'ValidFrom', 'Comparator': 'date'},
'f': {'Col': 'ValidTo', 'Comparator': 'date'}
},
def: 'n',
sorter: {
'n': 'Name',
's': 'ValidFrom',
'f': 'ValidTo',
'c': 'Costing',
'cp': 'CostPrice',
'sp': 'SalePrice'
},
flags: {}
};
$scope.filterRecipes = _.debounce(function (q) {
if (q !== $scope._search) {
$scope._search = q;
if (angular.isUndefined(q) || q === '') {
$location.path('/Recipes').search('q', null).replace();
} else {
$location.path('/Recipes').search({'q': q}).replace();
}
$scope.$apply(function () {
var matches = Tokenizer.parseFilterString(q, $scope.searchInfo);
$scope.recipes = _.map($scope.info, function (prod) {
var item = angular.copy(prod);
item.Prices = Tokenizer.doFilter(prod.ProductID + ' ' + q, prod.Prices, matches);
return item;
});
});
}
}, 350);
}]
;
$scope.subFilter = function (prefix, arr, q, o) {
q = angular.copy(q);
o = angular.copy(o);
if (prefix === null) {
q = q.filter(x => !x.Col.includes('.'));
o = o.filter(x => !x.includes('.'));
} else {
q = q.filter(x => x.Col.startsWith(prefix));
q.forEach(function (x) {x.Col = x.Col.substr(prefix.length)});
o = o.filter(x => x.substr(1).startsWith(prefix)).map(x => x[0] + x.substr(prefix.length + 1));
}
if (q.length !== 0) {
arr = arr.filter(item => q.every(exp => exp.Comparator(item[exp.Col], exp.Value)));
}
return $filter('orderBy')(arr, o);
}
}];
RecipeListController.resolve = {
recipes: ['Recipe', function (Recipe) {
@ -181,7 +220,12 @@ var RecipeController = ['$scope', '$location', '$window', '$modal', 'asDateFilte
});
};
$scope.products = function ($viewValue, options) {
return Product.autocomplete(angular.extend({term: $viewValue, count: 20, a: true, e: true}, options)).$promise;
return Product.autocomplete(angular.extend({
term: $viewValue,
count: 20,
a: true,
e: true
}, options)).$promise;
};

View File

@ -226,7 +226,8 @@ def show_list(request):
product_id = None
for item in list:
if item.product_id != product_id:
recipe = {'ProductID': item.product_id, 'Name': item.product.name, 'Prices': []}
recipe = {'ProductID': item.product_id, 'Name': item.product.name,
'ProductGroup': item.product.product_group.name, 'Prices': []}
recipes.append(recipe)
product_id = item.product_id
costing = 0