@litusarchieve18/agricore-utils 1.0.25 → 1.0.27
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/README.md +8 -0
- package/dist/{chunk-OFNQBQP4.mjs → chunk-Y5KVDVNY.mjs} +4 -2
- package/dist/{constants-D5sndLi7.d.mts → constants-dvGSNAt_.d.mts} +62 -1
- package/dist/{constants-D5sndLi7.d.ts → constants-dvGSNAt_.d.ts} +62 -1
- package/dist/constants.d.mts +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +4 -2
- package/dist/constants.mjs +1 -1
- package/dist/index.d.mts +29 -6
- package/dist/index.d.ts +29 -6
- package/dist/index.js +212 -9
- package/dist/index.mjs +205 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -183,6 +183,14 @@ Run the publish command:
|
|
|
183
183
|
Bash
|
|
184
184
|
npm publish --access public
|
|
185
185
|
📝 Version History
|
|
186
|
+
V1.0.29 —
|
|
187
|
+
|
|
188
|
+
V1.0.28 —
|
|
189
|
+
|
|
190
|
+
V1.0.27 — adding get price helper, and adding adapter pattern to handle the conflicting price
|
|
191
|
+
|
|
192
|
+
V1.0.26 — updating evaluatebaseAccess to try look up the parent scope given the current scope is not
|
|
193
|
+
|
|
186
194
|
V1.0.25 — adding privileges and changing RESOURCE_PATHS
|
|
187
195
|
|
|
188
196
|
V1.0.24 — splitting role dependency into a new file
|
|
@@ -17,7 +17,8 @@ var CAT = {
|
|
|
17
17
|
SUBSCRIPTION: "1",
|
|
18
18
|
PERMISSION: "2",
|
|
19
19
|
VALIDATION: "3",
|
|
20
|
-
INTEGRITY: "4"
|
|
20
|
+
INTEGRITY: "4",
|
|
21
|
+
DATABASE: "5"
|
|
21
22
|
};
|
|
22
23
|
var ACT = {
|
|
23
24
|
GENERIC: "00",
|
|
@@ -63,7 +64,8 @@ var ERROR_DICTIONARY = {
|
|
|
63
64
|
"400": { title: "Data Conflict", text: "A conflict occurred with an existing record. Please review and try again." },
|
|
64
65
|
"402": { title: "Duplicate Entry", text: "A record with this name or identifier already exists." },
|
|
65
66
|
"403": { title: "Edit Conflict", text: "This record was recently updated by someone else. Please refresh and try again." },
|
|
66
|
-
"404": { title: "Record In Use", text: "This record cannot be deleted because it is referenced by other data." }
|
|
67
|
+
"404": { title: "Record In Use", text: "This record cannot be deleted because it is referenced by other data." },
|
|
68
|
+
"405": { title: "Pricing Conflict", text: "Multiple active prices were found for this product. An administrator must resolve the conflict before this price can be used." }
|
|
67
69
|
};
|
|
68
70
|
var UserRole = {
|
|
69
71
|
OWNER: "owner",
|
|
@@ -206,6 +206,66 @@ type ProductUnitType = typeof ProductUnit[keyof typeof ProductUnit];
|
|
|
206
206
|
type CatalogSyncStatusType = typeof CatalogSyncStatus[keyof typeof CatalogSyncStatus];
|
|
207
207
|
type StorageType = typeof StoragePhases[keyof typeof StoragePhases];
|
|
208
208
|
type StockPolicyTierType = typeof StockPolicyTier[keyof typeof StockPolicyTier];
|
|
209
|
+
interface PricingContext {
|
|
210
|
+
productId: string;
|
|
211
|
+
companyId: string;
|
|
212
|
+
facilityId?: string;
|
|
213
|
+
accountCode?: string | null;
|
|
214
|
+
contractReferenceNumber?: string | null;
|
|
215
|
+
evaluationDate?: Date;
|
|
216
|
+
}
|
|
217
|
+
interface PriceResolutionResult {
|
|
218
|
+
unitPriceCents: number;
|
|
219
|
+
minimumOrderQuantity: number;
|
|
220
|
+
leadTimeDays: number | null;
|
|
221
|
+
priceSource: 'EXPLICIT_CONTRACT' | 'ACCOUNT_MATRIX' | 'INTERNAL_DEFAULT' | 'PRODUCT_FALLBACK';
|
|
222
|
+
sourceRecordId: string;
|
|
223
|
+
}
|
|
224
|
+
interface PricingDataSource {
|
|
225
|
+
findContractPrice(args: {
|
|
226
|
+
productId: string;
|
|
227
|
+
contractReferenceNumber: string;
|
|
228
|
+
targetDate: Date;
|
|
229
|
+
}): Promise<MatrixRow | null>;
|
|
230
|
+
findAccountPrice(args: {
|
|
231
|
+
productId: string;
|
|
232
|
+
accountCode: string;
|
|
233
|
+
targetDate: Date;
|
|
234
|
+
}): Promise<MatrixRow | null>;
|
|
235
|
+
findDefaultPrice(args: {
|
|
236
|
+
productId: string;
|
|
237
|
+
targetDate: Date;
|
|
238
|
+
}): Promise<MatrixRow | null>;
|
|
239
|
+
findBaseProduct(args: {
|
|
240
|
+
productId: string;
|
|
241
|
+
}): Promise<BaseProductRow | null>;
|
|
242
|
+
}
|
|
243
|
+
interface MatrixRow {
|
|
244
|
+
canonicalId: string;
|
|
245
|
+
matrixUnitPrice: number;
|
|
246
|
+
minimumOrderQuantity: number;
|
|
247
|
+
leadTimeDays: number | null;
|
|
248
|
+
}
|
|
249
|
+
interface BaseProductRow {
|
|
250
|
+
canonicalId: string;
|
|
251
|
+
unitPriceCents: number;
|
|
252
|
+
}
|
|
253
|
+
type Tier = 'EXPLICIT_CONTRACT' | 'ACCOUNT_MATRIX' | 'INTERNAL_DEFAULT';
|
|
254
|
+
interface PriceConflictCandidate {
|
|
255
|
+
sourceRecordId: string;
|
|
256
|
+
unitPriceCents: number;
|
|
257
|
+
validFrom: string;
|
|
258
|
+
validTo: string | null;
|
|
259
|
+
}
|
|
260
|
+
interface ShadowedCleanMatch {
|
|
261
|
+
tier: Tier;
|
|
262
|
+
unitPriceCents: number;
|
|
263
|
+
sourceRecordId: string;
|
|
264
|
+
matchedOn: {
|
|
265
|
+
accountCode?: string;
|
|
266
|
+
contractReferenceNumber?: string;
|
|
267
|
+
};
|
|
268
|
+
}
|
|
209
269
|
|
|
210
270
|
declare const MOD: {
|
|
211
271
|
readonly OPERATIONS: "OPR";
|
|
@@ -227,6 +287,7 @@ declare const CAT: {
|
|
|
227
287
|
readonly PERMISSION: "2";
|
|
228
288
|
readonly VALIDATION: "3";
|
|
229
289
|
readonly INTEGRITY: "4";
|
|
290
|
+
readonly DATABASE: "5";
|
|
230
291
|
};
|
|
231
292
|
declare const ACT: {
|
|
232
293
|
readonly GENERIC: "00";
|
|
@@ -685,4 +746,4 @@ declare const StockPolicyTier: {
|
|
|
685
746
|
readonly BY_LOCATION: 3;
|
|
686
747
|
};
|
|
687
748
|
|
|
688
|
-
export {
|
|
749
|
+
export { MimeType as $, ACCESS_RANK as A, BarcodeNamespace as B, CAR_SUPPORTED_EVIDENCE_MIME_TYPES as C, type CompanyConfig as D, type CorrectiveActionCompileTaskMetadata as E, type CorrectiveActionGenerationSource as F, type CorrectiveActionStatus as G, CropCycleStatus as H, DOCUMENT_DISPLAY_INFO as I, DOCUMENT_MENU_MAP as J, DocumentCategory as K, DocumentType as L, ERROR_DICTIONARY as M, EVERYONE as N, EmitterType as O, EnabledModule as P, FORM_REGISTRY as Q, FacilityType as R, type FileUploadTaskType as S, type FormMeta as T, HarvestMethod as U, type InfrastructureFields as V, LinkedStatus as W, MOD as X, MODULE_LABELS as Y, type MatrixRow as Z, MicroclimateZone as _, ACT as a, type ModCode as a0, NettingType as a1, type NormalizedSignatureValue as a2, NotificationMode as a3, OrderStatus as a4, type PermissionLevel as a5, PhysicalState as a6, PlantingMethod as a7, type PriceConflictCandidate as a8, type PriceResolutionResult as a9, type StockPolicyTierType as aA, StoragePhases as aB, type StorageType as aC, TASK_TYPE_REGISTRY as aD, TEMPLATE_SCOPE_LEVELS as aE, type TabConfig as aF, TargetEntityType as aG, TaskStatus as aH, TaskType as aI, type TaskTypeConfig as aJ, type TemplateScopeLevel as aK, type Tier as aL, TransactionType as aM, TrellisType as aN, UserPrivilege as aO, type UserPrivilegeType as aP, UserRole as aQ, type UserRoleAssignment as aR, type UserRoleType as aS, VigorRating as aT, WaterSource as aU, type WireSession as aV, type PricingContext as aa, type PricingDataSource as ab, ProductType as ac, type ProductTypeType as ad, ProductUnit as ae, type ProductUnitType as af, RESOURCE_MODULE_MAP as ag, RESOURCE_PATHS as ah, RESOURCE_REQUIREMENTS as ai, ROLE_SECTIONS_BY_KEY as aj, RelationshipType as ak, type ResolvedError as al, type ResourceOverrides as am, type ResourceRequirement as an, type RoleAssignment as ao, RowOrientation as ap, SENTINEL_UUID as aq, SESSION_SCHEMA as ar, SIGNATURE_MODES as as, type SchemaField as at, type ScopeType as au, type ShadowedCleanMatch as av, type SignatureInput as aw, type SignatureMode as ax, SoilTexture as ay, StockPolicyTier as az, AccessLevel as b, type AccessLevelType as c, type AgriCoreSession as d, ApprovalStatus as e, ApprovalType as f, AuditStatus as g, AuditType as h, type AuthScopeFields as i, type AvailableScope as j, type BarcodeNamespaceType as k, BarcodeType as l, type BarcodeTypeType as m, type Base44Id as n, type BaseProductRow as o, CAR_SUPPORTED_SIGNATURE_MIME_TYPES as p, CAT as q, CATEGORY_TABS as r, CORRECTIVE_ACTION_GENERATION_SOURCES as s, CORRECTIVE_ACTION_STATUSES as t, type CanonicalId as u, type CarSupportedEvidenceMimeType as v, type CarSupportedSignatureMimeType as w, type CarTemplateFileMetadata as x, CatalogSyncStatus as y, type CatalogSyncStatusType as z };
|
|
@@ -206,6 +206,66 @@ type ProductUnitType = typeof ProductUnit[keyof typeof ProductUnit];
|
|
|
206
206
|
type CatalogSyncStatusType = typeof CatalogSyncStatus[keyof typeof CatalogSyncStatus];
|
|
207
207
|
type StorageType = typeof StoragePhases[keyof typeof StoragePhases];
|
|
208
208
|
type StockPolicyTierType = typeof StockPolicyTier[keyof typeof StockPolicyTier];
|
|
209
|
+
interface PricingContext {
|
|
210
|
+
productId: string;
|
|
211
|
+
companyId: string;
|
|
212
|
+
facilityId?: string;
|
|
213
|
+
accountCode?: string | null;
|
|
214
|
+
contractReferenceNumber?: string | null;
|
|
215
|
+
evaluationDate?: Date;
|
|
216
|
+
}
|
|
217
|
+
interface PriceResolutionResult {
|
|
218
|
+
unitPriceCents: number;
|
|
219
|
+
minimumOrderQuantity: number;
|
|
220
|
+
leadTimeDays: number | null;
|
|
221
|
+
priceSource: 'EXPLICIT_CONTRACT' | 'ACCOUNT_MATRIX' | 'INTERNAL_DEFAULT' | 'PRODUCT_FALLBACK';
|
|
222
|
+
sourceRecordId: string;
|
|
223
|
+
}
|
|
224
|
+
interface PricingDataSource {
|
|
225
|
+
findContractPrice(args: {
|
|
226
|
+
productId: string;
|
|
227
|
+
contractReferenceNumber: string;
|
|
228
|
+
targetDate: Date;
|
|
229
|
+
}): Promise<MatrixRow | null>;
|
|
230
|
+
findAccountPrice(args: {
|
|
231
|
+
productId: string;
|
|
232
|
+
accountCode: string;
|
|
233
|
+
targetDate: Date;
|
|
234
|
+
}): Promise<MatrixRow | null>;
|
|
235
|
+
findDefaultPrice(args: {
|
|
236
|
+
productId: string;
|
|
237
|
+
targetDate: Date;
|
|
238
|
+
}): Promise<MatrixRow | null>;
|
|
239
|
+
findBaseProduct(args: {
|
|
240
|
+
productId: string;
|
|
241
|
+
}): Promise<BaseProductRow | null>;
|
|
242
|
+
}
|
|
243
|
+
interface MatrixRow {
|
|
244
|
+
canonicalId: string;
|
|
245
|
+
matrixUnitPrice: number;
|
|
246
|
+
minimumOrderQuantity: number;
|
|
247
|
+
leadTimeDays: number | null;
|
|
248
|
+
}
|
|
249
|
+
interface BaseProductRow {
|
|
250
|
+
canonicalId: string;
|
|
251
|
+
unitPriceCents: number;
|
|
252
|
+
}
|
|
253
|
+
type Tier = 'EXPLICIT_CONTRACT' | 'ACCOUNT_MATRIX' | 'INTERNAL_DEFAULT';
|
|
254
|
+
interface PriceConflictCandidate {
|
|
255
|
+
sourceRecordId: string;
|
|
256
|
+
unitPriceCents: number;
|
|
257
|
+
validFrom: string;
|
|
258
|
+
validTo: string | null;
|
|
259
|
+
}
|
|
260
|
+
interface ShadowedCleanMatch {
|
|
261
|
+
tier: Tier;
|
|
262
|
+
unitPriceCents: number;
|
|
263
|
+
sourceRecordId: string;
|
|
264
|
+
matchedOn: {
|
|
265
|
+
accountCode?: string;
|
|
266
|
+
contractReferenceNumber?: string;
|
|
267
|
+
};
|
|
268
|
+
}
|
|
209
269
|
|
|
210
270
|
declare const MOD: {
|
|
211
271
|
readonly OPERATIONS: "OPR";
|
|
@@ -227,6 +287,7 @@ declare const CAT: {
|
|
|
227
287
|
readonly PERMISSION: "2";
|
|
228
288
|
readonly VALIDATION: "3";
|
|
229
289
|
readonly INTEGRITY: "4";
|
|
290
|
+
readonly DATABASE: "5";
|
|
230
291
|
};
|
|
231
292
|
declare const ACT: {
|
|
232
293
|
readonly GENERIC: "00";
|
|
@@ -685,4 +746,4 @@ declare const StockPolicyTier: {
|
|
|
685
746
|
readonly BY_LOCATION: 3;
|
|
686
747
|
};
|
|
687
748
|
|
|
688
|
-
export {
|
|
749
|
+
export { MimeType as $, ACCESS_RANK as A, BarcodeNamespace as B, CAR_SUPPORTED_EVIDENCE_MIME_TYPES as C, type CompanyConfig as D, type CorrectiveActionCompileTaskMetadata as E, type CorrectiveActionGenerationSource as F, type CorrectiveActionStatus as G, CropCycleStatus as H, DOCUMENT_DISPLAY_INFO as I, DOCUMENT_MENU_MAP as J, DocumentCategory as K, DocumentType as L, ERROR_DICTIONARY as M, EVERYONE as N, EmitterType as O, EnabledModule as P, FORM_REGISTRY as Q, FacilityType as R, type FileUploadTaskType as S, type FormMeta as T, HarvestMethod as U, type InfrastructureFields as V, LinkedStatus as W, MOD as X, MODULE_LABELS as Y, type MatrixRow as Z, MicroclimateZone as _, ACT as a, type ModCode as a0, NettingType as a1, type NormalizedSignatureValue as a2, NotificationMode as a3, OrderStatus as a4, type PermissionLevel as a5, PhysicalState as a6, PlantingMethod as a7, type PriceConflictCandidate as a8, type PriceResolutionResult as a9, type StockPolicyTierType as aA, StoragePhases as aB, type StorageType as aC, TASK_TYPE_REGISTRY as aD, TEMPLATE_SCOPE_LEVELS as aE, type TabConfig as aF, TargetEntityType as aG, TaskStatus as aH, TaskType as aI, type TaskTypeConfig as aJ, type TemplateScopeLevel as aK, type Tier as aL, TransactionType as aM, TrellisType as aN, UserPrivilege as aO, type UserPrivilegeType as aP, UserRole as aQ, type UserRoleAssignment as aR, type UserRoleType as aS, VigorRating as aT, WaterSource as aU, type WireSession as aV, type PricingContext as aa, type PricingDataSource as ab, ProductType as ac, type ProductTypeType as ad, ProductUnit as ae, type ProductUnitType as af, RESOURCE_MODULE_MAP as ag, RESOURCE_PATHS as ah, RESOURCE_REQUIREMENTS as ai, ROLE_SECTIONS_BY_KEY as aj, RelationshipType as ak, type ResolvedError as al, type ResourceOverrides as am, type ResourceRequirement as an, type RoleAssignment as ao, RowOrientation as ap, SENTINEL_UUID as aq, SESSION_SCHEMA as ar, SIGNATURE_MODES as as, type SchemaField as at, type ScopeType as au, type ShadowedCleanMatch as av, type SignatureInput as aw, type SignatureMode as ax, SoilTexture as ay, StockPolicyTier as az, AccessLevel as b, type AccessLevelType as c, type AgriCoreSession as d, ApprovalStatus as e, ApprovalType as f, AuditStatus as g, AuditType as h, type AuthScopeFields as i, type AvailableScope as j, type BarcodeNamespaceType as k, BarcodeType as l, type BarcodeTypeType as m, type Base44Id as n, type BaseProductRow as o, CAR_SUPPORTED_SIGNATURE_MIME_TYPES as p, CAT as q, CATEGORY_TABS as r, CORRECTIVE_ACTION_GENERATION_SOURCES as s, CORRECTIVE_ACTION_STATUSES as t, type CanonicalId as u, type CarSupportedEvidenceMimeType as v, type CarSupportedSignatureMimeType as w, type CarTemplateFileMetadata as x, CatalogSyncStatus as y, type CatalogSyncStatusType as z };
|
package/dist/constants.d.mts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { A as ACCESS_RANK, a as ACT, b as AccessLevel, e as ApprovalStatus, f as ApprovalType, g as AuditStatus, h as AuditType, B as BarcodeNamespace, l as BarcodeType, C as CAR_SUPPORTED_EVIDENCE_MIME_TYPES,
|
|
1
|
+
export { A as ACCESS_RANK, a as ACT, b as AccessLevel, e as ApprovalStatus, f as ApprovalType, g as AuditStatus, h as AuditType, B as BarcodeNamespace, l as BarcodeType, C as CAR_SUPPORTED_EVIDENCE_MIME_TYPES, p as CAR_SUPPORTED_SIGNATURE_MIME_TYPES, q as CAT, r as CATEGORY_TABS, s as CORRECTIVE_ACTION_GENERATION_SOURCES, t as CORRECTIVE_ACTION_STATUSES, y as CatalogSyncStatus, H as CropCycleStatus, I as DOCUMENT_DISPLAY_INFO, J as DOCUMENT_MENU_MAP, K as DocumentCategory, L as DocumentType, M as ERROR_DICTIONARY, N as EVERYONE, O as EmitterType, P as EnabledModule, Q as FORM_REGISTRY, R as FacilityType, U as HarvestMethod, W as LinkedStatus, X as MOD, Y as MODULE_LABELS, _ as MicroclimateZone, $ as MimeType, a0 as ModCode, a1 as NettingType, a3 as NotificationMode, a4 as OrderStatus, a6 as PhysicalState, a7 as PlantingMethod, ac as ProductType, ae as ProductUnit, ag as RESOURCE_MODULE_MAP, ah as RESOURCE_PATHS, ai as RESOURCE_REQUIREMENTS, aj as ROLE_SECTIONS_BY_KEY, ak as RelationshipType, an as ResourceRequirement, ap as RowOrientation, aq as SENTINEL_UUID, ar as SESSION_SCHEMA, as as SIGNATURE_MODES, ay as SoilTexture, az as StockPolicyTier, aB as StoragePhases, aD as TASK_TYPE_REGISTRY, aE as TEMPLATE_SCOPE_LEVELS, aG as TargetEntityType, aH as TaskStatus, aI as TaskType, aM as TransactionType, aN as TrellisType, aO as UserPrivilege, aQ as UserRole, aT as VigorRating, aU as WaterSource } from './constants-dvGSNAt_.mjs';
|
package/dist/constants.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { A as ACCESS_RANK, a as ACT, b as AccessLevel, e as ApprovalStatus, f as ApprovalType, g as AuditStatus, h as AuditType, B as BarcodeNamespace, l as BarcodeType, C as CAR_SUPPORTED_EVIDENCE_MIME_TYPES,
|
|
1
|
+
export { A as ACCESS_RANK, a as ACT, b as AccessLevel, e as ApprovalStatus, f as ApprovalType, g as AuditStatus, h as AuditType, B as BarcodeNamespace, l as BarcodeType, C as CAR_SUPPORTED_EVIDENCE_MIME_TYPES, p as CAR_SUPPORTED_SIGNATURE_MIME_TYPES, q as CAT, r as CATEGORY_TABS, s as CORRECTIVE_ACTION_GENERATION_SOURCES, t as CORRECTIVE_ACTION_STATUSES, y as CatalogSyncStatus, H as CropCycleStatus, I as DOCUMENT_DISPLAY_INFO, J as DOCUMENT_MENU_MAP, K as DocumentCategory, L as DocumentType, M as ERROR_DICTIONARY, N as EVERYONE, O as EmitterType, P as EnabledModule, Q as FORM_REGISTRY, R as FacilityType, U as HarvestMethod, W as LinkedStatus, X as MOD, Y as MODULE_LABELS, _ as MicroclimateZone, $ as MimeType, a0 as ModCode, a1 as NettingType, a3 as NotificationMode, a4 as OrderStatus, a6 as PhysicalState, a7 as PlantingMethod, ac as ProductType, ae as ProductUnit, ag as RESOURCE_MODULE_MAP, ah as RESOURCE_PATHS, ai as RESOURCE_REQUIREMENTS, aj as ROLE_SECTIONS_BY_KEY, ak as RelationshipType, an as ResourceRequirement, ap as RowOrientation, aq as SENTINEL_UUID, ar as SESSION_SCHEMA, as as SIGNATURE_MODES, ay as SoilTexture, az as StockPolicyTier, aB as StoragePhases, aD as TASK_TYPE_REGISTRY, aE as TEMPLATE_SCOPE_LEVELS, aG as TargetEntityType, aH as TaskStatus, aI as TaskType, aM as TransactionType, aN as TrellisType, aO as UserPrivilege, aQ as UserRole, aT as VigorRating, aU as WaterSource } from './constants-dvGSNAt_.js';
|
package/dist/constants.js
CHANGED
|
@@ -103,7 +103,8 @@ var CAT = {
|
|
|
103
103
|
SUBSCRIPTION: "1",
|
|
104
104
|
PERMISSION: "2",
|
|
105
105
|
VALIDATION: "3",
|
|
106
|
-
INTEGRITY: "4"
|
|
106
|
+
INTEGRITY: "4",
|
|
107
|
+
DATABASE: "5"
|
|
107
108
|
};
|
|
108
109
|
var ACT = {
|
|
109
110
|
GENERIC: "00",
|
|
@@ -149,7 +150,8 @@ var ERROR_DICTIONARY = {
|
|
|
149
150
|
"400": { title: "Data Conflict", text: "A conflict occurred with an existing record. Please review and try again." },
|
|
150
151
|
"402": { title: "Duplicate Entry", text: "A record with this name or identifier already exists." },
|
|
151
152
|
"403": { title: "Edit Conflict", text: "This record was recently updated by someone else. Please refresh and try again." },
|
|
152
|
-
"404": { title: "Record In Use", text: "This record cannot be deleted because it is referenced by other data." }
|
|
153
|
+
"404": { title: "Record In Use", text: "This record cannot be deleted because it is referenced by other data." },
|
|
154
|
+
"405": { title: "Pricing Conflict", text: "Multiple active prices were found for this product. An administrator must resolve the conflict before this price can be used." }
|
|
153
155
|
};
|
|
154
156
|
var UserRole = {
|
|
155
157
|
OWNER: "owner",
|
package/dist/constants.mjs
CHANGED
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { d as AgriCoreSession,
|
|
2
|
-
export { A as ACCESS_RANK, a as ACT, b as AccessLevel, e as ApprovalStatus, f as ApprovalType, g as AuditStatus, h as AuditType, i as AuthScopeFields, B as BarcodeNamespace, k as BarcodeNamespaceType, l as BarcodeType, m as BarcodeTypeType, n as Base44Id, C as CAR_SUPPORTED_EVIDENCE_MIME_TYPES,
|
|
1
|
+
import { d as AgriCoreSession, aR as UserRoleAssignment, aL as Tier, a8 as PriceConflictCandidate, av as ShadowedCleanMatch, ab as PricingDataSource, aa as PricingContext, a9 as PriceResolutionResult, au as ScopeType, aV as WireSession, an as ResourceRequirement, c as AccessLevelType, j as AvailableScope, V as InfrastructureFields, T as FormMeta, aF as TabConfig, al as ResolvedError } from './constants-dvGSNAt_.mjs';
|
|
2
|
+
export { A as ACCESS_RANK, a as ACT, b as AccessLevel, e as ApprovalStatus, f as ApprovalType, g as AuditStatus, h as AuditType, i as AuthScopeFields, B as BarcodeNamespace, k as BarcodeNamespaceType, l as BarcodeType, m as BarcodeTypeType, n as Base44Id, o as BaseProductRow, C as CAR_SUPPORTED_EVIDENCE_MIME_TYPES, p as CAR_SUPPORTED_SIGNATURE_MIME_TYPES, q as CAT, r as CATEGORY_TABS, s as CORRECTIVE_ACTION_GENERATION_SOURCES, t as CORRECTIVE_ACTION_STATUSES, u as CanonicalId, v as CarSupportedEvidenceMimeType, w as CarSupportedSignatureMimeType, x as CarTemplateFileMetadata, y as CatalogSyncStatus, z as CatalogSyncStatusType, D as CompanyConfig, E as CorrectiveActionCompileTaskMetadata, F as CorrectiveActionGenerationSource, G as CorrectiveActionStatus, H as CropCycleStatus, I as DOCUMENT_DISPLAY_INFO, J as DOCUMENT_MENU_MAP, K as DocumentCategory, L as DocumentType, M as ERROR_DICTIONARY, N as EVERYONE, O as EmitterType, P as EnabledModule, Q as FORM_REGISTRY, R as FacilityType, S as FileUploadTaskType, U as HarvestMethod, W as LinkedStatus, X as MOD, Y as MODULE_LABELS, Z as MatrixRow, _ as MicroclimateZone, $ as MimeType, a0 as ModCode, a1 as NettingType, a2 as NormalizedSignatureValue, a3 as NotificationMode, a4 as OrderStatus, a5 as PermissionLevel, a6 as PhysicalState, a7 as PlantingMethod, ac as ProductType, ad as ProductTypeType, ae as ProductUnit, af as ProductUnitType, ag as RESOURCE_MODULE_MAP, ah as RESOURCE_PATHS, ai as RESOURCE_REQUIREMENTS, aj as ROLE_SECTIONS_BY_KEY, ak as RelationshipType, am as ResourceOverrides, ao as RoleAssignment, ap as RowOrientation, aq as SENTINEL_UUID, ar as SESSION_SCHEMA, as as SIGNATURE_MODES, at as SchemaField, aw as SignatureInput, ax as SignatureMode, ay as SoilTexture, az as StockPolicyTier, aA as StockPolicyTierType, aB as StoragePhases, aC as StorageType, aD as TASK_TYPE_REGISTRY, aE as TEMPLATE_SCOPE_LEVELS, aG as TargetEntityType, aH as TaskStatus, aI as TaskType, aJ as TaskTypeConfig, aK as TemplateScopeLevel, aM as TransactionType, aN as TrellisType, aO as UserPrivilege, aP as UserPrivilegeType, aQ as UserRole, aS as UserRoleType, aT as VigorRating, aU as WaterSource } from './constants-dvGSNAt_.mjs';
|
|
3
3
|
|
|
4
4
|
declare const AgriLogger: {
|
|
5
5
|
log(level: "INFO" | "WARN" | "ERROR", message: string, meta: any): void;
|
|
@@ -18,6 +18,28 @@ declare function transformRoleAssignments(assignments: UserRoleAssignment[]): Re
|
|
|
18
18
|
privileges: string[];
|
|
19
19
|
}>;
|
|
20
20
|
|
|
21
|
+
declare class PriceAmbiguityError extends Error {
|
|
22
|
+
readonly productId: string;
|
|
23
|
+
readonly tier: Tier;
|
|
24
|
+
readonly candidates: PriceConflictCandidate[];
|
|
25
|
+
readonly matchedOn: {
|
|
26
|
+
accountCode?: string;
|
|
27
|
+
contractReferenceNumber?: string;
|
|
28
|
+
};
|
|
29
|
+
constructor(productId: string, tier: Tier, candidates: PriceConflictCandidate[], matchedOn?: {
|
|
30
|
+
accountCode?: string;
|
|
31
|
+
contractReferenceNumber?: string;
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
declare class AggregatePriceAmbiguityError extends Error {
|
|
35
|
+
readonly productId: string;
|
|
36
|
+
readonly conflicts: PriceAmbiguityError[];
|
|
37
|
+
readonly shadowedCleanMatches: ShadowedCleanMatch[];
|
|
38
|
+
constructor(productId: string, conflicts: PriceAmbiguityError[], shadowedCleanMatches?: ShadowedCleanMatch[]);
|
|
39
|
+
}
|
|
40
|
+
declare function resolvePrice(dataSource: PricingDataSource, context: PricingContext): Promise<PriceResolutionResult>;
|
|
41
|
+
declare function createBase44PricingAdapter(db: any): PricingDataSource;
|
|
42
|
+
|
|
21
43
|
declare const Validator: {
|
|
22
44
|
/** * 1. Email: The classic. Uses a standard RFC 5322 regex.
|
|
23
45
|
*/
|
|
@@ -84,15 +106,16 @@ declare class AppError extends Error {
|
|
|
84
106
|
status: number;
|
|
85
107
|
appCode: string;
|
|
86
108
|
isOperational: boolean;
|
|
87
|
-
|
|
109
|
+
details?: unknown;
|
|
110
|
+
constructor(status: number, mod: string, cat: string, act: string, message: string, details?: unknown);
|
|
88
111
|
}
|
|
89
112
|
declare const ErrorFactory: {
|
|
90
113
|
unauthorized: (mod: string, msg?: string) => AppError;
|
|
91
114
|
forbidden: (mod: string, act: string, msg?: string) => AppError;
|
|
92
115
|
badRequest: (mod: string, act: string, msg?: string) => AppError;
|
|
93
116
|
notFound: (mod: string, act?: "00", msg?: string) => AppError;
|
|
94
|
-
conflict: (mod: string, act: string, msg?: string) => AppError;
|
|
95
|
-
integrity: (mod: string, act?: "00", msg?: string) => AppError;
|
|
117
|
+
conflict: (mod: string, act: string, msg?: string, details?: unknown) => AppError;
|
|
118
|
+
integrity: (mod: string, act?: "00", msg?: string, details?: unknown) => AppError;
|
|
96
119
|
subscription: (mod: string, msg?: string) => AppError;
|
|
97
120
|
internal: (mod: string, msg?: string) => AppError;
|
|
98
121
|
};
|
|
@@ -178,4 +201,4 @@ declare function calculateFileMetadataMatch(criteria: {
|
|
|
178
201
|
contextMatch?: string[];
|
|
179
202
|
}, fileMetadata: Record<string, any>, auditContext: Record<string, any>): number;
|
|
180
203
|
|
|
181
|
-
export { AccessLevelType, AgriCoreSession, AgriLogger, AppError, AvailableScope, Converter, ErrorFactory, FormMeta, InfrastructureFields, ResolvedError, ResourceRequirement, ScopeType, TabConfig, UserRoleAssignment, Validator, WireSession, assertTenantBoundary, calculateFileMetadataMatch, decodeSession, encodeSession, evaluateBaseAccess, extractAppCode, findSpecificOverride, generateCanonicalId, generateDocumentPath, generateInfrastructureFields, getAcceptedMimeTypes, getApproveAction, getFormMeta, getFormsGroupedByModule, getPrivilegesAt, getRoleAt, getScopeAncestry, getTaskTypeOptions, handleAppErrorResponse, hasPrivilegeAt, hasRoleAt, isTabVisible, isTabWritable, resolveAccess, resolveAppError, resolveEffectiveAccess, resolveError, transformRoleAssignments, verifyAndExtractSession, withAgriLogging };
|
|
204
|
+
export { AccessLevelType, AggregatePriceAmbiguityError, AgriCoreSession, AgriLogger, AppError, AvailableScope, Converter, ErrorFactory, FormMeta, InfrastructureFields, PriceAmbiguityError, PriceConflictCandidate, PriceResolutionResult, PricingContext, PricingDataSource, ResolvedError, ResourceRequirement, ScopeType, ShadowedCleanMatch, TabConfig, Tier, UserRoleAssignment, Validator, WireSession, assertTenantBoundary, calculateFileMetadataMatch, createBase44PricingAdapter, decodeSession, encodeSession, evaluateBaseAccess, extractAppCode, findSpecificOverride, generateCanonicalId, generateDocumentPath, generateInfrastructureFields, getAcceptedMimeTypes, getApproveAction, getFormMeta, getFormsGroupedByModule, getPrivilegesAt, getRoleAt, getScopeAncestry, getTaskTypeOptions, handleAppErrorResponse, hasPrivilegeAt, hasRoleAt, isTabVisible, isTabWritable, resolveAccess, resolveAppError, resolveEffectiveAccess, resolveError, resolvePrice, transformRoleAssignments, verifyAndExtractSession, withAgriLogging };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { d as AgriCoreSession,
|
|
2
|
-
export { A as ACCESS_RANK, a as ACT, b as AccessLevel, e as ApprovalStatus, f as ApprovalType, g as AuditStatus, h as AuditType, i as AuthScopeFields, B as BarcodeNamespace, k as BarcodeNamespaceType, l as BarcodeType, m as BarcodeTypeType, n as Base44Id, C as CAR_SUPPORTED_EVIDENCE_MIME_TYPES,
|
|
1
|
+
import { d as AgriCoreSession, aR as UserRoleAssignment, aL as Tier, a8 as PriceConflictCandidate, av as ShadowedCleanMatch, ab as PricingDataSource, aa as PricingContext, a9 as PriceResolutionResult, au as ScopeType, aV as WireSession, an as ResourceRequirement, c as AccessLevelType, j as AvailableScope, V as InfrastructureFields, T as FormMeta, aF as TabConfig, al as ResolvedError } from './constants-dvGSNAt_.js';
|
|
2
|
+
export { A as ACCESS_RANK, a as ACT, b as AccessLevel, e as ApprovalStatus, f as ApprovalType, g as AuditStatus, h as AuditType, i as AuthScopeFields, B as BarcodeNamespace, k as BarcodeNamespaceType, l as BarcodeType, m as BarcodeTypeType, n as Base44Id, o as BaseProductRow, C as CAR_SUPPORTED_EVIDENCE_MIME_TYPES, p as CAR_SUPPORTED_SIGNATURE_MIME_TYPES, q as CAT, r as CATEGORY_TABS, s as CORRECTIVE_ACTION_GENERATION_SOURCES, t as CORRECTIVE_ACTION_STATUSES, u as CanonicalId, v as CarSupportedEvidenceMimeType, w as CarSupportedSignatureMimeType, x as CarTemplateFileMetadata, y as CatalogSyncStatus, z as CatalogSyncStatusType, D as CompanyConfig, E as CorrectiveActionCompileTaskMetadata, F as CorrectiveActionGenerationSource, G as CorrectiveActionStatus, H as CropCycleStatus, I as DOCUMENT_DISPLAY_INFO, J as DOCUMENT_MENU_MAP, K as DocumentCategory, L as DocumentType, M as ERROR_DICTIONARY, N as EVERYONE, O as EmitterType, P as EnabledModule, Q as FORM_REGISTRY, R as FacilityType, S as FileUploadTaskType, U as HarvestMethod, W as LinkedStatus, X as MOD, Y as MODULE_LABELS, Z as MatrixRow, _ as MicroclimateZone, $ as MimeType, a0 as ModCode, a1 as NettingType, a2 as NormalizedSignatureValue, a3 as NotificationMode, a4 as OrderStatus, a5 as PermissionLevel, a6 as PhysicalState, a7 as PlantingMethod, ac as ProductType, ad as ProductTypeType, ae as ProductUnit, af as ProductUnitType, ag as RESOURCE_MODULE_MAP, ah as RESOURCE_PATHS, ai as RESOURCE_REQUIREMENTS, aj as ROLE_SECTIONS_BY_KEY, ak as RelationshipType, am as ResourceOverrides, ao as RoleAssignment, ap as RowOrientation, aq as SENTINEL_UUID, ar as SESSION_SCHEMA, as as SIGNATURE_MODES, at as SchemaField, aw as SignatureInput, ax as SignatureMode, ay as SoilTexture, az as StockPolicyTier, aA as StockPolicyTierType, aB as StoragePhases, aC as StorageType, aD as TASK_TYPE_REGISTRY, aE as TEMPLATE_SCOPE_LEVELS, aG as TargetEntityType, aH as TaskStatus, aI as TaskType, aJ as TaskTypeConfig, aK as TemplateScopeLevel, aM as TransactionType, aN as TrellisType, aO as UserPrivilege, aP as UserPrivilegeType, aQ as UserRole, aS as UserRoleType, aT as VigorRating, aU as WaterSource } from './constants-dvGSNAt_.js';
|
|
3
3
|
|
|
4
4
|
declare const AgriLogger: {
|
|
5
5
|
log(level: "INFO" | "WARN" | "ERROR", message: string, meta: any): void;
|
|
@@ -18,6 +18,28 @@ declare function transformRoleAssignments(assignments: UserRoleAssignment[]): Re
|
|
|
18
18
|
privileges: string[];
|
|
19
19
|
}>;
|
|
20
20
|
|
|
21
|
+
declare class PriceAmbiguityError extends Error {
|
|
22
|
+
readonly productId: string;
|
|
23
|
+
readonly tier: Tier;
|
|
24
|
+
readonly candidates: PriceConflictCandidate[];
|
|
25
|
+
readonly matchedOn: {
|
|
26
|
+
accountCode?: string;
|
|
27
|
+
contractReferenceNumber?: string;
|
|
28
|
+
};
|
|
29
|
+
constructor(productId: string, tier: Tier, candidates: PriceConflictCandidate[], matchedOn?: {
|
|
30
|
+
accountCode?: string;
|
|
31
|
+
contractReferenceNumber?: string;
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
declare class AggregatePriceAmbiguityError extends Error {
|
|
35
|
+
readonly productId: string;
|
|
36
|
+
readonly conflicts: PriceAmbiguityError[];
|
|
37
|
+
readonly shadowedCleanMatches: ShadowedCleanMatch[];
|
|
38
|
+
constructor(productId: string, conflicts: PriceAmbiguityError[], shadowedCleanMatches?: ShadowedCleanMatch[]);
|
|
39
|
+
}
|
|
40
|
+
declare function resolvePrice(dataSource: PricingDataSource, context: PricingContext): Promise<PriceResolutionResult>;
|
|
41
|
+
declare function createBase44PricingAdapter(db: any): PricingDataSource;
|
|
42
|
+
|
|
21
43
|
declare const Validator: {
|
|
22
44
|
/** * 1. Email: The classic. Uses a standard RFC 5322 regex.
|
|
23
45
|
*/
|
|
@@ -84,15 +106,16 @@ declare class AppError extends Error {
|
|
|
84
106
|
status: number;
|
|
85
107
|
appCode: string;
|
|
86
108
|
isOperational: boolean;
|
|
87
|
-
|
|
109
|
+
details?: unknown;
|
|
110
|
+
constructor(status: number, mod: string, cat: string, act: string, message: string, details?: unknown);
|
|
88
111
|
}
|
|
89
112
|
declare const ErrorFactory: {
|
|
90
113
|
unauthorized: (mod: string, msg?: string) => AppError;
|
|
91
114
|
forbidden: (mod: string, act: string, msg?: string) => AppError;
|
|
92
115
|
badRequest: (mod: string, act: string, msg?: string) => AppError;
|
|
93
116
|
notFound: (mod: string, act?: "00", msg?: string) => AppError;
|
|
94
|
-
conflict: (mod: string, act: string, msg?: string) => AppError;
|
|
95
|
-
integrity: (mod: string, act?: "00", msg?: string) => AppError;
|
|
117
|
+
conflict: (mod: string, act: string, msg?: string, details?: unknown) => AppError;
|
|
118
|
+
integrity: (mod: string, act?: "00", msg?: string, details?: unknown) => AppError;
|
|
96
119
|
subscription: (mod: string, msg?: string) => AppError;
|
|
97
120
|
internal: (mod: string, msg?: string) => AppError;
|
|
98
121
|
};
|
|
@@ -178,4 +201,4 @@ declare function calculateFileMetadataMatch(criteria: {
|
|
|
178
201
|
contextMatch?: string[];
|
|
179
202
|
}, fileMetadata: Record<string, any>, auditContext: Record<string, any>): number;
|
|
180
203
|
|
|
181
|
-
export { AccessLevelType, AgriCoreSession, AgriLogger, AppError, AvailableScope, Converter, ErrorFactory, FormMeta, InfrastructureFields, ResolvedError, ResourceRequirement, ScopeType, TabConfig, UserRoleAssignment, Validator, WireSession, assertTenantBoundary, calculateFileMetadataMatch, decodeSession, encodeSession, evaluateBaseAccess, extractAppCode, findSpecificOverride, generateCanonicalId, generateDocumentPath, generateInfrastructureFields, getAcceptedMimeTypes, getApproveAction, getFormMeta, getFormsGroupedByModule, getPrivilegesAt, getRoleAt, getScopeAncestry, getTaskTypeOptions, handleAppErrorResponse, hasPrivilegeAt, hasRoleAt, isTabVisible, isTabWritable, resolveAccess, resolveAppError, resolveEffectiveAccess, resolveError, transformRoleAssignments, verifyAndExtractSession, withAgriLogging };
|
|
204
|
+
export { AccessLevelType, AggregatePriceAmbiguityError, AgriCoreSession, AgriLogger, AppError, AvailableScope, Converter, ErrorFactory, FormMeta, InfrastructureFields, PriceAmbiguityError, PriceConflictCandidate, PriceResolutionResult, PricingContext, PricingDataSource, ResolvedError, ResourceRequirement, ScopeType, ShadowedCleanMatch, TabConfig, Tier, UserRoleAssignment, Validator, WireSession, assertTenantBoundary, calculateFileMetadataMatch, createBase44PricingAdapter, decodeSession, encodeSession, evaluateBaseAccess, extractAppCode, findSpecificOverride, generateCanonicalId, generateDocumentPath, generateInfrastructureFields, getAcceptedMimeTypes, getApproveAction, getFormMeta, getFormsGroupedByModule, getPrivilegesAt, getRoleAt, getScopeAncestry, getTaskTypeOptions, handleAppErrorResponse, hasPrivilegeAt, hasRoleAt, isTabVisible, isTabWritable, resolveAccess, resolveAppError, resolveEffectiveAccess, resolveError, resolvePrice, transformRoleAssignments, verifyAndExtractSession, withAgriLogging };
|
package/dist/index.js
CHANGED
|
@@ -23,6 +23,7 @@ __export(index_exports, {
|
|
|
23
23
|
ACCESS_RANK: () => ACCESS_RANK,
|
|
24
24
|
ACT: () => ACT,
|
|
25
25
|
AccessLevel: () => AccessLevel,
|
|
26
|
+
AggregatePriceAmbiguityError: () => AggregatePriceAmbiguityError,
|
|
26
27
|
AgriLogger: () => AgriLogger,
|
|
27
28
|
AppError: () => AppError,
|
|
28
29
|
ApprovalStatus: () => ApprovalStatus,
|
|
@@ -62,6 +63,7 @@ __export(index_exports, {
|
|
|
62
63
|
OrderStatus: () => OrderStatus,
|
|
63
64
|
PhysicalState: () => PhysicalState,
|
|
64
65
|
PlantingMethod: () => PlantingMethod,
|
|
66
|
+
PriceAmbiguityError: () => PriceAmbiguityError,
|
|
65
67
|
ProductType: () => ProductType,
|
|
66
68
|
ProductUnit: () => ProductUnit,
|
|
67
69
|
RESOURCE_MODULE_MAP: () => RESOURCE_MODULE_MAP,
|
|
@@ -90,6 +92,7 @@ __export(index_exports, {
|
|
|
90
92
|
WaterSource: () => WaterSource,
|
|
91
93
|
assertTenantBoundary: () => assertTenantBoundary,
|
|
92
94
|
calculateFileMetadataMatch: () => calculateFileMetadataMatch,
|
|
95
|
+
createBase44PricingAdapter: () => createBase44PricingAdapter,
|
|
93
96
|
decodeSession: () => decodeSession,
|
|
94
97
|
encodeSession: () => encodeSession,
|
|
95
98
|
evaluateBaseAccess: () => evaluateBaseAccess,
|
|
@@ -115,6 +118,7 @@ __export(index_exports, {
|
|
|
115
118
|
resolveAppError: () => resolveAppError,
|
|
116
119
|
resolveEffectiveAccess: () => resolveEffectiveAccess,
|
|
117
120
|
resolveError: () => resolveError,
|
|
121
|
+
resolvePrice: () => resolvePrice,
|
|
118
122
|
transformRoleAssignments: () => transformRoleAssignments,
|
|
119
123
|
verifyAndExtractSession: () => verifyAndExtractSession,
|
|
120
124
|
withAgriLogging: () => withAgriLogging
|
|
@@ -247,6 +251,182 @@ function transformRoleAssignments(assignments) {
|
|
|
247
251
|
return result;
|
|
248
252
|
}
|
|
249
253
|
|
|
254
|
+
// src/priceHelpers.ts
|
|
255
|
+
var PriceAmbiguityError = class extends Error {
|
|
256
|
+
constructor(productId, tier, candidates, matchedOn = {}) {
|
|
257
|
+
const keyDescription = matchedOn.contractReferenceNumber ? `contract ${matchedOn.contractReferenceNumber}` : matchedOn.accountCode ? `account ${matchedOn.accountCode}` : "the system default";
|
|
258
|
+
super(
|
|
259
|
+
`Ambiguous pricing for product ${productId} at tier ${tier} (${keyDescription}): ${candidates.length} overlapping active prices found (${candidates.map((c) => `$${(c.unitPriceCents / 100).toFixed(2)}`).join(", ")}).`
|
|
260
|
+
);
|
|
261
|
+
this.productId = productId;
|
|
262
|
+
this.tier = tier;
|
|
263
|
+
this.candidates = candidates;
|
|
264
|
+
this.matchedOn = matchedOn;
|
|
265
|
+
this.name = "PriceAmbiguityError";
|
|
266
|
+
}
|
|
267
|
+
productId;
|
|
268
|
+
tier;
|
|
269
|
+
candidates;
|
|
270
|
+
matchedOn;
|
|
271
|
+
};
|
|
272
|
+
var AggregatePriceAmbiguityError = class extends Error {
|
|
273
|
+
constructor(productId, conflicts, shadowedCleanMatches = []) {
|
|
274
|
+
const tierList = conflicts.map((c) => c.tier).join(", ");
|
|
275
|
+
const note = shadowedCleanMatches.length > 0 ? ` Note: ${shadowedCleanMatches.length} other tier(s) had a clean price available but were not used, since a higher-priority tier must be resolved first.` : "";
|
|
276
|
+
super(
|
|
277
|
+
`Pricing for product ${productId} could not be resolved: ambiguous prices found at ${conflicts.length} tier(s) [${tierList}].${note}`
|
|
278
|
+
);
|
|
279
|
+
this.productId = productId;
|
|
280
|
+
this.conflicts = conflicts;
|
|
281
|
+
this.shadowedCleanMatches = shadowedCleanMatches;
|
|
282
|
+
this.name = "AggregatePriceAmbiguityError";
|
|
283
|
+
}
|
|
284
|
+
productId;
|
|
285
|
+
conflicts;
|
|
286
|
+
shadowedCleanMatches;
|
|
287
|
+
};
|
|
288
|
+
function fromMatrixRow(row, priceSource) {
|
|
289
|
+
return {
|
|
290
|
+
unitPriceCents: row.matrixUnitPrice,
|
|
291
|
+
minimumOrderQuantity: row.minimumOrderQuantity,
|
|
292
|
+
leadTimeDays: row.leadTimeDays,
|
|
293
|
+
priceSource,
|
|
294
|
+
sourceRecordId: row.canonicalId
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
async function resolvePrice(dataSource, context) {
|
|
298
|
+
const targetDate = context.evaluationDate ?? /* @__PURE__ */ new Date();
|
|
299
|
+
const conflicts = [];
|
|
300
|
+
const shadowedCleanMatches = [];
|
|
301
|
+
async function tryTier(lookup) {
|
|
302
|
+
try {
|
|
303
|
+
return await lookup();
|
|
304
|
+
} catch (err) {
|
|
305
|
+
if (err instanceof PriceAmbiguityError) {
|
|
306
|
+
conflicts.push(err);
|
|
307
|
+
return null;
|
|
308
|
+
}
|
|
309
|
+
throw err;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
function handleCleanRow(row, tier, matchedOn) {
|
|
313
|
+
if (!row) return null;
|
|
314
|
+
if (conflicts.length === 0) return fromMatrixRow(row, tier);
|
|
315
|
+
shadowedCleanMatches.push({
|
|
316
|
+
tier,
|
|
317
|
+
unitPriceCents: row.matrixUnitPrice,
|
|
318
|
+
sourceRecordId: row.canonicalId,
|
|
319
|
+
matchedOn
|
|
320
|
+
});
|
|
321
|
+
return null;
|
|
322
|
+
}
|
|
323
|
+
if (context.contractReferenceNumber) {
|
|
324
|
+
const row = await tryTier(
|
|
325
|
+
() => dataSource.findContractPrice({
|
|
326
|
+
productId: context.productId,
|
|
327
|
+
contractReferenceNumber: context.contractReferenceNumber,
|
|
328
|
+
targetDate
|
|
329
|
+
})
|
|
330
|
+
);
|
|
331
|
+
const result = handleCleanRow(row, "EXPLICIT_CONTRACT", {
|
|
332
|
+
contractReferenceNumber: context.contractReferenceNumber
|
|
333
|
+
});
|
|
334
|
+
if (result) return result;
|
|
335
|
+
}
|
|
336
|
+
if (context.accountCode && context.accountCode !== "INTERNAL_DEFAULT") {
|
|
337
|
+
const row = await tryTier(
|
|
338
|
+
() => dataSource.findAccountPrice({
|
|
339
|
+
productId: context.productId,
|
|
340
|
+
accountCode: context.accountCode,
|
|
341
|
+
targetDate
|
|
342
|
+
})
|
|
343
|
+
);
|
|
344
|
+
const result = handleCleanRow(row, "ACCOUNT_MATRIX", { accountCode: context.accountCode });
|
|
345
|
+
if (result) return result;
|
|
346
|
+
}
|
|
347
|
+
const defaultRow = await tryTier(
|
|
348
|
+
() => dataSource.findDefaultPrice({ productId: context.productId, targetDate })
|
|
349
|
+
);
|
|
350
|
+
const defaultResult = handleCleanRow(defaultRow, "INTERNAL_DEFAULT", {});
|
|
351
|
+
if (defaultResult) return defaultResult;
|
|
352
|
+
if (conflicts.length > 0) {
|
|
353
|
+
throw new AggregatePriceAmbiguityError(context.productId, conflicts, shadowedCleanMatches);
|
|
354
|
+
}
|
|
355
|
+
const baseProduct = await dataSource.findBaseProduct({ productId: context.productId });
|
|
356
|
+
if (baseProduct) {
|
|
357
|
+
return {
|
|
358
|
+
unitPriceCents: baseProduct.unitPriceCents,
|
|
359
|
+
minimumOrderQuantity: 1,
|
|
360
|
+
leadTimeDays: null,
|
|
361
|
+
priceSource: "PRODUCT_FALLBACK",
|
|
362
|
+
sourceRecordId: baseProduct.canonicalId
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
throw new Error(`Critical Architecture Error: Product ${context.productId} not found in system catalog.`);
|
|
366
|
+
}
|
|
367
|
+
var ACTIVE_ROW_LOOKBACK = 5;
|
|
368
|
+
var SORT_MOST_RECENT_FIRST = "-validFrom";
|
|
369
|
+
function pickActiveRow(rows, targetIso, productId, tier, matchedOn) {
|
|
370
|
+
const activeMatches = rows.filter(
|
|
371
|
+
(r) => r.validTo === null || new Date(r.validTo).toISOString() >= targetIso
|
|
372
|
+
);
|
|
373
|
+
if (activeMatches.length > 1) {
|
|
374
|
+
throw new PriceAmbiguityError(
|
|
375
|
+
productId,
|
|
376
|
+
tier,
|
|
377
|
+
activeMatches.map((r) => ({
|
|
378
|
+
sourceRecordId: r.canonicalId,
|
|
379
|
+
unitPriceCents: r.matrixUnitPrice,
|
|
380
|
+
validFrom: r.validFrom,
|
|
381
|
+
validTo: r.validTo
|
|
382
|
+
})),
|
|
383
|
+
matchedOn
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
return activeMatches[0] ?? null;
|
|
387
|
+
}
|
|
388
|
+
function createBase44PricingAdapter(db) {
|
|
389
|
+
return {
|
|
390
|
+
async findContractPrice({ productId, contractReferenceNumber, targetDate }) {
|
|
391
|
+
const targetIso = targetDate.toISOString();
|
|
392
|
+
const rows = await db.ProductPriceMatrix.filter(
|
|
393
|
+
{
|
|
394
|
+
productId,
|
|
395
|
+
contractReferenceNumber,
|
|
396
|
+
matrixType: "procurement_buy",
|
|
397
|
+
validFrom_lte: targetIso
|
|
398
|
+
},
|
|
399
|
+
SORT_MOST_RECENT_FIRST,
|
|
400
|
+
ACTIVE_ROW_LOOKBACK
|
|
401
|
+
);
|
|
402
|
+
return pickActiveRow(rows, targetIso, productId, "EXPLICIT_CONTRACT", {
|
|
403
|
+
contractReferenceNumber
|
|
404
|
+
});
|
|
405
|
+
},
|
|
406
|
+
async findAccountPrice({ productId, accountCode, targetDate }) {
|
|
407
|
+
const targetIso = targetDate.toISOString();
|
|
408
|
+
const rows = await db.ProductPriceMatrix.filter(
|
|
409
|
+
{ productId, accountCode, validFrom_lte: targetIso },
|
|
410
|
+
SORT_MOST_RECENT_FIRST,
|
|
411
|
+
ACTIVE_ROW_LOOKBACK
|
|
412
|
+
);
|
|
413
|
+
return pickActiveRow(rows, targetIso, productId, "ACCOUNT_MATRIX", { accountCode });
|
|
414
|
+
},
|
|
415
|
+
async findDefaultPrice({ productId, targetDate }) {
|
|
416
|
+
const targetIso = targetDate.toISOString();
|
|
417
|
+
const rows = await db.ProductPriceMatrix.filter(
|
|
418
|
+
{ productId, accountCode: "INTERNAL_DEFAULT", validFrom_lte: targetIso },
|
|
419
|
+
SORT_MOST_RECENT_FIRST,
|
|
420
|
+
ACTIVE_ROW_LOOKBACK
|
|
421
|
+
);
|
|
422
|
+
return pickActiveRow(rows, targetIso, productId, "INTERNAL_DEFAULT", {});
|
|
423
|
+
},
|
|
424
|
+
async findBaseProduct({ productId }) {
|
|
425
|
+
return await db.CompanyProduct.get(productId) ?? null;
|
|
426
|
+
}
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
|
|
250
430
|
// src/validators.ts
|
|
251
431
|
var Validator = {
|
|
252
432
|
/** * 1. Email: The classic. Uses a standard RFC 5322 regex.
|
|
@@ -389,7 +569,8 @@ var CAT = {
|
|
|
389
569
|
SUBSCRIPTION: "1",
|
|
390
570
|
PERMISSION: "2",
|
|
391
571
|
VALIDATION: "3",
|
|
392
|
-
INTEGRITY: "4"
|
|
572
|
+
INTEGRITY: "4",
|
|
573
|
+
DATABASE: "5"
|
|
393
574
|
};
|
|
394
575
|
var ACT = {
|
|
395
576
|
GENERIC: "00",
|
|
@@ -435,7 +616,8 @@ var ERROR_DICTIONARY = {
|
|
|
435
616
|
"400": { title: "Data Conflict", text: "A conflict occurred with an existing record. Please review and try again." },
|
|
436
617
|
"402": { title: "Duplicate Entry", text: "A record with this name or identifier already exists." },
|
|
437
618
|
"403": { title: "Edit Conflict", text: "This record was recently updated by someone else. Please refresh and try again." },
|
|
438
|
-
"404": { title: "Record In Use", text: "This record cannot be deleted because it is referenced by other data." }
|
|
619
|
+
"404": { title: "Record In Use", text: "This record cannot be deleted because it is referenced by other data." },
|
|
620
|
+
"405": { title: "Pricing Conflict", text: "Multiple active prices were found for this product. An administrator must resolve the conflict before this price can be used." }
|
|
439
621
|
};
|
|
440
622
|
var UserRole = {
|
|
441
623
|
OWNER: "owner",
|
|
@@ -1332,11 +1514,14 @@ var AppError = class extends Error {
|
|
|
1332
1514
|
status;
|
|
1333
1515
|
appCode;
|
|
1334
1516
|
isOperational;
|
|
1335
|
-
|
|
1517
|
+
details;
|
|
1518
|
+
// optional structured payload, e.g. conflicting price candidates
|
|
1519
|
+
constructor(status, mod, cat, act, message, details) {
|
|
1336
1520
|
super(message);
|
|
1337
1521
|
this.status = status;
|
|
1338
1522
|
this.appCode = `${mod}-${status}-${cat}${act}`;
|
|
1339
1523
|
this.isOperational = true;
|
|
1524
|
+
this.details = details;
|
|
1340
1525
|
Object.setPrototypeOf(this, new.target.prototype);
|
|
1341
1526
|
}
|
|
1342
1527
|
};
|
|
@@ -1345,15 +1530,21 @@ var ErrorFactory = {
|
|
|
1345
1530
|
forbidden: (mod, act, msg = "Access denied") => new AppError(403, mod, CAT.PERMISSION, act, msg),
|
|
1346
1531
|
badRequest: (mod, act, msg = "Bad request") => new AppError(400, mod, CAT.VALIDATION, act, msg),
|
|
1347
1532
|
notFound: (mod, act = ACT.GENERIC, msg = "Record not found") => new AppError(404, mod, CAT.VALIDATION, act, msg),
|
|
1348
|
-
conflict: (mod, act, msg = "Record already exists") => new AppError(409, mod, CAT.INTEGRITY, act, msg),
|
|
1349
|
-
integrity: (mod, act = ACT.GENERIC, msg = "Data integrity violation") => new AppError(409, mod, CAT.INTEGRITY, act, msg),
|
|
1533
|
+
conflict: (mod, act, msg = "Record already exists", details) => new AppError(409, mod, CAT.INTEGRITY, act, msg, details),
|
|
1534
|
+
integrity: (mod, act = ACT.GENERIC, msg = "Data integrity violation", details) => new AppError(409, mod, CAT.INTEGRITY, act, msg, details),
|
|
1350
1535
|
subscription: (mod, msg = "Module locked or subscription required") => new AppError(403, mod, CAT.SUBSCRIPTION, ACT.GENERIC, msg),
|
|
1351
1536
|
internal: (mod, msg = "Internal server error") => new AppError(500, mod, CAT.INTEGRITY, ACT.GENERIC, msg)
|
|
1352
1537
|
};
|
|
1353
1538
|
var handleAppErrorResponse = (error, mod = MOD.ADMIN) => {
|
|
1354
1539
|
if (error instanceof AppError && error.isOperational) {
|
|
1355
1540
|
return Response.json(
|
|
1356
|
-
{
|
|
1541
|
+
{
|
|
1542
|
+
error: {
|
|
1543
|
+
appCode: error.appCode,
|
|
1544
|
+
message: error.message,
|
|
1545
|
+
...error.details ? { details: error.details } : {}
|
|
1546
|
+
}
|
|
1547
|
+
},
|
|
1357
1548
|
{ status: error.status }
|
|
1358
1549
|
);
|
|
1359
1550
|
}
|
|
@@ -1565,10 +1756,18 @@ function isTabWritable(session, activeScopeId, tabConfig) {
|
|
|
1565
1756
|
return resolveAccess(session, activeScopeId, tabConfig) === "WRITE";
|
|
1566
1757
|
}
|
|
1567
1758
|
function evaluateBaseAccess(resourcePath, session, activeScopeId, requirements = {}) {
|
|
1568
|
-
const
|
|
1759
|
+
const ancestry = getScopeAncestry(activeScopeId, session.availableScopes);
|
|
1760
|
+
const directRole = getRoleAt(session, activeScopeId);
|
|
1761
|
+
const inheritedRole = !directRole ? ancestry.slice(1).reduce(
|
|
1762
|
+
(found, scopeId) => found ?? getRoleAt(session, scopeId),
|
|
1763
|
+
null
|
|
1764
|
+
) : null;
|
|
1765
|
+
const role = directRole ?? inheritedRole;
|
|
1766
|
+
const isInherited = !directRole && !!inheritedRole;
|
|
1569
1767
|
const privileges = getPrivilegesAt(session, activeScopeId);
|
|
1570
1768
|
if (!role) return "NONE";
|
|
1571
|
-
if (role === UserRole.OWNER) return "WRITE";
|
|
1769
|
+
if (role === UserRole.OWNER) return isInherited ? "READ" : "WRITE";
|
|
1770
|
+
if (role === UserRole.MANAGER) return isInherited ? "READ" : "WRITE";
|
|
1572
1771
|
const requiredModules = RESOURCE_MODULE_MAP[resourcePath] || [];
|
|
1573
1772
|
if (requiredModules.length > 0) {
|
|
1574
1773
|
const hasModule = requiredModules.some((m) => session.enabledModules?.includes(m));
|
|
@@ -1582,7 +1781,7 @@ function evaluateBaseAccess(resourcePath, session, activeScopeId, requirements =
|
|
|
1582
1781
|
const hasPrivilege = requirements.requiredPrivileges.some((p) => privileges.includes(p));
|
|
1583
1782
|
if (!hasPrivilege) return "NONE";
|
|
1584
1783
|
}
|
|
1585
|
-
return
|
|
1784
|
+
return "READ";
|
|
1586
1785
|
}
|
|
1587
1786
|
function resolveEffectiveAccess(base, override) {
|
|
1588
1787
|
return override === void 0 || override === null ? base : override;
|
|
@@ -1709,6 +1908,7 @@ function calculateFileMetadataMatch(criteria, fileMetadata, auditContext) {
|
|
|
1709
1908
|
ACCESS_RANK,
|
|
1710
1909
|
ACT,
|
|
1711
1910
|
AccessLevel,
|
|
1911
|
+
AggregatePriceAmbiguityError,
|
|
1712
1912
|
AgriLogger,
|
|
1713
1913
|
AppError,
|
|
1714
1914
|
ApprovalStatus,
|
|
@@ -1748,6 +1948,7 @@ function calculateFileMetadataMatch(criteria, fileMetadata, auditContext) {
|
|
|
1748
1948
|
OrderStatus,
|
|
1749
1949
|
PhysicalState,
|
|
1750
1950
|
PlantingMethod,
|
|
1951
|
+
PriceAmbiguityError,
|
|
1751
1952
|
ProductType,
|
|
1752
1953
|
ProductUnit,
|
|
1753
1954
|
RESOURCE_MODULE_MAP,
|
|
@@ -1776,6 +1977,7 @@ function calculateFileMetadataMatch(criteria, fileMetadata, auditContext) {
|
|
|
1776
1977
|
WaterSource,
|
|
1777
1978
|
assertTenantBoundary,
|
|
1778
1979
|
calculateFileMetadataMatch,
|
|
1980
|
+
createBase44PricingAdapter,
|
|
1779
1981
|
decodeSession,
|
|
1780
1982
|
encodeSession,
|
|
1781
1983
|
evaluateBaseAccess,
|
|
@@ -1801,6 +2003,7 @@ function calculateFileMetadataMatch(criteria, fileMetadata, auditContext) {
|
|
|
1801
2003
|
resolveAppError,
|
|
1802
2004
|
resolveEffectiveAccess,
|
|
1803
2005
|
resolveError,
|
|
2006
|
+
resolvePrice,
|
|
1804
2007
|
transformRoleAssignments,
|
|
1805
2008
|
verifyAndExtractSession,
|
|
1806
2009
|
withAgriLogging
|
package/dist/index.mjs
CHANGED
|
@@ -62,7 +62,7 @@ import {
|
|
|
62
62
|
UserRole,
|
|
63
63
|
VigorRating,
|
|
64
64
|
WaterSource
|
|
65
|
-
} from "./chunk-
|
|
65
|
+
} from "./chunk-Y5KVDVNY.mjs";
|
|
66
66
|
|
|
67
67
|
// src/index.ts
|
|
68
68
|
import { jwtVerify } from "jose";
|
|
@@ -192,6 +192,182 @@ function transformRoleAssignments(assignments) {
|
|
|
192
192
|
return result;
|
|
193
193
|
}
|
|
194
194
|
|
|
195
|
+
// src/priceHelpers.ts
|
|
196
|
+
var PriceAmbiguityError = class extends Error {
|
|
197
|
+
constructor(productId, tier, candidates, matchedOn = {}) {
|
|
198
|
+
const keyDescription = matchedOn.contractReferenceNumber ? `contract ${matchedOn.contractReferenceNumber}` : matchedOn.accountCode ? `account ${matchedOn.accountCode}` : "the system default";
|
|
199
|
+
super(
|
|
200
|
+
`Ambiguous pricing for product ${productId} at tier ${tier} (${keyDescription}): ${candidates.length} overlapping active prices found (${candidates.map((c) => `$${(c.unitPriceCents / 100).toFixed(2)}`).join(", ")}).`
|
|
201
|
+
);
|
|
202
|
+
this.productId = productId;
|
|
203
|
+
this.tier = tier;
|
|
204
|
+
this.candidates = candidates;
|
|
205
|
+
this.matchedOn = matchedOn;
|
|
206
|
+
this.name = "PriceAmbiguityError";
|
|
207
|
+
}
|
|
208
|
+
productId;
|
|
209
|
+
tier;
|
|
210
|
+
candidates;
|
|
211
|
+
matchedOn;
|
|
212
|
+
};
|
|
213
|
+
var AggregatePriceAmbiguityError = class extends Error {
|
|
214
|
+
constructor(productId, conflicts, shadowedCleanMatches = []) {
|
|
215
|
+
const tierList = conflicts.map((c) => c.tier).join(", ");
|
|
216
|
+
const note = shadowedCleanMatches.length > 0 ? ` Note: ${shadowedCleanMatches.length} other tier(s) had a clean price available but were not used, since a higher-priority tier must be resolved first.` : "";
|
|
217
|
+
super(
|
|
218
|
+
`Pricing for product ${productId} could not be resolved: ambiguous prices found at ${conflicts.length} tier(s) [${tierList}].${note}`
|
|
219
|
+
);
|
|
220
|
+
this.productId = productId;
|
|
221
|
+
this.conflicts = conflicts;
|
|
222
|
+
this.shadowedCleanMatches = shadowedCleanMatches;
|
|
223
|
+
this.name = "AggregatePriceAmbiguityError";
|
|
224
|
+
}
|
|
225
|
+
productId;
|
|
226
|
+
conflicts;
|
|
227
|
+
shadowedCleanMatches;
|
|
228
|
+
};
|
|
229
|
+
function fromMatrixRow(row, priceSource) {
|
|
230
|
+
return {
|
|
231
|
+
unitPriceCents: row.matrixUnitPrice,
|
|
232
|
+
minimumOrderQuantity: row.minimumOrderQuantity,
|
|
233
|
+
leadTimeDays: row.leadTimeDays,
|
|
234
|
+
priceSource,
|
|
235
|
+
sourceRecordId: row.canonicalId
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
async function resolvePrice(dataSource, context) {
|
|
239
|
+
const targetDate = context.evaluationDate ?? /* @__PURE__ */ new Date();
|
|
240
|
+
const conflicts = [];
|
|
241
|
+
const shadowedCleanMatches = [];
|
|
242
|
+
async function tryTier(lookup) {
|
|
243
|
+
try {
|
|
244
|
+
return await lookup();
|
|
245
|
+
} catch (err) {
|
|
246
|
+
if (err instanceof PriceAmbiguityError) {
|
|
247
|
+
conflicts.push(err);
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
throw err;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
function handleCleanRow(row, tier, matchedOn) {
|
|
254
|
+
if (!row) return null;
|
|
255
|
+
if (conflicts.length === 0) return fromMatrixRow(row, tier);
|
|
256
|
+
shadowedCleanMatches.push({
|
|
257
|
+
tier,
|
|
258
|
+
unitPriceCents: row.matrixUnitPrice,
|
|
259
|
+
sourceRecordId: row.canonicalId,
|
|
260
|
+
matchedOn
|
|
261
|
+
});
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
if (context.contractReferenceNumber) {
|
|
265
|
+
const row = await tryTier(
|
|
266
|
+
() => dataSource.findContractPrice({
|
|
267
|
+
productId: context.productId,
|
|
268
|
+
contractReferenceNumber: context.contractReferenceNumber,
|
|
269
|
+
targetDate
|
|
270
|
+
})
|
|
271
|
+
);
|
|
272
|
+
const result = handleCleanRow(row, "EXPLICIT_CONTRACT", {
|
|
273
|
+
contractReferenceNumber: context.contractReferenceNumber
|
|
274
|
+
});
|
|
275
|
+
if (result) return result;
|
|
276
|
+
}
|
|
277
|
+
if (context.accountCode && context.accountCode !== "INTERNAL_DEFAULT") {
|
|
278
|
+
const row = await tryTier(
|
|
279
|
+
() => dataSource.findAccountPrice({
|
|
280
|
+
productId: context.productId,
|
|
281
|
+
accountCode: context.accountCode,
|
|
282
|
+
targetDate
|
|
283
|
+
})
|
|
284
|
+
);
|
|
285
|
+
const result = handleCleanRow(row, "ACCOUNT_MATRIX", { accountCode: context.accountCode });
|
|
286
|
+
if (result) return result;
|
|
287
|
+
}
|
|
288
|
+
const defaultRow = await tryTier(
|
|
289
|
+
() => dataSource.findDefaultPrice({ productId: context.productId, targetDate })
|
|
290
|
+
);
|
|
291
|
+
const defaultResult = handleCleanRow(defaultRow, "INTERNAL_DEFAULT", {});
|
|
292
|
+
if (defaultResult) return defaultResult;
|
|
293
|
+
if (conflicts.length > 0) {
|
|
294
|
+
throw new AggregatePriceAmbiguityError(context.productId, conflicts, shadowedCleanMatches);
|
|
295
|
+
}
|
|
296
|
+
const baseProduct = await dataSource.findBaseProduct({ productId: context.productId });
|
|
297
|
+
if (baseProduct) {
|
|
298
|
+
return {
|
|
299
|
+
unitPriceCents: baseProduct.unitPriceCents,
|
|
300
|
+
minimumOrderQuantity: 1,
|
|
301
|
+
leadTimeDays: null,
|
|
302
|
+
priceSource: "PRODUCT_FALLBACK",
|
|
303
|
+
sourceRecordId: baseProduct.canonicalId
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
throw new Error(`Critical Architecture Error: Product ${context.productId} not found in system catalog.`);
|
|
307
|
+
}
|
|
308
|
+
var ACTIVE_ROW_LOOKBACK = 5;
|
|
309
|
+
var SORT_MOST_RECENT_FIRST = "-validFrom";
|
|
310
|
+
function pickActiveRow(rows, targetIso, productId, tier, matchedOn) {
|
|
311
|
+
const activeMatches = rows.filter(
|
|
312
|
+
(r) => r.validTo === null || new Date(r.validTo).toISOString() >= targetIso
|
|
313
|
+
);
|
|
314
|
+
if (activeMatches.length > 1) {
|
|
315
|
+
throw new PriceAmbiguityError(
|
|
316
|
+
productId,
|
|
317
|
+
tier,
|
|
318
|
+
activeMatches.map((r) => ({
|
|
319
|
+
sourceRecordId: r.canonicalId,
|
|
320
|
+
unitPriceCents: r.matrixUnitPrice,
|
|
321
|
+
validFrom: r.validFrom,
|
|
322
|
+
validTo: r.validTo
|
|
323
|
+
})),
|
|
324
|
+
matchedOn
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
return activeMatches[0] ?? null;
|
|
328
|
+
}
|
|
329
|
+
function createBase44PricingAdapter(db) {
|
|
330
|
+
return {
|
|
331
|
+
async findContractPrice({ productId, contractReferenceNumber, targetDate }) {
|
|
332
|
+
const targetIso = targetDate.toISOString();
|
|
333
|
+
const rows = await db.ProductPriceMatrix.filter(
|
|
334
|
+
{
|
|
335
|
+
productId,
|
|
336
|
+
contractReferenceNumber,
|
|
337
|
+
matrixType: "procurement_buy",
|
|
338
|
+
validFrom_lte: targetIso
|
|
339
|
+
},
|
|
340
|
+
SORT_MOST_RECENT_FIRST,
|
|
341
|
+
ACTIVE_ROW_LOOKBACK
|
|
342
|
+
);
|
|
343
|
+
return pickActiveRow(rows, targetIso, productId, "EXPLICIT_CONTRACT", {
|
|
344
|
+
contractReferenceNumber
|
|
345
|
+
});
|
|
346
|
+
},
|
|
347
|
+
async findAccountPrice({ productId, accountCode, targetDate }) {
|
|
348
|
+
const targetIso = targetDate.toISOString();
|
|
349
|
+
const rows = await db.ProductPriceMatrix.filter(
|
|
350
|
+
{ productId, accountCode, validFrom_lte: targetIso },
|
|
351
|
+
SORT_MOST_RECENT_FIRST,
|
|
352
|
+
ACTIVE_ROW_LOOKBACK
|
|
353
|
+
);
|
|
354
|
+
return pickActiveRow(rows, targetIso, productId, "ACCOUNT_MATRIX", { accountCode });
|
|
355
|
+
},
|
|
356
|
+
async findDefaultPrice({ productId, targetDate }) {
|
|
357
|
+
const targetIso = targetDate.toISOString();
|
|
358
|
+
const rows = await db.ProductPriceMatrix.filter(
|
|
359
|
+
{ productId, accountCode: "INTERNAL_DEFAULT", validFrom_lte: targetIso },
|
|
360
|
+
SORT_MOST_RECENT_FIRST,
|
|
361
|
+
ACTIVE_ROW_LOOKBACK
|
|
362
|
+
);
|
|
363
|
+
return pickActiveRow(rows, targetIso, productId, "INTERNAL_DEFAULT", {});
|
|
364
|
+
},
|
|
365
|
+
async findBaseProduct({ productId }) {
|
|
366
|
+
return await db.CompanyProduct.get(productId) ?? null;
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
|
|
195
371
|
// src/validators.ts
|
|
196
372
|
var Validator = {
|
|
197
373
|
/** * 1. Email: The classic. Uses a standard RFC 5322 regex.
|
|
@@ -320,11 +496,14 @@ var AppError = class extends Error {
|
|
|
320
496
|
status;
|
|
321
497
|
appCode;
|
|
322
498
|
isOperational;
|
|
323
|
-
|
|
499
|
+
details;
|
|
500
|
+
// optional structured payload, e.g. conflicting price candidates
|
|
501
|
+
constructor(status, mod, cat, act, message, details) {
|
|
324
502
|
super(message);
|
|
325
503
|
this.status = status;
|
|
326
504
|
this.appCode = `${mod}-${status}-${cat}${act}`;
|
|
327
505
|
this.isOperational = true;
|
|
506
|
+
this.details = details;
|
|
328
507
|
Object.setPrototypeOf(this, new.target.prototype);
|
|
329
508
|
}
|
|
330
509
|
};
|
|
@@ -333,15 +512,21 @@ var ErrorFactory = {
|
|
|
333
512
|
forbidden: (mod, act, msg = "Access denied") => new AppError(403, mod, CAT.PERMISSION, act, msg),
|
|
334
513
|
badRequest: (mod, act, msg = "Bad request") => new AppError(400, mod, CAT.VALIDATION, act, msg),
|
|
335
514
|
notFound: (mod, act = ACT.GENERIC, msg = "Record not found") => new AppError(404, mod, CAT.VALIDATION, act, msg),
|
|
336
|
-
conflict: (mod, act, msg = "Record already exists") => new AppError(409, mod, CAT.INTEGRITY, act, msg),
|
|
337
|
-
integrity: (mod, act = ACT.GENERIC, msg = "Data integrity violation") => new AppError(409, mod, CAT.INTEGRITY, act, msg),
|
|
515
|
+
conflict: (mod, act, msg = "Record already exists", details) => new AppError(409, mod, CAT.INTEGRITY, act, msg, details),
|
|
516
|
+
integrity: (mod, act = ACT.GENERIC, msg = "Data integrity violation", details) => new AppError(409, mod, CAT.INTEGRITY, act, msg, details),
|
|
338
517
|
subscription: (mod, msg = "Module locked or subscription required") => new AppError(403, mod, CAT.SUBSCRIPTION, ACT.GENERIC, msg),
|
|
339
518
|
internal: (mod, msg = "Internal server error") => new AppError(500, mod, CAT.INTEGRITY, ACT.GENERIC, msg)
|
|
340
519
|
};
|
|
341
520
|
var handleAppErrorResponse = (error, mod = MOD.ADMIN) => {
|
|
342
521
|
if (error instanceof AppError && error.isOperational) {
|
|
343
522
|
return Response.json(
|
|
344
|
-
{
|
|
523
|
+
{
|
|
524
|
+
error: {
|
|
525
|
+
appCode: error.appCode,
|
|
526
|
+
message: error.message,
|
|
527
|
+
...error.details ? { details: error.details } : {}
|
|
528
|
+
}
|
|
529
|
+
},
|
|
345
530
|
{ status: error.status }
|
|
346
531
|
);
|
|
347
532
|
}
|
|
@@ -553,10 +738,18 @@ function isTabWritable(session, activeScopeId, tabConfig) {
|
|
|
553
738
|
return resolveAccess(session, activeScopeId, tabConfig) === "WRITE";
|
|
554
739
|
}
|
|
555
740
|
function evaluateBaseAccess(resourcePath, session, activeScopeId, requirements = {}) {
|
|
556
|
-
const
|
|
741
|
+
const ancestry = getScopeAncestry(activeScopeId, session.availableScopes);
|
|
742
|
+
const directRole = getRoleAt(session, activeScopeId);
|
|
743
|
+
const inheritedRole = !directRole ? ancestry.slice(1).reduce(
|
|
744
|
+
(found, scopeId) => found ?? getRoleAt(session, scopeId),
|
|
745
|
+
null
|
|
746
|
+
) : null;
|
|
747
|
+
const role = directRole ?? inheritedRole;
|
|
748
|
+
const isInherited = !directRole && !!inheritedRole;
|
|
557
749
|
const privileges = getPrivilegesAt(session, activeScopeId);
|
|
558
750
|
if (!role) return "NONE";
|
|
559
|
-
if (role === UserRole.OWNER) return "WRITE";
|
|
751
|
+
if (role === UserRole.OWNER) return isInherited ? "READ" : "WRITE";
|
|
752
|
+
if (role === UserRole.MANAGER) return isInherited ? "READ" : "WRITE";
|
|
560
753
|
const requiredModules = RESOURCE_MODULE_MAP[resourcePath] || [];
|
|
561
754
|
if (requiredModules.length > 0) {
|
|
562
755
|
const hasModule = requiredModules.some((m) => session.enabledModules?.includes(m));
|
|
@@ -570,7 +763,7 @@ function evaluateBaseAccess(resourcePath, session, activeScopeId, requirements =
|
|
|
570
763
|
const hasPrivilege = requirements.requiredPrivileges.some((p) => privileges.includes(p));
|
|
571
764
|
if (!hasPrivilege) return "NONE";
|
|
572
765
|
}
|
|
573
|
-
return
|
|
766
|
+
return "READ";
|
|
574
767
|
}
|
|
575
768
|
function resolveEffectiveAccess(base, override) {
|
|
576
769
|
return override === void 0 || override === null ? base : override;
|
|
@@ -696,6 +889,7 @@ export {
|
|
|
696
889
|
ACCESS_RANK,
|
|
697
890
|
ACT,
|
|
698
891
|
AccessLevel,
|
|
892
|
+
AggregatePriceAmbiguityError,
|
|
699
893
|
AgriLogger,
|
|
700
894
|
AppError,
|
|
701
895
|
ApprovalStatus,
|
|
@@ -735,6 +929,7 @@ export {
|
|
|
735
929
|
OrderStatus,
|
|
736
930
|
PhysicalState,
|
|
737
931
|
PlantingMethod,
|
|
932
|
+
PriceAmbiguityError,
|
|
738
933
|
ProductType,
|
|
739
934
|
ProductUnit,
|
|
740
935
|
RESOURCE_MODULE_MAP,
|
|
@@ -763,6 +958,7 @@ export {
|
|
|
763
958
|
WaterSource,
|
|
764
959
|
assertTenantBoundary,
|
|
765
960
|
calculateFileMetadataMatch,
|
|
961
|
+
createBase44PricingAdapter,
|
|
766
962
|
decodeSession,
|
|
767
963
|
encodeSession,
|
|
768
964
|
evaluateBaseAccess,
|
|
@@ -788,6 +984,7 @@ export {
|
|
|
788
984
|
resolveAppError,
|
|
789
985
|
resolveEffectiveAccess,
|
|
790
986
|
resolveError,
|
|
987
|
+
resolvePrice,
|
|
791
988
|
transformRoleAssignments,
|
|
792
989
|
verifyAndExtractSession,
|
|
793
990
|
withAgriLogging
|