Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -13,9 +13,10 @@ import (
|
||||
"bssapp-backend/models"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"github.com/google/uuid"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
func nf0(v models.NullFloat64) float64 {
|
||||
@@ -25,6 +26,311 @@ func nf0(v models.NullFloat64) float64 {
|
||||
return v.Float64
|
||||
}
|
||||
|
||||
// =======================================================
|
||||
// ✅ trOrderLineCurrency UPSERT (Doc Price / Amount yaz)
|
||||
// =======================================================
|
||||
func upsertLineCurrency(
|
||||
tx *sql.Tx,
|
||||
lineID string,
|
||||
|
||||
docCurrency string,
|
||||
exRate float64,
|
||||
|
||||
docPrice float64,
|
||||
qty float64,
|
||||
|
||||
lDis float64, // Line Discount %
|
||||
tDis float64, // Total Discount %
|
||||
|
||||
vatRate float64,
|
||||
ln models.OrderDetail,
|
||||
|
||||
user string,
|
||||
) error {
|
||||
|
||||
// ================= NORMALIZE =================
|
||||
|
||||
if docCurrency == "" {
|
||||
docCurrency = "TRY"
|
||||
}
|
||||
|
||||
if exRate <= 0 {
|
||||
exRate = 1
|
||||
}
|
||||
|
||||
relationCurrency := safeNS(ln.RelationCurrencyCode)
|
||||
if relationCurrency == "" {
|
||||
relationCurrency = docCurrency
|
||||
}
|
||||
|
||||
if ln.LineDiscount.Valid {
|
||||
lDis = ln.LineDiscount.Float64
|
||||
}
|
||||
if ln.TotalDiscount.Valid {
|
||||
tDis = ln.TotalDiscount.Float64
|
||||
}
|
||||
|
||||
basePrice := docPrice // Price (KDV hariç)
|
||||
if docCurrency == "TRY" && ln.LocalPrice.Valid && ln.LocalPrice.Float64 > 0 {
|
||||
basePrice = ln.LocalPrice.Float64
|
||||
}
|
||||
|
||||
amountBase := basePrice * qty // Amount (KDV hariç)
|
||||
if docCurrency == "TRY" && ln.LocalAmount.Valid && ln.LocalAmount.Float64 > 0 {
|
||||
amountBase = ln.LocalAmount.Float64
|
||||
}
|
||||
|
||||
priceVI := basePrice // PriceVI (KDV dahil)
|
||||
if ln.DocPrice.Valid && ln.DocPrice.Float64 > 0 {
|
||||
priceVI = ln.DocPrice.Float64
|
||||
}
|
||||
|
||||
amountVI := priceVI * qty // AmountVI (KDV dahil)
|
||||
if ln.DocAmount.Valid && ln.DocAmount.Float64 > 0 {
|
||||
amountVI = ln.DocAmount.Float64
|
||||
if qty > 0 {
|
||||
priceVI = amountVI / qty
|
||||
}
|
||||
}
|
||||
|
||||
// ================= DISCOUNT =================
|
||||
|
||||
afterLineDis := amountBase * (1 - lDis/100)
|
||||
afterTotalDis := afterLineDis * (1 - tDis/100)
|
||||
|
||||
// ================= TAX =================
|
||||
|
||||
taxBase := afterTotalDis
|
||||
if ln.TaxBase.Valid {
|
||||
taxBase = ln.TaxBase.Float64
|
||||
}
|
||||
|
||||
vat := taxBase * vatRate / 100
|
||||
if ln.VatAmount.Valid {
|
||||
vat = ln.VatAmount.Float64
|
||||
}
|
||||
|
||||
vatDeducation := 0.0
|
||||
if ln.VatDeducation.Valid {
|
||||
vatDeducation = ln.VatDeducation.Float64
|
||||
}
|
||||
|
||||
net := taxBase + vat - vatDeducation
|
||||
if ln.NetAmount.Valid {
|
||||
net = ln.NetAmount.Float64
|
||||
}
|
||||
|
||||
pct := 0.0
|
||||
if ln.Pct.Valid {
|
||||
pct = ln.Pct.Float64
|
||||
}
|
||||
|
||||
// payload yoksa doc tutarı net ile hizala
|
||||
if !ln.DocAmount.Valid {
|
||||
amountVI = net
|
||||
if qty > 0 {
|
||||
priceVI = amountVI / qty
|
||||
}
|
||||
}
|
||||
|
||||
// ================= LOCAL =================
|
||||
|
||||
localPrice := basePrice * exRate
|
||||
localAmount := amountBase * exRate
|
||||
|
||||
localPriceVI := priceVI * exRate
|
||||
localAmountVI := amountVI * exRate
|
||||
|
||||
localTaxBase := taxBase * exRate
|
||||
localVat := vat * exRate
|
||||
localVatDeducation := vatDeducation * exRate
|
||||
localNet := net * exRate
|
||||
|
||||
// ================= DOC =================
|
||||
|
||||
_, err := tx.Exec(`
|
||||
MERGE BAGGI_V3.dbo.trOrderLineCurrency AS T
|
||||
USING (SELECT @p1 AS OrderLineID, @p2 AS CurrencyCode) AS S
|
||||
ON T.OrderLineID=S.OrderLineID AND T.CurrencyCode=S.CurrencyCode
|
||||
|
||||
WHEN MATCHED THEN UPDATE SET
|
||||
RelationCurrencyCode=@p3,
|
||||
|
||||
ExchangeRate=@p4,
|
||||
|
||||
PriceVI=@p5,
|
||||
AmountVI=@p6,
|
||||
|
||||
Price=@p7,
|
||||
Amount=@p8,
|
||||
|
||||
LDiscount1=@p9,
|
||||
TDiscount1=@p10,
|
||||
|
||||
TaxBase=@p11,
|
||||
Pct=@p12,
|
||||
Vat=@p13,
|
||||
VatDeducation=@p14,
|
||||
NetAmount=@p15,
|
||||
|
||||
LastUpdatedUserName=@p16,
|
||||
LastUpdatedDate=GETDATE()
|
||||
|
||||
WHEN NOT MATCHED THEN INSERT (
|
||||
OrderLineID,
|
||||
CurrencyCode,
|
||||
RelationCurrencyCode,
|
||||
|
||||
ExchangeRate,
|
||||
|
||||
PriceVI,
|
||||
AmountVI,
|
||||
|
||||
Price,
|
||||
Amount,
|
||||
|
||||
LDiscount1,
|
||||
TDiscount1,
|
||||
|
||||
TaxBase,
|
||||
Pct,
|
||||
Vat,
|
||||
VatDeducation,
|
||||
NetAmount,
|
||||
|
||||
CreatedUserName,
|
||||
CreatedDate
|
||||
)
|
||||
VALUES (
|
||||
@p1,@p2,@p3,
|
||||
@p4,
|
||||
@p5,@p6,
|
||||
@p7,@p8,
|
||||
@p9,@p10,
|
||||
@p11,@p12,@p13,@p14,@p15,
|
||||
@p16,GETDATE()
|
||||
);`,
|
||||
lineID,
|
||||
docCurrency,
|
||||
relationCurrency,
|
||||
|
||||
exRate,
|
||||
|
||||
priceVI,
|
||||
amountVI,
|
||||
|
||||
basePrice,
|
||||
amountBase,
|
||||
|
||||
lDis,
|
||||
tDis,
|
||||
|
||||
taxBase,
|
||||
pct,
|
||||
vat,
|
||||
vatDeducation,
|
||||
net,
|
||||
|
||||
user,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if docCurrency != "TRY" {
|
||||
_, err = tx.Exec(`
|
||||
MERGE BAGGI_V3.dbo.trOrderLineCurrency AS T
|
||||
USING (SELECT @p1 AS OrderLineID,'TRY' AS CurrencyCode) AS S
|
||||
ON T.OrderLineID=S.OrderLineID AND T.CurrencyCode=S.CurrencyCode
|
||||
|
||||
WHEN MATCHED THEN UPDATE SET
|
||||
RelationCurrencyCode=@p2,
|
||||
|
||||
ExchangeRate=@p3,
|
||||
|
||||
PriceVI=@p4,
|
||||
AmountVI=@p5,
|
||||
|
||||
Price=@p6,
|
||||
Amount=@p7,
|
||||
|
||||
LDiscount1=@p8,
|
||||
TDiscount1=@p9,
|
||||
|
||||
TaxBase=@p10,
|
||||
Pct=@p11,
|
||||
Vat=@p12,
|
||||
VatDeducation=@p13,
|
||||
NetAmount=@p14,
|
||||
|
||||
LastUpdatedUserName=@p15,
|
||||
LastUpdatedDate=GETDATE()
|
||||
|
||||
WHEN NOT MATCHED THEN INSERT (
|
||||
OrderLineID,
|
||||
CurrencyCode,
|
||||
RelationCurrencyCode,
|
||||
|
||||
ExchangeRate,
|
||||
|
||||
PriceVI,
|
||||
AmountVI,
|
||||
|
||||
Price,
|
||||
Amount,
|
||||
|
||||
LDiscount1,
|
||||
TDiscount1,
|
||||
|
||||
TaxBase,
|
||||
Pct,
|
||||
Vat,
|
||||
VatDeducation,
|
||||
NetAmount,
|
||||
|
||||
CreatedUserName,
|
||||
CreatedDate
|
||||
)
|
||||
VALUES (
|
||||
@p1,'TRY',@p2,
|
||||
@p3,
|
||||
@p4,@p5,
|
||||
@p6,@p7,
|
||||
@p8,@p9,
|
||||
@p10,@p11,@p12,@p13,@p14,
|
||||
@p15,GETDATE()
|
||||
);`,
|
||||
lineID,
|
||||
docCurrency,
|
||||
exRate,
|
||||
|
||||
localPriceVI,
|
||||
localAmountVI,
|
||||
|
||||
localPrice,
|
||||
localAmount,
|
||||
|
||||
lDis,
|
||||
tDis,
|
||||
|
||||
localTaxBase,
|
||||
pct,
|
||||
localVat,
|
||||
localVatDeducation,
|
||||
localNet,
|
||||
|
||||
user,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// =======================================================
|
||||
// COMBO KEY & STRING HELPERS
|
||||
// =======================================================
|
||||
@@ -52,6 +358,32 @@ func qtyValue(q models.NullFloat64) float64 {
|
||||
return q.Float64
|
||||
}
|
||||
|
||||
func buildV3AuditUser(user *models.User) string {
|
||||
if user == nil {
|
||||
return "V3-0"
|
||||
}
|
||||
|
||||
v3Name := strings.ToUpper(strings.TrimSpace(user.V3Username))
|
||||
v3Group := user.V3UserGroup
|
||||
if v3Group >= 100 && v3Group%100 == 0 {
|
||||
v3Group = v3Group / 100
|
||||
}
|
||||
|
||||
if v3Name != "" && v3Group > 0 {
|
||||
return fmt.Sprintf("V3-%s%d", v3Name, v3Group)
|
||||
}
|
||||
if v3Name != "" {
|
||||
return "V3-" + v3Name
|
||||
}
|
||||
if username := strings.TrimSpace(user.Username); username != "" {
|
||||
return username
|
||||
}
|
||||
if v3Group > 0 {
|
||||
return fmt.Sprintf("V3-%d", v3Group)
|
||||
}
|
||||
return "V3-0"
|
||||
}
|
||||
|
||||
// VatCode: NullString → string
|
||||
// - NULL → ""
|
||||
// - "0" → "" (FK patlamasın, sadece anlamlı kodlar gönderiyoruz)
|
||||
@@ -173,9 +505,6 @@ func ValidateItemVariant(tx *sql.Tx, ln models.OrderDetail) error {
|
||||
|
||||
}
|
||||
|
||||
// İstersen debug:
|
||||
// fmt.Printf("🧪 VARIANT CHECK item=%q color=%q dim1=%q dim2=%q clientKey=%s\n", item, color, dim1, dim2, safeNS(ln.ClientKey))
|
||||
|
||||
var exists int
|
||||
err := tx.QueryRow(`
|
||||
SELECT CASE WHEN EXISTS (
|
||||
@@ -297,12 +626,6 @@ type OrderLineResult struct {
|
||||
|
||||
// =======================================================
|
||||
// PART 1 — InsertOrder (header + lines insert) — FINAL v5.1
|
||||
// ✔ OrderHeaderID backend üretir
|
||||
// ✔ LOCAL-... numara gelirse gerçek WS numarası üretir
|
||||
// ✔ Full debug
|
||||
// ✔ Tüm satırlar INSERT edilir
|
||||
// ✔ INSERT öncesi ItemVariant Guard
|
||||
// ✔ Payload içi Duplicate Guard (comboKey)
|
||||
// =======================================================
|
||||
|
||||
func InsertOrder(header models.OrderHeader, lines []models.OrderDetail, user *models.User) (string, []OrderLineResult, error) {
|
||||
@@ -317,7 +640,7 @@ func InsertOrder(header models.OrderHeader, lines []models.OrderDetail, user *mo
|
||||
defer tx.Rollback()
|
||||
|
||||
now := time.Now()
|
||||
v3User := fmt.Sprintf("V3U%d-%s", user.V3UserGroup, user.V3Username)
|
||||
v3User := buildV3AuditUser(user)
|
||||
|
||||
// =======================================================
|
||||
// 1) BACKEND — OrderHeaderID üretimi (HER ZAMAN)
|
||||
@@ -368,7 +691,6 @@ func InsertOrder(header models.OrderHeader, lines []models.OrderDetail, user *mo
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =======================================================
|
||||
// 4) HEADER INSERT
|
||||
// =======================================================
|
||||
@@ -423,6 +745,7 @@ VALUES (
|
||||
|
||||
fmt.Println("🟪 HEADER INSERT ÇALIŞIYOR...")
|
||||
|
||||
// ✅ exRate burada gerçekten kullanılıyor (ExchangeRate parametresi)
|
||||
headerParams := []any{
|
||||
header.OrderHeaderID,
|
||||
nullableInt16(header.OrderTypeCode, 1),
|
||||
@@ -474,7 +797,7 @@ VALUES (
|
||||
nullableString(header.GLTypeCode, ""),
|
||||
nullableString(header.DocCurrencyCode, "TRY"),
|
||||
nullableString(header.LocalCurrencyCode, "TRY"),
|
||||
nullableFloat64(header.ExchangeRate, exRate),
|
||||
nullableFloat64(header.ExchangeRate, exRate), // ✅ exRate kullanıldı
|
||||
|
||||
nullableFloat64(header.TDisRate1, 0),
|
||||
nullableFloat64(header.TDisRate2, 0),
|
||||
@@ -520,6 +843,7 @@ VALUES (
|
||||
nullableBool(header.IsProposalBased, false),
|
||||
}
|
||||
|
||||
// ✅ queryHeader artık gerçekten kullanılıyor → "Unused variable 'queryHeader'" biter
|
||||
if _, err := tx.Exec(queryHeader, headerParams...); err != nil {
|
||||
fmt.Println("❌ HEADER INSERT ERROR:", err)
|
||||
return "", nil, fmt.Errorf("header insert hatasi: %w", err)
|
||||
@@ -527,6 +851,7 @@ VALUES (
|
||||
|
||||
fmt.Println("🟩 HEADER INSERT OK — ID:", newID)
|
||||
|
||||
// headerParams ... (senin mevcut hali aynen)
|
||||
// =======================================================
|
||||
// 5) LINE INSERT
|
||||
// =======================================================
|
||||
@@ -590,7 +915,6 @@ VALUES (
|
||||
seenCombo := make(map[string]bool)
|
||||
|
||||
for i, ln := range lines {
|
||||
// ===================== PART 2 (Satır 301-600) =====================
|
||||
fmt.Println("────────────────────────────────────")
|
||||
fmt.Printf("🟨 [INSERT] LINE %d — gelen OrderLineID=%s\n", i+1, ln.OrderLineID)
|
||||
|
||||
@@ -630,6 +954,7 @@ VALUES (
|
||||
}
|
||||
|
||||
planned := nullableDateString(ln.PlannedDateOfLading)
|
||||
|
||||
// ✅ INSERT ÖNCESİ ItemVariant GUARD
|
||||
if qtyValue(ln.Qty1) > 0 {
|
||||
if err := ValidateItemVariant(tx, ln); err != nil {
|
||||
@@ -637,6 +962,7 @@ VALUES (
|
||||
return "", nil, err
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf(
|
||||
"🚨 INSERT LINE[%d] | LineID=%s ClientKey=%s Item=%q Color=%q Dim1=%q Dim2=%q Dim3=%q Qty1=%v\n",
|
||||
i+1,
|
||||
@@ -708,6 +1034,28 @@ VALUES (
|
||||
return "", nil, fmt.Errorf("line insert hatasi: %w", err)
|
||||
}
|
||||
|
||||
// ✅ NEW: trOrderLineCurrency yaz
|
||||
if err := upsertLineCurrency(
|
||||
tx,
|
||||
ln.OrderLineID,
|
||||
|
||||
safeNS(ln.DocCurrencyCode),
|
||||
nf0(ln.PriceExchangeRate),
|
||||
|
||||
nf0(ln.Price),
|
||||
nf0(ln.Qty1),
|
||||
|
||||
nf0(ln.LDisRate1), // ✅ Line discount
|
||||
0, // ✅ Total discount (istersen header’dan alırsın)
|
||||
|
||||
nf0(ln.VatRate), // ✅ Vat rate
|
||||
ln,
|
||||
|
||||
v3User,
|
||||
); err != nil {
|
||||
return "", nil, fmt.Errorf("currency insert hatası: %w", err)
|
||||
}
|
||||
|
||||
if ln.ClientKey.Valid && ln.ClientKey.String != "" {
|
||||
lineResults = append(lineResults, OrderLineResult{
|
||||
ClientKey: ln.ClientKey.String,
|
||||
@@ -734,25 +1082,16 @@ VALUES (
|
||||
|
||||
// =======================================================
|
||||
// PART 2 — UpdateOrder FULL DEBUG (v4.3)
|
||||
// ✔ ComboKey ile açık satır eşleştirme
|
||||
// ✔ Kapalı satırları korur
|
||||
// ✔ Payload içi Duplicate Guard
|
||||
// ✔ INSERT/UPDATE öncesi ItemVariant Guard (tek noktada)
|
||||
// ✔ Grid’de olmayan açık satırları siler (önce child)
|
||||
// =======================================================
|
||||
|
||||
func UpdateOrder(header models.OrderHeader, lines []models.OrderDetail, user *models.User) ([]OrderLineResult, error) {
|
||||
conn := db.GetDB()
|
||||
|
||||
// ======================================================
|
||||
// 🔍 SCAN DEBUG — HEADER bilgisi
|
||||
// ======================================================
|
||||
fmt.Println("══════════════════════════════════════")
|
||||
fmt.Println("🔍 [DEBUG] UpdateOrder çağrıldı")
|
||||
fmt.Printf("🔍 HeaderID: %v\n", header.OrderHeaderID)
|
||||
fmt.Printf("🔍 Line sayısı: %v\n", len(lines))
|
||||
fmt.Printf("🔍 User: %v (V3: %s/%d)\n",
|
||||
user.Username, user.V3Username, user.V3UserGroup)
|
||||
fmt.Printf("🔍 User: %v (V3: %s/%d)\n", user.Username, user.V3Username, user.V3UserGroup)
|
||||
fmt.Println("══════════════════════════════════════")
|
||||
|
||||
tx, err := conn.Begin()
|
||||
@@ -762,9 +1101,9 @@ func UpdateOrder(header models.OrderHeader, lines []models.OrderDetail, user *mo
|
||||
defer tx.Rollback()
|
||||
|
||||
now := time.Now()
|
||||
v3User := fmt.Sprintf("V3U%d-%s", user.V3UserGroup, user.V3Username)
|
||||
v3User := buildV3AuditUser(user)
|
||||
|
||||
// Döviz kuru
|
||||
// Döviz kuru (Header ExchangeRate fallback)
|
||||
exRate := 1.0
|
||||
if header.DocCurrencyCode.Valid && header.DocCurrencyCode.String != "TRY" {
|
||||
if c, err := GetTodayCurrencyV3(conn, header.DocCurrencyCode.String); err == nil && c.Rate > 0 {
|
||||
@@ -775,11 +1114,16 @@ func UpdateOrder(header models.OrderHeader, lines []models.OrderDetail, user *mo
|
||||
// =======================================================
|
||||
// 0) Mevcut satırları oku (GUID STRING olarak!)
|
||||
// =======================================================
|
||||
|
||||
existingOpen := make(map[string]bool)
|
||||
existingClosed := make(map[string]bool)
|
||||
existingOpenCombo := make(map[string]string)
|
||||
existingClosedCombo := make(map[string]string)
|
||||
existingOpenMeta := make(map[string]struct {
|
||||
item string
|
||||
color string
|
||||
dim1 string
|
||||
dim2 string
|
||||
})
|
||||
|
||||
rows, err := tx.Query(`
|
||||
SELECT
|
||||
@@ -814,16 +1158,41 @@ WHERE OrderHeaderID=@p1
|
||||
}
|
||||
} else {
|
||||
existingOpen[id] = true
|
||||
existingOpenMeta[id] = struct {
|
||||
item string
|
||||
color string
|
||||
dim1 string
|
||||
dim2 string
|
||||
}{
|
||||
item: strings.TrimSpace(item),
|
||||
color: strings.TrimSpace(color),
|
||||
dim1: strings.TrimSpace(dim1),
|
||||
dim2: strings.TrimSpace(dim2),
|
||||
}
|
||||
if combo != "" {
|
||||
existingOpenCombo[combo] = id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isLineInvoiced := func(lineID string) (bool, error) {
|
||||
var exists int
|
||||
err := tx.QueryRow(`
|
||||
SELECT CASE WHEN EXISTS (
|
||||
SELECT 1
|
||||
FROM BAGGI_V3.dbo.trInvoiceLine WITH (NOLOCK)
|
||||
WHERE OrderLineID=@p1
|
||||
) THEN 1 ELSE 0 END
|
||||
`, lineID).Scan(&exists)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("invoice line kontrol query hatasi: %w", err)
|
||||
}
|
||||
return exists == 1, nil
|
||||
}
|
||||
|
||||
// ======================================================
|
||||
// HEADER UPDATE
|
||||
// ======================================================
|
||||
|
||||
_, err = tx.Exec(`
|
||||
UPDATE BAGGI_V3.dbo.trOrderHeader SET
|
||||
OrderDate=@p1,
|
||||
@@ -853,11 +1222,12 @@ WHERE OrderHeaderID=@p11
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// ======================================================
|
||||
// PREPARE STATEMENTS
|
||||
// ======================================================
|
||||
|
||||
insStmt, err := tx.Prepare(`INSERT INTO BAGGI_V3.dbo.trOrderLine (
|
||||
insStmt, err := tx.Prepare(`
|
||||
INSERT INTO BAGGI_V3.dbo.trOrderLine (
|
||||
OrderLineID, SortOrder, ItemTypeCode, ItemCode, ColorCode,
|
||||
ItemDim1Code, ItemDim2Code, ItemDim3Code,
|
||||
Qty1, Qty2, CancelQty1, CancelQty2, OrderCancelReasonCode,
|
||||
@@ -872,21 +1242,23 @@ BaseSubCurrAccID, BaseStoreCode,
|
||||
OrderHeaderID, CreatedUserName, CreatedDate,
|
||||
LastUpdatedUserName, LastUpdatedDate,
|
||||
SurplusOrderQtyToleranceRate,
|
||||
WithHoldingTaxTypeCode, DOVCode)
|
||||
WithHoldingTaxTypeCode, DOVCode
|
||||
)
|
||||
VALUES (
|
||||
@p1,@p2,@p3,@p4,@p5,@p6,@p7,@p8,@p9,@p10,
|
||||
@p11,@p12,@p13,@p14,@p15,@p16,@p17,@p18,
|
||||
@p19,@p20,@p21,@p22,@p23,@p24,@p25,@p26,@p27,
|
||||
@p28,@p29,@p30,@p31,@p32,@p33,@p34,@p35,
|
||||
@p36,@p37,@p38,@p39,@p40,@p41,@p42,@p43,
|
||||
@p44,@p45)`)
|
||||
|
||||
@p44,@p45
|
||||
)`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer insStmt.Close()
|
||||
|
||||
updStmt, err := tx.Prepare(`UPDATE BAGGI_V3.dbo.trOrderLine SET
|
||||
updStmt, err := tx.Prepare(`
|
||||
UPDATE BAGGI_V3.dbo.trOrderLine SET
|
||||
SortOrder=@p1, ItemTypeCode=@p2, ItemCode=@p3, ColorCode=@p4,
|
||||
ItemDim1Code=@p5, ItemDim2Code=@p6, ItemDim3Code=@p7,
|
||||
Qty1=@p8, Qty2=@p9, CancelQty1=@p10, CancelQty2=@p11,
|
||||
@@ -905,17 +1277,18 @@ LastUpdatedUserName=@p37, LastUpdatedDate=@p38,
|
||||
SurplusOrderQtyToleranceRate=@p39,
|
||||
WithHoldingTaxTypeCode=@p40, DOVCode=@p41
|
||||
WHERE OrderLineID=@p42 AND ISNULL(IsClosed,0)=0`)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer updStmt.Close()
|
||||
|
||||
// ======================================================
|
||||
// LOOP
|
||||
// ======================================================
|
||||
lineResults := make([]OrderLineResult, 0)
|
||||
seenCombo := make(map[string]bool)
|
||||
|
||||
for _, ln := range lines {
|
||||
|
||||
comboKey := normalizeComboKey(safeNS(ln.ComboKey))
|
||||
if comboKey == "" {
|
||||
comboKey = makeComboKey(ln)
|
||||
@@ -929,7 +1302,7 @@ WHERE OrderLineID=@p42 AND ISNULL(IsClosed,0)=0`)
|
||||
seenCombo[comboKey] = true
|
||||
}
|
||||
|
||||
// Kapalı satır
|
||||
// Kapalı satır guard
|
||||
if ln.OrderLineID != "" && existingClosed[ln.OrderLineID] {
|
||||
continue
|
||||
}
|
||||
@@ -941,23 +1314,39 @@ WHERE OrderLineID=@p42 AND ISNULL(IsClosed,0)=0`)
|
||||
|
||||
// DELETE SIGNAL
|
||||
if ln.OrderLineID != "" && qtyValue(ln.Qty1) <= 0 {
|
||||
_, err := tx.Exec(`DELETE FROM BAGGI_V3.dbo.trOrderLineCurrency WHERE OrderLineID=@p1`, ln.OrderLineID)
|
||||
invoiced, err := isLineInvoiced(ln.OrderLineID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = tx.Exec(`
|
||||
if invoiced {
|
||||
return nil, &models.ValidationError{
|
||||
Code: "ORDER_LINE_INVOICED",
|
||||
Message: fmt.Sprintf("Faturalanmis satir silinemez (OrderLineID=%s)", ln.OrderLineID),
|
||||
ClientKey: safeNS(ln.ClientKey),
|
||||
ItemCode: strings.TrimSpace(safeNS(ln.ItemCode)),
|
||||
ColorCode: strings.TrimSpace(safeNS(ln.ColorCode)),
|
||||
Dim1: strings.TrimSpace(safeNS(ln.ItemDim1Code)),
|
||||
Dim2: strings.TrimSpace(safeNS(ln.ItemDim2Code)),
|
||||
}
|
||||
}
|
||||
if _, err := tx.Exec(`DELETE FROM BAGGI_V3.dbo.trOrderLineCurrency WHERE OrderLineID=@p1`, ln.OrderLineID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := tx.Exec(`
|
||||
DELETE FROM BAGGI_V3.dbo.trOrderLine
|
||||
WHERE OrderHeaderID=@p1 AND OrderLineID=@p2 AND ISNULL(IsClosed,0)=0
|
||||
`, header.OrderHeaderID, ln.OrderLineID)
|
||||
if err != nil {
|
||||
`, header.OrderHeaderID, ln.OrderLineID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
delete(existingOpen, ln.OrderLineID)
|
||||
delete(existingOpenCombo, comboKey)
|
||||
continue
|
||||
}
|
||||
|
||||
isNew := false
|
||||
|
||||
// ID resolve: boşsa combo'dan yakala, yoksa yeni üret
|
||||
if ln.OrderLineID == "" {
|
||||
if dbID, ok := existingOpenCombo[comboKey]; ok {
|
||||
ln.OrderLineID = dbID
|
||||
@@ -967,6 +1356,7 @@ WHERE OrderHeaderID=@p1 AND OrderLineID=@p2 AND ISNULL(IsClosed,0)=0
|
||||
}
|
||||
}
|
||||
|
||||
// Variant guard
|
||||
if qtyValue(ln.Qty1) > 0 {
|
||||
if err := ValidateItemVariant(tx, ln); err != nil {
|
||||
return nil, err
|
||||
@@ -1048,9 +1438,28 @@ WHERE OrderHeaderID=@p1 AND OrderLineID=@p2 AND ISNULL(IsClosed,0)=0
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ Currency UPSERT (insert/update sonrası ortak)
|
||||
if err := upsertLineCurrency(
|
||||
tx,
|
||||
ln.OrderLineID,
|
||||
safeNS(ln.DocCurrencyCode),
|
||||
nf0(ln.PriceExchangeRate),
|
||||
nf0(ln.Price),
|
||||
nf0(ln.Qty1),
|
||||
nf0(ln.LDisRate1),
|
||||
0, // TODO: header TDisRate toplamını istersen buraya bağlarız
|
||||
nf0(ln.VatRate), // satır vat oranı
|
||||
ln,
|
||||
v3User,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Bu satır işlendi -> existingOpen setinden düş
|
||||
delete(existingOpen, ln.OrderLineID)
|
||||
delete(existingOpenCombo, comboKey)
|
||||
|
||||
// Sonuç mapping
|
||||
if ln.ClientKey.Valid {
|
||||
lineResults = append(lineResults, OrderLineResult{
|
||||
ClientKey: ln.ClientKey.String,
|
||||
@@ -1059,18 +1468,31 @@ WHERE OrderHeaderID=@p1 AND OrderLineID=@p2 AND ISNULL(IsClosed,0)=0
|
||||
}
|
||||
}
|
||||
|
||||
// Grid dışı kalan açık satırlar
|
||||
// =======================================================
|
||||
// Grid dışı kalan açık satırlar (payload'da yok -> sil)
|
||||
// =======================================================
|
||||
for id := range existingOpen {
|
||||
_, err := tx.Exec(`DELETE FROM BAGGI_V3.dbo.trOrderLineCurrency WHERE OrderLineID=@p1`, id)
|
||||
invoiced, err := isLineInvoiced(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = tx.Exec(`DELETE FROM BAGGI_V3.dbo.trOrderLine WHERE OrderLineID=@p1 AND ISNULL(IsClosed,0)=0`, id)
|
||||
if err != nil {
|
||||
if invoiced {
|
||||
meta := existingOpenMeta[id]
|
||||
fmt.Printf("[ORDER_UPDATE] skip delete invoiced line id=%s item=%s color=%s dim1=%s dim2=%s\n",
|
||||
id, meta.item, meta.color, meta.dim1, meta.dim2)
|
||||
continue
|
||||
}
|
||||
if _, err := tx.Exec(`DELETE FROM BAGGI_V3.dbo.trOrderLineCurrency WHERE OrderLineID=@p1`, id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := tx.Exec(`DELETE FROM BAGGI_V3.dbo.trOrderLine WHERE OrderLineID=@p1 AND ISNULL(IsClosed,0)=0`, id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// =======================================================
|
||||
// COMMIT + RETURN
|
||||
// =======================================================
|
||||
if err := tx.Commit(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user