Merge remote-tracking branch 'origin/master'

This commit is contained in:
M_Kececi
2026-05-20 16:18:48 +03:00
parent 8975e7127b
commit d094adf0b4
2 changed files with 113 additions and 111 deletions

View File

@@ -1228,6 +1228,15 @@ func PostProductionProductCostingOnMLSaveHandler(w http.ResponseWriter, r *http.
http.Error(w, "Gecersiz JSON", http.StatusBadRequest)
return
}
// Guardrail: do not allow blank stock codes for rows that specify a hammadde type.
// UI should already block this, but we enforce it server-side too.
for _, row := range req.Detail.Upserts {
if row.NHammaddeTuruNo > 0 && strings.TrimSpace(row.SKodu) == "" {
logger.Warn("validation failed: blank s_kodu", "n_hammadde_turu_no", row.NHammaddeTuruNo, "n_onml_det_no", row.NOnMLDetNo)
http.Error(w, "Kod bos olamaz (hammadde turu secili satir var)", http.StatusBadRequest)
return
}
}
req.DetailSource = strings.ToLower(strings.TrimSpace(req.DetailSource))
req.Header.UrunKodu = strings.TrimSpace(req.Header.UrunKodu)
@@ -1696,19 +1705,23 @@ WHEN NOT MATCHED THEN
// ============================================================
// Recipe sync (URETIM): ensure recipe contains all OnML hammadde rows
// so future no-cost loads don't keep showing them as missing.
// Table observed in queries: dbo.spUrtRecMBolumMik (nUrtMBolumID stores nHammaddeTuruNo).
// IMPORTANT: In current URETIM DB, the detail table is dbo.spUrtRecMBolum (NOT NULL cols, FK to dbo.spUrtMBolum).
// We must:
// 1) Skip hammadde types that do not exist in dbo.spUrtMBolum (FK safety),
// 2) Upsert by (nUrtReceteID, nUrtMBolumID, nHStokID_G=sKodu),
// 3) When inserting, generate nUrtRecMBolumID (smallint, not identity) and fill required columns incl. sIslemKodu=''.
// ============================================================
if req.Header.NUrtReceteID > 0 {
receteID := req.Header.NUrtReceteID
logger.Info("tx step", "trace_id", traceID, "n_onml_no", nOnMLNo, "step", "recipe_sync", "n_urt_recete_id", receteID)
// Determine next available recipe detail id (nUrtRecMBolumID)
// Determine next available recipe detail id (nUrtRecMBolumID) globally.
// NOTE: nUrtRecMBolumID is smallint and not identity in this schema.
nextRecDetID := 0
_ = tx.QueryRowContext(ctx, `
SELECT ISNULL(MAX(RMik.nUrtRecMBolumID), 0) + 1
FROM dbo.spUrtRecMBolumMik RMik WITH (UPDLOCK, HOLDLOCK)
WHERE RMik.nUrtReceteID = @p1
`, receteID).Scan(&nextRecDetID)
SELECT ISNULL(MAX(R.nUrtRecMBolumID), 0) + 1
FROM dbo.spUrtRecMBolum R WITH (UPDLOCK, HOLDLOCK)
`).Scan(&nextRecDetID)
if nextRecDetID <= 0 {
nextRecDetID = 1
}
@@ -1718,6 +1731,11 @@ WHERE RMik.nUrtReceteID = @p1
if hNo <= 0 {
continue
}
// Legacy mapping: merge deprecated hammadde types into canonical ones.
// We migrated 1104 -> 1105 historically; keep runtime mapping to avoid FK issues.
if hNo == 1104 {
hNo = 1105
}
// 1. FILTER: CM1/CM2 (Labor/Service) rows must NOT be written back into recipe tables.
// We check the group label (sAciklama3) from the row itself.
@@ -1727,132 +1745,93 @@ WHERE RMik.nUrtReceteID = @p1
continue
}
// In this version of URETIM DB, the table name is dbo.spUrtRecMBolumMik
// but it uses _G suffixes for quantity and amount columns.
// Also nHStokID_G stores the stock code string rather than just an ID.
rawSKodu := strings.TrimSpace(row.SKodu)
// Ensure a section entry (spUrtRecMBolum) exists for this hNo (Hammadde Turu)
// in the current recipe, otherwise detail rows (Mik) cannot be linked properly.
var sectionExists int
_ = tx.QueryRowContext(ctx, `
SELECT COUNT(1) FROM dbo.spUrtRecMBolum WITH (NOLOCK)
WHERE nUrtReceteID = @p1 AND nUrtMBolumID = @p2
`, receteID, hNo).Scan(&sectionExists)
if sectionExists <= 0 {
logger.Info("creating missing recipe section", "n_urt_recete_id", receteID, "n_urt_m_bolum_id", hNo)
_, _ = tx.ExecContext(ctx, `
INSERT INTO dbo.spUrtRecMBolum (nUrtReceteID, nUrtUBolumID, nUrtMBolumID, nUrtMTBolumID, sKullaniciAdi, dteIslemTarihi)
VALUES (@p1, 13, @p2, @p3, @p4, GETDATE())
`, receteID, hNo, row.NUrtMTBolumID, user)
}
// Update quantity and prices if row already exists for (recete, hammadde, stok_code)
// Using nHStokID_G (string code) for matching as per user screenshot.
var exists int
// FK safety: nUrtMBolumID must exist in dbo.spUrtMBolum.
var bolumExists int
if err := tx.QueryRowContext(ctx, `
SELECT COUNT(1)
FROM dbo.spUrtRecMBolumMik RMik WITH (NOLOCK)
WHERE RMik.nUrtReceteID = @p1
AND RMik.nUrtMBolumID = @p2
AND LTRIM(RTRIM(RMik.nHStokID_G)) = @p3
`, receteID, hNo, rawSKodu).Scan(&exists); err == nil && exists > 0 {
// Compute TRY unit price for recipe mirror columns.
cur := strings.ToUpper(strings.TrimSpace(row.FiyatDoviz))
in := row.FiyatGirilen
unitTRYRec := in
switch cur {
case "USD":
unitTRYRec = in * usdRate
case "EUR":
unitTRYRec = in * eurRate
case "GBP":
unitTRYRec = in * gbpRate
case "TRY", "TL", "":
unitTRYRec = in
default:
unitTRYRec = in
}
_, _ = tx.ExecContext(ctx, `
UPDATE dbo.spUrtRecMBolumMik
SET lHMiktar_G = @p4,
lHMaliyet_G = @p5,
sKullaniciAdiDeg = @p6,
dteIslemTarihiDeg = GETDATE(),
bIslem = @p7
WHERE nUrtReceteID = @p1
AND nUrtMBolumID = @p2
AND LTRIM(RTRIM(nHStokID_G)) = @p3
`, receteID, hNo, rawSKodu, row.LMiktar, unitTRYRec, user, row.MaliyeteDahil)
SELECT COUNT(1) FROM dbo.spUrtMBolum WITH (NOLOCK)
WHERE nUrtMBolumID = @p1
`, hNo).Scan(&bolumExists); err != nil || bolumExists <= 0 {
logger.Warn("recipe sync skip: missing spUrtMBolum", "n_urt_m_bolum_id", hNo, "s_kodu", strings.TrimSpace(row.SKodu))
continue
}
// Insert missing: using _G columns and storing code in nHStokID_G.
// Compute TRY unit price for recipe mirror columns.
cur := strings.ToUpper(strings.TrimSpace(row.FiyatDoviz))
in := row.FiyatGirilen
unitTRYRec := in
switch cur {
case "USD":
unitTRYRec = in * usdRate
case "EUR":
unitTRYRec = in * eurRate
case "GBP":
unitTRYRec = in * gbpRate
case "TRY", "TL", "":
unitTRYRec = in
default:
unitTRYRec = in
// Upsert target key: (receteID, hNo, sKodu).
rawSKodu := strings.TrimSpace(row.SKodu)
if rawSKodu == "" {
continue
}
// Update qty if exists.
var exists int
if err := tx.QueryRowContext(ctx, `
SELECT COUNT(1)
FROM dbo.spUrtRecMBolum R WITH (NOLOCK)
WHERE R.nUrtReceteID = @p1
AND R.nUrtMBolumID = @p2
AND LTRIM(RTRIM(R.nHStokID_G)) = @p3
`, receteID, hNo, rawSKodu).Scan(&exists); err == nil && exists > 0 {
_, _ = tx.ExecContext(ctx, `
UPDATE dbo.spUrtRecMBolum
SET lHMiktar_G = @p4
WHERE nUrtReceteID = @p1
AND nUrtMBolumID = @p2
AND LTRIM(RTRIM(nHStokID_G)) = @p3
`, receteID, hNo, rawSKodu, row.LMiktar)
continue
}
// Insert missing into dbo.spUrtRecMBolum.
// nUrtRecMBolumID is not identity; keep incrementing, but guard against smallint overflow.
if nextRecDetID > 32767 {
logger.Warn("recipe sync skip: nUrtRecMBolumID overflow risk", "next_id", nextRecDetID, "n_urt_recete_id", receteID)
continue
}
_, insertErr := tx.ExecContext(ctx, `
INSERT INTO dbo.spUrtRecMBolumMik (
INSERT INTO dbo.spUrtRecMBolum (
nUrtRecMBolumID,
nUrtReceteID,
nUrtUBolumID,
nUrtRecMBolumID,
nStokID,
nUrtMBolumID,
nUrtMTBolumID,
nStokTipiID,
nHStokID_G,
lHMiktar_G,
lHFire_G,
lHCarpan,
nMaliyetTipiID,
lHMaliyet_G,
lMiktar_G,
sIslemKodu,
nUrtMBolumID,
nUrtMTBolumID,
lHCarpan,
nMTalimat_G,
bIslem,
nSure,
sAciklama,
sKullaniciAdi,
dteIslemTarihi,
sIslemKodu,
lHMiktar_GHedef,
nMBolumSarfTipiNo
)
VALUES (
@p1,
13,
@p2,
0,
@p3, -- nHStokID_G (Code)
@p4, -- lHMiktar_G
0,
6,
@p5, -- lHMaliyet_G
1, -- lMiktar_G
'',
@p6,
@p7,
1,
@p8, -- bIslem
0,
NULL,
@p9,
GETDATE(),
1
@p1, -- nUrtRecMBolumID (smallint)
@p2, -- nUrtReceteID
@p3, -- nUrtUBolumID
@p4, -- nUrtMBolumID
0, -- nUrtMTBolumID (tinyint)
1, -- nStokTipiID
@p5, -- nHStokID_G (sKodu)
@p6, -- lHMiktar_G
0, -- lHFire_G
1, -- lHCarpan
6, -- nMaliyetTipiID
0, -- lHMaliyet_G
2, -- nMTalimat_G
0, -- bIslem
0, -- nSure
'', -- sIslemKodu (NOT NULL)
0, -- lHMiktar_GHedef
1 -- nMBolumSarfTipiNo
)
`, receteID, nextRecDetID, rawSKodu, row.LMiktar, unitTRYRec, hNo, row.NUrtMTBolumID, row.MaliyeteDahil, user)
`, nextRecDetID, receteID, 13, hNo, rawSKodu, row.LMiktar)
if insertErr == nil {
nextRecDetID += 1
} else {
logger.Warn("recipe sync insert error", "err", insertErr, "n_urt_recete_id", receteID, "n_urt_m_bolum_id", hNo, "s_kodu", rawSKodu)
}
}
}