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>
|
||||
<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>
|
||||
|
2
brewman/static/scripts/angular_service.js
vendored
2
brewman/static/scripts/angular_service.js
vendored
@ -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;
|
||||
});
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user