From ab42a926ed017a290476932fabbf358a7c8465e9 Mon Sep 17 00:00:00 2001 From: TemuulenBM Date: Thu, 7 Dec 2023 18:00:47 +0800 Subject: [PATCH] fixed inventory,sales-order,purchase-order, and product small issues created basic structure for product category page --- addon/components/admin/product-category.hbs | 1 + addon/components/admin/product-category.js | 3 + .../components/admin/visibility-controls.hbs | 12 +++ addon/components/admin/visibility-controls.js | 81 +++++++++++++++++++ addon/components/inventory-form-panel.hbs | 4 +- addon/components/inventory-panel/details.hbs | 4 + addon/components/purchase-order-form-panel.js | 2 +- addon/components/supplier-form-panel.hbs | 3 +- addon/controllers/admin/product-category.js | 3 + addon/controllers/inventory/expired-stock.js | 23 +----- addon/controllers/inventory/index.js | 52 +----------- addon/controllers/inventory/low-stock.js | 20 ----- addon/controllers/sales-orders/index.js | 3 +- addon/engine.js | 33 +++++++- addon/models/stock-adjustment.js | 21 +++++ addon/routes/admin/product-category.js | 24 ++++++ addon/serializers/stock-adjustment.js | 15 ++++ addon/templates/admin/product-category.hbs | 17 ++++ app/components/admin/product-category.js | 1 + app/components/admin/visibility-controls.js | 1 + app/controllers/admin/product-category.js | 1 + app/routes/admin/product-category.js | 1 + app/serializers/stock-adjustment.js | 1 + app/templates/admin/product-category.js | 1 + .../Http/Controllers/InventoryController.php | 3 +- server/src/Http/Resources/Batch.php | 16 ++-- server/src/Http/Resources/Inventory.php | 1 - server/src/Http/Resources/StockAdjustment.php | 32 ++++++++ server/src/Models/Batch.php | 14 ++++ server/src/Models/Inventory.php | 25 +++--- server/src/Models/StockAdjustment.php | 24 +++++- .../components/admin/product-category-test.js | 26 ++++++ .../admin/visibility-controls-test.js | 26 ++++++ .../admin/product-category-test.js | 12 +++ .../routes/admin/product-category-test.js | 11 +++ .../unit/serializers/stock-adjustment-test.js | 23 ++++++ 36 files changed, 421 insertions(+), 119 deletions(-) create mode 100644 addon/components/admin/product-category.hbs create mode 100644 addon/components/admin/product-category.js create mode 100644 addon/components/admin/visibility-controls.hbs create mode 100644 addon/components/admin/visibility-controls.js create mode 100644 addon/controllers/admin/product-category.js create mode 100644 addon/routes/admin/product-category.js create mode 100644 addon/serializers/stock-adjustment.js create mode 100644 addon/templates/admin/product-category.hbs create mode 100644 app/components/admin/product-category.js create mode 100644 app/components/admin/visibility-controls.js create mode 100644 app/controllers/admin/product-category.js create mode 100644 app/routes/admin/product-category.js create mode 100644 app/serializers/stock-adjustment.js create mode 100644 app/templates/admin/product-category.js create mode 100644 server/src/Http/Resources/StockAdjustment.php create mode 100644 tests/integration/components/admin/product-category-test.js create mode 100644 tests/integration/components/admin/visibility-controls-test.js create mode 100644 tests/unit/controllers/admin/product-category-test.js create mode 100644 tests/unit/routes/admin/product-category-test.js create mode 100644 tests/unit/serializers/stock-adjustment-test.js diff --git a/addon/components/admin/product-category.hbs b/addon/components/admin/product-category.hbs new file mode 100644 index 00000000..fb5c4b15 --- /dev/null +++ b/addon/components/admin/product-category.hbs @@ -0,0 +1 @@ +{{yield}} \ No newline at end of file diff --git a/addon/components/admin/product-category.js b/addon/components/admin/product-category.js new file mode 100644 index 00000000..48b9c2ac --- /dev/null +++ b/addon/components/admin/product-category.js @@ -0,0 +1,3 @@ +import Component from '@glimmer/component'; + +export default class AdminProductCategoryComponent extends Component {} diff --git a/addon/components/admin/visibility-controls.hbs b/addon/components/admin/visibility-controls.hbs new file mode 100644 index 00000000..dc14590e --- /dev/null +++ b/addon/components/admin/visibility-controls.hbs @@ -0,0 +1,12 @@ + +

Visibility controls allow you to disable sections of Pallet. Toggle the checkbox to enable or disable sections to hide in Pallet

+ {{#each this.visibilitySettings as |visibilityControl|}} + + + + {{/each}} +
+ +
+
\ No newline at end of file diff --git a/addon/components/admin/visibility-controls.js b/addon/components/admin/visibility-controls.js new file mode 100644 index 00000000..e6ea41bd --- /dev/null +++ b/addon/components/admin/visibility-controls.js @@ -0,0 +1,81 @@ +import Component from '@glimmer/component'; +import { tracked } from '@glimmer/tracking'; +import { inject as service } from '@ember/service'; +import { action } from '@ember/object'; +import { isArray } from '@ember/array'; + +export default class AdminVisibilityControlsComponent extends Component { + @service fetch; + @tracked visibilitySettings = [ + // { name: 'Dashboard', route: 'operations.orders', visible: true }, + { name: 'Products', route: 'products', visible: true }, + { name: 'Warehouses', route: 'warehouses', visible: true }, + { name: 'Suppliers', route: 'suppliers', visible: true }, + { name: 'Inventory', route: 'inventory', visible: true }, + { name: 'Sales Orders', route: 'sales-orders', visible: true }, + { name: 'Purchase Orders', route: 'purchase-orders', visible: true }, + { name: 'Audits', route: 'audits', visible: true }, + { name: 'Reports', route: 'reports', visible: true }, + ]; + @tracked isLoading = false; + + constructor() { + super(...arguments); + this.loadVisibilitySettings(); + } + + @action mutateVisibility(route, visible) { + this.visibilitySettings = [...this.visibilitySettings].map((visibilityControl) => { + if (visibilityControl.route === route) { + return { + ...visibilityControl, + visible, + }; + } + + return visibilityControl; + }); + } + + @action loadVisibilitySettings() { + this.isLoading = true; + + this.fetch + .get('pallet/settings/visibility') + .then(({ visibilitySettings }) => { + if (isArray(visibilitySettings)) { + for (let i = 0; i < visibilitySettings.length; i++) { + const visibilityControl = visibilitySettings.objectAt(i); + this.mutateVisibility(visibilityControl.route, visibilityControl.visible); + } + } + }) + .catch((error) => { + this.notifications.serverError(error); + }) + .finally(() => { + this.isLoading = false; + }); + } + + @action saveVisibilitySettings() { + this.isLoading = true; + + this.fetch + .post('pallet/settings/visibility', { visibilitySettings: this.visibilitySettings }) + .then(({ visibilitySettings }) => { + if (isArray(visibilitySettings)) { + for (let i = 0; i < visibilitySettings.length; i++) { + const visibilityControl = visibilitySettings.objectAt(i); + this.mutateVisibility(visibilityControl.route, visibilityControl.visible); + } + } + }) + .catch((error) => { + this.notifications.serverError(error); + }) + .finally(() => { + this.isLoading = false; + }); + } +} diff --git a/addon/components/inventory-form-panel.hbs b/addon/components/inventory-form-panel.hbs index a30bc745..2df14492 100644 --- a/addon/components/inventory-form-panel.hbs +++ b/addon/components/inventory-form-panel.hbs @@ -55,7 +55,7 @@ @infiniteScroll={{false}} @renderInPlace={{true}} @onChange={{this.inventory.supplier.name}} - @onChangeId={{fn (mut this.inventory.product_uuid)}} + @onChangeId={{fn (mut this.inventory.supplier_uuid)}} as |model| > {{model.name}} @@ -96,7 +96,7 @@ - + diff --git a/addon/components/inventory-panel/details.hbs b/addon/components/inventory-panel/details.hbs index b01fc142..c201aeb3 100644 --- a/addon/components/inventory-panel/details.hbs +++ b/addon/components/inventory-panel/details.hbs @@ -17,6 +17,10 @@
Batch
{{n-a @inventory.batch.batch_number}}
+
+
Supplier
+
{{n-a @inventory.supplier.name}}
+
Expiry Date
{{n-a @inventory.expiry_date_at}}
diff --git a/addon/components/purchase-order-form-panel.js b/addon/components/purchase-order-form-panel.js index 7d21da2e..3e56a19e 100644 --- a/addon/components/purchase-order-form-panel.js +++ b/addon/components/purchase-order-form-panel.js @@ -143,7 +143,7 @@ export default class PurchaseOrderFormPanelComponent extends Component { @action onPressCancel() { return contextComponentCallback(this, 'onPressCancel', this.purchaseOrder); } - + @action setExpectedDeliveryDate(event) { const { target: { value }, diff --git a/addon/components/supplier-form-panel.hbs b/addon/components/supplier-form-panel.hbs index ab99f884..6180fb0f 100644 --- a/addon/components/supplier-form-panel.hbs +++ b/addon/components/supplier-form-panel.hbs @@ -54,7 +54,8 @@ @triggerClass="form-select form-input" @infiniteScroll={{false}} @renderInPlace={{true}} - @onChange={{this.supplier.place.address}} + @onChange={{fn (mut this.supplier.place)}} + @onChangeId={{fn (mut this.supplier.place_uuid)}} as |model| >
diff --git a/addon/controllers/admin/product-category.js b/addon/controllers/admin/product-category.js new file mode 100644 index 00000000..948d3a6e --- /dev/null +++ b/addon/controllers/admin/product-category.js @@ -0,0 +1,3 @@ +import Controller from '@ember/controller'; + +export default class AdminProductCategoryController extends Controller {} diff --git a/addon/controllers/inventory/expired-stock.js b/addon/controllers/inventory/expired-stock.js index 89bd0450..a4136955 100644 --- a/addon/controllers/inventory/expired-stock.js +++ b/addon/controllers/inventory/expired-stock.js @@ -69,7 +69,7 @@ export default class InventoryExpiredStockController extends Controller { */ @tracked page = 1; - @tracked view = 'expired_stock' + @tracked view = 'expired_stock'; /** * The maximum number of items to show per page @@ -136,6 +136,7 @@ export default class InventoryExpiredStockController extends Controller { { label: 'Product', valuePath: 'product.name', + action: this.viewInventory, width: '170px', cellComponent: 'cell/product-info', modelPath: 'product', @@ -159,16 +160,6 @@ export default class InventoryExpiredStockController extends Controller { valuePath: 'quantity', width: '120px', }, - { - label: 'Warehouse', - valuePath: 'warehouse.address', - width: '120px', - cellComponent: 'click-to-copy', - resizable: true, - sortable: true, - filterable: true, - filterComponent: 'filter/string', - }, { label: 'Batch', valuePath: 'batch.name', @@ -199,16 +190,6 @@ export default class InventoryExpiredStockController extends Controller { filterable: true, filterComponent: 'filter/date', }, - { - label: 'Expiry Date', - valuePath: 'expiredAt', - sortParam: 'expiry_date_at', - width: '10%', - resizable: true, - sortable: true, - filterable: true, - filterComponent: 'filter/date', - }, { label: 'Updated At', valuePath: 'updatedAt', diff --git a/addon/controllers/inventory/index.js b/addon/controllers/inventory/index.js index c9f60c47..6f93ba48 100644 --- a/addon/controllers/inventory/index.js +++ b/addon/controllers/inventory/index.js @@ -134,6 +134,7 @@ export default class InventoryIndexController extends Controller { { label: 'Product', valuePath: 'product.name', + action: this.viewInventory, width: '170px', cellComponent: 'cell/product-info', modelPath: 'product', @@ -157,16 +158,6 @@ export default class InventoryIndexController extends Controller { valuePath: 'quantity', width: '120px', }, - { - label: 'Warehouse', - valuePath: 'warehouse.address', - width: '120px', - cellComponent: 'click-to-copy', - resizable: true, - sortable: true, - filterable: true, - filterComponent: 'filter/string', - }, { label: 'Batch', valuePath: 'batch.batch_number', @@ -199,7 +190,7 @@ export default class InventoryIndexController extends Controller { }, { label: 'Expiry Date', - valuePath: 'expiredAt', + valuePath: 'expiryDate', sortParam: 'expiry_date_at', width: '10%', resizable: true, @@ -286,7 +277,7 @@ export default class InventoryIndexController extends Controller { * @void */ @action viewInventory(inventory) { - return this.transitionToRoute('inventory.index.details', inventory); + return this.transitionToRoute('inventory.index.details', inventory.public_id); } /** @@ -313,41 +304,4 @@ export default class InventoryIndexController extends Controller { @action async editInventory(inventory) { return this.transitionToRoute('inventory.index.edit', inventory); } - - /** - * Delete a `inventory` via confirm prompt - * - * @param {InventoryModel} inventory - * @param {Object} options - * @void - */ - @action deleteInventory(inventory, options = {}) { - this.crud.delete(inventory, { - onConfirm: () => { - return this.hostRouter.refresh(); - }, - ...options, - }); - } - - /** - * Bulk deletes selected `inventory` via confirm prompt - * - * @param {Array} selected an array of selected models - * @void - */ - @action bulkDeleteInventorys() { - const selected = this.table.selectedRows; - - this.crud.bulkDelete(selected, { - modelNamePath: `public_id`, - acceptButtonText: 'Delete Inventories', - fetchOptions: { - namespace: 'pallet/int/v1', - }, - onSuccess: () => { - return this.hostRouter.refresh(); - }, - }); - } } diff --git a/addon/controllers/inventory/low-stock.js b/addon/controllers/inventory/low-stock.js index 9c126114..8409f7af 100644 --- a/addon/controllers/inventory/low-stock.js +++ b/addon/controllers/inventory/low-stock.js @@ -159,16 +159,6 @@ export default class InventoryLowStockController extends Controller { valuePath: 'quantity', width: '120px', }, - { - label: 'Warehouse', - valuePath: 'warehouse.address', - width: '120px', - cellComponent: 'click-to-copy', - resizable: true, - sortable: true, - filterable: true, - filterComponent: 'filter/string', - }, { label: 'Batch', valuePath: 'batch.name', @@ -199,16 +189,6 @@ export default class InventoryLowStockController extends Controller { filterable: true, filterComponent: 'filter/date', }, - { - label: 'Expiry Date', - valuePath: 'expiredAt', - sortParam: 'expiry_date_at', - width: '10%', - resizable: true, - sortable: true, - filterable: true, - filterComponent: 'filter/date', - }, { label: 'Updated At', valuePath: 'updatedAt', diff --git a/addon/controllers/sales-orders/index.js b/addon/controllers/sales-orders/index.js index 904d080b..9394614c 100644 --- a/addon/controllers/sales-orders/index.js +++ b/addon/controllers/sales-orders/index.js @@ -77,7 +77,6 @@ export default class SalesOrdersIndexController extends Controller { */ @tracked page = 1; - /** * The maximum number of items to show per page * @@ -291,7 +290,7 @@ export default class SalesOrdersIndexController extends Controller { this.crud.bulkDelete(selected, { modelNamePath: 'public_id', - acceptButtonText: "Delete Sales Orders", + acceptButtonText: 'Delete Sales Orders', fetchOptions: { namespace: 'pallet/int/v1', }, diff --git a/addon/engine.js b/addon/engine.js index cd7809bb..944b547e 100644 --- a/addon/engine.js +++ b/addon/engine.js @@ -3,6 +3,7 @@ import loadInitializers from 'ember-load-initializers'; import Resolver from 'ember-resolver'; import config from './config/environment'; import services from '@fleetbase/ember-core/exports/services'; +import AdminVisibilityControlsComponent from './components/admin/visibility-controls'; const { modulePrefix } = config; const externalRoutes = ['console', 'extensions']; @@ -16,7 +17,37 @@ export default class PalletEngine extends Engine { }; setupExtension = function (app, engine, universe) { // register menu item in header - universe.registerHeaderMenuItem('Pallet', 'console.pallet', { icon: 'pallet', priority: 1 }); + universe.registerHeaderMenuItem('Pallet', 'console.pallet', { icon: 'pallet', priority: 0 }); + + // register admin settings -- create a pallet menu panel with it's own setting options + universe.registerAdminMenuPanel( + 'Pallet Config', + [ + { + title: 'Visibility Controls', + icon: 'eye', + component: AdminVisibilityControlsComponent, + }, + ], + { + slug: 'pallet', + } + ); + + // create primary registry for engine + universe.createRegistry('engine:pallet'); + + // register the product panel + universe.createRegistry('component:product-panel'); + + // register the inventory panel + universe.createRegistry('component:inventory-panel'); + + // register the supplier panel + universe.createRegistry('component:supplier-panel'); + + // register the warehouse panel + universe.createRegistry('component:warehouse-panel'); }; } diff --git a/addon/models/stock-adjustment.js b/addon/models/stock-adjustment.js index 4e13f5d0..4e6ec3be 100644 --- a/addon/models/stock-adjustment.js +++ b/addon/models/stock-adjustment.js @@ -37,6 +37,20 @@ export default class StockAdjustmentModel extends Model { return formatDistanceToNow(this.created_at); } + @computed('created_at') get createdAt() { + if (!isValidDate(this.created_at)) { + return null; + } + return formatDate(this.created_at, 'PPP p'); + } + + @computed('created_at') get createdAtShort() { + if (!isValidDate(this.created_at)) { + return null; + } + return formatDate(this.created_at, 'PP'); + } + @computed('updated_at') get updatedAgo() { if (!isValidDate(this.updated_at)) { return null; @@ -50,4 +64,11 @@ export default class StockAdjustmentModel extends Model { } return formatDate(this.updated_at, 'PPP p'); } + + @computed('updated_at') get updatedAtShort() { + if (!isValidDate(this.updated_at)) { + return null; + } + return formatDate(this.updated_at, 'PP'); + } } diff --git a/addon/routes/admin/product-category.js b/addon/routes/admin/product-category.js new file mode 100644 index 00000000..0ada6d8f --- /dev/null +++ b/addon/routes/admin/product-category.js @@ -0,0 +1,24 @@ +import Route from '@ember/routing/route'; +import { inject as service } from '@ember/service'; + +export default class AdminProductCategoryRoute extends Route { + @service store; + + queryParams = { + page: { refreshModel: true }, + limit: { refreshModel: true }, + sort: { refreshModel: true }, + query: { refreshModel: true }, + created_at: { refreshModel: true }, + }; + + /** + * Fetch the model data based on the specified parameters. + * + * @param {Object} params - Query parameters for fetching notifications. + * @returns {Promise} - A promise that resolves with the notification data. + */ + model(params = {}) { + return this.store.query('notification', params); + } +} diff --git a/addon/serializers/stock-adjustment.js b/addon/serializers/stock-adjustment.js new file mode 100644 index 00000000..cd7e6745 --- /dev/null +++ b/addon/serializers/stock-adjustment.js @@ -0,0 +1,15 @@ +import ApplicationSerializer from '@fleetbase/ember-core/serializers/application'; +import { EmbeddedRecordsMixin } from '@ember-data/serializer/rest'; + +export default class StockAdjustment extends ApplicationSerializer.extend(EmbeddedRecordsMixin) { + /** + * Embedded relationship attributes + * + * @var {Object} + */ + get attrs() { + return { + product: { embedded: 'always' }, + }; + } +} diff --git a/addon/templates/admin/product-category.hbs b/addon/templates/admin/product-category.hbs new file mode 100644 index 00000000..4abdd78c --- /dev/null +++ b/addon/templates/admin/product-category.hbs @@ -0,0 +1,17 @@ + +