@litusarchieve18/agricore-utils 1.0.26 → 1.0.28

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 CHANGED
@@ -183,6 +183,12 @@ 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 — altered the logic and added scope aware filtering and write-time validation in priceHelpers.ts
189
+
190
+ V1.0.27 — adding get price helper, and adding adapter pattern to handle the conflicting price
191
+
186
192
  V1.0.26 — updating evaluatebaseAccess to try look up the parent scope given the current scope is not
187
193
 
188
194
  V1.0.25 — adding privileges and changing RESOURCE_PATHS
@@ -64,7 +64,8 @@ var ERROR_DICTIONARY = {
64
64
  "400": { title: "Data Conflict", text: "A conflict occurred with an existing record. Please review and try again." },
65
65
  "402": { title: "Duplicate Entry", text: "A record with this name or identifier already exists." },
66
66
  "403": { title: "Edit Conflict", text: "This record was recently updated by someone else. Please refresh and try again." },
67
- "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." }
68
69
  };
69
70
  var UserRole = {
70
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
+ matrixUnitPriceCents: 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";
@@ -686,4 +746,4 @@ declare const StockPolicyTier: {
686
746
  readonly BY_LOCATION: 3;
687
747
  };
688
748
 
689
- export { NettingType as $, ACCESS_RANK as A, BarcodeNamespace as B, CAR_SUPPORTED_EVIDENCE_MIME_TYPES as C, type CorrectiveActionCompileTaskMetadata as D, type CorrectiveActionGenerationSource as E, type CorrectiveActionStatus as F, CropCycleStatus as G, DOCUMENT_DISPLAY_INFO as H, DOCUMENT_MENU_MAP as I, DocumentCategory as J, DocumentType as K, ERROR_DICTIONARY as L, EVERYONE as M, EmitterType as N, EnabledModule as O, FORM_REGISTRY as P, FacilityType as Q, type FileUploadTaskType as R, type FormMeta as S, HarvestMethod as T, type InfrastructureFields as U, LinkedStatus as V, MOD as W, MODULE_LABELS as X, MicroclimateZone as Y, MimeType as Z, type ModCode as _, ACT as a, type NormalizedSignatureValue as a0, NotificationMode as a1, OrderStatus as a2, type PermissionLevel as a3, PhysicalState as a4, PlantingMethod as a5, ProductType as a6, type ProductTypeType as a7, ProductUnit as a8, type ProductUnitType as a9, TaskStatus as aA, TaskType as aB, type TaskTypeConfig as aC, type TemplateScopeLevel as aD, TransactionType as aE, TrellisType as aF, UserPrivilege as aG, type UserPrivilegeType as aH, UserRole as aI, type UserRoleAssignment as aJ, type UserRoleType as aK, VigorRating as aL, WaterSource as aM, type WireSession as aN, RESOURCE_MODULE_MAP as aa, RESOURCE_PATHS as ab, RESOURCE_REQUIREMENTS as ac, ROLE_SECTIONS_BY_KEY as ad, RelationshipType as ae, type ResolvedError as af, type ResourceOverrides as ag, type ResourceRequirement as ah, type RoleAssignment as ai, RowOrientation as aj, SENTINEL_UUID as ak, SESSION_SCHEMA as al, SIGNATURE_MODES as am, type SchemaField as an, type ScopeType as ao, type SignatureInput as ap, type SignatureMode as aq, SoilTexture as ar, StockPolicyTier as as, type StockPolicyTierType as at, StoragePhases as au, type StorageType as av, TASK_TYPE_REGISTRY as aw, TEMPLATE_SCOPE_LEVELS as ax, type TabConfig as ay, TargetEntityType 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, CAR_SUPPORTED_SIGNATURE_MIME_TYPES as o, CAT as p, CATEGORY_TABS as q, CORRECTIVE_ACTION_GENERATION_SOURCES as r, CORRECTIVE_ACTION_STATUSES as s, type CanonicalId as t, type CarSupportedEvidenceMimeType as u, type CarSupportedSignatureMimeType as v, type CarTemplateFileMetadata as w, CatalogSyncStatus as x, type CatalogSyncStatusType as y, type CompanyConfig as z };
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
+ matrixUnitPriceCents: 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";
@@ -686,4 +746,4 @@ declare const StockPolicyTier: {
686
746
  readonly BY_LOCATION: 3;
687
747
  };
688
748
 
689
- export { NettingType as $, ACCESS_RANK as A, BarcodeNamespace as B, CAR_SUPPORTED_EVIDENCE_MIME_TYPES as C, type CorrectiveActionCompileTaskMetadata as D, type CorrectiveActionGenerationSource as E, type CorrectiveActionStatus as F, CropCycleStatus as G, DOCUMENT_DISPLAY_INFO as H, DOCUMENT_MENU_MAP as I, DocumentCategory as J, DocumentType as K, ERROR_DICTIONARY as L, EVERYONE as M, EmitterType as N, EnabledModule as O, FORM_REGISTRY as P, FacilityType as Q, type FileUploadTaskType as R, type FormMeta as S, HarvestMethod as T, type InfrastructureFields as U, LinkedStatus as V, MOD as W, MODULE_LABELS as X, MicroclimateZone as Y, MimeType as Z, type ModCode as _, ACT as a, type NormalizedSignatureValue as a0, NotificationMode as a1, OrderStatus as a2, type PermissionLevel as a3, PhysicalState as a4, PlantingMethod as a5, ProductType as a6, type ProductTypeType as a7, ProductUnit as a8, type ProductUnitType as a9, TaskStatus as aA, TaskType as aB, type TaskTypeConfig as aC, type TemplateScopeLevel as aD, TransactionType as aE, TrellisType as aF, UserPrivilege as aG, type UserPrivilegeType as aH, UserRole as aI, type UserRoleAssignment as aJ, type UserRoleType as aK, VigorRating as aL, WaterSource as aM, type WireSession as aN, RESOURCE_MODULE_MAP as aa, RESOURCE_PATHS as ab, RESOURCE_REQUIREMENTS as ac, ROLE_SECTIONS_BY_KEY as ad, RelationshipType as ae, type ResolvedError as af, type ResourceOverrides as ag, type ResourceRequirement as ah, type RoleAssignment as ai, RowOrientation as aj, SENTINEL_UUID as ak, SESSION_SCHEMA as al, SIGNATURE_MODES as am, type SchemaField as an, type ScopeType as ao, type SignatureInput as ap, type SignatureMode as aq, SoilTexture as ar, StockPolicyTier as as, type StockPolicyTierType as at, StoragePhases as au, type StorageType as av, TASK_TYPE_REGISTRY as aw, TEMPLATE_SCOPE_LEVELS as ax, type TabConfig as ay, TargetEntityType 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, CAR_SUPPORTED_SIGNATURE_MIME_TYPES as o, CAT as p, CATEGORY_TABS as q, CORRECTIVE_ACTION_GENERATION_SOURCES as r, CORRECTIVE_ACTION_STATUSES as s, type CanonicalId as t, type CarSupportedEvidenceMimeType as u, type CarSupportedSignatureMimeType as v, type CarTemplateFileMetadata as w, CatalogSyncStatus as x, type CatalogSyncStatusType as y, type CompanyConfig as z };
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 };
@@ -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, o as CAR_SUPPORTED_SIGNATURE_MIME_TYPES, p as CAT, q as CATEGORY_TABS, r as CORRECTIVE_ACTION_GENERATION_SOURCES, s as CORRECTIVE_ACTION_STATUSES, x as CatalogSyncStatus, G as CropCycleStatus, H as DOCUMENT_DISPLAY_INFO, I as DOCUMENT_MENU_MAP, J as DocumentCategory, K as DocumentType, L as ERROR_DICTIONARY, M as EVERYONE, N as EmitterType, O as EnabledModule, P as FORM_REGISTRY, Q as FacilityType, T as HarvestMethod, V as LinkedStatus, W as MOD, X as MODULE_LABELS, Y as MicroclimateZone, Z as MimeType, _ as ModCode, $ as NettingType, a1 as NotificationMode, a2 as OrderStatus, a4 as PhysicalState, a5 as PlantingMethod, a6 as ProductType, a8 as ProductUnit, aa as RESOURCE_MODULE_MAP, ab as RESOURCE_PATHS, ac as RESOURCE_REQUIREMENTS, ad as ROLE_SECTIONS_BY_KEY, ae as RelationshipType, ah as ResourceRequirement, aj as RowOrientation, ak as SENTINEL_UUID, al as SESSION_SCHEMA, am as SIGNATURE_MODES, ar as SoilTexture, as as StockPolicyTier, au as StoragePhases, aw as TASK_TYPE_REGISTRY, ax as TEMPLATE_SCOPE_LEVELS, az as TargetEntityType, aA as TaskStatus, aB as TaskType, aE as TransactionType, aF as TrellisType, aG as UserPrivilege, aI as UserRole, aL as VigorRating, aM as WaterSource } from './constants-C0zV05Cf.mjs';
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-CrAPx_xU.mjs';
@@ -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, o as CAR_SUPPORTED_SIGNATURE_MIME_TYPES, p as CAT, q as CATEGORY_TABS, r as CORRECTIVE_ACTION_GENERATION_SOURCES, s as CORRECTIVE_ACTION_STATUSES, x as CatalogSyncStatus, G as CropCycleStatus, H as DOCUMENT_DISPLAY_INFO, I as DOCUMENT_MENU_MAP, J as DocumentCategory, K as DocumentType, L as ERROR_DICTIONARY, M as EVERYONE, N as EmitterType, O as EnabledModule, P as FORM_REGISTRY, Q as FacilityType, T as HarvestMethod, V as LinkedStatus, W as MOD, X as MODULE_LABELS, Y as MicroclimateZone, Z as MimeType, _ as ModCode, $ as NettingType, a1 as NotificationMode, a2 as OrderStatus, a4 as PhysicalState, a5 as PlantingMethod, a6 as ProductType, a8 as ProductUnit, aa as RESOURCE_MODULE_MAP, ab as RESOURCE_PATHS, ac as RESOURCE_REQUIREMENTS, ad as ROLE_SECTIONS_BY_KEY, ae as RelationshipType, ah as ResourceRequirement, aj as RowOrientation, ak as SENTINEL_UUID, al as SESSION_SCHEMA, am as SIGNATURE_MODES, ar as SoilTexture, as as StockPolicyTier, au as StoragePhases, aw as TASK_TYPE_REGISTRY, ax as TEMPLATE_SCOPE_LEVELS, az as TargetEntityType, aA as TaskStatus, aB as TaskType, aE as TransactionType, aF as TrellisType, aG as UserPrivilege, aI as UserRole, aL as VigorRating, aM as WaterSource } from './constants-C0zV05Cf.js';
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-CrAPx_xU.js';
package/dist/constants.js CHANGED
@@ -150,7 +150,8 @@ var ERROR_DICTIONARY = {
150
150
  "400": { title: "Data Conflict", text: "A conflict occurred with an existing record. Please review and try again." },
151
151
  "402": { title: "Duplicate Entry", text: "A record with this name or identifier already exists." },
152
152
  "403": { title: "Edit Conflict", text: "This record was recently updated by someone else. Please refresh and try again." },
153
- "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." }
154
155
  };
155
156
  var UserRole = {
156
157
  OWNER: "owner",
@@ -62,7 +62,7 @@ import {
62
62
  UserRole,
63
63
  VigorRating,
64
64
  WaterSource
65
- } from "./chunk-GZRAFNPA.mjs";
65
+ } from "./chunk-Y5KVDVNY.mjs";
66
66
  export {
67
67
  ACCESS_RANK,
68
68
  ACT,
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { d as AgriCoreSession, aJ as UserRoleAssignment, ao as ScopeType, aN as WireSession, ah as ResourceRequirement, c as AccessLevelType, j as AvailableScope, U as InfrastructureFields, S as FormMeta, ay as TabConfig, af as ResolvedError } from './constants-C0zV05Cf.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, C as CAR_SUPPORTED_EVIDENCE_MIME_TYPES, o as CAR_SUPPORTED_SIGNATURE_MIME_TYPES, p as CAT, q as CATEGORY_TABS, r as CORRECTIVE_ACTION_GENERATION_SOURCES, s as CORRECTIVE_ACTION_STATUSES, t as CanonicalId, u as CarSupportedEvidenceMimeType, v as CarSupportedSignatureMimeType, w as CarTemplateFileMetadata, x as CatalogSyncStatus, y as CatalogSyncStatusType, z as CompanyConfig, D as CorrectiveActionCompileTaskMetadata, E as CorrectiveActionGenerationSource, F as CorrectiveActionStatus, G as CropCycleStatus, H as DOCUMENT_DISPLAY_INFO, I as DOCUMENT_MENU_MAP, J as DocumentCategory, K as DocumentType, L as ERROR_DICTIONARY, M as EVERYONE, N as EmitterType, O as EnabledModule, P as FORM_REGISTRY, Q as FacilityType, R as FileUploadTaskType, T as HarvestMethod, V as LinkedStatus, W as MOD, X as MODULE_LABELS, Y as MicroclimateZone, Z as MimeType, _ as ModCode, $ as NettingType, a0 as NormalizedSignatureValue, a1 as NotificationMode, a2 as OrderStatus, a3 as PermissionLevel, a4 as PhysicalState, a5 as PlantingMethod, a6 as ProductType, a7 as ProductTypeType, a8 as ProductUnit, a9 as ProductUnitType, aa as RESOURCE_MODULE_MAP, ab as RESOURCE_PATHS, ac as RESOURCE_REQUIREMENTS, ad as ROLE_SECTIONS_BY_KEY, ae as RelationshipType, ag as ResourceOverrides, ai as RoleAssignment, aj as RowOrientation, ak as SENTINEL_UUID, al as SESSION_SCHEMA, am as SIGNATURE_MODES, an as SchemaField, ap as SignatureInput, aq as SignatureMode, ar as SoilTexture, as as StockPolicyTier, at as StockPolicyTierType, au as StoragePhases, av as StorageType, aw as TASK_TYPE_REGISTRY, ax as TEMPLATE_SCOPE_LEVELS, az as TargetEntityType, aA as TaskStatus, aB as TaskType, aC as TaskTypeConfig, aD as TemplateScopeLevel, aE as TransactionType, aF as TrellisType, aG as UserPrivilege, aH as UserPrivilegeType, aI as UserRole, aK as UserRoleType, aL as VigorRating, aM as WaterSource } from './constants-C0zV05Cf.mjs';
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-CrAPx_xU.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-CrAPx_xU.mjs';
3
3
 
4
4
  declare const AgriLogger: {
5
5
  log(level: "INFO" | "WARN" | "ERROR", message: string, meta: any): void;
@@ -18,6 +18,42 @@ 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
+ interface PricingAdapterScope {
42
+ authScopeId: string;
43
+ companyId?: string;
44
+ }
45
+ declare function createBase44PricingAdapter(db: any, scope: PricingAdapterScope): PricingDataSource;
46
+ interface OverlapCheckScope extends PricingAdapterScope {
47
+ productId: string;
48
+ accountCode?: string;
49
+ contractReferenceNumber?: string;
50
+ matrixType?: 'procurement_buy' | 'contract_packing_charge';
51
+ }
52
+ declare function findOverlappingPriceWindows(db: any, scope: OverlapCheckScope, newRow: {
53
+ validFrom: string;
54
+ validTo: string | null;
55
+ }, excludeId?: string): Promise<PriceConflictCandidate[]>;
56
+
21
57
  declare const Validator: {
22
58
  /** * 1. Email: The classic. Uses a standard RFC 5322 regex.
23
59
  */
@@ -84,15 +120,16 @@ declare class AppError extends Error {
84
120
  status: number;
85
121
  appCode: string;
86
122
  isOperational: boolean;
87
- constructor(status: number, mod: string, cat: string, act: string, message: string);
123
+ details?: unknown;
124
+ constructor(status: number, mod: string, cat: string, act: string, message: string, details?: unknown);
88
125
  }
89
126
  declare const ErrorFactory: {
90
127
  unauthorized: (mod: string, msg?: string) => AppError;
91
128
  forbidden: (mod: string, act: string, msg?: string) => AppError;
92
129
  badRequest: (mod: string, act: string, msg?: string) => AppError;
93
130
  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;
131
+ conflict: (mod: string, act: string, msg?: string, details?: unknown) => AppError;
132
+ integrity: (mod: string, act?: "00", msg?: string, details?: unknown) => AppError;
96
133
  subscription: (mod: string, msg?: string) => AppError;
97
134
  internal: (mod: string, msg?: string) => AppError;
98
135
  };
@@ -178,4 +215,4 @@ declare function calculateFileMetadataMatch(criteria: {
178
215
  contextMatch?: string[];
179
216
  }, fileMetadata: Record<string, any>, auditContext: Record<string, any>): number;
180
217
 
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 };
218
+ export { AccessLevelType, AggregatePriceAmbiguityError, AgriCoreSession, AgriLogger, AppError, AvailableScope, Converter, ErrorFactory, FormMeta, InfrastructureFields, type OverlapCheckScope, PriceAmbiguityError, PriceConflictCandidate, PriceResolutionResult, type PricingAdapterScope, PricingContext, PricingDataSource, ResolvedError, ResourceRequirement, ScopeType, ShadowedCleanMatch, TabConfig, Tier, UserRoleAssignment, Validator, WireSession, assertTenantBoundary, calculateFileMetadataMatch, createBase44PricingAdapter, decodeSession, encodeSession, evaluateBaseAccess, extractAppCode, findOverlappingPriceWindows, 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, aJ as UserRoleAssignment, ao as ScopeType, aN as WireSession, ah as ResourceRequirement, c as AccessLevelType, j as AvailableScope, U as InfrastructureFields, S as FormMeta, ay as TabConfig, af as ResolvedError } from './constants-C0zV05Cf.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, C as CAR_SUPPORTED_EVIDENCE_MIME_TYPES, o as CAR_SUPPORTED_SIGNATURE_MIME_TYPES, p as CAT, q as CATEGORY_TABS, r as CORRECTIVE_ACTION_GENERATION_SOURCES, s as CORRECTIVE_ACTION_STATUSES, t as CanonicalId, u as CarSupportedEvidenceMimeType, v as CarSupportedSignatureMimeType, w as CarTemplateFileMetadata, x as CatalogSyncStatus, y as CatalogSyncStatusType, z as CompanyConfig, D as CorrectiveActionCompileTaskMetadata, E as CorrectiveActionGenerationSource, F as CorrectiveActionStatus, G as CropCycleStatus, H as DOCUMENT_DISPLAY_INFO, I as DOCUMENT_MENU_MAP, J as DocumentCategory, K as DocumentType, L as ERROR_DICTIONARY, M as EVERYONE, N as EmitterType, O as EnabledModule, P as FORM_REGISTRY, Q as FacilityType, R as FileUploadTaskType, T as HarvestMethod, V as LinkedStatus, W as MOD, X as MODULE_LABELS, Y as MicroclimateZone, Z as MimeType, _ as ModCode, $ as NettingType, a0 as NormalizedSignatureValue, a1 as NotificationMode, a2 as OrderStatus, a3 as PermissionLevel, a4 as PhysicalState, a5 as PlantingMethod, a6 as ProductType, a7 as ProductTypeType, a8 as ProductUnit, a9 as ProductUnitType, aa as RESOURCE_MODULE_MAP, ab as RESOURCE_PATHS, ac as RESOURCE_REQUIREMENTS, ad as ROLE_SECTIONS_BY_KEY, ae as RelationshipType, ag as ResourceOverrides, ai as RoleAssignment, aj as RowOrientation, ak as SENTINEL_UUID, al as SESSION_SCHEMA, am as SIGNATURE_MODES, an as SchemaField, ap as SignatureInput, aq as SignatureMode, ar as SoilTexture, as as StockPolicyTier, at as StockPolicyTierType, au as StoragePhases, av as StorageType, aw as TASK_TYPE_REGISTRY, ax as TEMPLATE_SCOPE_LEVELS, az as TargetEntityType, aA as TaskStatus, aB as TaskType, aC as TaskTypeConfig, aD as TemplateScopeLevel, aE as TransactionType, aF as TrellisType, aG as UserPrivilege, aH as UserPrivilegeType, aI as UserRole, aK as UserRoleType, aL as VigorRating, aM as WaterSource } from './constants-C0zV05Cf.js';
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-CrAPx_xU.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-CrAPx_xU.js';
3
3
 
4
4
  declare const AgriLogger: {
5
5
  log(level: "INFO" | "WARN" | "ERROR", message: string, meta: any): void;
@@ -18,6 +18,42 @@ 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
+ interface PricingAdapterScope {
42
+ authScopeId: string;
43
+ companyId?: string;
44
+ }
45
+ declare function createBase44PricingAdapter(db: any, scope: PricingAdapterScope): PricingDataSource;
46
+ interface OverlapCheckScope extends PricingAdapterScope {
47
+ productId: string;
48
+ accountCode?: string;
49
+ contractReferenceNumber?: string;
50
+ matrixType?: 'procurement_buy' | 'contract_packing_charge';
51
+ }
52
+ declare function findOverlappingPriceWindows(db: any, scope: OverlapCheckScope, newRow: {
53
+ validFrom: string;
54
+ validTo: string | null;
55
+ }, excludeId?: string): Promise<PriceConflictCandidate[]>;
56
+
21
57
  declare const Validator: {
22
58
  /** * 1. Email: The classic. Uses a standard RFC 5322 regex.
23
59
  */
@@ -84,15 +120,16 @@ declare class AppError extends Error {
84
120
  status: number;
85
121
  appCode: string;
86
122
  isOperational: boolean;
87
- constructor(status: number, mod: string, cat: string, act: string, message: string);
123
+ details?: unknown;
124
+ constructor(status: number, mod: string, cat: string, act: string, message: string, details?: unknown);
88
125
  }
89
126
  declare const ErrorFactory: {
90
127
  unauthorized: (mod: string, msg?: string) => AppError;
91
128
  forbidden: (mod: string, act: string, msg?: string) => AppError;
92
129
  badRequest: (mod: string, act: string, msg?: string) => AppError;
93
130
  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;
131
+ conflict: (mod: string, act: string, msg?: string, details?: unknown) => AppError;
132
+ integrity: (mod: string, act?: "00", msg?: string, details?: unknown) => AppError;
96
133
  subscription: (mod: string, msg?: string) => AppError;
97
134
  internal: (mod: string, msg?: string) => AppError;
98
135
  };
@@ -178,4 +215,4 @@ declare function calculateFileMetadataMatch(criteria: {
178
215
  contextMatch?: string[];
179
216
  }, fileMetadata: Record<string, any>, auditContext: Record<string, any>): number;
180
217
 
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 };
218
+ export { AccessLevelType, AggregatePriceAmbiguityError, AgriCoreSession, AgriLogger, AppError, AvailableScope, Converter, ErrorFactory, FormMeta, InfrastructureFields, type OverlapCheckScope, PriceAmbiguityError, PriceConflictCandidate, PriceResolutionResult, type PricingAdapterScope, PricingContext, PricingDataSource, ResolvedError, ResourceRequirement, ScopeType, ShadowedCleanMatch, TabConfig, Tier, UserRoleAssignment, Validator, WireSession, assertTenantBoundary, calculateFileMetadataMatch, createBase44PricingAdapter, decodeSession, encodeSession, evaluateBaseAccess, extractAppCode, findOverlappingPriceWindows, 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,10 +92,12 @@ __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,
96
99
  extractAppCode: () => extractAppCode,
100
+ findOverlappingPriceWindows: () => findOverlappingPriceWindows,
97
101
  findSpecificOverride: () => findSpecificOverride,
98
102
  generateCanonicalId: () => generateCanonicalId,
99
103
  generateDocumentPath: () => generateDocumentPath,
@@ -115,6 +119,7 @@ __export(index_exports, {
115
119
  resolveAppError: () => resolveAppError,
116
120
  resolveEffectiveAccess: () => resolveEffectiveAccess,
117
121
  resolveError: () => resolveError,
122
+ resolvePrice: () => resolvePrice,
118
123
  transformRoleAssignments: () => transformRoleAssignments,
119
124
  verifyAndExtractSession: () => verifyAndExtractSession,
120
125
  withAgriLogging: () => withAgriLogging
@@ -247,6 +252,224 @@ function transformRoleAssignments(assignments) {
247
252
  return result;
248
253
  }
249
254
 
255
+ // src/priceHelpers.ts
256
+ var PriceAmbiguityError = class extends Error {
257
+ constructor(productId, tier, candidates, matchedOn = {}) {
258
+ const keyDescription = matchedOn.contractReferenceNumber ? `contract ${matchedOn.contractReferenceNumber}` : matchedOn.accountCode ? `account ${matchedOn.accountCode}` : "the system default";
259
+ super(
260
+ `Ambiguous pricing for product ${productId} at tier ${tier} (${keyDescription}): ${candidates.length} overlapping active prices found (${candidates.map((c) => `$${(c.unitPriceCents / 100).toFixed(2)}`).join(", ")}).`
261
+ );
262
+ this.productId = productId;
263
+ this.tier = tier;
264
+ this.candidates = candidates;
265
+ this.matchedOn = matchedOn;
266
+ this.name = "PriceAmbiguityError";
267
+ }
268
+ productId;
269
+ tier;
270
+ candidates;
271
+ matchedOn;
272
+ };
273
+ var AggregatePriceAmbiguityError = class extends Error {
274
+ constructor(productId, conflicts, shadowedCleanMatches = []) {
275
+ const tierList = conflicts.map((c) => c.tier).join(", ");
276
+ 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.` : "";
277
+ super(
278
+ `Pricing for product ${productId} could not be resolved: ambiguous prices found at ${conflicts.length} tier(s) [${tierList}].${note}`
279
+ );
280
+ this.productId = productId;
281
+ this.conflicts = conflicts;
282
+ this.shadowedCleanMatches = shadowedCleanMatches;
283
+ this.name = "AggregatePriceAmbiguityError";
284
+ }
285
+ productId;
286
+ conflicts;
287
+ shadowedCleanMatches;
288
+ };
289
+ function fromMatrixRow(row, priceSource) {
290
+ return {
291
+ unitPriceCents: row.matrixUnitPriceCents,
292
+ minimumOrderQuantity: row.minimumOrderQuantity,
293
+ leadTimeDays: row.leadTimeDays,
294
+ priceSource,
295
+ sourceRecordId: row.canonicalId
296
+ };
297
+ }
298
+ async function resolvePrice(dataSource, context) {
299
+ const targetDate = context.evaluationDate ?? /* @__PURE__ */ new Date();
300
+ const conflicts = [];
301
+ const shadowedCleanMatches = [];
302
+ async function tryTier(lookup) {
303
+ try {
304
+ return await lookup();
305
+ } catch (err) {
306
+ if (err instanceof PriceAmbiguityError) {
307
+ conflicts.push(err);
308
+ return null;
309
+ }
310
+ throw err;
311
+ }
312
+ }
313
+ function handleCleanRow(row, tier, matchedOn) {
314
+ if (!row) return null;
315
+ if (conflicts.length === 0) return fromMatrixRow(row, tier);
316
+ shadowedCleanMatches.push({
317
+ tier,
318
+ unitPriceCents: row.matrixUnitPriceCents,
319
+ sourceRecordId: row.canonicalId,
320
+ matchedOn
321
+ });
322
+ return null;
323
+ }
324
+ if (context.contractReferenceNumber) {
325
+ const row = await tryTier(
326
+ () => dataSource.findContractPrice({
327
+ productId: context.productId,
328
+ contractReferenceNumber: context.contractReferenceNumber,
329
+ targetDate
330
+ })
331
+ );
332
+ const result = handleCleanRow(row, "EXPLICIT_CONTRACT", {
333
+ contractReferenceNumber: context.contractReferenceNumber
334
+ });
335
+ if (result) return result;
336
+ }
337
+ if (context.accountCode && context.accountCode !== "INTERNAL_DEFAULT") {
338
+ const row = await tryTier(
339
+ () => dataSource.findAccountPrice({
340
+ productId: context.productId,
341
+ accountCode: context.accountCode,
342
+ targetDate
343
+ })
344
+ );
345
+ const result = handleCleanRow(row, "ACCOUNT_MATRIX", { accountCode: context.accountCode });
346
+ if (result) return result;
347
+ }
348
+ const defaultRow = await tryTier(
349
+ () => dataSource.findDefaultPrice({ productId: context.productId, targetDate })
350
+ );
351
+ const defaultResult = handleCleanRow(defaultRow, "INTERNAL_DEFAULT", {});
352
+ if (defaultResult) return defaultResult;
353
+ if (conflicts.length > 0) {
354
+ throw new AggregatePriceAmbiguityError(context.productId, conflicts, shadowedCleanMatches);
355
+ }
356
+ const baseProduct = await dataSource.findBaseProduct({ productId: context.productId });
357
+ if (baseProduct) {
358
+ return {
359
+ unitPriceCents: baseProduct.unitPriceCents,
360
+ minimumOrderQuantity: 1,
361
+ leadTimeDays: null,
362
+ priceSource: "PRODUCT_FALLBACK",
363
+ sourceRecordId: baseProduct.canonicalId
364
+ };
365
+ }
366
+ throw new Error(`Critical Architecture Error: Product ${context.productId} not found in system catalog.`);
367
+ }
368
+ var ACTIVE_ROW_LOOKBACK = 5;
369
+ var SORT_MOST_RECENT_FIRST = "-validFrom";
370
+ function pickActiveRow(rows, targetIso, productId, tier, matchedOn) {
371
+ const activeMatches = rows.filter(
372
+ (r) => r.validTo === null || new Date(r.validTo).toISOString() >= targetIso
373
+ );
374
+ if (activeMatches.length > 1) {
375
+ throw new PriceAmbiguityError(
376
+ productId,
377
+ tier,
378
+ activeMatches.map((r) => ({
379
+ sourceRecordId: r.canonicalId,
380
+ unitPriceCents: r.matrixUnitPriceCents,
381
+ validFrom: r.validFrom,
382
+ validTo: r.validTo
383
+ })),
384
+ matchedOn
385
+ );
386
+ }
387
+ return activeMatches[0] ?? null;
388
+ }
389
+ function createBase44PricingAdapter(db, scope) {
390
+ const scopeFilter = {
391
+ authScopeId: scope.authScopeId,
392
+ ...scope.companyId ? { companyId: scope.companyId } : {}
393
+ };
394
+ return {
395
+ async findContractPrice({ productId, contractReferenceNumber, targetDate }) {
396
+ const targetIso = targetDate.toISOString();
397
+ const rows = await db.ProductPriceMatrix.filter(
398
+ {
399
+ ...scopeFilter,
400
+ productId,
401
+ contractReferenceNumber,
402
+ matrixType: "procurement_buy",
403
+ validFrom_lte: targetIso
404
+ },
405
+ SORT_MOST_RECENT_FIRST,
406
+ ACTIVE_ROW_LOOKBACK
407
+ );
408
+ return pickActiveRow(rows, targetIso, productId, "EXPLICIT_CONTRACT", {
409
+ contractReferenceNumber
410
+ });
411
+ },
412
+ async findAccountPrice({ productId, accountCode, targetDate }) {
413
+ const targetIso = targetDate.toISOString();
414
+ const rows = await db.ProductPriceMatrix.filter(
415
+ { ...scopeFilter, productId, accountCode, validFrom_lte: targetIso },
416
+ SORT_MOST_RECENT_FIRST,
417
+ ACTIVE_ROW_LOOKBACK
418
+ );
419
+ return pickActiveRow(rows, targetIso, productId, "ACCOUNT_MATRIX", { accountCode });
420
+ },
421
+ async findDefaultPrice({ productId, targetDate }) {
422
+ const targetIso = targetDate.toISOString();
423
+ const rows = await db.ProductPriceMatrix.filter(
424
+ { ...scopeFilter, productId, accountCode: "INTERNAL_DEFAULT", validFrom_lte: targetIso },
425
+ SORT_MOST_RECENT_FIRST,
426
+ ACTIVE_ROW_LOOKBACK
427
+ );
428
+ return pickActiveRow(rows, targetIso, productId, "INTERNAL_DEFAULT", {});
429
+ },
430
+ async findBaseProduct({ productId }) {
431
+ const rows = await db.CompanyProduct.filter({ id: productId, authScopeId: scope.authScopeId });
432
+ return rows[0] ?? null;
433
+ }
434
+ };
435
+ }
436
+ function rangesOverlap(aFrom, aTo, bFrom, bTo) {
437
+ const aStart = new Date(aFrom).getTime();
438
+ const aEnd = aTo ? new Date(aTo).getTime() : Infinity;
439
+ const bStart = new Date(bFrom).getTime();
440
+ const bEnd = bTo ? new Date(bTo).getTime() : Infinity;
441
+ return aStart <= bEnd && bStart <= aEnd;
442
+ }
443
+ async function findOverlappingPriceWindows(db, scope, newRow, excludeId) {
444
+ const { productId, accountCode, contractReferenceNumber, matrixType, authScopeId, companyId } = scope;
445
+ const scopeFilter = {
446
+ authScopeId,
447
+ ...companyId ? { companyId } : {}
448
+ };
449
+ const existingRows = await db.ProductPriceMatrix.filter(
450
+ {
451
+ ...scopeFilter,
452
+ productId,
453
+ ...accountCode ? { accountCode } : {},
454
+ ...contractReferenceNumber ? { contractReferenceNumber } : {},
455
+ ...matrixType ? { matrixType } : {}
456
+ },
457
+ SORT_MOST_RECENT_FIRST,
458
+ // Write-time validation must see the product's full pricing history in
459
+ // this scope, not just rows near "now" — ACTIVE_ROW_LOOKBACK (5) is
460
+ // read-time-specific and too shallow here.
461
+ 100
462
+ );
463
+ return existingRows.filter(
464
+ (r) => r.canonicalId !== excludeId && rangesOverlap(newRow.validFrom, newRow.validTo, r.validFrom, r.validTo)
465
+ ).map((r) => ({
466
+ sourceRecordId: r.canonicalId,
467
+ unitPriceCents: r.matrixUnitPriceCents,
468
+ validFrom: r.validFrom,
469
+ validTo: r.validTo
470
+ }));
471
+ }
472
+
250
473
  // src/validators.ts
251
474
  var Validator = {
252
475
  /** * 1. Email: The classic. Uses a standard RFC 5322 regex.
@@ -436,7 +659,8 @@ var ERROR_DICTIONARY = {
436
659
  "400": { title: "Data Conflict", text: "A conflict occurred with an existing record. Please review and try again." },
437
660
  "402": { title: "Duplicate Entry", text: "A record with this name or identifier already exists." },
438
661
  "403": { title: "Edit Conflict", text: "This record was recently updated by someone else. Please refresh and try again." },
439
- "404": { title: "Record In Use", text: "This record cannot be deleted because it is referenced by other data." }
662
+ "404": { title: "Record In Use", text: "This record cannot be deleted because it is referenced by other data." },
663
+ "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." }
440
664
  };
441
665
  var UserRole = {
442
666
  OWNER: "owner",
@@ -1333,11 +1557,14 @@ var AppError = class extends Error {
1333
1557
  status;
1334
1558
  appCode;
1335
1559
  isOperational;
1336
- constructor(status, mod, cat, act, message) {
1560
+ details;
1561
+ // optional structured payload, e.g. conflicting price candidates
1562
+ constructor(status, mod, cat, act, message, details) {
1337
1563
  super(message);
1338
1564
  this.status = status;
1339
1565
  this.appCode = `${mod}-${status}-${cat}${act}`;
1340
1566
  this.isOperational = true;
1567
+ this.details = details;
1341
1568
  Object.setPrototypeOf(this, new.target.prototype);
1342
1569
  }
1343
1570
  };
@@ -1346,15 +1573,21 @@ var ErrorFactory = {
1346
1573
  forbidden: (mod, act, msg = "Access denied") => new AppError(403, mod, CAT.PERMISSION, act, msg),
1347
1574
  badRequest: (mod, act, msg = "Bad request") => new AppError(400, mod, CAT.VALIDATION, act, msg),
1348
1575
  notFound: (mod, act = ACT.GENERIC, msg = "Record not found") => new AppError(404, mod, CAT.VALIDATION, act, msg),
1349
- conflict: (mod, act, msg = "Record already exists") => new AppError(409, mod, CAT.INTEGRITY, act, msg),
1350
- integrity: (mod, act = ACT.GENERIC, msg = "Data integrity violation") => new AppError(409, mod, CAT.INTEGRITY, act, msg),
1576
+ conflict: (mod, act, msg = "Record already exists", details) => new AppError(409, mod, CAT.INTEGRITY, act, msg, details),
1577
+ integrity: (mod, act = ACT.GENERIC, msg = "Data integrity violation", details) => new AppError(409, mod, CAT.INTEGRITY, act, msg, details),
1351
1578
  subscription: (mod, msg = "Module locked or subscription required") => new AppError(403, mod, CAT.SUBSCRIPTION, ACT.GENERIC, msg),
1352
1579
  internal: (mod, msg = "Internal server error") => new AppError(500, mod, CAT.INTEGRITY, ACT.GENERIC, msg)
1353
1580
  };
1354
1581
  var handleAppErrorResponse = (error, mod = MOD.ADMIN) => {
1355
1582
  if (error instanceof AppError && error.isOperational) {
1356
1583
  return Response.json(
1357
- { error: { appCode: error.appCode, message: error.message } },
1584
+ {
1585
+ error: {
1586
+ appCode: error.appCode,
1587
+ message: error.message,
1588
+ ...error.details ? { details: error.details } : {}
1589
+ }
1590
+ },
1358
1591
  { status: error.status }
1359
1592
  );
1360
1593
  }
@@ -1718,6 +1951,7 @@ function calculateFileMetadataMatch(criteria, fileMetadata, auditContext) {
1718
1951
  ACCESS_RANK,
1719
1952
  ACT,
1720
1953
  AccessLevel,
1954
+ AggregatePriceAmbiguityError,
1721
1955
  AgriLogger,
1722
1956
  AppError,
1723
1957
  ApprovalStatus,
@@ -1757,6 +1991,7 @@ function calculateFileMetadataMatch(criteria, fileMetadata, auditContext) {
1757
1991
  OrderStatus,
1758
1992
  PhysicalState,
1759
1993
  PlantingMethod,
1994
+ PriceAmbiguityError,
1760
1995
  ProductType,
1761
1996
  ProductUnit,
1762
1997
  RESOURCE_MODULE_MAP,
@@ -1785,10 +2020,12 @@ function calculateFileMetadataMatch(criteria, fileMetadata, auditContext) {
1785
2020
  WaterSource,
1786
2021
  assertTenantBoundary,
1787
2022
  calculateFileMetadataMatch,
2023
+ createBase44PricingAdapter,
1788
2024
  decodeSession,
1789
2025
  encodeSession,
1790
2026
  evaluateBaseAccess,
1791
2027
  extractAppCode,
2028
+ findOverlappingPriceWindows,
1792
2029
  findSpecificOverride,
1793
2030
  generateCanonicalId,
1794
2031
  generateDocumentPath,
@@ -1810,6 +2047,7 @@ function calculateFileMetadataMatch(criteria, fileMetadata, auditContext) {
1810
2047
  resolveAppError,
1811
2048
  resolveEffectiveAccess,
1812
2049
  resolveError,
2050
+ resolvePrice,
1813
2051
  transformRoleAssignments,
1814
2052
  verifyAndExtractSession,
1815
2053
  withAgriLogging
package/dist/index.mjs CHANGED
@@ -62,7 +62,7 @@ import {
62
62
  UserRole,
63
63
  VigorRating,
64
64
  WaterSource
65
- } from "./chunk-GZRAFNPA.mjs";
65
+ } from "./chunk-Y5KVDVNY.mjs";
66
66
 
67
67
  // src/index.ts
68
68
  import { jwtVerify } from "jose";
@@ -192,6 +192,224 @@ 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.matrixUnitPriceCents,
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.matrixUnitPriceCents,
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.matrixUnitPriceCents,
321
+ validFrom: r.validFrom,
322
+ validTo: r.validTo
323
+ })),
324
+ matchedOn
325
+ );
326
+ }
327
+ return activeMatches[0] ?? null;
328
+ }
329
+ function createBase44PricingAdapter(db, scope) {
330
+ const scopeFilter = {
331
+ authScopeId: scope.authScopeId,
332
+ ...scope.companyId ? { companyId: scope.companyId } : {}
333
+ };
334
+ return {
335
+ async findContractPrice({ productId, contractReferenceNumber, targetDate }) {
336
+ const targetIso = targetDate.toISOString();
337
+ const rows = await db.ProductPriceMatrix.filter(
338
+ {
339
+ ...scopeFilter,
340
+ productId,
341
+ contractReferenceNumber,
342
+ matrixType: "procurement_buy",
343
+ validFrom_lte: targetIso
344
+ },
345
+ SORT_MOST_RECENT_FIRST,
346
+ ACTIVE_ROW_LOOKBACK
347
+ );
348
+ return pickActiveRow(rows, targetIso, productId, "EXPLICIT_CONTRACT", {
349
+ contractReferenceNumber
350
+ });
351
+ },
352
+ async findAccountPrice({ productId, accountCode, targetDate }) {
353
+ const targetIso = targetDate.toISOString();
354
+ const rows = await db.ProductPriceMatrix.filter(
355
+ { ...scopeFilter, productId, accountCode, validFrom_lte: targetIso },
356
+ SORT_MOST_RECENT_FIRST,
357
+ ACTIVE_ROW_LOOKBACK
358
+ );
359
+ return pickActiveRow(rows, targetIso, productId, "ACCOUNT_MATRIX", { accountCode });
360
+ },
361
+ async findDefaultPrice({ productId, targetDate }) {
362
+ const targetIso = targetDate.toISOString();
363
+ const rows = await db.ProductPriceMatrix.filter(
364
+ { ...scopeFilter, productId, accountCode: "INTERNAL_DEFAULT", validFrom_lte: targetIso },
365
+ SORT_MOST_RECENT_FIRST,
366
+ ACTIVE_ROW_LOOKBACK
367
+ );
368
+ return pickActiveRow(rows, targetIso, productId, "INTERNAL_DEFAULT", {});
369
+ },
370
+ async findBaseProduct({ productId }) {
371
+ const rows = await db.CompanyProduct.filter({ id: productId, authScopeId: scope.authScopeId });
372
+ return rows[0] ?? null;
373
+ }
374
+ };
375
+ }
376
+ function rangesOverlap(aFrom, aTo, bFrom, bTo) {
377
+ const aStart = new Date(aFrom).getTime();
378
+ const aEnd = aTo ? new Date(aTo).getTime() : Infinity;
379
+ const bStart = new Date(bFrom).getTime();
380
+ const bEnd = bTo ? new Date(bTo).getTime() : Infinity;
381
+ return aStart <= bEnd && bStart <= aEnd;
382
+ }
383
+ async function findOverlappingPriceWindows(db, scope, newRow, excludeId) {
384
+ const { productId, accountCode, contractReferenceNumber, matrixType, authScopeId, companyId } = scope;
385
+ const scopeFilter = {
386
+ authScopeId,
387
+ ...companyId ? { companyId } : {}
388
+ };
389
+ const existingRows = await db.ProductPriceMatrix.filter(
390
+ {
391
+ ...scopeFilter,
392
+ productId,
393
+ ...accountCode ? { accountCode } : {},
394
+ ...contractReferenceNumber ? { contractReferenceNumber } : {},
395
+ ...matrixType ? { matrixType } : {}
396
+ },
397
+ SORT_MOST_RECENT_FIRST,
398
+ // Write-time validation must see the product's full pricing history in
399
+ // this scope, not just rows near "now" — ACTIVE_ROW_LOOKBACK (5) is
400
+ // read-time-specific and too shallow here.
401
+ 100
402
+ );
403
+ return existingRows.filter(
404
+ (r) => r.canonicalId !== excludeId && rangesOverlap(newRow.validFrom, newRow.validTo, r.validFrom, r.validTo)
405
+ ).map((r) => ({
406
+ sourceRecordId: r.canonicalId,
407
+ unitPriceCents: r.matrixUnitPriceCents,
408
+ validFrom: r.validFrom,
409
+ validTo: r.validTo
410
+ }));
411
+ }
412
+
195
413
  // src/validators.ts
196
414
  var Validator = {
197
415
  /** * 1. Email: The classic. Uses a standard RFC 5322 regex.
@@ -320,11 +538,14 @@ var AppError = class extends Error {
320
538
  status;
321
539
  appCode;
322
540
  isOperational;
323
- constructor(status, mod, cat, act, message) {
541
+ details;
542
+ // optional structured payload, e.g. conflicting price candidates
543
+ constructor(status, mod, cat, act, message, details) {
324
544
  super(message);
325
545
  this.status = status;
326
546
  this.appCode = `${mod}-${status}-${cat}${act}`;
327
547
  this.isOperational = true;
548
+ this.details = details;
328
549
  Object.setPrototypeOf(this, new.target.prototype);
329
550
  }
330
551
  };
@@ -333,15 +554,21 @@ var ErrorFactory = {
333
554
  forbidden: (mod, act, msg = "Access denied") => new AppError(403, mod, CAT.PERMISSION, act, msg),
334
555
  badRequest: (mod, act, msg = "Bad request") => new AppError(400, mod, CAT.VALIDATION, act, msg),
335
556
  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),
557
+ conflict: (mod, act, msg = "Record already exists", details) => new AppError(409, mod, CAT.INTEGRITY, act, msg, details),
558
+ integrity: (mod, act = ACT.GENERIC, msg = "Data integrity violation", details) => new AppError(409, mod, CAT.INTEGRITY, act, msg, details),
338
559
  subscription: (mod, msg = "Module locked or subscription required") => new AppError(403, mod, CAT.SUBSCRIPTION, ACT.GENERIC, msg),
339
560
  internal: (mod, msg = "Internal server error") => new AppError(500, mod, CAT.INTEGRITY, ACT.GENERIC, msg)
340
561
  };
341
562
  var handleAppErrorResponse = (error, mod = MOD.ADMIN) => {
342
563
  if (error instanceof AppError && error.isOperational) {
343
564
  return Response.json(
344
- { error: { appCode: error.appCode, message: error.message } },
565
+ {
566
+ error: {
567
+ appCode: error.appCode,
568
+ message: error.message,
569
+ ...error.details ? { details: error.details } : {}
570
+ }
571
+ },
345
572
  { status: error.status }
346
573
  );
347
574
  }
@@ -704,6 +931,7 @@ export {
704
931
  ACCESS_RANK,
705
932
  ACT,
706
933
  AccessLevel,
934
+ AggregatePriceAmbiguityError,
707
935
  AgriLogger,
708
936
  AppError,
709
937
  ApprovalStatus,
@@ -743,6 +971,7 @@ export {
743
971
  OrderStatus,
744
972
  PhysicalState,
745
973
  PlantingMethod,
974
+ PriceAmbiguityError,
746
975
  ProductType,
747
976
  ProductUnit,
748
977
  RESOURCE_MODULE_MAP,
@@ -771,10 +1000,12 @@ export {
771
1000
  WaterSource,
772
1001
  assertTenantBoundary,
773
1002
  calculateFileMetadataMatch,
1003
+ createBase44PricingAdapter,
774
1004
  decodeSession,
775
1005
  encodeSession,
776
1006
  evaluateBaseAccess,
777
1007
  extractAppCode,
1008
+ findOverlappingPriceWindows,
778
1009
  findSpecificOverride,
779
1010
  generateCanonicalId,
780
1011
  generateDocumentPath,
@@ -796,6 +1027,7 @@ export {
796
1027
  resolveAppError,
797
1028
  resolveEffectiveAccess,
798
1029
  resolveError,
1030
+ resolvePrice,
799
1031
  transformRoleAssignments,
800
1032
  verifyAndExtractSession,
801
1033
  withAgriLogging
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@litusarchieve18/agricore-utils",
3
- "version": "1.0.26",
3
+ "version": "1.0.28",
4
4
  "description": "Canonical Backend Utility Library for AgriCore",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",