Fix product performance grouped JSON build
This commit is contained in:
@@ -3239,16 +3239,16 @@ SELECT (
|
|||||||
'image_product_code', image_product_code,
|
'image_product_code', image_product_code,
|
||||||
'image_color_code', image_color_code,
|
'image_color_code', image_color_code,
|
||||||
'image_yaka_kodu', image_yaka_kodu,
|
'image_yaka_kodu', image_yaka_kodu,
|
||||||
'product_code', CASE WHEN $%d = 'product_code' THEN group_value ELSE product_code END,
|
'product_code', CASE WHEN $%d = 'product_code' THEN group_value ELSE '' END,
|
||||||
'color_code', CASE WHEN $%d = 'color_code' THEN group_value ELSE color_code END,
|
'color_code', CASE WHEN $%d = 'color_code' THEN group_value ELSE '' END,
|
||||||
'yaka_kodu', CASE WHEN $%d = 'yaka_kodu' THEN group_value ELSE yaka_kodu END,
|
'yaka_kodu', CASE WHEN $%d = 'yaka_kodu' THEN group_value ELSE '' END,
|
||||||
'item_description', item_description,
|
'item_description', CASE WHEN $%d = 'item_description' THEN group_value ELSE '' END,
|
||||||
'kategori', CASE WHEN $%d = 'kategori' THEN group_value ELSE kategori END,
|
'kategori', CASE WHEN $%d = 'kategori' THEN group_value ELSE '' END,
|
||||||
'askili_yan', CASE WHEN $%d = 'askili_yan' THEN group_value ELSE askili_yan END,
|
'askili_yan', CASE WHEN $%d = 'askili_yan' THEN group_value ELSE '' END,
|
||||||
'urun_ilk_grubu', CASE WHEN $%d = 'urun_ilk_grubu' THEN group_value ELSE urun_ilk_grubu END,
|
'urun_ilk_grubu', CASE WHEN $%d = 'urun_ilk_grubu' THEN group_value ELSE '' END,
|
||||||
'urun_ana_grubu', CASE WHEN $%d = 'urun_ana_grubu' THEN group_value ELSE urun_ana_grubu END,
|
'urun_ana_grubu', CASE WHEN $%d = 'urun_ana_grubu' THEN group_value ELSE '' END,
|
||||||
'urun_alt_grubu', CASE WHEN $%d = 'urun_alt_grubu' THEN group_value ELSE urun_alt_grubu END,
|
'urun_alt_grubu', CASE WHEN $%d = 'urun_alt_grubu' THEN group_value ELSE '' END,
|
||||||
'market_key', CASE WHEN $%d = 'market_key' THEN group_value ELSE market_key END
|
'market_key', CASE WHEN $%d = 'market_key' THEN group_value ELSE '' END
|
||||||
)
|
)
|
||||||
|| jsonb_build_object(
|
|| jsonb_build_object(
|
||||||
'stock_qty', stock_qty,
|
'stock_qty', stock_qty,
|
||||||
@@ -3377,7 +3377,7 @@ FROM (
|
|||||||
GROUP BY COALESCE(%s, '')
|
GROUP BY COALESCE(%s, '')
|
||||||
) g
|
) g
|
||||||
ORDER BY group_value
|
ORDER BY group_value
|
||||||
`, len(args)+1, len(args)+1, len(args)+1, len(args)+1, len(args)+1, len(args)+1, len(args)+1, len(args)+1, len(args)+1, groupExpr, groupExpr, whereSQL, groupExpr)
|
`, len(args)+1, len(args)+1, len(args)+1, len(args)+1, len(args)+1, len(args)+1, len(args)+1, len(args)+1, len(args)+1, len(args)+1, groupExpr, groupExpr, whereSQL, groupExpr)
|
||||||
args = append(args, field)
|
args = append(args, field)
|
||||||
return queryProductPerformanceJSONRows(ctx, pg, query, args...)
|
return queryProductPerformanceJSONRows(ctx, pg, query, args...)
|
||||||
}
|
}
|
||||||
@@ -3678,6 +3678,7 @@ func appendProductPerformanceGroupedRows(out *[]map[string]any, sourceRows []map
|
|||||||
|
|
||||||
func makeProductPerformanceGroupedRow(key string, level int, field, value string, rows []map[string]any) map[string]any {
|
func makeProductPerformanceGroupedRow(key string, level int, field, value string, rows []map[string]any) map[string]any {
|
||||||
row := aggregateProductPerformanceRows(rows, field)
|
row := aggregateProductPerformanceRows(rows, field)
|
||||||
|
clearProductPerformanceGroupDimensions(row, field, value)
|
||||||
row["__group"] = true
|
row["__group"] = true
|
||||||
row["row_key"] = "group|" + key
|
row["row_key"] = "group|" + key
|
||||||
row["key"] = key
|
row["key"] = key
|
||||||
@@ -3686,7 +3687,6 @@ func makeProductPerformanceGroupedRow(key string, level int, field, value string
|
|||||||
row["group_value"] = value
|
row["group_value"] = value
|
||||||
row["label"] = value
|
row["label"] = value
|
||||||
row["count"] = len(rows)
|
row["count"] = len(rows)
|
||||||
row[field] = value
|
|
||||||
row["recommendation"] = fmt.Sprintf("%d satır", len(rows))
|
row["recommendation"] = fmt.Sprintf("%d satır", len(rows))
|
||||||
|
|
||||||
image := firstProductPerformanceImageSource(rows)
|
image := firstProductPerformanceImageSource(rows)
|
||||||
@@ -3696,6 +3696,33 @@ func makeProductPerformanceGroupedRow(key string, level int, field, value string
|
|||||||
return row
|
return row
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var productPerformanceGroupDimensionFields = map[string]bool{
|
||||||
|
"product_code": true,
|
||||||
|
"color_yaka": true,
|
||||||
|
"color_code": true,
|
||||||
|
"yaka_kodu": true,
|
||||||
|
"item_description": true,
|
||||||
|
"kategori": true,
|
||||||
|
"askili_yan": true,
|
||||||
|
"urun_ilk_grubu": true,
|
||||||
|
"urun_ana_grubu": true,
|
||||||
|
"urun_alt_grubu": true,
|
||||||
|
"market_key": true,
|
||||||
|
"country": true,
|
||||||
|
"customer_segment": true,
|
||||||
|
"customer_code": true,
|
||||||
|
"customer_name": true,
|
||||||
|
}
|
||||||
|
|
||||||
|
func clearProductPerformanceGroupDimensions(row map[string]any, groupField, groupValue string) {
|
||||||
|
for field := range productPerformanceGroupDimensionFields {
|
||||||
|
row[field] = ""
|
||||||
|
}
|
||||||
|
if productPerformanceGroupDimensionFields[groupField] {
|
||||||
|
row[groupField] = groupValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func aggregateProductPerformanceRows(rows []map[string]any, groupField string) map[string]any {
|
func aggregateProductPerformanceRows(rows []map[string]any, groupField string) map[string]any {
|
||||||
out := map[string]any{}
|
out := map[string]any{}
|
||||||
for _, row := range rows {
|
for _, row := range rows {
|
||||||
|
|||||||
@@ -273,7 +273,7 @@ func GetProductPerformanceGroupedHandler(pg *sql.DB) http.HandlerFunc {
|
|||||||
groupLevels := splitCSVQuery(r.URL.Query().Get("group_levels"))
|
groupLevels := splitCSVQuery(r.URL.Query().Get("group_levels"))
|
||||||
limit := intQuery(r, "limit", 50000)
|
limit := intQuery(r, "limit", 50000)
|
||||||
expanded := map[string]bool{}
|
expanded := map[string]bool{}
|
||||||
const maxExpandedGroupKeys = 200
|
const maxExpandedGroupKeys = 260
|
||||||
addExpandedKey := func(key string) {
|
addExpandedKey := func(key string) {
|
||||||
key = strings.TrimSpace(key)
|
key = strings.TrimSpace(key)
|
||||||
if key != "" && len(expanded) < maxExpandedGroupKeys {
|
if key != "" && len(expanded) < maxExpandedGroupKeys {
|
||||||
|
|||||||
@@ -35,6 +35,7 @@
|
|||||||
class="period-selector bg-white q-px-xs q-py-none"
|
class="period-selector bg-white q-px-xs q-py-none"
|
||||||
/>
|
/>
|
||||||
<q-btn-dropdown
|
<q-btn-dropdown
|
||||||
|
v-model="detailLevelMenuOpen"
|
||||||
dense
|
dense
|
||||||
outline
|
outline
|
||||||
color="primary"
|
color="primary"
|
||||||
@@ -1560,6 +1561,7 @@ const selectedPeriods = ref(['90', '180', '360', 'total'])
|
|||||||
const selectedDetailMainGroup = ref('')
|
const selectedDetailMainGroup = ref('')
|
||||||
const imageUrlByKey = ref({})
|
const imageUrlByKey = ref({})
|
||||||
const imageListByKey = ref({})
|
const imageListByKey = ref({})
|
||||||
|
const imageFailedKeys = ref(new Set())
|
||||||
const imageDialog = ref(false)
|
const imageDialog = ref(false)
|
||||||
const imageDialogUrls = ref([])
|
const imageDialogUrls = ref([])
|
||||||
const imageDialogTitle = ref('')
|
const imageDialogTitle = ref('')
|
||||||
@@ -1579,6 +1581,7 @@ const imagePrimeLoading = ref(false)
|
|||||||
const filterBusy = ref(false)
|
const filterBusy = ref(false)
|
||||||
const bulkExpandLoading = ref(false)
|
const bulkExpandLoading = ref(false)
|
||||||
const generalRowsLoaded = ref(false)
|
const generalRowsLoaded = ref(false)
|
||||||
|
const detailLevelMenuOpen = ref(false)
|
||||||
let backendGroupedTimer = null
|
let backendGroupedTimer = null
|
||||||
let imagePrimeActiveRequests = 0
|
let imagePrimeActiveRequests = 0
|
||||||
|
|
||||||
@@ -2940,9 +2943,9 @@ function sortValueForColumnName (row, columnName) {
|
|||||||
function makeGroupRow (key, level, groupDef, value, groupRows) {
|
function makeGroupRow (key, level, groupDef, value, groupRows) {
|
||||||
const imageSource = groupRows.find(row => row?.product_code) || {}
|
const imageSource = groupRows.find(row => row?.product_code) || {}
|
||||||
const aggregate = aggregateGroupFields(groupRows, groupDef.key)
|
const aggregate = aggregateGroupFields(groupRows, groupDef.key)
|
||||||
const colorSummary = groupDef.key === 'color_code' ? value : distinctSummary(groupRows, 'color_code')
|
const colorSummary = groupDef.key === 'color_code' ? value : ''
|
||||||
const yakaSummary = groupDef.key === 'yaka_kodu' ? value : distinctSummary(groupRows, 'yaka_kodu')
|
const yakaSummary = groupDef.key === 'yaka_kodu' ? value : ''
|
||||||
const colorDescriptionSummary = distinctSummary(groupRows, 'color_description')
|
const colorDescriptionSummary = groupDef.key === 'color_code' ? distinctSummary(groupRows, 'color_description') : ''
|
||||||
return {
|
return {
|
||||||
__group: true,
|
__group: true,
|
||||||
row_key: `group|${key}`,
|
row_key: `group|${key}`,
|
||||||
@@ -2956,21 +2959,21 @@ function makeGroupRow (key, level, groupDef, value, groupRows) {
|
|||||||
image_color_code: imageSource.color_code || '',
|
image_color_code: imageSource.color_code || '',
|
||||||
image_color_description: imageSource.color_description || '',
|
image_color_description: imageSource.color_description || '',
|
||||||
image_yaka_kodu: imageSource.yaka_kodu || '',
|
image_yaka_kodu: imageSource.yaka_kodu || '',
|
||||||
product_code: groupDef.key === 'product_code' ? value : distinctSummary(groupRows, 'product_code'),
|
product_code: groupDef.key === 'product_code' ? value : '',
|
||||||
color_code: colorSummary,
|
color_code: colorSummary,
|
||||||
color_description: colorDescriptionSummary,
|
color_description: colorDescriptionSummary,
|
||||||
yaka_kodu: yakaSummary,
|
yaka_kodu: yakaSummary,
|
||||||
item_description: distinctSummary(groupRows, 'item_description'),
|
item_description: groupDef.key === 'item_description' ? value : '',
|
||||||
kategori: groupDef.key === 'kategori' ? value : distinctSummary(groupRows, 'kategori'),
|
kategori: groupDef.key === 'kategori' ? value : '',
|
||||||
urun_ilk_grubu: groupDef.key === 'urun_ilk_grubu' ? value : distinctSummary(groupRows, 'urun_ilk_grubu'),
|
urun_ilk_grubu: groupDef.key === 'urun_ilk_grubu' ? value : '',
|
||||||
askili_yan: groupDef.key === 'askili_yan' ? value : distinctSummary(groupRows, 'askili_yan'),
|
askili_yan: groupDef.key === 'askili_yan' ? value : '',
|
||||||
urun_ana_grubu: groupDef.key === 'urun_ana_grubu' ? value : distinctSummary(groupRows, 'urun_ana_grubu'),
|
urun_ana_grubu: groupDef.key === 'urun_ana_grubu' ? value : '',
|
||||||
urun_alt_grubu: groupDef.key === 'urun_alt_grubu' ? value : distinctSummary(groupRows, 'urun_alt_grubu'),
|
urun_alt_grubu: groupDef.key === 'urun_alt_grubu' ? value : '',
|
||||||
market_key: groupDef.key === 'market_key' ? value : distinctSummary(groupRows, 'market_key'),
|
market_key: groupDef.key === 'market_key' ? value : '',
|
||||||
country: groupDef.key === 'country' ? value : distinctSummary(groupRows, 'country'),
|
country: groupDef.key === 'country' ? value : '',
|
||||||
customer_segment: groupDef.key === 'customer_segment' ? value : distinctSummary(groupRows, 'customer_segment'),
|
customer_segment: groupDef.key === 'customer_segment' ? value : '',
|
||||||
customer_code: groupDef.key === 'customer_code' ? value : distinctSummary(groupRows, 'customer_code'),
|
customer_code: groupDef.key === 'customer_code' ? value : '',
|
||||||
customer_name: groupDef.key === 'customer_name' ? value : distinctSummary(groupRows, 'customer_name'),
|
customer_name: groupDef.key === 'customer_name' ? value : '',
|
||||||
stock_qty: sumRows(groupRows, 'stock_qty'),
|
stock_qty: sumRows(groupRows, 'stock_qty'),
|
||||||
sales_qty_90d: sumRows(groupRows, 'sales_qty_90d'),
|
sales_qty_90d: sumRows(groupRows, 'sales_qty_90d'),
|
||||||
sales_qty_180d: sumRows(groupRows, 'sales_qty_180d'),
|
sales_qty_180d: sumRows(groupRows, 'sales_qty_180d'),
|
||||||
@@ -3392,6 +3395,8 @@ function toggleGroup (key) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function collapseAllProductGroups () {
|
async function collapseAllProductGroups () {
|
||||||
|
if (bulkExpandLoading.value) return
|
||||||
|
detailLevelMenuOpen.value = false
|
||||||
const tabPrefix = `tab:${activeTab.value}|`
|
const tabPrefix = `tab:${activeTab.value}|`
|
||||||
expandedGroups.value = Object.fromEntries(
|
expandedGroups.value = Object.fromEntries(
|
||||||
Object.entries(expandedGroups.value).filter(([key]) => !key.startsWith(tabPrefix))
|
Object.entries(expandedGroups.value).filter(([key]) => !key.startsWith(tabPrefix))
|
||||||
@@ -3407,6 +3412,8 @@ async function collapseAllProductGroups () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function expandSelectedProductGroups () {
|
async function expandSelectedProductGroups () {
|
||||||
|
if (bulkExpandLoading.value) return
|
||||||
|
detailLevelMenuOpen.value = false
|
||||||
const keys = productAutoExpandKeys.value
|
const keys = productAutoExpandKeys.value
|
||||||
const maxKeys = Math.min(maxBulkExpandKeys, maxBackendExpandedKeysForTab(activeTab.value))
|
const maxKeys = Math.min(maxBulkExpandKeys, maxBackendExpandedKeysForTab(activeTab.value))
|
||||||
const expandableKeys = keys.slice(0, maxKeys)
|
const expandableKeys = keys.slice(0, maxKeys)
|
||||||
@@ -4055,6 +4062,7 @@ function resolveProductImageUrl (item) {
|
|||||||
async function fetchProductImagesForRow (row) {
|
async function fetchProductImagesForRow (row) {
|
||||||
if (!hasProductVariantImageKey(row)) return []
|
if (!hasProductVariantImageKey(row)) return []
|
||||||
const key = productImageKey(row)
|
const key = productImageKey(row)
|
||||||
|
if (imageFailedKeys.value.has(key)) return []
|
||||||
if (Object.prototype.hasOwnProperty.call(imageListByKey.value, key)) {
|
if (Object.prototype.hasOwnProperty.call(imageListByKey.value, key)) {
|
||||||
return imageListByKey.value[key]
|
return imageListByKey.value[key]
|
||||||
}
|
}
|
||||||
@@ -4083,6 +4091,11 @@ async function fetchProductImagesForRow (row) {
|
|||||||
const list = Array.isArray(resp?.data) ? resp.data : []
|
const list = Array.isArray(resp?.data) ? resp.data : []
|
||||||
const urls = list.map(resolveProductImageUrl).filter(Boolean)
|
const urls = list.map(resolveProductImageUrl).filter(Boolean)
|
||||||
performanceStore.setImageCache(key, urls)
|
performanceStore.setImageCache(key, urls)
|
||||||
|
if (urls.length && imageFailedKeys.value.has(key)) {
|
||||||
|
const nextFailed = new Set(imageFailedKeys.value)
|
||||||
|
nextFailed.delete(key)
|
||||||
|
imageFailedKeys.value = nextFailed
|
||||||
|
}
|
||||||
imageListByKey.value = { ...imageListByKey.value, [key]: urls }
|
imageListByKey.value = { ...imageListByKey.value, [key]: urls }
|
||||||
imageUrlByKey.value = { ...imageUrlByKey.value, [key]: urls[0] || '' }
|
imageUrlByKey.value = { ...imageUrlByKey.value, [key]: urls[0] || '' }
|
||||||
return urls
|
return urls
|
||||||
@@ -4097,12 +4110,14 @@ async function fetchProductImagesForRow (row) {
|
|||||||
function getCachedProductImageUrl (row) {
|
function getCachedProductImageUrl (row) {
|
||||||
if (!canShowProductImage(row)) return ''
|
if (!canShowProductImage(row)) return ''
|
||||||
const key = productImageKey(row)
|
const key = productImageKey(row)
|
||||||
|
if (imageFailedKeys.value.has(key)) return ''
|
||||||
return imageUrlByKey.value[key] || performanceStore.imageUrl(key) || ''
|
return imageUrlByKey.value[key] || performanceStore.imageUrl(key) || ''
|
||||||
}
|
}
|
||||||
|
|
||||||
function markProductImageFailed (row) {
|
function markProductImageFailed (row) {
|
||||||
const key = productImageKey(row)
|
const key = productImageKey(row)
|
||||||
if (!key) return
|
if (!key) return
|
||||||
|
imageFailedKeys.value = new Set([...imageFailedKeys.value, key])
|
||||||
performanceStore.setImageCache(key, [])
|
performanceStore.setImageCache(key, [])
|
||||||
imageListByKey.value = { ...imageListByKey.value, [key]: [] }
|
imageListByKey.value = { ...imageListByKey.value, [key]: [] }
|
||||||
imageUrlByKey.value = { ...imageUrlByKey.value, [key]: '' }
|
imageUrlByKey.value = { ...imageUrlByKey.value, [key]: '' }
|
||||||
@@ -4119,7 +4134,7 @@ async function primeProductImages (sourceRows) {
|
|||||||
if (!canShowProductImage(row)) continue
|
if (!canShowProductImage(row)) continue
|
||||||
const key = productImageKey(row)
|
const key = productImageKey(row)
|
||||||
const code = String(row?.image_product_code || row?.product_code || '').trim()
|
const code = String(row?.image_product_code || row?.product_code || '').trim()
|
||||||
if (!key || !code || seen.has(key) || Object.prototype.hasOwnProperty.call(imageListByKey.value, key) || performanceStore.hasImageKey(key)) continue
|
if (!key || !code || imageFailedKeys.value.has(key) || seen.has(key) || Object.prototype.hasOwnProperty.call(imageListByKey.value, key) || performanceStore.hasImageKey(key)) continue
|
||||||
seen.add(key)
|
seen.add(key)
|
||||||
items.push({
|
items.push({
|
||||||
key,
|
key,
|
||||||
@@ -4136,8 +4151,9 @@ async function primeProductImages (sourceRows) {
|
|||||||
const nextUrls = { ...imageUrlByKey.value }
|
const nextUrls = { ...imageUrlByKey.value }
|
||||||
for (const item of items) {
|
for (const item of items) {
|
||||||
const urls = batch?.lists?.[item.key] || performanceStore.imageList(item.key) || []
|
const urls = batch?.lists?.[item.key] || performanceStore.imageList(item.key) || []
|
||||||
|
if (!urls.length) imageFailedKeys.value = new Set([...imageFailedKeys.value, item.key])
|
||||||
nextLists[item.key] = urls
|
nextLists[item.key] = urls
|
||||||
nextUrls[item.key] = batch?.urls?.[item.key] || urls[0] || ''
|
nextUrls[item.key] = imageFailedKeys.value.has(item.key) ? '' : (batch?.urls?.[item.key] || urls[0] || '')
|
||||||
}
|
}
|
||||||
imageListByKey.value = nextLists
|
imageListByKey.value = nextLists
|
||||||
imageUrlByKey.value = nextUrls
|
imageUrlByKey.value = nextUrls
|
||||||
|
|||||||
Reference in New Issue
Block a user