commit 1c42293e9ea31654cb856b2f99c0309d2e22ac10
parent 00dd50296ae9461e48d310055e567d117cf0f514
Author: Felicitus <felicitus@felicitus.org>
Date: Fri, 2 Oct 2015 20:53:51 +0200
Migrated parts filter and added helper fields for querying the data
Diffstat:
7 files changed, 866 insertions(+), 645 deletions(-)
diff --git a/src/PartKeepr/CoreBundle/DoctrineMigrations/Version20151002183125.php b/src/PartKeepr/CoreBundle/DoctrineMigrations/Version20151002183125.php
@@ -0,0 +1,41 @@
+<?php
+
+namespace PartKeepr\CoreBundle\DoctrineMigrations;
+
+use Doctrine\DBAL\Schema\Schema;
+
+/**
+ * Re-saves all parts in order to re-generate the averagePrice and removals field
+ */
+class Version20151002183125 extends BaseMigration
+{
+ /**
+ * @param Schema $schema
+ */
+ public function up(Schema $schema)
+ {
+ $this->performDatabaseUpgrade();
+
+ $userProviderRepository = $this->getEM()->getRepository(
+ 'PartKeeprPartBundle:Part'
+ );
+
+ $parts = $userProviderRepository->findAll();
+
+ foreach ($parts as $part) {
+ $part->recomputeStockLevels();
+ }
+
+
+ $this->getEM()->flush();
+ }
+
+ /**
+ * @param Schema $schema
+ */
+ public function down(Schema $schema)
+ {
+ // this down() migration is auto-generated, please modify it to your needs
+
+ }
+}
diff --git a/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Part/PartFilterPanel.js b/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Part/PartFilterPanel.js
@@ -1,346 +1,392 @@
/**
* Defines the part filter panel.
- *
- *
+ *
+ *
*/
Ext.define('PartKeepr.PartFilterPanel', {
- extend: 'Ext.form.Panel',
- alias: 'widget.PartFilterPanel',
-
- /**
- * Define a padding of 10px
- */
- bodyPadding: '10px',
-
- /**
- * The items are aligned in a wrappable column layout
- */
- layout: 'column',
-
- /**
- * Automatically scroll the container if the items exceed the container size.
- */
- autoScroll: true,
-
- /**
- * Fixed body background color style
- */
- bodyStyle: 'background:#DBDBDB;',
-
- /**
- * Initializes the component
- */
- initComponent: function () {
-
- // Create the filter fields
- this.createFilterFields();
-
- // Creates the left column of the filter panel
- this.leftColumn = {
- xtype: 'container',
- anchor: '100%',
- layout: 'anchor',
- minWidth: 340,
- style: 'margin-right: 10px',
- columnWidth: 0.5,
- items: [
- this.storageLocationContainer,
- this.categoryFilter,
- this.partsWithoutPrice,
- this.createDateFilter,
- this.partsWithoutStockRemovals,
- this.needsReview
- ]
- };
-
- // Creates the right column of the filter panel
- this.rightColumn = {
- xtype: 'container',
- anchor: '100%',
- minWidth: 340,
- columnWidth: 0.5,
- layout: 'anchor',
- items: [
- this.stockFilter,
- this.distributorOrderNumberFilter,
- this.distributorFilter,
- this.manufacturerFilter,
- this.footprintFilter,
- this.statusFilter,
- this.conditionFilter
- ]
- };
-
- // Apply both columns to this panel
- this.items = [ this.leftColumn, this.rightColumn ];
-
- // Create the reset button
- this.resetButton = Ext.create("Ext.button.Button", {
- text: i18n("Reset"),
- handler: this.onReset,
- iconCls: 'web-icon cancel',
- scope: this
- });
-
- // Create the apply button
- this.applyButton = Ext.create("Ext.button.Button", {
- text: i18n("Apply"),
- iconCls: 'web-icon accept',
- handler: this.onApply,
- scope: this
- });
-
- // Append both buttons to a toolbar
- this.dockedItems = [{
- xtype: 'toolbar',
- enableOverflow: true,
- dock: 'bottom',
- defaults: {minWidth: 100},
- items: [ this.applyButton, this.resetButton ]
- }];
-
- this.callParent();
- },
- /**
- * Applies the parameters from the filter panel to the proxy, then
- * reload the store to refresh the grid.
- *
- * @param none
- * @return nothing
- */
- onApply: function () {
- this.applyFilterParameters(this.store.getProxy().extraParams);
- this.store.currentPage = 1;
- this.store.load({ start: 0});
- },
- /**
- * Resets the fields to their original values, then call onApply()
- * to reload the store.
- */
- onReset: function () {
- this.storageLocationFilter.setValue("");
- this.storageLocationFilterCheckbox.setValue(false);
-
- this.categoryFilter.setValue({ category: 'all'});
- this.stockFilter.setValue({ stock: 'any'});
- this.distributorOrderNumberFilter.setValue("");
-
- this.createDateFilterSelect.setValue("");
- this.createDateField.setValue("");
- this.partsWithoutStockRemovals.setValue(false);
- this.needsReview.setValue(false);
- this.partsWithoutPrice.setValue(false);
-
- this.distributorFilterCombo.setValue("");
- this.distributorFilterCheckbox.setValue(false);
-
- this.manufacturerFilterCombo.setValue("");
- this.manufacturerFilterCheckbox.setValue(false);
+ extend: 'Ext.form.Panel',
+ alias: 'widget.PartFilterPanel',
+
+ /**
+ * Define a padding of 10px
+ */
+ bodyPadding: '10px',
+
+ /**
+ * The items are aligned in a wrappable column layout
+ */
+ layout: 'column',
+
+ /**
+ * Automatically scroll the container if the items exceed the container size.
+ */
+ autoScroll: true,
+
+ /**
+ * Fixed body background color style
+ */
+ bodyStyle: 'background:#DBDBDB;',
+
+ partManager: null,
+ storageLocationFilter: null,
+ storageLocationFilterCheckbox: null,
+ storageLocationContainer: null,
+ categoryFilter: null,
+ stockFilter: null,
+ partsWithoutPrice: null,
+ distributorOrderNumberFilter: null,
+ createDateField: null,
+ createDateFilterSelect: null,
+ createDateFilter: null,
+ partsWithoutStockRemovals: null,
+ needsReview: null,
+ manufacturerFilterCheckbox: null,
+ manufacturerFilterCombo: null,
+ manufacturerFilter: null,
+ distributorFilterCombo: null,
+ distributorFilter: null,
+ footprintFilterCheckbox: null,
+ footprintFilterCombo: null,
+ footprintFilter: null,
+ statusFilter: null,
+ conditionFilter: null,
+
+ /**
+ * Initializes the component
+ */
+ initComponent: function ()
+ {
+
+ // Create the filter fields
+ this.createFilterFields();
+
+ // Creates the left column of the filter panel
+ this.leftColumn = {
+ xtype: 'container',
+ anchor: '100%',
+ layout: 'anchor',
+ minWidth: 340,
+ style: 'margin-right: 10px',
+ columnWidth: 0.5,
+ items: [
+ this.storageLocationContainer,
+ this.categoryFilter,
+ this.partsWithoutPrice,
+ this.createDateFilter,
+ this.partsWithoutStockRemovals,
+ this.needsReview
+ ]
+ };
+
+ // Creates the right column of the filter panel
+ this.rightColumn = {
+ xtype: 'container',
+ anchor: '100%',
+ minWidth: 340,
+ columnWidth: 0.5,
+ layout: 'anchor',
+ items: [
+ this.stockFilter,
+ this.distributorOrderNumberFilter,
+ this.distributorFilter,
+ this.manufacturerFilter,
+ this.footprintFilter,
+ this.statusFilter,
+ this.conditionFilter
+ ]
+ };
+
+ // Apply both columns to this panel
+ this.items = [this.leftColumn, this.rightColumn];
+
+ // Create the reset button
+ this.resetButton = Ext.create("Ext.button.Button", {
+ text: i18n("Reset"),
+ handler: this.onReset,
+ iconCls: 'web-icon cancel',
+ scope: this
+ });
+
+ // Create the apply button
+ this.applyButton = Ext.create("Ext.button.Button", {
+ text: i18n("Apply"),
+ iconCls: 'web-icon accept',
+ handler: this.onApply,
+ scope: this
+ });
+
+ // Append both buttons to a toolbar
+ this.dockedItems = [
+ {
+ xtype: 'toolbar',
+ enableOverflow: true,
+ dock: 'bottom',
+ defaults: {minWidth: 100},
+ items: [this.applyButton, this.resetButton]
+ }
+ ];
+
+ this.callParent();
+ },
+ /**
+ * Applies the parameters from the filter panel to the proxy, then
+ * reload the store to refresh the grid.
+ *
+ * @param none
+ * @return nothing
+ */
+ onApply: function ()
+ {
+ var filters = this.getFilters();
+
+ this.store.clearFilter(true);
+
+ if (filters.length !== 0) {
+ this.store.addFilter(this.getFilters(), true);
+ }
+
+ this.store.load();
+ },
+ /**
+ * Resets the fields to their original values, then call onApply()
+ * to reload the store.
+ */
+ onReset: function ()
+ {
+ this.storageLocationFilter.setValue("");
+ this.storageLocationFilterCheckbox.setValue(false);
+
+ this.categoryFilter.setValue({category: 'all'});
+ this.stockFilter.setValue({stock: 'any'});
+ this.distributorOrderNumberFilter.setValue("");
+
+ this.createDateFilterSelect.setValue("");
+ this.createDateField.setValue("");
+ this.partsWithoutStockRemovals.setValue(false);
+ this.needsReview.setValue(false);
+ this.partsWithoutPrice.setValue(false);
+
+ this.distributorFilterCombo.setValue("");
+ this.distributorFilterCheckbox.setValue(false);
+
+ this.manufacturerFilterCombo.setValue("");
+ this.manufacturerFilterCheckbox.setValue(false);
this.footprintFilterCombo.setValue("");
this.footprintFilterCheckbox.setValue(false);
-
- this.statusFilter.setValue("");
-
- this.conditionFilter.setValue("");
-
- this.onApply();
- },
- /**
- * Creates the filter fields required for this filter panel
- */
- createFilterFields: function () {
-
- // Create the storage location filter field
- this.storageLocationFilter = Ext.create("PartKeepr.StorageLocationComboBox", {
- flex: 1,
- forceSelection: true,
- listeners: {
- select: function () {
- this.storageLocationFilterCheckbox.setValue(true);
- },
- scope: this
- }
- });
-
- this.storageLocationFilterCheckbox = Ext.create("Ext.form.field.Checkbox", {
- width: "20px",
- listeners: {
- change: function (obj, value) {
-
- if (!value) {
- this.storageLocationFilter.setValue("");
- }
- },
- scope: this
- }
- });
-
- this.storageLocationContainer = Ext.create("Ext.form.FieldContainer", {
- layout: 'hbox',
- items: [ this.storageLocationFilterCheckbox, this.storageLocationFilter ],
- anchor: '100%',
- minWidth: 300,
- fieldLabel: i18n("Storage Location")
- });
-
- // Create the category scope field
- this.categoryFilter = Ext.create("Ext.form.RadioGroup", {
- fieldLabel: i18n("Category Scope"),
- columns: 1,
- items: [{
- boxLabel: i18n("All Subcategories"),
- name: 'category',
- inputValue: "all",
- checked: true
- },
- {
- boxLabel: i18n("Selected Category"),
- name: 'category',
- inputValue: "selected"
- }]
- });
-
- // Create the stock level filter field
- this.stockFilter = Ext.create("Ext.form.RadioGroup", {
- fieldLabel: i18n("Stock Mode"),
- columns: 1,
- items: [{
- boxLabel: i18n("Any Stock Level"),
- name: 'stock',
- inputValue: "any",
- checked: true
- },{
- boxLabel: i18n("Stock Level = 0"),
- name: 'stock',
- inputValue: "zero"
- },{
- boxLabel: i18n("Stock Level > 0"),
- name: 'stock',
- inputValue: "nonzero"
- },{
- boxLabel: i18n("Stock Level < Minimum Stock Level"),
- name: 'stock',
- inputValue: "below"
- }]
- });
-
- this.partsWithoutPrice = Ext.create("Ext.form.field.Checkbox", {
- fieldLabel: i18n("Item Price"),
- boxLabel: i18n("Show Parts without Price only")
- });
-
- this.distributorOrderNumberFilter = Ext.create("Ext.form.field.Text", {
- fieldLabel: i18n("Order Number"),
- anchor: '100%'
- });
-
- this.createDateField = Ext.create("Ext.form.field.Date", {
- flex: 1
- });
-
- var filter = Ext.create('Ext.data.Store', {
- fields: ['type', 'name'],
- data : [
- {"type":"<", "name":"before"},
- {"type":">", "name":"after"},
- {"type":"=", "name":"on"},
- {"type":"", "name": "- none -"}
- ]
- });
-
- this.createDateFilterSelect = Ext.create('Ext.form.ComboBox', {
- store: filter,
- queryMode: 'local',
- forceSelection: true,
- editable: false,
- width: 60,
- value: '',
- triggerAction: 'all',
- displayField: 'name',
- valueField: 'type'
- });
-
- this.createDateFilter = {
- xtype: 'fieldcontainer',
- anchor: '100%',
- fieldLabel: i18n("Create date"),
- layout: 'hbox',
- border: false,
- items: [ this.createDateFilterSelect, this.createDateField ]
- };
-
- this.partsWithoutStockRemovals = Ext.create("Ext.form.field.Checkbox", {
- fieldLabel: i18n("Stock Settings"),
- boxLabel: i18n("Show Parts without stock removals only")
- });
-
- this.needsReview = Ext.create("Ext.form.field.Checkbox", {
- fieldLabel: i18n("Needs Review"),
- boxLabel: i18n("Show Parts that need to reviewed only")
- });
-
- this.manufacturerFilterCheckbox = Ext.create("Ext.form.field.Checkbox", {
- width: "20px",
- listeners: {
- change: function (obj, value) {
-
- if (!value) {
- this.manufacturerFilterCombo.setValue("");
- }
- },
- scope: this
- }
- });
-
- this.manufacturerFilterCombo = Ext.create("PartKeepr.ManufacturerComboBox", {
- flex: 1,
- listeners: {
- select: function () {
- this.manufacturerFilterCheckbox.setValue(true);
- },
- scope: this
- }
- });
-
- this.manufacturerFilter = Ext.create("Ext.form.FieldContainer", {
- layout: 'hbox',
- items: [ this.manufacturerFilterCheckbox, this.manufacturerFilterCombo ],
- fieldLabel: i18n("Manufacturer")
- });
-
- this.distributorFilterCheckbox = Ext.create("Ext.form.field.Checkbox", {
- width: "20px",
- listeners: {
- change: function (obj, value) {
- if (!value) {
- this.distributorFilterCombo.setValue("");
- }
- },
- scope: this
- }
- });
-
- this.distributorFilterCombo = Ext.create("PartKeepr.DistributorComboBox",{
- flex: 1,
- listeners: {
- select: function () {
- this.distributorFilterCheckbox.setValue(true);
- },
- scope: this
- }
- });
-
- this.distributorFilter = Ext.create("Ext.form.FieldContainer", {
- layout: 'hbox',
- items: [ this.distributorFilterCheckbox, this.distributorFilterCombo ],
- fieldLabel: i18n("Distributor")
- });
+
+ this.statusFilter.setValue("");
+
+ this.conditionFilter.setValue("");
+
+ this.onApply();
+ },
+ /**
+ * Creates the filter fields required for this filter panel
+ */
+ createFilterFields: function ()
+ {
+
+ // Create the storage location filter field
+ this.storageLocationFilter = Ext.create("PartKeepr.StorageLocationComboBox", {
+ flex: 1,
+ forceSelection: true,
+ listeners: {
+ select: function ()
+ {
+ this.storageLocationFilterCheckbox.setValue(true);
+ },
+ scope: this
+ }
+ });
+
+ this.storageLocationFilterCheckbox = Ext.create("Ext.form.field.Checkbox", {
+ width: "20px",
+ listeners: {
+ change: function (obj, value)
+ {
+
+ if (!value) {
+ this.storageLocationFilter.setValue("");
+ }
+ },
+ scope: this
+ }
+ });
+
+ this.storageLocationContainer = Ext.create("Ext.form.FieldContainer", {
+ layout: 'hbox',
+ items: [this.storageLocationFilterCheckbox, this.storageLocationFilter],
+ anchor: '100%',
+ minWidth: 300,
+ fieldLabel: i18n("Storage Location")
+ });
+
+ // Create the category scope field
+ this.categoryFilter = Ext.create("Ext.form.RadioGroup", {
+ fieldLabel: i18n("Category Scope"),
+ columns: 1,
+ items: [
+ {
+ boxLabel: i18n("All Subcategories"),
+ name: 'category',
+ inputValue: "all",
+ checked: true
+ },
+ {
+ boxLabel: i18n("Selected Category"),
+ name: 'category',
+ inputValue: "selected"
+ }
+ ]
+ });
+
+ // Create the stock level filter field
+ this.stockFilter = Ext.create("Ext.form.RadioGroup", {
+ fieldLabel: i18n("Stock Mode"),
+ columns: 1,
+ items: [
+ {
+ boxLabel: i18n("Any Stock Level"),
+ name: 'stock',
+ inputValue: "any",
+ checked: true
+ }, {
+ boxLabel: i18n("Stock Level = 0"),
+ name: 'stock',
+ inputValue: "zero"
+ }, {
+ boxLabel: i18n("Stock Level > 0"),
+ name: 'stock',
+ inputValue: "nonzero"
+ }, {
+ boxLabel: i18n("Stock Level < Minimum Stock Level"),
+ name: 'stock',
+ inputValue: "below"
+ }
+ ]
+ });
+
+ this.partsWithoutPrice = Ext.create("Ext.form.field.Checkbox", {
+ fieldLabel: i18n("Item Price"),
+ boxLabel: i18n("Show Parts without Price only")
+ });
+
+ this.distributorOrderNumberFilter = Ext.create("Ext.form.field.Text", {
+ fieldLabel: i18n("Order Number"),
+ anchor: '100%'
+ });
+
+ this.createDateField = Ext.create("Ext.form.field.Date", {
+ flex: 1
+ });
+
+ var filter = Ext.create('Ext.data.Store', {
+ fields: ['type', 'name'],
+ data: [
+ {"type": "<", "name": "before"},
+ {"type": ">", "name": "after"},
+ {"type": "", "name": "- none -"}
+ ]
+ });
+
+ this.createDateFilterSelect = Ext.create('Ext.form.ComboBox', {
+ store: filter,
+ queryMode: 'local',
+ forceSelection: true,
+ editable: false,
+ width: 60,
+ value: '',
+ triggerAction: 'all',
+ displayField: 'name',
+ valueField: 'type'
+ });
+
+ this.createDateFilter = {
+ xtype: 'fieldcontainer',
+ anchor: '100%',
+ fieldLabel: i18n("Create date"),
+ layout: 'hbox',
+ border: false,
+ items: [this.createDateFilterSelect, this.createDateField]
+ };
+
+ this.partsWithoutStockRemovals = Ext.create("Ext.form.field.Checkbox", {
+ fieldLabel: i18n("Stock Settings"),
+ boxLabel: i18n("Show Parts without stock removals only")
+ });
+
+ this.needsReview = Ext.create("Ext.form.field.Checkbox", {
+ fieldLabel: i18n("Needs Review"),
+ boxLabel: i18n("Show Parts that need to reviewed only")
+ });
+
+ this.manufacturerFilterCheckbox = Ext.create("Ext.form.field.Checkbox", {
+ width: "20px",
+ listeners: {
+ change: function (obj, value)
+ {
+
+ if (!value) {
+ this.manufacturerFilterCombo.setValue("");
+ }
+ },
+ scope: this
+ }
+ });
+
+ this.manufacturerFilterCombo = Ext.create("PartKeepr.ManufacturerComboBox", {
+ flex: 1,
+ listeners: {
+ select: function ()
+ {
+ this.manufacturerFilterCheckbox.setValue(true);
+ },
+ scope: this
+ }
+ });
+
+ this.manufacturerFilter = Ext.create("Ext.form.FieldContainer", {
+ layout: 'hbox',
+ items: [this.manufacturerFilterCheckbox, this.manufacturerFilterCombo],
+ fieldLabel: i18n("Manufacturer")
+ });
+
+ this.distributorFilterCheckbox = Ext.create("Ext.form.field.Checkbox", {
+ width: "20px",
+ listeners: {
+ change: function (obj, value)
+ {
+ if (!value) {
+ this.distributorFilterCombo.setValue("");
+ }
+ },
+ scope: this
+ }
+ });
+
+ this.distributorFilterCombo = Ext.create("PartKeepr.DistributorComboBox", {
+ flex: 1,
+ listeners: {
+ select: function ()
+ {
+ this.distributorFilterCheckbox.setValue(true);
+ },
+ scope: this
+ }
+ });
+
+ this.distributorFilter = Ext.create("Ext.form.FieldContainer", {
+ layout: 'hbox',
+ items: [this.distributorFilterCheckbox, this.distributorFilterCombo],
+ fieldLabel: i18n("Distributor")
+ });
this.footprintFilterCheckbox = Ext.create("Ext.form.field.Checkbox", {
width: "20px",
listeners: {
- change: function (obj, value) {
+ change: function (obj, value)
+ {
if (!value) {
this.footprintFilterCombo.setValue("");
}
@@ -349,10 +395,11 @@ Ext.define('PartKeepr.PartFilterPanel', {
}
});
- this.footprintFilterCombo = Ext.create("PartKeepr.FootprintComboBox", {
+ this.footprintFilterCombo = Ext.create("PartKeepr.FootprintComboBox", {
flex: 1,
listeners: {
- select: function () {
+ select: function ()
+ {
this.footprintFilterCheckbox.setValue(true);
},
scope: this
@@ -361,76 +408,163 @@ Ext.define('PartKeepr.PartFilterPanel', {
this.footprintFilter = Ext.create("Ext.form.FieldContainer", {
layout: 'hbox',
- items: [ this.footprintFilterCheckbox, this.footprintFilterCombo ],
+ items: [this.footprintFilterCheckbox, this.footprintFilterCombo],
fieldLabel: i18n("Footprint")
});
-
- /** **/
-
- this.statusFilter = Ext.create("Ext.form.field.Text", {
- fieldLabel: i18n("Status"),
- anchor: '100%'
- });
-
- this.conditionFilter = Ext.create("Ext.form.field.Text", {
- fieldLabel: i18n("Condition"),
- anchor: '100%'
- });
-
- },
- /**
- * Applies the filter parameters to the passed extraParams object.
- * @param extraParams An object containing the extraParams from a proxy.
- */
- applyFilterParameters: function (extraParams) {
- extraParams.withoutPrice = this.partsWithoutPrice.getValue();
- extraParams.categoryScope = this.categoryFilter.getValue().category;
- extraParams.stockMode = this.stockFilter.getValue().stock;
- extraParams.distributorOrderNumber = this.distributorOrderNumberFilter.getValue();
- /**
- * Get the raw (=text) value. I really wish that ExtJS would handle selected values (from a store)
- * distinct than entered values.
- */
- if (this.storageLocationFilter.getRawValue() !== "") {
- extraParams.storageLocation = this.storageLocationFilter.getValue();
- } else {
- delete extraParams.storageLocation;
- }
-
- if (this.manufacturerFilterCombo.getRawValue() !== "") {
- extraParams.manufacturer = this.manufacturerFilterCombo.getValue();
- } else {
- delete extraParams.manufacturer;
- }
-
- if (this.distributorFilterCombo.getRawValue() !== "") {
- extraParams.distributor = this.distributorFilterCombo.getValue();
- } else {
- delete extraParams.distributor;
- }
-
- if (this.footprintFilterCombo.getRawValue() !== "") {
- extraParams.footprint = this.footprintFilterCombo.getValue();
+
+ /** **/
+
+ this.statusFilter = Ext.create("Ext.form.field.Text", {
+ fieldLabel: i18n("Status"),
+ anchor: '100%'
+ });
+
+ this.conditionFilter = Ext.create("Ext.form.field.Text", {
+ fieldLabel: i18n("Condition"),
+ anchor: '100%'
+ });
+
+ },
+ /**
+ * Applies the filter parameters to the passed extraParams object.
+ * @param extraParams An object containing the extraParams from a proxy.
+ */
+ getFilters: function ()
+ {
+ var filters = [];
+
+ if (this.storageLocationFilterCheckbox.getValue() === true) {
+ filters.push(Ext.create("Ext.util.Filter", {
+ property: 'storageLocation',
+ operator: "=",
+ value: this.storageLocationFilter.getValue()
+ }));
+ }
+
+ if (this.categoryFilter.getValue().category === "all") {
+ if (this.partManager.getSelectedCategory() !== null) {
+ filters.push(Ext.create("Ext.util.Filter", {
+ id: 'categoryFilter',
+ property: 'category',
+ operator: 'IN',
+ value: this.partManager.getChildrenIds(this.partManager.getSelectedCategory())
+ }));
+ }
} else {
- delete extraParams.footprint;
+ filters.push(Ext.create("Ext.util.Filter", {
+ id: 'categoryFilter',
+ property: 'category',
+ operator: '=',
+ value: this.partManager.getSelectedCategory()
+ }));
+ }
+
+ if (this.partsWithoutPrice.getValue() === true) {
+ filters.push(Ext.create("Ext.util.Filter", {
+ property: 'averagePrice',
+ operator: '=',
+ value: 0
+ }));
+ }
+
+ if (this.createDateFilterSelect.getValue() !== "") {
+ filters.push(Ext.create("Ext.util.Filter", {
+ property: 'createDate',
+ operator: this.createDateFilterSelect.getValue(),
+ value: this.createDateField.getValue()
+ }));
+ }
+
+ if (this.partsWithoutStockRemovals.getValue() === true) {
+ filters.push(Ext.create("Ext.util.Filter", {
+ property: 'removals',
+ operator: "=",
+ value: false
+ }));
+ }
+
+ if (this.needsReview.getValue() === true) {
+ filters.push(Ext.create("Ext.util.Filter", {
+ property: 'needsReview',
+ operator: "=",
+ value: true
+ }));
+ }
+
+
+ if (this.stockFilter.getValue().stock !== "any") {
+ switch (this.stockFilter.getValue().stock) {
+ case "zero":
+ filters.push(Ext.create("Ext.util.Filter", {
+ property: 'stockLevel',
+ operator: "=",
+ value: 0
+ }));
+ break;
+ case "nonzero":
+ filters.push(Ext.create("Ext.util.Filter", {
+ property: 'stockLevel',
+ operator: ">",
+ value: 0
+ }));
+ break;
+ case "below":
+ filters.push(Ext.create("Ext.util.Filter", {
+ property: 'lowStock',
+ operator: "=",
+ value: true
+ }));
+ break;
+ }
+ }
+
+ if (this.distributorOrderNumberFilter.getValue() !== "") {
+ filters.push(Ext.create("Ext.util.Filter", {
+ property: 'distributors.orderNumber',
+ operator: "LIKE",
+ value: "%" + this.distributorOrderNumberFilter.getValue() + "%"
+ }));
+ }
+
+ if (this.distributorFilterCheckbox.getValue() === true) {
+ filters.push(Ext.create("Ext.util.Filter", {
+ property: 'distributors.distributor',
+ operator: "=",
+ value: this.distributorFilterCombo.getValue()
+ }));
+ }
+
+ if (this.manufacturerFilterCheckbox.getValue() === true) {
+ filters.push(Ext.create("Ext.util.Filter", {
+ property: 'manufacturers.manufacturer',
+ operator: "=",
+ value: this.manufacturerFilterCombo.getValue()
+ }));
+ }
+
+ if (this.footprintFilterCheckbox.getValue() === true) {
+ filters.push(Ext.create("Ext.util.Filter", {
+ property: 'footprint',
+ operator: "=",
+ value: this.footprintFilterCombo.getValue()
+ }));
+ }
+
+ if (this.statusFilter.getValue() !== "") {
+ filters.push(Ext.create("Ext.util.Filter", {
+ property: 'status',
+ operator: "LIKE",
+ value: "%" + this.statusFilter.getValue() + "%"
+ }));
+ }
+
+ if (this.conditionFilter.getValue() !== "") {
+ filters.push(Ext.create("Ext.util.Filter", {
+ property: 'condition',
+ operator: "LIKE",
+ value: "%" + this.conditionFilter.getValue() + "%"
+ }));
}
-
- extraParams.status = this.statusFilter.getValue();
- extraParams.condition = this.conditionFilter.getValue();
-
-
- extraParams.createDateRestriction = this.createDateFilterSelect.getValue();
- var createDate = Ext.util.Format.date(this.createDateField.getValue(), "Y-m-d H:i:s");
-
- if (createDate !== "") {
- extraParams.createDate = createDate;
- } else {
- delete extraParams.createDate;
- }
-
- extraParams.withoutStockRemovals = this.partsWithoutStockRemovals.getValue();
-
- extraParams.needsReview = this.needsReview.getValue();
- }
-
+ return filters;
+ }
});
diff --git a/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Part/PartsGrid.js b/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Part/PartsGrid.js
@@ -57,6 +57,7 @@ Ext.define('PartKeepr.PartsGrid', {
multiSelect: true,
autoScroll: false,
invalidateScrollerOnRefresh: true,
+ titleProperty: 'name',
initComponent: function ()
{
@@ -175,7 +176,7 @@ Ext.define('PartKeepr.PartsGrid', {
key: 'x',
ctrl: false,
alt: true,
- fn: function (e)
+ fn: function ()
{
var searchBox = this.searchField;
if (Ext.get(document).activeElement != searchBox) {
@@ -190,7 +191,7 @@ Ext.define('PartKeepr.PartsGrid', {
/**
* Called when an item was selected. Enables/disables the delete button.
*/
- _updateAddTemplateButton: function (selectionModel, record)
+ _updateAddTemplateButton: function ()
{
/* Right now, we support delete on a single record only */
if (this.getSelectionModel().getCount() == 1) {
@@ -446,9 +447,6 @@ Ext.define('PartKeepr.PartsGrid', {
var confirmText = "";
var headerText = "";
- var n = e.value.indexOf("+");
-
-
switch (mode) {
case "removal":
confirmText = sprintf(
@@ -520,8 +518,9 @@ Ext.define('PartKeepr.PartsGrid', {
{
var mode = this.getStockChangeMode(e.value);
var value = Math.abs(parseInt(e.value));
+ var call;
- if (e.value == 0) {
+ if (e.value === 0) {
return;
}
@@ -535,10 +534,12 @@ Ext.define('PartKeepr.PartsGrid', {
case "fixed":
call = "setStock";
break;
+ default:
+ return;
}
e.record.callPutAction(call, {
- quantity: e.value
+ quantity: value
}, Ext.bind(this.reloadPart, this, [e]));
},
/**
@@ -551,7 +552,7 @@ Ext.define('PartKeepr.PartsGrid', {
/**
* Load the part from the database.
*/
- loadPart: function (id, opts)
+ loadPart: function (id)
{
PartKeepr.PartBundle.Entity.Part.load(id, {
scope: this,
@@ -561,7 +562,7 @@ Ext.define('PartKeepr.PartsGrid', {
/**
* Callback after the part is loaded
*/
- onPartLoaded: function (record, opts)
+ onPartLoaded: function (record)
{
var rec = this.store.findRecord("id", record.getId());
if (rec) {
diff --git a/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Part/PartsManager.js b/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Part/PartsManager.js
@@ -22,6 +22,8 @@ Ext.define('PartKeepr.PartManager', {
*/
compactLayout: false,
+ selectedCategory: null,
+
initComponent: function ()
{
@@ -33,6 +35,10 @@ Ext.define('PartKeepr.PartManager', {
groupField: 'categoryPath',
sorters: [
{
+ property: 'category.categoryPath',
+ direction: 'ASC'
+ },
+ {
property: 'name',
direction: 'ASC'
}
@@ -114,7 +120,8 @@ Ext.define('PartKeepr.PartManager', {
split: true,
collapsed: true,
collapsible: true,
- store: this.store
+ store: this.store,
+ partManager: this
});
if (this.compactLayout) {
@@ -162,7 +169,10 @@ Ext.define('PartKeepr.PartManager', {
*/
onCategoryClick: function (tree, record)
{
+ this.selectedCategory = record;
+
var filter = Ext.create("Ext.util.Filter", {
+ id: 'categoryFilter',
property: 'category',
operator: 'IN',
value: this.getChildrenIds(record)
@@ -170,6 +180,10 @@ Ext.define('PartKeepr.PartManager', {
this.store.addFilter(filter);
},
+ getSelectedCategory: function ()
+ {
+ return this.selectedCategory;
+ },
/**
* Returns the ID for this node and all child nodes
*
@@ -277,7 +291,7 @@ Ext.define('PartKeepr.PartManager', {
{
var data = rec.getData();
- newItem = Ext.create("PartKeepr.PartBundle.Entity.Part");
+ var newItem = Ext.create("PartKeepr.PartBundle.Entity.Part");
newItem.set(data);
newItem.setAssociationData(rec.getAssociationData());
@@ -322,7 +336,7 @@ Ext.define('PartKeepr.PartManager', {
defaults.partUnit = defaultPartUnit.getId();
defaults.category = this.grid.currentCategory;
- record = Ext.create("PartKeepr.PartBundle.Entity.Part", defaults);
+ var record = Ext.create("PartKeepr.PartBundle.Entity.Part", defaults);
// Inject the defaults to the editor, so the editor can create a new item on its own
j.editor.partDefaults = defaults;
@@ -351,25 +365,25 @@ Ext.define('PartKeepr.PartManager', {
*/
onItemSelect: function ()
{
- if (this.grid.getSelection().length > 1) {
- this.detailPanel.collapse();
- this.tree.syncButton.disable();
- } else {
- if (this.grid.getSelection().length == 1) {
- var selection = this.grid.getSelection();
-
- var r = selection[0];
-
- this.detailPanel.setActiveTab(this.detail);
- this.detailPanel.expand();
- this.detail.setValues(r);
- this.stockLevel.part = r.getId();
-
- this.tree.syncButton.enable();
- } else {
- this.tree.syncButton.disable();
- }
- }
+ if (this.grid.getSelection().length > 1) {
+ this.detailPanel.collapse();
+ this.tree.syncButton.disable();
+ } else {
+ if (this.grid.getSelection().length == 1) {
+ var selection = this.grid.getSelection();
+
+ var r = selection[0];
+
+ this.detailPanel.setActiveTab(this.detail);
+ this.detailPanel.expand();
+ this.detail.setValues(r);
+ this.stockLevel.part = r.getId();
+
+ this.tree.syncButton.enable();
+ } else {
+ this.tree.syncButton.disable();
+ }
+ }
},
/**
diff --git a/src/PartKeepr/FrontendBundle/Resources/public/js/form/field/SearchField.js b/src/PartKeepr/FrontendBundle/Resources/public/js/form/field/SearchField.js
@@ -1,6 +1,6 @@
/**
* Defines a search field, which automatically hooks into the passed store.
- *
+ *
* The "clear" trigger is shown only when text is entered.
*/
Ext.define('PartKeepr.form.field.SearchField', {
@@ -21,12 +21,12 @@ Ext.define('PartKeepr.form.field.SearchField', {
}
},
- hasSearch : false,
-
- /**
- * Defines the parameter name which is being passed to the store's proxy.
- */
- targetField : 'query',
+ hasSearch: false,
+
+ /**
+ * Defines the parameter name which is being passed to the store's proxy.
+ */
+ targetField: 'query',
filter: null,
@@ -36,11 +36,12 @@ Ext.define('PartKeepr.form.field.SearchField', {
scope: 'this'
}
},
-
- /**
- * Initializes the component. Binds the enter key to startSearch.
- */
- initComponent: function(){
+
+ /**
+ * Initializes the component. Binds the enter key to startSearch.
+ */
+ initComponent: function ()
+ {
this.callParent(arguments);
this.filter = Ext.create("Ext.util.Filter", {
@@ -55,7 +56,8 @@ Ext.define('PartKeepr.form.field.SearchField', {
* Enter: Starts the search
* Escape: Removes the search and clears the field contents
*/
- keyHandler: function (field, e) {
+ keyHandler: function (field, e)
+ {
switch (e.getKey()) {
case e.ENTER:
this.startSearch();
@@ -65,13 +67,13 @@ Ext.define('PartKeepr.form.field.SearchField', {
break;
}
},
- /**
- * Resets the search field to empty and re-triggers the store to load the matching records.
- */
- resetSearch: function () {
- var me = this,
- store = me.store,
- val;
+ /**
+ * Resets the search field to empty and re-triggers the store to load the matching records.
+ */
+ resetSearch: function ()
+ {
+ var me = this,
+ store = me.store;
me.setValue('');
@@ -80,34 +82,36 @@ Ext.define('PartKeepr.form.field.SearchField', {
store.removeFilter(this.filter);
store.currentPage = 1;
- store.load({ start: 0 });
+ store.load({start: 0});
me.hasSearch = false;
this.getTrigger("clear").hide();
}
- },
- /**
- * Starts the search with the entered value.
- */
- startSearch: function () {
- var me = this,
+ },
+ /**
+ * Starts the search with the entered value.
+ */
+ startSearch: function ()
+ {
+ var me = this,
store = me.store,
value = me.getValue();
-
+
if (value.length < 1) {
me.resetSearch();
return;
}
- this.filter.setValue("%"+value+"%");
+ this.filter.setValue("%" + value + "%");
store.addFilter(this.filter);
store.currentPage = 1;
- store.load({ start: 0 });
-
+ store.load({start: 0});
+
me.hasSearch = true;
this.getTrigger("clear").show();
- },
- setStore: function (store) {
+ },
+ setStore: function (store)
+ {
this.store = store;
}
-});-
\ No newline at end of file
+});
diff --git a/src/PartKeepr/PartBundle/Entity/Part.php b/src/PartKeepr/PartBundle/Entity/Part.php
@@ -228,20 +228,10 @@ class Part extends BaseEntity
private $removals = false;
/**
- * @return mixed
- */
- public function hetRemovals()
- {
- return $this->removals;
- }
-
- /**
- * @param mixed $removals
+ * @ORM\Column(type="boolean",nullable=false)
+ * @var boolean
*/
- public function setRemovals($removals = false)
- {
- $this->removals = $removals;
- }
+ private $lowStock = false;
public function __construct()
{
@@ -250,18 +240,43 @@ class Part extends BaseEntity
$this->parameters = new ArrayCollection();
$this->images = new ArrayCollection();
$this->attachments = new ArrayCollection();
+ $this->stockLevels = new ArrayCollection();
$this->setCreateDate(new \DateTime());
$this->setNeedsReview(false);
}
/**
- * Sets the name for this part
+ * Sets the create date for this part
*
- * @param string $name The part's name
+ * @param \DateTime $dateTime The create date+time
*/
- public function setName($name)
+ private function setCreateDate(\DateTime $dateTime)
{
- $this->name = $name;
+ $this->createDate = $dateTime;
+ }
+
+ /**
+ * @return boolean
+ */
+ public function isLowStock()
+ {
+ return $this->lowStock;
+ }
+
+ /**
+ * @param boolean $lowStock
+ */
+ public function setLowStock($lowStock)
+ {
+ $this->lowStock = $lowStock;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function hasRemovals()
+ {
+ return $this->removals;
}
/**
@@ -275,13 +290,13 @@ class Part extends BaseEntity
}
/**
- * Sets the internal part number for this part
+ * Sets the name for this part
*
- * @param string $partNumber
+ * @param string $name The part's name
*/
- public function setInternalPartNumber($partNumber)
+ public function setName($name)
{
- $this->internalPartNumber = $partNumber;
+ $this->name = $name;
}
/**
@@ -295,13 +310,13 @@ class Part extends BaseEntity
}
/**
- * Sets the description for this part
+ * Sets the internal part number for this part
*
- * @param string $description The part's short description
+ * @param string $partNumber
*/
- public function setDescription($description)
+ public function setInternalPartNumber($partNumber)
{
- $this->description = $description;
+ $this->internalPartNumber = $partNumber;
}
/**
@@ -315,14 +330,13 @@ class Part extends BaseEntity
}
/**
- * Sets the part unit
- *
- * @param PartMeasurementUnit $partUnit The part unit object to set
+ * Sets the description for this part
*
+ * @param string $description The part's short description
*/
- public function setPartUnit(PartMeasurementUnit $partUnit)
+ public function setDescription($description)
{
- $this->partUnit = $partUnit;
+ $this->description = $description;
}
/**
@@ -338,25 +352,14 @@ class Part extends BaseEntity
}
/**
- * Sets the average price for this unit
- *
- * @todo Is this actually used?
+ * Sets the part unit
*
- * @param float $price The price to set
- */
- public function setAveragePrice($price)
- {
- $this->averagePrice = $price;
- }
-
- /**
- * Sets the review flag
+ * @param PartMeasurementUnit $partUnit The part unit object to set
*
- * @param boolean $bReview True if the part needs review, false otherwise
*/
- public function setNeedsReview($bReview)
+ public function setPartUnit(PartMeasurementUnit $partUnit)
{
- $this->needsReview = $bReview;
+ $this->partUnit = $partUnit;
}
/**
@@ -370,13 +373,13 @@ class Part extends BaseEntity
}
/**
- * Sets the condition for this part
+ * Sets the review flag
*
- * @param string $partCondition The part's condition
+ * @param boolean $bReview True if the part needs review, false otherwise
*/
- public function setPartCondition($partCondition)
+ public function setNeedsReview($bReview)
{
- $this->partCondition = $partCondition;
+ $this->needsReview = $bReview;
}
/**
@@ -389,36 +392,14 @@ class Part extends BaseEntity
return $this->partCondition;
}
-
- /**
- * Set the minimum stock level for this part
- *
- * Only positive values are allowed.
- *
- * @param int $minStockLevel A minimum stock level, only values >= 0 are allowed.
- *
- * @throws \PartKeepr\Util\Exceptions\OutOfRangeException If the passed stock level is not in range (>=0)
- */
- public function setMinStockLevel($minStockLevel)
- {
- $minStockLevel = intval($minStockLevel);
-
- if ($minStockLevel < 0) {
- $exception = new OutOfRangeException(PartKeepr::i18n("Minimum Stock Level is out of range"));
- $exception->setDetail(PartKeepr::i18n("The minimum stock level must be 0 or higher"));
- throw $exception;
- }
- $this->minStockLevel = $minStockLevel;
- }
-
/**
- * Returns the minimum stock level
+ * Sets the condition for this part
*
- * @return int
+ * @param string $partCondition The part's condition
*/
- public function getMinStockLevel()
+ public function setPartCondition($partCondition)
{
- return $this->minStockLevel;
+ $this->partCondition = $partCondition;
}
/**
@@ -437,43 +418,11 @@ class Part extends BaseEntity
}
/**
- * Sets the category for this part
- *
- * @param PartCategory $category The category
- */
- public function setCategory(PartCategory $category)
- {
- $this->category = $category;
- }
-
- /**
- * Returns the assigned category
- *
- * @return PartCategory
- */
- public function getCategory()
- {
- return $this->category;
- }
-
- /**
- * Sets the storage location for this part
- *
- * @param \PartKeepr\StorageLocationBundle\Entity\StorageLocation $storageLocation The storage location
- */
- public function setStorageLocation(StorageLocation $storageLocation)
- {
- $this->storageLocation = $storageLocation;
- }
-
- /**
- * Returns the storage location for this part
- *
- * @return \PartKeepr\StorageLocationBundle\Entity\StorageLocation $storageLocation The storage location
+ * Retrieves the footprint
*/
- public function getStorageLocation()
+ public function getFootprint()
{
- return $this->storageLocation;
+ return $this->footprint;
}
/**
@@ -487,11 +436,13 @@ class Part extends BaseEntity
}
/**
- * Retrieves the footprint
+ * Returns the comment for this part
+ *
+ * @return string The comment
*/
- public function getFootprint()
+ public function getComment()
{
- return $this->footprint;
+ return $this->comment;
}
/**
@@ -505,16 +456,6 @@ class Part extends BaseEntity
}
/**
- * Returns the comment for this part
- *
- * @return string The comment
- */
- public function getComment()
- {
- return $this->comment;
- }
-
- /**
* Returns the distributors array
*
* @return ArrayCollection the distributors
@@ -565,23 +506,23 @@ class Part extends BaseEntity
}
/**
- * Sets the create date for this part
+ * Returns the create date
*
- * @param \DateTime $dateTime The create date+time
+ * @return \DateTime The create date+time
*/
- private function setCreateDate(\DateTime $dateTime)
+ public function getCreateDate()
{
- $this->createDate = $dateTime;
+ return $this->createDate;
}
/**
- * Returns the create date
+ * Returns the status for this part.
*
- * @return \DateTime The create date+time
+ * @return string The status
*/
- public function getCreateDate()
+ public function getStatus()
{
- return $this->createDate;
+ return $this->status;
}
/**
@@ -596,13 +537,22 @@ class Part extends BaseEntity
}
/**
- * Returns the status for this part.
+ * Checks if the requirements for database persisting are given.
*
- * @return string The status
+ * @throws CategoryNotAssignedException Thrown if no category is set
+ * @throws StorageLocationNotAssignedException Thrown if no storage location is set
+ *
+ * @ORM\PrePersist
*/
- public function getStatus()
+ public function onPrePersist()
{
- return $this->status;
+ $this->executeSaveListener();
+ }
+
+ public function executeSaveListener()
+ {
+ $this->checkCategoryConsistency();
+ $this->checkStorageLocationConsistency();
}
/**
@@ -618,6 +568,26 @@ class Part extends BaseEntity
}
/**
+ * Returns the assigned category
+ *
+ * @return PartCategory
+ */
+ public function getCategory()
+ {
+ return $this->category;
+ }
+
+ /**
+ * Sets the category for this part
+ *
+ * @param PartCategory $category The category
+ */
+ public function setCategory(PartCategory $category)
+ {
+ $this->category = $category;
+ }
+
+ /**
* Checks if the part storage location is set.
*
* @throws StorageLocationNotAssignedException
@@ -630,59 +600,99 @@ class Part extends BaseEntity
}
/**
- * Checks if the requirements for database persisting are given.
+ * Returns the storage location for this part
*
- * @throws CategoryNotAssignedException Thrown if no category is set
- * @throws StorageLocationNotAssignedException Thrown if no storage location is set
+ * @return \PartKeepr\StorageLocationBundle\Entity\StorageLocation $storageLocation The storage location
+ */
+ public function getStorageLocation()
+ {
+ return $this->storageLocation;
+ }
+
+ /**
+ * @param mixed $removals
+ */
+ public function setRemovals($removals = false)
+ {
+ $this->removals = $removals;
+ }
+
+ /**
+ * Returns all stock entries
*
- * @ORM\PrePersist
+ * @return ArrayCollection
*/
- public function onPrePersist()
+ public function getStockLevels()
{
- $this->executeSaveListener();
+ return $this->stockLevels;
}
/**
+ * Returns the minimum stock level
*
- * Checks if the requirements for database persisting are given.
+ * @return int
+ */
+ public function getMinStockLevel()
+ {
+ return $this->minStockLevel;
+ }
+
+ /**
+ * Set the minimum stock level for this part
*
- * For a list of exceptions, see
+ * Only positive values are allowed.
*
- * @see PartKeepr\Part.Part::onPrePersist()
+ * @param int $minStockLevel A minimum stock level, only values >= 0 are allowed.
*
- * @ORM\PreUpdate
+ * @throws \PartKeepr\Util\Exceptions\OutOfRangeException If the passed stock level is not in range (>=0)
*/
- public function onPreUpdate()
+ public function setMinStockLevel($minStockLevel)
{
- $this->executeSaveListener();
- }
-
- public function executeSaveListener () {
- $this->checkCategoryConsistency();
- $this->checkStorageLocationConsistency();
-
- $price = 0;
-
- $this->setRemovals(false);
+ $minStockLevel = intval($minStockLevel);
- foreach ($this->getStockLevels() as $stockEntry) {
- $price += $stockEntry->getPrice();
- if ($stockEntry->getStockLevel() < 0) {
- $this->setRemovals(true);
- }
+ if ($minStockLevel < 0) {
+ $exception = new OutOfRangeException(PartKeepr::i18n("Minimum Stock Level is out of range"));
+ $exception->setDetail(PartKeepr::i18n("The minimum stock level must be 0 or higher"));
+ throw $exception;
}
+ $this->minStockLevel = $minStockLevel;
+ }
- $this->setAveragePrice($price);
+ /**
+ * Sets the average price for this unit
+ *
+ * @todo Is this actually used?
+ *
+ * @param float $price The price to set
+ */
+ public function setAveragePrice($price)
+ {
+ $this->averagePrice = $price;
}
/**
- * Sets the stock level
+ * Sets the storage location for this part
*
- * @param $stockLevel int The stock level to set
+ * @param \PartKeepr\StorageLocationBundle\Entity\StorageLocation $storageLocation The storage location
*/
- public function setStockLevel($stockLevel)
+ public function setStorageLocation(StorageLocation $storageLocation)
{
- $this->stockLevel = $stockLevel;
+ $this->storageLocation = $storageLocation;
+ }
+
+ /**
+ *
+ * Checks if the requirements for database persisting are given.
+ *
+ * For a list of exceptions, see
+ *
+ * @see PartKeepr\Part.Part::onPrePersist()
+ *
+ * @ORM\PreUpdate
+ */
+ public function onPreUpdate()
+ {
+ $this->executeSaveListener();
}
/**
@@ -696,13 +706,13 @@ class Part extends BaseEntity
}
/**
- * Returns all stock entries
+ * Sets the stock level
*
- * @return StockEntry[]
+ * @param $stockLevel int The stock level to set
*/
- public function getStockLevels()
+ public function setStockLevel($stockLevel)
{
- return $this->stockLevels;
+ $this->stockLevel = $stockLevel;
}
/**
@@ -712,6 +722,7 @@ class Part extends BaseEntity
*/
public function addStockEntry(StockEntry $stockEntry)
{
+ $this->executeSaveListener();
$stockEntry->setPart($this);
$this->getStockLevels()->add($stockEntry);
}
@@ -793,4 +804,27 @@ class Part extends BaseEntity
{
return $this->projectParts;
}
+
+ public function recomputeStockLevels () {
+ $sum = 0;
+ $price = 0;
+
+ foreach ($this->getStockLevels() as $stockLevel) {
+ $price += $stockLevel->getPrice();
+
+ if ($stockLevel->getStockLevel() < 0) {
+ $this->setRemovals(true);
+ }
+ $sum += $stockLevel->getStockLevel();
+ }
+
+ $this->setAveragePrice($price);
+ $this->setStockLevel($sum);
+
+ if ($sum < $this->getMinStockLevel()) {
+ $this->setLowStock(true);
+ } else {
+ $this->setLowStock(false);
+ }
+ }
}
diff --git a/src/PartKeepr/PartBundle/Listeners/StockLevelListener.php b/src/PartKeepr/PartBundle/Listeners/StockLevelListener.php
@@ -51,13 +51,7 @@ class StockLevelListener extends ContainerAware
{
$entityManager = $eventArgs->getEntityManager();
- $sum = 0;
-
- foreach ($part->getStockLevels() as $stockLevel) {
- $sum += $stockLevel->getStockLevel();
- }
-
- $part->setStockLevel($sum);
+ $part->recomputeStockLevels();
$entityManager->getUnitOfWork()->recomputeSingleEntityChangeSet(
$entityManager->getClassMetadata(get_class($part)),