@apptimate/core-lib 4.7.0 → 4.9.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 +1 -1
- package/src/api-services/inventory/parties.client.ts +8 -3
- package/src/api-services/inventory/transaction.client.ts +17 -0
- package/src/constants/menus.ts +10 -7
- package/src/index.ts +2 -1
- package/src/schemas/approvalWorkflowSchema.ts +2 -1
- package/src/utils/PriceCalculationService.ts +47 -0
package/package.json
CHANGED
|
@@ -56,10 +56,15 @@ export async function togglePartyStatus(id: number, status: string): Promise<IAp
|
|
|
56
56
|
return response.responseData as IApiResponse;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
export async function lookupParties(type?: string): Promise<IApiResponse> {
|
|
60
|
-
const
|
|
59
|
+
export async function lookupParties(params?: { type?: string; search?: string; limit?: number }): Promise<IApiResponse> {
|
|
60
|
+
const query = new URLSearchParams();
|
|
61
|
+
if (params?.type) query.append('type', params.type);
|
|
62
|
+
if (params?.search) query.append('search', params.search);
|
|
63
|
+
if (params?.limit) query.append('limit', params.limit.toString());
|
|
64
|
+
|
|
65
|
+
const queryParams = query.toString();
|
|
61
66
|
const response = await sendRequest({
|
|
62
|
-
url: `${BASE}/lookup${queryParams}`,
|
|
67
|
+
url: `${BASE}/lookup${queryParams ? `?${queryParams}` : ""}`,
|
|
63
68
|
method: "GET",
|
|
64
69
|
});
|
|
65
70
|
return response.responseData as IApiResponse;
|
|
@@ -15,6 +15,23 @@ export async function getItemBatches(itemId: number, params: Record<string, stri
|
|
|
15
15
|
return response.responseData as IApiResponse;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
export async function evaluatePriceLists(data: {
|
|
19
|
+
party_id?: number | null;
|
|
20
|
+
transaction_type: string;
|
|
21
|
+
items: Array<{ variant_id: number }>;
|
|
22
|
+
}): Promise<IApiResponse> {
|
|
23
|
+
const variant_ids = data.items.map(item => item.variant_id);
|
|
24
|
+
const response = await sendRequest({
|
|
25
|
+
url: `${process.env.NEXT_PUBLIC_API_URL}/api/sales/price-lists/evaluate`,
|
|
26
|
+
method: "POST",
|
|
27
|
+
data: {
|
|
28
|
+
party_id: data.party_id,
|
|
29
|
+
variant_ids
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
return response.responseData as IApiResponse;
|
|
33
|
+
}
|
|
34
|
+
|
|
18
35
|
export async function bulkStockCheck(data: {
|
|
19
36
|
warehouse_id: number;
|
|
20
37
|
lines: Array<{ item_id: number; variant_id?: number | null; batch_id?: number | null; quantity: number }>;
|
package/src/constants/menus.ts
CHANGED
|
@@ -77,6 +77,7 @@ export const MENU_ITEM_REGISTRY: RegistryItem[] = [
|
|
|
77
77
|
{ key: 'core__roles', label: 'Roles', path: '/roles', module: 'Core', defaultGroup: 'User Management', permission: 'role.view' },
|
|
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
|
+
{ key: 'core__party_groups', label: 'Party Groups', path: '/party-groups', module: 'Core', defaultGroup: 'User Management', permission: 'party_group.view' },
|
|
80
81
|
{ key: 'core__activity_logs', label: 'Activity Logs', path: '/activity-logs', module: 'Core', defaultGroup: 'System Auditing', permission: 'activity_log.view' },
|
|
81
82
|
{ key: 'core__api_requests', label: 'API Requests', path: '/system/api-requests', module: 'Core', defaultGroup: 'System Auditing', permission: 'api_request_log.view' },
|
|
82
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' },
|
|
@@ -89,6 +90,7 @@ export const MENU_ITEM_REGISTRY: RegistryItem[] = [
|
|
|
89
90
|
{ key: 'core__terminals', label: 'Terminals', path: '/terminals', module: 'Core', defaultGroup: 'Resources', permission: 'terminal.view' },
|
|
90
91
|
{ key: 'core__currencies', label: 'Currencies', path: '/currencies', module: 'Core', defaultGroup: 'Resources', permission: 'currency.view' },
|
|
91
92
|
{ key: 'core__settings', label: 'Settings', path: '/settings', module: 'Core', defaultGroup: 'Configuration', permission: 'core_setting.view' },
|
|
93
|
+
{ key: 'core__approval_workflows', label: 'Approval Workflows', path: '/settings/approvals', module: 'Core', defaultGroup: 'Configuration', permission: null },
|
|
92
94
|
|
|
93
95
|
// ── Inventory Module ──
|
|
94
96
|
{ key: 'inventory__items', label: 'Items', path: '/inventory/items', module: 'Inventory', defaultGroup: 'Catalog', permission: null },
|
|
@@ -111,9 +113,9 @@ export const MENU_ITEM_REGISTRY: RegistryItem[] = [
|
|
|
111
113
|
{ key: 'sales__returns', label: 'Sales Returns', path: '/sales/returns', module: 'Sales', defaultGroup: 'Post-Sales', permission: null },
|
|
112
114
|
{ key: 'sales__credit_notes', label: 'Credit Notes', path: '/sales/credit-notes', module: 'Sales', defaultGroup: 'Post-Sales', permission: null },
|
|
113
115
|
{ key: 'sales__pos', label: 'POS Terminal', path: '/sales/pos', module: 'Sales', defaultGroup: 'POS', permission: null },
|
|
114
|
-
|
|
115
116
|
{ key: 'sales__pos_sessions', label: 'Terminal Sessions', path: '/sales/pos/sessions', module: 'Sales', defaultGroup: 'POS', permission: null },
|
|
116
117
|
{ key: 'sales__reports', label: 'Sales Reports', path: '/sales/reports', module: 'Sales', defaultGroup: 'Analytics', permission: null },
|
|
118
|
+
{ key: 'sales__settings', label: 'Settings', path: '/sales/settings', module: 'Sales', defaultGroup: 'Settings', permission: 'sales_setting.view' },
|
|
117
119
|
|
|
118
120
|
// ── Procurement Module ──
|
|
119
121
|
{ key: 'procurement__orders', label: 'Purchase Orders', path: '/procurement/orders', module: 'Procurement', defaultGroup: 'Orders', permission: null },
|
|
@@ -261,7 +263,7 @@ export const MENU_ITEM_REGISTRY: RegistryItem[] = [
|
|
|
261
263
|
{ key: 'construction__attendance', label: 'Attendance & Timesheets', path: '/construction/attendance', module: 'Construction', defaultGroup: 'Workforce', permission: null },
|
|
262
264
|
|
|
263
265
|
{ key: 'construction__equipment_types', label: 'Equipment Types', path: '/construction/equipment/types', module: 'Construction', defaultGroup: 'Equipment & Plant', permission: null },
|
|
264
|
-
{ key: 'construction__equipment', label: '
|
|
266
|
+
{ key: 'construction__equipment', label: 'Equipments', path: '/construction/equipment', module: 'Construction', defaultGroup: 'Equipment & Plant', permission: null },
|
|
265
267
|
{ key: 'construction__equipment_allocation', label: 'Equipment Allocation', path: '/construction/equipment/allocation', module: 'Construction', defaultGroup: 'Equipment & Plant', permission: null },
|
|
266
268
|
{ key: 'construction__fuel_logs', label: 'Fuel Logs', path: '/construction/equipment/fuel-logs', module: 'Construction', defaultGroup: 'Equipment & Plant', permission: null },
|
|
267
269
|
{ key: 'construction__maintenance', label: 'Maintenance', path: '/construction/equipment/maintenance', module: 'Construction', defaultGroup: 'Equipment & Plant', permission: null },
|
|
@@ -280,8 +282,8 @@ export const MENU_ITEM_REGISTRY: RegistryItem[] = [
|
|
|
280
282
|
{ key: 'construction__drawings', label: 'Drawing Register', path: '/construction/document-control/drawings', module: 'Construction', defaultGroup: 'Document Control', permission: null },
|
|
281
283
|
{ key: 'construction__rfis', label: 'RFIs', path: '/construction/document-control/rfis', module: 'Construction', defaultGroup: 'Document Control', permission: null },
|
|
282
284
|
|
|
283
|
-
{ key: 'construction__approval_workflows', label: 'Approval Workflows', path: '/construction/settings/approvals', module: 'Construction', defaultGroup: 'Configuration', permission: null },
|
|
284
285
|
{ key: 'construction__test_types', label: 'Test Types', path: '/construction/settings/test-types', module: 'Construction', defaultGroup: 'Configuration', permission: null },
|
|
286
|
+
{ key: 'construction__project_categories', label: 'Project Categories', path: '/construction/settings/project-categories', module: 'Construction', defaultGroup: 'Configuration', permission: 'project_category.view' },
|
|
285
287
|
{ key: 'construction__settings', label: 'Settings', path: '/construction/settings', module: 'Construction', defaultGroup: 'Configuration', permission: null },
|
|
286
288
|
|
|
287
289
|
// ── CRM Module ──
|
|
@@ -372,11 +374,11 @@ export const MENU_PRESETS: MenuPreset[] = [
|
|
|
372
374
|
label: 'Core',
|
|
373
375
|
iconName: 'Settings',
|
|
374
376
|
secondaryItems: [
|
|
375
|
-
'core__users', 'core__roles', 'core__organizations', 'core__parties',
|
|
377
|
+
'core__users', 'core__roles', 'core__organizations', 'core__parties', 'core__party_groups',
|
|
376
378
|
'core__warehouses', 'core__terminals',
|
|
377
379
|
'core__activity_logs', 'core__api_requests', 'core__feature_access_logs',
|
|
378
380
|
'core__issue_logs', 'core__feature_registry', 'core__menu_config',
|
|
379
|
-
'core__printables', 'core__template_groups', 'core__currencies', 'core__settings',
|
|
381
|
+
'core__printables', 'core__template_groups', 'core__currencies', 'core__settings', 'core__approval_workflows',
|
|
380
382
|
],
|
|
381
383
|
},
|
|
382
384
|
{
|
|
@@ -397,7 +399,8 @@ export const MENU_PRESETS: MenuPreset[] = [
|
|
|
397
399
|
'sales__price_lists',
|
|
398
400
|
'sales__quotations', 'sales__orders', 'sales__goods_issue_notes',
|
|
399
401
|
'sales__returns', 'sales__credit_notes',
|
|
400
|
-
'sales__pos', 'sales__pos_sessions',
|
|
402
|
+
'sales__pos', 'sales__pos_sessions',
|
|
403
|
+
'sales__settings'
|
|
401
404
|
],
|
|
402
405
|
},
|
|
403
406
|
{
|
|
@@ -539,8 +542,8 @@ export const MENU_PRESETS: MenuPreset[] = [
|
|
|
539
542
|
'construction__material_testing',
|
|
540
543
|
'construction__safety_incidents',
|
|
541
544
|
'construction__ptw',
|
|
542
|
-
'construction__approval_workflows',
|
|
543
545
|
'construction__test_types',
|
|
546
|
+
'construction__project_categories',
|
|
544
547
|
'construction__settings',
|
|
545
548
|
],
|
|
546
549
|
},
|
package/src/index.ts
CHANGED
|
@@ -9,4 +9,5 @@ export * from './api-services/inventory/brands.client';
|
|
|
9
9
|
export * from './api-services/inventory/uom.client';
|
|
10
10
|
export * from './api-services/inventory/uom-group.client';
|
|
11
11
|
export * from './api-services/inventory/parties.client';
|
|
12
|
-
export * from './api-services/inventory/transaction.client';
|
|
12
|
+
export * from './api-services/inventory/transaction.client';
|
|
13
|
+
export * from './utils/PriceCalculationService';
|
|
@@ -3,7 +3,8 @@ import { z } from "zod";
|
|
|
3
3
|
export const approvalStepSchema = z.object({
|
|
4
4
|
step_order: z.number().int().positive(),
|
|
5
5
|
approver_type: z.enum(["user", "role"]),
|
|
6
|
-
|
|
6
|
+
approver_ids: z.array(z.string()).min(1, "At least one approver is required"),
|
|
7
|
+
required_approvals: z.number().int().min(1, "Must be at least 1")
|
|
7
8
|
});
|
|
8
9
|
|
|
9
10
|
export const approvalWorkflowSchema = z.object({
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export interface IPricingRule {
|
|
2
|
+
id: number;
|
|
3
|
+
price_list_name?: string;
|
|
4
|
+
min_quantity: number;
|
|
5
|
+
rate: number | null;
|
|
6
|
+
markdown_percentage: number | null;
|
|
7
|
+
final_rate?: number;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export class PriceCalculationService {
|
|
11
|
+
/**
|
|
12
|
+
* Calculate detailed price breakdown for a given quantity using pricing rules
|
|
13
|
+
*/
|
|
14
|
+
public static calculatePriceDetails(
|
|
15
|
+
basePrice: number,
|
|
16
|
+
quantity: number,
|
|
17
|
+
rules?: IPricingRule[]
|
|
18
|
+
): { unitPrice: number; discountPercent: number; discountAmount: number; appliedRule?: IPricingRule } {
|
|
19
|
+
if (!rules || rules.length === 0) {
|
|
20
|
+
return { unitPrice: basePrice, discountPercent: 0, discountAmount: 0 };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Filter rules that match the quantity condition
|
|
24
|
+
const applicableRules = rules.filter(r => quantity >= r.min_quantity);
|
|
25
|
+
|
|
26
|
+
if (applicableRules.length === 0) {
|
|
27
|
+
return { unitPrice: basePrice, discountPercent: 0, discountAmount: 0 };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Sort by min_quantity descending (highest threshold wins)
|
|
31
|
+
applicableRules.sort((a, b) => b.min_quantity - a.min_quantity);
|
|
32
|
+
|
|
33
|
+
const activeRule = applicableRules[0];
|
|
34
|
+
|
|
35
|
+
if (activeRule.rate !== null && activeRule.rate !== undefined) {
|
|
36
|
+
return { unitPrice: activeRule.rate, discountPercent: 0, discountAmount: 0, appliedRule: { ...activeRule, final_rate: activeRule.rate } };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (activeRule.markdown_percentage !== null && activeRule.markdown_percentage !== undefined) {
|
|
40
|
+
const markdownAmount = basePrice * (activeRule.markdown_percentage / 100);
|
|
41
|
+
const newUnitPrice = basePrice - markdownAmount;
|
|
42
|
+
return { unitPrice: newUnitPrice, discountPercent: 0, discountAmount: 0, appliedRule: { ...activeRule, final_rate: newUnitPrice } };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return { unitPrice: basePrice, discountPercent: 0, discountAmount: 0 };
|
|
46
|
+
}
|
|
47
|
+
}
|