fix: merging entity cus feature legacy data and passing ctx down to version change module

This commit is contained in:
John Yeo
2025-12-02 10:12:57 +00:00
parent 5955f9a7d7
commit c3ac0bca48
34 changed files with 515 additions and 140 deletions

View File

@@ -2,6 +2,159 @@
-- GET CUSTOMER/ENTITY UTILITY FUNCTIONS
-- ============================================================================
-- ============================================================================
-- LEGACY DATA MERGE UTILITIES
-- ============================================================================
-- Helper function to merge breakdown_legacy_data by key
-- Sums prepaid_quantity for matching keys, adds new items for non-matching keys
-- Mutates targetBreakdown by merging sourceBreakdown into it
local function mergeBreakdownLegacyData(targetBreakdown, sourceBreakdown)
if not sourceBreakdown then return end
if not targetBreakdown then return end
for _, sourceItem in ipairs(sourceBreakdown) do
local foundMatch = false
-- Try to find matching item by key
for _, targetItem in ipairs(targetBreakdown) do
if sourceItem.key and targetItem.key and sourceItem.key == targetItem.key then
-- Found match - sum prepaid_quantity
targetItem.prepaid_quantity = (targetItem.prepaid_quantity or 0) + (sourceItem.prepaid_quantity or 0)
foundMatch = true
break
end
end
-- If no match found, add as new item
if not foundMatch then
table.insert(targetBreakdown, {
key = sourceItem.key,
prepaid_quantity = sourceItem.prepaid_quantity or 0
})
end
end
end
-- Helper function to create a virtual breakdown item from top-level legacy data
-- Used when legacy data has no breakdown_legacy_data array but needs to be merged
local function legacyDataToBreakdownItem(featureData)
return {
key = featureData.key or "",
prepaid_quantity = featureData.prepaid_quantity or 0
}
end
-- Helper function to merge cusFeatureLegacyData from source into target
-- Merges by feature_id: sums prepaid_quantity, merges breakdown_legacy_data by key
-- Similar to balance breakdown merging:
-- - Each legacy data without breakdown_legacy_data is treated as a single "virtual" breakdown item
-- - Breakdown items are matched by key
-- - If keys match: sum prepaid_quantity
-- - If keys differ: create separate breakdown items
-- Mutates targetLegacyData
local function mergeCusFeatureLegacyData(targetLegacyData, sourceLegacyData)
if not sourceLegacyData then return end
if not targetLegacyData then return end
local targetCusFeature = targetLegacyData.cusFeatureLegacyData
local sourceCusFeature = sourceLegacyData.cusFeatureLegacyData
if not sourceCusFeature then return end
-- Initialize target cusFeatureLegacyData if nil
if not targetCusFeature then
targetLegacyData.cusFeatureLegacyData = {}
targetCusFeature = targetLegacyData.cusFeatureLegacyData
end
-- Merge each feature_id from source into target
for featureId, sourceFeatureData in pairs(sourceCusFeature) do
local targetFeatureData = targetCusFeature[featureId]
if targetFeatureData then
-- Feature exists in target - merge values
-- ============================================================================
-- CAPTURE VIRTUAL BREAKDOWN ITEMS BEFORE MUTATING TOP-LEVEL FIELDS
-- ============================================================================
-- Similar to balance merging: capture breakdown items BEFORE summing
-- Get effective breakdown from target
local targetBreakdowns = targetFeatureData.breakdown_legacy_data
local targetHadBreakdown = targetBreakdowns and #targetBreakdowns > 0
if not targetHadBreakdown then
-- Target has no breakdown - create virtual item from top-level fields
targetBreakdowns = { legacyDataToBreakdownItem(targetFeatureData) }
end
-- Get effective breakdown from source
local sourceBreakdowns = sourceFeatureData.breakdown_legacy_data
if not sourceBreakdowns or #sourceBreakdowns == 0 then
-- Source has no breakdown - create virtual item from top-level fields
sourceBreakdowns = { legacyDataToBreakdownItem(sourceFeatureData) }
end
-- ============================================================================
-- NOW MERGE TOP-LEVEL PREPAID_QUANTITY
-- ============================================================================
targetFeatureData.prepaid_quantity = (targetFeatureData.prepaid_quantity or 0) + (sourceFeatureData.prepaid_quantity or 0)
-- ============================================================================
-- MERGE BREAKDOWN ITEMS BY KEY
-- ============================================================================
for _, sourceItem in ipairs(sourceBreakdowns) do
local foundMatch = false
for _, targetItem in ipairs(targetBreakdowns) do
if sourceItem.key and targetItem.key and sourceItem.key == targetItem.key then
-- Found match - sum prepaid_quantity
targetItem.prepaid_quantity = (targetItem.prepaid_quantity or 0) + (sourceItem.prepaid_quantity or 0)
foundMatch = true
break
end
end
-- If no match found, add as new item
if not foundMatch then
table.insert(targetBreakdowns, {
key = sourceItem.key,
prepaid_quantity = sourceItem.prepaid_quantity or 0
})
end
end
-- Set breakdown_legacy_data if we have multiple items or originally had breakdown
if #targetBreakdowns > 1 or targetHadBreakdown then
targetFeatureData.breakdown_legacy_data = targetBreakdowns
-- Clear top-level key since it's now in breakdown items
targetFeatureData.key = nil
end
else
-- Feature doesn't exist in target - add it (deep copy)
local newFeatureData = {
key = sourceFeatureData.key,
prepaid_quantity = sourceFeatureData.prepaid_quantity or 0,
breakdown_legacy_data = {}
}
if sourceFeatureData.breakdown_legacy_data and #sourceFeatureData.breakdown_legacy_data > 0 then
for _, item in ipairs(sourceFeatureData.breakdown_legacy_data) do
table.insert(newFeatureData.breakdown_legacy_data, {
key = item.key,
prepaid_quantity = item.prepaid_quantity or 0
})
end
end
targetCusFeature[featureId] = newFeatureData
end
end
end
-- ============================================================================
-- CUSTOMER/ENTITY RETRIEVAL FUNCTIONS
-- ============================================================================
-- Get customer object with merged balances and subscriptions
-- Parameters:
-- orgId: Organization ID
@@ -95,6 +248,19 @@ local function getCustomerObject(orgId, env, customerId, skipEntityMerge)
-- Merge scheduled_subscriptions by plan ID and normalized status
baseCustomer.scheduled_subscriptions = mergeSubscriptions(allScheduledSubscriptions)
-- ============================================================================
-- MERGE ENTITY LEGACY DATA INTO CUSTOMER LEGACY DATA
-- ============================================================================
if not skipEntityMerge and baseCustomer.legacyData then
for _, entityId in ipairs(entityIds) do
local entityBase = entityBaseData[entityId]
if entityBase and entityBase.legacyData then
mergeCusFeatureLegacyData(baseCustomer.legacyData, entityBase.legacyData)
end
end
end
-- Merge invoices
-- Build final customer object
baseCustomer.invoices = baseCustomer.invoices or nil
@@ -159,19 +325,25 @@ local function getEntityObject(orgId, env, customerId, entityId, skipCustomerMer
local entityScheduledSubscriptions = baseEntity.scheduled_subscriptions or {}
if not skipCustomerMerge then
-- Get customer subscriptions and scheduled_subscriptions
local customerSubscriptions = nil
local customerScheduledSubscriptions = nil
-- Get customer base data for subscriptions and legacy data
local customerBase = nil
local customerBaseJson = redis.call("GET", customerCacheKey)
if customerBaseJson then
local customerBase = cjson.decode(customerBaseJson)
customerSubscriptions = customerBase.subscriptions
customerScheduledSubscriptions = customerBase.scheduled_subscriptions
customerBase = cjson.decode(customerBaseJson)
end
-- Merge customer subscriptions into entity subscriptions (only add if not exists)
local customerSubscriptions = customerBase and customerBase.subscriptions or nil
local customerScheduledSubscriptions = customerBase and customerBase.scheduled_subscriptions or nil
baseEntity.subscriptions = mergeCustomerSubscriptionsIntoEntity(entitySubscriptions, customerSubscriptions)
baseEntity.scheduled_subscriptions = mergeCustomerSubscriptionsIntoEntity(entityScheduledSubscriptions, customerScheduledSubscriptions)
-- ============================================================================
-- MERGE CUSTOMER LEGACY DATA INTO ENTITY LEGACY DATA
-- ============================================================================
if baseEntity.legacyData and customerBase and customerBase.legacyData then
mergeCusFeatureLegacyData(baseEntity.legacyData, customerBase.legacyData)
end
else
-- No merging - just use entity's own subscriptions
baseEntity.subscriptions = entitySubscriptions