Merge remote-tracking branch 'origin/master'
This commit is contained in:
35
svc/main.go
35
svc/main.go
@@ -796,6 +796,41 @@ func InitRoutes(pgDB *sql.DB, mssql *sql.DB, ml *mailer.GraphMailer) *mux.Router
|
||||
"order", "view",
|
||||
wrapV3(http.HandlerFunc(routes.PostProductionHasCostDetailBulkPricesHandler)),
|
||||
)
|
||||
bindV3(r, pgDB,
|
||||
"/api/pricing/production-product-costing/onml/save", "POST",
|
||||
"order", "view",
|
||||
wrapV3(http.HandlerFunc(routes.PostProductionProductCostingOnMLSaveHandler)),
|
||||
)
|
||||
bindV3(r, pgDB,
|
||||
"/api/pricing/production-product-costing/default-quantities", "GET",
|
||||
"order", "view",
|
||||
wrapV3(http.HandlerFunc(routes.GetProductionProductCostingDefaultQuantitiesHandler)),
|
||||
)
|
||||
bindV3(r, pgDB,
|
||||
"/api/pricing/production-product-costing/default-quantities/upsert", "POST",
|
||||
"order", "view",
|
||||
wrapV3(http.HandlerFunc(routes.PostProductionProductCostingDefaultQuantitiesUpsertHandler)),
|
||||
)
|
||||
bindV3(r, pgDB,
|
||||
"/api/pricing/production-product-costing/default-quantities/update-bulk", "POST",
|
||||
"order", "view",
|
||||
wrapV3(http.HandlerFunc(routes.PostProductionProductCostingDefaultQuantitiesBulkUpdateHandler)),
|
||||
)
|
||||
bindV3(r, pgDB,
|
||||
"/api/pricing/production-product-costing/default-quantities/calc-avg", "POST",
|
||||
"order", "view",
|
||||
wrapV3(http.HandlerFunc(routes.PostProductionProductCostingDefaultQuantitiesCalcAvgHandler)),
|
||||
)
|
||||
bindV3(r, pgDB,
|
||||
"/api/pricing/production-product-costing/default-quantities/lookup", "POST",
|
||||
"order", "view",
|
||||
wrapV3(http.HandlerFunc(routes.PostProductionProductCostingDefaultQuantitiesLookupHandler)),
|
||||
)
|
||||
bindV3(r, pgDB,
|
||||
"/api/pricing/production-product-costing/default-quantities/refresh", "POST",
|
||||
"order", "view",
|
||||
wrapV3(http.HandlerFunc(routes.PostProductionProductCostingDefaultQuantitiesRefreshHandler)),
|
||||
)
|
||||
bindV3(r, pgDB,
|
||||
"/api/pricing/production-product-costing/options/urun-ana-grup", "GET",
|
||||
"order", "view",
|
||||
|
||||
@@ -103,6 +103,8 @@ type ProductionHasCostDetailHeader struct {
|
||||
UrunAltGrubu string `json:"UrunAltGrubu"`
|
||||
UretimSekliID string `json:"UretimSekliID"`
|
||||
UretimSekli string `json:"UretimSekli"`
|
||||
FirmaKodu string `json:"FirmaKodu"`
|
||||
NFirmaID int `json:"nFirmaID"`
|
||||
DteKayitTarihi string `json:"dteKayitTarihi"`
|
||||
SKullaniciAdi string `json:"sKullaniciAdi"`
|
||||
LTutarTL float64 `json:"lTutarTL"`
|
||||
@@ -116,6 +118,100 @@ type ProductionHasCostDetailHeader struct {
|
||||
NUrtReceteID string `json:"nUrtReceteID"`
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Save (INSERT/UPDATE/DELETE/UPSERT) spUrtOnMLMas + spUrtOnMLMasDet
|
||||
// ============================================================
|
||||
|
||||
type ProductionProductCostingOnMLSaveHeader struct {
|
||||
NOnMLNo int `json:"n_onml_no"`
|
||||
UrunKodu string `json:"urun_kodu"`
|
||||
UrunAdi string `json:"urun_adi"`
|
||||
MaliyetTarihi string `json:"maliyet_tarihi"` // YYYY-MM-DD
|
||||
NUrtReceteID int `json:"n_urt_recete_id"`
|
||||
UretimSekliID int `json:"uretim_sekli_id"`
|
||||
SAciklama string `json:"s_aciklama"`
|
||||
FirmaKodu string `json:"firma_kodu"`
|
||||
NFirmaID int `json:"n_firma_id"`
|
||||
}
|
||||
|
||||
type ProductionProductCostingOnMLSaveDetailUpsertRow struct {
|
||||
NOnMLDetNo int `json:"n_onml_det_no"`
|
||||
NHammaddeTuruNo int `json:"n_hammadde_turu_no"`
|
||||
NUrtMTBolumID int `json:"n_urt_mt_bolum_id"`
|
||||
SKodu string `json:"s_kodu"`
|
||||
SAciklama string `json:"s_aciklama"`
|
||||
SRenk string `json:"s_renk"`
|
||||
SBeden string `json:"s_beden"`
|
||||
SAciklama2 string `json:"s_aciklama2"`
|
||||
SBirim string `json:"s_birim"`
|
||||
LMiktar float64 `json:"l_miktar"`
|
||||
FiyatGirilen float64 `json:"fiyat_girilen"`
|
||||
FiyatDoviz string `json:"fiyat_doviz"`
|
||||
MaliyeteDahil int `json:"maliyete_dahil"`
|
||||
CMPriceTypeID *int `json:"cm_price_type_id"`
|
||||
}
|
||||
|
||||
type ProductionProductCostingOnMLSaveDetailDeleteRow struct {
|
||||
NOnMLDetNo int `json:"n_onml_det_no"`
|
||||
}
|
||||
|
||||
type ProductionProductCostingOnMLSaveDetail struct {
|
||||
Upserts []ProductionProductCostingOnMLSaveDetailUpsertRow `json:"upserts"`
|
||||
Deletes []ProductionProductCostingOnMLSaveDetailDeleteRow `json:"deletes"`
|
||||
}
|
||||
|
||||
type ProductionProductCostingOnMLSaveRequest struct {
|
||||
DetailSource string `json:"detail_source"`
|
||||
Header ProductionProductCostingOnMLSaveHeader `json:"header"`
|
||||
Detail ProductionProductCostingOnMLSaveDetail `json:"detail"`
|
||||
}
|
||||
|
||||
type ProductionProductCostingOnMLSaveResponse struct {
|
||||
NOnMLNo int `json:"n_onml_no"`
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Default quantities (URETIM): mk_MaliyetParcaEslestirme_vmiktarlar
|
||||
// ============================================================
|
||||
|
||||
type ProductionProductCostingDefaultQtyRow struct {
|
||||
NHammaddeTuruNo int `json:"nHammaddeTuruNo"`
|
||||
SAciklama string `json:"sAciklama"`
|
||||
LDefaultMiktar float64 `json:"lDefaultMiktar"`
|
||||
DteCalcTarihi string `json:"dteCalcTarihi"`
|
||||
BAktif bool `json:"bAktif"`
|
||||
}
|
||||
|
||||
type ProductionProductCostingDefaultQtyUpdateRequest struct {
|
||||
NHammaddeTuruNo int `json:"nHammaddeTuruNo"`
|
||||
LDefaultMiktar float64 `json:"lDefaultMiktar"`
|
||||
BAktif *bool `json:"bAktif"`
|
||||
}
|
||||
|
||||
type ProductionProductCostingDefaultQtyBulkUpdateRequest struct {
|
||||
Items []ProductionProductCostingDefaultQtyUpdateRequest `json:"items"`
|
||||
}
|
||||
|
||||
type ProductionProductCostingDefaultQtyCalcRequest struct {
|
||||
NHammaddeTuruNo int `json:"nHammaddeTuruNo"`
|
||||
TopN int `json:"topN"`
|
||||
}
|
||||
|
||||
type ProductionProductCostingDefaultQtyCalcResponse struct {
|
||||
NHammaddeTuruNo int `json:"nHammaddeTuruNo"`
|
||||
LDefaultMiktar float64 `json:"lDefaultMiktar"`
|
||||
NSampleCount int `json:"nSampleCount"`
|
||||
}
|
||||
|
||||
type ProductionProductCostingDefaultQtyLookupRequest struct {
|
||||
NHammaddeTuruNos []int `json:"nHammaddeTuruNos"`
|
||||
}
|
||||
|
||||
type ProductionProductCostingDefaultQtyLookupItem struct {
|
||||
NHammaddeTuruNo int `json:"nHammaddeTuruNo"`
|
||||
LDefaultMiktar float64 `json:"lDefaultMiktar"`
|
||||
}
|
||||
|
||||
type ProductionHasCostDetailExchangeRates struct {
|
||||
RateDate string `json:"rateDate"`
|
||||
TRYRate float64 `json:"tryRate"`
|
||||
|
||||
@@ -7,9 +7,269 @@ import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
func GetNextOnMLNoFrom100k(ctx context.Context, uretimDB *sql.DB) (int, error) {
|
||||
// nOnMLNo is NOT identity. Generate next number safely.
|
||||
// Requirement: always continue from MAX, but never below 100001.
|
||||
sqlText := `
|
||||
SELECT
|
||||
ISNULL(MAX(M.nOnMLNo), 100000) + 1 AS NextNo
|
||||
FROM dbo.spUrtOnMLMas M WITH (UPDLOCK, HOLDLOCK)
|
||||
`
|
||||
var next int
|
||||
if err := uretimDB.QueryRowContext(ctx, sqlText).Scan(&next); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if next < 100001 {
|
||||
next = 100001
|
||||
}
|
||||
return next, nil
|
||||
}
|
||||
|
||||
func GetOnMLMamulTuruNoByAciklama(ctx context.Context, tx *sql.Tx, sAciklama string) (int, error) {
|
||||
sAciklama = strings.TrimSpace(sAciklama)
|
||||
if sAciklama == "" {
|
||||
return 1, nil
|
||||
}
|
||||
|
||||
var existing int
|
||||
err := tx.QueryRowContext(ctx, `
|
||||
SELECT TOP 1 ISNULL(nMamulTuruNo, 0)
|
||||
FROM dbo.spUrtOnMLMamulTuru WITH (UPDLOCK, HOLDLOCK)
|
||||
WHERE LTRIM(RTRIM(ISNULL(sAciklama,''))) = @p1
|
||||
ORDER BY nMamulTuruNo
|
||||
`, sAciklama).Scan(&existing)
|
||||
if err == sql.ErrNoRows {
|
||||
return 0, nil
|
||||
}
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return existing, nil
|
||||
}
|
||||
|
||||
func LookupFirmaIDByKodu(ctx context.Context, uretimDB *sql.DB, firmaKodu string) (int, error) {
|
||||
firmaKodu = strings.TrimSpace(firmaKodu)
|
||||
if firmaKodu == "" {
|
||||
return 0, nil
|
||||
}
|
||||
sqlText := `
|
||||
SELECT TOP 1 ISNULL(F.nFirmaID, 0) AS nFirmaID
|
||||
FROM dbo.tbFirma F WITH (NOLOCK)
|
||||
WHERE LTRIM(RTRIM(ISNULL(F.sKodu, ''))) = @p1
|
||||
ORDER BY F.nFirmaID
|
||||
`
|
||||
var id int
|
||||
if err := uretimDB.QueryRowContext(ctx, sqlText, firmaKodu).Scan(&id); err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return 0, nil
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
type OnMLHeaderUpsertArgs struct {
|
||||
NOnMLNo int
|
||||
UrunKodu string
|
||||
UrunAdi string
|
||||
Tarihi time.Time
|
||||
NMamulTuruNo int
|
||||
NUrtReceteID sql.NullInt64
|
||||
UretimSekliID sql.NullInt64
|
||||
SAciklama sql.NullString
|
||||
NFirmaID int
|
||||
SUser string
|
||||
LTutarTL float64
|
||||
LTutarUSD float64
|
||||
LTutarEURO float64
|
||||
SDovizCinsi string
|
||||
LTutarDoviz float64
|
||||
}
|
||||
|
||||
func UpsertOnMLHeader(tx *sql.Tx, ctx context.Context, args OnMLHeaderUpsertArgs) error {
|
||||
// Note: Many NOT NULL columns exist. We use dummy/system values as per business rules.
|
||||
sqlText := `
|
||||
IF EXISTS (SELECT 1 FROM dbo.spUrtOnMLMas WITH (UPDLOCK, HOLDLOCK) WHERE nOnMLNo = @p1)
|
||||
BEGIN
|
||||
UPDATE dbo.spUrtOnMLMas
|
||||
SET
|
||||
UrunKodu = @p2,
|
||||
UrunAdi = @p3,
|
||||
Tarihi = @p4,
|
||||
nDonemNo = 1,
|
||||
nMamulTuruNo = @p16,
|
||||
nBolgeNo = 2,
|
||||
nZorlukNo = 1,
|
||||
bDurum = 1,
|
||||
lTutarTL = @p5,
|
||||
lTutarUSD = @p6,
|
||||
lTutarEURO = @p7,
|
||||
sDovizCinsi = @p8,
|
||||
lTutarDoviz = @p9,
|
||||
bSablon = 0,
|
||||
dteGuncellemeTarihi = GETDATE(),
|
||||
sGuncellemeKullaniciAdi = @p10,
|
||||
nUrtReceteID = @p11,
|
||||
sAciklama = @p12,
|
||||
lMasMiktar = 0,
|
||||
sRenk = NULLIF(LTRIM(RTRIM(@p13)), ''),
|
||||
nFirmaID = @p14,
|
||||
uretim_sekli_id = @p15
|
||||
WHERE nOnMLNo = @p1
|
||||
END
|
||||
ELSE
|
||||
BEGIN
|
||||
INSERT INTO dbo.spUrtOnMLMas (
|
||||
nOnMLNo,
|
||||
UrunKodu,
|
||||
UrunAdi,
|
||||
Tarihi,
|
||||
nDonemNo,
|
||||
nMamulTuruNo,
|
||||
nBolgeNo,
|
||||
nZorlukNo,
|
||||
dteKayitTarihi,
|
||||
sKullaniciAdi,
|
||||
bDurum,
|
||||
lTutarTL,
|
||||
lTutarUSD,
|
||||
lTutarEURO,
|
||||
sDovizCinsi,
|
||||
lTutarDoviz,
|
||||
bSablon,
|
||||
dteGuncellemeTarihi,
|
||||
sGuncellemeKullaniciAdi,
|
||||
nUrtReceteID,
|
||||
sAciklama,
|
||||
lMasMiktar,
|
||||
nFirmaID,
|
||||
bVarsayilan,
|
||||
bOnay,
|
||||
bIptal,
|
||||
bRParcaTakip,
|
||||
uretim_sekli_id
|
||||
)
|
||||
VALUES (
|
||||
@p1,
|
||||
@p2,
|
||||
@p3,
|
||||
@p4,
|
||||
1,
|
||||
@p16,
|
||||
2,
|
||||
1,
|
||||
GETDATE(),
|
||||
@p10,
|
||||
1,
|
||||
@p5,
|
||||
@p6,
|
||||
@p7,
|
||||
@p8,
|
||||
@p9,
|
||||
0,
|
||||
GETDATE(),
|
||||
@p10,
|
||||
@p11,
|
||||
@p12,
|
||||
0,
|
||||
@p14,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
@p15
|
||||
)
|
||||
END
|
||||
`
|
||||
_, err := tx.ExecContext(
|
||||
ctx,
|
||||
sqlText,
|
||||
args.NOnMLNo,
|
||||
strings.TrimSpace(args.UrunKodu),
|
||||
strings.TrimSpace(args.UrunAdi),
|
||||
args.Tarihi,
|
||||
args.LTutarTL,
|
||||
args.LTutarUSD,
|
||||
args.LTutarEURO,
|
||||
strings.TrimSpace(args.SDovizCinsi),
|
||||
args.LTutarDoviz,
|
||||
strings.TrimSpace(args.SUser),
|
||||
args.NUrtReceteID,
|
||||
args.SAciklama,
|
||||
"", // sRenk (header) currently not driven from UI
|
||||
args.NFirmaID,
|
||||
args.UretimSekliID,
|
||||
args.NMamulTuruNo,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// V3 (Nebim) base price update
|
||||
// ============================================================
|
||||
|
||||
func UpsertV3ItemBasePriceUSD(
|
||||
ctx context.Context,
|
||||
mssqlDB *sql.DB,
|
||||
itemCode string,
|
||||
priceDate string, // YYYY-MM-DD
|
||||
priceUSD float64,
|
||||
user string,
|
||||
) error {
|
||||
itemCode = strings.TrimSpace(itemCode)
|
||||
priceDate = strings.TrimSpace(priceDate)
|
||||
user = strings.TrimSpace(user)
|
||||
if mssqlDB == nil || itemCode == "" || priceDate == "" {
|
||||
return fmt.Errorf("missing params for base price upsert")
|
||||
}
|
||||
|
||||
// NOTE: In this DB, PRIMARY KEY is on:
|
||||
// (ItemTypeCode, ItemCode, CountryCode, SeasonCode, BasePriceCode)
|
||||
// so we cannot insert multiple rows for different dates under the same base price.
|
||||
// We update the single row's PriceDate/Price to reflect latest costing.
|
||||
sqlText := `
|
||||
MERGE dbo.prItemBasePrice AS T
|
||||
USING (
|
||||
SELECT
|
||||
@p1 AS ItemTypeCode,
|
||||
@p2 AS ItemCode,
|
||||
'TR' AS CountryCode,
|
||||
'' AS SeasonCode,
|
||||
1 AS BasePriceCode,
|
||||
CONVERT(date, @p3, 23) AS PriceDate,
|
||||
'USD' AS CurrencyCode
|
||||
) AS S
|
||||
ON T.ItemTypeCode = S.ItemTypeCode
|
||||
AND LTRIM(RTRIM(T.ItemCode)) = LTRIM(RTRIM(S.ItemCode))
|
||||
AND ISNULL(T.CountryCode,'') = S.CountryCode
|
||||
AND ISNULL(T.SeasonCode,'') = S.SeasonCode
|
||||
AND ISNULL(T.BasePriceCode,0) = S.BasePriceCode
|
||||
WHEN MATCHED THEN
|
||||
UPDATE SET
|
||||
PriceDate = S.PriceDate,
|
||||
CurrencyCode = S.CurrencyCode,
|
||||
Price = @p4,
|
||||
LastUpdatedUserName = @p5,
|
||||
LastUpdatedDate = GETDATE()
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT (
|
||||
ItemTypeCode, ItemCode, CountryCode, SeasonCode, BasePriceCode,
|
||||
PriceDate, CurrencyCode, Price, CreatedUserName, CreatedDate, LastUpdatedUserName, LastUpdatedDate
|
||||
)
|
||||
VALUES (
|
||||
S.ItemTypeCode, S.ItemCode, S.CountryCode, S.SeasonCode, S.BasePriceCode,
|
||||
S.PriceDate, S.CurrencyCode, @p4, @p5, GETDATE(), @p5, GETDATE()
|
||||
);
|
||||
`
|
||||
|
||||
_, err := mssqlDB.ExecContext(ctx, sqlText, 1, itemCode, priceDate, priceUSD, user)
|
||||
return err
|
||||
}
|
||||
|
||||
func GetProductAnaAltGrupByUrunKodu(ctx context.Context, mssqlDB *sql.DB, urunKodu string) (urunAnaGrubu string, urunAltGrubu string, err error) {
|
||||
urunKodu = strings.TrimSpace(urunKodu)
|
||||
if mssqlDB == nil || urunKodu == "" {
|
||||
@@ -449,7 +709,7 @@ SELECT
|
||||
END AS UretimSekli,
|
||||
|
||||
RTRIM(CONVERT(VARCHAR(32), ISNULL(SonIsEmri.nUrtSiparisNo, 0))) AS nUrtSiparisNo,
|
||||
CONVERT(VARCHAR(10), SonIsEmri.dteIslemTarihi, 23) AS dteIslemTarihi,
|
||||
ISNULL(CONVERT(VARCHAR(10), SonIsEmri.dteIslemTarihi, 23), '') AS dteIslemTarihi,
|
||||
ISNULL(SonIsEmri.FirmaKodu, '') AS FirmaKodu,
|
||||
ISNULL(SonIsEmri.FirmaAdi, '') AS FirmaAdi,
|
||||
ISNULL(SonIsEmri.sVeren, '') AS SonIsEmriVeren,
|
||||
@@ -679,6 +939,8 @@ func GetProductionHasCostDetailHeaderByOnMLNo(
|
||||
SELECT TOP 1
|
||||
ISNULL(UF.UretimiYapanFirma, '') AS UretimiYapanFirma,
|
||||
ISNULL(UF.SonIsEmriVeren, '') AS SonIsEmriVeren,
|
||||
ISNULL(UF.FirmaKodu, '') AS FirmaKodu,
|
||||
ISNULL(UF.nFirmaID, 0) AS nFirmaID,
|
||||
RTRIM(CONVERT(VARCHAR(32), ISNULL(M.nOnMLNo, 0))) AS nOnMLNo,
|
||||
LTRIM(RTRIM(ISNULL(M.UrunKodu, ''))) AS UrunKodu,
|
||||
ISNULL(M.UrunAdi, '') AS UrunAdi,
|
||||
@@ -701,6 +963,8 @@ LEFT JOIN dbo.mk_uretim_sekli US
|
||||
OUTER APPLY (
|
||||
SELECT TOP 1
|
||||
ISNULL(F.sAciklama, '') AS UretimiYapanFirma,
|
||||
ISNULL(F.sKodu, '') AS FirmaKodu,
|
||||
ISNULL(F.nFirmaID, 0) AS nFirmaID,
|
||||
ISNULL(SM.sVeren, '') AS SonIsEmriVeren
|
||||
FROM dbo.spUrtSiparisDet SD
|
||||
INNER JOIN dbo.spUrtSiparis SM
|
||||
@@ -757,6 +1021,8 @@ WITH RecipeMatch AS (
|
||||
SELECT TOP 1
|
||||
ISNULL(SonIsEmri.FirmaAdi, '') AS UretimiYapanFirma,
|
||||
ISNULL(SonIsEmri.SonIsEmriVeren, '') AS SonIsEmriVeren,
|
||||
ISNULL(SonIsEmri.FirmaKodu, '') AS FirmaKodu,
|
||||
ISNULL(SonIsEmri.nFirmaID, 0) AS nFirmaID,
|
||||
'' AS nOnMLNo,
|
||||
RM.UrunKodu,
|
||||
RM.UrunAdi,
|
||||
@@ -777,6 +1043,8 @@ FROM RecipeMatch RM
|
||||
OUTER APPLY (
|
||||
SELECT TOP 1
|
||||
ISNULL(F.sAciklama, '') AS FirmaAdi,
|
||||
ISNULL(F.sKodu, '') AS FirmaKodu,
|
||||
ISNULL(F.nFirmaID, 0) AS nFirmaID,
|
||||
ISNULL(SM.sVeren, '') AS SonIsEmriVeren,
|
||||
CONVERT(VARCHAR(16), SD.dteIslemTarihi, 120) AS dteIslemTarihi
|
||||
FROM dbo.spUrtSiparisDet SD
|
||||
@@ -960,6 +1228,221 @@ ORDER BY
|
||||
return uretimDB.QueryContext(ctx, sqlText, search, limit, searchLike)
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Default quantities (URETIM): mk_MaliyetParcaEslestirme_vmiktarlar
|
||||
// ============================================================
|
||||
|
||||
func ListProductionProductCostingDefaultQtyRows(ctx context.Context, uretimDB *sql.DB, search string, limit int) (*sql.Rows, error) {
|
||||
search = strings.TrimSpace(search)
|
||||
if limit <= 0 {
|
||||
limit = 500
|
||||
}
|
||||
if limit > 5000 {
|
||||
limit = 5000
|
||||
}
|
||||
searchLike := "%" + search + "%"
|
||||
|
||||
sqlText := `
|
||||
SELECT TOP (@p3)
|
||||
ISNULL(V.nHammaddeTuruNo, 0) AS nHammaddeTuruNo,
|
||||
ISNULL(H.sAciklama, '') AS sAciklama,
|
||||
ISNULL(V.lDefaultMiktar, 0) AS lDefaultMiktar,
|
||||
CONVERT(VARCHAR(16), V.dteCalcTarihi, 120) AS dteCalcTarihi,
|
||||
CAST(CASE WHEN ISNULL(V.bAktif, 0) = 1 THEN 1 ELSE 0 END AS bit) AS bAktif
|
||||
FROM dbo.mk_MaliyetParcaEslestirme_vmiktarlar V WITH (NOLOCK)
|
||||
LEFT JOIN dbo.spUrtOnMLHammaddeTuru H WITH (NOLOCK)
|
||||
ON H.nHammaddeTuruNo = V.nHammaddeTuruNo
|
||||
WHERE
|
||||
(@p1 = '' OR CONVERT(VARCHAR(32), ISNULL(V.nHammaddeTuruNo, 0)) LIKE @p2)
|
||||
AND ISNULL(H.bAktif, 0) = 1
|
||||
ORDER BY
|
||||
V.nHammaddeTuruNo ASC
|
||||
`
|
||||
|
||||
return uretimDB.QueryContext(ctx, sqlText, search, searchLike, limit)
|
||||
}
|
||||
|
||||
func UpsertProductionProductCostingDefaultQtyRow(ctx context.Context, uretimDB *sql.DB, nHammaddeTuruNo int, lDefaultMiktar float64, bAktif *bool) error {
|
||||
// NOTE: Legacy helper kept for backward-compat but now behaves as UPDATE-only.
|
||||
activeVal := -1
|
||||
if bAktif != nil {
|
||||
if *bAktif {
|
||||
activeVal = 1
|
||||
} else {
|
||||
activeVal = 0
|
||||
}
|
||||
}
|
||||
|
||||
sqlText := `
|
||||
UPDATE dbo.mk_MaliyetParcaEslestirme_vmiktarlar
|
||||
SET
|
||||
lDefaultMiktar = @p2,
|
||||
dteCalcTarihi = GETDATE(),
|
||||
bAktif = CASE WHEN @p3 < 0 THEN ISNULL(bAktif, 1) ELSE @p3 END
|
||||
WHERE nHammaddeTuruNo = @p1;
|
||||
SELECT @@ROWCOUNT;
|
||||
`
|
||||
var affected int
|
||||
if err := uretimDB.QueryRowContext(ctx, sqlText, nHammaddeTuruNo, lDefaultMiktar, activeVal).Scan(&affected); err != nil {
|
||||
return err
|
||||
}
|
||||
if affected == 0 {
|
||||
return fmt.Errorf("default qty row not found: nHammaddeTuruNo=%d", nHammaddeTuruNo)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func RefreshProductionProductCostingDefaultQty(ctx context.Context, uretimDB *sql.DB, topN int) error {
|
||||
if topN <= 0 {
|
||||
topN = 10
|
||||
}
|
||||
if topN > 50 {
|
||||
topN = 50
|
||||
}
|
||||
sqlText := `
|
||||
;WITH ranked AS (
|
||||
SELECT
|
||||
D.nHammaddeTuruNo,
|
||||
D.lMiktar,
|
||||
ROW_NUMBER() OVER (
|
||||
PARTITION BY D.nHammaddeTuruNo
|
||||
ORDER BY
|
||||
ISNULL(M.Tarihi, '19000101') DESC,
|
||||
ISNULL(M.dteGuncellemeTarihi, '19000101') DESC,
|
||||
D.nOnMLNo DESC,
|
||||
D.dteIslemTarihi DESC,
|
||||
D.nOnMLDetNo DESC
|
||||
) AS rn
|
||||
FROM dbo.spUrtOnMLMasDet D WITH (NOLOCK)
|
||||
INNER JOIN dbo.spUrtOnMLMas M WITH (NOLOCK)
|
||||
ON M.nOnMLNo = D.nOnMLNo
|
||||
WHERE ISNULL(D.lMiktar, 0) > 0
|
||||
AND ISNULL(M.bIptal, 0) = 0
|
||||
),
|
||||
agg AS (
|
||||
SELECT
|
||||
nHammaddeTuruNo,
|
||||
CAST(AVG(CAST(lMiktar AS float)) AS numeric(14,4)) AS lDefaultMiktar
|
||||
FROM ranked
|
||||
WHERE rn <= @p1
|
||||
GROUP BY nHammaddeTuruNo
|
||||
)
|
||||
MERGE dbo.mk_MaliyetParcaEslestirme_vmiktarlar AS T
|
||||
USING agg AS S
|
||||
ON T.nHammaddeTuruNo = S.nHammaddeTuruNo
|
||||
WHEN MATCHED THEN
|
||||
UPDATE SET
|
||||
T.lDefaultMiktar = S.lDefaultMiktar,
|
||||
T.dteCalcTarihi = GETDATE(),
|
||||
T.bAktif = 1
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT (nHammaddeTuruNo, lDefaultMiktar, dteCalcTarihi, bAktif)
|
||||
VALUES (S.nHammaddeTuruNo, S.lDefaultMiktar, GETDATE(), 1);
|
||||
`
|
||||
_, err := uretimDB.ExecContext(ctx, sqlText, topN)
|
||||
return err
|
||||
}
|
||||
|
||||
func CalcProductionProductCostingDefaultQtyFromLastOnML(ctx context.Context, uretimDB *sql.DB, nHammaddeTuruNo int, topN int) (float64, int, error) {
|
||||
if nHammaddeTuruNo <= 0 {
|
||||
return 0, 0, fmt.Errorf("nHammaddeTuruNo required")
|
||||
}
|
||||
if topN <= 0 {
|
||||
topN = 10
|
||||
}
|
||||
if topN > 50 {
|
||||
topN = 50
|
||||
}
|
||||
sqlText := `
|
||||
;WITH ranked AS (
|
||||
SELECT TOP (@p2)
|
||||
ISNULL(D.lMiktar, 0) AS lMiktar
|
||||
FROM dbo.spUrtOnMLMasDet D WITH (NOLOCK)
|
||||
INNER JOIN dbo.spUrtOnMLMas M WITH (NOLOCK)
|
||||
ON M.nOnMLNo = D.nOnMLNo
|
||||
WHERE D.nHammaddeTuruNo = @p1
|
||||
AND ISNULL(D.lMiktar, 0) > 0
|
||||
ORDER BY ISNULL(M.Tarihi, M.dteKayitTarihi) DESC, D.nOnMLNo DESC, D.nOnMLDetNo DESC
|
||||
)
|
||||
SELECT
|
||||
CAST(ISNULL(AVG(CAST(lMiktar AS DECIMAL(18,4))), 0) AS FLOAT) AS avgQty,
|
||||
COUNT(1) AS sampleCount
|
||||
FROM ranked;
|
||||
`
|
||||
var avg float64
|
||||
var cnt int
|
||||
if err := uretimDB.QueryRowContext(ctx, sqlText, nHammaddeTuruNo, topN).Scan(&avg, &cnt); err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
return avg, cnt, nil
|
||||
}
|
||||
|
||||
func LookupProductionProductCostingDefaultQtyByNos(ctx context.Context, uretimDB *sql.DB, nos []int) (map[int]float64, error) {
|
||||
clean := make([]int, 0, len(nos))
|
||||
seen := make(map[int]struct{}, len(nos))
|
||||
for _, n := range nos {
|
||||
if n <= 0 {
|
||||
continue
|
||||
}
|
||||
if _, ok := seen[n]; ok {
|
||||
continue
|
||||
}
|
||||
seen[n] = struct{}{}
|
||||
clean = append(clean, n)
|
||||
}
|
||||
if len(clean) == 0 {
|
||||
return map[int]float64{}, nil
|
||||
}
|
||||
if len(clean) > 1000 {
|
||||
return nil, fmt.Errorf("too many hammadde nos")
|
||||
}
|
||||
|
||||
valueRows := make([]string, 0, len(clean))
|
||||
args := make([]any, 0, len(clean))
|
||||
for i, n := range clean {
|
||||
valueRows = append(valueRows, "(@p"+strconv.Itoa(i+1)+")")
|
||||
args = append(args, n)
|
||||
}
|
||||
|
||||
sqlText := `
|
||||
WITH req(nHammaddeTuruNo) AS (
|
||||
SELECT DISTINCT v.nHammaddeTuruNo
|
||||
FROM (VALUES ` + strings.Join(valueRows, ",") + `) v(nHammaddeTuruNo)
|
||||
)
|
||||
SELECT
|
||||
ISNULL(V.nHammaddeTuruNo, 0) AS nHammaddeTuruNo,
|
||||
CAST(ISNULL(V.lDefaultMiktar, 0) AS FLOAT) AS lDefaultMiktar
|
||||
FROM req R
|
||||
INNER JOIN dbo.mk_MaliyetParcaEslestirme_vmiktarlar V WITH (NOLOCK)
|
||||
ON V.nHammaddeTuruNo = R.nHammaddeTuruNo
|
||||
INNER JOIN dbo.spUrtOnMLHammaddeTuru H WITH (NOLOCK)
|
||||
ON H.nHammaddeTuruNo = V.nHammaddeTuruNo
|
||||
WHERE ISNULL(H.bAktif, 0) = 1;
|
||||
`
|
||||
|
||||
rows, err := uretimDB.QueryContext(ctx, sqlText, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
out := make(map[int]float64, len(clean))
|
||||
for rows.Next() {
|
||||
var no int
|
||||
var qty float64
|
||||
if err := rows.Scan(&no, &qty); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if no > 0 && qty > 0 {
|
||||
out[no] = qty
|
||||
}
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func buildSQLServerFullTextPrefixQuery(search string) string {
|
||||
terms := strings.Fields(strings.TrimSpace(search))
|
||||
parts := make([]string, 0, len(terms))
|
||||
|
||||
@@ -566,6 +566,8 @@ func GetProductionHasCostDetailHeaderHandler(w http.ResponseWriter, r *http.Requ
|
||||
if err := row.Scan(
|
||||
&item.UretimiYapanFirma,
|
||||
&item.SonIsEmriVeren,
|
||||
&item.FirmaKodu,
|
||||
&item.NFirmaID,
|
||||
&item.NOnMLNo,
|
||||
&item.UrunKodu,
|
||||
&item.UrunAdi,
|
||||
@@ -631,6 +633,8 @@ func GetProductionHasCostDetailHeaderHandler(w http.ResponseWriter, r *http.Requ
|
||||
if err := row.Scan(
|
||||
&item.UretimiYapanFirma,
|
||||
&item.SonIsEmriVeren,
|
||||
&item.FirmaKodu,
|
||||
&item.NFirmaID,
|
||||
&item.NOnMLNo,
|
||||
&item.UrunKodu,
|
||||
&item.UrunAdi,
|
||||
@@ -881,6 +885,827 @@ func GetProductionHasCostDetailEditorOptionsHandler(w http.ResponseWriter, r *ht
|
||||
http.Error(w, "kind hammadde, item veya color olmali", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
// GET /api/pricing/production-product-costing/default-quantities
|
||||
func GetProductionProductCostingDefaultQuantitiesHandler(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
|
||||
uretimDB := db.GetUretimDB()
|
||||
if uretimDB == nil {
|
||||
http.Error(w, "URETIM veritabani baglantisi aktif degil", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
|
||||
traceID := utils.TraceIDFromRequest(r)
|
||||
ctx := utils.ContextWithTraceID(r.Context(), traceID)
|
||||
search := strings.TrimSpace(r.URL.Query().Get("search"))
|
||||
limit := parsePositiveIntOrDefault(r.URL.Query().Get("limit"), 500)
|
||||
|
||||
// Always list only active hammadde turleri.
|
||||
rows, err := queries.ListProductionProductCostingDefaultQtyRows(ctx, uretimDB, search, limit)
|
||||
if err != nil {
|
||||
http.Error(w, "Veritabani hatasi", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
out := make([]models.ProductionProductCostingDefaultQtyRow, 0, 256)
|
||||
for rows.Next() {
|
||||
var item models.ProductionProductCostingDefaultQtyRow
|
||||
if err := rows.Scan(&item.NHammaddeTuruNo, &item.SAciklama, &item.LDefaultMiktar, &item.DteCalcTarihi, &item.BAktif); err != nil {
|
||||
http.Error(w, "Veritabani satir hatasi", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
out = append(out, item)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
http.Error(w, "Veritabani satir hatasi", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
_ = json.NewEncoder(w).Encode(out)
|
||||
}
|
||||
|
||||
// POST /api/pricing/production-product-costing/default-quantities/upsert
|
||||
func PostProductionProductCostingDefaultQuantitiesUpsertHandler(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
|
||||
uretimDB := db.GetUretimDB()
|
||||
if uretimDB == nil {
|
||||
http.Error(w, "URETIM veritabani baglantisi aktif degil", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
|
||||
traceID := utils.TraceIDFromRequest(r)
|
||||
ctx := utils.ContextWithTraceID(r.Context(), traceID)
|
||||
|
||||
var req models.ProductionProductCostingDefaultQtyUpdateRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "Gecersiz JSON", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if req.NHammaddeTuruNo <= 0 {
|
||||
http.Error(w, "nHammaddeTuruNo zorunlu", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if req.LDefaultMiktar <= 0 {
|
||||
http.Error(w, "lDefaultMiktar pozitif olmali", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if err := queries.UpsertProductionProductCostingDefaultQtyRow(ctx, uretimDB, req.NHammaddeTuruNo, req.LDefaultMiktar, req.BAktif); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{"ok": true})
|
||||
}
|
||||
|
||||
// POST /api/pricing/production-product-costing/default-quantities/update-bulk
|
||||
func PostProductionProductCostingDefaultQuantitiesBulkUpdateHandler(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
|
||||
uretimDB := db.GetUretimDB()
|
||||
if uretimDB == nil {
|
||||
http.Error(w, "URETIM veritabani baglantisi aktif degil", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
traceID := utils.TraceIDFromRequest(r)
|
||||
ctx := utils.ContextWithTraceID(r.Context(), traceID)
|
||||
|
||||
var req models.ProductionProductCostingDefaultQtyBulkUpdateRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "Gecersiz JSON", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if len(req.Items) == 0 {
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{"ok": true, "updated": 0})
|
||||
return
|
||||
}
|
||||
if len(req.Items) > 5000 {
|
||||
http.Error(w, "cok fazla satir", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
tx, err := uretimDB.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
http.Error(w, "Veritabani hatasi", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer func() { _ = tx.Rollback() }()
|
||||
|
||||
updated := 0
|
||||
for _, item := range req.Items {
|
||||
if item.NHammaddeTuruNo <= 0 {
|
||||
http.Error(w, "nHammaddeTuruNo zorunlu", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if item.LDefaultMiktar <= 0 {
|
||||
http.Error(w, "lDefaultMiktar pozitif olmali", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
activeVal := -1
|
||||
if item.BAktif != nil {
|
||||
if *item.BAktif {
|
||||
activeVal = 1
|
||||
} else {
|
||||
activeVal = 0
|
||||
}
|
||||
}
|
||||
sqlText := `
|
||||
UPDATE dbo.mk_MaliyetParcaEslestirme_vmiktarlar
|
||||
SET
|
||||
lDefaultMiktar = @p2,
|
||||
dteCalcTarihi = GETDATE(),
|
||||
bAktif = CASE WHEN @p3 < 0 THEN ISNULL(bAktif, 1) ELSE @p3 END
|
||||
WHERE nHammaddeTuruNo = @p1;
|
||||
SELECT @@ROWCOUNT;
|
||||
`
|
||||
var affected int
|
||||
if err := tx.QueryRowContext(ctx, sqlText, item.NHammaddeTuruNo, item.LDefaultMiktar, activeVal).Scan(&affected); err != nil {
|
||||
http.Error(w, "Veritabani hatasi", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if affected == 0 {
|
||||
http.Error(w, "satir bulunamadi: "+strconv.Itoa(item.NHammaddeTuruNo), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
updated++
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
http.Error(w, "Veritabani hatasi", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{"ok": true, "updated": updated})
|
||||
}
|
||||
|
||||
// POST /api/pricing/production-product-costing/default-quantities/calc-avg
|
||||
func PostProductionProductCostingDefaultQuantitiesCalcAvgHandler(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
|
||||
uretimDB := db.GetUretimDB()
|
||||
if uretimDB == nil {
|
||||
http.Error(w, "URETIM veritabani baglantisi aktif degil", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
traceID := utils.TraceIDFromRequest(r)
|
||||
ctx := utils.ContextWithTraceID(r.Context(), traceID)
|
||||
|
||||
var req models.ProductionProductCostingDefaultQtyCalcRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "Gecersiz JSON", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if req.NHammaddeTuruNo <= 0 {
|
||||
http.Error(w, "nHammaddeTuruNo zorunlu", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if req.TopN <= 0 {
|
||||
req.TopN = 10
|
||||
}
|
||||
|
||||
avg, cnt, err := queries.CalcProductionProductCostingDefaultQtyFromLastOnML(ctx, uretimDB, req.NHammaddeTuruNo, req.TopN)
|
||||
if err != nil {
|
||||
http.Error(w, "Veritabani hatasi", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
_ = json.NewEncoder(w).Encode(models.ProductionProductCostingDefaultQtyCalcResponse{
|
||||
NHammaddeTuruNo: req.NHammaddeTuruNo,
|
||||
LDefaultMiktar: avg,
|
||||
NSampleCount: cnt,
|
||||
})
|
||||
}
|
||||
|
||||
// POST /api/pricing/production-product-costing/default-quantities/lookup
|
||||
func PostProductionProductCostingDefaultQuantitiesLookupHandler(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
|
||||
uretimDB := db.GetUretimDB()
|
||||
if uretimDB == nil {
|
||||
http.Error(w, "URETIM veritabani baglantisi aktif degil", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
traceID := utils.TraceIDFromRequest(r)
|
||||
ctx := utils.ContextWithTraceID(r.Context(), traceID)
|
||||
|
||||
var req models.ProductionProductCostingDefaultQtyLookupRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "Gecersiz JSON", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
m, err := queries.LookupProductionProductCostingDefaultQtyByNos(ctx, uretimDB, req.NHammaddeTuruNos)
|
||||
if err != nil {
|
||||
http.Error(w, "Veritabani hatasi", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
out := make([]models.ProductionProductCostingDefaultQtyLookupItem, 0, len(m))
|
||||
for no, qty := range m {
|
||||
out = append(out, models.ProductionProductCostingDefaultQtyLookupItem{
|
||||
NHammaddeTuruNo: no,
|
||||
LDefaultMiktar: qty,
|
||||
})
|
||||
}
|
||||
_ = json.NewEncoder(w).Encode(out)
|
||||
}
|
||||
|
||||
// POST /api/pricing/production-product-costing/default-quantities/refresh
|
||||
func PostProductionProductCostingDefaultQuantitiesRefreshHandler(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
|
||||
uretimDB := db.GetUretimDB()
|
||||
if uretimDB == nil {
|
||||
http.Error(w, "URETIM veritabani baglantisi aktif degil", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
|
||||
traceID := utils.TraceIDFromRequest(r)
|
||||
ctx := utils.ContextWithTraceID(r.Context(), traceID)
|
||||
|
||||
topN := parsePositiveIntOrDefault(r.URL.Query().Get("top_n"), 10)
|
||||
if err := queries.RefreshProductionProductCostingDefaultQty(ctx, uretimDB, topN); err != nil {
|
||||
http.Error(w, "Veritabani hatasi", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{"ok": true, "top_n": topN})
|
||||
}
|
||||
|
||||
// POST /api/pricing/production-product-costing/onml/save
|
||||
func PostProductionProductCostingOnMLSaveHandler(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
|
||||
uretimDB := db.GetUretimDB()
|
||||
if uretimDB == nil {
|
||||
http.Error(w, "URETIM veritabani baglantisi aktif degil", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
mssqlDB := db.GetDB()
|
||||
|
||||
claims, _ := auth.GetClaimsFromContext(r.Context())
|
||||
user := ""
|
||||
if claims != nil {
|
||||
user = strings.TrimSpace(claims.Username)
|
||||
}
|
||||
|
||||
traceID := utils.TraceIDFromRequest(r)
|
||||
ctx := utils.ContextWithTraceID(r.Context(), traceID)
|
||||
logger := utils.SlogFromContext(ctx).With("handler", "production-product-costing.onml.save")
|
||||
|
||||
var req models.ProductionProductCostingOnMLSaveRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
logger.Warn("invalid json", "err", err)
|
||||
http.Error(w, "Gecersiz JSON", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
req.DetailSource = strings.ToLower(strings.TrimSpace(req.DetailSource))
|
||||
req.Header.UrunKodu = strings.TrimSpace(req.Header.UrunKodu)
|
||||
req.Header.UrunAdi = strings.TrimSpace(req.Header.UrunAdi)
|
||||
req.Header.MaliyetTarihi = strings.TrimSpace(req.Header.MaliyetTarihi)
|
||||
req.Header.SAciklama = strings.TrimSpace(req.Header.SAciklama)
|
||||
req.Header.FirmaKodu = strings.TrimSpace(req.Header.FirmaKodu)
|
||||
|
||||
if req.Header.UrunKodu == "" || req.Header.MaliyetTarihi == "" {
|
||||
http.Error(w, "urun_kodu ve maliyet_tarihi zorunlu", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Resolve exchange rates (for price conversions).
|
||||
usdRate := 0.0
|
||||
eurRate := 0.0
|
||||
gbpRate := 0.0
|
||||
if mssqlDB != nil {
|
||||
row, err := queries.GetProductionHasCostDetailExchangeRatesByDate(ctx, mssqlDB, req.Header.MaliyetTarihi)
|
||||
if err != nil {
|
||||
logger.Error("exchange rate query error", "err", err)
|
||||
http.Error(w, "Kur bilgisi alinamadi", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
var rateDate string
|
||||
if err := row.Scan(&rateDate, &usdRate, &eurRate, &gbpRate); err != nil {
|
||||
logger.Error("exchange rate scan error", "err", err)
|
||||
http.Error(w, "Kur bilgisi alinamadi", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
if usdRate <= 0 {
|
||||
usdRate = 1
|
||||
}
|
||||
if eurRate <= 0 {
|
||||
eurRate = 1
|
||||
}
|
||||
if gbpRate <= 0 {
|
||||
gbpRate = 1
|
||||
}
|
||||
|
||||
// Resolve firma id
|
||||
firmaID := req.Header.NFirmaID
|
||||
if firmaID <= 0 && req.Header.FirmaKodu != "" {
|
||||
id, err := queries.LookupFirmaIDByKodu(ctx, uretimDB, req.Header.FirmaKodu)
|
||||
if err != nil {
|
||||
logger.Error("firma lookup error", "err", err)
|
||||
http.Error(w, "Firma bulunamadi", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
firmaID = id
|
||||
}
|
||||
if firmaID <= 0 {
|
||||
http.Error(w, "Firma secilmeden kaydedilemez (n_firma_id / firma_kodu)", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Resolve or generate nOnMLNo
|
||||
nOnMLNo := req.Header.NOnMLNo
|
||||
if nOnMLNo <= 0 {
|
||||
next, err := queries.GetNextOnMLNoFrom100k(ctx, uretimDB)
|
||||
if err != nil {
|
||||
logger.Error("next onmlno error", "err", err)
|
||||
http.Error(w, "Yeni nOnMLNo uretilemedi", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
nOnMLNo = next
|
||||
}
|
||||
|
||||
// Resolve / create nMamulTuruNo from product groups (UrunAna + UrunAlt)
|
||||
nMamulTuruNo := 1
|
||||
|
||||
// Compute totals from incoming rows (TRY/USD/EUR)
|
||||
totalTRY := 0.0
|
||||
totalUSD := 0.0
|
||||
totalEUR := 0.0
|
||||
for _, r := range req.Detail.Upserts {
|
||||
qty := r.LMiktar
|
||||
if qty < 0 {
|
||||
qty = 0
|
||||
}
|
||||
// Convert input price to TRY (unit)
|
||||
cur := strings.ToUpper(strings.TrimSpace(r.FiyatDoviz))
|
||||
in := r.FiyatGirilen
|
||||
unitTRY := in
|
||||
switch cur {
|
||||
case "USD":
|
||||
unitTRY = in * usdRate
|
||||
case "EUR":
|
||||
unitTRY = in * eurRate
|
||||
case "GBP":
|
||||
unitTRY = in * gbpRate
|
||||
case "TRY", "TL", "":
|
||||
unitTRY = in
|
||||
default:
|
||||
unitTRY = in
|
||||
}
|
||||
unitUSD := unitTRY / usdRate
|
||||
unitEUR := unitTRY / eurRate
|
||||
totalTRY += unitTRY * qty
|
||||
totalUSD += unitUSD * qty
|
||||
totalEUR += unitEUR * qty
|
||||
}
|
||||
|
||||
// Parse Tarihi
|
||||
tarihi, err := time.Parse("2006-01-02", req.Header.MaliyetTarihi)
|
||||
if err != nil {
|
||||
http.Error(w, "maliyet_tarihi YYYY-MM-DD formatinda olmali", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
tx, err := uretimDB.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
logger.Error("tx begin error", "err", err)
|
||||
http.Error(w, "Islem baslatilamadi", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer func() { _ = tx.Rollback() }()
|
||||
|
||||
// Determine mamul turu inside same tx (to keep create atomic)
|
||||
mamulLabel := ""
|
||||
if mssqlDB != nil {
|
||||
_, ana, alt, err := queries.GetProductIlkAnaAltGrupByUrunKodu(ctx, mssqlDB, req.Header.UrunKodu)
|
||||
if err == nil {
|
||||
ana = strings.TrimSpace(ana)
|
||||
alt = strings.TrimSpace(alt)
|
||||
if alt == "" || alt == "-" {
|
||||
mamulLabel = ana
|
||||
} else {
|
||||
// matches inserted convention: "ANA-ALT" (no spaces)
|
||||
mamulLabel = strings.TrimSpace(ana + "-" + alt)
|
||||
}
|
||||
}
|
||||
}
|
||||
if strings.TrimSpace(mamulLabel) != "" && mamulLabel != "-" {
|
||||
mt, err := queries.GetOnMLMamulTuruNoByAciklama(ctx, tx, mamulLabel)
|
||||
if err != nil {
|
||||
logger.Error("mamul turu resolve error", "err", err)
|
||||
http.Error(w, "Mamul turu olusturulamadi", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if mt <= 0 {
|
||||
http.Error(w, "Mamul turu bulunamadi: "+mamulLabel, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
nMamulTuruNo = mt
|
||||
} else {
|
||||
nMamulTuruNo = 1
|
||||
}
|
||||
|
||||
var receteID sql.NullInt64
|
||||
if req.Header.NUrtReceteID > 0 {
|
||||
receteID = sql.NullInt64{Int64: int64(req.Header.NUrtReceteID), Valid: true}
|
||||
}
|
||||
var uretimSekliID sql.NullInt64
|
||||
if req.Header.UretimSekliID > 0 {
|
||||
uretimSekliID = sql.NullInt64{Int64: int64(req.Header.UretimSekliID), Valid: true}
|
||||
}
|
||||
var sAciklama sql.NullString
|
||||
if req.Header.SAciklama != "" {
|
||||
sAciklama = sql.NullString{String: req.Header.SAciklama, Valid: true}
|
||||
}
|
||||
|
||||
if err := queries.UpsertOnMLHeader(tx, ctx, queries.OnMLHeaderUpsertArgs{
|
||||
NOnMLNo: nOnMLNo,
|
||||
UrunKodu: req.Header.UrunKodu,
|
||||
UrunAdi: req.Header.UrunAdi,
|
||||
Tarihi: tarihi,
|
||||
NMamulTuruNo: nMamulTuruNo,
|
||||
NUrtReceteID: receteID,
|
||||
UretimSekliID: uretimSekliID,
|
||||
SAciklama: sAciklama,
|
||||
NFirmaID: firmaID,
|
||||
SUser: user,
|
||||
LTutarTL: totalTRY,
|
||||
LTutarUSD: totalUSD,
|
||||
LTutarEURO: totalEUR,
|
||||
SDovizCinsi: "USD",
|
||||
LTutarDoviz: totalUSD,
|
||||
}); err != nil {
|
||||
logger.Error("header upsert error", "err", err)
|
||||
http.Error(w, "Header kaydedilemedi", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// Deletes
|
||||
for _, d := range req.Detail.Deletes {
|
||||
if d.NOnMLDetNo <= 0 {
|
||||
continue
|
||||
}
|
||||
if _, err := tx.ExecContext(ctx, `DELETE FROM dbo.spUrtOnMLMasDet WHERE nOnMLNo=@p1 AND nOnMLDetNo=@p2`, nOnMLNo, d.NOnMLDetNo); err != nil {
|
||||
logger.Error("detail delete error", "err", err)
|
||||
http.Error(w, "Detay silinemedi", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Upserts
|
||||
for _, row := range req.Detail.Upserts {
|
||||
if row.NOnMLDetNo <= 0 {
|
||||
continue
|
||||
}
|
||||
if row.NHammaddeTuruNo <= 0 || strings.TrimSpace(row.SKodu) == "" {
|
||||
http.Error(w, "Detay satirinda n_hammadde_turu_no ve s_kodu zorunlu", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
qty := row.LMiktar
|
||||
if qty < 0 {
|
||||
qty = 0
|
||||
}
|
||||
cur := strings.ToUpper(strings.TrimSpace(row.FiyatDoviz))
|
||||
in := row.FiyatGirilen
|
||||
unitTRY := in
|
||||
switch cur {
|
||||
case "USD":
|
||||
unitTRY = in * usdRate
|
||||
case "EUR":
|
||||
unitTRY = in * eurRate
|
||||
case "GBP":
|
||||
unitTRY = in * gbpRate
|
||||
case "TRY", "TL", "":
|
||||
unitTRY = in
|
||||
default:
|
||||
unitTRY = in
|
||||
}
|
||||
unitUSD := unitTRY / usdRate
|
||||
|
||||
lTutar := unitTRY * qty
|
||||
lDovizTutari := unitUSD * qty
|
||||
|
||||
// Resolve stock type id from tbStok by sKodu (exact), then fallback to model-based match.
|
||||
// Note: In this DB, stock type is stored as tbStok.nStokTipi but spUrtOnMLMasDet expects nStokTipiID (int).
|
||||
rawSKodu := strings.TrimSpace(row.SKodu)
|
||||
var nStokTipiID int
|
||||
err := tx.QueryRowContext(ctx, `
|
||||
SELECT TOP 1 ISNULL(CONVERT(int, ISNULL(S.nStokTipi, 0)), 0) AS nStokTipiID
|
||||
FROM dbo.tbStok S WITH (NOLOCK)
|
||||
WHERE ISNULL(S.IsBlocked, 0) = 0
|
||||
AND (
|
||||
REPLACE(LTRIM(RTRIM(ISNULL(S.sKodu,''))), ' ', '') = REPLACE(@p1, ' ', '')
|
||||
OR LTRIM(RTRIM(ISNULL(S.sModel,''))) = @p1
|
||||
OR @p1 LIKE LTRIM(RTRIM(ISNULL(S.sModel,''))) + '%'
|
||||
)
|
||||
ORDER BY
|
||||
CASE
|
||||
WHEN REPLACE(LTRIM(RTRIM(ISNULL(S.sKodu,''))), ' ', '') = REPLACE(@p1, ' ', '') THEN 0
|
||||
WHEN LTRIM(RTRIM(ISNULL(S.sModel,''))) = @p1 THEN 1
|
||||
ELSE 2
|
||||
END,
|
||||
S.dteKayitTarihi DESC,
|
||||
S.nStokID DESC
|
||||
`, rawSKodu).Scan(&nStokTipiID)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
http.Error(w, "Stok tipi bulunamadi (s_kodu="+rawSKodu+")", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
logger.Error("stok tipi lookup error", "err", err)
|
||||
http.Error(w, "Stok tipi bulunamadi (tbStok sorgu hatasi)", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if nStokTipiID <= 0 {
|
||||
http.Error(w, "Stok tipi bulunamadi (s_kodu="+rawSKodu+")", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Dummy/system-mapped required fields:
|
||||
const bOtoFiyat = 0
|
||||
const lFireOrani = 0
|
||||
const nTutarGirisTipiNo = 1
|
||||
const sFiyatTipi = "ML"
|
||||
const nKurTipiNo = 6
|
||||
const lEkMasraf = 0
|
||||
const lMiktar2 = 0
|
||||
const lEkFiyat = 0
|
||||
|
||||
// MERGE by (nOnMLNo, nOnMLDetNo)
|
||||
mergeSQL := `
|
||||
MERGE dbo.spUrtOnMLMasDet AS T
|
||||
USING (SELECT @p1 AS nOnMLNo, @p2 AS nOnMLDetNo) AS S
|
||||
ON (T.nOnMLNo = S.nOnMLNo AND T.nOnMLDetNo = S.nOnMLDetNo)
|
||||
WHEN MATCHED THEN
|
||||
UPDATE SET
|
||||
nHammaddeTuruNo = @p3,
|
||||
sKodu = @p4,
|
||||
sAciklama = @p5,
|
||||
sRenk = @p6,
|
||||
sBeden = NULLIF(@p7,''),
|
||||
sAciklama2 = NULLIF(@p8,''),
|
||||
lMiktar = @p9,
|
||||
lFiyat = @p10,
|
||||
lTutar = @p11,
|
||||
bOtoFiyat = @p12,
|
||||
lFireOrani = @p13,
|
||||
nTutarGirisTipiNo = @p14,
|
||||
sFiyatTipi = @p15,
|
||||
sDovizCinsi = @p16,
|
||||
nKurTipiNo = @p17,
|
||||
lDovizKuru = @p18,
|
||||
lDovizFiyati = @p19,
|
||||
sBirim = @p20,
|
||||
lDovizTutari = @p21,
|
||||
lEkMasraf = @p22,
|
||||
lMiktar2 = @p23,
|
||||
nStokTipiID = @p24,
|
||||
lEkFiyat = @p25,
|
||||
nUrtMTBolumID = @p26,
|
||||
fiyat_girilen = NULLIF(@p27, 0),
|
||||
fiyat_doviz = NULLIF(@p28,''),
|
||||
Maliyete_dahil = @p29,
|
||||
cm_price_type_id = @p30,
|
||||
sKullaniciAdiDeg = @p31,
|
||||
dteIslemTarihiDeg = GETDATE()
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT (
|
||||
nOnMLNo,nOnMLDetNo,nHammaddeTuruNo,sKodu,sAciklama,sRenk,lMiktar,lFiyat,lTutar,
|
||||
bOtoFiyat,lFireOrani,nTutarGirisTipiNo,sFiyatTipi,sDovizCinsi,nKurTipiNo,lDovizKuru,lDovizFiyati,
|
||||
sBirim,sAciklama2,lDovizTutari,lEkMasraf,sKullaniciAdi,dteIslemTarihi,sBeden,sAciklama3,lMiktar2,nStokTipiID,
|
||||
lEkFiyat,nUrtMTBolumID,fiyat_girilen,fiyat_doviz,Maliyete_dahil,cm_price_type_id
|
||||
)
|
||||
VALUES (
|
||||
@p1,@p2,@p3,@p4,@p5,@p6,@p9,@p10,@p11,
|
||||
@p12,@p13,@p14,@p15,@p16,@p17,@p18,@p19,
|
||||
@p20,NULLIF(@p8,''),@p21,@p22,@p31,GETDATE(),NULLIF(@p7,''),NULL,@p23,@p24,
|
||||
@p25,@p26,NULLIF(@p27,0),NULLIF(@p28,''),@p29,@p30
|
||||
);
|
||||
`
|
||||
|
||||
if _, err := tx.ExecContext(
|
||||
ctx,
|
||||
mergeSQL,
|
||||
nOnMLNo,
|
||||
row.NOnMLDetNo,
|
||||
row.NHammaddeTuruNo,
|
||||
strings.TrimSpace(row.SKodu),
|
||||
strings.TrimSpace(row.SAciklama),
|
||||
strings.TrimSpace(row.SRenk),
|
||||
strings.TrimSpace(row.SBeden),
|
||||
strings.TrimSpace(row.SAciklama2),
|
||||
qty,
|
||||
unitTRY,
|
||||
lTutar,
|
||||
bOtoFiyat,
|
||||
lFireOrani,
|
||||
nTutarGirisTipiNo,
|
||||
sFiyatTipi,
|
||||
"USD",
|
||||
nKurTipiNo,
|
||||
usdRate,
|
||||
unitUSD,
|
||||
strings.TrimSpace(row.SBirim),
|
||||
lDovizTutari,
|
||||
lEkMasraf,
|
||||
lMiktar2,
|
||||
nStokTipiID,
|
||||
lEkFiyat,
|
||||
row.NUrtMTBolumID,
|
||||
row.FiyatGirilen,
|
||||
strings.TrimSpace(row.FiyatDoviz),
|
||||
row.MaliyeteDahil,
|
||||
row.CMPriceTypeID,
|
||||
user,
|
||||
); err != nil {
|
||||
logger.Error("detail merge error", "err", err)
|
||||
http.Error(w, "Detay kaydedilemedi", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 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).
|
||||
// ============================================================
|
||||
if req.Header.NUrtReceteID > 0 {
|
||||
receteID := req.Header.NUrtReceteID
|
||||
|
||||
// Determine next available recipe detail id (nUrtRecMBolumID)
|
||||
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)
|
||||
if nextRecDetID <= 0 {
|
||||
nextRecDetID = 1
|
||||
}
|
||||
|
||||
for _, row := range req.Detail.Upserts {
|
||||
hNo := row.NHammaddeTuruNo
|
||||
if hNo <= 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// CM1/CM2 rows are cost-only and must NOT be written back into recipe tables.
|
||||
// Source of truth: spUrtOnMLHammaddeTuru.sAciklama3 (e.g. 'CM2').
|
||||
var grp sql.NullString
|
||||
if err := tx.QueryRowContext(ctx, `
|
||||
SELECT TOP 1 LTRIM(RTRIM(ISNULL(H.sAciklama3, '')))
|
||||
FROM dbo.spUrtOnMLHammaddeTuru H WITH (NOLOCK)
|
||||
WHERE H.nHammaddeTuruNo = @p1
|
||||
`, hNo).Scan(&grp); err == nil {
|
||||
g := strings.ToUpper(strings.TrimSpace(grp.String))
|
||||
if g == "CM1" || g == "CM2" {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve nHStokID from tbStok using sKodu (exact), then sModel fallback.
|
||||
// IMPORTANT: nHStokID must be resolved; otherwise we'd update/insert too broadly.
|
||||
rawSKodu := strings.TrimSpace(row.SKodu)
|
||||
var nHStokID int
|
||||
err := tx.QueryRowContext(ctx, `
|
||||
SELECT TOP 1 ISNULL(S.nStokID, 0)
|
||||
FROM dbo.tbStok S WITH (NOLOCK)
|
||||
WHERE ISNULL(S.IsBlocked, 0) = 0
|
||||
AND (
|
||||
REPLACE(LTRIM(RTRIM(ISNULL(S.sKodu,''))), ' ', '') = REPLACE(@p1, ' ', '')
|
||||
OR LTRIM(RTRIM(ISNULL(S.sModel,''))) = @p1
|
||||
OR @p1 LIKE LTRIM(RTRIM(ISNULL(S.sModel,''))) + '%'
|
||||
)
|
||||
ORDER BY
|
||||
CASE
|
||||
WHEN REPLACE(LTRIM(RTRIM(ISNULL(S.sKodu,''))), ' ', '') = REPLACE(@p1, ' ', '') THEN 0
|
||||
WHEN LTRIM(RTRIM(ISNULL(S.sModel,''))) = @p1 THEN 1
|
||||
ELSE 2
|
||||
END,
|
||||
S.dteKayitTarihi DESC,
|
||||
S.nStokID DESC
|
||||
`, rawSKodu).Scan(&nHStokID)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
http.Error(w, "Recete sync icin stok bulunamadi (s_kodu="+rawSKodu+")", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
logger.Error("recipe stok lookup error", "err", err)
|
||||
http.Error(w, "Recete sync stok bulunamadi", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if nHStokID == 0 {
|
||||
http.Error(w, "Recete sync icin stok bulunamadi (s_kodu="+rawSKodu+")", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// If we cannot resolve stock, we still try to insert by hammadde no only.
|
||||
// Some recipes may omit nHStokID; but we prefer filling it when possible.
|
||||
|
||||
// Update quantity if row already exists for (recete, hammadde, stok)
|
||||
var exists int
|
||||
if err := tx.QueryRowContext(ctx, `
|
||||
SELECT COUNT(1)
|
||||
FROM dbo.spUrtRecMBolumMik RMik WITH (NOLOCK)
|
||||
WHERE RMik.nUrtReceteID = @p1
|
||||
AND RMik.nUrtMBolumID = @p2
|
||||
AND RMik.nHStokID = @p3
|
||||
`, receteID, hNo, nHStokID).Scan(&exists); err == nil && exists > 0 {
|
||||
_, _ = tx.ExecContext(ctx, `
|
||||
UPDATE dbo.spUrtRecMBolumMik
|
||||
SET lHMiktar = @p4,
|
||||
sKullaniciAdiDeg = @p5,
|
||||
dteIslemTarihiDeg = GETDATE()
|
||||
WHERE nUrtReceteID = @p1
|
||||
AND nUrtMBolumID = @p2
|
||||
AND nHStokID = @p3
|
||||
`, receteID, hNo, nHStokID, row.LMiktar, user)
|
||||
continue
|
||||
}
|
||||
|
||||
// Insert missing: best-effort with minimal columns.
|
||||
// NOTE: This assumes nUrtRecMBolumID can be a sequential int and that other columns are nullable/have defaults.
|
||||
_, insertErr := tx.ExecContext(ctx, `
|
||||
INSERT INTO dbo.spUrtRecMBolumMik (
|
||||
nUrtReceteID,
|
||||
nUrtUBolumID,
|
||||
nUrtRecMBolumID,
|
||||
nStokID,
|
||||
nHStokID,
|
||||
lHMiktar,
|
||||
lHFire,
|
||||
nMaliyetTipiID,
|
||||
lHMaliyet,
|
||||
lMiktar,
|
||||
sIslemKodu,
|
||||
nUrtMBolumID,
|
||||
nUrtMTBolumID,
|
||||
lHCarpan,
|
||||
bIslem,
|
||||
nSure,
|
||||
sAciklama,
|
||||
dteDovizTarihi,
|
||||
sDovizCinsi,
|
||||
lDovizOran,
|
||||
lDovizFiyat,
|
||||
sKullaniciAdi,
|
||||
dteIslemTarihi,
|
||||
nMBolumSarfTipiNo
|
||||
)
|
||||
VALUES (
|
||||
@p1,
|
||||
0,
|
||||
@p2,
|
||||
0,
|
||||
@p3,
|
||||
@p4,
|
||||
0,
|
||||
6,
|
||||
0,
|
||||
1,
|
||||
'',
|
||||
@p5,
|
||||
@p6,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
@p7,
|
||||
GETDATE(),
|
||||
1
|
||||
)
|
||||
`, receteID, nextRecDetID, nHStokID, row.LMiktar, hNo, row.NUrtMTBolumID, user)
|
||||
if insertErr == nil {
|
||||
nextRecDetID += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
logger.Error("tx commit error", "err", err)
|
||||
http.Error(w, "Kaydetme tamamlanamadi", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// V3: update base price table so pricing screens reflect latest costing.
|
||||
// Not transactional with URETIM DB; if this fails, URETIM save has already succeeded.
|
||||
if mssqlDB != nil {
|
||||
if err := queries.UpsertV3ItemBasePriceUSD(ctx, mssqlDB, req.Header.UrunKodu, req.Header.MaliyetTarihi, totalUSD, user); err != nil {
|
||||
logger.Error("v3 base price upsert error", "err", err)
|
||||
http.Error(w, "URETIM kaydedildi ama V3 maliyet guncellenemedi", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
_ = json.NewEncoder(w).Encode(models.ProductionProductCostingOnMLSaveResponse{NOnMLNo: nOnMLNo})
|
||||
}
|
||||
|
||||
// GET /api/pricing/production-product-costing/has-cost-detail-exchange-rates
|
||||
func GetProductionHasCostDetailExchangeRatesHandler(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
|
||||
Reference in New Issue
Block a user