Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -330,6 +330,7 @@ func GetProductionHasCostDetailGroupsHandler(w http.ResponseWriter, r *http.Requ
|
||||
nOnMLNoStr string
|
||||
nOnMLDetNoStr string
|
||||
hNoStr string
|
||||
mtBolumStr string
|
||||
fiyatGirilen sql.NullFloat64
|
||||
fiyatDoviz sql.NullString
|
||||
maliyeteDahil sql.NullBool
|
||||
@@ -344,6 +345,7 @@ func GetProductionHasCostDetailGroupsHandler(w http.ResponseWriter, r *http.Requ
|
||||
&nOnMLNoStr,
|
||||
&nOnMLDetNoStr,
|
||||
&hNoStr,
|
||||
&mtBolumStr,
|
||||
&item.SKodu,
|
||||
&item.SAciklama,
|
||||
&item.SRenk,
|
||||
@@ -378,6 +380,7 @@ func GetProductionHasCostDetailGroupsHandler(w http.ResponseWriter, r *http.Requ
|
||||
item.NOnMLNo = strings.TrimSpace(nOnMLNoStr)
|
||||
item.NOnMLDetNo = strings.TrimSpace(nOnMLDetNoStr)
|
||||
item.NHammaddeTuruNo = strings.TrimSpace(hNoStr)
|
||||
item.NUrtMTBolumID = strings.TrimSpace(mtBolumStr)
|
||||
|
||||
if fiyatGirilen.Valid {
|
||||
item.FiyatGirilen = new(float64)
|
||||
@@ -453,6 +456,7 @@ func GetProductionHasCostDetailGroupsHandler(w http.ResponseWriter, r *http.Requ
|
||||
nOnMLNoStr string
|
||||
nOnMLDetNoStr string
|
||||
hNoStr string
|
||||
mtBolumStr string
|
||||
fiyatGirilen sql.NullFloat64
|
||||
fiyatDoviz sql.NullString
|
||||
maliyeteDahil sql.NullBool
|
||||
@@ -467,6 +471,7 @@ func GetProductionHasCostDetailGroupsHandler(w http.ResponseWriter, r *http.Requ
|
||||
&nOnMLNoStr,
|
||||
&nOnMLDetNoStr,
|
||||
&hNoStr,
|
||||
&mtBolumStr,
|
||||
&item.SKodu,
|
||||
&item.SAciklama,
|
||||
&item.SRenk,
|
||||
@@ -501,6 +506,7 @@ func GetProductionHasCostDetailGroupsHandler(w http.ResponseWriter, r *http.Requ
|
||||
item.NOnMLNo = strings.TrimSpace(nOnMLNoStr)
|
||||
item.NOnMLDetNo = strings.TrimSpace(nOnMLDetNoStr)
|
||||
item.NHammaddeTuruNo = strings.TrimSpace(hNoStr)
|
||||
item.NUrtMTBolumID = strings.TrimSpace(mtBolumStr)
|
||||
|
||||
if fiyatGirilen.Valid {
|
||||
item.FiyatGirilen = new(float64)
|
||||
@@ -1497,6 +1503,14 @@ func PostProductionProductCostingOnMLSaveHandler(w http.ResponseWriter, r *http.
|
||||
logger.Info("tx step", "trace_id", traceID, "n_onml_no", nOnMLNo, "step", "detail_upserts", "count", len(req.Detail.Upserts))
|
||||
skippedUpserts := 0
|
||||
skippedUpsertsSample := 0
|
||||
// Cache hammadde_turu -> mt_bolum_id so we don't query master table for every row.
|
||||
mtBolumByHammadde := map[int]int{}
|
||||
// Collect source rows for recipe sync (variantless, non-CM2 only).
|
||||
type recipeKey struct {
|
||||
nUrtMBolumID int
|
||||
sKodu string
|
||||
}
|
||||
recipeQtyByKey := map[recipeKey]float64{}
|
||||
for _, row := range req.Detail.Upserts {
|
||||
if row.NOnMLDetNo <= 0 {
|
||||
skippedUpserts += 1
|
||||
@@ -1537,10 +1551,74 @@ func PostProductionProductCostingOnMLSaveHandler(w http.ResponseWriter, r *http.
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Guard: keep part/section binding stable.
|
||||
// UI sometimes doesn't send n_urt_mt_bolum_id; if we write 0 into spUrtOnMLMasDet,
|
||||
// joins to spUrtMTBolum will break and "parca adi" will render as "-".
|
||||
if row.NUrtMTBolumID <= 0 && row.NHammaddeTuruNo > 0 {
|
||||
if cached, ok := mtBolumByHammadde[row.NHammaddeTuruNo]; ok {
|
||||
if cached > 0 {
|
||||
row.NUrtMTBolumID = cached
|
||||
}
|
||||
} else {
|
||||
var mtID int
|
||||
err := tx.QueryRowContext(ctx, `
|
||||
SELECT TOP 1 ISNULL(MTnUrtMTBolumID, 0) AS MTnUrtMTBolumID
|
||||
FROM dbo.spUrtOnMLHammaddeTuru WITH (NOLOCK)
|
||||
WHERE nHammaddeTuruNo = @p1
|
||||
`, row.NHammaddeTuruNo).Scan(&mtID)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
logger.Warn("mt bolum lookup error (will keep incoming value)",
|
||||
"trace_id", traceID,
|
||||
"n_onml_no", nOnMLNo,
|
||||
"n_onml_det_no", row.NOnMLDetNo,
|
||||
"n_hammadde_turu_no", row.NHammaddeTuruNo,
|
||||
"err", err,
|
||||
)
|
||||
mtBolumByHammadde[row.NHammaddeTuruNo] = 0
|
||||
} else {
|
||||
mtBolumByHammadde[row.NHammaddeTuruNo] = mtID
|
||||
if mtID > 0 {
|
||||
row.NUrtMTBolumID = mtID
|
||||
logger.Info("mt bolum auto-filled from hammadde master",
|
||||
"trace_id", traceID,
|
||||
"n_onml_no", nOnMLNo,
|
||||
"n_onml_det_no", row.NOnMLDetNo,
|
||||
"n_hammadde_turu_no", row.NHammaddeTuruNo,
|
||||
"n_urt_mt_bolum_id", mtID,
|
||||
)
|
||||
} else {
|
||||
logger.Warn("mt bolum missing on hammadde master (keeping 0)",
|
||||
"trace_id", traceID,
|
||||
"n_onml_no", nOnMLNo,
|
||||
"n_onml_det_no", row.NOnMLDetNo,
|
||||
"n_hammadde_turu_no", row.NHammaddeTuruNo,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
qty := row.LMiktar
|
||||
if qty < 0 {
|
||||
qty = 0
|
||||
}
|
||||
|
||||
// Build recipe sync source data:
|
||||
// - never include CM2 / labor groups
|
||||
// - never include empty codes
|
||||
// - use variantless code (we already normalize sKodu on read; here we trust request)
|
||||
if req.Header.NUrtReceteID > 0 {
|
||||
group := strings.ToUpper(strings.TrimSpace(row.SAciklama3))
|
||||
code := strings.TrimSpace(row.SKodu)
|
||||
if code != "" && group != "CM2" && !strings.Contains(strings.ToUpper(code), " CM2") {
|
||||
if row.NHammaddeTuruNo > 0 {
|
||||
k := recipeKey{nUrtMBolumID: row.NHammaddeTuruNo, sKodu: code}
|
||||
recipeQtyByKey[k] += qty
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cur := strings.ToUpper(strings.TrimSpace(row.FiyatDoviz))
|
||||
in := row.FiyatGirilen
|
||||
unitTRY := in
|
||||
@@ -1745,8 +1823,150 @@ WHEN NOT MATCHED THEN
|
||||
logger.Warn("detail upserts skipped summary (det_no<=0)", "trace_id", traceID, "n_onml_no", nOnMLNo, "skipped", skippedUpserts)
|
||||
}
|
||||
|
||||
// NOTE: Recipe tables are intentionally NOT synced from OnML saves.
|
||||
// This costing screen is the source of truth only for dbo.spUrtOnMLMas / dbo.spUrtOnMLMasDet.
|
||||
// Recipe sync (spUrtRecMBolum): insert missing rows, update qty when changed.
|
||||
// IMPORTANT: We sync only variantless item codes (sModel-like) from OnML and never write CM2 items.
|
||||
if req.Header.NUrtReceteID > 0 && len(recipeQtyByKey) > 0 {
|
||||
logger.Info("tx step", "trace_id", traceID, "n_onml_no", nOnMLNo, "step", "recipe_sync", "n_urt_recete_id", req.Header.NUrtReceteID, "src_count", len(recipeQtyByKey))
|
||||
|
||||
// Determine default nUrtUBolumID from existing recipe rows; fallback to 13 (matches current data).
|
||||
nUrtUBolumID := 13
|
||||
_ = tx.QueryRowContext(ctx, `
|
||||
SELECT TOP 1 ISNULL(CONVERT(int, nUrtUBolumID), 0) AS nUrtUBolumID
|
||||
FROM dbo.spUrtRecMBolum WITH (NOLOCK)
|
||||
WHERE nUrtReceteID = @p1
|
||||
ORDER BY nUrtRecMBolumID ASC
|
||||
`, req.Header.NUrtReceteID).Scan(&nUrtUBolumID)
|
||||
if nUrtUBolumID <= 0 {
|
||||
nUrtUBolumID = 13
|
||||
}
|
||||
|
||||
// Load existing rows for quick compare.
|
||||
existingQty := map[recipeKey]float64{}
|
||||
if rows, err := tx.QueryContext(ctx, `
|
||||
SELECT
|
||||
ISNULL(CONVERT(int, nUrtMBolumID), 0) AS nUrtMBolumID,
|
||||
LTRIM(RTRIM(ISNULL(nHStokID_G,''))) AS nHStokID_G,
|
||||
ISNULL(CONVERT(float, lHMiktar_G), 0) AS lHMiktar_G
|
||||
FROM dbo.spUrtRecMBolum WITH (NOLOCK)
|
||||
WHERE nUrtReceteID = @p1
|
||||
`, req.Header.NUrtReceteID); err == nil {
|
||||
for rows.Next() {
|
||||
var bolumID int
|
||||
var code string
|
||||
var q float64
|
||||
if err := rows.Scan(&bolumID, &code, &q); err != nil {
|
||||
continue
|
||||
}
|
||||
code = strings.TrimSpace(code)
|
||||
if bolumID > 0 && code != "" {
|
||||
existingQty[recipeKey{nUrtMBolumID: bolumID, sKodu: code}] = q
|
||||
}
|
||||
}
|
||||
_ = rows.Close()
|
||||
}
|
||||
|
||||
// Update changed quantities.
|
||||
updated := 0
|
||||
for k, q := range recipeQtyByKey {
|
||||
old, ok := existingQty[k]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if old == q {
|
||||
continue
|
||||
}
|
||||
if _, err := tx.ExecContext(ctx, `
|
||||
UPDATE dbo.spUrtRecMBolum
|
||||
SET
|
||||
lHMiktar_G = @p4,
|
||||
sKullaniciAdiDeg = @p5,
|
||||
dteIslemTarihiDeg = GETDATE()
|
||||
WHERE nUrtReceteID = @p1
|
||||
AND nUrtMBolumID = @p2
|
||||
AND LTRIM(RTRIM(ISNULL(nHStokID_G,''))) = @p3
|
||||
`, req.Header.NUrtReceteID, k.nUrtMBolumID, k.sKodu, q, user); err != nil {
|
||||
logger.Warn("recipe qty update failed", "trace_id", traceID, "n_urt_recete_id", req.Header.NUrtReceteID, "n_urt_m_bolum_id", k.nUrtMBolumID, "s_kodu", k.sKodu, "err", err)
|
||||
continue
|
||||
}
|
||||
updated++
|
||||
}
|
||||
|
||||
// Insert missing rows.
|
||||
// We must generate nUrtRecMBolumID (smallint, non-identity) manually.
|
||||
var baseID int
|
||||
if err := tx.QueryRowContext(ctx, `
|
||||
SELECT ISNULL(MAX(CONVERT(int, nUrtRecMBolumID)), 0) AS MaxID
|
||||
FROM dbo.spUrtRecMBolum WITH (UPDLOCK, HOLDLOCK)
|
||||
`).Scan(&baseID); err != nil {
|
||||
logger.Warn("recipe base id lookup failed (skipping inserts)", "trace_id", traceID, "err", err)
|
||||
} else {
|
||||
inserted := 0
|
||||
nextID := baseID
|
||||
for k, q := range recipeQtyByKey {
|
||||
if _, ok := existingQty[k]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// FK guard: only insert if nUrtMBolumID exists in spUrtMBolum.
|
||||
var bolumExists int
|
||||
if err := tx.QueryRowContext(ctx, `
|
||||
SELECT CASE WHEN EXISTS (SELECT 1 FROM dbo.spUrtMBolum WITH (NOLOCK) WHERE nUrtMBolumID = @p1) THEN 1 ELSE 0 END
|
||||
`, k.nUrtMBolumID).Scan(&bolumExists); err != nil || bolumExists != 1 {
|
||||
logger.Warn("recipe insert skipped (missing spUrtMBolum FK)", "trace_id", traceID, "n_urt_m_bolum_id", k.nUrtMBolumID, "s_kodu", k.sKodu)
|
||||
continue
|
||||
}
|
||||
|
||||
nextID++
|
||||
if nextID > 32767 {
|
||||
logger.Warn("recipe insert stopped (nUrtRecMBolumID overflow)", "trace_id", traceID, "base_id", baseID, "next_id", nextID)
|
||||
break
|
||||
}
|
||||
|
||||
// NOTE: sIslemKodu is NOT NULL; keep empty string as default.
|
||||
// Keep lMiktar_G at 0 (NOT NULL) to avoid producing NULL rows.
|
||||
if _, err := tx.ExecContext(ctx, `
|
||||
INSERT INTO dbo.spUrtRecMBolum (
|
||||
nUrtRecMBolumID,
|
||||
nUrtReceteID,
|
||||
nUrtUBolumID,
|
||||
nUrtMBolumID,
|
||||
nUrtMTBolumID,
|
||||
nStokTipiID,
|
||||
nHStokID_G,
|
||||
lHMiktar_G,
|
||||
lHFire_G,
|
||||
lHCarpan,
|
||||
nMaliyetTipiID,
|
||||
lHMaliyet_G,
|
||||
nMTalimat_G,
|
||||
bIslem,
|
||||
lMiktar_G,
|
||||
nSure,
|
||||
sIslemKodu,
|
||||
lHMiktar_GHedef,
|
||||
nMBolumSarfTipiNo,
|
||||
sKullaniciAdi,
|
||||
dteIslemTarihi
|
||||
)
|
||||
VALUES (
|
||||
@p1,@p2,@p3,@p4,
|
||||
0,1,@p5,
|
||||
@p6,0,1,
|
||||
6,0,2,
|
||||
0,0,0,
|
||||
'',
|
||||
0,1,
|
||||
@p7,GETDATE()
|
||||
)
|
||||
`, nextID, req.Header.NUrtReceteID, nUrtUBolumID, k.nUrtMBolumID, k.sKodu, q, user); err != nil {
|
||||
logger.Warn("recipe insert failed", "trace_id", traceID, "n_urt_recete_id", req.Header.NUrtReceteID, "n_urt_m_bolum_id", k.nUrtMBolumID, "s_kodu", k.sKodu, "err", err)
|
||||
continue
|
||||
}
|
||||
inserted++
|
||||
}
|
||||
logger.Info("recipe sync done", "trace_id", traceID, "n_onml_no", nOnMLNo, "n_urt_recete_id", req.Header.NUrtReceteID, "updated", updated, "inserted", inserted)
|
||||
}
|
||||
}
|
||||
|
||||
logger.Info("tx step", "trace_id", traceID, "n_onml_no", nOnMLNo, "step", "commit")
|
||||
if err := tx.Commit(); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user