Feature: Recipe list shows the group and is also sortable on name, group and dates, etc.
This commit is contained in:
parent
1439befc46
commit
e9504753a5
|
@ -1,7 +1,7 @@
|
||||||
<h1>Recipes</h1>
|
<h1>Recipes</h1>
|
||||||
<div layout="row">
|
<div layout="row">
|
||||||
<md-input-container flex="grow">
|
<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>
|
||||||
<md-input-container flex>
|
<md-input-container flex>
|
||||||
<md-button ng-href="/Recipe">Add <i
|
<md-button ng-href="/Recipe">Add <i
|
||||||
|
@ -19,11 +19,18 @@
|
||||||
<md-datepicker md-placeholder="Finish Date" ng-model="finishDate"
|
<md-datepicker md-placeholder="Finish Date" ng-model="finishDate"
|
||||||
ng-model-options="{ getterSetter: true }"></md-datepicker>
|
ng-model-options="{ getterSetter: true }"></md-datepicker>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
<table class="table table-condensed table-bordered table-striped">
|
<table class="table table-condensed table-bordered table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
|
<th>Type</th>
|
||||||
<th>Valid From</th>
|
<th>Valid From</th>
|
||||||
<th>Valid To</th>
|
<th>Valid To</th>
|
||||||
<th class="text-right">Cost Price</th>
|
<th class="text-right">Cost Price</th>
|
||||||
|
@ -34,6 +41,7 @@
|
||||||
<tbody ng-repeat="items in recipes">
|
<tbody ng-repeat="items in recipes">
|
||||||
<tr ng-repeat="item in items.Prices">
|
<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.Name}}</td>
|
||||||
|
<td ng-if="$first" rowspan="{{items.Prices.length}}">{{items.ProductGroup}}</td>
|
||||||
<td><a href="{{item.Url}}">{{item.ValidFrom}}</a></td>
|
<td><a href="{{item.Url}}">{{item.ValidFrom}}</a></td>
|
||||||
<td>{{item.ValidTo}}</td>
|
<td>{{item.ValidTo}}</td>
|
||||||
<td class="text-right">{{item.CostPrice | currency}}</td>
|
<td class="text-right">{{item.CostPrice | currency}}</td>
|
||||||
|
|
|
@ -366,7 +366,7 @@ overlordService.factory('Tokenizer', ['$filter', function ($filter) {
|
||||||
return expression.Comparator(item[expression.Col], expression.Value);
|
return expression.Comparator(item[expression.Col], expression.Value);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
arrayCopy = sortPredicates.length === 0 ? arrayCopy : $filter('orderBy')(arrayCopy, sortPredicates);
|
arrayCopy = $filter('orderBy')(arrayCopy, sortPredicates);
|
||||||
return arrayCopy;
|
return arrayCopy;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -17,8 +17,9 @@ var ProductListController = ['$scope', '$location', '$routeParams', 'Tokenizer',
|
||||||
$scope._isPurchased = isPurchased;
|
$scope._isPurchased = isPurchased;
|
||||||
var re = /(('[p]'|"[p]"|[p]+)\s*:\s*('[^']+'|"[^"]+"|[^\s]+))/gi,
|
var re = /(('[p]'|"[p]"|[p]+)\s*:\s*('[^']+'|"[^"]+"|[^\s]+))/gi,
|
||||||
matches = $scope.search.match(re),
|
matches = $scope.search.match(re),
|
||||||
st = isPurchased === 'null' ? '' : 'p:' + isPurchased;
|
st = isPurchased === 'null' ? '' : 'p:' + isPurchased,
|
||||||
$scope.search = (matches === null) ? $scope.search.trim() + ' ' + st : $scope.search.replace(re, st);
|
search = (matches === null) ? $scope.search.trim() + ' ' + st : $scope.search.replace(re, st);
|
||||||
|
$scope.search = search.trim();
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.isSold = function (isSold) {
|
$scope.isSold = function (isSold) {
|
||||||
|
@ -28,8 +29,9 @@ var ProductListController = ['$scope', '$location', '$routeParams', 'Tokenizer',
|
||||||
$scope._isSold = isSold;
|
$scope._isSold = isSold;
|
||||||
var re = /(('[s]'|"[s]"|[s]+)\s*:\s*('[^']+'|"[^"]+"|[^\s]+))/gi,
|
var re = /(('[s]'|"[s]"|[s]+)\s*:\s*('[^']+'|"[^"]+"|[^\s]+))/gi,
|
||||||
matches = $scope.search.match(re),
|
matches = $scope.search.match(re),
|
||||||
st = isSold === 'null' ? '' : 's:' + isSold;
|
st = isSold === 'null' ? '' : 's:' + isSold,
|
||||||
$scope.search = (matches === null) ? $scope.search.trim() + ' ' + st : $scope.search.replace(re, st);
|
search = (matches === null) ? $scope.search.trim() + ' ' + st : $scope.search.replace(re, st);
|
||||||
|
$scope.search = search.trim();
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.productGroup = function (productGroup) {
|
$scope.productGroup = function (productGroup) {
|
||||||
|
@ -40,8 +42,9 @@ var ProductListController = ['$scope', '$location', '$routeParams', 'Tokenizer',
|
||||||
$scope._productGroup = productGroup;
|
$scope._productGroup = productGroup;
|
||||||
var re = /(('[t]'|"[t]"|[t]+)\s*:\s*('[^']+'|"[^"]+"|[^\s]+))/gi,
|
var re = /(('[t]'|"[t]"|[t]+)\s*:\s*('[^']+'|"[^"]+"|[^\s]+))/gi,
|
||||||
matches = $scope.search.match(re),
|
matches = $scope.search.match(re),
|
||||||
st = 't:"' + productGroup + '"';
|
st = 't:"' + productGroup + '"',
|
||||||
$scope.search = (matches === null) ? $scope.search.trim() + ' ' + st : $scope.search.replace(re, st);
|
search = (matches === null) ? $scope.search.trim() + ' ' + st : $scope.search.replace(re, st);
|
||||||
|
$scope.search = search.trim();
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.$watch('search', function (value) {
|
$scope.$watch('search', function (value) {
|
||||||
|
|
|
@ -1,80 +1,119 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var RecipeListController = ['$scope', 'recipes', '$location', '$routeParams', 'Tokenizer', function ($scope, recipes, $location, $routeParams, Tokenizer) {
|
var RecipeListController = ['$scope', 'recipes', '$location', '$routeParams', '$filter', 'Tokenizer', function ($scope, recipes, $location, $routeParams, $filter, Tokenizer) {
|
||||||
$scope.info = recipes;
|
$scope.info = recipes;
|
||||||
$scope.search = $routeParams.q || '';
|
$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) {
|
$scope.startDate = function (startDate) {
|
||||||
if (arguments.length === 0) {
|
if (arguments.length === 0) {
|
||||||
return $scope._startDate;
|
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;
|
$scope.$apply(function () {
|
||||||
var re = /(('[s]'|"[s]"|[s]+)\s*:\s*('[^']+'|"[^"]+"|[^\s]+))/gi,
|
$scope.recipes = $scope.doFilter(q);
|
||||||
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);
|
}, 350);
|
||||||
};
|
$scope.doFilter = _.memoize(function (q) {
|
||||||
|
var matches = Tokenizer.parseFilterString(q, $scope.searchInfo),
|
||||||
|
array = angular.copy($scope.info);
|
||||||
|
|
||||||
$scope.finishDate = function (finishDate) {
|
array = $scope.subFilter(null, array, matches.q, matches.o);
|
||||||
if (arguments.length === 0) {
|
array.forEach(function (prod) {
|
||||||
return $scope._finishDate;
|
prod.Prices = $scope.subFilter('Prices.', prod.Prices, matches.q, matches.o);
|
||||||
}
|
|
||||||
$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 = {};
|
|
||||||
});
|
});
|
||||||
|
return array;
|
||||||
|
});
|
||||||
|
|
||||||
$scope.searchInfo = {
|
$scope.subFilter = function (prefix, arr, q, o) {
|
||||||
comparator: {
|
q = angular.copy(q);
|
||||||
'n': {'Col': 'Name', 'Comparator': 'text'},
|
o = angular.copy(o);
|
||||||
's': {'Col': 'ValidFrom', 'Comparator': 'date'},
|
if (prefix === null) {
|
||||||
'f': {'Col': 'ValidTo', 'Comparator': 'date'}
|
q = q.filter(x => !x.Col.includes('.'));
|
||||||
},
|
o = o.filter(x => !x.includes('.'));
|
||||||
def: 'n',
|
} else {
|
||||||
sorter: {
|
q = q.filter(x => x.Col.startsWith(prefix));
|
||||||
'n': 'Name',
|
q.forEach(function (x) {x.Col = x.Col.substr(prefix.length)});
|
||||||
's': 'ValidFrom',
|
o = o.filter(x => x.substr(1).startsWith(prefix)).map(x => x[0] + x.substr(prefix.length + 1));
|
||||||
'f': 'ValidTo',
|
}
|
||||||
'c': 'Costing',
|
if (q.length !== 0) {
|
||||||
'cp': 'CostPrice',
|
arr = arr.filter(item => q.every(exp => exp.Comparator(item[exp.Col], exp.Value)));
|
||||||
'sp': 'SalePrice'
|
}
|
||||||
},
|
return $filter('orderBy')(arr, o);
|
||||||
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);
|
|
||||||
|
|
||||||
}]
|
|
||||||
;
|
|
||||||
|
|
||||||
RecipeListController.resolve = {
|
RecipeListController.resolve = {
|
||||||
recipes: ['Recipe', function (Recipe) {
|
recipes: ['Recipe', function (Recipe) {
|
||||||
|
@ -181,7 +220,12 @@ var RecipeController = ['$scope', '$location', '$window', '$modal', 'asDateFilte
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$scope.products = function ($viewValue, options) {
|
$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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -226,7 +226,8 @@ def show_list(request):
|
||||||
product_id = None
|
product_id = None
|
||||||
for item in list:
|
for item in list:
|
||||||
if item.product_id != product_id:
|
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)
|
recipes.append(recipe)
|
||||||
product_id = item.product_id
|
product_id = item.product_id
|
||||||
costing = 0
|
costing = 0
|
||||||
|
|
Loading…
Reference in New Issue