hvp-shared 3.4.0 → 3.5.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/dist/constants/catalog-item.constants.d.ts +63 -0
- package/dist/constants/catalog-item.constants.js +80 -0
- package/dist/constants/index.d.ts +4 -0
- package/dist/constants/index.js +4 -0
- package/dist/{inventory → constants}/qvet-catalog.d.ts +8 -58
- package/dist/{inventory → constants}/qvet-catalog.js +10 -133
- package/dist/constants/qvet-inventory.d.ts +28 -0
- package/dist/constants/qvet-inventory.js +56 -0
- package/dist/constants/qvet-warehouses.d.ts +6 -0
- package/dist/constants/qvet-warehouses.js +9 -0
- package/dist/contracts/catalog-item/index.d.ts +2 -0
- package/dist/{inventory → contracts/catalog-item}/index.js +2 -1
- package/dist/contracts/catalog-item/requests.d.ts +113 -0
- package/dist/contracts/catalog-item/requests.js +7 -0
- package/dist/contracts/catalog-item/responses.d.ts +132 -0
- package/dist/contracts/catalog-item/responses.js +7 -0
- package/dist/contracts/index.d.ts +1 -0
- package/dist/contracts/index.js +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/types/catalog-item.types.d.ts +104 -0
- package/dist/types/catalog-item.types.js +7 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/index.js +3 -0
- package/dist/types/qvet.types.d.ts +21 -0
- package/dist/types/qvet.types.js +9 -0
- package/dist/types/sync-field.types.d.ts +23 -0
- package/dist/types/sync-field.types.js +9 -0
- package/dist/utils/qvet-catalog.helpers.d.ts +37 -0
- package/dist/utils/qvet-catalog.helpers.js +104 -0
- package/dist/utils/sync-field.helpers.d.ts +84 -0
- package/dist/utils/sync-field.helpers.js +117 -0
- package/dist/utils/sync-field.helpers.test.d.ts +1 -0
- package/dist/utils/sync-field.helpers.test.js +149 -0
- package/package.json +1 -1
- package/dist/inventory/index.d.ts +0 -1
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CatalogItem Response Types
|
|
3
|
+
*
|
|
4
|
+
* Response DTOs for catalog item endpoints.
|
|
5
|
+
*/
|
|
6
|
+
import { SyncField } from '../../types/sync-field.types';
|
|
7
|
+
import { UsageType, SyncStatus } from '../../constants/catalog-item.constants';
|
|
8
|
+
import { Warehouse } from '../../types/qvet.types';
|
|
9
|
+
/**
|
|
10
|
+
* Warehouse stock response
|
|
11
|
+
*/
|
|
12
|
+
export interface WarehouseStockResponse {
|
|
13
|
+
warehouse: Warehouse;
|
|
14
|
+
currentStock: number;
|
|
15
|
+
minStock: SyncField<number>;
|
|
16
|
+
optimalStock: SyncField<number>;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* CatalogItem List Response
|
|
20
|
+
*
|
|
21
|
+
* Simplified item for list views with effective values.
|
|
22
|
+
*
|
|
23
|
+
* @example GET /api/catalog-items
|
|
24
|
+
*/
|
|
25
|
+
export interface CatalogItemListResponse {
|
|
26
|
+
id: string;
|
|
27
|
+
qvetCode: number;
|
|
28
|
+
description: string;
|
|
29
|
+
section: string;
|
|
30
|
+
family: string;
|
|
31
|
+
salePrice: number;
|
|
32
|
+
isActive: boolean;
|
|
33
|
+
usageType: UsageType;
|
|
34
|
+
hasPendingChanges: boolean;
|
|
35
|
+
/** Which fields have pending changes */
|
|
36
|
+
pendingFields?: string[];
|
|
37
|
+
syncStatus: SyncStatus;
|
|
38
|
+
lastSyncAt: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* CatalogItem Detail Response
|
|
42
|
+
*
|
|
43
|
+
* Full item detail with all sync fields.
|
|
44
|
+
*
|
|
45
|
+
* @example GET /api/catalog-items/:id
|
|
46
|
+
*/
|
|
47
|
+
export interface CatalogItemDetailResponse {
|
|
48
|
+
id: string;
|
|
49
|
+
qvetCode: number;
|
|
50
|
+
qvetSync: {
|
|
51
|
+
description: SyncField<string>;
|
|
52
|
+
barcode: SyncField<string>;
|
|
53
|
+
reference: SyncField<string>;
|
|
54
|
+
section: SyncField<string>;
|
|
55
|
+
family: SyncField<string>;
|
|
56
|
+
subfamily: SyncField<string>;
|
|
57
|
+
brand: SyncField<string>;
|
|
58
|
+
purchasePrice: SyncField<number>;
|
|
59
|
+
salePrice: SyncField<number>;
|
|
60
|
+
purchaseMargin: SyncField<number>;
|
|
61
|
+
saleMargin: SyncField<number>;
|
|
62
|
+
vatSale: SyncField<number>;
|
|
63
|
+
vatPurchase: SyncField<number>;
|
|
64
|
+
purchaseUnit: SyncField<string>;
|
|
65
|
+
saleUnit: SyncField<string>;
|
|
66
|
+
conversionFactor: SyncField<number>;
|
|
67
|
+
isActive: SyncField<boolean>;
|
|
68
|
+
stockControlType: SyncField<string>;
|
|
69
|
+
stockByWarehouse: WarehouseStockResponse[];
|
|
70
|
+
qvetCreatedAt?: string;
|
|
71
|
+
lastSaleDate?: string;
|
|
72
|
+
};
|
|
73
|
+
hvpData: {
|
|
74
|
+
usageType: UsageType;
|
|
75
|
+
notes?: string;
|
|
76
|
+
internalCategory?: string;
|
|
77
|
+
};
|
|
78
|
+
hasPendingChanges: boolean;
|
|
79
|
+
lastSyncAt: string;
|
|
80
|
+
syncStatus: SyncStatus;
|
|
81
|
+
createdAt: string;
|
|
82
|
+
updatedAt: string;
|
|
83
|
+
updatedBy?: string;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Paginated list response
|
|
87
|
+
*/
|
|
88
|
+
export interface CatalogItemPaginatedResponse {
|
|
89
|
+
items: CatalogItemListResponse[];
|
|
90
|
+
meta: {
|
|
91
|
+
total: number;
|
|
92
|
+
page: number;
|
|
93
|
+
limit: number;
|
|
94
|
+
totalPages: number;
|
|
95
|
+
hasNextPage: boolean;
|
|
96
|
+
hasPrevPage: boolean;
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Sync status response
|
|
101
|
+
*
|
|
102
|
+
* @example GET /api/catalog-items/sync/status
|
|
103
|
+
*/
|
|
104
|
+
export interface CatalogSyncStatusResponse {
|
|
105
|
+
/** Is a sync currently in progress? */
|
|
106
|
+
isRunning: boolean;
|
|
107
|
+
/** Last sync timestamp */
|
|
108
|
+
lastSyncAt?: string;
|
|
109
|
+
/** Last sync result */
|
|
110
|
+
lastSyncResult?: {
|
|
111
|
+
success: boolean;
|
|
112
|
+
itemsProcessed: number;
|
|
113
|
+
itemsCreated: number;
|
|
114
|
+
itemsUpdated: number;
|
|
115
|
+
itemsSkipped: number;
|
|
116
|
+
errors: string[];
|
|
117
|
+
duration: number;
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Sync trigger response
|
|
122
|
+
*
|
|
123
|
+
* @example POST /api/catalog-items/sync
|
|
124
|
+
*/
|
|
125
|
+
export interface CatalogSyncTriggerResponse {
|
|
126
|
+
/** Sync started successfully */
|
|
127
|
+
started: boolean;
|
|
128
|
+
/** Message */
|
|
129
|
+
message: string;
|
|
130
|
+
/** Estimated items to process */
|
|
131
|
+
estimatedItems?: number;
|
|
132
|
+
}
|
package/dist/contracts/index.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -6,5 +6,6 @@ export * from './types';
|
|
|
6
6
|
export * from './constants';
|
|
7
7
|
export * from './contracts';
|
|
8
8
|
export * from './validation';
|
|
9
|
-
export * from './inventory';
|
|
10
9
|
export { debugLog } from './utils/debug-logger';
|
|
10
|
+
export * from './utils/sync-field.helpers';
|
|
11
|
+
export * from './utils/qvet-catalog.helpers';
|
package/dist/index.js
CHANGED
|
@@ -23,6 +23,7 @@ __exportStar(require("./types"), exports);
|
|
|
23
23
|
__exportStar(require("./constants"), exports);
|
|
24
24
|
__exportStar(require("./contracts"), exports);
|
|
25
25
|
__exportStar(require("./validation"), exports);
|
|
26
|
-
__exportStar(require("./inventory"), exports);
|
|
27
26
|
var debug_logger_1 = require("./utils/debug-logger");
|
|
28
27
|
Object.defineProperty(exports, "debugLog", { enumerable: true, get: function () { return debug_logger_1.debugLog; } });
|
|
28
|
+
__exportStar(require("./utils/sync-field.helpers"), exports);
|
|
29
|
+
__exportStar(require("./utils/qvet-catalog.helpers"), exports);
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CatalogItem Types
|
|
3
|
+
*
|
|
4
|
+
* Type definitions for catalog items (products/services) that sync with QVET.
|
|
5
|
+
*/
|
|
6
|
+
import { SyncField } from './sync-field.types';
|
|
7
|
+
import { UsageType, SyncStatus } from '../constants/catalog-item.constants';
|
|
8
|
+
import { Warehouse } from './qvet.types';
|
|
9
|
+
/**
|
|
10
|
+
* Stock information for a specific warehouse
|
|
11
|
+
*/
|
|
12
|
+
export interface WarehouseStock {
|
|
13
|
+
/** Warehouse identifier */
|
|
14
|
+
warehouse: Warehouse;
|
|
15
|
+
/** Current stock quantity (read-only from QVET) */
|
|
16
|
+
currentStock: number;
|
|
17
|
+
/** Minimum stock level (sync with QVET) */
|
|
18
|
+
minStock: SyncField<number>;
|
|
19
|
+
/** Optimal stock level (sync with QVET) */
|
|
20
|
+
optimalStock: SyncField<number>;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* QVET Sync Data
|
|
24
|
+
*
|
|
25
|
+
* All fields that sync bidirectionally with QVET.
|
|
26
|
+
* Uses SyncField pattern for tracking pending changes.
|
|
27
|
+
*/
|
|
28
|
+
export interface CatalogItemQvetSync {
|
|
29
|
+
description: SyncField<string>;
|
|
30
|
+
barcode: SyncField<string>;
|
|
31
|
+
reference: SyncField<string>;
|
|
32
|
+
section: SyncField<string>;
|
|
33
|
+
family: SyncField<string>;
|
|
34
|
+
subfamily: SyncField<string>;
|
|
35
|
+
brand: SyncField<string>;
|
|
36
|
+
purchasePrice: SyncField<number>;
|
|
37
|
+
salePrice: SyncField<number>;
|
|
38
|
+
purchaseMargin: SyncField<number>;
|
|
39
|
+
saleMargin: SyncField<number>;
|
|
40
|
+
vatSale: SyncField<number>;
|
|
41
|
+
vatPurchase: SyncField<number>;
|
|
42
|
+
purchaseUnit: SyncField<string>;
|
|
43
|
+
saleUnit: SyncField<string>;
|
|
44
|
+
conversionFactor: SyncField<number>;
|
|
45
|
+
isActive: SyncField<boolean>;
|
|
46
|
+
stockControlType: SyncField<string>;
|
|
47
|
+
stockByWarehouse: WarehouseStock[];
|
|
48
|
+
qvetCreatedAt?: string;
|
|
49
|
+
lastSaleDate?: string;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* HVP-only Data
|
|
53
|
+
*
|
|
54
|
+
* Fields that only exist in HVP, not synced to QVET.
|
|
55
|
+
*/
|
|
56
|
+
export interface CatalogItemHvpData {
|
|
57
|
+
/** How the item is used (sales, internal, etc.) */
|
|
58
|
+
usageType: UsageType;
|
|
59
|
+
/** Internal notes */
|
|
60
|
+
notes?: string;
|
|
61
|
+
/** Internal category for HVP organization */
|
|
62
|
+
internalCategory?: string;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Complete CatalogItem
|
|
66
|
+
*
|
|
67
|
+
* Full catalog item structure as stored in MongoDB.
|
|
68
|
+
*/
|
|
69
|
+
export interface CatalogItem {
|
|
70
|
+
/** MongoDB ObjectId as string */
|
|
71
|
+
id: string;
|
|
72
|
+
/** QVET internal code (primary key from QVET) */
|
|
73
|
+
qvetCode: number;
|
|
74
|
+
/** Fields that sync with QVET */
|
|
75
|
+
qvetSync: CatalogItemQvetSync;
|
|
76
|
+
/** HVP-only fields (no sync) */
|
|
77
|
+
hvpData: CatalogItemHvpData;
|
|
78
|
+
/** Has any pending changes (computed) */
|
|
79
|
+
hasPendingChanges: boolean;
|
|
80
|
+
/** Last sync from QVET */
|
|
81
|
+
lastSyncAt: string;
|
|
82
|
+
/** Current sync status */
|
|
83
|
+
syncStatus: SyncStatus;
|
|
84
|
+
createdAt: string;
|
|
85
|
+
updatedAt: string;
|
|
86
|
+
updatedBy?: string;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Simplified CatalogItem for list views
|
|
90
|
+
*
|
|
91
|
+
* Flattened structure using effective values (pending ?? value).
|
|
92
|
+
*/
|
|
93
|
+
export interface CatalogItemListItem {
|
|
94
|
+
id: string;
|
|
95
|
+
qvetCode: number;
|
|
96
|
+
description: string;
|
|
97
|
+
section: string;
|
|
98
|
+
family: string;
|
|
99
|
+
salePrice: number;
|
|
100
|
+
isActive: boolean;
|
|
101
|
+
usageType: UsageType;
|
|
102
|
+
hasPendingChanges: boolean;
|
|
103
|
+
syncStatus: SyncStatus;
|
|
104
|
+
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -6,3 +6,6 @@ export * from './api-response.types';
|
|
|
6
6
|
export * from './company-settings.types';
|
|
7
7
|
export * from './collaborator-fiscal.types';
|
|
8
8
|
export * from './error-codes.types';
|
|
9
|
+
export * from './sync-field.types';
|
|
10
|
+
export * from './catalog-item.types';
|
|
11
|
+
export * from './qvet.types';
|
package/dist/types/index.js
CHANGED
|
@@ -22,3 +22,6 @@ __exportStar(require("./api-response.types"), exports);
|
|
|
22
22
|
__exportStar(require("./company-settings.types"), exports);
|
|
23
23
|
__exportStar(require("./collaborator-fiscal.types"), exports);
|
|
24
24
|
__exportStar(require("./error-codes.types"), exports);
|
|
25
|
+
__exportStar(require("./sync-field.types"), exports);
|
|
26
|
+
__exportStar(require("./catalog-item.types"), exports);
|
|
27
|
+
__exportStar(require("./qvet.types"), exports);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QVET Types
|
|
3
|
+
*
|
|
4
|
+
* Type definitions for QVET system integration.
|
|
5
|
+
* Constants are in constants/qvet-*.ts
|
|
6
|
+
* Helpers are in utils/qvet-catalog.helpers.ts
|
|
7
|
+
*/
|
|
8
|
+
import { QVET_SECTIONS, QVET_CATALOG } from '../constants/qvet-catalog';
|
|
9
|
+
import { WAREHOUSES } from '../constants/qvet-warehouses';
|
|
10
|
+
/**
|
|
11
|
+
* QVET Section type (derived from QVET_SECTIONS constant)
|
|
12
|
+
*/
|
|
13
|
+
export type QvetSection = (typeof QVET_SECTIONS)[keyof typeof QVET_SECTIONS];
|
|
14
|
+
/**
|
|
15
|
+
* QVET Catalog type (the complete catalog structure)
|
|
16
|
+
*/
|
|
17
|
+
export type QvetCatalog = typeof QVET_CATALOG;
|
|
18
|
+
/**
|
|
19
|
+
* Warehouse type (derived from WAREHOUSES constant)
|
|
20
|
+
*/
|
|
21
|
+
export type Warehouse = (typeof WAREHOUSES)[number];
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SyncField<T> - Bidirectional sync pattern
|
|
3
|
+
*
|
|
4
|
+
* Used for fields that sync between an external system (QVET) and HVP.
|
|
5
|
+
* Supports tracking proposed changes that are pending confirmation from the external system.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* // Clean state (no pending change)
|
|
9
|
+
* { value: "Original description", pending: undefined }
|
|
10
|
+
*
|
|
11
|
+
* // With pending change
|
|
12
|
+
* { value: "Original description", pending: "New description" }
|
|
13
|
+
*/
|
|
14
|
+
export interface SyncField<T> {
|
|
15
|
+
/** Current value from external system (QVET) */
|
|
16
|
+
value: T;
|
|
17
|
+
/** Proposed change waiting for external confirmation. undefined = no pending change */
|
|
18
|
+
pending: T | undefined;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Creates a new SyncField with an initial value
|
|
22
|
+
*/
|
|
23
|
+
export declare function createSyncField<T>(value: T): SyncField<T>;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createSyncField = createSyncField;
|
|
4
|
+
/**
|
|
5
|
+
* Creates a new SyncField with an initial value
|
|
6
|
+
*/
|
|
7
|
+
function createSyncField(value) {
|
|
8
|
+
return { value, pending: undefined };
|
|
9
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QVET Catalog Helpers
|
|
3
|
+
*
|
|
4
|
+
* Utility functions for working with QVET catalog data.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Get all families for a section
|
|
8
|
+
*/
|
|
9
|
+
export declare function getFamiliesForSection(section: string): string[];
|
|
10
|
+
/**
|
|
11
|
+
* Get all subfamilies for a section + family combination
|
|
12
|
+
*/
|
|
13
|
+
export declare function getSubfamiliesForFamily(section: string, family: string): readonly string[];
|
|
14
|
+
/**
|
|
15
|
+
* Check if a section exists in the catalog
|
|
16
|
+
*/
|
|
17
|
+
export declare function isValidSection(section: string): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Check if a section is countable (physical inventory)
|
|
20
|
+
*/
|
|
21
|
+
export declare function isCountableSection(section: string): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Check if a family exists within a section
|
|
24
|
+
*/
|
|
25
|
+
export declare function isValidFamily(section: string, family: string): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Check if a subfamily exists within a section + family
|
|
28
|
+
*/
|
|
29
|
+
export declare function isValidSubfamily(section: string, family: string, subfamily: string): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Get sort order for a section (lower = higher priority)
|
|
32
|
+
*/
|
|
33
|
+
export declare function getSectionSortOrder(section: string): number;
|
|
34
|
+
/**
|
|
35
|
+
* Check if an item requires expiration date tracking based on section and family
|
|
36
|
+
*/
|
|
37
|
+
export declare function requiresExpirationDate(section: string, family: string): boolean;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* QVET Catalog Helpers
|
|
4
|
+
*
|
|
5
|
+
* Utility functions for working with QVET catalog data.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.getFamiliesForSection = getFamiliesForSection;
|
|
9
|
+
exports.getSubfamiliesForFamily = getSubfamiliesForFamily;
|
|
10
|
+
exports.isValidSection = isValidSection;
|
|
11
|
+
exports.isCountableSection = isCountableSection;
|
|
12
|
+
exports.isValidFamily = isValidFamily;
|
|
13
|
+
exports.isValidSubfamily = isValidSubfamily;
|
|
14
|
+
exports.getSectionSortOrder = getSectionSortOrder;
|
|
15
|
+
exports.requiresExpirationDate = requiresExpirationDate;
|
|
16
|
+
const qvet_catalog_1 = require("../constants/qvet-catalog");
|
|
17
|
+
const qvet_inventory_1 = require("../constants/qvet-inventory");
|
|
18
|
+
// =============================================================================
|
|
19
|
+
// CATALOG NAVIGATION
|
|
20
|
+
// =============================================================================
|
|
21
|
+
/**
|
|
22
|
+
* Get all families for a section
|
|
23
|
+
*/
|
|
24
|
+
function getFamiliesForSection(section) {
|
|
25
|
+
const sectionData = qvet_catalog_1.QVET_CATALOG[section];
|
|
26
|
+
if (!sectionData)
|
|
27
|
+
return [];
|
|
28
|
+
return Object.keys(sectionData);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Get all subfamilies for a section + family combination
|
|
32
|
+
*/
|
|
33
|
+
function getSubfamiliesForFamily(section, family) {
|
|
34
|
+
const sectionData = qvet_catalog_1.QVET_CATALOG[section];
|
|
35
|
+
if (!sectionData)
|
|
36
|
+
return [];
|
|
37
|
+
const familyData = sectionData[family];
|
|
38
|
+
if (!familyData)
|
|
39
|
+
return [];
|
|
40
|
+
return familyData;
|
|
41
|
+
}
|
|
42
|
+
// =============================================================================
|
|
43
|
+
// VALIDATION
|
|
44
|
+
// =============================================================================
|
|
45
|
+
/**
|
|
46
|
+
* Check if a section exists in the catalog
|
|
47
|
+
*/
|
|
48
|
+
function isValidSection(section) {
|
|
49
|
+
return section in qvet_catalog_1.QVET_CATALOG;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Check if a section is countable (physical inventory)
|
|
53
|
+
*/
|
|
54
|
+
function isCountableSection(section) {
|
|
55
|
+
return qvet_inventory_1.INVENTORY_COUNTABLE_SECTIONS_SET.has(section);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Check if a family exists within a section
|
|
59
|
+
*/
|
|
60
|
+
function isValidFamily(section, family) {
|
|
61
|
+
const sectionData = qvet_catalog_1.QVET_CATALOG[section];
|
|
62
|
+
if (!sectionData)
|
|
63
|
+
return false;
|
|
64
|
+
return family in sectionData;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Check if a subfamily exists within a section + family
|
|
68
|
+
*/
|
|
69
|
+
function isValidSubfamily(section, family, subfamily) {
|
|
70
|
+
const subfamilies = getSubfamiliesForFamily(section, family);
|
|
71
|
+
return subfamilies.includes(subfamily);
|
|
72
|
+
}
|
|
73
|
+
// =============================================================================
|
|
74
|
+
// SORTING
|
|
75
|
+
// =============================================================================
|
|
76
|
+
/**
|
|
77
|
+
* Get sort order for a section (lower = higher priority)
|
|
78
|
+
*/
|
|
79
|
+
function getSectionSortOrder(section) {
|
|
80
|
+
const index = qvet_inventory_1.SECTION_SORT_PRIORITY.indexOf(section);
|
|
81
|
+
if (index >= 0) {
|
|
82
|
+
return index;
|
|
83
|
+
}
|
|
84
|
+
// Non-priority sections come after, sorted alphabetically
|
|
85
|
+
return qvet_inventory_1.SECTION_SORT_PRIORITY.length + section.charCodeAt(0);
|
|
86
|
+
}
|
|
87
|
+
// =============================================================================
|
|
88
|
+
// EXPIRATION DATE
|
|
89
|
+
// =============================================================================
|
|
90
|
+
/**
|
|
91
|
+
* Check if an item requires expiration date tracking based on section and family
|
|
92
|
+
*/
|
|
93
|
+
function requiresExpirationDate(section, family) {
|
|
94
|
+
// Full section requires expiration
|
|
95
|
+
if (qvet_inventory_1.SECTIONS_REQUIRE_EXPIRATION.includes(section)) {
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
// Check specific families within sections
|
|
99
|
+
const familiesForSection = qvet_inventory_1.FAMILIES_REQUIRE_EXPIRATION[section];
|
|
100
|
+
if (familiesForSection) {
|
|
101
|
+
return familiesForSection.includes(family);
|
|
102
|
+
}
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { SyncField } from '../types/sync-field.types';
|
|
2
|
+
/**
|
|
3
|
+
* Get the effective value (pending if exists, otherwise current)
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* getValue({ value: "old", pending: "new" }) // "new"
|
|
7
|
+
* getValue({ value: "old", pending: undefined }) // "old"
|
|
8
|
+
*/
|
|
9
|
+
export declare function getValue<T>(field: SyncField<T>): T;
|
|
10
|
+
/**
|
|
11
|
+
* Get the external system value (always the synced one, ignores pending)
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* getQvetValue({ value: "old", pending: "new" }) // "old"
|
|
15
|
+
*/
|
|
16
|
+
export declare function getQvetValue<T>(field: SyncField<T>): T;
|
|
17
|
+
/**
|
|
18
|
+
* Check if field has a pending change
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* hasPending({ value: "old", pending: "new" }) // true
|
|
22
|
+
* hasPending({ value: "old", pending: undefined }) // false
|
|
23
|
+
*/
|
|
24
|
+
export declare function hasPending<T>(field: SyncField<T>): boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Check if pending value differs from current value
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* isDirty({ value: "old", pending: "new" }) // true
|
|
30
|
+
* isDirty({ value: "old", pending: "old" }) // false (same value)
|
|
31
|
+
* isDirty({ value: "old", pending: undefined }) // false
|
|
32
|
+
*/
|
|
33
|
+
export declare function isDirty<T>(field: SyncField<T>): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Set a pending value. If newValue equals current value, clears pending instead.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* setPending({ value: "old", pending: undefined }, "new")
|
|
39
|
+
* // { value: "old", pending: "new" }
|
|
40
|
+
*
|
|
41
|
+
* setPending({ value: "old", pending: undefined }, "old")
|
|
42
|
+
* // { value: "old", pending: undefined } - no change needed
|
|
43
|
+
*/
|
|
44
|
+
export declare function setPending<T>(field: SyncField<T>, newValue: T): SyncField<T>;
|
|
45
|
+
/**
|
|
46
|
+
* Clear pending (revert to external system value)
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* clearPending({ value: "old", pending: "new" })
|
|
50
|
+
* // { value: "old", pending: undefined }
|
|
51
|
+
*/
|
|
52
|
+
export declare function clearPending<T>(field: SyncField<T>): SyncField<T>;
|
|
53
|
+
/**
|
|
54
|
+
* Confirm sync from external system.
|
|
55
|
+
* Updates the value to the new external value and clears pending.
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* // External system confirmed our change
|
|
59
|
+
* confirmSync({ value: "old", pending: "new" }, "new")
|
|
60
|
+
* // { value: "new", pending: undefined }
|
|
61
|
+
*
|
|
62
|
+
* // External system has different value
|
|
63
|
+
* confirmSync({ value: "old", pending: undefined }, "updated")
|
|
64
|
+
* // { value: "updated", pending: undefined }
|
|
65
|
+
*/
|
|
66
|
+
export declare function confirmSync<T>(field: SyncField<T>, externalValue: T): SyncField<T>;
|
|
67
|
+
/**
|
|
68
|
+
* Apply sync from external system, preserving pending if it differs from new value.
|
|
69
|
+
* Use this when you want to keep local changes during a sync.
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* // We have pending, external hasn't changed yet
|
|
73
|
+
* applySyncPreservePending({ value: "old", pending: "new" }, "old")
|
|
74
|
+
* // { value: "old", pending: "new" } - pending preserved
|
|
75
|
+
*
|
|
76
|
+
* // External confirmed our change
|
|
77
|
+
* applySyncPreservePending({ value: "old", pending: "new" }, "new")
|
|
78
|
+
* // { value: "new", pending: undefined } - pending cleared
|
|
79
|
+
*
|
|
80
|
+
* // External changed to something else (conflict scenario)
|
|
81
|
+
* applySyncPreservePending({ value: "old", pending: "new" }, "other")
|
|
82
|
+
* // { value: "other", pending: "new" } - pending preserved, value updated
|
|
83
|
+
*/
|
|
84
|
+
export declare function applySyncPreservePending<T>(field: SyncField<T>, externalValue: T): SyncField<T>;
|