tin-spa 20.3.6 → 20.3.8

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.
@@ -1529,6 +1529,7 @@ class DataServiceLib {
1529
1529
  this.capAccounts = new CapItem;
1530
1530
  this.capAggregates = new CapItem; // Added: Capability for aggregates view
1531
1531
  this.capAging = new CapItem; // Added: Capability for aging report
1532
+ this.capTaxRates = new CapItem; // Changed: Capability for tax rates management
1532
1533
  this.capTransactionTypes = new CapItem;
1533
1534
  this.capTransactions = new CapItem;
1534
1535
  this.capAccountingInvoices = new CapItem;
@@ -2224,7 +2225,7 @@ class DataServiceLib {
2224
2225
  this.capAccounting.name = "cap27";
2225
2226
  this.capAccounting.display = "Accounting";
2226
2227
  this.capAccounting.icon = "account_balance";
2227
- this.capAccounting.capSubItems = [this.capAccounts, this.capTransactions, this.capAggregates, this.capTransactionTypes,];
2228
+ this.capAccounting.capSubItems = [this.capAccounts, this.capTransactions, this.capAggregates, this.capTransactionTypes, this.capTaxRates,]; // Changed: Added Tax Rates to Accounting submenu
2228
2229
  this.capAccounts.name = "cap52"; // Changed: Fixed conflict with capBrands (was cap14)
2229
2230
  this.capAccounts.display = "Accounts";
2230
2231
  this.capAccounts.link = "home/admin/accounting-accounts";
@@ -2233,6 +2234,10 @@ class DataServiceLib {
2233
2234
  this.capAggregates.display = "Aggregates";
2234
2235
  this.capAggregates.link = "home/admin/accounting-aggregates";
2235
2236
  this.capAggregates.icon = "bar_chart";
2237
+ this.capTaxRates.name = "cap60"; // Changed: Tax rates capability
2238
+ this.capTaxRates.display = "Tax Rates";
2239
+ this.capTaxRates.link = "home/admin/accounting-tax-rates";
2240
+ this.capTaxRates.icon = "percent";
2236
2241
  this.capTransactionTypes.name = "cap28";
2237
2242
  this.capTransactionTypes.display = "Transaction Types";
2238
2243
  this.capTransactionTypes.link = "home/admin/accounting-transaction-types";
@@ -2823,6 +2828,8 @@ class AccountingService {
2823
2828
  { name: 'paymentTerms', type: 'select', alias: 'Payment Terms', required: true, defaultValue: 30, loadAction: { url: 'invoices/list/payment-terms' } },
2824
2829
  { name: 'dueDate', type: 'date', alias: 'Due Date', readonly: true, hideOnCreate: true },
2825
2830
  { name: 'status', type: 'select', hideOnCreate: true, readonly: true, loadAction: { url: 'invoices/list/statuses' } },
2831
+ { name: 'subTotal', type: 'money', alias: 'Sub Total', readonly: true, hideOnCreate: true }, // Changed: Show SubTotal (before tax)
2832
+ { name: 'taxTotal', type: 'money', alias: 'VAT', readonly: true, hideOnCreate: true }, // Changed: Show TaxTotal (VAT amount)
2826
2833
  { name: 'totalDisplay', type: 'label', alias: 'Total', format: 'text', readonly: true, hideOnCreate: true },
2827
2834
  { name: 'paidAmount', type: 'label', alias: 'Paid', readonly: true, hideOnCreate: true },
2828
2835
  { name: 'outstandingAmount', type: 'label', alias: 'Outstanding', readonly: true, hideOnCreate: true }
@@ -2856,11 +2863,20 @@ class AccountingService {
2856
2863
  { name: 'description', type: 'text', required: true, alias: 'Description', span: true }, // Changed: Description field (backend will populate for Product/Service)
2857
2864
  { name: 'quantity', type: 'number', required: true, defaultValue: 1 },
2858
2865
  { name: 'unitPrice', type: 'money', alias: 'Unit Price', required: true }, // Changed: Unit price field (backend will populate for Product/Service)
2859
- { name: 'poNumber', type: 'text', alias: 'PO Number', hint: 'Purchase Order Number' }, // Added: PO Number for invoice bundling
2866
+ { name: 'poNumber', type: 'text', alias: 'PO Number', }, // Added: PO Number for invoice bundling
2860
2867
  ],
2861
2868
  loadAction: { url: 'invoiceitems/id/{invoiceItemID}' },
2862
2869
  heroField: 'invoiceItemID',
2863
2870
  };
2871
+ this.invoiceItemEditButton = { name: 'edit', dialog: true, action: { url: 'invoiceitems?action=edit', method: 'post' }, disabled: x => x.status != InvoiceStatus.Draft };
2872
+ // Invoice item details dialog config
2873
+ this.invoiceItemDetailsConfig = {
2874
+ formConfig: this.invoiceItemsFormConfig,
2875
+ heroField: 'invoiceItemID',
2876
+ buttons: [
2877
+ this.invoiceItemEditButton
2878
+ ]
2879
+ };
2864
2880
  // Added: Move item form configuration - select destination invoice
2865
2881
  this.invoiceMoveItemFormConfig = {
2866
2882
  title: 'Move Item',
@@ -2890,15 +2906,17 @@ class AccountingService {
2890
2906
  causeFormRefresh: true,
2891
2907
  columns: [
2892
2908
  { name: 'invoiceItemID', type: 'number', alias: 'ID', hiddenCondition: () => true },
2893
- { name: 'description', type: 'text', alias: 'Description' },
2909
+ { name: 'description', type: 'chip', alias: 'Description', detailsConfig: this.invoiceItemDetailsConfig },
2894
2910
  { name: 'poNumber', type: 'text', alias: 'PO#' }, // Added: Purchase Order Number column
2895
2911
  { name: 'quantity', type: 'number', alias: 'Qty' },
2896
2912
  { name: 'unitPrice', type: 'money', alias: 'Unit Price' },
2897
- { name: 'amount', type: 'money', alias: 'Amount' }
2913
+ { name: 'amount', type: 'money', alias: 'Amount' },
2914
+ { name: 'taxAmount', type: 'money', alias: 'Tax' }, // Changed: Show per-item tax amount
2915
+ { name: 'lineTotal', type: 'money', alias: 'Total' } // Changed: Computed line total (amount + tax)
2898
2916
  ],
2899
2917
  buttons: [
2900
2918
  { name: 'create', display: 'Add Item', dialog: true, action: { url: 'invoiceitems?action=create', method: 'post' }, disabled: x => x.status != InvoiceStatus.Draft },
2901
- { name: 'edit', dialog: true, action: { url: 'invoiceitems?action=edit', method: 'post' }, disabled: x => x.status != InvoiceStatus.Draft },
2919
+ this.invoiceItemEditButton,
2902
2920
  { name: 'move', display: 'Move', dialog: true, icon: { name: 'swap_horiz' }, detailsConfig: this.invoiceMoveItemDetailsConfig, disabled: x => x.status != InvoiceStatus.Draft }, // Added: Move item button
2903
2921
  { 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 },
2904
2922
  ],
@@ -2913,7 +2931,13 @@ class AccountingService {
2913
2931
  this.invoiceDiscardButton = { name: 'discard', inDialog: true, display: 'Discard', icon: { name: 'close', color: 'red' },
2914
2932
  action: { url: 'invoices?action=discard', method: 'post', successMessage: 'Discarded' },
2915
2933
  confirm: { message: 'Invoice will be marked as cancelled?' },
2916
- visible: x => x.status == InvoiceStatus.Draft || x.status == InvoiceStatus.Submitted // Changed: Only allow on Draft and Submitted
2934
+ visible: x => x.status == InvoiceStatus.Draft // Changed: Only allow on Draft (use Return for Submitted)
2935
+ };
2936
+ // Changed: Return button — reverts submitted invoice back to draft, reverses accounting transactions
2937
+ this.invoiceReturnButton = { name: 'return', inDialog: true, display: 'Return', icon: { name: 'undo', color: 'orange' },
2938
+ action: { url: 'invoices?action=return', method: 'post', successMessage: 'Returned to Draft' },
2939
+ confirm: { message: 'Return invoice to draft? This will reverse the receivable and VAT transactions.' },
2940
+ visible: x => x.status == InvoiceStatus.Submitted
2917
2941
  };
2918
2942
  this.invoiceSubmitButton = { name: 'submit', inDialog: true, display: 'Submit', icon: { name: 'send', },
2919
2943
  action: { url: 'invoices?action=submit', method: 'post', successMessage: 'Submitted' },
@@ -2934,6 +2958,7 @@ class AccountingService {
2934
2958
  buttons: [
2935
2959
  this.invoiceSubmitButton,
2936
2960
  this.invoiceRecordPaymentButton,
2961
+ this.invoiceReturnButton, // Changed: Return submitted invoice to draft
2937
2962
  this.invoiceDiscardButton,
2938
2963
  this.invoiceEditButton
2939
2964
  ]
@@ -2984,6 +3009,7 @@ class AccountingService {
2984
3009
  // { name: 'view', dialog: true, detailsConfig: this.invoiceDetailsDialogConfig },
2985
3010
  this.invoiceSubmitButton,
2986
3011
  this.invoiceRecordPaymentButton,
3012
+ this.invoiceReturnButton, // Changed: Return submitted invoice to draft
2987
3013
  // this.invoicePayButton,
2988
3014
  this.invoiceDiscardButton,
2989
3015
  // this.invoiceEditButton,
@@ -3098,6 +3124,47 @@ class AccountingService {
3098
3124
  formConfig: this.outstandingInvoicesFormConfig,
3099
3125
  tileConfig: this.outstandingInvoicesTileConfig
3100
3126
  };
3127
+ //--------------------------Tax Rates-------------------------
3128
+ // Changed: Tax Rate form configuration for VAT management
3129
+ this.taxRateFormConfig = {
3130
+ title: 'Tax Rate',
3131
+ includeAudit: true,
3132
+ fields: [
3133
+ { name: 'name', type: 'text', required: true, alias: 'Name' },
3134
+ { name: 'rate', type: 'number', required: true, alias: 'Rate (%)' },
3135
+ { name: 'isDefault', type: 'checkbox', alias: 'Default Rate' },
3136
+ { name: 'isActive', type: 'checkbox', alias: 'Active', defaultValue: true }
3137
+ ],
3138
+ loadAction: { url: 'taxrates/id' },
3139
+ heroField: 'taxRateID'
3140
+ };
3141
+ this.taxRateCreateButton = { name: 'create', display: 'Create Tax Rate', dialog: true, action: { url: 'taxrates?action=create', method: 'post' } };
3142
+ this.taxRateEditButton = { name: 'edit', dialog: true, action: { url: 'taxrates?action=edit', method: 'post' } };
3143
+ this.taxRateDeleteButton = { name: 'delete', dialog: true, action: { url: 'taxrates?action=delete', method: 'post' } };
3144
+ this.taxRateDetailsConfig = {
3145
+ formConfig: this.taxRateFormConfig,
3146
+ heroField: 'taxRateID',
3147
+ buttons: [this.taxRateCreateButton, this.taxRateEditButton, this.taxRateDeleteButton]
3148
+ };
3149
+ this.taxRatesTableConfig = {
3150
+ showFilter: true,
3151
+ flatButtons: true,
3152
+ minColumns: ['name', 'rate', 'isActive'],
3153
+ columns: [
3154
+ { name: 'name', type: 'text', alias: 'Name' },
3155
+ { name: 'rate', type: 'number', alias: 'Rate (%)' },
3156
+ { name: 'isDefault', type: 'checkbox', alias: 'Default' },
3157
+ { name: 'isActive', type: 'checkbox', alias: 'Active' }
3158
+ ],
3159
+ buttons: [
3160
+ this.taxRateCreateButton,
3161
+ { name: 'view', dialog: true, detailsConfig: this.taxRateDetailsConfig },
3162
+ this.taxRateEditButton,
3163
+ this.taxRateDeleteButton
3164
+ ],
3165
+ loadAction: { url: 'taxrates/all/x' },
3166
+ formConfig: this.taxRateFormConfig
3167
+ };
3101
3168
  //--------------------------Transaction Types-------------------------
3102
3169
  this.transactionTypeFormConfig = {
3103
3170
  security: { allow: [this.dataService.capTransactionTypes] },
@@ -3807,6 +3874,8 @@ class InventoryService {
3807
3874
  { 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
3808
3875
  { name: 'pricingInfo', type: 'section', alias: 'Pricing' },
3809
3876
  { name: 'sellingPrice', type: 'money', required: true, alias: 'Selling Price', section: 'pricingInfo', infoMessage: 'Default selling price per unit' },
3877
+ { name: 'taxRateID', type: 'select', alias: 'Tax Rate', nullable: true, section: 'pricingInfo', loadAction: { url: 'products/list/tax-rates' }, infoMessage: 'VAT rate applied to this product' }, // Changed: Tax rate selector
3878
+ { name: 'isTaxInclusive', type: 'checkbox', alias: 'Price includes Tax', section: 'pricingInfo', infoMessage: 'If checked, the selling price already includes tax' }, // Changed: Tax-inclusive flag
3810
3879
  { name: 'inventoryInfo', type: 'section', alias: 'Inventory Settings', collapsedCondition: x => x.productID },
3811
3880
  { 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
3812
3881
  { 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
@@ -3834,8 +3903,11 @@ class InventoryService {
3834
3903
  { name: 'brandName', type: 'text', alias: 'Brand' },
3835
3904
  { name: 'currentInventoryLevel', type: 'number', alias: 'Current Stock', icon: { name: 'warning', color: 'orange', condition: (x) => x.isLowInventory } }, // Changed: Removed separate columns, added low stock icon to current stock
3836
3905
  { name: 'minimumInventoryLevel', type: 'number', alias: 'Min Stock' },
3837
- { name: 'sellingPrice', type: 'money', }
3838
- ], // Changed: Removed isSerialized, baseUnit, currentAverageCost, isLowInventory columns
3906
+ { name: 'sellingPrice', type: 'money' },
3907
+ { name: 'taxRateName', type: 'text', alias: 'Tax Rate' }, // Changed: Tax rate display with N/A fallback
3908
+ { name: 'priceExclTax', type: 'text', alias: 'Price (Excl)' }, // Changed: Computed price excluding tax
3909
+ { name: 'priceInclTax', type: 'text', alias: 'Price (Incl)' }, // Changed: Computed price including tax
3910
+ ],
3839
3911
  buttons: [
3840
3912
  this.productCreateButton,
3841
3913
  this.productViewButton,
@@ -3951,6 +4023,8 @@ class InventoryService {
3951
4023
  { name: 'isActive', type: 'checkbox', alias: 'Active', defaultValue: true },
3952
4024
  { name: 'description', type: 'text', alias: 'Description' },
3953
4025
  { name: 'unitPrice', type: 'money', required: true, alias: 'Unit Price' },
4026
+ { name: 'taxRateID', type: 'select', alias: 'Tax Rate', nullable: true, loadAction: { url: 'serviceitems/list/tax-rates' }, infoMessage: 'VAT rate applied to this service' }, // Changed: Tax rate selector
4027
+ { name: 'isTaxInclusive', type: 'checkbox', alias: 'Price includes Tax', infoMessage: 'If checked, the unit price already includes tax' }, // Changed: Tax-inclusive flag
3954
4028
  ],
3955
4029
  loadAction: { url: 'serviceitems/id' },
3956
4030
  heroField: 'serviceItemID'
@@ -3971,6 +4045,9 @@ class InventoryService {
3971
4045
  { name: 'name', type: 'text', alias: 'Service Name' },
3972
4046
  { name: 'description', type: 'text', alias: 'Description' },
3973
4047
  { name: 'unitPrice', type: 'money', alias: 'Unit Price' },
4048
+ { name: 'taxRateName', type: 'text', alias: 'Tax Rate' }, // Changed: Tax rate display with N/A fallback
4049
+ { name: 'priceExclTax', type: 'text', alias: 'Price (Excl)' }, // Changed: Computed price excluding tax
4050
+ { name: 'priceInclTax', type: 'text', alias: 'Price (Incl)' }, // Changed: Computed price including tax
3974
4051
  { name: 'statusName', type: 'text', alias: 'Status' }
3975
4052
  ],
3976
4053
  buttons: [
@@ -4778,6 +4855,8 @@ class InventoryService {
4778
4855
  { name: 'check_circle', color: '#4CAF50', condition: (x) => x.paymentStatus === 1, tip: 'Paid' } // Changed: Use status enum
4779
4856
  ]
4780
4857
  },
4858
+ { name: 'subTotal', type: 'money', alias: 'Sub Total' }, // Changed: Show subtotal before tax
4859
+ { name: 'taxAmount', type: 'money', alias: 'Tax' }, // Changed: Show tax amount
4781
4860
  { name: 'totalAmount', type: 'money', alias: 'Total' }
4782
4861
  ],
4783
4862
  buttons: [
@@ -16740,6 +16819,28 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImpo
16740
16819
  }]
16741
16820
  }] });
16742
16821
 
16822
+ // Tax Rates management page - CRUD for VAT tax rates
16823
+ class TaxRatesComponent {
16824
+ constructor() {
16825
+ this.accountingService = inject(AccountingService);
16826
+ this.pageConfig = {
16827
+ title: 'Tax Rates',
16828
+ tableConfig: this.accountingService.taxRatesTableConfig
16829
+ };
16830
+ }
16831
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: TaxRatesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
16832
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: TaxRatesComponent, isStandalone: true, selector: "spa-tax-rates", 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"] }] }); }
16833
+ }
16834
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: TaxRatesComponent, decorators: [{
16835
+ type: Component,
16836
+ args: [{
16837
+ selector: 'spa-tax-rates',
16838
+ standalone: true,
16839
+ imports: [TinSpaModule],
16840
+ template: '<spa-page [config]="pageConfig"></spa-page>'
16841
+ }]
16842
+ }] });
16843
+
16743
16844
  class GptCachesComponent {
16744
16845
  constructor() {
16745
16846
  this.dataService = inject(DataServiceLib);
@@ -16796,6 +16897,7 @@ const routes$1 = [
16796
16897
  { path: "accounting-invoices", component: InvoicesComponent },
16797
16898
  { path: "accounting-aging", component: AgingComponent }, // Added: Aging route
16798
16899
  { path: "accounting-outstanding-invoices", component: OutstandingInvoicesComponent },
16900
+ { path: "accounting-tax-rates", component: TaxRatesComponent }, // Changed: Tax rates management route
16799
16901
  { path: "loans-products", component: LoanProductsComponent },
16800
16902
  { path: "loans", component: LoansComponent },
16801
16903
  { path: "loans-payments", component: LoanPaymentsComponent },