Fix product performance grouped JSON build
This commit is contained in:
@@ -3465,11 +3465,13 @@ LIMIT $1
|
||||
}
|
||||
|
||||
type ProductPerformanceGroupedRequest struct {
|
||||
Mode string
|
||||
GroupLevels []string
|
||||
ExpandedKeys map[string]bool
|
||||
Limit int
|
||||
MainGroup string
|
||||
Mode string
|
||||
GroupLevels []string
|
||||
ExpandedKeys map[string]bool
|
||||
ExpandThroughLevel int
|
||||
Limit int
|
||||
MainGroup string
|
||||
Filters map[string][]string
|
||||
}
|
||||
|
||||
func ListProductPerformanceGrouped(ctx context.Context, pg *sql.DB, req ProductPerformanceGroupedRequest) ([]map[string]any, error) {
|
||||
@@ -3480,6 +3482,9 @@ func ListProductPerformanceGrouped(ctx context.Context, pg *sql.DB, req ProductP
|
||||
if len(levels) == 0 {
|
||||
levels = defaultProductPerformanceGroupLevels(req.Mode)
|
||||
}
|
||||
if req.ExpandThroughLevel > len(levels)-2 {
|
||||
req.ExpandThroughLevel = len(levels) - 2
|
||||
}
|
||||
|
||||
if out, ok, err := listProductPerformanceGroupedSQL(ctx, pg, req, levels); ok || err != nil {
|
||||
return out, err
|
||||
@@ -3489,14 +3494,16 @@ func ListProductPerformanceGrouped(ctx context.Context, pg *sql.DB, req ProductP
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sourceRows = filterProductPerformanceGroupedRows(sourceRows, req.Filters)
|
||||
out := make([]map[string]any, 0, len(sourceRows))
|
||||
appendProductPerformanceGroupedRows(&out, sourceRows, levels, 0, 0, []string{"tab:" + req.Mode}, req.ExpandedKeys)
|
||||
appendProductPerformanceGroupedRows(&out, sourceRows, levels, 0, 0, []string{"tab:" + req.Mode}, req.ExpandedKeys, req.ExpandThroughLevel)
|
||||
return out, nil
|
||||
}
|
||||
|
||||
type productPerformanceSQLGroupFilter struct {
|
||||
Field string
|
||||
Value string
|
||||
Field string
|
||||
Value string
|
||||
Values []string
|
||||
}
|
||||
|
||||
func listProductPerformanceGroupedSQL(ctx context.Context, pg *sql.DB, req ProductPerformanceGroupedRequest, levels []string) ([]map[string]any, bool, error) {
|
||||
@@ -3509,25 +3516,28 @@ func listProductPerformanceGroupedSQL(ctx context.Context, pg *sql.DB, req Produ
|
||||
}
|
||||
out := make([]map[string]any, 0, 512)
|
||||
filters := productPerformanceGroupedBaseFilters(req)
|
||||
err := appendProductPerformanceGroupedSQLRows(ctx, pg, &out, mode, levels, 0, 0, []string{"tab:" + mode}, filters, req.ExpandedKeys, req.Limit)
|
||||
err := appendProductPerformanceGroupedSQLRows(ctx, pg, &out, mode, levels, 0, 0, []string{"tab:" + mode}, filters, req.ExpandedKeys, req.ExpandThroughLevel, req.Limit)
|
||||
return out, true, err
|
||||
}
|
||||
|
||||
func productPerformanceGroupedBaseFilters(req ProductPerformanceGroupedRequest) []productPerformanceSQLGroupFilter {
|
||||
filters := make([]productPerformanceSQLGroupFilter, 0, len(req.Filters)+1)
|
||||
mainGroup := strings.TrimSpace(req.MainGroup)
|
||||
if strings.TrimSpace(req.Mode) != "product_detail" || mainGroup == "" {
|
||||
return nil
|
||||
if strings.TrimSpace(req.Mode) == "product_detail" && mainGroup != "" {
|
||||
filters = append(filters, productPerformanceSQLGroupFilter{Field: "urun_ana_grubu", Value: mainGroup})
|
||||
}
|
||||
return []productPerformanceSQLGroupFilter{{Field: "urun_ana_grubu", Value: mainGroup}}
|
||||
for field, values := range req.Filters {
|
||||
cleanValues := cleanProductPerformanceFilterValues(values)
|
||||
if len(cleanValues) == 0 {
|
||||
continue
|
||||
}
|
||||
filters = append(filters, productPerformanceSQLGroupFilter{Field: field, Values: cleanValues})
|
||||
}
|
||||
return filters
|
||||
}
|
||||
|
||||
func appendProductPerformanceGroupedSQLRows(ctx context.Context, pg *sql.DB, out *[]map[string]any, mode string, levels []string, level int, visualLevel int, parentKeys []string, filters []productPerformanceSQLGroupFilter, expandedKeys map[string]bool, limit int) error {
|
||||
func appendProductPerformanceGroupedSQLRows(ctx context.Context, pg *sql.DB, out *[]map[string]any, mode string, levels []string, level int, visualLevel int, parentKeys []string, filters []productPerformanceSQLGroupFilter, expandedKeys map[string]bool, expandThroughLevel int, limit int) error {
|
||||
if level >= len(levels) {
|
||||
rows, err := queryProductPerformanceSQLLeafRows(ctx, pg, mode, filters, limit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*out = append(*out, rows...)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -3540,7 +3550,7 @@ func appendProductPerformanceGroupedSQLRows(ctx context.Context, pg *sql.DB, out
|
||||
value := stringFromMap(row, "group_value")
|
||||
if shouldSkipProductPerformanceGroupValue(field, value) {
|
||||
nextFilters := append(append([]productPerformanceSQLGroupFilter{}, filters...), productPerformanceSQLGroupFilter{Field: field, Value: value})
|
||||
if err := appendProductPerformanceGroupedSQLRows(ctx, pg, out, mode, levels, level+1, visualLevel, parentKeys, nextFilters, expandedKeys, limit); err != nil {
|
||||
if err := appendProductPerformanceGroupedSQLRows(ctx, pg, out, mode, levels, level+1, visualLevel, parentKeys, nextFilters, expandedKeys, expandThroughLevel, limit); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
@@ -3556,9 +3566,9 @@ func appendProductPerformanceGroupedSQLRows(ctx context.Context, pg *sql.DB, out
|
||||
row["__group"] = true
|
||||
row[field] = value
|
||||
*out = append(*out, row)
|
||||
if expandedKeys[key] {
|
||||
if expandedKeys[key] || level <= expandThroughLevel {
|
||||
nextFilters := append(append([]productPerformanceSQLGroupFilter{}, filters...), productPerformanceSQLGroupFilter{Field: field, Value: value})
|
||||
if err := appendProductPerformanceGroupedSQLRows(ctx, pg, out, mode, levels, level+1, visualLevel+1, append(parentKeys, keyPart), nextFilters, expandedKeys, limit); err != nil {
|
||||
if err := appendProductPerformanceGroupedSQLRows(ctx, pg, out, mode, levels, level+1, visualLevel+1, append(parentKeys, keyPart), nextFilters, expandedKeys, expandThroughLevel, limit); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -3889,7 +3899,9 @@ func productPerformanceSQLGroupExpr(field string) (string, bool) {
|
||||
return "CASE WHEN btrim(COALESCE(urun_ilk_grubu,'')) = '-' THEN '' WHEN upper(translate(btrim(COALESCE(urun_ilk_grubu,'')), 'İŞĞÜÖÇ', 'ISGUOC')) IN ('YETISKIN', 'YETISKIN/GARSON', 'GARSON') THEN '' ELSE COALESCE(urun_ilk_grubu,'') END", true
|
||||
case "askili_yan":
|
||||
return "CASE WHEN btrim(COALESCE(askili_yan,'')) = '-' THEN '' ELSE COALESCE(askili_yan,'') END", true
|
||||
case "kategori", "urun_ana_grubu", "urun_alt_grubu", "product_code", "item_description", "color_code", "yaka_kodu":
|
||||
case "urun_ana_grubu":
|
||||
return "COALESCE(NULLIF(btrim(COALESCE(urun_ana_grubu,'')), ''), NULLIF(btrim(COALESCE(urun_alt_grubu,'')), ''), NULLIF(btrim(COALESCE(kategori,'')), ''), '')", true
|
||||
case "kategori", "urun_alt_grubu", "product_code", "item_description", "color_code", "yaka_kodu", "performance_bucket":
|
||||
return field, true
|
||||
case "color_yaka":
|
||||
return "concat_ws('/', NULLIF(btrim(COALESCE(color_code,'')), ''), NULLIF(btrim(COALESCE(yaka_kodu,'')), ''))", true
|
||||
@@ -3920,12 +3932,93 @@ func productPerformanceSQLFilterWhere(filters []productPerformanceSQLGroupFilter
|
||||
if !ok {
|
||||
return "", nil, fmt.Errorf("unsupported product performance filter field: %s", filter.Field)
|
||||
}
|
||||
args = append(args, normalizeProductPerformanceGroupValue(filter.Value))
|
||||
parts = append(parts, fmt.Sprintf("COALESCE(%s, '') = $%d", expr, len(args)))
|
||||
values := filter.Values
|
||||
if len(values) == 0 && strings.TrimSpace(filter.Value) != "" {
|
||||
values = []string{filter.Value}
|
||||
}
|
||||
values = cleanProductPerformanceFilterValues(values)
|
||||
if len(values) == 0 {
|
||||
continue
|
||||
}
|
||||
args = append(args, pq.Array(values))
|
||||
parts = append(parts, fmt.Sprintf("COALESCE(%s, '') = ANY($%d)", expr, len(args)))
|
||||
}
|
||||
if len(parts) == 0 {
|
||||
return "", nil, nil
|
||||
}
|
||||
return "WHERE " + strings.Join(parts, " AND "), args, nil
|
||||
}
|
||||
|
||||
func cleanProductPerformanceFilterValues(values []string) []string {
|
||||
out := make([]string, 0, len(values))
|
||||
seen := map[string]bool{}
|
||||
for _, value := range values {
|
||||
clean := normalizeProductPerformanceGroupValue(value)
|
||||
if clean == "" || seen[clean] {
|
||||
continue
|
||||
}
|
||||
seen[clean] = true
|
||||
out = append(out, clean)
|
||||
if len(out) >= 300 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func filterProductPerformanceGroupedRows(rows []map[string]any, filters map[string][]string) []map[string]any {
|
||||
if len(filters) == 0 || len(rows) == 0 {
|
||||
return rows
|
||||
}
|
||||
cleanFilters := map[string]map[string]bool{}
|
||||
for field, values := range filters {
|
||||
cleanValues := cleanProductPerformanceFilterValues(values)
|
||||
if len(cleanValues) == 0 {
|
||||
continue
|
||||
}
|
||||
set := map[string]bool{}
|
||||
for _, value := range cleanValues {
|
||||
set[value] = true
|
||||
}
|
||||
cleanFilters[field] = set
|
||||
}
|
||||
if len(cleanFilters) == 0 {
|
||||
return rows
|
||||
}
|
||||
out := make([]map[string]any, 0, len(rows))
|
||||
for _, row := range rows {
|
||||
matches := true
|
||||
for field, allowed := range cleanFilters {
|
||||
if !allowed[productPerformanceGroupedFilterValue(row, field)] {
|
||||
matches = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if matches {
|
||||
out = append(out, row)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func productPerformanceGroupedFilterValue(row map[string]any, field string) string {
|
||||
switch field {
|
||||
case "urun_ana_grubu":
|
||||
value := strings.TrimSpace(stringFromMap(row, "urun_ana_grubu"))
|
||||
if value != "" {
|
||||
return normalizeProductPerformanceGroupValue(value)
|
||||
}
|
||||
if value = strings.TrimSpace(stringFromMap(row, "urun_alt_grubu")); value != "" {
|
||||
return normalizeProductPerformanceGroupValue(value)
|
||||
}
|
||||
return normalizeProductPerformanceGroupValue(stringFromMap(row, "kategori"))
|
||||
case "market_key", "color_yaka", "urun_ilk_grubu", "askili_yan":
|
||||
return normalizeProductPerformanceGroupValue(mapGroupValue(row, field))
|
||||
default:
|
||||
return normalizeProductPerformanceGroupValue(stringFromMap(row, field))
|
||||
}
|
||||
}
|
||||
|
||||
func productPerformanceGroupedSourceRows(ctx context.Context, pg *sql.DB, mode string, limit int) ([]map[string]any, error) {
|
||||
switch mode {
|
||||
case "products":
|
||||
@@ -3986,9 +4079,8 @@ func productPerformanceIdleSourceRows(rows []map[string]any) []map[string]any {
|
||||
return out
|
||||
}
|
||||
|
||||
func appendProductPerformanceGroupedRows(out *[]map[string]any, sourceRows []map[string]any, levels []string, level int, visualLevel int, parentKeys []string, expandedKeys map[string]bool) {
|
||||
func appendProductPerformanceGroupedRows(out *[]map[string]any, sourceRows []map[string]any, levels []string, level int, visualLevel int, parentKeys []string, expandedKeys map[string]bool, expandThroughLevel int) {
|
||||
if level >= len(levels) {
|
||||
*out = append(*out, sourceRows...)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -4010,14 +4102,14 @@ func appendProductPerformanceGroupedRows(out *[]map[string]any, sourceRows []map
|
||||
for _, value := range values {
|
||||
groupRows := grouped[value]
|
||||
if shouldSkipProductPerformanceGroupValue(field, value) {
|
||||
appendProductPerformanceGroupedRows(out, groupRows, levels, level+1, visualLevel, parentKeys, expandedKeys)
|
||||
appendProductPerformanceGroupedRows(out, groupRows, levels, level+1, visualLevel, parentKeys, expandedKeys, expandThroughLevel)
|
||||
continue
|
||||
}
|
||||
keyPart := field + ":" + value
|
||||
key := strings.Join(append(parentKeys, keyPart), "|")
|
||||
*out = append(*out, makeProductPerformanceGroupedRow(key, visualLevel, field, value, groupRows))
|
||||
if expandedKeys[key] {
|
||||
appendProductPerformanceGroupedRows(out, groupRows, levels, level+1, visualLevel+1, append(parentKeys, keyPart), expandedKeys)
|
||||
if expandedKeys[key] || level <= expandThroughLevel {
|
||||
appendProductPerformanceGroupedRows(out, groupRows, levels, level+1, visualLevel+1, append(parentKeys, keyPart), expandedKeys, expandThroughLevel)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4076,6 +4168,9 @@ func aggregateProductPerformanceRows(rows []map[string]any, groupField string) m
|
||||
if key == "row_key" || key == "key" {
|
||||
continue
|
||||
}
|
||||
if isProductPerformanceMarginField(key) {
|
||||
continue
|
||||
}
|
||||
if key == "stock_qty" {
|
||||
out[key] = distinctProductPerformanceVariantStockQty(rows)
|
||||
continue
|
||||
@@ -4093,6 +4188,9 @@ func aggregateProductPerformanceRows(rows []map[string]any, groupField string) m
|
||||
}
|
||||
for _, row := range rows {
|
||||
for key := range row {
|
||||
if isProductPerformanceMarginField(key) {
|
||||
continue
|
||||
}
|
||||
if shouldAverageProductPerformanceField(key) {
|
||||
out[key] = weightedAverageProductPerformanceRows(rows, key, productPerformanceMetricWeightField(key))
|
||||
}
|
||||
@@ -4103,6 +4201,10 @@ func aggregateProductPerformanceRows(rows []map[string]any, groupField string) m
|
||||
return out
|
||||
}
|
||||
|
||||
func isProductPerformanceMarginField(field string) bool {
|
||||
return strings.HasPrefix(field, "gross_margin")
|
||||
}
|
||||
|
||||
func distinctProductPerformanceVariantStockQty(rows []map[string]any) float64 {
|
||||
seen := map[string]bool{}
|
||||
total := 0.0
|
||||
|
||||
Reference in New Issue
Block a user