tin-spa 20.2.9 → 20.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -789,6 +789,7 @@ var InvoiceStatus;
789
789
  InvoiceStatus[InvoiceStatus["Submitted"] = 1] = "Submitted";
790
790
  InvoiceStatus[InvoiceStatus["Paid"] = 2] = "Paid";
791
791
  InvoiceStatus[InvoiceStatus["Discarded"] = 3] = "Discarded";
792
+ InvoiceStatus[InvoiceStatus["Paying"] = 4] = "Paying"; // Changed: Added Paying status for partially paid invoices
792
793
  })(InvoiceStatus || (InvoiceStatus = {}));
793
794
  // Inventory receipt status tracking enum - mirrors backend
794
795
  var InventoryReceiptStatus;
@@ -1467,6 +1468,7 @@ class DataServiceLib {
1467
1468
  this.capCategories = new CapItem;
1468
1469
  this.capSubCategories = new CapItem;
1469
1470
  this.capBrands = new CapItem;
1471
+ this.capGPTCaches = new CapItem; // Added: GPT Cache capability property
1470
1472
  this.capSuppliers = new CapItem;
1471
1473
  this.capTasks = new CapItem;
1472
1474
  this.capHR = new CapItem;
@@ -1484,6 +1486,7 @@ class DataServiceLib {
1484
1486
  this.capAccounting = new CapItem;
1485
1487
  this.capAccounts = new CapItem;
1486
1488
  this.capAggregates = new CapItem; // Added: Capability for aggregates view
1489
+ this.capAging = new CapItem; // Added: Capability for aging report
1487
1490
  this.capTransactionTypes = new CapItem;
1488
1491
  this.capTransactions = new CapItem;
1489
1492
  this.capAccountingInvoices = new CapItem;
@@ -1495,6 +1498,7 @@ class DataServiceLib {
1495
1498
  this.capInventoryDashboard = new CapItem;
1496
1499
  this.capInventoryStock = new CapItem; // Changed: Added new cap item for inventory stock view
1497
1500
  this.capProducts = new CapItem;
1501
+ this.capBundleProducts = new CapItem; // Added: Capability for bundle products
1498
1502
  this.capInventoryItems = new CapItem;
1499
1503
  this.capPurchaseOrders = new CapItem;
1500
1504
  this.capInventoryReceipts = new CapItem;
@@ -1824,6 +1828,45 @@ class DataServiceLib {
1824
1828
  loadAction: { url: 'suppliers/all/x' },
1825
1829
  formConfig: this.supplierFormConfig
1826
1830
  };
1831
+ //--------------------------GPT Caches-------------------------
1832
+ this.gptCachesFormConfig = {
1833
+ security: { allow: [this.capGPTCaches] },
1834
+ title: 'GPT Cache',
1835
+ fields: [
1836
+ { name: 'gptCacheID', type: 'number', readonly: true, alias: 'ID' },
1837
+ { name: 'methodName', type: 'text', required: true, alias: 'Method Name', span: true },
1838
+ { name: 'requestHash', type: 'text', readonly: true, alias: 'Request Hash', span: true },
1839
+ { name: 'requestText', type: 'text-area', required: true, alias: 'Request Text', span: true, rows: 4 },
1840
+ { name: 'responseJson', type: 'text-area', required: true, alias: 'Response JSON', span: true, rows: 4 },
1841
+ { name: 'matchCount', type: 'number', alias: 'Match Count' },
1842
+ { name: 'hitCount', type: 'number', alias: 'Hit Count' },
1843
+ { name: 'createdDate', type: 'datetime', readonly: true, alias: 'Created' },
1844
+ { name: 'updatedDate', type: 'datetime', readonly: true, alias: 'Updated' }
1845
+ ],
1846
+ loadAction: { url: 'gptcaches/id' },
1847
+ heroField: 'gptCacheID',
1848
+ };
1849
+ this.gptCachesTableConfig = {
1850
+ showFilter: true,
1851
+ minColumns: ['methodName', 'requestText', 'hitCount'],
1852
+ columns: [
1853
+ { name: 'methodName', alias: 'Method' },
1854
+ { name: 'requestText', alias: 'Request', maxLength: 100 },
1855
+ { name: 'responseJson', alias: 'Response', maxLength: 100 },
1856
+ { name: 'requestHash', alias: 'Hash', maxLength: 20 },
1857
+ { name: 'matchCount', type: 'number', alias: 'Matches', icon: { name: 'verified', color: 'green', condition: (x) => x.matchCount > 3 } },
1858
+ { name: 'hitCount', type: 'number', alias: 'Hits', icon: { name: 'trending_up', color: 'blue', condition: (x) => x.hitCount > 10 } },
1859
+ { name: 'createdDate', type: 'datetime', alias: 'Created' },
1860
+ { name: 'updatedDate', type: 'datetime', alias: 'Updated' }
1861
+ ],
1862
+ buttons: [
1863
+ { name: 'view', dialog: true, tip: 'View Cache Details' },
1864
+ { name: 'edit', dialog: true, action: { url: 'gptcaches?action=edit', method: 'post' }, tip: 'Edit Cache' },
1865
+ { name: 'delete', confirm: { message: 'Are you sure you want to delete this cache entry?' }, action: { url: 'gptcaches?action=delete', method: 'post' }, tip: 'Delete Cache' }
1866
+ ],
1867
+ loadAction: { url: 'gptcaches/all/x' },
1868
+ formConfig: this.gptCachesFormConfig
1869
+ };
1827
1870
  //--------------------------Notifications-------------------------
1828
1871
  this.notificationOptions = [
1829
1872
  { name: 'UI Only', value: 1 },
@@ -2030,7 +2073,7 @@ class DataServiceLib {
2030
2073
  this.capAdmin.name = "cap2";
2031
2074
  this.capAdmin.display = "Admin";
2032
2075
  this.capAdmin.icon = "security";
2033
- this.capAdmin.capSubItems = [this.capUsers, this.capRoles, this.capLogs, this.capSettings];
2076
+ this.capAdmin.capSubItems = [this.capUsers, this.capRoles, this.capLogs, this.capGPTCaches, this.capSettings]; // Added: GPT Cache to admin menu
2034
2077
  this.capUsers.name = "cap3";
2035
2078
  this.capUsers.display = "Users";
2036
2079
  this.capUsers.link = "home/admin/users";
@@ -2079,7 +2122,11 @@ class DataServiceLib {
2079
2122
  this.capBrands.display = "Brands";
2080
2123
  this.capBrands.link = "home/admin/brands";
2081
2124
  this.capBrands.icon = "loyalty";
2082
- //cap104 available
2125
+ // Added: GPT Cache capability
2126
+ this.capGPTCaches.name = "cap104";
2127
+ this.capGPTCaches.display = "GPT Cache";
2128
+ this.capGPTCaches.link = "home/admin/gpt-caches";
2129
+ this.capGPTCaches.icon = "memory";
2083
2130
  //cap105 available
2084
2131
  this.capSuppliers.name = "cap15";
2085
2132
  this.capSuppliers.display = "Suppliers";
@@ -2133,7 +2180,7 @@ class DataServiceLib {
2133
2180
  this.capAccounting.display = "Accounting";
2134
2181
  this.capAccounting.icon = "account_balance";
2135
2182
  this.capAccounting.capSubItems = [this.capAccounts, this.capTransactions, this.capAggregates, this.capTransactionTypes, this.capAccountingInvoices,
2136
- this.capAccountingOutstandingInvoices];
2183
+ this.capAging, this.capAccountingOutstandingInvoices]; // Added: capAging to submenu
2137
2184
  this.capAccounts.name = "cap52"; // Changed: Fixed conflict with capBrands (was cap14)
2138
2185
  this.capAccounts.display = "Accounts";
2139
2186
  this.capAccounts.link = "home/admin/accounting-accounts";
@@ -2142,6 +2189,10 @@ class DataServiceLib {
2142
2189
  this.capAggregates.display = "Aggregates";
2143
2190
  this.capAggregates.link = "home/admin/accounting-aggregates";
2144
2191
  this.capAggregates.icon = "bar_chart";
2192
+ this.capAging.name = "cap58"; // Added: Aging capability
2193
+ this.capAging.display = "Aging Report";
2194
+ this.capAging.link = "home/admin/accounting-aging";
2195
+ this.capAging.icon = "schedule";
2145
2196
  this.capTransactionTypes.name = "cap28";
2146
2197
  this.capTransactionTypes.display = "Transaction Types";
2147
2198
  this.capTransactionTypes.link = "home/admin/accounting-transaction-types";
@@ -2177,7 +2228,7 @@ class DataServiceLib {
2177
2228
  this.capInventory.name = "cap34";
2178
2229
  this.capInventory.display = "Inventory";
2179
2230
  this.capInventory.icon = "inventory";
2180
- this.capInventory.capSubItems = [this.capSales, this.capInventoryReceipts, this.capProducts, this.capInventoryStock, this.capInventoryTransactions, this.capInventoryItems,
2231
+ this.capInventory.capSubItems = [this.capSales, this.capInventoryReceipts, this.capProducts, this.capBundleProducts, this.capInventoryStock, this.capInventoryTransactions, this.capInventoryItems,
2181
2232
  this.capPurchaseOrders, this.capSalesOrders, this.capRequisitions, this.capInventoryAdjustments,
2182
2233
  this.capInventoryReturns, this.capRequisitionReturns, this.capProductionRecipes, this.capProductionOrders, this.capInventoryDashboard];
2183
2234
  this.capInventoryDashboard.name = "cap35";
@@ -2192,6 +2243,10 @@ class DataServiceLib {
2192
2243
  this.capProducts.display = "Products";
2193
2244
  this.capProducts.link = "home/admin/inventory-products";
2194
2245
  this.capProducts.icon = "category";
2246
+ this.capBundleProducts.name = "cap57"; // Added: Bundle products capability
2247
+ this.capBundleProducts.display = "Bundle Products";
2248
+ this.capBundleProducts.link = "home/admin/bundle-products";
2249
+ this.capBundleProducts.icon = "inventory_2";
2195
2250
  this.capInventoryItems.name = "cap37";
2196
2251
  this.capInventoryItems.display = "Inventory Items";
2197
2252
  this.capInventoryItems.link = "home/admin/inventory-items";
@@ -2624,17 +2679,65 @@ class AccountingService {
2624
2679
  buttons: [this.accountCreateButton, this.finAccounEditButton]
2625
2680
  };
2626
2681
  //--------------------------Invoices-------------------------
2682
+ // Added: Invoice payment form configuration
2683
+ this.invoicePaymentFormConfig = {
2684
+ security: { allow: [this.dataService.capAccountingInvoices] },
2685
+ title: 'Record Payment',
2686
+ fixedTitle: true,
2687
+ fields: [
2688
+ { name: 'invoiceID', type: 'number', hidden: true },
2689
+ { name: 'paymentDate', type: 'date', alias: 'Payment Date', required: true },
2690
+ { name: 'method', type: 'select', alias: 'Payment Method', required: true, defaultValue: 1, loadAction: { url: 'invoicepayments/list/methods' } },
2691
+ { name: 'outstandingAmount', type: 'label', alias: 'Outstanding', readonly: true },
2692
+ { name: 'amount', type: 'money', alias: 'Amount', required: true, span: true },
2693
+ { name: 'reference', type: 'text', alias: 'Reference', span: true }
2694
+ ],
2695
+ };
2696
+ this.invoicePaymentCreateButton = { name: 'edit', action: { url: 'invoicepayments?action=create', method: 'post', successMessage: 'Payment Recorded' } };
2697
+ this.invoicePaymentsCreateDetailsDialogConfig = {
2698
+ formConfig: this.invoicePaymentFormConfig,
2699
+ heroField: 'invoiceID',
2700
+ mode: 'edit',
2701
+ buttons: [
2702
+ this.invoicePaymentCreateButton
2703
+ ]
2704
+ };
2705
+ // Added: Invoice payments table for history tracking
2706
+ this.invoicePaymentsTableConfig = {
2707
+ tabTitle: 'Payment History',
2708
+ showFilter: false,
2709
+ minColumns: ['paymentDate', 'amount', 'methodName'],
2710
+ columns: [
2711
+ { name: 'invoicePaymentID', type: 'number', alias: 'ID', hiddenCondition: () => true },
2712
+ { name: 'paymentDate', type: 'date', alias: 'Date' },
2713
+ { name: 'amount', type: 'money', alias: 'Amount' },
2714
+ { name: 'methodName', type: 'text', alias: 'Method' },
2715
+ { name: 'reference', type: 'text', alias: 'Reference' }
2716
+ ],
2717
+ buttons: [
2718
+ { name: 'migrate-payments', display: 'Migrate Legacy Payments', inHeader: true, icon: { name: 'sync', color: 'green' },
2719
+ action: { url: 'invoicepayments/migrate', method: 'post',
2720
+ successMessage: 'Migration completed successfully' },
2721
+ confirm: { message: 'This will migrate existing invoice payments from the legacy PaidAmount field to the new InvoicePayments table. Continue?' }
2722
+ }
2723
+ ],
2724
+ loadAction: { url: 'invoicepayments/x/x' }, loadCriteria: 'invoice', loadIDField: 'invoiceID'
2725
+ };
2627
2726
  // Invoice form configuration with customer and status fields
2628
2727
  this.invoiceFormConfig = {
2629
2728
  security: { allow: [this.dataService.capAccountingInvoices] },
2630
2729
  title: 'Invoice',
2631
2730
  includeAudit: true,
2632
2731
  fields: [
2633
- { name: 'invoiceDate', type: 'date' },
2634
- { name: 'invoiceID', type: 'number', readonly: true, hideOnCreate: true },
2635
- { name: 'customerID', alias: 'Customer', type: 'select', loadAction: { url: 'customers/list/x' }, detailsConfig: this.dataService.customerDetailsConfig },
2732
+ { name: 'invoiceDate', type: 'date', required: true },
2733
+ { name: 'invoiceNumber', type: 'text', alias: 'Invoice #' }, // Changed: Made editable on create for optional manual numbering
2734
+ { name: 'customerID', alias: 'Customer', type: 'select', required: true, loadAction: { url: 'customers/list/x' }, detailsConfig: this.dataService.customerDetailsConfig },
2735
+ { name: 'paymentTerms', type: 'select', alias: 'Payment Terms', required: true, defaultValue: 30, loadAction: { url: 'invoices/list/payment-terms' } },
2736
+ { name: 'dueDate', type: 'date', alias: 'Due Date', readonly: true, hideOnCreate: true },
2636
2737
  { name: 'status', type: 'select', hideOnCreate: true, readonly: true, loadAction: { url: 'invoices/list/statuses' } },
2637
2738
  { name: 'totalDisplay', type: 'label', alias: 'Total', format: 'text', readonly: true, hideOnCreate: true },
2739
+ { name: 'paidAmount', type: 'label', alias: 'Paid', readonly: true, hideOnCreate: true },
2740
+ { name: 'outstandingAmount', type: 'label', alias: 'Outstanding', readonly: true, hideOnCreate: true }
2638
2741
  ],
2639
2742
  loadAction: { url: 'invoices/id' },
2640
2743
  heroField: 'invoiceID',
@@ -2644,6 +2747,7 @@ class AccountingService {
2644
2747
  tabTitle: 'Invoice Items',
2645
2748
  showFilter: false,
2646
2749
  minColumns: ['description', 'quantity', 'amount'],
2750
+ causeFormRefresh: true,
2647
2751
  columns: [
2648
2752
  { name: 'invoiceItemID', type: 'number', alias: 'ID', hiddenCondition: () => true },
2649
2753
  { name: 'description', type: 'text', alias: 'Description' },
@@ -2652,7 +2756,9 @@ class AccountingService {
2652
2756
  { name: 'amount', type: 'money', alias: 'Amount' }
2653
2757
  ],
2654
2758
  buttons: [
2655
- { name: 'create', display: 'Add Item', dialog: true, action: { url: 'invoiceitems?action=create', method: 'post' }, disabled: x => x.status != InvoiceStatus.Draft },
2759
+ { name: 'create', display: 'Add Item', dialog: true, action: { url: 'invoiceitems?action=create', method: 'post' },
2760
+ disabled: x => x.status != InvoiceStatus.Draft
2761
+ },
2656
2762
  { name: 'edit', dialog: true, action: { url: 'invoiceitems?action=edit', method: 'post' }, disabled: x => x.status != InvoiceStatus.Draft },
2657
2763
  { name: 'delete', inDialog: true, icon: { name: 'delete', color: 'red' }, action: { url: 'invoiceitems?action=delete', method: 'post', successMessage: 'Deleted' }, confirm: { message: 'Delete this item?' }, disabled: x => x.status != InvoiceStatus.Draft }
2658
2764
  ],
@@ -2660,8 +2766,8 @@ class AccountingService {
2660
2766
  formConfig: {
2661
2767
  title: 'Invoice Item',
2662
2768
  fields: [
2663
- { name: 'description', type: 'text', required: true },
2664
- { name: 'quantity', type: 'number', required: true },
2769
+ { name: 'description', type: 'text', required: true, span: true },
2770
+ { name: 'quantity', type: 'number', required: true, defaultValue: 1 },
2665
2771
  { name: 'unitPrice', type: 'number', alias: 'Unit Price', required: true },
2666
2772
  { name: 'amount', type: 'number', readonly: true, hideOnCreate: true }
2667
2773
  ],
@@ -2669,18 +2775,32 @@ class AccountingService {
2669
2775
  }
2670
2776
  };
2671
2777
  // Invoice action buttons
2672
- this.invoicePayButton = { name: 'pay', inDialog: true, display: 'Pay', icon: { name: 'attach_money', color: 'green' }, action: { url: 'invoices?action=pay', method: 'post', successMessage: 'Paid' }, confirm: { message: 'All loads on invoice have been Paid ?' }, visible: x => x.status == InvoiceStatus.Submitted };
2673
- this.invoiceDiscardButton = { name: 'discard', inDialog: true, display: 'Discard', icon: { name: 'close', color: 'red' }, action: { url: 'invoices?action=discard', method: 'post', successMessage: 'Discarded' }, confirm: { message: 'Invoice will be marked as cancelled and all related loads removed ?' }, visible: x => x.status == InvoiceStatus.Draft };
2674
- this.invoiceSubmitButton = { name: 'submit', inDialog: true, display: 'Submit', icon: { name: 'send', }, action: { url: 'invoices?action=submit', method: 'post', successMessage: 'Submitted' }, confirm: { message: 'Submit ?' }, visible: x => x.status == InvoiceStatus.Draft, disabled: x => x.totalAmount == 0 };
2675
- this.invoiceEditButton = { name: 'edit', dialog: true, action: { url: 'invoices?action=edit', method: 'post', }, confirm: { message: 'Proceed ?' }, disabled: x => x.status == InvoiceStatus.Paid };
2778
+ this.invoiceRecordPaymentButton = { name: 'record-payment', display: 'Record Payment', dialog: true, icon: { name: 'payment', color: 'blue' },
2779
+ detailsConfig: this.invoicePaymentsCreateDetailsDialogConfig,
2780
+ visible: x => (x.status == InvoiceStatus.Submitted || x.status == InvoiceStatus.Paying) && x.outstandingAmount > 0 // Changed: Allow on both Submitted and Paying
2781
+ }; // Changed: Use detailsConfig to open payment form dialog
2782
+ this.invoiceDiscardButton = { name: 'discard', inDialog: true, display: 'Discard', icon: { name: 'close', color: 'red' },
2783
+ action: { url: 'invoices?action=discard', method: 'post', successMessage: 'Discarded' }, confirm: { message: 'Invoice will be marked as cancelled?' },
2784
+ visible: x => x.status == InvoiceStatus.Draft || x.status == InvoiceStatus.Submitted // Changed: Only allow on Draft and Submitted
2785
+ };
2786
+ this.invoiceSubmitButton = { name: 'submit', inDialog: true, display: 'Submit', icon: { name: 'send', },
2787
+ action: { url: 'invoices?action=submit', method: 'post', successMessage: 'Submitted' },
2788
+ confirm: { message: 'Submit invoice? This will record revenue and create an accounts receivable entry.' },
2789
+ visible: x => x.status == InvoiceStatus.Draft,
2790
+ disabled: x => x.totalAmount == 0
2791
+ };
2792
+ this.invoiceEditButton = { name: 'edit', dialog: true, action: { url: 'invoices?action=edit', method: 'post', }, confirm: { message: 'Proceed ?' },
2793
+ visible: x => x.status == InvoiceStatus.Draft || x.status == InvoiceStatus.Submitted // Changed: Disable on Paying and Paid
2794
+ };
2676
2795
  this.invoiceDownloadButton = { name: 'pdf', display: 'Download PDF', inDialog: true, icon: { name: 'picture_as_pdf', color: 'red' } };
2677
- // Invoice details dialog with items and action buttons
2796
+ // Invoice details dialog with items, payments history, and action buttons
2678
2797
  this.invoiceDetailsDialogConfig = {
2679
2798
  formConfig: this.invoiceFormConfig,
2680
- tableConfigs: [this.invoiceItemsTableConfig],
2799
+ tableConfigs: [this.invoiceItemsTableConfig, this.invoicePaymentsTableConfig],
2681
2800
  heroField: 'invoiceID',
2682
- buttons: [this.invoiceSubmitButton, this.invoicePayButton, this.invoiceDiscardButton, this.invoiceEditButton]
2801
+ buttons: [this.invoiceSubmitButton, this.invoiceRecordPaymentButton, this.invoiceDiscardButton, this.invoiceEditButton]
2683
2802
  };
2803
+ this.invoiceViewButton = { name: 'view', dialog: true, detailsConfig: this.invoiceDetailsDialogConfig };
2684
2804
  // Invoice summary tiles
2685
2805
  this.invoicesTileConfig = {
2686
2806
  clickable: true,
@@ -2694,34 +2814,75 @@ class AccountingService {
2694
2814
  // Main invoices table configuration
2695
2815
  this.invoicesTableConfig = {
2696
2816
  showFilter: true,
2697
- minColumns: ['invoiceID', 'customerName', 'statusName'],
2817
+ minColumns: ['invoiceNumber', 'customerName', 'statusName'],
2698
2818
  minButtons: ['view', 'pdf'],
2699
2819
  flatButtons: true,
2700
2820
  collapseButtons: true,
2701
2821
  greyOut: x => x.status == InvoiceStatus.Paid,
2702
2822
  columns: [
2703
- { name: 'invoiceID', type: 'number', alias: 'ID' },
2823
+ { name: 'invoiceNumber', type: 'text', alias: 'Invoice #' },
2704
2824
  { name: 'customerName', type: 'text', alias: 'Customer' },
2705
2825
  { name: 'invoiceDate', type: 'date' },
2706
- { name: 'statusName', type: 'chip', alias: 'Status', colors: [{ name: '#FFCC80', condition: x => x.status == InvoiceStatus.Draft }, { name: '#90CAF9', condition: x => x.status == InvoiceStatus.Submitted }, { name: '#A5D6A7', condition: x => x.status == InvoiceStatus.Paid }, { name: '#EF9A9A', condition: x => x.status == InvoiceStatus.Discarded }] },
2826
+ { name: 'dueDate', type: 'date', alias: 'Due Date' },
2827
+ { name: 'daysOverdue', type: 'number', alias: 'Days Overdue', },
2828
+ { name: 'statusName', type: 'chip', alias: 'Status', detailsConfig: this.invoiceDetailsDialogConfig,
2829
+ colors: [
2830
+ { name: '#FFCC80', condition: x => x.status == InvoiceStatus.Draft },
2831
+ { name: '#90CAF9', condition: x => x.status == InvoiceStatus.Submitted },
2832
+ { name: '#FFD54F', condition: x => x.status == InvoiceStatus.Paying }, // Changed: Added Paying status color (amber/yellow)
2833
+ { name: '#A5D6A7', condition: x => x.status == InvoiceStatus.Paid },
2834
+ { name: '#EF9A9A', condition: x => x.status == InvoiceStatus.Discarded }
2835
+ ]
2836
+ },
2707
2837
  { name: 'itemCount', type: 'number', alias: 'Items' },
2708
2838
  { name: 'totalAmount', type: 'money', alias: 'Total' },
2709
2839
  { name: 'paidAmount', type: 'money', alias: 'Paid' },
2710
2840
  { name: 'outstandingAmount', type: 'money', alias: 'Outstanding' }
2711
2841
  ],
2712
2842
  buttons: [
2713
- { name: 'create', display: 'Create', dialog: true, action: { url: 'invoices?action=create', method: 'post' } },
2843
+ { name: 'create', display: 'Create', dialog: true, action: { url: 'invoices?action=create', method: 'post' }, onSuccessButton: this.invoiceViewButton },
2714
2844
  { name: 'view', dialog: true, detailsConfig: this.invoiceDetailsDialogConfig },
2715
- this.invoiceDownloadButton,
2845
+ this.invoiceRecordPaymentButton,
2716
2846
  this.invoiceSubmitButton,
2717
- this.invoicePayButton,
2847
+ // this.invoicePayButton,
2718
2848
  this.invoiceDiscardButton,
2719
- this.invoiceEditButton
2849
+ this.invoiceEditButton,
2850
+ this.invoiceDownloadButton,
2720
2851
  ],
2721
2852
  loadAction: { url: 'invoices/all/x' },
2722
2853
  formConfig: this.invoiceFormConfig,
2723
2854
  tileConfig: this.invoicesTileConfig
2724
2855
  };
2856
+ //--------------------------Aging Report-------------------------
2857
+ // Added: Aging report summary tiles
2858
+ this.agingTileConfig = {
2859
+ // clickable: true,
2860
+ tiles: [
2861
+ { name: 'current', alias: 'Current', color: '#4CAF50', info: 'Not yet due', },
2862
+ { name: 'days30', alias: '1-30 Days', color: '#FFC107', info: '1-30 days overdue', },
2863
+ { name: 'days60', alias: '31-60 Days', color: '#FF9800', info: '31-60 days overdue', },
2864
+ { name: 'days90', alias: '61-90 Days', color: '#F44336', info: '61-90 days overdue', },
2865
+ { name: 'days90Plus', alias: '90+ Days', color: '#B71C1C', info: 'Over 90 days overdue', },
2866
+ { name: 'total', alias: 'Total Outstanding', color: '#9E9E9E', info: 'Total outstanding amount' }
2867
+ ],
2868
+ // loadAction: { url: 'invoices/aging-summary/x' }
2869
+ };
2870
+ // Added: Base aging table configuration with common properties
2871
+ this.agingBaseTableConfig = {
2872
+ showFilter: true,
2873
+ minColumns: ['invoiceNumber', 'customerName', 'outstandingAmount'],
2874
+ columns: [
2875
+ { name: 'invoiceNumber', type: 'text', alias: 'Invoice #' },
2876
+ { name: 'customerName', type: 'text', alias: 'Customer' },
2877
+ { name: 'invoiceDate', type: 'date', alias: 'Invoice Date' },
2878
+ { name: 'dueDate', type: 'date', alias: 'Due Date' },
2879
+ { name: 'daysOverdue', type: 'number', alias: 'Days Overdue' },
2880
+ { name: 'totalAmount', type: 'money', alias: 'Total' },
2881
+ { name: 'paidAmount', type: 'money', alias: 'Paid' },
2882
+ { name: 'outstandingAmount', type: 'money', alias: 'Outstanding' }
2883
+ ],
2884
+ buttons: [{ name: 'view', dialog: true, detailsConfig: this.invoiceDetailsDialogConfig }]
2885
+ };
2725
2886
  //--------------------------Customer Invoices-------------------------
2726
2887
  // Customer invoice form without customerID field
2727
2888
  this.customerInvoiceFormConfig = {
@@ -2735,19 +2896,24 @@ class AccountingService {
2735
2896
  loadCriteria: 'customer-invoice',
2736
2897
  buttons: []
2737
2898
  };
2899
+ // Added: Customer invoice payments table (read-only)
2900
+ this.customerInvoicePaymentsTableConfig = {
2901
+ ...this.invoicePaymentsTableConfig,
2902
+ loadCriteria: 'customer-invoice'
2903
+ };
2738
2904
  // Customer invoice details dialog (read-only)
2739
2905
  this.customerInvoiceDetailsDialogConfig = {
2740
2906
  formConfig: this.customerInvoiceFormConfig,
2741
- tableConfigs: [this.customerInvoiceItemsTableConfig],
2907
+ tableConfigs: [this.customerInvoiceItemsTableConfig, this.customerInvoicePaymentsTableConfig],
2742
2908
  heroField: 'invoiceID',
2743
2909
  buttons: []
2744
2910
  };
2745
2911
  // Customer invoices table configuration
2746
2912
  this.customerInvoicesTableConfig = {
2747
2913
  ...this.invoicesTableConfig,
2748
- minColumns: ['invoiceID', 'totalDisplay'],
2914
+ minColumns: ['invoiceNumber', 'totalDisplay'],
2749
2915
  minButtons: ['view', 'pdf'],
2750
- columns: [...this.invoicesTableConfig.columns.filter(x => ['invoiceID', 'invoiceDate', 'statusName', 'itemCount', 'totalAmount'].includes(x.name))],
2916
+ columns: [...this.invoicesTableConfig.columns.filter(x => ['invoiceNumber', 'invoiceDate', 'dueDate', 'statusName', 'itemCount', 'totalAmount', 'paidAmount', 'outstandingAmount'].includes(x.name))],
2751
2917
  buttons: [{ name: 'view', dialog: true, detailsConfig: this.customerInvoiceDetailsDialogConfig }, this.invoiceDownloadButton],
2752
2918
  loadAction: { url: 'invoices/customer-all/x' }
2753
2919
  };
@@ -3413,12 +3579,13 @@ class InventoryService {
3413
3579
  { name: 'name', type: 'text', required: true, alias: 'Product Name', section: 'productInfo', infoMessage: 'The unique name of the product' },
3414
3580
  { name: 'sku', type: 'text', alias: 'SKU (optional)', section: 'productInfo', infoMessage: 'Stock Keeping Unit for inventory tracking (optional)' }, // Changed: Removed required flag
3415
3581
  { name: 'description', type: 'text', alias: 'Description', span: true, section: 'productInfo', infoMessage: 'Detailed description of the product' },
3582
+ { name: 'isBundle', type: 'checkbox', alias: 'Bundle Product', span: true, section: 'productInfo', infoMessage: 'Check if this is a bundle/combo product made up of other products' }, // Added: Bundle product flag
3416
3583
  { name: 'pricingInfo', type: 'section', alias: 'Pricing' },
3417
3584
  { name: 'sellingPrice', type: 'money', required: true, alias: 'Selling Price', section: 'pricingInfo', infoMessage: 'Default selling price per unit' },
3418
3585
  { name: 'inventoryInfo', type: 'section', alias: 'Inventory Settings', collapsedCondition: x => x.productID },
3419
- { name: 'isSerialized', type: 'checkbox', alias: 'Serialized Product', span: true, section: 'inventoryInfo', infoMessage: 'Track individual items by serial number' }, // Changed: Added span to cross two columns
3586
+ { name: 'isSerialized', type: 'checkbox', alias: 'Serialized Product', span: true, section: 'inventoryInfo', infoMessage: 'Track individual items by serial number', hiddenCondition: x => x.isBundle }, // Changed: Hide for bundles
3420
3587
  { name: 'baseUnit', type: 'select', required: true, alias: 'Base Unit', section: 'inventoryInfo', loadAction: { url: 'products/list/base-unit' }, defaultFirstValue: true, infoMessage: 'Unit of measure for inventory tracking' }, // Changed: Added defaultFirstValue flag
3421
- { name: 'minimumInventoryLevel', type: 'number', required: true, alias: 'Minimum Inventory Level', section: 'inventoryInfo', infoMessage: 'Alert threshold for low stock' },
3588
+ { name: 'minimumInventoryLevel', type: 'number', required: true, alias: 'Minimum Inventory Level', section: 'inventoryInfo', infoMessage: 'Alert threshold for low stock', hiddenCondition: x => x.isBundle }, // Changed: Hide for bundles
3422
3589
  { name: 'categoryInfo', type: 'section', alias: 'Categorization', collapsed: true },
3423
3590
  { name: 'categoryID', type: 'select', alias: 'Category', nullable: true, section: 'categoryInfo', loadAction: { url: 'categories/list/x' }, detailsConfig: this.generalService.categoryDetailsConfig, infoMessage: 'The main product category' }, // Changed: Added nullable flag
3424
3591
  { name: 'subCategoryID', type: 'select', alias: 'Sub Category', section: 'categoryInfo', loadAction: { url: 'subcategories/list/x' }, detailsConfig: this.generalService.subCategoryDetailsConfig, infoMessage: 'More specific product classification' },
@@ -3453,6 +3620,102 @@ class InventoryService {
3453
3620
  loadAction: { url: 'products/all/x' },
3454
3621
  formConfig: this.productFormConfig
3455
3622
  };
3623
+ //--------------------------Product Components (Bundle Configuration)-------------------------
3624
+ // Product component form for adding/editing bundle components
3625
+ this.productComponentFormConfig = {
3626
+ title: 'Bundle Component',
3627
+ fixedTitle: true,
3628
+ fields: [
3629
+ { name: 'productComponentID', type: 'number', alias: 'ID', hidden: true },
3630
+ { name: 'productID', type: 'number', alias: 'Bundle Product ID', hidden: true },
3631
+ { name: 'componentProductID', type: 'select', alias: 'Component Product', required: true, detailsConfig: this.productDetailsConfig,
3632
+ loadAction: { url: 'products/list/components' },
3633
+ infoMessage: 'Select a non-bundle product to include in this bundle'
3634
+ },
3635
+ { name: 'quantity', type: 'number', alias: 'Quantity', required: true, defaultValue: 1, infoMessage: 'Number of units required per bundle' }
3636
+ ],
3637
+ loadAction: { url: 'productcomponents/id' },
3638
+ heroField: 'productComponentID'
3639
+ };
3640
+ // Product components table for bundle details
3641
+ this.productComponentsTableConfig = {
3642
+ tabTitle: 'Components',
3643
+ showFilter: false,
3644
+ elevation: 'none',
3645
+ flatButtons: true,
3646
+ causeFormRefresh: true,
3647
+ minColumns: ['componentProductName', 'quantity', 'estimatedCost'],
3648
+ columns: [
3649
+ { name: 'componentProductName', type: 'text', alias: 'Component' },
3650
+ { name: 'quantity', type: 'number', alias: 'Quantity per Bundle' },
3651
+ { name: 'componentCurrentStock', type: 'number', alias: 'Available Stock' },
3652
+ { name: 'estimatedCost', type: 'money', alias: 'Est. Cost' }
3653
+ ],
3654
+ buttons: [
3655
+ { name: 'create', display: 'Add Component', dialog: true, action: { url: 'productcomponents?action=create', method: 'post' } },
3656
+ { name: 'edit', dialog: true, action: { url: 'productcomponents?action=edit', method: 'post' } },
3657
+ { name: 'delete', dialog: true, action: { url: 'productcomponents?action=delete', method: 'post' } }
3658
+ ],
3659
+ loadAction: { url: 'productcomponents/x/x' }, loadCriteria: 'bundle', loadIDField: 'productID',
3660
+ formConfig: this.productComponentFormConfig
3661
+ };
3662
+ // Bundle product form configuration
3663
+ this.bundleProductFormConfig = {
3664
+ security: { allow: [this.dataService.capProducts] },
3665
+ title: 'Bundle Product',
3666
+ fixedTitle: true,
3667
+ multiColumn: true,
3668
+ includeAudit: true,
3669
+ fields: [
3670
+ { name: 'productInfo', type: 'section', alias: 'Bundle Information' },
3671
+ { name: 'name', type: 'text', required: true, alias: 'Bundle Name', section: 'productInfo', infoMessage: 'Name for this product bundle' },
3672
+ { name: 'sku', type: 'text', alias: 'SKU (optional)', section: 'productInfo', infoMessage: 'Stock Keeping Unit for bundle tracking' },
3673
+ { name: 'description', type: 'text', alias: 'Description', span: true, section: 'productInfo', infoMessage: 'Description of what is included in this bundle' },
3674
+ { name: 'isBundle', type: 'checkbox', alias: 'Is Bundle', hidden: true, defaultValue: true },
3675
+ { name: 'pricingInfo', type: 'section', alias: 'Pricing' },
3676
+ { name: 'sellingPrice', type: 'money', required: true, alias: 'Bundle Price', section: 'pricingInfo', infoMessage: 'Selling price for the complete bundle' },
3677
+ { name: 'bundleEstimatedCost', type: 'money', alias: 'Estimated Cost', section: 'pricingInfo', readonly: true, hideOnCreate: true, infoMessage: 'Total estimated cost based on component costs' },
3678
+ { name: 'inventoryInfo', type: 'section', alias: 'Inventory Settings', collapsedCondition: x => x.productID },
3679
+ { name: 'baseUnit', type: 'select', required: true, alias: 'Base Unit', section: 'inventoryInfo', loadAction: { url: 'products/list/base-unit' }, defaultFirstValue: true, infoMessage: 'Unit of measure for bundle' },
3680
+ { name: 'bundleAvailableQuantity', type: 'number', alias: 'Available to Sell', section: 'inventoryInfo', readonly: true, hideOnCreate: true, infoMessage: 'Number of bundles that can be assembled from current component stock' },
3681
+ ],
3682
+ loadAction: { url: 'products/id' },
3683
+ heroField: 'productID'
3684
+ };
3685
+ this.bundleProductDetailsConfig = {
3686
+ formConfig: this.bundleProductFormConfig,
3687
+ tableConfigs: [this.productComponentsTableConfig],
3688
+ heroField: 'productID',
3689
+ buttons: [
3690
+ { name: 'create', display: 'Create Bundle', dialog: true, action: { url: 'products?action=create', method: 'post' } },
3691
+ { name: 'edit', dialog: true, action: { url: 'products?action=edit', method: 'post' } },
3692
+ { name: 'delete', dialog: true, action: { url: 'products?action=delete', method: 'post' } }
3693
+ ]
3694
+ };
3695
+ this.bundleProductViewButton = { name: 'view', dialog: true, detailsConfig: this.bundleProductDetailsConfig };
3696
+ this.bundleProductCreateButton = { name: 'create', display: 'Create Bundle', dialog: true, action: { url: 'products?action=create', method: 'post' } };
3697
+ // Table configuration for bundle products
3698
+ this.bundleProductsTableConfig = {
3699
+ showFilter: true,
3700
+ flatButtons: true,
3701
+ minColumns: ['name', 'sku', 'bundleAvailableQuantity'],
3702
+ columns: [
3703
+ { name: 'name', type: 'text', alias: 'Bundle Name' },
3704
+ { name: 'sku', type: 'text', alias: 'SKU' },
3705
+ { name: 'bundleAvailableQuantity', type: 'number', alias: 'Available', icon: { name: 'info', color: '#2196F3', condition: (x) => x.bundleAvailableQuantity > 0 } }, // Changed: Removed infoMessage as it's not valid for columns
3706
+ { name: 'bundleEstimatedCost', type: 'money', alias: 'Est. Cost' },
3707
+ { name: 'sellingPrice', type: 'money', alias: 'Selling Price' },
3708
+ { name: 'categoryName', type: 'text', alias: 'Category' }
3709
+ ],
3710
+ buttons: [
3711
+ this.bundleProductCreateButton,
3712
+ this.bundleProductViewButton,
3713
+ { name: 'edit', dialog: true, action: { url: 'products?action=edit', method: 'post' } },
3714
+ { name: 'delete', dialog: true, action: { url: 'products?action=delete', method: 'post' } }
3715
+ ],
3716
+ loadAction: { url: 'products/bundles/x' },
3717
+ formConfig: this.bundleProductFormConfig
3718
+ };
3456
3719
  //--------------------------Requisitions-------------------------
3457
3720
  this.requisitionItemFormConfig = {
3458
3721
  title: 'Requisition Item',
@@ -3822,7 +4085,7 @@ class InventoryService {
3822
4085
  title: 'Sales Order Item',
3823
4086
  fixedTitle: true, // Changed: Use fixedTitle to avoid duplication with 'Add Item' button
3824
4087
  fields: [
3825
- { name: 'productID', type: 'select', alias: 'Product', required: true, loadAction: { url: 'products/list/x' }, detailsConfig: this.productDetailsConfig },
4088
+ { name: 'productID', type: 'select', alias: 'Product', required: true, loadAction: { url: 'products/list/x' }, detailsConfig: this.productDetailsConfig }, // Changed: Show all products including bundles
3826
4089
  { name: 'quantity', type: 'number', alias: 'Quantity', required: true },
3827
4090
  { name: 'unitPrice', type: 'money', alias: 'Unit Price', required: true },
3828
4091
  { name: 'discount', type: 'money', alias: 'Discount' },
@@ -4045,7 +4308,7 @@ class InventoryService {
4045
4308
  fixedTitle: true,
4046
4309
  fields: [
4047
4310
  {
4048
- name: 'productID', type: 'select', required: true, alias: 'Product', span: true, loadAction: { url: 'products/list/x' },
4311
+ name: 'productID', type: 'select', required: true, alias: 'Product', span: true, loadAction: { url: 'products/list/x' }, // Changed: Show all products including bundles
4049
4312
  onSelectChange: (selectedProductId, formData, option) => {
4050
4313
  if (option && option.sellingPrice) {
4051
4314
  formData.unitPrice = option.sellingPrice; // Changed: Auto-populate unitPrice with product's sellingPrice
@@ -4103,7 +4366,7 @@ class InventoryService {
4103
4366
  { name: 'multipleProducts', type: 'checkbox', alias: 'Multiple Products', defaultValue: false, hideOnExists: true, infoMessage: 'Check this box if you want to add multiple products to this sale' }, // Changed: Added checkbox for multiple products mode
4104
4367
  { name: 'saleType', type: 'select', required: true, alias: 'Sale Type', section: 'saleInfo', options: [{ name: 'Quick Sale', value: 0 }, { name: 'From Order', value: 1 }], defaultFirstValue: true, hideOnCreate: true, infoMessage: 'Select if this is a quick sale or created from an order' },
4105
4368
  { name: 'quickSaleItem', type: 'section', alias: 'Quick Sale Item', hiddenCondition: x => x.multipleProducts === true || x.saleID }, // Changed: Section for single product entry
4106
- { name: 'productID', type: 'select', required: true, alias: 'Product', section: 'quickSaleItem', span: true, loadAction: { url: 'products/list/x' }, hiddenCondition: x => x.multipleProducts === true, // Changed: Product selection for quick sale
4369
+ { name: 'productID', type: 'select', required: true, alias: 'Product', section: 'quickSaleItem', span: true, loadAction: { url: 'products/list/x' }, hiddenCondition: x => x.multipleProducts === true, // Changed: Show all products including bundles
4107
4370
  onSelectChange: (selectedProductId, formData, option) => {
4108
4371
  if (option && option.sellingPrice) {
4109
4372
  formData.unitPrice = option.sellingPrice; // Changed: Auto-populate unitPrice with product's sellingPrice
@@ -4242,6 +4505,7 @@ class InventoryService {
4242
4505
  };
4243
4506
  this.productDetailsConfig = {
4244
4507
  formConfig: this.productFormConfig,
4508
+ tableConfigs: [this.productComponentsTableConfig], // Added: Show components for bundle products
4245
4509
  heroField: 'productID',
4246
4510
  buttons: [this.productCreateButton, this.productEditButton, this.productDeleteButton]
4247
4511
  };
@@ -14055,11 +14319,11 @@ class ChangePasswordComponent {
14055
14319
  }
14056
14320
  }
14057
14321
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: ChangePasswordComponent, deps: [{ token: i1$2.Router }, { token: i2.Location }, { token: HttpService }, { token: MessageService }, { token: DataServiceLib }, { token: AuthService }, { token: i1$2.ActivatedRoute }], target: i0.ɵɵFactoryTarget.Component }); }
14058
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: ChangePasswordComponent, isStandalone: false, selector: "spa-change-password", ngImport: i0, template: "<h4>Change Password</h4>\r\n<hr>\r\n\r\n\r\n<div class=\"container tin-grid\" style=\"font-size:14px;\">\r\n\r\n <div class=\"fill\">\r\n\r\n <spa-text id=\"txtuserName\" display=\"Username\" [(value)]=\"changePassword.userName\" [readonly]=\"true\"></spa-text>\r\n <spa-text id=\"txtPassword\" display=\"Current Password\" [(value)]=\"changePassword.currentPassword\" type=\"password\"></spa-text>\r\n <spa-text id=\"txtNewPassword\" display=\"New Password\" [(value)]=\"changePassword.newPassword\" type=\"password\"></spa-text>\r\n <spa-text id=\"txtConfirmPassword\" display=\"Confirm Password\" [(value)]=\"changePassword.confirmPassword\" type=\"password\"></spa-text>\r\n <button id=\"btnChange\" mat-raised-button color=\"primary\" (click)=\"change()\" cdkFocusInitial>Change</button>\r\n\r\n </div>\r\n\r\n <div class=\"alert alert-info\" style=\"font-size: 14px;\" role=\"alert\">\r\n <b>*Please consider these requirements for your new password.</b> <br><br>\r\n\r\n At least 8 characters<br>\r\n At least 1 uppercase letter (A-Z)<br>\r\n At least 2 lowercase letters (a-z)<br>\r\n At least 1 digit (0-9)<br>\r\n At least 1 special character (~`! \u2026)<br>\r\n\r\n </div>\r\n\r\n</div>\r\n\r\n\r\n<!-- <div class=\"container\">\r\n\r\n <div class=\"d-flex justify-content-center row align-items-center\" >\r\n\r\n <div class=\"col\">\r\n\r\n <div class=\"tin-input \" style=\"font-size:14px;\">\r\n\r\n <div class=\"col\" *ngIf=\"changePassword.userName!=''\">\r\n <spa-text id=\"txtuserName\" display=\"Username\" [(value)]=\"changePassword.userName\" width=\"300px\" [readonly]=\"true\"></spa-text>\r\n </div>\r\n\r\n <div class=\"col\" *ngIf=\"!myRole[dataService.capUsers.name]\">\r\n <spa-text id=\"txtPassword\" display=\"Current Password\" [(value)]=\"changePassword.currentPassword\" width=\"300px\" type=\"password\"></spa-text>\r\n </div>\r\n\r\n <div class=\"col\">\r\n <spa-text id=\"txtNewPassword\" display=\"New Password\" [(value)]=\"changePassword.newPassword\" width=\"300px\" type=\"password\"></spa-text>\r\n </div>\r\n\r\n <div class=\"col\">\r\n <spa-text id=\"txtConfirmPassword\" display=\"Confirm Password\" [(value)]=\"changePassword.confirmPassword\" width=\"300px\" type=\"password\"></spa-text>\r\n </div>\r\n\r\n <div class=\"col mt-3\">\r\n <button id=\"btnChange\" mat-raised-button color=\"primary\" (click)=\"change()\" cdkFocusInitial>Change</button>\r\n </div>\r\n\r\n </div>\r\n\r\n </div>\r\n\r\n <div class=\"col\">\r\n\r\n <div class=\"alert alert-info\" style=\"font-size: 14px;\" role=\"alert\">\r\n <b>*Please consider these requirements for your new password.</b> <br><br>\r\n\r\n At least 8 characters<br>\r\n At least 1 uppercase letter (A-Z)<br>\r\n At least 2 lowercase letters (a-z)<br>\r\n At least 1 digit (0-9)<br>\r\n At least 1 special character (~`! \u2026)<br>\r\n\r\n </div>\r\n\r\n </div>\r\n\r\n\r\n\r\n\r\n\r\n </div>\r\n</div> -->\r\n\r\n", styles: [""], dependencies: [{ kind: "component", type: i5$1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: TextComponent, selector: "spa-text", inputs: ["appearance", "readonly", "hint", "display", "placeholder", "value", "format", "type", "width", "copyContent", "clearContent", "required", "min", "max", "regex", "suffix", "infoMessage"], outputs: ["valueChange", "leave", "enterPress"] }] }); }
14322
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: ChangePasswordComponent, isStandalone: false, selector: "spa-change-password", ngImport: i0, template: "<h4>Change Password</h4>\r\n<hr>\r\n\r\n\r\n<div class=\"container tin-grid\" style=\"font-size:14px;\">\r\n\r\n <div class=\"fill\">\r\n\r\n <spa-text id=\"txtuserName\" display=\"Username\" [(value)]=\"changePassword.userName\" [readonly]=\"true\"></spa-text>\r\n <spa-text-mask id=\"txtPassword\" display=\"Current Password\" [(value)]=\"changePassword.currentPassword\" type=\"password\"></spa-text-mask>\r\n <spa-text-mask id=\"txtNewPassword\" display=\"New Password\" [(value)]=\"changePassword.newPassword\" type=\"password\"></spa-text-mask>\r\n <spa-text-mask id=\"txtConfirmPassword\" display=\"Confirm Password\" [(value)]=\"changePassword.confirmPassword\" type=\"password\"></spa-text-mask>\r\n <button id=\"btnChange\" mat-raised-button color=\"primary\" (click)=\"change()\" cdkFocusInitial>Change</button>\r\n\r\n </div>\r\n\r\n <div class=\"alert alert-info\" style=\"font-size: 14px;\" role=\"alert\">\r\n <b>*Please consider these requirements for your new password.</b> <br><br>\r\n\r\n At least 8 characters<br>\r\n At least 1 uppercase letter (A-Z)<br>\r\n At least 2 lowercase letters (a-z)<br>\r\n At least 1 digit (0-9)<br>\r\n At least 1 special character (~`! \u2026)<br>\r\n\r\n </div>\r\n\r\n</div>\r\n\r\n\r\n<!-- <div class=\"container\">\r\n\r\n <div class=\"d-flex justify-content-center row align-items-center\" >\r\n\r\n <div class=\"col\">\r\n\r\n <div class=\"tin-input \" style=\"font-size:14px;\">\r\n\r\n <div class=\"col\" *ngIf=\"changePassword.userName!=''\">\r\n <spa-text id=\"txtuserName\" display=\"Username\" [(value)]=\"changePassword.userName\" width=\"300px\" [readonly]=\"true\"></spa-text>\r\n </div>\r\n\r\n <div class=\"col\" *ngIf=\"!myRole[dataService.capUsers.name]\">\r\n <spa-text id=\"txtPassword\" display=\"Current Password\" [(value)]=\"changePassword.currentPassword\" width=\"300px\" type=\"password\"></spa-text>\r\n </div>\r\n\r\n <div class=\"col\">\r\n <spa-text id=\"txtNewPassword\" display=\"New Password\" [(value)]=\"changePassword.newPassword\" width=\"300px\" type=\"password\"></spa-text>\r\n </div>\r\n\r\n <div class=\"col\">\r\n <spa-text id=\"txtConfirmPassword\" display=\"Confirm Password\" [(value)]=\"changePassword.confirmPassword\" width=\"300px\" type=\"password\"></spa-text>\r\n </div>\r\n\r\n <div class=\"col mt-3\">\r\n <button id=\"btnChange\" mat-raised-button color=\"primary\" (click)=\"change()\" cdkFocusInitial>Change</button>\r\n </div>\r\n\r\n </div>\r\n\r\n </div>\r\n\r\n <div class=\"col\">\r\n\r\n <div class=\"alert alert-info\" style=\"font-size: 14px;\" role=\"alert\">\r\n <b>*Please consider these requirements for your new password.</b> <br><br>\r\n\r\n At least 8 characters<br>\r\n At least 1 uppercase letter (A-Z)<br>\r\n At least 2 lowercase letters (a-z)<br>\r\n At least 1 digit (0-9)<br>\r\n At least 1 special character (~`! \u2026)<br>\r\n\r\n </div>\r\n\r\n </div>\r\n\r\n\r\n\r\n\r\n\r\n </div>\r\n</div> -->\r\n\r\n", styles: [""], dependencies: [{ kind: "component", type: i5$1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: TextComponent, selector: "spa-text", inputs: ["appearance", "readonly", "hint", "display", "placeholder", "value", "format", "type", "width", "copyContent", "clearContent", "required", "min", "max", "regex", "suffix", "infoMessage"], outputs: ["valueChange", "leave", "enterPress"] }, { kind: "component", type: TextMaskComponent, selector: "spa-text-mask", inputs: ["appearance", "readonly", "hint", "display", "placeholder", "value", "width", "required", "min", "max", "regex", "infoMessage"], outputs: ["valueChange", "leave", "enterPress"] }] }); }
14059
14323
  }
14060
14324
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: ChangePasswordComponent, decorators: [{
14061
14325
  type: Component,
14062
- args: [{ selector: 'spa-change-password', standalone: false, template: "<h4>Change Password</h4>\r\n<hr>\r\n\r\n\r\n<div class=\"container tin-grid\" style=\"font-size:14px;\">\r\n\r\n <div class=\"fill\">\r\n\r\n <spa-text id=\"txtuserName\" display=\"Username\" [(value)]=\"changePassword.userName\" [readonly]=\"true\"></spa-text>\r\n <spa-text id=\"txtPassword\" display=\"Current Password\" [(value)]=\"changePassword.currentPassword\" type=\"password\"></spa-text>\r\n <spa-text id=\"txtNewPassword\" display=\"New Password\" [(value)]=\"changePassword.newPassword\" type=\"password\"></spa-text>\r\n <spa-text id=\"txtConfirmPassword\" display=\"Confirm Password\" [(value)]=\"changePassword.confirmPassword\" type=\"password\"></spa-text>\r\n <button id=\"btnChange\" mat-raised-button color=\"primary\" (click)=\"change()\" cdkFocusInitial>Change</button>\r\n\r\n </div>\r\n\r\n <div class=\"alert alert-info\" style=\"font-size: 14px;\" role=\"alert\">\r\n <b>*Please consider these requirements for your new password.</b> <br><br>\r\n\r\n At least 8 characters<br>\r\n At least 1 uppercase letter (A-Z)<br>\r\n At least 2 lowercase letters (a-z)<br>\r\n At least 1 digit (0-9)<br>\r\n At least 1 special character (~`! \u2026)<br>\r\n\r\n </div>\r\n\r\n</div>\r\n\r\n\r\n<!-- <div class=\"container\">\r\n\r\n <div class=\"d-flex justify-content-center row align-items-center\" >\r\n\r\n <div class=\"col\">\r\n\r\n <div class=\"tin-input \" style=\"font-size:14px;\">\r\n\r\n <div class=\"col\" *ngIf=\"changePassword.userName!=''\">\r\n <spa-text id=\"txtuserName\" display=\"Username\" [(value)]=\"changePassword.userName\" width=\"300px\" [readonly]=\"true\"></spa-text>\r\n </div>\r\n\r\n <div class=\"col\" *ngIf=\"!myRole[dataService.capUsers.name]\">\r\n <spa-text id=\"txtPassword\" display=\"Current Password\" [(value)]=\"changePassword.currentPassword\" width=\"300px\" type=\"password\"></spa-text>\r\n </div>\r\n\r\n <div class=\"col\">\r\n <spa-text id=\"txtNewPassword\" display=\"New Password\" [(value)]=\"changePassword.newPassword\" width=\"300px\" type=\"password\"></spa-text>\r\n </div>\r\n\r\n <div class=\"col\">\r\n <spa-text id=\"txtConfirmPassword\" display=\"Confirm Password\" [(value)]=\"changePassword.confirmPassword\" width=\"300px\" type=\"password\"></spa-text>\r\n </div>\r\n\r\n <div class=\"col mt-3\">\r\n <button id=\"btnChange\" mat-raised-button color=\"primary\" (click)=\"change()\" cdkFocusInitial>Change</button>\r\n </div>\r\n\r\n </div>\r\n\r\n </div>\r\n\r\n <div class=\"col\">\r\n\r\n <div class=\"alert alert-info\" style=\"font-size: 14px;\" role=\"alert\">\r\n <b>*Please consider these requirements for your new password.</b> <br><br>\r\n\r\n At least 8 characters<br>\r\n At least 1 uppercase letter (A-Z)<br>\r\n At least 2 lowercase letters (a-z)<br>\r\n At least 1 digit (0-9)<br>\r\n At least 1 special character (~`! \u2026)<br>\r\n\r\n </div>\r\n\r\n </div>\r\n\r\n\r\n\r\n\r\n\r\n </div>\r\n</div> -->\r\n\r\n" }]
14326
+ args: [{ selector: 'spa-change-password', standalone: false, template: "<h4>Change Password</h4>\r\n<hr>\r\n\r\n\r\n<div class=\"container tin-grid\" style=\"font-size:14px;\">\r\n\r\n <div class=\"fill\">\r\n\r\n <spa-text id=\"txtuserName\" display=\"Username\" [(value)]=\"changePassword.userName\" [readonly]=\"true\"></spa-text>\r\n <spa-text-mask id=\"txtPassword\" display=\"Current Password\" [(value)]=\"changePassword.currentPassword\" type=\"password\"></spa-text-mask>\r\n <spa-text-mask id=\"txtNewPassword\" display=\"New Password\" [(value)]=\"changePassword.newPassword\" type=\"password\"></spa-text-mask>\r\n <spa-text-mask id=\"txtConfirmPassword\" display=\"Confirm Password\" [(value)]=\"changePassword.confirmPassword\" type=\"password\"></spa-text-mask>\r\n <button id=\"btnChange\" mat-raised-button color=\"primary\" (click)=\"change()\" cdkFocusInitial>Change</button>\r\n\r\n </div>\r\n\r\n <div class=\"alert alert-info\" style=\"font-size: 14px;\" role=\"alert\">\r\n <b>*Please consider these requirements for your new password.</b> <br><br>\r\n\r\n At least 8 characters<br>\r\n At least 1 uppercase letter (A-Z)<br>\r\n At least 2 lowercase letters (a-z)<br>\r\n At least 1 digit (0-9)<br>\r\n At least 1 special character (~`! \u2026)<br>\r\n\r\n </div>\r\n\r\n</div>\r\n\r\n\r\n<!-- <div class=\"container\">\r\n\r\n <div class=\"d-flex justify-content-center row align-items-center\" >\r\n\r\n <div class=\"col\">\r\n\r\n <div class=\"tin-input \" style=\"font-size:14px;\">\r\n\r\n <div class=\"col\" *ngIf=\"changePassword.userName!=''\">\r\n <spa-text id=\"txtuserName\" display=\"Username\" [(value)]=\"changePassword.userName\" width=\"300px\" [readonly]=\"true\"></spa-text>\r\n </div>\r\n\r\n <div class=\"col\" *ngIf=\"!myRole[dataService.capUsers.name]\">\r\n <spa-text id=\"txtPassword\" display=\"Current Password\" [(value)]=\"changePassword.currentPassword\" width=\"300px\" type=\"password\"></spa-text>\r\n </div>\r\n\r\n <div class=\"col\">\r\n <spa-text id=\"txtNewPassword\" display=\"New Password\" [(value)]=\"changePassword.newPassword\" width=\"300px\" type=\"password\"></spa-text>\r\n </div>\r\n\r\n <div class=\"col\">\r\n <spa-text id=\"txtConfirmPassword\" display=\"Confirm Password\" [(value)]=\"changePassword.confirmPassword\" width=\"300px\" type=\"password\"></spa-text>\r\n </div>\r\n\r\n <div class=\"col mt-3\">\r\n <button id=\"btnChange\" mat-raised-button color=\"primary\" (click)=\"change()\" cdkFocusInitial>Change</button>\r\n </div>\r\n\r\n </div>\r\n\r\n </div>\r\n\r\n <div class=\"col\">\r\n\r\n <div class=\"alert alert-info\" style=\"font-size: 14px;\" role=\"alert\">\r\n <b>*Please consider these requirements for your new password.</b> <br><br>\r\n\r\n At least 8 characters<br>\r\n At least 1 uppercase letter (A-Z)<br>\r\n At least 2 lowercase letters (a-z)<br>\r\n At least 1 digit (0-9)<br>\r\n At least 1 special character (~`! \u2026)<br>\r\n\r\n </div>\r\n\r\n </div>\r\n\r\n\r\n\r\n\r\n\r\n </div>\r\n</div> -->\r\n\r\n" }]
14063
14327
  }], ctorParameters: () => [{ type: i1$2.Router }, { type: i2.Location }, { type: HttpService }, { type: MessageService }, { type: DataServiceLib }, { type: AuthService }, { type: i1$2.ActivatedRoute }] });
14064
14328
 
14065
14329
  class ProfileComponent {
@@ -14132,11 +14396,11 @@ class ProfileComponent {
14132
14396
  });
14133
14397
  }
14134
14398
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: ProfileComponent, deps: [{ token: DataServiceLib }, { token: MessageService }, { token: HttpService }, { token: i1$2.Router }, { token: AuthService }], target: i0.ɵɵFactoryTarget.Component }); }
14135
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: ProfileComponent, isStandalone: false, selector: "spa-profile", inputs: { appConfig: "appConfig" }, ngImport: i0, template: "\r\n<h4>Profile</h4>\r\n<hr>\r\n\r\n<div class=\"container tin-grid-auto\" style=\"font-size:14px;\">\r\n\r\n <div class=\"tin-center centa\">\r\n <div class=\"profileImage\">{{initials}}</div>\r\n <mat-label id=\"lbluserName\" >{{profile?.userName}}</mat-label>\r\n </div>\r\n\r\n <div>\r\n\r\n <spa-text id=\"txtFirstName\" display=\"First Name\" [(value)]=\"profile.firstName\" [readonly]=\"selfProfile\"></spa-text>\r\n <spa-text id=\"txtLastName\" display=\"Last Name\" [(value)]=\"profile.lastName\" [readonly]=\"selfProfile\"></spa-text>\r\n <spa-text id=\"txtAuth\" display=\"Authentication\" [(value)]=\"profile.authType\" [readonly]=\"true\"></spa-text>\r\n <spa-text id=\"txtEmail\" display=\"Email\" [(value)]=\"profile.email\" [readonly]=\"selfProfile\"></spa-text>\r\n <spa-select id=\"cboRole\" display=\"Role\" [options]=\"roles\" optionDisplay=\"roleName\" optionValue=\"roleID\" [(value)]=\"profile.roleID\" [readonly]=\"selfProfile\"></spa-select>\r\n <spa-label *ngIf=\"dataService.appConfig.multitenant\" display=\"Code\" [value]=\"profile.code\" ></spa-label>\r\n\r\n <button id=\"btnUpdate\" class=\"w-100\" mat-raised-button color=\"primary\" *ngIf=\"!selfProfile\" [disabled]=\"isProcessing\" (click)=\"updateProfile()\">Update Profile</button>\r\n </div>\r\n\r\n\r\n <div class=\"tin-center centa\">\r\n <a mat-button id=\"lnkUserManager\" style=\"margin-left: 1em\" *ngIf=\"!selfProfile\" (click)=\"gotoUsers()\">User Manager</a>\r\n <a mat-button id=\"lnkChangePassword\" style=\"margin-left: 1em\" *ngIf=\"(selfProfile || myRole[dataService.capUsers.name]) && profile.authType=='local'\" (click)=\"changePassword()\">Change Password</a>\r\n </div>\r\n\r\n\r\n</div>\r\n\r\n\r\n\r\n\r\n\r\n", styles: [".centa{display:flex;flex-direction:column}.profileImage{width:150px;height:150px;border-radius:50%;background:#512da8;font-size:50px;color:#fff;text-align:center;line-height:150px;margin:20px 0 0}#lbluserName{font-size:20px;font-style:italic}\n"], dependencies: [{ kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i5$1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "component", type: TextComponent, selector: "spa-text", inputs: ["appearance", "readonly", "hint", "display", "placeholder", "value", "format", "type", "width", "copyContent", "clearContent", "required", "min", "max", "regex", "suffix", "infoMessage"], outputs: ["valueChange", "leave", "enterPress"] }, { kind: "component", type: SelectComponent, selector: "spa-select", inputs: ["detailsConfig"] }, { kind: "component", type: LabelComponent, selector: "spa-label", inputs: ["display", "value", "format", "suffix", "size"] }] }); }
14399
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: ProfileComponent, isStandalone: false, selector: "spa-profile", inputs: { appConfig: "appConfig" }, ngImport: i0, template: "\r\n<h4>Profile</h4>\r\n<hr>\r\n\r\n<div class=\"container tin-grid-auto\" style=\"font-size:14px;\">\r\n\r\n <div class=\"tin-center centa\">\r\n <div class=\"profileImage\">{{initials}}</div>\r\n <mat-label id=\"lbluserName\" >{{profile?.userName}}</mat-label>\r\n </div>\r\n\r\n <div>\r\n\r\n <spa-text id=\"txtFirstName\" display=\"First Name\" [(value)]=\"profile.firstName\" [readonly]=\"selfProfile\"></spa-text>\r\n <spa-text id=\"txtLastName\" display=\"Last Name\" [(value)]=\"profile.lastName\" [readonly]=\"selfProfile\"></spa-text>\r\n <spa-text id=\"txtAuth\" display=\"Authentication\" [(value)]=\"profile.authType\" [readonly]=\"true\"></spa-text>\r\n <spa-text id=\"txtEmail\" display=\"Email\" [(value)]=\"profile.email\" [readonly]=\"selfProfile\"></spa-text>\r\n <spa-text id=\"txtRole\" display=\"Role\" [(value)]=\"profile.role.roleName\" [readonly]=\"selfProfile\"></spa-text>\r\n <!-- <spa-select id=\"cboRole\" display=\"Role\" [options]=\"roles\" optionDisplay=\"roleName\" optionValue=\"roleID\" [(value)]=\"profile.roleID\" [readonly]=\"selfProfile\"></spa-select> -->\r\n <spa-label *ngIf=\"dataService.appConfig.multitenant\" display=\"Code\" [value]=\"profile.code\" ></spa-label>\r\n\r\n <button id=\"btnUpdate\" class=\"w-100\" mat-raised-button color=\"primary\" *ngIf=\"!selfProfile\" [disabled]=\"isProcessing\" (click)=\"updateProfile()\">Update Profile</button>\r\n </div>\r\n\r\n\r\n <div class=\"tin-center centa\">\r\n <a mat-button id=\"lnkUserManager\" style=\"margin-left: 1em\" *ngIf=\"!selfProfile\" (click)=\"gotoUsers()\">User Manager</a>\r\n <a mat-button id=\"lnkChangePassword\" style=\"margin-left: 1em\" *ngIf=\"(selfProfile || myRole[dataService.capUsers.name]) && profile.authType=='local'\" (click)=\"changePassword()\">Change Password</a>\r\n </div>\r\n\r\n\r\n</div>\r\n\r\n\r\n\r\n\r\n\r\n", styles: [".centa{display:flex;flex-direction:column}.profileImage{width:150px;height:150px;border-radius:50%;background:#512da8;font-size:50px;color:#fff;text-align:center;line-height:150px;margin:20px 0 0}#lbluserName{font-size:20px;font-style:italic}\n"], dependencies: [{ kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i5$1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "component", type: TextComponent, selector: "spa-text", inputs: ["appearance", "readonly", "hint", "display", "placeholder", "value", "format", "type", "width", "copyContent", "clearContent", "required", "min", "max", "regex", "suffix", "infoMessage"], outputs: ["valueChange", "leave", "enterPress"] }, { kind: "component", type: LabelComponent, selector: "spa-label", inputs: ["display", "value", "format", "suffix", "size"] }] }); }
14136
14400
  }
14137
14401
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: ProfileComponent, decorators: [{
14138
14402
  type: Component,
14139
- args: [{ selector: 'spa-profile', standalone: false, template: "\r\n<h4>Profile</h4>\r\n<hr>\r\n\r\n<div class=\"container tin-grid-auto\" style=\"font-size:14px;\">\r\n\r\n <div class=\"tin-center centa\">\r\n <div class=\"profileImage\">{{initials}}</div>\r\n <mat-label id=\"lbluserName\" >{{profile?.userName}}</mat-label>\r\n </div>\r\n\r\n <div>\r\n\r\n <spa-text id=\"txtFirstName\" display=\"First Name\" [(value)]=\"profile.firstName\" [readonly]=\"selfProfile\"></spa-text>\r\n <spa-text id=\"txtLastName\" display=\"Last Name\" [(value)]=\"profile.lastName\" [readonly]=\"selfProfile\"></spa-text>\r\n <spa-text id=\"txtAuth\" display=\"Authentication\" [(value)]=\"profile.authType\" [readonly]=\"true\"></spa-text>\r\n <spa-text id=\"txtEmail\" display=\"Email\" [(value)]=\"profile.email\" [readonly]=\"selfProfile\"></spa-text>\r\n <spa-select id=\"cboRole\" display=\"Role\" [options]=\"roles\" optionDisplay=\"roleName\" optionValue=\"roleID\" [(value)]=\"profile.roleID\" [readonly]=\"selfProfile\"></spa-select>\r\n <spa-label *ngIf=\"dataService.appConfig.multitenant\" display=\"Code\" [value]=\"profile.code\" ></spa-label>\r\n\r\n <button id=\"btnUpdate\" class=\"w-100\" mat-raised-button color=\"primary\" *ngIf=\"!selfProfile\" [disabled]=\"isProcessing\" (click)=\"updateProfile()\">Update Profile</button>\r\n </div>\r\n\r\n\r\n <div class=\"tin-center centa\">\r\n <a mat-button id=\"lnkUserManager\" style=\"margin-left: 1em\" *ngIf=\"!selfProfile\" (click)=\"gotoUsers()\">User Manager</a>\r\n <a mat-button id=\"lnkChangePassword\" style=\"margin-left: 1em\" *ngIf=\"(selfProfile || myRole[dataService.capUsers.name]) && profile.authType=='local'\" (click)=\"changePassword()\">Change Password</a>\r\n </div>\r\n\r\n\r\n</div>\r\n\r\n\r\n\r\n\r\n\r\n", styles: [".centa{display:flex;flex-direction:column}.profileImage{width:150px;height:150px;border-radius:50%;background:#512da8;font-size:50px;color:#fff;text-align:center;line-height:150px;margin:20px 0 0}#lbluserName{font-size:20px;font-style:italic}\n"] }]
14403
+ args: [{ selector: 'spa-profile', standalone: false, template: "\r\n<h4>Profile</h4>\r\n<hr>\r\n\r\n<div class=\"container tin-grid-auto\" style=\"font-size:14px;\">\r\n\r\n <div class=\"tin-center centa\">\r\n <div class=\"profileImage\">{{initials}}</div>\r\n <mat-label id=\"lbluserName\" >{{profile?.userName}}</mat-label>\r\n </div>\r\n\r\n <div>\r\n\r\n <spa-text id=\"txtFirstName\" display=\"First Name\" [(value)]=\"profile.firstName\" [readonly]=\"selfProfile\"></spa-text>\r\n <spa-text id=\"txtLastName\" display=\"Last Name\" [(value)]=\"profile.lastName\" [readonly]=\"selfProfile\"></spa-text>\r\n <spa-text id=\"txtAuth\" display=\"Authentication\" [(value)]=\"profile.authType\" [readonly]=\"true\"></spa-text>\r\n <spa-text id=\"txtEmail\" display=\"Email\" [(value)]=\"profile.email\" [readonly]=\"selfProfile\"></spa-text>\r\n <spa-text id=\"txtRole\" display=\"Role\" [(value)]=\"profile.role.roleName\" [readonly]=\"selfProfile\"></spa-text>\r\n <!-- <spa-select id=\"cboRole\" display=\"Role\" [options]=\"roles\" optionDisplay=\"roleName\" optionValue=\"roleID\" [(value)]=\"profile.roleID\" [readonly]=\"selfProfile\"></spa-select> -->\r\n <spa-label *ngIf=\"dataService.appConfig.multitenant\" display=\"Code\" [value]=\"profile.code\" ></spa-label>\r\n\r\n <button id=\"btnUpdate\" class=\"w-100\" mat-raised-button color=\"primary\" *ngIf=\"!selfProfile\" [disabled]=\"isProcessing\" (click)=\"updateProfile()\">Update Profile</button>\r\n </div>\r\n\r\n\r\n <div class=\"tin-center centa\">\r\n <a mat-button id=\"lnkUserManager\" style=\"margin-left: 1em\" *ngIf=\"!selfProfile\" (click)=\"gotoUsers()\">User Manager</a>\r\n <a mat-button id=\"lnkChangePassword\" style=\"margin-left: 1em\" *ngIf=\"(selfProfile || myRole[dataService.capUsers.name]) && profile.authType=='local'\" (click)=\"changePassword()\">Change Password</a>\r\n </div>\r\n\r\n\r\n</div>\r\n\r\n\r\n\r\n\r\n\r\n", styles: [".centa{display:flex;flex-direction:column}.profileImage{width:150px;height:150px;border-radius:50%;background:#512da8;font-size:50px;color:#fff;text-align:center;line-height:150px;margin:20px 0 0}#lbluserName{font-size:20px;font-style:italic}\n"] }]
14140
14404
  }], ctorParameters: () => [{ type: DataServiceLib }, { type: MessageService }, { type: HttpService }, { type: i1$2.Router }, { type: AuthService }], propDecorators: { appConfig: [{
14141
14405
  type: Input
14142
14406
  }] } });
@@ -14985,6 +15249,84 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImpo
14985
15249
  }]
14986
15250
  }], ctorParameters: () => [{ type: DataServiceLib }, { type: AccountingService }] });
14987
15251
 
15252
+ // Aging report component for accounts receivable tracking
15253
+ class AgingComponent {
15254
+ ngOnInit() {
15255
+ this.loadSummaryData();
15256
+ }
15257
+ loadSummaryData() {
15258
+ this.dataServiceLib.CallApi({ url: 'invoices/aging-summary/x' }, '').subscribe((apiResponse) => {
15259
+ if (apiResponse.success) {
15260
+ this.summaryData = apiResponse.data;
15261
+ }
15262
+ });
15263
+ }
15264
+ constructor(dataServiceLib) {
15265
+ this.dataServiceLib = dataServiceLib;
15266
+ this.accountingService = inject(AccountingService);
15267
+ this.tileConfig = this.accountingService.agingTileConfig;
15268
+ // this.tileConfig = this.accountingService.agingTileConfig;
15269
+ this.agingTableConfigs = [
15270
+ {
15271
+ ...this.accountingService.agingBaseTableConfig,
15272
+ tabTitle: 'Current',
15273
+ loadAction: { url: 'invoices/aging-current/x' },
15274
+ countAction: { url: 'invoices/count/aging-current' }
15275
+ },
15276
+ {
15277
+ ...this.accountingService.agingBaseTableConfig,
15278
+ tabTitle: '1-30 Days',
15279
+ loadAction: { url: 'invoices/aging-30/x' },
15280
+ countAction: { url: 'invoices/count/aging-30' }
15281
+ },
15282
+ {
15283
+ ...this.accountingService.agingBaseTableConfig,
15284
+ tabTitle: '31-60 Days',
15285
+ loadAction: { url: 'invoices/aging-60/x' },
15286
+ countAction: { url: 'invoices/count/aging-60' }
15287
+ },
15288
+ {
15289
+ ...this.accountingService.agingBaseTableConfig,
15290
+ tabTitle: '61-90 Days',
15291
+ loadAction: { url: 'invoices/aging-90/x' },
15292
+ countAction: { url: 'invoices/count/aging-90' }
15293
+ },
15294
+ {
15295
+ ...this.accountingService.agingBaseTableConfig,
15296
+ tabTitle: '90+ Days',
15297
+ loadAction: { url: 'invoices/aging-90plus/x' },
15298
+ countAction: { url: 'invoices/count/aging-90plus' }
15299
+ }
15300
+ ];
15301
+ }
15302
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: AgingComponent, deps: [{ token: DataServiceLib }], target: i0.ɵɵFactoryTarget.Component }); }
15303
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: AgingComponent, isStandalone: false, selector: "spa-aging", ngImport: i0, template: `
15304
+ <h4>Accounts Receivable Aging</h4>
15305
+ <hr>
15306
+ <div *ngIf="summaryData" class="mt-3 mb-3">
15307
+ <spa-tiles [config]="tileConfig" [data]="summaryData"></spa-tiles>
15308
+ </div>
15309
+
15310
+ <spa-tabs [tableConfigs]="agingTableConfigs"></spa-tabs>
15311
+ `, isInline: true, dependencies: [{ kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: TilesComponent, selector: "spa-tiles", inputs: ["config", "lastSearch", "data", "reload"], outputs: ["tileActionSelected", "tileClick", "tileUnClick"] }, { kind: "component", type: TabsComponent, selector: "spa-tabs", inputs: ["tableConfigs", "reload", "parentDetails"], outputs: ["formRefresh"] }] }); }
15312
+ }
15313
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: AgingComponent, decorators: [{
15314
+ type: Component,
15315
+ args: [{
15316
+ selector: 'spa-aging',
15317
+ template: `
15318
+ <h4>Accounts Receivable Aging</h4>
15319
+ <hr>
15320
+ <div *ngIf="summaryData" class="mt-3 mb-3">
15321
+ <spa-tiles [config]="tileConfig" [data]="summaryData"></spa-tiles>
15322
+ </div>
15323
+
15324
+ <spa-tabs [tableConfigs]="agingTableConfigs"></spa-tabs>
15325
+ `,
15326
+ standalone: false
15327
+ }]
15328
+ }], ctorParameters: () => [{ type: DataServiceLib }] });
15329
+
14988
15330
  class TransactionTypesComponent {
14989
15331
  constructor() {
14990
15332
  this.accountingService = inject(AccountingService);
@@ -15675,11 +16017,55 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImpo
15675
16017
  }]
15676
16018
  }] });
15677
16019
 
16020
+ // Component for managing bundle/combo products
16021
+ class BundleProductsComponent {
16022
+ constructor() {
16023
+ this.inventoryService = inject(InventoryService);
16024
+ this.pageConfig = {
16025
+ title: 'Bundle Products',
16026
+ tableConfig: this.inventoryService.bundleProductsTableConfig
16027
+ };
16028
+ }
16029
+ ngOnInit() { }
16030
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: BundleProductsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
16031
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: BundleProductsComponent, isStandalone: false, selector: "spa-bundle-products", ngImport: i0, template: '<spa-page [config]="pageConfig"></spa-page>', isInline: true, dependencies: [{ kind: "component", type: PageComponent, selector: "spa-page", inputs: ["config"], outputs: ["searchModeActivated", "searchModeDeactivated", "refreshClick", "actionClick", "actionResponse", "inputChange", "createClick", "searchClick", "dataLoad", "titleActionChange"] }] }); }
16032
+ }
16033
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: BundleProductsComponent, decorators: [{
16034
+ type: Component,
16035
+ args: [{
16036
+ selector: 'spa-bundle-products',
16037
+ standalone: false,
16038
+ template: '<spa-page [config]="pageConfig"></spa-page>'
16039
+ }]
16040
+ }] });
16041
+
16042
+ class GptCachesComponent {
16043
+ constructor() {
16044
+ this.dataService = inject(DataServiceLib);
16045
+ this.pageConfig = {
16046
+ title: 'GPT Cache',
16047
+ tableConfig: this.dataService.gptCachesTableConfig
16048
+ };
16049
+ }
16050
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: GptCachesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
16051
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: GptCachesComponent, isStandalone: true, selector: "spa-gpt-caches", ngImport: i0, template: '<spa-page [config]="pageConfig"></spa-page>', isInline: true, dependencies: [{ kind: "ngmodule", type: TinSpaModule }, { kind: "component", type: PageComponent, selector: "spa-page", inputs: ["config"], outputs: ["searchModeActivated", "searchModeDeactivated", "refreshClick", "actionClick", "actionResponse", "inputChange", "createClick", "searchClick", "dataLoad", "titleActionChange"] }] }); }
16052
+ }
16053
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: GptCachesComponent, decorators: [{
16054
+ type: Component,
16055
+ args: [{
16056
+ selector: 'spa-gpt-caches',
16057
+ standalone: true,
16058
+ imports: [TinSpaModule],
16059
+ template: '<spa-page [config]="pageConfig"></spa-page>'
16060
+ }]
16061
+ }] });
16062
+
15678
16063
  const routes$1 = [
15679
16064
  { path: "users", component: UsersComponent },
15680
16065
  { path: "roles", component: RolesComponent },
15681
16066
  { path: "create-account", component: CreateAccountComponent },
15682
16067
  { path: "logs", component: LogsComponent },
16068
+ { path: "gpt-caches", component: GptCachesComponent }, // Added: GPT Cache route
15683
16069
  { path: "settings", component: SettingsComponent },
15684
16070
  { path: "tenant-settings", component: TenantSettingsComponent },
15685
16071
  { path: "tenants", component: TenantsComponent },
@@ -15707,6 +16093,7 @@ const routes$1 = [
15707
16093
  { path: "accounting-transaction-types", component: TransactionTypesComponent },
15708
16094
  { path: "accounting-transactions", component: TransactionsComponent },
15709
16095
  { path: "accounting-invoices", component: InvoicesComponent },
16096
+ { path: "accounting-aging", component: AgingComponent }, // Added: Aging route
15710
16097
  { path: "accounting-outstanding-invoices", component: OutstandingInvoicesComponent },
15711
16098
  { path: "loans-products", component: LoanProductsComponent },
15712
16099
  { path: "loans", component: LoansComponent },
@@ -15724,6 +16111,7 @@ const routes$1 = [
15724
16111
  { path: "inventory-transactions", component: InventoryTransactionsComponent },
15725
16112
  { path: "inventory-dashboard", component: InventoryDashboardComponent },
15726
16113
  { path: "inventory-stock", component: InventoryStockComponent }, // Changed: Added inventory stock route
16114
+ { path: "bundle-products", component: BundleProductsComponent }, // Added: Bundle products route
15727
16115
  { path: "production-recipes", component: ProductionRecipesComponent },
15728
16116
  { path: "production-orders", component: ProductionOrdersComponent }
15729
16117
  ];
@@ -15746,6 +16134,7 @@ class AdminModule {
15746
16134
  // Accounting components
15747
16135
  AccountsComponent,
15748
16136
  AggregatesComponent, // Added: Aggregates component declaration
16137
+ AgingComponent, // Added: Aging component declaration
15749
16138
  TransactionTypesComponent,
15750
16139
  TransactionsComponent,
15751
16140
  InvoicesComponent,
@@ -15765,7 +16154,9 @@ class AdminModule {
15765
16154
  InventoryDashboardComponent,
15766
16155
  InventoryStockComponent,
15767
16156
  ProductionRecipesComponent,
15768
- ProductionOrdersComponent], imports: [CommonModule,
16157
+ ProductionOrdersComponent,
16158
+ BundleProductsComponent // Added: Bundle products component
16159
+ ], imports: [CommonModule,
15769
16160
  AdminRoutingModule,
15770
16161
  SpaAdminModule] }); }
15771
16162
  static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: AdminModule, imports: [CommonModule,
@@ -15779,6 +16170,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImpo
15779
16170
  // Accounting components
15780
16171
  AccountsComponent,
15781
16172
  AggregatesComponent, // Added: Aggregates component declaration
16173
+ AgingComponent, // Added: Aging component declaration
15782
16174
  TransactionTypesComponent,
15783
16175
  TransactionsComponent,
15784
16176
  InvoicesComponent,
@@ -15798,7 +16190,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImpo
15798
16190
  InventoryDashboardComponent,
15799
16191
  InventoryStockComponent,
15800
16192
  ProductionRecipesComponent,
15801
- ProductionOrdersComponent
16193
+ ProductionOrdersComponent,
16194
+ BundleProductsComponent // Added: Bundle products component
15802
16195
  ],
15803
16196
  imports: [
15804
16197
  CommonModule,
@@ -15855,5 +16248,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImpo
15855
16248
  * Generated bundle index. Do not edit.
15856
16249
  */
15857
16250
 
15858
- export { Account, AccountsComponent as AccountingAccountsComponent, AggregatesComponent as AccountingAggregatesComponent, InvoicesComponent as AccountingInvoicesComponent, OutstandingInvoicesComponent as AccountingOutstandingInvoicesComponent, AccountingService, TransactionTypesComponent as AccountingTransactionTypesComponent, TransactionsComponent as AccountingTransactionsComponent, Action, ActivityComponent, AdminModule, AlertComponent, AlertConfig, AlertMessage, ApiResponse, AppConfig, AppModelsComponent, AttachComponent, AuthService, BrandsComponent, CacheConfig, CapItem, CapsulesComponent, CategoriesComponent, ChangePasswordComponent, ChangeUserPassword, CheckComponent, ChipsComponent, Constants, Core, CreateAccountComponent, CustomersComponent, DataServiceLib, DateComponent, DatetimeComponent, DepartmentsComponent, DetailsDialog, DetailsDialogConfig, DetailsDialogProcessor, DetailsSource, DialogService, EmailComponent, EmployeesComponent, ExportService, FilterComponent, FormComponent, FormConfig, GeneralService, GradesComponent, GroupsComponent, HtmlComponent, HttpService, IndexModule, InventoryReceiptStatus, InventoryService, InvoiceStatus, LabelComponent, ListDialogComponent, ListDialogConfig, LoaderComponent, LoaderService, LoanPaymentsComponent, LoanProductsComponent, LoansComponent, LoansService, LogLevel, LogService, LoginComponent, LogsComponent, MembershipComponent, MessageService, MoneyComponent, MovementType, NavMenuComponent, NotesComponent, NotesConfig, NumberComponent, OnboardingComponent, OptionComponent, PageComponent, PageConfig, PlansComponent, PositionsComponent, Profile, ProfileComponent, RecoverAccountComponent, Register, Role, RoleAccess, RolesComponent, SearchComponent, SearchConfig, SecurityConfig, SelectBitwiseComponent, SelectComponent, SelectMultiComponent, SettingsComponent, SignupComponent, SpaAdminModule, SpaIndexModule, SpaMatModule, SpaUserModule, StatusesComponent, Step, StepConfig, StepsComponent, StorageService, SubCategoriesComponent, SuppliersComponent, TabService, TableComponent, TableConfig, TabsComponent, TabsInternalComponent, TabsLiteComponent, TasksComponent, TenantsComponent, TextAreaComponent, TextComponent, TextMaskComponent, TextMultiComponent, TextSingleComponent, TileConfig, TilesComponent, TinSpaComponent, TinSpaModule, TinSpaService, TitleActionsComponent, UnitOfMeasure, User, UserModule, UsersComponent, ViewerComponent, WelcomeComponent, authGuard, dialogOptions, loginConfig, messageDialog, viewerDialog };
16251
+ export { Account, AccountsComponent as AccountingAccountsComponent, AggregatesComponent as AccountingAggregatesComponent, AgingComponent as AccountingAgingComponent, InvoicesComponent as AccountingInvoicesComponent, OutstandingInvoicesComponent as AccountingOutstandingInvoicesComponent, AccountingService, TransactionTypesComponent as AccountingTransactionTypesComponent, TransactionsComponent as AccountingTransactionsComponent, Action, ActivityComponent, AdminModule, AlertComponent, AlertConfig, AlertMessage, ApiResponse, AppConfig, AppModelsComponent, AttachComponent, AuthService, BrandsComponent, CacheConfig, CapItem, CapsulesComponent, CategoriesComponent, ChangePasswordComponent, ChangeUserPassword, CheckComponent, ChipsComponent, Constants, Core, CreateAccountComponent, CustomersComponent, DataServiceLib, DateComponent, DatetimeComponent, DepartmentsComponent, DetailsDialog, DetailsDialogConfig, DetailsDialogProcessor, DetailsSource, DialogService, EmailComponent, EmployeesComponent, ExportService, FilterComponent, FormComponent, FormConfig, GeneralService, GradesComponent, GroupsComponent, HtmlComponent, HttpService, IndexModule, InventoryReceiptStatus, InventoryService, InvoiceStatus, LabelComponent, ListDialogComponent, ListDialogConfig, LoaderComponent, LoaderService, LoanPaymentsComponent, LoanProductsComponent, LoansComponent, LoansService, LogLevel, LogService, LoginComponent, LogsComponent, MembershipComponent, MessageService, MoneyComponent, MovementType, NavMenuComponent, NotesComponent, NotesConfig, NumberComponent, OnboardingComponent, OptionComponent, PageComponent, PageConfig, PlansComponent, PositionsComponent, Profile, ProfileComponent, RecoverAccountComponent, Register, Role, RoleAccess, RolesComponent, SearchComponent, SearchConfig, SecurityConfig, SelectBitwiseComponent, SelectComponent, SelectMultiComponent, SettingsComponent, SignupComponent, SpaAdminModule, SpaIndexModule, SpaMatModule, SpaUserModule, StatusesComponent, Step, StepConfig, StepsComponent, StorageService, SubCategoriesComponent, SuppliersComponent, TabService, TableComponent, TableConfig, TabsComponent, TabsInternalComponent, TabsLiteComponent, TasksComponent, TenantsComponent, TextAreaComponent, TextComponent, TextMaskComponent, TextMultiComponent, TextSingleComponent, TileConfig, TilesComponent, TinSpaComponent, TinSpaModule, TinSpaService, TitleActionsComponent, UnitOfMeasure, User, UserModule, UsersComponent, ViewerComponent, WelcomeComponent, authGuard, dialogOptions, loginConfig, messageDialog, viewerDialog };
15859
16252
  //# sourceMappingURL=tin-spa.mjs.map