diff --git a/apps/gauzy/src/app/pages/inventory/inventory-routing.module.ts b/apps/gauzy/src/app/pages/inventory/inventory-routing.module.ts new file mode 100644 index 0000000000..471655e743 --- /dev/null +++ b/apps/gauzy/src/app/pages/inventory/inventory-routing.module.ts @@ -0,0 +1,16 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { InventoryComponent } from './inventory-table/inventory.component'; + +const routes: Routes = [ + { + path: '', + component: InventoryComponent + } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class InventoryRoutingModule {} diff --git a/apps/gauzy/src/app/pages/inventory/inventory-table/inventory.component.html b/apps/gauzy/src/app/pages/inventory/inventory-table/inventory.component.html new file mode 100644 index 0000000000..66128abdc3 --- /dev/null +++ b/apps/gauzy/src/app/pages/inventory/inventory-table/inventory.component.html @@ -0,0 +1,44 @@ + + +

{{ 'INVENTORY_PAGE.HEADER' | translate }}

+
+ + +
+ + + +
+ + + +
+
diff --git a/apps/gauzy/src/app/pages/inventory/inventory-table/inventory.component.scss b/apps/gauzy/src/app/pages/inventory/inventory-table/inventory.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apps/gauzy/src/app/pages/inventory/inventory-table/inventory.component.ts b/apps/gauzy/src/app/pages/inventory/inventory-table/inventory.component.ts new file mode 100644 index 0000000000..baeb908261 --- /dev/null +++ b/apps/gauzy/src/app/pages/inventory/inventory-table/inventory.component.ts @@ -0,0 +1,107 @@ +import { Component, OnInit, ViewChild } from '@angular/core'; +import { LocalDataSource } from 'ng2-smart-table'; +import { FormGroup } from '@angular/forms'; +import { TranslateService } from '@ngx-translate/core'; +import { NbDialogService, NbToastrService } from '@nebular/theme'; +import { Router } from '@angular/router'; +import { TranslationBaseComponent } from '../../../@shared/language-base/translation-base.component'; +import { InventoryItem, Inventory } from '@gauzy/models'; + +@Component({ + selector: 'ngx-inventory', + templateUrl: './inventory.component.html', + styleUrls: ['./inventory.component.scss'] +}) +export class InventoryComponent extends TranslationBaseComponent + implements OnInit { + settingsSmartTable: object; + loading = true; + selectedItem: InventoryItem; + inventory: Inventory; + smartTableSource = new LocalDataSource(); + form: FormGroup; + disableButton = true; + + @ViewChild('inventoryTable', { static: false }) inventoryTable; + + ngOnInit(): void { + this.loadSmartTable(); + this._applyTranslationOnSmartTable(); + this.loadSettings(); + } + + constructor( + readonly translateService: TranslateService, + private dialogService: NbDialogService, + private toastrService: NbToastrService, + private router: Router + ) { + super(translateService); + } + + async loadSmartTable() { + this.settingsSmartTable = { + actions: false, + columns: { + name: { + title: this.getTranslation('INVENTORY_PAGE.NAME'), + type: 'string' + }, + productType: { + title: this.getTranslation('INVENTORY_PAGE.PRODUCT_TYPE'), + type: 'string' + }, + productCategory: { + title: this.getTranslation( + 'INVENTORY_PAGE.PRODUCT_CATEGORY' + ), + type: 'string' + }, + description: { + title: this.getTranslation('INVENTORY_PAGE.DESCRIPTION'), + type: 'string', + filter: false + }, + unitCost: { + title: this.getTranslation('INVENTORY_PAGE.UNIT_COST'), + type: 'number', + filter: false + }, + quantity: { + title: this.getTranslation('INVENTORY_PAGE.QUANTITY'), + type: 'number', + filter: false + } + } + }; + } + + async save() {} + + async delete() {} + + async loadSettings() { + this.selectedItem = null; + // const { items } = await this.inventoryService.getAll(); + const items = []; + this.loading = false; + this.smartTableSource.load(items); + } + + //todo + async selectItem($event: any) { + if ($event.isSelected) { + this.selectedItem = $event.data; + this.disableButton = false; + this.inventoryTable.grid.dataSet.willSelect = false; + } else { + this.disableButton = true; + } + } + + _applyTranslationOnSmartTable() { + this.translateService.onLangChange.subscribe(() => { + this.loadSmartTable(); + }); + } +} diff --git a/apps/gauzy/src/app/pages/inventory/inventory.module.ts b/apps/gauzy/src/app/pages/inventory/inventory.module.ts new file mode 100644 index 0000000000..1b4aa5f12c --- /dev/null +++ b/apps/gauzy/src/app/pages/inventory/inventory.module.ts @@ -0,0 +1,41 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { InventoryRoutingModule } from './inventory-routing.module'; +import { InventoryComponent } from './inventory-table/inventory.component'; +import { + NbCardModule, + NbButtonModule, + NbIconModule, + NbSpinnerModule +} from '@nebular/theme'; +import { Ng2SmartTableModule } from 'ng2-smart-table'; +import { TableComponentsModule } from '../../@shared/table-components/table-components.module'; +import { TranslateModule, TranslateLoader } from '@ngx-translate/core'; +import { TranslateHttpLoader } from '@ngx-translate/http-loader'; +import { HttpClient } from '@angular/common/http'; + +export function HttpLoaderFactory(http: HttpClient) { + return new TranslateHttpLoader(http, './assets/i18n/', '.json'); +} + +@NgModule({ + declarations: [InventoryComponent], + imports: [ + InventoryRoutingModule, + CommonModule, + NbCardModule, + NbButtonModule, + NbIconModule, + Ng2SmartTableModule, + TableComponentsModule, + TranslateModule.forChild({ + loader: { + provide: TranslateLoader, + useFactory: HttpLoaderFactory, + deps: [HttpClient] + } + }), + NbSpinnerModule + ] +}) +export class InventoryModule {} diff --git a/apps/gauzy/src/app/pages/pages-routing.module.ts b/apps/gauzy/src/app/pages/pages-routing.module.ts index 7e8cca3814..5d32b31c71 100644 --- a/apps/gauzy/src/app/pages/pages-routing.module.ts +++ b/apps/gauzy/src/app/pages/pages-routing.module.ts @@ -183,6 +183,13 @@ const routes: Routes = [ (m) => m.EquipmentModule ) }, + { + path: 'inventory', + loadChildren: () => + import('./inventory/inventory.module').then( + (m) => m.InventoryModule + ) + }, { path: 'tags', loadChildren: () => diff --git a/apps/gauzy/src/app/pages/pages.component.ts b/apps/gauzy/src/app/pages/pages.component.ts index a02ffd79eb..ce13642069 100644 --- a/apps/gauzy/src/app/pages/pages.component.ts +++ b/apps/gauzy/src/app/pages/pages.component.ts @@ -263,6 +263,16 @@ export class PagesComponent implements OnInit, OnDestroy { translationKey: 'MENU.EQUIPMENT' } }, + { + title: 'Inventory', + icon: 'grid-outline', + link: '/pages/organization/inventory', + data: { + translated: false, + // permissionKeys: [PermissionsEnum.ALL_ORG_VIEW], + translationKey: 'MENU.INVENTORY' + } + }, { title: 'Tags', icon: 'pricetags-outline', diff --git a/apps/gauzy/src/assets/i18n/en.json b/apps/gauzy/src/assets/i18n/en.json index 844c86964a..645f6b3c88 100644 --- a/apps/gauzy/src/assets/i18n/en.json +++ b/apps/gauzy/src/assets/i18n/en.json @@ -330,7 +330,8 @@ "TIME_REPORTS": "Time Reports", "ACCOUNTING_REPORTS": "Accounting Reports", "PAYMENT_GATEWAYS": "Payment Gateways", - "CUSTOM_SMTP": "Custom SMTP" + "CUSTOM_SMTP": "Custom SMTP", + "INVENTORY": "Inventory" }, "SETTINGS_MENU": { "THEMES": "Themes", @@ -715,6 +716,35 @@ "ITEM_RETURNED_BEFORE_ERR": "Item should be returned before " } }, + "INVENTORY_PAGE": { + "HEADER": "Inventory", + "ADD_INVENTORY_ITEM": "Add inventory item", + "EDIT_INVENTPRY_ITEM": "Edit inventory item", + "INVENTORY_ITEM_DELETED": "Inventory item deleted!", + "INVENTORY_ITEM_SAVED": "Inventory item saved!", + "NAME": "Name", + "ENABLED": "Enabled", + "PRODUCT_TYPE": "Product type", + "PRODUCT_CATEGORY": "Product category", + "IS_SUBSCRIPTION": "Is subscription", + "IS_PURCHASE_AUTOMATICALLY": "Is purchased automatically", + "CAN_BE_SOLD": "Can be sold", + "CAN_BE_PURCHASED": "Can be purchased", + "CAN_BE_CHARGED": "Can be charged", + "CAN_BE_RENTED": "Can be rented", + "IS_EQUIPMENT": "Is equipment", + "INTERNATIONAL_REFERENCE": "International reference", + "CODE": "Code", + "NOTES": "Notes", + "DESCRIPTION": "Description", + "UNIT_COST": "Unit Cost", + "UNIT_COST_CURRENCY": "Unit cost currency", + "RETAIL_PRICE": "Retail price", + "RETAIL_PRICE_CURRENCY": "Retail price currency", + "QUANTITY": "Quantity", + "TAXES": "Taxes", + "BILLING_INVOICING_POLICY": "Billing invoicing policy" + }, "TASKS_PAGE": { "HEADER": "Tasks", "ADD_TASKS": "Add Tasks", diff --git a/libs/models/src/index.ts b/libs/models/src/index.ts index 904068d03c..7491629b00 100644 --- a/libs/models/src/index.ts +++ b/libs/models/src/index.ts @@ -39,6 +39,8 @@ export * from './lib/expense-category.model'; export * from './lib/integration.model'; export * from './lib/hubstaff.model'; export * from './lib/tenant.model'; +export * from './lib/product.model'; +export * from './lib/inventory.model'; export { Role, RolesEnum } from './lib/role.model'; export { BaseEntityModel } from './lib/base-entity.model'; diff --git a/libs/models/src/lib/inventory.model.ts b/libs/models/src/lib/inventory.model.ts new file mode 100644 index 0000000000..182f18c801 --- /dev/null +++ b/libs/models/src/lib/inventory.model.ts @@ -0,0 +1,14 @@ +import { Product as IProduct } from './product.model'; +import { BaseEntityModel as IBaseEntityModel } from '..'; + +export interface Inventory extends IBaseEntityModel { + stock: InventoryStock[]; + trackInventory: boolean; +} + +export interface InventoryItem extends IBaseEntityModel, IProduct {} + +export interface InventoryStock { + item: InventoryItem; + currentStock: number; +} diff --git a/libs/models/src/lib/product.model.ts b/libs/models/src/lib/product.model.ts new file mode 100644 index 0000000000..22c2907222 --- /dev/null +++ b/libs/models/src/lib/product.model.ts @@ -0,0 +1,36 @@ +import { BaseEntityModel as IBaseEntityModel } from './base-entity.model'; + +export interface Product { + name: string; + enabled: boolean; + productTypeId: string; + productCategoryId: string; + isSubscription: boolean; + isPurchaseAutomatically: boolean; + canBeSold: boolean; + canBePurchased: boolean; + canBeCharged: boolean; + canBeRented: boolean; + isEquipment: boolean; + internalReference: string; + code: string; + notes: string; + description: string; + unitCost: number; + unitCostCurrency: string; + retailPrice: number; + retailPriceCurrency: string; + quantity: number; + taxes: number; + billingInvoicingPolicy: string; + productType?: ProductType; + productCategory?: ProductCategory; +} + +export interface ProductType extends IBaseEntityModel { + name: string; +} + +export interface ProductCategory extends IBaseEntityModel { + name: string; +}