@apptimate/core-lib 6.6.0 → 6.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apptimate/core-lib",
3
- "version": "6.6.0",
3
+ "version": "6.8.0",
4
4
  "main": "src/index.ts",
5
5
  "types": "src/index.ts",
6
6
  "publishConfig": {
@@ -53,3 +53,8 @@ export async function getChartOfAccountsLookupShared(type?: string): Promise<IAp
53
53
  const response = await sendRequest({ url: `${BASE}/chart-of-accounts/lookup`, method: "GET", params });
54
54
  return response.responseData as IApiResponse;
55
55
  }
56
+
57
+ export async function getDirectTransactionsShared(params?: any): Promise<IApiResponse> {
58
+ const response = await sendRequest({ url: `${BASE}/direct-transactions`, method: "GET", params });
59
+ return response.responseData as IApiResponse;
60
+ }
@@ -34,8 +34,9 @@ export async function deleteItem(id: number): Promise<IApiResponse> {
34
34
  return response.responseData as IApiResponse;
35
35
  }
36
36
 
37
- export async function lookupItems(): Promise<IApiResponse> {
38
- const response = await sendRequest({ url: `${BASE}/lookup`, method: "GET" });
37
+ export async function lookupItems(params?: Record<string, string | number | boolean>): Promise<IApiResponse> {
38
+ const queryParams = params ? new URLSearchParams(params as Record<string, string>).toString() : "";
39
+ const response = await sendRequest({ url: `${BASE}/lookup${queryParams ? `?${queryParams}` : ""}`, method: "GET" });
39
40
  return response.responseData as IApiResponse;
40
41
  }
41
42
 
@@ -78,11 +78,11 @@ export const MENU_ITEM_REGISTRY: RegistryItem[] = [
78
78
  { key: 'core__organizations', label: 'Organizations', path: '/organizations', module: 'Core', defaultGroup: 'User Management', permission: 'organization.view' },
79
79
  { key: 'core__parties', label: 'Parties', path: '/parties', module: 'Core', defaultGroup: 'User Management', permission: 'party.view' },
80
80
  { key: 'core__party_groups', label: 'Party Groups', path: '/party-groups', module: 'Core', defaultGroup: 'User Management', permission: 'party_group.view' },
81
- { key: 'core__activity_logs', label: 'Activity Logs', path: '/activity-logs', module: 'Core', defaultGroup: 'System Auditing', permission: 'activity_log.view' },
82
- { key: 'core__api_requests', label: 'API Requests', path: '/system/api-requests', module: 'Core', defaultGroup: 'System Auditing', permission: 'api_request_log.view' },
83
- { key: 'core__feature_access_logs', label: 'Feature Access Logs', path: '/system/feature-access-logs', module: 'Core', defaultGroup: 'System Auditing', permission: 'feature_access_log.view' },
84
- { key: 'core__issue_logs', label: 'Issue Logs', path: '/system/issue-logs', module: 'Core', defaultGroup: 'System Auditing', permission: 'issue_log.view' },
85
- { key: 'core__feature_registry', label: 'Feature Registry', path: '/system/features', module: 'Core', defaultGroup: 'System Auditing', permission: 'feature.view' },
81
+ // { key: 'core__activity_logs', label: 'Activity Logs', path: '/activity-logs', module: 'Core', defaultGroup: 'System Auditing', permission: 'activity_log.view' },
82
+ // { key: 'core__api_requests', label: 'API Requests', path: '/system/api-requests', module: 'Core', defaultGroup: 'System Auditing', permission: 'api_request_log.view' },
83
+ // { key: 'core__feature_access_logs', label: 'Feature Access Logs', path: '/system/feature-access-logs', module: 'Core', defaultGroup: 'System Auditing', permission: 'feature_access_log.view' },
84
+ // { key: 'core__issue_logs', label: 'Issue Logs', path: '/system/issue-logs', module: 'Core', defaultGroup: 'System Auditing', permission: 'issue_log.view' },
85
+ // { key: 'core__feature_registry', label: 'Feature Registry', path: '/system/features', module: 'Core', defaultGroup: 'System Auditing', permission: 'feature.view' },
86
86
  { key: 'core__menu_config', label: 'Menu Configuration', path: '/menu-config', module: 'Core', defaultGroup: 'System', permission: 'menu_config.view' },
87
87
  { key: 'core__printables', label: 'Printable Templates', path: '/system/printables', module: 'Core', defaultGroup: 'System', permission: 'printable_template.view' },
88
88
  { key: 'core__email_templates', label: 'Email Templates', path: '/system/email-templates', module: 'Core', defaultGroup: 'System', permission: 'email_template.view' },
@@ -94,6 +94,12 @@ export const MENU_ITEM_REGISTRY: RegistryItem[] = [
94
94
  { key: 'core__field_configs', label: 'Field Configurations', path: '/settings/field-configs', module: 'Core', defaultGroup: 'Configuration', permission: 'entity_field_config.view' },
95
95
  { key: 'core__email_services', label: 'Integrations', path: '/settings/integrations', module: 'Core', defaultGroup: 'Configuration', permission: 'email_connection.view' },
96
96
  { key: 'core__approval_workflows', label: 'Approval Workflows', path: '/settings/approvals', module: 'Core', defaultGroup: 'Configuration', permission: null },
97
+ { key: 'core__commission_salespersons', label: 'Salespersons', path: '/commission/salespersons', module: 'Core', defaultGroup: 'Commission', permission: 'commission_salesperson.view' },
98
+ { key: 'core__commission_plans', label: 'Commission Plans', path: '/commission/plans', module: 'Core', defaultGroup: 'Commission', permission: 'commission_plan.view' },
99
+ { key: 'core__commission_assignments', label: 'Assignments', path: '/commission/assignments', module: 'Core', defaultGroup: 'Commission', permission: 'commission_assignment.view' },
100
+ { key: 'core__commission_transactions', label: 'Transactions', path: '/commission/transactions', module: 'Core', defaultGroup: 'Commission', permission: 'commission_transaction.view' },
101
+ { key: 'core__commission_adjustments', label: 'Adjustments', path: '/commission/adjustments', module: 'Core', defaultGroup: 'Commission', permission: 'commission_adjustment.view' },
102
+ { key: 'core__commission_payouts', label: 'Payout Batches', path: '/commission/payouts', module: 'Core', defaultGroup: 'Commission', permission: 'commission_payout.view' },
97
103
 
98
104
  // ── Inventory Module ──
99
105
  { key: 'inventory__items', label: 'Items', path: '/inventory/items', module: 'Inventory', defaultGroup: 'Catalog', permission: null },
@@ -126,8 +132,7 @@ export const MENU_ITEM_REGISTRY: RegistryItem[] = [
126
132
  { key: 'procurement__orders', label: 'Purchase Orders', path: '/procurement/orders', module: 'Procurement', defaultGroup: 'Orders', permission: null },
127
133
  { key: 'procurement__grn', label: 'Goods Receipt', path: '/procurement/grns', module: 'Procurement', defaultGroup: 'Receiving', permission: null },
128
134
  { key: 'procurement__settings', label: 'Settings', path: '/procurement/settings', module: 'Procurement', defaultGroup: 'Configuration', permission: 'procurement_setting.view' },
129
- { key: 'procurement__invoices', label: 'Invoices', path: '/finance/invoices', module: 'Procurement', defaultGroup: 'Finance', permission: null },
130
- { key: 'procurement__payments', label: 'Payments', path: '/finance/payments', module: 'Procurement', defaultGroup: 'Finance', permission: null },
135
+ { key: 'procurement__invoices', label: 'Supplier Invoices', path: '/procurement/invoices', module: 'Procurement', defaultGroup: 'Post-Purchase', permission: null },
131
136
 
132
137
  // ── Finance Module ──
133
138
  { key: 'finance__invoices', label: 'Invoices', path: '/finance/invoices', module: 'Finance', defaultGroup: 'Transactions', permission: 'finance.invoice.view' },
@@ -253,6 +258,7 @@ export const MENU_ITEM_REGISTRY: RegistryItem[] = [
253
258
  { key: 'construction__hierarchy', label: 'Hierarchy', path: '/construction/hierarchy', module: 'Construction', defaultGroup: 'Project Planning', permission: null },
254
259
  { key: 'construction__team', label: 'Team', path: '/construction/team', module: 'Construction', defaultGroup: 'Project Planning', permission: null },
255
260
  { key: 'construction__budget', label: 'Budget', path: '/construction/budget', module: 'Construction', defaultGroup: 'Project Planning', permission: null },
261
+ { key: 'construction__expenses', label: 'Expenses', path: '/construction/expenses', module: 'Construction', defaultGroup: 'Project Planning', permission: null },
256
262
 
257
263
  { key: 'construction__main_contract', label: 'Client', path: '/construction/contracts/main-contract', module: 'Construction', defaultGroup: 'Contracts & Billing', permission: null },
258
264
  { key: 'construction__subcontractors', label: 'Subcontractors', path: '/construction/contracts/subcontractors', module: 'Construction', defaultGroup: 'Contracts & Billing', permission: null },
@@ -289,16 +295,22 @@ export const MENU_ITEM_REGISTRY: RegistryItem[] = [
289
295
  { key: 'construction__safety_incidents', label: 'Incident Log', path: '/construction/safety/incidents', module: 'Construction', defaultGroup: 'Quality & Safety', permission: null },
290
296
  { key: 'construction__ptw', label: 'Permit to Work', path: '/construction/safety/ptw', module: 'Construction', defaultGroup: 'Quality & Safety', permission: null },
291
297
 
292
- { key: 'construction__drawings', label: 'Drawing Register', path: '/construction/document-control/drawings', module: 'Construction', defaultGroup: 'Document Control', permission: null },
298
+ { key: 'construction__documents', label: 'Documents', path: '/construction/document-control/documents', module: 'Construction', defaultGroup: 'Document Control', permission: null },
293
299
  { key: 'construction__rfis', label: 'RFIs', path: '/construction/document-control/rfis', module: 'Construction', defaultGroup: 'Document Control', permission: null },
294
300
 
295
301
  { key: 'construction__test_types', label: 'Test Types', path: '/construction/settings/test-types', module: 'Construction', defaultGroup: 'Configuration', permission: null },
296
302
  { key: 'construction__material_types', label: 'Material Types', path: '/construction/settings/material-types', module: 'Construction', defaultGroup: 'Configuration', permission: 'construction_material_type.view' },
303
+ { key: 'construction__document_categories', label: 'Document Categories', path: '/construction/settings/document-categories', module: 'Construction', defaultGroup: 'Configuration', permission: 'construction_document_category.view' },
297
304
  { key: 'construction__project_categories', label: 'Project Categories', path: '/construction/settings/project-categories', module: 'Construction', defaultGroup: 'Configuration', permission: 'project_category.view' },
298
305
  { key: 'construction__daily_tasks', label: 'Daily Tasks', path: '/construction/settings/daily-tasks', module: 'Construction', defaultGroup: 'Configuration', permission: null },
299
306
  { key: 'construction__incident_notifications', label: 'Incident Reminders', path: '/construction/settings/incident-notifications', module: 'Construction', defaultGroup: 'Configuration', permission: null },
300
307
  { key: 'construction__settings', label: 'Settings', path: '/construction/settings', module: 'Construction', defaultGroup: 'Configuration', permission: null },
301
308
 
309
+ // ── Production Module ──
310
+ { key: 'production_min__dashboard', label: 'Dashboard', path: '/production-min', module: 'Production', defaultGroup: 'Overview', permission: null },
311
+ { key: 'production_min__phantom_boms', label: 'Phantom BOMs', path: '/production-min/boms', module: 'Production', defaultGroup: 'Operations', permission: null },
312
+ { key: 'production_min__settings', label: 'Settings', path: '/production-min/settings', module: 'Production', defaultGroup: 'Configuration', permission: null },
313
+
302
314
  // ── CRM Module ──
303
315
  { key: 'crm__spaces', label: 'Spaces', path: '/crm/spaces', module: 'CRM', defaultGroup: 'Sales', permission: 'crm_space.view' },
304
316
  { key: 'crm__tasks', label: 'Tasks', path: '/crm/tasks', module: 'CRM', defaultGroup: 'Operations', permission: 'crm_task.view' },
@@ -392,6 +404,7 @@ export const MENU_PRESETS: MenuPreset[] = [
392
404
  'core__activity_logs', 'core__api_requests', 'core__feature_access_logs',
393
405
  'core__issue_logs', 'core__feature_registry', 'core__menu_config',
394
406
  'core__printables', 'core__email_templates', 'core__template_groups', 'core__currencies', 'core__settings', 'core__field_configs', 'core__email_services', 'core__approval_workflows',
407
+ 'core__commission_salespersons', 'core__commission_plans', 'core__commission_assignments', 'core__commission_transactions', 'core__commission_adjustments', 'core__commission_payouts',
395
408
  ],
396
409
  },
397
410
  {
@@ -423,7 +436,7 @@ export const MENU_PRESETS: MenuPreset[] = [
423
436
  iconName: 'Truck',
424
437
  secondaryItems: [
425
438
  'procurement__orders', 'procurement__grn',
426
- 'procurement__invoices', 'procurement__payments',
439
+ 'procurement__invoices',
427
440
  'procurement__settings',
428
441
  ],
429
442
  },
@@ -541,11 +554,20 @@ export const MENU_PRESETS: MenuPreset[] = [
541
554
  'construction__project_categories',
542
555
  'construction__test_types',
543
556
  'construction__material_types',
557
+ 'construction__document_categories',
544
558
  'construction__daily_tasks',
545
559
  'construction__incident_notifications',
546
560
  'construction__settings',
547
561
  ],
548
562
  },
563
+ {
564
+ id: 'production_min',
565
+ label: 'Production',
566
+ iconName: 'Factory',
567
+ secondaryItems: [
568
+ 'production_min__phantom_boms',
569
+ ],
570
+ },
549
571
  {
550
572
  id: 'crm',
551
573
  label: 'CRM',
@@ -613,12 +635,13 @@ const PROJECT_KEY_MAPPING: Record<string, string> = {
613
635
  'project_hierarchy': 'construction__hierarchy',
614
636
  'project_team': 'construction__team',
615
637
  'project_budget': 'construction__budget',
638
+ 'project_expenses': 'construction__expenses',
616
639
  'project_resource_calendar': 'construction__schedule_calendar',
617
640
  'project_progress_billing': 'construction__ipcs',
618
641
  'project_variations': 'construction__variations',
619
642
  'project_boq': 'construction__boq',
620
643
  'project_boq_revisions': 'construction__boq_revisions',
621
- 'project_drawings': 'construction__drawings',
644
+ 'project_documents': 'construction__documents',
622
645
  'project_rfis': 'construction__rfis',
623
646
  'project_dsr': 'construction__dsr',
624
647
  'project_progress_updates': 'construction__progress_updates',
@@ -671,13 +694,14 @@ export const getProjectConstructionGroups = (projectId: string | number): MainMe
671
694
  items: [
672
695
  { id: 'project_boq', label: 'Bill of Quantities (BOQ)', path: `/construction/projects/${projectId}/boq` },
673
696
  { id: 'project_boq_revisions', label: 'BOQ Revisions', path: `/construction/projects/${projectId}/boq/revisions` },
697
+ { id: 'project_expenses', label: 'Direct Expenses', path: `/construction/projects/${projectId}/expenses` },
674
698
  ]
675
699
  },
676
700
  {
677
701
  id: 'project_document_control',
678
702
  label: 'Document Control',
679
703
  items: [
680
- { id: 'project_drawings', label: 'Drawing Register', path: `/construction/projects/${projectId}/drawings` },
704
+ { id: 'project_documents', label: 'Documents', path: `/construction/projects/${projectId}/documents` },
681
705
  { id: 'project_rfis', label: 'RFIs', path: `/construction/projects/${projectId}/rfis` },
682
706
  ]
683
707
  },
@@ -12,5 +12,8 @@ export const local_storage = {
12
12
  user_info: { name: 'user_info', secretName: 'USER_INFO_SECRET', encrypted: false } as IStorageOptions,
13
13
  selected_organization: { name: 'selected_organization', secretName: 'SELECTED_ORG', encrypted: false } as IStorageOptions,
14
14
  sales_terminal: { name: 'sales_terminal', secretName: 'SALES_TERMINAL', encrypted: false } as IStorageOptions,
15
- active_project: { name: 'active_project', secretName: 'ACTIVE_PROJECT', encrypted: false } as IStorageOptions
15
+ active_project: { name: 'active_project', secretName: 'ACTIVE_PROJECT', encrypted: false } as IStorageOptions,
16
+ pos_fullscreen: { name: 'pos_fullscreen', secretName: 'POS_FULLSCREEN', encrypted: false } as IStorageOptions,
17
+ hide_pricing_note: { name: 'hide_pricing_note', secretName: 'HIDE_PRICING_NOTE', encrypted: false } as IStorageOptions,
18
+ hide_rule_note: { name: 'hide_rule_note', secretName: 'HIDE_RULE_NOTE', encrypted: false } as IStorageOptions
16
19
  };
@@ -6,6 +6,7 @@ export interface IPricingRule {
6
6
  markdown_percentage: number | null;
7
7
  batch_id?: number | null;
8
8
  final_rate?: number;
9
+ specificity_score?: number;
9
10
  }
10
11
 
11
12
  export class PriceCalculationService {
@@ -33,10 +34,17 @@ export class PriceCalculationService {
33
34
  return { unitPrice: basePrice, discountPercent: 0, discountAmount: 0 };
34
35
  }
35
36
 
36
- // Sort by batch specificity first, then min_quantity descending (highest threshold wins)
37
+ // Sort by batch specificity first, then specificity score, then min_quantity descending
37
38
  applicableRules.sort((a, b) => {
38
39
  if (a.batch_id && !b.batch_id) return -1;
39
40
  if (!a.batch_id && b.batch_id) return 1;
41
+
42
+ const scoreA = a.specificity_score || 0;
43
+ const scoreB = b.specificity_score || 0;
44
+ if (scoreA !== scoreB) {
45
+ return scoreB - scoreA;
46
+ }
47
+
40
48
  return b.min_quantity - a.min_quantity;
41
49
  });
42
50
 
@@ -117,6 +117,8 @@ export async function forwardApiProxyRequest(request: Request, pathSegments: str
117
117
  const contentType = request.headers.get("content-type");
118
118
  const organizationId = request.headers.get("x-organization-id") ?? selectedOrganizationId;
119
119
 
120
+ const tenantDomain = request.headers.get("x-tenant-domain");
121
+
120
122
  if (accept) {
121
123
  headers.set("accept", accept);
122
124
  }
@@ -129,6 +131,10 @@ export async function forwardApiProxyRequest(request: Request, pathSegments: str
129
131
  headers.set("x-organization-id", organizationId);
130
132
  }
131
133
 
134
+ if (tenantDomain) {
135
+ headers.set("x-tenant-domain", tenantDomain);
136
+ }
137
+
132
138
  if (token) {
133
139
  headers.set("authorization", `Bearer ${token}`);
134
140
  }
@@ -113,6 +113,9 @@ export async function sendRequest<T = any>({ url, method, data, params, headers,
113
113
 
114
114
  finalUrl = buildBrowserProxyUrl(finalUrl);
115
115
 
116
+ // Inject Tenant Domain dynamically for Database-per-Tenant isolation
117
+ defaultHeaders['X-Tenant-Domain'] = window.location.hostname;
118
+
116
119
  // Automatically inject selected organization ID into every request
117
120
  try {
118
121
  const orgRaw = localStorage.getItem('selected_organization');
@@ -131,6 +134,22 @@ export async function sendRequest<T = any>({ url, method, data, params, headers,
131
134
  defaultHeaders['Content-Type'] = 'application/json';
132
135
  }
133
136
 
137
+ // Server-side Tenant Domain extraction
138
+ if (typeof window === 'undefined') {
139
+ try {
140
+ // Dynamically import next/headers to avoid client-side bundling issues
141
+ const nextHeaders = require('next/headers');
142
+ if (nextHeaders && typeof nextHeaders.headers === 'function') {
143
+ const host = nextHeaders.headers().get('host');
144
+ if (host) {
145
+ defaultHeaders['X-Tenant-Domain'] = host.split(':')[0];
146
+ }
147
+ }
148
+ } catch (error) {
149
+ // Ignore if next/headers is unavailable
150
+ }
151
+ }
152
+
134
153
  try {
135
154
  const response = await fetch(finalUrl, {
136
155
  method,