Merge remote-tracking branch 'origin/master'

This commit is contained in:
M_Kececi
2026-06-19 00:49:47 +03:00
parent 7512e7fe7c
commit 55e36366c3
7 changed files with 413 additions and 90 deletions

View File

@@ -53,9 +53,7 @@ CREATE TABLE IF NOT EXISTS mk_order_price_list_first_group_mail (
urun_ilk_grubu TEXT NOT NULL,
mail_id UUID NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
PRIMARY KEY (urun_ilk_grubu, mail_id),
CONSTRAINT fk_order_price_list_first_group_mail_mail
FOREIGN KEY (mail_id) REFERENCES mk_mail(id) ON DELETE CASCADE
PRIMARY KEY (urun_ilk_grubu, mail_id)
)
`,
`CREATE INDEX IF NOT EXISTS ix_order_price_list_first_group_mail_group ON mk_order_price_list_first_group_mail (urun_ilk_grubu)`,
@@ -79,7 +77,7 @@ func GetCostingFirstGroupMailMappingLookupsHandler(pg *sql.DB) http.HandlerFunc
return
}
if err := ensureFirstGroupMailMappingTables(pg); err != nil {
http.Error(w, "mapping table bootstrap error", http.StatusInternalServerError)
http.Error(w, "mapping table bootstrap error: "+err.Error(), http.StatusInternalServerError)
return
}
@@ -156,7 +154,7 @@ func GetCostingFirstGroupMailMappingsHandler(pg *sql.DB) http.HandlerFunc {
return
}
if err := ensureFirstGroupMailMappingTables(pg); err != nil {
http.Error(w, "mapping table bootstrap error", http.StatusInternalServerError)
http.Error(w, "mapping table bootstrap error: "+err.Error(), http.StatusInternalServerError)
return
}
@@ -279,7 +277,7 @@ func GetPricingFirstGroupMailMappingsHandler(pg *sql.DB) http.HandlerFunc {
return
}
if err := ensureFirstGroupMailMappingTables(pg); err != nil {
http.Error(w, "mapping table bootstrap error", http.StatusInternalServerError)
http.Error(w, "mapping table bootstrap error: "+err.Error(), http.StatusInternalServerError)
return
}
@@ -531,7 +529,7 @@ func getFirstGroupMailMappingsByQuery(pg *sql.DB, mappingQuery string) http.Hand
return
}
if err := ensureFirstGroupMailMappingTables(pg); err != nil {
http.Error(w, "mapping table bootstrap error", http.StatusInternalServerError)
http.Error(w, "mapping table bootstrap error: "+err.Error(), http.StatusInternalServerError)
return
}

View File

@@ -619,16 +619,13 @@ DO UPDATE SET dim_id = EXCLUDED.dim_id, updated_at = EXCLUDED.updated_at
if c.Dim1 <= 0 {
continue
}
v2 := int64(0)
var v2any any = nil
if c.Dim3.Valid && c.Dim3.Int64 > 0 {
v2 = c.Dim3.Int64
v2any = v2
}
// If we managed to resolve an "ItemDim3Code" id too, store it in val3 and mark mmdim_id=3.
// Active key: val1=color, val3=itemdim3. val2 is size and is not part of price/campaign key.
v3 := int64(0)
if extraVal3 != nil {
if vv, ok := extraVal3[fmt.Sprintf("%d|%d", c.Dim1, v2)]; ok && vv > 0 {
if c.Dim3.Valid && c.Dim3.Int64 > 0 {
v3 = c.Dim3.Int64
} else if extraVal3 != nil {
if vv, ok := extraVal3[fmt.Sprintf("%d|0", c.Dim1)]; ok && vv > 0 {
v3 = vv
}
}
@@ -1071,18 +1068,38 @@ VALUES (
_ = upsertDimCombosCache(code, dims) // best-effort cache fill
}
// 2) Last resort: MSSQL stock tokens, then seed mmitem_dim. Do not use
// mk_mmitem_dim_combo as a write source; stale cache rows can create wrong keys.
if len(dims) == 0 {
d, err := loadDimsFromMssqlStock(code)
if err != nil {
logger.Error("save:pg:dims:mssql:error", "product_code", code, "err", err)
} else {
dims = d
_ = upsertDimCombosCache(code, dims)
// If PG doesn't have mmitem_dim rows for this product yet, try to seed them.
ensureMMItemDimRows(mmItemID, dims, nil)
// 2) Merge MSSQL variant master combos. PG may be partially populated; missing
// colors/dim3 combos still need to be seeded before sdprc/zbggcampaign writes.
if d, err := loadDimsFromMssqlStock(code); err != nil {
logger.Error("save:pg:dims:mssql:error", "product_code", code, "err", err)
} else if len(d) > 0 {
seenDims := make(map[string]struct{}, len(dims)+len(d))
merged := make([]dimCombo, 0, len(dims)+len(d))
dim3Value := func(v sql.NullInt64) int64 {
if v.Valid {
return v.Int64
}
return 0
}
for _, c := range dims {
k := fmt.Sprintf("%d|%d", c.Dim1, dim3Value(c.Dim3))
if _, ok := seenDims[k]; ok {
continue
}
seenDims[k] = struct{}{}
merged = append(merged, c)
}
for _, c := range d {
k := fmt.Sprintf("%d|%d", c.Dim1, dim3Value(c.Dim3))
if _, ok := seenDims[k]; ok {
continue
}
seenDims[k] = struct{}{}
merged = append(merged, c)
}
dims = merged
_ = upsertDimCombosCache(code, dims)
ensureMMItemDimRows(mmItemID, d, nil)
}
}

View File

@@ -9,6 +9,7 @@ import (
"fmt"
"log"
"net/http"
"sort"
"strconv"
"strings"
"time"
@@ -420,6 +421,18 @@ type wholesaleVariantRow struct {
CampaignLast string `json:"campaign_last_dttm"`
}
func buildNebimVariantDisplayCode(colorCode string, dim3Code string) string {
colorCode = strings.TrimSpace(colorCode)
dim3Code = strings.TrimSpace(dim3Code)
if colorCode != "" && dim3Code != "" {
return colorCode + "-" + dim3Code
}
if colorCode != "" {
return colorCode
}
return dim3Code
}
type wholesaleCampaignHistoryRow struct {
ID int64 `json:"id"`
CampaignID *int64 `json:"campaign_id"`
@@ -736,6 +749,7 @@ DO UPDATE SET dim_id = EXCLUDED.dim_id, updated_at = EXCLUDED.updated_at
ItemID int64
Dim1 int64
Dim3Key int64
HasMSSQL bool
}
// Build base variant keys from PG's authoritative table (mmitem_dim).
@@ -805,7 +819,8 @@ WHERE mmitem_id = ANY($1::bigint[])
rows.Close()
}
// Resolve dim ids -> tokens for a readable VariantCode.
// Resolve dim ids -> tokens for a fallback readable VariantCode.
// MSSQL/Nebim tokens override this below; PG ids are only storage keys.
idToToken := map[int64]string{}
if len(dimIDs) > 0 {
// uniq
@@ -900,23 +915,23 @@ ORDER BY dim_id, updated_at DESC;
key := fmt.Sprintf("%d|%d|%d", itemID, d1, d3k)
prev, ok := tmpMap[key]
if !ok {
// If PG does not have mmitem_dim rows for this item yet, seed it from MSSQL and include it.
if !hasMMItemDim[itemID] {
var v2 any = nil
if d3k > 0 {
v2 = d3k
}
v3 := int64(0)
if id, ok := resolveDimID("dimval1", dim3Code); ok {
v3 = id
}
mmdimID := int64(2)
var v3any any = nil
if v3 > 0 {
mmdimID = 3
v3any = v3
}
_, _ = pg.ExecContext(ctx, `
// Seed missing MSSQL combos even when the product already has some mmitem_dim rows.
// PG remains the storage key source, but MSSQL may reveal new/missing color or dim3 combos.
var v2 any = nil
if sizeID, ok := resolveDimID("dimval1", dim1Code); ok && sizeID > 0 {
v2 = sizeID
}
v3 := int64(0)
if id, ok := resolveDimID("dimval1", dim3Code); ok {
v3 = id
}
mmdimID := int64(2)
var v3any any = nil
if v3 > 0 {
mmdimID = 3
v3any = v3
}
_, _ = pg.ExecContext(ctx, `
INSERT INTO mmitem_dim (mmitem_id, mmdim_id, val1, val2, val3, is_active, qty)
SELECT $1, $2, $3, $4, $5, TRUE, 0
WHERE NOT EXISTS (
@@ -930,26 +945,25 @@ WHERE NOT EXISTS (
LIMIT 1
);
`, itemID, mmdimID, d1, v2, v3any)
hasMMItemDim[itemID] = true
hasMMItemDim[itemID] = true
code := strings.TrimSpace(itemToCode[itemID])
if code != "" {
tmpMap[key] = tmpRow{
ProductCode: code,
VariantCode: "",
StockQty: 0,
ItemID: itemID,
Dim1: d1,
Dim3Key: d3k,
}
// Keep dim token cache for VariantCode formatting.
dimIDs = append(dimIDs, d1)
if d3k > 0 {
dimIDs = append(dimIDs, d3k)
}
prev = tmpMap[key]
ok = true
code := strings.TrimSpace(itemToCode[itemID])
if code != "" {
tmpMap[key] = tmpRow{
ProductCode: code,
VariantCode: "",
StockQty: 0,
ItemID: itemID,
Dim1: d1,
Dim3Key: d3k,
}
// Keep dim token cache for VariantCode formatting.
dimIDs = append(dimIDs, d1)
if d3k > 0 {
dimIDs = append(dimIDs, d3k)
}
prev = tmpMap[key]
ok = true
}
if !ok {
continue
@@ -960,6 +974,8 @@ WHERE NOT EXISTS (
q = qty.Float64
}
prev.StockQty += q
prev.VariantCode = buildNebimVariantDisplayCode(colorCode, dim3Code)
prev.HasMSSQL = true
tmpMap[key] = prev
_ = colorCode // display-only
}
@@ -968,6 +984,15 @@ WHERE NOT EXISTS (
for _, v := range tmpMap {
tmp = append(tmp, v)
}
sort.SliceStable(tmp, func(i, j int) bool {
if tmp[i].ProductCode != tmp[j].ProductCode {
return tmp[i].ProductCode < tmp[j].ProductCode
}
if tmp[i].HasMSSQL != tmp[j].HasMSSQL {
return tmp[i].HasMSSQL
}
return strings.TrimSpace(tmp[i].VariantCode) < strings.TrimSpace(tmp[j].VariantCode)
})
// Bulk load campaign assignment for each (mmitem_id, dim1, dim3_key)
type keyRec struct {