-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New blade with prices for product (#5)
* New blade with prices for product #5 * Improved validation for min quantity of product, so user will not be able to add several prices with the same number of min. quantity in same pricelist. * Fix permissions * Inline styles moved to stylesheet file * Grid rows validation fix * Display name of currency in price list selector * Empty initial list price instead 0
- Loading branch information
1 parent
f36c340
commit e0ffed4
Showing
11 changed files
with
401 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
.head-label { | ||
font-size: 17px; | ||
margin-bottom: 15px; | ||
} | ||
|
||
.head-select { | ||
width: calc(100% - 120px); | ||
} | ||
|
||
button.btn.head-button { | ||
background: #43b0e6; | ||
color: #fff; | ||
height: 30px; | ||
line-height: 28px; | ||
padding: 0 10px; | ||
} | ||
|
||
button.btn.head-button:hover { | ||
background: #43b0e6; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
225 changes: 225 additions & 0 deletions
225
VirtoCommerce.PricingModule.Web/Scripts/blades/item/item-prices.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,225 @@ | ||
angular.module('virtoCommerce.pricingModule') | ||
.controller('virtoCommerce.pricingModule.itemPriceListController', ['$scope', 'platformWebApp.bladeNavigationService', 'uiGridConstants', 'virtoCommerce.pricingModule.prices', 'virtoCommerce.catalogModule.catalogs', 'platformWebApp.ui-grid.extension', 'platformWebApp.objCompareService', 'virtoCommerce.pricingModule.priceValidatorsService', 'platformWebApp.dialogService', | ||
function ($scope, bladeNavigationService, uiGridConstants, prices, catalogs, gridOptionExtension, objCompareService, priceValidatorsService, dialogService) { | ||
$scope.uiGridConstants = uiGridConstants; | ||
var blade = $scope.blade; | ||
blade.updatePermission = 'pricing:update'; | ||
|
||
blade.refresh = function () { | ||
blade.isLoading = true; | ||
prices.getProductPricelists({ id: blade.itemId }, function (pricelists) { | ||
//Loading catalogs for assignments because they do not contains them | ||
//Need to display name of catalog in assignments grid | ||
catalogs.getCatalogs(function (catalogsList) { | ||
$scope.catalogsList = catalogsList; | ||
|
||
blade.origEntity = []; | ||
|
||
//Collect all available pricelists | ||
blade.pricelistList = _.map(pricelists, function (pricelist) { | ||
return { | ||
id: pricelist.id, | ||
code: pricelist.name, | ||
currency: pricelist.currency, | ||
assignments: pricelist.assignments, | ||
displayName: pricelist.name + ' - ' + pricelist.currency | ||
}; | ||
}); | ||
blade.selectedPricelist = _.first(blade.pricelistList); | ||
|
||
var pricelistsWithPrices = _.filter(pricelists, function (pricelist) { return pricelist.prices.length > 0; }); | ||
_.each(pricelistsWithPrices, function (pricelistWithPrices) { | ||
var priceListData = { | ||
name: pricelistWithPrices.name, | ||
currency: pricelistWithPrices.currency | ||
}; | ||
|
||
var catalogsId = _.pluck(pricelistWithPrices.assignments, 'catalogId'); | ||
var catalogsName = _.map(catalogsId, function (catalogId) { | ||
return _.findWhere($scope.catalogsList, { id: catalogId }).name; | ||
}); | ||
priceListData.catalog = catalogsName.join(', '); | ||
|
||
_.each(pricelistWithPrices.prices, function (price) { | ||
var priceData = angular.copy(priceListData); | ||
priceData = angular.extend(price, priceData); | ||
blade.origEntity.push(priceData); | ||
}); | ||
}); | ||
|
||
priceValidatorsService.setAllPrices(blade.currentEntities); | ||
blade.currentEntities = angular.copy(blade.origEntity); | ||
blade.isLoading = false; | ||
}); | ||
}); | ||
} | ||
|
||
$scope.createNewPricelist = function () { | ||
var newBlade = { | ||
id: 'pricingList', | ||
controller: 'virtoCommerce.pricingModule.pricelistListController', | ||
template: 'Modules/$(VirtoCommerce.Pricing)/Scripts/blades/pricelist-list.tpl.html', | ||
title: 'pricing.blades.pricing-main.menu.pricelist-list.title', | ||
parentRefresh: blade.refresh | ||
}; | ||
|
||
bladeNavigationService.showBlade(newBlade, blade); | ||
}; | ||
|
||
$scope.selectPricelist = function (entity) { | ||
var newBlade = { | ||
id: 'listItemChild', | ||
currentEntityId: entity.pricelistId, | ||
title: entity.name, | ||
controller: 'virtoCommerce.pricingModule.pricelistDetailController', | ||
template: 'Modules/$(VirtoCommerce.Pricing)/Scripts/blades/pricelist-detail.tpl.html' | ||
}; | ||
|
||
bladeNavigationService.showBlade(newBlade, blade); | ||
}; | ||
|
||
blade.onClose = function (closeCallback) { | ||
bladeNavigationService.showConfirmationIfNeeded(isDirty(), canSave(), blade, $scope.saveChanges, closeCallback, "pricing.dialogs.prices-save.title", "pricing.dialogs.prices-save.message"); | ||
}; | ||
|
||
function isDirty() { | ||
return blade.currentEntities && !objCompareService.equal(blade.origEntity, blade.currentEntities) && blade.hasUpdatePermission() | ||
} | ||
|
||
function canSave() { | ||
return isDirty() && $scope.isValid(); | ||
} | ||
|
||
$scope.isValid = function () { | ||
return $scope.formScope && $scope.formScope.$valid && | ||
_.all(blade.currentEntities, $scope.isListPriceValid) && | ||
_.all(blade.currentEntities, $scope.isSalePriceValid) && | ||
_.all(blade.currentEntities, $scope.isUniqueQtyForPricelist) && | ||
(blade.currentEntities.length == 0 || _.some(blade.currentEntities, function (x) { return x.minQuantity == 1; })); | ||
} | ||
|
||
$scope.saveChanges = function () { | ||
blade.isLoading = true; | ||
|
||
angular.copy(blade.currentEntities, blade.origEntity); | ||
if (_.any(blade.currentEntities)) { | ||
var productPrices = { | ||
productId: blade.itemId, | ||
product: blade.item, | ||
prices: blade.currentEntities | ||
}; | ||
prices.update({ id: blade.itemId }, productPrices, function (data) { | ||
blade.refresh(); | ||
}, | ||
function (error) { bladeNavigationService.setError('Error ' + error.status, $scope.blade); }); | ||
} | ||
}; | ||
|
||
$scope.setForm = function (form) { $scope.formScope = form; } | ||
|
||
blade.toolbarCommands = [ | ||
{ | ||
name: "platform.commands.save", | ||
icon: 'fa fa-save', | ||
executeMethod: $scope.saveChanges, | ||
canExecuteMethod: canSave, | ||
permission: blade.updatePermission | ||
}, | ||
{ | ||
name: "platform.commands.delete", | ||
icon: 'fa fa-trash-o', | ||
executeMethod: function () { | ||
var selection = $scope.gridApi.selection.getSelectedRows(); | ||
var ids = _.map(selection, function (item) { return item.id; }); | ||
|
||
var dialog = { | ||
id: "confirmDeleteItem", | ||
title: "pricing.dialogs.item-prices-delete.title", | ||
message: "pricing.dialogs.item-prices-delete.message", | ||
callback: function (remove) { | ||
if (remove) { | ||
prices.removePrice({ priceIds: ids }, function () { | ||
//blade.refresh(); | ||
angular.forEach(selection, function (listItem) { | ||
blade.currentEntities.splice(blade.currentEntities.indexOf(listItem), 1); | ||
}); | ||
}, function (error) { | ||
bladeNavigationService.setError('Error ' + error.status, blade); | ||
}); | ||
} | ||
} | ||
} | ||
dialogService.showConfirmationDialog(dialog); | ||
}, | ||
canExecuteMethod: function () { | ||
return $scope.gridApi && _.any($scope.gridApi.selection.getSelectedRows()); | ||
}, | ||
permission: 'pricing:delete' | ||
}, | ||
{ | ||
name: "platform.commands.refresh", | ||
icon: 'fa fa-refresh', | ||
executeMethod: blade.refresh, | ||
canExecuteMethod: function () { return true; } | ||
} | ||
]; | ||
|
||
blade.addNewPrice = function (targetPricelist) { | ||
//populate prices data for correct work of validation service | ||
priceValidatorsService.setAllPrices(blade.currentEntities); | ||
|
||
var catalogsId = _.pluck(targetPricelist.assignments, 'catalogId'); | ||
var catalogsName = _.map(catalogsId, function (catalogId) { | ||
return _.findWhere($scope.catalogsList, { id: catalogId }).name; | ||
}); | ||
|
||
var newPrice = { | ||
productId: blade.itemId, | ||
list: '', | ||
minQuantity: 1, | ||
currency: targetPricelist.currency, | ||
pricelistId: targetPricelist.id, | ||
name: targetPricelist.code, | ||
catalog: catalogsName.join(', ') | ||
}; | ||
blade.currentEntities.push(newPrice); | ||
$scope.validateGridData(); | ||
} | ||
|
||
$scope.isListPriceValid = priceValidatorsService.isListPriceValid; | ||
$scope.isSalePriceValid = priceValidatorsService.isSalePriceValid; | ||
$scope.isUniqueQtyForPricelist = priceValidatorsService.isUniqueQtyForPricelist; | ||
|
||
// ui-grid | ||
$scope.setGridOptions = function (gridId, gridOptions) { | ||
gridOptions.onRegisterApi = function (gridApi) { | ||
$scope.gridApi = gridApi; | ||
|
||
gridApi.edit.on.afterCellEdit($scope, function () { | ||
//to process validation for all rows in grid. | ||
//e.g. if we have two rows with the same count of min qty, both of this rows will be marked as error. | ||
//when we change data to valid in one row, another one should became valid too. | ||
//more info about ui-grid validation: https://github.com/angular-ui/ui-grid/issues/4152 | ||
$scope.validateGridData(); | ||
}); | ||
|
||
$scope.validateGridData(); | ||
}; | ||
|
||
$scope.gridOptions = gridOptions; | ||
gridOptionExtension.tryExtendGridOptions(gridId, gridOptions); | ||
return gridOptions; | ||
}; | ||
|
||
$scope.validateGridData = function () { | ||
if ($scope.gridApi) { | ||
angular.forEach(blade.currentEntities, function (rowEntity) { | ||
angular.forEach($scope.gridOptions.columnDefs, function (colDef) { | ||
$scope.gridApi.grid.validate.runValidators(rowEntity, colDef, rowEntity[colDef.name], undefined, $scope.gridApi.grid) | ||
}); | ||
}); | ||
} | ||
}; | ||
|
||
blade.refresh(); | ||
}]); |
87 changes: 87 additions & 0 deletions
87
VirtoCommerce.PricingModule.Web/Scripts/blades/item/item-prices.tpl.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
<div class="blade-static __expanded"> | ||
<div class="form-group"> | ||
<label class="form-label head-label">{{'pricing.blades.item-prices.head-label' | translate}} <a href="" ng-click="createNewPricelist()" class="form-edit" va-permission="pricing:read"><i class="form-ico fa fa-pencil"></i></a></label> | ||
|
||
<div class="form-input"> | ||
<ui-select ng-model="blade.selectedPricelist" class="head-select" required> | ||
<ui-select-match placeholder="{{'pricing.blades.item-prices.pricelist-select-placeholder' | translate}}">{{$select.selected.displayName}}</ui-select-match> | ||
<ui-select-choices repeat="x in blade.pricelistList | filter: $select.search"> | ||
<span ng-bind-html="x.displayName | highlight: $select.search"></span> | ||
</ui-select-choices> | ||
</ui-select> | ||
|
||
<button class="btn head-button" type="button" ng-disabled="!blade.selectedPricelist" ng-click="blade.addNewPrice(blade.selectedPricelist)">Add new price</button> | ||
</div> | ||
</div> | ||
</div> | ||
<div class="blade-content __large-wide"> | ||
<div class="blade-inner"> | ||
<div class="inner-block"> | ||
<form name="formScope" ng-init="setGridOptions('item-prices-grid', { | ||
data: 'blade.currentEntities', | ||
rowHeight: 60, | ||
rowTemplate: 'list.row.html', | ||
enableCellEdit: false, | ||
enableCellEditOnFocus: true, | ||
editableCellTemplate: 'default-cellTextEditor', | ||
columnDefs: [ | ||
{ name: 'name', displayName: 'pricing.blades.item-prices.labels.name', cellTooltip: true, cellTemplate: 'ui-grid/cellNameLinkTemplate', width: 180 }, | ||
{ name: 'currency', displayName: 'pricing.blades.item-prices.labels.currency', width: 60 }, | ||
{ name: 'catalog', displayName: 'pricing.blades.item-prices.labels.catalog', cellTooltip: true}, | ||
{ name: 'list', displayName: 'pricing.blades.item-prices.labels.list-price', editableCellTemplate: 'list-cellTextEditor', validators: {listValidator: true, required: true}, cellTemplate: 'priceCellTitleValidator', enableCellEdit: true, enableHiding: false }, | ||
{ name: 'sale', displayName: 'pricing.blades.item-prices.labels.sale-price', editableCellTemplate: 'sale-cellTextEditor', validators: {saleValidator: true}, cellTemplate: 'priceCellTitleValidator', enableCellEdit: true }, | ||
{ name: 'minQuantity', displayName: 'pricing.blades.item-prices.labels.min-quantity', editableCellTemplate: 'minQuantity-cellTextEditor', validators: {minQuantityForPricelistValidator: true, required: true}, cellTemplate: 'ui-grid/cellTitleValidator', enableCellEdit: true, enableHiding: false } | ||
]})"> | ||
<div class="table-wrapper" ng-if="blade.currentEntities.length" ng-init="setForm(formScope);"> | ||
<div ui-grid="gridOptions" ui-grid-cellNav ui-grid-edit ui-grid-validate ui-grid-auto-resize ui-grid-save-state ui-grid-selection ui-grid-resize-columns ui-grid-move-columns ui-grid-height></div> | ||
</div> | ||
</form> | ||
<div class="note" ng-if="!blade.currentEntities.length">{{ 'platform.list.no-data' | translate }}</div> | ||
</div> | ||
</div> | ||
</div> | ||
|
||
|
||
<script type="text/ng-template" id="ui-grid/cellNameLinkTemplate"> | ||
<div class="ui-grid-cell-contents"> | ||
<a class="form-input" ng-href='' ng-click='grid.appScope.selectPricelist(row.entity)'>{{row.entity.name}}</a> | ||
</div> | ||
</script> | ||
<script type="text/ng-template" id="ui-grid/cellTitleValidator"> | ||
<div class="ui-grid-cell-contents" title="{{grid.validate.getTitleFormattedErrors(row.entity,col.colDef)}}"> | ||
<div class="form-editor form-input"> | ||
<input class="ng-valid form-input" ng-class="{'ng-invalid' : grid.validate.isInvalid(row.entity,col.colDef)}" value="{{COL_FIELD CUSTOM_FILTERS}}" placeholder="{{'platform.placeholders.n-a' | translate}}" readonly="readonly" /> | ||
</div> | ||
</div> | ||
</script> | ||
<script type="text/ng-template" id="priceCellTitleValidator"> | ||
<div class="ui-grid-cell-contents" title="{{grid.validate.getTitleFormattedErrors(row.entity,col.colDef)}}"> | ||
<div class="form-editor form-input"> | ||
<input class="ng-valid form-input" money ng-model="MODEL_COL_FIELD" ng-class="{'ng-invalid' : grid.validate.isInvalid(row.entity,col.colDef)}" placeholder="{{'platform.placeholders.n-a' | translate}}" readonly="readonly" /> | ||
</div> | ||
</div> | ||
</script> | ||
|
||
<script type="text/ng-template" id="default-cellTextEditor"> | ||
<div class="form-editor form-input"> | ||
<input class="form-input" ng-model="MODEL_COL_FIELD" ui-grid-editor placeholder="{{'platform.placeholders.n-a' | translate}}" /> | ||
</div> | ||
</script> | ||
<script type="text/ng-template" id="list-cellTextEditor"> | ||
<div class="form-editor form-input"> | ||
<input class="form-input" money required ng-model="MODEL_COL_FIELD" ui-grid-editor ng-class="{'ng-invalid': !grid.appScope.isListPriceValid(row.entity)}" /> | ||
</div> | ||
</script> | ||
<script type="text/ng-template" id="sale-cellTextEditor"> | ||
<div class="form-editor form-input"> | ||
<input class="form-input" money ng-model="MODEL_COL_FIELD" ui-grid-editor ng-class="{'ng-invalid': !grid.appScope.isSalePriceValid(row.entity)}" placeholder="{{'platform.placeholders.n-a' | translate}}"> | ||
</div> | ||
</script> | ||
<script type="text/ng-template" id="minQuantity-cellTextEditor"> | ||
<div class="form-editor form-input"> | ||
<input class="form-input" smart-float num-type="integer" required ng-model="MODEL_COL_FIELD" ui-grid-editor ng-class="{'ng-invalid': !grid.appScope.isUniqueQtyForPricelist(row.entity)}"> | ||
</div> | ||
</script> | ||
<script type="text/ng-template" id="list.row.html"> | ||
<div ng-repeat="(colRenderIndex, col) in colContainer.renderedColumns track by col.uid" ui-grid-one-bind-id-grid="rowRenderIndex + '-' + col.uid + '-cell'" class="ui-grid-cell" ng-class="{'ui-grid-row-header-cell': col.isRowHeader }" role="{{col.isRowHeader ? 'rowheader' : 'gridcell' }}" ui-grid-cell></div> | ||
</script> |
Oops, something went wrong.