diff --git a/svc/auth/claims_mapper.go b/svc/auth/claims_mapper.go
index fdb1d83..8d939c6 100644
--- a/svc/auth/claims_mapper.go
+++ b/svc/auth/claims_mapper.go
@@ -15,10 +15,11 @@ func BuildClaimsFromUser(u *models.MkUser, ttl time.Duration) Claims {
// 🔴 mk_dfusr.id
ID: u.ID,
- Username: u.Username,
- RoleCode: u.RoleCode,
- RoleID: u.RoleID,
- // ✅ BURASI
+ Username: u.Username,
+ RoleCode: u.RoleCode,
+ RoleID: u.RoleID,
+ V3Username: u.V3Username,
+ V3UserGroup: u.V3UserGroup,
DepartmentCodes: u.DepartmentCodes,
SessionID: u.SessionID,
diff --git a/svc/models/orderdetail.go b/svc/models/orderdetail.go
index f8aee81..fd60125 100644
--- a/svc/models/orderdetail.go
+++ b/svc/models/orderdetail.go
@@ -54,9 +54,10 @@ type OrderDetail struct {
LDisRate4 NullFloat64 `json:"LDisRate4"`
LDisRate5 NullFloat64 `json:"LDisRate5"`
- DocCurrencyCode NullString `json:"DocCurrencyCode"`
- PriceCurrencyCode NullString `json:"PriceCurrencyCode"`
- PriceExchangeRate NullFloat64 `json:"PriceExchangeRate"`
+ DocCurrencyCode NullString `json:"DocCurrencyCode"`
+ RelationCurrencyCode NullString `json:"RelationCurrencyCode"`
+ PriceCurrencyCode NullString `json:"PriceCurrencyCode"`
+ PriceExchangeRate NullFloat64 `json:"PriceExchangeRate"`
Price NullFloat64 `json:"Price"`
@@ -95,4 +96,29 @@ type OrderDetail struct {
UrunAltGrubu NullString `json:"UrunAltGrubu"`
Fit1 NullString `json:"Fit1"`
Fit2 NullString `json:"Fit2"`
+ // ============================
+ // 💰 Currency / Amount Fields
+ // (trOrderLineCurrency)
+ // ============================
+
+ // Döviz bazlı
+ DocPrice NullFloat64 `json:"DocPrice"` // PriceVI
+ DocAmount NullFloat64 `json:"DocAmount"` // AmountVI
+
+ // Yerel para (TRY)
+ LocalPrice NullFloat64 `json:"LocalPrice"` // Price
+ LocalAmount NullFloat64 `json:"LocalAmount"` // Amount
+
+ // İndirimler
+ LineDiscount NullFloat64 `json:"LineDiscount"` // LDiscount1
+ TotalDiscount NullFloat64 `json:"TotalDiscount"` // TDiscount1
+
+ // Vergi / Matrah
+ TaxBase NullFloat64 `json:"TaxBase"`
+ VatAmount NullFloat64 `json:"VatAmount"`
+ VatDeducation NullFloat64 `json:"VatDeducation"`
+ NetAmount NullFloat64 `json:"NetAmount"`
+
+ // Genel oran
+ Pct NullFloat64 `json:"Pct"`
}
diff --git a/svc/models/orderlist.go b/svc/models/orderlist.go
index a91ceff..b635b5d 100644
--- a/svc/models/orderlist.go
+++ b/svc/models/orderlist.go
@@ -28,6 +28,9 @@ type OrderList struct {
// 💰 Tutarlar
TotalAmount float64 `json:"TotalAmount"`
TotalAmountUSD float64 `json:"TotalAmountUSD"`
+ PackedAmount float64 `json:"PackedAmount"`
+ PackedUSD float64 `json:"PackedUSD"`
+ PackedRatePct float64 `json:"PackedRatePct"`
// 📝 Açıklama
Description string `json:"Description"`
diff --git a/svc/queries/get_order_list_excel.go b/svc/queries/get_order_list_excel.go
index 81c2c3c..b66d4fa 100644
--- a/svc/queries/get_order_list_excel.go
+++ b/svc/queries/get_order_list_excel.go
@@ -12,40 +12,230 @@ func GetOrderListExcel(
orderDate string,
) (*sql.Rows, error) {
- q := OrderListBaseQuery + " AND 1=1 "
- args := []interface{}{}
+ q := `
+SELECT
+ CAST(h.OrderHeaderID AS NVARCHAR(50)) AS OrderHeaderID,
+ ISNULL(h.OrderNumber,'') AS OrderNumber,
+ CONVERT(varchar,h.OrderDate,23) AS OrderDate,
- // SEARCH
+ ISNULL(h.CurrAccCode,'') AS CurrAccCode,
+ ISNULL(ca.CurrAccDescription,'') AS CurrAccDescription,
+
+ ISNULL(mt.AttributeDescription,'') AS MusteriTemsilcisi,
+ ISNULL(py.AttributeDescription,'') AS Piyasa,
+
+ ISNULL(h.DocCurrencyCode,'TRY') AS DocCurrencyCode,
+
+ -------------------------------------------------
+ -- BELGE PB TOPLAM
+ -------------------------------------------------
+ ISNULL(t.TotalAmount,0) AS TotalAmount,
+
+ -------------------------------------------------
+ -- USD TOPLAM
+ -------------------------------------------------
+ CASE
+ WHEN h.DocCurrencyCode = 'USD'
+ THEN ISNULL(t.TotalAmount,0)
+
+ WHEN h.DocCurrencyCode = 'TRY'
+ AND usd.Rate > 0
+ THEN ISNULL(t.TotalTRY,0) / usd.Rate
+
+ WHEN h.DocCurrencyCode IN ('EUR','GBP')
+ AND cur.Rate > 0
+ AND usd.Rate > 0
+ THEN (ISNULL(t.TotalAmount,0) * cur.Rate) / usd.Rate
+
+ ELSE 0
+ END AS TotalAmountUSD,
+
+ ISNULL(t.PackedAmount,0) AS PackedAmount,
+
+ CASE
+ WHEN h.DocCurrencyCode = 'USD'
+ THEN ISNULL(t.PackedAmount,0)
+
+ WHEN h.DocCurrencyCode = 'TRY'
+ AND usd.Rate > 0
+ THEN ISNULL(t.PackedTRY,0) / usd.Rate
+
+ WHEN cur.Rate > 0
+ AND usd.Rate > 0
+ THEN (ISNULL(t.PackedAmount,0) * cur.Rate) / usd.Rate
+
+ ELSE 0
+ END AS PackedUSD,
+
+ CASE
+ WHEN ISNULL(t.TotalAmount,0) > 0
+ THEN (ISNULL(t.PackedAmount,0) * 100.0) / NULLIF(t.TotalAmount,0)
+ ELSE 0
+ END AS PackedRatePct,
+
+ ISNULL(h.Description,'') AS Description,
+
+ usd.Rate AS ExchangeRateUSD
+
+FROM dbo.trOrderHeader h
+
+
+-------------------------------------------------
+-- ✅ LINE CURRENCY TOPLAM
+-------------------------------------------------
+LEFT JOIN (
+
+ SELECT
+ l.OrderHeaderID,
+
+ -- Belge para birimi toplam
+ SUM(
+ CASE
+ WHEN c.CurrencyCode = h.DocCurrencyCode
+ THEN c.NetAmount
+ ELSE 0
+ END
+ ) AS TotalAmount,
+
+ -- TRY toplam
+ SUM(
+ CASE
+ WHEN c.CurrencyCode = 'TRY'
+ THEN c.NetAmount
+ ELSE 0
+ END
+ ) AS TotalTRY,
+
+ -- Paketlenen (OrderLine IsClosed=1) belge PB toplam
+ SUM(
+ CASE
+ WHEN ISNULL(l.IsClosed,0) = 1
+ AND c.CurrencyCode = h.DocCurrencyCode
+ THEN c.NetAmount
+ ELSE 0
+ END
+ ) AS PackedAmount,
+
+ -- Paketlenen TRY toplam
+ SUM(
+ CASE
+ WHEN ISNULL(l.IsClosed,0) = 1
+ AND c.CurrencyCode = 'TRY'
+ THEN c.NetAmount
+ ELSE 0
+ END
+ ) AS PackedTRY
+
+ FROM dbo.trOrderLineCurrency c
+
+ JOIN dbo.trOrderLine l
+ ON l.OrderLineID = c.OrderLineID
+
+ JOIN dbo.trOrderHeader h
+ ON h.OrderHeaderID = l.OrderHeaderID
+
+ GROUP BY l.OrderHeaderID
+
+) t ON t.OrderHeaderID = h.OrderHeaderID
+
+
+-------------------------------------------------
+-- CARİ
+-------------------------------------------------
+LEFT JOIN dbo.cdCurrAccDesc ca
+ ON ca.CurrAccCode = h.CurrAccCode
+ AND ca.LangCode='TR'
+
+
+-------------------------------------------------
+-- TEMSİLCİ / PİYASA
+-------------------------------------------------
+LEFT JOIN dbo.CustomerAttributesFilter f
+ ON f.CurrAccCode = h.CurrAccCode
+
+LEFT JOIN dbo.cdCurrAccAttributeDesc mt
+ ON mt.CurrAccTypeCode=3
+ AND mt.AttributeTypeCode=2
+ AND mt.AttributeCode=f.CustomerAtt02
+ AND mt.LangCode='TR'
+
+LEFT JOIN dbo.cdCurrAccAttributeDesc py
+ ON py.CurrAccTypeCode=3
+ AND py.AttributeTypeCode=1
+ AND py.AttributeCode=f.CustomerAtt01
+ AND py.LangCode='TR'
+
+
+-------------------------------------------------
+-- USD → TRY
+-------------------------------------------------
+OUTER APPLY (
+ SELECT TOP 1 Rate
+ FROM dbo.AllExchangeRates
+ WHERE CurrencyCode='USD'
+ AND RelationCurrencyCode='TRY'
+ AND ExchangeTypeCode=6
+ AND Rate>0
+ AND Date<=CAST(GETDATE() AS date)
+ ORDER BY Date DESC
+) usd
+
+
+-------------------------------------------------
+-- DOC → TRY
+-------------------------------------------------
+OUTER APPLY (
+ SELECT TOP 1 Rate
+ FROM dbo.AllExchangeRates
+ WHERE CurrencyCode=h.DocCurrencyCode
+ AND RelationCurrencyCode='TRY'
+ AND ExchangeTypeCode=6
+ AND Rate>0
+ AND Date<=CAST(GETDATE() AS date)
+ ORDER BY Date DESC
+) cur
+
+
+WHERE
+ ISNULL(h.IsCancelOrder,0)=0
+ AND h.OrderTypeCode=1
+ AND h.ProcessCode='WS'
+ AND h.IsClosed=0
+`
+
+ args := []any{}
+
+ // ================= SEARCH =================
if search != "" {
q += `
AND (
- LOWER(h.OrderNumber) LIKE LOWER(@p1) OR
- LOWER(h.CurrAccCode) LIKE LOWER(@p1) OR
- LOWER(ca.CurrAccDescription) LIKE LOWER(@p1) OR
- LOWER(h.Description) LIKE LOWER(@p1) OR
- LOWER(mt.AttributeDescription) LIKE LOWER(@p1) OR
- LOWER(py.AttributeDescription) LIKE LOWER(@p1)
+ LOWER(h.OrderNumber) LIKE LOWER(@p1)
+ OR LOWER(h.CurrAccCode) LIKE LOWER(@p1)
+ OR LOWER(ca.CurrAccDescription) LIKE LOWER(@p1)
+ OR LOWER(h.Description) LIKE LOWER(@p1)
+ OR LOWER(mt.AttributeDescription) LIKE LOWER(@p1)
+ OR LOWER(py.AttributeDescription) LIKE LOWER(@p1)
)
`
args = append(args, "%"+search+"%")
}
- // CURRACC
+ // ================= CURRACC =================
if currAcc != "" {
q += fmt.Sprintf(" AND h.CurrAccCode = @p%d ", len(args)+1)
args = append(args, currAcc)
}
- // DATE
+ // ================= DATE =================
if orderDate != "" {
q += fmt.Sprintf(
- " AND CONVERT(varchar, h.OrderDate, 23) = @p%d ",
+ " AND CONVERT(varchar,h.OrderDate,23) = @p%d ",
len(args)+1,
)
args = append(args, orderDate)
}
- // ORDER BY SONDA
+ // ================= ORDER =================
q += " ORDER BY h.CreatedDate DESC "
return db.Query(q, args...)
diff --git a/svc/queries/order_get.go b/svc/queries/order_get.go
index b7fb55a..4e8c8da 100644
--- a/svc/queries/order_get.go
+++ b/svc/queries/order_get.go
@@ -8,15 +8,12 @@ import (
"fmt"
)
-// GetOrderByID — Sipariş başlığı (header) ve satırlarını (lines) getirir.
+// GetOrderByID returns order header and lines.
func GetOrderByID(orderID string) (*models.OrderHeader, []models.OrderDetail, error) {
conn := db.GetDB()
- logger.Printf("🧾 [GetOrderByID] begin • id=%s", orderID)
+ logger.Printf("[GetOrderByID] begin id=%s", orderID)
- // =====================================================
- // HEADER (Cari adı join'li)
- // =====================================================
var header models.OrderHeader
qHeader := `
SELECT
@@ -176,61 +173,76 @@ func GetOrderByID(orderID string) (*models.OrderHeader, []models.OrderDetail, er
&header.LastUpdatedDate,
&header.IsProposalBased,
)
-
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
- logger.Printf("⚠️ [GetOrderByID] sipariş bulunamadı: %s", orderID)
+ logger.Printf("[GetOrderByID] not found: %s", orderID)
return nil, nil, sql.ErrNoRows
}
- logger.Printf("❌ [GetOrderByID] header sorgu hatası: %v", err)
+ logger.Printf("[GetOrderByID] header error: %v", err)
return nil, nil, err
}
- logger.Printf("✅ [GetOrderByID] header loaded • orderNo=%v currAcc=%v",
- header.OrderNumber, header.CurrAccCode.String)
-
- // =====================================================
- // LINES
- // =====================================================
qLines := `
- SELECT
- CAST(L.OrderLineID AS varchar(36)) AS OrderLineID,
- L.SortOrder,
- L.ItemTypeCode,
- L.ItemCode,
- L.ColorCode,
- L.ItemDim1Code,
- L.ItemDim2Code,
- L.ItemDim3Code,
- L.Qty1,
- L.Qty2,
- L.Price,
- L.VatRate,
- L.PCTRate,
- L.DocCurrencyCode,
- L.DeliveryDate,
- L.PlannedDateOfLading,
- L.LineDescription,
- L.IsClosed,
- L.CreatedUserName,
- L.CreatedDate,
- L.LastUpdatedUserName,
- L.LastUpdatedDate,
- P.ProductAtt42Desc AS UrunIlkGrubu,
- P.ProductAtt01Desc AS UrunAnaGrubu,
- P.ProductAtt02Desc AS UrunAltGrubu,
- P.ProductAtt38Desc AS Fit1,
- P.ProductAtt39Desc AS Fit2
- FROM BAGGI_V3.dbo.trOrderLine AS L
- LEFT JOIN ProductFilterWithDescription('TR') AS P
- ON LTRIM(RTRIM(P.ProductCode)) = LTRIM(RTRIM(L.ItemCode))
- WHERE L.OrderHeaderID = @p1
- ORDER BY L.SortOrder ASC;
- `
+SELECT
+ CAST(L.OrderLineID AS varchar(36)) AS OrderLineID,
+ L.SortOrder,
+ L.ItemTypeCode,
+ L.ItemCode,
+ L.ColorCode,
+ L.ItemDim1Code,
+ L.ItemDim2Code,
+ L.ItemDim3Code,
+ L.Qty1,
+ L.Qty2,
+
+ ISNULL(CD.Price, 0) AS Price,
+ ISNULL(CD.CurrencyCode, ISNULL(L.DocCurrencyCode, 'TRY')) AS DocCurrencyCode,
+ ISNULL(CD.RelationCurrencyCode, ISNULL(L.DocCurrencyCode, 'TRY')) AS RelationCurrencyCode,
+ ISNULL(CD.ExchangeRate, ISNULL(L.PriceExchangeRate, 1)) AS PriceExchangeRate,
+ ISNULL(CD.PriceVI, ISNULL(L.Price, 0)) AS DocPrice,
+ ISNULL(CD.AmountVI, ISNULL(L.Price, 0) * ISNULL(L.Qty1, 0)) AS DocAmount,
+ ISNULL(CD.LDiscount1, 0) AS LineDiscount,
+ ISNULL(CD.TDiscount1, 0) AS TotalDiscount,
+ ISNULL(CD.TaxBase, 0) AS TaxBase,
+ ISNULL(CD.Pct, 0) AS Pct,
+ ISNULL(CD.Vat, 0) AS VatAmount,
+ ISNULL(CD.VatDeducation, 0) AS VatDeducation,
+ ISNULL(CD.NetAmount, 0) AS NetAmount,
+ ISNULL(CL.Price, ISNULL(CD.Price, 0)) AS LocalPrice,
+ ISNULL(CL.Amount, ISNULL(CD.Amount, 0)) AS LocalAmount,
+
+ L.VatRate,
+ L.PCTRate,
+ L.DeliveryDate,
+ L.PlannedDateOfLading,
+ L.LineDescription,
+ L.IsClosed,
+ L.CreatedUserName,
+ L.CreatedDate,
+ L.LastUpdatedUserName,
+ L.LastUpdatedDate,
+
+ P.ProductAtt42Desc AS UrunIlkGrubu,
+ P.ProductAtt01Desc AS UrunAnaGrubu,
+ P.ProductAtt02Desc AS UrunAltGrubu,
+ P.ProductAtt38Desc AS Fit1,
+ P.ProductAtt39Desc AS Fit2
+FROM BAGGI_V3.dbo.trOrderLine AS L
+LEFT JOIN BAGGI_V3.dbo.trOrderLineCurrency AS CD WITH (NOLOCK)
+ ON CD.OrderLineID = L.OrderLineID
+ AND CD.CurrencyCode = ISNULL(NULLIF(LTRIM(RTRIM(L.DocCurrencyCode)), ''), 'TRY')
+LEFT JOIN BAGGI_V3.dbo.trOrderLineCurrency AS CL WITH (NOLOCK)
+ ON CL.OrderLineID = L.OrderLineID
+ AND CL.CurrencyCode = 'TRY'
+LEFT JOIN ProductFilterWithDescription('TR') AS P
+ ON LTRIM(RTRIM(P.ProductCode)) = LTRIM(RTRIM(L.ItemCode))
+WHERE L.OrderHeaderID = @p1
+ORDER BY L.SortOrder ASC;
+`
rows, err := conn.Query(qLines, orderID)
if err != nil {
- logger.Printf("❌ [GetOrderByID] line sorgu hatası: %v", err)
+ logger.Printf("[GetOrderByID] lines error: %v", err)
return &header, nil, err
}
defer rows.Close()
@@ -250,9 +262,22 @@ func GetOrderByID(orderID string) (*models.OrderHeader, []models.OrderDetail, er
&ln.Qty1,
&ln.Qty2,
&ln.Price,
+ &ln.DocCurrencyCode,
+ &ln.RelationCurrencyCode,
+ &ln.PriceExchangeRate,
+ &ln.DocPrice,
+ &ln.DocAmount,
+ &ln.LineDiscount,
+ &ln.TotalDiscount,
+ &ln.TaxBase,
+ &ln.Pct,
+ &ln.VatAmount,
+ &ln.VatDeducation,
+ &ln.NetAmount,
+ &ln.LocalPrice,
+ &ln.LocalAmount,
&ln.VatRate,
&ln.PCTRate,
- &ln.DocCurrencyCode,
&ln.DeliveryDate,
&ln.PlannedDateOfLading,
&ln.LineDescription,
@@ -267,14 +292,14 @@ func GetOrderByID(orderID string) (*models.OrderHeader, []models.OrderDetail, er
&ln.Fit1,
&ln.Fit2,
); err != nil {
- return &header, nil, fmt.Errorf("line scan hatası: %w", err)
+ return &header, nil, fmt.Errorf("line scan error: %w", err)
}
lines = append(lines, ln)
}
if err := rows.Err(); err != nil {
- return &header, nil, fmt.Errorf("line rows hatası: %w", err)
+ return &header, nil, fmt.Errorf("line rows error: %w", err)
}
- logger.Printf("📦 [GetOrderByID] lines loaded • count=%d", len(lines))
+ logger.Printf("[GetOrderByID] lines loaded count=%d", len(lines))
return &header, lines, nil
}
diff --git a/svc/queries/order_write.go b/svc/queries/order_write.go
index 4e3f41d..1e2b71e 100644
--- a/svc/queries/order_write.go
+++ b/svc/queries/order_write.go
@@ -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
}
diff --git a/svc/queries/orderlist.go b/svc/queries/orderlist.go
index 78a7107..9e7cbe7 100644
--- a/svc/queries/orderlist.go
+++ b/svc/queries/orderlist.go
@@ -10,6 +10,10 @@ import (
// ========================================================
// 📌 GetOrderList — FINAL + CURRENCY SAFE + PIYASA AUTHZ
+//
+// ✅ TotalAmount artık trOrderLineCurrency(NetAmount) üzerinden
+// ve CurrencyCode = Header.DocCurrencyCode satırından gelir.
+//
// ========================================================
func GetOrderList(
ctx context.Context,
@@ -36,10 +40,8 @@ func GetOrderList(
}
if len(codes) == 0 {
- // hiç yetkisi yok → hiç kayıt dönmesin
piyasaWhere = "1=0"
} else {
- // ⚠️ EXISTS içinde kullanılacak
piyasaWhere = authz.BuildINClause(
"UPPER(f2.CustomerAtt01)",
codes,
@@ -86,6 +88,29 @@ SELECT
ELSE 0
END AS TotalAmountUSD,
+ ISNULL(l.PackedAmount,0) AS PackedAmount,
+
+ CASE
+ WHEN h.DocCurrencyCode = 'USD'
+ THEN ISNULL(l.PackedAmount,0)
+
+ WHEN h.DocCurrencyCode = 'TRY'
+ AND usd.Rate > 0
+ THEN ISNULL(l.PackedTRY,0) / usd.Rate
+
+ WHEN cur.Rate > 0
+ AND usd.Rate > 0
+ THEN (ISNULL(l.PackedAmount,0) * cur.Rate) / usd.Rate
+
+ ELSE 0
+ END AS PackedUSD,
+
+ CASE
+ WHEN ISNULL(l.TotalAmount,0) > 0
+ THEN (ISNULL(l.PackedAmount,0) * 100.0) / NULLIF(l.TotalAmount,0)
+ ELSE 0
+ END AS PackedRatePct,
+
ISNULL(h.IsCreditableConfirmed,0) AS IsCreditableConfirmed,
ISNULL(h.Description,'') AS Description,
@@ -93,12 +118,40 @@ SELECT
FROM dbo.trOrderHeader h
+-- ✅ TOPLAM ARTIK trOrderLineCurrency'den: CurrencyCode = DocCurrencyCode
JOIN (
SELECT
- OrderHeaderID,
- SUM(Qty1 * Price) AS TotalAmount
- FROM dbo.trOrderLine
- GROUP BY OrderHeaderID
+ l.OrderHeaderID,
+ SUM(ISNULL(c.NetAmount,0)) AS TotalAmount,
+ SUM(
+ CASE
+ WHEN ISNULL(c.CurrencyCode,'') = 'TRY'
+ THEN ISNULL(c.NetAmount,0)
+ ELSE 0
+ END
+ ) AS TotalTRY,
+ SUM(
+ CASE
+ WHEN ISNULL(l.IsClosed,0) = 1
+ THEN ISNULL(c.NetAmount,0)
+ ELSE 0
+ END
+ ) AS PackedAmount,
+ SUM(
+ CASE
+ WHEN ISNULL(l.IsClosed,0) = 1
+ AND ISNULL(c.CurrencyCode,'') = 'TRY'
+ THEN ISNULL(c.NetAmount,0)
+ ELSE 0
+ END
+ ) AS PackedTRY
+ FROM dbo.trOrderLine l
+ JOIN dbo.trOrderHeader h2
+ ON h2.OrderHeaderID = l.OrderHeaderID
+ LEFT JOIN dbo.trOrderLineCurrency c
+ ON c.OrderLineID = l.OrderLineID
+ AND c.CurrencyCode = ISNULL(h2.DocCurrencyCode,'TRY')
+ GROUP BY l.OrderHeaderID
) l
ON l.OrderHeaderID = h.OrderHeaderID
diff --git a/svc/repository/mk_user_repository.go b/svc/repository/mk_user_repository.go
index 7eb0bce..aad9974 100644
--- a/svc/repository/mk_user_repository.go
+++ b/svc/repository/mk_user_repository.go
@@ -45,6 +45,8 @@ func (r *MkUserRepository) GetByUsername(username string) (*models.MkUser, error
FILTER (WHERE d.code IS NOT NULL),
'{}'
) AS department_codes,
+ COALESCE(MAX(n.username), '') AS v3_username,
+ COALESCE(MAX(n.user_group_code::text), '') AS v3_usergroup,
u.password_updated_at,
u.created_at,
@@ -67,6 +69,13 @@ LEFT JOIN dfusr_dprt ud
LEFT JOIN mk_dprt d
ON d.id = ud.dprt_id
+LEFT JOIN dfusr_nebim_user un
+ ON un.dfusr_id = u.id
+
+LEFT JOIN mk_nebim_user n
+ ON n.id = un.mk_nebim_user_id
+ AND n.is_active = true
+
WHERE LOWER(u.username) = LOWER($1)
GROUP BY
@@ -85,6 +94,8 @@ LIMIT 1
&u.RoleCode,
pq.Array(&u.DepartmentCodes), // ✅
+ &u.V3Username,
+ &u.V3UserGroup,
&u.PasswordUpdatedAt,
@@ -127,6 +138,8 @@ func (r *MkUserRepository) GetByID(id int64) (*models.MkUser, error) {
FILTER (WHERE d.code IS NOT NULL),
'{}'
) AS department_codes,
+ COALESCE(MAX(n.username), '') AS v3_username,
+ COALESCE(MAX(n.user_group_code::text), '') AS v3_usergroup,
u.password_updated_at,
u.created_at,
@@ -149,7 +162,14 @@ LEFT JOIN dfusr_dprt ud
LEFT JOIN mk_dprt d
ON d.id = ud.dprt_id
-WHERE LOWER(u.username) = LOWER($1)
+LEFT JOIN dfusr_nebim_user un
+ ON un.dfusr_id = u.id
+
+LEFT JOIN mk_nebim_user n
+ ON n.id = un.mk_nebim_user_id
+ AND n.is_active = true
+
+WHERE u.id = $1
GROUP BY
u.id, r.id
@@ -166,6 +186,8 @@ LIMIT 1
&u.RoleID,
&u.RoleCode,
pq.Array(&u.DepartmentCodes), // ✅
+ &u.V3Username,
+ &u.V3UserGroup,
&u.PasswordUpdatedAt,
&u.CreatedAt,
&u.UpdatedAt,
diff --git a/svc/routes/order_list_excel.go b/svc/routes/order_list_excel.go
index 90ff2a8..e27abdf 100644
--- a/svc/routes/order_list_excel.go
+++ b/svc/routes/order_list_excel.go
@@ -1,7 +1,6 @@
package routes
import (
- "bssapp-backend/models"
"bssapp-backend/queries"
"database/sql"
"fmt"
@@ -14,25 +13,50 @@ import (
func OrderListExcelRoute(db *sql.DB) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Cache-Control", "no-store")
+
+ // ======================
+ // PARAMS
+ // ======================
search := r.URL.Query().Get("search")
currAcc := r.URL.Query().Get("CurrAccCode")
orderDate := r.URL.Query().Get("OrderDate")
+ // ======================
+ // QUERY
+ // ======================
rows, err := queries.GetOrderListExcel(db, search, currAcc, orderDate)
if err != nil {
- http.Error(w, "Veritabanı hatası", 500)
+ http.Error(w, err.Error(), 500)
return
}
defer rows.Close()
+ // ======================
+ // EXCEL INIT
+ // ======================
f := excelize.NewFile()
sheet := "Orders"
f.SetSheetName("Sheet1", sheet)
+ // ======================
+ // HEADERS
+ // ======================
headers := []string{
- "Sipariş No", "Tarih", "Cari Kod", "Cari Adı",
- "Temsilci", "Piyasa", "Onay Tarihi", "PB",
- "Tutar", "Tutar (USD)", "Açıklama",
+ "Sipariş No",
+ "Tarih",
+ "Cari Kod",
+ "Cari Adı",
+ "Temsilci",
+ "Piyasa",
+ "PB",
+ "Tutar",
+ "Tutar (USD)",
+ "Paketlenen Tutar",
+ "Paketlenen (USD)",
+ "Paketlenme (%)",
+ "USD Kur",
+ "Açıklama",
}
for i, h := range headers {
@@ -40,52 +64,108 @@ func OrderListExcelRoute(db *sql.DB) http.Handler {
f.SetCellValue(sheet, cell, h)
}
- rowIdx := 2
+ // ======================
+ // ROWS
+ // ======================
+ row := 2
+
for rows.Next() {
- var o models.OrderList
- _ = rows.Scan(
- &o.OrderHeaderID,
- &o.OrderNumber,
- &o.OrderDate,
- &o.CurrAccCode,
- &o.CurrAccDescription,
- &o.MusteriTemsilcisi,
- &o.Piyasa,
- &o.CreditableConfirmedDate,
- &o.DocCurrencyCode,
- &o.TotalAmount,
- &o.TotalAmountUSD,
- &o.IsCreditableConfirmed,
- &o.Description,
- &o.ExchangeRateUSD,
+
+ // 🔴 15 KOLON = 15 DEĞİŞKEN
+ var (
+ id, no, date, code, name string
+ rep, piyasa, cur string
+
+ total float64
+ totalUSD float64
+ packedAmount float64
+ packedUSD float64
+ packedRatePct float64
+ usdRate float64
+
+ desc string
)
- f.SetSheetRow(sheet, fmt.Sprintf("A%d", rowIdx), &[]interface{}{
- o.OrderNumber,
- o.OrderDate,
- o.CurrAccCode,
- o.CurrAccDescription,
- o.MusteriTemsilcisi,
- o.Piyasa,
- o.CreditableConfirmedDate,
- o.DocCurrencyCode,
- o.TotalAmount,
- o.TotalAmountUSD,
- o.Description,
+ // 🔴 SELECT SIRASIYLA BİREBİR
+ err := rows.Scan(
+ &id, // 1
+ &no, // 2
+ &date, // 3
+ &code, // 4
+ &name, // 5
+ &rep, // 6
+ &piyasa, // 7
+ &cur, // 8
+ &total, // 9
+ &totalUSD, // 10
+ &packedAmount, // 11
+ &packedUSD, // 12
+ &packedRatePct, // 13
+ &desc, // 14
+ &usdRate, // 15
+ )
+
+ if err != nil {
+ http.Error(w, "Scan error: "+err.Error(), 500)
+ return
+ }
+
+ // ======================
+ // WRITE ROW
+ // ======================
+ f.SetSheetRow(sheet, fmt.Sprintf("A%d", row), &[]any{
+ no,
+ date,
+ code,
+ name,
+ rep,
+ piyasa,
+ cur,
+ total,
+ totalUSD,
+ packedAmount,
+ packedUSD,
+ packedRatePct,
+ usdRate,
+ desc,
})
- rowIdx++
+
+ row++
+ }
+
+ // ======================
+ // BUFFER WRITE
+ // ======================
+ buf, err := f.WriteToBuffer()
+ if err != nil {
+ http.Error(w, err.Error(), 500)
+ return
}
filename := fmt.Sprintf(
- "order_list_%s.xlsx",
- time.Now().Format("2006-01-02_15-04"),
+ "siparis_listesi_%s.xlsx",
+ time.Now().Format("20060102_150405"),
)
- w.Header().Set("Content-Type",
- "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
- w.Header().Set("Content-Disposition",
- "attachment; filename="+filename)
+ // ======================
+ // RESPONSE
+ // ======================
+ w.Header().Set(
+ "Content-Type",
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+ )
- _ = f.Write(w)
+ w.Header().Set(
+ "Content-Disposition",
+ "attachment; filename=\""+filename+"\"",
+ )
+
+ w.Header().Set(
+ "Content-Length",
+ fmt.Sprint(len(buf.Bytes())),
+ )
+
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write(buf.Bytes())
})
}
diff --git a/svc/routes/orderlist.go b/svc/routes/orderlist.go
index 078cd16..09d9f83 100644
--- a/svc/routes/orderlist.go
+++ b/svc/routes/orderlist.go
@@ -58,7 +58,7 @@ func OrderListRoute(mssql *sql.DB) http.Handler {
count := 0
// ==================================================
- // 🧠 SCAN — SQL SELECT ile BİRE BİR (14 kolon)
+ // 🧠 SCAN — SQL SELECT ile BİRE BİR (17 kolon)
// ==================================================
for rows.Next() {
@@ -80,11 +80,14 @@ func OrderListRoute(mssql *sql.DB) http.Handler {
&o.TotalAmount, // 10
&o.TotalAmountUSD, // 11
+ &o.PackedAmount, // 12
+ &o.PackedUSD, // 13
+ &o.PackedRatePct, // 14
- &o.IsCreditableConfirmed, // 12
- &o.Description, // 13
+ &o.IsCreditableConfirmed, // 15
+ &o.Description, // 16
- &o.ExchangeRateUSD, // 14
+ &o.ExchangeRateUSD, // 17
)
if err != nil {
diff --git a/svc/utils/utils.go b/svc/utils/utils.go
index cf9addd..207ce9e 100644
--- a/svc/utils/utils.go
+++ b/svc/utils/utils.go
@@ -3,6 +3,8 @@ package utils
import (
"bssapp-backend/auth"
"bssapp-backend/models"
+ "strconv"
+ "strings"
)
func UserFromClaims(c *auth.Claims) *models.User {
@@ -10,8 +12,20 @@ func UserFromClaims(c *auth.Claims) *models.User {
return nil
}
+ v3Group := 0
+ if raw := strings.TrimSpace(c.V3UserGroup); raw != "" {
+ if parsed, err := strconv.Atoi(raw); err == nil {
+ v3Group = parsed
+ }
+ }
+
return &models.User{
- ID: int(c.ID),
- Username: c.Username,
+ ID: int(c.ID),
+ Username: c.Username,
+ RoleID: int(c.RoleID),
+ RoleCode: c.RoleCode,
+ V3Username: strings.TrimSpace(c.V3Username),
+ V3UserGroup: v3Group,
+ ForcePasswordChange: c.ForcePasswordChange,
}
}
diff --git a/ui/quasar.config.js.temporary.compiled.1770889244552.mjs b/ui/quasar.config.js.temporary.compiled.1770889244552.mjs
deleted file mode 100644
index b4f411c..0000000
--- a/ui/quasar.config.js.temporary.compiled.1770889244552.mjs
+++ /dev/null
@@ -1,83 +0,0 @@
-/* eslint-disable */
-/**
- * THIS FILE IS GENERATED AUTOMATICALLY.
- * 1. DO NOT edit this file directly as it won't do anything.
- * 2. EDIT the original quasar.config file INSTEAD.
- * 3. DO NOT git commit this file. It should be ignored.
- *
- * This file is still here because there was an error in
- * the original quasar.config file and this allows you to
- * investigate the Node.js stack error.
- *
- * After you fix the original file, this file will be
- * deleted automatically.
- **/
-
-
-// quasar.config.js
-import { defineConfig } from "@quasar/app-webpack/wrappers";
-var quasar_config_default = defineConfig(() => {
- return {
- // ✅ UYGULAMA KİMLİĞİ (WEB'DE GÖRÜNEN İSİM)
- productName: "Baggi BSS",
- productDescription: "Baggi Tekstil Business Support System",
- // 🔹 Boot dosyaları
- boot: ["axios", "dayjs"],
- // 🔹 Global CSS
- css: ["app.css"],
- // 🔹 Ekstra icon/font setleri
- extras: [
- "roboto-font",
- "material-icons"
- ],
- // 🔹 Derleme Ayarları
- build: {
- vueRouterMode: "hash",
- env: {
- VITE_API_BASE_URL: "http://localhost:8080/api"
- },
- esbuildTarget: {
- browser: ["es2022", "firefox115", "chrome115", "safari14"],
- node: "node20"
- }
- },
- // 🔹 Geliştirme Sunucusu
- devServer: {
- server: { type: "http" },
- port: 9e3,
- open: true
- },
- // 🔹 Quasar Framework ayarları
- framework: {
- config: {
- notify: { position: "top", timeout: 2500 }
- },
- lang: "tr",
- plugins: ["Loading", "Dialog", "Notify"]
- },
- animations: [],
- ssr: {
- prodPort: 3e3,
- middlewares: ["render"],
- pwa: false
- },
- pwa: {
- workboxMode: "GenerateSW"
- },
- capacitor: {
- hideSplashscreen: true
- },
- electron: {
- preloadScripts: ["electron-preload"],
- inspectPort: 5858,
- bundler: "packager",
- builder: { appId: "baggisowtfaresystem" }
- },
- bex: {
- extraScripts: []
- }
- };
-});
-export {
- quasar_config_default as default
-};
diff --git a/ui/src/layouts/MainLayout.vue b/ui/src/layouts/MainLayout.vue
index a58a79d..fbc5d65 100644
--- a/ui/src/layouts/MainLayout.vue
+++ b/ui/src/layouts/MainLayout.vue
@@ -135,13 +135,6 @@ import { Dialog } from 'quasar'
import { useAuthStore } from 'stores/authStore'
import { usePermissionStore } from 'stores/permissionStore'
-import { usePermission } from 'src/composables/usePermission'
-
-const { canRead, canWrite, canUpdate } = usePermission()
-
-const canReadOrder = canRead('order')
-const canWriteOrder = canWrite('order')
-const canUpdateOrder = canUpdate('order')
/* ================= STORES ================= */
diff --git a/ui/src/pages/ActivityLogs.vue b/ui/src/pages/ActivityLogs.vue
index 9748805..5569058 100644
--- a/ui/src/pages/ActivityLogs.vue
+++ b/ui/src/pages/ActivityLogs.vue
@@ -1,5 +1,5 @@
- DashBoard