tin-spa 20.3.0 → 20.3.2
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.
- package/fesm2022/tin-spa.mjs +486 -88
- package/fesm2022/tin-spa.mjs.map +1 -1
- package/index.d.ts +43 -5
- package/package.json +1 -1
package/fesm2022/tin-spa.mjs
CHANGED
|
@@ -662,9 +662,20 @@ class DetailsDialogProcessor {
|
|
|
662
662
|
transformStepUrl(updatedDetailsConfig.stepConfig);
|
|
663
663
|
}
|
|
664
664
|
function transformLoadUrl(tableConfig) {
|
|
665
|
-
if (tableConfig.loadAction
|
|
665
|
+
if (!tableConfig.loadAction)
|
|
666
|
+
return;
|
|
667
|
+
// Changed: Check if URL contains {propertyName} placeholders
|
|
668
|
+
if (tableConfig.loadAction.url.includes('{')) {
|
|
669
|
+
tableConfig.loadAction.url = tableConfig.loadAction.url.replace(/\{(\w+)\}/g, (match, propName) => {
|
|
670
|
+
const value = updatedDetailsConfig.details?.[propName];
|
|
671
|
+
return value !== undefined && value !== null ? String(value) : match;
|
|
672
|
+
});
|
|
673
|
+
console.log("Transformed (placeholder)", tableConfig.loadAction.url);
|
|
674
|
+
}
|
|
675
|
+
else if (tableConfig.loadCriteria && tableConfig.loadIDField) {
|
|
676
|
+
// Changed: Fallback to existing loadCriteria/loadIDField approach
|
|
666
677
|
tableConfig.loadAction.url = `${tableConfig.loadAction.url.split('/')[0]}/${tableConfig.loadCriteria}/${updatedDetailsConfig.details[tableConfig.loadIDField]}`;
|
|
667
|
-
console.log("Transformed", tableConfig.loadAction.url);
|
|
678
|
+
console.log("Transformed (criteria)", tableConfig.loadAction.url);
|
|
668
679
|
}
|
|
669
680
|
else {
|
|
670
681
|
console.log("NOT Transformed");
|
|
@@ -672,8 +683,17 @@ class DetailsDialogProcessor {
|
|
|
672
683
|
}
|
|
673
684
|
function transformStepUrl(stepConfig) {
|
|
674
685
|
if (stepConfig.loadAction && stepConfig.loadIDField) {
|
|
675
|
-
//
|
|
676
|
-
|
|
686
|
+
// Changed: Support generic {propertyName} placeholders
|
|
687
|
+
if (stepConfig.loadAction.url.includes('{')) {
|
|
688
|
+
stepConfig.loadAction.url = stepConfig.loadAction.url.replace(/\{(\w+)\}/g, (match, propName) => {
|
|
689
|
+
const value = updatedDetailsConfig.details?.[propName];
|
|
690
|
+
return value !== undefined && value !== null ? String(value) : match;
|
|
691
|
+
});
|
|
692
|
+
}
|
|
693
|
+
else {
|
|
694
|
+
// Fallback to legacy {id} replacement for backward compatibility
|
|
695
|
+
stepConfig.loadAction.url = `${stepConfig.loadAction.url.replace('{id}', updatedDetailsConfig.details[stepConfig.loadIDField])}`;
|
|
696
|
+
}
|
|
677
697
|
// console.log("Transformed step URL", stepConfig.loadAction.url);
|
|
678
698
|
}
|
|
679
699
|
}
|
|
@@ -744,7 +764,16 @@ class DetailsDialogProcessor {
|
|
|
744
764
|
static loadDetailsFromUrl(detailsConfig, formConfig) {
|
|
745
765
|
let action;
|
|
746
766
|
if (formConfig.loadAction) {
|
|
747
|
-
if
|
|
767
|
+
// Changed: Check if URL contains {propertyName} placeholders first - takes priority over hero field
|
|
768
|
+
if (formConfig.loadAction.url.includes('{') && detailsConfig.details) {
|
|
769
|
+
const transformedUrl = formConfig.loadAction.url.replace(/\{(\w+)\}/g, (match, propName) => {
|
|
770
|
+
const value = detailsConfig.details?.[propName];
|
|
771
|
+
return value !== undefined && value !== null ? String(value) : match;
|
|
772
|
+
});
|
|
773
|
+
action = { url: transformedUrl };
|
|
774
|
+
console.log("Transformed form URL (placeholder)", transformedUrl);
|
|
775
|
+
}
|
|
776
|
+
else if (detailsConfig.heroField && detailsConfig.details) {
|
|
748
777
|
// console.log("DetailsConfig Hero ID Field " + detailsConfig.heroField);
|
|
749
778
|
action = { url: `${formConfig.loadAction.url}/${detailsConfig.details[detailsConfig.heroField]}` };
|
|
750
779
|
}
|
|
@@ -791,6 +820,19 @@ var InvoiceStatus;
|
|
|
791
820
|
InvoiceStatus[InvoiceStatus["Discarded"] = 3] = "Discarded";
|
|
792
821
|
InvoiceStatus[InvoiceStatus["Paying"] = 4] = "Paying"; // Changed: Added Paying status for partially paid invoices
|
|
793
822
|
})(InvoiceStatus || (InvoiceStatus = {}));
|
|
823
|
+
// Added: Invoice item type enum - mirrors backend InvoiceItemType
|
|
824
|
+
var InvoiceItemType;
|
|
825
|
+
(function (InvoiceItemType) {
|
|
826
|
+
InvoiceItemType[InvoiceItemType["General"] = 0] = "General";
|
|
827
|
+
InvoiceItemType[InvoiceItemType["Product"] = 1] = "Product";
|
|
828
|
+
InvoiceItemType[InvoiceItemType["Service"] = 2] = "Service";
|
|
829
|
+
})(InvoiceItemType || (InvoiceItemType = {}));
|
|
830
|
+
// Transaction timing enum - separates WHEN payment occurs from HOW
|
|
831
|
+
var TransactionTiming;
|
|
832
|
+
(function (TransactionTiming) {
|
|
833
|
+
TransactionTiming[TransactionTiming["Immediate"] = 0] = "Immediate";
|
|
834
|
+
TransactionTiming[TransactionTiming["Deferred"] = 1] = "Deferred"; // Payment occurs later - creates AR/AP
|
|
835
|
+
})(TransactionTiming || (TransactionTiming = {}));
|
|
794
836
|
// Inventory receipt status tracking enum - mirrors backend
|
|
795
837
|
var InventoryReceiptStatus;
|
|
796
838
|
(function (InventoryReceiptStatus) {
|
|
@@ -1498,6 +1540,7 @@ class DataServiceLib {
|
|
|
1498
1540
|
this.capInventoryDashboard = new CapItem;
|
|
1499
1541
|
this.capInventoryStock = new CapItem; // Changed: Added new cap item for inventory stock view
|
|
1500
1542
|
this.capProducts = new CapItem;
|
|
1543
|
+
this.capServiceItems = new CapItem; // Added: Capability for service items
|
|
1501
1544
|
this.capBundleProducts = new CapItem; // Added: Capability for bundle products
|
|
1502
1545
|
this.capInventoryItems = new CapItem;
|
|
1503
1546
|
this.capPurchaseOrders = new CapItem;
|
|
@@ -2228,9 +2271,9 @@ class DataServiceLib {
|
|
|
2228
2271
|
this.capInventory.name = "cap34";
|
|
2229
2272
|
this.capInventory.display = "Inventory";
|
|
2230
2273
|
this.capInventory.icon = "inventory";
|
|
2231
|
-
this.capInventory.capSubItems = [this.capSales, this.capInventoryReceipts, this.capProducts, this.capBundleProducts, this.capInventoryStock, this.capInventoryTransactions, this.capInventoryItems,
|
|
2274
|
+
this.capInventory.capSubItems = [this.capSales, this.capInventoryReceipts, this.capProducts, this.capServiceItems, this.capBundleProducts, this.capInventoryStock, this.capInventoryTransactions, this.capInventoryItems,
|
|
2232
2275
|
this.capPurchaseOrders, this.capSalesOrders, this.capRequisitions, this.capInventoryAdjustments,
|
|
2233
|
-
this.capInventoryReturns, this.capRequisitionReturns, this.capProductionRecipes, this.capProductionOrders, this.capInventoryDashboard];
|
|
2276
|
+
this.capInventoryReturns, this.capRequisitionReturns, this.capProductionRecipes, this.capProductionOrders, this.capInventoryDashboard]; // Changed: Added capServiceItems to inventory menu
|
|
2234
2277
|
this.capInventoryDashboard.name = "cap35";
|
|
2235
2278
|
this.capInventoryDashboard.display = "Dashboard";
|
|
2236
2279
|
this.capInventoryDashboard.link = "home/admin/inventory-dashboard";
|
|
@@ -2243,6 +2286,10 @@ class DataServiceLib {
|
|
|
2243
2286
|
this.capProducts.display = "Products";
|
|
2244
2287
|
this.capProducts.link = "home/admin/inventory-products";
|
|
2245
2288
|
this.capProducts.icon = "category";
|
|
2289
|
+
this.capServiceItems.name = "cap59"; // Added: Service items capability
|
|
2290
|
+
this.capServiceItems.display = "Services";
|
|
2291
|
+
this.capServiceItems.link = "home/admin/inventory-service-items";
|
|
2292
|
+
this.capServiceItems.icon = "build";
|
|
2246
2293
|
this.capBundleProducts.name = "cap57"; // Added: Bundle products capability
|
|
2247
2294
|
this.capBundleProducts.display = "Bundle Products";
|
|
2248
2295
|
this.capBundleProducts.link = "home/admin/bundle-products";
|
|
@@ -2670,7 +2717,7 @@ class AccountingService {
|
|
|
2670
2717
|
loadAction: { url: 'accounts/id' },
|
|
2671
2718
|
heroField: 'accountID',
|
|
2672
2719
|
};
|
|
2673
|
-
this.accountCreateButton = { name: 'create', display: 'Create', dialog: true, action: { url: 'accounts?action=create', method: 'post' } };
|
|
2720
|
+
this.accountCreateButton = { name: 'create', display: 'Create Account', dialog: true, action: { url: 'accounts?action=create', method: 'post' } };
|
|
2674
2721
|
this.finAccounEditButton = { name: 'edit', dialog: true, action: { url: 'accounts?action=edit', method: 'post' } };
|
|
2675
2722
|
this.accountBaseDetailsConfig = {
|
|
2676
2723
|
formConfig: this.accountFormConfig,
|
|
@@ -2708,7 +2755,6 @@ class AccountingService {
|
|
|
2708
2755
|
showFilter: false,
|
|
2709
2756
|
minColumns: ['paymentDate', 'amount', 'methodName'],
|
|
2710
2757
|
columns: [
|
|
2711
|
-
{ name: 'invoicePaymentID', type: 'number', alias: 'ID', hiddenCondition: () => true },
|
|
2712
2758
|
{ name: 'paymentDate', type: 'date', alias: 'Date' },
|
|
2713
2759
|
{ name: 'amount', type: 'money', alias: 'Amount' },
|
|
2714
2760
|
{ name: 'methodName', type: 'text', alias: 'Method' },
|
|
@@ -2719,9 +2765,21 @@ class AccountingService {
|
|
|
2719
2765
|
action: { url: 'invoicepayments/migrate', method: 'post',
|
|
2720
2766
|
successMessage: 'Migration completed successfully' },
|
|
2721
2767
|
confirm: { message: 'This will migrate existing invoice payments from the legacy PaidAmount field to the new InvoicePayments table. Continue?' }
|
|
2722
|
-
}
|
|
2768
|
+
},
|
|
2769
|
+
{ name: 'edit', dialog: true, action: { url: 'invoicepayments?action=edit', method: 'post' }, visible: x => x.status != InvoiceStatus.Paid }, // Changed: Added edit button, hide on Paid invoices
|
|
2770
|
+
{ name: 'delete', inDialog: true, icon: { name: 'delete', color: 'red' }, action: { url: 'invoicepayments?action=delete', method: 'post', successMessage: 'Payment deleted' }, confirm: { message: 'Delete this payment? The linked financial transaction will be reversed.' }, visible: x => x.status != InvoiceStatus.Paid } // Changed: Added delete button with reversal warning, hide on Paid invoices
|
|
2723
2771
|
],
|
|
2724
|
-
loadAction: { url: 'invoicepayments/x/x' }, loadCriteria: 'invoice', loadIDField: 'invoiceID'
|
|
2772
|
+
loadAction: { url: 'invoicepayments/x/x' }, loadCriteria: 'invoice', loadIDField: 'invoiceID',
|
|
2773
|
+
formConfig: {
|
|
2774
|
+
title: 'Edit Payment',
|
|
2775
|
+
fields: [
|
|
2776
|
+
{ name: 'paymentDate', type: 'date', alias: 'Payment Date', required: true },
|
|
2777
|
+
{ name: 'method', type: 'select', alias: 'Payment Method', required: true, loadAction: { url: 'invoicepayments/list/methods' } },
|
|
2778
|
+
{ name: 'amount', type: 'money', alias: 'Amount', required: true },
|
|
2779
|
+
{ name: 'reference', type: 'text', alias: 'Reference' }
|
|
2780
|
+
],
|
|
2781
|
+
loadAction: { url: 'invoicepayments/id' }
|
|
2782
|
+
}
|
|
2725
2783
|
};
|
|
2726
2784
|
// Invoice form configuration with customer and status fields
|
|
2727
2785
|
this.invoiceFormConfig = {
|
|
@@ -2742,6 +2800,36 @@ class AccountingService {
|
|
|
2742
2800
|
loadAction: { url: 'invoices/id' },
|
|
2743
2801
|
heroField: 'invoiceID',
|
|
2744
2802
|
};
|
|
2803
|
+
// Invoice items form configuration
|
|
2804
|
+
this.invoiceItemsFormConfig = {
|
|
2805
|
+
title: 'Invoice Item',
|
|
2806
|
+
fields: [
|
|
2807
|
+
{ name: 'itemType', type: 'select', required: true, alias: 'Item Type', loadAction: { url: 'invoiceitems/list/item-types' }, defaultValue: 0 }, // Added: Item type selector
|
|
2808
|
+
{ name: 'productID', type: 'select', alias: 'Product', loadAction: { url: 'products/list/invoice-items' },
|
|
2809
|
+
hiddenCondition: x => x.itemType !== 1, // Added: Product selector - visible only when itemType = Product (1)
|
|
2810
|
+
onSelectChange: (selectedId, formData, option) => {
|
|
2811
|
+
if (option) {
|
|
2812
|
+
formData.description = option.description || option.name;
|
|
2813
|
+
formData.unitPrice = option.unitPrice;
|
|
2814
|
+
}
|
|
2815
|
+
}
|
|
2816
|
+
},
|
|
2817
|
+
{ name: 'serviceItemID', type: 'select', alias: 'Service', loadAction: { url: 'serviceitems/list/active' },
|
|
2818
|
+
hiddenCondition: x => x.itemType !== 2, // Added: Service selector - visible only when itemType = Service (2)
|
|
2819
|
+
onSelectChange: (selectedId, formData, option) => {
|
|
2820
|
+
if (option) {
|
|
2821
|
+
formData.description = option.description || option.name;
|
|
2822
|
+
formData.unitPrice = option.unitPrice;
|
|
2823
|
+
}
|
|
2824
|
+
}
|
|
2825
|
+
},
|
|
2826
|
+
{ name: 'description', type: 'text', required: true, alias: 'Description', span: true }, // Changed: Description field (backend will populate for Product/Service)
|
|
2827
|
+
{ name: 'quantity', type: 'number', required: true, defaultValue: 1 },
|
|
2828
|
+
{ name: 'unitPrice', type: 'money', alias: 'Unit Price', required: true }, // Changed: Unit price field (backend will populate for Product/Service)
|
|
2829
|
+
],
|
|
2830
|
+
loadAction: { url: 'invoiceitems/id/{invoiceItemID}' },
|
|
2831
|
+
heroField: 'invoiceItemID',
|
|
2832
|
+
};
|
|
2745
2833
|
// Invoice items table for manual line item entry
|
|
2746
2834
|
this.invoiceItemsTableConfig = {
|
|
2747
2835
|
tabTitle: 'Invoice Items',
|
|
@@ -2759,20 +2847,17 @@ class AccountingService {
|
|
|
2759
2847
|
{ name: 'create', display: 'Add Item', dialog: true, action: { url: 'invoiceitems?action=create', method: 'post' },
|
|
2760
2848
|
disabled: x => x.status != InvoiceStatus.Draft
|
|
2761
2849
|
},
|
|
2762
|
-
{ name: 'edit', dialog: true, action: { url: 'invoiceitems?action=edit', method: 'post' },
|
|
2763
|
-
|
|
2850
|
+
{ name: 'edit', dialog: true, action: { url: 'invoiceitems?action=edit', method: 'post' },
|
|
2851
|
+
disabled: x => x.status != InvoiceStatus.Draft
|
|
2852
|
+
},
|
|
2853
|
+
{ name: 'delete', inDialog: true, icon: { name: 'delete', color: 'red' }, action: { url: 'invoiceitems?action=delete', method: 'post',
|
|
2854
|
+
successMessage: 'Deleted' },
|
|
2855
|
+
confirm: { message: 'Delete this item?' },
|
|
2856
|
+
disabled: x => x.status != InvoiceStatus.Draft
|
|
2857
|
+
},
|
|
2764
2858
|
],
|
|
2765
2859
|
loadAction: { url: 'invoiceitems/x/x' }, loadCriteria: 'invoice', loadIDField: 'invoiceID',
|
|
2766
|
-
formConfig:
|
|
2767
|
-
title: 'Invoice Item',
|
|
2768
|
-
fields: [
|
|
2769
|
-
{ name: 'description', type: 'text', required: true, span: true },
|
|
2770
|
-
{ name: 'quantity', type: 'number', required: true, defaultValue: 1 },
|
|
2771
|
-
{ name: 'unitPrice', type: 'number', alias: 'Unit Price', required: true },
|
|
2772
|
-
{ name: 'amount', type: 'number', readonly: true, hideOnCreate: true }
|
|
2773
|
-
],
|
|
2774
|
-
loadAction: { url: 'invoiceitems/id' }
|
|
2775
|
-
}
|
|
2860
|
+
formConfig: this.invoiceItemsFormConfig // Changed: Reference extracted form config
|
|
2776
2861
|
};
|
|
2777
2862
|
// Invoice action buttons
|
|
2778
2863
|
this.invoiceRecordPaymentButton = { name: 'record-payment', display: 'Record Payment', dialog: true, icon: { name: 'payment', color: 'blue' },
|
|
@@ -2780,7 +2865,8 @@ class AccountingService {
|
|
|
2780
2865
|
visible: x => (x.status == InvoiceStatus.Submitted || x.status == InvoiceStatus.Paying) && x.outstandingAmount > 0 // Changed: Allow on both Submitted and Paying
|
|
2781
2866
|
}; // Changed: Use detailsConfig to open payment form dialog
|
|
2782
2867
|
this.invoiceDiscardButton = { name: 'discard', inDialog: true, display: 'Discard', icon: { name: 'close', color: 'red' },
|
|
2783
|
-
action: { url: 'invoices?action=discard', method: 'post', successMessage: 'Discarded' },
|
|
2868
|
+
action: { url: 'invoices?action=discard', method: 'post', successMessage: 'Discarded' },
|
|
2869
|
+
confirm: { message: 'Invoice will be marked as cancelled?' },
|
|
2784
2870
|
visible: x => x.status == InvoiceStatus.Draft || x.status == InvoiceStatus.Submitted // Changed: Only allow on Draft and Submitted
|
|
2785
2871
|
};
|
|
2786
2872
|
this.invoiceSubmitButton = { name: 'submit', inDialog: true, display: 'Submit', icon: { name: 'send', },
|
|
@@ -2789,8 +2875,9 @@ class AccountingService {
|
|
|
2789
2875
|
visible: x => x.status == InvoiceStatus.Draft,
|
|
2790
2876
|
disabled: x => x.totalAmount == 0
|
|
2791
2877
|
};
|
|
2792
|
-
this.invoiceEditButton = { name: 'edit', dialog: true, action: { url: 'invoices?action=edit', method: 'post', },
|
|
2793
|
-
|
|
2878
|
+
this.invoiceEditButton = { name: 'edit', dialog: true, action: { url: 'invoices?action=edit', method: 'post', },
|
|
2879
|
+
confirm: { message: 'Proceed ?' },
|
|
2880
|
+
visible: x => x.status == InvoiceStatus.Draft // Changed: Draft only
|
|
2794
2881
|
};
|
|
2795
2882
|
this.invoiceDownloadButton = { name: 'pdf', display: 'Download PDF', inDialog: true, icon: { name: 'picture_as_pdf', color: 'red' } };
|
|
2796
2883
|
// Invoice details dialog with items, payments history, and action buttons
|
|
@@ -2820,7 +2907,7 @@ class AccountingService {
|
|
|
2820
2907
|
collapseButtons: true,
|
|
2821
2908
|
greyOut: x => x.status == InvoiceStatus.Paid,
|
|
2822
2909
|
columns: [
|
|
2823
|
-
{ name: 'invoiceNumber', type: '
|
|
2910
|
+
{ name: 'invoiceNumber', type: 'button', alias: 'Invoice #', detailsConfig: this.invoiceDetailsDialogConfig },
|
|
2824
2911
|
{ name: 'customerName', type: 'text', alias: 'Customer' },
|
|
2825
2912
|
{ name: 'invoiceDate', type: 'date' },
|
|
2826
2913
|
{ name: 'dueDate', type: 'date', alias: 'Due Date' },
|
|
@@ -2840,14 +2927,16 @@ class AccountingService {
|
|
|
2840
2927
|
{ name: 'outstandingAmount', type: 'money', alias: 'Outstanding' }
|
|
2841
2928
|
],
|
|
2842
2929
|
buttons: [
|
|
2843
|
-
{ name: 'create', display: 'Create', dialog: true,
|
|
2844
|
-
|
|
2845
|
-
|
|
2930
|
+
{ name: 'create', display: 'Create', dialog: true, onSuccessButton: this.invoiceViewButton,
|
|
2931
|
+
action: { url: 'invoices?action=create', method: 'post' }
|
|
2932
|
+
},
|
|
2933
|
+
// { name: 'view', dialog: true, detailsConfig: this.invoiceDetailsDialogConfig },
|
|
2846
2934
|
this.invoiceSubmitButton,
|
|
2935
|
+
this.invoiceRecordPaymentButton,
|
|
2847
2936
|
// this.invoicePayButton,
|
|
2848
2937
|
this.invoiceDiscardButton,
|
|
2849
|
-
this.invoiceEditButton,
|
|
2850
|
-
this.invoiceDownloadButton,
|
|
2938
|
+
// this.invoiceEditButton,
|
|
2939
|
+
// this.invoiceDownloadButton,
|
|
2851
2940
|
],
|
|
2852
2941
|
loadAction: { url: 'invoices/all/x' },
|
|
2853
2942
|
formConfig: this.invoiceFormConfig,
|
|
@@ -2870,9 +2959,10 @@ class AccountingService {
|
|
|
2870
2959
|
// Added: Base aging table configuration with common properties
|
|
2871
2960
|
this.agingBaseTableConfig = {
|
|
2872
2961
|
showFilter: true,
|
|
2962
|
+
flatButtons: true,
|
|
2873
2963
|
minColumns: ['invoiceNumber', 'customerName', 'outstandingAmount'],
|
|
2874
2964
|
columns: [
|
|
2875
|
-
{ name: 'invoiceNumber', type: 'text', alias: 'Invoice #' },
|
|
2965
|
+
{ name: 'invoiceNumber', type: 'text', alias: 'Invoice #', detailsConfig: this.invoiceDetailsDialogConfig },
|
|
2876
2966
|
{ name: 'customerName', type: 'text', alias: 'Customer' },
|
|
2877
2967
|
{ name: 'invoiceDate', type: 'date', alias: 'Invoice Date' },
|
|
2878
2968
|
{ name: 'dueDate', type: 'date', alias: 'Due Date' },
|
|
@@ -2881,7 +2971,10 @@ class AccountingService {
|
|
|
2881
2971
|
{ name: 'paidAmount', type: 'money', alias: 'Paid' },
|
|
2882
2972
|
{ name: 'outstandingAmount', type: 'money', alias: 'Outstanding' }
|
|
2883
2973
|
],
|
|
2884
|
-
buttons: [
|
|
2974
|
+
buttons: [
|
|
2975
|
+
{ name: 'view', dialog: true, detailsConfig: this.invoiceDetailsDialogConfig },
|
|
2976
|
+
this.invoiceRecordPaymentButton,
|
|
2977
|
+
]
|
|
2885
2978
|
};
|
|
2886
2979
|
//--------------------------Customer Invoices-------------------------
|
|
2887
2980
|
// Customer invoice form without customerID field
|
|
@@ -3061,15 +3154,15 @@ class AccountingService {
|
|
|
3061
3154
|
{ name: 'red', condition: x => x.status == 1 },
|
|
3062
3155
|
],
|
|
3063
3156
|
},
|
|
3064
|
-
{ name: 'amount', type: 'money',
|
|
3065
|
-
color: { name: 'red', condition: x => x.reducesBalance && !x.isAggregate },
|
|
3066
|
-
},
|
|
3067
3157
|
{ name: 'typeName', type: 'text', alias: 'Type' },
|
|
3068
3158
|
{ name: 'debitAccountName', type: 'text', alias: 'Debit Account' },
|
|
3069
3159
|
{ name: 'creditAccountName', type: 'text', alias: 'Credit Account' },
|
|
3160
|
+
{ name: 'amount', type: 'money',
|
|
3161
|
+
color: { name: 'red', condition: x => x.reducesBalance && !x.isAggregate },
|
|
3162
|
+
},
|
|
3070
3163
|
],
|
|
3071
3164
|
buttons: [
|
|
3072
|
-
{ name: 'create', display: 'Create', dialog: true, action: { url: 'transactions/dto?action=create', method: 'post' } },
|
|
3165
|
+
{ name: 'create', display: 'Create Transaction', dialog: true, action: { url: 'transactions/dto?action=create', method: 'post' } },
|
|
3073
3166
|
{ name: 'view', dialog: true, detailsConfig: this.transactionDetailsConfig },
|
|
3074
3167
|
this.transactionEditButton,
|
|
3075
3168
|
this.transactionVoidButton,
|
|
@@ -3080,12 +3173,91 @@ class AccountingService {
|
|
|
3080
3173
|
this.accountTransactionsTableConfig = {
|
|
3081
3174
|
...this.transactionsTableConfig,
|
|
3082
3175
|
causeFormRefresh: true,
|
|
3176
|
+
minColumns: ['date', 'description', 'amount', 'runningBalance'],
|
|
3083
3177
|
tabTitle: 'Account Transactions',
|
|
3084
3178
|
loadAction: { url: 'transactions/account/x' }, loadCriteria: 'account', loadIDField: 'accountID',
|
|
3085
3179
|
columns: [
|
|
3086
3180
|
...this.transactionsTableConfig.columns,
|
|
3087
|
-
{ name: 'runningBalance', type: 'money', alias: 'Balance' }
|
|
3181
|
+
{ name: 'runningBalance', type: 'money', alias: 'Balance' }
|
|
3182
|
+
],
|
|
3183
|
+
};
|
|
3184
|
+
//--------------------------Transaction Templates-------------------------
|
|
3185
|
+
// Template form configuration (accountID hidden - auto-populated from context)
|
|
3186
|
+
this.transactionTemplateFormConfig = {
|
|
3187
|
+
security: { allow: [this.dataService.capTransactions] },
|
|
3188
|
+
title: 'Transaction Template',
|
|
3189
|
+
includeAudit: true,
|
|
3190
|
+
fields: [
|
|
3191
|
+
{ name: 'name', type: 'text', required: true, span: true },
|
|
3192
|
+
{ name: 'accountID', type: 'number', hidden: true }, // Auto-populated from Account Details context
|
|
3193
|
+
{ name: 'transactionTypeID', type: 'select', alias: 'Transaction Type', required: true, detailsConfig: this.transactionTypeDetailsConfig, loadAction: { url: 'transactionTypes/list/x' } },
|
|
3194
|
+
{ name: 'blank', type: 'blank' },
|
|
3195
|
+
{ name: 'debitAccountID', type: 'select', alias: 'Default Debit Account', nullable: true, masterField: 'transactionTypeID', masterValueField: 'debitAccountType', masterDefaultValueField: 'defaultDebitAccountID', masterOptionValue: 'type', loadAction: { url: 'accounts/list/x' }, detailsConfig: this.accountBaseDetailsConfig },
|
|
3196
|
+
{ name: 'creditAccountID', type: 'select', alias: 'Default Credit Account', nullable: true, masterField: 'transactionTypeID', masterValueField: 'creditAccountType', masterDefaultValueField: 'defaultCreditAccountID', masterOptionValue: 'type', loadAction: { url: 'accounts/list/x' }, detailsConfig: this.accountBaseDetailsConfig },
|
|
3197
|
+
{ name: 'description', type: 'text', alias: 'Default Description', span: true },
|
|
3198
|
+
{ name: 'defaultAmount', type: 'money', alias: 'Default Amount', span: true },
|
|
3199
|
+
],
|
|
3200
|
+
loadAction: { url: 'transactiontemplates/id' },
|
|
3201
|
+
heroField: 'transactionTemplateID',
|
|
3202
|
+
};
|
|
3203
|
+
// Process form configuration (convert template to transaction)
|
|
3204
|
+
this.transactionTemplateProcessFormConfig = {
|
|
3205
|
+
title: 'Create Transaction from Template',
|
|
3206
|
+
fixedTitle: true,
|
|
3207
|
+
fields: [
|
|
3208
|
+
{ name: 'transactionTemplateID', type: 'number', hidden: true },
|
|
3209
|
+
{ name: 'name', type: 'label', alias: 'Template', readonly: true, span: true },
|
|
3210
|
+
{ name: 'transactionTypeID', type: 'select', alias: 'Transaction Type', required: true, detailsConfig: this.transactionTypeDetailsConfig, loadAction: { url: 'transactionTypes/list/x' } },
|
|
3211
|
+
{ name: 'date', type: 'date', required: true, defaultValue: 'now' },
|
|
3212
|
+
{ name: 'debitAccountID', type: 'select', alias: 'Debit Account', required: true, masterField: 'transactionTypeID', masterValueField: 'debitAccountType', masterOptionValue: 'type', loadAction: { url: 'accounts/list/x' }, detailsConfig: this.accountBaseDetailsConfig },
|
|
3213
|
+
{ name: 'creditAccountID', type: 'select', alias: 'Credit Account', required: true, masterField: 'transactionTypeID', masterValueField: 'creditAccountType', masterOptionValue: 'type', loadAction: { url: 'accounts/list/x' }, detailsConfig: this.accountBaseDetailsConfig },
|
|
3214
|
+
{ name: 'description', type: 'text', required: true, span: true },
|
|
3215
|
+
{ name: 'amount', type: 'money', required: true, span: true },
|
|
3216
|
+
],
|
|
3217
|
+
};
|
|
3218
|
+
// Template action buttons
|
|
3219
|
+
this.transactionTemplateCreateButton = { name: 'create', display: 'Create Template', dialog: true, action: { url: 'transactiontemplates?action=create', method: 'post' } };
|
|
3220
|
+
this.transactionTemplateEditButton = { name: 'edit', dialog: true, action: { url: 'transactiontemplates?action=edit', method: 'post' } };
|
|
3221
|
+
// Template details config (for view/edit)
|
|
3222
|
+
this.transactionTemplateDetailsConfig = {
|
|
3223
|
+
formConfig: this.transactionTemplateFormConfig,
|
|
3224
|
+
heroField: 'transactionTemplateID',
|
|
3225
|
+
buttons: [this.transactionTemplateCreateButton, this.transactionTemplateEditButton]
|
|
3226
|
+
};
|
|
3227
|
+
// Process details config (for processing template)
|
|
3228
|
+
this.transactionTemplateProcessDetailsConfig = {
|
|
3229
|
+
formConfig: this.transactionTemplateProcessFormConfig,
|
|
3230
|
+
heroField: 'transactionTemplateID',
|
|
3231
|
+
mode: 'edit',
|
|
3232
|
+
buttons: [
|
|
3233
|
+
{ name: 'process', display: 'Create Transaction', icon: { name: 'play_arrow', color: 'green' }, inDialog: true,
|
|
3234
|
+
action: { url: 'transactiontemplates/dto?action=process', method: 'post',
|
|
3235
|
+
successMessage: 'Transaction created from template' }
|
|
3236
|
+
}
|
|
3237
|
+
]
|
|
3238
|
+
};
|
|
3239
|
+
// Templates table configuration (3rd tab in Account Details)
|
|
3240
|
+
this.transactionTemplatesTableConfig = {
|
|
3241
|
+
tabTitle: 'Transaction Templates',
|
|
3242
|
+
showFilter: true,
|
|
3243
|
+
minColumns: ['name', 'transactionTypeName', 'defaultAmountDisplay'],
|
|
3244
|
+
flatButtons: true,
|
|
3245
|
+
columns: [
|
|
3246
|
+
{ name: 'name', type: 'button', detailsConfig: this.transactionTemplateProcessDetailsConfig },
|
|
3247
|
+
{ name: 'transactionTypeName', type: 'text', alias: 'Type' },
|
|
3248
|
+
{ name: 'debitAccountName', type: 'text', alias: 'Debit Account' },
|
|
3249
|
+
{ name: 'creditAccountName', type: 'text', alias: 'Credit Account' },
|
|
3250
|
+
{ name: 'defaultAmountDisplay', type: 'text', alias: 'Default Amount' },
|
|
3251
|
+
],
|
|
3252
|
+
buttons: [
|
|
3253
|
+
this.transactionTemplateCreateButton,
|
|
3254
|
+
{ name: 'process', display: 'Create Transaction', icon: { name: 'play_arrow', color: 'green' }, dialog: true, detailsConfig: this.transactionTemplateProcessDetailsConfig },
|
|
3255
|
+
{ name: 'view', dialog: true, detailsConfig: this.transactionTemplateDetailsConfig },
|
|
3256
|
+
{ name: 'edit', dialog: true, action: { url: 'transactiontemplates?action=edit', method: 'post' }, detailsConfig: this.transactionTemplateDetailsConfig }, // Changed: Inline edit button with both action and detailsConfig
|
|
3257
|
+
{ name: 'delete', inDialog: true, icon: { name: 'delete', color: 'red' }, action: { url: 'transactiontemplates?action=delete', method: 'post', successMessage: 'Template deleted' }, confirm: { message: 'Delete this template?' } },
|
|
3088
3258
|
],
|
|
3259
|
+
loadAction: { url: 'transactiontemplates/account/x' }, loadCriteria: 'account', loadIDField: 'accountID',
|
|
3260
|
+
formConfig: this.transactionTemplateFormConfig
|
|
3089
3261
|
};
|
|
3090
3262
|
//--------------------------Accounts-------------------------
|
|
3091
3263
|
this.accountDetailsConfig = {
|
|
@@ -3098,6 +3270,7 @@ class AccountingService {
|
|
|
3098
3270
|
buttons: [],
|
|
3099
3271
|
loadAction: { url: 'transactionTypes/account/x' }, loadCriteria: 'account', loadIDField: 'accountID'
|
|
3100
3272
|
},
|
|
3273
|
+
{ ...this.transactionTemplatesTableConfig }, // Changed: Added 3rd tab for transaction templates
|
|
3101
3274
|
]
|
|
3102
3275
|
};
|
|
3103
3276
|
this.accountsTableConfig = {
|
|
@@ -3716,6 +3889,47 @@ class InventoryService {
|
|
|
3716
3889
|
loadAction: { url: 'products/bundles/x' },
|
|
3717
3890
|
formConfig: this.bundleProductFormConfig
|
|
3718
3891
|
};
|
|
3892
|
+
//--------------------------ServiceItems-------------------------
|
|
3893
|
+
// ServiceItem form configuration - follows Product pattern
|
|
3894
|
+
this.serviceItemFormConfig = {
|
|
3895
|
+
title: 'Service',
|
|
3896
|
+
includeAudit: true,
|
|
3897
|
+
fields: [
|
|
3898
|
+
{ name: 'name', type: 'text', required: true, alias: 'Service Name' },
|
|
3899
|
+
{ name: 'description', type: 'text', alias: 'Description' },
|
|
3900
|
+
{ name: 'unitPrice', type: 'money', required: true, alias: 'Unit Price' },
|
|
3901
|
+
{ name: 'isActive', type: 'checkbox', alias: 'Active', defaultValue: true }
|
|
3902
|
+
],
|
|
3903
|
+
loadAction: { url: 'serviceitems/id' },
|
|
3904
|
+
heroField: 'serviceItemID'
|
|
3905
|
+
};
|
|
3906
|
+
this.serviceItemCreateButton = { name: 'create', display: 'Create Service', dialog: true, action: { url: 'serviceitems?action=create', method: 'post' } };
|
|
3907
|
+
this.serviceItemEditButton = { name: 'edit', dialog: true, action: { url: 'serviceitems?action=edit', method: 'post' } };
|
|
3908
|
+
this.serviceItemDeleteButton = { name: 'delete', dialog: true, action: { url: 'serviceitems?action=delete', method: 'post' } };
|
|
3909
|
+
this.serviceItemDetailsConfig = {
|
|
3910
|
+
formConfig: this.serviceItemFormConfig,
|
|
3911
|
+
heroField: 'serviceItemID',
|
|
3912
|
+
buttons: [this.serviceItemCreateButton, this.serviceItemEditButton, this.serviceItemDeleteButton]
|
|
3913
|
+
};
|
|
3914
|
+
this.serviceItemsTableConfig = {
|
|
3915
|
+
showFilter: true,
|
|
3916
|
+
flatButtons: true,
|
|
3917
|
+
minColumns: ['name', 'unitPrice', 'statusName'],
|
|
3918
|
+
columns: [
|
|
3919
|
+
{ name: 'name', type: 'text', alias: 'Service Name' },
|
|
3920
|
+
{ name: 'description', type: 'text', alias: 'Description' },
|
|
3921
|
+
{ name: 'unitPrice', type: 'money', alias: 'Unit Price' },
|
|
3922
|
+
{ name: 'statusName', type: 'text', alias: 'Status' }
|
|
3923
|
+
],
|
|
3924
|
+
buttons: [
|
|
3925
|
+
this.serviceItemCreateButton,
|
|
3926
|
+
{ name: 'view', dialog: true, detailsConfig: this.serviceItemDetailsConfig },
|
|
3927
|
+
this.serviceItemEditButton,
|
|
3928
|
+
this.serviceItemDeleteButton
|
|
3929
|
+
],
|
|
3930
|
+
loadAction: { url: 'serviceitems/all/x' },
|
|
3931
|
+
formConfig: this.serviceItemFormConfig
|
|
3932
|
+
};
|
|
3719
3933
|
//--------------------------Requisitions-------------------------
|
|
3720
3934
|
this.requisitionItemFormConfig = {
|
|
3721
3935
|
title: 'Requisition Item',
|
|
@@ -3807,10 +4021,10 @@ class InventoryService {
|
|
|
3807
4021
|
};
|
|
3808
4022
|
//--------------------------Purchase Orders-------------------------
|
|
3809
4023
|
// Payment type options (used across forms)
|
|
4024
|
+
// Changed: Removed Credit option - timing now determines credit vs cash purchase
|
|
3810
4025
|
this.paymentTypeOptions = [
|
|
3811
4026
|
{ value: 0, name: 'Cash' },
|
|
3812
|
-
{ value: 1, name: 'Bank' }
|
|
3813
|
-
{ value: 2, name: 'Credit' }
|
|
4027
|
+
{ value: 1, name: 'Bank' }
|
|
3814
4028
|
];
|
|
3815
4029
|
// PO Item form configuration
|
|
3816
4030
|
this.purchaseOrderItemFormConfig = {
|
|
@@ -3994,9 +4208,15 @@ class InventoryService {
|
|
|
3994
4208
|
{ name: 'supplierID', type: 'select', required: true, alias: 'Supplier', section: 'receiptInfo',
|
|
3995
4209
|
loadAction: { url: 'suppliers/list/x' }, detailsConfig: this.dataService.supplierDetailsConfig, infoMessage: 'Supplier providing the goods'
|
|
3996
4210
|
},
|
|
3997
|
-
{ name: '
|
|
3998
|
-
|
|
3999
|
-
|
|
4211
|
+
{ name: 'timing', type: 'select', required: true, alias: 'Transaction Type', section: 'receiptInfo',
|
|
4212
|
+
loadAction: { url: 'inventoryreceipts/list/timing' }, defaultFirstValue: true,
|
|
4213
|
+
infoMessage: 'Cash Purchase (pay now) or Credit Purchase (pay later)'
|
|
4214
|
+
},
|
|
4215
|
+
{ name: 'paymentType', type: 'select', required: true, alias: 'Payment Method', section: 'receiptInfo', defaultFirstValue: true,
|
|
4216
|
+
options: this.paymentTypeOptions, infoMessage: 'Payment method used for this purchase',
|
|
4217
|
+
hiddenCondition: x => x.timing === 1,
|
|
4218
|
+
requiredCondition: x => x.timing === 0
|
|
4219
|
+
},
|
|
4000
4220
|
{ name: 'receiptNumber', type: 'text', alias: 'Receipt Number', readonly: true, hideOnCreate: true, section: 'receiptInfo', infoMessage: 'Auto-generated unique receipt identifier' },
|
|
4001
4221
|
{ name: 'poNumber', type: 'text', alias: 'PO Number', hideOnCreate: true, section: 'receiptInfo', infoMessage: 'Linked purchase order if receiving against a PO',
|
|
4002
4222
|
hiddenCondition: (row) => !row.purchaseOrderID,
|
|
@@ -4023,12 +4243,16 @@ class InventoryService {
|
|
|
4023
4243
|
{ name: 'totals', type: 'section', alias: 'Totals', hideOnCreate: true, collapsed: true },
|
|
4024
4244
|
{ name: 'totalAmount', type: 'money', alias: 'Total Amount', readonly: true, section: 'totals', infoMessage: 'Total value of all items' },
|
|
4025
4245
|
{ name: 'additionalInfo', type: 'section', alias: 'Additional Information', collapsed: true },
|
|
4026
|
-
{ name: 'notes', type: 'text', alias: 'Notes', span: true, section: 'additionalInfo', infoMessage: 'Additional notes about this receipt' }
|
|
4246
|
+
{ name: 'notes', type: 'text', alias: 'Notes', span: true, section: 'additionalInfo', infoMessage: 'Additional notes about this receipt' },
|
|
4247
|
+
{ name: 'supplierInvoiceNumber', type: 'text', alias: 'Supplier Invoice #', section: 'additionalInfo', infoMessage: 'Invoice number from supplier' },
|
|
4027
4248
|
],
|
|
4028
4249
|
loadAction: { url: 'inventoryreceipts/id' },
|
|
4029
4250
|
heroField: 'inventoryReceiptID'
|
|
4030
4251
|
};
|
|
4031
|
-
this.inventoryReceiptEditButton = { name: 'edit', dialog: true,
|
|
4252
|
+
this.inventoryReceiptEditButton = { name: 'edit', dialog: true,
|
|
4253
|
+
action: { url: 'inventoryreceipts?action=edit', method: 'post' },
|
|
4254
|
+
visible: (x) => x.status !== InventoryReceiptStatus.Completed
|
|
4255
|
+
};
|
|
4032
4256
|
this.inventoryReceiptCompleteButton = { name: 'complete', display: 'Complete Receipt', inDialog: true, // Changed: Updated display text and action name
|
|
4033
4257
|
icon: { name: 'check_circle', color: 'green' },
|
|
4034
4258
|
action: { url: 'inventoryreceipts?action=complete', method: 'post', successMessage: 'Receipt Completed' }, // Changed: Updated endpoint and message
|
|
@@ -4061,7 +4285,7 @@ class InventoryService {
|
|
|
4061
4285
|
{ name: 'receiptDate', type: 'date', alias: 'Date' },
|
|
4062
4286
|
{ name: 'supplierName', type: 'text', alias: 'Supplier' },
|
|
4063
4287
|
{ name: 'productsDisplay', type: 'text', alias: 'Products' }, // Added: Display product names or count
|
|
4064
|
-
{ name: '
|
|
4288
|
+
{ name: 'timingName', type: 'text', alias: 'Payment' },
|
|
4065
4289
|
{ name: 'status', type: 'icon', alias: 'Status', detailsConfig: this.inventoryReceiptDetailsConfig, // Changed: Use status instead of statusName
|
|
4066
4290
|
icons: [
|
|
4067
4291
|
{ name: 'article', color: '#9E9E9E', condition: (x) => x.status === InventoryReceiptStatus.Draft, tip: 'Draft' }, // Changed: Use status enum
|
|
@@ -4362,7 +4586,23 @@ class InventoryService {
|
|
|
4362
4586
|
fields: [
|
|
4363
4587
|
{ name: 'saleInfo', type: 'section', alias: 'Sale Information' }, // Changed: Hide when single product mode or when editing existing sale
|
|
4364
4588
|
{ name: 'saleDate', type: 'date', required: true, alias: 'Sale Date', section: 'saleInfo', infoMessage: 'Date when the sale was completed' },
|
|
4365
|
-
{ name: 'customerID', type: 'select', required: true, alias: 'Customer', section: 'saleInfo',
|
|
4589
|
+
{ name: 'customerID', type: 'select', required: true, alias: 'Customer', section: 'saleInfo',
|
|
4590
|
+
loadAction: { url: 'customers/list/x' }, detailsConfig: this.dataService.customerDetailsConfig
|
|
4591
|
+
},
|
|
4592
|
+
{ name: 'timing', type: 'select', required: true, alias: 'Transaction Type', section: 'saleInfo',
|
|
4593
|
+
loadAction: { url: 'sales/list/timing' }, defaultFirstValue: true, infoMessage: 'Cash Sale (pay now) or Credit Sale (pay later)'
|
|
4594
|
+
},
|
|
4595
|
+
{ name: 'paymentMethod', type: 'select', required: true, alias: 'Payment Method', section: 'saleInfo', defaultFirstValue: true, infoMessage: 'Method of payment used for this sale',
|
|
4596
|
+
requiredCondition: x => x.timing === 0,
|
|
4597
|
+
hiddenCondition: x => x.timing === 1,
|
|
4598
|
+
options: [
|
|
4599
|
+
{ name: 'Cash', value: 0 },
|
|
4600
|
+
{ name: 'Bank Transfer', value: 1 },
|
|
4601
|
+
{ name: 'Mobile Money', value: 2 },
|
|
4602
|
+
{ name: 'Card', value: 3 },
|
|
4603
|
+
{ name: 'Complementary', value: 5 }
|
|
4604
|
+
],
|
|
4605
|
+
},
|
|
4366
4606
|
{ 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
|
|
4367
4607
|
{ 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' },
|
|
4368
4608
|
{ name: 'quickSaleItem', type: 'section', alias: 'Quick Sale Item', hiddenCondition: x => x.multipleProducts === true || x.saleID }, // Changed: Section for single product entry
|
|
@@ -4375,17 +4615,16 @@ class InventoryService {
|
|
|
4375
4615
|
},
|
|
4376
4616
|
{ name: 'quantity', type: 'number', required: true, alias: 'Quantity', section: 'quickSaleItem', defaultValue: 1, hiddenCondition: x => x.multipleProducts === true, infoMessage: 'Quantity of the product' }, // Changed: Quantity field for quick sale
|
|
4377
4617
|
{ name: 'unitPrice', type: 'money', required: true, alias: 'Unit Price', section: 'quickSaleItem', hiddenCondition: x => x.multipleProducts === true, infoMessage: 'Price per unit' }, // Changed: Unit price field for quick sale
|
|
4378
|
-
{ name: 'paymentInfo', type: 'section', alias: 'Payment Information', collapsedCondition: x => x.saleID },
|
|
4379
|
-
{ name: 'paymentMethod', type: 'select', required: true, alias: 'Payment Method', section: 'paymentInfo', options: [{ name: 'Cash', value: 0 }, { name: 'Bank Transfer', value: 1 }, { name: 'Mobile Money', value: 2 }, { name: 'Card', value: 3 }, { name: 'Credit', value: 4 }, { name: 'Complementary', value: 5 }], defaultFirstValue: true, infoMessage: 'Method of payment used for this sale' },
|
|
4380
|
-
{ name: 'paymentReference', type: 'text', alias: 'Payment Reference', section: 'paymentInfo', infoMessage: 'Transaction reference number or payment receipt' },
|
|
4381
|
-
{ name: 'paymentStatus', type: 'select', alias: 'Payment Status', readonly: true, section: 'paymentInfo', loadAction: { url: 'sales/list/payment-status' }, hideOnCreate: true, infoMessage: 'Current payment status of the sale' },
|
|
4382
4618
|
{ name: 'totals', type: 'section', alias: 'Totals', collapsed: true, hideOnCreate: true },
|
|
4383
4619
|
{ name: 'subTotal', type: 'money', alias: 'Sub Total', readonly: true, section: 'totals', infoMessage: 'Total before tax and discounts' },
|
|
4384
4620
|
{ name: 'taxAmount', type: 'money', alias: 'Tax Amount', section: 'totals', infoMessage: 'Tax amount applied to the sale' },
|
|
4385
4621
|
{ name: 'discount', type: 'money', alias: 'Discount', section: 'totals', infoMessage: 'Discount amount applied to the sale' },
|
|
4386
4622
|
{ name: 'totalAmount', type: 'money', alias: 'Total Amount', readonly: true, section: 'totals', infoMessage: 'Final amount after tax and discounts' },
|
|
4387
4623
|
{ name: 'additionalInfo', type: 'section', alias: 'Additional Information', collapsed: true },
|
|
4388
|
-
{ name: 'notes', type: 'text', alias: 'Notes', span: true, section: 'additionalInfo', infoMessage: 'Additional notes or comments about this sale' }
|
|
4624
|
+
{ name: 'notes', type: 'text', alias: 'Notes', span: true, section: 'additionalInfo', infoMessage: 'Additional notes or comments about this sale' },
|
|
4625
|
+
{ name: 'paymentReference', type: 'text', alias: 'Payment Reference', section: 'additionalInfo', infoMessage: 'Transaction reference number or payment receipt',
|
|
4626
|
+
hiddenCondition: x => x.timing === 1
|
|
4627
|
+
},
|
|
4389
4628
|
],
|
|
4390
4629
|
loadAction: { url: 'sales/id' },
|
|
4391
4630
|
heroField: 'saleID'
|
|
@@ -4399,26 +4638,72 @@ class InventoryService {
|
|
|
4399
4638
|
{ name: 'Partial', value: 2, icon: 'payment' }
|
|
4400
4639
|
]
|
|
4401
4640
|
};
|
|
4641
|
+
// Added: Sale payment form configuration (matches invoice payment form)
|
|
4642
|
+
this.salePaymentFormConfig = {
|
|
4643
|
+
security: { allow: [this.dataService.capSales] },
|
|
4644
|
+
title: 'Record Payment',
|
|
4645
|
+
fixedTitle: true,
|
|
4646
|
+
fields: [
|
|
4647
|
+
{ name: 'saleID', type: 'number', hidden: true },
|
|
4648
|
+
{ name: 'paymentDate', type: 'date', alias: 'Payment Date', required: true },
|
|
4649
|
+
{ name: 'method', type: 'select', alias: 'Payment Method', required: true, defaultValue: 1, loadAction: { url: 'invoicepayments/list/methods' } }, // Changed: Default to 1 (Bank)
|
|
4650
|
+
{ name: 'totalAmount', type: 'label', alias: 'Total Amount', readonly: true },
|
|
4651
|
+
{ name: 'amount', type: 'money', alias: 'Amount', required: true, span: true },
|
|
4652
|
+
{ name: 'paymentReferenceTemp', type: 'text', alias: 'Reference', span: true }
|
|
4653
|
+
],
|
|
4654
|
+
};
|
|
4655
|
+
// Added: Sale payment button (matches invoice payment button pattern)
|
|
4656
|
+
this.salePaymentCreateButton = {
|
|
4657
|
+
name: 'edit', // Changed: Use standard 'edit' button name for framework recognition
|
|
4658
|
+
action: { url: 'sales?action=record-payment', method: 'post', successMessage: 'Payment recorded successfully' }
|
|
4659
|
+
};
|
|
4660
|
+
// Added: Sale payment details dialog config
|
|
4661
|
+
this.salePaymentDetailsConfig = {
|
|
4662
|
+
formConfig: this.salePaymentFormConfig,
|
|
4663
|
+
heroField: 'saleID',
|
|
4664
|
+
mode: 'edit',
|
|
4665
|
+
buttons: [
|
|
4666
|
+
this.salePaymentCreateButton // Changed: Use separate button definition
|
|
4667
|
+
]
|
|
4668
|
+
};
|
|
4669
|
+
// Added: Sale payment record button
|
|
4670
|
+
this.saleRecordPaymentButton = {
|
|
4671
|
+
name: 'record-payment', display: 'Record Payment', inDialog: true, dialog: true, icon: { name: 'payment', color: 'blue' },
|
|
4672
|
+
detailsConfig: this.salePaymentDetailsConfig,
|
|
4673
|
+
visible: x => x.timing === 1 && x.paymentStatus !== 1
|
|
4674
|
+
};
|
|
4675
|
+
// Added: Sale payments table for payment history (from linked invoice)
|
|
4676
|
+
this.salePaymentsTableConfig = {
|
|
4677
|
+
tabTitle: 'Payment History',
|
|
4678
|
+
showFilter: false,
|
|
4679
|
+
minColumns: ['paymentDate', 'amount', 'methodName'],
|
|
4680
|
+
columns: [
|
|
4681
|
+
{ name: 'paymentDate', type: 'date', alias: 'Date' },
|
|
4682
|
+
{ name: 'amount', type: 'money', alias: 'Amount' },
|
|
4683
|
+
{ name: 'methodName', type: 'text', alias: 'Method' },
|
|
4684
|
+
{ name: 'reference', type: 'text', alias: 'Reference' }
|
|
4685
|
+
],
|
|
4686
|
+
loadAction: { url: 'invoicepayments/x/x' }, loadCriteria: 'invoice', loadIDField: 'invoiceID',
|
|
4687
|
+
hideTabCondition: (x) => x.timing !== 1 // Changed: Only show for deferred payments (credit sales)
|
|
4688
|
+
};
|
|
4402
4689
|
// Details dialog config for sale with complete button (only visible if not yet paid)
|
|
4403
4690
|
this.saleDetailsConfig = {
|
|
4404
4691
|
formConfig: this.saleFormConfig,
|
|
4405
|
-
tableConfigs: [this.saleItemsTableConfig],
|
|
4692
|
+
tableConfigs: [this.saleItemsTableConfig, this.salePaymentsTableConfig], // Added: Include payment history table
|
|
4406
4693
|
heroField: 'saleID',
|
|
4407
4694
|
stepConfig: this.saleStepConfig, // Changed: Added step config for payment status tracking
|
|
4408
4695
|
buttons: [
|
|
4409
4696
|
{ name: 'complete', display: 'Complete Sale', color: 'primary', inDialog: true,
|
|
4410
4697
|
action: { url: 'sales?action=complete', method: 'post' },
|
|
4411
4698
|
confirm: { message: 'Complete this sale? This will reduce inventory and create accounting entries.' },
|
|
4412
|
-
visible: x => x.paymentStatus !== 1
|
|
4413
|
-
}
|
|
4699
|
+
visible: x => x.timing === 0 && x.paymentStatus !== 1 // Changed: Only for immediate sales that aren't paid (deferred sales are auto-completed)
|
|
4700
|
+
},
|
|
4701
|
+
this.saleRecordPaymentButton // Added: Record payment button for credit sales
|
|
4414
4702
|
]
|
|
4415
4703
|
};
|
|
4416
4704
|
this.saleViewButton = { name: 'view', dialog: true, detailsConfig: this.saleDetailsConfig };
|
|
4417
4705
|
this.saleCreateButton = {
|
|
4418
|
-
name: 'create',
|
|
4419
|
-
display: 'Quick Sale',
|
|
4420
|
-
dialog: true,
|
|
4421
|
-
action: { url: 'sales?action=create', method: 'post' },
|
|
4706
|
+
name: 'create', display: 'Quick Sale', dialog: true, action: { url: 'sales?action=create', method: 'post' },
|
|
4422
4707
|
};
|
|
4423
4708
|
this.saleVoidButton = { name: 'void', display: 'Void', icon: { name: 'undo', color: 'red' },
|
|
4424
4709
|
confirm: { message: 'Caution: Voiding this sale will reverse all inventory transactions and void accounting entries. This action cannot be undone.' },
|
|
@@ -4433,7 +4718,7 @@ class InventoryService {
|
|
|
4433
4718
|
{ name: 'saleDate', type: 'date', alias: 'Date' },
|
|
4434
4719
|
{ name: 'displayCustomerName', type: 'text', alias: 'Customer' },
|
|
4435
4720
|
{ name: 'productsDisplay', type: 'text', alias: 'Products' }, // Added: Display product names or count
|
|
4436
|
-
{ name: '
|
|
4721
|
+
{ name: 'timingName', type: 'text', alias: 'Payment' },
|
|
4437
4722
|
{ name: 'paymentStatus', type: 'icon', alias: 'Status', detailsConfig: this.inventoryReceiptDetailsConfig, // Changed: Use status instead of statusName
|
|
4438
4723
|
icons: [
|
|
4439
4724
|
{ name: 'article', color: '#9E9E9E', condition: (x) => x.paymentStatus === 0, tip: 'Unpaid' }, // Changed: Use status enum
|
|
@@ -4444,7 +4729,10 @@ class InventoryService {
|
|
|
4444
4729
|
{ name: 'totalAmount', type: 'money', alias: 'Total' }
|
|
4445
4730
|
],
|
|
4446
4731
|
buttons: [
|
|
4447
|
-
this.saleViewButton,
|
|
4732
|
+
this.saleViewButton,
|
|
4733
|
+
this.saleRecordPaymentButton,
|
|
4734
|
+
this.saleCreateButton,
|
|
4735
|
+
this.saleVoidButton
|
|
4448
4736
|
],
|
|
4449
4737
|
loadAction: { url: 'sales/all/x' },
|
|
4450
4738
|
formConfig: this.saleFormConfig
|
|
@@ -4757,31 +5045,45 @@ class TabService {
|
|
|
4757
5045
|
this.buttonService = buttonService;
|
|
4758
5046
|
this.tableConfigService = tableConfigService;
|
|
4759
5047
|
}
|
|
5048
|
+
// Replace {propertyName} placeholders in URL with values from parentData
|
|
5049
|
+
transformCountUrl(url, parentData) {
|
|
5050
|
+
if (!parentData || !url || !url.includes('{'))
|
|
5051
|
+
return url; // Changed: Only process if URL contains placeholders
|
|
5052
|
+
return url.replace(/\{(\w+)\}/g, (match, propName) => {
|
|
5053
|
+
const value = parentData[propName];
|
|
5054
|
+
return value !== undefined && value !== null ? String(value) : match;
|
|
5055
|
+
});
|
|
5056
|
+
}
|
|
4760
5057
|
// Initialize tab counts and reload subjects for all tabs
|
|
4761
|
-
initializeTabs(tableConfigs) {
|
|
5058
|
+
initializeTabs(tableConfigs, parentData) {
|
|
4762
5059
|
const tabCounts = {};
|
|
4763
5060
|
const tableReloads = {};
|
|
4764
5061
|
tableConfigs.forEach((config, index) => {
|
|
4765
5062
|
if (config.countAction) {
|
|
4766
|
-
this.loadTabCount(index, config.countAction, tabCounts);
|
|
5063
|
+
this.loadTabCount(index, config.countAction, tabCounts, parentData); // Changed: Pass parentData for URL placeholder replacement
|
|
4767
5064
|
}
|
|
4768
5065
|
tableReloads[index] = new Subject();
|
|
4769
5066
|
});
|
|
4770
5067
|
return { tabCounts, tableReloads };
|
|
4771
5068
|
}
|
|
4772
5069
|
// Load count for a specific tab
|
|
4773
|
-
loadTabCount(tabIndex, countAction, tabCounts
|
|
4774
|
-
|
|
5070
|
+
loadTabCount(tabIndex, countAction, tabCounts, parentData // Changed: Accept parentData for URL placeholder replacement
|
|
5071
|
+
) {
|
|
5072
|
+
// Changed: Transform URL by replacing {propertyName} placeholders with parentData values
|
|
5073
|
+
const transformedUrl = this.transformCountUrl(countAction.url, parentData);
|
|
5074
|
+
const transformedAction = { ...countAction, url: transformedUrl };
|
|
5075
|
+
this.dataService.CallApi(transformedAction).subscribe((apiResponse) => {
|
|
4775
5076
|
if (apiResponse.success) {
|
|
4776
5077
|
tabCounts[tabIndex] = apiResponse.data;
|
|
4777
5078
|
}
|
|
4778
5079
|
});
|
|
4779
5080
|
}
|
|
4780
5081
|
// Refresh count for a specific tab
|
|
4781
|
-
refreshTabCount(index, tableConfigs, tabCounts
|
|
5082
|
+
refreshTabCount(index, tableConfigs, tabCounts, parentData // Changed: Accept parentData for URL placeholder replacement
|
|
5083
|
+
) {
|
|
4782
5084
|
const config = tableConfigs[index];
|
|
4783
5085
|
if (config?.countAction) {
|
|
4784
|
-
this.loadTabCount(index, config.countAction, tabCounts);
|
|
5086
|
+
this.loadTabCount(index, config.countAction, tabCounts, parentData); // Changed: Pass parentData
|
|
4785
5087
|
}
|
|
4786
5088
|
}
|
|
4787
5089
|
// Handle tab change - mark tab as loaded for lazy loading
|
|
@@ -5975,6 +6277,7 @@ class SelectCommonComponent {
|
|
|
5975
6277
|
this.subscription = this.field.optionsSubject.subscribe(newOptions => {
|
|
5976
6278
|
if (newOptions) {
|
|
5977
6279
|
this.options = newOptions;
|
|
6280
|
+
this.setDefaultValue(); // Changed: Apply default after options update via subscription
|
|
5978
6281
|
this.updateSelectedOptionHint();
|
|
5979
6282
|
}
|
|
5980
6283
|
});
|
|
@@ -5991,15 +6294,54 @@ class SelectCommonComponent {
|
|
|
5991
6294
|
this.updateSelectedOptionHint();
|
|
5992
6295
|
}
|
|
5993
6296
|
setDefaultValue() {
|
|
5994
|
-
|
|
5995
|
-
|
|
6297
|
+
// Guard: Need options to set any default
|
|
6298
|
+
if (!this.options || this.options.length === 0) {
|
|
6299
|
+
return;
|
|
6300
|
+
}
|
|
6301
|
+
// Guard: Check if value is already meaningfully set
|
|
6302
|
+
const hasValue = this.value != null &&
|
|
6303
|
+
this.value !== undefined &&
|
|
6304
|
+
!(typeof this.value === 'string' && this.value === '') &&
|
|
6305
|
+
!(typeof this.value === 'number' && this.value === 0 && !this.nullable);
|
|
6306
|
+
if (hasValue) {
|
|
6307
|
+
// Value already set - verify it exists in options
|
|
6308
|
+
const valueExists = this.options.some(opt => this.compareValues(opt[this.optionValue], this.value));
|
|
6309
|
+
if (valueExists) {
|
|
6310
|
+
return; // Valid value already selected
|
|
6311
|
+
}
|
|
6312
|
+
}
|
|
6313
|
+
// Priority 1: Use field.defaultValue if configured
|
|
6314
|
+
const defaultVal = this.field?.defaultValue;
|
|
6315
|
+
if (defaultVal != null && defaultVal !== undefined) {
|
|
6316
|
+
const matchingOption = this.options.find(opt => this.compareValues(opt[this.optionValue], defaultVal));
|
|
6317
|
+
if (matchingOption) {
|
|
6318
|
+
this.value = matchingOption[this.optionValue];
|
|
6319
|
+
this.changed();
|
|
6320
|
+
return;
|
|
6321
|
+
}
|
|
6322
|
+
}
|
|
6323
|
+
// Priority 2: Use defaultFirstValue if configured
|
|
6324
|
+
if (this.defaultFirstValue) {
|
|
5996
6325
|
this.value = this.options[0][this.optionValue];
|
|
5997
6326
|
this.changed();
|
|
5998
6327
|
}
|
|
5999
6328
|
}
|
|
6329
|
+
// Changed: Added helper method for type-coerced value comparison (handles number vs string)
|
|
6330
|
+
compareValues(optionValue, targetValue) {
|
|
6331
|
+
if (optionValue === targetValue)
|
|
6332
|
+
return true;
|
|
6333
|
+
if (optionValue == null || targetValue == null)
|
|
6334
|
+
return false;
|
|
6335
|
+
return String(optionValue) === String(targetValue);
|
|
6336
|
+
}
|
|
6000
6337
|
changed() {
|
|
6001
6338
|
this.valueChange.emit(this.value);
|
|
6002
6339
|
this.updateSelectedOptionHint();
|
|
6340
|
+
// Added: Call onSelectChange callback if provided
|
|
6341
|
+
if (this.field?.onSelectChange && this.data) {
|
|
6342
|
+
const selectedOption = this.options?.find(opt => opt[this.optionValue] === this.value);
|
|
6343
|
+
this.field.onSelectChange(this.value, this.data, selectedOption);
|
|
6344
|
+
}
|
|
6003
6345
|
}
|
|
6004
6346
|
updateSelectedOptionHint() {
|
|
6005
6347
|
if (!this.options || this.options.length === 0 || this.value == null || this.value == undefined) {
|
|
@@ -6038,7 +6380,18 @@ class SelectCommonComponent {
|
|
|
6038
6380
|
this.getData(refreshAction);
|
|
6039
6381
|
}
|
|
6040
6382
|
transformLoadUrl(action) {
|
|
6041
|
-
if (!action?.url || !this.
|
|
6383
|
+
if (!action?.url || !this.data)
|
|
6384
|
+
return action;
|
|
6385
|
+
// Changed: Check if URL contains {propertyName} placeholders first
|
|
6386
|
+
if (action.url.includes('{')) {
|
|
6387
|
+
const transformedUrl = action.url.replace(/\{(\w+)\}/g, (match, propName) => {
|
|
6388
|
+
const value = this.data?.[propName];
|
|
6389
|
+
return value !== undefined && value !== null ? String(value) : match;
|
|
6390
|
+
});
|
|
6391
|
+
return { ...action, url: transformedUrl };
|
|
6392
|
+
}
|
|
6393
|
+
// Changed: Fallback to existing loadIDField pattern for backward compatibility
|
|
6394
|
+
if (!this.loadIDField)
|
|
6042
6395
|
return action;
|
|
6043
6396
|
const idValue = this.data[this.loadIDField];
|
|
6044
6397
|
if (!idValue)
|
|
@@ -6956,7 +7309,18 @@ class ViewerComponent {
|
|
|
6956
7309
|
// fileList: string[];
|
|
6957
7310
|
loadData() {
|
|
6958
7311
|
console.log("Calling files");
|
|
6959
|
-
let url = this.fileAction.url
|
|
7312
|
+
let url = this.fileAction.url;
|
|
7313
|
+
// Changed: Support generic {propertyName} placeholders first
|
|
7314
|
+
if (url.includes('{')) {
|
|
7315
|
+
url = url.replace(/\{(\w+)\}/g, (match, propName) => {
|
|
7316
|
+
const value = { folderName: this.folderName }[propName];
|
|
7317
|
+
return value !== undefined && value !== null ? String(value) : match;
|
|
7318
|
+
});
|
|
7319
|
+
}
|
|
7320
|
+
else {
|
|
7321
|
+
// Fallback: Legacy /x replacement for backward compatibility
|
|
7322
|
+
url = url.replace("/x", '/' + this.folderName);
|
|
7323
|
+
}
|
|
6960
7324
|
this.dataService.CallApi({ url: url }, "").subscribe((apiResponse) => {
|
|
6961
7325
|
this.fileNames = apiResponse.data;
|
|
6962
7326
|
// console.log(this.fileNames)
|
|
@@ -7532,12 +7896,22 @@ class NotesComponent {
|
|
|
7532
7896
|
return;
|
|
7533
7897
|
}
|
|
7534
7898
|
// Otherwise, load from the URL if configured
|
|
7535
|
-
if (this.loadAction && this.data
|
|
7536
|
-
|
|
7537
|
-
|
|
7538
|
-
|
|
7899
|
+
if (this.loadAction && this.data) {
|
|
7900
|
+
let url = this.loadAction.url;
|
|
7901
|
+
// Changed: Support generic {propertyName} placeholders first
|
|
7902
|
+
if (url.includes('{')) {
|
|
7903
|
+
url = url.replace(/\{(\w+)\}/g, (match, propName) => {
|
|
7904
|
+
const value = this.data?.[propName];
|
|
7905
|
+
return value !== undefined && value !== null ? String(value) : match;
|
|
7906
|
+
});
|
|
7907
|
+
}
|
|
7908
|
+
else if (this.loadIDField) {
|
|
7909
|
+
// Fallback: Use loadIDField for backward compatibility
|
|
7910
|
+
const idValue = this.data[this.loadIDField];
|
|
7911
|
+
if (!idValue)
|
|
7912
|
+
return;
|
|
7913
|
+
url = url.replace('/x', '/' + idValue);
|
|
7539
7914
|
}
|
|
7540
|
-
let url = this.loadAction.url.replace('{x}', idValue);
|
|
7541
7915
|
this.dataService.CallApi({ ...this.loadAction, url }).subscribe((apiResponse) => {
|
|
7542
7916
|
if (apiResponse.success && apiResponse.data) {
|
|
7543
7917
|
this.notes = apiResponse.data;
|
|
@@ -9882,7 +10256,7 @@ class TabsLiteComponent {
|
|
|
9882
10256
|
}
|
|
9883
10257
|
ngOnInit() {
|
|
9884
10258
|
// Initialize tabs with counts and reload subjects using service
|
|
9885
|
-
const initialized = this.tabService.initializeTabs(this.tableConfigs);
|
|
10259
|
+
const initialized = this.tabService.initializeTabs(this.tableConfigs, this.parentDetails); // Changed: Pass parentDetails for URL placeholder replacement
|
|
9886
10260
|
this.tabCounts = initialized.tabCounts;
|
|
9887
10261
|
this.tableReloads = initialized.tableReloads;
|
|
9888
10262
|
}
|
|
@@ -9909,7 +10283,7 @@ class TabsLiteComponent {
|
|
|
9909
10283
|
}
|
|
9910
10284
|
// Refresh count for specific tab
|
|
9911
10285
|
refreshTabCount(index) {
|
|
9912
|
-
this.tabService.refreshTabCount(index, this.tableConfigs, this.tabCounts);
|
|
10286
|
+
this.tabService.refreshTabCount(index, this.tableConfigs, this.tabCounts, this.parentDetails); // Changed: Pass parentDetails for URL placeholder replacement
|
|
9913
10287
|
}
|
|
9914
10288
|
// Handle action success from table
|
|
9915
10289
|
onTableActionSuccess(tabIndex, event) {
|
|
@@ -11113,7 +11487,7 @@ class TabsInternalComponent {
|
|
|
11113
11487
|
}
|
|
11114
11488
|
ngOnInit() {
|
|
11115
11489
|
// Initialize tabs with counts and reload subjects using service
|
|
11116
|
-
const initialized = this.tabService.initializeTabs(this.tableConfigs);
|
|
11490
|
+
const initialized = this.tabService.initializeTabs(this.tableConfigs, this.parentDetails); // Changed: Pass parentDetails for URL placeholder replacement
|
|
11117
11491
|
this.tabCounts = initialized.tabCounts;
|
|
11118
11492
|
this.tableReloads = initialized.tableReloads;
|
|
11119
11493
|
}
|
|
@@ -11140,7 +11514,7 @@ class TabsInternalComponent {
|
|
|
11140
11514
|
}
|
|
11141
11515
|
// Refresh count for specific tab
|
|
11142
11516
|
refreshTabCount(index) {
|
|
11143
|
-
this.tabService.refreshTabCount(index, this.tableConfigs, this.tabCounts);
|
|
11517
|
+
this.tabService.refreshTabCount(index, this.tableConfigs, this.tabCounts, this.parentDetails); // Changed: Pass parentDetails for URL placeholder replacement
|
|
11144
11518
|
}
|
|
11145
11519
|
// Handle action success from table
|
|
11146
11520
|
onTableActionSuccess(tabIndex, event) {
|
|
@@ -13536,7 +13910,7 @@ class TabsComponent {
|
|
|
13536
13910
|
}
|
|
13537
13911
|
ngOnInit() {
|
|
13538
13912
|
// Initialize tabs with counts and reload subjects using service
|
|
13539
|
-
const initialized = this.tabService.initializeTabs(this.tableConfigs);
|
|
13913
|
+
const initialized = this.tabService.initializeTabs(this.tableConfigs, this.parentDetails); // Changed: Pass parentDetails for URL placeholder replacement
|
|
13540
13914
|
this.tabCounts = initialized.tabCounts;
|
|
13541
13915
|
this.tableReloads = initialized.tableReloads;
|
|
13542
13916
|
}
|
|
@@ -13563,7 +13937,7 @@ class TabsComponent {
|
|
|
13563
13937
|
}
|
|
13564
13938
|
// Refresh count for specific tab
|
|
13565
13939
|
refreshTabCount(index) {
|
|
13566
|
-
this.tabService.refreshTabCount(index, this.tableConfigs, this.tabCounts);
|
|
13940
|
+
this.tabService.refreshTabCount(index, this.tableConfigs, this.tabCounts, this.parentDetails); // Changed: Pass parentDetails for URL placeholder replacement
|
|
13567
13941
|
}
|
|
13568
13942
|
// Changed: Handle action success from table (matching tabs-internal)
|
|
13569
13943
|
onTableActionSuccess(tabIndex, event) {
|
|
@@ -14827,11 +15201,11 @@ class RolesComponent {
|
|
|
14827
15201
|
});
|
|
14828
15202
|
}
|
|
14829
15203
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: RolesComponent, deps: [{ token: HttpService }, { token: i1$2.Router }, { token: AuthService }, { token: DataServiceLib }, { token: DialogService }, { token: i5.MatDialog }, { token: MessageService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
14830
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: RolesComponent, isStandalone: false, selector: "spa-roles", ngImport: i0, template: "<h4> Roles </h4>\r\n<hr />\r\n\r\n<div class=\"container-fluid mb-5\">\r\n\r\n <div class=\"d-flex justify-content-between mb-2\">\r\n\r\n <div >\r\n <button id=\"btnNewRole\" mat-raised-button color=\"primary\" (click)=\"addRole()\">New Role</button>\r\n </div>\r\n\r\n <div class=\"d-flex justify-content-end\">\r\n <button id=\"btnRefresh\" mat-icon-button color=\"primary\" (click)=\"refresh()\" matTooltip=\"refresh data\" matTooltipPosition=\"right\"><mat-icon >refresh</mat-icon></button>\r\n </div>\r\n\r\n </div>\r\n\r\n\r\n <div class=\"row mt-2 mb-1\" *ngFor=\"let role of roles\">\r\n\r\n <mat-card class=\"mat-elevation-z8\" style=\"width:100%\">\r\n\r\n <div class=\"d-flex justify-content-between align-items-center\">\r\n\r\n <label style=\"font-size: 16px;\">{{role.roleName}}</label>\r\n\r\n <button mat-icon-button color=\"primary\" matTooltip=\"Rename Role\" (click)=\"renameRole(role)\">\r\n <mat-icon>edit</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <hr style=\"margin-top: 0px;\">\r\n\r\n <div class=\"tin-row\" style=\" font-size:12px;\">\r\n\r\n\r\n <div class=\"tin-row\" *ngFor=\"let capItem of appConfig.capItems\">\r\n\r\n <!-- Main item-->\r\n <mat-checkbox *ngIf=\"capItem.isBool || capItem.capSubItems\"\r\n color=\"primary\" style=\"min-width: 100px;\" [(ngModel)]=\"role[capItem.name]\" (ngModelChange)=\"onCapItemChange(capItem, $event, role)\">\r\n {{capItem.display}}\r\n <span *ngIf=\"!role[capItem.name] && hasSubItemsAccess(capItem, role)\" class=\"asterisk\" style=\"color: red;\">*</span>\r\n </mat-checkbox>\r\n\r\n <spa-select\r\n *ngIf=\"!capItem.isBool && !capItem.capSubItems\"\r\n [options]=\"roleAccessOptions\"\r\n optionDisplay=\"name\"\r\n optionValue=\"value\"\r\n [display]=\"capItem.display\"\r\n [(value)]=\"role[capItem.name]\"\r\n width=\"150px\" \r\n style=\"font-size: 12px;\">\r\n </spa-select>\r\n\r\n\r\n <ng-container *ngIf=\"capItem.capSubItems && role[capItem.name]\">\r\n\r\n <div class=\"tin-row\" *ngFor=\"let capSubItem of capItem.capSubItems\">\r\n\r\n\r\n <!-- Sub Item -->\r\n <mat-checkbox *ngIf=\"capSubItem.isBool\"\r\n color=\"primary\" style=\"min-width: 100px;\" [(ngModel)]=\"role[capSubItem.name]\">\r\n {{capSubItem.display}}\r\n </mat-checkbox>\r\n\r\n <spa-select\r\n *ngIf=\"!capSubItem.isBool\"\r\n [options]=\"roleAccessOptions\"\r\n optionDisplay=\"name\"\r\n optionValue=\"value\"\r\n [display]=\"capSubItem.display\"\r\n [(value)]=\"role[capSubItem.name]\"\r\n width=\"150px\"\r\n style=\"font-size: 12px;\">\r\n </spa-select>\r\n\r\n <ng-container *ngIf=\"capSubItem.capSubItems\">\r\n\r\n <div class=\"tin-row\" *ngFor=\"let capSubSubItem of capSubItem.capSubItems\">\r\n\r\n <!-- Sub Sub Items -->\r\n <mat-checkbox *ngIf=\"capSubSubItem.isBool\"\r\n color=\"primary\" style=\"min-width: 100px;\" [(ngModel)]=\"role[capSubSubItem.name]\">\r\n {{capSubSubItem.display}}\r\n </mat-checkbox>\r\n\r\n <spa-select\r\n *ngIf=\"!capSubSubItem.isBool\"\r\n [options]=\"roleAccessOptions\"\r\n optionDisplay=\"name\"\r\n optionValue=\"value\"\r\n [display]=\"capSubSubItem.display\"\r\n [(value)]=\"role[capSubSubItem.name]\"\r\n width=\"150px\" \r\n style=\"font-size: 12px;\">\r\n </spa-select>\r\n\r\n </div>\r\n\r\n </ng-container>\r\n\r\n\r\n\r\n </div>\r\n\r\n </ng-container>\r\n\r\n </div>\r\n\r\n </div>\r\n\r\n\r\n <mat-card-actions>\r\n\r\n <button mat-raised-button color=\"primary\" (click)=\"updateRole(role)\" style=\"margin-right:10px;\">\r\n <mat-icon>done_all</mat-icon>\r\n Update\r\n </button>\r\n\r\n <button mat-raised-button (click)=\"deleteRole(role)\" style=\"margin-right:10px\">\r\n <mat-icon>delete</mat-icon>\r\n Delete\r\n </button>\r\n\r\n </mat-card-actions>\r\n\r\n </mat-card>\r\n\r\n </div>\r\n\r\n\r\n</div>\r\n\r\n", styles: [""], dependencies: [{ kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i3$1.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { 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: i5$1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i6$2.MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "directive", type: i6$2.MatCardActions, selector: "mat-card-actions", inputs: ["align"], exportAs: ["matCardActions"] }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: SelectComponent, selector: "spa-select", inputs: ["detailsConfig"] }] }); }
|
|
15204
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: RolesComponent, isStandalone: false, selector: "spa-roles", ngImport: i0, template: "<h4> Roles </h4>\r\n<hr />\r\n\r\n<div class=\"container-fluid mb-5\">\r\n\r\n <div class=\"d-flex justify-content-between mb-2\">\r\n\r\n <div >\r\n <button id=\"btnNewRole\" mat-raised-button color=\"primary\" (click)=\"addRole()\">New Role</button>\r\n </div>\r\n\r\n <div class=\"d-flex justify-content-end\">\r\n <button id=\"btnRefresh\" mat-icon-button color=\"primary\" (click)=\"refresh()\" matTooltip=\"refresh data\" matTooltipPosition=\"right\"><mat-icon >refresh</mat-icon></button>\r\n </div>\r\n\r\n </div>\r\n\r\n\r\n <div class=\"row mt-2 mb-1\" *ngFor=\"let role of roles\">\r\n\r\n <mat-card class=\"mat-elevation-z8\" style=\"width:100%\">\r\n\r\n <div class=\"d-flex justify-content-between align-items-center\">\r\n\r\n <label style=\"font-size: 16px;\">{{role.roleName}}</label>\r\n\r\n <button mat-icon-button color=\"primary\" matTooltip=\"Rename Role\" (click)=\"renameRole(role)\">\r\n <mat-icon>edit</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <hr style=\"margin-top: 0px;\">\r\n\r\n <div class=\"tin-row\" style=\" font-size:12px;\">\r\n\r\n\r\n <div class=\"tin-row\" *ngFor=\"let capItem of appConfig.capItems\">\r\n\r\n <!-- Main item-->\r\n <mat-checkbox *ngIf=\"capItem.isBool || capItem.capSubItems\"\r\n color=\"primary\" style=\"min-width: 100px;\" [(ngModel)]=\"role[capItem.name]\" (ngModelChange)=\"onCapItemChange(capItem, $event, role)\">\r\n {{capItem.display}}\r\n <span *ngIf=\"!role[capItem.name] && hasSubItemsAccess(capItem, role)\" class=\"asterisk\" style=\"color: red;\">*</span>\r\n </mat-checkbox>\r\n\r\n <spa-select\r\n *ngIf=\"!capItem.isBool && !capItem.capSubItems\"\r\n [options]=\"roleAccessOptions\"\r\n optionDisplay=\"name\"\r\n optionValue=\"value\"\r\n [display]=\"capItem.display\"\r\n [(value)]=\"role[capItem.name]\"\r\n width=\"150px\" \r\n style=\"font-size: 12px;\">\r\n </spa-select>\r\n\r\n\r\n <ng-container *ngIf=\"capItem.capSubItems && role[capItem.name]\">\r\n\r\n <div class=\"tin-row\" *ngFor=\"let capSubItem of capItem.capSubItems\">\r\n\r\n\r\n <!-- Sub Item -->\r\n <mat-checkbox *ngIf=\"capSubItem.isBool\"\r\n color=\"primary\" style=\"min-width: 100px;\" [(ngModel)]=\"role[capSubItem.name]\">\r\n {{capSubItem.display}}\r\n </mat-checkbox>\r\n\r\n <spa-select\r\n *ngIf=\"!capSubItem.isBool\"\r\n [options]=\"roleAccessOptions\"\r\n optionDisplay=\"name\"\r\n optionValue=\"value\"\r\n [display]=\"capSubItem.display\"\r\n [(value)]=\"role[capSubItem.name]\"\r\n width=\"150px\"\r\n style=\"font-size: 12px;\">\r\n </spa-select>\r\n\r\n <ng-container *ngIf=\"capSubItem.capSubItems\">\r\n\r\n <div class=\"tin-row\" *ngFor=\"let capSubSubItem of capSubItem.capSubItems\">\r\n\r\n <!-- Sub Sub Items -->\r\n <mat-checkbox *ngIf=\"capSubSubItem.isBool\"\r\n color=\"primary\" style=\"min-width: 100px;\" [(ngModel)]=\"role[capSubSubItem.name]\">\r\n {{capSubSubItem.display}}\r\n </mat-checkbox>\r\n\r\n <spa-select\r\n *ngIf=\"!capSubSubItem.isBool\"\r\n [options]=\"roleAccessOptions\"\r\n optionDisplay=\"name\"\r\n optionValue=\"value\"\r\n [display]=\"capSubSubItem.display\"\r\n [(value)]=\"role[capSubSubItem.name]\"\r\n width=\"150px\" \r\n style=\"font-size: 12px;\">\r\n </spa-select>\r\n\r\n </div>\r\n\r\n </ng-container>\r\n\r\n\r\n\r\n </div>\r\n\r\n </ng-container>\r\n\r\n </div>\r\n\r\n </div>\r\n\r\n\r\n <mat-card-actions>\r\n\r\n <button mat-raised-button color=\"primary\" (click)=\"updateRole(role)\" style=\"margin-right:10px;\">\r\n <mat-icon>done_all</mat-icon>\r\n Update\r\n </button>\r\n\r\n <button mat-raised-button (click)=\"deleteRole(role)\" style=\"margin-right:10px\">\r\n <mat-icon>delete</mat-icon>\r\n Delete\r\n </button>\r\n\r\n </mat-card-actions>\r\n\r\n </mat-card>\r\n\r\n </div>\r\n\r\n <hr style=\"margin-top: 50px;\" />\r\n\r\n\r\n</div>\r\n\r\n", styles: [""], dependencies: [{ kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i3$1.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { 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: i5$1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i6$2.MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "directive", type: i6$2.MatCardActions, selector: "mat-card-actions", inputs: ["align"], exportAs: ["matCardActions"] }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: SelectComponent, selector: "spa-select", inputs: ["detailsConfig"] }] }); }
|
|
14831
15205
|
}
|
|
14832
15206
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: RolesComponent, decorators: [{
|
|
14833
15207
|
type: Component,
|
|
14834
|
-
args: [{ selector: "spa-roles", standalone: false, template: "<h4> Roles </h4>\r\n<hr />\r\n\r\n<div class=\"container-fluid mb-5\">\r\n\r\n <div class=\"d-flex justify-content-between mb-2\">\r\n\r\n <div >\r\n <button id=\"btnNewRole\" mat-raised-button color=\"primary\" (click)=\"addRole()\">New Role</button>\r\n </div>\r\n\r\n <div class=\"d-flex justify-content-end\">\r\n <button id=\"btnRefresh\" mat-icon-button color=\"primary\" (click)=\"refresh()\" matTooltip=\"refresh data\" matTooltipPosition=\"right\"><mat-icon >refresh</mat-icon></button>\r\n </div>\r\n\r\n </div>\r\n\r\n\r\n <div class=\"row mt-2 mb-1\" *ngFor=\"let role of roles\">\r\n\r\n <mat-card class=\"mat-elevation-z8\" style=\"width:100%\">\r\n\r\n <div class=\"d-flex justify-content-between align-items-center\">\r\n\r\n <label style=\"font-size: 16px;\">{{role.roleName}}</label>\r\n\r\n <button mat-icon-button color=\"primary\" matTooltip=\"Rename Role\" (click)=\"renameRole(role)\">\r\n <mat-icon>edit</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <hr style=\"margin-top: 0px;\">\r\n\r\n <div class=\"tin-row\" style=\" font-size:12px;\">\r\n\r\n\r\n <div class=\"tin-row\" *ngFor=\"let capItem of appConfig.capItems\">\r\n\r\n <!-- Main item-->\r\n <mat-checkbox *ngIf=\"capItem.isBool || capItem.capSubItems\"\r\n color=\"primary\" style=\"min-width: 100px;\" [(ngModel)]=\"role[capItem.name]\" (ngModelChange)=\"onCapItemChange(capItem, $event, role)\">\r\n {{capItem.display}}\r\n <span *ngIf=\"!role[capItem.name] && hasSubItemsAccess(capItem, role)\" class=\"asterisk\" style=\"color: red;\">*</span>\r\n </mat-checkbox>\r\n\r\n <spa-select\r\n *ngIf=\"!capItem.isBool && !capItem.capSubItems\"\r\n [options]=\"roleAccessOptions\"\r\n optionDisplay=\"name\"\r\n optionValue=\"value\"\r\n [display]=\"capItem.display\"\r\n [(value)]=\"role[capItem.name]\"\r\n width=\"150px\" \r\n style=\"font-size: 12px;\">\r\n </spa-select>\r\n\r\n\r\n <ng-container *ngIf=\"capItem.capSubItems && role[capItem.name]\">\r\n\r\n <div class=\"tin-row\" *ngFor=\"let capSubItem of capItem.capSubItems\">\r\n\r\n\r\n <!-- Sub Item -->\r\n <mat-checkbox *ngIf=\"capSubItem.isBool\"\r\n color=\"primary\" style=\"min-width: 100px;\" [(ngModel)]=\"role[capSubItem.name]\">\r\n {{capSubItem.display}}\r\n </mat-checkbox>\r\n\r\n <spa-select\r\n *ngIf=\"!capSubItem.isBool\"\r\n [options]=\"roleAccessOptions\"\r\n optionDisplay=\"name\"\r\n optionValue=\"value\"\r\n [display]=\"capSubItem.display\"\r\n [(value)]=\"role[capSubItem.name]\"\r\n width=\"150px\"\r\n style=\"font-size: 12px;\">\r\n </spa-select>\r\n\r\n <ng-container *ngIf=\"capSubItem.capSubItems\">\r\n\r\n <div class=\"tin-row\" *ngFor=\"let capSubSubItem of capSubItem.capSubItems\">\r\n\r\n <!-- Sub Sub Items -->\r\n <mat-checkbox *ngIf=\"capSubSubItem.isBool\"\r\n color=\"primary\" style=\"min-width: 100px;\" [(ngModel)]=\"role[capSubSubItem.name]\">\r\n {{capSubSubItem.display}}\r\n </mat-checkbox>\r\n\r\n <spa-select\r\n *ngIf=\"!capSubSubItem.isBool\"\r\n [options]=\"roleAccessOptions\"\r\n optionDisplay=\"name\"\r\n optionValue=\"value\"\r\n [display]=\"capSubSubItem.display\"\r\n [(value)]=\"role[capSubSubItem.name]\"\r\n width=\"150px\" \r\n style=\"font-size: 12px;\">\r\n </spa-select>\r\n\r\n </div>\r\n\r\n </ng-container>\r\n\r\n\r\n\r\n </div>\r\n\r\n </ng-container>\r\n\r\n </div>\r\n\r\n </div>\r\n\r\n\r\n <mat-card-actions>\r\n\r\n <button mat-raised-button color=\"primary\" (click)=\"updateRole(role)\" style=\"margin-right:10px;\">\r\n <mat-icon>done_all</mat-icon>\r\n Update\r\n </button>\r\n\r\n <button mat-raised-button (click)=\"deleteRole(role)\" style=\"margin-right:10px\">\r\n <mat-icon>delete</mat-icon>\r\n Delete\r\n </button>\r\n\r\n </mat-card-actions>\r\n\r\n </mat-card>\r\n\r\n </div>\r\n\r\n\r\n</div>\r\n\r\n" }]
|
|
15208
|
+
args: [{ selector: "spa-roles", standalone: false, template: "<h4> Roles </h4>\r\n<hr />\r\n\r\n<div class=\"container-fluid mb-5\">\r\n\r\n <div class=\"d-flex justify-content-between mb-2\">\r\n\r\n <div >\r\n <button id=\"btnNewRole\" mat-raised-button color=\"primary\" (click)=\"addRole()\">New Role</button>\r\n </div>\r\n\r\n <div class=\"d-flex justify-content-end\">\r\n <button id=\"btnRefresh\" mat-icon-button color=\"primary\" (click)=\"refresh()\" matTooltip=\"refresh data\" matTooltipPosition=\"right\"><mat-icon >refresh</mat-icon></button>\r\n </div>\r\n\r\n </div>\r\n\r\n\r\n <div class=\"row mt-2 mb-1\" *ngFor=\"let role of roles\">\r\n\r\n <mat-card class=\"mat-elevation-z8\" style=\"width:100%\">\r\n\r\n <div class=\"d-flex justify-content-between align-items-center\">\r\n\r\n <label style=\"font-size: 16px;\">{{role.roleName}}</label>\r\n\r\n <button mat-icon-button color=\"primary\" matTooltip=\"Rename Role\" (click)=\"renameRole(role)\">\r\n <mat-icon>edit</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <hr style=\"margin-top: 0px;\">\r\n\r\n <div class=\"tin-row\" style=\" font-size:12px;\">\r\n\r\n\r\n <div class=\"tin-row\" *ngFor=\"let capItem of appConfig.capItems\">\r\n\r\n <!-- Main item-->\r\n <mat-checkbox *ngIf=\"capItem.isBool || capItem.capSubItems\"\r\n color=\"primary\" style=\"min-width: 100px;\" [(ngModel)]=\"role[capItem.name]\" (ngModelChange)=\"onCapItemChange(capItem, $event, role)\">\r\n {{capItem.display}}\r\n <span *ngIf=\"!role[capItem.name] && hasSubItemsAccess(capItem, role)\" class=\"asterisk\" style=\"color: red;\">*</span>\r\n </mat-checkbox>\r\n\r\n <spa-select\r\n *ngIf=\"!capItem.isBool && !capItem.capSubItems\"\r\n [options]=\"roleAccessOptions\"\r\n optionDisplay=\"name\"\r\n optionValue=\"value\"\r\n [display]=\"capItem.display\"\r\n [(value)]=\"role[capItem.name]\"\r\n width=\"150px\" \r\n style=\"font-size: 12px;\">\r\n </spa-select>\r\n\r\n\r\n <ng-container *ngIf=\"capItem.capSubItems && role[capItem.name]\">\r\n\r\n <div class=\"tin-row\" *ngFor=\"let capSubItem of capItem.capSubItems\">\r\n\r\n\r\n <!-- Sub Item -->\r\n <mat-checkbox *ngIf=\"capSubItem.isBool\"\r\n color=\"primary\" style=\"min-width: 100px;\" [(ngModel)]=\"role[capSubItem.name]\">\r\n {{capSubItem.display}}\r\n </mat-checkbox>\r\n\r\n <spa-select\r\n *ngIf=\"!capSubItem.isBool\"\r\n [options]=\"roleAccessOptions\"\r\n optionDisplay=\"name\"\r\n optionValue=\"value\"\r\n [display]=\"capSubItem.display\"\r\n [(value)]=\"role[capSubItem.name]\"\r\n width=\"150px\"\r\n style=\"font-size: 12px;\">\r\n </spa-select>\r\n\r\n <ng-container *ngIf=\"capSubItem.capSubItems\">\r\n\r\n <div class=\"tin-row\" *ngFor=\"let capSubSubItem of capSubItem.capSubItems\">\r\n\r\n <!-- Sub Sub Items -->\r\n <mat-checkbox *ngIf=\"capSubSubItem.isBool\"\r\n color=\"primary\" style=\"min-width: 100px;\" [(ngModel)]=\"role[capSubSubItem.name]\">\r\n {{capSubSubItem.display}}\r\n </mat-checkbox>\r\n\r\n <spa-select\r\n *ngIf=\"!capSubSubItem.isBool\"\r\n [options]=\"roleAccessOptions\"\r\n optionDisplay=\"name\"\r\n optionValue=\"value\"\r\n [display]=\"capSubSubItem.display\"\r\n [(value)]=\"role[capSubSubItem.name]\"\r\n width=\"150px\" \r\n style=\"font-size: 12px;\">\r\n </spa-select>\r\n\r\n </div>\r\n\r\n </ng-container>\r\n\r\n\r\n\r\n </div>\r\n\r\n </ng-container>\r\n\r\n </div>\r\n\r\n </div>\r\n\r\n\r\n <mat-card-actions>\r\n\r\n <button mat-raised-button color=\"primary\" (click)=\"updateRole(role)\" style=\"margin-right:10px;\">\r\n <mat-icon>done_all</mat-icon>\r\n Update\r\n </button>\r\n\r\n <button mat-raised-button (click)=\"deleteRole(role)\" style=\"margin-right:10px\">\r\n <mat-icon>delete</mat-icon>\r\n Delete\r\n </button>\r\n\r\n </mat-card-actions>\r\n\r\n </mat-card>\r\n\r\n </div>\r\n\r\n <hr style=\"margin-top: 50px;\" />\r\n\r\n\r\n</div>\r\n\r\n" }]
|
|
14835
15209
|
}], ctorParameters: () => [{ type: HttpService }, { type: i1$2.Router }, { type: AuthService }, { type: DataServiceLib }, { type: DialogService }, { type: i5.MatDialog }, { type: MessageService }] });
|
|
14836
15210
|
|
|
14837
15211
|
class CreateAccountComponent {
|
|
@@ -16039,6 +16413,27 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImpo
|
|
|
16039
16413
|
}]
|
|
16040
16414
|
}] });
|
|
16041
16415
|
|
|
16416
|
+
// ServiceItems component for managing service items
|
|
16417
|
+
class ServiceItemsComponent {
|
|
16418
|
+
constructor() {
|
|
16419
|
+
this.inventoryService = inject(InventoryService);
|
|
16420
|
+
this.pageConfig = {
|
|
16421
|
+
title: 'Services',
|
|
16422
|
+
tableConfig: this.inventoryService.serviceItemsTableConfig
|
|
16423
|
+
};
|
|
16424
|
+
}
|
|
16425
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: ServiceItemsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
16426
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: ServiceItemsComponent, isStandalone: false, selector: "spa-service-items", 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"] }] }); }
|
|
16427
|
+
}
|
|
16428
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: ServiceItemsComponent, decorators: [{
|
|
16429
|
+
type: Component,
|
|
16430
|
+
args: [{
|
|
16431
|
+
selector: 'spa-service-items',
|
|
16432
|
+
standalone: false,
|
|
16433
|
+
template: '<spa-page [config]="pageConfig"></spa-page>'
|
|
16434
|
+
}]
|
|
16435
|
+
}] });
|
|
16436
|
+
|
|
16042
16437
|
class GptCachesComponent {
|
|
16043
16438
|
constructor() {
|
|
16044
16439
|
this.dataService = inject(DataServiceLib);
|
|
@@ -16099,6 +16494,7 @@ const routes$1 = [
|
|
|
16099
16494
|
{ path: "loans", component: LoansComponent },
|
|
16100
16495
|
{ path: "loans-payments", component: LoanPaymentsComponent },
|
|
16101
16496
|
{ path: "inventory-products", component: ProductsComponent },
|
|
16497
|
+
{ path: "inventory-service-items", component: ServiceItemsComponent }, // Added: Service items route
|
|
16102
16498
|
{ path: "inventory-items", component: InventoryItemsComponent },
|
|
16103
16499
|
{ path: "purchase-orders", component: PurchaseOrdersComponent },
|
|
16104
16500
|
{ path: "inventory-receipts", component: InventoryReceiptsComponent },
|
|
@@ -16155,7 +16551,8 @@ class AdminModule {
|
|
|
16155
16551
|
InventoryStockComponent,
|
|
16156
16552
|
ProductionRecipesComponent,
|
|
16157
16553
|
ProductionOrdersComponent,
|
|
16158
|
-
BundleProductsComponent // Added: Bundle products component
|
|
16554
|
+
BundleProductsComponent, // Added: Bundle products component
|
|
16555
|
+
ServiceItemsComponent // Added: Service items component
|
|
16159
16556
|
], imports: [CommonModule,
|
|
16160
16557
|
AdminRoutingModule,
|
|
16161
16558
|
SpaAdminModule] }); }
|
|
@@ -16191,7 +16588,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImpo
|
|
|
16191
16588
|
InventoryStockComponent,
|
|
16192
16589
|
ProductionRecipesComponent,
|
|
16193
16590
|
ProductionOrdersComponent,
|
|
16194
|
-
BundleProductsComponent // Added: Bundle products component
|
|
16591
|
+
BundleProductsComponent, // Added: Bundle products component
|
|
16592
|
+
ServiceItemsComponent // Added: Service items component
|
|
16195
16593
|
],
|
|
16196
16594
|
imports: [
|
|
16197
16595
|
CommonModule,
|
|
@@ -16248,5 +16646,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImpo
|
|
|
16248
16646
|
* Generated bundle index. Do not edit.
|
|
16249
16647
|
*/
|
|
16250
16648
|
|
|
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 };
|
|
16649
|
+
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, InvoiceItemType, 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, TransactionTiming, UnitOfMeasure, User, UserModule, UsersComponent, ViewerComponent, WelcomeComponent, authGuard, dialogOptions, loginConfig, messageDialog, viewerDialog };
|
|
16252
16650
|
//# sourceMappingURL=tin-spa.mjs.map
|