diff --git a/addon/components/admin/product-category.hbs b/addon/components/admin/product-category.hbs new file mode 100644 index 00000000..7819d8cb --- /dev/null +++ b/addon/components/admin/product-category.hbs @@ -0,0 +1,32 @@ + +
+
+
+ +
+
\ 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..27ec16de --- /dev/null +++ b/addon/components/admin/product-category.js @@ -0,0 +1,139 @@ +import Component from '@glimmer/component'; +import { tracked } from '@glimmer/tracking'; +import { inject as service } from '@ember/service'; +import { action } from '@ember/object'; +import { dasherize } from '@ember/string'; + +export default class AdminProductCategoryComponent extends Component { + @service store; + @service modalsManager; + @service currentUser; + @service modalsManager; + @service notifications; + @service fetch; + @service hostRouter; + @tracked categories = []; + @tracked selectedCategory; + @tracked isLoading = false; + @tracked buttonTitle = null; + + constructor() { + super(...arguments); + this.category = this.args.category; + this.fetchCategoryHierarchy(); + } + + @action async addCategory() { + const category = this.store.createRecord('category', { + for: 'pallet_product', + }); + + this.modalsManager.show('modals/create-product-category', { + title: 'Create a new product category', + acceptButtonIcon: 'check', + acceptButtonIconPrefix: 'fas', + declineButtonIcon: 'times', + declineButtonIconPrefix: 'fas', + category, + uploadNewPhoto: (file) => { + this.fetch.uploadFile.perform( + file, + { + path: `uploads/${category.company_uuid}/product-category-icon/${dasherize(category.name ?? this.currentUser.companyId)}`, + subject_uuid: category.id, + subject_type: `category`, + type: `category_icon`, + }, + (uploadedFile) => { + category.setProperties({ + icon_file_uuid: uploadedFile.id, + icon_url: uploadedFile.url, + icon: uploadedFile, + }); + } + ); + }, + confirm: (modal) => { + modal.startLoading(); + + return category.save().then(() => { + this.notifications.success('New product category created.'); + return this.fetchCategoryHierarchy(); + }); + }, + }); + } + + @action async fetchCategoryHierarchy() { + const allCategories = await this.store.query('category', { + for: 'pallet_product', + with_subcategories: true, + }); + + this.categories = allCategories.filter((category) => !category.parent); + this.categories.forEach((parentCategory) => { + parentCategory.subcategories = allCategories.filter((subcategory) => subcategory.parent?.id === parentCategory.id); + }); + } + + @action async addSubCategory(parentCategory) { + const subCategory = this.store.createRecord('category', { + parent: parentCategory, + for: 'pallet_product', + }); + + this.modalsManager.show('modals/create-product-category', { + title: 'Create a new subcategory', + acceptButtonIcon: 'check', + acceptButtonIconPrefix: 'fas', + declineButtonIcon: 'times', + declineButtonIconPrefix: 'fas', + category: subCategory, + uploadNewPhoto: (file) => { + this.fetch.uploadFile.perform( + file, + { + path: `uploads/${category.company_uuid}/product-category-icon/${dasherize(category.name ?? this.currentUser.companyId)}`, + subject_uuid: category.id, + subject_type: `category`, + type: `category_icon`, + }, + (uploadedFile) => { + category.setProperties({ + icon_file_uuid: uploadedFile.id, + icon_url: uploadedFile.url, + icon: uploadedFile, + }); + } + ); + }, + confirm: async (modal) => { + modal.startLoading(); + + try { + await subCategory.save(); + this.notifications.success('New subcategory created.'); + await this.fetchCategoryHierarchy(); + } catch (error) { + this.notifications.error('Error creating subcategory.'); + console.error('Error creating subcategory:', error); + } + }, + }); + } + + @action async deleteCategory(category) { + const confirmation = confirm(`Are you sure you want to delete the category "${category.name}"?`); + + if (confirmation) { + try { + await category.destroyRecord(); + this.notifications.success('Category deleted successfully.'); + await this.fetchCategoryHierarchy(); + } catch (error) { + this.notifications.error('Error deleting category.'); + console.error('Error deleting category:', error); + } + } + } +} diff --git a/addon/components/batch-form-panel.js b/addon/components/batch-form-panel.js index ce7e6444..a43f1fda 100644 --- a/addon/components/batch-form-panel.js +++ b/addon/components/batch-form-panel.js @@ -2,8 +2,8 @@ import Component from '@glimmer/component'; import { tracked } from '@glimmer/tracking'; import { inject as service } from '@ember/service'; import { action } from '@ember/object'; -import contextComponentCallback from '../utils/context-component-callback'; -import applyContextComponentArguments from '../utils/apply-context-component-arguments'; +import contextComponentCallback from '@fleetbase/ember-core/utils/context-component-callback'; +import applyContextComponentArguments from '@fleetbase/ember-core/utils/apply-context-component-arguments'; export default class BatchFormPanelComponent extends Component { /** diff --git a/addon/components/batch-panel.js b/addon/components/batch-panel.js index 7e339641..8948e80b 100644 --- a/addon/components/batch-panel.js +++ b/addon/components/batch-panel.js @@ -4,8 +4,8 @@ import { inject as service } from '@ember/service'; import { action } from '@ember/object'; import { isArray } from '@ember/array'; import BatchPanelDetailsComponent from './batch-panel/details'; -import contextComponentCallback from '../utils/context-component-callback'; -import applyContextComponentArguments from '../utils/apply-context-component-arguments'; +import contextComponentCallback from '@fleetbase/ember-core/utils/context-component-callback'; +import applyContextComponentArguments from '@fleetbase/ember-core/utils/apply-context-component-arguments'; export default class BatchPanelComponent extends Component { /** 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-form-panel.js b/addon/components/inventory-form-panel.js index ea8c63b3..568ccdf7 100644 --- a/addon/components/inventory-form-panel.js +++ b/addon/components/inventory-form-panel.js @@ -2,8 +2,8 @@ import Component from '@glimmer/component'; import { tracked } from '@glimmer/tracking'; import { inject as service } from '@ember/service'; import { action } from '@ember/object'; -import contextComponentCallback from '../utils/context-component-callback'; -import applyContextComponentArguments from '../utils/apply-context-component-arguments'; +import contextComponentCallback from '@fleetbase/ember-core/utils/context-component-callback'; +import applyContextComponentArguments from '@fleetbase/ember-core/utils/apply-context-component-arguments'; export default class InventoryFormPanelComponent extends Component { /** diff --git a/addon/components/inventory-panel.js b/addon/components/inventory-panel.js index a15c4be1..5a3e73fc 100644 --- a/addon/components/inventory-panel.js +++ b/addon/components/inventory-panel.js @@ -4,8 +4,8 @@ import { inject as service } from '@ember/service'; import { action } from '@ember/object'; import { isArray } from '@ember/array'; import InventoryPanelDetailsComponent from './inventory-panel/details'; -import contextComponentCallback from '../utils/context-component-callback'; -import applyContextComponentArguments from '../utils/apply-context-component-arguments'; +import contextComponentCallback from '@fleetbase/ember-core/utils/context-component-callback'; +import applyContextComponentArguments from '@fleetbase/ember-core/utils/apply-context-component-arguments'; export default class InventoryPanelComponent extends Component { /** 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/product-form-panel.js b/addon/components/product-form-panel.js index c19d1a1c..f7323b69 100644 --- a/addon/components/product-form-panel.js +++ b/addon/components/product-form-panel.js @@ -3,8 +3,8 @@ import { tracked } from '@glimmer/tracking'; import { inject as service } from '@ember/service'; import { action } from '@ember/object'; import getWithDefault from '@fleetbase/ember-core/utils/get-with-default'; -import contextComponentCallback from '../utils/context-component-callback'; -import applyContextComponentArguments from '../utils/apply-context-component-arguments'; +import contextComponentCallback from '@fleetbase/ember-core/utils/context-component-callback'; +import applyContextComponentArguments from '@fleetbase/ember-core/utils/apply-context-component-arguments'; export default class ProductFormPanelComponent extends Component { /** diff --git a/addon/components/product-panel.js b/addon/components/product-panel.js index d6e47057..90bf16ee 100644 --- a/addon/components/product-panel.js +++ b/addon/components/product-panel.js @@ -4,8 +4,8 @@ import { action } from '@ember/object'; import { inject as service } from '@ember/service'; import { isArray } from '@ember/array'; import ProductPanelDetailComponent from './product-panel/details'; -import contextComponentCallback from '../utils/context-component-callback'; -import applyContextComponentArguments from '../utils/apply-context-component-arguments'; +import contextComponentCallback from '@fleetbase/ember-core/utils/context-component-callback'; +import applyContextComponentArguments from '@fleetbase/ember-core/utils/apply-context-component-arguments'; export default class ProductPanelComponent extends Component { /** diff --git a/addon/components/purchase-order-form-panel.js b/addon/components/purchase-order-form-panel.js index 7d21da2e..a6adead9 100644 --- a/addon/components/purchase-order-form-panel.js +++ b/addon/components/purchase-order-form-panel.js @@ -2,8 +2,8 @@ import Component from '@glimmer/component'; import { tracked } from '@glimmer/tracking'; import { inject as service } from '@ember/service'; import { action } from '@ember/object'; -import contextComponentCallback from '../utils/context-component-callback'; -import applyContextComponentArguments from '../utils/apply-context-component-arguments'; +import contextComponentCallback from '@fleetbase/ember-core/utils/context-component-callback'; +import applyContextComponentArguments from '@fleetbase/ember-core/utils/apply-context-component-arguments'; export default class PurchaseOrderFormPanelComponent extends Component { /** @@ -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/purchase-order-panel.js b/addon/components/purchase-order-panel.js index 5b940b34..0ccb208e 100644 --- a/addon/components/purchase-order-panel.js +++ b/addon/components/purchase-order-panel.js @@ -4,8 +4,8 @@ import { inject as service } from '@ember/service'; import { action } from '@ember/object'; import { isArray } from '@ember/array'; import PurchaseOrderPanelDetailsComponent from './purchase-order-panel/details'; -import contextComponentCallback from '../utils/context-component-callback'; -import applyContextComponentArguments from '../utils/apply-context-component-arguments'; +import contextComponentCallback from '@fleetbase/ember-core/utils/context-component-callback'; +import applyContextComponentArguments from '@fleetbase/ember-core/utils/apply-context-component-arguments'; export default class PurchaseOrderPanelComponent extends Component { /** diff --git a/addon/components/sales-order-form-panel.js b/addon/components/sales-order-form-panel.js index 52c07fe9..25e7635c 100644 --- a/addon/components/sales-order-form-panel.js +++ b/addon/components/sales-order-form-panel.js @@ -3,8 +3,8 @@ import { tracked } from '@glimmer/tracking'; import { inject as service } from '@ember/service'; import { action } from '@ember/object'; import { isValid as isValidDate } from 'date-fns'; -import contextComponentCallback from '../utils/context-component-callback'; -import applyContextComponentArguments from '../utils/apply-context-component-arguments'; +import contextComponentCallback from '@fleetbase/ember-core/utils/context-component-callback'; +import applyContextComponentArguments from '@fleetbase/ember-core/utils/apply-context-component-arguments'; export default class SalesOrderFormPanelComponent extends Component { /** diff --git a/addon/components/sales-order-panel.js b/addon/components/sales-order-panel.js index 21ce38a7..bdfeef3b 100644 --- a/addon/components/sales-order-panel.js +++ b/addon/components/sales-order-panel.js @@ -4,8 +4,8 @@ import { inject as service } from '@ember/service'; import { action } from '@ember/object'; import { isArray } from '@ember/array'; import SalesOrderPanelDetailsComponent from './sales-order-panel/details'; -import contextComponentCallback from '../utils/context-component-callback'; -import applyContextComponentArguments from '../utils/apply-context-component-arguments'; +import contextComponentCallback from '@fleetbase/ember-core/utils/context-component-callback'; +import applyContextComponentArguments from '@fleetbase/ember-core/utils/apply-context-component-arguments'; export default class SalesOrderPanelComponent extends Component { /** diff --git a/addon/components/stock-adjustment-form-panel.js b/addon/components/stock-adjustment-form-panel.js index 622f72cd..45410a98 100644 --- a/addon/components/stock-adjustment-form-panel.js +++ b/addon/components/stock-adjustment-form-panel.js @@ -2,8 +2,8 @@ import Component from '@glimmer/component'; import { tracked } from '@glimmer/tracking'; import { inject as service } from '@ember/service'; import { action } from '@ember/object'; -import contextComponentCallback from '../utils/context-component-callback'; -import applyContextComponentArguments from '../utils/apply-context-component-arguments'; +import contextComponentCallback from '@fleetbase/ember-core/utils/context-component-callback'; +import applyContextComponentArguments from '@fleetbase/ember-core/utils/apply-context-component-arguments'; export default class StockAdjustmentFormPanelComponent extends Component { /** 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/components/supplier-form-panel.js b/addon/components/supplier-form-panel.js index ca5e3c9e..38d5ee86 100644 --- a/addon/components/supplier-form-panel.js +++ b/addon/components/supplier-form-panel.js @@ -2,8 +2,8 @@ import Component from '@glimmer/component'; import { tracked } from '@glimmer/tracking'; import { inject as service } from '@ember/service'; import { action } from '@ember/object'; -import contextComponentCallback from '../utils/context-component-callback'; -import applyContextComponentArguments from '../utils/apply-context-component-arguments'; +import contextComponentCallback from '@fleetbase/ember-core/utils/context-component-callback'; +import applyContextComponentArguments from '@fleetbase/ember-core/utils/apply-context-component-arguments'; export default class SupplierFormPanelComponent extends Component { /** diff --git a/addon/components/supplier-panel.js b/addon/components/supplier-panel.js index a1866af0..efa9b2e0 100644 --- a/addon/components/supplier-panel.js +++ b/addon/components/supplier-panel.js @@ -4,8 +4,8 @@ import { inject as service } from '@ember/service'; import { action } from '@ember/object'; import { isArray } from '@ember/array'; import SupplierPanelDetailsComponent from './supplier-panel/details'; -import contextComponentCallback from '../utils/context-component-callback'; -import applyContextComponentArguments from '../utils/apply-context-component-arguments'; +import contextComponentCallback from '@fleetbase/ember-core/utils/context-component-callback'; +import applyContextComponentArguments from '@fleetbase/ember-core/utils/apply-context-component-arguments'; export default class SupplierPanelComponent extends Component { /** diff --git a/addon/components/warehouse-form-panel.js b/addon/components/warehouse-form-panel.js index 21b47520..b57c7625 100644 --- a/addon/components/warehouse-form-panel.js +++ b/addon/components/warehouse-form-panel.js @@ -5,8 +5,8 @@ import { action } from '@ember/object'; import { isBlank } from '@ember/utils'; import { all } from 'rsvp'; import Point from '@fleetbase/fleetops-data/utils/geojson/point'; -import contextComponentCallback from '../utils/context-component-callback'; -import applyContextComponentArguments from '../utils/apply-context-component-arguments'; +import contextComponentCallback from '@fleetbase/ember-core/utils/context-component-callback'; +import applyContextComponentArguments from '@fleetbase/ember-core/utils/apply-context-component-arguments'; export default class WarehouseFormPanelComponent extends Component { /** diff --git a/addon/components/warehouse-panel.js b/addon/components/warehouse-panel.js index a60e52ff..60dfad3a 100644 --- a/addon/components/warehouse-panel.js +++ b/addon/components/warehouse-panel.js @@ -4,8 +4,8 @@ import { action } from '@ember/object'; import { inject as service } from '@ember/service'; import { isArray } from '@ember/array'; import WarehousePanelDetailComponent from './warehouse-panel/details'; -import contextComponentCallback from '../utils/context-component-callback'; -import applyContextComponentArguments from '../utils/apply-context-component-arguments'; +import contextComponentCallback from '@fleetbase/ember-core/utils/context-component-callback'; +import applyContextComponentArguments from '@fleetbase/ember-core/utils/apply-context-component-arguments'; export default class WarehousePanelComponent extends Component { /** 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..afbe46fa 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 AdminProductCategoryComponent from './components/admin/product-category'; const { modulePrefix } = config; const externalRoutes = ['console', 'extensions']; @@ -17,6 +18,21 @@ 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 }); + + // register admin settings -- create a pallet menu panel with it's own setting options + universe.registerAdminMenuPanel( + 'Pallet Config', + [ + { + title: 'Product Category', + icon: 'eye', + component: AdminProductCategoryComponent, + }, + ], + { + slug: 'pallet', + } + ); }; } 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/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/purchase-orders/index.hbs b/addon/templates/purchase-orders/index.hbs index 8e5a9438..5ac34b2d 100644 --- a/addon/templates/purchase-orders/index.hbs +++ b/addon/templates/purchase-orders/index.hbs @@ -13,7 +13,7 @@ diff --git a/addon/utils/apply-context-component-arguments.js b/addon/utils/apply-context-component-arguments.js deleted file mode 100644 index a715a6fb..00000000 --- a/addon/utils/apply-context-component-arguments.js +++ /dev/null @@ -1,31 +0,0 @@ -import getModelName from '@fleetbase/ember-core/utils/get-model-name'; - -/** - * Applies context and dynamic arguments to a given component. - * - * @param {Component} component - The component to which context and arguments will be applied. - */ -export default function applyContextComponentArguments(component) { - const { context, dynamicArgs = {} } = component.args; - - // Apply context model if available - if (context) { - const contextModelName = getModelName(context); - if (contextModelName) { - component[contextModelName] = context; - } - } - - // Execute any apply callback present in dynamic arguments - const { applyCallback } = dynamicArgs; - if (typeof applyCallback === 'function') { - applyCallback(component); - } - - // Apply other dynamic arguments to the component - for (const [key, value] of Object.entries(dynamicArgs)) { - if (key !== 'applyCallback') { - component[key] = value; - } - } -} diff --git a/addon/utils/context-component-callback.js b/addon/utils/context-component-callback.js deleted file mode 100644 index a31c2a30..00000000 --- a/addon/utils/context-component-callback.js +++ /dev/null @@ -1,16 +0,0 @@ -export default function contextComponentCallback(component, name, ...params) { - let callbackInvoked = false; - - if (typeof component.args[name] === 'function') { - component.args[name](...params); - callbackInvoked = true; - } - - // now do for context options - if (typeof component.args.options === 'object' && typeof component.args.options[name] === 'function') { - component.args.options[name](...params); - callbackInvoked = true; - } - - return callbackInvoked; -} diff --git a/app/components/admin/product-category.js b/app/components/admin/product-category.js new file mode 100644 index 00000000..6d81baa9 --- /dev/null +++ b/app/components/admin/product-category.js @@ -0,0 +1 @@ +export { default } from '@fleetbase/pallet-engine/components/admin/product-category'; diff --git a/app/serializers/stock-adjustment.js b/app/serializers/stock-adjustment.js new file mode 100644 index 00000000..d79dd4f4 --- /dev/null +++ b/app/serializers/stock-adjustment.js @@ -0,0 +1 @@ +export { default } from '@fleetbase/pallet-engine/serializers/stock-adjustment'; diff --git a/app/utils/apply-context-component-arguments.js b/app/utils/apply-context-component-arguments.js deleted file mode 100644 index ba44c2f2..00000000 --- a/app/utils/apply-context-component-arguments.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@fleetbase/pallet-engine/utils/apply-context-component-arguments'; diff --git a/app/utils/context-component-callback.js b/app/utils/context-component-callback.js deleted file mode 100644 index 88cc0cb7..00000000 --- a/app/utils/context-component-callback.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@fleetbase/pallet-engine/utils/context-component-callback'; diff --git a/server/src/Http/Controllers/InventoryController.php b/server/src/Http/Controllers/InventoryController.php index d0ba6038..b2bc61ad 100644 --- a/server/src/Http/Controllers/InventoryController.php +++ b/server/src/Http/Controllers/InventoryController.php @@ -66,6 +66,8 @@ public function createRecord(Request $request) 'product_uuid' => data_get($data, 'product_uuid'), 'batch_number' => data_get($data, 'batch_number', now()->format('Y-m-d')), 'quantity' => data_get($data, 'quantity', 0), + 'expiry_date_at' => data_get($data, 'expiry_date_at'), + 'manufacture_date_at' => data_get($data, 'manufacture_date_at'), ]); $batch->save(); @@ -80,7 +82,6 @@ public function createRecord(Request $request) 'min_quantity' => data_get($data, 'min_quantity', 0), 'comments' => data_get($data, 'comments'), 'expiry_date_at' => data_get($data, 'expiry_date_at'), - 'manufactured_date_at' => data_get($data, 'manufactured_date_at') ]); $inventory->batch()->associate($batch); diff --git a/server/src/Http/Resources/Batch.php b/server/src/Http/Resources/Batch.php index 1df28dd4..e822dc89 100644 --- a/server/src/Http/Resources/Batch.php +++ b/server/src/Http/Resources/Batch.php @@ -17,13 +17,15 @@ class Batch extends FleetbaseResource public function toArray($request) { return [ - 'id' => $this->when(Http::isInternalRequest(), $this->incrementing_id, $this->public_id), - 'uuid' => $this->when(Http::isInternalRequest(), $this->uuid), - 'public_id' => $this->when(Http::isInternalRequest(), $this->public_id), - 'batch_number' => $this->batch_number, - 'batch_quantity' => $this->batch_quantity, - 'updated_at' => $this->updated_at, - 'created_at' => $this->created_at, + 'id' => $this->when(Http::isInternalRequest(), $this->incrementing_id, $this->public_id), + 'uuid' => $this->when(Http::isInternalRequest(), $this->uuid), + 'public_id' => $this->when(Http::isInternalRequest(), $this->public_id), + 'batch_number' => $this->batch_number, + 'batch_quantity' => $this->batch_quantity, + 'updated_at' => $this->updated_at, + 'created_at' => $this->created_at, + 'expiry_date_at' => $this->expiry_date_at, + 'manufacture_date_at' => $this->manufacture_date_at, ]; } } diff --git a/server/src/Http/Resources/Inventory.php b/server/src/Http/Resources/Inventory.php index 09d01c54..db766e17 100644 --- a/server/src/Http/Resources/Inventory.php +++ b/server/src/Http/Resources/Inventory.php @@ -32,7 +32,6 @@ public function toArray($request) 'min_quantity' => (int) $this->min_quantity, 'comments' => $this->comments, 'expiry_date_at' => $this->expiry_date_at, - 'manufactured_date_at' => $this->manufactured_date_at, 'updated_at' => $this->updated_at, 'created_at' => $this->created_at, ]; diff --git a/server/src/Http/Resources/StockAdjustment.php b/server/src/Http/Resources/StockAdjustment.php new file mode 100644 index 00000000..c8edc8ec --- /dev/null +++ b/server/src/Http/Resources/StockAdjustment.php @@ -0,0 +1,32 @@ + $this->when(Http::isInternalRequest(), $this->id, $this->public_id), + 'uuid' => $this->when(Http::isInternalRequest(), $this->uuid), + 'public_id' => $this->when(Http::isInternalRequest(), $this->public_id), + 'product_uuid' => $this->product_uuid, + 'product' => $this->whenLoaded('product', $this->product), + 'quantity' => (int) $this->quantity, + 'before_quantity' => (int) $this->before_quantity, + 'after_quantity' => (int) $this->after_quantity, + 'updated_at' => $this->updated_at, + 'created_at' => $this->created_at, + ]; + } +} diff --git a/server/src/Models/Batch.php b/server/src/Models/Batch.php index fc8bc1af..f432ffb0 100644 --- a/server/src/Models/Batch.php +++ b/server/src/Models/Batch.php @@ -59,6 +59,10 @@ class Batch extends Model 'updated_at', ]; + public $timestamps = true; + + protected $dates = ['expiry_date_at']; + /** * The attributes that should be cast to native types. * @@ -120,4 +124,14 @@ public function product() { return $this->belongsTo(Product::class); } + + protected static function boot() + { + parent::boot(); + + static::creating(function ($model) { + $model->created_at = now(); + $model->manufacture_date_at = now(); + }); + } } diff --git a/server/src/Models/Inventory.php b/server/src/Models/Inventory.php index 8d7581dd..8f04a239 100644 --- a/server/src/Models/Inventory.php +++ b/server/src/Models/Inventory.php @@ -48,6 +48,10 @@ class Inventory extends Model * @var array */ protected $fillable = [ + 'supplier', + 'supplier_uuid', + 'company_uuid', + 'created_by_uuid', 'manufactured_date_at', 'expiry_date_at', 'created_at', @@ -88,14 +92,15 @@ class Inventory extends Model */ protected $hidden = []; - protected $with = ['product', 'batch', 'warehouse']; + protected $with = ['product', 'batch', 'warehouse', 'supplier']; + + protected $filterParams = ['comments', 'expiry_date_at', 'status', 'company', 'createdBy',]; - protected $filterParams = ['supplier_uuid', 'comments', 'expiry_date_at', 'status', 'company', 'createdBy', 'supplier']; - /** * @return null|int */ - public function getIncrementingIdAttribute(): ?int { + public function getIncrementingIdAttribute(): ?int + { return static::select('id')->where('uuid', $this->uuid)->value('id'); } @@ -143,28 +148,30 @@ public function scopeSummarizeByProduct($query) return $query ->selectRaw(' pallet_inventories.product_uuid, + pallet_inventories.batch_uuid, + pallet_inventories.supplier_uuid, + pallet_inventories.warehouse_uuid, MAX(pallet_inventories.created_at) as latest_created_at, MAX(pallet_inventories.updated_at) as latest_updated_at, MAX(pallet_inventories.public_id) as latest_public_id, MAX(pallet_inventories.uuid) as latest_uuid, MAX(pallet_inventories.comments) as latest_comments, - GROUP_CONCAT(DISTINCT pallet_batches.uuid) as batch_uuids, - GROUP_CONCAT(DISTINCT pallet_batches.batch_number) as batch_numbers, + (SELECT GROUP_CONCAT(DISTINCT pallet_batches.uuid) FROM pallet_batches WHERE pallet_batches.uuid = pallet_inventories.batch_uuid) as batch_uuids, + (SELECT GROUP_CONCAT(DISTINCT pallet_batches.batch_number) FROM pallet_batches WHERE pallet_batches.uuid = pallet_inventories.batch_uuid) as batch_numbers, SUM(pallet_inventories.quantity) as total_quantity, MAX(pallet_inventories.min_quantity) as minimum_quantity, MAX(pallet_inventories.expiry_date_at) as latest_expiry_date_at ') ->leftJoin('pallet_batches', 'pallet_inventories.batch_uuid', '=', 'pallet_batches.uuid') - ->groupBy('pallet_inventories.product_uuid'); + ->groupBy('pallet_inventories.product_uuid', 'pallet_inventories.batch_uuid', 'pallet_inventories.supplier_uuid', 'pallet_inventories.warehouse_uuid'); } - + protected static function boot() { parent::boot(); static::creating(function ($model) { $model->created_at = now(); - $model->manufactured_date_at = now(); }); } } diff --git a/server/src/Models/StockAdjustment.php b/server/src/Models/StockAdjustment.php index 95e02b91..d2810f1d 100644 --- a/server/src/Models/StockAdjustment.php +++ b/server/src/Models/StockAdjustment.php @@ -2,13 +2,16 @@ namespace Fleetbase\Pallet\Models; +use Fleetbase\Casts\Json; use Fleetbase\Models\Model; use Fleetbase\Traits\HasApiModelBehavior; +use Fleetbase\Traits\HasPublicId; use Fleetbase\Traits\HasUuid; class StockAdjustment extends Model { use HasUuid; + use HasPublicId; use HasApiModelBehavior; /** @@ -25,12 +28,26 @@ class StockAdjustment extends Model */ protected $singularName = 'stock_adjustment'; + /** + * Overwrite both entity resource name with `payloadKey`. + * + * @var string + */ + protected $payloadKey = 'stock_adjustment'; + + /** + * The type of `public_id` to generate. + * + * @var string + */ + protected $publicIdType = 'stock_adjustment'; + /** * These attributes that can be queried. * * @var array */ - protected $searchableColumns = ['uuid', 'public_id', 'company_uuid', 'created_by_uuid', 'product_uuid', 'created_at']; + protected $searchableColumns = ['reason']; /** * The attributes that are mass assignable. @@ -44,7 +61,6 @@ class StockAdjustment extends Model 'created_by_uuid', 'product_uuid', 'assignee_uuid', - 'meta', 'type', 'reason', 'approval_required', @@ -61,7 +77,7 @@ class StockAdjustment extends Model * @var array */ protected $casts = [ - 'meta' => 'json', + 'meta' => JSON::class, ]; /** @@ -105,7 +121,7 @@ public function createdBy() */ public function product() { - return $this->belongsTo(Entity::class, 'product_uuid', 'uuid'); + return $this->belongsTo(Product::class, 'product_uuid', 'uuid'); } public function user() diff --git a/tests/integration/components/admin/product-category-test.js b/tests/integration/components/admin/product-category-test.js new file mode 100644 index 00000000..5e37ee18 --- /dev/null +++ b/tests/integration/components/admin/product-category-test.js @@ -0,0 +1,26 @@ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'dummy/tests/helpers'; +import { render } from '@ember/test-helpers'; +import { hbs } from 'ember-cli-htmlbars'; + +module('Integration | Component | admin/product-category', function (hooks) { + setupRenderingTest(hooks); + + test('it renders', async function (assert) { + // Set any properties with this.set('myProperty', 'value'); + // Handle any actions with this.set('myAction', function(val) { ... }); + + await render(hbs``); + + assert.dom(this.element).hasText(''); + + // Template block usage: + await render(hbs` + + template block text + + `); + + assert.dom(this.element).hasText('template block text'); + }); +}); diff --git a/tests/unit/serializers/stock-adjustment-test.js b/tests/unit/serializers/stock-adjustment-test.js new file mode 100644 index 00000000..524f1180 --- /dev/null +++ b/tests/unit/serializers/stock-adjustment-test.js @@ -0,0 +1,23 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'dummy/tests/helpers'; + +module('Unit | Serializer | stock adjustment', function (hooks) { + setupTest(hooks); + + // Replace this with your real tests. + test('it exists', function (assert) { + let store = this.owner.lookup('service:store'); + let serializer = store.serializerFor('stock-adjustment'); + + assert.ok(serializer); + }); + + test('it serializes records', function (assert) { + let store = this.owner.lookup('service:store'); + let record = store.createRecord('stock-adjustment', {}); + + let serializedRecord = record.serialize(); + + assert.ok(serializedRecord); + }); +}); diff --git a/tests/unit/utils/apply-context-component-arguments-test.js b/tests/unit/utils/apply-context-component-arguments-test.js deleted file mode 100644 index 17998ef4..00000000 --- a/tests/unit/utils/apply-context-component-arguments-test.js +++ /dev/null @@ -1,10 +0,0 @@ -import applyContextComponentArguments from 'dummy/utils/apply-context-component-arguments'; -import { module, test } from 'qunit'; - -module('Unit | Utility | apply-context-component-arguments', function () { - // TODO: Replace this with your real tests. - test('it works', function (assert) { - let result = applyContextComponentArguments(); - assert.ok(result); - }); -}); diff --git a/tests/unit/utils/context-component-callback-test.js b/tests/unit/utils/context-component-callback-test.js deleted file mode 100644 index b0855fb9..00000000 --- a/tests/unit/utils/context-component-callback-test.js +++ /dev/null @@ -1,10 +0,0 @@ -import contextComponentCallback from 'dummy/utils/context-component-callback'; -import { module, test } from 'qunit'; - -module('Unit | Utility | context-component-callback', function () { - // TODO: Replace this with your real tests. - test('it works', function (assert) { - let result = contextComponentCallback(); - assert.ok(result); - }); -});