@apptimate/core-lib 4.8.0 → 5.1.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
|
@@ -15,6 +15,31 @@ 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 checkHasActivePriceLists(): Promise<IApiResponse> {
|
|
19
|
+
const response = await sendRequest({
|
|
20
|
+
url: `${process.env.NEXT_PUBLIC_API_URL}/api/sales/price-lists/has-active?t=${new Date().getTime()}`,
|
|
21
|
+
method: "GET"
|
|
22
|
+
});
|
|
23
|
+
return response.responseData as IApiResponse;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export async function evaluatePriceLists(data: {
|
|
27
|
+
party_id?: number | null;
|
|
28
|
+
transaction_type: string;
|
|
29
|
+
items: Array<{ variant_id: number }>;
|
|
30
|
+
}): Promise<IApiResponse> {
|
|
31
|
+
const variant_ids = data.items.map(item => item.variant_id);
|
|
32
|
+
const response = await sendRequest({
|
|
33
|
+
url: `${process.env.NEXT_PUBLIC_API_URL}/api/sales/price-lists/evaluate`,
|
|
34
|
+
method: "POST",
|
|
35
|
+
data: {
|
|
36
|
+
party_id: data.party_id,
|
|
37
|
+
variant_ids
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
return response.responseData as IApiResponse;
|
|
41
|
+
}
|
|
42
|
+
|
|
18
43
|
export async function bulkStockCheck(data: {
|
|
19
44
|
warehouse_id: number;
|
|
20
45
|
lines: Array<{ item_id: number; variant_id?: number | null; batch_id?: number | null; quantity: number }>;
|
package/src/constants/menus.ts
CHANGED
|
@@ -90,6 +90,7 @@ export const MENU_ITEM_REGISTRY: RegistryItem[] = [
|
|
|
90
90
|
{ key: 'core__terminals', label: 'Terminals', path: '/terminals', module: 'Core', defaultGroup: 'Resources', permission: 'terminal.view' },
|
|
91
91
|
{ key: 'core__currencies', label: 'Currencies', path: '/currencies', module: 'Core', defaultGroup: 'Resources', permission: 'currency.view' },
|
|
92
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 },
|
|
93
94
|
|
|
94
95
|
// ── Inventory Module ──
|
|
95
96
|
{ key: 'inventory__items', label: 'Items', path: '/inventory/items', module: 'Inventory', defaultGroup: 'Catalog', permission: null },
|
|
@@ -112,9 +113,9 @@ export const MENU_ITEM_REGISTRY: RegistryItem[] = [
|
|
|
112
113
|
{ key: 'sales__returns', label: 'Sales Returns', path: '/sales/returns', module: 'Sales', defaultGroup: 'Post-Sales', permission: null },
|
|
113
114
|
{ key: 'sales__credit_notes', label: 'Credit Notes', path: '/sales/credit-notes', module: 'Sales', defaultGroup: 'Post-Sales', permission: null },
|
|
114
115
|
{ key: 'sales__pos', label: 'POS Terminal', path: '/sales/pos', module: 'Sales', defaultGroup: 'POS', permission: null },
|
|
115
|
-
|
|
116
116
|
{ key: 'sales__pos_sessions', label: 'Terminal Sessions', path: '/sales/pos/sessions', module: 'Sales', defaultGroup: 'POS', permission: null },
|
|
117
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' },
|
|
118
119
|
|
|
119
120
|
// ── Procurement Module ──
|
|
120
121
|
{ key: 'procurement__orders', label: 'Purchase Orders', path: '/procurement/orders', module: 'Procurement', defaultGroup: 'Orders', permission: null },
|
|
@@ -248,21 +249,14 @@ export const MENU_ITEM_REGISTRY: RegistryItem[] = [
|
|
|
248
249
|
{ key: 'construction__ipcs', label: 'Progress Billing', path: '/construction/contracts/ipcs', module: 'Construction', defaultGroup: 'Contracts & Billing', permission: null },
|
|
249
250
|
{ key: 'construction__variations', label: 'Subcontractor Variations', path: '/construction/variations', module: 'Construction', defaultGroup: 'Contracts & Billing', permission: null },
|
|
250
251
|
|
|
251
|
-
{ key: 'construction__prs', label: 'Purchase Requisitions', path: '/construction/procurement/prs', module: 'Construction', defaultGroup: 'Procurement', permission: null },
|
|
252
|
-
{ key: 'construction__pos', label: 'Purchase Orders', path: '/construction/procurement/pos', module: 'Construction', defaultGroup: 'Procurement', permission: null },
|
|
253
|
-
{ key: 'construction__grns', label: 'Goods Receipt', path: '/construction/procurement/grns', module: 'Construction', defaultGroup: 'Procurement', permission: null },
|
|
254
|
-
{ key: 'construction__mtrs', label: 'Transfer Requests', path: '/construction/procurement/mtrs', module: 'Construction', defaultGroup: 'Procurement', permission: null },
|
|
255
252
|
|
|
256
|
-
|
|
257
|
-
{ key: 'construction__site_issues', label: 'Project Issues', path: '/construction/site-inventory/issues', module: 'Construction', defaultGroup: 'Project Inventory', permission: null },
|
|
258
|
-
{ key: 'construction__wastage_returns', label: 'Wastage & Returns', path: '/construction/site-inventory/wastage-returns', module: 'Construction', defaultGroup: 'Project Inventory', permission: null },
|
|
259
|
-
|
|
253
|
+
|
|
260
254
|
{ key: 'construction__workforce', label: 'Workforce Master', path: '/construction/workforce', module: 'Construction', defaultGroup: 'Workforce', permission: null },
|
|
261
255
|
{ key: 'construction__shifts', label: 'Shift Master', path: '/construction/shifts', module: 'Construction', defaultGroup: 'Workforce', permission: null },
|
|
262
256
|
{ key: 'construction__attendance', label: 'Attendance & Timesheets', path: '/construction/attendance', module: 'Construction', defaultGroup: 'Workforce', permission: null },
|
|
263
257
|
|
|
264
258
|
{ key: 'construction__equipment_types', label: 'Equipment Types', path: '/construction/equipment/types', module: 'Construction', defaultGroup: 'Equipment & Plant', permission: null },
|
|
265
|
-
{ key: 'construction__equipment', label: '
|
|
259
|
+
{ key: 'construction__equipment', label: 'Equipments', path: '/construction/equipment', module: 'Construction', defaultGroup: 'Equipment & Plant', permission: null },
|
|
266
260
|
{ key: 'construction__equipment_allocation', label: 'Equipment Allocation', path: '/construction/equipment/allocation', module: 'Construction', defaultGroup: 'Equipment & Plant', permission: null },
|
|
267
261
|
{ key: 'construction__fuel_logs', label: 'Fuel Logs', path: '/construction/equipment/fuel-logs', module: 'Construction', defaultGroup: 'Equipment & Plant', permission: null },
|
|
268
262
|
{ key: 'construction__maintenance', label: 'Maintenance', path: '/construction/equipment/maintenance', module: 'Construction', defaultGroup: 'Equipment & Plant', permission: null },
|
|
@@ -281,8 +275,8 @@ export const MENU_ITEM_REGISTRY: RegistryItem[] = [
|
|
|
281
275
|
{ key: 'construction__drawings', label: 'Drawing Register', path: '/construction/document-control/drawings', module: 'Construction', defaultGroup: 'Document Control', permission: null },
|
|
282
276
|
{ key: 'construction__rfis', label: 'RFIs', path: '/construction/document-control/rfis', module: 'Construction', defaultGroup: 'Document Control', permission: null },
|
|
283
277
|
|
|
284
|
-
{ key: 'construction__approval_workflows', label: 'Approval Workflows', path: '/construction/settings/approvals', module: 'Construction', defaultGroup: 'Configuration', permission: null },
|
|
285
278
|
{ key: 'construction__test_types', label: 'Test Types', path: '/construction/settings/test-types', module: 'Construction', defaultGroup: 'Configuration', permission: null },
|
|
279
|
+
{ key: 'construction__project_categories', label: 'Project Categories', path: '/construction/settings/project-categories', module: 'Construction', defaultGroup: 'Configuration', permission: 'project_category.view' },
|
|
286
280
|
{ key: 'construction__settings', label: 'Settings', path: '/construction/settings', module: 'Construction', defaultGroup: 'Configuration', permission: null },
|
|
287
281
|
|
|
288
282
|
// ── CRM Module ──
|
|
@@ -377,7 +371,7 @@ export const MENU_PRESETS: MenuPreset[] = [
|
|
|
377
371
|
'core__warehouses', 'core__terminals',
|
|
378
372
|
'core__activity_logs', 'core__api_requests', 'core__feature_access_logs',
|
|
379
373
|
'core__issue_logs', 'core__feature_registry', 'core__menu_config',
|
|
380
|
-
'core__printables', 'core__template_groups', 'core__currencies', 'core__settings',
|
|
374
|
+
'core__printables', 'core__template_groups', 'core__currencies', 'core__settings', 'core__approval_workflows',
|
|
381
375
|
],
|
|
382
376
|
},
|
|
383
377
|
{
|
|
@@ -399,6 +393,7 @@ export const MENU_PRESETS: MenuPreset[] = [
|
|
|
399
393
|
'sales__quotations', 'sales__orders', 'sales__goods_issue_notes',
|
|
400
394
|
'sales__returns', 'sales__credit_notes',
|
|
401
395
|
'sales__pos', 'sales__pos_sessions',
|
|
396
|
+
'sales__settings'
|
|
402
397
|
],
|
|
403
398
|
},
|
|
404
399
|
{
|
|
@@ -540,8 +535,8 @@ export const MENU_PRESETS: MenuPreset[] = [
|
|
|
540
535
|
'construction__material_testing',
|
|
541
536
|
'construction__safety_incidents',
|
|
542
537
|
'construction__ptw',
|
|
543
|
-
'construction__approval_workflows',
|
|
544
538
|
'construction__test_types',
|
|
539
|
+
'construction__project_categories',
|
|
545
540
|
'construction__settings',
|
|
546
541
|
],
|
|
547
542
|
},
|
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,57 @@
|
|
|
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
|
+
batch_id?: number | null;
|
|
8
|
+
final_rate?: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export class PriceCalculationService {
|
|
12
|
+
/**
|
|
13
|
+
* Calculate detailed price breakdown for a given quantity using pricing rules
|
|
14
|
+
*/
|
|
15
|
+
public static calculatePriceDetails(
|
|
16
|
+
basePrice: number,
|
|
17
|
+
quantity: number,
|
|
18
|
+
rules?: IPricingRule[],
|
|
19
|
+
batchId?: number | null
|
|
20
|
+
): { unitPrice: number; discountPercent: number; discountAmount: number; appliedRule?: IPricingRule } {
|
|
21
|
+
if (!rules || rules.length === 0) {
|
|
22
|
+
return { unitPrice: basePrice, discountPercent: 0, discountAmount: 0 };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Filter rules that match the quantity condition and batch condition
|
|
26
|
+
const applicableRules = rules.filter(r => {
|
|
27
|
+
if (quantity < r.min_quantity) return false;
|
|
28
|
+
if (r.batch_id && String(r.batch_id) !== String(batchId)) return false;
|
|
29
|
+
return true;
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
if (applicableRules.length === 0) {
|
|
33
|
+
return { unitPrice: basePrice, discountPercent: 0, discountAmount: 0 };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Sort by batch specificity first, then min_quantity descending (highest threshold wins)
|
|
37
|
+
applicableRules.sort((a, b) => {
|
|
38
|
+
if (a.batch_id && !b.batch_id) return -1;
|
|
39
|
+
if (!a.batch_id && b.batch_id) return 1;
|
|
40
|
+
return b.min_quantity - a.min_quantity;
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const activeRule = applicableRules[0];
|
|
44
|
+
|
|
45
|
+
if (activeRule.rate !== null && activeRule.rate !== undefined) {
|
|
46
|
+
return { unitPrice: activeRule.rate, discountPercent: 0, discountAmount: 0, appliedRule: { ...activeRule, final_rate: activeRule.rate } };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (activeRule.markdown_percentage !== null && activeRule.markdown_percentage !== undefined) {
|
|
50
|
+
const markdownAmount = basePrice * (activeRule.markdown_percentage / 100);
|
|
51
|
+
const newUnitPrice = basePrice - markdownAmount;
|
|
52
|
+
return { unitPrice: newUnitPrice, discountPercent: 0, discountAmount: 0, appliedRule: { ...activeRule, final_rate: newUnitPrice } };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return { unitPrice: basePrice, discountPercent: 0, discountAmount: 0 };
|
|
56
|
+
}
|
|
57
|
+
}
|